[
  {
    "path": ".errcheck-excludes",
    "content": "(go.mongodb.org/mongo-driver/x/mongo/driver.Connection).Close\n(*go.mongodb.org/mongo-driver/x/network/connection.connection).Close\n(go.mongodb.org/mongo-driver/x/network/connection.Connection).Close\n(*go.mongodb.org/mongo-driver/x/mongo/driver/topology.connection).close\n(*go.mongodb.org/mongo-driver/x/mongo/driver/topology.Topology).Unsubscribe\n(*go.mongodb.org/mongo-driver/x/mongo/driver/topology.Server).Close\n(*go.mongodb.org/mongo-driver/x/network/connection.pool).closeConnection\n(*go.mongodb.org/mongo-driver/x/mongo/driver/topology.pool).close\n(go.mongodb.org/mongo-driver/x/network/wiremessage.ReadWriteCloser).Close\n(*go.mongodb.org/mongo-driver/mongo.Cursor).Close\n(*go.mongodb.org/mongo-driver/mongo.ChangeStream).Close\n(*go.mongodb.org/mongo-driver/mongo.Client).Disconnect\n(net.Conn).Close\nencoding/pem.Encode\nfmt.Fprintf\nfmt.Fprint\n"
  },
  {
    "path": ".evergreen/config.yml",
    "content": "########################################\n# Evergreen Template for MongoDB Drivers\n########################################\n\n# When a task that used to pass starts to fail\n# Go through all versions that may have been skipped to detect\n# when the task started failing\nstepback: true\n# Mark a failure as a system/bootstrap failure (purple box) rather then a task\n# failure by default.\n# Actual testing tasks are marked with `type: test`\ncommand_type: setup\n# Fail builds when pre tasks fail.\npre_error_fails_task: true\n# Protect the CI from long or indefinite runtimes.\nexec_timeout_secs: 3600\n# What to do when evergreen hits the timeout (`post:` tasks are run automatically)\ntimeout:\n  - command: subprocess.exec\n    params:\n      binary: bash\n      args: [ls, -la]\nfunctions:\n  setup-system:\n    # Executes clone and applies the submitted patch, if any\n    - command: git.get_project\n      type: system\n      params:\n        directory: src/go.mongodb.org/mongo-driver\n    - command: shell.exec\n      params:\n        working_dir: \"src/go.mongodb.org/mongo-driver\"\n        script: |\n          git submodule update --init\n    # Make an env.sh and evergreen expansion file with dynamic values\n    - command: subprocess.exec\n      params:\n        binary: bash\n        working_dir: src/go.mongodb.org/mongo-driver\n        env:\n          GOROOT: ${GO_DIST}\n          IS_PATCH: ${is_patch}\n          VERSION_ID: ${version_id}\n          # Define an alias for the task runner script.\n          TASK_RUNNER_ALIAS: &task-runner src/go.mongodb.org/mongo-driver/.evergreen/run-task.sh\n        args: [.evergreen/setup-system.sh]\n    - command: expansions.update\n      params:\n        file: src/go.mongodb.org/mongo-driver/expansion.yml\n    - command: subprocess.exec\n      params:\n        binary: bash\n        include_expansions_in_env: [\"PROJECT_DIRECTORY\"]\n        args:\n          - \"${DRIVERS_TOOLS}/.evergreen/setup.sh\"\n  handle-test-artifacts:\n    - command: gotest.parse_files\n      params:\n        optional_output: \"true\"\n        files:\n          - \"src/go.mongodb.org/mongo-driver/*.suite\"\n    - command: ec2.assume_role\n      params:\n        role_arn: ${assume_role_arn}\n    - command: s3.put\n      params:\n        aws_key: ${AWS_ACCESS_KEY_ID}\n        aws_secret: ${AWS_SECRET_ACCESS_KEY}\n        aws_session_token: ${AWS_SESSION_TOKEN}\n        local_file: ${DRIVERS_TOOLS}/.evergreen/test_logs.tar.gz\n        remote_file: ${build_variant}/${revision}/${version_id}/${build_id}/logs/${task_id}-${execution}-drivers-tools-logs.tar.gz\n        bucket: ${aws_bucket}\n        permissions: public-read\n        content_type: ${content_type|application/x-gzip}\n        display_name: \"drivers-tools-logs.tar.gz\"\n    - command: s3.put\n      params:\n        aws_key: ${AWS_ACCESS_KEY_ID}\n        aws_secret: ${AWS_SECRET_ACCESS_KEY}\n        aws_session_token: ${AWS_SESSION_TOKEN}\n        optional: true\n        local_file: ${PROJECT_DIRECTORY}/fuzz.tgz\n        remote_file: ${build_variant}/${revision}/${version_id}/${build_id}/${task_id}-${execution}-fuzz.tgz\n        bucket: ${aws_bucket}\n        permissions: public-read\n        content_type: application/x-gzip\n        display_name: \"fuzz.tgz\"\n    - command: subprocess.exec\n      params:\n        binary: bash\n        args: [*task-runner, evg-gather-test-suites]\n    - command: s3.put\n      params:\n        aws_key: ${AWS_ACCESS_KEY_ID}\n        aws_secret: ${AWS_SECRET_ACCESS_KEY}\n        aws_session_token: ${AWS_SESSION_TOKEN}\n        local_file: src/go.mongodb.org/mongo-driver/test_suite.tgz\n        optional: true\n        remote_file: ${build_variant}/${revision}/${version_id}/${build_id}/logs/${task_id}-${execution}-test_suite.tgz\n        bucket: ${aws_bucket}\n        permissions: public-read\n        content_type: ${content_type|text/plain}\n        display_name: test_suite.tgz\n  bootstrap-mongohoused:\n    - command: ec2.assume_role\n      params:\n        role_arn: ${aws_test_secrets_role}\n    - command: subprocess.exec\n      params:\n        binary: bash\n        add_expansions_to_env: true\n        args:\n          - ${DRIVERS_TOOLS}/.evergreen/atlas_data_lake/pull-mongohouse-image.sh\n    - command: subprocess.exec\n      params:\n        binary: bash\n        args:\n          - ${DRIVERS_TOOLS}/.evergreen/atlas_data_lake/run-mongohouse-image.sh\n  bootstrap-mongo-orchestration:\n    - command: subprocess.exec\n      params:\n        binary: bash\n        env:\n          MONGODB_VERSION: ${VERSION}\n        include_expansions_in_env: [TOPOLOGY, AUTH, SSL, ORCHESTRATION_FILE, REQUIRE_API_VERSION, LOAD_BALANCER]\n        args: [\"${DRIVERS_TOOLS}/.evergreen/run-orchestration.sh\"]\n    - command: expansions.update\n      params:\n        file: mo-expansion.yml\n  ocsp-bootstrap-mongo-orchestration:\n    - command: subprocess.exec\n      params:\n        binary: bash\n        env:\n          MONGODB_VERSION: ${VERSION}\n        include_expansions_in_env: [TOPOLOGY, AUTH, SSL, ORCHESTRATION_FILE]\n        args: [\"${DRIVERS_TOOLS}/.evergreen/run-orchestration.sh\"]\n    - command: expansions.update\n      params:\n        file: mo-expansion.yml\n  teardown:\n    - command: subprocess.exec\n      params:\n        binary: bash\n        args:\n          # Ensure the instance profile is reassigned for aws tests.\n          - ${DRIVERS_TOOLS}/.evergreen/auth_aws/teardown.sh\n    - command: subprocess.exec\n      params:\n        binary: bash\n        args:\n          - ${DRIVERS_TOOLS}/.evergreen/csfle/teardown.sh\n    - command: subprocess.exec\n      params:\n        binary: bash\n        args:\n          - ${DRIVERS_TOOLS}/.evergreen/ocsp/teardown.sh\n    - command: subprocess.exec\n      params:\n        binary: bash\n        args:\n          - ${DRIVERS_TOOLS}/.evergreen/teardown.sh\n  assume-test-secrets-ec2-role:\n    - command: ec2.assume_role\n      params:\n        role_arn: ${aws_test_secrets_role}\n        duration_seconds: 1800\n  run-oidc-auth-test-with-test-credentials:\n    - command: subprocess.exec\n      type: test\n      params:\n        binary: bash\n        env:\n          OIDC: oidc\n        include_expansions_in_env: [DRIVERS_TOOLS, AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN]\n        args: [*task-runner, test-oidc]\n  run-tests:\n    - command: subprocess.exec\n      params:\n        binary: bash\n        env:\n          GO_BUILD_TAGS: \"cse,mongointernal\"\n        include_expansions_in_env: [\"TOPOLOGY\", \"AUTH\", \"SSL\", \"SKIP_CSOT_TESTS\", \"MONGODB_URI\", \"CRYPT_SHARED_LIB_PATH\", \"SKIP_CRYPT_SHARED_LIB\", \"RACE\", \"MONGO_GO_DRIVER_COMPRESSOR\", \"REQUIRE_API_VERSION\", \"LOAD_BALANCER\"]\n        args: [*task-runner, setup-test]\n    - command: subprocess.exec\n      type: test\n      retry_on_failure: true\n      params:\n        binary: bash\n        args: [*task-runner, \"${DEFAULT_TASK}\"]\n  create-api-report:\n    - command: subprocess.exec\n      type: test\n      params:\n        binary: bash\n        add_expansions_to_env: true\n        env:\n          BASE_SHA: \"${revision}\"\n          HEAD_SHA: \"${github_commit}\"\n        args: [*task-runner, api-report]\n  \"backport pr\":\n    - command: subprocess.exec\n      type: test\n      params:\n        binary: bash\n        env:\n          COMMIT: \"${github_commit}\"\n          PR_TASK: backport-pr\n        args: [*task-runner, pr-task]\n  send-perf-data:\n    # Here we begin to generate the request to send the data to SPS\n    - command: shell.exec\n      params:\n        script: |\n          # We use the requester expansion to determine whether the data is from a mainline evergreen run or not\n          if [ \"${requester}\" == \"commit\" ]; then\n            is_mainline=true\n          else\n            is_mainline=false\n          fi\n\n          # We parse the username out of the order_id as patches append that in and SPS does not need that information\n          parsed_order_id=$(echo \"${revision_order_id}\" | awk -F'_' '{print $NF}')\n          # Submit the performance data to the SPS endpoint\n          response=$(curl -s -w \"\\nHTTP_STATUS:%{http_code}\" -X 'POST' \\\n            \"https://performance-monitoring-api.corp.mongodb.com/raw_perf_results/cedar_report?project=${project_id}&version=${version_id}&variant=${build_variant}&order=$parsed_order_id&task_name=${task_name}&task_id=${task_id}&execution=${execution}&mainline=$is_mainline\" \\\n            -H 'accept: application/json' \\\n            -H 'Content-Type: application/json' \\\n            -d @src/go.mongodb.org/mongo-driver/perf.json)\n\n          http_status=$(echo \"$response\" | grep \"HTTP_STATUS\" | awk -F':' '{print $2}')\n          response_body=$(echo \"$response\" | sed '/HTTP_STATUS/d')\n\n          # We want to throw an error if the data was not successfully submitted\n          if [ \"$http_status\" -ne 200 ]; then\n            echo \"Error: Received HTTP status $http_status\"\n            echo \"Response Body: $response_body\"\n            exit 1\n          fi\n\n          echo \"Response Body: $response_body\"\n          echo \"HTTP Status: $http_status\"\n  send-perf-pr-comment:\n    - command: subprocess.exec\n      type: test\n      params:\n        binary: bash\n        add_expansions_to_env: true\n        env:\n          VERSION_ID: ${version_id}\n          BASE_SHA: \"${revision}\"\n          HEAD_SHA: \"${github_commit}\"\n        include_expansions_in_env: [PERF_URI_PRIVATE_ENDPOINT]\n        args: [*task-runner, perf-pr-comment]\n  run-enterprise-auth-tests:\n    - command: ec2.assume_role\n      params:\n        role_arn: \"${aws_test_secrets_role}\"\n    - command: subprocess.exec\n      params:\n        binary: bash\n        include_expansions_in_env: [AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN]\n        env:\n          TEST_ENTERPRISE_AUTH: plain\n        args: [*task-runner, setup-test]\n    - command: subprocess.exec\n      type: test\n      retry_on_failure: true\n      params:\n        binary: bash\n        args: [*task-runner, --silent, evg-test-enterprise-auth]\n  run-enterprise-gssapi-auth-tests:\n    - command: ec2.assume_role\n      params:\n        role_arn: \"${aws_test_secrets_role}\"\n    - command: subprocess.exec\n      params:\n        binary: bash\n        include_expansions_in_env: [AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN]\n        env:\n          TEST_ENTERPRISE_AUTH: gssapi\n        args: [*task-runner, setup-test]\n    - command: subprocess.exec\n      type: test\n      retry_on_failure: true\n      params:\n        binary: bash\n        args: [*task-runner, --silent, evg-test-enterprise-auth]\n  run-atlas-test:\n    - command: ec2.assume_role\n      params:\n        role_arn: \"${aws_test_secrets_role}\"\n    - command: subprocess.exec\n      params:\n        binary: bash\n        include_expansions_in_env: [AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN]\n        env:\n          TEST_ATLAS_CONNECT: \"1\"\n        args: [*task-runner, setup-test]\n    - command: subprocess.exec\n      type: test\n      retry_on_failure: true\n      params:\n        binary: bash\n        args: [*task-runner, test-atlas-connect]\n  run-ocsp-test:\n    - command: subprocess.exec\n      params:\n        binary: bash\n        env:\n          TOPOLOGY: server\n          AUTH: auth\n          SSL: ssl\n        include_expansions_in_env: [OCSP_ALGORITHM, MONGODB_URI]\n        args: [*task-runner, setup-test]\n    - command: subprocess.exec\n      type: test\n      retry_on_failure: true\n      params:\n        binary: bash\n        include_expansions_in_env: [OCSP_TLS_SHOULD_SUCCEED]\n        args: [*task-runner, evg-test-ocsp]\n  run-versioned-api-test:\n    - command: subprocess.exec\n      params:\n        binary: bash\n        env:\n          GO_BUILD_TAGS: cse\n        include_expansions_in_env: [AUTH, SSL, MONGODB_URI, TOPOLOGY, MONGO_GO_DRIVER_COMPRESSOR, REQUIRE_API_VERSION, SKIP_CRYPT_SHARED_LIB, CRYPT_SHARED_LIB_PATH]\n        args: [*task-runner, setup-test]\n    - command: subprocess.exec\n      type: test\n      retry_on_failure: true\n      params:\n        binary: bash\n        args: [*task-runner, evg-test-versioned-api]\n  run-load-balancer-tests:\n    - command: subprocess.exec\n      params:\n        binary: bash\n        include_expansions_in_env: [SINGLE_MONGOS_LB_URI, MULTI_MONGOS_LB_URI, AUTH, SSL, MONGO_GO_DRIVER_COMPRESSOR]\n        env:\n          LOAD_BALANCER: \"true\"\n        args: [*task-runner, setup-test]\n    - command: subprocess.exec\n      type: test\n      retry_on_failure: true\n      params:\n        binary: bash\n        args: [*task-runner, evg-test-load-balancers]\n  run-docker-test:\n    - command: subprocess.exec\n      type: test\n      params:\n        binary: \"bash\"\n        env:\n          TASKFILE_TARGET: test-short\n        args: [*task-runner, run-docker]\n    - command: subprocess.exec\n      type: test\n      params:\n        binary: \"bash\"\n        env:\n          TOPOLOGY: sharded_cluster\n          TASKFILE_TARGET: test-short\n        args: [*task-runner, run-docker]\n  run-goleak-test:\n    - command: subprocess.exec\n      type: test\n      params:\n        binary: \"bash\"\n        include_expansions_in_env: [\"MONGODB_URI\"]\n        args: [*task-runner, test-goleak]\n  \"run oidc k8s test\":\n    - command: subprocess.exec\n      type: test\n      params:\n        binary: bash\n        include_expansions_in_env: [AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN, VARIANT, DRIVERS_TOOLS]\n        env:\n          OIDC_ENV: k8s\n        args: [*task-runner, test-oidc-remote]\n  run-ocsp-server:\n    - command: subprocess.exec\n      params:\n        binary: bash\n        background: true\n        include_expansions_in_env: [SERVER_TYPE, OCSP_ALGORITHM]\n        args:\n          - ${DRIVERS_TOOLS}/.evergreen/ocsp/setup.sh\n  run-load-balancer:\n    - command: subprocess.exec\n      params:\n        binary: \"bash\"\n        include_expansions_in_env: [\"MONGODB-AWS\", \"MONGODB_URI\"]\n        args: [\"${DRIVERS_TOOLS}/.evergreen/run-load-balancer.sh\", start]\n    - command: expansions.update\n      params:\n        file: lb-expansion.yml\n  run-search-index-tests:\n    - command: subprocess.exec\n      type: test\n      params:\n        binary: \"bash\"\n        env:\n          SEARCH_INDEX_URI: \"${SEARCH_INDEX_URI}\"\n        args: [*task-runner, evg-test-search-index]\n  add-aws-auth-variables-to-file:\n    - command: ec2.assume_role\n      params:\n        role_arn: ${aws_test_secrets_role}\n    - command: subprocess.exec\n      type: test\n      params:\n        include_expansions_in_env: [\"AWS_ACCESS_KEY_ID\", \"AWS_SECRET_ACCESS_KEY\", \"AWS_SESSION_TOKEN\"]\n        binary: \"bash\"\n        args:\n          - ${DRIVERS_TOOLS}/.evergreen/auth_aws/setup-secrets.sh\n  run-aws-auth-test-with-regular-aws-credentials:\n    - command: subprocess.exec\n      type: test\n      params:\n        binary: \"bash\"\n        env:\n          AWS_TEST: regular\n        args: [*task-runner, evg-test-aws]\n  run-aws-auth-test-with-assume-role-credentials:\n    - command: subprocess.exec\n      type: test\n      params:\n        binary: \"bash\"\n        env:\n          AWS_TEST: assume-role\n        args: [*task-runner, evg-test-aws]\n  run-aws-auth-test-with-aws-EC2-credentials:\n    - command: subprocess.exec\n      type: test\n      params:\n        binary: bash\n        include_expansions_in_env: [SKIP_EC2_AUTH_TEST]\n        env:\n          AWS_TEST: ec2\n        args: [*task-runner, evg-test-aws]\n  run-aws-auth-test-with-aws-credentials-as-environment-variables:\n    - command: subprocess.exec\n      type: test\n      params:\n        binary: \"bash\"\n        env:\n          AWS_TEST: env-creds\n        args: [*task-runner, evg-test-aws]\n  run-aws-auth-test-with-aws-credentials-and-session-token-as-environment-variables:\n    - command: subprocess.exec\n      type: test\n      params:\n        binary: \"bash\"\n        env:\n          AWS_TEST: session-creds\n        args: [*task-runner, evg-test-aws]\n  run-aws-ECS-auth-test:\n    - command: subprocess.exec\n      type: test\n      params:\n        binary: \"bash\"\n        include_expansions_in_env: [SKIP_ECS_AUTH_TEST]\n        args: [*task-runner, evg-test-aws-ecs]\n  run-aws-auth-test-with-aws-web-identity-credentials:\n    - command: subprocess.exec\n      type: test\n      params:\n        binary: bash\n        include_expansions_in_env: [SKIP_WEB_IDENTITY_AUTH_TEST]\n        env:\n          AWS_TEST: web-identity\n        args: [*task-runner, evg-test-aws]\n    - command: subprocess.exec\n      type: test\n      params:\n        binary: bash\n        env:\n          AWS_ROLE_SESSION_NAME: test\n          AWS_TEST: web-identity\n        include_expansions_in_env: [SKIP_WEB_IDENTITY_AUTH_TEST]\n        args: [*task-runner, evg-test-aws]\n  start-cse-servers:\n    - command: ec2.assume_role\n      params:\n        role_arn: ${aws_test_secrets_role}\n    - command: subprocess.exec\n      params:\n        working_dir: src/go.mongodb.org/mongo-driver\n        binary: bash\n        background: true\n        include_expansions_in_env: [\"AWS_ACCESS_KEY_ID\", \"AWS_SECRET_ACCESS_KEY\", \"AWS_SESSION_TOKEN\", \"DRIVERS_TOOLS\"]\n        # This cannot use task because it will hang on Windows.\n        args: [etc/setup-encryption.sh]\n    - command: subprocess.exec\n      params:\n        binary: bash\n        args: [\"${DRIVERS_TOOLS}/.evergreen/csfle/await-servers.sh\"]\n  run-kmip-tests:\n    - command: subprocess.exec\n      params:\n        binary: \"bash\"\n        env:\n          GO_BUILD_TAGS: cse\n        include_expansions_in_env: [AUTH, SSL, MONGODB_URI, TOPOLOGY, MONGO_GO_DRIVER_COMPRESSOR]\n        args: [*task-runner, setup-test]\n    - command: subprocess.exec\n      type: test\n      retry_on_failure: true\n      params:\n        binary: \"bash\"\n        env:\n          KMS_MOCK_SERVERS_RUNNING: \"true\"\n        args: [*task-runner, evg-test-kmip]\n  run-client-side-encryption-test:\n    - command: subprocess.exec\n      params:\n        binary: \"bash\"\n        env:\n          GO_BUILD_TAGS: cse\n        include_expansions_in_env: [AUTH, SSL, MONGODB_URI, TOPOLOGY, MONGO_GO_DRIVER_COMPRESSOR, CRYPT_SHARED_LIB_PATH]\n        args: [*task-runner, setup-test]\n    - command: subprocess.exec\n      type: test\n      retry_on_failure: true\n      params:\n        binary: \"bash\"\n        env:\n          KMS_MOCK_SERVERS_RUNNING: \"true\"\n          KMS_FAILPOINT_CA_FILE: \"${DRIVERS_TOOLS}/.evergreen/x509gen/ca.pem\"\n          KMS_FAILPOINT_SERVER_RUNNING: \"true\"\n        args: [*task-runner, evg-test-client-side-encryption]\n  run-fuzz-tests:\n    - command: subprocess.exec\n      type: test\n      params:\n        binary: \"bash\"\n        args: [*task-runner, run-fuzz]\npre:\n  - func: setup-system\npost:\n  - func: teardown\n  - func: handle-test-artifacts\ntasks:\n  - name: static-analysis\n    tags: [\"static-analysis\"]\n    commands:\n      - command: subprocess.exec\n        params:\n          binary: bash\n          args: [*task-runner, check-fmt, check-license, check-modules, lint]\n  - name: govulncheck\n    tags: [\"static-analysis\"]\n    commands:\n      - command: subprocess.exec\n        type: test\n        params:\n          binary: bash\n          args: [*task-runner, govulncheck]\n  - name: pull-request-helpers\n    allowed_requesters: [\"patch\", \"github_pr\"]\n    commands:\n      - func: assume-test-secrets-ec2-role\n      - func: \"create-api-report\"\n  - name: backport-pr\n    allowed_requesters: [\"commit\"]\n    commands:\n      - func: \"backport pr\"\n  - name: perf\n    tags: [\"performance\"]\n    exec_timeout_secs: 7200\n    commands:\n      - func: bootstrap-mongo-orchestration\n        vars:\n          VERSION: \"v6.0-perf\"\n          TOPOLOGY: \"server\"\n          AUTH: \"noauth\"\n          SSL: \"nossl\"\n          SKIP_LEGACY_SHELL: \"true\"\n      - command: subprocess.exec\n        params:\n          binary: bash\n          args: [*task-runner, driver-benchmark]\n      - func: assume-test-secrets-ec2-role\n      - func: send-perf-data\n      - func: send-perf-pr-comment\n  - name: test-standalone-noauth-nossl\n    tags: [\"test\", \"standalone\"]\n    commands:\n      - func: bootstrap-mongo-orchestration\n        vars:\n          TOPOLOGY: \"server\"\n          AUTH: \"noauth\"\n          SSL: \"nossl\"\n      - func: start-cse-servers\n      - func: run-tests\n        vars:\n          TOPOLOGY: \"server\"\n          AUTH: \"noauth\"\n          SSL: \"nossl\"\n  - name: test-standalone-noauth-nossl-snappy-compression\n    tags: [\"test\", \"standalone\", \"compression\", \"snappy\"]\n    commands:\n      - func: bootstrap-mongo-orchestration\n        vars:\n          TOPOLOGY: \"server\"\n          AUTH: \"noauth\"\n          SSL: \"nossl\"\n      - func: start-cse-servers\n      - func: run-tests\n        vars:\n          TOPOLOGY: \"server\"\n          AUTH: \"noauth\"\n          SSL: \"nossl\"\n          MONGO_GO_DRIVER_COMPRESSOR: \"snappy\"\n  - name: test-standalone-noauth-nossl-zlib-compression\n    tags: [\"test\", \"standalone\", \"compression\", \"zlib\"]\n    commands:\n      - func: bootstrap-mongo-orchestration\n        vars:\n          TOPOLOGY: \"server\"\n          AUTH: \"noauth\"\n          SSL: \"nossl\"\n      - func: start-cse-servers\n      - func: run-tests\n        vars:\n          TOPOLOGY: \"server\"\n          AUTH: \"noauth\"\n          SSL: \"nossl\"\n          MONGO_GO_DRIVER_COMPRESSOR: \"zlib\"\n  - name: test-standalone-noauth-nossl-zstd-compression\n    tags: [\"test\", \"standalone\", \"compression\", \"zstd\"]\n    commands:\n      - func: bootstrap-mongo-orchestration\n        vars:\n          TOPOLOGY: \"server\"\n          AUTH: \"noauth\"\n          SSL: \"nossl\"\n      - func: start-cse-servers\n      - func: run-tests\n        vars:\n          TOPOLOGY: \"server\"\n          AUTH: \"noauth\"\n          SSL: \"nossl\"\n          MONGO_GO_DRIVER_COMPRESSOR: \"zstd\"\n  - name: test-standalone-auth-ssl\n    tags: [\"test\", \"standalone\", \"authssl\"]\n    commands:\n      - func: bootstrap-mongo-orchestration\n        vars:\n          TOPOLOGY: \"server\"\n          AUTH: \"auth\"\n          SSL: \"ssl\"\n      - func: start-cse-servers\n      - func: run-tests\n        vars:\n          TOPOLOGY: \"server\"\n          AUTH: \"auth\"\n          SSL: \"ssl\"\n  - name: test-standalone-auth-nossl\n    tags: [\"test\", \"standalone\", \"authssl\"]\n    commands:\n      - func: bootstrap-mongo-orchestration\n        vars:\n          TOPOLOGY: \"server\"\n          AUTH: \"auth\"\n          SSL: \"nossl\"\n      - func: start-cse-servers\n      - func: run-tests\n        vars:\n          TOPOLOGY: \"server\"\n          AUTH: \"auth\"\n          SSL: \"nossl\"\n  - name: test-standalone-auth-ssl-snappy-compression\n    tags: [\"test\", \"standalone\", \"authssl\", \"compression\", \"snappy\"]\n    commands:\n      - func: bootstrap-mongo-orchestration\n        vars:\n          TOPOLOGY: \"server\"\n          AUTH: \"auth\"\n          SSL: \"ssl\"\n      - func: start-cse-servers\n      - func: run-tests\n        vars:\n          TOPOLOGY: \"server\"\n          AUTH: \"auth\"\n          SSL: \"ssl\"\n          MONGO_GO_DRIVER_COMPRESSOR: \"snappy\"\n  - name: test-standalone-auth-ssl-zlib-compression\n    tags: [\"test\", \"standalone\", \"authssl\", \"compression\", \"zlib\"]\n    commands:\n      - func: bootstrap-mongo-orchestration\n        vars:\n          TOPOLOGY: \"server\"\n          AUTH: \"auth\"\n          SSL: \"ssl\"\n      - func: start-cse-servers\n      - func: run-tests\n        vars:\n          TOPOLOGY: \"server\"\n          AUTH: \"auth\"\n          SSL: \"ssl\"\n          MONGO_GO_DRIVER_COMPRESSOR: \"zlib\"\n  - name: test-standalone-auth-ssl-zstd-compression\n    tags: [\"test\", \"standalone\", \"authssl\", \"compression\", \"zstd\"]\n    commands:\n      - func: bootstrap-mongo-orchestration\n        vars:\n          TOPOLOGY: \"server\"\n          AUTH: \"auth\"\n          SSL: \"ssl\"\n      - func: start-cse-servers\n      - func: run-tests\n        vars:\n          TOPOLOGY: \"server\"\n          AUTH: \"auth\"\n          SSL: \"ssl\"\n          MONGO_GO_DRIVER_COMPRESSOR: \"zstd\"\n  - name: test-ocsp-rsa-valid-cert-server-staples\n    tags: [\"ocsp\", \"ocsp-rsa\", \"ocsp-staple\"]\n    commands:\n      - func: run-ocsp-server\n        vars:\n          OCSP_ALGORITHM: \"rsa\"\n          SERVER_TYPE: valid\n      - func: ocsp-bootstrap-mongo-orchestration\n        vars:\n          ORCHESTRATION_FILE: \"rsa-basic-tls-ocsp-mustStaple.json\"\n      - func: run-ocsp-test\n        vars:\n          OCSP_ALGORITHM: \"rsa\"\n          OCSP_TLS_SHOULD_SUCCEED: \"true\"\n  - name: test-ocsp-rsa-invalid-cert-server-staples\n    tags: [\"ocsp\", \"ocsp-rsa\", \"ocsp-staple\"]\n    commands:\n      - func: run-ocsp-server\n        vars:\n          OCSP_ALGORITHM: \"rsa\"\n          SERVER_TYPE: revoked\n      - func: ocsp-bootstrap-mongo-orchestration\n        vars:\n          ORCHESTRATION_FILE: \"rsa-basic-tls-ocsp-mustStaple.json\"\n      - func: run-ocsp-test\n        vars:\n          OCSP_ALGORITHM: \"rsa\"\n          OCSP_TLS_SHOULD_SUCCEED: \"false\"\n  - name: test-ocsp-rsa-valid-cert-server-does-not-staple\n    tags: [\"ocsp\", \"ocsp-rsa\"]\n    commands:\n      - func: run-ocsp-server\n        vars:\n          OCSP_ALGORITHM: \"rsa\"\n          SERVER_TYPE: valid\n      - func: ocsp-bootstrap-mongo-orchestration\n        vars:\n          ORCHESTRATION_FILE: \"rsa-basic-tls-ocsp-disableStapling.json\"\n      - func: run-ocsp-test\n        vars:\n          OCSP_ALGORITHM: \"rsa\"\n          OCSP_TLS_SHOULD_SUCCEED: \"true\"\n  - name: test-ocsp-rsa-invalid-cert-server-does-not-staple\n    tags: [\"ocsp\", \"ocsp-rsa\"]\n    commands:\n      - func: run-ocsp-server\n        vars:\n          OCSP_ALGORITHM: \"rsa\"\n          SERVER_TYPE: revoked\n      - func: ocsp-bootstrap-mongo-orchestration\n        vars:\n          ORCHESTRATION_FILE: \"rsa-basic-tls-ocsp-disableStapling.json\"\n      - func: run-ocsp-test\n        vars:\n          OCSP_ALGORITHM: \"rsa\"\n          OCSP_TLS_SHOULD_SUCCEED: \"false\"\n  - name: test-ocsp-rsa-soft-fail\n    tags: [\"ocsp\", \"ocsp-rsa\"]\n    commands:\n      - func: ocsp-bootstrap-mongo-orchestration\n        vars:\n          ORCHESTRATION_FILE: \"rsa-basic-tls-ocsp-disableStapling.json\"\n      - func: run-ocsp-test\n        vars:\n          OCSP_ALGORITHM: \"rsa\"\n          OCSP_TLS_SHOULD_SUCCEED: \"true\"\n  - name: test-ocsp-rsa-malicious-invalid-cert-mustStaple-server-does-not-staple\n    tags: [\"ocsp\", \"ocsp-rsa\"]\n    commands:\n      - func: run-ocsp-server\n        vars:\n          OCSP_ALGORITHM: \"rsa\"\n          SERVER_TYPE: revoked\n      - func: ocsp-bootstrap-mongo-orchestration\n        vars:\n          ORCHESTRATION_FILE: \"rsa-basic-tls-ocsp-mustStaple-disableStapling.json\"\n      - func: run-ocsp-test\n        vars:\n          OCSP_ALGORITHM: \"rsa\"\n          OCSP_TLS_SHOULD_SUCCEED: \"false\"\n  - name: test-ocsp-rsa-malicious-no-responder-mustStaple-server-does-not-staple\n    tags: [\"ocsp\", \"ocsp-rsa\"]\n    commands:\n      - func: ocsp-bootstrap-mongo-orchestration\n        vars:\n          ORCHESTRATION_FILE: \"rsa-basic-tls-ocsp-mustStaple-disableStapling.json\"\n      - func: run-ocsp-test\n        vars:\n          OCSP_ALGORITHM: \"rsa\"\n          OCSP_TLS_SHOULD_SUCCEED: \"false\"\n  - name: test-ocsp-rsa-delegate-valid-cert-server-staples\n    tags: [\"ocsp\", \"ocsp-rsa\", \"ocsp-staple\"]\n    commands:\n      - func: run-ocsp-server\n        vars:\n          OCSP_ALGORITHM: \"rsa\"\n          SERVER_TYPE: valid-delegate\n      - func: ocsp-bootstrap-mongo-orchestration\n        vars:\n          ORCHESTRATION_FILE: \"rsa-basic-tls-ocsp-mustStaple.json\"\n      - func: run-ocsp-test\n        vars:\n          OCSP_ALGORITHM: \"rsa\"\n          OCSP_TLS_SHOULD_SUCCEED: \"true\"\n  - name: test-ocsp-rsa-delegate-invalid-cert-server-staples\n    tags: [\"ocsp\", \"ocsp-rsa\", \"ocsp-staple\"]\n    commands:\n      - func: run-ocsp-server\n        vars:\n          OCSP_ALGORITHM: \"rsa\"\n          SERVER_TYPE: revoked-delegate\n      - func: ocsp-bootstrap-mongo-orchestration\n        vars:\n          ORCHESTRATION_FILE: \"rsa-basic-tls-ocsp-mustStaple.json\"\n      - func: run-ocsp-test\n        vars:\n          OCSP_ALGORITHM: \"rsa\"\n          OCSP_TLS_SHOULD_SUCCEED: \"false\"\n  - name: test-ocsp-rsa-delegate-valid-cert-server-does-not-staple\n    tags: [\"ocsp\", \"ocsp-rsa\"]\n    commands:\n      - func: run-ocsp-server\n        vars:\n          OCSP_ALGORITHM: \"rsa\"\n          SERVER_TYPE: valid-delegate\n      - func: ocsp-bootstrap-mongo-orchestration\n        vars:\n          ORCHESTRATION_FILE: \"rsa-basic-tls-ocsp-disableStapling.json\"\n      - func: run-ocsp-test\n        vars:\n          OCSP_ALGORITHM: \"rsa\"\n          OCSP_TLS_SHOULD_SUCCEED: \"true\"\n  - name: test-ocsp-rsa-delegate-invalid-cert-server-does-not-staple\n    tags: [\"ocsp\", \"ocsp-rsa\"]\n    commands:\n      - func: run-ocsp-server\n        vars:\n          OCSP_ALGORITHM: \"rsa\"\n          SERVER_TYPE: revoked-delegate\n      - func: ocsp-bootstrap-mongo-orchestration\n        vars:\n          ORCHESTRATION_FILE: \"rsa-basic-tls-ocsp-disableStapling.json\"\n      - func: run-ocsp-test\n        vars:\n          OCSP_ALGORITHM: \"rsa\"\n          OCSP_TLS_SHOULD_SUCCEED: \"false\"\n  - name: test-ocsp-rsa-delegate-malicious-invalid-cert-mustStaple-server-does-not-staple\n    tags: [\"ocsp\", \"ocsp-rsa\"]\n    commands:\n      - func: run-ocsp-server\n        vars:\n          OCSP_ALGORITHM: \"rsa\"\n          SERVER_TYPE: revoked-delegate\n      - func: ocsp-bootstrap-mongo-orchestration\n        vars:\n          ORCHESTRATION_FILE: \"rsa-basic-tls-ocsp-mustStaple-disableStapling.json\"\n      - func: run-ocsp-test\n        vars:\n          OCSP_ALGORITHM: \"rsa\"\n          OCSP_TLS_SHOULD_SUCCEED: \"false\"\n  - name: test-ocsp-ecdsa-valid-cert-server-staples\n    tags: [\"ocsp\", \"ocsp-ecdsa\", \"ocsp-staple\"]\n    commands:\n      - func: run-ocsp-server\n        vars:\n          OCSP_ALGORITHM: \"ecdsa\"\n          SERVER_TYPE: valid\n      - func: ocsp-bootstrap-mongo-orchestration\n        vars:\n          ORCHESTRATION_FILE: \"ecdsa-basic-tls-ocsp-mustStaple.json\"\n      - func: run-ocsp-test\n        vars:\n          OCSP_ALGORITHM: \"ecdsa\"\n          OCSP_TLS_SHOULD_SUCCEED: \"true\"\n  - name: test-ocsp-ecdsa-invalid-cert-server-staples\n    tags: [\"ocsp\", \"ocsp-ecdsa\", \"ocsp-staple\"]\n    commands:\n      - func: run-ocsp-server\n        vars:\n          OCSP_ALGORITHM: \"ecdsa\"\n          SERVER_TYPE: revoked\n      - func: ocsp-bootstrap-mongo-orchestration\n        vars:\n          ORCHESTRATION_FILE: \"ecdsa-basic-tls-ocsp-mustStaple.json\"\n      - func: run-ocsp-test\n        vars:\n          OCSP_ALGORITHM: \"ecdsa\"\n          OCSP_TLS_SHOULD_SUCCEED: \"false\"\n  - name: test-ocsp-ecdsa-valid-cert-server-does-not-staple\n    tags: [\"ocsp\", \"ocsp-ecdsa\"]\n    commands:\n      - func: run-ocsp-server\n        vars:\n          OCSP_ALGORITHM: \"ecdsa\"\n          SERVER_TYPE: valid\n      - func: ocsp-bootstrap-mongo-orchestration\n        vars:\n          ORCHESTRATION_FILE: \"ecdsa-basic-tls-ocsp-disableStapling.json\"\n      - func: run-ocsp-test\n        vars:\n          OCSP_ALGORITHM: \"ecdsa\"\n          OCSP_TLS_SHOULD_SUCCEED: \"true\"\n  - name: test-ocsp-ecdsa-invalid-cert-server-does-not-staple\n    tags: [\"ocsp\", \"ocsp-ecdsa\"]\n    commands:\n      - func: run-ocsp-server\n        vars:\n          OCSP_ALGORITHM: \"ecdsa\"\n          SERVER_TYPE: revoked\n      - func: ocsp-bootstrap-mongo-orchestration\n        vars:\n          ORCHESTRATION_FILE: \"ecdsa-basic-tls-ocsp-disableStapling.json\"\n      - func: run-ocsp-test\n        vars:\n          OCSP_ALGORITHM: \"ecdsa\"\n          OCSP_TLS_SHOULD_SUCCEED: \"false\"\n  - name: test-ocsp-ecdsa-soft-fail\n    tags: [\"ocsp\", \"ocsp-ecdsa\"]\n    commands:\n      - func: ocsp-bootstrap-mongo-orchestration\n        vars:\n          ORCHESTRATION_FILE: \"ecdsa-basic-tls-ocsp-disableStapling.json\"\n      - func: run-ocsp-test\n        vars:\n          OCSP_ALGORITHM: \"ecdsa\"\n          OCSP_TLS_SHOULD_SUCCEED: \"true\"\n  - name: test-ocsp-ecdsa-malicious-invalid-cert-mustStaple-server-does-not-staple\n    tags: [\"ocsp\", \"ocsp-ecdsa\"]\n    commands:\n      - func: run-ocsp-server\n        vars:\n          OCSP_ALGORITHM: \"ecdsa\"\n          SERVER_TYPE: revoked\n      - func: ocsp-bootstrap-mongo-orchestration\n        vars:\n          ORCHESTRATION_FILE: \"ecdsa-basic-tls-ocsp-mustStaple-disableStapling.json\"\n      - func: run-ocsp-test\n        vars:\n          OCSP_ALGORITHM: \"ecdsa\"\n          OCSP_TLS_SHOULD_SUCCEED: \"false\"\n  - name: test-ocsp-ecdsa-malicious-no-responder-mustStaple-server-does-not-staple\n    tags: [\"ocsp\", \"ocsp-ecdsa\"]\n    commands:\n      - func: ocsp-bootstrap-mongo-orchestration\n        vars:\n          ORCHESTRATION_FILE: \"ecdsa-basic-tls-ocsp-mustStaple-disableStapling.json\"\n      - func: run-ocsp-test\n        vars:\n          OCSP_ALGORITHM: \"ecdsa\"\n          OCSP_TLS_SHOULD_SUCCEED: \"false\"\n  - name: test-ocsp-ecdsa-delegate-valid-cert-server-staples\n    tags: [\"ocsp\", \"ocsp-ecdsa\", \"ocsp-staple\"]\n    commands:\n      - func: run-ocsp-server\n        vars:\n          OCSP_ALGORITHM: \"ecdsa\"\n          SERVER_TYPE: valid-delegate\n      - func: ocsp-bootstrap-mongo-orchestration\n        vars:\n          ORCHESTRATION_FILE: \"ecdsa-basic-tls-ocsp-mustStaple.json\"\n      - func: run-ocsp-test\n        vars:\n          OCSP_ALGORITHM: \"ecdsa\"\n          OCSP_TLS_SHOULD_SUCCEED: \"true\"\n  - name: test-ocsp-ecdsa-delegate-invalid-cert-server-staples\n    tags: [\"ocsp\", \"ocsp-ecdsa\", \"ocsp-staple\"]\n    commands:\n      - func: run-ocsp-server\n        vars:\n          OCSP_ALGORITHM: \"ecdsa\"\n          SERVER_TYPE: revoked-delegate\n      - func: ocsp-bootstrap-mongo-orchestration\n        vars:\n          ORCHESTRATION_FILE: \"ecdsa-basic-tls-ocsp-mustStaple.json\"\n      - func: run-ocsp-test\n        vars:\n          OCSP_ALGORITHM: \"ecdsa\"\n          OCSP_TLS_SHOULD_SUCCEED: \"false\"\n  - name: test-ocsp-ecdsa-delegate-valid-cert-server-does-not-staple\n    tags: [\"ocsp\", \"ocsp-ecdsa\"]\n    commands:\n      - func: run-ocsp-server\n        vars:\n          OCSP_ALGORITHM: \"ecdsa\"\n          SERVER_TYPE: valid-delegate\n      - func: ocsp-bootstrap-mongo-orchestration\n        vars:\n          ORCHESTRATION_FILE: \"ecdsa-basic-tls-ocsp-disableStapling.json\"\n      - func: run-ocsp-test\n        vars:\n          OCSP_ALGORITHM: \"ecdsa\"\n          OCSP_TLS_SHOULD_SUCCEED: \"true\"\n  - name: test-ocsp-ecdsa-delegate-invalid-cert-server-does-not-staple\n    tags: [\"ocsp\", \"ocsp-ecdsa\"]\n    commands:\n      - func: run-ocsp-server\n        vars:\n          OCSP_ALGORITHM: \"ecdsa\"\n          SERVER_TYPE: revoked-delegate\n      - func: ocsp-bootstrap-mongo-orchestration\n        vars:\n          ORCHESTRATION_FILE: \"ecdsa-basic-tls-ocsp-disableStapling.json\"\n      - func: run-ocsp-test\n        vars:\n          OCSP_ALGORITHM: \"ecdsa\"\n          OCSP_TLS_SHOULD_SUCCEED: \"false\"\n  - name: test-ocsp-ecdsa-delegate-malicious-invalid-cert-mustStaple-server-does-not-staple\n    tags: [\"ocsp\", \"ocsp-ecdsa\"]\n    commands:\n      - func: run-ocsp-server\n        vars:\n          OCSP_ALGORITHM: \"ecdsa\"\n          SERVER_TYPE: revoked-delegate\n      - func: ocsp-bootstrap-mongo-orchestration\n        vars:\n          ORCHESTRATION_FILE: \"ecdsa-basic-tls-ocsp-mustStaple-disableStapling.json\"\n      - func: run-ocsp-test\n        vars:\n          OCSP_ALGORITHM: \"ecdsa\"\n          OCSP_TLS_SHOULD_SUCCEED: \"false\"\n  - name: test-docker-runner\n    commands:\n      - func: bootstrap-mongo-orchestration\n      - func: run-docker-test\n  - name: test-goroutine-leaks-replicaset\n    tags: [\"goleak\"]\n    commands:\n      - func: bootstrap-mongo-orchestration\n        vars:\n          TOPOLOGY: \"replica_set\"\n          AUTH: \"noauth\"\n          SSL: \"nossl\"\n      - func: run-goleak-test\n  - name: test-goroutine-leaks-sharded\n    tags: [\"goleak\"]\n    commands:\n      - func: bootstrap-mongo-orchestration\n        vars:\n          TOPOLOGY: \"sharded_cluster\"\n          AUTH: \"noauth\"\n          SSL: \"nossl\"\n      - func: run-goleak-test\n  - name: test-load-balancer-noauth-nossl\n    tags: [\"load-balancer\"]\n    commands:\n      - func: bootstrap-mongo-orchestration\n        vars:\n          TOPOLOGY: \"sharded_cluster\"\n          AUTH: \"noauth\"\n          SSL: \"nossl\"\n          LOAD_BALANCER: \"true\"\n      - func: run-load-balancer\n      - func: run-load-balancer-tests\n        vars:\n          AUTH: \"noauth\"\n          SSL: \"nossl\"\n  - name: test-load-balancer-auth-ssl\n    tags: [\"load-balancer\"]\n    commands:\n      - func: bootstrap-mongo-orchestration\n        vars:\n          TOPOLOGY: \"sharded_cluster\"\n          AUTH: \"auth\"\n          SSL: \"ssl\"\n          LOAD_BALANCER: \"true\"\n      - func: run-load-balancer\n      - func: run-load-balancer-tests\n        vars:\n          AUTH: \"auth\"\n          SSL: \"ssl\"\n  - name: test-race\n    tags: [\"race\"]\n    commands:\n      - func: bootstrap-mongo-orchestration\n        vars:\n          TOPOLOGY: \"replica_set\"\n          AUTH: \"noauth\"\n          SSL: \"nossl\"\n      - func: start-cse-servers\n      - func: run-tests\n        vars:\n          TOPOLOGY: \"replica_set\"\n          AUTH: \"noauth\"\n          SSL: \"nossl\"\n          RACE: \"-race\"\n  - name: test-replicaset-noauth-nossl\n    tags: [\"test\", \"replicaset\"]\n    commands:\n      - func: bootstrap-mongo-orchestration\n        vars:\n          TOPOLOGY: \"replica_set\"\n          AUTH: \"noauth\"\n          SSL: \"nossl\"\n      - func: start-cse-servers\n      - func: run-tests\n        vars:\n          TOPOLOGY: \"replica_set\"\n          AUTH: \"noauth\"\n          SSL: \"nossl\"\n  - name: test-replicaset-auth-ssl\n    tags: [\"test\", \"replicaset\", \"authssl\"]\n    commands:\n      - func: bootstrap-mongo-orchestration\n        vars:\n          TOPOLOGY: \"replica_set\"\n          AUTH: \"auth\"\n          SSL: \"ssl\"\n      - func: start-cse-servers\n      - func: run-tests\n        vars:\n          TOPOLOGY: \"replica_set\"\n          AUTH: \"auth\"\n          SSL: \"ssl\"\n  - name: test-replicaset-auth-ssl-mongocryptd\n    tags: [\"test\", \"replicaset\", \"authssl\", \"mongocryptd\"]\n    commands:\n      - func: bootstrap-mongo-orchestration\n        vars:\n          TOPOLOGY: \"replica_set\"\n          AUTH: \"auth\"\n          SSL: \"ssl\"\n      - func: start-cse-servers\n      - func: run-tests\n        vars:\n          TOPOLOGY: \"replica_set\"\n          AUTH: \"auth\"\n          SSL: \"ssl\"\n          # Don't use the crypt_shared library, which should cause all of the tests to fall\n          # back to using mongocryptd instead of crypt_shared.\n          SKIP_CRYPT_SHARED_LIB: \"true\"\n  - name: test-replicaset-auth-nossl\n    tags: [\"test\", \"replicaset\", \"authssl\"]\n    commands:\n      - func: bootstrap-mongo-orchestration\n        vars:\n          TOPOLOGY: \"replica_set\"\n          AUTH: \"auth\"\n          SSL: \"nossl\"\n      - func: start-cse-servers\n      - func: run-tests\n        vars:\n          TOPOLOGY: \"replica_set\"\n          AUTH: \"auth\"\n          SSL: \"nossl\"\n  - name: test-sharded-noauth-nossl\n    tags: [\"test\", \"sharded\"]\n    commands:\n      - func: bootstrap-mongo-orchestration\n        vars:\n          TOPOLOGY: \"sharded_cluster\"\n          AUTH: \"noauth\"\n          SSL: \"nossl\"\n      - func: start-cse-servers\n      - func: run-tests\n        vars:\n          TOPOLOGY: \"sharded_cluster\"\n          AUTH: \"noauth\"\n          SSL: \"nossl\"\n  - name: test-sharded-noauth-nossl-snappy-compression\n    tags: [\"test\", \"sharded\", \"compression\", \"snappy\"]\n    commands:\n      - func: bootstrap-mongo-orchestration\n        vars:\n          TOPOLOGY: \"sharded_cluster\"\n          AUTH: \"noauth\"\n          SSL: \"nossl\"\n      - func: start-cse-servers\n      - func: run-tests\n        vars:\n          TOPOLOGY: \"sharded_cluster\"\n          AUTH: \"noauth\"\n          SSL: \"nossl\"\n          MONGO_GO_DRIVER_COMPRESSOR: \"snappy\"\n  - name: test-sharded-noauth-nossl-zlib-compression\n    tags: [\"test\", \"sharded\", \"compression\", \"zlib\"]\n    commands:\n      - func: bootstrap-mongo-orchestration\n        vars:\n          TOPOLOGY: \"sharded_cluster\"\n          AUTH: \"noauth\"\n          SSL: \"nossl\"\n      - func: start-cse-servers\n      - func: run-tests\n        vars:\n          TOPOLOGY: \"sharded_cluster\"\n          AUTH: \"noauth\"\n          SSL: \"nossl\"\n          MONGO_GO_DRIVER_COMPRESSOR: \"zlib\"\n  - name: test-sharded-noauth-nossl-zstd-compression\n    tags: [\"test\", \"sharded\", \"compression\", \"zstd\"]\n    commands:\n      - func: bootstrap-mongo-orchestration\n        vars:\n          TOPOLOGY: \"sharded_cluster\"\n          AUTH: \"noauth\"\n          SSL: \"nossl\"\n      - func: start-cse-servers\n      - func: run-tests\n        vars:\n          TOPOLOGY: \"sharded_cluster\"\n          AUTH: \"noauth\"\n          SSL: \"nossl\"\n          MONGO_GO_DRIVER_COMPRESSOR: \"zstd\"\n  - name: test-sharded-auth-ssl\n    tags: [\"test\", \"sharded\", \"authssl\"]\n    commands:\n      - func: bootstrap-mongo-orchestration\n        vars:\n          TOPOLOGY: \"sharded_cluster\"\n          AUTH: \"auth\"\n          SSL: \"ssl\"\n      - func: start-cse-servers\n      - func: run-tests\n        vars:\n          TOPOLOGY: \"sharded_cluster\"\n          AUTH: \"auth\"\n          SSL: \"ssl\"\n  - name: test-sharded-auth-ssl-snappy-compression\n    tags: [\"test\", \"sharded\", \"authssl\", \"compression\", \"snappy\"]\n    commands:\n      - func: bootstrap-mongo-orchestration\n        vars:\n          TOPOLOGY: \"sharded_cluster\"\n          AUTH: \"auth\"\n          SSL: \"ssl\"\n      - func: start-cse-servers\n      - func: run-tests\n        vars:\n          TOPOLOGY: \"sharded_cluster\"\n          AUTH: \"auth\"\n          SSL: \"ssl\"\n          MONGO_GO_DRIVER_COMPRESSOR: \"snappy\"\n  - name: test-sharded-auth-ssl-zlib-compression\n    tags: [\"test\", \"sharded\", \"authssl\", \"compression\", \"zlib\"]\n    commands:\n      - func: bootstrap-mongo-orchestration\n        vars:\n          TOPOLOGY: \"sharded_cluster\"\n          AUTH: \"auth\"\n          SSL: \"ssl\"\n      - func: start-cse-servers\n      - func: run-tests\n        vars:\n          TOPOLOGY: \"sharded_cluster\"\n          AUTH: \"auth\"\n          SSL: \"ssl\"\n          MONGO_GO_DRIVER_COMPRESSOR: \"zlib\"\n  - name: test-sharded-auth-ssl-zstd-compression\n    tags: [\"test\", \"sharded\", \"authssl\", \"compression\", \"zstd\"]\n    commands:\n      - func: bootstrap-mongo-orchestration\n        vars:\n          TOPOLOGY: \"sharded_cluster\"\n          AUTH: \"auth\"\n          SSL: \"ssl\"\n      - func: start-cse-servers\n      - func: run-tests\n        vars:\n          TOPOLOGY: \"sharded_cluster\"\n          AUTH: \"auth\"\n          SSL: \"ssl\"\n          MONGO_GO_DRIVER_COMPRESSOR: \"zstd\"\n  - name: test-sharded-auth-nossl\n    tags: [\"test\", \"sharded\", \"authssl\"]\n    commands:\n      - func: bootstrap-mongo-orchestration\n        vars:\n          TOPOLOGY: \"sharded_cluster\"\n          AUTH: \"auth\"\n          SSL: \"nossl\"\n      - func: start-cse-servers\n      - func: run-tests\n        vars:\n          TOPOLOGY: \"sharded_cluster\"\n          AUTH: \"auth\"\n          SSL: \"nossl\"\n  - name: test-enterprise-auth-plain\n    tags: [\"test\", \"enterprise-auth\"]\n    commands:\n      - func: run-enterprise-auth-tests\n  - name: test-enterprise-auth-gssapi\n    tags: [\"test\", \"enterprise-auth\"]\n    commands:\n      - func: run-enterprise-gssapi-auth-tests\n        vars:\n          MONGO_GO_DRIVER_COMPRESSOR: \"snappy\"\n  # Build the compilecheck submodule with all supported versions of Go >=\n  # the minimum supported version.\n  - name: go-build\n    tags: [\"compile-check\"]\n    commands:\n      - command: subprocess.exec\n        params:\n          binary: bash\n          args: [*task-runner, build-compile-check-all]\n  # Build with the same Go version that we're using for tests.\n  - name: build\n    tags: [\"compile-check\"]\n    commands:\n      - command: subprocess.exec\n        params:\n          binary: bash\n          # Set the GO_VERSION to empty string to use the Go installation in the\n          # PATH.\n          env:\n            GO_VERSION: \"\"\n          args: [*task-runner, build]\n  - name: \"atlas-test\"\n    commands:\n      - func: \"run-atlas-test\"\n  - name: \"aws-auth-test\"\n    commands:\n      - func: bootstrap-mongo-orchestration\n        vars:\n          AUTH: \"auth\"\n          ORCHESTRATION_FILE: \"auth-aws.json\"\n          TOPOLOGY: \"server\"\n      - func: add-aws-auth-variables-to-file\n      - func: run-aws-auth-test-with-regular-aws-credentials\n      - func: run-aws-auth-test-with-assume-role-credentials\n      - func: run-aws-auth-test-with-aws-credentials-as-environment-variables\n      - func: run-aws-auth-test-with-aws-credentials-and-session-token-as-environment-variables\n      - func: run-aws-auth-test-with-aws-EC2-credentials\n      - func: run-aws-ECS-auth-test\n      - func: run-aws-auth-test-with-aws-web-identity-credentials\n  - name: \"test-standalone-versioned-api\"\n    tags: [\"versioned-api\"]\n    commands:\n      - func: bootstrap-mongo-orchestration\n        vars:\n          TOPOLOGY: \"server\"\n          AUTH: \"auth\"\n          SSL: \"nossl\"\n          REQUIRE_API_VERSION: true\n      - func: start-cse-servers\n      - func: run-versioned-api-test\n        vars:\n          TOPOLOGY: \"server\"\n          AUTH: \"auth\"\n          SSL: \"nossl\"\n          REQUIRE_API_VERSION: true\n  - name: \"test-sharded-versioned-api\"\n    tags: [\"versioned-api\"]\n    commands:\n      - func: bootstrap-mongo-orchestration\n        vars:\n          TOPOLOGY: \"sharded_cluster\"\n          AUTH: \"auth\"\n          SSL: \"nossl\"\n          REQUIRE_API_VERSION: true\n      - func: start-cse-servers\n      - func: run-versioned-api-test\n        vars:\n          TOPOLOGY: \"sharded_cluster\"\n          AUTH: \"auth\"\n          SSL: \"nossl\"\n          REQUIRE_API_VERSION: true\n  - name: \"test-standalone-versioned-api-test-commands\"\n    tags: [\"versioned-api\"]\n    commands:\n      - func: bootstrap-mongo-orchestration\n        vars:\n          TOPOLOGY: \"server\"\n          AUTH: \"noauth\"\n          SSL: \"nossl\"\n          ORCHESTRATION_FILE: \"versioned-api-testing.json\"\n      - func: start-cse-servers\n      - func: run-versioned-api-test\n        vars:\n          TOPOLOGY: \"server\"\n          AUTH: \"noauth\"\n          SSL: \"nossl\"\n  - name: \"test-kms-kmip\"\n    tags: [\"kms-kmip\"]\n    commands:\n      - func: bootstrap-mongo-orchestration\n        vars:\n          TOPOLOGY: \"server\"\n          AUTH: \"noauth\"\n          SSL: \"nossl\"\n      - func: start-cse-servers\n      - func: run-kmip-tests\n        vars:\n          TOPOLOGY: \"server\"\n          AUTH: \"noauth\"\n          SSL: \"nossl\"\n  - name: \"test-client-side-encryption\"\n    tags: [\"client-side-encryption-test\"]\n    commands:\n      - func: bootstrap-mongo-orchestration\n        vars:\n          TOPOLOGY: \"replica_set\"\n          AUTH: \"noauth\"\n          SSL: \"nossl\"\n      - func: start-cse-servers\n      - func: run-client-side-encryption-test\n        vars:\n          TOPOLOGY: \"replica_set\"\n          AUTH: \"noauth\"\n          SSL: \"nossl\"\n  - name: \"testgcpkms-task\"\n    commands:\n      - command: subprocess.exec\n        type: test\n        params:\n          binary: bash\n          args: [*task-runner, test-gcpkms]\n  - name: \"testgcpkms-fail-task\"\n    # testgcpkms-fail-task runs in a non-GCE environment.\n    # It is expected to fail to obtain GCE credentials.\n    commands:\n      - command: subprocess.exec\n        type: test\n        params:\n          binary: bash\n          env:\n            EXPECT_ERROR: \"1\"\n          args: [*task-runner, test-gcpkms]\n  - name: \"testawskms-task\"\n    commands:\n      - func: assume-test-secrets-ec2-role\n      - command: subprocess.exec\n        type: test\n        params:\n          binary: \"bash\"\n          add_expansions_to_env: true\n          args: [*task-runner, test-awskms]\n  - name: \"testawskms-fail-task\"\n    # testawskms-fail-task runs without environment variables.\n    # It is expected to fail to obtain credentials.\n    commands:\n      - func: assume-test-secrets-ec2-role\n      - command: subprocess.exec\n        type: test\n        params:\n          binary: \"bash\"\n          add_expansions_to_env: true\n          env:\n            EXPECT_ERROR: 'status=400'\n          args: [*task-runner, test-awskms]\n  - name: \"testazurekms-task\"\n    commands:\n      - command: subprocess.exec\n        type: test\n        params:\n          binary: bash\n          add_expansions_to_env: true\n          args: [*task-runner, test-azurekms]\n  - name: \"testazurekms-fail-task\"\n    # testazurekms-fail-task runs without environment variables.\n    # It is expected to fail to obtain credentials.\n    commands:\n      - func: assume-test-secrets-ec2-role\n      - command: subprocess.exec\n        type: test\n        params:\n          binary: bash\n          add_expansions_to_env: true\n          env:\n            EXPECT_ERROR: \"1\"\n          args: [*task-runner, test-azurekms]\n  - name: \"test-fuzz\"\n    commands:\n      - func: bootstrap-mongo-orchestration\n      - func: run-fuzz-tests\n  - name: \"test-aws-lambda-deployed\"\n    commands:\n      - command: subprocess.exec\n        type: test\n        params:\n          binary: bash\n          add_expansions_to_env: true\n          env:\n            TEST_LAMBDA_DIRECTORY: ${PROJECT_DIRECTORY}/internal/cmd/faas/awslambda\n            LAMBDA_STACK_NAME: dbx-go-lambda\n            AWS_REGION: us-east-1\n          args: [*task-runner, evg-test-deployed-lambda-aws]\n  - name: \"oidc-auth-test\"\n    commands:\n      - func: \"run-oidc-auth-test-with-test-credentials\"\n  - name: \"oidc-auth-test-azure\"\n    commands:\n      - command: subprocess.exec\n        type: test\n        params:\n          binary: bash\n          env:\n            OIDC_ENV: azure\n          args: [*task-runner, test-oidc-remote]\n  - name: \"oidc-auth-test-gcp\"\n    commands:\n      - command: subprocess.exec\n        type: test\n        params:\n          binary: bash\n          env:\n            OIDC_ENV: gcp\n          args: [*task-runner, test-oidc-remote]\n  - name: \"oidc-auth-test-k8s\"\n    commands:\n      - func: assume-test-secrets-ec2-role\n      - func: \"run oidc k8s test\"\n        vars:\n          VARIANT: eks\n      - func: \"run oidc k8s test\"\n        vars:\n          VARIANT: gke\n      - func: \"run oidc k8s test\"\n        vars:\n          VARIANT: aks\n  - name: \"test-search-index\"\n    commands:\n      - func: \"bootstrap-mongo-orchestration\"\n        vars:\n          VERSION: \"latest\"\n          TOPOLOGY: \"replica_set\"\n      - func: \"run-search-index-tests\"\naxes:\n  - id: version\n    display_name: MongoDB Version\n    values:\n      - id: \"8.0\"\n        display_name: \"8.0\"\n        variables:\n          VERSION: \"8.0\"\n      - id: \"7.0\"\n        display_name: \"7.0\"\n        variables:\n          VERSION: \"7.0\"\n      - id: \"6.0\"\n        display_name: \"6.0\"\n        variables:\n          VERSION: \"6.0\"\n      - id: \"5.0\"\n        display_name: \"5.0\"\n        variables:\n          VERSION: \"5.0\"\n      - id: \"4.4\"\n        display_name: \"4.4\"\n        variables:\n          VERSION: \"4.4\"\n      - id: \"4.2\"\n        display_name: \"4.2\"\n        variables:\n          VERSION: \"4.2\"\n      - id: \"rapid\"\n        display_name: \"rapid\"\n        variables:\n          VERSION: \"rapid\"\n      - id: \"latest\"\n        display_name: \"latest\"\n        variables:\n          VERSION: \"latest\"\n  # OSes that require >= 3.2 for SSL\n  - id: os-ssl-32\n    display_name: OS\n    values:\n      - id: \"windows-64\"\n        display_name: \"Windows 64-bit\"\n        run_on:\n          - windows-2022-latest-small\n        variables:\n          GCC_PATH: \"/cygdrive/c/ProgramData/chocolatey/lib/mingw/tools/install/mingw64/bin\"\n          GO_DIST: \"C:\\\\golang\\\\go1.25\"\n          VENV_BIN_DIR: \"Scripts\"\n          DEFAULT_TASK: evg-test\n          # CSOT tests are unreliable on our slow Windows hosts.\n          SKIP_CSOT_TESTS: true\n      - id: \"rhel87-64\"\n        display_name: \"RHEL 8.7\"\n        run_on: rhel8.7-large\n        variables:\n          GO_DIST: \"/opt/golang/go1.25\"\n          DEFAULT_TASK: evg-test\n      - id: \"macos\"\n        display_name: \"MacOS 14.0\"\n        run_on: macos-14\n        batchtime: 1440 # Run at most once per 24 hours.\n        variables:\n          GO_DIST: \"/opt/golang/go1.25\"\n          DEFAULT_TASK: evg-test-load-balancers\n          # CSOT tests are unreliable on our slow macOS hosts.\n          SKIP_CSOT_TESTS: true\n  # OSes that require >= 4.0 for SSL\n  - id: os-ssl-40\n    display_name: OS\n    values:\n      - id: \"windows-64\"\n        display_name: \"Windows 64-bit\"\n        run_on:\n          - windows-2022-latest-small\n        variables:\n          GCC_PATH: \"/cygdrive/c/ProgramData/chocolatey/lib/mingw/tools/install/mingw64/bin\"\n          GO_DIST: \"C:\\\\golang\\\\go1.25\"\n          VENV_BIN_DIR: \"Scripts\"\n          DEFAULT_TASK: evg-test\n          # CSOT tests are unreliable on our slow Windows hosts.\n          SKIP_CSOT_TESTS: true\n      - id: \"rhel87-64\"\n        display_name: \"RHEL 8.7\"\n        run_on: rhel8.7-large\n        variables:\n          GO_DIST: \"/opt/golang/go1.25\"\n          DEFAULT_TASK: evg-test\n      - id: \"macos\"\n        display_name: \"MacOS 14.0\"\n        run_on: macos-14\n        batchtime: 1440 # Run at most once per 24 hours.\n        variables:\n          GO_DIST: \"/opt/golang/go1.25\"\n          DEFAULT_TASK: evg-test-load-balancers\n          # CSOT tests are unreliable on our slow macOS hosts.\n          SKIP_CSOT_TESTS: true\n  - id: ocsp-rhel-87\n    display_name: OS\n    values:\n      - id: \"rhel87\"\n        display_name: \"RHEL 8.7\"\n        run_on: rhel8.7-large\n        variables:\n          GO_DIST: \"/opt/golang/go1.25\"\n          DEFAULT_TASK: evg-test\n  - id: os-aws-auth\n    display_name: OS\n    values:\n      - id: \"windows-64-2022-latest-small\"\n        display_name: \"Windows 64-bit\"\n        run_on:\n          - windows-2022-latest-small\n        variables:\n          GCC_PATH: \"/cygdrive/c/ProgramData/chocolatey/lib/mingw/tools/install/mingw64/bin\"\n          GO_DIST: \"C:\\\\golang\\\\go1.25\"\n          SKIP_ECS_AUTH_TEST: true\n          DEFAULT_TASK: evg-test\n          # CSOT tests are unreliable on our slow Windows hosts.\n          SKIP_CSOT_TESTS: true\n      - id: \"ubuntu2004-64\"\n        display_name: \"Ubuntu 20.04\"\n        run_on: ubuntu2004-test\n        variables:\n          GO_DIST: \"/opt/golang/go1.25\"\n          DEFAULT_TASK: evg-test\n      - id: \"macos\"\n        display_name: \"MacOS 14.0\"\n        run_on: macos-14\n        batchtime: 1440 # Run at most once per 24 hours.\n        variables:\n          GO_DIST: \"/opt/golang/go1.25\"\n          SKIP_ECS_AUTH_TEST: true\n          SKIP_EC2_AUTH_TEST: true\n          SKIP_WEB_IDENTITY_AUTH_TEST: true\n          DEFAULT_TASK: evg-test-load-balancers\n          # CSOT tests are unreliable on our slow macOS hosts.\n          SKIP_CSOT_TESTS: true\n  - id: os-faas-80\n    display_name: OS\n    values:\n      - id: \"rhel87-large\"\n        display_name: \"RHEL 8.7\"\n        run_on: rhel8.7-large\n        variables:\n          GO_DIST: \"/opt/golang/go1.25\"\ntask_groups:\n  - name: testgcpkms_task_group\n    setup_group_can_fail_task: true\n    setup_group_timeout_secs: 1800 # 30 minutes\n    setup_group:\n      - func: setup-system\n      - func: assume-test-secrets-ec2-role\n      - command: subprocess.exec\n        params:\n          binary: \"bash\"\n          add_expansions_to_env: true\n          args:\n            - ${DRIVERS_TOOLS}/.evergreen/csfle/gcpkms/setup.sh\n    teardown_group:\n      - command: subprocess.exec\n        params:\n          binary: \"bash\"\n          args:\n            - ${DRIVERS_TOOLS}/.evergreen/csfle/gcpkms/teardown.sh\n      - func: teardown\n      - func: handle-test-artifacts\n    tasks:\n      - testgcpkms-task\n  - name: testazurekms_task_group\n    setup_group_can_fail_task: true\n    teardown_task_can_fail_task: true\n    setup_group_timeout_secs: 1800 # 30 minutes\n    setup_group:\n      - func: setup-system\n      - func: assume-test-secrets-ec2-role\n      - command: subprocess.exec\n        params:\n          binary: bash\n          add_expansions_to_env: true\n          env:\n            AZUREKMS_VMNAME_PREFIX: GODRIVER\n          args:\n            - ${DRIVERS_TOOLS}/.evergreen/csfle/azurekms/setup.sh\n    teardown_group:\n      - command: subprocess.exec\n        params:\n          binary: \"bash\"\n          args:\n            - ${DRIVERS_TOOLS}/.evergreen/csfle/azurekms/teardown.sh\n      - func: teardown\n      - func: handle-test-artifacts\n    tasks:\n      - testazurekms-task\n  - name: testoidc_task_group\n    setup_group_can_fail_task: true\n    setup_group_timeout_secs: 1800\n    # TODO(DRIVERS-3141): Uncomment the following line once the teardown bug is\n    # fixed. See DRIVERS-3141 for more context.\n    #\n    # teardown_task_can_fail_task: true\n    teardown_group_timeout_secs: 180 # 3 minutes (max allowed time)\n    setup_group:\n      - func: setup-system\n      - func: assume-test-secrets-ec2-role\n      - command: subprocess.exec\n        params:\n          binary: bash\n          include_expansions_in_env: [\"AWS_ACCESS_KEY_ID\", \"AWS_SECRET_ACCESS_KEY\", \"AWS_SESSION_TOKEN\"]\n          env:\n            MONGODB_VERSION: \"8.0\"\n          args:\n            - ${DRIVERS_TOOLS}/.evergreen/auth_oidc/setup.sh\n    teardown_task:\n      - command: subprocess.exec\n        params:\n          binary: bash\n          args:\n            - ${DRIVERS_TOOLS}/.evergreen/auth_oidc/teardown.sh\n      - func: teardown\n      - func: handle-test-artifacts\n    tasks:\n      - oidc-auth-test\n  - name: testazureoidc_task_group\n    setup_group_can_fail_task: true\n    setup_group_timeout_secs: 1800\n    teardown_task_can_fail_task: true\n    teardown_group_timeout_secs: 180 # 3 minutes (max allowed time)\n    setup_group:\n      - func: setup-system\n      - func: assume-test-secrets-ec2-role\n      - command: subprocess.exec\n        params:\n          binary: bash\n          add_expansions_to_env: true\n          env:\n            AZUREOIDC_VMNAME_PREFIX: \"GO_DRIVER\"\n          args:\n            - ${DRIVERS_TOOLS}/.evergreen/auth_oidc/azure/setup.sh\n    teardown_task:\n      - command: subprocess.exec\n        params:\n          binary: bash\n          args:\n            - ${DRIVERS_TOOLS}/.evergreen/auth_oidc/azure/teardown.sh\n      - func: teardown\n      - func: handle-test-artifacts\n    tasks:\n      - oidc-auth-test-azure\n  - name: testgcpoidc_task_group\n    setup_group_can_fail_task: true\n    setup_group_timeout_secs: 1800\n    teardown_task_can_fail_task: true\n    teardown_group_timeout_secs: 180 # 3 minutes (max allowed time)\n    setup_group:\n      - func: setup-system\n      - func: assume-test-secrets-ec2-role\n      - command: subprocess.exec\n        params:\n          binary: bash\n          add_expansions_to_env: true\n          env:\n            AZUREOIDC_VMNAME_PREFIX: \"GO_DRIVER\"\n          args:\n            - ${DRIVERS_TOOLS}/.evergreen/auth_oidc/gcp/setup.sh\n    teardown_task:\n      - command: subprocess.exec\n        params:\n          binary: bash\n          args:\n            - ${DRIVERS_TOOLS}/.evergreen/auth_oidc/gcp/teardown.sh\n      - func: teardown\n      - func: handle-test-artifacts\n    tasks:\n      - oidc-auth-test-gcp\n  - name: testk8soidc_task_group\n    setup_group_can_fail_task: true\n    setup_group_timeout_secs: 1800\n    teardown_task_can_fail_task: true\n    teardown_group_timeout_secs: 180 # 3 minutes (max allowed time)\n    setup_group:\n      - func: setup-system\n      - func: assume-test-secrets-ec2-role\n      - command: subprocess.exec\n        params:\n          binary: bash\n          include_expansions_in_env: [\"AWS_ACCESS_KEY_ID\", \"AWS_SECRET_ACCESS_KEY\", \"AWS_SESSION_TOKEN\"]\n          args:\n            - ${DRIVERS_TOOLS}/.evergreen/auth_oidc/k8s/setup.sh\n    teardown_group:\n      - command: subprocess.exec\n        params:\n          binary: bash\n          args:\n            - ${DRIVERS_TOOLS}/.evergreen/auth_oidc/k8s/teardown.sh\n      - func: teardown\n      - func: handle-test-artifacts\n    tasks:\n      - oidc-auth-test-k8s\n  - name: test-aws-lambda-task-group\n    setup_group:\n      - func: setup-system\n      - func: assume-test-secrets-ec2-role\n      - command: subprocess.exec\n        params:\n          working_dir: src/go.mongodb.org/mongo-driver\n          binary: bash\n          include_expansions_in_env: [AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN]\n          env:\n            MONGODB_VERSION: ${VERSION}\n            LAMBDA_STACK_NAME: dbx-go-lambda\n          args:\n            - ${DRIVERS_TOOLS}/.evergreen/atlas/setup.sh\n    teardown_group:\n      - command: subprocess.exec\n        params:\n          working_dir: src/go.mongodb.org/mongo-driver\n          binary: bash\n          add_expansions_to_env: true\n          env:\n            LAMBDA_STACK_NAME: dbx-go-lambda\n            AWS_REGION: us-east-1\n          args:\n            - ${DRIVERS_TOOLS}/.evergreen/atlas/teardown.sh\n      - func: teardown\n      - func: handle-test-artifacts\n    setup_group_can_fail_task: true\n    setup_group_timeout_secs: 1800\n    tasks:\n      - test-aws-lambda-deployed\n  - name: test-search-index-task-group\n    setup_group:\n      - func: setup-system\n      - func: assume-test-secrets-ec2-role\n      - command: subprocess.exec\n        params:\n          working_dir: src/go.mongodb.org/mongo-driver\n          binary: bash\n          include_expansions_in_env: [AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN, MONGODB_URI]\n          env:\n            MONGODB_VERSION: ${VERSION}\n            LAMBDA_STACK_NAME: dbx-go-lambda\n          args:\n            - ${DRIVERS_TOOLS}/.evergreen/atlas/setup.sh\n      - command: expansions.update\n        params:\n          file: src/go.mongodb.org/mongo-driver/atlas-expansion.yml\n      - command: shell.exec\n        params:\n          working_dir: src/go.mongodb.org/mongo-driver\n          shell: bash\n          script: |-\n            echo \"SEARCH_INDEX_URI: ${MONGODB_URI}\" > atlas-expansion.yml\n      - command: expansions.update\n        params:\n          file: src/go.mongodb.org/mongo-driver/atlas-expansion.yml\n    teardown_group:\n      - command: subprocess.exec\n        params:\n          working_dir: src/go.mongodb.org/mongo-driver\n          binary: bash\n          args:\n            - ${DRIVERS_TOOLS}/.evergreen/atlas/teardown.sh\n      - func: teardown\n      - func: handle-test-artifacts\n    setup_group_can_fail_task: true\n    setup_group_timeout_secs: 1800\n    tasks:\n      - test-search-index\nbuildvariants:\n  - name: static-analysis\n    tags: [\"pullrequest\"]\n    display_name: \"Static Analysis\"\n    run_on:\n      - rhel8.7-small\n    expansions:\n      # Keep this in sync with go version used in etc/golangci-lint.sh\n      GO_DIST: \"/opt/golang/go1.25\"\n    tasks:\n      - name: \".static-analysis\"\n  - name: pull-request-helpers\n    tags: [\"pullrequest\"]\n    display_name: \"Pull Request Helpers\"\n    run_on:\n      - rhel8.7-small\n    expansions:\n      GO_DIST: \"/opt/golang/go1.25\"\n    tasks:\n      - name: \"pull-request-helpers\"\n  - name: perf\n    tags: [\"pullrequest\"]\n    display_name: \"Performance\"\n    run_on: rhel90-dbx-perf-large\n    expansions:\n      GO_DIST: \"/opt/golang/go1.25\"\n    tasks:\n      - name: \".performance\"\n  - name: build-check\n    tags: [\"pullrequest\"]\n    display_name: \"Compile Only Checks\"\n    run_on:\n      - ubuntu2204-small\n    expansions:\n      GO_DIST: \"/opt/golang/go1.25\"\n    tasks:\n      - name: \".compile-check\"\n  - name: backport-pr\n    display_name: \"Backport PR\"\n    run_on:\n      - rhel8.7-small\n    expansions:\n      GO_DIST: \"/opt/golang/go1.25\"\n    tasks:\n      - name: \"backport-pr\"\n  - name: atlas-test\n    tags: [\"pullrequest\"]\n    display_name: \"Atlas test\"\n    run_on:\n      - rhel8.7-large\n    expansions:\n      GO_DIST: \"/opt/golang/go1.25\"\n    tasks:\n      - name: \"atlas-test\"\n  - name: docker-runner-test\n    tags: [\"pullrequest\"]\n    display_name: \"Docker Runner Test\"\n    run_on:\n      - ubuntu2204-large\n    expansions:\n      GO_DIST: \"/opt/golang/go1.25\"\n    tasks:\n      - name: \"test-docker-runner\"\n  - name: goroutine-leaks-test\n    tags: [\"pullrequest\"]\n    display_name: \"Goroutine Leaks Test\"\n    run_on:\n      - ubuntu2204-large\n    expansions:\n      GO_DIST: \"/opt/golang/go1.25\"\n    tasks:\n      - name: \".goleak\"\n  - matrix_name: \"tests-rhel-44-plus-zlib-zstd-support\"\n    tags: [\"pullrequest\"]\n    matrix_spec: {version: [\"4.2\", \"4.4\", \"5.0\", \"6.0\", \"7.0\", \"8.0\"], os-ssl-40: [\"rhel87-64\"]}\n    display_name: \"${version} ${os-ssl-40}\"\n    tasks:\n      - name: \".test !.enterprise-auth !.snappy\"\n  - matrix_name: \"tests-windows-42-plus-zlib-zstd-support\"\n    matrix_spec: {version: [\"4.2\", \"4.4\", \"5.0\", \"6.0\", \"7.0\"], os-ssl-40: [\"windows-64\"]}\n    display_name: \"${version} ${os-ssl-40}\"\n    tasks:\n      - name: \".test !.enterprise-auth !.snappy\"\n  - matrix_name: \"tests-windows-80-zlib-zstd-support\"\n    tags: [\"pullrequest\"]\n    matrix_spec: {version: [\"8.0\"], os-ssl-40: [\"windows-64\"]}\n    display_name: \"${version} ${os-ssl-40}\"\n    tasks:\n      - name: \".test !.enterprise-auth !.snappy\"\n  - matrix_name: \"tests-latest-rapid-zlib-zstd-support\"\n    matrix_spec: {version: [\"latest\", \"rapid\"], os-ssl-40: [\"windows-64\", \"rhel87-64\"]}\n    display_name: \"${version} ${os-ssl-40}\"\n    tasks:\n      - name: \".test !.enterprise-auth !.snappy\"\n  - matrix_name: \"enterprise-auth-tests\"\n    matrix_spec: {os-ssl-32: \"*\"}\n    display_name: \"Enterprise Auth - ${os-ssl-32}\"\n    tasks:\n      - name: \".test .enterprise-auth\"\n  - matrix_name: \"aws-auth-test\"\n    matrix_spec: {version: [\"4.4\", \"5.0\", \"6.0\", \"7.0\", \"8.0\", \"latest\", \"rapid\"], os-aws-auth: \"*\"}\n    display_name: \"MONGODB-AWS Auth ${version} ${os-aws-auth}\"\n    tasks:\n      - name: \"aws-auth-test\"\n  - matrix_name: \"ocsp-test\"\n    matrix_spec: {version: [\"4.4\", \"5.0\", \"6.0\", \"7.0\", \"8.0\", \"latest\", \"rapid\"], ocsp-rhel-87: [\"rhel87\"]}\n    display_name: \"OCSP ${version} ${ocsp-rhel-87}\"\n    batchtime: 20160 # Use a batchtime of 14 days as suggested by the OCSP test README\n    tasks:\n      - name: \".ocsp\"\n  - matrix_name: \"ocsp-test-windows\"\n    matrix_spec: {version: [\"4.4\", \"5.0\", \"6.0\", \"7.0\", \"8.0\", \"latest\", \"rapid\"], os-ssl-40: [\"windows-64\"]}\n    display_name: \"OCSP ${version} ${os-ssl-40}\"\n    batchtime: 20160 # Use a batchtime of 14 days as suggested by the OCSP test README\n    tasks:\n      # Windows MongoDB servers do not staple OCSP responses and only support RSA.\n      - name: \".ocsp-rsa !.ocsp-staple\"\n  - matrix_name: \"ocsp-test-macos\"\n    matrix_spec: {version: [\"4.4\", \"5.0\", \"6.0\", \"7.0\", \"8.0\", \"latest\", \"rapid\"], os-ssl-40: [\"macos\"]}\n    display_name: \"OCSP ${version} ${os-ssl-40}\"\n    batchtime: 20160 # Use a batchtime of 14 days as suggested by the OCSP test README\n    tasks:\n      # macos MongoDB servers do not staple OCSP responses and only support RSA.\n      - name: \".ocsp-rsa !.ocsp-staple\"\n  - matrix_name: \"race-test\"\n    tags: [\"pullrequest\"]\n    matrix_spec: {version: [\"7.0\"], os-ssl-40: [\"rhel87-64\"]}\n    display_name: \"Race Detector Test\"\n    tasks:\n      - name: \".race\"\n  - matrix_name: \"versioned-api-test\"\n    tags: [\"pullrequest\"]\n    matrix_spec: {version: [\"5.0\", \"6.0\", \"7.0\", \"8.0\"], os-ssl-40: [\"windows-64\", \"rhel87-64\"]}\n    display_name: \"API Version ${version} ${os-ssl-40}\"\n    tasks:\n      - name: \".versioned-api\"\n  - matrix_name: \"versioned-api-latest-rapid-test\"\n    matrix_spec: {version: [\"latest\", \"rapid\"], os-ssl-40: [\"windows-64\", \"rhel87-64\"]}\n    display_name: \"API Version ${version} ${os-ssl-40}\"\n    tasks:\n      - name: \".versioned-api\"\n  - matrix_name: \"client-side-encryption-test\"\n    matrix_spec: {version: [\"latest\", \"rapid\"], os-ssl-40: [\"rhel87-64\"]}\n    display_name: \"Client Side Encryption Tests ${version} ${os-ssl-40}\"\n    tasks:\n      - name: \".client-side-encryption-test\"\n  - matrix_name: \"load-balancer-test\"\n    tags: [\"pullrequest\"]\n    matrix_spec: {version: [\"5.0\", \"6.0\", \"7.0\", \"8.0\"], os-ssl-40: [\"rhel87-64\"]}\n    display_name: \"Load Balancer Support ${version} ${os-ssl-40}\"\n    tasks:\n      - name: \".load-balancer\"\n  - matrix_name: \"load-balancer-latest-rapid-test\"\n    matrix_spec: {version: [\"latest\", \"rapid\"], os-ssl-40: [\"rhel87-64\"]}\n    display_name: \"Load Balancer Support ${version} ${os-ssl-40}\"\n    tasks:\n      - name: \".load-balancer\"\n  - matrix_name: \"kms-kmip-test\"\n    matrix_spec: {version: [\"7.0\"], os-ssl-40: [\"rhel87-64\"]}\n    display_name: \"KMS KMIP ${os-ssl-40}\"\n    tasks:\n      - name: \".kms-kmip\"\n  - matrix_name: \"fuzz-test\"\n    matrix_spec: {version: [\"5.0\"], os-ssl-40: [\"rhel87-64\"]}\n    display_name: \"Fuzz ${version} ${os-ssl-40}\"\n    tasks:\n      - name: \"test-fuzz\"\n        batchtime: 1440 # Run at most once per 24 hours.\n  - matrix_name: \"faas-test\"\n    matrix_spec: {version: [\"7.0\"], os-faas-80: [\"rhel87-large\"]}\n    display_name: \"FaaS ${version} ${os-faas-80}\"\n    tasks:\n      - test-aws-lambda-task-group\n  - matrix_name: \"searchindex-test\"\n    matrix_spec: {version: [\"7.0\"], os-faas-80: [\"rhel87-large\"]}\n    display_name: \"Search Index ${version} ${os-faas-80}\"\n    tasks:\n      - test-search-index-task-group\n  - name: testgcpkms-variant\n    display_name: \"GCP KMS\"\n    run_on:\n      - rhel8.7-small\n    expansions:\n      GO_DIST: \"/opt/golang/go1.25\"\n    tasks:\n      - name: testgcpkms_task_group\n        batchtime: 20160 # Use a batchtime of 14 days as suggested by the CSFLE test README\n      - testgcpkms-fail-task\n  - name: testawskms-variant\n    display_name: \"AWS KMS\"\n    run_on:\n      - rhel8.7-small\n    expansions:\n      GO_DIST: \"/opt/golang/go1.25\"\n    tasks:\n      - testawskms-task\n      - testawskms-fail-task\n  - name: testazurekms-variant\n    display_name: \"AZURE KMS\"\n    run_on:\n      - rhel8.7-small\n    expansions:\n      GO_DIST: \"/opt/golang/go1.25\"\n    tasks:\n      - name: testazurekms_task_group\n        batchtime: 20160 # Use a batchtime of 14 days as suggested by the CSFLE test README\n      - testazurekms-fail-task\n  - name: testoidc-variant\n    display_name: \"OIDC\"\n    run_on:\n      - ubuntu2204-small\n    expansions:\n      GO_DIST: \"/opt/golang/go1.25\"\n    tasks:\n      - name: testoidc_task_group\n      - name: testazureoidc_task_group\n      - name: testgcpoidc_task_group\n      - name: testk8soidc_task_group\n"
  },
  {
    "path": ".evergreen/krb5.config",
    "content": "[realms]\n  LDAPTEST.10GEN.CC = {\n    kdc = ldaptest.10gen.cc\n    admin_server = ldaptest.10gen.cc\n  }\n\n[libdefaults]\n  rdns = false\n"
  },
  {
    "path": ".evergreen/ocsp-requirements.txt",
    "content": "asn1crypto==1.3.0\nbottle==0.12.20\noscrypto==1.2.0\n"
  },
  {
    "path": ".evergreen/run-mongodb-aws-ecs-test.sh",
    "content": "#!/bin/bash\n\nset -o errexit  # Exit the script with error if any of the commands fail\n\n############################################\n#            Main Program                  #\n############################################\n\nif [[ -z \"$1\" ]]; then\n    echo \"usage: $0 <MONGODB_URI>\"\n    exit 1\nfi\nexport MONGODB_URI=\"$1\"\n\necho \"Running MONGODB-AWS ECS authentication tests\"\n\nif echo \"$MONGODB_URI\" | grep -q \"@\"; then\n  echo \"MONGODB_URI unexpectedly contains user credentials in ECS test!\";\n  exit 1\nfi\n\n./src/main\n"
  },
  {
    "path": ".evergreen/run-task.sh",
    "content": "#!/usr/bin/env bash\n#\n# Source the env.sh file and run the given task\nset -eu\n\nSCRIPT_DIR=$( cd -- \"$( dirname -- \"${BASH_SOURCE[0]}\" )\" &> /dev/null && pwd )\nPROJECT_DIRECTORY=$(dirname $SCRIPT_DIR)\npushd ${PROJECT_DIRECTORY} > /dev/null\n\nsource env.sh\ntask \"$@\"\n\npopd > /dev/null\n"
  },
  {
    "path": ".evergreen/setup-system.sh",
    "content": "#!/usr/bin/env bash\n#\n# Set up environment and write env.sh and expansion.yml files.\nset -eu\n\n# Set up default environment variables.\nSCRIPT_DIR=$( cd -- \"$( dirname -- \"${BASH_SOURCE[0]}\" )\" &> /dev/null && pwd )\nPROJECT_DIRECTORY=$(dirname $SCRIPT_DIR)\npushd $PROJECT_DIRECTORY\nROOT_DIR=$(dirname $PROJECT_DIRECTORY)\nDRIVERS_TOOLS=${DRIVERS_TOOLS:-${ROOT_DIR}/drivers-evergreen-tools}\nMONGO_ORCHESTRATION_HOME=\"${DRIVERS_TOOLS}/.evergreen/orchestration\"\nMONGODB_BINARIES=\"${DRIVERS_TOOLS}/mongodb/bin\"\nOS=\"${OS:-\"\"}\"\n\n# Set Golang environment vars. GOROOT is wherever current Go distribution is, and is set in evergreen config.\n# GOPATH is always 3 directories up from pwd on EVG; GOCACHE is under .cache in the pwd.\nGOROOT=${GOROOT:-$(dirname \"$(dirname \"$(which go)\")\")}\nexport GOPATH=${GOPATH:-$ROOT_DIR}\nexport GOCACHE=\"${GO_CACHE:-$PROJECT_DIRECTORY/.cache}\"\n\n# Handle paths on Windows.\nif [ \"Windows_NT\" = \"${OS:-}\" ]; then # Magic variable in cygwin\n  GOPATH=$(cygpath -m $GOPATH)\n  GOCACHE=$(cygpath -w $GOCACHE)\n  DRIVERS_TOOLS=$(cygpath -m $DRIVERS_TOOLS)\n  PROJECT_DIRECTORY=$(cygpath -m $PROJECT_DIRECTORY)\n  EXTRA_PATH=/cygdrive/c/libmongocrypt/bin\n  MONGO_ORCHESTRATION_HOME=$(cygpath -m $MONGO_ORCHESTRATION_HOME)\n  MONGODB_BINARIES=$(cygpath -m $MONGODB_BINARIES)\n  # Set home variables for Windows, too.\n  USERPROFILE=$(cygpath -w \"$ROOT_DIR\")\n  HOME=$USERPROFILE\nelse\n  EXTRA_PATH=${GCC:-}\nfi\n\n# Add binaries to the path.\nPATH=\"${GOROOT}/bin:${GOPATH}/bin:${MONGODB_BINARIES}:${EXTRA_PATH}:${PATH}\"\n\n# Get the current unique version of this checkout.\nif [ \"${IS_PATCH:-}\" = \"true\" ]; then\n    CURRENT_VERSION=$(git describe)-patch-${VERSION_ID}\nelse\n    CURRENT_VERSION=latest\nfi\n\n# Ensure a checkout of drivers-tools.\nif [ ! -d \"$DRIVERS_TOOLS\" ]; then\n  git clone https://github.com/mongodb-labs/drivers-evergreen-tools $DRIVERS_TOOLS\nfi\n\n# Write the .env file for drivers-tools.\ncat <<EOT > ${DRIVERS_TOOLS}/.env\nSKIP_LEGACY_SHELL=1\nDRIVERS_TOOLS=\"$DRIVERS_TOOLS\"\nMONGO_ORCHESTRATION_HOME=\"$MONGO_ORCHESTRATION_HOME\"\nMONGODB_BINARIES=\"$MONGODB_BINARIES\"\nTMPDIR=\"$MONGO_ORCHESTRATION_HOME/db\"\nEOT\n\n# Check Go installation.\ngo version\ngo env\n\n# Install taskfile.\ngo install github.com/go-task/task/v3/cmd/task@v3.39.1\n\n# Write our own env file.\ncat <<EOT > env.sh\nexport GOROOT=\"$GOROOT\"\nexport GOPATH=\"$GOPATH\"\nexport GOCACHE=\"$GOCACHE\"\nexport DRIVERS_TOOLS=\"$DRIVERS_TOOLS\"\nexport PROJECT_DIRECTORY=\"$PROJECT_DIRECTORY\"\nexport MONGODB_BINARIES=\"$MONGODB_BINARIES\"\nexport PATH=\"$PATH\"\nEOT\n\nif [ \"Windows_NT\" = \"$OS\" ]; then\n    echo \"export USERPROFILE=$USERPROFILE\" >> env.sh\n    echo \"export HOME=$HOME\" >> env.sh\nfi\n\n# source the env.sh file and write the expansion file.\ncat <<EOT > expansion.yml\nCURRENT_VERSION: \"$CURRENT_VERSION\"\nDRIVERS_TOOLS: \"$DRIVERS_TOOLS\"\nPROJECT_DIRECTORY: \"$PROJECT_DIRECTORY\"\nRUN_TASK: \"$PROJECT_DIRECTORY/.evergreen/run-task.sh\"\nEOT\n\ncat env.sh\npopd\n"
  },
  {
    "path": ".github/dependabot.yml",
    "content": "version: 2\nupdates:\n  - package-ecosystem: \"github-actions\"\n    directory: \"/\"\n    schedule:\n      interval: \"weekly\"\n    groups:\n      actions:\n        patterns:\n          - \"*\"\n  - package-ecosystem: gomod\n    directory: /\n    schedule:\n      interval: \"weekly\"\n  - package-ecosystem: \"gitsubmodule\"\n    directory: \"/\"\n    schedule:\n      interval: \"monthly\"\n"
  },
  {
    "path": ".github/labeler.yml",
    "content": "review-priority-normal:\n  - changed-files:\n      - any-glob-to-any-file: \"*\"\ndocumentation:\n  - changed-files:\n      - any-glob-to-any-file:\n          - docs/**\n          - examples/**\ndependencies:\n  - changed-files:\n      - any-glob-to-any-file:\n          - go.mod\n"
  },
  {
    "path": ".github/release.yml",
    "content": "changelog:\n  exclude:\n    labels:\n      - ignore-for-release\n      - github_actions\n      - submodules\n    authors:\n      - mongodb-drivers-pr-bot\n  categories:\n    - title: ⚠️ Breaking Changes\n      labels:\n        - breaking\n    - title: ✨ New Features\n      labels:\n        - enhancement\n        - feature\n    - title: 🐛 Fixed\n      labels:\n        - bug\n    - title: 📦 Dependency Updates\n      labels:\n        - dependencies\n    - title: 📝 Other Changes\n      labels:\n        - \"*\"\n"
  },
  {
    "path": ".github/workflows/check-labels.yml",
    "content": "name: Label Checker\non:\n  pull_request:\n    types:\n      - opened\n      - synchronize\n      - reopened\n      - labeled\n      - unlabeled\npermissions:\n  pull-requests: read\njobs:\n  check_labels:\n    name: Check labels\n    runs-on: ubuntu-latest\n    steps:\n      - uses: docker://agilepathway/pull-request-label-checker@sha256:65e57fd98ba3ab6ca4fcbc5a0aef288dd984ee4ab988f124d83424d19b55b801\n        with:\n          one_of: bug,feature,enhancement,documentation,dependencies,ignore-for-release,ci/cd\n          repo_token: ${{ secrets.GITHUB_TOKEN }}\n"
  },
  {
    "path": ".github/workflows/codeql.yml",
    "content": "name: \"CodeQL\"\non:\n  push:\n    branches:\n      - \"v1\"\n      - \"cloud-*\"\n      - \"master\"\n      - \"release/*\"\n      - \"feature/*\"\n  pull_request:\n    branches:\n      - \"v1\"\n      - \"cloud-*\"\n      - \"master\"\n      - \"release/*\"\n      - \"feature/*\"\n  schedule:\n    - cron: \"36 17 * * 0\"\n  workflow_call:\n    inputs:\n      ref:\n        required: true\n        type: string\npermissions:\n  contents: read\njobs:\n  analyze:\n    name: Analyze (go)\n    runs-on: \"ubuntu-latest\"\n    timeout-minutes: 360\n    permissions:\n      # required for all workflows\n      contents: read\n      security-events: write\n    steps:\n      - name: Checkout repository\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd #v6.0.2\n      - name: Set up Go\n        uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 #v6.3.0\n        with:\n          go-version: \"1.25.0\"\n      # Initializes the CodeQL tools for scanning.\n      - name: Initialize CodeQL\n        uses: github/codeql-action/init@v4.33.0 #immutable\n        with:\n          languages: go\n          build-mode: manual\n      - name: Build (CodeQL-instrumented)\n        shell: bash\n        run: |\n          # TODO(GODRIVER-3723): Run using taskfile targets.\n          go build ./...\n          go test -short -run ^$$ ./...\n\n          go test -v ./internal/test/compilecheck -run '^TestCompileCheck/go:1\\.19$'\n      - name: Perform CodeQL Analysis\n        uses: github/codeql-action/analyze@v4.33.0 #immutable\n        with:\n          category: \"/language:go\"\n"
  },
  {
    "path": ".github/workflows/create-release-branch.yml",
    "content": "name: Create Release Branch\non:\n  workflow_dispatch:\n    inputs:\n      branch_name:\n        description: The name of the new branch\n        required: true\n      version:\n        description: The version to set on the branch\n        required: true\n      base_ref:\n        description: The base reference for the branch\n      push_changes:\n        description: Whether to push the changes\n        default: \"true\"\npermissions:\n  contents: read\nconcurrency:\n  group: create-branch-${{ github.ref }}\n  cancel-in-progress: true\ndefaults:\n  run:\n    shell: bash -eux {0}\njobs:\n  create-branch:\n    environment: release\n    runs-on: ubuntu-latest\n    permissions:\n      id-token: write\n      contents: write\n    outputs:\n      version: ${{ steps.pre-publish.outputs.version }}\n    steps:\n      - uses: mongodb-labs/drivers-github-tools/secure-checkout@fac300d2aa6fe2ddd6316aa673df485941b882f0 #v3\n        with:\n          app_id: ${{ vars.APP_ID }}\n          private_key: ${{ secrets.APP_PRIVATE_KEY }}\n      - uses: mongodb-labs/drivers-github-tools/setup@fac300d2aa6fe2ddd6316aa673df485941b882f0 #v3\n        with:\n          aws_role_arn: ${{ secrets.AWS_ROLE_ARN }}\n          aws_region_name: ${{ vars.AWS_REGION_NAME }}\n          aws_secret_id: ${{ secrets.AWS_SECRET_ID }}\n          artifactory_username: ${{ vars.ARTIFACTORY_USERNAME }}\n      - uses: mongodb-labs/drivers-github-tools/create-branch@fac300d2aa6fe2ddd6316aa673df485941b882f0 #v3\n        id: create-branch\n        with:\n          branch_name: ${{ inputs.branch_name }}\n          version: ${{ inputs.version }}\n          base_ref: ${{ inputs.base_ref }}\n          push_changes: ${{ inputs.push_changes }}\n          version_bump_script: \"go run ${{ github.action_path }}/bump-version.go\"\n          evergreen_project: mongo-go-driver-release\n          release_workflow_path: ./.github/workflows/release.yml\n"
  },
  {
    "path": ".github/workflows/labeler.yml",
    "content": "name: \"Pull Request Labeler\"\non:\n  - pull_request_target\npermissions:\n  contents: read\njobs:\n  labeler:\n    permissions:\n      contents: read\n      pull-requests: write\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/labeler@634933edcd8ababfe52f92936142cc22ac488b1b #v6.0.1\n"
  },
  {
    "path": ".github/workflows/merge-up.yml",
    "content": "name: Merge up\non:\n  push:\n    branches:\n      - release/*.*\n      - v*\npermissions:\n  contents: read\njobs:\n  merge-up:\n    name: Create merge up pull request\n    runs-on: ubuntu-latest\n    permissions:\n      id-token: write\n      contents: write\n      pull-requests: write\n    steps:\n      - uses: mongodb-labs/drivers-github-tools/secure-checkout@fac300d2aa6fe2ddd6316aa673df485941b882f0 #v3\n        with:\n          app_id: ${{ vars.PR_APP_ID }}\n          private_key: ${{ secrets.PR_APP_PRIVATE_KEY }}\n          # Make sure to include fetch-depth 0 so all branches are fetched, not\n          # just the current one\n          fetch-depth: 0\n      - name: Create pull request\n        id: create-pull-request\n        uses: alcaeus/automatic-merge-up-action@e23e7c71d58f12531cbaba73473bbe78fd14baf0 #v1.0.1\n        with:\n          ref: ${{ github.ref_name }}\n          branchNamePattern: \"release/<major>.<minor>\"\n          devBranchNamePattern: \"v<major>\"\n          fallbackBranch: \"master\"\n          ignoredBranches: ${{ vars.IGNORED_MERGE_UP_BRANCHES }}\n          enableAutoMerge: true\n"
  },
  {
    "path": ".github/workflows/release.yml",
    "content": "name: Release\non:\n  workflow_dispatch:\n    inputs:\n      version:\n        description: \"The new version to set\"\n        required: true\n      prev_version:\n        description: \"The previous tagged version\"\n        required: true\n      push_changes:\n        description: \"Push changes?\"\n        default: true\n        type: boolean\npermissions:\n  contents: read\ndefaults:\n  run:\n    shell: bash -eux {0}\nenv:\n  # Changes per branch\n  SILK_ASSET_GROUP: mongodb-go-driver\n  EVERGREEN_PROJECT: mongo-go-driver\njobs:\n  pre-publish:\n    environment: release\n    runs-on: ubuntu-latest\n    permissions:\n      id-token: write\n      contents: write\n    outputs:\n      prev_version: ${{ steps.pre-publish.outputs.prev_version }}\n    steps:\n      - uses: mongodb-labs/drivers-github-tools/secure-checkout@fac300d2aa6fe2ddd6316aa673df485941b882f0 #v3\n        with:\n          app_id: ${{ vars.APP_ID }}\n          private_key: ${{ secrets.APP_PRIVATE_KEY }}\n      - uses: mongodb-labs/drivers-github-tools/setup@fac300d2aa6fe2ddd6316aa673df485941b882f0 #v3\n        with:\n          aws_role_arn: ${{ secrets.AWS_ROLE_ARN }}\n          aws_region_name: ${{ vars.AWS_REGION_NAME }}\n          aws_secret_id: ${{ secrets.AWS_SECRET_ID }}\n          artifactory_username: ${{ vars.ARTIFACTORY_USERNAME }}\n      - name: Pre Publish\n        id: pre-publish\n        uses: mongodb-labs/drivers-github-tools/golang/pre-publish@fac300d2aa6fe2ddd6316aa673df485941b882f0 #v3\n        with:\n          version: ${{ inputs.version }}\n          push_changes: ${{ inputs.push_changes }}\n          ignored_branches: ${{ vars.IGNORED_MERGE_UP_BRANCHES }}\n  static-scan:\n    needs: [pre-publish]\n    permissions:\n      contents: read\n      security-events: write\n    uses: ./.github/workflows/codeql.yml\n    with:\n      ref: ${{ github.ref }}\n  publish:\n    needs: [pre-publish, static-scan]\n    runs-on: ubuntu-latest\n    environment: release\n    permissions:\n      id-token: write\n      contents: write\n      security-events: read\n    steps:\n      - uses: mongodb-labs/drivers-github-tools/secure-checkout@fac300d2aa6fe2ddd6316aa673df485941b882f0 #v3\n        with:\n          app_id: ${{ vars.APP_ID }}\n          private_key: ${{ secrets.APP_PRIVATE_KEY }}\n      - uses: mongodb-labs/drivers-github-tools/setup@fac300d2aa6fe2ddd6316aa673df485941b882f0 #v3\n        with:\n          aws_role_arn: ${{ secrets.AWS_ROLE_ARN }}\n          aws_region_name: ${{ vars.AWS_REGION_NAME }}\n          aws_secret_id: ${{ secrets.AWS_SECRET_ID }}\n          artifactory_username: ${{ vars.ARTIFACTORY_USERNAME }}\n      - name: Publish\n        uses: mongodb-labs/drivers-github-tools/golang/publish@fac300d2aa6fe2ddd6316aa673df485941b882f0 #v3\n        with:\n          version: ${{ inputs.version }}\n          silk_asset_group: ${{ env.SILK_ASSET_GROUP }}\n          evergreen_project: ${{ env.EVERGREEN_PROJECT }}\n          prev_version: ${{ inputs.prev_version }}\n          push_changes: ${{ inputs.push_changes }}\n          token: ${{ env.GH_TOKEN }}\n"
  },
  {
    "path": ".github/workflows/scorecard.yml",
    "content": "# This workflow uses actions that are not certified by GitHub. They are provided\n# by a third-party and are governed by separate terms of service, privacy\n# policy, and support documentation.\n\nname: Scorecard supply-chain security\non:\n  # For Branch-Protection check. Only the default branch is supported. See\n  # https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection\n  branch_protection_rule:\n  # To guarantee Maintained check is occasionally updated. See\n  # https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained\n  schedule:\n    - cron: \"24 21 * * 1\"\n  push:\n    branches: [\"master\"]\n# Declare default permissions as read only.\npermissions: read-all\njobs:\n  analysis:\n    name: Scorecard analysis\n    runs-on: ubuntu-latest\n    # `publish_results: true` only works when run from the default branch. conditional can be removed if disabled.\n    if: github.event.repository.default_branch == github.ref_name || github.event_name == 'pull_request'\n    permissions:\n      # Needed to upload the results to code-scanning dashboard.\n      security-events: write\n      # Needed to publish results and get a badge (see publish_results below).\n      id-token: write\n      # Uncomment the permissions below if installing in a private repository.\n      # contents: read\n      # actions: read\n    steps:\n      - name: \"Checkout code\"\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd #v6.0.2\n        with:\n          persist-credentials: false\n      - name: \"Run analysis\"\n        uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3\n        with:\n          results_file: results.sarif\n          results_format: sarif\n          # (Optional) \"write\" PAT token. Uncomment the `repo_token` line below if:\n          # - you want to enable the Branch-Protection check on a *public* repository, or\n          # - you are installing Scorecard on a *private* repository\n          # To create the PAT, follow the steps in https://github.com/ossf/scorecard-action?tab=readme-ov-file#authentication-with-fine-grained-pat-optional.\n          # repo_token: ${{ secrets.SCORECARD_TOKEN }}\n\n          # Public repositories:\n          #   - Publish results to OpenSSF REST API for easy access by consumers\n          #   - Allows the repository to include the Scorecard badge.\n          #   - See https://github.com/ossf/scorecard-action#publishing-results.\n          # For private repositories:\n          #   - `publish_results` will always be set to `false`, regardless\n          #     of the value entered here.\n          publish_results: true\n          # (Optional) Uncomment file_mode if you have a .gitattributes with files marked export-ignore\n          # file_mode: git\n      # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF\n      # format to the repository Actions tab.\n      - name: \"Upload artifact\"\n        uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0\n        with:\n          name: SARIF file\n          path: results.sarif\n          retention-days: 5\n      # Upload the results to GitHub's code scanning dashboard (optional).\n      # Commenting out will disable upload of results to your repo's Code Scanning dashboard\n      - name: \"Upload to code-scanning\"\n        uses: github/codeql-action/upload-sarif@v4.33.0 #immutable\n        with:\n          sarif_file: results.sarif\n"
  },
  {
    "path": ".github/workflows/test.yml",
    "content": "name: GoDriver Tests\non:\n  push:\n  pull_request:\npermissions:\n  contents: read\nconcurrency:\n  group: test-${{ github.ref }}\n  cancel-in-progress: true\ndefaults:\n  run:\n    shell: bash -eux {0}\njobs:\n  pre_commit:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd #v6.0.2\n      - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 #v6.2.0\n      - uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 #v6.3.0\n        with:\n          go-version: 'stable'\n      - uses: pre-commit/action@2c7b3805fd2a0fd8c1884dcaebf91fc102a13ecd #v3.0.1\n"
  },
  {
    "path": ".gitignore",
    "content": ".vscode\ndebug\n.idea\n*.iml\n*.ipr\n*.iws\n.idea\n*.sublime-project\n*.sublime-workspace\ndriver-test-data.tar.gz\nperf\nperf.json\nperf.suite\n**mongocryptd.pid\n*.test\n.DS_Store\ninstall\nmain.so\n.cache\ninstall\nlibmongocrypt\nvenv\ntest.suite\ngo.work.sum\n.task\nenv.sh\nexpansion.yml\nbin\n\n# AWS SAM-generated files\ninternal/cmd/faas/awslambda/.aws-sam\ninternal/cmd/faas/awslambda/events/event.json\n\n# Ignore api report files\napi-report.md\napi-report.txt\n\n# Ignore perf report files\nperf-report.md\nperf-report.txt\n\n# Ignore secrets files\nsecrets-expansion.yml\nsecrets-export.sh\n.test.env\n"
  },
  {
    "path": ".gitmodules",
    "content": "[submodule \"specifications\"]\n\tpath = testdata/specifications\n\turl = https://github.com/mongodb/specifications\n"
  },
  {
    "path": ".golangci.yml",
    "content": "version: \"2\"\nlinters:\n  default: none\n  enable:\n    - errcheck\n    - gocritic\n    # TODO(GODRIVER-3712): Enable gosec in golangci-lint version 2.8.0\n    #- gosec\n    - govet\n    - ineffassign\n    - makezero\n    - misspell\n    - nakedret\n    - paralleltest\n    - prealloc\n    - revive\n    - staticcheck\n    - unconvert\n    - unparam\n    - unused\n  settings:\n    errcheck:\n      exclude-functions:\n        - .errcheck-excludes\n    govet:\n      disable:\n        - cgocall\n        - composites\n    paralleltest:\n      # Ignore missing calls to `t.Parallel()` and only report incorrect uses of\n      # `t.Parallel()`.\n      ignore-missing: true\n    staticcheck:\n      checks:\n        - all\n        # Disable deprecation warnings for now.\n        - -SA1012\n        # Disable \"do not pass a nil Context\" to allow testing nil contexts in\n        # tests.\n        - -SA1019\n  exclusions:\n    generated: lax\n    rules:\n      # Ignore some linters for example code that is intentionally simplified.\n      - linters:\n          - errcheck\n          - revive\n        path: examples/\n      # Disable \"unused\" linter for code files that depend on the\n      # \"mongocrypt.MongoCrypt\" type because the linter build doesn't work\n      # correctly with CGO enabled. As a result, all calls to a\n      # \"mongocrypt.MongoCrypt\" API appear to always panic (see\n      # mongocrypt_not_enabled.go), leading to confusing messages about unused\n      # code.\n      - linters:\n          - unused\n        path: x/mongo/driver/crypt.go|mongo/(crypt_retrievers|mongocryptd).go\n      # Ignore \"TLS MinVersion too low\", \"TLS InsecureSkipVerify set true\", and\n      # \"Use of weak random number generator (math/rand instead of crypto/rand)\"\n      # in tests. Disable gosec entirely for test files to reduce noise.\n      - linters:\n          - gosec\n        path: _test\\.go\n      # Ignore prealloc warnings for test files.\n      - linters:\n          - prealloc\n        path: _test\\.go\n      # Ignore missing comments for exported variable/function/type for code in\n      # the \"internal\" and \"benchmark\" directories.\n      - path: (internal\\/|benchmark\\/)\n        text: exported (.+) should have comment( \\(or a comment on this block\\))? or be unexported\n      # Ignore missing package comments for directories that aren't frequently\n      # used by external users.\n      - path: (internal\\/|benchmark\\/|x\\/|cmd\\/|mongo\\/integration\\/)\n        text: should have a package comment\n      # Add all default excluded issues except issues related to exported\n      # types/functions not having comments; we want those warnings. The\n      # defaults are copied from the \"--exclude-use-default\" documentation on\n      # https://golangci-lint.run/usage/configuration/#command-line-options\n      #\n      ## Defaults ##\n      #\n      # EXC0001 errcheck: Almost all programs ignore errors on these functions\n      # and in most cases it's ok\n      - path: (.+)\\.go$\n        text: Error return value of .((os\\.)?std(out|err)\\..*|.*Close|.*Flush|os\\.Remove(All)?|.*print(f|ln)?|os\\.(Un)?Setenv). is not checked\n      # EXC0003 golint: False positive when tests are defined in package 'test'\n      - path: (.+)\\.go$\n        text: func name will be used as test\\.Test.* by other packages, and that stutters; consider calling this\n      # EXC0004 govet: Common false positives\n      - path: (.+)\\.go$\n        text: (possible misuse of unsafe.Pointer|should have signature)\n      # EXC0005 staticcheck: Developers tend to write in C-style with an explicit 'break' in a 'switch', so it's ok to ignore\n      - path: (.+)\\.go$\n        text: ineffective break statement. Did you mean to break out of the outer loop\n      # EXC0006 gosec: Too many false-positives on 'unsafe' usage\n      - path: (.+)\\.go$\n        text: Use of unsafe calls should be audited\n      # EXC0007 gosec: Too many false-positives for parametrized shell calls\n      - path: (.+)\\.go$\n        text: Subprocess launch(ed with variable|ing should be audited)\n      # EXC0008 gosec: Duplicated errcheck checks\n      - path: (.+)\\.go$\n        text: (G104|G307)\n      # EXC0009 gosec: Too many issues in popular repos\n      - path: (.+)\\.go$\n        text: (Expect directory permissions to be 0750 or less|Expect file permissions to be 0600 or less)\n      # EXC0010 gosec: False positive is triggered by\n      # 'src, err := ioutil.ReadFile(filename)'\n      - path: (.+)\\.go$\n        text: Potential file inclusion via variable\n      ## End Defaults ##\n      # Ignore capitalization warning for this weird field name.\n      - path: (.+)\\.go$\n        text: \"var-naming: struct field CqCssWxW should be CqCSSWxW\"\n      # Ignore warnings for common \"wiremessage.Read...\" usage because the\n      # safest way to use that API is by assigning possibly unused returned byte\n      # buffers.\n      - path: (.+)\\.go$\n        text: \"SA4006: this value of `wm` is never used\"\n      - path: (.+)\\.go$\n        text: \"SA4006: this value of `rem` is never used\"\n      - path: (.+)\\.go$\n        text: ineffectual assignment to wm\n      - path: (.+)\\.go$\n        text: ineffectual assignment to rem\n    paths:\n      - (^|/)testdata($|/)\n      - (^|/)etc($|/)\n      # Disable all linters for copied third-party code.\n      - internal/rand\n      - internal/aws\n      - internal/assert\nformatters:\n  enable:\n    - goimports\n  exclusions:\n    generated: lax\n    paths:\n      - (^|/)testdata($|/)\n      - (^|/)etc($|/)\n      - internal/rand\n      - internal/aws\n      - internal/assert\n"
  },
  {
    "path": ".pre-commit-config.yaml",
    "content": "repos:\n  - repo: https://github.com/pre-commit/pre-commit-hooks\n    rev: v6.0.0\n    hooks:\n      - id: check-case-conflict\n      - id: check-executables-have-shebangs\n      - id: check-added-large-files\n      - id: check-case-conflict\n      - id: check-merge-conflict\n      - id: check-json\n      - id: end-of-file-fixer\n        exclude: ^(vendor/|bson/testdata/lorem.txt)\n        exclude_types: [json, yaml]\n      - id: trailing-whitespace\n        exclude: ^(vendor/|internal/assert/assertions_test.go|bson/testdata/lorem.txt)\n        exclude_types: [json, yaml]\n  - repo: https://github.com/executablebooks/mdformat\n    rev: 0.7.17\n    hooks:\n      - id: mdformat\n        exclude: ^vendor/\n  - repo: https://github.com/python-jsonschema/check-jsonschema\n    rev: 0.27.0\n    hooks:\n      - id: check-github-workflows\n  # We use the Python version instead of the original version which seems to require Docker\n  # https://github.com/koalaman/shellcheck-precommit\n  - repo: https://github.com/shellcheck-py/shellcheck-py\n    rev: v0.9.0.6\n    hooks:\n      - id: shellcheck\n        name: shellcheck\n        args: [\"--severity=warning\"]\n  - repo: https://github.com/codespell-project/codespell\n    rev: \"v2.2.6\"\n    hooks:\n      - id: codespell\n        args: [\"-L\", \"te,fo,fle,alo,nin,compres,wil,collone,asess,sav,ot,wll,dne,nulll,hellow,aks\"]\n        exclude: ^(vendor/|internal/cmd/benchmark/operation_test.go|bson/testdata/)\n        exclude_types: [json, yaml, pem]\n  - repo: https://github.com/tcort/markdown-link-check\n    rev: v3.11.2\n    hooks:\n      - id: markdown-link-check\n        exclude: ^(vendor)\n        # If the endpoint returns HTTP 429 (Too Many Requests), consider it a\n        # successful check.\n        args: [\"-a 200,206,429\"]\n  - repo: local\n    hooks:\n      - id: executable-shell\n        name: executable-shell\n        entry: chmod +x\n        language: system\n        types: [shell]\n      - id: gofumpt\n        name: gofumpt\n        entry: gofumpt -w\n        language: golang\n        types: [go]\n        exclude: ^vendor/\n        additional_dependencies: [mvdan.cc/gofumpt@v0.9.2]\n      - id: golangci-lint\n        name: golangci-lint\n        language: system\n        types: [go]\n        require_serial: true\n        pass_filenames: false\n        entry: etc/golangci-lint.sh\n      - id: check-licenses\n        name: check-licenses\n        language: system\n        types: [go]\n        entry: etc/check_license.sh\n  - repo: https://github.com/google/yamlfmt\n    rev: v0.10.0\n    hooks:\n      - id: yamlfmt\n"
  },
  {
    "path": "Dockerfile",
    "content": "# Dockerfile for Go Driver local development.\n# sha found via this command: docker inspect --format='{{index .RepoDigests 0}}' golang:1.25.6-trixie\nFROM golang:1.25.6-trixie@sha256:fb4b74a39c7318d53539ebda43ccd3ecba6e447a78591889c0efc0a7235ea8b3 AS base\n\n# Build libmongocrypt in a separate build stage.\nFROM base AS libmongocrypt\n\nRUN apt-get -qq update && \\\n  apt-get -qqy install --no-install-recommends \\\n  git \\\n  ca-certificates \\\n  curl \\\n  build-essential \\\n  libssl-dev \\\n  pkg-config \\\n  python3 \\\n  python3-packaging \\\n  python-is-python3 && \\\n  rm -rf /var/lib/apt/lists/*\n\nCOPY etc/install-libmongocrypt.sh /root/install-libmongocrypt.sh\nRUN cd /root && bash ./install-libmongocrypt.sh\n\n\n# Final dev image (already has Go 1.25.x).\nFROM base\n\nRUN export DEBIAN_FRONTEND=noninteractive && \\\n  export TZ=Etc/UTC && \\\n  apt-get -qq update && \\\n  apt-get -qqy install --reinstall --no-install-recommends \\\n  git \\\n  ca-certificates \\\n  curl \\\n  wget \\\n  tzdata \\\n  pkg-config \\\n  gpg \\\n  apt-utils \\\n  libc6-dev \\\n  gcc \\\n  make \\\n  libkrb5-dev && \\\n  update-ca-certificates && \\\n  rm -rf /var/lib/apt/lists/*\n\n# Install taskfile\nRUN go install github.com/go-task/task/v3/cmd/task@v3.39.2\n\nCOPY etc/docker_entry.sh /root/docker_entry.sh\nCOPY --from=libmongocrypt /root/install /root/install\n\n# Copy the Go driver source for local development and compile checks.\nCOPY . /mongo-go-driver\n\nENV DOCKER_RUNNING=true\nENTRYPOINT [\"/bin/bash\", \"/root/docker_entry.sh\"]\n"
  },
  {
    "path": "LICENSE",
    "content": "                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright [yyyy] [name of copyright owner]\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "README.md",
    "content": "<p align=\"center\"><img src=\"etc/assets/mongo-gopher.png\" width=\"250\"></p>\n<p align=\"center\">\n  <a href=\"https://goreportcard.com/report/go.mongodb.org/mongo-driver/v2\"><img src=\"https://goreportcard.com/badge/go.mongodb.org/mongo-driver/v2\"></a>\n  <a href=\"https://pkg.go.dev/go.mongodb.org/mongo-driver/v2/mongo\"><img src=\"etc/assets/godev-mongo-blue.svg\" alt=\"docs\"></a>\n  <a href=\"https://pkg.go.dev/go.mongodb.org/mongo-driver/v2/bson\"><img src=\"etc/assets/godev-bson-blue.svg\" alt=\"docs\"></a>\n  <a href=\"https://www.mongodb.com/docs/drivers/go/current/\"><img src=\"etc/assets/docs-mongodb-green.svg\"></a>\n  <a href=\"https://securityscorecards.dev/viewer/?uri=github.com/mongodb/mongo-go-driver\">\n    <img src=\"https://api.securityscorecards.dev/projects/github.com/mongodb/mongo-go-driver/badge\" alt=\"OpenSSF Scorecard\" />\n  </a>\n</p>\n\n# MongoDB Go Driver\n\nThe MongoDB supported driver for Go.\n\nSee the following resources to learn more about upgrading from version 1.x to 2.0.:\n\n- [v2.0 Migration Guide](docs/migration-2.0.md)\n- [v2.0 What's New](https://www.mongodb.com/docs/drivers/go/upcoming/whats-new/#what-s-new-in-2.0)\n\nThe MongoDB Go driver follows [semantic versioning](https://semver.org/) for its releases.\n\n## Requirements\n\n- Go 1.19 or higher. We aim to support the latest versions of Go.\n- Go 1.25 or higher is required to run the driver test suite.\n- MongoDB 4.2 and higher.\n\n## Installation\n\nThe recommended way to get started using the MongoDB Go driver is by using Go modules to install the dependency in\nyour project. This can be done either by importing packages from `go.mongodb.org/mongo-driver` and having the build\nstep install the dependency or by explicitly running\n\n```bash\ngo get go.mongodb.org/mongo-driver/v2/mongo\n```\n\nWhen using a version of Go that does not support modules, the driver can be installed using `dep` by running\n\n```bash\ndep ensure -add \"go.mongodb.org/mongo-driver/v2/mongo\"\n```\n\n## Usage\n\nTo get started with the driver, import the `mongo` package and create a `mongo.Client` with the `Connect` function:\n\n```go\nimport (\n    \"context\"\n    \"time\"\n\n    \"go.mongodb.org/mongo-driver/v2/mongo\"\n    \"go.mongodb.org/mongo-driver/v2/mongo/options\"\n    \"go.mongodb.org/mongo-driver/v2/mongo/readpref\"\n)\n\nclient, _ := mongo.Connect(options.Client().ApplyURI(\"mongodb://localhost:27017\"))\n```\n\nMake sure to defer a call to `Disconnect` after instantiating your client:\n\n```go\ndefer func() {\n    if err := client.Disconnect(ctx); err != nil {\n        panic(err)\n    }\n}()\n```\n\nFor more advanced configuration and authentication, see the [documentation for mongo.Connect](https://pkg.go.dev/go.mongodb.org/mongo-driver/v2/mongo#Connect).\n\nCalling `Connect` does not block for server discovery. If you wish to know if a MongoDB server has been found and connected to,\nuse the `Ping` method:\n\n```go\nctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)\ndefer cancel()\n\n_ = client.Ping(ctx, readpref.Primary())\n```\n\nTo insert a document into a collection, first retrieve a `Database` and then `Collection` instance from the `Client`:\n\n```go\ncollection := client.Database(\"testing\").Collection(\"numbers\")\n```\n\nThe `Collection` instance can then be used to insert documents:\n\n```go\nctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)\ndefer cancel()\n\nres, _ := collection.InsertOne(ctx, bson.D{{\"name\", \"pi\"}, {\"value\", 3.14159}})\nid := res.InsertedID\n```\n\nTo use `bson.D`, you will need to add `\"go.mongodb.org/mongo-driver/v2/bson\"` to your imports.\n\nYour import statement should now look like this:\n\n```go\nimport (\n    \"context\"\n    \"log\"\n    \"time\"\n\n    \"go.mongodb.org/mongo-driver/v2/bson\"\n    \"go.mongodb.org/mongo-driver/v2/mongo\"\n    \"go.mongodb.org/mongo-driver/v2/mongo/options\"\n    \"go.mongodb.org/mongo-driver/v2/mongo/readpref\"\n)\n```\n\nSeveral query methods return a cursor, which can be used like this:\n\n```go\nctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)\ndefer cancel()\n\ncur, err := collection.Find(ctx, bson.D{})\nif err != nil {\n  log.Fatal(err)\n}\n\ndefer cur.Close(ctx)\nfor cur.Next(ctx) {\n    var result bson.D\n    if err := cur.Decode(&result); err != nil {\n      log.Fatal(err)\n    }\n\n    // do something with result....\n}\n\nif err := cur.Err(); err != nil {\n    log.Fatal(err)\n}\n```\n\nFor methods that return a single item, a `SingleResult` instance is returned:\n\n```go\nvar result struct {\n    Value float64\n}\n\nfilter := bson.D{{\"name\", \"pi\"}}\nctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)\ndefer cancel()\n\nerr := collection.FindOne(ctx, filter).Decode(&result)\nif errors.Is(err, mongo.ErrNoDocuments) {\n    // Do something when no record was found\n} else if err != nil {\n    log.Fatal(err)\n}\n\n// Do something with result...\n```\n\nAdditional examples and documentation can be found under the examples directory and [on the MongoDB Documentation website](https://www.mongodb.com/docs/drivers/go/current/).\n\n### Network Compression\n\nNetwork compression will reduce bandwidth requirements between MongoDB and the application.\n\nThe Go Driver supports the following compression algorithms:\n\n1. [Snappy](https://google.github.io/snappy/) (`snappy`): available in MongoDB 3.4 and later.\n1. [Zlib](https://zlib.net/) (`zlib`): available in MongoDB 3.6 and later.\n1. [Zstandard](https://github.com/facebook/zstd/) (`zstd`): available in MongoDB 4.2 and later.\n\n#### Specify Compression Algorithms\n\nCompression can be enabled using the `compressors` parameter on the connection string or by using [`ClientOptions.SetCompressors`](https://pkg.go.dev/go.mongodb.org/mongo-driver/mongo/options#ClientOptions.SetCompressors):\n\n```go\nopts := options.Client().ApplyURI(\"mongodb://localhost:27017/?compressors=snappy,zlib,zstd\")\nclient, _ := mongo.Connect(opts)\n```\n\n```go\nopts := options.Client().SetCompressors([]string{\"snappy\", \"zlib\", \"zstd\"})\nclient, _ := mongo.Connect(opts)\n```\n\nIf compressors are set, the Go Driver negotiates with the server to select the first common compressor. For server configuration and defaults, refer to [`networkMessageCompressors`](https://www.mongodb.com/docs/manual/reference/program/mongod/#std-option-mongod.--networkMessageCompressors).\n\nMessages compress when both parties enable network compression; otherwise, messages remain uncompressed\n\n## Support / Feedback\n\nFor issues with, questions about, or feedback for the Go Driver, please look into our [support channels](https://www.mongodb.com/docs/manual/support/), including [StackOverflow](https://stackoverflow.com/questions/tagged/mongodb%20go?sort=Newest).\n\nNew features and bugs can be reported on the [GODRIVER Jira project](https://jira.mongodb.org/browse/GODRIVER).\n\n## Contribution\n\nCheck out the [GODRIVER Jira project](https://jira.mongodb.org/browse/GODRIVER) for tickets that need completing. See our [contribution guidelines](docs/CONTRIBUTING.md) for details.\n\n## Continuous Integration\n\nCommits to master are run automatically on [evergreen](https://evergreen.mongodb.com/waterfall/mongo-go-driver).\n\n## Frequently Encountered Issues\n\nSee our [common issues](docs/common-issues.md) documentation for troubleshooting frequently encountered issues.\n\n## Thanks and Acknowledgement\n\n- The Go Gopher artwork by [@ashleymcnamara](https://github.com/ashleymcnamara)\n- The original Go Gopher was designed by [Renee French](http://reneefrench.blogspot.com/)\n\n## License\n\nThe MongoDB Go Driver is licensed under the [Apache License](LICENSE).\n"
  },
  {
    "path": "THIRD-PARTY-NOTICES",
    "content": "----------------------------------------------------------------------\nLicense notice for AWS V4 signing code from github.com/aws/aws-sdk-go\nAWS SDK for Go\nCopyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.\nCopyright 2014-2015 Stripe, Inc.\n----------------------------------------------------------------------\n\n\n                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n---------------------------------------------------------------------\nLicense notice for gopkg.in/mgo.v2/bson\n---------------------------------------------------------------------\n\nBSON library for Go\n\nCopyright (c) 2010-2013 - Gustavo Niemeyer <gustavo@niemeyer.net>\n\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n1. Redistributions of source code must retain the above copyright notice, this\n   list of conditions and the following disclaimer.\n2. Redistributions in binary form must reproduce the above copyright notice,\n   this list of conditions and the following disclaimer in the documentation\n   and/or other materials provided with the distribution.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\nANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\nWARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\nANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\nLOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\nON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\nSOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n---------------------------------------------------------------------\nLicense notice for JSON and CSV code from github.com/golang/go\n---------------------------------------------------------------------\n\nCopyright (c) 2009 The Go Authors. All rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n   * Redistributions of source code must retain the above copyright\nnotice, this list of conditions and the following disclaimer.\n   * Redistributions in binary form must reproduce the above\ncopyright notice, this list of conditions and the following disclaimer\nin the documentation and/or other materials provided with the\ndistribution.\n   * Neither the name of Google Inc. nor the names of its\ncontributors may be used to endorse or promote products derived from\nthis software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\nLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\nA PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\nOWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\nLIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\nDATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\nTHEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n---------------------------------------------------------------------\nLicense notice for rand code from golang.org/x/exp\n---------------------------------------------------------------------\n\nCopyright (c) 2009 The Go Authors. All rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n   * Redistributions of source code must retain the above copyright\nnotice, this list of conditions and the following disclaimer.\n   * Redistributions in binary form must reproduce the above\ncopyright notice, this list of conditions and the following disclaimer\nin the documentation and/or other materials provided with the\ndistribution.\n   * Neither the name of Google Inc. nor the names of its\ncontributors may be used to endorse or promote products derived from\nthis software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\nLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\nA PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\nOWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\nLIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\nDATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\nTHEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n---------------------------------------------------------------------\nLicense notice for Add64 and Mul64 code from github.com/golang/go\n---------------------------------------------------------------------\n\nCopyright (c) 2009 The Go Authors. All rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n   * Redistributions of source code must retain the above copyright\nnotice, this list of conditions and the following disclaimer.\n   * Redistributions in binary form must reproduce the above\ncopyright notice, this list of conditions and the following disclaimer\nin the documentation and/or other materials provided with the\ndistribution.\n   * Neither the name of Google Inc. nor the names of its\ncontributors may be used to endorse or promote products derived from\nthis software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\nLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\nA PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\nOWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\nLIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\nDATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\nTHEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n----------------------------------------------------------------------\nLicense notice for github.com/davecgh/go-spew\n----------------------------------------------------------------------\n\nISC License\n\nCopyright (c) 2012-2016 Dave Collins <dave@davec.name>\n\nPermission to use, copy, modify, and/or distribute this software for any\npurpose with or without fee is hereby granted, provided that the above\ncopyright notice and this permission notice appear in all copies.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\nWITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\nMERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR\nANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\nWHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN\nACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF\nOR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n\n----------------------------------------------------------------------\nLicense notice for github.com/google/go-cmp\n----------------------------------------------------------------------\n\nCopyright (c) 2017 The Go Authors. All rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n   * Redistributions of source code must retain the above copyright\nnotice, this list of conditions and the following disclaimer.\n   * Redistributions in binary form must reproduce the above\ncopyright notice, this list of conditions and the following disclaimer\nin the documentation and/or other materials provided with the\ndistribution.\n   * Neither the name of Google Inc. nor the names of its\ncontributors may be used to endorse or promote products derived from\nthis software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\nLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\nA PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\nOWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\nLIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\nDATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\nTHEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n----------------------------------------------------------------------\nLicense notice for github.com/klauspost/compress\n----------------------------------------------------------------------\n\nCopyright (c) 2012 The Go Authors. All rights reserved.\nCopyright (c) 2019 Klaus Post. All rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n   * Redistributions of source code must retain the above copyright\nnotice, this list of conditions and the following disclaimer.\n   * Redistributions in binary form must reproduce the above\ncopyright notice, this list of conditions and the following disclaimer\nin the documentation and/or other materials provided with the\ndistribution.\n   * Neither the name of Google Inc. nor the names of its\ncontributors may be used to endorse or promote products derived from\nthis software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\nLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\nA PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\nOWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\nLIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\nDATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\nTHEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n----------------------------------------------------------------------\nLicense notice for github.com/klauspost/compress/snappy\n----------------------------------------------------------------------\n\nCopyright (c) 2011 The Snappy-Go Authors. All rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n   * Redistributions of source code must retain the above copyright\nnotice, this list of conditions and the following disclaimer.\n   * Redistributions in binary form must reproduce the above\ncopyright notice, this list of conditions and the following disclaimer\nin the documentation and/or other materials provided with the\ndistribution.\n   * Neither the name of Google Inc. nor the names of its\ncontributors may be used to endorse or promote products derived from\nthis software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\nLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\nA PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\nOWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\nLIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\nDATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\nTHEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n----------------------------------------------------------------------\nLicense notice for github.com/konsorten/go-windows-terminal-sequences\n----------------------------------------------------------------------\n\n(The MIT License)\n\nCopyright (c) 2017 marvin + konsorten GmbH (open-source@konsorten.de)\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\n----------------------------------------------------------------------\nLicense notice for github.com/markbates/oncer\n----------------------------------------------------------------------\n\nThe MIT License (MIT)\n\nCopyright (c) 2018 Mark Bates\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n----------------------------------------------------------------------\nLicense notice for github.com/markbates/safe\n----------------------------------------------------------------------\n\nThe MIT License (MIT)\n\nCopyright (c) 2018 Mark Bates\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n----------------------------------------------------------------------\nLicense notice for github.com/montanaflynn/stats\n----------------------------------------------------------------------\n\nThe MIT License (MIT)\n\nCopyright (c) 2014-2015 Montana Flynn (https://anonfunction.com)\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n----------------------------------------------------------------------\nLicense notice for github.com/pkg/errors\n----------------------------------------------------------------------\n\nCopyright (c) 2015, Dave Cheney <dave@cheney.net>\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n* Redistributions of source code must retain the above copyright notice, this\n  list of conditions and the following disclaimer.\n\n* Redistributions in binary form must reproduce the above copyright notice,\n  this list of conditions and the following disclaimer in the documentation\n  and/or other materials provided with the distribution.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\nFOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\nDAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\nSERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\nCAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\nOR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n----------------------------------------------------------------------\nLicense notice for github.com/pmezard/go-difflib\n----------------------------------------------------------------------\n\nCopyright (c) 2013, Patrick Mezard\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n    Redistributions of source code must retain the above copyright\nnotice, this list of conditions and the following disclaimer.\n    Redistributions in binary form must reproduce the above copyright\nnotice, this list of conditions and the following disclaimer in the\ndocumentation and/or other materials provided with the distribution.\n    The names of its contributors may not be used to endorse or promote\nproducts derived from this software without specific prior written\npermission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS\nIS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED\nTO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A\nPARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\nHOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED\nTO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\nPROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\nLIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\nNEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\nSOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n----------------------------------------------------------------------\nLicense notice for github.com/rogpeppe/go-internal\n----------------------------------------------------------------------\n\nCopyright (c) 2018 The Go Authors. All rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n   * Redistributions of source code must retain the above copyright\nnotice, this list of conditions and the following disclaimer.\n   * Redistributions in binary form must reproduce the above\ncopyright notice, this list of conditions and the following disclaimer\nin the documentation and/or other materials provided with the\ndistribution.\n   * Neither the name of Google Inc. nor the names of its\ncontributors may be used to endorse or promote products derived from\nthis software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\nLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\nA PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\nOWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\nLIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\nDATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\nTHEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n----------------------------------------------------------------------\nLicense notice for github.com/stretchr/testify\n----------------------------------------------------------------------\n\nMIT License\n\nCopyright (c) 2012-2020 Mat Ryer, Tyler Bunnell and contributors.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n----------------------------------------------------------------------\nLicense notice for github.com/xdg-go/pbkdf2\n----------------------------------------------------------------------\n\n\n                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n----------------------------------------------------------------------\nLicense notice for github.com/xdg-go/scram\n----------------------------------------------------------------------\n\n\n                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n----------------------------------------------------------------------\nLicense notice for github.com/xdg-go/stringprep\n----------------------------------------------------------------------\n\n\n                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n----------------------------------------------------------------------\nLicense notice for github.com/youmark/pkcs8\n----------------------------------------------------------------------\n\nThe MIT License (MIT)\n\nCopyright (c) 2014 youmark\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n----------------------------------------------------------------------\nLicense notice for golang.org/x/crypto\n----------------------------------------------------------------------\n\nCopyright (c) 2009 The Go Authors. All rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n   * Redistributions of source code must retain the above copyright\nnotice, this list of conditions and the following disclaimer.\n   * Redistributions in binary form must reproduce the above\ncopyright notice, this list of conditions and the following disclaimer\nin the documentation and/or other materials provided with the\ndistribution.\n   * Neither the name of Google Inc. nor the names of its\ncontributors may be used to endorse or promote products derived from\nthis software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\nLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\nA PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\nOWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\nLIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\nDATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\nTHEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n----------------------------------------------------------------------\nLicense notice for golang.org/x/sync\n----------------------------------------------------------------------\n\nCopyright (c) 2009 The Go Authors. All rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n   * Redistributions of source code must retain the above copyright\nnotice, this list of conditions and the following disclaimer.\n   * Redistributions in binary form must reproduce the above\ncopyright notice, this list of conditions and the following disclaimer\nin the documentation and/or other materials provided with the\ndistribution.\n   * Neither the name of Google Inc. nor the names of its\ncontributors may be used to endorse or promote products derived from\nthis software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\nLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\nA PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\nOWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\nLIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\nDATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\nTHEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n----------------------------------------------------------------------\nLicense notice for golang.org/x/sys\n----------------------------------------------------------------------\n\nCopyright (c) 2009 The Go Authors. All rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n   * Redistributions of source code must retain the above copyright\nnotice, this list of conditions and the following disclaimer.\n   * Redistributions in binary form must reproduce the above\ncopyright notice, this list of conditions and the following disclaimer\nin the documentation and/or other materials provided with the\ndistribution.\n   * Neither the name of Google Inc. nor the names of its\ncontributors may be used to endorse or promote products derived from\nthis software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\nLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\nA PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\nOWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\nLIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\nDATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\nTHEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n----------------------------------------------------------------------\nLicense notice for golang.org/x/text\n----------------------------------------------------------------------\n\nCopyright (c) 2009 The Go Authors. All rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n   * Redistributions of source code must retain the above copyright\nnotice, this list of conditions and the following disclaimer.\n   * Redistributions in binary form must reproduce the above\ncopyright notice, this list of conditions and the following disclaimer\nin the documentation and/or other materials provided with the\ndistribution.\n   * Neither the name of Google Inc. nor the names of its\ncontributors may be used to endorse or promote products derived from\nthis software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\nLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\nA PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\nOWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\nLIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\nDATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\nTHEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n----------------------------------------------------------------------\nLicense notice for golang.org/x/tools\n----------------------------------------------------------------------\n\nCopyright (c) 2009 The Go Authors. All rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n   * Redistributions of source code must retain the above copyright\nnotice, this list of conditions and the following disclaimer.\n   * Redistributions in binary form must reproduce the above\ncopyright notice, this list of conditions and the following disclaimer\nin the documentation and/or other materials provided with the\ndistribution.\n   * Neither the name of Google Inc. nor the names of its\ncontributors may be used to endorse or promote products derived from\nthis software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\nLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\nA PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\nOWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\nLIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\nDATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\nTHEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n----------------------------------------------------------------------\nLicense notice for golang.org/x/xerrors\n----------------------------------------------------------------------\n\nCopyright (c) 2019 The Go Authors. All rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n   * Redistributions of source code must retain the above copyright\nnotice, this list of conditions and the following disclaimer.\n   * Redistributions in binary form must reproduce the above\ncopyright notice, this list of conditions and the following disclaimer\nin the documentation and/or other materials provided with the\ndistribution.\n   * Neither the name of Google Inc. nor the names of its\ncontributors may be used to endorse or promote products derived from\nthis software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\nLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\nA PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\nOWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\nLIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\nDATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\nTHEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n----------------------------------------------------------------------\nLicense notice for gopkg.in/yaml.v3\n----------------------------------------------------------------------\n\n\nThis project is covered by two different licenses: MIT and Apache.\n\n#### MIT License ####\n\nThe following files were ported to Go from C files of libyaml, and thus\nare still covered by their original MIT license, with the additional\ncopyright staring in 2011 when the project was ported over:\n\n    apic.go emitterc.go parserc.go readerc.go scannerc.go\n    writerc.go yamlh.go yamlprivateh.go\n\nCopyright (c) 2006-2010 Kirill Simonov\nCopyright (c) 2006-2011 Kirill Simonov\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis software and associated documentation files (the \"Software\"), to deal in\nthe Software without restriction, including without limitation the rights to\nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies\nof the Software, and to permit persons to whom the Software is furnished to do\nso, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n### Apache License ###\n\nAll the remaining project files are covered by the Apache license:\n\nCopyright (c) 2011-2019 Canonical Ltd\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n"
  },
  {
    "path": "Taskfile.yml",
    "content": "# See https://taskfile.dev/usage/\nversion: \"3\"\nenv:\n  TEST_TIMEOUT: 1800\n  LONG_TEST_TIMEOUT: 3600\ndotenv: [\".test.env\"]\ntasks:\n  ### Utility tasks. ###\n  default:\n    deps: [build, check-license, check-fmt, check-modules, lint, test-short]\n  add-license: bash etc/check_license.sh -a\n  check-license: bash etc/check_license.sh\n  init-submodule: git submodule update --init\n  build:\n    deps: [install-libmongocrypt]\n    cmds:\n      - go build ./...\n      - go build ${BUILD_TAGS} ./...\n      - task: build-tests\n      - task: compilecheck-119\n  build-tests: go test -short ${BUILD_TAGS} -run ^$$ ./...\n  compilecheck-119:\n    dir: internal/test/compilecheck\n    cmds:\n      - go mod download\n      - GOTOOLCHAIN=auto go test -v -run '^TestCompileCheck/go:1\\.19$'\n  build-compile-check-all: bash etc/run-compile-check-test.sh\n  build-aws-ecs-test: go test -c ./internal/test/aws -o aws.testbin\n  check-fmt:\n    deps: [install-lll, install-gofumpt]\n    cmds:\n      - bash etc/check_fmt.sh\n  check-modules: bash etc/check_modules.sh\n  doc: godoc -http=:6060 -index\n  fmt:\n    deps: [install-gofumpt]\n    cmds:\n      - gofumpt -w .\n  api-report: bash etc/api_report.sh\n  install-libmongocrypt:\n    cmds: [bash etc/install-libmongocrypt.sh]\n    status:\n      - test -d install || test -d /cygdrive/c/libmongocrypt/bin\n  run-docker: bash etc/run_docker.sh\n  run-fuzz: bash etc/run-fuzz.sh\n  cherry-picker: bash etc/cherry-picker.sh\n  pr-task: bash etc/pr-task.sh\n  perf-pr-comment: bash etc/perf-pr-comment.sh\n  # Lint with various GOOS and GOARCH tasks to catch static analysis failures that may only affect\n  # specific operating systems or architectures. For example, staticcheck will only check for 64-bit\n  # alignment of atomically accessed variables on 32-bit architectures (see\n  # https://staticcheck.io/docs/checks#SA1027)\n  lint:\n    cmds:\n      - GOOS=linux GOARCH=386 etc/golangci-lint.sh\n      - GOOS=linux GOARCH=arm etc/golangci-lint.sh\n      - GOOS=linux GOARCH=arm64 etc/golangci-lint.sh\n      - GOOS=linux GOARCH=amd64 etc/golangci-lint.sh\n      - GOOS=linux GOARCH=ppc64le etc/golangci-lint.sh\n      - GOOS=linux GOARCH=s390x etc/golangci-lint.sh\n  govulncheck: bash etc/govulncheck.sh\n  update-notices: bash etc/generate_notices.pl > THIRD-PARTY-NOTICES\n  ### Local testing tasks. ###\n  test: go test ${BUILD_TAGS} -timeout {{.TEST_TIMEOUT}}s -p 1 ./...\n  test-cover:\n    - go test ${BUILD_TAGS} -timeout {{.TEST_TIMEOUT}}s -cover ${COVER_ARGS} -p 1 ./...\n  test-race:\n    - go test ${BUILD_TAGS} -timeout {{.TEST_TIMEOUT}}s -race -p 1 ./...\n  test-short: go test ${BUILD_TAGS} -timeout 60s -short -race ./...\n  test-oidc: bash etc/run-oidc-test.sh\n  test-oidc-remote: bash etc/run-oidc-remote-test.sh\n  test-atlas-connect:\n    - go test -v -run ^TestAtlas$ go.mongodb.org/mongo-driver/v2/internal/cmd/testatlas -tags atlastest >> test.suite\n  test-awskms: bash etc/run-awskms-test.sh\n  test-azurekms: bash etc/run-azurekms-test.sh\n  test-gcpkms: bash etc/run-gcpkms-test.sh\n  test-goleak: bash etc/run-goleak-test.sh\n  ### Local FaaS tasks. ###\n  build-faas-awslambda:\n    requires:\n      vars: [MONGODB_URI]\n    cmds:\n      - make -c internal/cmd/faas/awslambda\n  ### Evergreen specific tasks. ###\n  setup-test: bash etc/setup-test.sh\n  setup-encryption: bash etc/setup-encryption.sh\n  evg-test:\n    - go test -exec \"env PKG_CONFIG_PATH=${PKG_CONFIG_PATH} LD_LIBRARY_PATH=${LD_LIBRARY_PATH} DYLD_LIBRARY_PATH=$MACOS_LIBRARY_PATH}\" ${BUILD_TAGS} -v -timeout {{.TEST_TIMEOUT}}s -p 1 ./... >> test.suite\n  evg-test-enterprise-auth:\n    - go run -tags gssapi ./internal/cmd/testentauth/main.go\n  evg-test-oidc-auth:\n    - go test -v ./internal/test/oidcauth/... >> test.suite\n    - go test -v -race ./internal/test/oidcauth/... >> test.suite\n  evg-test-kmip:\n    - go test -exec \"env PKG_CONFIG_PATH=${PKG_CONFIG_PATH} LD_LIBRARY_PATH=${LD_LIBRARY_PATH} DYLD_LIBRARY_PATH=${MACOS_LIBRARY_PATH}\" ${BUILD_TAGS} -v -timeout {{.TEST_TIMEOUT}}s ./internal/integration -run TestClientSideEncryptionSpec/kmipKMS >> test.suite\n  evg-test-client-side-encryption:\n    - go test -exec \"env PKG_CONFIG_PATH=${PKG_CONFIG_PATH} LD_LIBRARY_PATH=${LD_LIBRARY_PATH} DYLD_LIBRARY_PATH=${MACOS_LIBRARY_PATH}\" ${BUILD_TAGS} -v -timeout {{.TEST_TIMEOUT}}s ./internal/integration -run TestClientSideEncryptionProse >> test.suite\n  evg-test-load-balancers:\n    # Load balancer should be tested with all unified tests as well as tests in the following\n    # components: retryable reads, retryable writes, change streams, initial DNS seedlist discovery.\n    - go test ${BUILD_TAGS} ./internal/integration -run TestInitialDNSSeedlistDiscoverySpec/load_balanced -v -timeout {{.TEST_TIMEOUT}}s >> test.suite\n    - go test ${BUILD_TAGS} ./internal/integration -run TestLoadBalancerSupport -v -timeout {{.TEST_TIMEOUT}}s >> test.suite\n    - go test ${BUILD_TAGS} ./internal/integration -run TestLoadBalancedConnectionHandshake -v -timeout {{.TEST_TIMEOUT}}s >> test.suite\n    - go test ${BUILD_TAGS} ./internal/integration/unified -run TestUnifiedSpec -v -timeout {{.TEST_TIMEOUT}}s >> test.suite\n  evg-test-search-index:\n    # Use the long timeout to wait for the responses from the server.\n    - go test ./internal/integration -run TestSearchIndexProse -v -timeout {{.LONG_TEST_TIMEOUT}}s >> test.suite\n  evg-test-ocsp:\n    - go test -v ./mongo -run TestOCSP ${OCSP_TLS_SHOULD_SUCCEED} >> test.suite\n  evg-test-versioned-api:\n    # Versioned API related tests are in the mongo, integration and unified packages.\n    - go test -exec \"env PKG_CONFIG_PATH=${PKG_CONFIG_PATH} LD_LIBRARY_PATH=${LD_LIBRARY_PATH} DYLD_LIBRARY_PATH=${MACOS_LIBRARY_PATH}\" ${BUILD_TAGS} -v -timeout {{.TEST_TIMEOUT}}s ./mongo >> test.suite\n    - go test -exec \"env PKG_CONFIG_PATH=${PKG_CONFIG_PATH} LD_LIBRARY_PATH=${LD_LIBRARY_PATH} DYLD_LIBRARY_PATH=${MACOS_LIBRARY_PATH}\" ${BUILD_TAGS} -v -timeout {{.TEST_TIMEOUT}}s ./internal/integration >> test.suite\n    - go test -exec \"env PKG_CONFIG_PATH=${PKG_CONFIG_PATH} LD_LIBRARY_PATH=${LD_LIBRARY_PATH} DYLD_LIBRARY_PATH=${MACOS_LIBRARY_PATH}\" ${BUILD_TAGS} -v -timeout {{.TEST_TIMEOUT}}s ./internal/integration/unified >> test.suite\n  evg-test-aws: bash etc/run-mongodb-aws-test.sh\n  evg-test-aws-ecs: bash etc/run-mongodb-aws-ecs-test.sh\n  evg-test-deployed-lambda-aws: bash ${DRIVERS_TOOLS}/.evergreen/aws_lambda/run-deployed-lambda-aws-tests.sh\n  evg-gather-test-suites: find . -name \\*.suite | xargs --no-run-if-empty tar czf test_suite.tgz\n  build-kms-test: go build ${BUILD_TAGS} ./internal/cmd/testkms\n  ### Benchmark specific tasks and support. ###\n  benchmark:\n    deps: [perf-files]\n    cmds:\n      - go test ${BUILD_TAGS} -benchmem -bench=. ./benchmark | test benchmark.suite\n  driver-benchmark:\n    cmds:\n      - go test ./internal/cmd/benchmark -v --fullRun | tee perf.suite\n  ### Internal tasks. ###\n  install-gofumpt:\n    internal: true\n    cmds:\n      - go install mvdan.cc/gofumpt@v0.9.2\n    status:\n      - command -v gofumpt\n  install-lll:\n    internal: true\n    cmds:\n      - go install github.com/walle/lll/...@latest\n"
  },
  {
    "path": "bson/array_codec.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bson\n\nimport (\n\t\"fmt\"\n\t\"reflect\"\n\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n)\n\n// arrayCodec is the Codec used for bsoncore.Array values.\ntype arrayCodec struct{}\n\n// EncodeValue is the ValueEncoder for bsoncore.Array values.\nfunc (ac *arrayCodec) EncodeValue(_ EncodeContext, vw ValueWriter, val reflect.Value) error {\n\tif !val.IsValid() || val.Type() != tCoreArray {\n\t\treturn ValueEncoderError{Name: \"CoreArrayEncodeValue\", Types: []reflect.Type{tCoreArray}, Received: val}\n\t}\n\n\tarr := val.Interface().(bsoncore.Array)\n\treturn copyArrayFromBytes(vw, arr)\n}\n\n// DecodeValue is the ValueDecoder for bsoncore.Array values.\nfunc (ac *arrayCodec) DecodeValue(_ DecodeContext, vr ValueReader, val reflect.Value) error {\n\tif !val.CanSet() || val.Type() != tCoreArray {\n\t\treturn ValueDecoderError{Name: \"CoreArrayDecodeValue\", Types: []reflect.Type{tCoreArray}, Received: val}\n\t}\n\tif vrType := vr.Type(); vrType != TypeArray {\n\t\treturn fmt.Errorf(\"cannot decode %v into a %s\", vrType, val.Type())\n\t}\n\n\tif val.IsNil() {\n\t\tval.Set(reflect.MakeSlice(val.Type(), 0, 0))\n\t}\n\n\tval.SetLen(0)\n\tarr, err := appendArrayBytes(val.Interface().(bsoncore.Array), vr)\n\tval.Set(reflect.ValueOf(arr))\n\treturn err\n}\n"
  },
  {
    "path": "bson/benchmark_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bson\n\nimport (\n\t\"bytes\"\n\t\"compress/gzip\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"io/ioutil\"\n\t\"os\"\n\t\"path\"\n\t\"sync\"\n\t\"testing\"\n)\n\nvar encodetestBsonD D\n\nfunc init() {\n\tb, err := Marshal(encodetestInstance)\n\tif err != nil {\n\t\tpanic(fmt.Sprintf(\"error marshling struct: %v\", err))\n\t}\n\n\terr = Unmarshal(b, &encodetestBsonD)\n\tif err != nil {\n\t\tpanic(fmt.Sprintf(\"error unmarshaling BSON: %v\", err))\n\t}\n}\n\ntype encodetest struct {\n\tField1String  string\n\tField1Int64   int64\n\tField1Float64 float64\n\tField2String  string\n\tField2Int64   int64\n\tField2Float64 float64\n\tField3String  string\n\tField3Int64   int64\n\tField3Float64 float64\n\tField4String  string\n\tField4Int64   int64\n\tField4Float64 float64\n}\n\ntype nestedtest1 struct {\n\tNested nestedtest2\n}\n\ntype nestedtest2 struct {\n\tNested nestedtest3\n}\n\ntype nestedtest3 struct {\n\tNested nestedtest4\n}\n\ntype nestedtest4 struct {\n\tNested nestedtest5\n}\n\ntype nestedtest5 struct {\n\tNested nestedtest6\n}\n\ntype nestedtest6 struct {\n\tNested nestedtest7\n}\n\ntype nestedtest7 struct {\n\tNested nestedtest8\n}\n\ntype nestedtest8 struct {\n\tNested nestedtest9\n}\n\ntype nestedtest9 struct {\n\tNested nestedtest10\n}\n\ntype nestedtest10 struct {\n\tNested nestedtest11\n}\n\ntype nestedtest11 struct {\n\tNested encodetest\n}\n\nvar encodetestInstance = encodetest{\n\tField1String:  \"foo\",\n\tField1Int64:   1,\n\tField1Float64: 3.0,\n\tField2String:  \"bar\",\n\tField2Int64:   2,\n\tField2Float64: 3.1,\n\tField3String:  \"baz\",\n\tField3Int64:   3,\n\tField3Float64: 3.14,\n\tField4String:  \"qux\",\n\tField4Int64:   4,\n\tField4Float64: 3.141,\n}\n\nvar nestedInstance = nestedtest1{\n\tnestedtest2{\n\t\tnestedtest3{\n\t\t\tnestedtest4{\n\t\t\t\tnestedtest5{\n\t\t\t\t\tnestedtest6{\n\t\t\t\t\t\tnestedtest7{\n\t\t\t\t\t\t\tnestedtest8{\n\t\t\t\t\t\t\t\tnestedtest9{\n\t\t\t\t\t\t\t\t\tnestedtest10{\n\t\t\t\t\t\t\t\t\t\tnestedtest11{\n\t\t\t\t\t\t\t\t\t\t\tencodetest{\n\t\t\t\t\t\t\t\t\t\t\t\tField1String:  \"foo\",\n\t\t\t\t\t\t\t\t\t\t\t\tField1Int64:   1,\n\t\t\t\t\t\t\t\t\t\t\t\tField1Float64: 3.0,\n\t\t\t\t\t\t\t\t\t\t\t\tField2String:  \"bar\",\n\t\t\t\t\t\t\t\t\t\t\t\tField2Int64:   2,\n\t\t\t\t\t\t\t\t\t\t\t\tField2Float64: 3.1,\n\t\t\t\t\t\t\t\t\t\t\t\tField3String:  \"baz\",\n\t\t\t\t\t\t\t\t\t\t\t\tField3Int64:   3,\n\t\t\t\t\t\t\t\t\t\t\t\tField3Float64: 3.14,\n\t\t\t\t\t\t\t\t\t\t\t\tField4String:  \"qux\",\n\t\t\t\t\t\t\t\t\t\t\t\tField4Int64:   4,\n\t\t\t\t\t\t\t\t\t\t\t\tField4Float64: 3.141,\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t},\n}\n\nconst extendedBSONDir = \"../testdata/extended_bson\"\n\nvar (\n\textJSONFiles   map[string]map[string]any\n\textJSONFilesMu sync.Mutex\n)\n\n// readExtJSONFile reads the GZIP-compressed extended JSON document from the given filename in the\n// \"extended BSON\" test data directory (../testdata/extended_bson) and returns it as a\n// map[string]any. It panics on any errors.\nfunc readExtJSONFile(filename string) map[string]any {\n\textJSONFilesMu.Lock()\n\tdefer extJSONFilesMu.Unlock()\n\tif v, ok := extJSONFiles[filename]; ok {\n\t\treturn v\n\t}\n\tfilePath := path.Join(extendedBSONDir, filename)\n\tfile, err := os.Open(filePath)\n\tif err != nil {\n\t\tpanic(fmt.Sprintf(\"error opening file %q: %s\", filePath, err))\n\t}\n\tdefer func() {\n\t\t_ = file.Close()\n\t}()\n\n\tgz, err := gzip.NewReader(file)\n\tif err != nil {\n\t\tpanic(fmt.Sprintf(\"error creating GZIP reader: %s\", err))\n\t}\n\tdefer func() {\n\t\t_ = gz.Close()\n\t}()\n\n\tdata, err := ioutil.ReadAll(gz)\n\tif err != nil {\n\t\tpanic(fmt.Sprintf(\"error reading GZIP contents of file: %s\", err))\n\t}\n\n\tvar v map[string]any\n\terr = UnmarshalExtJSON(data, false, &v)\n\tif err != nil {\n\t\tpanic(fmt.Sprintf(\"error unmarshalling extended JSON: %s\", err))\n\t}\n\n\tif extJSONFiles == nil {\n\t\textJSONFiles = make(map[string]map[string]any)\n\t}\n\textJSONFiles[filename] = v\n\treturn v\n}\n\nfunc BenchmarkMarshal(b *testing.B) {\n\tcases := []struct {\n\t\tdesc  string\n\t\tvalue any\n\t}{\n\t\t{\n\t\t\tdesc:  \"simple struct\",\n\t\t\tvalue: encodetestInstance,\n\t\t},\n\t\t{\n\t\t\tdesc:  \"nested struct\",\n\t\t\tvalue: nestedInstance,\n\t\t},\n\t\t{\n\t\t\tdesc:  \"simple D\",\n\t\t\tvalue: encodetestBsonD,\n\t\t},\n\t\t{\n\t\t\tdesc:  \"deep_bson.json.gz\",\n\t\t\tvalue: readExtJSONFile(\"deep_bson.json.gz\"),\n\t\t},\n\t\t{\n\t\t\tdesc:  \"flat_bson.json.gz\",\n\t\t\tvalue: readExtJSONFile(\"flat_bson.json.gz\"),\n\t\t},\n\t\t{\n\t\t\tdesc:  \"full_bson.json.gz\",\n\t\t\tvalue: readExtJSONFile(\"full_bson.json.gz\"),\n\t\t},\n\t}\n\n\tb.Run(\"BSON\", func(b *testing.B) {\n\t\tfor _, tc := range cases {\n\t\t\ttc := tc // Capture range variable.\n\n\t\t\tb.Run(tc.desc, func(b *testing.B) {\n\t\t\t\tb.ReportAllocs()\n\t\t\t\tb.RunParallel(func(pb *testing.PB) {\n\t\t\t\t\tfor pb.Next() {\n\t\t\t\t\t\t_, err := Marshal(tc.value)\n\t\t\t\t\t\tif err != nil {\n\t\t\t\t\t\t\tb.Errorf(\"error marshalling BSON: %s\", err)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t})\n\t\t}\n\t})\n\n\tb.Run(\"extJSON\", func(b *testing.B) {\n\t\tfor _, tc := range cases {\n\t\t\ttc := tc // Capture range variable.\n\n\t\t\tb.Run(tc.desc, func(b *testing.B) {\n\t\t\t\tb.ReportAllocs()\n\t\t\t\tb.RunParallel(func(pb *testing.PB) {\n\t\t\t\t\tfor pb.Next() {\n\t\t\t\t\t\t_, err := MarshalExtJSON(tc.value, true, false)\n\t\t\t\t\t\tif err != nil {\n\t\t\t\t\t\t\tb.Errorf(\"error marshalling extended JSON: %s\", err)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t})\n\t\t}\n\t})\n\n\tb.Run(\"JSON\", func(b *testing.B) {\n\t\tfor _, tc := range cases {\n\t\t\ttc := tc // Capture range variable.\n\n\t\t\tb.Run(tc.desc, func(b *testing.B) {\n\t\t\t\tb.ReportAllocs()\n\t\t\t\tb.RunParallel(func(pb *testing.PB) {\n\t\t\t\t\tfor pb.Next() {\n\t\t\t\t\t\t_, err := json.Marshal(tc.value)\n\t\t\t\t\t\tif err != nil {\n\t\t\t\t\t\t\tb.Errorf(\"error marshalling JSON: %s\", err)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t})\n\t\t}\n\t})\n}\n\nfunc BenchmarkUnmarshal(b *testing.B) {\n\ttype testcase struct {\n\t\tdesc  string\n\t\tvalue any\n\t\tdst   func() any\n\t}\n\n\tcases := []testcase{\n\t\t{\n\t\t\tdesc:  \"simple struct\",\n\t\t\tvalue: encodetestInstance,\n\t\t\tdst:   func() any { return &encodetest{} },\n\t\t},\n\t\t{\n\t\t\tdesc:  \"nested struct\",\n\t\t\tvalue: nestedInstance,\n\t\t\tdst:   func() any { return &encodetest{} },\n\t\t},\n\t}\n\n\tinputs := []struct {\n\t\tname  string\n\t\tvalue any\n\t}{\n\t\t{\n\t\t\tname:  \"simple\",\n\t\t\tvalue: encodetestInstance,\n\t\t},\n\t\t{\n\t\t\tname:  \"nested\",\n\t\t\tvalue: nestedInstance,\n\t\t},\n\t\t{\n\t\t\tname:  \"deep_bson.json.gz\",\n\t\t\tvalue: readExtJSONFile(\"deep_bson.json.gz\"),\n\t\t},\n\t\t{\n\t\t\tname:  \"flat_bson.json.gz\",\n\t\t\tvalue: readExtJSONFile(\"flat_bson.json.gz\"),\n\t\t},\n\t\t{\n\t\t\tname:  \"full_bson.json.gz\",\n\t\t\tvalue: readExtJSONFile(\"full_bson.json.gz\"),\n\t\t},\n\t}\n\n\tdestinations := []struct {\n\t\tname string\n\t\tdst  func() any\n\t}{\n\t\t{\n\t\t\tname: \"to map\",\n\t\t\tdst:  func() any { return &map[string]any{} },\n\t\t},\n\t\t{\n\t\t\tname: \"to D\",\n\t\t\tdst:  func() any { return &D{} },\n\t\t},\n\t}\n\n\tfor _, input := range inputs {\n\t\tfor _, dest := range destinations {\n\t\t\tcases = append(cases, testcase{\n\t\t\t\tdesc:  input.name + \" \" + dest.name,\n\t\t\t\tvalue: input.value,\n\t\t\t\tdst:   dest.dst,\n\t\t\t})\n\t\t}\n\t}\n\n\tb.Run(\"BSON\", func(b *testing.B) {\n\t\tfor _, tc := range cases {\n\t\t\ttc := tc // Capture range variable.\n\n\t\t\tb.Run(tc.desc, func(b *testing.B) {\n\t\t\t\tb.ReportAllocs()\n\t\t\t\tdata, err := Marshal(tc.value)\n\t\t\t\tif err != nil {\n\t\t\t\t\tb.Errorf(\"error marshalling BSON: %s\", err)\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tb.SetBytes(int64(len(data)))\n\t\t\t\tb.ResetTimer()\n\t\t\t\tb.RunParallel(func(pb *testing.PB) {\n\t\t\t\t\tfor pb.Next() {\n\t\t\t\t\t\tval := tc.dst()\n\t\t\t\t\t\terr := Unmarshal(data, val)\n\t\t\t\t\t\tif err != nil {\n\t\t\t\t\t\t\tb.Errorf(\"error unmarshalling BSON: %s\", err)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t})\n\t\t}\n\t})\n\n\tb.Run(\"extJSON\", func(b *testing.B) {\n\t\tfor _, tc := range cases {\n\t\t\ttc := tc // Capture range variable.\n\n\t\t\tb.Run(tc.desc, func(b *testing.B) {\n\t\t\t\tb.ReportAllocs()\n\t\t\t\tdata, err := MarshalExtJSON(tc.value, true, false)\n\t\t\t\tif err != nil {\n\t\t\t\t\tb.Errorf(\"error marshalling extended JSON: %s\", err)\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tb.SetBytes(int64(len(data)))\n\t\t\t\tb.ResetTimer()\n\t\t\t\tb.RunParallel(func(pb *testing.PB) {\n\t\t\t\t\tfor pb.Next() {\n\t\t\t\t\t\tval := tc.dst()\n\t\t\t\t\t\terr := UnmarshalExtJSON(data, true, val)\n\t\t\t\t\t\tif err != nil {\n\t\t\t\t\t\t\tb.Errorf(\"error unmarshalling extended JSON: %s\", err)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t})\n\t\t}\n\t})\n\n\tb.Run(\"JSON\", func(b *testing.B) {\n\t\tfor _, tc := range cases {\n\t\t\ttc := tc // Capture range variable.\n\n\t\t\tb.Run(tc.desc, func(b *testing.B) {\n\t\t\t\tb.ReportAllocs()\n\t\t\t\tdata, err := json.Marshal(tc.value)\n\t\t\t\tif err != nil {\n\t\t\t\t\tb.Errorf(\"error marshalling JSON: %s\", err)\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tb.SetBytes(int64(len(data)))\n\t\t\t\tb.ResetTimer()\n\t\t\t\tb.RunParallel(func(pb *testing.PB) {\n\t\t\t\t\tfor pb.Next() {\n\t\t\t\t\t\tval := tc.dst()\n\t\t\t\t\t\terr := json.Unmarshal(data, val)\n\t\t\t\t\t\tif err != nil {\n\t\t\t\t\t\t\tb.Errorf(\"error unmarshalling JSON: %s\", err)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t})\n\t\t}\n\t})\n}\n\n// The following benchmarks are copied from the Go standard library's\n// encoding/json package.\n\ntype codeResponse struct {\n\tTree     *codeNode `json:\"tree\"`\n\tUsername string    `json:\"username\"`\n}\n\ntype codeNode struct {\n\tName     string      `json:\"name\"`\n\tKids     []*codeNode `json:\"kids\"`\n\tCLWeight float64     `json:\"cl_weight\"`\n\tTouches  int         `json:\"touches\"`\n\tMinT     int64       `json:\"min_t\"`\n\tMaxT     int64       `json:\"max_t\"`\n\tMeanT    int64       `json:\"mean_t\"`\n}\n\nvar (\n\tcodeJSON   []byte\n\tcodeBSON   []byte\n\tcodeStruct codeResponse\n)\n\nfunc codeInit() {\n\tf, err := os.Open(\"testdata/code.json.gz\")\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tdefer f.Close()\n\tgz, err := gzip.NewReader(f)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tdata, err := io.ReadAll(gz)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tcodeJSON = data\n\n\tif err := json.Unmarshal(codeJSON, &codeStruct); err != nil {\n\t\tpanic(\"json.Unmarshal code.json: \" + err.Error())\n\t}\n\n\tif data, err = json.Marshal(&codeStruct); err != nil {\n\t\tpanic(\"json.Marshal code.json: \" + err.Error())\n\t}\n\n\tif codeBSON, err = Marshal(&codeStruct); err != nil {\n\t\tpanic(\"Marshal code.json: \" + err.Error())\n\t}\n\n\tif !bytes.Equal(data, codeJSON) {\n\t\tprintln(\"different lengths\", len(data), len(codeJSON))\n\t\tfor i := 0; i < len(data) && i < len(codeJSON); i++ {\n\t\t\tif data[i] != codeJSON[i] {\n\t\t\t\tprintln(\"re-marshal: changed at byte\", i)\n\t\t\t\tprintln(\"orig: \", string(codeJSON[i-10:i+10]))\n\t\t\t\tprintln(\"new: \", string(data[i-10:i+10]))\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tpanic(\"re-marshal code.json: different result\")\n\t}\n}\n\nfunc BenchmarkCodeUnmarshal(b *testing.B) {\n\tif codeJSON == nil {\n\t\tb.StopTimer()\n\t\tcodeInit()\n\t\tb.StartTimer()\n\t}\n\tb.Run(\"BSON\", func(b *testing.B) {\n\t\tb.ReportAllocs()\n\t\tb.RunParallel(func(pb *testing.PB) {\n\t\t\tfor pb.Next() {\n\t\t\t\tvar r codeResponse\n\t\t\t\tif err := Unmarshal(codeBSON, &r); err != nil {\n\t\t\t\t\tb.Fatal(\"Unmarshal:\", err)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t\tb.SetBytes(int64(len(codeBSON)))\n\t})\n\tb.Run(\"JSON\", func(b *testing.B) {\n\t\tb.ReportAllocs()\n\t\tb.RunParallel(func(pb *testing.PB) {\n\t\t\tfor pb.Next() {\n\t\t\t\tvar r codeResponse\n\t\t\t\tif err := json.Unmarshal(codeJSON, &r); err != nil {\n\t\t\t\t\tb.Fatal(\"json.Unmarshal:\", err)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t\tb.SetBytes(int64(len(codeJSON)))\n\t})\n}\n\nfunc BenchmarkCodeMarshal(b *testing.B) {\n\tif codeJSON == nil {\n\t\tb.StopTimer()\n\t\tcodeInit()\n\t\tb.StartTimer()\n\t}\n\tb.Run(\"BSON\", func(b *testing.B) {\n\t\tb.ReportAllocs()\n\t\tb.RunParallel(func(pb *testing.PB) {\n\t\t\tfor pb.Next() {\n\t\t\t\tif _, err := Marshal(&codeStruct); err != nil {\n\t\t\t\t\tb.Fatal(\"Marshal:\", err)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t\tb.SetBytes(int64(len(codeBSON)))\n\t})\n\tb.Run(\"JSON\", func(b *testing.B) {\n\t\tb.ReportAllocs()\n\t\tb.RunParallel(func(pb *testing.PB) {\n\t\t\tfor pb.Next() {\n\t\t\t\tif _, err := json.Marshal(&codeStruct); err != nil {\n\t\t\t\t\tb.Fatal(\"json.Marshal:\", err)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t\tb.SetBytes(int64(len(codeJSON)))\n\t})\n}\n"
  },
  {
    "path": "bson/bson_binary_vector_spec_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2024-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bson\n\nimport (\n\t\"encoding/hex\"\n\t\"encoding/json\"\n\t\"os\"\n\t\"path\"\n\t\"testing\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/require\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/spectest\"\n)\n\nvar bsonBinaryVectorDir = spectest.Path(\"bson-binary-vector\")\n\ntype bsonBinaryVectorTests struct {\n\tDescription string                     `json:\"description\"`\n\tTestKey     string                     `json:\"test_key\"`\n\tTests       []bsonBinaryVectorTestCase `json:\"tests\"`\n}\n\ntype bsonBinaryVectorTestCase struct {\n\tDescription   string          `json:\"description\"`\n\tValid         bool            `json:\"valid\"`\n\tVector        json.RawMessage `json:\"vector\"`\n\tDtypeHex      string          `json:\"dtype_hex\"`\n\tDtypeAlias    string          `json:\"dtype_alias\"`\n\tPadding       int             `json:\"padding\"`\n\tCanonicalBson string          `json:\"canonical_bson\"`\n}\n\nfunc TestBsonBinaryVectorSpec(t *testing.T) {\n\tt.Parallel()\n\n\tjsonFiles, err := findJSONFilesInDir(bsonBinaryVectorDir)\n\trequire.NoErrorf(t, err, \"error finding JSON files in %s: %v\", bsonBinaryVectorDir, err)\n\n\tfor _, file := range jsonFiles {\n\t\tfilepath := path.Join(bsonBinaryVectorDir, file)\n\t\tcontent, err := os.ReadFile(filepath)\n\t\trequire.NoErrorf(t, err, \"reading test file %s\", filepath)\n\n\t\tvar tests bsonBinaryVectorTests\n\t\trequire.NoErrorf(t, json.Unmarshal(content, &tests), \"parsing test file %s\", filepath)\n\n\t\tt.Run(tests.Description, func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tfor _, test := range tests.Tests {\n\t\t\t\ttest := test\n\t\t\t\tt.Run(test.Description, func(t *testing.T) {\n\t\t\t\t\tt.Parallel()\n\n\t\t\t\t\trunBsonBinaryVectorTest(t, tests.TestKey, test)\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\t}\n\n\tt.Run(\"Padding specified with no vector data PACKED_BIT\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tt.Run(\"Marshaling\", func(t *testing.T) {\n\t\t\t_, err := NewPackedBitVector(nil, 1)\n\t\t\trequire.EqualError(t, err, errNonZeroVectorPadding.Error())\n\t\t})\n\t})\n\n\tt.Run(\"Exceeding maximum padding PACKED_BIT\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tt.Run(\"Marshaling\", func(t *testing.T) {\n\t\t\t_, err := NewPackedBitVector(nil, 8)\n\t\t\trequire.EqualError(t, err, errVectorPaddingTooLarge.Error())\n\t\t})\n\t})\n}\n\nfunc decodeTestSlice[T int8 | float32 | byte](t *testing.T, data []byte) []T {\n\tt.Helper()\n\n\tif len(data) == 0 {\n\t\treturn nil\n\t}\n\tvar s []float64\n\terr := UnmarshalExtJSON(data, true, &s)\n\trequire.NoError(t, err)\n\n\tv := make([]T, len(s))\n\tfor i, e := range s {\n\t\tv[i] = T(e)\n\t}\n\treturn v\n}\n\nfunc runBsonBinaryVectorTest(t *testing.T, testKey string, test bsonBinaryVectorTestCase) {\n\ttestVector := make(map[string]Vector)\n\tswitch alias := test.DtypeHex; alias {\n\tcase \"0x03\":\n\t\ttestVector[testKey] = Vector{\n\t\t\tdType:    Int8Vector,\n\t\t\tint8Data: decodeTestSlice[int8](t, test.Vector),\n\t\t}\n\tcase \"0x27\":\n\t\ttestVector[testKey] = Vector{\n\t\t\tdType:       Float32Vector,\n\t\t\tfloat32Data: decodeTestSlice[float32](t, test.Vector),\n\t\t}\n\tcase \"0x10\":\n\t\ttestVector[testKey] = Vector{\n\t\t\tdType:      PackedBitVector,\n\t\t\tbitData:    decodeTestSlice[byte](t, test.Vector),\n\t\t\tbitPadding: uint8(test.Padding),\n\t\t}\n\tdefault:\n\t\tt.Fatalf(\"unsupported vector type: %s\", alias)\n\t}\n\n\ttestBSON, err := hex.DecodeString(test.CanonicalBson)\n\trequire.NoError(t, err, \"decoding canonical BSON\")\n\n\tt.Run(\"Unmarshaling\", func(t *testing.T) {\n\t\tspectest.CheckSkip(t)\n\n\t\terrMap := map[string]string{\n\t\t\t\"FLOAT32 with padding\":                             \"padding must be 0\",\n\t\t\t\"INT8 with padding\":                                \"padding must be 0\",\n\t\t\t\"Padding specified with no vector data PACKED_BIT\": \"padding must be 0\",\n\t\t\t\"Exceeding maximum padding PACKED_BIT\":             \"padding cannot be larger than 7\",\n\t\t}\n\n\t\tt.Parallel()\n\n\t\tvar got map[string]Vector\n\t\terr := Unmarshal(testBSON, &got)\n\t\tif test.Valid {\n\t\t\trequire.NoError(t, err)\n\t\t\trequire.Equal(t, testVector, got)\n\t\t} else if errMsg, ok := errMap[test.Description]; ok {\n\t\t\trequire.ErrorContains(t, err, errMsg)\n\t\t} else {\n\t\t\trequire.Error(t, err)\n\t\t}\n\t})\n\n\tt.Run(\"Marshaling\", func(t *testing.T) {\n\t\tspectest.CheckSkip(t)\n\n\t\tt.Parallel()\n\n\t\tgot, err := Marshal(testVector)\n\t\trequire.NoError(t, err)\n\t\trequire.Equal(t, testBSON, got)\n\t})\n}\n"
  },
  {
    "path": "bson/bson_corpus_spec_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bson\n\nimport (\n\t\"bytes\"\n\t\"encoding/hex\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"os\"\n\t\"path\"\n\t\"reflect\"\n\t\"strconv\"\n\t\"strings\"\n\t\"testing\"\n\t\"unicode\"\n\t\"unicode/utf8\"\n\n\t\"github.com/google/go-cmp/cmp\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/require\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/spectest\"\n)\n\ntype testCase struct {\n\tDescription  string                `json:\"description\"`\n\tBsonType     string                `json:\"bson_type\"`\n\tTestKey      *string               `json:\"test_key\"`\n\tValid        []validityTestCase    `json:\"valid\"`\n\tDecodeErrors []decodeErrorTestCase `json:\"decodeErrors\"`\n\tParseErrors  []parseErrorTestCase  `json:\"parseErrors\"`\n\tDeprecated   *bool                 `json:\"deprecated\"`\n}\n\ntype validityTestCase struct {\n\tDescription       string  `json:\"description\"`\n\tCanonicalBson     string  `json:\"canonical_bson\"`\n\tCanonicalExtJSON  string  `json:\"canonical_extjson\"`\n\tRelaxedExtJSON    *string `json:\"relaxed_extjson\"`\n\tDegenerateBSON    *string `json:\"degenerate_bson\"`\n\tDegenerateExtJSON *string `json:\"degenerate_extjson\"`\n\tConvertedBSON     *string `json:\"converted_bson\"`\n\tConvertedExtJSON  *string `json:\"converted_extjson\"`\n\tLossy             *bool   `json:\"lossy\"`\n}\n\ntype decodeErrorTestCase struct {\n\tDescription string `json:\"description\"`\n\tBson        string `json:\"bson\"`\n}\n\ntype parseErrorTestCase struct {\n\tDescription string `json:\"description\"`\n\tString      string `json:\"string\"`\n}\n\nvar dataDir = spectest.Path(\"bson-corpus/tests\")\n\nfunc findJSONFilesInDir(dir string) ([]string, error) {\n\tfiles := make([]string, 0)\n\n\tentries, err := os.ReadDir(dir)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tfor _, entry := range entries {\n\t\tif entry.IsDir() || path.Ext(entry.Name()) != \".json\" {\n\t\t\tcontinue\n\t\t}\n\n\t\tfiles = append(files, entry.Name())\n\t}\n\n\treturn files, nil\n}\n\n// seedExtJSON will add the byte representation of the \"extJSON\" string to the fuzzer's coprus.\nfunc seedExtJSON(f *testing.F, extJSON string, extJSONType string, desc string) {\n\tjbytes, err := jsonToBytes(extJSON, extJSONType, desc)\n\tif err != nil {\n\t\tf.Fatalf(\"failed to convert JSON to bytes: %v\", err)\n\t}\n\n\tf.Add(jbytes)\n}\n\n// seedTestCase will add the byte representation for each \"extJSON\" string of each valid test case to the fuzzer's\n// corpus.\nfunc seedTestCase(f *testing.F, tcase *testCase) {\n\tfor _, vtc := range tcase.Valid {\n\t\tseedExtJSON(f, vtc.CanonicalExtJSON, \"canonical\", vtc.Description)\n\n\t\t// Seed the relaxed extended JSON.\n\t\tif vtc.RelaxedExtJSON != nil {\n\t\t\tseedExtJSON(f, *vtc.RelaxedExtJSON, \"relaxed\", vtc.Description)\n\t\t}\n\n\t\t// Seed the degenerate extended JSON.\n\t\tif vtc.DegenerateExtJSON != nil {\n\t\t\tseedExtJSON(f, *vtc.DegenerateExtJSON, \"degenerate\", vtc.Description)\n\t\t}\n\n\t\t// Seed the converted extended JSON.\n\t\tif vtc.ConvertedExtJSON != nil {\n\t\t\tseedExtJSON(f, *vtc.ConvertedExtJSON, \"converted\", vtc.Description)\n\t\t}\n\t}\n}\n\n// seedBSONCorpus will unmarshal the data from \"testdata/bson-corpus\" into a slice of \"testCase\" structs and then\n// marshal the \"*_extjson\" field of each \"validityTestCase\" into a slice of bytes to seed the fuzz corpus.\nfunc seedBSONCorpus(f *testing.F) {\n\tfileNames, err := findJSONFilesInDir(dataDir)\n\tif err != nil {\n\t\tf.Fatalf(\"failed to find JSON files in directory %q: %v\", dataDir, err)\n\t}\n\n\tfor _, fileName := range fileNames {\n\t\tfilePath := path.Join(dataDir, fileName)\n\n\t\tfile, err := os.Open(filePath)\n\t\tif err != nil {\n\t\t\tf.Fatalf(\"failed to open file %q: %v\", filePath, err)\n\t\t}\n\n\t\tvar tcase testCase\n\t\tif err := json.NewDecoder(file).Decode(&tcase); err != nil {\n\t\t\tf.Fatal(err)\n\t\t}\n\n\t\tseedTestCase(f, &tcase)\n\t}\n}\n\nfunc needsEscapedUnicode(bsonType string) bool {\n\treturn bsonType == \"0x02\" || bsonType == \"0x0D\" || bsonType == \"0x0E\" || bsonType == \"0x0F\"\n}\n\nfunc unescapeUnicode(s, bsonType string) string {\n\tif !needsEscapedUnicode(bsonType) {\n\t\treturn s\n\t}\n\n\tnewS := \"\"\n\n\tfor i := 0; i < len(s); i++ {\n\t\tc := s[i]\n\t\tswitch c {\n\t\tcase '\\\\':\n\t\t\tswitch s[i+1] {\n\t\t\tcase 'u':\n\t\t\t\tus := s[i : i+6]\n\t\t\t\tu, err := strconv.Unquote(strings.Replace(strconv.Quote(us), `\\\\u`, `\\u`, 1))\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn \"\"\n\t\t\t\t}\n\t\t\t\tfor _, r := range u {\n\t\t\t\t\tif r < ' ' {\n\t\t\t\t\t\tnewS += fmt.Sprintf(`\\u%04x`, r)\n\t\t\t\t\t} else {\n\t\t\t\t\t\tnewS += string(r)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\ti += 5\n\t\t\tdefault:\n\t\t\t\tnewS += string(c)\n\t\t\t}\n\t\tdefault:\n\t\t\tif c > unicode.MaxASCII {\n\t\t\t\tr, size := utf8.DecodeRune([]byte(s[i:]))\n\t\t\t\tnewS += string(r)\n\t\t\t\ti += size - 1\n\t\t\t} else {\n\t\t\t\tnewS += string(c)\n\t\t\t}\n\t\t}\n\t}\n\n\treturn newS\n}\n\nfunc normalizeCanonicalDouble(t *testing.T, key string, cEJ string) string {\n\t// Unmarshal string into map\n\tcEJMap := make(map[string]map[string]string)\n\terr := json.Unmarshal([]byte(cEJ), &cEJMap)\n\trequire.NoError(t, err)\n\n\t// Parse the float contained by the map.\n\texpectedString := cEJMap[key][\"$numberDouble\"]\n\texpectedFloat, err := strconv.ParseFloat(expectedString, 64)\n\trequire.NoError(t, err)\n\n\t// Normalize the string\n\treturn fmt.Sprintf(`{\"%s\":{\"$numberDouble\":\"%s\"}}`, key, formatDouble(expectedFloat))\n}\n\nfunc normalizeRelaxedDouble(t *testing.T, key string, rEJ string) string {\n\t// Unmarshal string into map\n\trEJMap := make(map[string]float64)\n\terr := json.Unmarshal([]byte(rEJ), &rEJMap)\n\tif err != nil {\n\t\treturn normalizeCanonicalDouble(t, key, rEJ)\n\t}\n\n\t// Parse the float contained by the map.\n\texpectedFloat := rEJMap[key]\n\n\t// Normalize the string\n\treturn fmt.Sprintf(`{\"%s\":%s}`, key, formatDouble(expectedFloat))\n}\n\n// bsonToNative decodes the BSON bytes (b) into a native Document\nfunc bsonToNative(t *testing.T, b []byte, bType, testDesc string) D {\n\tvar doc D\n\terr := Unmarshal(b, &doc)\n\trequire.NoErrorf(t, err, \"%s: decoding %s BSON\", testDesc, bType)\n\treturn doc\n}\n\n// nativeToBSON encodes the native Document (doc) into canonical BSON and compares it to the expected\n// canonical BSON (cB)\nfunc nativeToBSON(t *testing.T, cB []byte, doc D, testDesc, bType, docSrcDesc string) {\n\tactual, err := Marshal(doc)\n\trequire.NoErrorf(t, err, \"%s: encoding %s BSON\", testDesc, bType)\n\n\tif diff := cmp.Diff(cB, actual); diff != \"\" {\n\t\tt.Errorf(\"%s: 'native_to_bson(%s) = cB' failed (-want, +got):\\n-%v\\n+%v\\n\",\n\t\t\ttestDesc, docSrcDesc, cB, actual)\n\t\tt.FailNow()\n\t}\n}\n\n// jsonToNative decodes the extended JSON string (ej) into a native Document\nfunc jsonToNative(ej, ejType, testDesc string) (D, error) {\n\tvar doc D\n\tif err := UnmarshalExtJSON([]byte(ej), ejType != \"relaxed\", &doc); err != nil {\n\t\treturn nil, fmt.Errorf(\"%s: decoding %s extended JSON: %w\", testDesc, ejType, err)\n\t}\n\treturn doc, nil\n}\n\n// jsonToBytes decodes the extended JSON string (ej) into canonical BSON and then encodes it into a byte slice.\nfunc jsonToBytes(ej, ejType, testDesc string) ([]byte, error) {\n\tnative, err := jsonToNative(ej, ejType, testDesc)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tb, err := Marshal(native)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"%s: encoding %s BSON: %w\", testDesc, ejType, err)\n\t}\n\n\treturn b, nil\n}\n\n// nativeToJSON encodes the native Document (doc) into an extended JSON string\nfunc nativeToJSON(t *testing.T, ej string, doc D, testDesc, ejType, ejShortName, docSrcDesc string) {\n\tactualEJ, err := MarshalExtJSON(doc, ejType != \"relaxed\", true)\n\trequire.NoErrorf(t, err, \"%s: encoding %s extended JSON\", testDesc, ejType)\n\n\tif diff := cmp.Diff(ej, string(actualEJ)); diff != \"\" {\n\t\tt.Errorf(\"%s: 'native_to_%s_extended_json(%s) = %s' failed (-want, +got):\\n%s\\n\",\n\t\t\ttestDesc, ejType, docSrcDesc, ejShortName, diff)\n\t\tt.FailNow()\n\t}\n}\n\nfunc runTest(t *testing.T, file string) {\n\tfilepath := path.Join(dataDir, file)\n\tcontent, err := os.ReadFile(filepath)\n\trequire.NoError(t, err)\n\n\tt.Run(file, func(t *testing.T) {\n\t\tvar test testCase\n\t\trequire.NoError(t, json.Unmarshal(content, &test))\n\n\t\tt.Run(\"valid\", func(t *testing.T) {\n\t\t\tfor _, v := range test.Valid {\n\t\t\t\tt.Run(v.Description, func(t *testing.T) {\n\t\t\t\t\t// get canonical BSON\n\t\t\t\t\tcB, err := hex.DecodeString(v.CanonicalBson)\n\t\t\t\t\trequire.NoErrorf(t, err, \"%s: reading canonical BSON\", v.Description)\n\n\t\t\t\t\t// get canonical extended JSON\n\t\t\t\t\tvar compactEJ bytes.Buffer\n\t\t\t\t\trequire.NoError(t, json.Compact(&compactEJ, []byte(v.CanonicalExtJSON)))\n\t\t\t\t\tcEJ := unescapeUnicode(compactEJ.String(), test.BsonType)\n\t\t\t\t\tif test.BsonType == \"0x01\" {\n\t\t\t\t\t\tcEJ = normalizeCanonicalDouble(t, *test.TestKey, cEJ)\n\t\t\t\t\t}\n\n\t\t\t\t\t/*** canonical BSON round-trip tests ***/\n\t\t\t\t\tdoc := bsonToNative(t, cB, \"canonical\", v.Description)\n\n\t\t\t\t\t// native_to_bson(bson_to_native(cB)) = cB\n\t\t\t\t\tnativeToBSON(t, cB, doc, v.Description, \"canonical\", \"bson_to_native(cB)\")\n\n\t\t\t\t\t// native_to_canonical_extended_json(bson_to_native(cB)) = cEJ\n\t\t\t\t\tnativeToJSON(t, cEJ, doc, v.Description, \"canonical\", \"cEJ\", \"bson_to_native(cB)\")\n\n\t\t\t\t\t// native_to_relaxed_extended_json(bson_to_native(cB)) = rEJ (if rEJ exists)\n\t\t\t\t\tif v.RelaxedExtJSON != nil {\n\t\t\t\t\t\tvar compactEJ bytes.Buffer\n\t\t\t\t\t\trequire.NoError(t, json.Compact(&compactEJ, []byte(*v.RelaxedExtJSON)))\n\t\t\t\t\t\trEJ := unescapeUnicode(compactEJ.String(), test.BsonType)\n\t\t\t\t\t\tif test.BsonType == \"0x01\" {\n\t\t\t\t\t\t\trEJ = normalizeRelaxedDouble(t, *test.TestKey, rEJ)\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tnativeToJSON(t, rEJ, doc, v.Description, \"relaxed\", \"rEJ\", \"bson_to_native(cB)\")\n\n\t\t\t\t\t\t/*** relaxed extended JSON round-trip tests (if exists) ***/\n\t\t\t\t\t\tdoc, err = jsonToNative(rEJ, \"relaxed\", v.Description)\n\t\t\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\t\t\t// native_to_relaxed_extended_json(json_to_native(rEJ)) = rEJ\n\t\t\t\t\t\tnativeToJSON(t, rEJ, doc, v.Description, \"relaxed\", \"eJR\", \"json_to_native(rEJ)\")\n\t\t\t\t\t}\n\n\t\t\t\t\t/*** canonical extended JSON round-trip tests ***/\n\t\t\t\t\tdoc, err = jsonToNative(cEJ, \"canonical\", v.Description)\n\t\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\t\t// native_to_canonical_extended_json(json_to_native(cEJ)) = cEJ\n\t\t\t\t\tnativeToJSON(t, cEJ, doc, v.Description, \"canonical\", \"cEJ\", \"json_to_native(cEJ)\")\n\n\t\t\t\t\t// native_to_bson(json_to_native(cEJ)) = cb (unless lossy)\n\t\t\t\t\tif v.Lossy == nil || !*v.Lossy {\n\t\t\t\t\t\tnativeToBSON(t, cB, doc, v.Description, \"canonical\", \"json_to_native(cEJ)\")\n\t\t\t\t\t}\n\n\t\t\t\t\t/*** degenerate BSON round-trip tests (if exists) ***/\n\t\t\t\t\tif v.DegenerateBSON != nil {\n\t\t\t\t\t\tdB, err := hex.DecodeString(*v.DegenerateBSON)\n\t\t\t\t\t\trequire.NoErrorf(t, err, \"%s: reading degenerate BSON\", v.Description)\n\n\t\t\t\t\t\tdoc = bsonToNative(t, dB, \"degenerate\", v.Description)\n\n\t\t\t\t\t\t// native_to_bson(bson_to_native(dB)) = cB\n\t\t\t\t\t\tnativeToBSON(t, cB, doc, v.Description, \"degenerate\", \"bson_to_native(dB)\")\n\t\t\t\t\t}\n\n\t\t\t\t\t/*** degenerate JSON round-trip tests (if exists) ***/\n\t\t\t\t\tif v.DegenerateExtJSON != nil {\n\t\t\t\t\t\tvar compactEJ bytes.Buffer\n\t\t\t\t\t\trequire.NoError(t, json.Compact(&compactEJ, []byte(*v.DegenerateExtJSON)))\n\t\t\t\t\t\tdEJ := unescapeUnicode(compactEJ.String(), test.BsonType)\n\t\t\t\t\t\tif test.BsonType == \"0x01\" {\n\t\t\t\t\t\t\tdEJ = normalizeCanonicalDouble(t, *test.TestKey, dEJ)\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tdoc, err = jsonToNative(dEJ, \"degenerate canonical\", v.Description)\n\t\t\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\t\t\t// native_to_canonical_extended_json(json_to_native(dEJ)) = cEJ\n\t\t\t\t\t\tnativeToJSON(t, cEJ, doc, v.Description, \"degenerate canonical\", \"cEJ\", \"json_to_native(dEJ)\")\n\n\t\t\t\t\t\t// native_to_bson(json_to_native(dEJ)) = cB (unless lossy)\n\t\t\t\t\t\tif v.Lossy == nil || !*v.Lossy {\n\t\t\t\t\t\t\tnativeToBSON(t, cB, doc, v.Description, \"canonical\", \"json_to_native(dEJ)\")\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\n\t\tt.Run(\"decode error\", func(t *testing.T) {\n\t\t\tfor _, d := range test.DecodeErrors {\n\t\t\t\tt.Run(d.Description, func(t *testing.T) {\n\t\t\t\t\tb, err := hex.DecodeString(d.Bson)\n\t\t\t\t\trequire.NoError(t, err, d.Description)\n\n\t\t\t\t\tvar doc D\n\t\t\t\t\terr = Unmarshal(b, &doc)\n\n\t\t\t\t\t// The driver unmarshals invalid UTF-8 strings without error. Loop over the unmarshalled elements\n\t\t\t\t\t// and assert that there was no error if any of the string or DBPointer values contain invalid UTF-8\n\t\t\t\t\t// characters.\n\t\t\t\t\tfor _, elem := range doc {\n\t\t\t\t\t\tvalue := reflect.ValueOf(elem.Value)\n\t\t\t\t\t\tinvalidString := (value.Kind() == reflect.String) && !utf8.ValidString(value.String())\n\t\t\t\t\t\tdbPtr, ok := elem.Value.(DBPointer)\n\t\t\t\t\t\tinvalidDBPtr := ok && !utf8.ValidString(dbPtr.DB)\n\n\t\t\t\t\t\tif invalidString || invalidDBPtr {\n\t\t\t\t\t\t\trequire.NoError(t, err, d.Description)\n\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\trequire.Errorf(t, err, \"%s: expected decode error\", d.Description)\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\n\t\tt.Run(\"parse error\", func(t *testing.T) {\n\t\t\tfor _, p := range test.ParseErrors {\n\t\t\t\tt.Run(p.Description, func(t *testing.T) {\n\t\t\t\t\ts := unescapeUnicode(p.String, test.BsonType)\n\t\t\t\t\tif test.BsonType == \"0x13\" {\n\t\t\t\t\t\ts = fmt.Sprintf(`{\"decimal128\": {\"$numberDecimal\": \"%s\"}}`, s)\n\t\t\t\t\t}\n\n\t\t\t\t\tswitch test.BsonType {\n\t\t\t\t\tcase \"0x00\", \"0x05\", \"0x13\":\n\t\t\t\t\t\tvar doc D\n\t\t\t\t\t\terr := UnmarshalExtJSON([]byte(s), true, &doc)\n\t\t\t\t\t\t// Null bytes are validated when marshaling to BSON\n\t\t\t\t\t\tif strings.Contains(p.Description, \"Null\") {\n\t\t\t\t\t\t\t_, err = Marshal(doc)\n\t\t\t\t\t\t}\n\t\t\t\t\t\trequire.Errorf(t, err, \"%s: expected parse error\", p.Description)\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tt.Errorf(\"Update test to check for parse errors for type %s\", test.BsonType)\n\t\t\t\t\t\tt.Fail()\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\t})\n}\n\nfunc TestBSONCorpus(t *testing.T) {\n\tjsonFiles, err := findJSONFilesInDir(dataDir)\n\trequire.NoErrorf(t, err, \"error finding JSON files in %s: %v\", dataDir, err)\n\n\tfor _, file := range jsonFiles {\n\t\trunTest(t, file)\n\t}\n}\n\nfunc TestRelaxedUUIDValidation(t *testing.T) {\n\ttestCases := []struct {\n\t\tdescription       string\n\t\tcanonicalExtJSON  string\n\t\tdegenerateExtJSON string\n\t\texpectedErr       string\n\t}{\n\t\t{\n\t\t\t\"valid uuid\",\n\t\t\t\"{\\\"x\\\" : { \\\"$binary\\\" : {\\\"base64\\\" : \\\"c//SZESzTGmQ6OfR38A11A==\\\", \\\"subType\\\" : \\\"04\\\"}}}\",\n\t\t\t\"{\\\"x\\\" : { \\\"$uuid\\\" : \\\"73ffd264-44b3-4c69-90e8-e7d1dfc035d4\\\"}}\",\n\t\t\t\"\",\n\t\t},\n\t\t{\n\t\t\t\"invalid uuid--no hyphens\",\n\t\t\t\"\",\n\t\t\t\"{\\\"x\\\" : { \\\"$uuid\\\" : \\\"73ffd26444b34c6990e8e7d1dfc035d4\\\"}}\",\n\t\t\t\"$uuid value does not follow RFC 4122 format regarding length and hyphens\",\n\t\t},\n\t\t{\n\t\t\t\"invalid uuid--trailing hyphens\",\n\t\t\t\"\",\n\t\t\t\"{\\\"x\\\" : { \\\"$uuid\\\" : \\\"73ffd264-44b3-4c69-90e8-e7d1dfc035--\\\"}}\",\n\t\t\t\"$uuid value does not follow RFC 4122 format regarding length and hyphens\",\n\t\t},\n\t\t{\n\t\t\t\"invalid uuid--malformed hex\",\n\t\t\t\"\",\n\t\t\t\"{\\\"x\\\" : { \\\"$uuid\\\" : \\\"q3@fd26l-44b3-4c69-90e8-e7d1dfc035d4\\\"}}\",\n\t\t\t\"$uuid value does not follow RFC 4122 format regarding hex bytes: encoding/hex: invalid byte: U+0071 'q'\",\n\t\t},\n\t}\n\n\tfor _, tc := range testCases {\n\t\tt.Run(tc.description, func(t *testing.T) {\n\t\t\t// get canonical extended JSON (if provided)\n\t\t\tcEJ := \"\"\n\t\t\tif tc.canonicalExtJSON != \"\" {\n\t\t\t\tvar compactCEJ bytes.Buffer\n\t\t\t\trequire.NoError(t, json.Compact(&compactCEJ, []byte(tc.canonicalExtJSON)))\n\t\t\t\tcEJ = unescapeUnicode(compactCEJ.String(), \"0x05\")\n\t\t\t}\n\n\t\t\t// get degenerate extended JSON\n\t\t\tvar compactDEJ bytes.Buffer\n\t\t\trequire.NoError(t, json.Compact(&compactDEJ, []byte(tc.degenerateExtJSON)))\n\t\t\tdEJ := unescapeUnicode(compactDEJ.String(), \"0x05\")\n\n\t\t\t// convert dEJ to native doc\n\t\t\tvar doc D\n\t\t\terr := UnmarshalExtJSON([]byte(dEJ), true, &doc)\n\n\t\t\tif tc.expectedErr != \"\" {\n\t\t\t\tassert.Equal(t, tc.expectedErr, err.Error(), \"expected error %v, got %v\", tc.expectedErr, err)\n\t\t\t} else {\n\t\t\t\tassert.Nil(t, err, \"expected no error, got error: %v\", err)\n\n\t\t\t\t// Marshal doc into extended JSON and compare with cEJ\n\t\t\t\tnativeToJSON(t, cEJ, doc, tc.description, \"degenerate canonical\", \"cEJ\", \"json_to_native(dEJ)\")\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "bson/bson_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bson\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"reflect\"\n\t\"strconv\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/google/go-cmp/cmp\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/require\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n)\n\nfunc noerr(t *testing.T, err error) {\n\tif err != nil {\n\t\tt.Helper()\n\t\tt.Errorf(\"Unexpected error: (%T)%v\", err, err)\n\t\tt.FailNow()\n\t}\n}\n\nfunc TestTimestamp(t *testing.T) {\n\tt.Parallel()\n\n\ttestCases := []struct {\n\t\tdescription     string\n\t\ttp              Timestamp\n\t\ttp2             Timestamp\n\t\texpectedAfter   bool\n\t\texpectedBefore  bool\n\t\texpectedEqual   bool\n\t\texpectedCompare int\n\t}{\n\t\t{\n\t\t\tdescription:     \"equal\",\n\t\t\ttp:              Timestamp{T: 12345, I: 67890},\n\t\t\ttp2:             Timestamp{T: 12345, I: 67890},\n\t\t\texpectedBefore:  false,\n\t\t\texpectedAfter:   false,\n\t\t\texpectedEqual:   true,\n\t\t\texpectedCompare: 0,\n\t\t},\n\t\t{\n\t\t\tdescription:     \"T greater than\",\n\t\t\ttp:              Timestamp{T: 12345, I: 67890},\n\t\t\ttp2:             Timestamp{T: 2345, I: 67890},\n\t\t\texpectedBefore:  false,\n\t\t\texpectedAfter:   true,\n\t\t\texpectedEqual:   false,\n\t\t\texpectedCompare: 1,\n\t\t},\n\t\t{\n\t\t\tdescription:     \"I greater than\",\n\t\t\ttp:              Timestamp{T: 12345, I: 67890},\n\t\t\ttp2:             Timestamp{T: 12345, I: 7890},\n\t\t\texpectedBefore:  false,\n\t\t\texpectedAfter:   true,\n\t\t\texpectedEqual:   false,\n\t\t\texpectedCompare: 1,\n\t\t},\n\t\t{\n\t\t\tdescription:     \"T less than\",\n\t\t\ttp:              Timestamp{T: 12345, I: 67890},\n\t\t\ttp2:             Timestamp{T: 112345, I: 67890},\n\t\t\texpectedBefore:  true,\n\t\t\texpectedAfter:   false,\n\t\t\texpectedEqual:   false,\n\t\t\texpectedCompare: -1,\n\t\t},\n\t\t{\n\t\t\tdescription:     \"I less than\",\n\t\t\ttp:              Timestamp{T: 12345, I: 67890},\n\t\t\ttp2:             Timestamp{T: 12345, I: 167890},\n\t\t\texpectedBefore:  true,\n\t\t\texpectedAfter:   false,\n\t\t\texpectedEqual:   false,\n\t\t\texpectedCompare: -1,\n\t\t},\n\t}\n\n\tfor _, tc := range testCases {\n\t\ttc := tc // Capture range variable.\n\n\t\tt.Run(tc.description, func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tassert.Equal(t, tc.expectedAfter, tc.tp.After(tc.tp2), \"expected After results to be the same\")\n\t\t\tassert.Equal(t, tc.expectedBefore, tc.tp.Before(tc.tp2), \"expected Before results to be the same\")\n\t\t\tassert.Equal(t, tc.expectedEqual, tc.tp.Equal(tc.tp2), \"expected Equal results to be the same\")\n\t\t\tassert.Equal(t, tc.expectedCompare, tc.tp.Compare(tc.tp2), \"expected Compare result to be the same\")\n\t\t})\n\t}\n}\n\nfunc TestPrimitiveIsZero(t *testing.T) {\n\ttestcases := []struct {\n\t\tname    string\n\t\tzero    Zeroer\n\t\tnonzero Zeroer\n\t}{\n\t\t{\"binary\", Binary{}, Binary{Data: []byte{0x01, 0x02, 0x03}, Subtype: 0xFF}},\n\t\t{\"decimal128\", Decimal128{}, NewDecimal128(1, 2)},\n\t\t{\"objectID\", ObjectID{}, NewObjectID()},\n\t\t{\"regex\", Regex{}, Regex{Pattern: \"foo\", Options: \"bar\"}},\n\t\t{\"dbPointer\", DBPointer{}, DBPointer{DB: \"foobar\", Pointer: ObjectID{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C}}},\n\t\t{\"timestamp\", Timestamp{}, Timestamp{T: 12345, I: 67890}},\n\t}\n\n\tfor _, tc := range testcases {\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\trequire.True(t, tc.zero.IsZero())\n\t\t\trequire.False(t, tc.nonzero.IsZero())\n\t\t})\n\t}\n}\n\nfunc TestRegexCompare(t *testing.T) {\n\ttestcases := []struct {\n\t\tname string\n\t\tr1   Regex\n\t\tr2   Regex\n\t\teq   bool\n\t}{\n\t\t{\"equal\", Regex{Pattern: \"foo1\", Options: \"bar1\"}, Regex{Pattern: \"foo1\", Options: \"bar1\"}, true},\n\t\t{\"not equal\", Regex{Pattern: \"foo1\", Options: \"bar1\"}, Regex{Pattern: \"foo2\", Options: \"bar2\"}, false},\n\t\t{\"not equal\", Regex{Pattern: \"foo1\", Options: \"bar1\"}, Regex{Pattern: \"foo1\", Options: \"bar2\"}, false},\n\t\t{\"not equal\", Regex{Pattern: \"foo1\", Options: \"bar1\"}, Regex{Pattern: \"foo2\", Options: \"bar1\"}, false},\n\t}\n\n\tfor _, tc := range testcases {\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\trequire.True(t, tc.r1.Equal(tc.r2) == tc.eq)\n\t\t})\n\t}\n}\n\nfunc TestDateTime(t *testing.T) {\n\tt.Run(\"json\", func(t *testing.T) {\n\t\tt.Run(\"round trip\", func(t *testing.T) {\n\t\t\toriginal := DateTime(1000)\n\t\t\tjsonBytes, err := json.Marshal(original)\n\t\t\tassert.Nil(t, err, \"Marshal error: %v\", err)\n\n\t\t\tvar unmarshalled DateTime\n\t\t\terr = json.Unmarshal(jsonBytes, &unmarshalled)\n\t\t\tassert.Nil(t, err, \"Unmarshal error: %v\", err)\n\n\t\t\tassert.Equal(t, original, unmarshalled, \"expected DateTime %v, got %v\", original, unmarshalled)\n\t\t})\n\t\tt.Run(\"decode null\", func(t *testing.T) {\n\t\t\tjsonBytes := []byte(\"null\")\n\t\t\tvar dt DateTime\n\t\t\terr := json.Unmarshal(jsonBytes, &dt)\n\t\t\tassert.Nil(t, err, \"Unmarshal error: %v\", err)\n\t\t\tassert.Equal(t, DateTime(0), dt, \"expected DateTime value to be 0, got %v\", dt)\n\t\t})\n\t\tt.Run(\"UTC\", func(t *testing.T) {\n\t\t\tdt := DateTime(1681145535123)\n\t\t\tjsonBytes, err := json.Marshal(dt)\n\t\t\tassert.Nil(t, err, \"Marshal error: %v\", err)\n\t\t\tassert.Equal(t, `\"2023-04-10T16:52:15.123Z\"`, string(jsonBytes))\n\t\t})\n\t})\n\tt.Run(\"NewDateTimeFromTime\", func(t *testing.T) {\n\t\tt.Run(\"range is not limited\", func(t *testing.T) {\n\t\t\t// If the implementation internally calls time.Time.UnixNano(), the constructor cannot handle times after\n\t\t\t// the year 2262.\n\n\t\t\ttimeFormat := \"2006-01-02T15:04:05.999Z07:00\"\n\t\t\ttimeString := \"3001-01-01T00:00:00Z\"\n\t\t\ttt, err := time.Parse(timeFormat, timeString)\n\t\t\tassert.Nil(t, err, \"Parse error: %v\", err)\n\n\t\t\tdt := NewDateTimeFromTime(tt)\n\t\t\tassert.True(t, dt > 0, \"expected a valid DateTime greater than 0, got %v\", dt)\n\t\t})\n\t})\n}\n\nfunc TestTimeRoundTrip(t *testing.T) {\n\tval := struct {\n\t\tValue time.Time\n\t\tID    string\n\t}{\n\t\tID: \"time-rt-test\",\n\t}\n\n\tif !val.Value.IsZero() {\n\t\tt.Errorf(\"Did not get zero time as expected.\")\n\t}\n\n\tbsonOut, err := Marshal(val)\n\tnoerr(t, err)\n\trtval := struct {\n\t\tValue time.Time\n\t\tID    string\n\t}{}\n\n\terr = Unmarshal(bsonOut, &rtval)\n\tnoerr(t, err)\n\tif !cmp.Equal(val, rtval) {\n\t\tt.Errorf(\"Did not round trip properly. got %v; want %v\", val, rtval)\n\t}\n\tif !rtval.Value.IsZero() {\n\t\tt.Errorf(\"Did not get zero time as expected.\")\n\t}\n}\n\nfunc TestNonNullTimeRoundTrip(t *testing.T) {\n\tnow := time.Now()\n\tnow = time.Unix(now.Unix(), 0)\n\tval := struct {\n\t\tValue time.Time\n\t\tID    string\n\t}{\n\t\tID:    \"time-rt-test\",\n\t\tValue: now,\n\t}\n\n\tbsonOut, err := Marshal(val)\n\tnoerr(t, err)\n\trtval := struct {\n\t\tValue time.Time\n\t\tID    string\n\t}{}\n\n\terr = Unmarshal(bsonOut, &rtval)\n\tnoerr(t, err)\n\tif !cmp.Equal(val, rtval) {\n\t\tt.Errorf(\"Did not round trip properly. got %v; want %v\", val, rtval)\n\t}\n}\n\nfunc TestD(t *testing.T) {\n\tt.Run(\"can marshal\", func(t *testing.T) {\n\t\td := D{{\"foo\", \"bar\"}, {\"hello\", \"world\"}, {\"pi\", 3.14159}}\n\t\tidx, want := bsoncore.AppendDocumentStart(nil)\n\t\twant = bsoncore.AppendStringElement(want, \"foo\", \"bar\")\n\t\twant = bsoncore.AppendStringElement(want, \"hello\", \"world\")\n\t\twant = bsoncore.AppendDoubleElement(want, \"pi\", 3.14159)\n\t\twant, err := bsoncore.AppendDocumentEnd(want, idx)\n\t\tnoerr(t, err)\n\t\tgot, err := Marshal(d)\n\t\tnoerr(t, err)\n\t\tif !bytes.Equal(got, want) {\n\t\t\tt.Errorf(\"Marshaled documents do not match. got %v; want %v\", Raw(got), Raw(want))\n\t\t}\n\t})\n\tt.Run(\"can unmarshal\", func(t *testing.T) {\n\t\twant := D{{\"foo\", \"bar\"}, {\"hello\", \"world\"}, {\"pi\", 3.14159}}\n\t\tidx, doc := bsoncore.AppendDocumentStart(nil)\n\t\tdoc = bsoncore.AppendStringElement(doc, \"foo\", \"bar\")\n\t\tdoc = bsoncore.AppendStringElement(doc, \"hello\", \"world\")\n\t\tdoc = bsoncore.AppendDoubleElement(doc, \"pi\", 3.14159)\n\t\tdoc, err := bsoncore.AppendDocumentEnd(doc, idx)\n\t\tnoerr(t, err)\n\t\tvar got D\n\t\terr = Unmarshal(doc, &got)\n\t\tnoerr(t, err)\n\t\tif !cmp.Equal(got, want) {\n\t\t\tt.Errorf(\"Unmarshaled documents do not match. got %v; want %v\", got, want)\n\t\t}\n\t})\n}\n\nfunc TestDStringer(t *testing.T) {\n\tgot := D{{\"a\", 1}, {\"b\", 2}}.String()\n\twant := `{\"a\":{\"$numberInt\":\"1\"},\"b\":{\"$numberInt\":\"2\"}}`\n\tassert.Equal(t, want, got, \"expected: %s, got: %s\", want, got)\n}\n\nfunc TestMStringer(t *testing.T) {\n\ttype msg struct {\n\t\tA json.RawMessage `json:\"a\"`\n\t\tB json.RawMessage `json:\"b\"`\n\t}\n\n\tvar res msg\n\tgot := M{\"a\": 1, \"b\": 2}.String()\n\terr := json.Unmarshal([]byte(got), &res)\n\trequire.NoError(t, err, \"Unmarshal error\")\n\n\twant := msg{\n\t\tA: json.RawMessage(`{\"$numberInt\":\"1\"}`),\n\t\tB: json.RawMessage(`{\"$numberInt\":\"2\"}`),\n\t}\n\n\tassert.Equal(t, want, res, \"returned string did not unmarshal to the expected document, returned string: %s\", got)\n}\n\nfunc TestD_MarshalJSON(t *testing.T) {\n\tt.Parallel()\n\n\ttestcases := []struct {\n\t\tname     string\n\t\ttest     D\n\t\texpected any\n\t}{\n\t\t{\n\t\t\t\"nil\",\n\t\t\tnil,\n\t\t\tnil,\n\t\t},\n\t\t{\n\t\t\t\"empty\",\n\t\t\tD{},\n\t\t\tstruct{}{},\n\t\t},\n\t\t{\n\t\t\t\"non-empty\",\n\t\t\tD{\n\t\t\t\t{\"a\", 42},\n\t\t\t\t{\"b\", true},\n\t\t\t\t{\"c\", \"answer\"},\n\t\t\t\t{\"d\", nil},\n\t\t\t\t{\"e\", 2.71828},\n\t\t\t\t{\"f\", A{42, true, \"answer\", nil, 2.71828}},\n\t\t\t\t{\"g\", D{{\"foo\", \"bar\"}}},\n\t\t\t},\n\t\t\tstruct {\n\t\t\t\tA int            `json:\"a\"`\n\t\t\t\tB bool           `json:\"b\"`\n\t\t\t\tC string         `json:\"c\"`\n\t\t\t\tD any            `json:\"d\"`\n\t\t\t\tE float32        `json:\"e\"`\n\t\t\t\tF []any          `json:\"f\"`\n\t\t\t\tG map[string]any `json:\"g\"`\n\t\t\t}{\n\t\t\t\tA: 42,\n\t\t\t\tB: true,\n\t\t\t\tC: \"answer\",\n\t\t\t\tD: nil,\n\t\t\t\tE: 2.71828,\n\t\t\t\tF: []any{42, true, \"answer\", nil, 2.71828},\n\t\t\t\tG: map[string]any{\"foo\": \"bar\"},\n\t\t\t},\n\t\t},\n\t}\n\tfor _, tc := range testcases {\n\t\ttc := tc\n\t\tt.Run(\"json.Marshal \"+tc.name, func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tgot, err := json.Marshal(tc.test)\n\t\t\tassert.NoError(t, err)\n\t\t\twant, _ := json.Marshal(tc.expected)\n\t\t\tassert.Equal(t, want, got)\n\t\t})\n\t}\n\tfor _, tc := range testcases {\n\t\ttc := tc\n\t\tt.Run(\"json.MarshalIndent \"+tc.name, func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tgot, err := json.MarshalIndent(tc.test, \"<prefix>\", \"<indent>\")\n\t\t\tassert.NoError(t, err)\n\t\t\twant, _ := json.MarshalIndent(tc.expected, \"<prefix>\", \"<indent>\")\n\t\t\tassert.Equal(t, want, got)\n\t\t})\n\t}\n}\n\nfunc TestD_UnmarshalJSON(t *testing.T) {\n\tt.Parallel()\n\n\tt.Run(\"success\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tfor _, tc := range []struct {\n\t\t\tname     string\n\t\t\ttest     []byte\n\t\t\texpected D\n\t\t}{\n\t\t\t{\n\t\t\t\t\"nil\",\n\t\t\t\t[]byte(`null`),\n\t\t\t\tnil,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"empty\",\n\t\t\t\t[]byte(`{}`),\n\t\t\t\tD{},\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"non-empty\",\n\t\t\t\t[]byte(`{\"hello\":\"world\",\"pi\":3.142,\"boolean\":true,\"nothing\":null,\"list\":[\"hello world\",3.142,false,null,{\"Lorem\":\"ipsum\"}],\"document\":{\"foo\":\"bar\"}}`),\n\t\t\t\tD{\n\t\t\t\t\t{\"hello\", \"world\"},\n\t\t\t\t\t{\"pi\", 3.142},\n\t\t\t\t\t{\"boolean\", true},\n\t\t\t\t\t{\"nothing\", nil},\n\t\t\t\t\t{\"list\", []any{\"hello world\", 3.142, false, nil, D{{\"Lorem\", \"ipsum\"}}}},\n\t\t\t\t\t{\"document\", D{{\"foo\", \"bar\"}}},\n\t\t\t\t},\n\t\t\t},\n\t\t} {\n\t\t\ttc := tc\n\t\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\t\tt.Parallel()\n\n\t\t\t\tvar got D\n\t\t\t\terr := json.Unmarshal(tc.test, &got)\n\t\t\t\tassert.NoError(t, err)\n\t\t\t\tassert.Equal(t, tc.expected, got)\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"failure\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tfor _, tc := range []struct {\n\t\t\tname string\n\t\t\ttest string\n\t\t}{\n\t\t\t{\n\t\t\t\t\"illegal\",\n\t\t\t\t`nil`,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"invalid\",\n\t\t\t\t`{\"pi\": 3.142ipsum}`,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"malformatted\",\n\t\t\t\t`{\"pi\", 3.142}`,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"truncated\",\n\t\t\t\t`{\"pi\": 3.142`,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"array type\",\n\t\t\t\t`[\"pi\", 3.142]`,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"boolean type\",\n\t\t\t\t`true`,\n\t\t\t},\n\t\t} {\n\t\t\ttc := tc\n\t\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\t\tt.Parallel()\n\n\t\t\t\tvar a map[string]any\n\t\t\t\twant := json.Unmarshal([]byte(tc.test), &a)\n\t\t\t\tvar b D\n\t\t\t\tgot := json.Unmarshal([]byte(tc.test), &b)\n\t\t\t\tw := new(json.UnmarshalTypeError)\n\t\t\t\tif errors.As(want, &w) {\n\t\t\t\t\tw.Type = reflect.TypeOf(b)\n\t\t\t\t\trequire.IsType(t, want, got)\n\t\t\t\t\tg := new(json.UnmarshalTypeError)\n\t\t\t\t\tassert.True(t, errors.As(got, &g))\n\t\t\t\t\tassert.Equal(t, w, g)\n\t\t\t\t} else {\n\t\t\t\t\tassert.Equal(t, want, got)\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t})\n}\n\ntype stringerString string\n\nfunc (ss stringerString) String() string {\n\treturn \"bar\"\n}\n\ntype keyBool bool\n\nfunc (kb keyBool) MarshalKey() (string, error) {\n\treturn fmt.Sprintf(\"%v\", kb), nil\n}\n\nfunc (kb *keyBool) UnmarshalKey(key string) error {\n\tswitch key {\n\tcase \"true\":\n\t\t*kb = true\n\tcase \"false\":\n\t\t*kb = false\n\tdefault:\n\t\treturn fmt.Errorf(\"invalid bool value %v\", key)\n\t}\n\treturn nil\n}\n\ntype keyStruct struct {\n\tval int64\n}\n\nfunc (k keyStruct) MarshalText() (text []byte, err error) {\n\tstr := strconv.FormatInt(k.val, 10)\n\n\treturn []byte(str), nil\n}\n\nfunc (k *keyStruct) UnmarshalText(text []byte) error {\n\tval, err := strconv.ParseInt(string(text), 10, 64)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*k = keyStruct{\n\t\tval: val,\n\t}\n\n\treturn nil\n}\n\nfunc TestMapCodec(t *testing.T) {\n\tt.Run(\"EncodeKeysWithStringer\", func(t *testing.T) {\n\t\tstrstr := stringerString(\"foo\")\n\t\tmapObj := map[stringerString]int{strstr: 1}\n\t\ttestCases := []struct {\n\t\t\tname     string\n\t\t\tmapCodec *mapCodec\n\t\t\tkey      string\n\t\t}{\n\t\t\t{\"default\", &mapCodec{}, \"foo\"},\n\t\t\t{\"true\", &mapCodec{encodeKeysWithStringer: true}, \"bar\"},\n\t\t\t{\"false\", &mapCodec{encodeKeysWithStringer: false}, \"foo\"},\n\t\t}\n\t\tfor _, tc := range testCases {\n\t\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\t\tmapRegistry := NewRegistry()\n\t\t\t\tmapRegistry.RegisterKindEncoder(reflect.Map, tc.mapCodec)\n\t\t\t\tbuf := new(bytes.Buffer)\n\t\t\t\tvw := NewDocumentWriter(buf)\n\t\t\t\tenc := NewEncoder(vw)\n\t\t\t\tenc.SetRegistry(mapRegistry)\n\t\t\t\terr := enc.Encode(mapObj)\n\t\t\t\tassert.Nil(t, err, \"Encode error: %v\", err)\n\t\t\t\tstr := buf.String()\n\t\t\t\tassert.True(t, strings.Contains(str, tc.key), \"expected result to contain %v, got: %v\", tc.key, str)\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"keys implements keyMarshaler and keyUnmarshaler\", func(t *testing.T) {\n\t\tmapObj := map[keyBool]int{keyBool(true): 1}\n\n\t\tdoc, err := Marshal(mapObj)\n\t\tassert.Nil(t, err, \"Marshal error: %v\", err)\n\t\tidx, want := bsoncore.AppendDocumentStart(nil)\n\t\twant = bsoncore.AppendInt32Element(want, \"true\", 1)\n\t\twant, _ = bsoncore.AppendDocumentEnd(want, idx)\n\t\tassert.Equal(t, want, doc, \"expected result %v, got %v\", string(want), string(doc))\n\n\t\tvar got map[keyBool]int\n\t\terr = Unmarshal(doc, &got)\n\t\tassert.Nil(t, err, \"Unmarshal error: %v\", err)\n\t\tassert.Equal(t, mapObj, got, \"expected result %v, got %v\", mapObj, got)\n\t})\n\n\tt.Run(\"keys implements encoding.TextMarshaler and encoding.TextUnmarshaler\", func(t *testing.T) {\n\t\tmapObj := map[keyStruct]int{\n\t\t\t{val: 10}: 100,\n\t\t}\n\n\t\tdoc, err := Marshal(mapObj)\n\t\tassert.Nil(t, err, \"Marshal error: %v\", err)\n\t\tidx, want := bsoncore.AppendDocumentStart(nil)\n\t\twant = bsoncore.AppendInt32Element(want, \"10\", 100)\n\t\twant, _ = bsoncore.AppendDocumentEnd(want, idx)\n\t\tassert.Equal(t, want, doc, \"expected result %v, got %v\", string(want), string(doc))\n\n\t\tvar got map[keyStruct]int\n\t\terr = Unmarshal(doc, &got)\n\t\tassert.Nil(t, err, \"Unmarshal error: %v\", err)\n\t\tassert.Equal(t, mapObj, got, \"expected result %v, got %v\", mapObj, got)\n\t})\n}\n\nfunc TestExtJSONEscapeKey(t *testing.T) {\n\tdoc := D{\n\t\t{\n\t\t\tKey:   \"\\\\usb#\",\n\t\t\tValue: int32(1),\n\t\t},\n\t\t{\n\t\t\tKey:   \"regex\",\n\t\t\tValue: Regex{Pattern: \"ab\\\\\\\\\\\\\\\"ab\", Options: \"\\\"\"},\n\t\t},\n\t}\n\tb, err := MarshalExtJSON(&doc, false, false)\n\tnoerr(t, err)\n\n\twant := `{\"\\\\usb#\":1,\"regex\":{\"$regularExpression\":{\"pattern\":\"ab\\\\\\\\\\\\\\\"ab\",\"options\":\"\\\"\"}}}`\n\tif diff := cmp.Diff(want, string(b)); diff != \"\" {\n\t\tt.Errorf(\"Marshaled documents do not match. got %v, want %v\", string(b), want)\n\t}\n\n\tvar got D\n\terr = UnmarshalExtJSON(b, false, &got)\n\tnoerr(t, err)\n\tif !cmp.Equal(got, doc) {\n\t\tt.Errorf(\"Unmarshaled documents do not match. got %v; want %v\", got, doc)\n\t}\n}\n\nfunc TestBsoncoreArray(t *testing.T) {\n\ttype BSONDocumentArray struct {\n\t\tArray []D `bson:\"array\"`\n\t}\n\n\ttype BSONArray struct {\n\t\tArray bsoncore.Array `bson:\"array\"`\n\t}\n\n\tbda := BSONDocumentArray{\n\t\tArray: []D{\n\t\t\t{{\"x\", 1}},\n\t\t\t{{\"x\", 2}},\n\t\t\t{{\"x\", 3}},\n\t\t},\n\t}\n\n\texpectedBSON, err := Marshal(bda)\n\tassert.Nil(t, err, \"Marshal bsoncore.Document array error: %v\", err)\n\n\tvar ba BSONArray\n\terr = Unmarshal(expectedBSON, &ba)\n\tassert.Nil(t, err, \"Unmarshal error: %v\", err)\n\n\tactualBSON, err := Marshal(ba)\n\tassert.Nil(t, err, \"Marshal bsoncore.Array error: %v\", err)\n\n\tassert.Equal(t, expectedBSON, actualBSON,\n\t\t\"expected BSON to be %v after Marshalling again; got %v\", expectedBSON, actualBSON)\n\n\tdoc := bsoncore.Document(actualBSON)\n\tv := doc.Lookup(\"array\")\n\tassert.Equal(t, bsoncore.TypeArray, v.Type, \"expected type array, got %v\", v.Type)\n}\n\nvar baseTime = time.Date(2024, 10, 11, 12, 13, 14, 12345678, time.UTC)\n\nfunc BenchmarkDateTimeMarshalJSON(b *testing.B) {\n\tt := NewDateTimeFromTime(baseTime)\n\tdata, err := t.MarshalJSON()\n\tif err != nil {\n\t\tb.Fatal(err)\n\t}\n\tb.ReportAllocs()\n\tb.SetBytes(int64(len(data)))\n\tfor i := 0; i < b.N; i++ {\n\t\tif _, err := t.MarshalJSON(); err != nil {\n\t\t\tb.Fatal(err)\n\t\t}\n\t}\n}\n\nfunc BenchmarkDateTimeUnmarshalJSON(b *testing.B) {\n\tt := NewDateTimeFromTime(baseTime)\n\tdata, err := t.MarshalJSON()\n\tif err != nil {\n\t\tb.Fatal(err)\n\t}\n\tb.ReportAllocs()\n\tb.SetBytes(int64(len(data)))\n\tfor i := 0; i < b.N; i++ {\n\t\tvar dt DateTime\n\t\tif err := dt.UnmarshalJSON(data); err != nil {\n\t\t\tb.Fatal(err)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "bson/bsoncodec.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bson\n\nimport (\n\t\"fmt\"\n\t\"reflect\"\n\t\"strings\"\n)\n\nvar emptyValue = reflect.Value{}\n\n// ValueEncoderError is an error returned from a ValueEncoder when the provided value can't be\n// encoded by the ValueEncoder.\ntype ValueEncoderError struct {\n\tName     string\n\tTypes    []reflect.Type\n\tKinds    []reflect.Kind\n\tReceived reflect.Value\n}\n\nfunc (vee ValueEncoderError) Error() string {\n\ttypeKinds := make([]string, 0, len(vee.Types)+len(vee.Kinds))\n\tfor _, t := range vee.Types {\n\t\ttypeKinds = append(typeKinds, t.String())\n\t}\n\tfor _, k := range vee.Kinds {\n\t\tif k == reflect.Map {\n\t\t\ttypeKinds = append(typeKinds, \"map[string]*\")\n\t\t\tcontinue\n\t\t}\n\t\ttypeKinds = append(typeKinds, k.String())\n\t}\n\treceived := vee.Received.Kind().String()\n\tif vee.Received.IsValid() {\n\t\treceived = vee.Received.Type().String()\n\t}\n\treturn fmt.Sprintf(\"%s can only encode valid %s, but got %s\", vee.Name, strings.Join(typeKinds, \", \"), received)\n}\n\n// ValueDecoderError is an error returned from a ValueDecoder when the provided value can't be\n// decoded by the ValueDecoder.\ntype ValueDecoderError struct {\n\tName     string\n\tTypes    []reflect.Type\n\tKinds    []reflect.Kind\n\tReceived reflect.Value\n}\n\nfunc (vde ValueDecoderError) Error() string {\n\ttypeKinds := make([]string, 0, len(vde.Types)+len(vde.Kinds))\n\tfor _, t := range vde.Types {\n\t\ttypeKinds = append(typeKinds, t.String())\n\t}\n\tfor _, k := range vde.Kinds {\n\t\tif k == reflect.Map {\n\t\t\ttypeKinds = append(typeKinds, \"map[string]*\")\n\t\t\tcontinue\n\t\t}\n\t\ttypeKinds = append(typeKinds, k.String())\n\t}\n\treceived := vde.Received.Kind().String()\n\tif vde.Received.IsValid() {\n\t\treceived = vde.Received.Type().String()\n\t}\n\tif !vde.Received.CanSet() {\n\t\treceived = \"unsettable \" + received\n\t}\n\treturn fmt.Sprintf(\"%s can only decode valid and settable %s, but got %s\", vde.Name, strings.Join(typeKinds, \", \"), received)\n}\n\n// EncodeContext is the contextual information required for a Codec to encode a\n// value.\ntype EncodeContext struct {\n\t*Registry\n\n\t// minSize causes the Encoder to marshal Go integer values (int, int8, int16, int32, int64,\n\t// uint, uint8, uint16, uint32, or uint64) as the minimum BSON int size (either 32 or 64 bits)\n\t// that can represent the integer value.\n\tminSize bool\n\n\terrorOnInlineDuplicates bool\n\tstringifyMapKeysWithFmt bool\n\tnilMapAsEmpty           bool\n\tnilSliceAsEmpty         bool\n\tnilByteSliceAsEmpty     bool\n\tomitZeroStruct          bool\n\tomitEmpty               bool\n\tuseJSONStructTags       bool\n}\n\n// DecodeContext is the contextual information required for a Codec to decode a\n// value.\ntype DecodeContext struct {\n\t*Registry\n\n\t// truncate, if true, instructs decoders to to truncate the fractional part of BSON \"double\"\n\t// values when attempting to unmarshal them into a Go integer (int, int8, int16, int32, int64,\n\t// uint, uint8, uint16, uint32, or uint64) struct field. The truncation logic does not apply to\n\t// BSON \"decimal128\" values.\n\ttruncate bool\n\n\t// defaultDocumentType specifies the Go type to decode top-level and nested BSON documents into. In particular, the\n\t// usage for this field is restricted to data typed as \"any\" or \"map[string]any\". If DocumentType is\n\t// set to a type that a BSON document cannot be unmarshaled into (e.g. \"string\"), unmarshalling will result in an\n\t// error.\n\tdefaultDocumentType reflect.Type\n\n\tbinaryAsSlice bool\n\n\t// a false value results in a decoding error.\n\tobjectIDAsHexString bool\n\n\tuseJSONStructTags bool\n\tuseLocalTimeZone  bool\n\tzeroMaps          bool\n\tzeroStructs       bool\n}\n\n// ValueEncoder is the interface implemented by types that can encode a provided Go type to BSON.\n// The value to encode is provided as a reflect.Value and a bson.ValueWriter is used within the\n// EncodeValue method to actually create the BSON representation. For convenience, ValueEncoderFunc\n// is provided to allow use of a function with the correct signature as a ValueEncoder. An\n// EncodeContext instance is provided to allow implementations to lookup further ValueEncoders and\n// to provide configuration information.\ntype ValueEncoder interface {\n\tEncodeValue(EncodeContext, ValueWriter, reflect.Value) error\n}\n\n// ValueEncoderFunc is an adapter function that allows a function with the correct signature to be\n// used as a ValueEncoder.\ntype ValueEncoderFunc func(EncodeContext, ValueWriter, reflect.Value) error\n\n// EncodeValue implements the ValueEncoder interface.\nfunc (fn ValueEncoderFunc) EncodeValue(ec EncodeContext, vw ValueWriter, val reflect.Value) error {\n\treturn fn(ec, vw, val)\n}\n\n// ValueDecoder is the interface implemented by types that can decode BSON to a provided Go type.\n// Implementations should ensure that the value they receive is settable. Similar to ValueEncoderFunc,\n// ValueDecoderFunc is provided to allow the use of a function with the correct signature as a\n// ValueDecoder. A DecodeContext instance is provided and serves similar functionality to the\n// EncodeContext.\ntype ValueDecoder interface {\n\tDecodeValue(DecodeContext, ValueReader, reflect.Value) error\n}\n\n// ValueDecoderFunc is an adapter function that allows a function with the correct signature to be\n// used as a ValueDecoder.\ntype ValueDecoderFunc func(DecodeContext, ValueReader, reflect.Value) error\n\n// DecodeValue implements the ValueDecoder interface.\nfunc (fn ValueDecoderFunc) DecodeValue(dc DecodeContext, vr ValueReader, val reflect.Value) error {\n\treturn fn(dc, vr, val)\n}\n\n// typeDecoder is the interface implemented by types that can handle the decoding of a value given its type.\ntype typeDecoder interface {\n\tdecodeType(DecodeContext, ValueReader, reflect.Type) (reflect.Value, error)\n}\n\n// typeDecoderFunc is an adapter function that allows a function with the correct signature to be used as a typeDecoder.\ntype typeDecoderFunc func(DecodeContext, ValueReader, reflect.Type) (reflect.Value, error)\n\nfunc (fn typeDecoderFunc) decodeType(dc DecodeContext, vr ValueReader, t reflect.Type) (reflect.Value, error) {\n\treturn fn(dc, vr, t)\n}\n\n// decodeAdapter allows two functions with the correct signatures to be used as both a ValueDecoder and typeDecoder.\ntype decodeAdapter struct {\n\tValueDecoderFunc\n\ttypeDecoderFunc\n}\n\nvar (\n\t_ ValueDecoder = decodeAdapter{}\n\t_ typeDecoder  = decodeAdapter{}\n)\n\nfunc decodeTypeOrValueWithInfo(vd ValueDecoder, dc DecodeContext, vr ValueReader, t reflect.Type) (reflect.Value, error) {\n\tif td, _ := vd.(typeDecoder); td != nil {\n\t\tval, err := td.decodeType(dc, vr, t)\n\t\tif err == nil && val.Type() != t {\n\t\t\t// This conversion step is necessary for slices and maps. If a user declares variables like:\n\t\t\t//\n\t\t\t// type myBool bool\n\t\t\t// var m map[string]myBool\n\t\t\t//\n\t\t\t// and tries to decode BSON bytes into the map, the decoding will fail if this conversion is not present\n\t\t\t// because we'll try to assign a value of type bool to one of type myBool.\n\t\t\tval = val.Convert(t)\n\t\t}\n\t\treturn val, err\n\t}\n\n\tval := reflect.New(t).Elem()\n\terr := vd.DecodeValue(dc, vr, val)\n\treturn val, err\n}\n"
  },
  {
    "path": "bson/bsoncodec_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bson\n\nimport (\n\t\"fmt\"\n\t\"reflect\"\n\t\"testing\"\n)\n\nfunc ExampleValueEncoder() {\n\tvar _ ValueEncoderFunc = func(_ EncodeContext, vw ValueWriter, val reflect.Value) error {\n\t\tif val.Kind() != reflect.String {\n\t\t\treturn ValueEncoderError{Name: \"StringEncodeValue\", Kinds: []reflect.Kind{reflect.String}, Received: val}\n\t\t}\n\n\t\treturn vw.WriteString(val.String())\n\t}\n}\n\nfunc ExampleValueDecoder() {\n\tvar _ ValueDecoderFunc = func(_ DecodeContext, vr ValueReader, val reflect.Value) error {\n\t\tif !val.CanSet() || val.Kind() != reflect.String {\n\t\t\treturn ValueDecoderError{Name: \"StringDecodeValue\", Kinds: []reflect.Kind{reflect.String}, Received: val}\n\t\t}\n\n\t\tif vr.Type() != TypeString {\n\t\t\treturn fmt.Errorf(\"cannot decode %v into a string type\", vr.Type())\n\t\t}\n\n\t\tstr, err := vr.ReadString()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tval.SetString(str)\n\t\treturn nil\n\t}\n}\n\ntype llCodec struct {\n\tt         *testing.T\n\tdecodeval any\n\tencodeval any\n\terr       error\n}\n\nfunc (llc *llCodec) EncodeValue(_ EncodeContext, _ ValueWriter, i any) error {\n\tif llc.err != nil {\n\t\treturn llc.err\n\t}\n\n\tllc.encodeval = i\n\treturn nil\n}\n\nfunc (llc *llCodec) DecodeValue(_ DecodeContext, _ ValueReader, val reflect.Value) error {\n\tif llc.err != nil {\n\t\treturn llc.err\n\t}\n\n\tif !reflect.TypeOf(llc.decodeval).AssignableTo(val.Type()) {\n\t\tllc.t.Errorf(\"decodeval must be assignable to val provided to DecodeValue, but is not. decodeval %T; val %T\", llc.decodeval, val)\n\t\treturn nil\n\t}\n\n\tval.Set(reflect.ValueOf(llc.decodeval))\n\treturn nil\n}\n"
  },
  {
    "path": "bson/bsonrw_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bson\n\nimport (\n\t\"testing\"\n\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n)\n\nvar (\n\t_ ValueReader = &valueReaderWriter{}\n\t_ ValueWriter = &valueReaderWriter{}\n)\n\n// invoked is a type used to indicate what method was called last.\ntype invoked byte\n\n// These are the different methods that can be invoked.\nconst (\n\tnothing invoked = iota\n\treadArray\n\treadBinary\n\treadBoolean\n\treadDocument\n\treadCodeWithScope\n\treadDBPointer\n\treadDateTime\n\treadDecimal128\n\treadDouble\n\treadInt32\n\treadInt64\n\treadJavascript\n\treadMaxKey\n\treadMinKey\n\treadNull\n\treadObjectID\n\treadRegex\n\treadString\n\treadSymbol\n\treadTimestamp\n\treadUndefined\n\treadElement\n\treadValue\n\twriteArray\n\twriteBinary\n\twriteBinaryWithSubtype\n\twriteBoolean\n\twriteCodeWithScope\n\twriteDBPointer\n\twriteDateTime\n\twriteDecimal128\n\twriteDouble\n\twriteInt32\n\twriteInt64\n\twriteJavascript\n\twriteMaxKey\n\twriteMinKey\n\twriteNull\n\twriteObjectID\n\twriteRegex\n\twriteString\n\twriteDocument\n\twriteSymbol\n\twriteTimestamp\n\twriteUndefined\n\twriteDocumentElement\n\twriteDocumentEnd\n\twriteArrayElement\n\twriteArrayEnd\n\tskip\n)\n\nfunc (i invoked) String() string {\n\tswitch i {\n\tcase nothing:\n\t\treturn \"Nothing\"\n\tcase readArray:\n\t\treturn \"ReadArray\"\n\tcase readBinary:\n\t\treturn \"ReadBinary\"\n\tcase readBoolean:\n\t\treturn \"ReadBoolean\"\n\tcase readDocument:\n\t\treturn \"ReadDocument\"\n\tcase readCodeWithScope:\n\t\treturn \"ReadCodeWithScope\"\n\tcase readDBPointer:\n\t\treturn \"ReadDBPointer\"\n\tcase readDateTime:\n\t\treturn \"ReadDateTime\"\n\tcase readDecimal128:\n\t\treturn \"ReadDecimal128\"\n\tcase readDouble:\n\t\treturn \"ReadDouble\"\n\tcase readInt32:\n\t\treturn \"ReadInt32\"\n\tcase readInt64:\n\t\treturn \"ReadInt64\"\n\tcase readJavascript:\n\t\treturn \"ReadJavascript\"\n\tcase readMaxKey:\n\t\treturn \"ReadMaxKey\"\n\tcase readMinKey:\n\t\treturn \"ReadMinKey\"\n\tcase readNull:\n\t\treturn \"ReadNull\"\n\tcase readObjectID:\n\t\treturn \"ReadObjectID\"\n\tcase readRegex:\n\t\treturn \"ReadRegex\"\n\tcase readString:\n\t\treturn \"ReadString\"\n\tcase readSymbol:\n\t\treturn \"ReadSymbol\"\n\tcase readTimestamp:\n\t\treturn \"ReadTimestamp\"\n\tcase readUndefined:\n\t\treturn \"ReadUndefined\"\n\tcase readElement:\n\t\treturn \"ReadElement\"\n\tcase readValue:\n\t\treturn \"ReadValue\"\n\tcase writeArray:\n\t\treturn \"WriteArray\"\n\tcase writeBinary:\n\t\treturn \"WriteBinary\"\n\tcase writeBinaryWithSubtype:\n\t\treturn \"WriteBinaryWithSubtype\"\n\tcase writeBoolean:\n\t\treturn \"WriteBoolean\"\n\tcase writeCodeWithScope:\n\t\treturn \"WriteCodeWithScope\"\n\tcase writeDBPointer:\n\t\treturn \"WriteDBPointer\"\n\tcase writeDateTime:\n\t\treturn \"WriteDateTime\"\n\tcase writeDecimal128:\n\t\treturn \"WriteDecimal128\"\n\tcase writeDouble:\n\t\treturn \"WriteDouble\"\n\tcase writeInt32:\n\t\treturn \"WriteInt32\"\n\tcase writeInt64:\n\t\treturn \"WriteInt64\"\n\tcase writeJavascript:\n\t\treturn \"WriteJavascript\"\n\tcase writeMaxKey:\n\t\treturn \"WriteMaxKey\"\n\tcase writeMinKey:\n\t\treturn \"WriteMinKey\"\n\tcase writeNull:\n\t\treturn \"WriteNull\"\n\tcase writeObjectID:\n\t\treturn \"WriteObjectID\"\n\tcase writeRegex:\n\t\treturn \"WriteRegex\"\n\tcase writeString:\n\t\treturn \"WriteString\"\n\tcase writeDocument:\n\t\treturn \"WriteDocument\"\n\tcase writeSymbol:\n\t\treturn \"WriteSymbol\"\n\tcase writeTimestamp:\n\t\treturn \"WriteTimestamp\"\n\tcase writeUndefined:\n\t\treturn \"WriteUndefined\"\n\tcase writeDocumentElement:\n\t\treturn \"WriteDocumentElement\"\n\tcase writeDocumentEnd:\n\t\treturn \"WriteDocumentEnd\"\n\tcase writeArrayElement:\n\t\treturn \"WriteArrayElement\"\n\tcase writeArrayEnd:\n\t\treturn \"WriteArrayEnd\"\n\tdefault:\n\t\treturn \"<unknown>\"\n\t}\n}\n\n// valueReaderWriter is a test implementation of a bsonrw.ValueReader and bsonrw.ValueWriter\ntype valueReaderWriter struct {\n\tT        *testing.T\n\tinvoked  invoked\n\tReturn   any // Can be a primitive or a bsoncore.Value\n\tBSONType Type\n\tErr      error\n\tErrAfter invoked // error after this method is called\n\tdepth    uint64\n}\n\n// prevent infinite recursion.\nfunc (llvrw *valueReaderWriter) checkdepth() {\n\tllvrw.depth++\n\tif llvrw.depth > 1000 {\n\t\tpanic(\"max depth exceeded\")\n\t}\n}\n\n// Type implements the ValueReader interface.\nfunc (llvrw *valueReaderWriter) Type() Type {\n\tllvrw.checkdepth()\n\treturn llvrw.BSONType\n}\n\n// Skip implements the ValueReader interface.\nfunc (llvrw *valueReaderWriter) Skip() error {\n\tllvrw.checkdepth()\n\tllvrw.invoked = skip\n\tif llvrw.ErrAfter == llvrw.invoked {\n\t\treturn llvrw.Err\n\t}\n\treturn nil\n}\n\n// ReadArray implements the ValueReader interface.\nfunc (llvrw *valueReaderWriter) ReadArray() (ArrayReader, error) {\n\tllvrw.checkdepth()\n\tllvrw.invoked = readArray\n\tif llvrw.ErrAfter == llvrw.invoked {\n\t\treturn nil, llvrw.Err\n\t}\n\n\treturn llvrw, nil\n}\n\n// ReadBinary implements the ValueReader interface.\nfunc (llvrw *valueReaderWriter) ReadBinary() (b []byte, btype byte, err error) {\n\tllvrw.checkdepth()\n\tllvrw.invoked = readBinary\n\tif llvrw.ErrAfter == llvrw.invoked {\n\t\treturn nil, 0x00, llvrw.Err\n\t}\n\n\tswitch tt := llvrw.Return.(type) {\n\tcase bsoncore.Value:\n\t\tsubtype, data, _, ok := bsoncore.ReadBinary(tt.Data)\n\t\tif !ok {\n\t\t\tllvrw.T.Error(\"Invalid Value provided for return value of ReadBinary.\")\n\t\t\treturn nil, 0x00, nil\n\t\t}\n\t\treturn data, subtype, nil\n\tdefault:\n\t\tllvrw.T.Errorf(\"Incorrect type provided for return value of ReadBinary: %T\", llvrw.Return)\n\t\treturn nil, 0x00, nil\n\t}\n}\n\n// ReadBoolean implements the ValueReader interface.\nfunc (llvrw *valueReaderWriter) ReadBoolean() (bool, error) {\n\tllvrw.checkdepth()\n\tllvrw.invoked = readBoolean\n\tif llvrw.ErrAfter == llvrw.invoked {\n\t\treturn false, llvrw.Err\n\t}\n\n\tswitch tt := llvrw.Return.(type) {\n\tcase bool:\n\t\treturn tt, nil\n\tcase bsoncore.Value:\n\t\tb, _, ok := bsoncore.ReadBoolean(tt.Data)\n\t\tif !ok {\n\t\t\tllvrw.T.Error(\"Invalid Value provided for return value of ReadBoolean.\")\n\t\t\treturn false, nil\n\t\t}\n\t\treturn b, nil\n\tdefault:\n\t\tllvrw.T.Errorf(\"Incorrect type provided for return value of ReadBoolean: %T\", llvrw.Return)\n\t\treturn false, nil\n\t}\n}\n\n// ReadDocument implements the ValueReader interface.\nfunc (llvrw *valueReaderWriter) ReadDocument() (DocumentReader, error) {\n\tllvrw.checkdepth()\n\tllvrw.invoked = readDocument\n\tif llvrw.ErrAfter == llvrw.invoked {\n\t\treturn nil, llvrw.Err\n\t}\n\n\treturn llvrw, nil\n}\n\n// ReadCodeWithScope implements the ValueReader interface.\nfunc (llvrw *valueReaderWriter) ReadCodeWithScope() (code string, dr DocumentReader, err error) {\n\tllvrw.checkdepth()\n\tllvrw.invoked = readCodeWithScope\n\tif llvrw.ErrAfter == llvrw.invoked {\n\t\treturn \"\", nil, llvrw.Err\n\t}\n\n\treturn \"\", llvrw, nil\n}\n\n// ReadDBPointer implements the ValueReader interface.\nfunc (llvrw *valueReaderWriter) ReadDBPointer() (ns string, oid ObjectID, err error) {\n\tllvrw.checkdepth()\n\tllvrw.invoked = readDBPointer\n\tif llvrw.ErrAfter == llvrw.invoked {\n\t\treturn \"\", ObjectID{}, llvrw.Err\n\t}\n\n\tswitch tt := llvrw.Return.(type) {\n\tcase bsoncore.Value:\n\t\tns, oid, _, ok := bsoncore.ReadDBPointer(tt.Data)\n\t\tif !ok {\n\t\t\tllvrw.T.Error(\"Invalid Value instance provided for return value of ReadDBPointer\")\n\t\t\treturn \"\", ObjectID{}, nil\n\t\t}\n\t\treturn ns, oid, nil\n\tdefault:\n\t\tllvrw.T.Errorf(\"Incorrect type provided for return value of ReadDBPointer: %T\", llvrw.Return)\n\t\treturn \"\", ObjectID{}, nil\n\t}\n}\n\n// ReadDateTime implements the ValueReader interface.\nfunc (llvrw *valueReaderWriter) ReadDateTime() (int64, error) {\n\tllvrw.checkdepth()\n\tllvrw.invoked = readDateTime\n\tif llvrw.ErrAfter == llvrw.invoked {\n\t\treturn 0, llvrw.Err\n\t}\n\n\tdt, ok := llvrw.Return.(int64)\n\tif !ok {\n\t\tllvrw.T.Errorf(\"Incorrect type provided for return value of ReadDateTime: %T\", llvrw.Return)\n\t\treturn 0, nil\n\t}\n\n\treturn dt, nil\n}\n\n// ReadDecimal128 implements the ValueReader interface.\nfunc (llvrw *valueReaderWriter) ReadDecimal128() (Decimal128, error) {\n\tllvrw.checkdepth()\n\tllvrw.invoked = readDecimal128\n\tif llvrw.ErrAfter == llvrw.invoked {\n\t\treturn Decimal128{}, llvrw.Err\n\t}\n\n\td128, ok := llvrw.Return.(Decimal128)\n\tif !ok {\n\t\tllvrw.T.Errorf(\"Incorrect type provided for return value of ReadDecimal128: %T\", llvrw.Return)\n\t\treturn Decimal128{}, nil\n\t}\n\n\treturn d128, nil\n}\n\n// ReadDouble implements the ValueReader interface.\nfunc (llvrw *valueReaderWriter) ReadDouble() (float64, error) {\n\tllvrw.checkdepth()\n\tllvrw.invoked = readDouble\n\tif llvrw.ErrAfter == llvrw.invoked {\n\t\treturn 0, llvrw.Err\n\t}\n\n\tf64, ok := llvrw.Return.(float64)\n\tif !ok {\n\t\tllvrw.T.Errorf(\"Incorrect type provided for return value of ReadDouble: %T\", llvrw.Return)\n\t\treturn 0, nil\n\t}\n\n\treturn f64, nil\n}\n\n// ReadInt32 implements the ValueReader interface.\nfunc (llvrw *valueReaderWriter) ReadInt32() (int32, error) {\n\tllvrw.checkdepth()\n\tllvrw.invoked = readInt32\n\tif llvrw.ErrAfter == llvrw.invoked {\n\t\treturn 0, llvrw.Err\n\t}\n\n\ti32, ok := llvrw.Return.(int32)\n\tif !ok {\n\t\tllvrw.T.Errorf(\"Incorrect type provided for return value of ReadInt32: %T\", llvrw.Return)\n\t\treturn 0, nil\n\t}\n\n\treturn i32, nil\n}\n\n// ReadInt64 implements the ValueReader interface.\nfunc (llvrw *valueReaderWriter) ReadInt64() (int64, error) {\n\tllvrw.checkdepth()\n\tllvrw.invoked = readInt64\n\tif llvrw.ErrAfter == llvrw.invoked {\n\t\treturn 0, llvrw.Err\n\t}\n\ti64, ok := llvrw.Return.(int64)\n\tif !ok {\n\t\tllvrw.T.Errorf(\"Incorrect type provided for return value of ReadInt64: %T\", llvrw.Return)\n\t\treturn 0, nil\n\t}\n\n\treturn i64, nil\n}\n\n// ReadJavascript implements the ValueReader interface.\nfunc (llvrw *valueReaderWriter) ReadJavascript() (code string, err error) {\n\tllvrw.checkdepth()\n\tllvrw.invoked = readJavascript\n\tif llvrw.ErrAfter == llvrw.invoked {\n\t\treturn \"\", llvrw.Err\n\t}\n\tjs, ok := llvrw.Return.(string)\n\tif !ok {\n\t\tllvrw.T.Errorf(\"Incorrect type provided for return value of ReadJavascript: %T\", llvrw.Return)\n\t\treturn \"\", nil\n\t}\n\n\treturn js, nil\n}\n\n// ReadMaxKey implements the ValueReader interface.\nfunc (llvrw *valueReaderWriter) ReadMaxKey() error {\n\tllvrw.checkdepth()\n\tllvrw.invoked = readMaxKey\n\tif llvrw.ErrAfter == llvrw.invoked {\n\t\treturn llvrw.Err\n\t}\n\n\treturn nil\n}\n\n// ReadMinKey implements the ValueReader interface.\nfunc (llvrw *valueReaderWriter) ReadMinKey() error {\n\tllvrw.checkdepth()\n\tllvrw.invoked = readMinKey\n\tif llvrw.ErrAfter == llvrw.invoked {\n\t\treturn llvrw.Err\n\t}\n\n\treturn nil\n}\n\n// ReadNull implements the ValueReader interface.\nfunc (llvrw *valueReaderWriter) ReadNull() error {\n\tllvrw.checkdepth()\n\tllvrw.invoked = readNull\n\tif llvrw.ErrAfter == llvrw.invoked {\n\t\treturn llvrw.Err\n\t}\n\n\treturn nil\n}\n\n// ReadObjectID implements the ValueReader interface.\nfunc (llvrw *valueReaderWriter) ReadObjectID() (ObjectID, error) {\n\tllvrw.checkdepth()\n\tllvrw.invoked = readObjectID\n\tif llvrw.ErrAfter == llvrw.invoked {\n\t\treturn ObjectID{}, llvrw.Err\n\t}\n\toid, ok := llvrw.Return.(ObjectID)\n\tif !ok {\n\t\tllvrw.T.Errorf(\"Incorrect type provided for return value of ReadObjectID: %T\", llvrw.Return)\n\t\treturn ObjectID{}, nil\n\t}\n\n\treturn oid, nil\n}\n\n// ReadRegex implements the ValueReader interface.\nfunc (llvrw *valueReaderWriter) ReadRegex() (pattern string, options string, err error) {\n\tllvrw.checkdepth()\n\tllvrw.invoked = readRegex\n\tif llvrw.ErrAfter == llvrw.invoked {\n\t\treturn \"\", \"\", llvrw.Err\n\t}\n\tswitch tt := llvrw.Return.(type) {\n\tcase bsoncore.Value:\n\t\tpattern, options, _, ok := bsoncore.ReadRegex(tt.Data)\n\t\tif !ok {\n\t\t\tllvrw.T.Error(\"Invalid Value instance provided for ReadRegex\")\n\t\t\treturn \"\", \"\", nil\n\t\t}\n\t\treturn pattern, options, nil\n\tdefault:\n\t\tllvrw.T.Errorf(\"Incorrect type provided for return value of ReadRegex: %T\", llvrw.Return)\n\t\treturn \"\", \"\", nil\n\t}\n}\n\n// ReadString implements the ValueReader interface.\nfunc (llvrw *valueReaderWriter) ReadString() (string, error) {\n\tllvrw.checkdepth()\n\tllvrw.invoked = readString\n\tif llvrw.ErrAfter == llvrw.invoked {\n\t\treturn \"\", llvrw.Err\n\t}\n\tstr, ok := llvrw.Return.(string)\n\tif !ok {\n\t\tllvrw.T.Errorf(\"Incorrect type provided for return value of ReadString: %T\", llvrw.Return)\n\t\treturn \"\", nil\n\t}\n\n\treturn str, nil\n}\n\n// ReadSymbol implements the ValueReader interface.\nfunc (llvrw *valueReaderWriter) ReadSymbol() (symbol string, err error) {\n\tllvrw.checkdepth()\n\tllvrw.invoked = readSymbol\n\tif llvrw.ErrAfter == llvrw.invoked {\n\t\treturn \"\", llvrw.Err\n\t}\n\tswitch tt := llvrw.Return.(type) {\n\tcase string:\n\t\treturn tt, nil\n\tcase bsoncore.Value:\n\t\tsymbol, _, ok := bsoncore.ReadSymbol(tt.Data)\n\t\tif !ok {\n\t\t\tllvrw.T.Error(\"Invalid Value instance provided for ReadSymbol\")\n\t\t\treturn \"\", nil\n\t\t}\n\t\treturn symbol, nil\n\tdefault:\n\t\tllvrw.T.Errorf(\"Incorrect type provided for return value of ReadSymbol: %T\", llvrw.Return)\n\t\treturn \"\", nil\n\t}\n}\n\n// ReadTimestamp implements the ValueReader interface.\nfunc (llvrw *valueReaderWriter) ReadTimestamp() (t uint32, i uint32, err error) {\n\tllvrw.checkdepth()\n\tllvrw.invoked = readTimestamp\n\tif llvrw.ErrAfter == llvrw.invoked {\n\t\treturn 0, 0, llvrw.Err\n\t}\n\tswitch tt := llvrw.Return.(type) {\n\tcase bsoncore.Value:\n\t\tt, i, _, ok := bsoncore.ReadTimestamp(tt.Data)\n\t\tif !ok {\n\t\t\tllvrw.T.Errorf(\"Invalid Value instance provided for return value of ReadTimestamp\")\n\t\t\treturn 0, 0, nil\n\t\t}\n\t\treturn t, i, nil\n\tdefault:\n\t\tllvrw.T.Errorf(\"Incorrect type provided for return value of ReadTimestamp: %T\", llvrw.Return)\n\t\treturn 0, 0, nil\n\t}\n}\n\n// ReadUndefined implements the ValueReader interface.\nfunc (llvrw *valueReaderWriter) ReadUndefined() error {\n\tllvrw.checkdepth()\n\tllvrw.invoked = readUndefined\n\tif llvrw.ErrAfter == llvrw.invoked {\n\t\treturn llvrw.Err\n\t}\n\n\treturn nil\n}\n\n// WriteArray implements the ValueWriter interface.\nfunc (llvrw *valueReaderWriter) WriteArray() (ArrayWriter, error) {\n\tllvrw.checkdepth()\n\tllvrw.invoked = writeArray\n\tif llvrw.ErrAfter == llvrw.invoked {\n\t\treturn nil, llvrw.Err\n\t}\n\treturn llvrw, nil\n}\n\n// WriteBinary implements the ValueWriter interface.\nfunc (llvrw *valueReaderWriter) WriteBinary([]byte) error {\n\tllvrw.checkdepth()\n\tllvrw.invoked = writeBinary\n\tif llvrw.ErrAfter == llvrw.invoked {\n\t\treturn llvrw.Err\n\t}\n\treturn nil\n}\n\n// WriteBinaryWithSubtype implements the ValueWriter interface.\nfunc (llvrw *valueReaderWriter) WriteBinaryWithSubtype([]byte, byte) error {\n\tllvrw.checkdepth()\n\tllvrw.invoked = writeBinaryWithSubtype\n\tif llvrw.ErrAfter == llvrw.invoked {\n\t\treturn llvrw.Err\n\t}\n\treturn nil\n}\n\n// WriteBoolean implements the ValueWriter interface.\nfunc (llvrw *valueReaderWriter) WriteBoolean(bool) error {\n\tllvrw.checkdepth()\n\tllvrw.invoked = writeBoolean\n\tif llvrw.ErrAfter == llvrw.invoked {\n\t\treturn llvrw.Err\n\t}\n\treturn nil\n}\n\n// WriteCodeWithScope implements the ValueWriter interface.\nfunc (llvrw *valueReaderWriter) WriteCodeWithScope(string) (DocumentWriter, error) {\n\tllvrw.checkdepth()\n\tllvrw.invoked = writeCodeWithScope\n\tif llvrw.ErrAfter == llvrw.invoked {\n\t\treturn nil, llvrw.Err\n\t}\n\treturn llvrw, nil\n}\n\n// WriteDBPointer implements the ValueWriter interface.\nfunc (llvrw *valueReaderWriter) WriteDBPointer(string, ObjectID) error {\n\tllvrw.checkdepth()\n\tllvrw.invoked = writeDBPointer\n\tif llvrw.ErrAfter == llvrw.invoked {\n\t\treturn llvrw.Err\n\t}\n\treturn nil\n}\n\n// WriteDateTime implements the ValueWriter interface.\nfunc (llvrw *valueReaderWriter) WriteDateTime(int64) error {\n\tllvrw.checkdepth()\n\tllvrw.invoked = writeDateTime\n\tif llvrw.ErrAfter == llvrw.invoked {\n\t\treturn llvrw.Err\n\t}\n\treturn nil\n}\n\n// WriteDecimal128 implements the ValueWriter interface.\nfunc (llvrw *valueReaderWriter) WriteDecimal128(Decimal128) error {\n\tllvrw.checkdepth()\n\tllvrw.invoked = writeDecimal128\n\tif llvrw.ErrAfter == llvrw.invoked {\n\t\treturn llvrw.Err\n\t}\n\treturn nil\n}\n\n// WriteDouble implements the ValueWriter interface.\nfunc (llvrw *valueReaderWriter) WriteDouble(float64) error {\n\tllvrw.checkdepth()\n\tllvrw.invoked = writeDouble\n\tif llvrw.ErrAfter == llvrw.invoked {\n\t\treturn llvrw.Err\n\t}\n\treturn nil\n}\n\n// WriteInt32 implements the ValueWriter interface.\nfunc (llvrw *valueReaderWriter) WriteInt32(int32) error {\n\tllvrw.checkdepth()\n\tllvrw.invoked = writeInt32\n\tif llvrw.ErrAfter == llvrw.invoked {\n\t\treturn llvrw.Err\n\t}\n\treturn nil\n}\n\n// WriteInt64 implements the ValueWriter interface.\nfunc (llvrw *valueReaderWriter) WriteInt64(int64) error {\n\tllvrw.checkdepth()\n\tllvrw.invoked = writeInt64\n\tif llvrw.ErrAfter == llvrw.invoked {\n\t\treturn llvrw.Err\n\t}\n\treturn nil\n}\n\n// WriteJavascript implements the ValueWriter interface.\nfunc (llvrw *valueReaderWriter) WriteJavascript(string) error {\n\tllvrw.checkdepth()\n\tllvrw.invoked = writeJavascript\n\tif llvrw.ErrAfter == llvrw.invoked {\n\t\treturn llvrw.Err\n\t}\n\treturn nil\n}\n\n// WriteMaxKey implements the ValueWriter interface.\nfunc (llvrw *valueReaderWriter) WriteMaxKey() error {\n\tllvrw.checkdepth()\n\tllvrw.invoked = writeMaxKey\n\tif llvrw.ErrAfter == llvrw.invoked {\n\t\treturn llvrw.Err\n\t}\n\treturn nil\n}\n\n// WriteMinKey implements the ValueWriter interface.\nfunc (llvrw *valueReaderWriter) WriteMinKey() error {\n\tllvrw.checkdepth()\n\tllvrw.invoked = writeMinKey\n\tif llvrw.ErrAfter == llvrw.invoked {\n\t\treturn llvrw.Err\n\t}\n\treturn nil\n}\n\n// WriteNull implements the ValueWriter interface.\nfunc (llvrw *valueReaderWriter) WriteNull() error {\n\tllvrw.checkdepth()\n\tllvrw.invoked = writeNull\n\tif llvrw.ErrAfter == llvrw.invoked {\n\t\treturn llvrw.Err\n\t}\n\treturn nil\n}\n\n// WriteObjectID implements the ValueWriter interface.\nfunc (llvrw *valueReaderWriter) WriteObjectID(ObjectID) error {\n\tllvrw.checkdepth()\n\tllvrw.invoked = writeObjectID\n\tif llvrw.ErrAfter == llvrw.invoked {\n\t\treturn llvrw.Err\n\t}\n\treturn nil\n}\n\n// WriteRegex implements the ValueWriter interface.\nfunc (llvrw *valueReaderWriter) WriteRegex(string, string) error {\n\tllvrw.checkdepth()\n\tllvrw.invoked = writeRegex\n\tif llvrw.ErrAfter == llvrw.invoked {\n\t\treturn llvrw.Err\n\t}\n\treturn nil\n}\n\n// WriteString implements the ValueWriter interface.\nfunc (llvrw *valueReaderWriter) WriteString(string) error {\n\tllvrw.checkdepth()\n\tllvrw.invoked = writeString\n\tif llvrw.ErrAfter == llvrw.invoked {\n\t\treturn llvrw.Err\n\t}\n\treturn nil\n}\n\n// WriteDocument implements the ValueWriter interface.\nfunc (llvrw *valueReaderWriter) WriteDocument() (DocumentWriter, error) {\n\tllvrw.checkdepth()\n\tllvrw.invoked = writeDocument\n\tif llvrw.ErrAfter == llvrw.invoked {\n\t\treturn nil, llvrw.Err\n\t}\n\treturn llvrw, nil\n}\n\n// WriteSymbol implements the ValueWriter interface.\nfunc (llvrw *valueReaderWriter) WriteSymbol(string) error {\n\tllvrw.checkdepth()\n\tllvrw.invoked = writeSymbol\n\tif llvrw.ErrAfter == llvrw.invoked {\n\t\treturn llvrw.Err\n\t}\n\treturn nil\n}\n\n// WriteTimestamp implements the ValueWriter interface.\nfunc (llvrw *valueReaderWriter) WriteTimestamp(uint32, uint32) error {\n\tllvrw.checkdepth()\n\tllvrw.invoked = writeTimestamp\n\tif llvrw.ErrAfter == llvrw.invoked {\n\t\treturn llvrw.Err\n\t}\n\treturn nil\n}\n\n// WriteUndefined implements the ValueWriter interface.\nfunc (llvrw *valueReaderWriter) WriteUndefined() error {\n\tllvrw.checkdepth()\n\tllvrw.invoked = writeUndefined\n\tif llvrw.ErrAfter == llvrw.invoked {\n\t\treturn llvrw.Err\n\t}\n\treturn nil\n}\n\n// ReadElement implements the DocumentReader interface.\nfunc (llvrw *valueReaderWriter) ReadElement() (string, ValueReader, error) {\n\tllvrw.checkdepth()\n\tllvrw.invoked = readElement\n\tif llvrw.ErrAfter == llvrw.invoked {\n\t\treturn \"\", nil, llvrw.Err\n\t}\n\n\treturn \"\", llvrw, nil\n}\n\n// WriteDocumentElement implements the DocumentWriter interface.\nfunc (llvrw *valueReaderWriter) WriteDocumentElement(string) (ValueWriter, error) {\n\tllvrw.checkdepth()\n\tllvrw.invoked = writeDocumentElement\n\tif llvrw.ErrAfter == llvrw.invoked {\n\t\treturn nil, llvrw.Err\n\t}\n\n\treturn llvrw, nil\n}\n\n// WriteDocumentEnd implements the DocumentWriter interface.\nfunc (llvrw *valueReaderWriter) WriteDocumentEnd() error {\n\tllvrw.checkdepth()\n\tllvrw.invoked = writeDocumentEnd\n\tif llvrw.ErrAfter == llvrw.invoked {\n\t\treturn llvrw.Err\n\t}\n\n\treturn nil\n}\n\n// ReadValue implements the ArrayReader interface.\nfunc (llvrw *valueReaderWriter) ReadValue() (ValueReader, error) {\n\tllvrw.checkdepth()\n\tllvrw.invoked = readValue\n\tif llvrw.ErrAfter == llvrw.invoked {\n\t\treturn nil, llvrw.Err\n\t}\n\n\treturn llvrw, nil\n}\n\n// WriteArrayElement implements the ArrayWriter interface.\nfunc (llvrw *valueReaderWriter) WriteArrayElement() (ValueWriter, error) {\n\tllvrw.checkdepth()\n\tllvrw.invoked = writeArrayElement\n\tif llvrw.ErrAfter == llvrw.invoked {\n\t\treturn nil, llvrw.Err\n\t}\n\n\treturn llvrw, nil\n}\n\n// WriteArrayEnd implements the ArrayWriter interface.\nfunc (llvrw *valueReaderWriter) WriteArrayEnd() error {\n\tllvrw.checkdepth()\n\tllvrw.invoked = writeArrayEnd\n\tif llvrw.ErrAfter == llvrw.invoked {\n\t\treturn llvrw.Err\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "bson/buffered_byte_src.go",
    "content": "// Copyright (C) MongoDB, Inc. 2025-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bson\n\nimport (\n\t\"bytes\"\n\t\"io\"\n)\n\n// bufferedByteSrc implements the low-level byteSrc interface by reading\n// directly from an in-memory byte slice. It provides efficient, zero-copy\n// access for parsing BSON when the entire document is buffered in memory.\ntype bufferedByteSrc struct {\n\tbuf    []byte // entire BSON document\n\toffset int64  // Current read index into buf\n}\n\nvar _ byteSrc = (*bufferedByteSrc)(nil)\n\n// Read reads up to len(p) bytes from the in-memory buffer, advancing the offset\n// by the number of bytes read.\nfunc (b *bufferedByteSrc) readExact(p []byte) (int, error) {\n\tif b.offset >= int64(len(b.buf)) {\n\t\treturn 0, io.EOF\n\t}\n\tn := copy(p, b.buf[b.offset:])\n\tb.offset += int64(n)\n\treturn n, nil\n}\n\n// ReadByte returns the single byte at buf[offset] and advances offset by 1.\nfunc (b *bufferedByteSrc) ReadByte() (byte, error) {\n\tif b.offset >= int64(len(b.buf)) {\n\t\treturn 0, io.EOF\n\t}\n\tb.offset++\n\treturn b.buf[b.offset-1], nil\n}\n\n// peek returns buf[offset:offset+n] without advancing offset.\nfunc (b *bufferedByteSrc) peek(n int) ([]byte, error) {\n\t// Ensure we don't read past the end of the buffer.\n\tif int64(n)+b.offset > int64(len(b.buf)) {\n\t\treturn b.buf[b.offset:], io.EOF\n\t}\n\n\t// Return the next n bytes without advancing the offset\n\treturn b.buf[b.offset : b.offset+int64(n)], nil\n}\n\n// discard advances offset by n bytes, returning the number of bytes discarded.\nfunc (b *bufferedByteSrc) discard(n int) (int, error) {\n\t// Ensure we don't read past the end of the buffer.\n\tif int64(n)+b.offset > int64(len(b.buf)) {\n\t\t// If we have exceeded the buffer length, discard only up to the end.\n\t\tleft := len(b.buf) - int(b.offset)\n\t\tb.offset = int64(len(b.buf))\n\n\t\treturn left, io.EOF\n\t}\n\n\t// Advance the read position\n\tb.offset += int64(n)\n\treturn n, nil\n}\n\n// readSlice reads buf[offset:] for the first occurrence of delim, returning\n// buf[offset:idx+1], and advances offset past it; errors if delim not found.\nfunc (b *bufferedByteSrc) readSlice(delim byte) ([]byte, error) {\n\t// Ensure we don't read past the end of the buffer.\n\tif b.offset >= int64(len(b.buf)) {\n\t\treturn nil, io.EOF\n\t}\n\n\t// Look for the delimiter in the remaining bytes\n\trem := b.buf[b.offset:]\n\tidx := bytes.IndexByte(rem, delim)\n\tif idx < 0 {\n\t\treturn nil, io.EOF\n\t}\n\n\t// Build the result slice up through the delimiter.\n\tresult := rem[:idx+1]\n\n\t// Advance the offset past the delimiter.\n\tb.offset += int64(idx + 1)\n\n\treturn result, nil\n}\n\n// pos returns the current read position in the buffer.\nfunc (b *bufferedByteSrc) pos() int64 {\n\treturn b.offset\n}\n\n// regexLength will return the total byte length of a BSON regex value.\nfunc (b *bufferedByteSrc) regexLength() (int32, error) {\n\trem := b.buf[b.offset:]\n\n\t// Find end of the first C-string (pattern).\n\ti := bytes.IndexByte(rem, 0x00)\n\tif i < 0 {\n\t\treturn 0, io.EOF\n\t}\n\n\t// Find end of second C-string (options).\n\tj := bytes.IndexByte(rem[i+1:], 0x00)\n\tif j < 0 {\n\t\treturn 0, io.EOF\n\t}\n\n\t// Total length = first C-string length (pattern) + second C-string length\n\t// (options) + 2 null terminators\n\treturn int32(i + j + 2), nil\n}\n\nfunc (*bufferedByteSrc) streamable() bool {\n\treturn false\n}\n\nfunc (b *bufferedByteSrc) reset() {\n\tb.buf = nil\n\tb.offset = 0\n}\n"
  },
  {
    "path": "bson/buffered_byte_src_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2025-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bson\n\nimport (\n\t\"bytes\"\n\t\"io\"\n\t\"testing\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/require\"\n)\n\nfunc TestBufferedvalueReader_discard(t *testing.T) {\n\ttests := []struct {\n\t\tname       string\n\t\tbuf        []byte\n\t\tn          int\n\t\twant       int\n\t\twantOffset int64\n\t\twantErr    error\n\t}{\n\t\t{\n\t\t\tname:       \"nothing\",\n\t\t\tbuf:        bytes.Repeat([]byte(\"a\"), 1024),\n\t\t\tn:          0,\n\t\t\twant:       0,\n\t\t\twantOffset: 0,\n\t\t\twantErr:    nil,\n\t\t},\n\t\t{\n\t\t\tname:       \"amount less than buffer size\",\n\t\t\tbuf:        bytes.Repeat([]byte(\"a\"), 1024),\n\t\t\tn:          100,\n\t\t\twant:       100,\n\t\t\twantOffset: 100,\n\t\t\twantErr:    nil,\n\t\t},\n\t\t{\n\t\t\tname:       \"amount greater than buffer size\",\n\t\t\tbuf:        bytes.Repeat([]byte(\"a\"), 1024),\n\t\t\tn:          10000,\n\t\t\twant:       1024,\n\t\t\twantOffset: 1024,\n\t\t\twantErr:    io.EOF,\n\t\t},\n\t\t{\n\t\t\tname:       \"exact buffer size\",\n\t\t\tbuf:        bytes.Repeat([]byte(\"a\"), 1024),\n\t\t\tn:          1024,\n\t\t\twant:       1024,\n\t\t\twantOffset: 1024,\n\t\t\twantErr:    nil,\n\t\t},\n\t\t{\n\t\t\tname:       \"from empty buffer\",\n\t\t\tbuf:        []byte{},\n\t\t\tn:          10,\n\t\t\twant:       0,\n\t\t\twantOffset: 0,\n\t\t\twantErr:    io.EOF,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\treader := &bufferedByteSrc{buf: tt.buf, offset: 0}\n\t\t\tn, err := reader.discard(tt.n)\n\t\t\tif tt.wantErr != nil {\n\t\t\t\tassert.ErrorIs(t, err, tt.wantErr, \"Expected error %v, got %v\", tt.wantErr, err)\n\t\t\t} else {\n\t\t\t\trequire.NoError(t, err, \"Expected no error when discarding %d bytes\", tt.n)\n\t\t\t}\n\n\t\t\tassert.Equal(t, tt.want, n, \"Expected to discard %d bytes, got %d\", tt.want, n)\n\t\t\tassert.Equal(t, tt.wantOffset, reader.offset, \"Expected offset to be %d, got %d\", tt.wantOffset, reader.offset)\n\t\t})\n\t}\n}\n\nfunc TestBufferedvalueReader_peek(t *testing.T) {\n\ttests := []struct {\n\t\tname    string\n\t\tbuf     []byte\n\t\tn       int\n\t\toffset  int64\n\t\twant    []byte\n\t\twantErr error\n\t}{\n\t\t{\n\t\t\tname:    \"nothing\",\n\t\t\tbuf:     bytes.Repeat([]byte(\"a\"), 1024),\n\t\t\tn:       0,\n\t\t\twant:    []byte{},\n\t\t\twantErr: nil,\n\t\t},\n\t\t{\n\t\t\tname:    \"amount less than buffer size\",\n\t\t\tbuf:     bytes.Repeat([]byte(\"a\"), 1024),\n\t\t\tn:       100,\n\t\t\twant:    bytes.Repeat([]byte(\"a\"), 100),\n\t\t\twantErr: nil,\n\t\t},\n\t\t{\n\t\t\tname:    \"amount greater than buffer size\",\n\t\t\tbuf:     bytes.Repeat([]byte(\"a\"), 1024),\n\t\t\tn:       10000,\n\t\t\twant:    bytes.Repeat([]byte(\"a\"), 1024),\n\t\t\twantErr: io.EOF,\n\t\t},\n\t\t{\n\t\t\tname:    \"exact buffer size\",\n\t\t\tbuf:     bytes.Repeat([]byte(\"a\"), 1024),\n\t\t\tn:       1024,\n\t\t\twant:    bytes.Repeat([]byte(\"a\"), 1024),\n\t\t\twantErr: nil,\n\t\t},\n\t\t{\n\t\t\tname:    \"from empty buffer\",\n\t\t\tbuf:     []byte{},\n\t\t\tn:       10,\n\t\t\twant:    []byte{},\n\t\t\twantErr: io.EOF,\n\t\t},\n\t\t{\n\t\t\tname:    \"peek with offset\",\n\t\t\tbuf:     append(bytes.Repeat([]byte(\"a\"), 100), bytes.Repeat([]byte(\"b\"), 100)...),\n\t\t\toffset:  100,\n\t\t\tn:       100,\n\t\t\twant:    bytes.Repeat([]byte(\"b\"), 100),\n\t\t\twantErr: nil,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\treader := &bufferedByteSrc{buf: tt.buf, offset: tt.offset}\n\t\t\tn, err := reader.peek(tt.n)\n\t\t\tif tt.wantErr != nil {\n\t\t\t\tassert.ErrorIs(t, err, tt.wantErr, \"Expected error %v, got %v\", tt.wantErr, err)\n\t\t\t} else {\n\t\t\t\trequire.NoError(t, err, \"Expected no error when peeking %d bytes\", tt.n)\n\t\t\t}\n\n\t\t\tassert.Equal(t, tt.want, n, \"Expected to peek %d bytes, got %d\", len(tt.want), len(n))\n\t\t\tassert.Equal(t, tt.offset, reader.offset, \"Expected offset to be %d, got %d\", tt.offset, reader.offset)\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "bson/byte_slice_codec.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bson\n\nimport (\n\t\"fmt\"\n\t\"reflect\"\n)\n\n// byteSliceCodec is the Codec used for []byte values.\ntype byteSliceCodec struct {\n\t// encodeNilAsEmpty causes EncodeValue to marshal nil Go byte slices as empty BSON binary values\n\t// instead of BSON null.\n\tencodeNilAsEmpty bool\n}\n\n// Assert that byteSliceCodec satisfies the typeDecoder interface, which allows it to be\n// used by collection type decoders (e.g. map, slice, etc) to set individual values in a\n// collection.\nvar _ typeDecoder = &byteSliceCodec{}\n\n// EncodeValue is the ValueEncoder for []byte.\nfunc (bsc *byteSliceCodec) EncodeValue(ec EncodeContext, vw ValueWriter, val reflect.Value) error {\n\tif !val.IsValid() || val.Type() != tByteSlice {\n\t\treturn ValueEncoderError{Name: \"ByteSliceEncodeValue\", Types: []reflect.Type{tByteSlice}, Received: val}\n\t}\n\tif val.IsNil() && !bsc.encodeNilAsEmpty && !ec.nilByteSliceAsEmpty {\n\t\treturn vw.WriteNull()\n\t}\n\treturn vw.WriteBinary(val.Interface().([]byte))\n}\n\nfunc (bsc *byteSliceCodec) decodeType(_ DecodeContext, vr ValueReader, t reflect.Type) (reflect.Value, error) {\n\tif t != tByteSlice {\n\t\treturn emptyValue, ValueDecoderError{\n\t\t\tName:     \"ByteSliceDecodeValue\",\n\t\t\tTypes:    []reflect.Type{tByteSlice},\n\t\t\tReceived: reflect.Zero(t),\n\t\t}\n\t}\n\n\tvar data []byte\n\tvar err error\n\tswitch vrType := vr.Type(); vrType {\n\tcase TypeString:\n\t\tstr, err := vr.ReadString()\n\t\tif err != nil {\n\t\t\treturn emptyValue, err\n\t\t}\n\t\tdata = []byte(str)\n\tcase TypeSymbol:\n\t\tsym, err := vr.ReadSymbol()\n\t\tif err != nil {\n\t\t\treturn emptyValue, err\n\t\t}\n\t\tdata = []byte(sym)\n\tcase TypeBinary:\n\t\tvar subtype byte\n\t\tdata, subtype, err = vr.ReadBinary()\n\t\tif err != nil {\n\t\t\treturn emptyValue, err\n\t\t}\n\t\tif subtype != TypeBinaryGeneric && subtype != TypeBinaryBinaryOld {\n\t\t\treturn emptyValue, decodeBinaryError{subtype: subtype, typeName: \"[]byte\"}\n\t\t}\n\tcase TypeNull:\n\t\terr = vr.ReadNull()\n\tcase TypeUndefined:\n\t\terr = vr.ReadUndefined()\n\tdefault:\n\t\treturn emptyValue, fmt.Errorf(\"cannot decode %v into a []byte\", vrType)\n\t}\n\tif err != nil {\n\t\treturn emptyValue, err\n\t}\n\n\treturn reflect.ValueOf(data), nil\n}\n\n// DecodeValue is the ValueDecoder for []byte.\nfunc (bsc *byteSliceCodec) DecodeValue(dc DecodeContext, vr ValueReader, val reflect.Value) error {\n\tif !val.CanSet() || val.Type() != tByteSlice {\n\t\treturn ValueDecoderError{Name: \"ByteSliceDecodeValue\", Types: []reflect.Type{tByteSlice}, Received: val}\n\t}\n\n\telem, err := bsc.decodeType(dc, vr, tByteSlice)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tval.Set(elem)\n\treturn nil\n}\n"
  },
  {
    "path": "bson/codec_cache.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bson\n\nimport (\n\t\"reflect\"\n\t\"sync\"\n\t\"sync/atomic\"\n)\n\n// Runtime check that the kind encoder and decoder caches can store any valid\n// reflect.Kind constant.\nfunc init() {\n\tif s := reflect.Kind(len(kindEncoderCache{}.entries)).String(); s != \"kind27\" {\n\t\tpanic(\"The capacity of kindEncoderCache is too small.\\n\" +\n\t\t\t\"This is due to a new type being added to reflect.Kind.\")\n\t}\n}\n\n// statically assert array size\nvar (\n\t_ = (kindEncoderCache{}).entries[reflect.UnsafePointer]\n\t_ = (kindDecoderCache{}).entries[reflect.UnsafePointer]\n)\n\ntype typeEncoderCache struct {\n\tcache sync.Map // map[reflect.Type]ValueEncoder\n}\n\nfunc (c *typeEncoderCache) Store(rt reflect.Type, enc ValueEncoder) {\n\tc.cache.Store(rt, enc)\n}\n\nfunc (c *typeEncoderCache) Load(rt reflect.Type) (ValueEncoder, bool) {\n\tif v, _ := c.cache.Load(rt); v != nil {\n\t\treturn v.(ValueEncoder), true\n\t}\n\treturn nil, false\n}\n\nfunc (c *typeEncoderCache) LoadOrStore(rt reflect.Type, enc ValueEncoder) ValueEncoder {\n\tif v, loaded := c.cache.LoadOrStore(rt, enc); loaded {\n\t\tenc = v.(ValueEncoder)\n\t}\n\treturn enc\n}\n\nfunc (c *typeEncoderCache) Clone() *typeEncoderCache {\n\tcc := new(typeEncoderCache)\n\tc.cache.Range(func(k, v any) bool {\n\t\tif k != nil && v != nil {\n\t\t\tcc.cache.Store(k, v)\n\t\t}\n\t\treturn true\n\t})\n\treturn cc\n}\n\ntype typeDecoderCache struct {\n\tcache sync.Map // map[reflect.Type]ValueDecoder\n}\n\nfunc (c *typeDecoderCache) Store(rt reflect.Type, dec ValueDecoder) {\n\tc.cache.Store(rt, dec)\n}\n\nfunc (c *typeDecoderCache) Load(rt reflect.Type) (ValueDecoder, bool) {\n\tif v, _ := c.cache.Load(rt); v != nil {\n\t\treturn v.(ValueDecoder), true\n\t}\n\treturn nil, false\n}\n\nfunc (c *typeDecoderCache) LoadOrStore(rt reflect.Type, dec ValueDecoder) ValueDecoder {\n\tif v, loaded := c.cache.LoadOrStore(rt, dec); loaded {\n\t\tdec = v.(ValueDecoder)\n\t}\n\treturn dec\n}\n\nfunc (c *typeDecoderCache) Clone() *typeDecoderCache {\n\tcc := new(typeDecoderCache)\n\tc.cache.Range(func(k, v any) bool {\n\t\tif k != nil && v != nil {\n\t\t\tcc.cache.Store(k, v)\n\t\t}\n\t\treturn true\n\t})\n\treturn cc\n}\n\n// atomic.Value requires that all calls to Store() have the same concrete type\n// so we wrap the ValueEncoder with a kindEncoderCacheEntry to ensure the type\n// is always the same (since different concrete types may implement the\n// ValueEncoder interface).\ntype kindEncoderCacheEntry struct {\n\tenc ValueEncoder\n}\n\ntype kindEncoderCache struct {\n\tentries [reflect.UnsafePointer + 1]atomic.Value // *kindEncoderCacheEntry\n}\n\nfunc (c *kindEncoderCache) Store(rt reflect.Kind, enc ValueEncoder) {\n\tif enc != nil && rt < reflect.Kind(len(c.entries)) {\n\t\tc.entries[rt].Store(&kindEncoderCacheEntry{enc: enc})\n\t}\n}\n\nfunc (c *kindEncoderCache) Load(rt reflect.Kind) (ValueEncoder, bool) {\n\tif rt < reflect.Kind(len(c.entries)) {\n\t\tif ent, ok := c.entries[rt].Load().(*kindEncoderCacheEntry); ok {\n\t\t\treturn ent.enc, ent.enc != nil\n\t\t}\n\t}\n\treturn nil, false\n}\n\nfunc (c *kindEncoderCache) Clone() *kindEncoderCache {\n\tcc := new(kindEncoderCache)\n\tfor i, v := range c.entries {\n\t\tif val := v.Load(); val != nil {\n\t\t\tcc.entries[i].Store(val)\n\t\t}\n\t}\n\treturn cc\n}\n\n// atomic.Value requires that all calls to Store() have the same concrete type\n// so we wrap the ValueDecoder with a kindDecoderCacheEntry to ensure the type\n// is always the same (since different concrete types may implement the\n// ValueDecoder interface).\ntype kindDecoderCacheEntry struct {\n\tdec ValueDecoder\n}\n\ntype kindDecoderCache struct {\n\tentries [reflect.UnsafePointer + 1]atomic.Value // *kindDecoderCacheEntry\n}\n\nfunc (c *kindDecoderCache) Store(rt reflect.Kind, dec ValueDecoder) {\n\tif rt < reflect.Kind(len(c.entries)) {\n\t\tc.entries[rt].Store(&kindDecoderCacheEntry{dec: dec})\n\t}\n}\n\nfunc (c *kindDecoderCache) Load(rt reflect.Kind) (ValueDecoder, bool) {\n\tif rt < reflect.Kind(len(c.entries)) {\n\t\tif ent, ok := c.entries[rt].Load().(*kindDecoderCacheEntry); ok {\n\t\t\treturn ent.dec, ent.dec != nil\n\t\t}\n\t}\n\treturn nil, false\n}\n\nfunc (c *kindDecoderCache) Clone() *kindDecoderCache {\n\tcc := new(kindDecoderCache)\n\tfor i, v := range c.entries {\n\t\tif val := v.Load(); val != nil {\n\t\t\tcc.entries[i].Store(val)\n\t\t}\n\t}\n\treturn cc\n}\n"
  },
  {
    "path": "bson/codec_cache_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bson\n\nimport (\n\t\"reflect\"\n\t\"strconv\"\n\t\"strings\"\n\t\"testing\"\n)\n\n// NB(charlie): the array size is a power of 2 because we use the remainder of\n// it (mod) in benchmarks and that is faster when the size is a power of 2.\nvar codecCacheTestTypes = [16]reflect.Type{\n\treflect.TypeOf(uint8(0)),\n\treflect.TypeOf(uint16(0)),\n\treflect.TypeOf(uint32(0)),\n\treflect.TypeOf(uint64(0)),\n\treflect.TypeOf(uint(0)),\n\treflect.TypeOf(uintptr(0)),\n\treflect.TypeOf(int8(0)),\n\treflect.TypeOf(int16(0)),\n\treflect.TypeOf(int32(0)),\n\treflect.TypeOf(int64(0)),\n\treflect.TypeOf(int(0)),\n\treflect.TypeOf(float32(0)),\n\treflect.TypeOf(float64(0)),\n\treflect.TypeOf(true),\n\treflect.TypeOf(struct{ A int }{}),\n\treflect.TypeOf(map[int]int{}),\n}\n\nfunc TestTypeCache(t *testing.T) {\n\trt := reflect.TypeOf(int(0))\n\tec := new(typeEncoderCache)\n\tdc := new(typeDecoderCache)\n\n\tcodec := new(fakeCodec)\n\tec.Store(rt, codec)\n\tdc.Store(rt, codec)\n\tif v, ok := ec.Load(rt); !ok || !reflect.DeepEqual(v, codec) {\n\t\tt.Errorf(\"Load(%s) = %v, %t; want: %v, %t\", rt, v, ok, codec, true)\n\t}\n\tif v, ok := dc.Load(rt); !ok || !reflect.DeepEqual(v, codec) {\n\t\tt.Errorf(\"Load(%s) = %v, %t; want: %v, %t\", rt, v, ok, codec, true)\n\t}\n\n\t// Make sure we overwrite the stored value with nil\n\tec.Store(rt, nil)\n\tdc.Store(rt, nil)\n\tif v, ok := ec.Load(rt); ok || v != nil {\n\t\tt.Errorf(\"Load(%s) = %v, %t; want: %v, %t\", rt, v, ok, nil, false)\n\t}\n\tif v, ok := dc.Load(rt); ok || v != nil {\n\t\tt.Errorf(\"Load(%s) = %v, %t; want: %v, %t\", rt, v, ok, nil, false)\n\t}\n}\n\nfunc TestTypeCacheClone(t *testing.T) {\n\tcodec := new(fakeCodec)\n\tec1 := new(typeEncoderCache)\n\tdc1 := new(typeDecoderCache)\n\tfor _, rt := range codecCacheTestTypes {\n\t\tec1.Store(rt, codec)\n\t\tdc1.Store(rt, codec)\n\t}\n\tec2 := ec1.Clone()\n\tdc2 := dc1.Clone()\n\tfor _, rt := range codecCacheTestTypes {\n\t\tif v, _ := ec2.Load(rt); !reflect.DeepEqual(v, codec) {\n\t\t\tt.Errorf(\"Load(%s) = %#v; want: %#v\", rt, v, codec)\n\t\t}\n\t\tif v, _ := dc2.Load(rt); !reflect.DeepEqual(v, codec) {\n\t\t\tt.Errorf(\"Load(%s) = %#v; want: %#v\", rt, v, codec)\n\t\t}\n\t}\n}\n\nfunc TestKindCacheArray(t *testing.T) {\n\t// Check array bounds\n\tvar c kindEncoderCache\n\tcodec := new(fakeCodec)\n\tc.Store(reflect.UnsafePointer, codec)   // valid\n\tc.Store(reflect.UnsafePointer+1, codec) // ignored\n\tif v, ok := c.Load(reflect.UnsafePointer); !ok || v != codec {\n\t\tt.Errorf(\"Load(reflect.UnsafePointer) = %v, %t; want: %v, %t\", v, ok, codec, true)\n\t}\n\tif v, ok := c.Load(reflect.UnsafePointer + 1); ok || v != nil {\n\t\tt.Errorf(\"Load(reflect.UnsafePointer + 1) = %v, %t; want: %v, %t\", v, ok, nil, false)\n\t}\n\n\t// Make sure that reflect.UnsafePointer is the last/largest reflect.Type.\n\t//\n\t// The String() method of invalid reflect.Type types are of the format\n\t// \"kind{NUMBER}\".\n\tfor rt := reflect.UnsafePointer + 1; rt < reflect.UnsafePointer+16; rt++ {\n\t\ts := rt.String()\n\t\tif !strings.Contains(s, strconv.Itoa(int(rt))) {\n\t\t\tt.Errorf(\"reflect.Type(%d) appears to be valid: %q\", rt, s)\n\t\t}\n\t}\n}\n\nfunc TestKindCacheClone(t *testing.T) {\n\te1 := new(kindEncoderCache)\n\td1 := new(kindDecoderCache)\n\tcodec := new(fakeCodec)\n\tfor k := reflect.Invalid; k <= reflect.UnsafePointer; k++ {\n\t\te1.Store(k, codec)\n\t\td1.Store(k, codec)\n\t}\n\te2 := e1.Clone()\n\tfor k := reflect.Invalid; k <= reflect.UnsafePointer; k++ {\n\t\tv1, ok1 := e1.Load(k)\n\t\tv2, ok2 := e2.Load(k)\n\t\tif ok1 != ok2 || !reflect.DeepEqual(v1, v2) || v1 == nil || v2 == nil {\n\t\t\tt.Errorf(\"Encoder(%s): %#v, %t != %#v, %t\", k, v1, ok1, v2, ok2)\n\t\t}\n\t}\n\td2 := d1.Clone()\n\tfor k := reflect.Invalid; k <= reflect.UnsafePointer; k++ {\n\t\tv1, ok1 := d1.Load(k)\n\t\tv2, ok2 := d2.Load(k)\n\t\tif ok1 != ok2 || !reflect.DeepEqual(v1, v2) || v1 == nil || v2 == nil {\n\t\t\tt.Errorf(\"Decoder(%s): %#v, %t != %#v, %t\", k, v1, ok1, v2, ok2)\n\t\t}\n\t}\n}\n\nfunc TestKindCacheEncoderNilEncoder(t *testing.T) {\n\tt.Run(\"Encoder\", func(t *testing.T) {\n\t\tc := new(kindEncoderCache)\n\t\tc.Store(reflect.Invalid, ValueEncoder(nil))\n\t\tv, ok := c.Load(reflect.Invalid)\n\t\tif v != nil || ok {\n\t\t\tt.Errorf(\"Load of nil ValueEncoder should return: nil, false; got: %v, %t\", v, ok)\n\t\t}\n\t})\n\tt.Run(\"Decoder\", func(t *testing.T) {\n\t\tc := new(kindDecoderCache)\n\t\tc.Store(reflect.Invalid, ValueDecoder(nil))\n\t\tv, ok := c.Load(reflect.Invalid)\n\t\tif v != nil || ok {\n\t\t\tt.Errorf(\"Load of nil ValueDecoder should return: nil, false; got: %v, %t\", v, ok)\n\t\t}\n\t})\n}\n\nfunc BenchmarkEncoderCacheLoad(b *testing.B) {\n\tc := new(typeEncoderCache)\n\tcodec := new(fakeCodec)\n\ttyps := codecCacheTestTypes\n\tfor _, t := range typs {\n\t\tc.Store(t, codec)\n\t}\n\tb.RunParallel(func(pb *testing.PB) {\n\t\tfor i := 0; pb.Next(); i++ {\n\t\t\tc.Load(typs[i%len(typs)])\n\t\t}\n\t})\n}\n\nfunc BenchmarkEncoderCacheStore(b *testing.B) {\n\tc := new(typeEncoderCache)\n\tcodec := new(fakeCodec)\n\tb.RunParallel(func(pb *testing.PB) {\n\t\ttyps := codecCacheTestTypes\n\t\tfor i := 0; pb.Next(); i++ {\n\t\t\tc.Store(typs[i%len(typs)], codec)\n\t\t}\n\t})\n}\n"
  },
  {
    "path": "bson/cond_addr_codec.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bson\n\nimport (\n\t\"reflect\"\n)\n\n// condAddrEncoder is the encoder used when a pointer to the encoding value has an encoder.\ntype condAddrEncoder struct {\n\tcanAddrEnc ValueEncoder\n\telseEnc    ValueEncoder\n}\n\nvar _ ValueEncoder = &condAddrEncoder{}\n\n// newCondAddrEncoder returns an condAddrEncoder.\nfunc newCondAddrEncoder(canAddrEnc, elseEnc ValueEncoder) *condAddrEncoder {\n\tencoder := condAddrEncoder{canAddrEnc: canAddrEnc, elseEnc: elseEnc}\n\treturn &encoder\n}\n\n// EncodeValue is the ValueEncoderFunc for a value that may be addressable.\nfunc (cae *condAddrEncoder) EncodeValue(ec EncodeContext, vw ValueWriter, val reflect.Value) error {\n\tif val.CanAddr() {\n\t\treturn cae.canAddrEnc.EncodeValue(ec, vw, val)\n\t}\n\tif cae.elseEnc != nil {\n\t\treturn cae.elseEnc.EncodeValue(ec, vw, val)\n\t}\n\treturn errNoEncoder{Type: val.Type()}\n}\n\n// condAddrDecoder is the decoder used when a pointer to the value has a decoder.\ntype condAddrDecoder struct {\n\tcanAddrDec ValueDecoder\n\telseDec    ValueDecoder\n}\n\nvar _ ValueDecoder = &condAddrDecoder{}\n\n// newCondAddrDecoder returns an CondAddrDecoder.\nfunc newCondAddrDecoder(canAddrDec, elseDec ValueDecoder) *condAddrDecoder {\n\tdecoder := condAddrDecoder{canAddrDec: canAddrDec, elseDec: elseDec}\n\treturn &decoder\n}\n\n// DecodeValue is the ValueDecoderFunc for a value that may be addressable.\nfunc (cad *condAddrDecoder) DecodeValue(dc DecodeContext, vr ValueReader, val reflect.Value) error {\n\tif val.CanAddr() {\n\t\treturn cad.canAddrDec.DecodeValue(dc, vr, val)\n\t}\n\tif cad.elseDec != nil {\n\t\treturn cad.elseDec.DecodeValue(dc, vr, val)\n\t}\n\treturn errNoDecoder{Type: val.Type()}\n}\n"
  },
  {
    "path": "bson/cond_addr_codec_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bson\n\nimport (\n\t\"reflect\"\n\t\"testing\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n)\n\nfunc TestCondAddrCodec(t *testing.T) {\n\tvar inner int\n\tcanAddrVal := reflect.ValueOf(&inner)\n\taddressable := canAddrVal.Elem()\n\tunaddressable := reflect.ValueOf(inner)\n\trw := &valueReaderWriter{}\n\n\tt.Run(\"addressEncode\", func(t *testing.T) {\n\t\tinvoked := 0\n\t\tencode1 := ValueEncoderFunc(func(EncodeContext, ValueWriter, reflect.Value) error {\n\t\t\tinvoked = 1\n\t\t\treturn nil\n\t\t})\n\t\tencode2 := ValueEncoderFunc(func(EncodeContext, ValueWriter, reflect.Value) error {\n\t\t\tinvoked = 2\n\t\t\treturn nil\n\t\t})\n\t\tcondEncoder := newCondAddrEncoder(encode1, encode2)\n\n\t\ttestCases := []struct {\n\t\t\tname    string\n\t\t\tval     reflect.Value\n\t\t\tinvoked int\n\t\t}{\n\t\t\t{\"canAddr\", addressable, 1},\n\t\t\t{\"else\", unaddressable, 2},\n\t\t}\n\t\tfor _, tc := range testCases {\n\t\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\t\terr := condEncoder.EncodeValue(EncodeContext{}, rw, tc.val)\n\t\t\t\tassert.Nil(t, err, \"CondAddrEncoder error: %v\", err)\n\n\t\t\t\tassert.Equal(t, invoked, tc.invoked, \"Expected function %v to be called, called %v\", tc.invoked, invoked)\n\t\t\t})\n\t\t}\n\n\t\tt.Run(\"error\", func(t *testing.T) {\n\t\t\terrEncoder := newCondAddrEncoder(encode1, nil)\n\t\t\terr := errEncoder.EncodeValue(EncodeContext{}, rw, unaddressable)\n\t\t\twant := errNoEncoder{Type: unaddressable.Type()}\n\t\t\tassert.Equal(t, err, want, \"expected error %v, got %v\", want, err)\n\t\t})\n\t})\n\tt.Run(\"addressDecode\", func(t *testing.T) {\n\t\tinvoked := 0\n\t\tdecode1 := ValueDecoderFunc(func(DecodeContext, ValueReader, reflect.Value) error {\n\t\t\tinvoked = 1\n\t\t\treturn nil\n\t\t})\n\t\tdecode2 := ValueDecoderFunc(func(DecodeContext, ValueReader, reflect.Value) error {\n\t\t\tinvoked = 2\n\t\t\treturn nil\n\t\t})\n\t\tcondDecoder := newCondAddrDecoder(decode1, decode2)\n\n\t\ttestCases := []struct {\n\t\t\tname    string\n\t\t\tval     reflect.Value\n\t\t\tinvoked int\n\t\t}{\n\t\t\t{\"canAddr\", addressable, 1},\n\t\t\t{\"else\", unaddressable, 2},\n\t\t}\n\t\tfor _, tc := range testCases {\n\t\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\t\terr := condDecoder.DecodeValue(DecodeContext{}, rw, tc.val)\n\t\t\t\tassert.Nil(t, err, \"CondAddrDecoder error: %v\", err)\n\n\t\t\t\tassert.Equal(t, invoked, tc.invoked, \"Expected function %v to be called, called %v\", tc.invoked, invoked)\n\t\t\t})\n\t\t}\n\n\t\tt.Run(\"error\", func(t *testing.T) {\n\t\t\terrDecoder := newCondAddrDecoder(decode1, nil)\n\t\t\terr := errDecoder.DecodeValue(DecodeContext{}, rw, unaddressable)\n\t\t\twant := errNoDecoder{Type: unaddressable.Type()}\n\t\t\tassert.Equal(t, err, want, \"expected error %v, got %v\", want, err)\n\t\t})\n\t})\n}\n"
  },
  {
    "path": "bson/copier.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bson\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n)\n\n// copyDocument handles copying one document from the src to the dst.\nfunc copyDocument(dst ValueWriter, src ValueReader) error {\n\tdr, err := src.ReadDocument()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tdw, err := dst.WriteDocument()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn copyDocumentCore(dw, dr)\n}\n\n// copyArrayFromBytes copies the values from a BSON array represented as a\n// []byte to a ValueWriter.\nfunc copyArrayFromBytes(dst ValueWriter, src []byte) error {\n\taw, err := dst.WriteArray()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\terr = copyBytesToArrayWriter(aw, src)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn aw.WriteArrayEnd()\n}\n\n// copyDocumentFromBytes copies the values from a BSON document represented as a\n// []byte to a ValueWriter.\nfunc copyDocumentFromBytes(dst ValueWriter, src []byte) error {\n\tdw, err := dst.WriteDocument()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\terr = copyBytesToDocumentWriter(dw, src)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn dw.WriteDocumentEnd()\n}\n\ntype writeElementFn func(key string) (ValueWriter, error)\n\n// copyBytesToArrayWriter copies the values from a BSON Array represented as a []byte to an\n// ArrayWriter.\nfunc copyBytesToArrayWriter(dst ArrayWriter, src []byte) error {\n\twef := func(_ string) (ValueWriter, error) {\n\t\treturn dst.WriteArrayElement()\n\t}\n\n\treturn copyBytesToValueWriter(src, wef)\n}\n\n// copyBytesToDocumentWriter copies the values from a BSON document represented as a []byte to a\n// DocumentWriter.\nfunc copyBytesToDocumentWriter(dst DocumentWriter, src []byte) error {\n\twef := func(key string) (ValueWriter, error) {\n\t\treturn dst.WriteDocumentElement(key)\n\t}\n\n\treturn copyBytesToValueWriter(src, wef)\n}\n\nfunc copyBytesToValueWriter(src []byte, wef writeElementFn) error {\n\t// TODO(skriptble): Create errors types here. Anything that is a tag should be a property.\n\tlength, rem, ok := bsoncore.ReadLength(src)\n\tif !ok {\n\t\treturn fmt.Errorf(\"couldn't read length from src, not enough bytes. length=%d\", len(src))\n\t}\n\tif len(src) < int(length) {\n\t\treturn fmt.Errorf(\"length read exceeds number of bytes available. length=%d bytes=%d\", len(src), length)\n\t}\n\trem = rem[:length-4]\n\n\tvar t bsoncore.Type\n\tvar key string\n\tvar val bsoncore.Value\n\tfor {\n\t\tt, rem, ok = bsoncore.ReadType(rem)\n\t\tif !ok {\n\t\t\treturn io.EOF\n\t\t}\n\t\tif t == bsoncore.Type(0) {\n\t\t\tif len(rem) != 0 {\n\t\t\t\treturn fmt.Errorf(\"document end byte found before end of document. remaining bytes=%v\", rem)\n\t\t\t}\n\t\t\tbreak\n\t\t}\n\n\t\tkey, rem, ok = bsoncore.ReadKey(rem)\n\t\tif !ok {\n\t\t\treturn fmt.Errorf(\"invalid key found. remaining bytes=%v\", rem)\n\t\t}\n\n\t\t// write as either array element or document element using writeElementFn\n\t\tvw, err := wef(key)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tval, rem, ok = bsoncore.ReadValue(rem, t)\n\t\tif !ok {\n\t\t\treturn fmt.Errorf(\"not enough bytes available to read type. bytes=%d type=%s\", len(rem), t)\n\t\t}\n\t\terr = copyValueFromBytes(vw, Type(t), val.Data)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\n// copyDocumentToBytes copies an entire document from the ValueReader and\n// returns it as bytes.\nfunc copyDocumentToBytes(src ValueReader) ([]byte, error) {\n\treturn appendDocumentBytes(nil, src)\n}\n\n// appendDocumentBytes functions the same as CopyDocumentToBytes, but will\n// append the result to dst.\nfunc appendDocumentBytes(dst []byte, src ValueReader) ([]byte, error) {\n\tif br, ok := src.(bytesReader); ok {\n\t\t_, dst, err := br.readValueBytes(dst)\n\t\treturn dst, err\n\t}\n\n\tvw := vwPool.Get().(*valueWriter)\n\tdefer putValueWriter(vw)\n\n\tvw.reset(dst)\n\n\terr := copyDocument(vw, src)\n\tdst = vw.buf\n\treturn dst, err\n}\n\n// appendArrayBytes copies an array from the ValueReader to dst.\nfunc appendArrayBytes(dst []byte, src ValueReader) ([]byte, error) {\n\tif br, ok := src.(bytesReader); ok {\n\t\t_, dst, err := br.readValueBytes(dst)\n\t\treturn dst, err\n\t}\n\n\tvw := vwPool.Get().(*valueWriter)\n\tdefer putValueWriter(vw)\n\n\tvw.reset(dst)\n\n\terr := copyArray(vw, src)\n\tdst = vw.buf\n\treturn dst, err\n}\n\n// copyValueFromBytes will write the value represtend by t and src to dst.\nfunc copyValueFromBytes(dst ValueWriter, t Type, src []byte) error {\n\tif wvb, ok := dst.(bytesWriter); ok {\n\t\treturn wvb.writeValueBytes(t, src)\n\t}\n\n\tvr := newBufferedDocumentReader(src)\n\tvr.advanceFrame()\n\n\tvr.stack[vr.frame].mode = mElement\n\tvr.stack[vr.frame].vType = t\n\n\treturn copyValue(dst, vr)\n}\n\n// copyValueToBytes copies a value from src and returns it as a Type and a\n// []byte.\nfunc copyValueToBytes(src ValueReader) (Type, []byte, error) {\n\tif br, ok := src.(bytesReader); ok {\n\t\treturn br.readValueBytes(nil)\n\t}\n\n\tvw := vwPool.Get().(*valueWriter)\n\tdefer putValueWriter(vw)\n\n\tvw.reset(nil)\n\tvw.push(mElement)\n\n\terr := copyValue(vw, src)\n\tif err != nil {\n\t\treturn 0, nil, err\n\t}\n\n\treturn Type(vw.buf[0]), vw.buf[2:], nil\n}\n\n// copyValue will copy a single value from src to dst.\nfunc copyValue(dst ValueWriter, src ValueReader) error {\n\tvar err error\n\tswitch src.Type() {\n\tcase TypeDouble:\n\t\tvar f64 float64\n\t\tf64, err = src.ReadDouble()\n\t\tif err != nil {\n\t\t\tbreak\n\t\t}\n\t\terr = dst.WriteDouble(f64)\n\tcase TypeString:\n\t\tvar str string\n\t\tstr, err = src.ReadString()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\terr = dst.WriteString(str)\n\tcase TypeEmbeddedDocument:\n\t\terr = copyDocument(dst, src)\n\tcase TypeArray:\n\t\terr = copyArray(dst, src)\n\tcase TypeBinary:\n\t\tvar data []byte\n\t\tvar subtype byte\n\t\tdata, subtype, err = src.ReadBinary()\n\t\tif err != nil {\n\t\t\tbreak\n\t\t}\n\t\terr = dst.WriteBinaryWithSubtype(data, subtype)\n\tcase TypeUndefined:\n\t\terr = src.ReadUndefined()\n\t\tif err != nil {\n\t\t\tbreak\n\t\t}\n\t\terr = dst.WriteUndefined()\n\tcase TypeObjectID:\n\t\tvar oid ObjectID\n\t\toid, err = src.ReadObjectID()\n\t\tif err != nil {\n\t\t\tbreak\n\t\t}\n\t\terr = dst.WriteObjectID(oid)\n\tcase TypeBoolean:\n\t\tvar b bool\n\t\tb, err = src.ReadBoolean()\n\t\tif err != nil {\n\t\t\tbreak\n\t\t}\n\t\terr = dst.WriteBoolean(b)\n\tcase TypeDateTime:\n\t\tvar dt int64\n\t\tdt, err = src.ReadDateTime()\n\t\tif err != nil {\n\t\t\tbreak\n\t\t}\n\t\terr = dst.WriteDateTime(dt)\n\tcase TypeNull:\n\t\terr = src.ReadNull()\n\t\tif err != nil {\n\t\t\tbreak\n\t\t}\n\t\terr = dst.WriteNull()\n\tcase TypeRegex:\n\t\tvar pattern, options string\n\t\tpattern, options, err = src.ReadRegex()\n\t\tif err != nil {\n\t\t\tbreak\n\t\t}\n\t\terr = dst.WriteRegex(pattern, options)\n\tcase TypeDBPointer:\n\t\tvar ns string\n\t\tvar pointer ObjectID\n\t\tns, pointer, err = src.ReadDBPointer()\n\t\tif err != nil {\n\t\t\tbreak\n\t\t}\n\t\terr = dst.WriteDBPointer(ns, pointer)\n\tcase TypeJavaScript:\n\t\tvar js string\n\t\tjs, err = src.ReadJavascript()\n\t\tif err != nil {\n\t\t\tbreak\n\t\t}\n\t\terr = dst.WriteJavascript(js)\n\tcase TypeSymbol:\n\t\tvar symbol string\n\t\tsymbol, err = src.ReadSymbol()\n\t\tif err != nil {\n\t\t\tbreak\n\t\t}\n\t\terr = dst.WriteSymbol(symbol)\n\tcase TypeCodeWithScope:\n\t\tvar code string\n\t\tvar srcScope DocumentReader\n\t\tcode, srcScope, err = src.ReadCodeWithScope()\n\t\tif err != nil {\n\t\t\tbreak\n\t\t}\n\n\t\tvar dstScope DocumentWriter\n\t\tdstScope, err = dst.WriteCodeWithScope(code)\n\t\tif err != nil {\n\t\t\tbreak\n\t\t}\n\t\terr = copyDocumentCore(dstScope, srcScope)\n\tcase TypeInt32:\n\t\tvar i32 int32\n\t\ti32, err = src.ReadInt32()\n\t\tif err != nil {\n\t\t\tbreak\n\t\t}\n\t\terr = dst.WriteInt32(i32)\n\tcase TypeTimestamp:\n\t\tvar t, i uint32\n\t\tt, i, err = src.ReadTimestamp()\n\t\tif err != nil {\n\t\t\tbreak\n\t\t}\n\t\terr = dst.WriteTimestamp(t, i)\n\tcase TypeInt64:\n\t\tvar i64 int64\n\t\ti64, err = src.ReadInt64()\n\t\tif err != nil {\n\t\t\tbreak\n\t\t}\n\t\terr = dst.WriteInt64(i64)\n\tcase TypeDecimal128:\n\t\tvar d128 Decimal128\n\t\td128, err = src.ReadDecimal128()\n\t\tif err != nil {\n\t\t\tbreak\n\t\t}\n\t\terr = dst.WriteDecimal128(d128)\n\tcase TypeMinKey:\n\t\terr = src.ReadMinKey()\n\t\tif err != nil {\n\t\t\tbreak\n\t\t}\n\t\terr = dst.WriteMinKey()\n\tcase TypeMaxKey:\n\t\terr = src.ReadMaxKey()\n\t\tif err != nil {\n\t\t\tbreak\n\t\t}\n\t\terr = dst.WriteMaxKey()\n\tdefault:\n\t\terr = fmt.Errorf(\"cannot copy unknown BSON type %s\", src.Type())\n\t}\n\n\treturn err\n}\n\nfunc copyArray(dst ValueWriter, src ValueReader) error {\n\tar, err := src.ReadArray()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\taw, err := dst.WriteArray()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor {\n\t\tvr, err := ar.ReadValue()\n\t\tif errors.Is(err, ErrEOA) {\n\t\t\tbreak\n\t\t}\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tvw, err := aw.WriteArrayElement()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\terr = copyValue(vw, vr)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\treturn aw.WriteArrayEnd()\n}\n\nfunc copyDocumentCore(dw DocumentWriter, dr DocumentReader) error {\n\tfor {\n\t\tkey, vr, err := dr.ReadElement()\n\t\tif errors.Is(err, ErrEOD) {\n\t\t\tbreak\n\t\t}\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tvw, err := dw.WriteDocumentElement(key)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\terr = copyValue(vw, vr)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\treturn dw.WriteDocumentEnd()\n}\n\n// bytesReader is the interface used to read BSON bytes from a valueReader.\n//\n// The bytes of the value will be appended to dst.\ntype bytesReader interface {\n\treadValueBytes(dst []byte) (Type, []byte, error)\n}\n\n// bytesWriter is the interface used to write BSON bytes to a valueWriter.\ntype bytesWriter interface {\n\twriteValueBytes(t Type, b []byte) error\n}\n"
  },
  {
    "path": "bson/copier_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bson\n\nimport (\n\t\"bytes\"\n\t\"errors\"\n\t\"fmt\"\n\t\"testing\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n)\n\nfunc TestCopier(t *testing.T) {\n\tt.Run(\"CopyDocument\", func(t *testing.T) {\n\t\tt.Run(\"ReadDocument Error\", func(t *testing.T) {\n\t\t\twant := errors.New(\"ReadDocumentError\")\n\t\t\tsrc := &TestValueReaderWriter{t: t, err: want, errAfter: llvrwReadDocument}\n\t\t\tgot := copyDocument(nil, src)\n\t\t\tif !assert.CompareErrors(got, want) {\n\t\t\t\tt.Errorf(\"Did not receive correct error. got %v; want %v\", got, want)\n\t\t\t}\n\t\t})\n\t\tt.Run(\"WriteDocument Error\", func(t *testing.T) {\n\t\t\twant := errors.New(\"WriteDocumentError\")\n\t\t\tsrc := &TestValueReaderWriter{}\n\t\t\tdst := &TestValueReaderWriter{t: t, err: want, errAfter: llvrwWriteDocument}\n\t\t\tgot := copyDocument(dst, src)\n\t\t\tif !assert.CompareErrors(got, want) {\n\t\t\t\tt.Errorf(\"Did not receive correct error. got %v; want %v\", got, want)\n\t\t\t}\n\t\t})\n\t\tt.Run(\"success\", func(t *testing.T) {\n\t\t\tidx, doc := bsoncore.AppendDocumentStart(nil)\n\t\t\tdoc = bsoncore.AppendStringElement(doc, \"Hello\", \"world\")\n\t\t\tdoc, err := bsoncore.AppendDocumentEnd(doc, idx)\n\t\t\tnoerr(t, err)\n\t\t\tsrc := newBufferedDocumentReader(doc)\n\t\t\tdst := newValueWriterFromSlice(make([]byte, 0))\n\t\t\twant := doc\n\t\t\terr = copyDocument(dst, src)\n\t\t\tnoerr(t, err)\n\t\t\tgot := dst.buf\n\t\t\tif !bytes.Equal(got, want) {\n\t\t\t\tt.Errorf(\"Bytes are not equal. got %v; want %v\", got, want)\n\t\t\t}\n\t\t})\n\t})\n\tt.Run(\"copyArray\", func(t *testing.T) {\n\t\tt.Run(\"ReadArray Error\", func(t *testing.T) {\n\t\t\twant := errors.New(\"ReadArrayError\")\n\t\t\tsrc := &TestValueReaderWriter{t: t, err: want, errAfter: llvrwReadArray}\n\t\t\tgot := copyArray(nil, src)\n\t\t\tif !assert.CompareErrors(got, want) {\n\t\t\t\tt.Errorf(\"Did not receive correct error. got %v; want %v\", got, want)\n\t\t\t}\n\t\t})\n\t\tt.Run(\"WriteArray Error\", func(t *testing.T) {\n\t\t\twant := errors.New(\"WriteArrayError\")\n\t\t\tsrc := &TestValueReaderWriter{}\n\t\t\tdst := &TestValueReaderWriter{t: t, err: want, errAfter: llvrwWriteArray}\n\t\t\tgot := copyArray(dst, src)\n\t\t\tif !assert.CompareErrors(got, want) {\n\t\t\t\tt.Errorf(\"Did not receive correct error. got %v; want %v\", got, want)\n\t\t\t}\n\t\t})\n\t\tt.Run(\"success\", func(t *testing.T) {\n\t\t\tidx, doc := bsoncore.AppendDocumentStart(nil)\n\t\t\taidx, doc := bsoncore.AppendArrayElementStart(doc, \"foo\")\n\t\t\tdoc = bsoncore.AppendStringElement(doc, \"0\", \"Hello, world!\")\n\t\t\tdoc, err := bsoncore.AppendArrayEnd(doc, aidx)\n\t\t\tnoerr(t, err)\n\t\t\tdoc, err = bsoncore.AppendDocumentEnd(doc, idx)\n\t\t\tnoerr(t, err)\n\t\t\tsrc := newBufferedDocumentReader(doc)\n\n\t\t\t_, err = src.ReadDocument()\n\t\t\tnoerr(t, err)\n\t\t\t_, _, err = src.ReadElement()\n\t\t\tnoerr(t, err)\n\n\t\t\tdst := newValueWriterFromSlice(make([]byte, 0))\n\t\t\t_, err = dst.WriteDocument()\n\t\t\tnoerr(t, err)\n\t\t\t_, err = dst.WriteDocumentElement(\"foo\")\n\t\t\tnoerr(t, err)\n\t\t\twant := doc\n\n\t\t\terr = copyArray(dst, src)\n\t\t\tnoerr(t, err)\n\n\t\t\terr = dst.WriteDocumentEnd()\n\t\t\tnoerr(t, err)\n\n\t\t\tgot := dst.buf\n\t\t\tif !bytes.Equal(got, want) {\n\t\t\t\tt.Errorf(\"Bytes are not equal. got %v; want %v\", got, want)\n\t\t\t}\n\t\t})\n\t})\n\tt.Run(\"CopyValue\", func(t *testing.T) {\n\t\ttestCases := []struct {\n\t\t\tname string\n\t\t\tdst  *TestValueReaderWriter\n\t\t\tsrc  *TestValueReaderWriter\n\t\t\terr  error\n\t\t}{\n\t\t\t{\n\t\t\t\t\"Double/src/error\",\n\t\t\t\t&TestValueReaderWriter{},\n\t\t\t\t&TestValueReaderWriter{bsontype: TypeDouble, err: errors.New(\"1\"), errAfter: llvrwReadDouble},\n\t\t\t\terrors.New(\"1\"),\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"Double/dst/error\",\n\t\t\t\t&TestValueReaderWriter{bsontype: TypeDouble, err: errors.New(\"2\"), errAfter: llvrwWriteDouble},\n\t\t\t\t&TestValueReaderWriter{bsontype: TypeDouble, readval: float64(3.14159)},\n\t\t\t\terrors.New(\"2\"),\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"String/src/error\",\n\t\t\t\t&TestValueReaderWriter{},\n\t\t\t\t&TestValueReaderWriter{bsontype: TypeString, err: errors.New(\"1\"), errAfter: llvrwReadString},\n\t\t\t\terrors.New(\"1\"),\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"String/dst/error\",\n\t\t\t\t&TestValueReaderWriter{bsontype: TypeString, err: errors.New(\"2\"), errAfter: llvrwWriteString},\n\t\t\t\t&TestValueReaderWriter{bsontype: TypeString, readval: \"hello, world\"},\n\t\t\t\terrors.New(\"2\"),\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"Document/src/error\",\n\t\t\t\t&TestValueReaderWriter{},\n\t\t\t\t&TestValueReaderWriter{bsontype: TypeEmbeddedDocument, err: errors.New(\"1\"), errAfter: llvrwReadDocument},\n\t\t\t\terrors.New(\"1\"),\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"Array/dst/error\",\n\t\t\t\t&TestValueReaderWriter{},\n\t\t\t\t&TestValueReaderWriter{bsontype: TypeArray, err: errors.New(\"2\"), errAfter: llvrwReadArray},\n\t\t\t\terrors.New(\"2\"),\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"Binary/src/error\",\n\t\t\t\t&TestValueReaderWriter{},\n\t\t\t\t&TestValueReaderWriter{bsontype: TypeBinary, err: errors.New(\"1\"), errAfter: llvrwReadBinary},\n\t\t\t\terrors.New(\"1\"),\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"Binary/dst/error\",\n\t\t\t\t&TestValueReaderWriter{bsontype: TypeBinary, err: errors.New(\"2\"), errAfter: llvrwWriteBinaryWithSubtype},\n\t\t\t\t&TestValueReaderWriter{\n\t\t\t\t\tbsontype: TypeBinary,\n\t\t\t\t\treadval: bsoncore.Value{\n\t\t\t\t\t\tType: bsoncore.TypeBinary,\n\t\t\t\t\t\tData: []byte{0x03, 0x00, 0x00, 0x00, 0xFF, 0x01, 0x02, 0x03},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\terrors.New(\"2\"),\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"Undefined/src/error\",\n\t\t\t\t&TestValueReaderWriter{},\n\t\t\t\t&TestValueReaderWriter{bsontype: TypeUndefined, err: errors.New(\"1\"), errAfter: llvrwReadUndefined},\n\t\t\t\terrors.New(\"1\"),\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"Undefined/dst/error\",\n\t\t\t\t&TestValueReaderWriter{bsontype: TypeUndefined, err: errors.New(\"2\"), errAfter: llvrwWriteUndefined},\n\t\t\t\t&TestValueReaderWriter{bsontype: TypeUndefined},\n\t\t\t\terrors.New(\"2\"),\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"ObjectID/src/error\",\n\t\t\t\t&TestValueReaderWriter{},\n\t\t\t\t&TestValueReaderWriter{bsontype: TypeObjectID, err: errors.New(\"1\"), errAfter: llvrwReadObjectID},\n\t\t\t\terrors.New(\"1\"),\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"ObjectID/dst/error\",\n\t\t\t\t&TestValueReaderWriter{bsontype: TypeObjectID, err: errors.New(\"2\"), errAfter: llvrwWriteObjectID},\n\t\t\t\t&TestValueReaderWriter{bsontype: TypeObjectID, readval: ObjectID{0x01, 0x02, 0x03}},\n\t\t\t\terrors.New(\"2\"),\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"Boolean/src/error\",\n\t\t\t\t&TestValueReaderWriter{},\n\t\t\t\t&TestValueReaderWriter{bsontype: TypeBoolean, err: errors.New(\"1\"), errAfter: llvrwReadBoolean},\n\t\t\t\terrors.New(\"1\"),\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"Boolean/dst/error\",\n\t\t\t\t&TestValueReaderWriter{bsontype: TypeBoolean, err: errors.New(\"2\"), errAfter: llvrwWriteBoolean},\n\t\t\t\t&TestValueReaderWriter{bsontype: TypeBoolean, readval: bool(true)},\n\t\t\t\terrors.New(\"2\"),\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"DateTime/src/error\",\n\t\t\t\t&TestValueReaderWriter{},\n\t\t\t\t&TestValueReaderWriter{bsontype: TypeDateTime, err: errors.New(\"1\"), errAfter: llvrwReadDateTime},\n\t\t\t\terrors.New(\"1\"),\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"DateTime/dst/error\",\n\t\t\t\t&TestValueReaderWriter{bsontype: TypeDateTime, err: errors.New(\"2\"), errAfter: llvrwWriteDateTime},\n\t\t\t\t&TestValueReaderWriter{bsontype: TypeDateTime, readval: int64(1234567890)},\n\t\t\t\terrors.New(\"2\"),\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"Null/src/error\",\n\t\t\t\t&TestValueReaderWriter{},\n\t\t\t\t&TestValueReaderWriter{bsontype: TypeNull, err: errors.New(\"1\"), errAfter: llvrwReadNull},\n\t\t\t\terrors.New(\"1\"),\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"Null/dst/error\",\n\t\t\t\t&TestValueReaderWriter{bsontype: TypeNull, err: errors.New(\"2\"), errAfter: llvrwWriteNull},\n\t\t\t\t&TestValueReaderWriter{bsontype: TypeNull},\n\t\t\t\terrors.New(\"2\"),\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"Regex/src/error\",\n\t\t\t\t&TestValueReaderWriter{},\n\t\t\t\t&TestValueReaderWriter{bsontype: TypeRegex, err: errors.New(\"1\"), errAfter: llvrwReadRegex},\n\t\t\t\terrors.New(\"1\"),\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"Regex/dst/error\",\n\t\t\t\t&TestValueReaderWriter{bsontype: TypeRegex, err: errors.New(\"2\"), errAfter: llvrwWriteRegex},\n\t\t\t\t&TestValueReaderWriter{\n\t\t\t\t\tbsontype: TypeRegex,\n\t\t\t\t\treadval: bsoncore.Value{\n\t\t\t\t\t\tType: bsoncore.TypeRegex,\n\t\t\t\t\t\tData: bsoncore.AppendRegex(nil, \"hello\", \"world\"),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\terrors.New(\"2\"),\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"DBPointer/src/error\",\n\t\t\t\t&TestValueReaderWriter{},\n\t\t\t\t&TestValueReaderWriter{bsontype: TypeDBPointer, err: errors.New(\"1\"), errAfter: llvrwReadDBPointer},\n\t\t\t\terrors.New(\"1\"),\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"DBPointer/dst/error\",\n\t\t\t\t&TestValueReaderWriter{bsontype: TypeDBPointer, err: errors.New(\"2\"), errAfter: llvrwWriteDBPointer},\n\t\t\t\t&TestValueReaderWriter{\n\t\t\t\t\tbsontype: TypeDBPointer,\n\t\t\t\t\treadval: bsoncore.Value{\n\t\t\t\t\t\tType: bsoncore.TypeDBPointer,\n\t\t\t\t\t\tData: bsoncore.AppendDBPointer(nil, \"foo\", ObjectID{0x01, 0x02, 0x03}),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\terrors.New(\"2\"),\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"Javascript/src/error\",\n\t\t\t\t&TestValueReaderWriter{},\n\t\t\t\t&TestValueReaderWriter{bsontype: TypeJavaScript, err: errors.New(\"1\"), errAfter: llvrwReadJavascript},\n\t\t\t\terrors.New(\"1\"),\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"Javascript/dst/error\",\n\t\t\t\t&TestValueReaderWriter{bsontype: TypeJavaScript, err: errors.New(\"2\"), errAfter: llvrwWriteJavascript},\n\t\t\t\t&TestValueReaderWriter{bsontype: TypeJavaScript, readval: \"hello, world\"},\n\t\t\t\terrors.New(\"2\"),\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"Symbol/src/error\",\n\t\t\t\t&TestValueReaderWriter{},\n\t\t\t\t&TestValueReaderWriter{bsontype: TypeSymbol, err: errors.New(\"1\"), errAfter: llvrwReadSymbol},\n\t\t\t\terrors.New(\"1\"),\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"Symbol/dst/error\",\n\t\t\t\t&TestValueReaderWriter{bsontype: TypeSymbol, err: errors.New(\"2\"), errAfter: llvrwWriteSymbol},\n\t\t\t\t&TestValueReaderWriter{\n\t\t\t\t\tbsontype: TypeSymbol,\n\t\t\t\t\treadval: bsoncore.Value{\n\t\t\t\t\t\tType: bsoncore.TypeSymbol,\n\t\t\t\t\t\tData: bsoncore.AppendSymbol(nil, \"hello, world\"),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\terrors.New(\"2\"),\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"CodeWithScope/src/error\",\n\t\t\t\t&TestValueReaderWriter{},\n\t\t\t\t&TestValueReaderWriter{bsontype: TypeCodeWithScope, err: errors.New(\"1\"), errAfter: llvrwReadCodeWithScope},\n\t\t\t\terrors.New(\"1\"),\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"CodeWithScope/dst/error\",\n\t\t\t\t&TestValueReaderWriter{bsontype: TypeCodeWithScope, err: errors.New(\"2\"), errAfter: llvrwWriteCodeWithScope},\n\t\t\t\t&TestValueReaderWriter{bsontype: TypeCodeWithScope},\n\t\t\t\terrors.New(\"2\"),\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"CodeWithScope/dst/copyDocumentCore error\",\n\t\t\t\t&TestValueReaderWriter{err: errors.New(\"3\"), errAfter: llvrwWriteDocumentElement},\n\t\t\t\t&TestValueReaderWriter{bsontype: TypeCodeWithScope},\n\t\t\t\terrors.New(\"3\"),\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"Int32/src/error\",\n\t\t\t\t&TestValueReaderWriter{},\n\t\t\t\t&TestValueReaderWriter{bsontype: TypeInt32, err: errors.New(\"1\"), errAfter: llvrwReadInt32},\n\t\t\t\terrors.New(\"1\"),\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"Int32/dst/error\",\n\t\t\t\t&TestValueReaderWriter{bsontype: TypeInt32, err: errors.New(\"2\"), errAfter: llvrwWriteInt32},\n\t\t\t\t&TestValueReaderWriter{bsontype: TypeInt32, readval: int32(12345)},\n\t\t\t\terrors.New(\"2\"),\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"Timestamp/src/error\",\n\t\t\t\t&TestValueReaderWriter{},\n\t\t\t\t&TestValueReaderWriter{bsontype: TypeTimestamp, err: errors.New(\"1\"), errAfter: llvrwReadTimestamp},\n\t\t\t\terrors.New(\"1\"),\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"Timestamp/dst/error\",\n\t\t\t\t&TestValueReaderWriter{bsontype: TypeTimestamp, err: errors.New(\"2\"), errAfter: llvrwWriteTimestamp},\n\t\t\t\t&TestValueReaderWriter{\n\t\t\t\t\tbsontype: TypeTimestamp,\n\t\t\t\t\treadval: bsoncore.Value{\n\t\t\t\t\t\tType: bsoncore.TypeTimestamp,\n\t\t\t\t\t\tData: bsoncore.AppendTimestamp(nil, 12345, 67890),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\terrors.New(\"2\"),\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"Int64/src/error\",\n\t\t\t\t&TestValueReaderWriter{},\n\t\t\t\t&TestValueReaderWriter{bsontype: TypeInt64, err: errors.New(\"1\"), errAfter: llvrwReadInt64},\n\t\t\t\terrors.New(\"1\"),\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"Int64/dst/error\",\n\t\t\t\t&TestValueReaderWriter{bsontype: TypeInt64, err: errors.New(\"2\"), errAfter: llvrwWriteInt64},\n\t\t\t\t&TestValueReaderWriter{bsontype: TypeInt64, readval: int64(1234567890)},\n\t\t\t\terrors.New(\"2\"),\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"Decimal128/src/error\",\n\t\t\t\t&TestValueReaderWriter{},\n\t\t\t\t&TestValueReaderWriter{bsontype: TypeDecimal128, err: errors.New(\"1\"), errAfter: llvrwReadDecimal128},\n\t\t\t\terrors.New(\"1\"),\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"Decimal128/dst/error\",\n\t\t\t\t&TestValueReaderWriter{bsontype: TypeDecimal128, err: errors.New(\"2\"), errAfter: llvrwWriteDecimal128},\n\t\t\t\t&TestValueReaderWriter{bsontype: TypeDecimal128, readval: NewDecimal128(12345, 67890)},\n\t\t\t\terrors.New(\"2\"),\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"MinKey/src/error\",\n\t\t\t\t&TestValueReaderWriter{},\n\t\t\t\t&TestValueReaderWriter{bsontype: TypeMinKey, err: errors.New(\"1\"), errAfter: llvrwReadMinKey},\n\t\t\t\terrors.New(\"1\"),\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"MinKey/dst/error\",\n\t\t\t\t&TestValueReaderWriter{bsontype: TypeMinKey, err: errors.New(\"2\"), errAfter: llvrwWriteMinKey},\n\t\t\t\t&TestValueReaderWriter{bsontype: TypeMinKey},\n\t\t\t\terrors.New(\"2\"),\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"MaxKey/src/error\",\n\t\t\t\t&TestValueReaderWriter{},\n\t\t\t\t&TestValueReaderWriter{bsontype: TypeMaxKey, err: errors.New(\"1\"), errAfter: llvrwReadMaxKey},\n\t\t\t\terrors.New(\"1\"),\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"MaxKey/dst/error\",\n\t\t\t\t&TestValueReaderWriter{bsontype: TypeMaxKey, err: errors.New(\"2\"), errAfter: llvrwWriteMaxKey},\n\t\t\t\t&TestValueReaderWriter{bsontype: TypeMaxKey},\n\t\t\t\terrors.New(\"2\"),\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"Unknown BSON type error\",\n\t\t\t\t&TestValueReaderWriter{},\n\t\t\t\t&TestValueReaderWriter{},\n\t\t\t\tfmt.Errorf(\"cannot copy unknown BSON type %s\", Type(0)),\n\t\t\t},\n\t\t}\n\n\t\tfor _, tc := range testCases {\n\t\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\t\ttc.dst.t, tc.src.t = t, t\n\t\t\t\terr := copyValue(tc.dst, tc.src)\n\t\t\t\tif !assert.CompareErrors(err, tc.err) {\n\t\t\t\t\tt.Errorf(\"Did not receive expected error. got %v; want %v\", err, tc.err)\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t})\n\tt.Run(\"CopyValueFromBytes\", func(t *testing.T) {\n\t\tt.Run(\"BytesWriter\", func(t *testing.T) {\n\t\t\tvw := newValueWriterFromSlice(make([]byte, 0))\n\t\t\t_, err := vw.WriteDocument()\n\t\t\tnoerr(t, err)\n\t\t\t_, err = vw.WriteDocumentElement(\"foo\")\n\t\t\tnoerr(t, err)\n\t\t\terr = copyValueFromBytes(vw, TypeString, bsoncore.AppendString(nil, \"bar\"))\n\t\t\tnoerr(t, err)\n\t\t\terr = vw.WriteDocumentEnd()\n\t\t\tnoerr(t, err)\n\t\t\tvar idx int32\n\t\t\twant, err := bsoncore.AppendDocumentEnd(\n\t\t\t\tbsoncore.AppendStringElement(\n\t\t\t\t\tbsoncore.AppendDocumentStartInline(nil, &idx),\n\t\t\t\t\t\"foo\", \"bar\",\n\t\t\t\t),\n\t\t\t\tidx,\n\t\t\t)\n\t\t\tnoerr(t, err)\n\t\t\tgot := vw.buf\n\t\t\tif !bytes.Equal(got, want) {\n\t\t\t\tt.Errorf(\"Bytes are not equal. got %v; want %v\", got, want)\n\t\t\t}\n\t\t})\n\t\tt.Run(\"Non BytesWriter\", func(t *testing.T) {\n\t\t\tllvrw := &TestValueReaderWriter{t: t}\n\t\t\terr := copyValueFromBytes(llvrw, TypeString, bsoncore.AppendString(nil, \"bar\"))\n\t\t\tnoerr(t, err)\n\t\t\tgot, want := llvrw.invoked, llvrwWriteString\n\t\t\tif got != want {\n\t\t\t\tt.Errorf(\"Incorrect method invoked on llvrw. got %v; want %v\", got, want)\n\t\t\t}\n\t\t})\n\t})\n\tt.Run(\"CopyValueToBytes\", func(t *testing.T) {\n\t\tt.Run(\"BytesReader\", func(t *testing.T) {\n\t\t\tvar idx int32\n\t\t\tb, err := bsoncore.AppendDocumentEnd(\n\t\t\t\tbsoncore.AppendStringElement(\n\t\t\t\t\tbsoncore.AppendDocumentStartInline(nil, &idx),\n\t\t\t\t\t\"hello\", \"world\",\n\t\t\t\t),\n\t\t\t\tidx,\n\t\t\t)\n\t\t\tnoerr(t, err)\n\t\t\tvr := newBufferedDocumentReader(b)\n\t\t\t_, err = vr.ReadDocument()\n\t\t\tnoerr(t, err)\n\t\t\t_, _, err = vr.ReadElement()\n\t\t\tnoerr(t, err)\n\t\t\tbtype, got, err := copyValueToBytes(vr)\n\t\t\tnoerr(t, err)\n\t\t\twant := bsoncore.AppendString(nil, \"world\")\n\t\t\tif btype != TypeString {\n\t\t\t\tt.Errorf(\"Incorrect type returned. got %v; want %v\", btype, TypeString)\n\t\t\t}\n\t\t\tif !bytes.Equal(got, want) {\n\t\t\t\tt.Errorf(\"Bytes do not match. got %v; want %v\", got, want)\n\t\t\t}\n\t\t})\n\t\tt.Run(\"Non BytesReader\", func(t *testing.T) {\n\t\t\tllvrw := &TestValueReaderWriter{t: t, bsontype: TypeString, readval: \"Hello, world!\"}\n\t\t\tbtype, got, err := copyValueToBytes(llvrw)\n\t\t\tnoerr(t, err)\n\t\t\twant := bsoncore.AppendString(nil, \"Hello, world!\")\n\t\t\tif btype != TypeString {\n\t\t\t\tt.Errorf(\"Incorrect type returned. got %v; want %v\", btype, TypeString)\n\t\t\t}\n\t\t\tif !bytes.Equal(got, want) {\n\t\t\t\tt.Errorf(\"Bytes do not match. got %v; want %v\", got, want)\n\t\t\t}\n\t\t})\n\t})\n\tt.Run(\"AppendValueBytes\", func(t *testing.T) {\n\t\tt.Run(\"BytesReader\", func(t *testing.T) {\n\t\t\tvar idx int32\n\t\t\tb, err := bsoncore.AppendDocumentEnd(\n\t\t\t\tbsoncore.AppendStringElement(\n\t\t\t\t\tbsoncore.AppendDocumentStartInline(nil, &idx),\n\t\t\t\t\t\"hello\", \"world\",\n\t\t\t\t),\n\t\t\t\tidx,\n\t\t\t)\n\t\t\tnoerr(t, err)\n\t\t\tvr := newBufferedDocumentReader(b)\n\t\t\t_, err = vr.ReadDocument()\n\t\t\tnoerr(t, err)\n\t\t\t_, _, err = vr.ReadElement()\n\t\t\tnoerr(t, err)\n\t\t\tbtype, got, err := copyValueToBytes(vr)\n\t\t\tnoerr(t, err)\n\t\t\twant := bsoncore.AppendString(nil, \"world\")\n\t\t\tif btype != TypeString {\n\t\t\t\tt.Errorf(\"Incorrect type returned. got %v; want %v\", btype, TypeString)\n\t\t\t}\n\t\t\tif !bytes.Equal(got, want) {\n\t\t\t\tt.Errorf(\"Bytes do not match. got %v; want %v\", got, want)\n\t\t\t}\n\t\t})\n\t\tt.Run(\"Non BytesReader\", func(t *testing.T) {\n\t\t\tllvrw := &TestValueReaderWriter{t: t, bsontype: TypeString, readval: \"Hello, world!\"}\n\t\t\tbtype, got, err := copyValueToBytes(llvrw)\n\t\t\tnoerr(t, err)\n\t\t\twant := bsoncore.AppendString(nil, \"Hello, world!\")\n\t\t\tif btype != TypeString {\n\t\t\t\tt.Errorf(\"Incorrect type returned. got %v; want %v\", btype, TypeString)\n\t\t\t}\n\t\t\tif !bytes.Equal(got, want) {\n\t\t\t\tt.Errorf(\"Bytes do not match. got %v; want %v\", got, want)\n\t\t\t}\n\t\t})\n\t\tt.Run(\"CopyValue error\", func(t *testing.T) {\n\t\t\twant := errors.New(\"CopyValue error\")\n\t\t\tllvrw := &TestValueReaderWriter{t: t, bsontype: TypeString, err: want, errAfter: llvrwReadString}\n\t\t\t_, _, got := copyValueToBytes(llvrw)\n\t\t\tif !assert.CompareErrors(got, want) {\n\t\t\t\tt.Errorf(\"Errors do not match. got %v; want %v\", got, want)\n\t\t\t}\n\t\t})\n\t})\n}\n"
  },
  {
    "path": "bson/decimal.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n//\n// Based on gopkg.in/mgo.v2/bson by Gustavo Niemeyer\n// See THIRD-PARTY-NOTICES for original license terms.\n\npackage bson\n\nimport (\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"math/big\"\n\t\"regexp\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/decimal128\"\n)\n\n// These constants are the maximum and minimum values for the exponent field in a decimal128 value.\nconst (\n\tMaxDecimal128Exp = 6111\n\tMinDecimal128Exp = -6176\n)\n\n// These errors are returned when an invalid value is parsed as a big.Int.\nvar (\n\tErrParseNaN    = errors.New(\"cannot parse NaN as a *big.Int\")\n\tErrParseInf    = errors.New(\"cannot parse Infinity as a *big.Int\")\n\tErrParseNegInf = errors.New(\"cannot parse -Infinity as a *big.Int\")\n)\n\n// Decimal128 holds decimal128 BSON values.\ntype Decimal128 struct {\n\th, l uint64\n}\n\n// NewDecimal128 creates a Decimal128 using the provide high and low uint64s.\nfunc NewDecimal128(h, l uint64) Decimal128 {\n\treturn Decimal128{h: h, l: l}\n}\n\n// GetBytes returns the underlying bytes of the BSON decimal value as two uint64 values. The first\n// contains the most first 8 bytes of the value and the second contains the latter.\nfunc (d Decimal128) GetBytes() (uint64, uint64) {\n\treturn d.h, d.l\n}\n\n// String returns a string representation of the decimal value.\nfunc (d Decimal128) String() string {\n\treturn decimal128.String(d.h, d.l)\n}\n\n// BigInt returns significand as big.Int and exponent, bi * 10 ^ exp.\nfunc (d Decimal128) BigInt() (*big.Int, int, error) {\n\thigh, low := d.GetBytes()\n\tposSign := high>>63&1 == 0 // positive sign\n\n\tswitch high >> 58 & (1<<5 - 1) {\n\tcase 0x1F:\n\t\treturn nil, 0, ErrParseNaN\n\tcase 0x1E:\n\t\tif posSign {\n\t\t\treturn nil, 0, ErrParseInf\n\t\t}\n\t\treturn nil, 0, ErrParseNegInf\n\t}\n\n\tvar exp int\n\tif high>>61&3 == 3 {\n\t\t// Bits: 1*sign 2*ignored 14*exponent 111*significand.\n\t\t// Implicit 0b100 prefix in significand.\n\t\texp = int(high >> 47 & (1<<14 - 1))\n\t\t// Spec says all of these values are out of range.\n\t\thigh, low = 0, 0\n\t} else {\n\t\t// Bits: 1*sign 14*exponent 113*significand\n\t\texp = int(high >> 49 & (1<<14 - 1))\n\t\thigh &= (1<<49 - 1)\n\t}\n\texp += MinDecimal128Exp\n\n\t// Would be handled by the logic below, but that's trivial and common.\n\tif high == 0 && low == 0 && exp == 0 {\n\t\treturn new(big.Int), 0, nil\n\t}\n\n\tbi := big.NewInt(0)\n\tconst host32bit = ^uint(0)>>32 == 0\n\tif host32bit {\n\t\tbi.SetBits([]big.Word{big.Word(low), big.Word(low >> 32), big.Word(high), big.Word(high >> 32)})\n\t} else {\n\t\tbi.SetBits([]big.Word{big.Word(low), big.Word(high)})\n\t}\n\n\tif !posSign {\n\t\treturn bi.Neg(bi), exp, nil\n\t}\n\treturn bi, exp, nil\n}\n\n// IsNaN returns whether d is NaN.\nfunc (d Decimal128) IsNaN() bool {\n\treturn d.h>>58&(1<<5-1) == 0x1F\n}\n\n// IsInf returns:\n//\n//\t+1 d == Infinity\n//\t 0 other case\n//\t-1 d == -Infinity\nfunc (d Decimal128) IsInf() int {\n\tif d.h>>58&(1<<5-1) != 0x1E {\n\t\treturn 0\n\t}\n\n\tif d.h>>63&1 == 0 {\n\t\treturn 1\n\t}\n\treturn -1\n}\n\n// IsZero returns true if d is the empty Decimal128.\nfunc (d Decimal128) IsZero() bool {\n\treturn d.h == 0 && d.l == 0\n}\n\n// MarshalJSON returns Decimal128 as a string.\nfunc (d Decimal128) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(d.String())\n}\n\n// UnmarshalJSON creates a Decimal128 from a JSON string, an extended JSON $numberDecimal value, or the string\n// \"null\". If b is a JSON string or extended JSON value, d will have the value of that string, and if b is \"null\", d will\n// be unchanged.\nfunc (d *Decimal128) UnmarshalJSON(b []byte) error {\n\t// Ignore \"null\" to keep parity with the standard library. Decoding a JSON null into a non-pointer Decimal128 field\n\t// will leave the field unchanged. For pointer values, encoding/json will set the pointer to nil and will not\n\t// enter the UnmarshalJSON hook.\n\tif string(b) == \"null\" {\n\t\treturn nil\n\t}\n\n\tvar res any\n\terr := json.Unmarshal(b, &res)\n\tif err != nil {\n\t\treturn err\n\t}\n\tstr, ok := res.(string)\n\n\t// Extended JSON\n\tif !ok {\n\t\tm, ok := res.(map[string]any)\n\t\tif !ok {\n\t\t\treturn errors.New(\"not an extended JSON Decimal128: expected document\")\n\t\t}\n\t\td128, ok := m[\"$numberDecimal\"]\n\t\tif !ok {\n\t\t\treturn errors.New(\"not an extended JSON Decimal128: expected key $numberDecimal\")\n\t\t}\n\t\tstr, ok = d128.(string)\n\t\tif !ok {\n\t\t\treturn errors.New(\"not an extended JSON Decimal128: expected decimal to be string\")\n\t\t}\n\t}\n\n\t*d, err = ParseDecimal128(str)\n\treturn err\n}\n\nvar (\n\tdNaN    = Decimal128{0x1F << 58, 0}\n\tdPosInf = Decimal128{0x1E << 58, 0}\n\tdNegInf = Decimal128{0x3E << 58, 0}\n)\n\nfunc dErr(s string) (Decimal128, error) {\n\treturn dNaN, fmt.Errorf(\"cannot parse %q as a decimal128\", s)\n}\n\n// match scientific notation number, example -10.15e-18\nvar normalNumber = regexp.MustCompile(`^(?P<int>[-+]?\\d*)?(?:\\.(?P<dec>\\d*))?(?:[Ee](?P<exp>[-+]?\\d+))?$`)\n\n// ParseDecimal128 takes the given string and attempts to parse it into a valid\n// Decimal128 value.\nfunc ParseDecimal128(s string) (Decimal128, error) {\n\tif s == \"\" {\n\t\treturn dErr(s)\n\t}\n\n\tmatches := normalNumber.FindStringSubmatch(s)\n\tif len(matches) == 0 {\n\t\torig := s\n\t\tneg := s[0] == '-'\n\t\tif neg || s[0] == '+' {\n\t\t\ts = s[1:]\n\t\t}\n\n\t\tif s == \"NaN\" || s == \"nan\" || strings.EqualFold(s, \"nan\") {\n\t\t\treturn dNaN, nil\n\t\t}\n\t\tif s == \"Inf\" || s == \"inf\" || strings.EqualFold(s, \"inf\") || strings.EqualFold(s, \"infinity\") {\n\t\t\tif neg {\n\t\t\t\treturn dNegInf, nil\n\t\t\t}\n\t\t\treturn dPosInf, nil\n\t\t}\n\t\treturn dErr(orig)\n\t}\n\n\tintPart := matches[1]\n\tdecPart := matches[2]\n\texpPart := matches[3]\n\n\tvar err error\n\texp := 0\n\tif expPart != \"\" {\n\t\texp, err = strconv.Atoi(expPart)\n\t\tif err != nil {\n\t\t\treturn dErr(s)\n\t\t}\n\t}\n\tif decPart != \"\" {\n\t\texp -= len(decPart)\n\t}\n\n\tif len(strings.Trim(intPart+decPart, \"-0\")) > 35 {\n\t\treturn dErr(s)\n\t}\n\n\t// Parse the significand (i.e. the non-exponent part) as a big.Int.\n\tbi, ok := new(big.Int).SetString(intPart+decPart, 10)\n\tif !ok {\n\t\treturn dErr(s)\n\t}\n\n\td, ok := ParseDecimal128FromBigInt(bi, exp)\n\tif !ok {\n\t\treturn dErr(s)\n\t}\n\n\tif bi.Sign() == 0 && s[0] == '-' {\n\t\td.h |= 1 << 63\n\t}\n\n\treturn d, nil\n}\n\nvar (\n\tten  = big.NewInt(10)\n\tzero = new(big.Int)\n\n\tmaxS, _ = new(big.Int).SetString(\"9999999999999999999999999999999999\", 10)\n)\n\n// ParseDecimal128FromBigInt attempts to parse the given significand and exponent into a valid Decimal128 value.\nfunc ParseDecimal128FromBigInt(bi *big.Int, exp int) (Decimal128, bool) {\n\t// copy\n\tbi = new(big.Int).Set(bi)\n\n\tq := new(big.Int)\n\tr := new(big.Int)\n\n\t// If the significand is zero, the logical value will always be zero, independent of the\n\t// exponent. However, the loops for handling out-of-range exponent values below may be extremely\n\t// slow for zero values because the significand never changes. Limit the exponent value to the\n\t// supported range here to prevent entering the loops below.\n\tif bi.Cmp(zero) == 0 {\n\t\tif exp > MaxDecimal128Exp {\n\t\t\texp = MaxDecimal128Exp\n\t\t}\n\t\tif exp < MinDecimal128Exp {\n\t\t\texp = MinDecimal128Exp\n\t\t}\n\t}\n\n\tfor bigIntCmpAbs(bi, maxS) == 1 {\n\t\tbi, _ = q.QuoRem(bi, ten, r)\n\t\tif r.Cmp(zero) != 0 {\n\t\t\treturn Decimal128{}, false\n\t\t}\n\t\texp++\n\t\tif exp > MaxDecimal128Exp {\n\t\t\treturn Decimal128{}, false\n\t\t}\n\t}\n\n\tfor exp < MinDecimal128Exp {\n\t\t// Subnormal.\n\t\tbi, _ = q.QuoRem(bi, ten, r)\n\t\tif r.Cmp(zero) != 0 {\n\t\t\treturn Decimal128{}, false\n\t\t}\n\t\texp++\n\t}\n\tfor exp > MaxDecimal128Exp {\n\t\t// Clamped.\n\t\tbi.Mul(bi, ten)\n\t\tif bigIntCmpAbs(bi, maxS) == 1 {\n\t\t\treturn Decimal128{}, false\n\t\t}\n\t\texp--\n\t}\n\n\tb := bi.Bytes()\n\tvar h, l uint64\n\tfor i := 0; i < len(b); i++ {\n\t\tif i < len(b)-8 {\n\t\t\th = h<<8 | uint64(b[i])\n\t\t\tcontinue\n\t\t}\n\t\tl = l<<8 | uint64(b[i])\n\t}\n\n\th |= uint64(exp-MinDecimal128Exp) & uint64(1<<14-1) << 49\n\tif bi.Sign() == -1 {\n\t\th |= 1 << 63\n\t}\n\n\treturn Decimal128{h: h, l: l}, true\n}\n\n// bigIntCmpAbs computes big.Int.Cmp(absoluteValue(x), absoluteValue(y)).\nfunc bigIntCmpAbs(x, y *big.Int) int {\n\txAbs := bigIntAbsValue(x)\n\tyAbs := bigIntAbsValue(y)\n\treturn xAbs.Cmp(yAbs)\n}\n\n// bigIntAbsValue returns a big.Int containing the absolute value of b.\n// If b is already a non-negative number, it is returned without any changes or copies.\nfunc bigIntAbsValue(b *big.Int) *big.Int {\n\tif b.Sign() >= 0 {\n\t\treturn b // already positive\n\t}\n\treturn new(big.Int).Abs(b)\n}\n"
  },
  {
    "path": "bson/decimal_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2022-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bson\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"math/big\"\n\t\"testing\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/require\"\n)\n\ntype bigIntTestCase struct {\n\ts string\n\n\th uint64\n\tl uint64\n\n\tbi  *big.Int\n\texp int\n\n\tremark string\n}\n\nfunc parseBigInt(s string) *big.Int {\n\tbi, _ := new(big.Int).SetString(s, 10)\n\treturn bi\n}\n\nvar (\n\tone = big.NewInt(1)\n\n\tbiMaxS  = new(big.Int).SetBytes([]byte{0x1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff})\n\tbiNMaxS = new(big.Int).Neg(biMaxS)\n\n\tbiOverflow  = new(big.Int).Add(biMaxS, one)\n\tbiNOverflow = new(big.Int).Neg(biOverflow)\n\n\tbi12345  = parseBigInt(\"12345\")\n\tbiN12345 = parseBigInt(\"-12345\")\n\n\tbi9_14  = parseBigInt(\"90123456789012\")\n\tbiN9_14 = parseBigInt(\"-90123456789012\")\n\n\tbi9_34  = parseBigInt(\"9999999999999999999999999999999999\")\n\tbiN9_34 = parseBigInt(\"-9999999999999999999999999999999999\")\n)\n\nvar bigIntTestCases = []bigIntTestCase{\n\t{s: \"12345\", h: 0x3040000000000000, l: 12345, bi: bi12345},\n\t{s: \"-12345\", h: 0xB040000000000000, l: 12345, bi: biN12345},\n\n\t{s: \"90123456.789012\", h: 0x3034000000000000, l: 90123456789012, bi: bi9_14, exp: -6},\n\t{s: \"-90123456.789012\", h: 0xB034000000000000, l: 90123456789012, bi: biN9_14, exp: -6},\n\t{s: \"9.0123456789012E+22\", h: 0x3052000000000000, l: 90123456789012, bi: bi9_14, exp: 9},\n\t{s: \"-9.0123456789012E+22\", h: 0xB052000000000000, l: 90123456789012, bi: biN9_14, exp: 9},\n\t{s: \"9.0123456789012E-8\", h: 0x3016000000000000, l: 90123456789012, bi: bi9_14, exp: -21},\n\t{s: \"-9.0123456789012E-8\", h: 0xB016000000000000, l: 90123456789012, bi: biN9_14, exp: -21},\n\n\t{s: \"9999999999999999999999999999999999\", h: 3477321013416265664, l: 4003012203950112767, bi: bi9_34},\n\t{s: \"-9999999999999999999999999999999999\", h: 12700693050271041472, l: 4003012203950112767, bi: biN9_34},\n\t{s: \"0.9999999999999999999999999999999999\", h: 3458180714999941056, l: 4003012203950112767, bi: bi9_34, exp: -34},\n\t{s: \"-0.9999999999999999999999999999999999\", h: 12681552751854716864, l: 4003012203950112767, bi: biN9_34, exp: -34},\n\t{s: \"99999999999999999.99999999999999999\", h: 3467750864208103360, l: 4003012203950112767, bi: bi9_34, exp: -17},\n\t{s: \"-99999999999999999.99999999999999999\", h: 12691122901062879168, l: 4003012203950112767, bi: biN9_34, exp: -17},\n\t{s: \"9.999999999999999999999999999999999E+35\", h: 3478446913323108288, l: 4003012203950112767, bi: bi9_34, exp: 2},\n\t{s: \"-9.999999999999999999999999999999999E+35\", h: 12701818950177884096, l: 4003012203950112767, bi: biN9_34, exp: 2},\n\t{s: \"9.999999999999999999999999999999999E+40\", h: 3481261663090214848, l: 4003012203950112767, bi: bi9_34, exp: 7},\n\t{s: \"-9.999999999999999999999999999999999E+40\", h: 12704633699944990656, l: 4003012203950112767, bi: biN9_34, exp: 7},\n\t{s: \"99999999999999999999999999999.99999\", h: 3474506263649159104, l: 4003012203950112767, bi: bi9_34, exp: -5},\n\t{s: \"-99999999999999999999999999999.99999\", h: 12697878300503934912, l: 4003012203950112767, bi: biN9_34, exp: -5},\n\n\t{s: \"1.038459371706965525706099265844019E-6143\", remark: \"subnormal\", h: 0x333333333333, l: 0x3333333333333333, bi: parseBigInt(\"10384593717069655257060992658440190\"), exp: MinDecimal128Exp - 1},\n\t{s: \"-1.038459371706965525706099265844019E-6143\", remark: \"subnormal\", h: 0x8000333333333333, l: 0x3333333333333333, bi: parseBigInt(\"-10384593717069655257060992658440190\"), exp: MinDecimal128Exp - 1},\n\n\t{s: \"rounding overflow 1\", remark: \"overflow\", bi: parseBigInt(\"103845937170696552570609926584401910\"), exp: MaxDecimal128Exp},\n\t{s: \"rounding overflow 2\", remark: \"overflow\", bi: parseBigInt(\"103845937170696552570609926584401910\"), exp: MaxDecimal128Exp},\n\n\t{s: \"subnormal overflow 1\", remark: \"overflow\", bi: biMaxS, exp: MinDecimal128Exp - 1},\n\t{s: \"subnormal overflow 2\", remark: \"overflow\", bi: biNMaxS, exp: MinDecimal128Exp - 1},\n\n\t{s: \"clamped overflow 1\", remark: \"overflow\", bi: biMaxS, exp: MaxDecimal128Exp + 1},\n\t{s: \"clamped overflow 2\", remark: \"overflow\", bi: biNMaxS, exp: MaxDecimal128Exp + 1},\n\n\t{s: \"biMaxS+1 overflow\", remark: \"overflow\", bi: biOverflow, exp: MaxDecimal128Exp},\n\t{s: \"biNMaxS-1 overflow\", remark: \"overflow\", bi: biNOverflow, exp: MaxDecimal128Exp},\n\n\t{s: \"NaN\", h: 0x7c00000000000000, l: 0, remark: \"NaN\"},\n\t{s: \"Infinity\", h: 0x7800000000000000, l: 0, remark: \"Infinity\"},\n\t{s: \"-Infinity\", h: 0xf800000000000000, l: 0, remark: \"-Infinity\"},\n}\n\nfunc TestDecimal128_BigInt(t *testing.T) {\n\tfor _, c := range bigIntTestCases {\n\t\tt.Run(c.s, func(t *testing.T) {\n\t\t\tswitch c.remark {\n\t\t\tcase \"NaN\", \"Infinity\", \"-Infinity\":\n\t\t\t\td128 := NewDecimal128(c.h, c.l)\n\t\t\t\t_, _, err := d128.BigInt()\n\t\t\t\trequire.Error(t, err, \"case %s\", c.s)\n\t\t\tcase \"\":\n\t\t\t\td128 := NewDecimal128(c.h, c.l)\n\t\t\t\tbi, e, err := d128.BigInt()\n\t\t\t\trequire.NoError(t, err, \"case %s\", c.s)\n\t\t\t\trequire.Equal(t, 0, c.bi.Cmp(bi), \"case %s e:%s a:%s\", c.s, c.bi.String(), bi.String())\n\t\t\t\trequire.Equal(t, c.exp, e, \"case %s\", c.s, d128.String())\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestParseDecimal128FromBigInt(t *testing.T) {\n\tfor _, c := range bigIntTestCases {\n\t\tswitch c.remark {\n\t\tcase \"overflow\":\n\t\t\td128, ok := ParseDecimal128FromBigInt(c.bi, c.exp)\n\t\t\trequire.Equal(t, false, ok, \"case %s %s\", c.s, d128.String(), c.remark)\n\t\tcase \"\", \"rounding\", \"subnormal\", \"clamped\":\n\t\t\td128, ok := ParseDecimal128FromBigInt(c.bi, c.exp)\n\t\t\trequire.Equal(t, true, ok, \"case %s\", c.s)\n\t\t\trequire.Equal(t, c.s, d128.String(), \"case %s\", c.s)\n\n\t\t\trequire.Equal(t, c.h, d128.h, \"case %s\", c.s, d128.l)\n\t\t\trequire.Equal(t, c.l, d128.l, \"case %s\", c.s, d128.h)\n\t\t}\n\t}\n}\n\nfunc TestParseDecimal128(t *testing.T) {\n\tcases := make([]bigIntTestCase, 0, len(bigIntTestCases))\n\tcases = append(cases, bigIntTestCases...)\n\tcases = append(cases,\n\t\tbigIntTestCase{s: \"-0001231.453454000000565600000000E-21\", h: 0xafe6000003faa269, l: 0x81cfeceaabdb1800},\n\t\tbigIntTestCase{s: \"12345E+21\", h: 0x306a000000000000, l: 12345},\n\t\tbigIntTestCase{s: \"0.10000000000000000000000000000000000000000001\", remark: \"parse fail\"},\n\t\tbigIntTestCase{s: \".125e1\", h: 0x303c000000000000, l: 125},\n\t\tbigIntTestCase{s: \".125\", h: 0x303a000000000000, l: 125},\n\t\t// Test that parsing negative zero returns negative zero with a zero exponent.\n\t\tbigIntTestCase{s: \"-0\", h: 0xb040000000000000, l: 0},\n\t\t// Test that parsing negative zero with an in-range exponent returns negative zero and\n\t\t// preserves the specified exponent value.\n\t\tbigIntTestCase{s: \"-0E999\", h: 0xb80e000000000000, l: 0},\n\t\t// Test that parsing zero with an out-of-range positive exponent returns zero with the\n\t\t// maximum positive exponent (i.e. 0e+6111).\n\t\tbigIntTestCase{s: \"0E2000000000000\", h: 0x5ffe000000000000, l: 0},\n\t\t// Test that parsing zero with an out-of-range negative exponent returns zero with the\n\t\t// minimum negative exponent (i.e. 0e-6176).\n\t\tbigIntTestCase{s: \"-0E2000000000000\", h: 0xdffe000000000000, l: 0},\n\t\tbigIntTestCase{s: \"\", remark: \"parse fail\"})\n\n\tfor _, c := range cases {\n\t\tt.Run(c.s, func(t *testing.T) {\n\t\t\tswitch c.remark {\n\t\t\tcase \"overflow\", \"parse fail\":\n\t\t\t\t_, err := ParseDecimal128(c.s)\n\t\t\t\tassert.Error(t, err, \"ParseDecimal128(%q) should return an error\", c.s)\n\t\t\tdefault:\n\t\t\t\tgot, err := ParseDecimal128(c.s)\n\t\t\t\trequire.NoError(t, err, \"ParseDecimal128(%q) error\", c.s)\n\n\t\t\t\twant := Decimal128{h: c.h, l: c.l}\n\t\t\t\t// Decimal128 doesn't implement an equality function, so compare the expected\n\t\t\t\t// low/high uint64 values directly. Also print the string representation of each\n\t\t\t\t// number to make debugging failures easier.\n\t\t\t\tassert.Equal(t, want, got, \"ParseDecimal128(%q) = %s, want %s\", c.s, got, want)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestDecimal128_JSON(t *testing.T) {\n\tt.Run(\"roundTrip\", func(t *testing.T) {\n\t\tdecimal := NewDecimal128(0x3040000000000000, 12345)\n\t\tbytes, err := json.Marshal(decimal)\n\t\tassert.Nil(t, err, \"json.Marshal error: %v\", err)\n\t\tgot := NewDecimal128(0, 0)\n\t\terr = json.Unmarshal(bytes, &got)\n\t\tassert.Nil(t, err, \"json.Unmarshal error: %v\", err)\n\t\tassert.Equal(t, decimal.h, got.h, \"expected h: %v got: %v\", decimal.h, got.h)\n\t\tassert.Equal(t, decimal.l, got.l, \"expected l: %v got: %v\", decimal.l, got.l)\n\t})\n\tt.Run(\"unmarshal extendedJSON\", func(t *testing.T) {\n\t\twant := NewDecimal128(0x3040000000000000, 12345)\n\t\textJSON := fmt.Sprintf(`{\"$numberDecimal\": %q}`, want.String())\n\n\t\tgot := NewDecimal128(0, 0)\n\t\terr := json.Unmarshal([]byte(extJSON), &got)\n\t\tassert.Nil(t, err, \"json.Unmarshal error: %v\", err)\n\t\tassert.Equal(t, want.h, got.h, \"expected h: %v got: %v\", want.h, got.h)\n\t\tassert.Equal(t, want.l, got.l, \"expected l: %v got: %v\", want.l, got.l)\n\t})\n\tt.Run(\"unmarshal null\", func(t *testing.T) {\n\t\twant := NewDecimal128(0, 0)\n\t\textJSON := `null`\n\n\t\tgot := NewDecimal128(0, 0)\n\t\terr := json.Unmarshal([]byte(extJSON), &got)\n\t\tassert.Nil(t, err, \"json.Unmarshal error: %v\", err)\n\t\tassert.Equal(t, want.h, got.h, \"expected h: %v got: %v\", want.h, got.h)\n\t\tassert.Equal(t, want.l, got.l, \"expected l: %v got: %v\", want.l, got.l)\n\t})\n\tt.Run(\"unmarshal\", func(t *testing.T) {\n\t\tcases := make([]bigIntTestCase, 0, len(bigIntTestCases))\n\t\tcases = append(cases, bigIntTestCases...)\n\t\tcases = append(cases,\n\t\t\tbigIntTestCase{s: \"-0001231.453454000000565600000000E-21\", h: 0xafe6000003faa269, l: 0x81cfeceaabdb1800},\n\t\t\tbigIntTestCase{s: \"12345E+21\", h: 0x306a000000000000, l: 12345},\n\t\t\tbigIntTestCase{s: \"0.10000000000000000000000000000000000000000001\", remark: \"parse fail\"},\n\t\t\tbigIntTestCase{s: \".125e1\", h: 0x303c000000000000, l: 125},\n\t\t\tbigIntTestCase{s: \".125\", h: 0x303a000000000000, l: 125})\n\n\t\tfor _, c := range cases {\n\t\t\tt.Run(c.s, func(t *testing.T) {\n\t\t\t\tinput := fmt.Sprintf(`{\"foo\": %q}`, c.s)\n\t\t\t\tvar got map[string]Decimal128\n\t\t\t\terr := json.Unmarshal([]byte(input), &got)\n\n\t\t\t\tswitch c.remark {\n\t\t\t\tcase \"overflow\", \"parse fail\":\n\t\t\t\t\tassert.NotNil(t, err, \"expected Unmarshal error, got nil\")\n\t\t\t\tdefault:\n\t\t\t\t\tassert.Nil(t, err, \"Unmarshal error: %v\", err)\n\t\t\t\t\tgotDecimal := got[\"foo\"]\n\t\t\t\t\tassert.Equal(t, c.h, gotDecimal.h, \"expected h: %v got: %v\", c.h, gotDecimal.l)\n\t\t\t\t\tassert.Equal(t, c.l, gotDecimal.l, \"expected l: %v got: %v\", c.l, gotDecimal.h)\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t})\n}\n"
  },
  {
    "path": "bson/decode_value_fuzz_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2025-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bson\n\nimport (\n\t\"math\"\n\t\"strings\"\n\t\"testing\"\n)\n\nfunc FuzzDecodeValue(f *testing.F) {\n\t// Seed the fuzz corpus with all BSON values from the MarshalValue test\n\t// cases.\n\tfor _, tc := range marshalValueTestCases {\n\t\tf.Add(byte(tc.bsontype), tc.bytes)\n\t}\n\n\t// Also seed the fuzz corpus with special values that we want to test.\n\tvalues := []any{\n\t\t// int32, including max and min values.\n\t\tint32(0), int32(math.MaxInt32), int32(math.MinInt32),\n\t\t// int64, including max and min values.\n\t\tint64(0), int64(math.MaxInt64), int64(math.MinInt64),\n\t\t// string, including empty and large string.\n\t\t\"\", strings.Repeat(\"z\", 10_000),\n\t\t// map\n\t\tmap[string]any{\"nested\": []any{1, \"two\", map[string]any{\"three\": 3}}},\n\t\t// array\n\t\t[]any{1, 2, 3, \"four\"},\n\t}\n\n\tfor _, v := range values {\n\t\ttyp, b, err := MarshalValue(v)\n\t\tif err != nil {\n\t\t\tf.Fatal(err)\n\t\t}\n\t\tf.Add(byte(typ), b)\n\t}\n\n\tf.Fuzz(func(t *testing.T, bsonType byte, data []byte) {\n\t\tvar v any\n\t\tif err := UnmarshalValue(Type(bsonType), data, &v); err != nil {\n\t\t\treturn\n\t\t}\n\n\t\t// There is no value encoder for Go \"nil\" (nil values handled\n\t\t// differently by each type encoder), so skip anything that unmarshals\n\t\t// to \"nil\". It's not clear if MarshalValue should support \"nil\", but\n\t\t// for now we skip it.\n\t\tif v == nil {\n\t\t\tt.Logf(\"data unmarshaled to nil: %v\", data)\n\t\t\treturn\n\t\t}\n\n\t\ttyp, encoded, err := MarshalValue(v)\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"failed to marshal: %v\", err)\n\t\t}\n\n\t\tvar v2 any\n\t\tif err := UnmarshalValue(typ, encoded, &v2); err != nil {\n\t\t\tt.Fatalf(\"failed to unmarshal: %v\", err)\n\t\t}\n\t})\n}\n"
  },
  {
    "path": "bson/decoder.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bson\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"reflect\"\n\t\"sync\"\n)\n\n// ErrDecodeToNil is the error returned when trying to decode to a nil value\nvar ErrDecodeToNil = errors.New(\"cannot Decode to nil value\")\n\n// This pool is used to keep the allocations of Decoders down. This is only used for the Marshal*\n// methods and is not consumable from outside of this package. The Decoders retrieved from this pool\n// must have both Reset and SetRegistry called on them.\nvar decPool = sync.Pool{\n\tNew: func() any {\n\t\treturn new(Decoder)\n\t},\n}\n\n// A Decoder reads and decodes BSON documents from a stream. It reads from a ValueReader as\n// the source of BSON data.\ntype Decoder struct {\n\tdc DecodeContext\n\tvr ValueReader\n}\n\n// NewDecoder returns a new decoder that reads from vr.\nfunc NewDecoder(vr ValueReader) *Decoder {\n\treturn &Decoder{\n\t\tdc: DecodeContext{Registry: defaultRegistry},\n\t\tvr: vr,\n\t}\n}\n\n// Decode reads the next BSON document from the stream and decodes it into the\n// value pointed to by val.\n//\n// See [Unmarshal] for details about BSON unmarshaling behavior.\nfunc (d *Decoder) Decode(val any) error {\n\tif unmarshaler, ok := val.(Unmarshaler); ok {\n\t\t// TODO(skriptble): Reuse a []byte here and use the AppendDocumentBytes method.\n\t\tbuf, err := copyDocumentToBytes(d.vr)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn unmarshaler.UnmarshalBSON(buf)\n\t}\n\n\trval := reflect.ValueOf(val)\n\tswitch rval.Kind() {\n\tcase reflect.Ptr:\n\t\tif rval.IsNil() {\n\t\t\treturn ErrDecodeToNil\n\t\t}\n\t\trval = rval.Elem()\n\tcase reflect.Map:\n\t\tif rval.IsNil() {\n\t\t\treturn ErrDecodeToNil\n\t\t}\n\tdefault:\n\t\treturn fmt.Errorf(\"argument to Decode must be a pointer or a map, but got %v\", rval)\n\t}\n\tdecoder, err := d.dc.LookupDecoder(rval.Type())\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn decoder.DecodeValue(d.dc, d.vr, rval)\n}\n\n// Reset will reset the state of the decoder, using the same *DecodeContext used in\n// the original construction but using vr for reading.\nfunc (d *Decoder) Reset(vr ValueReader) {\n\td.vr = vr\n}\n\n// SetRegistry replaces the current registry of the decoder with r.\nfunc (d *Decoder) SetRegistry(r *Registry) {\n\td.dc.Registry = r\n}\n\n// DefaultDocumentM causes the Decoder to always unmarshal documents into the bson.M type. This\n// behavior is restricted to data typed as \"any\" or \"map[string]any\".\nfunc (d *Decoder) DefaultDocumentM() {\n\td.dc.defaultDocumentType = reflect.TypeOf(M{})\n}\n\n// DefaultDocumentMap causes the Decoder to always unmarshal documents into the\n// map[string]any type. This behavior is restricted to data typed as \"any\" or\n// \"map[string]any\".\nfunc (d *Decoder) DefaultDocumentMap() {\n\td.dc.defaultDocumentType = reflect.TypeOf(map[string]any{})\n}\n\n// AllowTruncatingDoubles causes the Decoder to truncate the fractional part of BSON \"double\" values\n// when attempting to unmarshal them into a Go integer (int, int8, int16, int32, or int64) struct\n// field. The truncation logic does not apply to BSON \"decimal128\" values.\nfunc (d *Decoder) AllowTruncatingDoubles() {\n\td.dc.truncate = true\n}\n\n// BinaryAsSlice causes the Decoder to unmarshal BSON binary field values that are the \"Generic\" or\n// \"Old\" BSON binary subtype as a Go byte slice instead of a bson.Binary.\nfunc (d *Decoder) BinaryAsSlice() {\n\td.dc.binaryAsSlice = true\n}\n\n// ObjectIDAsHexString causes the Decoder to decode object IDs to their hex representation.\nfunc (d *Decoder) ObjectIDAsHexString() {\n\td.dc.objectIDAsHexString = true\n}\n\n// UseJSONStructTags causes the Decoder to fall back to using the \"json\" struct tag if a \"bson\"\n// struct tag is not specified.\nfunc (d *Decoder) UseJSONStructTags() {\n\td.dc.useJSONStructTags = true\n}\n\n// UseLocalTimeZone causes the Decoder to unmarshal time.Time values in the local timezone instead\n// of the UTC timezone.\nfunc (d *Decoder) UseLocalTimeZone() {\n\td.dc.useLocalTimeZone = true\n}\n\n// ZeroMaps causes the Decoder to delete any existing values from Go maps in the destination value\n// passed to Decode before unmarshaling BSON documents into them.\nfunc (d *Decoder) ZeroMaps() {\n\td.dc.zeroMaps = true\n}\n\n// ZeroStructs causes the Decoder to delete any existing values from Go structs in the destination\n// value passed to Decode before unmarshaling BSON documents into them.\nfunc (d *Decoder) ZeroStructs() {\n\td.dc.zeroStructs = true\n}\n"
  },
  {
    "path": "bson/decoder_example_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2023-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bson_test\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n)\n\nfunc ExampleDecoder() {\n\t// Marshal a BSON document that contains the name, SKU, and price (in cents)\n\t// of a product.\n\tdoc := bson.D{\n\t\t{Key: \"name\", Value: \"Cereal Rounds\"},\n\t\t{Key: \"sku\", Value: \"AB12345\"},\n\t\t{Key: \"price_cents\", Value: 399},\n\t}\n\tdata, err := bson.Marshal(doc)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t// Create a Decoder that reads the marshaled BSON document and use it to\n\t// unmarshal the document into a Product struct.\n\tdecoder := bson.NewDecoder(bson.NewDocumentReader(bytes.NewReader(data)))\n\n\ttype Product struct {\n\t\tName  string `bson:\"name\"`\n\t\tSKU   string `bson:\"sku\"`\n\t\tPrice int64  `bson:\"price_cents\"`\n\t}\n\n\tvar res Product\n\terr = decoder.Decode(&res)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Printf(\"%+v\\n\", res)\n\t// Output: {Name:Cereal Rounds SKU:AB12345 Price:399}\n}\n\nfunc ExampleDecoder_DefaultDocumentM() {\n\t// Marshal a BSON document that contains a city name and a nested document\n\t// with various city properties.\n\tdoc := bson.D{\n\t\t{Key: \"name\", Value: \"New York\"},\n\t\t{Key: \"properties\", Value: bson.D{\n\t\t\t{Key: \"state\", Value: \"NY\"},\n\t\t\t{Key: \"population\", Value: 8_804_190},\n\t\t\t{Key: \"elevation\", Value: 10},\n\t\t}},\n\t}\n\tdata, err := bson.Marshal(doc)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t// Create a Decoder that reads the marshaled BSON document and use it to unmarshal the document\n\t// into a City struct.\n\tdecoder := bson.NewDecoder(bson.NewDocumentReader(bytes.NewReader(data)))\n\n\ttype City struct {\n\t\tName       string `bson:\"name\"`\n\t\tProperties any    `bson:\"properties\"`\n\t}\n\n\t// Configure the Decoder to default to decoding BSON documents as the M\n\t// type if the decode destination has no type information. The Properties\n\t// field in the City struct will be decoded as a \"M\" (i.e. map) instead\n\t// of the default \"D\".\n\tdecoder.DefaultDocumentM()\n\n\tvar res City\n\terr = decoder.Decode(&res)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tdata, err = json.Marshal(res)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tfmt.Printf(\"%+v\\n\", string(data))\n\t// Output: {\"Name\":\"New York\",\"Properties\":{\"elevation\":10,\"population\":8804190,\"state\":\"NY\"}}\n}\n\nfunc ExampleDecoder_DefaultDocumentMap() {\n\t// Marshal a BSON document that contains a city name and a nested document\n\t// with various city properties.\n\tdoc := bson.D{\n\t\t{Key: \"name\", Value: \"New York\"},\n\t\t{Key: \"properties\", Value: bson.D{\n\t\t\t{Key: \"state\", Value: \"NY\"},\n\t\t\t{Key: \"population\", Value: 8_804_190},\n\t\t\t{Key: \"elevation\", Value: 10},\n\t\t}},\n\t}\n\tdata, err := bson.Marshal(doc)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t// Create a Decoder that reads the marshaled BSON document and use it to unmarshal the document\n\t// into a City struct.\n\tdecoder := bson.NewDecoder(bson.NewDocumentReader(bytes.NewReader(data)))\n\n\ttype City struct {\n\t\tName       string `bson:\"name\"`\n\t\tProperties any    `bson:\"properties\"`\n\t}\n\n\t// Configure the Decoder to default to decoding BSON documents as a\n\t// map[string]any type if the decode destination has no type information. The\n\t// Properties field in the City struct will be decoded as map[string]any\n\t// instead of the default \"D\".\n\tdecoder.DefaultDocumentMap()\n\n\tvar res City\n\terr = decoder.Decode(&res)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tdata, err = json.Marshal(res)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tfmt.Printf(\"%+v\\n\", string(data))\n\t// Output: {\"Name\":\"New York\",\"Properties\":{\"elevation\":10,\"population\":8804190,\"state\":\"NY\"}}\n}\n\nfunc ExampleDecoder_UseJSONStructTags() {\n\t// Marshal a BSON document that contains the name, SKU, and price (in cents)\n\t// of a product.\n\tdoc := bson.D{\n\t\t{Key: \"name\", Value: \"Cereal Rounds\"},\n\t\t{Key: \"sku\", Value: \"AB12345\"},\n\t\t{Key: \"price_cents\", Value: 399},\n\t}\n\tdata, err := bson.Marshal(doc)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t// Create a Decoder that reads the marshaled BSON document and use it to\n\t// unmarshal the document into a Product struct.\n\tdecoder := bson.NewDecoder(bson.NewDocumentReader(bytes.NewReader(data)))\n\n\ttype Product struct {\n\t\tName  string `json:\"name\"`\n\t\tSKU   string `json:\"sku\"`\n\t\tPrice int64  `json:\"price_cents\"`\n\t}\n\n\t// Configure the Decoder to use \"json\" struct tags when decoding if \"bson\"\n\t// struct tags are not present.\n\tdecoder.UseJSONStructTags()\n\n\tvar res Product\n\terr = decoder.Decode(&res)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Printf(\"%+v\\n\", res)\n\t// Output: {Name:Cereal Rounds SKU:AB12345 Price:399}\n}\n\nfunc ExampleDecoder_extendedJSON() {\n\t// Define an Extended JSON document that contains the name, SKU, and price\n\t// (in cents) of a product.\n\tdata := []byte(`{\"name\":\"Cereal Rounds\",\"sku\":\"AB12345\",\"price_cents\":{\"$numberLong\":\"399\"}}`)\n\n\t// Create a Decoder that reads the Extended JSON document and use it to\n\t// unmarshal the document into a Product struct.\n\tvr, err := bson.NewExtJSONValueReader(bytes.NewReader(data), true)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tdecoder := bson.NewDecoder(vr)\n\n\ttype Product struct {\n\t\tName  string `bson:\"name\"`\n\t\tSKU   string `bson:\"sku\"`\n\t\tPrice int64  `bson:\"price_cents\"`\n\t}\n\n\tvar res Product\n\terr = decoder.Decode(&res)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Printf(\"%+v\\n\", res)\n\t// Output: {Name:Cereal Rounds SKU:AB12345 Price:399}\n}\n\nfunc ExampleDecoder_multipleExtendedJSONDocuments() {\n\t// Define a newline-separated sequence of Extended JSON documents that\n\t// contain X,Y coordinates.\n\tdata := []byte(`\n{\"x\":{\"$numberInt\":\"0\"},\"y\":{\"$numberInt\":\"0\"}}\n{\"x\":{\"$numberInt\":\"1\"},\"y\":{\"$numberInt\":\"1\"}}\n{\"x\":{\"$numberInt\":\"2\"},\"y\":{\"$numberInt\":\"2\"}}\n{\"x\":{\"$numberInt\":\"3\"},\"y\":{\"$numberInt\":\"3\"}}\n{\"x\":{\"$numberInt\":\"4\"},\"y\":{\"$numberInt\":\"4\"}}\n`)\n\n\t// Create a Decoder that reads the Extended JSON documents and use it to\n\t// unmarshal the documents Coordinate structs.\n\tvr, err := bson.NewExtJSONValueReader(bytes.NewReader(data), true)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tdecoder := bson.NewDecoder(vr)\n\n\ttype Coordinate struct {\n\t\tX int\n\t\tY int\n\t}\n\n\t// Read and unmarshal each Extended JSON document from the sequence. If\n\t// Decode returns error io.EOF, that means the Decoder has reached the end\n\t// of the input, so break the loop.\n\tfor {\n\t\tvar res Coordinate\n\t\terr = decoder.Decode(&res)\n\t\tif errors.Is(err, io.EOF) {\n\t\t\tbreak\n\t\t}\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\n\t\tfmt.Printf(\"%+v\\n\", res)\n\t}\n\t// Output:\n\t// {X:0 Y:0}\n\t// {X:1 Y:1}\n\t// {X:2 Y:2}\n\t// {X:3 Y:3}\n\t// {X:4 Y:4}\n}\n"
  },
  {
    "path": "bson/decoder_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bson\n\nimport (\n\t\"bytes\"\n\t\"errors\"\n\t\"reflect\"\n\t\"testing\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/require\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n)\n\nfunc TestDecodeValue(t *testing.T) {\n\tt.Parallel()\n\n\tfor _, tc := range unmarshalingTestCases() {\n\t\ttc := tc\n\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tgot := reflect.New(tc.sType).Elem()\n\t\t\tvr := NewDocumentReader(bytes.NewReader(tc.data))\n\t\t\treg := defaultRegistry\n\t\t\tdecoder, err := reg.LookupDecoder(reflect.TypeOf(got))\n\t\t\tnoerr(t, err)\n\t\t\terr = decoder.DecodeValue(DecodeContext{Registry: reg}, vr, got)\n\t\t\tnoerr(t, err)\n\t\t\tassert.Equal(t, tc.want, got.Addr().Interface(), \"Results do not match.\")\n\t\t})\n\t}\n}\n\nfunc TestDecodingInterfaces(t *testing.T) {\n\tt.Parallel()\n\n\ttype testCase struct {\n\t\tname string\n\t\tstub func() ([]byte, any, func(*testing.T))\n\t}\n\ttestCases := []testCase{\n\t\t{\n\t\t\tname: \"struct with interface containing a concrete value\",\n\t\t\tstub: func() ([]byte, any, func(*testing.T)) {\n\t\t\t\ttype testStruct struct {\n\t\t\t\t\tValue any\n\t\t\t\t}\n\t\t\t\tvar value string\n\n\t\t\t\tdata := docToBytes(struct {\n\t\t\t\t\tValue string\n\t\t\t\t}{\n\t\t\t\t\tValue: \"foo\",\n\t\t\t\t})\n\n\t\t\t\treceiver := testStruct{&value}\n\n\t\t\t\tcheck := func(t *testing.T) {\n\t\t\t\t\tt.Helper()\n\t\t\t\t\tassert.Equal(t, \"foo\", value)\n\t\t\t\t}\n\n\t\t\t\treturn data, &receiver, check\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"struct with interface containing a struct\",\n\t\t\tstub: func() ([]byte, any, func(*testing.T)) {\n\t\t\t\ttype demo struct {\n\t\t\t\t\tData string\n\t\t\t\t}\n\n\t\t\t\ttype testStruct struct {\n\t\t\t\t\tValue any\n\t\t\t\t}\n\t\t\t\tvar value demo\n\n\t\t\t\tdata := docToBytes(struct {\n\t\t\t\t\tValue demo\n\t\t\t\t}{\n\t\t\t\t\tValue: demo{\"foo\"},\n\t\t\t\t})\n\n\t\t\t\treceiver := testStruct{&value}\n\n\t\t\t\tcheck := func(t *testing.T) {\n\t\t\t\t\tt.Helper()\n\t\t\t\t\tassert.Equal(t, \"foo\", value.Data)\n\t\t\t\t}\n\n\t\t\t\treturn data, &receiver, check\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"struct with interface containing a slice\",\n\t\t\tstub: func() ([]byte, any, func(*testing.T)) {\n\t\t\t\ttype testStruct struct {\n\t\t\t\t\tValues any\n\t\t\t\t}\n\t\t\t\tvar values []string\n\n\t\t\t\tdata := docToBytes(struct {\n\t\t\t\t\tValues []string\n\t\t\t\t}{\n\t\t\t\t\tValues: []string{\"foo\", \"bar\"},\n\t\t\t\t})\n\n\t\t\t\treceiver := testStruct{&values}\n\n\t\t\t\tcheck := func(t *testing.T) {\n\t\t\t\t\tt.Helper()\n\t\t\t\t\tassert.Equal(t, []string{\"foo\", \"bar\"}, values)\n\t\t\t\t}\n\n\t\t\t\treturn data, &receiver, check\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"struct with interface containing an array\",\n\t\t\tstub: func() ([]byte, any, func(*testing.T)) {\n\t\t\t\ttype testStruct struct {\n\t\t\t\t\tValues any\n\t\t\t\t}\n\t\t\t\tvar values [2]string\n\n\t\t\t\tdata := docToBytes(struct {\n\t\t\t\t\tValues []string\n\t\t\t\t}{\n\t\t\t\t\tValues: []string{\"foo\", \"bar\"},\n\t\t\t\t})\n\n\t\t\t\treceiver := testStruct{&values}\n\n\t\t\t\tcheck := func(t *testing.T) {\n\t\t\t\t\tt.Helper()\n\t\t\t\t\tassert.Equal(t, [2]string{\"foo\", \"bar\"}, values)\n\t\t\t\t}\n\n\t\t\t\treturn data, &receiver, check\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"struct with interface array containing concrete values\",\n\t\t\tstub: func() ([]byte, any, func(*testing.T)) {\n\t\t\t\ttype testStruct struct {\n\t\t\t\t\tValues [3]any\n\t\t\t\t}\n\t\t\t\tvar str string\n\t\t\t\tvar i, j int\n\n\t\t\t\tdata := docToBytes(struct {\n\t\t\t\t\tValues []any\n\t\t\t\t}{\n\t\t\t\t\tValues: []any{\"foo\", 42, nil},\n\t\t\t\t})\n\n\t\t\t\treceiver := testStruct{[3]any{&str, &i, &j}}\n\n\t\t\t\tcheck := func(t *testing.T) {\n\t\t\t\t\tt.Helper()\n\t\t\t\t\tassert.Equal(t, \"foo\", str)\n\t\t\t\t\tassert.Equal(t, 42, i)\n\t\t\t\t\tassert.Equal(t, 0, j)\n\t\t\t\t\tassert.Equal(t, testStruct{[3]any{&str, &i, nil}}, receiver)\n\t\t\t\t}\n\n\t\t\t\treturn data, &receiver, check\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"overwriting prepopulated slice\",\n\t\t\tstub: func() ([]byte, any, func(*testing.T)) {\n\t\t\t\ttype testStruct struct {\n\t\t\t\t\tValues []any\n\t\t\t\t}\n\n\t\t\t\tdata := docToBytes(struct {\n\t\t\t\t\tValues []any\n\t\t\t\t}{\n\t\t\t\t\tValues: []any{1, 2, 3},\n\t\t\t\t})\n\n\t\t\t\treceiver := testStruct{[]any{7, 8}}\n\n\t\t\t\tcheck := func(t *testing.T) {\n\t\t\t\t\tt.Helper()\n\t\t\t\t\tassert.Equal(t, testStruct{[]any{1, 2, int32(3)}}, receiver)\n\t\t\t\t}\n\n\t\t\t\treturn data, &receiver, check\n\t\t\t},\n\t\t},\n\t}\n\tfor _, tc := range testCases {\n\t\ttc := tc\n\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tdata, receiver, check := tc.stub()\n\t\t\tgot := reflect.ValueOf(receiver).Elem()\n\t\t\tvr := NewDocumentReader(bytes.NewReader(data))\n\t\t\treg := defaultRegistry\n\t\t\tdecoder, err := reg.LookupDecoder(got.Type())\n\t\t\tnoerr(t, err)\n\t\t\terr = decoder.DecodeValue(DecodeContext{Registry: reg}, vr, got)\n\t\t\tnoerr(t, err)\n\t\t\tcheck(t)\n\t\t})\n\t}\n}\n\nfunc TestDecoder(t *testing.T) {\n\tt.Parallel()\n\n\tt.Run(\"Decode\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tt.Run(\"basic\", func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tfor _, tc := range unmarshalingTestCases() {\n\t\t\t\ttc := tc\n\n\t\t\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\t\t\tt.Parallel()\n\n\t\t\t\t\tgot := reflect.New(tc.sType).Interface()\n\t\t\t\t\tvr := NewDocumentReader(bytes.NewReader(tc.data))\n\t\t\t\t\tdec := NewDecoder(vr)\n\t\t\t\t\terr := dec.Decode(got)\n\t\t\t\t\tnoerr(t, err)\n\t\t\t\t\tassert.Equal(t, tc.want, got, \"Results do not match.\")\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\t\tt.Run(\"stream\", func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tvar buf bytes.Buffer\n\t\t\tvr := NewDocumentReader(&buf)\n\t\t\tdec := NewDecoder(vr)\n\t\t\tfor _, tc := range unmarshalingTestCases() {\n\t\t\t\ttc := tc\n\n\t\t\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\t\t\tbuf.Write(tc.data)\n\t\t\t\t\tgot := reflect.New(tc.sType).Interface()\n\t\t\t\t\terr := dec.Decode(got)\n\t\t\t\t\tnoerr(t, err)\n\t\t\t\t\tassert.Equal(t, tc.want, got, \"Results do not match.\")\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\t\tt.Run(\"lookup error\", func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\ttype certainlydoesntexistelsewhereihope func(string, string) string\n\t\t\t// Avoid unused code lint error.\n\t\t\t_ = certainlydoesntexistelsewhereihope(func(string, string) string { return \"\" })\n\n\t\t\tcdeih := func(string, string) string { return \"certainlydoesntexistelsewhereihope\" }\n\t\t\tdec := NewDecoder(NewDocumentReader(bytes.NewReader([]byte{})))\n\t\t\twant := errNoDecoder{Type: reflect.TypeOf(cdeih)}\n\t\t\tgot := dec.Decode(&cdeih)\n\t\t\tassert.Equal(t, want, got, \"Received unexpected error.\")\n\t\t})\n\t\tt.Run(\"Unmarshaler\", func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\ttestCases := []struct {\n\t\t\t\tname    string\n\t\t\t\terr     error\n\t\t\t\tvr      ValueReader\n\t\t\t\tinvoked bool\n\t\t\t}{\n\t\t\t\t{\n\t\t\t\t\t\"error\",\n\t\t\t\t\terrors.New(\"Unmarshaler error\"),\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeEmbeddedDocument, Err: ErrEOD, ErrAfter: readElement},\n\t\t\t\t\ttrue,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"copy error\",\n\t\t\t\t\terrors.New(\"copy error\"),\n\t\t\t\t\t&valueReaderWriter{Err: errors.New(\"copy error\"), ErrAfter: readDocument},\n\t\t\t\t\tfalse,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"success\",\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeEmbeddedDocument, Err: ErrEOD, ErrAfter: readElement},\n\t\t\t\t\ttrue,\n\t\t\t\t},\n\t\t\t}\n\n\t\t\tfor _, tc := range testCases {\n\t\t\t\ttc := tc\n\n\t\t\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\t\t\tt.Parallel()\n\n\t\t\t\t\tunmarshaler := &testUnmarshaler{Err: tc.err}\n\t\t\t\t\tdec := NewDecoder(tc.vr)\n\t\t\t\t\tgot := dec.Decode(unmarshaler)\n\t\t\t\t\twant := tc.err\n\t\t\t\t\tif !assert.CompareErrors(got, want) {\n\t\t\t\t\t\tt.Errorf(\"Did not receive expected error. got %v; want %v\", got, want)\n\t\t\t\t\t}\n\t\t\t\t\tif unmarshaler.Invoked != tc.invoked {\n\t\t\t\t\t\tif tc.invoked {\n\t\t\t\t\t\t\tt.Error(\"Expected to have UnmarshalBSON invoked, but it wasn't.\")\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tt.Error(\"Expected UnmarshalBSON to not be invoked, but it was.\")\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t}\n\n\t\t\tt.Run(\"Unmarshaler/success ValueReader\", func(t *testing.T) {\n\t\t\t\tt.Parallel()\n\n\t\t\t\twant := bsoncore.BuildDocument(nil, bsoncore.AppendDoubleElement(nil, \"pi\", 3.14159))\n\t\t\t\tunmarshaler := &testUnmarshaler{}\n\t\t\t\tvr := NewDocumentReader(bytes.NewReader(want))\n\t\t\t\tdec := NewDecoder(vr)\n\t\t\t\terr := dec.Decode(unmarshaler)\n\t\t\t\tnoerr(t, err)\n\t\t\t\tgot := unmarshaler.Val\n\t\t\t\tif !bytes.Equal(got, want) {\n\t\t\t\t\tt.Errorf(\"Did not unmarshal properly. got %v; want %v\", got, want)\n\t\t\t\t}\n\t\t\t})\n\t\t})\n\t})\n\tt.Run(\"NewDecoder\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tt.Run(\"success\", func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tgot := NewDecoder(NewDocumentReader(bytes.NewReader([]byte{})))\n\t\t\tif got == nil {\n\t\t\t\tt.Errorf(\"Was expecting a non-nil Decoder, but got <nil>\")\n\t\t\t}\n\t\t})\n\t})\n\tt.Run(\"NewDecoderWithContext\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tt.Run(\"success\", func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tgot := NewDecoder(NewDocumentReader(bytes.NewReader([]byte{})))\n\t\t\tif got == nil {\n\t\t\t\tt.Errorf(\"Was expecting a non-nil Decoder, but got <nil>\")\n\t\t\t}\n\t\t})\n\t})\n\tt.Run(\"Decode doesn't zero struct\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\ttype foo struct {\n\t\t\tItem  string\n\t\t\tQty   int\n\t\t\tBonus int\n\t\t}\n\t\tvar got foo\n\t\tgot.Item = \"apple\"\n\t\tgot.Bonus = 2\n\t\tdata := docToBytes(D{{\"item\", \"canvas\"}, {\"qty\", 4}})\n\t\tvr := NewDocumentReader(bytes.NewReader(data))\n\t\tdec := NewDecoder(vr)\n\t\terr := dec.Decode(&got)\n\t\tnoerr(t, err)\n\t\twant := foo{Item: \"canvas\", Qty: 4, Bonus: 2}\n\t\tassert.Equal(t, want, got, \"Results do not match.\")\n\t})\n\tt.Run(\"Reset\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tvr1, vr2 := NewDocumentReader(bytes.NewReader([]byte{})), NewDocumentReader(bytes.NewReader([]byte{}))\n\t\tdec := NewDecoder(vr1)\n\t\tif dec.vr != vr1 {\n\t\t\tt.Errorf(\"Decoder should use the value reader provided. got %v; want %v\", dec.vr, vr1)\n\t\t}\n\t\tdec.Reset(vr2)\n\t\tif dec.vr != vr2 {\n\t\t\tt.Errorf(\"Decoder should use the value reader provided. got %v; want %v\", dec.vr, vr2)\n\t\t}\n\t})\n\tt.Run(\"SetRegistry\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tr1, r2 := defaultRegistry, NewRegistry()\n\t\tdc1 := DecodeContext{Registry: r1}\n\t\tdc2 := DecodeContext{Registry: r2}\n\t\tdec := NewDecoder(NewDocumentReader(bytes.NewReader([]byte{})))\n\t\tif !reflect.DeepEqual(dec.dc, dc1) {\n\t\t\tt.Errorf(\"Decoder should use the Registry provided. got %v; want %v\", dec.dc, dc1)\n\t\t}\n\t\tdec.SetRegistry(r2)\n\t\tif !reflect.DeepEqual(dec.dc, dc2) {\n\t\t\tt.Errorf(\"Decoder should use the Registry provided. got %v; want %v\", dec.dc, dc2)\n\t\t}\n\t})\n\tt.Run(\"DecodeToNil\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tdata := docToBytes(D{{\"item\", \"canvas\"}, {\"qty\", 4}})\n\t\tvr := NewDocumentReader(bytes.NewReader(data))\n\t\tdec := NewDecoder(vr)\n\n\t\tvar got *D\n\t\terr := dec.Decode(got)\n\t\tif !errors.Is(err, ErrDecodeToNil) {\n\t\t\tt.Fatalf(\"Decode error mismatch; expected %v, got %v\", ErrDecodeToNil, err)\n\t\t}\n\t})\n}\n\ntype testUnmarshaler struct {\n\tInvoked bool\n\tVal     []byte\n\tErr     error\n}\n\nfunc (tu *testUnmarshaler) UnmarshalBSON(d []byte) error {\n\ttu.Invoked = true\n\ttu.Val = d\n\treturn tu.Err\n}\n\nfunc TestDecoderConfiguration(t *testing.T) {\n\ttype truncateDoublesTest struct {\n\t\tMyInt    int\n\t\tMyInt8   int8\n\t\tMyInt16  int16\n\t\tMyInt32  int32\n\t\tMyInt64  int64\n\t\tMyUint   uint\n\t\tMyUint8  uint8\n\t\tMyUint16 uint16\n\t\tMyUint32 uint32\n\t\tMyUint64 uint64\n\t}\n\n\ttype objectIDTest struct {\n\t\tID string\n\t}\n\n\ttype jsonStructTest struct {\n\t\tStructFieldName string `json:\"jsonFieldName\"`\n\t}\n\n\ttype localTimeZoneTest struct {\n\t\tMyTime time.Time\n\t}\n\n\ttype zeroMapsTest struct {\n\t\tMyMap map[string]string\n\t}\n\n\ttype zeroStructsTest struct {\n\t\tMyString string\n\t\tMyInt    int\n\t}\n\n\ttestCases := []struct {\n\t\tdescription string\n\t\tconfigure   func(*Decoder)\n\t\tinput       []byte\n\t\tdecodeInto  func() any\n\t\twant        any\n\t}{\n\t\t// Test that AllowTruncatingDoubles causes the Decoder to unmarshal BSON doubles with\n\t\t// fractional parts into Go integer types by truncating the fractional part.\n\t\t{\n\t\t\tdescription: \"AllowTruncatingDoubles\",\n\t\t\tconfigure: func(dec *Decoder) {\n\t\t\t\tdec.AllowTruncatingDoubles()\n\t\t\t},\n\t\t\tinput: bsoncore.NewDocumentBuilder().\n\t\t\t\tAppendDouble(\"myInt\", 1.999).\n\t\t\t\tAppendDouble(\"myInt8\", 1.999).\n\t\t\t\tAppendDouble(\"myInt16\", 1.999).\n\t\t\t\tAppendDouble(\"myInt32\", 1.999).\n\t\t\t\tAppendDouble(\"myInt64\", 1.999).\n\t\t\t\tAppendDouble(\"myUint\", 1.999).\n\t\t\t\tAppendDouble(\"myUint8\", 1.999).\n\t\t\t\tAppendDouble(\"myUint16\", 1.999).\n\t\t\t\tAppendDouble(\"myUint32\", 1.999).\n\t\t\t\tAppendDouble(\"myUint64\", 1.999).\n\t\t\t\tBuild(),\n\t\t\tdecodeInto: func() any { return &truncateDoublesTest{} },\n\t\t\twant: &truncateDoublesTest{\n\t\t\t\tMyInt:    1,\n\t\t\t\tMyInt8:   1,\n\t\t\t\tMyInt16:  1,\n\t\t\t\tMyInt32:  1,\n\t\t\t\tMyInt64:  1,\n\t\t\t\tMyUint:   1,\n\t\t\t\tMyUint8:  1,\n\t\t\t\tMyUint16: 1,\n\t\t\t\tMyUint32: 1,\n\t\t\t\tMyUint64: 1,\n\t\t\t},\n\t\t},\n\t\t// Test that BinaryAsSlice causes the Decoder to unmarshal BSON binary fields into Go byte\n\t\t// slices when there is no type information (e.g when unmarshaling into a bson.D).\n\t\t{\n\t\t\tdescription: \"BinaryAsSlice\",\n\t\t\tconfigure: func(dec *Decoder) {\n\t\t\t\tdec.BinaryAsSlice()\n\t\t\t},\n\t\t\tinput: bsoncore.NewDocumentBuilder().\n\t\t\t\tAppendBinary(\"myBinary\", TypeBinaryGeneric, []byte{}).\n\t\t\t\tBuild(),\n\t\t\tdecodeInto: func() any { return &D{} },\n\t\t\twant:       &D{{Key: \"myBinary\", Value: []byte{}}},\n\t\t},\n\t\t// Test that the default decoder always decodes BSON documents into bson.D values,\n\t\t// independent of the top-level Go value type.\n\t\t{\n\t\t\tdescription: \"DocumentD nested by default\",\n\t\t\tconfigure:   func(_ *Decoder) {},\n\t\t\tinput: bsoncore.NewDocumentBuilder().\n\t\t\t\tAppendDocument(\"myDocument\", bsoncore.NewDocumentBuilder().\n\t\t\t\t\tAppendString(\"myString\", \"test value\").\n\t\t\t\t\tBuild()).\n\t\t\t\tBuild(),\n\t\t\tdecodeInto: func() any { return M{} },\n\t\t\twant: M{\n\t\t\t\t\"myDocument\": D{{Key: \"myString\", Value: \"test value\"}},\n\t\t\t},\n\t\t},\n\t\t// Test that DefaultDocumentM always decodes BSON documents into bson.M values,\n\t\t// independent of the top-level Go value type.\n\t\t{\n\t\t\tdescription: \"DefaultDocumentM nested\",\n\t\t\tconfigure: func(dec *Decoder) {\n\t\t\t\tdec.DefaultDocumentM()\n\t\t\t},\n\t\t\tinput: bsoncore.NewDocumentBuilder().\n\t\t\t\tAppendDocument(\"myDocument\", bsoncore.NewDocumentBuilder().\n\t\t\t\t\tAppendString(\"myString\", \"test value\").\n\t\t\t\t\tBuild()).\n\t\t\t\tBuild(),\n\t\t\tdecodeInto: func() any { return &D{} },\n\t\t\twant: &D{\n\t\t\t\t{Key: \"myDocument\", Value: M{\"myString\": \"test value\"}},\n\t\t\t},\n\t\t},\n\t\t// Test that DefaultDocumentMap always decodes BSON documents into\n\t\t// map[string]any values, independent of the top-level Go value type.\n\t\t{\n\t\t\tdescription: \"DefaultDocumentMap nested\",\n\t\t\tconfigure: func(dec *Decoder) {\n\t\t\t\tdec.DefaultDocumentMap()\n\t\t\t},\n\t\t\tinput: bsoncore.NewDocumentBuilder().\n\t\t\t\tAppendDocument(\"myDocument\", bsoncore.NewDocumentBuilder().\n\t\t\t\t\tAppendString(\"myString\", \"test value\").\n\t\t\t\t\tBuild()).\n\t\t\t\tBuild(),\n\t\t\tdecodeInto: func() any { return &D{} },\n\t\t\twant: &D{\n\t\t\t\t{Key: \"myDocument\", Value: map[string]any{\"myString\": \"test value\"}},\n\t\t\t},\n\t\t},\n\t\t// Test that ObjectIDAsHexString causes the Decoder to decode object ID to hex.\n\t\t{\n\t\t\tdescription: \"ObjectIDAsHexString\",\n\t\t\tconfigure: func(dec *Decoder) {\n\t\t\t\tdec.ObjectIDAsHexString()\n\t\t\t},\n\t\t\tinput: bsoncore.NewDocumentBuilder().\n\t\t\t\tAppendObjectID(\"id\", func() ObjectID {\n\t\t\t\t\tid, _ := ObjectIDFromHex(\"5ef7fdd91c19e3222b41b839\")\n\t\t\t\t\treturn id\n\t\t\t\t}()).\n\t\t\t\tBuild(),\n\t\t\tdecodeInto: func() any { return &objectIDTest{} },\n\t\t\twant:       &objectIDTest{ID: \"5ef7fdd91c19e3222b41b839\"},\n\t\t},\n\t\t// Test that UseJSONStructTags causes the Decoder to fall back to \"json\" struct tags if\n\t\t// \"bson\" struct tags are not available.\n\t\t{\n\t\t\tdescription: \"UseJSONStructTags\",\n\t\t\tconfigure: func(dec *Decoder) {\n\t\t\t\tdec.UseJSONStructTags()\n\t\t\t},\n\t\t\tinput: bsoncore.NewDocumentBuilder().\n\t\t\t\tAppendString(\"jsonFieldName\", \"test value\").\n\t\t\t\tBuild(),\n\t\t\tdecodeInto: func() any { return &jsonStructTest{} },\n\t\t\twant:       &jsonStructTest{StructFieldName: \"test value\"},\n\t\t},\n\t\t// Test that UseLocalTimeZone causes the Decoder to use the local time zone for decoded\n\t\t// time.Time values instead of UTC.\n\t\t{\n\t\t\tdescription: \"UseLocalTimeZone\",\n\t\t\tconfigure: func(dec *Decoder) {\n\t\t\t\tdec.UseLocalTimeZone()\n\t\t\t},\n\t\t\tinput: bsoncore.NewDocumentBuilder().\n\t\t\t\tAppendDateTime(\"myTime\", 1684349179939).\n\t\t\t\tBuild(),\n\t\t\tdecodeInto: func() any { return &localTimeZoneTest{} },\n\t\t\twant:       &localTimeZoneTest{MyTime: time.UnixMilli(1684349179939)},\n\t\t},\n\t\t// Test that ZeroMaps causes the Decoder to empty any Go map values before decoding BSON\n\t\t// documents into them.\n\t\t{\n\t\t\tdescription: \"ZeroMaps\",\n\t\t\tconfigure: func(dec *Decoder) {\n\t\t\t\tdec.ZeroMaps()\n\t\t\t},\n\t\t\tinput: bsoncore.NewDocumentBuilder().\n\t\t\t\tAppendDocument(\"myMap\", bsoncore.NewDocumentBuilder().\n\t\t\t\t\tAppendString(\"myString\", \"test value\").\n\t\t\t\t\tBuild()).\n\t\t\t\tBuild(),\n\t\t\tdecodeInto: func() any {\n\t\t\t\treturn &zeroMapsTest{MyMap: map[string]string{\"myExtraValue\": \"extra value\"}}\n\t\t\t},\n\t\t\twant: &zeroMapsTest{MyMap: map[string]string{\"myString\": \"test value\"}},\n\t\t},\n\t\t// Test that ZeroStructs causes the Decoder to empty any Go struct values before decoding\n\t\t// BSON documents into them.\n\t\t{\n\t\t\tdescription: \"ZeroStructs\",\n\t\t\tconfigure: func(dec *Decoder) {\n\t\t\t\tdec.ZeroStructs()\n\t\t\t},\n\t\t\tinput: bsoncore.NewDocumentBuilder().\n\t\t\t\tAppendString(\"myString\", \"test value\").\n\t\t\t\tBuild(),\n\t\t\tdecodeInto: func() any {\n\t\t\t\treturn &zeroStructsTest{MyInt: 1}\n\t\t\t},\n\t\t\twant: &zeroStructsTest{MyString: \"test value\"},\n\t\t},\n\t}\n\n\tfor _, tc := range testCases {\n\t\ttc := tc // Capture range variable.\n\n\t\tt.Run(tc.description, func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tdec := NewDecoder(NewDocumentReader(bytes.NewReader(tc.input)))\n\n\t\t\ttc.configure(dec)\n\n\t\t\tgot := tc.decodeInto()\n\t\t\terr := dec.Decode(got)\n\t\t\trequire.NoError(t, err, \"Decode error\")\n\n\t\t\tassert.Equal(t, tc.want, got, \"expected and actual decode results do not match\")\n\t\t})\n\t}\n\n\tt.Run(\"Decoding an object ID to string\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\ttype objectIDTest struct {\n\t\t\tID string\n\t\t}\n\n\t\tdoc := bsoncore.NewDocumentBuilder().\n\t\t\tAppendObjectID(\"id\", func() ObjectID {\n\t\t\t\tid, _ := ObjectIDFromHex(\"5ef7fdd91c19e3222b41b839\")\n\t\t\t\treturn id\n\t\t\t}()).\n\t\t\tBuild()\n\n\t\tdec := NewDecoder(NewDocumentReader(bytes.NewReader(doc)))\n\n\t\tvar got objectIDTest\n\t\terr := dec.Decode(&got)\n\t\tconst want = \"error decoding key id: decoding an object ID into a string is not supported by default (set Decoder.ObjectIDAsHexString to enable decoding as a hexadecimal string)\"\n\t\tassert.EqualError(t, err, want)\n\t})\n\tt.Run(\"DefaultDocumentM top-level\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tinput := bsoncore.NewDocumentBuilder().\n\t\t\tAppendDocument(\"myDocument\", bsoncore.NewDocumentBuilder().\n\t\t\t\tAppendString(\"myString\", \"test value\").\n\t\t\t\tBuild()).\n\t\t\tBuild()\n\n\t\tdec := NewDecoder(NewDocumentReader(bytes.NewReader(input)))\n\n\t\tdec.DefaultDocumentM()\n\n\t\tvar got any\n\t\terr := dec.Decode(&got)\n\t\trequire.NoError(t, err, \"Decode error\")\n\n\t\twant := M{\n\t\t\t\"myDocument\": M{\n\t\t\t\t\"myString\": \"test value\",\n\t\t\t},\n\t\t}\n\t\tassert.Equal(t, want, got, \"expected and actual decode results do not match\")\n\t})\n\tt.Run(\"DefaultDocumentMap top-level\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tinput := bsoncore.NewDocumentBuilder().\n\t\t\tAppendDocument(\"myDocument\", bsoncore.NewDocumentBuilder().\n\t\t\t\tAppendString(\"myString\", \"test value\").\n\t\t\t\tBuild()).\n\t\t\tBuild()\n\n\t\tdec := NewDecoder(NewDocumentReader(bytes.NewReader(input)))\n\n\t\tdec.DefaultDocumentMap()\n\n\t\tvar got any\n\t\terr := dec.Decode(&got)\n\t\trequire.NoError(t, err, \"Decode error\")\n\n\t\twant := map[string]any{\n\t\t\t\"myDocument\": map[string]any{\n\t\t\t\t\"myString\": \"test value\",\n\t\t\t},\n\t\t}\n\t\tassert.Equal(t, want, got, \"expected and actual decode results do not match\")\n\t})\n\tt.Run(\"Default decodes DocumentD for top-level\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tinput := bsoncore.NewDocumentBuilder().\n\t\t\tAppendDocument(\"myDocument\", bsoncore.NewDocumentBuilder().\n\t\t\t\tAppendString(\"myString\", \"test value\").\n\t\t\t\tBuild()).\n\t\t\tBuild()\n\n\t\tdec := NewDecoder(NewDocumentReader(bytes.NewReader(input)))\n\n\t\tvar got any\n\t\terr := dec.Decode(&got)\n\t\trequire.NoError(t, err, \"Decode error\")\n\n\t\twant := D{\n\t\t\t{Key: \"myDocument\", Value: D{\n\t\t\t\t{Key: \"myString\", Value: \"test value\"},\n\t\t\t}},\n\t\t}\n\t\tassert.Equal(t, want, got, \"expected and actual decode results do not match\")\n\t})\n}\n"
  },
  {
    "path": "bson/default_value_decoders.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bson\n\nimport (\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"math\"\n\t\"net/url\"\n\t\"reflect\"\n\t\"strconv\"\n\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n)\n\nvar errCannotTruncate = errors.New(\"float64 can only be truncated to a lower precision type when truncation is enabled\")\n\ntype decodeBinaryError struct {\n\tsubtype  byte\n\ttypeName string\n}\n\nfunc (d decodeBinaryError) Error() string {\n\treturn fmt.Sprintf(\"only binary values with subtype 0x00 or 0x02 can be decoded into %s, but got subtype %v\", d.typeName, d.subtype)\n}\n\n// registerDefaultDecoders will register the decoder methods attached to DefaultValueDecoders with\n// the provided RegistryBuilder.\n//\n// There is no support for decoding map[string]any because there is no decoder for\n// any, so users must either register this decoder themselves or use the\n// EmptyInterfaceDecoder available in the bson package.\nfunc registerDefaultDecoders(reg *Registry) {\n\tintDecoder := decodeAdapter{intDecodeValue, intDecodeType}\n\tfloatDecoder := decodeAdapter{floatDecodeValue, floatDecodeType}\n\tuintCodec := &uintCodec{}\n\n\treg.RegisterTypeDecoder(tD, ValueDecoderFunc(dDecodeValue))\n\treg.RegisterTypeDecoder(tBinary, decodeAdapter{binaryDecodeValue, binaryDecodeType})\n\treg.RegisterTypeDecoder(tVector, decodeAdapter{vectorDecodeValue, vectorDecodeType})\n\treg.RegisterTypeDecoder(tUndefined, decodeAdapter{undefinedDecodeValue, undefinedDecodeType})\n\treg.RegisterTypeDecoder(tDateTime, decodeAdapter{dateTimeDecodeValue, dateTimeDecodeType})\n\treg.RegisterTypeDecoder(tNull, decodeAdapter{nullDecodeValue, nullDecodeType})\n\treg.RegisterTypeDecoder(tRegex, decodeAdapter{regexDecodeValue, regexDecodeType})\n\treg.RegisterTypeDecoder(tDBPointer, decodeAdapter{dbPointerDecodeValue, dbPointerDecodeType})\n\treg.RegisterTypeDecoder(tTimestamp, decodeAdapter{timestampDecodeValue, timestampDecodeType})\n\treg.RegisterTypeDecoder(tMinKey, decodeAdapter{minKeyDecodeValue, minKeyDecodeType})\n\treg.RegisterTypeDecoder(tMaxKey, decodeAdapter{maxKeyDecodeValue, maxKeyDecodeType})\n\treg.RegisterTypeDecoder(tJavaScript, decodeAdapter{javaScriptDecodeValue, javaScriptDecodeType})\n\treg.RegisterTypeDecoder(tSymbol, decodeAdapter{symbolDecodeValue, symbolDecodeType})\n\treg.RegisterTypeDecoder(tByteSlice, &byteSliceCodec{})\n\treg.RegisterTypeDecoder(tTime, &timeCodec{})\n\treg.RegisterTypeDecoder(tEmpty, &emptyInterfaceCodec{})\n\treg.RegisterTypeDecoder(tCoreArray, &arrayCodec{})\n\treg.RegisterTypeDecoder(tOID, decodeAdapter{objectIDDecodeValue, objectIDDecodeType})\n\treg.RegisterTypeDecoder(tDecimal, decodeAdapter{decimal128DecodeValue, decimal128DecodeType})\n\treg.RegisterTypeDecoder(tJSONNumber, decodeAdapter{jsonNumberDecodeValue, jsonNumberDecodeType})\n\treg.RegisterTypeDecoder(tURL, decodeAdapter{urlDecodeValue, urlDecodeType})\n\treg.RegisterTypeDecoder(tCoreDocument, ValueDecoderFunc(coreDocumentDecodeValue))\n\treg.RegisterTypeDecoder(tCodeWithScope, decodeAdapter{codeWithScopeDecodeValue, codeWithScopeDecodeType})\n\treg.RegisterKindDecoder(reflect.Bool, decodeAdapter{booleanDecodeValue, booleanDecodeType})\n\treg.RegisterKindDecoder(reflect.Int, intDecoder)\n\treg.RegisterKindDecoder(reflect.Int8, intDecoder)\n\treg.RegisterKindDecoder(reflect.Int16, intDecoder)\n\treg.RegisterKindDecoder(reflect.Int32, intDecoder)\n\treg.RegisterKindDecoder(reflect.Int64, intDecoder)\n\treg.RegisterKindDecoder(reflect.Uint, uintCodec)\n\treg.RegisterKindDecoder(reflect.Uint8, uintCodec)\n\treg.RegisterKindDecoder(reflect.Uint16, uintCodec)\n\treg.RegisterKindDecoder(reflect.Uint32, uintCodec)\n\treg.RegisterKindDecoder(reflect.Uint64, uintCodec)\n\treg.RegisterKindDecoder(reflect.Float32, floatDecoder)\n\treg.RegisterKindDecoder(reflect.Float64, floatDecoder)\n\treg.RegisterKindDecoder(reflect.Array, ValueDecoderFunc(arrayDecodeValue))\n\treg.RegisterKindDecoder(reflect.Map, &mapCodec{})\n\treg.RegisterKindDecoder(reflect.Slice, &sliceCodec{})\n\treg.RegisterKindDecoder(reflect.String, &stringCodec{})\n\treg.RegisterKindDecoder(reflect.Struct, newStructCodec(nil))\n\treg.RegisterKindDecoder(reflect.Ptr, &pointerCodec{})\n\treg.RegisterTypeMapEntry(TypeDouble, tFloat64)\n\treg.RegisterTypeMapEntry(TypeString, tString)\n\treg.RegisterTypeMapEntry(TypeArray, tA)\n\treg.RegisterTypeMapEntry(TypeBinary, tBinary)\n\treg.RegisterTypeMapEntry(TypeUndefined, tUndefined)\n\treg.RegisterTypeMapEntry(TypeObjectID, tOID)\n\treg.RegisterTypeMapEntry(TypeBoolean, tBool)\n\treg.RegisterTypeMapEntry(TypeDateTime, tDateTime)\n\treg.RegisterTypeMapEntry(TypeRegex, tRegex)\n\treg.RegisterTypeMapEntry(TypeDBPointer, tDBPointer)\n\treg.RegisterTypeMapEntry(TypeJavaScript, tJavaScript)\n\treg.RegisterTypeMapEntry(TypeSymbol, tSymbol)\n\treg.RegisterTypeMapEntry(TypeCodeWithScope, tCodeWithScope)\n\treg.RegisterTypeMapEntry(TypeInt32, tInt32)\n\treg.RegisterTypeMapEntry(TypeInt64, tInt64)\n\treg.RegisterTypeMapEntry(TypeTimestamp, tTimestamp)\n\treg.RegisterTypeMapEntry(TypeDecimal128, tDecimal)\n\treg.RegisterTypeMapEntry(TypeMinKey, tMinKey)\n\treg.RegisterTypeMapEntry(TypeMaxKey, tMaxKey)\n\treg.RegisterTypeMapEntry(Type(0), tD)\n\treg.RegisterTypeMapEntry(TypeEmbeddedDocument, tD)\n\treg.RegisterInterfaceDecoder(tValueUnmarshaler, ValueDecoderFunc(valueUnmarshalerDecodeValue))\n\treg.RegisterInterfaceDecoder(tUnmarshaler, ValueDecoderFunc(unmarshalerDecodeValue))\n}\n\n// dDecodeValue is the ValueDecoderFunc for D instances.\nfunc dDecodeValue(dc DecodeContext, vr ValueReader, val reflect.Value) error {\n\tif !val.IsValid() || !val.CanSet() || val.Type() != tD {\n\t\treturn ValueDecoderError{Name: \"DDecodeValue\", Kinds: []reflect.Kind{reflect.Slice}, Received: val}\n\t}\n\n\tswitch vrType := vr.Type(); vrType {\n\tcase Type(0), TypeEmbeddedDocument:\n\t\tbreak\n\tcase TypeNull:\n\t\tval.Set(reflect.Zero(val.Type()))\n\t\treturn vr.ReadNull()\n\tdefault:\n\t\treturn fmt.Errorf(\"cannot decode %v into a D\", vrType)\n\t}\n\n\tdr, err := vr.ReadDocument()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tdecoder, err := dc.LookupDecoder(tEmpty)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// Use the elements in the provided value if it's non nil. Otherwise, allocate a new D instance.\n\tvar elems D\n\tif !val.IsNil() {\n\t\tval.SetLen(0)\n\t\telems = val.Interface().(D)\n\t} else {\n\t\telems = make(D, 0)\n\t}\n\n\tfor {\n\t\tkey, elemVr, err := dr.ReadElement()\n\t\tif errors.Is(err, ErrEOD) {\n\t\t\tbreak\n\t\t} else if err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tvar v any\n\t\terr = decoder.DecodeValue(dc, elemVr, reflect.ValueOf(&v).Elem())\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\telems = append(elems, E{Key: key, Value: v})\n\t}\n\n\tval.Set(reflect.ValueOf(elems))\n\treturn nil\n}\n\nfunc booleanDecodeType(_ DecodeContext, vr ValueReader, t reflect.Type) (reflect.Value, error) {\n\tif t.Kind() != reflect.Bool {\n\t\treturn emptyValue, ValueDecoderError{\n\t\t\tName:     \"BooleanDecodeValue\",\n\t\t\tKinds:    []reflect.Kind{reflect.Bool},\n\t\t\tReceived: reflect.Zero(t),\n\t\t}\n\t}\n\n\tvar b bool\n\tvar err error\n\tswitch vrType := vr.Type(); vrType {\n\tcase TypeInt32:\n\t\ti32, err := vr.ReadInt32()\n\t\tif err != nil {\n\t\t\treturn emptyValue, err\n\t\t}\n\t\tb = (i32 != 0)\n\tcase TypeInt64:\n\t\ti64, err := vr.ReadInt64()\n\t\tif err != nil {\n\t\t\treturn emptyValue, err\n\t\t}\n\t\tb = (i64 != 0)\n\tcase TypeDouble:\n\t\tf64, err := vr.ReadDouble()\n\t\tif err != nil {\n\t\t\treturn emptyValue, err\n\t\t}\n\t\tb = (f64 != 0)\n\tcase TypeBoolean:\n\t\tb, err = vr.ReadBoolean()\n\tcase TypeNull:\n\t\terr = vr.ReadNull()\n\tcase TypeUndefined:\n\t\terr = vr.ReadUndefined()\n\tdefault:\n\t\treturn emptyValue, fmt.Errorf(\"cannot decode %v into a boolean\", vrType)\n\t}\n\tif err != nil {\n\t\treturn emptyValue, err\n\t}\n\n\treturn reflect.ValueOf(b), nil\n}\n\n// booleanDecodeValue is the ValueDecoderFunc for bool types.\nfunc booleanDecodeValue(dctx DecodeContext, vr ValueReader, val reflect.Value) error {\n\tif !val.IsValid() || !val.CanSet() || val.Kind() != reflect.Bool {\n\t\treturn ValueDecoderError{Name: \"BooleanDecodeValue\", Kinds: []reflect.Kind{reflect.Bool}, Received: val}\n\t}\n\n\telem, err := booleanDecodeType(dctx, vr, val.Type())\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tval.SetBool(elem.Bool())\n\treturn nil\n}\n\nfunc intDecodeType(dc DecodeContext, vr ValueReader, t reflect.Type) (reflect.Value, error) {\n\tvar i64 int64\n\tvar err error\n\tswitch vrType := vr.Type(); vrType {\n\tcase TypeInt32:\n\t\ti32, err := vr.ReadInt32()\n\t\tif err != nil {\n\t\t\treturn emptyValue, err\n\t\t}\n\t\ti64 = int64(i32)\n\tcase TypeInt64:\n\t\ti64, err = vr.ReadInt64()\n\t\tif err != nil {\n\t\t\treturn emptyValue, err\n\t\t}\n\tcase TypeDouble:\n\t\tf64, err := vr.ReadDouble()\n\t\tif err != nil {\n\t\t\treturn emptyValue, err\n\t\t}\n\t\tif !dc.truncate && math.Floor(f64) != f64 {\n\t\t\treturn emptyValue, errCannotTruncate\n\t\t}\n\t\tif f64 > float64(math.MaxInt64) {\n\t\t\treturn emptyValue, fmt.Errorf(\"%g overflows int64\", f64)\n\t\t}\n\t\ti64 = int64(f64)\n\tcase TypeBoolean:\n\t\tb, err := vr.ReadBoolean()\n\t\tif err != nil {\n\t\t\treturn emptyValue, err\n\t\t}\n\t\tif b {\n\t\t\ti64 = 1\n\t\t}\n\tcase TypeNull:\n\t\tif err = vr.ReadNull(); err != nil {\n\t\t\treturn emptyValue, err\n\t\t}\n\tcase TypeUndefined:\n\t\tif err = vr.ReadUndefined(); err != nil {\n\t\t\treturn emptyValue, err\n\t\t}\n\tdefault:\n\t\treturn emptyValue, fmt.Errorf(\"cannot decode %v into an integer type\", vrType)\n\t}\n\n\tswitch t.Kind() {\n\tcase reflect.Int8:\n\t\tif i64 < math.MinInt8 || i64 > math.MaxInt8 {\n\t\t\treturn emptyValue, fmt.Errorf(\"%d overflows int8\", i64)\n\t\t}\n\n\t\treturn reflect.ValueOf(int8(i64)), nil\n\tcase reflect.Int16:\n\t\tif i64 < math.MinInt16 || i64 > math.MaxInt16 {\n\t\t\treturn emptyValue, fmt.Errorf(\"%d overflows int16\", i64)\n\t\t}\n\n\t\treturn reflect.ValueOf(int16(i64)), nil\n\tcase reflect.Int32:\n\t\tif i64 < math.MinInt32 || i64 > math.MaxInt32 {\n\t\t\treturn emptyValue, fmt.Errorf(\"%d overflows int32\", i64)\n\t\t}\n\n\t\treturn reflect.ValueOf(int32(i64)), nil\n\tcase reflect.Int64:\n\t\treturn reflect.ValueOf(i64), nil\n\tcase reflect.Int:\n\t\tif i64 > math.MaxInt { // Can we fit this inside of an int\n\t\t\treturn emptyValue, fmt.Errorf(\"%d overflows int\", i64)\n\t\t}\n\n\t\treturn reflect.ValueOf(int(i64)), nil\n\tdefault:\n\t\treturn emptyValue, ValueDecoderError{\n\t\t\tName:     \"IntDecodeValue\",\n\t\t\tKinds:    []reflect.Kind{reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int},\n\t\t\tReceived: reflect.Zero(t),\n\t\t}\n\t}\n}\n\n// intDecodeValue is the ValueDecoderFunc for int types.\nfunc intDecodeValue(dc DecodeContext, vr ValueReader, val reflect.Value) error {\n\tif !val.CanSet() {\n\t\treturn ValueDecoderError{\n\t\t\tName:     \"IntDecodeValue\",\n\t\t\tKinds:    []reflect.Kind{reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int},\n\t\t\tReceived: val,\n\t\t}\n\t}\n\n\telem, err := intDecodeType(dc, vr, val.Type())\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tval.SetInt(elem.Int())\n\treturn nil\n}\n\nfunc floatDecodeType(dc DecodeContext, vr ValueReader, t reflect.Type) (reflect.Value, error) {\n\tvar f float64\n\tvar err error\n\tswitch vrType := vr.Type(); vrType {\n\tcase TypeInt32:\n\t\ti32, err := vr.ReadInt32()\n\t\tif err != nil {\n\t\t\treturn emptyValue, err\n\t\t}\n\t\tf = float64(i32)\n\tcase TypeInt64:\n\t\ti64, err := vr.ReadInt64()\n\t\tif err != nil {\n\t\t\treturn emptyValue, err\n\t\t}\n\t\tf = float64(i64)\n\tcase TypeDouble:\n\t\tf, err = vr.ReadDouble()\n\t\tif err != nil {\n\t\t\treturn emptyValue, err\n\t\t}\n\tcase TypeBoolean:\n\t\tb, err := vr.ReadBoolean()\n\t\tif err != nil {\n\t\t\treturn emptyValue, err\n\t\t}\n\t\tif b {\n\t\t\tf = 1\n\t\t}\n\tcase TypeNull:\n\t\tif err = vr.ReadNull(); err != nil {\n\t\t\treturn emptyValue, err\n\t\t}\n\tcase TypeUndefined:\n\t\tif err = vr.ReadUndefined(); err != nil {\n\t\t\treturn emptyValue, err\n\t\t}\n\tdefault:\n\t\treturn emptyValue, fmt.Errorf(\"cannot decode %v into a float32 or float64 type\", vrType)\n\t}\n\n\tswitch t.Kind() {\n\tcase reflect.Float32:\n\t\tif !dc.truncate && float64(float32(f)) != f {\n\t\t\treturn emptyValue, errCannotTruncate\n\t\t}\n\n\t\treturn reflect.ValueOf(float32(f)), nil\n\tcase reflect.Float64:\n\t\treturn reflect.ValueOf(f), nil\n\tdefault:\n\t\treturn emptyValue, ValueDecoderError{\n\t\t\tName:     \"FloatDecodeValue\",\n\t\t\tKinds:    []reflect.Kind{reflect.Float32, reflect.Float64},\n\t\t\tReceived: reflect.Zero(t),\n\t\t}\n\t}\n}\n\n// floatDecodeValue is the ValueDecoderFunc for float types.\nfunc floatDecodeValue(ec DecodeContext, vr ValueReader, val reflect.Value) error {\n\tif !val.CanSet() {\n\t\treturn ValueDecoderError{\n\t\t\tName:     \"FloatDecodeValue\",\n\t\t\tKinds:    []reflect.Kind{reflect.Float32, reflect.Float64},\n\t\t\tReceived: val,\n\t\t}\n\t}\n\n\telem, err := floatDecodeType(ec, vr, val.Type())\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tval.SetFloat(elem.Float())\n\treturn nil\n}\n\nfunc javaScriptDecodeType(_ DecodeContext, vr ValueReader, t reflect.Type) (reflect.Value, error) {\n\tif t != tJavaScript {\n\t\treturn emptyValue, ValueDecoderError{\n\t\t\tName:     \"JavaScriptDecodeValue\",\n\t\t\tTypes:    []reflect.Type{tJavaScript},\n\t\t\tReceived: reflect.Zero(t),\n\t\t}\n\t}\n\n\tvar js string\n\tvar err error\n\tswitch vrType := vr.Type(); vrType {\n\tcase TypeJavaScript:\n\t\tjs, err = vr.ReadJavascript()\n\tcase TypeNull:\n\t\terr = vr.ReadNull()\n\tcase TypeUndefined:\n\t\terr = vr.ReadUndefined()\n\tdefault:\n\t\treturn emptyValue, fmt.Errorf(\"cannot decode %v into a JavaScript\", vrType)\n\t}\n\tif err != nil {\n\t\treturn emptyValue, err\n\t}\n\n\treturn reflect.ValueOf(JavaScript(js)), nil\n}\n\n// javaScriptDecodeValue is the ValueDecoderFunc for the JavaScript type.\nfunc javaScriptDecodeValue(dctx DecodeContext, vr ValueReader, val reflect.Value) error {\n\tif !val.CanSet() || val.Type() != tJavaScript {\n\t\treturn ValueDecoderError{Name: \"JavaScriptDecodeValue\", Types: []reflect.Type{tJavaScript}, Received: val}\n\t}\n\n\telem, err := javaScriptDecodeType(dctx, vr, tJavaScript)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tval.SetString(elem.String())\n\treturn nil\n}\n\nfunc symbolDecodeType(_ DecodeContext, vr ValueReader, t reflect.Type) (reflect.Value, error) {\n\tif t != tSymbol {\n\t\treturn emptyValue, ValueDecoderError{\n\t\t\tName:     \"SymbolDecodeValue\",\n\t\t\tTypes:    []reflect.Type{tSymbol},\n\t\t\tReceived: reflect.Zero(t),\n\t\t}\n\t}\n\n\tvar symbol string\n\tvar err error\n\tswitch vrType := vr.Type(); vrType {\n\tcase TypeString:\n\t\tsymbol, err = vr.ReadString()\n\tcase TypeSymbol:\n\t\tsymbol, err = vr.ReadSymbol()\n\tcase TypeBinary:\n\t\tdata, subtype, err := vr.ReadBinary()\n\t\tif err != nil {\n\t\t\treturn emptyValue, err\n\t\t}\n\n\t\tif subtype != TypeBinaryGeneric && subtype != TypeBinaryBinaryOld {\n\t\t\treturn emptyValue, decodeBinaryError{subtype: subtype, typeName: \"Symbol\"}\n\t\t}\n\t\tsymbol = string(data)\n\tcase TypeNull:\n\t\terr = vr.ReadNull()\n\tcase TypeUndefined:\n\t\terr = vr.ReadUndefined()\n\tdefault:\n\t\treturn emptyValue, fmt.Errorf(\"cannot decode %v into a Symbol\", vrType)\n\t}\n\tif err != nil {\n\t\treturn emptyValue, err\n\t}\n\n\treturn reflect.ValueOf(Symbol(symbol)), nil\n}\n\n// symbolDecodeValue is the ValueDecoderFunc for the Symbol type.\nfunc symbolDecodeValue(dctx DecodeContext, vr ValueReader, val reflect.Value) error {\n\tif !val.CanSet() || val.Type() != tSymbol {\n\t\treturn ValueDecoderError{Name: \"SymbolDecodeValue\", Types: []reflect.Type{tSymbol}, Received: val}\n\t}\n\n\telem, err := symbolDecodeType(dctx, vr, tSymbol)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tval.SetString(elem.String())\n\treturn nil\n}\n\nfunc binaryDecode(vr ValueReader) (Binary, error) {\n\tvar b Binary\n\n\tvar data []byte\n\tvar subtype byte\n\tvar err error\n\tswitch vrType := vr.Type(); vrType {\n\tcase TypeBinary:\n\t\tdata, subtype, err = vr.ReadBinary()\n\tcase TypeNull:\n\t\terr = vr.ReadNull()\n\tcase TypeUndefined:\n\t\terr = vr.ReadUndefined()\n\tdefault:\n\t\treturn b, fmt.Errorf(\"cannot decode %v into a Binary\", vrType)\n\t}\n\tif err != nil {\n\t\treturn b, err\n\t}\n\tb.Subtype = subtype\n\tb.Data = data\n\n\treturn b, nil\n}\n\nfunc binaryDecodeType(_ DecodeContext, vr ValueReader, t reflect.Type) (reflect.Value, error) {\n\tif t != tBinary {\n\t\treturn emptyValue, ValueDecoderError{\n\t\t\tName:     \"BinaryDecodeValue\",\n\t\t\tTypes:    []reflect.Type{tBinary},\n\t\t\tReceived: reflect.Zero(t),\n\t\t}\n\t}\n\n\tb, err := binaryDecode(vr)\n\tif err != nil {\n\t\treturn emptyValue, err\n\t}\n\treturn reflect.ValueOf(b), nil\n}\n\n// binaryDecodeValue is the ValueDecoderFunc for Binary.\nfunc binaryDecodeValue(dc DecodeContext, vr ValueReader, val reflect.Value) error {\n\tif !val.CanSet() || val.Type() != tBinary {\n\t\treturn ValueDecoderError{Name: \"BinaryDecodeValue\", Types: []reflect.Type{tBinary}, Received: val}\n\t}\n\n\telem, err := binaryDecodeType(dc, vr, tBinary)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tval.Set(elem)\n\treturn nil\n}\n\nfunc vectorDecodeType(_ DecodeContext, vr ValueReader, t reflect.Type) (reflect.Value, error) {\n\tif t != tVector {\n\t\treturn emptyValue, ValueDecoderError{\n\t\t\tName:     \"VectorDecodeValue\",\n\t\t\tTypes:    []reflect.Type{tVector},\n\t\t\tReceived: reflect.Zero(t),\n\t\t}\n\t}\n\n\tb, err := binaryDecode(vr)\n\tif err != nil {\n\t\treturn emptyValue, err\n\t}\n\n\tv, err := NewVectorFromBinary(b)\n\tif err != nil {\n\t\treturn emptyValue, err\n\t}\n\n\treturn reflect.ValueOf(v), nil\n}\n\n// vectorDecodeValue is the ValueDecoderFunc for Vector.\nfunc vectorDecodeValue(dctx DecodeContext, vr ValueReader, val reflect.Value) error {\n\tt := val.Type()\n\tif !val.CanSet() || t != tVector {\n\t\treturn ValueDecoderError{\n\t\t\tName:     \"VectorDecodeValue\",\n\t\t\tTypes:    []reflect.Type{tVector},\n\t\t\tReceived: val,\n\t\t}\n\t}\n\n\telem, err := vectorDecodeType(dctx, vr, t)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tval.Set(elem)\n\treturn nil\n}\n\nfunc undefinedDecodeType(_ DecodeContext, vr ValueReader, t reflect.Type) (reflect.Value, error) {\n\tif t != tUndefined {\n\t\treturn emptyValue, ValueDecoderError{\n\t\t\tName:     \"UndefinedDecodeValue\",\n\t\t\tTypes:    []reflect.Type{tUndefined},\n\t\t\tReceived: reflect.Zero(t),\n\t\t}\n\t}\n\n\tvar err error\n\tswitch vrType := vr.Type(); vrType {\n\tcase TypeUndefined:\n\t\terr = vr.ReadUndefined()\n\tcase TypeNull:\n\t\terr = vr.ReadNull()\n\tdefault:\n\t\treturn emptyValue, fmt.Errorf(\"cannot decode %v into an Undefined\", vr.Type())\n\t}\n\tif err != nil {\n\t\treturn emptyValue, err\n\t}\n\n\treturn reflect.ValueOf(Undefined{}), nil\n}\n\n// undefinedDecodeValue is the ValueDecoderFunc for Undefined.\nfunc undefinedDecodeValue(dc DecodeContext, vr ValueReader, val reflect.Value) error {\n\tif !val.CanSet() || val.Type() != tUndefined {\n\t\treturn ValueDecoderError{Name: \"UndefinedDecodeValue\", Types: []reflect.Type{tUndefined}, Received: val}\n\t}\n\n\telem, err := undefinedDecodeType(dc, vr, tUndefined)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tval.Set(elem)\n\treturn nil\n}\n\n// Accept both 12-byte string and pretty-printed 24-byte hex string formats.\nfunc objectIDDecodeType(_ DecodeContext, vr ValueReader, t reflect.Type) (reflect.Value, error) {\n\tif t != tOID {\n\t\treturn emptyValue, ValueDecoderError{\n\t\t\tName:     \"ObjectIDDecodeValue\",\n\t\t\tTypes:    []reflect.Type{tOID},\n\t\t\tReceived: reflect.Zero(t),\n\t\t}\n\t}\n\n\tvar oid ObjectID\n\tvar err error\n\tswitch vrType := vr.Type(); vrType {\n\tcase TypeObjectID:\n\t\toid, err = vr.ReadObjectID()\n\t\tif err != nil {\n\t\t\treturn emptyValue, err\n\t\t}\n\tcase TypeString:\n\t\tstr, err := vr.ReadString()\n\t\tif err != nil {\n\t\t\treturn emptyValue, err\n\t\t}\n\t\tif oid, err = ObjectIDFromHex(str); err == nil {\n\t\t\tbreak\n\t\t}\n\t\tif len(str) != 12 {\n\t\t\treturn emptyValue, fmt.Errorf(\"an ObjectID string must be exactly 12 bytes long (got %v)\", len(str))\n\t\t}\n\t\tbyteArr := []byte(str)\n\t\tcopy(oid[:], byteArr)\n\tcase TypeNull:\n\t\tif err = vr.ReadNull(); err != nil {\n\t\t\treturn emptyValue, err\n\t\t}\n\tcase TypeUndefined:\n\t\tif err = vr.ReadUndefined(); err != nil {\n\t\t\treturn emptyValue, err\n\t\t}\n\tdefault:\n\t\treturn emptyValue, fmt.Errorf(\"cannot decode %v into an ObjectID\", vrType)\n\t}\n\n\treturn reflect.ValueOf(oid), nil\n}\n\n// objectIDDecodeValue is the ValueDecoderFunc for ObjectID.\nfunc objectIDDecodeValue(dc DecodeContext, vr ValueReader, val reflect.Value) error {\n\tif !val.CanSet() || val.Type() != tOID {\n\t\treturn ValueDecoderError{Name: \"ObjectIDDecodeValue\", Types: []reflect.Type{tOID}, Received: val}\n\t}\n\n\telem, err := objectIDDecodeType(dc, vr, tOID)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tval.Set(elem)\n\treturn nil\n}\n\nfunc dateTimeDecodeType(_ DecodeContext, vr ValueReader, t reflect.Type) (reflect.Value, error) {\n\tif t != tDateTime {\n\t\treturn emptyValue, ValueDecoderError{\n\t\t\tName:     \"DateTimeDecodeValue\",\n\t\t\tTypes:    []reflect.Type{tDateTime},\n\t\t\tReceived: reflect.Zero(t),\n\t\t}\n\t}\n\n\tvar dt int64\n\tvar err error\n\tswitch vrType := vr.Type(); vrType {\n\tcase TypeDateTime:\n\t\tdt, err = vr.ReadDateTime()\n\tcase TypeNull:\n\t\terr = vr.ReadNull()\n\tcase TypeUndefined:\n\t\terr = vr.ReadUndefined()\n\tdefault:\n\t\treturn emptyValue, fmt.Errorf(\"cannot decode %v into a DateTime\", vrType)\n\t}\n\tif err != nil {\n\t\treturn emptyValue, err\n\t}\n\n\treturn reflect.ValueOf(DateTime(dt)), nil\n}\n\n// dateTimeDecodeValue is the ValueDecoderFunc for DateTime.\nfunc dateTimeDecodeValue(dc DecodeContext, vr ValueReader, val reflect.Value) error {\n\tif !val.CanSet() || val.Type() != tDateTime {\n\t\treturn ValueDecoderError{Name: \"DateTimeDecodeValue\", Types: []reflect.Type{tDateTime}, Received: val}\n\t}\n\n\telem, err := dateTimeDecodeType(dc, vr, tDateTime)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tval.Set(elem)\n\treturn nil\n}\n\nfunc nullDecodeType(_ DecodeContext, vr ValueReader, t reflect.Type) (reflect.Value, error) {\n\tif t != tNull {\n\t\treturn emptyValue, ValueDecoderError{\n\t\t\tName:     \"NullDecodeValue\",\n\t\t\tTypes:    []reflect.Type{tNull},\n\t\t\tReceived: reflect.Zero(t),\n\t\t}\n\t}\n\n\tvar err error\n\tswitch vrType := vr.Type(); vrType {\n\tcase TypeUndefined:\n\t\terr = vr.ReadUndefined()\n\tcase TypeNull:\n\t\terr = vr.ReadNull()\n\tdefault:\n\t\treturn emptyValue, fmt.Errorf(\"cannot decode %v into a Null\", vr.Type())\n\t}\n\tif err != nil {\n\t\treturn emptyValue, err\n\t}\n\n\treturn reflect.ValueOf(Null{}), nil\n}\n\n// nullDecodeValue is the ValueDecoderFunc for Null.\nfunc nullDecodeValue(dc DecodeContext, vr ValueReader, val reflect.Value) error {\n\tif !val.CanSet() || val.Type() != tNull {\n\t\treturn ValueDecoderError{Name: \"NullDecodeValue\", Types: []reflect.Type{tNull}, Received: val}\n\t}\n\n\telem, err := nullDecodeType(dc, vr, tNull)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tval.Set(elem)\n\treturn nil\n}\n\nfunc regexDecodeType(_ DecodeContext, vr ValueReader, t reflect.Type) (reflect.Value, error) {\n\tif t != tRegex {\n\t\treturn emptyValue, ValueDecoderError{\n\t\t\tName:     \"RegexDecodeValue\",\n\t\t\tTypes:    []reflect.Type{tRegex},\n\t\t\tReceived: reflect.Zero(t),\n\t\t}\n\t}\n\n\tvar pattern, options string\n\tvar err error\n\tswitch vrType := vr.Type(); vrType {\n\tcase TypeRegex:\n\t\tpattern, options, err = vr.ReadRegex()\n\tcase TypeNull:\n\t\terr = vr.ReadNull()\n\tcase TypeUndefined:\n\t\terr = vr.ReadUndefined()\n\tdefault:\n\t\treturn emptyValue, fmt.Errorf(\"cannot decode %v into a Regex\", vrType)\n\t}\n\tif err != nil {\n\t\treturn emptyValue, err\n\t}\n\n\treturn reflect.ValueOf(Regex{Pattern: pattern, Options: options}), nil\n}\n\n// regexDecodeValue is the ValueDecoderFunc for Regex.\nfunc regexDecodeValue(dc DecodeContext, vr ValueReader, val reflect.Value) error {\n\tif !val.CanSet() || val.Type() != tRegex {\n\t\treturn ValueDecoderError{Name: \"RegexDecodeValue\", Types: []reflect.Type{tRegex}, Received: val}\n\t}\n\n\telem, err := regexDecodeType(dc, vr, tRegex)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tval.Set(elem)\n\treturn nil\n}\n\nfunc dbPointerDecodeType(_ DecodeContext, vr ValueReader, t reflect.Type) (reflect.Value, error) {\n\tif t != tDBPointer {\n\t\treturn emptyValue, ValueDecoderError{\n\t\t\tName:     \"DBPointerDecodeValue\",\n\t\t\tTypes:    []reflect.Type{tDBPointer},\n\t\t\tReceived: reflect.Zero(t),\n\t\t}\n\t}\n\n\tvar ns string\n\tvar pointer ObjectID\n\tvar err error\n\tswitch vrType := vr.Type(); vrType {\n\tcase TypeDBPointer:\n\t\tns, pointer, err = vr.ReadDBPointer()\n\tcase TypeNull:\n\t\terr = vr.ReadNull()\n\tcase TypeUndefined:\n\t\terr = vr.ReadUndefined()\n\tdefault:\n\t\treturn emptyValue, fmt.Errorf(\"cannot decode %v into a DBPointer\", vrType)\n\t}\n\tif err != nil {\n\t\treturn emptyValue, err\n\t}\n\n\treturn reflect.ValueOf(DBPointer{DB: ns, Pointer: pointer}), nil\n}\n\n// dbPointerDecodeValue is the ValueDecoderFunc for DBPointer.\nfunc dbPointerDecodeValue(dc DecodeContext, vr ValueReader, val reflect.Value) error {\n\tif !val.CanSet() || val.Type() != tDBPointer {\n\t\treturn ValueDecoderError{Name: \"DBPointerDecodeValue\", Types: []reflect.Type{tDBPointer}, Received: val}\n\t}\n\n\telem, err := dbPointerDecodeType(dc, vr, tDBPointer)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tval.Set(elem)\n\treturn nil\n}\n\nfunc timestampDecodeType(_ DecodeContext, vr ValueReader, reflectType reflect.Type) (reflect.Value, error) {\n\tif reflectType != tTimestamp {\n\t\treturn emptyValue, ValueDecoderError{\n\t\t\tName:     \"TimestampDecodeValue\",\n\t\t\tTypes:    []reflect.Type{tTimestamp},\n\t\t\tReceived: reflect.Zero(reflectType),\n\t\t}\n\t}\n\n\tvar t, incr uint32\n\tvar err error\n\tswitch vrType := vr.Type(); vrType {\n\tcase TypeTimestamp:\n\t\tt, incr, err = vr.ReadTimestamp()\n\tcase TypeNull:\n\t\terr = vr.ReadNull()\n\tcase TypeUndefined:\n\t\terr = vr.ReadUndefined()\n\tdefault:\n\t\treturn emptyValue, fmt.Errorf(\"cannot decode %v into a Timestamp\", vrType)\n\t}\n\tif err != nil {\n\t\treturn emptyValue, err\n\t}\n\n\treturn reflect.ValueOf(Timestamp{T: t, I: incr}), nil\n}\n\n// timestampDecodeValue is the ValueDecoderFunc for Timestamp.\nfunc timestampDecodeValue(dc DecodeContext, vr ValueReader, val reflect.Value) error {\n\tif !val.CanSet() || val.Type() != tTimestamp {\n\t\treturn ValueDecoderError{Name: \"TimestampDecodeValue\", Types: []reflect.Type{tTimestamp}, Received: val}\n\t}\n\n\telem, err := timestampDecodeType(dc, vr, tTimestamp)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tval.Set(elem)\n\treturn nil\n}\n\nfunc minKeyDecodeType(_ DecodeContext, vr ValueReader, t reflect.Type) (reflect.Value, error) {\n\tif t != tMinKey {\n\t\treturn emptyValue, ValueDecoderError{\n\t\t\tName:     \"MinKeyDecodeValue\",\n\t\t\tTypes:    []reflect.Type{tMinKey},\n\t\t\tReceived: reflect.Zero(t),\n\t\t}\n\t}\n\n\tvar err error\n\tswitch vrType := vr.Type(); vrType {\n\tcase TypeMinKey:\n\t\terr = vr.ReadMinKey()\n\tcase TypeNull:\n\t\terr = vr.ReadNull()\n\tcase TypeUndefined:\n\t\terr = vr.ReadUndefined()\n\tdefault:\n\t\treturn emptyValue, fmt.Errorf(\"cannot decode %v into a MinKey\", vr.Type())\n\t}\n\tif err != nil {\n\t\treturn emptyValue, err\n\t}\n\n\treturn reflect.ValueOf(MinKey{}), nil\n}\n\n// minKeyDecodeValue is the ValueDecoderFunc for MinKey.\nfunc minKeyDecodeValue(dc DecodeContext, vr ValueReader, val reflect.Value) error {\n\tif !val.CanSet() || val.Type() != tMinKey {\n\t\treturn ValueDecoderError{Name: \"MinKeyDecodeValue\", Types: []reflect.Type{tMinKey}, Received: val}\n\t}\n\n\telem, err := minKeyDecodeType(dc, vr, tMinKey)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tval.Set(elem)\n\treturn nil\n}\n\nfunc maxKeyDecodeType(_ DecodeContext, vr ValueReader, t reflect.Type) (reflect.Value, error) {\n\tif t != tMaxKey {\n\t\treturn emptyValue, ValueDecoderError{\n\t\t\tName:     \"MaxKeyDecodeValue\",\n\t\t\tTypes:    []reflect.Type{tMaxKey},\n\t\t\tReceived: reflect.Zero(t),\n\t\t}\n\t}\n\n\tvar err error\n\tswitch vrType := vr.Type(); vrType {\n\tcase TypeMaxKey:\n\t\terr = vr.ReadMaxKey()\n\tcase TypeNull:\n\t\terr = vr.ReadNull()\n\tcase TypeUndefined:\n\t\terr = vr.ReadUndefined()\n\tdefault:\n\t\treturn emptyValue, fmt.Errorf(\"cannot decode %v into a MaxKey\", vr.Type())\n\t}\n\tif err != nil {\n\t\treturn emptyValue, err\n\t}\n\n\treturn reflect.ValueOf(MaxKey{}), nil\n}\n\n// maxKeyDecodeValue is the ValueDecoderFunc for MaxKey.\nfunc maxKeyDecodeValue(dc DecodeContext, vr ValueReader, val reflect.Value) error {\n\tif !val.CanSet() || val.Type() != tMaxKey {\n\t\treturn ValueDecoderError{Name: \"MaxKeyDecodeValue\", Types: []reflect.Type{tMaxKey}, Received: val}\n\t}\n\n\telem, err := maxKeyDecodeType(dc, vr, tMaxKey)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tval.Set(elem)\n\treturn nil\n}\n\nfunc decimal128DecodeType(_ DecodeContext, vr ValueReader, t reflect.Type) (reflect.Value, error) {\n\tif t != tDecimal {\n\t\treturn emptyValue, ValueDecoderError{\n\t\t\tName:     \"Decimal128DecodeValue\",\n\t\t\tTypes:    []reflect.Type{tDecimal},\n\t\t\tReceived: reflect.Zero(t),\n\t\t}\n\t}\n\n\tvar d128 Decimal128\n\tvar err error\n\tswitch vrType := vr.Type(); vrType {\n\tcase TypeDecimal128:\n\t\td128, err = vr.ReadDecimal128()\n\tcase TypeNull:\n\t\terr = vr.ReadNull()\n\tcase TypeUndefined:\n\t\terr = vr.ReadUndefined()\n\tdefault:\n\t\treturn emptyValue, fmt.Errorf(\"cannot decode %v into a Decimal128\", vr.Type())\n\t}\n\tif err != nil {\n\t\treturn emptyValue, err\n\t}\n\n\treturn reflect.ValueOf(d128), nil\n}\n\n// decimal128DecodeValue is the ValueDecoderFunc for Decimal128.\nfunc decimal128DecodeValue(dctx DecodeContext, vr ValueReader, val reflect.Value) error {\n\tif !val.CanSet() || val.Type() != tDecimal {\n\t\treturn ValueDecoderError{Name: \"Decimal128DecodeValue\", Types: []reflect.Type{tDecimal}, Received: val}\n\t}\n\n\telem, err := decimal128DecodeType(dctx, vr, tDecimal)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tval.Set(elem)\n\treturn nil\n}\n\nfunc jsonNumberDecodeType(_ DecodeContext, vr ValueReader, t reflect.Type) (reflect.Value, error) {\n\tif t != tJSONNumber {\n\t\treturn emptyValue, ValueDecoderError{\n\t\t\tName:     \"JSONNumberDecodeValue\",\n\t\t\tTypes:    []reflect.Type{tJSONNumber},\n\t\t\tReceived: reflect.Zero(t),\n\t\t}\n\t}\n\n\tvar jsonNum json.Number\n\tvar err error\n\tswitch vrType := vr.Type(); vrType {\n\tcase TypeDouble:\n\t\tf64, err := vr.ReadDouble()\n\t\tif err != nil {\n\t\t\treturn emptyValue, err\n\t\t}\n\t\tjsonNum = json.Number(strconv.FormatFloat(f64, 'f', -1, 64))\n\tcase TypeInt32:\n\t\ti32, err := vr.ReadInt32()\n\t\tif err != nil {\n\t\t\treturn emptyValue, err\n\t\t}\n\t\tjsonNum = json.Number(strconv.FormatInt(int64(i32), 10))\n\tcase TypeInt64:\n\t\ti64, err := vr.ReadInt64()\n\t\tif err != nil {\n\t\t\treturn emptyValue, err\n\t\t}\n\t\tjsonNum = json.Number(strconv.FormatInt(i64, 10))\n\tcase TypeNull:\n\t\terr = vr.ReadNull()\n\tcase TypeUndefined:\n\t\terr = vr.ReadUndefined()\n\tdefault:\n\t\treturn emptyValue, fmt.Errorf(\"cannot decode %v into a json.Number\", vrType)\n\t}\n\tif err != nil {\n\t\treturn emptyValue, err\n\t}\n\n\treturn reflect.ValueOf(jsonNum), nil\n}\n\n// jsonNumberDecodeValue is the ValueDecoderFunc for json.Number.\nfunc jsonNumberDecodeValue(dc DecodeContext, vr ValueReader, val reflect.Value) error {\n\tif !val.CanSet() || val.Type() != tJSONNumber {\n\t\treturn ValueDecoderError{Name: \"JSONNumberDecodeValue\", Types: []reflect.Type{tJSONNumber}, Received: val}\n\t}\n\n\telem, err := jsonNumberDecodeType(dc, vr, tJSONNumber)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tval.Set(elem)\n\treturn nil\n}\n\nfunc urlDecodeType(_ DecodeContext, vr ValueReader, t reflect.Type) (reflect.Value, error) {\n\tif t != tURL {\n\t\treturn emptyValue, ValueDecoderError{\n\t\t\tName:     \"URLDecodeValue\",\n\t\t\tTypes:    []reflect.Type{tURL},\n\t\t\tReceived: reflect.Zero(t),\n\t\t}\n\t}\n\n\turlPtr := &url.URL{}\n\tvar err error\n\tswitch vrType := vr.Type(); vrType {\n\tcase TypeString:\n\t\tvar str string // Declare str here to avoid shadowing err during the ReadString call.\n\t\tstr, err = vr.ReadString()\n\t\tif err != nil {\n\t\t\treturn emptyValue, err\n\t\t}\n\n\t\turlPtr, err = url.Parse(str)\n\tcase TypeNull:\n\t\terr = vr.ReadNull()\n\tcase TypeUndefined:\n\t\terr = vr.ReadUndefined()\n\tdefault:\n\t\treturn emptyValue, fmt.Errorf(\"cannot decode %v into a *url.URL\", vrType)\n\t}\n\tif err != nil {\n\t\treturn emptyValue, err\n\t}\n\n\treturn reflect.ValueOf(urlPtr).Elem(), nil\n}\n\n// urlDecodeValue is the ValueDecoderFunc for url.URL.\nfunc urlDecodeValue(dc DecodeContext, vr ValueReader, val reflect.Value) error {\n\tif !val.CanSet() || val.Type() != tURL {\n\t\treturn ValueDecoderError{Name: \"URLDecodeValue\", Types: []reflect.Type{tURL}, Received: val}\n\t}\n\n\telem, err := urlDecodeType(dc, vr, tURL)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tval.Set(elem)\n\treturn nil\n}\n\n// arrayDecodeValue is the ValueDecoderFunc for array types.\nfunc arrayDecodeValue(dc DecodeContext, vr ValueReader, val reflect.Value) error {\n\tif !val.IsValid() || val.Kind() != reflect.Array {\n\t\treturn ValueDecoderError{Name: \"ArrayDecodeValue\", Kinds: []reflect.Kind{reflect.Array}, Received: val}\n\t}\n\n\tswitch vrType := vr.Type(); vrType {\n\tcase TypeArray:\n\tcase Type(0), TypeEmbeddedDocument:\n\t\tif val.Type().Elem() != tE {\n\t\t\treturn fmt.Errorf(\"cannot decode document into %s\", val.Type())\n\t\t}\n\tcase TypeBinary:\n\t\tif val.Type().Elem() != tByte {\n\t\t\treturn fmt.Errorf(\"ArrayDecodeValue can only be used to decode binary into a byte array, got %v\", vrType)\n\t\t}\n\t\tdata, subtype, err := vr.ReadBinary()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif subtype != TypeBinaryGeneric && subtype != TypeBinaryBinaryOld {\n\t\t\treturn fmt.Errorf(\"ArrayDecodeValue can only be used to decode subtype 0x00 or 0x02 for %s, got %v\", TypeBinary, subtype)\n\t\t}\n\n\t\tif len(data) > val.Len() {\n\t\t\treturn fmt.Errorf(\"more elements returned in array than can fit inside %s\", val.Type())\n\t\t}\n\n\t\tfor idx, elem := range data {\n\t\t\tval.Index(idx).Set(reflect.ValueOf(elem))\n\t\t}\n\t\treturn nil\n\tcase TypeNull:\n\t\tval.Set(reflect.Zero(val.Type()))\n\t\treturn vr.ReadNull()\n\tcase TypeUndefined:\n\t\tval.Set(reflect.Zero(val.Type()))\n\t\treturn vr.ReadUndefined()\n\tdefault:\n\t\treturn fmt.Errorf(\"cannot decode %v into an array\", vrType)\n\t}\n\n\tvar elemsFunc func(DecodeContext, ValueReader, reflect.Value) ([]reflect.Value, error)\n\tswitch val.Type().Elem() {\n\tcase tE:\n\t\telemsFunc = decodeD\n\tdefault:\n\t\telemsFunc = decodeDefault\n\t}\n\n\telems, err := elemsFunc(dc, vr, val)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif len(elems) > val.Len() {\n\t\treturn fmt.Errorf(\"more elements returned in array than can fit inside %s, got %v elements\", val.Type(), len(elems))\n\t}\n\n\tfor idx, elem := range elems {\n\t\tval.Index(idx).Set(elem)\n\t}\n\n\treturn nil\n}\n\n// valueUnmarshalerDecodeValue is the ValueDecoderFunc for ValueUnmarshaler implementations.\nfunc valueUnmarshalerDecodeValue(_ DecodeContext, vr ValueReader, val reflect.Value) error {\n\tif !val.IsValid() || (!val.Type().Implements(tValueUnmarshaler) && !reflect.PtrTo(val.Type()).Implements(tValueUnmarshaler)) {\n\t\treturn ValueDecoderError{Name: \"ValueUnmarshalerDecodeValue\", Types: []reflect.Type{tValueUnmarshaler}, Received: val}\n\t}\n\n\t// If BSON value is null and the go value is a pointer, then don't call\n\t// UnmarshalBSONValue. Even if the Go pointer is already initialized (i.e.,\n\t// non-nil), encountering null in BSON will result in the pointer being\n\t// directly set to nil here. Since the pointer is being replaced with nil,\n\t// there is no opportunity (or reason) for the custom UnmarshalBSONValue logic\n\t// to be called.\n\tif vr.Type() == TypeNull && val.Kind() == reflect.Ptr {\n\t\tval.Set(reflect.Zero(val.Type()))\n\n\t\treturn vr.ReadNull()\n\t}\n\n\tif val.Kind() == reflect.Ptr && val.IsNil() {\n\t\tif !val.CanSet() {\n\t\t\treturn ValueDecoderError{Name: \"ValueUnmarshalerDecodeValue\", Types: []reflect.Type{tValueUnmarshaler}, Received: val}\n\t\t}\n\t\tval.Set(reflect.New(val.Type().Elem()))\n\t}\n\n\tif !val.Type().Implements(tValueUnmarshaler) {\n\t\tif !val.CanAddr() {\n\t\t\treturn ValueDecoderError{Name: \"ValueUnmarshalerDecodeValue\", Types: []reflect.Type{tValueUnmarshaler}, Received: val}\n\t\t}\n\t\tval = val.Addr() // If the type doesn't implement the interface, a pointer to it must.\n\t}\n\n\tt, src, err := copyValueToBytes(vr)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tm, ok := val.Interface().(ValueUnmarshaler)\n\tif !ok {\n\t\t// NB: this error should be unreachable due to the above checks\n\t\treturn ValueDecoderError{Name: \"ValueUnmarshalerDecodeValue\", Types: []reflect.Type{tValueUnmarshaler}, Received: val}\n\t}\n\treturn m.UnmarshalBSONValue(byte(t), src)\n}\n\n// unmarshalerDecodeValue is the ValueDecoderFunc for Unmarshaler implementations.\nfunc unmarshalerDecodeValue(_ DecodeContext, vr ValueReader, val reflect.Value) error {\n\tif !val.IsValid() || (!val.Type().Implements(tUnmarshaler) && !reflect.PtrTo(val.Type()).Implements(tUnmarshaler)) {\n\t\treturn ValueDecoderError{Name: \"UnmarshalerDecodeValue\", Types: []reflect.Type{tUnmarshaler}, Received: val}\n\t}\n\n\tif val.Kind() == reflect.Ptr && val.IsNil() {\n\t\tif !val.CanSet() {\n\t\t\treturn ValueDecoderError{Name: \"UnmarshalerDecodeValue\", Types: []reflect.Type{tUnmarshaler}, Received: val}\n\t\t}\n\t\tval.Set(reflect.New(val.Type().Elem()))\n\t}\n\n\t_, src, err := copyValueToBytes(vr)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// If the target Go value is a pointer and the BSON field value is empty, set the value to the\n\t// zero value of the pointer (nil) and don't call UnmarshalBSON. UnmarshalBSON has no way to\n\t// change the pointer value from within the function (only the value at the pointer address),\n\t// so it can't set the pointer to \"nil\" itself. Since the most common Go value for an empty BSON\n\t// field value is \"nil\", we set \"nil\" here and don't call UnmarshalBSON. This behavior matches\n\t// the behavior of the Go \"encoding/json\" unmarshaler when the target Go value is a pointer and\n\t// the JSON field value is \"null\".\n\tif val.Kind() == reflect.Ptr && len(src) == 0 {\n\t\tval.Set(reflect.Zero(val.Type()))\n\t\treturn nil\n\t}\n\n\tif !val.Type().Implements(tUnmarshaler) {\n\t\tif !val.CanAddr() {\n\t\t\treturn ValueDecoderError{Name: \"UnmarshalerDecodeValue\", Types: []reflect.Type{tUnmarshaler}, Received: val}\n\t\t}\n\t\tval = val.Addr() // If the type doesn't implement the interface, a pointer to it must.\n\t}\n\n\tm, ok := val.Interface().(Unmarshaler)\n\tif !ok {\n\t\t// NB: this error should be unreachable due to the above checks\n\t\treturn ValueDecoderError{Name: \"UnmarshalerDecodeValue\", Types: []reflect.Type{tUnmarshaler}, Received: val}\n\t}\n\treturn m.UnmarshalBSON(src)\n}\n\n// coreDocumentDecodeValue is the ValueDecoderFunc for bsoncore.Document.\nfunc coreDocumentDecodeValue(_ DecodeContext, vr ValueReader, val reflect.Value) error {\n\tif !val.CanSet() || val.Type() != tCoreDocument {\n\t\treturn ValueDecoderError{Name: \"CoreDocumentDecodeValue\", Types: []reflect.Type{tCoreDocument}, Received: val}\n\t}\n\tvrType := vr.Type()\n\tisDocument := vrType == Type(0) || vrType == TypeEmbeddedDocument || vrType == TypeArray\n\tif !isDocument {\n\t\treturn fmt.Errorf(\"cannot decode %v into a %s\", vrType, val.Type())\n\t}\n\n\tif val.IsNil() {\n\t\tval.Set(reflect.MakeSlice(val.Type(), 0, 0))\n\t}\n\n\tval.SetLen(0)\n\n\tcdoc, err := appendDocumentBytes(val.Interface().(bsoncore.Document), vr)\n\tval.Set(reflect.ValueOf(cdoc))\n\treturn err\n}\n\nfunc decodeDefault(dc DecodeContext, vr ValueReader, val reflect.Value) ([]reflect.Value, error) {\n\telems := make([]reflect.Value, 0)\n\n\tar, err := vr.ReadArray()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\teType := val.Type().Elem()\n\n\tisInterfaceSlice := eType.Kind() == reflect.Interface && val.Len() > 0\n\n\t// If this is not an interface slice with pre-populated elements, we can look up\n\t// the decoder for eType once.\n\tvar vDecoder ValueDecoder\n\tif !isInterfaceSlice {\n\t\tvDecoder, err = dc.LookupDecoder(eType)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\tidx := 0\n\tfor {\n\t\tvr, err := ar.ReadValue()\n\t\tif errors.Is(err, ErrEOA) {\n\t\t\tbreak\n\t\t}\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tvar elem reflect.Value\n\t\tif isInterfaceSlice && idx < val.Len() {\n\t\t\t// Decode into an existing any slot.\n\n\t\t\telem = val.Index(idx).Elem()\n\t\t\tswitch {\n\t\t\tcase elem.Kind() != reflect.Ptr || elem.IsNil():\n\t\t\t\tvalueDecoder, err := dc.LookupDecoder(elem.Type())\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\n\t\t\t\t// If an element is allocated and unsettable, it must be overwritten.\n\t\t\t\tif !elem.CanSet() {\n\t\t\t\t\telem = reflect.New(elem.Type()).Elem()\n\t\t\t\t}\n\n\t\t\t\terr = valueDecoder.DecodeValue(dc, vr, elem)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, newDecodeError(strconv.Itoa(idx), err)\n\t\t\t\t}\n\t\t\tcase vr.Type() == TypeNull:\n\t\t\t\tif err = vr.ReadNull(); err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t\telem = reflect.Zero(val.Index(idx).Type())\n\t\t\tdefault:\n\t\t\t\te := elem.Elem()\n\t\t\t\tvalueDecoder, err := dc.LookupDecoder(e.Type())\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t\terr = valueDecoder.DecodeValue(dc, vr, e)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, newDecodeError(strconv.Itoa(idx), err)\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\t// For non-interface slices, or if we've exhausted the pre-populated\n\t\t\t// slots, we create a fresh value.\n\n\t\t\tif vDecoder == nil {\n\t\t\t\tvDecoder, err = dc.LookupDecoder(eType)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t}\n\t\t\telem, err = decodeTypeOrValueWithInfo(vDecoder, dc, vr, eType)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, newDecodeError(strconv.Itoa(idx), err)\n\t\t\t}\n\t\t}\n\n\t\telems = append(elems, elem)\n\t\tidx++\n\t}\n\n\treturn elems, nil\n}\n\nfunc codeWithScopeDecodeType(dc DecodeContext, vr ValueReader, t reflect.Type) (reflect.Value, error) {\n\tif t != tCodeWithScope {\n\t\treturn emptyValue, ValueDecoderError{\n\t\t\tName:     \"CodeWithScopeDecodeValue\",\n\t\t\tTypes:    []reflect.Type{tCodeWithScope},\n\t\t\tReceived: reflect.Zero(t),\n\t\t}\n\t}\n\n\tvar cws CodeWithScope\n\tvar err error\n\tswitch vrType := vr.Type(); vrType {\n\tcase TypeCodeWithScope:\n\t\tcode, dr, err := vr.ReadCodeWithScope()\n\t\tif err != nil {\n\t\t\treturn emptyValue, err\n\t\t}\n\n\t\tscope := reflect.New(tD).Elem()\n\t\telems, err := decodeElemsFromDocumentReader(dc, dr)\n\t\tif err != nil {\n\t\t\treturn emptyValue, err\n\t\t}\n\n\t\tscope.Set(reflect.MakeSlice(tD, 0, len(elems)))\n\t\tscope.Set(reflect.Append(scope, elems...))\n\n\t\tcws = CodeWithScope{\n\t\t\tCode:  JavaScript(code),\n\t\t\tScope: scope.Interface().(D),\n\t\t}\n\tcase TypeNull:\n\t\terr = vr.ReadNull()\n\tcase TypeUndefined:\n\t\terr = vr.ReadUndefined()\n\tdefault:\n\t\treturn emptyValue, fmt.Errorf(\"cannot decode %v into a CodeWithScope\", vrType)\n\t}\n\tif err != nil {\n\t\treturn emptyValue, err\n\t}\n\n\treturn reflect.ValueOf(cws), nil\n}\n\n// codeWithScopeDecodeValue is the ValueDecoderFunc for CodeWithScope.\nfunc codeWithScopeDecodeValue(dc DecodeContext, vr ValueReader, val reflect.Value) error {\n\tif !val.CanSet() || val.Type() != tCodeWithScope {\n\t\treturn ValueDecoderError{Name: \"CodeWithScopeDecodeValue\", Types: []reflect.Type{tCodeWithScope}, Received: val}\n\t}\n\n\telem, err := codeWithScopeDecodeType(dc, vr, tCodeWithScope)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tval.Set(elem)\n\treturn nil\n}\n\nfunc decodeD(dc DecodeContext, vr ValueReader, _ reflect.Value) ([]reflect.Value, error) {\n\tswitch vr.Type() {\n\tcase Type(0), TypeEmbeddedDocument:\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"cannot decode %v into a D\", vr.Type())\n\t}\n\n\tdr, err := vr.ReadDocument()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn decodeElemsFromDocumentReader(dc, dr)\n}\n\nfunc decodeElemsFromDocumentReader(dc DecodeContext, dr DocumentReader) ([]reflect.Value, error) {\n\tdecoder, err := dc.LookupDecoder(tEmpty)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\telems := make([]reflect.Value, 0)\n\tfor {\n\t\tkey, vr, err := dr.ReadElement()\n\t\tif errors.Is(err, ErrEOD) {\n\t\t\tbreak\n\t\t}\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tval := reflect.New(tEmpty).Elem()\n\t\terr = decoder.DecodeValue(dc, vr, val)\n\t\tif err != nil {\n\t\t\treturn nil, newDecodeError(key, err)\n\t\t}\n\n\t\telems = append(elems, reflect.ValueOf(E{Key: key, Value: val.Interface()}))\n\t}\n\n\treturn elems, nil\n}\n"
  },
  {
    "path": "bson/default_value_decoders_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bson\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"math\"\n\t\"net/url\"\n\t\"reflect\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/google/go-cmp/cmp\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n)\n\nfunc TestDefaultValueDecoders(t *testing.T) {\n\twrong := func(string, string) string { return \"wrong\" }\n\n\ttype mybool bool\n\ttype myint8 int8\n\ttype myint16 int16\n\ttype myint32 int32\n\ttype myint64 int64\n\ttype myint int\n\ttype myuint8 uint8\n\ttype myuint16 uint16\n\ttype myuint32 uint32\n\ttype myuint64 uint64\n\ttype myuint uint\n\ttype myfloat32 float32\n\ttype myfloat64 float64\n\ttype mystring string\n\ttype mystruct struct{}\n\n\tconst cansetreflectiontest = \"cansetreflectiontest\"\n\tconst cansettest = \"cansettest\"\n\n\tnow := time.Now().Truncate(time.Millisecond)\n\td128 := NewDecimal128(12345, 67890)\n\tpbool := func(b bool) *bool { return &b }\n\tpi32 := func(i32 int32) *int32 { return &i32 }\n\tpi64 := func(i64 int64) *int64 { return &i64 }\n\n\ttype subtest struct {\n\t\tname   string\n\t\tval    any\n\t\tdctx   *DecodeContext\n\t\tllvrw  *valueReaderWriter\n\t\tinvoke invoked\n\t\terr    error\n\t}\n\n\ttestCases := []struct {\n\t\tname     string\n\t\tvd       ValueDecoder\n\t\tsubtests []subtest\n\t}{\n\t\t{\n\t\t\t\"BooleanDecodeValue\",\n\t\t\tValueDecoderFunc(booleanDecodeValue),\n\t\t\t[]subtest{\n\t\t\t\t{\n\t\t\t\t\t\"wrong type\",\n\t\t\t\t\twrong,\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeBoolean},\n\t\t\t\t\tnothing,\n\t\t\t\t\tValueDecoderError{\n\t\t\t\t\t\tName:     \"BooleanDecodeValue\",\n\t\t\t\t\t\tKinds:    []reflect.Kind{reflect.Bool},\n\t\t\t\t\t\tReceived: reflect.New(reflect.TypeOf(wrong)).Elem(),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type not boolean\",\n\t\t\t\t\tbool(false),\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeString},\n\t\t\t\t\tnothing,\n\t\t\t\t\tfmt.Errorf(\"cannot decode %v into a boolean\", TypeString),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"fast path\",\n\t\t\t\t\tbool(true),\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeBoolean, Return: bool(true)},\n\t\t\t\t\treadBoolean,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"reflection path\",\n\t\t\t\t\tmybool(true),\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeBoolean, Return: bool(true)},\n\t\t\t\t\treadBoolean,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"reflection path error\",\n\t\t\t\t\tmybool(true),\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeBoolean, Return: bool(true), Err: errors.New(\"ReadBoolean Error\"), ErrAfter: readBoolean},\n\t\t\t\t\treadBoolean, errors.New(\"ReadBoolean Error\"),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"can set false\",\n\t\t\t\t\tcansettest,\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeBoolean},\n\t\t\t\t\tnothing,\n\t\t\t\t\tValueDecoderError{Name: \"BooleanDecodeValue\", Kinds: []reflect.Kind{reflect.Bool}},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"decode null\",\n\t\t\t\t\tmybool(false),\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeNull},\n\t\t\t\t\treadNull,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"decode undefined\",\n\t\t\t\t\tmybool(false),\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeUndefined},\n\t\t\t\t\treadUndefined,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"IntDecodeValue\",\n\t\t\tValueDecoderFunc(intDecodeValue),\n\t\t\t[]subtest{\n\t\t\t\t{\n\t\t\t\t\t\"wrong type\",\n\t\t\t\t\twrong,\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeInt32, Return: int32(0)},\n\t\t\t\t\treadInt32,\n\t\t\t\t\tValueDecoderError{\n\t\t\t\t\t\tName:     \"IntDecodeValue\",\n\t\t\t\t\t\tKinds:    []reflect.Kind{reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int},\n\t\t\t\t\t\tReceived: reflect.ValueOf(wrong),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type not int32/int64\",\n\t\t\t\t\t0,\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeString},\n\t\t\t\t\tnothing,\n\t\t\t\t\tfmt.Errorf(\"cannot decode %v into an integer type\", TypeString),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"ReadInt32 error\",\n\t\t\t\t\t0,\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeInt32, Return: int32(0), Err: errors.New(\"ReadInt32 error\"), ErrAfter: readInt32},\n\t\t\t\t\treadInt32,\n\t\t\t\t\terrors.New(\"ReadInt32 error\"),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"ReadInt64 error\",\n\t\t\t\t\t0,\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeInt64, Return: int64(0), Err: errors.New(\"ReadInt64 error\"), ErrAfter: readInt64},\n\t\t\t\t\treadInt64,\n\t\t\t\t\terrors.New(\"ReadInt64 error\"),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"ReadDouble error\",\n\t\t\t\t\t0,\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeDouble, Return: float64(0), Err: errors.New(\"ReadDouble error\"), ErrAfter: readDouble},\n\t\t\t\t\treadDouble,\n\t\t\t\t\terrors.New(\"ReadDouble error\"),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"ReadDouble\", int64(3), &DecodeContext{},\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeDouble, Return: float64(3.00)}, readDouble,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"ReadDouble (truncate)\", int64(3), &DecodeContext{truncate: true},\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeDouble, Return: float64(3.14)}, readDouble,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"ReadDouble (no truncate)\", int64(0), nil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeDouble, Return: float64(3.14)}, readDouble,\n\t\t\t\t\terrCannotTruncate,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"ReadDouble overflows int64\", int64(0), nil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeDouble, Return: math.MaxFloat64}, readDouble,\n\t\t\t\t\tfmt.Errorf(\"%g overflows int64\", math.MaxFloat64),\n\t\t\t\t},\n\t\t\t\t{\"int8/fast path\", int8(127), nil, &valueReaderWriter{BSONType: TypeInt32, Return: int32(127)}, readInt32, nil},\n\t\t\t\t{\"int16/fast path\", int16(32676), nil, &valueReaderWriter{BSONType: TypeInt32, Return: int32(32676)}, readInt32, nil},\n\t\t\t\t{\"int32/fast path\", int32(1234), nil, &valueReaderWriter{BSONType: TypeInt32, Return: int32(1234)}, readInt32, nil},\n\t\t\t\t{\"int64/fast path\", int64(1234), nil, &valueReaderWriter{BSONType: TypeInt64, Return: int64(1234)}, readInt64, nil},\n\t\t\t\t{\"int/fast path\", int(1234), nil, &valueReaderWriter{BSONType: TypeInt64, Return: int64(1234)}, readInt64, nil},\n\t\t\t\t{\n\t\t\t\t\t\"int8/fast path - nil\", (*int8)(nil), nil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeInt32, Return: int32(0)}, readInt32,\n\t\t\t\t\tValueDecoderError{\n\t\t\t\t\t\tName:     \"IntDecodeValue\",\n\t\t\t\t\t\tKinds:    []reflect.Kind{reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int},\n\t\t\t\t\t\tReceived: reflect.ValueOf((*int8)(nil)),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"int16/fast path - nil\", (*int16)(nil), nil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeInt32, Return: int32(0)}, readInt32,\n\t\t\t\t\tValueDecoderError{\n\t\t\t\t\t\tName:     \"IntDecodeValue\",\n\t\t\t\t\t\tKinds:    []reflect.Kind{reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int},\n\t\t\t\t\t\tReceived: reflect.ValueOf((*int16)(nil)),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"int32/fast path - nil\", (*int32)(nil), nil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeInt32, Return: int32(0)}, readInt32,\n\t\t\t\t\tValueDecoderError{\n\t\t\t\t\t\tName:     \"IntDecodeValue\",\n\t\t\t\t\t\tKinds:    []reflect.Kind{reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int},\n\t\t\t\t\t\tReceived: reflect.ValueOf((*int32)(nil)),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"int64/fast path - nil\", (*int64)(nil), nil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeInt32, Return: int32(0)}, readInt32,\n\t\t\t\t\tValueDecoderError{\n\t\t\t\t\t\tName:     \"IntDecodeValue\",\n\t\t\t\t\t\tKinds:    []reflect.Kind{reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int},\n\t\t\t\t\t\tReceived: reflect.ValueOf((*int64)(nil)),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"int/fast path - nil\", (*int)(nil), nil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeInt32, Return: int32(0)}, readInt32,\n\t\t\t\t\tValueDecoderError{\n\t\t\t\t\t\tName:     \"IntDecodeValue\",\n\t\t\t\t\t\tKinds:    []reflect.Kind{reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int},\n\t\t\t\t\t\tReceived: reflect.ValueOf((*int)(nil)),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"int8/fast path - overflow\", int8(0), nil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeInt32, Return: int32(129)}, readInt32,\n\t\t\t\t\tfmt.Errorf(\"%d overflows int8\", 129),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"int16/fast path - overflow\", int16(0), nil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeInt32, Return: int32(32768)}, readInt32,\n\t\t\t\t\tfmt.Errorf(\"%d overflows int16\", 32768),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"int32/fast path - overflow\", int32(0), nil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeInt64, Return: int64(2147483648)}, readInt64,\n\t\t\t\t\tfmt.Errorf(\"%d overflows int32\", int64(2147483648)),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"int8/fast path - overflow (negative)\", int8(0), nil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeInt32, Return: int32(-129)}, readInt32,\n\t\t\t\t\tfmt.Errorf(\"%d overflows int8\", -129),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"int16/fast path - overflow (negative)\", int16(0), nil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeInt32, Return: int32(-32769)}, readInt32,\n\t\t\t\t\tfmt.Errorf(\"%d overflows int16\", -32769),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"int32/fast path - overflow (negative)\", int32(0), nil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeInt64, Return: int64(-2147483649)}, readInt64,\n\t\t\t\t\tfmt.Errorf(\"%d overflows int32\", int64(-2147483649)),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"int8/reflection path\", myint8(127), nil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeInt32, Return: int32(127)}, readInt32,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"int16/reflection path\", myint16(255), nil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeInt32, Return: int32(255)}, readInt32,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"int32/reflection path\", myint32(511), nil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeInt32, Return: int32(511)}, readInt32,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"int64/reflection path\", myint64(1023), nil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeInt32, Return: int32(1023)}, readInt32,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"int/reflection path\", myint(2047), nil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeInt32, Return: int32(2047)}, readInt32,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"int8/reflection path - overflow\", myint8(0), nil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeInt32, Return: int32(129)}, readInt32,\n\t\t\t\t\tfmt.Errorf(\"%d overflows int8\", 129),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"int16/reflection path - overflow\", myint16(0), nil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeInt32, Return: int32(32768)}, readInt32,\n\t\t\t\t\tfmt.Errorf(\"%d overflows int16\", 32768),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"int32/reflection path - overflow\", myint32(0), nil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeInt64, Return: int64(2147483648)}, readInt64,\n\t\t\t\t\tfmt.Errorf(\"%d overflows int32\", int64(2147483648)),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"int8/reflection path - overflow (negative)\", myint8(0), nil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeInt32, Return: int32(-129)}, readInt32,\n\t\t\t\t\tfmt.Errorf(\"%d overflows int8\", -129),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"int16/reflection path - overflow (negative)\", myint16(0), nil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeInt32, Return: int32(-32769)}, readInt32,\n\t\t\t\t\tfmt.Errorf(\"%d overflows int16\", -32769),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"int32/reflection path - overflow (negative)\", myint32(0), nil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeInt64, Return: int64(-2147483649)}, readInt64,\n\t\t\t\t\tfmt.Errorf(\"%d overflows int32\", int64(-2147483649)),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"can set false\",\n\t\t\t\t\tcansettest,\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeInt32, Return: int32(0)},\n\t\t\t\t\tnothing,\n\t\t\t\t\tValueDecoderError{\n\t\t\t\t\t\tName:  \"IntDecodeValue\",\n\t\t\t\t\t\tKinds: []reflect.Kind{reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"decode null\",\n\t\t\t\t\tmyint(0),\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeNull},\n\t\t\t\t\treadNull,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"decode undefined\",\n\t\t\t\t\tmyint(0),\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeUndefined},\n\t\t\t\t\treadUndefined,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"defaultUIntCodec.DecodeValue\",\n\t\t\t&uintCodec{},\n\t\t\t[]subtest{\n\t\t\t\t{\n\t\t\t\t\t\"wrong type\",\n\t\t\t\t\twrong,\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeInt32, Return: int32(0)},\n\t\t\t\t\treadInt32,\n\t\t\t\t\tValueDecoderError{\n\t\t\t\t\t\tName:     \"UintDecodeValue\",\n\t\t\t\t\t\tKinds:    []reflect.Kind{reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint},\n\t\t\t\t\t\tReceived: reflect.ValueOf(wrong),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type not int32/int64\",\n\t\t\t\t\t0,\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeString},\n\t\t\t\t\tnothing,\n\t\t\t\t\tfmt.Errorf(\"cannot decode %v into an integer type\", TypeString),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"ReadInt32 error\",\n\t\t\t\t\tuint(0),\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeInt32, Return: int32(0), Err: errors.New(\"ReadInt32 error\"), ErrAfter: readInt32},\n\t\t\t\t\treadInt32,\n\t\t\t\t\terrors.New(\"ReadInt32 error\"),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"ReadInt64 error\",\n\t\t\t\t\tuint(0),\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeInt64, Return: int64(0), Err: errors.New(\"ReadInt64 error\"), ErrAfter: readInt64},\n\t\t\t\t\treadInt64,\n\t\t\t\t\terrors.New(\"ReadInt64 error\"),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"ReadDouble error\",\n\t\t\t\t\t0,\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeDouble, Return: float64(0), Err: errors.New(\"ReadDouble error\"), ErrAfter: readDouble},\n\t\t\t\t\treadDouble,\n\t\t\t\t\terrors.New(\"ReadDouble error\"),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"ReadDouble\", uint64(3), &DecodeContext{},\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeDouble, Return: float64(3.00)}, readDouble,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"ReadDouble (truncate)\", uint64(3), &DecodeContext{truncate: true},\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeDouble, Return: float64(3.14)}, readDouble,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"ReadDouble (no truncate)\", uint64(0), nil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeDouble, Return: float64(3.14)}, readDouble,\n\t\t\t\t\terrCannotTruncate,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"ReadDouble overflows int64\", uint64(0), nil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeDouble, Return: math.MaxFloat64}, readDouble,\n\t\t\t\t\tfmt.Errorf(\"%g overflows int64\", math.MaxFloat64),\n\t\t\t\t},\n\t\t\t\t{\"uint8/fast path\", uint8(127), nil, &valueReaderWriter{BSONType: TypeInt32, Return: int32(127)}, readInt32, nil},\n\t\t\t\t{\"uint16/fast path\", uint16(255), nil, &valueReaderWriter{BSONType: TypeInt32, Return: int32(255)}, readInt32, nil},\n\t\t\t\t{\"uint32/fast path\", uint32(1234), nil, &valueReaderWriter{BSONType: TypeInt32, Return: int32(1234)}, readInt32, nil},\n\t\t\t\t{\"uint64/fast path\", uint64(1234), nil, &valueReaderWriter{BSONType: TypeInt64, Return: int64(1234)}, readInt64, nil},\n\t\t\t\t{\"uint/fast path\", uint(1234), nil, &valueReaderWriter{BSONType: TypeInt64, Return: int64(1234)}, readInt64, nil},\n\t\t\t\t{\n\t\t\t\t\t\"uint8/fast path - nil\", (*uint8)(nil), nil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeInt32, Return: int32(0)}, readInt32,\n\t\t\t\t\tValueDecoderError{\n\t\t\t\t\t\tName:     \"UintDecodeValue\",\n\t\t\t\t\t\tKinds:    []reflect.Kind{reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint},\n\t\t\t\t\t\tReceived: reflect.ValueOf((*uint8)(nil)),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"uint16/fast path - nil\", (*uint16)(nil), nil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeInt32, Return: int32(0)}, readInt32,\n\t\t\t\t\tValueDecoderError{\n\t\t\t\t\t\tName:     \"UintDecodeValue\",\n\t\t\t\t\t\tKinds:    []reflect.Kind{reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint},\n\t\t\t\t\t\tReceived: reflect.ValueOf((*uint16)(nil)),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"uint32/fast path - nil\", (*uint32)(nil), nil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeInt32, Return: int32(0)}, readInt32,\n\t\t\t\t\tValueDecoderError{\n\t\t\t\t\t\tName:     \"UintDecodeValue\",\n\t\t\t\t\t\tKinds:    []reflect.Kind{reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint},\n\t\t\t\t\t\tReceived: reflect.ValueOf((*uint32)(nil)),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"uint64/fast path - nil\", (*uint64)(nil), nil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeInt32, Return: int32(0)}, readInt32,\n\t\t\t\t\tValueDecoderError{\n\t\t\t\t\t\tName:     \"UintDecodeValue\",\n\t\t\t\t\t\tKinds:    []reflect.Kind{reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint},\n\t\t\t\t\t\tReceived: reflect.ValueOf((*uint64)(nil)),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"uint/fast path - nil\", (*uint)(nil), nil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeInt32, Return: int32(0)}, readInt32,\n\t\t\t\t\tValueDecoderError{\n\t\t\t\t\t\tName:     \"UintDecodeValue\",\n\t\t\t\t\t\tKinds:    []reflect.Kind{reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint},\n\t\t\t\t\t\tReceived: reflect.ValueOf((*uint)(nil)),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"uint8/fast path - overflow\", uint8(0), nil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeInt32, Return: int32(1 << 8)}, readInt32,\n\t\t\t\t\tfmt.Errorf(\"%d overflows uint8\", 1<<8),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"uint16/fast path - overflow\", uint16(0), nil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeInt32, Return: int32(1 << 16)}, readInt32,\n\t\t\t\t\tfmt.Errorf(\"%d overflows uint16\", 1<<16),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"uint32/fast path - overflow\", uint32(0), nil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeInt64, Return: int64(1 << 32)}, readInt64,\n\t\t\t\t\tfmt.Errorf(\"%d overflows uint32\", int64(1<<32)),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"uint8/fast path - overflow (negative)\", uint8(0), nil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeInt32, Return: int32(-1)}, readInt32,\n\t\t\t\t\tfmt.Errorf(\"%d overflows uint8\", -1),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"uint16/fast path - overflow (negative)\", uint16(0), nil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeInt32, Return: int32(-1)}, readInt32,\n\t\t\t\t\tfmt.Errorf(\"%d overflows uint16\", -1),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"uint32/fast path - overflow (negative)\", uint32(0), nil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeInt64, Return: int64(-1)}, readInt64,\n\t\t\t\t\tfmt.Errorf(\"%d overflows uint32\", -1),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"uint64/fast path - overflow (negative)\", uint64(0), nil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeInt64, Return: int64(-1)}, readInt64,\n\t\t\t\t\tfmt.Errorf(\"%d overflows uint64\", -1),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"uint/fast path - overflow (negative)\", uint(0), nil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeInt64, Return: int64(-1)}, readInt64,\n\t\t\t\t\tfmt.Errorf(\"%d overflows uint\", -1),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"uint8/reflection path\", myuint8(127), nil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeInt32, Return: int32(127)}, readInt32,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"uint16/reflection path\", myuint16(255), nil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeInt32, Return: int32(255)}, readInt32,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"uint32/reflection path\", myuint32(511), nil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeInt32, Return: int32(511)}, readInt32,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"uint64/reflection path\", myuint64(1023), nil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeInt32, Return: int32(1023)}, readInt32,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"uint/reflection path\", myuint(2047), nil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeInt32, Return: int32(2047)}, readInt32,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"uint8/reflection path - overflow\", myuint8(0), nil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeInt32, Return: int32(1 << 8)}, readInt32,\n\t\t\t\t\tfmt.Errorf(\"%d overflows uint8\", 1<<8),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"uint16/reflection path - overflow\", myuint16(0), nil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeInt32, Return: int32(1 << 16)}, readInt32,\n\t\t\t\t\tfmt.Errorf(\"%d overflows uint16\", 1<<16),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"uint32/reflection path - overflow\", myuint32(0), nil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeInt64, Return: int64(1 << 32)}, readInt64,\n\t\t\t\t\tfmt.Errorf(\"%d overflows uint32\", int64(1<<32)),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"uint8/reflection path - overflow (negative)\", myuint8(0), nil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeInt32, Return: int32(-1)}, readInt32,\n\t\t\t\t\tfmt.Errorf(\"%d overflows uint8\", -1),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"uint16/reflection path - overflow (negative)\", myuint16(0), nil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeInt32, Return: int32(-1)}, readInt32,\n\t\t\t\t\tfmt.Errorf(\"%d overflows uint16\", -1),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"uint32/reflection path - overflow (negative)\", myuint32(0), nil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeInt64, Return: int64(-1)}, readInt64,\n\t\t\t\t\tfmt.Errorf(\"%d overflows uint32\", -1),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"uint64/reflection path - overflow (negative)\", myuint64(0), nil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeInt64, Return: int64(-1)}, readInt64,\n\t\t\t\t\tfmt.Errorf(\"%d overflows uint64\", -1),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"uint/reflection path - overflow (negative)\", myuint(0), nil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeInt64, Return: int64(-1)}, readInt64,\n\t\t\t\t\tfmt.Errorf(\"%d overflows uint\", -1),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"can set false\",\n\t\t\t\t\tcansettest,\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeInt32, Return: int32(0)},\n\t\t\t\t\tnothing,\n\t\t\t\t\tValueDecoderError{\n\t\t\t\t\t\tName:  \"UintDecodeValue\",\n\t\t\t\t\t\tKinds: []reflect.Kind{reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"FloatDecodeValue\",\n\t\t\tValueDecoderFunc(floatDecodeValue),\n\t\t\t[]subtest{\n\t\t\t\t{\n\t\t\t\t\t\"wrong type\",\n\t\t\t\t\twrong,\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeDouble, Return: float64(0)},\n\t\t\t\t\treadDouble,\n\t\t\t\t\tValueDecoderError{\n\t\t\t\t\t\tName:     \"FloatDecodeValue\",\n\t\t\t\t\t\tKinds:    []reflect.Kind{reflect.Float32, reflect.Float64},\n\t\t\t\t\t\tReceived: reflect.ValueOf(wrong),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type not double\",\n\t\t\t\t\t0,\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeString},\n\t\t\t\t\tnothing,\n\t\t\t\t\tfmt.Errorf(\"cannot decode %v into a float32 or float64 type\", TypeString),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"ReadDouble error\",\n\t\t\t\t\tfloat64(0),\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeDouble, Return: float64(0), Err: errors.New(\"ReadDouble error\"), ErrAfter: readDouble},\n\t\t\t\t\treadDouble,\n\t\t\t\t\terrors.New(\"ReadDouble error\"),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"ReadInt32 error\",\n\t\t\t\t\tfloat64(0),\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeInt32, Return: int32(0), Err: errors.New(\"ReadInt32 error\"), ErrAfter: readInt32},\n\t\t\t\t\treadInt32,\n\t\t\t\t\terrors.New(\"ReadInt32 error\"),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"ReadInt64 error\",\n\t\t\t\t\tfloat64(0),\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeInt64, Return: int64(0), Err: errors.New(\"ReadInt64 error\"), ErrAfter: readInt64},\n\t\t\t\t\treadInt64,\n\t\t\t\t\terrors.New(\"ReadInt64 error\"),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"float64/int32\", float32(32.0), nil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeInt32, Return: int32(32)}, readInt32,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"float64/int64\", float32(64.0), nil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeInt64, Return: int64(64)}, readInt64,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"float32/fast path (equal)\", float32(3.0), nil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeDouble, Return: float64(3.0)}, readDouble,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"float64/fast path\", float64(3.14159), nil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeDouble, Return: float64(3.14159)}, readDouble,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"float32/fast path (truncate)\", float32(3.14), &DecodeContext{truncate: true},\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeDouble, Return: float64(3.14)}, readDouble,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"float32/fast path (no truncate)\", float32(0), nil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeDouble, Return: float64(3.14)}, readDouble,\n\t\t\t\t\terrCannotTruncate,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"float32/fast path - nil\", (*float32)(nil), nil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeDouble, Return: float64(0)}, readDouble,\n\t\t\t\t\tValueDecoderError{\n\t\t\t\t\t\tName:     \"FloatDecodeValue\",\n\t\t\t\t\t\tKinds:    []reflect.Kind{reflect.Float32, reflect.Float64},\n\t\t\t\t\t\tReceived: reflect.ValueOf((*float32)(nil)),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"float64/fast path - nil\", (*float64)(nil), nil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeDouble, Return: float64(0)}, readDouble,\n\t\t\t\t\tValueDecoderError{\n\t\t\t\t\t\tName:     \"FloatDecodeValue\",\n\t\t\t\t\t\tKinds:    []reflect.Kind{reflect.Float32, reflect.Float64},\n\t\t\t\t\t\tReceived: reflect.ValueOf((*float64)(nil)),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"float32/reflection path (equal)\", myfloat32(3.0), nil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeDouble, Return: float64(3.0)}, readDouble,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"float64/reflection path\", myfloat64(3.14159), nil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeDouble, Return: float64(3.14159)}, readDouble,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"float32/reflection path (truncate)\", myfloat32(3.14), &DecodeContext{truncate: true},\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeDouble, Return: float64(3.14)}, readDouble,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"float32/reflection path (no truncate)\", myfloat32(0), nil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeDouble, Return: float64(3.14)}, readDouble,\n\t\t\t\t\terrCannotTruncate,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"can set false\",\n\t\t\t\t\tcansettest,\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeDouble, Return: float64(0)},\n\t\t\t\t\tnothing,\n\t\t\t\t\tValueDecoderError{\n\t\t\t\t\t\tName:  \"FloatDecodeValue\",\n\t\t\t\t\t\tKinds: []reflect.Kind{reflect.Float32, reflect.Float64},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"defaultTimeCodec.DecodeValue\",\n\t\t\t&timeCodec{},\n\t\t\t[]subtest{\n\t\t\t\t{\n\t\t\t\t\t\"wrong type\",\n\t\t\t\t\twrong,\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeDateTime, Return: int64(0)},\n\t\t\t\t\tnothing,\n\t\t\t\t\tValueDecoderError{\n\t\t\t\t\t\tName:     \"TimeDecodeValue\",\n\t\t\t\t\t\tTypes:    []reflect.Type{tTime},\n\t\t\t\t\t\tReceived: reflect.New(reflect.TypeOf(wrong)).Elem(),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"ReadDateTime error\",\n\t\t\t\t\ttime.Time{},\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeDateTime, Return: int64(0), Err: errors.New(\"ReadDateTime error\"), ErrAfter: readDateTime},\n\t\t\t\t\treadDateTime,\n\t\t\t\t\terrors.New(\"ReadDateTime error\"),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"time.Time\",\n\t\t\t\t\tnow,\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeDateTime, Return: now.UnixNano() / int64(time.Millisecond)},\n\t\t\t\t\treadDateTime,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"can set false\",\n\t\t\t\t\tcansettest,\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeDateTime, Return: int64(0)},\n\t\t\t\t\tnothing,\n\t\t\t\t\tValueDecoderError{Name: \"TimeDecodeValue\", Types: []reflect.Type{tTime}},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"decode null\",\n\t\t\t\t\ttime.Time{},\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeNull},\n\t\t\t\t\treadNull,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"decode undefined\",\n\t\t\t\t\ttime.Time{},\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeUndefined},\n\t\t\t\t\treadUndefined,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"defaultMapCodec.DecodeValue\",\n\t\t\t&mapCodec{},\n\t\t\t[]subtest{\n\t\t\t\t{\n\t\t\t\t\t\"wrong kind\",\n\t\t\t\t\twrong,\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{},\n\t\t\t\t\tnothing,\n\t\t\t\t\tValueDecoderError{\n\t\t\t\t\t\tName:     \"MapDecodeValue\",\n\t\t\t\t\t\tKinds:    []reflect.Kind{reflect.Map},\n\t\t\t\t\t\tReceived: reflect.New(reflect.TypeOf(wrong)).Elem(),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"wrong kind (non-string key)\",\n\t\t\t\t\tmap[bool]any{},\n\t\t\t\t\t&DecodeContext{Registry: buildDefaultRegistry()},\n\t\t\t\t\t&valueReaderWriter{},\n\t\t\t\t\treadElement,\n\t\t\t\t\tfmt.Errorf(\"unsupported key type: %T\", false),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"ReadDocument Error\",\n\t\t\t\t\tmake(map[string]any),\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{Err: errors.New(\"rd error\"), ErrAfter: readDocument},\n\t\t\t\t\treadDocument,\n\t\t\t\t\terrors.New(\"rd error\"),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"Lookup Error\",\n\t\t\t\t\tmap[string]string{},\n\t\t\t\t\t&DecodeContext{Registry: newTestRegistry()},\n\t\t\t\t\t&valueReaderWriter{},\n\t\t\t\t\treadDocument,\n\t\t\t\t\terrNoDecoder{Type: reflect.TypeOf(\"\")},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"ReadElement Error\",\n\t\t\t\t\tmake(map[string]any),\n\t\t\t\t\t&DecodeContext{Registry: buildDefaultRegistry()},\n\t\t\t\t\t&valueReaderWriter{Err: errors.New(\"re error\"), ErrAfter: readElement},\n\t\t\t\t\treadElement,\n\t\t\t\t\terrors.New(\"re error\"),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"can set false\",\n\t\t\t\t\tcansettest,\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{},\n\t\t\t\t\tnothing,\n\t\t\t\t\tValueDecoderError{Name: \"MapDecodeValue\", Kinds: []reflect.Kind{reflect.Map}},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"wrong BSON type\",\n\t\t\t\t\tmap[string]any{},\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeString},\n\t\t\t\t\tnothing,\n\t\t\t\t\terrors.New(\"cannot decode string into a map[string]interface {}\"),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"decode null\",\n\t\t\t\t\t(map[string]any)(nil),\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeNull},\n\t\t\t\t\treadNull,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"decode undefined\",\n\t\t\t\t\t(map[string]any)(nil),\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeUndefined},\n\t\t\t\t\treadUndefined,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"ArrayDecodeValue\",\n\t\t\tValueDecoderFunc(arrayDecodeValue),\n\t\t\t[]subtest{\n\t\t\t\t{\n\t\t\t\t\t\"wrong kind\",\n\t\t\t\t\twrong,\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{},\n\t\t\t\t\tnothing,\n\t\t\t\t\tValueDecoderError{\n\t\t\t\t\t\tName:     \"ArrayDecodeValue\",\n\t\t\t\t\t\tKinds:    []reflect.Kind{reflect.Array},\n\t\t\t\t\t\tReceived: reflect.New(reflect.TypeOf(wrong)).Elem(),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"can set false\",\n\t\t\t\t\tcansettest,\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{},\n\t\t\t\t\tnothing,\n\t\t\t\t\tValueDecoderError{Name: \"ArrayDecodeValue\", Kinds: []reflect.Kind{reflect.Array}},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"Not Type Array\",\n\t\t\t\t\t[1]any{},\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeString},\n\t\t\t\t\tnothing,\n\t\t\t\t\terrors.New(\"cannot decode string into an array\"),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"ReadArray Error\",\n\t\t\t\t\t[1]any{},\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{Err: errors.New(\"ra error\"), ErrAfter: readArray, BSONType: TypeArray},\n\t\t\t\t\treadArray,\n\t\t\t\t\terrors.New(\"ra error\"),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"Lookup Error\",\n\t\t\t\t\t[1]string{},\n\t\t\t\t\t&DecodeContext{Registry: newTestRegistry()},\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeArray},\n\t\t\t\t\treadArray,\n\t\t\t\t\terrNoDecoder{Type: reflect.TypeOf(\"\")},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"ReadValue Error\",\n\t\t\t\t\t[1]string{},\n\t\t\t\t\t&DecodeContext{Registry: buildDefaultRegistry()},\n\t\t\t\t\t&valueReaderWriter{Err: errors.New(\"rv error\"), ErrAfter: readValue, BSONType: TypeArray},\n\t\t\t\t\treadValue,\n\t\t\t\t\terrors.New(\"rv error\"),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"DecodeValue Error\",\n\t\t\t\t\t[1]string{},\n\t\t\t\t\t&DecodeContext{Registry: buildDefaultRegistry()},\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeArray},\n\t\t\t\t\treadValue,\n\t\t\t\t\t&DecodeError{keys: []string{\"0\"}, wrapped: errors.New(\"cannot decode array into a string type\")},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"Document but not D\",\n\t\t\t\t\t[1]string{},\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: Type(0)},\n\t\t\t\t\tnothing,\n\t\t\t\t\terrors.New(\"cannot decode document into [1]string\"),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"EmbeddedDocument but not D\",\n\t\t\t\t\t[1]string{},\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeEmbeddedDocument},\n\t\t\t\t\tnothing,\n\t\t\t\t\terrors.New(\"cannot decode document into [1]string\"),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"decode null\",\n\t\t\t\t\t[1]string{},\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeNull},\n\t\t\t\t\treadNull,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"decode undefined\",\n\t\t\t\t\t[1]string{},\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeUndefined},\n\t\t\t\t\treadUndefined,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"defaultSliceCodec.DecodeValue\",\n\t\t\t&sliceCodec{},\n\t\t\t[]subtest{\n\t\t\t\t{\n\t\t\t\t\t\"wrong kind\",\n\t\t\t\t\twrong,\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{},\n\t\t\t\t\tnothing,\n\t\t\t\t\tValueDecoderError{\n\t\t\t\t\t\tName:     \"SliceDecodeValue\",\n\t\t\t\t\t\tKinds:    []reflect.Kind{reflect.Slice},\n\t\t\t\t\t\tReceived: reflect.New(reflect.TypeOf(wrong)).Elem(),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"can set false\",\n\t\t\t\t\tcansettest,\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{},\n\t\t\t\t\tnothing,\n\t\t\t\t\tValueDecoderError{Name: \"SliceDecodeValue\", Kinds: []reflect.Kind{reflect.Slice}},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"Not Type Array\",\n\t\t\t\t\t[]any{},\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeInt32},\n\t\t\t\t\tnothing,\n\t\t\t\t\terrors.New(\"cannot decode 32-bit integer into a slice\"),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"ReadArray Error\",\n\t\t\t\t\t[]any{},\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{Err: errors.New(\"ra error\"), ErrAfter: readArray, BSONType: TypeArray},\n\t\t\t\t\treadArray,\n\t\t\t\t\terrors.New(\"ra error\"),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"Lookup Error\",\n\t\t\t\t\t[]string{},\n\t\t\t\t\t&DecodeContext{Registry: newTestRegistry()},\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeArray},\n\t\t\t\t\treadArray,\n\t\t\t\t\terrNoDecoder{Type: reflect.TypeOf(\"\")},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"ReadValue Error\",\n\t\t\t\t\t[]string{},\n\t\t\t\t\t&DecodeContext{Registry: buildDefaultRegistry()},\n\t\t\t\t\t&valueReaderWriter{Err: errors.New(\"rv error\"), ErrAfter: readValue, BSONType: TypeArray},\n\t\t\t\t\treadValue,\n\t\t\t\t\terrors.New(\"rv error\"),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"DecodeValue Error\",\n\t\t\t\t\t[]string{},\n\t\t\t\t\t&DecodeContext{Registry: buildDefaultRegistry()},\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeArray},\n\t\t\t\t\treadValue,\n\t\t\t\t\t&DecodeError{keys: []string{\"0\"}, wrapped: errors.New(\"cannot decode array into a string type\")},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"Document but not D\",\n\t\t\t\t\t[]string{},\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: Type(0)},\n\t\t\t\t\tnothing,\n\t\t\t\t\terrors.New(\"cannot decode document into []string\"),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"EmbeddedDocument but not D\",\n\t\t\t\t\t[]string{},\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeEmbeddedDocument},\n\t\t\t\t\tnothing,\n\t\t\t\t\terrors.New(\"cannot decode document into []string\"),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"decode null\",\n\t\t\t\t\t([]string)(nil),\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeNull},\n\t\t\t\t\treadNull,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"decode undefined\",\n\t\t\t\t\t([]string)(nil),\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeUndefined},\n\t\t\t\t\treadUndefined,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"ObjectIDDecodeValue\",\n\t\t\tValueDecoderFunc(objectIDDecodeValue),\n\t\t\t[]subtest{\n\t\t\t\t{\n\t\t\t\t\t\"wrong type\",\n\t\t\t\t\twrong,\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeObjectID},\n\t\t\t\t\tnothing,\n\t\t\t\t\tValueDecoderError{\n\t\t\t\t\t\tName:     \"ObjectIDDecodeValue\",\n\t\t\t\t\t\tTypes:    []reflect.Type{tOID},\n\t\t\t\t\t\tReceived: reflect.New(reflect.TypeOf(wrong)).Elem(),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type not objectID\",\n\t\t\t\t\tObjectID{},\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeInt32},\n\t\t\t\t\tnothing,\n\t\t\t\t\tfmt.Errorf(\"cannot decode %v into an ObjectID\", TypeInt32),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"ReadObjectID Error\",\n\t\t\t\t\tObjectID{},\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeObjectID, Err: errors.New(\"roid error\"), ErrAfter: readObjectID},\n\t\t\t\t\treadObjectID,\n\t\t\t\t\terrors.New(\"roid error\"),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"can set false\",\n\t\t\t\t\tcansettest,\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeObjectID, Return: ObjectID{}},\n\t\t\t\t\tnothing,\n\t\t\t\t\tValueDecoderError{Name: \"ObjectIDDecodeValue\", Types: []reflect.Type{tOID}},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"success\",\n\t\t\t\t\tObjectID{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C},\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{\n\t\t\t\t\t\tBSONType: TypeObjectID,\n\t\t\t\t\t\tReturn:   ObjectID{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C},\n\t\t\t\t\t},\n\t\t\t\t\treadObjectID,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"success/string\",\n\t\t\t\t\tObjectID{0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x61, 0x62},\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{\n\t\t\t\t\t\tBSONType: TypeString,\n\t\t\t\t\t\tReturn:   \"0123456789ab\",\n\t\t\t\t\t},\n\t\t\t\t\treadString,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"success/string-hex\",\n\t\t\t\t\tObjectID{0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x61, 0x62},\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{\n\t\t\t\t\t\tBSONType: TypeString,\n\t\t\t\t\t\tReturn:   \"303132333435363738396162\",\n\t\t\t\t\t},\n\t\t\t\t\treadString,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"decode null\",\n\t\t\t\t\tObjectID{},\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeNull},\n\t\t\t\t\treadNull,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"decode undefined\",\n\t\t\t\t\tObjectID{},\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeUndefined},\n\t\t\t\t\treadUndefined,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"Decimal128DecodeValue\",\n\t\t\tValueDecoderFunc(decimal128DecodeValue),\n\t\t\t[]subtest{\n\t\t\t\t{\n\t\t\t\t\t\"wrong type\",\n\t\t\t\t\twrong,\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeDecimal128},\n\t\t\t\t\tnothing,\n\t\t\t\t\tValueDecoderError{\n\t\t\t\t\t\tName:     \"Decimal128DecodeValue\",\n\t\t\t\t\t\tTypes:    []reflect.Type{tDecimal},\n\t\t\t\t\t\tReceived: reflect.New(reflect.TypeOf(wrong)).Elem(),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type not decimal128\",\n\t\t\t\t\tDecimal128{},\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeString},\n\t\t\t\t\tnothing,\n\t\t\t\t\tfmt.Errorf(\"cannot decode %v into a Decimal128\", TypeString),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"ReadDecimal128 Error\",\n\t\t\t\t\tDecimal128{},\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeDecimal128, Err: errors.New(\"rd128 error\"), ErrAfter: readDecimal128},\n\t\t\t\t\treadDecimal128,\n\t\t\t\t\terrors.New(\"rd128 error\"),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"can set false\",\n\t\t\t\t\tcansettest,\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeDecimal128, Return: d128},\n\t\t\t\t\tnothing,\n\t\t\t\t\tValueDecoderError{Name: \"Decimal128DecodeValue\", Types: []reflect.Type{tDecimal}},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"success\",\n\t\t\t\t\td128,\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeDecimal128, Return: d128},\n\t\t\t\t\treadDecimal128,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"decode null\",\n\t\t\t\t\tDecimal128{},\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeNull},\n\t\t\t\t\treadNull,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"decode undefined\",\n\t\t\t\t\tDecimal128{},\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeUndefined},\n\t\t\t\t\treadUndefined,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"JSONNumberDecodeValue\",\n\t\t\tValueDecoderFunc(jsonNumberDecodeValue),\n\t\t\t[]subtest{\n\t\t\t\t{\n\t\t\t\t\t\"wrong type\",\n\t\t\t\t\twrong,\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeObjectID},\n\t\t\t\t\tnothing,\n\t\t\t\t\tValueDecoderError{\n\t\t\t\t\t\tName:     \"JSONNumberDecodeValue\",\n\t\t\t\t\t\tTypes:    []reflect.Type{tJSONNumber},\n\t\t\t\t\t\tReceived: reflect.New(reflect.TypeOf(wrong)).Elem(),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type not double/int32/int64\",\n\t\t\t\t\tjson.Number(\"\"),\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeString},\n\t\t\t\t\tnothing,\n\t\t\t\t\tfmt.Errorf(\"cannot decode %v into a json.Number\", TypeString),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"ReadDouble Error\",\n\t\t\t\t\tjson.Number(\"\"),\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeDouble, Err: errors.New(\"rd error\"), ErrAfter: readDouble},\n\t\t\t\t\treadDouble,\n\t\t\t\t\terrors.New(\"rd error\"),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"ReadInt32 Error\",\n\t\t\t\t\tjson.Number(\"\"),\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeInt32, Err: errors.New(\"ri32 error\"), ErrAfter: readInt32},\n\t\t\t\t\treadInt32,\n\t\t\t\t\terrors.New(\"ri32 error\"),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"ReadInt64 Error\",\n\t\t\t\t\tjson.Number(\"\"),\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeInt64, Err: errors.New(\"ri64 error\"), ErrAfter: readInt64},\n\t\t\t\t\treadInt64,\n\t\t\t\t\terrors.New(\"ri64 error\"),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"can set false\",\n\t\t\t\t\tcansettest,\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeObjectID, Return: ObjectID{}},\n\t\t\t\t\tnothing,\n\t\t\t\t\tValueDecoderError{Name: \"JSONNumberDecodeValue\", Types: []reflect.Type{tJSONNumber}},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"success/double\",\n\t\t\t\t\tjson.Number(\"3.14159\"),\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeDouble, Return: float64(3.14159)},\n\t\t\t\t\treadDouble,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"success/int32\",\n\t\t\t\t\tjson.Number(\"12345\"),\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeInt32, Return: int32(12345)},\n\t\t\t\t\treadInt32,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"success/int64\",\n\t\t\t\t\tjson.Number(\"1234567890\"),\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeInt64, Return: int64(1234567890)},\n\t\t\t\t\treadInt64,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"decode null\",\n\t\t\t\t\tjson.Number(\"\"),\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeNull},\n\t\t\t\t\treadNull,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"decode undefined\",\n\t\t\t\t\tjson.Number(\"\"),\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeUndefined},\n\t\t\t\t\treadUndefined,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"URLDecodeValue\",\n\t\t\tValueDecoderFunc(urlDecodeValue),\n\t\t\t[]subtest{\n\t\t\t\t{\n\t\t\t\t\t\"wrong type\",\n\t\t\t\t\turl.URL{},\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeInt32},\n\t\t\t\t\tnothing,\n\t\t\t\t\tfmt.Errorf(\"cannot decode %v into a *url.URL\", TypeInt32),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type not *url.URL\",\n\t\t\t\t\tint64(0),\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeString, Return: \"http://example.com\"},\n\t\t\t\t\tnothing,\n\t\t\t\t\tValueDecoderError{\n\t\t\t\t\t\tName:     \"URLDecodeValue\",\n\t\t\t\t\t\tTypes:    []reflect.Type{tURL},\n\t\t\t\t\t\tReceived: reflect.New(reflect.TypeOf(int64(0))).Elem(),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"ReadString error\",\n\t\t\t\t\turl.URL{},\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeString, Err: errors.New(\"rs error\"), ErrAfter: readString},\n\t\t\t\t\treadString,\n\t\t\t\t\terrors.New(\"rs error\"),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"url.Parse error\",\n\t\t\t\t\turl.URL{},\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeString, Return: \"not-valid-%%%%://\"},\n\t\t\t\t\treadString,\n\t\t\t\t\t&url.Error{\n\t\t\t\t\t\tOp:  \"parse\",\n\t\t\t\t\t\tURL: \"not-valid-%%%%://\",\n\t\t\t\t\t\tErr: errors.New(\"first path segment in URL cannot contain colon\"),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"can set false\",\n\t\t\t\t\tcansettest,\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeString, Return: \"http://example.com\"},\n\t\t\t\t\tnothing,\n\t\t\t\t\tValueDecoderError{Name: \"URLDecodeValue\", Types: []reflect.Type{tURL}},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"url.URL\",\n\t\t\t\t\turl.URL{Scheme: \"http\", Host: \"example.com\"},\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeString, Return: \"http://example.com\"},\n\t\t\t\t\treadString,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"decode null\",\n\t\t\t\t\turl.URL{},\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeNull},\n\t\t\t\t\treadNull,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"decode undefined\",\n\t\t\t\t\turl.URL{},\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeUndefined},\n\t\t\t\t\treadUndefined,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"defaultByteSliceCodec.DecodeValue\",\n\t\t\t&byteSliceCodec{},\n\t\t\t[]subtest{\n\t\t\t\t{\n\t\t\t\t\t\"wrong type\",\n\t\t\t\t\t[]byte{},\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeInt32},\n\t\t\t\t\tnothing,\n\t\t\t\t\tfmt.Errorf(\"cannot decode %v into a []byte\", TypeInt32),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type not []byte\",\n\t\t\t\t\tint64(0),\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeBinary, Return: bsoncore.Value{Type: bsoncore.TypeBinary}},\n\t\t\t\t\tnothing,\n\t\t\t\t\tValueDecoderError{\n\t\t\t\t\t\tName:     \"ByteSliceDecodeValue\",\n\t\t\t\t\t\tTypes:    []reflect.Type{tByteSlice},\n\t\t\t\t\t\tReceived: reflect.New(reflect.TypeOf(int64(0))).Elem(),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"ReadBinary error\",\n\t\t\t\t\t[]byte{},\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeBinary, Err: errors.New(\"rb error\"), ErrAfter: readBinary},\n\t\t\t\t\treadBinary,\n\t\t\t\t\terrors.New(\"rb error\"),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"incorrect subtype\",\n\t\t\t\t\t[]byte{},\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{\n\t\t\t\t\t\tBSONType: TypeBinary,\n\t\t\t\t\t\tReturn: bsoncore.Value{\n\t\t\t\t\t\t\tType: bsoncore.TypeBinary,\n\t\t\t\t\t\t\tData: bsoncore.AppendBinary(nil, 0xFF, []byte{0x01, 0x02, 0x03}),\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\treadBinary,\n\t\t\t\t\tdecodeBinaryError{subtype: byte(0xFF), typeName: \"[]byte\"},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"can set false\",\n\t\t\t\t\tcansettest,\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeBinary, Return: bsoncore.AppendBinary(nil, 0x00, []byte{0x01, 0x02, 0x03})},\n\t\t\t\t\tnothing,\n\t\t\t\t\tValueDecoderError{Name: \"ByteSliceDecodeValue\", Types: []reflect.Type{tByteSlice}},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"decode null\",\n\t\t\t\t\t([]byte)(nil),\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeNull},\n\t\t\t\t\treadNull,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"decode undefined\",\n\t\t\t\t\t([]byte)(nil),\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeUndefined},\n\t\t\t\t\treadUndefined,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"defaultStringCodec.DecodeValue\",\n\t\t\t&stringCodec{},\n\t\t\t[]subtest{\n\t\t\t\t{\n\t\t\t\t\t\"symbol\",\n\t\t\t\t\t\"var hello = 'world';\",\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeSymbol, Return: \"var hello = 'world';\"},\n\t\t\t\t\treadSymbol,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"decode null\",\n\t\t\t\t\t\"\",\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeNull},\n\t\t\t\t\treadNull,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"decode undefined\",\n\t\t\t\t\t\"\",\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeUndefined},\n\t\t\t\t\treadUndefined,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"ValueUnmarshalerDecodeValue\",\n\t\t\tValueDecoderFunc(valueUnmarshalerDecodeValue),\n\t\t\t[]subtest{\n\t\t\t\t{\n\t\t\t\t\t\"wrong type\",\n\t\t\t\t\twrong,\n\t\t\t\t\tnil,\n\t\t\t\t\tnil,\n\t\t\t\t\tnothing,\n\t\t\t\t\tValueDecoderError{\n\t\t\t\t\t\tName:     \"ValueUnmarshalerDecodeValue\",\n\t\t\t\t\t\tTypes:    []reflect.Type{tValueUnmarshaler},\n\t\t\t\t\t\tReceived: reflect.New(reflect.TypeOf(wrong)).Elem(),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"copy error\",\n\t\t\t\t\t&testValueUnmarshaler{},\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeString, Err: errors.New(\"copy error\"), ErrAfter: readString},\n\t\t\t\t\treadString,\n\t\t\t\t\terrors.New(\"copy error\"),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"ValueUnmarshaler\",\n\t\t\t\t\t&testValueUnmarshaler{t: TypeString, val: bsoncore.AppendString(nil, \"hello, world\")},\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeString, Return: \"hello, world\"},\n\t\t\t\t\treadString,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"UnmarshalerDecodeValue\",\n\t\t\tValueDecoderFunc(unmarshalerDecodeValue),\n\t\t\t[]subtest{\n\t\t\t\t{\n\t\t\t\t\t\"wrong type\",\n\t\t\t\t\twrong,\n\t\t\t\t\tnil,\n\t\t\t\t\tnil,\n\t\t\t\t\tnothing,\n\t\t\t\t\tValueDecoderError{\n\t\t\t\t\t\tName:     \"UnmarshalerDecodeValue\",\n\t\t\t\t\t\tTypes:    []reflect.Type{tUnmarshaler},\n\t\t\t\t\t\tReceived: reflect.New(reflect.TypeOf(wrong)).Elem(),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"copy error\",\n\t\t\t\t\t&testUnmarshaler{},\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeString, Err: errors.New(\"copy error\"), ErrAfter: readString},\n\t\t\t\t\treadString,\n\t\t\t\t\terrors.New(\"copy error\"),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t// Only the pointer form of testUnmarshaler implements Unmarshaler\n\t\t\t\t\t\"value does not implement Unmarshaler\",\n\t\t\t\t\t&testUnmarshaler{\n\t\t\t\t\t\tInvoked: true,\n\t\t\t\t\t\tVal:     bsoncore.AppendDouble(nil, 3.14159),\n\t\t\t\t\t},\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeDouble, Return: float64(3.14159)},\n\t\t\t\t\treadDouble,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"Unmarshaler\",\n\t\t\t\t\t&testUnmarshaler{\n\t\t\t\t\t\tInvoked: true,\n\t\t\t\t\t\tVal:     bsoncore.AppendDouble(nil, 3.14159),\n\t\t\t\t\t},\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeDouble, Return: float64(3.14159)},\n\t\t\t\t\treadDouble,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"PointerCodec.DecodeValue\",\n\t\t\t&pointerCodec{},\n\t\t\t[]subtest{\n\t\t\t\t{\n\t\t\t\t\t\"not valid\", nil, nil, nil, nothing,\n\t\t\t\t\tValueDecoderError{Name: \"PointerCodec.DecodeValue\", Kinds: []reflect.Kind{reflect.Ptr}, Received: reflect.Value{}},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"can set\", cansettest, nil, nil, nothing,\n\t\t\t\t\tValueDecoderError{Name: \"PointerCodec.DecodeValue\", Kinds: []reflect.Kind{reflect.Ptr}},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"No Decoder\", &wrong, &DecodeContext{Registry: buildDefaultRegistry()}, nil, nothing,\n\t\t\t\t\terrNoDecoder{Type: reflect.TypeOf(wrong)},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"decode null\",\n\t\t\t\t\t(*mystruct)(nil),\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeNull},\n\t\t\t\t\treadNull,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"decode undefined\",\n\t\t\t\t\t(*mystruct)(nil),\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeUndefined},\n\t\t\t\t\treadUndefined,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"BinaryDecodeValue\",\n\t\t\tValueDecoderFunc(binaryDecodeValue),\n\t\t\t[]subtest{\n\t\t\t\t{\n\t\t\t\t\t\"wrong type\",\n\t\t\t\t\twrong,\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{},\n\t\t\t\t\tnothing,\n\t\t\t\t\tValueDecoderError{\n\t\t\t\t\t\tName:     \"BinaryDecodeValue\",\n\t\t\t\t\t\tTypes:    []reflect.Type{tBinary},\n\t\t\t\t\t\tReceived: reflect.New(reflect.TypeOf(wrong)).Elem(),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type not binary\",\n\t\t\t\t\tBinary{},\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeString},\n\t\t\t\t\tnothing,\n\t\t\t\t\tfmt.Errorf(\"cannot decode %v into a Binary\", TypeString),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"ReadBinary Error\",\n\t\t\t\t\tBinary{},\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeBinary, Err: errors.New(\"rb error\"), ErrAfter: readBinary},\n\t\t\t\t\treadBinary,\n\t\t\t\t\terrors.New(\"rb error\"),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"Binary/success\",\n\t\t\t\t\tBinary{Data: []byte{0x01, 0x02, 0x03}, Subtype: 0xFF},\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{\n\t\t\t\t\t\tBSONType: TypeBinary,\n\t\t\t\t\t\tReturn: bsoncore.Value{\n\t\t\t\t\t\t\tType: bsoncore.TypeBinary,\n\t\t\t\t\t\t\tData: bsoncore.AppendBinary(nil, 0xFF, []byte{0x01, 0x02, 0x03}),\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\treadBinary,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"decode null\",\n\t\t\t\t\tBinary{},\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeNull},\n\t\t\t\t\treadNull,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"decode undefined\",\n\t\t\t\t\tBinary{},\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeUndefined},\n\t\t\t\t\treadUndefined,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"UndefinedDecodeValue\",\n\t\t\tValueDecoderFunc(undefinedDecodeValue),\n\t\t\t[]subtest{\n\t\t\t\t{\n\t\t\t\t\t\"wrong type\",\n\t\t\t\t\twrong,\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeUndefined},\n\t\t\t\t\tnothing,\n\t\t\t\t\tValueDecoderError{\n\t\t\t\t\t\tName:     \"UndefinedDecodeValue\",\n\t\t\t\t\t\tTypes:    []reflect.Type{tUndefined},\n\t\t\t\t\t\tReceived: reflect.New(reflect.TypeOf(wrong)).Elem(),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type not undefined\",\n\t\t\t\t\tUndefined{},\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeString},\n\t\t\t\t\tnothing,\n\t\t\t\t\tfmt.Errorf(\"cannot decode %v into an Undefined\", TypeString),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"ReadUndefined Error\",\n\t\t\t\t\tUndefined{},\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeUndefined, Err: errors.New(\"ru error\"), ErrAfter: readUndefined},\n\t\t\t\t\treadUndefined,\n\t\t\t\t\terrors.New(\"ru error\"),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"ReadUndefined/success\",\n\t\t\t\t\tUndefined{},\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeUndefined},\n\t\t\t\t\treadUndefined,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"decode null\",\n\t\t\t\t\tUndefined{},\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeNull},\n\t\t\t\t\treadNull,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"DateTimeDecodeValue\",\n\t\t\tValueDecoderFunc(dateTimeDecodeValue),\n\t\t\t[]subtest{\n\t\t\t\t{\n\t\t\t\t\t\"wrong type\",\n\t\t\t\t\twrong,\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeDateTime},\n\t\t\t\t\tnothing,\n\t\t\t\t\tValueDecoderError{\n\t\t\t\t\t\tName:     \"DateTimeDecodeValue\",\n\t\t\t\t\t\tTypes:    []reflect.Type{tDateTime},\n\t\t\t\t\t\tReceived: reflect.New(reflect.TypeOf(wrong)).Elem(),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type not datetime\",\n\t\t\t\t\tDateTime(0),\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeString},\n\t\t\t\t\tnothing,\n\t\t\t\t\tfmt.Errorf(\"cannot decode %v into a DateTime\", TypeString),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"ReadDateTime Error\",\n\t\t\t\t\tDateTime(0),\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeDateTime, Err: errors.New(\"rdt error\"), ErrAfter: readDateTime},\n\t\t\t\t\treadDateTime,\n\t\t\t\t\terrors.New(\"rdt error\"),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"success\",\n\t\t\t\t\tDateTime(1234567890),\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeDateTime, Return: int64(1234567890)},\n\t\t\t\t\treadDateTime,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"decode null\",\n\t\t\t\t\tDateTime(0),\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeNull},\n\t\t\t\t\treadNull,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"decode undefined\",\n\t\t\t\t\tDateTime(0),\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeUndefined},\n\t\t\t\t\treadUndefined,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"NullDecodeValue\",\n\t\t\tValueDecoderFunc(nullDecodeValue),\n\t\t\t[]subtest{\n\t\t\t\t{\n\t\t\t\t\t\"wrong type\",\n\t\t\t\t\twrong,\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeNull},\n\t\t\t\t\tnothing,\n\t\t\t\t\tValueDecoderError{\n\t\t\t\t\t\tName:     \"NullDecodeValue\",\n\t\t\t\t\t\tTypes:    []reflect.Type{tNull},\n\t\t\t\t\t\tReceived: reflect.New(reflect.TypeOf(wrong)).Elem(),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type not null\",\n\t\t\t\t\tNull{},\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeString},\n\t\t\t\t\tnothing,\n\t\t\t\t\tfmt.Errorf(\"cannot decode %v into a Null\", TypeString),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"ReadNull Error\",\n\t\t\t\t\tNull{},\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeNull, Err: errors.New(\"rn error\"), ErrAfter: readNull},\n\t\t\t\t\treadNull,\n\t\t\t\t\terrors.New(\"rn error\"),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"success\",\n\t\t\t\t\tNull{},\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeNull},\n\t\t\t\t\treadNull,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"RegexDecodeValue\",\n\t\t\tValueDecoderFunc(regexDecodeValue),\n\t\t\t[]subtest{\n\t\t\t\t{\n\t\t\t\t\t\"wrong type\",\n\t\t\t\t\twrong,\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeRegex},\n\t\t\t\t\tnothing,\n\t\t\t\t\tValueDecoderError{\n\t\t\t\t\t\tName:     \"RegexDecodeValue\",\n\t\t\t\t\t\tTypes:    []reflect.Type{tRegex},\n\t\t\t\t\t\tReceived: reflect.New(reflect.TypeOf(wrong)).Elem(),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type not regex\",\n\t\t\t\t\tRegex{},\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeString},\n\t\t\t\t\tnothing,\n\t\t\t\t\tfmt.Errorf(\"cannot decode %v into a Regex\", TypeString),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"ReadRegex Error\",\n\t\t\t\t\tRegex{},\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeRegex, Err: errors.New(\"rr error\"), ErrAfter: readRegex},\n\t\t\t\t\treadRegex,\n\t\t\t\t\terrors.New(\"rr error\"),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"success\",\n\t\t\t\t\tRegex{Pattern: \"foo\", Options: \"bar\"},\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{\n\t\t\t\t\t\tBSONType: TypeRegex,\n\t\t\t\t\t\tReturn: bsoncore.Value{\n\t\t\t\t\t\t\tType: bsoncore.TypeRegex,\n\t\t\t\t\t\t\tData: bsoncore.AppendRegex(nil, \"foo\", \"bar\"),\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\treadRegex,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"decode null\",\n\t\t\t\t\tRegex{},\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeNull},\n\t\t\t\t\treadNull,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"decode undefined\",\n\t\t\t\t\tRegex{},\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeUndefined},\n\t\t\t\t\treadUndefined,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"DBPointerDecodeValue\",\n\t\t\tValueDecoderFunc(dbPointerDecodeValue),\n\t\t\t[]subtest{\n\t\t\t\t{\n\t\t\t\t\t\"wrong type\",\n\t\t\t\t\twrong,\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeDBPointer},\n\t\t\t\t\tnothing,\n\t\t\t\t\tValueDecoderError{\n\t\t\t\t\t\tName:     \"DBPointerDecodeValue\",\n\t\t\t\t\t\tTypes:    []reflect.Type{tDBPointer},\n\t\t\t\t\t\tReceived: reflect.New(reflect.TypeOf(wrong)).Elem(),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type not dbpointer\",\n\t\t\t\t\tDBPointer{},\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeString},\n\t\t\t\t\tnothing,\n\t\t\t\t\tfmt.Errorf(\"cannot decode %v into a DBPointer\", TypeString),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"ReadDBPointer Error\",\n\t\t\t\t\tDBPointer{},\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeDBPointer, Err: errors.New(\"rdbp error\"), ErrAfter: readDBPointer},\n\t\t\t\t\treadDBPointer,\n\t\t\t\t\terrors.New(\"rdbp error\"),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"success\",\n\t\t\t\t\tDBPointer{\n\t\t\t\t\t\tDB:      \"foobar\",\n\t\t\t\t\t\tPointer: ObjectID{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C},\n\t\t\t\t\t},\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{\n\t\t\t\t\t\tBSONType: TypeDBPointer,\n\t\t\t\t\t\tReturn: bsoncore.Value{\n\t\t\t\t\t\t\tType: bsoncore.TypeDBPointer,\n\t\t\t\t\t\t\tData: bsoncore.AppendDBPointer(\n\t\t\t\t\t\t\t\tnil, \"foobar\", ObjectID{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C},\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\treadDBPointer,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"decode null\",\n\t\t\t\t\tDBPointer{},\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeNull},\n\t\t\t\t\treadNull,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"decode undefined\",\n\t\t\t\t\tDBPointer{},\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeUndefined},\n\t\t\t\t\treadUndefined,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"TimestampDecodeValue\",\n\t\t\tValueDecoderFunc(timestampDecodeValue),\n\t\t\t[]subtest{\n\t\t\t\t{\n\t\t\t\t\t\"wrong type\",\n\t\t\t\t\twrong,\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeTimestamp},\n\t\t\t\t\tnothing,\n\t\t\t\t\tValueDecoderError{\n\t\t\t\t\t\tName:     \"TimestampDecodeValue\",\n\t\t\t\t\t\tTypes:    []reflect.Type{tTimestamp},\n\t\t\t\t\t\tReceived: reflect.New(reflect.TypeOf(wrong)).Elem(),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type not timestamp\",\n\t\t\t\t\tTimestamp{},\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeString},\n\t\t\t\t\tnothing,\n\t\t\t\t\tfmt.Errorf(\"cannot decode %v into a Timestamp\", TypeString),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"ReadTimestamp Error\",\n\t\t\t\t\tTimestamp{},\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeTimestamp, Err: errors.New(\"rt error\"), ErrAfter: readTimestamp},\n\t\t\t\t\treadTimestamp,\n\t\t\t\t\terrors.New(\"rt error\"),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"success\",\n\t\t\t\t\tTimestamp{T: 12345, I: 67890},\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{\n\t\t\t\t\t\tBSONType: TypeTimestamp,\n\t\t\t\t\t\tReturn: bsoncore.Value{\n\t\t\t\t\t\t\tType: bsoncore.TypeTimestamp,\n\t\t\t\t\t\t\tData: bsoncore.AppendTimestamp(nil, 12345, 67890),\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\treadTimestamp,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"decode null\",\n\t\t\t\t\tTimestamp{},\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeNull},\n\t\t\t\t\treadNull,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"decode undefined\",\n\t\t\t\t\tTimestamp{},\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeUndefined},\n\t\t\t\t\treadUndefined,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"MinKeyDecodeValue\",\n\t\t\tValueDecoderFunc(minKeyDecodeValue),\n\t\t\t[]subtest{\n\t\t\t\t{\n\t\t\t\t\t\"wrong type\",\n\t\t\t\t\twrong,\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeMinKey},\n\t\t\t\t\tnothing,\n\t\t\t\t\tValueDecoderError{\n\t\t\t\t\t\tName:     \"MinKeyDecodeValue\",\n\t\t\t\t\t\tTypes:    []reflect.Type{tMinKey},\n\t\t\t\t\t\tReceived: reflect.New(reflect.TypeOf(wrong)).Elem(),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type not null\",\n\t\t\t\t\tMinKey{},\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeString},\n\t\t\t\t\tnothing,\n\t\t\t\t\tfmt.Errorf(\"cannot decode %v into a MinKey\", TypeString),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"ReadMinKey Error\",\n\t\t\t\t\tMinKey{},\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeMinKey, Err: errors.New(\"rn error\"), ErrAfter: readMinKey},\n\t\t\t\t\treadMinKey,\n\t\t\t\t\terrors.New(\"rn error\"),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"success\",\n\t\t\t\t\tMinKey{},\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeMinKey},\n\t\t\t\t\treadMinKey,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"decode null\",\n\t\t\t\t\tMinKey{},\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeNull},\n\t\t\t\t\treadNull,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"decode undefined\",\n\t\t\t\t\tMinKey{},\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeUndefined},\n\t\t\t\t\treadUndefined,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"MaxKeyDecodeValue\",\n\t\t\tValueDecoderFunc(maxKeyDecodeValue),\n\t\t\t[]subtest{\n\t\t\t\t{\n\t\t\t\t\t\"wrong type\",\n\t\t\t\t\twrong,\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeMaxKey},\n\t\t\t\t\tnothing,\n\t\t\t\t\tValueDecoderError{\n\t\t\t\t\t\tName:     \"MaxKeyDecodeValue\",\n\t\t\t\t\t\tTypes:    []reflect.Type{tMaxKey},\n\t\t\t\t\t\tReceived: reflect.New(reflect.TypeOf(wrong)).Elem(),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type not null\",\n\t\t\t\t\tMaxKey{},\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeString},\n\t\t\t\t\tnothing,\n\t\t\t\t\tfmt.Errorf(\"cannot decode %v into a MaxKey\", TypeString),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"ReadMaxKey Error\",\n\t\t\t\t\tMaxKey{},\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeMaxKey, Err: errors.New(\"rn error\"), ErrAfter: readMaxKey},\n\t\t\t\t\treadMaxKey,\n\t\t\t\t\terrors.New(\"rn error\"),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"success\",\n\t\t\t\t\tMaxKey{},\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeMaxKey},\n\t\t\t\t\treadMaxKey,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"decode null\",\n\t\t\t\t\tMaxKey{},\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeNull},\n\t\t\t\t\treadNull,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"decode undefined\",\n\t\t\t\t\tMaxKey{},\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeUndefined},\n\t\t\t\t\treadUndefined,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"JavaScriptDecodeValue\",\n\t\t\tValueDecoderFunc(javaScriptDecodeValue),\n\t\t\t[]subtest{\n\t\t\t\t{\n\t\t\t\t\t\"wrong type\",\n\t\t\t\t\twrong,\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeJavaScript, Return: \"\"},\n\t\t\t\t\tnothing,\n\t\t\t\t\tValueDecoderError{\n\t\t\t\t\t\tName:     \"JavaScriptDecodeValue\",\n\t\t\t\t\t\tTypes:    []reflect.Type{tJavaScript},\n\t\t\t\t\t\tReceived: reflect.New(reflect.TypeOf(wrong)).Elem(),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type not Javascript\",\n\t\t\t\t\tJavaScript(\"\"),\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeString},\n\t\t\t\t\tnothing,\n\t\t\t\t\tfmt.Errorf(\"cannot decode %v into a JavaScript\", TypeString),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"ReadJavascript Error\",\n\t\t\t\t\tJavaScript(\"\"),\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeJavaScript, Err: errors.New(\"rjs error\"), ErrAfter: readJavascript},\n\t\t\t\t\treadJavascript,\n\t\t\t\t\terrors.New(\"rjs error\"),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"JavaScript/success\",\n\t\t\t\t\tJavaScript(\"var hello = 'world';\"),\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeJavaScript, Return: \"var hello = 'world';\"},\n\t\t\t\t\treadJavascript,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"decode null\",\n\t\t\t\t\tJavaScript(\"\"),\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeNull},\n\t\t\t\t\treadNull,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"decode undefined\",\n\t\t\t\t\tJavaScript(\"\"),\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeUndefined},\n\t\t\t\t\treadUndefined,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"SymbolDecodeValue\",\n\t\t\tValueDecoderFunc(symbolDecodeValue),\n\t\t\t[]subtest{\n\t\t\t\t{\n\t\t\t\t\t\"wrong type\",\n\t\t\t\t\twrong,\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeSymbol, Return: \"\"},\n\t\t\t\t\tnothing,\n\t\t\t\t\tValueDecoderError{\n\t\t\t\t\t\tName:     \"SymbolDecodeValue\",\n\t\t\t\t\t\tTypes:    []reflect.Type{tSymbol},\n\t\t\t\t\t\tReceived: reflect.New(reflect.TypeOf(wrong)).Elem(),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type not Symbol\",\n\t\t\t\t\tSymbol(\"\"),\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeInt32},\n\t\t\t\t\tnothing,\n\t\t\t\t\tfmt.Errorf(\"cannot decode %v into a Symbol\", TypeInt32),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"ReadSymbol Error\",\n\t\t\t\t\tSymbol(\"\"),\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeSymbol, Err: errors.New(\"rjs error\"), ErrAfter: readSymbol},\n\t\t\t\t\treadSymbol,\n\t\t\t\t\terrors.New(\"rjs error\"),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"Symbol/success\",\n\t\t\t\t\tSymbol(\"var hello = 'world';\"),\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeSymbol, Return: \"var hello = 'world';\"},\n\t\t\t\t\treadSymbol,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"decode null\",\n\t\t\t\t\tSymbol(\"\"),\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeNull},\n\t\t\t\t\treadNull,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"decode undefined\",\n\t\t\t\t\tSymbol(\"\"),\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeUndefined},\n\t\t\t\t\treadUndefined,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"CoreDocumentDecodeValue\",\n\t\t\tValueDecoderFunc(coreDocumentDecodeValue),\n\t\t\t[]subtest{\n\t\t\t\t{\n\t\t\t\t\t\"wrong type\",\n\t\t\t\t\twrong,\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{},\n\t\t\t\t\tnothing,\n\t\t\t\t\tValueDecoderError{\n\t\t\t\t\t\tName:     \"CoreDocumentDecodeValue\",\n\t\t\t\t\t\tTypes:    []reflect.Type{tCoreDocument},\n\t\t\t\t\t\tReceived: reflect.New(reflect.TypeOf(wrong)).Elem(),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"*bsoncore.Document is nil\",\n\t\t\t\t\t(*bsoncore.Document)(nil),\n\t\t\t\t\tnil,\n\t\t\t\t\tnil,\n\t\t\t\t\tnothing,\n\t\t\t\t\tValueDecoderError{\n\t\t\t\t\t\tName:     \"CoreDocumentDecodeValue\",\n\t\t\t\t\t\tTypes:    []reflect.Type{tCoreDocument},\n\t\t\t\t\t\tReceived: reflect.New(reflect.TypeOf((*bsoncore.Document)(nil))).Elem(),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"Copy error\",\n\t\t\t\t\tbsoncore.Document{},\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{Err: errors.New(\"copy error\"), ErrAfter: readDocument},\n\t\t\t\t\treadDocument,\n\t\t\t\t\terrors.New(\"copy error\"),\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"StructCodec.DecodeValue\",\n\t\t\tnewStructCodec(nil),\n\t\t\t[]subtest{\n\t\t\t\t{\n\t\t\t\t\t\"Not struct\",\n\t\t\t\t\treflect.New(reflect.TypeOf(struct{ Foo string }{})).Elem().Interface(),\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeString},\n\t\t\t\t\tnothing,\n\t\t\t\t\terrors.New(\"cannot decode string into a struct { Foo string }\"),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"decode null\",\n\t\t\t\t\treflect.New(reflect.TypeOf(struct{ Foo string }{})).Elem().Interface(),\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeNull},\n\t\t\t\t\treadNull,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"decode undefined\",\n\t\t\t\t\treflect.New(reflect.TypeOf(struct{ Foo string }{})).Elem().Interface(),\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeUndefined},\n\t\t\t\t\treadUndefined,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"CodeWithScopeDecodeValue\",\n\t\t\tValueDecoderFunc(codeWithScopeDecodeValue),\n\t\t\t[]subtest{\n\t\t\t\t{\n\t\t\t\t\t\"wrong type\",\n\t\t\t\t\twrong,\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeCodeWithScope},\n\t\t\t\t\tnothing,\n\t\t\t\t\tValueDecoderError{\n\t\t\t\t\t\tName:     \"CodeWithScopeDecodeValue\",\n\t\t\t\t\t\tTypes:    []reflect.Type{tCodeWithScope},\n\t\t\t\t\t\tReceived: reflect.New(reflect.TypeOf(wrong)).Elem(),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type not codewithscope\",\n\t\t\t\t\tCodeWithScope{},\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeString},\n\t\t\t\t\tnothing,\n\t\t\t\t\tfmt.Errorf(\"cannot decode %v into a CodeWithScope\", TypeString),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"ReadCodeWithScope Error\",\n\t\t\t\t\tCodeWithScope{},\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeCodeWithScope, Err: errors.New(\"rcws error\"), ErrAfter: readCodeWithScope},\n\t\t\t\t\treadCodeWithScope,\n\t\t\t\t\terrors.New(\"rcws error\"),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"decodeDocument Error\",\n\t\t\t\t\tCodeWithScope{\n\t\t\t\t\t\tCode:  \"var hello = 'world';\",\n\t\t\t\t\t\tScope: D{{\"foo\", nil}},\n\t\t\t\t\t},\n\t\t\t\t\t&DecodeContext{Registry: buildDefaultRegistry()},\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeCodeWithScope, Err: errors.New(\"dd error\"), ErrAfter: readElement},\n\t\t\t\t\treadElement,\n\t\t\t\t\terrors.New(\"dd error\"),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"decode null\",\n\t\t\t\t\tCodeWithScope{},\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeNull},\n\t\t\t\t\treadNull,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"decode undefined\",\n\t\t\t\t\tCodeWithScope{},\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{BSONType: TypeUndefined},\n\t\t\t\t\treadUndefined,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"CoreArrayDecodeValue\",\n\t\t\t&arrayCodec{},\n\t\t\t[]subtest{\n\t\t\t\t{\n\t\t\t\t\t\"wrong type\",\n\t\t\t\t\twrong,\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{},\n\t\t\t\t\tnothing,\n\t\t\t\t\tValueDecoderError{\n\t\t\t\t\t\tName:     \"CoreArrayDecodeValue\",\n\t\t\t\t\t\tTypes:    []reflect.Type{tCoreArray},\n\t\t\t\t\t\tReceived: reflect.New(reflect.TypeOf(wrong)).Elem(),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"*bsoncore.Array is nil\",\n\t\t\t\t\t(*bsoncore.Array)(nil),\n\t\t\t\t\tnil,\n\t\t\t\t\tnil,\n\t\t\t\t\tnothing,\n\t\t\t\t\tValueDecoderError{\n\t\t\t\t\t\tName:     \"CoreArrayDecodeValue\",\n\t\t\t\t\t\tTypes:    []reflect.Type{tCoreArray},\n\t\t\t\t\t\tReceived: reflect.New(reflect.TypeOf((*bsoncore.Array)(nil))).Elem(),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n\n\tfor _, tc := range testCases {\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\tfor _, rc := range tc.subtests {\n\t\t\t\tt.Run(rc.name, func(t *testing.T) {\n\t\t\t\t\tvar dc DecodeContext\n\t\t\t\t\tif rc.dctx != nil {\n\t\t\t\t\t\tdc = *rc.dctx\n\t\t\t\t\t}\n\t\t\t\t\tllvrw := new(valueReaderWriter)\n\t\t\t\t\tif rc.llvrw != nil {\n\t\t\t\t\t\tllvrw = rc.llvrw\n\t\t\t\t\t}\n\t\t\t\t\tllvrw.T = t\n\t\t\t\t\t// var got any\n\t\t\t\t\tif rc.val == cansetreflectiontest { // We're doing a CanSet reflection test\n\t\t\t\t\t\terr := tc.vd.DecodeValue(dc, llvrw, reflect.Value{})\n\t\t\t\t\t\tif !assert.CompareErrors(err, rc.err) {\n\t\t\t\t\t\t\tt.Errorf(\"Errors do not match. got %v; want %v\", err, rc.err)\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tval := reflect.New(reflect.TypeOf(rc.val)).Elem()\n\t\t\t\t\t\terr = tc.vd.DecodeValue(dc, llvrw, val)\n\t\t\t\t\t\tif !assert.CompareErrors(err, rc.err) {\n\t\t\t\t\t\t\tt.Errorf(\"Errors do not match. got %v; want %v\", err, rc.err)\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t\tif rc.val == cansettest { // We're doing an IsValid and CanSet test\n\t\t\t\t\t\tvar wanterr ValueDecoderError\n\t\t\t\t\t\tif !errors.As(rc.err, &wanterr) {\n\t\t\t\t\t\t\tt.Fatalf(\"Error must be a DecodeValueError, but got a %T\", rc.err)\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\terr := tc.vd.DecodeValue(dc, llvrw, reflect.Value{})\n\t\t\t\t\t\twanterr.Received = reflect.ValueOf(nil)\n\t\t\t\t\t\tif !assert.CompareErrors(err, wanterr) {\n\t\t\t\t\t\t\tt.Errorf(\"Errors do not match. got %v; want %v\", err, wanterr)\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\terr = tc.vd.DecodeValue(dc, llvrw, reflect.ValueOf(int(12345)))\n\t\t\t\t\t\twanterr.Received = reflect.ValueOf(int(12345))\n\t\t\t\t\t\tif !assert.CompareErrors(err, wanterr) {\n\t\t\t\t\t\t\tt.Errorf(\"Errors do not match. got %v; want %v\", err, wanterr)\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t\tvar val reflect.Value\n\t\t\t\t\tif rtype := reflect.TypeOf(rc.val); rtype != nil {\n\t\t\t\t\t\tval = reflect.New(rtype).Elem()\n\t\t\t\t\t}\n\t\t\t\t\twant := rc.val\n\t\t\t\t\tdefer func() {\n\t\t\t\t\t\tif err := recover(); err != nil {\n\t\t\t\t\t\t\tfmt.Println(t.Name())\n\t\t\t\t\t\t\tpanic(err)\n\t\t\t\t\t\t}\n\t\t\t\t\t}()\n\t\t\t\t\terr := tc.vd.DecodeValue(dc, llvrw, val)\n\t\t\t\t\tif !assert.CompareErrors(err, rc.err) {\n\t\t\t\t\t\tt.Errorf(\"Errors do not match. got %v; want %v\", err, rc.err)\n\t\t\t\t\t}\n\t\t\t\t\tinvoked := llvrw.invoked\n\t\t\t\t\tif !cmp.Equal(invoked, rc.invoke) {\n\t\t\t\t\t\tt.Errorf(\"Incorrect method invoked. got %v; want %v\", invoked, rc.invoke)\n\t\t\t\t\t}\n\t\t\t\t\tvar got any\n\t\t\t\t\tif val.IsValid() && val.CanInterface() {\n\t\t\t\t\t\tgot = val.Interface()\n\t\t\t\t\t}\n\t\t\t\t\tif rc.err == nil && !cmp.Equal(got, want, cmp.Comparer(compareDecimal128)) {\n\t\t\t\t\t\tt.Errorf(\"Values do not match. got (%T)%v; want (%T)%v\", got, got, want, want)\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\t}\n\n\tt.Run(\"CodeWithScopeCodec/DecodeValue/success\", func(t *testing.T) {\n\t\tdc := DecodeContext{Registry: buildDefaultRegistry()}\n\t\tb := bsoncore.BuildDocument(nil,\n\t\t\tbsoncore.AppendCodeWithScopeElement(\n\t\t\t\tnil, \"foo\", \"var hello = 'world';\",\n\t\t\t\tbuildDocument(bsoncore.AppendNullElement(nil, \"bar\")),\n\t\t\t),\n\t\t)\n\t\tdvr := NewDocumentReader(bytes.NewReader(b))\n\t\tdr, err := dvr.ReadDocument()\n\t\tnoerr(t, err)\n\t\t_, vr, err := dr.ReadElement()\n\t\tnoerr(t, err)\n\n\t\twant := CodeWithScope{\n\t\t\tCode:  \"var hello = 'world';\",\n\t\t\tScope: D{{\"bar\", nil}},\n\t\t}\n\t\tval := reflect.New(tCodeWithScope).Elem()\n\t\terr = codeWithScopeDecodeValue(dc, vr, val)\n\t\tnoerr(t, err)\n\n\t\tgot := val.Interface().(CodeWithScope)\n\t\tif got.Code != want.Code && !cmp.Equal(got.Scope, want.Scope) {\n\t\t\tt.Errorf(\"CodeWithScopes do not match. got %v; want %v\", got, want)\n\t\t}\n\t})\n\tt.Run(\"ValueUnmarshalerDecodeValue/UnmarshalBSONValue error\", func(t *testing.T) {\n\t\tvar dc DecodeContext\n\t\tllvrw := &valueReaderWriter{BSONType: TypeString, Return: string(\"hello, world!\")}\n\t\tllvrw.T = t\n\n\t\twant := errors.New(\"ubsonv error\")\n\t\tvalUnmarshaler := &testValueUnmarshaler{err: want}\n\t\tgot := valueUnmarshalerDecodeValue(dc, llvrw, reflect.ValueOf(valUnmarshaler))\n\t\tif !assert.CompareErrors(got, want) {\n\t\t\tt.Errorf(\"Errors do not match. got %v; want %v\", got, want)\n\t\t}\n\t})\n\tt.Run(\"ValueUnmarshalerDecodeValue/Unaddressable value\", func(t *testing.T) {\n\t\tvar dc DecodeContext\n\t\tllvrw := &valueReaderWriter{BSONType: TypeString, Return: string(\"hello, world!\")}\n\t\tllvrw.T = t\n\n\t\tval := reflect.ValueOf(testValueUnmarshaler{})\n\t\twant := ValueDecoderError{Name: \"ValueUnmarshalerDecodeValue\", Types: []reflect.Type{tValueUnmarshaler}, Received: val}\n\t\tgot := valueUnmarshalerDecodeValue(dc, llvrw, val)\n\t\tif !assert.CompareErrors(got, want) {\n\t\t\tt.Errorf(\"Errors do not match. got %v; want %v\", got, want)\n\t\t}\n\t})\n\n\tt.Run(\"SliceCodec/DecodeValue/too many elements\", func(t *testing.T) {\n\t\tidx, doc := bsoncore.AppendDocumentStart(nil)\n\t\taidx, doc := bsoncore.AppendArrayElementStart(doc, \"foo\")\n\t\tdoc = bsoncore.AppendStringElement(doc, \"0\", \"foo\")\n\t\tdoc = bsoncore.AppendStringElement(doc, \"1\", \"bar\")\n\t\tdoc, err := bsoncore.AppendArrayEnd(doc, aidx)\n\t\tnoerr(t, err)\n\t\tdoc, err = bsoncore.AppendDocumentEnd(doc, idx)\n\t\tnoerr(t, err)\n\t\tdvr := NewDocumentReader(bytes.NewReader(doc))\n\t\tnoerr(t, err)\n\t\tdr, err := dvr.ReadDocument()\n\t\tnoerr(t, err)\n\t\t_, vr, err := dr.ReadElement()\n\t\tnoerr(t, err)\n\t\tvar val [1]string\n\t\twant := fmt.Errorf(\"more elements returned in array than can fit inside %T, got 2 elements\", val)\n\n\t\tdc := DecodeContext{Registry: buildDefaultRegistry()}\n\t\tgot := arrayDecodeValue(dc, vr, reflect.ValueOf(val))\n\t\tif !assert.CompareErrors(got, want) {\n\t\t\tt.Errorf(\"Errors do not match. got %v; want %v\", got, want)\n\t\t}\n\t})\n\n\tt.Run(\"success path\", func(t *testing.T) {\n\t\toid := NewObjectID()\n\t\toids := []ObjectID{NewObjectID(), NewObjectID(), NewObjectID()}\n\t\tstr := new(string)\n\t\t*str = \"bar\"\n\t\tnow := time.Now().Truncate(time.Millisecond).UTC()\n\t\tmurl, err := url.Parse(\"https://mongodb.com/random-url?hello=world\")\n\t\tif err != nil {\n\t\t\tt.Errorf(\"Error parsing URL: %v\", err)\n\t\t\tt.FailNow()\n\t\t}\n\t\tdecimal128, err := ParseDecimal128(\"1.5e10\")\n\t\tif err != nil {\n\t\t\tt.Errorf(\"Error parsing decimal128: %v\", err)\n\t\t\tt.FailNow()\n\t\t}\n\n\t\ttestCases := []struct {\n\t\t\tname  string\n\t\t\tvalue any\n\t\t\tb     []byte\n\t\t\terr   error\n\t\t}{\n\t\t\t{\n\t\t\t\t\"map[string]int\",\n\t\t\t\tmap[string]int32{\"foo\": 1},\n\t\t\t\t[]byte{\n\t\t\t\t\t0x0E, 0x00, 0x00, 0x00,\n\t\t\t\t\t0x10, 'f', 'o', 'o', 0x00,\n\t\t\t\t\t0x01, 0x00, 0x00, 0x00,\n\t\t\t\t\t0x00,\n\t\t\t\t},\n\t\t\t\tnil,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"map[string]ObjectID\",\n\t\t\t\tmap[string]ObjectID{\"foo\": oid},\n\t\t\t\tfunc() []byte {\n\t\t\t\t\tidx, doc := bsoncore.AppendDocumentStart(nil)\n\t\t\t\t\tdoc = bsoncore.AppendObjectIDElement(doc, \"foo\", oid)\n\t\t\t\t\tdoc, _ = bsoncore.AppendDocumentEnd(doc, idx)\n\t\t\t\t\treturn doc\n\t\t\t\t}(),\n\t\t\t\tnil,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"map[string][]int32\",\n\t\t\t\tmap[string][]int32{\"Z\": {1, 2, 3}},\n\t\t\t\tbuildDocumentArray(func(doc []byte) []byte {\n\t\t\t\t\tdoc = bsoncore.AppendInt32Element(doc, \"0\", 1)\n\t\t\t\t\tdoc = bsoncore.AppendInt32Element(doc, \"1\", 2)\n\t\t\t\t\treturn bsoncore.AppendInt32Element(doc, \"2\", 3)\n\t\t\t\t}),\n\t\t\t\tnil,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"map[string][]ObjectID\",\n\t\t\t\tmap[string][]ObjectID{\"Z\": oids},\n\t\t\t\tbuildDocumentArray(func(doc []byte) []byte {\n\t\t\t\t\tdoc = bsoncore.AppendObjectIDElement(doc, \"0\", oids[0])\n\t\t\t\t\tdoc = bsoncore.AppendObjectIDElement(doc, \"1\", oids[1])\n\t\t\t\t\treturn bsoncore.AppendObjectIDElement(doc, \"2\", oids[2])\n\t\t\t\t}),\n\t\t\t\tnil,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"map[string][]json.Number(int64)\",\n\t\t\t\tmap[string][]json.Number{\"Z\": {json.Number(\"5\"), json.Number(\"10\")}},\n\t\t\t\tbuildDocumentArray(func(doc []byte) []byte {\n\t\t\t\t\tdoc = bsoncore.AppendInt64Element(doc, \"0\", 5)\n\t\t\t\t\treturn bsoncore.AppendInt64Element(doc, \"1\", 10)\n\t\t\t\t}),\n\t\t\t\tnil,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"map[string][]json.Number(float64)\",\n\t\t\t\tmap[string][]json.Number{\"Z\": {json.Number(\"5\"), json.Number(\"10.1\")}},\n\t\t\t\tbuildDocumentArray(func(doc []byte) []byte {\n\t\t\t\t\tdoc = bsoncore.AppendInt64Element(doc, \"0\", 5)\n\t\t\t\t\treturn bsoncore.AppendDoubleElement(doc, \"1\", 10.1)\n\t\t\t\t}),\n\t\t\t\tnil,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"map[string][]*url.URL\",\n\t\t\t\tmap[string][]*url.URL{\"Z\": {murl}},\n\t\t\t\tbuildDocumentArray(func(doc []byte) []byte {\n\t\t\t\t\treturn bsoncore.AppendStringElement(doc, \"0\", murl.String())\n\t\t\t\t}),\n\t\t\t\tnil,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"map[string][]Decimal128\",\n\t\t\t\tmap[string][]Decimal128{\"Z\": {decimal128}},\n\t\t\t\tbuildDocumentArray(func(doc []byte) []byte {\n\t\t\t\t\treturn bsoncore.AppendDecimal128Element(doc, \"0\", decimal128.h, decimal128.l)\n\t\t\t\t}),\n\t\t\t\tnil,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"map[mystring]any\",\n\t\t\t\tmap[mystring]any{\"pi\": 3.14159},\n\t\t\t\tbuildDocument(bsoncore.AppendDoubleElement(nil, \"pi\", 3.14159)),\n\t\t\t\tnil,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"-\",\n\t\t\t\tstruct {\n\t\t\t\t\tA string `bson:\"-\"`\n\t\t\t\t}{\n\t\t\t\t\tA: \"\",\n\t\t\t\t},\n\t\t\t\t[]byte{0x05, 0x00, 0x00, 0x00, 0x00},\n\t\t\t\tnil,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"omitempty\",\n\t\t\t\tstruct {\n\t\t\t\t\tA string `bson:\",omitempty\"`\n\t\t\t\t}{\n\t\t\t\t\tA: \"\",\n\t\t\t\t},\n\t\t\t\t[]byte{0x05, 0x00, 0x00, 0x00, 0x00},\n\t\t\t\tnil,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"omitempty, empty time\",\n\t\t\t\tstruct {\n\t\t\t\t\tA time.Time `bson:\",omitempty\"`\n\t\t\t\t}{\n\t\t\t\t\tA: time.Time{},\n\t\t\t\t},\n\t\t\t\t[]byte{0x05, 0x00, 0x00, 0x00, 0x00},\n\t\t\t\tnil,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"no private fields\",\n\t\t\t\tnoPrivateFields{a: \"should be empty\"},\n\t\t\t\t[]byte{0x05, 0x00, 0x00, 0x00, 0x00},\n\t\t\t\tnil,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"minsize\",\n\t\t\t\tstruct {\n\t\t\t\t\tA int64 `bson:\",minsize\"`\n\t\t\t\t}{\n\t\t\t\t\tA: 12345,\n\t\t\t\t},\n\t\t\t\tbuildDocument(bsoncore.AppendInt32Element(nil, \"a\", 12345)),\n\t\t\t\tnil,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"inline\",\n\t\t\t\tstruct {\n\t\t\t\t\tFoo struct {\n\t\t\t\t\t\tA int64 `bson:\",minsize\"`\n\t\t\t\t\t} `bson:\",inline\"`\n\t\t\t\t}{\n\t\t\t\t\tFoo: struct {\n\t\t\t\t\t\tA int64 `bson:\",minsize\"`\n\t\t\t\t\t}{\n\t\t\t\t\t\tA: 12345,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tbuildDocument(bsoncore.AppendInt32Element(nil, \"a\", 12345)),\n\t\t\t\tnil,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"inline struct pointer\",\n\t\t\t\tstruct {\n\t\t\t\t\tFoo *struct {\n\t\t\t\t\t\tA int64 `bson:\",minsize\"`\n\t\t\t\t\t} `bson:\",inline\"`\n\t\t\t\t\tBar *struct {\n\t\t\t\t\t\tB int64\n\t\t\t\t\t} `bson:\",inline\"`\n\t\t\t\t}{\n\t\t\t\t\tFoo: &struct {\n\t\t\t\t\t\tA int64 `bson:\",minsize\"`\n\t\t\t\t\t}{\n\t\t\t\t\t\tA: 12345,\n\t\t\t\t\t},\n\t\t\t\t\tBar: nil,\n\t\t\t\t},\n\t\t\t\tbuildDocument(bsoncore.AppendInt32Element(nil, \"a\", 12345)),\n\t\t\t\tnil,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"nested inline struct pointer\",\n\t\t\t\tstruct {\n\t\t\t\t\tFoo *struct {\n\t\t\t\t\t\tBar *struct {\n\t\t\t\t\t\t\tA int64 `bson:\",minsize\"`\n\t\t\t\t\t\t} `bson:\",inline\"`\n\t\t\t\t\t} `bson:\",inline\"`\n\t\t\t\t}{\n\t\t\t\t\tFoo: &struct {\n\t\t\t\t\t\tBar *struct {\n\t\t\t\t\t\t\tA int64 `bson:\",minsize\"`\n\t\t\t\t\t\t} `bson:\",inline\"`\n\t\t\t\t\t}{\n\t\t\t\t\t\tBar: &struct {\n\t\t\t\t\t\t\tA int64 `bson:\",minsize\"`\n\t\t\t\t\t\t}{\n\t\t\t\t\t\t\tA: 12345,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tbuildDocument(bsoncore.AppendInt32Element(nil, \"a\", 12345)),\n\t\t\t\tnil,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"inline nil struct pointer\",\n\t\t\t\tstruct {\n\t\t\t\t\tFoo *struct {\n\t\t\t\t\t\tA int64 `bson:\",minsize\"`\n\t\t\t\t\t} `bson:\",inline\"`\n\t\t\t\t}{\n\t\t\t\t\tFoo: nil,\n\t\t\t\t},\n\t\t\t\tbuildDocument([]byte{}),\n\t\t\t\tnil,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"inline overwrite\",\n\t\t\t\tstruct {\n\t\t\t\t\tFoo struct {\n\t\t\t\t\t\tA int32\n\t\t\t\t\t\tB string\n\t\t\t\t\t} `bson:\",inline\"`\n\t\t\t\t\tA int64\n\t\t\t\t}{\n\t\t\t\t\tFoo: struct {\n\t\t\t\t\t\tA int32\n\t\t\t\t\t\tB string\n\t\t\t\t\t}{\n\t\t\t\t\t\tA: 0,\n\t\t\t\t\t\tB: \"foo\",\n\t\t\t\t\t},\n\t\t\t\t\tA: 54321,\n\t\t\t\t},\n\t\t\t\tbuildDocument(func(doc []byte) []byte {\n\t\t\t\t\tdoc = bsoncore.AppendStringElement(doc, \"b\", \"foo\")\n\t\t\t\t\tdoc = bsoncore.AppendInt64Element(doc, \"a\", 54321)\n\t\t\t\t\treturn doc\n\t\t\t\t}(nil)),\n\t\t\t\tnil,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"inline overwrite with nested structs\",\n\t\t\t\tstruct {\n\t\t\t\t\tFoo struct {\n\t\t\t\t\t\tA int32\n\t\t\t\t\t} `bson:\",inline\"`\n\t\t\t\t\tBar struct {\n\t\t\t\t\t\tA int32\n\t\t\t\t\t} `bson:\",inline\"`\n\t\t\t\t\tA int64\n\t\t\t\t}{\n\t\t\t\t\tFoo: struct {\n\t\t\t\t\t\tA int32\n\t\t\t\t\t}{},\n\t\t\t\t\tBar: struct {\n\t\t\t\t\t\tA int32\n\t\t\t\t\t}{},\n\t\t\t\t\tA: 54321,\n\t\t\t\t},\n\t\t\t\tbuildDocument(bsoncore.AppendInt64Element(nil, \"a\", 54321)),\n\t\t\t\tnil,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"inline map\",\n\t\t\t\tstruct {\n\t\t\t\t\tFoo map[string]string `bson:\",inline\"`\n\t\t\t\t}{\n\t\t\t\t\tFoo: map[string]string{\"foo\": \"bar\"},\n\t\t\t\t},\n\t\t\t\tbuildDocument(bsoncore.AppendStringElement(nil, \"foo\", \"bar\")),\n\t\t\t\tnil,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"alternate name bson:name\",\n\t\t\t\tstruct {\n\t\t\t\t\tA string `bson:\"foo\"`\n\t\t\t\t}{\n\t\t\t\t\tA: \"bar\",\n\t\t\t\t},\n\t\t\t\tbuildDocument(bsoncore.AppendStringElement(nil, \"foo\", \"bar\")),\n\t\t\t\tnil,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"alternate name\",\n\t\t\t\tstruct {\n\t\t\t\t\tA string `bson:\"foo\"`\n\t\t\t\t}{\n\t\t\t\t\tA: \"bar\",\n\t\t\t\t},\n\t\t\t\tbuildDocument(bsoncore.AppendStringElement(nil, \"foo\", \"bar\")),\n\t\t\t\tnil,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"inline, omitempty\",\n\t\t\t\tstruct {\n\t\t\t\t\tA   string\n\t\t\t\t\tFoo zeroTest `bson:\"omitempty,inline\"`\n\t\t\t\t}{\n\t\t\t\t\tA:   \"bar\",\n\t\t\t\t\tFoo: zeroTest{true},\n\t\t\t\t},\n\t\t\t\tbuildDocument(bsoncore.AppendStringElement(nil, \"a\", \"bar\")),\n\t\t\t\tnil,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"struct{}\",\n\t\t\t\tstruct {\n\t\t\t\t\tA bool\n\t\t\t\t\tB int32\n\t\t\t\t\tC int64\n\t\t\t\t\tD uint16\n\t\t\t\t\tE uint64\n\t\t\t\t\tF float64\n\t\t\t\t\tG string\n\t\t\t\t\tH map[string]string\n\t\t\t\t\tI []byte\n\t\t\t\t\tK [2]string\n\t\t\t\t\tL struct {\n\t\t\t\t\t\tM string\n\t\t\t\t\t}\n\t\t\t\t\tQ  ObjectID\n\t\t\t\t\tT  []struct{}\n\t\t\t\t\tY  json.Number\n\t\t\t\t\tZ  time.Time\n\t\t\t\t\tAA json.Number\n\t\t\t\t\tAB *url.URL\n\t\t\t\t\tAC Decimal128\n\t\t\t\t\tAD *time.Time\n\t\t\t\t\tAE *testValueUnmarshaler\n\t\t\t\t\tAF *bool\n\t\t\t\t\tAG *bool\n\t\t\t\t\tAH *int32\n\t\t\t\t\tAI *int64\n\t\t\t\t\tAJ *ObjectID\n\t\t\t\t\tAK *ObjectID\n\t\t\t\t\tAL testValueUnmarshaler\n\t\t\t\t\tAM any\n\t\t\t\t\tAN any\n\t\t\t\t\tAO any\n\t\t\t\t\tAP D\n\t\t\t\t\tAQ A\n\t\t\t\t\tAR [2]E\n\t\t\t\t\tAS []byte\n\t\t\t\t\tAT map[string]any\n\t\t\t\t\tAU CodeWithScope\n\t\t\t\t\tAV M\n\t\t\t\t\tAW D\n\t\t\t\t\tAX map[string]any\n\t\t\t\t\tAY []E\n\t\t\t\t\tAZ any\n\t\t\t\t}{\n\t\t\t\t\tA: true,\n\t\t\t\t\tB: 123,\n\t\t\t\t\tC: 456,\n\t\t\t\t\tD: 789,\n\t\t\t\t\tE: 101112,\n\t\t\t\t\tF: 3.14159,\n\t\t\t\t\tG: \"Hello, world\",\n\t\t\t\t\tH: map[string]string{\"foo\": \"bar\"},\n\t\t\t\t\tI: []byte{0x01, 0x02, 0x03},\n\t\t\t\t\tK: [2]string{\"baz\", \"qux\"},\n\t\t\t\t\tL: struct {\n\t\t\t\t\t\tM string\n\t\t\t\t\t}{\n\t\t\t\t\t\tM: \"foobar\",\n\t\t\t\t\t},\n\t\t\t\t\tQ:  oid,\n\t\t\t\t\tT:  nil,\n\t\t\t\t\tY:  json.Number(\"5\"),\n\t\t\t\t\tZ:  now,\n\t\t\t\t\tAA: json.Number(\"10.1\"),\n\t\t\t\t\tAB: murl,\n\t\t\t\t\tAC: decimal128,\n\t\t\t\t\tAD: &now,\n\t\t\t\t\tAE: &testValueUnmarshaler{t: TypeString, val: bsoncore.AppendString(nil, \"hello, world!\")},\n\t\t\t\t\tAF: func(b bool) *bool { return &b }(true),\n\t\t\t\t\tAG: nil,\n\t\t\t\t\tAH: func(i32 int32) *int32 { return &i32 }(12345),\n\t\t\t\t\tAI: func(i64 int64) *int64 { return &i64 }(1234567890),\n\t\t\t\t\tAJ: &oid,\n\t\t\t\t\tAK: nil,\n\t\t\t\t\tAL: testValueUnmarshaler{t: TypeString, val: bsoncore.AppendString(nil, \"hello, world!\")},\n\t\t\t\t\tAM: \"hello, world\",\n\t\t\t\t\tAN: int32(12345),\n\t\t\t\t\tAO: oid,\n\t\t\t\t\tAP: D{{\"foo\", \"bar\"}},\n\t\t\t\t\tAQ: A{\"foo\", \"bar\"},\n\t\t\t\t\tAR: [2]E{{\"hello\", \"world\"}, {\"pi\", 3.14159}},\n\t\t\t\t\tAS: nil,\n\t\t\t\t\tAT: nil,\n\t\t\t\t\tAU: CodeWithScope{Code: \"var hello = 'world';\", Scope: D{{\"pi\", 3.14159}}},\n\t\t\t\t\tAV: M{\"foo\": D{{\"bar\", \"baz\"}}},\n\t\t\t\t\tAW: D{{\"foo\", D{{\"bar\", \"baz\"}}}},\n\t\t\t\t\tAX: map[string]any{\"foo\": D{{\"bar\", \"baz\"}}},\n\t\t\t\t\tAY: []E{{\"foo\", D{{\"bar\", \"baz\"}}}},\n\t\t\t\t\tAZ: D{{\"foo\", D{{\"bar\", \"baz\"}}}},\n\t\t\t\t},\n\t\t\t\tbuildDocument(func(doc []byte) []byte {\n\t\t\t\t\tdoc = bsoncore.AppendBooleanElement(doc, \"a\", true)\n\t\t\t\t\tdoc = bsoncore.AppendInt32Element(doc, \"b\", 123)\n\t\t\t\t\tdoc = bsoncore.AppendInt64Element(doc, \"c\", 456)\n\t\t\t\t\tdoc = bsoncore.AppendInt32Element(doc, \"d\", 789)\n\t\t\t\t\tdoc = bsoncore.AppendInt64Element(doc, \"e\", 101112)\n\t\t\t\t\tdoc = bsoncore.AppendDoubleElement(doc, \"f\", 3.14159)\n\t\t\t\t\tdoc = bsoncore.AppendStringElement(doc, \"g\", \"Hello, world\")\n\t\t\t\t\tdoc = bsoncore.AppendDocumentElement(doc, \"h\", buildDocument(bsoncore.AppendStringElement(nil, \"foo\", \"bar\")))\n\t\t\t\t\tdoc = bsoncore.AppendBinaryElement(doc, \"i\", 0x00, []byte{0x01, 0x02, 0x03})\n\t\t\t\t\tdoc = bsoncore.AppendArrayElement(doc, \"k\",\n\t\t\t\t\t\tbuildArray(bsoncore.AppendStringElement(bsoncore.AppendStringElement(nil, \"0\", \"baz\"), \"1\", \"qux\")),\n\t\t\t\t\t)\n\t\t\t\t\tdoc = bsoncore.AppendDocumentElement(doc, \"l\", buildDocument(bsoncore.AppendStringElement(nil, \"m\", \"foobar\")))\n\t\t\t\t\tdoc = bsoncore.AppendObjectIDElement(doc, \"q\", oid)\n\t\t\t\t\tdoc = bsoncore.AppendNullElement(doc, \"t\")\n\t\t\t\t\tdoc = bsoncore.AppendInt64Element(doc, \"y\", 5)\n\t\t\t\t\tdoc = bsoncore.AppendDateTimeElement(doc, \"z\", now.UnixNano()/int64(time.Millisecond))\n\t\t\t\t\tdoc = bsoncore.AppendDoubleElement(doc, \"aa\", 10.1)\n\t\t\t\t\tdoc = bsoncore.AppendStringElement(doc, \"ab\", murl.String())\n\t\t\t\t\tdoc = bsoncore.AppendDecimal128Element(doc, \"ac\", decimal128.h, decimal128.l)\n\t\t\t\t\tdoc = bsoncore.AppendDateTimeElement(doc, \"ad\", now.UnixNano()/int64(time.Millisecond))\n\t\t\t\t\tdoc = bsoncore.AppendStringElement(doc, \"ae\", \"hello, world!\")\n\t\t\t\t\tdoc = bsoncore.AppendBooleanElement(doc, \"af\", true)\n\t\t\t\t\tdoc = bsoncore.AppendNullElement(doc, \"ag\")\n\t\t\t\t\tdoc = bsoncore.AppendInt32Element(doc, \"ah\", 12345)\n\t\t\t\t\tdoc = bsoncore.AppendInt32Element(doc, \"ai\", 1234567890)\n\t\t\t\t\tdoc = bsoncore.AppendObjectIDElement(doc, \"aj\", oid)\n\t\t\t\t\tdoc = bsoncore.AppendNullElement(doc, \"ak\")\n\t\t\t\t\tdoc = bsoncore.AppendStringElement(doc, \"al\", \"hello, world!\")\n\t\t\t\t\tdoc = bsoncore.AppendStringElement(doc, \"am\", \"hello, world\")\n\t\t\t\t\tdoc = bsoncore.AppendInt32Element(doc, \"an\", 12345)\n\t\t\t\t\tdoc = bsoncore.AppendObjectIDElement(doc, \"ao\", oid)\n\t\t\t\t\tdoc = bsoncore.AppendDocumentElement(doc, \"ap\", buildDocument(bsoncore.AppendStringElement(nil, \"foo\", \"bar\")))\n\t\t\t\t\tdoc = bsoncore.AppendArrayElement(doc, \"aq\",\n\t\t\t\t\t\tbuildArray(bsoncore.AppendStringElement(bsoncore.AppendStringElement(nil, \"0\", \"foo\"), \"1\", \"bar\")),\n\t\t\t\t\t)\n\t\t\t\t\tdoc = bsoncore.AppendDocumentElement(doc, \"ar\",\n\t\t\t\t\t\tbuildDocument(bsoncore.AppendDoubleElement(bsoncore.AppendStringElement(nil, \"hello\", \"world\"), \"pi\", 3.14159)),\n\t\t\t\t\t)\n\t\t\t\t\tdoc = bsoncore.AppendNullElement(doc, \"as\")\n\t\t\t\t\tdoc = bsoncore.AppendNullElement(doc, \"at\")\n\t\t\t\t\tdoc = bsoncore.AppendCodeWithScopeElement(doc, \"au\",\n\t\t\t\t\t\t\"var hello = 'world';\", buildDocument(bsoncore.AppendDoubleElement(nil, \"pi\", 3.14159)),\n\t\t\t\t\t)\n\t\t\t\t\tfor _, name := range [5]string{\"av\", \"aw\", \"ax\", \"ay\", \"az\"} {\n\t\t\t\t\t\tdoc = bsoncore.AppendDocumentElement(doc, name, buildDocument(\n\t\t\t\t\t\t\tbsoncore.AppendDocumentElement(nil, \"foo\", buildDocument(\n\t\t\t\t\t\t\t\tbsoncore.AppendStringElement(nil, \"bar\", \"baz\"),\n\t\t\t\t\t\t\t)),\n\t\t\t\t\t\t))\n\t\t\t\t\t}\n\t\t\t\t\treturn doc\n\t\t\t\t}(nil)),\n\t\t\t\tnil,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"struct{[]any}\",\n\t\t\t\tstruct {\n\t\t\t\t\tA []bool\n\t\t\t\t\tB []int32\n\t\t\t\t\tC []int64\n\t\t\t\t\tD []uint16\n\t\t\t\t\tE []uint64\n\t\t\t\t\tF []float64\n\t\t\t\t\tG []string\n\t\t\t\t\tH []map[string]string\n\t\t\t\t\tI [][]byte\n\t\t\t\t\tK [1][2]string\n\t\t\t\t\tL []struct {\n\t\t\t\t\t\tM string\n\t\t\t\t\t}\n\t\t\t\t\tN  [][]string\n\t\t\t\t\tR  []ObjectID\n\t\t\t\t\tT  []struct{}\n\t\t\t\t\tW  []map[string]struct{}\n\t\t\t\t\tX  []map[string]struct{}\n\t\t\t\t\tY  []map[string]struct{}\n\t\t\t\t\tZ  []time.Time\n\t\t\t\t\tAA []json.Number\n\t\t\t\t\tAB []*url.URL\n\t\t\t\t\tAC []Decimal128\n\t\t\t\t\tAD []*time.Time\n\t\t\t\t\tAE []*testValueUnmarshaler\n\t\t\t\t\tAF []*bool\n\t\t\t\t\tAG []*int32\n\t\t\t\t\tAH []*int64\n\t\t\t\t\tAI []*ObjectID\n\t\t\t\t\tAJ []D\n\t\t\t\t\tAK []A\n\t\t\t\t\tAL [][2]E\n\t\t\t\t}{\n\t\t\t\t\tA: []bool{true},\n\t\t\t\t\tB: []int32{123},\n\t\t\t\t\tC: []int64{456},\n\t\t\t\t\tD: []uint16{789},\n\t\t\t\t\tE: []uint64{101112},\n\t\t\t\t\tF: []float64{3.14159},\n\t\t\t\t\tG: []string{\"Hello, world\"},\n\t\t\t\t\tH: []map[string]string{{\"foo\": \"bar\"}},\n\t\t\t\t\tI: [][]byte{{0x01, 0x02, 0x03}},\n\t\t\t\t\tK: [1][2]string{{\"baz\", \"qux\"}},\n\t\t\t\t\tL: []struct {\n\t\t\t\t\t\tM string\n\t\t\t\t\t}{\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tM: \"foobar\",\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\tN:  [][]string{{\"foo\", \"bar\"}},\n\t\t\t\t\tR:  oids,\n\t\t\t\t\tT:  nil,\n\t\t\t\t\tW:  nil,\n\t\t\t\t\tX:  []map[string]struct{}{},   // Should be empty BSON Array\n\t\t\t\t\tY:  []map[string]struct{}{{}}, // Should be BSON array with one element, an empty BSON SubDocument\n\t\t\t\t\tZ:  []time.Time{now, now},\n\t\t\t\t\tAA: []json.Number{json.Number(\"5\"), json.Number(\"10.1\")},\n\t\t\t\t\tAB: []*url.URL{murl},\n\t\t\t\t\tAC: []Decimal128{decimal128},\n\t\t\t\t\tAD: []*time.Time{&now, &now},\n\t\t\t\t\tAE: []*testValueUnmarshaler{\n\t\t\t\t\t\t{t: TypeString, val: bsoncore.AppendString(nil, \"hello\")},\n\t\t\t\t\t\t{t: TypeString, val: bsoncore.AppendString(nil, \"world\")},\n\t\t\t\t\t},\n\t\t\t\t\tAF: []*bool{pbool(true), nil},\n\t\t\t\t\tAG: []*int32{pi32(12345), nil},\n\t\t\t\t\tAH: []*int64{pi64(1234567890), nil, pi64(9012345678)},\n\t\t\t\t\tAI: []*ObjectID{&oid, nil},\n\t\t\t\t\tAJ: []D{{{\"foo\", \"bar\"}}, nil},\n\t\t\t\t\tAK: []A{{\"foo\", \"bar\"}, nil},\n\t\t\t\t\tAL: [][2]E{{{\"hello\", \"world\"}, {\"pi\", 3.14159}}},\n\t\t\t\t},\n\t\t\t\tbuildDocument(func(doc []byte) []byte {\n\t\t\t\t\tdoc = appendArrayElement(doc, \"a\", bsoncore.AppendBooleanElement(nil, \"0\", true))\n\t\t\t\t\tdoc = appendArrayElement(doc, \"b\", bsoncore.AppendInt32Element(nil, \"0\", 123))\n\t\t\t\t\tdoc = appendArrayElement(doc, \"c\", bsoncore.AppendInt64Element(nil, \"0\", 456))\n\t\t\t\t\tdoc = appendArrayElement(doc, \"d\", bsoncore.AppendInt32Element(nil, \"0\", 789))\n\t\t\t\t\tdoc = appendArrayElement(doc, \"e\", bsoncore.AppendInt64Element(nil, \"0\", 101112))\n\t\t\t\t\tdoc = appendArrayElement(doc, \"f\", bsoncore.AppendDoubleElement(nil, \"0\", 3.14159))\n\t\t\t\t\tdoc = appendArrayElement(doc, \"g\", bsoncore.AppendStringElement(nil, \"0\", \"Hello, world\"))\n\t\t\t\t\tdoc = appendArrayElement(doc, \"h\", bsoncore.BuildDocumentElement(nil, \"0\", bsoncore.AppendStringElement(nil, \"foo\", \"bar\")))\n\t\t\t\t\tdoc = appendArrayElement(doc, \"i\", bsoncore.AppendBinaryElement(nil, \"0\", 0x00, []byte{0x01, 0x02, 0x03}))\n\t\t\t\t\tdoc = appendArrayElement(doc, \"k\",\n\t\t\t\t\t\tappendArrayElement(nil, \"0\",\n\t\t\t\t\t\t\tbsoncore.AppendStringElement(bsoncore.AppendStringElement(nil, \"0\", \"baz\"), \"1\", \"qux\")),\n\t\t\t\t\t)\n\t\t\t\t\tdoc = appendArrayElement(doc, \"l\", bsoncore.BuildDocumentElement(nil, \"0\", bsoncore.AppendStringElement(nil, \"m\", \"foobar\")))\n\t\t\t\t\tdoc = appendArrayElement(doc, \"n\",\n\t\t\t\t\t\tappendArrayElement(nil, \"0\",\n\t\t\t\t\t\t\tbsoncore.AppendStringElement(bsoncore.AppendStringElement(nil, \"0\", \"foo\"), \"1\", \"bar\")),\n\t\t\t\t\t)\n\t\t\t\t\tdoc = appendArrayElement(doc, \"r\",\n\t\t\t\t\t\tbsoncore.AppendObjectIDElement(\n\t\t\t\t\t\t\tbsoncore.AppendObjectIDElement(\n\t\t\t\t\t\t\t\tbsoncore.AppendObjectIDElement(nil,\n\t\t\t\t\t\t\t\t\t\"0\", oids[0]),\n\t\t\t\t\t\t\t\t\"1\", oids[1]),\n\t\t\t\t\t\t\t\"2\", oids[2]),\n\t\t\t\t\t)\n\t\t\t\t\tdoc = bsoncore.AppendNullElement(doc, \"t\")\n\t\t\t\t\tdoc = bsoncore.AppendNullElement(doc, \"w\")\n\t\t\t\t\tdoc = appendArrayElement(doc, \"x\", nil)\n\t\t\t\t\tdoc = appendArrayElement(doc, \"y\", bsoncore.BuildDocumentElement(nil, \"0\", nil))\n\t\t\t\t\tdoc = appendArrayElement(doc, \"z\",\n\t\t\t\t\t\tbsoncore.AppendDateTimeElement(\n\t\t\t\t\t\t\tbsoncore.AppendDateTimeElement(\n\t\t\t\t\t\t\t\tnil, \"0\", now.UnixNano()/int64(time.Millisecond)),\n\t\t\t\t\t\t\t\"1\", now.UnixNano()/int64(time.Millisecond)),\n\t\t\t\t\t)\n\t\t\t\t\tdoc = appendArrayElement(doc, \"aa\", bsoncore.AppendDoubleElement(bsoncore.AppendInt64Element(nil, \"0\", 5), \"1\", 10.10))\n\t\t\t\t\tdoc = appendArrayElement(doc, \"ab\", bsoncore.AppendStringElement(nil, \"0\", murl.String()))\n\t\t\t\t\tdoc = appendArrayElement(doc, \"ac\", bsoncore.AppendDecimal128Element(nil, \"0\", decimal128.h, decimal128.l))\n\t\t\t\t\tdoc = appendArrayElement(doc, \"ad\",\n\t\t\t\t\t\tbsoncore.AppendDateTimeElement(\n\t\t\t\t\t\t\tbsoncore.AppendDateTimeElement(nil, \"0\", now.UnixNano()/int64(time.Millisecond)),\n\t\t\t\t\t\t\t\"1\", now.UnixNano()/int64(time.Millisecond)),\n\t\t\t\t\t)\n\t\t\t\t\tdoc = appendArrayElement(doc, \"ae\",\n\t\t\t\t\t\tbsoncore.AppendStringElement(bsoncore.AppendStringElement(nil, \"0\", \"hello\"), \"1\", \"world\"),\n\t\t\t\t\t)\n\t\t\t\t\tdoc = appendArrayElement(doc, \"af\",\n\t\t\t\t\t\tbsoncore.AppendNullElement(bsoncore.AppendBooleanElement(nil, \"0\", true), \"1\"),\n\t\t\t\t\t)\n\t\t\t\t\tdoc = appendArrayElement(doc, \"ag\",\n\t\t\t\t\t\tbsoncore.AppendNullElement(bsoncore.AppendInt32Element(nil, \"0\", 12345), \"1\"),\n\t\t\t\t\t)\n\t\t\t\t\tdoc = appendArrayElement(doc, \"ah\",\n\t\t\t\t\t\tbsoncore.AppendInt64Element(\n\t\t\t\t\t\t\tbsoncore.AppendNullElement(bsoncore.AppendInt64Element(nil, \"0\", 1234567890), \"1\"),\n\t\t\t\t\t\t\t\"2\", 9012345678,\n\t\t\t\t\t\t),\n\t\t\t\t\t)\n\t\t\t\t\tdoc = appendArrayElement(doc, \"ai\",\n\t\t\t\t\t\tbsoncore.AppendNullElement(bsoncore.AppendObjectIDElement(nil, \"0\", oid), \"1\"),\n\t\t\t\t\t)\n\t\t\t\t\tdoc = appendArrayElement(doc, \"aj\",\n\t\t\t\t\t\tbsoncore.AppendNullElement(\n\t\t\t\t\t\t\tbsoncore.AppendDocumentElement(nil, \"0\", buildDocument(bsoncore.AppendStringElement(nil, \"foo\", \"bar\"))),\n\t\t\t\t\t\t\t\"1\",\n\t\t\t\t\t\t),\n\t\t\t\t\t)\n\t\t\t\t\tdoc = appendArrayElement(doc, \"ak\",\n\t\t\t\t\t\tbsoncore.AppendNullElement(\n\t\t\t\t\t\t\tappendArrayElement(nil, \"0\",\n\t\t\t\t\t\t\t\tbsoncore.AppendStringElement(bsoncore.AppendStringElement(nil, \"0\", \"foo\"), \"1\", \"bar\"),\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\"1\",\n\t\t\t\t\t\t),\n\t\t\t\t\t)\n\t\t\t\t\tdoc = appendArrayElement(doc, \"al\",\n\t\t\t\t\t\tbsoncore.BuildDocumentElement(nil, \"0\",\n\t\t\t\t\t\t\tbsoncore.AppendDoubleElement(bsoncore.AppendStringElement(nil, \"hello\", \"world\"), \"pi\", 3.14159),\n\t\t\t\t\t\t),\n\t\t\t\t\t)\n\t\t\t\t\treturn doc\n\t\t\t\t}(nil)),\n\t\t\t\tnil,\n\t\t\t},\n\t\t}\n\n\t\tt.Run(\"Decode\", func(t *testing.T) {\n\t\t\tcompareTime := func(t1, t2 time.Time) bool {\n\t\t\t\tif t1.Location() != t2.Location() {\n\t\t\t\t\treturn false\n\t\t\t\t}\n\t\t\t\treturn t1.Equal(t2)\n\t\t\t}\n\n\t\t\tfor _, tc := range testCases {\n\t\t\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\t\t\tvr := NewDocumentReader(bytes.NewReader(tc.b))\n\t\t\t\t\treg := buildDefaultRegistry()\n\t\t\t\t\tvtype := reflect.TypeOf(tc.value)\n\t\t\t\t\tdec, err := reg.LookupDecoder(vtype)\n\t\t\t\t\tnoerr(t, err)\n\n\t\t\t\t\tgotVal := reflect.New(reflect.TypeOf(tc.value)).Elem()\n\t\t\t\t\terr = dec.DecodeValue(DecodeContext{Registry: reg}, vr, gotVal)\n\t\t\t\t\tnoerr(t, err)\n\n\t\t\t\t\tgot := gotVal.Interface()\n\t\t\t\t\twant := tc.value\n\t\t\t\t\tif diff := cmp.Diff(\n\t\t\t\t\t\tgot, want,\n\t\t\t\t\t\tcmp.Comparer(compareDecimal128),\n\t\t\t\t\t\tcmp.Comparer(compareNoPrivateFields),\n\t\t\t\t\t\tcmp.Comparer(compareZeroTest),\n\t\t\t\t\t\tcmp.Comparer(compareTime),\n\t\t\t\t\t); diff != \"\" {\n\t\t\t\t\t\tt.Errorf(\"difference:\\n%s\", diff)\n\t\t\t\t\t\tt.Errorf(\"Values are not equal.\\ngot: %#v\\nwant:%#v\", got, want)\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\t})\n\tt.Run(\"error path\", func(t *testing.T) {\n\t\ttestCases := []struct {\n\t\t\tname  string\n\t\t\tvalue any\n\t\t\tb     []byte\n\t\t\terr   error\n\t\t}{\n\t\t\t{\n\t\t\t\t\"duplicate name struct\",\n\t\t\t\tstruct {\n\t\t\t\t\tA int64\n\t\t\t\t\tB int64 `bson:\"a\"`\n\t\t\t\t}{\n\t\t\t\t\tA: 0,\n\t\t\t\t\tB: 54321,\n\t\t\t\t},\n\t\t\t\tbuildDocument(bsoncore.AppendInt32Element(nil, \"a\", 12345)),\n\t\t\t\tfmt.Errorf(\"duplicated key a\"),\n\t\t\t},\n\t\t}\n\n\t\tfor _, tc := range testCases {\n\t\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\t\tvr := NewDocumentReader(bytes.NewReader(tc.b))\n\t\t\t\treg := buildDefaultRegistry()\n\t\t\t\tvtype := reflect.TypeOf(tc.value)\n\t\t\t\tdec, err := reg.LookupDecoder(vtype)\n\t\t\t\tnoerr(t, err)\n\n\t\t\t\tgotVal := reflect.New(reflect.TypeOf(tc.value)).Elem()\n\t\t\t\terr = dec.DecodeValue(DecodeContext{Registry: reg}, vr, gotVal)\n\t\t\t\tif err == nil || !strings.Contains(err.Error(), tc.err.Error()) {\n\t\t\t\t\tt.Errorf(\"Did not receive expected error. got %v; want %v\", err, tc.err)\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"defaultEmptyInterfaceCodec.DecodeValue\", func(t *testing.T) {\n\t\tt.Run(\"DecodeValue\", func(t *testing.T) {\n\t\t\ttestCases := []struct {\n\t\t\t\tname     string\n\t\t\t\tval      any\n\t\t\t\tbsontype Type\n\t\t\t}{\n\t\t\t\t{\n\t\t\t\t\t\"Double - float64\",\n\t\t\t\t\tfloat64(3.14159),\n\t\t\t\t\tTypeDouble,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"String - string\",\n\t\t\t\t\t\"foo bar baz\",\n\t\t\t\t\tTypeString,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"Array - A\",\n\t\t\t\t\tA{3.14159},\n\t\t\t\t\tTypeArray,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"Binary - Binary\",\n\t\t\t\t\tBinary{Subtype: 0xFF, Data: []byte{0x01, 0x02, 0x03}},\n\t\t\t\t\tTypeBinary,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"Undefined - Undefined\",\n\t\t\t\t\tUndefined{},\n\t\t\t\t\tTypeUndefined,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"ObjectID - ObjectID\",\n\t\t\t\t\tObjectID{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C},\n\t\t\t\t\tTypeObjectID,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"Boolean - bool\",\n\t\t\t\t\tbool(true),\n\t\t\t\t\tTypeBoolean,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"DateTime - DateTime\",\n\t\t\t\t\tDateTime(1234567890),\n\t\t\t\t\tTypeDateTime,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"Null - Null\",\n\t\t\t\t\tnil,\n\t\t\t\t\tTypeNull,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"Regex - Regex\",\n\t\t\t\t\tRegex{Pattern: \"foo\", Options: \"bar\"},\n\t\t\t\t\tTypeRegex,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"DBPointer - DBPointer\",\n\t\t\t\t\tDBPointer{\n\t\t\t\t\t\tDB:      \"foobar\",\n\t\t\t\t\t\tPointer: ObjectID{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C},\n\t\t\t\t\t},\n\t\t\t\t\tTypeDBPointer,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"JavaScript - JavaScript\",\n\t\t\t\t\tJavaScript(\"var foo = 'bar';\"),\n\t\t\t\t\tTypeJavaScript,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"Symbol - Symbol\",\n\t\t\t\t\tSymbol(\"foobarbazlolz\"),\n\t\t\t\t\tTypeSymbol,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"Int32 - int32\",\n\t\t\t\t\tint32(123456),\n\t\t\t\t\tTypeInt32,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"Int64 - int64\",\n\t\t\t\t\tint64(1234567890),\n\t\t\t\t\tTypeInt64,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"Timestamp - Timestamp\",\n\t\t\t\t\tTimestamp{T: 12345, I: 67890},\n\t\t\t\t\tTypeTimestamp,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"Decimal128 - decimal.Decimal128\",\n\t\t\t\t\tNewDecimal128(12345, 67890),\n\t\t\t\t\tTypeDecimal128,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"MinKey - MinKey\",\n\t\t\t\t\tMinKey{},\n\t\t\t\t\tTypeMinKey,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"MaxKey - MaxKey\",\n\t\t\t\t\tMaxKey{},\n\t\t\t\t\tTypeMaxKey,\n\t\t\t\t},\n\t\t\t}\n\t\t\tfor _, tc := range testCases {\n\t\t\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\t\t\tllvr := &valueReaderWriter{BSONType: tc.bsontype}\n\n\t\t\t\t\tt.Run(\"Type Map failure\", func(t *testing.T) {\n\t\t\t\t\t\tif tc.bsontype == TypeNull {\n\t\t\t\t\t\t\tt.Skip()\n\t\t\t\t\t\t}\n\t\t\t\t\t\tval := reflect.New(tEmpty).Elem()\n\t\t\t\t\t\tdc := DecodeContext{Registry: newTestRegistry()}\n\t\t\t\t\t\twant := errNoTypeMapEntry{Type: tc.bsontype}\n\t\t\t\t\t\tgot := (&emptyInterfaceCodec{}).DecodeValue(dc, llvr, val)\n\t\t\t\t\t\tif !assert.CompareErrors(got, want) {\n\t\t\t\t\t\t\tt.Errorf(\"Errors are not equal. got %v; want %v\", got, want)\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\n\t\t\t\t\tt.Run(\"Lookup failure\", func(t *testing.T) {\n\t\t\t\t\t\tif tc.bsontype == TypeNull {\n\t\t\t\t\t\t\tt.Skip()\n\t\t\t\t\t\t}\n\t\t\t\t\t\tval := reflect.New(tEmpty).Elem()\n\t\t\t\t\t\treg := newTestRegistry()\n\t\t\t\t\t\treg.RegisterTypeMapEntry(tc.bsontype, reflect.TypeOf(tc.val))\n\t\t\t\t\t\tdc := DecodeContext{\n\t\t\t\t\t\t\tRegistry: reg,\n\t\t\t\t\t\t}\n\t\t\t\t\t\twant := errNoDecoder{Type: reflect.TypeOf(tc.val)}\n\t\t\t\t\t\tgot := (&emptyInterfaceCodec{}).DecodeValue(dc, llvr, val)\n\t\t\t\t\t\tif !assert.CompareErrors(got, want) {\n\t\t\t\t\t\t\tt.Errorf(\"Errors are not equal. got %v; want %v\", got, want)\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\n\t\t\t\t\tt.Run(\"DecodeValue failure\", func(t *testing.T) {\n\t\t\t\t\t\tif tc.bsontype == TypeNull {\n\t\t\t\t\t\t\tt.Skip()\n\t\t\t\t\t\t}\n\t\t\t\t\t\twant := errors.New(\"DecodeValue failure error\")\n\t\t\t\t\t\tllc := &llCodec{t: t, err: want}\n\t\t\t\t\t\treg := newTestRegistry()\n\t\t\t\t\t\treg.RegisterTypeDecoder(reflect.TypeOf(tc.val), llc)\n\t\t\t\t\t\treg.RegisterTypeMapEntry(tc.bsontype, reflect.TypeOf(tc.val))\n\t\t\t\t\t\tdc := DecodeContext{\n\t\t\t\t\t\t\tRegistry: reg,\n\t\t\t\t\t\t}\n\t\t\t\t\t\tgot := (&emptyInterfaceCodec{}).DecodeValue(dc, llvr, reflect.New(tEmpty).Elem())\n\t\t\t\t\t\tif !assert.CompareErrors(got, want) {\n\t\t\t\t\t\t\tt.Errorf(\"Errors are not equal. got %v; want %v\", got, want)\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\n\t\t\t\t\tt.Run(\"Success\", func(t *testing.T) {\n\t\t\t\t\t\twant := tc.val\n\t\t\t\t\t\tllc := &llCodec{t: t, decodeval: tc.val}\n\t\t\t\t\t\treg := newTestRegistry()\n\t\t\t\t\t\treg.RegisterTypeDecoder(reflect.TypeOf(tc.val), llc)\n\t\t\t\t\t\treg.RegisterTypeMapEntry(tc.bsontype, reflect.TypeOf(tc.val))\n\t\t\t\t\t\tdc := DecodeContext{\n\t\t\t\t\t\t\tRegistry: reg,\n\t\t\t\t\t\t}\n\t\t\t\t\t\tgot := reflect.New(tEmpty).Elem()\n\t\t\t\t\t\terr := (&emptyInterfaceCodec{}).DecodeValue(dc, llvr, got)\n\t\t\t\t\t\tnoerr(t, err)\n\t\t\t\t\t\tif !cmp.Equal(got.Interface(), want, cmp.Comparer(compareDecimal128)) {\n\t\t\t\t\t\t\tt.Errorf(\"Did not receive expected value. got %v; want %v\", got.Interface(), want)\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\n\t\tt.Run(\"non-any\", func(t *testing.T) {\n\t\t\tval := uint64(1234567890)\n\t\t\twant := ValueDecoderError{Name: \"EmptyInterfaceDecodeValue\", Types: []reflect.Type{tEmpty}, Received: reflect.ValueOf(val)}\n\t\t\tgot := (&emptyInterfaceCodec{}).DecodeValue(DecodeContext{}, nil, reflect.ValueOf(val))\n\t\t\tif !assert.CompareErrors(got, want) {\n\t\t\t\tt.Errorf(\"Errors are not equal. got %v; want %v\", got, want)\n\t\t\t}\n\t\t})\n\n\t\tt.Run(\"nil *any\", func(t *testing.T) {\n\t\t\tvar val any\n\t\t\twant := ValueDecoderError{Name: \"EmptyInterfaceDecodeValue\", Types: []reflect.Type{tEmpty}, Received: reflect.ValueOf(val)}\n\t\t\tgot := (&emptyInterfaceCodec{}).DecodeValue(DecodeContext{}, nil, reflect.ValueOf(val))\n\t\t\tif !assert.CompareErrors(got, want) {\n\t\t\t\tt.Errorf(\"Errors are not equal. got %v; want %v\", got, want)\n\t\t\t}\n\t\t})\n\n\t\tt.Run(\"no type registered\", func(t *testing.T) {\n\t\t\tllvr := &valueReaderWriter{BSONType: TypeDouble}\n\t\t\twant := errNoTypeMapEntry{Type: TypeDouble}\n\t\t\tval := reflect.New(tEmpty).Elem()\n\t\t\tgot := (&emptyInterfaceCodec{}).DecodeValue(DecodeContext{Registry: newTestRegistry()}, llvr, val)\n\t\t\tif !assert.CompareErrors(got, want) {\n\t\t\t\tt.Errorf(\"Errors are not equal. got %v; want %v\", got, want)\n\t\t\t}\n\t\t})\n\t\tt.Run(\"top level document\", func(t *testing.T) {\n\t\t\tdata := bsoncore.BuildDocument(nil, bsoncore.AppendDoubleElement(nil, \"pi\", 3.14159))\n\t\t\tvr := NewDocumentReader(bytes.NewReader(data))\n\t\t\twant := D{{\"pi\", 3.14159}}\n\t\t\tvar got any\n\t\t\tval := reflect.ValueOf(&got).Elem()\n\t\t\terr := (&emptyInterfaceCodec{}).DecodeValue(DecodeContext{Registry: buildDefaultRegistry()}, vr, val)\n\t\t\tnoerr(t, err)\n\t\t\tif !cmp.Equal(got, want) {\n\t\t\t\tt.Errorf(\"Did not get correct result. got %v; want %v\", got, want)\n\t\t\t}\n\t\t})\n\t\tt.Run(\"custom type map entry\", func(t *testing.T) {\n\t\t\t// registering a custom type map entry for both Type(0) anad TypeEmbeddedDocument should cause\n\t\t\t// the top-level to decode to registered type when unmarshalling to any\n\n\t\t\ttopLevelReg := &Registry{\n\t\t\t\ttypeEncoders: new(typeEncoderCache),\n\t\t\t\ttypeDecoders: new(typeDecoderCache),\n\t\t\t\tkindEncoders: new(kindEncoderCache),\n\t\t\t\tkindDecoders: new(kindDecoderCache),\n\t\t\t}\n\t\t\tregisterDefaultEncoders(topLevelReg)\n\t\t\tregisterDefaultDecoders(topLevelReg)\n\t\t\ttopLevelReg.RegisterTypeMapEntry(Type(0), reflect.TypeOf(M{}))\n\n\t\t\tembeddedReg := &Registry{\n\t\t\t\ttypeEncoders: new(typeEncoderCache),\n\t\t\t\ttypeDecoders: new(typeDecoderCache),\n\t\t\t\tkindEncoders: new(kindEncoderCache),\n\t\t\t\tkindDecoders: new(kindDecoderCache),\n\t\t\t}\n\t\t\tregisterDefaultEncoders(embeddedReg)\n\t\t\tregisterDefaultDecoders(embeddedReg)\n\t\t\tembeddedReg.RegisterTypeMapEntry(Type(0), reflect.TypeOf(M{}))\n\n\t\t\t// create doc {\"nested\": {\"foo\": 1}}\n\t\t\tinnerDoc := bsoncore.BuildDocument(\n\t\t\t\tnil,\n\t\t\t\tbsoncore.AppendInt32Element(nil, \"foo\", 1),\n\t\t\t)\n\t\t\tdoc := bsoncore.BuildDocument(\n\t\t\t\tnil,\n\t\t\t\tbsoncore.AppendDocumentElement(nil, \"nested\", innerDoc),\n\t\t\t)\n\t\t\twant := M{\n\t\t\t\t\"nested\": D{{\"foo\", int32(1)}},\n\t\t\t}\n\n\t\t\ttestCases := []struct {\n\t\t\t\tname     string\n\t\t\t\tregistry *Registry\n\t\t\t}{\n\t\t\t\t{\"top level\", topLevelReg},\n\t\t\t\t{\"embedded\", embeddedReg},\n\t\t\t}\n\t\t\tfor _, tc := range testCases {\n\t\t\t\tvar got any\n\t\t\t\tvr := NewDocumentReader(bytes.NewReader(doc))\n\t\t\t\tval := reflect.ValueOf(&got).Elem()\n\n\t\t\t\terr := (&emptyInterfaceCodec{}).DecodeValue(DecodeContext{Registry: tc.registry}, vr, val)\n\t\t\t\tnoerr(t, err)\n\t\t\t\tif !cmp.Equal(got, want) {\n\t\t\t\t\tt.Fatalf(\"got %v, want %v\", got, want)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t\tt.Run(\"custom type map entry is used if there is no type information\", func(t *testing.T) {\n\t\t\t// If a type map entry is registered for TypeEmbeddedDocument, the decoder should use it when\n\t\t\t// type information is not available.\n\n\t\t\treg := &Registry{\n\t\t\t\ttypeEncoders: new(typeEncoderCache),\n\t\t\t\ttypeDecoders: new(typeDecoderCache),\n\t\t\t\tkindEncoders: new(kindEncoderCache),\n\t\t\t\tkindDecoders: new(kindDecoderCache),\n\t\t\t}\n\t\t\tregisterDefaultEncoders(reg)\n\t\t\tregisterDefaultDecoders(reg)\n\t\t\treg.RegisterTypeMapEntry(TypeEmbeddedDocument, reflect.TypeOf(M{}))\n\n\t\t\t// build document {\"nested\": {\"foo\": 10}}\n\t\t\tinner := bsoncore.BuildDocument(\n\t\t\t\tnil,\n\t\t\t\tbsoncore.AppendInt32Element(nil, \"foo\", 10),\n\t\t\t)\n\t\t\tdoc := bsoncore.BuildDocument(\n\t\t\t\tnil,\n\t\t\t\tbsoncore.AppendDocumentElement(nil, \"nested\", inner),\n\t\t\t)\n\t\t\twant := D{\n\t\t\t\t{\"nested\", M{\n\t\t\t\t\t\"foo\": int32(10),\n\t\t\t\t}},\n\t\t\t}\n\n\t\t\tvar got D\n\t\t\tvr := NewDocumentReader(bytes.NewReader(doc))\n\t\t\tval := reflect.ValueOf(&got).Elem()\n\t\t\terr := (&sliceCodec{}).DecodeValue(DecodeContext{Registry: reg}, vr, val)\n\t\t\tnoerr(t, err)\n\t\t\tif !cmp.Equal(got, want) {\n\t\t\t\tt.Fatalf(\"got %v, want %v\", got, want)\n\t\t\t}\n\t\t})\n\t})\n\n\tt.Run(\"decode errors contain key information\", func(t *testing.T) {\n\t\tdecodeValueError := errors.New(\"decode value error\")\n\t\temptyInterfaceErrorDecode := func(DecodeContext, ValueReader, reflect.Value) error {\n\t\t\treturn decodeValueError\n\t\t}\n\t\temptyInterfaceErrorRegistry := newTestRegistry()\n\t\temptyInterfaceErrorRegistry.RegisterTypeDecoder(tEmpty, ValueDecoderFunc(emptyInterfaceErrorDecode))\n\n\t\t// Set up a document {foo: 10} and an error that would happen if the value were decoded into any\n\t\t// using the registry defined above.\n\t\tdocBytes := bsoncore.BuildDocumentFromElements(\n\t\t\tnil,\n\t\t\tbsoncore.AppendInt32Element(nil, \"foo\", 10),\n\t\t)\n\t\tdocEmptyInterfaceErr := &DecodeError{\n\t\t\tkeys:    []string{\"foo\"},\n\t\t\twrapped: decodeValueError,\n\t\t}\n\n\t\t// Set up struct definitions where Foo maps to any and string. When decoded using the registry defined\n\t\t// above, the any struct will get an error when calling DecodeValue and the string struct will get an\n\t\t// error when looking up a decoder.\n\t\ttype emptyInterfaceStruct struct {\n\t\t\tFoo any\n\t\t}\n\t\ttype stringStruct struct {\n\t\t\tFoo string\n\t\t}\n\t\temptyInterfaceStructErr := &DecodeError{\n\t\t\tkeys:    []string{\"foo\"},\n\t\t\twrapped: decodeValueError,\n\t\t}\n\t\tstringStructErr := &DecodeError{\n\t\t\tkeys:    []string{\"foo\"},\n\t\t\twrapped: errNoDecoder{reflect.TypeOf(\"\")},\n\t\t}\n\n\t\t// Test a deeply nested struct mixed with maps and slices.\n\t\t// Build document {\"first\": {\"second\": {\"randomKey\": {\"third\": [{}, {\"fourth\": \"value\"}]}}}}\n\t\ttype inner3 struct{ Fourth any }\n\t\ttype inner2 struct{ Third []inner3 }\n\t\ttype inner1 struct{ Second map[string]inner2 }\n\t\ttype outer struct{ First inner1 }\n\t\tinner3EmptyDoc := buildDocument(nil)\n\t\tinner3Doc := buildDocument(bsoncore.AppendStringElement(nil, \"fourth\", \"value\"))\n\t\tinner3Array := buildArray(\n\t\t\t// buildArray takes []byte so we first append() all of the values into a single []byte\n\t\t\tappend(\n\t\t\t\tbsoncore.AppendDocumentElement(nil, \"0\", inner3EmptyDoc),\n\t\t\t\tbsoncore.AppendDocumentElement(nil, \"1\", inner3Doc)...,\n\t\t\t),\n\t\t)\n\t\tinner2Doc := buildDocument(bsoncore.AppendArrayElement(nil, \"third\", inner3Array))\n\t\tinner2Map := buildDocument(bsoncore.AppendDocumentElement(nil, \"randomKey\", inner2Doc))\n\t\tinner1Doc := buildDocument(bsoncore.AppendDocumentElement(nil, \"second\", inner2Map))\n\t\touterDoc := buildDocument(bsoncore.AppendDocumentElement(nil, \"first\", inner1Doc))\n\n\t\t// Use a registry that has all default decoders with the custom any decoder that always errors.\n\t\tnestedRegistry := &Registry{\n\t\t\ttypeEncoders: new(typeEncoderCache),\n\t\t\ttypeDecoders: new(typeDecoderCache),\n\t\t\tkindEncoders: new(kindEncoderCache),\n\t\t\tkindDecoders: new(kindDecoderCache),\n\t\t}\n\t\tregisterDefaultDecoders(nestedRegistry)\n\t\tnestedRegistry.RegisterTypeDecoder(tEmpty, ValueDecoderFunc(emptyInterfaceErrorDecode))\n\t\tnestedErr := &DecodeError{\n\t\t\tkeys:    []string{\"fourth\", \"1\", \"third\", \"randomKey\", \"second\", \"first\"},\n\t\t\twrapped: decodeValueError,\n\t\t}\n\n\t\ttestCases := []struct {\n\t\t\tname     string\n\t\t\tval      any\n\t\t\tvr       ValueReader\n\t\t\tregistry *Registry // buildDefaultRegistry will be used if this is nil\n\t\t\tdecoder  ValueDecoder\n\t\t\terr      error\n\t\t}{\n\t\t\t{\n\t\t\t\t// DecodeValue error when decoding into a D.\n\t\t\t\t\"D slice\",\n\t\t\t\tD{},\n\t\t\t\tNewDocumentReader(bytes.NewReader(docBytes)),\n\t\t\t\temptyInterfaceErrorRegistry,\n\t\t\t\t&sliceCodec{},\n\t\t\t\tdocEmptyInterfaceErr,\n\t\t\t},\n\t\t\t{\n\t\t\t\t// DecodeValue error when decoding into a []string.\n\t\t\t\t\"string slice\",\n\t\t\t\t[]string{},\n\t\t\t\t&valueReaderWriter{BSONType: TypeArray},\n\t\t\t\tnil,\n\t\t\t\t&sliceCodec{},\n\t\t\t\t&DecodeError{\n\t\t\t\t\tkeys:    []string{\"0\"},\n\t\t\t\t\twrapped: errors.New(\"cannot decode array into a string type\"),\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\t// DecodeValue error when decoding into a E array. This should have the same behavior as\n\t\t\t\t// the \"D slice\" test above because both the defaultSliceCodec and ArrayDecodeValue use\n\t\t\t\t// the decodeD helper function.\n\t\t\t\t\"D array\",\n\t\t\t\t[1]E{},\n\t\t\t\tNewDocumentReader(bytes.NewReader(docBytes)),\n\t\t\t\temptyInterfaceErrorRegistry,\n\t\t\t\tValueDecoderFunc(arrayDecodeValue),\n\t\t\t\tdocEmptyInterfaceErr,\n\t\t\t},\n\t\t\t{\n\t\t\t\t// DecodeValue error when decoding into a string array. This should have the same behavior as\n\t\t\t\t// the \"D slice\" test above because both the defaultSliceCodec and ArrayDecodeValue use\n\t\t\t\t// the decodeDefault helper function.\n\t\t\t\t\"string array\",\n\t\t\t\t[1]string{},\n\t\t\t\t&valueReaderWriter{BSONType: TypeArray},\n\t\t\t\tnil,\n\t\t\t\tValueDecoderFunc(arrayDecodeValue),\n\t\t\t\t&DecodeError{\n\t\t\t\t\tkeys:    []string{\"0\"},\n\t\t\t\t\twrapped: errors.New(\"cannot decode array into a string type\"),\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\t// DecodeValue error when decoding into a map.\n\t\t\t\t\"map\",\n\t\t\t\tmap[string]any{},\n\t\t\t\tNewDocumentReader(bytes.NewReader(docBytes)),\n\t\t\t\temptyInterfaceErrorRegistry,\n\t\t\t\t&mapCodec{},\n\t\t\t\tdocEmptyInterfaceErr,\n\t\t\t},\n\t\t\t{\n\t\t\t\t// DecodeValue error when decoding into a struct.\n\t\t\t\t\"struct - DecodeValue error\",\n\t\t\t\temptyInterfaceStruct{},\n\t\t\t\tNewDocumentReader(bytes.NewReader(docBytes)),\n\t\t\t\temptyInterfaceErrorRegistry,\n\t\t\t\tnewStructCodec(nil),\n\t\t\t\temptyInterfaceStructErr,\n\t\t\t},\n\t\t\t{\n\t\t\t\t// ErrNoDecoder when decoding into a struct.\n\t\t\t\t// This test uses NewRegistryBuilder().Build rather than buildDefaultRegistry to ensure that there is\n\t\t\t\t// no decoder for strings.\n\t\t\t\t\"struct - no decoder found\",\n\t\t\t\tstringStruct{},\n\t\t\t\tNewDocumentReader(bytes.NewReader(docBytes)),\n\t\t\t\tnewTestRegistry(),\n\t\t\t\tnewStructCodec(nil),\n\t\t\t\tstringStructErr,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"deeply nested struct\",\n\t\t\t\touter{},\n\t\t\t\tNewDocumentReader(bytes.NewReader(outerDoc)),\n\t\t\t\tnestedRegistry,\n\t\t\t\tnewStructCodec(nil),\n\t\t\t\tnestedErr,\n\t\t\t},\n\t\t}\n\t\tfor _, tc := range testCases {\n\t\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\t\tdc := DecodeContext{Registry: tc.registry}\n\t\t\t\tif dc.Registry == nil {\n\t\t\t\t\tdc.Registry = buildDefaultRegistry()\n\t\t\t\t}\n\n\t\t\t\tvar val reflect.Value\n\t\t\t\tif rtype := reflect.TypeOf(tc.val); rtype != nil {\n\t\t\t\t\tval = reflect.New(rtype).Elem()\n\t\t\t\t}\n\t\t\t\terr := tc.decoder.DecodeValue(dc, tc.vr, val)\n\t\t\t\tassert.Equal(t, tc.err, err, \"expected error %v, got %v\", tc.err, err)\n\t\t\t})\n\t\t}\n\n\t\tt.Run(\"keys are correctly reversed\", func(t *testing.T) {\n\t\t\tinnerBytes := bsoncore.BuildDocumentFromElements(nil, bsoncore.AppendInt32Element(nil, \"bar\", 10))\n\t\t\touterBytes := bsoncore.BuildDocumentFromElements(nil, bsoncore.AppendDocumentElement(nil, \"foo\", innerBytes))\n\n\t\t\ttype inner struct{ Bar string }\n\t\t\ttype outer struct{ Foo inner }\n\n\t\t\tdc := DecodeContext{Registry: buildDefaultRegistry()}\n\t\t\tvr := NewDocumentReader(bytes.NewReader(outerBytes))\n\t\t\tval := reflect.New(reflect.TypeOf(outer{})).Elem()\n\t\t\terr := newStructCodec(nil).DecodeValue(dc, vr, val)\n\n\t\t\tvar decodeErr *DecodeError\n\t\t\tassert.True(t, errors.As(err, &decodeErr), \"expected DecodeError, got %v of type %T\", err, err)\n\t\t\texpectedKeys := []string{\"foo\", \"bar\"}\n\t\t\tassert.Equal(t, expectedKeys, decodeErr.Keys(), \"expected keys slice %v, got %v\", expectedKeys,\n\t\t\t\tdecodeErr.Keys())\n\t\t\tkeyPath := strings.Join(expectedKeys, \".\")\n\t\t\tassert.True(t, strings.Contains(decodeErr.Error(), keyPath),\n\t\t\t\t\"expected error %v to contain key pattern %s\", decodeErr, keyPath)\n\t\t})\n\t})\n\n\tt.Run(\"values are converted\", func(t *testing.T) {\n\t\t// When decoding into a D or M, values must be converted if they are not being decoded to the default type.\n\n\t\tt.Run(\"D\", func(t *testing.T) {\n\t\t\ttrueValue := bsoncore.Value{\n\t\t\t\tType: bsoncore.TypeBoolean,\n\t\t\t\tData: bsoncore.AppendBoolean(nil, true),\n\t\t\t}\n\t\t\tdocBytes := bsoncore.BuildDocumentFromElements(nil,\n\t\t\t\tbsoncore.AppendBooleanElement(nil, \"bool\", true),\n\t\t\t\tbsoncore.BuildArrayElement(nil, \"boolArray\", trueValue),\n\t\t\t)\n\n\t\t\treg := &Registry{\n\t\t\t\ttypeEncoders: new(typeEncoderCache),\n\t\t\t\ttypeDecoders: new(typeDecoderCache),\n\t\t\t\tkindEncoders: new(kindEncoderCache),\n\t\t\t\tkindDecoders: new(kindDecoderCache),\n\t\t\t}\n\t\t\tregisterDefaultDecoders(reg)\n\t\t\treg.RegisterTypeMapEntry(TypeBoolean, reflect.TypeOf(mybool(true)))\n\n\t\t\tdc := DecodeContext{Registry: reg}\n\t\t\tvr := NewDocumentReader(bytes.NewReader(docBytes))\n\t\t\tval := reflect.New(tD).Elem()\n\t\t\terr := dDecodeValue(dc, vr, val)\n\t\t\tassert.Nil(t, err, \"DDecodeValue error: %v\", err)\n\n\t\t\twant := D{\n\t\t\t\t{\"bool\", mybool(true)},\n\t\t\t\t{\"boolArray\", A{mybool(true)}},\n\t\t\t}\n\t\t\tgot := val.Interface().(D)\n\t\t\tassert.Equal(t, want, got, \"want document %v, got %v\", want, got)\n\t\t})\n\t\tt.Run(\"M\", func(t *testing.T) {\n\t\t\tdocBytes := bsoncore.BuildDocumentFromElements(nil,\n\t\t\t\tbsoncore.AppendBooleanElement(nil, \"bool\", true),\n\t\t\t)\n\n\t\t\ttype myMap map[string]mybool\n\t\t\tdc := DecodeContext{Registry: buildDefaultRegistry()}\n\t\t\tvr := NewDocumentReader(bytes.NewReader(docBytes))\n\t\t\tval := reflect.New(reflect.TypeOf(myMap{})).Elem()\n\t\t\terr := (&mapCodec{}).DecodeValue(dc, vr, val)\n\t\t\tassert.Nil(t, err, \"DecodeValue error: %v\", err)\n\n\t\t\twant := myMap{\n\t\t\t\t\"bool\": mybool(true),\n\t\t\t}\n\t\t\tgot := val.Interface().(myMap)\n\t\t\tassert.Equal(t, want, got, \"expected map %v, got %v\", want, got)\n\t\t})\n\t})\n}\n\n// buildDocumentArray inserts vals inside of an array inside of a document.\nfunc buildDocumentArray(fn func([]byte) []byte) []byte {\n\taix, doc := bsoncore.AppendArrayElementStart(nil, \"Z\")\n\tdoc = fn(doc)\n\tdoc, _ = bsoncore.AppendArrayEnd(doc, aix)\n\treturn buildDocument(doc)\n}\n\nfunc buildArray(vals []byte) []byte {\n\taix, doc := bsoncore.AppendArrayStart(nil)\n\tdoc = append(doc, vals...)\n\tdoc, _ = bsoncore.AppendArrayEnd(doc, aix)\n\treturn doc\n}\n\nfunc appendArrayElement(dst []byte, key string, vals []byte) []byte {\n\taix, doc := bsoncore.AppendArrayElementStart(dst, key)\n\tdoc = append(doc, vals...)\n\tdoc, _ = bsoncore.AppendArrayEnd(doc, aix)\n\treturn doc\n}\n\n// buildDocument inserts elems inside of a document.\nfunc buildDocument(elems []byte) []byte {\n\tidx, doc := bsoncore.AppendDocumentStart(nil)\n\tdoc = append(doc, elems...)\n\tdoc, _ = bsoncore.AppendDocumentEnd(doc, idx)\n\treturn doc\n}\n\nfunc buildDefaultRegistry() *Registry {\n\treg := &Registry{\n\t\ttypeEncoders: new(typeEncoderCache),\n\t\ttypeDecoders: new(typeDecoderCache),\n\t\tkindEncoders: new(kindEncoderCache),\n\t\tkindDecoders: new(kindDecoderCache),\n\t}\n\tregisterDefaultEncoders(reg)\n\tregisterDefaultDecoders(reg)\n\treturn reg\n}\n"
  },
  {
    "path": "bson/default_value_encoders.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bson\n\nimport (\n\t\"encoding/json\"\n\t\"errors\"\n\t\"math\"\n\t\"net/url\"\n\t\"reflect\"\n\t\"sync\"\n\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n)\n\nvar bvwPool = sync.Pool{\n\tNew: func() any {\n\t\treturn new(valueWriter)\n\t},\n}\n\nvar errInvalidValue = errors.New(\"cannot encode invalid element\")\n\nvar sliceWriterPool = sync.Pool{\n\tNew: func() any {\n\t\tsw := make(sliceWriter, 0)\n\t\treturn &sw\n\t},\n}\n\nfunc encodeElement(ec EncodeContext, dw DocumentWriter, e E) error {\n\tvw, err := dw.WriteDocumentElement(e.Key)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif e.Value == nil {\n\t\treturn vw.WriteNull()\n\t}\n\tencoder, err := ec.LookupEncoder(reflect.TypeOf(e.Value))\n\tif err != nil {\n\t\treturn err\n\t}\n\n\terr = encoder.EncodeValue(ec, vw, reflect.ValueOf(e.Value))\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\n// registerDefaultEncoders will register the encoder methods attached to DefaultValueEncoders with\n// the provided RegistryBuilder.\nfunc registerDefaultEncoders(reg *Registry) {\n\tmapEncoder := &mapCodec{}\n\tuintCodec := &uintCodec{}\n\n\treg.RegisterTypeEncoder(tByteSlice, &byteSliceCodec{})\n\treg.RegisterTypeEncoder(tTime, &timeCodec{})\n\treg.RegisterTypeEncoder(tEmpty, &emptyInterfaceCodec{})\n\treg.RegisterTypeEncoder(tCoreArray, &arrayCodec{})\n\treg.RegisterTypeEncoder(tOID, ValueEncoderFunc(objectIDEncodeValue))\n\treg.RegisterTypeEncoder(tDecimal, ValueEncoderFunc(decimal128EncodeValue))\n\treg.RegisterTypeEncoder(tJSONNumber, ValueEncoderFunc(jsonNumberEncodeValue))\n\treg.RegisterTypeEncoder(tURL, ValueEncoderFunc(urlEncodeValue))\n\treg.RegisterTypeEncoder(tJavaScript, ValueEncoderFunc(javaScriptEncodeValue))\n\treg.RegisterTypeEncoder(tSymbol, ValueEncoderFunc(symbolEncodeValue))\n\treg.RegisterTypeEncoder(tBinary, ValueEncoderFunc(binaryEncodeValue))\n\treg.RegisterTypeEncoder(tVector, ValueEncoderFunc(vectorEncodeValue))\n\treg.RegisterTypeEncoder(tUndefined, ValueEncoderFunc(undefinedEncodeValue))\n\treg.RegisterTypeEncoder(tDateTime, ValueEncoderFunc(dateTimeEncodeValue))\n\treg.RegisterTypeEncoder(tNull, ValueEncoderFunc(nullEncodeValue))\n\treg.RegisterTypeEncoder(tRegex, ValueEncoderFunc(regexEncodeValue))\n\treg.RegisterTypeEncoder(tDBPointer, ValueEncoderFunc(dbPointerEncodeValue))\n\treg.RegisterTypeEncoder(tTimestamp, ValueEncoderFunc(timestampEncodeValue))\n\treg.RegisterTypeEncoder(tMinKey, ValueEncoderFunc(minKeyEncodeValue))\n\treg.RegisterTypeEncoder(tMaxKey, ValueEncoderFunc(maxKeyEncodeValue))\n\treg.RegisterTypeEncoder(tCoreDocument, ValueEncoderFunc(coreDocumentEncodeValue))\n\treg.RegisterTypeEncoder(tCodeWithScope, ValueEncoderFunc(codeWithScopeEncodeValue))\n\treg.RegisterKindEncoder(reflect.Bool, ValueEncoderFunc(booleanEncodeValue))\n\treg.RegisterKindEncoder(reflect.Int, ValueEncoderFunc(intEncodeValue))\n\treg.RegisterKindEncoder(reflect.Int8, ValueEncoderFunc(intEncodeValue))\n\treg.RegisterKindEncoder(reflect.Int16, ValueEncoderFunc(intEncodeValue))\n\treg.RegisterKindEncoder(reflect.Int32, ValueEncoderFunc(intEncodeValue))\n\treg.RegisterKindEncoder(reflect.Int64, ValueEncoderFunc(intEncodeValue))\n\treg.RegisterKindEncoder(reflect.Uint, uintCodec)\n\treg.RegisterKindEncoder(reflect.Uint8, uintCodec)\n\treg.RegisterKindEncoder(reflect.Uint16, uintCodec)\n\treg.RegisterKindEncoder(reflect.Uint32, uintCodec)\n\treg.RegisterKindEncoder(reflect.Uint64, uintCodec)\n\treg.RegisterKindEncoder(reflect.Float32, ValueEncoderFunc(floatEncodeValue))\n\treg.RegisterKindEncoder(reflect.Float64, ValueEncoderFunc(floatEncodeValue))\n\treg.RegisterKindEncoder(reflect.Array, ValueEncoderFunc(arrayEncodeValue))\n\treg.RegisterKindEncoder(reflect.Map, mapEncoder)\n\treg.RegisterKindEncoder(reflect.Slice, &sliceCodec{})\n\treg.RegisterKindEncoder(reflect.String, &stringCodec{})\n\treg.RegisterKindEncoder(reflect.Struct, newStructCodec(mapEncoder))\n\treg.RegisterKindEncoder(reflect.Ptr, &pointerCodec{})\n\treg.RegisterInterfaceEncoder(tValueMarshaler, ValueEncoderFunc(valueMarshalerEncodeValue))\n\treg.RegisterInterfaceEncoder(tMarshaler, ValueEncoderFunc(marshalerEncodeValue))\n}\n\n// booleanEncodeValue is the ValueEncoderFunc for bool types.\nfunc booleanEncodeValue(_ EncodeContext, vw ValueWriter, val reflect.Value) error {\n\tif !val.IsValid() || val.Kind() != reflect.Bool {\n\t\treturn ValueEncoderError{Name: \"BooleanEncodeValue\", Kinds: []reflect.Kind{reflect.Bool}, Received: val}\n\t}\n\treturn vw.WriteBoolean(val.Bool())\n}\n\nfunc fitsIn32Bits(i int64) bool {\n\treturn math.MinInt32 <= i && i <= math.MaxInt32\n}\n\n// intEncodeValue is the ValueEncoderFunc for int types.\nfunc intEncodeValue(ec EncodeContext, vw ValueWriter, val reflect.Value) error {\n\tswitch val.Kind() {\n\tcase reflect.Int8, reflect.Int16, reflect.Int32:\n\t\treturn vw.WriteInt32(int32(val.Int()))\n\tcase reflect.Int:\n\t\ti64 := val.Int()\n\t\tif fitsIn32Bits(i64) {\n\t\t\treturn vw.WriteInt32(int32(i64))\n\t\t}\n\t\treturn vw.WriteInt64(i64)\n\tcase reflect.Int64:\n\t\ti64 := val.Int()\n\t\tif ec.minSize && fitsIn32Bits(i64) {\n\t\t\treturn vw.WriteInt32(int32(i64))\n\t\t}\n\t\treturn vw.WriteInt64(i64)\n\t}\n\n\treturn ValueEncoderError{\n\t\tName:     \"IntEncodeValue\",\n\t\tKinds:    []reflect.Kind{reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int},\n\t\tReceived: val,\n\t}\n}\n\n// floatEncodeValue is the ValueEncoderFunc for float types.\nfunc floatEncodeValue(_ EncodeContext, vw ValueWriter, val reflect.Value) error {\n\tswitch val.Kind() {\n\tcase reflect.Float32, reflect.Float64:\n\t\treturn vw.WriteDouble(val.Float())\n\t}\n\n\treturn ValueEncoderError{Name: \"FloatEncodeValue\", Kinds: []reflect.Kind{reflect.Float32, reflect.Float64}, Received: val}\n}\n\n// objectIDEncodeValue is the ValueEncoderFunc for ObjectID.\nfunc objectIDEncodeValue(_ EncodeContext, vw ValueWriter, val reflect.Value) error {\n\tif !val.IsValid() || val.Type() != tOID {\n\t\treturn ValueEncoderError{Name: \"ObjectIDEncodeValue\", Types: []reflect.Type{tOID}, Received: val}\n\t}\n\treturn vw.WriteObjectID(val.Interface().(ObjectID))\n}\n\n// decimal128EncodeValue is the ValueEncoderFunc for Decimal128.\nfunc decimal128EncodeValue(_ EncodeContext, vw ValueWriter, val reflect.Value) error {\n\tif !val.IsValid() || val.Type() != tDecimal {\n\t\treturn ValueEncoderError{Name: \"Decimal128EncodeValue\", Types: []reflect.Type{tDecimal}, Received: val}\n\t}\n\treturn vw.WriteDecimal128(val.Interface().(Decimal128))\n}\n\n// jsonNumberEncodeValue is the ValueEncoderFunc for json.Number.\nfunc jsonNumberEncodeValue(ec EncodeContext, vw ValueWriter, val reflect.Value) error {\n\tif !val.IsValid() || val.Type() != tJSONNumber {\n\t\treturn ValueEncoderError{Name: \"JSONNumberEncodeValue\", Types: []reflect.Type{tJSONNumber}, Received: val}\n\t}\n\tjsnum := val.Interface().(json.Number)\n\n\t// Attempt int first, then float64\n\tif i64, err := jsnum.Int64(); err == nil {\n\t\treturn intEncodeValue(ec, vw, reflect.ValueOf(i64))\n\t}\n\n\tf64, err := jsnum.Float64()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn floatEncodeValue(ec, vw, reflect.ValueOf(f64))\n}\n\n// urlEncodeValue is the ValueEncoderFunc for url.URL.\nfunc urlEncodeValue(_ EncodeContext, vw ValueWriter, val reflect.Value) error {\n\tif !val.IsValid() || val.Type() != tURL {\n\t\treturn ValueEncoderError{Name: \"URLEncodeValue\", Types: []reflect.Type{tURL}, Received: val}\n\t}\n\tu := val.Interface().(url.URL)\n\treturn vw.WriteString(u.String())\n}\n\n// arrayEncodeValue is the ValueEncoderFunc for array types.\nfunc arrayEncodeValue(ec EncodeContext, vw ValueWriter, val reflect.Value) error {\n\tif !val.IsValid() || val.Kind() != reflect.Array {\n\t\treturn ValueEncoderError{Name: \"ArrayEncodeValue\", Kinds: []reflect.Kind{reflect.Array}, Received: val}\n\t}\n\n\t// If we have a []E we want to treat it as a document instead of as an array.\n\tif val.Type().Elem() == tE {\n\t\tdw, err := vw.WriteDocument()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tfor idx := 0; idx < val.Len(); idx++ {\n\t\t\te := val.Index(idx).Interface().(E)\n\t\t\terr = encodeElement(ec, dw, e)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\n\t\treturn dw.WriteDocumentEnd()\n\t}\n\n\t// If we have a []byte we want to treat it as a binary instead of as an array.\n\tif val.Type().Elem() == tByte {\n\t\tvar byteSlice []byte\n\t\tfor idx := 0; idx < val.Len(); idx++ {\n\t\t\tbyteSlice = append(byteSlice, val.Index(idx).Interface().(byte))\n\t\t}\n\t\treturn vw.WriteBinary(byteSlice)\n\t}\n\n\taw, err := vw.WriteArray()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\telemType := val.Type().Elem()\n\tencoder, err := ec.LookupEncoder(elemType)\n\tif err != nil && elemType.Kind() != reflect.Interface {\n\t\treturn err\n\t}\n\n\tfor idx := 0; idx < val.Len(); idx++ {\n\t\tcurrEncoder, currVal, lookupErr := lookupElementEncoder(ec, encoder, val.Index(idx))\n\t\tif lookupErr != nil && !errors.Is(lookupErr, errInvalidValue) {\n\t\t\treturn lookupErr\n\t\t}\n\n\t\tvw, err := aw.WriteArrayElement()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tif errors.Is(lookupErr, errInvalidValue) {\n\t\t\terr = vw.WriteNull()\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\n\t\terr = currEncoder.EncodeValue(ec, vw, currVal)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn aw.WriteArrayEnd()\n}\n\nfunc lookupElementEncoder(ec EncodeContext, origEncoder ValueEncoder, currVal reflect.Value) (ValueEncoder, reflect.Value, error) {\n\tif origEncoder != nil || (currVal.Kind() != reflect.Interface) {\n\t\treturn origEncoder, currVal, nil\n\t}\n\tcurrVal = currVal.Elem()\n\tif !currVal.IsValid() {\n\t\treturn nil, currVal, errInvalidValue\n\t}\n\tcurrEncoder, err := ec.LookupEncoder(currVal.Type())\n\n\treturn currEncoder, currVal, err\n}\n\n// valueMarshalerEncodeValue is the ValueEncoderFunc for ValueMarshaler implementations.\nfunc valueMarshalerEncodeValue(_ EncodeContext, vw ValueWriter, val reflect.Value) error {\n\t// Either val or a pointer to val must implement ValueMarshaler\n\tswitch {\n\tcase !val.IsValid():\n\t\treturn ValueEncoderError{Name: \"ValueMarshalerEncodeValue\", Types: []reflect.Type{tValueMarshaler}, Received: val}\n\tcase val.Type().Implements(tValueMarshaler):\n\t\t// If ValueMarshaler is implemented on a concrete type, make sure that val isn't a nil pointer\n\t\tif isImplementationNil(val, tValueMarshaler) {\n\t\t\treturn vw.WriteNull()\n\t\t}\n\tcase reflect.PtrTo(val.Type()).Implements(tValueMarshaler) && val.CanAddr():\n\t\tval = val.Addr()\n\tdefault:\n\t\treturn ValueEncoderError{Name: \"ValueMarshalerEncodeValue\", Types: []reflect.Type{tValueMarshaler}, Received: val}\n\t}\n\n\tm, ok := val.Interface().(ValueMarshaler)\n\tif !ok {\n\t\treturn vw.WriteNull()\n\t}\n\tt, data, err := m.MarshalBSONValue()\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn copyValueFromBytes(vw, Type(t), data)\n}\n\n// marshalerEncodeValue is the ValueEncoderFunc for Marshaler implementations.\nfunc marshalerEncodeValue(_ EncodeContext, vw ValueWriter, val reflect.Value) error {\n\t// Either val or a pointer to val must implement Marshaler\n\tswitch {\n\tcase !val.IsValid():\n\t\treturn ValueEncoderError{Name: \"MarshalerEncodeValue\", Types: []reflect.Type{tMarshaler}, Received: val}\n\tcase val.Type().Implements(tMarshaler):\n\t\t// If Marshaler is implemented on a concrete type, make sure that val isn't a nil pointer\n\t\tif isImplementationNil(val, tMarshaler) {\n\t\t\treturn vw.WriteNull()\n\t\t}\n\tcase reflect.PtrTo(val.Type()).Implements(tMarshaler) && val.CanAddr():\n\t\tval = val.Addr()\n\tdefault:\n\t\treturn ValueEncoderError{Name: \"MarshalerEncodeValue\", Types: []reflect.Type{tMarshaler}, Received: val}\n\t}\n\n\tm, ok := val.Interface().(Marshaler)\n\tif !ok {\n\t\treturn vw.WriteNull()\n\t}\n\tdata, err := m.MarshalBSON()\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn copyValueFromBytes(vw, TypeEmbeddedDocument, data)\n}\n\n// javaScriptEncodeValue is the ValueEncoderFunc for the JavaScript type.\nfunc javaScriptEncodeValue(_ EncodeContext, vw ValueWriter, val reflect.Value) error {\n\tif !val.IsValid() || val.Type() != tJavaScript {\n\t\treturn ValueEncoderError{Name: \"JavaScriptEncodeValue\", Types: []reflect.Type{tJavaScript}, Received: val}\n\t}\n\n\treturn vw.WriteJavascript(val.String())\n}\n\n// symbolEncodeValue is the ValueEncoderFunc for the Symbol type.\nfunc symbolEncodeValue(_ EncodeContext, vw ValueWriter, val reflect.Value) error {\n\tif !val.IsValid() || val.Type() != tSymbol {\n\t\treturn ValueEncoderError{Name: \"SymbolEncodeValue\", Types: []reflect.Type{tSymbol}, Received: val}\n\t}\n\n\treturn vw.WriteSymbol(val.String())\n}\n\n// binaryEncodeValue is the ValueEncoderFunc for Binary.\nfunc binaryEncodeValue(_ EncodeContext, vw ValueWriter, val reflect.Value) error {\n\tif !val.IsValid() || val.Type() != tBinary {\n\t\treturn ValueEncoderError{Name: \"BinaryEncodeValue\", Types: []reflect.Type{tBinary}, Received: val}\n\t}\n\tb := val.Interface().(Binary)\n\n\treturn vw.WriteBinaryWithSubtype(b.Data, b.Subtype)\n}\n\n// vectorEncodeValue is the ValueEncoderFunc for Vector.\nfunc vectorEncodeValue(_ EncodeContext, vw ValueWriter, val reflect.Value) error {\n\tt := val.Type()\n\tif !val.IsValid() || t != tVector {\n\t\treturn ValueEncoderError{\n\t\t\tName:     \"VectorEncodeValue\",\n\t\t\tTypes:    []reflect.Type{tVector},\n\t\t\tReceived: val,\n\t\t}\n\t}\n\tv := val.Interface().(Vector)\n\tb := v.Binary()\n\treturn vw.WriteBinaryWithSubtype(b.Data, b.Subtype)\n}\n\n// undefinedEncodeValue is the ValueEncoderFunc for Undefined.\nfunc undefinedEncodeValue(_ EncodeContext, vw ValueWriter, val reflect.Value) error {\n\tif !val.IsValid() || val.Type() != tUndefined {\n\t\treturn ValueEncoderError{Name: \"UndefinedEncodeValue\", Types: []reflect.Type{tUndefined}, Received: val}\n\t}\n\n\treturn vw.WriteUndefined()\n}\n\n// dateTimeEncodeValue is the ValueEncoderFunc for DateTime.\nfunc dateTimeEncodeValue(_ EncodeContext, vw ValueWriter, val reflect.Value) error {\n\tif !val.IsValid() || val.Type() != tDateTime {\n\t\treturn ValueEncoderError{Name: \"DateTimeEncodeValue\", Types: []reflect.Type{tDateTime}, Received: val}\n\t}\n\n\treturn vw.WriteDateTime(val.Int())\n}\n\n// nullEncodeValue is the ValueEncoderFunc for Null.\nfunc nullEncodeValue(_ EncodeContext, vw ValueWriter, val reflect.Value) error {\n\tif !val.IsValid() || val.Type() != tNull {\n\t\treturn ValueEncoderError{Name: \"NullEncodeValue\", Types: []reflect.Type{tNull}, Received: val}\n\t}\n\n\treturn vw.WriteNull()\n}\n\n// regexEncodeValue is the ValueEncoderFunc for Regex.\nfunc regexEncodeValue(_ EncodeContext, vw ValueWriter, val reflect.Value) error {\n\tif !val.IsValid() || val.Type() != tRegex {\n\t\treturn ValueEncoderError{Name: \"RegexEncodeValue\", Types: []reflect.Type{tRegex}, Received: val}\n\t}\n\n\tregex := val.Interface().(Regex)\n\n\treturn vw.WriteRegex(regex.Pattern, regex.Options)\n}\n\n// dbPointerEncodeValue is the ValueEncoderFunc for DBPointer.\nfunc dbPointerEncodeValue(_ EncodeContext, vw ValueWriter, val reflect.Value) error {\n\tif !val.IsValid() || val.Type() != tDBPointer {\n\t\treturn ValueEncoderError{Name: \"DBPointerEncodeValue\", Types: []reflect.Type{tDBPointer}, Received: val}\n\t}\n\n\tdbp := val.Interface().(DBPointer)\n\n\treturn vw.WriteDBPointer(dbp.DB, dbp.Pointer)\n}\n\n// timestampEncodeValue is the ValueEncoderFunc for Timestamp.\nfunc timestampEncodeValue(_ EncodeContext, vw ValueWriter, val reflect.Value) error {\n\tif !val.IsValid() || val.Type() != tTimestamp {\n\t\treturn ValueEncoderError{Name: \"TimestampEncodeValue\", Types: []reflect.Type{tTimestamp}, Received: val}\n\t}\n\n\tts := val.Interface().(Timestamp)\n\n\treturn vw.WriteTimestamp(ts.T, ts.I)\n}\n\n// minKeyEncodeValue is the ValueEncoderFunc for MinKey.\nfunc minKeyEncodeValue(_ EncodeContext, vw ValueWriter, val reflect.Value) error {\n\tif !val.IsValid() || val.Type() != tMinKey {\n\t\treturn ValueEncoderError{Name: \"MinKeyEncodeValue\", Types: []reflect.Type{tMinKey}, Received: val}\n\t}\n\n\treturn vw.WriteMinKey()\n}\n\n// maxKeyEncodeValue is the ValueEncoderFunc for MaxKey.\nfunc maxKeyEncodeValue(_ EncodeContext, vw ValueWriter, val reflect.Value) error {\n\tif !val.IsValid() || val.Type() != tMaxKey {\n\t\treturn ValueEncoderError{Name: \"MaxKeyEncodeValue\", Types: []reflect.Type{tMaxKey}, Received: val}\n\t}\n\n\treturn vw.WriteMaxKey()\n}\n\n// coreDocumentEncodeValue is the ValueEncoderFunc for bsoncore.Document.\nfunc coreDocumentEncodeValue(_ EncodeContext, vw ValueWriter, val reflect.Value) error {\n\tif !val.IsValid() || val.Type() != tCoreDocument {\n\t\treturn ValueEncoderError{Name: \"CoreDocumentEncodeValue\", Types: []reflect.Type{tCoreDocument}, Received: val}\n\t}\n\n\tcdoc := val.Interface().(bsoncore.Document)\n\n\treturn copyDocumentFromBytes(vw, cdoc)\n}\n\n// codeWithScopeEncodeValue is the ValueEncoderFunc for CodeWithScope.\nfunc codeWithScopeEncodeValue(ec EncodeContext, vw ValueWriter, val reflect.Value) error {\n\tif !val.IsValid() || val.Type() != tCodeWithScope {\n\t\treturn ValueEncoderError{Name: \"CodeWithScopeEncodeValue\", Types: []reflect.Type{tCodeWithScope}, Received: val}\n\t}\n\n\tcws := val.Interface().(CodeWithScope)\n\n\tdw, err := vw.WriteCodeWithScope(string(cws.Code))\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tsw := sliceWriterPool.Get().(*sliceWriter)\n\tdefer sliceWriterPool.Put(sw)\n\t*sw = (*sw)[:0]\n\n\tscopeVW := bvwPool.Get().(*valueWriter)\n\tscopeVW.reset(scopeVW.buf[:0])\n\tscopeVW.w = sw\n\tdefer bvwPool.Put(scopeVW)\n\n\tencoder, err := ec.LookupEncoder(reflect.TypeOf(cws.Scope))\n\tif err != nil {\n\t\treturn err\n\t}\n\n\terr = encoder.EncodeValue(ec, scopeVW, reflect.ValueOf(cws.Scope))\n\tif err != nil {\n\t\treturn err\n\t}\n\n\terr = copyBytesToDocumentWriter(dw, *sw)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn dw.WriteDocumentEnd()\n}\n\n// isImplementationNil returns if val is a nil pointer and inter is implemented on a concrete type\nfunc isImplementationNil(val reflect.Value, inter reflect.Type) bool {\n\tvt := val.Type()\n\tfor vt.Kind() == reflect.Ptr {\n\t\tvt = vt.Elem()\n\t}\n\treturn vt.Implements(inter) && val.Kind() == reflect.Ptr && val.IsNil()\n}\n"
  },
  {
    "path": "bson/default_value_encoders_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bson\n\nimport (\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"math\"\n\t\"net/url\"\n\t\"reflect\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/google/go-cmp/cmp\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n)\n\ntype myInterface interface {\n\tFoo() int\n}\n\ntype myStruct struct {\n\tVal int\n}\n\nfunc (ms myStruct) Foo() int {\n\treturn ms.Val\n}\n\nfunc TestDefaultValueEncoders(t *testing.T) {\n\twrong := func(string, string) string { return \"wrong\" }\n\n\ttype mybool bool\n\ttype myint8 int8\n\ttype myint16 int16\n\ttype myint32 int32\n\ttype myint64 int64\n\ttype myint int\n\ttype myuint8 uint8\n\ttype myuint16 uint16\n\ttype myuint32 uint32\n\ttype myuint64 uint64\n\ttype myuint uint\n\ttype myfloat32 float32\n\ttype myfloat64 float64\n\n\tnow := time.Now().Truncate(time.Millisecond)\n\tpjsnum := new(json.Number)\n\t*pjsnum = json.Number(\"3.14159\")\n\td128 := NewDecimal128(12345, 67890)\n\tvar nilValueMarshaler *testValueMarshaler\n\tvar nilMarshaler *testMarshaler\n\n\tvmStruct := struct{ V testValueMarshalPtr }{testValueMarshalPtr{t: TypeString, buf: []byte{0x04, 0x00, 0x00, 0x00, 'f', 'o', 'o', 0x00}}}\n\tmStruct := struct{ V testMarshalPtr }{testMarshalPtr{buf: bsoncore.BuildDocument(nil, bsoncore.AppendDoubleElement(nil, \"pi\", 3.14159))}}\n\n\ttype subtest struct {\n\t\tname   string\n\t\tval    any\n\t\tectx   *EncodeContext\n\t\tllvrw  *valueReaderWriter\n\t\tinvoke invoked\n\t\terr    error\n\t}\n\n\ttestCases := []struct {\n\t\tname     string\n\t\tve       ValueEncoder\n\t\tsubtests []subtest\n\t}{\n\t\t{\n\t\t\t\"BooleanEncodeValue\",\n\t\t\tValueEncoderFunc(booleanEncodeValue),\n\t\t\t[]subtest{\n\t\t\t\t{\n\t\t\t\t\t\"wrong type\",\n\t\t\t\t\twrong,\n\t\t\t\t\tnil,\n\t\t\t\t\tnil,\n\t\t\t\t\tnothing,\n\t\t\t\t\tValueEncoderError{Name: \"BooleanEncodeValue\", Kinds: []reflect.Kind{reflect.Bool}, Received: reflect.ValueOf(wrong)},\n\t\t\t\t},\n\t\t\t\t{\"fast path\", bool(true), nil, nil, writeBoolean, nil},\n\t\t\t\t{\"reflection path\", mybool(true), nil, nil, writeBoolean, nil},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"IntEncodeValue\",\n\t\t\tValueEncoderFunc(intEncodeValue),\n\t\t\t[]subtest{\n\t\t\t\t{\n\t\t\t\t\t\"wrong type\",\n\t\t\t\t\twrong,\n\t\t\t\t\tnil,\n\t\t\t\t\tnil,\n\t\t\t\t\tnothing,\n\t\t\t\t\tValueEncoderError{\n\t\t\t\t\t\tName:     \"IntEncodeValue\",\n\t\t\t\t\t\tKinds:    []reflect.Kind{reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int},\n\t\t\t\t\t\tReceived: reflect.ValueOf(wrong),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\"int8/fast path\", int8(127), nil, nil, writeInt32, nil},\n\t\t\t\t{\"int16/fast path\", int16(32767), nil, nil, writeInt32, nil},\n\t\t\t\t{\"int32/fast path\", int32(2147483647), nil, nil, writeInt32, nil},\n\t\t\t\t{\"int64/fast path\", int64(1234567890987), nil, nil, writeInt64, nil},\n\t\t\t\t{\"int64/fast path - minsize\", int64(math.MaxInt32), &EncodeContext{minSize: true}, nil, writeInt32, nil},\n\t\t\t\t{\"int64/fast path - minsize too large\", int64(math.MaxInt32 + 1), &EncodeContext{minSize: true}, nil, writeInt64, nil},\n\t\t\t\t{\"int64/fast path - minsize too small\", int64(math.MinInt32 - 1), &EncodeContext{minSize: true}, nil, writeInt64, nil},\n\t\t\t\t{\"int/fast path - positive int32\", int(math.MaxInt32 - 1), nil, nil, writeInt32, nil},\n\t\t\t\t{\"int/fast path - negative int32\", int(math.MinInt32 + 1), nil, nil, writeInt32, nil},\n\t\t\t\t{\"int/fast path - MaxInt32\", int(math.MaxInt32), nil, nil, writeInt32, nil},\n\t\t\t\t{\"int/fast path - MinInt32\", int(math.MinInt32), nil, nil, writeInt32, nil},\n\t\t\t\t{\"int8/reflection path\", myint8(127), nil, nil, writeInt32, nil},\n\t\t\t\t{\"int16/reflection path\", myint16(32767), nil, nil, writeInt32, nil},\n\t\t\t\t{\"int32/reflection path\", myint32(2147483647), nil, nil, writeInt32, nil},\n\t\t\t\t{\"int64/reflection path\", myint64(1234567890987), nil, nil, writeInt64, nil},\n\t\t\t\t{\"int64/reflection path - minsize\", myint64(math.MaxInt32), &EncodeContext{minSize: true}, nil, writeInt32, nil},\n\t\t\t\t{\"int64/reflection path - minsize too large\", myint64(math.MaxInt32 + 1), &EncodeContext{minSize: true}, nil, writeInt64, nil},\n\t\t\t\t{\"int64/reflection path - minsize too small\", myint64(math.MinInt32 - 1), &EncodeContext{minSize: true}, nil, writeInt64, nil},\n\t\t\t\t{\"int/reflection path - positive int32\", myint(math.MaxInt32 - 1), nil, nil, writeInt32, nil},\n\t\t\t\t{\"int/reflection path - negative int32\", myint(math.MinInt32 + 1), nil, nil, writeInt32, nil},\n\t\t\t\t{\"int/reflection path - MaxInt32\", myint(math.MaxInt32), nil, nil, writeInt32, nil},\n\t\t\t\t{\"int/reflection path - MinInt32\", myint(math.MinInt32), nil, nil, writeInt32, nil},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"UintEncodeValue\",\n\t\t\t&uintCodec{},\n\t\t\t[]subtest{\n\t\t\t\t{\n\t\t\t\t\t\"wrong type\",\n\t\t\t\t\twrong,\n\t\t\t\t\tnil,\n\t\t\t\t\tnil,\n\t\t\t\t\tnothing,\n\t\t\t\t\tValueEncoderError{\n\t\t\t\t\t\tName:     \"UintEncodeValue\",\n\t\t\t\t\t\tKinds:    []reflect.Kind{reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint},\n\t\t\t\t\t\tReceived: reflect.ValueOf(wrong),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\"uint8/fast path\", uint8(127), nil, nil, writeInt32, nil},\n\t\t\t\t{\"uint16/fast path\", uint16(32767), nil, nil, writeInt32, nil},\n\t\t\t\t{\"uint32/fast path\", uint32(2147483647), nil, nil, writeInt64, nil},\n\t\t\t\t{\"uint64/fast path\", uint64(1234567890987), nil, nil, writeInt64, nil},\n\t\t\t\t{\"uint/fast path\", uint(1234567), nil, nil, writeInt64, nil},\n\t\t\t\t{\"uint32/fast path - minsize\", uint32(2147483647), &EncodeContext{minSize: true}, nil, writeInt32, nil},\n\t\t\t\t{\"uint64/fast path - minsize\", uint64(2147483647), &EncodeContext{minSize: true}, nil, writeInt32, nil},\n\t\t\t\t{\"uint/fast path - minsize\", uint(2147483647), &EncodeContext{minSize: true}, nil, writeInt32, nil},\n\t\t\t\t{\"uint32/fast path - minsize too large\", uint32(2147483648), &EncodeContext{minSize: true}, nil, writeInt64, nil},\n\t\t\t\t{\"uint64/fast path - minsize too large\", uint64(2147483648), &EncodeContext{minSize: true}, nil, writeInt64, nil},\n\t\t\t\t{\"uint/fast path - minsize too large\", uint(2147483648), &EncodeContext{minSize: true}, nil, writeInt64, nil},\n\t\t\t\t{\"uint64/fast path - overflow\", uint64(1 << 63), nil, nil, nothing, fmt.Errorf(\"%d overflows int64\", uint64(1<<63))},\n\t\t\t\t{\"uint8/reflection path\", myuint8(127), nil, nil, writeInt32, nil},\n\t\t\t\t{\"uint16/reflection path\", myuint16(32767), nil, nil, writeInt32, nil},\n\t\t\t\t{\"uint32/reflection path\", myuint32(2147483647), nil, nil, writeInt64, nil},\n\t\t\t\t{\"uint64/reflection path\", myuint64(1234567890987), nil, nil, writeInt64, nil},\n\t\t\t\t{\"uint32/reflection path - minsize\", myuint32(2147483647), &EncodeContext{minSize: true}, nil, writeInt32, nil},\n\t\t\t\t{\"uint64/reflection path - minsize\", myuint64(2147483647), &EncodeContext{minSize: true}, nil, writeInt32, nil},\n\t\t\t\t{\"uint/reflection path - minsize\", myuint(2147483647), &EncodeContext{minSize: true}, nil, writeInt32, nil},\n\t\t\t\t{\"uint32/reflection path - minsize too large\", myuint(1 << 31), &EncodeContext{minSize: true}, nil, writeInt64, nil},\n\t\t\t\t{\"uint64/reflection path - minsize too large\", myuint64(1 << 31), &EncodeContext{minSize: true}, nil, writeInt64, nil},\n\t\t\t\t{\"uint/reflection path - minsize too large\", myuint(2147483648), &EncodeContext{minSize: true}, nil, writeInt64, nil},\n\t\t\t\t{\"uint64/reflection path - overflow\", myuint64(1 << 63), nil, nil, nothing, fmt.Errorf(\"%d overflows int64\", uint64(1<<63))},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"FloatEncodeValue\",\n\t\t\tValueEncoderFunc(floatEncodeValue),\n\t\t\t[]subtest{\n\t\t\t\t{\n\t\t\t\t\t\"wrong type\",\n\t\t\t\t\twrong,\n\t\t\t\t\tnil,\n\t\t\t\t\tnil,\n\t\t\t\t\tnothing,\n\t\t\t\t\tValueEncoderError{\n\t\t\t\t\t\tName:     \"FloatEncodeValue\",\n\t\t\t\t\t\tKinds:    []reflect.Kind{reflect.Float32, reflect.Float64},\n\t\t\t\t\t\tReceived: reflect.ValueOf(wrong),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\"float32/fast path\", float32(3.14159), nil, nil, writeDouble, nil},\n\t\t\t\t{\"float64/fast path\", float64(3.14159), nil, nil, writeDouble, nil},\n\t\t\t\t{\"float32/reflection path\", myfloat32(3.14159), nil, nil, writeDouble, nil},\n\t\t\t\t{\"float64/reflection path\", myfloat64(3.14159), nil, nil, writeDouble, nil},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"TimeEncodeValue\",\n\t\t\t&timeCodec{},\n\t\t\t[]subtest{\n\t\t\t\t{\n\t\t\t\t\t\"wrong type\",\n\t\t\t\t\twrong,\n\t\t\t\t\tnil,\n\t\t\t\t\tnil,\n\t\t\t\t\tnothing,\n\t\t\t\t\tValueEncoderError{Name: \"TimeEncodeValue\", Types: []reflect.Type{tTime}, Received: reflect.ValueOf(wrong)},\n\t\t\t\t},\n\t\t\t\t{\"time.Time\", now, nil, nil, writeDateTime, nil},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"MapEncodeValue\",\n\t\t\t&mapCodec{},\n\t\t\t[]subtest{\n\t\t\t\t{\n\t\t\t\t\t\"wrong kind\",\n\t\t\t\t\twrong,\n\t\t\t\t\tnil,\n\t\t\t\t\tnil,\n\t\t\t\t\tnothing,\n\t\t\t\t\tValueEncoderError{Name: \"MapEncodeValue\", Kinds: []reflect.Kind{reflect.Map}, Received: reflect.ValueOf(wrong)},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"WriteDocument Error\",\n\t\t\t\t\tmap[string]any{},\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{Err: errors.New(\"wd error\"), ErrAfter: writeDocument},\n\t\t\t\t\twriteDocument,\n\t\t\t\t\terrors.New(\"wd error\"),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"Lookup Error\",\n\t\t\t\t\tmap[string]int{\"foo\": 1},\n\t\t\t\t\t&EncodeContext{Registry: newTestRegistry()},\n\t\t\t\t\t&valueReaderWriter{},\n\t\t\t\t\twriteDocument,\n\t\t\t\t\tfmt.Errorf(\"no encoder found for int\"),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"WriteDocumentElement Error\",\n\t\t\t\t\tmap[string]any{\"foo\": \"bar\"},\n\t\t\t\t\t&EncodeContext{Registry: buildDefaultRegistry()},\n\t\t\t\t\t&valueReaderWriter{Err: errors.New(\"wde error\"), ErrAfter: writeDocumentElement},\n\t\t\t\t\twriteDocumentElement,\n\t\t\t\t\terrors.New(\"wde error\"),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"EncodeValue Error\",\n\t\t\t\t\tmap[string]any{\"foo\": \"bar\"},\n\t\t\t\t\t&EncodeContext{Registry: buildDefaultRegistry()},\n\t\t\t\t\t&valueReaderWriter{Err: errors.New(\"ev error\"), ErrAfter: writeString},\n\t\t\t\t\twriteString,\n\t\t\t\t\terrors.New(\"ev error\"),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"empty map/success\",\n\t\t\t\t\tmap[string]any{},\n\t\t\t\t\t&EncodeContext{Registry: newTestRegistry()},\n\t\t\t\t\t&valueReaderWriter{},\n\t\t\t\t\twriteDocumentEnd,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"with interface/success\",\n\t\t\t\t\tmap[string]myInterface{\"foo\": myStruct{1}},\n\t\t\t\t\t&EncodeContext{Registry: buildDefaultRegistry()},\n\t\t\t\t\tnil,\n\t\t\t\t\twriteDocumentEnd,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"with interface/nil/success\",\n\t\t\t\t\tmap[string]myInterface{\"foo\": nil},\n\t\t\t\t\t&EncodeContext{Registry: buildDefaultRegistry()},\n\t\t\t\t\tnil,\n\t\t\t\t\twriteDocumentEnd,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"non-string key success\",\n\t\t\t\t\tmap[int]any{\n\t\t\t\t\t\t1: \"foobar\",\n\t\t\t\t\t},\n\t\t\t\t\t&EncodeContext{Registry: buildDefaultRegistry()},\n\t\t\t\t\t&valueReaderWriter{},\n\t\t\t\t\twriteDocumentEnd,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"ArrayEncodeValue\",\n\t\t\tValueEncoderFunc(arrayEncodeValue),\n\t\t\t[]subtest{\n\t\t\t\t{\n\t\t\t\t\t\"wrong kind\",\n\t\t\t\t\twrong,\n\t\t\t\t\tnil,\n\t\t\t\t\tnil,\n\t\t\t\t\tnothing,\n\t\t\t\t\tValueEncoderError{Name: \"ArrayEncodeValue\", Kinds: []reflect.Kind{reflect.Array}, Received: reflect.ValueOf(wrong)},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"WriteArray Error\",\n\t\t\t\t\t[1]string{},\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{Err: errors.New(\"wa error\"), ErrAfter: writeArray},\n\t\t\t\t\twriteArray,\n\t\t\t\t\terrors.New(\"wa error\"),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"Lookup Error\",\n\t\t\t\t\t[1]int{1},\n\t\t\t\t\t&EncodeContext{Registry: newTestRegistry()},\n\t\t\t\t\t&valueReaderWriter{},\n\t\t\t\t\twriteArray,\n\t\t\t\t\tfmt.Errorf(\"no encoder found for int\"),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"WriteArrayElement Error\",\n\t\t\t\t\t[1]string{\"foo\"},\n\t\t\t\t\t&EncodeContext{Registry: buildDefaultRegistry()},\n\t\t\t\t\t&valueReaderWriter{Err: errors.New(\"wae error\"), ErrAfter: writeArrayElement},\n\t\t\t\t\twriteArrayElement,\n\t\t\t\t\terrors.New(\"wae error\"),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"EncodeValue Error\",\n\t\t\t\t\t[1]string{\"foo\"},\n\t\t\t\t\t&EncodeContext{Registry: buildDefaultRegistry()},\n\t\t\t\t\t&valueReaderWriter{Err: errors.New(\"ev error\"), ErrAfter: writeString},\n\t\t\t\t\twriteString,\n\t\t\t\t\terrors.New(\"ev error\"),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"[1]E/success\",\n\t\t\t\t\t[1]E{{\"hello\", \"world\"}},\n\t\t\t\t\t&EncodeContext{Registry: buildDefaultRegistry()},\n\t\t\t\t\tnil,\n\t\t\t\t\twriteDocumentEnd,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"[1]E/success\",\n\t\t\t\t\t[1]E{{\"hello\", nil}},\n\t\t\t\t\t&EncodeContext{Registry: buildDefaultRegistry()},\n\t\t\t\t\tnil,\n\t\t\t\t\twriteDocumentEnd,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"[1]interface/success\",\n\t\t\t\t\t[1]myInterface{myStruct{1}},\n\t\t\t\t\t&EncodeContext{Registry: buildDefaultRegistry()},\n\t\t\t\t\tnil,\n\t\t\t\t\twriteArrayEnd,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"[1]interface/nil/success\",\n\t\t\t\t\t[1]myInterface{nil},\n\t\t\t\t\t&EncodeContext{Registry: buildDefaultRegistry()},\n\t\t\t\t\tnil,\n\t\t\t\t\twriteArrayEnd,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"SliceEncodeValue\",\n\t\t\t&sliceCodec{},\n\t\t\t[]subtest{\n\t\t\t\t{\n\t\t\t\t\t\"wrong kind\",\n\t\t\t\t\twrong,\n\t\t\t\t\tnil,\n\t\t\t\t\tnil,\n\t\t\t\t\tnothing,\n\t\t\t\t\tValueEncoderError{Name: \"SliceEncodeValue\", Kinds: []reflect.Kind{reflect.Slice}, Received: reflect.ValueOf(wrong)},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"WriteArray Error\",\n\t\t\t\t\t[]string{},\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{Err: errors.New(\"wa error\"), ErrAfter: writeArray},\n\t\t\t\t\twriteArray,\n\t\t\t\t\terrors.New(\"wa error\"),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"Lookup Error\",\n\t\t\t\t\t[]int{1},\n\t\t\t\t\t&EncodeContext{Registry: newTestRegistry()},\n\t\t\t\t\t&valueReaderWriter{},\n\t\t\t\t\twriteArray,\n\t\t\t\t\tfmt.Errorf(\"no encoder found for int\"),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"WriteArrayElement Error\",\n\t\t\t\t\t[]string{\"foo\"},\n\t\t\t\t\t&EncodeContext{Registry: buildDefaultRegistry()},\n\t\t\t\t\t&valueReaderWriter{Err: errors.New(\"wae error\"), ErrAfter: writeArrayElement},\n\t\t\t\t\twriteArrayElement,\n\t\t\t\t\terrors.New(\"wae error\"),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"EncodeValue Error\",\n\t\t\t\t\t[]string{\"foo\"},\n\t\t\t\t\t&EncodeContext{Registry: buildDefaultRegistry()},\n\t\t\t\t\t&valueReaderWriter{Err: errors.New(\"ev error\"), ErrAfter: writeString},\n\t\t\t\t\twriteString,\n\t\t\t\t\terrors.New(\"ev error\"),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"D/success\",\n\t\t\t\t\tD{{\"hello\", \"world\"}},\n\t\t\t\t\t&EncodeContext{Registry: buildDefaultRegistry()},\n\t\t\t\t\tnil,\n\t\t\t\t\twriteDocumentEnd,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"D/success\",\n\t\t\t\t\tD{{\"hello\", nil}},\n\t\t\t\t\t&EncodeContext{Registry: buildDefaultRegistry()},\n\t\t\t\t\tnil,\n\t\t\t\t\twriteDocumentEnd,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"empty slice/success\",\n\t\t\t\t\t[]any{},\n\t\t\t\t\t&EncodeContext{Registry: newTestRegistry()},\n\t\t\t\t\t&valueReaderWriter{},\n\t\t\t\t\twriteArrayEnd,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"interface/success\",\n\t\t\t\t\t[]myInterface{myStruct{1}},\n\t\t\t\t\t&EncodeContext{Registry: buildDefaultRegistry()},\n\t\t\t\t\tnil,\n\t\t\t\t\twriteArrayEnd,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"interface/success\",\n\t\t\t\t\t[]myInterface{nil},\n\t\t\t\t\t&EncodeContext{Registry: buildDefaultRegistry()},\n\t\t\t\t\tnil,\n\t\t\t\t\twriteArrayEnd,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"ObjectIDEncodeValue\",\n\t\t\tValueEncoderFunc(objectIDEncodeValue),\n\t\t\t[]subtest{\n\t\t\t\t{\n\t\t\t\t\t\"wrong type\",\n\t\t\t\t\twrong,\n\t\t\t\t\tnil,\n\t\t\t\t\tnil,\n\t\t\t\t\tnothing,\n\t\t\t\t\tValueEncoderError{Name: \"ObjectIDEncodeValue\", Types: []reflect.Type{tOID}, Received: reflect.ValueOf(wrong)},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"ObjectID/success\",\n\t\t\t\t\tObjectID{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C},\n\t\t\t\t\tnil, nil, writeObjectID, nil,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"Decimal128EncodeValue\",\n\t\t\tValueEncoderFunc(decimal128EncodeValue),\n\t\t\t[]subtest{\n\t\t\t\t{\n\t\t\t\t\t\"wrong type\",\n\t\t\t\t\twrong,\n\t\t\t\t\tnil,\n\t\t\t\t\tnil,\n\t\t\t\t\tnothing,\n\t\t\t\t\tValueEncoderError{Name: \"Decimal128EncodeValue\", Types: []reflect.Type{tDecimal}, Received: reflect.ValueOf(wrong)},\n\t\t\t\t},\n\t\t\t\t{\"Decimal128/success\", d128, nil, nil, writeDecimal128, nil},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"JSONNumberEncodeValue\",\n\t\t\tValueEncoderFunc(jsonNumberEncodeValue),\n\t\t\t[]subtest{\n\t\t\t\t{\n\t\t\t\t\t\"wrong type\",\n\t\t\t\t\twrong,\n\t\t\t\t\tnil,\n\t\t\t\t\tnil,\n\t\t\t\t\tnothing,\n\t\t\t\t\tValueEncoderError{Name: \"JSONNumberEncodeValue\", Types: []reflect.Type{tJSONNumber}, Received: reflect.ValueOf(wrong)},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"json.Number/invalid\",\n\t\t\t\t\tjson.Number(\"hello world\"),\n\t\t\t\t\tnil, nil, nothing, errors.New(`strconv.ParseFloat: parsing \"hello world\": invalid syntax`),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"json.Number/int64/success\",\n\t\t\t\t\tjson.Number(\"1234567890\"),\n\t\t\t\t\tnil, nil, writeInt64, nil,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"json.Number/float64/success\",\n\t\t\t\t\tjson.Number(\"3.14159\"),\n\t\t\t\t\tnil, nil, writeDouble, nil,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"URLEncodeValue\",\n\t\t\tValueEncoderFunc(urlEncodeValue),\n\t\t\t[]subtest{\n\t\t\t\t{\n\t\t\t\t\t\"wrong type\",\n\t\t\t\t\twrong,\n\t\t\t\t\tnil,\n\t\t\t\t\tnil,\n\t\t\t\t\tnothing,\n\t\t\t\t\tValueEncoderError{Name: \"URLEncodeValue\", Types: []reflect.Type{tURL}, Received: reflect.ValueOf(wrong)},\n\t\t\t\t},\n\t\t\t\t{\"url.URL\", url.URL{Scheme: \"http\", Host: \"example.com\"}, nil, nil, writeString, nil},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"ByteSliceEncodeValue\",\n\t\t\t&byteSliceCodec{},\n\t\t\t[]subtest{\n\t\t\t\t{\n\t\t\t\t\t\"wrong type\",\n\t\t\t\t\twrong,\n\t\t\t\t\tnil,\n\t\t\t\t\tnil,\n\t\t\t\t\tnothing,\n\t\t\t\t\tValueEncoderError{Name: \"ByteSliceEncodeValue\", Types: []reflect.Type{tByteSlice}, Received: reflect.ValueOf(wrong)},\n\t\t\t\t},\n\t\t\t\t{\"[]byte\", []byte{0x01, 0x02, 0x03}, nil, nil, writeBinary, nil},\n\t\t\t\t{\"[]byte/nil\", []byte(nil), nil, nil, writeNull, nil},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"EmptyInterfaceEncodeValue\",\n\t\t\t&emptyInterfaceCodec{},\n\t\t\t[]subtest{\n\t\t\t\t{\n\t\t\t\t\t\"wrong type\",\n\t\t\t\t\twrong,\n\t\t\t\t\tnil,\n\t\t\t\t\tnil,\n\t\t\t\t\tnothing,\n\t\t\t\t\tValueEncoderError{Name: \"EmptyInterfaceEncodeValue\", Types: []reflect.Type{tEmpty}, Received: reflect.ValueOf(wrong)},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"ValueMarshalerEncodeValue\",\n\t\t\tValueEncoderFunc(valueMarshalerEncodeValue),\n\t\t\t[]subtest{\n\t\t\t\t{\n\t\t\t\t\t\"wrong type\",\n\t\t\t\t\twrong,\n\t\t\t\t\tnil,\n\t\t\t\t\tnil,\n\t\t\t\t\tnothing,\n\t\t\t\t\tValueEncoderError{\n\t\t\t\t\t\tName:     \"ValueMarshalerEncodeValue\",\n\t\t\t\t\t\tTypes:    []reflect.Type{tValueMarshaler},\n\t\t\t\t\t\tReceived: reflect.ValueOf(wrong),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"MarshalBSONValue error\",\n\t\t\t\t\ttestValueMarshaler{err: errors.New(\"mbsonv error\")},\n\t\t\t\t\tnil,\n\t\t\t\t\tnil,\n\t\t\t\t\tnothing,\n\t\t\t\t\terrors.New(\"mbsonv error\"),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"Copy error\",\n\t\t\t\t\ttestValueMarshaler{},\n\t\t\t\t\tnil,\n\t\t\t\t\tnil,\n\t\t\t\t\tnothing,\n\t\t\t\t\tfmt.Errorf(\"cannot copy unknown BSON type %s\", Type(0)),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"success struct implementation\",\n\t\t\t\t\ttestValueMarshaler{t: TypeString, buf: []byte{0x04, 0x00, 0x00, 0x00, 'f', 'o', 'o', 0x00}},\n\t\t\t\t\tnil,\n\t\t\t\t\tnil,\n\t\t\t\t\twriteString,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"success ptr to struct implementation\",\n\t\t\t\t\t&testValueMarshaler{t: TypeString, buf: []byte{0x04, 0x00, 0x00, 0x00, 'f', 'o', 'o', 0x00}},\n\t\t\t\t\tnil,\n\t\t\t\t\tnil,\n\t\t\t\t\twriteString,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"success nil ptr to struct implementation\",\n\t\t\t\t\tnilValueMarshaler,\n\t\t\t\t\tnil,\n\t\t\t\t\tnil,\n\t\t\t\t\twriteNull,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"success ptr to ptr implementation\",\n\t\t\t\t\t&testValueMarshalPtr{t: TypeString, buf: []byte{0x04, 0x00, 0x00, 0x00, 'f', 'o', 'o', 0x00}},\n\t\t\t\t\tnil,\n\t\t\t\t\tnil,\n\t\t\t\t\twriteString,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"unaddressable ptr implementation\",\n\t\t\t\t\ttestValueMarshalPtr{t: TypeString, buf: []byte{0x04, 0x00, 0x00, 0x00, 'f', 'o', 'o', 0x00}},\n\t\t\t\t\tnil,\n\t\t\t\t\tnil,\n\t\t\t\t\tnothing,\n\t\t\t\t\tValueEncoderError{\n\t\t\t\t\t\tName:     \"ValueMarshalerEncodeValue\",\n\t\t\t\t\t\tTypes:    []reflect.Type{tValueMarshaler},\n\t\t\t\t\t\tReceived: reflect.ValueOf(testValueMarshalPtr{}),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"MarshalerEncodeValue\",\n\t\t\tValueEncoderFunc(marshalerEncodeValue),\n\t\t\t[]subtest{\n\t\t\t\t{\n\t\t\t\t\t\"wrong type\",\n\t\t\t\t\twrong,\n\t\t\t\t\tnil,\n\t\t\t\t\tnil,\n\t\t\t\t\tnothing,\n\t\t\t\t\tValueEncoderError{Name: \"MarshalerEncodeValue\", Types: []reflect.Type{tMarshaler}, Received: reflect.ValueOf(wrong)},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"MarshalBSON error\",\n\t\t\t\t\ttestMarshaler{err: errors.New(\"mbson error\")},\n\t\t\t\t\tnil,\n\t\t\t\t\tnil,\n\t\t\t\t\tnothing,\n\t\t\t\t\terrors.New(\"mbson error\"),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"success struct implementation\",\n\t\t\t\t\ttestMarshaler{buf: bsoncore.BuildDocument(nil, bsoncore.AppendDoubleElement(nil, \"pi\", 3.14159))},\n\t\t\t\t\tnil,\n\t\t\t\t\tnil,\n\t\t\t\t\twriteDocumentEnd,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"success ptr to struct implementation\",\n\t\t\t\t\t&testMarshaler{buf: bsoncore.BuildDocument(nil, bsoncore.AppendDoubleElement(nil, \"pi\", 3.14159))},\n\t\t\t\t\tnil,\n\t\t\t\t\tnil,\n\t\t\t\t\twriteDocumentEnd,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"success nil ptr to struct implementation\",\n\t\t\t\t\tnilMarshaler,\n\t\t\t\t\tnil,\n\t\t\t\t\tnil,\n\t\t\t\t\twriteNull,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"success ptr to ptr implementation\",\n\t\t\t\t\t&testMarshalPtr{buf: bsoncore.BuildDocument(nil, bsoncore.AppendDoubleElement(nil, \"pi\", 3.14159))},\n\t\t\t\t\tnil,\n\t\t\t\t\tnil,\n\t\t\t\t\twriteDocumentEnd,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"unaddressable ptr implementation\",\n\t\t\t\t\ttestMarshalPtr{buf: bsoncore.BuildDocument(nil, bsoncore.AppendDoubleElement(nil, \"pi\", 3.14159))},\n\t\t\t\t\tnil,\n\t\t\t\t\tnil,\n\t\t\t\t\tnothing,\n\t\t\t\t\tValueEncoderError{Name: \"MarshalerEncodeValue\", Types: []reflect.Type{tMarshaler}, Received: reflect.ValueOf(testMarshalPtr{})},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"PointerCodec.EncodeValue\",\n\t\t\t&pointerCodec{},\n\t\t\t[]subtest{\n\t\t\t\t{\n\t\t\t\t\t\"nil\",\n\t\t\t\t\tnil,\n\t\t\t\t\tnil,\n\t\t\t\t\tnil,\n\t\t\t\t\twriteNull,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"not pointer\",\n\t\t\t\t\tint32(123456),\n\t\t\t\t\tnil,\n\t\t\t\t\tnil,\n\t\t\t\t\tnothing,\n\t\t\t\t\tValueEncoderError{Name: \"PointerCodec.EncodeValue\", Kinds: []reflect.Kind{reflect.Ptr}, Received: reflect.ValueOf(int32(123456))},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"typed nil\",\n\t\t\t\t\t(*int32)(nil),\n\t\t\t\t\tnil,\n\t\t\t\t\tnil,\n\t\t\t\t\twriteNull,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"no encoder\",\n\t\t\t\t\t&wrong,\n\t\t\t\t\t&EncodeContext{Registry: buildDefaultRegistry()},\n\t\t\t\t\tnil,\n\t\t\t\t\tnothing,\n\t\t\t\t\terrNoEncoder{Type: reflect.TypeOf(wrong)},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"pointer implementation addressable interface\",\n\t\t\t&pointerCodec{},\n\t\t\t[]subtest{\n\t\t\t\t{\n\t\t\t\t\t\"ValueMarshaler\",\n\t\t\t\t\t&vmStruct,\n\t\t\t\t\t&EncodeContext{Registry: buildDefaultRegistry()},\n\t\t\t\t\tnil,\n\t\t\t\t\twriteDocumentEnd,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"Marshaler\",\n\t\t\t\t\t&mStruct,\n\t\t\t\t\t&EncodeContext{Registry: buildDefaultRegistry()},\n\t\t\t\t\tnil,\n\t\t\t\t\twriteDocumentEnd,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"JavaScriptEncodeValue\",\n\t\t\tValueEncoderFunc(javaScriptEncodeValue),\n\t\t\t[]subtest{\n\t\t\t\t{\n\t\t\t\t\t\"wrong type\",\n\t\t\t\t\twrong,\n\t\t\t\t\tnil,\n\t\t\t\t\tnil,\n\t\t\t\t\tnothing,\n\t\t\t\t\tValueEncoderError{Name: \"JavaScriptEncodeValue\", Types: []reflect.Type{tJavaScript}, Received: reflect.ValueOf(wrong)},\n\t\t\t\t},\n\t\t\t\t{\"JavaScript\", JavaScript(\"foobar\"), nil, nil, writeJavascript, nil},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"SymbolEncodeValue\",\n\t\t\tValueEncoderFunc(symbolEncodeValue),\n\t\t\t[]subtest{\n\t\t\t\t{\n\t\t\t\t\t\"wrong type\",\n\t\t\t\t\twrong,\n\t\t\t\t\tnil,\n\t\t\t\t\tnil,\n\t\t\t\t\tnothing,\n\t\t\t\t\tValueEncoderError{Name: \"SymbolEncodeValue\", Types: []reflect.Type{tSymbol}, Received: reflect.ValueOf(wrong)},\n\t\t\t\t},\n\t\t\t\t{\"Symbol\", Symbol(\"foobar\"), nil, nil, writeSymbol, nil},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"BinaryEncodeValue\",\n\t\t\tValueEncoderFunc(binaryEncodeValue),\n\t\t\t[]subtest{\n\t\t\t\t{\n\t\t\t\t\t\"wrong type\",\n\t\t\t\t\twrong,\n\t\t\t\t\tnil,\n\t\t\t\t\tnil,\n\t\t\t\t\tnothing,\n\t\t\t\t\tValueEncoderError{Name: \"BinaryEncodeValue\", Types: []reflect.Type{tBinary}, Received: reflect.ValueOf(wrong)},\n\t\t\t\t},\n\t\t\t\t{\"Binary/success\", Binary{Data: []byte{0x01, 0x02}, Subtype: 0xFF}, nil, nil, writeBinaryWithSubtype, nil},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"UndefinedEncodeValue\",\n\t\t\tValueEncoderFunc(undefinedEncodeValue),\n\t\t\t[]subtest{\n\t\t\t\t{\n\t\t\t\t\t\"wrong type\",\n\t\t\t\t\twrong,\n\t\t\t\t\tnil,\n\t\t\t\t\tnil,\n\t\t\t\t\tnothing,\n\t\t\t\t\tValueEncoderError{Name: \"UndefinedEncodeValue\", Types: []reflect.Type{tUndefined}, Received: reflect.ValueOf(wrong)},\n\t\t\t\t},\n\t\t\t\t{\"Undefined/success\", Undefined{}, nil, nil, writeUndefined, nil},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"DateTimeEncodeValue\",\n\t\t\tValueEncoderFunc(dateTimeEncodeValue),\n\t\t\t[]subtest{\n\t\t\t\t{\n\t\t\t\t\t\"wrong type\",\n\t\t\t\t\twrong,\n\t\t\t\t\tnil,\n\t\t\t\t\tnil,\n\t\t\t\t\tnothing,\n\t\t\t\t\tValueEncoderError{Name: \"DateTimeEncodeValue\", Types: []reflect.Type{tDateTime}, Received: reflect.ValueOf(wrong)},\n\t\t\t\t},\n\t\t\t\t{\"DateTime/success\", DateTime(1234567890), nil, nil, writeDateTime, nil},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"NullEncodeValue\",\n\t\t\tValueEncoderFunc(nullEncodeValue),\n\t\t\t[]subtest{\n\t\t\t\t{\n\t\t\t\t\t\"wrong type\",\n\t\t\t\t\twrong,\n\t\t\t\t\tnil,\n\t\t\t\t\tnil,\n\t\t\t\t\tnothing,\n\t\t\t\t\tValueEncoderError{Name: \"NullEncodeValue\", Types: []reflect.Type{tNull}, Received: reflect.ValueOf(wrong)},\n\t\t\t\t},\n\t\t\t\t{\"Null/success\", Null{}, nil, nil, writeNull, nil},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"RegexEncodeValue\",\n\t\t\tValueEncoderFunc(regexEncodeValue),\n\t\t\t[]subtest{\n\t\t\t\t{\n\t\t\t\t\t\"wrong type\",\n\t\t\t\t\twrong,\n\t\t\t\t\tnil,\n\t\t\t\t\tnil,\n\t\t\t\t\tnothing,\n\t\t\t\t\tValueEncoderError{Name: \"RegexEncodeValue\", Types: []reflect.Type{tRegex}, Received: reflect.ValueOf(wrong)},\n\t\t\t\t},\n\t\t\t\t{\"Regex/success\", Regex{Pattern: \"foo\", Options: \"bar\"}, nil, nil, writeRegex, nil},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"DBPointerEncodeValue\",\n\t\t\tValueEncoderFunc(dbPointerEncodeValue),\n\t\t\t[]subtest{\n\t\t\t\t{\n\t\t\t\t\t\"wrong type\",\n\t\t\t\t\twrong,\n\t\t\t\t\tnil,\n\t\t\t\t\tnil,\n\t\t\t\t\tnothing,\n\t\t\t\t\tValueEncoderError{Name: \"DBPointerEncodeValue\", Types: []reflect.Type{tDBPointer}, Received: reflect.ValueOf(wrong)},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"DBPointer/success\",\n\t\t\t\t\tDBPointer{\n\t\t\t\t\t\tDB:      \"foobar\",\n\t\t\t\t\t\tPointer: ObjectID{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C},\n\t\t\t\t\t},\n\t\t\t\t\tnil, nil, writeDBPointer, nil,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"TimestampEncodeValue\",\n\t\t\tValueEncoderFunc(timestampEncodeValue),\n\t\t\t[]subtest{\n\t\t\t\t{\n\t\t\t\t\t\"wrong type\",\n\t\t\t\t\twrong,\n\t\t\t\t\tnil,\n\t\t\t\t\tnil,\n\t\t\t\t\tnothing,\n\t\t\t\t\tValueEncoderError{Name: \"TimestampEncodeValue\", Types: []reflect.Type{tTimestamp}, Received: reflect.ValueOf(wrong)},\n\t\t\t\t},\n\t\t\t\t{\"Timestamp/success\", Timestamp{T: 12345, I: 67890}, nil, nil, writeTimestamp, nil},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"MinKeyEncodeValue\",\n\t\t\tValueEncoderFunc(minKeyEncodeValue),\n\t\t\t[]subtest{\n\t\t\t\t{\n\t\t\t\t\t\"wrong type\",\n\t\t\t\t\twrong,\n\t\t\t\t\tnil,\n\t\t\t\t\tnil,\n\t\t\t\t\tnothing,\n\t\t\t\t\tValueEncoderError{Name: \"MinKeyEncodeValue\", Types: []reflect.Type{tMinKey}, Received: reflect.ValueOf(wrong)},\n\t\t\t\t},\n\t\t\t\t{\"MinKey/success\", MinKey{}, nil, nil, writeMinKey, nil},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"MaxKeyEncodeValue\",\n\t\t\tValueEncoderFunc(maxKeyEncodeValue),\n\t\t\t[]subtest{\n\t\t\t\t{\n\t\t\t\t\t\"wrong type\",\n\t\t\t\t\twrong,\n\t\t\t\t\tnil,\n\t\t\t\t\tnil,\n\t\t\t\t\tnothing,\n\t\t\t\t\tValueEncoderError{Name: \"MaxKeyEncodeValue\", Types: []reflect.Type{tMaxKey}, Received: reflect.ValueOf(wrong)},\n\t\t\t\t},\n\t\t\t\t{\"MaxKey/success\", MaxKey{}, nil, nil, writeMaxKey, nil},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"CoreDocumentEncodeValue\",\n\t\t\tValueEncoderFunc(coreDocumentEncodeValue),\n\t\t\t[]subtest{\n\t\t\t\t{\n\t\t\t\t\t\"wrong type\",\n\t\t\t\t\twrong,\n\t\t\t\t\tnil,\n\t\t\t\t\tnil,\n\t\t\t\t\tnothing,\n\t\t\t\t\tValueEncoderError{\n\t\t\t\t\t\tName:     \"CoreDocumentEncodeValue\",\n\t\t\t\t\t\tTypes:    []reflect.Type{tCoreDocument},\n\t\t\t\t\t\tReceived: reflect.ValueOf(wrong),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"WriteDocument Error\",\n\t\t\t\t\tbsoncore.Document{},\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{Err: errors.New(\"wd error\"), ErrAfter: writeDocument},\n\t\t\t\t\twriteDocument,\n\t\t\t\t\terrors.New(\"wd error\"),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"bsoncore.Document.Elements Error\",\n\t\t\t\t\tbsoncore.Document{0xFF, 0x00, 0x00, 0x00, 0x00},\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{},\n\t\t\t\t\twriteDocument,\n\t\t\t\t\terrors.New(\"length read exceeds number of bytes available. length=5 bytes=255\"),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"WriteDocumentElement Error\",\n\t\t\t\t\tbsoncore.Document(buildDocument(bsoncore.AppendNullElement(nil, \"foo\"))),\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{Err: errors.New(\"wde error\"), ErrAfter: writeDocumentElement},\n\t\t\t\t\twriteDocumentElement,\n\t\t\t\t\terrors.New(\"wde error\"),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"encodeValue error\",\n\t\t\t\t\tbsoncore.Document(buildDocument(bsoncore.AppendNullElement(nil, \"foo\"))),\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{Err: errors.New(\"ev error\"), ErrAfter: writeNull},\n\t\t\t\t\twriteNull,\n\t\t\t\t\terrors.New(\"ev error\"),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"iterator error\",\n\t\t\t\t\tbsoncore.Document{0x0C, 0x00, 0x00, 0x00, 0x01, 'f', 'o', 'o', 0x00, 0x01, 0x02, 0x03},\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{},\n\t\t\t\t\twriteDocumentElement,\n\t\t\t\t\terrors.New(\"not enough bytes available to read type. bytes=3 type=double\"),\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"StructEncodeValue\",\n\t\t\tnewStructCodec(&mapCodec{}),\n\t\t\t[]subtest{\n\t\t\t\t{\n\t\t\t\t\t\"interface value\",\n\t\t\t\t\tstruct{ Foo myInterface }{Foo: myStruct{1}},\n\t\t\t\t\t&EncodeContext{Registry: buildDefaultRegistry()},\n\t\t\t\t\tnil,\n\t\t\t\t\twriteDocumentEnd,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"nil interface value\",\n\t\t\t\t\tstruct{ Foo myInterface }{Foo: nil},\n\t\t\t\t\t&EncodeContext{Registry: buildDefaultRegistry()},\n\t\t\t\t\tnil,\n\t\t\t\t\twriteDocumentEnd,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"CodeWithScopeEncodeValue\",\n\t\t\tValueEncoderFunc(codeWithScopeEncodeValue),\n\t\t\t[]subtest{\n\t\t\t\t{\n\t\t\t\t\t\"wrong type\",\n\t\t\t\t\twrong,\n\t\t\t\t\tnil,\n\t\t\t\t\tnil,\n\t\t\t\t\tnothing,\n\t\t\t\t\tValueEncoderError{\n\t\t\t\t\t\tName:     \"CodeWithScopeEncodeValue\",\n\t\t\t\t\t\tTypes:    []reflect.Type{tCodeWithScope},\n\t\t\t\t\t\tReceived: reflect.ValueOf(wrong),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"WriteCodeWithScope error\",\n\t\t\t\t\tCodeWithScope{},\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{Err: errors.New(\"wcws error\"), ErrAfter: writeCodeWithScope},\n\t\t\t\t\twriteCodeWithScope,\n\t\t\t\t\terrors.New(\"wcws error\"),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"CodeWithScope/success\",\n\t\t\t\t\tCodeWithScope{\n\t\t\t\t\t\tCode:  \"var hello = 'world';\",\n\t\t\t\t\t\tScope: D{},\n\t\t\t\t\t},\n\t\t\t\t\t&EncodeContext{Registry: buildDefaultRegistry()},\n\t\t\t\t\tnil, writeDocumentEnd, nil,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"CoreArrayEncodeValue\",\n\t\t\t&arrayCodec{},\n\t\t\t[]subtest{\n\t\t\t\t{\n\t\t\t\t\t\"wrong type\",\n\t\t\t\t\twrong,\n\t\t\t\t\tnil,\n\t\t\t\t\tnil,\n\t\t\t\t\tnothing,\n\t\t\t\t\tValueEncoderError{\n\t\t\t\t\t\tName:     \"CoreArrayEncodeValue\",\n\t\t\t\t\t\tTypes:    []reflect.Type{tCoreArray},\n\t\t\t\t\t\tReceived: reflect.ValueOf(wrong),\n\t\t\t\t\t},\n\t\t\t\t},\n\n\t\t\t\t{\n\t\t\t\t\t\"WriteArray Error\",\n\t\t\t\t\tbsoncore.Array{},\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{Err: errors.New(\"wa error\"), ErrAfter: writeArray},\n\t\t\t\t\twriteArray,\n\t\t\t\t\terrors.New(\"wa error\"),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"WriteArrayElement Error\",\n\t\t\t\t\tbsoncore.Array(buildDocumentArray(func([]byte) []byte {\n\t\t\t\t\t\treturn bsoncore.AppendNullElement(nil, \"foo\")\n\t\t\t\t\t})),\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{Err: errors.New(\"wae error\"), ErrAfter: writeArrayElement},\n\t\t\t\t\twriteArrayElement,\n\t\t\t\t\terrors.New(\"wae error\"),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"encodeValue error\",\n\t\t\t\t\tbsoncore.Array(buildDocumentArray(func([]byte) []byte {\n\t\t\t\t\t\treturn bsoncore.AppendNullElement(nil, \"foo\")\n\t\t\t\t\t})),\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{Err: errors.New(\"ev error\"), ErrAfter: writeNull},\n\t\t\t\t\twriteNull,\n\t\t\t\t\terrors.New(\"ev error\"),\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n\n\tfor _, tc := range testCases {\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\tfor _, subtest := range tc.subtests {\n\t\t\t\tt.Run(subtest.name, func(t *testing.T) {\n\t\t\t\t\tvar ec EncodeContext\n\t\t\t\t\tif subtest.ectx != nil {\n\t\t\t\t\t\tec = *subtest.ectx\n\t\t\t\t\t}\n\t\t\t\t\tllvrw := new(valueReaderWriter)\n\t\t\t\t\tif subtest.llvrw != nil {\n\t\t\t\t\t\tllvrw = subtest.llvrw\n\t\t\t\t\t}\n\t\t\t\t\tllvrw.T = t\n\t\t\t\t\terr := tc.ve.EncodeValue(ec, llvrw, reflect.ValueOf(subtest.val))\n\t\t\t\t\tif !assert.CompareErrors(err, subtest.err) {\n\t\t\t\t\t\tt.Errorf(\"Errors do not match. got %v; want %v\", err, subtest.err)\n\t\t\t\t\t}\n\t\t\t\t\tinvoked := llvrw.invoked\n\t\t\t\t\tif !cmp.Equal(invoked, subtest.invoke) {\n\t\t\t\t\t\tt.Errorf(\"Incorrect method invoked. got %v; want %v\", invoked, subtest.invoke)\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\t}\n\n\tt.Run(\"success path\", func(t *testing.T) {\n\t\toid := NewObjectID()\n\t\toids := []ObjectID{NewObjectID(), NewObjectID(), NewObjectID()}\n\t\tstr := new(string)\n\t\t*str = \"bar\"\n\t\tnow := time.Now().Truncate(time.Millisecond)\n\t\tmurl, err := url.Parse(\"https://mongodb.com/random-url?hello=world\")\n\t\tif err != nil {\n\t\t\tt.Errorf(\"Error parsing URL: %v\", err)\n\t\t\tt.FailNow()\n\t\t}\n\t\tdecimal128, err := ParseDecimal128(\"1.5e10\")\n\t\tif err != nil {\n\t\t\tt.Errorf(\"Error parsing decimal128: %v\", err)\n\t\t\tt.FailNow()\n\t\t}\n\n\t\ttestCases := []struct {\n\t\t\tname  string\n\t\t\tvalue any\n\t\t\tb     []byte\n\t\t\terr   error\n\t\t}{\n\t\t\t{\n\t\t\t\t\"map[string]int\",\n\t\t\t\tmap[string]int32{\"foo\": 1},\n\t\t\t\t[]byte{\n\t\t\t\t\t0x0E, 0x00, 0x00, 0x00,\n\t\t\t\t\t0x10, 'f', 'o', 'o', 0x00,\n\t\t\t\t\t0x01, 0x00, 0x00, 0x00,\n\t\t\t\t\t0x00,\n\t\t\t\t},\n\t\t\t\tnil,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"map[string]ObjectID\",\n\t\t\t\tmap[string]ObjectID{\"foo\": oid},\n\t\t\t\tbuildDocument(bsoncore.AppendObjectIDElement(nil, \"foo\", oid)),\n\t\t\t\tnil,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"map[string][]int32\",\n\t\t\t\tmap[string][]int32{\"Z\": {1, 2, 3}},\n\t\t\t\tbuildDocumentArray(func(doc []byte) []byte {\n\t\t\t\t\tdoc = bsoncore.AppendInt32Element(doc, \"0\", 1)\n\t\t\t\t\tdoc = bsoncore.AppendInt32Element(doc, \"1\", 2)\n\t\t\t\t\treturn bsoncore.AppendInt32Element(doc, \"2\", 3)\n\t\t\t\t}),\n\t\t\t\tnil,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"map[string][]ObjectID\",\n\t\t\t\tmap[string][]ObjectID{\"Z\": oids},\n\t\t\t\tbuildDocumentArray(func(doc []byte) []byte {\n\t\t\t\t\tdoc = bsoncore.AppendObjectIDElement(doc, \"0\", oids[0])\n\t\t\t\t\tdoc = bsoncore.AppendObjectIDElement(doc, \"1\", oids[1])\n\t\t\t\t\treturn bsoncore.AppendObjectIDElement(doc, \"2\", oids[2])\n\t\t\t\t}),\n\t\t\t\tnil,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"map[string][]json.Number(int64)\",\n\t\t\t\tmap[string][]json.Number{\"Z\": {json.Number(\"5\"), json.Number(\"10\")}},\n\t\t\t\tbuildDocumentArray(func(doc []byte) []byte {\n\t\t\t\t\tdoc = bsoncore.AppendInt64Element(doc, \"0\", 5)\n\t\t\t\t\treturn bsoncore.AppendInt64Element(doc, \"1\", 10)\n\t\t\t\t}),\n\t\t\t\tnil,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"map[string][]json.Number(float64)\",\n\t\t\t\tmap[string][]json.Number{\"Z\": {json.Number(\"5\"), json.Number(\"10.1\")}},\n\t\t\t\tbuildDocumentArray(func(doc []byte) []byte {\n\t\t\t\t\tdoc = bsoncore.AppendInt64Element(doc, \"0\", 5)\n\t\t\t\t\treturn bsoncore.AppendDoubleElement(doc, \"1\", 10.1)\n\t\t\t\t}),\n\t\t\t\tnil,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"map[string][]*url.URL\",\n\t\t\t\tmap[string][]*url.URL{\"Z\": {murl}},\n\t\t\t\tbuildDocumentArray(func(doc []byte) []byte {\n\t\t\t\t\treturn bsoncore.AppendStringElement(doc, \"0\", murl.String())\n\t\t\t\t}),\n\t\t\t\tnil,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"map[string][]Decimal128\",\n\t\t\t\tmap[string][]Decimal128{\"Z\": {decimal128}},\n\t\t\t\tbuildDocumentArray(func(doc []byte) []byte {\n\t\t\t\t\treturn bsoncore.AppendDecimal128Element(doc, \"0\", decimal128.h, decimal128.l)\n\t\t\t\t}),\n\t\t\t\tnil,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"-\",\n\t\t\t\tstruct {\n\t\t\t\t\tA string `bson:\"-\"`\n\t\t\t\t}{\n\t\t\t\t\tA: \"\",\n\t\t\t\t},\n\t\t\t\t[]byte{0x05, 0x00, 0x00, 0x00, 0x00},\n\t\t\t\tnil,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"omitempty\",\n\t\t\t\tstruct {\n\t\t\t\t\tA string `bson:\",omitempty\"`\n\t\t\t\t}{\n\t\t\t\t\tA: \"\",\n\t\t\t\t},\n\t\t\t\t[]byte{0x05, 0x00, 0x00, 0x00, 0x00},\n\t\t\t\tnil,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"omitempty, empty time\",\n\t\t\t\tstruct {\n\t\t\t\t\tA time.Time `bson:\",omitempty\"`\n\t\t\t\t}{\n\t\t\t\t\tA: time.Time{},\n\t\t\t\t},\n\t\t\t\t[]byte{0x05, 0x00, 0x00, 0x00, 0x00},\n\t\t\t\tnil,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"no private fields\",\n\t\t\t\tnoPrivateFields{a: \"should be empty\"},\n\t\t\t\t[]byte{0x05, 0x00, 0x00, 0x00, 0x00},\n\t\t\t\tnil,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"minsize\",\n\t\t\t\tstruct {\n\t\t\t\t\tA int64 `bson:\",minsize\"`\n\t\t\t\t}{\n\t\t\t\t\tA: 12345,\n\t\t\t\t},\n\t\t\t\tbuildDocument(bsoncore.AppendInt32Element(nil, \"a\", 12345)),\n\t\t\t\tnil,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"inline\",\n\t\t\t\tstruct {\n\t\t\t\t\tFoo struct {\n\t\t\t\t\t\tA int64 `bson:\",minsize\"`\n\t\t\t\t\t} `bson:\",inline\"`\n\t\t\t\t}{\n\t\t\t\t\tFoo: struct {\n\t\t\t\t\t\tA int64 `bson:\",minsize\"`\n\t\t\t\t\t}{\n\t\t\t\t\t\tA: 12345,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tbuildDocument(bsoncore.AppendInt32Element(nil, \"a\", 12345)),\n\t\t\t\tnil,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"inline struct pointer\",\n\t\t\t\tstruct {\n\t\t\t\t\tFoo *struct {\n\t\t\t\t\t\tA int64 `bson:\",minsize\"`\n\t\t\t\t\t} `bson:\",inline\"`\n\t\t\t\t\tBar *struct {\n\t\t\t\t\t\tB int64\n\t\t\t\t\t} `bson:\",inline\"`\n\t\t\t\t}{\n\t\t\t\t\tFoo: &struct {\n\t\t\t\t\t\tA int64 `bson:\",minsize\"`\n\t\t\t\t\t}{\n\t\t\t\t\t\tA: 12345,\n\t\t\t\t\t},\n\t\t\t\t\tBar: nil,\n\t\t\t\t},\n\t\t\t\tbuildDocument(bsoncore.AppendInt32Element(nil, \"a\", 12345)),\n\t\t\t\tnil,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"nested inline struct pointer\",\n\t\t\t\tstruct {\n\t\t\t\t\tFoo *struct {\n\t\t\t\t\t\tBar *struct {\n\t\t\t\t\t\t\tA int64 `bson:\",minsize\"`\n\t\t\t\t\t\t} `bson:\",inline\"`\n\t\t\t\t\t} `bson:\",inline\"`\n\t\t\t\t}{\n\t\t\t\t\tFoo: &struct {\n\t\t\t\t\t\tBar *struct {\n\t\t\t\t\t\t\tA int64 `bson:\",minsize\"`\n\t\t\t\t\t\t} `bson:\",inline\"`\n\t\t\t\t\t}{\n\t\t\t\t\t\tBar: &struct {\n\t\t\t\t\t\t\tA int64 `bson:\",minsize\"`\n\t\t\t\t\t\t}{\n\t\t\t\t\t\t\tA: 12345,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tbuildDocument(bsoncore.AppendInt32Element(nil, \"a\", 12345)),\n\t\t\t\tnil,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"inline nil struct pointer\",\n\t\t\t\tstruct {\n\t\t\t\t\tFoo *struct {\n\t\t\t\t\t\tA int64 `bson:\",minsize\"`\n\t\t\t\t\t} `bson:\",inline\"`\n\t\t\t\t}{\n\t\t\t\t\tFoo: nil,\n\t\t\t\t},\n\t\t\t\tbuildDocument([]byte{}),\n\t\t\t\tnil,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"inline overwrite\",\n\t\t\t\tstruct {\n\t\t\t\t\tFoo struct {\n\t\t\t\t\t\tA int32\n\t\t\t\t\t\tB string\n\t\t\t\t\t} `bson:\",inline\"`\n\t\t\t\t\tA int64\n\t\t\t\t}{\n\t\t\t\t\tFoo: struct {\n\t\t\t\t\t\tA int32\n\t\t\t\t\t\tB string\n\t\t\t\t\t}{\n\t\t\t\t\t\tA: 0,\n\t\t\t\t\t\tB: \"foo\",\n\t\t\t\t\t},\n\t\t\t\t\tA: 54321,\n\t\t\t\t},\n\t\t\t\tbuildDocument(func(doc []byte) []byte {\n\t\t\t\t\tdoc = bsoncore.AppendStringElement(doc, \"b\", \"foo\")\n\t\t\t\t\tdoc = bsoncore.AppendInt64Element(doc, \"a\", 54321)\n\t\t\t\t\treturn doc\n\t\t\t\t}(nil)),\n\t\t\t\tnil,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"inline overwrite respects ordering\",\n\t\t\t\tstruct {\n\t\t\t\t\tA   int64\n\t\t\t\t\tFoo struct {\n\t\t\t\t\t\tA int32\n\t\t\t\t\t\tB string\n\t\t\t\t\t} `bson:\",inline\"`\n\t\t\t\t}{\n\t\t\t\t\tA: 54321,\n\t\t\t\t\tFoo: struct {\n\t\t\t\t\t\tA int32\n\t\t\t\t\t\tB string\n\t\t\t\t\t}{\n\t\t\t\t\t\tA: 0,\n\t\t\t\t\t\tB: \"foo\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tbuildDocument(func(doc []byte) []byte {\n\t\t\t\t\tdoc = bsoncore.AppendInt64Element(doc, \"a\", 54321)\n\t\t\t\t\tdoc = bsoncore.AppendStringElement(doc, \"b\", \"foo\")\n\t\t\t\t\treturn doc\n\t\t\t\t}(nil)),\n\t\t\t\tnil,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"inline overwrite with nested structs\",\n\t\t\t\tstruct {\n\t\t\t\t\tFoo struct {\n\t\t\t\t\t\tA int32\n\t\t\t\t\t} `bson:\",inline\"`\n\t\t\t\t\tBar struct {\n\t\t\t\t\t\tA int32\n\t\t\t\t\t} `bson:\",inline\"`\n\t\t\t\t\tA int64\n\t\t\t\t}{\n\t\t\t\t\tFoo: struct {\n\t\t\t\t\t\tA int32\n\t\t\t\t\t}{},\n\t\t\t\t\tBar: struct {\n\t\t\t\t\t\tA int32\n\t\t\t\t\t}{},\n\t\t\t\t\tA: 54321,\n\t\t\t\t},\n\t\t\t\tbuildDocument(bsoncore.AppendInt64Element(nil, \"a\", 54321)),\n\t\t\t\tnil,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"inline map\",\n\t\t\t\tstruct {\n\t\t\t\t\tFoo map[string]string `bson:\",inline\"`\n\t\t\t\t}{\n\t\t\t\t\tFoo: map[string]string{\"foo\": \"bar\"},\n\t\t\t\t},\n\t\t\t\tbuildDocument(bsoncore.AppendStringElement(nil, \"foo\", \"bar\")),\n\t\t\t\tnil,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"alternate name bson:name\",\n\t\t\t\tstruct {\n\t\t\t\t\tA string `bson:\"foo\"`\n\t\t\t\t}{\n\t\t\t\t\tA: \"bar\",\n\t\t\t\t},\n\t\t\t\tbuildDocument(bsoncore.AppendStringElement(nil, \"foo\", \"bar\")),\n\t\t\t\tnil,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"alternate name\",\n\t\t\t\tstruct {\n\t\t\t\t\tA string `bson:\"foo\"`\n\t\t\t\t}{\n\t\t\t\t\tA: \"bar\",\n\t\t\t\t},\n\t\t\t\tbuildDocument(bsoncore.AppendStringElement(nil, \"foo\", \"bar\")),\n\t\t\t\tnil,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"inline, omitempty\",\n\t\t\t\tstruct {\n\t\t\t\t\tA   string\n\t\t\t\t\tFoo zeroTest `bson:\"omitempty,inline\"`\n\t\t\t\t}{\n\t\t\t\t\tA:   \"bar\",\n\t\t\t\t\tFoo: zeroTest{true},\n\t\t\t\t},\n\t\t\t\tbuildDocument(bsoncore.AppendStringElement(nil, \"a\", \"bar\")),\n\t\t\t\tnil,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"struct{}\",\n\t\t\t\tstruct {\n\t\t\t\t\tA bool\n\t\t\t\t\tB int32\n\t\t\t\t\tC int64\n\t\t\t\t\tD uint16\n\t\t\t\t\tE uint64\n\t\t\t\t\tF float64\n\t\t\t\t\tG string\n\t\t\t\t\tH map[string]string\n\t\t\t\t\tI []byte\n\t\t\t\t\tK [2]string\n\t\t\t\t\tL struct {\n\t\t\t\t\t\tM string\n\t\t\t\t\t}\n\t\t\t\t\tQ  ObjectID\n\t\t\t\t\tT  []struct{}\n\t\t\t\t\tY  json.Number\n\t\t\t\t\tZ  time.Time\n\t\t\t\t\tAA json.Number\n\t\t\t\t\tAB *url.URL\n\t\t\t\t\tAC Decimal128\n\t\t\t\t\tAD *time.Time\n\t\t\t\t\tAE testValueMarshaler\n\t\t\t\t\tAF map[string]any\n\t\t\t\t\tAG CodeWithScope\n\t\t\t\t}{\n\t\t\t\t\tA: true,\n\t\t\t\t\tB: 123,\n\t\t\t\t\tC: 456,\n\t\t\t\t\tD: 789,\n\t\t\t\t\tE: 101112,\n\t\t\t\t\tF: 3.14159,\n\t\t\t\t\tG: \"Hello, world\",\n\t\t\t\t\tH: map[string]string{\"foo\": \"bar\"},\n\t\t\t\t\tI: []byte{0x01, 0x02, 0x03},\n\t\t\t\t\tK: [2]string{\"baz\", \"qux\"},\n\t\t\t\t\tL: struct {\n\t\t\t\t\t\tM string\n\t\t\t\t\t}{\n\t\t\t\t\t\tM: \"foobar\",\n\t\t\t\t\t},\n\t\t\t\t\tQ:  oid,\n\t\t\t\t\tT:  nil,\n\t\t\t\t\tY:  json.Number(\"5\"),\n\t\t\t\t\tZ:  now,\n\t\t\t\t\tAA: json.Number(\"10.1\"),\n\t\t\t\t\tAB: murl,\n\t\t\t\t\tAC: decimal128,\n\t\t\t\t\tAD: &now,\n\t\t\t\t\tAE: testValueMarshaler{t: TypeString, buf: bsoncore.AppendString(nil, \"hello, world\")},\n\t\t\t\t\tAF: nil,\n\t\t\t\t\tAG: CodeWithScope{Code: \"var hello = 'world';\", Scope: D{{\"pi\", 3.14159}}},\n\t\t\t\t},\n\t\t\t\tbuildDocument(func(doc []byte) []byte {\n\t\t\t\t\tdoc = bsoncore.AppendBooleanElement(doc, \"a\", true)\n\t\t\t\t\tdoc = bsoncore.AppendInt32Element(doc, \"b\", 123)\n\t\t\t\t\tdoc = bsoncore.AppendInt64Element(doc, \"c\", 456)\n\t\t\t\t\tdoc = bsoncore.AppendInt32Element(doc, \"d\", 789)\n\t\t\t\t\tdoc = bsoncore.AppendInt64Element(doc, \"e\", 101112)\n\t\t\t\t\tdoc = bsoncore.AppendDoubleElement(doc, \"f\", 3.14159)\n\t\t\t\t\tdoc = bsoncore.AppendStringElement(doc, \"g\", \"Hello, world\")\n\t\t\t\t\tdoc = bsoncore.AppendDocumentElement(doc, \"h\", buildDocument(bsoncore.AppendStringElement(nil, \"foo\", \"bar\")))\n\t\t\t\t\tdoc = bsoncore.AppendBinaryElement(doc, \"i\", 0x00, []byte{0x01, 0x02, 0x03})\n\t\t\t\t\tdoc = bsoncore.AppendArrayElement(doc, \"k\",\n\t\t\t\t\t\tbuildArray(bsoncore.AppendStringElement(bsoncore.AppendStringElement(nil, \"0\", \"baz\"), \"1\", \"qux\")),\n\t\t\t\t\t)\n\t\t\t\t\tdoc = bsoncore.AppendDocumentElement(doc, \"l\", buildDocument(bsoncore.AppendStringElement(nil, \"m\", \"foobar\")))\n\t\t\t\t\tdoc = bsoncore.AppendObjectIDElement(doc, \"q\", oid)\n\t\t\t\t\tdoc = bsoncore.AppendNullElement(doc, \"t\")\n\t\t\t\t\tdoc = bsoncore.AppendInt64Element(doc, \"y\", 5)\n\t\t\t\t\tdoc = bsoncore.AppendDateTimeElement(doc, \"z\", now.UnixNano()/int64(time.Millisecond))\n\t\t\t\t\tdoc = bsoncore.AppendDoubleElement(doc, \"aa\", 10.1)\n\t\t\t\t\tdoc = bsoncore.AppendStringElement(doc, \"ab\", murl.String())\n\t\t\t\t\tdoc = bsoncore.AppendDecimal128Element(doc, \"ac\", decimal128.h, decimal128.l)\n\t\t\t\t\tdoc = bsoncore.AppendDateTimeElement(doc, \"ad\", now.UnixNano()/int64(time.Millisecond))\n\t\t\t\t\tdoc = bsoncore.AppendStringElement(doc, \"ae\", \"hello, world\")\n\t\t\t\t\tdoc = bsoncore.AppendNullElement(doc, \"af\")\n\t\t\t\t\tdoc = bsoncore.AppendCodeWithScopeElement(doc, \"ag\",\n\t\t\t\t\t\t\"var hello = 'world';\", buildDocument(bsoncore.AppendDoubleElement(nil, \"pi\", 3.14159)),\n\t\t\t\t\t)\n\t\t\t\t\treturn doc\n\t\t\t\t}(nil)),\n\t\t\t\tnil,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"struct{[]any}\",\n\t\t\t\tstruct {\n\t\t\t\t\tA []bool\n\t\t\t\t\tB []int32\n\t\t\t\t\tC []int64\n\t\t\t\t\tD []uint16\n\t\t\t\t\tE []uint64\n\t\t\t\t\tF []float64\n\t\t\t\t\tG []string\n\t\t\t\t\tH []map[string]string\n\t\t\t\t\tI [][]byte\n\t\t\t\t\tK [1][2]string\n\t\t\t\t\tL []struct {\n\t\t\t\t\t\tM string\n\t\t\t\t\t}\n\t\t\t\t\tN  [][]string\n\t\t\t\t\tR  []ObjectID\n\t\t\t\t\tT  []struct{}\n\t\t\t\t\tW  []map[string]struct{}\n\t\t\t\t\tX  []map[string]struct{}\n\t\t\t\t\tY  []map[string]struct{}\n\t\t\t\t\tZ  []time.Time\n\t\t\t\t\tAA []json.Number\n\t\t\t\t\tAB []*url.URL\n\t\t\t\t\tAC []Decimal128\n\t\t\t\t\tAD []*time.Time\n\t\t\t\t\tAE []testValueMarshaler\n\t\t\t\t}{\n\t\t\t\t\tA: []bool{true},\n\t\t\t\t\tB: []int32{123},\n\t\t\t\t\tC: []int64{456},\n\t\t\t\t\tD: []uint16{789},\n\t\t\t\t\tE: []uint64{101112},\n\t\t\t\t\tF: []float64{3.14159},\n\t\t\t\t\tG: []string{\"Hello, world\"},\n\t\t\t\t\tH: []map[string]string{{\"foo\": \"bar\"}},\n\t\t\t\t\tI: [][]byte{{0x01, 0x02, 0x03}},\n\t\t\t\t\tK: [1][2]string{{\"baz\", \"qux\"}},\n\t\t\t\t\tL: []struct {\n\t\t\t\t\t\tM string\n\t\t\t\t\t}{\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tM: \"foobar\",\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\tN:  [][]string{{\"foo\", \"bar\"}},\n\t\t\t\t\tR:  oids,\n\t\t\t\t\tT:  nil,\n\t\t\t\t\tW:  nil,\n\t\t\t\t\tX:  []map[string]struct{}{},   // Should be empty BSON Array\n\t\t\t\t\tY:  []map[string]struct{}{{}}, // Should be BSON array with one element, an empty BSON SubDocument\n\t\t\t\t\tZ:  []time.Time{now, now},\n\t\t\t\t\tAA: []json.Number{json.Number(\"5\"), json.Number(\"10.1\")},\n\t\t\t\t\tAB: []*url.URL{murl},\n\t\t\t\t\tAC: []Decimal128{decimal128},\n\t\t\t\t\tAD: []*time.Time{&now, &now},\n\t\t\t\t\tAE: []testValueMarshaler{\n\t\t\t\t\t\t{t: TypeString, buf: bsoncore.AppendString(nil, \"hello\")},\n\t\t\t\t\t\t{t: TypeString, buf: bsoncore.AppendString(nil, \"world\")},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tbuildDocument(func(doc []byte) []byte {\n\t\t\t\t\tdoc = appendArrayElement(doc, \"a\", bsoncore.AppendBooleanElement(nil, \"0\", true))\n\t\t\t\t\tdoc = appendArrayElement(doc, \"b\", bsoncore.AppendInt32Element(nil, \"0\", 123))\n\t\t\t\t\tdoc = appendArrayElement(doc, \"c\", bsoncore.AppendInt64Element(nil, \"0\", 456))\n\t\t\t\t\tdoc = appendArrayElement(doc, \"d\", bsoncore.AppendInt32Element(nil, \"0\", 789))\n\t\t\t\t\tdoc = appendArrayElement(doc, \"e\", bsoncore.AppendInt64Element(nil, \"0\", 101112))\n\t\t\t\t\tdoc = appendArrayElement(doc, \"f\", bsoncore.AppendDoubleElement(nil, \"0\", 3.14159))\n\t\t\t\t\tdoc = appendArrayElement(doc, \"g\", bsoncore.AppendStringElement(nil, \"0\", \"Hello, world\"))\n\t\t\t\t\tdoc = appendArrayElement(doc, \"h\", bsoncore.BuildDocumentElement(nil, \"0\", bsoncore.AppendStringElement(nil, \"foo\", \"bar\")))\n\t\t\t\t\tdoc = appendArrayElement(doc, \"i\", bsoncore.AppendBinaryElement(nil, \"0\", 0x00, []byte{0x01, 0x02, 0x03}))\n\t\t\t\t\tdoc = appendArrayElement(doc, \"k\",\n\t\t\t\t\t\tappendArrayElement(nil, \"0\",\n\t\t\t\t\t\t\tbsoncore.AppendStringElement(bsoncore.AppendStringElement(nil, \"0\", \"baz\"), \"1\", \"qux\")),\n\t\t\t\t\t)\n\t\t\t\t\tdoc = appendArrayElement(doc, \"l\", bsoncore.BuildDocumentElement(nil, \"0\", bsoncore.AppendStringElement(nil, \"m\", \"foobar\")))\n\t\t\t\t\tdoc = appendArrayElement(doc, \"n\",\n\t\t\t\t\t\tappendArrayElement(nil, \"0\",\n\t\t\t\t\t\t\tbsoncore.AppendStringElement(bsoncore.AppendStringElement(nil, \"0\", \"foo\"), \"1\", \"bar\")),\n\t\t\t\t\t)\n\t\t\t\t\tdoc = appendArrayElement(doc, \"r\",\n\t\t\t\t\t\tbsoncore.AppendObjectIDElement(\n\t\t\t\t\t\t\tbsoncore.AppendObjectIDElement(\n\t\t\t\t\t\t\t\tbsoncore.AppendObjectIDElement(nil,\n\t\t\t\t\t\t\t\t\t\"0\", oids[0]),\n\t\t\t\t\t\t\t\t\"1\", oids[1]),\n\t\t\t\t\t\t\t\"2\", oids[2]),\n\t\t\t\t\t)\n\t\t\t\t\tdoc = bsoncore.AppendNullElement(doc, \"t\")\n\t\t\t\t\tdoc = bsoncore.AppendNullElement(doc, \"w\")\n\t\t\t\t\tdoc = appendArrayElement(doc, \"x\", nil)\n\t\t\t\t\tdoc = appendArrayElement(doc, \"y\", bsoncore.BuildDocumentElement(nil, \"0\", nil))\n\t\t\t\t\tdoc = appendArrayElement(doc, \"z\",\n\t\t\t\t\t\tbsoncore.AppendDateTimeElement(\n\t\t\t\t\t\t\tbsoncore.AppendDateTimeElement(\n\t\t\t\t\t\t\t\tnil, \"0\", now.UnixNano()/int64(time.Millisecond)),\n\t\t\t\t\t\t\t\"1\", now.UnixNano()/int64(time.Millisecond)),\n\t\t\t\t\t)\n\t\t\t\t\tdoc = appendArrayElement(doc, \"aa\", bsoncore.AppendDoubleElement(bsoncore.AppendInt64Element(nil, \"0\", 5), \"1\", 10.10))\n\t\t\t\t\tdoc = appendArrayElement(doc, \"ab\", bsoncore.AppendStringElement(nil, \"0\", murl.String()))\n\t\t\t\t\tdoc = appendArrayElement(doc, \"ac\", bsoncore.AppendDecimal128Element(nil, \"0\", decimal128.h, decimal128.l))\n\t\t\t\t\tdoc = appendArrayElement(doc, \"ad\",\n\t\t\t\t\t\tbsoncore.AppendDateTimeElement(\n\t\t\t\t\t\t\tbsoncore.AppendDateTimeElement(nil, \"0\", now.UnixNano()/int64(time.Millisecond)),\n\t\t\t\t\t\t\t\"1\", now.UnixNano()/int64(time.Millisecond)),\n\t\t\t\t\t)\n\t\t\t\t\tdoc = appendArrayElement(doc, \"ae\",\n\t\t\t\t\t\tbsoncore.AppendStringElement(bsoncore.AppendStringElement(nil, \"0\", \"hello\"), \"1\", \"world\"),\n\t\t\t\t\t)\n\t\t\t\t\treturn doc\n\t\t\t\t}(nil)),\n\t\t\t\tnil,\n\t\t\t},\n\t\t}\n\n\t\tfor _, tc := range testCases {\n\t\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\t\tb := make(sliceWriter, 0, 512)\n\t\t\t\tvw := NewDocumentWriter(&b)\n\t\t\t\treg := buildDefaultRegistry()\n\t\t\t\tenc, err := reg.LookupEncoder(reflect.TypeOf(tc.value))\n\t\t\t\tnoerr(t, err)\n\t\t\t\terr = enc.EncodeValue(EncodeContext{Registry: reg}, vw, reflect.ValueOf(tc.value))\n\t\t\t\tif !errors.Is(err, tc.err) {\n\t\t\t\t\tt.Errorf(\"Did not receive expected error. got %v; want %v\", err, tc.err)\n\t\t\t\t}\n\t\t\t\tif diff := cmp.Diff([]byte(b), tc.b); diff != \"\" {\n\t\t\t\t\tt.Errorf(\"Bytes written differ: (-got +want)\\n%s\", diff)\n\t\t\t\t\tt.Errorf(\"Bytes\\ngot: %v\\nwant:%v\\n\", b, tc.b)\n\t\t\t\t\tt.Errorf(\"Readers\\ngot: %v\\nwant:%v\\n\", bsoncore.Document(b), bsoncore.Document(tc.b))\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"error path\", func(t *testing.T) {\n\t\ttestCases := []struct {\n\t\t\tname  string\n\t\t\tvalue any\n\t\t\terr   error\n\t\t}{\n\t\t\t{\n\t\t\t\t\"duplicate name struct\",\n\t\t\t\tstruct {\n\t\t\t\t\tA int64\n\t\t\t\t\tB int64 `bson:\"a\"`\n\t\t\t\t}{\n\t\t\t\t\tA: 0,\n\t\t\t\t\tB: 54321,\n\t\t\t\t},\n\t\t\t\tfmt.Errorf(\"duplicated key a\"),\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"inline map\",\n\t\t\t\tstruct {\n\t\t\t\t\tFoo map[string]string `bson:\",inline\"`\n\t\t\t\t\tBaz string\n\t\t\t\t}{\n\t\t\t\t\tFoo: map[string]string{\"baz\": \"bar\"},\n\t\t\t\t\tBaz: \"hi\",\n\t\t\t\t},\n\t\t\t\tfmt.Errorf(\"Key baz of inlined map conflicts with a struct field name\"),\n\t\t\t},\n\t\t}\n\n\t\tfor _, tc := range testCases {\n\t\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\t\tb := make(sliceWriter, 0, 512)\n\t\t\t\tvw := NewDocumentWriter(&b)\n\t\t\t\treg := buildDefaultRegistry()\n\t\t\t\tenc, err := reg.LookupEncoder(reflect.TypeOf(tc.value))\n\t\t\t\tnoerr(t, err)\n\t\t\t\terr = enc.EncodeValue(EncodeContext{Registry: reg}, vw, reflect.ValueOf(tc.value))\n\t\t\t\tif err == nil || !strings.Contains(err.Error(), tc.err.Error()) {\n\t\t\t\t\tt.Errorf(\"Did not receive expected error. got %v; want %v\", err, tc.err)\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"EmptyInterfaceEncodeValue/nil\", func(t *testing.T) {\n\t\tval := reflect.New(tEmpty).Elem()\n\t\tllvrw := new(valueReaderWriter)\n\t\terr := (&emptyInterfaceCodec{}).EncodeValue(EncodeContext{Registry: newTestRegistry()}, llvrw, val)\n\t\tnoerr(t, err)\n\t\tif llvrw.invoked != writeNull {\n\t\t\tt.Errorf(\"Incorrect method called. got %v; want %v\", llvrw.invoked, writeNull)\n\t\t}\n\t})\n\n\tt.Run(\"EmptyInterfaceEncodeValue/LookupEncoder error\", func(t *testing.T) {\n\t\tval := reflect.New(tEmpty).Elem()\n\t\tval.Set(reflect.ValueOf(int64(1234567890)))\n\t\tllvrw := new(valueReaderWriter)\n\t\tgot := (&emptyInterfaceCodec{}).EncodeValue(EncodeContext{Registry: newTestRegistry()}, llvrw, val)\n\t\twant := errNoEncoder{Type: tInt64}\n\t\tif !assert.CompareErrors(got, want) {\n\t\t\tt.Errorf(\"Did not receive expected error. got %v; want %v\", got, want)\n\t\t}\n\t})\n}\n\ntype testValueMarshalPtr struct {\n\tt   Type\n\tbuf []byte\n\terr error\n}\n\nfunc (tvm *testValueMarshalPtr) MarshalBSONValue() (byte, []byte, error) {\n\treturn byte(tvm.t), tvm.buf, tvm.err\n}\n\ntype testMarshalPtr struct {\n\tbuf []byte\n\terr error\n}\n\nfunc (tvm *testMarshalPtr) MarshalBSON() ([]byte, error) {\n\treturn tvm.buf, tvm.err\n}\n"
  },
  {
    "path": "bson/doc.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\n// Package bson is a library for reading, writing, and manipulating BSON. BSON is a binary serialization\n// format used to store documents and make remote procedure calls in MongoDB. For more information about\n// the Go BSON library, including usage examples, check out the [Work with BSON] page in the Go Driver\n// docs site. For more information about BSON, see https://bsonspec.org.\n//\n// # Native Go Types\n//\n// The [D] and [M] types defined in this package can be used to build representations of BSON using native Go types. D is a\n// slice and M is a map. For more information about the use cases for these types, see the documentation on the type\n// definitions.\n//\n// Note that a D should not be constructed with duplicate key names, as that can cause undefined server behavior.\n//\n// Example:\n//\n//\tbson.D{{\"foo\", \"bar\"}, {\"hello\", \"world\"}, {\"pi\", 3.14159}}\n//\tbson.M{\"foo\": \"bar\", \"hello\": \"world\", \"pi\": 3.14159}\n//\n// When decoding BSON to a D or M, the following type mappings apply when unmarshaling:\n//\n//  1. BSON int32 unmarshals to an int32.\n//  2. BSON int64 unmarshals to an int64.\n//  3. BSON double unmarshals to a float64.\n//  4. BSON string unmarshals to a string.\n//  5. BSON boolean unmarshals to a bool.\n//  6. BSON embedded document unmarshals to the parent type (i.e. D for a D, M for an M).\n//  7. BSON array unmarshals to a bson.A.\n//  8. BSON ObjectId unmarshals to a bson.ObjectID.\n//  9. BSON datetime unmarshals to a bson.DateTime.\n//  10. BSON binary unmarshals to a bson.Binary.\n//  11. BSON regular expression unmarshals to a bson.Regex.\n//  12. BSON JavaScript unmarshals to a bson.JavaScript.\n//  13. BSON code with scope unmarshals to a bson.CodeWithScope.\n//  14. BSON timestamp unmarshals to an bson.Timestamp.\n//  15. BSON 128-bit decimal unmarshals to an bson.Decimal128.\n//  16. BSON min key unmarshals to an bson.MinKey.\n//  17. BSON max key unmarshals to an bson.MaxKey.\n//  18. BSON undefined unmarshals to a bson.Undefined.\n//  19. BSON null unmarshals to nil.\n//  20. BSON DBPointer unmarshals to a bson.DBPointer.\n//  21. BSON symbol unmarshals to a bson.Symbol.\n//\n// The above mappings also apply when marshaling a D or M to BSON. Some other useful marshaling mappings are:\n//\n//  1. time.Time marshals to a BSON datetime.\n//  2. int8, int16, and int32 marshal to a BSON int32.\n//  3. int marshals to a BSON int32 if the value is between math.MinInt32 and math.MaxInt32, inclusive, and a BSON int64\n//     otherwise.\n//  4. int64 marshals to BSON int64 (unless [Encoder.IntMinSize] is set).\n//  5. uint8 and uint16 marshal to a BSON int32.\n//  6. uint, uint32, and uint64 marshal to a BSON int64 (unless [Encoder.IntMinSize] is set).\n//  7. BSON null and undefined values will unmarshal into the zero value of a field (e.g. unmarshaling a BSON null or\n//     undefined value into a string will yield the empty string.).\n//\n// # Structs\n//\n// Structs can be marshaled/unmarshaled to/from BSON or Extended JSON. When transforming structs to/from BSON or Extended\n// JSON, the following rules apply:\n//\n//  1. Only exported fields in structs will be marshaled or unmarshaled.\n//\n//  2. When marshaling a struct, each field will be lowercased to generate the key for the corresponding BSON element.\n//     For example, a struct field named \"Foo\" will generate key \"foo\". This can be overridden via a struct tag (e.g.\n//     `bson:\"fooField\"` to generate key \"fooField\" instead).\n//\n//  3. An embedded struct field is marshaled as a subdocument. The key will be the lowercased name of the field's type.\n//\n//  4. A pointer field is marshaled as the underlying type if the pointer is non-nil. If the pointer is nil, it is\n//     marshaled as a BSON null value.\n//\n//  5. When unmarshaling, a field of type any will follow the D/M type mappings listed above. BSON documents\n//     unmarshaled into an any field will be unmarshaled as a D.\n//\n// The encoding of each struct field can be customized by the \"bson\" struct tag. The \"bson\" tag gives the name of the\n// field, followed by a comma-separated list of options. The name may be omitted in order to specify options without\n// overriding the default field name. The following options can be used to configure behavior:\n//\n//  1. omitempty: If the \"omitempty\" struct tag is specified on a field, the field will not be marshaled if it is set to\n//     an \"empty\" value. Numbers, booleans, and strings are considered empty if their value is equal to the zero value for\n//     the type (i.e. 0 for numbers, false for booleans, and \"\" for strings). Slices, maps, and arrays are considered\n//     empty if they are of length zero. Interfaces and pointers are considered empty if their value is nil. By default,\n//     structs are only considered empty if the struct type implements [Zeroer] and the \"IsZero\"\n//     method returns true. Struct types that do not implement [Zeroer] are never considered empty and will be\n//     marshaled as embedded documents. NOTE: It is recommended that this tag be used for all slice and map fields.\n//\n//  2. minsize: If the minsize struct tag is specified on a field of type int64, uint, uint32, or uint64 and the value of\n//     the field can fit in a signed int32, the field will be serialized as a BSON int32 rather than a BSON int64. For\n//     other types, this tag is ignored.\n//\n//  3. truncate: If the truncate struct tag is specified on a field with a non-float numeric type, BSON doubles\n//     unmarshaled into that field will be truncated at the decimal point. For example, if 3.14 is unmarshaled into a\n//     field of type int, it will be unmarshaled as 3. If this tag is not specified, the decoder will throw an error if\n//     the value cannot be decoded without losing precision. For float64 or non-numeric types, this tag is ignored.\n//\n//  4. inline: If the inline struct tag is specified for a struct or map field, the field will be \"flattened\" when\n//     marshaling and \"un-flattened\" when unmarshaling. This means that all of the fields in that struct/map will be\n//     pulled up one level and will become top-level fields rather than being fields in a nested document. For example,\n//     if a map field named \"Map\" with value map[string]any{\"foo\": \"bar\"} is inlined, the resulting document will\n//     be {\"foo\": \"bar\"} instead of {\"map\": {\"foo\": \"bar\"}}. There can only be one inlined map field in a struct. If\n//     there are duplicated fields in the resulting document when an inlined struct is marshaled, the inlined field will\n//     be overwritten. If there are duplicated fields in the resulting document when an inlined map is marshaled, an\n//     error will be returned. This tag can be used with fields that are pointers to structs. If an inlined pointer field\n//     is nil, it will not be marshaled. For fields that are not maps or structs, this tag is ignored.\n//\n// # Raw BSON\n//\n// The Raw family of types is used to validate and retrieve elements from a slice of bytes. This\n// type is most useful when you want do lookups on BSON bytes without unmarshaling it into another\n// type.\n//\n// Example:\n//\n//\tvar raw bson.Raw = ... // bytes from somewhere\n//\terr := raw.Validate()\n//\tif err != nil { return err }\n//\tval := raw.Lookup(\"foo\")\n//\ti32, ok := val.Int32OK()\n//\t// do something with i32...\n//\n// # Custom Registry\n//\n// The Go BSON library uses a [Registry] to define encoding and decoding behavior for different data types.\n// The default encoding and decoding behavior can be customized or extended by using a modified Registry.\n// The custom registry system is composed of two parts:\n//\n// 1) [ValueEncoder] and [ValueDecoder] that handle encoding and decoding Go values to and from BSON\n// representations.\n//\n// 2) A [Registry] that holds these ValueEncoders and ValueDecoders and provides methods for\n// retrieving them.\n//\n// To use a custom Registry, use [Encoder.SetRegistry] or [Decoder.SetRegistry].\n//\n// [Work with BSON]: https://www.mongodb.com/docs/drivers/go/current/fundamentals/bson/\npackage bson\n"
  },
  {
    "path": "bson/empty_interface_codec.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bson\n\nimport (\n\t\"reflect\"\n)\n\n// emptyInterfaceCodec is the Codec used for any values.\ntype emptyInterfaceCodec struct {\n\t// decodeBinaryAsSlice causes DecodeValue to unmarshal BSON binary field values that are the\n\t// \"Generic\" or \"Old\" BSON binary subtype as a Go byte slice instead of a Binary.\n\tdecodeBinaryAsSlice bool\n}\n\n// Assert that emptyInterfaceCodec satisfies the typeDecoder interface, which allows it\n// to be used by collection type decoders (e.g. map, slice, etc) to set individual values in a\n// collection.\nvar _ typeDecoder = &emptyInterfaceCodec{}\n\n// EncodeValue is the ValueEncoderFunc for any.\nfunc (eic *emptyInterfaceCodec) EncodeValue(ec EncodeContext, vw ValueWriter, val reflect.Value) error {\n\tif !val.IsValid() || val.Type() != tEmpty {\n\t\treturn ValueEncoderError{Name: \"EmptyInterfaceEncodeValue\", Types: []reflect.Type{tEmpty}, Received: val}\n\t}\n\n\tif val.IsNil() {\n\t\treturn vw.WriteNull()\n\t}\n\tencoder, err := ec.LookupEncoder(val.Elem().Type())\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn encoder.EncodeValue(ec, vw, val.Elem())\n}\n\nfunc (eic *emptyInterfaceCodec) getEmptyInterfaceDecodeType(dc DecodeContext, valueType Type) (reflect.Type, error) {\n\tisDocument := valueType == Type(0) || valueType == TypeEmbeddedDocument\n\tif isDocument {\n\t\tif dc.defaultDocumentType != nil {\n\t\t\t// If the bsontype is an embedded document and the DocumentType is set on the DecodeContext, then return\n\t\t\t// that type.\n\t\t\treturn dc.defaultDocumentType, nil\n\t\t}\n\t}\n\n\trtype, err := dc.LookupTypeMapEntry(valueType)\n\tif err == nil {\n\t\treturn rtype, nil\n\t}\n\n\tif isDocument {\n\t\t// For documents, fallback to looking up a type map entry for Type(0) or TypeEmbeddedDocument,\n\t\t// depending on the original valueType.\n\t\tvar lookupType Type\n\t\tswitch valueType {\n\t\tcase Type(0):\n\t\t\tlookupType = TypeEmbeddedDocument\n\t\tcase TypeEmbeddedDocument:\n\t\t\tlookupType = Type(0)\n\t\t}\n\n\t\trtype, err = dc.LookupTypeMapEntry(lookupType)\n\t\tif err == nil {\n\t\t\treturn rtype, nil\n\t\t}\n\t\t// fallback to bson.D\n\t\treturn tD, nil\n\t}\n\n\treturn nil, err\n}\n\nfunc (eic *emptyInterfaceCodec) decodeType(dc DecodeContext, vr ValueReader, t reflect.Type) (reflect.Value, error) {\n\tif t != tEmpty {\n\t\treturn emptyValue, ValueDecoderError{Name: \"EmptyInterfaceDecodeValue\", Types: []reflect.Type{tEmpty}, Received: reflect.Zero(t)}\n\t}\n\n\trtype, err := eic.getEmptyInterfaceDecodeType(dc, vr.Type())\n\tif err != nil {\n\t\tswitch vr.Type() {\n\t\tcase TypeNull:\n\t\t\treturn reflect.Zero(t), vr.ReadNull()\n\t\tdefault:\n\t\t\treturn emptyValue, err\n\t\t}\n\t}\n\n\tdecoder, err := dc.LookupDecoder(rtype)\n\tif err != nil {\n\t\treturn emptyValue, err\n\t}\n\n\telem, err := decodeTypeOrValueWithInfo(decoder, dc, vr, rtype)\n\tif err != nil {\n\t\treturn emptyValue, err\n\t}\n\n\tif (eic.decodeBinaryAsSlice || dc.binaryAsSlice) && rtype == tBinary {\n\t\tbinElem := elem.Interface().(Binary)\n\t\tif binElem.Subtype == TypeBinaryGeneric || binElem.Subtype == TypeBinaryBinaryOld {\n\t\t\telem = reflect.ValueOf(binElem.Data)\n\t\t}\n\t}\n\n\treturn elem, nil\n}\n\n// DecodeValue is the ValueDecoderFunc for any.\nfunc (eic *emptyInterfaceCodec) DecodeValue(dc DecodeContext, vr ValueReader, val reflect.Value) error {\n\tif !val.CanSet() || val.Type() != tEmpty {\n\t\treturn ValueDecoderError{Name: \"EmptyInterfaceDecodeValue\", Types: []reflect.Type{tEmpty}, Received: val}\n\t}\n\n\telem, err := eic.decodeType(dc, vr, val.Type())\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tval.Set(elem)\n\treturn nil\n}\n"
  },
  {
    "path": "bson/encoder.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bson\n\nimport (\n\t\"reflect\"\n\t\"sync\"\n)\n\n// This pool is used to keep the allocations of Encoders down. This is only used for the Marshal*\n// methods and is not consumable from outside of this package. The Encoders retrieved from this pool\n// must have both Reset and SetRegistry called on them.\nvar encPool = sync.Pool{\n\tNew: func() any {\n\t\treturn new(Encoder)\n\t},\n}\n\n// An Encoder writes a serialization format to an output stream. It writes to a ValueWriter\n// as the destination of BSON data.\ntype Encoder struct {\n\tec EncodeContext\n\tvw ValueWriter\n}\n\n// NewEncoder returns a new encoder that writes to vw.\nfunc NewEncoder(vw ValueWriter) *Encoder {\n\treturn &Encoder{\n\t\tec: EncodeContext{Registry: defaultRegistry},\n\t\tvw: vw,\n\t}\n}\n\n// Encode writes the BSON encoding of val to the stream.\n//\n// See [Marshal] for details about BSON marshaling behavior.\nfunc (e *Encoder) Encode(val any) error {\n\tif marshaler, ok := val.(Marshaler); ok {\n\t\t// TODO(skriptble): Should we have a MarshalAppender interface so that we can have []byte reuse?\n\t\tbuf, err := marshaler.MarshalBSON()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn copyDocumentFromBytes(e.vw, buf)\n\t}\n\n\tencoder, err := e.ec.LookupEncoder(reflect.TypeOf(val))\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn encoder.EncodeValue(e.ec, e.vw, reflect.ValueOf(val))\n}\n\n// Reset will reset the state of the Encoder, using the same *EncodeContext used in\n// the original construction but using vw.\nfunc (e *Encoder) Reset(vw ValueWriter) {\n\te.vw = vw\n}\n\n// SetRegistry replaces the current registry of the Encoder with r.\nfunc (e *Encoder) SetRegistry(r *Registry) {\n\te.ec.Registry = r\n}\n\n// ErrorOnInlineDuplicates causes the Encoder to return an error if there is a duplicate field in\n// the marshaled BSON when the \"inline\" struct tag option is set.\nfunc (e *Encoder) ErrorOnInlineDuplicates() {\n\te.ec.errorOnInlineDuplicates = true\n}\n\n// IntMinSize causes the Encoder to marshal Go integer values (int, int8, int16, int32, int64, uint,\n// uint8, uint16, uint32, or uint64) as the minimum BSON int size (either 32 or 64 bits) that can\n// represent the integer value.\nfunc (e *Encoder) IntMinSize() {\n\te.ec.minSize = true\n}\n\n// StringifyMapKeysWithFmt causes the Encoder to convert Go map keys to BSON document field name\n// strings using fmt.Sprint instead of the default string conversion logic.\nfunc (e *Encoder) StringifyMapKeysWithFmt() {\n\te.ec.stringifyMapKeysWithFmt = true\n}\n\n// NilMapAsEmpty causes the Encoder to marshal nil Go maps as empty BSON documents instead of BSON\n// null.\nfunc (e *Encoder) NilMapAsEmpty() {\n\te.ec.nilMapAsEmpty = true\n}\n\n// NilSliceAsEmpty causes the Encoder to marshal nil Go slices as empty BSON arrays instead of BSON\n// null.\nfunc (e *Encoder) NilSliceAsEmpty() {\n\te.ec.nilSliceAsEmpty = true\n}\n\n// NilByteSliceAsEmpty causes the Encoder to marshal nil Go byte slices as empty BSON binary values\n// instead of BSON null.\nfunc (e *Encoder) NilByteSliceAsEmpty() {\n\te.ec.nilByteSliceAsEmpty = true\n}\n\n// TODO(GODRIVER-2820): Update the description to remove the note about only examining exported\n// TODO struct fields once the logic is updated to also inspect private struct fields.\n\n// OmitZeroStruct causes the Encoder to consider the zero value for a struct (e.g. MyStruct{})\n// as empty and omit it from the marshaled BSON when the \"omitempty\" struct tag option is set\n// or the OmitEmpty() method is called.\n//\n// Note that the Encoder only examines exported struct fields when determining if a struct is the\n// zero value. It considers pointers to a zero struct value (e.g. &MyStruct{}) not empty.\nfunc (e *Encoder) OmitZeroStruct() {\n\te.ec.omitZeroStruct = true\n}\n\n// OmitEmpty causes the Encoder to omit empty values from the marshaled BSON as the \"omitempty\"\n// struct tag option is set.\nfunc (e *Encoder) OmitEmpty() {\n\te.ec.omitEmpty = true\n}\n\n// UseJSONStructTags causes the Encoder to fall back to using the \"json\" struct tag if a \"bson\"\n// struct tag is not specified.\nfunc (e *Encoder) UseJSONStructTags() {\n\te.ec.useJSONStructTags = true\n}\n"
  },
  {
    "path": "bson/encoder_example_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2023-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bson_test\n\nimport (\n\t\"bytes\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n)\n\nfunc ExampleEncoder() {\n\t// Create an Encoder that writes BSON values to a bytes.Buffer.\n\tbuf := new(bytes.Buffer)\n\tvw := bson.NewDocumentWriter(buf)\n\tencoder := bson.NewEncoder(vw)\n\n\ttype Product struct {\n\t\tName  string `bson:\"name\"`\n\t\tSKU   string `bson:\"sku\"`\n\t\tPrice int64  `bson:\"price_cents\"`\n\t}\n\n\t// Use the Encoder to marshal a BSON document that contains the name, SKU,\n\t// and price (in cents) of a product.\n\tproduct := Product{\n\t\tName:  \"Cereal Rounds\",\n\t\tSKU:   \"AB12345\",\n\t\tPrice: 399,\n\t}\n\terr := encoder.Encode(product)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t// Print the BSON document as Extended JSON by converting it to bson.Raw.\n\tfmt.Println(bson.Raw(buf.Bytes()).String())\n\t// Output: {\"name\": \"Cereal Rounds\",\"sku\": \"AB12345\",\"price_cents\": {\"$numberLong\":\"399\"}}\n}\n\ntype CityState struct {\n\tCity  string\n\tState string\n}\n\nfunc (k CityState) String() string {\n\treturn fmt.Sprintf(\"%s, %s\", k.City, k.State)\n}\n\nfunc ExampleEncoder_StringifyMapKeysWithFmt() {\n\t// Create an Encoder that writes BSON values to a bytes.Buffer.\n\tbuf := new(bytes.Buffer)\n\tvw := bson.NewDocumentWriter(buf)\n\tencoder := bson.NewEncoder(vw)\n\n\t// Configure the Encoder to convert Go map keys to BSON document field names\n\t// using fmt.Sprintf instead of the default string conversion logic.\n\tencoder.StringifyMapKeysWithFmt()\n\n\t// Use the Encoder to marshal a BSON document that contains is a map of\n\t// city and state to a list of zip codes in that city.\n\tzipCodes := map[CityState][]int{\n\t\t{City: \"New York\", State: \"NY\"}: {10001, 10301, 10451},\n\t}\n\terr := encoder.Encode(zipCodes)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t// Print the BSON document as Extended JSON by converting it to bson.Raw.\n\tfmt.Println(bson.Raw(buf.Bytes()).String())\n\t// Output: {\"New York, NY\": [{\"$numberInt\":\"10001\"},{\"$numberInt\":\"10301\"},{\"$numberInt\":\"10451\"}]}\n}\n\nfunc ExampleEncoder_UseJSONStructTags() {\n\t// Create an Encoder that writes BSON values to a bytes.Buffer.\n\tbuf := new(bytes.Buffer)\n\tvw := bson.NewDocumentWriter(buf)\n\tencoder := bson.NewEncoder(vw)\n\n\ttype Product struct {\n\t\tName  string `json:\"name\"`\n\t\tSKU   string `json:\"sku\"`\n\t\tPrice int64  `json:\"price_cents\"`\n\t}\n\n\t// Configure the Encoder to use \"json\" struct tags when decoding if \"bson\"\n\t// struct tags are not present.\n\tencoder.UseJSONStructTags()\n\n\t// Use the Encoder to marshal a BSON document that contains the name, SKU,\n\t// and price (in cents) of a product.\n\tproduct := Product{\n\t\tName:  \"Cereal Rounds\",\n\t\tSKU:   \"AB12345\",\n\t\tPrice: 399,\n\t}\n\terr := encoder.Encode(product)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t// Print the BSON document as Extended JSON by converting it to bson.Raw.\n\tfmt.Println(bson.Raw(buf.Bytes()).String())\n\t// Output: {\"name\": \"Cereal Rounds\",\"sku\": \"AB12345\",\"price_cents\": {\"$numberLong\":\"399\"}}\n}\n\nfunc ExampleEncoder_multipleBSONDocuments() {\n\t// Create an Encoder that writes BSON values to a bytes.Buffer.\n\tbuf := new(bytes.Buffer)\n\tvw := bson.NewDocumentWriter(buf)\n\tencoder := bson.NewEncoder(vw)\n\n\ttype Coordinate struct {\n\t\tX int\n\t\tY int\n\t}\n\n\t// Use the encoder to marshal 5 Coordinate values as a sequence of BSON\n\t// documents.\n\tfor i := 0; i < 5; i++ {\n\t\terr := encoder.Encode(Coordinate{\n\t\t\tX: i,\n\t\t\tY: i + 1,\n\t\t})\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t}\n\n\t// Read each marshaled BSON document from the buffer and print them as\n\t// Extended JSON by converting them to bson.Raw.\n\tfor {\n\t\tdoc, err := bson.ReadDocument(buf)\n\t\tif errors.Is(err, io.EOF) {\n\t\t\treturn\n\t\t}\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t\tfmt.Println(doc.String())\n\t}\n\t// Output:\n\t// {\"x\": {\"$numberInt\":\"0\"},\"y\": {\"$numberInt\":\"1\"}}\n\t// {\"x\": {\"$numberInt\":\"1\"},\"y\": {\"$numberInt\":\"2\"}}\n\t// {\"x\": {\"$numberInt\":\"2\"},\"y\": {\"$numberInt\":\"3\"}}\n\t// {\"x\": {\"$numberInt\":\"3\"},\"y\": {\"$numberInt\":\"4\"}}\n\t// {\"x\": {\"$numberInt\":\"4\"},\"y\": {\"$numberInt\":\"5\"}}\n}\n\nfunc ExampleEncoder_extendedJSON() {\n\t// Create an Encoder that writes canonical Extended JSON values to a\n\t// bytes.Buffer.\n\tbuf := new(bytes.Buffer)\n\tvw := bson.NewExtJSONValueWriter(buf, true, false)\n\tencoder := bson.NewEncoder(vw)\n\n\ttype Product struct {\n\t\tName  string `bson:\"name\"`\n\t\tSKU   string `bson:\"sku\"`\n\t\tPrice int64  `bson:\"price_cents\"`\n\t}\n\n\t// Use the Encoder to marshal a BSON document that contains the name, SKU,\n\t// and price (in cents) of a product.\n\tproduct := Product{\n\t\tName:  \"Cereal Rounds\",\n\t\tSKU:   \"AB12345\",\n\t\tPrice: 399,\n\t}\n\terr := encoder.Encode(product)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(buf.String())\n\t// Output: {\"name\":\"Cereal Rounds\",\"sku\":\"AB12345\",\"price_cents\":{\"$numberLong\":\"399\"}}\n}\n\nfunc ExampleEncoder_multipleExtendedJSONDocuments() {\n\t// Create an Encoder that writes canonical Extended JSON values to a\n\t// bytes.Buffer.\n\tbuf := new(bytes.Buffer)\n\tvw := bson.NewExtJSONValueWriter(buf, true, false)\n\tencoder := bson.NewEncoder(vw)\n\n\ttype Coordinate struct {\n\t\tX int\n\t\tY int\n\t}\n\n\t// Use the encoder to marshal 5 Coordinate values as a sequence of Extended\n\t// JSON documents.\n\tfor i := 0; i < 5; i++ {\n\t\terr := encoder.Encode(Coordinate{\n\t\t\tX: i,\n\t\t\tY: i + 1,\n\t\t})\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t}\n\n\tfmt.Println(buf.String())\n\t// Output:\n\t// {\"x\":{\"$numberInt\":\"0\"},\"y\":{\"$numberInt\":\"1\"}}\n\t// {\"x\":{\"$numberInt\":\"1\"},\"y\":{\"$numberInt\":\"2\"}}\n\t// {\"x\":{\"$numberInt\":\"2\"},\"y\":{\"$numberInt\":\"3\"}}\n\t// {\"x\":{\"$numberInt\":\"3\"},\"y\":{\"$numberInt\":\"4\"}}\n\t// {\"x\":{\"$numberInt\":\"4\"},\"y\":{\"$numberInt\":\"5\"}}\n}\n\nfunc ExampleEncoder_IntMinSize() {\n\t// Create an encoder that will marshal integers as the minimum BSON int size\n\t// (either 32 or 64 bits) that can represent the integer value.\n\ttype foo struct {\n\t\tBar uint32\n\t}\n\n\tbuf := new(bytes.Buffer)\n\tvw := bson.NewDocumentWriter(buf)\n\n\tenc := bson.NewEncoder(vw)\n\tenc.IntMinSize()\n\n\terr := enc.Encode(foo{2})\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfmt.Println(bson.Raw(buf.Bytes()).String())\n\t// Output:\n\t// {\"bar\": {\"$numberInt\":\"2\"}}\n}\n"
  },
  {
    "path": "bson/encoder_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bson\n\nimport (\n\t\"bytes\"\n\t\"errors\"\n\t\"reflect\"\n\t\"testing\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/require\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n)\n\nfunc TestBasicEncode(t *testing.T) {\n\tfor _, tc := range marshalingTestCases {\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\tgot := make(sliceWriter, 0, 1024)\n\t\t\tvw := NewDocumentWriter(&got)\n\t\t\treg := defaultRegistry\n\t\t\tencoder, err := reg.LookupEncoder(reflect.TypeOf(tc.val))\n\t\t\tnoerr(t, err)\n\t\t\terr = encoder.EncodeValue(EncodeContext{Registry: reg}, vw, reflect.ValueOf(tc.val))\n\t\t\tnoerr(t, err)\n\n\t\t\tif !bytes.Equal(got, tc.want) {\n\t\t\t\tt.Errorf(\"Bytes are not equal. got %v; want %v\", got, tc.want)\n\t\t\t\tt.Errorf(\"Bytes:\\n%v\\n%v\", got, tc.want)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestEncoderEncode(t *testing.T) {\n\tfor _, tc := range marshalingTestCases {\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\tgot := make(sliceWriter, 0, 1024)\n\t\t\tvw := NewDocumentWriter(&got)\n\t\t\tenc := NewEncoder(vw)\n\t\t\terr := enc.Encode(tc.val)\n\t\t\tnoerr(t, err)\n\n\t\t\tif !bytes.Equal(got, tc.want) {\n\t\t\t\tt.Errorf(\"Bytes are not equal. got %v; want %v\", got, tc.want)\n\t\t\t\tt.Errorf(\"Bytes:\\n%v\\n%v\", got, tc.want)\n\t\t\t}\n\t\t})\n\t}\n\n\tt.Run(\"Marshaler\", func(t *testing.T) {\n\t\ttestCases := []struct {\n\t\t\tname    string\n\t\t\tbuf     []byte\n\t\t\terr     error\n\t\t\twanterr error\n\t\t\tvw      ValueWriter\n\t\t}{\n\t\t\t{\n\t\t\t\t\"error\",\n\t\t\t\tnil,\n\t\t\t\terrors.New(\"Marshaler error\"),\n\t\t\t\terrors.New(\"Marshaler error\"),\n\t\t\t\t&valueReaderWriter{},\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"copy error\",\n\t\t\t\t[]byte{0x05, 0x00, 0x00, 0x00, 0x00},\n\t\t\t\tnil,\n\t\t\t\terrors.New(\"copy error\"),\n\t\t\t\t&valueReaderWriter{Err: errors.New(\"copy error\"), ErrAfter: writeDocument},\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"success\",\n\t\t\t\t[]byte{0x07, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00},\n\t\t\t\tnil,\n\t\t\t\tnil,\n\t\t\t\tnil,\n\t\t\t},\n\t\t}\n\n\t\tfor _, tc := range testCases {\n\t\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\t\tmarshaler := testMarshaler{buf: tc.buf, err: tc.err}\n\n\t\t\t\tvar vw ValueWriter\n\t\t\t\tb := make(sliceWriter, 0, 100)\n\t\t\t\tcompareVW := false\n\t\t\t\tif tc.vw != nil {\n\t\t\t\t\tvw = tc.vw\n\t\t\t\t} else {\n\t\t\t\t\tcompareVW = true\n\t\t\t\t\tvw = NewDocumentWriter(&b)\n\t\t\t\t}\n\t\t\t\tenc := NewEncoder(vw)\n\t\t\t\tgot := enc.Encode(marshaler)\n\t\t\t\twant := tc.wanterr\n\t\t\t\tif !assert.CompareErrors(got, want) {\n\t\t\t\t\tt.Errorf(\"Did not receive expected error. got %v; want %v\", got, want)\n\t\t\t\t}\n\t\t\t\tif compareVW {\n\t\t\t\t\tbuf := b\n\t\t\t\t\tif !bytes.Equal(buf, tc.buf) {\n\t\t\t\t\t\tt.Errorf(\"Copied bytes do not match. got %v; want %v\", buf, tc.buf)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t})\n}\n\ntype testMarshaler struct {\n\tbuf []byte\n\terr error\n}\n\nfunc (tm testMarshaler) MarshalBSON() ([]byte, error) { return tm.buf, tm.err }\n\nfunc docToBytes(d any) []byte {\n\tb, err := Marshal(d)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treturn b\n}\n\ntype stringerTest struct{}\n\nfunc (stringerTest) String() string {\n\treturn \"test key\"\n}\n\nfunc TestEncoderConfiguration(t *testing.T) {\n\ttype inlineDuplicateInner struct {\n\t\tDuplicate string\n\t}\n\n\ttype inlineDuplicateOuter struct {\n\t\tInline    inlineDuplicateInner `bson:\",inline\"`\n\t\tDuplicate string\n\t}\n\n\ttype zeroStruct struct {\n\t\tMyString string\n\t}\n\n\ttestCases := []struct {\n\t\tdescription string\n\t\tconfigure   func(*Encoder)\n\t\tinput       any\n\t\twant        []byte\n\t\twantErr     error\n\t}{\n\t\t// Test that ErrorOnInlineDuplicates causes the Encoder to return an error if there are any\n\t\t// duplicate fields in the marshaled document caused by using the \"inline\" struct tag.\n\t\t{\n\t\t\tdescription: \"ErrorOnInlineDuplicates\",\n\t\t\tconfigure: func(enc *Encoder) {\n\t\t\t\tenc.ErrorOnInlineDuplicates()\n\t\t\t},\n\t\t\tinput: inlineDuplicateOuter{\n\t\t\t\tInline:    inlineDuplicateInner{Duplicate: \"inner\"},\n\t\t\t\tDuplicate: \"outer\",\n\t\t\t},\n\t\t\twantErr: errors.New(\"struct bson.inlineDuplicateOuter has duplicated key duplicate\"),\n\t\t},\n\t\t// Test that IntMinSize encodes Go int and int64 values as BSON int32 if the value is small\n\t\t// enough.\n\t\t{\n\t\t\tdescription: \"IntMinSize\",\n\t\t\tconfigure: func(enc *Encoder) {\n\t\t\t\tenc.IntMinSize()\n\t\t\t},\n\t\t\tinput: D{\n\t\t\t\t{Key: \"myInt\", Value: int(1)},\n\t\t\t\t{Key: \"myInt64\", Value: int64(1)},\n\t\t\t\t{Key: \"myUint\", Value: uint(1)},\n\t\t\t\t{Key: \"myUint32\", Value: uint32(1)},\n\t\t\t\t{Key: \"myUint64\", Value: uint64(1)},\n\t\t\t},\n\t\t\twant: bsoncore.NewDocumentBuilder().\n\t\t\t\tAppendInt32(\"myInt\", 1).\n\t\t\t\tAppendInt32(\"myInt64\", 1).\n\t\t\t\tAppendInt32(\"myUint\", 1).\n\t\t\t\tAppendInt32(\"myUint32\", 1).\n\t\t\t\tAppendInt32(\"myUint64\", 1).\n\t\t\t\tBuild(),\n\t\t},\n\t\t// Test that StringifyMapKeysWithFmt uses fmt.Sprint to convert map keys to BSON field names.\n\t\t{\n\t\t\tdescription: \"StringifyMapKeysWithFmt\",\n\t\t\tconfigure: func(enc *Encoder) {\n\t\t\t\tenc.StringifyMapKeysWithFmt()\n\t\t\t},\n\t\t\tinput: map[stringerTest]string{\n\t\t\t\t{}: \"test value\",\n\t\t\t},\n\t\t\twant: bsoncore.NewDocumentBuilder().\n\t\t\t\tAppendString(\"test key\", \"test value\").\n\t\t\t\tBuild(),\n\t\t},\n\t\t// Test that NilMapAsEmpty encodes nil Go maps as empty BSON documents.\n\t\t{\n\t\t\tdescription: \"NilMapAsEmpty\",\n\t\t\tconfigure: func(enc *Encoder) {\n\t\t\t\tenc.NilMapAsEmpty()\n\t\t\t},\n\t\t\tinput: D{{Key: \"myMap\", Value: map[string]string(nil)}},\n\t\t\twant: bsoncore.NewDocumentBuilder().\n\t\t\t\tAppendDocument(\"myMap\", bsoncore.NewDocumentBuilder().Build()).\n\t\t\t\tBuild(),\n\t\t},\n\t\t// Test that NilSliceAsEmpty encodes nil Go slices as empty BSON arrays.\n\t\t{\n\t\t\tdescription: \"NilSliceAsEmpty\",\n\t\t\tconfigure: func(enc *Encoder) {\n\t\t\t\tenc.NilSliceAsEmpty()\n\t\t\t},\n\t\t\tinput: D{{Key: \"mySlice\", Value: []string(nil)}},\n\t\t\twant: bsoncore.NewDocumentBuilder().\n\t\t\t\tAppendArray(\"mySlice\", bsoncore.NewArrayBuilder().Build()).\n\t\t\t\tBuild(),\n\t\t},\n\t\t// Test that NilByteSliceAsEmpty encodes nil Go byte slices as empty BSON binary elements.\n\t\t{\n\t\t\tdescription: \"NilByteSliceAsEmpty\",\n\t\t\tconfigure: func(enc *Encoder) {\n\t\t\t\tenc.NilByteSliceAsEmpty()\n\t\t\t},\n\t\t\tinput: D{{Key: \"myBytes\", Value: []byte(nil)}},\n\t\t\twant: bsoncore.NewDocumentBuilder().\n\t\t\t\tAppendBinary(\"myBytes\", TypeBinaryGeneric, []byte{}).\n\t\t\t\tBuild(),\n\t\t},\n\t\t// Test that OmitZeroStruct omits empty structs from the marshaled document if the\n\t\t// \"omitempty\" struct tag is used.\n\t\t{\n\t\t\tdescription: \"OmitZeroStruct\",\n\t\t\tconfigure: func(enc *Encoder) {\n\t\t\t\tenc.OmitZeroStruct()\n\t\t\t},\n\t\t\tinput: struct {\n\t\t\t\tZero zeroStruct `bson:\",omitempty\"`\n\t\t\t}{},\n\t\t\twant: bsoncore.NewDocumentBuilder().Build(),\n\t\t},\n\t\t// Test that OmitZeroStruct omits empty structs from the marshaled document if\n\t\t// OmitEmpty is also set.\n\t\t{\n\t\t\tdescription: \"OmitEmpty with non-zeroer struct\",\n\t\t\tconfigure: func(enc *Encoder) {\n\t\t\t\tenc.OmitZeroStruct()\n\t\t\t\tenc.OmitEmpty()\n\t\t\t},\n\t\t\tinput: struct {\n\t\t\t\tZero zeroStruct\n\t\t\t}{},\n\t\t\twant: bsoncore.NewDocumentBuilder().Build(),\n\t\t},\n\t\t// Test that OmitEmpty omits empty values from the marshaled document.\n\t\t{\n\t\t\tdescription: \"OmitEmpty\",\n\t\t\tconfigure: func(enc *Encoder) {\n\t\t\t\tenc.OmitEmpty()\n\t\t\t},\n\t\t\tinput: struct {\n\t\t\t\tZero    zeroTest\n\t\t\t\tI64     int64\n\t\t\t\tF64     float64\n\t\t\t\tString  string\n\t\t\t\tBoolean bool\n\t\t\t\tSlice   []int\n\t\t\t\tArray   [0]int\n\t\t\t\tMap     map[string]int\n\t\t\t\tBytes   []byte\n\t\t\t\tTime    time.Time\n\t\t\t\tPointer *int\n\t\t\t}{\n\t\t\t\tZero: zeroTest{true},\n\t\t\t},\n\t\t\twant: bsoncore.NewDocumentBuilder().Build(),\n\t\t},\n\t\t// Test that UseJSONStructTags causes the Encoder to fall back to \"json\" struct tags if\n\t\t// \"bson\" struct tags are not available.\n\t\t{\n\t\t\tdescription: \"UseJSONStructTags\",\n\t\t\tconfigure: func(enc *Encoder) {\n\t\t\t\tenc.UseJSONStructTags()\n\t\t\t},\n\t\t\tinput: struct {\n\t\t\t\tStructFieldName string `json:\"jsonFieldName\"`\n\t\t\t}{\n\t\t\t\tStructFieldName: \"test value\",\n\t\t\t},\n\t\t\twant: bsoncore.NewDocumentBuilder().\n\t\t\t\tAppendString(\"jsonFieldName\", \"test value\").\n\t\t\t\tBuild(),\n\t\t},\n\t}\n\n\tfor _, tc := range testCases {\n\t\ttc := tc // Capture range variable.\n\n\t\tt.Run(tc.description, func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tgot := new(bytes.Buffer)\n\t\t\tvw := NewDocumentWriter(got)\n\t\t\tenc := NewEncoder(vw)\n\n\t\t\ttc.configure(enc)\n\n\t\t\terr := enc.Encode(tc.input)\n\t\t\tif tc.wantErr != nil {\n\t\t\t\tassert.Equal(t, tc.wantErr, err, \"expected and actual errors do not match\")\n\t\t\t\treturn\n\t\t\t}\n\t\t\trequire.NoError(t, err, \"Encode error\")\n\n\t\t\tassert.Equal(t, tc.want, got.Bytes(), \"expected and actual encoded BSON do not match\")\n\n\t\t\t// After we compare the raw bytes, also decode the expected and actual BSON as a bson.D\n\t\t\t// and compare them. The goal is to make assertion failures easier to debug because\n\t\t\t// binary diffs are very difficult to understand.\n\t\t\tvar wantDoc D\n\t\t\terr = Unmarshal(tc.want, &wantDoc)\n\t\t\trequire.NoError(t, err, \"Unmarshal error\")\n\t\t\tvar gotDoc D\n\t\t\terr = Unmarshal(got.Bytes(), &gotDoc)\n\t\t\trequire.NoError(t, err, \"Unmarshal error\")\n\n\t\t\tassert.Equal(t, wantDoc, gotDoc, \"expected and actual decoded documents do not match\")\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "bson/example_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2023-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bson_test\n\nimport (\n\t\"fmt\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n)\n\n// This example uses Raw to skip parsing a nested document in a BSON message.\nfunc ExampleRaw_unmarshal() {\n\tb, err := bson.Marshal(bson.M{\n\t\t\"Word\":     \"beach\",\n\t\t\"Synonyms\": bson.A{\"coast\", \"shore\", \"waterfront\"},\n\t})\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tvar res struct {\n\t\tWord     string\n\t\tSynonyms bson.Raw // Don't parse the whole list, we just want to count the elements.\n\t}\n\n\terr = bson.Unmarshal(b, &res)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\telems, err := res.Synonyms.Elements()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tfmt.Printf(\"%s, synonyms count: %d\\n\", res.Word, len(elems))\n\n\t// Output: beach, synonyms count: 3\n}\n\n// This example uses Raw to add a precomputed BSON document during marshal.\nfunc ExampleRaw_marshal() {\n\tprecomputed, err := bson.Marshal(bson.M{\"Precomputed\": true})\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tmsg := struct {\n\t\tMessage  string\n\t\tMetadata bson.Raw\n\t}{\n\t\tMessage:  \"Hello World!\",\n\t\tMetadata: precomputed,\n\t}\n\n\tb, err := bson.Marshal(msg)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\t// Print the Extended JSON by converting BSON to bson.Raw.\n\tfmt.Println(bson.Raw(b).String())\n\n\t// Output: {\"message\": \"Hello World!\",\"metadata\": {\"Precomputed\": true}}\n}\n\n// This example uses RawValue to delay parsing a value in a BSON message.\nfunc ExampleRawValue_unmarshal() {\n\tb1, err := bson.Marshal(bson.M{\n\t\t\"Format\":    \"UNIX\",\n\t\t\"Timestamp\": 1675282389,\n\t})\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tb2, err := bson.Marshal(bson.M{\n\t\t\"Format\":    \"RFC3339\",\n\t\t\"Timestamp\": time.Unix(1675282389, 0).Format(time.RFC3339),\n\t})\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfor _, b := range [][]byte{b1, b2} {\n\t\tvar res struct {\n\t\t\tFormat    string\n\t\t\tTimestamp bson.RawValue // Delay parsing until we know the timestamp format.\n\t\t}\n\n\t\terr = bson.Unmarshal(b, &res)\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\n\t\tvar t time.Time\n\t\tswitch res.Format {\n\t\tcase \"UNIX\":\n\t\t\tt = time.Unix(res.Timestamp.AsInt64(), 0)\n\t\tcase \"RFC3339\":\n\t\t\tt, err = time.Parse(time.RFC3339, res.Timestamp.StringValue())\n\t\t\tif err != nil {\n\t\t\t\tpanic(err)\n\t\t\t}\n\t\t}\n\t\tfmt.Println(res.Format, t.Unix())\n\t}\n\n\t// Output:\n\t// UNIX 1675282389\n\t// RFC3339 1675282389\n}\n\n// This example uses RawValue to add a precomputed BSON string value during marshal.\nfunc ExampleRawValue_marshal() {\n\tt, val, err := bson.MarshalValue(\"Precomputed message!\")\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tprecomputed := bson.RawValue{\n\t\tType:  t,\n\t\tValue: val,\n\t}\n\n\tmsg := struct {\n\t\tMessage bson.RawValue\n\t\tTime    time.Time\n\t}{\n\t\tMessage: precomputed,\n\t\tTime:    time.Unix(1675282389, 0),\n\t}\n\n\tb, err := bson.Marshal(msg)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\t// Print the Extended JSON by converting BSON to bson.Raw.\n\tfmt.Println(bson.Raw(b).String())\n\n\t// Output: {\"message\": \"Precomputed message!\",\"time\": {\"$date\":{\"$numberLong\":\"1675282389000\"}}}\n}\n"
  },
  {
    "path": "bson/extjson_parser.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bson\n\nimport (\n\t\"encoding/base64\"\n\t\"encoding/hex\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"strings\"\n)\n\nconst maxNestingDepth = 200\n\n// ErrInvalidJSON indicates the JSON input is invalid\nvar ErrInvalidJSON = errors.New(\"invalid JSON input\")\n\ntype jsonParseState byte\n\nconst (\n\tjpsStartState jsonParseState = iota\n\tjpsSawBeginObject\n\tjpsSawEndObject\n\tjpsSawBeginArray\n\tjpsSawEndArray\n\tjpsSawColon\n\tjpsSawComma\n\tjpsSawKey\n\tjpsSawValue\n\tjpsDoneState\n\tjpsInvalidState\n)\n\ntype jsonParseMode byte\n\nconst (\n\tjpmInvalidMode jsonParseMode = iota\n\tjpmObjectMode\n\tjpmArrayMode\n)\n\ntype extJSONValue struct {\n\tt Type\n\tv any\n}\n\ntype extJSONObject struct {\n\tkeys   []string\n\tvalues []*extJSONValue\n}\n\ntype extJSONParser struct {\n\tjs *jsonScanner\n\ts  jsonParseState\n\tm  []jsonParseMode\n\tk  string\n\tv  *extJSONValue\n\n\terr           error\n\tcanonicalOnly bool\n\tdepth         int\n\tmaxDepth      int\n\n\temptyObject bool\n\trelaxedUUID bool\n}\n\n// newExtJSONParser returns a new extended JSON parser, ready to to begin\n// parsing from the first character of the argued json input. It will not\n// perform any read-ahead and will therefore not report any errors about\n// malformed JSON at this point.\nfunc newExtJSONParser(r io.Reader, canonicalOnly bool) *extJSONParser {\n\treturn &extJSONParser{\n\t\tjs:            &jsonScanner{r: r},\n\t\ts:             jpsStartState,\n\t\tm:             []jsonParseMode{},\n\t\tcanonicalOnly: canonicalOnly,\n\t\tmaxDepth:      maxNestingDepth,\n\t}\n}\n\n// peekType examines the next value and returns its BSON Type\nfunc (ejp *extJSONParser) peekType() (Type, error) {\n\tvar t Type\n\tvar err error\n\tinitialState := ejp.s\n\n\tejp.advanceState()\n\tswitch ejp.s {\n\tcase jpsSawValue:\n\t\tt = ejp.v.t\n\tcase jpsSawBeginArray:\n\t\tt = TypeArray\n\tcase jpsInvalidState:\n\t\terr = ejp.err\n\tcase jpsSawComma:\n\t\t// in array mode, seeing a comma means we need to progress again to actually observe a type\n\t\tif ejp.peekMode() == jpmArrayMode {\n\t\t\treturn ejp.peekType()\n\t\t}\n\tcase jpsSawEndArray:\n\t\t// this would only be a valid state if we were in array mode, so return end-of-array error\n\t\terr = ErrEOA\n\tcase jpsSawBeginObject:\n\t\t// peek key to determine type\n\t\tejp.advanceState()\n\t\tswitch ejp.s {\n\t\tcase jpsSawEndObject: // empty embedded document\n\t\t\tt = TypeEmbeddedDocument\n\t\t\tejp.emptyObject = true\n\t\tcase jpsInvalidState:\n\t\t\terr = ejp.err\n\t\tcase jpsSawKey:\n\t\t\tif initialState == jpsStartState {\n\t\t\t\treturn TypeEmbeddedDocument, nil\n\t\t\t}\n\t\t\tt = wrapperKeyBSONType(ejp.k)\n\n\t\t\t// if $uuid is encountered, parse as binary subtype 4\n\t\t\tif ejp.k == \"$uuid\" {\n\t\t\t\tejp.relaxedUUID = true\n\t\t\t\tt = TypeBinary\n\t\t\t}\n\n\t\t\tswitch t {\n\t\t\tcase TypeJavaScript:\n\t\t\t\t// just saw $code, need to check for $scope at same level\n\t\t\t\t_, err = ejp.readValue(TypeJavaScript)\n\t\t\t\tif err != nil {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\n\t\t\t\tswitch ejp.s {\n\t\t\t\tcase jpsSawEndObject: // type is TypeJavaScript\n\t\t\t\tcase jpsSawComma:\n\t\t\t\t\tejp.advanceState()\n\n\t\t\t\t\tif ejp.s == jpsSawKey && ejp.k == \"$scope\" {\n\t\t\t\t\t\tt = TypeCodeWithScope\n\t\t\t\t\t} else {\n\t\t\t\t\t\terr = fmt.Errorf(\"invalid extended JSON: unexpected key %s in CodeWithScope object\", ejp.k)\n\t\t\t\t\t}\n\t\t\t\tcase jpsInvalidState:\n\t\t\t\t\terr = ejp.err\n\t\t\t\tdefault:\n\t\t\t\t\terr = ErrInvalidJSON\n\t\t\t\t}\n\t\t\tcase TypeCodeWithScope:\n\t\t\t\terr = errors.New(\"invalid extended JSON: code with $scope must contain $code before $scope\")\n\t\t\t}\n\t\t}\n\t}\n\n\treturn t, err\n}\n\n// readKey parses the next key and its type and returns them\nfunc (ejp *extJSONParser) readKey() (string, Type, error) {\n\tif ejp.emptyObject {\n\t\tejp.emptyObject = false\n\t\treturn \"\", 0, ErrEOD\n\t}\n\n\t// advance to key (or return with error)\n\tswitch ejp.s {\n\tcase jpsStartState:\n\t\tejp.advanceState()\n\t\tif ejp.s == jpsSawBeginObject {\n\t\t\tejp.advanceState()\n\t\t}\n\tcase jpsSawBeginObject:\n\t\tejp.advanceState()\n\tcase jpsSawValue, jpsSawEndObject, jpsSawEndArray:\n\t\tejp.advanceState()\n\t\tswitch ejp.s {\n\t\tcase jpsSawBeginObject, jpsSawComma:\n\t\t\tejp.advanceState()\n\t\tcase jpsSawEndObject:\n\t\t\treturn \"\", 0, ErrEOD\n\t\tcase jpsDoneState:\n\t\t\treturn \"\", 0, io.EOF\n\t\tcase jpsInvalidState:\n\t\t\treturn \"\", 0, ejp.err\n\t\tdefault:\n\t\t\treturn \"\", 0, ErrInvalidJSON\n\t\t}\n\tcase jpsSawKey: // do nothing (key was peeked before)\n\tdefault:\n\t\treturn \"\", 0, invalidRequestError(\"key\")\n\t}\n\n\t// read key\n\tvar key string\n\n\tswitch ejp.s {\n\tcase jpsSawKey:\n\t\tkey = ejp.k\n\tcase jpsSawEndObject:\n\t\treturn \"\", 0, ErrEOD\n\tcase jpsInvalidState:\n\t\treturn \"\", 0, ejp.err\n\tdefault:\n\t\treturn \"\", 0, invalidRequestError(\"key\")\n\t}\n\n\t// check for colon\n\tejp.advanceState()\n\tif err := ensureColon(ejp.s, key); err != nil {\n\t\treturn \"\", 0, err\n\t}\n\n\t// peek at the value to determine type\n\tt, err := ejp.peekType()\n\tif err != nil {\n\t\treturn \"\", 0, err\n\t}\n\n\treturn key, t, nil\n}\n\n// readValue returns the value corresponding to the Type returned by peekType\nfunc (ejp *extJSONParser) readValue(t Type) (*extJSONValue, error) {\n\tif ejp.s == jpsInvalidState {\n\t\treturn nil, ejp.err\n\t}\n\n\tvar v *extJSONValue\n\n\tswitch t {\n\tcase TypeNull, TypeBoolean, TypeString:\n\t\tif ejp.s != jpsSawValue {\n\t\t\treturn nil, invalidRequestError(t.String())\n\t\t}\n\t\tv = ejp.v\n\tcase TypeInt32, TypeInt64, TypeDouble:\n\t\t// relaxed version allows these to be literal number values\n\t\tif ejp.s == jpsSawValue {\n\t\t\tv = ejp.v\n\t\t\tbreak\n\t\t}\n\t\tfallthrough\n\tcase TypeDecimal128, TypeSymbol, TypeObjectID, TypeMinKey, TypeMaxKey, TypeUndefined:\n\t\tswitch ejp.s {\n\t\tcase jpsSawKey:\n\t\t\t// read colon\n\t\t\tejp.advanceState()\n\t\t\tif err := ensureColon(ejp.s, ejp.k); err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\n\t\t\t// read value\n\t\t\tejp.advanceState()\n\t\t\tif ejp.s != jpsSawValue || !ejp.ensureExtValueType(t) {\n\t\t\t\treturn nil, invalidJSONErrorForType(\"value\", t)\n\t\t\t}\n\n\t\t\tv = ejp.v\n\n\t\t\t// read end object\n\t\t\tejp.advanceState()\n\t\t\tif ejp.s != jpsSawEndObject {\n\t\t\t\treturn nil, invalidJSONErrorForType(\"} after value\", t)\n\t\t\t}\n\t\tdefault:\n\t\t\treturn nil, invalidRequestError(t.String())\n\t\t}\n\tcase TypeBinary, TypeRegex, TypeTimestamp, TypeDBPointer:\n\t\tif ejp.s != jpsSawKey {\n\t\t\treturn nil, invalidRequestError(t.String())\n\t\t}\n\t\t// read colon\n\t\tejp.advanceState()\n\t\tif err := ensureColon(ejp.s, ejp.k); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tejp.advanceState()\n\t\tif t == TypeBinary && ejp.s == jpsSawValue {\n\t\t\t// convert relaxed $uuid format\n\t\t\tif ejp.relaxedUUID {\n\t\t\t\tdefer func() { ejp.relaxedUUID = false }()\n\t\t\t\tuuid, err := ejp.v.parseSymbol()\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\n\t\t\t\t// RFC 4122 defines the length of a UUID as 36 and the hyphens in a UUID as appearing\n\t\t\t\t// in the 8th, 13th, 18th, and 23rd characters.\n\t\t\t\t//\n\t\t\t\t// See https://tools.ietf.org/html/rfc4122#section-3\n\t\t\t\tvalid := len(uuid) == 36 &&\n\t\t\t\t\tstring(uuid[8]) == \"-\" &&\n\t\t\t\t\tstring(uuid[13]) == \"-\" &&\n\t\t\t\t\tstring(uuid[18]) == \"-\" &&\n\t\t\t\t\tstring(uuid[23]) == \"-\"\n\t\t\t\tif !valid {\n\t\t\t\t\treturn nil, fmt.Errorf(\"$uuid value does not follow RFC 4122 format regarding length and hyphens\")\n\t\t\t\t}\n\n\t\t\t\t// remove hyphens\n\t\t\t\tuuidNoHyphens := strings.ReplaceAll(uuid, \"-\", \"\")\n\t\t\t\tif len(uuidNoHyphens) != 32 {\n\t\t\t\t\treturn nil, fmt.Errorf(\"$uuid value does not follow RFC 4122 format regarding length and hyphens\")\n\t\t\t\t}\n\n\t\t\t\t// convert hex to bytes\n\t\t\t\tbytes, err := hex.DecodeString(uuidNoHyphens)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, fmt.Errorf(\"$uuid value does not follow RFC 4122 format regarding hex bytes: %w\", err)\n\t\t\t\t}\n\n\t\t\t\tejp.advanceState()\n\t\t\t\tif ejp.s != jpsSawEndObject {\n\t\t\t\t\treturn nil, invalidJSONErrorForType(\"$uuid and value and then }\", TypeBinary)\n\t\t\t\t}\n\n\t\t\t\tbase64 := &extJSONValue{\n\t\t\t\t\tt: TypeString,\n\t\t\t\t\tv: base64.StdEncoding.EncodeToString(bytes),\n\t\t\t\t}\n\t\t\t\tsubType := &extJSONValue{\n\t\t\t\t\tt: TypeString,\n\t\t\t\t\tv: \"04\",\n\t\t\t\t}\n\n\t\t\t\tv = &extJSONValue{\n\t\t\t\t\tt: TypeEmbeddedDocument,\n\t\t\t\t\tv: &extJSONObject{\n\t\t\t\t\t\tkeys:   []string{\"base64\", \"subType\"},\n\t\t\t\t\t\tvalues: []*extJSONValue{base64, subType},\n\t\t\t\t\t},\n\t\t\t\t}\n\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\t// convert legacy $binary format\n\t\t\tbase64 := ejp.v\n\n\t\t\tejp.advanceState()\n\t\t\tif ejp.s != jpsSawComma {\n\t\t\t\treturn nil, invalidJSONErrorForType(\",\", TypeBinary)\n\t\t\t}\n\n\t\t\tejp.advanceState()\n\t\t\tkey, t, err := ejp.readKey()\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tif key != \"$type\" {\n\t\t\t\treturn nil, invalidJSONErrorForType(\"$type\", TypeBinary)\n\t\t\t}\n\n\t\t\tsubType, err := ejp.readValue(t)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\n\t\t\tejp.advanceState()\n\t\t\tif ejp.s != jpsSawEndObject {\n\t\t\t\treturn nil, invalidJSONErrorForType(\"2 key-value pairs and then }\", TypeBinary)\n\t\t\t}\n\n\t\t\tv = &extJSONValue{\n\t\t\t\tt: TypeEmbeddedDocument,\n\t\t\t\tv: &extJSONObject{\n\t\t\t\t\tkeys:   []string{\"base64\", \"subType\"},\n\t\t\t\t\tvalues: []*extJSONValue{base64, subType},\n\t\t\t\t},\n\t\t\t}\n\t\t\tbreak\n\t\t}\n\n\t\t// read KV pairs\n\t\tif ejp.s != jpsSawBeginObject {\n\t\t\treturn nil, invalidJSONErrorForType(\"{\", t)\n\t\t}\n\n\t\tkeys, vals, err := ejp.readObject(2, true)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tejp.advanceState()\n\t\tif ejp.s != jpsSawEndObject {\n\t\t\treturn nil, invalidJSONErrorForType(\"2 key-value pairs and then }\", t)\n\t\t}\n\n\t\tv = &extJSONValue{t: TypeEmbeddedDocument, v: &extJSONObject{keys: keys, values: vals}}\n\n\tcase TypeDateTime:\n\t\tswitch ejp.s {\n\t\tcase jpsSawValue:\n\t\t\tv = ejp.v\n\t\tcase jpsSawKey:\n\t\t\t// read colon\n\t\t\tejp.advanceState()\n\t\t\tif err := ensureColon(ejp.s, ejp.k); err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\n\t\t\tejp.advanceState()\n\t\t\tswitch ejp.s {\n\t\t\tcase jpsSawBeginObject:\n\t\t\t\tkeys, vals, err := ejp.readObject(1, true)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t\tv = &extJSONValue{t: TypeEmbeddedDocument, v: &extJSONObject{keys: keys, values: vals}}\n\t\t\tcase jpsSawValue:\n\t\t\t\tif ejp.canonicalOnly {\n\t\t\t\t\treturn nil, invalidJSONError(\"{\")\n\t\t\t\t}\n\t\t\t\tv = ejp.v\n\t\t\tdefault:\n\t\t\t\tif ejp.canonicalOnly {\n\t\t\t\t\treturn nil, invalidJSONErrorForType(\"object\", t)\n\t\t\t\t}\n\t\t\t\treturn nil, invalidJSONErrorForType(\"ISO-8601 Internet Date/Time Format as described in RFC-3339\", t)\n\t\t\t}\n\n\t\t\tejp.advanceState()\n\t\t\tif ejp.s != jpsSawEndObject {\n\t\t\t\treturn nil, invalidJSONErrorForType(\"value and then }\", t)\n\t\t\t}\n\t\tdefault:\n\t\t\treturn nil, invalidRequestError(t.String())\n\t\t}\n\tcase TypeJavaScript:\n\t\tswitch ejp.s {\n\t\tcase jpsSawKey:\n\t\t\t// read colon\n\t\t\tejp.advanceState()\n\t\t\tif err := ensureColon(ejp.s, ejp.k); err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\n\t\t\t// read value\n\t\t\tejp.advanceState()\n\t\t\tif ejp.s != jpsSawValue {\n\t\t\t\treturn nil, invalidJSONErrorForType(\"value\", t)\n\t\t\t}\n\t\t\tv = ejp.v\n\n\t\t\t// read end object or comma and just return\n\t\t\tejp.advanceState()\n\t\tcase jpsSawEndObject:\n\t\t\tv = ejp.v\n\t\tdefault:\n\t\t\treturn nil, invalidRequestError(t.String())\n\t\t}\n\tcase TypeCodeWithScope:\n\t\tif ejp.s == jpsSawKey && ejp.k == \"$scope\" {\n\t\t\tv = ejp.v // this is the $code string from earlier\n\n\t\t\t// read colon\n\t\t\tejp.advanceState()\n\t\t\tif err := ensureColon(ejp.s, ejp.k); err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\n\t\t\t// read {\n\t\t\tejp.advanceState()\n\t\t\tif ejp.s != jpsSawBeginObject {\n\t\t\t\treturn nil, invalidJSONError(\"$scope to be embedded document\")\n\t\t\t}\n\t\t} else {\n\t\t\treturn nil, invalidRequestError(t.String())\n\t\t}\n\tcase TypeEmbeddedDocument, TypeArray:\n\t\treturn nil, invalidRequestError(t.String())\n\t}\n\n\treturn v, nil\n}\n\n// readObject is a utility method for reading full objects of known (or expected) size\n// it is useful for extended JSON types such as binary, datetime, regex, and timestamp\nfunc (ejp *extJSONParser) readObject(numKeys int, started bool) ([]string, []*extJSONValue, error) {\n\tkeys := make([]string, numKeys)\n\tvals := make([]*extJSONValue, numKeys)\n\n\tif !started {\n\t\tejp.advanceState()\n\t\tif ejp.s != jpsSawBeginObject {\n\t\t\treturn nil, nil, invalidJSONError(\"{\")\n\t\t}\n\t}\n\n\tfor i := 0; i < numKeys; i++ {\n\t\tkey, t, err := ejp.readKey()\n\t\tif err != nil {\n\t\t\treturn nil, nil, err\n\t\t}\n\n\t\tswitch ejp.s {\n\t\tcase jpsSawKey:\n\t\t\tv, err := ejp.readValue(t)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, nil, err\n\t\t\t}\n\n\t\t\tkeys[i] = key\n\t\t\tvals[i] = v\n\t\tcase jpsSawValue:\n\t\t\tkeys[i] = key\n\t\t\tvals[i] = ejp.v\n\t\tdefault:\n\t\t\treturn nil, nil, invalidJSONError(\"value\")\n\t\t}\n\t}\n\n\tejp.advanceState()\n\tif ejp.s != jpsSawEndObject {\n\t\treturn nil, nil, invalidJSONError(\"}\")\n\t}\n\n\treturn keys, vals, nil\n}\n\n// advanceState reads the next JSON token from the scanner and transitions\n// from the current state based on that token's type\nfunc (ejp *extJSONParser) advanceState() {\n\tif ejp.s == jpsDoneState || ejp.s == jpsInvalidState {\n\t\treturn\n\t}\n\n\tjt, err := ejp.js.nextToken()\n\tif err != nil {\n\t\tejp.err = err\n\t\tejp.s = jpsInvalidState\n\t\treturn\n\t}\n\n\tvalid := ejp.validateToken(jt.t)\n\tif !valid {\n\t\tejp.err = unexpectedTokenError(jt)\n\t\tejp.s = jpsInvalidState\n\t\treturn\n\t}\n\n\tswitch jt.t {\n\tcase jttBeginObject:\n\t\tejp.s = jpsSawBeginObject\n\t\tejp.pushMode(jpmObjectMode)\n\t\tejp.depth++\n\n\t\tif ejp.depth > ejp.maxDepth {\n\t\t\tejp.err = nestingDepthError(jt.p, ejp.depth)\n\t\t\tejp.s = jpsInvalidState\n\t\t}\n\tcase jttEndObject:\n\t\tejp.s = jpsSawEndObject\n\t\tejp.depth--\n\n\t\tif ejp.popMode() != jpmObjectMode {\n\t\t\tejp.err = unexpectedTokenError(jt)\n\t\t\tejp.s = jpsInvalidState\n\t\t}\n\tcase jttBeginArray:\n\t\tejp.s = jpsSawBeginArray\n\t\tejp.pushMode(jpmArrayMode)\n\tcase jttEndArray:\n\t\tejp.s = jpsSawEndArray\n\n\t\tif ejp.popMode() != jpmArrayMode {\n\t\t\tejp.err = unexpectedTokenError(jt)\n\t\t\tejp.s = jpsInvalidState\n\t\t}\n\tcase jttColon:\n\t\tejp.s = jpsSawColon\n\tcase jttComma:\n\t\tejp.s = jpsSawComma\n\tcase jttEOF:\n\t\tejp.s = jpsDoneState\n\t\tif len(ejp.m) != 0 {\n\t\t\tejp.err = unexpectedTokenError(jt)\n\t\t\tejp.s = jpsInvalidState\n\t\t}\n\tcase jttString:\n\t\tswitch ejp.s {\n\t\tcase jpsSawComma:\n\t\t\tif ejp.peekMode() == jpmArrayMode {\n\t\t\t\tejp.s = jpsSawValue\n\t\t\t\tejp.v = extendJSONToken(jt)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tfallthrough\n\t\tcase jpsSawBeginObject:\n\t\t\tejp.s = jpsSawKey\n\t\t\tejp.k = jt.v.(string)\n\t\t\treturn\n\t\t}\n\t\tfallthrough\n\tdefault:\n\t\tejp.s = jpsSawValue\n\t\tejp.v = extendJSONToken(jt)\n\t}\n}\n\nvar jpsValidTransitionTokens = map[jsonParseState]map[jsonTokenType]bool{\n\tjpsStartState: {\n\t\tjttBeginObject: true,\n\t\tjttBeginArray:  true,\n\t\tjttInt32:       true,\n\t\tjttInt64:       true,\n\t\tjttDouble:      true,\n\t\tjttString:      true,\n\t\tjttBool:        true,\n\t\tjttNull:        true,\n\t\tjttEOF:         true,\n\t},\n\tjpsSawBeginObject: {\n\t\tjttEndObject: true,\n\t\tjttString:    true,\n\t},\n\tjpsSawEndObject: {\n\t\tjttEndObject: true,\n\t\tjttEndArray:  true,\n\t\tjttComma:     true,\n\t\tjttEOF:       true,\n\t},\n\tjpsSawBeginArray: {\n\t\tjttBeginObject: true,\n\t\tjttBeginArray:  true,\n\t\tjttEndArray:    true,\n\t\tjttInt32:       true,\n\t\tjttInt64:       true,\n\t\tjttDouble:      true,\n\t\tjttString:      true,\n\t\tjttBool:        true,\n\t\tjttNull:        true,\n\t},\n\tjpsSawEndArray: {\n\t\tjttEndObject: true,\n\t\tjttEndArray:  true,\n\t\tjttComma:     true,\n\t\tjttEOF:       true,\n\t},\n\tjpsSawColon: {\n\t\tjttBeginObject: true,\n\t\tjttBeginArray:  true,\n\t\tjttInt32:       true,\n\t\tjttInt64:       true,\n\t\tjttDouble:      true,\n\t\tjttString:      true,\n\t\tjttBool:        true,\n\t\tjttNull:        true,\n\t},\n\tjpsSawComma: {\n\t\tjttBeginObject: true,\n\t\tjttBeginArray:  true,\n\t\tjttInt32:       true,\n\t\tjttInt64:       true,\n\t\tjttDouble:      true,\n\t\tjttString:      true,\n\t\tjttBool:        true,\n\t\tjttNull:        true,\n\t},\n\tjpsSawKey: {\n\t\tjttColon: true,\n\t},\n\tjpsSawValue: {\n\t\tjttEndObject: true,\n\t\tjttEndArray:  true,\n\t\tjttComma:     true,\n\t\tjttEOF:       true,\n\t},\n\tjpsDoneState:    {},\n\tjpsInvalidState: {},\n}\n\nfunc (ejp *extJSONParser) validateToken(jtt jsonTokenType) bool {\n\tswitch ejp.s {\n\tcase jpsSawEndObject:\n\t\t// if we are at depth zero and the next token is a '{',\n\t\t// we can consider it valid only if we are not in array mode.\n\t\tif jtt == jttBeginObject && ejp.depth == 0 {\n\t\t\treturn ejp.peekMode() != jpmArrayMode\n\t\t}\n\tcase jpsSawComma:\n\t\tswitch ejp.peekMode() {\n\t\t// the only valid next token after a comma inside a document is a string (a key)\n\t\tcase jpmObjectMode:\n\t\t\treturn jtt == jttString\n\t\tcase jpmInvalidMode:\n\t\t\treturn false\n\t\t}\n\t}\n\n\t_, ok := jpsValidTransitionTokens[ejp.s][jtt]\n\treturn ok\n}\n\n// ensureExtValueType returns true if the current value has the expected\n// value type for single-key extended JSON types. For example,\n// {\"$numberInt\": v} v must be TypeString\nfunc (ejp *extJSONParser) ensureExtValueType(t Type) bool {\n\tswitch t {\n\tcase TypeMinKey, TypeMaxKey:\n\t\treturn ejp.v.t == TypeInt32\n\tcase TypeUndefined:\n\t\treturn ejp.v.t == TypeBoolean\n\tcase TypeInt32, TypeInt64, TypeDouble, TypeDecimal128, TypeSymbol, TypeObjectID:\n\t\treturn ejp.v.t == TypeString\n\tdefault:\n\t\treturn false\n\t}\n}\n\nfunc (ejp *extJSONParser) pushMode(m jsonParseMode) {\n\tejp.m = append(ejp.m, m)\n}\n\nfunc (ejp *extJSONParser) popMode() jsonParseMode {\n\tl := len(ejp.m)\n\tif l == 0 {\n\t\treturn jpmInvalidMode\n\t}\n\n\tm := ejp.m[l-1]\n\tejp.m = ejp.m[:l-1]\n\n\treturn m\n}\n\nfunc (ejp *extJSONParser) peekMode() jsonParseMode {\n\tl := len(ejp.m)\n\tif l == 0 {\n\t\treturn jpmInvalidMode\n\t}\n\n\treturn ejp.m[l-1]\n}\n\nfunc extendJSONToken(jt *jsonToken) *extJSONValue {\n\tvar t Type\n\n\tswitch jt.t {\n\tcase jttInt32:\n\t\tt = TypeInt32\n\tcase jttInt64:\n\t\tt = TypeInt64\n\tcase jttDouble:\n\t\tt = TypeDouble\n\tcase jttString:\n\t\tt = TypeString\n\tcase jttBool:\n\t\tt = TypeBoolean\n\tcase jttNull:\n\t\tt = TypeNull\n\tdefault:\n\t\treturn nil\n\t}\n\n\treturn &extJSONValue{t: t, v: jt.v}\n}\n\nfunc ensureColon(s jsonParseState, key string) error {\n\tif s != jpsSawColon {\n\t\treturn fmt.Errorf(\"invalid JSON input: missing colon after key \\\"%s\\\"\", key)\n\t}\n\n\treturn nil\n}\n\nfunc invalidRequestError(s string) error {\n\treturn fmt.Errorf(\"invalid request to read %s\", s)\n}\n\nfunc invalidJSONError(expected string) error {\n\treturn fmt.Errorf(\"invalid JSON input; expected %s\", expected)\n}\n\nfunc invalidJSONErrorForType(expected string, t Type) error {\n\treturn fmt.Errorf(\"invalid JSON input; expected %s for %s\", expected, t)\n}\n\nfunc unexpectedTokenError(jt *jsonToken) error {\n\tswitch jt.t {\n\tcase jttInt32, jttInt64, jttDouble:\n\t\treturn fmt.Errorf(\"invalid JSON input; unexpected number (%v) at position %d\", jt.v, jt.p)\n\tcase jttString:\n\t\treturn fmt.Errorf(\"invalid JSON input; unexpected string (\\\"%v\\\") at position %d\", jt.v, jt.p)\n\tcase jttBool:\n\t\treturn fmt.Errorf(\"invalid JSON input; unexpected boolean literal (%v) at position %d\", jt.v, jt.p)\n\tcase jttNull:\n\t\treturn fmt.Errorf(\"invalid JSON input; unexpected null literal at position %d\", jt.p)\n\tcase jttEOF:\n\t\treturn fmt.Errorf(\"invalid JSON input; unexpected end of input at position %d\", jt.p)\n\tdefault:\n\t\treturn fmt.Errorf(\"invalid JSON input; unexpected %c at position %d\", jt.v.(byte), jt.p)\n\t}\n}\n\nfunc nestingDepthError(p, depth int) error {\n\treturn fmt.Errorf(\"invalid JSON input; nesting too deep (%d levels) at position %d\", depth, p)\n}\n"
  },
  {
    "path": "bson/extjson_parser_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bson\n\nimport (\n\t\"errors\"\n\t\"io\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/google/go-cmp/cmp\"\n)\n\nvar (\n\tkeyDiff = specificDiff(\"key\")\n\ttypDiff = specificDiff(\"type\")\n\tvalDiff = specificDiff(\"value\")\n\n\texpectErrEOF = expectSpecificError(io.EOF)\n\texpectErrEOD = expectSpecificError(ErrEOD)\n\texpectErrEOA = expectSpecificError(ErrEOA)\n)\n\ntype expectedErrorFunc func(t *testing.T, err error, desc string)\n\ntype peekTypeTestCase struct {\n\tdesc  string\n\tinput string\n\ttyps  []Type\n\terrFs []expectedErrorFunc\n}\n\ntype readKeyValueTestCase struct {\n\tdesc  string\n\tinput string\n\tkeys  []string\n\ttyps  []Type\n\tvals  []*extJSONValue\n\n\tkeyEFs []expectedErrorFunc\n\tvalEFs []expectedErrorFunc\n}\n\nfunc expectNoError(t *testing.T, err error, desc string) {\n\tif err != nil {\n\t\tt.Helper()\n\t\tt.Errorf(\"%s: Unepexted error: %v\", desc, err)\n\t\tt.FailNow()\n\t}\n}\n\nfunc expectError(t *testing.T, err error, desc string) {\n\tif err == nil {\n\t\tt.Helper()\n\t\tt.Errorf(\"%s: Expected error\", desc)\n\t\tt.FailNow()\n\t}\n}\n\nfunc expectSpecificError(expected error) expectedErrorFunc {\n\treturn func(t *testing.T, err error, desc string) {\n\t\tif !errors.Is(err, expected) {\n\t\t\tt.Helper()\n\t\t\tt.Errorf(\"%s: Expected %v but got: %v\", desc, expected, err)\n\t\t\tt.FailNow()\n\t\t}\n\t}\n}\n\nfunc specificDiff(name string) func(t *testing.T, expected, actual any, desc string) {\n\treturn func(t *testing.T, expected, actual any, desc string) {\n\t\tif diff := cmp.Diff(expected, actual); diff != \"\" {\n\t\t\tt.Helper()\n\t\t\tt.Errorf(\"%s: Incorrect JSON %s (-want, +got): %s\\n\", desc, name, diff)\n\t\t\tt.FailNow()\n\t\t}\n\t}\n}\n\nfunc expectErrorNOOP(_ *testing.T, _ error, _ string) {\n}\n\nfunc readKeyDiff(t *testing.T, eKey, aKey string, eTyp, aTyp Type, err error, errF expectedErrorFunc, desc string) {\n\tkeyDiff(t, eKey, aKey, desc)\n\ttypDiff(t, eTyp, aTyp, desc)\n\terrF(t, err, desc)\n}\n\nfunc readValueDiff(t *testing.T, eVal, aVal *extJSONValue, err error, errF expectedErrorFunc, desc string) {\n\tif aVal != nil {\n\t\ttypDiff(t, eVal.t, aVal.t, desc)\n\t\tvalDiff(t, eVal.v, aVal.v, desc)\n\t} else {\n\t\tvalDiff(t, eVal, aVal, desc)\n\t}\n\n\terrF(t, err, desc)\n}\n\nfunc TestExtJSONParserPeekType(t *testing.T) {\n\tmakeValidPeekTypeTestCase := func(input string, typ Type, desc string) peekTypeTestCase {\n\t\treturn peekTypeTestCase{\n\t\t\tdesc: desc, input: input,\n\t\t\ttyps:  []Type{typ},\n\t\t\terrFs: []expectedErrorFunc{expectNoError},\n\t\t}\n\t}\n\tmakeInvalidTestCase := func(desc, input string, lastEF expectedErrorFunc) peekTypeTestCase {\n\t\treturn peekTypeTestCase{\n\t\t\tdesc: desc, input: input,\n\t\t\ttyps:  []Type{Type(0)},\n\t\t\terrFs: []expectedErrorFunc{lastEF},\n\t\t}\n\t}\n\n\tmakeInvalidPeekTypeTestCase := func(desc, input string, lastEF expectedErrorFunc) peekTypeTestCase {\n\t\treturn peekTypeTestCase{\n\t\t\tdesc: desc, input: input,\n\t\t\ttyps:  []Type{TypeArray, TypeString, Type(0)},\n\t\t\terrFs: []expectedErrorFunc{expectNoError, expectNoError, lastEF},\n\t\t}\n\t}\n\n\tcases := []peekTypeTestCase{\n\t\tmakeValidPeekTypeTestCase(`null`, TypeNull, \"Null\"),\n\t\tmakeValidPeekTypeTestCase(`\"string\"`, TypeString, \"String\"),\n\t\tmakeValidPeekTypeTestCase(`true`, TypeBoolean, \"Boolean--true\"),\n\t\tmakeValidPeekTypeTestCase(`false`, TypeBoolean, \"Boolean--false\"),\n\t\tmakeValidPeekTypeTestCase(`{\"$minKey\": 1}`, TypeMinKey, \"MinKey\"),\n\t\tmakeValidPeekTypeTestCase(`{\"$maxKey\": 1}`, TypeMaxKey, \"MaxKey\"),\n\t\tmakeValidPeekTypeTestCase(`{\"$numberInt\": \"42\"}`, TypeInt32, \"Int32\"),\n\t\tmakeValidPeekTypeTestCase(`{\"$numberLong\": \"42\"}`, TypeInt64, \"Int64\"),\n\t\tmakeValidPeekTypeTestCase(`{\"$symbol\": \"symbol\"}`, TypeSymbol, \"Symbol\"),\n\t\tmakeValidPeekTypeTestCase(`{\"$numberDouble\": \"42.42\"}`, TypeDouble, \"Double\"),\n\t\tmakeValidPeekTypeTestCase(`{\"$undefined\": true}`, TypeUndefined, \"Undefined\"),\n\t\tmakeValidPeekTypeTestCase(`{\"$numberDouble\": \"NaN\"}`, TypeDouble, \"Double--NaN\"),\n\t\tmakeValidPeekTypeTestCase(`{\"$numberDecimal\": \"1234\"}`, TypeDecimal128, \"Decimal\"),\n\t\tmakeValidPeekTypeTestCase(`{\"foo\": \"bar\"}`, TypeEmbeddedDocument, \"Toplevel document\"),\n\t\tmakeValidPeekTypeTestCase(`{\"$date\": {\"$numberLong\": \"0\"}}`, TypeDateTime, \"Datetime\"),\n\t\tmakeValidPeekTypeTestCase(`{\"$code\": \"function() {}\"}`, TypeJavaScript, \"Code no scope\"),\n\t\tmakeValidPeekTypeTestCase(`[{\"$numberInt\": \"1\"},{\"$numberInt\": \"2\"}]`, TypeArray, \"Array\"),\n\t\tmakeValidPeekTypeTestCase(`{\"$timestamp\": {\"t\": 42, \"i\": 1}}`, TypeTimestamp, \"Timestamp\"),\n\t\tmakeValidPeekTypeTestCase(`{\"$oid\": \"57e193d7a9cc81b4027498b5\"}`, TypeObjectID, \"Object ID\"),\n\t\tmakeValidPeekTypeTestCase(`{\"$binary\": {\"base64\": \"AQIDBAU=\", \"subType\": \"80\"}}`, TypeBinary, \"Binary\"),\n\t\tmakeValidPeekTypeTestCase(`{\"$code\": \"function() {}\", \"$scope\": {}}`, TypeCodeWithScope, \"Code With Scope\"),\n\t\tmakeValidPeekTypeTestCase(`{\"$binary\": {\"base64\": \"o0w498Or7cijeBSpkquNtg==\", \"subType\": \"03\"}}`, TypeBinary, \"Binary\"),\n\t\tmakeValidPeekTypeTestCase(`{\"$binary\": \"o0w498Or7cijeBSpkquNtg==\", \"$type\": \"03\"}`, TypeBinary, \"Binary\"),\n\t\tmakeValidPeekTypeTestCase(`{\"$regularExpression\": {\"pattern\": \"foo*\", \"options\": \"ix\"}}`, TypeRegex, \"Regular expression\"),\n\t\tmakeValidPeekTypeTestCase(`{\"$dbPointer\": {\"$ref\": \"db.collection\", \"$id\": {\"$oid\": \"57e193d7a9cc81b4027498b1\"}}}`, TypeDBPointer, \"DBPointer\"),\n\t\tmakeValidPeekTypeTestCase(`{\"$ref\": \"collection\", \"$id\": {\"$oid\": \"57fd71e96e32ab4225b723fb\"}, \"$db\": \"database\"}`, TypeEmbeddedDocument, \"DBRef\"),\n\t\tmakeInvalidPeekTypeTestCase(\"invalid array--missing ]\", `[\"a\"`, expectError),\n\t\tmakeInvalidPeekTypeTestCase(\"invalid array--colon in array\", `[\"a\":`, expectError),\n\t\tmakeInvalidPeekTypeTestCase(\"invalid array--extra comma\", `[\"a\",,`, expectError),\n\t\tmakeInvalidPeekTypeTestCase(\"invalid array--trailing comma\", `[\"a\",]`, expectError),\n\t\tmakeInvalidPeekTypeTestCase(\"peekType after end of array\", `[\"a\"]`, expectErrEOA),\n\t\t{\n\t\t\tdesc:  \"invalid array--leading comma\",\n\t\t\tinput: `[,`,\n\t\t\ttyps:  []Type{TypeArray, Type(0)},\n\t\t\terrFs: []expectedErrorFunc{expectNoError, expectError},\n\t\t},\n\t\tmakeInvalidTestCase(\"lone $scope\", `{\"$scope\": {}}`, expectError),\n\t\tmakeInvalidTestCase(\"empty code with unknown extra key\", `{\"$code\":\"\", \"0\":\"\"}`, expectError),\n\t\tmakeInvalidTestCase(\"non-empty code with unknown extra key\", `{\"$code\":\"foobar\", \"0\":\"\"}`, expectError),\n\t}\n\n\tfor _, tc := range cases {\n\t\tt.Run(tc.desc, func(t *testing.T) {\n\t\t\tejp := newExtJSONParser(strings.NewReader(tc.input), true)\n\t\t\t// Manually set the parser's starting state to jpsSawColon so peekType will read ahead to find the extjson\n\t\t\t// type of the value. If not set, the parser will be in jpsStartState and advance to jpsSawKey, which will\n\t\t\t// cause it to return without peeking the extjson type.\n\t\t\tejp.s = jpsSawColon\n\n\t\t\tfor i, eTyp := range tc.typs {\n\t\t\t\terrF := tc.errFs[i]\n\n\t\t\t\ttyp, err := ejp.peekType()\n\t\t\t\terrF(t, err, tc.desc)\n\t\t\t\tif err != nil {\n\t\t\t\t\t// Don't inspect the type if there was an error\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\ttypDiff(t, eTyp, typ, tc.desc)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestExtJSONParserReadKeyReadValue(t *testing.T) {\n\t// several test cases will use the same keys, types, and values, and only differ on input structure\n\n\tkeys := []string{\"_id\", \"Symbol\", \"String\", \"Int32\", \"Int64\", \"Int\", \"MinKey\"}\n\ttypes := []Type{TypeObjectID, TypeSymbol, TypeString, TypeInt32, TypeInt64, TypeInt32, TypeMinKey}\n\tvalues := []*extJSONValue{\n\t\t{t: TypeString, v: \"57e193d7a9cc81b4027498b5\"},\n\t\t{t: TypeString, v: \"symbol\"},\n\t\t{t: TypeString, v: \"string\"},\n\t\t{t: TypeString, v: \"42\"},\n\t\t{t: TypeString, v: \"42\"},\n\t\t{t: TypeInt32, v: int32(42)},\n\t\t{t: TypeInt32, v: int32(1)},\n\t}\n\n\terrFuncs := make([]expectedErrorFunc, 7)\n\tfor i := 0; i < 7; i++ {\n\t\terrFuncs[i] = expectNoError\n\t}\n\n\tfirstKeyError := func(desc, input string) readKeyValueTestCase {\n\t\treturn readKeyValueTestCase{\n\t\t\tdesc:   desc,\n\t\t\tinput:  input,\n\t\t\tkeys:   []string{\"\"},\n\t\t\ttyps:   []Type{Type(0)},\n\t\t\tvals:   []*extJSONValue{nil},\n\t\t\tkeyEFs: []expectedErrorFunc{expectError},\n\t\t\tvalEFs: []expectedErrorFunc{expectErrorNOOP},\n\t\t}\n\t}\n\n\tsecondKeyError := func(desc, input, firstKey string, firstType Type, firstValue *extJSONValue) readKeyValueTestCase {\n\t\treturn readKeyValueTestCase{\n\t\t\tdesc:   desc,\n\t\t\tinput:  input,\n\t\t\tkeys:   []string{firstKey, \"\"},\n\t\t\ttyps:   []Type{firstType, Type(0)},\n\t\t\tvals:   []*extJSONValue{firstValue, nil},\n\t\t\tkeyEFs: []expectedErrorFunc{expectNoError, expectError},\n\t\t\tvalEFs: []expectedErrorFunc{expectNoError, expectErrorNOOP},\n\t\t}\n\t}\n\n\tcases := []readKeyValueTestCase{\n\t\t{\n\t\t\tdesc: \"normal spacing\",\n\t\t\tinput: `{\n\t\t\t\t\t\"_id\": { \"$oid\": \"57e193d7a9cc81b4027498b5\" },\n\t\t\t\t\t\"Symbol\": { \"$symbol\": \"symbol\" },\n\t\t\t\t\t\"String\": \"string\",\n\t\t\t\t\t\"Int32\": { \"$numberInt\": \"42\" },\n\t\t\t\t\t\"Int64\": { \"$numberLong\": \"42\" },\n\t\t\t\t\t\"Int\": 42,\n\t\t\t\t\t\"MinKey\": { \"$minKey\": 1 }\n\t\t\t\t}`,\n\t\t\tkeys: keys, typs: types, vals: values,\n\t\t\tkeyEFs: errFuncs, valEFs: errFuncs,\n\t\t},\n\t\t{\n\t\t\tdesc: \"new line before comma\",\n\t\t\tinput: `{ \"_id\": { \"$oid\": \"57e193d7a9cc81b4027498b5\" }\n\t\t\t\t , \"Symbol\": { \"$symbol\": \"symbol\" }\n\t\t\t\t , \"String\": \"string\"\n\t\t\t\t , \"Int32\": { \"$numberInt\": \"42\" }\n\t\t\t\t , \"Int64\": { \"$numberLong\": \"42\" }\n\t\t\t\t , \"Int\": 42\n\t\t\t\t , \"MinKey\": { \"$minKey\": 1 }\n\t\t\t\t }`,\n\t\t\tkeys: keys, typs: types, vals: values,\n\t\t\tkeyEFs: errFuncs, valEFs: errFuncs,\n\t\t},\n\t\t{\n\t\t\tdesc: \"tabs around colons\",\n\t\t\tinput: `{\n\t\t\t\t\t\"_id\":    { \"$oid\"       : \"57e193d7a9cc81b4027498b5\" },\n\t\t\t\t\t\"Symbol\": { \"$symbol\"    : \"symbol\" },\n\t\t\t\t\t\"String\": \"string\",\n\t\t\t\t\t\"Int32\":  { \"$numberInt\" : \"42\" },\n\t\t\t\t\t\"Int64\":  { \"$numberLong\": \"42\" },\n\t\t\t\t\t\"Int\":    42,\n\t\t\t\t\t\"MinKey\": { \"$minKey\": 1 }\n\t\t\t\t}`,\n\t\t\tkeys: keys, typs: types, vals: values,\n\t\t\tkeyEFs: errFuncs, valEFs: errFuncs,\n\t\t},\n\t\t{\n\t\t\tdesc:  \"no whitespace\",\n\t\t\tinput: `{\"_id\":{\"$oid\":\"57e193d7a9cc81b4027498b5\"},\"Symbol\":{\"$symbol\":\"symbol\"},\"String\":\"string\",\"Int32\":{\"$numberInt\":\"42\"},\"Int64\":{\"$numberLong\":\"42\"},\"Int\":42,\"MinKey\":{\"$minKey\":1}}`,\n\t\t\tkeys:  keys, typs: types, vals: values,\n\t\t\tkeyEFs: errFuncs, valEFs: errFuncs,\n\t\t},\n\t\t{\n\t\t\tdesc: \"mixed whitespace\",\n\t\t\tinput: `\t{\n\t\t\t\t\t\"_id\"\t\t: { \"$oid\": \"57e193d7a9cc81b4027498b5\" },\n\t\t\t        \"Symbol\"\t: { \"$symbol\": \"symbol\" }\t,\n\t\t\t\t    \"String\"\t: \"string\",\n\t\t\t\t\t\"Int32\"\t\t: { \"$numberInt\": \"42\" }    ,\n\t\t\t\t\t\"Int64\"\t\t: {\"$numberLong\" : \"42\"},\n\t\t\t\t\t\"Int\"\t\t: 42,\n\t\t\t      \t\"MinKey\"\t: { \"$minKey\": 1 } \t}\t`,\n\t\t\tkeys: keys, typs: types, vals: values,\n\t\t\tkeyEFs: errFuncs, valEFs: errFuncs,\n\t\t},\n\t\t{\n\t\t\tdesc:  \"nested object\",\n\t\t\tinput: `{\"k1\": 1, \"k2\": { \"k3\": { \"k4\": 4 } }, \"k5\": 5}`,\n\t\t\tkeys:  []string{\"k1\", \"k2\", \"k3\", \"k4\", \"\", \"\", \"k5\", \"\"},\n\t\t\ttyps:  []Type{TypeInt32, TypeEmbeddedDocument, TypeEmbeddedDocument, TypeInt32, Type(0), Type(0), TypeInt32, Type(0)},\n\t\t\tvals: []*extJSONValue{\n\t\t\t\t{t: TypeInt32, v: int32(1)}, nil, nil, {t: TypeInt32, v: int32(4)}, nil, nil, {t: TypeInt32, v: int32(5)}, nil,\n\t\t\t},\n\t\t\tkeyEFs: []expectedErrorFunc{\n\t\t\t\texpectNoError, expectNoError, expectNoError, expectNoError, expectErrEOD,\n\t\t\t\texpectErrEOD, expectNoError, expectErrEOD,\n\t\t\t},\n\t\t\tvalEFs: []expectedErrorFunc{\n\t\t\t\texpectNoError, expectError, expectError, expectNoError, expectErrorNOOP,\n\t\t\t\texpectErrorNOOP, expectNoError, expectErrorNOOP,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tdesc:   \"invalid input: invalid values for extended type\",\n\t\t\tinput:  `{\"a\": {\"$numberInt\": \"1\", \"x\"`,\n\t\t\tkeys:   []string{\"a\"},\n\t\t\ttyps:   []Type{TypeInt32},\n\t\t\tvals:   []*extJSONValue{nil},\n\t\t\tkeyEFs: []expectedErrorFunc{expectNoError},\n\t\t\tvalEFs: []expectedErrorFunc{expectError},\n\t\t},\n\t\tfirstKeyError(\"invalid input: missing key--EOF\", \"{\"),\n\t\tfirstKeyError(\"invalid input: missing key--colon first\", \"{:\"),\n\t\tfirstKeyError(\"invalid input: missing value\", `{\"a\":`),\n\t\tfirstKeyError(\"invalid input: missing colon\", `{\"a\" 1`),\n\t\tfirstKeyError(\"invalid input: extra colon\", `{\"a\"::`),\n\t\tsecondKeyError(\"invalid input: missing }\", `{\"a\": 1`, \"a\", TypeInt32, &extJSONValue{t: TypeInt32, v: int32(1)}),\n\t\tsecondKeyError(\"invalid input: missing comma\", `{\"a\": 1 \"b\"`, \"a\", TypeInt32, &extJSONValue{t: TypeInt32, v: int32(1)}),\n\t\tsecondKeyError(\"invalid input: extra comma\", `{\"a\": 1,, \"b\"`, \"a\", TypeInt32, &extJSONValue{t: TypeInt32, v: int32(1)}),\n\t\tsecondKeyError(\"invalid input: trailing comma in object\", `{\"a\": 1,}`, \"a\", TypeInt32, &extJSONValue{t: TypeInt32, v: int32(1)}),\n\t\t{\n\t\t\tdesc:   \"invalid input: lone scope after a complete value\",\n\t\t\tinput:  `{\"a\": \"\", \"b\": {\"$scope: \"\"}}`,\n\t\t\tkeys:   []string{\"a\"},\n\t\t\ttyps:   []Type{TypeString},\n\t\t\tvals:   []*extJSONValue{{TypeString, \"\"}},\n\t\t\tkeyEFs: []expectedErrorFunc{expectNoError, expectNoError},\n\t\t\tvalEFs: []expectedErrorFunc{expectNoError, expectError},\n\t\t},\n\t\t{\n\t\t\tdesc:   \"invalid input: lone scope nested\",\n\t\t\tinput:  `{\"a\":{\"b\":{\"$scope\":{`,\n\t\t\tkeys:   []string{},\n\t\t\ttyps:   []Type{},\n\t\t\tvals:   []*extJSONValue{nil},\n\t\t\tkeyEFs: []expectedErrorFunc{expectNoError},\n\t\t\tvalEFs: []expectedErrorFunc{expectError},\n\t\t},\n\t}\n\n\tfor _, tc := range cases {\n\t\tt.Run(tc.desc, func(t *testing.T) {\n\t\t\tejp := newExtJSONParser(strings.NewReader(tc.input), true)\n\n\t\t\tfor i, eKey := range tc.keys {\n\t\t\t\teTyp := tc.typs[i]\n\t\t\t\teVal := tc.vals[i]\n\n\t\t\t\tkeyErrF := tc.keyEFs[i]\n\t\t\t\tvalErrF := tc.valEFs[i]\n\n\t\t\t\tk, typ, err := ejp.readKey()\n\t\t\t\treadKeyDiff(t, eKey, k, eTyp, typ, err, keyErrF, tc.desc)\n\n\t\t\t\tv, err := ejp.readValue(typ)\n\t\t\t\treadValueDiff(t, eVal, v, err, valErrF, tc.desc)\n\t\t\t}\n\t\t})\n\t}\n}\n\ntype ejpExpectationTest func(t *testing.T, p *extJSONParser, expectedKey string, expectedType Type, expectedValue any)\n\ntype ejpTestCase struct {\n\tf ejpExpectationTest\n\tp *extJSONParser\n\tk string\n\tt Type\n\tv any\n}\n\n// expectSingleValue is used for simple JSON types (strings, numbers, literals) and for extended JSON types that\n// have single key-value pairs (i.e. { \"$minKey\": 1 }, { \"$numberLong\": \"42.42\" })\nfunc expectSingleValue(t *testing.T, p *extJSONParser, expectedKey string, expectedType Type, expectedValue any) {\n\teVal := expectedValue.(*extJSONValue)\n\n\tk, typ, err := p.readKey()\n\treadKeyDiff(t, expectedKey, k, expectedType, typ, err, expectNoError, expectedKey)\n\n\tv, err := p.readValue(typ)\n\treadValueDiff(t, eVal, v, err, expectNoError, expectedKey)\n}\n\n// expectMultipleValues is used for values that are subdocuments of known size and with known keys (such as extended\n// JSON types { \"$timestamp\": {\"t\": 1, \"i\": 1} } and { \"$regularExpression\": {\"pattern\": \"\", options: \"\"} })\nfunc expectMultipleValues(t *testing.T, p *extJSONParser, expectedKey string, expectedType Type, expectedValue any) {\n\tk, typ, err := p.readKey()\n\treadKeyDiff(t, expectedKey, k, expectedType, typ, err, expectNoError, expectedKey)\n\n\tv, err := p.readValue(typ)\n\texpectNoError(t, err, \"\")\n\ttypDiff(t, TypeEmbeddedDocument, v.t, expectedKey)\n\n\tactObj := v.v.(*extJSONObject)\n\texpObj := expectedValue.(*extJSONObject)\n\n\tfor i, actKey := range actObj.keys {\n\t\texpKey := expObj.keys[i]\n\t\tactVal := actObj.values[i]\n\t\texpVal := expObj.values[i]\n\n\t\tkeyDiff(t, expKey, actKey, expectedKey)\n\t\ttypDiff(t, expVal.t, actVal.t, expectedKey)\n\t\tvalDiff(t, expVal.v, actVal.v, expectedKey)\n\t}\n}\n\ntype ejpKeyTypValTriple struct {\n\tkey string\n\ttyp Type\n\tval *extJSONValue\n}\n\ntype ejpSubDocumentTestValue struct {\n\tcode string               // code is only used for TypeCodeWithScope (and is ignored for TypeEmbeddedDocument\n\tktvs []ejpKeyTypValTriple // list of (key, type, value) triples; this is \"scope\" for TypeCodeWithScope\n}\n\n// expectSubDocument is used for embedded documents and code with scope types; it reads all the keys and values\n// in the embedded document (or scope for codeWithScope) and compares them to the expectedValue's list of (key, type,\n// value) triples\nfunc expectSubDocument(t *testing.T, p *extJSONParser, expectedKey string, expectedType Type, expectedValue any) {\n\tsubdoc := expectedValue.(ejpSubDocumentTestValue)\n\n\tk, typ, err := p.readKey()\n\treadKeyDiff(t, expectedKey, k, expectedType, typ, err, expectNoError, expectedKey)\n\n\tif expectedType == TypeCodeWithScope {\n\t\tv, err := p.readValue(typ)\n\t\treadValueDiff(t, &extJSONValue{t: TypeString, v: subdoc.code}, v, err, expectNoError, expectedKey)\n\t}\n\n\tfor _, ktv := range subdoc.ktvs {\n\t\teKey := ktv.key\n\t\teTyp := ktv.typ\n\t\teVal := ktv.val\n\n\t\tk, typ, err = p.readKey()\n\t\treadKeyDiff(t, eKey, k, eTyp, typ, err, expectNoError, expectedKey)\n\n\t\tv, err := p.readValue(typ)\n\t\treadValueDiff(t, eVal, v, err, expectNoError, expectedKey)\n\t}\n\n\tif expectedType == TypeCodeWithScope {\n\t\t// expect scope doc to close\n\t\tk, typ, err = p.readKey()\n\t\treadKeyDiff(t, \"\", k, Type(0), typ, err, expectErrEOD, expectedKey)\n\t}\n\n\t// expect subdoc to close\n\tk, typ, err = p.readKey()\n\treadKeyDiff(t, \"\", k, Type(0), typ, err, expectErrEOD, expectedKey)\n}\n\n// expectArray takes the expectedKey, ignores the expectedType, and uses the expectedValue\n// as a slice of (type Type, value *extJSONValue) pairs\nfunc expectArray(t *testing.T, p *extJSONParser, expectedKey string, _ Type, expectedValue any) {\n\tktvs := expectedValue.([]ejpKeyTypValTriple)\n\n\tk, typ, err := p.readKey()\n\treadKeyDiff(t, expectedKey, k, TypeArray, typ, err, expectNoError, expectedKey)\n\n\tfor _, ktv := range ktvs {\n\t\teTyp := ktv.typ\n\t\teVal := ktv.val\n\n\t\ttyp, err = p.peekType()\n\t\ttypDiff(t, eTyp, typ, expectedKey)\n\t\texpectNoError(t, err, expectedKey)\n\n\t\tv, err := p.readValue(typ)\n\t\treadValueDiff(t, eVal, v, err, expectNoError, expectedKey)\n\t}\n\n\t// expect array to end\n\ttyp, err = p.peekType()\n\ttypDiff(t, Type(0), typ, expectedKey)\n\texpectErrEOA(t, err, expectedKey)\n}\n\nfunc TestExtJSONParserAllTypes(t *testing.T) {\n\tin := ` { \"_id\"\t\t\t\t\t: { \"$oid\": \"57e193d7a9cc81b4027498b5\"}\n\t\t\t, \"Symbol\"\t\t\t\t: { \"$symbol\": \"symbol\"}\n\t\t\t, \"String\"\t\t\t\t: \"string\"\n\t\t\t, \"Int32\"\t\t\t\t: { \"$numberInt\": \"42\"}\n\t\t\t, \"Int64\"\t\t\t\t: { \"$numberLong\": \"42\"}\n\t\t\t, \"Double\"\t\t\t\t: { \"$numberDouble\": \"42.42\"}\n\t\t\t, \"SpecialFloat\"\t\t: { \"$numberDouble\": \"NaN\" }\n\t\t\t, \"Decimal\"\t\t\t\t: { \"$numberDecimal\": \"1234\" }\n\t\t\t, \"Binary\"\t\t\t \t: { \"$binary\": { \"base64\": \"o0w498Or7cijeBSpkquNtg==\", \"subType\": \"03\" } }\n\t\t\t, \"BinaryLegacy\"  : { \"$binary\": \"o0w498Or7cijeBSpkquNtg==\", \"$type\": \"03\" }\n\t\t\t, \"BinaryUserDefined\"\t: { \"$binary\": { \"base64\": \"AQIDBAU=\", \"subType\": \"80\" } }\n\t\t\t, \"Code\"\t\t\t\t: { \"$code\": \"function() {}\" }\n\t\t\t, \"CodeWithEmptyScope\"\t: { \"$code\": \"function() {}\", \"$scope\": {} }\n\t\t\t, \"CodeWithScope\"\t\t: { \"$code\": \"function() {}\", \"$scope\": { \"x\": 1 } }\n\t\t\t, \"EmptySubdocument\"    : {}\n\t\t\t, \"Subdocument\"\t\t\t: { \"foo\": \"bar\", \"baz\": { \"$numberInt\": \"42\" } }\n\t\t\t, \"Array\"\t\t\t\t: [{\"$numberInt\": \"1\"}, {\"$numberLong\": \"2\"}, {\"$numberDouble\": \"3\"}, 4, \"string\", 5.0]\n\t\t\t, \"Timestamp\"\t\t\t: { \"$timestamp\": { \"t\": 42, \"i\": 1 } }\n\t\t\t, \"RegularExpression\"\t: { \"$regularExpression\": { \"pattern\": \"foo*\", \"options\": \"ix\" } }\n\t\t\t, \"DatetimeEpoch\"\t\t: { \"$date\": { \"$numberLong\": \"0\" } }\n\t\t\t, \"DatetimePositive\"\t: { \"$date\": { \"$numberLong\": \"9223372036854775807\" } }\n\t\t\t, \"DatetimeNegative\"\t: { \"$date\": { \"$numberLong\": \"-9223372036854775808\" } }\n\t\t\t, \"True\"\t\t\t\t: true\n\t\t\t, \"False\"\t\t\t\t: false\n\t\t\t, \"DBPointer\"\t\t\t: { \"$dbPointer\": { \"$ref\": \"db.collection\", \"$id\": { \"$oid\": \"57e193d7a9cc81b4027498b1\" } } }\n\t\t\t, \"DBRef\"\t\t\t\t: { \"$ref\": \"collection\", \"$id\": { \"$oid\": \"57fd71e96e32ab4225b723fb\" }, \"$db\": \"database\" }\n\t\t\t, \"DBRefNoDB\"\t\t\t: { \"$ref\": \"collection\", \"$id\": { \"$oid\": \"57fd71e96e32ab4225b723fb\" } }\n\t\t\t, \"MinKey\"\t\t\t\t: { \"$minKey\": 1 }\n\t\t\t, \"MaxKey\"\t\t\t\t: { \"$maxKey\": 1 }\n\t\t\t, \"Null\"\t\t\t\t: null\n\t\t\t, \"Undefined\"\t\t\t: { \"$undefined\": true }\n\t\t\t}`\n\n\tejp := newExtJSONParser(strings.NewReader(in), true)\n\n\tcases := []ejpTestCase{\n\t\t{\n\t\t\tf: expectSingleValue, p: ejp,\n\t\t\tk: \"_id\", t: TypeObjectID, v: &extJSONValue{t: TypeString, v: \"57e193d7a9cc81b4027498b5\"},\n\t\t},\n\t\t{\n\t\t\tf: expectSingleValue, p: ejp,\n\t\t\tk: \"Symbol\", t: TypeSymbol, v: &extJSONValue{t: TypeString, v: \"symbol\"},\n\t\t},\n\t\t{\n\t\t\tf: expectSingleValue, p: ejp,\n\t\t\tk: \"String\", t: TypeString, v: &extJSONValue{t: TypeString, v: \"string\"},\n\t\t},\n\t\t{\n\t\t\tf: expectSingleValue, p: ejp,\n\t\t\tk: \"Int32\", t: TypeInt32, v: &extJSONValue{t: TypeString, v: \"42\"},\n\t\t},\n\t\t{\n\t\t\tf: expectSingleValue, p: ejp,\n\t\t\tk: \"Int64\", t: TypeInt64, v: &extJSONValue{t: TypeString, v: \"42\"},\n\t\t},\n\t\t{\n\t\t\tf: expectSingleValue, p: ejp,\n\t\t\tk: \"Double\", t: TypeDouble, v: &extJSONValue{t: TypeString, v: \"42.42\"},\n\t\t},\n\t\t{\n\t\t\tf: expectSingleValue, p: ejp,\n\t\t\tk: \"SpecialFloat\", t: TypeDouble, v: &extJSONValue{t: TypeString, v: \"NaN\"},\n\t\t},\n\t\t{\n\t\t\tf: expectSingleValue, p: ejp,\n\t\t\tk: \"Decimal\", t: TypeDecimal128, v: &extJSONValue{t: TypeString, v: \"1234\"},\n\t\t},\n\t\t{\n\t\t\tf: expectMultipleValues, p: ejp,\n\t\t\tk: \"Binary\", t: TypeBinary,\n\t\t\tv: &extJSONObject{\n\t\t\t\tkeys: []string{\"base64\", \"subType\"},\n\t\t\t\tvalues: []*extJSONValue{\n\t\t\t\t\t{t: TypeString, v: \"o0w498Or7cijeBSpkquNtg==\"},\n\t\t\t\t\t{t: TypeString, v: \"03\"},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tf: expectMultipleValues, p: ejp,\n\t\t\tk: \"BinaryLegacy\", t: TypeBinary,\n\t\t\tv: &extJSONObject{\n\t\t\t\tkeys: []string{\"base64\", \"subType\"},\n\t\t\t\tvalues: []*extJSONValue{\n\t\t\t\t\t{t: TypeString, v: \"o0w498Or7cijeBSpkquNtg==\"},\n\t\t\t\t\t{t: TypeString, v: \"03\"},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tf: expectMultipleValues, p: ejp,\n\t\t\tk: \"BinaryUserDefined\", t: TypeBinary,\n\t\t\tv: &extJSONObject{\n\t\t\t\tkeys: []string{\"base64\", \"subType\"},\n\t\t\t\tvalues: []*extJSONValue{\n\t\t\t\t\t{t: TypeString, v: \"AQIDBAU=\"},\n\t\t\t\t\t{t: TypeString, v: \"80\"},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tf: expectSingleValue, p: ejp,\n\t\t\tk: \"Code\", t: TypeJavaScript, v: &extJSONValue{t: TypeString, v: \"function() {}\"},\n\t\t},\n\t\t{\n\t\t\tf: expectSubDocument, p: ejp,\n\t\t\tk: \"CodeWithEmptyScope\", t: TypeCodeWithScope,\n\t\t\tv: ejpSubDocumentTestValue{\n\t\t\t\tcode: \"function() {}\",\n\t\t\t\tktvs: []ejpKeyTypValTriple{},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tf: expectSubDocument, p: ejp,\n\t\t\tk: \"CodeWithScope\", t: TypeCodeWithScope,\n\t\t\tv: ejpSubDocumentTestValue{\n\t\t\t\tcode: \"function() {}\",\n\t\t\t\tktvs: []ejpKeyTypValTriple{\n\t\t\t\t\t{\"x\", TypeInt32, &extJSONValue{t: TypeInt32, v: int32(1)}},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tf: expectSubDocument, p: ejp,\n\t\t\tk: \"EmptySubdocument\", t: TypeEmbeddedDocument,\n\t\t\tv: ejpSubDocumentTestValue{\n\t\t\t\tktvs: []ejpKeyTypValTriple{},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tf: expectSubDocument, p: ejp,\n\t\t\tk: \"Subdocument\", t: TypeEmbeddedDocument,\n\t\t\tv: ejpSubDocumentTestValue{\n\t\t\t\tktvs: []ejpKeyTypValTriple{\n\t\t\t\t\t{\"foo\", TypeString, &extJSONValue{t: TypeString, v: \"bar\"}},\n\t\t\t\t\t{\"baz\", TypeInt32, &extJSONValue{t: TypeString, v: \"42\"}},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tf: expectArray, p: ejp,\n\t\t\tk: \"Array\", t: TypeArray,\n\t\t\tv: []ejpKeyTypValTriple{\n\t\t\t\t{typ: TypeInt32, val: &extJSONValue{t: TypeString, v: \"1\"}},\n\t\t\t\t{typ: TypeInt64, val: &extJSONValue{t: TypeString, v: \"2\"}},\n\t\t\t\t{typ: TypeDouble, val: &extJSONValue{t: TypeString, v: \"3\"}},\n\t\t\t\t{typ: TypeInt32, val: &extJSONValue{t: TypeInt32, v: int32(4)}},\n\t\t\t\t{typ: TypeString, val: &extJSONValue{t: TypeString, v: \"string\"}},\n\t\t\t\t{typ: TypeDouble, val: &extJSONValue{t: TypeDouble, v: 5.0}},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tf: expectMultipleValues, p: ejp,\n\t\t\tk: \"Timestamp\", t: TypeTimestamp,\n\t\t\tv: &extJSONObject{\n\t\t\t\tkeys: []string{\"t\", \"i\"},\n\t\t\t\tvalues: []*extJSONValue{\n\t\t\t\t\t{t: TypeInt32, v: int32(42)},\n\t\t\t\t\t{t: TypeInt32, v: int32(1)},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tf: expectMultipleValues, p: ejp,\n\t\t\tk: \"RegularExpression\", t: TypeRegex,\n\t\t\tv: &extJSONObject{\n\t\t\t\tkeys: []string{\"pattern\", \"options\"},\n\t\t\t\tvalues: []*extJSONValue{\n\t\t\t\t\t{t: TypeString, v: \"foo*\"},\n\t\t\t\t\t{t: TypeString, v: \"ix\"},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tf: expectMultipleValues, p: ejp,\n\t\t\tk: \"DatetimeEpoch\", t: TypeDateTime,\n\t\t\tv: &extJSONObject{\n\t\t\t\tkeys: []string{\"$numberLong\"},\n\t\t\t\tvalues: []*extJSONValue{\n\t\t\t\t\t{t: TypeString, v: \"0\"},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tf: expectMultipleValues, p: ejp,\n\t\t\tk: \"DatetimePositive\", t: TypeDateTime,\n\t\t\tv: &extJSONObject{\n\t\t\t\tkeys: []string{\"$numberLong\"},\n\t\t\t\tvalues: []*extJSONValue{\n\t\t\t\t\t{t: TypeString, v: \"9223372036854775807\"},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tf: expectMultipleValues, p: ejp,\n\t\t\tk: \"DatetimeNegative\", t: TypeDateTime,\n\t\t\tv: &extJSONObject{\n\t\t\t\tkeys: []string{\"$numberLong\"},\n\t\t\t\tvalues: []*extJSONValue{\n\t\t\t\t\t{t: TypeString, v: \"-9223372036854775808\"},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tf: expectSingleValue, p: ejp,\n\t\t\tk: \"True\", t: TypeBoolean, v: &extJSONValue{t: TypeBoolean, v: true},\n\t\t},\n\t\t{\n\t\t\tf: expectSingleValue, p: ejp,\n\t\t\tk: \"False\", t: TypeBoolean, v: &extJSONValue{t: TypeBoolean, v: false},\n\t\t},\n\t\t{\n\t\t\tf: expectMultipleValues, p: ejp,\n\t\t\tk: \"DBPointer\", t: TypeDBPointer,\n\t\t\tv: &extJSONObject{\n\t\t\t\tkeys: []string{\"$ref\", \"$id\"},\n\t\t\t\tvalues: []*extJSONValue{\n\t\t\t\t\t{t: TypeString, v: \"db.collection\"},\n\t\t\t\t\t{t: TypeString, v: \"57e193d7a9cc81b4027498b1\"},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tf: expectSubDocument, p: ejp,\n\t\t\tk: \"DBRef\", t: TypeEmbeddedDocument,\n\t\t\tv: ejpSubDocumentTestValue{\n\t\t\t\tktvs: []ejpKeyTypValTriple{\n\t\t\t\t\t{\"$ref\", TypeString, &extJSONValue{t: TypeString, v: \"collection\"}},\n\t\t\t\t\t{\"$id\", TypeObjectID, &extJSONValue{t: TypeString, v: \"57fd71e96e32ab4225b723fb\"}},\n\t\t\t\t\t{\"$db\", TypeString, &extJSONValue{t: TypeString, v: \"database\"}},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tf: expectSubDocument, p: ejp,\n\t\t\tk: \"DBRefNoDB\", t: TypeEmbeddedDocument,\n\t\t\tv: ejpSubDocumentTestValue{\n\t\t\t\tktvs: []ejpKeyTypValTriple{\n\t\t\t\t\t{\"$ref\", TypeString, &extJSONValue{t: TypeString, v: \"collection\"}},\n\t\t\t\t\t{\"$id\", TypeObjectID, &extJSONValue{t: TypeString, v: \"57fd71e96e32ab4225b723fb\"}},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tf: expectSingleValue, p: ejp,\n\t\t\tk: \"MinKey\", t: TypeMinKey, v: &extJSONValue{t: TypeInt32, v: int32(1)},\n\t\t},\n\t\t{\n\t\t\tf: expectSingleValue, p: ejp,\n\t\t\tk: \"MaxKey\", t: TypeMaxKey, v: &extJSONValue{t: TypeInt32, v: int32(1)},\n\t\t},\n\t\t{\n\t\t\tf: expectSingleValue, p: ejp,\n\t\t\tk: \"Null\", t: TypeNull, v: &extJSONValue{t: TypeNull, v: nil},\n\t\t},\n\t\t{\n\t\t\tf: expectSingleValue, p: ejp,\n\t\t\tk: \"Undefined\", t: TypeUndefined, v: &extJSONValue{t: TypeBoolean, v: true},\n\t\t},\n\t}\n\n\t// run the test cases\n\tfor _, tc := range cases {\n\t\ttc.f(t, tc.p, tc.k, tc.t, tc.v)\n\t}\n\n\t// expect end of whole document: read final }\n\tk, typ, err := ejp.readKey()\n\treadKeyDiff(t, \"\", k, Type(0), typ, err, expectErrEOD, \"\")\n\n\t// expect end of whole document: read EOF\n\tk, typ, err = ejp.readKey()\n\treadKeyDiff(t, \"\", k, Type(0), typ, err, expectErrEOF, \"\")\n\tif diff := cmp.Diff(jpsDoneState, ejp.s); diff != \"\" {\n\t\tt.Errorf(\"expected parser to be in done state but instead is in %v\\n\", ejp.s)\n\t\tt.FailNow()\n\t}\n}\n\nfunc TestExtJSONValue(t *testing.T) {\n\tt.Run(\"Large Date\", func(t *testing.T) {\n\t\tval := &extJSONValue{\n\t\t\tt: TypeString,\n\t\t\tv: \"3001-01-01T00:00:00Z\",\n\t\t}\n\n\t\tintVal, err := val.parseDateTime()\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"error parsing date time: %v\", err)\n\t\t}\n\n\t\tif intVal <= 0 {\n\t\t\tt.Fatalf(\"expected value above 0, got %v\", intVal)\n\t\t}\n\t})\n\tt.Run(\"fallback time format\", func(t *testing.T) {\n\t\tval := &extJSONValue{\n\t\t\tt: TypeString,\n\t\t\tv: \"2019-06-04T14:54:31.416+0000\",\n\t\t}\n\n\t\t_, err := val.parseDateTime()\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"error parsing date time: %v\", err)\n\t\t}\n\t})\n}\n"
  },
  {
    "path": "bson/extjson_prose_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bson\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n)\n\nfunc TestExtJSON(t *testing.T) {\n\ttimestampNegativeInt32Err := fmt.Errorf(\"$timestamp i number should be uint32: -1\")\n\ttimestampNegativeInt64Err := fmt.Errorf(\"$timestamp i number should be uint32: -2147483649\")\n\ttimestampLargeValueErr := fmt.Errorf(\"$timestamp i number should be uint32: 4294967296\")\n\n\ttestCases := []struct {\n\t\tname      string\n\t\tinput     string\n\t\tcanonical bool\n\t\terr       error\n\t}{\n\t\t{\"timestamp - negative int32 value\", `{\"\":{\"$timestamp\":{\"t\":0,\"i\":-1}}}`, false, timestampNegativeInt32Err},\n\t\t{\"timestamp - negative int64 value\", `{\"\":{\"$timestamp\":{\"t\":0,\"i\":-2147483649}}}`, false, timestampNegativeInt64Err},\n\t\t{\"timestamp - value overflows uint32\", `{\"\":{\"$timestamp\":{\"t\":0,\"i\":4294967296}}}`, false, timestampLargeValueErr},\n\t\t{\"top level key is not treated as special\", `{\"$code\": \"foo\"}`, false, nil},\n\t\t{\"escaped single quote errors\", `{\"f\\'oo\": \"bar\"}`, false, ErrInvalidJSON},\n\t}\n\tfor _, tc := range testCases {\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\tvar res Raw\n\t\t\terr := UnmarshalExtJSON([]byte(tc.input), tc.canonical, &res)\n\t\t\tif tc.err == nil {\n\t\t\t\tassert.Nil(t, err, \"UnmarshalExtJSON error: %v\", err)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tassert.NotNil(t, err, \"expected error %v, got nil\", tc.err)\n\t\t\tassert.Equal(t, tc.err.Error(), err.Error(), \"expected error %v, got %v\", tc.err, err)\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "bson/extjson_reader.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bson\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n)\n\ntype ejvrState struct {\n\tmode  mode\n\tvType Type\n\tdepth int\n}\n\n// extJSONValueReader is for reading extended JSON.\ntype extJSONValueReader struct {\n\tp *extJSONParser\n\n\tstack []ejvrState\n\tframe int\n}\n\n// NewExtJSONValueReader returns a ValueReader that reads Extended JSON values\n// from r. If canonicalOnly is true, reading values from the ValueReader returns\n// an error if the Extended JSON was not marshaled in canonical mode.\nfunc NewExtJSONValueReader(r io.Reader, canonicalOnly bool) (ValueReader, error) {\n\treturn newExtJSONValueReader(r, canonicalOnly)\n}\n\nfunc newExtJSONValueReader(r io.Reader, canonicalOnly bool) (*extJSONValueReader, error) {\n\tejvr := new(extJSONValueReader)\n\treturn ejvr.reset(r, canonicalOnly)\n}\n\nfunc (ejvr *extJSONValueReader) reset(r io.Reader, canonicalOnly bool) (*extJSONValueReader, error) {\n\tp := newExtJSONParser(r, canonicalOnly)\n\ttyp, err := p.peekType()\n\tif err != nil {\n\t\treturn nil, ErrInvalidJSON\n\t}\n\n\tvar m mode\n\tswitch typ {\n\tcase TypeEmbeddedDocument:\n\t\tm = mTopLevel\n\tcase TypeArray:\n\t\tm = mArray\n\tdefault:\n\t\tm = mValue\n\t}\n\n\tstack := make([]ejvrState, 1, 5)\n\tstack[0] = ejvrState{\n\t\tmode:  m,\n\t\tvType: typ,\n\t}\n\treturn &extJSONValueReader{\n\t\tp:     p,\n\t\tstack: stack,\n\t}, nil\n}\n\nfunc (ejvr *extJSONValueReader) advanceFrame() {\n\tif ejvr.frame+1 >= len(ejvr.stack) { // We need to grow the stack\n\t\tlength := len(ejvr.stack)\n\t\tif length+1 >= cap(ejvr.stack) {\n\t\t\t// double it\n\t\t\tbuf := make([]ejvrState, 2*cap(ejvr.stack)+1)\n\t\t\tcopy(buf, ejvr.stack)\n\t\t\tejvr.stack = buf\n\t\t}\n\t\tejvr.stack = ejvr.stack[:length+1]\n\t}\n\tejvr.frame++\n\n\t// Clean the stack\n\tejvr.stack[ejvr.frame].mode = 0\n\tejvr.stack[ejvr.frame].vType = 0\n\tejvr.stack[ejvr.frame].depth = 0\n}\n\nfunc (ejvr *extJSONValueReader) pushDocument() {\n\tejvr.advanceFrame()\n\n\tejvr.stack[ejvr.frame].mode = mDocument\n\tejvr.stack[ejvr.frame].depth = ejvr.p.depth\n}\n\nfunc (ejvr *extJSONValueReader) pushCodeWithScope() {\n\tejvr.advanceFrame()\n\n\tejvr.stack[ejvr.frame].mode = mCodeWithScope\n}\n\nfunc (ejvr *extJSONValueReader) pushArray() {\n\tejvr.advanceFrame()\n\n\tejvr.stack[ejvr.frame].mode = mArray\n}\n\nfunc (ejvr *extJSONValueReader) push(m mode, t Type) {\n\tejvr.advanceFrame()\n\n\tejvr.stack[ejvr.frame].mode = m\n\tejvr.stack[ejvr.frame].vType = t\n}\n\nfunc (ejvr *extJSONValueReader) pop() {\n\tswitch ejvr.stack[ejvr.frame].mode {\n\tcase mElement, mValue:\n\t\tejvr.frame--\n\tcase mDocument, mArray, mCodeWithScope:\n\t\tejvr.frame -= 2 // we pop twice to jump over the vrElement: vrDocument -> vrElement -> vrDocument/TopLevel/etc...\n\t}\n}\n\nfunc (ejvr *extJSONValueReader) skipObject() {\n\t// read entire object until depth returns to 0 (last ending } or ] seen)\n\tdepth := 1\n\tfor depth > 0 {\n\t\tejvr.p.advanceState()\n\n\t\t// If object is empty, raise depth and continue. When emptyObject is true, the\n\t\t// parser has already read both the opening and closing brackets of an empty\n\t\t// object (\"{}\"), so the next valid token will be part of the parent document,\n\t\t// not part of the nested document.\n\t\t//\n\t\t// If there is a comma, there are remaining fields, emptyObject must be set back\n\t\t// to false, and comma must be skipped with advanceState().\n\t\tif ejvr.p.emptyObject {\n\t\t\tif ejvr.p.s == jpsSawComma {\n\t\t\t\tejvr.p.emptyObject = false\n\t\t\t\tejvr.p.advanceState()\n\t\t\t}\n\t\t\tdepth--\n\t\t\tcontinue\n\t\t}\n\n\t\tswitch ejvr.p.s {\n\t\tcase jpsSawBeginObject, jpsSawBeginArray:\n\t\t\tdepth++\n\t\tcase jpsSawEndObject, jpsSawEndArray:\n\t\t\tdepth--\n\t\t}\n\t}\n}\n\nfunc (ejvr *extJSONValueReader) invalidTransitionErr(destination mode, name string, modes []mode) error {\n\tte := TransitionError{\n\t\tname:        name,\n\t\tcurrent:     ejvr.stack[ejvr.frame].mode,\n\t\tdestination: destination,\n\t\tmodes:       modes,\n\t\taction:      \"read\",\n\t}\n\tif ejvr.frame != 0 {\n\t\tte.parent = ejvr.stack[ejvr.frame-1].mode\n\t}\n\treturn te\n}\n\nfunc (ejvr *extJSONValueReader) typeError(t Type) error {\n\treturn fmt.Errorf(\"positioned on %s, but attempted to read %s\", ejvr.stack[ejvr.frame].vType, t)\n}\n\nfunc (ejvr *extJSONValueReader) ensureElementValue(t Type, destination mode, callerName string, addModes ...mode) error {\n\tswitch ejvr.stack[ejvr.frame].mode {\n\tcase mElement, mValue:\n\t\tif ejvr.stack[ejvr.frame].vType != t {\n\t\t\treturn ejvr.typeError(t)\n\t\t}\n\tdefault:\n\t\tmodes := []mode{mElement, mValue}\n\t\tif addModes != nil {\n\t\t\tmodes = append(modes, addModes...)\n\t\t}\n\t\treturn ejvr.invalidTransitionErr(destination, callerName, modes)\n\t}\n\n\treturn nil\n}\n\nfunc (ejvr *extJSONValueReader) Type() Type {\n\treturn ejvr.stack[ejvr.frame].vType\n}\n\nfunc (ejvr *extJSONValueReader) Skip() error {\n\tswitch ejvr.stack[ejvr.frame].mode {\n\tcase mElement, mValue:\n\tdefault:\n\t\treturn ejvr.invalidTransitionErr(0, \"Skip\", []mode{mElement, mValue})\n\t}\n\n\tdefer ejvr.pop()\n\n\tt := ejvr.stack[ejvr.frame].vType\n\tswitch t {\n\tcase TypeArray, TypeEmbeddedDocument, TypeCodeWithScope:\n\t\t// read entire array, doc or CodeWithScope\n\t\tejvr.skipObject()\n\tdefault:\n\t\t_, err := ejvr.p.readValue(t)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc (ejvr *extJSONValueReader) ReadArray() (ArrayReader, error) {\n\tswitch ejvr.stack[ejvr.frame].mode {\n\tcase mTopLevel: // allow reading array from top level\n\tcase mArray:\n\t\treturn ejvr, nil\n\tdefault:\n\t\tif err := ejvr.ensureElementValue(TypeArray, mArray, \"ReadArray\", mTopLevel, mArray); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\tejvr.pushArray()\n\n\treturn ejvr, nil\n}\n\nfunc (ejvr *extJSONValueReader) ReadBinary() (b []byte, btype byte, err error) {\n\tif err := ejvr.ensureElementValue(TypeBinary, 0, \"ReadBinary\"); err != nil {\n\t\treturn nil, 0, err\n\t}\n\n\tv, err := ejvr.p.readValue(TypeBinary)\n\tif err != nil {\n\t\treturn nil, 0, err\n\t}\n\n\tb, btype, err = v.parseBinary()\n\n\tejvr.pop()\n\treturn b, btype, err\n}\n\nfunc (ejvr *extJSONValueReader) ReadBoolean() (bool, error) {\n\tif err := ejvr.ensureElementValue(TypeBoolean, 0, \"ReadBoolean\"); err != nil {\n\t\treturn false, err\n\t}\n\n\tv, err := ejvr.p.readValue(TypeBoolean)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\n\tif v.t != TypeBoolean {\n\t\treturn false, fmt.Errorf(\"expected type bool, but got type %s\", v.t)\n\t}\n\n\tejvr.pop()\n\treturn v.v.(bool), nil\n}\n\nfunc (ejvr *extJSONValueReader) ReadDocument() (DocumentReader, error) {\n\tswitch ejvr.stack[ejvr.frame].mode {\n\tcase mTopLevel:\n\t\treturn ejvr, nil\n\tcase mElement, mValue:\n\t\tif ejvr.stack[ejvr.frame].vType != TypeEmbeddedDocument {\n\t\t\treturn nil, ejvr.typeError(TypeEmbeddedDocument)\n\t\t}\n\n\t\tejvr.pushDocument()\n\t\treturn ejvr, nil\n\tdefault:\n\t\treturn nil, ejvr.invalidTransitionErr(mDocument, \"ReadDocument\", []mode{mTopLevel, mElement, mValue})\n\t}\n}\n\nfunc (ejvr *extJSONValueReader) ReadCodeWithScope() (code string, dr DocumentReader, err error) {\n\tif err = ejvr.ensureElementValue(TypeCodeWithScope, 0, \"ReadCodeWithScope\"); err != nil {\n\t\treturn \"\", nil, err\n\t}\n\n\tv, err := ejvr.p.readValue(TypeCodeWithScope)\n\tif err != nil {\n\t\treturn \"\", nil, err\n\t}\n\n\tcode, err = v.parseJavascript()\n\n\tejvr.pushCodeWithScope()\n\treturn code, ejvr, err\n}\n\nfunc (ejvr *extJSONValueReader) ReadDBPointer() (ns string, oid ObjectID, err error) {\n\tif err = ejvr.ensureElementValue(TypeDBPointer, 0, \"ReadDBPointer\"); err != nil {\n\t\treturn \"\", NilObjectID, err\n\t}\n\n\tv, err := ejvr.p.readValue(TypeDBPointer)\n\tif err != nil {\n\t\treturn \"\", NilObjectID, err\n\t}\n\n\tns, oid, err = v.parseDBPointer()\n\n\tejvr.pop()\n\treturn ns, oid, err\n}\n\nfunc (ejvr *extJSONValueReader) ReadDateTime() (int64, error) {\n\tif err := ejvr.ensureElementValue(TypeDateTime, 0, \"ReadDateTime\"); err != nil {\n\t\treturn 0, err\n\t}\n\n\tv, err := ejvr.p.readValue(TypeDateTime)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\td, err := v.parseDateTime()\n\n\tejvr.pop()\n\treturn d, err\n}\n\nfunc (ejvr *extJSONValueReader) ReadDecimal128() (Decimal128, error) {\n\tif err := ejvr.ensureElementValue(TypeDecimal128, 0, \"ReadDecimal128\"); err != nil {\n\t\treturn Decimal128{}, err\n\t}\n\n\tv, err := ejvr.p.readValue(TypeDecimal128)\n\tif err != nil {\n\t\treturn Decimal128{}, err\n\t}\n\n\td, err := v.parseDecimal128()\n\n\tejvr.pop()\n\treturn d, err\n}\n\nfunc (ejvr *extJSONValueReader) ReadDouble() (float64, error) {\n\tif err := ejvr.ensureElementValue(TypeDouble, 0, \"ReadDouble\"); err != nil {\n\t\treturn 0, err\n\t}\n\n\tv, err := ejvr.p.readValue(TypeDouble)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\td, err := v.parseDouble()\n\n\tejvr.pop()\n\treturn d, err\n}\n\nfunc (ejvr *extJSONValueReader) ReadInt32() (int32, error) {\n\tif err := ejvr.ensureElementValue(TypeInt32, 0, \"ReadInt32\"); err != nil {\n\t\treturn 0, err\n\t}\n\n\tv, err := ejvr.p.readValue(TypeInt32)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\ti, err := v.parseInt32()\n\n\tejvr.pop()\n\treturn i, err\n}\n\nfunc (ejvr *extJSONValueReader) ReadInt64() (int64, error) {\n\tif err := ejvr.ensureElementValue(TypeInt64, 0, \"ReadInt64\"); err != nil {\n\t\treturn 0, err\n\t}\n\n\tv, err := ejvr.p.readValue(TypeInt64)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\ti, err := v.parseInt64()\n\n\tejvr.pop()\n\treturn i, err\n}\n\nfunc (ejvr *extJSONValueReader) ReadJavascript() (code string, err error) {\n\tif err = ejvr.ensureElementValue(TypeJavaScript, 0, \"ReadJavascript\"); err != nil {\n\t\treturn \"\", err\n\t}\n\n\tv, err := ejvr.p.readValue(TypeJavaScript)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\tcode, err = v.parseJavascript()\n\n\tejvr.pop()\n\treturn code, err\n}\n\nfunc (ejvr *extJSONValueReader) ReadMaxKey() error {\n\tif err := ejvr.ensureElementValue(TypeMaxKey, 0, \"ReadMaxKey\"); err != nil {\n\t\treturn err\n\t}\n\n\tv, err := ejvr.p.readValue(TypeMaxKey)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\terr = v.parseMinMaxKey(\"max\")\n\n\tejvr.pop()\n\treturn err\n}\n\nfunc (ejvr *extJSONValueReader) ReadMinKey() error {\n\tif err := ejvr.ensureElementValue(TypeMinKey, 0, \"ReadMinKey\"); err != nil {\n\t\treturn err\n\t}\n\n\tv, err := ejvr.p.readValue(TypeMinKey)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\terr = v.parseMinMaxKey(\"min\")\n\n\tejvr.pop()\n\treturn err\n}\n\nfunc (ejvr *extJSONValueReader) ReadNull() error {\n\tif err := ejvr.ensureElementValue(TypeNull, 0, \"ReadNull\"); err != nil {\n\t\treturn err\n\t}\n\n\tv, err := ejvr.p.readValue(TypeNull)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif v.t != TypeNull {\n\t\treturn fmt.Errorf(\"expected type null but got type %s\", v.t)\n\t}\n\n\tejvr.pop()\n\treturn nil\n}\n\nfunc (ejvr *extJSONValueReader) ReadObjectID() (ObjectID, error) {\n\tif err := ejvr.ensureElementValue(TypeObjectID, 0, \"ReadObjectID\"); err != nil {\n\t\treturn ObjectID{}, err\n\t}\n\n\tv, err := ejvr.p.readValue(TypeObjectID)\n\tif err != nil {\n\t\treturn ObjectID{}, err\n\t}\n\n\toid, err := v.parseObjectID()\n\n\tejvr.pop()\n\treturn oid, err\n}\n\nfunc (ejvr *extJSONValueReader) ReadRegex() (pattern string, options string, err error) {\n\tif err = ejvr.ensureElementValue(TypeRegex, 0, \"ReadRegex\"); err != nil {\n\t\treturn \"\", \"\", err\n\t}\n\n\tv, err := ejvr.p.readValue(TypeRegex)\n\tif err != nil {\n\t\treturn \"\", \"\", err\n\t}\n\n\tpattern, options, err = v.parseRegex()\n\n\tejvr.pop()\n\treturn pattern, options, err\n}\n\nfunc (ejvr *extJSONValueReader) ReadString() (string, error) {\n\tif err := ejvr.ensureElementValue(TypeString, 0, \"ReadString\"); err != nil {\n\t\treturn \"\", err\n\t}\n\n\tv, err := ejvr.p.readValue(TypeString)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\tif v.t != TypeString {\n\t\treturn \"\", fmt.Errorf(\"expected type string but got type %s\", v.t)\n\t}\n\n\tejvr.pop()\n\treturn v.v.(string), nil\n}\n\nfunc (ejvr *extJSONValueReader) ReadSymbol() (symbol string, err error) {\n\tif err = ejvr.ensureElementValue(TypeSymbol, 0, \"ReadSymbol\"); err != nil {\n\t\treturn \"\", err\n\t}\n\n\tv, err := ejvr.p.readValue(TypeSymbol)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\tsymbol, err = v.parseSymbol()\n\n\tejvr.pop()\n\treturn symbol, err\n}\n\nfunc (ejvr *extJSONValueReader) ReadTimestamp() (t uint32, i uint32, err error) {\n\tif err = ejvr.ensureElementValue(TypeTimestamp, 0, \"ReadTimestamp\"); err != nil {\n\t\treturn 0, 0, err\n\t}\n\n\tv, err := ejvr.p.readValue(TypeTimestamp)\n\tif err != nil {\n\t\treturn 0, 0, err\n\t}\n\n\tt, i, err = v.parseTimestamp()\n\n\tejvr.pop()\n\treturn t, i, err\n}\n\nfunc (ejvr *extJSONValueReader) ReadUndefined() error {\n\tif err := ejvr.ensureElementValue(TypeUndefined, 0, \"ReadUndefined\"); err != nil {\n\t\treturn err\n\t}\n\n\tv, err := ejvr.p.readValue(TypeUndefined)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\terr = v.parseUndefined()\n\n\tejvr.pop()\n\treturn err\n}\n\nfunc (ejvr *extJSONValueReader) ReadElement() (string, ValueReader, error) {\n\tswitch ejvr.stack[ejvr.frame].mode {\n\tcase mTopLevel, mDocument, mCodeWithScope:\n\tdefault:\n\t\treturn \"\", nil, ejvr.invalidTransitionErr(mElement, \"ReadElement\", []mode{mTopLevel, mDocument, mCodeWithScope})\n\t}\n\n\tname, t, err := ejvr.p.readKey()\n\tif err != nil {\n\t\tif errors.Is(err, ErrEOD) {\n\t\t\tif ejvr.stack[ejvr.frame].mode == mCodeWithScope {\n\t\t\t\t_, err := ejvr.p.peekType()\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn \"\", nil, err\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tejvr.pop()\n\t\t}\n\n\t\treturn \"\", nil, err\n\t}\n\n\tejvr.push(mElement, t)\n\treturn name, ejvr, nil\n}\n\nfunc (ejvr *extJSONValueReader) ReadValue() (ValueReader, error) {\n\tswitch ejvr.stack[ejvr.frame].mode {\n\tcase mArray:\n\tdefault:\n\t\treturn nil, ejvr.invalidTransitionErr(mValue, \"ReadValue\", []mode{mArray})\n\t}\n\n\tt, err := ejvr.p.peekType()\n\tif err != nil {\n\t\tif errors.Is(err, ErrEOA) {\n\t\t\tejvr.pop()\n\t\t}\n\n\t\treturn nil, err\n\t}\n\n\tejvr.push(mValue, t)\n\treturn ejvr, nil\n}\n"
  },
  {
    "path": "bson/extjson_reader_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bson\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/google/go-cmp/cmp\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n)\n\nfunc TestExtJSONReader(t *testing.T) {\n\tt.Run(\"ReadDocument\", func(t *testing.T) {\n\t\tt.Run(\"EmbeddedDocument\", func(t *testing.T) {\n\t\t\tejvr := &extJSONValueReader{\n\t\t\t\tstack: []ejvrState{\n\t\t\t\t\t{mode: mTopLevel},\n\t\t\t\t\t{mode: mElement, vType: TypeBoolean},\n\t\t\t\t},\n\t\t\t\tframe: 1,\n\t\t\t}\n\n\t\t\tejvr.stack[1].mode = mArray\n\t\t\twanterr := ejvr.invalidTransitionErr(mDocument, \"ReadDocument\", []mode{mTopLevel, mElement, mValue})\n\t\t\t_, err := ejvr.ReadDocument()\n\t\t\tif err == nil || err.Error() != wanterr.Error() {\n\t\t\t\tt.Errorf(\"Incorrect returned error. got %v; want %v\", err, wanterr)\n\t\t\t}\n\t\t})\n\t})\n\n\tt.Run(\"invalid transition\", func(t *testing.T) {\n\t\tt.Run(\"Skip\", func(t *testing.T) {\n\t\t\tejvr := &extJSONValueReader{stack: []ejvrState{{mode: mTopLevel}}}\n\t\t\twanterr := (&extJSONValueReader{stack: []ejvrState{{mode: mTopLevel}}}).invalidTransitionErr(0, \"Skip\", []mode{mElement, mValue})\n\t\t\tgoterr := ejvr.Skip()\n\t\t\tif !cmp.Equal(goterr, wanterr, cmp.Comparer(assert.CompareErrors)) {\n\t\t\t\tt.Errorf(\"Expected correct invalid transition error. got %v; want %v\", goterr, wanterr)\n\t\t\t}\n\t\t})\n\t})\n}\n\nfunc TestReadMultipleTopLevelDocuments(t *testing.T) {\n\ttestCases := []struct {\n\t\tname     string\n\t\tinput    string\n\t\texpected [][]byte\n\t}{\n\t\t{\n\t\t\t\"single top-level document\",\n\t\t\t\"{\\\"foo\\\":1}\",\n\t\t\t[][]byte{\n\t\t\t\t{0x0E, 0x00, 0x00, 0x00, 0x10, 'f', 'o', 'o', 0x00, 0x01, 0x00, 0x00, 0x00, 0x00},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"single top-level document with leading and trailing whitespace\",\n\t\t\t\"\\n\\n   {\\\"foo\\\":1}   \\n\",\n\t\t\t[][]byte{\n\t\t\t\t{0x0E, 0x00, 0x00, 0x00, 0x10, 'f', 'o', 'o', 0x00, 0x01, 0x00, 0x00, 0x00, 0x00},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"two top-level documents\",\n\t\t\t\"{\\\"foo\\\":1}{\\\"foo\\\":2}\",\n\t\t\t[][]byte{\n\t\t\t\t{0x0E, 0x00, 0x00, 0x00, 0x10, 'f', 'o', 'o', 0x00, 0x01, 0x00, 0x00, 0x00, 0x00},\n\t\t\t\t{0x0E, 0x00, 0x00, 0x00, 0x10, 'f', 'o', 'o', 0x00, 0x02, 0x00, 0x00, 0x00, 0x00},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"two top-level documents with leading and trailing whitespace and whitespace separation \",\n\t\t\t\"\\n\\n  {\\\"foo\\\":1}\\n{\\\"foo\\\":2}\\n  \",\n\t\t\t[][]byte{\n\t\t\t\t{0x0E, 0x00, 0x00, 0x00, 0x10, 'f', 'o', 'o', 0x00, 0x01, 0x00, 0x00, 0x00, 0x00},\n\t\t\t\t{0x0E, 0x00, 0x00, 0x00, 0x10, 'f', 'o', 'o', 0x00, 0x02, 0x00, 0x00, 0x00, 0x00},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"top-level array with single document\",\n\t\t\t\"[{\\\"foo\\\":1}]\",\n\t\t\t[][]byte{\n\t\t\t\t{0x0E, 0x00, 0x00, 0x00, 0x10, 'f', 'o', 'o', 0x00, 0x01, 0x00, 0x00, 0x00, 0x00},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"top-level array with 2 documents\",\n\t\t\t\"[{\\\"foo\\\":1},{\\\"foo\\\":2}]\",\n\t\t\t[][]byte{\n\t\t\t\t{0x0E, 0x00, 0x00, 0x00, 0x10, 'f', 'o', 'o', 0x00, 0x01, 0x00, 0x00, 0x00, 0x00},\n\t\t\t\t{0x0E, 0x00, 0x00, 0x00, 0x10, 'f', 'o', 'o', 0x00, 0x02, 0x00, 0x00, 0x00, 0x00},\n\t\t\t},\n\t\t},\n\t}\n\n\tfor _, tc := range testCases {\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\tr := strings.NewReader(tc.input)\n\t\t\tvr, err := NewExtJSONValueReader(r, false)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"expected no error, but got %v\", err)\n\t\t\t}\n\n\t\t\tactual, err := readAllDocuments(vr)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"expected no error, but got %v\", err)\n\t\t\t}\n\n\t\t\tif diff := cmp.Diff(tc.expected, actual); diff != \"\" {\n\t\t\t\tt.Fatalf(\"expected does not match actual: %v\", diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc readAllDocuments(vr ValueReader) ([][]byte, error) {\n\tvar actual [][]byte\n\n\tswitch vr.Type() {\n\tcase TypeEmbeddedDocument:\n\t\tfor {\n\t\t\tresult, err := copyDocumentToBytes(vr)\n\t\t\tif err != nil {\n\t\t\t\tif errors.Is(err, io.EOF) {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\treturn nil, err\n\t\t\t}\n\n\t\t\tactual = append(actual, result)\n\t\t}\n\tcase TypeArray:\n\t\tar, err := vr.ReadArray()\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tfor {\n\t\t\tevr, err := ar.ReadValue()\n\t\t\tif err != nil {\n\t\t\t\tif errors.Is(err, ErrEOA) {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\treturn nil, err\n\t\t\t}\n\n\t\t\tresult, err := copyDocumentToBytes(evr)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\n\t\t\tactual = append(actual, result)\n\t\t}\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"expected an array or a document, but got %s\", vr.Type())\n\t}\n\n\treturn actual, nil\n}\n"
  },
  {
    "path": "bson/extjson_tables.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n//\n// Based on github.com/golang/go by The Go Authors\n// See THIRD-PARTY-NOTICES for original license terms.\n\npackage bson\n\nimport \"unicode/utf8\"\n\n// safeSet holds the value true if the ASCII character with the given array\n// position can be represented inside a JSON string without any further\n// escaping.\n//\n// All values are true except for the ASCII control characters (0-31), the\n// double quote (\"), and the backslash character (\"\\\").\nvar safeSet = [utf8.RuneSelf]bool{\n\t' ':      true,\n\t'!':      true,\n\t'\"':      false,\n\t'#':      true,\n\t'$':      true,\n\t'%':      true,\n\t'&':      true,\n\t'\\'':     true,\n\t'(':      true,\n\t')':      true,\n\t'*':      true,\n\t'+':      true,\n\t',':      true,\n\t'-':      true,\n\t'.':      true,\n\t'/':      true,\n\t'0':      true,\n\t'1':      true,\n\t'2':      true,\n\t'3':      true,\n\t'4':      true,\n\t'5':      true,\n\t'6':      true,\n\t'7':      true,\n\t'8':      true,\n\t'9':      true,\n\t':':      true,\n\t';':      true,\n\t'<':      true,\n\t'=':      true,\n\t'>':      true,\n\t'?':      true,\n\t'@':      true,\n\t'A':      true,\n\t'B':      true,\n\t'C':      true,\n\t'D':      true,\n\t'E':      true,\n\t'F':      true,\n\t'G':      true,\n\t'H':      true,\n\t'I':      true,\n\t'J':      true,\n\t'K':      true,\n\t'L':      true,\n\t'M':      true,\n\t'N':      true,\n\t'O':      true,\n\t'P':      true,\n\t'Q':      true,\n\t'R':      true,\n\t'S':      true,\n\t'T':      true,\n\t'U':      true,\n\t'V':      true,\n\t'W':      true,\n\t'X':      true,\n\t'Y':      true,\n\t'Z':      true,\n\t'[':      true,\n\t'\\\\':     false,\n\t']':      true,\n\t'^':      true,\n\t'_':      true,\n\t'`':      true,\n\t'a':      true,\n\t'b':      true,\n\t'c':      true,\n\t'd':      true,\n\t'e':      true,\n\t'f':      true,\n\t'g':      true,\n\t'h':      true,\n\t'i':      true,\n\t'j':      true,\n\t'k':      true,\n\t'l':      true,\n\t'm':      true,\n\t'n':      true,\n\t'o':      true,\n\t'p':      true,\n\t'q':      true,\n\t'r':      true,\n\t's':      true,\n\t't':      true,\n\t'u':      true,\n\t'v':      true,\n\t'w':      true,\n\t'x':      true,\n\t'y':      true,\n\t'z':      true,\n\t'{':      true,\n\t'|':      true,\n\t'}':      true,\n\t'~':      true,\n\t'\\u007f': true,\n}\n\n// htmlSafeSet holds the value true if the ASCII character with the given\n// array position can be safely represented inside a JSON string, embedded\n// inside of HTML <script> tags, without any additional escaping.\n//\n// All values are true except for the ASCII control characters (0-31), the\n// double quote (\"), the backslash character (\"\\\"), HTML opening and closing\n// tags (\"<\" and \">\"), and the ampersand (\"&\").\nvar htmlSafeSet = [utf8.RuneSelf]bool{\n\t' ':      true,\n\t'!':      true,\n\t'\"':      false,\n\t'#':      true,\n\t'$':      true,\n\t'%':      true,\n\t'&':      false,\n\t'\\'':     true,\n\t'(':      true,\n\t')':      true,\n\t'*':      true,\n\t'+':      true,\n\t',':      true,\n\t'-':      true,\n\t'.':      true,\n\t'/':      true,\n\t'0':      true,\n\t'1':      true,\n\t'2':      true,\n\t'3':      true,\n\t'4':      true,\n\t'5':      true,\n\t'6':      true,\n\t'7':      true,\n\t'8':      true,\n\t'9':      true,\n\t':':      true,\n\t';':      true,\n\t'<':      false,\n\t'=':      true,\n\t'>':      false,\n\t'?':      true,\n\t'@':      true,\n\t'A':      true,\n\t'B':      true,\n\t'C':      true,\n\t'D':      true,\n\t'E':      true,\n\t'F':      true,\n\t'G':      true,\n\t'H':      true,\n\t'I':      true,\n\t'J':      true,\n\t'K':      true,\n\t'L':      true,\n\t'M':      true,\n\t'N':      true,\n\t'O':      true,\n\t'P':      true,\n\t'Q':      true,\n\t'R':      true,\n\t'S':      true,\n\t'T':      true,\n\t'U':      true,\n\t'V':      true,\n\t'W':      true,\n\t'X':      true,\n\t'Y':      true,\n\t'Z':      true,\n\t'[':      true,\n\t'\\\\':     false,\n\t']':      true,\n\t'^':      true,\n\t'_':      true,\n\t'`':      true,\n\t'a':      true,\n\t'b':      true,\n\t'c':      true,\n\t'd':      true,\n\t'e':      true,\n\t'f':      true,\n\t'g':      true,\n\t'h':      true,\n\t'i':      true,\n\t'j':      true,\n\t'k':      true,\n\t'l':      true,\n\t'm':      true,\n\t'n':      true,\n\t'o':      true,\n\t'p':      true,\n\t'q':      true,\n\t'r':      true,\n\t's':      true,\n\t't':      true,\n\t'u':      true,\n\t'v':      true,\n\t'w':      true,\n\t'x':      true,\n\t'y':      true,\n\t'z':      true,\n\t'{':      true,\n\t'|':      true,\n\t'}':      true,\n\t'~':      true,\n\t'\\u007f': true,\n}\n"
  },
  {
    "path": "bson/extjson_wrappers.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bson\n\nimport (\n\t\"encoding/base64\"\n\t\"errors\"\n\t\"fmt\"\n\t\"math\"\n\t\"strconv\"\n\t\"time\"\n)\n\nfunc wrapperKeyBSONType(key string) Type {\n\tswitch key {\n\tcase \"$numberInt\":\n\t\treturn TypeInt32\n\tcase \"$numberLong\":\n\t\treturn TypeInt64\n\tcase \"$oid\":\n\t\treturn TypeObjectID\n\tcase \"$symbol\":\n\t\treturn TypeSymbol\n\tcase \"$numberDouble\":\n\t\treturn TypeDouble\n\tcase \"$numberDecimal\":\n\t\treturn TypeDecimal128\n\tcase \"$binary\":\n\t\treturn TypeBinary\n\tcase \"$code\":\n\t\treturn TypeJavaScript\n\tcase \"$scope\":\n\t\treturn TypeCodeWithScope\n\tcase \"$timestamp\":\n\t\treturn TypeTimestamp\n\tcase \"$regularExpression\":\n\t\treturn TypeRegex\n\tcase \"$dbPointer\":\n\t\treturn TypeDBPointer\n\tcase \"$date\":\n\t\treturn TypeDateTime\n\tcase \"$minKey\":\n\t\treturn TypeMinKey\n\tcase \"$maxKey\":\n\t\treturn TypeMaxKey\n\tcase \"$undefined\":\n\t\treturn TypeUndefined\n\t}\n\n\treturn TypeEmbeddedDocument\n}\n\nfunc (ejv *extJSONValue) parseBinary() (b []byte, subType byte, err error) {\n\tif ejv.t != TypeEmbeddedDocument {\n\t\treturn nil, 0, fmt.Errorf(\"$binary value should be object, but instead is %s\", ejv.t)\n\t}\n\n\tbinObj := ejv.v.(*extJSONObject)\n\tbFound := false\n\tstFound := false\n\n\tfor i, key := range binObj.keys {\n\t\tval := binObj.values[i]\n\n\t\tswitch key {\n\t\tcase \"base64\":\n\t\t\tif bFound {\n\t\t\t\treturn nil, 0, errors.New(\"duplicate base64 key in $binary\")\n\t\t\t}\n\n\t\t\tif val.t != TypeString {\n\t\t\t\treturn nil, 0, fmt.Errorf(\"$binary base64 value should be string, but instead is %s\", val.t)\n\t\t\t}\n\n\t\t\tbase64Bytes, err := base64.StdEncoding.DecodeString(val.v.(string))\n\t\t\tif err != nil {\n\t\t\t\treturn nil, 0, fmt.Errorf(\"invalid $binary base64 string: %s\", val.v.(string))\n\t\t\t}\n\n\t\t\tb = base64Bytes\n\t\t\tbFound = true\n\t\tcase \"subType\":\n\t\t\tif stFound {\n\t\t\t\treturn nil, 0, errors.New(\"duplicate subType key in $binary\")\n\t\t\t}\n\n\t\t\tif val.t != TypeString {\n\t\t\t\treturn nil, 0, fmt.Errorf(\"$binary subType value should be string, but instead is %s\", val.t)\n\t\t\t}\n\n\t\t\ti, err := strconv.ParseUint(val.v.(string), 16, 8)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, 0, fmt.Errorf(\"invalid $binary subType string: %q: %w\", val.v.(string), err)\n\t\t\t}\n\n\t\t\tsubType = byte(i)\n\t\t\tstFound = true\n\t\tdefault:\n\t\t\treturn nil, 0, fmt.Errorf(\"invalid key in $binary object: %s\", key)\n\t\t}\n\t}\n\n\tif !bFound {\n\t\treturn nil, 0, errors.New(\"missing base64 field in $binary object\")\n\t}\n\n\tif !stFound {\n\t\treturn nil, 0, errors.New(\"missing subType field in $binary object\")\n\t}\n\n\treturn b, subType, nil\n}\n\nfunc (ejv *extJSONValue) parseDBPointer() (ns string, oid ObjectID, err error) {\n\tif ejv.t != TypeEmbeddedDocument {\n\t\treturn \"\", NilObjectID, fmt.Errorf(\"$dbPointer value should be object, but instead is %s\", ejv.t)\n\t}\n\n\tdbpObj := ejv.v.(*extJSONObject)\n\toidFound := false\n\tnsFound := false\n\n\tfor i, key := range dbpObj.keys {\n\t\tval := dbpObj.values[i]\n\n\t\tswitch key {\n\t\tcase \"$ref\":\n\t\t\tif nsFound {\n\t\t\t\treturn \"\", NilObjectID, errors.New(\"duplicate $ref key in $dbPointer\")\n\t\t\t}\n\n\t\t\tif val.t != TypeString {\n\t\t\t\treturn \"\", NilObjectID, fmt.Errorf(\"$dbPointer $ref value should be string, but instead is %s\", val.t)\n\t\t\t}\n\n\t\t\tns = val.v.(string)\n\t\t\tnsFound = true\n\t\tcase \"$id\":\n\t\t\tif oidFound {\n\t\t\t\treturn \"\", NilObjectID, errors.New(\"duplicate $id key in $dbPointer\")\n\t\t\t}\n\n\t\t\tif val.t != TypeString {\n\t\t\t\treturn \"\", NilObjectID, fmt.Errorf(\"$dbPointer $id value should be string, but instead is %s\", val.t)\n\t\t\t}\n\n\t\t\toid, err = ObjectIDFromHex(val.v.(string))\n\t\t\tif err != nil {\n\t\t\t\treturn \"\", NilObjectID, err\n\t\t\t}\n\n\t\t\toidFound = true\n\t\tdefault:\n\t\t\treturn \"\", NilObjectID, fmt.Errorf(\"invalid key in $dbPointer object: %s\", key)\n\t\t}\n\t}\n\n\tif !nsFound {\n\t\treturn \"\", oid, errors.New(\"missing $ref field in $dbPointer object\")\n\t}\n\n\tif !oidFound {\n\t\treturn \"\", oid, errors.New(\"missing $id field in $dbPointer object\")\n\t}\n\n\treturn ns, oid, nil\n}\n\nconst (\n\trfc3339Milli = \"2006-01-02T15:04:05.999Z07:00\"\n)\n\nvar timeFormats = []string{rfc3339Milli, \"2006-01-02T15:04:05.999Z0700\"}\n\nfunc (ejv *extJSONValue) parseDateTime() (int64, error) {\n\tswitch ejv.t {\n\tcase TypeInt32:\n\t\treturn int64(ejv.v.(int32)), nil\n\tcase TypeInt64:\n\t\treturn ejv.v.(int64), nil\n\tcase TypeString:\n\t\treturn parseDatetimeString(ejv.v.(string))\n\tcase TypeEmbeddedDocument:\n\t\treturn parseDatetimeObject(ejv.v.(*extJSONObject))\n\tdefault:\n\t\treturn 0, fmt.Errorf(\"$date value should be string or object, but instead is %s\", ejv.t)\n\t}\n}\n\nfunc parseDatetimeString(data string) (int64, error) {\n\tvar t time.Time\n\tvar err error\n\t// try acceptable time formats until one matches\n\tfor _, format := range timeFormats {\n\t\tt, err = time.Parse(format, data)\n\t\tif err == nil {\n\t\t\tbreak\n\t\t}\n\t}\n\tif err != nil {\n\t\treturn 0, fmt.Errorf(\"invalid $date value string: %s\", data)\n\t}\n\n\treturn int64(NewDateTimeFromTime(t)), nil\n}\n\nfunc parseDatetimeObject(data *extJSONObject) (d int64, err error) {\n\tdFound := false\n\n\tfor i, key := range data.keys {\n\t\tval := data.values[i]\n\n\t\tswitch key {\n\t\tcase \"$numberLong\":\n\t\t\tif dFound {\n\t\t\t\treturn 0, errors.New(\"duplicate $numberLong key in $date\")\n\t\t\t}\n\n\t\t\tif val.t != TypeString {\n\t\t\t\treturn 0, fmt.Errorf(\"$date $numberLong field should be string, but instead is %s\", val.t)\n\t\t\t}\n\n\t\t\td, err = val.parseInt64()\n\t\t\tif err != nil {\n\t\t\t\treturn 0, err\n\t\t\t}\n\t\t\tdFound = true\n\t\tdefault:\n\t\t\treturn 0, fmt.Errorf(\"invalid key in $date object: %s\", key)\n\t\t}\n\t}\n\n\tif !dFound {\n\t\treturn 0, errors.New(\"missing $numberLong field in $date object\")\n\t}\n\n\treturn d, nil\n}\n\nfunc (ejv *extJSONValue) parseDecimal128() (Decimal128, error) {\n\tif ejv.t != TypeString {\n\t\treturn Decimal128{}, fmt.Errorf(\"$numberDecimal value should be string, but instead is %s\", ejv.t)\n\t}\n\n\td, err := ParseDecimal128(ejv.v.(string))\n\tif err != nil {\n\t\treturn Decimal128{}, fmt.Errorf(\"$invalid $numberDecimal string: %s\", ejv.v.(string))\n\t}\n\n\treturn d, nil\n}\n\nfunc (ejv *extJSONValue) parseDouble() (float64, error) {\n\tif ejv.t == TypeDouble {\n\t\treturn ejv.v.(float64), nil\n\t}\n\n\tif ejv.t != TypeString {\n\t\treturn 0, fmt.Errorf(\"$numberDouble value should be string, but instead is %s\", ejv.t)\n\t}\n\n\tswitch ejv.v.(string) {\n\tcase \"Infinity\":\n\t\treturn math.Inf(1), nil\n\tcase \"-Infinity\":\n\t\treturn math.Inf(-1), nil\n\tcase \"NaN\":\n\t\treturn math.NaN(), nil\n\t}\n\n\tf, err := strconv.ParseFloat(ejv.v.(string), 64)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\treturn f, nil\n}\n\nfunc (ejv *extJSONValue) parseInt32() (int32, error) {\n\tif ejv.t == TypeInt32 {\n\t\treturn ejv.v.(int32), nil\n\t}\n\n\tif ejv.t != TypeString {\n\t\treturn 0, fmt.Errorf(\"$numberInt value should be string, but instead is %s\", ejv.t)\n\t}\n\n\ti, err := strconv.ParseInt(ejv.v.(string), 10, 64)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\tif i < math.MinInt32 || i > math.MaxInt32 {\n\t\treturn 0, fmt.Errorf(\"$numberInt value should be int32 but instead is int64: %d\", i)\n\t}\n\n\treturn int32(i), nil\n}\n\nfunc (ejv *extJSONValue) parseInt64() (int64, error) {\n\tif ejv.t == TypeInt64 {\n\t\treturn ejv.v.(int64), nil\n\t}\n\n\tif ejv.t != TypeString {\n\t\treturn 0, fmt.Errorf(\"$numberLong value should be string, but instead is %s\", ejv.t)\n\t}\n\n\ti, err := strconv.ParseInt(ejv.v.(string), 10, 64)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\treturn i, nil\n}\n\nfunc (ejv *extJSONValue) parseJavascript() (code string, err error) {\n\tif ejv.t != TypeString {\n\t\treturn \"\", fmt.Errorf(\"$code value should be string, but instead is %s\", ejv.t)\n\t}\n\n\treturn ejv.v.(string), nil\n}\n\nfunc (ejv *extJSONValue) parseMinMaxKey(minmax string) error {\n\tif ejv.t != TypeInt32 {\n\t\treturn fmt.Errorf(\"$%sKey value should be int32, but instead is %s\", minmax, ejv.t)\n\t}\n\n\tif ejv.v.(int32) != 1 {\n\t\treturn fmt.Errorf(\"$%sKey value must be 1, but instead is %d\", minmax, ejv.v.(int32))\n\t}\n\n\treturn nil\n}\n\nfunc (ejv *extJSONValue) parseObjectID() (ObjectID, error) {\n\tif ejv.t != TypeString {\n\t\treturn NilObjectID, fmt.Errorf(\"$oid value should be string, but instead is %s\", ejv.t)\n\t}\n\n\treturn ObjectIDFromHex(ejv.v.(string))\n}\n\nfunc (ejv *extJSONValue) parseRegex() (pattern, options string, err error) {\n\tif ejv.t != TypeEmbeddedDocument {\n\t\treturn \"\", \"\", fmt.Errorf(\"$regularExpression value should be object, but instead is %s\", ejv.t)\n\t}\n\n\tregexObj := ejv.v.(*extJSONObject)\n\tpatFound := false\n\toptFound := false\n\n\tfor i, key := range regexObj.keys {\n\t\tval := regexObj.values[i]\n\n\t\tswitch key {\n\t\tcase \"pattern\":\n\t\t\tif patFound {\n\t\t\t\treturn \"\", \"\", errors.New(\"duplicate pattern key in $regularExpression\")\n\t\t\t}\n\n\t\t\tif val.t != TypeString {\n\t\t\t\treturn \"\", \"\", fmt.Errorf(\"$regularExpression pattern value should be string, but instead is %s\", val.t)\n\t\t\t}\n\n\t\t\tpattern = val.v.(string)\n\t\t\tpatFound = true\n\t\tcase \"options\":\n\t\t\tif optFound {\n\t\t\t\treturn \"\", \"\", errors.New(\"duplicate options key in $regularExpression\")\n\t\t\t}\n\n\t\t\tif val.t != TypeString {\n\t\t\t\treturn \"\", \"\", fmt.Errorf(\"$regularExpression options value should be string, but instead is %s\", val.t)\n\t\t\t}\n\n\t\t\toptions = val.v.(string)\n\t\t\toptFound = true\n\t\tdefault:\n\t\t\treturn \"\", \"\", fmt.Errorf(\"invalid key in $regularExpression object: %s\", key)\n\t\t}\n\t}\n\n\tif !patFound {\n\t\treturn \"\", \"\", errors.New(\"missing pattern field in $regularExpression object\")\n\t}\n\n\tif !optFound {\n\t\treturn \"\", \"\", errors.New(\"missing options field in $regularExpression object\")\n\t}\n\n\treturn pattern, options, nil\n}\n\nfunc (ejv *extJSONValue) parseSymbol() (string, error) {\n\tif ejv.t != TypeString {\n\t\treturn \"\", fmt.Errorf(\"$symbol value should be string, but instead is %s\", ejv.t)\n\t}\n\n\treturn ejv.v.(string), nil\n}\n\nfunc (ejv *extJSONValue) parseTimestamp() (t, i uint32, err error) {\n\tif ejv.t != TypeEmbeddedDocument {\n\t\treturn 0, 0, fmt.Errorf(\"$timestamp value should be object, but instead is %s\", ejv.t)\n\t}\n\n\thandleKey := func(key string, val *extJSONValue, flag bool) (uint32, error) {\n\t\tif flag {\n\t\t\treturn 0, fmt.Errorf(\"duplicate %s key in $timestamp\", key)\n\t\t}\n\n\t\tswitch val.t {\n\t\tcase TypeInt32:\n\t\t\tvalue := val.v.(int32)\n\n\t\t\tif value < 0 {\n\t\t\t\treturn 0, fmt.Errorf(\"$timestamp %s number should be uint32: %d\", key, value)\n\t\t\t}\n\n\t\t\treturn uint32(value), nil\n\t\tcase TypeInt64:\n\t\t\tvalue := val.v.(int64)\n\t\t\tif value < 0 || value > int64(math.MaxUint32) {\n\t\t\t\treturn 0, fmt.Errorf(\"$timestamp %s number should be uint32: %d\", key, value)\n\t\t\t}\n\n\t\t\treturn uint32(value), nil\n\t\tdefault:\n\t\t\treturn 0, fmt.Errorf(\"$timestamp %s value should be uint32, but instead is %s\", key, val.t)\n\t\t}\n\t}\n\n\ttsObj := ejv.v.(*extJSONObject)\n\ttFound := false\n\tiFound := false\n\n\tfor j, key := range tsObj.keys {\n\t\tval := tsObj.values[j]\n\n\t\tswitch key {\n\t\tcase \"t\":\n\t\t\tif t, err = handleKey(key, val, tFound); err != nil {\n\t\t\t\treturn 0, 0, err\n\t\t\t}\n\n\t\t\ttFound = true\n\t\tcase \"i\":\n\t\t\tif i, err = handleKey(key, val, iFound); err != nil {\n\t\t\t\treturn 0, 0, err\n\t\t\t}\n\n\t\t\tiFound = true\n\t\tdefault:\n\t\t\treturn 0, 0, fmt.Errorf(\"invalid key in $timestamp object: %s\", key)\n\t\t}\n\t}\n\n\tif !tFound {\n\t\treturn 0, 0, errors.New(\"missing t field in $timestamp object\")\n\t}\n\n\tif !iFound {\n\t\treturn 0, 0, errors.New(\"missing i field in $timestamp object\")\n\t}\n\n\treturn t, i, nil\n}\n\nfunc (ejv *extJSONValue) parseUndefined() error {\n\tif ejv.t != TypeBoolean {\n\t\treturn fmt.Errorf(\"undefined value should be boolean, but instead is %s\", ejv.t)\n\t}\n\n\tif !ejv.v.(bool) {\n\t\treturn fmt.Errorf(\"$undefined balue boolean should be true, but instead is %v\", ejv.v.(bool))\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "bson/extjson_writer.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bson\n\nimport (\n\t\"bytes\"\n\t\"encoding/base64\"\n\t\"fmt\"\n\t\"io\"\n\t\"math\"\n\t\"sort\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\t\"unicode/utf8\"\n)\n\ntype ejvwState struct {\n\tmode mode\n}\n\ntype extJSONValueWriter struct {\n\tw   io.Writer\n\tbuf []byte\n\n\tstack      []ejvwState\n\tframe      int64\n\tcanonical  bool\n\tescapeHTML bool\n\tnewlines   bool\n}\n\n// NewExtJSONValueWriter creates a ValueWriter that writes Extended JSON to w.\nfunc NewExtJSONValueWriter(w io.Writer, canonical, escapeHTML bool) ValueWriter {\n\t// Enable newlines for all Extended JSON value writers created by NewExtJSONValueWriter. We\n\t// expect these value writers to be used with an Encoder, which should add newlines after\n\t// encoded Extended JSON documents.\n\treturn newExtJSONWriter(w, canonical, escapeHTML, true)\n}\n\nfunc newExtJSONWriter(w io.Writer, canonical, escapeHTML, newlines bool) *extJSONValueWriter {\n\tstack := make([]ejvwState, 1, 5)\n\tstack[0] = ejvwState{mode: mTopLevel}\n\n\treturn &extJSONValueWriter{\n\t\tw:          w,\n\t\tbuf:        []byte{},\n\t\tstack:      stack,\n\t\tcanonical:  canonical,\n\t\tescapeHTML: escapeHTML,\n\t\tnewlines:   newlines,\n\t}\n}\n\nfunc newExtJSONWriterFromSlice(buf []byte, canonical, escapeHTML bool) *extJSONValueWriter {\n\tstack := make([]ejvwState, 1, 5)\n\tstack[0] = ejvwState{mode: mTopLevel}\n\n\treturn &extJSONValueWriter{\n\t\tbuf:        buf,\n\t\tstack:      stack,\n\t\tcanonical:  canonical,\n\t\tescapeHTML: escapeHTML,\n\t}\n}\n\nfunc (ejvw *extJSONValueWriter) reset(buf []byte, canonical, escapeHTML bool) {\n\tif ejvw.stack == nil {\n\t\tejvw.stack = make([]ejvwState, 1, 5)\n\t}\n\n\tejvw.stack = ejvw.stack[:1]\n\tejvw.stack[0] = ejvwState{mode: mTopLevel}\n\tejvw.canonical = canonical\n\tejvw.escapeHTML = escapeHTML\n\tejvw.frame = 0\n\tejvw.buf = buf\n\tejvw.w = nil\n}\n\nfunc (ejvw *extJSONValueWriter) advanceFrame() {\n\tif ejvw.frame+1 >= int64(len(ejvw.stack)) { // We need to grow the stack\n\t\tlength := len(ejvw.stack)\n\t\tif length+1 >= cap(ejvw.stack) {\n\t\t\t// double it\n\t\t\tbuf := make([]ejvwState, 2*cap(ejvw.stack)+1)\n\t\t\tcopy(buf, ejvw.stack)\n\t\t\tejvw.stack = buf\n\t\t}\n\t\tejvw.stack = ejvw.stack[:length+1]\n\t}\n\tejvw.frame++\n}\n\nfunc (ejvw *extJSONValueWriter) push(m mode) {\n\tejvw.advanceFrame()\n\n\tejvw.stack[ejvw.frame].mode = m\n}\n\nfunc (ejvw *extJSONValueWriter) pop() {\n\tswitch ejvw.stack[ejvw.frame].mode {\n\tcase mElement, mValue:\n\t\tejvw.frame--\n\tcase mDocument, mArray, mCodeWithScope:\n\t\tejvw.frame -= 2 // we pop twice to jump over the mElement: mDocument -> mElement -> mDocument/mTopLevel/etc...\n\t}\n}\n\nfunc (ejvw *extJSONValueWriter) invalidTransitionErr(destination mode, name string, modes []mode) error {\n\tte := TransitionError{\n\t\tname:        name,\n\t\tcurrent:     ejvw.stack[ejvw.frame].mode,\n\t\tdestination: destination,\n\t\tmodes:       modes,\n\t\taction:      \"write\",\n\t}\n\tif ejvw.frame != 0 {\n\t\tte.parent = ejvw.stack[ejvw.frame-1].mode\n\t}\n\treturn te\n}\n\nfunc (ejvw *extJSONValueWriter) ensureElementValue(destination mode, callerName string, addmodes ...mode) error {\n\tswitch ejvw.stack[ejvw.frame].mode {\n\tcase mElement, mValue:\n\tdefault:\n\t\tmodes := []mode{mElement, mValue}\n\t\tif addmodes != nil {\n\t\t\tmodes = append(modes, addmodes...)\n\t\t}\n\t\treturn ejvw.invalidTransitionErr(destination, callerName, modes)\n\t}\n\n\treturn nil\n}\n\nfunc (ejvw *extJSONValueWriter) writeExtendedSingleValue(key string, value string, quotes bool) {\n\tvar s string\n\tif quotes {\n\t\ts = fmt.Sprintf(`{\"$%s\":\"%s\"}`, key, value)\n\t} else {\n\t\ts = fmt.Sprintf(`{\"$%s\":%s}`, key, value)\n\t}\n\n\tejvw.buf = append(ejvw.buf, []byte(s)...)\n}\n\nfunc (ejvw *extJSONValueWriter) WriteArray() (ArrayWriter, error) {\n\tif err := ejvw.ensureElementValue(mArray, \"WriteArray\"); err != nil {\n\t\treturn nil, err\n\t}\n\n\tejvw.buf = append(ejvw.buf, '[')\n\n\tejvw.push(mArray)\n\treturn ejvw, nil\n}\n\nfunc (ejvw *extJSONValueWriter) WriteBinary(b []byte) error {\n\treturn ejvw.WriteBinaryWithSubtype(b, 0x00)\n}\n\nfunc (ejvw *extJSONValueWriter) WriteBinaryWithSubtype(b []byte, btype byte) error {\n\tif err := ejvw.ensureElementValue(mode(0), \"WriteBinaryWithSubtype\"); err != nil {\n\t\treturn err\n\t}\n\n\tvar buf bytes.Buffer\n\tbuf.WriteString(`{\"$binary\":{\"base64\":\"`)\n\tbuf.WriteString(base64.StdEncoding.EncodeToString(b))\n\tbuf.WriteString(fmt.Sprintf(`\",\"subType\":\"%02x\"}},`, btype))\n\n\tejvw.buf = append(ejvw.buf, buf.Bytes()...)\n\n\tejvw.pop()\n\treturn nil\n}\n\nfunc (ejvw *extJSONValueWriter) WriteBoolean(b bool) error {\n\tif err := ejvw.ensureElementValue(mode(0), \"WriteBoolean\"); err != nil {\n\t\treturn err\n\t}\n\n\tejvw.buf = append(ejvw.buf, []byte(strconv.FormatBool(b))...)\n\tejvw.buf = append(ejvw.buf, ',')\n\n\tejvw.pop()\n\treturn nil\n}\n\nfunc (ejvw *extJSONValueWriter) WriteCodeWithScope(code string) (DocumentWriter, error) {\n\tif err := ejvw.ensureElementValue(mCodeWithScope, \"WriteCodeWithScope\"); err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar buf bytes.Buffer\n\tbuf.WriteString(`{\"$code\":`)\n\twriteStringWithEscapes(code, &buf, ejvw.escapeHTML)\n\tbuf.WriteString(`,\"$scope\":{`)\n\n\tejvw.buf = append(ejvw.buf, buf.Bytes()...)\n\n\tejvw.push(mCodeWithScope)\n\treturn ejvw, nil\n}\n\nfunc (ejvw *extJSONValueWriter) WriteDBPointer(ns string, oid ObjectID) error {\n\tif err := ejvw.ensureElementValue(mode(0), \"WriteDBPointer\"); err != nil {\n\t\treturn err\n\t}\n\n\tvar buf bytes.Buffer\n\tbuf.WriteString(`{\"$dbPointer\":{\"$ref\":\"`)\n\tbuf.WriteString(ns)\n\tbuf.WriteString(`\",\"$id\":{\"$oid\":\"`)\n\tbuf.WriteString(oid.Hex())\n\tbuf.WriteString(`\"}}},`)\n\n\tejvw.buf = append(ejvw.buf, buf.Bytes()...)\n\n\tejvw.pop()\n\treturn nil\n}\n\nfunc (ejvw *extJSONValueWriter) WriteDateTime(dt int64) error {\n\tif err := ejvw.ensureElementValue(mode(0), \"WriteDateTime\"); err != nil {\n\t\treturn err\n\t}\n\n\tt := time.Unix(dt/1e3, dt%1e3*1e6).UTC()\n\n\tif ejvw.canonical || t.Year() < 1970 || t.Year() > 9999 {\n\t\ts := fmt.Sprintf(`{\"$numberLong\":\"%d\"}`, dt)\n\t\tejvw.writeExtendedSingleValue(\"date\", s, false)\n\t} else {\n\t\tejvw.writeExtendedSingleValue(\"date\", t.Format(rfc3339Milli), true)\n\t}\n\n\tejvw.buf = append(ejvw.buf, ',')\n\n\tejvw.pop()\n\treturn nil\n}\n\nfunc (ejvw *extJSONValueWriter) WriteDecimal128(d Decimal128) error {\n\tif err := ejvw.ensureElementValue(mode(0), \"WriteDecimal128\"); err != nil {\n\t\treturn err\n\t}\n\n\tejvw.writeExtendedSingleValue(\"numberDecimal\", d.String(), true)\n\tejvw.buf = append(ejvw.buf, ',')\n\n\tejvw.pop()\n\treturn nil\n}\n\nfunc (ejvw *extJSONValueWriter) WriteDocument() (DocumentWriter, error) {\n\tif ejvw.stack[ejvw.frame].mode == mTopLevel {\n\t\tejvw.buf = append(ejvw.buf, '{')\n\t\treturn ejvw, nil\n\t}\n\n\tif err := ejvw.ensureElementValue(mDocument, \"WriteDocument\", mTopLevel); err != nil {\n\t\treturn nil, err\n\t}\n\n\tejvw.buf = append(ejvw.buf, '{')\n\tejvw.push(mDocument)\n\treturn ejvw, nil\n}\n\nfunc (ejvw *extJSONValueWriter) WriteDouble(f float64) error {\n\tif err := ejvw.ensureElementValue(mode(0), \"WriteDouble\"); err != nil {\n\t\treturn err\n\t}\n\n\ts := formatDouble(f)\n\n\tif ejvw.canonical {\n\t\tejvw.writeExtendedSingleValue(\"numberDouble\", s, true)\n\t} else {\n\t\tswitch s {\n\t\tcase \"Infinity\":\n\t\t\tfallthrough\n\t\tcase \"-Infinity\":\n\t\t\tfallthrough\n\t\tcase \"NaN\":\n\t\t\ts = fmt.Sprintf(`{\"$numberDouble\":\"%s\"}`, s)\n\t\t}\n\t\tejvw.buf = append(ejvw.buf, []byte(s)...)\n\t}\n\n\tejvw.buf = append(ejvw.buf, ',')\n\n\tejvw.pop()\n\treturn nil\n}\n\nfunc (ejvw *extJSONValueWriter) WriteInt32(i int32) error {\n\tif err := ejvw.ensureElementValue(mode(0), \"WriteInt32\"); err != nil {\n\t\treturn err\n\t}\n\n\ts := strconv.FormatInt(int64(i), 10)\n\n\tif ejvw.canonical {\n\t\tejvw.writeExtendedSingleValue(\"numberInt\", s, true)\n\t} else {\n\t\tejvw.buf = append(ejvw.buf, []byte(s)...)\n\t}\n\n\tejvw.buf = append(ejvw.buf, ',')\n\n\tejvw.pop()\n\treturn nil\n}\n\nfunc (ejvw *extJSONValueWriter) WriteInt64(i int64) error {\n\tif err := ejvw.ensureElementValue(mode(0), \"WriteInt64\"); err != nil {\n\t\treturn err\n\t}\n\n\ts := strconv.FormatInt(i, 10)\n\n\tif ejvw.canonical {\n\t\tejvw.writeExtendedSingleValue(\"numberLong\", s, true)\n\t} else {\n\t\tejvw.buf = append(ejvw.buf, []byte(s)...)\n\t}\n\n\tejvw.buf = append(ejvw.buf, ',')\n\n\tejvw.pop()\n\treturn nil\n}\n\nfunc (ejvw *extJSONValueWriter) WriteJavascript(code string) error {\n\tif err := ejvw.ensureElementValue(mode(0), \"WriteJavascript\"); err != nil {\n\t\treturn err\n\t}\n\n\tvar buf bytes.Buffer\n\twriteStringWithEscapes(code, &buf, ejvw.escapeHTML)\n\n\tejvw.writeExtendedSingleValue(\"code\", buf.String(), false)\n\tejvw.buf = append(ejvw.buf, ',')\n\n\tejvw.pop()\n\treturn nil\n}\n\nfunc (ejvw *extJSONValueWriter) WriteMaxKey() error {\n\tif err := ejvw.ensureElementValue(mode(0), \"WriteMaxKey\"); err != nil {\n\t\treturn err\n\t}\n\n\tejvw.writeExtendedSingleValue(\"maxKey\", \"1\", false)\n\tejvw.buf = append(ejvw.buf, ',')\n\n\tejvw.pop()\n\treturn nil\n}\n\nfunc (ejvw *extJSONValueWriter) WriteMinKey() error {\n\tif err := ejvw.ensureElementValue(mode(0), \"WriteMinKey\"); err != nil {\n\t\treturn err\n\t}\n\n\tejvw.writeExtendedSingleValue(\"minKey\", \"1\", false)\n\tejvw.buf = append(ejvw.buf, ',')\n\n\tejvw.pop()\n\treturn nil\n}\n\nfunc (ejvw *extJSONValueWriter) WriteNull() error {\n\tif err := ejvw.ensureElementValue(mode(0), \"WriteNull\"); err != nil {\n\t\treturn err\n\t}\n\n\tejvw.buf = append(ejvw.buf, []byte(\"null\")...)\n\tejvw.buf = append(ejvw.buf, ',')\n\n\tejvw.pop()\n\treturn nil\n}\n\nfunc (ejvw *extJSONValueWriter) WriteObjectID(oid ObjectID) error {\n\tif err := ejvw.ensureElementValue(mode(0), \"WriteObjectID\"); err != nil {\n\t\treturn err\n\t}\n\n\tejvw.writeExtendedSingleValue(\"oid\", oid.Hex(), true)\n\tejvw.buf = append(ejvw.buf, ',')\n\n\tejvw.pop()\n\treturn nil\n}\n\nfunc (ejvw *extJSONValueWriter) WriteRegex(pattern string, options string) error {\n\tif err := ejvw.ensureElementValue(mode(0), \"WriteRegex\"); err != nil {\n\t\treturn err\n\t}\n\n\toptions = sortStringAlphebeticAscending(options)\n\tvar buf bytes.Buffer\n\tbuf.WriteString(`{\"$regularExpression\":{\"pattern\":`)\n\twriteStringWithEscapes(pattern, &buf, ejvw.escapeHTML)\n\tbuf.WriteString(`,\"options\":`)\n\twriteStringWithEscapes(options, &buf, ejvw.escapeHTML)\n\tbuf.WriteString(`}},`)\n\n\tejvw.buf = append(ejvw.buf, buf.Bytes()...)\n\n\tejvw.pop()\n\treturn nil\n}\n\nfunc (ejvw *extJSONValueWriter) WriteString(s string) error {\n\tif err := ejvw.ensureElementValue(mode(0), \"WriteString\"); err != nil {\n\t\treturn err\n\t}\n\n\tvar buf bytes.Buffer\n\twriteStringWithEscapes(s, &buf, ejvw.escapeHTML)\n\n\tejvw.buf = append(ejvw.buf, buf.Bytes()...)\n\tejvw.buf = append(ejvw.buf, ',')\n\n\tejvw.pop()\n\treturn nil\n}\n\nfunc (ejvw *extJSONValueWriter) WriteSymbol(symbol string) error {\n\tif err := ejvw.ensureElementValue(mode(0), \"WriteSymbol\"); err != nil {\n\t\treturn err\n\t}\n\n\tvar buf bytes.Buffer\n\twriteStringWithEscapes(symbol, &buf, ejvw.escapeHTML)\n\n\tejvw.writeExtendedSingleValue(\"symbol\", buf.String(), false)\n\tejvw.buf = append(ejvw.buf, ',')\n\n\tejvw.pop()\n\treturn nil\n}\n\nfunc (ejvw *extJSONValueWriter) WriteTimestamp(t uint32, i uint32) error {\n\tif err := ejvw.ensureElementValue(mode(0), \"WriteTimestamp\"); err != nil {\n\t\treturn err\n\t}\n\n\tvar buf bytes.Buffer\n\tbuf.WriteString(`{\"$timestamp\":{\"t\":`)\n\tbuf.WriteString(strconv.FormatUint(uint64(t), 10))\n\tbuf.WriteString(`,\"i\":`)\n\tbuf.WriteString(strconv.FormatUint(uint64(i), 10))\n\tbuf.WriteString(`}},`)\n\n\tejvw.buf = append(ejvw.buf, buf.Bytes()...)\n\n\tejvw.pop()\n\treturn nil\n}\n\nfunc (ejvw *extJSONValueWriter) WriteUndefined() error {\n\tif err := ejvw.ensureElementValue(mode(0), \"WriteUndefined\"); err != nil {\n\t\treturn err\n\t}\n\n\tejvw.writeExtendedSingleValue(\"undefined\", \"true\", false)\n\tejvw.buf = append(ejvw.buf, ',')\n\n\tejvw.pop()\n\treturn nil\n}\n\nfunc (ejvw *extJSONValueWriter) WriteDocumentElement(key string) (ValueWriter, error) {\n\tswitch ejvw.stack[ejvw.frame].mode {\n\tcase mDocument, mTopLevel, mCodeWithScope:\n\t\tvar buf bytes.Buffer\n\t\twriteStringWithEscapes(key, &buf, ejvw.escapeHTML)\n\n\t\tejvw.buf = append(ejvw.buf, []byte(fmt.Sprintf(`%s:`, buf.String()))...)\n\t\tejvw.push(mElement)\n\tdefault:\n\t\treturn nil, ejvw.invalidTransitionErr(mElement, \"WriteDocumentElement\", []mode{mDocument, mTopLevel, mCodeWithScope})\n\t}\n\n\treturn ejvw, nil\n}\n\nfunc (ejvw *extJSONValueWriter) WriteDocumentEnd() error {\n\tswitch ejvw.stack[ejvw.frame].mode {\n\tcase mDocument, mTopLevel, mCodeWithScope:\n\tdefault:\n\t\treturn fmt.Errorf(\"incorrect mode to end document: %s\", ejvw.stack[ejvw.frame].mode)\n\t}\n\n\t// close the document\n\tif ejvw.buf[len(ejvw.buf)-1] == ',' {\n\t\tejvw.buf[len(ejvw.buf)-1] = '}'\n\t} else {\n\t\tejvw.buf = append(ejvw.buf, '}')\n\t}\n\n\tswitch ejvw.stack[ejvw.frame].mode {\n\tcase mCodeWithScope:\n\t\tejvw.buf = append(ejvw.buf, '}')\n\t\tfallthrough\n\tcase mDocument:\n\t\tejvw.buf = append(ejvw.buf, ',')\n\tcase mTopLevel:\n\t\t// If the value writer has newlines enabled, end top-level documents with a newline so that\n\t\t// multiple documents encoded to the same writer are separated by newlines. That matches the\n\t\t// Go json.Encoder behavior and also works with NewExtJSONValueReader.\n\t\tif ejvw.newlines {\n\t\t\tejvw.buf = append(ejvw.buf, '\\n')\n\t\t}\n\t\tif ejvw.w != nil {\n\t\t\tif _, err := ejvw.w.Write(ejvw.buf); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tejvw.buf = ejvw.buf[:0]\n\t\t}\n\t}\n\n\tejvw.pop()\n\treturn nil\n}\n\nfunc (ejvw *extJSONValueWriter) WriteArrayElement() (ValueWriter, error) {\n\tswitch ejvw.stack[ejvw.frame].mode {\n\tcase mArray:\n\t\tejvw.push(mValue)\n\tdefault:\n\t\treturn nil, ejvw.invalidTransitionErr(mValue, \"WriteArrayElement\", []mode{mArray})\n\t}\n\n\treturn ejvw, nil\n}\n\nfunc (ejvw *extJSONValueWriter) WriteArrayEnd() error {\n\tswitch ejvw.stack[ejvw.frame].mode {\n\tcase mArray:\n\t\t// close the array\n\t\tif ejvw.buf[len(ejvw.buf)-1] == ',' {\n\t\t\tejvw.buf[len(ejvw.buf)-1] = ']'\n\t\t} else {\n\t\t\tejvw.buf = append(ejvw.buf, ']')\n\t\t}\n\n\t\tejvw.buf = append(ejvw.buf, ',')\n\n\t\tejvw.pop()\n\tdefault:\n\t\treturn fmt.Errorf(\"incorrect mode to end array: %s\", ejvw.stack[ejvw.frame].mode)\n\t}\n\n\treturn nil\n}\n\nfunc formatDouble(f float64) string {\n\tvar s string\n\tswitch {\n\tcase math.IsInf(f, 1):\n\t\ts = \"Infinity\"\n\tcase math.IsInf(f, -1):\n\t\ts = \"-Infinity\"\n\tcase math.IsNaN(f):\n\t\ts = \"NaN\"\n\tdefault:\n\t\t// Print exactly one decimalType place for integers; otherwise, print as many are necessary to\n\t\t// perfectly represent it.\n\t\ts = strconv.FormatFloat(f, 'G', -1, 64)\n\t\tif !strings.ContainsRune(s, 'E') && !strings.ContainsRune(s, '.') {\n\t\t\ts += \".0\"\n\t\t}\n\t}\n\n\treturn s\n}\n\nvar hexChars = \"0123456789abcdef\"\n\nfunc writeStringWithEscapes(s string, buf *bytes.Buffer, escapeHTML bool) {\n\tbuf.WriteByte('\"')\n\tstart := 0\n\tfor i := 0; i < len(s); {\n\t\tif b := s[i]; b < utf8.RuneSelf {\n\t\t\tif htmlSafeSet[b] || (!escapeHTML && safeSet[b]) {\n\t\t\t\ti++\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif start < i {\n\t\t\t\tbuf.WriteString(s[start:i])\n\t\t\t}\n\t\t\tswitch b {\n\t\t\tcase '\\\\', '\"':\n\t\t\t\tbuf.WriteByte('\\\\')\n\t\t\t\tbuf.WriteByte(b)\n\t\t\tcase '\\n':\n\t\t\t\tbuf.WriteByte('\\\\')\n\t\t\t\tbuf.WriteByte('n')\n\t\t\tcase '\\r':\n\t\t\t\tbuf.WriteByte('\\\\')\n\t\t\t\tbuf.WriteByte('r')\n\t\t\tcase '\\t':\n\t\t\t\tbuf.WriteByte('\\\\')\n\t\t\t\tbuf.WriteByte('t')\n\t\t\tcase '\\b':\n\t\t\t\tbuf.WriteByte('\\\\')\n\t\t\t\tbuf.WriteByte('b')\n\t\t\tcase '\\f':\n\t\t\t\tbuf.WriteByte('\\\\')\n\t\t\t\tbuf.WriteByte('f')\n\t\t\tdefault:\n\t\t\t\t// This encodes bytes < 0x20 except for \\t, \\n and \\r.\n\t\t\t\t// If escapeHTML is set, it also escapes <, >, and &\n\t\t\t\t// because they can lead to security holes when\n\t\t\t\t// user-controlled strings are rendered into JSON\n\t\t\t\t// and served to some browsers.\n\t\t\t\tbuf.WriteString(`\\u00`)\n\t\t\t\tbuf.WriteByte(hexChars[b>>4])\n\t\t\t\tbuf.WriteByte(hexChars[b&0xF])\n\t\t\t}\n\t\t\ti++\n\t\t\tstart = i\n\t\t\tcontinue\n\t\t}\n\t\tc, size := utf8.DecodeRuneInString(s[i:])\n\t\tif c == utf8.RuneError && size == 1 {\n\t\t\tif start < i {\n\t\t\t\tbuf.WriteString(s[start:i])\n\t\t\t}\n\t\t\tbuf.WriteString(`\\ufffd`)\n\t\t\ti += size\n\t\t\tstart = i\n\t\t\tcontinue\n\t\t}\n\t\t// U+2028 is LINE SEPARATOR.\n\t\t// U+2029 is PARAGRAPH SEPARATOR.\n\t\t// They are both technically valid characters in JSON strings,\n\t\t// but don't work in JSONP, which has to be evaluated as JavaScript,\n\t\t// and can lead to security holes there. It is valid JSON to\n\t\t// escape them, so we do so unconditionally.\n\t\t// See http://timelessrepo.com/json-isnt-a-javascript-subset for discussion.\n\t\tif c == '\\u2028' || c == '\\u2029' {\n\t\t\tif start < i {\n\t\t\t\tbuf.WriteString(s[start:i])\n\t\t\t}\n\t\t\tbuf.WriteString(`\\u202`)\n\t\t\tbuf.WriteByte(hexChars[c&0xF])\n\t\t\ti += size\n\t\t\tstart = i\n\t\t\tcontinue\n\t\t}\n\t\ti += size\n\t}\n\tif start < len(s) {\n\t\tbuf.WriteString(s[start:])\n\t}\n\tbuf.WriteByte('\"')\n}\n\ntype sortableString []rune\n\nfunc (ss sortableString) Len() int {\n\treturn len(ss)\n}\n\nfunc (ss sortableString) Less(i, j int) bool {\n\treturn ss[i] < ss[j]\n}\n\nfunc (ss sortableString) Swap(i, j int) {\n\tss[i], ss[j] = ss[j], ss[i]\n}\n\nfunc sortStringAlphebeticAscending(s string) string {\n\tss := sortableString([]rune(s))\n\tsort.Sort(ss)\n\treturn string([]rune(ss))\n}\n"
  },
  {
    "path": "bson/extjson_writer_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bson\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"reflect\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n)\n\nfunc TestExtJSONValueWriter(t *testing.T) {\n\toid := ObjectID{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C}\n\ttestCases := []struct {\n\t\tname   string\n\t\tfn     any\n\t\tparams []any\n\t}{\n\t\t{\n\t\t\t\"WriteBinary\",\n\t\t\t(*extJSONValueWriter).WriteBinary,\n\t\t\t[]any{[]byte{0x01, 0x02, 0x03}},\n\t\t},\n\t\t{\n\t\t\t\"WriteBinaryWithSubtype (not 0x02)\",\n\t\t\t(*extJSONValueWriter).WriteBinaryWithSubtype,\n\t\t\t[]any{[]byte{0x01, 0x02, 0x03}, byte(0xFF)},\n\t\t},\n\t\t{\n\t\t\t\"WriteBinaryWithSubtype (0x02)\",\n\t\t\t(*extJSONValueWriter).WriteBinaryWithSubtype,\n\t\t\t[]any{[]byte{0x01, 0x02, 0x03}, byte(0x02)},\n\t\t},\n\t\t{\n\t\t\t\"WriteBoolean\",\n\t\t\t(*extJSONValueWriter).WriteBoolean,\n\t\t\t[]any{true},\n\t\t},\n\t\t{\n\t\t\t\"WriteDBPointer\",\n\t\t\t(*extJSONValueWriter).WriteDBPointer,\n\t\t\t[]any{\"bar\", oid},\n\t\t},\n\t\t{\n\t\t\t\"WriteDateTime\",\n\t\t\t(*extJSONValueWriter).WriteDateTime,\n\t\t\t[]any{int64(12345678)},\n\t\t},\n\t\t{\n\t\t\t\"WriteDecimal128\",\n\t\t\t(*extJSONValueWriter).WriteDecimal128,\n\t\t\t[]any{NewDecimal128(10, 20)},\n\t\t},\n\t\t{\n\t\t\t\"WriteDouble\",\n\t\t\t(*extJSONValueWriter).WriteDouble,\n\t\t\t[]any{float64(3.14159)},\n\t\t},\n\t\t{\n\t\t\t\"WriteInt32\",\n\t\t\t(*extJSONValueWriter).WriteInt32,\n\t\t\t[]any{int32(123456)},\n\t\t},\n\t\t{\n\t\t\t\"WriteInt64\",\n\t\t\t(*extJSONValueWriter).WriteInt64,\n\t\t\t[]any{int64(1234567890)},\n\t\t},\n\t\t{\n\t\t\t\"WriteJavascript\",\n\t\t\t(*extJSONValueWriter).WriteJavascript,\n\t\t\t[]any{\"var foo = 'bar';\"},\n\t\t},\n\t\t{\n\t\t\t\"WriteMaxKey\",\n\t\t\t(*extJSONValueWriter).WriteMaxKey,\n\t\t\t[]any{},\n\t\t},\n\t\t{\n\t\t\t\"WriteMinKey\",\n\t\t\t(*extJSONValueWriter).WriteMinKey,\n\t\t\t[]any{},\n\t\t},\n\t\t{\n\t\t\t\"WriteNull\",\n\t\t\t(*extJSONValueWriter).WriteNull,\n\t\t\t[]any{},\n\t\t},\n\t\t{\n\t\t\t\"WriteObjectID\",\n\t\t\t(*extJSONValueWriter).WriteObjectID,\n\t\t\t[]any{oid},\n\t\t},\n\t\t{\n\t\t\t\"WriteRegex\",\n\t\t\t(*extJSONValueWriter).WriteRegex,\n\t\t\t[]any{\"bar\", \"baz\"},\n\t\t},\n\t\t{\n\t\t\t\"WriteString\",\n\t\t\t(*extJSONValueWriter).WriteString,\n\t\t\t[]any{\"hello, world!\"},\n\t\t},\n\t\t{\n\t\t\t\"WriteSymbol\",\n\t\t\t(*extJSONValueWriter).WriteSymbol,\n\t\t\t[]any{\"symbollolz\"},\n\t\t},\n\t\t{\n\t\t\t\"WriteTimestamp\",\n\t\t\t(*extJSONValueWriter).WriteTimestamp,\n\t\t\t[]any{uint32(10), uint32(20)},\n\t\t},\n\t\t{\n\t\t\t\"WriteUndefined\",\n\t\t\t(*extJSONValueWriter).WriteUndefined,\n\t\t\t[]any{},\n\t\t},\n\t}\n\n\tfor _, tc := range testCases {\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\tfn := reflect.ValueOf(tc.fn)\n\t\t\tif fn.Kind() != reflect.Func {\n\t\t\t\tt.Fatalf(\"fn must be of kind Func but it is a %v\", fn.Kind())\n\t\t\t}\n\t\t\tif fn.Type().NumIn() != len(tc.params)+1 || fn.Type().In(0) != reflect.TypeOf((*extJSONValueWriter)(nil)) {\n\t\t\t\tt.Fatalf(\"fn must have at least one parameter and the first parameter must be a *valueWriter\")\n\t\t\t}\n\t\t\tif fn.Type().NumOut() != 1 || fn.Type().Out(0) != reflect.TypeOf((*error)(nil)).Elem() {\n\t\t\t\tt.Fatalf(\"fn must have one return value and it must be an error.\")\n\t\t\t}\n\t\t\tparams := make([]reflect.Value, 1, len(tc.params)+1)\n\t\t\tejvw := newExtJSONWriter(io.Discard, true, true, false)\n\t\t\tparams[0] = reflect.ValueOf(ejvw)\n\t\t\tfor _, param := range tc.params {\n\t\t\t\tparams = append(params, reflect.ValueOf(param))\n\t\t\t}\n\n\t\t\tt.Run(\"incorrect transition\", func(t *testing.T) {\n\t\t\t\tresults := fn.Call(params)\n\t\t\t\tgot := results[0].Interface().(error)\n\t\t\t\tfnName := tc.name\n\t\t\t\tif strings.Contains(fnName, \"WriteBinary\") {\n\t\t\t\t\tfnName = \"WriteBinaryWithSubtype\"\n\t\t\t\t}\n\t\t\t\twant := TransitionError{\n\t\t\t\t\tcurrent: mTopLevel, name: fnName, modes: []mode{mElement, mValue},\n\t\t\t\t\taction: \"write\",\n\t\t\t\t}\n\t\t\t\tif !assert.CompareErrors(got, want) {\n\t\t\t\t\tt.Errorf(\"Errors do not match. got %v; want %v\", got, want)\n\t\t\t\t}\n\t\t\t})\n\t\t})\n\t}\n\n\tt.Run(\"WriteArray\", func(t *testing.T) {\n\t\tejvw := newExtJSONWriter(io.Discard, true, true, false)\n\t\tejvw.push(mArray)\n\t\twant := TransitionError{\n\t\t\tcurrent: mArray, destination: mArray, parent: mTopLevel,\n\t\t\tname: \"WriteArray\", modes: []mode{mElement, mValue}, action: \"write\",\n\t\t}\n\t\t_, got := ejvw.WriteArray()\n\t\tif !assert.CompareErrors(got, want) {\n\t\t\tt.Errorf(\"Did not get expected error. got %v; want %v\", got, want)\n\t\t}\n\t})\n\tt.Run(\"WriteCodeWithScope\", func(t *testing.T) {\n\t\tejvw := newExtJSONWriter(io.Discard, true, true, false)\n\t\tejvw.push(mArray)\n\t\twant := TransitionError{\n\t\t\tcurrent: mArray, destination: mCodeWithScope, parent: mTopLevel,\n\t\t\tname: \"WriteCodeWithScope\", modes: []mode{mElement, mValue}, action: \"write\",\n\t\t}\n\t\t_, got := ejvw.WriteCodeWithScope(\"\")\n\t\tif !assert.CompareErrors(got, want) {\n\t\t\tt.Errorf(\"Did not get expected error. got %v; want %v\", got, want)\n\t\t}\n\t})\n\tt.Run(\"WriteDocument\", func(t *testing.T) {\n\t\tejvw := newExtJSONWriter(io.Discard, true, true, false)\n\t\tejvw.push(mArray)\n\t\twant := TransitionError{\n\t\t\tcurrent: mArray, destination: mDocument, parent: mTopLevel,\n\t\t\tname: \"WriteDocument\", modes: []mode{mElement, mValue, mTopLevel}, action: \"write\",\n\t\t}\n\t\t_, got := ejvw.WriteDocument()\n\t\tif !assert.CompareErrors(got, want) {\n\t\t\tt.Errorf(\"Did not get expected error. got %v; want %v\", got, want)\n\t\t}\n\t})\n\tt.Run(\"WriteDocumentElement\", func(t *testing.T) {\n\t\tejvw := newExtJSONWriter(io.Discard, true, true, false)\n\t\tejvw.push(mElement)\n\t\twant := TransitionError{\n\t\t\tcurrent:     mElement,\n\t\t\tdestination: mElement,\n\t\t\tparent:      mTopLevel,\n\t\t\tname:        \"WriteDocumentElement\",\n\t\t\tmodes:       []mode{mDocument, mTopLevel, mCodeWithScope},\n\t\t\taction:      \"write\",\n\t\t}\n\t\t_, got := ejvw.WriteDocumentElement(\"\")\n\t\tif !assert.CompareErrors(got, want) {\n\t\t\tt.Errorf(\"Did not get expected error. got %v; want %v\", got, want)\n\t\t}\n\t})\n\tt.Run(\"WriteDocumentEnd\", func(t *testing.T) {\n\t\tejvw := newExtJSONWriter(io.Discard, true, true, false)\n\t\tejvw.push(mElement)\n\t\twant := fmt.Errorf(\"incorrect mode to end document: %s\", mElement)\n\t\tgot := ejvw.WriteDocumentEnd()\n\t\tif !assert.CompareErrors(got, want) {\n\t\t\tt.Errorf(\"Did not get expected error. got %v; want %v\", got, want)\n\t\t}\n\t})\n\tt.Run(\"WriteArrayElement\", func(t *testing.T) {\n\t\tejvw := newExtJSONWriter(io.Discard, true, true, false)\n\t\tejvw.push(mElement)\n\t\twant := TransitionError{\n\t\t\tcurrent:     mElement,\n\t\t\tdestination: mValue,\n\t\t\tparent:      mTopLevel,\n\t\t\tname:        \"WriteArrayElement\",\n\t\t\tmodes:       []mode{mArray},\n\t\t\taction:      \"write\",\n\t\t}\n\t\t_, got := ejvw.WriteArrayElement()\n\t\tif !assert.CompareErrors(got, want) {\n\t\t\tt.Errorf(\"Did not get expected error. got %v; want %v\", got, want)\n\t\t}\n\t})\n\tt.Run(\"WriteArrayEnd\", func(t *testing.T) {\n\t\tejvw := newExtJSONWriter(io.Discard, true, true, false)\n\t\tejvw.push(mElement)\n\t\twant := fmt.Errorf(\"incorrect mode to end array: %s\", mElement)\n\t\tgot := ejvw.WriteArrayEnd()\n\t\tif !assert.CompareErrors(got, want) {\n\t\t\tt.Errorf(\"Did not get expected error. got %v; want %v\", got, want)\n\t\t}\n\t})\n\n\tt.Run(\"WriteBytes\", func(t *testing.T) {\n\t\tt.Run(\"writeElementHeader error\", func(t *testing.T) {\n\t\t\tejvw := newExtJSONWriterFromSlice(nil, true, true)\n\t\t\twant := TransitionError{\n\t\t\t\tcurrent: mTopLevel, destination: mode(0),\n\t\t\t\tname: \"WriteBinaryWithSubtype\", modes: []mode{mElement, mValue}, action: \"write\",\n\t\t\t}\n\t\t\tgot := ejvw.WriteBinaryWithSubtype(nil, (byte)(TypeEmbeddedDocument))\n\t\t\tif !assert.CompareErrors(got, want) {\n\t\t\t\tt.Errorf(\"Did not received expected error. got %v; want %v\", got, want)\n\t\t\t}\n\t\t})\n\t})\n\n\tt.Run(\"FormatDoubleWithExponent\", func(t *testing.T) {\n\t\twant := \"3E-12\"\n\t\tgot := formatDouble(float64(0.000000000003))\n\t\tif got != want {\n\t\t\tt.Errorf(\"Did not receive expected string. got %s: want %s\", got, want)\n\t\t}\n\t})\n}\n"
  },
  {
    "path": "bson/fuzz_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2022-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\n// fuzz_test.go is used by the \"oss-fuzz\" integration. Use caution when\n// modifying this file because it may break that integration.\n//\n// See https://github.com/google/oss-fuzz/tree/master/projects/mongo-go-driver\n\npackage bson\n\nimport (\n\t\"testing\"\n)\n\nfunc FuzzDecode(f *testing.F) {\n\tseedBSONCorpus(f)\n\n\tf.Fuzz(func(t *testing.T, data []byte) {\n\t\tfor _, typ := range []func() any{\n\t\t\tfunc() any { return new(D) },\n\t\t\tfunc() any { return new([]E) },\n\t\t\tfunc() any { return new(M) },\n\t\t\tfunc() any { return new(any) },\n\t\t\tfunc() any { return make(map[string]any) },\n\t\t\tfunc() any { return new([]any) },\n\t\t} {\n\t\t\ti := typ()\n\t\t\tif err := Unmarshal(data, i); err != nil {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tencoded, err := Marshal(i)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"failed to marshal: %v\", err)\n\t\t\t}\n\n\t\t\tif err := Unmarshal(encoded, i); err != nil {\n\t\t\t\tt.Fatalf(\"failed to unmarshal: %v\", err)\n\t\t\t}\n\t\t}\n\t})\n}\n"
  },
  {
    "path": "bson/json_scanner.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bson\n\nimport (\n\t\"bytes\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"math\"\n\t\"strconv\"\n\t\"unicode\"\n\t\"unicode/utf16\"\n)\n\ntype jsonTokenType byte\n\nconst (\n\tjttBeginObject jsonTokenType = iota\n\tjttEndObject\n\tjttBeginArray\n\tjttEndArray\n\tjttColon\n\tjttComma\n\tjttInt32\n\tjttInt64\n\tjttDouble\n\tjttString\n\tjttBool\n\tjttNull\n\tjttEOF\n)\n\ntype jsonToken struct {\n\tt jsonTokenType\n\tv any\n\tp int\n}\n\ntype jsonScanner struct {\n\tr           io.Reader\n\tbuf         []byte\n\tpos         int\n\tlastReadErr error\n}\n\n// nextToken returns the next JSON token if one exists. A token is a character\n// of the JSON grammar, a number, a string, or a literal.\nfunc (js *jsonScanner) nextToken() (*jsonToken, error) {\n\tc, err := js.readNextByte()\n\n\t// keep reading until a non-space is encountered (break on read error or EOF)\n\tfor isWhiteSpace(c) && err == nil {\n\t\tc, err = js.readNextByte()\n\t}\n\n\tif errors.Is(err, io.EOF) {\n\t\treturn &jsonToken{t: jttEOF}, nil\n\t} else if err != nil {\n\t\treturn nil, err\n\t}\n\n\t// switch on the character\n\tswitch c {\n\tcase '{':\n\t\treturn &jsonToken{t: jttBeginObject, v: byte('{'), p: js.pos - 1}, nil\n\tcase '}':\n\t\treturn &jsonToken{t: jttEndObject, v: byte('}'), p: js.pos - 1}, nil\n\tcase '[':\n\t\treturn &jsonToken{t: jttBeginArray, v: byte('['), p: js.pos - 1}, nil\n\tcase ']':\n\t\treturn &jsonToken{t: jttEndArray, v: byte(']'), p: js.pos - 1}, nil\n\tcase ':':\n\t\treturn &jsonToken{t: jttColon, v: byte(':'), p: js.pos - 1}, nil\n\tcase ',':\n\t\treturn &jsonToken{t: jttComma, v: byte(','), p: js.pos - 1}, nil\n\tcase '\"': // RFC-8259 only allows for double quotes (\") not single (')\n\t\treturn js.scanString()\n\tdefault:\n\t\t// check if it's a number\n\t\tswitch {\n\t\tcase c == '-' || isDigit(c):\n\t\t\treturn js.scanNumber(c)\n\t\tcase c == 't' || c == 'f' || c == 'n':\n\t\t\t// maybe a literal\n\t\t\treturn js.scanLiteral(c)\n\t\tdefault:\n\t\t\treturn nil, fmt.Errorf(\"invalid JSON input. Position: %d. Character: %c\", js.pos-1, c)\n\t\t}\n\t}\n}\n\n// readNextByte attempts to read the next byte from the buffer. If the buffer\n// has been exhausted, this function calls readIntoBuf, thus refilling the\n// buffer and resetting the read position to 0\nfunc (js *jsonScanner) readNextByte() (byte, error) {\n\tif js.pos >= len(js.buf) {\n\t\terr := js.readIntoBuf()\n\t\tif err != nil {\n\t\t\treturn 0, err\n\t\t}\n\t}\n\n\tb := js.buf[js.pos]\n\tjs.pos++\n\n\treturn b, nil\n}\n\n// readNNextBytes reads n bytes into dst, starting at offset\nfunc (js *jsonScanner) readNNextBytes(dst []byte, n, offset int) error {\n\tvar err error\n\n\tfor i := 0; i < n; i++ {\n\t\tdst[i+offset], err = js.readNextByte()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// readIntoBuf reads up to 512 bytes from the scanner's io.Reader into the buffer\nfunc (js *jsonScanner) readIntoBuf() error {\n\tif js.lastReadErr != nil {\n\t\tjs.buf = js.buf[:0]\n\t\tjs.pos = 0\n\t\treturn js.lastReadErr\n\t}\n\n\tif cap(js.buf) == 0 {\n\t\tjs.buf = make([]byte, 0, 512)\n\t}\n\n\tn, err := js.r.Read(js.buf[:cap(js.buf)])\n\tif err != nil {\n\t\tjs.lastReadErr = err\n\t\tif n > 0 {\n\t\t\terr = nil\n\t\t}\n\t}\n\tjs.buf = js.buf[:n]\n\tjs.pos = 0\n\n\treturn err\n}\n\nfunc isWhiteSpace(c byte) bool {\n\treturn c == ' ' || c == '\\t' || c == '\\r' || c == '\\n'\n}\n\nfunc isDigit(c byte) bool {\n\treturn unicode.IsDigit(rune(c))\n}\n\nfunc isValueTerminator(c byte) bool {\n\treturn c == ',' || c == '}' || c == ']' || isWhiteSpace(c)\n}\n\n// getu4 decodes the 4-byte hex sequence from the beginning of s, returning the hex value as a rune,\n// or it returns -1. Note that the \"\\u\" from the unicode escape sequence should not be present.\n// It is copied and lightly modified from the Go JSON decode function at\n// https://github.com/golang/go/blob/1b0a0316802b8048d69da49dc23c5a5ab08e8ae8/src/encoding/json/decode.go#L1169-L1188\nfunc getu4(s []byte) rune {\n\tif len(s) < 4 {\n\t\treturn -1\n\t}\n\tvar r rune\n\tfor _, c := range s[:4] {\n\t\tswitch {\n\t\tcase '0' <= c && c <= '9':\n\t\t\tc -= '0'\n\t\tcase 'a' <= c && c <= 'f':\n\t\t\tc = c - 'a' + 10\n\t\tcase 'A' <= c && c <= 'F':\n\t\t\tc = c - 'A' + 10\n\t\tdefault:\n\t\t\treturn -1\n\t\t}\n\t\tr = r*16 + rune(c)\n\t}\n\treturn r\n}\n\n// scanString reads from an opening '\"' to a closing '\"' and handles escaped characters\nfunc (js *jsonScanner) scanString() (*jsonToken, error) {\n\tvar b bytes.Buffer\n\tvar c byte\n\tvar err error\n\n\tp := js.pos - 1\n\n\tfor {\n\t\tc, err = js.readNextByte()\n\t\tif err != nil {\n\t\t\tif errors.Is(err, io.EOF) {\n\t\t\t\treturn nil, errors.New(\"end of input in JSON string\")\n\t\t\t}\n\t\t\treturn nil, err\n\t\t}\n\n\tevalNextChar:\n\t\tswitch c {\n\t\tcase '\\\\':\n\t\t\tc, err = js.readNextByte()\n\t\t\tif err != nil {\n\t\t\t\tif errors.Is(err, io.EOF) {\n\t\t\t\t\treturn nil, errors.New(\"end of input in JSON string\")\n\t\t\t\t}\n\t\t\t\treturn nil, err\n\t\t\t}\n\n\t\tevalNextEscapeChar:\n\t\t\tswitch c {\n\t\t\tcase '\"', '\\\\', '/':\n\t\t\t\tb.WriteByte(c)\n\t\t\tcase 'b':\n\t\t\t\tb.WriteByte('\\b')\n\t\t\tcase 'f':\n\t\t\t\tb.WriteByte('\\f')\n\t\t\tcase 'n':\n\t\t\t\tb.WriteByte('\\n')\n\t\t\tcase 'r':\n\t\t\t\tb.WriteByte('\\r')\n\t\t\tcase 't':\n\t\t\t\tb.WriteByte('\\t')\n\t\t\tcase 'u':\n\t\t\t\tus := make([]byte, 4)\n\t\t\t\terr = js.readNNextBytes(us, 4, 0)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, fmt.Errorf(\"invalid unicode sequence in JSON string: %s\", us)\n\t\t\t\t}\n\n\t\t\t\trn := getu4(us)\n\n\t\t\t\t// If the rune we just decoded is the high or low value of a possible surrogate pair,\n\t\t\t\t// try to decode the next sequence as the low value of a surrogate pair. We're\n\t\t\t\t// expecting the next sequence to be another Unicode escape sequence (e.g. \"\\uDD1E\"),\n\t\t\t\t// but need to handle cases where the input is not a valid surrogate pair.\n\t\t\t\t// For more context on unicode surrogate pairs, see:\n\t\t\t\t// https://www.christianfscott.com/rust-chars-vs-go-runes/\n\t\t\t\t// https://www.unicode.org/glossary/#high_surrogate_code_point\n\t\t\t\tif utf16.IsSurrogate(rn) {\n\t\t\t\t\tc, err = js.readNextByte()\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\tif errors.Is(err, io.EOF) {\n\t\t\t\t\t\t\treturn nil, errors.New(\"end of input in JSON string\")\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn nil, err\n\t\t\t\t\t}\n\n\t\t\t\t\t// If the next value isn't the beginning of a backslash escape sequence, write\n\t\t\t\t\t// the Unicode replacement character for the surrogate value and goto the\n\t\t\t\t\t// beginning of the next char eval block.\n\t\t\t\t\tif c != '\\\\' {\n\t\t\t\t\t\tb.WriteRune(unicode.ReplacementChar)\n\t\t\t\t\t\tgoto evalNextChar\n\t\t\t\t\t}\n\n\t\t\t\t\tc, err = js.readNextByte()\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\tif errors.Is(err, io.EOF) {\n\t\t\t\t\t\t\treturn nil, errors.New(\"end of input in JSON string\")\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn nil, err\n\t\t\t\t\t}\n\n\t\t\t\t\t// If the next value isn't the beginning of a unicode escape sequence, write the\n\t\t\t\t\t// Unicode replacement character for the surrogate value and goto the beginning\n\t\t\t\t\t// of the next escape char eval block.\n\t\t\t\t\tif c != 'u' {\n\t\t\t\t\t\tb.WriteRune(unicode.ReplacementChar)\n\t\t\t\t\t\tgoto evalNextEscapeChar\n\t\t\t\t\t}\n\n\t\t\t\t\terr = js.readNNextBytes(us, 4, 0)\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\treturn nil, fmt.Errorf(\"invalid unicode sequence in JSON string: %s\", us)\n\t\t\t\t\t}\n\n\t\t\t\t\trn2 := getu4(us)\n\n\t\t\t\t\t// Try to decode the pair of runes as a utf16 surrogate pair. If that fails, write\n\t\t\t\t\t// the Unicode replacement character for the surrogate value and the 2nd decoded rune.\n\t\t\t\t\tif rnPair := utf16.DecodeRune(rn, rn2); rnPair != unicode.ReplacementChar {\n\t\t\t\t\t\tb.WriteRune(rnPair)\n\t\t\t\t\t} else {\n\t\t\t\t\t\tb.WriteRune(unicode.ReplacementChar)\n\t\t\t\t\t\tb.WriteRune(rn2)\n\t\t\t\t\t}\n\n\t\t\t\t\tbreak\n\t\t\t\t}\n\n\t\t\t\tb.WriteRune(rn)\n\t\t\tdefault:\n\t\t\t\treturn nil, fmt.Errorf(\"invalid escape sequence in JSON string '\\\\%c'\", c)\n\t\t\t}\n\t\tcase '\"':\n\t\t\treturn &jsonToken{t: jttString, v: b.String(), p: p}, nil\n\t\tdefault:\n\t\t\tb.WriteByte(c)\n\t\t}\n\t}\n}\n\n// scanLiteral reads an unquoted sequence of characters and determines if it is one of\n// three valid JSON literals (true, false, null); if so, it returns the appropriate\n// jsonToken; otherwise, it returns an error\nfunc (js *jsonScanner) scanLiteral(first byte) (*jsonToken, error) {\n\tp := js.pos - 1\n\n\tlit := make([]byte, 4)\n\tlit[0] = first\n\n\terr := js.readNNextBytes(lit, 3, 1)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tc5, err := js.readNextByte()\n\n\tswitch {\n\tcase bytes.Equal([]byte(\"true\"), lit) && (isValueTerminator(c5) || errors.Is(err, io.EOF)):\n\t\tjs.pos = int(math.Max(0, float64(js.pos-1)))\n\t\treturn &jsonToken{t: jttBool, v: true, p: p}, nil\n\tcase bytes.Equal([]byte(\"null\"), lit) && (isValueTerminator(c5) || errors.Is(err, io.EOF)):\n\t\tjs.pos = int(math.Max(0, float64(js.pos-1)))\n\t\treturn &jsonToken{t: jttNull, v: nil, p: p}, nil\n\tcase bytes.Equal([]byte(\"fals\"), lit):\n\t\tif c5 == 'e' {\n\t\t\tc5, err = js.readNextByte()\n\n\t\t\tif isValueTerminator(c5) || errors.Is(err, io.EOF) {\n\t\t\t\tjs.pos = int(math.Max(0, float64(js.pos-1)))\n\t\t\t\treturn &jsonToken{t: jttBool, v: false, p: p}, nil\n\t\t\t}\n\t\t}\n\t}\n\n\treturn nil, fmt.Errorf(\"invalid JSON literal. Position: %d, literal: %s\", p, lit)\n}\n\ntype numberScanState byte\n\nconst (\n\tnssSawLeadingMinus numberScanState = iota\n\tnssSawLeadingZero\n\tnssSawIntegerDigits\n\tnssSawDecimalPoint\n\tnssSawFractionDigits\n\tnssSawExponentLetter\n\tnssSawExponentSign\n\tnssSawExponentDigits\n\tnssDone\n\tnssInvalid\n)\n\n// scanNumber reads a JSON number (according to RFC-8259)\nfunc (js *jsonScanner) scanNumber(first byte) (*jsonToken, error) {\n\tvar b bytes.Buffer\n\tvar s numberScanState\n\tvar c byte\n\tvar err error\n\n\tt := jttInt64 // assume it's an int64 until the type can be determined\n\tstart := js.pos - 1\n\n\tb.WriteByte(first)\n\n\tswitch first {\n\tcase '-':\n\t\ts = nssSawLeadingMinus\n\tcase '0':\n\t\ts = nssSawLeadingZero\n\tdefault:\n\t\ts = nssSawIntegerDigits\n\t}\n\n\tfor {\n\t\tc, err = js.readNextByte()\n\n\t\tif err != nil && !errors.Is(err, io.EOF) {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tswitch s {\n\t\tcase nssSawLeadingMinus:\n\t\t\tswitch c {\n\t\t\tcase '0':\n\t\t\t\ts = nssSawLeadingZero\n\t\t\t\tb.WriteByte(c)\n\t\t\tdefault:\n\t\t\t\tif isDigit(c) {\n\t\t\t\t\ts = nssSawIntegerDigits\n\t\t\t\t\tb.WriteByte(c)\n\t\t\t\t} else {\n\t\t\t\t\ts = nssInvalid\n\t\t\t\t}\n\t\t\t}\n\t\tcase nssSawLeadingZero:\n\t\t\tswitch c {\n\t\t\tcase '.':\n\t\t\t\ts = nssSawDecimalPoint\n\t\t\t\tb.WriteByte(c)\n\t\t\tcase 'e', 'E':\n\t\t\t\ts = nssSawExponentLetter\n\t\t\t\tb.WriteByte(c)\n\t\t\tcase '}', ']', ',':\n\t\t\t\ts = nssDone\n\t\t\tdefault:\n\t\t\t\tif isWhiteSpace(c) || errors.Is(err, io.EOF) {\n\t\t\t\t\ts = nssDone\n\t\t\t\t} else {\n\t\t\t\t\ts = nssInvalid\n\t\t\t\t}\n\t\t\t}\n\t\tcase nssSawIntegerDigits:\n\t\t\tswitch c {\n\t\t\tcase '.':\n\t\t\t\ts = nssSawDecimalPoint\n\t\t\t\tb.WriteByte(c)\n\t\t\tcase 'e', 'E':\n\t\t\t\ts = nssSawExponentLetter\n\t\t\t\tb.WriteByte(c)\n\t\t\tcase '}', ']', ',':\n\t\t\t\ts = nssDone\n\t\t\tdefault:\n\t\t\t\tswitch {\n\t\t\t\tcase isWhiteSpace(c) || errors.Is(err, io.EOF):\n\t\t\t\t\ts = nssDone\n\t\t\t\tcase isDigit(c):\n\t\t\t\t\ts = nssSawIntegerDigits\n\t\t\t\t\tb.WriteByte(c)\n\t\t\t\tdefault:\n\t\t\t\t\ts = nssInvalid\n\t\t\t\t}\n\t\t\t}\n\t\tcase nssSawDecimalPoint:\n\t\t\tt = jttDouble\n\t\t\tif isDigit(c) {\n\t\t\t\ts = nssSawFractionDigits\n\t\t\t\tb.WriteByte(c)\n\t\t\t} else {\n\t\t\t\ts = nssInvalid\n\t\t\t}\n\t\tcase nssSawFractionDigits:\n\t\t\tswitch c {\n\t\t\tcase 'e', 'E':\n\t\t\t\ts = nssSawExponentLetter\n\t\t\t\tb.WriteByte(c)\n\t\t\tcase '}', ']', ',':\n\t\t\t\ts = nssDone\n\t\t\tdefault:\n\t\t\t\tswitch {\n\t\t\t\tcase isWhiteSpace(c) || errors.Is(err, io.EOF):\n\t\t\t\t\ts = nssDone\n\t\t\t\tcase isDigit(c):\n\t\t\t\t\ts = nssSawFractionDigits\n\t\t\t\t\tb.WriteByte(c)\n\t\t\t\tdefault:\n\t\t\t\t\ts = nssInvalid\n\t\t\t\t}\n\t\t\t}\n\t\tcase nssSawExponentLetter:\n\t\t\tt = jttDouble\n\t\t\tswitch c {\n\t\t\tcase '+', '-':\n\t\t\t\ts = nssSawExponentSign\n\t\t\t\tb.WriteByte(c)\n\t\t\tdefault:\n\t\t\t\tif isDigit(c) {\n\t\t\t\t\ts = nssSawExponentDigits\n\t\t\t\t\tb.WriteByte(c)\n\t\t\t\t} else {\n\t\t\t\t\ts = nssInvalid\n\t\t\t\t}\n\t\t\t}\n\t\tcase nssSawExponentSign:\n\t\t\tif isDigit(c) {\n\t\t\t\ts = nssSawExponentDigits\n\t\t\t\tb.WriteByte(c)\n\t\t\t} else {\n\t\t\t\ts = nssInvalid\n\t\t\t}\n\t\tcase nssSawExponentDigits:\n\t\t\tswitch c {\n\t\t\tcase '}', ']', ',':\n\t\t\t\ts = nssDone\n\t\t\tdefault:\n\t\t\t\tswitch {\n\t\t\t\tcase isWhiteSpace(c) || errors.Is(err, io.EOF):\n\t\t\t\t\ts = nssDone\n\t\t\t\tcase isDigit(c):\n\t\t\t\t\ts = nssSawExponentDigits\n\t\t\t\t\tb.WriteByte(c)\n\t\t\t\tdefault:\n\t\t\t\t\ts = nssInvalid\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tswitch s {\n\t\tcase nssInvalid:\n\t\t\treturn nil, fmt.Errorf(\"invalid JSON number. Position: %d\", start)\n\t\tcase nssDone:\n\t\t\tjs.pos = int(math.Max(0, float64(js.pos-1)))\n\t\t\tif t != jttDouble {\n\t\t\t\tv, err := strconv.ParseInt(b.String(), 10, 64)\n\t\t\t\tif err == nil {\n\t\t\t\t\tif v < math.MinInt32 || v > math.MaxInt32 {\n\t\t\t\t\t\treturn &jsonToken{t: jttInt64, v: v, p: start}, nil\n\t\t\t\t\t}\n\n\t\t\t\t\treturn &jsonToken{t: jttInt32, v: int32(v), p: start}, nil\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tv, err := strconv.ParseFloat(b.String(), 64)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\n\t\t\treturn &jsonToken{t: jttDouble, v: v, p: start}, nil\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "bson/json_scanner_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bson\n\nimport (\n\t\"strings\"\n\t\"testing\"\n\t\"testing/iotest\"\n\n\t\"github.com/google/go-cmp/cmp\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/require\"\n)\n\nfunc jttDiff(t *testing.T, expected, actual jsonTokenType, desc string) {\n\tif diff := cmp.Diff(expected, actual); diff != \"\" {\n\t\tt.Helper()\n\t\tt.Errorf(\"%s: Incorrect JSON Token Type (-want, +got): %s\\n\", desc, diff)\n\t\tt.FailNow()\n\t}\n}\n\nfunc jtvDiff(t *testing.T, expected, actual any, desc string) {\n\tif diff := cmp.Diff(expected, actual); diff != \"\" {\n\t\tt.Helper()\n\t\tt.Errorf(\"%s: Incorrect JSON Token Value (-want, +got): %s\\n\", desc, diff)\n\t\tt.FailNow()\n\t}\n}\n\nfunc expectNilToken(t *testing.T, v *jsonToken, desc string) {\n\tif v != nil {\n\t\tt.Helper()\n\t\tt.Errorf(\"%s: Expected nil JSON token\", desc)\n\t\tt.FailNow()\n\t}\n}\n\ntype jsonScannerTestCase struct {\n\tdesc   string\n\tinput  string\n\ttokens []jsonToken\n}\n\n// length = 512\nconst longKey = \"abcdefghijklmnopqrstuvwxyz\" + \"abcdefghijklmnopqrstuvwxyz\" +\n\t\"abcdefghijklmnopqrstuvwxyz\" + \"abcdefghijklmnopqrstuvwxyz\" +\n\t\"abcdefghijklmnopqrstuvwxyz\" + \"abcdefghijklmnopqrstuvwxyz\" +\n\t\"abcdefghijklmnopqrstuvwxyz\" + \"abcdefghijklmnopqrstuvwxyz\" +\n\t\"abcdefghijklmnopqrstuvwxyz\" + \"abcdefghijklmnopqrstuvwxyz\" +\n\t\"abcdefghijklmnopqrstuvwxyz\" + \"abcdefghijklmnopqrstuvwxyz\" +\n\t\"abcdefghijklmnopqrstuvwxyz\" + \"abcdefghijklmnopqrstuvwxyz\" +\n\t\"abcdefghijklmnopqrstuvwxyz\" + \"abcdefghijklmnopqrstuvwxyz\" +\n\t\"abcdefghijklmnopqrstuvwxyz\" + \"abcdefghijklmnopqrstuvwxyz\" +\n\t\"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqr\"\n\nfunc TestJsonScannerValidInputs(t *testing.T) {\n\tcases := []jsonScannerTestCase{\n\t\t{\n\t\t\tdesc: \"empty input\", input: \"\",\n\t\t\ttokens: []jsonToken{},\n\t\t},\n\t\t{\n\t\t\tdesc: \"empty object\", input: \"{}\",\n\t\t\ttokens: []jsonToken{{t: jttBeginObject, v: byte('{')}, {t: jttEndObject, v: byte('}')}},\n\t\t},\n\t\t{\n\t\t\tdesc: \"empty array\", input: \"[]\",\n\t\t\ttokens: []jsonToken{{t: jttBeginArray, v: byte('[')}, {t: jttEndArray, v: byte(']')}},\n\t\t},\n\t\t{\n\t\t\tdesc: \"valid empty string\", input: `\"\"`,\n\t\t\ttokens: []jsonToken{{t: jttString, v: \"\"}},\n\t\t},\n\t\t{\n\t\t\tdesc:   \"valid string--no escaped characters\",\n\t\t\tinput:  `\"string\"`,\n\t\t\ttokens: []jsonToken{{t: jttString, v: \"string\"}},\n\t\t},\n\t\t{\n\t\t\tdesc:   \"valid string--escaped characters\",\n\t\t\tinput:  `\"\\\"\\\\\\/\\b\\f\\n\\r\\t\"`,\n\t\t\ttokens: []jsonToken{{t: jttString, v: \"\\\"\\\\/\\b\\f\\n\\r\\t\"}},\n\t\t},\n\t\t{\n\t\t\tdesc:   \"valid string--surrogate pair\",\n\t\t\tinput:  `\"abc \\uD834\\uDd1e 123\"`,\n\t\t\ttokens: []jsonToken{{t: jttString, v: \"abc 𝄞 123\"}},\n\t\t},\n\t\t{\n\t\t\tdesc:   \"valid string--high surrogate at end of string\",\n\t\t\tinput:  `\"abc \\uD834\"`,\n\t\t\ttokens: []jsonToken{{t: jttString, v: \"abc �\"}},\n\t\t},\n\t\t{\n\t\t\tdesc:   \"valid string--low surrogate at end of string\",\n\t\t\tinput:  `\"abc \\uDD1E\"`,\n\t\t\ttokens: []jsonToken{{t: jttString, v: \"abc �\"}},\n\t\t},\n\t\t{\n\t\t\tdesc:   \"valid string--high surrogate with non-surrogate Unicode value\",\n\t\t\tinput:  `\"abc \\uDD1E\\u00BF\"`,\n\t\t\ttokens: []jsonToken{{t: jttString, v: \"abc �¿\"}},\n\t\t},\n\t\t{\n\t\t\tdesc:   \"valid string--high surrogate with non-Unicode escape sequence\",\n\t\t\tinput:  `\"abc \\uDD1E\\t\"`,\n\t\t\ttokens: []jsonToken{{t: jttString, v: \"abc �\\t\"}},\n\t\t},\n\t\t{\n\t\t\tdesc: \"valid literal--true\", input: \"true\",\n\t\t\ttokens: []jsonToken{{t: jttBool, v: true}},\n\t\t},\n\t\t{\n\t\t\tdesc: \"valid literal--false\", input: \"false\",\n\t\t\ttokens: []jsonToken{{t: jttBool, v: false}},\n\t\t},\n\t\t{\n\t\t\tdesc: \"valid literal--null\", input: \"null\",\n\t\t\ttokens: []jsonToken{{t: jttNull}},\n\t\t},\n\t\t{\n\t\t\tdesc: \"valid int32: 0\", input: \"0\",\n\t\t\ttokens: []jsonToken{{t: jttInt32, v: int32(0)}},\n\t\t},\n\t\t{\n\t\t\tdesc: \"valid int32: -0\", input: \"-0\",\n\t\t\ttokens: []jsonToken{{t: jttInt32, v: int32(0)}},\n\t\t},\n\t\t{\n\t\t\tdesc: \"valid int32: 1\", input: \"1\",\n\t\t\ttokens: []jsonToken{{t: jttInt32, v: int32(1)}},\n\t\t},\n\t\t{\n\t\t\tdesc: \"valid int32: -1\", input: \"-1\",\n\t\t\ttokens: []jsonToken{{t: jttInt32, v: int32(-1)}},\n\t\t},\n\t\t{\n\t\t\tdesc: \"valid int32: 10\", input: \"10\",\n\t\t\ttokens: []jsonToken{{t: jttInt32, v: int32(10)}},\n\t\t},\n\t\t{\n\t\t\tdesc: \"valid int32: 1234\", input: \"1234\",\n\t\t\ttokens: []jsonToken{{t: jttInt32, v: int32(1234)}},\n\t\t},\n\t\t{\n\t\t\tdesc: \"valid int32: -10\", input: \"-10\",\n\t\t\ttokens: []jsonToken{{t: jttInt32, v: int32(-10)}},\n\t\t},\n\t\t{\n\t\t\tdesc: \"valid int32: -1234\", input: \"-1234\",\n\t\t\ttokens: []jsonToken{{t: jttInt32, v: int32(-1234)}},\n\t\t},\n\t\t{\n\t\t\tdesc: \"valid int64: 2147483648\", input: \"2147483648\",\n\t\t\ttokens: []jsonToken{{t: jttInt64, v: int64(2147483648)}},\n\t\t},\n\t\t{\n\t\t\tdesc: \"valid int64: -2147483649\", input: \"-2147483649\",\n\t\t\ttokens: []jsonToken{{t: jttInt64, v: int64(-2147483649)}},\n\t\t},\n\t\t{\n\t\t\tdesc: \"valid double: 0.0\", input: \"0.0\",\n\t\t\ttokens: []jsonToken{{t: jttDouble, v: 0.0}},\n\t\t},\n\t\t{\n\t\t\tdesc: \"valid double: -0.0\", input: \"-0.0\",\n\t\t\ttokens: []jsonToken{{t: jttDouble, v: 0.0}},\n\t\t},\n\t\t{\n\t\t\tdesc: \"valid double: 0.1\", input: \"0.1\",\n\t\t\ttokens: []jsonToken{{t: jttDouble, v: 0.1}},\n\t\t},\n\t\t{\n\t\t\tdesc: \"valid double: 0.1234\", input: \"0.1234\",\n\t\t\ttokens: []jsonToken{{t: jttDouble, v: 0.1234}},\n\t\t},\n\t\t{\n\t\t\tdesc: \"valid double: 1.0\", input: \"1.0\",\n\t\t\ttokens: []jsonToken{{t: jttDouble, v: 1.0}},\n\t\t},\n\t\t{\n\t\t\tdesc: \"valid double: -1.0\", input: \"-1.0\",\n\t\t\ttokens: []jsonToken{{t: jttDouble, v: -1.0}},\n\t\t},\n\t\t{\n\t\t\tdesc: \"valid double: 1.234\", input: \"1.234\",\n\t\t\ttokens: []jsonToken{{t: jttDouble, v: 1.234}},\n\t\t},\n\t\t{\n\t\t\tdesc: \"valid double: -1.234\", input: \"-1.234\",\n\t\t\ttokens: []jsonToken{{t: jttDouble, v: -1.234}},\n\t\t},\n\t\t{\n\t\t\tdesc: \"valid double: 1e10\", input: \"1e10\",\n\t\t\ttokens: []jsonToken{{t: jttDouble, v: 1e+10}},\n\t\t},\n\t\t{\n\t\t\tdesc: \"valid double: 1E10\", input: \"1E10\",\n\t\t\ttokens: []jsonToken{{t: jttDouble, v: 1e+10}},\n\t\t},\n\t\t{\n\t\t\tdesc: \"valid double: 1.2e10\", input: \"1.2e10\",\n\t\t\ttokens: []jsonToken{{t: jttDouble, v: 1.2e+10}},\n\t\t},\n\t\t{\n\t\t\tdesc: \"valid double: 1.2E10\", input: \"1.2E10\",\n\t\t\ttokens: []jsonToken{{t: jttDouble, v: 1.2e+10}},\n\t\t},\n\t\t{\n\t\t\tdesc: \"valid double: -1.2e10\", input: \"-1.2e10\",\n\t\t\ttokens: []jsonToken{{t: jttDouble, v: -1.2e+10}},\n\t\t},\n\t\t{\n\t\t\tdesc: \"valid double: -1.2E10\", input: \"-1.2E10\",\n\t\t\ttokens: []jsonToken{{t: jttDouble, v: -1.2e+10}},\n\t\t},\n\t\t{\n\t\t\tdesc: \"valid double: -1.2e+10\", input: \"-1.2e+10\",\n\t\t\ttokens: []jsonToken{{t: jttDouble, v: -1.2e+10}},\n\t\t},\n\t\t{\n\t\t\tdesc: \"valid double: -1.2E+10\", input: \"-1.2E+10\",\n\t\t\ttokens: []jsonToken{{t: jttDouble, v: -1.2e+10}},\n\t\t},\n\t\t{\n\t\t\tdesc: \"valid double: 1.2e-10\", input: \"1.2e-10\",\n\t\t\ttokens: []jsonToken{{t: jttDouble, v: 1.2e-10}},\n\t\t},\n\t\t{\n\t\t\tdesc: \"valid double: 1.2E-10\", input: \"1.2e-10\",\n\t\t\ttokens: []jsonToken{{t: jttDouble, v: 1.2e-10}},\n\t\t},\n\t\t{\n\t\t\tdesc: \"valid double: -1.2e-10\", input: \"-1.2e-10\",\n\t\t\ttokens: []jsonToken{{t: jttDouble, v: -1.2e-10}},\n\t\t},\n\t\t{\n\t\t\tdesc: \"valid double: -1.2E-10\", input: \"-1.2E-10\",\n\t\t\ttokens: []jsonToken{{t: jttDouble, v: -1.2e-10}},\n\t\t},\n\t\t{\n\t\t\tdesc: \"valid double: 8005332285744496613785600\", input: \"8005332285744496613785600\",\n\t\t\ttokens: []jsonToken{{t: jttDouble, v: float64(8005332285744496613785600)}},\n\t\t},\n\t\t{\n\t\t\tdesc:  \"valid object, only spaces\",\n\t\t\tinput: `{\"key\": \"string\", \"key2\": 2, \"key3\": {}, \"key4\": [], \"key5\": false }`,\n\t\t\ttokens: []jsonToken{\n\t\t\t\t{t: jttBeginObject, v: byte('{')},\n\t\t\t\t{t: jttString, v: \"key\"},\n\t\t\t\t{t: jttColon, v: byte(':')},\n\t\t\t\t{t: jttString, v: \"string\"},\n\t\t\t\t{t: jttComma, v: byte(',')},\n\t\t\t\t{t: jttString, v: \"key2\"},\n\t\t\t\t{t: jttColon, v: byte(':')},\n\t\t\t\t{t: jttInt32, v: int32(2)},\n\t\t\t\t{t: jttComma, v: byte(',')},\n\t\t\t\t{t: jttString, v: \"key3\"},\n\t\t\t\t{t: jttColon, v: byte(':')},\n\t\t\t\t{t: jttBeginObject, v: byte('{')},\n\t\t\t\t{t: jttEndObject, v: byte('}')},\n\t\t\t\t{t: jttComma, v: byte(',')},\n\t\t\t\t{t: jttString, v: \"key4\"},\n\t\t\t\t{t: jttColon, v: byte(':')},\n\t\t\t\t{t: jttBeginArray, v: byte('[')},\n\t\t\t\t{t: jttEndArray, v: byte(']')},\n\t\t\t\t{t: jttComma, v: byte(',')},\n\t\t\t\t{t: jttString, v: \"key5\"},\n\t\t\t\t{t: jttColon, v: byte(':')},\n\t\t\t\t{t: jttBool, v: false},\n\t\t\t\t{t: jttEndObject, v: byte('}')},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tdesc: \"valid object, mixed whitespace\",\n\t\t\tinput: `\n\t\t\t\t\t{ \"key\" : \"string\"\n\t\t\t\t\t, \"key2\": 2\n\t\t\t\t\t, \"key3\": {}\n\t\t\t\t\t, \"key4\": []\n\t\t\t\t\t, \"key5\": false\n\t\t\t\t\t}`,\n\t\t\ttokens: []jsonToken{\n\t\t\t\t{t: jttBeginObject, v: byte('{')},\n\t\t\t\t{t: jttString, v: \"key\"},\n\t\t\t\t{t: jttColon, v: byte(':')},\n\t\t\t\t{t: jttString, v: \"string\"},\n\t\t\t\t{t: jttComma, v: byte(',')},\n\t\t\t\t{t: jttString, v: \"key2\"},\n\t\t\t\t{t: jttColon, v: byte(':')},\n\t\t\t\t{t: jttInt32, v: int32(2)},\n\t\t\t\t{t: jttComma, v: byte(',')},\n\t\t\t\t{t: jttString, v: \"key3\"},\n\t\t\t\t{t: jttColon, v: byte(':')},\n\t\t\t\t{t: jttBeginObject, v: byte('{')},\n\t\t\t\t{t: jttEndObject, v: byte('}')},\n\t\t\t\t{t: jttComma, v: byte(',')},\n\t\t\t\t{t: jttString, v: \"key4\"},\n\t\t\t\t{t: jttColon, v: byte(':')},\n\t\t\t\t{t: jttBeginArray, v: byte('[')},\n\t\t\t\t{t: jttEndArray, v: byte(']')},\n\t\t\t\t{t: jttComma, v: byte(',')},\n\t\t\t\t{t: jttString, v: \"key5\"},\n\t\t\t\t{t: jttColon, v: byte(':')},\n\t\t\t\t{t: jttBool, v: false},\n\t\t\t\t{t: jttEndObject, v: byte('}')},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tdesc:  \"input greater than buffer size\",\n\t\t\tinput: `{\"` + longKey + `\": 1}`,\n\t\t\ttokens: []jsonToken{\n\t\t\t\t{t: jttBeginObject, v: byte('{')},\n\t\t\t\t{t: jttString, v: longKey},\n\t\t\t\t{t: jttColon, v: byte(':')},\n\t\t\t\t{t: jttInt32, v: int32(1)},\n\t\t\t\t{t: jttEndObject, v: byte('}')},\n\t\t\t},\n\t\t},\n\t}\n\n\tfor _, tc := range cases {\n\t\tt.Run(tc.desc, func(t *testing.T) {\n\t\t\tjs := &jsonScanner{r: strings.NewReader(tc.input)}\n\n\t\t\tfor _, token := range tc.tokens {\n\t\t\t\tc, err := js.nextToken()\n\t\t\t\trequire.NoError(t, err, tc.desc)\n\t\t\t\tjttDiff(t, token.t, c.t, tc.desc)\n\t\t\t\tjtvDiff(t, token.v, c.v, tc.desc)\n\t\t\t}\n\n\t\t\tc, err := js.nextToken()\n\t\t\tnoerr(t, err)\n\t\t\tjttDiff(t, jttEOF, c.t, tc.desc)\n\n\t\t\t// testing early EOF reading\n\t\t\tjs = &jsonScanner{r: iotest.DataErrReader(strings.NewReader(tc.input))}\n\n\t\t\tfor _, token := range tc.tokens {\n\t\t\t\tc, err := js.nextToken()\n\t\t\t\trequire.NoError(t, err, tc.desc)\n\t\t\t\tjttDiff(t, token.t, c.t, tc.desc)\n\t\t\t\tjtvDiff(t, token.v, c.v, tc.desc)\n\t\t\t}\n\n\t\t\tc, err = js.nextToken()\n\t\t\tnoerr(t, err)\n\t\t\tjttDiff(t, jttEOF, c.t, tc.desc)\n\t\t})\n\t}\n}\n\nfunc TestJsonScannerInvalidInputs(t *testing.T) {\n\tcases := []jsonScannerTestCase{\n\t\t{desc: \"missing quotation\", input: `\"missing`},\n\t\t{desc: \"invalid escape character--first character\", input: `\"\\invalid\"`},\n\t\t{desc: \"invalid escape character--middle\", input: `\"i\\nv\\alid\"`},\n\t\t{desc: \"invalid escape character--single quote\", input: `\"f\\'oo\"`},\n\t\t{desc: \"invalid literal--trueee\", input: \"trueee\"},\n\t\t{desc: \"invalid literal--tire\", input: \"tire\"},\n\t\t{desc: \"invalid literal--nulll\", input: \"nulll\"},\n\t\t{desc: \"invalid literal--fals\", input: \"fals\"},\n\t\t{desc: \"invalid literal--falsee\", input: \"falsee\"},\n\t\t{desc: \"invalid literal--fake\", input: \"fake\"},\n\t\t{desc: \"invalid literal--bad\", input: \"bad\"},\n\t\t{desc: \"invalid number: -\", input: \"-\"},\n\t\t{desc: \"invalid number: --0\", input: \"--0\"},\n\t\t{desc: \"invalid number: -a\", input: \"-a\"},\n\t\t{desc: \"invalid number: 00\", input: \"00\"},\n\t\t{desc: \"invalid number: 01\", input: \"01\"},\n\t\t{desc: \"invalid number: 0-\", input: \"0-\"},\n\t\t{desc: \"invalid number: 1-\", input: \"1-\"},\n\t\t{desc: \"invalid number: 0..\", input: \"0..\"},\n\t\t{desc: \"invalid number: 0.-\", input: \"0.-\"},\n\t\t{desc: \"invalid number: 0..0\", input: \"0..0\"},\n\t\t{desc: \"invalid number: 0.1.0\", input: \"0.1.0\"},\n\t\t{desc: \"invalid number: 0e\", input: \"0e\"},\n\t\t{desc: \"invalid number: 0e.\", input: \"0e.\"},\n\t\t{desc: \"invalid number: 0e1.\", input: \"0e1.\"},\n\t\t{desc: \"invalid number: 0e1e\", input: \"0e1e\"},\n\t\t{desc: \"invalid number: 0e+.1\", input: \"0e+.1\"},\n\t\t{desc: \"invalid number: 0e+1.\", input: \"0e+1.\"},\n\t\t{desc: \"invalid number: 0e+1e\", input: \"0e+1e\"},\n\t}\n\n\tfor _, tc := range cases {\n\t\tt.Run(tc.desc, func(t *testing.T) {\n\t\t\tjs := &jsonScanner{r: strings.NewReader(tc.input)}\n\n\t\t\tc, err := js.nextToken()\n\t\t\texpectNilToken(t, c, tc.desc)\n\t\t\trequire.Error(t, err, tc.desc)\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "bson/map_codec.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bson\n\nimport (\n\t\"encoding\"\n\t\"errors\"\n\t\"fmt\"\n\t\"reflect\"\n\t\"strconv\"\n)\n\n// mapCodec is the Codec used for map values.\ntype mapCodec struct {\n\t// DecodeZerosMap causes DecodeValue to delete any existing values from Go maps in the destination\n\t// value passed to Decode before unmarshaling BSON documents into them.\n\tdecodeZerosMap bool\n\n\t// EncodeNilAsEmpty causes EncodeValue to marshal nil Go maps as empty BSON documents instead of\n\t// BSON null.\n\tencodeNilAsEmpty bool\n\n\t// EncodeKeysWithStringer causes the Encoder to convert Go map keys to BSON document field name\n\t// strings using fmt.Sprintf() instead of the default string conversion logic.\n\tencodeKeysWithStringer bool\n}\n\n// KeyMarshaler is the interface implemented by an object that can marshal itself into a string key.\n// This applies to types used as map keys and is similar to encoding.TextMarshaler.\ntype KeyMarshaler interface {\n\tMarshalKey() (key string, err error)\n}\n\n// KeyUnmarshaler is the interface implemented by an object that can unmarshal a string representation\n// of itself. This applies to types used as map keys and is similar to encoding.TextUnmarshaler.\n//\n// UnmarshalKey must be able to decode the form generated by MarshalKey.\n// UnmarshalKey must copy the text if it wishes to retain the text\n// after returning.\ntype KeyUnmarshaler interface {\n\tUnmarshalKey(key string) error\n}\n\n// EncodeValue is the ValueEncoder for map[*]* types.\nfunc (mc *mapCodec) EncodeValue(ec EncodeContext, vw ValueWriter, val reflect.Value) error {\n\tif !val.IsValid() || val.Kind() != reflect.Map {\n\t\treturn ValueEncoderError{Name: \"MapEncodeValue\", Kinds: []reflect.Kind{reflect.Map}, Received: val}\n\t}\n\n\tif val.IsNil() && !mc.encodeNilAsEmpty && !ec.nilMapAsEmpty {\n\t\t// If we have a nil map but we can't WriteNull, that means we're probably trying to encode\n\t\t// to a TopLevel document. We can't currently tell if this is what actually happened, but if\n\t\t// there's a deeper underlying problem, the error will also be returned from WriteDocument,\n\t\t// so just continue. The operations on a map reflection value are valid, so we can call\n\t\t// MapKeys within mapEncodeValue without a problem.\n\t\terr := vw.WriteNull()\n\t\tif err == nil {\n\t\t\treturn nil\n\t\t}\n\t}\n\n\tdw, err := vw.WriteDocument()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\terr = mc.encodeMapElements(ec, dw, val, nil)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn dw.WriteDocumentEnd()\n}\n\n// encodeMapElements handles encoding of the values of a map. The collisionFn returns\n// true if the provided key exists, this is mainly used for inline maps in the\n// struct codec.\nfunc (mc *mapCodec) encodeMapElements(ec EncodeContext, dw DocumentWriter, val reflect.Value, collisionFn func(string) bool) error {\n\telemType := val.Type().Elem()\n\tencoder, err := ec.LookupEncoder(elemType)\n\tif err != nil && elemType.Kind() != reflect.Interface {\n\t\treturn err\n\t}\n\n\tkeys := val.MapKeys()\n\tfor _, key := range keys {\n\t\tkeyStr, err := mc.encodeKey(key, ec.stringifyMapKeysWithFmt)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tif collisionFn != nil && collisionFn(keyStr) {\n\t\t\treturn fmt.Errorf(\"Key %s of inlined map conflicts with a struct field name\", key)\n\t\t}\n\n\t\tcurrEncoder, currVal, lookupErr := lookupElementEncoder(ec, encoder, val.MapIndex(key))\n\t\tif lookupErr != nil && !errors.Is(lookupErr, errInvalidValue) {\n\t\t\treturn lookupErr\n\t\t}\n\n\t\tvw, err := dw.WriteDocumentElement(keyStr)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tif errors.Is(lookupErr, errInvalidValue) {\n\t\t\terr = vw.WriteNull()\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\n\t\terr = currEncoder.EncodeValue(ec, vw, currVal)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// DecodeValue is the ValueDecoder for map[string/decimal]* types.\nfunc (mc *mapCodec) DecodeValue(dc DecodeContext, vr ValueReader, val reflect.Value) error {\n\tif val.Kind() != reflect.Map || (!val.CanSet() && val.IsNil()) {\n\t\treturn ValueDecoderError{Name: \"MapDecodeValue\", Kinds: []reflect.Kind{reflect.Map}, Received: val}\n\t}\n\n\tswitch vrType := vr.Type(); vrType {\n\tcase Type(0), TypeEmbeddedDocument:\n\tcase TypeNull:\n\t\tval.Set(reflect.Zero(val.Type()))\n\t\treturn vr.ReadNull()\n\tcase TypeUndefined:\n\t\tval.Set(reflect.Zero(val.Type()))\n\t\treturn vr.ReadUndefined()\n\tdefault:\n\t\treturn fmt.Errorf(\"cannot decode %v into a %s\", vrType, val.Type())\n\t}\n\n\tdr, err := vr.ReadDocument()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif val.IsNil() {\n\t\tval.Set(reflect.MakeMap(val.Type()))\n\t}\n\n\tif val.Len() > 0 && (mc.decodeZerosMap || dc.zeroMaps) {\n\t\tclearMap(val)\n\t}\n\n\teType := val.Type().Elem()\n\tdecoder, err := dc.LookupDecoder(eType)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tkeyType := val.Type().Key()\n\n\tfor {\n\t\tkey, vr, err := dr.ReadElement()\n\t\tif errors.Is(err, ErrEOD) {\n\t\t\tbreak\n\t\t}\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tk, err := mc.decodeKey(key, keyType)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\telem, err := decodeTypeOrValueWithInfo(decoder, dc, vr, eType)\n\t\tif err != nil {\n\t\t\treturn newDecodeError(key, err)\n\t\t}\n\n\t\tval.SetMapIndex(k, elem)\n\t}\n\treturn nil\n}\n\nfunc clearMap(m reflect.Value) {\n\tvar none reflect.Value\n\tfor _, k := range m.MapKeys() {\n\t\tm.SetMapIndex(k, none)\n\t}\n}\n\nfunc (mc *mapCodec) encodeKey(val reflect.Value, encodeKeysWithStringer bool) (string, error) {\n\tif mc.encodeKeysWithStringer || encodeKeysWithStringer {\n\t\treturn fmt.Sprint(val), nil\n\t}\n\n\t// keys of any string type are used directly\n\tif val.Kind() == reflect.String {\n\t\treturn val.String(), nil\n\t}\n\t// KeyMarshalers are marshaled\n\tif km, ok := val.Interface().(KeyMarshaler); ok {\n\t\tif val.Kind() == reflect.Ptr && val.IsNil() {\n\t\t\treturn \"\", nil\n\t\t}\n\t\tbuf, err := km.MarshalKey()\n\t\tif err == nil {\n\t\t\treturn buf, nil\n\t\t}\n\t\treturn \"\", err\n\t}\n\t// keys implement encoding.TextMarshaler are marshaled.\n\tif km, ok := val.Interface().(encoding.TextMarshaler); ok {\n\t\tif val.Kind() == reflect.Ptr && val.IsNil() {\n\t\t\treturn \"\", nil\n\t\t}\n\n\t\tbuf, err := km.MarshalText()\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\n\t\treturn string(buf), nil\n\t}\n\n\tswitch val.Kind() {\n\tcase reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:\n\t\treturn strconv.FormatInt(val.Int(), 10), nil\n\tcase reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:\n\t\treturn strconv.FormatUint(val.Uint(), 10), nil\n\t}\n\treturn \"\", fmt.Errorf(\"unsupported key type: %v\", val.Type())\n}\n\nvar (\n\tkeyUnmarshalerType  = reflect.TypeOf((*KeyUnmarshaler)(nil)).Elem()\n\ttextUnmarshalerType = reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem()\n)\n\nfunc (mc *mapCodec) decodeKey(key string, keyType reflect.Type) (reflect.Value, error) {\n\tkeyVal := reflect.ValueOf(key)\n\tvar err error\n\tswitch {\n\t// First, if EncodeKeysWithStringer is not enabled, try to decode withKeyUnmarshaler\n\tcase !mc.encodeKeysWithStringer && reflect.PtrTo(keyType).Implements(keyUnmarshalerType):\n\t\tkeyVal = reflect.New(keyType)\n\t\tv := keyVal.Interface().(KeyUnmarshaler)\n\t\terr = v.UnmarshalKey(key)\n\t\tkeyVal = keyVal.Elem()\n\t// Try to decode encoding.TextUnmarshalers.\n\tcase reflect.PtrTo(keyType).Implements(textUnmarshalerType):\n\t\tkeyVal = reflect.New(keyType)\n\t\tv := keyVal.Interface().(encoding.TextUnmarshaler)\n\t\terr = v.UnmarshalText([]byte(key))\n\t\tkeyVal = keyVal.Elem()\n\t// Otherwise, go to type specific behavior\n\tdefault:\n\t\tswitch keyType.Kind() {\n\t\tcase reflect.String:\n\t\t\tkeyVal = reflect.ValueOf(key).Convert(keyType)\n\t\tcase reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:\n\t\t\tn, parseErr := strconv.ParseInt(key, 10, 64)\n\t\t\tif parseErr != nil || reflect.Zero(keyType).OverflowInt(n) {\n\t\t\t\terr = fmt.Errorf(\"failed to unmarshal number key %v\", key)\n\t\t\t}\n\t\t\tkeyVal = reflect.ValueOf(n).Convert(keyType)\n\t\tcase reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:\n\t\t\tn, parseErr := strconv.ParseUint(key, 10, 64)\n\t\t\tif parseErr != nil || reflect.Zero(keyType).OverflowUint(n) {\n\t\t\t\terr = fmt.Errorf(\"failed to unmarshal number key %v\", key)\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tkeyVal = reflect.ValueOf(n).Convert(keyType)\n\t\tcase reflect.Float32, reflect.Float64:\n\t\t\tif mc.encodeKeysWithStringer {\n\t\t\t\tparsed, err := strconv.ParseFloat(key, 64)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn keyVal, fmt.Errorf(\"map key is defined to be a decimal type (%v) but got error %w\", keyType.Kind(), err)\n\t\t\t\t}\n\t\t\t\tkeyVal = reflect.ValueOf(parsed)\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tfallthrough\n\t\tdefault:\n\t\t\treturn keyVal, fmt.Errorf(\"unsupported key type: %v\", keyType)\n\t\t}\n\t}\n\treturn keyVal, err\n}\n"
  },
  {
    "path": "bson/marshal.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bson\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"sync\"\n)\n\nconst defaultDstCap = 256\n\nvar extjPool = sync.Pool{\n\tNew: func() any {\n\t\treturn new(extJSONValueWriter)\n\t},\n}\n\n// Marshaler is the interface implemented by types that can marshal themselves\n// into a valid BSON document.\n//\n// Implementations of Marshaler must return a full BSON document. To create\n// custom BSON marshaling behavior for individual values in a BSON document,\n// implement the ValueMarshaler interface instead.\ntype Marshaler interface {\n\tMarshalBSON() ([]byte, error)\n}\n\n// ValueMarshaler is the interface implemented by types that can marshal\n// themselves into a valid BSON value. The format of the returned bytes must\n// match the returned type.\n//\n// Implementations of ValueMarshaler must return an individual BSON value. To\n// create custom BSON marshaling behavior for an entire BSON document, implement\n// the Marshaler interface instead.\ntype ValueMarshaler interface {\n\tMarshalBSONValue() (typ byte, data []byte, err error)\n}\n\n// Pool of buffers for marshalling BSON.\nvar bufPool = sync.Pool{\n\tNew: func() any {\n\t\treturn new(bytes.Buffer)\n\t},\n}\n\n// Marshal returns the BSON encoding of val as a BSON document. If val is not a type that can be transformed into a\n// document, MarshalValue should be used instead.\n//\n// Marshal will use the default registry created by NewRegistry to recursively\n// marshal val into a []byte. Marshal will inspect struct tags and alter the\n// marshaling process accordingly.\nfunc Marshal(val any) ([]byte, error) {\n\tsw := bufPool.Get().(*bytes.Buffer)\n\tdefer func() {\n\t\t// Proper usage of a sync.Pool requires each entry to have approximately\n\t\t// the same memory cost. To obtain this property when the stored type\n\t\t// contains a variably-sized buffer, we add a hard limit on the maximum\n\t\t// buffer to place back in the pool. We limit the size to 16MiB because\n\t\t// that's the maximum wire message size supported by any current MongoDB\n\t\t// server.\n\t\t//\n\t\t// Comment based on\n\t\t// https://cs.opensource.google/go/go/+/refs/tags/go1.19:src/fmt/print.go;l=147\n\t\t//\n\t\t// Recycle byte slices that are smaller than 16MiB and at least half\n\t\t// occupied.\n\t\tif sw.Cap() < 16*1024*1024 && sw.Cap()/2 < sw.Len() {\n\t\t\tbufPool.Put(sw)\n\t\t}\n\t}()\n\tsw.Reset()\n\n\tvw := getDocumentWriter(sw)\n\tdefer putDocumentWriter(vw)\n\n\tenc := encPool.Get().(*Encoder)\n\tdefer encPool.Put(enc)\n\tenc.Reset(vw)\n\tenc.SetRegistry(defaultRegistry)\n\terr := enc.Encode(val)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tbuf := append([]byte(nil), sw.Bytes()...)\n\treturn buf, nil\n}\n\n// MarshalValue returns the BSON encoding of val.\n//\n// MarshalValue will use bson.NewRegistry() to transform val into a BSON value. If val is a struct, this function will\n// inspect struct tags and alter the marshalling process accordingly.\nfunc MarshalValue(val any) (Type, []byte, error) {\n\tsw := bufPool.Get().(*bytes.Buffer)\n\tdefer func() {\n\t\t// Proper usage of a sync.Pool requires each entry to have approximately\n\t\t// the same memory cost. To obtain this property when the stored type\n\t\t// contains a variably-sized buffer, we add a hard limit on the maximum\n\t\t// buffer to place back in the pool. We limit the size to 16MiB because\n\t\t// that's the maximum wire message size supported by any current MongoDB\n\t\t// server.\n\t\t//\n\t\t// Comment based on\n\t\t// https://cs.opensource.google/go/go/+/refs/tags/go1.19:src/fmt/print.go;l=147\n\t\t//\n\t\t// Recycle byte slices that are smaller than 16MiB and at least half\n\t\t// occupied.\n\t\tif sw.Cap() < 16*1024*1024 && sw.Cap()/2 < sw.Len() {\n\t\t\tbufPool.Put(sw)\n\t\t}\n\t}()\n\tsw.Reset()\n\tvwFlusher := newDocumentWriter(sw)\n\tvw, err := vwFlusher.WriteDocumentElement(\"\")\n\tif err != nil {\n\t\treturn 0, nil, err\n\t}\n\n\t// get an Encoder and encode the value\n\tenc := encPool.Get().(*Encoder)\n\tdefer encPool.Put(enc)\n\tenc.Reset(vw)\n\tenc.SetRegistry(defaultRegistry)\n\tif err := enc.Encode(val); err != nil {\n\t\treturn 0, nil, err\n\t}\n\n\t// flush the bytes written because we cannot guarantee that a full document has been written\n\t// after the flush, *sw will be in the format\n\t// [value type, 0 (null byte to indicate end of empty element name), value bytes..]\n\tif err := vwFlusher.Flush(); err != nil {\n\t\treturn 0, nil, err\n\t}\n\ttyp := sw.Next(2)\n\tclone := append([]byte{}, sw.Bytes()...) // Don't hand out a shared reference to byte buffer bytes\n\t// and fully copy the data. The byte buffer is (potentially) reused\n\t// and handing out only a reference to the bytes may lead to race-conditions with the buffer.\n\treturn Type(typ[0]), clone, nil\n}\n\n// MarshalExtJSON returns the extended JSON encoding of val.\nfunc MarshalExtJSON(val any, canonical, escapeHTML bool) ([]byte, error) {\n\tsw := sliceWriter(make([]byte, 0, defaultDstCap))\n\tejvw := extjPool.Get().(*extJSONValueWriter)\n\tejvw.reset(sw, canonical, escapeHTML)\n\tejvw.w = &sw\n\tdefer func() {\n\t\tejvw.buf = nil\n\t\tejvw.w = nil\n\t\textjPool.Put(ejvw)\n\t}()\n\n\tenc := encPool.Get().(*Encoder)\n\tdefer encPool.Put(enc)\n\n\tenc.Reset(ejvw)\n\tenc.ec = EncodeContext{Registry: defaultRegistry}\n\n\terr := enc.Encode(val)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn sw, nil\n}\n\n// IndentExtJSON will prefix and indent the provided extended JSON src and append it to dst.\nfunc IndentExtJSON(dst *bytes.Buffer, src []byte, prefix, indent string) error {\n\treturn json.Indent(dst, src, prefix, indent)\n}\n\n// MarshalExtJSONIndent returns the extended JSON encoding of val with each line with prefixed\n// and indented.\nfunc MarshalExtJSONIndent(val any, canonical, escapeHTML bool, prefix, indent string) ([]byte, error) {\n\tmarshaled, err := MarshalExtJSON(val, canonical, escapeHTML)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar buf bytes.Buffer\n\terr = IndentExtJSON(&buf, marshaled, prefix, indent)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn buf.Bytes(), nil\n}\n"
  },
  {
    "path": "bson/marshal_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bson\n\nimport (\n\t\"bytes\"\n\t\"errors\"\n\t\"fmt\"\n\t\"reflect\"\n\t\"sync\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/google/go-cmp/cmp\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/require\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n)\n\nfunc TestMarshalWithRegistry(t *testing.T) {\n\tfor _, tc := range marshalingTestCases {\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\tvar reg *Registry\n\t\t\tif tc.reg != nil {\n\t\t\t\treg = tc.reg\n\t\t\t} else {\n\t\t\t\treg = defaultRegistry\n\t\t\t}\n\t\t\tbuf := new(bytes.Buffer)\n\t\t\tvw := NewDocumentWriter(buf)\n\t\t\tenc := NewEncoder(vw)\n\t\t\tenc.SetRegistry(reg)\n\t\t\terr := enc.Encode(tc.val)\n\t\t\tnoerr(t, err)\n\n\t\t\tif got := buf.Bytes(); !bytes.Equal(got, tc.want) {\n\t\t\t\tt.Errorf(\"Bytes are not equal. got %v; want %v\", got, tc.want)\n\t\t\t\tt.Errorf(\"Bytes:\\n%v\\n%v\", got, tc.want)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestMarshalWithContext(t *testing.T) {\n\tfor _, tc := range marshalingTestCases {\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\tvar reg *Registry\n\t\t\tif tc.reg != nil {\n\t\t\t\treg = tc.reg\n\t\t\t} else {\n\t\t\t\treg = defaultRegistry\n\t\t\t}\n\t\t\tbuf := new(bytes.Buffer)\n\t\t\tvw := NewDocumentWriter(buf)\n\t\t\tenc := NewEncoder(vw)\n\t\t\tenc.IntMinSize()\n\t\t\tenc.SetRegistry(reg)\n\t\t\terr := enc.Encode(tc.val)\n\t\t\tnoerr(t, err)\n\n\t\t\tif got := buf.Bytes(); !bytes.Equal(got, tc.want) {\n\t\t\t\tt.Errorf(\"Bytes are not equal. got %v; want %v\", got, tc.want)\n\t\t\t\tt.Errorf(\"Bytes:\\n%v\\n%v\", got, tc.want)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestMarshalExtJSON(t *testing.T) {\n\tt.Run(\"MarshalExtJSON\", func(t *testing.T) {\n\t\ttype teststruct struct{ Foo int }\n\t\tval := teststruct{1}\n\t\tgot, err := MarshalExtJSON(val, true, false)\n\t\tnoerr(t, err)\n\t\twant := []byte(`{\"foo\":{\"$numberInt\":\"1\"}}`)\n\t\tif !bytes.Equal(got, want) {\n\t\t\tt.Errorf(\"Bytes are not equal. got %v; want %v\", got, want)\n\t\t\tt.Errorf(\"Bytes:\\n%s\\n%s\", got, want)\n\t\t}\n\t})\n}\n\nfunc TestMarshal_roundtripFromBytes(t *testing.T) {\n\tbefore := []byte{\n\t\t// length\n\t\t0x1c, 0x0, 0x0, 0x0,\n\n\t\t// --- begin array ---\n\n\t\t// type - document\n\t\t0x3,\n\t\t// key - \"foo\"\n\t\t0x66, 0x6f, 0x6f, 0x0,\n\n\t\t// length\n\t\t0x12, 0x0, 0x0, 0x0,\n\t\t// type - string\n\t\t0x2,\n\t\t// key - \"bar\"\n\t\t0x62, 0x61, 0x72, 0x0,\n\t\t// value - string length\n\t\t0x4, 0x0, 0x0, 0x0,\n\t\t// value - \"baz\"\n\t\t0x62, 0x61, 0x7a, 0x0,\n\n\t\t// null terminator\n\t\t0x0,\n\n\t\t// --- end array ---\n\n\t\t// null terminator\n\t\t0x0,\n\t}\n\n\tvar doc D\n\trequire.NoError(t, Unmarshal(before, &doc))\n\n\tafter, err := Marshal(doc)\n\trequire.NoError(t, err)\n\n\trequire.True(t, bytes.Equal(before, after))\n}\n\nfunc TestMarshal_roundtripFromDoc(t *testing.T) {\n\tbefore := D{\n\t\t{\"foo\", \"bar\"},\n\t\t{\"baz\", int64(-27)},\n\t\t{\"bing\", A{nil, Regex{Pattern: \"word\", Options: \"i\"}}},\n\t}\n\n\tb, err := Marshal(before)\n\trequire.NoError(t, err)\n\n\tvar after D\n\trequire.NoError(t, Unmarshal(b, &after))\n\n\tif !cmp.Equal(after, before) {\n\t\tt.Errorf(\"Documents to not match. got %v; want %v\", after, before)\n\t}\n}\n\nfunc TestCachingEncodersNotSharedAcrossRegistries(t *testing.T) {\n\t// Encoders that have caches for recursive encoder lookup should not be shared across Registry instances. Otherwise,\n\t// the first EncodeValue call would cache an encoder and a subsequent call would see that encoder even if a\n\t// different Registry is used.\n\n\t// Create a custom Registry that negates int32 values when encoding.\n\tvar encodeInt32 ValueEncoderFunc = func(_ EncodeContext, vw ValueWriter, val reflect.Value) error {\n\t\tif val.Kind() != reflect.Int32 {\n\t\t\treturn fmt.Errorf(\"expected kind to be int32, got %v\", val.Kind())\n\t\t}\n\n\t\treturn vw.WriteInt32(int32(val.Int()) * -1)\n\t}\n\tcustomReg := NewRegistry()\n\tcustomReg.RegisterTypeEncoder(tInt32, encodeInt32)\n\n\t// Helper function to run the test and make assertions. The provided original value should result in the document\n\t// {\"x\": {$numberInt: 1}} when marshalled with the default registry.\n\tverifyResults := func(t *testing.T, original any) {\n\t\t// Marshal using the default and custom registries. Assert that the result is {x: 1} and {x: -1}, respectively.\n\n\t\tfirst, err := Marshal(original)\n\t\tassert.Nil(t, err, \"Marshal error: %v\", err)\n\t\texpectedFirst := Raw(bsoncore.BuildDocumentFromElements(\n\t\t\tnil,\n\t\t\tbsoncore.AppendInt32Element(nil, \"x\", 1),\n\t\t))\n\t\tassert.Equal(t, expectedFirst, Raw(first), \"expected document %v, got %v\", expectedFirst, Raw(first))\n\n\t\tbuf := new(bytes.Buffer)\n\t\tvw := NewDocumentWriter(buf)\n\t\tenc := NewEncoder(vw)\n\t\tenc.SetRegistry(customReg)\n\t\terr = enc.Encode(original)\n\t\tassert.Nil(t, err, \"Encode error: %v\", err)\n\t\tsecond := buf.Bytes()\n\t\texpectedSecond := Raw(bsoncore.BuildDocumentFromElements(\n\t\t\tnil,\n\t\t\tbsoncore.AppendInt32Element(nil, \"x\", -1),\n\t\t))\n\t\tassert.Equal(t, expectedSecond, Raw(second), \"expected document %v, got %v\", expectedSecond, Raw(second))\n\t}\n\n\tt.Run(\"struct\", func(t *testing.T) {\n\t\ttype Struct struct {\n\t\t\tX int32\n\t\t}\n\t\tverifyResults(t, Struct{\n\t\t\tX: 1,\n\t\t})\n\t})\n\tt.Run(\"pointer\", func(t *testing.T) {\n\t\ti32 := int32(1)\n\t\tverifyResults(t, M{\n\t\t\t\"x\": &i32,\n\t\t})\n\t})\n}\n\nfunc TestNullBytes(t *testing.T) {\n\tt.Run(\"element keys\", func(t *testing.T) {\n\t\tdoc := D{{\"a\\x00\", \"foobar\"}}\n\t\tres, err := Marshal(doc)\n\t\twant := errors.New(\"BSON element key cannot contain null bytes\")\n\t\tassert.Equal(t, want, err, \"expected Marshal error %v, got error %v with result %q\", want, err, Raw(res))\n\t})\n\n\tt.Run(\"regex values\", func(t *testing.T) {\n\t\twantErr := errors.New(\"BSON regex values cannot contain null bytes\")\n\n\t\ttestCases := []struct {\n\t\t\tname    string\n\t\t\tpattern string\n\t\t\toptions string\n\t\t}{\n\t\t\t{\"null bytes in pattern\", \"a\\x00\", \"i\"},\n\t\t\t{\"null bytes in options\", \"pattern\", \"i\\x00\"},\n\t\t}\n\t\tfor _, tc := range testCases {\n\t\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\t\tregex := Regex{\n\t\t\t\t\tPattern: tc.pattern,\n\t\t\t\t\tOptions: tc.options,\n\t\t\t\t}\n\t\t\t\tres, err := Marshal(D{{\"foo\", regex}})\n\t\t\t\tassert.Equal(t, wantErr, err, \"expected Marshal error %v, got error %v with result %q\", wantErr, err, Raw(res))\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"sub document field name\", func(t *testing.T) {\n\t\tdoc := D{{\"foo\", D{{\"foobar\", D{{\"a\\x00\", \"foobar\"}}}}}}\n\t\tres, err := Marshal(doc)\n\t\twantErr := errors.New(\"BSON element key cannot contain null bytes\")\n\t\tassert.Equal(t, wantErr, err, \"expected Marshal error %v, got error %v with result %q\", wantErr, err, Raw(res))\n\t})\n}\n\nfunc TestMarshalExtJSONIndent(t *testing.T) {\n\ttype indentTestCase struct {\n\t\tname            string\n\t\tval             any\n\t\texpectedExtJSON string\n\t}\n\n\t// expectedExtJSON must be written as below because single-quoted\n\t// literal strings capture undesired code formatting tabs\n\ttestCases := []indentTestCase{\n\t\t{\n\t\t\t\"empty val\",\n\t\t\tstruct{}{},\n\t\t\t`{}`,\n\t\t},\n\t\t{\n\t\t\t\"embedded struct\",\n\t\t\tstruct {\n\t\t\t\tEmbedded any    `json:\"embedded\"`\n\t\t\t\tFoo      string `json:\"foo\"`\n\t\t\t}{\n\t\t\t\tEmbedded: struct {\n\t\t\t\t\tName string `json:\"name\"`\n\t\t\t\t\tWord string `json:\"word\"`\n\t\t\t\t}{\n\t\t\t\t\tName: \"test\",\n\t\t\t\t\tWord: \"word\",\n\t\t\t\t},\n\t\t\t\tFoo: \"bar\",\n\t\t\t},\n\t\t\t\"{\\n\\t\\\"embedded\\\": {\\n\\t\\t\\\"name\\\": \\\"test\\\",\\n\\t\\t\\\"word\\\": \\\"word\\\"\\n\\t},\\n\\t\\\"foo\\\": \\\"bar\\\"\\n}\",\n\t\t},\n\t\t{\n\t\t\t\"date struct\",\n\t\t\tstruct {\n\t\t\t\tFoo  string    `json:\"foo\"`\n\t\t\t\tDate time.Time `json:\"date\"`\n\t\t\t}{\n\t\t\t\tFoo:  \"bar\",\n\t\t\t\tDate: time.Date(2000, time.January, 1, 12, 0, 0, 0, time.UTC),\n\t\t\t},\n\t\t\t\"{\\n\\t\\\"foo\\\": \\\"bar\\\",\\n\\t\\\"date\\\": {\\n\\t\\t\\\"$date\\\": {\\n\\t\\t\\t\\\"$numberLong\\\": \\\"946728000000\\\"\\n\\t\\t}\\n\\t}\\n}\",\n\t\t},\n\t\t{\n\t\t\t\"float struct\",\n\t\t\tstruct {\n\t\t\t\tFoo   string  `json:\"foo\"`\n\t\t\t\tFloat float32 `json:\"float\"`\n\t\t\t}{\n\t\t\t\tFoo:   \"bar\",\n\t\t\t\tFloat: 3.14,\n\t\t\t},\n\t\t\t\"{\\n\\t\\\"foo\\\": \\\"bar\\\",\\n\\t\\\"float\\\": {\\n\\t\\t\\\"$numberDouble\\\": \\\"3.140000104904175\\\"\\n\\t}\\n}\",\n\t\t},\n\t}\n\n\tfor _, tc := range testCases {\n\t\ttc := tc\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\tt.Parallel()\n\t\t\textJSONBytes, err := MarshalExtJSONIndent(tc.val, true, false, \"\", \"\\t\")\n\t\t\tassert.Nil(t, err, \"Marshal indent error: %v\", err)\n\n\t\t\texpectedExtJSONBytes := []byte(tc.expectedExtJSON)\n\n\t\t\tassert.Equal(t, expectedExtJSONBytes, extJSONBytes, \"expected:\\n%s\\ngot:\\n%s\", expectedExtJSONBytes, extJSONBytes)\n\t\t})\n\t}\n}\n\nfunc TestMarshalConcurrently(t *testing.T) {\n\tt.Parallel()\n\n\tconst size = 10_000\n\n\twg := sync.WaitGroup{}\n\twg.Add(size)\n\tfor i := 0; i < size; i++ {\n\t\tgo func() {\n\t\t\tdefer wg.Done()\n\t\t\t_, _ = Marshal(struct{ LastError error }{})\n\t\t}()\n\t}\n\twg.Wait()\n}\n"
  },
  {
    "path": "bson/marshal_value_cases_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2023-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bson\n\nimport (\n\t\"io\"\n\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n)\n\nvar marshalValueTestCases = []marshalValueTestCase{\n\t{\n\t\tname:     \"double\",\n\t\tval:      3.14,\n\t\tbsontype: TypeDouble,\n\t\tbytes:    bsoncore.AppendDouble(nil, 3.14),\n\t},\n\t{\n\t\tname:     \"string\",\n\t\tval:      \"hello world\",\n\t\tbsontype: TypeString,\n\t\tbytes:    bsoncore.AppendString(nil, \"hello world\"),\n\t},\n\t{\n\t\tname:     \"binary\",\n\t\tval:      Binary{1, []byte{1, 2}},\n\t\tbsontype: TypeBinary,\n\t\tbytes:    bsoncore.AppendBinary(nil, 1, []byte{1, 2}),\n\t},\n\t{\n\t\tname:     \"undefined\",\n\t\tval:      Undefined{},\n\t\tbsontype: TypeUndefined,\n\t\tbytes:    []byte{},\n\t},\n\t{\n\t\tname:     \"object id\",\n\t\tval:      ObjectID{103, 116, 166, 161, 70, 33, 67, 139, 164, 144, 255, 112},\n\t\tbsontype: TypeObjectID,\n\t\tbytes:    bsoncore.AppendObjectID(nil, ObjectID{103, 116, 166, 161, 70, 33, 67, 139, 164, 144, 255, 112}),\n\t},\n\t{\n\t\tname:     \"boolean\",\n\t\tval:      true,\n\t\tbsontype: TypeBoolean,\n\t\tbytes:    bsoncore.AppendBoolean(nil, true),\n\t},\n\t{\n\t\tname:     \"datetime\",\n\t\tval:      DateTime(5),\n\t\tbsontype: TypeDateTime,\n\t\tbytes:    bsoncore.AppendDateTime(nil, 5),\n\t},\n\t{\n\t\tname:     \"null\",\n\t\tval:      Null{},\n\t\tbsontype: TypeNull,\n\t\tbytes:    []byte{},\n\t},\n\t{\n\t\tname:     \"regex\",\n\t\tval:      Regex{Pattern: \"pattern\", Options: \"imx\"},\n\t\tbsontype: TypeRegex,\n\t\tbytes:    bsoncore.AppendRegex(nil, \"pattern\", \"imx\"),\n\t},\n\t{\n\t\tname: \"dbpointer\",\n\t\tval: DBPointer{\n\t\t\tDB:      \"db\",\n\t\t\tPointer: ObjectID{103, 116, 166, 161, 70, 33, 67, 139, 164, 144, 255, 112},\n\t\t},\n\t\tbsontype: TypeDBPointer,\n\t\tbytes: bsoncore.AppendDBPointer(\n\t\t\tnil,\n\t\t\t\"db\",\n\t\t\tObjectID{103, 116, 166, 161, 70, 33, 67, 139, 164, 144, 255, 112},\n\t\t),\n\t},\n\t{\n\t\tname:     \"javascript\",\n\t\tval:      JavaScript(\"js\"),\n\t\tbsontype: TypeJavaScript,\n\t\tbytes:    bsoncore.AppendJavaScript(nil, \"js\"),\n\t},\n\t{\n\t\tname:     \"symbol\",\n\t\tval:      Symbol(\"symbol\"),\n\t\tbsontype: TypeSymbol,\n\t\tbytes:    bsoncore.AppendSymbol(nil, \"symbol\"),\n\t},\n\t{\n\t\tname:     \"code with scope\",\n\t\tval:      CodeWithScope{Code: \"code\", Scope: D{{\"a\", \"b\"}}},\n\t\tbsontype: TypeCodeWithScope,\n\t\tbytes: bsoncore.AppendCodeWithScope(\n\t\t\tnil,\n\t\t\t\"code\",\n\t\t\tbsoncore.NewDocumentBuilder().\n\t\t\t\tAppendString(\"a\", \"b\").\n\t\t\t\tBuild()),\n\t},\n\t{\n\t\tname:     \"int32\",\n\t\tval:      5,\n\t\tbsontype: TypeInt32,\n\t\tbytes:    bsoncore.AppendInt32(nil, 5),\n\t},\n\t{\n\t\tname:     \"int64\",\n\t\tval:      int64(5),\n\t\tbsontype: TypeInt64,\n\t\tbytes:    bsoncore.AppendInt64(nil, 5),\n\t},\n\t{\n\t\tname:     \"timestamp\",\n\t\tval:      Timestamp{T: 1, I: 5},\n\t\tbsontype: TypeTimestamp,\n\t\tbytes:    bsoncore.AppendTimestamp(nil, 1, 5),\n\t},\n\t{\n\t\tname:     \"decimal128\",\n\t\tval:      NewDecimal128(5, 10),\n\t\tbsontype: TypeDecimal128,\n\t\tbytes:    bsoncore.AppendDecimal128(nil, 5, 10),\n\t},\n\t{\n\t\tname:     \"min key\",\n\t\tval:      MinKey{},\n\t\tbsontype: TypeMinKey,\n\t\tbytes:    []byte{},\n\t},\n\t{\n\t\tname:     \"max key\",\n\t\tval:      MaxKey{},\n\t\tbsontype: TypeMaxKey,\n\t\tbytes:    []byte{},\n\t},\n\t{\n\t\tname:     \"struct\",\n\t\tval:      marshalValueStruct{Foo: 10},\n\t\tbsontype: TypeEmbeddedDocument,\n\t\tbytes: bsoncore.NewDocumentBuilder().\n\t\t\tAppendInt32(\"foo\", 10).\n\t\t\tBuild(),\n\t},\n\t{\n\t\tname:     \"D\",\n\t\tval:      D{{\"foo\", int32(10)}},\n\t\tbsontype: TypeEmbeddedDocument,\n\t\tbytes: bsoncore.NewDocumentBuilder().\n\t\t\tAppendInt32(\"foo\", 10).\n\t\t\tBuild(),\n\t},\n\t{\n\t\tname:     \"M\",\n\t\tval:      M{\"foo\": int32(10)},\n\t\tbsontype: TypeEmbeddedDocument,\n\t\tbytes: bsoncore.NewDocumentBuilder().\n\t\t\tAppendInt32(\"foo\", 10).\n\t\t\tBuild(),\n\t},\n\t{\n\t\tname:     \"ValueMarshaler\",\n\t\tval:      marshalValueMarshaler{Foo: 10},\n\t\tbsontype: TypeInt32,\n\t\tbytes:    bsoncore.AppendInt32(nil, 10),\n\t},\n}\n\n// helper type for testing MarshalValue that implements io.Reader\ntype marshalValueInterfaceInner struct {\n\tFoo int\n}\n\nvar _ io.Reader = marshalValueInterfaceInner{}\n\nfunc (marshalValueInterfaceInner) Read([]byte) (int, error) {\n\treturn 0, nil\n}\n\n// helper type for testing MarshalValue that contains an interface\ntype marshalValueInterfaceOuter struct {\n\tReader io.Reader\n}\n\n// helper type for testing MarshalValue that implements ValueMarshaler\ntype marshalValueMarshaler struct {\n\tFoo int\n}\n\nvar _ ValueMarshaler = marshalValueMarshaler{}\n\nfunc (mvi marshalValueMarshaler) MarshalBSONValue() (byte, []byte, error) {\n\treturn byte(TypeInt32), bsoncore.AppendInt32(nil, int32(mvi.Foo)), nil\n}\n\nvar _ ValueUnmarshaler = &marshalValueMarshaler{}\n\nfunc (mvi *marshalValueMarshaler) UnmarshalBSONValue(_ byte, b []byte) error {\n\tv, _, _ := bsoncore.ReadInt32(b)\n\tmvi.Foo = int(v)\n\treturn nil\n}\n\ntype marshalValueStruct struct {\n\tFoo int\n}\n\ntype marshalValueTestCase struct {\n\tname     string\n\tval      any\n\tbsontype Type\n\tbytes    []byte\n}\n"
  },
  {
    "path": "bson/marshal_value_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bson\n\nimport (\n\t\"slices\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/require\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n)\n\nfunc TestMarshalValue(t *testing.T) {\n\tt.Parallel()\n\n\ttestCases := slices.Clone(marshalValueTestCases)\n\ttestCases = append(testCases, marshalValueTestCase{\n\t\tname: \"interface\",\n\t\tval: marshalValueInterfaceOuter{\n\t\t\tReader: marshalValueInterfaceInner{\n\t\t\t\tFoo: 10,\n\t\t\t},\n\t\t},\n\t\tbsontype: TypeEmbeddedDocument,\n\t\tbytes: bsoncore.NewDocumentBuilder().\n\t\t\tAppendDocument(\"reader\", bsoncore.NewDocumentBuilder().\n\t\t\t\tAppendInt32(\"foo\", 10).\n\t\t\t\tBuild()).\n\t\t\tBuild(),\n\t})\n\n\tfor _, tc := range testCases {\n\t\ttc := tc\n\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tvalueType, valueBytes, err := MarshalValue(tc.val)\n\t\t\tassert.Nil(t, err, \"MarshalValue error: %v\", err)\n\t\t\tcompareMarshalValueResults(t, tc, valueType, valueBytes)\n\t\t})\n\t}\n\n\tt.Run(\"returns distinct address ranges\", func(t *testing.T) {\n\t\t// Call MarshalValue in a loop with the same large value (make sure to\n\t\t// trigger the buffer pooling, which currently doesn't happen for very\n\t\t// small values). Compare the previous and current BSON byte slices and\n\t\t// make sure they always have distinct memory ranges.\n\t\t//\n\t\t// Don't run this test in parallel to maximize the chance that we get\n\t\t// the same pooled buffer for most/all calls.\n\t\tlargeVal := strings.Repeat(\"1234567890\", 100_000)\n\t\tvar prev []byte\n\t\tfor i := 0; i < 20; i++ {\n\t\t\t_, b, err := MarshalValue(largeVal)\n\t\t\trequire.NoError(t, err)\n\n\t\t\tassert.DifferentAddressRanges(t, b, prev)\n\t\t\tprev = b\n\t\t}\n\t})\n}\n\nfunc compareMarshalValueResults(t *testing.T, tc marshalValueTestCase, gotType Type, gotBytes []byte) {\n\tt.Helper()\n\texpectedValue := RawValue{Type: tc.bsontype, Value: tc.bytes}\n\tgotValue := RawValue{Type: gotType, Value: gotBytes}\n\tassert.Equal(t, expectedValue, gotValue, \"value mismatch; expected %s, got %s\", expectedValue, gotValue)\n}\n\n// benchmark covering GODRIVER-2779\nfunc BenchmarkSliceCodecMarshal(b *testing.B) {\n\ttestStruct := unmarshalerNonPtrStruct{B: []byte(strings.Repeat(\"t\", 4096))}\n\n\tb.ResetTimer()\n\tb.RunParallel(func(pb *testing.PB) {\n\t\tfor pb.Next() {\n\t\t\t_, _, err := MarshalValue(testStruct)\n\t\t\tif err != nil {\n\t\t\t\tb.Fatal(err)\n\t\t\t}\n\t\t}\n\t})\n}\n"
  },
  {
    "path": "bson/marshaling_cases_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bson\n\ntype marshalingTestCase struct {\n\tname string\n\treg  *Registry\n\tval  any\n\twant []byte\n}\n\nvar marshalingTestCases = []marshalingTestCase{\n\t{\n\t\t\"small struct\",\n\t\tnil,\n\t\tstruct {\n\t\t\tFoo bool\n\t\t}{Foo: true},\n\t\tdocToBytes(D{{\"foo\", true}}),\n\t},\n}\n"
  },
  {
    "path": "bson/mgocompat/doc.go",
    "content": "// Copyright (C) MongoDB, Inc. 2022-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\n// Package mgocompat provides Registry, a BSON registry compatible with globalsign/mgo's BSON,\n// with some remaining differences. It also provides RegistryRespectNilValues for compatibility\n// with mgo's BSON with RespectNilValues set to true. A registry can be configured on a\n// mongo.Client with the SetRegistry option. See the bson docs for more details on registries.\n//\n// Registry supports Getter and Setter equivalents by registering hooks. Note that if a value\n// matches the hook for bson.Marshaler, bson.ValueMarshaler, or bson.Proxy, that\n// hook will take priority over the Getter hook. The same is true for the hooks for\n// bson.Unmarshaler and bson.ValueUnmarshaler and the Setter hook.\n//\n// The functional differences between Registry and globalsign/mgo's BSON library are:\n//\n// 1) Registry errors instead of silently skipping mismatched types when decoding.\n//\n// 2) Registry does not have special handling for marshaling array ops (\"$in\", \"$nin\", \"$all\").\n//\n// The driver uses different types than mgo's bson. The differences are:\n//\n//  1. The driver's bson.RawValue is equivalent to mgo's bson.Raw, but uses Value instead of Data and uses Type,\n//     which is a bsontype.Type object that wraps a byte, instead of bson.Raw's Kind, a byte.\n//\n//  2. The driver uses bson.ObjectID, which is a [12]byte instead of mgo's\n//     bson.ObjectId, a string. Due to this, the zero value marshals and unmarshals differently\n//     for Extended JSON, with the driver marshaling as {\"ID\":\"000000000000000000000000\"} and\n//     mgo as {\"Id\":\"\"}. The driver can unmarshal {\"ID\":\"\"} to a bson.ObjectID.\n//\n//  3. The driver's bson.Symbol is equivalent to mgo's bson.Symbol.\n//\n//  4. The driver uses bson.Timestamp instead of mgo's bson.MongoTimestamp. While\n//     MongoTimestamp is an int64, bson.Timestamp stores the time and counter as two separate\n//     uint32 values, T and I respectively.\n//\n//  5. The driver uses bson.MinKey and bson.MaxKey, which are struct{}, instead\n//     of mgo's bson.MinKey and bson.MaxKey, which are int64.\n//\n//  6. The driver's bson.Undefined is equivalent to mgo's bson.Undefined.\n//\n//  7. The driver's bson.Binary is equivalent to mgo's bson.Binary, with variables named Subtype\n//     and Data instead of Kind and Data.\n//\n//  8. The driver's bson.Regex is equivalent to mgo's bson.RegEx.\n//\n//  9. The driver's bson.JavaScript is equivalent to mgo's bson.JavaScript with no\n//     scope and bson.CodeWithScope is equivalent to mgo's bson.JavaScript with scope.\n//\n//  10. The driver's bson.DBPointer is equivalent to mgo's bson.DBPointer, with variables\n//     named DB and Pointer instead of Namespace and Id.\n//\n//  11. When implementing the Setter interface, mgocompat.ErrSetZero is equivalent to mgo's\n//     bson.ErrSetZero.\npackage mgocompat\n"
  },
  {
    "path": "bson/mgocompat/registry.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage mgocompat\n\nimport (\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n)\n\n// Registry is the mgo compatible bson.Registry. It contains the default and\n// primitive codecs with mgo compatible options.\nvar Registry = bson.NewMgoRegistry()\n\n// RespectNilValuesRegistry is the bson.Registry compatible with mgo withSetRespectNilValues set to true.\nvar RespectNilValuesRegistry = bson.NewRespectNilValuesMgoRegistry()\n"
  },
  {
    "path": "bson/mgoregistry.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bson\n\nimport (\n\t\"errors\"\n\t\"reflect\"\n)\n\nvar (\n\t// ErrMgoSetZero may be returned from a SetBSON method to have the value set to its respective zero value.\n\tErrMgoSetZero = errors.New(\"set to zero\")\n\n\ttInt            = reflect.TypeOf(int(0))\n\ttM              = reflect.TypeOf(M{})\n\ttInterfaceSlice = reflect.TypeOf([]any{})\n\ttGetter         = reflect.TypeOf((*getter)(nil)).Elem()\n\ttSetter         = reflect.TypeOf((*setter)(nil)).Elem()\n)\n\n// NewMgoRegistry creates a new bson.Registry configured with the default encoders and decoders.\nfunc NewMgoRegistry() *Registry {\n\tmapCodec := &mapCodec{\n\t\tdecodeZerosMap:         true,\n\t\tencodeNilAsEmpty:       true,\n\t\tencodeKeysWithStringer: true,\n\t}\n\tstructCodec := &structCodec{\n\t\tinlineMapEncoder:        mapCodec,\n\t\tdecodeZeroStruct:        true,\n\t\tencodeOmitDefaultStruct: true,\n\t\tallowUnexportedFields:   true,\n\t}\n\tuintCodec := &uintCodec{encodeToMinSize: true}\n\n\treg := NewRegistry()\n\treg.RegisterTypeDecoder(tEmpty, &emptyInterfaceCodec{decodeBinaryAsSlice: true})\n\treg.RegisterKindDecoder(reflect.String, ValueDecoderFunc(mgoStringDecodeValue))\n\treg.RegisterKindDecoder(reflect.Struct, structCodec)\n\treg.RegisterKindDecoder(reflect.Map, mapCodec)\n\treg.RegisterTypeEncoder(tByteSlice, &byteSliceCodec{encodeNilAsEmpty: true})\n\treg.RegisterKindEncoder(reflect.Struct, structCodec)\n\treg.RegisterKindEncoder(reflect.Slice, &sliceCodec{encodeNilAsEmpty: true})\n\treg.RegisterKindEncoder(reflect.Map, mapCodec)\n\treg.RegisterKindEncoder(reflect.Uint, uintCodec)\n\treg.RegisterKindEncoder(reflect.Uint8, uintCodec)\n\treg.RegisterKindEncoder(reflect.Uint16, uintCodec)\n\treg.RegisterKindEncoder(reflect.Uint32, uintCodec)\n\treg.RegisterKindEncoder(reflect.Uint64, uintCodec)\n\treg.RegisterTypeMapEntry(TypeInt32, tInt)\n\treg.RegisterTypeMapEntry(TypeDateTime, tTime)\n\treg.RegisterTypeMapEntry(TypeArray, tInterfaceSlice)\n\treg.RegisterTypeMapEntry(Type(0), tM)\n\treg.RegisterTypeMapEntry(TypeEmbeddedDocument, tM)\n\treg.RegisterInterfaceEncoder(tGetter, ValueEncoderFunc(getterEncodeValue))\n\treg.RegisterInterfaceDecoder(tSetter, ValueDecoderFunc(setterDecodeValue))\n\treturn reg\n}\n\n// NewRespectNilValuesMgoRegistry creates a new bson.Registry configured to behave like mgo/bson\n// with RespectNilValues set to true.\nfunc NewRespectNilValuesMgoRegistry() *Registry {\n\tmapCodec := &mapCodec{\n\t\tdecodeZerosMap: true,\n\t}\n\n\treg := NewMgoRegistry()\n\treg.RegisterKindDecoder(reflect.Map, mapCodec)\n\treg.RegisterTypeEncoder(tByteSlice, &byteSliceCodec{encodeNilAsEmpty: false})\n\treg.RegisterKindEncoder(reflect.Slice, &sliceCodec{})\n\treg.RegisterKindEncoder(reflect.Map, mapCodec)\n\treturn reg\n}\n\nfunc mgoStringDecodeValue(dc DecodeContext, vr ValueReader, val reflect.Value) error {\n\tif val.Kind() != reflect.String {\n\t\treturn ValueDecoderError{\n\t\t\tName:     \"StringDecodeValue\",\n\t\t\tKinds:    []reflect.Kind{reflect.String},\n\t\t\tReceived: reflect.Zero(val.Type()),\n\t\t}\n\t}\n\n\tif vr.Type() == TypeObjectID {\n\t\toid, err := vr.ReadObjectID()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif dc.objectIDAsHexString {\n\t\t\tval.SetString(oid.Hex())\n\t\t} else {\n\t\t\tval.SetString(string(oid[:]))\n\t\t}\n\t\treturn nil\n\t}\n\treturn (&stringCodec{}).DecodeValue(dc, vr, val)\n}\n\n// setter interface: a value implementing the bson.Setter interface will receive the BSON\n// value via the SetBSON method during unmarshaling, and the object\n// itself will not be changed as usual.\n//\n// If setting the value works, the method should return nil or alternatively\n// ErrMgoSetZero to set the respective field to its zero value (nil for\n// pointer types). If SetBSON returns a non-nil error, the unmarshalling\n// procedure will stop and error out with the provided value.\n//\n// This interface is generally useful in pointer receivers, since the method\n// will want to change the receiver. A type field that implements the Setter\n// interface doesn't have to be a pointer, though.\n//\n// For example:\n//\n//\ttype MyString string\n//\n//\tfunc (s *MyString) SetBSON(raw bson.RawValue) error {\n//\t    return raw.Unmarshal(s)\n//\t}\ntype setter interface {\n\tSetBSON(raw RawValue) error\n}\n\n// getter interface: a value implementing the bson.Getter interface will have its GetBSON\n// method called when the given value has to be marshalled, and the result\n// of this method will be marshaled in place of the actual object.\n//\n// If GetBSON returns return a non-nil error, the marshalling procedure\n// will stop and error out with the provided value.\ntype getter interface {\n\tGetBSON() (any, error)\n}\n\n// setterDecodeValue is the ValueDecoderFunc for Setter types.\nfunc setterDecodeValue(_ DecodeContext, vr ValueReader, val reflect.Value) error {\n\tif !val.IsValid() || (!val.Type().Implements(tSetter) && !reflect.PtrTo(val.Type()).Implements(tSetter)) {\n\t\treturn ValueDecoderError{Name: \"SetterDecodeValue\", Types: []reflect.Type{tSetter}, Received: val}\n\t}\n\n\tif val.Kind() == reflect.Ptr && val.IsNil() {\n\t\tif !val.CanSet() {\n\t\t\treturn ValueDecoderError{Name: \"SetterDecodeValue\", Types: []reflect.Type{tSetter}, Received: val}\n\t\t}\n\t\tval.Set(reflect.New(val.Type().Elem()))\n\t}\n\n\tif !val.Type().Implements(tSetter) {\n\t\tif !val.CanAddr() {\n\t\t\treturn ValueDecoderError{Name: \"ValueUnmarshalerDecodeValue\", Types: []reflect.Type{tSetter}, Received: val}\n\t\t}\n\t\tval = val.Addr() // If the type doesn't implement the interface, a pointer to it must.\n\t}\n\n\tt, src, err := copyValueToBytes(vr)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tm, ok := val.Interface().(setter)\n\tif !ok {\n\t\treturn ValueDecoderError{Name: \"SetterDecodeValue\", Types: []reflect.Type{tSetter}, Received: val}\n\t}\n\tif err := m.SetBSON(RawValue{Type: t, Value: src}); err != nil {\n\t\tif !errors.Is(err, ErrMgoSetZero) {\n\t\t\treturn err\n\t\t}\n\t\tval.Set(reflect.Zero(val.Type()))\n\t}\n\treturn nil\n}\n\n// getterEncodeValue is the ValueEncoderFunc for Getter types.\nfunc getterEncodeValue(ec EncodeContext, vw ValueWriter, val reflect.Value) error {\n\t// Either val or a pointer to val must implement Getter\n\tswitch {\n\tcase !val.IsValid():\n\t\treturn ValueEncoderError{Name: \"GetterEncodeValue\", Types: []reflect.Type{tGetter}, Received: val}\n\tcase val.Type().Implements(tGetter):\n\t\t// If Getter is implemented on a concrete type, make sure that val isn't a nil pointer\n\t\tif isImplementationNil(val, tGetter) {\n\t\t\treturn vw.WriteNull()\n\t\t}\n\tcase reflect.PtrTo(val.Type()).Implements(tGetter) && val.CanAddr():\n\t\tval = val.Addr()\n\tdefault:\n\t\treturn ValueEncoderError{Name: \"GetterEncodeValue\", Types: []reflect.Type{tGetter}, Received: val}\n\t}\n\n\tm, ok := val.Interface().(getter)\n\tif !ok {\n\t\treturn vw.WriteNull()\n\t}\n\tx, err := m.GetBSON()\n\tif err != nil {\n\t\treturn err\n\t}\n\tif x == nil {\n\t\treturn vw.WriteNull()\n\t}\n\tvv := reflect.ValueOf(x)\n\tencoder, err := ec.LookupEncoder(vv.Type())\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn encoder.EncodeValue(ec, vw, vv)\n}\n"
  },
  {
    "path": "bson/mgoregistry_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n//\n// Based on gopkg.in/mgo.v2/bson by Gustavo Niemeyer\n// See THIRD-PARTY-NOTICES for original license terms.\n\npackage bson\n\nimport (\n\t\"bytes\"\n\t\"encoding/binary\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"net/url\"\n\t\"reflect\"\n\t\"strconv\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n)\n\n// Wrap up the document elements contained in data, prepending the int32\n// length of the data, and appending the '\\x00' value closing the document.\nfunc wrapInDoc(data string) string {\n\tresult := make([]byte, len(data)+5)\n\tbinary.LittleEndian.PutUint32(result, uint32(len(result)))\n\tcopy(result[4:], []byte(data))\n\treturn string(result)\n}\n\nfunc makeZeroDoc(value any) (zero any) {\n\tv := reflect.ValueOf(value)\n\tt := v.Type()\n\tswitch t.Kind() {\n\tcase reflect.Map:\n\t\tmv := reflect.MakeMap(t)\n\t\tzero = mv.Interface()\n\tcase reflect.Ptr:\n\t\tpv := reflect.New(v.Type().Elem())\n\t\tzero = pv.Interface()\n\tcase reflect.Slice, reflect.Int, reflect.Int64, reflect.Struct:\n\t\tzero = reflect.New(t).Interface()\n\tdefault:\n\t\tpanic(\"unsupported doc type: \" + t.Name())\n\t}\n\treturn zero\n}\n\nfunc unmarshalWithRegistry(t *testing.T, r *Registry, data []byte, val any) error {\n\tt.Helper()\n\n\tdec := NewDecoder(NewDocumentReader(bytes.NewReader(data)))\n\tdec.SetRegistry(r)\n\treturn dec.Decode(val)\n}\n\nfunc testUnmarshal(t *testing.T, data string, obj any) {\n\tzero := makeZeroDoc(obj)\n\terr := unmarshalWithRegistry(t, NewMgoRegistry(), []byte(data), zero)\n\tassert.Nil(t, err, \"expected nil error, got: %v\", err)\n\tassert.True(t, reflect.DeepEqual(zero, obj), \"expected: %v, got: %v\", obj, zero)\n}\n\ntype testItemType struct {\n\tobj  any\n\tdata string\n}\n\n// --------------------------------------------------------------------------\n// Samples from bsonspec.org:\n\nvar sampleItems = []testItemType{\n\t{\n\t\tM{\"hello\": \"world\"},\n\t\t\"\\x16\\x00\\x00\\x00\\x02hello\\x00\\x06\\x00\\x00\\x00world\\x00\\x00\",\n\t},\n\t{\n\t\tM{\"BSON\": []any{\"awesome\", float64(5.05), 1986}},\n\t\t\"1\\x00\\x00\\x00\\x04BSON\\x00&\\x00\\x00\\x00\\x020\\x00\\x08\\x00\\x00\\x00\" +\n\t\t\t\"awesome\\x00\\x011\\x00333333\\x14@\\x102\\x00\\xc2\\x07\\x00\\x00\\x00\\x00\",\n\t},\n\t{\n\t\tM{\"slice\": []uint8{1, 2}},\n\t\t\"\\x13\\x00\\x00\\x00\\x05slice\\x00\\x02\\x00\\x00\\x00\\x00\\x01\\x02\\x00\",\n\t},\n\t{\n\t\tM{\"slice\": []byte{1, 2}},\n\t\t\"\\x13\\x00\\x00\\x00\\x05slice\\x00\\x02\\x00\\x00\\x00\\x00\\x01\\x02\\x00\",\n\t},\n}\n\nfunc TestMarshalSampleItems(t *testing.T) {\n\tbuf := new(bytes.Buffer)\n\tenc := new(Encoder)\n\tfor i, item := range sampleItems {\n\t\tt.Run(strconv.Itoa(i), func(t *testing.T) {\n\t\t\tbuf.Reset()\n\t\t\tvw := NewDocumentWriter(buf)\n\t\t\tenc.Reset(vw)\n\t\t\tenc.SetRegistry(NewMgoRegistry())\n\t\t\terr := enc.Encode(item.obj)\n\t\t\tassert.Nil(t, err, \"expected nil error, got: %v\", err)\n\t\t\tstr := buf.String()\n\t\t\tassert.Equal(t, str, item.data, \"expected: %v, got: %v\", item.data, str)\n\t\t})\n\t}\n}\n\nfunc TestUnmarshalSampleItems(t *testing.T) {\n\tfor i, item := range sampleItems {\n\t\tt.Run(strconv.Itoa(i), func(t *testing.T) {\n\t\t\tvalue := M{}\n\t\t\terr := unmarshalWithRegistry(t, NewMgoRegistry(), []byte(item.data), &value)\n\t\t\tassert.Nil(t, err, \"expected nil error, got: %v\", err)\n\t\t\tassert.True(t, reflect.DeepEqual(value, item.obj), \"expected: %v, got: %v\", item.obj, value)\n\t\t})\n\t}\n}\n\n// --------------------------------------------------------------------------\n// Every type, ordered by the type flag. These are not wrapped with the\n// length and last \\x00 from the document. wrapInDoc() computes them.\n// Note that all of them should be supported as two-way conversions.\n\nvar allItems = []testItemType{\n\t{\n\t\tM{},\n\t\t\"\",\n\t},\n\t{\n\t\tM{\"_\": float64(5.05)},\n\t\t\"\\x01_\\x00333333\\x14@\",\n\t},\n\t{\n\t\tM{\"_\": \"yo\"},\n\t\t\"\\x02_\\x00\\x03\\x00\\x00\\x00yo\\x00\",\n\t},\n\t{\n\t\tM{\"_\": M{\"a\": true}},\n\t\t\"\\x03_\\x00\\x09\\x00\\x00\\x00\\x08a\\x00\\x01\\x00\",\n\t},\n\t{\n\t\tM{\"_\": []any{true, false}},\n\t\t\"\\x04_\\x00\\r\\x00\\x00\\x00\\x080\\x00\\x01\\x081\\x00\\x00\\x00\",\n\t},\n\t{\n\t\tM{\"_\": []byte(\"yo\")},\n\t\t\"\\x05_\\x00\\x02\\x00\\x00\\x00\\x00yo\",\n\t},\n\t{\n\t\tM{\"_\": Binary{Subtype: 0x80, Data: []byte(\"udef\")}},\n\t\t\"\\x05_\\x00\\x04\\x00\\x00\\x00\\x80udef\",\n\t},\n\t{M{\"_\": Undefined{}}, // Obsolete, but still seen in the wild.\n\t\t\"\\x06_\\x00\"},\n\t{\n\t\tM{\"_\": ObjectID{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B}},\n\t\t\"\\x07_\\x00\\x00\\x01\\x02\\x03\\x04\\x05\\x06\\x07\\x08\\x09\\x0A\\x0B\",\n\t}, // technically this is not the same as the original mgo test\n\t{\n\t\tM{\"_\": DBPointer{DB: \"testnamespace\", Pointer: ObjectID{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B}}},\n\t\t\"\\x0C_\\x00\\x0e\\x00\\x00\\x00testnamespace\\x00\\x00\\x01\\x02\\x03\\x04\\x05\\x06\\x07\\x08\\x09\\x0A\\x0B\",\n\t},\n\t{\n\t\tM{\"_\": false},\n\t\t\"\\x08_\\x00\\x00\",\n\t},\n\t{\n\t\tM{\"_\": true},\n\t\t\"\\x08_\\x00\\x01\",\n\t},\n\t{M{\"_\": time.Unix(0, 258e6).UTC()}, // Note the NS <=> MS conversion.\n\t\t\"\\x09_\\x00\\x02\\x01\\x00\\x00\\x00\\x00\\x00\\x00\"},\n\t{\n\t\tM{\"_\": nil},\n\t\t\"\\x0A_\\x00\",\n\t},\n\t{\n\t\tM{\"_\": Regex{Pattern: \"ab\", Options: \"cd\"}},\n\t\t\"\\x0B_\\x00ab\\x00cd\\x00\",\n\t},\n\t{\n\t\tM{\"_\": JavaScript(\"code\")},\n\t\t\"\\x0D_\\x00\\x05\\x00\\x00\\x00code\\x00\",\n\t},\n\t{\n\t\tM{\"_\": Symbol(\"sym\")},\n\t\t\"\\x0E_\\x00\\x04\\x00\\x00\\x00sym\\x00\",\n\t},\n\t{\n\t\tM{\"_\": CodeWithScope{Code: \"code\", Scope: D{{\"\", nil}}}},\n\t\t\"\\x0F_\\x00\\x14\\x00\\x00\\x00\\x05\\x00\\x00\\x00code\\x00\" +\n\t\t\t\"\\x07\\x00\\x00\\x00\\x0A\\x00\\x00\",\n\t},\n\t{\n\t\tM{\"_\": 258},\n\t\t\"\\x10_\\x00\\x02\\x01\\x00\\x00\",\n\t},\n\t{\n\t\tM{\"_\": Timestamp{0, 258}},\n\t\t\"\\x11_\\x00\\x02\\x01\\x00\\x00\\x00\\x00\\x00\\x00\",\n\t},\n\t{\n\t\tM{\"_\": int64(258)},\n\t\t\"\\x12_\\x00\\x02\\x01\\x00\\x00\\x00\\x00\\x00\\x00\",\n\t},\n\t{\n\t\tM{\"_\": int64(258 << 32)},\n\t\t\"\\x12_\\x00\\x00\\x00\\x00\\x00\\x02\\x01\\x00\\x00\",\n\t},\n\t{\n\t\tM{\"_\": MaxKey{}},\n\t\t\"\\x7F_\\x00\",\n\t},\n\t{\n\t\tM{\"_\": MinKey{}},\n\t\t\"\\xFF_\\x00\",\n\t},\n}\n\nfunc TestMarshalAllItems(t *testing.T) {\n\tbuf := new(bytes.Buffer)\n\tenc := new(Encoder)\n\tfor i, item := range allItems {\n\t\tt.Run(strconv.Itoa(i), func(t *testing.T) {\n\t\t\tbuf.Reset()\n\t\t\tvw := NewDocumentWriter(buf)\n\t\t\tenc.Reset(vw)\n\t\t\tenc.SetRegistry(NewMgoRegistry())\n\t\t\terr := enc.Encode(item.obj)\n\t\t\tassert.Nil(t, err, \"expected nil error, got: %v\", err)\n\t\t\tstr := buf.String()\n\t\t\tassert.Equal(t, str, wrapInDoc(item.data), \"expected: %v, got: %v\", wrapInDoc(item.data), str)\n\t\t})\n\t}\n}\n\nfunc TestUnmarshalAllItems(t *testing.T) {\n\tfor i, item := range allItems {\n\t\tt.Run(strconv.Itoa(i), func(t *testing.T) {\n\t\t\tvalue := M{}\n\t\t\terr := unmarshalWithRegistry(t, NewMgoRegistry(), []byte(wrapInDoc(item.data)), &value)\n\t\t\tassert.Nil(t, err, \"expected nil error, got: %v\", err)\n\t\t\tassert.True(t, reflect.DeepEqual(value, item.obj), \"expected: %v, got: %v\", item.obj, value)\n\t\t})\n\t}\n}\n\nfunc TestUnmarshalRawAllItems(t *testing.T) {\n\tfor i, item := range allItems {\n\t\tif len(item.data) == 0 {\n\t\t\tcontinue\n\t\t}\n\t\tvalue := item.obj.(M)[\"_\"]\n\t\tif value == nil {\n\t\t\tcontinue\n\t\t}\n\t\tt.Run(strconv.Itoa(i), func(t *testing.T) {\n\t\t\tpv := reflect.New(reflect.ValueOf(value).Type())\n\t\t\traw := RawValue{Type: Type(item.data[0]), Value: []byte(item.data[3:])}\n\t\t\terr := raw.UnmarshalWithRegistry(NewMgoRegistry(), pv.Interface())\n\t\t\tassert.Nil(t, err, \"expected nil error, got: %v\", err)\n\t\t\tassert.True(t, reflect.DeepEqual(value, pv.Elem().Interface()), \"expected: %v, got: %v\", value, pv.Elem().Interface())\n\t\t})\n\t}\n}\n\nfunc TestUnmarshalRawIncompatible(t *testing.T) {\n\traw := RawValue{Type: 0x08, Value: []byte{0x01}} // true\n\terr := raw.UnmarshalWithRegistry(NewMgoRegistry(), &struct{}{})\n\tassert.NotNil(t, err, \"expected an error\")\n}\n\nfunc TestUnmarshalZeroesStruct(t *testing.T) {\n\tbuf := new(bytes.Buffer)\n\tvw := NewDocumentWriter(buf)\n\tenc := NewEncoder(vw)\n\tenc.SetRegistry(NewMgoRegistry())\n\terr := enc.Encode(M{\"b\": 2})\n\tassert.Nil(t, err, \"expected nil error, got: %v\", err)\n\ttype T struct{ A, B int }\n\tv := T{A: 1}\n\terr = unmarshalWithRegistry(t, NewMgoRegistry(), buf.Bytes(), &v)\n\tassert.Nil(t, err, \"expected nil error, got: %v\", err)\n\tassert.Equal(t, 0, v.A, \"expected: 0, got: %v\", v.A)\n\tassert.Equal(t, 2, v.B, \"expected: 2, got: %v\", v.B)\n}\n\nfunc TestUnmarshalZeroesMap(t *testing.T) {\n\tbuf := new(bytes.Buffer)\n\tvw := NewDocumentWriter(buf)\n\tenc := NewEncoder(vw)\n\tenc.SetRegistry(NewMgoRegistry())\n\terr := enc.Encode(M{\"b\": 2})\n\tassert.Nil(t, err, \"expected nil error, got: %v\", err)\n\tm := M{\"a\": 1}\n\terr = unmarshalWithRegistry(t, NewMgoRegistry(), buf.Bytes(), &m)\n\tassert.Nil(t, err, \"expected nil error, got: %v\", err)\n\n\twant := M{\"b\": 2}\n\tassert.True(t, reflect.DeepEqual(want, m), \"expected: %v, got: %v\", want, m)\n}\n\nfunc TestUnmarshalNonNilInterface(t *testing.T) {\n\tbuf := new(bytes.Buffer)\n\tvw := NewDocumentWriter(buf)\n\tenc := NewEncoder(vw)\n\tenc.SetRegistry(NewMgoRegistry())\n\terr := enc.Encode(M{\"b\": 2})\n\tassert.Nil(t, err, \"expected nil error, got: %v\", err)\n\tm := M{\"a\": 1}\n\tvar i any = m\n\terr = unmarshalWithRegistry(t, NewMgoRegistry(), buf.Bytes(), &i)\n\tassert.Nil(t, err, \"expected nil error, got: %v\", err)\n\tassert.True(t, reflect.DeepEqual(M{\"b\": 2}, i), \"expected: %v, got: %v\", M{\"b\": 2}, i)\n\tassert.True(t, reflect.DeepEqual(M{\"a\": 1}, m), \"expected: %v, got: %v\", M{\"a\": 1}, m)\n}\n\nfunc TestPtrInline(t *testing.T) {\n\tcases := []struct {\n\t\tIn  any\n\t\tOut M\n\t}{\n\t\t{\n\t\t\tIn:  InlinePtrStruct{A: 1, MStruct: &MStruct{M: 3}},\n\t\t\tOut: M{\"a\": 1, \"m\": 3},\n\t\t},\n\t\t{ // go deeper\n\t\t\tIn:  inlinePtrPtrStruct{B: 10, InlinePtrStruct: &InlinePtrStruct{A: 20, MStruct: &MStruct{M: 30}}},\n\t\t\tOut: M{\"b\": 10, \"a\": 20, \"m\": 30},\n\t\t},\n\t\t{\n\t\t\t// nil embed struct\n\t\t\tIn:  &InlinePtrStruct{A: 3},\n\t\t\tOut: M{\"a\": 3},\n\t\t},\n\t\t{\n\t\t\t// nil embed struct\n\t\t\tIn:  &inlinePtrPtrStruct{B: 5},\n\t\t\tOut: M{\"b\": 5},\n\t\t},\n\t}\n\n\tbuf := new(bytes.Buffer)\n\tenc := new(Encoder)\n\tfor i, cs := range cases {\n\t\tt.Run(strconv.Itoa(i), func(t *testing.T) {\n\t\t\tbuf.Reset()\n\t\t\tvw := NewDocumentWriter(buf)\n\t\t\tenc.Reset(vw)\n\t\t\tenc.SetRegistry(NewMgoRegistry())\n\t\t\terr := enc.Encode(cs.In)\n\t\t\tassert.Nil(t, err, \"expected nil error, got: %v\", err)\n\t\t\tvar dataBSON M\n\t\t\terr = unmarshalWithRegistry(t, NewMgoRegistry(), buf.Bytes(), &dataBSON)\n\t\t\tassert.Nil(t, err, \"expected nil error, got: %v\", err)\n\n\t\t\tassert.True(t, reflect.DeepEqual(cs.Out, dataBSON), \"expected: %v, got: %v\", cs.Out, dataBSON)\n\t\t})\n\t}\n}\n\n// --------------------------------------------------------------------------\n// Some one way marshaling operations which would unmarshal differently.\n\nvar js = JavaScript(\"code\")\n\nvar oneWayMarshalItems = []testItemType{\n\t// These are being passed as pointers, and will unmarshal as values.\n\t{\n\t\tM{\"\": &Binary{Subtype: 0x02, Data: []byte(\"old\")}},\n\t\t\"\\x05\\x00\\x07\\x00\\x00\\x00\\x02\\x03\\x00\\x00\\x00old\",\n\t},\n\t{\n\t\tM{\"\": &Binary{Subtype: 0x80, Data: []byte(\"udef\")}},\n\t\t\"\\x05\\x00\\x04\\x00\\x00\\x00\\x80udef\",\n\t},\n\t{\n\t\tM{\"\": &Regex{Pattern: \"ab\", Options: \"cd\"}},\n\t\t\"\\x0B\\x00ab\\x00cd\\x00\",\n\t},\n\t{\n\t\tM{\"\": &js},\n\t\t\"\\x0D\\x00\\x05\\x00\\x00\\x00code\\x00\",\n\t},\n\t{\n\t\tM{\"\": &CodeWithScope{Code: \"code\", Scope: M{\"\": nil}}},\n\t\t\"\\x0F\\x00\\x14\\x00\\x00\\x00\\x05\\x00\\x00\\x00code\\x00\" +\n\t\t\t\"\\x07\\x00\\x00\\x00\\x0A\\x00\\x00\",\n\t},\n\n\t// There's no float32 type in BSON.  Will encode as a float64.\n\t{\n\t\tM{\"\": float32(5.05)},\n\t\t\"\\x01\\x00\\x00\\x00\\x00@33\\x14@\",\n\t},\n\n\t// The array will be unmarshaled as a slice instead.\n\t{\n\t\tM{\"\": [2]bool{true, false}},\n\t\t\"\\x04\\x00\\r\\x00\\x00\\x00\\x080\\x00\\x01\\x081\\x00\\x00\\x00\",\n\t},\n\n\t// The typed slice will be unmarshaled as []any.\n\t{\n\t\tM{\"\": []bool{true, false}},\n\t\t\"\\x04\\x00\\r\\x00\\x00\\x00\\x080\\x00\\x01\\x081\\x00\\x00\\x00\",\n\t},\n\n\t// Will unmarshal as a []byte.\n\t{\n\t\tM{\"\": Binary{Subtype: 0x00, Data: []byte(\"yo\")}},\n\t\t\"\\x05\\x00\\x02\\x00\\x00\\x00\\x00yo\",\n\t},\n\t{\n\t\tM{\"\": Binary{Subtype: 0x02, Data: []byte(\"old\")}},\n\t\t\"\\x05\\x00\\x07\\x00\\x00\\x00\\x02\\x03\\x00\\x00\\x00old\",\n\t},\n\n\t// No way to preserve the type information here. We might encode as a zero\n\t// value, but this would mean that pointer values in structs wouldn't be\n\t// able to correctly distinguish between unset and set to the zero value.\n\t{\n\t\tM{\"\": (*byte)(nil)},\n\t\t\"\\x0A\\x00\",\n\t},\n\n\t// No int types smaller than int32 in BSON. Could encode this as a char,\n\t// but it would still be ambiguous, take more, and be awkward in Go when\n\t// loaded without typing information.\n\t{\n\t\tM{\"\": byte(8)},\n\t\t\"\\x10\\x00\\x08\\x00\\x00\\x00\",\n\t},\n\n\t// There are no unsigned types in BSON.  Will unmarshal as int32 or int64.\n\t{\n\t\tM{\"\": uint32(258)},\n\t\t\"\\x10\\x00\\x02\\x01\\x00\\x00\",\n\t},\n\t{\n\t\tM{\"\": uint64(258)},\n\t\t\"\\x12\\x00\\x02\\x01\\x00\\x00\\x00\\x00\\x00\\x00\",\n\t},\n\t{\n\t\tM{\"\": uint64(258 << 32)},\n\t\t\"\\x12\\x00\\x00\\x00\\x00\\x00\\x02\\x01\\x00\\x00\",\n\t},\n\n\t// This will unmarshal as int.\n\t{\n\t\tM{\"\": int32(258)},\n\t\t\"\\x10\\x00\\x02\\x01\\x00\\x00\",\n\t},\n\n\t// That's a special case. The unsigned value is too large for an int32,\n\t// so an int64 is used instead.\n\t{\n\t\tM{\"\": uint32(1<<32 - 1)},\n\t\t\"\\x12\\x00\\xFF\\xFF\\xFF\\xFF\\x00\\x00\\x00\\x00\",\n\t},\n\t{\n\t\tM{\"\": uint(1<<32 - 1)},\n\t\t\"\\x12\\x00\\xFF\\xFF\\xFF\\xFF\\x00\\x00\\x00\\x00\",\n\t},\n}\n\nfunc TestOneWayMarshalItems(t *testing.T) {\n\tbuf := new(bytes.Buffer)\n\tenc := new(Encoder)\n\tfor i, item := range oneWayMarshalItems {\n\t\tt.Run(strconv.Itoa(i), func(t *testing.T) {\n\t\t\tbuf.Reset()\n\t\t\tvw := NewDocumentWriter(buf)\n\t\t\tenc.Reset(vw)\n\t\t\tenc.SetRegistry(NewMgoRegistry())\n\t\t\terr := enc.Encode(item.obj)\n\t\t\tassert.Nil(t, err, \"expected nil error, got: %v\", err)\n\n\t\t\tassert.Equal(t, wrapInDoc(item.data), buf.String(), \"expected: %v, got: %v\", Raw(wrapInDoc(item.data)), Raw(buf.Bytes()))\n\t\t})\n\t}\n}\n\n// --------------------------------------------------------------------------\n// Two-way tests for user-defined structures using the samples\n// from bsonspec.org.\n\ntype specSample1 struct {\n\tHello string\n}\n\ntype specSample2 struct {\n\tBSON []any `bson:\"BSON\"`\n}\n\nvar structSampleItems = []testItemType{\n\t{\n\t\t&specSample1{\"world\"},\n\t\t\"\\x16\\x00\\x00\\x00\\x02hello\\x00\\x06\\x00\\x00\\x00world\\x00\\x00\",\n\t},\n\t{\n\t\t&specSample2{[]any{\"awesome\", float64(5.05), 1986}},\n\t\t\"1\\x00\\x00\\x00\\x04BSON\\x00&\\x00\\x00\\x00\\x020\\x00\\x08\\x00\\x00\\x00\" +\n\t\t\t\"awesome\\x00\\x011\\x00333333\\x14@\\x102\\x00\\xc2\\x07\\x00\\x00\\x00\\x00\",\n\t},\n}\n\nfunc TestMarshalStructSampleItems(t *testing.T) {\n\tbuf := new(bytes.Buffer)\n\tenc := new(Encoder)\n\tfor i, item := range structSampleItems {\n\t\tt.Run(strconv.Itoa(i), func(t *testing.T) {\n\t\t\tbuf.Reset()\n\t\t\tvw := NewDocumentWriter(buf)\n\t\t\tenc.Reset(vw)\n\t\t\tenc.SetRegistry(NewMgoRegistry())\n\t\t\terr := enc.Encode(item.obj)\n\t\t\tassert.Nil(t, err, \"expected nil error, got: %v\", err)\n\t\t\tassert.Equal(t, item.data, buf.String(), \"expected: %v, got: %v\", item.data, buf.String())\n\t\t})\n\t}\n}\n\nfunc TestUnmarshalStructSampleItems(t *testing.T) {\n\tfor i, item := range structSampleItems {\n\t\tt.Run(strconv.Itoa(i), func(t *testing.T) {\n\t\t\ttestUnmarshal(t, item.data, item.obj)\n\t\t})\n\t}\n}\n\nfunc Test64bitInt(t *testing.T) {\n\tvar i int64 = (1 << 31)\n\tif int(i) > 0 {\n\t\tbuf := new(bytes.Buffer)\n\t\tvw := NewDocumentWriter(buf)\n\t\tenc := NewEncoder(vw)\n\t\tenc.SetRegistry(NewMgoRegistry())\n\t\terr := enc.Encode(M{\"i\": int(i)})\n\t\tassert.Nil(t, err, \"expected nil error, got: %v\", err)\n\t\twant := wrapInDoc(\"\\x12i\\x00\\x00\\x00\\x00\\x80\\x00\\x00\\x00\\x00\")\n\t\tassert.Equal(t, want, buf.String(), \"expected: %v, got: %v\", want, buf.String())\n\n\t\tvar result struct{ I int }\n\t\terr = unmarshalWithRegistry(t, NewMgoRegistry(), buf.Bytes(), &result)\n\t\tassert.Nil(t, err, \"expected nil error, got: %v\", err)\n\t\tassert.Equal(t, i, int64(result.I), \"expected: %v, got: %v\", i, int64(result.I))\n\t}\n}\n\n// --------------------------------------------------------------------------\n// Generic two-way struct marshaling tests.\n\ntype (\n\tprefixPtr string\n\tprefixVal string\n)\n\nfunc (t *prefixPtr) GetBSON() (any, error) {\n\tif t == nil {\n\t\treturn nil, nil\n\t}\n\treturn \"foo-\" + string(*t), nil\n}\n\nfunc (t *prefixPtr) SetBSON(raw RawValue) error {\n\tvar s string\n\tif raw.Type == 0x0A {\n\t\treturn ErrMgoSetZero\n\t}\n\trval := reflect.ValueOf(&s).Elem()\n\tdecoder, err := NewMgoRegistry().LookupDecoder(rval.Type())\n\tif err != nil {\n\t\treturn err\n\t}\n\tvr := newBufferedValueReader(raw.Type, raw.Value)\n\terr = decoder.DecodeValue(DecodeContext{Registry: NewMgoRegistry()}, vr, rval)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif !strings.HasPrefix(s, \"foo-\") {\n\t\treturn errors.New(\"Prefix not found: \" + s)\n\t}\n\t*t = prefixPtr(s[4:])\n\treturn nil\n}\n\nfunc (t prefixVal) GetBSON() (any, error) {\n\treturn \"foo-\" + string(t), nil\n}\n\nfunc (t *prefixVal) SetBSON(raw RawValue) error {\n\tvar s string\n\tif raw.Type == 0x0A {\n\t\treturn ErrMgoSetZero\n\t}\n\trval := reflect.ValueOf(&s).Elem()\n\tdecoder, err := NewMgoRegistry().LookupDecoder(rval.Type())\n\tif err != nil {\n\t\treturn err\n\t}\n\tvr := newBufferedValueReader(raw.Type, raw.Value)\n\terr = decoder.DecodeValue(DecodeContext{Registry: NewMgoRegistry()}, vr, rval)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif !strings.HasPrefix(s, \"foo-\") {\n\t\treturn errors.New(\"Prefix not found: \" + s)\n\t}\n\t*t = prefixVal(s[4:])\n\treturn nil\n}\n\nvar (\n\tbytevar   = byte(8)\n\tbyteptr   = &bytevar\n\tprefixptr = prefixPtr(\"bar\")\n\tprefixval = prefixVal(\"bar\")\n)\n\nvar structItems = []testItemType{\n\t{\n\t\t&struct{ Ptr *byte }{nil},\n\t\t\"\\x0Aptr\\x00\",\n\t},\n\t{\n\t\t&struct{ Ptr *byte }{&bytevar},\n\t\t\"\\x10ptr\\x00\\x08\\x00\\x00\\x00\",\n\t},\n\t{\n\t\t&struct{ Ptr **byte }{&byteptr},\n\t\t\"\\x10ptr\\x00\\x08\\x00\\x00\\x00\",\n\t},\n\t{\n\t\t&struct{ Byte byte }{8},\n\t\t\"\\x10byte\\x00\\x08\\x00\\x00\\x00\",\n\t},\n\t{\n\t\t&struct{ Byte byte }{0},\n\t\t\"\\x10byte\\x00\\x00\\x00\\x00\\x00\",\n\t},\n\t{\n\t\t&struct {\n\t\t\tV byte `bson:\"Tag\"`\n\t\t}{8},\n\t\t\"\\x10Tag\\x00\\x08\\x00\\x00\\x00\",\n\t},\n\t{\n\t\t&struct {\n\t\t\tV *struct {\n\t\t\t\tByte byte\n\t\t\t}\n\t\t}{&struct{ Byte byte }{8}},\n\t\t\"\\x03v\\x00\" + \"\\x0f\\x00\\x00\\x00\\x10byte\\x00\\b\\x00\\x00\\x00\\x00\",\n\t},\n\t{&struct{ priv byte }{}, \"\"},\n\n\t// The order of the dumped fields should be the same in the struct.\n\t{\n\t\t&struct{ A, C, B, D, F, E *byte }{},\n\t\t\"\\x0Aa\\x00\\x0Ac\\x00\\x0Ab\\x00\\x0Ad\\x00\\x0Af\\x00\\x0Ae\\x00\",\n\t},\n\n\t{\n\t\t&struct{ V RawValue }{RawValue{Type: 0x03, Value: []byte(\"\\x0f\\x00\\x00\\x00\\x10byte\\x00\\b\\x00\\x00\\x00\\x00\")}},\n\t\t\"\\x03v\\x00\" + \"\\x0f\\x00\\x00\\x00\\x10byte\\x00\\b\\x00\\x00\\x00\\x00\",\n\t},\n\t{\n\t\t&struct{ V RawValue }{RawValue{Type: 0x10, Value: []byte(\"\\x00\\x00\\x00\\x00\")}},\n\t\t\"\\x10v\\x00\" + \"\\x00\\x00\\x00\\x00\",\n\t},\n\n\t// Byte arrays.\n\t{\n\t\t&struct{ V [2]byte }{[2]byte{'y', 'o'}},\n\t\t\"\\x05v\\x00\\x02\\x00\\x00\\x00\\x00yo\",\n\t},\n\n\t{\n\t\t&struct{ V prefixPtr }{prefixPtr(\"buzz\")},\n\t\t\"\\x02v\\x00\\x09\\x00\\x00\\x00foo-buzz\\x00\",\n\t},\n\n\t{\n\t\t&struct{ V *prefixPtr }{&prefixptr},\n\t\t\"\\x02v\\x00\\x08\\x00\\x00\\x00foo-bar\\x00\",\n\t},\n\n\t{\n\t\t&struct{ V *prefixPtr }{nil},\n\t\t\"\\x0Av\\x00\",\n\t},\n\n\t{\n\t\t&struct{ V prefixVal }{prefixVal(\"buzz\")},\n\t\t\"\\x02v\\x00\\x09\\x00\\x00\\x00foo-buzz\\x00\",\n\t},\n\n\t{\n\t\t&struct{ V *prefixVal }{&prefixval},\n\t\t\"\\x02v\\x00\\x08\\x00\\x00\\x00foo-bar\\x00\",\n\t},\n\n\t{\n\t\t&struct{ V *prefixVal }{nil},\n\t\t\"\\x0Av\\x00\",\n\t},\n}\n\nfunc TestMarshalStructItems(t *testing.T) {\n\tbuf := new(bytes.Buffer)\n\tenc := new(Encoder)\n\tfor i, item := range structItems {\n\t\tt.Run(strconv.Itoa(i), func(t *testing.T) {\n\t\t\tbuf.Reset()\n\t\t\tvw := NewDocumentWriter(buf)\n\t\t\tenc.Reset(vw)\n\t\t\tenc.SetRegistry(NewMgoRegistry())\n\t\t\terr := enc.Encode(item.obj)\n\t\t\tassert.Nil(t, err, \"expected nil error, got: %v\", err)\n\t\t\tassert.Equal(t, wrapInDoc(item.data), buf.String(), \"expected: %v, got: %v\", wrapInDoc(item.data), buf.String())\n\t\t})\n\t}\n}\n\nfunc TestUnmarshalStructItems(t *testing.T) {\n\tfor i, item := range structItems {\n\t\tt.Run(strconv.Itoa(i), func(t *testing.T) {\n\t\t\ttestUnmarshal(t, wrapInDoc(item.data), item.obj)\n\t\t})\n\t}\n}\n\nfunc TestUnmarshalRawStructItems(t *testing.T) {\n\tfor i, item := range structItems {\n\t\tt.Run(strconv.Itoa(i), func(t *testing.T) {\n\t\t\traw := Raw(wrapInDoc(item.data))\n\t\t\tzero := makeZeroDoc(item.obj)\n\t\t\terr := unmarshalWithRegistry(t, NewMgoRegistry(), raw, zero)\n\t\t\tassert.Nil(t, err, \"expected nil error, got: %v\", err)\n\t\t\tassert.True(t, reflect.DeepEqual(item.obj, zero), \"expected: %v, got: %v\", item.obj, zero)\n\t\t})\n\t}\n}\n\n// func TestUnmarshalRawNil(t *testing.T) {\n// \t// Regression test: shouldn't try to nil out the pointer itself,\n// \t// as it's not settable.\n// \traw := RawValue{Type: 0x0A, Value: []byte{}}\n// \terr := raw.UnmarshalWithRegistry(Registry, &struct{}{})\n// \tassert.Nil(t, err, \"expected nil error, got: %v\", err)\n// }\n\n// --------------------------------------------------------------------------\n// One-way marshaling tests.\n\ntype dOnIface struct {\n\tD any\n}\n\ntype ignoreField struct {\n\tBefore string\n\tIgnore string `bson:\"-\"`\n\tAfter  string\n}\n\nvar marshalItems = []testItemType{\n\t// Ordered document dump.  Will unmarshal as a dictionary by default.\n\t{\n\t\tD{{\"a\", nil}, {\"c\", nil}, {\"b\", nil}, {\"d\", nil}, {\"f\", nil}, {\"e\", true}},\n\t\t\"\\x0Aa\\x00\\x0Ac\\x00\\x0Ab\\x00\\x0Ad\\x00\\x0Af\\x00\\x08e\\x00\\x01\",\n\t},\n\t{\n\t\tMyD{{\"a\", nil}, {\"c\", nil}, {\"b\", nil}, {\"d\", nil}, {\"f\", nil}, {\"e\", true}},\n\t\t\"\\x0Aa\\x00\\x0Ac\\x00\\x0Ab\\x00\\x0Ad\\x00\\x0Af\\x00\\x08e\\x00\\x01\",\n\t},\n\t{\n\t\t&dOnIface{D{{\"a\", nil}, {\"c\", nil}, {\"b\", nil}, {\"d\", true}}},\n\t\t\"\\x03d\\x00\" + wrapInDoc(\"\\x0Aa\\x00\\x0Ac\\x00\\x0Ab\\x00\\x08d\\x00\\x01\"),\n\t},\n\n\t{\n\t\t&ignoreField{\"before\", \"ignore\", \"after\"},\n\t\t\"\\x02before\\x00\\a\\x00\\x00\\x00before\\x00\\x02after\\x00\\x06\\x00\\x00\\x00after\\x00\",\n\t},\n\n\t// Marshalling a Raw document does nothing.\n\t// {RawValue{Type: 0x03, Value: []byte(wrapInDoc(\"anything\"))},\n\t// \t\"anything\"},\n\t// {RawValue{Value: []byte(wrapInDoc(\"anything\"))},\n\t// \t\"anything\"},\n}\n\nfunc TestMarshalOneWayItems(t *testing.T) {\n\tbuf := new(bytes.Buffer)\n\tenc := new(Encoder)\n\tfor i, item := range marshalItems {\n\t\tt.Run(strconv.Itoa(i), func(t *testing.T) {\n\t\t\tbuf.Reset()\n\t\t\tvw := NewDocumentWriter(buf)\n\t\t\tenc.Reset(vw)\n\t\t\tenc.SetRegistry(NewMgoRegistry())\n\t\t\terr := enc.Encode(item.obj)\n\t\t\tassert.Nil(t, err, \"expected nil error, got: %v\", err)\n\t\t\tassert.Equal(t, wrapInDoc(item.data), buf.String(), \"expected: %v, got: %v\", wrapInDoc(item.data), buf.String())\n\t\t})\n\t}\n}\n\n// --------------------------------------------------------------------------\n// One-way unmarshaling tests.\n\ntype intAlias int\n\nvar unmarshalItems = []testItemType{\n\t// Field is private.  Should not attempt to unmarshal it.\n\t{\n\t\t&struct{ priv byte }{},\n\t\t\"\\x10priv\\x00\\x08\\x00\\x00\\x00\",\n\t},\n\n\t// Ignore non-existing field.\n\t{\n\t\t&struct{ Byte byte }{9},\n\t\t\"\\x10boot\\x00\\x08\\x00\\x00\\x00\" + \"\\x10byte\\x00\\x09\\x00\\x00\\x00\",\n\t},\n\n\t// Do not unmarshal on ignored field.\n\t{\n\t\t&ignoreField{\"before\", \"\", \"after\"},\n\t\t\"\\x02before\\x00\\a\\x00\\x00\\x00before\\x00\" +\n\t\t\t\"\\x02-\\x00\\a\\x00\\x00\\x00ignore\\x00\" +\n\t\t\t\"\\x02after\\x00\\x06\\x00\\x00\\x00after\\x00\",\n\t},\n\n\t// Ordered document.\n\t{\n\t\t&struct{ D }{D{{\"a\", nil}, {\"c\", nil}, {\"b\", nil}, {\"d\", true}}},\n\t\t\"\\x03d\\x00\" + wrapInDoc(\"\\x0Aa\\x00\\x0Ac\\x00\\x0Ab\\x00\\x08d\\x00\\x01\"),\n\t},\n\n\t// Decode old binary.\n\t{\n\t\tM{\"_\": []byte(\"old\")},\n\t\t\"\\x05_\\x00\\x07\\x00\\x00\\x00\\x02\\x03\\x00\\x00\\x00old\",\n\t},\n\n\t// Decode old binary without length. According to the spec, this shouldn't happen.\n\t{\n\t\tM{\"_\": []byte(\"old\")},\n\t\t\"\\x05_\\x00\\x03\\x00\\x00\\x00\\x02old\",\n\t},\n\n\t// int key maps\n\t{\n\t\tmap[int]string{10: \"s\"},\n\t\t\"\\x0210\\x00\\x02\\x00\\x00\\x00s\\x00\",\n\t},\n\n\t//// event if type is alias to int\n\t{\n\t\tmap[intAlias]string{10: \"s\"},\n\t\t\"\\x0210\\x00\\x02\\x00\\x00\\x00s\\x00\",\n\t},\n}\n\nfunc TestUnmarshalOneWayItems(t *testing.T) {\n\tfor i, item := range unmarshalItems {\n\t\tt.Run(strconv.Itoa(i), func(t *testing.T) {\n\t\t\ttestUnmarshal(t, wrapInDoc(item.data), item.obj)\n\t\t})\n\t}\n}\n\nfunc TestUnmarshalNilInStruct(t *testing.T) {\n\t// Nil is the default value, so we need to ensure it's indeed being set.\n\tb := byte(1)\n\tv := &struct{ Ptr *byte }{&b}\n\terr := unmarshalWithRegistry(t, NewMgoRegistry(), []byte(wrapInDoc(\"\\x0Aptr\\x00\")), v)\n\tassert.Nil(t, err, \"expected nil error, got: %v\", err)\n\n\twant := &struct{ Ptr *byte }{nil}\n\tassert.Equal(t, *want, *v, \"expected: %v, got: %v\", *want, *v)\n}\n\n// --------------------------------------------------------------------------\n// Marshalling error cases.\n\ntype structWithDupKeys struct {\n\tName  byte\n\tOther byte `bson:\"name\"` // Tag should precede.\n}\n\nvar marshalErrorItems = []testItemType{\n\t{\n\t\tM{\"\": uint64(1 << 63)},\n\t\t\"BSON has no uint64 type, and value is too large to fit correctly in an int64\",\n\t},\n\t{\n\t\tint64(123),\n\t\t\"Can't marshal int64 as a BSON document\",\n\t},\n\t{\n\t\tM{\"\": 1i},\n\t\t\"Can't marshal complex128 in a BSON document\",\n\t},\n\t{\n\t\t&structWithDupKeys{},\n\t\t\"Duplicated key 'name' in struct bson_test.structWithDupKeys\",\n\t},\n\t{\n\t\tRawValue{Type: 0xA, Value: []byte{}},\n\t\t\"Attempted to marshal Raw kind 10 as a document\",\n\t},\n\t{\n\t\tRaw{},\n\t\t\"Attempted to marshal empty Raw document\",\n\t},\n\t{\n\t\tM{\"w\": Raw{}},\n\t\t\"Attempted to marshal empty Raw document\",\n\t},\n\t{\n\t\t&inlineDupName{1, struct{ A, B int }{2, 3}},\n\t\t\"Duplicated key 'a' in struct bson_test.inlineDupName\",\n\t},\n\t{\n\t\t&inlineDupMap{},\n\t\t\"Multiple ,inline maps in struct bson_test.inlineDupMap\",\n\t},\n\t{\n\t\t&inlineBadKeyMap{},\n\t\t\"Option ,inline needs a map with string keys in struct bson_test.inlineBadKeyMap\",\n\t},\n\t{\n\t\t&inlineMap{A: 1, M: map[string]any{\"a\": 1}},\n\t\t`Can't have key \"a\" in inlined map; conflicts with struct field`,\n\t},\n}\n\nfunc TestMarshalErrorItems(t *testing.T) {\n\tbuf := new(bytes.Buffer)\n\tenc := new(Encoder)\n\tfor i, item := range marshalErrorItems {\n\t\tt.Run(strconv.Itoa(i), func(t *testing.T) {\n\t\t\tbuf.Reset()\n\t\t\tvw := NewDocumentWriter(buf)\n\t\t\tenc.Reset(vw)\n\t\t\tenc.SetRegistry(NewMgoRegistry())\n\t\t\terr := enc.Encode(item.obj)\n\n\t\t\tassert.NotNil(t, err, \"expected error\")\n\t\t\tassert.Nil(t, buf.Bytes(), \" expected nil data, got: %v\", buf.Bytes())\n\t\t})\n\t}\n}\n\n// --------------------------------------------------------------------------\n// Unmarshalling error cases.\n\ntype unmarshalErrorType struct {\n\tobj  any\n\tdata string\n}\n\nvar unmarshalErrorItems = []unmarshalErrorType{\n\t// Tag name conflicts with existing parameter.\n\t{\n\t\tobj:  &structWithDupKeys{},\n\t\tdata: \"\\x10name\\x00\\x08\\x00\\x00\\x00\",\n\t},\n\t{\n\t\tobj:  nil,\n\t\tdata: \"\\xEEname\\x00\",\n\t},\n\t{\n\t\tobj:  struct{ Name bool }{},\n\t\tdata: \"\\x10name\\x00\\x08\\x00\\x00\\x00\",\n\t},\n\t{\n\t\tobj:  123,\n\t\tdata: \"\\x10name\\x00\\x08\\x00\\x00\\x00\",\n\t},\n\t{\n\t\tobj:  nil,\n\t\tdata: \"\\x08\\x62\\x00\\x02\",\n\t},\n\t// Non-string and not numeric map key.\n\t{\n\t\tobj:  map[bool]any{true: 1},\n\t\tdata: \"\\x10true\\x00\\x01\\x00\\x00\\x00\",\n\t},\n}\n\nfunc TestUnmarshalErrorItems(t *testing.T) {\n\tfor i, item := range unmarshalErrorItems {\n\t\tt.Run(strconv.Itoa(i), func(t *testing.T) {\n\t\t\tdata := []byte(wrapInDoc(item.data))\n\t\t\tvar value any\n\t\t\tswitch reflect.ValueOf(item.obj).Kind() {\n\t\t\tcase reflect.Map, reflect.Ptr:\n\t\t\t\tvalue = makeZeroDoc(item.obj)\n\t\t\tcase reflect.Invalid:\n\t\t\t\tvalue = M{}\n\t\t\tdefault:\n\t\t\t\tvalue = item.obj\n\t\t\t}\n\t\t\terr := unmarshalWithRegistry(t, NewMgoRegistry(), data, value)\n\t\t\tassert.NotNil(t, err, \"expected error\")\n\t\t})\n\t}\n}\n\ntype unmarshalRawErrorType struct {\n\tobj any\n\traw RawValue\n}\n\nvar unmarshalRawErrorItems = []unmarshalRawErrorType{\n\t// Tag name conflicts with existing parameter.\n\t{\n\t\tobj: &structWithDupKeys{},\n\t\traw: RawValue{Type: 0x03, Value: []byte(\"\\x10byte\\x00\\x08\\x00\\x00\\x00\")},\n\t},\n\t{\n\t\tobj: &struct{}{},\n\t\traw: RawValue{Type: 0xEE, Value: []byte{}},\n\t},\n\t{\n\t\tobj: struct{ Name bool }{},\n\t\traw: RawValue{Type: 0x10, Value: []byte(\"\\x08\\x00\\x00\\x00\")},\n\t},\n\t{\n\t\tobj: 123,\n\t\traw: RawValue{Type: 0x10, Value: []byte(\"\\x08\\x00\\x00\\x00\")},\n\t},\n}\n\nfunc TestUnmarshalRawErrorItems(t *testing.T) {\n\tfor i, item := range unmarshalRawErrorItems {\n\t\tt.Run(strconv.Itoa(i), func(t *testing.T) {\n\t\t\terr := item.raw.UnmarshalWithRegistry(NewMgoRegistry(), item.obj)\n\t\t\tassert.NotNil(t, err, \"expected error\")\n\t\t})\n\t}\n}\n\nvar corruptedData = []string{\n\t\"\\x04\\x00\\x00\\x00\\x00\",         // Document shorter than minimum\n\t\"\\x06\\x00\\x00\\x00\\x00\",         // Not enough data\n\t\"\\x05\\x00\\x00\",                 // Broken length\n\t\"\\x05\\x00\\x00\\x00\\xff\",         // Corrupted termination\n\t\"\\x0A\\x00\\x00\\x00\\x0Aooop\\x00\", // Unfinished C string\n\n\t// Array end past end of string (s[2]=0x07 is correct)\n\twrapInDoc(\"\\x04\\x00\\x09\\x00\\x00\\x00\\x0A\\x00\\x00\"),\n\n\t// Array end within string, but past acceptable.\n\twrapInDoc(\"\\x04\\x00\\x08\\x00\\x00\\x00\\x0A\\x00\\x00\"),\n\n\t// Document end within string, but past acceptable.\n\twrapInDoc(\"\\x03\\x00\\x08\\x00\\x00\\x00\\x0A\\x00\\x00\"),\n\n\t// // String with corrupted end.\n\t// wrapInDoc(\"\\x02\\x00\\x03\\x00\\x00\\x00yo\\xFF\"),\n\n\t// String with negative length (issue #116).\n\t\"\\x0c\\x00\\x00\\x00\\x02x\\x00\\xff\\xff\\xff\\xff\\x00\",\n\n\t// // String with zero length (must include trailing '\\x00')\n\t// \"\\x0c\\x00\\x00\\x00\\x02x\\x00\\x00\\x00\\x00\\x00\\x00\",\n\n\t// Binary with negative length.\n\t\"\\r\\x00\\x00\\x00\\x05x\\x00\\xff\\xff\\xff\\xff\\x00\\x00\",\n}\n\nfunc TestUnmarshalMapDocumentTooShort(t *testing.T) {\n\tfor i, data := range corruptedData {\n\t\tt.Run(strconv.Itoa(i), func(t *testing.T) {\n\t\t\terr := unmarshalWithRegistry(t, NewMgoRegistry(), []byte(data), M{})\n\t\t\tassert.NotNil(t, err, \"expected error, got nil\")\n\n\t\t\terr = unmarshalWithRegistry(t, NewMgoRegistry(), []byte(data), &struct{}{})\n\t\t\tassert.NotNil(t, err, \"expected error, got nil\")\n\t\t})\n\t}\n}\n\n// --------------------------------------------------------------------------\n// Setter test cases.\n\nvar setterResult = map[string]error{}\n\ntype setterType struct {\n\tReceived any\n}\n\nfunc (o *setterType) SetBSON(raw RawValue) error {\n\trval := reflect.ValueOf(o).Elem().Field(0)\n\tdecoder, err := NewMgoRegistry().LookupDecoder(rval.Type())\n\tif err != nil {\n\t\treturn err\n\t}\n\tif raw.Type == 0x00 {\n\t\traw.Type = TypeEmbeddedDocument\n\t}\n\tvr := newBufferedValueReader(raw.Type, raw.Value)\n\terr = decoder.DecodeValue(DecodeContext{Registry: NewMgoRegistry()}, vr, rval)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif s, ok := o.Received.(string); ok {\n\t\tif result, ok := setterResult[s]; ok {\n\t\t\treturn result\n\t\t}\n\t}\n\treturn nil\n}\n\ntype ptrSetterDoc struct {\n\tField *setterType `bson:\"_\"`\n}\n\ntype valSetterDoc struct {\n\tField setterType `bson:\"_\"`\n}\n\nfunc TestUnmarshalAllItemsWithPtrSetter(t *testing.T) {\n\tfor ind, item := range allItems {\n\t\tif ind == 3 {\n\t\t\tcontinue\n\t\t}\n\t\tt.Run(strconv.Itoa(ind), func(t *testing.T) {\n\t\t\tfor i := 0; i != 2; i++ {\n\t\t\t\tvar field *setterType\n\t\t\t\tif i == 0 {\n\t\t\t\t\tobj := &ptrSetterDoc{}\n\t\t\t\t\terr := unmarshalWithRegistry(t, NewMgoRegistry(), []byte(wrapInDoc(item.data)), obj)\n\t\t\t\t\tassert.Nil(t, err, \"expected nil error, got: %v\", err)\n\t\t\t\t\tfield = obj.Field\n\t\t\t\t} else {\n\t\t\t\t\tobj := &valSetterDoc{}\n\t\t\t\t\terr := unmarshalWithRegistry(t, NewMgoRegistry(), []byte(wrapInDoc(item.data)), obj)\n\t\t\t\t\tassert.Nil(t, err, \"expected nil error, got: %v\", err)\n\t\t\t\t\tfield = &obj.Field\n\t\t\t\t}\n\t\t\t\tif item.data == \"\" {\n\t\t\t\t\t// Nothing to unmarshal. Should be untouched.\n\t\t\t\t\tif i == 0 {\n\t\t\t\t\t\tassert.Nil(t, field, \"expected field to be nil, got: %v\", field)\n\t\t\t\t\t} else {\n\t\t\t\t\t\tassert.Nil(t, field.Received, \"expected field.received to be nil, got: %v\", field.Received)\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\texpected := item.obj.(M)[\"_\"]\n\t\t\t\t\tassert.NotNil(t, field, \"Pointer not initialized (%#v)\", expected)\n\n\t\t\t\t\tassert.True(t, reflect.DeepEqual(expected, field.Received), \"expected field.received to be: %v, got: %v\", expected, field.Received)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestUnmarshalWholeDocumentWithSetter(t *testing.T) {\n\tobj := &setterType{}\n\terr := unmarshalWithRegistry(t, NewMgoRegistry(), []byte(sampleItems[0].data), obj)\n\tassert.Nil(t, err, \"expected nil error, got: %v\", err)\n\tassert.True(t, reflect.DeepEqual(M{\"hello\": \"world\"}, obj.Received), \"expected obj.received to be: %v, got: %v\", M{\"hello\": \"world\"}, obj.Received)\n}\n\nfunc TestUnmarshalSetterErrors(t *testing.T) {\n\tboom := errors.New(\"BOOM\")\n\tsetterResult[\"2\"] = boom\n\tdefer delete(setterResult, \"2\")\n\n\tm := map[string]*setterType{}\n\tdata := wrapInDoc(\"\\x02abc\\x00\\x02\\x00\\x00\\x001\\x00\" +\n\t\t\"\\x02def\\x00\\x02\\x00\\x00\\x002\\x00\" +\n\t\t\"\\x02ghi\\x00\\x02\\x00\\x00\\x003\\x00\")\n\terr := unmarshalWithRegistry(t, NewMgoRegistry(), []byte(data), m)\n\tassert.NotNil(t, err, \"expected unmarshal error %v, got nil\", boom)\n\n\t// It's not possible to generate the actual expected error here because it's an *UnmarshalError, which is defined\n\t// in bsoncodec and only contains unexported fields.\n\texpectedErr := errors.New(\"error decoding key def: BOOM\")\n\tassert.Equal(t, expectedErr.Error(), err.Error(), \"expected unmarshal error %v, got %v\", expectedErr, err)\n\n\tassert.NotNil(t, m[\"abc\"], \"expected value not to be nil\")\n\tassert.Nil(t, m[\"def\"], \"expected value to be nil, got: %v\", m[\"def\"])\n\tassert.Nil(t, m[\"ghi\"], \"expected value to be nil, got: %v\", m[\"ghi\"])\n\n\tassert.Equal(t, \"1\", m[\"abc\"].Received, \"expected m[\\\"abc\\\"].Received to be: %v, got: %v\", \"1\", m[\"abc\"].Received)\n}\n\nfunc TestUnmarshalSetterErrSetZero(t *testing.T) {\n\tsetterResult[\"foo\"] = ErrMgoSetZero\n\tdefer delete(setterResult, \"field\")\n\n\tbuf := new(bytes.Buffer)\n\tvw := NewDocumentWriter(buf)\n\tenc := NewEncoder(vw)\n\tenc.SetRegistry(NewMgoRegistry())\n\terr := enc.Encode(M{\"field\": \"foo\"})\n\tassert.Nil(t, err, \"expected nil error, got: %v\", err)\n\n\tm := map[string]*setterType{}\n\terr = unmarshalWithRegistry(t, NewMgoRegistry(), buf.Bytes(), m)\n\tassert.Nil(t, err, \"expected nil error, got: %v\", err)\n\n\tvalue, ok := m[\"field\"]\n\tassert.True(t, reflect.DeepEqual(true, ok), \"expected ok to be: %v, got: %v\", true, ok)\n\tassert.Nil(t, value, \"expected nil value, got: %v\", value)\n}\n\n// --------------------------------------------------------------------------\n// Getter test cases.\n\ntype typeWithGetter struct {\n\tresult any\n\terr    error\n}\n\nfunc (t *typeWithGetter) GetBSON() (any, error) {\n\tif t == nil {\n\t\treturn \"<value is nil>\", nil\n\t}\n\treturn t.result, t.err\n}\n\ntype docWithGetterField struct {\n\tField *typeWithGetter `bson:\"_\"`\n}\n\nfunc TestMarshalAllItemsWithGetter(t *testing.T) {\n\tbuf := new(bytes.Buffer)\n\tenc := new(Encoder)\n\tfor i, item := range allItems {\n\t\tif item.data == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tt.Run(strconv.Itoa(i), func(t *testing.T) {\n\t\t\tbuf.Reset()\n\t\t\tobj := &docWithGetterField{}\n\t\t\tobj.Field = &typeWithGetter{result: item.obj.(M)[\"_\"]}\n\t\t\tvw := NewDocumentWriter(buf)\n\t\t\tenc.Reset(vw)\n\t\t\tenc.SetRegistry(NewMgoRegistry())\n\t\t\terr := enc.Encode(obj)\n\t\t\tassert.Nil(t, err, \"expected nil error, got: %v\", err)\n\t\t\tassert.Equal(t, wrapInDoc(item.data), buf.String(),\n\t\t\t\t\"expected value at %v to be: %v, got: %v\", i, wrapInDoc(item.data), buf.String())\n\t\t})\n\t}\n}\n\nfunc TestMarshalWholeDocumentWithGetter(t *testing.T) {\n\tobj := &typeWithGetter{result: sampleItems[0].obj}\n\tbuf := new(bytes.Buffer)\n\tvw := NewDocumentWriter(buf)\n\tenc := NewEncoder(vw)\n\tenc.SetRegistry(NewMgoRegistry())\n\terr := enc.Encode(obj)\n\tassert.Nil(t, err, \"expected nil error, got: %v\", err)\n\tassert.Equal(t, sampleItems[0].data, buf.String(),\n\t\t\"expected: %v, got: %v\", sampleItems[0].data, buf.String())\n}\n\nfunc TestGetterErrors(t *testing.T) {\n\te := errors.New(\"oops\")\n\n\tobj1 := &docWithGetterField{}\n\tobj1.Field = &typeWithGetter{sampleItems[0].obj, e}\n\tbuf := new(bytes.Buffer)\n\tvw := NewDocumentWriter(buf)\n\tenc := NewEncoder(vw)\n\tenc.SetRegistry(NewMgoRegistry())\n\terr := enc.Encode(obj1)\n\tassert.Equal(t, e, err, \"expected error: %v, got: %v\", e, err)\n\tassert.Nil(t, buf.Bytes(), \"expected nil data, got: %v\", buf.Bytes())\n\n\tobj2 := &typeWithGetter{sampleItems[0].obj, e}\n\tbuf.Reset()\n\tvw = NewDocumentWriter(buf)\n\tenc = NewEncoder(vw)\n\tenc.SetRegistry(NewMgoRegistry())\n\terr = enc.Encode(obj2)\n\tassert.Equal(t, e, err, \"expected error: %v, got: %v\", e, err)\n\tassert.Nil(t, buf.Bytes(), \"expected nil data, got: %v\", buf.Bytes())\n}\n\ntype intGetter int64\n\nfunc (t intGetter) GetBSON() (any, error) {\n\treturn int64(t), nil\n}\n\ntype typeWithIntGetter struct {\n\tV intGetter `bson:\",minsize\"`\n}\n\nfunc TestMarshalShortWithGetter(t *testing.T) {\n\tobj := typeWithIntGetter{42}\n\tbuf := new(bytes.Buffer)\n\tvw := NewDocumentWriter(buf)\n\tenc := NewEncoder(vw)\n\tenc.SetRegistry(NewMgoRegistry())\n\terr := enc.Encode(obj)\n\tassert.Nil(t, err, \"expected nil error, got: %v\", err)\n\tm := M{}\n\terr = unmarshalWithRegistry(t, NewMgoRegistry(), buf.Bytes(), &m)\n\tassert.Nil(t, err, \"expected nil error, got: %v\", err)\n\tassert.Equal(t, 42, m[\"v\"], \"expected m[\\\"v\\\"] to be: %v, got: %v\", 42, m[\"v\"])\n}\n\nfunc TestMarshalWithGetterNil(t *testing.T) {\n\tobj := docWithGetterField{}\n\tbuf := new(bytes.Buffer)\n\tvw := NewDocumentWriter(buf)\n\tenc := NewEncoder(vw)\n\tenc.SetRegistry(NewMgoRegistry())\n\terr := enc.Encode(obj)\n\tassert.Nil(t, err, \"expected nil error, got: %v\", err)\n\tm := M{}\n\terr = unmarshalWithRegistry(t, NewMgoRegistry(), buf.Bytes(), &m)\n\tassert.Nil(t, err, \"expected nil error, got: %v\", err)\n\twant := M{\"_\": \"<value is nil>\"}\n\tassert.Equal(t, want, m, \"expected m[\\\"v\\\"] to be: %v, got: %v\", want, m)\n}\n\n// --------------------------------------------------------------------------\n// Cross-type conversion tests.\n\ntype crossTypeItem struct {\n\tobj1 any\n\tobj2 any\n}\n\ntype condStr struct {\n\tV string `bson:\",omitempty\"`\n}\ntype condStrNS struct {\n\tV string `a:\"A\" bson:\",omitempty\" b:\"B\"`\n}\ntype condBool struct {\n\tV bool `bson:\",omitempty\"`\n}\ntype condInt struct {\n\tV int `bson:\",omitempty\"`\n}\ntype condUInt struct {\n\tV uint `bson:\",omitempty\"`\n}\ntype condFloat struct {\n\tV float64 `bson:\",omitempty\"`\n}\ntype condIface struct {\n\tV any `bson:\",omitempty\"`\n}\ntype condPtr struct {\n\tV *bool `bson:\",omitempty\"`\n}\ntype condSlice struct {\n\tV []string `bson:\",omitempty\"`\n}\ntype condMap struct {\n\tV map[string]int `bson:\",omitempty\"`\n}\ntype namedCondStr struct {\n\tV string `bson:\"myv,omitempty\"`\n}\ntype condTime struct {\n\tV time.Time `bson:\",omitempty\"`\n}\ntype condStruct struct {\n\tV struct{ A []int } `bson:\",omitempty\"`\n}\n\ntype shortInt struct {\n\tV int64 `bson:\",minsize\"`\n}\ntype shortUint struct {\n\tV uint64 `bson:\",minsize\"`\n}\ntype shortIface struct {\n\tV any `bson:\",minsize\"`\n}\ntype shortPtr struct {\n\tV *int64 `bson:\",minsize\"`\n}\ntype shortNonEmptyInt struct {\n\tV int64 `bson:\",minsize,omitempty\"`\n}\n\ntype inlineInt struct {\n\tV struct{ A, B int } `bson:\",inline\"`\n}\ntype inlineDupName struct {\n\tA int\n\tV struct{ A, B int } `bson:\",inline\"`\n}\ntype inlineMap struct {\n\tA int\n\tM map[string]any `bson:\",inline\"`\n}\ntype inlineMapInt struct {\n\tA int\n\tM map[string]int `bson:\",inline\"`\n}\ntype inlineMapMyM struct {\n\tA int\n\tM MyM `bson:\",inline\"`\n}\ntype inlineDupMap struct {\n\tM1 map[string]any `bson:\",inline\"`\n\tM2 map[string]any `bson:\",inline\"`\n}\ntype inlineBadKeyMap struct {\n\tM map[int]int `bson:\",inline\"`\n}\ntype inlineUnexported struct {\n\tM          map[string]any `bson:\",inline\"`\n\tunexported `bson:\",inline\"`\n}\ntype MStruct struct {\n\tM int `bson:\"m,omitempty\"`\n}\ntype InlinePtrStruct struct {\n\tA        int\n\t*MStruct `bson:\",inline\"`\n}\ntype inlinePtrPtrStruct struct {\n\tB                int\n\t*InlinePtrStruct `bson:\",inline\"`\n}\ntype unexported struct {\n\tA int\n}\n\ntype getterSetterD D\n\nfunc (s getterSetterD) GetBSON() (any, error) {\n\tif len(s) == 0 {\n\t\treturn D{}, nil\n\t}\n\treturn D(s[:len(s)-1]), nil\n}\n\nfunc (s *getterSetterD) SetBSON(raw RawValue) error {\n\tvar doc D\n\trval := reflect.ValueOf(&doc).Elem()\n\tdecoder, err := NewMgoRegistry().LookupDecoder(rval.Type())\n\tif err != nil {\n\t\treturn err\n\t}\n\tif raw.Type == 0x00 {\n\t\traw.Type = TypeEmbeddedDocument\n\t}\n\tvr := newBufferedValueReader(raw.Type, raw.Value)\n\terr = decoder.DecodeValue(DecodeContext{Registry: NewMgoRegistry()}, vr, rval)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdoc = append(doc, E{\"suffix\", true})\n\t*s = getterSetterD(doc)\n\treturn err\n}\n\ntype getterSetterInt int\n\nfunc (i getterSetterInt) GetBSON() (any, error) {\n\treturn D{{\"a\", int(i)}}, nil\n}\n\nfunc (i *getterSetterInt) SetBSON(raw RawValue) error {\n\tvar doc struct{ A int }\n\trval := reflect.ValueOf(&doc).Elem()\n\tdecoder, err := NewMgoRegistry().LookupDecoder(rval.Type())\n\tif err != nil {\n\t\treturn err\n\t}\n\tif raw.Type == 0x00 {\n\t\traw.Type = TypeEmbeddedDocument\n\t}\n\tvr := newBufferedValueReader(raw.Type, raw.Value)\n\terr = decoder.DecodeValue(DecodeContext{Registry: NewMgoRegistry()}, vr, rval)\n\tif err != nil {\n\t\treturn err\n\t}\n\t*i = getterSetterInt(doc.A)\n\treturn err\n}\n\ntype ifaceType interface {\n\tHello()\n}\n\ntype ifaceSlice []ifaceType\n\nfunc (s *ifaceSlice) SetBSON(raw RawValue) error {\n\tvar ns []int\n\trval := reflect.ValueOf(&ns).Elem()\n\tdecoder, err := NewMgoRegistry().LookupDecoder(rval.Type())\n\tif err != nil {\n\t\treturn err\n\t}\n\tvr := newBufferedValueReader(raw.Type, raw.Value)\n\terr = decoder.DecodeValue(DecodeContext{Registry: NewMgoRegistry()}, vr, rval)\n\tif err != nil {\n\t\treturn err\n\t}\n\t*s = make(ifaceSlice, ns[0])\n\treturn nil\n}\n\nfunc (s ifaceSlice) GetBSON() (any, error) {\n\treturn []int{len(s)}, nil\n}\n\ntype (\n\tMyString string\n\tMyBytes  []byte\n\tMyBool   bool\n\tMyD      D\n\tMyRawD   Raw\n\tMyM      map[string]any\n)\n\nvar (\n\ttruevar  = true\n\tfalsevar = false\n\n\tint64var = int64(42)\n\tint64ptr = &int64var\n\tintvar   = int(42)\n\n\tgsintvar = getterSetterInt(42)\n)\n\nfunc parseURL(s string) *url.URL {\n\tu, err := url.Parse(s)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treturn u\n}\n\n// That's a pretty fun test.  It will dump the first item, generate a zero\n// value equivalent to the second one, load the dumped data onto it, and then\n// verify that the resulting value is deep-equal to the untouched second value.\n// Then, it will do the same in the *opposite* direction!\nvar twoWayCrossItems = []crossTypeItem{\n\t// int<=>int\n\t{&struct{ I int }{42}, &struct{ I int8 }{42}},\n\t{&struct{ I int }{42}, &struct{ I int32 }{42}},\n\t{&struct{ I int }{42}, &struct{ I int64 }{42}},\n\t{&struct{ I int8 }{42}, &struct{ I int32 }{42}},\n\t{&struct{ I int8 }{42}, &struct{ I int64 }{42}},\n\t{&struct{ I int32 }{42}, &struct{ I int64 }{42}},\n\n\t// uint<=>uint\n\t{&struct{ I uint }{42}, &struct{ I uint8 }{42}},\n\t{&struct{ I uint }{42}, &struct{ I uint32 }{42}},\n\t{&struct{ I uint }{42}, &struct{ I uint64 }{42}},\n\t{&struct{ I uint8 }{42}, &struct{ I uint32 }{42}},\n\t{&struct{ I uint8 }{42}, &struct{ I uint64 }{42}},\n\t{&struct{ I uint32 }{42}, &struct{ I uint64 }{42}},\n\n\t// float32<=>float64\n\t{&struct{ I float32 }{42}, &struct{ I float64 }{42}},\n\n\t// int<=>uint\n\t{&struct{ I uint }{42}, &struct{ I int }{42}},\n\t{&struct{ I uint }{42}, &struct{ I int8 }{42}},\n\t{&struct{ I uint }{42}, &struct{ I int32 }{42}},\n\t{&struct{ I uint }{42}, &struct{ I int64 }{42}},\n\t{&struct{ I uint8 }{42}, &struct{ I int }{42}},\n\t{&struct{ I uint8 }{42}, &struct{ I int8 }{42}},\n\t{&struct{ I uint8 }{42}, &struct{ I int32 }{42}},\n\t{&struct{ I uint8 }{42}, &struct{ I int64 }{42}},\n\t{&struct{ I uint32 }{42}, &struct{ I int }{42}},\n\t{&struct{ I uint32 }{42}, &struct{ I int8 }{42}},\n\t{&struct{ I uint32 }{42}, &struct{ I int32 }{42}},\n\t{&struct{ I uint32 }{42}, &struct{ I int64 }{42}},\n\t{&struct{ I uint64 }{42}, &struct{ I int }{42}},\n\t{&struct{ I uint64 }{42}, &struct{ I int8 }{42}},\n\t{&struct{ I uint64 }{42}, &struct{ I int32 }{42}},\n\t{&struct{ I uint64 }{42}, &struct{ I int64 }{42}},\n\n\t// int <=> float\n\t{&struct{ I int }{42}, &struct{ I float64 }{42}},\n\n\t// int <=> bool\n\t{&struct{ I int }{1}, &struct{ I bool }{true}},\n\t{&struct{ I int }{0}, &struct{ I bool }{false}},\n\n\t// uint <=> float64\n\t{&struct{ I uint }{42}, &struct{ I float64 }{42}},\n\n\t// uint <=> bool\n\t{&struct{ I uint }{1}, &struct{ I bool }{true}},\n\t{&struct{ I uint }{0}, &struct{ I bool }{false}},\n\n\t// float64 <=> bool\n\t{&struct{ I float64 }{1}, &struct{ I bool }{true}},\n\t{&struct{ I float64 }{0}, &struct{ I bool }{false}},\n\n\t// string <=> string and string <=> []byte\n\t{&struct{ S []byte }{[]byte(\"abc\")}, &struct{ S string }{\"abc\"}},\n\t{&struct{ S []byte }{[]byte(\"def\")}, &struct{ S Symbol }{\"def\"}},\n\t{&struct{ S string }{\"ghi\"}, &struct{ S Symbol }{\"ghi\"}},\n\n\t{\n\t\t&struct{ S string }{\"0123456789ab\"},\n\t\t&struct{ S ObjectID }{ObjectID{0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x61, 0x62}},\n\t},\n\n\t// map <=> struct\n\t{\n\t\t&struct {\n\t\t\tA struct {\n\t\t\t\tB, C int\n\t\t\t}\n\t\t}{struct{ B, C int }{1, 2}},\n\t\tmap[string]map[string]int{\"a\": {\"b\": 1, \"c\": 2}},\n\t},\n\n\t{&struct{ A Symbol }{\"abc\"}, map[string]string{\"a\": \"abc\"}},\n\t{&struct{ A Symbol }{\"abc\"}, map[string][]byte{\"a\": []byte(\"abc\")}},\n\t{&struct{ A []byte }{[]byte(\"abc\")}, map[string]string{\"a\": \"abc\"}},\n\t{&struct{ A uint }{42}, map[string]int{\"a\": 42}},\n\t{&struct{ A uint }{42}, map[string]float64{\"a\": 42}},\n\t{&struct{ A uint }{1}, map[string]bool{\"a\": true}},\n\t{&struct{ A int }{42}, map[string]uint{\"a\": 42}},\n\t{&struct{ A int }{42}, map[string]float64{\"a\": 42}},\n\t{&struct{ A int }{1}, map[string]bool{\"a\": true}},\n\t{&struct{ A float64 }{42}, map[string]float32{\"a\": 42}},\n\t{&struct{ A float64 }{42}, map[string]int{\"a\": 42}},\n\t{&struct{ A float64 }{42}, map[string]uint{\"a\": 42}},\n\t{&struct{ A float64 }{1}, map[string]bool{\"a\": true}},\n\t{&struct{ A bool }{true}, map[string]int{\"a\": 1}},\n\t{&struct{ A bool }{true}, map[string]uint{\"a\": 1}},\n\t{&struct{ A bool }{true}, map[string]float64{\"a\": 1}},\n\t{&struct{ A **byte }{&byteptr}, map[string]byte{\"a\": 8}},\n\n\t// url.URL <=> string\n\t{&struct{ URL *url.URL }{parseURL(\"h://e.c/p\")}, map[string]string{\"url\": \"h://e.c/p\"}},\n\t{&struct{ URL url.URL }{*parseURL(\"h://e.c/p\")}, map[string]string{\"url\": \"h://e.c/p\"}},\n\n\t// Slices\n\t{&struct{ S []int }{[]int{1, 2, 3}}, map[string][]int{\"s\": {1, 2, 3}}},\n\t{&struct{ S *[]int }{&[]int{1, 2, 3}}, map[string][]int{\"s\": {1, 2, 3}}},\n\n\t// Conditionals\n\t{&condBool{true}, map[string]bool{\"v\": true}},\n\t{&condBool{}, map[string]bool{}},\n\t{&condInt{1}, map[string]int{\"v\": 1}},\n\t{&condInt{}, map[string]int{}},\n\t{&condUInt{1}, map[string]uint{\"v\": 1}},\n\t{&condUInt{}, map[string]uint{}},\n\t{&condFloat{}, map[string]int{}},\n\t{&condStr{\"yo\"}, map[string]string{\"v\": \"yo\"}},\n\t{&condStr{}, map[string]string{}},\n\t{&condStrNS{\"yo\"}, map[string]string{\"v\": \"yo\"}},\n\t{&condStrNS{}, map[string]string{}},\n\t{&condSlice{[]string{\"yo\"}}, map[string][]string{\"v\": {\"yo\"}}},\n\t{&condSlice{}, map[string][]string{}},\n\t{&condMap{map[string]int{\"k\": 1}}, M{\"v\": M{\"k\": 1}}},\n\t{&condMap{}, map[string][]string{}},\n\t{&condIface{\"yo\"}, map[string]string{\"v\": \"yo\"}},\n\t{&condIface{\"\"}, map[string]string{\"v\": \"\"}},\n\t{&condIface{}, map[string]string{}},\n\t{&condPtr{&truevar}, map[string]bool{\"v\": true}},\n\t{&condPtr{&falsevar}, map[string]bool{\"v\": false}},\n\t{&condPtr{}, map[string]string{}},\n\n\t{&condTime{time.Unix(123456789, 123e6).UTC()}, map[string]time.Time{\"v\": time.Unix(123456789, 123e6).UTC()}},\n\t{&condTime{}, map[string]string{}},\n\n\t{&condStruct{struct{ A []int }{[]int{1}}}, M{\"v\": M{\"a\": []any{1}}}},\n\t{&condStruct{struct{ A []int }{}}, M{}},\n\n\t{&namedCondStr{\"yo\"}, map[string]string{\"myv\": \"yo\"}},\n\t{&namedCondStr{}, map[string]string{}},\n\n\t{&shortInt{1}, map[string]any{\"v\": 1}},\n\t{&shortInt{1 << 30}, map[string]any{\"v\": 1 << 30}},\n\t{&shortInt{1 << 31}, map[string]any{\"v\": int64(1 << 31)}},\n\t{&shortUint{1 << 30}, map[string]any{\"v\": 1 << 30}},\n\t{&shortUint{1 << 31}, map[string]any{\"v\": int64(1 << 31)}},\n\t{&shortIface{int64(1) << 31}, map[string]any{\"v\": int64(1 << 31)}},\n\t{&shortPtr{int64ptr}, map[string]any{\"v\": intvar}},\n\n\t{&shortNonEmptyInt{1}, map[string]any{\"v\": 1}},\n\t{&shortNonEmptyInt{1 << 31}, map[string]any{\"v\": int64(1 << 31)}},\n\t{&shortNonEmptyInt{}, map[string]any{}},\n\n\t{&inlineInt{struct{ A, B int }{1, 2}}, map[string]any{\"a\": 1, \"b\": 2}},\n\t{&inlineMap{A: 1, M: map[string]any{\"b\": 2}}, map[string]any{\"a\": 1, \"b\": 2}},\n\t{&inlineMap{A: 1, M: nil}, map[string]any{\"a\": 1}},\n\t{&inlineMapInt{A: 1, M: map[string]int{\"b\": 2}}, map[string]int{\"a\": 1, \"b\": 2}},\n\t{&inlineMapInt{A: 1, M: nil}, map[string]int{\"a\": 1}},\n\t{&inlineUnexported{M: map[string]any{\"b\": 1}, unexported: unexported{A: 2}}, map[string]any{\"b\": 1, \"a\": 2}},\n\n\t// []byte <=> Binary\n\t{&struct{ B []byte }{[]byte(\"abc\")}, map[string]Binary{\"b\": {Data: []byte(\"abc\")}}},\n\n\t// []byte <=> MyBytes\n\t{&struct{ B MyBytes }{[]byte(\"abc\")}, &map[string]string{\"b\": \"abc\"}},\n\t{&struct{ B MyBytes }{[]byte{}}, &map[string]string{\"b\": \"\"}},\n\t// {&struct{ B MyBytes }{}, &map[string]bool{}},\n\t{&struct{ B []byte }{[]byte(\"abc\")}, &map[string]MyBytes{\"b\": []byte(\"abc\")}},\n\n\t// bool <=> MyBool\n\t{&struct{ B MyBool }{true}, map[string]bool{\"b\": true}},\n\t{&struct{ B MyBool }{}, map[string]bool{\"b\": false}},\n\t// {&struct{ B MyBool }{}, map[string]string{}},\n\t{&struct{ B bool }{}, map[string]MyBool{\"b\": false}},\n\n\t// arrays\n\t{&struct{ V [2]int }{[...]int{1, 2}}, map[string][2]int{\"v\": {1, 2}}},\n\t{&struct{ V [2]byte }{[...]byte{1, 2}}, map[string][2]byte{\"v\": {1, 2}}},\n\n\t// zero time\n\t{&struct{ V time.Time }{}, map[string]any{\"v\": time.Time{}}},\n\n\t// zero time + 1 second + 1 millisecond; overflows int64 as nanoseconds\n\t{\n\t\t&struct{ V time.Time }{time.Unix(-62135596799, 1e6).UTC()},\n\t\tmap[string]any{\"v\": time.Unix(-62135596799, 1e6).UTC()},\n\t},\n\n\t// json.Number <=> int64, float64\n\t{&struct{ N json.Number }{\"5\"}, map[string]any{\"n\": int64(5)}},\n\t{&struct{ N json.Number }{\"5.05\"}, map[string]any{\"n\": 5.05}},\n\t{&struct{ N json.Number }{\"9223372036854776000\"}, map[string]any{\"n\": float64(1 << 63)}},\n\n\t// D <=> non-struct getter/setter\n\t{&D{{\"a\", 1}}, &getterSetterD{{\"a\", 1}, {\"suffix\", true}}},\n\t{&D{{\"a\", 42}}, &gsintvar},\n\n\t// Interface slice setter.\n\t{&struct{ V ifaceSlice }{ifaceSlice{nil, nil, nil}}, M{\"v\": []any{3}}},\n}\n\n// Same thing, but only one way (obj1 => obj2).\nvar oneWayCrossItems = []crossTypeItem{\n\t// Would get decoded into a int32 too in the opposite direction.\n\t{&shortIface{int64(1) << 30}, map[string]any{\"v\": 1 << 30}},\n\n\t// Ensure omitempty on struct with private fields works properly.\n\t{&struct {\n\t\tV struct{ v time.Time } `bson:\",omitempty\"`\n\t}{}, map[string]any{}},\n\n\t{&inlineMapMyM{A: 1, M: MyM{\"b\": MyM{\"c\": 3}}}, map[string]any{\"a\": 1, \"b\": M{\"c\": 3}}},\n\t{map[string]any{\"a\": 1, \"b\": map[string]any{\"c\": 3}}, &inlineMapMyM{A: 1, M: MyM{\"b\": M{\"c\": 3}}}},\n\n\t{&D{{\"a\", D{{\"b\", 1}, {\"c\", 2}}}}, &D{{\"a\", M{\"b\": 1, \"c\": 2}}}},\n\n\t{&D{{\"a\", D{{\"b\", 1}, {\"c\", 2}}}}, &MyD{{\"a\", M{\"b\": 1, \"c\": 2}}}},\n\t{&MyD{{\"a\", MyD{{\"b\", 1}, {\"c\", 2}}}}, &D{{\"a\", M{\"b\": 1, \"c\": 2}}}},\n\n\t{&struct{ V MyD }{MyD{{\"a\", 1}}}, &D{{\"v\", M{\"a\": 1}}}},\n\t{&D{{\"v\", D{{\"a\", 1}}}}, &struct{ V MyD }{MyD{{\"a\", 1}}}},\n\n\t{&M{\"a\": M{\"b\": 1, \"c\": 2}}, MyM{\"a\": M{\"b\": 1, \"c\": 2}}},\n\t{MyM{\"a\": MyM{\"b\": 1, \"c\": 2}}, &M{\"a\": M{\"b\": 1, \"c\": 2}}},\n\n\t{map[string]any{\"a\": map[string]any{\"b\": 1, \"c\": 2}}, &M{\"a\": M{\"b\": 1, \"c\": 2}}},\n\n\t{&M{\"a\": M{\"b\": 1, \"c\": 2}}, map[MyString]any{\"a\": M{\"b\": 1, \"c\": 2}}},\n\t{map[MyString]any{\"a\": map[MyString]any{\"b\": 1, \"c\": 2}}, &M{\"a\": M{\"b\": 1, \"c\": 2}}},\n}\n\nfunc testCrossPair(t *testing.T, dump any, load any) {\n\tzero := makeZeroDoc(load)\n\tbuf := new(bytes.Buffer)\n\tvw := NewDocumentWriter(buf)\n\tenc := NewEncoder(vw)\n\tenc.SetRegistry(NewMgoRegistry())\n\terr := enc.Encode(dump)\n\tassert.Nil(t, err, \"expected nil error, got: %v\", err)\n\terr = unmarshalWithRegistry(t, NewMgoRegistry(), buf.Bytes(), zero)\n\tassert.Nil(t, err, \"expected nil error, got: %v\", err)\n\n\tassert.True(t, reflect.DeepEqual(load, zero), \"expected: %v, got: %v\", load, zero)\n}\n\nfunc TestTwoWayCrossPairs(t *testing.T) {\n\tfor i, item := range twoWayCrossItems {\n\t\tt.Run(strconv.Itoa(i), func(t *testing.T) {\n\t\t\ttestCrossPair(t, item.obj1, item.obj2)\n\t\t\ttestCrossPair(t, item.obj2, item.obj1)\n\t\t})\n\t}\n}\n\nfunc TestOneWayCrossPairs(t *testing.T) {\n\tfor i, item := range oneWayCrossItems {\n\t\tt.Run(strconv.Itoa(i), func(t *testing.T) {\n\t\t\ttestCrossPair(t, item.obj1, item.obj2)\n\t\t})\n\t}\n}\n\n// --------------------------------------------------------------------------\n// ObjectId JSON marshalling.\n\ntype jsonType struct {\n\tID ObjectID\n}\n\nfunc objectIDHex(s string) ObjectID {\n\toid, _ := ObjectIDFromHex(s)\n\treturn oid\n}\n\nvar jsonIDTests = []struct {\n\tvalue     jsonType\n\tjson      string\n\tmarshal   bool\n\tunmarshal bool\n\terror     string\n}{{\n\tvalue:     jsonType{ID: objectIDHex(\"4d88e15b60f486e428412dc9\")},\n\tjson:      `{\"ID\":\"4d88e15b60f486e428412dc9\"}`,\n\tmarshal:   true,\n\tunmarshal: true,\n}, {\n\t// \tvalue:     jsonType{},\n\t// \tjson:      `{\"Id\":\"\"}`,\n\t// \tmarshal:   true,\n\t// \tunmarshal: true,\n\t// }, {\n\t// \tvalue:     jsonType{},\n\t// \tjson:      `{\"Id\":null}`,\n\t// \tmarshal:   false,\n\t// \tunmarshal: true,\n\t// }, {\n\tjson:      `{\"Id\":\"4d88e15b60f486e428412dc9A\"}`,\n\terror:     `invalid ObjectId in JSON: \"4d88e15b60f486e428412dc9A\"`,\n\tmarshal:   false,\n\tunmarshal: true,\n}, {\n\tjson:      `{\"Id\":\"4d88e15b60f486e428412dcZ\"}`,\n\terror:     `invalid ObjectId in JSON: \"4d88e15b60f486e428412dcZ\" .*`,\n\tmarshal:   false,\n\tunmarshal: true,\n}}\n\nfunc TestObjectIdJSONMarshaling(t *testing.T) {\n\tfor i, test := range jsonIDTests {\n\t\ttest := test // Capture range variable.\n\n\t\tt.Run(strconv.Itoa(i), func(t *testing.T) {\n\t\t\tif test.marshal {\n\t\t\t\tdata, err := json.Marshal(&test.value)\n\t\t\t\tif test.error == \"\" {\n\t\t\t\t\tassert.Nil(t, err, \"expected nil error, got: %v\", err)\n\t\t\t\t\tassert.Equal(t, test.json, string(data), \"expected: %v, got: %v\", test.json, string(data))\n\t\t\t\t} else {\n\t\t\t\t\tassert.NotNil(t, err, \"expected a marshal error\")\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif test.unmarshal {\n\t\t\t\tvar value jsonType\n\t\t\t\terr := json.Unmarshal([]byte(test.json), &value)\n\t\t\t\tif test.error == \"\" {\n\t\t\t\t\tassert.Nil(t, err, \"expected nil error, got: %v\", err)\n\t\t\t\t\tassert.True(t, reflect.DeepEqual(test.value, value), \"expected: %v, got: %v\", test.value, value)\n\t\t\t\t} else {\n\t\t\t\t\tassert.NotNil(t, err, \"expected a unmarshal error\")\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestMarshalNotRespectNil(t *testing.T) {\n\ttype T struct {\n\t\tSlice  []int\n\t\tBSlice []byte\n\t\tMap    map[string]any\n\t}\n\n\ttestStruct1 := T{}\n\n\tassert.Nil(t, testStruct1.Slice, \"expected nil slice, got: %v\", testStruct1.Slice)\n\tassert.Nil(t, testStruct1.BSlice, \"expected nil byte slice, got: %v\", testStruct1.BSlice)\n\tassert.Nil(t, testStruct1.Map, \"expected nil map, got: %v\", testStruct1.Map)\n\n\tbuf := new(bytes.Buffer)\n\tvw := NewDocumentWriter(buf)\n\tenc := NewEncoder(vw)\n\tenc.SetRegistry(NewMgoRegistry())\n\terr := enc.Encode(testStruct1)\n\tassert.Nil(t, err, \"expected nil error, got: %v\", err)\n\n\ttestStruct2 := T{}\n\n\t_ = unmarshalWithRegistry(t, NewMgoRegistry(), buf.Bytes(), &testStruct2)\n\n\tassert.NotNil(t, testStruct2.Slice, \"expected non-nil slice\")\n\tassert.NotNil(t, testStruct2.BSlice, \"expected non-nil byte slice\")\n\tassert.NotNil(t, testStruct2.Map, \"expected non-nil map\")\n}\n\nfunc TestMarshalRespectNil(t *testing.T) {\n\ttype T struct {\n\t\tSlice    []int\n\t\tSlicePtr *[]int\n\t\tPtr      *int\n\t\tMap      map[string]any\n\t\tMapPtr   *map[string]any\n\t}\n\n\ttestStruct1 := T{}\n\n\tassert.Nil(t, testStruct1.Slice, \"expected nil slice, got: %v\", testStruct1.Slice)\n\tassert.Nil(t, testStruct1.SlicePtr, \"expected nil slice ptr, got: %v\", testStruct1.SlicePtr)\n\tassert.Nil(t, testStruct1.Map, \"expected nil map, got: %v\", testStruct1.Map)\n\tassert.Nil(t, testStruct1.MapPtr, \"expected nil map ptr, got: %v\", testStruct1.MapPtr)\n\tassert.Nil(t, testStruct1.Ptr, \"expected nil ptr, got: %v\", testStruct1.Ptr)\n\n\tbuf := new(bytes.Buffer)\n\tvw := NewDocumentWriter(buf)\n\tenc := NewEncoder(vw)\n\tenc.SetRegistry(NewMgoRegistry())\n\terr := enc.Encode(testStruct1)\n\tassert.Nil(t, err, \"expected nil error, got: %v\", err)\n\n\ttestStruct2 := T{}\n\n\t_ = unmarshalWithRegistry(t, NewRespectNilValuesMgoRegistry(), buf.Bytes(), &testStruct2)\n\n\tassert.Len(t, testStruct2.Slice, 0, \"expected empty slice, got: %v\", testStruct2.Slice)\n\tassert.Nil(t, testStruct2.SlicePtr, \"expected nil slice ptr, got: %v\", testStruct2.SlicePtr)\n\tassert.Len(t, testStruct2.Map, 0, \"expected empty map, got: %v\", testStruct2.Map)\n\tassert.Nil(t, testStruct2.MapPtr, \"expected nil map ptr, got: %v\", testStruct2.MapPtr)\n\tassert.Nil(t, testStruct2.Ptr, \"expected nil ptr, got: %v\", testStruct2.Ptr)\n\n\ttestStruct1 = T{\n\t\tSlice:    []int{},\n\t\tSlicePtr: &[]int{},\n\t\tMap:      map[string]any{},\n\t\tMapPtr:   &map[string]any{},\n\t}\n\n\tassert.NotNil(t, testStruct1.Slice, \"expected non-nil slice\")\n\tassert.NotNil(t, testStruct1.SlicePtr, \"expected non-nil slice ptr\")\n\tassert.NotNil(t, testStruct1.Map, \"expected non-nil map\")\n\tassert.NotNil(t, testStruct1.MapPtr, \"expected non-nil map ptr\")\n\n\tbuf.Reset()\n\tvw = NewDocumentWriter(buf)\n\tenc = NewEncoder(vw)\n\tenc.SetRegistry(NewMgoRegistry())\n\terr = enc.Encode(testStruct1)\n\tassert.Nil(t, err, \"expected nil error, got: %v\", err)\n\n\ttestStruct2 = T{}\n\n\t_ = unmarshalWithRegistry(t, NewRespectNilValuesMgoRegistry(), buf.Bytes(), &testStruct2)\n\n\tassert.NotNil(t, testStruct2.Slice, \"expected non-nil slice\")\n\tassert.NotNil(t, testStruct2.SlicePtr, \"expected non-nil slice ptr\")\n\tassert.NotNil(t, testStruct2.Map, \"expected non-nil map\")\n\tassert.NotNil(t, testStruct2.MapPtr, \"expected non-nil map ptr\")\n}\n\n// Our mgocompat.Registry tests\ntype Inner struct {\n\tID string\n}\n\ntype InlineLoop struct {\n\tInner `bson:\",inline\"`\n\tValue string\n\tDraft *InlineLoop `bson:\",omitempty\"`\n}\n\nfunc TestInlineWithPointerToSelf(t *testing.T) {\n\tx1 := InlineLoop{\n\t\tInner: Inner{\n\t\t\tID: \"1\",\n\t\t},\n\t\tValue: \"\",\n\t}\n\n\tbuf := new(bytes.Buffer)\n\tvw := NewDocumentWriter(buf)\n\tenc := NewEncoder(vw)\n\tenc.SetRegistry(NewMgoRegistry())\n\terr := enc.Encode(x1)\n\tassert.Nil(t, err, \"expected nil error, got: %v\", err)\n\n\tvar x2 InlineLoop\n\terr = unmarshalWithRegistry(t, NewMgoRegistry(), buf.Bytes(), &x2)\n\tassert.Nil(t, err, \"expected nil error, got: %v\", err)\n\tassert.Equal(t, x1, x2, \"Expected %v, got %v\", x1, x2)\n}\n"
  },
  {
    "path": "bson/mode.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bson\n\nimport (\n\t\"fmt\"\n)\n\ntype mode int\n\nconst (\n\t_ mode = iota\n\tmTopLevel\n\tmDocument\n\tmArray\n\tmValue\n\tmElement\n\tmCodeWithScope\n\tmSpacer\n)\n\nfunc (m mode) String() string {\n\tvar str string\n\n\tswitch m {\n\tcase mTopLevel:\n\t\tstr = \"TopLevel\"\n\tcase mDocument:\n\t\tstr = \"Document\"\n\tcase mArray:\n\t\tstr = \"Array\"\n\tcase mValue:\n\t\tstr = \"Value\"\n\tcase mElement:\n\t\tstr = \"Element\"\n\tcase mCodeWithScope:\n\t\tstr = \"CodeWithScope\"\n\tcase mSpacer:\n\t\tstr = \"CodeWithScopeSpacer\"\n\tdefault:\n\t\tstr = \"Unknown\"\n\t}\n\n\treturn str\n}\n\n// TransitionError is an error returned when an invalid progressing a\n// ValueReader or ValueWriter state machine occurs.\ntype TransitionError struct {\n\tname        string\n\tparent      mode\n\tcurrent     mode\n\tdestination mode\n\tmodes       []mode\n\taction      string\n}\n\nfunc (te TransitionError) Error() string {\n\terrString := fmt.Sprintf(\"%s can only %s\", te.name, te.action)\n\tif te.destination != mode(0) {\n\t\terrString = fmt.Sprintf(\"%s a %s\", errString, te.destination)\n\t}\n\terrString = fmt.Sprintf(\"%s while positioned on a\", errString)\n\tfor ind, m := range te.modes {\n\t\tif ind != 0 && len(te.modes) > 2 {\n\t\t\terrString = fmt.Sprintf(\"%s,\", errString)\n\t\t}\n\t\tif ind == len(te.modes)-1 && len(te.modes) > 1 {\n\t\t\terrString = fmt.Sprintf(\"%s or\", errString)\n\t\t}\n\t\terrString = fmt.Sprintf(\"%s %s\", errString, m)\n\t}\n\terrString = fmt.Sprintf(\"%s but is positioned on a %s\", errString, te.current)\n\tif te.parent != mode(0) {\n\t\terrString = fmt.Sprintf(\"%s with parent %s\", errString, te.parent)\n\t}\n\treturn errString\n}\n"
  },
  {
    "path": "bson/objectid.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n//\n// Based on gopkg.in/mgo.v2/bson by Gustavo Niemeyer\n// See THIRD-PARTY-NOTICES for original license terms.\n\npackage bson\n\nimport (\n\t\"crypto/rand\"\n\t\"encoding\"\n\t\"encoding/binary\"\n\t\"encoding/hex\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"sync/atomic\"\n\t\"time\"\n)\n\n// ErrInvalidHex indicates that a hex string cannot be converted to an ObjectID.\nvar ErrInvalidHex = errors.New(\"the provided hex string is not a valid ObjectID\")\n\n// ObjectID is the BSON ObjectID type.\ntype ObjectID [12]byte\n\n// NilObjectID is the zero value for ObjectID.\nvar NilObjectID ObjectID\n\nvar (\n\tobjectIDCounter = readRandomUint32()\n\tprocessUnique   = processUniqueBytes()\n)\n\nvar (\n\t_ encoding.TextMarshaler   = ObjectID{}\n\t_ encoding.TextUnmarshaler = &ObjectID{}\n)\n\n// NewObjectID generates a new ObjectID.\nfunc NewObjectID() ObjectID {\n\treturn NewObjectIDFromTimestamp(time.Now())\n}\n\n// NewObjectIDFromTimestamp generates a new ObjectID based on the given time.\nfunc NewObjectIDFromTimestamp(timestamp time.Time) ObjectID {\n\tvar b [12]byte\n\n\tbinary.BigEndian.PutUint32(b[0:4], uint32(timestamp.Unix()))\n\tcopy(b[4:9], processUnique[:])\n\tputUint24(b[9:12], atomic.AddUint32(&objectIDCounter, 1))\n\n\treturn b\n}\n\n// Timestamp extracts the time part of the ObjectId.\nfunc (id ObjectID) Timestamp() time.Time {\n\tunixSecs := binary.BigEndian.Uint32(id[0:4])\n\treturn time.Unix(int64(unixSecs), 0).UTC()\n}\n\n// Hex returns the hex encoding of the ObjectID as a string.\nfunc (id ObjectID) Hex() string {\n\tvar buf [24]byte\n\thex.Encode(buf[:], id[:])\n\treturn string(buf[:])\n}\n\nfunc (id ObjectID) String() string {\n\treturn `ObjectID(\"` + id.Hex() + `\")`\n}\n\n// IsZero returns true if id is the empty ObjectID.\nfunc (id ObjectID) IsZero() bool {\n\treturn id == NilObjectID\n}\n\n// ObjectIDFromHex creates a new ObjectID from a hex string. It returns an error if the hex string is not a\n// valid ObjectID.\nfunc ObjectIDFromHex(s string) (ObjectID, error) {\n\tif len(s) != 24 {\n\t\treturn NilObjectID, ErrInvalidHex\n\t}\n\n\tvar oid [12]byte\n\t_, err := hex.Decode(oid[:], []byte(s))\n\tif err != nil {\n\t\treturn NilObjectID, err\n\t}\n\n\treturn oid, nil\n}\n\n// MarshalText returns the ObjectID as UTF-8-encoded text. Implementing this allows us to use ObjectID\n// as a map key when marshalling JSON. See https://pkg.go.dev/encoding#TextMarshaler\nfunc (id ObjectID) MarshalText() ([]byte, error) {\n\tvar buf [24]byte\n\thex.Encode(buf[:], id[:])\n\treturn buf[:], nil\n}\n\n// UnmarshalText populates the byte slice with the ObjectID. If the byte slice\n// is 24 bytes long, it will be populated with the hex representation of the\n// ObjectID. If the byte slice is 12 bytes long, it will be populated with the\n// BSON representation of the ObjectID. This method also accepts empty strings\n// and decodes them as NilObjectID.\n//\n// For any other inputs, an error will be returned.\n//\n// Implementing this allows us to use ObjectID as a map key when unmarshalling\n// JSON. See https://pkg.go.dev/encoding#TextUnmarshaler\nfunc (id *ObjectID) UnmarshalText(b []byte) error {\n\t// NB(charlie): The json package will use UnmarshalText instead of\n\t// UnmarshalJSON if the value is a string.\n\n\t// An empty string is not a valid ObjectID, but we treat it as a\n\t// special value that decodes as NilObjectID.\n\tif len(b) == 0 {\n\t\treturn nil\n\t}\n\toid, err := ObjectIDFromHex(string(b))\n\tif err != nil {\n\t\treturn err\n\t}\n\t*id = oid\n\treturn nil\n}\n\n// MarshalJSON returns the ObjectID as a string\nfunc (id ObjectID) MarshalJSON() ([]byte, error) {\n\tvar buf [26]byte\n\tbuf[0] = '\"'\n\thex.Encode(buf[1:25], id[:])\n\tbuf[25] = '\"'\n\treturn buf[:], nil\n}\n\n// UnmarshalJSON populates the byte slice with the ObjectID. If the byte slice\n// is 24 bytes long, it will be populated with the hex representation of the\n// ObjectID. If the byte slice is 12 bytes long, it will be populated with the\n// BSON representation of the ObjectID. This method also accepts empty strings\n// and decodes them as NilObjectID.\n//\n// As a special case UnmarshalJSON will decode a JSON object with key \"$oid\"\n// that stores a hex encoded ObjectID: {\"$oid\": \"65b3f7edd9bfca00daa6e3b31\"}.\n//\n// For any other inputs, an error will be returned.\nfunc (id *ObjectID) UnmarshalJSON(b []byte) error {\n\t// Ignore \"null\" to keep parity with the standard library. Decoding a JSON\n\t// null into a non-pointer ObjectID field will leave the field unchanged.\n\t// For pointer values, encoding/json will set the pointer to nil and will\n\t// not enter the UnmarshalJSON hook.\n\tif string(b) == \"null\" {\n\t\treturn nil\n\t}\n\n\t// Handle string\n\tif len(b) >= 2 && b[0] == '\"' {\n\t\t// TODO: fails because of error\n\t\treturn id.UnmarshalText(b[1 : len(b)-1])\n\t}\n\tif len(b) == 12 {\n\t\tcopy(id[:], b)\n\t\treturn nil\n\t}\n\tvar v struct {\n\t\tOID *string `json:\"$oid\"`\n\t}\n\tif err := json.Unmarshal(b, &v); err != nil {\n\t\treturn fmt.Errorf(\"failed to parse extended JSON ObjectID: %w\", err)\n\t}\n\tif v.OID == nil {\n\t\treturn errors.New(\"not an extended JSON ObjectID\")\n\t}\n\ti, err := ObjectIDFromHex(*v.OID)\n\tif err != nil {\n\t\treturn err\n\t}\n\t*id = i\n\treturn nil\n}\n\nfunc processUniqueBytes() [5]byte {\n\tvar b [5]byte\n\t_, err := io.ReadFull(rand.Reader, b[:])\n\tif err != nil {\n\t\tpanic(fmt.Errorf(\"cannot initialize objectid package with crypto.rand.Reader: %w\", err))\n\t}\n\n\treturn b\n}\n\nfunc readRandomUint32() uint32 {\n\tvar b [4]byte\n\t_, err := io.ReadFull(rand.Reader, b[:])\n\tif err != nil {\n\t\tpanic(fmt.Errorf(\"cannot initialize objectid package with crypto.rand.Reader: %w\", err))\n\t}\n\n\treturn (uint32(b[0]) << 0) | (uint32(b[1]) << 8) | (uint32(b[2]) << 16) | (uint32(b[3]) << 24)\n}\n\nfunc putUint24(b []byte, v uint32) {\n\tb[0] = byte(v >> 16)\n\tb[1] = byte(v >> 8)\n\tb[2] = byte(v)\n}\n"
  },
  {
    "path": "bson/objectid_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bson\n\nimport (\n\t\"encoding/binary\"\n\t\"encoding/hex\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"testing\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/require\"\n)\n\nfunc TestString(t *testing.T) {\n\tid := NewObjectID()\n\trequire.Contains(t, id.String(), id.Hex())\n}\n\nfunc BenchmarkHex(b *testing.B) {\n\tid := NewObjectID()\n\tfor i := 0; i < b.N; i++ {\n\t\tid.Hex()\n\t}\n}\n\nfunc BenchmarkObjectIDFromHex(b *testing.B) {\n\tid := NewObjectID().Hex()\n\tfor i := 0; i < b.N; i++ {\n\t\t_, _ = ObjectIDFromHex(id)\n\t}\n}\n\nfunc BenchmarkNewObjectIDFromTimestamp(b *testing.B) {\n\tfor i := 0; i < b.N; i++ {\n\t\ttimestamp := time.Now().Add(time.Duration(i) * time.Millisecond)\n\t\t_ = NewObjectIDFromTimestamp(timestamp)\n\t}\n}\n\nfunc BenchmarkObjectIDJSON(b *testing.B) {\n\tb.Run(\"Marshal\", func(b *testing.B) {\n\t\tid := NewObjectID()\n\t\tdata, err := id.MarshalJSON()\n\t\tif err != nil {\n\t\t\tb.Fatal(err)\n\t\t}\n\t\tb.SetBytes(int64(len(data)))\n\t\tb.ReportAllocs()\n\t\tfor i := 0; i < b.N; i++ {\n\t\t\t_, _ = id.MarshalJSON()\n\t\t}\n\t})\n\tb.Run(\"Unmarshal\", func(b *testing.B) {\n\t\tid := NewObjectID()\n\t\tdata, err := id.MarshalJSON()\n\t\tif err != nil {\n\t\t\tb.Fatal(err)\n\t\t}\n\t\tb.SetBytes(int64(len(data)))\n\t\tb.ReportAllocs()\n\t\tfor i := 0; i < b.N; i++ {\n\t\t\tvar v ObjectID\n\t\t\tif err := v.UnmarshalJSON(data); err != nil {\n\t\t\t\tb.Fatal(err)\n\t\t\t}\n\t\t}\n\t})\n\tb.Run(\"UnmarshalOID\", func(b *testing.B) {\n\t\tdata, err := json.Marshal(map[string]string{\n\t\t\t\"$oid\": NewObjectID().Hex(),\n\t\t})\n\t\tif err != nil {\n\t\t\tb.Fatal(err)\n\t\t}\n\t\tb.SetBytes(int64(len(data)))\n\t\tb.ReportAllocs()\n\t\tfor i := 0; i < b.N; i++ {\n\t\t\tvar v ObjectID\n\t\t\tif err := v.UnmarshalJSON(data); err != nil {\n\t\t\t\tb.Fatal(err)\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc TestFromHex_RoundTrip(t *testing.T) {\n\tbefore := NewObjectID()\n\tafter, err := ObjectIDFromHex(before.Hex())\n\trequire.NoError(t, err)\n\n\trequire.Equal(t, before, after)\n}\n\nfunc TestFromHex_InvalidHex(t *testing.T) {\n\t_, err := ObjectIDFromHex(\"this is not a valid hex string!\")\n\trequire.Error(t, err)\n}\n\nfunc TestFromHex_WrongLength(t *testing.T) {\n\t_, err := ObjectIDFromHex(\"deadbeef\")\n\trequire.Equal(t, ErrInvalidHex, err)\n}\n\nfunc TestTimeStamp(t *testing.T) {\n\ttestCases := []struct {\n\t\tHex      string\n\t\tExpected string\n\t}{\n\t\t{\n\t\t\t\"000000001111111111111111\",\n\t\t\t\"1970-01-01 00:00:00 +0000 UTC\",\n\t\t},\n\t\t{\n\t\t\t\"7FFFFFFF1111111111111111\",\n\t\t\t\"2038-01-19 03:14:07 +0000 UTC\",\n\t\t},\n\t\t{\n\t\t\t\"800000001111111111111111\",\n\t\t\t\"2038-01-19 03:14:08 +0000 UTC\",\n\t\t},\n\t\t{\n\t\t\t\"FFFFFFFF1111111111111111\",\n\t\t\t\"2106-02-07 06:28:15 +0000 UTC\",\n\t\t},\n\t}\n\n\tfor _, testcase := range testCases {\n\t\tid, err := ObjectIDFromHex(testcase.Hex)\n\t\trequire.NoError(t, err)\n\t\tsecs := int64(binary.BigEndian.Uint32(id[0:4]))\n\t\ttimestamp := time.Unix(secs, 0).UTC()\n\t\trequire.Equal(t, testcase.Expected, timestamp.String())\n\t}\n}\n\nfunc TestCreateFromTime(t *testing.T) {\n\ttestCases := []struct {\n\t\ttime     string\n\t\tExpected string\n\t}{\n\t\t{\n\t\t\t\"1970-01-01T00:00:00.000Z\",\n\t\t\t\"00000000\",\n\t\t},\n\t\t{\n\t\t\t\"2038-01-19T03:14:07.000Z\",\n\t\t\t\"7fffffff\",\n\t\t},\n\t\t{\n\t\t\t\"2038-01-19T03:14:08.000Z\",\n\t\t\t\"80000000\",\n\t\t},\n\t\t{\n\t\t\t\"2106-02-07T06:28:15.000Z\",\n\t\t\t\"ffffffff\",\n\t\t},\n\t}\n\n\tlayout := \"2006-01-02T15:04:05.000Z\"\n\tfor _, testcase := range testCases {\n\t\ttime, err := time.Parse(layout, testcase.time)\n\t\trequire.NoError(t, err)\n\n\t\tid := NewObjectIDFromTimestamp(time)\n\t\ttimeStr := hex.EncodeToString(id[0:4])\n\n\t\trequire.Equal(t, testcase.Expected, timeStr)\n\t}\n}\n\nfunc TestGenerationTime(t *testing.T) {\n\ttestCases := []struct {\n\t\thex      string\n\t\tExpected string\n\t}{\n\t\t{\n\t\t\t\"000000001111111111111111\",\n\t\t\t\"1970-01-01 00:00:00 +0000 UTC\",\n\t\t},\n\t\t{\n\t\t\t\"7FFFFFFF1111111111111111\",\n\t\t\t\"2038-01-19 03:14:07 +0000 UTC\",\n\t\t},\n\t\t{\n\t\t\t\"800000001111111111111111\",\n\t\t\t\"2038-01-19 03:14:08 +0000 UTC\",\n\t\t},\n\t\t{\n\t\t\t\"FFFFFFFF1111111111111111\",\n\t\t\t\"2106-02-07 06:28:15 +0000 UTC\",\n\t\t},\n\t}\n\n\tfor _, testcase := range testCases {\n\t\tid, err := ObjectIDFromHex(testcase.hex)\n\t\trequire.NoError(t, err)\n\n\t\tgenTime := id.Timestamp()\n\t\trequire.Equal(t, testcase.Expected, genTime.String())\n\t}\n}\n\nfunc TestCounterOverflow(t *testing.T) {\n\tobjectIDCounter = 0xFFFFFFFF\n\tNewObjectID()\n\trequire.Equal(t, uint32(0), objectIDCounter)\n}\n\nfunc TestObjectID_MarshalJSONMap(t *testing.T) {\n\ttype mapOID struct {\n\t\tMap map[ObjectID]string\n\t}\n\n\toid := NewObjectID()\n\texpectedJSON := []byte(fmt.Sprintf(`{\"Map\":{%q:\"foo\"}}`, oid.Hex()))\n\tdata := mapOID{\n\t\tMap: map[ObjectID]string{oid: \"foo\"},\n\t}\n\n\tout, err := json.Marshal(&data)\n\trequire.NoError(t, err)\n\trequire.Equal(t, expectedJSON, out)\n}\n\nfunc TestObjectID_UnmarshalJSONMap(t *testing.T) {\n\ttype mapOID struct {\n\t\tMap map[ObjectID]string\n\t}\n\toid := NewObjectID()\n\tmapOIDJSON := []byte(fmt.Sprintf(`{\"Map\":{%q:\"foo\"}}`, oid.Hex()))\n\texpectedData := mapOID{\n\t\tMap: map[ObjectID]string{oid: \"foo\"},\n\t}\n\n\tdata := mapOID{}\n\terr := json.Unmarshal(mapOIDJSON, &data)\n\trequire.NoError(t, err)\n\trequire.Equal(t, expectedData, data)\n}\n\nfunc TestObjectID_UnmarshalJSON(t *testing.T) {\n\toid := NewObjectID()\n\n\thexJSON := fmt.Sprintf(`{\"foo\": %q}`, oid.Hex())\n\textJSON := fmt.Sprintf(`{\"foo\": {\"$oid\": %q}}`, oid.Hex())\n\temptyStringJSON := `{\"foo\": \"\"}`\n\tnullJSON := `{\"foo\": null}`\n\n\ttestCases := []struct {\n\t\tname       string\n\t\tjsonString string\n\t\texpected   ObjectID\n\t}{\n\t\t{\"hex bytes\", hexJSON, oid},\n\t\t{\"extended JSON\", extJSON, oid},\n\t\t{\"empty string\", emptyStringJSON, NilObjectID},\n\t\t{\"null\", nullJSON, NilObjectID},\n\t}\n\tfor _, tc := range testCases {\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\tvar got map[string]ObjectID\n\t\t\terr := json.Unmarshal([]byte(tc.jsonString), &got)\n\t\t\tassert.Nil(t, err, \"Unmarshal error: %v\", err)\n\n\t\t\tgotOid := got[\"foo\"]\n\t\t\tassert.Equal(t, tc.expected, gotOid, \"expected ObjectID %s, got %s\", tc.expected, gotOid)\n\t\t})\n\t}\n}\n\nfunc TestObjectID_MarshalText(t *testing.T) {\n\toid := ObjectID{0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA, 0xB}\n\tb, err := oid.MarshalText()\n\tassert.Nil(t, err, \"MarshalText error: %v\", err)\n\twant := \"000102030405060708090a0b\"\n\tgot := string(b)\n\tassert.Equal(t, want, got, \"want %v, got %v\", want, got)\n}\n\nfunc TestObjectID_UnmarshalText(t *testing.T) {\n\tvar oid ObjectID\n\terr := oid.UnmarshalText([]byte(\"000102030405060708090a0b\"))\n\tassert.Nil(t, err, \"UnmarshalText error: %v\", err)\n\twant := ObjectID{0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA, 0xB}\n\tassert.Equal(t, want, oid, \"want %v, got %v\", want, oid)\n}\n"
  },
  {
    "path": "bson/pointer_codec.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bson\n\nimport (\n\t\"reflect\"\n)\n\nvar (\n\t_ ValueEncoder = &pointerCodec{}\n\t_ ValueDecoder = &pointerCodec{}\n)\n\n// pointerCodec is the Codec used for pointers.\ntype pointerCodec struct {\n\tecache typeEncoderCache\n\tdcache typeDecoderCache\n}\n\n// EncodeValue handles encoding a pointer by either encoding it to BSON Null if the pointer is nil\n// or looking up an encoder for the type of value the pointer points to.\nfunc (pc *pointerCodec) EncodeValue(ec EncodeContext, vw ValueWriter, val reflect.Value) error {\n\tif val.Kind() != reflect.Ptr {\n\t\tif !val.IsValid() {\n\t\t\treturn vw.WriteNull()\n\t\t}\n\t\treturn ValueEncoderError{Name: \"PointerCodec.EncodeValue\", Kinds: []reflect.Kind{reflect.Ptr}, Received: val}\n\t}\n\n\tif val.IsNil() {\n\t\treturn vw.WriteNull()\n\t}\n\n\ttyp := val.Type()\n\tif v, ok := pc.ecache.Load(typ); ok {\n\t\tif v == nil {\n\t\t\treturn errNoEncoder{Type: typ}\n\t\t}\n\t\treturn v.EncodeValue(ec, vw, val.Elem())\n\t}\n\t// TODO(charlie): handle concurrent requests for the same type\n\tenc, err := ec.LookupEncoder(typ.Elem())\n\tenc = pc.ecache.LoadOrStore(typ, enc)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn enc.EncodeValue(ec, vw, val.Elem())\n}\n\n// DecodeValue handles decoding a pointer by looking up a decoder for the type it points to and\n// using that to decode. If the BSON value is Null, this method will set the pointer to nil.\nfunc (pc *pointerCodec) DecodeValue(dc DecodeContext, vr ValueReader, val reflect.Value) error {\n\tif !val.CanSet() || val.Kind() != reflect.Ptr {\n\t\treturn ValueDecoderError{Name: \"PointerCodec.DecodeValue\", Kinds: []reflect.Kind{reflect.Ptr}, Received: val}\n\t}\n\n\ttyp := val.Type()\n\tif vr.Type() == TypeNull {\n\t\tval.Set(reflect.Zero(typ))\n\t\treturn vr.ReadNull()\n\t}\n\tif vr.Type() == TypeUndefined {\n\t\tval.Set(reflect.Zero(typ))\n\t\treturn vr.ReadUndefined()\n\t}\n\n\tif val.IsNil() {\n\t\tval.Set(reflect.New(typ.Elem()))\n\t}\n\n\tif v, ok := pc.dcache.Load(typ); ok {\n\t\tif v == nil {\n\t\t\treturn errNoDecoder{Type: typ}\n\t\t}\n\t\treturn v.DecodeValue(dc, vr, val.Elem())\n\t}\n\t// TODO(charlie): handle concurrent requests for the same type\n\tdec, err := dc.LookupDecoder(typ.Elem())\n\tdec = pc.dcache.LoadOrStore(typ, dec)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn dec.DecodeValue(dc, vr, val.Elem())\n}\n"
  },
  {
    "path": "bson/primitive.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n//\n// Based on gopkg.in/mgo.v2/bson by Gustavo Niemeyer\n// See THIRD-PARTY-NOTICES for original license terms.\n\npackage bson\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"reflect\"\n\t\"time\"\n)\n\n// Zeroer allows custom struct types to implement a report of zero\n// state. All struct types that don't implement Zeroer or where IsZero\n// returns false are considered to be not zero.\ntype Zeroer interface {\n\tIsZero() bool\n}\n\n// The following primitive types are similar to Go primitives for BSON types that\n// do not have direct Go primitive representations.\n\n// Binary represents a BSON binary value.\ntype Binary struct {\n\tSubtype byte\n\tData    []byte\n}\n\n// Equal compares bp to bp2 and returns true if they are equal.\nfunc (bp Binary) Equal(bp2 Binary) bool {\n\tif bp.Subtype != bp2.Subtype {\n\t\treturn false\n\t}\n\treturn bytes.Equal(bp.Data, bp2.Data)\n}\n\n// IsZero returns if bp is the empty Binary.\nfunc (bp Binary) IsZero() bool {\n\treturn bp.Subtype == 0 && len(bp.Data) == 0\n}\n\n// Undefined represents the BSON undefined value type.\ntype Undefined struct{}\n\n// DateTime represents the BSON datetime value.\ntype DateTime int64\n\nvar (\n\t_ json.Marshaler   = DateTime(0)\n\t_ json.Unmarshaler = (*DateTime)(nil)\n)\n\n// MarshalJSON marshal to time type.\nfunc (d DateTime) MarshalJSON() ([]byte, error) {\n\treturn d.Time().UTC().MarshalJSON()\n}\n\n// UnmarshalJSON creates a bson.DateTime from a JSON string.\nfunc (d *DateTime) UnmarshalJSON(data []byte) error {\n\t// Ignore \"null\" so that we can distinguish between a \"null\" value and\n\t// valid value that is the zero time (as reported by time.Time.IsZero).\n\tif string(data) == \"null\" {\n\t\treturn nil\n\t}\n\tvar t time.Time\n\tif err := t.UnmarshalJSON(data); err != nil {\n\t\treturn err\n\t}\n\t*d = NewDateTimeFromTime(t)\n\treturn nil\n}\n\n// Time returns the date as a time type.\nfunc (d DateTime) Time() time.Time {\n\treturn time.Unix(int64(d)/1000, int64(d)%1000*1000000)\n}\n\n// NewDateTimeFromTime creates a new DateTime from a Time.\nfunc NewDateTimeFromTime(t time.Time) DateTime {\n\treturn DateTime(t.Unix()*1e3 + int64(t.Nanosecond())/1e6)\n}\n\n// Null represents the BSON null value.\ntype Null struct{}\n\n// Regex represents a BSON regex value.\ntype Regex struct {\n\tPattern string\n\tOptions string\n}\n\nfunc (rp Regex) String() string {\n\treturn fmt.Sprintf(`{\"pattern\": \"%s\", \"options\": \"%s\"}`, rp.Pattern, rp.Options)\n}\n\n// Equal compares rp to rp2 and returns true if they are equal.\nfunc (rp Regex) Equal(rp2 Regex) bool {\n\treturn rp.Pattern == rp2.Pattern && rp.Options == rp2.Options\n}\n\n// IsZero returns if rp is the empty Regex.\nfunc (rp Regex) IsZero() bool {\n\treturn rp.Pattern == \"\" && rp.Options == \"\"\n}\n\n// DBPointer represents a BSON dbpointer value.\ntype DBPointer struct {\n\tDB      string\n\tPointer ObjectID\n}\n\nfunc (d DBPointer) String() string {\n\treturn fmt.Sprintf(`{\"db\": \"%s\", \"pointer\": \"%s\"}`, d.DB, d.Pointer)\n}\n\n// Equal compares d to d2 and returns true if they are equal.\nfunc (d DBPointer) Equal(d2 DBPointer) bool {\n\treturn d == d2\n}\n\n// IsZero returns if d is the empty DBPointer.\nfunc (d DBPointer) IsZero() bool {\n\treturn d.DB == \"\" && d.Pointer.IsZero()\n}\n\n// JavaScript represents a BSON JavaScript code value.\ntype JavaScript string\n\n// Symbol represents a BSON symbol value.\ntype Symbol string\n\n// CodeWithScope represents a BSON JavaScript code with scope value.\ntype CodeWithScope struct {\n\tCode  JavaScript\n\tScope any\n}\n\nfunc (cws CodeWithScope) String() string {\n\treturn fmt.Sprintf(`{\"code\": \"%s\", \"scope\": %v}`, cws.Code, cws.Scope)\n}\n\n// Timestamp represents a BSON timestamp value.\ntype Timestamp struct {\n\tT uint32\n\tI uint32\n}\n\n// After reports whether the time instant tp is after tp2.\nfunc (tp Timestamp) After(tp2 Timestamp) bool {\n\treturn tp.T > tp2.T || (tp.T == tp2.T && tp.I > tp2.I)\n}\n\n// Before reports whether the time instant tp is before tp2.\nfunc (tp Timestamp) Before(tp2 Timestamp) bool {\n\treturn tp.T < tp2.T || (tp.T == tp2.T && tp.I < tp2.I)\n}\n\n// Equal compares tp to tp2 and returns true if they are equal.\nfunc (tp Timestamp) Equal(tp2 Timestamp) bool {\n\treturn tp.T == tp2.T && tp.I == tp2.I\n}\n\n// IsZero returns if tp is the zero Timestamp.\nfunc (tp Timestamp) IsZero() bool {\n\treturn tp.T == 0 && tp.I == 0\n}\n\n// Compare compares the time instant tp with tp2. If tp is before tp2, it returns -1; if tp is after\n// tp2, it returns +1; if they're the same, it returns 0.\nfunc (tp Timestamp) Compare(tp2 Timestamp) int {\n\tswitch {\n\tcase tp.Equal(tp2):\n\t\treturn 0\n\tcase tp.Before(tp2):\n\t\treturn -1\n\tdefault:\n\t\treturn +1\n\t}\n}\n\n// MinKey represents the BSON minkey value.\ntype MinKey struct{}\n\n// MaxKey represents the BSON maxkey value.\ntype MaxKey struct{}\n\n// D is an ordered representation of a BSON document. This type should be used when the order of the elements matters,\n// such as MongoDB command documents. If the order of the elements does not matter, an M should be used instead.\n//\n// Example usage:\n//\n//\tbson.D{{\"foo\", \"bar\"}, {\"hello\", \"world\"}, {\"pi\", 3.14159}}\ntype D []E\n\nfunc (d D) String() string {\n\tb, err := MarshalExtJSON(d, true, false)\n\tif err != nil {\n\t\treturn \"\"\n\t}\n\treturn string(b)\n}\n\n// MarshalJSON encodes D into JSON.\nfunc (d D) MarshalJSON() ([]byte, error) {\n\tif d == nil {\n\t\treturn json.Marshal(nil)\n\t}\n\tvar err error\n\tvar buf bytes.Buffer\n\tbuf.Write([]byte(\"{\"))\n\tenc := json.NewEncoder(&buf)\n\tfor i, e := range d {\n\t\terr = enc.Encode(e.Key)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tbuf.Write([]byte(\":\"))\n\t\terr = enc.Encode(e.Value)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif i < len(d)-1 {\n\t\t\tbuf.Write([]byte(\",\"))\n\t\t}\n\t}\n\tbuf.Write([]byte(\"}\"))\n\treturn json.RawMessage(buf.Bytes()).MarshalJSON()\n}\n\n// UnmarshalJSON decodes D from JSON.\nfunc (d *D) UnmarshalJSON(b []byte) error {\n\tdec := json.NewDecoder(bytes.NewReader(b))\n\tt, err := dec.Token()\n\tif err != nil {\n\t\treturn err\n\t}\n\tif t == nil {\n\t\t*d = nil\n\t\treturn nil\n\t}\n\tif v, ok := t.(json.Delim); !ok || v != '{' {\n\t\treturn &json.UnmarshalTypeError{\n\t\t\tValue:  tokenString(t),\n\t\t\tType:   reflect.TypeOf(D(nil)),\n\t\t\tOffset: dec.InputOffset(),\n\t\t}\n\t}\n\t*d, err = jsonDecodeD(dec)\n\treturn err\n}\n\n// E represents a BSON element for a D. It is usually used inside a D.\ntype E struct {\n\tKey   string\n\tValue any\n}\n\n// M is an unordered representation of a BSON document. This type should be used when the order of the elements does not\n// matter. This type is handled as a regular map[string]any when encoding and decoding. Elements will be\n// serialized in an undefined, random order. If the order of the elements matters, a D should be used instead.\n//\n// Example usage:\n//\n//\tbson.M{\"foo\": \"bar\", \"hello\": \"world\", \"pi\": 3.14159}\ntype M map[string]any\n\nfunc (m M) String() string {\n\tb, err := MarshalExtJSON(m, true, false)\n\tif err != nil {\n\t\treturn \"\"\n\t}\n\treturn string(b)\n}\n\n// An A is an ordered representation of a BSON array.\n//\n// Example usage:\n//\n//\tbson.A{\"bar\", \"world\", 3.14159, bson.D{{\"qux\", 12345}}}\ntype A []any\n\nfunc jsonDecodeD(dec *json.Decoder) (D, error) {\n\tres := D{}\n\tfor {\n\t\tvar e E\n\n\t\tt, err := dec.Token()\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tkey, ok := t.(string)\n\t\tif !ok {\n\t\t\tbreak\n\t\t}\n\t\te.Key = key\n\n\t\tt, err = dec.Token()\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tswitch v := t.(type) {\n\t\tcase json.Delim:\n\t\t\tswitch v {\n\t\t\tcase '[':\n\t\t\t\te.Value, err = jsonDecodeSlice(dec)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\tcase '{':\n\t\t\t\te.Value, err = jsonDecodeD(dec)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t}\n\t\tdefault:\n\t\t\te.Value = t\n\t\t}\n\n\t\tres = append(res, e)\n\t}\n\treturn res, nil\n}\n\nfunc jsonDecodeSlice(dec *json.Decoder) ([]any, error) {\n\tvar res []any\n\tdone := false\n\tfor !done {\n\t\tt, err := dec.Token()\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tswitch v := t.(type) {\n\t\tcase json.Delim:\n\t\t\tswitch v {\n\t\t\tcase '[':\n\t\t\t\ta, err := jsonDecodeSlice(dec)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t\tres = append(res, a)\n\t\t\tcase '{':\n\t\t\t\td, err := jsonDecodeD(dec)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t\tres = append(res, d)\n\t\t\tdefault:\n\t\t\t\tdone = true\n\t\t\t}\n\t\tdefault:\n\t\t\tres = append(res, t)\n\t\t}\n\t}\n\treturn res, nil\n}\n\nfunc tokenString(t json.Token) string {\n\tswitch v := t.(type) {\n\tcase json.Delim:\n\t\tswitch v {\n\t\tcase '{':\n\t\t\treturn \"object\"\n\t\tcase '[':\n\t\t\treturn \"array\"\n\t\t}\n\tcase bool:\n\t\treturn \"bool\"\n\tcase float64:\n\t\treturn \"number\"\n\tcase json.Number, string:\n\t\treturn \"string\"\n\t}\n\treturn \"unknown\"\n}\n"
  },
  {
    "path": "bson/primitive_codecs.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bson\n\nimport (\n\t\"fmt\"\n\t\"reflect\"\n)\n\nvar (\n\ttRawValue = reflect.TypeOf(RawValue{})\n\ttRaw      = reflect.TypeOf(Raw(nil))\n)\n\n// registerPrimitiveCodecs will register the encode and decode methods attached to PrimitiveCodecs\n// with the provided RegistryBuilder. if rb is nil, a new empty RegistryBuilder will be created.\nfunc registerPrimitiveCodecs(reg *Registry) {\n\treg.RegisterTypeEncoder(tRawValue, ValueEncoderFunc(rawValueEncodeValue))\n\treg.RegisterTypeEncoder(tRaw, ValueEncoderFunc(rawEncodeValue))\n\treg.RegisterTypeDecoder(tRawValue, ValueDecoderFunc(rawValueDecodeValue))\n\treg.RegisterTypeDecoder(tRaw, ValueDecoderFunc(rawDecodeValue))\n}\n\n// rawValueEncodeValue is the ValueEncoderFunc for RawValue.\n//\n// If the RawValue's Type is \"invalid\" and the RawValue's Value is not empty or\n// nil, then this method will return an error.\nfunc rawValueEncodeValue(_ EncodeContext, vw ValueWriter, val reflect.Value) error {\n\tif !val.IsValid() || val.Type() != tRawValue {\n\t\treturn ValueEncoderError{\n\t\t\tName:     \"RawValueEncodeValue\",\n\t\t\tTypes:    []reflect.Type{tRawValue},\n\t\t\tReceived: val,\n\t\t}\n\t}\n\n\trawvalue := val.Interface().(RawValue)\n\n\tif !rawvalue.Type.IsValid() {\n\t\treturn fmt.Errorf(\"the RawValue Type specifies an invalid BSON type: %#x\", byte(rawvalue.Type))\n\t}\n\n\treturn copyValueFromBytes(vw, rawvalue.Type, rawvalue.Value)\n}\n\n// rawValueDecodeValue is the ValueDecoderFunc for RawValue.\nfunc rawValueDecodeValue(_ DecodeContext, vr ValueReader, val reflect.Value) error {\n\tif !val.CanSet() || val.Type() != tRawValue {\n\t\treturn ValueDecoderError{Name: \"RawValueDecodeValue\", Types: []reflect.Type{tRawValue}, Received: val}\n\t}\n\n\tt, value, err := copyValueToBytes(vr)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tval.Set(reflect.ValueOf(RawValue{Type: t, Value: value}))\n\treturn nil\n}\n\n// rawEncodeValue is the ValueEncoderFunc for Reader.\nfunc rawEncodeValue(_ EncodeContext, vw ValueWriter, val reflect.Value) error {\n\tif !val.IsValid() || val.Type() != tRaw {\n\t\treturn ValueEncoderError{Name: \"RawEncodeValue\", Types: []reflect.Type{tRaw}, Received: val}\n\t}\n\n\trdr := val.Interface().(Raw)\n\n\treturn copyDocumentFromBytes(vw, rdr)\n}\n\n// rawDecodeValue is the ValueDecoderFunc for Reader.\nfunc rawDecodeValue(_ DecodeContext, vr ValueReader, val reflect.Value) error {\n\tif !val.CanSet() || val.Type() != tRaw {\n\t\treturn ValueDecoderError{Name: \"RawDecodeValue\", Types: []reflect.Type{tRaw}, Received: val}\n\t}\n\tvrType := vr.Type()\n\tisDocument := vrType == Type(0) || vrType == TypeEmbeddedDocument || vrType == TypeArray\n\tif !isDocument {\n\t\treturn fmt.Errorf(\"cannot decode %v into a %s\", vrType, val.Type())\n\t}\n\n\tif val.IsNil() {\n\t\tval.Set(reflect.MakeSlice(val.Type(), 0, 0))\n\t}\n\n\tval.SetLen(0)\n\n\trdr, err := appendDocumentBytes(val.Interface().(Raw), vr)\n\tval.Set(reflect.ValueOf(rdr))\n\treturn err\n}\n"
  },
  {
    "path": "bson/primitive_codecs_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bson\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"net/url\"\n\t\"reflect\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/google/go-cmp/cmp\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n)\n\nfunc bytesFromDoc(doc any) []byte {\n\tb, err := Marshal(doc)\n\tif err != nil {\n\t\tpanic(fmt.Errorf(\"Couldn't marshal BSON document: %w\", err))\n\t}\n\treturn b\n}\n\nfunc TestPrimitiveValueEncoders(t *testing.T) {\n\tt.Parallel()\n\n\twrong := func(string, string) string { return \"wrong\" }\n\n\ttype subtest struct {\n\t\tname   string\n\t\tval    any\n\t\tectx   *EncodeContext\n\t\tllvrw  *valueReaderWriter\n\t\tinvoke invoked\n\t\terr    error\n\t}\n\n\ttestCases := []struct {\n\t\tname     string\n\t\tve       ValueEncoder\n\t\tsubtests []subtest\n\t}{\n\t\t{\n\t\t\t\"RawValueEncodeValue\",\n\t\t\tValueEncoderFunc(rawValueEncodeValue),\n\t\t\t[]subtest{\n\t\t\t\t{\n\t\t\t\t\t\"wrong type\",\n\t\t\t\t\twrong,\n\t\t\t\t\tnil,\n\t\t\t\t\tnil,\n\t\t\t\t\tnothing,\n\t\t\t\t\tValueEncoderError{\n\t\t\t\t\t\tName:     \"RawValueEncodeValue\",\n\t\t\t\t\t\tTypes:    []reflect.Type{tRawValue},\n\t\t\t\t\t\tReceived: reflect.ValueOf(wrong),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"RawValue/success\",\n\t\t\t\t\tRawValue{Type: TypeDouble, Value: bsoncore.AppendDouble(nil, 3.14159)},\n\t\t\t\t\tnil,\n\t\t\t\t\tnil,\n\t\t\t\t\twriteDouble,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"RawValue Type is zero with non-zero value\",\n\t\t\t\t\tRawValue{\n\t\t\t\t\t\tType:  0x00,\n\t\t\t\t\t\tValue: bsoncore.AppendDouble(nil, 3.14159),\n\t\t\t\t\t},\n\t\t\t\t\tnil,\n\t\t\t\t\tnil,\n\t\t\t\t\tnothing,\n\t\t\t\t\tfmt.Errorf(\"the RawValue Type specifies an invalid BSON type: 0x0\"),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"RawValue Type is invalid\",\n\t\t\t\t\tRawValue{\n\t\t\t\t\t\tType:  0x8F,\n\t\t\t\t\t\tValue: bsoncore.AppendDouble(nil, 3.14159),\n\t\t\t\t\t},\n\t\t\t\t\tnil,\n\t\t\t\t\tnil,\n\t\t\t\t\tnothing,\n\t\t\t\t\tfmt.Errorf(\"the RawValue Type specifies an invalid BSON type: 0x8f\"),\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"RawEncodeValue\",\n\t\t\tValueEncoderFunc(rawEncodeValue),\n\t\t\t[]subtest{\n\t\t\t\t{\n\t\t\t\t\t\"wrong type\",\n\t\t\t\t\twrong,\n\t\t\t\t\tnil,\n\t\t\t\t\tnil,\n\t\t\t\t\tnothing,\n\t\t\t\t\tValueEncoderError{Name: \"RawEncodeValue\", Types: []reflect.Type{tRaw}, Received: reflect.ValueOf(wrong)},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"WriteDocument Error\",\n\t\t\t\t\tRaw{},\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{Err: errors.New(\"wd error\"), ErrAfter: writeDocument},\n\t\t\t\t\twriteDocument,\n\t\t\t\t\terrors.New(\"wd error\"),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"Raw.Elements Error\",\n\t\t\t\t\tRaw{0xFF, 0x00, 0x00, 0x00, 0x00},\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{},\n\t\t\t\t\twriteDocument,\n\t\t\t\t\terrors.New(\"length read exceeds number of bytes available. length=5 bytes=255\"),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"WriteDocumentElement Error\",\n\t\t\t\t\tRaw(bytesFromDoc(D{{\"foo\", nil}})),\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{Err: errors.New(\"wde error\"), ErrAfter: writeDocumentElement},\n\t\t\t\t\twriteDocumentElement,\n\t\t\t\t\terrors.New(\"wde error\"),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"encodeValue error\",\n\t\t\t\t\tRaw(bytesFromDoc(D{{\"foo\", nil}})),\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{Err: errors.New(\"ev error\"), ErrAfter: writeNull},\n\t\t\t\t\twriteNull,\n\t\t\t\t\terrors.New(\"ev error\"),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"iterator error\",\n\t\t\t\t\tRaw{0x0C, 0x00, 0x00, 0x00, 0x01, 'f', 'o', 'o', 0x00, 0x01, 0x02, 0x03},\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{},\n\t\t\t\t\twriteDocumentElement,\n\t\t\t\t\terrors.New(\"not enough bytes available to read type. bytes=3 type=double\"),\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n\n\tfor _, tc := range testCases {\n\t\ttc := tc // Capture the range variable\n\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tfor _, subtest := range tc.subtests {\n\t\t\t\tsubtest := subtest // Capture the range variable\n\n\t\t\t\tt.Run(subtest.name, func(t *testing.T) {\n\t\t\t\t\tt.Parallel()\n\n\t\t\t\t\tvar ec EncodeContext\n\t\t\t\t\tif subtest.ectx != nil {\n\t\t\t\t\t\tec = *subtest.ectx\n\t\t\t\t\t}\n\t\t\t\t\tllvrw := new(valueReaderWriter)\n\t\t\t\t\tif subtest.llvrw != nil {\n\t\t\t\t\t\tllvrw = subtest.llvrw\n\t\t\t\t\t}\n\t\t\t\t\tllvrw.T = t\n\t\t\t\t\terr := tc.ve.EncodeValue(ec, llvrw, reflect.ValueOf(subtest.val))\n\t\t\t\t\tif !assert.CompareErrors(err, subtest.err) {\n\t\t\t\t\t\tt.Errorf(\"Errors do not match. got %v; want %v\", err, subtest.err)\n\t\t\t\t\t}\n\t\t\t\t\tinvoked := llvrw.invoked\n\t\t\t\t\tif !cmp.Equal(invoked, subtest.invoke) {\n\t\t\t\t\t\tt.Errorf(\"Incorrect method invoked. got %v; want %v\", invoked, subtest.invoke)\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\t}\n\n\tt.Run(\"success path\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\toid := NewObjectID()\n\t\toids := []ObjectID{NewObjectID(), NewObjectID(), NewObjectID()}\n\t\tstr := new(string)\n\t\t*str = \"bar\"\n\t\tnow := time.Now().Truncate(time.Millisecond)\n\t\tmurl, err := url.Parse(\"https://mongodb.com/random-url?hello=world\")\n\t\tif err != nil {\n\t\t\tt.Errorf(\"Error parsing URL: %v\", err)\n\t\t\tt.FailNow()\n\t\t}\n\t\tdecimal128, err := ParseDecimal128(\"1.5e10\")\n\t\tif err != nil {\n\t\t\tt.Errorf(\"Error parsing decimal128: %v\", err)\n\t\t\tt.FailNow()\n\t\t}\n\n\t\ttestCases := []struct {\n\t\t\tname  string\n\t\t\tvalue any\n\t\t\tb     []byte\n\t\t\terr   error\n\t\t}{\n\t\t\t{\n\t\t\t\t\"D to JavaScript\",\n\t\t\t\tD{{\"a\", JavaScript(`function() { var hello = \"world\"; }`)}},\n\t\t\t\tdocToBytes(D{{\"a\", JavaScript(`function() { var hello = \"world\"; }`)}}),\n\t\t\t\tnil,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"D to Symbol\",\n\t\t\t\tD{{\"a\", Symbol(\"foobarbaz\")}},\n\t\t\t\tdocToBytes(D{{\"a\", Symbol(\"foobarbaz\")}}),\n\t\t\t\tnil,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"omitempty map\",\n\t\t\t\tstruct {\n\t\t\t\t\tT map[string]string `bson:\",omitempty\"`\n\t\t\t\t}{\n\t\t\t\t\tT: map[string]string{},\n\t\t\t\t},\n\t\t\t\tdocToBytes(D{}),\n\t\t\t\tnil,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"omitempty slice\",\n\t\t\t\tstruct {\n\t\t\t\t\tT []struct{} `bson:\",omitempty\"`\n\t\t\t\t}{\n\t\t\t\t\tT: []struct{}{},\n\t\t\t\t},\n\t\t\t\tdocToBytes(D{}),\n\t\t\t\tnil,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"omitempty string\",\n\t\t\t\tstruct {\n\t\t\t\t\tT string `bson:\",omitempty\"`\n\t\t\t\t}{\n\t\t\t\t\tT: \"\",\n\t\t\t\t},\n\t\t\t\tdocToBytes(D{}),\n\t\t\t\tnil,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"struct{}\",\n\t\t\t\tstruct {\n\t\t\t\t\tA bool\n\t\t\t\t\tB int32\n\t\t\t\t\tC int64\n\t\t\t\t\tD uint16\n\t\t\t\t\tE uint64\n\t\t\t\t\tF float64\n\t\t\t\t\tG string\n\t\t\t\t\tH map[string]string\n\t\t\t\t\tI []byte\n\t\t\t\t\tK [2]string\n\t\t\t\t\tL struct {\n\t\t\t\t\t\tM string\n\t\t\t\t\t}\n\t\t\t\t\tP  Raw\n\t\t\t\t\tQ  ObjectID\n\t\t\t\t\tT  []struct{}\n\t\t\t\t\tY  json.Number\n\t\t\t\t\tZ  time.Time\n\t\t\t\t\tAA json.Number\n\t\t\t\t\tAB *url.URL\n\t\t\t\t\tAC Decimal128\n\t\t\t\t\tAD *time.Time\n\t\t\t\t\tAE testValueMarshaler\n\t\t\t\t\tAF RawValue\n\t\t\t\t\tAG *RawValue\n\t\t\t\t\tAH D\n\t\t\t\t\tAI *D\n\t\t\t\t\tAJ *D\n\t\t\t\t}{\n\t\t\t\t\tA: true,\n\t\t\t\t\tB: 123,\n\t\t\t\t\tC: 456,\n\t\t\t\t\tD: 789,\n\t\t\t\t\tE: 101112,\n\t\t\t\t\tF: 3.14159,\n\t\t\t\t\tG: \"Hello, world\",\n\t\t\t\t\tH: map[string]string{\"foo\": \"bar\"},\n\t\t\t\t\tI: []byte{0x01, 0x02, 0x03},\n\t\t\t\t\tK: [2]string{\"baz\", \"qux\"},\n\t\t\t\t\tL: struct {\n\t\t\t\t\t\tM string\n\t\t\t\t\t}{\n\t\t\t\t\t\tM: \"foobar\",\n\t\t\t\t\t},\n\t\t\t\t\tP:  Raw{0x05, 0x00, 0x00, 0x00, 0x00},\n\t\t\t\t\tQ:  oid,\n\t\t\t\t\tT:  nil,\n\t\t\t\t\tY:  json.Number(\"5\"),\n\t\t\t\t\tZ:  now,\n\t\t\t\t\tAA: json.Number(\"10.1\"),\n\t\t\t\t\tAB: murl,\n\t\t\t\t\tAC: decimal128,\n\t\t\t\t\tAD: &now,\n\t\t\t\t\tAE: testValueMarshaler{t: TypeString, buf: bsoncore.AppendString(nil, \"hello, world\")},\n\t\t\t\t\tAF: RawValue{Type: TypeString, Value: bsoncore.AppendString(nil, \"hello, raw value\")},\n\t\t\t\t\tAG: &RawValue{Type: TypeDouble, Value: bsoncore.AppendDouble(nil, 3.14159)},\n\t\t\t\t\tAH: D{{\"foo\", \"bar\"}},\n\t\t\t\t\tAI: &D{{\"pi\", 3.14159}},\n\t\t\t\t\tAJ: nil,\n\t\t\t\t},\n\t\t\t\tdocToBytes(D{\n\t\t\t\t\t{\"a\", true},\n\t\t\t\t\t{\"b\", int32(123)},\n\t\t\t\t\t{\"c\", int64(456)},\n\t\t\t\t\t{\"d\", int32(789)},\n\t\t\t\t\t{\"e\", int64(101112)},\n\t\t\t\t\t{\"f\", float64(3.14159)},\n\t\t\t\t\t{\"g\", \"Hello, world\"},\n\t\t\t\t\t{\"h\", D{{\"foo\", \"bar\"}}},\n\t\t\t\t\t{\"i\", Binary{Subtype: 0x00, Data: []byte{0x01, 0x02, 0x03}}},\n\t\t\t\t\t{\"k\", A{\"baz\", \"qux\"}},\n\t\t\t\t\t{\"l\", D{{\"m\", \"foobar\"}}},\n\t\t\t\t\t{\"p\", D{}},\n\t\t\t\t\t{\"q\", oid},\n\t\t\t\t\t{\"t\", nil},\n\t\t\t\t\t{\"y\", int64(5)},\n\t\t\t\t\t{\"z\", DateTime(now.UnixNano() / int64(time.Millisecond))},\n\t\t\t\t\t{\"aa\", float64(10.1)},\n\t\t\t\t\t{\"ab\", murl.String()},\n\t\t\t\t\t{\"ac\", decimal128},\n\t\t\t\t\t{\"ad\", DateTime(now.UnixNano() / int64(time.Millisecond))},\n\t\t\t\t\t{\"ae\", \"hello, world\"},\n\t\t\t\t\t{\"af\", \"hello, raw value\"},\n\t\t\t\t\t{\"ag\", 3.14159},\n\t\t\t\t\t{\"ah\", D{{\"foo\", \"bar\"}}},\n\t\t\t\t\t{\"ai\", D{{\"pi\", float64(3.14159)}}},\n\t\t\t\t\t{\"aj\", nil},\n\t\t\t\t}),\n\t\t\t\tnil,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"struct{[]any}\",\n\t\t\t\tstruct {\n\t\t\t\t\tA []bool\n\t\t\t\t\tB []int32\n\t\t\t\t\tC []int64\n\t\t\t\t\tD []uint16\n\t\t\t\t\tE []uint64\n\t\t\t\t\tF []float64\n\t\t\t\t\tG []string\n\t\t\t\t\tH []map[string]string\n\t\t\t\t\tI [][]byte\n\t\t\t\t\tK [1][2]string\n\t\t\t\t\tL []struct {\n\t\t\t\t\t\tM string\n\t\t\t\t\t}\n\t\t\t\t\tN  [][]string\n\t\t\t\t\tQ  []Raw\n\t\t\t\t\tR  []ObjectID\n\t\t\t\t\tT  []struct{}\n\t\t\t\t\tW  []map[string]struct{}\n\t\t\t\t\tX  []map[string]struct{}\n\t\t\t\t\tY  []map[string]struct{}\n\t\t\t\t\tZ  []time.Time\n\t\t\t\t\tAA []json.Number\n\t\t\t\t\tAB []*url.URL\n\t\t\t\t\tAC []Decimal128\n\t\t\t\t\tAD []*time.Time\n\t\t\t\t\tAE []testValueMarshaler\n\t\t\t\t\tAF []D\n\t\t\t\t\tAG []*D\n\t\t\t\t}{\n\t\t\t\t\tA: []bool{true},\n\t\t\t\t\tB: []int32{123},\n\t\t\t\t\tC: []int64{456},\n\t\t\t\t\tD: []uint16{789},\n\t\t\t\t\tE: []uint64{101112},\n\t\t\t\t\tF: []float64{3.14159},\n\t\t\t\t\tG: []string{\"Hello, world\"},\n\t\t\t\t\tH: []map[string]string{{\"foo\": \"bar\"}},\n\t\t\t\t\tI: [][]byte{{0x01, 0x02, 0x03}},\n\t\t\t\t\tK: [1][2]string{{\"baz\", \"qux\"}},\n\t\t\t\t\tL: []struct {\n\t\t\t\t\t\tM string\n\t\t\t\t\t}{\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tM: \"foobar\",\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\tN:  [][]string{{\"foo\", \"bar\"}},\n\t\t\t\t\tQ:  []Raw{{0x05, 0x00, 0x00, 0x00, 0x00}},\n\t\t\t\t\tR:  oids,\n\t\t\t\t\tT:  nil,\n\t\t\t\t\tW:  nil,\n\t\t\t\t\tX:  []map[string]struct{}{},   // Should be empty BSON Array\n\t\t\t\t\tY:  []map[string]struct{}{{}}, // Should be BSON array with one element, an empty BSON SubDocument\n\t\t\t\t\tZ:  []time.Time{now, now},\n\t\t\t\t\tAA: []json.Number{json.Number(\"5\"), json.Number(\"10.1\")},\n\t\t\t\t\tAB: []*url.URL{murl},\n\t\t\t\t\tAC: []Decimal128{decimal128},\n\t\t\t\t\tAD: []*time.Time{&now, &now},\n\t\t\t\t\tAE: []testValueMarshaler{\n\t\t\t\t\t\t{t: TypeString, buf: bsoncore.AppendString(nil, \"hello\")},\n\t\t\t\t\t\t{t: TypeString, buf: bsoncore.AppendString(nil, \"world\")},\n\t\t\t\t\t},\n\t\t\t\t\tAF: []D{{{\"foo\", \"bar\"}}, {{\"hello\", \"world\"}, {\"number\", 12345}}},\n\t\t\t\t\tAG: []*D{{{\"pi\", 3.14159}}, nil},\n\t\t\t\t},\n\t\t\t\tdocToBytes(D{\n\t\t\t\t\t{\"a\", A{true}},\n\t\t\t\t\t{\"b\", A{int32(123)}},\n\t\t\t\t\t{\"c\", A{int64(456)}},\n\t\t\t\t\t{\"d\", A{int32(789)}},\n\t\t\t\t\t{\"e\", A{int64(101112)}},\n\t\t\t\t\t{\"f\", A{float64(3.14159)}},\n\t\t\t\t\t{\"g\", A{\"Hello, world\"}},\n\t\t\t\t\t{\"h\", A{D{{\"foo\", \"bar\"}}}},\n\t\t\t\t\t{\"i\", A{Binary{Subtype: 0x00, Data: []byte{0x01, 0x02, 0x03}}}},\n\t\t\t\t\t{\"k\", A{A{\"baz\", \"qux\"}}},\n\t\t\t\t\t{\"l\", A{D{{\"m\", \"foobar\"}}}},\n\t\t\t\t\t{\"n\", A{A{\"foo\", \"bar\"}}},\n\t\t\t\t\t{\"q\", A{D{}}},\n\t\t\t\t\t{\"r\", A{oids[0], oids[1], oids[2]}},\n\t\t\t\t\t{\"t\", nil},\n\t\t\t\t\t{\"w\", nil},\n\t\t\t\t\t{\"x\", A{}},\n\t\t\t\t\t{\"y\", A{D{}}},\n\t\t\t\t\t{\"z\", A{\n\t\t\t\t\t\tDateTime(now.UnixNano() / int64(time.Millisecond)),\n\t\t\t\t\t\tDateTime(now.UnixNano() / int64(time.Millisecond)),\n\t\t\t\t\t}},\n\t\t\t\t\t{\"aa\", A{int64(5), float64(10.10)}},\n\t\t\t\t\t{\"ab\", A{murl.String()}},\n\t\t\t\t\t{\"ac\", A{decimal128}},\n\t\t\t\t\t{\"ad\", A{\n\t\t\t\t\t\tDateTime(now.UnixNano() / int64(time.Millisecond)),\n\t\t\t\t\t\tDateTime(now.UnixNano() / int64(time.Millisecond)),\n\t\t\t\t\t}},\n\t\t\t\t\t{\"ae\", A{\"hello\", \"world\"}},\n\t\t\t\t\t{\"af\", A{D{{\"foo\", \"bar\"}}, D{{\"hello\", \"world\"}, {\"number\", int32(12345)}}}},\n\t\t\t\t\t{\"ag\", A{D{{\"pi\", float64(3.14159)}}, nil}},\n\t\t\t\t}),\n\t\t\t\tnil,\n\t\t\t},\n\t\t}\n\n\t\tfor _, tc := range testCases {\n\t\t\ttc := tc // Capture the range variable\n\n\t\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\t\tt.Parallel()\n\n\t\t\t\tb := make(sliceWriter, 0, 512)\n\t\t\t\tvw := NewDocumentWriter(&b)\n\t\t\t\tenc := NewEncoder(vw)\n\t\t\t\terr := enc.Encode(tc.value)\n\t\t\t\tif !errors.Is(err, tc.err) {\n\t\t\t\t\tt.Errorf(\"Did not receive expected error. got %v; want %v\", err, tc.err)\n\t\t\t\t}\n\t\t\t\tif diff := cmp.Diff([]byte(b), tc.b); diff != \"\" {\n\t\t\t\t\tt.Errorf(\"Bytes written differ: (-got +want)\\n%s\", diff)\n\t\t\t\t\tt.Errorf(\"Bytes\\ngot: %v\\nwant:%v\\n\", b, tc.b)\n\t\t\t\t\tt.Errorf(\"Readers\\ngot: %v\\nwant:%v\\n\", Raw(b), Raw(tc.b))\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t})\n}\n\nfunc TestPrimitiveValueDecoders(t *testing.T) {\n\twrong := func(string, string) string { return \"wrong\" }\n\n\tconst cansetreflectiontest = \"cansetreflectiontest\"\n\n\ttype subtest struct {\n\t\tname   string\n\t\tval    any\n\t\tdctx   *DecodeContext\n\t\tllvrw  *valueReaderWriter\n\t\tinvoke invoked\n\t\terr    error\n\t}\n\n\ttestCases := []struct {\n\t\tname     string\n\t\tvd       ValueDecoder\n\t\tsubtests []subtest\n\t}{\n\t\t{\n\t\t\t\"RawValueDecodeValue\",\n\t\t\tValueDecoderFunc(rawValueDecodeValue),\n\t\t\t[]subtest{\n\t\t\t\t{\n\t\t\t\t\t\"wrong type\",\n\t\t\t\t\twrong,\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{},\n\t\t\t\t\tnothing,\n\t\t\t\t\tValueDecoderError{\n\t\t\t\t\t\tName:     \"RawValueDecodeValue\",\n\t\t\t\t\t\tTypes:    []reflect.Type{tRawValue},\n\t\t\t\t\t\tReceived: reflect.New(reflect.TypeOf(wrong)).Elem(),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"ReadValue Error\",\n\t\t\t\t\tRawValue{},\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{\n\t\t\t\t\t\tBSONType: TypeBinary,\n\t\t\t\t\t\tErr:      errors.New(\"rb error\"),\n\t\t\t\t\t\tErrAfter: readBinary,\n\t\t\t\t\t},\n\t\t\t\t\treadBinary,\n\t\t\t\t\terrors.New(\"rb error\"),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"RawValue/success\",\n\t\t\t\t\tRawValue{Type: TypeBinary, Value: bsoncore.AppendBinary(nil, 0xFF, []byte{0x01, 0x02, 0x03})},\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{\n\t\t\t\t\t\tBSONType: TypeBinary,\n\t\t\t\t\t\tReturn: bsoncore.Value{\n\t\t\t\t\t\t\tType: bsoncore.TypeBinary,\n\t\t\t\t\t\t\tData: bsoncore.AppendBinary(nil, 0xFF, []byte{0x01, 0x02, 0x03}),\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\treadBinary,\n\t\t\t\t\tnil,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"RawDecodeValue\",\n\t\t\tValueDecoderFunc(rawDecodeValue),\n\t\t\t[]subtest{\n\t\t\t\t{\n\t\t\t\t\t\"wrong type\",\n\t\t\t\t\twrong,\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{},\n\t\t\t\t\tnothing,\n\t\t\t\t\tValueDecoderError{\n\t\t\t\t\t\tName:     \"RawDecodeValue\",\n\t\t\t\t\t\tTypes:    []reflect.Type{tRaw},\n\t\t\t\t\t\tReceived: reflect.New(reflect.TypeOf(wrong)).Elem(),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"*Raw is nil\",\n\t\t\t\t\t(*Raw)(nil),\n\t\t\t\t\tnil,\n\t\t\t\t\tnil,\n\t\t\t\t\tnothing,\n\t\t\t\t\tValueDecoderError{\n\t\t\t\t\t\tName:     \"RawDecodeValue\",\n\t\t\t\t\t\tTypes:    []reflect.Type{tRaw},\n\t\t\t\t\t\tReceived: reflect.New(reflect.TypeOf((*Raw)(nil))).Elem(),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"Copy error\",\n\t\t\t\t\tRaw{},\n\t\t\t\t\tnil,\n\t\t\t\t\t&valueReaderWriter{Err: errors.New(\"copy error\"), ErrAfter: readDocument},\n\t\t\t\t\treadDocument,\n\t\t\t\t\terrors.New(\"copy error\"),\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n\n\tfor _, tc := range testCases {\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\tfor _, rc := range tc.subtests {\n\t\t\t\tt.Run(rc.name, func(t *testing.T) {\n\t\t\t\t\tvar dc DecodeContext\n\t\t\t\t\tif rc.dctx != nil {\n\t\t\t\t\t\tdc = *rc.dctx\n\t\t\t\t\t}\n\t\t\t\t\tllvrw := new(valueReaderWriter)\n\t\t\t\t\tif rc.llvrw != nil {\n\t\t\t\t\t\tllvrw = rc.llvrw\n\t\t\t\t\t}\n\t\t\t\t\tllvrw.T = t\n\t\t\t\t\tif rc.val == cansetreflectiontest { // We're doing a CanSet reflection test\n\t\t\t\t\t\terr := tc.vd.DecodeValue(dc, llvrw, reflect.Value{})\n\t\t\t\t\t\tif !assert.CompareErrors(err, rc.err) {\n\t\t\t\t\t\t\tt.Errorf(\"Errors do not match. got %v; want %v\", err, rc.err)\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tval := reflect.New(reflect.TypeOf(rc.val)).Elem()\n\t\t\t\t\t\terr = tc.vd.DecodeValue(dc, llvrw, val)\n\t\t\t\t\t\tif !assert.CompareErrors(err, rc.err) {\n\t\t\t\t\t\t\tt.Errorf(\"Errors do not match. got %v; want %v\", err, rc.err)\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t\tvar val reflect.Value\n\t\t\t\t\tif rtype := reflect.TypeOf(rc.val); rtype != nil {\n\t\t\t\t\t\tval = reflect.New(rtype).Elem()\n\t\t\t\t\t}\n\t\t\t\t\twant := rc.val\n\t\t\t\t\tdefer func() {\n\t\t\t\t\t\tif err := recover(); err != nil {\n\t\t\t\t\t\t\tfmt.Println(t.Name())\n\t\t\t\t\t\t\tpanic(err)\n\t\t\t\t\t\t}\n\t\t\t\t\t}()\n\t\t\t\t\terr := tc.vd.DecodeValue(dc, llvrw, val)\n\t\t\t\t\tif !assert.CompareErrors(err, rc.err) {\n\t\t\t\t\t\tt.Errorf(\"Errors do not match. got %v; want %v\", err, rc.err)\n\t\t\t\t\t}\n\t\t\t\t\tinvoked := llvrw.invoked\n\t\t\t\t\tif !cmp.Equal(invoked, rc.invoke) {\n\t\t\t\t\t\tt.Errorf(\"Incorrect method invoked. got %v; want %v\", invoked, rc.invoke)\n\t\t\t\t\t}\n\t\t\t\t\tvar got any\n\t\t\t\t\tif val.IsValid() && val.CanInterface() {\n\t\t\t\t\t\tgot = val.Interface()\n\t\t\t\t\t}\n\t\t\t\t\tif rc.err == nil && !cmp.Equal(got, want) {\n\t\t\t\t\t\tt.Errorf(\"Values do not match. got (%T)%v; want (%T)%v\", got, got, want, want)\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\t}\n\n\tt.Run(\"success path\", func(t *testing.T) {\n\t\toid := NewObjectID()\n\t\toids := []ObjectID{NewObjectID(), NewObjectID(), NewObjectID()}\n\t\tstr := new(string)\n\t\t*str = \"bar\"\n\t\tnow := time.Now().Truncate(time.Millisecond)\n\t\tmurl, err := url.Parse(\"https://mongodb.com/random-url?hello=world\")\n\t\tif err != nil {\n\t\t\tt.Errorf(\"Error parsing URL: %v\", err)\n\t\t\tt.FailNow()\n\t\t}\n\t\tdecimal128, err := ParseDecimal128(\"1.5e10\")\n\t\tif err != nil {\n\t\t\tt.Errorf(\"Error parsing decimal128: %v\", err)\n\t\t\tt.FailNow()\n\t\t}\n\n\t\ttestCases := []struct {\n\t\t\tname  string\n\t\t\tvalue any\n\t\t\tb     []byte\n\t\t\terr   error\n\t\t}{\n\t\t\t{\n\t\t\t\t\"map[string]int\",\n\t\t\t\tmap[string]int32{\"foo\": 1},\n\t\t\t\t[]byte{\n\t\t\t\t\t0x0E, 0x00, 0x00, 0x00,\n\t\t\t\t\t0x10, 'f', 'o', 'o', 0x00,\n\t\t\t\t\t0x01, 0x00, 0x00, 0x00,\n\t\t\t\t\t0x00,\n\t\t\t\t},\n\t\t\t\tnil,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"map[string]ObjectID\",\n\t\t\t\tmap[string]ObjectID{\"foo\": oid},\n\t\t\t\tdocToBytes(D{{\"foo\", oid}}),\n\t\t\t\tnil,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"map[string]Reader\",\n\t\t\t\tmap[string]Raw{\"Z\": {0x05, 0x00, 0x00, 0x00, 0x00}},\n\t\t\t\tdocToBytes(D{{\"Z\", Raw{0x05, 0x00, 0x00, 0x00, 0x00}}}),\n\t\t\t\tnil,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"map[string][]int32\",\n\t\t\t\tmap[string][]int32{\"Z\": {1, 2, 3}},\n\t\t\t\tdocToBytes(D{{\"Z\", A{int32(1), int32(2), int32(3)}}}),\n\t\t\t\tnil,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"map[string][]ObjectID\",\n\t\t\t\tmap[string][]ObjectID{\"Z\": oids},\n\t\t\t\tdocToBytes(D{{\"Z\", A{oids[0], oids[1], oids[2]}}}),\n\t\t\t\tnil,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"map[string][]json.Number(int64)\",\n\t\t\t\tmap[string][]json.Number{\"Z\": {json.Number(\"5\"), json.Number(\"10\")}},\n\t\t\t\tdocToBytes(D{{\"Z\", A{int64(5), int64(10)}}}),\n\t\t\t\tnil,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"map[string][]json.Number(float64)\",\n\t\t\t\tmap[string][]json.Number{\"Z\": {json.Number(\"5\"), json.Number(\"10.1\")}},\n\t\t\t\tdocToBytes(D{{\"Z\", A{int64(5), float64(10.1)}}}),\n\t\t\t\tnil,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"map[string][]*url.URL\",\n\t\t\t\tmap[string][]*url.URL{\"Z\": {murl}},\n\t\t\t\tdocToBytes(D{{\"Z\", A{murl.String()}}}),\n\t\t\t\tnil,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"map[string][]Decimal128\",\n\t\t\t\tmap[string][]Decimal128{\"Z\": {decimal128}},\n\t\t\t\tdocToBytes(D{{\"Z\", A{decimal128}}}),\n\t\t\t\tnil,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"-\",\n\t\t\t\tstruct {\n\t\t\t\t\tA string `bson:\"-\"`\n\t\t\t\t}{\n\t\t\t\t\tA: \"\",\n\t\t\t\t},\n\t\t\t\tdocToBytes(D{}),\n\t\t\t\tnil,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"omitempty\",\n\t\t\t\tstruct {\n\t\t\t\t\tA string `bson:\",omitempty\"`\n\t\t\t\t}{\n\t\t\t\t\tA: \"\",\n\t\t\t\t},\n\t\t\t\tdocToBytes(D{}),\n\t\t\t\tnil,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"omitempty, empty time\",\n\t\t\t\tstruct {\n\t\t\t\t\tA time.Time `bson:\",omitempty\"`\n\t\t\t\t}{\n\t\t\t\t\tA: time.Time{},\n\t\t\t\t},\n\t\t\t\tdocToBytes(D{}),\n\t\t\t\tnil,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"no private fields\",\n\t\t\t\tnoPrivateFields{a: \"should be empty\"},\n\t\t\t\tdocToBytes(D{}),\n\t\t\t\tnil,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"minsize\",\n\t\t\t\tstruct {\n\t\t\t\t\tA int64 `bson:\",minsize\"`\n\t\t\t\t}{\n\t\t\t\t\tA: 12345,\n\t\t\t\t},\n\t\t\t\tdocToBytes(D{{\"a\", int32(12345)}}),\n\t\t\t\tnil,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"inline\",\n\t\t\t\tstruct {\n\t\t\t\t\tFoo struct {\n\t\t\t\t\t\tA int64 `bson:\",minsize\"`\n\t\t\t\t\t} `bson:\",inline\"`\n\t\t\t\t}{\n\t\t\t\t\tFoo: struct {\n\t\t\t\t\t\tA int64 `bson:\",minsize\"`\n\t\t\t\t\t}{\n\t\t\t\t\t\tA: 12345,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tdocToBytes(D{{\"a\", int32(12345)}}),\n\t\t\t\tnil,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"inline map\",\n\t\t\t\tstruct {\n\t\t\t\t\tFoo map[string]string `bson:\",inline\"`\n\t\t\t\t}{\n\t\t\t\t\tFoo: map[string]string{\"foo\": \"bar\"},\n\t\t\t\t},\n\t\t\t\tdocToBytes(D{{\"foo\", \"bar\"}}),\n\t\t\t\tnil,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"alternate name bson:name\",\n\t\t\t\tstruct {\n\t\t\t\t\tA string `bson:\"foo\"`\n\t\t\t\t}{\n\t\t\t\t\tA: \"bar\",\n\t\t\t\t},\n\t\t\t\tdocToBytes(D{{\"foo\", \"bar\"}}),\n\t\t\t\tnil,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"alternate name\",\n\t\t\t\tstruct {\n\t\t\t\t\tA string `bson:\"foo\"`\n\t\t\t\t}{\n\t\t\t\t\tA: \"bar\",\n\t\t\t\t},\n\t\t\t\tdocToBytes(D{{\"foo\", \"bar\"}}),\n\t\t\t\tnil,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"inline, omitempty\",\n\t\t\t\tstruct {\n\t\t\t\t\tA   string\n\t\t\t\t\tFoo zeroTest `bson:\"omitempty,inline\"`\n\t\t\t\t}{\n\t\t\t\t\tA:   \"bar\",\n\t\t\t\t\tFoo: zeroTest{true},\n\t\t\t\t},\n\t\t\t\tdocToBytes(D{{\"a\", \"bar\"}}),\n\t\t\t\tnil,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"JavaScript to D\",\n\t\t\t\tD{{\"a\", JavaScript(`function() { var hello = \"world\"; }`)}},\n\t\t\t\tdocToBytes(D{{\"a\", JavaScript(`function() { var hello = \"world\"; }`)}}),\n\t\t\t\tnil,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"Symbol to D\",\n\t\t\t\tD{{\"a\", Symbol(\"foobarbaz\")}},\n\t\t\t\tdocToBytes(D{{\"a\", Symbol(\"foobarbaz\")}}),\n\t\t\t\tnil,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"struct{}\",\n\t\t\t\tstruct {\n\t\t\t\t\tA bool\n\t\t\t\t\tB int32\n\t\t\t\t\tC int64\n\t\t\t\t\tD uint16\n\t\t\t\t\tE uint64\n\t\t\t\t\tF float64\n\t\t\t\t\tG string\n\t\t\t\t\tH map[string]string\n\t\t\t\t\tI []byte\n\t\t\t\t\tK [2]string\n\t\t\t\t\tL struct {\n\t\t\t\t\t\tM string\n\t\t\t\t\t}\n\t\t\t\t\tP  Raw\n\t\t\t\t\tQ  ObjectID\n\t\t\t\t\tT  []struct{}\n\t\t\t\t\tY  json.Number\n\t\t\t\t\tZ  time.Time\n\t\t\t\t\tAA json.Number\n\t\t\t\t\tAB *url.URL\n\t\t\t\t\tAC Decimal128\n\t\t\t\t\tAD *time.Time\n\t\t\t\t\tAE *testValueUnmarshaler\n\t\t\t\t\tAF RawValue\n\t\t\t\t\tAG *RawValue\n\t\t\t\t\tAH D\n\t\t\t\t\tAI *D\n\t\t\t\t\tAJ *D\n\t\t\t\t}{\n\t\t\t\t\tA: true,\n\t\t\t\t\tB: 123,\n\t\t\t\t\tC: 456,\n\t\t\t\t\tD: 789,\n\t\t\t\t\tE: 101112,\n\t\t\t\t\tF: 3.14159,\n\t\t\t\t\tG: \"Hello, world\",\n\t\t\t\t\tH: map[string]string{\"foo\": \"bar\"},\n\t\t\t\t\tI: []byte{0x01, 0x02, 0x03},\n\t\t\t\t\tK: [2]string{\"baz\", \"qux\"},\n\t\t\t\t\tL: struct {\n\t\t\t\t\t\tM string\n\t\t\t\t\t}{\n\t\t\t\t\t\tM: \"foobar\",\n\t\t\t\t\t},\n\t\t\t\t\tP:  Raw{0x05, 0x00, 0x00, 0x00, 0x00},\n\t\t\t\t\tQ:  oid,\n\t\t\t\t\tT:  nil,\n\t\t\t\t\tY:  json.Number(\"5\"),\n\t\t\t\t\tZ:  now,\n\t\t\t\t\tAA: json.Number(\"10.1\"),\n\t\t\t\t\tAB: murl,\n\t\t\t\t\tAC: decimal128,\n\t\t\t\t\tAD: &now,\n\t\t\t\t\tAE: &testValueUnmarshaler{t: TypeString, val: bsoncore.AppendString(nil, \"hello, world!\")},\n\t\t\t\t\tAF: RawValue{Type: TypeDouble, Value: bsoncore.AppendDouble(nil, 3.14159)},\n\t\t\t\t\tAG: &RawValue{Type: TypeBinary, Value: bsoncore.AppendBinary(nil, 0xFF, []byte{0x01, 0x02, 0x03})},\n\t\t\t\t\tAH: D{{\"foo\", \"bar\"}},\n\t\t\t\t\tAI: &D{{\"pi\", 3.14159}},\n\t\t\t\t\tAJ: nil,\n\t\t\t\t},\n\t\t\t\tdocToBytes(D{\n\t\t\t\t\t{\"a\", true},\n\t\t\t\t\t{\"b\", int32(123)},\n\t\t\t\t\t{\"c\", int64(456)},\n\t\t\t\t\t{\"d\", int32(789)},\n\t\t\t\t\t{\"e\", int64(101112)},\n\t\t\t\t\t{\"f\", float64(3.14159)},\n\t\t\t\t\t{\"g\", \"Hello, world\"},\n\t\t\t\t\t{\"h\", D{{\"foo\", \"bar\"}}},\n\t\t\t\t\t{\"i\", Binary{Subtype: 0x00, Data: []byte{0x01, 0x02, 0x03}}},\n\t\t\t\t\t{\"k\", A{\"baz\", \"qux\"}},\n\t\t\t\t\t{\"l\", D{{\"m\", \"foobar\"}}},\n\t\t\t\t\t{\"p\", D{}},\n\t\t\t\t\t{\"q\", oid},\n\t\t\t\t\t{\"t\", nil},\n\t\t\t\t\t{\"y\", int64(5)},\n\t\t\t\t\t{\"z\", DateTime(now.UnixNano() / int64(time.Millisecond))},\n\t\t\t\t\t{\"aa\", float64(10.1)},\n\t\t\t\t\t{\"ab\", murl.String()},\n\t\t\t\t\t{\"ac\", decimal128},\n\t\t\t\t\t{\"ad\", DateTime(now.UnixNano() / int64(time.Millisecond))},\n\t\t\t\t\t{\"ae\", \"hello, world!\"},\n\t\t\t\t\t{\"af\", float64(3.14159)},\n\t\t\t\t\t{\"ag\", Binary{Subtype: 0xFF, Data: []byte{0x01, 0x02, 0x03}}},\n\t\t\t\t\t{\"ah\", D{{\"foo\", \"bar\"}}},\n\t\t\t\t\t{\"ai\", D{{\"pi\", float64(3.14159)}}},\n\t\t\t\t\t{\"aj\", nil},\n\t\t\t\t}),\n\t\t\t\tnil,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"struct{[]any}\",\n\t\t\t\tstruct {\n\t\t\t\t\tA []bool\n\t\t\t\t\tB []int32\n\t\t\t\t\tC []int64\n\t\t\t\t\tD []uint16\n\t\t\t\t\tE []uint64\n\t\t\t\t\tF []float64\n\t\t\t\t\tG []string\n\t\t\t\t\tH []map[string]string\n\t\t\t\t\tI [][]byte\n\t\t\t\t\tK [1][2]string\n\t\t\t\t\tL []struct {\n\t\t\t\t\t\tM string\n\t\t\t\t\t}\n\t\t\t\t\tN  [][]string\n\t\t\t\t\tQ  []Raw\n\t\t\t\t\tR  []ObjectID\n\t\t\t\t\tT  []struct{}\n\t\t\t\t\tW  []map[string]struct{}\n\t\t\t\t\tX  []map[string]struct{}\n\t\t\t\t\tY  []map[string]struct{}\n\t\t\t\t\tZ  []time.Time\n\t\t\t\t\tAA []json.Number\n\t\t\t\t\tAB []*url.URL\n\t\t\t\t\tAC []Decimal128\n\t\t\t\t\tAD []*time.Time\n\t\t\t\t\tAE []*testValueUnmarshaler\n\t\t\t\t\tAF []D\n\t\t\t\t\tAG []*D\n\t\t\t\t}{\n\t\t\t\t\tA: []bool{true},\n\t\t\t\t\tB: []int32{123},\n\t\t\t\t\tC: []int64{456},\n\t\t\t\t\tD: []uint16{789},\n\t\t\t\t\tE: []uint64{101112},\n\t\t\t\t\tF: []float64{3.14159},\n\t\t\t\t\tG: []string{\"Hello, world\"},\n\t\t\t\t\tH: []map[string]string{{\"foo\": \"bar\"}},\n\t\t\t\t\tI: [][]byte{{0x01, 0x02, 0x03}},\n\t\t\t\t\tK: [1][2]string{{\"baz\", \"qux\"}},\n\t\t\t\t\tL: []struct {\n\t\t\t\t\t\tM string\n\t\t\t\t\t}{\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tM: \"foobar\",\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\tN:  [][]string{{\"foo\", \"bar\"}},\n\t\t\t\t\tQ:  []Raw{{0x05, 0x00, 0x00, 0x00, 0x00}},\n\t\t\t\t\tR:  oids,\n\t\t\t\t\tT:  nil,\n\t\t\t\t\tW:  nil,\n\t\t\t\t\tX:  []map[string]struct{}{},   // Should be empty BSON Array\n\t\t\t\t\tY:  []map[string]struct{}{{}}, // Should be BSON array with one element, an empty BSON SubDocument\n\t\t\t\t\tZ:  []time.Time{now, now},\n\t\t\t\t\tAA: []json.Number{json.Number(\"5\"), json.Number(\"10.1\")},\n\t\t\t\t\tAB: []*url.URL{murl},\n\t\t\t\t\tAC: []Decimal128{decimal128},\n\t\t\t\t\tAD: []*time.Time{&now, &now},\n\t\t\t\t\tAE: []*testValueUnmarshaler{\n\t\t\t\t\t\t{t: TypeString, val: bsoncore.AppendString(nil, \"hello\")},\n\t\t\t\t\t\t{t: TypeString, val: bsoncore.AppendString(nil, \"world\")},\n\t\t\t\t\t},\n\t\t\t\t\tAF: []D{{{\"foo\", \"bar\"}}, {{\"hello\", \"world\"}, {\"number\", int64(12345)}}},\n\t\t\t\t\tAG: []*D{{{\"pi\", 3.14159}}, nil},\n\t\t\t\t},\n\t\t\t\tdocToBytes(D{\n\t\t\t\t\t{\"a\", A{true}},\n\t\t\t\t\t{\"b\", A{int32(123)}},\n\t\t\t\t\t{\"c\", A{int64(456)}},\n\t\t\t\t\t{\"d\", A{int32(789)}},\n\t\t\t\t\t{\"e\", A{int64(101112)}},\n\t\t\t\t\t{\"f\", A{float64(3.14159)}},\n\t\t\t\t\t{\"g\", A{\"Hello, world\"}},\n\t\t\t\t\t{\"h\", A{D{{\"foo\", \"bar\"}}}},\n\t\t\t\t\t{\"i\", A{Binary{Subtype: 0x00, Data: []byte{0x01, 0x02, 0x03}}}},\n\t\t\t\t\t{\"k\", A{A{\"baz\", \"qux\"}}},\n\t\t\t\t\t{\"l\", A{D{{\"m\", \"foobar\"}}}},\n\t\t\t\t\t{\"n\", A{A{\"foo\", \"bar\"}}},\n\t\t\t\t\t{\"q\", A{D{}}},\n\t\t\t\t\t{\"r\", A{oids[0], oids[1], oids[2]}},\n\t\t\t\t\t{\"t\", nil},\n\t\t\t\t\t{\"w\", nil},\n\t\t\t\t\t{\"x\", A{}},\n\t\t\t\t\t{\"y\", A{D{}}},\n\t\t\t\t\t{\"z\", A{\n\t\t\t\t\t\tDateTime(now.UnixNano() / int64(time.Millisecond)),\n\t\t\t\t\t\tDateTime(now.UnixNano() / int64(time.Millisecond)),\n\t\t\t\t\t}},\n\t\t\t\t\t{\"aa\", A{int64(5), float64(10.10)}},\n\t\t\t\t\t{\"ab\", A{murl.String()}},\n\t\t\t\t\t{\"ac\", A{decimal128}},\n\t\t\t\t\t{\"ad\", A{\n\t\t\t\t\t\tDateTime(now.UnixNano() / int64(time.Millisecond)),\n\t\t\t\t\t\tDateTime(now.UnixNano() / int64(time.Millisecond)),\n\t\t\t\t\t}},\n\t\t\t\t\t{\"ae\", A{\"hello\", \"world\"}},\n\t\t\t\t\t{\"af\", A{\n\t\t\t\t\t\tD{{\"foo\", \"bar\"}},\n\t\t\t\t\t\tD{{\"hello\", \"world\"}, {\"number\", int64(12345)}},\n\t\t\t\t\t}},\n\t\t\t\t\t{\"ag\", A{D{{\"pi\", float64(3.14159)}}, nil}},\n\t\t\t\t}),\n\t\t\t\tnil,\n\t\t\t},\n\t\t}\n\n\t\tt.Run(\"Decode\", func(t *testing.T) {\n\t\t\tfor _, tc := range testCases {\n\t\t\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\t\t\tvr := NewDocumentReader(bytes.NewReader(tc.b))\n\t\t\t\t\tdec := NewDecoder(vr)\n\t\t\t\t\tgotVal := reflect.New(reflect.TypeOf(tc.value))\n\t\t\t\t\terr := dec.Decode(gotVal.Interface())\n\t\t\t\t\tnoerr(t, err)\n\t\t\t\t\tgot := gotVal.Elem().Interface()\n\t\t\t\t\twant := tc.value\n\t\t\t\t\tif diff := cmp.Diff(\n\t\t\t\t\t\tgot, want,\n\t\t\t\t\t\tcmp.Comparer(compareDecimal128),\n\t\t\t\t\t\tcmp.Comparer(compareNoPrivateFields),\n\t\t\t\t\t\tcmp.Comparer(compareZeroTest),\n\t\t\t\t\t); diff != \"\" {\n\t\t\t\t\t\tt.Errorf(\"difference:\\n%s\", diff)\n\t\t\t\t\t\tt.Errorf(\"Values are not equal.\\ngot: %#v\\nwant:%#v\", got, want)\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\t})\n}\n\ntype testValueMarshaler struct {\n\tt   Type\n\tbuf []byte\n\terr error\n}\n\nfunc (tvm testValueMarshaler) MarshalBSONValue() (byte, []byte, error) {\n\treturn byte(tvm.t), tvm.buf, tvm.err\n}\n\ntype testValueUnmarshaler struct {\n\tt   Type\n\tval []byte\n\terr error\n}\n\nfunc (tvu *testValueUnmarshaler) UnmarshalBSONValue(t byte, val []byte) error {\n\ttvu.t, tvu.val = Type(t), val\n\treturn tvu.err\n}\n\nfunc (tvu testValueUnmarshaler) Equal(tvu2 testValueUnmarshaler) bool {\n\treturn tvu.t == tvu2.t && bytes.Equal(tvu.val, tvu2.val)\n}\n\ntype noPrivateFields struct {\n\ta string\n}\n\nfunc compareNoPrivateFields(npf1, npf2 noPrivateFields) bool {\n\treturn npf1.a != npf2.a // We don't want these to be equal\n}\n\ntype zeroTest struct {\n\treportZero bool\n}\n\nfunc (z zeroTest) IsZero() bool { return z.reportZero }\n\nvar _ Zeroer = zeroTest{}\n\nfunc compareZeroTest(_, _ zeroTest) bool { return true }\n\nfunc compareDecimal128(d1, d2 Decimal128) bool {\n\td1H, d1L := d1.GetBytes()\n\td2H, d2L := d2.GetBytes()\n\n\tif d1H != d2H {\n\t\treturn false\n\t}\n\n\tif d1L != d2L {\n\t\treturn false\n\t}\n\n\treturn true\n}\n"
  },
  {
    "path": "bson/raw.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bson\n\nimport (\n\t\"errors\"\n\t\"io\"\n\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n)\n\n// ErrNilReader indicates that an operation was attempted on a nil bson.Reader.\nvar ErrNilReader = errors.New(\"nil reader\")\n\n// Raw is a raw encoded BSON document. It can be used to delay BSON document decoding or precompute\n// a BSON encoded document.\n//\n// A Raw must be a full BSON document. Use the RawValue type for individual BSON values.\ntype Raw []byte\n\n// ReadDocument reads a BSON document from the io.Reader and returns it as a bson.Raw. If the\n// reader contains multiple BSON documents, only the first document is read.\nfunc ReadDocument(r io.Reader) (Raw, error) {\n\tdoc, err := bsoncore.NewDocumentFromReader(r)\n\treturn Raw(doc), err\n}\n\n// Validate validates the document. This method only validates the first document in\n// the slice, to validate other documents, the slice must be resliced.\nfunc (r Raw) Validate() (err error) { return bsoncore.Document(r).Validate() }\n\n// Lookup search the document, potentially recursively, for the given key. If\n// there are multiple keys provided, this method will recurse down, as long as\n// the top and intermediate nodes are either documents or arrays.If an error\n// occurs or if the value doesn't exist, an empty RawValue is returned.\nfunc (r Raw) Lookup(key ...string) RawValue {\n\treturn convertFromCoreValue(bsoncore.Document(r).Lookup(key...))\n}\n\n// LookupErr searches the document and potentially subdocuments or arrays for the\n// provided key. Each key provided to this method represents a layer of depth.\nfunc (r Raw) LookupErr(key ...string) (RawValue, error) {\n\tval, err := bsoncore.Document(r).LookupErr(key...)\n\treturn convertFromCoreValue(val), err\n}\n\n// Elements returns this document as a slice of elements. The returned slice will contain valid\n// elements. If the document is not valid, the elements up to the invalid point will be returned\n// along with an error.\nfunc (r Raw) Elements() ([]RawElement, error) {\n\tdoc := bsoncore.Document(r)\n\tif len(doc) == 0 {\n\t\treturn nil, nil\n\t}\n\telems, err := doc.Elements()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\trelems := make([]RawElement, 0, len(elems))\n\tfor _, elem := range elems {\n\t\trelems = append(relems, RawElement(elem))\n\t}\n\treturn relems, nil\n}\n\n// Values returns this document as a slice of values. The returned slice will contain valid values.\n// If the document is not valid, the values up to the invalid point will be returned along with an\n// error.\nfunc (r Raw) Values() ([]RawValue, error) {\n\tvals, err := bsoncore.Document(r).Values()\n\trvals := make([]RawValue, 0, len(vals))\n\tfor _, val := range vals {\n\t\trvals = append(rvals, convertFromCoreValue(val))\n\t}\n\treturn rvals, err\n}\n\n// Index searches for and retrieves the element at the given index. This method will panic if\n// the document is invalid or if the index is out of bounds.\nfunc (r Raw) Index(index uint) RawElement { return RawElement(bsoncore.Document(r).Index(index)) }\n\n// IndexErr searches for and retrieves the element at the given index.\nfunc (r Raw) IndexErr(index uint) (RawElement, error) {\n\telem, err := bsoncore.Document(r).IndexErr(index)\n\treturn RawElement(elem), err\n}\n\n// String returns the BSON document encoded as Extended JSON.\nfunc (r Raw) String() string { return bsoncore.Document(r).String() }\n"
  },
  {
    "path": "bson/raw_array.go",
    "content": "// Copyright (C) MongoDB, Inc. 2024-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bson\n\nimport (\n\t\"io\"\n\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n)\n\n// RawArray is a raw bytes representation of a BSON array.\ntype RawArray []byte\n\n// ReadArray reads a BSON array from the io.Reader and returns it as a\n// bson.RawArray.\nfunc ReadArray(r io.Reader) (RawArray, error) {\n\tdoc, err := bsoncore.NewArrayFromReader(r)\n\n\treturn RawArray(doc), err\n}\n\n// Index searches for and retrieves the value at the given index. This method\n// will panic if the array is invalid or if the index is out of bounds.\nfunc (a RawArray) Index(index uint) RawValue {\n\treturn convertFromCoreValue(bsoncore.Array(a).Index(index))\n}\n\n// IndexErr searches for and retrieves the value at the given index.\nfunc (a RawArray) IndexErr(index uint) (RawValue, error) {\n\telem, err := bsoncore.Array(a).IndexErr(index)\n\n\treturn convertFromCoreValue(elem), err\n}\n\n// DebugString outputs a human readable version of Array. It will attempt to\n// stringify the valid components of the array even if the entire array is not\n// valid.\nfunc (a RawArray) DebugString() string {\n\treturn bsoncore.Array(a).DebugString()\n}\n\n// String outputs an ExtendedJSON version of Array. If the Array is not valid,\n// this method returns an empty string.\nfunc (a RawArray) String() string {\n\treturn bsoncore.Array(a).String()\n}\n\n// Values returns this array as a slice of values. The returned slice will\n// contain valid values. If the array is not valid, the values up to the invalid\n// point will be returned along with an error.\nfunc (a RawArray) Values() ([]RawValue, error) {\n\tvals, err := bsoncore.Array(a).Values()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\trvals := make([]RawValue, 0, len(vals))\n\tfor _, val := range vals {\n\t\trvals = append(rvals, convertFromCoreValue(val))\n\t}\n\n\treturn rvals, err\n}\n\n// Validate validates the array and ensures the elements contained within are\n// valid.\nfunc (a RawArray) Validate() error {\n\treturn bsoncore.Array(a).Validate()\n}\n"
  },
  {
    "path": "bson/raw_array_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2024-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bson\n\nimport (\n\t\"bytes\"\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"io\"\n\t\"testing\"\n\n\t\"github.com/google/go-cmp/cmp\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/require\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n)\n\nfunc TestReadArray(t *testing.T) {\n\tt.Parallel()\n\n\ttestCases := []struct {\n\t\tname     string\n\t\tioReader io.Reader\n\t\tarr      RawArray\n\t\terr      error\n\t}{\n\t\t{\n\t\t\t\"nil reader\",\n\t\t\tnil,\n\t\t\tnil,\n\t\t\tErrNilReader,\n\t\t},\n\t\t{\n\t\t\t\"premature end of reader\",\n\t\t\tbytes.NewBuffer([]byte{}),\n\t\t\tnil,\n\t\t\tio.EOF,\n\t\t},\n\t\t{\n\t\t\t\"empty Array\",\n\t\t\tbytes.NewBuffer([]byte{5, 0, 0, 0, 0}),\n\t\t\t[]byte{5, 0, 0, 0, 0},\n\t\t\tnil,\n\t\t},\n\t\t{\n\t\t\t\"non-empty Array\",\n\t\t\tbytes.NewBuffer([]byte{\n\t\t\t\t'\\x1B', '\\x00', '\\x00', '\\x00',\n\t\t\t\t'\\x02',\n\t\t\t\t'0', '\\x00',\n\t\t\t\t'\\x04', '\\x00', '\\x00', '\\x00',\n\t\t\t\t'\\x62', '\\x61', '\\x72', '\\x00',\n\t\t\t\t'\\x02',\n\t\t\t\t'1', '\\x00',\n\t\t\t\t'\\x04', '\\x00', '\\x00', '\\x00',\n\t\t\t\t'\\x62', '\\x61', '\\x7a', '\\x00',\n\t\t\t\t'\\x00',\n\t\t\t}),\n\t\t\t[]byte{\n\t\t\t\t'\\x1B', '\\x00', '\\x00', '\\x00',\n\t\t\t\t'\\x02',\n\t\t\t\t'0', '\\x00',\n\t\t\t\t'\\x04', '\\x00', '\\x00', '\\x00',\n\t\t\t\t'\\x62', '\\x61', '\\x72', '\\x00',\n\t\t\t\t'\\x02',\n\t\t\t\t'1', '\\x00',\n\t\t\t\t'\\x04', '\\x00', '\\x00', '\\x00',\n\t\t\t\t'\\x62', '\\x61', '\\x7a', '\\x00',\n\t\t\t\t'\\x00',\n\t\t\t},\n\t\t\tnil,\n\t\t},\n\t}\n\n\tfor _, tc := range testCases {\n\t\ttc := tc // Capture range variable\n\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tarr, err := ReadArray(tc.ioReader)\n\t\t\tif !assert.CompareErrors(err, tc.err) {\n\t\t\t\tt.Errorf(\"errors do not match. got %v; want %v\", err, tc.err)\n\t\t\t}\n\t\t\tif !bytes.Equal(tc.arr, arr) {\n\t\t\t\tt.Errorf(\"Arrays differ. got %v; want %v\", tc.arr, arr)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestArray_Validate(t *testing.T) {\n\tt.Parallel()\n\n\tt.Run(\"TooShort\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\twant := bsoncore.NewInsufficientBytesError(nil, nil)\n\t\tgot := RawArray{'\\x00', '\\x00'}.Validate()\n\t\tif !assert.CompareErrors(got, want) {\n\t\t\tt.Errorf(\"Did not get expected error. got %v; want %v\", got, want)\n\t\t}\n\t})\n\n\tt.Run(\"InvalidLength\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\twant := bsoncore.NewArrayLengthError(200, 5)\n\t\tr := make(RawArray, 5)\n\t\tbinary.LittleEndian.PutUint32(r[0:4], 200)\n\t\tgot := r.Validate()\n\t\tif !assert.CompareErrors(got, want) {\n\t\t\tt.Errorf(\"Did not get expected error. got %v; want %v\", got, want)\n\t\t}\n\t})\n\n\tt.Run(\"Invalid Element\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\twant := bsoncore.NewInsufficientBytesError(nil, nil)\n\t\tr := make(RawArray, 7)\n\t\tbinary.LittleEndian.PutUint32(r[0:4], 7)\n\t\tr[4], r[5], r[6] = 0x02, 'f', 0x00\n\t\tgot := r.Validate()\n\t\tif !assert.CompareErrors(got, want) {\n\t\t\tt.Errorf(\"Did not get expected error. got %v; want %v\", got, want)\n\t\t}\n\t})\n\n\tt.Run(\"Missing Null Terminator\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\twant := bsoncore.ErrMissingNull\n\t\tr := make(RawArray, 6)\n\t\tbinary.LittleEndian.PutUint32(r[0:4], 6)\n\t\tr[4], r[5] = 0x0A, '0'\n\t\tgot := r.Validate()\n\t\tif !assert.CompareErrors(got, want) {\n\t\t\tt.Errorf(\"Did not get expected error. got %v; want %v\", got, want)\n\t\t}\n\t})\n\n\ttestCases := []struct {\n\t\tname string\n\t\tr    RawArray\n\t\twant error\n\t}{\n\t\t{\"array null\", RawArray{'\\x08', '\\x00', '\\x00', '\\x00', '\\x0A', '0', '\\x00', '\\x00'}, nil},\n\t\t{\n\t\t\t\"array\",\n\t\t\tRawArray{\n\t\t\t\t'\\x1B', '\\x00', '\\x00', '\\x00',\n\t\t\t\t'\\x02',\n\t\t\t\t'0', '\\x00',\n\t\t\t\t'\\x04', '\\x00', '\\x00', '\\x00',\n\t\t\t\t'\\x62', '\\x61', '\\x72', '\\x00',\n\t\t\t\t'\\x02',\n\t\t\t\t'1', '\\x00',\n\t\t\t\t'\\x04', '\\x00', '\\x00', '\\x00',\n\t\t\t\t'\\x62', '\\x61', '\\x7a', '\\x00',\n\t\t\t\t'\\x00',\n\t\t\t},\n\t\t\tnil,\n\t\t},\n\t\t{\n\t\t\t\"subarray\",\n\t\t\tRawArray{\n\t\t\t\t'\\x13', '\\x00', '\\x00', '\\x00',\n\t\t\t\t'\\x04',\n\t\t\t\t'0', '\\x00',\n\t\t\t\t'\\x0B', '\\x00', '\\x00', '\\x00', '\\x0A', '0', '\\x00',\n\t\t\t\t'\\x0A', '1', '\\x00', '\\x00', '\\x00',\n\t\t\t},\n\t\t\tnil,\n\t\t},\n\t\t{\n\t\t\t\"invalid key order\",\n\t\t\tRawArray{\n\t\t\t\t'\\x0B', '\\x00', '\\x00', '\\x00', '\\x0A', '2', '\\x00',\n\t\t\t\t'\\x0A', '0', '\\x00', '\\x00', '\\x00',\n\t\t\t},\n\t\t\terrors.New(`array key \"2\" is out of order or invalid`),\n\t\t},\n\t\t{\n\t\t\t\"invalid key type\",\n\t\t\tRawArray{\n\t\t\t\t'\\x0B', '\\x00', '\\x00', '\\x00', '\\x0A', 'p', '\\x00',\n\t\t\t\t'\\x0A', 'q', '\\x00', '\\x00', '\\x00',\n\t\t\t},\n\t\t\terrors.New(`array key \"p\" is out of order or invalid`),\n\t\t},\n\t}\n\n\tfor _, tc := range testCases {\n\t\ttc := tc // Capture the range variable\n\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tgot := tc.r.Validate()\n\t\t\tif !assert.CompareErrors(got, tc.want) {\n\t\t\t\tt.Errorf(\"Returned error does not match. got %v; want %v\", got, tc.want)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestArray_Index(t *testing.T) {\n\tt.Parallel()\n\n\tt.Run(\"Out of bounds\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\trdr := RawArray{0xe, 0x0, 0x0, 0x0, 0xa, '0', 0x0, 0xa, '1', 0x0, 0xa, 0x7a, 0x0, 0x0}\n\t\t_, err := rdr.IndexErr(3)\n\t\tif !errors.Is(err, bsoncore.ErrOutOfBounds) {\n\t\t\tt.Errorf(\"Out of bounds should be returned when accessing element beyond end of Array. got %v; want %v\", err,\n\t\t\t\tbsoncore.ErrOutOfBounds)\n\t\t}\n\t})\n\n\tt.Run(\"Validation Error\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\trdr := RawArray{0x07, 0x00, 0x00, 0x00, 0x00}\n\t\t_, got := rdr.IndexErr(1)\n\t\twant := bsoncore.NewInsufficientBytesError(nil, nil)\n\t\tif !assert.CompareErrors(got, want) {\n\t\t\tt.Errorf(\"Did not receive expected error. got %v; want %v\", got, want)\n\t\t}\n\t})\n\n\ttestArray := RawArray{\n\t\t'\\x26', '\\x00', '\\x00', '\\x00',\n\t\t'\\x02',\n\t\t'0', '\\x00',\n\t\t'\\x04', '\\x00', '\\x00', '\\x00',\n\t\t'\\x62', '\\x61', '\\x72', '\\x00',\n\t\t'\\x02',\n\t\t'1', '\\x00',\n\t\t'\\x04', '\\x00', '\\x00', '\\x00',\n\t\t'\\x62', '\\x61', '\\x7a', '\\x00',\n\t\t'\\x02',\n\t\t'2', '\\x00',\n\t\t'\\x04', '\\x00', '\\x00', '\\x00',\n\t\t'\\x71', '\\x75', '\\x78', '\\x00',\n\t\t'\\x00',\n\t}\n\ttestCases := []struct {\n\t\tname  string\n\t\tindex uint\n\t\twant  RawValue\n\t}{\n\t\t{\n\t\t\t\"first\",\n\t\t\t0,\n\t\t\tRawValue{\n\t\t\t\tType:  TypeString,\n\t\t\t\tValue: []byte{0x04, 0x00, 0x00, 0x00, 0x62, 0x61, 0x72, 0x00},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"second\",\n\t\t\t1,\n\t\t\tRawValue{\n\t\t\t\tType:  TypeString,\n\t\t\t\tValue: []byte{0x04, 0x00, 0x00, 0x00, 0x62, 0x61, 0x7a, 0x00},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"third\",\n\t\t\t2,\n\t\t\tRawValue{\n\t\t\t\tType:  TypeString,\n\t\t\t\tValue: []byte{0x04, 0x00, 0x00, 0x00, 0x71, 0x75, 0x78, 0x00},\n\t\t\t},\n\t\t},\n\t}\n\n\tfor _, tc := range testCases {\n\t\ttc := tc // Capture the range variable\n\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tt.Run(\"IndexErr\", func(t *testing.T) {\n\t\t\t\tt.Parallel()\n\n\t\t\t\tgot, err := testArray.IndexErr(tc.index)\n\t\t\t\tif err != nil {\n\t\t\t\t\tt.Errorf(\"Unexpected error from IndexErr: %s\", err)\n\t\t\t\t}\n\t\t\t\tif diff := cmp.Diff(got, tc.want); diff != \"\" {\n\t\t\t\t\tt.Errorf(\"Arrays differ: (-got +want)\\n%s\", diff)\n\t\t\t\t}\n\t\t\t})\n\n\t\t\tt.Run(\"Index\", func(t *testing.T) {\n\t\t\t\tt.Parallel()\n\n\t\t\t\tdefer func() {\n\t\t\t\t\tif err := recover(); err != nil {\n\t\t\t\t\t\tt.Errorf(\"Unexpected error: %v\", err)\n\t\t\t\t\t}\n\t\t\t\t}()\n\t\t\t\tgot := testArray.Index(tc.index)\n\t\t\t\tif diff := cmp.Diff(got, tc.want); diff != \"\" {\n\t\t\t\t\tt.Errorf(\"Arrays differ: (-got +want)\\n%s\", diff)\n\t\t\t\t}\n\t\t\t})\n\t\t})\n\t}\n}\n\nfunc TestRawArray_Stringt(t *testing.T) {\n\tt.Parallel()\n\n\ttestCases := []struct {\n\t\tname             string\n\t\tarr              RawArray\n\t\tarrayString      string\n\t\tarrayDebugString string\n\t}{\n\t\t{\n\t\t\t\"array\",\n\t\t\tRawArray{\n\t\t\t\t'\\x1B', '\\x00', '\\x00', '\\x00',\n\t\t\t\t'\\x02',\n\t\t\t\t'0', '\\x00',\n\t\t\t\t'\\x04', '\\x00', '\\x00', '\\x00',\n\t\t\t\t'\\x62', '\\x61', '\\x72', '\\x00',\n\t\t\t\t'\\x02',\n\t\t\t\t'1', '\\x00',\n\t\t\t\t'\\x04', '\\x00', '\\x00', '\\x00',\n\t\t\t\t'\\x62', '\\x61', '\\x7a', '\\x00',\n\t\t\t\t'\\x00',\n\t\t\t},\n\t\t\t`[\"bar\",\"baz\"]`,\n\t\t\t`Array(27)[\"bar\",\"baz\"]`,\n\t\t},\n\t\t{\n\t\t\t\"subarray\",\n\t\t\tRawArray{\n\t\t\t\t'\\x13', '\\x00', '\\x00', '\\x00',\n\t\t\t\t'\\x04',\n\t\t\t\t'0', '\\x00',\n\t\t\t\t'\\x0B', '\\x00', '\\x00', '\\x00',\n\t\t\t\t'\\x0A', '0', '\\x00',\n\t\t\t\t'\\x0A', '1', '\\x00',\n\t\t\t\t'\\x00', '\\x00',\n\t\t\t},\n\t\t\t`[[null,null]]`,\n\t\t\t`Array(19)[Array(11)[null,null]]`,\n\t\t},\n\t\t{\n\t\t\t\"malformed--length too small\",\n\t\t\tRawArray{\n\t\t\t\t'\\x04', '\\x00', '\\x00', '\\x00',\n\t\t\t\t'\\x00',\n\t\t\t},\n\t\t\t``,\n\t\t\t`Array(4)[]`,\n\t\t},\n\t\t{\n\t\t\t\"malformed--length too large\",\n\t\t\tRawArray{\n\t\t\t\t'\\x13', '\\x00', '\\x00', '\\x00',\n\t\t\t\t'\\x00',\n\t\t\t},\n\t\t\t``,\n\t\t\t`Array(19)[<malformed (15)>]`,\n\t\t},\n\t\t{\n\t\t\t\"malformed--missing null byte\",\n\t\t\tRawArray{\n\t\t\t\t'\\x06', '\\x00', '\\x00', '\\x00',\n\t\t\t\t'\\x02', '0',\n\t\t\t},\n\t\t\t``,\n\t\t\t`Array(6)[<malformed (2)>]`,\n\t\t},\n\t}\n\n\tfor _, tc := range testCases {\n\t\ttc := tc // Capture the range variable\n\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tarrayString := tc.arr.String()\n\t\t\tif arrayString != tc.arrayString {\n\t\t\t\tt.Errorf(\"array strings do not match. got %q; want %q\",\n\t\t\t\t\tarrayString, tc.arrayString)\n\t\t\t}\n\n\t\t\tarrayDebugString := tc.arr.DebugString()\n\t\t\tif arrayDebugString != tc.arrayDebugString {\n\t\t\t\tt.Errorf(\"array debug strings do not match. got %q; want %q\",\n\t\t\t\t\tarrayDebugString, tc.arrayDebugString)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestRawArray_Values(t *testing.T) {\n\tt.Parallel()\n\n\ttestCases := []struct {\n\t\tname string\n\t\tarr  RawArray\n\t\twant []RawValue\n\t}{\n\t\t{\n\t\t\tname: \"empty\",\n\t\t\tarr:  []byte{0x05, 0x00, 0x00, 0x00, 0x00},\n\t\t\twant: []RawValue{},\n\t\t},\n\t\t{\n\t\t\tname: \"null document\",\n\t\t\tarr:  []byte{0x07, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00}, // [{}]\n\t\t\twant: []RawValue{\n\t\t\t\t{\n\t\t\t\t\tType:  TypeNull,\n\t\t\t\t\tValue: []byte{},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"same\",\n\t\t\tarr: []byte{\n\t\t\t\t0x13, 0x00, 0x00, 0x00, 0x10, 0x30, 0x00, 0x01, 0x00, 0x00,\n\t\t\t\t0x00, 0x10, 0x31, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,\n\t\t\t}, // [int32(1), int32(2)]\n\t\t\twant: []RawValue{\n\t\t\t\t{ // int32(1)\n\t\t\t\t\tType:  TypeInt32,\n\t\t\t\t\tValue: []byte{0x01, 0x00, 0x00, 0x00},\n\t\t\t\t},\n\t\t\t\t{ // int32(2)\n\t\t\t\t\tType:  TypeInt32,\n\t\t\t\t\tValue: []byte{0x02, 0x00, 0x00, 0x00},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"mixed\",\n\t\t\tarr: []byte{\n\t\t\t\t0x17, 0x00, 0x00, 0x00, 0x10, 0x30, 0x00, 0x01, 0x00, 0x00,\n\t\t\t\t0x00, 0x02, 0x31, 0x00, 0x04, 0x00, 0x00, 0x00, 0x66, 0x6F, 0x6F, 0x00, 0x00,\n\t\t\t}, // [int32(1), \"foo\"]\n\t\t\twant: []RawValue{\n\t\t\t\t{ // int32(1)\n\t\t\t\t\tType:  TypeInt32,\n\t\t\t\t\tValue: []byte{0x01, 0x00, 0x00, 0x00},\n\t\t\t\t},\n\t\t\t\t{ // \"foo\"\n\t\t\t\t\tType:  TypeString,\n\t\t\t\t\tValue: []byte{0x04, 0x00, 0x00, 0x00, 0x66, 0x6F, 0x6F, 0x00},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n\n\tfor _, tcase := range testCases {\n\t\ttcase := tcase\n\n\t\tt.Run(tcase.name, func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tvalues, err := tcase.arr.Values()\n\t\t\trequire.NoError(t, err, \"failed to turn array into values\")\n\t\t\trequire.Len(t, values, len(tcase.want), \"got len does not match want\")\n\n\t\t\tfor idx, want := range tcase.want {\n\t\t\t\tassert.True(t, want.Equal(values[idx]), \"want: %v, got: %v\", want, values[idx])\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "bson/raw_element.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bson\n\nimport (\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n)\n\n// RawElement is a raw encoded BSON document or array element.\ntype RawElement []byte\n\n// Key returns the key for this element. If the element is not valid, this method returns an empty\n// string. If knowing if the element is valid is important, use KeyErr.\nfunc (re RawElement) Key() string { return bsoncore.Element(re).Key() }\n\n// KeyErr returns the key for this element, returning an error if the element is not valid.\nfunc (re RawElement) KeyErr() (string, error) { return bsoncore.Element(re).KeyErr() }\n\n// Value returns the value of this element. If the element is not valid, this method returns an\n// empty Value. If knowing if the element is valid is important, use ValueErr.\nfunc (re RawElement) Value() RawValue { return convertFromCoreValue(bsoncore.Element(re).Value()) }\n\n// ValueErr returns the value for this element, returning an error if the element is not valid.\nfunc (re RawElement) ValueErr() (RawValue, error) {\n\tval, err := bsoncore.Element(re).ValueErr()\n\treturn convertFromCoreValue(val), err\n}\n\n// Validate ensures re is a valid BSON element.\nfunc (re RawElement) Validate() error { return bsoncore.Element(re).Validate() }\n\n// String returns the BSON element encoded as Extended JSON.\nfunc (re RawElement) String() string {\n\tdoc := bsoncore.BuildDocument(nil, re)\n\tj, err := MarshalExtJSON(Raw(doc), true, false)\n\tif err != nil {\n\t\treturn \"<malformed>\"\n\t}\n\treturn string(j)\n}\n\n// DebugString outputs a human readable version of RawElement. It will attempt to stringify the\n// valid components of the element even if the entire element is not valid.\nfunc (re RawElement) DebugString() string { return bsoncore.Element(re).DebugString() }\n"
  },
  {
    "path": "bson/raw_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bson\n\nimport (\n\t\"bytes\"\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/google/go-cmp/cmp\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/require\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n)\n\nfunc ExampleRaw_Validate() {\n\trdr := make(Raw, 500)\n\trdr[250], rdr[251], rdr[252], rdr[253], rdr[254] = '\\x05', '\\x00', '\\x00', '\\x00', '\\x00'\n\terr := rdr[250:].Validate()\n\tfmt.Println(err)\n\n\t// Output: <nil>\n}\n\nfunc BenchmarkRawValidate(b *testing.B) {\n\tfor i := 0; i < b.N; i++ {\n\t\trdr := make(Raw, 500)\n\t\trdr[250], rdr[251], rdr[252], rdr[253], rdr[254] = '\\x05', '\\x00', '\\x00', '\\x00', '\\x00'\n\t\t_ = rdr[250:].Validate()\n\t}\n}\n\nfunc TestRaw(t *testing.T) {\n\tt.Run(\"Validate\", func(t *testing.T) {\n\t\tt.Run(\"TooShort\", func(t *testing.T) {\n\t\t\twant := bsoncore.NewInsufficientBytesError(nil, nil)\n\t\t\tgot := Raw{'\\x00', '\\x00'}.Validate()\n\t\t\tif !assert.CompareErrors(got, want) {\n\t\t\t\tt.Errorf(\"Did not get expected error. got %v; want %v\", got, want)\n\t\t\t}\n\t\t})\n\t\tt.Run(\"InvalidLength\", func(t *testing.T) {\n\t\t\twant := bsoncore.ValidationError(\"document length exceeds available bytes. length=200 remainingBytes=5\")\n\t\t\tr := make(Raw, 5)\n\t\t\tbinary.LittleEndian.PutUint32(r[0:4], 200)\n\t\t\tgot := r.Validate()\n\t\t\tif !errors.Is(got, want) {\n\t\t\t\tt.Errorf(\"Did not get expected error. got %v; want %v\", got, want)\n\t\t\t}\n\t\t})\n\t\tt.Run(\"keyLength-error\", func(t *testing.T) {\n\t\t\twant := bsoncore.ErrMissingNull\n\t\t\tr := make(Raw, 8)\n\t\t\tbinary.LittleEndian.PutUint32(r[0:4], 8)\n\t\t\tr[4], r[5], r[6], r[7] = '\\x02', 'f', 'o', 'o'\n\t\t\tgot := r.Validate()\n\t\t\tif !errors.Is(got, want) {\n\t\t\t\tt.Errorf(\"Did not get expected error. got %v; want %v\", got, want)\n\t\t\t}\n\t\t})\n\t\tt.Run(\"Missing-Null-Terminator\", func(t *testing.T) {\n\t\t\twant := bsoncore.ErrMissingNull\n\t\t\tr := make(Raw, 9)\n\t\t\tbinary.LittleEndian.PutUint32(r[0:4], 9)\n\t\t\tr[4], r[5], r[6], r[7], r[8] = '\\x0A', 'f', 'o', 'o', '\\x00'\n\t\t\tgot := r.Validate()\n\t\t\tif !errors.Is(got, want) {\n\t\t\t\tt.Errorf(\"Did not get expected error. got %v; want %v\", got, want)\n\t\t\t}\n\t\t})\n\t\tt.Run(\"validateValue-error\", func(t *testing.T) {\n\t\t\twant := bsoncore.ErrMissingNull\n\t\t\tr := make(Raw, 11)\n\t\t\tbinary.LittleEndian.PutUint32(r[0:4], 11)\n\t\t\tr[4], r[5], r[6], r[7], r[8], r[9], r[10] = '\\x01', 'f', 'o', 'o', '\\x00', '\\x01', '\\x02'\n\t\t\tgot := r.Validate()\n\t\t\tif !assert.CompareErrors(got, want) {\n\t\t\t\tt.Errorf(\"Did not get expected error. got %v; want %v\", got, want)\n\t\t\t}\n\t\t})\n\t\ttestCases := []struct {\n\t\t\tname string\n\t\t\tr    Raw\n\t\t\terr  error\n\t\t}{\n\t\t\t{\"null\", Raw{'\\x08', '\\x00', '\\x00', '\\x00', '\\x0A', 'x', '\\x00', '\\x00'}, nil},\n\t\t\t{\n\t\t\t\t\"subdocument\",\n\t\t\t\tRaw{\n\t\t\t\t\t'\\x15', '\\x00', '\\x00', '\\x00',\n\t\t\t\t\t'\\x03',\n\t\t\t\t\t'f', 'o', 'o', '\\x00',\n\t\t\t\t\t'\\x0B', '\\x00', '\\x00', '\\x00', '\\x0A', 'a', '\\x00',\n\t\t\t\t\t'\\x0A', 'b', '\\x00', '\\x00', '\\x00',\n\t\t\t\t},\n\t\t\t\tnil,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"array\",\n\t\t\t\tRaw{\n\t\t\t\t\t'\\x15', '\\x00', '\\x00', '\\x00',\n\t\t\t\t\t'\\x04',\n\t\t\t\t\t'f', 'o', 'o', '\\x00',\n\t\t\t\t\t'\\x0B', '\\x00', '\\x00', '\\x00', '\\x0A', '1', '\\x00',\n\t\t\t\t\t'\\x0A', '2', '\\x00', '\\x00', '\\x00',\n\t\t\t\t},\n\t\t\t\tnil,\n\t\t\t},\n\t\t}\n\n\t\tfor _, tc := range testCases {\n\t\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\t\terr := tc.r.Validate()\n\t\t\t\tif !errors.Is(err, tc.err) {\n\t\t\t\t\tt.Errorf(\"Returned error does not match. got %v; want %v\", err, tc.err)\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t})\n\tt.Run(\"Lookup\", func(t *testing.T) {\n\t\tt.Run(\"empty-key\", func(t *testing.T) {\n\t\t\trdr := Raw{'\\x05', '\\x00', '\\x00', '\\x00', '\\x00'}\n\t\t\t_, err := rdr.LookupErr()\n\t\t\tif !errors.Is(err, bsoncore.ErrEmptyKey) {\n\t\t\t\tt.Errorf(\"Empty key lookup did not return expected result. got %v; want %v\", err, bsoncore.ErrEmptyKey)\n\t\t\t}\n\t\t})\n\t\tt.Run(\"corrupted-subdocument\", func(t *testing.T) {\n\t\t\trdr := Raw{\n\t\t\t\t'\\x0D', '\\x00', '\\x00', '\\x00',\n\t\t\t\t'\\x03', 'x', '\\x00',\n\t\t\t\t'\\x06', '\\x00', '\\x00', '\\x00',\n\t\t\t\t'\\x01',\n\t\t\t\t'\\x00',\n\t\t\t\t'\\x00',\n\t\t\t}\n\t\t\t_, err := rdr.LookupErr(\"x\", \"y\")\n\t\t\twant := bsoncore.NewInsufficientBytesError(nil, nil)\n\t\t\tif !assert.CompareErrors(err, want) {\n\t\t\t\tt.Errorf(\"Empty key lookup did not return expected result. got %v; want %v\", err, want)\n\t\t\t}\n\t\t})\n\t\tt.Run(\"corrupted-array\", func(t *testing.T) {\n\t\t\trdr := Raw{\n\t\t\t\t'\\x0D', '\\x00', '\\x00', '\\x00',\n\t\t\t\t'\\x04', 'x', '\\x00',\n\t\t\t\t'\\x06', '\\x00', '\\x00', '\\x00',\n\t\t\t\t'\\x01',\n\t\t\t\t'\\x00',\n\t\t\t\t'\\x00',\n\t\t\t}\n\t\t\t_, err := rdr.LookupErr(\"x\", \"y\")\n\t\t\twant := bsoncore.NewInsufficientBytesError(nil, nil)\n\t\t\tif !assert.CompareErrors(err, want) {\n\t\t\t\tt.Errorf(\"Empty key lookup did not return expected result. got %v; want %v\", err, want)\n\t\t\t}\n\t\t})\n\t\tt.Run(\"invalid-traversal\", func(t *testing.T) {\n\t\t\trdr := Raw{'\\x08', '\\x00', '\\x00', '\\x00', '\\x0A', 'x', '\\x00', '\\x00'}\n\t\t\t_, err := rdr.LookupErr(\"x\", \"y\")\n\t\t\twant := bsoncore.InvalidDepthTraversalError{Key: \"x\", Type: bsoncore.TypeNull}\n\t\t\tif !assert.CompareErrors(err, want) {\n\t\t\t\tt.Errorf(\"Empty key lookup did not return expected result. got %v; want %v\", err, want)\n\t\t\t}\n\t\t})\n\t\ttestCases := []struct {\n\t\t\tname string\n\t\t\tr    Raw\n\t\t\tkey  []string\n\t\t\twant RawValue\n\t\t\terr  error\n\t\t}{\n\t\t\t{\n\t\t\t\t\"first\",\n\t\t\t\tRaw{\n\t\t\t\t\t'\\x08', '\\x00', '\\x00', '\\x00', '\\x0A', 'x', '\\x00', '\\x00',\n\t\t\t\t},\n\t\t\t\t[]string{\"x\"},\n\t\t\t\tRawValue{Type: TypeNull},\n\t\t\t\tnil,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"first-second\",\n\t\t\t\tRaw{\n\t\t\t\t\t'\\x15', '\\x00', '\\x00', '\\x00',\n\t\t\t\t\t'\\x03',\n\t\t\t\t\t'f', 'o', 'o', '\\x00',\n\t\t\t\t\t'\\x0B', '\\x00', '\\x00', '\\x00', '\\x0A', 'a', '\\x00',\n\t\t\t\t\t'\\x0A', 'b', '\\x00', '\\x00', '\\x00',\n\t\t\t\t},\n\t\t\t\t[]string{\"foo\", \"b\"},\n\t\t\t\tRawValue{Type: TypeNull},\n\t\t\t\tnil,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"first-second-array\",\n\t\t\t\tRaw{\n\t\t\t\t\t'\\x15', '\\x00', '\\x00', '\\x00',\n\t\t\t\t\t'\\x04',\n\t\t\t\t\t'f', 'o', 'o', '\\x00',\n\t\t\t\t\t'\\x0B', '\\x00', '\\x00', '\\x00', '\\x0A', '1', '\\x00',\n\t\t\t\t\t'\\x0A', '2', '\\x00', '\\x00', '\\x00',\n\t\t\t\t},\n\t\t\t\t[]string{\"foo\", \"2\"},\n\t\t\t\tRawValue{Type: TypeNull},\n\t\t\t\tnil,\n\t\t\t},\n\t\t}\n\n\t\tfor _, tc := range testCases {\n\t\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\t\tgot, err := tc.r.LookupErr(tc.key...)\n\t\t\t\tif !errors.Is(err, tc.err) {\n\t\t\t\t\tt.Errorf(\"Returned error does not match. got %v; want %v\", err, tc.err)\n\t\t\t\t}\n\t\t\t\tif !cmp.Equal(got, tc.want) {\n\t\t\t\t\tt.Errorf(\"Returned element does not match expected element. got %v; want %v\", got, tc.want)\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t})\n\tt.Run(\"ElementAt\", func(t *testing.T) {\n\t\tt.Run(\"Out of bounds\", func(t *testing.T) {\n\t\t\trdr := Raw{0xe, 0x0, 0x0, 0x0, 0xa, 0x78, 0x0, 0xa, 0x79, 0x0, 0xa, 0x7a, 0x0, 0x0}\n\t\t\t_, err := rdr.IndexErr(3)\n\t\t\tif !errors.Is(err, bsoncore.ErrOutOfBounds) {\n\t\t\t\tt.Errorf(\"Out of bounds should be returned when accessing element beyond end of document. got %v; want %v\", err, bsoncore.ErrOutOfBounds)\n\t\t\t}\n\t\t})\n\t\tt.Run(\"Validation Error\", func(t *testing.T) {\n\t\t\trdr := Raw{0x07, 0x00, 0x00, 0x00, 0x00}\n\t\t\t_, err := rdr.IndexErr(1)\n\t\t\twant := bsoncore.NewInsufficientBytesError(nil, nil)\n\t\t\tif !assert.CompareErrors(err, want) {\n\t\t\t\tt.Errorf(\"Did not receive expected error. got %v; want %v\", err, want)\n\t\t\t}\n\t\t})\n\t\ttestCases := []struct {\n\t\t\tname  string\n\t\t\trdr   Raw\n\t\t\tindex uint\n\t\t\twant  RawElement\n\t\t}{\n\t\t\t{\n\t\t\t\t\"first\",\n\t\t\t\tRaw{0xe, 0x0, 0x0, 0x0, 0xa, 0x78, 0x0, 0xa, 0x79, 0x0, 0xa, 0x7a, 0x0, 0x0},\n\t\t\t\t0, bsoncore.AppendNullElement(nil, \"x\"),\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"second\",\n\t\t\t\tRaw{0xe, 0x0, 0x0, 0x0, 0xa, 0x78, 0x0, 0xa, 0x79, 0x0, 0xa, 0x7a, 0x0, 0x0},\n\t\t\t\t1, bsoncore.AppendNullElement(nil, \"y\"),\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"third\",\n\t\t\t\tRaw{0xe, 0x0, 0x0, 0x0, 0xa, 0x78, 0x0, 0xa, 0x79, 0x0, 0xa, 0x7a, 0x0, 0x0},\n\t\t\t\t2, bsoncore.AppendNullElement(nil, \"z\"),\n\t\t\t},\n\t\t}\n\n\t\tfor _, tc := range testCases {\n\t\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\t\tgot, err := tc.rdr.IndexErr(tc.index)\n\t\t\t\tif err != nil {\n\t\t\t\t\tt.Errorf(\"Unexpected error from ElementAt: %s\", err)\n\t\t\t\t}\n\t\t\t\tif diff := cmp.Diff(got, tc.want); diff != \"\" {\n\t\t\t\t\tt.Errorf(\"Documents differ: (-got +want)\\n%s\", diff)\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t})\n\tt.Run(\"ReadDocument\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\ttestCases := []struct {\n\t\t\tname       string\n\t\t\tioReader   io.Reader\n\t\t\tbsonReader Raw\n\t\t\terr        error\n\t\t}{\n\t\t\t{\n\t\t\t\t\"nil reader\",\n\t\t\t\tnil,\n\t\t\t\tnil,\n\t\t\t\tErrNilReader,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"premature end of reader\",\n\t\t\t\tbytes.NewBuffer([]byte{}),\n\t\t\t\tnil,\n\t\t\t\tio.EOF,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"empty document\",\n\t\t\t\tbytes.NewBuffer([]byte{5, 0, 0, 0, 0}),\n\t\t\t\t[]byte{5, 0, 0, 0, 0},\n\t\t\t\tnil,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"non-empty document\",\n\t\t\t\tbytes.NewBuffer([]byte{\n\t\t\t\t\t// length\n\t\t\t\t\t0x17, 0x0, 0x0, 0x0,\n\n\t\t\t\t\t// type - string\n\t\t\t\t\t0x2,\n\t\t\t\t\t// key - \"foo\"\n\t\t\t\t\t0x66, 0x6f, 0x6f, 0x0,\n\t\t\t\t\t// value - string length\n\t\t\t\t\t0x4, 0x0, 0x0, 0x0,\n\t\t\t\t\t// value - string \"bar\"\n\t\t\t\t\t0x62, 0x61, 0x72, 0x0,\n\n\t\t\t\t\t// type - null\n\t\t\t\t\t0xa,\n\t\t\t\t\t// key - \"baz\"\n\t\t\t\t\t0x62, 0x61, 0x7a, 0x0,\n\n\t\t\t\t\t// null terminator\n\t\t\t\t\t0x0,\n\t\t\t\t}),\n\t\t\t\t[]byte{\n\t\t\t\t\t// length\n\t\t\t\t\t0x17, 0x0, 0x0, 0x0,\n\n\t\t\t\t\t// type - string\n\t\t\t\t\t0x2,\n\t\t\t\t\t// key - \"foo\"\n\t\t\t\t\t0x66, 0x6f, 0x6f, 0x0,\n\t\t\t\t\t// value - string length\n\t\t\t\t\t0x4, 0x0, 0x0, 0x0,\n\t\t\t\t\t// value - string \"bar\"\n\t\t\t\t\t0x62, 0x61, 0x72, 0x0,\n\n\t\t\t\t\t// type - null\n\t\t\t\t\t0xa,\n\t\t\t\t\t// key - \"baz\"\n\t\t\t\t\t0x62, 0x61, 0x7a, 0x0,\n\n\t\t\t\t\t// null terminator\n\t\t\t\t\t0x0,\n\t\t\t\t},\n\t\t\t\tnil,\n\t\t\t},\n\t\t}\n\n\t\tfor _, tc := range testCases {\n\t\t\ttc := tc // Capture range variable.\n\t\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\t\tt.Parallel()\n\n\t\t\t\treader, err := ReadDocument(tc.ioReader)\n\t\t\t\trequire.Equal(t, err, tc.err)\n\t\t\t\trequire.True(t, bytes.Equal(tc.bsonReader, reader))\n\t\t\t})\n\t\t}\n\t})\n}\n\nfunc BenchmarkRawString(b *testing.B) {\n\t// Create 1KiB and 128B strings to exercise the string-heavy call paths in\n\t// the \"Raw.String\" method.\n\tvar buf strings.Builder\n\tfor i := 0; i < 16000000; i++ {\n\t\tbuf.WriteString(\"abcdefgh\")\n\t}\n\tstr1k := buf.String()\n\tstr128 := str1k[:128]\n\n\tcases := []struct {\n\t\tdescription string\n\t\tvalue       any\n\t}{\n\t\t{\n\t\t\tdescription: \"string\",\n\t\t\tvalue:       D{{Key: \"key\", Value: str128}},\n\t\t},\n\t\t{\n\t\t\tdescription: \"integer\",\n\t\t\tvalue:       D{{Key: \"key\", Value: int64(1234567890)}},\n\t\t},\n\t\t{\n\t\t\tdescription: \"float\",\n\t\t\tvalue:       D{{Key: \"key\", Value: float64(1234567890.123456789)}},\n\t\t},\n\t\t{\n\t\t\tdescription: \"nested document\",\n\t\t\tvalue: D{{\n\t\t\t\tKey: \"key\",\n\t\t\t\tValue: D{{\n\t\t\t\t\tKey: \"key\",\n\t\t\t\t\tValue: D{{\n\t\t\t\t\t\tKey:   \"key\",\n\t\t\t\t\t\tValue: str128,\n\t\t\t\t\t}},\n\t\t\t\t}},\n\t\t\t}},\n\t\t},\n\t\t{\n\t\t\tdescription: \"array of strings\",\n\t\t\tvalue: D{{\n\t\t\t\tKey:   \"key\",\n\t\t\t\tValue: []string{str128, str128, str128, str128},\n\t\t\t}},\n\t\t},\n\t\t{\n\t\t\tdescription: \"mixed struct\",\n\t\t\tvalue: struct {\n\t\t\t\tKey1 struct {\n\t\t\t\t\tNested string\n\t\t\t\t}\n\t\t\t\tKey2 string\n\t\t\t\tKey3 []string\n\t\t\t\tKey4 float64\n\t\t\t}{\n\t\t\t\tKey1: struct{ Nested string }{Nested: str1k},\n\t\t\t\tKey2: str1k,\n\t\t\t\tKey3: []string{str1k, str1k, str1k, str1k},\n\t\t\t\tKey4: 1234567890.123456789,\n\t\t\t},\n\t\t},\n\t\t// Very voluminous document with hundreds of thousands of keys\n\t\t{\n\t\t\tdescription: \"very_voluminous_document\",\n\t\t\tvalue:       createVoluminousDocument(100000),\n\t\t},\n\t\t// Document containing large strings and values\n\t\t{\n\t\t\tdescription: \"large_strings_and_values\",\n\t\t\tvalue:       createLargeStringsDocument(10),\n\t\t},\n\t\t// Document with massive arrays that are large\n\t\t{\n\t\t\tdescription: \"massive_arrays\",\n\t\t\tvalue:       createMassiveArraysDocument(100000),\n\t\t},\n\t}\n\n\tfor _, tc := range cases {\n\t\tb.Run(tc.description, func(b *testing.B) {\n\t\t\tbs, err := Marshal(tc.value)\n\t\t\trequire.NoError(b, err)\n\n\t\t\tb.ReportAllocs()\n\t\t\tb.ResetTimer()\n\t\t\tfor i := 0; i < b.N; i++ {\n\t\t\t\t_ = Raw(bs).String()\n\t\t\t}\n\t\t})\n\n\t\tb.Run(tc.description+\"_StringN\", func(b *testing.B) {\n\t\t\tbs, err := Marshal(tc.value)\n\t\t\trequire.NoError(b, err)\n\n\t\t\tb.ReportAllocs()\n\t\t\tb.ResetTimer()\n\t\t\tfor i := 0; i < b.N; i++ {\n\t\t\t\t_, _ = bsoncore.Document(bs).StringN(1024) // Assuming you want to limit to 1024 bytes for this benchmark\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestComplexDocuments_StringN(t *testing.T) {\n\ttestCases := []struct {\n\t\tdescription string\n\t\tn           int\n\t\tdoc         any\n\t}{\n\t\t{\"n>0, massive array documents\", 1000, createMassiveArraysDocument(1000)},\n\t\t{\"n>0, voluminous document with unique values\", 1000, createUniqueVoluminousDocument(t, 1000)},\n\t\t{\"n>0, large single document\", 1000, createLargeSingleDoc(t)},\n\t\t{\"n>0, voluminous document with arrays containing documents\", 1000, createVoluminousArrayDocuments(t, 1000)},\n\t}\n\n\tfor _, tc := range testCases {\n\t\tt.Run(tc.description, func(t *testing.T) {\n\t\t\tbson, _ := Marshal(tc.doc)\n\t\t\tbsonDoc := bsoncore.Document(bson)\n\n\t\t\tgot, _ := bsonDoc.StringN(tc.n)\n\t\t\tassert.Equal(t, tc.n, len(got))\n\t\t})\n\t}\n}\n\n// createVoluminousDocument generates a document with a specified number of keys, simulating a very large document in terms of the number of keys.\nfunc createVoluminousDocument(numKeys int) D {\n\td := make(D, numKeys)\n\tfor i := 0; i < numKeys; i++ {\n\t\td = append(d, E{Key: fmt.Sprintf(\"key%d\", i), Value: \"value\"})\n\t}\n\treturn d\n}\n\n// createLargeStringsDocument generates a document with large string values, simulating a document with very large data values.\nfunc createLargeStringsDocument(sizeMB int) D {\n\tlargeString := strings.Repeat(\"a\", sizeMB*1024*1024)\n\treturn D{\n\t\t{Key: \"largeString1\", Value: largeString},\n\t\t{Key: \"largeString2\", Value: largeString},\n\t\t{Key: \"largeString3\", Value: largeString},\n\t\t{Key: \"largeString4\", Value: largeString},\n\t}\n}\n\n// createMassiveArraysDocument generates a document with massive arrays, simulating a document that contains large arrays of data.\nfunc createMassiveArraysDocument(arraySize int) D {\n\tmassiveArray := make([]string, arraySize)\n\tfor i := 0; i < arraySize; i++ {\n\t\tmassiveArray[i] = \"value\"\n\t}\n\n\treturn D{\n\t\t{Key: \"massiveArray1\", Value: massiveArray},\n\t\t{Key: \"massiveArray2\", Value: massiveArray},\n\t\t{Key: \"massiveArray3\", Value: massiveArray},\n\t\t{Key: \"massiveArray4\", Value: massiveArray},\n\t}\n}\n\n// createUniqueVoluminousDocument creates a BSON document with multiple key value pairs and unique value types.\nfunc createUniqueVoluminousDocument(t *testing.T, size int) bsoncore.Document {\n\tt.Helper()\n\n\tdocs := make(D, 0, size)\n\n\tfor i := 0; i < size; i++ {\n\t\tdocs = append(docs, E{\n\t\t\tKey: \"x\", Value: NewObjectID(),\n\t\t})\n\t\tdocs = append(docs, E{\n\t\t\tKey: \"z\", Value: \"y\",\n\t\t})\n\t}\n\n\tbsonData, err := Marshal(docs)\n\tassert.NoError(t, err)\n\n\treturn bsoncore.Document(bsonData)\n}\n\n// createLargeSingleDoc creates a large single BSON document.\nfunc createLargeSingleDoc(t *testing.T) bsoncore.Document {\n\tt.Helper()\n\n\tvar b strings.Builder\n\tb.Grow(1048577)\n\n\tfor i := 0; i < 1048577; i++ {\n\t\tb.WriteByte(0)\n\t}\n\ts := b.String()\n\n\tdoc := D{\n\t\t{Key: \"x\", Value: s},\n\t}\n\n\tbsonData, err := Marshal(doc)\n\tassert.NoError(t, err)\n\n\treturn bsoncore.Document(bsonData)\n}\n\n// createVoluminousArrayDocuments creates a volumninous BSON document with arrays containing documents.\nfunc createVoluminousArrayDocuments(t *testing.T, size int) bsoncore.Document {\n\tt.Helper()\n\n\tdocs := make(D, 0, size)\n\n\tfor i := 0; i < size; i++ {\n\t\tdocs = append(docs, E{\n\t\t\tKey: \"x\", Value: NewObjectID(),\n\t\t})\n\t\tdocs = append(docs, E{\n\t\t\tKey: \"z\", Value: A{D{{Key: \"x\", Value: \"y\"}}},\n\t\t})\n\t}\n\n\tbsonData, err := Marshal(docs)\n\tassert.NoError(t, err)\n\n\treturn bsoncore.Document(bsonData)\n}\n"
  },
  {
    "path": "bson/raw_value.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bson\n\nimport (\n\t\"bytes\"\n\t\"errors\"\n\t\"fmt\"\n\t\"reflect\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n)\n\n// ErrNilContext is returned when the provided DecodeContext is nil.\nvar ErrNilContext = errors.New(\"DecodeContext cannot be nil\")\n\n// ErrNilRegistry is returned when the provided registry is nil.\nvar ErrNilRegistry = errors.New(\"Registry cannot be nil\")\n\n// RawValue is a raw encoded BSON value. It can be used to delay BSON value decoding or precompute\n// BSON encoded value. Type is the BSON type of the value and Value is the raw encoded BSON value.\n//\n// A RawValue must be an individual BSON value. Use the Raw type for full BSON documents.\ntype RawValue struct {\n\tType  Type\n\tValue []byte\n\n\tr *Registry\n}\n\n// IsZero reports whether the RawValue is zero, i.e. no data is present on\n// the RawValue. It returns true if Type is 0 and Value is empty or nil.\nfunc (rv RawValue) IsZero() bool {\n\treturn rv.Type == 0x00 && len(rv.Value) == 0\n}\n\n// Unmarshal deserializes BSON into the provided val. If RawValue cannot be unmarshaled into val, an\n// error is returned. This method will use the registry used to create the RawValue, if the RawValue\n// was created from partial BSON processing, or it will use the default registry. Users wishing to\n// specify the registry to use should use UnmarshalWithRegistry.\nfunc (rv RawValue) Unmarshal(val any) error {\n\treg := rv.r\n\tif reg == nil {\n\t\treg = defaultRegistry\n\t}\n\treturn rv.UnmarshalWithRegistry(reg, val)\n}\n\n// Equal compares rv and rv2 and returns true if they are equal.\nfunc (rv RawValue) Equal(rv2 RawValue) bool {\n\tif rv.Type != rv2.Type {\n\t\treturn false\n\t}\n\n\tif !bytes.Equal(rv.Value, rv2.Value) {\n\t\treturn false\n\t}\n\n\treturn true\n}\n\n// UnmarshalWithRegistry performs the same unmarshalling as Unmarshal but uses the provided registry\n// instead of the one attached or the default registry.\nfunc (rv RawValue) UnmarshalWithRegistry(r *Registry, val any) error {\n\tif r == nil {\n\t\treturn ErrNilRegistry\n\t}\n\n\tvr := newBufferedValueReader(rv.Type, rv.Value)\n\trval := reflect.ValueOf(val)\n\tif rval.Kind() != reflect.Ptr {\n\t\treturn fmt.Errorf(\"argument to Unmarshal* must be a pointer to a type, but got %v\", rval)\n\t}\n\trval = rval.Elem()\n\tdec, err := r.LookupDecoder(rval.Type())\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn dec.DecodeValue(DecodeContext{Registry: r}, vr, rval)\n}\n\n// UnmarshalWithContext performs the same unmarshalling as Unmarshal but uses the provided DecodeContext\n// instead of the one attached or the default registry.\nfunc (rv RawValue) UnmarshalWithContext(dc *DecodeContext, val any) error {\n\tif dc == nil {\n\t\treturn ErrNilContext\n\t}\n\n\tvr := newBufferedValueReader(rv.Type, rv.Value)\n\trval := reflect.ValueOf(val)\n\tif rval.Kind() != reflect.Ptr {\n\t\treturn fmt.Errorf(\"argument to Unmarshal* must be a pointer to a type, but got %v\", rval)\n\t}\n\trval = rval.Elem()\n\tdec, err := dc.LookupDecoder(rval.Type())\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn dec.DecodeValue(*dc, vr, rval)\n}\n\nfunc convertFromCoreValue(v bsoncore.Value) RawValue {\n\treturn RawValue{Type: Type(v.Type), Value: v.Data}\n}\n\nfunc convertToCoreValue(v RawValue) bsoncore.Value {\n\treturn bsoncore.Value{Type: bsoncore.Type(v.Type), Data: v.Value}\n}\n\n// Validate ensures the value is a valid BSON value.\nfunc (rv RawValue) Validate() error { return convertToCoreValue(rv).Validate() }\n\n// IsNumber returns true if the type of v is a numeric BSON type.\nfunc (rv RawValue) IsNumber() bool { return convertToCoreValue(rv).IsNumber() }\n\n// String implements the fmt.String interface. This method will return values in extended JSON\n// format. If the value is not valid, this returns an empty string\nfunc (rv RawValue) String() string { return convertToCoreValue(rv).String() }\n\n// DebugString outputs a human readable version of Document. It will attempt to stringify the\n// valid components of the document even if the entire document is not valid.\nfunc (rv RawValue) DebugString() string { return convertToCoreValue(rv).DebugString() }\n\n// Double returns the float64 value for this element.\n// It panics if e's BSON type is not bson.TypeDouble.\nfunc (rv RawValue) Double() float64 { return convertToCoreValue(rv).Double() }\n\n// DoubleOK is the same as Double, but returns a boolean instead of panicking.\nfunc (rv RawValue) DoubleOK() (float64, bool) { return convertToCoreValue(rv).DoubleOK() }\n\n// StringValue returns the string value for this element.\n// It panics if e's BSON type is not bson.TypeString.\n//\n// NOTE: This method is called StringValue to avoid a collision with the String method which\n// implements the fmt.Stringer interface.\nfunc (rv RawValue) StringValue() string { return convertToCoreValue(rv).StringValue() }\n\n// StringValueOK is the same as StringValue, but returns a boolean instead of\n// panicking.\nfunc (rv RawValue) StringValueOK() (string, bool) { return convertToCoreValue(rv).StringValueOK() }\n\n// Document returns the BSON document the Value represents as a Document. It panics if the\n// value is a BSON type other than document.\nfunc (rv RawValue) Document() Raw { return Raw(convertToCoreValue(rv).Document()) }\n\n// DocumentOK is the same as Document, except it returns a boolean\n// instead of panicking.\nfunc (rv RawValue) DocumentOK() (Raw, bool) {\n\tdoc, ok := convertToCoreValue(rv).DocumentOK()\n\treturn Raw(doc), ok\n}\n\n// Array returns the BSON array the Value represents as an Array. It panics if the\n// value is a BSON type other than array.\nfunc (rv RawValue) Array() RawArray { return RawArray(convertToCoreValue(rv).Array()) }\n\n// ArrayOK is the same as Array, except it returns a boolean instead\n// of panicking.\nfunc (rv RawValue) ArrayOK() (RawArray, bool) {\n\tdoc, ok := convertToCoreValue(rv).ArrayOK()\n\n\treturn RawArray(doc), ok\n}\n\n// Binary returns the BSON binary value the Value represents. It panics if the value is a BSON type\n// other than binary.\nfunc (rv RawValue) Binary() (subtype byte, data []byte) { return convertToCoreValue(rv).Binary() }\n\n// BinaryOK is the same as Binary, except it returns a boolean instead of\n// panicking.\nfunc (rv RawValue) BinaryOK() (subtype byte, data []byte, ok bool) {\n\treturn convertToCoreValue(rv).BinaryOK()\n}\n\n// ObjectID returns the BSON objectid value the Value represents. It panics if the value is a BSON\n// type other than objectid.\nfunc (rv RawValue) ObjectID() ObjectID { return convertToCoreValue(rv).ObjectID() }\n\n// ObjectIDOK is the same as ObjectID, except it returns a boolean instead of\n// panicking.\nfunc (rv RawValue) ObjectIDOK() (ObjectID, bool) {\n\treturn convertToCoreValue(rv).ObjectIDOK()\n}\n\n// Boolean returns the boolean value the Value represents. It panics if the\n// value is a BSON type other than boolean.\nfunc (rv RawValue) Boolean() bool { return convertToCoreValue(rv).Boolean() }\n\n// BooleanOK is the same as Boolean, except it returns a boolean instead of\n// panicking.\nfunc (rv RawValue) BooleanOK() (bool, bool) { return convertToCoreValue(rv).BooleanOK() }\n\n// DateTime returns the BSON datetime value the Value represents as a\n// unix timestamp. It panics if the value is a BSON type other than datetime.\nfunc (rv RawValue) DateTime() int64 { return convertToCoreValue(rv).DateTime() }\n\n// DateTimeOK is the same as DateTime, except it returns a boolean instead of\n// panicking.\nfunc (rv RawValue) DateTimeOK() (int64, bool) { return convertToCoreValue(rv).DateTimeOK() }\n\n// Time returns the BSON datetime value the Value represents. It panics if the value is a BSON\n// type other than datetime.\nfunc (rv RawValue) Time() time.Time { return convertToCoreValue(rv).Time() }\n\n// TimeOK is the same as Time, except it returns a boolean instead of\n// panicking.\nfunc (rv RawValue) TimeOK() (time.Time, bool) { return convertToCoreValue(rv).TimeOK() }\n\n// Regex returns the BSON regex value the Value represents. It panics if the value is a BSON\n// type other than regex.\nfunc (rv RawValue) Regex() (pattern, options string) { return convertToCoreValue(rv).Regex() }\n\n// RegexOK is the same as Regex, except it returns a boolean instead of\n// panicking.\nfunc (rv RawValue) RegexOK() (pattern, options string, ok bool) {\n\treturn convertToCoreValue(rv).RegexOK()\n}\n\n// DBPointer returns the BSON dbpointer value the Value represents. It panics if the value is a BSON\n// type other than DBPointer.\nfunc (rv RawValue) DBPointer() (string, ObjectID) {\n\treturn convertToCoreValue(rv).DBPointer()\n}\n\n// DBPointerOK is the same as DBPoitner, except that it returns a boolean\n// instead of panicking.\nfunc (rv RawValue) DBPointerOK() (string, ObjectID, bool) {\n\treturn convertToCoreValue(rv).DBPointerOK()\n}\n\n// JavaScript returns the BSON JavaScript code value the Value represents. It panics if the value is\n// a BSON type other than JavaScript code.\nfunc (rv RawValue) JavaScript() string { return convertToCoreValue(rv).JavaScript() }\n\n// JavaScriptOK is the same as Javascript, excepti that it returns a boolean\n// instead of panicking.\nfunc (rv RawValue) JavaScriptOK() (string, bool) { return convertToCoreValue(rv).JavaScriptOK() }\n\n// Symbol returns the BSON symbol value the Value represents. It panics if the value is a BSON\n// type other than symbol.\nfunc (rv RawValue) Symbol() string { return convertToCoreValue(rv).Symbol() }\n\n// SymbolOK is the same as Symbol, excepti that it returns a boolean\n// instead of panicking.\nfunc (rv RawValue) SymbolOK() (string, bool) { return convertToCoreValue(rv).SymbolOK() }\n\n// CodeWithScope returns the BSON JavaScript code with scope the Value represents.\n// It panics if the value is a BSON type other than JavaScript code with scope.\nfunc (rv RawValue) CodeWithScope() (string, Raw) {\n\tcode, scope := convertToCoreValue(rv).CodeWithScope()\n\treturn code, Raw(scope)\n}\n\n// CodeWithScopeOK is the same as CodeWithScope, except that it returns a boolean instead of\n// panicking.\nfunc (rv RawValue) CodeWithScopeOK() (string, Raw, bool) {\n\tcode, scope, ok := convertToCoreValue(rv).CodeWithScopeOK()\n\treturn code, Raw(scope), ok\n}\n\n// Int32 returns the int32 the Value represents. It panics if the value is a BSON type other than\n// int32.\nfunc (rv RawValue) Int32() int32 { return convertToCoreValue(rv).Int32() }\n\n// Int32OK is the same as Int32, except that it returns a boolean instead of\n// panicking.\nfunc (rv RawValue) Int32OK() (int32, bool) { return convertToCoreValue(rv).Int32OK() }\n\n// Timestamp returns the BSON timestamp value the Value represents. It panics if the value is a\n// BSON type other than timestamp.\nfunc (rv RawValue) Timestamp() (t, i uint32) { return convertToCoreValue(rv).Timestamp() }\n\n// TimestampOK is the same as Timestamp, except that it returns a boolean\n// instead of panicking.\nfunc (rv RawValue) TimestampOK() (t, i uint32, ok bool) { return convertToCoreValue(rv).TimestampOK() }\n\n// Int64 returns the int64 the Value represents. It panics if the value is a BSON type other than\n// int64.\nfunc (rv RawValue) Int64() int64 { return convertToCoreValue(rv).Int64() }\n\n// Int64OK is the same as Int64, except that it returns a boolean instead of\n// panicking.\nfunc (rv RawValue) Int64OK() (int64, bool) { return convertToCoreValue(rv).Int64OK() }\n\n// AsInt64 returns a BSON number as an int64. If the BSON type is not a numeric one, this method\n// will panic.\nfunc (rv RawValue) AsInt64() int64 { return convertToCoreValue(rv).AsInt64() }\n\n// AsInt64OK is the same as AsInt64, except that it returns a boolean instead of\n// panicking.\nfunc (rv RawValue) AsInt64OK() (int64, bool) { return convertToCoreValue(rv).AsInt64OK() }\n\n// AsFloat64 returns a BSON number as a float64. If the BSON type is not a numeric one, this method\n// will panic.\nfunc (rv RawValue) AsFloat64() float64 { return convertToCoreValue(rv).AsFloat64() }\n\n// AsFloat64OK is the same as AsFloat64, except that it returns a boolean instead of\n// panicking.\nfunc (rv RawValue) AsFloat64OK() (float64, bool) { return convertToCoreValue(rv).AsFloat64OK() }\n\n// Decimal128 returns the decimal the Value represents. It panics if the value is a BSON type other than\n// decimal.\nfunc (rv RawValue) Decimal128() Decimal128 { return NewDecimal128(convertToCoreValue(rv).Decimal128()) }\n\n// Decimal128OK is the same as Decimal128, except that it returns a boolean\n// instead of panicking.\nfunc (rv RawValue) Decimal128OK() (Decimal128, bool) {\n\th, l, ok := convertToCoreValue(rv).Decimal128OK()\n\treturn NewDecimal128(h, l), ok\n}\n"
  },
  {
    "path": "bson/raw_value_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bson\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"reflect\"\n\t\"testing\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n)\n\nfunc TestRawValue(t *testing.T) {\n\tt.Parallel()\n\n\tt.Run(\"Unmarshal\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tt.Run(\"Uses registry attached to value\", func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\treg := newTestRegistry()\n\t\t\tval := RawValue{Type: TypeString, Value: bsoncore.AppendString(nil, \"foobar\"), r: reg}\n\t\t\tvar s string\n\t\t\twant := errNoDecoder{Type: reflect.TypeOf(s)}\n\t\t\tgot := val.Unmarshal(&s)\n\t\t\tif !assert.CompareErrors(got, want) {\n\t\t\t\tt.Errorf(\"Expected errors to match. got %v; want %v\", got, want)\n\t\t\t}\n\t\t})\n\t\tt.Run(\"Uses default registry if no registry attached\", func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\twant := \"foobar\"\n\t\t\tval := RawValue{Type: TypeString, Value: bsoncore.AppendString(nil, want)}\n\t\t\tvar got string\n\t\t\terr := val.Unmarshal(&got)\n\t\t\tnoerr(t, err)\n\t\t\tif got != want {\n\t\t\t\tt.Errorf(\"Expected strings to match. got %s; want %s\", got, want)\n\t\t\t}\n\t\t})\n\t})\n\tt.Run(\"UnmarshalWithRegistry\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tt.Run(\"Returns error when registry is nil\", func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\twant := ErrNilRegistry\n\t\t\tvar val RawValue\n\t\t\tgot := val.UnmarshalWithRegistry(nil, &D{})\n\t\t\tif !errors.Is(got, want) {\n\t\t\t\tt.Errorf(\"Expected errors to match. got %v; want %v\", got, want)\n\t\t\t}\n\t\t})\n\t\tt.Run(\"Returns lookup error\", func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\treg := newTestRegistry()\n\t\t\tvar val RawValue\n\t\t\tvar s string\n\t\t\twant := errNoDecoder{Type: reflect.TypeOf(s)}\n\t\t\tgot := val.UnmarshalWithRegistry(reg, &s)\n\t\t\tif !assert.CompareErrors(got, want) {\n\t\t\t\tt.Errorf(\"Expected errors to match. got %v; want %v\", got, want)\n\t\t\t}\n\t\t})\n\t\tt.Run(\"Returns DecodeValue error\", func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\treg := NewRegistry()\n\t\t\tval := RawValue{Type: TypeDouble, Value: bsoncore.AppendDouble(nil, 3.14159)}\n\t\t\tvar s string\n\t\t\twant := fmt.Errorf(\"cannot decode %v into a string type\", TypeDouble)\n\t\t\tgot := val.UnmarshalWithRegistry(reg, &s)\n\t\t\tif !assert.CompareErrors(got, want) {\n\t\t\t\tt.Errorf(\"Expected errors to match. got %v; want %v\", got, want)\n\t\t\t}\n\t\t})\n\t\tt.Run(\"Success\", func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\treg := NewRegistry()\n\t\t\twant := float64(3.14159)\n\t\t\tval := RawValue{Type: TypeDouble, Value: bsoncore.AppendDouble(nil, want)}\n\t\t\tvar got float64\n\t\t\terr := val.UnmarshalWithRegistry(reg, &got)\n\t\t\tnoerr(t, err)\n\t\t\tif got != want {\n\t\t\t\tt.Errorf(\"Expected results to match. got %g; want %g\", got, want)\n\t\t\t}\n\t\t})\n\t})\n\tt.Run(\"UnmarshalWithContext\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tt.Run(\"Returns error when DecodeContext is nil\", func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\twant := ErrNilContext\n\t\t\tvar val RawValue\n\t\t\tgot := val.UnmarshalWithContext(nil, &D{})\n\t\t\tif !errors.Is(got, want) {\n\t\t\t\tt.Errorf(\"Expected errors to match. got %v; want %v\", got, want)\n\t\t\t}\n\t\t})\n\t\tt.Run(\"Returns lookup error\", func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tdc := DecodeContext{Registry: newTestRegistry()}\n\t\t\tvar val RawValue\n\t\t\tvar s string\n\t\t\twant := errNoDecoder{Type: reflect.TypeOf(s)}\n\t\t\tgot := val.UnmarshalWithContext(&dc, &s)\n\t\t\tif !assert.CompareErrors(got, want) {\n\t\t\t\tt.Errorf(\"Expected errors to match. got %v; want %v\", got, want)\n\t\t\t}\n\t\t})\n\t\tt.Run(\"Returns DecodeValue error\", func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tdc := DecodeContext{Registry: NewRegistry()}\n\t\t\tval := RawValue{Type: TypeDouble, Value: bsoncore.AppendDouble(nil, 3.14159)}\n\t\t\tvar s string\n\t\t\twant := fmt.Errorf(\"cannot decode %v into a string type\", TypeDouble)\n\t\t\tgot := val.UnmarshalWithContext(&dc, &s)\n\t\t\tif !assert.CompareErrors(got, want) {\n\t\t\t\tt.Errorf(\"Expected errors to match. got %v; want %v\", got, want)\n\t\t\t}\n\t\t})\n\t\tt.Run(\"Success\", func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tdc := DecodeContext{Registry: NewRegistry()}\n\t\t\twant := float64(3.14159)\n\t\t\tval := RawValue{Type: TypeDouble, Value: bsoncore.AppendDouble(nil, want)}\n\t\t\tvar got float64\n\t\t\terr := val.UnmarshalWithContext(&dc, &got)\n\t\t\tnoerr(t, err)\n\t\t\tif got != want {\n\t\t\t\tt.Errorf(\"Expected results to match. got %g; want %g\", got, want)\n\t\t\t}\n\t\t})\n\t})\n\n\tt.Run(\"IsZero\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\ttests := []struct {\n\t\t\tname string\n\t\t\tval  RawValue\n\t\t\twant bool\n\t\t}{\n\t\t\t{\n\t\t\t\tname: \"empty\",\n\t\t\t\tval:  RawValue{},\n\t\t\t\twant: true,\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"zero type but non-zero value\",\n\t\t\t\tval: RawValue{\n\t\t\t\t\tType:  0x00,\n\t\t\t\t\tValue: bsoncore.AppendInt32(nil, 0),\n\t\t\t\t},\n\t\t\t\twant: false,\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"zero type and zero value\",\n\t\t\t\tval: RawValue{\n\t\t\t\t\tType:  0x00,\n\t\t\t\t\tValue: bsoncore.AppendInt32(nil, 0),\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"non-zero type and non-zero value\",\n\t\t\t\tval: RawValue{\n\t\t\t\t\tType:  TypeString,\n\t\t\t\t\tValue: bsoncore.AppendString(nil, \"foobar\"),\n\t\t\t\t},\n\t\t\t\twant: false,\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"non-zero type and zero value\",\n\t\t\t\tval: RawValue{\n\t\t\t\t\tType:  TypeString,\n\t\t\t\t\tValue: bsoncore.AppendString(nil, \"foobar\"),\n\t\t\t\t},\n\t\t\t},\n\t\t}\n\n\t\tfor _, tt := range tests {\n\t\t\ttt := tt // Capture the range variable\n\t\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\t\tt.Parallel()\n\n\t\t\t\tassert.Equal(t, tt.want, tt.val.IsZero())\n\t\t\t})\n\t\t}\n\t})\n}\n"
  },
  {
    "path": "bson/reader.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bson\n\n// ArrayReader is implemented by types that allow reading values from a BSON\n// array.\ntype ArrayReader interface {\n\tReadValue() (ValueReader, error)\n}\n\n// DocumentReader is implemented by types that allow reading elements from a\n// BSON document.\ntype DocumentReader interface {\n\tReadElement() (string, ValueReader, error)\n}\n\n// ValueReader is a generic interface used to read values from BSON. This type\n// is implemented by several types with different underlying representations of\n// BSON, such as a bson.Document, raw BSON bytes, or extended JSON.\ntype ValueReader interface {\n\tType() Type\n\tSkip() error\n\n\tReadArray() (ArrayReader, error)\n\tReadBinary() (b []byte, btype byte, err error)\n\tReadBoolean() (bool, error)\n\tReadDocument() (DocumentReader, error)\n\tReadCodeWithScope() (code string, dr DocumentReader, err error)\n\tReadDBPointer() (ns string, oid ObjectID, err error)\n\tReadDateTime() (int64, error)\n\tReadDecimal128() (Decimal128, error)\n\tReadDouble() (float64, error)\n\tReadInt32() (int32, error)\n\tReadInt64() (int64, error)\n\tReadJavascript() (code string, err error)\n\tReadMaxKey() error\n\tReadMinKey() error\n\tReadNull() error\n\tReadObjectID() (ObjectID, error)\n\tReadRegex() (pattern, options string, err error)\n\tReadString() (string, error)\n\tReadSymbol() (symbol string, err error)\n\tReadTimestamp() (t, i uint32, err error)\n\tReadUndefined() error\n}\n"
  },
  {
    "path": "bson/registry.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bson\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"reflect\"\n\t\"sync\"\n)\n\n// defaultRegistry is the default Registry. It contains the default codecs and the\n// primitive codecs.\nvar defaultRegistry = NewRegistry()\n\n// errNoEncoder is returned when there wasn't an encoder available for a type.\ntype errNoEncoder struct {\n\tType reflect.Type\n}\n\nfunc (ene errNoEncoder) Error() string {\n\tif ene.Type == nil {\n\t\treturn \"no encoder found for <nil>\"\n\t}\n\treturn \"no encoder found for \" + ene.Type.String()\n}\n\n// errNoDecoder is returned when there wasn't a decoder available for a type.\ntype errNoDecoder struct {\n\tType reflect.Type\n}\n\nfunc (end errNoDecoder) Error() string {\n\treturn \"no decoder found for \" + end.Type.String()\n}\n\n// errNoTypeMapEntry is returned when there wasn't a type available for the provided BSON type.\ntype errNoTypeMapEntry struct {\n\tType Type\n}\n\nfunc (entme errNoTypeMapEntry) Error() string {\n\treturn \"no type map entry found for \" + entme.Type.String()\n}\n\n// A Registry is a store for ValueEncoders, ValueDecoders, and a type map. See the Registry type\n// documentation for examples of registering various custom encoders and decoders. A Registry can\n// have four main types of codecs:\n//\n// 1. Type encoders/decoders - These can be registered using the RegisterTypeEncoder and\n// RegisterTypeDecoder methods. The registered codec will be invoked when encoding/decoding a value\n// whose type matches the registered type exactly.\n// If the registered type is an interface, the codec will be invoked when encoding or decoding\n// values whose type is the interface, but not for values with concrete types that implement the\n// interface.\n//\n// 2. Interface encoders/decoders - These can be registered using the RegisterInterfaceEncoder and\n// RegisterInterfaceDecoder methods. These methods only accept interface types and the registered codecs\n// will be invoked when encoding or decoding values whose types implement the interface. An example\n// of an interface defined by the driver is bson.Marshaler. The driver will call the MarshalBSON method\n// for any value whose type implements bson.Marshaler, regardless of the value's concrete type.\n//\n// 3. Type map entries - This can be used to associate a BSON type with a Go type. These type\n// associations are used when decoding into a bson.D/bson.M or a struct field of type any.\n// For example, by default, BSON int32 and int64 values decode as Go int32 and int64 instances,\n// respectively, when decoding into a bson.D. The following code would change the behavior so these\n// values decode as Go int instances instead:\n//\n//\tintType := reflect.TypeOf(int(0))\n//\tregistry.RegisterTypeMapEntry(bson.TypeInt32, intType).RegisterTypeMapEntry(bson.TypeInt64, intType)\n//\n// 4. Kind encoder/decoders - These can be registered using the RegisterDefaultEncoder and\n// RegisterDefaultDecoder methods. The registered codec will be invoked when encoding or decoding\n// values whose reflect.Kind matches the registered reflect.Kind as long as the value's type doesn't\n// match a registered type or interface encoder/decoder first. These methods should be used to change the\n// behavior for all values for a specific kind.\n//\n// Read [Registry.LookupDecoder] and [Registry.LookupEncoder] for Registry lookup procedure.\ntype Registry struct {\n\tinterfaceEncoders []interfaceValueEncoder\n\tinterfaceDecoders []interfaceValueDecoder\n\ttypeEncoders      *typeEncoderCache\n\ttypeDecoders      *typeDecoderCache\n\tkindEncoders      *kindEncoderCache\n\tkindDecoders      *kindDecoderCache\n\ttypeMap           sync.Map // map[Type]reflect.Type\n}\n\n// NewRegistry creates a new empty Registry.\nfunc NewRegistry() *Registry {\n\treg := &Registry{\n\t\ttypeEncoders: new(typeEncoderCache),\n\t\ttypeDecoders: new(typeDecoderCache),\n\t\tkindEncoders: new(kindEncoderCache),\n\t\tkindDecoders: new(kindDecoderCache),\n\t}\n\tregisterDefaultEncoders(reg)\n\tregisterDefaultDecoders(reg)\n\tregisterPrimitiveCodecs(reg)\n\treturn reg\n}\n\n// RegisterTypeEncoder registers the provided ValueEncoder for the provided type.\n//\n// The type will be used as provided, so an encoder can be registered for a type and a different\n// encoder can be registered for a pointer to that type.\n//\n// If the given type is an interface, the encoder will be called when marshaling a type that is\n// that interface. It will not be called when marshaling a non-interface type that implements the\n// interface. To get the latter behavior, call RegisterHookEncoder instead.\n//\n// RegisterTypeEncoder should not be called concurrently with any other Registry method.\nfunc (r *Registry) RegisterTypeEncoder(valueType reflect.Type, enc ValueEncoder) {\n\tr.typeEncoders.Store(valueType, enc)\n}\n\n// RegisterTypeDecoder registers the provided ValueDecoder for the provided type.\n//\n// The type will be used as provided, so a decoder can be registered for a type and a different\n// decoder can be registered for a pointer to that type.\n//\n// If the given type is an interface, the decoder will be called when unmarshaling into a type that\n// is that interface. It will not be called when unmarshaling into a non-interface type that\n// implements the interface. To get the latter behavior, call RegisterHookDecoder instead.\n//\n// RegisterTypeDecoder should not be called concurrently with any other Registry method.\nfunc (r *Registry) RegisterTypeDecoder(valueType reflect.Type, dec ValueDecoder) {\n\tr.typeDecoders.Store(valueType, dec)\n}\n\n// RegisterKindEncoder registers the provided ValueEncoder for the provided kind.\n//\n// Use RegisterKindEncoder to register an encoder for any type with the same underlying kind. For\n// example, consider the type MyInt defined as\n//\n//\ttype MyInt int32\n//\n// To define an encoder for MyInt and int32, use RegisterKindEncoder like\n//\n//\treg.RegisterKindEncoder(reflect.Int32, myEncoder)\n//\n// RegisterKindEncoder should not be called concurrently with any other Registry method.\nfunc (r *Registry) RegisterKindEncoder(kind reflect.Kind, enc ValueEncoder) {\n\tr.kindEncoders.Store(kind, enc)\n}\n\n// RegisterKindDecoder registers the provided ValueDecoder for the provided kind.\n//\n// Use RegisterKindDecoder to register a decoder for any type with the same underlying kind. For\n// example, consider the type MyInt defined as\n//\n//\ttype MyInt int32\n//\n// To define an decoder for MyInt and int32, use RegisterKindDecoder like\n//\n//\treg.RegisterKindDecoder(reflect.Int32, myDecoder)\n//\n// RegisterKindDecoder should not be called concurrently with any other Registry method.\nfunc (r *Registry) RegisterKindDecoder(kind reflect.Kind, dec ValueDecoder) {\n\tr.kindDecoders.Store(kind, dec)\n}\n\n// RegisterInterfaceEncoder registers an encoder for the provided interface type iface. This encoder will\n// be called when marshaling a type if the type implements iface or a pointer to the type\n// implements iface. If the provided type is not an interface\n// (i.e. iface.Kind() != reflect.Interface), this method will panic.\n//\n// RegisterInterfaceEncoder should not be called concurrently with any other Registry method.\nfunc (r *Registry) RegisterInterfaceEncoder(iface reflect.Type, enc ValueEncoder) {\n\tif iface.Kind() != reflect.Interface {\n\t\tpanicStr := fmt.Errorf(\"RegisterInterfaceEncoder expects a type with kind reflect.Interface, \"+\n\t\t\t\"got type %s with kind %s\", iface, iface.Kind())\n\t\tpanic(panicStr)\n\t}\n\n\tfor idx, encoder := range r.interfaceEncoders {\n\t\tif encoder.i == iface {\n\t\t\tr.interfaceEncoders[idx].ve = enc\n\t\t\treturn\n\t\t}\n\t}\n\n\tr.interfaceEncoders = append(r.interfaceEncoders, interfaceValueEncoder{i: iface, ve: enc})\n}\n\n// RegisterInterfaceDecoder registers an decoder for the provided interface type iface. This decoder will\n// be called when unmarshaling into a type if the type implements iface or a pointer to the type\n// implements iface. If the provided type is not an interface (i.e. iface.Kind() != reflect.Interface),\n// this method will panic.\n//\n// RegisterInterfaceDecoder should not be called concurrently with any other Registry method.\nfunc (r *Registry) RegisterInterfaceDecoder(iface reflect.Type, dec ValueDecoder) {\n\tif iface.Kind() != reflect.Interface {\n\t\tpanicStr := fmt.Errorf(\"RegisterInterfaceDecoder expects a type with kind reflect.Interface, \"+\n\t\t\t\"got type %s with kind %s\", iface, iface.Kind())\n\t\tpanic(panicStr)\n\t}\n\n\tfor idx, decoder := range r.interfaceDecoders {\n\t\tif decoder.i == iface {\n\t\t\tr.interfaceDecoders[idx].vd = dec\n\t\t\treturn\n\t\t}\n\t}\n\n\tr.interfaceDecoders = append(r.interfaceDecoders, interfaceValueDecoder{i: iface, vd: dec})\n}\n\n// RegisterTypeMapEntry will register the provided type to the BSON type. The primary usage for this\n// mapping is decoding situations where an empty interface is used and a default type needs to be\n// created and decoded into.\n//\n// By default, BSON documents will decode into any values as bson.D. To change the default type for BSON\n// documents, a type map entry for TypeEmbeddedDocument should be registered. For example, to force BSON documents\n// to decode to bson.Raw, use the following code:\n//\n//\treg.RegisterTypeMapEntry(TypeEmbeddedDocument, reflect.TypeOf(bson.Raw{}))\nfunc (r *Registry) RegisterTypeMapEntry(bt Type, rt reflect.Type) {\n\tr.typeMap.Store(bt, rt)\n}\n\n// LookupEncoder returns the first matching encoder in the Registry. It uses the following lookup\n// order:\n//\n// 1. An encoder registered for the exact type. If the given type is an interface, an encoder\n// registered using RegisterTypeEncoder for that interface will be selected.\n//\n// 2. An encoder registered using RegisterInterfaceEncoder for an interface implemented by the type\n// or by a pointer to the type. If the value matches multiple interfaces (e.g. the type implements\n// bson.Marshaler and bson.ValueMarshaler), the first one registered will be selected.\n// Note that registries constructed using bson.NewRegistry have driver-defined interfaces registered\n// for the bson.Marshaler, bson.ValueMarshaler, and bson.Proxy interfaces, so those will take\n// precedence over any new interfaces.\n//\n// 3. An encoder registered using RegisterKindEncoder for the kind of value.\n//\n// If no encoder is found, an error of type ErrNoEncoder is returned. LookupEncoder is safe for\n// concurrent use by multiple goroutines after all codecs and encoders are registered.\nfunc (r *Registry) LookupEncoder(valueType reflect.Type) (ValueEncoder, error) {\n\tif valueType == nil {\n\t\treturn nil, errNoEncoder{Type: valueType}\n\t}\n\tenc, found := r.lookupTypeEncoder(valueType)\n\tif found {\n\t\tif enc == nil {\n\t\t\treturn nil, errNoEncoder{Type: valueType}\n\t\t}\n\t\treturn enc, nil\n\t}\n\n\tenc, found = r.lookupInterfaceEncoder(valueType, true)\n\tif found {\n\t\treturn r.typeEncoders.LoadOrStore(valueType, enc), nil\n\t}\n\n\tif v, ok := r.kindEncoders.Load(valueType.Kind()); ok {\n\t\treturn r.storeTypeEncoder(valueType, v), nil\n\t}\n\treturn nil, errNoEncoder{Type: valueType}\n}\n\nfunc (r *Registry) storeTypeEncoder(rt reflect.Type, enc ValueEncoder) ValueEncoder {\n\treturn r.typeEncoders.LoadOrStore(rt, enc)\n}\n\nfunc (r *Registry) lookupTypeEncoder(rt reflect.Type) (ValueEncoder, bool) {\n\treturn r.typeEncoders.Load(rt)\n}\n\nfunc (r *Registry) lookupInterfaceEncoder(valueType reflect.Type, allowAddr bool) (ValueEncoder, bool) {\n\tif valueType == nil {\n\t\treturn nil, false\n\t}\n\tfor _, ienc := range r.interfaceEncoders {\n\t\tif valueType.Implements(ienc.i) {\n\t\t\treturn ienc.ve, true\n\t\t}\n\t\tif allowAddr && valueType.Kind() != reflect.Ptr && reflect.PtrTo(valueType).Implements(ienc.i) {\n\t\t\t// if *t implements an interface, this will catch if t implements an interface further\n\t\t\t// ahead in interfaceEncoders\n\t\t\tdefaultEnc, found := r.lookupInterfaceEncoder(valueType, false)\n\t\t\tif !found {\n\t\t\t\tdefaultEnc, _ = r.kindEncoders.Load(valueType.Kind())\n\t\t\t}\n\t\t\treturn newCondAddrEncoder(ienc.ve, defaultEnc), true\n\t\t}\n\t}\n\treturn nil, false\n}\n\n// LookupDecoder returns the first matching decoder in the Registry. It uses the following lookup\n// order:\n//\n// 1. A decoder registered for the exact type. If the given type is an interface, a decoder\n// registered using RegisterTypeDecoder for that interface will be selected.\n//\n// 2. A decoder registered using RegisterInterfaceDecoder for an interface implemented by the type or by\n// a pointer to the type. If the value matches multiple interfaces (e.g. the type implements\n// bson.Unmarshaler and bson.ValueUnmarshaler), the first one registered will be selected.\n// Note that registries constructed using bson.NewRegistry have driver-defined interfaces registered\n// for the bson.Unmarshaler and bson.ValueUnmarshaler interfaces, so those will take\n// precedence over any new interfaces.\n//\n// 3. A decoder registered using RegisterKindDecoder for the kind of value.\n//\n// If no decoder is found, an error of type ErrNoDecoder is returned. LookupDecoder is safe for\n// concurrent use by multiple goroutines after all codecs and decoders are registered.\nfunc (r *Registry) LookupDecoder(valueType reflect.Type) (ValueDecoder, error) {\n\tif valueType == nil {\n\t\treturn nil, errors.New(\"cannot perform a decoder lookup on <nil>\")\n\t}\n\tdec, found := r.lookupTypeDecoder(valueType)\n\tif found {\n\t\tif dec == nil {\n\t\t\treturn nil, errNoDecoder{Type: valueType}\n\t\t}\n\t\treturn dec, nil\n\t}\n\n\tdec, found = r.lookupInterfaceDecoder(valueType, true)\n\tif found {\n\t\treturn r.storeTypeDecoder(valueType, dec), nil\n\t}\n\n\tif v, ok := r.kindDecoders.Load(valueType.Kind()); ok {\n\t\treturn r.storeTypeDecoder(valueType, v), nil\n\t}\n\treturn nil, errNoDecoder{Type: valueType}\n}\n\nfunc (r *Registry) lookupTypeDecoder(valueType reflect.Type) (ValueDecoder, bool) {\n\treturn r.typeDecoders.Load(valueType)\n}\n\nfunc (r *Registry) storeTypeDecoder(typ reflect.Type, dec ValueDecoder) ValueDecoder {\n\treturn r.typeDecoders.LoadOrStore(typ, dec)\n}\n\nfunc (r *Registry) lookupInterfaceDecoder(valueType reflect.Type, allowAddr bool) (ValueDecoder, bool) {\n\tfor _, idec := range r.interfaceDecoders {\n\t\tif valueType.Implements(idec.i) {\n\t\t\treturn idec.vd, true\n\t\t}\n\t\tif allowAddr && valueType.Kind() != reflect.Ptr && reflect.PtrTo(valueType).Implements(idec.i) {\n\t\t\t// if *t implements an interface, this will catch if t implements an interface further\n\t\t\t// ahead in interfaceDecoders\n\t\t\tdefaultDec, found := r.lookupInterfaceDecoder(valueType, false)\n\t\t\tif !found {\n\t\t\t\tdefaultDec, _ = r.kindDecoders.Load(valueType.Kind())\n\t\t\t}\n\t\t\treturn newCondAddrDecoder(idec.vd, defaultDec), true\n\t\t}\n\t}\n\treturn nil, false\n}\n\n// LookupTypeMapEntry inspects the registry's type map for a Go type for the corresponding BSON\n// type. If no type is found, ErrNoTypeMapEntry is returned.\n//\n// LookupTypeMapEntry should not be called concurrently with any other Registry method.\nfunc (r *Registry) LookupTypeMapEntry(bt Type) (reflect.Type, error) {\n\tv, ok := r.typeMap.Load(bt)\n\tif v == nil || !ok {\n\t\treturn nil, errNoTypeMapEntry{Type: bt}\n\t}\n\treturn v.(reflect.Type), nil\n}\n\ntype interfaceValueEncoder struct {\n\ti  reflect.Type\n\tve ValueEncoder\n}\n\ntype interfaceValueDecoder struct {\n\ti  reflect.Type\n\tvd ValueDecoder\n}\n"
  },
  {
    "path": "bson/registry_examples_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bson_test\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"math\"\n\t\"reflect\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n)\n\nfunc ExampleRegistry_customEncoder() {\n\t// Create a custom encoder for an integer type that multiplies the input\n\t// value by -1 when encoding.\n\ttype negatedInt int\n\n\tnegatedIntType := reflect.TypeOf(negatedInt(0))\n\n\tnegatedIntEncoder := func(\n\t\t_ bson.EncodeContext,\n\t\tvw bson.ValueWriter,\n\t\tval reflect.Value,\n\t) error {\n\t\t// All encoder implementations should check that val is valid and is of\n\t\t// the correct type before proceeding.\n\t\tif !val.IsValid() || val.Type() != negatedIntType {\n\t\t\treturn bson.ValueEncoderError{\n\t\t\t\tName:     \"negatedIntEncoder\",\n\t\t\t\tTypes:    []reflect.Type{negatedIntType},\n\t\t\t\tReceived: val,\n\t\t\t}\n\t\t}\n\n\t\t// Negate val and encode as a BSON int32 if it can fit in 32 bits and a\n\t\t// BSON int64 otherwise.\n\t\tnegatedVal := val.Int() * -1\n\t\tif math.MinInt32 <= negatedVal && negatedVal <= math.MaxInt32 {\n\t\t\treturn vw.WriteInt32(int32(negatedVal))\n\t\t}\n\t\treturn vw.WriteInt64(negatedVal)\n\t}\n\n\treg := bson.NewRegistry()\n\treg.RegisterTypeEncoder(\n\t\tnegatedIntType,\n\t\tbson.ValueEncoderFunc(negatedIntEncoder))\n\n\t// Define a document that includes both int and negatedInt fields with the\n\t// same value.\n\ttype myDocument struct {\n\t\tInt        int\n\t\tNegatedInt negatedInt\n\t}\n\tdoc := myDocument{\n\t\tInt:        1,\n\t\tNegatedInt: 1,\n\t}\n\n\t// Marshal the document as BSON. Expect that the int field is encoded to the\n\t// same value and that the negatedInt field is encoded as the negated value.\n\tbuf := new(bytes.Buffer)\n\tvw := bson.NewDocumentWriter(buf)\n\tenc := bson.NewEncoder(vw)\n\tenc.SetRegistry(reg)\n\terr := enc.Encode(doc)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tfmt.Println(bson.Raw(buf.Bytes()).String())\n\t// Output: {\"int\": {\"$numberInt\":\"1\"},\"negatedint\": {\"$numberInt\":\"-1\"}}\n}\n\nfunc ExampleRegistry_customDecoder() {\n\t// Create a custom decoder for a boolean type that can be stored in the\n\t// database as a BSON boolean, int32, or int64. For our custom decoder, BSON\n\t// int32 or int64 values are considered \"true\" if they are non-zero.\n\ttype lenientBool bool\n\n\tlenientBoolType := reflect.TypeOf(lenientBool(true))\n\n\tlenientBoolDecoder := func(\n\t\t_ bson.DecodeContext,\n\t\tvr bson.ValueReader,\n\t\tval reflect.Value,\n\t) error {\n\t\t// All decoder implementations should check that val is valid, settable,\n\t\t// and is of the correct kind before proceeding.\n\t\tif !val.IsValid() || !val.CanSet() || val.Type() != lenientBoolType {\n\t\t\treturn bson.ValueDecoderError{\n\t\t\t\tName:     \"lenientBoolDecoder\",\n\t\t\t\tTypes:    []reflect.Type{lenientBoolType},\n\t\t\t\tReceived: val,\n\t\t\t}\n\t\t}\n\n\t\tvar result bool\n\t\tswitch vr.Type() {\n\t\tcase bson.TypeBoolean:\n\t\t\tb, err := vr.ReadBoolean()\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tresult = b\n\t\tcase bson.TypeInt32:\n\t\t\ti32, err := vr.ReadInt32()\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tresult = i32 != 0\n\t\tcase bson.TypeInt64:\n\t\t\ti64, err := vr.ReadInt64()\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tresult = i64 != 0\n\t\tdefault:\n\t\t\treturn fmt.Errorf(\n\t\t\t\t\"received invalid BSON type to decode into lenientBool: %s\",\n\t\t\t\tvr.Type())\n\t\t}\n\n\t\tval.SetBool(result)\n\t\treturn nil\n\t}\n\n\treg := bson.NewRegistry()\n\treg.RegisterTypeDecoder(\n\t\tlenientBoolType,\n\t\tbson.ValueDecoderFunc(lenientBoolDecoder))\n\n\t// Marshal a BSON document with a single field \"isOK\" that is a non-zero\n\t// integer value.\n\tb, err := bson.Marshal(bson.M{\"isOK\": 1})\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t// Now try to decode the BSON document to a struct with a field \"IsOK\" that\n\t// is type lenientBool. Expect that the non-zero integer value is decoded\n\t// as boolean true.\n\ttype MyDocument struct {\n\t\tIsOK lenientBool `bson:\"isOK\"`\n\t}\n\tvar doc MyDocument\n\tdec := bson.NewDecoder(bson.NewDocumentReader(bytes.NewReader(b)))\n\tdec.SetRegistry(reg)\n\terr = dec.Decode(&doc)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tfmt.Printf(\"%+v\\n\", doc)\n\t// Output: {IsOK:true}\n}\n\nfunc ExampleRegistry_RegisterKindEncoder() {\n\t// Create a custom encoder that writes any Go type that has underlying type\n\t// int32 as an a BSON int64. To do that, we register the encoder as a \"kind\"\n\t// encoder for kind reflect.Int32. That way, even user-defined types with\n\t// underlying type int32 will be encoded as a BSON int64.\n\tint32To64Encoder := func(\n\t\t_ bson.EncodeContext,\n\t\tvw bson.ValueWriter,\n\t\tval reflect.Value,\n\t) error {\n\t\t// All encoder implementations should check that val is valid and is of\n\t\t// the correct type or kind before proceeding.\n\t\tif !val.IsValid() || val.Kind() != reflect.Int32 {\n\t\t\treturn bson.ValueEncoderError{\n\t\t\t\tName:     \"int32To64Encoder\",\n\t\t\t\tKinds:    []reflect.Kind{reflect.Int32},\n\t\t\t\tReceived: val,\n\t\t\t}\n\t\t}\n\n\t\treturn vw.WriteInt64(val.Int())\n\t}\n\n\t// Create a default registry and register our int32-to-int64 encoder for\n\t// kind reflect.Int32.\n\treg := bson.NewRegistry()\n\treg.RegisterKindEncoder(\n\t\treflect.Int32,\n\t\tbson.ValueEncoderFunc(int32To64Encoder))\n\n\t// Define a document that includes an int32, an int64, and a user-defined\n\t// type \"myInt\" that has underlying type int32.\n\ttype myInt int32\n\ttype myDocument struct {\n\t\tMyInt myInt\n\t\tInt32 int32\n\t\tInt64 int64\n\t}\n\tdoc := myDocument{\n\t\tInt32: 1,\n\t\tInt64: 1,\n\t\tMyInt: 1,\n\t}\n\n\t// Marshal the document as BSON. Expect that all fields are encoded as BSON\n\t// int64 (represented as \"$numberLong\" when encoded as Extended JSON).\n\tbuf := new(bytes.Buffer)\n\tvw := bson.NewDocumentWriter(buf)\n\tenc := bson.NewEncoder(vw)\n\tenc.SetRegistry(reg)\n\terr := enc.Encode(doc)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tfmt.Println(bson.Raw(buf.Bytes()).String())\n\t// Output: {\"myint\": {\"$numberLong\":\"1\"},\"int32\": {\"$numberLong\":\"1\"},\"int64\": {\"$numberLong\":\"1\"}}\n}\n\nfunc ExampleRegistry_RegisterKindDecoder() {\n\t// Create a custom decoder that can decode any integer value, including\n\t// integer values encoded as floating point numbers, to any Go type\n\t// with underlying type int64. To do that, we register the decoder as a\n\t// \"kind\" decoder for kind reflect.Int64. That way, we can even decode to\n\t// user-defined types with underlying type int64.\n\tflexibleInt64KindDecoder := func(\n\t\t_ bson.DecodeContext,\n\t\tvr bson.ValueReader,\n\t\tval reflect.Value,\n\t) error {\n\t\t// All decoder implementations should check that val is valid, settable,\n\t\t// and is of the correct kind before proceeding.\n\t\tif !val.IsValid() || !val.CanSet() || val.Kind() != reflect.Int64 {\n\t\t\treturn bson.ValueDecoderError{\n\t\t\t\tName:     \"flexibleInt64KindDecoder\",\n\t\t\t\tKinds:    []reflect.Kind{reflect.Int64},\n\t\t\t\tReceived: val,\n\t\t\t}\n\t\t}\n\n\t\tvar result int64\n\t\tswitch vr.Type() {\n\t\tcase bson.TypeInt64:\n\t\t\ti64, err := vr.ReadInt64()\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tresult = i64\n\t\tcase bson.TypeInt32:\n\t\t\ti32, err := vr.ReadInt32()\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tresult = int64(i32)\n\t\tcase bson.TypeDouble:\n\t\t\td, err := vr.ReadDouble()\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\ti64 := int64(d)\n\t\t\t// Make sure the double field is an integer value.\n\t\t\tif d != float64(i64) {\n\t\t\t\treturn fmt.Errorf(\"double %f is not an integer value\", d)\n\t\t\t}\n\t\t\tresult = i64\n\t\tdefault:\n\t\t\treturn fmt.Errorf(\n\t\t\t\t\"received invalid BSON type to decode into int64: %s\",\n\t\t\t\tvr.Type())\n\t\t}\n\n\t\tval.SetInt(result)\n\t\treturn nil\n\t}\n\n\treg := bson.NewRegistry()\n\treg.RegisterKindDecoder(\n\t\treflect.Int64,\n\t\tbson.ValueDecoderFunc(flexibleInt64KindDecoder))\n\n\t// Marshal a BSON document with fields that are mixed numeric types but all\n\t// hold integer values (i.e. values with no fractional part).\n\tb, err := bson.Marshal(bson.M{\"myInt\": float64(8), \"int64\": int32(9)})\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t// Now try to decode the BSON document to a struct with fields\n\t// that is type int32. Expect that the float value is successfully decoded.\n\ttype myInt int64\n\ttype myDocument struct {\n\t\tMyInt myInt\n\t\tInt64 int64\n\t}\n\tvar doc myDocument\n\tdec := bson.NewDecoder(bson.NewDocumentReader(bytes.NewReader(b)))\n\tdec.SetRegistry(reg)\n\terr = dec.Decode(&doc)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tfmt.Printf(\"%+v\\n\", doc)\n\t// Output: {MyInt:8 Int64:9}\n}\n"
  },
  {
    "path": "bson/registry_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bson\n\nimport (\n\t\"errors\"\n\t\"reflect\"\n\t\"testing\"\n\n\t\"github.com/google/go-cmp/cmp\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n)\n\n// newTestRegistry creates a new Registry.\nfunc newTestRegistry() *Registry {\n\treturn &Registry{\n\t\ttypeEncoders: new(typeEncoderCache),\n\t\ttypeDecoders: new(typeDecoderCache),\n\t\tkindEncoders: new(kindEncoderCache),\n\t\tkindDecoders: new(kindDecoderCache),\n\t}\n}\n\nfunc TestRegistry(t *testing.T) {\n\tt.Parallel()\n\n\tt.Run(\"Register\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tfc1, fc2, fc3, fc4 := new(fakeCodec), new(fakeCodec), new(fakeCodec), new(fakeCodec)\n\t\tt.Run(\"interface\", func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tvar t1f *testInterface1\n\t\t\tvar t2f *testInterface2\n\t\t\tvar t4f *testInterface4\n\t\t\tips := []interfaceValueEncoder{\n\t\t\t\t{i: reflect.TypeOf(t1f).Elem(), ve: fc1},\n\t\t\t\t{i: reflect.TypeOf(t2f).Elem(), ve: fc2},\n\t\t\t\t{i: reflect.TypeOf(t1f).Elem(), ve: fc3},\n\t\t\t\t{i: reflect.TypeOf(t4f).Elem(), ve: fc4},\n\t\t\t}\n\t\t\twant := []interfaceValueEncoder{\n\t\t\t\t{i: reflect.TypeOf(t1f).Elem(), ve: fc3},\n\t\t\t\t{i: reflect.TypeOf(t2f).Elem(), ve: fc2},\n\t\t\t\t{i: reflect.TypeOf(t4f).Elem(), ve: fc4},\n\t\t\t}\n\t\t\treg := newTestRegistry()\n\t\t\tfor _, ip := range ips {\n\t\t\t\treg.RegisterInterfaceEncoder(ip.i, ip.ve)\n\t\t\t}\n\t\t\tgot := reg.interfaceEncoders\n\t\t\tif !cmp.Equal(got, want, cmp.AllowUnexported(interfaceValueEncoder{}, fakeCodec{}), cmp.Comparer(typeComparer)) {\n\t\t\t\tt.Errorf(\"registered interfaces are not correct: got %#v, want %#v\", got, want)\n\t\t\t}\n\t\t})\n\t\tt.Run(\"type\", func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tft1, ft2, ft4 := fakeType1{}, fakeType2{}, fakeType4{}\n\t\t\treg := newTestRegistry()\n\t\t\treg.RegisterTypeEncoder(reflect.TypeOf(ft1), fc1)\n\t\t\treg.RegisterTypeEncoder(reflect.TypeOf(ft2), fc2)\n\t\t\treg.RegisterTypeEncoder(reflect.TypeOf(ft1), fc3)\n\t\t\treg.RegisterTypeEncoder(reflect.TypeOf(ft4), fc4)\n\n\t\t\twant := []struct {\n\t\t\t\tt reflect.Type\n\t\t\t\tc ValueEncoder\n\t\t\t}{\n\t\t\t\t{reflect.TypeOf(ft1), fc3},\n\t\t\t\t{reflect.TypeOf(ft2), fc2},\n\t\t\t\t{reflect.TypeOf(ft4), fc4},\n\t\t\t}\n\t\t\tgot := reg.typeEncoders\n\t\t\tfor _, s := range want {\n\t\t\t\twantT, wantC := s.t, s.c\n\t\t\t\tgotC, exists := got.Load(wantT)\n\t\t\t\tif !exists {\n\t\t\t\t\tt.Errorf(\"type missing in registry: %v\", wantT)\n\t\t\t\t}\n\t\t\t\tif !cmp.Equal(gotC, wantC, cmp.AllowUnexported(fakeCodec{})) {\n\t\t\t\t\tt.Errorf(\"codecs did not match: got %#v; want %#v\", gotC, wantC)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t\tt.Run(\"kind\", func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tk1, k2, k4 := reflect.Struct, reflect.Slice, reflect.Map\n\t\t\treg := newTestRegistry()\n\t\t\treg.RegisterKindEncoder(k1, fc1)\n\t\t\treg.RegisterKindEncoder(k2, fc2)\n\t\t\treg.RegisterKindEncoder(k1, fc3)\n\t\t\treg.RegisterKindEncoder(k4, fc4)\n\n\t\t\twant := []struct {\n\t\t\t\tk reflect.Kind\n\t\t\t\tc ValueEncoder\n\t\t\t}{\n\t\t\t\t{k1, fc3},\n\t\t\t\t{k2, fc2},\n\t\t\t\t{k4, fc4},\n\t\t\t}\n\t\t\tgot := reg.kindEncoders\n\t\t\tfor _, s := range want {\n\t\t\t\twantK, wantC := s.k, s.c\n\t\t\t\tgotC, exists := got.Load(wantK)\n\t\t\t\tif !exists {\n\t\t\t\t\tt.Errorf(\"type missing in registry: %v\", wantK)\n\t\t\t\t}\n\t\t\t\tif !cmp.Equal(gotC, wantC, cmp.AllowUnexported(fakeCodec{})) {\n\t\t\t\t\tt.Errorf(\"codecs did not match: got %#v, want %#v\", gotC, wantC)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t\tt.Run(\"RegisterDefault\", func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tt.Run(\"MapCodec\", func(t *testing.T) {\n\t\t\t\tt.Parallel()\n\n\t\t\t\tcodec := &fakeCodec{num: 1}\n\t\t\t\tcodec2 := &fakeCodec{num: 2}\n\t\t\t\treg := newTestRegistry()\n\t\t\t\treg.RegisterKindEncoder(reflect.Map, codec)\n\t\t\t\tif reg.kindEncoders.get(reflect.Map) != codec {\n\t\t\t\t\tt.Errorf(\"map codec not properly set: got %#v, want %#v\", reg.kindEncoders.get(reflect.Map), codec)\n\t\t\t\t}\n\t\t\t\treg.RegisterKindEncoder(reflect.Map, codec2)\n\t\t\t\tif reg.kindEncoders.get(reflect.Map) != codec2 {\n\t\t\t\t\tt.Errorf(\"map codec properly set: got %#v, want %#v\", reg.kindEncoders.get(reflect.Map), codec2)\n\t\t\t\t}\n\t\t\t})\n\t\t\tt.Run(\"StructCodec\", func(t *testing.T) {\n\t\t\t\tt.Parallel()\n\n\t\t\t\tcodec := &fakeCodec{num: 1}\n\t\t\t\tcodec2 := &fakeCodec{num: 2}\n\t\t\t\treg := newTestRegistry()\n\t\t\t\treg.RegisterKindEncoder(reflect.Struct, codec)\n\t\t\t\tif reg.kindEncoders.get(reflect.Struct) != codec {\n\t\t\t\t\tt.Errorf(\"struct codec not properly set: got %#v, want %#v\", reg.kindEncoders.get(reflect.Struct), codec)\n\t\t\t\t}\n\t\t\t\treg.RegisterKindEncoder(reflect.Struct, codec2)\n\t\t\t\tif reg.kindEncoders.get(reflect.Struct) != codec2 {\n\t\t\t\t\tt.Errorf(\"struct codec not properly set: got %#v, want %#v\", reg.kindEncoders.get(reflect.Struct), codec2)\n\t\t\t\t}\n\t\t\t})\n\t\t\tt.Run(\"SliceCodec\", func(t *testing.T) {\n\t\t\t\tt.Parallel()\n\n\t\t\t\tcodec := &fakeCodec{num: 1}\n\t\t\t\tcodec2 := &fakeCodec{num: 2}\n\t\t\t\treg := newTestRegistry()\n\t\t\t\treg.RegisterKindEncoder(reflect.Slice, codec)\n\t\t\t\tif reg.kindEncoders.get(reflect.Slice) != codec {\n\t\t\t\t\tt.Errorf(\"slice codec not properly set: got %#v, want %#v\", reg.kindEncoders.get(reflect.Slice), codec)\n\t\t\t\t}\n\t\t\t\treg.RegisterKindEncoder(reflect.Slice, codec2)\n\t\t\t\tif reg.kindEncoders.get(reflect.Slice) != codec2 {\n\t\t\t\t\tt.Errorf(\"slice codec not properly set: got %#v, want %#v\", reg.kindEncoders.get(reflect.Slice), codec2)\n\t\t\t\t}\n\t\t\t})\n\t\t\tt.Run(\"ArrayCodec\", func(t *testing.T) {\n\t\t\t\tt.Parallel()\n\n\t\t\t\tcodec := &fakeCodec{num: 1}\n\t\t\t\tcodec2 := &fakeCodec{num: 2}\n\t\t\t\treg := newTestRegistry()\n\t\t\t\treg.RegisterKindEncoder(reflect.Array, codec)\n\t\t\t\tif reg.kindEncoders.get(reflect.Array) != codec {\n\t\t\t\t\tt.Errorf(\"slice codec not properly set: got %#v, want %#v\", reg.kindEncoders.get(reflect.Array), codec)\n\t\t\t\t}\n\t\t\t\treg.RegisterKindEncoder(reflect.Array, codec2)\n\t\t\t\tif reg.kindEncoders.get(reflect.Array) != codec2 {\n\t\t\t\t\tt.Errorf(\"slice codec not properly set: got %#v, want %#v\", reg.kindEncoders.get(reflect.Array), codec2)\n\t\t\t\t}\n\t\t\t})\n\t\t})\n\t\tt.Run(\"Lookup\", func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\ttype Codec interface {\n\t\t\t\tValueEncoder\n\t\t\t\tValueDecoder\n\t\t\t}\n\n\t\t\tvar (\n\t\t\t\tarrinstance     [12]int\n\t\t\t\tarr             = reflect.TypeOf(arrinstance)\n\t\t\t\tslc             = reflect.TypeOf(make([]int, 12))\n\t\t\t\tm               = reflect.TypeOf(make(map[string]int))\n\t\t\t\tstrct           = reflect.TypeOf(struct{ Foo string }{})\n\t\t\t\tft1             = reflect.PtrTo(reflect.TypeOf(fakeType1{}))\n\t\t\t\tft2             = reflect.TypeOf(fakeType2{})\n\t\t\t\tft3             = reflect.TypeOf(fakeType5(func(string, string) string { return \"fakeType5\" }))\n\t\t\t\tti1             = reflect.TypeOf((*testInterface1)(nil)).Elem()\n\t\t\t\tti2             = reflect.TypeOf((*testInterface2)(nil)).Elem()\n\t\t\t\tti1Impl         = reflect.TypeOf(testInterface1Impl{})\n\t\t\t\tti2Impl         = reflect.TypeOf(testInterface2Impl{})\n\t\t\t\tti3             = reflect.TypeOf((*testInterface3)(nil)).Elem()\n\t\t\t\tti3Impl         = reflect.TypeOf(testInterface3Impl{})\n\t\t\t\tti3ImplPtr      = reflect.TypeOf((*testInterface3Impl)(nil))\n\t\t\t\tfc1, fc2        = &fakeCodec{num: 1}, &fakeCodec{num: 2}\n\t\t\t\tfsc, fslcc, fmc = new(fakeStructCodec), new(fakeSliceCodec), new(fakeMapCodec)\n\t\t\t\tpc              = &pointerCodec{}\n\t\t\t)\n\n\t\t\treg := newTestRegistry()\n\t\t\treg.RegisterTypeEncoder(ft1, fc1)\n\t\t\treg.RegisterTypeEncoder(ft2, fc2)\n\t\t\treg.RegisterTypeEncoder(ti1, fc1)\n\t\t\treg.RegisterKindEncoder(reflect.Struct, fsc)\n\t\t\treg.RegisterKindEncoder(reflect.Slice, fslcc)\n\t\t\treg.RegisterKindEncoder(reflect.Array, fslcc)\n\t\t\treg.RegisterKindEncoder(reflect.Map, fmc)\n\t\t\treg.RegisterKindEncoder(reflect.Ptr, pc)\n\t\t\treg.RegisterTypeDecoder(ft1, fc1)\n\t\t\treg.RegisterTypeDecoder(ft2, fc2)\n\t\t\treg.RegisterTypeDecoder(ti1, fc1) // values whose exact type is testInterface1 will use fc1 encoder\n\t\t\treg.RegisterKindDecoder(reflect.Struct, fsc)\n\t\t\treg.RegisterKindDecoder(reflect.Slice, fslcc)\n\t\t\treg.RegisterKindDecoder(reflect.Array, fslcc)\n\t\t\treg.RegisterKindDecoder(reflect.Map, fmc)\n\t\t\treg.RegisterKindDecoder(reflect.Ptr, pc)\n\t\t\treg.RegisterInterfaceEncoder(ti2, fc2)\n\t\t\treg.RegisterInterfaceDecoder(ti2, fc2)\n\t\t\treg.RegisterInterfaceEncoder(ti3, fc3)\n\t\t\treg.RegisterInterfaceDecoder(ti3, fc3)\n\n\t\t\ttestCases := []struct {\n\t\t\t\tname      string\n\t\t\t\tt         reflect.Type\n\t\t\t\twantcodec Codec\n\t\t\t\twanterr   error\n\t\t\t\ttestcache bool\n\t\t\t}{\n\t\t\t\t{\n\t\t\t\t\t\"type registry (pointer)\",\n\t\t\t\t\tft1,\n\t\t\t\t\tfc1,\n\t\t\t\t\tnil,\n\t\t\t\t\tfalse,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type registry (non-pointer)\",\n\t\t\t\t\tft2,\n\t\t\t\t\tfc2,\n\t\t\t\t\tnil,\n\t\t\t\t\tfalse,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t// lookup an interface type and expect that the registered encoder is returned\n\t\t\t\t\t\"interface with type encoder\",\n\t\t\t\t\tti1,\n\t\t\t\t\tfc1,\n\t\t\t\t\tnil,\n\t\t\t\t\ttrue,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t// lookup a type that implements an interface and expect that the default struct codec is returned\n\t\t\t\t\t\"interface implementation with type encoder\",\n\t\t\t\t\tti1Impl,\n\t\t\t\t\tfsc,\n\t\t\t\t\tnil,\n\t\t\t\t\tfalse,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t// lookup an interface type and expect that the registered hook is returned\n\t\t\t\t\t\"interface with hook\",\n\t\t\t\t\tti2,\n\t\t\t\t\tfc2,\n\t\t\t\t\tnil,\n\t\t\t\t\tfalse,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t// lookup a type that implements an interface and expect that the registered hook is returned\n\t\t\t\t\t\"interface implementation with hook\",\n\t\t\t\t\tti2Impl,\n\t\t\t\t\tfc2,\n\t\t\t\t\tnil,\n\t\t\t\t\tfalse,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t// lookup a pointer to a type where the pointer implements an interface and expect that the\n\t\t\t\t\t// registered hook is returned\n\t\t\t\t\t\"interface pointer to implementation with hook (pointer)\",\n\t\t\t\t\tti3ImplPtr,\n\t\t\t\t\tfc3,\n\t\t\t\t\tnil,\n\t\t\t\t\tfalse,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"default struct codec (pointer)\",\n\t\t\t\t\treflect.PtrTo(strct),\n\t\t\t\t\tpc,\n\t\t\t\t\tnil,\n\t\t\t\t\tfalse,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"default struct codec (non-pointer)\",\n\t\t\t\t\tstrct,\n\t\t\t\t\tfsc,\n\t\t\t\t\tnil,\n\t\t\t\t\tfalse,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"default array codec\",\n\t\t\t\t\tarr,\n\t\t\t\t\tfslcc,\n\t\t\t\t\tnil,\n\t\t\t\t\tfalse,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"default slice codec\",\n\t\t\t\t\tslc,\n\t\t\t\t\tfslcc,\n\t\t\t\t\tnil,\n\t\t\t\t\tfalse,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"default map\",\n\t\t\t\t\tm,\n\t\t\t\t\tfmc,\n\t\t\t\t\tnil,\n\t\t\t\t\tfalse,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"map non-string key\",\n\t\t\t\t\treflect.TypeOf(map[int]int{}),\n\t\t\t\t\tfmc,\n\t\t\t\t\tnil,\n\t\t\t\t\tfalse,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"No Codec Registered\",\n\t\t\t\t\tft3,\n\t\t\t\t\tnil,\n\t\t\t\t\terrNoEncoder{Type: ft3},\n\t\t\t\t\tfalse,\n\t\t\t\t},\n\t\t\t}\n\n\t\t\tallowunexported := cmp.AllowUnexported(fakeCodec{}, fakeStructCodec{}, fakeSliceCodec{}, fakeMapCodec{})\n\t\t\tcomparepc := func(pc1, pc2 *pointerCodec) bool { return pc1 == pc2 }\n\t\t\tfor _, tc := range testCases {\n\t\t\t\ttc := tc\n\n\t\t\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\t\t\tt.Parallel()\n\n\t\t\t\t\tt.Run(\"Encoder\", func(t *testing.T) {\n\t\t\t\t\t\tt.Parallel()\n\n\t\t\t\t\t\tgotcodec, goterr := reg.LookupEncoder(tc.t)\n\t\t\t\t\t\tif !cmp.Equal(goterr, tc.wanterr, cmp.Comparer(assert.CompareErrors)) {\n\t\t\t\t\t\t\tt.Errorf(\"errors did not match: got %#v, want %#v\", goterr, tc.wanterr)\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif !cmp.Equal(gotcodec, tc.wantcodec, allowunexported, cmp.Comparer(comparepc)) {\n\t\t\t\t\t\t\tt.Errorf(\"codecs did not match: got %#v, want %#v\", gotcodec, tc.wantcodec)\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t\t\tt.Run(\"Decoder\", func(t *testing.T) {\n\t\t\t\t\t\tt.Parallel()\n\n\t\t\t\t\t\twanterr := tc.wanterr\n\t\t\t\t\t\tvar ene errNoEncoder\n\t\t\t\t\t\tif errors.As(tc.wanterr, &ene) {\n\t\t\t\t\t\t\twanterr = errNoDecoder(ene)\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tgotcodec, goterr := reg.LookupDecoder(tc.t)\n\t\t\t\t\t\tif !cmp.Equal(goterr, wanterr, cmp.Comparer(assert.CompareErrors)) {\n\t\t\t\t\t\t\tt.Errorf(\"errors did not match: got %#v, want %#v\", goterr, wanterr)\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif !cmp.Equal(gotcodec, tc.wantcodec, allowunexported, cmp.Comparer(comparepc)) {\n\t\t\t\t\t\t\tt.Errorf(\"codecs did not match: got %v: want %v\", gotcodec, tc.wantcodec)\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t\t})\n\t\t\t}\n\t\t\tt.Run(\"nil type\", func(t *testing.T) {\n\t\t\t\tt.Parallel()\n\n\t\t\t\tt.Run(\"Encoder\", func(t *testing.T) {\n\t\t\t\t\tt.Parallel()\n\n\t\t\t\t\twanterr := errNoEncoder{Type: reflect.TypeOf(nil)}\n\n\t\t\t\t\tgotcodec, goterr := reg.LookupEncoder(nil)\n\t\t\t\t\tif !cmp.Equal(goterr, wanterr, cmp.Comparer(assert.CompareErrors)) {\n\t\t\t\t\t\tt.Errorf(\"errors did not match: got %#v, want %#v\", goterr, wanterr)\n\t\t\t\t\t}\n\t\t\t\t\tif !cmp.Equal(gotcodec, nil, allowunexported, cmp.Comparer(comparepc)) {\n\t\t\t\t\t\tt.Errorf(\"codecs did not match: got %#v, want nil\", gotcodec)\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t\tt.Run(\"Decoder\", func(t *testing.T) {\n\t\t\t\t\tt.Parallel()\n\n\t\t\t\t\twanterr := \"cannot perform a decoder lookup on <nil>\"\n\n\t\t\t\t\tgotcodec, goterr := reg.LookupDecoder(nil)\n\t\t\t\t\tassert.EqualError(t, goterr, wanterr, \"errors did not match\")\n\t\t\t\t\tif !cmp.Equal(gotcodec, nil, allowunexported, cmp.Comparer(comparepc)) {\n\t\t\t\t\t\tt.Errorf(\"codecs did not match: got %v: want nil\", gotcodec)\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t})\n\t\t\t// lookup a type whose pointer implements an interface and expect that the registered hook is\n\t\t\t// returned\n\t\t\tt.Run(\"interface implementation with hook (pointer)\", func(t *testing.T) {\n\t\t\t\tt.Parallel()\n\n\t\t\t\tt.Run(\"Encoder\", func(t *testing.T) {\n\t\t\t\t\tt.Parallel()\n\t\t\t\t\tgotEnc, err := reg.LookupEncoder(ti3Impl)\n\t\t\t\t\tassert.Nil(t, err, \"LookupEncoder error: %v\", err)\n\n\t\t\t\t\tcae, ok := gotEnc.(*condAddrEncoder)\n\t\t\t\t\tassert.True(t, ok, \"Expected CondAddrEncoder, got %T\", gotEnc)\n\t\t\t\t\tif !cmp.Equal(cae.canAddrEnc, fc3, allowunexported, cmp.Comparer(comparepc)) {\n\t\t\t\t\t\tt.Errorf(\"expected canAddrEnc %#v, got %#v\", cae.canAddrEnc, fc3)\n\t\t\t\t\t}\n\t\t\t\t\tif !cmp.Equal(cae.elseEnc, fsc, allowunexported, cmp.Comparer(comparepc)) {\n\t\t\t\t\t\tt.Errorf(\"expected elseEnc %#v, got %#v\", cae.elseEnc, fsc)\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t\tt.Run(\"Decoder\", func(t *testing.T) {\n\t\t\t\t\tt.Parallel()\n\n\t\t\t\t\tgotDec, err := reg.LookupDecoder(ti3Impl)\n\t\t\t\t\tassert.Nil(t, err, \"LookupDecoder error: %v\", err)\n\n\t\t\t\t\tcad, ok := gotDec.(*condAddrDecoder)\n\t\t\t\t\tassert.True(t, ok, \"Expected CondAddrDecoder, got %T\", gotDec)\n\t\t\t\t\tif !cmp.Equal(cad.canAddrDec, fc3, allowunexported, cmp.Comparer(comparepc)) {\n\t\t\t\t\t\tt.Errorf(\"expected canAddrDec %#v, got %#v\", cad.canAddrDec, fc3)\n\t\t\t\t\t}\n\t\t\t\t\tif !cmp.Equal(cad.elseDec, fsc, allowunexported, cmp.Comparer(comparepc)) {\n\t\t\t\t\t\tt.Errorf(\"expected elseDec %#v, got %#v\", cad.elseDec, fsc)\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t})\n\t\t})\n\t})\n\tt.Run(\"Type Map\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\treg := newTestRegistry()\n\t\treg.RegisterTypeMapEntry(TypeString, reflect.TypeOf(\"\"))\n\t\treg.RegisterTypeMapEntry(TypeInt32, reflect.TypeOf(int(0)))\n\n\t\tvar got, want reflect.Type\n\n\t\twant = reflect.TypeOf(\"\")\n\t\tgot, err := reg.LookupTypeMapEntry(TypeString)\n\t\tnoerr(t, err)\n\t\tif got != want {\n\t\t\tt.Errorf(\"unexpected type: got %#v, want %#v\", got, want)\n\t\t}\n\n\t\twant = reflect.TypeOf(int(0))\n\t\tgot, err = reg.LookupTypeMapEntry(TypeInt32)\n\t\tnoerr(t, err)\n\t\tif got != want {\n\t\t\tt.Errorf(\"unexpected type: got %#v, want %#v\", got, want)\n\t\t}\n\n\t\twant = nil\n\t\twanterr := errNoTypeMapEntry{Type: TypeObjectID}\n\t\tgot, err = reg.LookupTypeMapEntry(TypeObjectID)\n\t\tif !errors.Is(err, wanterr) {\n\t\t\tt.Errorf(\"unexpected error: got %#v, want %#v\", err, wanterr)\n\t\t}\n\t\tif got != want {\n\t\t\tt.Errorf(\"unexpected error: got %#v, want %#v\", got, want)\n\t\t}\n\t})\n}\n\n// get is only for testing as it does return if the value was found\nfunc (c *kindEncoderCache) get(rt reflect.Kind) ValueEncoder {\n\te, _ := c.Load(rt)\n\treturn e\n}\n\nfunc BenchmarkLookupEncoder(b *testing.B) {\n\ttype childStruct struct {\n\t\tV1, V2, V3, V4 int\n\t}\n\ttype nestedStruct struct {\n\t\tchildStruct\n\t\tA struct{ C1, C2, C3, C4 childStruct }\n\t\tB struct{ C1, C2, C3, C4 childStruct }\n\t\tC struct{ M1, M2, M3, M4 map[int]int }\n\t}\n\ttypes := [...]reflect.Type{\n\t\treflect.TypeOf(int64(1)),\n\t\treflect.TypeOf(&fakeCodec{}),\n\t\treflect.TypeOf(&testInterface1Impl{}),\n\t\treflect.TypeOf(&nestedStruct{}),\n\t}\n\tr := NewRegistry()\n\tfor _, typ := range types {\n\t\tr.RegisterTypeEncoder(typ, &fakeCodec{})\n\t}\n\tb.Run(\"Serial\", func(b *testing.B) {\n\t\tfor i := 0; i < b.N; i++ {\n\t\t\t_, err := r.LookupEncoder(types[i%len(types)])\n\t\t\tif err != nil {\n\t\t\t\tb.Fatal(err)\n\t\t\t}\n\t\t}\n\t})\n\tb.Run(\"Parallel\", func(b *testing.B) {\n\t\tb.RunParallel(func(pb *testing.PB) {\n\t\t\tfor i := 0; pb.Next(); i++ {\n\t\t\t\t_, err := r.LookupEncoder(types[i%len(types)])\n\t\t\t\tif err != nil {\n\t\t\t\t\tb.Fatal(err)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t})\n}\n\ntype (\n\tfakeType1       struct{}\n\tfakeType2       struct{}\n\tfakeType4       struct{}\n\tfakeType5       func(string, string) string\n\tfakeStructCodec struct{ *fakeCodec }\n\tfakeSliceCodec  struct{ *fakeCodec }\n\tfakeMapCodec    struct{ *fakeCodec }\n)\n\ntype fakeCodec struct {\n\t// num is used to differentiate fakeCodec instances and to force Go to allocate a new value in\n\t// memory for every fakeCodec. If fakeCodec were an empty struct, Go may use the same pointer\n\t// for every instance of fakeCodec, making comparisons between pointers to instances of\n\t// fakeCodec sometimes meaningless.\n\tnum int\n}\n\nfunc (*fakeCodec) EncodeValue(EncodeContext, ValueWriter, reflect.Value) error {\n\treturn nil\n}\n\nfunc (*fakeCodec) DecodeValue(DecodeContext, ValueReader, reflect.Value) error {\n\treturn nil\n}\n\ntype (\n\ttestInterface1 interface{ test1() }\n\ttestInterface2 interface{ test2() }\n\ttestInterface3 interface{ test3() }\n\ttestInterface4 interface{ test4() }\n)\n\ntype testInterface1Impl struct{}\n\nvar _ testInterface1 = testInterface1Impl{}\n\nfunc (testInterface1Impl) test1() {}\n\ntype testInterface2Impl struct{}\n\nvar _ testInterface2 = testInterface2Impl{}\n\nfunc (testInterface2Impl) test2() {}\n\ntype testInterface3Impl struct{}\n\nvar _ testInterface3 = (*testInterface3Impl)(nil)\n\nfunc (*testInterface3Impl) test3() {}\n\nfunc typeComparer(i1, i2 reflect.Type) bool { return i1 == i2 }\n"
  },
  {
    "path": "bson/slice_codec.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bson\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"reflect\"\n)\n\n// sliceCodec is the Codec used for slice values.\ntype sliceCodec struct {\n\t// encodeNilAsEmpty causes EncodeValue to marshal nil Go slices as empty BSON arrays instead of\n\t// BSON null.\n\tencodeNilAsEmpty bool\n}\n\n// EncodeValue is the ValueEncoder for slice types.\nfunc (sc *sliceCodec) EncodeValue(ec EncodeContext, vw ValueWriter, val reflect.Value) error {\n\tif !val.IsValid() || val.Kind() != reflect.Slice {\n\t\treturn ValueEncoderError{Name: \"SliceEncodeValue\", Kinds: []reflect.Kind{reflect.Slice}, Received: val}\n\t}\n\n\tif val.IsNil() && !sc.encodeNilAsEmpty && !ec.nilSliceAsEmpty {\n\t\treturn vw.WriteNull()\n\t}\n\n\t// If we have a []byte we want to treat it as a binary instead of as an array.\n\tif val.Type().Elem() == tByte {\n\t\tbyteSlice := make([]byte, val.Len())\n\t\treflect.Copy(reflect.ValueOf(byteSlice), val)\n\t\treturn vw.WriteBinary(byteSlice)\n\t}\n\n\t// If we have a []E we want to treat it as a document instead of as an array.\n\tif val.Type() == tD || val.Type().ConvertibleTo(tD) {\n\t\td := val.Convert(tD).Interface().(D)\n\n\t\tdw, err := vw.WriteDocument()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tfor _, e := range d {\n\t\t\terr = encodeElement(ec, dw, e)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\n\t\treturn dw.WriteDocumentEnd()\n\t}\n\n\taw, err := vw.WriteArray()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\telemType := val.Type().Elem()\n\tencoder, err := ec.LookupEncoder(elemType)\n\tif err != nil && elemType.Kind() != reflect.Interface {\n\t\treturn err\n\t}\n\n\tfor idx := 0; idx < val.Len(); idx++ {\n\t\tcurrEncoder, currVal, lookupErr := lookupElementEncoder(ec, encoder, val.Index(idx))\n\t\tif lookupErr != nil && !errors.Is(lookupErr, errInvalidValue) {\n\t\t\treturn lookupErr\n\t\t}\n\n\t\tvw, err := aw.WriteArrayElement()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tif errors.Is(lookupErr, errInvalidValue) {\n\t\t\terr = vw.WriteNull()\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\n\t\terr = currEncoder.EncodeValue(ec, vw, currVal)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn aw.WriteArrayEnd()\n}\n\n// DecodeValue is the ValueDecoder for slice types.\nfunc (sc *sliceCodec) DecodeValue(dc DecodeContext, vr ValueReader, val reflect.Value) error {\n\tif !val.CanSet() || val.Kind() != reflect.Slice {\n\t\treturn ValueDecoderError{Name: \"SliceDecodeValue\", Kinds: []reflect.Kind{reflect.Slice}, Received: val}\n\t}\n\n\tswitch vrType := vr.Type(); vrType {\n\tcase TypeArray:\n\tcase TypeNull:\n\t\tval.Set(reflect.Zero(val.Type()))\n\t\treturn vr.ReadNull()\n\tcase TypeUndefined:\n\t\tval.Set(reflect.Zero(val.Type()))\n\t\treturn vr.ReadUndefined()\n\tcase Type(0), TypeEmbeddedDocument:\n\t\tif val.Type().Elem() != tE {\n\t\t\treturn fmt.Errorf(\"cannot decode document into %s\", val.Type())\n\t\t}\n\tcase TypeBinary:\n\t\tif val.Type().Elem() != tByte {\n\t\t\treturn fmt.Errorf(\"SliceDecodeValue can only decode a binary into a byte array, got %v\", vrType)\n\t\t}\n\t\tdata, subtype, err := vr.ReadBinary()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif subtype != TypeBinaryGeneric && subtype != TypeBinaryBinaryOld {\n\t\t\treturn fmt.Errorf(\"SliceDecodeValue can only be used to decode subtype 0x00 or 0x02 for %s, got %v\", TypeBinary, subtype)\n\t\t}\n\n\t\tif val.IsNil() {\n\t\t\tval.Set(reflect.MakeSlice(val.Type(), 0, len(data)))\n\t\t}\n\t\tval.SetLen(0)\n\t\tval.Set(reflect.AppendSlice(val, reflect.ValueOf(data)))\n\t\treturn nil\n\tcase TypeString:\n\t\tif sliceType := val.Type().Elem(); sliceType != tByte {\n\t\t\treturn fmt.Errorf(\"SliceDecodeValue can only decode a string into a byte array, got %v\", sliceType)\n\t\t}\n\t\tstr, err := vr.ReadString()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tbyteStr := []byte(str)\n\n\t\tif val.IsNil() {\n\t\t\tval.Set(reflect.MakeSlice(val.Type(), 0, len(byteStr)))\n\t\t}\n\t\tval.SetLen(0)\n\t\tval.Set(reflect.AppendSlice(val, reflect.ValueOf(byteStr)))\n\t\treturn nil\n\tdefault:\n\t\treturn fmt.Errorf(\"cannot decode %v into a slice\", vrType)\n\t}\n\n\tvar elemsFunc func(DecodeContext, ValueReader, reflect.Value) ([]reflect.Value, error)\n\tswitch val.Type().Elem() {\n\tcase tE:\n\t\telemsFunc = decodeD\n\tdefault:\n\t\telemsFunc = decodeDefault\n\t}\n\n\telems, err := elemsFunc(dc, vr, val)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif val.IsNil() {\n\t\tval.Set(reflect.MakeSlice(val.Type(), 0, len(elems)))\n\t}\n\n\tval.SetLen(0)\n\tval.Set(reflect.Append(val, elems...))\n\n\treturn nil\n}\n"
  },
  {
    "path": "bson/streaming_byte_src.go",
    "content": "// Copyright (C) MongoDB, Inc. 2025-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bson\n\nimport (\n\t\"bufio\"\n\t\"io\"\n)\n\n// streamingByteSrc reads from an ioReader wrapped in a bufio.Reader. It\n// first reads the BSON length header, then ensures it only ever reads exactly\n// that many bytes.\n//\n// Note: this approach trades memory usage for extra buffering and reader calls,\n// so it is less performanted than the in-memory bufferedValueReader.\ntype streamingByteSrc struct {\n\tbr     *bufio.Reader\n\toffset int64 // offset is the current read position in the buffer\n}\n\nvar _ byteSrc = (*streamingByteSrc)(nil)\n\n// Read reads up to len(p) bytes from the underlying bufio.Reader, advancing\n// the offset by the number of bytes read.\nfunc (s *streamingByteSrc) readExact(p []byte) (int, error) {\n\tn, err := io.ReadFull(s.br, p)\n\tif err == nil {\n\t\ts.offset += int64(n)\n\t}\n\n\treturn n, err\n}\n\n// ReadByte returns the single byte at buf[offset] and advances offset by 1.\nfunc (s *streamingByteSrc) ReadByte() (byte, error) {\n\tc, err := s.br.ReadByte()\n\tif err == nil {\n\t\ts.offset++\n\t}\n\treturn c, err\n}\n\n// peek returns buf[offset:offset+n] without advancing offset.\nfunc (s *streamingByteSrc) peek(n int) ([]byte, error) {\n\treturn s.br.Peek(n)\n}\n\n// discard advances offset by n bytes, returning the number of bytes discarded.\nfunc (s *streamingByteSrc) discard(n int) (int, error) {\n\tm, err := s.br.Discard(n)\n\ts.offset += int64(m)\n\treturn m, err\n}\n\n// readSlice reads until the first occurrence of delim, returning a slice\n// containing the data up to and including the delimiter, and advances offset\n// past it; errors if delim not found.\nfunc (s *streamingByteSrc) readSlice(delim byte) ([]byte, error) {\n\tvar full [][]byte\n\tvar frag []byte\n\tvar err error\n\tvar n int\n\n\tfor {\n\t\tif l := len(frag); l > 0 {\n\t\t\t// Make a copy of the fragment to accumulate full buffers.\n\t\t\tbuf := make([]byte, l)\n\t\t\tcopy(buf, frag)\n\t\t\tfull = append(full, buf)\n\t\t}\n\t\tfrag, err = s.br.ReadSlice(delim)\n\t\tn += len(frag)\n\t\tif err != bufio.ErrBufferFull {\n\t\t\tbreak\n\t\t}\n\t}\n\ts.offset += int64(n)\n\n\t// If ReadSlice is only called once, we can return the fragment directly.\n\tif len(full) == 0 {\n\t\treturn frag, err\n\t}\n\n\t// Allocate new buffer to hold the full buffers and the fragment.\n\tbuf := make([]byte, n)\n\tn = 0\n\tfor i := range full {\n\t\tn += copy(buf[n:], full[i])\n\t}\n\tcopy(buf[n:], frag)\n\treturn buf, err\n}\n\n// pos returns the current read position in the buffer.\nfunc (s *streamingByteSrc) pos() int64 {\n\treturn s.offset\n}\n\n// regexLength will return the total byte length of a BSON regex value.\nfunc (s *streamingByteSrc) regexLength() (int32, error) {\n\tvar (\n\t\tcount    int32\n\t\tnulCount int\n\t)\n\n\tfor nulCount < 2 {\n\t\tbuf, err := s.br.Peek(int(count) + 1)\n\t\tif err != nil {\n\t\t\treturn 0, err\n\t\t}\n\n\t\tb := buf[count]\n\t\tcount++\n\t\tif b == 0x00 {\n\t\t\tnulCount++\n\t\t}\n\t}\n\n\treturn count, nil\n}\n\nfunc (*streamingByteSrc) streamable() bool {\n\treturn true\n}\n\nfunc (s *streamingByteSrc) reset() {\n\ts.offset = 0\n}\n"
  },
  {
    "path": "bson/string_codec.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bson\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"reflect\"\n)\n\n// stringCodec is the Codec used for string values.\ntype stringCodec struct{}\n\n// Assert that stringCodec satisfies the typeDecoder interface, which allows it to be\n// used by collection type decoders (e.g. map, slice, etc) to set individual values in a\n// collection.\nvar _ typeDecoder = &stringCodec{}\n\n// EncodeValue is the ValueEncoder for string types.\nfunc (sc *stringCodec) EncodeValue(_ EncodeContext, vw ValueWriter, val reflect.Value) error {\n\tif val.Kind() != reflect.String {\n\t\treturn ValueEncoderError{\n\t\t\tName:     \"StringEncodeValue\",\n\t\t\tKinds:    []reflect.Kind{reflect.String},\n\t\t\tReceived: val,\n\t\t}\n\t}\n\n\treturn vw.WriteString(val.String())\n}\n\nfunc (sc *stringCodec) decodeType(dc DecodeContext, vr ValueReader, t reflect.Type) (reflect.Value, error) {\n\tif t.Kind() != reflect.String {\n\t\treturn emptyValue, ValueDecoderError{\n\t\t\tName:     \"StringDecodeValue\",\n\t\t\tKinds:    []reflect.Kind{reflect.String},\n\t\t\tReceived: reflect.Zero(t),\n\t\t}\n\t}\n\n\tvar str string\n\tvar err error\n\tswitch vr.Type() {\n\tcase TypeString:\n\t\tstr, err = vr.ReadString()\n\t\tif err != nil {\n\t\t\treturn emptyValue, err\n\t\t}\n\tcase TypeObjectID:\n\t\tif dc.objectIDAsHexString {\n\t\t\toid, err := vr.ReadObjectID()\n\t\t\tif err != nil {\n\t\t\t\treturn emptyValue, err\n\t\t\t}\n\t\t\tstr = oid.Hex()\n\t\t} else {\n\t\t\tconst msg = \"decoding an object ID into a string is not supported by default \" +\n\t\t\t\t\"(set Decoder.ObjectIDAsHexString to enable decoding as a hexadecimal string)\"\n\t\t\treturn emptyValue, errors.New(msg)\n\t\t}\n\tcase TypeSymbol:\n\t\tstr, err = vr.ReadSymbol()\n\t\tif err != nil {\n\t\t\treturn emptyValue, err\n\t\t}\n\tcase TypeBinary:\n\t\tdata, subtype, err := vr.ReadBinary()\n\t\tif err != nil {\n\t\t\treturn emptyValue, err\n\t\t}\n\t\tif subtype != TypeBinaryGeneric && subtype != TypeBinaryBinaryOld {\n\t\t\treturn emptyValue, decodeBinaryError{subtype: subtype, typeName: \"string\"}\n\t\t}\n\t\tstr = string(data)\n\tcase TypeNull:\n\t\tif err = vr.ReadNull(); err != nil {\n\t\t\treturn emptyValue, err\n\t\t}\n\tcase TypeUndefined:\n\t\tif err = vr.ReadUndefined(); err != nil {\n\t\t\treturn emptyValue, err\n\t\t}\n\tdefault:\n\t\treturn emptyValue, fmt.Errorf(\"cannot decode %v into a string type\", vr.Type())\n\t}\n\n\treturn reflect.ValueOf(str), nil\n}\n\n// DecodeValue is the ValueDecoder for string types.\nfunc (sc *stringCodec) DecodeValue(dctx DecodeContext, vr ValueReader, val reflect.Value) error {\n\tif !val.CanSet() || val.Kind() != reflect.String {\n\t\treturn ValueDecoderError{Name: \"StringDecodeValue\", Kinds: []reflect.Kind{reflect.String}, Received: val}\n\t}\n\n\telem, err := sc.decodeType(dctx, vr, val.Type())\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tval.SetString(elem.String())\n\treturn nil\n}\n"
  },
  {
    "path": "bson/struct_codec.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bson\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"reflect\"\n\t\"sort\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n)\n\n// DecodeError represents an error that occurs when unmarshalling BSON bytes into a native Go type.\ntype DecodeError struct {\n\tkeys    []string\n\twrapped error\n}\n\n// Unwrap returns the underlying error\nfunc (de *DecodeError) Unwrap() error {\n\treturn de.wrapped\n}\n\n// Error implements the error interface.\nfunc (de *DecodeError) Error() string {\n\t// The keys are stored in reverse order because the de.keys slice is builtup while propagating the error up the\n\t// stack of BSON keys, so we call de.Keys(), which reverses them.\n\tkeyPath := strings.Join(de.Keys(), \".\")\n\treturn fmt.Sprintf(\"error decoding key %s: %v\", keyPath, de.wrapped)\n}\n\n// Keys returns the BSON key path that caused an error as a slice of strings. The keys in the slice are in top-down\n// order. For example, if the document being unmarshalled was {a: {b: {c: 1}}} and the value for c was supposed to be\n// a string, the keys slice will be [\"a\", \"b\", \"c\"].\nfunc (de *DecodeError) Keys() []string {\n\treversedKeys := make([]string, 0, len(de.keys))\n\tfor idx := len(de.keys) - 1; idx >= 0; idx-- {\n\t\treversedKeys = append(reversedKeys, de.keys[idx])\n\t}\n\n\treturn reversedKeys\n}\n\n// mapElementsEncoder handles encoding of the values of an inline  map.\ntype mapElementsEncoder interface {\n\tencodeMapElements(EncodeContext, DocumentWriter, reflect.Value, func(string) bool) error\n}\n\n// structCodec is the Codec used for struct values.\ntype structCodec struct {\n\tcache            sync.Map // map[reflect.Type]*structDescription\n\tinlineMapEncoder mapElementsEncoder\n\n\t// decodeZeroStruct causes DecodeValue to delete any existing values from Go structs in the\n\t// destination value passed to Decode before unmarshaling BSON documents into them.\n\tdecodeZeroStruct bool\n\n\t// decodeDeepZeroInline causes DecodeValue to delete any existing values from Go structs in the\n\t// destination value passed to Decode before unmarshaling BSON documents into them.\n\tdecodeDeepZeroInline bool\n\n\t// encodeOmitDefaultStruct causes the Encoder to consider the zero value for a struct (e.g.\n\t// MyStruct{}) as empty and omit it from the marshaled BSON when the \"omitempty\" struct tag\n\t// option is set.\n\tencodeOmitDefaultStruct bool\n\n\t// allowUnexportedFields allows encoding and decoding values from un-exported struct fields.\n\tallowUnexportedFields bool\n\n\t// overwriteDuplicatedInlinedFields, if false, causes EncodeValue to return an error if there is\n\t// a duplicate field in the marshaled BSON when the \"inline\" struct tag option is set. The\n\t// default value is true.\n\toverwriteDuplicatedInlinedFields bool\n}\n\nvar (\n\t_ ValueEncoder = &structCodec{}\n\t_ ValueDecoder = &structCodec{}\n)\n\n// newStructCodec returns a StructCodec that uses p for struct tag parsing.\nfunc newStructCodec(elemEncoder mapElementsEncoder) *structCodec {\n\treturn &structCodec{\n\t\tinlineMapEncoder:                 elemEncoder,\n\t\toverwriteDuplicatedInlinedFields: true,\n\t}\n}\n\n// EncodeValue handles encoding generic struct types.\nfunc (sc *structCodec) EncodeValue(ec EncodeContext, vw ValueWriter, val reflect.Value) error {\n\tif !val.IsValid() || val.Kind() != reflect.Struct {\n\t\treturn ValueEncoderError{Name: \"StructCodec.EncodeValue\", Kinds: []reflect.Kind{reflect.Struct}, Received: val}\n\t}\n\n\tsd, err := sc.describeStruct(ec.Registry, val.Type(), ec.useJSONStructTags, ec.errorOnInlineDuplicates)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tdw, err := vw.WriteDocument()\n\tif err != nil {\n\t\treturn err\n\t}\n\tvar rv reflect.Value\n\tfor _, desc := range sd.fl {\n\t\tif desc.inline == nil {\n\t\t\trv = val.Field(desc.idx)\n\t\t} else {\n\t\t\trv, err = fieldByIndexErr(val, desc.inline)\n\t\t\tif err != nil {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t}\n\n\t\tif ec.omitEmpty {\n\t\t\tdesc.omitEmpty = true\n\t\t}\n\n\t\tdesc.encoder, rv, err = lookupElementEncoder(ec, desc.encoder, rv)\n\n\t\tif err != nil && !errors.Is(err, errInvalidValue) {\n\t\t\treturn err\n\t\t}\n\n\t\tif errors.Is(err, errInvalidValue) {\n\t\t\tif desc.omitEmpty {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tvw2, err := dw.WriteDocumentElement(desc.name)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\terr = vw2.WriteNull()\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\n\t\tif desc.encoder == nil {\n\t\t\treturn errNoEncoder{Type: rv.Type()}\n\t\t}\n\n\t\tencoder := desc.encoder\n\n\t\tvar empty bool\n\t\tif rv.Kind() == reflect.Interface {\n\t\t\t// isEmpty will not treat an interface rv as an interface, so we need to check for the\n\t\t\t// nil interface separately.\n\t\t\tempty = rv.IsNil()\n\t\t} else {\n\t\t\tempty = isEmpty(rv, sc.encodeOmitDefaultStruct || ec.omitZeroStruct)\n\t\t}\n\t\tif desc.omitEmpty && empty {\n\t\t\tcontinue\n\t\t}\n\n\t\tvw2, err := dw.WriteDocumentElement(desc.name)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tectx := EncodeContext{\n\t\t\tRegistry:                ec.Registry,\n\t\t\tminSize:                 desc.minSize || ec.minSize,\n\t\t\terrorOnInlineDuplicates: ec.errorOnInlineDuplicates,\n\t\t\tstringifyMapKeysWithFmt: ec.stringifyMapKeysWithFmt,\n\t\t\tnilMapAsEmpty:           ec.nilMapAsEmpty,\n\t\t\tnilSliceAsEmpty:         ec.nilSliceAsEmpty,\n\t\t\tnilByteSliceAsEmpty:     ec.nilByteSliceAsEmpty,\n\t\t\tomitZeroStruct:          ec.omitZeroStruct,\n\t\t\tuseJSONStructTags:       ec.useJSONStructTags,\n\t\t}\n\t\terr = encoder.EncodeValue(ectx, vw2, rv)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tif sd.inlineMap >= 0 {\n\t\trv := val.Field(sd.inlineMap)\n\t\tcollisionFn := func(key string) bool {\n\t\t\t_, exists := sd.fm[key]\n\t\t\treturn exists\n\t\t}\n\n\t\terr = sc.inlineMapEncoder.encodeMapElements(ec, dw, rv, collisionFn)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\treturn dw.WriteDocumentEnd()\n}\n\nfunc newDecodeError(key string, original error) error {\n\tvar de *DecodeError\n\tif !errors.As(original, &de) {\n\t\treturn &DecodeError{\n\t\t\tkeys:    []string{key},\n\t\t\twrapped: original,\n\t\t}\n\t}\n\n\tde.keys = append(de.keys, key)\n\treturn de\n}\n\n// DecodeValue implements the Codec interface.\n// By default, map types in val will not be cleared. If a map has existing key/value pairs, it will be extended with the new ones from vr.\n// For slices, the decoder will set the length of the slice to zero and append all elements. The underlying array will not be cleared.\nfunc (sc *structCodec) DecodeValue(dc DecodeContext, vr ValueReader, val reflect.Value) error {\n\tif !val.CanSet() || val.Kind() != reflect.Struct {\n\t\treturn ValueDecoderError{Name: \"StructCodec.DecodeValue\", Kinds: []reflect.Kind{reflect.Struct}, Received: val}\n\t}\n\n\tswitch vrType := vr.Type(); vrType {\n\tcase Type(0), TypeEmbeddedDocument:\n\tcase TypeNull:\n\t\tif err := vr.ReadNull(); err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tval.Set(reflect.Zero(val.Type()))\n\t\treturn nil\n\tcase TypeUndefined:\n\t\tif err := vr.ReadUndefined(); err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tval.Set(reflect.Zero(val.Type()))\n\t\treturn nil\n\tdefault:\n\t\treturn fmt.Errorf(\"cannot decode %v into a %s\", vrType, val.Type())\n\t}\n\n\tsd, err := sc.describeStruct(dc.Registry, val.Type(), dc.useJSONStructTags, false)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif sc.decodeZeroStruct || dc.zeroStructs {\n\t\tval.Set(reflect.Zero(val.Type()))\n\t}\n\tif sc.decodeDeepZeroInline && sd.inline {\n\t\tval.Set(deepZero(val.Type()))\n\t}\n\n\tvar decoder ValueDecoder\n\tvar inlineMap reflect.Value\n\tif sd.inlineMap >= 0 {\n\t\tinlineMap = val.Field(sd.inlineMap)\n\t\tdecoder, err = dc.LookupDecoder(inlineMap.Type().Elem())\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tdr, err := vr.ReadDocument()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor {\n\t\tname, vr, err := dr.ReadElement()\n\t\tif errors.Is(err, ErrEOD) {\n\t\t\tbreak\n\t\t}\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tfd, exists := sd.fm[name]\n\t\tif !exists {\n\t\t\t// if the original name isn't found in the struct description, try again with the name in lowercase\n\t\t\t// this could match if a BSON tag isn't specified because by default, describeStruct lowercases all field\n\t\t\t// names\n\t\t\tfd, exists = sd.fm[strings.ToLower(name)]\n\t\t}\n\n\t\tif !exists {\n\t\t\tif sd.inlineMap < 0 {\n\t\t\t\t// The encoding/json package requires a flag to return on error for non-existent fields.\n\t\t\t\t// This functionality seems appropriate for the struct codec.\n\t\t\t\terr = vr.Skip()\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tif inlineMap.IsNil() {\n\t\t\t\tinlineMap.Set(reflect.MakeMap(inlineMap.Type()))\n\t\t\t}\n\n\t\t\telem := reflect.New(inlineMap.Type().Elem()).Elem()\n\t\t\terr = decoder.DecodeValue(dc, vr, elem)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tinlineMap.SetMapIndex(reflect.ValueOf(name), elem)\n\t\t\tcontinue\n\t\t}\n\n\t\tvar field reflect.Value\n\t\tif fd.inline == nil {\n\t\t\tfield = val.Field(fd.idx)\n\t\t} else {\n\t\t\tfield, err = getInlineField(val, fd.inline)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\n\t\tif field.Kind() == reflect.Interface && !field.IsNil() && field.Elem().Kind() == reflect.Ptr {\n\t\t\tv := field.Elem().Elem()\n\t\t\tdecoder, err = dc.LookupDecoder(v.Type())\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\terr = decoder.DecodeValue(dc, vr, v)\n\t\t\tif err != nil {\n\t\t\t\treturn newDecodeError(fd.name, err)\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\n\t\tif !field.CanSet() { // Being settable is a super set of being addressable.\n\t\t\tinnerErr := fmt.Errorf(\"field %v is not settable\", field)\n\t\t\treturn newDecodeError(fd.name, innerErr)\n\t\t}\n\t\tif field.Kind() == reflect.Ptr && field.IsNil() {\n\t\t\tfield.Set(reflect.New(field.Type().Elem()))\n\t\t}\n\t\tfield = field.Addr()\n\n\t\tdctx := DecodeContext{\n\t\t\tRegistry:            dc.Registry,\n\t\t\ttruncate:            fd.truncate || dc.truncate,\n\t\t\tdefaultDocumentType: dc.defaultDocumentType,\n\t\t\tbinaryAsSlice:       dc.binaryAsSlice,\n\t\t\tobjectIDAsHexString: dc.objectIDAsHexString,\n\t\t\tuseJSONStructTags:   dc.useJSONStructTags,\n\t\t\tuseLocalTimeZone:    dc.useLocalTimeZone,\n\t\t\tzeroMaps:            dc.zeroMaps,\n\t\t\tzeroStructs:         dc.zeroStructs,\n\t\t}\n\n\t\tif fd.decoder == nil {\n\t\t\treturn newDecodeError(fd.name, errNoDecoder{Type: field.Elem().Type()})\n\t\t}\n\n\t\terr = fd.decoder.DecodeValue(dctx, vr, field.Elem())\n\t\tif err != nil {\n\t\t\treturn newDecodeError(fd.name, err)\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc isEmpty(v reflect.Value, omitZeroStruct bool) bool {\n\tkind := v.Kind()\n\tif (kind != reflect.Ptr || !v.IsNil()) && v.Type().Implements(tZeroer) {\n\t\treturn v.Interface().(Zeroer).IsZero()\n\t}\n\tswitch kind {\n\tcase reflect.Array, reflect.Map, reflect.Slice, reflect.String:\n\t\treturn v.Len() == 0\n\tcase reflect.Struct:\n\t\tif !omitZeroStruct {\n\t\t\treturn false\n\t\t}\n\t\tvt := v.Type()\n\t\tif vt == tTime {\n\t\t\treturn v.Interface().(time.Time).IsZero()\n\t\t}\n\t\tnumField := vt.NumField()\n\t\tfor i := 0; i < numField; i++ {\n\t\t\tff := vt.Field(i)\n\t\t\tif ff.PkgPath != \"\" && !ff.Anonymous {\n\t\t\t\tcontinue // Private field\n\t\t\t}\n\t\t\tif !isEmpty(v.Field(i), omitZeroStruct) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t}\n\t\treturn true\n\t}\n\treturn !v.IsValid() || v.IsZero()\n}\n\ntype structDescription struct {\n\tfm        map[string]fieldDescription\n\tfl        []fieldDescription\n\tinlineMap int\n\tinline    bool\n}\n\ntype fieldDescription struct {\n\tname      string // BSON key name\n\tfieldName string // struct field name\n\tidx       int\n\tomitEmpty bool\n\tminSize   bool\n\ttruncate  bool\n\tinline    []int\n\tencoder   ValueEncoder\n\tdecoder   ValueDecoder\n}\n\ntype byIndex []fieldDescription\n\nfunc (bi byIndex) Len() int { return len(bi) }\n\nfunc (bi byIndex) Swap(i, j int) { bi[i], bi[j] = bi[j], bi[i] }\n\nfunc (bi byIndex) Less(i, j int) bool {\n\t// If a field is inlined, its index in the top level struct is stored at inline[0]\n\tiIdx, jIdx := bi[i].idx, bi[j].idx\n\tif len(bi[i].inline) > 0 {\n\t\tiIdx = bi[i].inline[0]\n\t}\n\tif len(bi[j].inline) > 0 {\n\t\tjIdx = bi[j].inline[0]\n\t}\n\tif iIdx != jIdx {\n\t\treturn iIdx < jIdx\n\t}\n\tfor k, biik := range bi[i].inline {\n\t\tif k >= len(bi[j].inline) {\n\t\t\treturn false\n\t\t}\n\t\tif biik != bi[j].inline[k] {\n\t\t\treturn biik < bi[j].inline[k]\n\t\t}\n\t}\n\treturn len(bi[i].inline) < len(bi[j].inline)\n}\n\nfunc (sc *structCodec) describeStruct(\n\tr *Registry,\n\tt reflect.Type,\n\tuseJSONStructTags bool,\n\terrorOnDuplicates bool,\n) (*structDescription, error) {\n\t// We need to analyze the struct, including getting the tags, collecting\n\t// information about inlining, and create a map of the field name to the field.\n\tif v, ok := sc.cache.Load(t); ok {\n\t\treturn v.(*structDescription), nil\n\t}\n\t// TODO(charlie): Only describe the struct once when called\n\t// concurrently with the same type.\n\tds, err := sc.describeStructSlow(r, t, useJSONStructTags, errorOnDuplicates)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif v, loaded := sc.cache.LoadOrStore(t, ds); loaded {\n\t\tds = v.(*structDescription)\n\t}\n\treturn ds, nil\n}\n\nfunc (sc *structCodec) describeStructSlow(\n\tr *Registry,\n\tt reflect.Type,\n\tuseJSONStructTags bool,\n\terrorOnDuplicates bool,\n) (*structDescription, error) {\n\tnumFields := t.NumField()\n\tsd := &structDescription{\n\t\tfm:        make(map[string]fieldDescription, numFields),\n\t\tfl:        make([]fieldDescription, 0, numFields),\n\t\tinlineMap: -1,\n\t}\n\n\tvar fields []fieldDescription\n\tfor i := 0; i < numFields; i++ {\n\t\tsf := t.Field(i)\n\t\tif sf.PkgPath != \"\" && (!sc.allowUnexportedFields || !sf.Anonymous) {\n\t\t\t// field is private or unexported fields aren't allowed, ignore\n\t\t\tcontinue\n\t\t}\n\n\t\tsfType := sf.Type\n\t\tencoder, err := r.LookupEncoder(sfType)\n\t\tif err != nil {\n\t\t\tencoder = nil\n\t\t}\n\t\tdecoder, err := r.LookupDecoder(sfType)\n\t\tif err != nil {\n\t\t\tdecoder = nil\n\t\t}\n\n\t\tdescription := fieldDescription{\n\t\t\tfieldName: sf.Name,\n\t\t\tidx:       i,\n\t\t\tencoder:   encoder,\n\t\t\tdecoder:   decoder,\n\t\t}\n\n\t\tvar stags *structTags\n\t\t// If the caller requested that we use JSON struct tags, use the JSONFallbackStructTagParser\n\t\t// instead of the parser defined on the codec.\n\t\tif useJSONStructTags {\n\t\t\tstags, err = parseJSONStructTags(sf)\n\t\t} else {\n\t\t\tstags, err = parseStructTags(sf)\n\t\t}\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif stags.Skip {\n\t\t\tcontinue\n\t\t}\n\t\tdescription.name = stags.Name\n\t\tdescription.omitEmpty = stags.OmitEmpty\n\t\tdescription.minSize = stags.MinSize\n\t\tdescription.truncate = stags.Truncate\n\n\t\tif stags.Inline {\n\t\t\tsd.inline = true\n\t\t\tswitch sfType.Kind() {\n\t\t\tcase reflect.Map:\n\t\t\t\tif sd.inlineMap >= 0 {\n\t\t\t\t\treturn nil, errors.New(\"(struct \" + t.String() + \") multiple inline maps\")\n\t\t\t\t}\n\t\t\t\tif sfType.Key() != tString {\n\t\t\t\t\treturn nil, errors.New(\"(struct \" + t.String() + \") inline map must have a string keys\")\n\t\t\t\t}\n\t\t\t\tsd.inlineMap = description.idx\n\t\t\tcase reflect.Ptr:\n\t\t\t\tsfType = sfType.Elem()\n\t\t\t\tif sfType.Kind() != reflect.Struct {\n\t\t\t\t\treturn nil, fmt.Errorf(\"(struct %s) inline fields must be a struct, a struct pointer, or a map\", t.String())\n\t\t\t\t}\n\t\t\t\tfallthrough\n\t\t\tcase reflect.Struct:\n\t\t\t\tinlinesf, err := sc.describeStruct(r, sfType, useJSONStructTags, errorOnDuplicates)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t\tfor _, fd := range inlinesf.fl {\n\t\t\t\t\tif fd.inline == nil {\n\t\t\t\t\t\tfd.inline = []int{i, fd.idx}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tfd.inline = append([]int{i}, fd.inline...)\n\t\t\t\t\t}\n\t\t\t\t\tfields = append(fields, fd)\n\n\t\t\t\t}\n\t\t\tdefault:\n\t\t\t\treturn nil, fmt.Errorf(\"(struct %s) inline fields must be a struct, a struct pointer, or a map\", t.String())\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\t\tfields = append(fields, description)\n\t}\n\n\t// Sort fieldDescriptions by name and use dominance rules to determine which should be added for each name\n\tsort.Slice(fields, func(i, j int) bool {\n\t\tx := fields\n\t\t// sort field by name, breaking ties with depth, then\n\t\t// breaking ties with index sequence.\n\t\tif x[i].name != x[j].name {\n\t\t\treturn x[i].name < x[j].name\n\t\t}\n\t\tif len(x[i].inline) != len(x[j].inline) {\n\t\t\treturn len(x[i].inline) < len(x[j].inline)\n\t\t}\n\t\treturn byIndex(x).Less(i, j)\n\t})\n\n\tfor advance, i := 0, 0; i < len(fields); i += advance {\n\t\t// One iteration per name.\n\t\t// Find the sequence of fields with the name of this first field.\n\t\tfi := fields[i]\n\t\tname := fi.name\n\t\tfor advance = 1; i+advance < len(fields); advance++ {\n\t\t\tfj := fields[i+advance]\n\t\t\tif fj.name != name {\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tif advance == 1 { // Only one field with this name\n\t\t\tsd.fl = append(sd.fl, fi)\n\t\t\tsd.fm[name] = fi\n\t\t\tcontinue\n\t\t}\n\t\tdominant, ok := dominantField(fields[i : i+advance])\n\t\tif !ok || !sc.overwriteDuplicatedInlinedFields || errorOnDuplicates {\n\t\t\treturn nil, fmt.Errorf(\"struct %s has duplicated key %s\", t.String(), name)\n\t\t}\n\t\tsd.fl = append(sd.fl, dominant)\n\t\tsd.fm[name] = dominant\n\t}\n\n\tsort.Sort(byIndex(sd.fl))\n\n\treturn sd, nil\n}\n\n// dominantField looks through the fields, all of which are known to\n// have the same name, to find the single field that dominates the\n// others using Go's inlining rules. If there are multiple top-level\n// fields, the boolean will be false: This condition is an error in Go\n// and we skip all the fields.\nfunc dominantField(fields []fieldDescription) (fieldDescription, bool) {\n\t// The fields are sorted in increasing index-length order, then by presence of tag.\n\t// That means that the first field is the dominant one. We need only check\n\t// for error cases: two fields at top level.\n\tif len(fields) > 1 &&\n\t\tlen(fields[0].inline) == len(fields[1].inline) {\n\t\treturn fieldDescription{}, false\n\t}\n\treturn fields[0], true\n}\n\nfunc fieldByIndexErr(v reflect.Value, index []int) (result reflect.Value, err error) {\n\tdefer func() {\n\t\tif recovered := recover(); recovered != nil {\n\t\t\tswitch r := recovered.(type) {\n\t\t\tcase string:\n\t\t\t\terr = fmt.Errorf(\"%s\", r)\n\t\t\tcase error:\n\t\t\t\terr = r\n\t\t\t}\n\t\t}\n\t}()\n\n\tresult = v.FieldByIndex(index)\n\treturn\n}\n\nfunc getInlineField(val reflect.Value, index []int) (reflect.Value, error) {\n\tfield, err := fieldByIndexErr(val, index)\n\tif err == nil {\n\t\treturn field, nil\n\t}\n\n\t// if parent of this element doesn't exist, fix its parent\n\tinlineParent := index[:len(index)-1]\n\tvar fParent reflect.Value\n\tif fParent, err = fieldByIndexErr(val, inlineParent); err != nil {\n\t\tfParent, err = getInlineField(val, inlineParent)\n\t\tif err != nil {\n\t\t\treturn fParent, err\n\t\t}\n\t}\n\tfParent.Set(reflect.New(fParent.Type().Elem()))\n\n\treturn fieldByIndexErr(val, index)\n}\n\n// DeepZero returns recursive zero object\nfunc deepZero(st reflect.Type) (result reflect.Value) {\n\tif st.Kind() == reflect.Struct {\n\t\tnumField := st.NumField()\n\t\tfor i := 0; i < numField; i++ {\n\t\t\tif result == emptyValue {\n\t\t\t\tresult = reflect.Indirect(reflect.New(st))\n\t\t\t}\n\t\t\tf := result.Field(i)\n\t\t\tif f.CanInterface() {\n\t\t\t\tif f.Type().Kind() == reflect.Struct {\n\t\t\t\t\tresult.Field(i).Set(recursivePointerTo(deepZero(f.Type().Elem())))\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn result\n}\n\n// recursivePointerTo calls reflect.New(v.Type) but recursively for its fields inside\nfunc recursivePointerTo(v reflect.Value) reflect.Value {\n\tv = reflect.Indirect(v)\n\tresult := reflect.New(v.Type())\n\tif v.Kind() == reflect.Struct {\n\t\tfor i := 0; i < v.NumField(); i++ {\n\t\t\tif f := v.Field(i); f.Kind() == reflect.Ptr {\n\t\t\t\tif f.Elem().Kind() == reflect.Struct {\n\t\t\t\t\tresult.Elem().Field(i).Set(recursivePointerTo(f))\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn result\n}\n"
  },
  {
    "path": "bson/struct_codec_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bson\n\nimport (\n\t\"reflect\"\n\t\"testing\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n)\n\nfunc TestIsZero(t *testing.T) {\n\tt.Parallel()\n\ttestCases := []struct {\n\t\tdescription    string\n\t\tvalue          any\n\t\tomitZeroStruct bool\n\t\twant           bool\n\t}{\n\t\t{\n\t\t\tdescription: \"false\",\n\t\t\tvalue:       false,\n\t\t\twant:        true,\n\t\t},\n\t\t{\n\t\t\tdescription: \"0\",\n\t\t\tvalue:       0,\n\t\t\twant:        true,\n\t\t},\n\t\t{\n\t\t\tdescription: \"nil pointer to int\",\n\t\t\tvalue:       (*int)(nil),\n\t\t\twant:        true,\n\t\t},\n\t\t{\n\t\t\tdescription: \"time.Time\",\n\t\t\tvalue:       time.Unix(1682123781, 0),\n\t\t\twant:        false,\n\t\t},\n\t\t{\n\t\t\tdescription: \"empty time.Time\",\n\t\t\tvalue:       time.Time{},\n\t\t\twant:        true,\n\t\t},\n\t\t{\n\t\t\tdescription: \"nil pointer to time.Time\",\n\t\t\tvalue:       (*time.Time)(nil),\n\t\t\twant:        true,\n\t\t},\n\t\t{\n\t\t\tdescription: \"zero struct\",\n\t\t\tvalue:       struct{ Val bool }{},\n\t\t\twant:        false,\n\t\t},\n\t\t{\n\t\t\tdescription: \"non-zero struct\",\n\t\t\tvalue:       struct{ Val bool }{Val: true},\n\t\t\twant:        false,\n\t\t},\n\t\t{\n\t\t\tdescription: \"nil pointer to struct\",\n\t\t\tvalue:       (*struct{ Val bool })(nil),\n\t\t\twant:        true,\n\t\t},\n\t\t{\n\t\t\tdescription: \"pointer to struct\",\n\t\t\tvalue:       &struct{ Val bool }{},\n\t\t\twant:        false,\n\t\t},\n\t\t{\n\t\t\tdescription: \"zero struct that implements Zeroer\",\n\t\t\tvalue:       zeroTest{},\n\t\t\twant:        false,\n\t\t},\n\t\t{\n\t\t\tdescription: \"non-zero struct that implements Zeroer\",\n\t\t\tvalue:       zeroTest{reportZero: true},\n\t\t\twant:        true,\n\t\t},\n\t\t{\n\t\t\tdescription: \"pointer to zero struct that implements Zeroer\",\n\t\t\tvalue:       &zeroTest{},\n\t\t\twant:        false,\n\t\t},\n\t\t{\n\t\t\tdescription: \"pointer to non-zero struct that implements Zeroer\",\n\t\t\tvalue:       &zeroTest{reportZero: true},\n\t\t\twant:        true,\n\t\t},\n\t\t{\n\t\t\tdescription:    \"zero struct with omitZeroStruct\",\n\t\t\tvalue:          struct{ Val bool }{},\n\t\t\tomitZeroStruct: true,\n\t\t\twant:           true,\n\t\t},\n\t\t{\n\t\t\tdescription:    \"non-zero struct with omitZeroStruct\",\n\t\t\tvalue:          struct{ Val bool }{Val: true},\n\t\t\tomitZeroStruct: true,\n\t\t\twant:           false,\n\t\t},\n\t\t{\n\t\t\tdescription:    \"zero struct with only private fields omitZeroStruct\",\n\t\t\tvalue:          struct{ val bool }{},\n\t\t\tomitZeroStruct: true,\n\t\t\twant:           true,\n\t\t},\n\t\t// TODO(GODRIVER-2820): Change the expected value to \"false\" once the logic is updated to\n\t\t// TODO also inspect private struct fields.\n\t\t{\n\t\t\tdescription:    \"non-zero struct with only private fields with omitZeroStruct\",\n\t\t\tvalue:          struct{ val bool }{val: true},\n\t\t\tomitZeroStruct: true,\n\t\t\twant:           true,\n\t\t},\n\t\t{\n\t\t\tdescription:    \"pointer to zero struct with omitZeroStruct\",\n\t\t\tvalue:          &struct{ Val bool }{},\n\t\t\tomitZeroStruct: true,\n\t\t\twant:           false,\n\t\t},\n\t\t{\n\t\t\tdescription:    \"pointer to non-zero struct with omitZeroStruct\",\n\t\t\tvalue:          &struct{ Val bool }{Val: true},\n\t\t\tomitZeroStruct: true,\n\t\t\twant:           false,\n\t\t},\n\t\t{\n\t\t\tdescription: \"empty map\",\n\t\t\tvalue:       map[string]string{},\n\t\t\twant:        true,\n\t\t},\n\t\t{\n\t\t\tdescription: \"empty slice\",\n\t\t\tvalue:       []struct{}{},\n\t\t\twant:        true,\n\t\t},\n\t\t{\n\t\t\tdescription: \"empty string\",\n\t\t\tvalue:       \"\",\n\t\t\twant:        true,\n\t\t},\n\t}\n\n\tfor _, tc := range testCases {\n\t\ttc := tc // Capture range variable.\n\n\t\tt.Run(tc.description, func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tgot := isEmpty(reflect.ValueOf(tc.value), tc.omitZeroStruct)\n\t\t\tassert.Equal(t, tc.want, got, \"expected and actual isEmpty return are different\")\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "bson/struct_tag_parser.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bson\n\nimport (\n\t\"reflect\"\n\t\"strings\"\n)\n\n// structTags represents the struct tag fields that the StructCodec uses during\n// the encoding and decoding process.\n//\n// In the case of a struct, the lowercased field name is used as the key for each exported\n// field but this behavior may be changed using a struct tag. The tag may also contain flags to\n// adjust the marshalling behavior for the field.\n//\n// The properties are defined below:\n//\n//\tOmitEmpty  Only include the field if it's not set to the zero value for the type or to\n//\t           empty slices or maps.\n//\n//\tMinSize    Marshal an integer of a type larger than 32 bits value as an int32, if that's\n//\t           feasible while preserving the numeric value.\n//\n//\tTruncate   When unmarshaling a BSON double, it is permitted to lose precision to fit within\n//\t           a float32.\n//\n//\tInline     Inline the field, which must be a struct or a map, causing all of its fields\n//\t           or keys to be processed as if they were part of the outer struct. For maps,\n//\t           keys must not conflict with the bson keys of other struct fields.\n//\n//\tSkip       This struct field should be skipped. This is usually denoted by parsing a \"-\"\n//\t           for the name.\ntype structTags struct {\n\tName      string\n\tOmitEmpty bool\n\tMinSize   bool\n\tTruncate  bool\n\tInline    bool\n\tSkip      bool\n}\n\n// DefaultStructTagParser is the StructTagParser used by the StructCodec by default.\n// It will handle the bson struct tag. See the documentation for StructTags to see\n// what each of the returned fields means.\n//\n// If there is no name in the struct tag fields, the struct field name is lowercased.\n// The tag formats accepted are:\n//\n//\t\"[<key>][,<flag1>[,<flag2>]]\"\n//\n//\t`(...) bson:\"[<key>][,<flag1>[,<flag2>]]\" (...)`\n//\n// An example:\n//\n//\ttype T struct {\n//\t    A bool\n//\t    B int    \"myb\"\n//\t    C string \"myc,omitempty\"\n//\t    D string `bson:\",omitempty\" json:\"jsonkey\"`\n//\t    E int64  \",minsize\"\n//\t    F int64  \"myf,omitempty,minsize\"\n//\t}\n//\n// A struct tag either consisting entirely of '-' or with a bson key with a\n// value consisting entirely of '-' will return a StructTags with Skip true and\n// the remaining fields will be their default values.\nfunc parseStructTags(sf reflect.StructField) (*structTags, error) {\n\tkey := strings.ToLower(sf.Name)\n\ttag, ok := sf.Tag.Lookup(\"bson\")\n\tif !ok && !strings.Contains(string(sf.Tag), \":\") && len(sf.Tag) > 0 {\n\t\ttag = string(sf.Tag)\n\t}\n\treturn parseTags(key, tag)\n}\n\n// jsonStructTagParser has the same behavior as DefaultStructTagParser\n// but will also fallback to parsing the json tag instead on a field where the\n// bson tag isn't available.\nfunc parseJSONStructTags(sf reflect.StructField) (*structTags, error) {\n\tkey := strings.ToLower(sf.Name)\n\ttag, ok := sf.Tag.Lookup(\"bson\")\n\tif !ok {\n\t\ttag, ok = sf.Tag.Lookup(\"json\")\n\t}\n\tif !ok && !strings.Contains(string(sf.Tag), \":\") && len(sf.Tag) > 0 {\n\t\ttag = string(sf.Tag)\n\t}\n\n\treturn parseTags(key, tag)\n}\n\nfunc parseTags(key string, tag string) (*structTags, error) {\n\tvar st structTags\n\tif tag == \"-\" {\n\t\tst.Skip = true\n\t\treturn &st, nil\n\t}\n\n\tfor idx, str := range strings.Split(tag, \",\") {\n\t\tif idx == 0 && str != \"\" {\n\t\t\tkey = str\n\t\t}\n\t\tswitch str {\n\t\tcase \"omitempty\":\n\t\t\tst.OmitEmpty = true\n\t\tcase \"minsize\":\n\t\t\tst.MinSize = true\n\t\tcase \"truncate\":\n\t\t\tst.Truncate = true\n\t\tcase \"inline\":\n\t\t\tst.Inline = true\n\t\t}\n\t}\n\n\tst.Name = key\n\n\treturn &st, nil\n}\n"
  },
  {
    "path": "bson/struct_tag_parser_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bson\n\nimport (\n\t\"reflect\"\n\t\"testing\"\n\n\t\"github.com/google/go-cmp/cmp\"\n)\n\nfunc TestStructTagParsers(t *testing.T) {\n\ttestCases := []struct {\n\t\tname   string\n\t\tsf     reflect.StructField\n\t\twant   *structTags\n\t\tparser func(reflect.StructField) (*structTags, error)\n\t}{\n\t\t{\n\t\t\t\"default no bson tag\",\n\t\t\treflect.StructField{Name: \"foo\", Tag: reflect.StructTag(\"bar\")},\n\t\t\t&structTags{Name: \"bar\"},\n\t\t\tparseStructTags,\n\t\t},\n\t\t{\n\t\t\t\"default empty\",\n\t\t\treflect.StructField{Name: \"foo\", Tag: reflect.StructTag(\"\")},\n\t\t\t&structTags{Name: \"foo\"},\n\t\t\tparseStructTags,\n\t\t},\n\t\t{\n\t\t\t\"default tag only dash\",\n\t\t\treflect.StructField{Name: \"foo\", Tag: reflect.StructTag(\"-\")},\n\t\t\t&structTags{Skip: true},\n\t\t\tparseStructTags,\n\t\t},\n\t\t{\n\t\t\t\"default bson tag only dash\",\n\t\t\treflect.StructField{Name: \"foo\", Tag: reflect.StructTag(`bson:\"-\"`)},\n\t\t\t&structTags{Skip: true},\n\t\t\tparseStructTags,\n\t\t},\n\t\t{\n\t\t\t\"default all options\",\n\t\t\treflect.StructField{Name: \"foo\", Tag: reflect.StructTag(`bar,omitempty,minsize,truncate,inline`)},\n\t\t\t&structTags{Name: \"bar\", OmitEmpty: true, MinSize: true, Truncate: true, Inline: true},\n\t\t\tparseStructTags,\n\t\t},\n\t\t{\n\t\t\t\"default all options default name\",\n\t\t\treflect.StructField{Name: \"foo\", Tag: reflect.StructTag(`,omitempty,minsize,truncate,inline`)},\n\t\t\t&structTags{Name: \"foo\", OmitEmpty: true, MinSize: true, Truncate: true, Inline: true},\n\t\t\tparseStructTags,\n\t\t},\n\t\t{\n\t\t\t\"default bson tag all options\",\n\t\t\treflect.StructField{Name: \"foo\", Tag: reflect.StructTag(`bson:\"bar,omitempty,minsize,truncate,inline\"`)},\n\t\t\t&structTags{Name: \"bar\", OmitEmpty: true, MinSize: true, Truncate: true, Inline: true},\n\t\t\tparseStructTags,\n\t\t},\n\t\t{\n\t\t\t\"default bson tag all options default name\",\n\t\t\treflect.StructField{Name: \"foo\", Tag: reflect.StructTag(`bson:\",omitempty,minsize,truncate,inline\"`)},\n\t\t\t&structTags{Name: \"foo\", OmitEmpty: true, MinSize: true, Truncate: true, Inline: true},\n\t\t\tparseStructTags,\n\t\t},\n\t\t{\n\t\t\t\"default ignore xml\",\n\t\t\treflect.StructField{Name: \"foo\", Tag: reflect.StructTag(`xml:\"bar\"`)},\n\t\t\t&structTags{Name: \"foo\"},\n\t\t\tparseStructTags,\n\t\t},\n\t\t{\n\t\t\t\"JSONFallback no bson tag\",\n\t\t\treflect.StructField{Name: \"foo\", Tag: reflect.StructTag(\"bar\")},\n\t\t\t&structTags{Name: \"bar\"},\n\t\t\tparseStructTags,\n\t\t},\n\t\t{\n\t\t\t\"JSONFallback empty\",\n\t\t\treflect.StructField{Name: \"foo\", Tag: reflect.StructTag(\"\")},\n\t\t\t&structTags{Name: \"foo\"},\n\t\t\tparseJSONStructTags,\n\t\t},\n\t\t{\n\t\t\t\"JSONFallback tag only dash\",\n\t\t\treflect.StructField{Name: \"foo\", Tag: reflect.StructTag(\"-\")},\n\t\t\t&structTags{Skip: true},\n\t\t\tparseJSONStructTags,\n\t\t},\n\t\t{\n\t\t\t\"JSONFallback bson tag only dash\",\n\t\t\treflect.StructField{Name: \"foo\", Tag: reflect.StructTag(`bson:\"-\"`)},\n\t\t\t&structTags{Skip: true},\n\t\t\tparseJSONStructTags,\n\t\t},\n\t\t{\n\t\t\t\"JSONFallback all options\",\n\t\t\treflect.StructField{Name: \"foo\", Tag: reflect.StructTag(`bar,omitempty,minsize,truncate,inline`)},\n\t\t\t&structTags{Name: \"bar\", OmitEmpty: true, MinSize: true, Truncate: true, Inline: true},\n\t\t\tparseJSONStructTags,\n\t\t},\n\t\t{\n\t\t\t\"JSONFallback all options default name\",\n\t\t\treflect.StructField{Name: \"foo\", Tag: reflect.StructTag(`,omitempty,minsize,truncate,inline`)},\n\t\t\t&structTags{Name: \"foo\", OmitEmpty: true, MinSize: true, Truncate: true, Inline: true},\n\t\t\tparseJSONStructTags,\n\t\t},\n\t\t{\n\t\t\t\"JSONFallback bson tag all options\",\n\t\t\treflect.StructField{Name: \"foo\", Tag: reflect.StructTag(`bson:\"bar,omitempty,minsize,truncate,inline\"`)},\n\t\t\t&structTags{Name: \"bar\", OmitEmpty: true, MinSize: true, Truncate: true, Inline: true},\n\t\t\tparseJSONStructTags,\n\t\t},\n\t\t{\n\t\t\t\"JSONFallback bson tag all options default name\",\n\t\t\treflect.StructField{Name: \"foo\", Tag: reflect.StructTag(`bson:\",omitempty,minsize,truncate,inline\"`)},\n\t\t\t&structTags{Name: \"foo\", OmitEmpty: true, MinSize: true, Truncate: true, Inline: true},\n\t\t\tparseJSONStructTags,\n\t\t},\n\t\t{\n\t\t\t\"JSONFallback json tag all options\",\n\t\t\treflect.StructField{Name: \"foo\", Tag: reflect.StructTag(`json:\"bar,omitempty,minsize,truncate,inline\"`)},\n\t\t\t&structTags{Name: \"bar\", OmitEmpty: true, MinSize: true, Truncate: true, Inline: true},\n\t\t\tparseJSONStructTags,\n\t\t},\n\t\t{\n\t\t\t\"JSONFallback json tag all options default name\",\n\t\t\treflect.StructField{Name: \"foo\", Tag: reflect.StructTag(`json:\",omitempty,minsize,truncate,inline\"`)},\n\t\t\t&structTags{Name: \"foo\", OmitEmpty: true, MinSize: true, Truncate: true, Inline: true},\n\t\t\tparseJSONStructTags,\n\t\t},\n\t\t{\n\t\t\t\"JSONFallback bson tag overrides other tags\",\n\t\t\treflect.StructField{Name: \"foo\", Tag: reflect.StructTag(`bson:\"bar\" json:\"qux,truncate\"`)},\n\t\t\t&structTags{Name: \"bar\"},\n\t\t\tparseJSONStructTags,\n\t\t},\n\t\t{\n\t\t\t\"JSONFallback ignore xml\",\n\t\t\treflect.StructField{Name: \"foo\", Tag: reflect.StructTag(`xml:\"bar\"`)},\n\t\t\t&structTags{Name: \"foo\"},\n\t\t\tparseJSONStructTags,\n\t\t},\n\t}\n\n\tfor _, tc := range testCases {\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\tgot, err := tc.parser(tc.sf)\n\t\t\tnoerr(t, err)\n\t\t\tif !cmp.Equal(got, tc.want) {\n\t\t\t\tt.Errorf(\"Returned struct tags do not match. got %#v; want %#v\", got, tc.want)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "bson/testdata/fuzz/FuzzDecode/002ae7d43f636100116fede772a03d07726ed75c3c3b83da865fe9b718adf8ae",
    "content": "go test fuzz v1\n[]byte(\"\\x10\\x00\\x00\\x00\\v\\x00\\x00\\x00\\b\\x00\\x00\\v\\x00\\x00\\x00\\x00\")\n"
  },
  {
    "path": "bson/testdata/fuzz/FuzzDecode/0de854041b0055ca1e5e6e54a7fb667ed38461db171af267665c21776f9a9ef4",
    "content": "go test fuzz v1\n[]byte(\"0\\\\x00\\\\x00\\\\x00\\\\x0f\\\\x00000\\\\x8a00000000000000000000000000000000000000\\n\")\n"
  },
  {
    "path": "bson/testdata/fuzz/FuzzDecode/718592474a0a3626039f3471449b9aa374c746754d4925fcfe4ba747e7101504",
    "content": "go test fuzz v1\n[]byte(\"\\\\x80\\\\x00\\\\x00\\\\x00\\\\x03000000\\\\x00s\\\\x00\\\\x00\\\\x00\\\\x0300000\\\\x00g\\\\x00\\\\x00\\\\x00\\\\x100z\\\\x000000\\\\x11\\\\x00000\\\\x150000\\\\x020\\\\x00\\\\x02\\\\x00\\\\x00\\\\x000\\\\x12\\\\x00\\\\x050\\\\x00\\\\x01\\\\x00\\\\x00\\\\x0000\\\\x050\\\\x00\\\\x01\\\\x00\\\\x00\\\\x0000\\\\x040\\\\x00200000\\\\x00\\\\x000\\\\x02\\\\x00\\\\x10\\\\x0000000\\\\x110\\\\x0000000000\\\\x020\\\\x00\\\\x02\\\\x00\\\\x00\\\\x000\\\\x00\\\\x050\\\\x00\\\\x01\\\\x00\\\\x00\\\\x0000\\\\x050\\\\x00\\\\x01\\\\x00\\\\x00\\\\x0000\\\\x00\\\\x00\\\\x00\\\\x00\\n\")\n"
  },
  {
    "path": "bson/testdata/fuzz/FuzzDecode/93c43e3c1cf35c19b7618a618d128cea0ce05cef0711fdd91e403fe3b2f45628",
    "content": "go test fuzz v1\n[]byte(\"\\\\x59\\\\x01\\\\x00\\\\x00\\\\x01\\\\x64\\\\x6f\\\\x75\\\\x62\\\\x6c\\\\x65\\\\x00\\\\x9a\\\\x99\\\\x99\\\\x99\\\\x99\\\\x99\\\\xf1\\\\x3f\\\\x02\\\\x73\\\\x74\\\\x72\\\\x69\\\\x6e\\\\x67\\\\x00\\\\x06\\\\x00\\\\x00\\\\x00\\\\x68\\\\x65\\\\x6c\\\\x6c\\\\x6f\\\\x00\\\\x03\\\\x65\\\\x6d\\\\x62\\\\x65\\\\x64\\\\x64\\\\x65\\\\x64\\\\x00\\\\x4b\\\\x00\\\\x00\\\\x00\\\\x04\\\\x61\\\\x72\\\\x72\\\\x61\\\\x79\\\\x00\\\\x3f\\\\x00\\\\x00\\\\x00\\\\x10\\\\x30\\\\x00\\\\x01\\\\x00\\\\x00\\\\x00\\\\x01\\\\x31\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x40\\\\x02\\\\x32\\\\x00\\\\x02\\\\x00\\\\x00\\\\x00\\\\x33\\\\x00\\\\x04\\\\x33\\\\x00\\\\x0c\\\\x00\\\\x00\\\\x00\\\\x10\\\\x30\\\\x00\\\\x04\\\\x00\\\\x00\\\\x00\\\\x00\\\\x03\\\\x34\\\\x00\\\\x0d\\\\x00\\\\x00\\\\x00\\\\x03\\\\x35\\\\x00\\\\x05\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x05\\\\x62\\\\x69\\\\x6e\\\\x61\\\\x72\\\\x79\\\\x00\\\\x03\\\\x00\\\\x00\\\\x00\\\\x00\\\\x01\\\\x02\\\\x03\\\\x07\\\\x6f\\\\x62\\\\x6a\\\\x65\\\\x63\\\\x74\\\\x69\\\\x64\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x08\\\\x62\\\\x6f\\\\x6f\\\\x6c\\\\x65\\\\x61\\\\x6e\\\\x00\\\\x01\\\\x09\\\\x64\\\\x61\\\\x74\\\\x65\\\\x74\\\\x69\\\\x6d\\\\x65\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x0a\\\\x6e\\\\x75\\\\x6c\\\\x6c\\\\x00\\\\x0b\\\\x72\\\\x65\\\\x67\\\\x65\\\\x78\\\\x00\\\\x68\\\\x65\\\\x6c\\\\x6c\\\\x6f\\\\x00\\\\x69\\\\x00\\\\x0d\\\\x6a\\\\x73\\\\x00\\\\x0e\\\\x00\\\\x00\\\\x00\\\\x66\\\\x75\\\\x6e\\\\x63\\\\x74\\\\x69\\\\x6f\\\\x6e\\\\x28\\\\x29\\\\x20\\\\x7b\\\\x7d\\\\x00\\\\x0f\\\\x73\\\\x63\\\\x6f\\\\x70\\\\x65\\\\x00\\\\x2c\\\\x00\\\\x00\\\\x00\\\\x0e\\\\x00\\\\x00\\\\x00\\\\x66\\\\x75\\\\x6e\\\\x63\\\\x74\\\\x69\\\\x6f\\\\x6e\\\\x28\\\\x29\\\\x20\\\\x7b\\\\x7d\\\\x00\\\\x16\\\\x00\\\\x00\\\\x00\\\\x02\\\\x68\\\\x65\\\\x6c\\\\x6c\\\\x6f\\\\x00\\\\x06\\\\x00\\\\x00\\\\x00\\\\x77\\\\x6f\\\\x72\\\\x6c\\\\x64\\\\x00\\\\x00\\\\x10\\\\x69\\\\x6e\\\\x74\\\\x33\\\\x32\\\\x00\\\\x20\\\\x00\\\\x00\\\\x00\\\\x11\\\\x74\\\\x69\\\\x6d\\\\x65\\\\x73\\\\x74\\\\x61\\\\x6d\\\\x70\\\\x00\\\\x02\\\\x00\\\\x00\\\\x00\\\\x01\\\\x00\\\\x00\\\\x00\\\\x12\\\\x69\\\\x6e\\\\x74\\\\x36\\\\x34\\\\x00\\\\x40\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\x00\\\\xff\\\\x6d\\\\x69\\\\x6e\\\\x6b\\\\x65\\\\x79\\\\x00\\\\x7f\\\\x6d\\\\x61\\\\x78\\\\x6b\\\\x65\\\\x79\\\\x00\\\\x00\\\"\\n\")\n"
  },
  {
    "path": "bson/testdata/fuzz/FuzzDecode/c3ffbb42eb85b743ede396f00b7706e6ad0529c32689c63ca663dae37d072627",
    "content": "go test fuzz v1\n[]byte(\"\\\\x05\\\\xf0\\\\xff\\\\x00\\\\x7f\\n\")\n"
  },
  {
    "path": "bson/testdata/lorem.txt",
    "content": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc hendrerit nibh eu lorem scelerisque iaculis. Mauris tincidunt est quis gravida sollicitudin. Vestibulum ante dolor, semper eget finibus accumsan, finibus id nulla. Nam mauris libero, mattis sed libero at, vestibulum vehicula tellus. Nullam in finibus neque. Ut bibendum blandit condimentum. Etiam tortor mauris, tempus et accumsan a, lobortis cursus lacus.  Pellentesque fermentum gravida aliquam. Donec fermentum nisi ut venenatis convallis. Praesent ultrices, justo ac fringilla eleifend, augue libero tristique quam, imperdiet rutrum lectus odio id purus. Curabitur vel metus ullamcorper massa dapibus tincidunt. Nam mollis feugiat sapien ut sagittis. Cras sed ex ligula. Etiam nec nisi diam. Pellentesque id metus molestie, tempus velit et, malesuada eros. Praesent convallis arcu et semper molestie. Morbi eu orci tempor, blandit dui egestas, faucibus diam. Aenean ac tempor justo.  Donec id lectus tristique, ultrices enim sed, suscipit sapien. In arcu mauris, venenatis sed venenatis id, mollis a risus. Phasellus id massa et magna volutpat elementum in quis libero. Aliquam a aliquam erat. Nunc leo massa, sagittis at egestas vel, iaculis vitae quam. Pellentesque placerat metus id velit lobortis euismod in non sapien. Duis a quam sed elit fringilla maximus at et purus. Nullam pharetra accumsan efficitur. Integer a mattis urna. Suspendisse vehicula, nunc euismod luctus suscipit, mi mauris sollicitudin diam, porta rhoncus libero metus vitae erat. Sed lacus sem, feugiat vitae nisi at, malesuada ultricies ipsum. Quisque hendrerit posuere metus. Donec magna erat, facilisis et dictum at, tempor in leo. Maecenas luctus vestibulum quam, eu ornare ex aliquam vitae. Mauris ac mauris posuere, mattis nisl nec, fringilla libero.  Nulla in ipsum ut arcu condimentum mollis. Donec viverra massa nec lacus condimentum vulputate. Nulla at dictum eros, quis sodales ante. Duis condimentum accumsan consectetur. Aenean sodales at turpis vitae efficitur. Vestibulum in diam faucibus, consequat sem ut, auctor lorem. Duis tincidunt non eros pretium rhoncus. Sed quis eros ligula. Donec vulputate ultrices enim, quis interdum dui rhoncus eu. In at mollis ex.  In sed pulvinar risus. Morbi efficitur leo magna, eget bibendum leo consequat id. Pellentesque ultricies ipsum leo, sit amet cursus est bibendum a. Mauris eget porta felis. Vivamus dignissim pellentesque risus eget interdum. Mauris ultrices metus at blandit tincidunt. Duis tempor sapien vel luctus mattis. Vivamus ac orci nibh. Nam eget tempor neque. Proin lacus nibh, porttitor nec pellentesque id, dignissim et eros. Nullam et libero faucibus, iaculis mi sed, faucibus leo. In mollis sem ac porta suscipit.  Ut rutrum, justo a gravida lobortis, neque nibh tincidunt mi, id eleifend dolor dolor vel arcu. Fusce vel egestas ante, eu commodo eros. Donec augue odio, bibendum ut nulla ultricies, cursus eleifend lacus. Nulla viverra ac massa vel placerat. Duis aliquam, ipsum vitae ultricies imperdiet, tellus nisl venenatis mauris, et congue elit nulla vitae est. Suspendisse volutpat ullamcorper diam, et vehicula leo bibendum at. In hac habitasse platea dictumst.  Donec mattis neque a lorem ullamcorper rutrum. Curabitur mattis auctor velit, vitae iaculis mauris convallis in. Donec vulputate sapien non ex pretium semper. Vestibulum ut ligula sit amet arcu pellentesque aliquam. Pellentesque odio diam, pharetra at nunc varius, pharetra consectetur sem. Integer pretium magna pretium, mattis lectus eget, laoreet libero. Morbi dictum et dolor eu finibus. Etiam vestibulum finibus quam, vel eleifend tortor mattis viverra. Donec porttitor est vitae ligula volutpat lobortis. Donec ac semper diam. Maecenas sapien eros, blandit non velit in, faucibus auctor risus. In quam nibh, congue a nisl sit amet, tempor volutpat tortor. Curabitur dignissim auctor orci a varius. Nulla faucibus lacus libero, vitae fringilla elit facilisis id.  Aliquam id elit dui. Cras convallis ligula ac leo bibendum lacinia. Duis interdum ac lectus sed tristique. Maecenas sem magna, gravida quis sapien sit amet, varius luctus ligula. Curabitur eleifend mi nibh. Suspendisse iaculis commodo justo, vitae pretium risus scelerisque non. Sed pulvinar augue nec fermentum feugiat. Nam et ligula tellus. Vestibulum euismod accumsan nibh, at rutrum est tristique sit amet. Duis porttitor ex felis, quis consectetur nunc tempor ut. Nulla vitae consequat velit, id condimentum orci. Sed lacinia velit urna, nec laoreet est varius ac. Integer dapibus libero vel bibendum posuere. Curabitur cursus est vel ante euismod dapibus. Ut hendrerit odio id rhoncus efficitur.  Nam luctus sem orci, in congue ipsum ultrices at. Morbi sed tortor ut metus elementum ultrices. Cras vehicula ante magna, nec faucibus neque placerat et. Vivamus justo lacus, aliquet sit amet semper ac, porta vehicula nibh. Duis et rutrum elit. In nisi eros, fringilla ut odio eget, vehicula laoreet elit. Suspendisse potenti. Vivamus ut ultricies lacus. Integer pellentesque posuere mauris, eget aliquet purus tincidunt in. Suspendisse potenti. Nam quis purus iaculis, cursus mauris a, tempus mi.  Pellentesque ullamcorper lacus vitae lacus volutpat, quis ultricies metus sagittis. Etiam imperdiet libero vitae ante cursus tempus. Nulla eu mi sodales neque scelerisque eleifend id non nisi. Maecenas blandit vitae turpis nec lacinia. Duis posuere cursus metus. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridaculus mus. Ut ultrices hendrerit quam, sit amet condimentum massa finibus a. Donec et vehicula urna. Pellentesque lorem felis, fermentum vel feugiat et, congue eleifend orci. Suspendisse potenti. Sed dignissim massa at justo tristique, id feugiat neque vulputate.  Pellentesque dui massa, maximus quis consequat quis, fringilla vel turpis. Etiam fermentum ex eget massa varius, a fringilla velit placerat. Sed fringilla convallis urna, ut finibus purus accumsan a. Vestibulum porttitor risus lorem, eu venenatis velit suscipit vel. Ut nec diam egestas, sollicitudin nulla sit amet, porttitor felis. Duis a nisl a ante interdum hendrerit. Integer sollicitudin scelerisque ex, et blandit magna blandit at.  Vivamus a vulputate ante. Nam non tortor a lacus euismod venenatis. Vestibulum libero augue, consequat vitae turpis nec, mattis tristique nibh. Fusce pulvinar dolor vel ipsum eleifend varius. Morbi id ante eget tellus venenatis interdum non sit amet ante. Nulla luctus tempor purus, eget ultrices odio varius eget. Duis commodo eros ac molestie fermentum. Praesent vestibulum est eu massa posuere, et fermentum orci tincidunt. Duis dignissim nunc sit amet elit laoreet mollis. Aenean porttitor et nunc vel venenatis. Nunc viverra ligula nec tincidunt vehicula. Pellentesque in magna volutpat, consequat est eget, varius mauris. Maecenas in tellus eros. Aliquam erat volutpat. Phasellus blandit faucibus velit ac placerat.  Donec luctus hendrerit pretium. Sed mauris purus, lobortis non erat sed, mattis ornare nulla. Fusce eu vulputate lacus. In enim justo, elementum at tortor nec, interdum semper ligula. Donec condimentum erat elit, non luctus augue rhoncus et. Quisque interdum elit dui, in vestibulum lacus aliquet et. Mauris aliquam sed ante id eleifend. Donec velit dolor, blandit et mattis non, bibendum at lorem. Nullam blandit quam sapien. Duis rutrum nunc vitae odio imperdiet condimentum. Nunc vel pellentesque purus. Cras iaculis dui est, quis cursus tortor mattis non. Donec non tincidunt lacus.  Sed eget molestie lacus. Phasellus pharetra ullamcorper purus. Sed sit amet ultricies ligula, aliquam elementum velit. Cras commodo mauris vel sapien rutrum, ac pharetra libero mollis. Donec facilisis sit amet elit ac porttitor. Phasellus rutrum rhoncus sagittis. Interdum et malesuada fames ac ante ipsum primis in faucibus. Etiam iaculis ac odio eu sodales.  Proin blandit fermentum arcu efficitur ornare. Vestibulum pharetra est quis mi lobortis interdum. Proin molestie pretium viverra. Integer pellentesque eros nisi, non rutrum odio interdum ut. Quisque vel ante et mi placerat mollis ut eget eros. Etiam vitae orci lectus. Nulla scelerisque dui in dictum ornare. Aliquam vestibulum fringilla eros, id fermentum dolor euismod eget. Ut vitae massa a augue suscipit bibendum non ac mi. Pellentesque id ligula in sapien fermentum fermentum. In ut sem molestie, consectetur ex tristique, tempor risus.  Maecenas scelerisque, ex eget cursus ornare, dolor nisi condimentum tellus, in venenatis nibh elit rutrum turpis. Sed sed vestibulum ex, molestie sodales leo. Vivamus cursus aliquet consequat. Aliquam et enim eget lorem placerat egestas a at justo. Praesent congue vitae purus vel scelerisque. Praesent faucibus massa felis, non porttitor dolor varius at. Nam fringilla risus sit amet faucibus vestibulum. Aliquam rhoncus ex vel magna blandit, eu dapibus felis tristique. Nam dignissim vestibulum neque vitae suscipit. Nunc a pharetra dui. Etiam nec quam sed mauris pharetra finibus in a tellus. Ut vehicula molestie lectus in pretium. Donec sit amet dui purus.  Nunc in vestibulum sapien. Donec elit quam, mollis luctus gravida ac, ullamcorper quis urna. Vivamus a urna egestas velit tempor interdum non eget dui. Maecenas maximus diam at consequat dictum. Etiam sed metus quis enim faucibus cursus condimentum ut nisi. In et iaculis odio. Curabitur sollicitudin ultrices finibus. Aliquam et nisi porta, vehicula urna id, dictum turpis. Sed id iaculis justo, non semper metus.  Quisque euismod, tellus iaculis sagittis vestibulum, leo magna blandit felis, non pharetra velit lacus sed nunc. Curabitur mollis porttitor odio, sed feugiat leo rhoncus quis. Duis faucibus tellus id venenatis vestibulum. Duis interdum pretium cursus. Integer sed iaculis mi. Phasellus at odio at felis fermentum congue. Morbi at ante ut lacus posuere accumsan quis in orci. Nullam eget sapien eu nibh venenatis malesuada.  Nulla sed ligula et metus mattis placerat sed eget nisl. Nunc cursus et nulla id dictum. Vivamus efficitur aliquam. "
  },
  {
    "path": "bson/time_codec.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bson\n\nimport (\n\t\"fmt\"\n\t\"reflect\"\n\t\"time\"\n)\n\nconst (\n\ttimeFormatString = \"2006-01-02T15:04:05.999Z07:00\"\n)\n\n// timeCodec is the Codec used for time.Time values.\ntype timeCodec struct {\n\t// useLocalTimeZone specifies if we should decode into the local time zone. Defaults to false.\n\tuseLocalTimeZone bool\n}\n\n// Assert that timeCodec satisfies the typeDecoder interface, which allows it to be used\n// by collection type decoders (e.g. map, slice, etc) to set individual values in a collection.\nvar _ typeDecoder = &timeCodec{}\n\nfunc (tc *timeCodec) decodeType(dc DecodeContext, vr ValueReader, t reflect.Type) (reflect.Value, error) {\n\tif t != tTime {\n\t\treturn emptyValue, ValueDecoderError{\n\t\t\tName:     \"TimeDecodeValue\",\n\t\t\tTypes:    []reflect.Type{tTime},\n\t\t\tReceived: reflect.Zero(t),\n\t\t}\n\t}\n\n\tvar timeVal time.Time\n\tswitch vrType := vr.Type(); vrType {\n\tcase TypeDateTime:\n\t\tdt, err := vr.ReadDateTime()\n\t\tif err != nil {\n\t\t\treturn emptyValue, err\n\t\t}\n\t\ttimeVal = time.Unix(dt/1000, dt%1000*1000000)\n\tcase TypeString:\n\t\t// assume strings are in the isoTimeFormat\n\t\ttimeStr, err := vr.ReadString()\n\t\tif err != nil {\n\t\t\treturn emptyValue, err\n\t\t}\n\t\ttimeVal, err = time.Parse(timeFormatString, timeStr)\n\t\tif err != nil {\n\t\t\treturn emptyValue, err\n\t\t}\n\tcase TypeInt64:\n\t\ti64, err := vr.ReadInt64()\n\t\tif err != nil {\n\t\t\treturn emptyValue, err\n\t\t}\n\t\ttimeVal = time.Unix(i64/1000, i64%1000*1000000)\n\tcase TypeTimestamp:\n\t\tt, _, err := vr.ReadTimestamp()\n\t\tif err != nil {\n\t\t\treturn emptyValue, err\n\t\t}\n\t\ttimeVal = time.Unix(int64(t), 0)\n\tcase TypeNull:\n\t\tif err := vr.ReadNull(); err != nil {\n\t\t\treturn emptyValue, err\n\t\t}\n\tcase TypeUndefined:\n\t\tif err := vr.ReadUndefined(); err != nil {\n\t\t\treturn emptyValue, err\n\t\t}\n\tdefault:\n\t\treturn emptyValue, fmt.Errorf(\"cannot decode %v into a time.Time\", vrType)\n\t}\n\n\tif !tc.useLocalTimeZone && !dc.useLocalTimeZone {\n\t\ttimeVal = timeVal.UTC()\n\t}\n\treturn reflect.ValueOf(timeVal), nil\n}\n\n// DecodeValue is the ValueDecoderFunc for time.Time.\nfunc (tc *timeCodec) DecodeValue(dc DecodeContext, vr ValueReader, val reflect.Value) error {\n\tif !val.CanSet() || val.Type() != tTime {\n\t\treturn ValueDecoderError{Name: \"TimeDecodeValue\", Types: []reflect.Type{tTime}, Received: val}\n\t}\n\n\telem, err := tc.decodeType(dc, vr, tTime)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tval.Set(elem)\n\treturn nil\n}\n\n// EncodeValue is the ValueEncoderFunc for time.Time.\nfunc (tc *timeCodec) EncodeValue(_ EncodeContext, vw ValueWriter, val reflect.Value) error {\n\tif !val.IsValid() || val.Type() != tTime {\n\t\treturn ValueEncoderError{Name: \"TimeEncodeValue\", Types: []reflect.Type{tTime}, Received: val}\n\t}\n\ttt := val.Interface().(time.Time)\n\tdt := NewDateTimeFromTime(tt)\n\treturn vw.WriteDateTime(int64(dt))\n}\n"
  },
  {
    "path": "bson/time_codec_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bson\n\nimport (\n\t\"reflect\"\n\t\"testing\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n)\n\nfunc TestTimeCodec(t *testing.T) {\n\tnow := time.Now().Truncate(time.Millisecond)\n\n\tt.Run(\"UseLocalTimeZone\", func(t *testing.T) {\n\t\treader := &valueReaderWriter{BSONType: TypeDateTime, Return: now.UnixNano() / int64(time.Millisecond)}\n\t\ttestCases := []struct {\n\t\t\tname      string\n\t\t\ttimeCodec *timeCodec\n\t\t\tutc       bool\n\t\t}{\n\t\t\t{\"default\", &timeCodec{}, true},\n\t\t\t{\"false\", &timeCodec{useLocalTimeZone: false}, true},\n\t\t\t{\"true\", &timeCodec{useLocalTimeZone: true}, false},\n\t\t}\n\t\tfor _, tc := range testCases {\n\t\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\t\tactual := reflect.New(reflect.TypeOf(now)).Elem()\n\t\t\t\terr := tc.timeCodec.DecodeValue(DecodeContext{}, reader, actual)\n\t\t\t\tassert.Nil(t, err, \"TimeCodec.DecodeValue error: %v\", err)\n\n\t\t\t\tactualTime := actual.Interface().(time.Time)\n\t\t\t\tassert.Equal(t, actualTime.Location().String() == \"UTC\", tc.utc,\n\t\t\t\t\t\"Expected UTC: %v, got %v\", tc.utc, actualTime.Location())\n\n\t\t\t\tif tc.utc {\n\t\t\t\t\tnowUTC := now.UTC()\n\t\t\t\t\tassert.Equal(t, nowUTC, actualTime, \"expected time %v, got %v\", nowUTC, actualTime)\n\t\t\t\t} else {\n\t\t\t\t\tassert.Equal(t, now, actualTime, \"expected time %v, got %v\", now, actualTime)\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"DecodeFromBsontype\", func(t *testing.T) {\n\t\ttestCases := []struct {\n\t\t\tname   string\n\t\t\treader *valueReaderWriter\n\t\t}{\n\t\t\t{\"string\", &valueReaderWriter{BSONType: TypeString, Return: now.Format(timeFormatString)}},\n\t\t\t{\"int64\", &valueReaderWriter{BSONType: TypeInt64, Return: now.Unix()*1000 + int64(now.Nanosecond()/1e6)}},\n\t\t\t{\n\t\t\t\t\"timestamp\", &valueReaderWriter{\n\t\t\t\t\tBSONType: TypeTimestamp,\n\t\t\t\t\tReturn: bsoncore.Value{\n\t\t\t\t\t\tType: bsoncore.TypeTimestamp,\n\t\t\t\t\t\tData: bsoncore.AppendTimestamp(nil, uint32(now.Unix()), 0),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t}\n\t\tfor _, tc := range testCases {\n\t\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\t\tactual := reflect.New(reflect.TypeOf(now)).Elem()\n\t\t\t\terr := (&timeCodec{}).DecodeValue(DecodeContext{}, tc.reader, actual)\n\t\t\t\tassert.Nil(t, err, \"DecodeValue error: %v\", err)\n\n\t\t\t\tactualTime := actual.Interface().(time.Time)\n\t\t\t\tif tc.name == \"timestamp\" {\n\t\t\t\t\tnow = time.Unix(now.Unix(), 0)\n\t\t\t\t}\n\t\t\t\tnowUTC := now.UTC()\n\t\t\t\tassert.Equal(t, nowUTC, actualTime, \"expected time %v, got %v\", nowUTC, actualTime)\n\t\t\t})\n\t\t}\n\t})\n}\n"
  },
  {
    "path": "bson/truncation_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2022-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bson\n\nimport (\n\t\"bytes\"\n\t\"testing\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n)\n\ntype inputArgs struct {\n\tName string\n\tVal  *float64\n}\n\ntype outputArgs struct {\n\tName string\n\tVal  *int64\n}\n\nfunc unmarshalWithContext(t *testing.T, dc DecodeContext, data []byte, val any) error {\n\tt.Helper()\n\n\tvr := NewDocumentReader(bytes.NewReader(data))\n\treturn unmarshalFromReader(dc, vr, val)\n}\n\nfunc TestTruncation(t *testing.T) {\n\tt.Run(\"truncation\", func(t *testing.T) {\n\t\tinputName := \"truncation\"\n\t\tinputVal := 4.7892\n\n\t\tinput := inputArgs{Name: inputName, Val: &inputVal}\n\n\t\tbuf := new(bytes.Buffer)\n\t\tvw := NewDocumentWriter(buf)\n\t\tenc := NewEncoder(vw)\n\t\tenc.IntMinSize()\n\t\tenc.SetRegistry(defaultRegistry)\n\t\terr := enc.Encode(&input)\n\t\tassert.Nil(t, err)\n\n\t\tvar output outputArgs\n\t\tdc := DecodeContext{\n\t\t\tRegistry: defaultRegistry,\n\t\t\ttruncate: true,\n\t\t}\n\n\t\terr = unmarshalWithContext(t, dc, buf.Bytes(), &output)\n\t\tassert.Nil(t, err)\n\n\t\tassert.Equal(t, inputName, output.Name)\n\t\tassert.Equal(t, int64(inputVal), *output.Val)\n\t})\n\tt.Run(\"no truncation\", func(t *testing.T) {\n\t\tinputName := \"no truncation\"\n\t\tinputVal := 7.382\n\n\t\tinput := inputArgs{Name: inputName, Val: &inputVal}\n\n\t\tbuf := new(bytes.Buffer)\n\t\tvw := NewDocumentWriter(buf)\n\t\tenc := NewEncoder(vw)\n\t\tenc.IntMinSize()\n\t\tenc.SetRegistry(defaultRegistry)\n\t\terr := enc.Encode(&input)\n\t\tassert.Nil(t, err)\n\n\t\tvar output outputArgs\n\t\tdc := DecodeContext{\n\t\t\tRegistry: defaultRegistry,\n\t\t\ttruncate: false,\n\t\t}\n\n\t\t// case throws an error when truncation is disabled\n\t\terr = unmarshalWithContext(t, dc, buf.Bytes(), &output)\n\t\tassert.NotNil(t, err)\n\t})\n}\n"
  },
  {
    "path": "bson/type_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bson\n\nimport (\n\t\"testing\"\n)\n\nfunc TestType(t *testing.T) {\n\ttestCases := []struct {\n\t\tname string\n\t\tt    Type\n\t\twant string\n\t}{\n\t\t{\"double\", TypeDouble, \"double\"},\n\t\t{\"string\", TypeString, \"string\"},\n\t\t{\"embedded document\", TypeEmbeddedDocument, \"embedded document\"},\n\t\t{\"array\", TypeArray, \"array\"},\n\t\t{\"binary\", TypeBinary, \"binary\"},\n\t\t{\"undefined\", TypeUndefined, \"undefined\"},\n\t\t{\"objectID\", TypeObjectID, \"objectID\"},\n\t\t{\"boolean\", TypeBoolean, \"boolean\"},\n\t\t{\"UTC datetime\", TypeDateTime, \"UTC datetime\"},\n\t\t{\"null\", TypeNull, \"null\"},\n\t\t{\"regex\", TypeRegex, \"regex\"},\n\t\t{\"dbPointer\", TypeDBPointer, \"dbPointer\"},\n\t\t{\"javascript\", TypeJavaScript, \"javascript\"},\n\t\t{\"symbol\", TypeSymbol, \"symbol\"},\n\t\t{\"code with scope\", TypeCodeWithScope, \"code with scope\"},\n\t\t{\"32-bit integer\", TypeInt32, \"32-bit integer\"},\n\t\t{\"timestamp\", TypeTimestamp, \"timestamp\"},\n\t\t{\"64-bit integer\", TypeInt64, \"64-bit integer\"},\n\t\t{\"128-bit decimal\", TypeDecimal128, \"128-bit decimal\"},\n\t\t{\"max key\", TypeMaxKey, \"max key\"},\n\t\t{\"min key\", TypeMinKey, \"min key\"},\n\t\t{\"invalid\", (0), \"invalid\"},\n\t}\n\n\tfor _, tc := range testCases {\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\tgot := tc.t.String()\n\t\t\tif got != tc.want {\n\t\t\t\tt.Errorf(\"String outputs do not match. got %s; want %s\", got, tc.want)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "bson/types.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bson\n\nimport (\n\t\"encoding/json\"\n\t\"net/url\"\n\t\"reflect\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n)\n\n// Type represents a BSON type.\ntype Type byte\n\n// String returns the string representation of the BSON type's name.\nfunc (bt Type) String() string {\n\treturn bsoncore.Type(bt).String()\n}\n\n// IsValid will return true if the Type is valid.\nfunc (bt Type) IsValid() bool {\n\tswitch bt {\n\tcase TypeDouble, TypeString, TypeEmbeddedDocument, TypeArray, TypeBinary,\n\t\tTypeUndefined, TypeObjectID, TypeBoolean, TypeDateTime, TypeNull, TypeRegex,\n\t\tTypeDBPointer, TypeJavaScript, TypeSymbol, TypeCodeWithScope, TypeInt32,\n\t\tTypeTimestamp, TypeInt64, TypeDecimal128, TypeMinKey, TypeMaxKey:\n\t\treturn true\n\tdefault:\n\t\treturn false\n\t}\n}\n\n// BSON element types as described in https://bsonspec.org/spec.html.\nconst (\n\tTypeDouble           Type = 0x01\n\tTypeString           Type = 0x02\n\tTypeEmbeddedDocument Type = 0x03\n\tTypeArray            Type = 0x04\n\tTypeBinary           Type = 0x05\n\tTypeUndefined        Type = 0x06\n\tTypeObjectID         Type = 0x07\n\tTypeBoolean          Type = 0x08\n\tTypeDateTime         Type = 0x09\n\tTypeNull             Type = 0x0A\n\tTypeRegex            Type = 0x0B\n\tTypeDBPointer        Type = 0x0C\n\tTypeJavaScript       Type = 0x0D\n\tTypeSymbol           Type = 0x0E\n\tTypeCodeWithScope    Type = 0x0F\n\tTypeInt32            Type = 0x10\n\tTypeTimestamp        Type = 0x11\n\tTypeInt64            Type = 0x12\n\tTypeDecimal128       Type = 0x13\n\tTypeMaxKey           Type = 0x7F\n\tTypeMinKey           Type = 0xFF\n)\n\n// BSON binary element subtypes as described in https://bsonspec.org/spec.html.\nconst (\n\tTypeBinaryGeneric     byte = 0x00\n\tTypeBinaryFunction    byte = 0x01\n\tTypeBinaryBinaryOld   byte = 0x02\n\tTypeBinaryUUIDOld     byte = 0x03\n\tTypeBinaryUUID        byte = 0x04\n\tTypeBinaryMD5         byte = 0x05\n\tTypeBinaryEncrypted   byte = 0x06\n\tTypeBinaryColumn      byte = 0x07\n\tTypeBinarySensitive   byte = 0x08\n\tTypeBinaryVector      byte = 0x09\n\tTypeBinaryUserDefined byte = 0x80\n)\n\nvar (\n\ttBool    = reflect.TypeOf(false)\n\ttFloat64 = reflect.TypeOf(float64(0))\n\ttInt32   = reflect.TypeOf(int32(0))\n\ttInt64   = reflect.TypeOf(int64(0))\n\ttString  = reflect.TypeOf(\"\")\n\ttTime    = reflect.TypeOf(time.Time{})\n)\n\nvar (\n\ttEmpty      = reflect.TypeOf((*any)(nil)).Elem()\n\ttByteSlice  = reflect.TypeOf([]byte(nil))\n\ttByte       = reflect.TypeOf(byte(0x00))\n\ttURL        = reflect.TypeOf(url.URL{})\n\ttJSONNumber = reflect.TypeOf(json.Number(\"\"))\n)\n\nvar (\n\ttValueMarshaler   = reflect.TypeOf((*ValueMarshaler)(nil)).Elem()\n\ttValueUnmarshaler = reflect.TypeOf((*ValueUnmarshaler)(nil)).Elem()\n\ttMarshaler        = reflect.TypeOf((*Marshaler)(nil)).Elem()\n\ttUnmarshaler      = reflect.TypeOf((*Unmarshaler)(nil)).Elem()\n\ttZeroer           = reflect.TypeOf((*Zeroer)(nil)).Elem()\n)\n\nvar (\n\ttBinary        = reflect.TypeOf(Binary{})\n\ttUndefined     = reflect.TypeOf(Undefined{})\n\ttOID           = reflect.TypeOf(ObjectID{})\n\ttDateTime      = reflect.TypeOf(DateTime(0))\n\ttNull          = reflect.TypeOf(Null{})\n\ttRegex         = reflect.TypeOf(Regex{})\n\ttCodeWithScope = reflect.TypeOf(CodeWithScope{})\n\ttDBPointer     = reflect.TypeOf(DBPointer{})\n\ttJavaScript    = reflect.TypeOf(JavaScript(\"\"))\n\ttSymbol        = reflect.TypeOf(Symbol(\"\"))\n\ttTimestamp     = reflect.TypeOf(Timestamp{})\n\ttDecimal       = reflect.TypeOf(Decimal128{})\n\ttVector        = reflect.TypeOf(Vector{})\n\ttMinKey        = reflect.TypeOf(MinKey{})\n\ttMaxKey        = reflect.TypeOf(MaxKey{})\n\ttD             = reflect.TypeOf(D{})\n\ttA             = reflect.TypeOf(A{})\n\ttE             = reflect.TypeOf(E{})\n)\n\nvar (\n\ttCoreDocument = reflect.TypeOf(bsoncore.Document{})\n\ttCoreArray    = reflect.TypeOf(bsoncore.Array{})\n)\n"
  },
  {
    "path": "bson/uint_codec.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bson\n\nimport (\n\t\"fmt\"\n\t\"math\"\n\t\"reflect\"\n)\n\n// uintCodec is the Codec used for uint values.\ntype uintCodec struct {\n\t// encodeToMinSize causes EncodeValue to marshal Go uint values (excluding uint64) as the\n\t// minimum BSON int size (either 32-bit or 64-bit) that can represent the integer value.\n\tencodeToMinSize bool\n}\n\n// Assert that uintCodec satisfies the typeDecoder interface, which allows it to be used\n// by collection type decoders (e.g. map, slice, etc) to set individual values in a collection.\nvar _ typeDecoder = &uintCodec{}\n\n// EncodeValue is the ValueEncoder for uint types.\nfunc (uic *uintCodec) EncodeValue(ec EncodeContext, vw ValueWriter, val reflect.Value) error {\n\tswitch val.Kind() {\n\tcase reflect.Uint8, reflect.Uint16:\n\t\treturn vw.WriteInt32(int32(val.Uint()))\n\tcase reflect.Uint, reflect.Uint32, reflect.Uint64:\n\t\tu64 := val.Uint()\n\n\t\t// If ec.MinSize or if encodeToMinSize is true for a non-uint64 value we should write val as an int32\n\t\tuseMinSize := ec.minSize || (uic.encodeToMinSize && val.Kind() != reflect.Uint64)\n\n\t\tif u64 <= math.MaxInt32 && useMinSize {\n\t\t\treturn vw.WriteInt32(int32(u64))\n\t\t}\n\t\tif u64 > math.MaxInt64 {\n\t\t\treturn fmt.Errorf(\"%d overflows int64\", u64)\n\t\t}\n\t\treturn vw.WriteInt64(int64(u64))\n\t}\n\n\treturn ValueEncoderError{\n\t\tName:     \"UintEncodeValue\",\n\t\tKinds:    []reflect.Kind{reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint},\n\t\tReceived: val,\n\t}\n}\n\nfunc (uic *uintCodec) decodeType(dc DecodeContext, vr ValueReader, t reflect.Type) (reflect.Value, error) {\n\tvar i64 int64\n\tvar err error\n\tswitch vrType := vr.Type(); vrType {\n\tcase TypeInt32:\n\t\ti32, err := vr.ReadInt32()\n\t\tif err != nil {\n\t\t\treturn emptyValue, err\n\t\t}\n\t\ti64 = int64(i32)\n\tcase TypeInt64:\n\t\ti64, err = vr.ReadInt64()\n\t\tif err != nil {\n\t\t\treturn emptyValue, err\n\t\t}\n\tcase TypeDouble:\n\t\tf64, err := vr.ReadDouble()\n\t\tif err != nil {\n\t\t\treturn emptyValue, err\n\t\t}\n\t\tif !dc.truncate && math.Floor(f64) != f64 {\n\t\t\treturn emptyValue, errCannotTruncate\n\t\t}\n\t\tif f64 > float64(math.MaxInt64) {\n\t\t\treturn emptyValue, fmt.Errorf(\"%g overflows int64\", f64)\n\t\t}\n\t\ti64 = int64(f64)\n\tcase TypeBoolean:\n\t\tb, err := vr.ReadBoolean()\n\t\tif err != nil {\n\t\t\treturn emptyValue, err\n\t\t}\n\t\tif b {\n\t\t\ti64 = 1\n\t\t}\n\tcase TypeNull:\n\t\tif err = vr.ReadNull(); err != nil {\n\t\t\treturn emptyValue, err\n\t\t}\n\tcase TypeUndefined:\n\t\tif err = vr.ReadUndefined(); err != nil {\n\t\t\treturn emptyValue, err\n\t\t}\n\tdefault:\n\t\treturn emptyValue, fmt.Errorf(\"cannot decode %v into an integer type\", vrType)\n\t}\n\n\tswitch t.Kind() {\n\tcase reflect.Uint8:\n\t\tif i64 < 0 || i64 > math.MaxUint8 {\n\t\t\treturn emptyValue, fmt.Errorf(\"%d overflows uint8\", i64)\n\t\t}\n\n\t\treturn reflect.ValueOf(uint8(i64)), nil\n\tcase reflect.Uint16:\n\t\tif i64 < 0 || i64 > math.MaxUint16 {\n\t\t\treturn emptyValue, fmt.Errorf(\"%d overflows uint16\", i64)\n\t\t}\n\n\t\treturn reflect.ValueOf(uint16(i64)), nil\n\tcase reflect.Uint32:\n\t\tif i64 < 0 || i64 > math.MaxUint32 {\n\t\t\treturn emptyValue, fmt.Errorf(\"%d overflows uint32\", i64)\n\t\t}\n\n\t\treturn reflect.ValueOf(uint32(i64)), nil\n\tcase reflect.Uint64:\n\t\tif i64 < 0 {\n\t\t\treturn emptyValue, fmt.Errorf(\"%d overflows uint64\", i64)\n\t\t}\n\n\t\treturn reflect.ValueOf(uint64(i64)), nil\n\tcase reflect.Uint:\n\t\tif i64 < 0 {\n\t\t\treturn emptyValue, fmt.Errorf(\"%d overflows uint\", i64)\n\t\t}\n\t\tv := uint64(i64)\n\t\tif v > math.MaxUint { // Can we fit this inside of an uint\n\t\t\treturn emptyValue, fmt.Errorf(\"%d overflows uint\", i64)\n\t\t}\n\n\t\treturn reflect.ValueOf(uint(v)), nil\n\tdefault:\n\t\treturn emptyValue, ValueDecoderError{\n\t\t\tName:     \"UintDecodeValue\",\n\t\t\tKinds:    []reflect.Kind{reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint},\n\t\t\tReceived: reflect.Zero(t),\n\t\t}\n\t}\n}\n\n// DecodeValue is the ValueDecoder for uint types.\nfunc (uic *uintCodec) DecodeValue(dc DecodeContext, vr ValueReader, val reflect.Value) error {\n\tif !val.CanSet() {\n\t\treturn ValueDecoderError{\n\t\t\tName:     \"UintDecodeValue\",\n\t\t\tKinds:    []reflect.Kind{reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint},\n\t\t\tReceived: val,\n\t\t}\n\t}\n\n\telem, err := uic.decodeType(dc, vr, val.Type())\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tval.SetUint(elem.Uint())\n\treturn nil\n}\n"
  },
  {
    "path": "bson/unmarshal.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bson\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n)\n\n// Unmarshaler is the interface implemented by types that can unmarshal a BSON\n// document representation of themselves. The input can be assumed to be a valid\n// encoding of a BSON document. UnmarshalBSON must copy the JSON data if it\n// wishes to retain the data after returning.\n//\n// Unmarshaler is only used to unmarshal full BSON documents. To create custom\n// BSON unmarshaling behavior for individual values in a BSON document,\n// implement the ValueUnmarshaler interface instead.\ntype Unmarshaler interface {\n\tUnmarshalBSON([]byte) error\n}\n\n// ValueUnmarshaler is the interface implemented by types that can unmarshal a\n// BSON value representation of themselves. The input can be assumed to be a\n// valid encoding of a BSON value. UnmarshalBSONValue must copy the BSON value\n// bytes if it wishes to retain the data after returning.\n//\n// ValueUnmarshaler is only used to unmarshal individual values in a BSON\n// document. To create custom BSON unmarshaling behavior for an entire BSON\n// document, implement the Unmarshaler interface instead.\ntype ValueUnmarshaler interface {\n\tUnmarshalBSONValue(typ byte, data []byte) error\n}\n\n// Unmarshal parses the BSON-encoded data and stores the result in the value\n// pointed to by val. If val is nil or not a pointer, Unmarshal returns an\n// error.\n//\n// When unmarshaling BSON, if the BSON value is null and the Go value is a\n// pointer, the pointer is set to nil without calling UnmarshalBSONValue.\nfunc Unmarshal(data []byte, val any) error {\n\tvr := getBufferedDocumentReader(data)\n\tdefer putBufferedDocumentReader(vr)\n\n\tif l, err := vr.peekLength(); err != nil {\n\t\treturn err\n\t} else if int(l) != len(data) {\n\t\treturn fmt.Errorf(\"invalid document length\")\n\t}\n\treturn unmarshalFromReader(DecodeContext{Registry: defaultRegistry}, vr, val)\n}\n\n// UnmarshalValue parses the BSON value of type t with bson.NewRegistry() and\n// stores the result in the value pointed to by val. If val is nil or not a pointer,\n// UnmarshalValue returns an error.\nfunc UnmarshalValue(t Type, data []byte, val any) error {\n\tvr := newBufferedValueReader(t, data)\n\treturn unmarshalFromReader(DecodeContext{Registry: defaultRegistry}, vr, val)\n}\n\n// UnmarshalExtJSON parses the extended JSON-encoded data and stores the result\n// in the value pointed to by val. If val is nil or not a pointer, UnmarshalExtJSON\n// returns an error.\n//\n// If canonicalOnly is true, UnmarshalExtJSON returns an error if the Extended\n// JSON was not marshaled in canonical mode.\n//\n// For more information about Extended JSON, see\n// https://www.mongodb.com/docs/manual/reference/mongodb-extended-json/\nfunc UnmarshalExtJSON(data []byte, canonicalOnly bool, val any) error {\n\tejvr, err := NewExtJSONValueReader(bytes.NewReader(data), canonicalOnly)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn unmarshalFromReader(DecodeContext{Registry: defaultRegistry}, ejvr, val)\n}\n\nfunc unmarshalFromReader(dc DecodeContext, vr ValueReader, val any) error {\n\tdec := decPool.Get().(*Decoder)\n\tdefer decPool.Put(dec)\n\n\tdec.Reset(vr)\n\tdec.dc = dc\n\n\treturn dec.Decode(val)\n}\n"
  },
  {
    "path": "bson/unmarshal_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bson\n\nimport (\n\t\"bytes\"\n\t\"math/rand\"\n\t\"reflect\"\n\t\"sync\"\n\t\"testing\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n)\n\nfunc TestUnmarshal(t *testing.T) {\n\tfor _, tc := range unmarshalingTestCases() {\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\t// Make a copy of the test data so we can modify it later.\n\t\t\tdata := make([]byte, len(tc.data))\n\t\t\tcopy(data, tc.data)\n\n\t\t\t// Assert that unmarshaling the input data results in the expected value.\n\t\t\tgot := reflect.New(tc.sType).Interface()\n\t\t\terr := Unmarshal(data, got)\n\t\t\tnoerr(t, err)\n\t\t\tassert.Equal(t, tc.want, got, \"Did not unmarshal as expected.\")\n\n\t\t\t// Fill the input data slice with random bytes and then assert that the result still\n\t\t\t// matches the expected value.\n\t\t\t_, err = rand.Read(data)\n\t\t\tnoerr(t, err)\n\t\t\tassert.Equal(t, tc.want, got, \"unmarshaled value does not match expected after modifying the input bytes\")\n\t\t})\n\t}\n}\n\nfunc TestUnmarshalWithRegistry(t *testing.T) {\n\tt.Parallel()\n\n\ttestCases := []struct {\n\t\tname     string\n\t\tval      any\n\t\tbsontype Type\n\t\tbytes    []byte\n\t}{\n\t\t{\n\t\t\tname:     \"SliceCodec binary\",\n\t\t\tval:      []byte(\"hello world\"),\n\t\t\tbsontype: TypeBinary,\n\t\t\tbytes:    bsoncore.AppendBinary(nil, TypeBinaryGeneric, []byte(\"hello world\")),\n\t\t},\n\t\t{\n\t\t\tname:     \"SliceCodec string\",\n\t\t\tval:      []byte(\"hello world\"),\n\t\t\tbsontype: TypeString,\n\t\t\tbytes:    bsoncore.AppendString(nil, \"hello world\"),\n\t\t},\n\t}\n\treg := NewRegistry()\n\treg.RegisterTypeDecoder(reflect.TypeOf([]byte{}), &sliceCodec{})\n\tfor _, tc := range testCases {\n\t\ttc := tc\n\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\t// Assert that unmarshaling the input data results in the expected value.\n\t\t\tgotValue := reflect.New(reflect.TypeOf(tc.val))\n\t\t\tdec := NewDecoder(newBufferedValueReader(tc.bsontype, tc.bytes))\n\t\t\tdec.SetRegistry(reg)\n\t\t\terr := dec.Decode(gotValue.Interface())\n\t\t\tnoerr(t, err)\n\t\t\tassert.Equal(t, tc.val, gotValue.Elem().Interface(), \"value mismatch; expected %s, got %s\", tc.val, gotValue.Elem())\n\n\t\t\t// Fill the input data slice with random bytes and then assert that the result still\n\t\t\t// matches the expected value.\n\t\t\t_, err = rand.Read(tc.bytes)\n\t\t\tnoerr(t, err)\n\t\t\tassert.Equal(t, tc.val, gotValue.Elem().Interface(), \"unmarshaled value does not match expected after modifying the input bytes\")\n\t\t})\n\t}\n}\n\nfunc TestCachingDecodersNotSharedAcrossRegistries(t *testing.T) {\n\t// Decoders that have caches for recursive decoder lookup should not be shared across Registry instances. Otherwise,\n\t// the first DecodeValue call would cache an decoder and a subsequent call would see that decoder even if a\n\t// different Registry is used.\n\n\t// Create a custom Registry that negates BSON int32 values when decoding.\n\tvar decodeInt32 ValueDecoderFunc = func(_ DecodeContext, vr ValueReader, val reflect.Value) error {\n\t\ti32, err := vr.ReadInt32()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tval.SetInt(int64(-1 * i32))\n\t\treturn nil\n\t}\n\tcustomReg := NewRegistry()\n\tcustomReg.RegisterTypeDecoder(tInt32, decodeInt32)\n\n\tdocBytes := bsoncore.BuildDocumentFromElements(\n\t\tnil,\n\t\tbsoncore.AppendInt32Element(nil, \"x\", 1),\n\t)\n\n\t// For all sub-tests, unmarshal docBytes into a struct and assert that value for \"x\" is 1 when using the default\n\t// registry and -1 when using the custom registry.\n\tt.Run(\"struct\", func(t *testing.T) {\n\t\ttype Struct struct {\n\t\t\tX int32\n\t\t}\n\n\t\tvar first Struct\n\t\terr := Unmarshal(docBytes, &first)\n\t\tassert.Nil(t, err, \"Unmarshal error: %v\", err)\n\t\tassert.Equal(t, int32(1), first.X, \"expected X value to be 1, got %v\", first.X)\n\n\t\tvar second Struct\n\t\tdec := NewDecoder(NewDocumentReader(bytes.NewReader(docBytes)))\n\t\tdec.SetRegistry(customReg)\n\t\terr = dec.Decode(&second)\n\t\tassert.Nil(t, err, \"Unmarshal error: %v\", err)\n\t\tassert.Equal(t, int32(-1), second.X, \"expected X value to be -1, got %v\", second.X)\n\t})\n\tt.Run(\"pointer\", func(t *testing.T) {\n\t\ttype Struct struct {\n\t\t\tX *int32\n\t\t}\n\n\t\tvar first Struct\n\t\terr := Unmarshal(docBytes, &first)\n\t\tassert.Nil(t, err, \"Unmarshal error: %v\", err)\n\t\tassert.Equal(t, int32(1), *first.X, \"expected X value to be 1, got %v\", *first.X)\n\n\t\tvar second Struct\n\t\tdec := NewDecoder(NewDocumentReader(bytes.NewReader(docBytes)))\n\t\tdec.SetRegistry(customReg)\n\t\terr = dec.Decode(&second)\n\t\tassert.Nil(t, err, \"Unmarshal error: %v\", err)\n\t\tassert.Equal(t, int32(-1), *second.X, \"expected X value to be -1, got %v\", *second.X)\n\t})\n}\n\nfunc TestUnmarshalExtJSONWithUndefinedField(t *testing.T) {\n\t// When unmarshalling extJSON, fields that are undefined in the destination struct are skipped.\n\t// This process must not skip other, defined fields and must not raise errors.\n\ttype expectedResponse struct {\n\t\tDefinedField any\n\t}\n\n\tunmarshalExpectedResponse := func(t *testing.T, extJSON string) *expectedResponse {\n\t\tt.Helper()\n\t\tresponseDoc := expectedResponse{}\n\t\terr := UnmarshalExtJSON([]byte(extJSON), false, &responseDoc)\n\t\tassert.Nil(t, err, \"UnmarshalExtJSON error: %v\", err)\n\t\treturn &responseDoc\n\t}\n\n\ttestCases := []struct {\n\t\tname          string\n\t\ttestJSON      string\n\t\texpectedValue any\n\t}{\n\t\t{\n\t\t\t\"no array\",\n\t\t\t`{\n\t\t\t\t\"UndefinedField\": {\"key\": 1},\n\t\t\t\t\"DefinedField\": \"value\"\n\t\t\t}`,\n\t\t\t\"value\",\n\t\t},\n\t\t{\n\t\t\t\"outer array\",\n\t\t\t`{\n\t\t\t\t\"UndefinedField\": [{\"key\": 1}],\n\t\t\t\t\"DefinedField\": \"value\"\n\t\t\t}`,\n\t\t\t\"value\",\n\t\t},\n\t\t{\n\t\t\t\"embedded array\",\n\t\t\t`{\n\t\t\t\t\"UndefinedField\": {\"keys\": [2]},\n\t\t\t\t\"DefinedField\": \"value\"\n\t\t\t}`,\n\t\t\t\"value\",\n\t\t},\n\t\t{\n\t\t\t\"outer array and embedded array\",\n\t\t\t`{\n\t\t\t\t\"UndefinedField\": [{\"keys\": [2]}],\n\t\t\t\t\"DefinedField\": \"value\"\n\t\t\t}`,\n\t\t\t\"value\",\n\t\t},\n\t\t{\n\t\t\t\"embedded document\",\n\t\t\t`{\n\t\t\t\t\"UndefinedField\": {\"key\": {\"one\": \"two\"}},\n\t\t\t\t\"DefinedField\": \"value\"\n\t\t\t}`,\n\t\t\t\"value\",\n\t\t},\n\t\t{\n\t\t\t\"doubly embedded document\",\n\t\t\t`{\n\t\t\t\t\"UndefinedField\": {\"key\": {\"one\": {\"two\": \"three\"}}},\n\t\t\t\t\"DefinedField\": \"value\"\n\t\t\t}`,\n\t\t\t\"value\",\n\t\t},\n\t\t{\n\t\t\t\"embedded document and embedded array\",\n\t\t\t`{\n\t\t\t\t\"UndefinedField\": {\"key\": {\"one\": {\"two\": [3]}}},\n\t\t\t\t\"DefinedField\": \"value\"\n\t\t\t}`,\n\t\t\t\"value\",\n\t\t},\n\t\t{\n\t\t\t\"embedded document and embedded array in outer array\",\n\t\t\t`{\n\t\t\t\t\"UndefinedField\": [{\"key\": {\"one\": [3]}}],\n\t\t\t\t\"DefinedField\": \"value\"\n\t\t\t}`,\n\t\t\t\"value\",\n\t\t},\n\t\t{\n\t\t\t\"code with scope\",\n\t\t\t`{\n\t\t\t\t\"UndefinedField\": {\"logic\": {\"$code\": \"foo\", \"$scope\": {\"bar\": 1}}},\n\t\t\t\t\"DefinedField\": \"value\"\n\t\t\t}`,\n\t\t\t\"value\",\n\t\t},\n\t\t{\n\t\t\t\"embedded array of code with scope\",\n\t\t\t`{\n\t\t\t\t\"UndefinedField\": {\"logic\": [{\"$code\": \"foo\", \"$scope\": {\"bar\": 1}}]},\n\t\t\t\t\"DefinedField\": \"value\"\n\t\t\t}`,\n\t\t\t\"value\",\n\t\t},\n\t\t{\n\t\t\t\"type definition embedded document\",\n\t\t\t`{\n\t\t\t\t\"UndefinedField\": {\"myDouble\": {\"$numberDouble\": \"1.24\"}},\n\t\t\t\t\"DefinedField\": \"value\"\n\t\t\t}`,\n\t\t\t\"value\",\n\t\t},\n\t\t{\n\t\t\t\"empty embedded document\",\n\t\t\t`{\n\t\t\t\t\"UndefinedField\": {\"empty\": {}, \"key\": 1},\n\t\t\t\t\"DefinedField\": \"value\"\n\t\t\t}`,\n\t\t\t\"value\",\n\t\t},\n\t\t{\n\t\t\t\"empty object before\",\n\t\t\t`{\n\t\t\t\t\"UndefinedField\": {},\n\t\t\t\t\"DefinedField\": {\"value\": \"a\"}\n\t\t\t}`,\n\t\t\tD{{\"value\", \"a\"}},\n\t\t},\n\t\t{\n\t\t\t\"empty object after\",\n\t\t\t`{\n\t\t\t\t\"DefinedField\": {\"value\": \"a\"},\n\t\t\t\t\"UndefinedField\": {}\n\t\t\t}`,\n\t\t\tD{{\"value\", \"a\"}},\n\t\t},\n\t}\n\tfor _, tc := range testCases {\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\tresponseDoc := unmarshalExpectedResponse(t, tc.testJSON)\n\t\t\tassert.Equal(t, tc.expectedValue, responseDoc.DefinedField, \"expected DefinedField to be %v, got %q\",\n\t\t\t\ttc.expectedValue, responseDoc.DefinedField)\n\t\t})\n\t}\n}\n\nfunc TestUnmarshalInterface(t *testing.T) {\n\tt.Parallel()\n\n\ttype testCase struct {\n\t\tname string\n\t\tstub func() ([]byte, any, func(*testing.T))\n\t}\n\ttestCases := []testCase{\n\t\t{\n\t\t\tname: \"struct with interface containing a concrete value\",\n\t\t\tstub: func() ([]byte, any, func(*testing.T)) {\n\t\t\t\ttype testStruct struct {\n\t\t\t\t\tValue any\n\t\t\t\t}\n\t\t\t\tvar value string\n\n\t\t\t\tdata := docToBytes(struct {\n\t\t\t\t\tValue string\n\t\t\t\t}{\n\t\t\t\t\tValue: \"foo\",\n\t\t\t\t})\n\n\t\t\t\treceiver := testStruct{&value}\n\n\t\t\t\tcheck := func(t *testing.T) {\n\t\t\t\t\tt.Helper()\n\t\t\t\t\tassert.Equal(t, \"foo\", value)\n\t\t\t\t}\n\n\t\t\t\treturn data, &receiver, check\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"struct with interface containing a struct\",\n\t\t\tstub: func() ([]byte, any, func(*testing.T)) {\n\t\t\t\ttype demo struct {\n\t\t\t\t\tData string\n\t\t\t\t}\n\n\t\t\t\ttype testStruct struct {\n\t\t\t\t\tValue any\n\t\t\t\t}\n\t\t\t\tvar value demo\n\n\t\t\t\tdata := docToBytes(struct {\n\t\t\t\t\tValue demo\n\t\t\t\t}{\n\t\t\t\t\tValue: demo{\"foo\"},\n\t\t\t\t})\n\n\t\t\t\treceiver := testStruct{&value}\n\n\t\t\t\tcheck := func(t *testing.T) {\n\t\t\t\t\tt.Helper()\n\t\t\t\t\tassert.Equal(t, \"foo\", value.Data)\n\t\t\t\t}\n\n\t\t\t\treturn data, &receiver, check\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"struct with interface containing a slice\",\n\t\t\tstub: func() ([]byte, any, func(*testing.T)) {\n\t\t\t\ttype testStruct struct {\n\t\t\t\t\tValues any\n\t\t\t\t}\n\t\t\t\tvar values []string\n\n\t\t\t\tdata := docToBytes(struct {\n\t\t\t\t\tValues []string\n\t\t\t\t}{\n\t\t\t\t\tValues: []string{\"foo\", \"bar\"},\n\t\t\t\t})\n\n\t\t\t\treceiver := testStruct{&values}\n\n\t\t\t\tcheck := func(t *testing.T) {\n\t\t\t\t\tt.Helper()\n\t\t\t\t\tassert.Equal(t, []string{\"foo\", \"bar\"}, values)\n\t\t\t\t}\n\n\t\t\t\treturn data, &receiver, check\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"struct with interface containing an array\",\n\t\t\tstub: func() ([]byte, any, func(*testing.T)) {\n\t\t\t\ttype testStruct struct {\n\t\t\t\t\tValues any\n\t\t\t\t}\n\t\t\t\tvar values [2]string\n\n\t\t\t\tdata := docToBytes(struct {\n\t\t\t\t\tValues []string\n\t\t\t\t}{\n\t\t\t\t\tValues: []string{\"foo\", \"bar\"},\n\t\t\t\t})\n\n\t\t\t\treceiver := testStruct{&values}\n\n\t\t\t\tcheck := func(t *testing.T) {\n\t\t\t\t\tt.Helper()\n\t\t\t\t\tassert.Equal(t, [2]string{\"foo\", \"bar\"}, values)\n\t\t\t\t}\n\n\t\t\t\treturn data, &receiver, check\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"struct with interface array containing concrete values\",\n\t\t\tstub: func() ([]byte, any, func(*testing.T)) {\n\t\t\t\ttype testStruct struct {\n\t\t\t\t\tValues [3]any\n\t\t\t\t}\n\t\t\t\tvar str string\n\t\t\t\tvar i, j int\n\n\t\t\t\tdata := docToBytes(struct {\n\t\t\t\t\tValues []any\n\t\t\t\t}{\n\t\t\t\t\tValues: []any{\"foo\", 42, nil},\n\t\t\t\t})\n\n\t\t\t\treceiver := testStruct{[3]any{&str, &i, &j}}\n\n\t\t\t\tcheck := func(t *testing.T) {\n\t\t\t\t\tt.Helper()\n\t\t\t\t\tassert.Equal(t, \"foo\", str)\n\t\t\t\t\tassert.Equal(t, 42, i)\n\t\t\t\t\tassert.Equal(t, 0, j)\n\t\t\t\t\tassert.Equal(t, testStruct{[3]any{&str, &i, nil}}, receiver)\n\t\t\t\t}\n\n\t\t\t\treturn data, &receiver, check\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"overwriting prepopulated slice\",\n\t\t\tstub: func() ([]byte, any, func(*testing.T)) {\n\t\t\t\ttype testStruct struct {\n\t\t\t\t\tValues []any\n\t\t\t\t}\n\t\t\t\tdata := docToBytes(struct {\n\t\t\t\t\tValues []any\n\t\t\t\t}{\n\t\t\t\t\tValues: []any{1, 2, 3},\n\t\t\t\t})\n\n\t\t\t\treceiver := testStruct{[]any{7, 8}}\n\n\t\t\t\tcheck := func(t *testing.T) {\n\t\t\t\t\tt.Helper()\n\t\t\t\t\tassert.Equal(t, testStruct{[]any{1, 2, int32(3)}}, receiver)\n\t\t\t\t}\n\n\t\t\t\treturn data, &receiver, check\n\t\t\t},\n\t\t},\n\t}\n\tfor _, tc := range testCases {\n\t\ttc := tc\n\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tdata, receiver, check := tc.stub()\n\t\t\terr := Unmarshal(data, receiver)\n\t\t\tnoerr(t, err)\n\t\t\tcheck(t)\n\t\t})\n\t}\n}\n\nfunc TestUnmarshalBSONWithUndefinedField(t *testing.T) {\n\t// When unmarshalling BSON, fields that are undefined in the destination struct are skipped.\n\t// This process must not skip other, defined fields and must not raise errors.\n\ttype expectedResponse struct {\n\t\tDefinedField string `bson:\"DefinedField\"`\n\t}\n\n\tcreateExpectedResponse := func(t *testing.T, doc D) *expectedResponse {\n\t\tt.Helper()\n\n\t\tmarshalledBSON, err := Marshal(doc)\n\t\tassert.Nil(t, err, \"error marshalling BSON: %v\", err)\n\n\t\tresponseDoc := expectedResponse{}\n\t\terr = Unmarshal(marshalledBSON, &responseDoc)\n\t\tassert.Nil(t, err, \"error unmarshalling BSON: %v\", err)\n\t\treturn &responseDoc\n\t}\n\n\ttestCases := []struct {\n\t\tname     string\n\t\ttestBSON D\n\t}{\n\t\t{\n\t\t\t\"no array\",\n\t\t\tD{\n\t\t\t\t{\"UndefinedField\", D{\n\t\t\t\t\t{\"key\", 1},\n\t\t\t\t}},\n\t\t\t\t{\"DefinedField\", \"value\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"outer array\",\n\t\t\tD{\n\t\t\t\t{\"UndefinedField\", A{D{\n\t\t\t\t\t{\"key\", 1},\n\t\t\t\t}}},\n\t\t\t\t{\"DefinedField\", \"value\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"embedded array\",\n\t\t\tD{\n\t\t\t\t{\"UndefinedField\", D{\n\t\t\t\t\t{\"key\", A{1}},\n\t\t\t\t}},\n\t\t\t\t{\"DefinedField\", \"value\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"outer array and embedded array\",\n\t\t\tD{\n\t\t\t\t{\"UndefinedField\", A{D{\n\t\t\t\t\t{\"key\", A{1}},\n\t\t\t\t}}},\n\t\t\t\t{\"DefinedField\", \"value\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"embedded document\",\n\t\t\tD{\n\t\t\t\t{\"UndefinedField\", D{\n\t\t\t\t\t{\"key\", D{\n\t\t\t\t\t\t{\"one\", \"two\"},\n\t\t\t\t\t}},\n\t\t\t\t}},\n\t\t\t\t{\"DefinedField\", \"value\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"doubly embedded document\",\n\t\t\tD{\n\t\t\t\t{\"UndefinedField\", D{\n\t\t\t\t\t{\"key\", D{\n\t\t\t\t\t\t{\"one\", D{\n\t\t\t\t\t\t\t{\"two\", \"three\"},\n\t\t\t\t\t\t}},\n\t\t\t\t\t}},\n\t\t\t\t}},\n\t\t\t\t{\"DefinedField\", \"value\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"embedded document and embedded array\",\n\t\t\tD{\n\t\t\t\t{\"UndefinedField\", D{\n\t\t\t\t\t{\"key\", D{\n\t\t\t\t\t\t{\"one\", D{\n\t\t\t\t\t\t\t{\"two\", A{3}},\n\t\t\t\t\t\t}},\n\t\t\t\t\t}},\n\t\t\t\t}},\n\t\t\t\t{\"DefinedField\", \"value\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"embedded document and embedded array in outer array\",\n\t\t\tD{\n\t\t\t\t{\"UndefinedField\", A{D{\n\t\t\t\t\t{\"key\", D{\n\t\t\t\t\t\t{\"one\", A{3}},\n\t\t\t\t\t}},\n\t\t\t\t}}},\n\t\t\t\t{\"DefinedField\", \"value\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"code with scope\",\n\t\t\tD{\n\t\t\t\t{\"UndefinedField\", D{\n\t\t\t\t\t{\"logic\", D{\n\t\t\t\t\t\t{\"$code\", \"foo\"},\n\t\t\t\t\t\t{\"$scope\", D{\n\t\t\t\t\t\t\t{\"bar\", 1},\n\t\t\t\t\t\t}},\n\t\t\t\t\t}},\n\t\t\t\t}},\n\t\t\t\t{\"DefinedField\", \"value\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"embedded array of code with scope\",\n\t\t\tD{\n\t\t\t\t{\"UndefinedField\", D{\n\t\t\t\t\t{\"logic\", A{D{\n\t\t\t\t\t\t{\"$code\", \"foo\"},\n\t\t\t\t\t\t{\"$scope\", D{\n\t\t\t\t\t\t\t{\"bar\", 1},\n\t\t\t\t\t\t}},\n\t\t\t\t\t}}},\n\t\t\t\t}},\n\t\t\t\t{\"DefinedField\", \"value\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"empty embedded document\",\n\t\t\tD{\n\t\t\t\t{\"UndefinedField\", D{}},\n\t\t\t\t{\"DefinedField\", \"value\"},\n\t\t\t},\n\t\t},\n\t}\n\tfor _, tc := range testCases {\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\tresponseDoc := createExpectedResponse(t, tc.testBSON)\n\t\t\tassert.Equal(t, \"value\", responseDoc.DefinedField, \"expected DefinedField to be 'value', got %q\", responseDoc.DefinedField)\n\t\t})\n\t}\n}\n\n// GODRIVER-2311\n// Assert that unmarshaled values containing byte slices do not reference the same underlying byte\n// array as the BSON input data byte slice.\nfunc TestUnmarshalByteSlicesUseDistinctArrays(t *testing.T) {\n\ttype fooBytes struct {\n\t\tFoo []byte\n\t}\n\n\ttype myBytes []byte\n\ttype fooMyBytes struct {\n\t\tFoo myBytes\n\t}\n\n\ttype fooBinary struct {\n\t\tFoo Binary\n\t}\n\n\ttype fooObjectID struct {\n\t\tFoo ObjectID\n\t}\n\n\ttype fooDBPointer struct {\n\t\tFoo DBPointer\n\t}\n\n\ttestCases := []struct {\n\t\tdescription string\n\t\tdata        []byte\n\t\tsType       reflect.Type\n\t\twant        any\n\n\t\t// getByteSlice returns the byte slice from the unmarshaled value, allowing the test to\n\t\t// inspect the addresses of the underlying byte array.\n\t\tgetByteSlice func(any) []byte\n\t}{\n\t\t{\n\t\t\tdescription: \"struct with byte slice\",\n\t\t\tdata: docToBytes(fooBytes{\n\t\t\t\tFoo: []byte{0, 1, 2, 3, 4, 5},\n\t\t\t}),\n\t\t\tsType: reflect.TypeOf(fooBytes{}),\n\t\t\twant: &fooBytes{\n\t\t\t\tFoo: []byte{0, 1, 2, 3, 4, 5},\n\t\t\t},\n\t\t\tgetByteSlice: func(val any) []byte {\n\t\t\t\treturn (val.(*fooBytes)).Foo\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tdescription: \"bson.D with byte slice\",\n\t\t\tdata: docToBytes(D{\n\t\t\t\t{\"foo\", []byte{0, 1, 2, 3, 4, 5}},\n\t\t\t}),\n\t\t\tsType: reflect.TypeOf(D{}),\n\t\t\twant: &D{\n\t\t\t\t{\"foo\", Binary{Subtype: 0, Data: []byte{0, 1, 2, 3, 4, 5}}},\n\t\t\t},\n\t\t\tgetByteSlice: func(val any) []byte {\n\t\t\t\treturn (*(val.(*D)))[0].Value.(Binary).Data\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tdescription: \"struct with custom byte slice type\",\n\t\t\tdata: docToBytes(fooMyBytes{\n\t\t\t\tFoo: myBytes{0, 1, 2, 3, 4, 5},\n\t\t\t}),\n\t\t\tsType: reflect.TypeOf(fooMyBytes{}),\n\t\t\twant: &fooMyBytes{\n\t\t\t\tFoo: myBytes{0, 1, 2, 3, 4, 5},\n\t\t\t},\n\t\t\tgetByteSlice: func(val any) []byte {\n\t\t\t\treturn (val.(*fooMyBytes)).Foo\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tdescription: \"bson.D with custom byte slice type\",\n\t\t\tdata: docToBytes(D{\n\t\t\t\t{\"foo\", myBytes{0, 1, 2, 3, 4, 5}},\n\t\t\t}),\n\t\t\tsType: reflect.TypeOf(D{}),\n\t\t\twant: &D{\n\t\t\t\t{\"foo\", Binary{Subtype: 0, Data: myBytes{0, 1, 2, 3, 4, 5}}},\n\t\t\t},\n\t\t\tgetByteSlice: func(val any) []byte {\n\t\t\t\treturn (*(val.(*D)))[0].Value.(Binary).Data\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tdescription: \"struct with Binary\",\n\t\t\tdata: docToBytes(fooBinary{\n\t\t\t\tFoo: Binary{Subtype: 0, Data: []byte{0, 1, 2, 3, 4, 5}},\n\t\t\t}),\n\t\t\tsType: reflect.TypeOf(fooBinary{}),\n\t\t\twant: &fooBinary{\n\t\t\t\tFoo: Binary{Subtype: 0, Data: []byte{0, 1, 2, 3, 4, 5}},\n\t\t\t},\n\t\t\tgetByteSlice: func(val any) []byte {\n\t\t\t\treturn (val.(*fooBinary)).Foo.Data\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tdescription: \"bson.D with Binary\",\n\t\t\tdata: docToBytes(D{\n\t\t\t\t{\"foo\", Binary{Subtype: 0, Data: []byte{0, 1, 2, 3, 4, 5}}},\n\t\t\t}),\n\t\t\tsType: reflect.TypeOf(D{}),\n\t\t\twant: &D{\n\t\t\t\t{\"foo\", Binary{Subtype: 0, Data: []byte{0, 1, 2, 3, 4, 5}}},\n\t\t\t},\n\t\t\tgetByteSlice: func(val any) []byte {\n\t\t\t\treturn (*(val.(*D)))[0].Value.(Binary).Data\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tdescription: \"struct with ObjectID\",\n\t\t\tdata: docToBytes(fooObjectID{\n\t\t\t\tFoo: ObjectID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11},\n\t\t\t}),\n\t\t\tsType: reflect.TypeOf(fooObjectID{}),\n\t\t\twant: &fooObjectID{\n\t\t\t\tFoo: ObjectID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11},\n\t\t\t},\n\t\t\tgetByteSlice: func(val any) []byte {\n\t\t\t\treturn (val.(*fooObjectID)).Foo[:]\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tdescription: \"bson.D with ObjectID\",\n\t\t\tdata: docToBytes(D{\n\t\t\t\t{\"foo\", ObjectID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}},\n\t\t\t}),\n\t\t\tsType: reflect.TypeOf(D{}),\n\t\t\twant: &D{\n\t\t\t\t{\"foo\", ObjectID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}},\n\t\t\t},\n\t\t\tgetByteSlice: func(val any) []byte {\n\t\t\t\toid := (*(val.(*D)))[0].Value.(ObjectID)\n\t\t\t\treturn oid[:]\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tdescription: \"struct with DBPointer\",\n\t\t\tdata: docToBytes(fooDBPointer{\n\t\t\t\tFoo: DBPointer{\n\t\t\t\t\tDB:      \"test\",\n\t\t\t\t\tPointer: ObjectID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11},\n\t\t\t\t},\n\t\t\t}),\n\t\t\tsType: reflect.TypeOf(fooDBPointer{}),\n\t\t\twant: &fooDBPointer{\n\t\t\t\tFoo: DBPointer{\n\t\t\t\t\tDB:      \"test\",\n\t\t\t\t\tPointer: ObjectID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11},\n\t\t\t\t},\n\t\t\t},\n\t\t\tgetByteSlice: func(val any) []byte {\n\t\t\t\treturn (val.(*fooDBPointer)).Foo.Pointer[:]\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tdescription: \"bson.D with DBPointer\",\n\t\t\tdata: docToBytes(D{\n\t\t\t\t{\"foo\", DBPointer{\n\t\t\t\t\tDB:      \"test\",\n\t\t\t\t\tPointer: ObjectID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11},\n\t\t\t\t}},\n\t\t\t}),\n\t\t\tsType: reflect.TypeOf(D{}),\n\t\t\twant: &D{\n\t\t\t\t{\"foo\", DBPointer{\n\t\t\t\t\tDB:      \"test\",\n\t\t\t\t\tPointer: ObjectID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11},\n\t\t\t\t}},\n\t\t\t},\n\t\t\tgetByteSlice: func(val any) []byte {\n\t\t\t\toid := (*(val.(*D)))[0].Value.(DBPointer).Pointer\n\t\t\t\treturn oid[:]\n\t\t\t},\n\t\t},\n\t}\n\n\tfor _, tc := range testCases {\n\t\ttc := tc // Capture range variable.\n\t\tt.Run(tc.description, func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\t// Make a copy of the test data so we can modify it later.\n\t\t\tdata := make([]byte, len(tc.data))\n\t\t\tcopy(data, tc.data)\n\n\t\t\t// Assert that unmarshaling the input data results in the expected value.\n\t\t\tgot := reflect.New(tc.sType).Interface()\n\t\t\terr := Unmarshal(data, got)\n\t\t\tnoerr(t, err)\n\t\t\tassert.Equal(t, tc.want, got, \"unmarshaled value does not match the expected value\")\n\n\t\t\t// Fill the input data slice with random bytes and then assert that the result still\n\t\t\t// matches the expected value.\n\t\t\t_, err = rand.Read(data)\n\t\t\tnoerr(t, err)\n\t\t\tassert.Equal(t, tc.want, got, \"unmarshaled value does not match expected after modifying the input bytes\")\n\n\t\t\t// Assert that the byte slice in the unmarshaled value does not share any memory\n\t\t\t// addresses with the input byte slice.\n\t\t\tassert.DifferentAddressRanges(t, data, tc.getByteSlice(got))\n\t\t})\n\t}\n}\n\nfunc TestUnmarshalConcurrently(t *testing.T) {\n\tt.Parallel()\n\n\tconst size = 10_000\n\n\tdata := []byte{16, 0, 0, 0, 10, 108, 97, 115, 116, 101, 114, 114, 111, 114, 0, 0}\n\twg := sync.WaitGroup{}\n\twg.Add(size)\n\tfor i := 0; i < size; i++ {\n\t\tgo func() {\n\t\t\tdefer wg.Done()\n\t\t\tvar res struct{ LastError error }\n\t\t\t_ = Unmarshal(data, &res)\n\t\t}()\n\t}\n\twg.Wait()\n}\n\nfunc TestUnmarshalTypeCompatibility(t *testing.T) {\n\tt.Parallel()\n\n\tt.Run(\"negative\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\ttestCases := []struct {\n\t\t\tname   string\n\t\t\tval    any\n\t\t\tdata   []byte\n\t\t\terrMsg string\n\t\t}{\n\t\t\t{\n\t\t\t\tname: \"integer into a bson.Raw\",\n\t\t\t\tval: &struct {\n\t\t\t\t\tFoo Raw\n\t\t\t\t}{},\n\t\t\t\tdata:   docToBytes(D{{\"foo\", 42}}),\n\t\t\t\terrMsg: \"cannot decode 32-bit integer into a bson.Raw\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"integer into a bsoncore.Document\",\n\t\t\t\tval: &struct {\n\t\t\t\t\tFoo bsoncore.Document\n\t\t\t\t}{},\n\t\t\t\tdata:   docToBytes(D{{\"foo\", 42}}),\n\t\t\t\terrMsg: \"cannot decode 32-bit integer into a bsoncore.Document\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"integer into bsoncore.Array\",\n\t\t\t\tval: &struct {\n\t\t\t\t\tFoo bsoncore.Array\n\t\t\t\t}{},\n\t\t\t\tdata:   docToBytes(D{{\"foo\", 42}}),\n\t\t\t\terrMsg: \"cannot decode 32-bit integer into a bsoncore.Array\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"bson.D into bsoncore.Array\",\n\t\t\t\tval: &struct {\n\t\t\t\t\tFoo bsoncore.Array\n\t\t\t\t}{},\n\t\t\t\tdata:   docToBytes(D{{\"foo\", D{}}}),\n\t\t\t\terrMsg: \"cannot decode embedded document into a bsoncore.Array\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"bsoncore.Document into bsoncore.Array\",\n\t\t\t\tval: &struct {\n\t\t\t\t\tFoo bsoncore.Array\n\t\t\t\t}{},\n\t\t\t\tdata:   docToBytes(D{{\"foo\", bsoncore.NewDocumentBuilder().Build()}}),\n\t\t\t\terrMsg: \"cannot decode embedded document into a bsoncore.Array\",\n\t\t\t},\n\t\t}\n\t\tfor _, tc := range testCases {\n\t\t\ttc := tc\n\t\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\t\tt.Parallel()\n\n\t\t\t\terr := Unmarshal(tc.data, tc.val)\n\t\t\t\tassert.ErrorContains(t, err, tc.errMsg)\n\t\t\t})\n\t\t}\n\t})\n\tt.Run(\"positive\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tdata := docToBytes(D{{\"foo\", A{true}}})\n\t\twant := []byte(bsoncore.NewArrayBuilder().AppendBoolean(true).Build())\n\n\t\tt.Run(\"bson.A into bson.Raw\", func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tvar val struct {\n\t\t\t\tFoo Raw\n\t\t\t}\n\n\t\t\terr := Unmarshal(data, &val)\n\t\t\tassert.NoError(t, err)\n\t\t\tassert.Equal(t, want, []byte(val.Foo))\n\t\t})\n\t\tt.Run(\"bson.A into bsoncore.Document\", func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tvar val struct {\n\t\t\t\tFoo bsoncore.Document\n\t\t\t}\n\n\t\t\terr := Unmarshal(data, &val)\n\t\t\tassert.NoError(t, err)\n\t\t\tassert.Equal(t, want, []byte(val.Foo))\n\t\t})\n\t})\n}\n"
  },
  {
    "path": "bson/unmarshal_value_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2023-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bson\n\nimport (\n\t\"reflect\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/require\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n)\n\nfunc TestUnmarshalValue(t *testing.T) {\n\tt.Parallel()\n\n\tfor _, tc := range marshalValueTestCases {\n\t\ttc := tc\n\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tgotValue := reflect.New(reflect.TypeOf(tc.val))\n\t\t\terr := UnmarshalValue(tc.bsontype, tc.bytes, gotValue.Interface())\n\t\t\tassert.Nil(t, err, \"UnmarshalValueWithRegistry error: %v\", err)\n\t\t\tassert.Equal(t, tc.val, gotValue.Elem().Interface(), \"value mismatch; expected %s, got %s\", tc.val, gotValue.Elem())\n\t\t})\n\t}\n}\n\nfunc TestInitializedPointerDataWithBSONNull(t *testing.T) {\n\t// Set up the test case with initialized pointers.\n\ttc := unmarshalBehaviorTestCase{\n\t\tBSONValuePtrTracker: &unmarshalBSONValueCallTracker{},\n\t\tBSONPtrTracker:      &unmarshalBSONCallTracker{},\n\t}\n\t// Create BSON data where the '*_ptr_tracker' fields are explicitly set to\n\t// null.\n\tbytes := docToBytes(D{\n\t\t{Key: \"bv_ptr_tracker\", Value: nil},\n\t\t{Key: \"b_ptr_tracker\", Value: nil},\n\t})\n\t// Unmarshal the BSON data into the test case struct. This should set the\n\t// pointer fields to nil due to the BSON null value.\n\terr := Unmarshal(bytes, &tc)\n\trequire.NoError(t, err)\n\tassert.Nil(t, tc.BSONValuePtrTracker)\n\tassert.Nil(t, tc.BSONPtrTracker)\n}\n\n// tests covering GODRIVER-2779\nfunc BenchmarkSliceCodecUnmarshal(b *testing.B) {\n\tbenchmarks := []struct {\n\t\tname     string\n\t\tbsontype Type\n\t\tbytes    []byte\n\t}{\n\t\t{\n\t\t\tname:     \"SliceCodec binary\",\n\t\t\tbsontype: TypeBinary,\n\t\t\tbytes:    bsoncore.AppendBinary(nil, TypeBinaryGeneric, []byte(strings.Repeat(\"t\", 4096))),\n\t\t},\n\t\t{\n\t\t\tname:     \"SliceCodec string\",\n\t\t\tbsontype: TypeString,\n\t\t\tbytes:    bsoncore.AppendString(nil, strings.Repeat(\"t\", 4096)),\n\t\t},\n\t}\n\treg := NewRegistry()\n\treg.RegisterTypeDecoder(reflect.TypeOf([]byte{}), &sliceCodec{})\n\tfor _, bm := range benchmarks {\n\t\tb.Run(bm.name, func(b *testing.B) {\n\t\t\tb.RunParallel(func(pb *testing.PB) {\n\t\t\t\tdec := NewDecoder(nil)\n\t\t\t\tdec.SetRegistry(reg)\n\t\t\t\tfor pb.Next() {\n\t\t\t\t\tdec.Reset(newBufferedValueReader(bm.bsontype, bm.bytes))\n\t\t\t\t\terr := dec.Decode(&[]byte{})\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\tb.Fatal(err)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t})\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "bson/unmarshaling_cases_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bson\n\nimport (\n\t\"fmt\"\n\t\"reflect\"\n\t\"strings\"\n)\n\ntype unmarshalingTestCase struct {\n\tname  string\n\tsType reflect.Type\n\twant  any\n\tdata  []byte\n}\n\nfunc unmarshalingTestCases() []unmarshalingTestCase {\n\tvar zeroPtrStruct unmarshalerPtrStruct\n\t{\n\t\ti := myInt64(0)\n\t\tm := myMap{}\n\t\tb := myBytes{}\n\t\ts := myString(\"\")\n\t\tzeroPtrStruct = unmarshalerPtrStruct{I: &i, M: &m, B: &b, S: &s}\n\t}\n\n\tvar zeroNonPtrStruct unmarshalerNonPtrStruct\n\t{\n\t\ti := myInt64(0)\n\t\tm := myMap{}\n\t\tb := myBytes{}\n\t\ts := myString(\"\")\n\t\tzeroNonPtrStruct = unmarshalerNonPtrStruct{I: i, M: m, B: b, S: s}\n\t}\n\n\tvar valPtrStruct unmarshalerPtrStruct\n\t{\n\t\ti := myInt64(5)\n\t\tm := myMap{\"key\": \"value\"}\n\t\tb := myBytes{0x00, 0x01}\n\t\ts := myString(\"test\")\n\t\tvalPtrStruct = unmarshalerPtrStruct{I: &i, M: &m, B: &b, S: &s}\n\t}\n\n\tvar valNonPtrStruct unmarshalerNonPtrStruct\n\t{\n\t\ti := myInt64(5)\n\t\tm := myMap{\"key\": \"value\"}\n\t\tb := myBytes{0x00, 0x01}\n\t\ts := myString(\"test\")\n\t\tvalNonPtrStruct = unmarshalerNonPtrStruct{I: i, M: m, B: b, S: s}\n\t}\n\n\ttype fooBytes struct {\n\t\tFoo []byte\n\t}\n\n\tlongKey := strings.Repeat(\"k\", 16_000_000)\n\ttLongKey := reflect.StructOf([]reflect.StructField{\n\t\t{\n\t\t\tName: \"Foo\",\n\t\t\tType: reflect.TypeOf(false),\n\t\t\tTag:  reflect.StructTag(fmt.Sprintf(`bson:\"%s\"`, longKey)),\n\t\t},\n\t})\n\n\treturn []unmarshalingTestCase{\n\t\t{\n\t\t\tname: \"small struct\",\n\t\t\tsType: reflect.TypeOf(struct {\n\t\t\t\tFoo bool\n\t\t\t}{}),\n\t\t\twant: &struct {\n\t\t\t\tFoo bool\n\t\t\t}{Foo: true},\n\t\t\tdata: docToBytes(D{{\"foo\", true}}),\n\t\t},\n\t\t{\n\t\t\tname: \"nested document\",\n\t\t\tsType: reflect.TypeOf(struct {\n\t\t\t\tFoo struct {\n\t\t\t\t\tBar bool\n\t\t\t\t}\n\t\t\t}{}),\n\t\t\twant: &struct {\n\t\t\t\tFoo struct {\n\t\t\t\t\tBar bool\n\t\t\t\t}\n\t\t\t}{\n\t\t\t\tFoo: struct {\n\t\t\t\t\tBar bool\n\t\t\t\t}{Bar: true},\n\t\t\t},\n\t\t\tdata: docToBytes(D{{\"foo\", D{{\"bar\", true}}}}),\n\t\t},\n\t\t{\n\t\t\tname: \"simple array\",\n\t\t\tsType: reflect.TypeOf(struct {\n\t\t\t\tFoo []bool\n\t\t\t}{}),\n\t\t\twant: &struct {\n\t\t\t\tFoo []bool\n\t\t\t}{\n\t\t\t\tFoo: []bool{true},\n\t\t\t},\n\t\t\tdata: docToBytes(D{{\"foo\", A{true}}}),\n\t\t},\n\t\t{\n\t\t\tname: \"struct with mixed case fields\",\n\t\t\tsType: reflect.TypeOf(struct {\n\t\t\t\tFooBar int32\n\t\t\t}{}),\n\t\t\twant: &struct {\n\t\t\t\tFooBar int32\n\t\t\t}{\n\t\t\t\tFooBar: 10,\n\t\t\t},\n\t\t\tdata: docToBytes(D{{\"fooBar\", int32(10)}}),\n\t\t},\n\t\t// GODRIVER-2252\n\t\t// Test that a struct of pointer types with UnmarshalBSON functions defined marshal and\n\t\t// unmarshal to the same Go values when the pointer values are \"nil\".\n\t\t{\n\t\t\tname:  \"nil pointer fields with UnmarshalBSON function should marshal and unmarshal to the same values\",\n\t\t\tsType: reflect.TypeOf(unmarshalerPtrStruct{}),\n\t\t\twant:  &unmarshalerPtrStruct{},\n\t\t\tdata:  docToBytes(unmarshalerPtrStruct{}),\n\t\t},\n\t\t// GODRIVER-2252\n\t\t// Test that a struct of pointer types with UnmarshalBSON functions defined marshal and\n\t\t// unmarshal to the same Go values when the pointer values are the respective zero values.\n\t\t{\n\t\t\tname:  \"zero-value pointer fields with UnmarshalBSON function should marshal and unmarshal to the same values\",\n\t\t\tsType: reflect.TypeOf(unmarshalerPtrStruct{}),\n\t\t\twant:  &zeroPtrStruct,\n\t\t\tdata:  docToBytes(zeroPtrStruct),\n\t\t},\n\t\t// GODRIVER-2252\n\t\t// Test that a struct of pointer types with UnmarshalBSON functions defined marshal and\n\t\t// unmarshal to the same Go values when the pointer values are non-zero values.\n\t\t{\n\t\t\tname:  \"non-zero-value pointer fields with UnmarshalBSON function should marshal and unmarshal to the same values\",\n\t\t\tsType: reflect.TypeOf(unmarshalerPtrStruct{}),\n\t\t\twant:  &valPtrStruct,\n\t\t\tdata:  docToBytes(valPtrStruct),\n\t\t},\n\t\t// GODRIVER-2311\n\t\t// Test that an unmarshaled struct that has a byte slice value does not reference the same\n\t\t// underlying array as the input.\n\t\t{\n\t\t\tname:  \"struct with byte slice\",\n\t\t\tsType: reflect.TypeOf(fooBytes{}),\n\t\t\twant: &fooBytes{\n\t\t\t\tFoo: []byte{0, 1, 2, 3, 4, 5},\n\t\t\t},\n\t\t\tdata: docToBytes(fooBytes{\n\t\t\t\tFoo: []byte{0, 1, 2, 3, 4, 5},\n\t\t\t}),\n\t\t},\n\t\t// GODRIVER-2427\n\t\t// Test that a struct of non-pointer types with UnmarshalBSON functions defined for the pointer of the field\n\t\t// will marshal and unmarshal to the same Go values when the non-pointer values are the respctive zero values.\n\t\t{\n\t\t\tname: `zero-value non-pointer fields with pointer UnmarshalBSON function should marshal and unmarshal to\n\t\t\tthe same values`,\n\t\t\tsType: reflect.TypeOf(unmarshalerNonPtrStruct{}),\n\t\t\twant:  &zeroNonPtrStruct,\n\t\t\tdata:  docToBytes(zeroNonPtrStruct),\n\t\t},\n\t\t// GODRIVER-2427\n\t\t// Test that a struct of non-pointer types with UnmarshalBSON functions defined for the pointer of the field\n\t\t// unmarshal to the same Go values when the non-pointer values are non-zero values.\n\t\t{\n\t\t\tname: `non-zero-value non-pointer fields with pointer UnmarshalBSON function should marshal and unmarshal\n\t\t\tto the same values`,\n\t\t\tsType: reflect.TypeOf(unmarshalerNonPtrStruct{}),\n\t\t\twant:  &valNonPtrStruct,\n\t\t\tdata:  docToBytes(valNonPtrStruct),\n\t\t},\n\t\t{\n\t\t\tname:  \"nil pointer and non-pointer type with literal null BSON\",\n\t\t\tsType: reflect.TypeOf(unmarshalBehaviorTestCase{}),\n\t\t\twant: &unmarshalBehaviorTestCase{\n\t\t\t\tBSONValueTracker: unmarshalBSONValueCallTracker{\n\t\t\t\t\tcalled: true,\n\t\t\t\t},\n\t\t\t\tBSONValuePtrTracker: nil,\n\t\t\t\tBSONTracker: unmarshalBSONCallTracker{\n\t\t\t\t\tcalled: true,\n\t\t\t\t},\n\t\t\t\tBSONPtrTracker: nil,\n\t\t\t},\n\t\t\tdata: docToBytes(D{\n\t\t\t\t{Key: \"bv_tracker\", Value: nil},\n\t\t\t\t{Key: \"bv_ptr_tracker\", Value: nil},\n\t\t\t\t{Key: \"b_tracker\", Value: nil},\n\t\t\t\t{Key: \"b_ptr_tracker\", Value: nil},\n\t\t\t}),\n\t\t},\n\t\t{\n\t\t\tname:  \"nil pointer and non-pointer type with BSON minkey\",\n\t\t\tsType: reflect.TypeOf(unmarshalBehaviorTestCase{}),\n\t\t\twant: &unmarshalBehaviorTestCase{\n\t\t\t\tBSONValueTracker: unmarshalBSONValueCallTracker{\n\t\t\t\t\tcalled: true,\n\t\t\t\t},\n\t\t\t\tBSONValuePtrTracker: &unmarshalBSONValueCallTracker{\n\t\t\t\t\tcalled: true,\n\t\t\t\t},\n\t\t\t\tBSONTracker: unmarshalBSONCallTracker{\n\t\t\t\t\tcalled: true,\n\t\t\t\t},\n\t\t\t\tBSONPtrTracker: nil,\n\t\t\t},\n\t\t\tdata: docToBytes(D{\n\t\t\t\t{Key: \"bv_tracker\", Value: MinKey{}},\n\t\t\t\t{Key: \"bv_ptr_tracker\", Value: MinKey{}},\n\t\t\t\t{Key: \"b_tracker\", Value: MinKey{}},\n\t\t\t\t{Key: \"b_ptr_tracker\", Value: MinKey{}},\n\t\t\t}),\n\t\t},\n\t\t{\n\t\t\tname:  \"nil pointer and non-pointer type with BSON maxkey\",\n\t\t\tsType: reflect.TypeOf(unmarshalBehaviorTestCase{}),\n\t\t\twant: &unmarshalBehaviorTestCase{\n\t\t\t\tBSONValueTracker: unmarshalBSONValueCallTracker{\n\t\t\t\t\tcalled: true,\n\t\t\t\t},\n\t\t\t\tBSONValuePtrTracker: &unmarshalBSONValueCallTracker{\n\t\t\t\t\tcalled: true,\n\t\t\t\t},\n\t\t\t\tBSONTracker: unmarshalBSONCallTracker{\n\t\t\t\t\tcalled: true,\n\t\t\t\t},\n\t\t\t\tBSONPtrTracker: nil,\n\t\t\t},\n\t\t\tdata: docToBytes(D{\n\t\t\t\t{Key: \"bv_tracker\", Value: MaxKey{}},\n\t\t\t\t{Key: \"bv_ptr_tracker\", Value: MaxKey{}},\n\t\t\t\t{Key: \"b_tracker\", Value: MaxKey{}},\n\t\t\t\t{Key: \"b_ptr_tracker\", Value: MaxKey{}},\n\t\t\t}),\n\t\t},\n\t\t{\n\t\t\tname:  \"long key\",\n\t\t\tsType: tLongKey,\n\t\t\twant: func() any {\n\t\t\t\tvLongKey := reflect.New(tLongKey)\n\t\t\t\tvLongKey.Elem().Field(0).SetBool(true)\n\t\t\t\treturn vLongKey.Interface()\n\t\t\t}(),\n\t\t\tdata: docToBytes(D{{longKey, true}}),\n\t\t},\n\t}\n}\n\n// unmarshalerPtrStruct contains a collection of fields that are all pointers to custom types that\n// implement the bson.Unmarshaler interface. It is used to test the BSON unmarshal behavior for\n// pointer types with custom UnmarshalBSON functions.\ntype unmarshalerPtrStruct struct {\n\tI *myInt64\n\tM *myMap\n\tB *myBytes\n\tS *myString\n}\n\n// unmarshalerNonPtrStruct contains a collection of non-pointer fields that are all to custom types that implement the\n// bson.Unmarshaler interface. It is used to test the BSON unmarshal behavior for types with custom UnmarshalBSON\n// functions.\ntype unmarshalerNonPtrStruct struct {\n\tI myInt64\n\tM myMap\n\tB myBytes\n\tS myString\n}\n\ntype myInt64 int64\n\nvar _ ValueUnmarshaler = (*myInt64)(nil)\n\nfunc (mi *myInt64) UnmarshalBSONValue(t byte, b []byte) error {\n\tif len(b) == 0 {\n\t\treturn nil\n\t}\n\n\tif Type(t) == TypeInt64 {\n\t\ti, err := newBufferedValueReader(TypeInt64, b).ReadInt64()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\t*mi = myInt64(i)\n\t}\n\n\treturn nil\n}\n\nfunc (mi *myInt64) UnmarshalBSON(b []byte) error {\n\tif len(b) == 0 {\n\t\treturn nil\n\t}\n\ti, err := newBufferedValueReader(TypeInt64, b).ReadInt64()\n\tif err != nil {\n\t\treturn err\n\t}\n\t*mi = myInt64(i)\n\treturn nil\n}\n\ntype myMap map[string]string\n\nfunc (mm *myMap) UnmarshalBSON(bytes []byte) error {\n\tif len(bytes) == 0 {\n\t\treturn nil\n\t}\n\tvar m map[string]string\n\terr := Unmarshal(bytes, &m)\n\t*mm = myMap(m)\n\treturn err\n}\n\ntype myBytes []byte\n\nfunc (mb *myBytes) UnmarshalBSON(b []byte) error {\n\tif len(b) == 0 {\n\t\treturn nil\n\t}\n\tb, _, err := newBufferedValueReader(TypeBinary, b).ReadBinary()\n\tif err != nil {\n\t\treturn err\n\t}\n\t*mb = b\n\treturn nil\n}\n\ntype myString string\n\nfunc (ms *myString) UnmarshalBSON(b []byte) error {\n\tif len(b) == 0 {\n\t\treturn nil\n\t}\n\ts, err := newBufferedValueReader(TypeString, b).ReadString()\n\tif err != nil {\n\t\treturn err\n\t}\n\t*ms = myString(s)\n\treturn nil\n}\n\n// unmarshalBSONValueCallTracker is a test struct that tracks whether the\n// UnmarshalBSONValue method has been called.\ntype unmarshalBSONValueCallTracker struct {\n\tcalled bool // called is set to true when UnmarshalBSONValue is invoked.\n}\n\nvar _ ValueUnmarshaler = &unmarshalBSONValueCallTracker{}\n\n// unmarshalBSONCallTracker is a test struct that tracks whether the\n// UnmarshalBSON method has been called.\ntype unmarshalBSONCallTracker struct {\n\tcalled bool // called is set to true when UnmarshalBSON is invoked.\n}\n\n// Ensure unmarshalBSONCallTracker implements the Unmarshaler interface.\nvar _ Unmarshaler = &unmarshalBSONCallTracker{}\n\n// unmarshalBehaviorTestCase holds instances of call trackers for testing BSON\n// unmarshaling behavior.\ntype unmarshalBehaviorTestCase struct {\n\tBSONValueTracker    unmarshalBSONValueCallTracker  `bson:\"bv_tracker\"`     // BSON value unmarshaling by value.\n\tBSONValuePtrTracker *unmarshalBSONValueCallTracker `bson:\"bv_ptr_tracker\"` // BSON value unmarshaling by pointer.\n\tBSONTracker         unmarshalBSONCallTracker       `bson:\"b_tracker\"`      // BSON unmarshaling by value.\n\tBSONPtrTracker      *unmarshalBSONCallTracker      `bson:\"b_ptr_tracker\"`  // BSON unmarshaling by pointer.\n}\n\nfunc (tracker *unmarshalBSONValueCallTracker) UnmarshalBSONValue(byte, []byte) error {\n\ttracker.called = true\n\treturn nil\n}\n\nfunc (tracker *unmarshalBSONCallTracker) UnmarshalBSON([]byte) error {\n\ttracker.called = true\n\treturn nil\n}\n"
  },
  {
    "path": "bson/value_reader.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bson\n\nimport (\n\t\"bufio\"\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"math\"\n\t\"sync\"\n)\n\ntype byteSrc interface {\n\tio.ByteReader\n\n\treadExact(p []byte) (int, error)\n\n\t// Peek returns the next n bytes without advancing the cursor. It must return\n\t// exactly n bytes or n error if fewer are available.\n\tpeek(n int) ([]byte, error)\n\n\t// discard advanced the cursor by n bytes, returning the actual number\n\t// discarded or an error if fewer were available.\n\tdiscard(n int) (int, error)\n\n\t// readSlice reads until (and including) the first occurrence of delim,\n\t// returning the entire slice [start...delimiter] and advancing the cursor.\n\t// past it. Returns an error if delim is not found.\n\treadSlice(delim byte) ([]byte, error)\n\n\t// pos returns the number of bytes consumed so far.\n\tpos() int64\n\n\t// regexLength returns the total byte length of a BSON regex value (two\n\t// C-strings including their terminating NULs) in buffered mode.\n\tregexLength() (int32, error)\n\n\t// streamable returns true if this source can be used in a streaming context.\n\tstreamable() bool\n\n\t// reset resets the source to its initial state.\n\treset()\n}\n\nvar _ ValueReader = &valueReader{}\n\n// ErrEOA is the error returned when the end of a BSON array has been reached.\nvar ErrEOA = errors.New(\"end of array\")\n\n// ErrEOD is the error returned when the end of a BSON document has been reached.\nvar ErrEOD = errors.New(\"end of document\")\n\ntype vrState struct {\n\tmode  mode\n\tvType Type\n\tend   int64\n}\n\nvar vrPool = sync.Pool{\n\tNew: func() any {\n\t\treturn &valueReader{\n\t\t\tstack: make([]vrState, 1, 5),\n\t\t}\n\t},\n}\n\n// valueReader is for reading BSON values.\ntype valueReader struct {\n\tsrc    byteSrc\n\toffset int64\n\n\tstack []vrState\n\tframe int64\n}\n\nfunc getBufferedDocumentReader(b []byte) *valueReader {\n\treturn newBufferedDocumentReader(b)\n}\n\nfunc putBufferedDocumentReader(vr *valueReader) {\n\tif vr == nil {\n\t\treturn\n\t}\n\n\tvr.src.reset()\n\n\t// Reset src and stack to avoid holding onto memory.\n\tvr.src = nil\n\tvr.frame = 0\n\tvr.stack = vr.stack[:0]\n\n\tvrPool.Put(vr)\n}\n\n// NewDocumentReader returns a ValueReader using b for the underlying BSON\n// representation.\nfunc NewDocumentReader(r io.Reader) ValueReader {\n\tstack := make([]vrState, 1, 5)\n\tstack[0] = vrState{\n\t\tmode: mTopLevel,\n\t}\n\n\treturn &valueReader{\n\t\tsrc:   &streamingByteSrc{br: bufio.NewReader(r), offset: 0},\n\t\tstack: stack,\n\t}\n}\n\n// newBufferedValueReader returns a ValueReader that starts in the Value mode\n// instead of in top level document mode. This enables the creation of a\n// ValueReader for a single BSON value.\nfunc newBufferedValueReader(t Type, b []byte) ValueReader {\n\tbVR := newBufferedDocumentReader(b)\n\n\tbVR.stack[0].vType = t\n\tbVR.stack[0].mode = mValue\n\n\treturn bVR\n}\n\nfunc newBufferedDocumentReader(b []byte) *valueReader {\n\tvr := vrPool.Get().(*valueReader)\n\n\tvr.src = &bufferedByteSrc{\n\t\tbuf:    b,\n\t\toffset: 0,\n\t}\n\n\t// Reset parse state.\n\tvr.frame = 0\n\tif cap(vr.stack) < 1 {\n\t\tvr.stack = make([]vrState, 1, 5)\n\t} else {\n\t\tvr.stack = vr.stack[:1]\n\t}\n\n\tvr.stack[0] = vrState{\n\t\tmode: mTopLevel,\n\t\tend:  int64(len(b)),\n\t}\n\n\treturn vr\n}\n\nfunc (vr *valueReader) advanceFrame() {\n\tif vr.frame+1 >= int64(len(vr.stack)) { // We need to grow the stack\n\t\tlength := len(vr.stack)\n\t\tif length+1 >= cap(vr.stack) {\n\t\t\t// double it\n\t\t\tbuf := make([]vrState, 2*cap(vr.stack)+1)\n\t\t\tcopy(buf, vr.stack)\n\t\t\tvr.stack = buf\n\t\t}\n\t\tvr.stack = vr.stack[:length+1]\n\t}\n\tvr.frame++\n\n\t// Clean the stack\n\tvr.stack[vr.frame].mode = 0\n\tvr.stack[vr.frame].vType = 0\n\tvr.stack[vr.frame].end = 0\n}\n\nfunc (vr *valueReader) pop() error {\n\tvar cnt int\n\tswitch vr.stack[vr.frame].mode {\n\tcase mElement, mValue:\n\t\tcnt = 1\n\tcase mDocument, mArray, mCodeWithScope:\n\t\tcnt = 2 // we pop twice to jump over the vrElement: vrDocument -> vrElement -> vrDocument/TopLevel/etc...\n\t}\n\tfor i := 0; i < cnt && vr.frame > 0; i++ {\n\t\tif vr.src.pos() < vr.stack[vr.frame].end {\n\t\t\t_, err := vr.src.discard(int(vr.stack[vr.frame].end - vr.src.pos()))\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\tvr.frame--\n\t}\n\n\tif vr.src.streamable() {\n\t\tif vr.frame == 0 {\n\t\t\tif vr.stack[0].end > vr.src.pos() {\n\t\t\t\tvr.stack[0].end -= vr.src.pos()\n\t\t\t} else {\n\t\t\t\tvr.stack[0].end = 0\n\t\t\t}\n\n\t\t\tvr.src.reset()\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc (vr *valueReader) invalidTransitionErr(destination mode, name string, modes []mode) error {\n\tte := TransitionError{\n\t\tname:        name,\n\t\tcurrent:     vr.stack[vr.frame].mode,\n\t\tdestination: destination,\n\t\tmodes:       modes,\n\t\taction:      \"read\",\n\t}\n\tif vr.frame != 0 {\n\t\tte.parent = vr.stack[vr.frame-1].mode\n\t}\n\treturn te\n}\n\nfunc (vr *valueReader) typeError(t Type) error {\n\treturn fmt.Errorf(\"positioned on %s, but attempted to read %s\", vr.stack[vr.frame].vType, t)\n}\n\nfunc (vr *valueReader) invalidDocumentLengthError() error {\n\treturn fmt.Errorf(\"document is invalid, end byte is at %d, but null byte found at %d\", vr.stack[vr.frame].end, vr.offset)\n}\n\nfunc (vr *valueReader) ensureElementValue(t Type, destination mode, callerName string) error {\n\tswitch vr.stack[vr.frame].mode {\n\tcase mElement, mValue:\n\t\tif vr.stack[vr.frame].vType != t {\n\t\t\treturn vr.typeError(t)\n\t\t}\n\tdefault:\n\t\treturn vr.invalidTransitionErr(destination, callerName, []mode{mElement, mValue})\n\t}\n\n\treturn nil\n}\n\nfunc (vr *valueReader) Type() Type {\n\treturn vr.stack[vr.frame].vType\n}\n\n// peekNextValueSize returns the length of the next value in the stream without\n// offsetting the reader position.\nfunc peekNextValueSize(vr *valueReader) (int32, error) {\n\tvar length int32\n\tvar err error\n\tswitch vr.stack[vr.frame].vType {\n\tcase TypeArray, TypeEmbeddedDocument, TypeCodeWithScope:\n\t\tlength, err = vr.peekLength()\n\tcase TypeBinary:\n\t\tlength, err = vr.peekLength()\n\t\tlength += 4 + 1 // binary length + subtype byte\n\tcase TypeBoolean:\n\t\tlength = 1\n\tcase TypeDBPointer:\n\t\tlength, err = vr.peekLength()\n\t\tlength += 4 + 12 // string length + ObjectID length\n\tcase TypeDateTime, TypeDouble, TypeInt64, TypeTimestamp:\n\t\tlength = 8\n\tcase TypeDecimal128:\n\t\tlength = 16\n\tcase TypeInt32:\n\t\tlength = 4\n\tcase TypeJavaScript, TypeString, TypeSymbol:\n\t\tlength, err = vr.peekLength()\n\t\tlength += 4\n\tcase TypeMaxKey, TypeMinKey, TypeNull, TypeUndefined:\n\t\tlength = 0\n\tcase TypeObjectID:\n\t\tlength = 12\n\tcase TypeRegex:\n\t\tlength, err = vr.src.regexLength()\n\tdefault:\n\t\treturn 0, fmt.Errorf(\"attempted to read bytes of unknown BSON type %v\", vr.stack[vr.frame].vType)\n\t}\n\n\treturn length, err\n}\n\n// readBytes tries to grab the next n bytes zero-allocation using peek+discard.\n// If peek fails (e.g. bufio buffer full), it falls back to io.ReadFull.\nfunc readBytes(src byteSrc, n int) ([]byte, error) {\n\tif src.streamable() {\n\t\tdata := make([]byte, n)\n\t\tif _, err := src.readExact(data); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\treturn data, nil\n\t}\n\n\t// Zero-allocation path.\n\tbuf, err := src.peek(n)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t_, _ = src.discard(n) // Discard the bytes from the source.\n\treturn buf, nil\n}\n\n// readBytesValueReader returns a subslice [offset, offset+length) or EOF.\nfunc (vr *valueReader) readBytes(n int32) ([]byte, error) {\n\tif n < 0 {\n\t\treturn nil, fmt.Errorf(\"invalid length: %d\", n)\n\t}\n\n\treturn readBytes(vr.src, int(n))\n}\n\n//nolint:unparam\nfunc (vr *valueReader) readValueBytes(dst []byte) (Type, []byte, error) {\n\tswitch vr.stack[vr.frame].mode {\n\tcase mTopLevel:\n\t\tlength, err := vr.peekLength()\n\t\tif err != nil {\n\t\t\treturn 0, nil, err\n\t\t}\n\t\tb, err := vr.readBytes(length)\n\t\treturn Type(0), append(dst, b...), err\n\tcase mElement, mValue:\n\t\tt := vr.stack[vr.frame].vType\n\n\t\tlength, err := peekNextValueSize(vr)\n\t\tif err != nil {\n\t\t\treturn t, dst, err\n\t\t}\n\n\t\tb, err := vr.readBytes(length)\n\n\t\tif err := vr.pop(); err != nil {\n\t\t\treturn Type(0), nil, err\n\t\t}\n\n\t\treturn t, append(dst, b...), err\n\n\tdefault:\n\t\treturn Type(0), nil, vr.invalidTransitionErr(0, \"readValueBytes\", []mode{mElement, mValue})\n\t}\n}\n\nfunc (vr *valueReader) Skip() error {\n\tswitch vr.stack[vr.frame].mode {\n\tcase mElement, mValue:\n\tdefault:\n\t\treturn vr.invalidTransitionErr(0, \"Skip\", []mode{mElement, mValue})\n\t}\n\n\tlength, err := peekNextValueSize(vr)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t_, err = vr.src.discard(int(length))\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn vr.pop()\n}\n\n// ReadArray returns an ArrayReader for the next BSON array in the valueReader\n// source, advancing the reader position to the end of the array.\nfunc (vr *valueReader) ReadArray() (ArrayReader, error) {\n\tif err := vr.ensureElementValue(TypeArray, mArray, \"ReadArray\"); err != nil {\n\t\treturn nil, err\n\t}\n\n\t// Push a new frame for the array.\n\tvr.advanceFrame()\n\n\t// Read the 4-byte length.\n\tsize, err := vr.readLength()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// Compute the end position: current position + total size - length.\n\tvr.stack[vr.frame].mode = mArray\n\tvr.stack[vr.frame].end = vr.src.pos() + int64(size) - 4\n\n\treturn vr, nil\n}\n\n// ReadBinary reads a BSON binary value, returning the byte slice and the\n// type of the binary data (0x02 for old binary, 0x00 for new binary, etc.),\n// advancing the reader position to the end of the binary value.\nfunc (vr *valueReader) ReadBinary() ([]byte, byte, error) {\n\tif err := vr.ensureElementValue(TypeBinary, 0, \"ReadBinary\"); err != nil {\n\t\treturn nil, 0, err\n\t}\n\n\tlength, err := vr.readLength()\n\tif err != nil {\n\t\treturn nil, 0, err\n\t}\n\n\tbtype, err := vr.readByte()\n\tif err != nil {\n\t\treturn nil, 0, err\n\t}\n\n\t// Check length in case it is an old binary without a length.\n\tif btype == 0x02 && length > 4 {\n\t\tlength, err = vr.readLength()\n\t\tif err != nil {\n\t\t\treturn nil, 0, err\n\t\t}\n\t}\n\n\tb, err := vr.readBytes(length)\n\tif err != nil {\n\t\treturn nil, 0, err\n\t}\n\n\t// copy so user doesn’t share underlying buffer\n\tcp := make([]byte, len(b))\n\tcopy(cp, b)\n\n\tif err := vr.pop(); err != nil {\n\t\treturn nil, 0, err\n\t}\n\n\treturn cp, btype, nil\n}\n\n// ReadBoolean reads a BSON boolean value, returning true or false, advancing\n// the reader position to the end of the boolean value.\nfunc (vr *valueReader) ReadBoolean() (bool, error) {\n\tif err := vr.ensureElementValue(TypeBoolean, 0, \"ReadBoolean\"); err != nil {\n\t\treturn false, err\n\t}\n\n\tb, err := vr.readByte()\n\tif err != nil {\n\t\treturn false, err\n\t}\n\n\tif b > 1 {\n\t\treturn false, fmt.Errorf(\"invalid byte for boolean, %b\", b)\n\t}\n\n\tif err := vr.pop(); err != nil {\n\t\treturn false, err\n\t}\n\treturn b == 1, nil\n}\n\n// ReadDocument reads a BSON embedded document, returning a DocumentReader,\n// advancing the reader position to the end of the document.\nfunc (vr *valueReader) ReadDocument() (DocumentReader, error) {\n\tswitch vr.stack[vr.frame].mode {\n\tcase mTopLevel:\n\t\tlength, err := vr.readLength()\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif length <= 4 {\n\t\t\treturn nil, fmt.Errorf(\"invalid string length: %d\", length)\n\t\t}\n\n\t\tvr.stack[vr.frame].end = int64(length) + vr.src.pos() - 4\n\t\treturn vr, nil\n\tcase mElement, mValue:\n\t\tif vr.stack[vr.frame].vType != TypeEmbeddedDocument {\n\t\t\treturn nil, vr.typeError(TypeEmbeddedDocument)\n\t\t}\n\tdefault:\n\t\treturn nil, vr.invalidTransitionErr(mDocument, \"ReadDocument\", []mode{mTopLevel, mElement, mValue})\n\t}\n\n\tvr.advanceFrame()\n\n\tsize, err := vr.readLength()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvr.stack[vr.frame].mode = mDocument\n\tvr.stack[vr.frame].end = int64(size) + vr.src.pos() - 4\n\n\treturn vr, nil\n}\n\n// ReadCodeWithScope reads a BSON CodeWithScope value, returning the code as a\n// string, advancing the reader position to the end of the CodeWithScope value.\nfunc (vr *valueReader) ReadCodeWithScope() (string, DocumentReader, error) {\n\tif err := vr.ensureElementValue(TypeCodeWithScope, 0, \"ReadCodeWithScope\"); err != nil {\n\t\treturn \"\", nil, err\n\t}\n\n\ttotalLength, err := vr.readLength()\n\tif err != nil {\n\t\treturn \"\", nil, err\n\t}\n\tstrLength, err := vr.readLength()\n\tif err != nil {\n\t\treturn \"\", nil, err\n\t}\n\tif strLength <= 0 {\n\t\treturn \"\", nil, fmt.Errorf(\"invalid string length: %d\", strLength)\n\t}\n\tbuf, err := vr.readBytes(strLength)\n\tif err != nil {\n\t\treturn \"\", nil, err\n\t}\n\n\tcode := string(buf[:len(buf)-1])\n\tvr.advanceFrame()\n\n\t// Use readLength to ensure that we are not out of bounds.\n\tsize, err := vr.readLength()\n\tif err != nil {\n\t\treturn \"\", nil, err\n\t}\n\n\tvr.stack[vr.frame].mode = mCodeWithScope\n\tvr.stack[vr.frame].end = vr.src.pos() + int64(size) - 4\n\n\t// The total length should equal:\n\t// 4 (total length) + strLength + 4 (the length of str itself) + (document length)\n\tcomponentsLength := int64(4+strLength+4) + int64(size)\n\tif int64(totalLength) != componentsLength {\n\t\treturn \"\", nil, fmt.Errorf(\n\t\t\t\"length of CodeWithScope does not match lengths of components; total: %d; components: %d\",\n\t\t\ttotalLength, componentsLength,\n\t\t)\n\t}\n\treturn code, vr, nil\n}\n\n// ReadDBPointer reads a BSON DBPointer value, returning the namespace, the\n// object ID, and an error if any, advancing the reader position to the end of\n// the DBPointer value.\nfunc (vr *valueReader) ReadDBPointer() (string, ObjectID, error) {\n\tif err := vr.ensureElementValue(TypeDBPointer, 0, \"ReadDBPointer\"); err != nil {\n\t\treturn \"\", ObjectID{}, err\n\t}\n\tns, err := vr.readString()\n\tif err != nil {\n\t\treturn \"\", ObjectID{}, err\n\t}\n\n\toidBytes, err := vr.readBytes(12)\n\tif err != nil {\n\t\treturn \"\", ObjectID{}, err\n\t}\n\n\tvar oid ObjectID\n\tcopy(oid[:], oidBytes)\n\n\tif err := vr.pop(); err != nil {\n\t\treturn \"\", ObjectID{}, err\n\t}\n\treturn ns, oid, nil\n}\n\n// ReadDateTime reads a BSON DateTime value, advancing the reader position to\n// the end of the DateTime value.\nfunc (vr *valueReader) ReadDateTime() (int64, error) {\n\tif err := vr.ensureElementValue(TypeDateTime, 0, \"ReadDateTime\"); err != nil {\n\t\treturn 0, err\n\t}\n\n\ti, err := vr.readi64()\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\tif err := vr.pop(); err != nil {\n\t\treturn 0, err\n\t}\n\treturn i, nil\n}\n\n// ReadDecimal128 reads a BSON Decimal128 value, advancing the reader\n// to the end of the Decimal128 value.\nfunc (vr *valueReader) ReadDecimal128() (Decimal128, error) {\n\tif err := vr.ensureElementValue(TypeDecimal128, 0, \"ReadDecimal128\"); err != nil {\n\t\treturn Decimal128{}, err\n\t}\n\tb, err := vr.readBytes(16)\n\tif err != nil {\n\t\treturn Decimal128{}, err\n\t}\n\tl := binary.LittleEndian.Uint64(b[0:8])\n\th := binary.LittleEndian.Uint64(b[8:16])\n\n\tif err := vr.pop(); err != nil {\n\t\treturn Decimal128{}, err\n\t}\n\treturn NewDecimal128(h, l), nil\n}\n\n// ReadDouble reads a BSON double value, advancing the reader position to\n// to the end of the double value.\nfunc (vr *valueReader) ReadDouble() (float64, error) {\n\tif err := vr.ensureElementValue(TypeDouble, 0, \"ReadDouble\"); err != nil {\n\t\treturn 0, err\n\t}\n\n\tu, err := vr.readu64()\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\tif err := vr.pop(); err != nil {\n\t\treturn 0, err\n\t}\n\treturn math.Float64frombits(u), nil\n}\n\n// ReadInt32 reads a BSON int32 value, advancing the reader position to the end\n// of the int32 value.\nfunc (vr *valueReader) ReadInt32() (int32, error) {\n\tif err := vr.ensureElementValue(TypeInt32, 0, \"ReadInt32\"); err != nil {\n\t\treturn 0, err\n\t}\n\ti, err := vr.readi32()\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\tif err := vr.pop(); err != nil {\n\t\treturn 0, err\n\t}\n\treturn i, nil\n}\n\n// ReadInt64 reads a BSON int64 value, advancing the reader position to the end\n// of the int64 value.\nfunc (vr *valueReader) ReadInt64() (int64, error) {\n\tif err := vr.ensureElementValue(TypeInt64, 0, \"ReadInt64\"); err != nil {\n\t\treturn 0, err\n\t}\n\ti, err := vr.readi64()\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\tif err := vr.pop(); err != nil {\n\t\treturn 0, err\n\t}\n\treturn i, nil\n}\n\n// ReadJavascript reads a BSON JavaScript value, advancing the reader\n// to the end of the JavaScript value.\nfunc (vr *valueReader) ReadJavascript() (string, error) {\n\tif err := vr.ensureElementValue(TypeJavaScript, 0, \"ReadJavascript\"); err != nil {\n\t\treturn \"\", err\n\t}\n\ts, err := vr.readString()\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\tif err := vr.pop(); err != nil {\n\t\treturn \"\", err\n\t}\n\treturn s, nil\n}\n\n// ReadMaxKey reads a BSON MaxKey value, advancing the reader position to the\n// end of the MaxKey value.\nfunc (vr *valueReader) ReadMaxKey() error {\n\tif err := vr.ensureElementValue(TypeMaxKey, 0, \"ReadMaxKey\"); err != nil {\n\t\treturn err\n\t}\n\n\treturn vr.pop()\n}\n\n// ReadMinKey reads a BSON MinKey value, advancing the reader position to the\n// end of the MinKey value.\nfunc (vr *valueReader) ReadMinKey() error {\n\tif err := vr.ensureElementValue(TypeMinKey, 0, \"ReadMinKey\"); err != nil {\n\t\treturn err\n\t}\n\n\treturn vr.pop()\n}\n\n// REadNull reads a BSON Null value, advancing the reader position to the\n// end of the Null value.\nfunc (vr *valueReader) ReadNull() error {\n\tif err := vr.ensureElementValue(TypeNull, 0, \"ReadNull\"); err != nil {\n\t\treturn err\n\t}\n\n\treturn vr.pop()\n}\n\n// ReadObjectID reads a BSON ObjectID value, advancing the reader to the end of\n// the ObjectID value.\nfunc (vr *valueReader) ReadObjectID() (ObjectID, error) {\n\tif err := vr.ensureElementValue(TypeObjectID, 0, \"ReadObjectID\"); err != nil {\n\t\treturn ObjectID{}, err\n\t}\n\n\toidBytes, err := vr.readBytes(12)\n\tif err != nil {\n\t\treturn ObjectID{}, err\n\t}\n\n\tvar oid ObjectID\n\tcopy(oid[:], oidBytes)\n\n\tif err := vr.pop(); err != nil {\n\t\treturn ObjectID{}, err\n\t}\n\treturn oid, nil\n}\n\n// ReadRegex reads a BSON Regex value, advancing the reader position to the\n// regex value.\nfunc (vr *valueReader) ReadRegex() (string, string, error) {\n\tif err := vr.ensureElementValue(TypeRegex, 0, \"ReadRegex\"); err != nil {\n\t\treturn \"\", \"\", err\n\t}\n\n\tpattern, err := vr.readCString()\n\tif err != nil {\n\t\treturn \"\", \"\", err\n\t}\n\n\toptions, err := vr.readCString()\n\tif err != nil {\n\t\treturn \"\", \"\", err\n\t}\n\n\tif err := vr.pop(); err != nil {\n\t\treturn \"\", \"\", err\n\t}\n\treturn pattern, options, nil\n}\n\n// ReadString reads a BSON String value, advancing the reader position to the\n// end of the String value.\nfunc (vr *valueReader) ReadString() (string, error) {\n\tif err := vr.ensureElementValue(TypeString, 0, \"ReadString\"); err != nil {\n\t\treturn \"\", err\n\t}\n\ts, err := vr.readString()\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\tif err := vr.pop(); err != nil {\n\t\treturn \"\", err\n\t}\n\treturn s, nil\n}\n\n// ReadSymbol reads a BSON Symbol value, advancing the reader position to the\n// end of the Symbol value.\nfunc (vr *valueReader) ReadSymbol() (string, error) {\n\tif err := vr.ensureElementValue(TypeSymbol, 0, \"ReadSymbol\"); err != nil {\n\t\treturn \"\", err\n\t}\n\ts, err := vr.readString()\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tif err := vr.pop(); err != nil {\n\t\treturn \"\", err\n\t}\n\treturn s, nil\n}\n\n// ReadTimestamp reads a BSON Timestamp value, advancing the reader to the end\n// of the Timestamp value.\nfunc (vr *valueReader) ReadTimestamp() (uint32, uint32, error) {\n\tif err := vr.ensureElementValue(TypeTimestamp, 0, \"ReadTimestamp\"); err != nil {\n\t\treturn 0, 0, err\n\t}\n\n\ti, err := vr.readu32()\n\tif err != nil {\n\t\treturn 0, 0, err\n\t}\n\n\tt, err := vr.readu32()\n\tif err != nil {\n\t\treturn 0, 0, err\n\t}\n\n\tif err := vr.pop(); err != nil {\n\t\treturn 0, 0, err\n\t}\n\treturn t, i, nil\n}\n\n// ReadUndefined reads a BSON Undefined value, advancing the reader position\n// to the end of the Undefined value.\nfunc (vr *valueReader) ReadUndefined() error {\n\tif err := vr.ensureElementValue(TypeUndefined, 0, \"ReadUndefined\"); err != nil {\n\t\treturn err\n\t}\n\n\treturn vr.pop()\n}\n\n// ReadElement reads the next element in the BSON document, advancing the\n// reader position to the end of the element.\nfunc (vr *valueReader) ReadElement() (string, ValueReader, error) {\n\tswitch vr.stack[vr.frame].mode {\n\tcase mTopLevel, mDocument, mCodeWithScope:\n\tdefault:\n\t\treturn \"\", nil, vr.invalidTransitionErr(mElement, \"ReadElement\", []mode{mTopLevel, mDocument, mCodeWithScope})\n\t}\n\n\tt, err := vr.readByte()\n\tif err != nil {\n\t\treturn \"\", nil, err\n\t}\n\n\tif t == 0 {\n\t\tif vr.src.pos() != vr.stack[vr.frame].end {\n\t\t\treturn \"\", nil, vr.invalidDocumentLengthError()\n\t\t}\n\n\t\t_ = vr.pop() // Ignore the error because the call here never reads from the underlying reader.\n\t\treturn \"\", nil, ErrEOD\n\t}\n\n\tname, err := vr.readCString()\n\tif err != nil {\n\t\treturn \"\", nil, err\n\t}\n\n\tvr.advanceFrame()\n\n\tvr.stack[vr.frame].mode = mElement\n\tvr.stack[vr.frame].vType = Type(t)\n\treturn name, vr, nil\n}\n\n// ReadValue reads the next value in the BSON array, advancing the to the end of\n// the value.\nfunc (vr *valueReader) ReadValue() (ValueReader, error) {\n\tswitch vr.stack[vr.frame].mode {\n\tcase mArray:\n\tdefault:\n\t\treturn nil, vr.invalidTransitionErr(mValue, \"ReadValue\", []mode{mArray})\n\t}\n\n\tt, err := vr.readByte()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif t == 0 {\n\t\tif vr.src.pos() != vr.stack[vr.frame].end {\n\t\t\treturn nil, vr.invalidDocumentLengthError()\n\t\t}\n\n\t\t_ = vr.pop() // Ignore the error because the call here never reads from the underlying reader.\n\t\treturn nil, ErrEOA\n\t}\n\n\t_, err = vr.src.readSlice(0x00)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvr.advanceFrame()\n\n\tvr.stack[vr.frame].mode = mValue\n\tvr.stack[vr.frame].vType = Type(t)\n\treturn vr, nil\n}\n\nfunc (vr *valueReader) readByte() (byte, error) {\n\tb, err := vr.src.ReadByte()\n\tif err != nil {\n\t\treturn 0x0, err\n\t}\n\treturn b, nil\n}\n\nfunc (vr *valueReader) readCString() (string, error) {\n\tdata, err := vr.src.readSlice(0x00)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\treturn string(data[:len(data)-1]), nil\n}\n\nfunc (vr *valueReader) readString() (string, error) {\n\tlength, err := vr.readLength()\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\tif length <= 0 {\n\t\treturn \"\", fmt.Errorf(\"invalid string length: %d\", length)\n\t}\n\n\traw, err := readBytes(vr.src, int(length))\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\t// Check that the last byte is the NUL terminator.\n\tif raw[len(raw)-1] != 0x00 {\n\t\treturn \"\", fmt.Errorf(\"string does not end with null byte, but with %v\", raw[len(raw)-1])\n\t}\n\n\t// Convert and strip the trailing NUL.\n\treturn string(raw[:len(raw)-1]), nil\n}\n\nfunc (vr *valueReader) peekLength() (int32, error) {\n\tbuf, err := vr.src.peek(4)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\treturn int32(binary.LittleEndian.Uint32(buf)), nil\n}\n\nfunc (vr *valueReader) readLength() (int32, error) {\n\treturn vr.readi32()\n}\n\nfunc (vr *valueReader) readi32() (int32, error) {\n\traw, err := readBytes(vr.src, 4)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\treturn int32(binary.LittleEndian.Uint32(raw)), nil\n}\n\nfunc (vr *valueReader) readu32() (uint32, error) {\n\traw, err := readBytes(vr.src, 4)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\treturn binary.LittleEndian.Uint32(raw), nil\n}\n\nfunc (vr *valueReader) readi64() (int64, error) {\n\traw, err := readBytes(vr.src, 8)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\treturn int64(binary.LittleEndian.Uint64(raw)), nil\n}\n\nfunc (vr *valueReader) readu64() (uint64, error) {\n\traw, err := readBytes(vr.src, 8)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\treturn binary.LittleEndian.Uint64(raw), nil\n}\n"
  },
  {
    "path": "bson/value_reader_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bson\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t_ \"embed\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"math\"\n\t\"testing\"\n\n\t\"github.com/google/go-cmp/cmp\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n)\n\n//go:embed testdata/lorem.txt\nvar lorem []byte\n\nvar testcstring = append(lorem, []byte{0x00}...)\n\nfunc TestValueReader_ReadBinary(t *testing.T) {\n\ttestCases := []struct {\n\t\tname  string\n\t\tdata  []byte\n\t\tbtype byte\n\t\tb     []byte\n\t\terr   error\n\t\tvType Type\n\t}{\n\t\t{\n\t\t\tname:  \"incorrect type\",\n\t\t\tdata:  []byte{},\n\t\t\tbtype: 0,\n\t\t\tb:     nil,\n\t\t\terr:   (&valueReader{stack: []vrState{{vType: TypeEmbeddedDocument}}, frame: 0}).typeError(TypeBinary),\n\t\t\tvType: TypeEmbeddedDocument,\n\t\t},\n\t\t{\n\t\t\tname:  \"length too short\",\n\t\t\tdata:  []byte{},\n\t\t\tbtype: 0,\n\t\t\tb:     nil,\n\t\t\terr:   io.EOF,\n\t\t\tvType: TypeBinary,\n\t\t},\n\t\t{\n\t\t\tname:  \"no byte available\",\n\t\t\tdata:  []byte{0x00, 0x00, 0x00, 0x00},\n\t\t\tbtype: 0,\n\t\t\tb:     nil,\n\t\t\terr:   io.EOF,\n\t\t\tvType: TypeBinary,\n\t\t},\n\t\t{\n\t\t\tname:  \"not enough bytes for binary\",\n\t\t\tdata:  []byte{0x05, 0x00, 0x00, 0x00, 0x00},\n\t\t\tbtype: 0,\n\t\t\tb:     nil,\n\t\t\terr:   io.EOF,\n\t\t\tvType: TypeBinary,\n\t\t},\n\t\t{\n\t\t\tname:  \"success\",\n\t\t\tdata:  []byte{0x03, 0x00, 0x00, 0x00, 0xEA, 0x01, 0x02, 0x03},\n\t\t\tbtype: 0xEA,\n\t\t\tb:     []byte{0x01, 0x02, 0x03},\n\t\t\terr:   nil,\n\t\t\tvType: TypeBinary,\n\t\t},\n\t}\n\n\tfor _, tc := range testCases {\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\tt.Run(\"buffered\", func(t *testing.T) {\n\t\t\t\tvr := &valueReader{\n\t\t\t\t\tsrc: &bufferedByteSrc{buf: tc.data},\n\t\t\t\t\tstack: []vrState{\n\t\t\t\t\t\t{mode: mTopLevel},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tmode:  mElement,\n\t\t\t\t\t\t\tvType: tc.vType,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\tframe: 1,\n\t\t\t\t}\n\n\t\t\t\tb, btype, err := vr.ReadBinary()\n\t\t\t\tif !errequal(t, err, tc.err) {\n\t\t\t\t\tt.Errorf(\"Returned errors do not match. got %v; want %v\", err, tc.err)\n\t\t\t\t}\n\t\t\t\tif btype != tc.btype {\n\t\t\t\t\tt.Errorf(\"Incorrect binary type returned. got %v; want %v\", btype, tc.btype)\n\t\t\t\t}\n\t\t\t\tif !bytes.Equal(b, tc.b) {\n\t\t\t\t\tt.Errorf(\"Binary data does not match. got %v; want %v\", b, tc.b)\n\t\t\t\t}\n\t\t\t})\n\n\t\t\tt.Run(\"streaming\", func(t *testing.T) {\n\t\t\t\tvr := &valueReader{\n\t\t\t\t\tsrc: &streamingByteSrc{br: bufio.NewReader(bytes.NewReader(tc.data))},\n\t\t\t\t\tstack: []vrState{\n\t\t\t\t\t\t{mode: mTopLevel},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tmode:  mElement,\n\t\t\t\t\t\t\tvType: tc.vType,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\tframe: 1,\n\t\t\t\t}\n\n\t\t\t\tb, btype, err := vr.ReadBinary()\n\t\t\t\tif !errequal(t, err, tc.err) {\n\t\t\t\t\tt.Errorf(\"Returned errors do not match. got %v; want %v\", err, tc.err)\n\t\t\t\t}\n\t\t\t\tif btype != tc.btype {\n\t\t\t\t\tt.Errorf(\"Incorrect binary type returned. got %v; want %v\", btype, tc.btype)\n\t\t\t\t}\n\t\t\t\tif !bytes.Equal(b, tc.b) {\n\t\t\t\t\tt.Errorf(\"Binary data does not match. got %v; want %v\", b, tc.b)\n\t\t\t\t}\n\t\t\t})\n\t\t})\n\t}\n}\n\nfunc TestValueReader_ReadBoolean(t *testing.T) {\n\ttestCases := []struct {\n\t\tname    string\n\t\tdata    []byte\n\t\tboolean bool\n\t\terr     error\n\t\tvType   Type\n\t}{\n\t\t{\n\t\t\tname:    \"incorrect type\",\n\t\t\tdata:    []byte{},\n\t\t\tboolean: false,\n\t\t\terr:     (&valueReader{stack: []vrState{{vType: TypeEmbeddedDocument}}, frame: 0}).typeError(TypeBoolean),\n\t\t\tvType:   TypeEmbeddedDocument,\n\t\t},\n\t\t{\n\t\t\tname:    \"no byte available\",\n\t\t\tdata:    []byte{},\n\t\t\tboolean: false,\n\t\t\terr:     io.EOF,\n\t\t\tvType:   TypeBoolean,\n\t\t},\n\t\t{\n\t\t\tname:    \"invalid byte for boolean\",\n\t\t\tdata:    []byte{0x03},\n\t\t\tboolean: false,\n\t\t\terr:     fmt.Errorf(\"invalid byte for boolean, %b\", 0x03),\n\t\t\tvType:   TypeBoolean,\n\t\t},\n\t\t{\n\t\t\tname:    \"success\",\n\t\t\tdata:    []byte{0x01},\n\t\t\tboolean: true,\n\t\t\terr:     nil,\n\t\t\tvType:   TypeBoolean,\n\t\t},\n\t}\n\n\tfor _, tc := range testCases {\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\tt.Run(\"buffered\", func(t *testing.T) {\n\t\t\t\tvr := &valueReader{\n\t\t\t\t\tsrc: &bufferedByteSrc{buf: tc.data},\n\t\t\t\t\tstack: []vrState{\n\t\t\t\t\t\t{mode: mTopLevel},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tmode:  mElement,\n\t\t\t\t\t\t\tvType: tc.vType,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\tframe: 1,\n\t\t\t\t}\n\n\t\t\t\tboolean, err := vr.ReadBoolean()\n\t\t\t\tif !errequal(t, err, tc.err) {\n\t\t\t\t\tt.Errorf(\"Returned errors do not match. got %v; want %v\", err, tc.err)\n\t\t\t\t}\n\t\t\t\tif boolean != tc.boolean {\n\t\t\t\t\tt.Errorf(\"Incorrect boolean returned. got %v; want %v\", boolean, tc.boolean)\n\t\t\t\t}\n\t\t\t})\n\n\t\t\tt.Run(\"streaming\", func(t *testing.T) {\n\t\t\t\tvr := &valueReader{\n\t\t\t\t\tsrc: &streamingByteSrc{br: bufio.NewReader(bytes.NewReader(tc.data))},\n\t\t\t\t\tstack: []vrState{\n\t\t\t\t\t\t{mode: mTopLevel},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tmode:  mElement,\n\t\t\t\t\t\t\tvType: tc.vType,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\tframe: 1,\n\t\t\t\t}\n\n\t\t\t\tboolean, err := vr.ReadBoolean()\n\t\t\t\tif !errequal(t, err, tc.err) {\n\t\t\t\t\tt.Errorf(\"Returned errors do not match. got %v; want %v\", err, tc.err)\n\t\t\t\t}\n\t\t\t\tif boolean != tc.boolean {\n\t\t\t\t\tt.Errorf(\"Incorrect boolean returned. got %v; want %v\", boolean, tc.boolean)\n\t\t\t\t}\n\t\t\t})\n\t\t})\n\t}\n}\n\nfunc TestValueReader_ReadDocument_TopLevel_InvalidLength(t *testing.T) {\n\tt.Run(\"buffered\", func(t *testing.T) {\n\t\tvr := &valueReader{\n\t\t\tsrc:   &bufferedByteSrc{buf: []byte{0x00, 0x00}},\n\t\t\tstack: []vrState{{mode: mTopLevel}},\n\t\t\tframe: 0,\n\t\t}\n\n\t\t_, err := vr.ReadDocument()\n\t\tif !errors.Is(err, io.EOF) {\n\t\t\tt.Errorf(\"Expected io.ErrUnexpectedEOF with document length too small. got %v; want %v\", err, io.EOF)\n\t\t}\n\t\tif vr.src.pos() != 0 {\n\t\t\tt.Errorf(\"Expected 0 offset. got %d\", vr.src.pos())\n\t\t}\n\t})\n\n\tt.Run(\"streaming\", func(t *testing.T) {\n\t\tvr := &valueReader{\n\t\t\tsrc:   &streamingByteSrc{br: bufio.NewReader(bytes.NewReader([]byte{0x00, 0x00}))},\n\t\t\tstack: []vrState{{mode: mTopLevel}},\n\t\t\tframe: 0,\n\t\t}\n\n\t\t_, err := vr.ReadDocument()\n\t\tif !errors.Is(err, io.ErrUnexpectedEOF) {\n\t\t\tt.Errorf(\"Expected io.ErrUnexpectedEOF with document length too small. got %v; want %v\", err, io.EOF)\n\t\t}\n\t\tif vr.src.pos() != 0 {\n\t\t\tt.Errorf(\"Expected 0 offset. got %d\", vr.src.pos())\n\t\t}\n\t})\n}\n\nfunc TestValueReader_ReadDocument_TopLevel_ValidDocumentWithIncorrectEnd(t *testing.T) {\n\tt.Run(\"buffered\", func(t *testing.T) {\n\t\tvr := &valueReader{\n\t\t\tsrc:   &bufferedByteSrc{buf: []byte{0x05, 0x00, 0x00, 0x00, 0x00}},\n\t\t\tstack: []vrState{{mode: mTopLevel}},\n\t\t\tframe: 0,\n\t\t}\n\n\t\t_, err := vr.ReadDocument()\n\t\tnoerr(t, err)\n\t\tif vr.stack[vr.frame].end != 5 {\n\t\t\tt.Errorf(\"Incorrect end for document. got %d; want %d\", vr.stack[vr.frame].end, 5)\n\t\t}\n\t})\n\n\tt.Run(\"streaming\", func(t *testing.T) {\n\t\tvr := &valueReader{\n\t\t\tsrc:   &streamingByteSrc{br: bufio.NewReader(bytes.NewReader([]byte{0x05, 0x00, 0x00, 0x00, 0x00}))},\n\t\t\tstack: []vrState{{mode: mTopLevel}},\n\t\t\tframe: 0,\n\t\t}\n\n\t\t_, err := vr.ReadDocument()\n\t\tnoerr(t, err)\n\t\tif vr.stack[vr.frame].end != 5 {\n\t\t\tt.Errorf(\"Incorrect end for document. got %d; want %d\", vr.stack[vr.frame].end, 5)\n\t\t}\n\t})\n}\n\nfunc TestValueReader_ReadDocument_EmbeddedDocument(t *testing.T) {\n\tt.Run(\"buffered\", func(t *testing.T) {\n\t\tvr := &valueReader{\n\t\t\tsrc: &bufferedByteSrc{buf: []byte{0x0a, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00}},\n\t\t\tstack: []vrState{\n\t\t\t\t{mode: mTopLevel},\n\t\t\t\t{mode: mElement, vType: TypeBoolean},\n\t\t\t},\n\t\t\tframe: 1,\n\t\t}\n\n\t\twanterr := (&valueReader{stack: []vrState{{mode: mElement, vType: TypeBoolean}}}).typeError(TypeEmbeddedDocument)\n\t\t_, err := vr.ReadDocument()\n\t\tif err == nil || err.Error() != wanterr.Error() {\n\t\t\tt.Errorf(\"Incorrect returned error. got %v; want %v\", err, wanterr)\n\t\t}\n\n\t\tvr.stack[1].mode = mArray\n\t\twanterr = vr.invalidTransitionErr(mDocument, \"ReadDocument\", []mode{mTopLevel, mElement, mValue})\n\t\t_, err = vr.ReadDocument()\n\t\tif err == nil || err.Error() != wanterr.Error() {\n\t\t\tt.Errorf(\"Incorrect returned error. got %v; want %v\", err, wanterr)\n\t\t}\n\n\t\tvr.stack[1].mode, vr.stack[1].vType = mElement, TypeEmbeddedDocument\n\t\t_, _ = vr.src.discard(4)\n\n\t\t_, err = vr.ReadDocument()\n\t\tnoerr(t, err)\n\t\tif len(vr.stack) != 3 {\n\t\t\tt.Errorf(\"Incorrect number of stack frames. got %d; want %d\", len(vr.stack), 3)\n\t\t}\n\t\tif vr.stack[2].mode != mDocument {\n\t\t\tt.Errorf(\"Incorrect mode set. got %v; want %v\", vr.stack[2].mode, mDocument)\n\t\t}\n\t\tif vr.stack[2].end != 9 {\n\t\t\tt.Errorf(\"End of embedded document is not correct. got %d; want %d\", vr.stack[2].end, 9)\n\t\t}\n\t\tif vr.src.pos() != 8 {\n\t\t\tt.Errorf(\"Offset not incremented correctly. got %d; want %d\", vr.src.pos(), 8)\n\t\t}\n\n\t\tvr.frame--\n\t\t_, err = vr.ReadDocument()\n\n\t\tif !errors.Is(err, io.EOF) {\n\t\t\tt.Errorf(\"Should return error when attempting to read length with not enough bytes. got %v; want %v\", err, io.EOF)\n\t\t}\n\t})\n\n\tt.Run(\"streaming\", func(t *testing.T) {\n\t\tvr := &valueReader{\n\t\t\tsrc: &streamingByteSrc{\n\t\t\t\tbr: bufio.NewReader(bytes.NewReader([]byte{0x0a, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00})),\n\t\t\t},\n\t\t\tstack: []vrState{\n\t\t\t\t{mode: mTopLevel},\n\t\t\t\t{mode: mElement, vType: TypeBoolean},\n\t\t\t},\n\t\t\tframe: 1,\n\t\t}\n\n\t\twanterr := (&valueReader{stack: []vrState{{mode: mElement, vType: TypeBoolean}}}).typeError(TypeEmbeddedDocument)\n\t\t_, err := vr.ReadDocument()\n\t\tif err == nil || err.Error() != wanterr.Error() {\n\t\t\tt.Errorf(\"Incorrect returned error. got %v; want %v\", err, wanterr)\n\t\t}\n\n\t\tvr.stack[1].mode = mArray\n\t\twanterr = vr.invalidTransitionErr(mDocument, \"ReadDocument\", []mode{mTopLevel, mElement, mValue})\n\t\t_, err = vr.ReadDocument()\n\t\tif err == nil || err.Error() != wanterr.Error() {\n\t\t\tt.Errorf(\"Incorrect returned error. got %v; want %v\", err, wanterr)\n\t\t}\n\n\t\tvr.stack[1].mode, vr.stack[1].vType = mElement, TypeEmbeddedDocument\n\t\t_, _ = vr.src.discard(4)\n\n\t\t_, err = vr.ReadDocument()\n\t\tnoerr(t, err)\n\t\tif len(vr.stack) != 3 {\n\t\t\tt.Errorf(\"Incorrect number of stack frames. got %d; want %d\", len(vr.stack), 3)\n\t\t}\n\t\tif vr.stack[2].mode != mDocument {\n\t\t\tt.Errorf(\"Incorrect mode set. got %v; want %v\", vr.stack[2].mode, mDocument)\n\t\t}\n\t\tif vr.stack[2].end != 9 {\n\t\t\tt.Errorf(\"End of embedded document is not correct. got %d; want %d\", vr.stack[2].end, 9)\n\t\t}\n\t\tif vr.src.pos() != 8 {\n\t\t\tt.Errorf(\"Offset not incremented correctly. got %d; want %d\", vr.src.pos(), 8)\n\t\t}\n\n\t\tvr.frame--\n\t\t_, err = vr.ReadDocument()\n\n\t\tif !errors.Is(err, io.ErrUnexpectedEOF) {\n\t\t\tt.Errorf(\"Should return error when attempting to read length with not enough bytes. got %v; want %v\", err, io.EOF)\n\t\t}\n\t})\n}\n\nfunc TestValueReader_ReadCodeWithScope(t *testing.T) {\n\tcodeWithScope := []byte{\n\t\t0x11, 0x00, 0x00, 0x00, // total length\n\t\t0x4, 0x00, 0x00, 0x00, // string length\n\t\t'f', 'o', 'o', 0x00, // string\n\t\t0x05, 0x00, 0x00, 0x00, 0x00, // document\n\t}\n\tmismatchCodeWithScope := []byte{\n\t\t0x11, 0x00, 0x00, 0x00, // total length\n\t\t0x4, 0x00, 0x00, 0x00, // string length\n\t\t'f', 'o', 'o', 0x00, // string\n\t\t0x07, 0x00, 0x00, 0x00, // document\n\t\t0x0A, 0x00, // null element, empty key\n\t\t0x00, // document end\n\t}\n\tinvalidCodeWithScope := []byte{\n\t\t0x7, 0x00, 0x00, 0x00, // total length\n\t\t0x0, 0x00, 0x00, 0x00, // string length = 0\n\t\t0x05, 0x00, 0x00, 0x00, 0x00, // document\n\t}\n\n\ttestCases := []struct {\n\t\tname  string\n\t\tdata  []byte\n\t\terr   error\n\t\tvType Type\n\t}{\n\t\t{\n\t\t\tname:  \"incorrect type\",\n\t\t\tdata:  []byte{},\n\t\t\terr:   (&valueReader{stack: []vrState{{vType: TypeEmbeddedDocument}}, frame: 0}).typeError(TypeCodeWithScope),\n\t\t\tvType: TypeEmbeddedDocument,\n\t\t},\n\t\t{\n\t\t\tname:  \"total length not enough bytes\",\n\t\t\tdata:  []byte{},\n\t\t\terr:   io.EOF,\n\t\t\tvType: TypeCodeWithScope,\n\t\t},\n\t\t{\n\t\t\tname:  \"string length not enough bytes\",\n\t\t\tdata:  codeWithScope[:4],\n\t\t\terr:   io.EOF,\n\t\t\tvType: TypeCodeWithScope,\n\t\t},\n\t\t{\n\t\t\tname:  \"not enough string bytes\",\n\t\t\tdata:  codeWithScope[:8],\n\t\t\terr:   io.EOF,\n\t\t\tvType: TypeCodeWithScope,\n\t\t},\n\t\t{\n\t\t\tname:  \"document length not enough bytes\",\n\t\t\tdata:  codeWithScope[:12],\n\t\t\terr:   io.EOF,\n\t\t\tvType: TypeCodeWithScope,\n\t\t},\n\t\t{\n\t\t\tname:  \"length mismatch\",\n\t\t\tdata:  mismatchCodeWithScope,\n\t\t\terr:   fmt.Errorf(\"length of CodeWithScope does not match lengths of components; total: %d; components: %d\", 17, 19),\n\t\t\tvType: TypeCodeWithScope,\n\t\t},\n\t\t{\n\t\t\tname:  \"invalid strLength\",\n\t\t\tdata:  invalidCodeWithScope,\n\t\t\terr:   fmt.Errorf(\"invalid string length: %d\", 0),\n\t\t\tvType: TypeCodeWithScope,\n\t\t},\n\t}\n\n\tfor _, tc := range testCases {\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\tt.Run(\"buffered\", func(t *testing.T) {\n\t\t\t\tvr := &valueReader{\n\t\t\t\t\tsrc: &bufferedByteSrc{buf: tc.data},\n\t\t\t\t\tstack: []vrState{\n\t\t\t\t\t\t{mode: mTopLevel},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tmode:  mElement,\n\t\t\t\t\t\t\tvType: tc.vType,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\tframe: 1,\n\t\t\t\t}\n\n\t\t\t\t_, _, err := vr.ReadCodeWithScope()\n\t\t\t\tif !errequal(t, err, tc.err) {\n\t\t\t\t\tt.Errorf(\"Returned errors do not match. got %v; want %v\", err, tc.err)\n\t\t\t\t}\n\t\t\t})\n\t\t})\n\n\t\tt.Run(\"streaming\", func(t *testing.T) {\n\t\t\tvr := &valueReader{\n\t\t\t\tsrc: &streamingByteSrc{br: bufio.NewReader(bytes.NewReader(tc.data))},\n\t\t\t\tstack: []vrState{\n\t\t\t\t\t{mode: mTopLevel},\n\t\t\t\t\t{\n\t\t\t\t\t\tmode:  mElement,\n\t\t\t\t\t\tvType: tc.vType,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tframe: 1,\n\t\t\t}\n\n\t\t\t_, _, err := vr.ReadCodeWithScope()\n\t\t\tif !errequal(t, err, tc.err) {\n\t\t\t\tt.Errorf(\"Returned errors do not match. got %v; want %v\", err, tc.err)\n\t\t\t}\n\t\t})\n\t}\n\n\tt.Run(\"buffered success\", func(t *testing.T) {\n\t\tdoc := []byte{0x00, 0x00, 0x00, 0x00}\n\t\tdoc = append(doc, codeWithScope...)\n\t\tdoc = append(doc, 0x00)\n\t\tvr := &valueReader{\n\t\t\tsrc: &bufferedByteSrc{buf: doc},\n\t\t\tstack: []vrState{\n\t\t\t\t{mode: mTopLevel},\n\t\t\t\t{mode: mElement, vType: TypeCodeWithScope},\n\t\t\t},\n\t\t\tframe: 1,\n\t\t}\n\t\t_, _ = vr.src.discard(4) // discard the document length\n\n\t\tcode, _, err := vr.ReadCodeWithScope()\n\t\tnoerr(t, err)\n\t\tif code != \"foo\" {\n\t\t\tt.Errorf(\"Code does not match. got %s; want %s\", code, \"foo\")\n\t\t}\n\t\tif len(vr.stack) != 3 {\n\t\t\tt.Errorf(\"Incorrect number of stack frames. got %d; want %d\", len(vr.stack), 3)\n\t\t}\n\t\tif vr.stack[2].mode != mCodeWithScope {\n\t\t\tt.Errorf(\"Incorrect mode set. got %v; want %v\", vr.stack[2].mode, mDocument)\n\t\t}\n\t\tif vr.stack[2].end != 21 {\n\t\t\tt.Errorf(\"End of scope is not correct. got %d; want %d\", vr.stack[2].end, 21)\n\t\t}\n\t\tif vr.src.pos() != 20 {\n\t\t\tt.Errorf(\"Offset not incremented correctly. got %d; want %d\", vr.src.pos(), 20)\n\t\t}\n\t})\n\n\tt.Run(\"streaming success\", func(t *testing.T) {\n\t\tdoc := []byte{0x00, 0x00, 0x00, 0x00}\n\t\tdoc = append(doc, codeWithScope...)\n\t\tdoc = append(doc, 0x00)\n\t\tvr := &valueReader{\n\t\t\tsrc: &streamingByteSrc{br: bufio.NewReader(bytes.NewReader(doc))},\n\t\t\tstack: []vrState{\n\t\t\t\t{mode: mTopLevel},\n\t\t\t\t{mode: mElement, vType: TypeCodeWithScope},\n\t\t\t},\n\t\t\tframe: 1,\n\t\t}\n\t\t_, _ = vr.src.discard(4) // discard the document length\n\n\t\tcode, _, err := vr.ReadCodeWithScope()\n\t\tnoerr(t, err)\n\t\tif code != \"foo\" {\n\t\t\tt.Errorf(\"Code does not match. got %s; want %s\", code, \"foo\")\n\t\t}\n\t\tif len(vr.stack) != 3 {\n\t\t\tt.Errorf(\"Incorrect number of stack frames. got %d; want %d\", len(vr.stack), 3)\n\t\t}\n\t\tif vr.stack[2].mode != mCodeWithScope {\n\t\t\tt.Errorf(\"Incorrect mode set. got %v; want %v\", vr.stack[2].mode, mDocument)\n\t\t}\n\t\tif vr.stack[2].end != 21 {\n\t\t\tt.Errorf(\"End of scope is not correct. got %d; want %d\", vr.stack[2].end, 21)\n\t\t}\n\t\tif vr.src.pos() != 20 {\n\t\t\tt.Errorf(\"Offset not incremented correctly. got %d; want %d\", vr.src.pos(), 20)\n\t\t}\n\t})\n}\n\nfunc TestValueReader_ReadDBPointer(t *testing.T) {\n\ttestCases := []struct {\n\t\tname  string\n\t\tdata  []byte\n\t\tns    string\n\t\toid   ObjectID\n\t\terr   error\n\t\tvType Type\n\t}{\n\t\t{\n\t\t\tname:  \"incorrect type\",\n\t\t\tdata:  []byte{},\n\t\t\tns:    \"\",\n\t\t\toid:   ObjectID{},\n\t\t\terr:   (&valueReader{stack: []vrState{{vType: TypeEmbeddedDocument}}, frame: 0}).typeError(TypeDBPointer),\n\t\t\tvType: TypeEmbeddedDocument,\n\t\t},\n\t\t{\n\t\t\tname:  \"length too short\",\n\t\t\tdata:  []byte{},\n\t\t\tns:    \"\",\n\t\t\toid:   ObjectID{},\n\t\t\terr:   io.EOF,\n\t\t\tvType: TypeDBPointer,\n\t\t},\n\t\t{\n\t\t\tname:  \"not enough bytes for namespace\",\n\t\t\tdata:  []byte{0x04, 0x00, 0x00, 0x00},\n\t\t\tns:    \"\",\n\t\t\toid:   ObjectID{},\n\t\t\terr:   io.EOF,\n\t\t\tvType: TypeDBPointer,\n\t\t},\n\t\t{\n\t\t\tname:  \"not enough bytes for objectID\",\n\t\t\tdata:  []byte{0x04, 0x00, 0x00, 0x00, 'f', 'o', 'o', 0x00},\n\t\t\tns:    \"\",\n\t\t\toid:   ObjectID{},\n\t\t\terr:   io.EOF,\n\t\t\tvType: TypeDBPointer,\n\t\t},\n\t\t{\n\t\t\tname: \"success\",\n\t\t\tdata: []byte{\n\t\t\t\t0x04, 0x00, 0x00, 0x00, 'f', 'o', 'o', 0x00,\n\t\t\t\t0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C,\n\t\t\t},\n\t\t\tns:    \"foo\",\n\t\t\toid:   ObjectID{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C},\n\t\t\terr:   nil,\n\t\t\tvType: TypeDBPointer,\n\t\t},\n\t}\n\n\tfor _, tc := range testCases {\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\tt.Run(\"buffered\", func(t *testing.T) {\n\t\t\t\tvr := &valueReader{\n\t\t\t\t\tsrc: &bufferedByteSrc{buf: tc.data},\n\t\t\t\t\tstack: []vrState{\n\t\t\t\t\t\t{mode: mTopLevel},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tmode:  mElement,\n\t\t\t\t\t\t\tvType: tc.vType,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\tframe: 1,\n\t\t\t\t}\n\n\t\t\t\tns, oid, err := vr.ReadDBPointer()\n\t\t\t\tif !errequal(t, err, tc.err) {\n\t\t\t\t\tt.Errorf(\"Returned errors do not match. got %v; want %v\", err, tc.err)\n\t\t\t\t}\n\t\t\t\tif ns != tc.ns {\n\t\t\t\t\tt.Errorf(\"Incorrect namespace returned. got %v; want %v\", ns, tc.ns)\n\t\t\t\t}\n\t\t\t\tif oid != tc.oid {\n\t\t\t\t\tt.Errorf(\"ObjectIDs did not match. got %v; want %v\", oid, tc.oid)\n\t\t\t\t}\n\t\t\t})\n\n\t\t\tt.Run(\"streaming\", func(t *testing.T) {\n\t\t\t\tvr := &valueReader{\n\t\t\t\t\tsrc: &streamingByteSrc{br: bufio.NewReader(bytes.NewReader(tc.data))},\n\t\t\t\t\tstack: []vrState{\n\t\t\t\t\t\t{mode: mTopLevel},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tmode:  mElement,\n\t\t\t\t\t\t\tvType: tc.vType,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\tframe: 1,\n\t\t\t\t}\n\n\t\t\t\tns, oid, err := vr.ReadDBPointer()\n\t\t\t\tif !errequal(t, err, tc.err) {\n\t\t\t\t\tt.Errorf(\"Returned errors do not match. got %v; want %v\", err, tc.err)\n\t\t\t\t}\n\t\t\t\tif ns != tc.ns {\n\t\t\t\t\tt.Errorf(\"Incorrect namespace returned. got %v; want %v\", ns, tc.ns)\n\t\t\t\t}\n\t\t\t\tif oid != tc.oid {\n\t\t\t\t\tt.Errorf(\"ObjectIDs did not match. got %v; want %v\", oid, tc.oid)\n\t\t\t\t}\n\t\t\t})\n\t\t})\n\t}\n}\n\nfunc TestValueReader_ReadDateTime(t *testing.T) {\n\ttestCases := []struct {\n\t\tname  string\n\t\tdata  []byte\n\t\tdt    int64\n\t\terr   error\n\t\tvType Type\n\t}{\n\t\t{\n\t\t\tname:  \"incorrect type\",\n\t\t\tdata:  []byte{},\n\t\t\tdt:    0,\n\t\t\terr:   (&valueReader{stack: []vrState{{vType: TypeEmbeddedDocument}}, frame: 0}).typeError(TypeDateTime),\n\t\t\tvType: TypeEmbeddedDocument,\n\t\t},\n\t\t{\n\t\t\tname:  \"length too short\",\n\t\t\tdata:  []byte{},\n\t\t\tdt:    0,\n\t\t\terr:   io.EOF,\n\t\t\tvType: TypeDateTime,\n\t\t},\n\t\t{\n\t\t\tname:  \"success\",\n\t\t\tdata:  []byte{0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},\n\t\t\tdt:    255,\n\t\t\terr:   nil,\n\t\t\tvType: TypeDateTime,\n\t\t},\n\t}\n\n\tfor _, tc := range testCases {\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\tt.Run(\"buffered\", func(t *testing.T) {\n\t\t\t\tvr := &valueReader{\n\t\t\t\t\tsrc: &bufferedByteSrc{buf: tc.data},\n\t\t\t\t\tstack: []vrState{\n\t\t\t\t\t\t{mode: mTopLevel},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tmode:  mElement,\n\t\t\t\t\t\t\tvType: tc.vType,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\tframe: 1,\n\t\t\t\t}\n\n\t\t\t\tdt, err := vr.ReadDateTime()\n\t\t\t\tif !errequal(t, err, tc.err) {\n\t\t\t\t\tt.Errorf(\"Returned errors do not match. got %v; want %v\", err, tc.err)\n\t\t\t\t}\n\t\t\t\tif dt != tc.dt {\n\t\t\t\t\tt.Errorf(\"Incorrect datetime returned. got %d; want %d\", dt, tc.dt)\n\t\t\t\t}\n\t\t\t})\n\n\t\t\tt.Run(\"streaming\", func(t *testing.T) {\n\t\t\t\tvr := &valueReader{\n\t\t\t\t\tsrc: &streamingByteSrc{br: bufio.NewReader(bytes.NewReader(tc.data))},\n\t\t\t\t\tstack: []vrState{\n\t\t\t\t\t\t{mode: mTopLevel},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tmode:  mElement,\n\t\t\t\t\t\t\tvType: tc.vType,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\tframe: 1,\n\t\t\t\t}\n\n\t\t\t\tdt, err := vr.ReadDateTime()\n\t\t\t\tif !errequal(t, err, tc.err) {\n\t\t\t\t\tt.Errorf(\"Returned errors do not match. got %v; want %v\", err, tc.err)\n\t\t\t\t}\n\t\t\t\tif dt != tc.dt {\n\t\t\t\t\tt.Errorf(\"Incorrect datetime returned. got %d; want %d\", dt, tc.dt)\n\t\t\t\t}\n\t\t\t})\n\t\t})\n\t}\n}\n\nfunc TestValueReader_ReadDecimal128(t *testing.T) {\n\ttestCases := []struct {\n\t\tname  string\n\t\tdata  []byte\n\t\tdc128 Decimal128\n\t\terr   error\n\t\tvType Type\n\t}{\n\t\t{\n\t\t\tname:  \"incorrect type\",\n\t\t\tdata:  []byte{},\n\t\t\tdc128: Decimal128{},\n\t\t\terr:   (&valueReader{stack: []vrState{{vType: TypeEmbeddedDocument}}, frame: 0}).typeError(TypeDecimal128),\n\t\t\tvType: TypeEmbeddedDocument,\n\t\t},\n\t\t{\n\t\t\tname:  \"length too short\",\n\t\t\tdata:  []byte{},\n\t\t\tdc128: Decimal128{},\n\t\t\terr:   io.EOF,\n\t\t\tvType: TypeDecimal128,\n\t\t},\n\t\t{\n\t\t\tname: \"success\",\n\t\t\tdata: []byte{\n\t\t\t\t0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Low\n\t\t\t\t0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // High\n\t\t\t},\n\t\t\tdc128: NewDecimal128(65280, 255),\n\t\t\terr:   nil,\n\t\t\tvType: TypeDecimal128,\n\t\t},\n\t}\n\n\tfor _, tc := range testCases {\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\tt.Run(\"buffered\", func(t *testing.T) {\n\t\t\t\tvr := &valueReader{\n\t\t\t\t\tsrc: &bufferedByteSrc{buf: tc.data},\n\t\t\t\t\tstack: []vrState{\n\t\t\t\t\t\t{mode: mTopLevel},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tmode:  mElement,\n\t\t\t\t\t\t\tvType: tc.vType,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\tframe: 1,\n\t\t\t\t}\n\n\t\t\t\tdc128, err := vr.ReadDecimal128()\n\t\t\t\tif !errequal(t, err, tc.err) {\n\t\t\t\t\tt.Errorf(\"Returned errors do not match. got %v; want %v\", err, tc.err)\n\t\t\t\t}\n\t\t\t\tgotHigh, gotLow := dc128.GetBytes()\n\t\t\t\twantHigh, wantLow := tc.dc128.GetBytes()\n\t\t\t\tif gotHigh != wantHigh {\n\t\t\t\t\tt.Errorf(\"Returned high byte does not match. got %d; want %d\", gotHigh, wantHigh)\n\t\t\t\t}\n\t\t\t\tif gotLow != wantLow {\n\t\t\t\t\tt.Errorf(\"Returned low byte does not match. got %d; want %d\", gotLow, wantLow)\n\t\t\t\t}\n\t\t\t})\n\n\t\t\tt.Run(\"streaming\", func(t *testing.T) {\n\t\t\t\tvr := &valueReader{\n\t\t\t\t\tsrc: &streamingByteSrc{br: bufio.NewReader(bytes.NewReader(tc.data))},\n\t\t\t\t\tstack: []vrState{\n\t\t\t\t\t\t{mode: mTopLevel},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tmode:  mElement,\n\t\t\t\t\t\t\tvType: tc.vType,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\tframe: 1,\n\t\t\t\t}\n\n\t\t\t\tdc128, err := vr.ReadDecimal128()\n\t\t\t\tif !errequal(t, err, tc.err) {\n\t\t\t\t\tt.Errorf(\"Returned errors do not match. got %v; want %v\", err, tc.err)\n\t\t\t\t}\n\t\t\t\tgotHigh, gotLow := dc128.GetBytes()\n\t\t\t\twantHigh, wantLow := tc.dc128.GetBytes()\n\t\t\t\tif gotHigh != wantHigh {\n\t\t\t\t\tt.Errorf(\"Returned high byte does not match. got %d; want %d\", gotHigh, wantHigh)\n\t\t\t\t}\n\t\t\t\tif gotLow != wantLow {\n\t\t\t\t\tt.Errorf(\"Returned low byte does not match. got %d; want %d\", gotLow, wantLow)\n\t\t\t\t}\n\t\t\t})\n\t\t})\n\t}\n}\n\nfunc TestValueReader_ReadDouble(t *testing.T) {\n\ttestCases := []struct {\n\t\tname   string\n\t\tdata   []byte\n\t\tdouble float64\n\t\terr    error\n\t\tvType  Type\n\t}{\n\t\t{\n\t\t\tname:   \"incorrect type\",\n\t\t\tdata:   []byte{},\n\t\t\tdouble: 0,\n\t\t\terr:    (&valueReader{stack: []vrState{{vType: TypeEmbeddedDocument}}, frame: 0}).typeError(TypeDouble),\n\t\t\tvType:  TypeEmbeddedDocument,\n\t\t},\n\t\t{\n\t\t\tname:   \"length too short\",\n\t\t\tdata:   []byte{},\n\t\t\tdouble: 0,\n\t\t\terr:    io.EOF,\n\t\t\tvType:  TypeDouble,\n\t\t},\n\t\t{\n\t\t\tname:   \"success\",\n\t\t\tdata:   []byte{0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},\n\t\t\tdouble: math.Float64frombits(255),\n\t\t\terr:    nil,\n\t\t\tvType:  TypeDouble,\n\t\t},\n\t}\n\n\tfor _, tc := range testCases {\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\tt.Run(\"buffered\", func(t *testing.T) {\n\t\t\t\tvr := &valueReader{\n\t\t\t\t\tsrc: &bufferedByteSrc{buf: tc.data},\n\t\t\t\t\tstack: []vrState{\n\t\t\t\t\t\t{mode: mTopLevel},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tmode:  mElement,\n\t\t\t\t\t\t\tvType: tc.vType,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\tframe: 1,\n\t\t\t\t}\n\n\t\t\t\tdouble, err := vr.ReadDouble()\n\t\t\t\tif !errequal(t, err, tc.err) {\n\t\t\t\t\tt.Errorf(\"Returned errors do not match. got %v; want %v\", err, tc.err)\n\t\t\t\t}\n\t\t\t\tif double != tc.double {\n\t\t\t\t\tt.Errorf(\"Incorrect double returned. got %f; want %f\", double, tc.double)\n\t\t\t\t}\n\t\t\t})\n\n\t\t\tt.Run(\"streaming\", func(t *testing.T) {\n\t\t\t\tvr := &valueReader{\n\t\t\t\t\tsrc: &streamingByteSrc{br: bufio.NewReader(bytes.NewReader(tc.data))},\n\t\t\t\t\tstack: []vrState{\n\t\t\t\t\t\t{mode: mTopLevel},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tmode:  mElement,\n\t\t\t\t\t\t\tvType: tc.vType,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\tframe: 1,\n\t\t\t\t}\n\n\t\t\t\tdouble, err := vr.ReadDouble()\n\t\t\t\tif !errequal(t, err, tc.err) {\n\t\t\t\t\tt.Errorf(\"Returned errors do not match. got %v; want %v\", err, tc.err)\n\t\t\t\t}\n\t\t\t\tif double != tc.double {\n\t\t\t\t\tt.Errorf(\"Incorrect double returned. got %f; want %f\", double, tc.double)\n\t\t\t\t}\n\t\t\t})\n\t\t})\n\t}\n}\n\nfunc TestValueReader_ReadInt32(t *testing.T) {\n\ttestCases := []struct {\n\t\tname  string\n\t\tdata  []byte\n\t\ti32   int32\n\t\terr   error\n\t\tvType Type\n\t}{\n\t\t{\n\t\t\tname:  \"incorrect type\",\n\t\t\tdata:  []byte{},\n\t\t\ti32:   0,\n\t\t\terr:   (&valueReader{stack: []vrState{{vType: TypeEmbeddedDocument}}, frame: 0}).typeError(TypeInt32),\n\t\t\tvType: TypeEmbeddedDocument,\n\t\t},\n\t\t{\n\t\t\tname:  \"length too short\",\n\t\t\tdata:  []byte{},\n\t\t\ti32:   0,\n\t\t\terr:   io.EOF,\n\t\t\tvType: TypeInt32,\n\t\t},\n\t\t{\n\t\t\tname:  \"success\",\n\t\t\tdata:  []byte{0xFF, 0x00, 0x00, 0x00},\n\t\t\ti32:   255,\n\t\t\terr:   nil,\n\t\t\tvType: TypeInt32,\n\t\t},\n\t}\n\n\tfor _, tc := range testCases {\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\tt.Run(\"buffered\", func(t *testing.T) {\n\t\t\t\tvr := &valueReader{\n\t\t\t\t\tsrc: &bufferedByteSrc{buf: tc.data},\n\t\t\t\t\tstack: []vrState{\n\t\t\t\t\t\t{mode: mTopLevel},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tmode:  mElement,\n\t\t\t\t\t\t\tvType: tc.vType,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\tframe: 1,\n\t\t\t\t}\n\n\t\t\t\ti32, err := vr.ReadInt32()\n\t\t\t\tif !errequal(t, err, tc.err) {\n\t\t\t\t\tt.Errorf(\"Returned errors do not match. got %v; want %v\", err, tc.err)\n\t\t\t\t}\n\t\t\t\tif i32 != tc.i32 {\n\t\t\t\t\tt.Errorf(\"Incorrect int32 returned. got %d; want %d\", i32, tc.i32)\n\t\t\t\t}\n\t\t\t})\n\n\t\t\tt.Run(\"streaming\", func(t *testing.T) {\n\t\t\t\tvr := &valueReader{\n\t\t\t\t\tsrc: &streamingByteSrc{br: bufio.NewReader(bytes.NewReader(tc.data))},\n\t\t\t\t\tstack: []vrState{\n\t\t\t\t\t\t{mode: mTopLevel},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tmode:  mElement,\n\t\t\t\t\t\t\tvType: tc.vType,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\tframe: 1,\n\t\t\t\t}\n\n\t\t\t\ti32, err := vr.ReadInt32()\n\t\t\t\tif !errequal(t, err, tc.err) {\n\t\t\t\t\tt.Errorf(\"Returned errors do not match. got %v; want %v\", err, tc.err)\n\t\t\t\t}\n\t\t\t\tif i32 != tc.i32 {\n\t\t\t\t\tt.Errorf(\"Incorrect int32 returned. got %d; want %d\", i32, tc.i32)\n\t\t\t\t}\n\t\t\t})\n\t\t})\n\t}\n}\n\nfunc TestValueReader_ReadInt64(t *testing.T) {\n\ttestCases := []struct {\n\t\tname  string\n\t\tdata  []byte\n\t\ti64   int64\n\t\terr   error\n\t\tvType Type\n\t}{\n\t\t{\n\t\t\tname:  \"incorrect type\",\n\t\t\tdata:  []byte{},\n\t\t\ti64:   0,\n\t\t\terr:   (&valueReader{stack: []vrState{{vType: TypeEmbeddedDocument}}, frame: 0}).typeError(TypeInt64),\n\t\t\tvType: TypeEmbeddedDocument,\n\t\t},\n\t\t{\n\t\t\tname:  \"length too short\",\n\t\t\tdata:  []byte{},\n\t\t\ti64:   0,\n\t\t\terr:   io.EOF,\n\t\t\tvType: TypeInt64,\n\t\t},\n\t\t{\n\t\t\tname:  \"success\",\n\t\t\tdata:  []byte{0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},\n\t\t\ti64:   255,\n\t\t\terr:   nil,\n\t\t\tvType: TypeInt64,\n\t\t},\n\t}\n\n\tfor _, tc := range testCases {\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\tt.Run(\"buffered\", func(t *testing.T) {\n\t\t\t\tvr := &valueReader{\n\t\t\t\t\tsrc: &bufferedByteSrc{buf: tc.data},\n\t\t\t\t\tstack: []vrState{\n\t\t\t\t\t\t{mode: mTopLevel},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tmode:  mElement,\n\t\t\t\t\t\t\tvType: tc.vType,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\tframe: 1,\n\t\t\t\t}\n\n\t\t\t\ti64, err := vr.ReadInt64()\n\t\t\t\tif !errequal(t, err, tc.err) {\n\t\t\t\t\tt.Errorf(\"Returned errors do not match. got %v; want %v\", err, tc.err)\n\t\t\t\t}\n\t\t\t\tif i64 != tc.i64 {\n\t\t\t\t\tt.Errorf(\"Incorrect int64 returned. got %d; want %d\", i64, tc.i64)\n\t\t\t\t}\n\t\t\t})\n\n\t\t\tt.Run(\"streaming\", func(t *testing.T) {\n\t\t\t\tvr := &valueReader{\n\t\t\t\t\tsrc: &streamingByteSrc{br: bufio.NewReader(bytes.NewReader(tc.data))},\n\t\t\t\t\tstack: []vrState{\n\t\t\t\t\t\t{mode: mTopLevel},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tmode:  mElement,\n\t\t\t\t\t\t\tvType: tc.vType,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\tframe: 1,\n\t\t\t\t}\n\n\t\t\t\ti64, err := vr.ReadInt64()\n\t\t\t\tif !errequal(t, err, tc.err) {\n\t\t\t\t\tt.Errorf(\"Returned errors do not match. got %v; want %v\", err, tc.err)\n\t\t\t\t}\n\t\t\t\tif i64 != tc.i64 {\n\t\t\t\t\tt.Errorf(\"Incorrect int64 returned. got %d; want %d\", i64, tc.i64)\n\t\t\t\t}\n\t\t\t})\n\t\t})\n\t}\n}\n\nfunc TestValueReader_ReadJavascript_ReadString_ReadSymbol(t *testing.T) {\n\ttestCases := []struct {\n\t\tname          string\n\t\tdata          []byte\n\t\tfn            func(*valueReader) (string, error)\n\t\tcss           string // code, string, symbol\n\t\tstreamingErr  error\n\t\tbufferedError error\n\t\tvType         Type\n\t}{\n\t\t{\n\t\t\tname:          \"ReadJavascript/incorrect type\",\n\t\t\tdata:          []byte{},\n\t\t\tfn:            (*valueReader).ReadJavascript,\n\t\t\tcss:           \"\",\n\t\t\tstreamingErr:  (&valueReader{stack: []vrState{{vType: TypeEmbeddedDocument}}, frame: 0}).typeError(TypeJavaScript),\n\t\t\tbufferedError: (&valueReader{stack: []vrState{{vType: TypeEmbeddedDocument}}, frame: 0}).typeError(TypeJavaScript),\n\t\t\tvType:         TypeEmbeddedDocument,\n\t\t},\n\t\t{\n\t\t\tname:          \"ReadString/incorrect type\",\n\t\t\tdata:          []byte{},\n\t\t\tfn:            (*valueReader).ReadString,\n\t\t\tcss:           \"\",\n\t\t\tstreamingErr:  (&valueReader{stack: []vrState{{vType: TypeEmbeddedDocument}}, frame: 0}).typeError(TypeString),\n\t\t\tbufferedError: (&valueReader{stack: []vrState{{vType: TypeEmbeddedDocument}}, frame: 0}).typeError(TypeString),\n\t\t\tvType:         TypeEmbeddedDocument,\n\t\t},\n\t\t{\n\t\t\tname:          \"ReadSymbol/incorrect type\",\n\t\t\tdata:          []byte{},\n\t\t\tfn:            (*valueReader).ReadSymbol,\n\t\t\tcss:           \"\",\n\t\t\tstreamingErr:  (&valueReader{stack: []vrState{{vType: TypeEmbeddedDocument}}, frame: 0}).typeError(TypeSymbol),\n\t\t\tbufferedError: (&valueReader{stack: []vrState{{vType: TypeEmbeddedDocument}}, frame: 0}).typeError(TypeSymbol),\n\t\t\tvType:         TypeEmbeddedDocument,\n\t\t},\n\t\t{\n\t\t\tname:          \"ReadJavascript/length too short\",\n\t\t\tdata:          []byte{},\n\t\t\tfn:            (*valueReader).ReadJavascript,\n\t\t\tcss:           \"\",\n\t\t\tstreamingErr:  io.EOF,\n\t\t\tbufferedError: io.EOF,\n\t\t\tvType:         TypeJavaScript,\n\t\t},\n\t\t{\n\t\t\tname:          \"ReadString/length too short\",\n\t\t\tdata:          []byte{},\n\t\t\tfn:            (*valueReader).ReadString,\n\t\t\tcss:           \"\",\n\t\t\tstreamingErr:  io.EOF,\n\t\t\tbufferedError: io.EOF,\n\t\t\tvType:         TypeString,\n\t\t},\n\t\t{\n\t\t\tname:          \"ReadString/long - length too short\",\n\t\t\tdata:          append([]byte{0x40, 0x27, 0x00, 0x00}, testcstring...),\n\t\t\tfn:            (*valueReader).ReadString,\n\t\t\tcss:           \"\",\n\t\t\tstreamingErr:  io.ErrUnexpectedEOF,\n\t\t\tbufferedError: io.EOF,\n\t\t\tvType:         TypeString,\n\t\t},\n\t\t{\n\t\t\tname:          \"ReadSymbol/length too short\",\n\t\t\tdata:          []byte{},\n\t\t\tfn:            (*valueReader).ReadSymbol,\n\t\t\tcss:           \"\",\n\t\t\tstreamingErr:  io.EOF,\n\t\t\tbufferedError: io.EOF,\n\t\t\tvType:         TypeSymbol,\n\t\t},\n\t\t{\n\t\t\tname:          \"ReadJavascript/incorrect end byte\",\n\t\t\tdata:          []byte{0x01, 0x00, 0x00, 0x00, 0x05},\n\t\t\tfn:            (*valueReader).ReadJavascript,\n\t\t\tcss:           \"\",\n\t\t\tstreamingErr:  fmt.Errorf(\"string does not end with null byte, but with %v\", 0x05),\n\t\t\tbufferedError: fmt.Errorf(\"string does not end with null byte, but with %v\", 0x05),\n\t\t\tvType:         TypeJavaScript,\n\t\t},\n\t\t{\n\t\t\tname:          \"ReadString/incorrect end byte\",\n\t\t\tdata:          []byte{0x01, 0x00, 0x00, 0x00, 0x05},\n\t\t\tfn:            (*valueReader).ReadString,\n\t\t\tcss:           \"\",\n\t\t\tstreamingErr:  fmt.Errorf(\"string does not end with null byte, but with %v\", 0x05),\n\t\t\tbufferedError: fmt.Errorf(\"string does not end with null byte, but with %v\", 0x05),\n\t\t\tvType:         TypeString,\n\t\t},\n\t\t{\n\t\t\tname:          \"ReadString/long - incorrect end byte\",\n\t\t\tdata:          append([]byte{0x35, 0x27, 0x00, 0x00}, testcstring[:len(testcstring)-1]...),\n\t\t\tfn:            (*valueReader).ReadString,\n\t\t\tcss:           \"\",\n\t\t\tstreamingErr:  fmt.Errorf(\"string does not end with null byte, but with %v\", 0x20),\n\t\t\tbufferedError: fmt.Errorf(\"string does not end with null byte, but with %v\", 0x20),\n\t\t\tvType:         TypeString,\n\t\t},\n\t\t{\n\t\t\tname:          \"ReadSymbol/incorrect end byte\",\n\t\t\tdata:          []byte{0x01, 0x00, 0x00, 0x00, 0x05},\n\t\t\tfn:            (*valueReader).ReadSymbol,\n\t\t\tcss:           \"\",\n\t\t\tstreamingErr:  fmt.Errorf(\"string does not end with null byte, but with %v\", 0x05),\n\t\t\tbufferedError: fmt.Errorf(\"string does not end with null byte, but with %v\", 0x05),\n\t\t\tvType:         TypeSymbol,\n\t\t},\n\t\t{\n\t\t\tname:          \"ReadJavascript/success\",\n\t\t\tdata:          []byte{0x04, 0x00, 0x00, 0x00, 'f', 'o', 'o', 0x00},\n\t\t\tfn:            (*valueReader).ReadJavascript,\n\t\t\tcss:           \"foo\",\n\t\t\tstreamingErr:  nil,\n\t\t\tbufferedError: nil,\n\t\t\tvType:         TypeJavaScript,\n\t\t},\n\t\t{\n\t\t\tname:          \"ReadString/success\",\n\t\t\tdata:          []byte{0x04, 0x00, 0x00, 0x00, 'f', 'o', 'o', 0x00},\n\t\t\tfn:            (*valueReader).ReadString,\n\t\t\tcss:           \"foo\",\n\t\t\tstreamingErr:  nil,\n\t\t\tbufferedError: nil,\n\t\t\tvType:         TypeString,\n\t\t},\n\t\t{\n\t\t\tname:          \"ReadString/long - success\",\n\t\t\tdata:          append([]byte{0x36, 0x27, 0x00, 0x00}, testcstring...),\n\t\t\tfn:            (*valueReader).ReadString,\n\t\t\tcss:           string(testcstring[:len(testcstring)-1]),\n\t\t\tstreamingErr:  nil,\n\t\t\tbufferedError: nil,\n\t\t\tvType:         TypeString,\n\t\t},\n\t\t{\n\t\t\tname:          \"ReadSymbol/success\",\n\t\t\tdata:          []byte{0x04, 0x00, 0x00, 0x00, 'f', 'o', 'o', 0x00},\n\t\t\tfn:            (*valueReader).ReadSymbol,\n\t\t\tcss:           \"foo\",\n\t\t\tstreamingErr:  nil,\n\t\t\tbufferedError: nil,\n\t\t\tvType:         TypeSymbol,\n\t\t},\n\t}\n\n\tfor _, tc := range testCases {\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\tt.Run(\"buffered\", func(t *testing.T) {\n\t\t\t\tvr := &valueReader{\n\t\t\t\t\tsrc: &bufferedByteSrc{buf: tc.data},\n\t\t\t\t\tstack: []vrState{\n\t\t\t\t\t\t{mode: mTopLevel},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tmode:  mElement,\n\t\t\t\t\t\t\tvType: tc.vType,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\tframe: 1,\n\t\t\t\t}\n\t\t\t\twantErr := tc.bufferedError\n\n\t\t\t\tcss, err := tc.fn(vr)\n\t\t\t\tif !errequal(t, err, wantErr) {\n\t\t\t\t\tt.Errorf(\"Returned errors do not match. got %v; want %v\", err, wantErr)\n\t\t\t\t}\n\t\t\t\tif css != tc.css {\n\t\t\t\t\tt.Errorf(\"Incorrect (JavaScript,String,Symbol) returned. got %s; want %s\", css, tc.css)\n\t\t\t\t}\n\t\t\t})\n\n\t\t\tt.Run(\"streaming\", func(t *testing.T) {\n\t\t\t\tvr := &valueReader{\n\t\t\t\t\tsrc: &streamingByteSrc{br: bufio.NewReader(bytes.NewReader(tc.data))},\n\t\t\t\t\tstack: []vrState{\n\t\t\t\t\t\t{mode: mTopLevel},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tmode:  mElement,\n\t\t\t\t\t\t\tvType: tc.vType,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\tframe: 1,\n\t\t\t\t}\n\t\t\t\twantErr := tc.streamingErr\n\n\t\t\t\tcss, err := tc.fn(vr)\n\t\t\t\tif !errequal(t, err, wantErr) {\n\t\t\t\t\tt.Errorf(\"Returned errors do not match. got %v; want %v\", err, wantErr)\n\t\t\t\t}\n\t\t\t\tif css != tc.css {\n\t\t\t\t\tt.Errorf(\"Incorrect (JavaScript,String,Symbol) returned. got %s; want %s\", css, tc.css)\n\t\t\t\t}\n\t\t\t})\n\t\t})\n\t}\n}\n\nfunc TestValueReader_ReadMaxKey_ReadMinKey_ReadNull_ReadUndefined(t *testing.T) {\n\ttestCases := []struct {\n\t\tname         string\n\t\tfn           func(*valueReader) error\n\t\tstreamingErr error\n\t\tbufferedErr  error\n\t\tvType        Type\n\t}{\n\t\t{\n\t\t\tname:         \"ReadMaxKey/incorrect type\",\n\t\t\tfn:           (*valueReader).ReadMaxKey,\n\t\t\tstreamingErr: (&valueReader{stack: []vrState{{vType: TypeEmbeddedDocument}}, frame: 0}).typeError(TypeMaxKey),\n\t\t\tbufferedErr:  (&valueReader{stack: []vrState{{vType: TypeEmbeddedDocument}}, frame: 0}).typeError(TypeMaxKey),\n\t\t\tvType:        TypeEmbeddedDocument,\n\t\t},\n\t\t{\n\t\t\tname:         \"ReadMaxKey/success\",\n\t\t\tfn:           (*valueReader).ReadMaxKey,\n\t\t\tstreamingErr: nil,\n\t\t\tbufferedErr:  nil,\n\t\t\tvType:        TypeMaxKey,\n\t\t},\n\t\t{\n\t\t\tname:         \"ReadMinKey/incorrect type\",\n\t\t\tfn:           (*valueReader).ReadMinKey,\n\t\t\tstreamingErr: (&valueReader{stack: []vrState{{vType: TypeEmbeddedDocument}}, frame: 0}).typeError(TypeMinKey),\n\t\t\tbufferedErr:  (&valueReader{stack: []vrState{{vType: TypeEmbeddedDocument}}, frame: 0}).typeError(TypeMinKey),\n\t\t\tvType:        TypeEmbeddedDocument,\n\t\t},\n\t\t{\n\t\t\tname:         \"ReadMinKey/success\",\n\t\t\tfn:           (*valueReader).ReadMinKey,\n\t\t\tstreamingErr: nil,\n\t\t\tbufferedErr:  nil,\n\t\t\tvType:        TypeMinKey,\n\t\t},\n\t\t{\n\t\t\tname:         \"ReadNull/incorrect type\",\n\t\t\tfn:           (*valueReader).ReadNull,\n\t\t\tstreamingErr: (&valueReader{stack: []vrState{{vType: TypeEmbeddedDocument}}, frame: 0}).typeError(TypeNull),\n\t\t\tbufferedErr:  (&valueReader{stack: []vrState{{vType: TypeEmbeddedDocument}}, frame: 0}).typeError(TypeNull),\n\t\t\tvType:        TypeEmbeddedDocument,\n\t\t},\n\t\t{\n\t\t\tname:         \"ReadNull/success\",\n\t\t\tfn:           (*valueReader).ReadNull,\n\t\t\tstreamingErr: nil,\n\t\t\tbufferedErr:  nil,\n\t\t\tvType:        TypeNull,\n\t\t},\n\t\t{\n\t\t\tname:         \"ReadUndefined/incorrect type\",\n\t\t\tfn:           (*valueReader).ReadUndefined,\n\t\t\tstreamingErr: (&valueReader{stack: []vrState{{vType: TypeEmbeddedDocument}}, frame: 0}).typeError(TypeUndefined),\n\t\t\tbufferedErr:  (&valueReader{stack: []vrState{{vType: TypeEmbeddedDocument}}, frame: 0}).typeError(TypeUndefined),\n\t\t\tvType:        TypeEmbeddedDocument,\n\t\t},\n\t\t{\n\t\t\tname:         \"ReadUndefined/success\",\n\t\t\tfn:           (*valueReader).ReadUndefined,\n\t\t\tstreamingErr: nil,\n\t\t\tbufferedErr:  nil,\n\t\t\tvType:        TypeUndefined,\n\t\t},\n\t}\n\n\tfor _, tc := range testCases {\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\tt.Run(\"buffered\", func(t *testing.T) {\n\t\t\t\tvr := &valueReader{\n\t\t\t\t\tsrc: &bufferedByteSrc{buf: []byte{}},\n\t\t\t\t\tstack: []vrState{\n\t\t\t\t\t\t{mode: mTopLevel},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tmode:  mElement,\n\t\t\t\t\t\t\tvType: tc.vType,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\tframe: 1,\n\t\t\t\t}\n\t\t\t\twantErr := tc.bufferedErr\n\n\t\t\t\terr := tc.fn(vr)\n\t\t\t\tif !errequal(t, err, wantErr) {\n\t\t\t\t\tt.Errorf(\"Returned errors do not match. got %v; want %v\", err, wantErr)\n\t\t\t\t}\n\t\t\t})\n\n\t\t\tt.Run(\"streaming\", func(t *testing.T) {\n\t\t\t\tvr := &valueReader{\n\t\t\t\t\tsrc: &streamingByteSrc{br: bufio.NewReader(bytes.NewReader([]byte{}))},\n\t\t\t\t\tstack: []vrState{\n\t\t\t\t\t\t{mode: mTopLevel},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tmode:  mElement,\n\t\t\t\t\t\t\tvType: tc.vType,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\tframe: 1,\n\t\t\t\t}\n\t\t\t\twantErr := tc.streamingErr\n\n\t\t\t\terr := tc.fn(vr)\n\t\t\t\tif !errequal(t, err, wantErr) {\n\t\t\t\t\tt.Errorf(\"Returned errors do not match. got %v; want %v\", err, wantErr)\n\t\t\t\t}\n\t\t\t})\n\t\t})\n\t}\n}\n\nfunc TestValueReader_ReadObjectID(t *testing.T) {\n\ttestCases := []struct {\n\t\tname  string\n\t\tdata  []byte\n\t\toid   ObjectID\n\t\terr   error\n\t\tvType Type\n\t}{\n\t\t{\n\t\t\tname:  \"incorrect type\",\n\t\t\tdata:  []byte{},\n\t\t\toid:   ObjectID{},\n\t\t\terr:   (&valueReader{stack: []vrState{{vType: TypeEmbeddedDocument}}, frame: 0}).typeError(TypeObjectID),\n\t\t\tvType: TypeEmbeddedDocument,\n\t\t},\n\t\t{\n\t\t\tname:  \"not enough bytes for objectID\",\n\t\t\tdata:  []byte{},\n\t\t\toid:   ObjectID{},\n\t\t\terr:   io.EOF,\n\t\t\tvType: TypeObjectID,\n\t\t},\n\t\t{\n\t\t\tname:  \"success\",\n\t\t\tdata:  []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C},\n\t\t\toid:   ObjectID{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C},\n\t\t\terr:   nil,\n\t\t\tvType: TypeObjectID,\n\t\t},\n\t}\n\n\tfor _, tc := range testCases {\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\tt.Run(\"buffered\", func(t *testing.T) {\n\t\t\t\tvr := &valueReader{\n\t\t\t\t\tsrc: &bufferedByteSrc{buf: tc.data},\n\t\t\t\t\tstack: []vrState{\n\t\t\t\t\t\t{mode: mTopLevel},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tmode:  mElement,\n\t\t\t\t\t\t\tvType: tc.vType,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\tframe: 1,\n\t\t\t\t}\n\n\t\t\t\toid, err := vr.ReadObjectID()\n\t\t\t\tif !errequal(t, err, tc.err) {\n\t\t\t\t\tt.Errorf(\"Returned errors do not match. got %v; want %v\", err, tc.err)\n\t\t\t\t}\n\t\t\t\tif oid != tc.oid {\n\t\t\t\t\tt.Errorf(\"ObjectIDs did not match. got %v; want %v\", oid, tc.oid)\n\t\t\t\t}\n\t\t\t})\n\n\t\t\tt.Run(\"streaming\", func(t *testing.T) {\n\t\t\t\tvr := &valueReader{\n\t\t\t\t\tsrc: &streamingByteSrc{br: bufio.NewReader(bytes.NewReader(tc.data))},\n\t\t\t\t\tstack: []vrState{\n\t\t\t\t\t\t{mode: mTopLevel},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tmode:  mElement,\n\t\t\t\t\t\t\tvType: tc.vType,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\tframe: 1,\n\t\t\t\t}\n\n\t\t\t\toid, err := vr.ReadObjectID()\n\t\t\t\tif !errequal(t, err, tc.err) {\n\t\t\t\t\tt.Errorf(\"Returned errors do not match. got %v; want %v\", err, tc.err)\n\t\t\t\t}\n\t\t\t\tif oid != tc.oid {\n\t\t\t\t\tt.Errorf(\"ObjectIDs did not match. got %v; want %v\", oid, tc.oid)\n\t\t\t\t}\n\t\t\t})\n\t\t})\n\t}\n}\n\nfunc TestValueReader_ReadRegex(t *testing.T) {\n\ttestCases := []struct {\n\t\tname    string\n\t\tdata    []byte\n\t\tpattern string\n\t\toptions string\n\t\terr     error\n\t\tvType   Type\n\t}{\n\t\t{\n\t\t\tname:    \"incorrect type\",\n\t\t\tdata:    []byte{},\n\t\t\tpattern: \"\",\n\t\t\toptions: \"\",\n\t\t\terr:     (&valueReader{stack: []vrState{{vType: TypeEmbeddedDocument}}, frame: 0}).typeError(TypeRegex),\n\t\t\tvType:   TypeEmbeddedDocument,\n\t\t},\n\t\t{\n\t\t\tname:    \"length too short\",\n\t\t\tdata:    []byte{},\n\t\t\tpattern: \"\",\n\t\t\toptions: \"\",\n\t\t\terr:     io.EOF,\n\t\t\tvType:   TypeRegex,\n\t\t},\n\t\t{\n\t\t\tname:    \"not enough bytes for options\",\n\t\t\tdata:    []byte{'f', 'o', 'o', 0x00},\n\t\t\tpattern: \"\",\n\t\t\toptions: \"\",\n\t\t\terr:     io.EOF,\n\t\t\tvType:   TypeRegex,\n\t\t},\n\t\t{\n\t\t\tname:    \"success\",\n\t\t\tdata:    []byte{'f', 'o', 'o', 0x00, 'b', 'a', 'r', 0x00},\n\t\t\tpattern: \"foo\",\n\t\t\toptions: \"bar\",\n\t\t\terr:     nil,\n\t\t\tvType:   TypeRegex,\n\t\t},\n\t}\n\n\tfor _, tc := range testCases {\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\tt.Run(\"buffered\", func(t *testing.T) {\n\t\t\t\tvr := &valueReader{\n\t\t\t\t\tsrc: &bufferedByteSrc{buf: tc.data},\n\t\t\t\t\tstack: []vrState{\n\t\t\t\t\t\t{mode: mTopLevel},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tmode:  mElement,\n\t\t\t\t\t\t\tvType: tc.vType,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\tframe: 1,\n\t\t\t\t}\n\n\t\t\t\tpattern, options, err := vr.ReadRegex()\n\t\t\t\tif !errequal(t, err, tc.err) {\n\t\t\t\t\tt.Errorf(\"Returned errors do not match. got %v; want %v\", err, tc.err)\n\t\t\t\t}\n\t\t\t\tif pattern != tc.pattern {\n\t\t\t\t\tt.Errorf(\"Incorrect pattern returned. got %s; want %s\", pattern, tc.pattern)\n\t\t\t\t}\n\t\t\t\tif options != tc.options {\n\t\t\t\t\tt.Errorf(\"Incorrect options returned. got %s; want %s\", options, tc.options)\n\t\t\t\t}\n\t\t\t})\n\n\t\t\tt.Run(\"streaming\", func(t *testing.T) {\n\t\t\t\tvr := &valueReader{\n\t\t\t\t\tsrc: &streamingByteSrc{br: bufio.NewReader(bytes.NewReader(tc.data))},\n\t\t\t\t\tstack: []vrState{\n\t\t\t\t\t\t{mode: mTopLevel},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tmode:  mElement,\n\t\t\t\t\t\t\tvType: tc.vType,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\tframe: 1,\n\t\t\t\t}\n\n\t\t\t\tpattern, options, err := vr.ReadRegex()\n\t\t\t\tif !errequal(t, err, tc.err) {\n\t\t\t\t\tt.Errorf(\"Returned errors do not match. got %v; want %v\", err, tc.err)\n\t\t\t\t}\n\t\t\t\tif pattern != tc.pattern {\n\t\t\t\t\tt.Errorf(\"Incorrect pattern returned. got %s; want %s\", pattern, tc.pattern)\n\t\t\t\t}\n\t\t\t\tif options != tc.options {\n\t\t\t\t\tt.Errorf(\"Incorrect options returned. got %s; want %s\", options, tc.options)\n\t\t\t\t}\n\t\t\t})\n\t\t})\n\t}\n}\n\nfunc TestValueReader_ReadTimestamp(t *testing.T) {\n\ttestCases := []struct {\n\t\tname  string\n\t\tdata  []byte\n\t\tts    uint32\n\t\tincr  uint32\n\t\terr   error\n\t\tvType Type\n\t}{\n\t\t{\n\t\t\tname:  \"incorrect type\",\n\t\t\tdata:  []byte{},\n\t\t\tts:    0,\n\t\t\tincr:  0,\n\t\t\terr:   (&valueReader{stack: []vrState{{vType: TypeEmbeddedDocument}}, frame: 0}).typeError(TypeTimestamp),\n\t\t\tvType: TypeEmbeddedDocument,\n\t\t},\n\t\t{\n\t\t\tname:  \"not enough bytes for increment\",\n\t\t\tdata:  []byte{},\n\t\t\tts:    0,\n\t\t\tincr:  0,\n\t\t\terr:   io.EOF,\n\t\t\tvType: TypeTimestamp,\n\t\t},\n\t\t{\n\t\t\tname:  \"not enough bytes for timestamp\",\n\t\t\tdata:  []byte{0x01, 0x02, 0x03, 0x04},\n\t\t\tts:    0,\n\t\t\tincr:  0,\n\t\t\terr:   io.EOF,\n\t\t\tvType: TypeTimestamp,\n\t\t},\n\t\t{\n\t\t\tname:  \"success\",\n\t\t\tdata:  []byte{0xFF, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00},\n\t\t\tts:    256,\n\t\t\tincr:  255,\n\t\t\terr:   nil,\n\t\t\tvType: TypeTimestamp,\n\t\t},\n\t}\n\n\tfor _, tc := range testCases {\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\tt.Run(\"buffered\", func(t *testing.T) {\n\t\t\t\tvr := &valueReader{\n\t\t\t\t\tsrc: &bufferedByteSrc{buf: tc.data},\n\t\t\t\t\tstack: []vrState{\n\t\t\t\t\t\t{mode: mTopLevel},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tmode:  mElement,\n\t\t\t\t\t\t\tvType: tc.vType,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\tframe: 1,\n\t\t\t\t}\n\n\t\t\t\tts, incr, err := vr.ReadTimestamp()\n\t\t\t\tif !errequal(t, err, tc.err) {\n\t\t\t\t\tt.Errorf(\"Returned errors do not match. got %v; want %v\", err, tc.err)\n\t\t\t\t}\n\t\t\t\tif ts != tc.ts {\n\t\t\t\t\tt.Errorf(\"Incorrect timestamp returned. got %d; want %d\", ts, tc.ts)\n\t\t\t\t}\n\t\t\t\tif incr != tc.incr {\n\t\t\t\t\tt.Errorf(\"Incorrect increment returned. got %d; want %d\", incr, tc.incr)\n\t\t\t\t}\n\t\t\t})\n\n\t\t\tt.Run(\"streaming\", func(t *testing.T) {\n\t\t\t\tvr := &valueReader{\n\t\t\t\t\tsrc: &streamingByteSrc{br: bufio.NewReader(bytes.NewReader(tc.data))},\n\t\t\t\t\tstack: []vrState{\n\t\t\t\t\t\t{mode: mTopLevel},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tmode:  mElement,\n\t\t\t\t\t\t\tvType: tc.vType,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\tframe: 1,\n\t\t\t\t}\n\n\t\t\t\tts, incr, err := vr.ReadTimestamp()\n\t\t\t\tif !errequal(t, err, tc.err) {\n\t\t\t\t\tt.Errorf(\"Returned errors do not match. got %v; want %v\", err, tc.err)\n\t\t\t\t}\n\t\t\t\tif ts != tc.ts {\n\t\t\t\t\tt.Errorf(\"Incorrect timestamp returned. got %d; want %d\", ts, tc.ts)\n\t\t\t\t}\n\t\t\t\tif incr != tc.incr {\n\t\t\t\t\tt.Errorf(\"Incorrect increment returned. got %d; want %d\", incr, tc.incr)\n\t\t\t\t}\n\t\t\t})\n\t\t})\n\t}\n}\n\nfunc TestValueReader_ReadBytes_Skip_Buffered(t *testing.T) {\n\tindex, docb := bsoncore.ReserveLength(nil)\n\tdocb = bsoncore.AppendNullElement(docb, \"foobar\")\n\tdocb = append(docb, 0x00)\n\tdocb = bsoncore.UpdateLength(docb, index, int32(len(docb)))\n\tcwsbytes := bsoncore.AppendCodeWithScope(nil, \"var hellow = world;\", docb)\n\tstrbytes := []byte{0x04, 0x00, 0x00, 0x00, 'f', 'o', 'o', 0x00}\n\n\ttestCases := []struct {\n\t\tname           string\n\t\tt              Type\n\t\tdata           []byte\n\t\terr            error\n\t\toffset         int64\n\t\tstartingOffset int64\n\t}{\n\t\t{\n\t\t\tname: \"Array/invalid length\",\n\t\t\tt:    TypeArray,\n\t\t\tdata: []byte{0x01, 0x02, 0x03},\n\t\t\terr:  io.EOF, offset: 0, startingOffset: 0,\n\t\t},\n\t\t{\n\t\t\tname: \"Array/not enough bytes\",\n\t\t\tt:    TypeArray,\n\t\t\tdata: []byte{0x0F, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03},\n\t\t\terr:  io.EOF, offset: 0, startingOffset: 0,\n\t\t},\n\t\t{\n\t\t\tname: \"Array/success\",\n\t\t\tt:    TypeArray,\n\t\t\tdata: []byte{0x08, 0x00, 0x00, 0x00, 0x0A, '1', 0x00, 0x00},\n\t\t\terr:  nil, offset: 8, startingOffset: 0,\n\t\t},\n\t\t{\n\t\t\tname: \"EmbeddedDocument/invalid length\",\n\t\t\tt:    TypeEmbeddedDocument,\n\t\t\tdata: []byte{0x01, 0x02, 0x03},\n\t\t\terr:  io.EOF, offset: 0, startingOffset: 0,\n\t\t},\n\t\t{\n\t\t\tname: \"EmbeddedDocument/not enough bytes\",\n\t\t\tt:    TypeEmbeddedDocument,\n\t\t\tdata: []byte{0x0F, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03},\n\t\t\terr:  io.EOF, offset: 0, startingOffset: 0,\n\t\t},\n\t\t{\n\t\t\tname: \"EmbeddedDocument/success\",\n\t\t\tt:    TypeEmbeddedDocument,\n\t\t\tdata: []byte{0x08, 0x00, 0x00, 0x00, 0x0A, 'A', 0x00, 0x00},\n\t\t\terr:  nil, offset: 8, startingOffset: 0,\n\t\t},\n\t\t{\n\t\t\tname: \"CodeWithScope/invalid length\",\n\t\t\tt:    TypeCodeWithScope,\n\t\t\tdata: []byte{0x01, 0x02, 0x03},\n\t\t\terr:  io.EOF, offset: 0, startingOffset: 0,\n\t\t},\n\t\t{\n\t\t\tname: \"CodeWithScope/not enough bytes\",\n\t\t\tt:    TypeCodeWithScope,\n\t\t\tdata: []byte{0x0F, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03},\n\t\t\terr:  io.EOF, offset: 0, startingOffset: 0,\n\t\t},\n\t\t{\n\t\t\tname: \"CodeWithScope/success\",\n\t\t\tt:    TypeCodeWithScope,\n\t\t\tdata: cwsbytes,\n\t\t\terr:  nil, offset: 41, startingOffset: 0,\n\t\t},\n\t\t{\n\t\t\tname: \"Binary/invalid length\",\n\t\t\tt:    TypeBinary,\n\t\t\tdata: []byte{0x01, 0x02, 0x03},\n\t\t\terr:  io.EOF, offset: 0, startingOffset: 0,\n\t\t},\n\t\t{\n\t\t\tname: \"Binary/not enough bytes\",\n\t\t\tt:    TypeBinary,\n\t\t\tdata: []byte{0x0F, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03},\n\t\t\terr:  io.EOF, offset: 0, startingOffset: 0,\n\t\t},\n\t\t{\n\t\t\tname: \"Binary/success\",\n\t\t\tt:    TypeBinary,\n\t\t\tdata: []byte{0x03, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03},\n\t\t\terr:  nil, offset: 8, startingOffset: 0,\n\t\t},\n\t\t{\n\t\t\tname: \"Boolean/invalid length\",\n\t\t\tt:    TypeBoolean,\n\t\t\tdata: []byte{},\n\t\t\terr:  io.EOF, offset: 0, startingOffset: 0,\n\t\t},\n\t\t{\n\t\t\tname: \"Boolean/success\",\n\t\t\tt:    TypeBoolean,\n\t\t\tdata: []byte{0x01},\n\t\t\terr:  nil, offset: 1, startingOffset: 0,\n\t\t},\n\t\t{\n\t\t\tname: \"DBPointer/invalid length\",\n\t\t\tt:    TypeDBPointer,\n\t\t\tdata: []byte{0x01, 0x02, 0x03},\n\t\t\terr:  io.EOF, offset: 0, startingOffset: 0,\n\t\t},\n\t\t{\n\t\t\tname: \"DBPointer/not enough bytes\",\n\t\t\tt:    TypeDBPointer,\n\t\t\tdata: []byte{0x0F, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03},\n\t\t\terr:  io.EOF, offset: 0, startingOffset: 0,\n\t\t},\n\t\t{\n\t\t\tname: \"DBPointer/success\",\n\t\t\tt:    TypeDBPointer,\n\t\t\tdata: []byte{0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C},\n\t\t\terr:  nil, offset: 17, startingOffset: 0,\n\t\t},\n\t\t{\n\t\t\tname: \"DBPointer/not enough bytes\",\n\t\t\tt:    TypeDateTime,\n\t\t\tdata: []byte{0x01, 0x02, 0x03, 0x04},\n\t\t\terr:  io.EOF, offset: 0, startingOffset: 0,\n\t\t},\n\t\t{\n\t\t\tname: \"DBPointer/success\",\n\t\t\tt:    TypeDateTime,\n\t\t\tdata: []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08},\n\t\t\terr:  nil, offset: 8, startingOffset: 0,\n\t\t},\n\t\t{\n\t\t\tname: \"Double/not enough bytes\",\n\t\t\tt:    TypeDouble,\n\t\t\tdata: []byte{0x01, 0x02, 0x03, 0x04},\n\t\t\terr:  io.EOF, offset: 0, startingOffset: 0,\n\t\t},\n\t\t{\n\t\t\tname: \"Double/success\",\n\t\t\tt:    TypeDouble,\n\t\t\tdata: []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08},\n\t\t\terr:  nil, offset: 8, startingOffset: 0,\n\t\t},\n\t\t{\n\t\t\tname: \"Int64/not enough bytes\",\n\t\t\tt:    TypeInt64,\n\t\t\tdata: []byte{0x01, 0x02, 0x03, 0x04},\n\t\t\terr:  io.EOF, offset: 0, startingOffset: 0,\n\t\t},\n\t\t{\n\t\t\tname: \"Int64/success\",\n\t\t\tt:    TypeInt64,\n\t\t\tdata: []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08},\n\t\t\terr:  nil, offset: 8, startingOffset: 0,\n\t\t},\n\t\t{\n\t\t\tname: \"Timestamp/not enough bytes\",\n\t\t\tt:    TypeTimestamp,\n\t\t\tdata: []byte{0x01, 0x02, 0x03, 0x04},\n\t\t\terr:  io.EOF, offset: 0, startingOffset: 0,\n\t\t},\n\t\t{\n\t\t\tname: \"Timestamp/success\",\n\t\t\tt:    TypeTimestamp,\n\t\t\tdata: []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08},\n\t\t\terr:  nil, offset: 8, startingOffset: 0,\n\t\t},\n\t\t{\n\t\t\tname: \"Decimal128/not enough bytes\",\n\t\t\tt:    TypeDecimal128,\n\t\t\tdata: []byte{0x01, 0x02, 0x03, 0x04},\n\t\t\terr:  io.EOF, offset: 0, startingOffset: 0,\n\t\t},\n\t\t{\n\t\t\tname: \"Decimal128/success\",\n\t\t\tt:    TypeDecimal128,\n\t\t\tdata: []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10},\n\t\t\terr:  nil, offset: 16, startingOffset: 0,\n\t\t},\n\t\t{\n\t\t\tname: \"Int32/not enough bytes\",\n\t\t\tt:    TypeInt32,\n\t\t\tdata: []byte{0x01, 0x02},\n\t\t\terr:  io.EOF, offset: 0, startingOffset: 0,\n\t\t},\n\t\t{\n\t\t\tname: \"Int32/success\",\n\t\t\tt:    TypeInt32,\n\t\t\tdata: []byte{0x01, 0x02, 0x03, 0x04},\n\t\t\terr:  nil, offset: 4, startingOffset: 0,\n\t\t},\n\t\t{\n\t\t\tname: \"Javascript/invalid length\",\n\t\t\tt:    TypeJavaScript,\n\t\t\tdata: strbytes[:2],\n\t\t\terr:  io.EOF, offset: 0, startingOffset: 0,\n\t\t},\n\t\t{\n\t\t\tname: \"Javascript/not enough bytes\",\n\t\t\tt:    TypeJavaScript,\n\t\t\tdata: strbytes[:5],\n\t\t\terr:  io.EOF, offset: 0, startingOffset: 0,\n\t\t},\n\t\t{\n\t\t\tname: \"Javascript/success\",\n\t\t\tt:    TypeJavaScript,\n\t\t\tdata: strbytes,\n\t\t\terr:  nil, offset: 8, startingOffset: 0,\n\t\t},\n\t\t{\n\t\t\tname: \"String/invalid length\",\n\t\t\tt:    TypeString,\n\t\t\tdata: strbytes[:2],\n\t\t\terr:  io.EOF, offset: 0, startingOffset: 0,\n\t\t},\n\t\t{\n\t\t\tname: \"String/not enough bytes\",\n\t\t\tt:    TypeString,\n\t\t\tdata: strbytes[:5],\n\t\t\terr:  io.EOF, offset: 0, startingOffset: 0,\n\t\t},\n\t\t{\n\t\t\tname: \"String/success\",\n\t\t\tt:    TypeString,\n\t\t\tdata: strbytes,\n\t\t\terr:  nil, offset: 8, startingOffset: 0,\n\t\t},\n\t\t{\n\t\t\tname: \"Symbol/invalid length\",\n\t\t\tt:    TypeSymbol,\n\t\t\tdata: strbytes[:2],\n\t\t\terr:  io.EOF, offset: 0, startingOffset: 0,\n\t\t},\n\t\t{\n\t\t\tname: \"Symbol/not enough bytes\",\n\t\t\tt:    TypeSymbol,\n\t\t\tdata: strbytes[:5],\n\t\t\terr:  io.EOF, offset: 0, startingOffset: 0,\n\t\t},\n\t\t{\n\t\t\tname: \"Symbol/success\",\n\t\t\tt:    TypeSymbol,\n\t\t\tdata: strbytes,\n\t\t\terr:  nil, offset: 8, startingOffset: 0,\n\t\t},\n\t\t{\n\t\t\tname: \"MaxKey/success\",\n\t\t\tt:    TypeMaxKey,\n\t\t\tdata: []byte{},\n\t\t\terr:  nil, offset: 0, startingOffset: 0,\n\t\t},\n\t\t{\n\t\t\tname: \"MinKey/success\",\n\t\t\tt:    TypeMinKey,\n\t\t\tdata: []byte{},\n\t\t\terr:  nil, offset: 0, startingOffset: 0,\n\t\t},\n\t\t{\n\t\t\tname: \"Null/success\",\n\t\t\tt:    TypeNull,\n\t\t\tdata: []byte{},\n\t\t\terr:  nil, offset: 0, startingOffset: 0,\n\t\t},\n\t\t{\n\t\t\tname: \"Undefined/success\",\n\t\t\tt:    TypeUndefined,\n\t\t\tdata: []byte{},\n\t\t\terr:  nil, offset: 0, startingOffset: 0,\n\t\t},\n\t\t{\n\t\t\tname: \"ObjectID/not enough bytes\",\n\t\t\tt:    TypeObjectID,\n\t\t\tdata: []byte{0x01, 0x02, 0x03, 0x04},\n\t\t\terr:  io.EOF, offset: 0, startingOffset: 0,\n\t\t},\n\t\t{\n\t\t\tname: \"ObjectID/success\",\n\t\t\tt:    TypeObjectID,\n\t\t\tdata: []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C},\n\t\t\terr:  nil, offset: 12, startingOffset: 0,\n\t\t},\n\t\t{\n\t\t\tname: \"Regex/not enough bytes (first string)\",\n\t\t\tt:    TypeRegex,\n\t\t\tdata: []byte{'f', 'o', 'o'},\n\t\t\terr:  io.EOF, offset: 0, startingOffset: 0,\n\t\t},\n\t\t{\n\t\t\tname: \"Regex/not enough bytes (second string)\",\n\t\t\tt:    TypeRegex,\n\t\t\tdata: []byte{'f', 'o', 'o', 0x00, 'b', 'a', 'r'},\n\t\t\terr:  io.EOF, offset: 0, startingOffset: 0,\n\t\t},\n\t\t{\n\t\t\tname: \"Regex/success\",\n\t\t\tt:    TypeRegex,\n\t\t\tdata: []byte{0x00, 0x00, 0x00, 'f', 'o', 'o', 0x00, 'i', 0x00},\n\t\t\terr:  nil, offset: 9, startingOffset: 3,\n\t\t},\n\t\t{\n\t\t\tname:   \"Unknown Type\",\n\t\t\tt:      Type(0),\n\t\t\tdata:   nil,\n\t\t\terr:    fmt.Errorf(\"attempted to read bytes of unknown BSON type %v\", Type(0)),\n\t\t\toffset: 0, startingOffset: 0,\n\t\t},\n\t}\n\n\tfor _, tc := range testCases {\n\t\ttc := tc\n\t\tconst startingEnd = 64\n\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\tt.Run(\"Skip\", func(t *testing.T) {\n\t\t\t\tvr := &valueReader{\n\t\t\t\t\tsrc: &bufferedByteSrc{buf: tc.data, offset: tc.startingOffset},\n\t\t\t\t\tstack: []vrState{\n\t\t\t\t\t\t{mode: mTopLevel, end: startingEnd},\n\t\t\t\t\t\t{mode: mElement, vType: tc.t},\n\t\t\t\t\t},\n\t\t\t\t\tframe: 1,\n\t\t\t\t}\n\n\t\t\t\terr := vr.Skip()\n\t\t\t\tif !errequal(t, err, tc.err) {\n\t\t\t\t\tt.Errorf(\"Did not receive expected error; got %v; want %v\", err, tc.err)\n\t\t\t\t}\n\t\t\t\tif tc.err == nil && vr.src.pos() != tc.offset {\n\t\t\t\t\tt.Errorf(\"Offset not set at correct position; got %d; want %d\", vr.src.pos(), tc.offset)\n\t\t\t\t}\n\t\t\t})\n\t\t\tt.Run(\"ReadBytes\", func(t *testing.T) {\n\t\t\t\tvr := &valueReader{\n\t\t\t\t\tsrc: &bufferedByteSrc{buf: tc.data, offset: tc.startingOffset},\n\t\t\t\t\tstack: []vrState{\n\t\t\t\t\t\t{mode: mTopLevel, end: startingEnd},\n\t\t\t\t\t\t{mode: mElement, vType: tc.t},\n\t\t\t\t\t},\n\t\t\t\t\tframe: 1,\n\t\t\t\t}\n\n\t\t\t\t_, got, err := vr.readValueBytes(nil)\n\t\t\t\tif !errequal(t, err, tc.err) {\n\t\t\t\t\tt.Errorf(\"Did not receive expected error; got %v; want %v\", err, tc.err)\n\t\t\t\t}\n\t\t\t\tif tc.err == nil && vr.src.pos() != tc.offset {\n\t\t\t\t\tt.Errorf(\"Offset not set at correct position; got %d; want %d\", vr.src.pos(), tc.offset)\n\t\t\t\t}\n\t\t\t\tif tc.err == nil && !bytes.Equal(got, tc.data[tc.startingOffset:]) {\n\t\t\t\t\tt.Errorf(\"Did not receive expected bytes. got %v; want %v\", got, tc.data[tc.startingOffset:])\n\t\t\t\t}\n\t\t\t})\n\t\t})\n\t}\n\n\tt.Run(\"ReadValueBytes/Top Level Doc\", func(t *testing.T) {\n\t\ttestCases := []struct {\n\t\t\tname     string\n\t\t\twant     []byte\n\t\t\twantType Type\n\t\t\twantErr  error\n\t\t}{\n\t\t\t{\n\t\t\t\t\"success\",\n\t\t\t\tbsoncore.BuildDocument(nil, bsoncore.AppendDoubleElement(nil, \"pi\", 3.14159)),\n\t\t\t\tType(0),\n\t\t\t\tnil,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"wrong length\",\n\t\t\t\t[]byte{0x01, 0x02, 0x03},\n\t\t\t\tType(0),\n\t\t\t\tio.EOF,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"append bytes\",\n\t\t\t\t[]byte{0x01, 0x02, 0x03, 0x04},\n\t\t\t\tType(0),\n\t\t\t\tio.EOF,\n\t\t\t},\n\t\t}\n\n\t\tfor _, tc := range testCases {\n\t\t\ttc := tc\n\t\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\t\tt.Parallel()\n\t\t\t\tvr := &valueReader{\n\t\t\t\t\tsrc: &bufferedByteSrc{buf: tc.want},\n\t\t\t\t\tstack: []vrState{\n\t\t\t\t\t\t{mode: mTopLevel},\n\t\t\t\t\t},\n\t\t\t\t\tframe: 0,\n\t\t\t\t}\n\t\t\t\tgotType, got, gotErr := vr.readValueBytes(nil)\n\t\t\t\tif !errors.Is(gotErr, tc.wantErr) {\n\t\t\t\t\tt.Errorf(\"Did not receive expected error. got %v; want %v\", gotErr, tc.wantErr)\n\t\t\t\t}\n\t\t\t\tif tc.wantErr == nil && gotType != tc.wantType {\n\t\t\t\t\tt.Errorf(\"Did not receive expected type. got %v; want %v\", gotType, tc.wantType)\n\t\t\t\t}\n\t\t\t\tif tc.wantErr == nil && !bytes.Equal(got, tc.want) {\n\t\t\t\t\tt.Errorf(\"Did not receive expected bytes. got %v; want %v\", got, tc.want)\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t})\n}\n\nfunc TestValueReader_ReadBytes_Skip_Streaming(t *testing.T) {\n\tindex, docb := bsoncore.ReserveLength(nil)\n\tdocb = bsoncore.AppendNullElement(docb, \"foobar\")\n\tdocb = append(docb, 0x00)\n\tdocb = bsoncore.UpdateLength(docb, index, int32(len(docb)))\n\tcwsbytes := bsoncore.AppendCodeWithScope(nil, \"var hellow = world;\", docb)\n\tstrbytes := []byte{0x04, 0x00, 0x00, 0x00, 'f', 'o', 'o', 0x00}\n\n\ttestCases := []struct {\n\t\tname           string\n\t\tt              Type\n\t\tdata           []byte\n\t\terr            error\n\t\toffset         int64\n\t\tstartingOffset int64\n\t}{\n\t\t{\n\t\t\tname: \"Array/invalid length\",\n\t\t\tt:    TypeArray,\n\t\t\tdata: []byte{0x01, 0x02, 0x03},\n\t\t\terr:  io.EOF, offset: 0, startingOffset: 0,\n\t\t},\n\t\t{\n\t\t\tname: \"Array/not enough bytes\",\n\t\t\tt:    TypeArray,\n\t\t\tdata: []byte{0x0F, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03},\n\t\t\terr:  io.EOF, offset: 0, startingOffset: 0,\n\t\t},\n\t\t{\n\t\t\tname: \"Array/success\",\n\t\t\tt:    TypeArray,\n\t\t\tdata: []byte{0x08, 0x00, 0x00, 0x00, 0x0A, '1', 0x00, 0x00},\n\t\t\terr:  nil, offset: 8, startingOffset: 0,\n\t\t},\n\t\t{\n\t\t\tname: \"EmbeddedDocument/invalid length\",\n\t\t\tt:    TypeEmbeddedDocument,\n\t\t\tdata: []byte{0x01, 0x02, 0x03},\n\t\t\terr:  io.EOF, offset: 0, startingOffset: 0,\n\t\t},\n\t\t{\n\t\t\tname: \"EmbeddedDocument/not enough bytes\",\n\t\t\tt:    TypeEmbeddedDocument,\n\t\t\tdata: []byte{0x0F, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03},\n\t\t\terr:  io.EOF, offset: 0, startingOffset: 0,\n\t\t},\n\t\t{\n\t\t\tname: \"EmbeddedDocument/success\",\n\t\t\tt:    TypeEmbeddedDocument,\n\t\t\tdata: []byte{0x08, 0x00, 0x00, 0x00, 0x0A, 'A', 0x00, 0x00},\n\t\t\terr:  nil, offset: 8, startingOffset: 0,\n\t\t},\n\t\t{\n\t\t\tname: \"CodeWithScope/invalid length\",\n\t\t\tt:    TypeCodeWithScope,\n\t\t\tdata: []byte{0x01, 0x02, 0x03},\n\t\t\terr:  io.EOF, offset: 0, startingOffset: 0,\n\t\t},\n\t\t{\n\t\t\tname: \"CodeWithScope/not enough bytes\",\n\t\t\tt:    TypeCodeWithScope,\n\t\t\tdata: []byte{0x0F, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03},\n\t\t\terr:  io.EOF, offset: 0, startingOffset: 0,\n\t\t},\n\t\t{\n\t\t\tname: \"CodeWithScope/success\",\n\t\t\tt:    TypeCodeWithScope,\n\t\t\tdata: cwsbytes,\n\t\t\terr:  nil, offset: 41, startingOffset: 0,\n\t\t},\n\t\t{\n\t\t\tname: \"Binary/invalid length\",\n\t\t\tt:    TypeBinary,\n\t\t\tdata: []byte{0x01, 0x02, 0x03},\n\t\t\terr:  io.EOF, offset: 0, startingOffset: 0,\n\t\t},\n\t\t{\n\t\t\tname: \"Binary/not enough bytes\",\n\t\t\tt:    TypeBinary,\n\t\t\tdata: []byte{0x0F, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03},\n\t\t\terr:  io.EOF, offset: 0, startingOffset: 0,\n\t\t},\n\t\t{\n\t\t\tname: \"Binary/success\",\n\t\t\tt:    TypeBinary,\n\t\t\tdata: []byte{0x03, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03},\n\t\t\terr:  nil, offset: 8, startingOffset: 0,\n\t\t},\n\t\t{\n\t\t\tname: \"Boolean/invalid length\",\n\t\t\tt:    TypeBoolean,\n\t\t\tdata: []byte{},\n\t\t\terr:  io.EOF, offset: 0, startingOffset: 0,\n\t\t},\n\t\t{\n\t\t\tname: \"Boolean/success\",\n\t\t\tt:    TypeBoolean,\n\t\t\tdata: []byte{0x01},\n\t\t\terr:  nil, offset: 1, startingOffset: 0,\n\t\t},\n\t\t{\n\t\t\tname: \"DBPointer/invalid length\",\n\t\t\tt:    TypeDBPointer,\n\t\t\tdata: []byte{0x01, 0x02, 0x03},\n\t\t\terr:  io.EOF, offset: 0, startingOffset: 0,\n\t\t},\n\t\t{\n\t\t\tname: \"DBPointer/not enough bytes\",\n\t\t\tt:    TypeDBPointer,\n\t\t\tdata: []byte{0x0F, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03},\n\t\t\terr:  io.EOF, offset: 0, startingOffset: 0,\n\t\t},\n\t\t{\n\t\t\tname: \"DBPointer/success\",\n\t\t\tt:    TypeDBPointer,\n\t\t\tdata: []byte{0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C},\n\t\t\terr:  nil, offset: 17, startingOffset: 0,\n\t\t},\n\t\t{\n\t\t\tname: \"DBPointer/not enough bytes\",\n\t\t\tt:    TypeDateTime,\n\t\t\tdata: []byte{0x01, 0x02, 0x03, 0x04},\n\t\t\terr:  io.EOF, offset: 0, startingOffset: 0,\n\t\t},\n\t\t{\n\t\t\tname: \"DBPointer/success\",\n\t\t\tt:    TypeDateTime,\n\t\t\tdata: []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08},\n\t\t\terr:  nil, offset: 8, startingOffset: 0,\n\t\t},\n\t\t{\n\t\t\tname: \"Double/not enough bytes\",\n\t\t\tt:    TypeDouble,\n\t\t\tdata: []byte{0x01, 0x02, 0x03, 0x04},\n\t\t\terr:  io.EOF, offset: 0, startingOffset: 0,\n\t\t},\n\t\t{\n\t\t\tname: \"Double/success\",\n\t\t\tt:    TypeDouble,\n\t\t\tdata: []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08},\n\t\t\terr:  nil, offset: 8, startingOffset: 0,\n\t\t},\n\t\t{\n\t\t\tname: \"Int64/not enough bytes\",\n\t\t\tt:    TypeInt64,\n\t\t\tdata: []byte{0x01, 0x02, 0x03, 0x04},\n\t\t\terr:  io.EOF, offset: 0, startingOffset: 0,\n\t\t},\n\t\t{\n\t\t\tname: \"Int64/success\",\n\t\t\tt:    TypeInt64,\n\t\t\tdata: []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08},\n\t\t\terr:  nil, offset: 8, startingOffset: 0,\n\t\t},\n\t\t{\n\t\t\tname: \"Timestamp/not enough bytes\",\n\t\t\tt:    TypeTimestamp,\n\t\t\tdata: []byte{0x01, 0x02, 0x03, 0x04},\n\t\t\terr:  io.EOF, offset: 0, startingOffset: 0,\n\t\t},\n\t\t{\n\t\t\tname: \"Timestamp/success\",\n\t\t\tt:    TypeTimestamp,\n\t\t\tdata: []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08},\n\t\t\terr:  nil, offset: 8, startingOffset: 0,\n\t\t},\n\t\t{\n\t\t\tname: \"Decimal128/not enough bytes\",\n\t\t\tt:    TypeDecimal128,\n\t\t\tdata: []byte{0x01, 0x02, 0x03, 0x04},\n\t\t\terr:  io.EOF, offset: 0, startingOffset: 0,\n\t\t},\n\t\t{\n\t\t\tname: \"Decimal128/success\",\n\t\t\tt:    TypeDecimal128,\n\t\t\tdata: []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10},\n\t\t\terr:  nil, offset: 16, startingOffset: 0,\n\t\t},\n\t\t{\n\t\t\tname: \"Int32/not enough bytes\",\n\t\t\tt:    TypeInt32,\n\t\t\tdata: []byte{0x01, 0x02},\n\t\t\terr:  io.EOF, offset: 0, startingOffset: 0,\n\t\t},\n\t\t{\n\t\t\tname: \"Int32/success\",\n\t\t\tt:    TypeInt32,\n\t\t\tdata: []byte{0x01, 0x02, 0x03, 0x04},\n\t\t\terr:  nil, offset: 4, startingOffset: 0,\n\t\t},\n\t\t{\n\t\t\tname: \"Javascript/invalid length\",\n\t\t\tt:    TypeJavaScript,\n\t\t\tdata: strbytes[:2],\n\t\t\terr:  io.EOF, offset: 0, startingOffset: 0,\n\t\t},\n\t\t{\n\t\t\tname: \"Javascript/not enough bytes\",\n\t\t\tt:    TypeJavaScript,\n\t\t\tdata: strbytes[:5],\n\t\t\terr:  io.EOF, offset: 0, startingOffset: 0,\n\t\t},\n\t\t{\n\t\t\tname: \"Javascript/success\",\n\t\t\tt:    TypeJavaScript,\n\t\t\tdata: strbytes,\n\t\t\terr:  nil, offset: 8, startingOffset: 0,\n\t\t},\n\t\t{\n\t\t\tname: \"String/invalid length\",\n\t\t\tt:    TypeString,\n\t\t\tdata: strbytes[:2],\n\t\t\terr:  io.EOF, offset: 0, startingOffset: 0,\n\t\t},\n\t\t{\n\t\t\tname: \"String/not enough bytes\",\n\t\t\tt:    TypeString,\n\t\t\tdata: strbytes[:5],\n\t\t\terr:  io.EOF, offset: 0, startingOffset: 0,\n\t\t},\n\t\t{\n\t\t\tname: \"String/success\",\n\t\t\tt:    TypeString,\n\t\t\tdata: strbytes,\n\t\t\terr:  nil, offset: 8, startingOffset: 0,\n\t\t},\n\t\t{\n\t\t\tname: \"Symbol/invalid length\",\n\t\t\tt:    TypeSymbol,\n\t\t\tdata: strbytes[:2],\n\t\t\terr:  io.EOF, offset: 0, startingOffset: 0,\n\t\t},\n\t\t{\n\t\t\tname: \"Symbol/not enough bytes\",\n\t\t\tt:    TypeSymbol,\n\t\t\tdata: strbytes[:5],\n\t\t\terr:  io.EOF, offset: 0, startingOffset: 0,\n\t\t},\n\t\t{\n\t\t\tname: \"Symbol/success\",\n\t\t\tt:    TypeSymbol,\n\t\t\tdata: strbytes,\n\t\t\terr:  nil, offset: 8, startingOffset: 0,\n\t\t},\n\t\t{\n\t\t\tname: \"MaxKey/success\",\n\t\t\tt:    TypeMaxKey,\n\t\t\tdata: []byte{},\n\t\t\terr:  nil, offset: 0, startingOffset: 0,\n\t\t},\n\t\t{\n\t\t\tname: \"MinKey/success\",\n\t\t\tt:    TypeMinKey,\n\t\t\tdata: []byte{},\n\t\t\terr:  nil, offset: 0, startingOffset: 0,\n\t\t},\n\t\t{\n\t\t\tname: \"Null/success\",\n\t\t\tt:    TypeNull,\n\t\t\tdata: []byte{},\n\t\t\terr:  nil, offset: 0, startingOffset: 0,\n\t\t},\n\t\t{\n\t\t\tname: \"Undefined/success\",\n\t\t\tt:    TypeUndefined,\n\t\t\tdata: []byte{},\n\t\t\terr:  nil, offset: 0, startingOffset: 0,\n\t\t},\n\t\t{\n\t\t\tname: \"ObjectID/not enough bytes\",\n\t\t\tt:    TypeObjectID,\n\t\t\tdata: []byte{0x01, 0x02, 0x03, 0x04},\n\t\t\terr:  io.EOF, offset: 0, startingOffset: 0,\n\t\t},\n\t\t{\n\t\t\tname: \"ObjectID/success\",\n\t\t\tt:    TypeObjectID,\n\t\t\tdata: []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C},\n\t\t\terr:  nil, offset: 12, startingOffset: 0,\n\t\t},\n\t\t{\n\t\t\tname: \"Regex/not enough bytes (first string)\",\n\t\t\tt:    TypeRegex,\n\t\t\tdata: []byte{'f', 'o', 'o'},\n\t\t\terr:  io.EOF, offset: 0, startingOffset: 0,\n\t\t},\n\t\t{\n\t\t\tname: \"Regex/not enough bytes (second string)\",\n\t\t\tt:    TypeRegex,\n\t\t\tdata: []byte{'f', 'o', 'o', 0x00, 'b', 'a', 'r'},\n\t\t\terr:  io.EOF, offset: 0, startingOffset: 0,\n\t\t},\n\t\t{\n\t\t\tname: \"Regex/success\",\n\t\t\tt:    TypeRegex,\n\t\t\tdata: []byte{0x00, 0x00, 0x00, 'f', 'o', 'o', 0x00, 'i', 0x00},\n\t\t\terr:  nil, offset: 9, startingOffset: 3,\n\t\t},\n\t\t{\n\t\t\tname:   \"Unknown Type\",\n\t\t\tt:      Type(0),\n\t\t\tdata:   nil,\n\t\t\terr:    fmt.Errorf(\"attempted to read bytes of unknown BSON type %v\", Type(0)),\n\t\t\toffset: 0, startingOffset: 0,\n\t\t},\n\t}\n\tfor _, tc := range testCases {\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\tconst startingEnd = 64\n\t\t\tt.Run(\"Skip\", func(t *testing.T) {\n\t\t\t\tvr := &valueReader{\n\t\t\t\t\tsrc: &streamingByteSrc{\n\t\t\t\t\t\tbr:     bufio.NewReader(bytes.NewReader(tc.data[tc.startingOffset:tc.offset])),\n\t\t\t\t\t\toffset: tc.startingOffset,\n\t\t\t\t\t},\n\t\t\t\t\tstack: []vrState{\n\t\t\t\t\t\t{mode: mTopLevel, end: startingEnd},\n\t\t\t\t\t\t{mode: mElement, vType: tc.t},\n\t\t\t\t\t},\n\t\t\t\t\tframe: 1,\n\t\t\t\t}\n\n\t\t\t\terr := vr.Skip()\n\t\t\t\tif !errequal(t, err, tc.err) {\n\t\t\t\t\tt.Errorf(\"Did not receive expected error; got %v; want %v\", err, tc.err)\n\t\t\t\t}\n\t\t\t\tif tc.err == nil {\n\t\t\t\t\toffset := startingEnd - vr.stack[0].end\n\t\t\t\t\tif offset != tc.offset {\n\t\t\t\t\t\tt.Errorf(\"Offset not set at correct position; got %d; want %d\", offset, tc.offset)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t})\n\t\t\tt.Run(\"ReadBytes\", func(t *testing.T) {\n\t\t\t\tvr := &valueReader{\n\t\t\t\t\tsrc: &streamingByteSrc{\n\t\t\t\t\t\tbr:     bufio.NewReader(bytes.NewReader(tc.data[tc.startingOffset:tc.offset])),\n\t\t\t\t\t\toffset: tc.startingOffset,\n\t\t\t\t\t},\n\t\t\t\t\tstack: []vrState{\n\t\t\t\t\t\t{mode: mTopLevel, end: startingEnd},\n\t\t\t\t\t\t{mode: mElement, vType: tc.t},\n\t\t\t\t\t},\n\t\t\t\t\tframe: 1,\n\t\t\t\t}\n\n\t\t\t\t_, got, err := vr.readValueBytes(nil)\n\t\t\t\tif !errequal(t, err, tc.err) {\n\t\t\t\t\tt.Errorf(\"Did not receive expected error; got %v; want %v\", err, tc.err)\n\t\t\t\t}\n\t\t\t\tif tc.err == nil {\n\t\t\t\t\toffset := startingEnd - vr.stack[0].end\n\t\t\t\t\tif offset != tc.offset {\n\t\t\t\t\t\tt.Errorf(\"Offset not set at correct position; got %d; want %d\", vr.offset, tc.offset)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif tc.err == nil && !bytes.Equal(got, tc.data[tc.startingOffset:]) {\n\t\t\t\t\tt.Errorf(\"Did not receive expected bytes. got %v; want %v\", got, tc.data[tc.startingOffset:])\n\t\t\t\t}\n\t\t\t})\n\t\t})\n\t}\n\n\tt.Run(\"ReadValueBytes/Top Level Doc\", func(t *testing.T) {\n\t\ttestCases := []struct {\n\t\t\tname     string\n\t\t\twant     []byte\n\t\t\twantType Type\n\t\t\twantErr  error\n\t\t}{\n\t\t\t{\n\t\t\t\t\"success\",\n\t\t\t\tbsoncore.BuildDocument(nil, bsoncore.AppendDoubleElement(nil, \"pi\", 3.14159)),\n\t\t\t\tType(0),\n\t\t\t\tnil,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"wrong length\",\n\t\t\t\t[]byte{0x01, 0x02, 0x03},\n\t\t\t\tType(0),\n\t\t\t\tio.EOF,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"append bytes\",\n\t\t\t\t[]byte{0x01, 0x02, 0x03, 0x04},\n\t\t\t\tType(0),\n\t\t\t\tio.ErrUnexpectedEOF,\n\t\t\t},\n\t\t}\n\n\t\tfor _, tc := range testCases {\n\t\t\ttc := tc\n\t\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\t\tt.Parallel()\n\t\t\t\tvr := &valueReader{\n\t\t\t\t\tsrc: &streamingByteSrc{\n\t\t\t\t\t\tbr: bufio.NewReader(bytes.NewReader(tc.want)),\n\t\t\t\t\t},\n\t\t\t\t\tstack: []vrState{\n\t\t\t\t\t\t{mode: mTopLevel},\n\t\t\t\t\t},\n\t\t\t\t\tframe: 0,\n\t\t\t\t}\n\t\t\t\tgotType, got, gotErr := vr.readValueBytes(nil)\n\t\t\t\tif !errors.Is(gotErr, tc.wantErr) {\n\t\t\t\t\tt.Errorf(\"Did not receive expected error. got %v; want %v\", gotErr, tc.wantErr)\n\t\t\t\t}\n\t\t\t\tif tc.wantErr == nil && gotType != tc.wantType {\n\t\t\t\t\tt.Errorf(\"Did not receive expected type. got %v; want %v\", gotType, tc.wantType)\n\t\t\t\t}\n\t\t\t\tif tc.wantErr == nil && !bytes.Equal(got, tc.want) {\n\t\t\t\t\tt.Errorf(\"Did not receive expected bytes. got %v; want %v\", got, tc.want)\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t})\n}\n\nfunc TestValueReader_InvalidTransition(t *testing.T) {\n\tt.Run(\"Skip\", func(t *testing.T) {\n\t\tvr := &valueReader{stack: []vrState{{mode: mTopLevel}}}\n\t\twanterr := (&valueReader{stack: []vrState{{mode: mTopLevel}}}).invalidTransitionErr(0, \"Skip\", []mode{mElement, mValue})\n\t\tgoterr := vr.Skip()\n\t\tif !cmp.Equal(goterr, wanterr, cmp.Comparer(assert.CompareErrors)) {\n\t\t\tt.Errorf(\"Expected correct invalid transition error. got %v; want %v\", goterr, wanterr)\n\t\t}\n\t})\n\n\tt.Run(\"ReadBytes\", func(t *testing.T) {\n\t\tvr := &valueReader{stack: []vrState{{mode: mTopLevel}, {mode: mDocument}}, frame: 1}\n\t\twanterr := (&valueReader{stack: []vrState{{mode: mTopLevel}, {mode: mDocument}}, frame: 1}).\n\t\t\tinvalidTransitionErr(0, \"readValueBytes\", []mode{mElement, mValue})\n\t\t_, _, goterr := vr.readValueBytes(nil)\n\t\tif !cmp.Equal(goterr, wanterr, cmp.Comparer(assert.CompareErrors)) {\n\t\t\tt.Errorf(\"Expected correct invalid transition error. got %v; want %v\", goterr, wanterr)\n\t\t}\n\t})\n}\n\nfunc errequal(t *testing.T, err1, err2 error) bool {\n\tt.Helper()\n\tif err1 == nil && err2 == nil { // If they are both nil, they are equal\n\t\treturn true\n\t}\n\tif err1 == nil || err2 == nil { // If only one is nil, they are not equal\n\t\treturn false\n\t}\n\n\tif errors.Is(err1, err2) { // They are the same error, they are equal\n\t\treturn true\n\t}\n\n\tif err1.Error() == err2.Error() { // They string formats match, they are equal\n\t\treturn true\n\t}\n\n\treturn false\n}\n"
  },
  {
    "path": "bson/value_reader_writer_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bson\n\nimport (\n\t\"testing\"\n\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n)\n\ntype VRWInvoked byte\n\nconst (\n\t_                         = iota\n\tllvrwReadArray VRWInvoked = 1\n\tllvrwReadBinary\n\tllvrwReadBoolean\n\tllvrwReadDocument\n\tllvrwReadCodeWithScope\n\tllvrwReadDBPointer\n\tllvrwReadDateTime\n\tllvrwReadDecimal128\n\tllvrwReadDouble\n\tllvrwReadInt32\n\tllvrwReadInt64\n\tllvrwReadJavascript\n\tllvrwReadMaxKey\n\tllvrwReadMinKey\n\tllvrwReadNull\n\tllvrwReadObjectID\n\tllvrwReadRegex\n\tllvrwReadString\n\tllvrwReadSymbol\n\tllvrwReadTimestamp\n\tllvrwReadUndefined\n\tllvrwReadElement\n\tllvrwReadValue\n\tllvrwWriteArray\n\tllvrwWriteBinary\n\tllvrwWriteBinaryWithSubtype\n\tllvrwWriteBoolean\n\tllvrwWriteCodeWithScope\n\tllvrwWriteDBPointer\n\tllvrwWriteDateTime\n\tllvrwWriteDecimal128\n\tllvrwWriteDouble\n\tllvrwWriteInt32\n\tllvrwWriteInt64\n\tllvrwWriteJavascript\n\tllvrwWriteMaxKey\n\tllvrwWriteMinKey\n\tllvrwWriteNull\n\tllvrwWriteObjectID\n\tllvrwWriteRegex\n\tllvrwWriteString\n\tllvrwWriteDocument\n\tllvrwWriteSymbol\n\tllvrwWriteTimestamp\n\tllvrwWriteUndefined\n\tllvrwWriteDocumentElement\n\tllvrwWriteDocumentEnd\n\tllvrwWriteArrayElement\n\tllvrwWriteArrayEnd\n)\n\ntype TestValueReaderWriter struct {\n\tt        *testing.T\n\tinvoked  VRWInvoked\n\treadval  any\n\tbsontype Type\n\terr      error\n\terrAfter VRWInvoked // error after this method is called\n}\n\nfunc (llvrw *TestValueReaderWriter) Type() Type {\n\treturn llvrw.bsontype\n}\n\nfunc (llvrw *TestValueReaderWriter) Skip() error {\n\tpanic(\"not implemented\")\n}\n\nfunc (llvrw *TestValueReaderWriter) ReadArray() (ArrayReader, error) {\n\tllvrw.invoked = llvrwReadArray\n\tif llvrw.errAfter == llvrw.invoked {\n\t\treturn nil, llvrw.err\n\t}\n\n\treturn llvrw, nil\n}\n\nfunc (llvrw *TestValueReaderWriter) ReadBinary() (b []byte, btype byte, err error) {\n\tllvrw.invoked = llvrwReadBinary\n\tif llvrw.errAfter == llvrw.invoked {\n\t\treturn nil, 0x00, llvrw.err\n\t}\n\n\tswitch tt := llvrw.readval.(type) {\n\tcase bsoncore.Value:\n\t\tsubtype, data, _, ok := bsoncore.ReadBinary(tt.Data)\n\t\tif !ok {\n\t\t\tllvrw.t.Error(\"Invalid Value provided for return value of ReadBinary.\")\n\t\t\treturn nil, 0x00, nil\n\t\t}\n\t\treturn data, subtype, nil\n\tdefault:\n\t\tllvrw.t.Errorf(\"Incorrect type provided for return value of ReadBinary: %T\", llvrw.readval)\n\t\treturn nil, 0x00, nil\n\t}\n}\n\nfunc (llvrw *TestValueReaderWriter) ReadBoolean() (bool, error) {\n\tllvrw.invoked = llvrwReadBoolean\n\tif llvrw.errAfter == llvrw.invoked {\n\t\treturn false, llvrw.err\n\t}\n\n\tb, ok := llvrw.readval.(bool)\n\tif !ok {\n\t\tllvrw.t.Errorf(\"Incorrect type provided for return value of ReadBoolean: %T\", llvrw.readval)\n\t\treturn false, nil\n\t}\n\n\treturn b, llvrw.err\n}\n\nfunc (llvrw *TestValueReaderWriter) ReadDocument() (DocumentReader, error) {\n\tllvrw.invoked = llvrwReadDocument\n\tif llvrw.errAfter == llvrw.invoked {\n\t\treturn nil, llvrw.err\n\t}\n\n\treturn llvrw, nil\n}\n\nfunc (llvrw *TestValueReaderWriter) ReadCodeWithScope() (code string, dr DocumentReader, err error) {\n\tllvrw.invoked = llvrwReadCodeWithScope\n\tif llvrw.errAfter == llvrw.invoked {\n\t\treturn \"\", nil, llvrw.err\n\t}\n\n\treturn \"\", llvrw, nil\n}\n\nfunc (llvrw *TestValueReaderWriter) ReadDBPointer() (ns string, oid ObjectID, err error) {\n\tllvrw.invoked = llvrwReadDBPointer\n\tif llvrw.errAfter == llvrw.invoked {\n\t\treturn \"\", ObjectID{}, llvrw.err\n\t}\n\n\tswitch tt := llvrw.readval.(type) {\n\tcase bsoncore.Value:\n\t\tns, oid, _, ok := bsoncore.ReadDBPointer(tt.Data)\n\t\tif !ok {\n\t\t\tllvrw.t.Error(\"Invalid Value instance provided for return value of ReadDBPointer\")\n\t\t\treturn \"\", ObjectID{}, nil\n\t\t}\n\t\treturn ns, oid, nil\n\tdefault:\n\t\tllvrw.t.Errorf(\"Incorrect type provided for return value of ReadDBPointer: %T\", llvrw.readval)\n\t\treturn \"\", ObjectID{}, nil\n\t}\n}\n\nfunc (llvrw *TestValueReaderWriter) ReadDateTime() (int64, error) {\n\tllvrw.invoked = llvrwReadDateTime\n\tif llvrw.errAfter == llvrw.invoked {\n\t\treturn 0, llvrw.err\n\t}\n\n\tdt, ok := llvrw.readval.(int64)\n\tif !ok {\n\t\tllvrw.t.Errorf(\"Incorrect type provided for return value of ReadDateTime: %T\", llvrw.readval)\n\t\treturn 0, nil\n\t}\n\n\treturn dt, nil\n}\n\nfunc (llvrw *TestValueReaderWriter) ReadDecimal128() (Decimal128, error) {\n\tllvrw.invoked = llvrwReadDecimal128\n\tif llvrw.errAfter == llvrw.invoked {\n\t\treturn Decimal128{}, llvrw.err\n\t}\n\n\td128, ok := llvrw.readval.(Decimal128)\n\tif !ok {\n\t\tllvrw.t.Errorf(\"Incorrect type provided for return value of ReadDecimal128: %T\", llvrw.readval)\n\t\treturn Decimal128{}, nil\n\t}\n\n\treturn d128, nil\n}\n\nfunc (llvrw *TestValueReaderWriter) ReadDouble() (float64, error) {\n\tllvrw.invoked = llvrwReadDouble\n\tif llvrw.errAfter == llvrw.invoked {\n\t\treturn 0, llvrw.err\n\t}\n\n\tf64, ok := llvrw.readval.(float64)\n\tif !ok {\n\t\tllvrw.t.Errorf(\"Incorrect type provided for return value of ReadDouble: %T\", llvrw.readval)\n\t\treturn 0, nil\n\t}\n\n\treturn f64, nil\n}\n\nfunc (llvrw *TestValueReaderWriter) ReadInt32() (int32, error) {\n\tllvrw.invoked = llvrwReadInt32\n\tif llvrw.errAfter == llvrw.invoked {\n\t\treturn 0, llvrw.err\n\t}\n\n\ti32, ok := llvrw.readval.(int32)\n\tif !ok {\n\t\tllvrw.t.Errorf(\"Incorrect type provided for return value of ReadInt32: %T\", llvrw.readval)\n\t\treturn 0, nil\n\t}\n\n\treturn i32, nil\n}\n\nfunc (llvrw *TestValueReaderWriter) ReadInt64() (int64, error) {\n\tllvrw.invoked = llvrwReadInt64\n\tif llvrw.errAfter == llvrw.invoked {\n\t\treturn 0, llvrw.err\n\t}\n\ti64, ok := llvrw.readval.(int64)\n\tif !ok {\n\t\tllvrw.t.Errorf(\"Incorrect type provided for return value of ReadInt64: %T\", llvrw.readval)\n\t\treturn 0, nil\n\t}\n\n\treturn i64, nil\n}\n\nfunc (llvrw *TestValueReaderWriter) ReadJavascript() (code string, err error) {\n\tllvrw.invoked = llvrwReadJavascript\n\tif llvrw.errAfter == llvrw.invoked {\n\t\treturn \"\", llvrw.err\n\t}\n\tjs, ok := llvrw.readval.(string)\n\tif !ok {\n\t\tllvrw.t.Errorf(\"Incorrect type provided for return value of ReadJavascript: %T\", llvrw.readval)\n\t\treturn \"\", nil\n\t}\n\n\treturn js, nil\n}\n\nfunc (llvrw *TestValueReaderWriter) ReadMaxKey() error {\n\tllvrw.invoked = llvrwReadMaxKey\n\tif llvrw.errAfter == llvrw.invoked {\n\t\treturn llvrw.err\n\t}\n\n\treturn nil\n}\n\nfunc (llvrw *TestValueReaderWriter) ReadMinKey() error {\n\tllvrw.invoked = llvrwReadMinKey\n\tif llvrw.errAfter == llvrw.invoked {\n\t\treturn llvrw.err\n\t}\n\n\treturn nil\n}\n\nfunc (llvrw *TestValueReaderWriter) ReadNull() error {\n\tllvrw.invoked = llvrwReadNull\n\tif llvrw.errAfter == llvrw.invoked {\n\t\treturn llvrw.err\n\t}\n\n\treturn nil\n}\n\nfunc (llvrw *TestValueReaderWriter) ReadObjectID() (ObjectID, error) {\n\tllvrw.invoked = llvrwReadObjectID\n\tif llvrw.errAfter == llvrw.invoked {\n\t\treturn ObjectID{}, llvrw.err\n\t}\n\toid, ok := llvrw.readval.(ObjectID)\n\tif !ok {\n\t\tllvrw.t.Errorf(\"Incorrect type provided for return value of ReadObjectID: %T\", llvrw.readval)\n\t\treturn ObjectID{}, nil\n\t}\n\n\treturn oid, nil\n}\n\nfunc (llvrw *TestValueReaderWriter) ReadRegex() (pattern string, options string, err error) {\n\tllvrw.invoked = llvrwReadRegex\n\tif llvrw.errAfter == llvrw.invoked {\n\t\treturn \"\", \"\", llvrw.err\n\t}\n\tswitch tt := llvrw.readval.(type) {\n\tcase bsoncore.Value:\n\t\tpattern, options, _, ok := bsoncore.ReadRegex(tt.Data)\n\t\tif !ok {\n\t\t\tllvrw.t.Error(\"Invalid Value instance provided for ReadRegex\")\n\t\t\treturn \"\", \"\", nil\n\t\t}\n\t\treturn pattern, options, nil\n\tdefault:\n\t\tllvrw.t.Errorf(\"Incorrect type provided for return value of ReadRegex: %T\", llvrw.readval)\n\t\treturn \"\", \"\", nil\n\t}\n}\n\nfunc (llvrw *TestValueReaderWriter) ReadString() (string, error) {\n\tllvrw.invoked = llvrwReadString\n\tif llvrw.errAfter == llvrw.invoked {\n\t\treturn \"\", llvrw.err\n\t}\n\tstr, ok := llvrw.readval.(string)\n\tif !ok {\n\t\tllvrw.t.Errorf(\"Incorrect type provided for return value of ReadString: %T\", llvrw.readval)\n\t\treturn \"\", nil\n\t}\n\n\treturn str, nil\n}\n\nfunc (llvrw *TestValueReaderWriter) ReadSymbol() (symbol string, err error) {\n\tllvrw.invoked = llvrwReadSymbol\n\tif llvrw.errAfter == llvrw.invoked {\n\t\treturn \"\", llvrw.err\n\t}\n\tswitch tt := llvrw.readval.(type) {\n\tcase bsoncore.Value:\n\t\tsymbol, _, ok := bsoncore.ReadSymbol(tt.Data)\n\t\tif !ok {\n\t\t\tllvrw.t.Error(\"Invalid Value instance provided for ReadSymbol\")\n\t\t\treturn \"\", nil\n\t\t}\n\t\treturn symbol, nil\n\tdefault:\n\t\tllvrw.t.Errorf(\"Incorrect type provided for return value of ReadSymbol: %T\", llvrw.readval)\n\t\treturn \"\", nil\n\t}\n}\n\nfunc (llvrw *TestValueReaderWriter) ReadTimestamp() (t uint32, i uint32, err error) {\n\tllvrw.invoked = llvrwReadTimestamp\n\tif llvrw.errAfter == llvrw.invoked {\n\t\treturn 0, 0, llvrw.err\n\t}\n\tswitch tt := llvrw.readval.(type) {\n\tcase bsoncore.Value:\n\t\tt, i, _, ok := bsoncore.ReadTimestamp(tt.Data)\n\t\tif !ok {\n\t\t\tllvrw.t.Errorf(\"Invalid Value instance provided for return value of ReadTimestamp\")\n\t\t\treturn 0, 0, nil\n\t\t}\n\t\treturn t, i, nil\n\tdefault:\n\t\tllvrw.t.Errorf(\"Incorrect type provided for return value of ReadTimestamp: %T\", llvrw.readval)\n\t\treturn 0, 0, nil\n\t}\n}\n\nfunc (llvrw *TestValueReaderWriter) ReadUndefined() error {\n\tllvrw.invoked = llvrwReadUndefined\n\tif llvrw.errAfter == llvrw.invoked {\n\t\treturn llvrw.err\n\t}\n\n\treturn nil\n}\n\nfunc (llvrw *TestValueReaderWriter) WriteArray() (ArrayWriter, error) {\n\tllvrw.invoked = llvrwWriteArray\n\tif llvrw.errAfter == llvrw.invoked {\n\t\treturn nil, llvrw.err\n\t}\n\treturn llvrw, nil\n}\n\nfunc (llvrw *TestValueReaderWriter) WriteBinary([]byte) error {\n\tllvrw.invoked = llvrwWriteBinary\n\tif llvrw.errAfter == llvrw.invoked {\n\t\treturn llvrw.err\n\t}\n\treturn nil\n}\n\nfunc (llvrw *TestValueReaderWriter) WriteBinaryWithSubtype([]byte, byte) error {\n\tllvrw.invoked = llvrwWriteBinaryWithSubtype\n\tif llvrw.errAfter == llvrw.invoked {\n\t\treturn llvrw.err\n\t}\n\treturn nil\n}\n\nfunc (llvrw *TestValueReaderWriter) WriteBoolean(bool) error {\n\tllvrw.invoked = llvrwWriteBoolean\n\tif llvrw.errAfter == llvrw.invoked {\n\t\treturn llvrw.err\n\t}\n\treturn nil\n}\n\nfunc (llvrw *TestValueReaderWriter) WriteCodeWithScope(string) (DocumentWriter, error) {\n\tllvrw.invoked = llvrwWriteCodeWithScope\n\tif llvrw.errAfter == llvrw.invoked {\n\t\treturn nil, llvrw.err\n\t}\n\treturn llvrw, nil\n}\n\nfunc (llvrw *TestValueReaderWriter) WriteDBPointer(string, ObjectID) error {\n\tllvrw.invoked = llvrwWriteDBPointer\n\tif llvrw.errAfter == llvrw.invoked {\n\t\treturn llvrw.err\n\t}\n\treturn nil\n}\n\nfunc (llvrw *TestValueReaderWriter) WriteDateTime(int64) error {\n\tllvrw.invoked = llvrwWriteDateTime\n\tif llvrw.errAfter == llvrw.invoked {\n\t\treturn llvrw.err\n\t}\n\treturn nil\n}\n\nfunc (llvrw *TestValueReaderWriter) WriteDecimal128(Decimal128) error {\n\tllvrw.invoked = llvrwWriteDecimal128\n\tif llvrw.errAfter == llvrw.invoked {\n\t\treturn llvrw.err\n\t}\n\treturn nil\n}\n\nfunc (llvrw *TestValueReaderWriter) WriteDouble(float64) error {\n\tllvrw.invoked = llvrwWriteDouble\n\tif llvrw.errAfter == llvrw.invoked {\n\t\treturn llvrw.err\n\t}\n\treturn nil\n}\n\nfunc (llvrw *TestValueReaderWriter) WriteInt32(int32) error {\n\tllvrw.invoked = llvrwWriteInt32\n\tif llvrw.errAfter == llvrw.invoked {\n\t\treturn llvrw.err\n\t}\n\treturn nil\n}\n\nfunc (llvrw *TestValueReaderWriter) WriteInt64(int64) error {\n\tllvrw.invoked = llvrwWriteInt64\n\tif llvrw.errAfter == llvrw.invoked {\n\t\treturn llvrw.err\n\t}\n\treturn nil\n}\n\nfunc (llvrw *TestValueReaderWriter) WriteJavascript(string) error {\n\tllvrw.invoked = llvrwWriteJavascript\n\tif llvrw.errAfter == llvrw.invoked {\n\t\treturn llvrw.err\n\t}\n\treturn nil\n}\n\nfunc (llvrw *TestValueReaderWriter) WriteMaxKey() error {\n\tllvrw.invoked = llvrwWriteMaxKey\n\tif llvrw.errAfter == llvrw.invoked {\n\t\treturn llvrw.err\n\t}\n\treturn nil\n}\n\nfunc (llvrw *TestValueReaderWriter) WriteMinKey() error {\n\tllvrw.invoked = llvrwWriteMinKey\n\tif llvrw.errAfter == llvrw.invoked {\n\t\treturn llvrw.err\n\t}\n\treturn nil\n}\n\nfunc (llvrw *TestValueReaderWriter) WriteNull() error {\n\tllvrw.invoked = llvrwWriteNull\n\tif llvrw.errAfter == llvrw.invoked {\n\t\treturn llvrw.err\n\t}\n\treturn nil\n}\n\nfunc (llvrw *TestValueReaderWriter) WriteObjectID(ObjectID) error {\n\tllvrw.invoked = llvrwWriteObjectID\n\tif llvrw.errAfter == llvrw.invoked {\n\t\treturn llvrw.err\n\t}\n\treturn nil\n}\n\nfunc (llvrw *TestValueReaderWriter) WriteRegex(string, string) error {\n\tllvrw.invoked = llvrwWriteRegex\n\tif llvrw.errAfter == llvrw.invoked {\n\t\treturn llvrw.err\n\t}\n\treturn nil\n}\n\nfunc (llvrw *TestValueReaderWriter) WriteString(string) error {\n\tllvrw.invoked = llvrwWriteString\n\tif llvrw.errAfter == llvrw.invoked {\n\t\treturn llvrw.err\n\t}\n\treturn nil\n}\n\nfunc (llvrw *TestValueReaderWriter) WriteDocument() (DocumentWriter, error) {\n\tllvrw.invoked = llvrwWriteDocument\n\tif llvrw.errAfter == llvrw.invoked {\n\t\treturn nil, llvrw.err\n\t}\n\treturn llvrw, nil\n}\n\nfunc (llvrw *TestValueReaderWriter) WriteSymbol(string) error {\n\tllvrw.invoked = llvrwWriteSymbol\n\tif llvrw.errAfter == llvrw.invoked {\n\t\treturn llvrw.err\n\t}\n\treturn nil\n}\n\nfunc (llvrw *TestValueReaderWriter) WriteTimestamp(uint32, uint32) error {\n\tllvrw.invoked = llvrwWriteTimestamp\n\tif llvrw.errAfter == llvrw.invoked {\n\t\treturn llvrw.err\n\t}\n\treturn nil\n}\n\nfunc (llvrw *TestValueReaderWriter) WriteUndefined() error {\n\tllvrw.invoked = llvrwWriteUndefined\n\tif llvrw.errAfter == llvrw.invoked {\n\t\treturn llvrw.err\n\t}\n\treturn nil\n}\n\nfunc (llvrw *TestValueReaderWriter) ReadElement() (string, ValueReader, error) {\n\tllvrw.invoked = llvrwReadElement\n\tif llvrw.errAfter == llvrw.invoked {\n\t\treturn \"\", nil, llvrw.err\n\t}\n\n\treturn \"\", llvrw, nil\n}\n\nfunc (llvrw *TestValueReaderWriter) WriteDocumentElement(string) (ValueWriter, error) {\n\tllvrw.invoked = llvrwWriteDocumentElement\n\tif llvrw.errAfter == llvrw.invoked {\n\t\treturn nil, llvrw.err\n\t}\n\n\treturn llvrw, nil\n}\n\nfunc (llvrw *TestValueReaderWriter) WriteDocumentEnd() error {\n\tllvrw.invoked = llvrwWriteDocumentEnd\n\tif llvrw.errAfter == llvrw.invoked {\n\t\treturn llvrw.err\n\t}\n\n\treturn nil\n}\n\nfunc (llvrw *TestValueReaderWriter) ReadValue() (ValueReader, error) {\n\tllvrw.invoked = llvrwReadValue\n\tif llvrw.errAfter == llvrw.invoked {\n\t\treturn nil, llvrw.err\n\t}\n\n\treturn llvrw, nil\n}\n\nfunc (llvrw *TestValueReaderWriter) WriteArrayElement() (ValueWriter, error) {\n\tllvrw.invoked = llvrwWriteArrayElement\n\tif llvrw.errAfter == llvrw.invoked {\n\t\treturn nil, llvrw.err\n\t}\n\n\treturn llvrw, nil\n}\n\nfunc (llvrw *TestValueReaderWriter) WriteArrayEnd() error {\n\tllvrw.invoked = llvrwWriteArrayEnd\n\tif llvrw.errAfter == llvrw.invoked {\n\t\treturn llvrw.err\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "bson/value_writer.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bson\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"math\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n)\n\nvar _ ValueWriter = &valueWriter{}\n\nvar vwPool = sync.Pool{\n\tNew: func() any {\n\t\treturn new(valueWriter)\n\t},\n}\n\nfunc putValueWriter(vw *valueWriter) {\n\tif vw != nil {\n\t\tvw.w = nil // don't leak the writer\n\t\tvwPool.Put(vw)\n\t}\n}\n\nvar documentWriterPool = sync.Pool{\n\tNew: func() any {\n\t\treturn newDocumentWriter(nil)\n\t},\n}\n\nfunc getDocumentWriter(w io.Writer) *valueWriter {\n\tvw := documentWriterPool.Get().(*valueWriter)\n\n\tvw.reset(vw.buf)\n\tvw.buf = vw.buf[:0]\n\tvw.w = w\n\n\treturn vw\n}\n\nfunc putDocumentWriter(vw *valueWriter) {\n\tif vw != nil {\n\t\tvw.w = nil // don't leak the writer\n\t\tdocumentWriterPool.Put(vw)\n\t}\n}\n\n// This is here so that during testing we can change it and not require\n// allocating a 4GB slice.\nvar maxSize = math.MaxInt32\n\ntype errMaxDocumentSizeExceeded struct {\n\tsize int64\n}\n\nfunc (mdse errMaxDocumentSizeExceeded) Error() string {\n\treturn fmt.Sprintf(\"document size (%d) is larger than the max int32\", mdse.size)\n}\n\ntype vwMode int\n\nconst (\n\t_ vwMode = iota\n\tvwTopLevel\n\tvwDocument\n\tvwArray\n\tvwValue\n\tvwElement\n\tvwCodeWithScope\n)\n\nfunc (vm vwMode) String() string {\n\tvar str string\n\n\tswitch vm {\n\tcase vwTopLevel:\n\t\tstr = \"TopLevel\"\n\tcase vwDocument:\n\t\tstr = \"DocumentMode\"\n\tcase vwArray:\n\t\tstr = \"ArrayMode\"\n\tcase vwValue:\n\t\tstr = \"ValueMode\"\n\tcase vwElement:\n\t\tstr = \"ElementMode\"\n\tcase vwCodeWithScope:\n\t\tstr = \"CodeWithScopeMode\"\n\tdefault:\n\t\tstr = \"UnknownMode\"\n\t}\n\n\treturn str\n}\n\ntype vwState struct {\n\tmode   mode\n\tkey    string\n\tarrkey int\n\tstart  int32\n}\n\ntype valueWriter struct {\n\tw   io.Writer\n\tbuf []byte\n\n\tstack []vwState\n\tframe int64\n}\n\nfunc (vw *valueWriter) advanceFrame() {\n\tvw.frame++\n\tif vw.frame >= int64(len(vw.stack)) {\n\t\tvw.stack = append(vw.stack, vwState{})\n\t}\n}\n\nfunc (vw *valueWriter) push(m mode) {\n\tvw.advanceFrame()\n\n\t// Clean the stack\n\tvw.stack[vw.frame] = vwState{mode: m}\n\n\tswitch m {\n\tcase mDocument, mArray, mCodeWithScope:\n\t\tvw.reserveLength() // WARN: this is not needed\n\t}\n}\n\nfunc (vw *valueWriter) reserveLength() {\n\tvw.stack[vw.frame].start = int32(len(vw.buf))\n\tvw.buf = append(vw.buf, 0x00, 0x00, 0x00, 0x00)\n}\n\nfunc (vw *valueWriter) pop() {\n\tswitch vw.stack[vw.frame].mode {\n\tcase mElement, mValue:\n\t\tvw.frame--\n\tcase mDocument, mArray, mCodeWithScope:\n\t\tvw.frame -= 2 // we pop twice to jump over the mElement: mDocument -> mElement -> mDocument/mTopLevel/etc...\n\t}\n}\n\n// NewDocumentWriter creates a ValueWriter that writes BSON to w.\n//\n// This ValueWriter will only write entire documents to the io.Writer and it\n// will buffer the document as it is built.\nfunc NewDocumentWriter(w io.Writer) ValueWriter {\n\treturn newDocumentWriter(w)\n}\n\nfunc newDocumentWriter(w io.Writer) *valueWriter {\n\tvw := new(valueWriter)\n\tstack := make([]vwState, 1, 5)\n\tstack[0] = vwState{mode: mTopLevel}\n\tvw.w = w\n\tvw.stack = stack\n\n\treturn vw\n}\n\nfunc newValueWriterFromSlice(buf []byte) *valueWriter {\n\tvw := new(valueWriter)\n\tstack := make([]vwState, 1, 5)\n\tstack[0] = vwState{mode: mTopLevel}\n\tvw.stack = stack\n\tvw.buf = buf\n\n\treturn vw\n}\n\nfunc (vw *valueWriter) reset(buf []byte) {\n\tif vw.stack == nil {\n\t\tvw.stack = make([]vwState, 1, 5)\n\t}\n\tvw.stack = vw.stack[:1]\n\tvw.stack[0] = vwState{mode: mTopLevel}\n\tvw.buf = buf\n\tvw.frame = 0\n\tvw.w = nil\n}\n\nfunc (vw *valueWriter) invalidTransitionError(destination mode, name string, modes []mode) error {\n\tte := TransitionError{\n\t\tname:        name,\n\t\tcurrent:     vw.stack[vw.frame].mode,\n\t\tdestination: destination,\n\t\tmodes:       modes,\n\t\taction:      \"write\",\n\t}\n\tif vw.frame != 0 {\n\t\tte.parent = vw.stack[vw.frame-1].mode\n\t}\n\treturn te\n}\n\nfunc (vw *valueWriter) writeElementHeader(t Type, destination mode, callerName string, addmodes ...mode) error {\n\tframe := &vw.stack[vw.frame]\n\tswitch frame.mode {\n\tcase mElement:\n\t\tkey := frame.key\n\t\tif !isValidCString(key) {\n\t\t\treturn errors.New(\"BSON element key cannot contain null bytes\")\n\t\t}\n\t\tvw.appendHeader(t, key)\n\tcase mValue:\n\t\tvw.appendIntHeader(t, frame.arrkey)\n\tdefault:\n\t\tmodes := []mode{mElement, mValue}\n\t\tif addmodes != nil {\n\t\t\tmodes = append(modes, addmodes...)\n\t\t}\n\t\treturn vw.invalidTransitionError(destination, callerName, modes)\n\t}\n\n\treturn nil\n}\n\nfunc (vw *valueWriter) writeValueBytes(t Type, b []byte) error {\n\tif err := vw.writeElementHeader(t, mode(0), \"WriteValueBytes\"); err != nil {\n\t\treturn err\n\t}\n\tvw.buf = append(vw.buf, b...)\n\tvw.pop()\n\treturn nil\n}\n\nfunc (vw *valueWriter) WriteArray() (ArrayWriter, error) {\n\tif err := vw.writeElementHeader(TypeArray, mArray, \"WriteArray\"); err != nil {\n\t\treturn nil, err\n\t}\n\n\tvw.push(mArray)\n\n\treturn vw, nil\n}\n\nfunc (vw *valueWriter) WriteBinary(b []byte) error {\n\treturn vw.WriteBinaryWithSubtype(b, 0x00)\n}\n\nfunc (vw *valueWriter) WriteBinaryWithSubtype(b []byte, btype byte) error {\n\tif err := vw.writeElementHeader(TypeBinary, mode(0), \"WriteBinaryWithSubtype\"); err != nil {\n\t\treturn err\n\t}\n\n\tvw.buf = bsoncore.AppendBinary(vw.buf, btype, b)\n\tvw.pop()\n\treturn nil\n}\n\nfunc (vw *valueWriter) WriteBoolean(b bool) error {\n\tif err := vw.writeElementHeader(TypeBoolean, mode(0), \"WriteBoolean\"); err != nil {\n\t\treturn err\n\t}\n\n\tvw.buf = bsoncore.AppendBoolean(vw.buf, b)\n\tvw.pop()\n\treturn nil\n}\n\nfunc (vw *valueWriter) WriteCodeWithScope(code string) (DocumentWriter, error) {\n\tif err := vw.writeElementHeader(TypeCodeWithScope, mCodeWithScope, \"WriteCodeWithScope\"); err != nil {\n\t\treturn nil, err\n\t}\n\n\t// CodeWithScope is a different than other types because we need an extra\n\t// frame on the stack. In the EndDocument code, we write the document\n\t// length, pop, write the code with scope length, and pop. To simplify the\n\t// pop code, we push a spacer frame that we'll always jump over.\n\tvw.push(mCodeWithScope)\n\tvw.buf = bsoncore.AppendString(vw.buf, code)\n\tvw.push(mSpacer)\n\tvw.push(mDocument)\n\n\treturn vw, nil\n}\n\nfunc (vw *valueWriter) WriteDBPointer(ns string, oid ObjectID) error {\n\tif err := vw.writeElementHeader(TypeDBPointer, mode(0), \"WriteDBPointer\"); err != nil {\n\t\treturn err\n\t}\n\n\tvw.buf = bsoncore.AppendDBPointer(vw.buf, ns, oid)\n\tvw.pop()\n\treturn nil\n}\n\nfunc (vw *valueWriter) WriteDateTime(dt int64) error {\n\tif err := vw.writeElementHeader(TypeDateTime, mode(0), \"WriteDateTime\"); err != nil {\n\t\treturn err\n\t}\n\n\tvw.buf = bsoncore.AppendDateTime(vw.buf, dt)\n\tvw.pop()\n\treturn nil\n}\n\nfunc (vw *valueWriter) WriteDecimal128(d128 Decimal128) error {\n\tif err := vw.writeElementHeader(TypeDecimal128, mode(0), \"WriteDecimal128\"); err != nil {\n\t\treturn err\n\t}\n\n\th, l := d128.GetBytes()\n\tvw.buf = bsoncore.AppendDecimal128(vw.buf, h, l)\n\tvw.pop()\n\treturn nil\n}\n\nfunc (vw *valueWriter) WriteDouble(f float64) error {\n\tif err := vw.writeElementHeader(TypeDouble, mode(0), \"WriteDouble\"); err != nil {\n\t\treturn err\n\t}\n\n\tvw.buf = bsoncore.AppendDouble(vw.buf, f)\n\tvw.pop()\n\treturn nil\n}\n\nfunc (vw *valueWriter) WriteInt32(i32 int32) error {\n\tif err := vw.writeElementHeader(TypeInt32, mode(0), \"WriteInt32\"); err != nil {\n\t\treturn err\n\t}\n\n\tvw.buf = bsoncore.AppendInt32(vw.buf, i32)\n\tvw.pop()\n\treturn nil\n}\n\nfunc (vw *valueWriter) WriteInt64(i64 int64) error {\n\tif err := vw.writeElementHeader(TypeInt64, mode(0), \"WriteInt64\"); err != nil {\n\t\treturn err\n\t}\n\n\tvw.buf = bsoncore.AppendInt64(vw.buf, i64)\n\tvw.pop()\n\treturn nil\n}\n\nfunc (vw *valueWriter) WriteJavascript(code string) error {\n\tif err := vw.writeElementHeader(TypeJavaScript, mode(0), \"WriteJavascript\"); err != nil {\n\t\treturn err\n\t}\n\n\tvw.buf = bsoncore.AppendJavaScript(vw.buf, code)\n\tvw.pop()\n\treturn nil\n}\n\nfunc (vw *valueWriter) WriteMaxKey() error {\n\tif err := vw.writeElementHeader(TypeMaxKey, mode(0), \"WriteMaxKey\"); err != nil {\n\t\treturn err\n\t}\n\n\tvw.pop()\n\treturn nil\n}\n\nfunc (vw *valueWriter) WriteMinKey() error {\n\tif err := vw.writeElementHeader(TypeMinKey, mode(0), \"WriteMinKey\"); err != nil {\n\t\treturn err\n\t}\n\n\tvw.pop()\n\treturn nil\n}\n\nfunc (vw *valueWriter) WriteNull() error {\n\tif err := vw.writeElementHeader(TypeNull, mode(0), \"WriteNull\"); err != nil {\n\t\treturn err\n\t}\n\n\tvw.pop()\n\treturn nil\n}\n\nfunc (vw *valueWriter) WriteObjectID(oid ObjectID) error {\n\tif err := vw.writeElementHeader(TypeObjectID, mode(0), \"WriteObjectID\"); err != nil {\n\t\treturn err\n\t}\n\n\tvw.buf = bsoncore.AppendObjectID(vw.buf, oid)\n\tvw.pop()\n\treturn nil\n}\n\nfunc (vw *valueWriter) WriteRegex(pattern string, options string) error {\n\tif !isValidCString(pattern) || !isValidCString(options) {\n\t\treturn errors.New(\"BSON regex values cannot contain null bytes\")\n\t}\n\tif err := vw.writeElementHeader(TypeRegex, mode(0), \"WriteRegex\"); err != nil {\n\t\treturn err\n\t}\n\n\tvw.buf = bsoncore.AppendRegex(vw.buf, pattern, sortStringAlphebeticAscending(options))\n\tvw.pop()\n\treturn nil\n}\n\nfunc (vw *valueWriter) WriteString(s string) error {\n\tif err := vw.writeElementHeader(TypeString, mode(0), \"WriteString\"); err != nil {\n\t\treturn err\n\t}\n\n\tvw.buf = bsoncore.AppendString(vw.buf, s)\n\tvw.pop()\n\treturn nil\n}\n\nfunc (vw *valueWriter) WriteDocument() (DocumentWriter, error) {\n\tif vw.stack[vw.frame].mode == mTopLevel {\n\t\tvw.reserveLength()\n\t\treturn vw, nil\n\t}\n\tif err := vw.writeElementHeader(TypeEmbeddedDocument, mDocument, \"WriteDocument\", mTopLevel); err != nil {\n\t\treturn nil, err\n\t}\n\n\tvw.push(mDocument)\n\treturn vw, nil\n}\n\nfunc (vw *valueWriter) WriteSymbol(symbol string) error {\n\tif err := vw.writeElementHeader(TypeSymbol, mode(0), \"WriteSymbol\"); err != nil {\n\t\treturn err\n\t}\n\n\tvw.buf = bsoncore.AppendSymbol(vw.buf, symbol)\n\tvw.pop()\n\treturn nil\n}\n\nfunc (vw *valueWriter) WriteTimestamp(t uint32, i uint32) error {\n\tif err := vw.writeElementHeader(TypeTimestamp, mode(0), \"WriteTimestamp\"); err != nil {\n\t\treturn err\n\t}\n\n\tvw.buf = bsoncore.AppendTimestamp(vw.buf, t, i)\n\tvw.pop()\n\treturn nil\n}\n\nfunc (vw *valueWriter) WriteUndefined() error {\n\tif err := vw.writeElementHeader(TypeUndefined, mode(0), \"WriteUndefined\"); err != nil {\n\t\treturn err\n\t}\n\n\tvw.pop()\n\treturn nil\n}\n\nfunc (vw *valueWriter) WriteDocumentElement(key string) (ValueWriter, error) {\n\tswitch vw.stack[vw.frame].mode {\n\tcase mTopLevel, mDocument:\n\tdefault:\n\t\treturn nil, vw.invalidTransitionError(mElement, \"WriteDocumentElement\", []mode{mTopLevel, mDocument})\n\t}\n\n\tvw.push(mElement)\n\tvw.stack[vw.frame].key = key\n\n\treturn vw, nil\n}\n\nfunc (vw *valueWriter) WriteDocumentEnd() error {\n\tswitch vw.stack[vw.frame].mode {\n\tcase mTopLevel, mDocument:\n\tdefault:\n\t\treturn fmt.Errorf(\"incorrect mode to end document: %s\", vw.stack[vw.frame].mode)\n\t}\n\n\tvw.buf = append(vw.buf, 0x00)\n\n\terr := vw.writeLength()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif vw.stack[vw.frame].mode == mTopLevel {\n\t\tif err = vw.Flush(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tvw.pop()\n\n\tif vw.stack[vw.frame].mode == mCodeWithScope {\n\t\t// We ignore the error here because of the guarantee of writeLength.\n\t\t// See the docs for writeLength for more info.\n\t\t_ = vw.writeLength()\n\t\tvw.pop()\n\t}\n\treturn nil\n}\n\nfunc (vw *valueWriter) Flush() error {\n\tif vw.w == nil {\n\t\treturn nil\n\t}\n\n\tif _, err := vw.w.Write(vw.buf); err != nil {\n\t\treturn err\n\t}\n\t// reset buffer\n\tvw.buf = vw.buf[:0]\n\treturn nil\n}\n\nfunc (vw *valueWriter) WriteArrayElement() (ValueWriter, error) {\n\tif vw.stack[vw.frame].mode != mArray {\n\t\treturn nil, vw.invalidTransitionError(mValue, \"WriteArrayElement\", []mode{mArray})\n\t}\n\n\tarrkey := vw.stack[vw.frame].arrkey\n\tvw.stack[vw.frame].arrkey++\n\n\tvw.push(mValue)\n\tvw.stack[vw.frame].arrkey = arrkey\n\n\treturn vw, nil\n}\n\nfunc (vw *valueWriter) WriteArrayEnd() error {\n\tif vw.stack[vw.frame].mode != mArray {\n\t\treturn fmt.Errorf(\"incorrect mode to end array: %s\", vw.stack[vw.frame].mode)\n\t}\n\n\tvw.buf = append(vw.buf, 0x00)\n\n\terr := vw.writeLength()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tvw.pop()\n\treturn nil\n}\n\n// NOTE: We assume that if we call writeLength more than once the same function\n// within the same function without altering the vw.buf that this method will\n// not return an error. If this changes ensure that the following methods are\n// updated:\n//\n// - WriteDocumentEnd\nfunc (vw *valueWriter) writeLength() error {\n\tlength := len(vw.buf)\n\tif length > maxSize {\n\t\treturn errMaxDocumentSizeExceeded{size: int64(len(vw.buf))}\n\t}\n\tframe := &vw.stack[vw.frame]\n\tlength -= int(frame.start)\n\tstart := frame.start\n\n\t_ = vw.buf[start+3] // BCE\n\tvw.buf[start+0] = byte(length)\n\tvw.buf[start+1] = byte(length >> 8)\n\tvw.buf[start+2] = byte(length >> 16)\n\tvw.buf[start+3] = byte(length >> 24)\n\treturn nil\n}\n\nfunc isValidCString(cs string) bool {\n\t// Disallow the zero byte in a cstring because the zero byte is used as the\n\t// terminating character.\n\t//\n\t// It's safe to check bytes instead of runes because all multibyte UTF-8\n\t// code points start with (binary) 11xxxxxx or 10xxxxxx, so 00000000 (i.e.\n\t// 0) will never be part of a multibyte UTF-8 code point. This logic is the\n\t// same as the \"r < utf8.RuneSelf\" case in strings.IndexRune but can be\n\t// inlined.\n\t//\n\t// https://cs.opensource.google/go/go/+/refs/tags/go1.21.1:src/strings/strings.go;l=127\n\treturn strings.IndexByte(cs, 0) == -1\n}\n\n// appendHeader is the same as bsoncore.AppendHeader but does not check if the\n// key is a valid C string since the caller has already checked for that.\n//\n// The caller of this function must check if key is a valid C string.\nfunc (vw *valueWriter) appendHeader(t Type, key string) {\n\tvw.buf = bsoncore.AppendType(vw.buf, bsoncore.Type(t))\n\tvw.buf = append(vw.buf, key...)\n\tvw.buf = append(vw.buf, 0x00)\n}\n\nfunc (vw *valueWriter) appendIntHeader(t Type, key int) {\n\tvw.buf = bsoncore.AppendType(vw.buf, bsoncore.Type(t))\n\tvw.buf = strconv.AppendInt(vw.buf, int64(key), 10)\n\tvw.buf = append(vw.buf, 0x00)\n}\n"
  },
  {
    "path": "bson/value_writer_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bson\n\nimport (\n\t\"bytes\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"math\"\n\t\"reflect\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n)\n\nfunc TestNewDocumentWriter(t *testing.T) {\n\tvw := newDocumentWriter(errWriter{})\n\tif vw == nil {\n\t\tt.Errorf(\"Expected non-nil ValueWriter to be returned from newDocumentWriter\")\n\t}\n}\n\nfunc TestDocumentWriter(t *testing.T) {\n\theader := []byte{0x00, 0x00, 0x00, 0x00}\n\toid := ObjectID{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C}\n\ttestCases := []struct {\n\t\tname   string\n\t\tfn     any\n\t\tparams []any\n\t\twant   []byte\n\t}{\n\t\t{\n\t\t\t\"WriteBinary\",\n\t\t\t(*valueWriter).WriteBinary,\n\t\t\t[]any{[]byte{0x01, 0x02, 0x03}},\n\t\t\tbsoncore.AppendBinaryElement(header, \"foo\", 0x00, []byte{0x01, 0x02, 0x03}),\n\t\t},\n\t\t{\n\t\t\t\"WriteBinaryWithSubtype (not 0x02)\",\n\t\t\t(*valueWriter).WriteBinaryWithSubtype,\n\t\t\t[]any{[]byte{0x01, 0x02, 0x03}, byte(0xFF)},\n\t\t\tbsoncore.AppendBinaryElement(header, \"foo\", 0xFF, []byte{0x01, 0x02, 0x03}),\n\t\t},\n\t\t{\n\t\t\t\"WriteBinaryWithSubtype (0x02)\",\n\t\t\t(*valueWriter).WriteBinaryWithSubtype,\n\t\t\t[]any{[]byte{0x01, 0x02, 0x03}, byte(0x02)},\n\t\t\tbsoncore.AppendBinaryElement(header, \"foo\", 0x02, []byte{0x01, 0x02, 0x03}),\n\t\t},\n\t\t{\n\t\t\t\"WriteBoolean\",\n\t\t\t(*valueWriter).WriteBoolean,\n\t\t\t[]any{true},\n\t\t\tbsoncore.AppendBooleanElement(header, \"foo\", true),\n\t\t},\n\t\t{\n\t\t\t\"WriteDBPointer\",\n\t\t\t(*valueWriter).WriteDBPointer,\n\t\t\t[]any{\"bar\", oid},\n\t\t\tbsoncore.AppendDBPointerElement(header, \"foo\", \"bar\", oid),\n\t\t},\n\t\t{\n\t\t\t\"WriteDateTime\",\n\t\t\t(*valueWriter).WriteDateTime,\n\t\t\t[]any{int64(12345678)},\n\t\t\tbsoncore.AppendDateTimeElement(header, \"foo\", 12345678),\n\t\t},\n\t\t{\n\t\t\t\"WriteDecimal128\",\n\t\t\t(*valueWriter).WriteDecimal128,\n\t\t\t[]any{NewDecimal128(10, 20)},\n\t\t\tbsoncore.AppendDecimal128Element(header, \"foo\", 10, 20),\n\t\t},\n\t\t{\n\t\t\t\"WriteDouble\",\n\t\t\t(*valueWriter).WriteDouble,\n\t\t\t[]any{float64(3.14159)},\n\t\t\tbsoncore.AppendDoubleElement(header, \"foo\", 3.14159),\n\t\t},\n\t\t{\n\t\t\t\"WriteInt32\",\n\t\t\t(*valueWriter).WriteInt32,\n\t\t\t[]any{int32(123456)},\n\t\t\tbsoncore.AppendInt32Element(header, \"foo\", 123456),\n\t\t},\n\t\t{\n\t\t\t\"WriteInt64\",\n\t\t\t(*valueWriter).WriteInt64,\n\t\t\t[]any{int64(1234567890)},\n\t\t\tbsoncore.AppendInt64Element(header, \"foo\", 1234567890),\n\t\t},\n\t\t{\n\t\t\t\"WriteJavascript\",\n\t\t\t(*valueWriter).WriteJavascript,\n\t\t\t[]any{\"var foo = 'bar';\"},\n\t\t\tbsoncore.AppendJavaScriptElement(header, \"foo\", \"var foo = 'bar';\"),\n\t\t},\n\t\t{\n\t\t\t\"WriteMaxKey\",\n\t\t\t(*valueWriter).WriteMaxKey,\n\t\t\t[]any{},\n\t\t\tbsoncore.AppendMaxKeyElement(header, \"foo\"),\n\t\t},\n\t\t{\n\t\t\t\"WriteMinKey\",\n\t\t\t(*valueWriter).WriteMinKey,\n\t\t\t[]any{},\n\t\t\tbsoncore.AppendMinKeyElement(header, \"foo\"),\n\t\t},\n\t\t{\n\t\t\t\"WriteNull\",\n\t\t\t(*valueWriter).WriteNull,\n\t\t\t[]any{},\n\t\t\tbsoncore.AppendNullElement(header, \"foo\"),\n\t\t},\n\t\t{\n\t\t\t\"WriteObjectID\",\n\t\t\t(*valueWriter).WriteObjectID,\n\t\t\t[]any{oid},\n\t\t\tbsoncore.AppendObjectIDElement(header, \"foo\", oid),\n\t\t},\n\t\t{\n\t\t\t\"WriteRegex\",\n\t\t\t(*valueWriter).WriteRegex,\n\t\t\t[]any{\"bar\", \"baz\"},\n\t\t\tbsoncore.AppendRegexElement(header, \"foo\", \"bar\", \"abz\"),\n\t\t},\n\t\t{\n\t\t\t\"WriteString\",\n\t\t\t(*valueWriter).WriteString,\n\t\t\t[]any{\"hello, world!\"},\n\t\t\tbsoncore.AppendStringElement(header, \"foo\", \"hello, world!\"),\n\t\t},\n\t\t{\n\t\t\t\"WriteSymbol\",\n\t\t\t(*valueWriter).WriteSymbol,\n\t\t\t[]any{\"symbollolz\"},\n\t\t\tbsoncore.AppendSymbolElement(header, \"foo\", \"symbollolz\"),\n\t\t},\n\t\t{\n\t\t\t\"WriteTimestamp\",\n\t\t\t(*valueWriter).WriteTimestamp,\n\t\t\t[]any{uint32(10), uint32(20)},\n\t\t\tbsoncore.AppendTimestampElement(header, \"foo\", 10, 20),\n\t\t},\n\t\t{\n\t\t\t\"WriteUndefined\",\n\t\t\t(*valueWriter).WriteUndefined,\n\t\t\t[]any{},\n\t\t\tbsoncore.AppendUndefinedElement(header, \"foo\"),\n\t\t},\n\t}\n\n\tfor _, tc := range testCases {\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\tfn := reflect.ValueOf(tc.fn)\n\t\t\tif fn.Kind() != reflect.Func {\n\t\t\t\tt.Fatalf(\"fn must be of kind Func but it is a %v\", fn.Kind())\n\t\t\t}\n\t\t\tif fn.Type().NumIn() != len(tc.params)+1 || fn.Type().In(0) != reflect.TypeOf((*valueWriter)(nil)) {\n\t\t\t\tt.Fatalf(\"fn must have at least one parameter and the first parameter must be a *valueWriter\")\n\t\t\t}\n\t\t\tif fn.Type().NumOut() != 1 || fn.Type().Out(0) != reflect.TypeOf((*error)(nil)).Elem() {\n\t\t\t\tt.Fatalf(\"fn must have one return value and it must be an error.\")\n\t\t\t}\n\t\t\tparams := make([]reflect.Value, 1, len(tc.params)+1)\n\t\t\tvw := newDocumentWriter(io.Discard)\n\t\t\tparams[0] = reflect.ValueOf(vw)\n\t\t\tfor _, param := range tc.params {\n\t\t\t\tparams = append(params, reflect.ValueOf(param))\n\t\t\t}\n\t\t\t_, err := vw.WriteDocument()\n\t\t\tnoerr(t, err)\n\t\t\t_, err = vw.WriteDocumentElement(\"foo\")\n\t\t\tnoerr(t, err)\n\n\t\t\tresults := fn.Call(params)\n\t\t\tif !results[0].IsValid() {\n\t\t\t\terr = results[0].Interface().(error)\n\t\t\t} else {\n\t\t\t\terr = nil\n\t\t\t}\n\t\t\tnoerr(t, err)\n\t\t\tgot := vw.buf\n\t\t\twant := tc.want\n\t\t\tif !bytes.Equal(got, want) {\n\t\t\t\tt.Errorf(\"Bytes are not equal.\\n\\tgot %v\\n\\twant %v\", got, want)\n\t\t\t}\n\n\t\t\tt.Run(\"incorrect transition\", func(t *testing.T) {\n\t\t\t\tvw = newDocumentWriter(io.Discard)\n\t\t\t\tresults := fn.Call(params)\n\t\t\t\tgot := results[0].Interface().(error)\n\t\t\t\tfnName := tc.name\n\t\t\t\tif strings.Contains(fnName, \"WriteBinary\") {\n\t\t\t\t\tfnName = \"WriteBinaryWithSubtype\"\n\t\t\t\t}\n\t\t\t\twant := TransitionError{\n\t\t\t\t\tcurrent: mTopLevel, name: fnName, modes: []mode{mElement, mValue},\n\t\t\t\t\taction: \"write\",\n\t\t\t\t}\n\t\t\t\tif !assert.CompareErrors(got, want) {\n\t\t\t\t\tt.Errorf(\"Errors do not match. got %v; want %v\", got, want)\n\t\t\t\t}\n\t\t\t})\n\t\t})\n\t}\n\n\tt.Run(\"WriteArray\", func(t *testing.T) {\n\t\tvw := newDocumentWriter(io.Discard)\n\t\tvw.push(mArray)\n\t\twant := TransitionError{\n\t\t\tcurrent: mArray, destination: mArray, parent: mTopLevel,\n\t\t\tname: \"WriteArray\", modes: []mode{mElement, mValue}, action: \"write\",\n\t\t}\n\t\t_, got := vw.WriteArray()\n\t\tif !assert.CompareErrors(got, want) {\n\t\t\tt.Errorf(\"Did not get expected error. got %v; want %v\", got, want)\n\t\t}\n\t})\n\tt.Run(\"WriteCodeWithScope\", func(t *testing.T) {\n\t\tvw := newDocumentWriter(io.Discard)\n\t\tvw.push(mArray)\n\t\twant := TransitionError{\n\t\t\tcurrent: mArray, destination: mCodeWithScope, parent: mTopLevel,\n\t\t\tname: \"WriteCodeWithScope\", modes: []mode{mElement, mValue}, action: \"write\",\n\t\t}\n\t\t_, got := vw.WriteCodeWithScope(\"\")\n\t\tif !assert.CompareErrors(got, want) {\n\t\t\tt.Errorf(\"Did not get expected error. got %v; want %v\", got, want)\n\t\t}\n\t})\n\tt.Run(\"WriteDocument\", func(t *testing.T) {\n\t\tvw := newDocumentWriter(io.Discard)\n\t\tvw.push(mArray)\n\t\twant := TransitionError{\n\t\t\tcurrent: mArray, destination: mDocument, parent: mTopLevel,\n\t\t\tname: \"WriteDocument\", modes: []mode{mElement, mValue, mTopLevel}, action: \"write\",\n\t\t}\n\t\t_, got := vw.WriteDocument()\n\t\tif !assert.CompareErrors(got, want) {\n\t\t\tt.Errorf(\"Did not get expected error. got %v; want %v\", got, want)\n\t\t}\n\t})\n\tt.Run(\"WriteDocumentElement\", func(t *testing.T) {\n\t\tvw := newDocumentWriter(io.Discard)\n\t\tvw.push(mElement)\n\t\twant := TransitionError{\n\t\t\tcurrent:     mElement,\n\t\t\tdestination: mElement,\n\t\t\tparent:      mTopLevel,\n\t\t\tname:        \"WriteDocumentElement\",\n\t\t\tmodes:       []mode{mTopLevel, mDocument},\n\t\t\taction:      \"write\",\n\t\t}\n\t\t_, got := vw.WriteDocumentElement(\"\")\n\t\tif !assert.CompareErrors(got, want) {\n\t\t\tt.Errorf(\"Did not get expected error. got %v; want %v\", got, want)\n\t\t}\n\t})\n\tt.Run(\"WriteDocumentEnd\", func(t *testing.T) {\n\t\tvw := newDocumentWriter(io.Discard)\n\t\tvw.push(mElement)\n\t\twant := fmt.Errorf(\"incorrect mode to end document: %s\", mElement)\n\t\tgot := vw.WriteDocumentEnd()\n\t\tif !assert.CompareErrors(got, want) {\n\t\t\tt.Errorf(\"Did not get expected error. got %v; want %v\", got, want)\n\t\t}\n\t\tvw.pop()\n\t\tvw.buf = append(vw.buf, make([]byte, 1023)...)\n\t\tmaxSize = 512\n\t\twant = errMaxDocumentSizeExceeded{size: 1024}\n\t\tgot = vw.WriteDocumentEnd()\n\t\tif !assert.CompareErrors(got, want) {\n\t\t\tt.Errorf(\"Did not get expected error. got %v; want %v\", got, want)\n\t\t}\n\t\tmaxSize = math.MaxInt32\n\t\twant = errors.New(\"what a nice fake error we have here\")\n\t\tvw.w = errWriter{err: want}\n\t\tgot = vw.WriteDocumentEnd()\n\t\tif !assert.CompareErrors(got, want) {\n\t\t\tt.Errorf(\"Did not get expected error. got %v; want %v\", got, want)\n\t\t}\n\t})\n\tt.Run(\"WriteArrayElement\", func(t *testing.T) {\n\t\tvw := newDocumentWriter(io.Discard)\n\t\tvw.push(mElement)\n\t\twant := TransitionError{\n\t\t\tcurrent:     mElement,\n\t\t\tdestination: mValue,\n\t\t\tparent:      mTopLevel,\n\t\t\tname:        \"WriteArrayElement\",\n\t\t\tmodes:       []mode{mArray},\n\t\t\taction:      \"write\",\n\t\t}\n\t\t_, got := vw.WriteArrayElement()\n\t\tif !assert.CompareErrors(got, want) {\n\t\t\tt.Errorf(\"Did not get expected error. got %v; want %v\", got, want)\n\t\t}\n\t})\n\tt.Run(\"WriteArrayEnd\", func(t *testing.T) {\n\t\tvw := newDocumentWriter(io.Discard)\n\t\tvw.push(mElement)\n\t\twant := fmt.Errorf(\"incorrect mode to end array: %s\", mElement)\n\t\tgot := vw.WriteArrayEnd()\n\t\tif !assert.CompareErrors(got, want) {\n\t\t\tt.Errorf(\"Did not get expected error. got %v; want %v\", got, want)\n\t\t}\n\t\tvw.push(mArray)\n\t\tvw.buf = append(vw.buf, make([]byte, 1019)...)\n\t\tmaxSize = 512\n\t\twant = errMaxDocumentSizeExceeded{size: 1024}\n\t\tgot = vw.WriteArrayEnd()\n\t\tif !assert.CompareErrors(got, want) {\n\t\t\tt.Errorf(\"Did not get expected error. got %v; want %v\", got, want)\n\t\t}\n\t\tmaxSize = math.MaxInt32\n\t})\n\n\tt.Run(\"WriteBytes\", func(t *testing.T) {\n\t\tt.Run(\"writeElementHeader error\", func(t *testing.T) {\n\t\t\tvw := newValueWriterFromSlice(nil)\n\t\t\twant := TransitionError{\n\t\t\t\tcurrent: mTopLevel, destination: mode(0),\n\t\t\t\tname: \"WriteValueBytes\", modes: []mode{mElement, mValue}, action: \"write\",\n\t\t\t}\n\t\t\tgot := vw.writeValueBytes(TypeEmbeddedDocument, nil)\n\t\t\tif !assert.CompareErrors(got, want) {\n\t\t\t\tt.Errorf(\"Did not received expected error. got %v; want %v\", got, want)\n\t\t\t}\n\t\t})\n\t\tt.Run(\"success\", func(t *testing.T) {\n\t\t\tindex, doc := bsoncore.ReserveLength(nil)\n\t\t\tdoc = bsoncore.AppendStringElement(doc, \"hello\", \"world\")\n\t\t\tdoc = append(doc, 0x00)\n\t\t\tdoc = bsoncore.UpdateLength(doc, index, int32(len(doc)))\n\n\t\t\tindex, want := bsoncore.ReserveLength(nil)\n\t\t\twant = bsoncore.AppendDocumentElement(want, \"foo\", doc)\n\t\t\twant = append(want, 0x00)\n\t\t\twant = bsoncore.UpdateLength(want, index, int32(len(want)))\n\n\t\t\tvw := newValueWriterFromSlice(make([]byte, 0, 512))\n\t\t\t_, err := vw.WriteDocument()\n\t\t\tnoerr(t, err)\n\t\t\t_, err = vw.WriteDocumentElement(\"foo\")\n\t\t\tnoerr(t, err)\n\t\t\terr = vw.writeValueBytes(TypeEmbeddedDocument, doc)\n\t\t\tnoerr(t, err)\n\t\t\terr = vw.WriteDocumentEnd()\n\t\t\tnoerr(t, err)\n\t\t\tgot := vw.buf\n\t\t\tif !bytes.Equal(got, want) {\n\t\t\t\tt.Errorf(\"Bytes are not equal. got %v; want %v\", got, want)\n\t\t\t}\n\t\t})\n\t})\n}\n\ntype errWriter struct {\n\terr error\n}\n\nfunc (ew errWriter) Write([]byte) (int, error) { return 0, ew.err }\n"
  },
  {
    "path": "bson/vector.go",
    "content": "// Copyright (C) MongoDB, Inc. 2024-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bson\n\nimport (\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"fmt\"\n\t\"math\"\n)\n\n// BSON binary vector types as described in https://bsonspec.org/spec.html.\nconst (\n\tInt8Vector      byte = 0x03\n\tFloat32Vector   byte = 0x27\n\tPackedBitVector byte = 0x10\n)\n\n// These are vector conversion errors.\nvar (\n\terrInsufficientVectorData = errors.New(\"insufficient data\")\n\terrNonZeroVectorPadding   = errors.New(\"padding must be 0\")\n\terrVectorPaddingTooLarge  = errors.New(\"padding cannot be larger than 7\")\n)\n\ntype vectorTypeError struct {\n\tMethod string\n\tType   byte\n}\n\n// Error implements the error interface.\nfunc (vte vectorTypeError) Error() string {\n\tt := \"invalid\"\n\tswitch vte.Type {\n\tcase Int8Vector:\n\t\tt = \"int8\"\n\tcase Float32Vector:\n\t\tt = \"float32\"\n\tcase PackedBitVector:\n\t\tt = \"packed bit\"\n\t}\n\treturn fmt.Sprintf(\"cannot call %s, on a type %s vector\", vte.Method, t)\n}\n\n// Vector represents a densely packed array of numbers / bits.\ntype Vector struct {\n\tdType       byte\n\tint8Data    []int8\n\tfloat32Data []float32\n\tbitData     []byte\n\tbitPadding  uint8\n}\n\n// Type returns the vector type.\nfunc (v Vector) Type() byte {\n\treturn v.dType\n}\n\n// Int8 returns the int8 slice hold by the vector.\n// It panics if v is not an int8 vector.\nfunc (v Vector) Int8() []int8 {\n\td, ok := v.Int8OK()\n\tif !ok {\n\t\tpanic(vectorTypeError{\"bson.Vector.Int8\", v.dType})\n\t}\n\treturn d\n}\n\n// Int8OK is the same as Int8, but returns a boolean instead of panicking.\nfunc (v Vector) Int8OK() ([]int8, bool) {\n\tif v.dType != Int8Vector {\n\t\treturn nil, false\n\t}\n\treturn v.int8Data, true\n}\n\n// Float32 returns the float32 slice hold by the vector.\n// It panics if v is not a float32 vector.\nfunc (v Vector) Float32() []float32 {\n\td, ok := v.Float32OK()\n\tif !ok {\n\t\tpanic(vectorTypeError{\"bson.Vector.Float32\", v.dType})\n\t}\n\treturn d\n}\n\n// Float32OK is the same as Float32, but returns a boolean instead of panicking.\nfunc (v Vector) Float32OK() ([]float32, bool) {\n\tif v.dType != Float32Vector {\n\t\treturn nil, false\n\t}\n\treturn v.float32Data, true\n}\n\n// PackedBit returns the byte slice representing the binary quantized (packed bit) vector and the byte padding, which\n// is the number of bits in the final byte that are to be ignored.\n// It panics if v is not a packed bit vector.\nfunc (v Vector) PackedBit() ([]byte, uint8) {\n\td, p, ok := v.PackedBitOK()\n\tif !ok {\n\t\tpanic(vectorTypeError{\"bson.Vector.PackedBit\", v.dType})\n\t}\n\treturn d, p\n}\n\n// PackedBitOK is the same as PackedBit, but returns a boolean instead of panicking.\nfunc (v Vector) PackedBitOK() ([]byte, uint8, bool) {\n\tif v.dType != PackedBitVector {\n\t\treturn nil, 0, false\n\t}\n\treturn v.bitData, v.bitPadding, true\n}\n\n// Binary returns the BSON Binary representation of the Vector.\nfunc (v Vector) Binary() Binary {\n\tswitch v.Type() {\n\tcase Int8Vector:\n\t\treturn binaryFromInt8Vector(v.Int8())\n\tcase Float32Vector:\n\t\treturn binaryFromFloat32Vector(v.Float32())\n\tcase PackedBitVector:\n\t\treturn binaryFromBitVector(v.PackedBit())\n\tdefault:\n\t\tpanic(fmt.Sprintf(\"invalid Vector data type: %d\", v.dType))\n\t}\n}\n\nfunc binaryFromInt8Vector(v []int8) Binary {\n\tdata := make([]byte, len(v)+2)\n\tdata[0] = Int8Vector\n\tdata[1] = 0\n\n\tfor i, e := range v {\n\t\tdata[i+2] = byte(e)\n\t}\n\n\treturn Binary{\n\t\tSubtype: TypeBinaryVector,\n\t\tData:    data,\n\t}\n}\n\nfunc binaryFromFloat32Vector(v []float32) Binary {\n\tdata := make([]byte, 2, len(v)*4+2)\n\tdata[0] = Float32Vector\n\tdata[1] = 0\n\tvar a [4]byte\n\tfor _, e := range v {\n\t\tbinary.LittleEndian.PutUint32(a[:], math.Float32bits(e))\n\t\tdata = append(data, a[:]...)\n\t}\n\n\treturn Binary{\n\t\tSubtype: TypeBinaryVector,\n\t\tData:    data,\n\t}\n}\n\nfunc binaryFromBitVector(bits []byte, padding uint8) Binary {\n\tdata := make([]byte, len(bits)+2)\n\tdata[0] = PackedBitVector\n\tdata[1] = padding\n\tcopy(data[2:], bits)\n\treturn Binary{\n\t\tSubtype: TypeBinaryVector,\n\t\tData:    data,\n\t}\n}\n\n// NewVector constructs a Vector from a slice of int8 or float32.\nfunc NewVector[T int8 | float32](data []T) Vector {\n\tvar v Vector\n\tswitch a := any(data).(type) {\n\tcase []int8:\n\t\tv.dType = Int8Vector\n\t\tv.int8Data = make([]int8, len(data))\n\t\tcopy(v.int8Data, a)\n\tcase []float32:\n\t\tv.dType = Float32Vector\n\t\tv.float32Data = make([]float32, len(data))\n\t\tcopy(v.float32Data, a)\n\tdefault:\n\t\tpanic(fmt.Errorf(\"unsupported type %T\", data))\n\t}\n\treturn v\n}\n\n// NewPackedBitVector constructs a Vector from a byte slice and a value of byte padding.\nfunc NewPackedBitVector(bits []byte, padding uint8) (Vector, error) {\n\tvar v Vector\n\tif padding > 7 {\n\t\treturn v, errVectorPaddingTooLarge\n\t}\n\tif padding > 0 && len(bits) == 0 {\n\t\treturn v, errNonZeroVectorPadding\n\t}\n\tv.dType = PackedBitVector\n\tv.bitData = make([]byte, len(bits))\n\tcopy(v.bitData, bits)\n\tv.bitPadding = padding\n\treturn v, nil\n}\n\n// NewVectorFromBinary unpacks a BSON Binary into a Vector.\nfunc NewVectorFromBinary(b Binary) (Vector, error) {\n\tvar v Vector\n\tif b.Subtype != TypeBinaryVector {\n\t\treturn v, errors.New(\"not a vector\")\n\t}\n\tif len(b.Data) < 2 {\n\t\treturn v, errInsufficientVectorData\n\t}\n\tswitch t := b.Data[0]; t {\n\tcase Int8Vector:\n\t\treturn newInt8Vector(b.Data[1:])\n\tcase Float32Vector:\n\t\treturn newFloat32Vector(b.Data[1:])\n\tcase PackedBitVector:\n\t\treturn newBitVector(b.Data[1:])\n\tdefault:\n\t\treturn v, fmt.Errorf(\"invalid Vector data type: %d\", t)\n\t}\n}\n\nfunc newInt8Vector(b []byte) (Vector, error) {\n\tvar v Vector\n\tif len(b) == 0 {\n\t\treturn v, errInsufficientVectorData\n\t}\n\tif padding := b[0]; padding > 0 {\n\t\treturn v, errNonZeroVectorPadding\n\t}\n\ts := make([]int8, 0, len(b)-1)\n\tfor i := 1; i < len(b); i++ {\n\t\ts = append(s, int8(b[i]))\n\t}\n\treturn NewVector(s), nil\n}\n\nfunc newFloat32Vector(b []byte) (Vector, error) {\n\tvar v Vector\n\tif len(b) == 0 {\n\t\treturn v, errInsufficientVectorData\n\t}\n\tif padding := b[0]; padding > 0 {\n\t\treturn v, errNonZeroVectorPadding\n\t}\n\tl := (len(b) - 1) / 4\n\tif l*4 != len(b)-1 {\n\t\treturn v, errInsufficientVectorData\n\t}\n\ts := make([]float32, 0, l)\n\tfor i := 1; i < len(b); i += 4 {\n\t\ts = append(s, math.Float32frombits(binary.LittleEndian.Uint32(b[i:i+4])))\n\t}\n\treturn NewVector(s), nil\n}\n\nfunc newBitVector(b []byte) (Vector, error) {\n\tif len(b) == 0 {\n\t\treturn Vector{}, errInsufficientVectorData\n\t}\n\treturn NewPackedBitVector(b[1:], b[0])\n}\n"
  },
  {
    "path": "bson/writer.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bson\n\n// ArrayWriter is the interface used to create a BSON or BSON adjacent array.\n// Callers must ensure they call WriteArrayEnd when they have finished creating\n// the array.\ntype ArrayWriter interface {\n\tWriteArrayElement() (ValueWriter, error)\n\tWriteArrayEnd() error\n}\n\n// DocumentWriter is the interface used to create a BSON or BSON adjacent\n// document. Callers must ensure they call WriteDocumentEnd when they have\n// finished creating the document.\ntype DocumentWriter interface {\n\tWriteDocumentElement(string) (ValueWriter, error)\n\tWriteDocumentEnd() error\n}\n\n// ValueWriter is the interface used to write BSON values. Implementations of\n// this interface handle creating BSON or BSON adjacent representations of the\n// values.\ntype ValueWriter interface {\n\tWriteArray() (ArrayWriter, error)\n\tWriteBinary(b []byte) error\n\tWriteBinaryWithSubtype(b []byte, btype byte) error\n\tWriteBoolean(bool) error\n\tWriteCodeWithScope(code string) (DocumentWriter, error)\n\tWriteDBPointer(ns string, oid ObjectID) error\n\tWriteDateTime(dt int64) error\n\tWriteDecimal128(Decimal128) error\n\tWriteDouble(float64) error\n\tWriteInt32(int32) error\n\tWriteInt64(int64) error\n\tWriteJavascript(code string) error\n\tWriteMaxKey() error\n\tWriteMinKey() error\n\tWriteNull() error\n\tWriteObjectID(ObjectID) error\n\tWriteRegex(pattern, options string) error\n\tWriteString(string) error\n\tWriteDocument() (DocumentWriter, error)\n\tWriteSymbol(symbol string) error\n\tWriteTimestamp(t, i uint32) error\n\tWriteUndefined() error\n}\n\n// sliceWriter allows a pointer to a slice of bytes to be used as an io.Writer.\ntype sliceWriter []byte\n\n// Write writes the bytes to the underlying slice.\nfunc (sw *sliceWriter) Write(p []byte) (int, error) {\n\twritten := len(p)\n\t*sw = append(*sw, p...)\n\treturn written, nil\n}\n"
  },
  {
    "path": "docs/CODEOWNERS",
    "content": "* @mongodb/dbx-go\n"
  },
  {
    "path": "docs/CONTRIBUTING.md",
    "content": "# Contributing to the MongoDB Go Driver\n\nThank you for your interest in contributing to the MongoDB Go Driver!\n\nWe are building this software together and strongly encourage contributions from the community that are within the guidelines set forth below.\n\n## Requirements\n\nGo 1.25 or higher is required to run the driver test suite.  We use [task](https://taskfile.dev/) as our task runner.\n\n## Bug Fixes and New Features\n\nBefore starting to write code, look for existing [tickets](https://jira.mongodb.org/browse/GODRIVER) or [create one](https://jira.mongodb.org/secure/CreateIssue!default.jspa) for your bug, issue, or feature request. This helps the community avoid working on something that might not be of interest or which has already been addressed. Before working on features or larger bug fixes, consider discussing your ideas in the corresponding JIRA issue to ensure that they align with the project's goals and to get feedback from the maintainers. This also ensures that your work can be reviewed and merged more smoothly.\n\n## Pull Requests & Patches\n\nThe Go Driver team uses GitHub to manage and review all code changes. Pull requests containing new features should generally be made against the master (default) branch and include relevant tests. For bug fixes, please target the latest stable branch, e.g. `release/2.2` for the 2.2.x series). The bug fix will be merged up to newer branches automatically after your pull request has been merged. If you are unsure which branch to target, please ask in the corresponding JIRA issue, and if you've created a pull request against the wrong branch, we can help you change it.\n\nWhen creating a pull request, please ensure that your code adheres to the following guidelines:\n\nCode should compile and tests should pass under all Go versions which the driver currently supports. Currently, the Go Driver supports a minimum version of Go 1.19 and requires Go 1.25 for development. Please run the following `Taskfile` targets to validate your changes:\n\n- `task fmt`\n- `task lint`\n- `task test`\n- `task test-race`\n\n**Running the tests requires that you have a `mongod` server running on localhost, listening on the default port (27017). At minimum, please test against the latest release version of the MongoDB server.**\n\nIf any tests do not pass, or relevant tests are not included, the patch will not be considered.\n\nIf you are working on a bug or feature listed in Jira, please include the ticket number prefixed with GODRIVER in the commit message and GitHub pull request title, (e.g. GODRIVER-123). For the patch commit message itself, please follow the [How to Write a Git Commit Message](https://chris.beams.io/posts/git-commit/) guide.\n\n### Linting on commit\n\nThe Go team uses [pre-commit](https://pre-commit.com/#installation) to lint both source and text files.\n\nTo install locally, run:\n\n```bash\nbrew install pre-commit\npre-commit install\n```\n\nAfter that, the checks will run on any changed files when committing.  To manually run the checks on all files, run:\n\n```bash\npre-commit run --all-files\n```\n\n### Merge up GitHub Action\n\nPR [#1962](https://github.com/mongodb/mongo-go-driver/pull/1962) added the \"Merge up\" GitHub Actions workflow to automatically roll changes from old branches into new ones. This section outlines how this process works.\n\n#### Regression\n\nIf a regression is identified in an older branch, the fix should be applied directly to the latest\nrelease branch. Once the pull request with the fix is merged into latest, the \"Merge up\" GitHub Action will\nautomatically create a pull request to merge these changes into the master branch. This ensures that all bug fixes are\nincorporated into the latest codebase and actively supported versions.\n\nFor example, suppose we have four minor release branches: release/2.0, release/2.1, release/2.2, and release/2.3. If a\nregression is found in the release/2.1 branch, you would create a pull request to fix the issue in the latest supported\nbranch, release/2.3. Once this pull request is merged, the \"Merge up\" GitHub Action will automatically create a pull\nrequest to merge the changes from release/2.3 into the master branch. Then you can proceed to release release/2.3.latest+1.\n\n```mermaid\ngitGraph\n   commit tag: \"Initial main setup\"\n\n   branch release/2.0\n   checkout release/2.0\n   commit tag: \"Initial release/2.0\"\n\n   checkout main\n   branch release/2.1\n   checkout release/2.1\n   commit tag: \"Bug introduced\"\n\n   checkout main\n   branch release/2.2\n   checkout release/2.2\n   commit tag: \"Initial release/2.2\"\n\n   checkout main\n   branch release/2.3\n   checkout release/2.3\n   commit tag: \"Initial release/2.3\"\n\n   checkout release/2.1\n   commit tag: \"Bug found in release/2.1\"\n\n   checkout release/2.3\n   commit tag: \"Bug fix applied in release/2.3 (Manual PR)\"\n\n   checkout main\n   merge release/2.3 tag: \"Merge fix from release/2.3 into master (GitHub Actions)\"\n   commit\n```\n\nIf necessary, it is also possible to apply the fix to the older branch where the bug was originally found. In our example,\nonce the pull request is merged into release/2.1, the \"Merge up\" GitHub Action will initiate a series of pull requests\nto roll the fix forward: first into release/2.2, then into release/2.3, and finally into master. This process makes sure\nthat the change cascades through every intermediate supported version.\n\n```mermaid\ngitGraph\n   commit tag: \"Initial main setup\"\n\n   branch release/2.0\n   checkout release/2.0\n   commit tag: \"Initial release/2.0\"\n\n   checkout main\n   branch release/2.1\n   checkout release/2.1\n   commit tag: \"Bug introduced\"\n\n   checkout main\n   branch release/2.2\n   checkout release/2.2\n   commit tag: \"Initial release/2.2\"\n\n   checkout main\n   branch release/2.3\n   checkout release/2.3\n   commit tag: \"Initial release/2.3\"\n\n   checkout release/2.1\n   commit tag: \"Bug fix in release/2.1 (Manual PR)\"\n\n   checkout release/2.2\n   merge release/2.1 tag: \"Merge fix from release/2.1 (GitHub Actions)\"\n   commit\n\n   checkout release/2.3\n   merge release/2.2 tag: \"Merge updates from release/2.2 (GitHub Actions)\"\n   commit\n\n   checkout main\n   merge release/2.3 tag: \"Merge updates from release/2.3 (GitHub Actions)\"\n   commit\n```\n\n#### Pull Request Management\n\nWhen the \"Merge up\" GitHub Action is enabled, multiple merge-up pull requests (such as PR1, PR2, and PR3) can be\nautomatically created at the same time for different bug fixes or features that all target, for example, the\nrelease/2.x branch. At first, PR1, PR2, and PR3 exist side by side—each handling separate changes. When PR1 and PR2 are\nclosed, the Action automatically combines their changes into PR3. This final PR3 then contains all updates,\nallowing you to merge everything into release/2.x+1 in a single, streamlined step.\n\n```mermaid\nflowchart LR\n   A[PR1: Merge up from release/2.x] --> B[Close PR1]\n   C[PR2: Merge up from release/2.x] --> D[Close PR2]\n\n   B --> E[PR3: Consolidated Final Pull Request]\n   D --> E\n    E --> F[release/2.x+1]\n    B[Close PR1]\n   D[Close PR2]\n   E[PR3: Includes changes from both PR1 and PR2]\n```\n\n#### Evergreen Config Merge Strategy\n\nChanges to the testing workflow should persist through all releases in a major version.\n\n### Cherry-picking between branches\n\n#### Using the GitHub App\n\nWithin a PR, you can make the comment:\n\n```\ndrivers-pr-bot please backport to {target_branch}\n```\n\nThe preferred workflow is to make the comment and then merge the PR.\n\nIf you merge the PR and the \"backport-pr\" task runs before you make the comment, you can\nmake the comment and then re-run the \"backport-pr\" task for that commit.\n\n#### Manually\n\nYou must first install the `gh` cli (`brew install gh`), then set your GitHub username:\n\n```bash\ngit config --global github.user <github_handle>\n```\n\nIf a Pull Request needs to be cherry-picked to a new branch, get the sha of the commit in the base branch, and then run\n\n```bash\ntask cherry-picker -- <sha>\n```\n\nBy default it will use `master` as the target branch.  The branch can be specified as the second argument, e.g.\n\n```bash\ntask cherry-picker -- <sha> branch\n```\n\nIt will create a new checkout in a temp dir, create a new branch, perform the cherry-pick, and then\nprompt before creating a PR to the target branch.\n\n## Testing / Development\n\nThe driver tests can be run against several database configurations. The most simple configuration is a standalone mongod with no auth, no ssl, and no compression. To run these basic driver tests, make sure a standalone MongoDB server instance is running at localhost:27017. To run the tests, you can run `task`. This will run coverage, run go-lint, run go-vet, and build the examples.\n\nYou can install `libmongocrypt` locally by running `task install-libmongocrypt`, which will create an `install` directory\nin the repository top level directory.  On Windows you will also need to add `c:/libmongocrypt/` to your `PATH`.\n\n### Testing Different Topologies\n\nTo test a **replica set** or **sharded cluster**, set `MONGODB_URI=\"<connection-string>\"` for the `make` command.\nFor example, for a local replica set named `rs1` comprised of three nodes on ports 27017, 27018, and 27019:\n\n```\nMONGODB_URI=\"mongodb://localhost:27017,localhost:27018,localhost:27019/?replicaSet=rs1\" make\n```\n\n### Testing Auth and TLS\n\nTo test authentication and TLS, first set up a MongoDB cluster with auth and TLS configured. Testing authentication requires a user with the `root` role on the `admin` database. Here is an example command that would run a mongod with TLS correctly configured for tests. Either set or replace PATH_TO_SERVER_KEY_FILE and PATH_TO_CA_FILE with paths to their respective files:\n\n```\nmongod \\\n--auth \\\n--tlsMode requireTLS \\\n--tlsCertificateKeyFile $PATH_TO_SERVER_KEY_FILE \\\n--tlsCAFile $PATH_TO_CA_FILE \\\n--tlsAllowInvalidCertificates\n```\n\nTo run the tests with `make`, set:\n\n- `MONGO_GO_DRIVER_CA_FILE` to the location of the CA file used by the database\n- `MONGO_GO_DRIVER_KEY_FILE` to the location of the client key file\n- `MONGO_GO_DRIVER_PKCS8_ENCRYPTED_KEY_FILE` to the location of the pkcs8 client key file encrypted with the password string: `password`\n- `MONGO_GO_DRIVER_PKCS8_UNENCRYPTED_KEY_FILE` to the location of the unencrypted pkcs8 key file\n- `MONGODB_URI` to the connection string of the server\n- `AUTH=auth`\n- `SSL=ssl`\n\nFor example:\n\n```\nAUTH=auth SSL=ssl \\\nMONGO_GO_DRIVER_CA_FILE=$PATH_TO_CA_FILE \\\nMONGO_GO_DRIVER_KEY_FILE=$PATH_TO_CLIENT_KEY_FILE \\\nMONGO_GO_DRIVER_PKCS8_ENCRYPTED_KEY_FILE=$PATH_TO_ENCRYPTED_KEY_FILE \\\nMONGO_GO_DRIVER_PKCS8_UNENCRYPTED_KEY_FILE=$PATH_TO_UNENCRYPTED_KEY_FILE \\\nMONGODB_URI=\"mongodb://user:password@localhost:27017/?authSource=admin\" \\\nmake\n```\n\nNotes:\n\n- The `--tlsAllowInvalidCertificates` flag is required on the server for the test suite to work correctly.\n- The test suite requires the auth database to be set with `?authSource=admin`, not `/admin`.\n\n### Testing Compression\n\nThe MongoDB Go Driver supports wire protocol compression using Snappy, zLib, or zstd. To run tests with wire protocol compression, set `MONGO_GO_DRIVER_COMPRESSOR` to `snappy`, `zlib`, or `zstd`.  For example:\n\n```\nMONGO_GO_DRIVER_COMPRESSOR=snappy make\n```\n\nEnsure the [`--networkMessageCompressors` flag](https://www.mongodb.com/docs/manual/reference/program/mongod/#cmdoption-mongod-networkmessagecompressors) on mongod or mongos includes `zlib` if testing zLib compression.\n\n### Testing FaaS\n\nThe requirements for testing FaaS implementations in the Go Driver vary depending on the specific runtime.\n\n#### AWS Lambda\n\nThe following are the requirements for running the AWS Lambda tests locally:\n\n1. [AWS SAM CLI](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/install-sam-cli.html)\n1. [Docker](https://www.docker.com/products/docker-desktop/)\n\nLocal testing requires exporting the `MONGODB_URI` environment variables. To build the AWS Lambda image and invoke the `MongoDBFunction` lambda function use the `build-faas-awslambda` Taskfile target:\n\n```bash\nMONGODB_URI=\"mongodb://host.docker.internal:27017\" task build-faas-awslambda\n```\n\nThe usage of host.docker.internal comes from the [Docker networking documentation](https://docs.docker.com/desktop/networking/#i-want-to-connect-from-a-container-to-a-service-on-the-host).\n\nThere is currently no arm64 support for the go1.x runtime, see [here](https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html). Known issues running on linux/arm64 include the inability to network with the localhost from the public.ecr.aws/lambda/go Docker image.\n\n### Encryption Tests\n\nMost of the tests requiring `libmongocrypt` can be run using the Docker workflow.\n\nHowever, some of the tests require secrets handling.  Please see the team [Wiki](https://wiki.corp.mongodb.com/pages/viewpage.action?spaceKey=DRIVERS&title=Testing+CSFLE) for more information.\n\nThe test suite can be run with or without the secrets as follows:\n\n```bash\ntask setup-env\ntask setup-test\ntask evg-test-versioned-api\n```\n\n### Load Balancer\n\nTo launch the load balancer on MacOS, run the following.\n\n- `brew install haproxy`\n- Clone drivers-evergreen-tools and save the path as `DRIVERS_TOOLS`.\n- Start the servers using (or use the docker-based method below):\n\n```bash\nLOAD_BALANCER=true TOPOLOGY=sharded_cluster AUTH=noauth SSL=nossl MONGODB_VERSION=6.0 DRIVERS_TOOLS=$PWD/drivers-evergreen-tools MONGO_ORCHESTRATION_HOME=$PWD/drivers-evergreen-tools/.evergreen/orchestration $PWD/drivers-evergreen-tools/.evergreen/run-orchestration.sh\n```\n\n- Start the load balancer using:\n\n```bash\nMONGODB_URI='mongodb://localhost:27017,localhost:27018/' $PWD/drivers-evergreen-tools/.evergreen/run-load-balancer.sh start\n```\n\n- Run the load balancer tests (or use the docker runner below with `evg-test-load-balancers`):\n\n```bash\ntask evg-test-load-balancers\n```\n\n### Testing in Docker\n\nWe support local testing in Docker.  To test using docker, you will need to set the `DRIVERS_TOOLs` environment variable to point to a local clone of the drivers-evergreen-tools repository. This is essential for running the testing matrix in a container. You can set the `DRIVERS_TOOLS` variable in your shell profile or in your project-specific environment.\n\n1. First, start the drivers-tools server docker container, as:\n\n```bash\nbash $DRIVERS_TOOLS/.evergreen/docker/start-server.sh\n```\n\nSee the readme in `$DRIVERS_TOOLS/.evergreen/docker` for more information on usage.\n\n2. Next, start any other required services in another terminal, like a load balancer.\n\n1. Finally, run the Go Driver tests using the following script in this repo:\n\n```bash\nmake run-docker\n```\n\nThe script takes an optional argument for the `TASKFILE_TARGET` and allows for some environment variable overrides.\nThe docker container has the required binaries, including libmongocrypt.\nThe entry script executes the desired `TASKFILE_TARGET`.\n\nFor example, to test against a sharded cluster (make sure you started the server with a sharded_cluster),\nusing enterprise auth, run:\n\n```bash\nTOPOLOGY=sharded_cluster task run-docker -- evg-test-enterprise-auth\n```\n\n## Talk To Us\n\nIf you want to work on the driver, write documentation, or have questions/complaints, please reach out to us either via [MongoDB Community Forums](https://www.mongodb.com/community/forums/tag/go-driver) or by creating a Question issue in [Jira](https://jira.mongodb.org/secure/CreateIssue!default.jspa).\n"
  },
  {
    "path": "docs/SECURITY.md",
    "content": "# Security Policy\n\n## Supported Versions\n\nWe support the latest released version of the MongoDB Go Driver. Please ensure you are using the most recent version before reporting a vulnerability.\n\n## Reporting a Vulnerability\n\nIf you discover a security vulnerability in the MongoDB Go Driver, please follow MongoDB’s responsible disclosure process:\n\n- **Do not publicly disclose the vulnerability.**\n- Report it by following the instructions at [MongoDB Vulnerability Reporting](https://www.mongodb.com/docs/manual/tutorial/create-a-vulnerability-report/).\n"
  },
  {
    "path": "docs/common-issues.md",
    "content": "# Frequently Encountered Issues\n\nThese are fixes or information for common issues encountered by Go Driver users. If none of these are helpful, [create a GODRIVER Jira ticket](https://jira.mongodb.org/secure/CreateIssue!default.jspa) and we'll try to troubleshoot your issue!\n\n## `WriteXXX` can only write while positioned on a Element or Value but is positioned on a TopLevel\n\nThe [`bson.Marshal`](https://pkg.go.dev/go.mongodb.org/mongo-driver/bson#Marshal) function requires a parameter that can be decoded into a BSON Document, i.e. a [`bson.D`](https://pkg.go.dev/go.mongodb.org/mongo-driver/bson#D). Therefore the error message\n\n> `WriteXXX` can only write while positioned on a Element or Value but is positioned on a TopLevel\n\noccurs when the input to `bson.Marshal` is something *other* than a BSON Document. Examples of this occurrence include\n\n- `WriteString`: the input into `bson.Marshal` is a string\n- `WriteNull`: the input into `bson.Marshal` is null\n- `WriteInt32`: the input into `bson.Marshal` is an integer\n\nMany CRUD operations in the Go Driver use `bson.Marshal` under the hood, so it's possible to encounter this particular error without directly attempting to encode data. For example, when using a sort on [`FindOneAndUpdate`](https://pkg.go.dev/go.mongodb.org/mongo-driver/mongo#Collection.FindOneAndUpdate) this error can occur when not properly initializing the `sort` variable:\n\n```go\nvar sort bson.D // this is nil and will result in a WriteNull error\nopts := options.FindOneAndUpdate().SetSort(sort)\nupdate := bson.D{{\"$inc\", bson.D{{\"x\", 1}}}}\nsr := coll.FindOneAndUpdate(ctx, bson.D{}, update)\nif err := sr.Err(); err != nil {\n\tlog.Fatalf(\"error getting single result: %v\", err)\n}\n```\n\nThe above example is resolved by initializing the `sort` variable:\n\n```go\nsort := bson.D{}\n```\n\n## Convert BSON Document to JSON\n\nThere are a variety of marshalers that can be used to encode a BSON document as JSON, including [MarshalExtJSON](https://pkg.go.dev/go.mongodb.org/mongo-driver/bson#MarshalExtJSON):\n\n```go\ndoc := bson.D{{\"x\", 1}}\n\njsonBytes, err := bson.MarshalExtJSON(doc, true, false)\nif err != nil {\n\tlog.Fatalf(\"error encoding json: %v\", err)\n}\n\nm := make(map[string]any)\nif err := json.Unmarshal(jsonBytes, &m); err != nil {\n\tlog.Fatalf(\"error decoding json: %v\", err)\n}\nfmt.Printf(\"json: %v\\n\", m)\n```\n\n## Authentication Failed\n\nWhen connecting to a MongoDB deployment with password authentication enabled, if your authentication information or configuration are incorrect, you may encounter an error message like:\n\n> connection() error occurred during connection handshake: auth error: sasl conversation error: unable to authenticate using mechanism \"SCRAM-SHA-256\": (AuthenticationFailed) Authentication failed.\n\nThat error can be caused by a number of issues. The error message intentionally omits the exact authentication failure reason (see the [OWASP Authentication and Error Messages guidelines](https://cheatsheetseries.owasp.org/cheatsheets/Authentication_Cheat_Sheet.html#authentication-and-error-messages) for an explanation). Possible causes of the error include:\n\n- Incorrect password.\n- Incorrect username.\n- Incorrect authentication database (i.e. [authSource](https://www.mongodb.com/docs/manual/reference/connection-string/#mongodb-urioption-urioption.authSource)).\n\nIf you encounter an `AuthenticationFailed` error like the one above, check that the username and password in your [connection string](https://www.mongodb.com/docs/manual/reference/connection-string/) or [SetAuth](https://pkg.go.dev/go.mongodb.org/mongo-driver/mongo/options#ClientOptions.SetAuth) call are correct. In most cases, [authSource](https://www.mongodb.com/docs/manual/reference/connection-string/#mongodb-urioption-urioption.authSource) does not need to be specified unless you are using a non-default authentication database (the default is \"admin\").\n"
  },
  {
    "path": "docs/migration-2.0.md",
    "content": "# Migrating from 1.x to 2.0\n\nTo upgrade imports of the Go Driver from v1 to v2, we recommend using [marwan-at-work/mod\n](https://github.com/marwan-at-work/mod):\n\n```\nmod upgrade --mod-name=go.mongodb.org/mongo-driver\n```\n\n## Description Package\n\nThe `description` package has been removed in v2.\n\n## Event Package\n\nReferences to `description.Server` and `description.Topology` have been replaced with `event.ServerDescription` and `event.TopologyDescription`, respectively. Additionally, the following changes have been made to the fields:\n\n- `Kind` has been changed from `uint32` to `string` for ease of use.\n- `SessionTimeoutMinutes` has been changed from `uint32` to `*int64` to differentiate between a zero timeout and no timeout.\n\nThe following event constants have been renamed to match their string literal value:\n\n- `PoolCreated` to `ConnectionPoolCreated`\n- `PoolReady` to `ConnectionPoolRead`\n- `PoolCleared` to `ConnectionPoolCleared`\n- `PoolClosedEvent` to `ConnectionPoolClosed`\n- `GetStarted` to `ConnectionCheckOutStarted`\n- `GetFailed` to `ConnectionCheckOutFailed`\n- `GetSucceeded` to `ConnectionCheckedOut`\n- `ConnectionReturned` to `ConnectionCheckedIn`\n\n### CommandFailedEvent\n\n`CommandFailedEvent.Failure` has been converted from a string type to an error type to convey additional error information and to match the type of other `*.Failure` fields, like `ServerHeartbeatFailedEvent.Failure`.\n\n### CommandFinishedEvent\n\nThe type for `ServerConnectionID` has been changed to `*int64` to prevent an `int32` overflow and to differentiate between an ID of `0` and no ID.\n\n### CommandStartedEvent\n\nThe type for `ServerConnectionID` has been changed to `*int64` to prevent an `int32` overflow and to differentiate between an ID of `0` and no ID.\n\n### ServerHeartbeatFailedEvent\n\n`DurationNanos` has been removed in favor of `Duration`.\n\n### ServerHeartbeatSucceededEvent\n\n`DurationNanos` has been removed in favor of `Duration`.\n\n## Mongo Package\n\n### Client\n\n#### Connect\n\n`Client.Connect()` has been removed in favor of `mongo.Connect()`. See the section on `NewClient` for more details.\n\nThe `context.Context` parameter has been removed from `mongo.Connect()` because the [deployment connector](https://github.com/mongodb/mongo-go-driver/blob/a76687682f080c9612295b646a00650d00dd16e1/x/mongo/driver/driver.go#L91) doesn’t accept a context, meaning that the context passed to `mongo.Connect()` in previous versions didn't serve a purpose.\n\n#### UseSession\\[WithOptions\\]\n\nThis example shows how to construct a session object from a context, rather than using a context to perform session operations.\n\n```go\n// v1\n\nclient.UseSession(context.TODO(), func(sctx mongo.SessionContext) error {\n  if err := sctx.StartTransaction(options.Transaction()); err != nil {\n    return err\n  }\n\n  _, err = coll.InsertOne(context.TODO(), bson.D{{\"x\", 1}})\n\n  return err\n})\n```\n\n```go\n// v2\n\nclient.UseSession(context.TODO(), func(ctx context.Context) error {\n  sess := mongo.SessionFromContext(ctx)\n\n  if err := sess.StartTransaction(options.Transaction()); err != nil {\n    return err\n  }\n\n  _, err = coll.InsertOne(context.TODO(), bson.D{{\"x\", 1}})\n\n  return err\n})\n```\n\n### Collection\n\n#### Clone\n\nThis example shows how to migrate usage of the `collection.Clone` method, which no longer returns an error.\n\n```go\n// v1\n\nclonedColl, err := coll.Clone(options.Collection())\nif err != nil {\n  log.Fatalf(\"failed to clone collection: %v\", err)\n}\n```\n\n```go\n// v2\n\nclonedColl := coll.Clone(options.Collection())\n```\n\n#### Distinct\n\nThe `Distinct()` collection method returns a struct that can be decoded, similar to `Collection.FindOne`. Instead of iterating through an untyped slice, users can decode same-type data using conventional Go syntax.\n\nIf the data returned is not same-type (i.e. `name` is not always a string) a user can iterate through the result directly as a `bson.RawArray` type:\n\n```go\n// v1\n\nfilter := bson.D{{\"age\", bson.D{{\"$gt\", 25}}}}\n\nvalues, err := coll.Distinct(context.TODO(), \"name\", filter)\nif err != nil {\n  log.Fatalf(\"failed to get distinct values: %v\", err)\n}\n\npeople := make([]any, 0, len(values))\nfor _, value := range values {\n  people = append(people, value)\n}\n\nfmt.Printf(\"car-renting persons: %v\\n\", people)\n```\n\n```go\n// v2\n\nfilter := bson.D{{\"age\", bson.D{{\"$gt\", 25}}}}\n\nres := coll.Distinct(context.TODO(), \"name\", filter)\nif err := res.Err(); err != nil {\n  log.Fatalf(\"failed to get distinct result: %v\", err)\n}\n\nvar people []string\nif err := res.Decode(&people); err != nil {\n  log.Fatal(\"failed to decode distinct result: %v\", err)\n}\n\nfmt.Printf(\"car-renting persons: %v\\n\", people)\n```\n\nIf the data returned is not same-type (i.e. `name` is not always a string) a user can iterate through the result directly as a `bson.RawArray` type:\n\n```go\n// v2\n\nfilter := bson.D{{\"age\", bson.D{{\"$gt\", 25}}}}\ndistinctOpts := options.Distinct().SetMaxTime(2 * time.Second)\n\nres := coll.Distinct(context.TODO(), \"name\", filter, distinctOpts)\nif err := res.Err(); err != nil {\n  log.Fatalf(\"failed to get distinct result: %v\", err)\n}\n\nrawArr, err := res.Raw()\nif err != nil {\n  log.Fatalf(\"failed to get raw data: %v\", err)\n}\n\nvalues, err := rawArr.Values()\nif err != nil {\n  log.Fatalf(\"failed to get values: %v\", err)\n}\n\npeople := make([]string, 0, len(rawArr))\nfor _, value := range values {\n  people = append(people, value.String())\n}\n\nfmt.Printf(\"car-renting persons: %v\\n\", people)\n```\n\n#### InsertMany\n\nThe `documents` parameter in the `Collection.InsertMany` function signature has been changed from an `[]any` type to an `any` type. This API no longer requires users to copy existing slice of documents to an `[]any` slice.\n\n```go\n// v1\n\nbooks := []book{\n  {\n    Name:   \"Don Quixote de la Mancha\",\n    Author: \"Miguel de Cervantes\",\n  },\n  {\n    Name:   \"Cien años de soledad\",\n    Author: \"Gabriel García Márquez\",\n  },\n  {\n    Name:   \"Crónica de una muerte anunciada\",\n    Author: \"Gabriel García Márquez\",\n  },\n}\n\nbooksi := make([]any, len(books))\nfor i, book := range books {\n  booksi[i] = book\n}\n\n_, err = collection.InsertMany(ctx, booksi)\nif err != nil {\n  log.Fatalf(\"could not insert Spanish authors: %v\", err)\n}\n```\n\n```go\n// v2\n\nbooks := []book{\n  {\n    Name:   \"Don Quixote de la Mancha\",\n    Author: \"Miguel de Cervantes\",\n  },\n  {\n    Name:   \"Cien años de soledad\",\n    Author: \"Gabriel García Márquez\",\n  },\n  {\n    Name:   \"Crónica de una muerte anunciada\",\n    Author: \"Gabriel García Márquez\",\n  },\n}\n```\n\n### Database\n\n#### ListCollectionSpecifications\n\n`ListCollectionSpecifications()` returns a slice of structs instead of a slice of pointers.\n\n```go\n// v1\n\nvar specs []*mongo.CollectionSpecification\nspecs, _ = db.ListCollectionSpecifications(context.TODO(), bson.D{})\n```\n\n```go\n// v2\n\nvar specs []mongo.CollectionSpecification\nspecs, _ = db.ListCollectionSpecifications(context.TODO(), bson.D{})\n```\n\n### ErrUnacknowledgedWrite\n\nThis sentinel error has been removed from the `mongo` package. Users that need to check if a write operation was unacknowledged can do so by inspecting the `Acknowledged` field on the associated struct:\n\n- `BulkWriteResult`\n- `DeleteResult`\n- `InsertManyResult`\n- `InsertOneResult`\n- `RewrapManyDataKeyResult`\n- `SingleResult`\n\n```go\n// v1\n\nres, err := coll.InsertMany(context.TODO(), books)\nif errors.Is(err, mongo.ErrUnacknowledgedWrite) {\n  // Do something\n}\n```\n\n```go\n// v2\n\nres, err := coll.InsertMany(context.TODO(), books)\nif !res.Acknowledged {\n  // Do something\n}\n```\n\nDDL commands such as dropping a collection will no longer return `ErrUnacknowledgedWrite`, nor will they return a result type that can be used to determine acknowledgement. It is recommended not to perform DDL commands with an unacknowledged write concern.\n\n### Cursor\n\n`Cursor.SetMaxTime` has been renamed to `Cursor.SetMaxAwaitTime`, specifying the maximum time for the server to wait for new documents that match the tailable cursor query on a capped collection.\n\n### GridFS\n\nThe `gridfs` package has been merged into the `mongo` package. Additionally, `gridfs.Bucket` has been renamed to `mongo.GridFSBucket`\n\n```go\n// v1\n\nvar bucket gridfs.Bucket\nbucket, _ = gridfs.NewBucket(db, opts)\n```\n\n```go\n// v2\n\nvar bucket mongo.GridFSBucket\nbucket, _ = db.GridFSBucket(opts)\n```\n\n#### ErrWrongIndex\n\n`ErrWrongIndex` has been renamed to the more intuitive `ErrMissingChunk`, which indicates that the number of chunks read from the server is less than expected.\n\n```go\n// v1\n\nn, err := source.Read(buf)\nif errors.Is(err, gridfs.ErrWrongIndex) {\n  // Do something\n}\n```\n\n```go\n// v2\n\nn, err := source.Read(buf)\nif errors.Is(err, mongo.ErrMissingChunk) {\n  // Do something\n}\n```\n\n#### SetWriteDeadline\n\n`SetWriteDeadline` methods have been removed from GridFS operations in favor of extending bucket methods to include a `context.Context` argument.\n\n```go\n// v1\n\nuploadStream, _ := bucket.OpenUploadStream(\"filename\", uploadOpts)\nuploadStream.SetWriteDeadline(time.Now().Add(2*time.Second))\n```\n\n```go\n// v2\n\nctx, cancel := context.WithTimeout(context.TODO(), 2*time.Second)\ndefer cancel()\n\nuploadStream, _ := bucket.OpenUploadStream(ctx, \"filename\", uploadOpts)\n```\n\nAdditionally, `Bucket.DeleteContext()`, `Bucket.FindContext()`, `Bucket.DropContext()`, and `Bucket.RenameContext()` have been removed.\n\n### IndexOptionsBuilder\n\n`mongo.IndexOptionsBuilder` has been removed, use the `IndexOptions` type in the `options` package instead.\n\n### IndexView\n\n#### DropAll\n\nDropping an index replies with a superset of the following message: `{nIndexesWas: n}`, where n indicates the number of indexes there were prior to removing whichever index(es) were dropped. In the case of `DropAll` this number is always `m - 1`, where m is the total number of indexes as you cannot delete the index on the `_id` field. Thus, we can simplify the `DropAll` method by removing the server response.\n\n```go\n// v1\n\nres, err := coll.Indexes().DropAll(context.TODO())\nif err != nil {\n  log.Fatalf(\"failed to drop indexes: %v\", err)\n}\n\ntype dropResult struct {\n  NIndexesWas int\n}\n\ndres := dropResult{}\nif err := bson.Unmarshal(res, &dres); err != nil {\n  log.Fatalf(\"failed to decode: %v\", err)\n}\n\nnumDropped := dres.NIndexWas\n\n// Use numDropped\n```\n\n```go\n// v2\n\n// List the indexes\ncur, err := coll.Indexes().List(context.TODO())\nif err != nil {\n  log.Fatalf(\"failed to list indexes: %v\", err)\n}\n\nnumDropped := 0\nfor cur.Next(context.TODO()) {\n  numDropped++\n}\n\nif err := coll.Indexes().DropAll(context.TODO()); err != nil {\n  log.Fatalf(\"failed to drop indexes: %v\", err)\n}\n\n// List the indexes\ncur, err := coll.Indexes().List(context.TODO())\nif err != nil {\n  log.Fatalf(\"failed to list indexes: %v\", err)\n}\n\n// Use numDropped\n```\n\n#### DropOne\n\nDropping an index replies with a superset of the following message: `{nIndexesWas: n}`, where n indicates the number of indexes there were prior to removing whichever index(es) were dropped. In the case of `DropOne` this number is always 1 in non-error cases. We can simplify the `DropOne` method by removing the server response.\n\n#### ListSpecifications\n\n<!-- markdown-link-check-disable -->\n\nUpdated to return a slice of structs instead of a slice of pointers. See the [database analogue](#ListCollectionSpecifications) for migration guide.\n\n<!-- markdown-link-check-enable -->\n\n### NewClient\n\n`NewClient` has been removed, use the `Connect` function in the `mongo` package instead.\n\n```go\nclient, err := mongo.NewClient(options.Client())\nif err != nil {\n  log.Fatalf(\"failed to create client: %v\", err)\n}\n\nif err := client.Connect(context.TODO()); err != nil {\n  log.Fatalf(\"failed to connect to server: %v\", err)\n}\n```\n\n```go\nclient, err := mongo.Connect(options.Client())\nif err != nil {\n  log.Fatalf(\"failed to connect to server: %v\", err)\n}\n```\n\n### Session\n\nUses of `mongo.Session` through driver constructors (such as `client.StartSession`) have been changed to return a pointer to a `mongo.Session` struct and will need to be updated accordingly.\n\n```go\n// v1\n\nvar sessions []mongo.Session\nfor i := 0; i < numSessions; i++ {\n  sess, _ := client.StartSession()\n  sessions = append(sessions, sess)\n}\n```\n\n```go\n// v2\n\nvar sessions []*mongo.Session\nfor i := 0; i < numSessions; i++ {\n  sess, _ := client.StartSession()\n  sessions = append(sessions, sess)\n}\n```\n\n### SingleResult\n\n`SingleResult.DecodeBytes` has been renamed to the more intuitive `SingleResult.Raw`.\n\n### WithSession\n\nThis example shows how to update the callback for `mongo.WithSession` to use a `context.Context` implementation, rather than the custom `mongo.SessionContext`.\n\n```go\n// v1\n\nmongo.WithSession(context.TODO(),sess,func(sctx mongo.SessionContext) error {\n  // Callback\n  return nil\n})\n```\n\n```go\n// v2\n\nmongo.WithSession(context.TODO(),sess,func(ctx context.Context) error {\n  // Callback\n  return nil\n})\n```\n\n## Options Package\n\n`ClientOptions.AuthenticateToAnything` was removed in v2 (it was marked for internal use in v1).\n\nThe following fields were removed because they are no longer supported by the server\n\n- `FindOptions.Snapshot` (4.0)\n- `FindOneOptions.Snapshot` (4.0)\n- `IndexOptions.Background` (4.2)\n\n### Options\n\nThe Go Driver offers users the ability to pass multiple options objects to operations in a last-on-wins algorithm, merging data at a field level:\n\n```pseudo\nfunction MergeOptions(target, optionsList):\n  for each options in optionsList:\n    if options is null or undefined:\n      continue\n\n  for each key, value in options:\n    if value is not null or undefined:\n      target[key] = value\n\n  return target\n```\n\nCurrently, the driver maintains this logic for every options type, e.g. [MergeFindOptions](https://github.com/mongodb/mongo-go-driver/blob/2e7cb372b05cba29facd58aac7e715c3cec4e377/mongo/options/findoptions.go#L257). For v2, we’ve decided to abstract the merge functions by changing the options builder pattern to maintain a slice of setter functions, rather than setting data directly to an options object. Typical usage of options should not change, for example the following is still honored:\n\n```go\nopts1 := options.Find().SetBatchSize(1)\nopts2 := options.Find().SetComment(\"foo\")\n\n_, err := coll.Find(context.TODO(), bson.D{{\"x\", 1\"}}, opts1, opts2)\nif err != nil {\n\tpanic(err)\n}\n```\n\nThere are two notable cases that will require a migration: (1) modifying options data after building, and (2) creating a slice of options objects.\n\n#### Modifying Fields After Building\n\nThe options builder is now a slice of setters, rather than a single options object. In order to modify the data after building, users will need to create a custom setter function and append the builder’s `Opts` slice:\n\n```go\n// v1\n\nopts := options.Find().SetBatchSize(1)\n\nif opts.MaxAwaitTime == nil {\n  opts.MaxAwaitTime = &defaultMaxAwaitTime\n}\n```\n\n```go\n// v2\n\nopts := options.Find().SetBatchSize(1)\n\nmaxAwaitTimeSetter := func(opts *options.FindOptions) error {\n  if opts.MaxAwaitTime == nil {\n    opts.MaxAwaitTime = &defaultMaxAwaitTime\n  }\n\n  return nil\n}\n\nopts.Opts = append(opts.Opts, maxAwaitTimeSetter)\n```\n\n#### Creating a Slice of Options\n\nUsing options created with the builder pattern as elements in a slice:\n\n```go\n// v1\n\nopts1 := options.Find().SetBatchSize(1)\nopts2 := options.Find().SetComment(\"foo\")\n\nopts := []*options.FindOptions{opts1, opts2}\n_, err := coll.Find(context.TODO(), bson.D{{\"x\", 1\"}}, opts...)\n```\n\n```go\n// v2\n\nopts1 := options.Find().SetBatchSize(1)\nopts2 := options.Find().SetComment(\"foo\")\n\nopts := []options.Lister[options.FindOptions]{opts1, opts2}\n_, err := coll.Find(context.TODO(), bson.D{{\"x\", 1\"}}, opts...)\n```\n\n#### Creating Options from Builder\n\nSince a builder is just a slice of option setters, users can create options directly from a builder:\n\n```go\n// v1\n\nopt := &options.FindOptions{}\nopt.SetBatchSize(1)\n\nreturn findOptionAdder{option: opt}\n```\n\n```go\n// v2\n\nvar opts options.FindOptions\nfor _, set := range options.Find().SetBatchSize(1).Opts {\n  _ = set(&opts)\n}\n\nreturn findOptionAdder{option: &opts}\n```\n\n### DeleteManyOptions / DeleteOneOptions\n\nThe `DeleteOptions` has been separated into `DeleteManyOptions` and `DeleteOneOptions` to configure the corresponding `DeleteMany` and `DeleteOne` operations.\n\n### FindOneOptions\n\nThe following types are not valid for a `findOne` operation and have been removed:\n\n- `BatchSize`\n- `CursorType`\n- `MaxAwaitTime`\n- `NoCursorTimeout`\n\n### FindOneAndUpdateOptions\n\nThe `ArrayFilters` struct type has been removed in v2. As a result, the `ArrayFilters` field in the `FindOneAndUpdateOptions` struct now uses the `[]any` type. The `ArrayFilters` field (now of type `[]any`) serves the same purpose as the `Filters` field in the original `ArrayFilters` struct.\n\n### UpdateManyOptions / UpdateOneOptions\n\nThe `UpdateOptions` has been separated into `UpdateManyOptions` and `UpdateOneOptions` to configure the corresponding `UpdateMany` and `UpdateOne` operations.\n\nThe `ArrayFilters` struct type has been removed in v2. As a result, the `ArrayFilters` fields in the new `UpdateManyOptions` and `UpdateOneOptions` structs (which replace the old `UpdateOptions` struct) now use the `[]any` type. The `ArrayFilters` field (now of type `[]any`) serves the same purpose as the `Filters` field in the original `ArrayFilters` struct.\n\n### Merge\\*Options\n\nWith the exception of `MergeClientOptions`, all functions that merge options have been removed in favor of a generic solution. `MergeClientOptions` is retained to allow combining `*ClientOptions` in a \"last-one-wins\" fashion. See [GODRIVER-2696](https://jira.mongodb.org/browse/GODRIVER-2696) for more information.\n\n### MaxTime\n\nUsers should time out operations using either the client-level operation timeout defined by `ClientOptions.Timeout` or by setting a deadline on the context object passed to an operation. The following fields and methods have been removed:\n\n- `AggregateOptions.MaxTime` and `AggregateOptions.SetMaxTime`\n- `ClientOptions.SocketTimeout` and `ClientOptions.SetSocketTimeout`\n- `CountOptions.MaxTime` and `CountOptions.SetMaxTime`\n- `DistinctOptions.MaxTime` and `DistinctOptions.SetMaxTime`\n- `EstimatedDocumentCountOptions.MaxTime` and `EstimatedDocumentCountOptions.SetMaxTime`\n- `FindOptions.MaxTime` and `FindOptions.SetMaxTime`\n- `FindOneOptions.MaxTime` and `FindOneOptions.SetMaxTime`\n- `FindOneAndReplaceOptions.MaxTime` and `FindOneAndReplaceOptions.SetMaxTime`\n- `FindOneAndUpdateOptions.MaxTime` and `FindOneAndUpdateOptions.SetMaxTime`\n- `GridFSFindOptions.MaxTime` and `GridFSFindOptions.SetMaxTime`\n- `CreateIndexesOptions.MaxTime` and `CreateIndexesOptions.SetMaxTime`\n- `DropIndexesOptions.MaxTime` and `DropIndexesOptions.SetMaxTime`\n- `ListIndexesOptions.MaxTime` and `ListIndexesOptions.SetMaxTime`\n- `SessionOptions.DefaultMaxCommitTime` and `SessionOptions.SetDefaultMaxCommitTime`\n- `TransactionOptions.MaxCommitTime` and `TransactionOptions.SetMaxCommitTime`\n\nThis example illustrates how to define an operation-level timeout using v2, without loss of generality.\n\n### SessionOptions\n\n`DefaultReadConcern`, `DefaultReadPreference`, and `DefaultWriteConcern` are all specific to transactions started by the session. Rather than maintain three fields on the `Session` struct, v2 has combined these options into `DefaultTransactionOptions` which specifies a `TransactionOptions` object.\n\n```go\n// v1\n\nsessOpts := options.Session().SetDefaultReadPreference(readpref.Primary())\n```\n\n```go\n// v2\n\ntxnOpts := options.Transaction().SetReadPreference(readpref.Primary())\nsessOpts := options.Session().SetDefaultTransactionOptions(txnOpts)\n```\n\n## Read Concern Package\n\nThe `Option` type and associated builder functions have been removed in v2 in favor of a `ReadConcern` literal declaration.\n\n### Options Builder\n\nThis example shows how to update usage of `New()` and `Level()` options builder with a `ReadConcern` literal declaration.\n\n```go\n// v1\n\nlocalRC := readconcern.New(readconcern.Level(\"local\"))\n```\n\n```go\n// v2\n\nlocalRC := &readconcern.ReadConcern{Level: \"local\"}\n```\n\n### ReadConcern.GetLevel()\n\nThe `ReadConcernGetLevel()` method has been removed. Use the `ReadConcern.Level` field to get the level instead.\n\n## Write Concern Package\n\n### WTimeout\n\nThe `WTimeout` field has been removed from the `WriteConcern` struct. Instead, users should define a timeout at the operation-level using a context object.\n\n## Bsoncodecs / Bsonoptions Package\n\n`*Codec` structs and `New*Codec` methods have been removed. Additionally, the correlated `bson/bsonoptions` package has been removed, so codecs are not directly configurable using `*CodecOptions` structs in Go Driver 2.0. To configure the encode and decode behavior, use the configuration methods on a `bson.Encoder` or `bson.Decoder`. To configure the encode and decode behavior for a `mongo.Client`, use `options.ClientOptions.SetBSONOptions` with `BSONOptions`.\n\nThis example shows how to set `ObjectIDAsHex`.\n\n```go\n// v1\n\nvar res struct {\n\tID string\n}\n\ncodecOpt := bsonoptions.StringCodec().SetDecodeObjectIDAsHex(true)\nstrCodec := bsoncodec.NewStringCodec(codecOpt)\nreg := bson.NewRegistryBuilder().RegisterDefaultDecoder(reflect.String, strCodec).Build()\ndc := bsoncodec.DecodeContext{Registry: reg}\ndec, err := bson.NewDecoderWithContext(dc, bsonrw.NewBSONDocumentReader(DOCUMENT))\nif err != nil {\n\tpanic(err)\n}\nerr = dec.Decode(&res)\nif err != nil {\n\tpanic(err)\n}\n```\n\n```go\n// v2\n\nvar res struct {\n\tID string\n}\n\ndecoder := bson.NewDecoder(bson.NewDocumentReader(bytes.NewReader(DOCUMENT)))\ndecoder.ObjectIDAsHexString()\nerr := decoder.Decode(&res)\nif err != nil {\n\tpanic(err)\n}\n```\n\n### RegistryBuilder\n\nThe `RegistryBuilder` struct and the `bson.NewRegistryBuilder` function have been removed in favor of `(*bson.Decoder).SetRegistry` and `(*bson.Encoder).SetRegistry`.\n\n### StructTag / StructTagParserFunc\n\nThe `StructTag` struct as well as the `StructTagParserFunc` type have been removed. Therefore, users have to specify BSON tags manually rather than define custom BSON tag parsers.\n\n### TransitionError\n\nThe `TransitionError` struct has been merged into the bson package.\n\n### Other interfaces removed from bsoncodes package\n\n`CodecZeroer` and `Proxy` have been removed.\n\n## Bsonrw Package\n\nThe `bson/bsonrw` package has been merged into the `bson` package.\n\nAs a result, interfaces `ArrayReader`, `ArrayWriter`, `DocumentReader`, `DocumentWriter`, `ValueReader`, and `ValueWriter` are located in the `bson` package now.\n\nInterfaces `BytesReader`, `BytesWriter`, and `ValueWriterFlusher` have been removed.\n\nFunctions `NewExtJSONValueReader` and `NewExtJSONValueWriter` have been moved to the bson package as well.\n\nMoreover, the `ErrInvalidJSON` variable has been merged into the bson package.\n\n### NewBSONDocumentReader / NewBSONValueReader\n\nThe `bsonrw.NewBSONDocumentReader` has been renamed to `NewDocumentReader`, which reads from an `io.Reader`, in the `bson` package.\n\nThe `NewBSONValueReader` has been removed.\n\nThis example creates a `Decoder` that reads from a byte slice.\n\n```go\n// v1\n\nb, _ := bson.Marshal(bson.M{\"isOK\": true})\ndecoder, err := bson.NewDecoder(bsonrw.NewBSONDocumentReader(b))\n```\n\n```go\n// v2\n\nb, _ := bson.Marshal(bson.M{\"isOK\": true})\ndecoder := bson.NewDecoder(bson.NewDocumentReader(bytes.NewReader(b)))\n```\n\n### NewBSONValueWriter\n\nThe `bsonrw.NewBSONValueWriter` function has been renamed to `NewDocumentWriter` in the `bson` package.\n\nThis example creates an `Encoder` that writes BSON values to a `bytes.Buffer`.\n\n```go\n// v1\n\nbuf := new(bytes.Buffer)\nvw, err := bsonrw.NewBSONValueWriter(buf)\nencoder, err := bson.NewEncoder(vw)\n```\n\n```go\n// v2\n\nbuf := new(bytes.Buffer)\nvw := bson.NewDocumentWriter(buf)\nencoder := bson.NewEncoder(vw)\n```\n\n## Mgocompat Package\n\nThe `bson/mgocompat` has been simplified. Its implementation has been merged into the `bson` package.\n\n`ErrSetZero` has been renamed to `ErrMgoSetZero` in the `bson` package.\n\n`NewRegistryBuilder` function has been simplified to `NewMgoRegistry` in the `bson` package.\n\nSimilarly, `NewRespectNilValuesRegistryBuilder` function has been simplified to `NewRespectNilValuesMgoRegistry` in the `bson` package.\n\n## Primitive Package\n\nThe `bson/primitive` package has been merged into the `bson` package.\n\nAdditionally, the `bson.D` has implemented the `json.Marshaler` and `json.Unmarshaler` interfaces, where it uses a key-value representation in \"regular\" (i.e. non-Extended) JSON.\n\nThe `bson.D.String` and `bson.M.String` methods return an Extended JSON representation of the document.\n\n```go\n// v2\n\nd := D{{\"a\", 1}, {\"b\", 2}}\nfmt.Printf(\"%s\\n\", d)\n// Output: {\"a\":{\"$numberInt\":\"1\"},\"b\":{\"$numberInt\":\"2\"}}\n```\n\n## Bson Package\n\n### DefaultRegistry / NewRegistryBuilder\n\n`DefaultRegistry` has been removed. Using `bson.DefaultRegistry` to either access the default registry behavior or to globally modify the default registry, will be impacted by this change and will need to configure their registry using another mechanism.\n\nThe `NewRegistryBuilder` function has been removed along with the `bsoncodec.RegistryBuilder` struct as mentioned above.\n\n### Decoder\n\nThe BSON decoding logic has changed to decode into a `bson.D` by default.\n\nThe example shows the behavior change.\n\n```go\n// v1\n\nb1 := bson.M{\"a\": 1, \"b\": bson.M{\"c\": 2}}\nb2, _ := bson.Marshal(b1)\nb3 := bson.M{}\nbson.Unmarshal(b2, &b3)\nfmt.Printf(\"b3.b type: %T\\n\", b3[\"b\"])\n// Output: b3.b type: primitive.M\n```\n\n```go\n// v2\n\nb1 := bson.M{\"a\": 1, \"b\": bson.M{\"c\": 2}}\nb2, _ := bson.Marshal(b1)\nb3 := bson.M{}\nbson.Unmarshal(b2, &b3)\nfmt.Printf(\"b3.b type: %T\\n\", b3[\"b\"])\n// Output: b3.b type: bson.D\n```\n\nUse `Decoder.DefaultDocumentM()` or set the `DefaultDocumentM` field of `options.BSONOptions` to always decode documents into the `bson.M` type.\n\nTo decode documents into `map[string]any` instead of `bson.M`, use `Decoder.DefaultDocumentMap()`. While\n`bson.M` is defined as `type M map[string]any`, Go's type system treats `bson.M`\nand `map[string]any` as distinct types. This can break compatibility with\nlibraries that expect actual `map[string]any` types.\n\n```go\nb1 := map[string]any{\"a\": 1, \"b\": map[string]any{\"c\": 2}}\nb2, _ := bson.Marshal(b1)\n\ndecoder := bson.NewDecoder(bson.NewDocumentReader(bytes.NewReader(b2)))\ndecoder.DefaultDocumentMap()\n\nvar b3 map[string]any\ndecoder.Decode(&b3)\nfmt.Printf(\"b3.b type: %T\\n\", b3[\"b\"])\n// Output: b3.b type: map[string]interface {}\n```\n\nOr configure at the client level:\n\n```go\nclientOpts := options.Client().\n    SetBSONOptions(&options.BSONOptions{\n        DefaultDocumentMap: true,\n    })\n```\n\n#### NewDecoder\n\nThe signature of `NewDecoder` has been updated without an error being returned.\n\n#### NewDecoderWithContext\n\n`NewDecoderWithContext` has been removed in favor of using the `SetRegistry` method to set a registry.\n\nCorrespondingly, the following methods have been removed:\n\n- `UnmarshalWithRegistry`\n- `UnmarshalWithContext`\n- `UnmarshalValueWithRegistry`\n- `UnmarshalExtJSONWithRegistry`\n- `UnmarshalExtJSONWithContext`\n\nFor example, a boolean type can be stored in the database as a BSON boolean or 32/64-bit integer. Given a registry:\n\n```go\ntype lenientBool bool\n\nlenientBoolType := reflect.TypeOf(lenientBool(true))\n\nlenientBoolDecoder := func(\n\tdc bsoncodec.DecodeContext,\n\tvr bsonrw.ValueReader,\n\tval reflect.Value,\n) error {\n\t// All decoder implementations should check that val is valid, settable,\n\t// and is of the correct kind before proceeding.\n\tif !val.IsValid() || !val.CanSet() || val.Type() != lenientBoolType {\n\t\treturn bsoncodec.ValueDecoderError{\n\t\t\tName:     \"lenientBoolDecoder\",\n\t\t\tTypes:    []reflect.Type{lenientBoolType},\n\t\t\tReceived: val,\n\t\t}\n\t}\n\n\tvar result bool\n\tswitch vr.Type() {\n\tcase bsontype.Boolean:\n\t\tb, err := vr.ReadBoolean()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tresult = b\n\tcase bsontype.Int32:\n\t\ti32, err := vr.ReadInt32()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tresult = i32 != 0\n\tcase bsontype.Int64:\n\t\ti64, err := vr.ReadInt64()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tresult = i64 != 0\n\tdefault:\n\t\treturn fmt.Errorf(\n\t\t\t\"received invalid BSON type to decode into lenientBool: %s\",\n\t\t\tvr.Type())\n\t}\n\n\tval.SetBool(result)\n\treturn nil\n}\n\n// Create the registry\nreg := bson.NewRegistry()\nreg.RegisterTypeDecoder(\n\tlenientBoolType,\n\tbsoncodec.ValueDecoderFunc(lenientBoolDecoder))\n```\n\nFor our custom decoder with such a registry, BSON 32/64-bit integer values are considered `true` if they are non-zero.\n\n```go\n// v1\n// Use UnmarshalWithRegistry\n\n// Marshal a BSON document with a single field \"isOK\" that is a non-zero\n// integer value.\nb, err := bson.Marshal(bson.M{\"isOK\": 1})\nif err != nil {\n\tpanic(err)\n}\n\n// Now try to decode the BSON document to a struct with a field \"IsOK\" that\n// is type lenientBool. Expect that the non-zero integer value is decoded\n// as boolean true.\ntype MyDocument struct {\n\tIsOK lenientBool `bson:\"isOK\"`\n}\nvar doc MyDocument\nerr = bson.UnmarshalWithRegistry(reg, b, &doc)\nif err != nil {\n\tpanic(err)\n}\nfmt.Printf(\"%+v\\n\", doc)\n// Output: {IsOK:true}\n```\n\n```go\n// v1\n// Use NewDecoderWithContext\n\n// Marshal a BSON document with a single field \"isOK\" that is a non-zero\n// integer value.\nb, err := bson.Marshal(bson.M{\"isOK\": 1})\nif err != nil {\n\tpanic(err)\n}\n\n// Now try to decode the BSON document to a struct with a field \"IsOK\" that\n// is type lenientBool. Expect that the non-zero integer value is decoded\n// as boolean true.\ntype MyDocument struct {\n\tIsOK lenientBool `bson:\"isOK\"`\n}\nvar doc MyDocument\ndc := bsoncodec.DecodeContext{Registry: reg}\ndec, err := bson.NewDecoderWithContext(dc, bsonrw.NewBSONDocumentReader(b))\nif err != nil {\n\tpanic(err)\n}\nerr = dec.Decode(&doc)\nif err != nil {\n\tpanic(err)\n}\nfmt.Printf(\"%+v\\n\", doc)\n// Output: {IsOK:true}\n```\n\n```go\n// v2\n// Use SetRegistry\n\n// Marshal a BSON document with a single field \"isOK\" that is a non-zero\n// integer value.\nb, err := bson.Marshal(bson.M{\"isOK\": 1})\nif err != nil {\n\tpanic(err)\n}\n\n// Now try to decode the BSON document to a struct with a field \"IsOK\" that\n// is type lenientBool. Expect that the non-zero integer value is decoded\n// as boolean true.\ntype MyDocument struct {\n\tIsOK lenientBool `bson:\"isOK\"`\n}\nvar doc MyDocument\ndec := bson.NewDecoder(bson.NewDocumentReader(bytes.NewReader(b)))\ndec.SetRegistry(reg)\nerr = dec.Decode(&doc)\nif err != nil {\n\tpanic(err)\n}\nfmt.Printf(\"%+v\\n\", doc)\n// Output: {IsOK:true}\n```\n\n#### SetContext\n\nThe `SetContext` method has been removed in favor of using `SetRegistry` to set the registry of a decoder.\n\n#### SetRegistry\n\nThe signature of `SetRegistry` has been updated without an error being returned.\n\n#### Reset\n\nThe signature of `Reset` has been updated without an error being returned.\n\n#### DefaultDocumentD / DefaultDocumentM\n\n`Decoder.DefaultDocumentD()` has been removed since a document, including a top-level value (e.g. you pass in an empty interface value to Decode), is always decoded into a `bson.D` by default. Therefore, use `Decoder.DefaultDocumentM()` to always decode a document into a `bson.M` to avoid unexpected decode results.\n\n#### ObjectIDAsHexString\n\n`Decoder.ObjectIDAsHexString()` method enables decoding a BSON ObjectId as a hexadecimal string. Otherwise, the decoder returns an error by default instead of decoding as the UTF-8 representation of the raw ObjectId bytes, which results in a garbled and unusable string.\n\n### Encoder\n\n#### NewEncoder\n\nThe signature of `NewEncoder` has been updated without an error being returned.\n\n#### NewEncoderWithContext\n\n`NewEncoderWithContext` has been removed in favor of using the `SetRegistry` method to set a registry.\n\nCorrespondingly, the following methods have been removed:\n\n- `MarshalWithRegistry`\n- `MarshalWithContext`\n- `MarshalAppend`\n- `MarshalAppendWithRegistry`\n- `MarshalAppendWithContext`\n- `MarshalValueWithRegistry`\n- `MarshalValueWithContext`\n- `MarshalValueAppendWithRegistry`\n- `MarshalValueAppendWithContext`\n- `MarshalExtJSONWithRegistry`\n- `MarshalExtJSONWithContext`\n- `MarshalExtJSONAppendWithRegistry`\n- `MarshalExtJSONAppendWithContext`\n\nHere is an example of a registry that multiplies the input value by -1 when encoding for a `negatedInt`.\n\n```go\ntype negatedInt int\n\nnegatedIntType := reflect.TypeOf(negatedInt(0))\n\nnegatedIntEncoder := func(\n\tec bsoncodec.EncodeContext,\n\tvw bsonrw.ValueWriter,\n\tval reflect.Value,\n) error {\n\t// All encoder implementations should check that val is valid and is of\n\t// the correct type before proceeding.\n\tif !val.IsValid() || val.Type() != negatedIntType {\n\t\treturn bsoncodec.ValueEncoderError{\n\t\t\tName:     \"negatedIntEncoder\",\n\t\t\tTypes:    []reflect.Type{negatedIntType},\n\t\t\tReceived: val,\n\t\t}\n\t}\n\n\t// Negate val and encode as a BSON int32 if it can fit in 32 bits and a\n\t// BSON int64 otherwise.\n\tnegatedVal := val.Int() * -1\n\tif math.MinInt32 <= negatedVal && negatedVal <= math.MaxInt32 {\n\t\treturn vw.WriteInt32(int32(negatedVal))\n\t}\n\treturn vw.WriteInt64(negatedVal)\n}\n\n// Create the registry.\nreg := bson.NewRegistry()\nreg.RegisterTypeEncoder(\n\tnegatedIntType,\n\tbsoncodec.ValueEncoderFunc(negatedIntEncoder))\n```\n\nEncode by creating a custom encoder with the registry:\n\n```go\n// v1\n// Use MarshalWithRegistry\n\nb, err := bson.MarshalWithRegistry(reg, bson.D{{\"negatedInt\", negatedInt(1)}})\nif err != nil {\n\tpanic(err)\n}\nfmt.Println(bson.Raw(b).String())\n// Output: {\"negatedint\": {\"$numberInt\":\"-1\"}}\n```\n\n```go\n// v1\n// Use NewEncoderWithContext\n\nbuf := new(bytes.Buffer)\nvw, err := bsonrw.NewBSONValueWriter(buf)\nif err != nil {\n\tpanic(err)\n}\nec := bsoncodec.EncodeContext{Registry: reg}\nenc, err := bson.NewEncoderWithContext(ec, vw)\nif err != nil {\n\tpanic(err)\n}\nerr = enc.Encode(bson.D{{\"negatedInt\", negatedInt(1)}})\nif err != nil {\n\tpanic(err)\n}\nfmt.Println(bson.Raw(buf.Bytes()).String())\n// Output: {\"negatedint\": {\"$numberInt\":\"-1\"}}\n```\n\n```go\n// v2\n// Use SetRegistry\n\nbuf := new(bytes.Buffer)\nvw := bson.NewDocumentWriter(buf)\nenc := bson.NewEncoder(vw)\nenc.SetRegistry(reg)\nerr := enc.Encode(bson.D{{\"negatedInt\", negatedInt(1)}})\nif err != nil {\n\tpanic(err)\n}\nfmt.Println(bson.Raw(buf.Bytes()).String())\n// Output: {\"negatedint\": {\"$numberInt\":\"-1\"}}\n```\n\n#### SetContext\n\nThe `SetContext` method has been removed in favor of using `SetRegistry` to set the registry of an encoder.\n\n#### SetRegistry\n\nThe signature of `SetRegistry` has been updated without an error being returned.\n\n#### Reset\n\nThe signature of `Reset` has been updated without an error being returned.\n\n### RawArray Type\n\nA new `RawArray` type has been added to the `bson` package as a primitive type to ​​represent a BSON array. Correspondingly, `RawValue.Array()` returns a `RawArray` instead of `Raw`.\n\n### ValueMarshaler\n\nThe `MarshalBSONValue` method of the [ValueMarshaler](https://pkg.go.dev/go.mongodb.org/mongo-driver/v2/bson#ValueMarshaler) interface now returns a `byte` value representing the [BSON type](https://pkg.go.dev/go.mongodb.org/mongo-driver/v2/bson#Type). That allows external packages to implement the `ValueMarshaler` interface without having to import the `bson` package. Convert a returned `byte` value to [bson.Type](https://pkg.go.dev/go.mongodb.org/mongo-driver/v2/bson#Type) to compare with the BSON type constants. For example:\n\n```go\nbtype, _, _ := m.MarshalBSONValue()\nfmt.Println(\"type of data: %s: \", bson.Type(btype))\nfmt.Println(\"type of data is an array: %v\", bson.Type(btype) == bson.TypeArray)\n```\n\n### ValueUnmarshaler\n\nThe `UnmarshalBSONValue` method of the [ValueUnmarshaler](https://pkg.go.dev/go.mongodb.org/mongo-driver/v2/bson#ValueUnmarshaler) interface now accepts a `byte` value representing the [BSON type](https://pkg.go.dev/go.mongodb.org/mongo-driver/v2/bson#Type) for the first argument. That allows packages to implement `ValueUnmarshaler` without having to import the `bson` package. For example:\n\n```go\nif err := m.UnmarshalBSONValue(bson.TypeEmbeddedDocument, bytes); err != nil {\n    log.Fatalf(\"failed to decode embedded document: %v\", err)\n}\n```\n"
  },
  {
    "path": "docs/pull_request_template.md",
    "content": "<!--- If applicable, issue number goes here, e.g. GODRIVER-ABCD -->\n\n## Summary\n\n<!--- A summary of the changes proposed by this pull request. -->\n\n## Background & Motivation\n\n<!--- Rationale for the pull request. -->\n"
  },
  {
    "path": "etc/api_report.sh",
    "content": "#!/usr/bin/env bash\n# api-report\n# Generates a report of Go Driver API changes for the current branch.\nset -eux\n\n# Skip the report of it isn't a PR run.\nif [ \"$BASE_SHA\" == \"$HEAD_SHA\" ]; then\n    echo \"Skipping API Report\"\n    exit 0\nfi\n\n# Ensure a clean checkout.\ngit checkout -b test-api-report\ngit add .\ngit commit -m \"local changes\"\n\n# Ensure gorelease is installed.\ncmd=$(command -v gorelease || true)\nif [ -z $cmd ]; then\n    go install golang.org/x/exp/cmd/gorelease@latest\nfi\n\n# Generate and parse the report.\ngorelease -base=$BASE_SHA > api-report.txt || true\ncat api-report.txt\ngo run ./internal/cmd/parse-api-report/main.go\nrm api-report.txt\n\n# Make the PR comment.\ntarget=$DRIVERS_TOOLS/.evergreen/github_app/create_or_modify_comment.sh\nbash $target -m \"## API Change Report\" -c \"$(pwd)/api-report.md\" -h $HEAD_SHA -o \"mongodb\" -n \"mongo-go-driver\"\n"
  },
  {
    "path": "etc/check_fmt.sh",
    "content": "#!/usr/bin/env bash\n# check_fmt\n# Runs gofumpt on all packages in the repo and checks that *_example_test.go files have wrapped lines.\n\n# Check if gofumpt is installed\nif ! command -v gofumpt &> /dev/null; then\n  echo \"gofumpt is not installed. Install it with: go install mvdan.cc/gofumpt@latest\"\n  exit 1\nfi\n\ngofumpt_out=\"$(gofumpt -l .)\"\n\nif [[ $gofumpt_out ]]; then\n  echo \"gofumpt check failed for:\";\n  sed -e 's/^/ - /' <<< \"$gofumpt_out\";\n  exit 1;\nfi\n\n# Use the \"github.com/walle/lll\" tool to check that all lines in *_example_test.go files are\n# wrapped at 80 characters to keep them readable when rendered on https://pkg.go.dev.\n# Ignore long lines that are comments containing URI-like strings and testable example output\n# comments like \"// Output: ...\".\n# E.g ignored lines:\n#     // \"mongodb://ldap-user:ldap-pwd@localhost:27017/?authMechanism=PLAIN\"\n#     // (https://www.mongodb.com/docs/manual/core/authentication-mechanisms-enterprise/#security-auth-ldap).\n#     // Output: {\"myint\": {\"$numberLong\":\"1\"},\"int32\": {\"$numberLong\":\"1\"},\"int64\": {\"$numberLong\":\"1\"}}\nlll_out=\"$(find . -type f -name \"*_examples_test.go\" | lll -w 4 -l 80 -e '^\\s*\\/\\/(.+:\\/\\/| Output:)' --files)\"\n\nif [[ $lll_out ]]; then\n  echo \"lll check failed for:\";\n  sed -e 's/^/ - /' <<< \"$lll_out\";\n  exit 1;\nfi\n"
  },
  {
    "path": "etc/check_license.sh",
    "content": "#!/usr/bin/env bash\n\nyear=$(date +\"%Y\")\ncopyright=$\"// Copyright (C) MongoDB, Inc. $year-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \\\"License\\\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\"\n\ncheck_or_add_copyright() {\n    file=$1\n\n    # Check if first 24 bytes match first 24 bytes of copyright notice.\n    local line\n    line=$(head -c 24 $file)\n    if [ \"$line\" == \"// Copyright (C) MongoDB\" ]; then\n        if [ ! -z \"$verbose\" ]; then\n            echo \"$file already has copyright notice\" >&2\n        fi\n        return\n    fi\n\n    # Check if first 14 bytes matches the prefix \"// Copied from\"\n    line=$(head -c 14 $file)\n    if [ \"$line\" == \"// Copied from\" ]; then\n        if [ ! -z \"$verbose\" ]; then\n            echo \"$file has a third-party copyright notice\" >&2\n        fi\n        return\n    fi\n\n    if [ ! -z \"$add\" ]; then\n        echo \"$copyright\" | cat - $file > temp && mv temp $file\n        return\n    fi\n\n    echo \"Missing copyright notice in \\\"$file\\\". Run \\\"task add-license\\\" to add missing licenses.\"\n    exit 1\n}\n\n# Options are:\n# -a : Add licenses that are missing.\n# -v : Verbose. Print all files as they're checked.\nwhile getopts at:vt: flag\ndo\n    case \"${flag}\" in\n        a) add=1;;\n        v) verbose=1;;\n        *)\n            echo \"flag not recognized\"\n            exit 1\n            ;;\n    esac\ndone\n\n# Shift script arguments so $1 contains only optional filename args.\n# E.g. check_license.sh -a ./mongo/cursor.go\nshift \"$((OPTIND - 1))\"\nFILES=$1\n\n# If no filenames were passed, find all .go files and try to write a license\n# notice.\nif [ -z \"$FILES\" ]; then\n    FILES=$(find . -type f -name \"*.go\" -print)\nfi\n\nfor file in $FILES\ndo\n    # Skip any files that aren't .go files.\n    if [[ ! \"$file\" =~ .go$ ]]; then\n        continue\n    fi\n\n    check_or_add_copyright \"$file\"\ndone\n"
  },
  {
    "path": "etc/check_modules.sh",
    "content": "#!/usr/bin/env bash\n# check-modules runs \"go mod tidy\" on each module and exits with a non-zero exit code if there\n# are any module changes. The intent is to confirm that exactly the required\n# modules are declared as dependencies. We should always be able to run \"go mod\n# tidy\" and expect that no unrelated changes are made to the \"go.mod\" file.\nset -eu\n\nmods=$(find . -name go.mod)\nexit_code=0\nfor mod in $mods; do\n  pushd \"$(dirname $mod)\" > /dev/null\n  echo \"Checking $mod...\"\n  go mod tidy -v\n  git diff --exit-code go.mod go.sum || {\n    exit_code=$?\n  }\n  echo \"Checking $mod... done\"\n  popd > /dev/null\ndone\nexit $exit_code\n"
  },
  {
    "path": "etc/cherry-picker.sh",
    "content": "#!/bin/bash\nset -e\n\nsha=$1\ntarget=${2:-v1}\ndirname=$(mktemp -d)\nuser=$(git config github.user)\n\nif [ -z \"$user\" ]; then\n    echo \"Please set GitHub User\"\n    echo \"git config --global github.user <github_handle>\"\n    exit 1\nfi\n\nmkdir -p $dirname\nif [ -z \"$AUTH_TOKEN\" ]; then\n    git clone git@github.com:mongodb/mongo-go-driver.git $dirname\nelse\n    echo \"$AUTH_TOKEN\" > mytoken.txt\n    gh auth login --with-token < mytoken.txt\n    git clone https://github.com/mongodb/mongo-go-driver.git $dirname\nfi\n\ncd $dirname\nif [ -z \"$AUTH_TOKEN\" ]; then\n    git remote add $user git@github.com:$user/mongo-go-driver.git\nelse\n    git remote add $user https://$user:${AUTH_TOKEN}@github.com/$user/mongo-go-driver.git\nfi\n\ngh repo set-default mongodb/mongo-go-driver\nbranch=\"cherry-pick-$sha\"\nhead=\"$user:$branch\"\ngit fetch origin\ngit checkout -b $branch origin/$target\ngit cherry-pick -x $sha || true\n\nfiles=$(git ls-files -m)\nif [ -n \"${files}\" ]; then\n    EDITOR=${EDITOR:-$(git config core.editor)}\n    EDITOR=${EDITOR:-vim}\n    for fname in $files; do\n        echo \"Fixing $fname...\"\n        $EDITOR $fname\n        git add $fname\n    done\n    echo \"Finishing cherry pick.\"\n    git cherry-pick --continue\nfi\n\nold_title=$(git --no-pager log  -1 --pretty=%B | head -n 1)\nticket=$(echo $old_title | sed -r 's/([A-Z]+-[0-9]+).*/\\1/')\ntext=$(echo $old_title | sed -r 's/([A-Z]+-[0-9]+) (.*) \\(#[0-9]*\\)/\\2/')\npr_number=$(echo $old_title| sed -r 's/.*(#[0-9]*)\\)/\\1/')\n\ntitle=\"$ticket [$target] $text\"\nbody=\"Cherry-pick of $pr_number to $target\"\n\necho\necho \"Creating PR...\"\necho \"Title: $title\"\necho \"Body: $body\"\necho \"Base: $target\"\necho \"Head: $head\"\necho\n\nif [ -n \"$GITHUB_ACTOR\" ]; then\n    choice=Y\nelse\n    read -p 'Push changes? (Y/n) ' choice\nfi\n\nif [[ \"$choice\" == \"Y\" || \"$choice\" == \"y\" || -z \"$choice\" ]]; then\n    if [ -n \"$user\" ]; then\n        git push $user\n    fi\n    gh pr create --title \"$title\" --base $target --head $head --body \"$body\"\nfi\n"
  },
  {
    "path": "etc/docker_entry.sh",
    "content": "#!/usr/bin/env bash\n#\n# Entry point for Dockerfile for running a go test.\n#\nset -eux\n\n# Prep files.\ncd /src\nrm -f test.suite\ncp -r $HOME/install ./install\n\nexport PATH=\"$MONGODB_BINARIES:$HOME/go/bin:$PATH\"\n\ntask setup-test\n\n# Run the test.\ntask $TASKFILE_TARGET\n"
  },
  {
    "path": "etc/gen-ec-certs/client.ext",
    "content": "basicConstraints = CA: FALSE\nsubjectAltName = DNS: localhost, IP: 127.0.0.1\nextendedKeyUsage = clientAuth\n"
  },
  {
    "path": "etc/gen-ec-certs/empty.cnf",
    "content": "# A nearly empty OpenSSL CA configuration file.\n# `openssl req` complains without a configuration file.\n[ req ]\ndistinguished_name = distinguished_name\n\n[ distinguished_name ]\n"
  },
  {
    "path": "etc/gen-ec-certs/gen-ec-certs.sh",
    "content": "#!/usr/bin/env bash\n# This script is used to generate Elliptic Curve (EC) certificates.\n# The EC certificates are used for testing the Go driver with PyKMIP.\n# PyKMIP does not support Go's default TLS cipher suites with RSA.\n# See: GODRIVER-2239.\nset -euo pipefail\nCA_SERIAL=$RANDOM\nSERVER_SERIAL=$RANDOM\nCLIENT_SERIAL=$RANDOM\nDAYS=14600\n\n# Generate CA certificate ... begin\n# Generate an EC private key.\nopenssl ecparam -name prime256v1 -genkey -out ca-ec.key -noout\n# Generate a certificate signing request.\nopenssl req -new -key ca-ec.key -out ca-ec.csr -subj \"/C=US/ST=New York/L=New York City/O=MongoDB/OU=DBX/CN=ca/\" -config empty.cnf -sha256\n# Self-sign the request.\nopenssl x509 -in ca-ec.csr -out ca-ec.pem -req -signkey ca-ec.key -days $DAYS -sha256 -set_serial $CA_SERIAL\n# Generate CA certificate ... end\n\n# Generate Server certificate ... begin\n# Generate an EC private key.\nopenssl ecparam -name prime256v1 -genkey -out server-ec.key -noout\n# Generate a certificate signing request.\nopenssl req -new -key server-ec.key -out server-ec.csr -subj \"/C=US/ST=New York/L=New York City/O=MongoDB/OU=DBX/CN=server/\" -config empty.cnf -sha256\n# Sign the request with the CA. Add server extensions.\nopenssl x509 -in server-ec.csr -out server-ec.pem -req -CA ca-ec.pem -CAkey ca-ec.key -days $DAYS -sha256 -set_serial $SERVER_SERIAL -extfile server.ext\n# Append private key to .pem file.\ncat server-ec.key >> server-ec.pem\n# Generate Server certificate ... end\n\n# Generate Client certificate ... begin\n# Generate an EC private key.\nopenssl ecparam -name prime256v1 -genkey -out client-ec.key -noout\n# Generate a certificate signing request.\n# Use the Common Name (CN) of \"client\". PyKMIP identifies the client by the CN. The test server expects the identity of \"client\".\nopenssl req -new -key client-ec.key -out client-ec.csr -subj \"/C=US/ST=New York/L=New York City/O=MongoDB/OU=DBX/CN=client/\" -config empty.cnf -sha256\n# Sign the request with the CA. Add client extensions.\nopenssl x509 -in client-ec.csr -out client-ec.pem -req -CA ca-ec.pem -CAkey ca-ec.key -days $DAYS -sha256 -set_serial $CLIENT_SERIAL -extfile client.ext\n# Append private key to .pem file.\ncat client-ec.key >> client-ec.pem\n# Generate Client certificate ... end\n\n# Clean-up.\nrm *.csr\nrm *.key\n"
  },
  {
    "path": "etc/gen-ec-certs/server.ext",
    "content": "basicConstraints = CA: FALSE\nsubjectAltName = DNS: localhost, IP: 127.0.0.1\nextendedKeyUsage = serverAuth\n"
  },
  {
    "path": "etc/generate_notices.pl",
    "content": "#!/usr/bin/env perl\nuse v5.10;\nuse strict;\nuse warnings;\nuse utf8;\nuse open qw/:std :utf8/;\nuse File::Find qw/find/;\n\nmy @license_files;\n\nfind(\n    sub {\n        return unless lc($_) eq 'license';\n        push @license_files, [ $File::Find::dir, $File::Find::name ];\n    },\n    'vendor'\n);\n\nprint forked_licenses();\n\nfor my $entry (sort { $a->[0] cmp $b->[0] } @license_files) {\n    ( my $package_name = $entry->[0] ) =~ s{vendor/}{};\n    my $license_text = do { local ( @ARGV, $/ ) = $entry->[1]; <> };\n    $license_text =~ s/ +$//mg;\n    say \"\" x 70;\n    say \"-\" x 70;\n    say \"License notice for $package_name\";\n    say \"-\" x 70;\n    say \"\";\n    print $license_text;\n}\n\n# These licenses are the originals for forked code; they must\n# be included along with license from the vendor directory\nsub forked_licenses {\n    return <<'HERE';\n----------------------------------------------------------------------\nLicense notice for AWS V4 signing code from github.com/aws/aws-sdk-go\nAWS SDK for Go\nCopyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.\nCopyright 2014-2015 Stripe, Inc.\n----------------------------------------------------------------------\n\n\n                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n---------------------------------------------------------------------\nLicense notice for gopkg.in/mgo.v2/bson\n---------------------------------------------------------------------\n\nBSON library for Go\n\nCopyright (c) 2010-2013 - Gustavo Niemeyer <gustavo@niemeyer.net>\n\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n1. Redistributions of source code must retain the above copyright notice, this\n   list of conditions and the following disclaimer.\n2. Redistributions in binary form must reproduce the above copyright notice,\n   this list of conditions and the following disclaimer in the documentation\n   and/or other materials provided with the distribution.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\nANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\nWARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\nANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\nLOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\nON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\nSOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n---------------------------------------------------------------------\nLicense notice for JSON and CSV code from github.com/golang/go\n---------------------------------------------------------------------\n\nCopyright (c) 2009 The Go Authors. All rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n   * Redistributions of source code must retain the above copyright\nnotice, this list of conditions and the following disclaimer.\n   * Redistributions in binary form must reproduce the above\ncopyright notice, this list of conditions and the following disclaimer\nin the documentation and/or other materials provided with the\ndistribution.\n   * Neither the name of Google Inc. nor the names of its\ncontributors may be used to endorse or promote products derived from\nthis software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\nLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\nA PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\nOWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\nLIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\nDATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\nTHEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\nHERE\n}\n"
  },
  {
    "path": "etc/golangci-lint.sh",
    "content": "#!/usr/bin/env bash\nset -ex\n\n# Keep this in sync with go version used in static-analysis Evergreen build variant.\nGO_VERSION=1.25.0\nGOLANGCI_LINT_VERSION=2.8.0\n\n# Unset the cross-compiler overrides while downloading binaries.\nGOOS_ORIG=${GOOS:-}\nexport GOOS=\nGOARCH_ORIG=${GOARCH:-}\nexport GOARCH=\n\ngo install golang.org/dl/go$GO_VERSION@latest\ngo${GO_VERSION} download\nGOROOT=\"$(go${GO_VERSION} env GOROOT)\"\nPATH=\"$GOROOT/bin:$PATH\"\nexport PATH\nexport GOROOT\ncurl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/HEAD/install.sh | sh -s -- -b \"$(go env GOPATH)\"/bin v${GOLANGCI_LINT_VERSION}\n\nexport GOOS=$GOOS_ORIG\nexport GOARCH=$GOARCH_ORIG\ngolangci-lint run --config .golangci.yml ./...\n"
  },
  {
    "path": "etc/govulncheck.sh",
    "content": "#!/usr/bin/env bash\nset -ex\n\n# Use a specific Go version so that local govulncheck results are consistent\n# with CI results.\n#\n# Note: this needs to be updated if the listed Go version has vulnerabilities\n# discovered because they will show up in the scan results along with Go Driver\n# and dependency vulnerabilities.\nVERSION_REGEX='1\\\\.25\\\\.[0-9]+'\nJQ_FILTER=\"first(.[] \\\n| select(.stable == true and (.version | test(\\\"^go${VERSION_REGEX}$\\\"))) \\\n| .version)\"\nGO_VERSION=$(curl -sSfL 'https://golang.org/dl/?mode=json' | jq -r \"$JQ_FILTER\")\n\ngo install golang.org/dl/$GO_VERSION@latest\n${GO_VERSION} download\ngo install golang.org/x/vuln/cmd/govulncheck@latest\n\n# govulncheck uses the Go binary it finds from the PATH, so modify PATH to point\n# to the Go version we just downloaded.\nPATH=\"$(${GO_VERSION} env GOROOT)/bin:$PATH\" govulncheck -show verbose ./...\n"
  },
  {
    "path": "etc/install-libmongocrypt.sh",
    "content": "#!/usr/bin/env bash\n# install libmongocrypt\n# This script installs libmongocrypt into an \"install\" directory.\nset -eux\n\nLIBMONGOCRYPT_TAG=\"1.15.1\"\n\n# Install libmongocrypt based on OS.\nif [ \"Windows_NT\" = \"${OS:-}\" ]; then\n    mkdir -p c:/libmongocrypt/include\n    mkdir -p c:/libmongocrypt/bin\n    echo \"fetching build for Windows ... begin\"\n    mkdir libmongocrypt-all\n    cd libmongocrypt-all\n    # The following URL is published from the upload-all task in the libmongocrypt Evergreen project.\n    curl -L https://github.com/mongodb/libmongocrypt/releases/download/$LIBMONGOCRYPT_TAG/libmongocrypt-windows-x86_64-$LIBMONGOCRYPT_TAG.tar.gz -o libmongocrypt-all.tar.gz\n    tar -xf libmongocrypt-all.tar.gz\n    cd ..\n    cp libmongocrypt-all/bin/mongocrypt.dll c:/libmongocrypt/bin\n    cp libmongocrypt-all/include/mongocrypt/*.h c:/libmongocrypt/include\n\n    rm -rf libmongocrypt-all\n    echo \"fetching build for Windows ... end\"\nelse\n    rm -rf libmongocrypt\n    git clone https://github.com/mongodb/libmongocrypt --depth=1 --branch $LIBMONGOCRYPT_TAG 2> /dev/null\n    if ! ( ./libmongocrypt/.evergreen/compile.sh >| output.txt 2>&1 ); then\n        cat output.txt 1>&2\n        exit 1\n    fi\n    mv output.txt install\n    rm -rf libmongocrypt\nfi\n"
  },
  {
    "path": "etc/perf-pr-comment.sh",
    "content": "#!/usr/bin/env bash\n# perf-pr-comment\n# Generates a report of Go Driver perf changes for the current branch.\n\nset -eux\n\npushd $DRIVERS_TOOLS/.evergreen >/dev/null\nPROJECT=\"mongo-go-driver\" CONTEXT=\"GoDriver perf task\" TASK=\"perf\" VARIANT=\"perf\" sh run-perf-comp.sh\n\nif [[ -n \"${BASE_SHA+set}\" && -n \"${HEAD_SHA+set}\" && \"$BASE_SHA\" != \"$HEAD_SHA\" ]]; then\n    # Make the PR comment.\n    target=github_app/create_or_modify_comment.sh\n    bash $target -m \"## 🧪 Performance Results\" -c \"$(pwd)/perfcomp/perf-report.md\" -h $HEAD_SHA -o \"mongodb\" -n \"mongo-go-driver\"\n    rm ./perfcomp/perf-report.md\nelse\n    # Skip comment if it isn't a PR run.\n    echo \"Skipping Perf PR comment\"\nfi\n\npopd >/dev/null\n"
  },
  {
    "path": "etc/pr-task.sh",
    "content": "#!/usr/bin/env bash\nset -eu\n\nVARLIST=(\n\tPR_TASK\n\tCOMMIT\n\tDRIVERS_TOOLS\n)\n\n# Ensure that all variables required to run the test are set, otherwise throw\n# an error.\nfor VARNAME in \"${VARLIST[@]}\"; do\n\t[[ -z \"${!VARNAME:-}\" ]] && echo \"ERROR: $VARNAME not set\" && exit 1;\ndone\n\ncase $PR_TASK in\n\tapply-labels)\n\t\tCONFIG=$(pwd)/.github/labeler.yml\n\t\tSCRIPT=\"${DRIVERS_TOOLS}/.evergreen/github_app/apply-labels.sh\"\n\t\tbash $SCRIPT -l $CONFIG -h $COMMIT -o \"mongodb\" -n \"mongo-go-driver\"\n\t\t;;\n\tassign-reviewer)\n\t\tCONFIG=$(pwd)/.github/reviewers.txt\n\t\tSCRIPT=\"${DRIVERS_TOOLS}/.evergreen/github_app/assign-reviewer.sh\"\n\t\tbash $SCRIPT -p $CONFIG -h $COMMIT -o \"mongodb\" -n \"mongo-go-driver\"\n\t\t;;\n\tbackport-pr)\n\t\tbash ${DRIVERS_TOOLS}/.evergreen/github_app/backport-pr.sh mongodb mongo-go-driver $COMMIT\n\t\t;;\nesac\n"
  },
  {
    "path": "etc/profile-test.sh",
    "content": "#!/usr/bin/env bash\n# Convenience wrapper for profiling and tracing Go tests.\nset -eu\n\n# Create a temporary file to store the profile or trace data. Trap the exit\n# signals to clean it up after the script exits (typically via Ctrl-C).\ntmpfile=$(mktemp)\ntrap 'rm -f \"$tmpfile\"' EXIT INT TERM\n\n# Set the prompt and options for the select menu.\nPS3=\"Choose a profile type: \"\noptions=(\"cpu\" \"mem\" \"block\" \"mutex\" \"trace\")\n\nselect choice in \"${options[@]}\"\ndo\n    case $choice in\n        \"cpu\" | \"mem\" | \"block\" | \"mutex\")\n            echo \"Writing $choice profile to $tmpfile\"\n\n            go test -${choice}profile ${tmpfile} \"$@\"\n            go tool pprof -http=: ${tmpfile}\n            break\n            ;;\n        \"trace\")\n            echo \"Writing trace to $tmpfile\"\n\n            go test -trace ${tmpfile} \"$@\"\n            go tool trace ${tmpfile}\n            break\n            ;;\n        *)\n            echo \"Invalid option. Please try again.\"\n            ;;\n    esac\ndone\n"
  },
  {
    "path": "etc/run-awskms-test.sh",
    "content": "#!/usr/bin/env bash\n# run-awskms-test\n# Runs the awskms test.\nset -eu\n\nGO_BUILD_TAGS=\"cse\" task setup-test\ntask build-kms-test\n\n. ${DRIVERS_TOOLS}/.evergreen/secrets_handling/setup-secrets.sh drivers/atlas_connect\nexport MONGODB_URI=\"$ATLAS_FREE\"\n\nif [ -z \"${EXPECT_ERROR:-}\" ]; then\n  . ${DRIVERS_TOOLS}/.evergreen/csfle/setup-secrets.sh\n  export AWS_SECRET_ACCESS_KEY=$FLE_AWS_SECRET_ACCESS_KEY\n  export AWS_ACCESS_KEY_ID=$FLE_AWS_ACCESS_KEY_ID\nfi\n\n# AWS_SESSION_TOKEN is required to get credentials from the drivers/csfle vault\n# but interferes with the testkms binary causing UnrecognizedClientException.\nunset AWS_SESSION_TOKEN\n\nLD_LIBRARY_PATH=./install/libmongocrypt/lib64 PROVIDER='aws' ./testkms\n"
  },
  {
    "path": "etc/run-azurekms-test.sh",
    "content": "#!/usr/bin/env bash\n# run-gcpkms-test\n# Runs gcpkms tests.\nset -eu\n\nGO_BUILD_TAGS=\"cse\" task setup-test\ntask build-kms-test\n\nif [ -n \"${EXPECT_ERROR:-}\" ]; then\n  . ${DRIVERS_TOOLS}/.evergreen/csfle/azurekms/setup-secrets.sh\n  LD_LIBRARY_PATH=./install/libmongocrypt/lib64 \\\n  MONGODB_URI='mongodb://localhost:27017' \\\n  EXPECT_ERROR='unable to retrieve azure credentials' \\\n  PROVIDER='azure' AZUREKMS_KEY_NAME=$AZUREKMS_KEYNAME AZUREKMS_KEY_VAULT_ENDPOINT=$AZUREKMS_KEYVAULTENDPOINT \\\n    ./testkms\n  exit 0\nfi\n\necho \"Copying files ... begin\"\nsource ${DRIVERS_TOOLS}/.evergreen/csfle/azurekms/secrets-export.sh\ntar czf testazurekms.tgz ./testkms ./install/libmongocrypt/lib64/libmongocrypt.*\nAZUREKMS_SRC=testazurekms.tgz AZUREKMS_DST=/tmp ${DRIVERS_TOOLS}/.evergreen/csfle/azurekms/copy-file.sh\necho \"Copying files ... end\"\necho \"Untarring file ... begin\"\nAZUREKMS_CMD=\"tar xf /tmp/testazurekms.tgz\" ${DRIVERS_TOOLS}/.evergreen/csfle/azurekms/run-command.sh\necho \"Untarring file ... end\"\n\nAZUREKMS_CMD=\"LD_LIBRARY_PATH=./install/libmongocrypt/lib64 MONGODB_URI='mongodb://localhost:27017' PROVIDER='azure' AZUREKMS_KEY_NAME=$AZUREKMS_KEYNAME AZUREKMS_KEY_VAULT_ENDPOINT=$AZUREKMS_KEYVAULTENDPOINT ./testkms\" ${DRIVERS_TOOLS}/.evergreen/csfle/azurekms/run-command.sh\n"
  },
  {
    "path": "etc/run-compile-check-test.sh",
    "content": "#!/usr/bin/env bash\n# run-compile-check-test\n# Run compile check tests for all supported Go versions.\nset -eu\nset +x\n\necho \"Running internal/test/compilecheck\"\npushd internal/test/compilecheck\ngo test -timeout 30m -v ./... >>../../../test.suite\npopd\n"
  },
  {
    "path": "etc/run-fuzz.sh",
    "content": "#!/bin/bash\n\nset -o errexit  # Exit the script with error if any of the commands fail\n\n# Default fuzztime to 10m.\nFUZZTIME=${FUZZTIME:-10m}\n\nif [ -z \"$PROJECT_DIRECTORY\" ]; then\n\techo \"Please set PROJECT_DIRECTORY env variable.\"\n    exit 1\nfi\n\n# Change the working directory to the root of the mongo repository directory\ncd $PROJECT_DIRECTORY\n\n# Get all go test files that contain a fuzz test.\nFILES=$(grep -r --include='**_test.go' --files-with-matches 'func Fuzz' .)\n\n# For each file, run all of the fuzz tests in sequence, each for -fuzztime=FUZZTIME.\nfor FILE in ${FILES}\ndo\n\tPARENTDIR=\"$(dirname -- \"$FILE\")\"\n\n\t# Get a list of all fuzz tests in the file.\n\tFUNCS=$(grep -o 'func Fuzz[A-Za-z0-9]*' $FILE | cut -d' ' -f2)\n\n\t# For each fuzz test in the file, run it for FUZZTIME.\n\tfor FUNC in ${FUNCS}\n\tdo\n\t\techo \"Fuzzing \\\"${FUNC}\\\" in \\\"${FILE}\\\" for ${FUZZTIME}\"\n\n\t\t# Create a set of directories that are already in the subdirectories testdata/fuzz/$fuzzer corpus. This\n\t\t# set will be used to differentiate between new and old corpus files.\n\t\tdeclare -a cset\n\n\t\tif [ -d $PARENTDIR/testdata/fuzz/$FUNC ]; then\n\t\t\t# Iterate over the files in the corpus directory and add them to the set.\n\t\t\tfor SEED in $PARENTDIR/testdata/fuzz/$FUNC/*\n\t\t\tdo\n\t\t\t\tcset+=(\"$SEED\")\n\t\t\tdone\n\t\tfi\n\n\t\tGOMAXPROCS=2 go test -race ${PARENTDIR} -run=${FUNC} -fuzz=^${FUNC}\\$ -fuzztime=${FUZZTIME} || true\n\n\t\t# Check if any new corpus files were generated for the fuzzer. If there are new corpus files, move them\n\t\t# to $PROJECT_DIRECTORY/fuzz/$FUNC/* so they can be tarred up and uploaded to S3.\n\t\tif [ -d $PARENTDIR/testdata/fuzz/$FUNC ]; then\n\t\t\t# Iterate over the files in the corpus directory and check if they are in the set.\n\t\t\tfor CORPUS_FILE in $PARENTDIR/testdata/fuzz/$FUNC/*\n\t\t\tdo\n\t\t\t\t# Check to see if the value for CORPUS_FILE is in cset.\n\t\t\t\t# shellcheck disable=SC2076\n\t\t\t\tif [[ ! \" ${cset[*]} \" =~ \" ${CORPUS_FILE} \" ]]; then\n\t\t\t\t\t# Create the directory if it doesn't exist.\n\t\t\t\t\tif [ ! -d $PROJECT_DIRECTORY/fuzz/$FUNC ]; then\n\t\t\t\t\t\tmkdir -p $PROJECT_DIRECTORY/fuzz/$FUNC\n\t\t\t\t\tfi\n\n\t\t\t\t\t# Move the file to the directory.\n\t\t\t\t\tmv $CORPUS_FILE $PROJECT_DIRECTORY/fuzz/$FUNC\n\n\t\t\t\t\techo \"Moved $CORPUS_FILE to $PROJECT_DIRECTORY/fuzz/$FUNC\"\n\t\t\t\tfi\n\t\t\tdone\n\t\tfi\n\tdone\ndone\n\n# If the fuzz directory exists, then tar it up in preparation to upload to S3.\nif [ -d $PROJECT_DIRECTORY/fuzz ]; then\n\techo \"Tarring up fuzz directory\"\n\n  \tcd $PROJECT_DIRECTORY\n  \ttar cfz fuzz.tgz ./fuzz\n\n\t# Exit with code 1 to indicate that errors occurred in fuzz tests, resulting in corpus files being generated.\n\t# This will trigger a notification to be sent to the Go Driver team.\n\texit 1\nfi\n"
  },
  {
    "path": "etc/run-gcpkms-test.sh",
    "content": "#!/usr/bin/env bash\n# run-gcpkms-test\n# Runs gcpkms tests.\nset -eu\n\nGO_BUILD_TAGS=\"cse\" task setup-test\ntask build-kms-test\n\nif [ -n \"${EXPECT_ERROR:-}\" ]; then\n  LD_LIBRARY_PATH=./install/libmongocrypt/lib64 \\\n  MONGODB_URI='mongodb://localhost:27017/' \\\n  EXPECT_ERROR='unable to retrieve GCP credentials' \\\n  PROVIDER='gcp' \\\n    ./testkms\n  exit 0\nfi\n\nsource ${DRIVERS_TOOLS}/.evergreen/csfle/gcpkms/secrets-export.sh\necho \"Copying files ... begin\"\ntar czf testgcpkms.tgz ./testkms ./install/libmongocrypt/lib64/libmongocrypt.*\nGCPKMS_SRC=testgcpkms.tgz GCPKMS_DST=$GCPKMS_INSTANCENAME: ${DRIVERS_TOOLS}/.evergreen/csfle/gcpkms/copy-file.sh\necho \"Copying files ... end\"\n\necho \"Untarring file ... begin\"\nGCPKMS_CMD=\"tar xf testgcpkms.tgz\" ${DRIVERS_TOOLS}/.evergreen/csfle/gcpkms/run-command.sh\necho \"Untarring file ... end\"\n\nGCPKMS_CMD=\"LD_LIBRARY_PATH=./install/libmongocrypt/lib64 MONGODB_URI='mongodb://localhost:27017' PROVIDER='gcp' ./testkms\" ${DRIVERS_TOOLS}/.evergreen/csfle/gcpkms/run-command.sh\n"
  },
  {
    "path": "etc/run-goleak-test.sh",
    "content": "#!/usr/bin/env bash\n# run-goleak-test\n# Run goroutine leak tests.\nset -eu\nset +x\n\necho \"Running internal/test/goleak\"\npushd internal/test/goleak\ngo test -v ./... >> ../../../test.suite\npopd\n"
  },
  {
    "path": "etc/run-mongodb-aws-ecs-test.sh",
    "content": "#!/bin/bash\nset -eu\n\nif [ \"${SKIP_ECS_AUTH_TEST:-}\" = \"true\" ]; then\n  echo \"This platform does not support the ECS auth test, skipping...\"\n  exit 0\nfi\n\ntask build-aws-ecs-test\n\nAUTH_AWS_DIR=${DRIVERS_TOOLS}/.evergreen/auth_aws\nECS_SRC_DIR=$AUTH_AWS_DIR/src\n\n# pack up project directory to ssh it to the container\nmkdir -p $ECS_SRC_DIR/.evergreen\ncp ${PROJECT_DIRECTORY}/aws.testbin $ECS_SRC_DIR/main\ncp ${PROJECT_DIRECTORY}/.evergreen/run-mongodb-aws-ecs-test.sh $ECS_SRC_DIR/.evergreen\ntar -czf $ECS_SRC_DIR/src.tgz -C ${PROJECT_DIRECTORY} .\n\nexport PROJECT_DIRECTORY=\"$ECS_SRC_DIR\"\n$AUTH_AWS_DIR/aws_setup.sh ecs\n"
  },
  {
    "path": "etc/run-mongodb-aws-test.sh",
    "content": "#!/bin/bash\n\nset -eu\n\n############################################\n#            Main Program                  #\n############################################\n\n# Supported/used environment variables:\n#  MONGODB_URI    Set the URI, including an optional username/password to use\n#                 to connect to the server via MONGODB-AWS authentication\n#                 mechanism.\n\necho \"Running MONGODB-AWS authentication tests\"\n\nif [ \"$AWS_TEST\" == \"ec2\" ] && [ \"${SKIP_EC2_AUTH_TEST:-}\" == \"true\" ]; then\n  echo \"This platform does not support the EC2 auth test, skipping...\"\n  exit 0\nfi\n\nif [ \"$AWS_TEST\" == \"web-identity\" ] && [ \"${SKIP_WEB_IDENTITY_AUTH_TEST:-}\" == \"true\" ]; then\n  echo \"This platform does not support the web identity auth test, skipping...\"\n  exit 0\nfi\n\n# Handle credentials and environment setup.\n. $DRIVERS_TOOLS/.evergreen/auth_aws/aws_setup.sh $AWS_TEST\n\n# show test output\nset -x\n\n# For Go 1.16+, Go builds requires a go.mod file in the current working directory or a parent\n# directory. Spawn a new subshell, \"cd\" to the project directory, then run \"go run\".\n(cd ${PROJECT_DIRECTORY} && go test -timeout 30m -v ./internal/test/aws/... | tee -a test.suite)\n"
  },
  {
    "path": "etc/run-oidc-remote-test.sh",
    "content": "#!/usr/bin/env bash\n# run-oidc-test\n# Runs oidc auth tests.\nset -eu\n\necho \"Running remote MONGODB-OIDC authentication tests on $OIDC_ENV\"\n\nDRIVERS_TAR_FILE=/tmp/mongo-go-driver.tar.gz\n# we need to statically link libc to avoid the situation where the VM has a different\n# version of libc\ngo test -c -tags osusergo,netgo -ldflags '-w -extldflags \"-static -lgcc -lc\"' -o test ./internal/test/oidcauth\nrm \"$DRIVERS_TAR_FILE\" || true\ntar -cf $DRIVERS_TAR_FILE ./test\ntar -uf $DRIVERS_TAR_FILE ./etc\nrm \"$DRIVERS_TAR_FILE\".gz || true\ngzip $DRIVERS_TAR_FILE\n\nif [ $OIDC_ENV == \"azure\" ]; then\n    export AZUREOIDC_DRIVERS_TAR_FILE=$DRIVERS_TAR_FILE\n    # Define the command to run on the azure VM.\n    # Ensure that we source the environment file created for us, set up any other variables we need,\n    # and then run our test suite on the vm.\n    export AZUREOIDC_TEST_CMD=\"PROJECT_DIRECTORY='.' OIDC_ENV=azure OIDC=oidc ./etc/run-oidc-test.sh ./test -test.v\"\n    bash ${DRIVERS_TOOLS}/.evergreen/auth_oidc/azure/run-driver-test.sh >> test.suite\n\nelif [ $OIDC_ENV == \"gcp\" ]; then\n    export GCPOIDC_DRIVERS_TAR_FILE=$DRIVERS_TAR_FILE\n    # Define the command to run on the gcp VM.\n    # Ensure that we source the environment file created for us, set up any other variables we need,\n    # and then run our test suite on the vm.\n    export GCPOIDC_TEST_CMD=\"PROJECT_DIRECTORY='.' OIDC_ENV=gcp OIDC=oidc ./etc/run-oidc-test.sh ./test -test.v\"\n    bash ${DRIVERS_TOOLS}/.evergreen/auth_oidc/gcp/run-driver-test.sh >> test.suite\n\nelif [ $OIDC_ENV == \"k8s\" ]; then\n    export K8S_VARIANT=${VARIANT}\n    export K8S_DRIVERS_TAR_FILE=$DRIVERS_TAR_FILE\n    export K8S_TEST_CMD=\"PROJECT_DIRECTORY='.' OIDC_ENV=k8s OIDC=oidc ./etc/run-oidc-test.sh ./test -test.v\"\n    bash ${DRIVERS_TOOLS}/.evergreen/auth_oidc/k8s/setup-pod.sh\n    bash ${DRIVERS_TOOLS}/.evergreen/auth_oidc/k8s/run-driver-test.sh >> test.suite\n    bash ${DRIVERS_TOOLS}/.evergreen/auth_oidc/k8s/teardown-pod.sh\n\nelse\n    echo \"Unrecognized OIDC_ENV $OIDC_ENV\"\n    exit 1\nfi\n"
  },
  {
    "path": "etc/run-oidc-test.sh",
    "content": "#!/usr/bin/env bash\n# run-oidc-test\n# Runs oidc auth tests.\nset -eu\n\necho \"Running MONGODB-OIDC authentication tests\"\n\nOIDC_ENV=\"${OIDC_ENV:-\"test\"}\"\n\nif [ $OIDC_ENV == \"test\" ]; then\n    # Make sure DRIVERS_TOOLS is set.\n    if [ -z \"$DRIVERS_TOOLS\" ]; then\n        echo \"Must specify DRIVERS_TOOLS\"\n        exit 1\n    fi\n    source ${DRIVERS_TOOLS}/.evergreen/auth_oidc/secrets-export.sh\n\nelif [ $OIDC_ENV == \"azure\" ]; then\n    source ./env.sh\n\nelif [ $OIDC_ENV == \"gcp\" ]; then\n    source ./secrets-export.sh\n\nelif [ $OIDC_ENV == \"k8s\" ]; then\n    # \"run-driver-test.sh\" in drivers-evergreen-tools takes care of sourcing\n    # \"secrets-export.sh\". Nothing to do in this block, but we still need a\n    # command to be syntactically valid, so use no-op command \":\".\n    :\n\nelse\n    echo \"Unrecognized OIDC_ENV $OIDC_ENV\"\n    exit 1\nfi\n\nexport TEST_AUTH_OIDC=1\nexport COVERAGE=1\nexport AUTH=\"auth\"\n\n# In the test environment, run the tests directly\n# In other environments, run whatever command was passed as arguments\nif [ $OIDC_ENV == \"test\" ]; then\n    go test -v ./internal/test/oidcauth/... >> test.suite\n    go test -v -race ./internal/test/oidcauth/... >> test.suite\nelse\n    # For non-test environments (azure, gcp, k8s), execute passed command if provided\n    if [ $# -gt 0 ]; then\n        \"$@\"\n    fi\nfi\n"
  },
  {
    "path": "etc/run_docker.sh",
    "content": "#!/usr/bin/env bash\n#\n# Script to run a test suite in docker locally\nset -eux\n\nif [ -z \"$DRIVERS_TOOLS\" ]; then\n    echo \"Please set DRIVERS_TOOLS env variable.\"\n    exit 1\nfi\nPLATFORM=${DOCKER_PLATFORM:-}\ndocker build $PLATFORM -t go-test .\n\n# Handle environment variables and optional positional arg for the taskfile target.\nTASKFILE_TARGET=${TASKFILE_TARGET:-$1}\nTASKFILE_TARGET=${TASKFILE_TARGET:-evg-test-versioned-api}\nGO_BUILD_TAGS=${GO_BUILD_TAGS:-\"\"}\n\nARGS=\" -e TASKFILE_TARGET=$TASKFILE_TARGET\"\nARGS=\"$ARGS -e GO_BUILD_TAGS=$GO_BUILD_TAGS\"\nARGS=\"$ARGS go-test\"\n\n$DRIVERS_TOOLS/.evergreen/docker/run-client.sh $ARGS\nif [ -f \"test.suite\" ]; then\n    tail test.suite\nfi\n"
  },
  {
    "path": "etc/setup-encryption.sh",
    "content": "#!/usr/bin/env bash\n#\n# Script to set up encryption assets and servers.\nset -eux\n\nif [ -z \"$DRIVERS_TOOLS\" ]; then\n    echo \"Please define DRIVERS_TOOLS variable\"\n    exit 1\nfi\n\nSCRIPT_DIR=$( cd -- \"$( dirname -- \"${BASH_SOURCE[0]}\" )\" &> /dev/null && pwd )\nPARENT_DIR=$(dirname $SCRIPT_DIR)\n\n# Handle the secrets\nexport CSFLE_TLS_CA_FILE=\"${PARENT_DIR}/testdata/kmip-certs/ca-ec.pem\"\nexport CSFLE_TLS_CERT_FILE=\"${PARENT_DIR}/testdata/kmip-certs/server-ec.pem\"\nexport CSFLE_TLS_CLIENT_CERT_FILE=\"${PARENT_DIR}/testdata/kmip-certs/client-ec.pem\"\n\nbash $DRIVERS_TOOLS/.evergreen/csfle/setup-secrets.sh\nbash $DRIVERS_TOOLS/.evergreen/csfle/start-servers.sh\n"
  },
  {
    "path": "etc/setup-test.sh",
    "content": "#!/usr/bin/env bash\n#\n# Set up test environment and write .test.env file.\nset -eu\n\nOS=${OS:-\"\"}\nSSL=${SSL:-nossl}\nGO_BUILD_TAGS=${GO_BUILD_TAGS:-}\nRACE=${RACE:-}\nSERVERLESS=${SERVERLESS:-}\nLOAD_BALANCER=${LOAD_BALANCER:-}\nMONGODB_URI=${MONGODB_URI:-}\n\n# Handle special cases first.\nif [ -n \"${TEST_ENTERPRISE_AUTH:-}\" ]; then\n    . $DRIVERS_TOOLS/.evergreen/secrets_handling/setup-secrets.sh drivers/enterprise_auth\n    AUTH=\"auth\"\n    case $TEST_ENTERPRISE_AUTH in\n        plain)\n            MONGODB_URI=\"mongodb://${SASL_USER}:${SASL_PASS}@${SASL_HOST}:${SASL_PORT}/ldap?authMechanism=PLAIN\"\n            ;;\n        gssapi)\n            if [ \"Windows_NT\" = \"${OS:-}\" ]; then\n                MONGODB_URI=\"mongodb://${PRINCIPAL/@/%40}:${SASL_PASS}@${SASL_HOST}:${SASL_PORT}/kerberos?authMechanism=GSSAPI\"\n            else\n                echo ${KEYTAB_BASE64} | base64 -d > ${PROJECT_DIRECTORY}/.evergreen/drivers.keytab\n                mkdir -p ~/.krb5\n                cat .evergreen/krb5.config | tee -a ~/.krb5/config\n                kinit -k -t .evergreen/drivers.keytab -p \"${PRINCIPAL}\"\n                MONGODB_URI=\"mongodb://${PRINCIPAL/@/%40}@${SASL_HOST}:${SASL_PORT}/kerberos?authMechanism=GSSAPI\"\n            fi\n            ;;\n    esac\n    rm secrets-export.sh\nfi\n\nif [ -n \"${SERVERLESS}\" ]; then\n    . $DRIVERS_TOOLS/.evergreen/serverless/secrets-export.sh\n    MONGODB_URI=\"${SERVERLESS_URI}\"\n    AUTH=\"auth\"\nfi\n\nif [ -n \"${TEST_ATLAS_CONNECT:-}\" ]; then\n    . $DRIVERS_TOOLS/.evergreen/secrets_handling/setup-secrets.sh drivers/atlas_connect\nfi\n\nif [ -n \"${LOAD_BALANCER}\" ]; then\n    # Verify that the required LB URI expansions are set to ensure that the test runner can correctly connect to\n    # the LBs.\n    if [ -z \"${SINGLE_MONGOS_LB_URI}\" ]; then\n        echo \"SINGLE_MONGOS_LB_URI must be set for testing against LBs\"\n        exit 1\n    fi\n    if [ -z \"${MULTI_MONGOS_LB_URI}\" ]; then\n        echo \"MULTI_MONGOS_LB_URI must be set for testing against LBs\"\n        exit 1\n    fi\n    MONGODB_URI=\"${SINGLE_MONGOS_LB_URI}\"\nfi\n\nif [ -n \"${OCSP_ALGORITHM:-}\" ]; then\n    MONGO_GO_DRIVER_CA_FILE=\"${DRIVERS_TOOLS}/.evergreen/ocsp/${OCSP_ALGORITHM}/ca.pem\"\n    if [ \"Windows_NT\" = \"$OS\" ]; then\n        MONGO_GO_DRIVER_CA_FILE=$(cygpath -m $MONGO_GO_DRIVER_CA_FILE)\n    fi\nfi\n\n# Handle encryption.\nif [[ \"${GO_BUILD_TAGS}\" =~ cse ]]; then\n    # Install libmongocrypt if needed.\n    task install-libmongocrypt\n\n    # Handle libmongocrypt paths.\n    PKG_CONFIG_PATH=$(pwd)/install/libmongocrypt/lib64/pkgconfig\n    LD_LIBRARY_PATH=$(pwd)/install/libmongocrypt/lib64\n\n    if [ \"$(uname -s)\" = \"Darwin\" ]; then\n      PKG_CONFIG_PATH=$(pwd)/install/libmongocrypt/lib/pkgconfig\n      DYLD_FALLBACK_LIBRARY_PATH=$(pwd)/install/libmongocrypt/lib\n    fi\n\n    if [ \"${SKIP_CRYPT_SHARED_LIB:-''}\" = \"true\" ]; then\n        CRYPT_SHARED_LIB_PATH=\"\"\n        echo \"crypt_shared library is skipped\"\n    elif [ -z \"${CRYPT_SHARED_LIB_PATH:-}\" ]; then\n        echo \"crypt_shared library path is empty\"\n    else\n        echo \"crypt_shared library will be loaded from path: $CRYPT_SHARED_LIB_PATH\"\n    fi\nfi\n\n# Handle the build tags argument.\nif [ -n \"${GO_BUILD_TAGS}\" ]; then\n    BUILD_TAGS=\"${RACE} --tags=${GO_BUILD_TAGS}\"\nelse\n    BUILD_TAGS=\"${RACE}\"\nfi\n\n# Handle certificates.\nif [ \"$SSL\" != \"nossl\" ] && [ -z \"${SERVERLESS}\" ] && [ -z \"${OCSP_ALGORITHM:-}\" ]; then\n    MONGO_GO_DRIVER_CA_FILE=\"$DRIVERS_TOOLS/.evergreen/x509gen/ca.pem\"\n    MONGO_GO_DRIVER_KEY_FILE=\"$DRIVERS_TOOLS/.evergreen/x509gen/client.pem\"\n    MONGO_GO_DRIVER_PKCS8_ENCRYPTED_KEY_FILE=\"$DRIVERS_TOOLS/.evergreen/x509gen/client-pkcs8-encrypted.pem\"\n    MONGO_GO_DRIVER_PKCS8_UNENCRYPTED_KEY_FILE=\"$DRIVERS_TOOLS/.evergreen/x509gen/client-pkcs8-unencrypted.pem\"\n\n    if [ \"Windows_NT\" = \"$OS\" ]; then\n        MONGO_GO_DRIVER_CA_FILE=$(cygpath -m $MONGO_GO_DRIVER_CA_FILE)\n        MONGO_GO_DRIVER_KEY_FILE=$(cygpath -m $MONGO_GO_DRIVER_KEY_FILE)\n        MONGO_GO_DRIVER_PKCS8_ENCRYPTED_KEY_FILE=$(cygpath -m $MONGO_GO_DRIVER_PKCS8_ENCRYPTED_KEY_FILE)\n        MONGO_GO_DRIVER_PKCS8_UNENCRYPTED_KEY_FILE=$(cygpath -m $MONGO_GO_DRIVER_PKCS8_UNENCRYPTED_KEY_FILE)\n    fi\nfi\n\ncat <<EOT > .test.env\nAUTH=\"${AUTH:-}\"\nSSL=\"${SSL}\"\nMONGO_GO_DRIVER_CA_FILE=\"${MONGO_GO_DRIVER_CA_FILE:-}\"\nMONGO_GO_DRIVER_KEY_FILE=\"${MONGO_GO_DRIVER_KEY_FILE:-}\"\nMONGO_GO_DRIVER_PKCS8_ENCRYPTED_KEY_FILE=\"${MONGO_GO_DRIVER_PKCS8_ENCRYPTED_KEY_FILE:-}\"\nMONGO_GO_DRIVER_PKCS8_UNENCRYPTED_KEY_FILE=\"${MONGO_GO_DRIVER_PKCS8_UNENCRYPTED_KEY_FILE:-}\"\nTOPOLOGY=\"${TOPOLOGY:-}\"\nSERVERLESS=\"${SERVERLESS}\"\nREQUIRE_API_VERSION=\"${REQUIRE_API_VERSION:-}\"\nLOAD_BALANCER=\"${LOAD_BALANCER}\"\nMONGO_GO_DRIVER_COMPRESSOR=\"${MONGO_GO_DRIVER_COMPRESSOR:-}\"\nBUILD_TAGS=\"${BUILD_TAGS}\"\nCRYPT_SHARED_LIB_PATH=\"${CRYPT_SHARED_LIB_PATH:-}\"\nPKG_CONFIG_PATH=\"${PKG_CONFIG_PATH:-}\"\nLD_LIBRARY_PATH=\"${LD_LIBRARY_PATH:-}\"\nMACOS_LIBRARY_PATH=\"${DYLD_FALLBACK_LIBRARY_PATH:-}\"\nSKIP_CSOT_TESTS=${SKIP_CSOT_TESTS:-}\nEOT\n\nif [ -n \"${MONGODB_URI}\" ]; then\n    echo \"MONGODB_URI=\\\"${MONGODB_URI}\\\"\" >> .test.env\nfi\n\nif [ -n \"${SERVERLESS}\" ]; then\n    echo \"SERVERLESS_ATLAS_USER=$SERVERLESS_ATLAS_USER\" >> .test.env\n    echo \"SERVERLESS_ATLAS_PASSWORD=$SERVERLESS_ATLAS_PASSWORD\" >> .test.env\nfi\n\nif [ -n \"${LOAD_BALANCER}\" ];then\n    echo \"SINGLE_MONGOS_LB_URI=${SINGLE_MONGOS_LB_URI}\" >> .test.env\n    echo \"MULTI_MONGOS_LB_URI=${MULTI_MONGOS_LB_URI}\" >> .test.env\nfi\n\n# Add secrets to the test file.\nif [ -f \"secrets-export.sh\" ]; then\n    while read p; do\n        if [[ \"$p\" =~ ^export ]]; then\n            echo \"$p\" | sed 's/export //' >> .test.env\n        fi\n    done <secrets-export.sh\nfi\n"
  },
  {
    "path": "etc/update_spec_tests.sh",
    "content": "#!/usr/bin/env bash\n# update_spec_tests spec\n# This script is used to fetch the latest tests for the given spec. It puts the tests in the\n# directory data/[specname]. It should be run from the root of the repository.\n\nset -o errexit\nset -o nounset\n\nif [ ! -d \".git\" ]; then\n    echo \"$0: This script must be run from the root of the repository\" >&2\n    exit 1\nfi\n\nif [ $# -ne 1 ]; then\n    echo \"$0: This script must be passed exactly one argument for which tests to sync\" >&2\n    exit 1\nfi\n\ntmpdir=`perl -MFile::Temp=tempdir -wle 'print tempdir(TMPDIR => 1, CLEANUP => 0)'`\ncurl -sL https://github.com/mongodb/specifications/archive/master.zip -o \"$tmpdir/specs.zip\"\nunzip -d \"$tmpdir\" \"$tmpdir/specs.zip\" > /dev/null\nmkdir -p \"data/$1\"\nrsync -ah \"$tmpdir/specifications-master/source/$1/tests/\" \"data/$1\"\nrm -rf \"$tmpdir\"\n"
  },
  {
    "path": "event/description.go",
    "content": "// Copyright (C) MongoDB, Inc. 2024-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage event\n\nimport (\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/address\"\n\t\"go.mongodb.org/mongo-driver/v2/tag\"\n)\n\n// ServerDescription contains information about a node in a cluster. This is\n// created from hello command responses. If the value of the Kind field is\n// LoadBalancer, only the Addr and Kind fields will be set. All other fields\n// will be set to the zero value of the field's type.\ntype ServerDescription struct {\n\tAddr                     address.Address\n\tArbiters                 []string\n\tCompression              []string // compression methods returned by server\n\tCanonicalAddr            address.Address\n\tElectionID               bson.ObjectID\n\tIsCryptd                 bool\n\tHelloOK                  bool\n\tHosts                    []string\n\tKind                     string\n\tLastWriteTime            time.Time\n\tMaxBatchCount            uint32\n\tMaxDocumentSize          uint32\n\tMaxMessageSize           uint32\n\tMaxWireVersion           int32\n\tMinWireVersion           int32\n\tMembers                  []address.Address\n\tPassives                 []string\n\tPassive                  bool\n\tPrimary                  address.Address\n\tReadOnly                 bool\n\tServiceID                *bson.ObjectID // Only set for servers that are deployed behind a load balancer.\n\tSessionTimeoutMinutes    *int64\n\tSetName                  string\n\tSetVersion               uint32\n\tTags                     tag.Set\n\tTopologyVersionProcessID bson.ObjectID\n\tTopologyVersionCounter   int64\n}\n\n// TopologyDescription contains information about a MongoDB cluster.\ntype TopologyDescription struct {\n\tServers               []ServerDescription\n\tSetName               string\n\tKind                  string\n\tSessionTimeoutMinutes *int64\n\tCompatibilityErr      error\n}\n"
  },
  {
    "path": "event/doc.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\n// Package event is a library for monitoring events from the MongoDB Go\n// driver. Monitors can be set for commands sent to the MongoDB cluster,\n// connection pool changes, or changes on the MongoDB cluster.\n//\n// Monitoring commands requires specifying a CommandMonitor when constructing\n// a mongo.Client. A CommandMonitor can be set to monitor started, succeeded,\n// and/or failed events. A CommandStartedEvent can be correlated to its matching\n// CommandSucceededEvent or CommandFailedEvent through the RequestID field. For\n// example, the following code collects the names of started events:\n//\n//\tvar commandStarted []string\n//\tcmdMonitor := &event.CommandMonitor{\n//\t  Started: func(_ context.Context, evt *event.CommandStartedEvent) {\n//\t    commandStarted = append(commandStarted, evt.CommandName)\n//\t  },\n//\t}\n//\tclientOpts := options.Client().ApplyURI(\"mongodb://localhost:27017\").SetMonitor(cmdMonitor)\n//\tclient, err := mongo.Connect( clientOpts)\n//\n// Monitoring the connection pool requires specifying a PoolMonitor when constructing\n// a mongo.Client. The following code tracks the number of checked out connections:\n//\n//\tvar int connsCheckedOut\n//\tpoolMonitor := &event.PoolMonitor{\n//\t  Event: func(evt *event.PoolEvent) {\n//\t    switch evt.Type {\n//\t    case event.ConnectionCheckedOut:\n//\t      connsCheckedOut++\n//\t    case event.ConnectionCheckedIn:\n//\t      connsCheckedOut--\n//\t    }\n//\t  },\n//\t}\n//\tclientOpts := options.Client().ApplyURI(\"mongodb://localhost:27017\").SetPoolMonitor(poolMonitor)\n//\tclient, err := mongo.Connect( clientOpts)\n//\n// Monitoring server changes specifying a ServerMonitor object when constructing\n// a mongo.Client. Different functions can be set on the ServerMonitor to\n// monitor different kinds of events. See ServerMonitor for more details.\n// The following code appends ServerHeartbeatStartedEvents to a slice:\n//\n//\t   var heartbeatStarted []*event.ServerHeartbeatStartedEvent\n//\t   svrMonitor := &event.ServerMonitor{\n//\t     ServerHeartbeatStarted: func(e *event.ServerHeartbeatStartedEvent) {\n//\t\t      heartbeatStarted = append(heartbeatStarted, e)\n//\t     }\n//\t   }\n//\t   clientOpts := options.Client().ApplyURI(\"mongodb://localhost:27017\").SetServerMonitor(svrMonitor)\n//\t   client, err := mongo.Connect( clientOpts)\npackage event\n"
  },
  {
    "path": "event/examples_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage event_test\n\nimport (\n\t\"context\"\n\t\"log\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/event\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n)\n\n// Event examples\n\n// CommandMonitor represents a monitor that is triggered for different events.\nfunc ExampleCommandMonitor() {\n\t// If the application makes multiple concurrent requests, it would have to\n\t// use a concurrent map like sync.Map\n\tstartedCommands := make(map[int64]bson.Raw)\n\tcmdMonitor := &event.CommandMonitor{\n\t\tStarted: func(_ context.Context, evt *event.CommandStartedEvent) {\n\t\t\tstartedCommands[evt.RequestID] = evt.Command\n\t\t},\n\t\tSucceeded: func(_ context.Context, evt *event.CommandSucceededEvent) {\n\t\t\tlog.Printf(\"Command: %v Reply: %v\\n\",\n\t\t\t\tstartedCommands[evt.RequestID],\n\t\t\t\tevt.Reply,\n\t\t\t)\n\n\t\t\t// Empty \"startedCommands\" for the request ID to avoid a memory leak.\n\t\t\tdelete(startedCommands, evt.RequestID)\n\t\t},\n\t\tFailed: func(_ context.Context, evt *event.CommandFailedEvent) {\n\t\t\tlog.Printf(\"Command: %v Failure: %v\\n\",\n\t\t\t\tstartedCommands[evt.RequestID],\n\t\t\t\tevt.Failure,\n\t\t\t)\n\n\t\t\t// Empty \"startedCommands\" for the request ID to avoid a memory leak.\n\t\t\tdelete(startedCommands, evt.RequestID)\n\t\t},\n\t}\n\tclientOpts := options.Client().ApplyURI(\"mongodb://localhost:27017\").SetMonitor(cmdMonitor)\n\tclient, err := mongo.Connect(clientOpts)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tdefer func() {\n\t\tif err = client.Disconnect(context.TODO()); err != nil {\n\t\t\tlog.Fatal(err)\n\t\t}\n\t}()\n}\n"
  },
  {
    "path": "event/monitoring.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage event\n\nimport (\n\t\"context\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/address\"\n)\n\n// CommandStartedEvent represents an event generated when a command is sent to a server.\ntype CommandStartedEvent struct {\n\tCommand      bson.Raw\n\tDatabaseName string\n\tCommandName  string\n\tRequestID    int64\n\tConnectionID string\n\t// ServerConnectionID64 contains the connection ID from the server of the operation. If the server does not\n\t// return this value (e.g. on MDB < 4.2), it is unset.\n\tServerConnectionID *int64\n\t// ServiceID contains the ID of the server to which the command was sent if it is running behind a load balancer.\n\t// Otherwise, it is unset.\n\tServiceID *bson.ObjectID\n}\n\n// CommandFinishedEvent represents a generic command finishing.\ntype CommandFinishedEvent struct {\n\tDuration     time.Duration\n\tCommandName  string\n\tDatabaseName string\n\tRequestID    int64\n\tConnectionID string\n\t// ServerConnectionID64 contains the connection ID from the server of the operation. If the server does not\n\t// return this value (e.g. on MDB < 4.2), it is unset.\n\tServerConnectionID *int64\n\t// ServiceID contains the ID of the server to which the command was sent if it is running behind a load balancer.\n\t// Otherwise, it is unset.\n\tServiceID *bson.ObjectID\n}\n\n// CommandSucceededEvent represents an event generated when a command's execution succeeds.\ntype CommandSucceededEvent struct {\n\tCommandFinishedEvent\n\tReply bson.Raw\n}\n\n// CommandFailedEvent represents an event generated when a command's execution fails.\ntype CommandFailedEvent struct {\n\tCommandFinishedEvent\n\tFailure error\n}\n\n// CommandMonitor represents a monitor that is triggered for different events.\ntype CommandMonitor struct {\n\tStarted   func(context.Context, *CommandStartedEvent)\n\tSucceeded func(context.Context, *CommandSucceededEvent)\n\tFailed    func(context.Context, *CommandFailedEvent)\n}\n\n// strings for pool command monitoring reasons\nconst (\n\tReasonIdle              = \"idle\"\n\tReasonPoolClosed        = \"poolClosed\"\n\tReasonStale             = \"stale\"\n\tReasonConnectionErrored = \"connectionError\"\n\tReasonTimedOut          = \"timeout\"\n\tReasonError             = \"error\"\n)\n\n// strings for pool command monitoring types\nconst (\n\tConnectionPoolCreated     = \"ConnectionPoolCreated\"\n\tConnectionPoolReady       = \"ConnectionPoolReady\"\n\tConnectionPoolCleared     = \"ConnectionPoolCleared\"\n\tConnectionPoolClosed      = \"ConnectionPoolClosed\"\n\tConnectionCreated         = \"ConnectionCreated\"\n\tConnectionReady           = \"ConnectionReady\"\n\tConnectionClosed          = \"ConnectionClosed\"\n\tConnectionCheckOutStarted = \"ConnectionCheckOutStarted\"\n\tConnectionCheckOutFailed  = \"ConnectionCheckOutFailed\"\n\tConnectionCheckedOut      = \"ConnectionCheckedOut\"\n\tConnectionCheckedIn       = \"ConnectionCheckedIn\"\n)\n\n// MonitorPoolOptions contains pool options as formatted in pool events\ntype MonitorPoolOptions struct {\n\tMaxPoolSize        uint64 `json:\"maxPoolSize\"`\n\tMinPoolSize        uint64 `json:\"minPoolSize\"`\n\tWaitQueueTimeoutMS uint64 `json:\"maxIdleTimeMS\"`\n}\n\n// PoolEvent contains all information summarizing a pool event\ntype PoolEvent struct {\n\tType         string              `json:\"type\"`\n\tAddress      string              `json:\"address\"`\n\tConnectionID int64               `json:\"connectionId\"`\n\tPoolOptions  *MonitorPoolOptions `json:\"options\"`\n\tDuration     time.Duration       `json:\"duration\"`\n\tReason       string              `json:\"reason\"`\n\t// ServiceID is only set if the Type is PoolCleared and the server is deployed behind a load balancer. This field\n\t// can be used to distinguish between individual servers in a load balanced deployment.\n\tServiceID    *bson.ObjectID `json:\"serviceId\"`\n\tInterruption bool           `json:\"interruptInUseConnections\"`\n\tError        error          `json:\"error\"`\n}\n\n// PoolMonitor is a function that allows the user to gain access to events occurring in the pool\ntype PoolMonitor struct {\n\tEvent func(*PoolEvent)\n}\n\n// ServerDescriptionChangedEvent represents a server description change.\ntype ServerDescriptionChangedEvent struct {\n\tAddress             address.Address\n\tTopologyID          bson.ObjectID // A unique identifier for the topology this server is a part of\n\tPreviousDescription ServerDescription\n\tNewDescription      ServerDescription\n}\n\n// ServerOpeningEvent is an event generated when the server is initialized.\ntype ServerOpeningEvent struct {\n\tAddress    address.Address\n\tTopologyID bson.ObjectID // A unique identifier for the topology this server is a part of\n}\n\n// ServerClosedEvent is an event generated when the server is closed.\ntype ServerClosedEvent struct {\n\tAddress    address.Address\n\tTopologyID bson.ObjectID // A unique identifier for the topology this server is a part of\n}\n\n// TopologyDescriptionChangedEvent represents a topology description change.\ntype TopologyDescriptionChangedEvent struct {\n\tTopologyID          bson.ObjectID // A unique identifier for the topology this server is a part of\n\tPreviousDescription TopologyDescription\n\tNewDescription      TopologyDescription\n}\n\n// TopologyOpeningEvent is an event generated when the topology is initialized.\ntype TopologyOpeningEvent struct {\n\tTopologyID bson.ObjectID // A unique identifier for the topology this server is a part of\n}\n\n// TopologyClosedEvent is an event generated when the topology is closed.\ntype TopologyClosedEvent struct {\n\tTopologyID bson.ObjectID // A unique identifier for the topology this server is a part of\n}\n\n// ServerHeartbeatStartedEvent is an event generated when the heartbeat is started.\ntype ServerHeartbeatStartedEvent struct {\n\tConnectionID string // The address this heartbeat was sent to with a unique identifier\n\tAwaited      bool   // If this heartbeat was awaitable\n}\n\n// ServerHeartbeatSucceededEvent is an event generated when the heartbeat succeeds.\ntype ServerHeartbeatSucceededEvent struct {\n\tDuration     time.Duration\n\tReply        ServerDescription\n\tConnectionID string // The address this heartbeat was sent to with a unique identifier\n\tAwaited      bool   // If this heartbeat was awaitable\n}\n\n// ServerHeartbeatFailedEvent is an event generated when the heartbeat fails.\ntype ServerHeartbeatFailedEvent struct {\n\tDuration     time.Duration\n\tFailure      error\n\tConnectionID string // The address this heartbeat was sent to with a unique identifier\n\tAwaited      bool   // If this heartbeat was awaitable\n}\n\n// ServerMonitor represents a monitor that is triggered for different server events. The client\n// will monitor changes on the MongoDB deployment it is connected to, and this monitor reports\n// the changes in the client's representation of the deployment. The topology represents the\n// overall deployment, and heartbeats are sent to individual servers to check their current status.\ntype ServerMonitor struct {\n\tServerDescriptionChanged func(*ServerDescriptionChangedEvent)\n\tServerOpening            func(*ServerOpeningEvent)\n\tServerClosed             func(*ServerClosedEvent)\n\t// TopologyDescriptionChanged is called when the topology is locked, so the callback should\n\t// not attempt any operation that requires server selection on the same client.\n\tTopologyDescriptionChanged func(*TopologyDescriptionChangedEvent)\n\tTopologyOpening            func(*TopologyOpeningEvent)\n\tTopologyClosed             func(*TopologyClosedEvent)\n\tServerHeartbeatStarted     func(*ServerHeartbeatStartedEvent)\n\tServerHeartbeatSucceeded   func(*ServerHeartbeatSucceededEvent)\n\tServerHeartbeatFailed      func(*ServerHeartbeatFailedEvent)\n}\n"
  },
  {
    "path": "examples/_example_customdns_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2022-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage examples\n\nimport (\n\t\"context\"\n\t\"log\"\n\t\"net\"\n\t\"sync\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/miekg/dns\"\n\t\"go.mongodb.org/mongo-driver/bson\"\n\t\"go.mongodb.org/mongo-driver/mongo\"\n\t\"go.mongodb.org/mongo-driver/mongo/options\"\n)\n\nfunc resolve(ctx context.Context, cache *dnsCache, in *dns.Conn, out *dns.Conn) {\n\tfor ctx.Err() == nil {\n\t\tq, err := in.ReadMsg()\n\t\tif err != nil {\n\t\t\t// TODO: Handle error.\n\t\t\tlog.Fatalf(\"Unhandled error in ReadMsg: %v\", err)\n\t\t}\n\t\tif len(q.Question) != 1 {\n\t\t\t// Multiple questions in a single query is not actually used in real life.\n\t\t\tcontinue\n\t\t}\n\n\t\ta, err := func() (*dns.Msg, error) {\n\t\t\tcache.lock.Lock()\n\t\t\tdefer cache.lock.Unlock()\n\n\t\t\tnow := time.Now()\n\t\t\tif rr, ok := cache.records[q.Question[0]]; ok && rr.exp.After(now) {\n\t\t\t\ta := new(dns.Msg)\n\t\t\t\ta.SetReply(q)\n\t\t\t\ta.Compress = false\n\t\t\t\ta.Answer = append(a.Answer, rr.record)\n\t\t\t\treturn a, nil\n\t\t\t}\n\n\t\t\terr := out.WriteMsg(q)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\n\t\t\tm, err := out.ReadMsg()\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\n\t\t\tl := len(m.Answer)\n\t\t\tfor i, q := range m.Question {\n\t\t\t\tif i >= l {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\ta := m.Answer[i]\n\t\t\t\tcache.records[q] = &RR{\n\t\t\t\t\ta,\n\t\t\t\t\tnow.Add(time.Second * time.Duration(a.Header().Ttl)),\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn m, nil\n\t\t}()\n\t\tif err != nil {\n\t\t\t// TODO: Handle error.\n\t\t\tlog.Fatalf(\"Unhandled error in record retrieval: %v\", err)\n\t\t}\n\n\t\tif err := in.WriteMsg(a); err != nil {\n\t\t\t// TODO: Handle error.\n\t\t\tlog.Fatalf(\"Unhandled error in WriteMsg: %v\", err)\n\t\t}\n\t}\n}\n\ntype RR struct {\n\trecord dns.RR\n\texp    time.Time\n}\n\ntype dnsCache struct {\n\trecords map[dns.Question]*RR\n\tlock    sync.Mutex\n}\n\ntype dialer struct {\n\t*net.Dialer\n\tcache *dnsCache\n}\n\nfunc NewDialer() dialer {\n\tcache := &dnsCache{\n\t\trecords: make(map[dns.Question]*RR),\n\t\tlock:    sync.Mutex{},\n\t}\n\treturn dialer{\n\t\tDialer: &net.Dialer{\n\t\t\tResolver: &net.Resolver{\n\t\t\t\tPreferGo: true,\n\t\t\t\tDial: func(ctx context.Context, network, address string) (net.Conn, error) {\n\t\t\t\t\tvar d net.Dialer\n\t\t\t\t\toutConn, err := d.DialContext(ctx, network, address)\n\t\t\t\t\tconn, inConn := net.Pipe()\n\t\t\t\t\tif err == nil {\n\t\t\t\t\t\tgo resolve(ctx, cache, &dns.Conn{Conn: inConn}, &dns.Conn{Conn: outConn})\n\t\t\t\t\t}\n\t\t\t\t\treturn conn, err\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\tcache: cache,\n\t}\n}\n\nfunc TestCustomDialer(t *testing.T) {\n\tclient, err := mongo.NewClient(options.Client().ApplyURI(\"mongodb://testurl:27017\").SetDialer(NewDialer()))\n\tif err != nil {\n\t\tt.Fatalf(\"error creating client: %v\", err)\n\t}\n\tctx := context.Background()\n\terr = client.Connect(ctx)\n\tif err != nil {\n\t\tt.Fatalf(\"error connecting: %v\", err)\n\t}\n\tdefer client.Disconnect(context.Background())\n\tcoll := client.Database(\"test\").Collection(\"test\")\n\t_, err = coll.InsertOne(context.Background(), bson.D{{\"text\", \"text\"}})\n\tif err != nil {\n\t\tt.Fatalf(\"error inserting: %v\", err)\n\t}\n}\n"
  },
  {
    "path": "examples/_example_overload_error_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2022-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage examples\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"log\"\n\t\"math/rand\"\n\t\"sync\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo\"\n)\n\nconst (\n\ttokenBucketCap = 10_000\n\tretryToken     = 10\n\trefreshToken   = 1\n\n\tmaxAttempts = 5\n\n\tbaseBackoff = 100 * time.Millisecond\n\tmaxBackoff  = 10_000 * time.Millisecond\n\n\terrSystemOverloadedError = \"SystemOverloadedError\"\n\terrRetryableError        = \"RetryableError\"\n)\n\n// isSystemOverloadedError detects overload errors\nfunc isSystemOverloadedError(err error) bool {\n\tvar lerr mongo.LabeledError\n\tif errors.As(err, &lerr) && lerr.HasErrorLabel(errSystemOverloadedError) {\n\t\treturn true\n\t}\n\treturn false\n}\n\n// tokenBucket is used to limit retries\ntype tokenBucket struct {\n\tcapacity int\n\ttokens   int\n\tlocker   sync.Mutex\n}\n\nfunc newTokenBucket(capacity int) *tokenBucket {\n\treturn &tokenBucket{\n\t\tcapacity: capacity,\n\t\ttokens:   capacity,\n\t}\n}\n\nfunc (tb *tokenBucket) Consume(amount int) bool {\n\ttb.locker.Lock()\n\tdefer tb.locker.Unlock()\n\tif amount > tb.tokens {\n\t\treturn false\n\t}\n\ttb.tokens -= amount\n\treturn true\n}\n\nfunc (tb *tokenBucket) Deposit(amount int) {\n\ttb.locker.Lock()\n\tdefer tb.locker.Unlock()\n\ttb.tokens += amount\n\tif tb.tokens > tb.capacity {\n\t\ttb.tokens = tb.capacity\n\t}\n}\n\n// executeWithRetries executes the given function with retries if it returns a\n// SystemOverloadedError.\nfunc executeWithRetries(\n\tctx context.Context, tb *tokenBucket,\n\tfn func(ctx context.Context) (any, error),\n) (any, error) {\n\tvar result any\n\tvar err error\n\texpDur := baseBackoff\n\tfor attempts := 0; attempts < maxAttempts; attempts++ {\n\t\tisRetry := attempts > 0\n\n\t\tif isRetry {\n\t\t\tif expDur > maxBackoff {\n\t\t\t\texpDur = maxBackoff\n\t\t\t}\n\t\t\tdelay := expDur * time.Duration(rand.Int63n(512)) / 512\n\t\t\tsleep := time.NewTimer(delay)\n\t\t\tselect {\n\t\t\tcase <-ctx.Done():\n\t\t\t\tsleep.Stop()\n\t\t\t\tif err == nil {\n\t\t\t\t\terr = ctx.Err()\n\t\t\t\t}\n\t\t\t\treturn result, err\n\t\t\tcase <-sleep.C:\n\t\t\t}\n\t\t\tif expDur < maxBackoff {\n\t\t\t\texpDur = expDur * 2\n\t\t\t}\n\t\t}\n\n\t\tresult, err = fn(ctx)\n\t\tif err == nil {\n\t\t\ttb.Deposit(refreshToken)\n\t\t\tif isRetry {\n\t\t\t\ttb.Deposit(retryToken)\n\t\t\t}\n\t\t\tbreak\n\t\t}\n\t\tif !isSystemOverloadedError(err) {\n\t\t\ttb.Deposit(retryToken)\n\t\t\tbreak\n\t\t}\n\t\tvar lerr mongo.LabeledError\n\t\tif !errors.As(err, &lerr) || !lerr.HasErrorLabel(errRetryableError) {\n\t\t\tbreak\n\t\t}\n\t\tif !tb.Consume(retryToken) {\n\t\t\tbreak\n\t\t}\n\t}\n\treturn result, err\n}\n\n// ExampleOverloadError_Find demonstrates how to use executeWithRetries to retry a Find operation\n// if it returns a SystemOverloadedError.\nfunc ExampleOverloadError_Find() {\n\tvar coll *mongo.Collection\n\n\tctx := context.Background()\n\ttb := newTokenBucket(tokenBucketCap)\n\tresult, err := executeWithRetries(ctx, tb, func(ctx context.Context) (any, error) {\n\t\tcursor, err := coll.Find(ctx, bson.D{})\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tvar res []bson.D\n\t\terr = cursor.All(ctx, &res)\n\t\treturn res, err\n\t})\n\tif err != nil {\n\t\t// ErrNoDocuments means that the filter did not match any documents in\n\t\t// the collection.\n\t\tif errors.Is(err, mongo.ErrNoDocuments) {\n\t\t\treturn\n\t\t}\n\t\tlog.Panic(err)\n\t}\n\tfmt.Printf(\"found %v\\n\", result)\n}\n"
  },
  {
    "path": "examples/_logger/logrus/go.mod",
    "content": "module go.mongodb.go/mongo-driver/v2/examples/logger/logrus\n\ngo 1.25\n\nreplace go.mongodb.org/mongo-driver/v2 => ../../../\n\nrequire (\n\tgithub.com/bombsimon/logrusr/v4 v4.0.0\n\tgithub.com/sirupsen/logrus v1.9.1\n\tgo.mongodb.org/mongo-driver/v2 v2.0.0-alpha2\n)\n\nrequire (\n\tgithub.com/go-logr/logr v1.2.3 // indirect\n\tgithub.com/klauspost/compress v1.17.6 // indirect\n\tgithub.com/xdg-go/pbkdf2 v1.0.0 // indirect\n\tgithub.com/xdg-go/scram v1.2.0 // indirect\n\tgithub.com/xdg-go/stringprep v1.0.4 // indirect\n\tgithub.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect\n\tgolang.org/x/crypto v0.45.0 // indirect\n\tgolang.org/x/sync v0.18.0 // indirect\n\tgolang.org/x/sys v0.38.0 // indirect\n\tgolang.org/x/text v0.31.0 // indirect\n)\n"
  },
  {
    "path": "examples/_logger/logrus/go.sum",
    "content": "github.com/bombsimon/logrusr/v4 v4.0.0 h1:Pm0InGphX0wMhPqC02t31onlq9OVyJ98eP/Vh63t1Oo=\ngithub.com/bombsimon/logrusr/v4 v4.0.0/go.mod h1:pjfHC5e59CvjTBIU3V3sGhFWFAnsnhOR03TRc6im0l8=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0=\ngithub.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=\ngithub.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=\ngithub.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=\ngithub.com/klauspost/compress v1.17.6 h1:60eq2E/jlfwQXtvZEeBUYADs+BwKBWURIY+Gj2eRGjI=\ngithub.com/klauspost/compress v1.17.6/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM=\ngithub.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/sirupsen/logrus v1.9.1 h1:Ou41VVR3nMWWmTiEUnj0OlsgOSCUFgsPAOl6jRIcVtQ=\ngithub.com/sirupsen/logrus v1.9.1/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=\ngithub.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=\ngithub.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=\ngithub.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=\ngithub.com/xdg-go/scram v1.2.0 h1:bYKF2AEwG5rqd1BumT4gAnvwU/M9nBp2pTSxeZw7Wvs=\ngithub.com/xdg-go/scram v1.2.0/go.mod h1:3dlrS0iBaWKYVt2ZfA4cj48umJZ+cAEbR6/SjLA88I8=\ngithub.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8=\ngithub.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=\ngithub.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM=\ngithub.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78/go.mod h1:aL8wCCfTfSfmXjznFBSZNN13rSJjlIOI1fUNAtF7rmI=\ngithub.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=\ngolang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=\ngolang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=\ngolang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=\ngolang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I=\ngolang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=\ngolang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=\ngolang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=\ngolang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=\ngolang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=\ngolang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=\ngolang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=\ngolang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=\ngopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\n"
  },
  {
    "path": "examples/_logger/logrus/main.go",
    "content": "// Copyright (C) MongoDB, Inc. 2023-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\n//go:build logrus\n\npackage main\n\nimport (\n\t\"context\"\n\t\"log\"\n\n\t\"github.com/bombsimon/logrusr/v4\"\n\t\"github.com/sirupsen/logrus\"\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n)\n\nfunc main() {\n\t// Create a new logrus logger instance.\n\tlogger := logrus.StandardLogger()\n\tlogger.SetLevel(logrus.DebugLevel)\n\n\t// Create a new sink for logrus using \"logrusr\".\n\tsink := logrusr.New(logger).GetSink()\n\n\t// Create a client with our logger options.\n\tloggerOptions := options.\n\t\tLogger().\n\t\tSetSink(sink).\n\t\tSetMaxDocumentLength(25).\n\t\tSetComponentLevel(options.LogComponentCommand, options.LogLevelDebug)\n\n\tclientOptions := options.\n\t\tClient().\n\t\tApplyURI(\"mongodb://localhost:27017\").\n\t\tSetLoggerOptions(loggerOptions)\n\n\tclient, err := mongo.Connect(clientOptions)\n\tif err != nil {\n\t\tlog.Fatalf(\"error connecting to MongoDB: %v\", err)\n\t}\n\n\tdefer client.Disconnect(context.TODO())\n\n\t// Make a database request to test our logging solution.\n\tcoll := client.Database(\"test\").Collection(\"test\")\n\n\t_, err = coll.InsertOne(context.TODO(), bson.D{{\"Alice\", \"123\"}})\n\tif err != nil {\n\t\tlog.Fatalf(\"InsertOne failed: %v\", err)\n\t}\n}\n"
  },
  {
    "path": "examples/_logger/zap/go.mod",
    "content": "module go.mongodb.go/mongo-driver/v2/examples/logger/zap\n\ngo 1.25\n\nreplace go.mongodb.org/mongo-driver/v2 => ../../../\n\nrequire (\n\tgithub.com/go-logr/zapr v1.2.3\n\tgo.mongodb.org/mongo-driver/v2 v2.0.0-alpha2\n\tgo.uber.org/zap v1.24.0\n)\n\nrequire (\n\tgithub.com/go-logr/logr v1.2.2 // indirect\n\tgithub.com/klauspost/compress v1.17.6 // indirect\n\tgithub.com/xdg-go/pbkdf2 v1.0.0 // indirect\n\tgithub.com/xdg-go/scram v1.2.0 // indirect\n\tgithub.com/xdg-go/stringprep v1.0.4 // indirect\n\tgithub.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect\n\tgo.uber.org/atomic v1.7.0 // indirect\n\tgo.uber.org/multierr v1.6.0 // indirect\n\tgolang.org/x/crypto v0.45.0 // indirect\n\tgolang.org/x/sync v0.18.0 // indirect\n\tgolang.org/x/text v0.31.0 // indirect\n)\n"
  },
  {
    "path": "examples/_logger/zap/go.sum",
    "content": "github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=\ngithub.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/go-logr/logr v1.2.2 h1:ahHml/yUpnlb96Rp8HCvtYVPY8ZYpxq3g7UYchIYwbs=\ngithub.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=\ngithub.com/go-logr/zapr v1.2.3 h1:a9vnzlIBPQBBkeaR9IuMUfmVOrQlkoC4YfPoFkX3T7A=\ngithub.com/go-logr/zapr v1.2.3/go.mod h1:eIauM6P8qSvTw5o2ez6UEAfGjQKrxQTl5EoK+Qa2oG4=\ngithub.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=\ngithub.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=\ngithub.com/klauspost/compress v1.17.6 h1:60eq2E/jlfwQXtvZEeBUYADs+BwKBWURIY+Gj2eRGjI=\ngithub.com/klauspost/compress v1.17.6/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM=\ngithub.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=\ngithub.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=\ngithub.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=\ngithub.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=\ngithub.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=\ngithub.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=\ngithub.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=\ngithub.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=\ngithub.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=\ngithub.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=\ngithub.com/xdg-go/scram v1.2.0 h1:bYKF2AEwG5rqd1BumT4gAnvwU/M9nBp2pTSxeZw7Wvs=\ngithub.com/xdg-go/scram v1.2.0/go.mod h1:3dlrS0iBaWKYVt2ZfA4cj48umJZ+cAEbR6/SjLA88I8=\ngithub.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8=\ngithub.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=\ngithub.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM=\ngithub.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78/go.mod h1:aL8wCCfTfSfmXjznFBSZNN13rSJjlIOI1fUNAtF7rmI=\ngithub.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=\ngo.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=\ngo.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=\ngo.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=\ngo.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI=\ngo.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=\ngo.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=\ngo.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=\ngo.uber.org/zap v1.19.0/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI=\ngo.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60=\ngo.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=\ngolang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=\ngolang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=\ngolang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=\ngolang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=\ngolang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I=\ngolang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=\ngolang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=\ngolang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=\ngolang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=\ngolang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=\ngolang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=\ngopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\n"
  },
  {
    "path": "examples/_logger/zap/main.go",
    "content": "// Copyright (C) MongoDB, Inc. 2023-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\n//go:build zap\n\npackage main\n\nimport (\n\t\"context\"\n\t\"log\"\n\n\t\"github.com/go-logr/zapr\"\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n\t\"go.uber.org/zap\"\n)\n\nfunc main() {\n\tlogger, err := zap.NewDevelopment()\n\tif err != nil {\n\t\tlog.Fatalf(\"error creating zap logger: %w\", err)\n\t}\n\n\tsink := zapr.NewLogger(logger).GetSink()\n\n\t// Create a client with our logger options.\n\tloggerOptions := options.\n\t\tLogger().\n\t\tSetSink(sink).\n\t\tSetMaxDocumentLength(25).\n\t\tSetComponentLevel(options.LogComponentCommand, options.LogLevelDebug)\n\n\tclientOptions := options.\n\t\tClient().\n\t\tApplyURI(\"mongodb://localhost:27017\").\n\t\tSetLoggerOptions(loggerOptions)\n\n\tclient, err := mongo.Connect(clientOptions)\n\tif err != nil {\n\t\tlog.Fatalf(\"error connecting to MongoDB: %v\", err)\n\t}\n\n\tdefer client.Disconnect(context.TODO())\n\n\t// Make a database request to test our logging solution.\n\tcoll := client.Database(\"test\").Collection(\"test\")\n\n\t_, err = coll.InsertOne(context.TODO(), bson.D{{\"Alice\", \"123\"}})\n\tif err != nil {\n\t\tlog.Fatalf(\"InsertOne failed: %v\", err)\n\t}\n}\n"
  },
  {
    "path": "examples/_logger/zerolog/go.mod",
    "content": "module go.mongodb.go/mongo-driver/v2/examples/logger/zerolog\n\ngo 1.25\n\nreplace go.mongodb.org/mongo-driver/v2 => ../../../\n\nrequire (\n\tgithub.com/go-logr/zerologr v1.2.2\n\tgithub.com/rs/zerolog v1.28.0\n\tgo.mongodb.org/mongo-driver/v2 v2.0.0-alpha2\n)\n\nrequire (\n\tgithub.com/go-logr/logr v1.2.3 // indirect\n\tgithub.com/klauspost/compress v1.17.6 // indirect\n\tgithub.com/mattn/go-colorable v0.1.12 // indirect\n\tgithub.com/mattn/go-isatty v0.0.14 // indirect\n\tgithub.com/xdg-go/pbkdf2 v1.0.0 // indirect\n\tgithub.com/xdg-go/scram v1.2.0 // indirect\n\tgithub.com/xdg-go/stringprep v1.0.4 // indirect\n\tgithub.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect\n\tgolang.org/x/crypto v0.45.0 // indirect\n\tgolang.org/x/sync v0.18.0 // indirect\n\tgolang.org/x/sys v0.38.0 // indirect\n\tgolang.org/x/text v0.31.0 // indirect\n)\n"
  },
  {
    "path": "examples/_logger/zerolog/go.sum",
    "content": "github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=\ngithub.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0=\ngithub.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=\ngithub.com/go-logr/zerologr v1.2.2 h1:nKJ1glUZQPURRpe20GaqCBgNyGYg9cylaerwrwKoogE=\ngithub.com/go-logr/zerologr v1.2.2/go.mod h1:eIsB+dwGuN3lAGytcpbXyBeiY8GKInIxy+Qwe+gI5lI=\ngithub.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=\ngithub.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=\ngithub.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=\ngithub.com/klauspost/compress v1.17.6 h1:60eq2E/jlfwQXtvZEeBUYADs+BwKBWURIY+Gj2eRGjI=\ngithub.com/klauspost/compress v1.17.6/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM=\ngithub.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40=\ngithub.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=\ngithub.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=\ngithub.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=\ngithub.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=\ngithub.com/rs/zerolog v1.28.0 h1:MirSo27VyNi7RJYP3078AA1+Cyzd2GB66qy3aUHvsWY=\ngithub.com/rs/zerolog v1.28.0/go.mod h1:NILgTygv/Uej1ra5XxGf82ZFSLk58MFGAUS2o6usyD0=\ngithub.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=\ngithub.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=\ngithub.com/xdg-go/scram v1.2.0 h1:bYKF2AEwG5rqd1BumT4gAnvwU/M9nBp2pTSxeZw7Wvs=\ngithub.com/xdg-go/scram v1.2.0/go.mod h1:3dlrS0iBaWKYVt2ZfA4cj48umJZ+cAEbR6/SjLA88I8=\ngithub.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8=\ngithub.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=\ngithub.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM=\ngithub.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78/go.mod h1:aL8wCCfTfSfmXjznFBSZNN13rSJjlIOI1fUNAtF7rmI=\ngithub.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=\ngolang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=\ngolang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=\ngolang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=\ngolang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I=\ngolang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=\ngolang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=\ngolang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=\ngolang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=\ngolang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=\ngolang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=\ngolang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=\ngolang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\n"
  },
  {
    "path": "examples/_logger/zerolog/main.go",
    "content": "// Copyright (C) MongoDB, Inc. 2023-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\n//go:build zerolog\n\npackage main\n\nimport (\n\t\"context\"\n\t\"log\"\n\t\"os\"\n\n\t\"github.com/go-logr/zerologr\"\n\t\"github.com/rs/zerolog\"\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n)\n\nfunc main() {\n\tlogger := zerolog.New(os.Stderr).With().Caller().Timestamp().Logger()\n\tsink := zerologr.New(&logger).GetSink()\n\n\t// Create a client with our logger options.\n\tloggerOptions := options.\n\t\tLogger().\n\t\tSetSink(sink).\n\t\tSetMaxDocumentLength(25).\n\t\tSetComponentLevel(options.LogComponentCommand, options.LogLevelDebug)\n\n\tclientOptions := options.\n\t\tClient().\n\t\tApplyURI(\"mongodb://localhost:27017\").\n\t\tSetLoggerOptions(loggerOptions)\n\n\tclient, err := mongo.Connect(clientOptions)\n\tif err != nil {\n\t\tlog.Fatalf(\"error connecting to MongoDB: %v\", err)\n\t}\n\n\tdefer client.Disconnect(context.TODO())\n\n\t// Make a database request to test our logging solution\n\tcoll := client.Database(\"test\").Collection(\"test\")\n\n\t_, err = coll.InsertOne(context.TODO(), bson.D{{\"Alice\", \"123\"}})\n\tif err != nil {\n\t\tlog.Fatalf(\"InsertOne failed: %v\", err)\n\t}\n}\n"
  },
  {
    "path": "go.mod",
    "content": "module go.mongodb.org/mongo-driver/v2\n\ngo 1.19\n\nrequire (\n\tgithub.com/davecgh/go-spew v1.1.1\n\tgithub.com/google/go-cmp v0.6.0\n\tgithub.com/klauspost/compress v1.17.6\n\tgithub.com/xdg-go/scram v1.2.0\n\tgithub.com/xdg-go/stringprep v1.0.4\n\tgithub.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78\n\tgolang.org/x/crypto v0.33.0\n\tgolang.org/x/sync v0.11.0\n)\n\nrequire (\n\tgithub.com/xdg-go/pbkdf2 v1.0.0 // indirect\n\tgolang.org/x/text v0.22.0 // indirect\n)\n\nreplace golang.org/x/net/http2 => golang.org/x/net/http2 v0.23.0 // GODRIVER-3225\n"
  },
  {
    "path": "go.sum",
    "content": "github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=\ngithub.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=\ngithub.com/klauspost/compress v1.17.6 h1:60eq2E/jlfwQXtvZEeBUYADs+BwKBWURIY+Gj2eRGjI=\ngithub.com/klauspost/compress v1.17.6/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM=\ngithub.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=\ngithub.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=\ngithub.com/xdg-go/scram v1.2.0 h1:bYKF2AEwG5rqd1BumT4gAnvwU/M9nBp2pTSxeZw7Wvs=\ngithub.com/xdg-go/scram v1.2.0/go.mod h1:3dlrS0iBaWKYVt2ZfA4cj48umJZ+cAEbR6/SjLA88I8=\ngithub.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8=\ngithub.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=\ngithub.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM=\ngithub.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78/go.mod h1:aL8wCCfTfSfmXjznFBSZNN13rSJjlIOI1fUNAtF7rmI=\ngithub.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=\ngolang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=\ngolang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=\ngolang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=\ngolang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=\ngolang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=\ngolang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=\ngolang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=\ngolang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=\ngolang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=\ngolang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\n"
  },
  {
    "path": "go.work",
    "content": "go 1.25.0\n\nuse (\n\t.\n\t./examples/_logger/logrus\n\t./examples/_logger/zap\n\t./examples/_logger/zerolog\n\t./internal/cmd/benchmark\n\t./internal/cmd/faas/awslambda/mongodb\n\t./internal/test/compilecheck\n\t./internal/test/goleak\n)\n"
  },
  {
    "path": "internal/assert/assertbson/assertbson.go",
    "content": "// Copyright (C) MongoDB, Inc. 2025-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage assertbson\n\nimport (\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n)\n\ntype tHelper interface {\n\tHelper()\n}\n\n// EqualDocument asserts that the expected and actual BSON documents are equal.\n// If the documents are not equal, it prints both the binary diff and Extended\n// JSON representation of the BSON documents.\nfunc EqualDocument(t assert.TestingT, expected, actual []byte) bool {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\n\treturn assert.Equal(t,\n\t\texpected,\n\t\tactual,\n\t\t`expected and actual BSON documents do not match\nAs Extended JSON:\nExpected: %s\nActual  : %s`,\n\t\tbson.Raw(expected),\n\t\tbson.Raw(actual))\n}\n\n// EqualValue asserts that the expected and actual BSON values are equal. If the\n// values are not equal, it prints both the binary diff and Extended JSON\n// representation of the BSON values.\nfunc EqualValue[T bson.RawValue | bsoncore.Value](t assert.TestingT, expected, actual T) bool {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\n\treturn assert.Equal(t,\n\t\texpected,\n\t\tactual,\n\t\t`expected and actual BSON values do not match\nAs Extended JSON:\nExpected: %s\nActual  : %s`,\n\t\texpected,\n\t\tactual)\n}\n"
  },
  {
    "path": "internal/assert/assertbson/assertbson_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2025-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage assertbson\n\nimport (\n\t\"testing\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n)\n\nfunc TestEqualDocument(t *testing.T) {\n\tt.Parallel()\n\n\ttestCases := []struct {\n\t\tname     string\n\t\texpected []byte\n\t\tactual   []byte\n\t\twant     bool\n\t}{\n\t\t{\n\t\t\tname:     \"equal bson.Raw\",\n\t\t\texpected: bson.Raw{5, 0, 0, 0, 0},\n\t\t\tactual:   bson.Raw{5, 0, 0, 0, 0},\n\t\t\twant:     true,\n\t\t},\n\t\t{\n\t\t\tname:     \"different bson.Raw\",\n\t\t\texpected: bson.Raw{8, 0, 0, 0, 10, 120, 0, 0},\n\t\t\tactual:   bson.Raw{5, 0, 0, 0, 0},\n\t\t\twant:     false,\n\t\t},\n\t\t{\n\t\t\tname:     \"invalid bson.Raw\",\n\t\t\texpected: bson.Raw{99, 99, 99, 99},\n\t\t\tactual:   bson.Raw{5, 0, 0, 0, 0},\n\t\t\twant:     false,\n\t\t},\n\t\t{\n\t\t\tname:     \"nil bson.Raw\",\n\t\t\texpected: bson.Raw(nil),\n\t\t\tactual:   bson.Raw(nil),\n\t\t\twant:     true,\n\t\t},\n\t}\n\n\tfor _, tc := range testCases {\n\t\ttc := tc // Capture range variable.\n\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tgot := EqualDocument(new(testing.T), tc.expected, tc.actual)\n\t\t\tif got != tc.want {\n\t\t\t\tt.Errorf(\"EqualDocument(%#v, %#v) = %v, want %v\", tc.expected, tc.actual, got, tc.want)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestEqualValue(t *testing.T) {\n\tt.Parallel()\n\n\tt.Run(\"bson.RawValue\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\ttestCases := []struct {\n\t\t\tname     string\n\t\t\texpected bson.RawValue\n\t\t\tactual   bson.RawValue\n\t\t\twant     bool\n\t\t}{\n\t\t\t{\n\t\t\t\tname: \"equal\",\n\t\t\t\texpected: bson.RawValue{\n\t\t\t\t\tType:  bson.TypeInt32,\n\t\t\t\t\tValue: []byte{1, 0, 0, 0},\n\t\t\t\t},\n\t\t\t\tactual: bson.RawValue{\n\t\t\t\t\tType:  bson.TypeInt32,\n\t\t\t\t\tValue: []byte{1, 0, 0, 0},\n\t\t\t\t},\n\t\t\t\twant: true,\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"same type, different value\",\n\t\t\t\texpected: bson.RawValue{\n\t\t\t\t\tType:  bson.TypeInt32,\n\t\t\t\t\tValue: []byte{1, 0, 0, 0},\n\t\t\t\t},\n\t\t\t\tactual: bson.RawValue{\n\t\t\t\t\tType:  bson.TypeInt32,\n\t\t\t\t\tValue: []byte{1, 1, 1, 1},\n\t\t\t\t},\n\t\t\t\twant: false,\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"same value, different type\",\n\t\t\t\texpected: bson.RawValue{\n\t\t\t\t\tType:  bson.TypeDouble,\n\t\t\t\t\tValue: []byte{1, 0, 0, 0, 0, 0, 0, 0},\n\t\t\t\t},\n\t\t\t\tactual: bson.RawValue{\n\t\t\t\t\tType:  bson.TypeInt64,\n\t\t\t\t\tValue: []byte{1, 0, 0, 0, 0, 0, 0, 0},\n\t\t\t\t},\n\t\t\t\twant: false,\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"different value, different type\",\n\t\t\t\texpected: bson.RawValue{\n\t\t\t\t\tType:  bson.TypeInt32,\n\t\t\t\t\tValue: []byte{1, 0, 0, 0},\n\t\t\t\t},\n\t\t\t\tactual: bson.RawValue{\n\t\t\t\t\tType:  bson.TypeString,\n\t\t\t\t\tValue: []byte{1, 1, 1, 1},\n\t\t\t\t},\n\t\t\t\twant: false,\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"invalid\",\n\t\t\t\texpected: bson.RawValue{\n\t\t\t\t\tType:  bson.TypeInt64,\n\t\t\t\t\tValue: []byte{1, 0, 0, 0},\n\t\t\t\t},\n\t\t\t\tactual: bson.RawValue{\n\t\t\t\t\tType:  bson.TypeInt32,\n\t\t\t\t\tValue: []byte{1, 0, 0, 0},\n\t\t\t\t},\n\t\t\t\twant: false,\n\t\t\t},\n\t\t\t{\n\t\t\t\tname:     \"empty\",\n\t\t\t\texpected: bson.RawValue{},\n\t\t\t\tactual:   bson.RawValue{},\n\t\t\t\twant:     true,\n\t\t\t},\n\t\t}\n\n\t\tfor _, tc := range testCases {\n\t\t\ttc := tc // Capture range variable.\n\n\t\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\t\tt.Parallel()\n\n\t\t\t\tgot := EqualValue(new(testing.T), tc.expected, tc.actual)\n\t\t\t\tif got != tc.want {\n\t\t\t\t\tt.Errorf(\"EqualValue(%#v, %#v) = %v, want %v\", tc.expected, tc.actual, got, tc.want)\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"bsoncore.Value\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\ttestCases := []struct {\n\t\t\tname     string\n\t\t\texpected bsoncore.Value\n\t\t\tactual   bsoncore.Value\n\t\t\twant     bool\n\t\t}{\n\t\t\t{\n\t\t\t\tname: \"equal\",\n\t\t\t\texpected: bsoncore.Value{\n\t\t\t\t\tType: bsoncore.TypeInt32,\n\t\t\t\t\tData: []byte{1, 0, 0, 0},\n\t\t\t\t},\n\t\t\t\tactual: bsoncore.Value{\n\t\t\t\t\tType: bsoncore.TypeInt32,\n\t\t\t\t\tData: []byte{1, 0, 0, 0},\n\t\t\t\t},\n\t\t\t\twant: true,\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"same type, different value\",\n\t\t\t\texpected: bsoncore.Value{\n\t\t\t\t\tType: bsoncore.TypeInt32,\n\t\t\t\t\tData: []byte{1, 0, 0, 0},\n\t\t\t\t},\n\t\t\t\tactual: bsoncore.Value{\n\t\t\t\t\tType: bsoncore.TypeInt32,\n\t\t\t\t\tData: []byte{1, 1, 1, 1},\n\t\t\t\t},\n\t\t\t\twant: false,\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"same value, different type\",\n\t\t\t\texpected: bsoncore.Value{\n\t\t\t\t\tType: bsoncore.TypeDouble,\n\t\t\t\t\tData: []byte{1, 0, 0, 0, 0, 0, 0, 0},\n\t\t\t\t},\n\t\t\t\tactual: bsoncore.Value{\n\t\t\t\t\tType: bsoncore.TypeInt64,\n\t\t\t\t\tData: []byte{1, 0, 0, 0, 0, 0, 0, 0},\n\t\t\t\t},\n\t\t\t\twant: false,\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"different value, different type\",\n\t\t\t\texpected: bsoncore.Value{\n\t\t\t\t\tType: bsoncore.TypeInt32,\n\t\t\t\t\tData: []byte{1, 0, 0, 0},\n\t\t\t\t},\n\t\t\t\tactual: bsoncore.Value{\n\t\t\t\t\tType: bsoncore.TypeString,\n\t\t\t\t\tData: []byte{1, 1, 1, 1},\n\t\t\t\t},\n\t\t\t\twant: false,\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"invalid\",\n\t\t\t\texpected: bsoncore.Value{\n\t\t\t\t\tType: bsoncore.TypeInt64,\n\t\t\t\t\tData: []byte{1, 0, 0, 0},\n\t\t\t\t},\n\t\t\t\tactual: bsoncore.Value{\n\t\t\t\t\tType: bsoncore.TypeInt32,\n\t\t\t\t\tData: []byte{1, 0, 0, 0},\n\t\t\t\t},\n\t\t\t\twant: false,\n\t\t\t},\n\t\t\t{\n\t\t\t\tname:     \"empty\",\n\t\t\t\texpected: bsoncore.Value{},\n\t\t\t\tactual:   bsoncore.Value{},\n\t\t\t\twant:     true,\n\t\t\t},\n\t\t}\n\n\t\tfor _, tc := range testCases {\n\t\t\ttc := tc // Capture range variable.\n\n\t\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\t\tt.Parallel()\n\n\t\t\t\tgot := EqualValue(new(testing.T), tc.expected, tc.actual)\n\t\t\t\tif got != tc.want {\n\t\t\t\t\tt.Errorf(\"EqualValue(%#v, %#v) = %v, want %v\", tc.expected, tc.actual, got, tc.want)\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t})\n}\n"
  },
  {
    "path": "internal/assert/assertion_compare.go",
    "content": "// Copied from https://github.com/stretchr/testify/blob/1333b5d3bda8cf5aedcf3e1aaa95cac28aaab892/assert/assertion_compare.go\n\n// Copyright 2020 Mat Ryer, Tyler Bunnell and all contributors. All rights reserved.\n// Use of this source code is governed by an MIT-style license that can be found in\n// the THIRD-PARTY-NOTICES file.\n\npackage assert\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"reflect\"\n\t\"time\"\n)\n\ntype CompareType int\n\nconst (\n\tcompareLess CompareType = iota - 1\n\tcompareEqual\n\tcompareGreater\n)\n\nvar (\n\tintType   = reflect.TypeOf(int(1))\n\tint8Type  = reflect.TypeOf(int8(1))\n\tint16Type = reflect.TypeOf(int16(1))\n\tint32Type = reflect.TypeOf(int32(1))\n\tint64Type = reflect.TypeOf(int64(1))\n\n\tuintType   = reflect.TypeOf(uint(1))\n\tuint8Type  = reflect.TypeOf(uint8(1))\n\tuint16Type = reflect.TypeOf(uint16(1))\n\tuint32Type = reflect.TypeOf(uint32(1))\n\tuint64Type = reflect.TypeOf(uint64(1))\n\n\tfloat32Type = reflect.TypeOf(float32(1))\n\tfloat64Type = reflect.TypeOf(float64(1))\n\n\tstringType = reflect.TypeOf(\"\")\n\n\ttimeType  = reflect.TypeOf(time.Time{})\n\tbytesType = reflect.TypeOf([]byte{})\n)\n\nfunc compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) {\n\tobj1Value := reflect.ValueOf(obj1)\n\tobj2Value := reflect.ValueOf(obj2)\n\n\t// throughout this switch we try and avoid calling .Convert() if possible,\n\t// as this has a pretty big performance impact\n\tswitch kind {\n\tcase reflect.Int:\n\t\t{\n\t\t\tintobj1, ok := obj1.(int)\n\t\t\tif !ok {\n\t\t\t\tintobj1 = obj1Value.Convert(intType).Interface().(int)\n\t\t\t}\n\t\t\tintobj2, ok := obj2.(int)\n\t\t\tif !ok {\n\t\t\t\tintobj2 = obj2Value.Convert(intType).Interface().(int)\n\t\t\t}\n\t\t\tif intobj1 > intobj2 {\n\t\t\t\treturn compareGreater, true\n\t\t\t}\n\t\t\tif intobj1 == intobj2 {\n\t\t\t\treturn compareEqual, true\n\t\t\t}\n\t\t\tif intobj1 < intobj2 {\n\t\t\t\treturn compareLess, true\n\t\t\t}\n\t\t}\n\tcase reflect.Int8:\n\t\t{\n\t\t\tint8obj1, ok := obj1.(int8)\n\t\t\tif !ok {\n\t\t\t\tint8obj1 = obj1Value.Convert(int8Type).Interface().(int8)\n\t\t\t}\n\t\t\tint8obj2, ok := obj2.(int8)\n\t\t\tif !ok {\n\t\t\t\tint8obj2 = obj2Value.Convert(int8Type).Interface().(int8)\n\t\t\t}\n\t\t\tif int8obj1 > int8obj2 {\n\t\t\t\treturn compareGreater, true\n\t\t\t}\n\t\t\tif int8obj1 == int8obj2 {\n\t\t\t\treturn compareEqual, true\n\t\t\t}\n\t\t\tif int8obj1 < int8obj2 {\n\t\t\t\treturn compareLess, true\n\t\t\t}\n\t\t}\n\tcase reflect.Int16:\n\t\t{\n\t\t\tint16obj1, ok := obj1.(int16)\n\t\t\tif !ok {\n\t\t\t\tint16obj1 = obj1Value.Convert(int16Type).Interface().(int16)\n\t\t\t}\n\t\t\tint16obj2, ok := obj2.(int16)\n\t\t\tif !ok {\n\t\t\t\tint16obj2 = obj2Value.Convert(int16Type).Interface().(int16)\n\t\t\t}\n\t\t\tif int16obj1 > int16obj2 {\n\t\t\t\treturn compareGreater, true\n\t\t\t}\n\t\t\tif int16obj1 == int16obj2 {\n\t\t\t\treturn compareEqual, true\n\t\t\t}\n\t\t\tif int16obj1 < int16obj2 {\n\t\t\t\treturn compareLess, true\n\t\t\t}\n\t\t}\n\tcase reflect.Int32:\n\t\t{\n\t\t\tint32obj1, ok := obj1.(int32)\n\t\t\tif !ok {\n\t\t\t\tint32obj1 = obj1Value.Convert(int32Type).Interface().(int32)\n\t\t\t}\n\t\t\tint32obj2, ok := obj2.(int32)\n\t\t\tif !ok {\n\t\t\t\tint32obj2 = obj2Value.Convert(int32Type).Interface().(int32)\n\t\t\t}\n\t\t\tif int32obj1 > int32obj2 {\n\t\t\t\treturn compareGreater, true\n\t\t\t}\n\t\t\tif int32obj1 == int32obj2 {\n\t\t\t\treturn compareEqual, true\n\t\t\t}\n\t\t\tif int32obj1 < int32obj2 {\n\t\t\t\treturn compareLess, true\n\t\t\t}\n\t\t}\n\tcase reflect.Int64:\n\t\t{\n\t\t\tint64obj1, ok := obj1.(int64)\n\t\t\tif !ok {\n\t\t\t\tint64obj1 = obj1Value.Convert(int64Type).Interface().(int64)\n\t\t\t}\n\t\t\tint64obj2, ok := obj2.(int64)\n\t\t\tif !ok {\n\t\t\t\tint64obj2 = obj2Value.Convert(int64Type).Interface().(int64)\n\t\t\t}\n\t\t\tif int64obj1 > int64obj2 {\n\t\t\t\treturn compareGreater, true\n\t\t\t}\n\t\t\tif int64obj1 == int64obj2 {\n\t\t\t\treturn compareEqual, true\n\t\t\t}\n\t\t\tif int64obj1 < int64obj2 {\n\t\t\t\treturn compareLess, true\n\t\t\t}\n\t\t}\n\tcase reflect.Uint:\n\t\t{\n\t\t\tuintobj1, ok := obj1.(uint)\n\t\t\tif !ok {\n\t\t\t\tuintobj1 = obj1Value.Convert(uintType).Interface().(uint)\n\t\t\t}\n\t\t\tuintobj2, ok := obj2.(uint)\n\t\t\tif !ok {\n\t\t\t\tuintobj2 = obj2Value.Convert(uintType).Interface().(uint)\n\t\t\t}\n\t\t\tif uintobj1 > uintobj2 {\n\t\t\t\treturn compareGreater, true\n\t\t\t}\n\t\t\tif uintobj1 == uintobj2 {\n\t\t\t\treturn compareEqual, true\n\t\t\t}\n\t\t\tif uintobj1 < uintobj2 {\n\t\t\t\treturn compareLess, true\n\t\t\t}\n\t\t}\n\tcase reflect.Uint8:\n\t\t{\n\t\t\tuint8obj1, ok := obj1.(uint8)\n\t\t\tif !ok {\n\t\t\t\tuint8obj1 = obj1Value.Convert(uint8Type).Interface().(uint8)\n\t\t\t}\n\t\t\tuint8obj2, ok := obj2.(uint8)\n\t\t\tif !ok {\n\t\t\t\tuint8obj2 = obj2Value.Convert(uint8Type).Interface().(uint8)\n\t\t\t}\n\t\t\tif uint8obj1 > uint8obj2 {\n\t\t\t\treturn compareGreater, true\n\t\t\t}\n\t\t\tif uint8obj1 == uint8obj2 {\n\t\t\t\treturn compareEqual, true\n\t\t\t}\n\t\t\tif uint8obj1 < uint8obj2 {\n\t\t\t\treturn compareLess, true\n\t\t\t}\n\t\t}\n\tcase reflect.Uint16:\n\t\t{\n\t\t\tuint16obj1, ok := obj1.(uint16)\n\t\t\tif !ok {\n\t\t\t\tuint16obj1 = obj1Value.Convert(uint16Type).Interface().(uint16)\n\t\t\t}\n\t\t\tuint16obj2, ok := obj2.(uint16)\n\t\t\tif !ok {\n\t\t\t\tuint16obj2 = obj2Value.Convert(uint16Type).Interface().(uint16)\n\t\t\t}\n\t\t\tif uint16obj1 > uint16obj2 {\n\t\t\t\treturn compareGreater, true\n\t\t\t}\n\t\t\tif uint16obj1 == uint16obj2 {\n\t\t\t\treturn compareEqual, true\n\t\t\t}\n\t\t\tif uint16obj1 < uint16obj2 {\n\t\t\t\treturn compareLess, true\n\t\t\t}\n\t\t}\n\tcase reflect.Uint32:\n\t\t{\n\t\t\tuint32obj1, ok := obj1.(uint32)\n\t\t\tif !ok {\n\t\t\t\tuint32obj1 = obj1Value.Convert(uint32Type).Interface().(uint32)\n\t\t\t}\n\t\t\tuint32obj2, ok := obj2.(uint32)\n\t\t\tif !ok {\n\t\t\t\tuint32obj2 = obj2Value.Convert(uint32Type).Interface().(uint32)\n\t\t\t}\n\t\t\tif uint32obj1 > uint32obj2 {\n\t\t\t\treturn compareGreater, true\n\t\t\t}\n\t\t\tif uint32obj1 == uint32obj2 {\n\t\t\t\treturn compareEqual, true\n\t\t\t}\n\t\t\tif uint32obj1 < uint32obj2 {\n\t\t\t\treturn compareLess, true\n\t\t\t}\n\t\t}\n\tcase reflect.Uint64:\n\t\t{\n\t\t\tuint64obj1, ok := obj1.(uint64)\n\t\t\tif !ok {\n\t\t\t\tuint64obj1 = obj1Value.Convert(uint64Type).Interface().(uint64)\n\t\t\t}\n\t\t\tuint64obj2, ok := obj2.(uint64)\n\t\t\tif !ok {\n\t\t\t\tuint64obj2 = obj2Value.Convert(uint64Type).Interface().(uint64)\n\t\t\t}\n\t\t\tif uint64obj1 > uint64obj2 {\n\t\t\t\treturn compareGreater, true\n\t\t\t}\n\t\t\tif uint64obj1 == uint64obj2 {\n\t\t\t\treturn compareEqual, true\n\t\t\t}\n\t\t\tif uint64obj1 < uint64obj2 {\n\t\t\t\treturn compareLess, true\n\t\t\t}\n\t\t}\n\tcase reflect.Float32:\n\t\t{\n\t\t\tfloat32obj1, ok := obj1.(float32)\n\t\t\tif !ok {\n\t\t\t\tfloat32obj1 = obj1Value.Convert(float32Type).Interface().(float32)\n\t\t\t}\n\t\t\tfloat32obj2, ok := obj2.(float32)\n\t\t\tif !ok {\n\t\t\t\tfloat32obj2 = obj2Value.Convert(float32Type).Interface().(float32)\n\t\t\t}\n\t\t\tif float32obj1 > float32obj2 {\n\t\t\t\treturn compareGreater, true\n\t\t\t}\n\t\t\tif float32obj1 == float32obj2 {\n\t\t\t\treturn compareEqual, true\n\t\t\t}\n\t\t\tif float32obj1 < float32obj2 {\n\t\t\t\treturn compareLess, true\n\t\t\t}\n\t\t}\n\tcase reflect.Float64:\n\t\t{\n\t\t\tfloat64obj1, ok := obj1.(float64)\n\t\t\tif !ok {\n\t\t\t\tfloat64obj1 = obj1Value.Convert(float64Type).Interface().(float64)\n\t\t\t}\n\t\t\tfloat64obj2, ok := obj2.(float64)\n\t\t\tif !ok {\n\t\t\t\tfloat64obj2 = obj2Value.Convert(float64Type).Interface().(float64)\n\t\t\t}\n\t\t\tif float64obj1 > float64obj2 {\n\t\t\t\treturn compareGreater, true\n\t\t\t}\n\t\t\tif float64obj1 == float64obj2 {\n\t\t\t\treturn compareEqual, true\n\t\t\t}\n\t\t\tif float64obj1 < float64obj2 {\n\t\t\t\treturn compareLess, true\n\t\t\t}\n\t\t}\n\tcase reflect.String:\n\t\t{\n\t\t\tstringobj1, ok := obj1.(string)\n\t\t\tif !ok {\n\t\t\t\tstringobj1 = obj1Value.Convert(stringType).Interface().(string)\n\t\t\t}\n\t\t\tstringobj2, ok := obj2.(string)\n\t\t\tif !ok {\n\t\t\t\tstringobj2 = obj2Value.Convert(stringType).Interface().(string)\n\t\t\t}\n\t\t\tif stringobj1 > stringobj2 {\n\t\t\t\treturn compareGreater, true\n\t\t\t}\n\t\t\tif stringobj1 == stringobj2 {\n\t\t\t\treturn compareEqual, true\n\t\t\t}\n\t\t\tif stringobj1 < stringobj2 {\n\t\t\t\treturn compareLess, true\n\t\t\t}\n\t\t}\n\t// Check for known struct types we can check for compare results.\n\tcase reflect.Struct:\n\t\t{\n\t\t\t// All structs enter here. We're not interested in most types.\n\t\t\tif !canConvert(obj1Value, timeType) {\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\t// time.Time can compared!\n\t\t\ttimeObj1, ok := obj1.(time.Time)\n\t\t\tif !ok {\n\t\t\t\ttimeObj1 = obj1Value.Convert(timeType).Interface().(time.Time)\n\t\t\t}\n\n\t\t\ttimeObj2, ok := obj2.(time.Time)\n\t\t\tif !ok {\n\t\t\t\ttimeObj2 = obj2Value.Convert(timeType).Interface().(time.Time)\n\t\t\t}\n\n\t\t\treturn compare(timeObj1.UnixNano(), timeObj2.UnixNano(), reflect.Int64)\n\t\t}\n\tcase reflect.Slice:\n\t\t{\n\t\t\t// We only care about the []byte type.\n\t\t\tif !canConvert(obj1Value, bytesType) {\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\t// []byte can be compared!\n\t\t\tbytesObj1, ok := obj1.([]byte)\n\t\t\tif !ok {\n\t\t\t\tbytesObj1 = obj1Value.Convert(bytesType).Interface().([]byte)\n\t\t\t}\n\t\t\tbytesObj2, ok := obj2.([]byte)\n\t\t\tif !ok {\n\t\t\t\tbytesObj2 = obj2Value.Convert(bytesType).Interface().([]byte)\n\t\t\t}\n\n\t\t\treturn CompareType(bytes.Compare(bytesObj1, bytesObj2)), true\n\t\t}\n\t}\n\n\treturn compareEqual, false\n}\n\n// Greater asserts that the first element is greater than the second\n//\n//\tassert.Greater(t, 2, 1)\n//\tassert.Greater(t, float64(2), float64(1))\n//\tassert.Greater(t, \"b\", \"a\")\nfunc Greater(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\treturn compareTwoValues(t, e1, e2, []CompareType{compareGreater}, \"\\\"%v\\\" is not greater than \\\"%v\\\"\", msgAndArgs...)\n}\n\n// GreaterOrEqual asserts that the first element is greater than or equal to the second\n//\n//\tassert.GreaterOrEqual(t, 2, 1)\n//\tassert.GreaterOrEqual(t, 2, 2)\n//\tassert.GreaterOrEqual(t, \"b\", \"a\")\n//\tassert.GreaterOrEqual(t, \"b\", \"b\")\nfunc GreaterOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\treturn compareTwoValues(t, e1, e2, []CompareType{compareGreater, compareEqual}, \"\\\"%v\\\" is not greater than or equal to \\\"%v\\\"\", msgAndArgs...)\n}\n\n// Less asserts that the first element is less than the second\n//\n//\tassert.Less(t, 1, 2)\n//\tassert.Less(t, float64(1), float64(2))\n//\tassert.Less(t, \"a\", \"b\")\nfunc Less(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\treturn compareTwoValues(t, e1, e2, []CompareType{compareLess}, \"\\\"%v\\\" is not less than \\\"%v\\\"\", msgAndArgs...)\n}\n\n// LessOrEqual asserts that the first element is less than or equal to the second\n//\n//\tassert.LessOrEqual(t, 1, 2)\n//\tassert.LessOrEqual(t, 2, 2)\n//\tassert.LessOrEqual(t, \"a\", \"b\")\n//\tassert.LessOrEqual(t, \"b\", \"b\")\nfunc LessOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\treturn compareTwoValues(t, e1, e2, []CompareType{compareLess, compareEqual}, \"\\\"%v\\\" is not less than or equal to \\\"%v\\\"\", msgAndArgs...)\n}\n\n// Positive asserts that the specified element is positive\n//\n//\tassert.Positive(t, 1)\n//\tassert.Positive(t, 1.23)\nfunc Positive(t TestingT, e interface{}, msgAndArgs ...interface{}) bool {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\tzero := reflect.Zero(reflect.TypeOf(e))\n\treturn compareTwoValues(t, e, zero.Interface(), []CompareType{compareGreater}, \"\\\"%v\\\" is not positive\", msgAndArgs...)\n}\n\n// Negative asserts that the specified element is negative\n//\n//\tassert.Negative(t, -1)\n//\tassert.Negative(t, -1.23)\nfunc Negative(t TestingT, e interface{}, msgAndArgs ...interface{}) bool {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\tzero := reflect.Zero(reflect.TypeOf(e))\n\treturn compareTwoValues(t, e, zero.Interface(), []CompareType{compareLess}, \"\\\"%v\\\" is not negative\", msgAndArgs...)\n}\n\nfunc compareTwoValues(t TestingT, e1 interface{}, e2 interface{}, allowedComparesResults []CompareType, failMessage string, msgAndArgs ...interface{}) bool {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\n\te1Kind := reflect.ValueOf(e1).Kind()\n\te2Kind := reflect.ValueOf(e2).Kind()\n\tif e1Kind != e2Kind {\n\t\treturn Fail(t, \"Elements should be the same type\", msgAndArgs...)\n\t}\n\n\tcompareResult, isComparable := compare(e1, e2, e1Kind)\n\tif !isComparable {\n\t\treturn Fail(t, fmt.Sprintf(\"Can not compare type \\\"%s\\\"\", reflect.TypeOf(e1)), msgAndArgs...)\n\t}\n\n\tif !containsValue(allowedComparesResults, compareResult) {\n\t\treturn Fail(t, fmt.Sprintf(failMessage, e1, e2), msgAndArgs...)\n\t}\n\n\treturn true\n}\n\nfunc containsValue(values []CompareType, value CompareType) bool {\n\tfor _, v := range values {\n\t\tif v == value {\n\t\t\treturn true\n\t\t}\n\t}\n\n\treturn false\n}\n\n// CompareErrors asserts two errors\nfunc CompareErrors(err1, err2 error) bool {\n\tif err1 == nil && err2 == nil {\n\t\treturn true\n\t}\n\n\tif err1 == nil || err2 == nil {\n\t\treturn false\n\t}\n\n\tif err1.Error() != err2.Error() {\n\t\treturn false\n\t}\n\n\treturn true\n}\n"
  },
  {
    "path": "internal/assert/assertion_compare_can_convert.go",
    "content": "// Copied from https://github.com/stretchr/testify/blob/1333b5d3bda8cf5aedcf3e1aaa95cac28aaab892/assert/assertion_compare_can_convert.go\n\n// Copyright 2020 Mat Ryer, Tyler Bunnell and all contributors. All rights reserved.\n// Use of this source code is governed by an MIT-style license that can be found in\n// the THIRD-PARTY-NOTICES file.\n\n//go:build go1.17\n// +build go1.17\n\npackage assert\n\nimport \"reflect\"\n\n// Wrapper around reflect.Value.CanConvert, for compatibility\n// reasons.\nfunc canConvert(value reflect.Value, to reflect.Type) bool {\n\treturn value.CanConvert(to)\n}\n"
  },
  {
    "path": "internal/assert/assertion_compare_go1.17_test.go",
    "content": "// Copied from https://github.com/stretchr/testify/blob/1333b5d3bda8cf5aedcf3e1aaa95cac28aaab892/assert/assertion_compare_go1.17_test.go\n\n// Copyright 2020 Mat Ryer, Tyler Bunnell and all contributors. All rights reserved.\n// Use of this source code is governed by an MIT-style license that can be found in\n// the THIRD-PARTY-NOTICES file.\n\n//go:build go1.17\n// +build go1.17\n\npackage assert\n\nimport (\n\t\"bytes\"\n\t\"reflect\"\n\t\"testing\"\n\t\"time\"\n)\n\nfunc TestCompare17(t *testing.T) {\n\ttype customTime time.Time\n\ttype customBytes []byte\n\tfor _, currCase := range []struct {\n\t\tless    interface{}\n\t\tgreater interface{}\n\t\tcType   string\n\t}{\n\t\t{less: time.Now(), greater: time.Now().Add(time.Hour), cType: \"time.Time\"},\n\t\t{less: customTime(time.Now()), greater: customTime(time.Now().Add(time.Hour)), cType: \"time.Time\"},\n\t\t{less: []byte{1, 1}, greater: []byte{1, 2}, cType: \"[]byte\"},\n\t\t{less: customBytes([]byte{1, 1}), greater: customBytes([]byte{1, 2}), cType: \"[]byte\"},\n\t} {\n\t\tresLess, isComparable := compare(currCase.less, currCase.greater, reflect.ValueOf(currCase.less).Kind())\n\t\tif !isComparable {\n\t\t\tt.Error(\"object should be comparable for type \" + currCase.cType)\n\t\t}\n\n\t\tif resLess != compareLess {\n\t\t\tt.Errorf(\"object less (%v) should be less than greater (%v) for type \"+currCase.cType,\n\t\t\t\tcurrCase.less, currCase.greater)\n\t\t}\n\n\t\tresGreater, isComparable := compare(currCase.greater, currCase.less, reflect.ValueOf(currCase.less).Kind())\n\t\tif !isComparable {\n\t\t\tt.Error(\"object are comparable for type \" + currCase.cType)\n\t\t}\n\n\t\tif resGreater != compareGreater {\n\t\t\tt.Errorf(\"object greater should be greater than less for type \" + currCase.cType)\n\t\t}\n\n\t\tresEqual, isComparable := compare(currCase.less, currCase.less, reflect.ValueOf(currCase.less).Kind())\n\t\tif !isComparable {\n\t\t\tt.Error(\"object are comparable for type \" + currCase.cType)\n\t\t}\n\n\t\tif resEqual != 0 {\n\t\t\tt.Errorf(\"objects should be equal for type \" + currCase.cType)\n\t\t}\n\t}\n}\n\nfunc TestGreater17(t *testing.T) {\n\tmockT := new(testing.T)\n\n\tif !Greater(mockT, 2, 1) {\n\t\tt.Error(\"Greater should return true\")\n\t}\n\n\tif Greater(mockT, 1, 1) {\n\t\tt.Error(\"Greater should return false\")\n\t}\n\n\tif Greater(mockT, 1, 2) {\n\t\tt.Error(\"Greater should return false\")\n\t}\n\n\t// Check error report\n\tfor _, currCase := range []struct {\n\t\tless    interface{}\n\t\tgreater interface{}\n\t\tmsg     string\n\t}{\n\t\t{less: []byte{1, 1}, greater: []byte{1, 2}, msg: `\"[1 1]\" is not greater than \"[1 2]\"`},\n\t\t{less: time.Time{}, greater: time.Time{}.Add(time.Hour), msg: `\"0001-01-01 00:00:00 +0000 UTC\" is not greater than \"0001-01-01 01:00:00 +0000 UTC\"`},\n\t} {\n\t\tout := &outputT{buf: bytes.NewBuffer(nil)}\n\t\tFalse(t, Greater(out, currCase.less, currCase.greater))\n\t\tContains(t, out.buf.String(), currCase.msg)\n\t\tContains(t, out.helpers, \"go.mongodb.org/mongo-driver/v2/internal/assert.Greater\")\n\t}\n}\n\nfunc TestGreaterOrEqual17(t *testing.T) {\n\tmockT := new(testing.T)\n\n\tif !GreaterOrEqual(mockT, 2, 1) {\n\t\tt.Error(\"GreaterOrEqual should return true\")\n\t}\n\n\tif !GreaterOrEqual(mockT, 1, 1) {\n\t\tt.Error(\"GreaterOrEqual should return true\")\n\t}\n\n\tif GreaterOrEqual(mockT, 1, 2) {\n\t\tt.Error(\"GreaterOrEqual should return false\")\n\t}\n\n\t// Check error report\n\tfor _, currCase := range []struct {\n\t\tless    interface{}\n\t\tgreater interface{}\n\t\tmsg     string\n\t}{\n\t\t{less: []byte{1, 1}, greater: []byte{1, 2}, msg: `\"[1 1]\" is not greater than or equal to \"[1 2]\"`},\n\t\t{less: time.Time{}, greater: time.Time{}.Add(time.Hour), msg: `\"0001-01-01 00:00:00 +0000 UTC\" is not greater than or equal to \"0001-01-01 01:00:00 +0000 UTC\"`},\n\t} {\n\t\tout := &outputT{buf: bytes.NewBuffer(nil)}\n\t\tFalse(t, GreaterOrEqual(out, currCase.less, currCase.greater))\n\t\tContains(t, out.buf.String(), currCase.msg)\n\t\tContains(t, out.helpers, \"go.mongodb.org/mongo-driver/v2/internal/assert.GreaterOrEqual\")\n\t}\n}\n\nfunc TestLess17(t *testing.T) {\n\tmockT := new(testing.T)\n\n\tif !Less(mockT, 1, 2) {\n\t\tt.Error(\"Less should return true\")\n\t}\n\n\tif Less(mockT, 1, 1) {\n\t\tt.Error(\"Less should return false\")\n\t}\n\n\tif Less(mockT, 2, 1) {\n\t\tt.Error(\"Less should return false\")\n\t}\n\n\t// Check error report\n\tfor _, currCase := range []struct {\n\t\tless    interface{}\n\t\tgreater interface{}\n\t\tmsg     string\n\t}{\n\t\t{less: []byte{1, 1}, greater: []byte{1, 2}, msg: `\"[1 2]\" is not less than \"[1 1]\"`},\n\t\t{less: time.Time{}, greater: time.Time{}.Add(time.Hour), msg: `\"0001-01-01 01:00:00 +0000 UTC\" is not less than \"0001-01-01 00:00:00 +0000 UTC\"`},\n\t} {\n\t\tout := &outputT{buf: bytes.NewBuffer(nil)}\n\t\tFalse(t, Less(out, currCase.greater, currCase.less))\n\t\tContains(t, out.buf.String(), currCase.msg)\n\t\tContains(t, out.helpers, \"go.mongodb.org/mongo-driver/v2/internal/assert.Less\")\n\t}\n}\n\nfunc TestLessOrEqual17(t *testing.T) {\n\tmockT := new(testing.T)\n\n\tif !LessOrEqual(mockT, 1, 2) {\n\t\tt.Error(\"LessOrEqual should return true\")\n\t}\n\n\tif !LessOrEqual(mockT, 1, 1) {\n\t\tt.Error(\"LessOrEqual should return true\")\n\t}\n\n\tif LessOrEqual(mockT, 2, 1) {\n\t\tt.Error(\"LessOrEqual should return false\")\n\t}\n\n\t// Check error report\n\tfor _, currCase := range []struct {\n\t\tless    interface{}\n\t\tgreater interface{}\n\t\tmsg     string\n\t}{\n\t\t{less: []byte{1, 1}, greater: []byte{1, 2}, msg: `\"[1 2]\" is not less than or equal to \"[1 1]\"`},\n\t\t{less: time.Time{}, greater: time.Time{}.Add(time.Hour), msg: `\"0001-01-01 01:00:00 +0000 UTC\" is not less than or equal to \"0001-01-01 00:00:00 +0000 UTC\"`},\n\t} {\n\t\tout := &outputT{buf: bytes.NewBuffer(nil)}\n\t\tFalse(t, LessOrEqual(out, currCase.greater, currCase.less))\n\t\tContains(t, out.buf.String(), currCase.msg)\n\t\tContains(t, out.helpers, \"go.mongodb.org/mongo-driver/v2/internal/assert.LessOrEqual\")\n\t}\n}\n"
  },
  {
    "path": "internal/assert/assertion_compare_legacy.go",
    "content": "// Copied from https://github.com/stretchr/testify/blob/1333b5d3bda8cf5aedcf3e1aaa95cac28aaab892/assert/assertion_compare_legacy.go\n\n// Copyright 2020 Mat Ryer, Tyler Bunnell and all contributors. All rights reserved.\n// Use of this source code is governed by an MIT-style license that can be found in\n// the THIRD-PARTY-NOTICES file.\n\n//go:build !go1.17\n// +build !go1.17\n\npackage assert\n\nimport \"reflect\"\n\n// Older versions of Go does not have the reflect.Value.CanConvert\n// method.\nfunc canConvert(value reflect.Value, to reflect.Type) bool {\n\treturn false\n}\n"
  },
  {
    "path": "internal/assert/assertion_compare_test.go",
    "content": "// Copied from https://github.com/stretchr/testify/blob/1333b5d3bda8cf5aedcf3e1aaa95cac28aaab892/assert/assertion_compare_test.go\n\n// Copyright 2020 Mat Ryer, Tyler Bunnell and all contributors. All rights reserved.\n// Use of this source code is governed by an MIT-style license that can be found in\n// the THIRD-PARTY-NOTICES file.\n\npackage assert\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"reflect\"\n\t\"runtime\"\n\t\"testing\"\n)\n\nfunc TestCompare(t *testing.T) {\n\ttype customInt int\n\ttype customInt8 int8\n\ttype customInt16 int16\n\ttype customInt32 int32\n\ttype customInt64 int64\n\ttype customUInt uint\n\ttype customUInt8 uint8\n\ttype customUInt16 uint16\n\ttype customUInt32 uint32\n\ttype customUInt64 uint64\n\ttype customFloat32 float32\n\ttype customFloat64 float64\n\ttype customString string\n\tfor _, currCase := range []struct {\n\t\tless    interface{}\n\t\tgreater interface{}\n\t\tcType   string\n\t}{\n\t\t{less: customString(\"a\"), greater: customString(\"b\"), cType: \"string\"},\n\t\t{less: \"a\", greater: \"b\", cType: \"string\"},\n\t\t{less: customInt(1), greater: customInt(2), cType: \"int\"},\n\t\t{less: int(1), greater: int(2), cType: \"int\"},\n\t\t{less: customInt8(1), greater: customInt8(2), cType: \"int8\"},\n\t\t{less: int8(1), greater: int8(2), cType: \"int8\"},\n\t\t{less: customInt16(1), greater: customInt16(2), cType: \"int16\"},\n\t\t{less: int16(1), greater: int16(2), cType: \"int16\"},\n\t\t{less: customInt32(1), greater: customInt32(2), cType: \"int32\"},\n\t\t{less: int32(1), greater: int32(2), cType: \"int32\"},\n\t\t{less: customInt64(1), greater: customInt64(2), cType: \"int64\"},\n\t\t{less: int64(1), greater: int64(2), cType: \"int64\"},\n\t\t{less: customUInt(1), greater: customUInt(2), cType: \"uint\"},\n\t\t{less: uint8(1), greater: uint8(2), cType: \"uint8\"},\n\t\t{less: customUInt8(1), greater: customUInt8(2), cType: \"uint8\"},\n\t\t{less: uint16(1), greater: uint16(2), cType: \"uint16\"},\n\t\t{less: customUInt16(1), greater: customUInt16(2), cType: \"uint16\"},\n\t\t{less: uint32(1), greater: uint32(2), cType: \"uint32\"},\n\t\t{less: customUInt32(1), greater: customUInt32(2), cType: \"uint32\"},\n\t\t{less: uint64(1), greater: uint64(2), cType: \"uint64\"},\n\t\t{less: customUInt64(1), greater: customUInt64(2), cType: \"uint64\"},\n\t\t{less: float32(1.23), greater: float32(2.34), cType: \"float32\"},\n\t\t{less: customFloat32(1.23), greater: customFloat32(2.23), cType: \"float32\"},\n\t\t{less: float64(1.23), greater: float64(2.34), cType: \"float64\"},\n\t\t{less: customFloat64(1.23), greater: customFloat64(2.34), cType: \"float64\"},\n\t} {\n\t\tresLess, isComparable := compare(currCase.less, currCase.greater, reflect.ValueOf(currCase.less).Kind())\n\t\tif !isComparable {\n\t\t\tt.Error(\"object should be comparable for type \" + currCase.cType)\n\t\t}\n\n\t\tif resLess != compareLess {\n\t\t\tt.Errorf(\"object less (%v) should be less than greater (%v) for type \"+currCase.cType,\n\t\t\t\tcurrCase.less, currCase.greater)\n\t\t}\n\n\t\tresGreater, isComparable := compare(currCase.greater, currCase.less, reflect.ValueOf(currCase.less).Kind())\n\t\tif !isComparable {\n\t\t\tt.Error(\"object are comparable for type \" + currCase.cType)\n\t\t}\n\n\t\tif resGreater != compareGreater {\n\t\t\tt.Errorf(\"object greater should be greater than less for type \" + currCase.cType)\n\t\t}\n\n\t\tresEqual, isComparable := compare(currCase.less, currCase.less, reflect.ValueOf(currCase.less).Kind())\n\t\tif !isComparable {\n\t\t\tt.Error(\"object are comparable for type \" + currCase.cType)\n\t\t}\n\n\t\tif resEqual != 0 {\n\t\t\tt.Errorf(\"objects should be equal for type \" + currCase.cType)\n\t\t}\n\t}\n}\n\ntype outputT struct {\n\tbuf     *bytes.Buffer\n\thelpers map[string]struct{}\n}\n\n// Implements TestingT\nfunc (t *outputT) Errorf(format string, args ...interface{}) {\n\ts := fmt.Sprintf(format, args...)\n\tt.buf.WriteString(s)\n}\n\nfunc (t *outputT) Helper() {\n\tif t.helpers == nil {\n\t\tt.helpers = make(map[string]struct{})\n\t}\n\tt.helpers[callerName(1)] = struct{}{}\n}\n\n// callerName gives the function name (qualified with a package path)\n// for the caller after skip frames (where 0 means the current function).\nfunc callerName(skip int) string {\n\t// Make room for the skip PC.\n\tvar pc [1]uintptr\n\tn := runtime.Callers(skip+2, pc[:]) // skip + runtime.Callers + callerName\n\tif n == 0 {\n\t\tpanic(\"testing: zero callers found\")\n\t}\n\tframes := runtime.CallersFrames(pc[:n])\n\tframe, _ := frames.Next()\n\treturn frame.Function\n}\n\nfunc TestGreater(t *testing.T) {\n\tmockT := new(testing.T)\n\n\tif !Greater(mockT, 2, 1) {\n\t\tt.Error(\"Greater should return true\")\n\t}\n\n\tif Greater(mockT, 1, 1) {\n\t\tt.Error(\"Greater should return false\")\n\t}\n\n\tif Greater(mockT, 1, 2) {\n\t\tt.Error(\"Greater should return false\")\n\t}\n\n\t// Check error report\n\tfor _, currCase := range []struct {\n\t\tless    interface{}\n\t\tgreater interface{}\n\t\tmsg     string\n\t}{\n\t\t{less: \"a\", greater: \"b\", msg: `\"a\" is not greater than \"b\"`},\n\t\t{less: int(1), greater: int(2), msg: `\"1\" is not greater than \"2\"`},\n\t\t{less: int8(1), greater: int8(2), msg: `\"1\" is not greater than \"2\"`},\n\t\t{less: int16(1), greater: int16(2), msg: `\"1\" is not greater than \"2\"`},\n\t\t{less: int32(1), greater: int32(2), msg: `\"1\" is not greater than \"2\"`},\n\t\t{less: int64(1), greater: int64(2), msg: `\"1\" is not greater than \"2\"`},\n\t\t{less: uint8(1), greater: uint8(2), msg: `\"1\" is not greater than \"2\"`},\n\t\t{less: uint16(1), greater: uint16(2), msg: `\"1\" is not greater than \"2\"`},\n\t\t{less: uint32(1), greater: uint32(2), msg: `\"1\" is not greater than \"2\"`},\n\t\t{less: uint64(1), greater: uint64(2), msg: `\"1\" is not greater than \"2\"`},\n\t\t{less: float32(1.23), greater: float32(2.34), msg: `\"1.23\" is not greater than \"2.34\"`},\n\t\t{less: float64(1.23), greater: float64(2.34), msg: `\"1.23\" is not greater than \"2.34\"`},\n\t} {\n\t\tout := &outputT{buf: bytes.NewBuffer(nil)}\n\t\tFalse(t, Greater(out, currCase.less, currCase.greater))\n\t\tContains(t, out.buf.String(), currCase.msg)\n\t\tContains(t, out.helpers, \"go.mongodb.org/mongo-driver/v2/internal/assert.Greater\")\n\t}\n}\n\nfunc TestGreaterOrEqual(t *testing.T) {\n\tmockT := new(testing.T)\n\n\tif !GreaterOrEqual(mockT, 2, 1) {\n\t\tt.Error(\"GreaterOrEqual should return true\")\n\t}\n\n\tif !GreaterOrEqual(mockT, 1, 1) {\n\t\tt.Error(\"GreaterOrEqual should return true\")\n\t}\n\n\tif GreaterOrEqual(mockT, 1, 2) {\n\t\tt.Error(\"GreaterOrEqual should return false\")\n\t}\n\n\t// Check error report\n\tfor _, currCase := range []struct {\n\t\tless    interface{}\n\t\tgreater interface{}\n\t\tmsg     string\n\t}{\n\t\t{less: \"a\", greater: \"b\", msg: `\"a\" is not greater than or equal to \"b\"`},\n\t\t{less: int(1), greater: int(2), msg: `\"1\" is not greater than or equal to \"2\"`},\n\t\t{less: int8(1), greater: int8(2), msg: `\"1\" is not greater than or equal to \"2\"`},\n\t\t{less: int16(1), greater: int16(2), msg: `\"1\" is not greater than or equal to \"2\"`},\n\t\t{less: int32(1), greater: int32(2), msg: `\"1\" is not greater than or equal to \"2\"`},\n\t\t{less: int64(1), greater: int64(2), msg: `\"1\" is not greater than or equal to \"2\"`},\n\t\t{less: uint8(1), greater: uint8(2), msg: `\"1\" is not greater than or equal to \"2\"`},\n\t\t{less: uint16(1), greater: uint16(2), msg: `\"1\" is not greater than or equal to \"2\"`},\n\t\t{less: uint32(1), greater: uint32(2), msg: `\"1\" is not greater than or equal to \"2\"`},\n\t\t{less: uint64(1), greater: uint64(2), msg: `\"1\" is not greater than or equal to \"2\"`},\n\t\t{less: float32(1.23), greater: float32(2.34), msg: `\"1.23\" is not greater than or equal to \"2.34\"`},\n\t\t{less: float64(1.23), greater: float64(2.34), msg: `\"1.23\" is not greater than or equal to \"2.34\"`},\n\t} {\n\t\tout := &outputT{buf: bytes.NewBuffer(nil)}\n\t\tFalse(t, GreaterOrEqual(out, currCase.less, currCase.greater))\n\t\tContains(t, out.buf.String(), currCase.msg)\n\t\tContains(t, out.helpers, \"go.mongodb.org/mongo-driver/v2/internal/assert.GreaterOrEqual\")\n\t}\n}\n\nfunc TestLess(t *testing.T) {\n\tmockT := new(testing.T)\n\n\tif !Less(mockT, 1, 2) {\n\t\tt.Error(\"Less should return true\")\n\t}\n\n\tif Less(mockT, 1, 1) {\n\t\tt.Error(\"Less should return false\")\n\t}\n\n\tif Less(mockT, 2, 1) {\n\t\tt.Error(\"Less should return false\")\n\t}\n\n\t// Check error report\n\tfor _, currCase := range []struct {\n\t\tless    interface{}\n\t\tgreater interface{}\n\t\tmsg     string\n\t}{\n\t\t{less: \"a\", greater: \"b\", msg: `\"b\" is not less than \"a\"`},\n\t\t{less: int(1), greater: int(2), msg: `\"2\" is not less than \"1\"`},\n\t\t{less: int8(1), greater: int8(2), msg: `\"2\" is not less than \"1\"`},\n\t\t{less: int16(1), greater: int16(2), msg: `\"2\" is not less than \"1\"`},\n\t\t{less: int32(1), greater: int32(2), msg: `\"2\" is not less than \"1\"`},\n\t\t{less: int64(1), greater: int64(2), msg: `\"2\" is not less than \"1\"`},\n\t\t{less: uint8(1), greater: uint8(2), msg: `\"2\" is not less than \"1\"`},\n\t\t{less: uint16(1), greater: uint16(2), msg: `\"2\" is not less than \"1\"`},\n\t\t{less: uint32(1), greater: uint32(2), msg: `\"2\" is not less than \"1\"`},\n\t\t{less: uint64(1), greater: uint64(2), msg: `\"2\" is not less than \"1\"`},\n\t\t{less: float32(1.23), greater: float32(2.34), msg: `\"2.34\" is not less than \"1.23\"`},\n\t\t{less: float64(1.23), greater: float64(2.34), msg: `\"2.34\" is not less than \"1.23\"`},\n\t} {\n\t\tout := &outputT{buf: bytes.NewBuffer(nil)}\n\t\tFalse(t, Less(out, currCase.greater, currCase.less))\n\t\tContains(t, out.buf.String(), currCase.msg)\n\t\tContains(t, out.helpers, \"go.mongodb.org/mongo-driver/v2/internal/assert.Less\")\n\t}\n}\n\nfunc TestLessOrEqual(t *testing.T) {\n\tmockT := new(testing.T)\n\n\tif !LessOrEqual(mockT, 1, 2) {\n\t\tt.Error(\"LessOrEqual should return true\")\n\t}\n\n\tif !LessOrEqual(mockT, 1, 1) {\n\t\tt.Error(\"LessOrEqual should return true\")\n\t}\n\n\tif LessOrEqual(mockT, 2, 1) {\n\t\tt.Error(\"LessOrEqual should return false\")\n\t}\n\n\t// Check error report\n\tfor _, currCase := range []struct {\n\t\tless    interface{}\n\t\tgreater interface{}\n\t\tmsg     string\n\t}{\n\t\t{less: \"a\", greater: \"b\", msg: `\"b\" is not less than or equal to \"a\"`},\n\t\t{less: int(1), greater: int(2), msg: `\"2\" is not less than or equal to \"1\"`},\n\t\t{less: int8(1), greater: int8(2), msg: `\"2\" is not less than or equal to \"1\"`},\n\t\t{less: int16(1), greater: int16(2), msg: `\"2\" is not less than or equal to \"1\"`},\n\t\t{less: int32(1), greater: int32(2), msg: `\"2\" is not less than or equal to \"1\"`},\n\t\t{less: int64(1), greater: int64(2), msg: `\"2\" is not less than or equal to \"1\"`},\n\t\t{less: uint8(1), greater: uint8(2), msg: `\"2\" is not less than or equal to \"1\"`},\n\t\t{less: uint16(1), greater: uint16(2), msg: `\"2\" is not less than or equal to \"1\"`},\n\t\t{less: uint32(1), greater: uint32(2), msg: `\"2\" is not less than or equal to \"1\"`},\n\t\t{less: uint64(1), greater: uint64(2), msg: `\"2\" is not less than or equal to \"1\"`},\n\t\t{less: float32(1.23), greater: float32(2.34), msg: `\"2.34\" is not less than or equal to \"1.23\"`},\n\t\t{less: float64(1.23), greater: float64(2.34), msg: `\"2.34\" is not less than or equal to \"1.23\"`},\n\t} {\n\t\tout := &outputT{buf: bytes.NewBuffer(nil)}\n\t\tFalse(t, LessOrEqual(out, currCase.greater, currCase.less))\n\t\tContains(t, out.buf.String(), currCase.msg)\n\t\tContains(t, out.helpers, \"go.mongodb.org/mongo-driver/v2/internal/assert.LessOrEqual\")\n\t}\n}\n\nfunc TestPositive(t *testing.T) {\n\tmockT := new(testing.T)\n\n\tif !Positive(mockT, 1) {\n\t\tt.Error(\"Positive should return true\")\n\t}\n\n\tif !Positive(mockT, 1.23) {\n\t\tt.Error(\"Positive should return true\")\n\t}\n\n\tif Positive(mockT, -1) {\n\t\tt.Error(\"Positive should return false\")\n\t}\n\n\tif Positive(mockT, -1.23) {\n\t\tt.Error(\"Positive should return false\")\n\t}\n\n\t// Check error report\n\tfor _, currCase := range []struct {\n\t\te   interface{}\n\t\tmsg string\n\t}{\n\t\t{e: int(-1), msg: `\"-1\" is not positive`},\n\t\t{e: int8(-1), msg: `\"-1\" is not positive`},\n\t\t{e: int16(-1), msg: `\"-1\" is not positive`},\n\t\t{e: int32(-1), msg: `\"-1\" is not positive`},\n\t\t{e: int64(-1), msg: `\"-1\" is not positive`},\n\t\t{e: float32(-1.23), msg: `\"-1.23\" is not positive`},\n\t\t{e: float64(-1.23), msg: `\"-1.23\" is not positive`},\n\t} {\n\t\tout := &outputT{buf: bytes.NewBuffer(nil)}\n\t\tFalse(t, Positive(out, currCase.e))\n\t\tContains(t, out.buf.String(), currCase.msg)\n\t\tContains(t, out.helpers, \"go.mongodb.org/mongo-driver/v2/internal/assert.Positive\")\n\t}\n}\n\nfunc TestNegative(t *testing.T) {\n\tmockT := new(testing.T)\n\n\tif !Negative(mockT, -1) {\n\t\tt.Error(\"Negative should return true\")\n\t}\n\n\tif !Negative(mockT, -1.23) {\n\t\tt.Error(\"Negative should return true\")\n\t}\n\n\tif Negative(mockT, 1) {\n\t\tt.Error(\"Negative should return false\")\n\t}\n\n\tif Negative(mockT, 1.23) {\n\t\tt.Error(\"Negative should return false\")\n\t}\n\n\t// Check error report\n\tfor _, currCase := range []struct {\n\t\te   interface{}\n\t\tmsg string\n\t}{\n\t\t{e: int(1), msg: `\"1\" is not negative`},\n\t\t{e: int8(1), msg: `\"1\" is not negative`},\n\t\t{e: int16(1), msg: `\"1\" is not negative`},\n\t\t{e: int32(1), msg: `\"1\" is not negative`},\n\t\t{e: int64(1), msg: `\"1\" is not negative`},\n\t\t{e: float32(1.23), msg: `\"1.23\" is not negative`},\n\t\t{e: float64(1.23), msg: `\"1.23\" is not negative`},\n\t} {\n\t\tout := &outputT{buf: bytes.NewBuffer(nil)}\n\t\tFalse(t, Negative(out, currCase.e))\n\t\tContains(t, out.buf.String(), currCase.msg)\n\t\tContains(t, out.helpers, \"go.mongodb.org/mongo-driver/v2/internal/assert.Negative\")\n\t}\n}\n\nfunc Test_compareTwoValuesDifferentValuesTypes(t *testing.T) {\n\tmockT := new(testing.T)\n\n\tfor _, currCase := range []struct {\n\t\tv1            interface{}\n\t\tv2            interface{}\n\t\tcompareResult bool\n\t}{\n\t\t{v1: 123, v2: \"abc\"},\n\t\t{v1: \"abc\", v2: 123456},\n\t\t{v1: float64(12), v2: \"123\"},\n\t\t{v1: \"float(12)\", v2: float64(1)},\n\t} {\n\t\tcompareResult := compareTwoValues(mockT, currCase.v1, currCase.v2, []CompareType{compareLess, compareEqual, compareGreater}, \"testFailMessage\")\n\t\tFalse(t, compareResult)\n\t}\n}\n\nfunc Test_compareTwoValuesNotComparableValues(t *testing.T) {\n\tmockT := new(testing.T)\n\n\ttype CompareStruct struct{}\n\n\tfor _, currCase := range []struct {\n\t\tv1 interface{}\n\t\tv2 interface{}\n\t}{\n\t\t{v1: CompareStruct{}, v2: CompareStruct{}},\n\t\t{v1: map[string]int{}, v2: map[string]int{}},\n\t\t{v1: make([]int, 5), v2: make([]int, 5)},\n\t} {\n\t\tcompareResult := compareTwoValues(mockT, currCase.v1, currCase.v2, []CompareType{compareLess, compareEqual, compareGreater}, \"testFailMessage\")\n\t\tFalse(t, compareResult)\n\t}\n}\n\nfunc Test_compareTwoValuesCorrectCompareResult(t *testing.T) {\n\tmockT := new(testing.T)\n\n\tfor _, currCase := range []struct {\n\t\tv1           interface{}\n\t\tv2           interface{}\n\t\tcompareTypes []CompareType\n\t}{\n\t\t{v1: 1, v2: 2, compareTypes: []CompareType{compareLess}},\n\t\t{v1: 1, v2: 2, compareTypes: []CompareType{compareLess, compareEqual}},\n\t\t{v1: 2, v2: 2, compareTypes: []CompareType{compareGreater, compareEqual}},\n\t\t{v1: 2, v2: 2, compareTypes: []CompareType{compareEqual}},\n\t\t{v1: 2, v2: 1, compareTypes: []CompareType{compareEqual, compareGreater}},\n\t\t{v1: 2, v2: 1, compareTypes: []CompareType{compareGreater}},\n\t} {\n\t\tcompareResult := compareTwoValues(mockT, currCase.v1, currCase.v2, currCase.compareTypes, \"testFailMessage\")\n\t\tTrue(t, compareResult)\n\t}\n}\n\nfunc Test_containsValue(t *testing.T) {\n\tfor _, currCase := range []struct {\n\t\tvalues []CompareType\n\t\tvalue  CompareType\n\t\tresult bool\n\t}{\n\t\t{values: []CompareType{compareGreater}, value: compareGreater, result: true},\n\t\t{values: []CompareType{compareGreater, compareLess}, value: compareGreater, result: true},\n\t\t{values: []CompareType{compareGreater, compareLess}, value: compareLess, result: true},\n\t\t{values: []CompareType{compareGreater, compareLess}, value: compareEqual, result: false},\n\t} {\n\t\tcompareResult := containsValue(currCase.values, currCase.value)\n\t\tEqual(t, currCase.result, compareResult)\n\t}\n}\n\nfunc TestComparingMsgAndArgsForwarding(t *testing.T) {\n\tmsgAndArgs := []interface{}{\"format %s %x\", \"this\", 0xc001}\n\texpectedOutput := \"format this c001\\n\"\n\tfuncs := []func(t TestingT){\n\t\tfunc(t TestingT) { Greater(t, 1, 2, msgAndArgs...) },\n\t\tfunc(t TestingT) { GreaterOrEqual(t, 1, 2, msgAndArgs...) },\n\t\tfunc(t TestingT) { Less(t, 2, 1, msgAndArgs...) },\n\t\tfunc(t TestingT) { LessOrEqual(t, 2, 1, msgAndArgs...) },\n\t\tfunc(t TestingT) { Positive(t, 0, msgAndArgs...) },\n\t\tfunc(t TestingT) { Negative(t, 0, msgAndArgs...) },\n\t}\n\tfor _, f := range funcs {\n\t\tout := &outputT{buf: bytes.NewBuffer(nil)}\n\t\tf(out)\n\t\tContains(t, out.buf.String(), expectedOutput)\n\t}\n}\n"
  },
  {
    "path": "internal/assert/assertion_format.go",
    "content": "// Copied from https://github.com/stretchr/testify/blob/1333b5d3bda8cf5aedcf3e1aaa95cac28aaab892/assert/assertion_format.go\n\n// Copyright 2020 Mat Ryer, Tyler Bunnell and all contributors. All rights reserved.\n// Use of this source code is governed by an MIT-style license that can be found in\n// the THIRD-PARTY-NOTICES file.\n\npackage assert\n\nimport (\n\ttime \"time\"\n)\n\n// Containsf asserts that the specified string, list(array, slice...) or map contains the\n// specified substring or element.\n//\n//\tassert.Containsf(t, \"Hello World\", \"World\", \"error message %s\", \"formatted\")\n//\tassert.Containsf(t, [\"Hello\", \"World\"], \"World\", \"error message %s\", \"formatted\")\n//\tassert.Containsf(t, {\"Hello\": \"World\"}, \"Hello\", \"error message %s\", \"formatted\")\nfunc Containsf(t TestingT, s interface{}, contains interface{}, msg string, args ...interface{}) bool {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\treturn Contains(t, s, contains, append([]interface{}{msg}, args...)...)\n}\n\n// ElementsMatchf asserts that the specified listA(array, slice...) is equal to specified\n// listB(array, slice...) ignoring the order of the elements. If there are duplicate elements,\n// the number of appearances of each of them in both lists should match.\n//\n// assert.ElementsMatchf(t, [1, 3, 2, 3], [1, 3, 3, 2], \"error message %s\", \"formatted\")\nfunc ElementsMatchf(t TestingT, listA interface{}, listB interface{}, msg string, args ...interface{}) bool {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\treturn ElementsMatch(t, listA, listB, append([]interface{}{msg}, args...)...)\n}\n\n// Equalf asserts that two objects are equal.\n//\n//\tassert.Equalf(t, 123, 123, \"error message %s\", \"formatted\")\n//\n// Pointer variable equality is determined based on the equality of the\n// referenced values (as opposed to the memory addresses). Function equality\n// cannot be determined and will always fail.\nfunc Equalf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\treturn Equal(t, expected, actual, append([]interface{}{msg}, args...)...)\n}\n\n// EqualErrorf asserts that a function returned an error (i.e. not `nil`)\n// and that it is equal to the provided error.\n//\n//\tactualObj, err := SomeFunction()\n//\tassert.EqualErrorf(t, err,  expectedErrorString, \"error message %s\", \"formatted\")\nfunc EqualErrorf(t TestingT, theError error, errString string, msg string, args ...interface{}) bool {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\treturn EqualError(t, theError, errString, append([]interface{}{msg}, args...)...)\n}\n\n// EqualValuesf asserts that two objects are equal or convertible to the same types\n// and equal.\n//\n//\tassert.EqualValuesf(t, uint32(123), int32(123), \"error message %s\", \"formatted\")\nfunc EqualValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\treturn EqualValues(t, expected, actual, append([]interface{}{msg}, args...)...)\n}\n\n// Errorf asserts that a function returned an error (i.e. not `nil`).\n//\n//\t  actualObj, err := SomeFunction()\n//\t  if assert.Errorf(t, err, \"error message %s\", \"formatted\") {\n//\t\t   assert.Equal(t, expectedErrorf, err)\n//\t  }\nfunc Errorf(t TestingT, err error, msg string, args ...interface{}) bool {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\treturn Error(t, err, append([]interface{}{msg}, args...)...)\n}\n\n// ErrorContainsf asserts that a function returned an error (i.e. not `nil`)\n// and that the error contains the specified substring.\n//\n//\tactualObj, err := SomeFunction()\n//\tassert.ErrorContainsf(t, err,  expectedErrorSubString, \"error message %s\", \"formatted\")\nfunc ErrorContainsf(t TestingT, theError error, contains string, msg string, args ...interface{}) bool {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\treturn ErrorContains(t, theError, contains, append([]interface{}{msg}, args...)...)\n}\n\n// Eventuallyf asserts that given condition will be met in waitFor time,\n// periodically checking target function each tick.\n//\n//\tassert.Eventuallyf(t, func() bool { return true; }, time.Second, 10*time.Millisecond, \"error message %s\", \"formatted\")\nfunc Eventuallyf(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) bool {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\treturn Eventually(t, condition, waitFor, tick, append([]interface{}{msg}, args...)...)\n}\n\n// Failf reports a failure through\nfunc Failf(t TestingT, failureMessage string, msg string, args ...interface{}) bool {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\treturn Fail(t, failureMessage, append([]interface{}{msg}, args...)...)\n}\n\n// FailNowf fails test\nfunc FailNowf(t TestingT, failureMessage string, msg string, args ...interface{}) bool {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\treturn FailNow(t, failureMessage, append([]interface{}{msg}, args...)...)\n}\n\n// Falsef asserts that the specified value is false.\n//\n//\tassert.Falsef(t, myBool, \"error message %s\", \"formatted\")\nfunc Falsef(t TestingT, value bool, msg string, args ...interface{}) bool {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\treturn False(t, value, append([]interface{}{msg}, args...)...)\n}\n\n// Greaterf asserts that the first element is greater than the second\n//\n//\tassert.Greaterf(t, 2, 1, \"error message %s\", \"formatted\")\n//\tassert.Greaterf(t, float64(2), float64(1), \"error message %s\", \"formatted\")\n//\tassert.Greaterf(t, \"b\", \"a\", \"error message %s\", \"formatted\")\nfunc Greaterf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\treturn Greater(t, e1, e2, append([]interface{}{msg}, args...)...)\n}\n\n// GreaterOrEqualf asserts that the first element is greater than or equal to the second\n//\n//\tassert.GreaterOrEqualf(t, 2, 1, \"error message %s\", \"formatted\")\n//\tassert.GreaterOrEqualf(t, 2, 2, \"error message %s\", \"formatted\")\n//\tassert.GreaterOrEqualf(t, \"b\", \"a\", \"error message %s\", \"formatted\")\n//\tassert.GreaterOrEqualf(t, \"b\", \"b\", \"error message %s\", \"formatted\")\nfunc GreaterOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\treturn GreaterOrEqual(t, e1, e2, append([]interface{}{msg}, args...)...)\n}\n\n// InDeltaf asserts that the two numerals are within delta of each other.\n//\n//\tassert.InDeltaf(t, math.Pi, 22/7.0, 0.01, \"error message %s\", \"formatted\")\nfunc InDeltaf(t TestingT, expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) bool {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\treturn InDelta(t, expected, actual, delta, append([]interface{}{msg}, args...)...)\n}\n\n// IsTypef asserts that the specified objects are of the same type.\nfunc IsTypef(t TestingT, expectedType interface{}, object interface{}, msg string, args ...interface{}) bool {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\treturn IsType(t, expectedType, object, append([]interface{}{msg}, args...)...)\n}\n\n// Lenf asserts that the specified object has specific length.\n// Lenf also fails if the object has a type that len() not accept.\n//\n//\tassert.Lenf(t, mySlice, 3, \"error message %s\", \"formatted\")\nfunc Lenf(t TestingT, object interface{}, length int, msg string, args ...interface{}) bool {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\treturn Len(t, object, length, append([]interface{}{msg}, args...)...)\n}\n\n// Lessf asserts that the first element is less than the second\n//\n//\tassert.Lessf(t, 1, 2, \"error message %s\", \"formatted\")\n//\tassert.Lessf(t, float64(1), float64(2), \"error message %s\", \"formatted\")\n//\tassert.Lessf(t, \"a\", \"b\", \"error message %s\", \"formatted\")\nfunc Lessf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\treturn Less(t, e1, e2, append([]interface{}{msg}, args...)...)\n}\n\n// LessOrEqualf asserts that the first element is less than or equal to the second\n//\n//\tassert.LessOrEqualf(t, 1, 2, \"error message %s\", \"formatted\")\n//\tassert.LessOrEqualf(t, 2, 2, \"error message %s\", \"formatted\")\n//\tassert.LessOrEqualf(t, \"a\", \"b\", \"error message %s\", \"formatted\")\n//\tassert.LessOrEqualf(t, \"b\", \"b\", \"error message %s\", \"formatted\")\nfunc LessOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\treturn LessOrEqual(t, e1, e2, append([]interface{}{msg}, args...)...)\n}\n\n// Negativef asserts that the specified element is negative\n//\n//\tassert.Negativef(t, -1, \"error message %s\", \"formatted\")\n//\tassert.Negativef(t, -1.23, \"error message %s\", \"formatted\")\nfunc Negativef(t TestingT, e interface{}, msg string, args ...interface{}) bool {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\treturn Negative(t, e, append([]interface{}{msg}, args...)...)\n}\n\n// Nilf asserts that the specified object is nil.\n//\n//\tassert.Nilf(t, err, \"error message %s\", \"formatted\")\nfunc Nilf(t TestingT, object interface{}, msg string, args ...interface{}) bool {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\treturn Nil(t, object, append([]interface{}{msg}, args...)...)\n}\n\n// NoErrorf asserts that a function returned no error (i.e. `nil`).\n//\n//\t  actualObj, err := SomeFunction()\n//\t  if assert.NoErrorf(t, err, \"error message %s\", \"formatted\") {\n//\t\t   assert.Equal(t, expectedObj, actualObj)\n//\t  }\nfunc NoErrorf(t TestingT, err error, msg string, args ...interface{}) bool {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\treturn NoError(t, err, append([]interface{}{msg}, args...)...)\n}\n\n// NotContainsf asserts that the specified string, list(array, slice...) or map does NOT contain the\n// specified substring or element.\n//\n//\tassert.NotContainsf(t, \"Hello World\", \"Earth\", \"error message %s\", \"formatted\")\n//\tassert.NotContainsf(t, [\"Hello\", \"World\"], \"Earth\", \"error message %s\", \"formatted\")\n//\tassert.NotContainsf(t, {\"Hello\": \"World\"}, \"Earth\", \"error message %s\", \"formatted\")\nfunc NotContainsf(t TestingT, s interface{}, contains interface{}, msg string, args ...interface{}) bool {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\treturn NotContains(t, s, contains, append([]interface{}{msg}, args...)...)\n}\n\n// NotEqualf asserts that the specified values are NOT equal.\n//\n//\tassert.NotEqualf(t, obj1, obj2, \"error message %s\", \"formatted\")\n//\n// Pointer variable equality is determined based on the equality of the\n// referenced values (as opposed to the memory addresses).\nfunc NotEqualf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\treturn NotEqual(t, expected, actual, append([]interface{}{msg}, args...)...)\n}\n\n// NotEqualValuesf asserts that two objects are not equal even when converted to the same type\n//\n//\tassert.NotEqualValuesf(t, obj1, obj2, \"error message %s\", \"formatted\")\nfunc NotEqualValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\treturn NotEqualValues(t, expected, actual, append([]interface{}{msg}, args...)...)\n}\n\n// NotNilf asserts that the specified object is not nil.\n//\n//\tassert.NotNilf(t, err, \"error message %s\", \"formatted\")\nfunc NotNilf(t TestingT, object interface{}, msg string, args ...interface{}) bool {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\treturn NotNil(t, object, append([]interface{}{msg}, args...)...)\n}\n\n// Positivef asserts that the specified element is positive\n//\n//\tassert.Positivef(t, 1, \"error message %s\", \"formatted\")\n//\tassert.Positivef(t, 1.23, \"error message %s\", \"formatted\")\nfunc Positivef(t TestingT, e interface{}, msg string, args ...interface{}) bool {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\treturn Positive(t, e, append([]interface{}{msg}, args...)...)\n}\n\n// Truef asserts that the specified value is true.\n//\n//\tassert.Truef(t, myBool, \"error message %s\", \"formatted\")\nfunc Truef(t TestingT, value bool, msg string, args ...interface{}) bool {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\treturn True(t, value, append([]interface{}{msg}, args...)...)\n}\n\n// WithinDurationf asserts that the two times are within duration delta of each other.\n//\n//\tassert.WithinDurationf(t, time.Now(), time.Now(), 10*time.Second, \"error message %s\", \"formatted\")\nfunc WithinDurationf(t TestingT, expected time.Time, actual time.Time, delta time.Duration, msg string, args ...interface{}) bool {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\treturn WithinDuration(t, expected, actual, delta, append([]interface{}{msg}, args...)...)\n}\n"
  },
  {
    "path": "internal/assert/assertion_mongo.go",
    "content": "// Copyright (C) MongoDB, Inc. 2023-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\n// assertion_mongo.go contains MongoDB-specific extensions to the \"assert\"\n// package.\n\npackage assert\n\nimport (\n\t\"context\"\n\t\"reflect\"\n\t\"time\"\n\t\"unsafe\"\n)\n\n// DifferentAddressRanges asserts that two byte slices reference distinct memory\n// address ranges, meaning they reference different underlying byte arrays.\nfunc DifferentAddressRanges(t TestingT, a, b []byte) (ok bool) {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\n\tif len(a) == 0 || len(b) == 0 {\n\t\treturn true\n\t}\n\n\t// Find the start and end memory addresses for the underlying byte array for\n\t// each input byte slice.\n\tsliceAddrRange := func(b []byte) (uintptr, uintptr) {\n\t\tsh := (*reflect.SliceHeader)(unsafe.Pointer(&b))\n\t\treturn sh.Data, sh.Data + uintptr(sh.Cap-1)\n\t}\n\taStart, aEnd := sliceAddrRange(a)\n\tbStart, bEnd := sliceAddrRange(b)\n\n\t// If \"b\" starts after \"a\" ends or \"a\" starts after \"b\" ends, there is no\n\t// overlap.\n\tif bStart > aEnd || aStart > bEnd {\n\t\treturn true\n\t}\n\n\t// Otherwise, calculate the overlap start and end and print the memory\n\t// overlap error message.\n\tmin := func(a, b uintptr) uintptr {\n\t\tif a < b {\n\t\t\treturn a\n\t\t}\n\t\treturn b\n\t}\n\tmax := func(a, b uintptr) uintptr {\n\t\tif a > b {\n\t\t\treturn a\n\t\t}\n\t\treturn b\n\t}\n\toverlapLow := max(aStart, bStart)\n\toverlapHigh := min(aEnd, bEnd)\n\n\tt.Errorf(\"Byte slices point to the same underlying byte array:\\n\"+\n\t\t\"\\ta addresses:\\t%d ... %d\\n\"+\n\t\t\"\\tb addresses:\\t%d ... %d\\n\"+\n\t\t\"\\toverlap:\\t%d ... %d\",\n\t\taStart, aEnd,\n\t\tbStart, bEnd,\n\t\toverlapLow, overlapHigh)\n\n\treturn false\n}\n\n// Soon runs the provided callback and fails the passed-in test if the callback\n// does not complete within timeout. The provided callback should respect the\n// passed-in context and cease execution when it has expired.\n//\n// Deprecated: This function will be removed with GODRIVER-2667, use\n// assert.Eventually instead.\nfunc Soon(t TestingT, callback func(ctx context.Context), timeout time.Duration) {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\n\t// Create context to manually cancel callback after Soon assertion.\n\tcallbackCtx, cancel := context.WithCancel(context.Background())\n\tdefer cancel()\n\n\tdone := make(chan struct{})\n\tfullCallback := func() {\n\t\tcallback(callbackCtx)\n\t\tdone <- struct{}{}\n\t}\n\n\ttimer := time.NewTimer(timeout)\n\tdefer timer.Stop()\n\n\tgo fullCallback()\n\n\tselect {\n\tcase <-done:\n\t\treturn\n\tcase <-timer.C:\n\t\tt.Errorf(\"timed out in %s waiting for callback\", timeout)\n\t}\n}\n"
  },
  {
    "path": "internal/assert/assertion_mongo_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2023-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage assert\n\nimport (\n\t\"testing\"\n)\n\nfunc TestDifferentAddressRanges(t *testing.T) {\n\tt.Parallel()\n\n\tslice := []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}\n\n\ttestCases := []struct {\n\t\tname string\n\t\ta    []byte\n\t\tb    []byte\n\t\twant bool\n\t}{\n\t\t{\n\t\t\tname: \"distinct byte slices\",\n\t\t\ta:    []byte{0, 1, 2, 3},\n\t\t\tb:    []byte{0, 1, 2, 3},\n\t\t\twant: true,\n\t\t},\n\t\t{\n\t\t\tname: \"same byte slice\",\n\t\t\ta:    slice,\n\t\t\tb:    slice,\n\t\t\twant: false,\n\t\t},\n\t\t{\n\t\t\tname: \"whole and subslice\",\n\t\t\ta:    slice,\n\t\t\tb:    slice[:4],\n\t\t\twant: false,\n\t\t},\n\t\t{\n\t\t\tname: \"two subslices\",\n\t\t\ta:    slice[1:2],\n\t\t\tb:    slice[3:4],\n\t\t\twant: false,\n\t\t},\n\t\t{\n\t\t\tname: \"empty\",\n\t\t\ta:    []byte{0, 1, 2, 3},\n\t\t\tb:    []byte{},\n\t\t\twant: true,\n\t\t},\n\t\t{\n\t\t\tname: \"nil\",\n\t\t\ta:    []byte{0, 1, 2, 3},\n\t\t\tb:    nil,\n\t\t\twant: true,\n\t\t},\n\t}\n\n\tfor _, tc := range testCases {\n\t\ttc := tc // Capture range variable.\n\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tgot := DifferentAddressRanges(new(testing.T), tc.a, tc.b)\n\t\t\tif got != tc.want {\n\t\t\t\tt.Errorf(\"DifferentAddressRanges(%p, %p) = %v, want %v\", tc.a, tc.b, got, tc.want)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "internal/assert/assertions.go",
    "content": "// Copied from https://github.com/stretchr/testify/blob/1333b5d3bda8cf5aedcf3e1aaa95cac28aaab892/assert/assertions.go\n// Copyright 2020 Mat Ryer, Tyler Bunnell and all contributors. All rights reserved.\n// Use of this source code is governed by an MIT-style license that can be found in\n// the THIRD-PARTY-NOTICES file.\n\npackage assert\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t\"errors\"\n\t\"fmt\"\n\t\"math\"\n\t\"path/filepath\"\n\t\"reflect\"\n\t\"runtime\"\n\t\"strings\"\n\t\"time\"\n\t\"unicode\"\n\t\"unicode/utf8\"\n\n\t\"github.com/davecgh/go-spew/spew\"\n)\n\n// TestingT is an interface wrapper around *testing.T\ntype TestingT interface {\n\tErrorf(format string, args ...interface{})\n}\n\n// ObjectsAreEqual determines if two objects are considered equal.\n//\n// This function does no assertion of any kind.\nfunc ObjectsAreEqual(expected, actual interface{}) bool {\n\tif expected == nil || actual == nil {\n\t\treturn expected == actual\n\t}\n\n\texp, ok := expected.([]byte)\n\tif !ok {\n\t\treturn reflect.DeepEqual(expected, actual)\n\t}\n\n\tact, ok := actual.([]byte)\n\tif !ok {\n\t\treturn false\n\t}\n\tif exp == nil || act == nil {\n\t\treturn exp == nil && act == nil\n\t}\n\treturn bytes.Equal(exp, act)\n}\n\n// ObjectsAreEqualValues gets whether two objects are equal, or if their\n// values are equal.\nfunc ObjectsAreEqualValues(expected, actual interface{}) bool {\n\tif ObjectsAreEqual(expected, actual) {\n\t\treturn true\n\t}\n\n\tactualType := reflect.TypeOf(actual)\n\tif actualType == nil {\n\t\treturn false\n\t}\n\texpectedValue := reflect.ValueOf(expected)\n\tif expectedValue.IsValid() && expectedValue.Type().ConvertibleTo(actualType) {\n\t\t// Attempt comparison after type conversion\n\t\treturn reflect.DeepEqual(expectedValue.Convert(actualType).Interface(), actual)\n\t}\n\n\treturn false\n}\n\n/* CallerInfo is necessary because the assert functions use the testing object\ninternally, causing it to print the file:line of the assert method, rather than where\nthe problem actually occurred in calling code.*/\n\n// CallerInfo returns an array of strings containing the file and line number\n// of each stack frame leading from the current test to the assert call that\n// failed.\nfunc CallerInfo() []string {\n\tvar pc uintptr\n\tvar ok bool\n\tvar file string\n\tvar line int\n\tvar name string\n\n\tcallers := []string{}\n\tfor i := 0; ; i++ {\n\t\tpc, file, line, ok = runtime.Caller(i)\n\t\tif !ok {\n\t\t\t// The breaks below failed to terminate the loop, and we ran off the\n\t\t\t// end of the call stack.\n\t\t\tbreak\n\t\t}\n\n\t\t// This is a huge edge case, but it will panic if this is the case, see #180\n\t\tif file == \"<autogenerated>\" {\n\t\t\tbreak\n\t\t}\n\n\t\tf := runtime.FuncForPC(pc)\n\t\tif f == nil {\n\t\t\tbreak\n\t\t}\n\t\tname = f.Name()\n\n\t\t// testing.tRunner is the standard library function that calls\n\t\t// tests. Subtests are called directly by tRunner, without going through\n\t\t// the Test/Benchmark/Example function that contains the t.Run calls, so\n\t\t// with subtests we should break when we hit tRunner, without adding it\n\t\t// to the list of callers.\n\t\tif name == \"testing.tRunner\" {\n\t\t\tbreak\n\t\t}\n\n\t\tparts := strings.Split(file, \"/\")\n\t\tfile = parts[len(parts)-1]\n\t\tif len(parts) > 1 {\n\t\t\tdir := parts[len(parts)-2]\n\t\t\tif (dir != \"assert\" && dir != \"mock\" && dir != \"require\") || file == \"mock_test.go\" {\n\t\t\t\tpath, _ := filepath.Abs(file)\n\t\t\t\tcallers = append(callers, fmt.Sprintf(\"%s:%d\", path, line))\n\t\t\t}\n\t\t}\n\n\t\t// Drop the package\n\t\tsegments := strings.Split(name, \".\")\n\t\tname = segments[len(segments)-1]\n\t\tif isTest(name, \"Test\") ||\n\t\t\tisTest(name, \"Benchmark\") ||\n\t\t\tisTest(name, \"Example\") {\n\t\t\tbreak\n\t\t}\n\t}\n\n\treturn callers\n}\n\n// Stolen from the `go test` tool.\n// isTest tells whether name looks like a test (or benchmark, according to prefix).\n// It is a Test (say) if there is a character after Test that is not a lower-case letter.\n// We don't want TesticularCancer.\nfunc isTest(name, prefix string) bool {\n\tif !strings.HasPrefix(name, prefix) {\n\t\treturn false\n\t}\n\tif len(name) == len(prefix) { // \"Test\" is ok\n\t\treturn true\n\t}\n\tr, _ := utf8.DecodeRuneInString(name[len(prefix):])\n\treturn !unicode.IsLower(r)\n}\n\nfunc messageFromMsgAndArgs(msgAndArgs ...interface{}) string {\n\tif len(msgAndArgs) == 0 || msgAndArgs == nil {\n\t\treturn \"\"\n\t}\n\tif len(msgAndArgs) == 1 {\n\t\tmsg := msgAndArgs[0]\n\t\tif msgAsStr, ok := msg.(string); ok {\n\t\t\treturn msgAsStr\n\t\t}\n\t\treturn fmt.Sprintf(\"%+v\", msg)\n\t}\n\tif len(msgAndArgs) > 1 {\n\t\treturn fmt.Sprintf(msgAndArgs[0].(string), msgAndArgs[1:]...)\n\t}\n\treturn \"\"\n}\n\n// Aligns the provided message so that all lines after the first line start at the same location as the first line.\n// Assumes that the first line starts at the correct location (after carriage return, tab, label, spacer and tab).\n// The longestLabelLen parameter specifies the length of the longest label in the output (required because this is the\n// basis on which the alignment occurs).\nfunc indentMessageLines(message string, longestLabelLen int) string {\n\toutBuf := new(bytes.Buffer)\n\n\tfor i, scanner := 0, bufio.NewScanner(strings.NewReader(message)); scanner.Scan(); i++ {\n\t\t// no need to align first line because it starts at the correct location (after the label)\n\t\tif i != 0 {\n\t\t\t// append alignLen+1 spaces to align with \"{{longestLabel}}:\" before adding tab\n\t\t\toutBuf.WriteString(\"\\n\\t\" + strings.Repeat(\" \", longestLabelLen+1) + \"\\t\")\n\t\t}\n\t\toutBuf.WriteString(scanner.Text())\n\t}\n\n\treturn outBuf.String()\n}\n\ntype failNower interface {\n\tFailNow()\n}\n\n// FailNow fails test\nfunc FailNow(t TestingT, failureMessage string, msgAndArgs ...interface{}) bool {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\tFail(t, failureMessage, msgAndArgs...)\n\n\t// We cannot extend TestingT with FailNow() and\n\t// maintain backwards compatibility, so we fallback\n\t// to panicking when FailNow is not available in\n\t// TestingT.\n\t// See issue #263\n\n\tif t, ok := t.(failNower); ok {\n\t\tt.FailNow()\n\t} else {\n\t\tpanic(\"test failed and t is missing `FailNow()`\")\n\t}\n\treturn false\n}\n\n// Fail reports a failure through\nfunc Fail(t TestingT, failureMessage string, msgAndArgs ...interface{}) bool {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\tcontent := []labeledContent{\n\t\t{\"Error Trace\", strings.Join(CallerInfo(), \"\\n\\t\\t\\t\")},\n\t\t{\"Error\", failureMessage},\n\t}\n\n\t// Add test name if the Go version supports it\n\tif n, ok := t.(interface {\n\t\tName() string\n\t}); ok {\n\t\tcontent = append(content, labeledContent{\"Test\", n.Name()})\n\t}\n\n\tmessage := messageFromMsgAndArgs(msgAndArgs...)\n\tif len(message) > 0 {\n\t\tcontent = append(content, labeledContent{\"Messages\", message})\n\t}\n\n\tt.Errorf(\"\\n%s\", \"\"+labeledOutput(content...))\n\n\treturn false\n}\n\ntype labeledContent struct {\n\tlabel   string\n\tcontent string\n}\n\n// labeledOutput returns a string consisting of the provided labeledContent. Each labeled output is appended in the following manner:\n//\n//\t\\t{{label}}:{{align_spaces}}\\t{{content}}\\n\n//\n// The initial carriage return is required to undo/erase any padding added by testing.T.Errorf. The \"\\t{{label}}:\" is for the label.\n// If a label is shorter than the longest label provided, padding spaces are added to make all the labels match in length. Once this\n// alignment is achieved, \"\\t{{content}}\\n\" is added for the output.\n//\n// If the content of the labeledOutput contains line breaks, the subsequent lines are aligned so that they start at the same location as the first line.\nfunc labeledOutput(content ...labeledContent) string {\n\tlongestLabel := 0\n\tfor _, v := range content {\n\t\tif len(v.label) > longestLabel {\n\t\t\tlongestLabel = len(v.label)\n\t\t}\n\t}\n\tvar output string\n\tfor _, v := range content {\n\t\toutput += \"\\t\" + v.label + \":\" + strings.Repeat(\" \", longestLabel-len(v.label)) + \"\\t\" + indentMessageLines(v.content, longestLabel) + \"\\n\"\n\t}\n\treturn output\n}\n\n// IsType asserts that the specified objects are of the same type.\nfunc IsType(t TestingT, expectedType interface{}, object interface{}, msgAndArgs ...interface{}) bool {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\n\tif !ObjectsAreEqual(reflect.TypeOf(object), reflect.TypeOf(expectedType)) {\n\t\treturn Fail(t, fmt.Sprintf(\"Object expected to be of type %v, but was %v\", reflect.TypeOf(expectedType), reflect.TypeOf(object)), msgAndArgs...)\n\t}\n\n\treturn true\n}\n\n// Equal asserts that two objects are equal.\n//\n//\tassert.Equal(t, 123, 123)\n//\n// Pointer variable equality is determined based on the equality of the\n// referenced values (as opposed to the memory addresses). Function equality\n// cannot be determined and will always fail.\nfunc Equal(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\tif err := validateEqualArgs(expected, actual); err != nil {\n\t\treturn Fail(t, fmt.Sprintf(\"Invalid operation: %#v == %#v (%s)\",\n\t\t\texpected, actual, err), msgAndArgs...)\n\t}\n\n\tif !ObjectsAreEqual(expected, actual) {\n\t\tdiff := diff(expected, actual)\n\t\texpected, actual = formatUnequalValues(expected, actual)\n\t\treturn Fail(t, fmt.Sprintf(\"Not equal: \\n\"+\n\t\t\t\"expected: %s\\n\"+\n\t\t\t\"actual  : %s%s\", expected, actual, diff), msgAndArgs...)\n\t}\n\n\treturn true\n}\n\n// validateEqualArgs checks whether provided arguments can be safely used in the\n// Equal/NotEqual functions.\nfunc validateEqualArgs(expected, actual interface{}) error {\n\tif expected == nil && actual == nil {\n\t\treturn nil\n\t}\n\n\tif isFunction(expected) || isFunction(actual) {\n\t\treturn errors.New(\"cannot take func type as argument\")\n\t}\n\treturn nil\n}\n\n// formatUnequalValues takes two values of arbitrary types and returns string\n// representations appropriate to be presented to the user.\n//\n// If the values are not of like type, the returned strings will be prefixed\n// with the type name, and the value will be enclosed in parenthesis similar\n// to a type conversion in the Go grammar.\nfunc formatUnequalValues(expected, actual interface{}) (e string, a string) {\n\tif reflect.TypeOf(expected) != reflect.TypeOf(actual) {\n\t\treturn fmt.Sprintf(\"%T(%s)\", expected, truncatingFormat(expected)),\n\t\t\tfmt.Sprintf(\"%T(%s)\", actual, truncatingFormat(actual))\n\t}\n\tswitch expected.(type) {\n\tcase time.Duration:\n\t\treturn fmt.Sprintf(\"%v\", expected), fmt.Sprintf(\"%v\", actual)\n\t}\n\treturn truncatingFormat(expected), truncatingFormat(actual)\n}\n\n// truncatingFormat formats the data and truncates it if it's too long.\n//\n// This helps keep formatted error messages lines from exceeding the\n// bufio.MaxScanTokenSize max line length that the go testing framework imposes.\nfunc truncatingFormat(data interface{}) string {\n\tvalue := fmt.Sprintf(\"%#v\", data)\n\tmax := bufio.MaxScanTokenSize - 100 // Give us some space the type info too if needed.\n\tif len(value) > max {\n\t\tvalue = value[0:max] + \"<... truncated>\"\n\t}\n\treturn value\n}\n\n// EqualValues asserts that two objects are equal or convertible to the same types\n// and equal.\n//\n//\tassert.EqualValues(t, uint32(123), int32(123))\nfunc EqualValues(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\n\tif !ObjectsAreEqualValues(expected, actual) {\n\t\tdiff := diff(expected, actual)\n\t\texpected, actual = formatUnequalValues(expected, actual)\n\t\treturn Fail(t, fmt.Sprintf(\"Not equal: \\n\"+\n\t\t\t\"expected: %s\\n\"+\n\t\t\t\"actual  : %s%s\", expected, actual, diff), msgAndArgs...)\n\t}\n\n\treturn true\n}\n\n// NotNil asserts that the specified object is not nil.\n//\n//\tassert.NotNil(t, err)\nfunc NotNil(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {\n\tif !isNil(object) {\n\t\treturn true\n\t}\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\treturn Fail(t, \"Expected value not to be nil.\", msgAndArgs...)\n}\n\n// containsKind checks if a specified kind in the slice of kinds.\nfunc containsKind(kinds []reflect.Kind, kind reflect.Kind) bool {\n\tfor i := 0; i < len(kinds); i++ {\n\t\tif kind == kinds[i] {\n\t\t\treturn true\n\t\t}\n\t}\n\n\treturn false\n}\n\n// isNil checks if a specified object is nil or not, without Failing.\nfunc isNil(object interface{}) bool {\n\tif object == nil {\n\t\treturn true\n\t}\n\n\tvalue := reflect.ValueOf(object)\n\tkind := value.Kind()\n\tisNilableKind := containsKind(\n\t\t[]reflect.Kind{\n\t\t\treflect.Chan, reflect.Func,\n\t\t\treflect.Interface, reflect.Map,\n\t\t\treflect.Ptr, reflect.Slice,\n\t\t},\n\t\tkind)\n\n\tif isNilableKind && value.IsNil() {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// Nil asserts that the specified object is nil.\n//\n//\tassert.Nil(t, err)\nfunc Nil(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {\n\tif isNil(object) {\n\t\treturn true\n\t}\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\treturn Fail(t, fmt.Sprintf(\"Expected nil, but got: %#v\", object), msgAndArgs...)\n}\n\n// getLen try to get length of object.\n// return (false, 0) if impossible.\nfunc getLen(x interface{}) (ok bool, length int) {\n\tv := reflect.ValueOf(x)\n\tdefer func() {\n\t\tif e := recover(); e != nil {\n\t\t\tok = false\n\t\t}\n\t}()\n\treturn true, v.Len()\n}\n\n// Len asserts that the specified object has specific length.\n// Len also fails if the object has a type that len() not accept.\n//\n//\tassert.Len(t, mySlice, 3)\nfunc Len(t TestingT, object interface{}, length int, msgAndArgs ...interface{}) bool {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\tok, l := getLen(object)\n\tif !ok {\n\t\treturn Fail(t, fmt.Sprintf(\"\\\"%s\\\" could not be applied builtin len()\", object), msgAndArgs...)\n\t}\n\n\tif l != length {\n\t\treturn Fail(t, fmt.Sprintf(\"\\\"%s\\\" should have %d item(s), but has %d\", object, length, l), msgAndArgs...)\n\t}\n\treturn true\n}\n\n// True asserts that the specified value is true.\n//\n//\tassert.True(t, myBool)\nfunc True(t TestingT, value bool, msgAndArgs ...interface{}) bool {\n\tif !value {\n\t\tif h, ok := t.(tHelper); ok {\n\t\t\th.Helper()\n\t\t}\n\t\treturn Fail(t, \"Should be true\", msgAndArgs...)\n\t}\n\n\treturn true\n}\n\n// False asserts that the specified value is false.\n//\n//\tassert.False(t, myBool)\nfunc False(t TestingT, value bool, msgAndArgs ...interface{}) bool {\n\tif value {\n\t\tif h, ok := t.(tHelper); ok {\n\t\t\th.Helper()\n\t\t}\n\t\treturn Fail(t, \"Should be false\", msgAndArgs...)\n\t}\n\n\treturn true\n}\n\n// NotEqual asserts that the specified values are NOT equal.\n//\n//\tassert.NotEqual(t, obj1, obj2)\n//\n// Pointer variable equality is determined based on the equality of the\n// referenced values (as opposed to the memory addresses).\nfunc NotEqual(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\tif err := validateEqualArgs(expected, actual); err != nil {\n\t\treturn Fail(t, fmt.Sprintf(\"Invalid operation: %#v != %#v (%s)\",\n\t\t\texpected, actual, err), msgAndArgs...)\n\t}\n\n\tif ObjectsAreEqual(expected, actual) {\n\t\treturn Fail(t, fmt.Sprintf(\"Should not be: %#v\\n\", actual), msgAndArgs...)\n\t}\n\n\treturn true\n}\n\n// NotEqualValues asserts that two objects are not equal even when converted to the same type\n//\n//\tassert.NotEqualValues(t, obj1, obj2)\nfunc NotEqualValues(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\n\tif ObjectsAreEqualValues(expected, actual) {\n\t\treturn Fail(t, fmt.Sprintf(\"Should not be: %#v\\n\", actual), msgAndArgs...)\n\t}\n\n\treturn true\n}\n\n// containsElement try loop over the list check if the list includes the element.\n// return (false, false) if impossible.\n// return (true, false) if element was not found.\n// return (true, true) if element was found.\nfunc containsElement(list interface{}, element interface{}) (ok, found bool) {\n\tlistValue := reflect.ValueOf(list)\n\tlistType := reflect.TypeOf(list)\n\tif listType == nil {\n\t\treturn false, false\n\t}\n\tlistKind := listType.Kind()\n\tdefer func() {\n\t\tif e := recover(); e != nil {\n\t\t\tok = false\n\t\t\tfound = false\n\t\t}\n\t}()\n\n\tif listKind == reflect.String {\n\t\telementValue := reflect.ValueOf(element)\n\t\treturn true, strings.Contains(listValue.String(), elementValue.String())\n\t}\n\n\tif listKind == reflect.Map {\n\t\tmapKeys := listValue.MapKeys()\n\t\tfor i := 0; i < len(mapKeys); i++ {\n\t\t\tif ObjectsAreEqual(mapKeys[i].Interface(), element) {\n\t\t\t\treturn true, true\n\t\t\t}\n\t\t}\n\t\treturn true, false\n\t}\n\n\tfor i := 0; i < listValue.Len(); i++ {\n\t\tif ObjectsAreEqual(listValue.Index(i).Interface(), element) {\n\t\t\treturn true, true\n\t\t}\n\t}\n\treturn true, false\n}\n\n// Contains asserts that the specified string, list(array, slice...) or map contains the\n// specified substring or element.\n//\n//\tassert.Contains(t, \"Hello World\", \"World\")\n//\tassert.Contains(t, [\"Hello\", \"World\"], \"World\")\n//\tassert.Contains(t, {\"Hello\": \"World\"}, \"Hello\")\nfunc Contains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) bool {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\n\tok, found := containsElement(s, contains)\n\tif !ok {\n\t\treturn Fail(t, fmt.Sprintf(\"%#v could not be applied builtin len()\", s), msgAndArgs...)\n\t}\n\tif !found {\n\t\treturn Fail(t, fmt.Sprintf(\"%#v does not contain %#v\", s, contains), msgAndArgs...)\n\t}\n\n\treturn true\n}\n\n// NotContains asserts that the specified string, list(array, slice...) or map does NOT contain the\n// specified substring or element.\n//\n//\tassert.NotContains(t, \"Hello World\", \"Earth\")\n//\tassert.NotContains(t, [\"Hello\", \"World\"], \"Earth\")\n//\tassert.NotContains(t, {\"Hello\": \"World\"}, \"Earth\")\nfunc NotContains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) bool {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\n\tok, found := containsElement(s, contains)\n\tif !ok {\n\t\treturn Fail(t, fmt.Sprintf(\"\\\"%s\\\" could not be applied builtin len()\", s), msgAndArgs...)\n\t}\n\tif found {\n\t\treturn Fail(t, fmt.Sprintf(\"\\\"%s\\\" should not contain \\\"%s\\\"\", s, contains), msgAndArgs...)\n\t}\n\n\treturn true\n}\n\n// isEmpty gets whether the specified object is considered empty or not.\nfunc isEmpty(object interface{}) bool {\n\t// get nil case out of the way\n\tif object == nil {\n\t\treturn true\n\t}\n\n\tobjValue := reflect.ValueOf(object)\n\n\tswitch objValue.Kind() {\n\t// collection types are empty when they have no element\n\tcase reflect.Chan, reflect.Map, reflect.Slice:\n\t\treturn objValue.Len() == 0\n\t// pointers are empty if nil or if the value they point to is empty\n\tcase reflect.Ptr:\n\t\tif objValue.IsNil() {\n\t\t\treturn true\n\t\t}\n\t\tderef := objValue.Elem().Interface()\n\t\treturn isEmpty(deref)\n\t// for all other types, compare against the zero value\n\t// array types are empty when they match their zero-initialized state\n\tdefault:\n\t\tzero := reflect.Zero(objValue.Type())\n\t\treturn reflect.DeepEqual(object, zero.Interface())\n\t}\n}\n\n// ElementsMatch asserts that the specified listA(array, slice...) is equal to specified\n// listB(array, slice...) ignoring the order of the elements. If there are duplicate elements,\n// the number of appearances of each of them in both lists should match.\n//\n// assert.ElementsMatch(t, [1, 3, 2, 3], [1, 3, 3, 2])\nfunc ElementsMatch(t TestingT, listA, listB interface{}, msgAndArgs ...interface{}) (ok bool) {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\tif isEmpty(listA) && isEmpty(listB) {\n\t\treturn true\n\t}\n\n\tif !isList(t, listA, msgAndArgs...) || !isList(t, listB, msgAndArgs...) {\n\t\treturn false\n\t}\n\n\textraA, extraB := diffLists(listA, listB)\n\n\tif len(extraA) == 0 && len(extraB) == 0 {\n\t\treturn true\n\t}\n\n\treturn Fail(t, formatListDiff(listA, listB, extraA, extraB), msgAndArgs...)\n}\n\n// isList checks that the provided value is array or slice.\nfunc isList(t TestingT, list interface{}, msgAndArgs ...interface{}) (ok bool) {\n\tkind := reflect.TypeOf(list).Kind()\n\tif kind != reflect.Array && kind != reflect.Slice {\n\t\treturn Fail(t, fmt.Sprintf(\"%q has an unsupported type %s, expecting array or slice\", list, kind),\n\t\t\tmsgAndArgs...)\n\t}\n\treturn true\n}\n\n// diffLists diffs two arrays/slices and returns slices of elements that are only in A and only in B.\n// If some element is present multiple times, each instance is counted separately (e.g. if something is 2x in A and\n// 5x in B, it will be 0x in extraA and 3x in extraB). The order of items in both lists is ignored.\nfunc diffLists(listA, listB interface{}) ([]interface{}, []interface{}) {\n\tvar extraA, extraB []interface{}\n\n\taValue := reflect.ValueOf(listA)\n\tbValue := reflect.ValueOf(listB)\n\n\taLen := aValue.Len()\n\tbLen := bValue.Len()\n\n\t// Mark indexes in bValue that we already used\n\tvisited := make([]bool, bLen)\n\tfor i := 0; i < aLen; i++ {\n\t\telement := aValue.Index(i).Interface()\n\t\tfound := false\n\t\tfor j := 0; j < bLen; j++ {\n\t\t\tif visited[j] {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif ObjectsAreEqual(bValue.Index(j).Interface(), element) {\n\t\t\t\tvisited[j] = true\n\t\t\t\tfound = true\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tif !found {\n\t\t\textraA = append(extraA, element)\n\t\t}\n\t}\n\n\tfor j := 0; j < bLen; j++ {\n\t\tif visited[j] {\n\t\t\tcontinue\n\t\t}\n\t\textraB = append(extraB, bValue.Index(j).Interface())\n\t}\n\n\treturn extraA, extraB\n}\n\nfunc formatListDiff(listA, listB interface{}, extraA, extraB []interface{}) string {\n\tvar msg bytes.Buffer\n\n\tmsg.WriteString(\"elements differ\")\n\tif len(extraA) > 0 {\n\t\tmsg.WriteString(\"\\n\\nextra elements in list A:\\n\")\n\t\tmsg.WriteString(spewConfig.Sdump(extraA))\n\t}\n\tif len(extraB) > 0 {\n\t\tmsg.WriteString(\"\\n\\nextra elements in list B:\\n\")\n\t\tmsg.WriteString(spewConfig.Sdump(extraB))\n\t}\n\tmsg.WriteString(\"\\n\\nlistA:\\n\")\n\tmsg.WriteString(spewConfig.Sdump(listA))\n\tmsg.WriteString(\"\\n\\nlistB:\\n\")\n\tmsg.WriteString(spewConfig.Sdump(listB))\n\n\treturn msg.String()\n}\n\n// WithinDuration asserts that the two times are within duration delta of each other.\n//\n//\tassert.WithinDuration(t, time.Now(), time.Now(), 10*time.Second)\nfunc WithinDuration(t TestingT, expected, actual time.Time, delta time.Duration, msgAndArgs ...interface{}) bool {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\n\tdt := expected.Sub(actual)\n\tif dt < -delta || dt > delta {\n\t\treturn Fail(t, fmt.Sprintf(\"Max difference between %v and %v allowed is %v, but difference was %v\", expected, actual, delta, dt), msgAndArgs...)\n\t}\n\n\treturn true\n}\n\nfunc toFloat(x interface{}) (float64, bool) {\n\tvar xf float64\n\txok := true\n\n\tswitch xn := x.(type) {\n\tcase uint:\n\t\txf = float64(xn)\n\tcase uint8:\n\t\txf = float64(xn)\n\tcase uint16:\n\t\txf = float64(xn)\n\tcase uint32:\n\t\txf = float64(xn)\n\tcase uint64:\n\t\txf = float64(xn)\n\tcase int:\n\t\txf = float64(xn)\n\tcase int8:\n\t\txf = float64(xn)\n\tcase int16:\n\t\txf = float64(xn)\n\tcase int32:\n\t\txf = float64(xn)\n\tcase int64:\n\t\txf = float64(xn)\n\tcase float32:\n\t\txf = float64(xn)\n\tcase float64:\n\t\txf = xn\n\tcase time.Duration:\n\t\txf = float64(xn)\n\tdefault:\n\t\txok = false\n\t}\n\n\treturn xf, xok\n}\n\n// InDelta asserts that the two numerals are within delta of each other.\n//\n//\tassert.InDelta(t, math.Pi, 22/7.0, 0.01)\nfunc InDelta(t TestingT, expected, actual interface{}, delta float64, msgAndArgs ...interface{}) bool {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\n\taf, aok := toFloat(expected)\n\tbf, bok := toFloat(actual)\n\n\tif !aok || !bok {\n\t\treturn Fail(t, \"Parameters must be numerical\", msgAndArgs...)\n\t}\n\n\tif math.IsNaN(af) && math.IsNaN(bf) {\n\t\treturn true\n\t}\n\n\tif math.IsNaN(af) {\n\t\treturn Fail(t, \"Expected must not be NaN\", msgAndArgs...)\n\t}\n\n\tif math.IsNaN(bf) {\n\t\treturn Fail(t, fmt.Sprintf(\"Expected %v with delta %v, but was NaN\", expected, delta), msgAndArgs...)\n\t}\n\n\tdt := af - bf\n\tif dt < -delta || dt > delta {\n\t\treturn Fail(t, fmt.Sprintf(\"Max difference between %v and %v allowed is %v, but difference was %v\", expected, actual, delta, dt), msgAndArgs...)\n\t}\n\n\treturn true\n}\n\n/*\n\tErrors\n*/\n\n// NoError asserts that a function returned no error (i.e. `nil`).\n//\n//\t  actualObj, err := SomeFunction()\n//\t  if assert.NoError(t, err) {\n//\t\t   assert.Equal(t, expectedObj, actualObj)\n//\t  }\nfunc NoError(t TestingT, err error, msgAndArgs ...interface{}) bool {\n\tif err != nil {\n\t\tif h, ok := t.(tHelper); ok {\n\t\t\th.Helper()\n\t\t}\n\t\treturn Fail(t, fmt.Sprintf(\"Received unexpected error:\\n%+v\", err), msgAndArgs...)\n\t}\n\n\treturn true\n}\n\n// Error asserts that a function returned an error (i.e. not `nil`).\n//\n//\t  actualObj, err := SomeFunction()\n//\t  if assert.Error(t, err) {\n//\t\t   assert.Equal(t, expectedError, err)\n//\t  }\nfunc Error(t TestingT, err error, msgAndArgs ...interface{}) bool {\n\tif err == nil {\n\t\tif h, ok := t.(tHelper); ok {\n\t\t\th.Helper()\n\t\t}\n\t\treturn Fail(t, \"An error is expected but got nil.\", msgAndArgs...)\n\t}\n\n\treturn true\n}\n\n// EqualError asserts that a function returned an error (i.e. not `nil`)\n// and that it is equal to the provided error.\n//\n//\tactualObj, err := SomeFunction()\n//\tassert.EqualError(t, err,  expectedErrorString)\nfunc EqualError(t TestingT, theError error, errString string, msgAndArgs ...interface{}) bool {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\tif !Error(t, theError, msgAndArgs...) {\n\t\treturn false\n\t}\n\texpected := errString\n\tactual := theError.Error()\n\t// don't need to use deep equals here, we know they are both strings\n\tif expected != actual {\n\t\treturn Fail(t, fmt.Sprintf(\"Error message not equal:\\n\"+\n\t\t\t\"expected: %q\\n\"+\n\t\t\t\"actual  : %q\", expected, actual), msgAndArgs...)\n\t}\n\treturn true\n}\n\n// ErrorIs asserts that at least one of the errors in err's chain matches target.\n// This is a wrapper for errors.Is.\nfunc ErrorIs(t TestingT, err, target error, msgAndArgs ...interface{}) bool {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\tif errors.Is(err, target) {\n\t\treturn true\n\t}\n\n\tvar expectedText string\n\tif target != nil {\n\t\texpectedText = target.Error()\n\t}\n\n\tchain := buildErrorChainString(err)\n\n\treturn Fail(t, fmt.Sprintf(\"Target error should be in err chain:\\n\"+\n\t\t\"expected: %q\\n\"+\n\t\t\"in chain: %s\", expectedText, chain,\n\t), msgAndArgs...)\n}\n\n// ErrorContains asserts that a function returned an error (i.e. not `nil`)\n// and that the error contains the specified substring.\n//\n//\tactualObj, err := SomeFunction()\n//\tassert.ErrorContains(t, err,  expectedErrorSubString)\nfunc ErrorContains(t TestingT, theError error, contains string, msgAndArgs ...interface{}) bool {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\tif !Error(t, theError, msgAndArgs...) {\n\t\treturn false\n\t}\n\n\tactual := theError.Error()\n\tif !strings.Contains(actual, contains) {\n\t\treturn Fail(t, fmt.Sprintf(\"Error %#v does not contain %#v\", actual, contains), msgAndArgs...)\n\t}\n\n\treturn true\n}\n\nfunc typeAndKind(v interface{}) (reflect.Type, reflect.Kind) {\n\tt := reflect.TypeOf(v)\n\tk := t.Kind()\n\n\tif k == reflect.Ptr {\n\t\tt = t.Elem()\n\t\tk = t.Kind()\n\t}\n\treturn t, k\n}\n\n// diff returns a diff of both values as long as both are of the same type and\n// are a struct, map, slice, array or string. Otherwise it returns an empty string.\nfunc diff(expected interface{}, actual interface{}) string {\n\tif expected == nil || actual == nil {\n\t\treturn \"\"\n\t}\n\n\tet, ek := typeAndKind(expected)\n\tat, _ := typeAndKind(actual)\n\n\tif et != at {\n\t\treturn \"\"\n\t}\n\n\tif ek != reflect.Struct && ek != reflect.Map && ek != reflect.Slice && ek != reflect.Array && ek != reflect.String {\n\t\treturn \"\"\n\t}\n\n\tvar e, a string\n\n\tswitch et {\n\tcase reflect.TypeOf(\"\"):\n\t\te = reflect.ValueOf(expected).String()\n\t\ta = reflect.ValueOf(actual).String()\n\tcase reflect.TypeOf(time.Time{}):\n\t\te = spewConfigStringerEnabled.Sdump(expected)\n\t\ta = spewConfigStringerEnabled.Sdump(actual)\n\tdefault:\n\t\te = spewConfig.Sdump(expected)\n\t\ta = spewConfig.Sdump(actual)\n\t}\n\n\tdiff, _ := GetUnifiedDiffString(UnifiedDiff{\n\t\tA:        SplitLines(e),\n\t\tB:        SplitLines(a),\n\t\tFromFile: \"Expected\",\n\t\tFromDate: \"\",\n\t\tToFile:   \"Actual\",\n\t\tToDate:   \"\",\n\t\tContext:  1,\n\t})\n\n\treturn \"\\n\\nDiff:\\n\" + diff\n}\n\nfunc isFunction(arg interface{}) bool {\n\tif arg == nil {\n\t\treturn false\n\t}\n\treturn reflect.TypeOf(arg).Kind() == reflect.Func\n}\n\nvar spewConfig = spew.ConfigState{\n\tIndent:                  \" \",\n\tDisablePointerAddresses: true,\n\tDisableCapacities:       true,\n\tSortKeys:                true,\n\tDisableMethods:          true,\n\tMaxDepth:                10,\n}\n\nvar spewConfigStringerEnabled = spew.ConfigState{\n\tIndent:                  \" \",\n\tDisablePointerAddresses: true,\n\tDisableCapacities:       true,\n\tSortKeys:                true,\n\tMaxDepth:                10,\n}\n\ntype tHelper interface {\n\tHelper()\n}\n\n// Eventually asserts that given condition will be met in waitFor time,\n// periodically checking target function each tick.\n//\n//\tassert.Eventually(t, func() bool { return true; }, time.Second, 10*time.Millisecond)\nfunc Eventually(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) bool {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\n\tch := make(chan bool, 1)\n\n\ttimer := time.NewTimer(waitFor)\n\tdefer timer.Stop()\n\n\tticker := time.NewTicker(tick)\n\tdefer ticker.Stop()\n\n\tfor tick := ticker.C; ; {\n\t\tselect {\n\t\tcase <-timer.C:\n\t\t\treturn Fail(t, \"Condition never satisfied\", msgAndArgs...)\n\t\tcase <-tick:\n\t\t\ttick = nil\n\t\t\tgo func() { ch <- condition() }()\n\t\tcase v := <-ch:\n\t\t\tif v {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\ttick = ticker.C\n\t\t}\n\t}\n}\n\nfunc buildErrorChainString(err error) string {\n\tif err == nil {\n\t\treturn \"\"\n\t}\n\n\te := errors.Unwrap(err)\n\tchain := fmt.Sprintf(\"%q\", err.Error())\n\tfor e != nil {\n\t\tchain += fmt.Sprintf(\"\\n\\t%q\", e.Error())\n\t\te = errors.Unwrap(e)\n\t}\n\treturn chain\n}\n\n// NotEmpty asserts that the specified object is NOT [Empty].\n//\n//\tif assert.NotEmpty(t, obj) {\n//\t  assert.Equal(t, \"two\", obj[1])\n//\t}\nfunc NotEmpty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {\n\tpass := !isEmpty(object)\n\tif !pass {\n\t\tif h, ok := t.(tHelper); ok {\n\t\t\th.Helper()\n\t\t}\n\t\tFail(t, fmt.Sprintf(\"Should NOT be empty, but was %v\", object), msgAndArgs...)\n\t}\n\n\treturn pass\n}\n\n// Empty asserts that the given value is \"empty\".\n//\n// [Zero values] are \"empty\".\n//\n// Arrays are \"empty\" if every element is the zero value of the type (stricter than \"empty\").\n//\n// Slices, maps and channels with zero length are \"empty\".\n//\n// Pointer values are \"empty\" if the pointer is nil or if the pointed value is \"empty\".\n//\n//\tassert.Empty(t, obj)\n//\n// [Zero values]: https://go.dev/ref/spec#The_zero_value\nfunc Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {\n\tpass := isEmpty(object)\n\tif !pass {\n\t\tif h, ok := t.(tHelper); ok {\n\t\t\th.Helper()\n\t\t}\n\t\tFail(t, fmt.Sprintf(\"Should be empty, but was %v\", object), msgAndArgs...)\n\t}\n\n\treturn pass\n}\n"
  },
  {
    "path": "internal/assert/assertions_test.go",
    "content": "// Copied from https://github.com/stretchr/testify/blob/1333b5d3bda8cf5aedcf3e1aaa95cac28aaab892/assert/assertions_test.go\n\n// Copyright 2020 Mat Ryer, Tyler Bunnell and all contributors. All rights reserved.\n// Use of this source code is governed by an MIT-style license that can be found in\n// the THIRD-PARTY-NOTICES file.\n\npackage assert\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t\"errors\"\n\t\"fmt\"\n\t\"math\"\n\t\"reflect\"\n\t\"runtime\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n)\n\n// AssertionTesterInterface defines an interface to be used for testing assertion methods\ntype AssertionTesterInterface interface {\n\tTestMethod()\n}\n\n// AssertionTesterConformingObject is an object that conforms to the AssertionTesterInterface interface\ntype AssertionTesterConformingObject struct{}\n\nfunc (a *AssertionTesterConformingObject) TestMethod() {\n}\n\n// AssertionTesterNonConformingObject is an object that does not conform to the AssertionTesterInterface interface\ntype AssertionTesterNonConformingObject struct{}\n\nfunc TestObjectsAreEqual(t *testing.T) {\n\tcases := []struct {\n\t\texpected interface{}\n\t\tactual   interface{}\n\t\tresult   bool\n\t}{\n\t\t// cases that are expected to be equal\n\t\t{\"Hello World\", \"Hello World\", true},\n\t\t{123, 123, true},\n\t\t{123.5, 123.5, true},\n\t\t{[]byte(\"Hello World\"), []byte(\"Hello World\"), true},\n\t\t{nil, nil, true},\n\n\t\t// cases that are expected not to be equal\n\t\t{map[int]int{5: 10}, map[int]int{10: 20}, false},\n\t\t{'x', \"x\", false},\n\t\t{\"x\", 'x', false},\n\t\t{0, 0.1, false},\n\t\t{0.1, 0, false},\n\t\t{time.Now, time.Now, false},\n\t\t{func() {}, func() {}, false},\n\t\t{uint32(10), int32(10), false},\n\t}\n\n\tfor _, c := range cases {\n\t\tt.Run(fmt.Sprintf(\"ObjectsAreEqual(%#v, %#v)\", c.expected, c.actual), func(t *testing.T) {\n\t\t\tres := ObjectsAreEqual(c.expected, c.actual)\n\n\t\t\tif res != c.result {\n\t\t\t\tt.Errorf(\"ObjectsAreEqual(%#v, %#v) should return %#v\", c.expected, c.actual, c.result)\n\t\t\t}\n\t\t})\n\t}\n\n\t// Cases where type differ but values are equal\n\tif !ObjectsAreEqualValues(uint32(10), int32(10)) {\n\t\tt.Error(\"ObjectsAreEqualValues should return true\")\n\t}\n\tif ObjectsAreEqualValues(0, nil) {\n\t\tt.Fail()\n\t}\n\tif ObjectsAreEqualValues(nil, 0) {\n\t\tt.Fail()\n\t}\n}\n\nfunc TestIsType(t *testing.T) {\n\tmockT := new(testing.T)\n\n\tif !IsType(mockT, new(AssertionTesterConformingObject), new(AssertionTesterConformingObject)) {\n\t\tt.Error(\"IsType should return true: AssertionTesterConformingObject is the same type as AssertionTesterConformingObject\")\n\t}\n\tif IsType(mockT, new(AssertionTesterConformingObject), new(AssertionTesterNonConformingObject)) {\n\t\tt.Error(\"IsType should return false: AssertionTesterConformingObject is not the same type as AssertionTesterNonConformingObject\")\n\t}\n}\n\nfunc TestEqual(t *testing.T) {\n\ttype myType string\n\n\tmockT := new(testing.T)\n\tvar m map[string]interface{}\n\n\tcases := []struct {\n\t\texpected interface{}\n\t\tactual   interface{}\n\t\tresult   bool\n\t\tremark   string\n\t}{\n\t\t{\"Hello World\", \"Hello World\", true, \"\"},\n\t\t{123, 123, true, \"\"},\n\t\t{123.5, 123.5, true, \"\"},\n\t\t{[]byte(\"Hello World\"), []byte(\"Hello World\"), true, \"\"},\n\t\t{nil, nil, true, \"\"},\n\t\t{int32(123), int32(123), true, \"\"},\n\t\t{uint64(123), uint64(123), true, \"\"},\n\t\t{myType(\"1\"), myType(\"1\"), true, \"\"},\n\t\t{&struct{}{}, &struct{}{}, true, \"pointer equality is based on equality of underlying value\"},\n\n\t\t// Not expected to be equal\n\t\t{m[\"bar\"], \"something\", false, \"\"},\n\t\t{myType(\"1\"), myType(\"2\"), false, \"\"},\n\n\t\t// A case that might be confusing, especially with numeric literals\n\t\t{10, uint(10), false, \"\"},\n\t}\n\n\tfor _, c := range cases {\n\t\tt.Run(fmt.Sprintf(\"Equal(%#v, %#v)\", c.expected, c.actual), func(t *testing.T) {\n\t\t\tres := Equal(mockT, c.expected, c.actual)\n\n\t\t\tif res != c.result {\n\t\t\t\tt.Errorf(\"Equal(%#v, %#v) should return %#v: %s\", c.expected, c.actual, c.result, c.remark)\n\t\t\t}\n\t\t})\n\t}\n}\n\n// bufferT implements TestingT. Its implementation of Errorf writes the output that would be produced by\n// testing.T.Errorf to an internal bytes.Buffer.\ntype bufferT struct {\n\tbuf bytes.Buffer\n}\n\nfunc (t *bufferT) Errorf(format string, args ...interface{}) {\n\t// implementation of decorate is copied from testing.T\n\tdecorate := func(s string) string {\n\t\t_, file, line, ok := runtime.Caller(3) // decorate + log + public function.\n\t\tif ok {\n\t\t\t// Truncate file name at last file name separator.\n\t\t\tif index := strings.LastIndex(file, \"/\"); index >= 0 {\n\t\t\t\tfile = file[index+1:]\n\t\t\t} else if index = strings.LastIndex(file, \"\\\\\"); index >= 0 {\n\t\t\t\tfile = file[index+1:]\n\t\t\t}\n\t\t} else {\n\t\t\tfile = \"???\"\n\t\t\tline = 1\n\t\t}\n\t\tbuf := new(bytes.Buffer)\n\t\t// Every line is indented at least one tab.\n\t\tbuf.WriteByte('\\t')\n\t\tfmt.Fprintf(buf, \"%s:%d: \", file, line)\n\t\tlines := strings.Split(s, \"\\n\")\n\t\tif l := len(lines); l > 1 && lines[l-1] == \"\" {\n\t\t\tlines = lines[:l-1]\n\t\t}\n\t\tfor i, line := range lines {\n\t\t\tif i > 0 {\n\t\t\t\t// Second and subsequent lines are indented an extra tab.\n\t\t\t\tbuf.WriteString(\"\\n\\t\\t\")\n\t\t\t}\n\t\t\tbuf.WriteString(line)\n\t\t}\n\t\tbuf.WriteByte('\\n')\n\t\treturn buf.String()\n\t}\n\tt.buf.WriteString(decorate(fmt.Sprintf(format, args...)))\n}\n\nfunc TestStringEqual(_ *testing.T) {\n\tfor _, currCase := range []struct {\n\t\tequalWant  string\n\t\tequalGot   string\n\t\tmsgAndArgs []interface{}\n\t\twant       string\n\t}{\n\t\t{equalWant: \"hi, \\nmy name is\", equalGot: \"what,\\nmy name is\", want: \"\\tassertions.go:\\\\d+: \\n\\t+Error Trace:\\t\\n\\t+Error:\\\\s+Not equal:\\\\s+\\n\\\\s+expected: \\\"hi, \\\\\\\\nmy name is\\\"\\n\\\\s+actual\\\\s+: \\\"what,\\\\\\\\nmy name is\\\"\\n\\\\s+Diff:\\n\\\\s+-+ Expected\\n\\\\s+\\\\++ Actual\\n\\\\s+@@ -1,2 \\\\+1,2 @@\\n\\\\s+-hi, \\n\\\\s+\\\\+what,\\n\\\\s+my name is\"},\n\t} {\n\t\tmockT := &bufferT{}\n\t\tEqual(mockT, currCase.equalWant, currCase.equalGot, currCase.msgAndArgs...)\n\t}\n}\n\nfunc TestEqualFormatting(_ *testing.T) {\n\tfor _, currCase := range []struct {\n\t\tequalWant  string\n\t\tequalGot   string\n\t\tmsgAndArgs []interface{}\n\t\twant       string\n\t}{\n\t\t{equalWant: \"want\", equalGot: \"got\", want: \"\\tassertions.go:\\\\d+: \\n\\t+Error Trace:\\t\\n\\t+Error:\\\\s+Not equal:\\\\s+\\n\\\\s+expected: \\\"want\\\"\\n\\\\s+actual\\\\s+: \\\"got\\\"\\n\\\\s+Diff:\\n\\\\s+-+ Expected\\n\\\\s+\\\\++ Actual\\n\\\\s+@@ -1 \\\\+1 @@\\n\\\\s+-want\\n\\\\s+\\\\+got\\n\"},\n\t\t{equalWant: \"want\", equalGot: \"got\", msgAndArgs: []interface{}{\"hello, %v!\", \"world\"}, want: \"\\tassertions.go:[0-9]+: \\n\\t+Error Trace:\\t\\n\\t+Error:\\\\s+Not equal:\\\\s+\\n\\\\s+expected: \\\"want\\\"\\n\\\\s+actual\\\\s+: \\\"got\\\"\\n\\\\s+Diff:\\n\\\\s+-+ Expected\\n\\\\s+\\\\++ Actual\\n\\\\s+@@ -1 \\\\+1 @@\\n\\\\s+-want\\n\\\\s+\\\\+got\\n\\\\s+Messages:\\\\s+hello, world!\\n\"},\n\t\t{equalWant: \"want\", equalGot: \"got\", msgAndArgs: []interface{}{123}, want: \"\\tassertions.go:[0-9]+: \\n\\t+Error Trace:\\t\\n\\t+Error:\\\\s+Not equal:\\\\s+\\n\\\\s+expected: \\\"want\\\"\\n\\\\s+actual\\\\s+: \\\"got\\\"\\n\\\\s+Diff:\\n\\\\s+-+ Expected\\n\\\\s+\\\\++ Actual\\n\\\\s+@@ -1 \\\\+1 @@\\n\\\\s+-want\\n\\\\s+\\\\+got\\n\\\\s+Messages:\\\\s+123\\n\"},\n\t\t{equalWant: \"want\", equalGot: \"got\", msgAndArgs: []interface{}{struct{ a string }{\"hello\"}}, want: \"\\tassertions.go:[0-9]+: \\n\\t+Error Trace:\\t\\n\\t+Error:\\\\s+Not equal:\\\\s+\\n\\\\s+expected: \\\"want\\\"\\n\\\\s+actual\\\\s+: \\\"got\\\"\\n\\\\s+Diff:\\n\\\\s+-+ Expected\\n\\\\s+\\\\++ Actual\\n\\\\s+@@ -1 \\\\+1 @@\\n\\\\s+-want\\n\\\\s+\\\\+got\\n\\\\s+Messages:\\\\s+{a:hello}\\n\"},\n\t} {\n\t\tmockT := &bufferT{}\n\t\tEqual(mockT, currCase.equalWant, currCase.equalGot, currCase.msgAndArgs...)\n\t}\n}\n\nfunc TestFormatUnequalValues(t *testing.T) {\n\texpected, actual := formatUnequalValues(\"foo\", \"bar\")\n\tEqual(t, `\"foo\"`, expected, \"value should not include type\")\n\tEqual(t, `\"bar\"`, actual, \"value should not include type\")\n\n\texpected, actual = formatUnequalValues(123, 123)\n\tEqual(t, `123`, expected, \"value should not include type\")\n\tEqual(t, `123`, actual, \"value should not include type\")\n\n\texpected, actual = formatUnequalValues(int64(123), int32(123))\n\tEqual(t, `int64(123)`, expected, \"value should include type\")\n\tEqual(t, `int32(123)`, actual, \"value should include type\")\n\n\texpected, actual = formatUnequalValues(int64(123), nil)\n\tEqual(t, `int64(123)`, expected, \"value should include type\")\n\tEqual(t, `<nil>(<nil>)`, actual, \"value should include type\")\n\n\ttype testStructType struct {\n\t\tVal string\n\t}\n\n\texpected, actual = formatUnequalValues(&testStructType{Val: \"test\"}, &testStructType{Val: \"test\"})\n\tEqual(t, `&assert.testStructType{Val:\"test\"}`, expected, \"value should not include type annotation\")\n\tEqual(t, `&assert.testStructType{Val:\"test\"}`, actual, \"value should not include type annotation\")\n}\n\nfunc TestNotNil(t *testing.T) {\n\tmockT := new(testing.T)\n\n\tif !NotNil(mockT, new(AssertionTesterConformingObject)) {\n\t\tt.Error(\"NotNil should return true: object is not nil\")\n\t}\n\tif NotNil(mockT, nil) {\n\t\tt.Error(\"NotNil should return false: object is nil\")\n\t}\n\tif NotNil(mockT, (*struct{})(nil)) {\n\t\tt.Error(\"NotNil should return false: object is (*struct{})(nil)\")\n\t}\n}\n\nfunc TestNil(t *testing.T) {\n\tmockT := new(testing.T)\n\n\tif !Nil(mockT, nil) {\n\t\tt.Error(\"Nil should return true: object is nil\")\n\t}\n\tif !Nil(mockT, (*struct{})(nil)) {\n\t\tt.Error(\"Nil should return true: object is (*struct{})(nil)\")\n\t}\n\tif Nil(mockT, new(AssertionTesterConformingObject)) {\n\t\tt.Error(\"Nil should return false: object is not nil\")\n\t}\n}\n\nfunc TestTrue(t *testing.T) {\n\tmockT := new(testing.T)\n\n\tif !True(mockT, true) {\n\t\tt.Error(\"True should return true\")\n\t}\n\tif True(mockT, false) {\n\t\tt.Error(\"True should return false\")\n\t}\n}\n\nfunc TestFalse(t *testing.T) {\n\tmockT := new(testing.T)\n\n\tif !False(mockT, false) {\n\t\tt.Error(\"False should return true\")\n\t}\n\tif False(mockT, true) {\n\t\tt.Error(\"False should return false\")\n\t}\n}\n\nfunc TestNotEqual(t *testing.T) {\n\tmockT := new(testing.T)\n\n\tcases := []struct {\n\t\texpected interface{}\n\t\tactual   interface{}\n\t\tresult   bool\n\t}{\n\t\t// cases that are expected not to match\n\t\t{\"Hello World\", \"Hello World!\", true},\n\t\t{123, 1234, true},\n\t\t{123.5, 123.55, true},\n\t\t{[]byte(\"Hello World\"), []byte(\"Hello World!\"), true},\n\t\t{nil, new(AssertionTesterConformingObject), true},\n\n\t\t// cases that are expected to match\n\t\t{nil, nil, false},\n\t\t{\"Hello World\", \"Hello World\", false},\n\t\t{123, 123, false},\n\t\t{123.5, 123.5, false},\n\t\t{[]byte(\"Hello World\"), []byte(\"Hello World\"), false},\n\t\t{new(AssertionTesterConformingObject), new(AssertionTesterConformingObject), false},\n\t\t{&struct{}{}, &struct{}{}, false},\n\t\t{func() int { return 23 }, func() int { return 24 }, false},\n\t\t// A case that might be confusing, especially with numeric literals\n\t\t{int(10), uint(10), true},\n\t}\n\n\tfor _, c := range cases {\n\t\tt.Run(fmt.Sprintf(\"NotEqual(%#v, %#v)\", c.expected, c.actual), func(t *testing.T) {\n\t\t\tres := NotEqual(mockT, c.expected, c.actual)\n\n\t\t\tif res != c.result {\n\t\t\t\tt.Errorf(\"NotEqual(%#v, %#v) should return %#v\", c.expected, c.actual, c.result)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestNotEqualValues(t *testing.T) {\n\tmockT := new(testing.T)\n\n\tcases := []struct {\n\t\texpected interface{}\n\t\tactual   interface{}\n\t\tresult   bool\n\t}{\n\t\t// cases that are expected not to match\n\t\t{\"Hello World\", \"Hello World!\", true},\n\t\t{123, 1234, true},\n\t\t{123.5, 123.55, true},\n\t\t{[]byte(\"Hello World\"), []byte(\"Hello World!\"), true},\n\t\t{nil, new(AssertionTesterConformingObject), true},\n\n\t\t// cases that are expected to match\n\t\t{nil, nil, false},\n\t\t{\"Hello World\", \"Hello World\", false},\n\t\t{123, 123, false},\n\t\t{123.5, 123.5, false},\n\t\t{[]byte(\"Hello World\"), []byte(\"Hello World\"), false},\n\t\t{new(AssertionTesterConformingObject), new(AssertionTesterConformingObject), false},\n\t\t{&struct{}{}, &struct{}{}, false},\n\n\t\t// Different behaviour from NotEqual()\n\t\t{func() int { return 23 }, func() int { return 24 }, true},\n\t\t{int(10), int(11), true},\n\t\t{int(10), uint(10), false},\n\n\t\t{struct{}{}, struct{}{}, false},\n\t}\n\n\tfor _, c := range cases {\n\t\tt.Run(fmt.Sprintf(\"NotEqualValues(%#v, %#v)\", c.expected, c.actual), func(t *testing.T) {\n\t\t\tres := NotEqualValues(mockT, c.expected, c.actual)\n\n\t\t\tif res != c.result {\n\t\t\t\tt.Errorf(\"NotEqualValues(%#v, %#v) should return %#v\", c.expected, c.actual, c.result)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestContainsNotContains(t *testing.T) {\n\ttype A struct {\n\t\tName, Value string\n\t}\n\tlist := []string{\"Foo\", \"Bar\"}\n\n\tcomplexList := []*A{\n\t\t{\"b\", \"c\"},\n\t\t{\"d\", \"e\"},\n\t\t{\"g\", \"h\"},\n\t\t{\"j\", \"k\"},\n\t}\n\tsimpleMap := map[interface{}]interface{}{\"Foo\": \"Bar\"}\n\tvar zeroMap map[interface{}]interface{}\n\n\tcases := []struct {\n\t\texpected interface{}\n\t\tactual   interface{}\n\t\tresult   bool\n\t}{\n\t\t{\"Hello World\", \"Hello\", true},\n\t\t{\"Hello World\", \"Salut\", false},\n\t\t{list, \"Bar\", true},\n\t\t{list, \"Salut\", false},\n\t\t{complexList, &A{\"g\", \"h\"}, true},\n\t\t{complexList, &A{\"g\", \"e\"}, false},\n\t\t{simpleMap, \"Foo\", true},\n\t\t{simpleMap, \"Bar\", false},\n\t\t{zeroMap, \"Bar\", false},\n\t}\n\n\tfor _, c := range cases {\n\t\tt.Run(fmt.Sprintf(\"Contains(%#v, %#v)\", c.expected, c.actual), func(t *testing.T) {\n\t\t\tmockT := new(testing.T)\n\t\t\tres := Contains(mockT, c.expected, c.actual)\n\n\t\t\tif res != c.result {\n\t\t\t\tif res {\n\t\t\t\t\tt.Errorf(\"Contains(%#v, %#v) should return true:\\n\\t%#v contains %#v\", c.expected, c.actual, c.expected, c.actual)\n\t\t\t\t} else {\n\t\t\t\t\tt.Errorf(\"Contains(%#v, %#v) should return false:\\n\\t%#v does not contain %#v\", c.expected, c.actual, c.expected, c.actual)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n\n\tfor _, c := range cases {\n\t\tt.Run(fmt.Sprintf(\"NotContains(%#v, %#v)\", c.expected, c.actual), func(t *testing.T) {\n\t\t\tmockT := new(testing.T)\n\t\t\tres := NotContains(mockT, c.expected, c.actual)\n\n\t\t\t// NotContains should be inverse of Contains. If it's not, something is wrong\n\t\t\tif res == Contains(mockT, c.expected, c.actual) {\n\t\t\t\tif res {\n\t\t\t\t\tt.Errorf(\"NotContains(%#v, %#v) should return true:\\n\\t%#v does not contains %#v\", c.expected, c.actual, c.expected, c.actual)\n\t\t\t\t} else {\n\t\t\t\t\tt.Errorf(\"NotContains(%#v, %#v) should return false:\\n\\t%#v contains %#v\", c.expected, c.actual, c.expected, c.actual)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestContainsFailMessage(t *testing.T) {\n\tmockT := new(mockTestingT)\n\n\tContains(mockT, \"Hello World\", errors.New(\"Hello\"))\n\texpectedFail := \"\\\"Hello World\\\" does not contain &errors.errorString{s:\\\"Hello\\\"}\"\n\tactualFail := mockT.errorString()\n\tif !strings.Contains(actualFail, expectedFail) {\n\t\tt.Errorf(\"Contains failure should include %q but was %q\", expectedFail, actualFail)\n\t}\n}\n\nfunc TestContainsNotContainsOnNilValue(t *testing.T) {\n\tmockT := new(mockTestingT)\n\n\tContains(mockT, nil, \"key\")\n\texpectedFail := \"<nil> could not be applied builtin len()\"\n\tactualFail := mockT.errorString()\n\tif !strings.Contains(actualFail, expectedFail) {\n\t\tt.Errorf(\"Contains failure should include %q but was %q\", expectedFail, actualFail)\n\t}\n\n\tNotContains(mockT, nil, \"key\")\n\tif !strings.Contains(actualFail, expectedFail) {\n\t\tt.Errorf(\"Contains failure should include %q but was %q\", expectedFail, actualFail)\n\t}\n}\n\nfunc Test_containsElement(t *testing.T) {\n\tlist1 := []string{\"Foo\", \"Bar\"}\n\tlist2 := []int{1, 2}\n\tsimpleMap := map[interface{}]interface{}{\"Foo\": \"Bar\"}\n\n\tok, found := containsElement(\"Hello World\", \"World\")\n\tTrue(t, ok)\n\tTrue(t, found)\n\n\tok, found = containsElement(list1, \"Foo\")\n\tTrue(t, ok)\n\tTrue(t, found)\n\n\tok, found = containsElement(list1, \"Bar\")\n\tTrue(t, ok)\n\tTrue(t, found)\n\n\tok, found = containsElement(list2, 1)\n\tTrue(t, ok)\n\tTrue(t, found)\n\n\tok, found = containsElement(list2, 2)\n\tTrue(t, ok)\n\tTrue(t, found)\n\n\tok, found = containsElement(list1, \"Foo!\")\n\tTrue(t, ok)\n\tFalse(t, found)\n\n\tok, found = containsElement(list2, 3)\n\tTrue(t, ok)\n\tFalse(t, found)\n\n\tok, found = containsElement(list2, \"1\")\n\tTrue(t, ok)\n\tFalse(t, found)\n\n\tok, found = containsElement(simpleMap, \"Foo\")\n\tTrue(t, ok)\n\tTrue(t, found)\n\n\tok, found = containsElement(simpleMap, \"Bar\")\n\tTrue(t, ok)\n\tFalse(t, found)\n\n\tok, found = containsElement(1433, \"1\")\n\tFalse(t, ok)\n\tFalse(t, found)\n}\n\nfunc TestElementsMatch(t *testing.T) {\n\tmockT := new(testing.T)\n\n\tcases := []struct {\n\t\texpected interface{}\n\t\tactual   interface{}\n\t\tresult   bool\n\t}{\n\t\t// matching\n\t\t{nil, nil, true},\n\n\t\t{nil, nil, true},\n\t\t{[]int{}, []int{}, true},\n\t\t{[]int{1}, []int{1}, true},\n\t\t{[]int{1, 1}, []int{1, 1}, true},\n\t\t{[]int{1, 2}, []int{1, 2}, true},\n\t\t{[]int{1, 2}, []int{2, 1}, true},\n\t\t{[2]int{1, 2}, [2]int{2, 1}, true},\n\t\t{[]string{\"hello\", \"world\"}, []string{\"world\", \"hello\"}, true},\n\t\t{[]string{\"hello\", \"hello\"}, []string{\"hello\", \"hello\"}, true},\n\t\t{[]string{\"hello\", \"hello\", \"world\"}, []string{\"hello\", \"world\", \"hello\"}, true},\n\t\t{[3]string{\"hello\", \"hello\", \"world\"}, [3]string{\"hello\", \"world\", \"hello\"}, true},\n\t\t{[]int{}, nil, true},\n\n\t\t// not matching\n\t\t{[]int{1}, []int{1, 1}, false},\n\t\t{[]int{1, 2}, []int{2, 2}, false},\n\t\t{[]string{\"hello\", \"hello\"}, []string{\"hello\"}, false},\n\t}\n\n\tfor _, c := range cases {\n\t\tt.Run(fmt.Sprintf(\"ElementsMatch(%#v, %#v)\", c.expected, c.actual), func(t *testing.T) {\n\t\t\tres := ElementsMatch(mockT, c.actual, c.expected)\n\n\t\t\tif res != c.result {\n\t\t\t\tt.Errorf(\"ElementsMatch(%#v, %#v) should return %v\", c.actual, c.expected, c.result)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestDiffLists(t *testing.T) {\n\ttests := []struct {\n\t\tname   string\n\t\tlistA  interface{}\n\t\tlistB  interface{}\n\t\textraA []interface{}\n\t\textraB []interface{}\n\t}{\n\t\t{\n\t\t\tname:   \"equal empty\",\n\t\t\tlistA:  []string{},\n\t\t\tlistB:  []string{},\n\t\t\textraA: nil,\n\t\t\textraB: nil,\n\t\t},\n\t\t{\n\t\t\tname:   \"equal same order\",\n\t\t\tlistA:  []string{\"hello\", \"world\"},\n\t\t\tlistB:  []string{\"hello\", \"world\"},\n\t\t\textraA: nil,\n\t\t\textraB: nil,\n\t\t},\n\t\t{\n\t\t\tname:   \"equal different order\",\n\t\t\tlistA:  []string{\"hello\", \"world\"},\n\t\t\tlistB:  []string{\"world\", \"hello\"},\n\t\t\textraA: nil,\n\t\t\textraB: nil,\n\t\t},\n\t\t{\n\t\t\tname:   \"extra A\",\n\t\t\tlistA:  []string{\"hello\", \"hello\", \"world\"},\n\t\t\tlistB:  []string{\"hello\", \"world\"},\n\t\t\textraA: []interface{}{\"hello\"},\n\t\t\textraB: nil,\n\t\t},\n\t\t{\n\t\t\tname:   \"extra A twice\",\n\t\t\tlistA:  []string{\"hello\", \"hello\", \"hello\", \"world\"},\n\t\t\tlistB:  []string{\"hello\", \"world\"},\n\t\t\textraA: []interface{}{\"hello\", \"hello\"},\n\t\t\textraB: nil,\n\t\t},\n\t\t{\n\t\t\tname:   \"extra B\",\n\t\t\tlistA:  []string{\"hello\", \"world\"},\n\t\t\tlistB:  []string{\"hello\", \"hello\", \"world\"},\n\t\t\textraA: nil,\n\t\t\textraB: []interface{}{\"hello\"},\n\t\t},\n\t\t{\n\t\t\tname:   \"extra B twice\",\n\t\t\tlistA:  []string{\"hello\", \"world\"},\n\t\t\tlistB:  []string{\"hello\", \"hello\", \"world\", \"hello\"},\n\t\t\textraA: nil,\n\t\t\textraB: []interface{}{\"hello\", \"hello\"},\n\t\t},\n\t\t{\n\t\t\tname:   \"integers 1\",\n\t\t\tlistA:  []int{1, 2, 3, 4, 5},\n\t\t\tlistB:  []int{5, 4, 3, 2, 1},\n\t\t\textraA: nil,\n\t\t\textraB: nil,\n\t\t},\n\t\t{\n\t\t\tname:   \"integers 2\",\n\t\t\tlistA:  []int{1, 2, 1, 2, 1},\n\t\t\tlistB:  []int{2, 1, 2, 1, 2},\n\t\t\textraA: []interface{}{1},\n\t\t\textraB: []interface{}{2},\n\t\t},\n\t}\n\tfor _, test := range tests {\n\t\ttest := test\n\t\tt.Run(test.name, func(t *testing.T) {\n\t\t\tactualExtraA, actualExtraB := diffLists(test.listA, test.listB)\n\t\t\tEqual(t, test.extraA, actualExtraA, \"extra A does not match for listA=%v listB=%v\",\n\t\t\t\ttest.listA, test.listB)\n\t\t\tEqual(t, test.extraB, actualExtraB, \"extra B does not match for listA=%v listB=%v\",\n\t\t\t\ttest.listA, test.listB)\n\t\t})\n\t}\n}\n\nfunc TestNoError(t *testing.T) {\n\tmockT := new(testing.T)\n\n\t// start with a nil error\n\tvar err error\n\n\tTrue(t, NoError(mockT, err), \"NoError should return True for nil arg\")\n\n\t// now set an error\n\terr = errors.New(\"some error\")\n\n\tFalse(t, NoError(mockT, err), \"NoError with error should return False\")\n\n\t// returning an empty error interface\n\terr = func() error {\n\t\tvar err *customError\n\t\treturn err\n\t}()\n\n\tif err == nil { // err is not nil here!\n\t\tt.Errorf(\"Error should be nil due to empty interface: %s\", err)\n\t}\n\n\tFalse(t, NoError(mockT, err), \"NoError should fail with empty error interface\")\n}\n\ntype customError struct{}\n\nfunc (*customError) Error() string { return \"fail\" }\n\nfunc TestError(t *testing.T) {\n\tmockT := new(testing.T)\n\n\t// start with a nil error\n\tvar err error\n\n\tFalse(t, Error(mockT, err), \"Error should return False for nil arg\")\n\n\t// now set an error\n\terr = errors.New(\"some error\")\n\n\tTrue(t, Error(mockT, err), \"Error with error should return True\")\n\n\t// go vet check\n\tTrue(t, Errorf(mockT, err, \"example with %s\", \"formatted message\"), \"Errorf with error should return True\")\n\n\t// returning an empty error interface\n\terr = func() error {\n\t\tvar err *customError\n\t\treturn err\n\t}()\n\n\tif err == nil { // err is not nil here!\n\t\tt.Errorf(\"Error should be nil due to empty interface: %s\", err)\n\t}\n\n\tTrue(t, Error(mockT, err), \"Error should pass with empty error interface\")\n}\n\nfunc TestEqualError(t *testing.T) {\n\tmockT := new(testing.T)\n\n\t// start with a nil error\n\tvar err error\n\tFalse(t, EqualError(mockT, err, \"\"),\n\t\t\"EqualError should return false for nil arg\")\n\n\t// now set an error\n\terr = errors.New(\"some error\")\n\tFalse(t, EqualError(mockT, err, \"Not some error\"),\n\t\t\"EqualError should return false for different error string\")\n\tTrue(t, EqualError(mockT, err, \"some error\"),\n\t\t\"EqualError should return true\")\n}\n\nfunc TestErrorContains(t *testing.T) {\n\tmockT := new(testing.T)\n\n\t// start with a nil error\n\tvar err error\n\tFalse(t, ErrorContains(mockT, err, \"\"),\n\t\t\"ErrorContains should return false for nil arg\")\n\n\t// now set an error\n\terr = errors.New(\"some error: another error\")\n\tFalse(t, ErrorContains(mockT, err, \"bad error\"),\n\t\t\"ErrorContains should return false for different error string\")\n\tTrue(t, ErrorContains(mockT, err, \"some error\"),\n\t\t\"ErrorContains should return true\")\n\tTrue(t, ErrorContains(mockT, err, \"another error\"),\n\t\t\"ErrorContains should return true\")\n}\n\nfunc Test_isEmpty(t *testing.T) {\n\tchWithValue := make(chan struct{}, 1)\n\tchWithValue <- struct{}{}\n\n\tTrue(t, isEmpty(\"\"))\n\tTrue(t, isEmpty(nil))\n\tTrue(t, isEmpty([]string{}))\n\tTrue(t, isEmpty(0))\n\tTrue(t, isEmpty(int32(0)))\n\tTrue(t, isEmpty(int64(0)))\n\tTrue(t, isEmpty(false))\n\tTrue(t, isEmpty(map[string]string{}))\n\tTrue(t, isEmpty(new(time.Time)))\n\tTrue(t, isEmpty(time.Time{}))\n\tTrue(t, isEmpty(make(chan struct{})))\n\tTrue(t, isEmpty([1]int{}))\n\tFalse(t, isEmpty(\"something\"))\n\tFalse(t, isEmpty(errors.New(\"something\")))\n\tFalse(t, isEmpty([]string{\"something\"}))\n\tFalse(t, isEmpty(1))\n\tFalse(t, isEmpty(true))\n\tFalse(t, isEmpty(map[string]string{\"Hello\": \"World\"}))\n\tFalse(t, isEmpty(chWithValue))\n\tFalse(t, isEmpty([1]int{42}))\n}\n\nfunc Test_getLen(t *testing.T) {\n\tfalseCases := []interface{}{\n\t\tnil,\n\t\t0,\n\t\ttrue,\n\t\tfalse,\n\t\t'A',\n\t\tstruct{}{},\n\t}\n\tfor _, v := range falseCases {\n\t\tok, l := getLen(v)\n\t\tFalse(t, ok, \"Expected getLen fail to get length of %#v\", v)\n\t\tEqual(t, 0, l, \"getLen should return 0 for %#v\", v)\n\t}\n\n\tch := make(chan int, 5)\n\tch <- 1\n\tch <- 2\n\tch <- 3\n\ttrueCases := []struct {\n\t\tv interface{}\n\t\tl int\n\t}{\n\t\t{[]int{1, 2, 3}, 3},\n\t\t{[...]int{1, 2, 3}, 3},\n\t\t{\"ABC\", 3},\n\t\t{map[int]int{1: 2, 2: 4, 3: 6}, 3},\n\t\t{ch, 3},\n\n\t\t{[]int{}, 0},\n\t\t{map[int]int{}, 0},\n\t\t{make(chan int), 0},\n\n\t\t{[]int(nil), 0},\n\t\t{map[int]int(nil), 0},\n\t\t{(chan int)(nil), 0},\n\t}\n\n\tfor _, c := range trueCases {\n\t\tok, l := getLen(c.v)\n\t\tTrue(t, ok, \"Expected getLen success to get length of %#v\", c.v)\n\t\tEqual(t, c.l, l)\n\t}\n}\n\nfunc TestLen(t *testing.T) {\n\tmockT := new(testing.T)\n\n\tFalse(t, Len(mockT, nil, 0), \"nil does not have length\")\n\tFalse(t, Len(mockT, 0, 0), \"int does not have length\")\n\tFalse(t, Len(mockT, true, 0), \"true does not have length\")\n\tFalse(t, Len(mockT, false, 0), \"false does not have length\")\n\tFalse(t, Len(mockT, 'A', 0), \"Rune does not have length\")\n\tFalse(t, Len(mockT, struct{}{}, 0), \"Struct does not have length\")\n\n\tch := make(chan int, 5)\n\tch <- 1\n\tch <- 2\n\tch <- 3\n\n\tcases := []struct {\n\t\tv interface{}\n\t\tl int\n\t}{\n\t\t{[]int{1, 2, 3}, 3},\n\t\t{[...]int{1, 2, 3}, 3},\n\t\t{\"ABC\", 3},\n\t\t{map[int]int{1: 2, 2: 4, 3: 6}, 3},\n\t\t{ch, 3},\n\n\t\t{[]int{}, 0},\n\t\t{map[int]int{}, 0},\n\t\t{make(chan int), 0},\n\n\t\t{[]int(nil), 0},\n\t\t{map[int]int(nil), 0},\n\t\t{(chan int)(nil), 0},\n\t}\n\n\tfor _, c := range cases {\n\t\tTrue(t, Len(mockT, c.v, c.l), \"%#v have %d items\", c.v, c.l)\n\t}\n\n\tcases = []struct {\n\t\tv interface{}\n\t\tl int\n\t}{\n\t\t{[]int{1, 2, 3}, 4},\n\t\t{[...]int{1, 2, 3}, 2},\n\t\t{\"ABC\", 2},\n\t\t{map[int]int{1: 2, 2: 4, 3: 6}, 4},\n\t\t{ch, 2},\n\n\t\t{[]int{}, 1},\n\t\t{map[int]int{}, 1},\n\t\t{make(chan int), 1},\n\n\t\t{[]int(nil), 1},\n\t\t{map[int]int(nil), 1},\n\t\t{(chan int)(nil), 1},\n\t}\n\n\tfor _, c := range cases {\n\t\tFalse(t, Len(mockT, c.v, c.l), \"%#v have %d items\", c.v, c.l)\n\t}\n}\n\nfunc TestWithinDuration(t *testing.T) {\n\tmockT := new(testing.T)\n\ta := time.Now()\n\tb := a.Add(10 * time.Second)\n\n\tTrue(t, WithinDuration(mockT, a, b, 10*time.Second), \"A 10s difference is within a 10s time difference\")\n\tTrue(t, WithinDuration(mockT, b, a, 10*time.Second), \"A 10s difference is within a 10s time difference\")\n\n\tFalse(t, WithinDuration(mockT, a, b, 9*time.Second), \"A 10s difference is not within a 9s time difference\")\n\tFalse(t, WithinDuration(mockT, b, a, 9*time.Second), \"A 10s difference is not within a 9s time difference\")\n\n\tFalse(t, WithinDuration(mockT, a, b, -9*time.Second), \"A 10s difference is not within a 9s time difference\")\n\tFalse(t, WithinDuration(mockT, b, a, -9*time.Second), \"A 10s difference is not within a 9s time difference\")\n\n\tFalse(t, WithinDuration(mockT, a, b, -11*time.Second), \"A 10s difference is not within a 9s time difference\")\n\tFalse(t, WithinDuration(mockT, b, a, -11*time.Second), \"A 10s difference is not within a 9s time difference\")\n}\n\nfunc TestInDelta(t *testing.T) {\n\tmockT := new(testing.T)\n\n\tTrue(t, InDelta(mockT, 1.001, 1, 0.01), \"|1.001 - 1| <= 0.01\")\n\tTrue(t, InDelta(mockT, 1, 1.001, 0.01), \"|1 - 1.001| <= 0.01\")\n\tTrue(t, InDelta(mockT, 1, 2, 1), \"|1 - 2| <= 1\")\n\tFalse(t, InDelta(mockT, 1, 2, 0.5), \"Expected |1 - 2| <= 0.5 to fail\")\n\tFalse(t, InDelta(mockT, 2, 1, 0.5), \"Expected |2 - 1| <= 0.5 to fail\")\n\tFalse(t, InDelta(mockT, \"\", nil, 1), \"Expected non numerals to fail\")\n\tFalse(t, InDelta(mockT, 42, math.NaN(), 0.01), \"Expected NaN for actual to fail\")\n\tFalse(t, InDelta(mockT, math.NaN(), 42, 0.01), \"Expected NaN for expected to fail\")\n\tTrue(t, InDelta(mockT, math.NaN(), math.NaN(), 0.01), \"Expected NaN for both to pass\")\n\n\tcases := []struct {\n\t\ta, b  interface{}\n\t\tdelta float64\n\t}{\n\t\t{uint(2), uint(1), 1},\n\t\t{uint8(2), uint8(1), 1},\n\t\t{uint16(2), uint16(1), 1},\n\t\t{uint32(2), uint32(1), 1},\n\t\t{uint64(2), uint64(1), 1},\n\n\t\t{int(2), int(1), 1},\n\t\t{int8(2), int8(1), 1},\n\t\t{int16(2), int16(1), 1},\n\t\t{int32(2), int32(1), 1},\n\t\t{int64(2), int64(1), 1},\n\n\t\t{float32(2), float32(1), 1},\n\t\t{float64(2), float64(1), 1},\n\t}\n\n\tfor _, tc := range cases {\n\t\tTrue(t, InDelta(mockT, tc.a, tc.b, tc.delta), \"Expected |%V - %V| <= %v\", tc.a, tc.b, tc.delta)\n\t}\n}\n\ntype diffTestingStruct struct {\n\tA string\n\tB int\n}\n\nfunc (d *diffTestingStruct) String() string {\n\treturn d.A\n}\n\nfunc TestDiff(t *testing.T) {\n\texpected := `\n\nDiff:\n--- Expected\n+++ Actual\n@@ -1,3 +1,3 @@\n (struct { foo string }) {\n- foo: (string) (len=5) \"hello\"\n+ foo: (string) (len=3) \"bar\"\n }\n`\n\tactual := diff(\n\t\tstruct{ foo string }{\"hello\"},\n\t\tstruct{ foo string }{\"bar\"},\n\t)\n\tEqual(t, expected, actual)\n\n\texpected = `\n\nDiff:\n--- Expected\n+++ Actual\n@@ -2,5 +2,5 @@\n  (int) 1,\n- (int) 2,\n  (int) 3,\n- (int) 4\n+ (int) 5,\n+ (int) 7\n }\n`\n\tactual = diff(\n\t\t[]int{1, 2, 3, 4},\n\t\t[]int{1, 3, 5, 7},\n\t)\n\tEqual(t, expected, actual)\n\n\texpected = `\n\nDiff:\n--- Expected\n+++ Actual\n@@ -2,4 +2,4 @@\n  (int) 1,\n- (int) 2,\n- (int) 3\n+ (int) 3,\n+ (int) 5\n }\n`\n\tactual = diff(\n\t\t[]int{1, 2, 3, 4}[0:3],\n\t\t[]int{1, 3, 5, 7}[0:3],\n\t)\n\tEqual(t, expected, actual)\n\n\texpected = `\n\nDiff:\n--- Expected\n+++ Actual\n@@ -1,6 +1,6 @@\n (map[string]int) (len=4) {\n- (string) (len=4) \"four\": (int) 4,\n+ (string) (len=4) \"five\": (int) 5,\n  (string) (len=3) \"one\": (int) 1,\n- (string) (len=5) \"three\": (int) 3,\n- (string) (len=3) \"two\": (int) 2\n+ (string) (len=5) \"seven\": (int) 7,\n+ (string) (len=5) \"three\": (int) 3\n }\n`\n\n\tactual = diff(\n\t\tmap[string]int{\"one\": 1, \"two\": 2, \"three\": 3, \"four\": 4},\n\t\tmap[string]int{\"one\": 1, \"three\": 3, \"five\": 5, \"seven\": 7},\n\t)\n\tEqual(t, expected, actual)\n\n\texpected = `\n\nDiff:\n--- Expected\n+++ Actual\n@@ -1,3 +1,3 @@\n (*errors.errorString)({\n- s: (string) (len=19) \"some expected error\"\n+ s: (string) (len=12) \"actual error\"\n })\n`\n\n\tactual = diff(\n\t\terrors.New(\"some expected error\"),\n\t\terrors.New(\"actual error\"),\n\t)\n\tEqual(t, expected, actual)\n\n\texpected = `\n\nDiff:\n--- Expected\n+++ Actual\n@@ -2,3 +2,3 @@\n  A: (string) (len=11) \"some string\",\n- B: (int) 10\n+ B: (int) 15\n }\n`\n\n\tactual = diff(\n\t\tdiffTestingStruct{A: \"some string\", B: 10},\n\t\tdiffTestingStruct{A: \"some string\", B: 15},\n\t)\n\tEqual(t, expected, actual)\n\n\texpected = `\n\nDiff:\n--- Expected\n+++ Actual\n@@ -1,2 +1,2 @@\n-(time.Time) 2020-09-24 00:00:00 +0000 UTC\n+(time.Time) 2020-09-25 00:00:00 +0000 UTC\n \n`\n\n\tactual = diff(\n\t\ttime.Date(2020, 9, 24, 0, 0, 0, 0, time.UTC),\n\t\ttime.Date(2020, 9, 25, 0, 0, 0, 0, time.UTC),\n\t)\n\tEqual(t, expected, actual)\n}\n\nfunc TestTimeEqualityErrorFormatting(_ *testing.T) {\n\tmockT := new(mockTestingT)\n\n\tEqual(mockT, time.Second*2, time.Millisecond)\n}\n\nfunc TestDiffEmptyCases(t *testing.T) {\n\tEqual(t, \"\", diff(nil, nil))\n\tEqual(t, \"\", diff(struct{ foo string }{}, nil))\n\tEqual(t, \"\", diff(nil, struct{ foo string }{}))\n\tEqual(t, \"\", diff(1, 2))\n\tEqual(t, \"\", diff(1, 2))\n\tEqual(t, \"\", diff([]int{1}, []bool{true}))\n}\n\n// Ensure there are no data races\nfunc TestDiffRace(t *testing.T) {\n\tt.Parallel()\n\n\texpected := map[string]string{\n\t\t\"a\": \"A\",\n\t\t\"b\": \"B\",\n\t\t\"c\": \"C\",\n\t}\n\n\tactual := map[string]string{\n\t\t\"d\": \"D\",\n\t\t\"e\": \"E\",\n\t\t\"f\": \"F\",\n\t}\n\n\t// run diffs in parallel simulating tests with t.Parallel()\n\tnumRoutines := 10\n\trChans := make([]chan string, numRoutines)\n\tfor idx := range rChans {\n\t\trChans[idx] = make(chan string)\n\t\tgo func(ch chan string) {\n\t\t\tdefer close(ch)\n\t\t\tch <- diff(expected, actual)\n\t\t}(rChans[idx])\n\t}\n\n\tfor _, ch := range rChans {\n\t\tfor msg := range ch {\n\t\t\tNotEqual(t, msg, \"\") // dummy assert\n\t\t}\n\t}\n}\n\ntype mockTestingT struct {\n\terrorFmt string\n\targs     []interface{}\n}\n\nfunc (m *mockTestingT) errorString() string {\n\treturn fmt.Sprintf(m.errorFmt, m.args...)\n}\n\nfunc (m *mockTestingT) Errorf(format string, args ...interface{}) {\n\tm.errorFmt = format\n\tm.args = args\n}\n\ntype mockFailNowTestingT struct{}\n\nfunc (m *mockFailNowTestingT) Errorf(string, ...interface{}) {}\n\nfunc (m *mockFailNowTestingT) FailNow() {}\n\nfunc TestBytesEqual(t *testing.T) {\n\tcases := []struct {\n\t\ta, b []byte\n\t}{\n\t\t{make([]byte, 2), make([]byte, 2)},\n\t\t{make([]byte, 2), make([]byte, 2, 3)},\n\t\t{nil, make([]byte, 0)},\n\t}\n\tfor i, c := range cases {\n\t\tEqual(t, reflect.DeepEqual(c.a, c.b), ObjectsAreEqual(c.a, c.b), \"case %d failed\", i+1)\n\t}\n}\n\nfunc BenchmarkBytesEqual(b *testing.B) {\n\tconst size = 1024 * 8\n\ts := make([]byte, size)\n\tfor i := range s {\n\t\ts[i] = byte(i % 255)\n\t}\n\ts2 := make([]byte, size)\n\tcopy(s2, s)\n\n\tmockT := &mockFailNowTestingT{}\n\tb.ResetTimer()\n\tfor i := 0; i < b.N; i++ {\n\t\tEqual(mockT, s, s2)\n\t}\n}\n\nfunc BenchmarkNotNil(b *testing.B) {\n\tfor i := 0; i < b.N; i++ {\n\t\tNotNil(b, b)\n\t}\n}\n\nfunc TestEventuallyFalse(t *testing.T) {\n\tmockT := new(testing.T)\n\n\tcondition := func() bool {\n\t\treturn false\n\t}\n\n\tFalse(t, Eventually(mockT, condition, 100*time.Millisecond, 20*time.Millisecond))\n}\n\nfunc TestEventuallyTrue(t *testing.T) {\n\tstate := 0\n\tcondition := func() bool {\n\t\tdefer func() {\n\t\t\tstate++\n\t\t}()\n\t\treturn state == 2\n\t}\n\n\tTrue(t, Eventually(t, condition, 100*time.Millisecond, 20*time.Millisecond))\n}\n\nfunc Test_validateEqualArgs(t *testing.T) {\n\tif validateEqualArgs(func() {}, func() {}) == nil {\n\t\tt.Error(\"non-nil functions should error\")\n\t}\n\n\tif validateEqualArgs(func() {}, func() {}) == nil {\n\t\tt.Error(\"non-nil functions should error\")\n\t}\n\n\tif validateEqualArgs(nil, nil) != nil {\n\t\tt.Error(\"nil functions are equal\")\n\t}\n}\n\nfunc Test_truncatingFormat(t *testing.T) {\n\toriginal := strings.Repeat(\"a\", bufio.MaxScanTokenSize-102)\n\tresult := truncatingFormat(original)\n\tEqual(t, fmt.Sprintf(\"%#v\", original), result, \"string should not be truncated\")\n\n\toriginal = original + \"x\"\n\tresult = truncatingFormat(original)\n\tNotEqual(t, fmt.Sprintf(\"%#v\", original), result, \"string should have been truncated.\")\n\n\tif !strings.HasSuffix(result, \"<... truncated>\") {\n\t\tt.Error(\"truncated string should have <... truncated> suffix\")\n\t}\n}\n"
  },
  {
    "path": "internal/assert/difflib.go",
    "content": "// Copied from https://github.com/pmezard/go-difflib/blob/5d4384ee4fb2527b0a1256a821ebfc92f91efefc/difflib/difflib.go\n\n// Copyright 2013 Patrick Mezard. All rights reserved. Use of this source code is\n// governed by a license that can be found in the THIRD-PARTY-NOTICES file.\n\npackage assert\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t\"fmt\"\n\t\"io\"\n\t\"strings\"\n)\n\nfunc min(a, b int) int {\n\tif a < b {\n\t\treturn a\n\t}\n\treturn b\n}\n\nfunc max(a, b int) int {\n\tif a > b {\n\t\treturn a\n\t}\n\treturn b\n}\n\nfunc calculateRatio(matches, length int) float64 {\n\tif length > 0 {\n\t\treturn 2.0 * float64(matches) / float64(length)\n\t}\n\treturn 1.0\n}\n\ntype Match struct {\n\tA    int\n\tB    int\n\tSize int\n}\n\ntype OpCode struct {\n\tTag byte\n\tI1  int\n\tI2  int\n\tJ1  int\n\tJ2  int\n}\n\n// SequenceMatcher compares sequence of strings. The basic\n// algorithm predates, and is a little fancier than, an algorithm\n// published in the late 1980's by Ratcliff and Obershelp under the\n// hyperbolic name \"gestalt pattern matching\".  The basic idea is to find\n// the longest contiguous matching subsequence that contains no \"junk\"\n// elements (R-O doesn't address junk).  The same idea is then applied\n// recursively to the pieces of the sequences to the left and to the right\n// of the matching subsequence.  This does not yield minimal edit\n// sequences, but does tend to yield matches that \"look right\" to people.\n//\n// SequenceMatcher tries to compute a \"human-friendly diff\" between two\n// sequences.  Unlike e.g. UNIX(tm) diff, the fundamental notion is the\n// longest *contiguous* & junk-free matching subsequence.  That's what\n// catches peoples' eyes.  The Windows(tm) windiff has another interesting\n// notion, pairing up elements that appear uniquely in each sequence.\n// That, and the method here, appear to yield more intuitive difference\n// reports than does diff.  This method appears to be the least vulnerable\n// to syncing up on blocks of \"junk lines\", though (like blank lines in\n// ordinary text files, or maybe \"<P>\" lines in HTML files).  That may be\n// because this is the only method of the 3 that has a *concept* of\n// \"junk\" <wink>.\n//\n// Timing:  Basic R-O is cubic time worst case and quadratic time expected\n// case.  SequenceMatcher is quadratic time for the worst case and has\n// expected-case behavior dependent in a complicated way on how many\n// elements the sequences have in common; best case time is linear.\ntype SequenceMatcher struct {\n\ta              []string\n\tb              []string\n\tb2j            map[string][]int\n\tIsJunk         func(string) bool\n\tautoJunk       bool\n\tbJunk          map[string]struct{}\n\tmatchingBlocks []Match\n\tfullBCount     map[string]int\n\tbPopular       map[string]struct{}\n\topCodes        []OpCode\n}\n\nfunc NewMatcher(a, b []string) *SequenceMatcher {\n\tm := SequenceMatcher{autoJunk: true}\n\tm.SetSeqs(a, b)\n\treturn &m\n}\n\nfunc NewMatcherWithJunk(a, b []string, autoJunk bool,\n\tisJunk func(string) bool,\n) *SequenceMatcher {\n\tm := SequenceMatcher{IsJunk: isJunk, autoJunk: autoJunk}\n\tm.SetSeqs(a, b)\n\treturn &m\n}\n\n// SetSeqs sets the two sequences to be compared.\nfunc (m *SequenceMatcher) SetSeqs(a, b []string) {\n\tm.SetSeq1(a)\n\tm.SetSeq2(b)\n}\n\n// SetSeq1 sets the first sequence to be compared. The second sequence to be compared is\n// not changed.\n//\n// SequenceMatcher computes and caches detailed information about the second\n// sequence, so if you want to compare one sequence S against many sequences,\n// use .SetSeq2(s) once and call .SetSeq1(x) repeatedly for each of the other\n// sequences.\n//\n// See also SetSeqs() and SetSeq2().\nfunc (m *SequenceMatcher) SetSeq1(a []string) {\n\tif &a == &m.a {\n\t\treturn\n\t}\n\tm.a = a\n\tm.matchingBlocks = nil\n\tm.opCodes = nil\n}\n\n// SetSeq2 sets the second sequence to be compared. The first sequence to be compared is\n// not changed.\nfunc (m *SequenceMatcher) SetSeq2(b []string) {\n\tif &b == &m.b {\n\t\treturn\n\t}\n\tm.b = b\n\tm.matchingBlocks = nil\n\tm.opCodes = nil\n\tm.fullBCount = nil\n\tm.chainB()\n}\n\nfunc (m *SequenceMatcher) chainB() {\n\t// Populate line -> index mapping\n\tb2j := map[string][]int{}\n\tfor i, s := range m.b {\n\t\tindices := b2j[s]\n\t\tindices = append(indices, i)\n\t\tb2j[s] = indices\n\t}\n\n\t// Purge junk elements\n\tm.bJunk = map[string]struct{}{}\n\tif m.IsJunk != nil {\n\t\tjunk := m.bJunk\n\t\tfor s := range b2j {\n\t\t\tif m.IsJunk(s) {\n\t\t\t\tjunk[s] = struct{}{}\n\t\t\t}\n\t\t}\n\t\tfor s := range junk {\n\t\t\tdelete(b2j, s)\n\t\t}\n\t}\n\n\t// Purge remaining popular elements\n\tpopular := map[string]struct{}{}\n\tn := len(m.b)\n\tif m.autoJunk && n >= 200 {\n\t\tntest := n/100 + 1\n\t\tfor s, indices := range b2j {\n\t\t\tif len(indices) > ntest {\n\t\t\t\tpopular[s] = struct{}{}\n\t\t\t}\n\t\t}\n\t\tfor s := range popular {\n\t\t\tdelete(b2j, s)\n\t\t}\n\t}\n\tm.bPopular = popular\n\tm.b2j = b2j\n}\n\nfunc (m *SequenceMatcher) isBJunk(s string) bool {\n\t_, ok := m.bJunk[s]\n\treturn ok\n}\n\n// Find longest matching block in a[alo:ahi] and b[blo:bhi].\n//\n// If IsJunk is not defined:\n//\n// Return (i,j,k) such that a[i:i+k] is equal to b[j:j+k], where\n//\n//\talo <= i <= i+k <= ahi\n//\tblo <= j <= j+k <= bhi\n//\n// and for all (i',j',k') meeting those conditions,\n//\n//\tk >= k'\n//\ti <= i'\n//\tand if i == i', j <= j'\n//\n// In other words, of all maximal matching blocks, return one that\n// starts earliest in a, and of all those maximal matching blocks that\n// start earliest in a, return the one that starts earliest in b.\n//\n// If IsJunk is defined, first the longest matching block is\n// determined as above, but with the additional restriction that no\n// junk element appears in the block.  Then that block is extended as\n// far as possible by matching (only) junk elements on both sides.  So\n// the resulting block never matches on junk except as identical junk\n// happens to be adjacent to an \"interesting\" match.\n//\n// If no blocks match, return (alo, blo, 0).\nfunc (m *SequenceMatcher) findLongestMatch(alo, ahi, blo, bhi int) Match {\n\t// CAUTION:  stripping common prefix or suffix would be incorrect.\n\t// E.g.,\n\t//    ab\n\t//    acab\n\t// Longest matching block is \"ab\", but if common prefix is\n\t// stripped, it's \"a\" (tied with \"b\").  UNIX(tm) diff does so\n\t// strip, so ends up claiming that ab is changed to acab by\n\t// inserting \"ca\" in the middle.  That's minimal but unintuitive:\n\t// \"it's obvious\" that someone inserted \"ac\" at the front.\n\t// Windiff ends up at the same place as diff, but by pairing up\n\t// the unique 'b's and then matching the first two 'a's.\n\tbesti, bestj, bestsize := alo, blo, 0\n\n\t// find longest junk-free match\n\t// during an iteration of the loop, j2len[j] = length of longest\n\t// junk-free match ending with a[i-1] and b[j]\n\tj2len := map[int]int{}\n\tfor i := alo; i != ahi; i++ {\n\t\t// look at all instances of a[i] in b; note that because\n\t\t// b2j has no junk keys, the loop is skipped if a[i] is junk\n\t\tnewj2len := map[int]int{}\n\t\tfor _, j := range m.b2j[m.a[i]] {\n\t\t\t// a[i] matches b[j]\n\t\t\tif j < blo {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif j >= bhi {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tk := j2len[j-1] + 1\n\t\t\tnewj2len[j] = k\n\t\t\tif k > bestsize {\n\t\t\t\tbesti, bestj, bestsize = i-k+1, j-k+1, k\n\t\t\t}\n\t\t}\n\t\tj2len = newj2len\n\t}\n\n\t// Extend the best by non-junk elements on each end.  In particular,\n\t// \"popular\" non-junk elements aren't in b2j, which greatly speeds\n\t// the inner loop above, but also means \"the best\" match so far\n\t// doesn't contain any junk *or* popular non-junk elements.\n\tfor besti > alo && bestj > blo && !m.isBJunk(m.b[bestj-1]) &&\n\t\tm.a[besti-1] == m.b[bestj-1] {\n\t\tbesti, bestj, bestsize = besti-1, bestj-1, bestsize+1\n\t}\n\tfor besti+bestsize < ahi && bestj+bestsize < bhi &&\n\t\t!m.isBJunk(m.b[bestj+bestsize]) &&\n\t\tm.a[besti+bestsize] == m.b[bestj+bestsize] {\n\t\tbestsize++\n\t}\n\n\t// Now that we have a wholly interesting match (albeit possibly\n\t// empty!), we may as well suck up the matching junk on each\n\t// side of it too.  Can't think of a good reason not to, and it\n\t// saves post-processing the (possibly considerable) expense of\n\t// figuring out what to do with it.  In the case of an empty\n\t// interesting match, this is clearly the right thing to do,\n\t// because no other kind of match is possible in the regions.\n\tfor besti > alo && bestj > blo && m.isBJunk(m.b[bestj-1]) &&\n\t\tm.a[besti-1] == m.b[bestj-1] {\n\t\tbesti, bestj, bestsize = besti-1, bestj-1, bestsize+1\n\t}\n\tfor besti+bestsize < ahi && bestj+bestsize < bhi &&\n\t\tm.isBJunk(m.b[bestj+bestsize]) &&\n\t\tm.a[besti+bestsize] == m.b[bestj+bestsize] {\n\t\tbestsize++\n\t}\n\n\treturn Match{A: besti, B: bestj, Size: bestsize}\n}\n\n// GetMatchingBlocks returns list of triples describing matching subsequences.\n//\n// Each triple is of the form (i, j, n), and means that\n// a[i:i+n] == b[j:j+n].  The triples are monotonically increasing in\n// i and in j. It's also guaranteed that if (i, j, n) and (i', j', n') are\n// adjacent triples in the list, and the second is not the last triple in the\n// list, then i+n != i' or j+n != j'. IOW, adjacent triples never describe\n// adjacent equal blocks.\n//\n// The last triple is a dummy, (len(a), len(b), 0), and is the only\n// triple with n==0.\nfunc (m *SequenceMatcher) GetMatchingBlocks() []Match {\n\tif m.matchingBlocks != nil {\n\t\treturn m.matchingBlocks\n\t}\n\n\tvar matchBlocks func(alo, ahi, blo, bhi int, matched []Match) []Match\n\tmatchBlocks = func(alo, ahi, blo, bhi int, matched []Match) []Match {\n\t\tmatch := m.findLongestMatch(alo, ahi, blo, bhi)\n\t\ti, j, k := match.A, match.B, match.Size\n\t\tif match.Size > 0 {\n\t\t\tif alo < i && blo < j {\n\t\t\t\tmatched = matchBlocks(alo, i, blo, j, matched)\n\t\t\t}\n\t\t\tmatched = append(matched, match)\n\t\t\tif i+k < ahi && j+k < bhi {\n\t\t\t\tmatched = matchBlocks(i+k, ahi, j+k, bhi, matched)\n\t\t\t}\n\t\t}\n\t\treturn matched\n\t}\n\tmatched := matchBlocks(0, len(m.a), 0, len(m.b), nil)\n\n\t// It's possible that we have adjacent equal blocks in the\n\t// matching_blocks list now.\n\tnonAdjacent := []Match{}\n\ti1, j1, k1 := 0, 0, 0\n\tfor _, b := range matched {\n\t\t// Is this block adjacent to i1, j1, k1?\n\t\ti2, j2, k2 := b.A, b.B, b.Size\n\t\tif i1+k1 == i2 && j1+k1 == j2 {\n\t\t\t// Yes, so collapse them -- this just increases the length of\n\t\t\t// the first block by the length of the second, and the first\n\t\t\t// block so lengthened remains the block to compare against.\n\t\t\tk1 += k2\n\t\t} else {\n\t\t\t// Not adjacent.  Remember the first block (k1==0 means it's\n\t\t\t// the dummy we started with), and make the second block the\n\t\t\t// new block to compare against.\n\t\t\tif k1 > 0 {\n\t\t\t\tnonAdjacent = append(nonAdjacent, Match{i1, j1, k1})\n\t\t\t}\n\t\t\ti1, j1, k1 = i2, j2, k2\n\t\t}\n\t}\n\tif k1 > 0 {\n\t\tnonAdjacent = append(nonAdjacent, Match{i1, j1, k1})\n\t}\n\n\tnonAdjacent = append(nonAdjacent, Match{len(m.a), len(m.b), 0})\n\tm.matchingBlocks = nonAdjacent\n\treturn m.matchingBlocks\n}\n\n// GetOpCodes returns a list of 5-tuples describing how to turn a into b.\n//\n// Each tuple is of the form (tag, i1, i2, j1, j2).  The first tuple\n// has i1 == j1 == 0, and remaining tuples have i1 == the i2 from the\n// tuple preceding it, and likewise for j1 == the previous j2.\n//\n// The tags are characters, with these meanings:\n//\n// 'r' (replace):  a[i1:i2] should be replaced by b[j1:j2]\n//\n// 'd' (delete):   a[i1:i2] should be deleted, j1==j2 in this case.\n//\n// 'i' (insert):   b[j1:j2] should be inserted at a[i1:i1], i1==i2 in this case.\n//\n// 'e' (equal):    a[i1:i2] == b[j1:j2]\nfunc (m *SequenceMatcher) GetOpCodes() []OpCode {\n\tif m.opCodes != nil {\n\t\treturn m.opCodes\n\t}\n\ti, j := 0, 0\n\tmatching := m.GetMatchingBlocks()\n\topCodes := make([]OpCode, 0, len(matching))\n\tfor _, m := range matching {\n\t\t//  invariant:  we've pumped out correct diffs to change\n\t\t//  a[:i] into b[:j], and the next matching block is\n\t\t//  a[ai:ai+size] == b[bj:bj+size]. So we need to pump\n\t\t//  out a diff to change a[i:ai] into b[j:bj], pump out\n\t\t//  the matching block, and move (i,j) beyond the match\n\t\tai, bj, size := m.A, m.B, m.Size\n\t\ttag := byte(0)\n\t\tif i < ai && j < bj {\n\t\t\ttag = 'r'\n\t\t} else if i < ai {\n\t\t\ttag = 'd'\n\t\t} else if j < bj {\n\t\t\ttag = 'i'\n\t\t}\n\t\tif tag > 0 {\n\t\t\topCodes = append(opCodes, OpCode{tag, i, ai, j, bj})\n\t\t}\n\t\ti, j = ai+size, bj+size\n\t\t// the list of matching blocks is terminated by a\n\t\t// sentinel with size 0\n\t\tif size > 0 {\n\t\t\topCodes = append(opCodes, OpCode{'e', ai, i, bj, j})\n\t\t}\n\t}\n\tm.opCodes = opCodes\n\treturn m.opCodes\n}\n\n// GetGroupedOpCodes isolates change clusters by eliminating ranges with no changes.\n//\n// Returns a generator of groups with up to n lines of context.\n// Each group is in the same format as returned by GetOpCodes().\nfunc (m *SequenceMatcher) GetGroupedOpCodes(n int) [][]OpCode {\n\tif n < 0 {\n\t\tn = 3\n\t}\n\tcodes := m.GetOpCodes()\n\tif len(codes) == 0 {\n\t\tcodes = []OpCode{{'e', 0, 1, 0, 1}}\n\t}\n\t// Fixup leading and trailing groups if they show no changes.\n\tif codes[0].Tag == 'e' {\n\t\tc := codes[0]\n\t\ti1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2\n\t\tcodes[0] = OpCode{c.Tag, max(i1, i2-n), i2, max(j1, j2-n), j2}\n\t}\n\tif codes[len(codes)-1].Tag == 'e' {\n\t\tc := codes[len(codes)-1]\n\t\ti1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2\n\t\tcodes[len(codes)-1] = OpCode{c.Tag, i1, min(i2, i1+n), j1, min(j2, j1+n)}\n\t}\n\tnn := n + n\n\tgroups := [][]OpCode{}\n\tgroup := []OpCode{}\n\tfor _, c := range codes {\n\t\ti1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2\n\t\t// End the current group and start a new one whenever\n\t\t// there is a large range with no changes.\n\t\tif c.Tag == 'e' && i2-i1 > nn {\n\t\t\tgroup = append(group, OpCode{\n\t\t\t\tc.Tag, i1, min(i2, i1+n),\n\t\t\t\tj1, min(j2, j1+n),\n\t\t\t})\n\t\t\tgroups = append(groups, group)\n\t\t\tgroup = []OpCode{}\n\t\t\ti1, j1 = max(i1, i2-n), max(j1, j2-n)\n\t\t}\n\t\tgroup = append(group, OpCode{c.Tag, i1, i2, j1, j2})\n\t}\n\tif len(group) > 0 && !(len(group) == 1 && group[0].Tag == 'e') {\n\t\tgroups = append(groups, group)\n\t}\n\treturn groups\n}\n\n// Ratio returns a measure of the sequences' similarity (float in [0,1]).\n//\n// Where T is the total number of elements in both sequences, and\n// M is the number of matches, this is 2.0*M / T.\n// Note that this is 1 if the sequences are identical, and 0 if\n// they have nothing in common.\n//\n// .Ratio() is expensive to compute if you haven't already computed\n// .GetMatchingBlocks() or .GetOpCodes(), in which case you may\n// want to try .QuickRatio() or .RealQuickRation() first to get an\n// upper bound.\nfunc (m *SequenceMatcher) Ratio() float64 {\n\tmatches := 0\n\tfor _, m := range m.GetMatchingBlocks() {\n\t\tmatches += m.Size\n\t}\n\treturn calculateRatio(matches, len(m.a)+len(m.b))\n}\n\n// QuickRatio returns an upper bound on ratio() relatively quickly.\n//\n// This isn't defined beyond that it is an upper bound on .Ratio(), and\n// is faster to compute.\nfunc (m *SequenceMatcher) QuickRatio() float64 {\n\t// viewing a and b as multisets, set matches to the cardinality\n\t// of their intersection; this counts the number of matches\n\t// without regard to order, so is clearly an upper bound\n\tif m.fullBCount == nil {\n\t\tm.fullBCount = map[string]int{}\n\t\tfor _, s := range m.b {\n\t\t\tm.fullBCount[s] = m.fullBCount[s] + 1\n\t\t}\n\t}\n\n\t// avail[x] is the number of times x appears in 'b' less the\n\t// number of times we've seen it in 'a' so far ... kinda\n\tavail := map[string]int{}\n\tmatches := 0\n\tfor _, s := range m.a {\n\t\tn, ok := avail[s]\n\t\tif !ok {\n\t\t\tn = m.fullBCount[s]\n\t\t}\n\t\tavail[s] = n - 1\n\t\tif n > 0 {\n\t\t\tmatches++\n\t\t}\n\t}\n\treturn calculateRatio(matches, len(m.a)+len(m.b))\n}\n\n// RealQuickRatio returns an upper bound on ratio() very quickly.\n//\n// This isn't defined beyond that it is an upper bound on .Ratio(), and\n// is faster to compute than either .Ratio() or .QuickRatio().\nfunc (m *SequenceMatcher) RealQuickRatio() float64 {\n\tla, lb := len(m.a), len(m.b)\n\treturn calculateRatio(min(la, lb), la+lb)\n}\n\n// Convert range to the \"ed\" format\nfunc formatRangeUnified(start, stop int) string {\n\t// Per the diff spec at http://www.unix.org/single_unix_specification/\n\tbeginning := start + 1 // lines start numbering with one\n\tlength := stop - start\n\tif length == 1 {\n\t\treturn fmt.Sprintf(\"%d\", beginning)\n\t}\n\tif length == 0 {\n\t\tbeginning-- // empty ranges begin at line just before the range\n\t}\n\treturn fmt.Sprintf(\"%d,%d\", beginning, length)\n}\n\n// UnifiedDiff represents the unified diff parameters.\ntype UnifiedDiff struct {\n\tA        []string // First sequence lines\n\tFromFile string   // First file name\n\tFromDate string   // First file time\n\tB        []string // Second sequence lines\n\tToFile   string   // Second file name\n\tToDate   string   // Second file time\n\tEol      string   // Headers end of line, defaults to LF\n\tContext  int      // Number of context lines\n}\n\n// WriteUnifiedDiff compares two sequences of lines; generates the delta as\n// a unified diff.\n//\n// Unified diffs are a compact way of showing line changes and a few\n// lines of context.  The number of context lines is set by 'n' which\n// defaults to three.\n//\n// By default, the diff control lines (those with ---, +++, or @@) are\n// created with a trailing newline.  This is helpful so that inputs\n// created from file.readlines() result in diffs that are suitable for\n// file.writelines() since both the inputs and outputs have trailing\n// newlines.\n//\n// For inputs that do not have trailing newlines, set the lineterm\n// argument to \"\" so that the output will be uniformly newline free.\n//\n// The unidiff format normally has a header for filenames and modification\n// times.  Any or all of these may be specified using strings for\n// 'fromfile', 'tofile', 'fromfiledate', and 'tofiledate'.\n// The modification times are normally expressed in the ISO 8601 format.\nfunc WriteUnifiedDiff(writer io.Writer, diff UnifiedDiff) error {\n\tbuf := bufio.NewWriter(writer)\n\tdefer buf.Flush()\n\twf := func(format string, args ...interface{}) error {\n\t\t_, err := buf.WriteString(fmt.Sprintf(format, args...))\n\t\treturn err\n\t}\n\tws := func(s string) error {\n\t\t_, err := buf.WriteString(s)\n\t\treturn err\n\t}\n\n\tif len(diff.Eol) == 0 {\n\t\tdiff.Eol = \"\\n\"\n\t}\n\n\tstarted := false\n\tm := NewMatcher(diff.A, diff.B)\n\tfor _, g := range m.GetGroupedOpCodes(diff.Context) {\n\t\tif !started {\n\t\t\tstarted = true\n\t\t\tfromDate := \"\"\n\t\t\tif len(diff.FromDate) > 0 {\n\t\t\t\tfromDate = \"\\t\" + diff.FromDate\n\t\t\t}\n\t\t\ttoDate := \"\"\n\t\t\tif len(diff.ToDate) > 0 {\n\t\t\t\ttoDate = \"\\t\" + diff.ToDate\n\t\t\t}\n\t\t\tif diff.FromFile != \"\" || diff.ToFile != \"\" {\n\t\t\t\terr := wf(\"--- %s%s%s\", diff.FromFile, fromDate, diff.Eol)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\terr = wf(\"+++ %s%s%s\", diff.ToFile, toDate, diff.Eol)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tfirst, last := g[0], g[len(g)-1]\n\t\trange1 := formatRangeUnified(first.I1, last.I2)\n\t\trange2 := formatRangeUnified(first.J1, last.J2)\n\t\tif err := wf(\"@@ -%s +%s @@%s\", range1, range2, diff.Eol); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tfor _, c := range g {\n\t\t\ti1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2\n\t\t\tif c.Tag == 'e' {\n\t\t\t\tfor _, line := range diff.A[i1:i2] {\n\t\t\t\t\tif err := ws(\" \" + line); err != nil {\n\t\t\t\t\t\treturn err\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif c.Tag == 'r' || c.Tag == 'd' {\n\t\t\t\tfor _, line := range diff.A[i1:i2] {\n\t\t\t\t\tif err := ws(\"-\" + line); err != nil {\n\t\t\t\t\t\treturn err\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif c.Tag == 'r' || c.Tag == 'i' {\n\t\t\t\tfor _, line := range diff.B[j1:j2] {\n\t\t\t\t\tif err := ws(\"+\" + line); err != nil {\n\t\t\t\t\t\treturn err\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn nil\n}\n\n// GetUnifiedDiffString is like WriteUnifiedDiff but returns the diff as a string.\nfunc GetUnifiedDiffString(diff UnifiedDiff) (string, error) {\n\tw := &bytes.Buffer{}\n\terr := WriteUnifiedDiff(w, diff)\n\treturn w.String(), err\n}\n\n// Convert range to the \"ed\" format.\nfunc formatRangeContext(start, stop int) string {\n\t// Per the diff spec at http://www.unix.org/single_unix_specification/\n\tbeginning := start + 1 // lines start numbering with one\n\tlength := stop - start\n\tif length == 0 {\n\t\tbeginning-- // empty ranges begin at line just before the range\n\t}\n\tif length <= 1 {\n\t\treturn fmt.Sprintf(\"%d\", beginning)\n\t}\n\treturn fmt.Sprintf(\"%d,%d\", beginning, beginning+length-1)\n}\n\ntype ContextDiff UnifiedDiff\n\n// WriteContextDiff compares two sequences of lines; generates the delta as a context diff.\n//\n// Context diffs are a compact way of showing line changes and a few\n// lines of context. The number of context lines is set by diff.Context\n// which defaults to three.\n//\n// By default, the diff control lines (those with *** or ---) are\n// created with a trailing newline.\n//\n// For inputs that do not have trailing newlines, set the diff.Eol\n// argument to \"\" so that the output will be uniformly newline free.\n//\n// The context diff format normally has a header for filenames and\n// modification times.  Any or all of these may be specified using\n// strings for diff.FromFile, diff.ToFile, diff.FromDate, diff.ToDate.\n// The modification times are normally expressed in the ISO 8601 format.\n// If not specified, the strings default to blanks.\nfunc WriteContextDiff(writer io.Writer, diff ContextDiff) error {\n\tbuf := bufio.NewWriter(writer)\n\tdefer buf.Flush()\n\tvar diffErr error\n\twf := func(format string, args ...interface{}) {\n\t\t_, err := buf.WriteString(fmt.Sprintf(format, args...))\n\t\tif diffErr == nil && err != nil {\n\t\t\tdiffErr = err\n\t\t}\n\t}\n\tws := func(s string) {\n\t\t_, err := buf.WriteString(s)\n\t\tif diffErr == nil && err != nil {\n\t\t\tdiffErr = err\n\t\t}\n\t}\n\n\tif len(diff.Eol) == 0 {\n\t\tdiff.Eol = \"\\n\"\n\t}\n\n\tprefix := map[byte]string{\n\t\t'i': \"+ \",\n\t\t'd': \"- \",\n\t\t'r': \"! \",\n\t\t'e': \"  \",\n\t}\n\n\tstarted := false\n\tm := NewMatcher(diff.A, diff.B)\n\tfor _, g := range m.GetGroupedOpCodes(diff.Context) {\n\t\tif !started {\n\t\t\tstarted = true\n\t\t\tfromDate := \"\"\n\t\t\tif len(diff.FromDate) > 0 {\n\t\t\t\tfromDate = \"\\t\" + diff.FromDate\n\t\t\t}\n\t\t\ttoDate := \"\"\n\t\t\tif len(diff.ToDate) > 0 {\n\t\t\t\ttoDate = \"\\t\" + diff.ToDate\n\t\t\t}\n\t\t\tif diff.FromFile != \"\" || diff.ToFile != \"\" {\n\t\t\t\twf(\"*** %s%s%s\", diff.FromFile, fromDate, diff.Eol)\n\t\t\t\twf(\"--- %s%s%s\", diff.ToFile, toDate, diff.Eol)\n\t\t\t}\n\t\t}\n\n\t\tfirst, last := g[0], g[len(g)-1]\n\t\tws(\"***************\" + diff.Eol)\n\n\t\trange1 := formatRangeContext(first.I1, last.I2)\n\t\twf(\"*** %s ****%s\", range1, diff.Eol)\n\t\tfor _, c := range g {\n\t\t\tif c.Tag == 'r' || c.Tag == 'd' {\n\t\t\t\tfor _, cc := range g {\n\t\t\t\t\tif cc.Tag == 'i' {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tfor _, line := range diff.A[cc.I1:cc.I2] {\n\t\t\t\t\t\tws(prefix[cc.Tag] + line)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\n\t\trange2 := formatRangeContext(first.J1, last.J2)\n\t\twf(\"--- %s ----%s\", range2, diff.Eol)\n\t\tfor _, c := range g {\n\t\t\tif c.Tag == 'r' || c.Tag == 'i' {\n\t\t\t\tfor _, cc := range g {\n\t\t\t\t\tif cc.Tag == 'd' {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tfor _, line := range diff.B[cc.J1:cc.J2] {\n\t\t\t\t\t\tws(prefix[cc.Tag] + line)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t}\n\treturn diffErr\n}\n\n// GetContextDiffString is like WriteContextDiff but returns the diff as a string.\nfunc GetContextDiffString(diff ContextDiff) (string, error) {\n\tw := &bytes.Buffer{}\n\terr := WriteContextDiff(w, diff)\n\treturn w.String(), err\n}\n\n// SplitLines splits a string on \"\\n\" while preserving them. The output can be used\n// as input for UnifiedDiff and ContextDiff structures.\nfunc SplitLines(s string) []string {\n\tlines := strings.SplitAfter(s, \"\\n\")\n\tlines[len(lines)-1] += \"\\n\"\n\treturn lines\n}\n"
  },
  {
    "path": "internal/assert/difflib_test.go",
    "content": "// Copied from https://github.com/pmezard/go-difflib/blob/5d4384ee4fb2527b0a1256a821ebfc92f91efefc/difflib/difflib_test.go\n\n// Copyright 2013 Patrick Mezard. All rights reserved. Use of this source code is\n// governed by a license that can be found in the THIRD-PARTY-NOTICES file.\n\npackage assert\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"math\"\n\t\"reflect\"\n\t\"strings\"\n\t\"testing\"\n)\n\nfunc assertAlmostEqual(t *testing.T, a, b float64, places int) {\n\tif math.Abs(a-b) > math.Pow10(-places) {\n\t\tt.Errorf(\"%.7f != %.7f\", a, b)\n\t}\n}\n\nfunc assertEqual(t *testing.T, a, b interface{}) {\n\tif !reflect.DeepEqual(a, b) {\n\t\tt.Errorf(\"%v != %v\", a, b)\n\t}\n}\n\nfunc splitChars(s string) []string {\n\tchars := make([]string, 0, len(s))\n\t// Assume ASCII inputs\n\tfor i := 0; i != len(s); i++ {\n\t\tchars = append(chars, string(s[i]))\n\t}\n\treturn chars\n}\n\nfunc TestSequenceMatcherRatio(t *testing.T) {\n\ts := NewMatcher(splitChars(\"abcd\"), splitChars(\"bcde\"))\n\tassertEqual(t, s.Ratio(), 0.75)\n\tassertEqual(t, s.QuickRatio(), 0.75)\n\tassertEqual(t, s.RealQuickRatio(), 1.0)\n}\n\nfunc TestGetOptCodes(t *testing.T) {\n\ta := \"qabxcd\"\n\tb := \"abycdf\"\n\ts := NewMatcher(splitChars(a), splitChars(b))\n\tw := &bytes.Buffer{}\n\tfor _, op := range s.GetOpCodes() {\n\t\tfmt.Fprintf(w, \"%s a[%d:%d], (%s) b[%d:%d] (%s)\\n\", string(op.Tag),\n\t\t\top.I1, op.I2, a[op.I1:op.I2], op.J1, op.J2, b[op.J1:op.J2])\n\t}\n\tresult := w.String()\n\texpected := `d a[0:1], (q) b[0:0] ()\ne a[1:3], (ab) b[0:2] (ab)\nr a[3:4], (x) b[2:3] (y)\ne a[4:6], (cd) b[3:5] (cd)\ni a[6:6], () b[5:6] (f)\n`\n\tif expected != result {\n\t\tt.Errorf(\"unexpected op codes: \\n%s\", result)\n\t}\n}\n\nfunc TestGroupedOpCodes(t *testing.T) {\n\ta := []string{}\n\tfor i := 0; i != 39; i++ {\n\t\ta = append(a, fmt.Sprintf(\"%02d\", i))\n\t}\n\tb := []string{}\n\tb = append(b, a[:8]...)\n\tb = append(b, \" i\")\n\tb = append(b, a[8:19]...)\n\tb = append(b, \" x\")\n\tb = append(b, a[20:22]...)\n\tb = append(b, a[27:34]...)\n\tb = append(b, \" y\")\n\tb = append(b, a[35:]...)\n\ts := NewMatcher(a, b)\n\tw := &bytes.Buffer{}\n\tfor _, g := range s.GetGroupedOpCodes(-1) {\n\t\tfmt.Fprintf(w, \"group\\n\")\n\t\tfor _, op := range g {\n\t\t\tfmt.Fprintf(w, \"  %s, %d, %d, %d, %d\\n\", string(op.Tag),\n\t\t\t\top.I1, op.I2, op.J1, op.J2)\n\t\t}\n\t}\n\tresult := w.String()\n\texpected := `group\n  e, 5, 8, 5, 8\n  i, 8, 8, 8, 9\n  e, 8, 11, 9, 12\ngroup\n  e, 16, 19, 17, 20\n  r, 19, 20, 20, 21\n  e, 20, 22, 21, 23\n  d, 22, 27, 23, 23\n  e, 27, 30, 23, 26\ngroup\n  e, 31, 34, 27, 30\n  r, 34, 35, 30, 31\n  e, 35, 38, 31, 34\n`\n\tif expected != result {\n\t\tt.Errorf(\"unexpected op codes: \\n%s\", result)\n\t}\n}\n\nfunc rep(s string, count int) string {\n\treturn strings.Repeat(s, count)\n}\n\nfunc TestWithAsciiOneInsert(t *testing.T) {\n\tsm := NewMatcher(splitChars(rep(\"b\", 100)),\n\t\tsplitChars(\"a\"+rep(\"b\", 100)))\n\tassertAlmostEqual(t, sm.Ratio(), 0.995, 3)\n\tassertEqual(t, sm.GetOpCodes(),\n\t\t[]OpCode{{'i', 0, 0, 0, 1}, {'e', 0, 100, 1, 101}})\n\tassertEqual(t, len(sm.bPopular), 0)\n\n\tsm = NewMatcher(splitChars(rep(\"b\", 100)),\n\t\tsplitChars(rep(\"b\", 50)+\"a\"+rep(\"b\", 50)))\n\tassertAlmostEqual(t, sm.Ratio(), 0.995, 3)\n\tassertEqual(t, sm.GetOpCodes(),\n\t\t[]OpCode{{'e', 0, 50, 0, 50}, {'i', 50, 50, 50, 51}, {'e', 50, 100, 51, 101}})\n\tassertEqual(t, len(sm.bPopular), 0)\n}\n\nfunc TestWithAsciiOnDelete(t *testing.T) {\n\tsm := NewMatcher(splitChars(rep(\"a\", 40)+\"c\"+rep(\"b\", 40)),\n\t\tsplitChars(rep(\"a\", 40)+rep(\"b\", 40)))\n\tassertAlmostEqual(t, sm.Ratio(), 0.994, 3)\n\tassertEqual(t, sm.GetOpCodes(),\n\t\t[]OpCode{{'e', 0, 40, 0, 40}, {'d', 40, 41, 40, 40}, {'e', 41, 81, 40, 80}})\n}\n\nfunc TestWithAsciiBJunk(t *testing.T) {\n\tisJunk := func(s string) bool {\n\t\treturn s == \" \"\n\t}\n\tsm := NewMatcherWithJunk(splitChars(rep(\"a\", 40)+rep(\"b\", 40)),\n\t\tsplitChars(rep(\"a\", 44)+rep(\"b\", 40)), true, isJunk)\n\tassertEqual(t, sm.bJunk, map[string]struct{}{})\n\n\tsm = NewMatcherWithJunk(splitChars(rep(\"a\", 40)+rep(\"b\", 40)),\n\t\tsplitChars(rep(\"a\", 44)+rep(\"b\", 40)+rep(\" \", 20)), false, isJunk)\n\tassertEqual(t, sm.bJunk, map[string]struct{}{\" \": {}})\n\n\tisJunk = func(s string) bool {\n\t\treturn s == \" \" || s == \"b\"\n\t}\n\tsm = NewMatcherWithJunk(splitChars(rep(\"a\", 40)+rep(\"b\", 40)),\n\t\tsplitChars(rep(\"a\", 44)+rep(\"b\", 40)+rep(\" \", 20)), false, isJunk)\n\tassertEqual(t, sm.bJunk, map[string]struct{}{\" \": {}, \"b\": {}})\n}\n\nfunc TestSFBugsRatioForNullSeqn(t *testing.T) {\n\tsm := NewMatcher(nil, nil)\n\tassertEqual(t, sm.Ratio(), 1.0)\n\tassertEqual(t, sm.QuickRatio(), 1.0)\n\tassertEqual(t, sm.RealQuickRatio(), 1.0)\n}\n\nfunc TestSFBugsComparingEmptyLists(t *testing.T) {\n\tgroups := NewMatcher(nil, nil).GetGroupedOpCodes(-1)\n\tassertEqual(t, len(groups), 0)\n\tdiff := UnifiedDiff{\n\t\tFromFile: \"Original\",\n\t\tToFile:   \"Current\",\n\t\tContext:  3,\n\t}\n\tresult, err := GetUnifiedDiffString(diff)\n\tassertEqual(t, err, nil)\n\tassertEqual(t, result, \"\")\n}\n\nfunc TestOutputFormatRangeFormatUnified(t *testing.T) {\n\t// Per the diff spec at http://www.unix.org/single_unix_specification/\n\t//\n\t// Each <range> field shall be of the form:\n\t//   %1d\", <beginning line number>  if the range contains exactly one line,\n\t// and:\n\t//  \"%1d,%1d\", <beginning line number>, <number of lines> otherwise.\n\t// If a range is empty, its beginning line number shall be the number of\n\t// the line just before the range, or 0 if the empty range starts the file.\n\tfm := formatRangeUnified\n\tassertEqual(t, fm(3, 3), \"3,0\")\n\tassertEqual(t, fm(3, 4), \"4\")\n\tassertEqual(t, fm(3, 5), \"4,2\")\n\tassertEqual(t, fm(3, 6), \"4,3\")\n\tassertEqual(t, fm(0, 0), \"0,0\")\n}\n\nfunc TestOutputFormatRangeFormatContext(t *testing.T) {\n\t// Per the diff spec at http://www.unix.org/single_unix_specification/\n\t//\n\t// The range of lines in file1 shall be written in the following format\n\t// if the range contains two or more lines:\n\t//     \"*** %d,%d ****\\n\", <beginning line number>, <ending line number>\n\t// and the following format otherwise:\n\t//     \"*** %d ****\\n\", <ending line number>\n\t// The ending line number of an empty range shall be the number of the preceding line,\n\t// or 0 if the range is at the start of the file.\n\t//\n\t// Next, the range of lines in file2 shall be written in the following format\n\t// if the range contains two or more lines:\n\t//     \"--- %d,%d ----\\n\", <beginning line number>, <ending line number>\n\t// and the following format otherwise:\n\t//     \"--- %d ----\\n\", <ending line number>\n\tfm := formatRangeContext\n\tassertEqual(t, fm(3, 3), \"3\")\n\tassertEqual(t, fm(3, 4), \"4\")\n\tassertEqual(t, fm(3, 5), \"4,5\")\n\tassertEqual(t, fm(3, 6), \"4,6\")\n\tassertEqual(t, fm(0, 0), \"0\")\n}\n\nfunc TestOutputFormatTabDelimiter(t *testing.T) {\n\tdiff := UnifiedDiff{\n\t\tA:        splitChars(\"one\"),\n\t\tB:        splitChars(\"two\"),\n\t\tFromFile: \"Original\",\n\t\tFromDate: \"2005-01-26 23:30:50\",\n\t\tToFile:   \"Current\",\n\t\tToDate:   \"2010-04-12 10:20:52\",\n\t\tEol:      \"\\n\",\n\t}\n\tud, err := GetUnifiedDiffString(diff)\n\tassertEqual(t, err, nil)\n\tassertEqual(t, SplitLines(ud)[:2], []string{\n\t\t\"--- Original\\t2005-01-26 23:30:50\\n\",\n\t\t\"+++ Current\\t2010-04-12 10:20:52\\n\",\n\t})\n\tcd, err := GetContextDiffString(ContextDiff(diff))\n\tassertEqual(t, err, nil)\n\tassertEqual(t, SplitLines(cd)[:2], []string{\n\t\t\"*** Original\\t2005-01-26 23:30:50\\n\",\n\t\t\"--- Current\\t2010-04-12 10:20:52\\n\",\n\t})\n}\n\nfunc TestOutputFormatNoTrailingTabOnEmptyFiledate(t *testing.T) {\n\tdiff := UnifiedDiff{\n\t\tA:        splitChars(\"one\"),\n\t\tB:        splitChars(\"two\"),\n\t\tFromFile: \"Original\",\n\t\tToFile:   \"Current\",\n\t\tEol:      \"\\n\",\n\t}\n\tud, err := GetUnifiedDiffString(diff)\n\tassertEqual(t, err, nil)\n\tassertEqual(t, SplitLines(ud)[:2], []string{\"--- Original\\n\", \"+++ Current\\n\"})\n\n\tcd, err := GetContextDiffString(ContextDiff(diff))\n\tassertEqual(t, err, nil)\n\tassertEqual(t, SplitLines(cd)[:2], []string{\"*** Original\\n\", \"--- Current\\n\"})\n}\n\nfunc TestOmitFilenames(t *testing.T) {\n\tdiff := UnifiedDiff{\n\t\tA:   SplitLines(\"o\\nn\\ne\\n\"),\n\t\tB:   SplitLines(\"t\\nw\\no\\n\"),\n\t\tEol: \"\\n\",\n\t}\n\tud, err := GetUnifiedDiffString(diff)\n\tassertEqual(t, err, nil)\n\tassertEqual(t, SplitLines(ud), []string{\n\t\t\"@@ -0,0 +1,2 @@\\n\",\n\t\t\"+t\\n\",\n\t\t\"+w\\n\",\n\t\t\"@@ -2,2 +3,0 @@\\n\",\n\t\t\"-n\\n\",\n\t\t\"-e\\n\",\n\t\t\"\\n\",\n\t})\n\n\tcd, err := GetContextDiffString(ContextDiff(diff))\n\tassertEqual(t, err, nil)\n\tassertEqual(t, SplitLines(cd), []string{\n\t\t\"***************\\n\",\n\t\t\"*** 0 ****\\n\",\n\t\t\"--- 1,2 ----\\n\",\n\t\t\"+ t\\n\",\n\t\t\"+ w\\n\",\n\t\t\"***************\\n\",\n\t\t\"*** 2,3 ****\\n\",\n\t\t\"- n\\n\",\n\t\t\"- e\\n\",\n\t\t\"--- 3 ----\\n\",\n\t\t\"\\n\",\n\t})\n}\n\nfunc TestSplitLines(t *testing.T) {\n\tallTests := []struct {\n\t\tinput string\n\t\twant  []string\n\t}{\n\t\t{\"foo\", []string{\"foo\\n\"}},\n\t\t{\"foo\\nbar\", []string{\"foo\\n\", \"bar\\n\"}},\n\t\t{\"foo\\nbar\\n\", []string{\"foo\\n\", \"bar\\n\", \"\\n\"}},\n\t}\n\tfor _, test := range allTests {\n\t\tassertEqual(t, SplitLines(test.input), test.want)\n\t}\n}\n\nfunc benchmarkSplitLines(b *testing.B, count int) {\n\tstr := strings.Repeat(\"foo\\n\", count)\n\n\tb.ResetTimer()\n\n\tn := 0\n\tfor i := 0; i < b.N; i++ {\n\t\tn += len(SplitLines(str))\n\t}\n}\n\nfunc BenchmarkSplitLines100(b *testing.B) {\n\tbenchmarkSplitLines(b, 100)\n}\n\nfunc BenchmarkSplitLines10000(b *testing.B) {\n\tbenchmarkSplitLines(b, 10000)\n}\n"
  },
  {
    "path": "internal/aws/awserr/error.go",
    "content": "// Copyright (C) MongoDB, Inc. 2023-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n//\n// Based on github.com/aws/aws-sdk-go by Amazon.com, Inc. with code from:\n// - github.com/aws/aws-sdk-go/blob/v1.44.225/aws/awserr/error.go\n// See THIRD-PARTY-NOTICES for original license terms\n\n// Package awserr represents API error interface accessors for the SDK.\npackage awserr\n\n// An Error wraps lower level errors with code, message and an original error.\n// The underlying concrete error type may also satisfy other interfaces which\n// can be to used to obtain more specific information about the error.\ntype Error interface {\n\t// Satisfy the generic error interface.\n\terror\n\n\t// Returns the short phrase depicting the classification of the error.\n\tCode() string\n\n\t// Returns the error details message.\n\tMessage() string\n\n\t// Returns the original error if one was set.  Nil is returned if not set.\n\tOrigErr() error\n}\n\n// BatchedErrors is a batch of errors which also wraps lower level errors with\n// code, message, and original errors. Calling Error() will include all errors\n// that occurred in the batch.\n//\n// Replaces BatchError\ntype BatchedErrors interface {\n\t// Satisfy the base Error interface.\n\tError\n\n\t// Returns the original error if one was set.  Nil is returned if not set.\n\tOrigErrs() []error\n}\n\n// New returns an Error object described by the code, message, and origErr.\n//\n// If origErr satisfies the Error interface it will not be wrapped within a new\n// Error object and will instead be returned.\nfunc New(code, message string, origErr error) Error {\n\tvar errs []error\n\tif origErr != nil {\n\t\terrs = append(errs, origErr)\n\t}\n\treturn newBaseError(code, message, errs)\n}\n\n// NewBatchError returns an BatchedErrors with a collection of errors as an\n// array of errors.\nfunc NewBatchError(code, message string, errs []error) BatchedErrors {\n\treturn newBaseError(code, message, errs)\n}\n"
  },
  {
    "path": "internal/aws/awserr/types.go",
    "content": "// Copyright (C) MongoDB, Inc. 2023-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n//\n// Based on github.com/aws/aws-sdk-go by Amazon.com, Inc. with code from:\n// - github.com/aws/aws-sdk-go/blob/v1.44.225/aws/awserr/types.go\n// See THIRD-PARTY-NOTICES for original license terms\n\npackage awserr\n\nimport (\n\t\"fmt\"\n)\n\n// SprintError returns a string of the formatted error code.\n//\n// Both extra and origErr are optional.  If they are included their lines\n// will be added, but if they are not included their lines will be ignored.\nfunc SprintError(code, message, extra string, origErr error) string {\n\tmsg := fmt.Sprintf(\"%s: %s\", code, message)\n\tif extra != \"\" {\n\t\tmsg = fmt.Sprintf(\"%s\\n\\t%s\", msg, extra)\n\t}\n\tif origErr != nil {\n\t\tmsg = fmt.Sprintf(\"%s\\ncaused by: %s\", msg, origErr.Error())\n\t}\n\treturn msg\n}\n\n// A baseError wraps the code and message which defines an error. It also\n// can be used to wrap an original error object.\n//\n// Should be used as the root for errors satisfying the awserr.Error. Also\n// for any error which does not fit into a specific error wrapper type.\ntype baseError struct {\n\t// Classification of error\n\tcode string\n\n\t// Detailed information about error\n\tmessage string\n\n\t// Optional original error this error is based off of. Allows building\n\t// chained errors.\n\terrs []error\n}\n\n// newBaseError returns an error object for the code, message, and errors.\n//\n// code is a short no whitespace phrase depicting the classification of\n// the error that is being created.\n//\n// message is the free flow string containing detailed information about the\n// error.\n//\n// origErrs is the error objects which will be nested under the new errors to\n// be returned.\nfunc newBaseError(code, message string, origErrs []error) *baseError {\n\tb := &baseError{\n\t\tcode:    code,\n\t\tmessage: message,\n\t\terrs:    origErrs,\n\t}\n\n\treturn b\n}\n\n// Error returns the string representation of the error.\n//\n// See ErrorWithExtra for formatting.\n//\n// Satisfies the error interface.\nfunc (b baseError) Error() string {\n\tsize := len(b.errs)\n\tif size > 0 {\n\t\treturn SprintError(b.code, b.message, \"\", errorList(b.errs))\n\t}\n\n\treturn SprintError(b.code, b.message, \"\", nil)\n}\n\n// String returns the string representation of the error.\n// Alias for Error to satisfy the stringer interface.\nfunc (b baseError) String() string {\n\treturn b.Error()\n}\n\n// Code returns the short phrase depicting the classification of the error.\nfunc (b baseError) Code() string {\n\treturn b.code\n}\n\n// Message returns the error details message.\nfunc (b baseError) Message() string {\n\treturn b.message\n}\n\n// OrigErr returns the original error if one was set. Nil is returned if no\n// error was set. This only returns the first element in the list. If the full\n// list is needed, use BatchedErrors.\nfunc (b baseError) OrigErr() error {\n\tswitch len(b.errs) {\n\tcase 0:\n\t\treturn nil\n\tcase 1:\n\t\treturn b.errs[0]\n\tdefault:\n\t\tif err, ok := b.errs[0].(Error); ok {\n\t\t\treturn NewBatchError(err.Code(), err.Message(), b.errs[1:])\n\t\t}\n\t\treturn NewBatchError(\"BatchedErrors\",\n\t\t\t\"multiple errors occurred\", b.errs)\n\t}\n}\n\n// OrigErrs returns the original errors if one was set. An empty slice is\n// returned if no error was set.\nfunc (b baseError) OrigErrs() []error {\n\treturn b.errs\n}\n\n// An error list that satisfies the golang interface\ntype errorList []error\n\n// Error returns the string representation of the error.\n//\n// Satisfies the error interface.\nfunc (e errorList) Error() string {\n\tmsg := \"\"\n\t// How do we want to handle the array size being zero\n\tif size := len(e); size > 0 {\n\t\tfor i := 0; i < size; i++ {\n\t\t\tmsg += e[i].Error()\n\t\t\t// We check the next index to see if it is within the slice.\n\t\t\t// If it is, then we append a newline. We do this, because unit tests\n\t\t\t// could be broken with the additional '\\n'\n\t\t\tif i+1 < size {\n\t\t\t\tmsg += \"\\n\"\n\t\t\t}\n\t\t}\n\t}\n\treturn msg\n}\n"
  },
  {
    "path": "internal/aws/credentials/chain_provider.go",
    "content": "// Copyright (C) MongoDB, Inc. 2023-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n//\n// Based on github.com/aws/aws-sdk-go by Amazon.com, Inc. with code from:\n// - github.com/aws/aws-sdk-go/blob/v1.44.225/aws/credentials/chain_provider.go\n// See THIRD-PARTY-NOTICES for original license terms\n\npackage credentials\n\nimport (\n\t\"go.mongodb.org/mongo-driver/v2/internal/aws/awserr\"\n)\n\n// A ChainProvider will search for a provider which returns credentials\n// and cache that provider until Retrieve is called again.\n//\n// The ChainProvider provides a way of chaining multiple providers together\n// which will pick the first available using priority order of the Providers\n// in the list.\n//\n// If none of the Providers retrieve valid credentials Value, ChainProvider's\n// Retrieve() will return the error ErrNoValidProvidersFoundInChain.\n//\n// If a Provider is found which returns valid credentials Value ChainProvider\n// will cache that Provider for all calls to IsExpired(), until Retrieve is\n// called again.\ntype ChainProvider struct {\n\tProviders []Provider\n\tcurr      Provider\n}\n\n// NewChainCredentials returns a pointer to a new Credentials object\n// wrapping a chain of providers.\nfunc NewChainCredentials(providers []Provider) *Credentials {\n\treturn NewCredentials(&ChainProvider{\n\t\tProviders: append([]Provider{}, providers...),\n\t})\n}\n\n// Retrieve returns the credentials value or error if no provider returned\n// without error.\n//\n// If a provider is found it will be cached and any calls to IsExpired()\n// will return the expired state of the cached provider.\nfunc (c *ChainProvider) Retrieve() (Value, error) {\n\terrs := make([]error, 0, len(c.Providers))\n\tfor _, p := range c.Providers {\n\t\tcreds, err := p.Retrieve()\n\t\tif err == nil {\n\t\t\tc.curr = p\n\t\t\treturn creds, nil\n\t\t}\n\t\terrs = append(errs, err)\n\t}\n\tc.curr = nil\n\n\terr := awserr.NewBatchError(\"NoCredentialProviders\", \"no valid providers in chain\", errs)\n\treturn Value{}, err\n}\n\n// IsExpired will returned the expired state of the currently cached provider\n// if there is one.  If there is no current provider, true will be returned.\nfunc (c *ChainProvider) IsExpired() bool {\n\tif c.curr != nil {\n\t\treturn c.curr.IsExpired()\n\t}\n\n\treturn true\n}\n"
  },
  {
    "path": "internal/aws/credentials/chain_provider_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2023-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n//\n// Based on github.com/aws/aws-sdk-go by Amazon.com, Inc. with code from:\n// - github.com/aws/aws-sdk-go/blob/v1.44.225/aws/credentials/chain_provider_test.go\n// See THIRD-PARTY-NOTICES for original license terms\n\npackage credentials\n\nimport (\n\t\"reflect\"\n\t\"testing\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/aws/awserr\"\n)\n\ntype secondStubProvider struct {\n\tcreds   Value\n\texpired bool\n\terr     error\n}\n\nfunc (s *secondStubProvider) Retrieve() (Value, error) {\n\ts.expired = false\n\ts.creds.ProviderName = \"secondStubProvider\"\n\treturn s.creds, s.err\n}\n\nfunc (s *secondStubProvider) IsExpired() bool {\n\treturn s.expired\n}\n\nfunc TestChainProviderWithNames(t *testing.T) {\n\tp := &ChainProvider{\n\t\tProviders: []Provider{\n\t\t\t&stubProvider{err: awserr.New(\"FirstError\", \"first provider error\", nil)},\n\t\t\t&stubProvider{err: awserr.New(\"SecondError\", \"second provider error\", nil)},\n\t\t\t&secondStubProvider{\n\t\t\t\tcreds: Value{\n\t\t\t\t\tAccessKeyID:     \"AKIF\",\n\t\t\t\t\tSecretAccessKey: \"NOSECRET\",\n\t\t\t\t\tSessionToken:    \"\",\n\t\t\t\t},\n\t\t\t},\n\t\t\t&stubProvider{\n\t\t\t\tcreds: Value{\n\t\t\t\t\tAccessKeyID:     \"AKID\",\n\t\t\t\t\tSecretAccessKey: \"SECRET\",\n\t\t\t\t\tSessionToken:    \"\",\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n\n\tcreds, err := p.Retrieve()\n\tif err != nil {\n\t\tt.Errorf(\"Expect no error, got %v\", err)\n\t}\n\tif e, a := \"secondStubProvider\", creds.ProviderName; e != a {\n\t\tt.Errorf(\"Expect provider name to match, %v got, %v\", e, a)\n\t}\n\n\t// Also check credentials\n\tif e, a := \"AKIF\", creds.AccessKeyID; e != a {\n\t\tt.Errorf(\"Expect access key ID to match, %v got %v\", e, a)\n\t}\n\tif e, a := \"NOSECRET\", creds.SecretAccessKey; e != a {\n\t\tt.Errorf(\"Expect secret access key to match, %v got %v\", e, a)\n\t}\n\tif v := creds.SessionToken; len(v) != 0 {\n\t\tt.Errorf(\"Expect session token to be empty, %v\", v)\n\t}\n}\n\nfunc TestChainProviderGet(t *testing.T) {\n\tp := &ChainProvider{\n\t\tProviders: []Provider{\n\t\t\t&stubProvider{err: awserr.New(\"FirstError\", \"first provider error\", nil)},\n\t\t\t&stubProvider{err: awserr.New(\"SecondError\", \"second provider error\", nil)},\n\t\t\t&stubProvider{\n\t\t\t\tcreds: Value{\n\t\t\t\t\tAccessKeyID:     \"AKID\",\n\t\t\t\t\tSecretAccessKey: \"SECRET\",\n\t\t\t\t\tSessionToken:    \"\",\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n\n\tcreds, err := p.Retrieve()\n\tif err != nil {\n\t\tt.Errorf(\"Expect no error, got %v\", err)\n\t}\n\tif e, a := \"AKID\", creds.AccessKeyID; e != a {\n\t\tt.Errorf(\"Expect access key ID to match, %v got %v\", e, a)\n\t}\n\tif e, a := \"SECRET\", creds.SecretAccessKey; e != a {\n\t\tt.Errorf(\"Expect secret access key to match, %v got %v\", e, a)\n\t}\n\tif v := creds.SessionToken; len(v) != 0 {\n\t\tt.Errorf(\"Expect session token to be empty, %v\", v)\n\t}\n}\n\nfunc TestChainProviderIsExpired(t *testing.T) {\n\tstubProvider := &stubProvider{expired: true}\n\tp := &ChainProvider{\n\t\tProviders: []Provider{\n\t\t\tstubProvider,\n\t\t},\n\t}\n\n\tif !p.IsExpired() {\n\t\tt.Errorf(\"Expect expired to be true before any Retrieve\")\n\t}\n\t_, err := p.Retrieve()\n\tif err != nil {\n\t\tt.Errorf(\"Expect no error, got %v\", err)\n\t}\n\tif p.IsExpired() {\n\t\tt.Errorf(\"Expect not expired after retrieve\")\n\t}\n\n\tstubProvider.expired = true\n\tif !p.IsExpired() {\n\t\tt.Errorf(\"Expect return of expired provider\")\n\t}\n\n\t_, err = p.Retrieve()\n\tif err != nil {\n\t\tt.Errorf(\"Expect no error, got %v\", err)\n\t}\n\tif p.IsExpired() {\n\t\tt.Errorf(\"Expect not expired after retrieve\")\n\t}\n}\n\nfunc TestChainProviderWithNoProvider(t *testing.T) {\n\tp := &ChainProvider{\n\t\tProviders: []Provider{},\n\t}\n\n\tif !p.IsExpired() {\n\t\tt.Errorf(\"Expect expired with no providers\")\n\t}\n\t_, err := p.Retrieve()\n\tif err.Error() != \"NoCredentialProviders: no valid providers in chain\" {\n\t\tt.Errorf(\"Expect no providers error returned, got %v\", err)\n\t}\n}\n\nfunc TestChainProviderWithNoValidProvider(t *testing.T) {\n\terrs := []error{\n\t\tawserr.New(\"FirstError\", \"first provider error\", nil),\n\t\tawserr.New(\"SecondError\", \"second provider error\", nil),\n\t}\n\tp := &ChainProvider{\n\t\tProviders: []Provider{\n\t\t\t&stubProvider{err: errs[0]},\n\t\t\t&stubProvider{err: errs[1]},\n\t\t},\n\t}\n\n\tif !p.IsExpired() {\n\t\tt.Errorf(\"Expect expired with no providers\")\n\t}\n\t_, err := p.Retrieve()\n\n\texpectErr := awserr.NewBatchError(\"NoCredentialProviders\", \"no valid providers in chain\", errs)\n\tif e, a := expectErr, err; !reflect.DeepEqual(e, a) {\n\t\tt.Errorf(\"Expect no providers error returned, %v, got %v\", e, a)\n\t}\n}\n"
  },
  {
    "path": "internal/aws/credentials/credentials.go",
    "content": "// Copyright (C) MongoDB, Inc. 2023-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n//\n// Based on github.com/aws/aws-sdk-go by Amazon.com, Inc. with code from:\n// - github.com/aws/aws-sdk-go/blob/v1.44.225/aws/credentials/credentials.go\n// See THIRD-PARTY-NOTICES for original license terms\n\npackage credentials\n\nimport (\n\t\"context\"\n\t\"sync\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/aws/awserr\"\n\t\"golang.org/x/sync/singleflight\"\n)\n\n// A Value is the AWS credentials value for individual credential fields.\n//\n// A Value is also used to represent Azure credentials.\n// Azure credentials only consist of an access token, which is stored in the `SessionToken` field.\ntype Value struct {\n\t// AWS Access key ID\n\tAccessKeyID string\n\n\t// AWS Secret Access Key\n\tSecretAccessKey string\n\n\t// AWS Session Token\n\tSessionToken string\n\n\t// Provider used to get credentials\n\tProviderName string\n}\n\n// HasKeys returns if the credentials Value has both AccessKeyID and\n// SecretAccessKey value set.\nfunc (v Value) HasKeys() bool {\n\treturn len(v.AccessKeyID) != 0 && len(v.SecretAccessKey) != 0\n}\n\n// A Provider is the interface for any component which will provide credentials\n// Value. A provider is required to manage its own Expired state, and what to\n// be expired means.\n//\n// The Provider should not need to implement its own mutexes, because\n// that will be managed by Credentials.\ntype Provider interface {\n\t// Retrieve returns nil if it successfully retrieved the value.\n\t// Error is returned if the value were not obtainable, or empty.\n\tRetrieve() (Value, error)\n\n\t// IsExpired returns if the credentials are no longer valid, and need\n\t// to be retrieved.\n\tIsExpired() bool\n}\n\n// ProviderWithContext is a Provider that can retrieve credentials with a Context\ntype ProviderWithContext interface {\n\tProvider\n\n\tRetrieveWithContext(context.Context) (Value, error)\n}\n\n// A Credentials provides concurrency safe retrieval of AWS credentials Value.\n//\n// A Credentials is also used to fetch Azure credentials Value.\n//\n// Credentials will cache the credentials value until they expire. Once the value\n// expires the next Get will attempt to retrieve valid credentials.\n//\n// Credentials is safe to use across multiple goroutines and will manage the\n// synchronous state so the Providers do not need to implement their own\n// synchronization.\n//\n// The first Credentials.Get() will always call Provider.Retrieve() to get the\n// first instance of the credentials Value. All calls to Get() after that\n// will return the cached credentials Value until IsExpired() returns true.\ntype Credentials struct {\n\tsf singleflight.Group\n\n\tm        sync.RWMutex\n\tcreds    Value\n\tprovider Provider\n}\n\n// NewCredentials returns a pointer to a new Credentials with the provider set.\nfunc NewCredentials(provider Provider) *Credentials {\n\tc := &Credentials{\n\t\tprovider: provider,\n\t}\n\treturn c\n}\n\n// GetWithContext returns the credentials value, or error if the credentials\n// Value failed to be retrieved. Will return early if the passed in context is\n// canceled.\n//\n// Will return the cached credentials Value if it has not expired. If the\n// credentials Value has expired the Provider's Retrieve() will be called\n// to refresh the credentials.\n//\n// If Credentials.Expire() was called the credentials Value will be force\n// expired, and the next call to Get() will cause them to be refreshed.\nfunc (c *Credentials) GetWithContext(ctx context.Context) (Value, error) {\n\t// Check if credentials are cached, and not expired.\n\tselect {\n\tcase curCreds, ok := <-c.asyncIsExpired():\n\t\t// ok will only be true, of the credentials were not expired. ok will\n\t\t// be false and have no value if the credentials are expired.\n\t\tif ok {\n\t\t\treturn curCreds, nil\n\t\t}\n\tcase <-ctx.Done():\n\t\treturn Value{}, awserr.New(\"RequestCanceled\",\n\t\t\t\"request context canceled\", ctx.Err())\n\t}\n\n\t// Cannot pass context down to the actual retrieve, because the first\n\t// context would cancel the whole group when there is not direct\n\t// association of items in the group.\n\tresCh := c.sf.DoChan(\"\", func() (interface{}, error) {\n\t\treturn c.singleRetrieve(&suppressedContext{ctx})\n\t})\n\tselect {\n\tcase res := <-resCh:\n\t\treturn res.Val.(Value), res.Err\n\tcase <-ctx.Done():\n\t\treturn Value{}, awserr.New(\"RequestCanceled\",\n\t\t\t\"request context canceled\", ctx.Err())\n\t}\n}\n\nfunc (c *Credentials) singleRetrieve(ctx context.Context) (interface{}, error) {\n\tc.m.Lock()\n\tdefer c.m.Unlock()\n\n\tif curCreds := c.creds; !c.isExpiredLocked(curCreds) {\n\t\treturn curCreds, nil\n\t}\n\n\tvar creds Value\n\tvar err error\n\tif p, ok := c.provider.(ProviderWithContext); ok {\n\t\tcreds, err = p.RetrieveWithContext(ctx)\n\t} else {\n\t\tcreds, err = c.provider.Retrieve()\n\t}\n\tif err == nil {\n\t\tc.creds = creds\n\t}\n\n\treturn creds, err\n}\n\n// asyncIsExpired returns a channel of credentials Value. If the channel is\n// closed the credentials are expired and credentials value are not empty.\nfunc (c *Credentials) asyncIsExpired() <-chan Value {\n\tch := make(chan Value, 1)\n\tgo func() {\n\t\tc.m.RLock()\n\t\tdefer c.m.RUnlock()\n\n\t\tif curCreds := c.creds; !c.isExpiredLocked(curCreds) {\n\t\t\tch <- curCreds\n\t\t}\n\n\t\tclose(ch)\n\t}()\n\n\treturn ch\n}\n\n// isExpiredLocked helper method wrapping the definition of expired credentials.\nfunc (c *Credentials) isExpiredLocked(creds interface{}) bool {\n\treturn creds == nil || creds.(Value) == Value{} || c.provider.IsExpired()\n}\n\ntype suppressedContext struct {\n\tcontext.Context\n}\n\nfunc (s *suppressedContext) Deadline() (deadline time.Time, ok bool) {\n\treturn time.Time{}, false\n}\n\nfunc (s *suppressedContext) Done() <-chan struct{} {\n\treturn nil\n}\n\nfunc (s *suppressedContext) Err() error {\n\treturn nil\n}\n"
  },
  {
    "path": "internal/aws/credentials/credentials_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2023-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n//\n// Based on github.com/aws/aws-sdk-go by Amazon.com, Inc. with code from:\n// - github.com/aws/aws-sdk-go/blob/v1.44.225/aws/credentials/credentials_test.go\n// See THIRD-PARTY-NOTICES for original license terms\n\npackage credentials\n\nimport (\n\t\"context\"\n\t\"sync\"\n\t\"testing\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/aws/awserr\"\n)\n\nfunc isExpired(c *Credentials) bool {\n\tc.m.RLock()\n\tdefer c.m.RUnlock()\n\n\treturn c.isExpiredLocked(c.creds)\n}\n\ntype stubProvider struct {\n\tcreds          Value\n\tretrievedCount int\n\texpired        bool\n\terr            error\n}\n\nfunc (s *stubProvider) Retrieve() (Value, error) {\n\ts.retrievedCount++\n\ts.expired = false\n\ts.creds.ProviderName = \"stubProvider\"\n\treturn s.creds, s.err\n}\n\nfunc (s *stubProvider) IsExpired() bool {\n\treturn s.expired\n}\n\nfunc TestCredentialsGet(t *testing.T) {\n\tc := NewCredentials(&stubProvider{\n\t\tcreds: Value{\n\t\t\tAccessKeyID:     \"AKID\",\n\t\t\tSecretAccessKey: \"SECRET\",\n\t\t\tSessionToken:    \"\",\n\t\t},\n\t\texpired: true,\n\t})\n\n\tcreds, err := c.GetWithContext(context.Background())\n\tif err != nil {\n\t\tt.Errorf(\"Expected no error, got %v\", err)\n\t}\n\tif e, a := \"AKID\", creds.AccessKeyID; e != a {\n\t\tt.Errorf(\"Expect access key ID to match, %v got %v\", e, a)\n\t}\n\tif e, a := \"SECRET\", creds.SecretAccessKey; e != a {\n\t\tt.Errorf(\"Expect secret access key to match, %v got %v\", e, a)\n\t}\n\tif v := creds.SessionToken; len(v) != 0 {\n\t\tt.Errorf(\"Expect session token to be empty, %v\", v)\n\t}\n}\n\nfunc TestCredentialsGetWithError(t *testing.T) {\n\tc := NewCredentials(&stubProvider{err: awserr.New(\"provider error\", \"\", nil), expired: true})\n\n\t_, err := c.GetWithContext(context.Background())\n\tif e, a := \"provider error\", err.(awserr.Error).Code(); e != a {\n\t\tt.Errorf(\"Expected provider error, %v got %v\", e, a)\n\t}\n}\n\nfunc TestCredentialsExpire(t *testing.T) {\n\tstub := &stubProvider{}\n\tc := NewCredentials(stub)\n\n\tstub.expired = false\n\tif !isExpired(c) {\n\t\tt.Errorf(\"Expected to start out expired\")\n\t}\n\n\t_, err := c.GetWithContext(context.Background())\n\tif err != nil {\n\t\tt.Errorf(\"Expected no err, got %v\", err)\n\t}\n\tif isExpired(c) {\n\t\tt.Errorf(\"Expected not to be expired\")\n\t}\n\n\tstub.expired = true\n\tif !isExpired(c) {\n\t\tt.Errorf(\"Expected to be expired\")\n\t}\n}\n\nfunc TestCredentialsGetWithProviderName(t *testing.T) {\n\tstub := &stubProvider{}\n\n\tc := NewCredentials(stub)\n\n\tcreds, err := c.GetWithContext(context.Background())\n\tif err != nil {\n\t\tt.Errorf(\"Expected no error, got %v\", err)\n\t}\n\tif e, a := creds.ProviderName, \"stubProvider\"; e != a {\n\t\tt.Errorf(\"Expected provider name to match, %v got %v\", e, a)\n\t}\n}\n\ntype MockProvider struct {\n\t// The date/time when to expire on\n\texpiration time.Time\n\n\t// If set will be used by IsExpired to determine the current time.\n\t// Defaults to time.Now if CurrentTime is not set.  Available for testing\n\t// to be able to mock out the current time.\n\tCurrentTime func() time.Time\n}\n\n// IsExpired returns if the credentials are expired.\nfunc (e *MockProvider) IsExpired() bool {\n\tcurTime := e.CurrentTime\n\tif curTime == nil {\n\t\tcurTime = time.Now\n\t}\n\treturn e.expiration.Before(curTime())\n}\n\nfunc (*MockProvider) Retrieve() (Value, error) {\n\treturn Value{}, nil\n}\n\nfunc TestCredentialsIsExpired_Race(_ *testing.T) {\n\tcreds := NewChainCredentials([]Provider{&MockProvider{}})\n\n\tstarter := make(chan struct{})\n\tvar wg sync.WaitGroup\n\twg.Add(10)\n\tfor i := 0; i < 10; i++ {\n\t\tgo func() {\n\t\t\tdefer wg.Done()\n\t\t\t<-starter\n\t\t\tfor i := 0; i < 100; i++ {\n\t\t\t\tisExpired(creds)\n\t\t\t}\n\t\t}()\n\t}\n\tclose(starter)\n\n\twg.Wait()\n}\n\ntype stubProviderConcurrent struct {\n\tstubProvider\n\tdone chan struct{}\n}\n\nfunc (s *stubProviderConcurrent) Retrieve() (Value, error) {\n\t<-s.done\n\treturn s.stubProvider.Retrieve()\n}\n\nfunc TestCredentialsGetConcurrent(t *testing.T) {\n\tstub := &stubProviderConcurrent{\n\t\tdone: make(chan struct{}),\n\t}\n\n\tc := NewCredentials(stub)\n\tdone := make(chan struct{})\n\n\tfor i := 0; i < 2; i++ {\n\t\tgo func() {\n\t\t\t_, err := c.GetWithContext(context.Background())\n\t\t\tif err != nil {\n\t\t\t\tt.Errorf(\"Expected no err, got %v\", err)\n\t\t\t}\n\t\t\tdone <- struct{}{}\n\t\t}()\n\t}\n\n\t// Validates that a single call to Retrieve is shared between two calls to Get\n\tstub.done <- struct{}{}\n\t<-done\n\t<-done\n}\n"
  },
  {
    "path": "internal/aws/signer/v4/header_rules.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n//\n// Based on github.com/aws/aws-sdk-go by Amazon.com, Inc. with code from:\n// - github.com/aws/aws-sdk-go/blob/v1.44.225/aws/signer/v4/header_rules.go\n// See THIRD-PARTY-NOTICES for original license terms\n\npackage v4\n\n// validator houses a set of rule needed for validation of a\n// string value\ntype rules []rule\n\n// rule interface allows for more flexible rules and just simply\n// checks whether or not a value adheres to that rule\ntype rule interface {\n\tIsValid(value string) bool\n}\n\n// IsValid will iterate through all rules and see if any rules\n// apply to the value and supports nested rules\nfunc (r rules) IsValid(value string) bool {\n\tfor _, rule := range r {\n\t\tif rule.IsValid(value) {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\n// mapRule generic rule for maps\ntype mapRule map[string]struct{}\n\n// IsValid for the map rule satisfies whether it exists in the map\nfunc (m mapRule) IsValid(value string) bool {\n\t_, ok := m[value]\n\treturn ok\n}\n\n// excludeList is a generic rule for exclude listing\ntype excludeList struct {\n\trule\n}\n\n// IsValid for exclude list checks if the value is within the exclude list\nfunc (b excludeList) IsValid(value string) bool {\n\treturn !b.rule.IsValid(value)\n}\n"
  },
  {
    "path": "internal/aws/signer/v4/request.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n//\n// Based on github.com/aws/aws-sdk-go by Amazon.com, Inc. with code from:\n// - github.com/aws/aws-sdk-go/blob/v1.44.225/aws/request/request.go\n// See THIRD-PARTY-NOTICES for original license terms\n\npackage v4\n\nimport (\n\t\"net/http\"\n\t\"strings\"\n)\n\n// Returns host from request\nfunc getHost(r *http.Request) string {\n\tif r.Host != \"\" {\n\t\treturn r.Host\n\t}\n\n\tif r.URL == nil {\n\t\treturn \"\"\n\t}\n\n\treturn r.URL.Host\n}\n\n// Hostname returns u.Host, without any port number.\n//\n// If Host is an IPv6 literal with a port number, Hostname returns the\n// IPv6 literal without the square brackets. IPv6 literals may include\n// a zone identifier.\n//\n// Copied from the Go 1.8 standard library (net/url)\nfunc stripPort(hostport string) string {\n\tcolon := strings.IndexByte(hostport, ':')\n\tif colon == -1 {\n\t\treturn hostport\n\t}\n\tif i := strings.IndexByte(hostport, ']'); i != -1 {\n\t\treturn strings.TrimPrefix(hostport[:i], \"[\")\n\t}\n\treturn hostport[:colon]\n}\n\n// Port returns the port part of u.Host, without the leading colon.\n// If u.Host doesn't contain a port, Port returns an empty string.\n//\n// Copied from the Go 1.8 standard library (net/url)\nfunc portOnly(hostport string) string {\n\tcolon := strings.IndexByte(hostport, ':')\n\tif colon == -1 {\n\t\treturn \"\"\n\t}\n\tif i := strings.Index(hostport, \"]:\"); i != -1 {\n\t\treturn hostport[i+len(\"]:\"):]\n\t}\n\tif strings.Contains(hostport, \"]\") {\n\t\treturn \"\"\n\t}\n\treturn hostport[colon+len(\":\"):]\n}\n\n// Returns true if the specified URI is using the standard port\n// (i.e. port 80 for HTTP URIs or 443 for HTTPS URIs)\nfunc isDefaultPort(scheme, port string) bool {\n\tif port == \"\" {\n\t\treturn true\n\t}\n\n\tlowerCaseScheme := strings.ToLower(scheme)\n\tif (lowerCaseScheme == \"http\" && port == \"80\") || (lowerCaseScheme == \"https\" && port == \"443\") {\n\t\treturn true\n\t}\n\n\treturn false\n}\n"
  },
  {
    "path": "internal/aws/signer/v4/uri_path.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n//\n// Based on github.com/aws/aws-sdk-go by Amazon.com, Inc. with code from:\n// - github.com/aws/aws-sdk-go/blob/v1.44.225/aws/signer/v4/uri_path.go\n// - github.com/aws/aws-sdk-go/blob/v1.44.225/private/protocol/rest/build.go\n// See THIRD-PARTY-NOTICES for original license terms\n\npackage v4\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"net/url\"\n\t\"strings\"\n)\n\n// Whether the byte value can be sent without escaping in AWS URLs\nvar noEscape [256]bool\n\nfunc init() {\n\tfor i := 0; i < len(noEscape); i++ {\n\t\t// AWS expects every character except these to be escaped\n\t\tnoEscape[i] = (i >= 'A' && i <= 'Z') ||\n\t\t\t(i >= 'a' && i <= 'z') ||\n\t\t\t(i >= '0' && i <= '9') ||\n\t\t\ti == '-' ||\n\t\t\ti == '.' ||\n\t\t\ti == '_' ||\n\t\t\ti == '~'\n\t}\n}\n\nfunc getURIPath(u *url.URL) string {\n\tvar uri string\n\n\tif len(u.Opaque) > 0 {\n\t\turi = \"/\" + strings.Join(strings.Split(u.Opaque, \"/\")[3:], \"/\")\n\t} else {\n\t\turi = u.EscapedPath()\n\t}\n\n\tif len(uri) == 0 {\n\t\turi = \"/\"\n\t}\n\n\treturn uri\n}\n\n// EscapePath escapes part of a URL path in Amazon style\nfunc EscapePath(path string, encodeSep bool) string {\n\tvar buf bytes.Buffer\n\tfor i := 0; i < len(path); i++ {\n\t\tc := path[i]\n\t\tif noEscape[c] || (c == '/' && !encodeSep) {\n\t\t\tbuf.WriteByte(c)\n\t\t} else {\n\t\t\tfmt.Fprintf(&buf, \"%%%02X\", c)\n\t\t}\n\t}\n\treturn buf.String()\n}\n"
  },
  {
    "path": "internal/aws/signer/v4/v4.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n//\n// Based on github.com/aws/aws-sdk-go by Amazon.com, Inc. with code from:\n// - github.com/aws/aws-sdk-go/blob/v1.44.225/aws/signer/v4/v4.go\n// See THIRD-PARTY-NOTICES for original license terms\n\npackage v4\n\nimport (\n\t\"crypto/hmac\"\n\t\"crypto/sha256\"\n\t\"encoding/hex\"\n\t\"fmt\"\n\t\"io\"\n\t\"io/ioutil\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"sort\"\n\t\"strings\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/aws\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/aws/credentials\"\n)\n\nconst (\n\tauthorizationHeader     = \"Authorization\"\n\tauthHeaderSignatureElem = \"Signature=\"\n\n\tauthHeaderPrefix = \"AWS4-HMAC-SHA256\"\n\ttimeFormat       = \"20060102T150405Z\"\n\tshortTimeFormat  = \"20060102\"\n\tawsV4Request     = \"aws4_request\"\n\n\t// emptyStringSHA256 is a SHA256 of an empty string\n\temptyStringSHA256 = `e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855`\n)\n\nvar ignoredHeaders = rules{\n\texcludeList{\n\t\tmapRule{\n\t\t\tauthorizationHeader: struct{}{},\n\t\t\t\"User-Agent\":        struct{}{},\n\t\t\t\"X-Amzn-Trace-Id\":   struct{}{},\n\t\t},\n\t},\n}\n\n// Signer applies AWS v4 signing to given request. Use this to sign requests\n// that need to be signed with AWS V4 Signatures.\ntype Signer struct {\n\t// The authentication credentials the request will be signed against.\n\t// This value must be set to sign requests.\n\tCredentials *credentials.Credentials\n}\n\n// NewSigner returns a Signer pointer configured with the credentials provided.\nfunc NewSigner(credentials *credentials.Credentials) *Signer {\n\tv4 := &Signer{\n\t\tCredentials: credentials,\n\t}\n\n\treturn v4\n}\n\ntype signingCtx struct {\n\tServiceName      string\n\tRegion           string\n\tRequest          *http.Request\n\tBody             io.ReadSeeker\n\tQuery            url.Values\n\tTime             time.Time\n\tSignedHeaderVals http.Header\n\n\tcredValues credentials.Value\n\n\tbodyDigest       string\n\tsignedHeaders    string\n\tcanonicalHeaders string\n\tcanonicalString  string\n\tcredentialString string\n\tstringToSign     string\n\tsignature        string\n}\n\n// Sign signs AWS v4 requests with the provided body, service name, region the\n// request is made to, and time the request is signed at. The signTime allows\n// you to specify that a request is signed for the future, and cannot be\n// used until then.\n//\n// Returns a list of HTTP headers that were included in the signature or an\n// error if signing the request failed. Generally for signed requests this value\n// is not needed as the full request context will be captured by the http.Request\n// value. It is included for reference though.\n//\n// Sign will set the request's Body to be the `body` parameter passed in. If\n// the body is not already an io.ReadCloser, it will be wrapped within one. If\n// a `nil` body parameter passed to Sign, the request's Body field will be\n// also set to nil. Its important to note that this functionality will not\n// change the request's ContentLength of the request.\n//\n// Sign differs from Presign in that it will sign the request using HTTP\n// header values. This type of signing is intended for http.Request values that\n// will not be shared, or are shared in a way the header values on the request\n// will not be lost.\n//\n// The requests body is an io.ReadSeeker so the SHA256 of the body can be\n// generated. To bypass the signer computing the hash you can set the\n// \"X-Amz-Content-Sha256\" header with a precomputed value. The signer will\n// only compute the hash if the request header value is empty.\nfunc (v4 Signer) Sign(r *http.Request, body io.ReadSeeker, service, region string, signTime time.Time) (http.Header, error) {\n\treturn v4.signWithBody(r, body, service, region, signTime)\n}\n\nfunc (v4 Signer) signWithBody(r *http.Request, body io.ReadSeeker, service, region string, signTime time.Time) (http.Header, error) {\n\tctx := &signingCtx{\n\t\tRequest:     r,\n\t\tBody:        body,\n\t\tQuery:       r.URL.Query(),\n\t\tTime:        signTime,\n\t\tServiceName: service,\n\t\tRegion:      region,\n\t}\n\n\tfor key := range ctx.Query {\n\t\tsort.Strings(ctx.Query[key])\n\t}\n\n\tif ctx.isRequestSigned() {\n\t\tctx.Time = time.Now()\n\t}\n\n\tvar err error\n\tctx.credValues, err = v4.Credentials.GetWithContext(r.Context())\n\tif err != nil {\n\t\treturn http.Header{}, err\n\t}\n\n\tctx.sanitizeHostForHeader()\n\tctx.assignAmzQueryValues()\n\tif err := ctx.build(); err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar reader io.ReadCloser\n\tif body != nil {\n\t\tvar ok bool\n\t\tif reader, ok = body.(io.ReadCloser); !ok {\n\t\t\treader = ioutil.NopCloser(body)\n\t\t}\n\t}\n\tr.Body = reader\n\n\treturn ctx.SignedHeaderVals, nil\n}\n\n// sanitizeHostForHeader removes default port from host and updates request.Host\nfunc (ctx *signingCtx) sanitizeHostForHeader() {\n\tr := ctx.Request\n\thost := getHost(r)\n\tport := portOnly(host)\n\tif port != \"\" && isDefaultPort(r.URL.Scheme, port) {\n\t\tr.Host = stripPort(host)\n\t}\n}\n\nfunc (ctx *signingCtx) assignAmzQueryValues() {\n\tif ctx.credValues.SessionToken != \"\" {\n\t\tctx.Request.Header.Set(\"X-Amz-Security-Token\", ctx.credValues.SessionToken)\n\t}\n}\n\nfunc (ctx *signingCtx) build() error {\n\tctx.buildTime()             // no depends\n\tctx.buildCredentialString() // no depends\n\n\tif err := ctx.buildBodyDigest(); err != nil {\n\t\treturn err\n\t}\n\n\tunsignedHeaders := ctx.Request.Header\n\n\tctx.buildCanonicalHeaders(ignoredHeaders, unsignedHeaders)\n\tctx.buildCanonicalString() // depends on canon headers / signed headers\n\tctx.buildStringToSign()    // depends on canon string\n\tctx.buildSignature()       // depends on string to sign\n\n\tparts := []string{\n\t\tauthHeaderPrefix + \" Credential=\" + ctx.credValues.AccessKeyID + \"/\" + ctx.credentialString,\n\t\t\"SignedHeaders=\" + ctx.signedHeaders,\n\t\tauthHeaderSignatureElem + ctx.signature,\n\t}\n\tctx.Request.Header.Set(authorizationHeader, strings.Join(parts, \", \"))\n\n\treturn nil\n}\n\nfunc (ctx *signingCtx) buildTime() {\n\tctx.Request.Header.Set(\"X-Amz-Date\", formatTime(ctx.Time))\n}\n\nfunc (ctx *signingCtx) buildCredentialString() {\n\tctx.credentialString = buildSigningScope(ctx.Region, ctx.ServiceName, ctx.Time)\n}\n\nfunc (ctx *signingCtx) buildCanonicalHeaders(r rule, header http.Header) {\n\theaders := make([]string, 0, len(header)+1)\n\theaders = append(headers, \"host\")\n\tfor k, v := range header {\n\t\tif !r.IsValid(k) {\n\t\t\tcontinue // ignored header\n\t\t}\n\t\tif ctx.SignedHeaderVals == nil {\n\t\t\tctx.SignedHeaderVals = make(http.Header)\n\t\t}\n\n\t\tlowerCaseKey := strings.ToLower(k)\n\t\tif _, ok := ctx.SignedHeaderVals[lowerCaseKey]; ok {\n\t\t\t// include additional values\n\t\t\tctx.SignedHeaderVals[lowerCaseKey] = append(ctx.SignedHeaderVals[lowerCaseKey], v...)\n\t\t\tcontinue\n\t\t}\n\n\t\theaders = append(headers, lowerCaseKey)\n\t\tctx.SignedHeaderVals[lowerCaseKey] = v\n\t}\n\tsort.Strings(headers)\n\n\tctx.signedHeaders = strings.Join(headers, \";\")\n\n\theaderItems := make([]string, len(headers))\n\tfor i, k := range headers {\n\t\tif k == \"host\" {\n\t\t\tif ctx.Request.Host != \"\" {\n\t\t\t\theaderItems[i] = \"host:\" + ctx.Request.Host\n\t\t\t} else {\n\t\t\t\theaderItems[i] = \"host:\" + ctx.Request.URL.Host\n\t\t\t}\n\t\t} else {\n\t\t\theaderValues := make([]string, len(ctx.SignedHeaderVals[k]))\n\t\t\tfor i, v := range ctx.SignedHeaderVals[k] {\n\t\t\t\theaderValues[i] = strings.TrimSpace(v)\n\t\t\t}\n\t\t\theaderItems[i] = k + \":\" +\n\t\t\t\tstrings.Join(headerValues, \",\")\n\t\t}\n\t}\n\tstripExcessSpaces(headerItems)\n\tctx.canonicalHeaders = strings.Join(headerItems, \"\\n\")\n}\n\nfunc (ctx *signingCtx) buildCanonicalString() {\n\tctx.Request.URL.RawQuery = strings.Replace(ctx.Query.Encode(), \"+\", \"%20\", -1)\n\n\turi := getURIPath(ctx.Request.URL)\n\n\turi = EscapePath(uri, false)\n\n\tctx.canonicalString = strings.Join([]string{\n\t\tctx.Request.Method,\n\t\turi,\n\t\tctx.Request.URL.RawQuery,\n\t\tctx.canonicalHeaders + \"\\n\",\n\t\tctx.signedHeaders,\n\t\tctx.bodyDigest,\n\t}, \"\\n\")\n}\n\nfunc (ctx *signingCtx) buildStringToSign() {\n\tctx.stringToSign = strings.Join([]string{\n\t\tauthHeaderPrefix,\n\t\tformatTime(ctx.Time),\n\t\tctx.credentialString,\n\t\thex.EncodeToString(hashSHA256([]byte(ctx.canonicalString))),\n\t}, \"\\n\")\n}\n\nfunc (ctx *signingCtx) buildSignature() {\n\tcreds := deriveSigningKey(ctx.Region, ctx.ServiceName, ctx.credValues.SecretAccessKey, ctx.Time)\n\tsignature := hmacSHA256(creds, []byte(ctx.stringToSign))\n\tctx.signature = hex.EncodeToString(signature)\n}\n\nfunc (ctx *signingCtx) buildBodyDigest() error {\n\thash := ctx.Request.Header.Get(\"X-Amz-Content-Sha256\")\n\tif hash == \"\" {\n\t\tif ctx.Body == nil {\n\t\t\thash = emptyStringSHA256\n\t\t} else {\n\t\t\tif !aws.IsReaderSeekable(ctx.Body) {\n\t\t\t\treturn fmt.Errorf(\"cannot use unseekable request body %T, for signed request with body\", ctx.Body)\n\t\t\t}\n\t\t\thashBytes, err := makeSha256Reader(ctx.Body)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\thash = hex.EncodeToString(hashBytes)\n\t\t}\n\t}\n\tctx.bodyDigest = hash\n\n\treturn nil\n}\n\n// isRequestSigned returns if the request is currently signed or presigned\nfunc (ctx *signingCtx) isRequestSigned() bool {\n\treturn ctx.Request.Header.Get(\"Authorization\") != \"\"\n}\n\nfunc hmacSHA256(key []byte, data []byte) []byte {\n\thash := hmac.New(sha256.New, key)\n\thash.Write(data)\n\treturn hash.Sum(nil)\n}\n\nfunc hashSHA256(data []byte) []byte {\n\thash := sha256.New()\n\thash.Write(data)\n\treturn hash.Sum(nil)\n}\n\nfunc makeSha256Reader(reader io.ReadSeeker) (hashBytes []byte, err error) {\n\thash := sha256.New()\n\tstart, err := reader.Seek(0, io.SeekCurrent)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer func() {\n\t\t// ensure error is return if unable to seek back to start of payload.\n\t\t_, err = reader.Seek(start, io.SeekStart)\n\t}()\n\n\t// Use CopyN to avoid allocating the 32KB buffer in io.Copy for bodies\n\t// smaller than 32KB. Fall back to io.Copy if we fail to determine the size.\n\tsize, err := aws.SeekerLen(reader)\n\tif err != nil {\n\t\t_, _ = io.Copy(hash, reader)\n\t} else {\n\t\t_, _ = io.CopyN(hash, reader, size)\n\t}\n\n\treturn hash.Sum(nil), nil\n}\n\nconst doubleSpace = \"  \"\n\n// stripExcessSpaces will rewrite the passed in slice's string values to not\n// contain multiple side-by-side spaces.\nfunc stripExcessSpaces(vals []string) {\n\tvar j, k, l, m, spaces int\n\tfor i, str := range vals {\n\t\t// revive:disable:empty-block\n\n\t\t// Trim trailing spaces\n\t\tfor j = len(str) - 1; j >= 0 && str[j] == ' '; j-- {\n\t\t}\n\n\t\t// Trim leading spaces\n\t\tfor k = 0; k < j && str[k] == ' '; k++ {\n\t\t}\n\n\t\t// revive:enable:empty-block\n\n\t\tstr = str[k : j+1]\n\n\t\t// Strip multiple spaces.\n\t\tj = strings.Index(str, doubleSpace)\n\t\tif j < 0 {\n\t\t\tvals[i] = str\n\t\t\tcontinue\n\t\t}\n\n\t\tbuf := []byte(str)\n\t\tfor k, m, l = j, j, len(buf); k < l; k++ {\n\t\t\tif buf[k] == ' ' {\n\t\t\t\tif spaces == 0 {\n\t\t\t\t\t// First space.\n\t\t\t\t\tbuf[m] = buf[k]\n\t\t\t\t\tm++\n\t\t\t\t}\n\t\t\t\tspaces++\n\t\t\t} else {\n\t\t\t\t// End of multiple spaces.\n\t\t\t\tspaces = 0\n\t\t\t\tbuf[m] = buf[k]\n\t\t\t\tm++\n\t\t\t}\n\t\t}\n\n\t\tvals[i] = string(buf[:m])\n\t}\n}\n\nfunc buildSigningScope(region, service string, dt time.Time) string {\n\treturn strings.Join([]string{\n\t\tformatShortTime(dt),\n\t\tregion,\n\t\tservice,\n\t\tawsV4Request,\n\t}, \"/\")\n}\n\nfunc deriveSigningKey(region, service, secretKey string, dt time.Time) []byte {\n\tkeyDate := hmacSHA256([]byte(\"AWS4\"+secretKey), []byte(formatShortTime(dt)))\n\tkeyRegion := hmacSHA256(keyDate, []byte(region))\n\tkeyService := hmacSHA256(keyRegion, []byte(service))\n\tsigningKey := hmacSHA256(keyService, []byte(awsV4Request))\n\treturn signingKey\n}\n\nfunc formatShortTime(dt time.Time) string {\n\treturn dt.UTC().Format(shortTimeFormat)\n}\n\nfunc formatTime(dt time.Time) string {\n\treturn dt.UTC().Format(timeFormat)\n}\n"
  },
  {
    "path": "internal/aws/signer/v4/v4_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n//\n// Based on github.com/aws/aws-sdk-go by Amazon.com, Inc. with code from:\n// - github.com/aws/aws-sdk-go/blob/v1.44.225/aws/signer/v4/v4_test.go\n// See THIRD-PARTY-NOTICES for original license terms\n\npackage v4\n\nimport (\n\t\"bytes\"\n\t\"io\"\n\t\"io/ioutil\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"reflect\"\n\t\"strconv\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/aws\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/aws/credentials\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/credproviders\"\n)\n\nfunc epochTime() time.Time { return time.Unix(0, 0) }\n\nfunc TestStripExcessHeaders(t *testing.T) {\n\tvals := []string{\n\t\t\"\",\n\t\t\"123\",\n\t\t\"1 2 3\",\n\t\t\"1 2 3 \",\n\t\t\"  1 2 3\",\n\t\t\"1  2 3\",\n\t\t\"1  23\",\n\t\t\"1  2  3\",\n\t\t\"1  2  \",\n\t\t\" 1  2  \",\n\t\t\"12   3\",\n\t\t\"12   3   1\",\n\t\t\"12           3     1\",\n\t\t\"12     3       1abc123\",\n\t}\n\n\texpected := []string{\n\t\t\"\",\n\t\t\"123\",\n\t\t\"1 2 3\",\n\t\t\"1 2 3\",\n\t\t\"1 2 3\",\n\t\t\"1 2 3\",\n\t\t\"1 23\",\n\t\t\"1 2 3\",\n\t\t\"1 2\",\n\t\t\"1 2\",\n\t\t\"12 3\",\n\t\t\"12 3 1\",\n\t\t\"12 3 1\",\n\t\t\"12 3 1abc123\",\n\t}\n\n\tstripExcessSpaces(vals)\n\tfor i := 0; i < len(vals); i++ {\n\t\tif e, a := expected[i], vals[i]; e != a {\n\t\t\tt.Errorf(\"%d, expect %v, got %v\", i, e, a)\n\t\t}\n\t}\n}\n\nfunc buildRequest(body string) (*http.Request, io.ReadSeeker) {\n\treader := strings.NewReader(body)\n\treturn buildRequestWithBodyReader(\"dynamodb\", \"us-east-1\", reader)\n}\n\nfunc buildRequestReaderSeeker(serviceName, region, body string) (*http.Request, io.ReadSeeker) {\n\treader := &readerSeekerWrapper{strings.NewReader(body)}\n\treturn buildRequestWithBodyReader(serviceName, region, reader)\n}\n\nfunc buildRequestWithBodyReader(serviceName, region string, body io.Reader) (*http.Request, io.ReadSeeker) {\n\tvar bodyLen int\n\n\ttype lenner interface {\n\t\tLen() int\n\t}\n\tif lr, ok := body.(lenner); ok {\n\t\tbodyLen = lr.Len()\n\t}\n\n\tendpoint := \"https://\" + serviceName + \".\" + region + \".amazonaws.com\"\n\treq, _ := http.NewRequest(\"POST\", endpoint, body)\n\treq.URL.Opaque = \"//example.org/bucket/key-._~,!@#$%^&*()\"\n\treq.Header.Set(\"X-Amz-Target\", \"prefix.Operation\")\n\treq.Header.Set(\"Content-Type\", \"application/x-amz-json-1.0\")\n\n\tif bodyLen > 0 {\n\t\treq.Header.Set(\"Content-Length\", strconv.Itoa(bodyLen))\n\t}\n\n\treq.Header.Set(\"X-Amz-Meta-Other-Header\", \"some-value=!@#$%^&* (+)\")\n\treq.Header.Add(\"X-Amz-Meta-Other-Header_With_Underscore\", \"some-value=!@#$%^&* (+)\")\n\treq.Header.Add(\"X-amz-Meta-Other-Header_With_Underscore\", \"some-value=!@#$%^&* (+)\")\n\n\tvar seeker io.ReadSeeker\n\tif sr, ok := body.(io.ReadSeeker); ok {\n\t\tseeker = sr\n\t} else {\n\t\tseeker = aws.ReadSeekCloser(body)\n\t}\n\n\treturn req, seeker\n}\n\nfunc buildSigner() Signer {\n\treturn Signer{\n\t\tCredentials: newTestStaticCredentials(),\n\t}\n}\n\nfunc newTestStaticCredentials() *credentials.Credentials {\n\treturn credentials.NewCredentials(&credproviders.StaticProvider{Value: credentials.Value{\n\t\tAccessKeyID:     \"AKID\",\n\t\tSecretAccessKey: \"SECRET\",\n\t\tSessionToken:    \"SESSION\",\n\t}})\n}\n\nfunc TestSignRequest(t *testing.T) {\n\treq, body := buildRequest(\"{}\")\n\tsigner := buildSigner()\n\t_, err := signer.Sign(req, body, \"dynamodb\", \"us-east-1\", epochTime())\n\tif err != nil {\n\t\tt.Errorf(\"Expected no err, got %v\", err)\n\t}\n\n\texpectedDate := \"19700101T000000Z\"\n\texpectedSig := \"AWS4-HMAC-SHA256 Credential=AKID/19700101/us-east-1/dynamodb/aws4_request, SignedHeaders=content-length;content-type;host;x-amz-date;x-amz-meta-other-header;x-amz-meta-other-header_with_underscore;x-amz-security-token;x-amz-target, Signature=a518299330494908a70222cec6899f6f32f297f8595f6df1776d998936652ad9\"\n\n\tq := req.Header\n\tif e, a := expectedSig, q.Get(\"Authorization\"); e != a {\n\t\tt.Errorf(\"expect\\n%v\\nactual\\n%v\\n\", e, a)\n\t}\n\tif e, a := expectedDate, q.Get(\"X-Amz-Date\"); e != a {\n\t\tt.Errorf(\"expect\\n%v\\nactual\\n%v\\n\", e, a)\n\t}\n}\n\nfunc TestSignUnseekableBody(t *testing.T) {\n\treq, body := buildRequestWithBodyReader(\"mock-service\", \"mock-region\", bytes.NewBuffer([]byte(\"hello\")))\n\tsigner := buildSigner()\n\t_, err := signer.Sign(req, body, \"mock-service\", \"mock-region\", time.Now())\n\tif err == nil {\n\t\tt.Fatalf(\"expect error signing request\")\n\t}\n\n\tif e, a := \"unseekable request body\", err.Error(); !strings.Contains(a, e) {\n\t\tt.Errorf(\"expect %q to be in %q\", e, a)\n\t}\n}\n\nfunc TestSignPreComputedHashUnseekableBody(t *testing.T) {\n\treq, body := buildRequestWithBodyReader(\"mock-service\", \"mock-region\", bytes.NewBuffer([]byte(\"hello\")))\n\n\tsigner := buildSigner()\n\n\treq.Header.Set(\"X-Amz-Content-Sha256\", \"some-content-sha256\")\n\t_, err := signer.Sign(req, body, \"mock-service\", \"mock-region\", time.Now())\n\tif err != nil {\n\t\tt.Fatalf(\"expect no error, got %v\", err)\n\t}\n\n\thash := req.Header.Get(\"X-Amz-Content-Sha256\")\n\tif e, a := \"some-content-sha256\", hash; e != a {\n\t\tt.Errorf(\"expect %v, got %v\", e, a)\n\t}\n}\n\nfunc TestSignPrecomputedBodyChecksum(t *testing.T) {\n\treq, body := buildRequest(\"hello\")\n\treq.Header.Set(\"X-Amz-Content-Sha256\", \"PRECOMPUTED\")\n\tsigner := buildSigner()\n\t_, err := signer.Sign(req, body, \"dynamodb\", \"us-east-1\", time.Now())\n\tif err != nil {\n\t\tt.Errorf(\"Expected no err, got %v\", err)\n\t}\n\thash := req.Header.Get(\"X-Amz-Content-Sha256\")\n\tif e, a := \"PRECOMPUTED\", hash; e != a {\n\t\tt.Errorf(\"expect %v, got %v\", e, a)\n\t}\n}\n\nfunc TestSignWithRequestBody(t *testing.T) {\n\tcreds := newTestStaticCredentials()\n\tsigner := NewSigner(creds)\n\n\texpectBody := []byte(\"abc123\")\n\n\tserver := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tb, err := ioutil.ReadAll(r.Body)\n\t\tr.Body.Close()\n\t\tif err != nil {\n\t\t\tt.Errorf(\"expect no error, got %v\", err)\n\t\t}\n\t\tif e, a := expectBody, b; !reflect.DeepEqual(e, a) {\n\t\t\tt.Errorf(\"expect %v, got %v\", e, a)\n\t\t}\n\t\tw.WriteHeader(http.StatusOK)\n\t}))\n\tdefer server.Close()\n\n\treq, err := http.NewRequest(\"POST\", server.URL, nil)\n\tif err != nil {\n\t\tt.Errorf(\"expect not no error, got %v\", err)\n\t}\n\n\t_, err = signer.Sign(req, bytes.NewReader(expectBody), \"service\", \"region\", time.Now())\n\tif err != nil {\n\t\tt.Errorf(\"expect not no error, got %v\", err)\n\t}\n\n\tresp, err := http.DefaultClient.Do(req)\n\tif err != nil {\n\t\tt.Errorf(\"expect not no error, got %v\", err)\n\t}\n\tif e, a := http.StatusOK, resp.StatusCode; e != a {\n\t\tt.Errorf(\"expect %v, got %v\", e, a)\n\t}\n}\n\nfunc TestSignWithRequestBody_Overwrite(t *testing.T) {\n\tcreds := newTestStaticCredentials()\n\tsigner := NewSigner(creds)\n\n\tvar expectBody []byte\n\n\tserver := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tb, err := ioutil.ReadAll(r.Body)\n\t\tr.Body.Close()\n\t\tif err != nil {\n\t\t\tt.Errorf(\"expect not no error, got %v\", err)\n\t\t}\n\t\tif e, a := len(expectBody), len(b); e != a {\n\t\t\tt.Errorf(\"expect %v, got %v\", e, a)\n\t\t}\n\t\tw.WriteHeader(http.StatusOK)\n\t}))\n\tdefer server.Close()\n\n\treq, err := http.NewRequest(\"GET\", server.URL, strings.NewReader(\"invalid body\"))\n\tif err != nil {\n\t\tt.Errorf(\"expect not no error, got %v\", err)\n\t}\n\n\t_, err = signer.Sign(req, nil, \"service\", \"region\", time.Now())\n\treq.ContentLength = 0\n\n\tif err != nil {\n\t\tt.Errorf(\"expect not no error, got %v\", err)\n\t}\n\n\tresp, err := http.DefaultClient.Do(req)\n\tif err != nil {\n\t\tt.Errorf(\"expect not no error, got %v\", err)\n\t}\n\tif e, a := http.StatusOK, resp.StatusCode; e != a {\n\t\tt.Errorf(\"expect %v, got %v\", e, a)\n\t}\n}\n\nfunc TestBuildCanonicalRequest(t *testing.T) {\n\treq, body := buildRequest(\"{}\")\n\treq.URL.RawQuery = \"Foo=z&Foo=o&Foo=m&Foo=a\"\n\tctx := &signingCtx{\n\t\tServiceName: \"dynamodb\",\n\t\tRegion:      \"us-east-1\",\n\t\tRequest:     req,\n\t\tBody:        body,\n\t\tQuery:       req.URL.Query(),\n\t\tTime:        time.Now(),\n\t}\n\n\tctx.buildCanonicalString()\n\texpected := \"https://example.org/bucket/key-._~,!@#$%^&*()?Foo=z&Foo=o&Foo=m&Foo=a\"\n\tif e, a := expected, ctx.Request.URL.String(); e != a {\n\t\tt.Errorf(\"expect %v, got %v\", e, a)\n\t}\n}\n\nfunc TestSignWithBody_ReplaceRequestBody(t *testing.T) {\n\tcreds := newTestStaticCredentials()\n\treq, seekerBody := buildRequest(\"{}\")\n\treq.Body = ioutil.NopCloser(bytes.NewReader([]byte{}))\n\n\ts := NewSigner(creds)\n\torigBody := req.Body\n\n\t_, err := s.Sign(req, seekerBody, \"dynamodb\", \"us-east-1\", time.Now())\n\tif err != nil {\n\t\tt.Fatalf(\"expect no error, got %v\", err)\n\t}\n\n\tif req.Body == origBody {\n\t\tt.Errorf(\"expect request body to not be origBody\")\n\t}\n\n\tif req.Body == nil {\n\t\tt.Errorf(\"expect request body to be changed but was nil\")\n\t}\n}\n\nfunc TestRequestHost(t *testing.T) {\n\treq, body := buildRequest(\"{}\")\n\treq.URL.RawQuery = \"Foo=z&Foo=o&Foo=m&Foo=a\"\n\treq.Host = \"myhost\"\n\tctx := &signingCtx{\n\t\tServiceName: \"dynamodb\",\n\t\tRegion:      \"us-east-1\",\n\t\tRequest:     req,\n\t\tBody:        body,\n\t\tQuery:       req.URL.Query(),\n\t\tTime:        time.Now(),\n\t}\n\n\tctx.buildCanonicalHeaders(ignoredHeaders, ctx.Request.Header)\n\tif !strings.Contains(ctx.canonicalHeaders, \"host:\"+req.Host) {\n\t\tt.Errorf(\"canonical host header invalid\")\n\t}\n}\n\nfunc TestSign_buildCanonicalHeaders(t *testing.T) {\n\tserviceName := \"mockAPI\"\n\tregion := \"mock-region\"\n\tendpoint := \"https://\" + serviceName + \".\" + region + \".amazonaws.com\"\n\n\treq, err := http.NewRequest(\"POST\", endpoint, nil)\n\tif err != nil {\n\t\tt.Fatalf(\"failed to create request, %v\", err)\n\t}\n\n\treq.Header.Set(\"FooInnerSpace\", \"   inner      space    \")\n\treq.Header.Set(\"FooLeadingSpace\", \"    leading-space\")\n\treq.Header.Add(\"FooMultipleSpace\", \"no-space\")\n\treq.Header.Add(\"FooMultipleSpace\", \"\\ttab-space\")\n\treq.Header.Add(\"FooMultipleSpace\", \"trailing-space    \")\n\treq.Header.Set(\"FooNoSpace\", \"no-space\")\n\treq.Header.Set(\"FooTabSpace\", \"\\ttab-space\\t\")\n\treq.Header.Set(\"FooTrailingSpace\", \"trailing-space    \")\n\treq.Header.Set(\"FooWrappedSpace\", \"   wrapped-space    \")\n\n\tctx := &signingCtx{\n\t\tServiceName: serviceName,\n\t\tRegion:      region,\n\t\tRequest:     req,\n\t\tBody:        nil,\n\t\tQuery:       req.URL.Query(),\n\t\tTime:        time.Now(),\n\t}\n\n\tctx.buildCanonicalHeaders(ignoredHeaders, ctx.Request.Header)\n\n\texpectCanonicalHeaders := strings.Join([]string{\n\t\t`fooinnerspace:inner space`,\n\t\t`fooleadingspace:leading-space`,\n\t\t`foomultiplespace:no-space,tab-space,trailing-space`,\n\t\t`foonospace:no-space`,\n\t\t`footabspace:tab-space`,\n\t\t`footrailingspace:trailing-space`,\n\t\t`foowrappedspace:wrapped-space`,\n\t\t`host:mockAPI.mock-region.amazonaws.com`,\n\t}, \"\\n\")\n\tif e, a := expectCanonicalHeaders, ctx.canonicalHeaders; e != a {\n\t\tt.Errorf(\"expect:\\n%s\\n\\nactual:\\n%s\", e, a)\n\t}\n}\n\nfunc BenchmarkSignRequest(b *testing.B) {\n\tsigner := buildSigner()\n\treq, body := buildRequestReaderSeeker(\"dynamodb\", \"us-east-1\", \"{}\")\n\tfor i := 0; i < b.N; i++ {\n\t\t_, err := signer.Sign(req, body, \"dynamodb\", \"us-east-1\", time.Now())\n\t\tif err != nil {\n\t\t\tb.Errorf(\"Expected no err, got %v\", err)\n\t\t}\n\t}\n}\n\nvar stripExcessSpaceCases = []string{\n\t`AWS4-HMAC-SHA256 Credential=AKIDFAKEIDFAKEID/20160628/us-west-2/s3/aws4_request, SignedHeaders=host;x-amz-date, Signature=1234567890abcdef1234567890abcdef1234567890abcdef`,\n\t`123   321   123   321`,\n\t`   123   321   123   321   `,\n\t`   123    321    123          321   `,\n\t\"123\",\n\t\"1 2 3\",\n\t\"  1 2 3\",\n\t\"1  2 3\",\n\t\"1  23\",\n\t\"1  2  3\",\n\t\"1  2  \",\n\t\" 1  2  \",\n\t\"12   3\",\n\t\"12   3   1\",\n\t\"12           3     1\",\n\t\"12     3       1abc123\",\n}\n\nfunc BenchmarkStripExcessSpaces(b *testing.B) {\n\tfor i := 0; i < b.N; i++ {\n\t\t// Make sure to start with a copy of the cases\n\t\tcases := append([]string{}, stripExcessSpaceCases...)\n\t\tstripExcessSpaces(cases)\n\t}\n}\n\n// readerSeekerWrapper mimics the interface provided by request.offsetReader\ntype readerSeekerWrapper struct {\n\tr *strings.Reader\n}\n\nfunc (r *readerSeekerWrapper) Read(p []byte) (n int, err error) {\n\treturn r.r.Read(p)\n}\n\nfunc (r *readerSeekerWrapper) Seek(offset int64, whence int) (int64, error) {\n\treturn r.r.Seek(offset, whence)\n}\n\nfunc (r *readerSeekerWrapper) Len() int {\n\treturn r.r.Len()\n}\n"
  },
  {
    "path": "internal/aws/types.go",
    "content": "// Copyright (C) MongoDB, Inc. 2023-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n//\n// Based on github.com/aws/aws-sdk-go by Amazon.com, Inc. with code from:\n// - github.com/aws/aws-sdk-go/blob/v1.44.225/aws/types.go\n// See THIRD-PARTY-NOTICES for original license terms\n\npackage aws\n\nimport (\n\t\"io\"\n)\n\n// ReadSeekCloser wraps a io.Reader returning a ReaderSeekerCloser. Allows the\n// SDK to accept an io.Reader that is not also an io.Seeker for unsigned\n// streaming payload API operations.\n//\n// A ReadSeekCloser wrapping an nonseekable io.Reader used in an API\n// operation's input will prevent that operation being retried in the case of\n// network errors, and cause operation requests to fail if the operation\n// requires payload signing.\n//\n// Note: If using With S3 PutObject to stream an object upload The SDK's S3\n// Upload manager (s3manager.Uploader) provides support for streaming with the\n// ability to retry network errors.\nfunc ReadSeekCloser(r io.Reader) ReaderSeekerCloser {\n\treturn ReaderSeekerCloser{r}\n}\n\n// ReaderSeekerCloser represents a reader that can also delegate io.Seeker and\n// io.Closer interfaces to the underlying object if they are available.\ntype ReaderSeekerCloser struct {\n\tr io.Reader\n}\n\n// IsReaderSeekable returns if the underlying reader type can be seeked. A\n// io.Reader might not actually be seekable if it is the ReaderSeekerCloser\n// type.\nfunc IsReaderSeekable(r io.Reader) bool {\n\tswitch v := r.(type) {\n\tcase ReaderSeekerCloser:\n\t\treturn v.IsSeeker()\n\tcase *ReaderSeekerCloser:\n\t\treturn v.IsSeeker()\n\tcase io.ReadSeeker:\n\t\treturn true\n\tdefault:\n\t\treturn false\n\t}\n}\n\n// Read reads from the reader up to size of p. The number of bytes read, and\n// error if it occurred will be returned.\n//\n// If the reader is not an io.Reader zero bytes read, and nil error will be\n// returned.\n//\n// Performs the same functionality as io.Reader Read\nfunc (r ReaderSeekerCloser) Read(p []byte) (int, error) {\n\tswitch t := r.r.(type) {\n\tcase io.Reader:\n\t\treturn t.Read(p)\n\t}\n\treturn 0, nil\n}\n\n// Seek sets the offset for the next Read to offset, interpreted according to\n// whence: 0 means relative to the origin of the file, 1 means relative to the\n// current offset, and 2 means relative to the end. Seek returns the new offset\n// and an error, if any.\n//\n// If the ReaderSeekerCloser is not an io.Seeker nothing will be done.\nfunc (r ReaderSeekerCloser) Seek(offset int64, whence int) (int64, error) {\n\tswitch t := r.r.(type) {\n\tcase io.Seeker:\n\t\treturn t.Seek(offset, whence)\n\t}\n\treturn int64(0), nil\n}\n\n// IsSeeker returns if the underlying reader is also a seeker.\nfunc (r ReaderSeekerCloser) IsSeeker() bool {\n\t_, ok := r.r.(io.Seeker)\n\treturn ok\n}\n\n// HasLen returns the length of the underlying reader if the value implements\n// the Len() int method.\nfunc (r ReaderSeekerCloser) HasLen() (int, bool) {\n\ttype lenner interface {\n\t\tLen() int\n\t}\n\n\tif lr, ok := r.r.(lenner); ok {\n\t\treturn lr.Len(), true\n\t}\n\n\treturn 0, false\n}\n\n// GetLen returns the length of the bytes remaining in the underlying reader.\n// Checks first for Len(), then io.Seeker to determine the size of the\n// underlying reader.\n//\n// Will return -1 if the length cannot be determined.\nfunc (r ReaderSeekerCloser) GetLen() (int64, error) {\n\tif l, ok := r.HasLen(); ok {\n\t\treturn int64(l), nil\n\t}\n\n\tif s, ok := r.r.(io.Seeker); ok {\n\t\treturn seekerLen(s)\n\t}\n\n\treturn -1, nil\n}\n\n// SeekerLen attempts to get the number of bytes remaining at the seeker's\n// current position.  Returns the number of bytes remaining or error.\nfunc SeekerLen(s io.Seeker) (int64, error) {\n\t// Determine if the seeker is actually seekable. ReaderSeekerCloser\n\t// hides the fact that a io.Readers might not actually be seekable.\n\tswitch v := s.(type) {\n\tcase ReaderSeekerCloser:\n\t\treturn v.GetLen()\n\tcase *ReaderSeekerCloser:\n\t\treturn v.GetLen()\n\t}\n\n\treturn seekerLen(s)\n}\n\nfunc seekerLen(s io.Seeker) (int64, error) {\n\tcurOffset, err := s.Seek(0, io.SeekCurrent)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\tendOffset, err := s.Seek(0, io.SeekEnd)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\t_, err = s.Seek(curOffset, io.SeekStart)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\treturn endOffset - curOffset, nil\n}\n"
  },
  {
    "path": "internal/binaryutil/binaryutil.go",
    "content": "// Copyright (C) MongoDB, Inc. 2025-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\n// Package binaryutil provides utility functions for working with binary data.\npackage binaryutil\n\nimport (\n\t\"bytes\"\n\t\"encoding/binary\"\n)\n\n// Append32 appends a uint32 or int32 value to dst in little-endian byte order.\n// Byte shifting is done directly to prevent overflow security errors, in\n// compliance with gosec G115.\n//\n// See: https://cs.opensource.google/go/go/+/refs/tags/go1.19:src/encoding/binary/binary.go;l=92\nfunc Append32[T ~uint32 | ~int32](dst []byte, v T) []byte {\n\treturn append(dst,\n\t\tbyte(v),\n\t\tbyte(v>>8),\n\t\tbyte(v>>16),\n\t\tbyte(v>>24),\n\t)\n}\n\n// Append64 appends a uint64 or int64 value to dst in little-endian byte order.\n// Byte shifting is done directly to prevent overflow security errors, in\n// compliance with gosec G115.\n//\n// See: https://cs.opensource.google/go/go/+/refs/tags/go1.19:src/encoding/binary/binary.go;l=119\nfunc Append64[T ~uint64 | ~int64](dst []byte, v T) []byte {\n\treturn append(dst,\n\t\tbyte(v),\n\t\tbyte(v>>8),\n\t\tbyte(v>>16),\n\t\tbyte(v>>24),\n\t\tbyte(v>>32),\n\t\tbyte(v>>40),\n\t\tbyte(v>>48),\n\t\tbyte(v>>56),\n\t)\n}\n\n// ReadU32 reads a uint32 from src in little-endian byte order. ReadU32 and\n// ReadI32 are separate functions to avoid unsafe casting between unsigned and\n// signed integers.\nfunc ReadU32(src []byte) (uint32, []byte, bool) {\n\tif len(src) < 4 {\n\t\treturn 0, src, false\n\t}\n\n\treturn binary.LittleEndian.Uint32(src), src[4:], true\n}\n\n// ReadI32 reads an int32 from src in little-endian byte order.\n// Byte shifting is done directly to prevent overflow security errors, in\n// compliance with gosec G115. ReadU32 and ReadI32 are separate functions to\n// avoid unsafe casting between unsigned and signed integers.\n//\n// See: https://cs.opensource.google/go/go/+/refs/tags/go1.19:src/encoding/binary/binary.go;l=79\nfunc ReadI32(src []byte) (int32, []byte, bool) {\n\tif len(src) < 4 {\n\t\treturn 0, src, false\n\t}\n\n\t_ = src[3] // bounds check hint to compiler\n\n\tvalue := int32(src[0]) |\n\t\tint32(src[1])<<8 |\n\t\tint32(src[2])<<16 |\n\t\tint32(src[3])<<24\n\n\treturn value, src[4:], true\n}\n\n// ReadU64 reads a uint64 from src in little-endian byte order. ReadU64 and\n// ReadI64 are separate functions to avoid unsafe casting between unsigned and\n// signed integers.\nfunc ReadU64(src []byte) (uint64, []byte, bool) {\n\tif len(src) < 8 {\n\t\treturn 0, src, false\n\t}\n\n\treturn binary.LittleEndian.Uint64(src), src[8:], true\n}\n\n// ReadI64 reads an int64 from src in little-endian byte order.\n// Byte shifting is done directly to prevent overflow security errors, in\n// compliance with gosec G115. ReadU64 and ReadI64 are separate functions to\n// avoid unsafe casting between unsigned and signed integers.\n//\n// See: https://cs.opensource.google/go/go/+/refs/tags/go1.19:src/encoding/binary/binary.go;l=101\nfunc ReadI64(src []byte) (int64, []byte, bool) {\n\tif len(src) < 8 {\n\t\treturn 0, src, false\n\t}\n\n\t_ = src[7] // bounds check hint to compiler\n\n\tvalue := int64(src[0]) |\n\t\tint64(src[1])<<8 |\n\t\tint64(src[2])<<16 |\n\t\tint64(src[3])<<24 |\n\t\tint64(src[4])<<32 |\n\t\tint64(src[5])<<40 |\n\t\tint64(src[6])<<48 |\n\t\tint64(src[7])<<56\n\n\treturn value, src[8:], true\n}\n\n// ReadCStringBytes reads a null-terminated C string from src as a byte slice.\n// This is the base implementation used by ReadCString to ensure a single source\n// of truth for C string parsing logic.\nfunc ReadCStringBytes(src []byte) ([]byte, []byte, bool) {\n\tidx := bytes.IndexByte(src, 0x00)\n\tif idx < 0 {\n\t\treturn nil, src, false\n\t}\n\treturn src[:idx], src[idx+1:], true\n}\n\n// ReadCString reads a null-terminated C string from src as a string.\n// It delegates to ReadCStringBytes to maintain a single source of truth for\n// C string parsing logic.\nfunc ReadCString(src []byte) (string, []byte, bool) {\n\tcstr, rem, ok := ReadCStringBytes(src)\n\tif !ok {\n\t\treturn \"\", src, false\n\t}\n\treturn string(cstr), rem, true\n}\n"
  },
  {
    "path": "internal/binaryutil/binaryutil_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2025-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage binaryutil\n\nimport (\n\t\"bytes\"\n\t\"encoding/binary\"\n\t\"fmt\"\n\t\"math\"\n\t\"testing\"\n)\n\n// Test values matching stdlib pattern for comprehensive coverage.\nvar testValues64 = []uint64{\n\t0x0000000000000000,\n\t0x0123456789abcdef,\n\t0xfedcba9876543210,\n\t0xffffffffffffffff,\n\t0xaaaaaaaaaaaaaaaa,\n\tmath.Float64bits(math.Pi),\n\tmath.Float64bits(math.E),\n}\n\nvar testValues32 = []uint32{\n\t0x00000000,\n\t0x01234567,\n\t0xfedcba98,\n\t0xffffffff,\n\t0xdeadbeef,\n\t0xaaaaaaaa,\n}\n\n// -----------------------------------------------------------------------------\n// Benchmark Tests - Append Functions\n// -----------------------------------------------------------------------------\n\nfunc BenchmarkAppend32(b *testing.B) {\n\tb.SetBytes(4)\n\tb.RunParallel(func(pb *testing.PB) {\n\t\tbuf := make([]byte, 0, 4)\n\t\ti := uint32(0)\n\t\tfor pb.Next() {\n\t\t\tbuf = Append32(buf[:0], i)\n\t\t\ti++\n\t\t}\n\t})\n}\n\nfunc BenchmarkAppend64(b *testing.B) {\n\tb.SetBytes(8)\n\tb.RunParallel(func(pb *testing.PB) {\n\t\tbuf := make([]byte, 0, 8)\n\t\ti := uint64(0)\n\t\tfor pb.Next() {\n\t\t\tbuf = Append64(buf[:0], i)\n\t\t\ti++\n\t\t}\n\t})\n}\n\n// Comparison benchmarks against stdlib LittleEndian.\nfunc BenchmarkStdlibAppendUint32(b *testing.B) {\n\tb.SetBytes(4)\n\tb.RunParallel(func(pb *testing.PB) {\n\t\tbuf := make([]byte, 0, 4)\n\t\ti := uint32(0)\n\t\tfor pb.Next() {\n\t\t\tbuf = binary.LittleEndian.AppendUint32(buf[:0], i)\n\t\t\ti++\n\t\t}\n\t})\n}\n\nfunc BenchmarkStdlibAppendUint64(b *testing.B) {\n\tb.SetBytes(8)\n\tb.RunParallel(func(pb *testing.PB) {\n\t\tbuf := make([]byte, 0, 8)\n\t\ti := uint64(0)\n\t\tfor pb.Next() {\n\t\t\tbuf = binary.LittleEndian.AppendUint64(buf[:0], i)\n\t\t\ti++\n\t\t}\n\t})\n}\n\n// -----------------------------------------------------------------------------\n// Benchmark Tests - Read Functions\n// -----------------------------------------------------------------------------\n\nfunc BenchmarkReadU32(b *testing.B) {\n\tbuf := []byte{0x01, 0x02, 0x03, 0x04}\n\tb.SetBytes(4)\n\tb.RunParallel(func(pb *testing.PB) {\n\t\tfor pb.Next() {\n\t\t\t_, _, _ = ReadU32(buf)\n\t\t}\n\t})\n}\n\nfunc BenchmarkReadI32(b *testing.B) {\n\tbuf := []byte{0x01, 0x02, 0x03, 0x04}\n\tb.SetBytes(4)\n\tb.RunParallel(func(pb *testing.PB) {\n\t\tfor pb.Next() {\n\t\t\t_, _, _ = ReadI32(buf)\n\t\t}\n\t})\n}\n\nfunc BenchmarkReadU64(b *testing.B) {\n\tbuf := []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}\n\tb.SetBytes(8)\n\tb.RunParallel(func(pb *testing.PB) {\n\t\tfor pb.Next() {\n\t\t\t_, _, _ = ReadU64(buf)\n\t\t}\n\t})\n}\n\nfunc BenchmarkReadI64(b *testing.B) {\n\tbuf := []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}\n\tb.SetBytes(8)\n\tb.RunParallel(func(pb *testing.PB) {\n\t\tfor pb.Next() {\n\t\t\t_, _, _ = ReadI64(buf)\n\t\t}\n\t})\n}\n\n// Comparison benchmarks against stdlib LittleEndian.\nfunc BenchmarkStdlibUint32(b *testing.B) {\n\tbuf := []byte{0x01, 0x02, 0x03, 0x04}\n\tb.SetBytes(4)\n\tb.RunParallel(func(pb *testing.PB) {\n\t\tfor pb.Next() {\n\t\t\t_ = binary.LittleEndian.Uint32(buf)\n\t\t}\n\t})\n}\n\nfunc BenchmarkStdlibUint64(b *testing.B) {\n\tbuf := []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}\n\tb.SetBytes(8)\n\tb.RunParallel(func(pb *testing.PB) {\n\t\tfor pb.Next() {\n\t\t\t_ = binary.LittleEndian.Uint64(buf)\n\t\t}\n\t})\n}\n\n// -----------------------------------------------------------------------------\n// Benchmark Tests - CString Functions\n// -----------------------------------------------------------------------------\n\nfunc BenchmarkReadCString(b *testing.B) {\n\tbuf := []byte(\"hello world\\x00remaining data\")\n\tb.SetBytes(int64(len(\"hello world\") + 1))\n\tb.RunParallel(func(pb *testing.PB) {\n\t\tfor pb.Next() {\n\t\t\t_, _, _ = ReadCString(buf)\n\t\t}\n\t})\n}\n\nfunc BenchmarkReadCStringBytes(b *testing.B) {\n\tbuf := []byte(\"hello world\\x00remaining data\")\n\tb.SetBytes(int64(len(\"hello world\") + 1))\n\tb.RunParallel(func(pb *testing.PB) {\n\t\tfor pb.Next() {\n\t\t\t_, _, _ = ReadCStringBytes(buf)\n\t\t}\n\t})\n}\n\nfunc BenchmarkReadCStringLong(b *testing.B) {\n\t// Test with a longer string to see IndexByte performance.\n\tlongStr := make([]byte, 256)\n\tfor i := range longStr {\n\t\tlongStr[i] = byte('a' + (i % 26))\n\t}\n\tlongStr[255] = 0x00\n\tb.SetBytes(256)\n\tb.RunParallel(func(pb *testing.PB) {\n\t\tfor pb.Next() {\n\t\t\t_, _, _ = ReadCString(longStr)\n\t\t}\n\t})\n}\n\n// -----------------------------------------------------------------------------\n// Unit Tests - Round-trip Tests\n// -----------------------------------------------------------------------------\n\nfunc TestAppend32RoundTrip(t *testing.T) {\n\tt.Parallel()\n\tfor _, want := range testValues32 {\n\t\twant := want // capture range variable\n\t\tt.Run(fmt.Sprintf(\"%#x\", want), func(t *testing.T) {\n\t\t\tt.Parallel()\n\t\t\tbuf := Append32(nil, want)\n\t\t\tgot, rem, ok := ReadU32(buf)\n\t\t\tif !ok {\n\t\t\t\tt.Errorf(\"ReadU32 failed for value %#x\", want)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif len(rem) != 0 {\n\t\t\t\tt.Errorf(\"ReadU32(%#x): remaining bytes = %d, want 0\", want, len(rem))\n\t\t\t}\n\t\t\tif got != want {\n\t\t\t\tt.Errorf(\"Append32/ReadU32 round-trip: got %#x, want %#x\", got, want)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestAppend32RoundTripSigned(t *testing.T) {\n\tt.Parallel()\n\tsignedValues := []int32{\n\t\t0,\n\t\t1,\n\t\t-1,\n\t\tmath.MaxInt32,\n\t\tmath.MinInt32,\n\t\t0x01234567,\n\t\t-0x01234567,\n\t}\n\tfor _, want := range signedValues {\n\t\twant := want // capture range variable\n\t\tt.Run(fmt.Sprintf(\"%d\", want), func(t *testing.T) {\n\t\t\tt.Parallel()\n\t\t\tbuf := Append32(nil, want)\n\t\t\tgot, rem, ok := ReadI32(buf)\n\t\t\tif !ok {\n\t\t\t\tt.Errorf(\"ReadI32 failed for value %d\", want)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif len(rem) != 0 {\n\t\t\t\tt.Errorf(\"ReadI32(%d): remaining bytes = %d, want 0\", want, len(rem))\n\t\t\t}\n\t\t\tif got != want {\n\t\t\t\tt.Errorf(\"Append32/ReadI32 round-trip: got %d, want %d\", got, want)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestAppend64RoundTrip(t *testing.T) {\n\tt.Parallel()\n\tfor _, want := range testValues64 {\n\t\twant := want // capture range variable\n\t\tt.Run(fmt.Sprintf(\"%#x\", want), func(t *testing.T) {\n\t\t\tt.Parallel()\n\t\t\tbuf := Append64(nil, want)\n\t\t\tgot, rem, ok := ReadU64(buf)\n\t\t\tif !ok {\n\t\t\t\tt.Errorf(\"ReadU64 failed for value %#x\", want)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif len(rem) != 0 {\n\t\t\t\tt.Errorf(\"ReadU64(%#x): remaining bytes = %d, want 0\", want, len(rem))\n\t\t\t}\n\t\t\tif got != want {\n\t\t\t\tt.Errorf(\"Append64/ReadU64 round-trip: got %#x, want %#x\", got, want)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestAppend64RoundTripSigned(t *testing.T) {\n\tt.Parallel()\n\tsignedValues := []int64{\n\t\t0,\n\t\t1,\n\t\t-1,\n\t\tmath.MaxInt64,\n\t\tmath.MinInt64,\n\t\t0x0123456789abcdef,\n\t\t-0x0123456789abcdef,\n\t}\n\tfor _, want := range signedValues {\n\t\twant := want // capture range variable\n\t\tt.Run(fmt.Sprintf(\"%d\", want), func(t *testing.T) {\n\t\t\tt.Parallel()\n\t\t\tbuf := Append64(nil, want)\n\t\t\tgot, rem, ok := ReadI64(buf)\n\t\t\tif !ok {\n\t\t\t\tt.Errorf(\"ReadI64 failed for value %d\", want)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif len(rem) != 0 {\n\t\t\t\tt.Errorf(\"ReadI64(%d): remaining bytes = %d, want 0\", want, len(rem))\n\t\t\t}\n\t\t\tif got != want {\n\t\t\t\tt.Errorf(\"Append64/ReadI64 round-trip: got %d, want %d\", got, want)\n\t\t\t}\n\t\t})\n\t}\n}\n\n// -----------------------------------------------------------------------------\n// Unit Tests - Stdlib Compatibility\n// -----------------------------------------------------------------------------\n\nfunc TestAppend32MatchesStdlib(t *testing.T) {\n\tt.Parallel()\n\tfor _, v := range testValues32 {\n\t\tv := v // capture range variable\n\t\tt.Run(fmt.Sprintf(\"%#x\", v), func(t *testing.T) {\n\t\t\tt.Parallel()\n\t\t\tgot := Append32(nil, v)\n\t\t\twant := binary.LittleEndian.AppendUint32(nil, v)\n\t\t\tif !bytes.Equal(got, want) {\n\t\t\t\tt.Errorf(\"Append32(%#x): got %v, want %v\", v, got, want)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestAppend64MatchesStdlib(t *testing.T) {\n\tt.Parallel()\n\tfor _, v := range testValues64 {\n\t\tv := v // capture range variable\n\t\tt.Run(fmt.Sprintf(\"%#x\", v), func(t *testing.T) {\n\t\t\tt.Parallel()\n\t\t\tgot := Append64(nil, v)\n\t\t\twant := binary.LittleEndian.AppendUint64(nil, v)\n\t\t\tif !bytes.Equal(got, want) {\n\t\t\t\tt.Errorf(\"Append64(%#x): got %v, want %v\", v, got, want)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestReadU32MatchesStdlib(t *testing.T) {\n\tt.Parallel()\n\tfor _, v := range testValues32 {\n\t\tv := v // capture range variable\n\t\tt.Run(fmt.Sprintf(\"%#x\", v), func(t *testing.T) {\n\t\t\tt.Parallel()\n\t\t\tbuf := binary.LittleEndian.AppendUint32(nil, v)\n\t\t\tgot, _, ok := ReadU32(buf)\n\t\t\tif !ok {\n\t\t\t\tt.Errorf(\"ReadU32 failed for value %#x\", v)\n\t\t\t\treturn\n\t\t\t}\n\t\t\twant := binary.LittleEndian.Uint32(buf)\n\t\t\tif got != want {\n\t\t\t\tt.Errorf(\"ReadU32: got %#x, want %#x\", got, want)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestReadU64MatchesStdlib(t *testing.T) {\n\tt.Parallel()\n\tfor _, v := range testValues64 {\n\t\tv := v // capture range variable\n\t\tt.Run(fmt.Sprintf(\"%#x\", v), func(t *testing.T) {\n\t\t\tt.Parallel()\n\t\t\tbuf := binary.LittleEndian.AppendUint64(nil, v)\n\t\t\tgot, _, ok := ReadU64(buf)\n\t\t\tif !ok {\n\t\t\t\tt.Errorf(\"ReadU64 failed for value %#x\", v)\n\t\t\t\treturn\n\t\t\t}\n\t\t\twant := binary.LittleEndian.Uint64(buf)\n\t\t\tif got != want {\n\t\t\t\tt.Errorf(\"ReadU64: got %#x, want %#x\", got, want)\n\t\t\t}\n\t\t})\n\t}\n}\n\n// -----------------------------------------------------------------------------\n// Unit Tests - Bounds Checking / Short Slices\n// -----------------------------------------------------------------------------\n\nfunc TestReadU32ShortSlice(t *testing.T) {\n\tt.Parallel()\n\ttestCases := []struct {\n\t\tname string\n\t\tbuf  []byte\n\t}{\n\t\t{\"empty\", []byte{}},\n\t\t{\"1 byte\", []byte{0x01}},\n\t\t{\"2 bytes\", []byte{0x01, 0x02}},\n\t\t{\"3 bytes\", []byte{0x01, 0x02, 0x03}},\n\t}\n\tfor _, tc := range testCases {\n\t\ttc := tc // capture range variable\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\tt.Parallel()\n\t\t\tval, rem, ok := ReadU32(tc.buf)\n\t\t\tif ok {\n\t\t\t\tt.Errorf(\"ReadU32(%v): expected ok=false, got ok=true\", tc.buf)\n\t\t\t}\n\t\t\tif val != 0 {\n\t\t\t\tt.Errorf(\"ReadU32(%v): expected val=0, got val=%d\", tc.buf, val)\n\t\t\t}\n\t\t\tif len(rem) != len(tc.buf) {\n\t\t\t\tt.Errorf(\"ReadU32(%v): expected rem len=%d, got len=%d\", tc.buf, len(tc.buf), len(rem))\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestReadI32ShortSlice(t *testing.T) {\n\tt.Parallel()\n\ttestCases := []struct {\n\t\tname string\n\t\tbuf  []byte\n\t}{\n\t\t{\"empty\", []byte{}},\n\t\t{\"1 byte\", []byte{0x01}},\n\t\t{\"2 bytes\", []byte{0x01, 0x02}},\n\t\t{\"3 bytes\", []byte{0x01, 0x02, 0x03}},\n\t}\n\tfor _, tc := range testCases {\n\t\ttc := tc // capture range variable\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\tt.Parallel()\n\t\t\tval, rem, ok := ReadI32(tc.buf)\n\t\t\tif ok {\n\t\t\t\tt.Errorf(\"ReadI32(%v): expected ok=false, got ok=true\", tc.buf)\n\t\t\t}\n\t\t\tif val != 0 {\n\t\t\t\tt.Errorf(\"ReadI32(%v): expected val=0, got val=%d\", tc.buf, val)\n\t\t\t}\n\t\t\tif len(rem) != len(tc.buf) {\n\t\t\t\tt.Errorf(\"ReadI32(%v): expected rem len=%d, got len=%d\", tc.buf, len(tc.buf), len(rem))\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestReadU64ShortSlice(t *testing.T) {\n\tt.Parallel()\n\ttestCases := []struct {\n\t\tname string\n\t\tbuf  []byte\n\t}{\n\t\t{\"empty\", []byte{}},\n\t\t{\"1 byte\", []byte{0x01}},\n\t\t{\"4 bytes\", []byte{0x01, 0x02, 0x03, 0x04}},\n\t\t{\"7 bytes\", []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}},\n\t}\n\tfor _, tc := range testCases {\n\t\ttc := tc // capture range variable\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\tt.Parallel()\n\t\t\tval, rem, ok := ReadU64(tc.buf)\n\t\t\tif ok {\n\t\t\t\tt.Errorf(\"ReadU64(%v): expected ok=false, got ok=true\", tc.buf)\n\t\t\t}\n\t\t\tif val != 0 {\n\t\t\t\tt.Errorf(\"ReadU64(%v): expected val=0, got val=%d\", tc.buf, val)\n\t\t\t}\n\t\t\tif len(rem) != len(tc.buf) {\n\t\t\t\tt.Errorf(\"ReadU64(%v): expected rem len=%d, got len=%d\", tc.buf, len(tc.buf), len(rem))\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestReadI64ShortSlice(t *testing.T) {\n\tt.Parallel()\n\ttestCases := []struct {\n\t\tname string\n\t\tbuf  []byte\n\t}{\n\t\t{\"empty\", []byte{}},\n\t\t{\"1 byte\", []byte{0x01}},\n\t\t{\"4 bytes\", []byte{0x01, 0x02, 0x03, 0x04}},\n\t\t{\"7 bytes\", []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}},\n\t}\n\tfor _, tc := range testCases {\n\t\ttc := tc // capture range variable\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\tt.Parallel()\n\t\t\tval, rem, ok := ReadI64(tc.buf)\n\t\t\tif ok {\n\t\t\t\tt.Errorf(\"ReadI64(%v): expected ok=false, got ok=true\", tc.buf)\n\t\t\t}\n\t\t\tif val != 0 {\n\t\t\t\tt.Errorf(\"ReadI64(%v): expected val=0, got val=%d\", tc.buf, val)\n\t\t\t}\n\t\t\tif len(rem) != len(tc.buf) {\n\t\t\t\tt.Errorf(\"ReadI64(%v): expected rem len=%d, got len=%d\", tc.buf, len(tc.buf), len(rem))\n\t\t\t}\n\t\t})\n\t}\n}\n\n// -----------------------------------------------------------------------------\n// Unit Tests - Remaining Slice Handling\n// -----------------------------------------------------------------------------\n\nfunc TestReadU32ReturnsRemaining(t *testing.T) {\n\tt.Parallel()\n\tbuf := []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}\n\t_, rem, ok := ReadU32(buf)\n\tif !ok {\n\t\tt.Fatal(\"ReadU32 failed\")\n\t}\n\tif len(rem) != 4 {\n\t\tt.Errorf(\"ReadU32: remaining len = %d, want 4\", len(rem))\n\t}\n\t// Verify remaining bytes are correct.\n\twant := []byte{0x05, 0x06, 0x07, 0x08}\n\tif !bytes.Equal(rem, want) {\n\t\tt.Errorf(\"ReadU32: remaining = %v, want %v\", rem, want)\n\t}\n}\n\nfunc TestReadU64ReturnsRemaining(t *testing.T) {\n\tt.Parallel()\n\tbuf := []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a}\n\t_, rem, ok := ReadU64(buf)\n\tif !ok {\n\t\tt.Fatal(\"ReadU64 failed\")\n\t}\n\tif len(rem) != 2 {\n\t\tt.Errorf(\"ReadU64: remaining len = %d, want 2\", len(rem))\n\t}\n\twant := []byte{0x09, 0x0a}\n\tif !bytes.Equal(rem, want) {\n\t\tt.Errorf(\"ReadU64: remaining = %v, want %v\", rem, want)\n\t}\n}\n\n// -----------------------------------------------------------------------------\n// Unit Tests - CString Functions\n// -----------------------------------------------------------------------------\n\nfunc TestReadCString(t *testing.T) {\n\tt.Parallel()\n\ttestCases := []struct {\n\t\tname    string\n\t\tinput   []byte\n\t\twantStr string\n\t\twantRem []byte\n\t\twantOK  bool\n\t}{\n\t\t{\n\t\t\tname:    \"simple string\",\n\t\t\tinput:   []byte(\"hello\\x00world\"),\n\t\t\twantStr: \"hello\",\n\t\t\twantRem: []byte(\"world\"),\n\t\t\twantOK:  true,\n\t\t},\n\t\t{\n\t\t\tname:    \"empty string\",\n\t\t\tinput:   []byte(\"\\x00remaining\"),\n\t\t\twantStr: \"\",\n\t\t\twantRem: []byte(\"remaining\"),\n\t\t\twantOK:  true,\n\t\t},\n\t\t{\n\t\t\tname:    \"no null terminator\",\n\t\t\tinput:   []byte(\"hello world\"),\n\t\t\twantStr: \"\",\n\t\t\twantRem: []byte(\"hello world\"),\n\t\t\twantOK:  false,\n\t\t},\n\t\t{\n\t\t\tname:    \"empty input\",\n\t\t\tinput:   []byte{},\n\t\t\twantStr: \"\",\n\t\t\twantRem: []byte{},\n\t\t\twantOK:  false,\n\t\t},\n\t\t{\n\t\t\tname:    \"only null byte\",\n\t\t\tinput:   []byte{0x00},\n\t\t\twantStr: \"\",\n\t\t\twantRem: []byte{},\n\t\t\twantOK:  true,\n\t\t},\n\t\t{\n\t\t\tname:    \"null at end\",\n\t\t\tinput:   []byte(\"test\\x00\"),\n\t\t\twantStr: \"test\",\n\t\t\twantRem: []byte{},\n\t\t\twantOK:  true,\n\t\t},\n\t}\n\tfor _, tc := range testCases {\n\t\ttc := tc // capture range variable\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\tt.Parallel()\n\t\t\tgotStr, gotRem, gotOK := ReadCString(tc.input)\n\t\t\tif gotOK != tc.wantOK {\n\t\t\t\tt.Errorf(\"ReadCString(%q): ok = %v, want %v\", tc.input, gotOK, tc.wantOK)\n\t\t\t}\n\t\t\tif gotStr != tc.wantStr {\n\t\t\t\tt.Errorf(\"ReadCString(%q): str = %q, want %q\", tc.input, gotStr, tc.wantStr)\n\t\t\t}\n\t\t\tif string(gotRem) != string(tc.wantRem) {\n\t\t\t\tt.Errorf(\"ReadCString(%q): rem = %q, want %q\", tc.input, gotRem, tc.wantRem)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestReadCStringBytes(t *testing.T) {\n\tt.Parallel()\n\ttestCases := []struct {\n\t\tname      string\n\t\tinput     []byte\n\t\twantBytes []byte\n\t\twantRem   []byte\n\t\twantOK    bool\n\t}{\n\t\t{\n\t\t\tname:      \"simple string\",\n\t\t\tinput:     []byte(\"hello\\x00world\"),\n\t\t\twantBytes: []byte(\"hello\"),\n\t\t\twantRem:   []byte(\"world\"),\n\t\t\twantOK:    true,\n\t\t},\n\t\t{\n\t\t\tname:      \"empty string\",\n\t\t\tinput:     []byte(\"\\x00remaining\"),\n\t\t\twantBytes: []byte{},\n\t\t\twantRem:   []byte(\"remaining\"),\n\t\t\twantOK:    true,\n\t\t},\n\t\t{\n\t\t\tname:      \"no null terminator\",\n\t\t\tinput:     []byte(\"hello world\"),\n\t\t\twantBytes: nil,\n\t\t\twantRem:   []byte(\"hello world\"),\n\t\t\twantOK:    false,\n\t\t},\n\t\t{\n\t\t\tname:      \"empty input\",\n\t\t\tinput:     []byte{},\n\t\t\twantBytes: nil,\n\t\t\twantRem:   []byte{},\n\t\t\twantOK:    false,\n\t\t},\n\t\t{\n\t\t\tname:      \"binary data with null\",\n\t\t\tinput:     []byte{0xff, 0xfe, 0x00, 0x01, 0x02},\n\t\t\twantBytes: []byte{0xff, 0xfe},\n\t\t\twantRem:   []byte{0x01, 0x02},\n\t\t\twantOK:    true,\n\t\t},\n\t}\n\tfor _, tc := range testCases {\n\t\ttc := tc // capture range variable\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\tt.Parallel()\n\t\t\tgotBytes, gotRem, gotOK := ReadCStringBytes(tc.input)\n\t\t\tif gotOK != tc.wantOK {\n\t\t\t\tt.Errorf(\"ReadCStringBytes(%v): ok = %v, want %v\", tc.input, gotOK, tc.wantOK)\n\t\t\t}\n\t\t\tif string(gotBytes) != string(tc.wantBytes) {\n\t\t\t\tt.Errorf(\"ReadCStringBytes(%v): bytes = %v, want %v\", tc.input, gotBytes, tc.wantBytes)\n\t\t\t}\n\t\t\tif string(gotRem) != string(tc.wantRem) {\n\t\t\t\tt.Errorf(\"ReadCStringBytes(%v): rem = %v, want %v\", tc.input, gotRem, tc.wantRem)\n\t\t\t}\n\t\t})\n\t}\n}\n\n// -----------------------------------------------------------------------------\n// Unit Tests - Append to Existing Buffer\n// -----------------------------------------------------------------------------\n\nfunc TestAppend32ToExistingBuffer(t *testing.T) {\n\tt.Parallel()\n\tprefix := []byte{0xaa, 0xbb, 0xcc}\n\tbuf := Append32(prefix, uint32(0x12345678))\n\tif len(buf) != 7 {\n\t\tt.Errorf(\"Append32 to prefix: len = %d, want 7\", len(buf))\n\t}\n\t// Verify prefix is unchanged.\n\tif buf[0] != 0xaa || buf[1] != 0xbb || buf[2] != 0xcc {\n\t\tt.Errorf(\"Append32: prefix corrupted: %v\", buf[:3])\n\t}\n\t// Read back the value.\n\tval, _, ok := ReadU32(buf[3:])\n\tif !ok {\n\t\tt.Fatal(\"ReadU32 failed after Append32\")\n\t}\n\tif val != 0x12345678 {\n\t\tt.Errorf(\"ReadU32 after Append32: got %#x, want 0x12345678\", val)\n\t}\n}\n\nfunc TestAppend64ToExistingBuffer(t *testing.T) {\n\tt.Parallel()\n\tprefix := []byte{0xaa, 0xbb, 0xcc}\n\tbuf := Append64(prefix, uint64(0x123456789abcdef0))\n\tif len(buf) != 11 {\n\t\tt.Errorf(\"Append64 to prefix: len = %d, want 11\", len(buf))\n\t}\n\t// Verify prefix is unchanged.\n\tif buf[0] != 0xaa || buf[1] != 0xbb || buf[2] != 0xcc {\n\t\tt.Errorf(\"Append64: prefix corrupted: %v\", buf[:3])\n\t}\n\t// Read back the value.\n\tval, _, ok := ReadU64(buf[3:])\n\tif !ok {\n\t\tt.Fatal(\"ReadU64 failed after Append64\")\n\t}\n\tif val != 0x123456789abcdef0 {\n\t\tt.Errorf(\"ReadU64 after Append64: got %#x, want 0x123456789abcdef0\", val)\n\t}\n}\n\n// -----------------------------------------------------------------------------\n// Unit Tests - Specific Byte Patterns (Little-Endian Verification)\n// -----------------------------------------------------------------------------\n\nfunc TestAppend32ByteOrder(t *testing.T) {\n\tt.Parallel()\n\t// 0x04030201 in little-endian should be [0x01, 0x02, 0x03, 0x04].\n\tbuf := Append32(nil, uint32(0x04030201))\n\twant := []byte{0x01, 0x02, 0x03, 0x04}\n\tif !bytes.Equal(buf, want) {\n\t\tt.Errorf(\"Append32: got %v, want %v\", buf, want)\n\t}\n}\n\nfunc TestAppend64ByteOrder(t *testing.T) {\n\tt.Parallel()\n\t// 0x0807060504030201 in little-endian should be [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08].\n\tbuf := Append64(nil, uint64(0x0807060504030201))\n\twant := []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}\n\tif !bytes.Equal(buf, want) {\n\t\tt.Errorf(\"Append64: got %v, want %v\", buf, want)\n\t}\n}\n\nfunc TestReadU32ByteOrder(t *testing.T) {\n\tt.Parallel()\n\t// [0x01, 0x02, 0x03, 0x04] in little-endian should be 0x04030201.\n\tbuf := []byte{0x01, 0x02, 0x03, 0x04}\n\tval, _, ok := ReadU32(buf)\n\tif !ok {\n\t\tt.Fatal(\"ReadU32 failed\")\n\t}\n\tif val != 0x04030201 {\n\t\tt.Errorf(\"ReadU32: got %#x, want 0x04030201\", val)\n\t}\n}\n\nfunc TestReadU64ByteOrder(t *testing.T) {\n\tt.Parallel()\n\t// [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08] in little-endian should be 0x0807060504030201.\n\tbuf := []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}\n\tval, _, ok := ReadU64(buf)\n\tif !ok {\n\t\tt.Fatal(\"ReadU64 failed\")\n\t}\n\tif val != 0x0807060504030201 {\n\t\tt.Errorf(\"ReadU64: got %#x, want 0x0807060504030201\", val)\n\t}\n}\n\n// -----------------------------------------------------------------------------\n// Unit Tests - Multiple Sequential Reads\n// -----------------------------------------------------------------------------\n\nfunc TestMultipleSequentialReads(t *testing.T) {\n\tt.Parallel()\n\t// Build a buffer with multiple values.\n\tvar buf []byte\n\tbuf = Append32(buf, uint32(0x11111111))\n\tbuf = Append64(buf, uint64(0x2222222222222222))\n\tbuf = Append32(buf, uint32(0x33333333))\n\tbuf = append(buf, []byte(\"test\\x00\")...)\n\n\t// Read them back sequentially.\n\tvar ok bool\n\tvar v32 uint32\n\tvar v64 uint64\n\tvar str string\n\n\tv32, buf, ok = ReadU32(buf)\n\tif !ok || v32 != 0x11111111 {\n\t\tt.Errorf(\"First ReadU32: got %#x, ok=%v\", v32, ok)\n\t}\n\n\tv64, buf, ok = ReadU64(buf)\n\tif !ok || v64 != 0x2222222222222222 {\n\t\tt.Errorf(\"ReadU64: got %#x, ok=%v\", v64, ok)\n\t}\n\n\tv32, buf, ok = ReadU32(buf)\n\tif !ok || v32 != 0x33333333 {\n\t\tt.Errorf(\"Second ReadU32: got %#x, ok=%v\", v32, ok)\n\t}\n\n\tstr, buf, ok = ReadCString(buf)\n\tif !ok || str != \"test\" {\n\t\tt.Errorf(\"ReadCString: got %q, ok=%v\", str, ok)\n\t}\n\n\tif len(buf) != 0 {\n\t\tt.Errorf(\"Buffer not empty after all reads: %d bytes remaining\", len(buf))\n\t}\n}\n"
  },
  {
    "path": "internal/binaryutil/doc.go",
    "content": "// Copyright (C) MongoDB, Inc. 2026-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\n// Package binaryutil provides functions for reading binary primitives from\n// byte slices. It is used internally for BSON parsing and wire protocol\n// operations.\n//\n// The functions in this package are designed for use in BSON operations.\n// Signed integer functions (ReadI32, ReadI64) use manual bit-shifting rather\n// than encoding/binary to avoid unsafe signed/unsigned conversions and comply\n// with gosec G115. Bounds-check elimination (BCE) hints help the compiler\n// inline these functions.\n//\n// Benchmarking across different ARM64 architectures (Apple M-series)\n// revealed non-deterministic performance discrepancies between using the\n// \"encoding/binary\" standard library and manual bit-shifting (\"straight-lining\").\n//\n// Without Loss of Generality (WLOG), benchmarking observed that:\n//   - On Apple M1 Pro: Standard library (ReadU32) outperformed manual\n//     bit-shifting (ReadI32) by ~2x (~0.08ns vs ~0.16ns).\n//   - On Apple M4 Max: Manual bit-shifting (ReadI32) outperformed the\n//     standard library (ReadU32) by ~1.6x (~0.03ns vs ~0.05ns).\n//\n// Further testing showed that \"straight-lining\" the ReadU32 implementation\n// to match ReadI32 normalized performance to ~0.03ns on the M4 Max, even\n// though the generated assembly for both approaches is virtually equivalent.\n//\n// The generated assembly is nearly identical for both approaches. These\n// sub-nanosecond variations likely stem from microarchitecture differences\n// (instruction caching, branch prediction) rather than the code itself.\n//\n// Since network I/O dominates driver latency, these differences do not have a\n// significant impact on driver performance. The implementation favors security\n// compliance and readability over hardware-specific tuning.\npackage binaryutil\n"
  },
  {
    "path": "internal/bsoncoreutil/bsoncoreutil.go",
    "content": "// Copyright (C) MongoDB, Inc. 2024-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bsoncoreutil\n\n// Truncate truncates a given string for a certain width\nfunc Truncate(str string, width int) string {\n\tif width <= 0 {\n\t\treturn \"\"\n\t}\n\n\tif len(str) <= width {\n\t\treturn str\n\t}\n\n\t// Truncate the byte slice of the string to the given width.\n\tnewStr := str[:width]\n\n\t// Check if the last byte is at the beginning of a multi-byte character.\n\t// If it is, then remove the last byte.\n\tif newStr[len(newStr)-1]&0xC0 == 0xC0 {\n\t\treturn newStr[:len(newStr)-1]\n\t}\n\n\t// Check if the last byte is a multi-byte character\n\tif newStr[len(newStr)-1]&0xC0 == 0x80 {\n\t\t// If it is, step back until you we are at the start of a character\n\t\tfor i := len(newStr) - 1; i >= 0; i-- {\n\t\t\tif newStr[i]&0xC0 == 0xC0 {\n\t\t\t\t// Truncate at the end of the character before the character we stepped back to\n\t\t\t\treturn newStr[:i]\n\t\t\t}\n\t\t}\n\t}\n\n\treturn newStr\n}\n"
  },
  {
    "path": "internal/bsoncoreutil/bsoncoreutil_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2024-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bsoncoreutil\n\nimport (\n\t\"testing\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n)\n\nfunc TestTruncate(t *testing.T) {\n\tt.Parallel()\n\n\tfor _, tcase := range []struct {\n\t\tname     string\n\t\targ      string\n\t\twidth    int\n\t\texpected string\n\t}{\n\t\t{\n\t\t\tname:     \"empty\",\n\t\t\targ:      \"\",\n\t\t\twidth:    0,\n\t\t\texpected: \"\",\n\t\t},\n\t\t{\n\t\t\tname:     \"short\",\n\t\t\targ:      \"foo\",\n\t\t\twidth:    1000,\n\t\t\texpected: \"foo\",\n\t\t},\n\t\t{\n\t\t\tname:     \"long\",\n\t\t\targ:      \"foo bar baz\",\n\t\t\twidth:    9,\n\t\t\texpected: \"foo bar b\",\n\t\t},\n\t\t{\n\t\t\tname:     \"multi-byte\",\n\t\t\targ:      \"你好\",\n\t\t\twidth:    4,\n\t\t\texpected: \"你\",\n\t\t},\n\t} {\n\t\ttcase := tcase\n\n\t\tt.Run(tcase.name, func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tactual := Truncate(tcase.arg, tcase.width)\n\t\t\tassert.Equal(t, tcase.expected, actual)\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "internal/bsonutil/bsonutil.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bsonutil\n\nimport (\n\t\"fmt\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n)\n\n// StringSliceFromRawValue decodes the provided BSON value into a []string. This function returns an error if the value\n// is not an array or any of the elements in the array are not strings. The name parameter is used to add context to\n// error messages.\nfunc StringSliceFromRawValue(name string, val bson.RawValue) ([]string, error) {\n\tarr, ok := val.ArrayOK()\n\tif !ok {\n\t\treturn nil, fmt.Errorf(\"expected '%s' to be an array but it's a BSON %s\", name, val.Type)\n\t}\n\n\tarrayValues, err := arr.Values()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tstrs := make([]string, 0, len(arrayValues))\n\tfor _, arrayVal := range arrayValues {\n\t\tstr, ok := arrayVal.StringValueOK()\n\t\tif !ok {\n\t\t\treturn nil, fmt.Errorf(\"expected '%s' to be an array of strings, but found a BSON %s\", name, arrayVal.Type)\n\t\t}\n\t\tstrs = append(strs, str)\n\t}\n\treturn strs, nil\n}\n\n// RawArrayToDocuments converts an array of documents to []bson.Raw.\nfunc RawArrayToDocuments(arr bson.RawArray) []bson.Raw {\n\tvalues, err := arr.Values()\n\tif err != nil {\n\t\tpanic(fmt.Sprintf(\"error converting BSON document to values: %v\", err))\n\t}\n\n\tout := make([]bson.Raw, len(values))\n\tfor i := range values {\n\t\tout[i] = values[i].Document()\n\t}\n\n\treturn out\n}\n\n// RawToInterfaces takes one or many bson.Raw documents and returns them as a []any.\nfunc RawToInterfaces(docs ...bson.Raw) []any {\n\tout := make([]any, len(docs))\n\tfor i := range docs {\n\t\tout[i] = docs[i]\n\t}\n\treturn out\n}\n"
  },
  {
    "path": "internal/cmd/benchmark/benchmark_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2024-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage main\n\nimport (\n\t\"archive/tar\"\n\t\"compress/gzip\"\n\t\"context\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"flag\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"sort\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/require\"\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo\"\n)\n\nconst (\n\tdefaultOutputFileName = \"perf.json\"\n\tlegacyHelloLowercase  = \"ismaster\"\n\ttarFile               = \"perf.tar.gz\"\n\tperfDir               = \"perf\"\n\ttestdataURL           = \"https://s3.amazonaws.com/boxes.10gen.com/build/driver-test-data.tar.gz\"\n\tperftestDB            = \"perftest\"\n\tcorpusColl            = \"corpus\"\n\tbsonDataDir           = \"extended_bson\"\n\n\t// Bson test data\n\tflatBSONData = \"flat_bson.json\"\n\tdeepBSONData = \"deep_bson.json\"\n\tfullBSONData = \"full_bson.json\"\n\n\t// Single test data\n\tsingleAndMultiDataDir = \"single_and_multi_document\"\n\ttweetData             = \"tweet.json\"\n\tsmallData             = \"small_doc.json\"\n\tlargeData             = \"large_doc.json\"\n\n\t// Reusable metric names\n\topsPerSecondMaxName = \"ops_per_second_max\"\n\topsPerSecondMinName = \"ops_per_second_min\"\n\topsPerSecondMedName = \"ops_per_second_med\"\n)\n\n// fullRun will run all of the tests. This is default to false, since it's\n// unlikely a user would need to run all tests locally. We still need to run\n// the test up to the point where the performance data is downloaded.\nvar fullRun bool\n\nfunc init() {\n\tflag.BoolVar(&fullRun, \"fullRun\", false, \"run all benchmarks in TestRunAllBenchmarks\")\n}\n\ntype metrics struct {\n\topsPerSecond []float64\n}\n\nfunc recordMetrics(b *testing.B, throughput *metrics, fn func(*testing.B)) {\n\tb.Helper()\n\n\tstart := time.Now()\n\n\tfn(b)\n\n\tduration := time.Since(start)\n\tthroughput.opsPerSecond = append(throughput.opsPerSecond, 1/duration.Seconds())\n}\n\n// calculate min, max, and median operations per second and report the metric\n// on the benchmark object.\nfunc reportThroughputStats(b *testing.B, times []float64) {\n\tsort.Float64s(times)\n\n\tb.ReportMetric(times[0], opsPerSecondMinName)\n\tb.ReportMetric(times[len(times)-1], opsPerSecondMaxName)\n\n\tvar median float64\n\tif len(times)%2 == 0 {\n\t\tmedian = (times[len(times)/2-1] + times[len(times)/2]) / 2\n\t} else {\n\t\tmedian = times[len(times)/2]\n\t}\n\n\tb.ReportMetric(median, opsPerSecondMedName)\n}\n\nfunc reportMetrics(b *testing.B, metrics *metrics) {\n\tb.Helper()\n\n\treportThroughputStats(b, metrics.opsPerSecond)\n}\n\n// find the testdata directory. We do this instead of hardcoding a relative path\n// (i.e. ../../../testdata) so that these benchmarks can be run from both the\n// root as a taskfile as well as from the benchmark directory.\nfunc testdataDir(tb testing.TB) string {\n\ttb.Helper()\n\n\twd, err := os.Getwd()\n\trequire.NoError(tb, err, \"failed to source working directory\")\n\n\tfor {\n\t\ttdPath := filepath.Join(wd, \"testdata\")\n\t\tif _, err := os.Stat(tdPath); !os.IsNotExist(err) {\n\t\t\treturn tdPath\n\t\t}\n\n\t\twd = filepath.Dir(wd)\n\t\tif filepath.Base(wd) == \"mongodb-go-driver\" {\n\t\t\ttb.Fatal(\"'testdata' directory not found\")\n\t\t}\n\t}\n}\n\n// where to download the tarball\nfunc testdataTarFileName(tb testing.TB) string {\n\treturn filepath.Join(testdataDir(tb), tarFile)\n}\n\n// where to extract the tarball\nfunc testdataPerfDir(tb testing.TB) string {\n\treturn filepath.Join(testdataDir(tb), perfDir)\n}\n\n// download the tarball of test data to testdata/perf\nfunc downloadTestDataTgz(t *testing.T) {\n\tresp, err := http.Get(testdataURL)\n\trequire.NoError(t, err, \"failed to get response from %q\", testdataURL)\n\n\tdefer resp.Body.Close()\n\n\tout, err := os.Create(testdataTarFileName(t))\n\trequire.NoError(t, err, \"failed to open testdata perf dir\")\n\n\tdefer out.Close()\n\n\t_, err = io.Copy(out, resp.Body)\n\trequire.NoError(t, err, \"failed to copy response body to testdata perf dir\")\n}\n\n// extract the tarball to the perf dir.\nfunc extractTestDataTgz(t *testing.T) {\n\ttarPath := testdataTarFileName(t)\n\tdefer func() { _ = os.Remove(tarPath) }()\n\n\tfile, err := os.Open(tarPath)\n\trequire.NoError(t, err, \"failed to open tar file\")\n\n\tdefer file.Close()\n\n\tgzipReader, err := gzip.NewReader(file)\n\trequire.NoError(t, err, \"failed to create a gzip reader\")\n\n\tdefer gzipReader.Close()\n\n\ttarReader := tar.NewReader(gzipReader)\n\tfor {\n\t\theader, err := tarReader.Next()\n\t\tif errors.Is(err, io.EOF) {\n\t\t\tbreak\n\t\t}\n\n\t\trequire.NoError(t, err, \"failed to advance tar entry\")\n\n\t\ttargetPath := filepath.Join(testdataPerfDir(t), strings.TrimPrefix(header.Name, \"data/\"))\n\n\t\tswitch header.Typeflag {\n\t\tcase tar.TypeDir:\n\t\t\terr := os.MkdirAll(targetPath, 0o755)\n\t\t\trequire.NoError(t, err, \"failed to extract dir from tgz\")\n\t\tcase tar.TypeReg:\n\t\t\toutFile, err := os.Create(targetPath)\n\t\t\trequire.NoError(t, err, \"failed to create path to extract file from tgz\")\n\n\t\t\t_, err = io.Copy(outFile, tarReader)\n\t\t\trequire.NoError(t, err, \"failed to extract file from tgz\")\n\n\t\t\toutFile.Close()\n\t\t}\n\t}\n}\n\nfunc loadSourceDocument(b *testing.B, canonicalOnly bool, pathParts ...string) bson.D {\n\tb.Helper()\n\n\tdata, err := os.ReadFile(filepath.Join(pathParts...))\n\trequire.NoError(b, err, \"failed to load source document\")\n\n\tvar doc bson.D\n\n\terr = bson.UnmarshalExtJSON(data, canonicalOnly, &doc)\n\trequire.NoError(b, err, \"failed to unmarshal source document\")\n\n\trequire.NotEmpty(b, doc)\n\n\treturn doc\n}\n\nfunc benchmarkBSONEncoding(b *testing.B, canonicalOnly bool, source string) {\n\tdoc := loadSourceDocument(b, canonicalOnly, testdataPerfDir(b), bsonDataDir, source)\n\n\tb.ResetTimer()\n\n\tmetrics := new(metrics)\n\n\tfor i := 0; i < b.N; i++ {\n\t\trecordMetrics(b, metrics, func(b *testing.B) {\n\t\t\tout, err := bson.Marshal(doc)\n\t\t\trequire.NoError(b, err, \"failed to encode flat bson data\")\n\n\t\t\trequire.NotEmpty(b, out)\n\t\t})\n\t}\n\n\treportMetrics(b, metrics)\n}\n\nfunc benchmarkBSONDecoding(b *testing.B, canonicalOnly bool, source string) {\n\tdoc := loadSourceDocument(b, canonicalOnly, testdataPerfDir(b), bsonDataDir, source)\n\n\traw, err := bson.Marshal(doc)\n\trequire.NoError(b, err, \"failed to encode bson data\")\n\n\tb.ResetTimer()\n\n\tmetrics := new(metrics)\n\n\tfor i := 0; i < b.N; i++ {\n\t\trecordMetrics(b, metrics, func(b *testing.B) {\n\t\t\tvar out bson.D\n\n\t\t\terr := bson.Unmarshal(raw, &out)\n\t\t\trequire.NoError(b, err, \"failed to encode flat bson data\")\n\t\t})\n\t}\n\n\treportMetrics(b, metrics)\n}\n\n// Test driver performance encoding documents with top level key/value pairs\n// involving the most commonly-used BSON types.\nfunc BenchmarkBSONFlatDocumentEncoding(b *testing.B) {\n\tbenchmarkBSONEncoding(b, true, flatBSONData)\n}\n\n// Test driver performance decoding documents with top level key/value pairs\n// involving the most commonly-used BSON types.\nfunc BenchmarkBSONFlatDocumentDecoding(b *testing.B) {\n\tbenchmarkBSONDecoding(b, true, flatBSONData)\n}\n\n// Test driver performance encoding documents with deeply nested key/value pairs\n// involving subdocuments, strings, integers, doubles and booleans.\nfunc BenchmarkBSONDeepDocumentEncoding(b *testing.B) {\n\tbenchmarkBSONEncoding(b, true, deepBSONData)\n}\n\n// Test driver performance decoding documents with deeply nested key/value pairs\n// involving subdocuments, strings, integers, doubles and booleans.\nfunc BenchmarkBSONDeepDocumentDecoding(b *testing.B) {\n\tbenchmarkBSONDecoding(b, true, deepBSONData)\n}\n\n// Test driver performance encoding documents with top level key/value pairs\n// involving the full range of BSON types.\nfunc BenchmarkBSONFullDocumentEncoding(b *testing.B) {\n\tbenchmarkBSONEncoding(b, false, fullBSONData)\n}\n\n// Test driver performance decoding documents with top level key/value pairs\n// involving the full range of BSON types.\nfunc BenchmarkBSONFullDocumentDecoding(b *testing.B) {\n\tbenchmarkBSONDecoding(b, false, fullBSONData)\n}\n\n// Test driver performance sending a command to the database and reading a\n// response.\nfunc BenchmarkSingleRunCommand(b *testing.B) {\n\tcoll, teardown := setupBench(b)\n\tdefer teardown(b)\n\n\tcmd := bson.D{{Key: legacyHelloLowercase, Value: true}}\n\n\tmetrics := new(metrics)\n\n\tb.ResetTimer()\n\n\tfor i := 0; i < b.N; i++ {\n\t\trecordMetrics(b, metrics, func(b *testing.B) {\n\t\t\tb.Helper()\n\n\t\t\tvar doc bson.D\n\t\t\terr := coll.Database().RunCommand(context.Background(), cmd).Decode(&doc)\n\t\t\trequire.NoError(b, err)\n\n\t\t\t// read the document and then throw it away to prevent\n\t\t\tout, err := bson.Marshal(doc)\n\t\t\trequire.NoError(b, err)\n\n\t\t\trequire.NotEmpty(b, out)\n\t\t})\n\t}\n\n\treportMetrics(b, metrics)\n}\n\nfunc setupBench(b *testing.B) (*mongo.Collection, func(b *testing.B)) {\n\tb.Helper()\n\n\tclient, err := mongo.Connect()\n\trequire.NoError(b, err, \"failed to connect to server\")\n\n\tctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)\n\n\terr = client.Ping(ctx, nil)\n\trequire.NoError(b, err, \"failed to ping the server\")\n\n\tctx, cancel = context.WithCancel(context.Background())\n\tdefer cancel()\n\n\tdb := client.Database(perftestDB)\n\n\terr = db.Drop(ctx)\n\trequire.NoError(b, err, \"failed to drop %q database\", perftestDB)\n\n\terr = db.Collection(corpusColl).Drop(ctx)\n\trequire.NoError(b, err, \"failed to drop %q\", corpusColl)\n\n\terr = db.CreateCollection(ctx, corpusColl)\n\trequire.NoError(b, err, \"failed to create %q collection\", corpusColl)\n\n\tcoll := db.Collection(corpusColl)\n\n\treturn coll, func(b *testing.B) {\n\t\terr := client.Disconnect(context.Background())\n\t\trequire.NoError(b, err, \"failed to disconnect client\")\n\t}\n}\n\n// Test driver performance sending an indexed query to the database and reading\n// a single document in response.\nfunc BenchmarkSingleFindOneByID(b *testing.B) {\n\tcoll, teardown := setupBench(b)\n\tdefer teardown(b)\n\n\tdoc := loadSourceDocument(b, true, testdataPerfDir(b), singleAndMultiDataDir, tweetData)\n\n\t// Insert 10_000 documents into the corpus.\n\tconst docCount = 10_000\n\n\tdocsToInsert := make([]bson.D, docCount)\n\tfor i := range docsToInsert {\n\t\tdocsToInsert[i] = make(bson.D, 0, len(doc)+1)\n\t\tdocsToInsert[i] = append(docsToInsert[i], bson.E{Key: \"_id\", Value: i})\n\t\tdocsToInsert[i] = append(docsToInsert[i], doc...)\n\t}\n\n\t_, err := coll.InsertMany(context.Background(), docsToInsert)\n\trequire.NoError(b, err, \"failed to insert corpus\")\n\n\tmetrics := new(metrics)\n\n\tb.ResetTimer()\n\n\tfor i := 0; i < b.N; i++ {\n\t\trecordMetrics(b, metrics, func(b *testing.B) {\n\t\t\terr := coll.FindOne(context.Background(), bson.D{{Key: \"_id\", Value: i % docCount}}).Decode(&bson.D{})\n\t\t\trequire.NoError(b, err, \"failed to find one\")\n\t\t})\n\t}\n\n\treportMetrics(b, metrics)\n}\n\nfunc benchmarkSingleInsert(b *testing.B, source string) {\n\tb.Helper()\n\n\tcoll, teardown := setupBench(b)\n\tdefer teardown(b)\n\n\tdoc := loadSourceDocument(b, true, testdataPerfDir(b), singleAndMultiDataDir, smallData)\n\n\tmetrics := new(metrics)\n\n\tfor i := 0; i < b.N; i++ {\n\t\trecordMetrics(b, metrics, func(b *testing.B) {\n\t\t\tb.Helper()\n\t\t\t_, err := coll.InsertOne(context.Background(), doc)\n\t\t\trequire.NoError(b, err, \"failed to insert small doc\")\n\t\t})\n\t}\n\n\treportMetrics(b, metrics)\n}\n\n// Test driver performance inserting a single, small document to the database.\nfunc BenchmarkSmallDocInsertOne(b *testing.B) {\n\tbenchmarkSingleInsert(b, smallData)\n}\n\n// Test driver performance inserting a single, large document to the database.\nfunc BenchmarkLargeDocInsertOne(b *testing.B) {\n\tbenchmarkSingleInsert(b, largeData)\n}\n\n// Test driver performance retrieving multiple documents from a query.\nfunc BenchmarkMultiFindMany(b *testing.B) {\n\tcoll, teardown := setupBench(b)\n\tdefer teardown(b)\n\n\tdoc := loadSourceDocument(b, true, testdataPerfDir(b), singleAndMultiDataDir, tweetData)\n\n\tdocsToInsert := make([]bson.D, b.N)\n\tfor i := range docsToInsert {\n\t\tdocsToInsert[i] = doc\n\t}\n\n\t_, err := coll.InsertMany(context.Background(), docsToInsert)\n\trequire.NoError(b, err, \"failed to insert docs\")\n\n\tb.ResetTimer()\n\n\tcursor, err := coll.Find(context.Background(), bson.D{})\n\trequire.NoError(b, err, \"failed to find data\")\n\n\tdefer cursor.Close(context.Background())\n\n\tmetrics := new(metrics)\n\n\tcounter := 0\n\tfor cursor.Next(context.Background()) {\n\t\trecordMetrics(b, metrics, func(b *testing.B) {\n\t\t\terr = cursor.Err()\n\t\t\trequire.NoError(b, err, \"failed to advance cursor\")\n\n\t\t\tif len(cursor.Current) == 0 {\n\t\t\t\tb.Fatalf(\"error retrieving document\")\n\t\t\t}\n\n\t\t\tcounter++\n\t\t})\n\t}\n\n\tif counter != b.N {\n\t\tb.Fatalf(\"problem iterating cursors\")\n\t}\n\n\terr = cursor.Close(context.Background())\n\trequire.NoError(b, err, \"failed to close cursor\")\n\n\treportMetrics(b, metrics)\n}\n\nfunc benchmarkMultiInsert(b *testing.B, source string) {\n\tb.Helper()\n\n\tcoll, teardown := setupBench(b)\n\tdefer teardown(b)\n\n\tdoc := loadSourceDocument(b, true, testdataPerfDir(b), singleAndMultiDataDir, source)\n\n\tdocsToInsert := make([]bson.D, b.N)\n\tfor i := range docsToInsert {\n\t\tdocsToInsert[i] = doc\n\t}\n\n\tb.ResetTimer()\n\n\tres, err := coll.InsertMany(context.Background(), docsToInsert)\n\trequire.NoError(b, err, \"failed to insert many\")\n\n\trequire.Len(b, res.InsertedIDs, b.N)\n}\n\n// Test driver performance inserting multiple, small documents to the database.\nfunc BenchmarkMultiInsertSmallDocument(b *testing.B) {\n\tbenchmarkMultiInsert(b, smallData)\n}\n\n// Test driver performance inserting multiple, large documents to the database.\nfunc BenchmarkMultiInsertLargeDocument(b *testing.B) {\n\tbenchmarkMultiInsert(b, largeData)\n}\n\nfunc runBenchmark(name string, fn func(*testing.B)) (poplarTest, error) {\n\ttest := poplarTest{\n\t\tID:        fmt.Sprintf(\"%d\", time.Now().UnixMilli()),\n\t\tCreatedAt: time.Now(),\n\t}\n\n\tresult := testing.Benchmark(fn)\n\n\tif result.N == 0 {\n\t\treturn test, fmt.Errorf(\"benchmark failed to run\")\n\t}\n\n\ttest.CompletedAt = test.CreatedAt.Add(result.T)\n\n\ttest.Metrics = []poplarTestMetrics{\n\t\t{Name: \"total_time_seconds\", Type: \"SUM\", Value: result.T.Seconds()},\n\t\t{Name: \"iterations\", Type: \"SUM\", Value: result.N},\n\t\t{Name: \"allocated_bytes_per_op\", Type: \"MEAN\", Value: result.AllocedBytesPerOp()},\n\t\t{Name: \"allocs_per_op\", Type: \"MEAN\", Value: result.AllocsPerOp()},\n\t\t{Name: \"total_mem_allocs\", Type: \"SUM\", Value: result.MemAllocs},\n\t\t{Name: \"total_bytes_allocated\", Type: \"SUM\", Value: result.MemBytes},\n\t\t{Name: \"ns_per_op\", Type: \"MEAN\", Value: result.NsPerOp()},\n\t}\n\n\t// Only tests that set bytes in the benchmark will have this metric.\n\tif result.Bytes != 0 {\n\t\tmegaBytesPerOp := (float64(result.Bytes) / 1024 / 1024) / float64(result.NsPerOp()) * 1e9\n\n\t\ttest.Metrics = append(test.Metrics,\n\t\t\tpoplarTestMetrics{Name: \"megabytes_per_second\", Type: \"THROUGHPUT\", Value: megaBytesPerOp})\n\t}\n\n\tif opsPerSecondMin := result.Extra[opsPerSecondMinName]; opsPerSecondMin != 0 {\n\t\ttest.Metrics = append(test.Metrics,\n\t\t\tpoplarTestMetrics{Name: opsPerSecondMinName, Type: \"THROUGHPUT\", Value: opsPerSecondMin})\n\t}\n\n\tif opsPerSecondMax := result.Extra[opsPerSecondMaxName]; opsPerSecondMax != 0 {\n\t\ttest.Metrics = append(test.Metrics,\n\t\t\tpoplarTestMetrics{Name: opsPerSecondMaxName, Type: \"THROUGHPUT\", Value: opsPerSecondMax})\n\t}\n\n\tif opsPerSecondMed := result.Extra[opsPerSecondMedName]; opsPerSecondMed != 0 {\n\t\ttest.Metrics = append(test.Metrics,\n\t\t\tpoplarTestMetrics{Name: opsPerSecondMedName, Type: \"THROUGHPUT\", Value: opsPerSecondMed})\n\t}\n\n\ttest.Info = poplarTestInfo{\n\t\tTestName: name,\n\t}\n\n\treturn test, nil\n}\n\nfunc TestRunAllBenchmarks(t *testing.T) {\n\tflag.Parse()\n\n\t// Download and extract the data if it doesn't exist.\n\tif _, err := os.Stat(testdataPerfDir(t)); os.IsNotExist(err) {\n\t\tdownloadTestDataTgz(t)\n\t\textractTestDataTgz(t)\n\t}\n\n\t// The test will be run any time a benchmark is run. To avoid running all\n\t// benchmarks in this case, we require a flag that defaults to false. The\n\t// intention is that this test will only ever need to fully run in CI.\n\tif !fullRun {\n\t\treturn\n\t}\n\n\t// Run the cases and accumulate the results.\n\tcases := []struct {\n\t\tname      string\n\t\tbenchmark func(*testing.B)\n\t}{\n\t\t{name: \"BenchmarkBSONFlatDocumentEncoding\", benchmark: BenchmarkBSONFlatDocumentEncoding},\n\t\t{name: \"BenchmarkBSONFlatDocumentDecoding\", benchmark: BenchmarkBSONFlatDocumentDecoding},\n\t\t{name: \"BenchmarkBSONDeepDocumentEncoding\", benchmark: BenchmarkBSONDeepDocumentEncoding},\n\t\t{name: \"BenchmarkBSONDeepDocumentDecoding\", benchmark: BenchmarkBSONDeepDocumentDecoding},\n\t\t{name: \"BenchmarkBSONFullDocumentEncoding\", benchmark: BenchmarkBSONFullDocumentEncoding},\n\t\t{name: \"BenchmarkBSONFullDocumentDecoding\", benchmark: BenchmarkBSONFullDocumentDecoding},\n\t\t{name: \"BenchmarkSingleRunCommand\", benchmark: BenchmarkSingleRunCommand},\n\t\t{name: \"BenchmarkSingleFindOneByID\", benchmark: BenchmarkSingleFindOneByID},\n\t\t{name: \"BenchmarkSmallDocInsertOne\", benchmark: BenchmarkSmallDocInsertOne},\n\t\t{name: \"BenchmarkLargeDocInsertOne\", benchmark: BenchmarkLargeDocInsertOne},\n\t\t{name: \"BenchmarkMultiFindMany\", benchmark: BenchmarkMultiFindMany},\n\t\t{name: \"BenchmarkMultiInsertSmallDocument\", benchmark: BenchmarkMultiInsertSmallDocument},\n\t\t{name: \"BenchmarkMultiInsertLargeDocument\", benchmark: BenchmarkMultiInsertLargeDocument},\n\t}\n\n\tresults := make([]poplarTest, len(cases))\n\tfor i := range cases {\n\t\tt.Run(cases[i].name, func(t *testing.T) {\n\t\t\tvar err error\n\n\t\t\tresults[i], err = runBenchmark(cases[i].name, cases[i].benchmark)\n\t\t\tif err != nil { // Avoid asserting to prevent failures on single-run benchmarks\n\t\t\t\tt.Logf(\"failed to run benchmark: %v\", err)\n\t\t\t}\n\t\t})\n\t}\n\n\t// Write the results to the performance file.\n\tevgOutput, err := json.MarshalIndent(results, \"\", \"   \")\n\trequire.NoError(t, err, \"failed to encode results\")\n\n\tevgOutput = append(evgOutput, []byte(\"\\n\")...)\n\n\t// Ignore gosec warning \"Expect WriteFile permissions to be 0600 or less\" for\n\t// benchmark result file.\n\t/* #nosec G306 */\n\terr = os.WriteFile(filepath.Join(filepath.Dir(testdataDir(t)), defaultOutputFileName), evgOutput, 0o644)\n\trequire.NoError(t, err, \"failed to write results\")\n}\n\n// poplarTest was copied from\n// https://github.com/evergreen-ci/poplar/blob/8d03d2bacde0897cedd73ed79ddc167ed1ed4c77/report.go#L38\ntype poplarTest struct {\n\tID          string               `bson:\"_id\" json:\"id\" yaml:\"id\"`\n\tInfo        poplarTestInfo       `bson:\"info\" json:\"info\" yaml:\"info\"`\n\tCreatedAt   time.Time            `bson:\"created_at\" json:\"created_at\" yaml:\"created_at\"`\n\tCompletedAt time.Time            `bson:\"completed_at\" json:\"completed_at\" yaml:\"completed_at\"`\n\tArtifacts   []poplarTestArtifact `bson:\"artifacts\" json:\"artifacts\" yaml:\"artifacts\"`\n\tMetrics     []poplarTestMetrics  `bson:\"metrics\" json:\"metrics\" yaml:\"metrics\"`\n\tSubTests    []poplarTest         `bson:\"sub_tests\" json:\"sub_tests\" yaml:\"sub_tests\"`\n}\n\n// poplarTestInfo was copied from\n// https://github.com/evergreen-ci/poplar/blob/8d03d2bacde0897cedd73ed79ddc167ed1ed4c77/report.go#L52\ntype poplarTestInfo struct {\n\tTestName  string           `bson:\"test_name\" json:\"test_name\" yaml:\"test_name\"`\n\tTrial     int              `bson:\"trial\" json:\"trial\" yaml:\"trial\"`\n\tParent    string           `bson:\"parent\" json:\"parent\" yaml:\"parent\"`\n\tTags      []string         `bson:\"tags\" json:\"tags\" yaml:\"tags\"`\n\tArguments map[string]int32 `bson:\"args\" json:\"args\" yaml:\"args\"`\n}\n\n// poplarTestArtifact was copied from\n// https://github.com/evergreen-ci/poplar/blob/8d03d2bacde0897cedd73ed79ddc167ed1ed4c77/report.go#L62\ntype poplarTestArtifact struct {\n\tBucket                string    `bson:\"bucket\" json:\"bucket\" yaml:\"bucket\"`\n\tPrefix                string    `bson:\"prefix\" json:\"prefix\" yaml:\"prefix\"`\n\tPermissions           string    `bson:\"permissions\" json:\"permissions\" yaml:\"permissions\"`\n\tPath                  string    `bson:\"path\" json:\"path\" yaml:\"path\"`\n\tTags                  []string  `bson:\"tags\" json:\"tags\" yaml:\"tags\"`\n\tCreatedAt             time.Time `bson:\"created_at\" json:\"created_at\" yaml:\"created_at\"`\n\tLocalFile             string    `bson:\"local_path,omitempty\" json:\"local_path,omitempty\" yaml:\"local_path,omitempty\"`\n\tPayloadTEXT           bool      `bson:\"is_text,omitempty\" json:\"is_text,omitempty\" yaml:\"is_text,omitempty\"`\n\tPayloadFTDC           bool      `bson:\"is_ftdc,omitempty\" json:\"is_ftdc,omitempty\" yaml:\"is_ftdc,omitempty\"`\n\tPayloadBSON           bool      `bson:\"is_bson,omitempty\" json:\"is_bson,omitempty\" yaml:\"is_bson,omitempty\"`\n\tPayloadJSON           bool      `bson:\"is_json,omitempty\" json:\"is_json,omitempty\" yaml:\"is_json,omitempty\"`\n\tPayloadCSV            bool      `bson:\"is_csv,omitempty\" json:\"is_csv,omitempty\" yaml:\"is_csv,omitempty\"`\n\tDataUncompressed      bool      `bson:\"is_uncompressed\" json:\"is_uncompressed\" yaml:\"is_uncompressed\"`\n\tDataGzipped           bool      `bson:\"is_gzip,omitempty\" json:\"is_gzip,omitempty\" yaml:\"is_gzip,omitempty\"`\n\tDataTarball           bool      `bson:\"is_tarball,omitempty\" json:\"is_tarball,omitempty\" yaml:\"is_tarball,omitempty\"`\n\tEventsRaw             bool      `bson:\"events_raw,omitempty\" json:\"events_raw,omitempty\" yaml:\"events_raw,omitempty\"`\n\tEventsHistogram       bool      `bson:\"events_histogram,omitempty\" json:\"events_histogram,omitempty\" yaml:\"events_histogram,omitempty\"`\n\tEventsIntervalSummary bool      `bson:\"events_interval_summary,omitempty\" json:\"events_interval_summary,omitempty\" yaml:\"events_interval_summary,omitempty\"`\n\tEventsCollapsed       bool      `bson:\"events_collapsed,omitempty\" json:\"events_collapsed,omitempty\" yaml:\"events_collapsed,omitempty\"`\n\tConvertGzip           bool      `bson:\"convert_gzip,omitempty\" json:\"convert_gzip,omitempty\" yaml:\"convert_gzip,omitempty\"`\n\tConvertBSON2FTDC      bool      `bson:\"convert_bson_to_ftdc,omitempty\" json:\"convert_bson_to_ftdc,omitempty\" yaml:\"convert_bson_to_ftdc,omitempty\"`\n\tConvertJSON2FTDC      bool      `bson:\"convert_json_to_ftdc\" json:\"convert_json_to_ftdc\" yaml:\"convert_json_to_ftdc\"`\n\tConvertCSV2FTDC       bool      `bson:\"convert_csv_to_ftdc\" json:\"convert_csv_to_ftdc\" yaml:\"convert_csv_to_ftdc\"`\n}\n\n// poplarTestMetrics was copied from\n// https://github.com/evergreen-ci/poplar/blob/8d03d2bacde0897cedd73ed79ddc167ed1ed4c77/report.go#L124\ntype poplarTestMetrics struct {\n\tName    string `bson:\"name\" json:\"name\" yaml:\"name\"`\n\tVersion int    `bson:\"version,omitempty\" json:\"version,omitempty\" yaml:\"version,omitempty\"`\n\tType    string `bson:\"type\" json:\"type\" yaml:\"type\"`\n\tValue   any    `bson:\"value\" json:\"value\" yaml:\"value\"`\n}\n"
  },
  {
    "path": "internal/cmd/benchmark/go.mod",
    "content": "module go.mongodb.go/mongo-driver/v2/internal/cmd/benchmark\n\ngo 1.24.0\n\nreplace go.mongodb.org/mongo-driver/v2 => ../../../\n\nrequire (\n\tgithub.com/stretchr/testify v1.8.1\n\tgo.mongodb.org/mongo-driver/v2 v2.0.0-00010101000000-000000000000\n)\n\nrequire (\n\tgithub.com/davecgh/go-spew v1.1.1 // indirect\n\tgithub.com/klauspost/compress v1.17.6 // indirect\n\tgithub.com/pmezard/go-difflib v1.0.0 // indirect\n\tgithub.com/xdg-go/pbkdf2 v1.0.0 // indirect\n\tgithub.com/xdg-go/scram v1.2.0 // indirect\n\tgithub.com/xdg-go/stringprep v1.0.4 // indirect\n\tgithub.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect\n\tgolang.org/x/crypto v0.45.0 // indirect\n\tgolang.org/x/sync v0.18.0 // indirect\n\tgolang.org/x/text v0.31.0 // indirect\n\tgopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect\n\tgopkg.in/yaml.v3 v3.0.1 // indirect\n)\n"
  },
  {
    "path": "internal/cmd/benchmark/go.sum",
    "content": "github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=\ngithub.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=\ngithub.com/klauspost/compress v1.17.6 h1:60eq2E/jlfwQXtvZEeBUYADs+BwKBWURIY+Gj2eRGjI=\ngithub.com/klauspost/compress v1.17.6/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM=\ngithub.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=\ngithub.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=\ngithub.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=\ngithub.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=\ngithub.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=\ngithub.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=\ngithub.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=\ngithub.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=\ngithub.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=\ngithub.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=\ngithub.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=\ngithub.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=\ngithub.com/xdg-go/scram v1.2.0 h1:bYKF2AEwG5rqd1BumT4gAnvwU/M9nBp2pTSxeZw7Wvs=\ngithub.com/xdg-go/scram v1.2.0/go.mod h1:3dlrS0iBaWKYVt2ZfA4cj48umJZ+cAEbR6/SjLA88I8=\ngithub.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8=\ngithub.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=\ngithub.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM=\ngithub.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78/go.mod h1:aL8wCCfTfSfmXjznFBSZNN13rSJjlIOI1fUNAtF7rmI=\ngithub.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=\ngolang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=\ngolang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=\ngolang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=\ngolang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I=\ngolang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=\ngolang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=\ngolang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=\ngolang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=\ngolang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=\ngolang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=\ngopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\n"
  },
  {
    "path": "internal/cmd/build-oss-fuzz-corpus/main.go",
    "content": "// Copyright (C) MongoDB, Inc. 2023-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\n// Entry point for the MongoDB Go Driver integration into the Google \"oss-fuzz\" project\n// (https://github.com/google/oss-fuzz).\npackage main\n\nimport (\n\t\"archive/zip\"\n\t\"crypto/sha256\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io/ioutil\"\n\t\"log\"\n\t\"os\"\n\t\"path\"\n\t\"path/filepath\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n)\n\nconst dataDir = \"testdata/bson-corpus/\"\n\ntype validityTestCase struct {\n\tDescription       string  `json:\"description\"`\n\tCanonicalExtJSON  string  `json:\"canonical_extjson\"`\n\tRelaxedExtJSON    *string `json:\"relaxed_extjson\"`\n\tDegenerateExtJSON *string `json:\"degenerate_extjson\"`\n\tConvertedExtJSON  *string `json:\"converted_extjson\"`\n}\n\nfunc findJSONFilesInDir(dir string) ([]string, error) {\n\tfiles := make([]string, 0)\n\n\tentries, err := ioutil.ReadDir(dir)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tfor _, entry := range entries {\n\t\tif entry.IsDir() || path.Ext(entry.Name()) != \".json\" {\n\t\t\tcontinue\n\t\t}\n\n\t\tfiles = append(files, entry.Name())\n\t}\n\n\treturn files, nil\n}\n\n// jsonToNative decodes the extended JSON string (ej) into a native Document\nfunc jsonToNative(ej, ejType, testDesc string) (bson.D, error) {\n\tvar doc bson.D\n\tif err := bson.UnmarshalExtJSON([]byte(ej), ejType != \"relaxed\", &doc); err != nil {\n\t\treturn nil, fmt.Errorf(\"%s: decoding %s extended JSON: %w\", testDesc, ejType, err)\n\t}\n\treturn doc, nil\n}\n\n// jsonToBytes decodes the extended JSON string (ej) into canonical BSON and then encodes it into a byte slice.\nfunc jsonToBytes(ej, ejType, testDesc string) ([]byte, error) {\n\tnative, err := jsonToNative(ej, ejType, testDesc)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tb, err := bson.Marshal(native)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"%s: encoding %s BSON: %w\", testDesc, ejType, err)\n\t}\n\n\treturn b, nil\n}\n\n// seedExtJSON will add the byte representation of the \"extJSON\" string to the fuzzer's coprus.\nfunc seedExtJSON(zw *zip.Writer, extJSON string, extJSONType string, desc string) {\n\tjbytes, err := jsonToBytes(extJSON, extJSONType, desc)\n\tif err != nil {\n\t\tlog.Panicf(\"failed to convert JSON to bytes: %v\", err)\n\t}\n\n\t// Use a SHA256 hash of the BSON bytes for the filename. This isn't an oss-fuzz requirement, it\n\t// just simplifies file naming.\n\tzipFile := fmt.Sprintf(\"%x\", sha256.Sum256(jbytes))\n\n\tf, err := zw.Create(zipFile)\n\tif err != nil {\n\t\tlog.Panicf(\"error creating zip file: %v\", err)\n\t}\n\n\t_, err = f.Write(jbytes)\n\tif err != nil {\n\t\tlog.Panicf(\"failed to write file: %s into zip file: %v\", zipFile, err)\n\t}\n}\n\n// seedTestCase will add the byte representation for each \"extJSON\" string of each valid test case to the fuzzer's\n// corpus.\nfunc seedTestCase(zw *zip.Writer, tcase []*validityTestCase) {\n\tfor _, vtc := range tcase {\n\t\tseedExtJSON(zw, vtc.CanonicalExtJSON, \"canonical\", vtc.Description)\n\n\t\t// Seed the relaxed extended JSON.\n\t\tif vtc.RelaxedExtJSON != nil {\n\t\t\tseedExtJSON(zw, *vtc.RelaxedExtJSON, \"relaxed\", vtc.Description)\n\t\t}\n\n\t\t// Seed the degenerate extended JSON.\n\t\tif vtc.DegenerateExtJSON != nil {\n\t\t\tseedExtJSON(zw, *vtc.DegenerateExtJSON, \"degenerate\", vtc.Description)\n\t\t}\n\n\t\t// Seed the converted extended JSON.\n\t\tif vtc.ConvertedExtJSON != nil {\n\t\t\tseedExtJSON(zw, *vtc.ConvertedExtJSON, \"converted\", vtc.Description)\n\t\t}\n\t}\n}\n\n// seedBSONCorpus will unmarshal the data from \"testdata/bson-corpus\" into a slice of \"testCase\" structs and then\n// marshal the \"*_extjson\" field of each \"validityTestCase\" into a slice of bytes to seed the fuzz corpus.\nfunc seedBSONCorpus(zw *zip.Writer) {\n\tfileNames, err := findJSONFilesInDir(dataDir)\n\tif err != nil {\n\t\tlog.Panicf(\"failed to find JSON files in directory %q: %v\", dataDir, err)\n\t}\n\n\tfor _, fileName := range fileNames {\n\t\tfilePath := path.Join(dataDir, fileName)\n\n\t\tfile, err := os.Open(filePath)\n\t\tif err != nil {\n\t\t\tlog.Panicf(\"failed to open file %q: %v\", filePath, err)\n\t\t}\n\n\t\ttc := struct {\n\t\t\tValid []*validityTestCase `json:\"valid\"`\n\t\t}{}\n\n\t\tif err := json.NewDecoder(file).Decode(&tc); err != nil {\n\t\t\tlog.Panicf(\"failed to decode file %q: %v\", filePath, err)\n\t\t}\n\n\t\tseedTestCase(zw, tc.Valid)\n\t}\n}\n\n// main packs the local corpus as <fuzzer_name>_seed_corpus.zip, which is used by OSS-Fuzz to seed remote fuzzing\n// of the MongoDB Go Driver. See here for more details: https://google.github.io/oss-fuzz/architecture/\nfunc main() {\n\tseedCorpus := os.Args[1]\n\tif filepath.Ext(seedCorpus) != \".zip\" {\n\t\tlog.Panicf(\"expected zip file <corpus>.zip, got %s\", seedCorpus)\n\t}\n\n\tzipFile, err := os.Create(seedCorpus)\n\tif err != nil {\n\t\tlog.Panicf(\"failed creating zip file: %v\", err)\n\t}\n\n\tdefer func() {\n\t\terr := zipFile.Close()\n\t\tif err != nil {\n\t\t\tlog.Panicf(\"failed to close zip file: %v\", err)\n\t\t}\n\t}()\n\n\tzipWriter := zip.NewWriter(zipFile)\n\tseedBSONCorpus(zipWriter)\n\n\tif err := zipWriter.Close(); err != nil {\n\t\tlog.Panicf(\"failed to close zip writer: %v\", err)\n\t}\n}\n"
  },
  {
    "path": "internal/cmd/faas/awslambda/Makefile",
    "content": ".PHONY: default\ndefault:\n\tsam build --debug\n\t# Cannot connect to a local mongo server on non x86_64 architectures.\n\t# These cases require either building a mongodb docker container or\n\t# using a serverless service such as Atlas.\n\tsam local invoke --parameter-overrides \"MongoDbUri=${MONGODB_URI}\"\n"
  },
  {
    "path": "internal/cmd/faas/awslambda/mongodb/bootstrap.go",
    "content": "// Copyright (C) MongoDB, Inc. 2023-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage main\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"os\"\n\t\"time\"\n\n\t\"github.com/aws/aws-lambda-go/events\"\n\t\"github.com/aws/aws-lambda-go/lambda\"\n\t\"go.mongodb.org/mongo-driver/v2/event\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n)\n\nconst timeout = 60 * time.Second\n\n// eventListener supports command, heartbeat, and pool event handlers to record\n// event durations, as well as the number of heartbeats, commands, and open\n// connections.\ntype eventListener struct {\n\tcommandCount          int\n\tcommandDuration       int64\n\theartbeatAwaitedCount int\n\theartbeatCount        int\n\theartbeatDuration     int64\n\topenConnections       int\n}\n\n// commandMonitor initializes an event.CommandMonitor that will count the number\n// of successful or failed command events and record a running duration of these\n// events.\nfunc (listener *eventListener) commandMonitor() *event.CommandMonitor {\n\tsucceeded := func(_ context.Context, e *event.CommandSucceededEvent) {\n\t\tlistener.commandCount++\n\t\tlistener.commandDuration += e.Duration.Nanoseconds()\n\t}\n\n\tfailed := func(_ context.Context, e *event.CommandFailedEvent) {\n\t\tlistener.commandCount++\n\t\tlistener.commandDuration += e.Duration.Nanoseconds()\n\t}\n\n\treturn &event.CommandMonitor{\n\t\tSucceeded: succeeded,\n\t\tFailed:    failed,\n\t}\n}\n\n// serverMonitor initializes an event.ServerMonitor that will count the number\n// of successful or failed heartbeat events and record a running duration of\n// these events.\nfunc (listener *eventListener) serverMonitor() *event.ServerMonitor {\n\tsucceeded := func(e *event.ServerHeartbeatSucceededEvent) {\n\t\tlistener.heartbeatCount++\n\t\tlistener.heartbeatDuration += e.Duration.Nanoseconds()\n\n\t\tif e.Awaited {\n\t\t\tlistener.heartbeatAwaitedCount++\n\t\t}\n\t}\n\n\tfailed := func(e *event.ServerHeartbeatFailedEvent) {\n\t\tlistener.heartbeatCount++\n\t\tlistener.heartbeatDuration += e.Duration.Nanoseconds()\n\n\t\tif e.Awaited {\n\t\t\tlistener.heartbeatAwaitedCount++\n\t\t}\n\t}\n\n\treturn &event.ServerMonitor{\n\t\tServerHeartbeatSucceeded: succeeded,\n\t\tServerHeartbeatFailed:    failed,\n\t}\n}\n\n// poolMonitor initialize an event.PoolMonitor that will increment each time a\n// new connection is created and decrement each time a connection is closed.\nfunc (listener *eventListener) poolMonitor() *event.PoolMonitor {\n\tpoolEvent := func(e *event.PoolEvent) {\n\t\tswitch e.Type {\n\t\tcase event.ConnectionCreated:\n\t\t\tlistener.openConnections++\n\t\tcase event.ConnectionClosed:\n\t\t\tlistener.openConnections--\n\t\t}\n\t}\n\n\treturn &event.PoolMonitor{Event: poolEvent}\n}\n\n// response is the data we return in the body of the API Gateway response.\ntype response struct {\n\tAvgCommandDuration   float64 `json:\"averageCommandDuration\"`\n\tAvgHeartbeatDuration float64 `json:\"averageHeartbeatDuration\"`\n\tOpenConnections      int     `json:\"openConnections\"`\n\tHeartbeatCount       int     `json:\"heartbeatCount\"`\n}\n\n// gateway500 is a convenience function for constructing a gateway response with\n// a 500 status code, indicating an internal server error.\nfunc gateway500() events.APIGatewayProxyResponse {\n\treturn events.APIGatewayProxyResponse{\n\t\tStatusCode: http.StatusInternalServerError,\n\t\tBody:       http.StatusText(http.StatusInternalServerError),\n\t}\n}\n\n// handler is the AWS Lambda handler, executing at runtime.\nfunc handler(ctx context.Context, request events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {\n\tctx, cancel := context.WithTimeout(ctx, timeout)\n\tdefer cancel()\n\n\tlistener := new(eventListener)\n\n\tclientOptions := options.Client().ApplyURI(os.Getenv(\"MONGODB_URI\")).\n\t\tSetMonitor(listener.commandMonitor()).\n\t\tSetServerMonitor(listener.serverMonitor()).\n\t\tSetPoolMonitor(listener.poolMonitor())\n\n\t// Create a MongoClient that points to MONGODB_URI and listens to the\n\t// ComandMonitor, ServerMonitor, and PoolMonitor events.\n\tclient, err := mongo.Connect(clientOptions)\n\tif err != nil {\n\t\treturn gateway500(), fmt.Errorf(\"failed to create client: %w\", err)\n\t}\n\n\tdefer client.Disconnect(ctx)\n\n\tcollection := client.Database(\"faas\").Collection(\"lambda\")\n\n\t// Create a document to insert for the automated test.\n\tdoc := map[string]string{\"hello\": \"world\"}\n\n\t// Insert the document.\n\t_, err = collection.InsertOne(ctx, doc)\n\tif err != nil {\n\t\treturn gateway500(), fmt.Errorf(\"failed to insert: %w\", err)\n\t}\n\n\t// Delete the document.\n\t_, err = collection.DeleteOne(ctx, doc)\n\tif err != nil {\n\t\treturn gateway500(), fmt.Errorf(\"failed to delete: %w\", err)\n\t}\n\n\t// Driver must switch to polling monitoring when running within a FaaS\n\t// environment.\n\tif listener.heartbeatAwaitedCount > 0 {\n\t\treturn gateway500(), fmt.Errorf(\"FaaS environment failed to switch to polling\")\n\t}\n\n\tvar avgCmdDur float64\n\tif count := listener.commandCount; count != 0 {\n\t\tavgCmdDur = float64(listener.commandDuration) / float64(count)\n\t}\n\n\tvar avgHBDur float64\n\tif count := listener.heartbeatCount; count != 0 {\n\t\tavgHBDur = float64(listener.heartbeatDuration) / float64(count)\n\t}\n\n\trsp := &response{\n\t\tAvgCommandDuration:   avgCmdDur,\n\t\tAvgHeartbeatDuration: avgHBDur,\n\t\tOpenConnections:      listener.openConnections,\n\t\tHeartbeatCount:       listener.heartbeatCount,\n\t}\n\n\tbody, err := json.Marshal(rsp)\n\tif err != nil {\n\t\treturn gateway500(), fmt.Errorf(\"failed to marshal: %w\", err)\n\t}\n\n\treturn events.APIGatewayProxyResponse{\n\t\tBody:       string(body),\n\t\tStatusCode: http.StatusOK,\n\t}, nil\n}\n\nfunc main() {\n\tctx := context.Background()\n\n\tlambda.StartWithOptions(handler, lambda.WithContext(ctx))\n}\n"
  },
  {
    "path": "internal/cmd/faas/awslambda/mongodb/go.mod",
    "content": "module go.mongodb.go/mongo-driver/v2/internal/cmd/faas/awslambda/mongodb\n\ngo 1.25\n\nreplace go.mongodb.org/mongo-driver/v2 => ../../../../../\n\nrequire github.com/aws/aws-lambda-go v1.41.0\n\nrequire go.mongodb.org/mongo-driver/v2 v2.0.0-00010101000000-000000000000\n\nrequire (\n\tgithub.com/klauspost/compress v1.17.6 // indirect\n\tgithub.com/stretchr/testify v1.8.1 // indirect\n\tgithub.com/xdg-go/pbkdf2 v1.0.0 // indirect\n\tgithub.com/xdg-go/scram v1.2.0 // indirect\n\tgithub.com/xdg-go/stringprep v1.0.4 // indirect\n\tgithub.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect\n\tgolang.org/x/crypto v0.45.0 // indirect\n\tgolang.org/x/sync v0.18.0 // indirect\n\tgolang.org/x/text v0.31.0 // indirect\n)\n\nreplace gopkg.in/yaml.v2 => gopkg.in/yaml.v2 v2.2.8\n"
  },
  {
    "path": "internal/cmd/faas/awslambda/mongodb/go.sum",
    "content": "github.com/aws/aws-lambda-go v1.41.0 h1:l/5fyVb6Ud9uYd411xdHZzSf2n86TakxzpvIoz7l+3Y=\ngithub.com/aws/aws-lambda-go v1.41.0/go.mod h1:jwFe2KmMsHmffA1X2R09hH6lFzJQxzI8qK17ewzbQMM=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=\ngithub.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=\ngithub.com/klauspost/compress v1.17.6 h1:60eq2E/jlfwQXtvZEeBUYADs+BwKBWURIY+Gj2eRGjI=\ngithub.com/klauspost/compress v1.17.6/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM=\ngithub.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=\ngithub.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=\ngithub.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=\ngithub.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=\ngithub.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=\ngithub.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=\ngithub.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=\ngithub.com/xdg-go/scram v1.2.0 h1:bYKF2AEwG5rqd1BumT4gAnvwU/M9nBp2pTSxeZw7Wvs=\ngithub.com/xdg-go/scram v1.2.0/go.mod h1:3dlrS0iBaWKYVt2ZfA4cj48umJZ+cAEbR6/SjLA88I8=\ngithub.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8=\ngithub.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=\ngithub.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM=\ngithub.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78/go.mod h1:aL8wCCfTfSfmXjznFBSZNN13rSJjlIOI1fUNAtF7rmI=\ngithub.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=\ngolang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=\ngolang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=\ngolang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=\ngolang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I=\ngolang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=\ngolang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=\ngolang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=\ngolang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=\ngolang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=\ngolang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=\ngopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\n"
  },
  {
    "path": "internal/cmd/faas/awslambda/template.yaml",
    "content": "AWSTemplateFormatVersion: \"2010-09-09\"\nTransform: AWS::Serverless-2016-10-31\nDescription: >\n  Go Driver lambda function test\n\nParameters:\n  MongoDbUri:\n    Type: String\n    Description: The MongoDB connection string.\nGlobals:\n  Function:\n    Timeout: 30\nResources:\n  MongoDBFunction:\n    Type: AWS::Serverless::Function\n    Metadata:\n      BuildMethod: go1.x\n    Properties:\n      CodeUri: mongodb/\n      Handler: bootstrap\n      Runtime: provided.al2\n      Architectures: [arm64]\n      Events:\n        MongoDB:\n          Type: Api\n          Properties:\n            Path: /mongodb\n            Method: GET\n      Environment:\n        Variables:\n          MONGODB_URI: !Ref MongoDbUri\nOutputs:\n  MongoDBApi:\n    Description: \"API Gateway endpoint URL for Prod stage for MongoDB function\"\n    Value: !Sub \"https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/mongodb/\"\n  MongoDBFunction:\n    Description: \"MongoDB Lambda Function ARN\"\n    Value: !GetAtt MongoDBFunction.Arn\n  MongoDBFunctionIamRole:\n    Description: \"Implicit IAM Role created for MongoDB function\"\n    Value: !GetAtt MongoDBFunctionRole.Arn\n"
  },
  {
    "path": "internal/cmd/parse-api-report/main.go",
    "content": "// Copyright (C) MongoDB, Inc. 2023-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage main\n\nimport (\n\t\"bufio\"\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\t\"strings\"\n)\n\nfunc main() {\n\tvar line string\n\tvar suppress bool\n\tfoundChange := false\n\tfoundSummary := false\n\n\t// open file to read\n\tfRead, err := os.Open(\"api-report.txt\")\n\tif err != nil {\n\t\tlog.Panic(err)\n\t}\n\t// remember to close the file at the end of the program\n\tdefer fRead.Close()\n\n\t// open file to write\n\tfWrite, err := os.Create(\"api-report.md\")\n\tif err != nil {\n\t\tlog.Panic(err)\n\t}\n\t// remember to close the file at the end of the program\n\tdefer fWrite.Close()\n\n\tfmt.Fprint(fWrite, \"## API Change Report\\n\")\n\n\t// read the file line by line using scanner\n\tscanner := bufio.NewScanner(fRead)\n\n\tfor scanner.Scan() {\n\t\t// do something with a line\n\t\tline = scanner.Text()\n\t\tif strings.Index(line, \"## \") == 0 {\n\t\t\tline = \"##\" + line\n\t\t}\n\n\t\tif strings.Contains(line, \"/mongo/integration/\") {\n\t\t\tsuppress = true\n\t\t}\n\t\tif strings.Index(line, \"# summary\") == 0 {\n\t\t\tsuppress = true\n\t\t\tfoundSummary = true\n\t\t}\n\n\t\tif strings.Contains(line, \"go.mongodb.org/mongo-driver\") {\n\t\t\tline = strings.ReplaceAll(line, \"go.mongodb.org/mongo-driver\", \".\")\n\t\t\tline = \"##\" + line\n\t\t}\n\t\tif !suppress {\n\t\t\tfmt.Fprintf(fWrite, \"%s\\n\", line)\n\t\t\tfoundChange = true\n\t\t}\n\t\tif len(line) == 0 {\n\t\t\tsuppress = false\n\t\t}\n\t}\n\n\tif !foundChange {\n\t\tfmt.Fprint(fWrite, \"No changes found!\\n\")\n\t}\n\n\tif !foundSummary {\n\t\tlog.Panic(\"Could not parse api summary\")\n\t}\n\n\tif err := scanner.Err(); err != nil {\n\t\tlog.Panic(err)\n\t}\n}\n"
  },
  {
    "path": "internal/cmd/testatlas/atlas_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\n//go:build atlastest\n// +build atlastest\n\npackage main\n\nimport (\n\t\"context\"\n\t\"crypto/tls\"\n\t\"encoding/base64\"\n\t\"errors\"\n\t\"fmt\"\n\t\"net/url\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"testing\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/handshake\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/require\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n)\n\nfunc TestAtlas(t *testing.T) {\n\tcases := []struct {\n\t\tname        string\n\t\tenvVar      string\n\t\tcertKeyFile string\n\t\twantErr     string\n\t}{\n\t\t{\n\t\t\tname:        \"Atlas with TLS\",\n\t\t\tenvVar:      \"ATLAS_REPL\",\n\t\t\tcertKeyFile: \"\",\n\t\t\twantErr:     \"\",\n\t\t},\n\t\t{\n\t\t\tname:        \"Atlas with TLS and shared cluster\",\n\t\t\tenvVar:      \"ATLAS_SHRD\",\n\t\t\tcertKeyFile: \"\",\n\t\t\twantErr:     \"\",\n\t\t},\n\t\t{\n\t\t\tname:        \"Atlas with free tier\",\n\t\t\tenvVar:      \"ATLAS_FREE\",\n\t\t\tcertKeyFile: \"\",\n\t\t\twantErr:     \"\",\n\t\t},\n\t\t{\n\t\t\tname:        \"Atlas with TLS 1.1\",\n\t\t\tenvVar:      \"ATLAS_TLS11\",\n\t\t\tcertKeyFile: \"\",\n\t\t\twantErr:     \"\",\n\t\t},\n\t\t{\n\t\t\tname:        \"Atlas with TLS 1.2\",\n\t\t\tenvVar:      \"ATLAS_TLS12\",\n\t\t\tcertKeyFile: \"\",\n\t\t\twantErr:     \"\",\n\t\t},\n\t\t{\n\t\t\tname:        \"Atlas with srv file on replica set\",\n\t\t\tenvVar:      \"ATLAS_SRV_REPL\",\n\t\t\tcertKeyFile: \"\",\n\t\t\twantErr:     \"\",\n\t\t},\n\t\t{\n\t\t\tname:        \"Atlas with srv file on shared cluster\",\n\t\t\tenvVar:      \"ATLAS_SRV_SHRD\",\n\t\t\tcertKeyFile: \"\",\n\t\t\twantErr:     \"\",\n\t\t},\n\t\t{\n\t\t\tname:        \"Atlas with srv file on free tier\",\n\t\t\tenvVar:      \"ATLAS_SRV_FREE\",\n\t\t\tcertKeyFile: \"\",\n\t\t\twantErr:     \"\",\n\t\t},\n\t\t{\n\t\t\tname:        \"Atlas with srv file on TLS 1.1\",\n\t\t\tenvVar:      \"ATLAS_SRV_TLS11\",\n\t\t\tcertKeyFile: \"\",\n\t\t\twantErr:     \"\",\n\t\t},\n\t\t{\n\t\t\tname:        \"Atlas with srv file on TLS 1.2\",\n\t\t\tenvVar:      \"ATLAS_SRV_TLS12\",\n\t\t\tcertKeyFile: \"\",\n\t\t\twantErr:     \"\",\n\t\t},\n\t\t{\n\t\t\tname:        \"Atlas with X509 Dev\",\n\t\t\tenvVar:      \"ATLAS_X509_DEV\",\n\t\t\tcertKeyFile: createAtlasX509DevCertKeyFile(t),\n\t\t\twantErr:     \"\",\n\t\t},\n\t\t{\n\t\t\tname:        \"Atlas with X509 Dev no user\",\n\t\t\tenvVar:      \"ATLAS_X509_DEV\",\n\t\t\tcertKeyFile: createAtlasX509DevCertKeyFileNoUser(t),\n\t\t\twantErr:     \"UserNotFound\",\n\t\t},\n\t}\n\n\tfor _, tc := range cases {\n\t\tt.Run(fmt.Sprintf(\"%s (%s)\", tc.name, tc.envVar), func(t *testing.T) {\n\t\t\turi := os.Getenv(tc.envVar)\n\t\t\trequire.NotEmpty(t, uri, \"Environment variable %s is not set\", tc.envVar)\n\n\t\t\tif tc.certKeyFile != \"\" {\n\t\t\t\turi = addTLSCertKeyFile(t, tc.certKeyFile, uri)\n\t\t\t}\n\n\t\t\t// Set a low server selection timeout so we fail fast if there are errors.\n\t\t\tclientOpts := options.Client().\n\t\t\t\tApplyURI(uri).\n\t\t\t\tSetServerSelectionTimeout(1 * time.Second)\n\n\t\t\t// Run basic connectivity test.\n\t\t\terr := runTest(context.Background(), clientOpts)\n\t\t\tif tc.wantErr != \"\" {\n\t\t\t\tassert.ErrorContains(t, err, tc.wantErr, \"expected error to contain %q\", tc.wantErr)\n\n\t\t\t\treturn\n\t\t\t}\n\t\t\trequire.NoError(t, err, \"error running test with TLS\")\n\n\t\t\torig := clientOpts.TLSConfig\n\t\t\tif orig == nil {\n\t\t\t\torig = &tls.Config{}\n\t\t\t}\n\n\t\t\tinsecure := orig.Clone()\n\t\t\tinsecure.InsecureSkipVerify = true\n\n\t\t\t// Run the connectivity test with InsecureSkipVerify to ensure SNI is done\n\t\t\t// correctly even if verification is disabled.\n\t\t\tinsecureClientOpts := options.Client().\n\t\t\t\tApplyURI(uri).\n\t\t\t\tSetServerSelectionTimeout(1 * time.Second).\n\t\t\t\tSetTLSConfig(insecure)\n\n\t\t\terr = runTest(context.Background(), insecureClientOpts)\n\t\t\trequire.NoError(t, err, \"error running test with tlsInsecure\")\n\t\t})\n\t}\n}\n\nfunc runTest(ctx context.Context, clientOpts *options.ClientOptions) error {\n\tclient, err := mongo.Connect(clientOpts)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"Connect error: %w\", err)\n\t}\n\n\tdefer func() {\n\t\t_ = client.Disconnect(ctx)\n\t}()\n\n\tdb := client.Database(\"test\")\n\tcmd := bson.D{{handshake.LegacyHello, 1}}\n\terr = db.RunCommand(ctx, cmd).Err()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"legacy hello error: %w\", err)\n\t}\n\n\tcoll := db.Collection(\"test\")\n\tif err = coll.FindOne(ctx, bson.D{{\"x\", 1}}).Err(); err != nil && !errors.Is(err, mongo.ErrNoDocuments) {\n\t\treturn fmt.Errorf(\"FindOne error: %w\", err)\n\t}\n\treturn nil\n}\n\nfunc createAtlasX509DevCertKeyFile(t *testing.T) string {\n\tt.Helper()\n\n\tb64 := os.Getenv(\"ATLAS_X509_DEV_CERT_BASE64\")\n\tassert.NotEmpty(t, b64, \"Environment variable ATLAS_X509_DEV_CERT_BASE64 is not set\")\n\n\tcertBytes, err := base64.StdEncoding.DecodeString(b64)\n\trequire.NoError(t, err, \"failed to decode ATLAS_X509_DEV_CERT_BASE64\")\n\n\tcertFilePath := filepath.Join(t.TempDir(), \"atlas_x509_dev_cert.pem\")\n\n\terr = os.WriteFile(certFilePath, certBytes, 0o600)\n\trequire.NoError(t, err, \"failed to write ATLAS_X509_DEV_CERT_BASE64 to file\")\n\n\treturn certFilePath\n}\n\nfunc createAtlasX509DevCertKeyFileNoUser(t *testing.T) string {\n\tt.Helper()\n\n\tb64 := os.Getenv(\"ATLAS_X509_DEV_CERT_NOUSER_BASE64\")\n\tassert.NotEmpty(t, b64, \"Environment variable ATLAS_X509_DEV_CERT_NOUSER_BASE64 is not set\")\n\n\tkeyBytes, err := base64.StdEncoding.DecodeString(b64)\n\trequire.NoError(t, err, \"failed to decode ATLAS_X509_DEV_CERT_NOUSER_BASE64\")\n\n\tkeyFilePath := filepath.Join(t.TempDir(), \"atlas_x509_dev_cert_no_user.pem\")\n\n\terr = os.WriteFile(keyFilePath, keyBytes, 0o600)\n\trequire.NoError(t, err, \"failed to write ATLAS_X509_DEV_CERT_NOUSER_BASE64 to file\")\n\n\treturn keyFilePath\n}\n\nfunc addTLSCertKeyFile(t *testing.T, certKeyFile, uri string) string {\n\tt.Helper()\n\n\tu, err := url.Parse(uri)\n\trequire.NoError(t, err, \"failed to parse uri\")\n\n\tq := u.Query()\n\tq.Set(\"tlsCertificateKeyFile\", filepath.ToSlash(certKeyFile))\n\n\tu.RawQuery = q.Encode()\n\n\treturn u.String()\n}\n"
  },
  {
    "path": "internal/cmd/testentauth/main.go",
    "content": "// Copyright (C) MongoDB, Inc. 2022-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage main\n\nimport (\n\t\"context\"\n\t\"log\"\n\t\"os\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/mongo\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/connstring\"\n)\n\nfunc main() {\n\turi := os.Getenv(\"MONGODB_URI\")\n\tcompressor := os.Getenv(\"MONGO_GO_DRIVER_COMPRESSOR\")\n\n\tclient, err := mongo.Connect(\n\t\toptions.Client().ApplyURI(uri).SetCompressors([]string{compressor}))\n\tif err != nil {\n\t\tlog.Panicf(\"Error connecting client: %v\", err)\n\t}\n\n\t// Use the defaultauthdb (i.e. the database name after the \"/\") specified in the connection\n\t// string to run the count operation.\n\tcs, err := connstring.Parse(uri)\n\tif err != nil {\n\t\tlog.Panicf(\"Error parsing connection string: %v\", err)\n\t}\n\tif cs.Database == \"\" {\n\t\tlog.Panic(\"Connection string must contain a defaultauthdb.\")\n\t}\n\n\tcoll := client.Database(cs.Database).Collection(\"test\")\n\n\tctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)\n\tdefer cancel()\n\n\tcount, err := coll.EstimatedDocumentCount(ctx)\n\tif err != nil {\n\t\tlog.Panicf(\"failed executing count command: %v\", err)\n\t}\n\tlog.Println(\"Count of test collection:\", count)\n}\n"
  },
  {
    "path": "internal/cmd/testkms/main.go",
    "content": "// Copyright (C) MongoDB, Inc. 2022-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"os\"\n\t\"strings\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n)\n\nvar datakeyopts = map[string]bson.M{\n\t\"aws\": {\n\t\t\"region\": \"us-east-1\",\n\t\t\"key\":    \"arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0\",\n\t},\n\t\"azure\": {\n\t\t\"keyVaultEndpoint\": \"\",\n\t\t\"keyName\":          \"\",\n\t},\n\t\"gcp\": {\n\t\t\"projectId\": \"devprod-drivers\",\n\t\t\"location\":  \"global\",\n\t\t\"keyRing\":   \"key-ring-csfle\",\n\t\t\"keyName\":   \"key-name-csfle\",\n\t},\n}\n\nfunc main() {\n\turi := os.Getenv(\"MONGODB_URI\")\n\tprovider := os.Getenv(\"PROVIDER\")\n\t// expecterror is an expect error substring. Set to empty string to expect no error.\n\texpecterror := os.Getenv(\"EXPECT_ERROR\")\n\n\tdatakeyopt, validKmsProvider := datakeyopts[provider]\n\tok := false\n\tswitch {\n\tcase uri == \"\":\n\t\tfmt.Println(\"ERROR: Please set required MONGODB_URI environment variable.\")\n\tcase provider == \"\":\n\t\tfmt.Println(\"ERROR: Please set required PROVIDER environment variable.\")\n\tcase !validKmsProvider:\n\t\tfmt.Println(\"ERROR: Unsupported PROVIDER value.\")\n\tdefault:\n\t\tok = true\n\t}\n\tif provider == \"azure\" {\n\t\tazureKmsKeyName := os.Getenv(\"AZUREKMS_KEY_NAME\")\n\t\tazureKmsKeyVaultEndpoint := os.Getenv(\"AZUREKMS_KEY_VAULT_ENDPOINT\")\n\t\tif azureKmsKeyName == \"\" {\n\t\t\tfmt.Println(\"ERROR: Please set required AZUREKMS_KEY_NAME environment variable.\")\n\t\t\tok = false\n\t\t}\n\t\tif azureKmsKeyVaultEndpoint == \"\" {\n\t\t\tfmt.Println(\"ERROR: Please set required AZUREKMS_KEY_VAULT_ENDPOINT environment variable.\")\n\t\t\tok = false\n\t\t}\n\t\tdatakeyopts[\"azure\"][\"keyName\"] = azureKmsKeyName\n\t\tdatakeyopts[\"azure\"][\"keyVaultEndpoint\"] = azureKmsKeyVaultEndpoint\n\t}\n\tif !ok {\n\t\tproviders := make([]string, 0, len(datakeyopts))\n\t\tfor p := range datakeyopts {\n\t\t\tproviders = append(providers, p)\n\t\t}\n\n\t\tfmt.Println(\"The following environment variables are understood:\")\n\t\tfmt.Println(\"- MONGODB_URI as a MongoDB URI. Example: 'mongodb://localhost:27017'\")\n\t\tfmt.Println(\"- EXPECT_ERROR as an optional expected error substring.\")\n\t\tfmt.Println(\"- PROVIDER as a KMS provider, which supports:\", strings.Join(providers, \", \"))\n\t\tfmt.Println(\"- AZUREKMS_KEY_NAME as the Azure key name. Required if PROVIDER=azure.\")\n\t\tfmt.Println(\"- AZUREKMS_KEY_VAULT_ENDPOINT as the Azure key name. Required if PROVIDER=azure.\")\n\t\tos.Exit(1)\n\t}\n\n\tcOpts := options.Client().ApplyURI(uri)\n\tkeyVaultClient, err := mongo.Connect(cOpts)\n\tif err != nil {\n\t\tpanic(fmt.Sprintf(\"Connect error: %v\", err))\n\t}\n\tdefer func() { _ = keyVaultClient.Disconnect(context.Background()) }()\n\n\tkmsProvidersMap := map[string]map[string]any{\n\t\tprovider: {},\n\t}\n\tceOpts := options.ClientEncryption().SetKmsProviders(kmsProvidersMap).SetKeyVaultNamespace(\"keyvault.datakeys\")\n\tce, err := mongo.NewClientEncryption(keyVaultClient, ceOpts)\n\tif err != nil {\n\t\tpanic(fmt.Sprintf(\"Error in NewClientEncryption: %v\", err))\n\t}\n\tdkOpts := options.DataKey().SetMasterKey(datakeyopt)\n\t_, err = ce.CreateDataKey(context.Background(), provider, dkOpts)\n\tif expecterror == \"\" {\n\t\tif err != nil {\n\t\t\tpanic(fmt.Sprintf(\"Expected success, but got error in CreateDataKey: %v\", err))\n\t\t}\n\t} else {\n\t\tif err == nil {\n\t\t\tpanic(fmt.Sprintf(\"Expected error message to contain %q, but got no error\", expecterror))\n\t\t}\n\t\tif !strings.Contains(err.Error(), expecterror) {\n\t\t\tpanic(fmt.Sprintf(\"Expected error message to contain %q, but got %q\", expecterror, err.Error()))\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "internal/codecutil/encoding.go",
    "content": "// Copyright (C) MongoDB, Inc. 2023-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage codecutil\n\nimport (\n\t\"bytes\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"reflect\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n)\n\nvar ErrNilValue = errors.New(\"value is nil\")\n\n// MarshalError is returned when attempting to transform a value into a document\n// results in an error.\ntype MarshalError struct {\n\tValue any\n\tErr   error\n}\n\n// Error implements the error interface.\nfunc (e MarshalError) Error() string {\n\treturn fmt.Sprintf(\"cannot marshal type %q to a BSON Document: %v\",\n\t\treflect.TypeOf(e.Value), e.Err)\n}\n\nfunc (e MarshalError) Unwrap() error { return e.Err }\n\n// EncoderFn is used to functionally construct an encoder for marshaling values.\ntype EncoderFn func(io.Writer) *bson.Encoder\n\n// MarshalValue will attempt to encode the value with the encoder returned by\n// the encoder function.\nfunc MarshalValue(val any, encFn EncoderFn) (bsoncore.Value, error) {\n\t// If the val is already a bsoncore.Value, then do nothing.\n\tif bval, ok := val.(bsoncore.Value); ok {\n\t\treturn bval, nil\n\t}\n\n\tif val == nil {\n\t\treturn bsoncore.Value{}, ErrNilValue\n\t}\n\n\tbuf := new(bytes.Buffer)\n\n\tenc := encFn(buf)\n\n\t// Encode the value in a single-element document with an empty key. Use\n\t// bsoncore to extract the first element and return the BSON value.\n\terr := enc.Encode(bson.D{{Key: \"\", Value: val}})\n\tif err != nil {\n\t\treturn bsoncore.Value{}, MarshalError{Value: val, Err: err}\n\t}\n\n\treturn bsoncore.Document(buf.Bytes()).Index(0).Value(), nil\n}\n"
  },
  {
    "path": "internal/codecutil/encoding_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2023-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage codecutil\n\nimport (\n\t\"io\"\n\t\"testing\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n)\n\nfunc testEncFn(t *testing.T) EncoderFn {\n\tt.Helper()\n\n\treturn func(w io.Writer) *bson.Encoder {\n\t\trw := bson.NewDocumentWriter(w)\n\t\treturn bson.NewEncoder(rw)\n\t}\n}\n\nfunc TestMarshalValue(t *testing.T) {\n\tt.Parallel()\n\n\ttests := []struct {\n\t\tname     string\n\t\tval      any\n\t\tregistry *bson.Registry\n\t\tencFn    EncoderFn\n\t\twant     string\n\t\twantErr  error\n\t}{\n\t\t{\n\t\t\tname:    \"empty\",\n\t\t\tval:     nil,\n\t\t\twant:    \"\",\n\t\t\twantErr: ErrNilValue,\n\t\t\tencFn:   testEncFn(t),\n\t\t},\n\t\t{\n\t\t\tname:  \"bson.D\",\n\t\t\tval:   bson.D{{\"foo\", \"bar\"}},\n\t\t\twant:  `{\"foo\": \"bar\"}`,\n\t\t\tencFn: testEncFn(t),\n\t\t},\n\t\t{\n\t\t\tname:  \"map\",\n\t\t\tval:   map[string]any{\"foo\": \"bar\"},\n\t\t\twant:  `{\"foo\": \"bar\"}`,\n\t\t\tencFn: testEncFn(t),\n\t\t},\n\t\t{\n\t\t\tname:  \"struct\",\n\t\t\tval:   struct{ Foo string }{Foo: \"bar\"},\n\t\t\twant:  `{\"foo\": \"bar\"}`,\n\t\t\tencFn: testEncFn(t),\n\t\t},\n\t\t{\n\t\t\tname:  \"non-document type\",\n\t\t\tval:   \"foo: bar\",\n\t\t\twant:  `\"foo: bar\"`,\n\t\t\tencFn: testEncFn(t),\n\t\t},\n\t}\n\n\tfor _, test := range tests {\n\t\ttest := test\n\n\t\tt.Run(test.name, func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tvalue, err := MarshalValue(test.val, test.encFn)\n\n\t\t\tassert.Equal(t, test.wantErr, err, \"expected and actual error do not match\")\n\t\t\tassert.Equal(t, test.want, value.String(), \"expected and actual comments are different\")\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "internal/credproviders/assume_role_provider.go",
    "content": "// Copyright (C) MongoDB, Inc. 2023-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage credproviders\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io/ioutil\"\n\t\"net/http\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/aws/credentials\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/uuid\"\n)\n\nconst (\n\t// assumeRoleProviderName provides a name of assume role provider\n\tassumeRoleProviderName = \"AssumeRoleProvider\"\n\n\tstsURI = `https://sts.amazonaws.com/?Action=AssumeRoleWithWebIdentity&RoleSessionName=%s&RoleArn=%s&WebIdentityToken=%s&Version=2011-06-15`\n)\n\n// An AssumeRoleProvider retrieves credentials for assume role with web identity.\ntype AssumeRoleProvider struct {\n\tAwsRoleArnEnv              EnvVar\n\tAwsWebIdentityTokenFileEnv EnvVar\n\tAwsRoleSessionNameEnv      EnvVar\n\n\thttpClient *http.Client\n\texpiration time.Time\n\n\t// expiryWindow will allow the credentials to trigger refreshing prior to the credentials actually expiring.\n\t// This is beneficial so expiring credentials do not cause request to fail unexpectedly due to exceptions.\n\t//\n\t// So a ExpiryWindow of 10s would cause calls to IsExpired() to return true\n\t// 10 seconds before the credentials are actually expired.\n\texpiryWindow time.Duration\n}\n\n// NewAssumeRoleProvider returns a pointer to an assume role provider.\nfunc NewAssumeRoleProvider(httpClient *http.Client, expiryWindow time.Duration) *AssumeRoleProvider {\n\treturn &AssumeRoleProvider{\n\t\t// AwsRoleArnEnv is the environment variable for AWS_ROLE_ARN\n\t\tAwsRoleArnEnv: EnvVar(\"AWS_ROLE_ARN\"),\n\t\t// AwsWebIdentityTokenFileEnv is the environment variable for AWS_WEB_IDENTITY_TOKEN_FILE\n\t\tAwsWebIdentityTokenFileEnv: EnvVar(\"AWS_WEB_IDENTITY_TOKEN_FILE\"),\n\t\t// AwsRoleSessionNameEnv is the environment variable for AWS_ROLE_SESSION_NAME\n\t\tAwsRoleSessionNameEnv: EnvVar(\"AWS_ROLE_SESSION_NAME\"),\n\t\thttpClient:            httpClient,\n\t\texpiryWindow:          expiryWindow,\n\t}\n}\n\n// RetrieveWithContext retrieves the keys from the AWS service.\nfunc (a *AssumeRoleProvider) RetrieveWithContext(ctx context.Context) (credentials.Value, error) {\n\tconst defaultHTTPTimeout = 10 * time.Second\n\n\tv := credentials.Value{ProviderName: assumeRoleProviderName}\n\n\troleArn := a.AwsRoleArnEnv.Get()\n\ttokenFile := a.AwsWebIdentityTokenFileEnv.Get()\n\tif tokenFile == \"\" && roleArn == \"\" {\n\t\treturn v, errors.New(\"AWS_WEB_IDENTITY_TOKEN_FILE and AWS_ROLE_ARN are missing\")\n\t}\n\tif tokenFile != \"\" && roleArn == \"\" {\n\t\treturn v, errors.New(\"AWS_WEB_IDENTITY_TOKEN_FILE is set, but AWS_ROLE_ARN is missing\")\n\t}\n\tif tokenFile == \"\" && roleArn != \"\" {\n\t\treturn v, errors.New(\"AWS_ROLE_ARN is set, but AWS_WEB_IDENTITY_TOKEN_FILE is missing\")\n\t}\n\ttoken, err := ioutil.ReadFile(tokenFile)\n\tif err != nil {\n\t\treturn v, err\n\t}\n\n\tsessionName := a.AwsRoleSessionNameEnv.Get()\n\tif sessionName == \"\" {\n\t\t// Use a UUID if the RoleSessionName is not given.\n\t\tid, err := uuid.New()\n\t\tif err != nil {\n\t\t\treturn v, err\n\t\t}\n\t\tsessionName = id.String()\n\t}\n\n\tfullURI := fmt.Sprintf(stsURI, sessionName, roleArn, string(token))\n\n\treq, err := http.NewRequest(http.MethodPost, fullURI, nil)\n\tif err != nil {\n\t\treturn v, err\n\t}\n\treq.Header.Set(\"Accept\", \"application/json\")\n\n\tctx, cancel := context.WithTimeout(ctx, defaultHTTPTimeout)\n\tdefer cancel()\n\tresp, err := a.httpClient.Do(req.WithContext(ctx))\n\tif err != nil {\n\t\treturn v, err\n\t}\n\tdefer resp.Body.Close()\n\tif resp.StatusCode != http.StatusOK {\n\t\treturn v, fmt.Errorf(\"response failure: %s\", resp.Status)\n\t}\n\n\tvar stsResp struct {\n\t\tResponse struct {\n\t\t\tResult struct {\n\t\t\t\tCredentials struct {\n\t\t\t\t\tAccessKeyID     string  `json:\"AccessKeyId\"`\n\t\t\t\t\tSecretAccessKey string  `json:\"SecretAccessKey\"`\n\t\t\t\t\tToken           string  `json:\"SessionToken\"`\n\t\t\t\t\tExpiration      float64 `json:\"Expiration\"`\n\t\t\t\t} `json:\"Credentials\"`\n\t\t\t} `json:\"AssumeRoleWithWebIdentityResult\"`\n\t\t} `json:\"AssumeRoleWithWebIdentityResponse\"`\n\t}\n\n\terr = json.NewDecoder(resp.Body).Decode(&stsResp)\n\tif err != nil {\n\t\treturn v, err\n\t}\n\tv.AccessKeyID = stsResp.Response.Result.Credentials.AccessKeyID\n\tv.SecretAccessKey = stsResp.Response.Result.Credentials.SecretAccessKey\n\tv.SessionToken = stsResp.Response.Result.Credentials.Token\n\tif !v.HasKeys() {\n\t\treturn v, errors.New(\"failed to retrieve web identity keys\")\n\t}\n\tsec := int64(stsResp.Response.Result.Credentials.Expiration)\n\ta.expiration = time.Unix(sec, 0).Add(-a.expiryWindow)\n\n\treturn v, nil\n}\n\n// Retrieve retrieves the keys from the AWS service.\nfunc (a *AssumeRoleProvider) Retrieve() (credentials.Value, error) {\n\treturn a.RetrieveWithContext(context.Background())\n}\n\n// IsExpired returns true if the credentials are expired.\nfunc (a *AssumeRoleProvider) IsExpired() bool {\n\treturn a.expiration.Before(time.Now())\n}\n"
  },
  {
    "path": "internal/credproviders/ec2_provider.go",
    "content": "// Copyright (C) MongoDB, Inc. 2023-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage credproviders\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io/ioutil\"\n\t\"net/http\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/aws/credentials\"\n)\n\nconst (\n\t// ec2ProviderName provides a name of EC2 provider\n\tec2ProviderName = \"EC2Provider\"\n\n\tawsEC2URI       = \"http://169.254.169.254/\"\n\tawsEC2RolePath  = \"latest/meta-data/iam/security-credentials/\"\n\tawsEC2TokenPath = \"latest/api/token\"\n\n\tdefaultHTTPTimeout = 10 * time.Second\n)\n\n// An EC2Provider retrieves credentials from EC2 metadata.\ntype EC2Provider struct {\n\thttpClient *http.Client\n\texpiration time.Time\n\n\t// expiryWindow will allow the credentials to trigger refreshing prior to the credentials actually expiring.\n\t// This is beneficial so expiring credentials do not cause request to fail unexpectedly due to exceptions.\n\t//\n\t// So a ExpiryWindow of 10s would cause calls to IsExpired() to return true\n\t// 10 seconds before the credentials are actually expired.\n\texpiryWindow time.Duration\n}\n\n// NewEC2Provider returns a pointer to an EC2 credential provider.\nfunc NewEC2Provider(httpClient *http.Client, expiryWindow time.Duration) *EC2Provider {\n\treturn &EC2Provider{\n\t\thttpClient:   httpClient,\n\t\texpiryWindow: expiryWindow,\n\t}\n}\n\nfunc (e *EC2Provider) getToken(ctx context.Context) (string, error) {\n\treq, err := http.NewRequest(http.MethodPut, awsEC2URI+awsEC2TokenPath, nil)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tconst defaultEC2TTLSeconds = \"30\"\n\treq.Header.Set(\"X-aws-ec2-metadata-token-ttl-seconds\", defaultEC2TTLSeconds)\n\n\tctx, cancel := context.WithTimeout(ctx, defaultHTTPTimeout)\n\tdefer cancel()\n\tresp, err := e.httpClient.Do(req.WithContext(ctx))\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tdefer resp.Body.Close()\n\tif resp.StatusCode != http.StatusOK {\n\t\treturn \"\", fmt.Errorf(\"%s %s failed: %s\", req.Method, req.URL.String(), resp.Status)\n\t}\n\n\ttoken, err := ioutil.ReadAll(resp.Body)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tif len(token) == 0 {\n\t\treturn \"\", errors.New(\"unable to retrieve token from EC2 metadata\")\n\t}\n\treturn string(token), nil\n}\n\nfunc (e *EC2Provider) getRoleName(ctx context.Context, token string) (string, error) {\n\treq, err := http.NewRequest(http.MethodGet, awsEC2URI+awsEC2RolePath, nil)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\treq.Header.Set(\"X-aws-ec2-metadata-token\", token)\n\n\tctx, cancel := context.WithTimeout(ctx, defaultHTTPTimeout)\n\tdefer cancel()\n\tresp, err := e.httpClient.Do(req.WithContext(ctx))\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tdefer resp.Body.Close()\n\tif resp.StatusCode != http.StatusOK {\n\t\treturn \"\", fmt.Errorf(\"%s %s failed: %s\", req.Method, req.URL.String(), resp.Status)\n\t}\n\n\trole, err := ioutil.ReadAll(resp.Body)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tif len(role) == 0 {\n\t\treturn \"\", errors.New(\"unable to retrieve role_name from EC2 metadata\")\n\t}\n\treturn string(role), nil\n}\n\nfunc (e *EC2Provider) getCredentials(ctx context.Context, token string, role string) (credentials.Value, time.Time, error) {\n\tv := credentials.Value{ProviderName: ec2ProviderName}\n\n\tpathWithRole := awsEC2URI + awsEC2RolePath + role\n\treq, err := http.NewRequest(http.MethodGet, pathWithRole, nil)\n\tif err != nil {\n\t\treturn v, time.Time{}, err\n\t}\n\treq.Header.Set(\"X-aws-ec2-metadata-token\", token)\n\tctx, cancel := context.WithTimeout(ctx, defaultHTTPTimeout)\n\tdefer cancel()\n\tresp, err := e.httpClient.Do(req.WithContext(ctx))\n\tif err != nil {\n\t\treturn v, time.Time{}, err\n\t}\n\tdefer resp.Body.Close()\n\tif resp.StatusCode != http.StatusOK {\n\t\treturn v, time.Time{}, fmt.Errorf(\"%s %s failed: %s\", req.Method, req.URL.String(), resp.Status)\n\t}\n\n\tvar ec2Resp struct {\n\t\tAccessKeyID     string    `json:\"AccessKeyId\"`\n\t\tSecretAccessKey string    `json:\"SecretAccessKey\"`\n\t\tToken           string    `json:\"Token\"`\n\t\tExpiration      time.Time `json:\"Expiration\"`\n\t}\n\n\terr = json.NewDecoder(resp.Body).Decode(&ec2Resp)\n\tif err != nil {\n\t\treturn v, time.Time{}, err\n\t}\n\n\tv.AccessKeyID = ec2Resp.AccessKeyID\n\tv.SecretAccessKey = ec2Resp.SecretAccessKey\n\tv.SessionToken = ec2Resp.Token\n\n\treturn v, ec2Resp.Expiration, nil\n}\n\n// RetrieveWithContext retrieves the keys from the AWS service.\nfunc (e *EC2Provider) RetrieveWithContext(ctx context.Context) (credentials.Value, error) {\n\tv := credentials.Value{ProviderName: ec2ProviderName}\n\n\ttoken, err := e.getToken(ctx)\n\tif err != nil {\n\t\treturn v, err\n\t}\n\n\trole, err := e.getRoleName(ctx, token)\n\tif err != nil {\n\t\treturn v, err\n\t}\n\n\tv, exp, err := e.getCredentials(ctx, token, role)\n\tif err != nil {\n\t\treturn v, err\n\t}\n\tif !v.HasKeys() {\n\t\treturn v, errors.New(\"failed to retrieve EC2 keys\")\n\t}\n\te.expiration = exp.Add(-e.expiryWindow)\n\n\treturn v, nil\n}\n\n// Retrieve retrieves the keys from the AWS service.\nfunc (e *EC2Provider) Retrieve() (credentials.Value, error) {\n\treturn e.RetrieveWithContext(context.Background())\n}\n\n// IsExpired returns true if the credentials are expired.\nfunc (e *EC2Provider) IsExpired() bool {\n\treturn e.expiration.Before(time.Now())\n}\n"
  },
  {
    "path": "internal/credproviders/ecs_provider.go",
    "content": "// Copyright (C) MongoDB, Inc. 2023-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage credproviders\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/aws/credentials\"\n)\n\nconst (\n\t// ecsProviderName provides a name of ECS provider\n\tecsProviderName = \"ECSProvider\"\n\n\tawsRelativeURI = \"http://169.254.170.2/\"\n)\n\n// An ECSProvider retrieves credentials from ECS metadata.\ntype ECSProvider struct {\n\tAwsContainerCredentialsRelativeURIEnv EnvVar\n\n\thttpClient *http.Client\n\texpiration time.Time\n\n\t// expiryWindow will allow the credentials to trigger refreshing prior to the credentials actually expiring.\n\t// This is beneficial so expiring credentials do not cause request to fail unexpectedly due to exceptions.\n\t//\n\t// So a ExpiryWindow of 10s would cause calls to IsExpired() to return true\n\t// 10 seconds before the credentials are actually expired.\n\texpiryWindow time.Duration\n}\n\n// NewECSProvider returns a pointer to an ECS credential provider.\nfunc NewECSProvider(httpClient *http.Client, expiryWindow time.Duration) *ECSProvider {\n\treturn &ECSProvider{\n\t\t// AwsContainerCredentialsRelativeURIEnv is the environment variable for AWS_CONTAINER_CREDENTIALS_RELATIVE_URI\n\t\tAwsContainerCredentialsRelativeURIEnv: EnvVar(\"AWS_CONTAINER_CREDENTIALS_RELATIVE_URI\"),\n\t\thttpClient:                            httpClient,\n\t\texpiryWindow:                          expiryWindow,\n\t}\n}\n\n// RetrieveWithContext retrieves the keys from the AWS service.\nfunc (e *ECSProvider) RetrieveWithContext(ctx context.Context) (credentials.Value, error) {\n\tconst defaultHTTPTimeout = 10 * time.Second\n\n\tv := credentials.Value{ProviderName: ecsProviderName}\n\n\trelativeEcsURI := e.AwsContainerCredentialsRelativeURIEnv.Get()\n\tif len(relativeEcsURI) == 0 {\n\t\treturn v, errors.New(\"AWS_CONTAINER_CREDENTIALS_RELATIVE_URI is missing\")\n\t}\n\tfullURI := awsRelativeURI + relativeEcsURI\n\n\treq, err := http.NewRequest(http.MethodGet, fullURI, nil)\n\tif err != nil {\n\t\treturn v, err\n\t}\n\treq.Header.Set(\"Accept\", \"application/json\")\n\n\tctx, cancel := context.WithTimeout(ctx, defaultHTTPTimeout)\n\tdefer cancel()\n\tresp, err := e.httpClient.Do(req.WithContext(ctx))\n\tif err != nil {\n\t\treturn v, err\n\t}\n\tdefer resp.Body.Close()\n\tif resp.StatusCode != http.StatusOK {\n\t\treturn v, fmt.Errorf(\"response failure: %s\", resp.Status)\n\t}\n\n\tvar ecsResp struct {\n\t\tAccessKeyID     string    `json:\"AccessKeyId\"`\n\t\tSecretAccessKey string    `json:\"SecretAccessKey\"`\n\t\tToken           string    `json:\"Token\"`\n\t\tExpiration      time.Time `json:\"Expiration\"`\n\t}\n\n\terr = json.NewDecoder(resp.Body).Decode(&ecsResp)\n\tif err != nil {\n\t\treturn v, err\n\t}\n\n\tv.AccessKeyID = ecsResp.AccessKeyID\n\tv.SecretAccessKey = ecsResp.SecretAccessKey\n\tv.SessionToken = ecsResp.Token\n\tif !v.HasKeys() {\n\t\treturn v, errors.New(\"failed to retrieve ECS keys\")\n\t}\n\te.expiration = ecsResp.Expiration.Add(-e.expiryWindow)\n\n\treturn v, nil\n}\n\n// Retrieve retrieves the keys from the AWS service.\nfunc (e *ECSProvider) Retrieve() (credentials.Value, error) {\n\treturn e.RetrieveWithContext(context.Background())\n}\n\n// IsExpired returns true if the credentials are expired.\nfunc (e *ECSProvider) IsExpired() bool {\n\treturn e.expiration.Before(time.Now())\n}\n"
  },
  {
    "path": "internal/credproviders/env_provider.go",
    "content": "// Copyright (C) MongoDB, Inc. 2023-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage credproviders\n\nimport (\n\t\"os\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/aws/credentials\"\n)\n\n// envProviderName provides a name of Env provider\nconst envProviderName = \"EnvProvider\"\n\n// EnvVar is an environment variable\ntype EnvVar string\n\n// Get retrieves the environment variable\nfunc (ev EnvVar) Get() string {\n\treturn os.Getenv(string(ev))\n}\n\n// A EnvProvider retrieves credentials from the environment variables of the\n// running process. Environment credentials never expire.\ntype EnvProvider struct {\n\tAwsAccessKeyIDEnv     EnvVar\n\tAwsSecretAccessKeyEnv EnvVar\n\tAwsSessionTokenEnv    EnvVar\n\n\tretrieved bool\n}\n\n// NewEnvProvider returns a pointer to an ECS credential provider.\nfunc NewEnvProvider() *EnvProvider {\n\treturn &EnvProvider{\n\t\t// AwsAccessKeyIDEnv is the environment variable for AWS_ACCESS_KEY_ID\n\t\tAwsAccessKeyIDEnv: EnvVar(\"AWS_ACCESS_KEY_ID\"),\n\t\t// AwsSecretAccessKeyEnv is the environment variable for AWS_SECRET_ACCESS_KEY\n\t\tAwsSecretAccessKeyEnv: EnvVar(\"AWS_SECRET_ACCESS_KEY\"),\n\t\t// AwsSessionTokenEnv is the environment variable for AWS_SESSION_TOKEN\n\t\tAwsSessionTokenEnv: EnvVar(\"AWS_SESSION_TOKEN\"),\n\t}\n}\n\n// Retrieve retrieves the keys from the environment.\nfunc (e *EnvProvider) Retrieve() (credentials.Value, error) {\n\te.retrieved = false\n\n\tv := credentials.Value{\n\t\tAccessKeyID:     e.AwsAccessKeyIDEnv.Get(),\n\t\tSecretAccessKey: e.AwsSecretAccessKeyEnv.Get(),\n\t\tSessionToken:    e.AwsSessionTokenEnv.Get(),\n\t\tProviderName:    envProviderName,\n\t}\n\terr := verify(v)\n\tif err == nil {\n\t\te.retrieved = true\n\t}\n\n\treturn v, err\n}\n\n// IsExpired returns true if the credentials have not been retrieved.\nfunc (e *EnvProvider) IsExpired() bool {\n\treturn !e.retrieved\n}\n"
  },
  {
    "path": "internal/credproviders/imds_provider.go",
    "content": "// Copyright (C) MongoDB, Inc. 2023-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage credproviders\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io/ioutil\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/aws/credentials\"\n)\n\nconst (\n\t// AzureProviderName provides a name of Azure provider\n\tAzureProviderName = \"AzureProvider\"\n\n\tazureURI = \"http://169.254.169.254/metadata/identity/oauth2/token\"\n)\n\n// An AzureProvider retrieves credentials from Azure IMDS.\ntype AzureProvider struct {\n\thttpClient   *http.Client\n\texpiration   time.Time\n\texpiryWindow time.Duration\n}\n\n// NewAzureProvider returns a pointer to an Azure credential provider.\nfunc NewAzureProvider(httpClient *http.Client, expiryWindow time.Duration) *AzureProvider {\n\treturn &AzureProvider{\n\t\thttpClient:   httpClient,\n\t\texpiration:   time.Time{},\n\t\texpiryWindow: expiryWindow,\n\t}\n}\n\n// RetrieveWithContext retrieves the keys from the Azure service.\nfunc (a *AzureProvider) RetrieveWithContext(ctx context.Context) (credentials.Value, error) {\n\tv := credentials.Value{ProviderName: AzureProviderName}\n\treq, err := http.NewRequest(http.MethodGet, azureURI, nil)\n\tif err != nil {\n\t\treturn v, fmt.Errorf(\"unable to retrieve Azure credentials: %w\", err)\n\t}\n\tq := make(url.Values)\n\tq.Set(\"api-version\", \"2018-02-01\")\n\tq.Set(\"resource\", \"https://vault.azure.net\")\n\treq.URL.RawQuery = q.Encode()\n\treq.Header.Set(\"Metadata\", \"true\")\n\treq.Header.Set(\"Accept\", \"application/json\")\n\n\tresp, err := a.httpClient.Do(req.WithContext(ctx))\n\tif err != nil {\n\t\treturn v, fmt.Errorf(\"unable to retrieve Azure credentials: %w\", err)\n\t}\n\tdefer resp.Body.Close()\n\tbody, err := ioutil.ReadAll(resp.Body)\n\tif err != nil {\n\t\treturn v, fmt.Errorf(\"unable to retrieve Azure credentials: error reading response body: %w\", err)\n\t}\n\tif resp.StatusCode != http.StatusOK {\n\t\treturn v, fmt.Errorf(\"unable to retrieve Azure credentials: expected StatusCode 200, got StatusCode: %v. Response body: %s\", resp.StatusCode, body)\n\t}\n\tvar tokenResponse struct {\n\t\tAccessToken string `json:\"access_token\"`\n\t\tExpiresIn   string `json:\"expires_in\"`\n\t}\n\t// Attempt to read body as JSON\n\terr = json.Unmarshal(body, &tokenResponse)\n\tif err != nil {\n\t\treturn v, fmt.Errorf(\"unable to retrieve Azure credentials: error reading body JSON: %w (response body: %s)\", err, body)\n\t}\n\tif tokenResponse.AccessToken == \"\" {\n\t\treturn v, fmt.Errorf(\"unable to retrieve Azure credentials: got unexpected empty accessToken from Azure Metadata Server. Response body: %s\", body)\n\t}\n\tv.SessionToken = tokenResponse.AccessToken\n\n\texpiresIn, err := time.ParseDuration(tokenResponse.ExpiresIn + \"s\")\n\tif err != nil {\n\t\treturn v, err\n\t}\n\tif expiration := expiresIn - a.expiryWindow; expiration > 0 {\n\t\ta.expiration = time.Now().Add(expiration)\n\t}\n\n\treturn v, err\n}\n\n// Retrieve retrieves the keys from the Azure service.\nfunc (a *AzureProvider) Retrieve() (credentials.Value, error) {\n\treturn a.RetrieveWithContext(context.Background())\n}\n\n// IsExpired returns if the credentials have been retrieved.\nfunc (a *AzureProvider) IsExpired() bool {\n\treturn a.expiration.Before(time.Now())\n}\n"
  },
  {
    "path": "internal/credproviders/static_provider.go",
    "content": "// Copyright (C) MongoDB, Inc. 2023-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage credproviders\n\nimport (\n\t\"errors\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/aws/credentials\"\n)\n\n// staticProviderName provides a name of Static provider\nconst staticProviderName = \"StaticProvider\"\n\n// A StaticProvider is a set of credentials which are set programmatically,\n// and will never expire.\ntype StaticProvider struct {\n\tcredentials.Value\n\n\tverified bool\n\terr      error\n}\n\nfunc verify(v credentials.Value) error {\n\tif !v.HasKeys() {\n\t\treturn errors.New(\"failed to retrieve ACCESS_KEY_ID and SECRET_ACCESS_KEY\")\n\t}\n\tif v.AccessKeyID != \"\" && v.SecretAccessKey == \"\" {\n\t\treturn errors.New(\"ACCESS_KEY_ID is set, but SECRET_ACCESS_KEY is missing\")\n\t}\n\tif v.AccessKeyID == \"\" && v.SecretAccessKey != \"\" {\n\t\treturn errors.New(\"SECRET_ACCESS_KEY is set, but ACCESS_KEY_ID is missing\")\n\t}\n\tif v.AccessKeyID == \"\" && v.SecretAccessKey == \"\" && v.SessionToken != \"\" {\n\t\treturn errors.New(\"AWS_SESSION_TOKEN is set, but ACCESS_KEY_ID and SECRET_ACCESS_KEY are missing\")\n\t}\n\treturn nil\n}\n\n// Retrieve returns the credentials or error if the credentials are invalid.\nfunc (s *StaticProvider) Retrieve() (credentials.Value, error) {\n\tif !s.verified {\n\t\ts.err = verify(s.Value)\n\t\ts.ProviderName = staticProviderName\n\t\ts.verified = true\n\t}\n\treturn s.Value, s.err\n}\n\n// IsExpired returns if the credentials are expired.\n//\n// For StaticProvider, the credentials never expired.\nfunc (s *StaticProvider) IsExpired() bool {\n\treturn false\n}\n"
  },
  {
    "path": "internal/csfle/csfle.go",
    "content": "// Copyright (C) MongoDB, Inc. 2022-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage csfle\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n)\n\nconst (\n\tEncryptedCacheCollection      = \"ecc\"\n\tEncryptedStateCollection      = \"esc\"\n\tEncryptedCompactionCollection = \"ecoc\"\n)\n\n// GetEncryptedStateCollectionName returns the encrypted state collection name associated with dataCollectionName.\nfunc GetEncryptedStateCollectionName(efBSON bsoncore.Document, dataCollectionName string, stateCollection string) (string, error) {\n\tfieldName := stateCollection + \"Collection\"\n\tval, err := efBSON.LookupErr(fieldName)\n\tif err != nil {\n\t\tif !errors.Is(err, bsoncore.ErrElementNotFound) {\n\t\t\treturn \"\", err\n\t\t}\n\t\t// Return default name.\n\t\tdefaultName := \"enxcol_.\" + dataCollectionName + \".\" + stateCollection\n\t\treturn defaultName, nil\n\t}\n\n\tstateCollectionName, ok := val.StringValueOK()\n\tif !ok {\n\t\treturn \"\", fmt.Errorf(\"expected string for '%v', got: %v\", fieldName, val.Type)\n\t}\n\treturn stateCollectionName, nil\n}\n"
  },
  {
    "path": "internal/csot/csot.go",
    "content": "// Copyright (C) MongoDB, Inc. 2022-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage csot\n\nimport (\n\t\"context\"\n\t\"time\"\n)\n\ntype clientLevel struct{}\n\nfunc isClientLevel(ctx context.Context) bool {\n\tval := ctx.Value(clientLevel{})\n\tif val == nil {\n\t\treturn false\n\t}\n\n\treturn val.(bool)\n}\n\n// IsTimeoutContext checks if the provided context has been assigned a deadline\n// or has unlimited retries.\nfunc IsTimeoutContext(ctx context.Context) bool {\n\t_, ok := ctx.Deadline()\n\n\treturn ok || isClientLevel(ctx)\n}\n\n// WithTimeout will set the given timeout on the context, if no deadline has\n// already been set.\n//\n// This function assumes that the timeout field is static, given that the\n// timeout should be sourced from the client. Therefore, once a timeout function\n// parameter has  been applied to the context, it will remain for the lifetime\n// of the context.\nfunc WithTimeout(parent context.Context, timeout *time.Duration) (context.Context, context.CancelFunc) {\n\tcancel := func() {}\n\n\tif timeout == nil || IsTimeoutContext(parent) {\n\t\t// In the following conditions, do nothing:\n\t\t//\t1. The parent already has a deadline\n\t\t//\t2. The parent does not have a deadline, but a client-level timeout has\n\t\t//\t\t been applied.\n\t\t//\t3. The parent does not have a deadline, there is not client-level\n\t\t//     timeout, and the timeout parameter DNE.\n\t\treturn parent, cancel\n\t}\n\n\t// If a client-level timeout has not been applied, then apply it.\n\tparent = context.WithValue(parent, clientLevel{}, true)\n\n\tdur := *timeout\n\n\tif dur == 0 {\n\t\t// If the parent does not have a deadline and the timeout is zero, then\n\t\t// do nothing.\n\t\treturn parent, cancel\n\t}\n\n\t// If the parent does not have a dealine and the timeout is non-zero, then\n\t// apply the timeout.\n\treturn context.WithTimeout(parent, dur)\n}\n\n// WithServerSelectionTimeout creates a context with a timeout that is the\n// minimum of serverSelectionTimeoutMS and context deadline. The usage of\n// non-positive values for serverSelectionTimeoutMS are an anti-pattern and are\n// not considered in this calculation.\nfunc WithServerSelectionTimeout(\n\tparent context.Context,\n\tserverSelectionTimeout time.Duration,\n) (context.Context, context.CancelFunc) {\n\tif serverSelectionTimeout <= 0 {\n\t\treturn parent, func() {}\n\t}\n\n\treturn context.WithTimeout(parent, serverSelectionTimeout)\n}\n\n// ZeroRTTMonitor implements the RTTMonitor interface and is used internally for testing. It returns 0 for all\n// RTT calculations and an empty string for RTT statistics.\ntype ZeroRTTMonitor struct{}\n\n// EWMA implements the RTT monitor interface.\nfunc (zrm *ZeroRTTMonitor) EWMA() time.Duration {\n\treturn 0\n}\n\n// Min implements the RTT monitor interface.\nfunc (zrm *ZeroRTTMonitor) Min() time.Duration {\n\treturn 0\n}\n\n// P90 implements the RTT monitor interface.\nfunc (zrm *ZeroRTTMonitor) P90() time.Duration {\n\treturn 0\n}\n\n// Stats implements the RTT monitor interface.\nfunc (zrm *ZeroRTTMonitor) Stats() string {\n\treturn \"\"\n}\n"
  },
  {
    "path": "internal/csot/csot_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2024-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage csot\n\nimport (\n\t\"context\"\n\t\"testing\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/ptrutil\"\n)\n\nfunc newTestContext(t *testing.T, timeout time.Duration) context.Context {\n\tctx, cancel := context.WithTimeout(context.Background(), timeout)\n\tt.Cleanup(cancel)\n\n\treturn ctx\n}\n\nfunc TestWithServerSelectionTimeout(t *testing.T) {\n\tt.Parallel()\n\n\ttests := []struct {\n\t\tname                   string\n\t\tparent                 context.Context\n\t\tserverSelectionTimeout time.Duration\n\t\twantTimeout            time.Duration\n\t\twantOk                 bool\n\t}{\n\t\t{\n\t\t\tname:                   \"no context deadine and ssto is zero\",\n\t\t\tparent:                 context.Background(),\n\t\t\tserverSelectionTimeout: 0,\n\t\t\twantTimeout:            0,\n\t\t\twantOk:                 false,\n\t\t},\n\t\t{\n\t\t\tname:                   \"no context deadline and ssto is positive\",\n\t\t\tparent:                 context.Background(),\n\t\t\tserverSelectionTimeout: 1,\n\t\t\twantTimeout:            1,\n\t\t\twantOk:                 true,\n\t\t},\n\t\t{\n\t\t\tname:                   \"no context deadline and ssto is negative\",\n\t\t\tparent:                 context.Background(),\n\t\t\tserverSelectionTimeout: -1,\n\t\t\twantTimeout:            0,\n\t\t\twantOk:                 false,\n\t\t},\n\t\t{\n\t\t\tname:                   \"context deadline is zero and ssto is positive\",\n\t\t\tparent:                 newTestContext(t, 0),\n\t\t\tserverSelectionTimeout: 1,\n\t\t\twantTimeout:            1,\n\t\t\twantOk:                 true,\n\t\t},\n\t\t{\n\t\t\tname:                   \"context deadline is zero and ssto is negative\",\n\t\t\tparent:                 newTestContext(t, 0),\n\t\t\tserverSelectionTimeout: -1,\n\t\t\twantTimeout:            0,\n\t\t\twantOk:                 true,\n\t\t},\n\t\t{\n\t\t\tname:                   \"context deadline is negative and ssto is zero\",\n\t\t\tparent:                 newTestContext(t, -1),\n\t\t\tserverSelectionTimeout: 0,\n\t\t\twantTimeout:            -1,\n\t\t\twantOk:                 true,\n\t\t},\n\t\t{\n\t\t\tname:                   \"context deadline is negative and ssto is positive\",\n\t\t\tparent:                 newTestContext(t, -1),\n\t\t\tserverSelectionTimeout: 1,\n\t\t\twantTimeout:            1,\n\t\t\twantOk:                 true,\n\t\t},\n\t\t{\n\t\t\tname:                   \"context deadline is negative and ssto is negative\",\n\t\t\tparent:                 newTestContext(t, -1),\n\t\t\tserverSelectionTimeout: -1,\n\t\t\twantTimeout:            -1,\n\t\t\twantOk:                 true,\n\t\t},\n\t\t{\n\t\t\tname:                   \"context deadline is positive and ssto is zero\",\n\t\t\tparent:                 newTestContext(t, 1),\n\t\t\tserverSelectionTimeout: 0,\n\t\t\twantTimeout:            1,\n\t\t\twantOk:                 true,\n\t\t},\n\t\t{\n\t\t\tname:                   \"context deadline is positive and equal to ssto\",\n\t\t\tparent:                 newTestContext(t, 1),\n\t\t\tserverSelectionTimeout: 1,\n\t\t\twantTimeout:            1,\n\t\t\twantOk:                 true,\n\t\t},\n\t\t{\n\t\t\tname:                   \"context deadline is positive lt ssto\",\n\t\t\tparent:                 newTestContext(t, 1),\n\t\t\tserverSelectionTimeout: 2,\n\t\t\twantTimeout:            2,\n\t\t\twantOk:                 true,\n\t\t},\n\t\t{\n\t\t\tname:                   \"context deadline is positive gt ssto\",\n\t\t\tparent:                 newTestContext(t, 2),\n\t\t\tserverSelectionTimeout: 1,\n\t\t\twantTimeout:            2,\n\t\t\twantOk:                 true,\n\t\t},\n\t\t{\n\t\t\tname:                   \"context deadline is positive and ssto is negative\",\n\t\t\tparent:                 newTestContext(t, -1),\n\t\t\tserverSelectionTimeout: -1,\n\t\t\twantTimeout:            1,\n\t\t\twantOk:                 true,\n\t\t},\n\t}\n\n\tfor _, test := range tests {\n\t\ttest := test // Capture the range variable\n\n\t\tt.Run(test.name, func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tctx, cancel := WithServerSelectionTimeout(test.parent, test.serverSelectionTimeout)\n\t\t\tt.Cleanup(cancel)\n\n\t\t\tdeadline, gotOk := ctx.Deadline()\n\t\t\tassert.Equal(t, test.wantOk, gotOk)\n\n\t\t\tif gotOk {\n\t\t\t\tdelta := time.Until(deadline) - test.wantTimeout\n\t\t\t\ttolerance := 10 * time.Millisecond\n\n\t\t\t\tassert.True(t, delta > -1*tolerance, \"expected delta=%d > %d\", delta, -1*tolerance)\n\t\t\t\tassert.True(t, delta <= tolerance, \"expected delta=%d <= %d\", delta, tolerance)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestWithTimeout(t *testing.T) {\n\tt.Parallel()\n\n\ttests := []struct {\n\t\tname         string\n\t\tparent       context.Context\n\t\ttimeout      *time.Duration\n\t\twantTimeout  time.Duration\n\t\twantDeadline bool\n\t\twantValues   []any\n\t}{\n\t\t{\n\t\t\tname:         \"deadline set with non-zero timeout\",\n\t\t\tparent:       newTestContext(t, 1),\n\t\t\ttimeout:      ptrutil.Ptr(time.Duration(2)),\n\t\t\twantTimeout:  1,\n\t\t\twantDeadline: true,\n\t\t\twantValues:   []any{},\n\t\t},\n\t\t{\n\t\t\tname:         \"deadline set with zero timeout\",\n\t\t\tparent:       newTestContext(t, 1),\n\t\t\ttimeout:      ptrutil.Ptr(time.Duration(0)),\n\t\t\twantTimeout:  1,\n\t\t\twantDeadline: true,\n\t\t\twantValues:   []any{},\n\t\t},\n\t\t{\n\t\t\tname:         \"deadline set with nil timeout\",\n\t\t\tparent:       newTestContext(t, 1),\n\t\t\ttimeout:      nil,\n\t\t\twantTimeout:  1,\n\t\t\twantDeadline: true,\n\t\t\twantValues:   []any{},\n\t\t},\n\t\t{\n\t\t\tname:         \"deadline unset with non-zero timeout\",\n\t\t\tparent:       context.Background(),\n\t\t\ttimeout:      ptrutil.Ptr(time.Duration(1)),\n\t\t\twantTimeout:  1,\n\t\t\twantDeadline: true,\n\t\t\twantValues:   []any{},\n\t\t},\n\t\t{\n\t\t\tname:         \"deadline unset with zero timeout\",\n\t\t\tparent:       context.Background(),\n\t\t\ttimeout:      ptrutil.Ptr(time.Duration(0)),\n\t\t\twantTimeout:  0,\n\t\t\twantDeadline: false,\n\t\t\twantValues:   []any{clientLevel{}},\n\t\t},\n\t\t{\n\t\t\tname:         \"deadline unset with nil timeout\",\n\t\t\tparent:       context.Background(),\n\t\t\ttimeout:      nil,\n\t\t\twantTimeout:  0,\n\t\t\twantDeadline: false,\n\t\t\twantValues:   []any{},\n\t\t},\n\t\t{\n\t\t\t// If \"clientLevel\" has been set, but a new timeout is applied\n\t\t\t// to the context, then the constructed context should retain the old\n\t\t\t// timeout. To simplify the code, we assume the first timeout is static.\n\t\t\tname:         \"deadline unset with non-zero timeout at clientLevel\",\n\t\t\tparent:       context.WithValue(context.Background(), clientLevel{}, true),\n\t\t\ttimeout:      ptrutil.Ptr(time.Duration(1)),\n\t\t\twantTimeout:  0,\n\t\t\twantDeadline: false,\n\t\t\twantValues:   []any{},\n\t\t},\n\t}\n\n\tfor _, test := range tests {\n\t\ttest := test // Capture the range variable\n\n\t\tt.Run(test.name, func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tctx, cancel := WithTimeout(test.parent, test.timeout)\n\t\t\tt.Cleanup(cancel)\n\n\t\t\tdeadline, gotDeadline := ctx.Deadline()\n\t\t\tassert.Equal(t, test.wantDeadline, gotDeadline)\n\n\t\t\tif gotDeadline {\n\t\t\t\tdelta := time.Until(deadline) - test.wantTimeout\n\t\t\t\ttolerance := 10 * time.Millisecond\n\n\t\t\t\tassert.True(t, delta > -1*tolerance, \"expected delta=%d > %d\", delta, -1*tolerance)\n\t\t\t\tassert.True(t, delta <= tolerance, \"expected delta=%d <= %d\", delta, tolerance)\n\t\t\t}\n\n\t\t\tfor _, wantValue := range test.wantValues {\n\t\t\t\tassert.NotNil(t, ctx.Value(wantValue), \"expected context to have value %v\", wantValue)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "internal/decimal128/decimal128.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage decimal128\n\nimport (\n\t\"strconv\"\n)\n\n// These constants are the maximum and minimum values for the exponent field in a decimal128 value.\nconst (\n\tMaxDecimal128Exp = 6111\n\tMinDecimal128Exp = -6176\n)\n\nfunc divmod(h, l uint64, div uint32) (qh, ql uint64, rem uint32) {\n\tdiv64 := uint64(div)\n\ta := h >> 32\n\taq := a / div64\n\tar := a % div64\n\tb := ar<<32 + h&(1<<32-1)\n\tbq := b / div64\n\tbr := b % div64\n\tc := br<<32 + l>>32\n\tcq := c / div64\n\tcr := c % div64\n\td := cr<<32 + l&(1<<32-1)\n\tdq := d / div64\n\tdr := d % div64\n\treturn (aq<<32 | bq), (cq<<32 | dq), uint32(dr)\n}\n\n// String returns a string representation of the decimal value.\nfunc String(h, l uint64) string {\n\tvar posSign int      // positive sign\n\tvar exp int          // exponent\n\tvar high, low uint64 // significand high/low\n\n\tif h>>63&1 == 0 {\n\t\tposSign = 1\n\t}\n\n\tswitch h >> 58 & (1<<5 - 1) {\n\tcase 0x1F:\n\t\treturn \"NaN\"\n\tcase 0x1E:\n\t\treturn \"-Infinity\"[posSign:]\n\t}\n\n\tlow = l\n\tif h>>61&3 == 3 {\n\t\t// Bits: 1*sign 2*ignored 14*exponent 111*significand.\n\t\t// Implicit 0b100 prefix in significand.\n\t\texp = int(h >> 47 & (1<<14 - 1))\n\t\t// Spec says all of these values are out of range.\n\t\thigh, low = 0, 0\n\t} else {\n\t\t// Bits: 1*sign 14*exponent 113*significand\n\t\texp = int(h >> 49 & (1<<14 - 1))\n\t\thigh = h & (1<<49 - 1)\n\t}\n\texp += MinDecimal128Exp\n\n\t// Would be handled by the logic below, but that's trivial and common.\n\tif high == 0 && low == 0 && exp == 0 {\n\t\treturn \"-0\"[posSign:]\n\t}\n\n\tvar repr [48]byte // Loop 5 times over 9 digits plus dot, negative sign, and leading zero.\n\tlast := len(repr)\n\ti := len(repr)\n\tdot := len(repr) + exp\n\tvar rem uint32\nLoop:\n\tfor d9 := 0; d9 < 5; d9++ {\n\t\thigh, low, rem = divmod(high, low, 1e9)\n\t\tfor d1 := 0; d1 < 9; d1++ {\n\t\t\t// Handle \"-0.0\", \"0.00123400\", \"-1.00E-6\", \"1.050E+3\", etc.\n\t\t\tif i < len(repr) && (dot == i || low == 0 && high == 0 && rem > 0 && rem < 10 && (dot < i-6 || exp > 0)) {\n\t\t\t\texp += len(repr) - i\n\t\t\t\ti--\n\t\t\t\trepr[i] = '.'\n\t\t\t\tlast = i - 1\n\t\t\t\tdot = len(repr) // Unmark.\n\t\t\t}\n\t\t\tc := '0' + byte(rem%10)\n\t\t\trem /= 10\n\t\t\ti--\n\t\t\trepr[i] = c\n\t\t\t// Handle \"0E+3\", \"1E+3\", etc.\n\t\t\tif low == 0 && high == 0 && rem == 0 && i == len(repr)-1 && (dot < i-5 || exp > 0) {\n\t\t\t\tlast = i\n\t\t\t\tbreak Loop\n\t\t\t}\n\t\t\tif c != '0' {\n\t\t\t\tlast = i\n\t\t\t}\n\t\t\t// Break early. Works without it, but why.\n\t\t\tif dot > i && low == 0 && high == 0 && rem == 0 {\n\t\t\t\tbreak Loop\n\t\t\t}\n\t\t}\n\t}\n\trepr[last-1] = '-'\n\tlast--\n\n\tif exp > 0 {\n\t\treturn string(repr[last+posSign:]) + \"E+\" + strconv.Itoa(exp)\n\t}\n\tif exp < 0 {\n\t\treturn string(repr[last+posSign:]) + \"E\" + strconv.Itoa(exp)\n\t}\n\treturn string(repr[last+posSign:])\n}\n"
  },
  {
    "path": "internal/docexamples/README",
    "content": "Go Driver Documentation Examples\n================================\n\nexamples.go contains numerous documentation examples that are rendered in various locations on www.mongodb.com/docs.\nEach example is delineated with starting and ending comments (// Start/End Example #). All examples in examples.go\nare valid Go driver code. Each example is run as part of our CI with examples_test.go, so all code is up-to-date and\naccurate. We recommend using www.mongodb.com/docs to interact with these examples rather than viewing these files\ndirectly.\n"
  },
  {
    "path": "internal/docexamples/examples.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage docexamples\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"io/ioutil\"\n\tlogger \"log\"\n\t\"strings\"\n\t\"sync/atomic\"\n\t\"testing\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/integration/mtest\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/readconcern\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/readpref\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/writeconcern\"\n)\n\nfunc requireCursorLength(t *testing.T, cursor *mongo.Cursor, length int) {\n\ti := 0\n\tfor cursor.Next(context.Background()) {\n\t\ti++\n\t}\n\n\tassert.NoError(t, cursor.Err())\n\tassert.Equal(t, i, length)\n}\n\nfunc containsKey(doc bson.Raw, key ...string) bool {\n\t_, err := doc.LookupErr(key...)\n\treturn err == nil\n}\n\nfunc parseDate(t *testing.T, dateString string) time.Time {\n\trfc3339MilliLayout := \"2006-01-02T15:04:05.999Z07:00\" // layout defined with Go reference time\n\tparsedDate, err := time.Parse(rfc3339MilliLayout, dateString)\n\n\tassert.NoError(t, err)\n\treturn parsedDate\n}\n\n// InsertExamples contains examples for insert operations.\n// Appears at https://www.mongodb.com/docs/manual/tutorial/insert-documents/.\nfunc InsertExamples(t *testing.T, db *mongo.Database) {\n\tcoll := db.Collection(\"inventory_insert\")\n\n\terr := coll.Drop(context.Background())\n\tassert.NoError(t, err)\n\n\t{\n\t\t// Start Example 1\n\n\t\tresult, err := coll.InsertOne(\n\t\t\tcontext.TODO(),\n\t\t\tbson.D{\n\t\t\t\t{\"item\", \"canvas\"},\n\t\t\t\t{\"qty\", 100},\n\t\t\t\t{\"tags\", bson.A{\"cotton\"}},\n\t\t\t\t{\"size\", bson.D{\n\t\t\t\t\t{\"h\", 28},\n\t\t\t\t\t{\"w\", 35.5},\n\t\t\t\t\t{\"uom\", \"cm\"},\n\t\t\t\t}},\n\t\t\t})\n\n\t\t// End Example 1\n\n\t\tassert.NoError(t, err)\n\t\tassert.NotNil(t, result.InsertedID)\n\t}\n\n\t{\n\t\t// Start Example 2\n\n\t\tcursor, err := coll.Find(\n\t\t\tcontext.TODO(),\n\t\t\tbson.D{{\"item\", \"canvas\"}},\n\t\t)\n\n\t\t// End Example 2\n\n\t\tassert.NoError(t, err)\n\t\trequireCursorLength(t, cursor, 1)\n\t}\n\n\t{\n\t\t// Start Example 3\n\n\t\tresult, err := coll.InsertMany(\n\t\t\tcontext.TODO(),\n\t\t\t[]any{\n\t\t\t\tbson.D{\n\t\t\t\t\t{\"item\", \"journal\"},\n\t\t\t\t\t{\"qty\", int32(25)},\n\t\t\t\t\t{\"tags\", bson.A{\"blank\", \"red\"}},\n\t\t\t\t\t{\"size\", bson.D{\n\t\t\t\t\t\t{\"h\", 14},\n\t\t\t\t\t\t{\"w\", 21},\n\t\t\t\t\t\t{\"uom\", \"cm\"},\n\t\t\t\t\t}},\n\t\t\t\t},\n\t\t\t\tbson.D{\n\t\t\t\t\t{\"item\", \"mat\"},\n\t\t\t\t\t{\"qty\", int32(25)},\n\t\t\t\t\t{\"tags\", bson.A{\"gray\"}},\n\t\t\t\t\t{\"size\", bson.D{\n\t\t\t\t\t\t{\"h\", 27.9},\n\t\t\t\t\t\t{\"w\", 35.5},\n\t\t\t\t\t\t{\"uom\", \"cm\"},\n\t\t\t\t\t}},\n\t\t\t\t},\n\t\t\t\tbson.D{\n\t\t\t\t\t{\"item\", \"mousepad\"},\n\t\t\t\t\t{\"qty\", 25},\n\t\t\t\t\t{\"tags\", bson.A{\"gel\", \"blue\"}},\n\t\t\t\t\t{\"size\", bson.D{\n\t\t\t\t\t\t{\"h\", 19},\n\t\t\t\t\t\t{\"w\", 22.85},\n\t\t\t\t\t\t{\"uom\", \"cm\"},\n\t\t\t\t\t}},\n\t\t\t\t},\n\t\t\t})\n\n\t\t// End Example 3\n\n\t\tassert.NoError(t, err)\n\t\tassert.Len(t, result.InsertedIDs, 3)\n\t}\n}\n\n// QueryToplevelFieldsExamples contains examples for querying top-level fields.\n// Appears at https://www.mongodb.com/docs/manual/tutorial/query-documents/.\nfunc QueryToplevelFieldsExamples(t *testing.T, db *mongo.Database) {\n\tcoll := db.Collection(\"inventory_query_top\")\n\n\terr := coll.Drop(context.Background())\n\tassert.NoError(t, err)\n\n\t{\n\t\t// Start Example 6\n\n\t\tdocs := []any{\n\t\t\tbson.D{\n\t\t\t\t{\"item\", \"journal\"},\n\t\t\t\t{\"qty\", 25},\n\t\t\t\t{\"size\", bson.D{\n\t\t\t\t\t{\"h\", 14},\n\t\t\t\t\t{\"w\", 21},\n\t\t\t\t\t{\"uom\", \"cm\"},\n\t\t\t\t}},\n\t\t\t\t{\"status\", \"A\"},\n\t\t\t},\n\t\t\tbson.D{\n\t\t\t\t{\"item\", \"notebook\"},\n\t\t\t\t{\"qty\", 50},\n\t\t\t\t{\"size\", bson.D{\n\t\t\t\t\t{\"h\", 8.5},\n\t\t\t\t\t{\"w\", 11},\n\t\t\t\t\t{\"uom\", \"in\"},\n\t\t\t\t}},\n\t\t\t\t{\"status\", \"A\"},\n\t\t\t},\n\t\t\tbson.D{\n\t\t\t\t{\"item\", \"paper\"},\n\t\t\t\t{\"qty\", 100},\n\t\t\t\t{\"size\", bson.D{\n\t\t\t\t\t{\"h\", 8.5},\n\t\t\t\t\t{\"w\", 11},\n\t\t\t\t\t{\"uom\", \"in\"},\n\t\t\t\t}},\n\t\t\t\t{\"status\", \"D\"},\n\t\t\t},\n\t\t\tbson.D{\n\t\t\t\t{\"item\", \"planner\"},\n\t\t\t\t{\"qty\", 75},\n\t\t\t\t{\"size\", bson.D{\n\t\t\t\t\t{\"h\", 22.85},\n\t\t\t\t\t{\"w\", 30},\n\t\t\t\t\t{\"uom\", \"cm\"},\n\t\t\t\t}},\n\t\t\t\t{\"status\", \"D\"},\n\t\t\t},\n\t\t\tbson.D{\n\t\t\t\t{\"item\", \"postcard\"},\n\t\t\t\t{\"qty\", 45},\n\t\t\t\t{\"size\", bson.D{\n\t\t\t\t\t{\"h\", 10},\n\t\t\t\t\t{\"w\", 15.25},\n\t\t\t\t\t{\"uom\", \"cm\"},\n\t\t\t\t}},\n\t\t\t\t{\"status\", \"A\"},\n\t\t\t},\n\t\t}\n\n\t\tresult, err := coll.InsertMany(context.TODO(), docs)\n\n\t\t// End Example 6\n\n\t\tassert.NoError(t, err)\n\t\tassert.Len(t, result.InsertedIDs, 5)\n\t}\n\n\t{\n\t\t// Start Example 7\n\n\t\tcursor, err := coll.Find(\n\t\t\tcontext.TODO(),\n\t\t\tbson.D{},\n\t\t)\n\n\t\t// End Example 7\n\n\t\tassert.NoError(t, err)\n\t\trequireCursorLength(t, cursor, 5)\n\t}\n\n\t{\n\t\t// Start Example 9\n\n\t\tcursor, err := coll.Find(\n\t\t\tcontext.TODO(),\n\t\t\tbson.D{{\"status\", \"D\"}},\n\t\t)\n\n\t\t// End Example 9\n\n\t\tassert.NoError(t, err)\n\t\trequireCursorLength(t, cursor, 2)\n\t}\n\n\t{\n\t\t// Start Example 10\n\n\t\tcursor, err := coll.Find(\n\t\t\tcontext.TODO(),\n\t\t\tbson.D{{\"status\", bson.D{{\"$in\", bson.A{\"A\", \"D\"}}}}})\n\n\t\t// End Example 10\n\n\t\tassert.NoError(t, err)\n\t\trequireCursorLength(t, cursor, 5)\n\t}\n\n\t{\n\t\t// Start Example 11\n\n\t\tcursor, err := coll.Find(\n\t\t\tcontext.TODO(),\n\t\t\tbson.D{\n\t\t\t\t{\"status\", \"A\"},\n\t\t\t\t{\"qty\", bson.D{{\"$lt\", 30}}},\n\t\t\t})\n\n\t\t// End Example 11\n\n\t\tassert.NoError(t, err)\n\t\trequireCursorLength(t, cursor, 1)\n\t}\n\n\t{\n\t\t// Start Example 12\n\n\t\tcursor, err := coll.Find(\n\t\t\tcontext.TODO(),\n\t\t\tbson.D{\n\t\t\t\t{\n\t\t\t\t\t\"$or\",\n\t\t\t\t\tbson.A{\n\t\t\t\t\t\tbson.D{{\"status\", \"A\"}},\n\t\t\t\t\t\tbson.D{{\"qty\", bson.D{{\"$lt\", 30}}}},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t})\n\n\t\t// End Example 12\n\n\t\tassert.NoError(t, err)\n\t\trequireCursorLength(t, cursor, 3)\n\t}\n\n\t{\n\t\t// Start Example 13\n\n\t\tcursor, err := coll.Find(\n\t\t\tcontext.TODO(),\n\t\t\tbson.D{\n\t\t\t\t{\"status\", \"A\"},\n\t\t\t\t{\"$or\", bson.A{\n\t\t\t\t\tbson.D{{\"qty\", bson.D{{\"$lt\", 30}}}},\n\t\t\t\t\tbson.D{{\"item\", bson.Regex{Pattern: \"^p\", Options: \"\"}}},\n\t\t\t\t}},\n\t\t\t})\n\n\t\t// End Example 13\n\n\t\tassert.NoError(t, err)\n\t\trequireCursorLength(t, cursor, 2)\n\t}\n}\n\n// QueryEmbeddedDocumentsExamples contains examples for querying embedded document fields.\n// Appears at https://www.mongodb.com/docs/manual/tutorial/query-embedded-documents/.\nfunc QueryEmbeddedDocumentsExamples(t *testing.T, db *mongo.Database) {\n\tcoll := db.Collection(\"inventory_query_embedded\")\n\n\terr := coll.Drop(context.Background())\n\tassert.NoError(t, err)\n\n\t{\n\t\t// Start Example 14\n\n\t\tdocs := []any{\n\t\t\tbson.D{\n\t\t\t\t{\"item\", \"journal\"},\n\t\t\t\t{\"qty\", 25},\n\t\t\t\t{\"size\", bson.D{\n\t\t\t\t\t{\"h\", 14},\n\t\t\t\t\t{\"w\", 21},\n\t\t\t\t\t{\"uom\", \"cm\"},\n\t\t\t\t}},\n\t\t\t\t{\"status\", \"A\"},\n\t\t\t},\n\t\t\tbson.D{\n\t\t\t\t{\"item\", \"notebook\"},\n\t\t\t\t{\"qty\", 50},\n\t\t\t\t{\"size\", bson.D{\n\t\t\t\t\t{\"h\", 8.5},\n\t\t\t\t\t{\"w\", 11},\n\t\t\t\t\t{\"uom\", \"in\"},\n\t\t\t\t}},\n\t\t\t\t{\"status\", \"A\"},\n\t\t\t},\n\t\t\tbson.D{\n\t\t\t\t{\"item\", \"paper\"},\n\t\t\t\t{\"qty\", 100},\n\t\t\t\t{\"size\", bson.D{\n\t\t\t\t\t{\"h\", 8.5},\n\t\t\t\t\t{\"w\", 11},\n\t\t\t\t\t{\"uom\", \"in\"},\n\t\t\t\t}},\n\t\t\t\t{\"status\", \"D\"},\n\t\t\t},\n\t\t\tbson.D{\n\t\t\t\t{\"item\", \"planner\"},\n\t\t\t\t{\"qty\", 75},\n\t\t\t\t{\"size\", bson.D{\n\t\t\t\t\t{\"h\", 22.85},\n\t\t\t\t\t{\"w\", 30},\n\t\t\t\t\t{\"uom\", \"cm\"},\n\t\t\t\t}},\n\t\t\t\t{\"status\", \"D\"},\n\t\t\t},\n\t\t\tbson.D{\n\t\t\t\t{\"item\", \"postcard\"},\n\t\t\t\t{\"qty\", 45},\n\t\t\t\t{\"size\", bson.D{\n\t\t\t\t\t{\"h\", 10},\n\t\t\t\t\t{\"w\", 15.25},\n\t\t\t\t\t{\"uom\", \"cm\"},\n\t\t\t\t}},\n\t\t\t\t{\"status\", \"A\"},\n\t\t\t},\n\t\t}\n\n\t\tresult, err := coll.InsertMany(context.TODO(), docs)\n\n\t\t// End Example 14\n\n\t\tassert.NoError(t, err)\n\t\tassert.Len(t, result.InsertedIDs, 5)\n\t}\n\n\t{\n\t\t// Start Example 15\n\n\t\tcursor, err := coll.Find(\n\t\t\tcontext.TODO(),\n\t\t\tbson.D{\n\t\t\t\t{\"size\", bson.D{\n\t\t\t\t\t{\"h\", 14},\n\t\t\t\t\t{\"w\", 21},\n\t\t\t\t\t{\"uom\", \"cm\"},\n\t\t\t\t}},\n\t\t\t})\n\n\t\t// End Example 15\n\n\t\tassert.NoError(t, err)\n\t\trequireCursorLength(t, cursor, 1)\n\t}\n\n\t{\n\t\t// Start Example 16\n\n\t\tcursor, err := coll.Find(\n\t\t\tcontext.TODO(),\n\t\t\tbson.D{\n\t\t\t\t{\"size\", bson.D{\n\t\t\t\t\t{\"w\", 21},\n\t\t\t\t\t{\"h\", 14},\n\t\t\t\t\t{\"uom\", \"cm\"},\n\t\t\t\t}},\n\t\t\t})\n\n\t\t// End Example 16\n\n\t\tassert.NoError(t, err)\n\t\trequireCursorLength(t, cursor, 0)\n\t}\n\n\t{\n\t\t// Start Example 17\n\n\t\tcursor, err := coll.Find(\n\t\t\tcontext.TODO(),\n\t\t\tbson.D{{\"size.uom\", \"in\"}},\n\t\t)\n\n\t\t// End Example 17\n\n\t\tassert.NoError(t, err)\n\t\trequireCursorLength(t, cursor, 2)\n\t}\n\n\t{\n\t\t// Start Example 18\n\n\t\tcursor, err := coll.Find(\n\t\t\tcontext.TODO(),\n\t\t\tbson.D{\n\t\t\t\t{\"size.h\", bson.D{\n\t\t\t\t\t{\"$lt\", 15},\n\t\t\t\t}},\n\t\t\t})\n\n\t\t// End Example 18\n\n\t\tassert.NoError(t, err)\n\t\trequireCursorLength(t, cursor, 4)\n\t}\n\n\t{\n\t\t// Start Example 19\n\n\t\tcursor, err := coll.Find(\n\t\t\tcontext.TODO(),\n\t\t\tbson.D{\n\t\t\t\t{\"size.h\", bson.D{\n\t\t\t\t\t{\"$lt\", 15},\n\t\t\t\t}},\n\t\t\t\t{\"size.uom\", \"in\"},\n\t\t\t\t{\"status\", \"D\"},\n\t\t\t})\n\n\t\t// End Example 19\n\n\t\tassert.NoError(t, err)\n\t\trequireCursorLength(t, cursor, 1)\n\t}\n}\n\n// QueryArraysExamples contains examples for querying array fields.\n// Appears at https://www.mongodb.com/docs/manual/tutorial/query-arrays/.\nfunc QueryArraysExamples(t *testing.T, db *mongo.Database) {\n\tcoll := db.Collection(\"inventory_query_array\")\n\n\terr := coll.Drop(context.Background())\n\tassert.NoError(t, err)\n\n\t{\n\t\t// Start Example 20\n\n\t\tdocs := []any{\n\t\t\tbson.D{\n\t\t\t\t{\"item\", \"journal\"},\n\t\t\t\t{\"qty\", 25},\n\t\t\t\t{\"tags\", bson.A{\"blank\", \"red\"}},\n\t\t\t\t{\"dim_cm\", bson.A{14, 21}},\n\t\t\t},\n\t\t\tbson.D{\n\t\t\t\t{\"item\", \"notebook\"},\n\t\t\t\t{\"qty\", 50},\n\t\t\t\t{\"tags\", bson.A{\"red\", \"blank\"}},\n\t\t\t\t{\"dim_cm\", bson.A{14, 21}},\n\t\t\t},\n\t\t\tbson.D{\n\t\t\t\t{\"item\", \"paper\"},\n\t\t\t\t{\"qty\", 100},\n\t\t\t\t{\"tags\", bson.A{\"red\", \"blank\", \"plain\"}},\n\t\t\t\t{\"dim_cm\", bson.A{14, 21}},\n\t\t\t},\n\t\t\tbson.D{\n\t\t\t\t{\"item\", \"planner\"},\n\t\t\t\t{\"qty\", 75},\n\t\t\t\t{\"tags\", bson.A{\"blank\", \"red\"}},\n\t\t\t\t{\"dim_cm\", bson.A{22.85, 30}},\n\t\t\t},\n\t\t\tbson.D{\n\t\t\t\t{\"item\", \"postcard\"},\n\t\t\t\t{\"qty\", 45},\n\t\t\t\t{\"tags\", bson.A{\"blue\"}},\n\t\t\t\t{\"dim_cm\", bson.A{10, 15.25}},\n\t\t\t},\n\t\t}\n\n\t\tresult, err := coll.InsertMany(context.TODO(), docs)\n\n\t\t// End Example 20\n\n\t\tassert.NoError(t, err)\n\t\tassert.Len(t, result.InsertedIDs, 5)\n\t}\n\n\t{\n\t\t// Start Example 21\n\n\t\tcursor, err := coll.Find(\n\t\t\tcontext.TODO(),\n\t\t\tbson.D{{\"tags\", bson.A{\"red\", \"blank\"}}},\n\t\t)\n\n\t\t// End Example 21\n\n\t\tassert.NoError(t, err)\n\t\trequireCursorLength(t, cursor, 1)\n\t}\n\n\t{\n\t\t// Start Example 22\n\n\t\tcursor, err := coll.Find(\n\t\t\tcontext.TODO(),\n\t\t\tbson.D{\n\t\t\t\t{\"tags\", bson.D{{\"$all\", bson.A{\"red\", \"blank\"}}}},\n\t\t\t})\n\n\t\t// End Example 22\n\n\t\tassert.NoError(t, err)\n\t\trequireCursorLength(t, cursor, 4)\n\t}\n\n\t{\n\t\t// Start Example 23\n\n\t\tcursor, err := coll.Find(\n\t\t\tcontext.TODO(),\n\t\t\tbson.D{\n\t\t\t\t{\"tags\", \"red\"},\n\t\t\t})\n\n\t\t// End Example 23\n\n\t\tassert.NoError(t, err)\n\t\trequireCursorLength(t, cursor, 4)\n\t}\n\n\t{\n\t\t// Start Example 24\n\n\t\tcursor, err := coll.Find(\n\t\t\tcontext.TODO(),\n\t\t\tbson.D{\n\t\t\t\t{\"dim_cm\", bson.D{\n\t\t\t\t\t{\"$gt\", 25},\n\t\t\t\t}},\n\t\t\t})\n\n\t\t// End Example 24\n\n\t\tassert.NoError(t, err)\n\t\trequireCursorLength(t, cursor, 1)\n\t}\n\n\t{\n\t\t// Start Example 25\n\n\t\tcursor, err := coll.Find(\n\t\t\tcontext.TODO(),\n\t\t\tbson.D{\n\t\t\t\t{\"dim_cm\", bson.D{\n\t\t\t\t\t{\"$gt\", 15},\n\t\t\t\t\t{\"$lt\", 20},\n\t\t\t\t}},\n\t\t\t})\n\n\t\t// End Example 25\n\n\t\tassert.NoError(t, err)\n\t\trequireCursorLength(t, cursor, 4)\n\t}\n\n\t{\n\t\t// Start Example 26\n\n\t\tcursor, err := coll.Find(\n\t\t\tcontext.TODO(),\n\t\t\tbson.D{\n\t\t\t\t{\"dim_cm\", bson.D{\n\t\t\t\t\t{\"$elemMatch\", bson.D{\n\t\t\t\t\t\t{\"$gt\", 22},\n\t\t\t\t\t\t{\"$lt\", 30},\n\t\t\t\t\t}},\n\t\t\t\t}},\n\t\t\t})\n\n\t\t// End Example 26\n\n\t\tassert.NoError(t, err)\n\t\trequireCursorLength(t, cursor, 1)\n\t}\n\n\t{\n\t\t// Start Example 27\n\n\t\tcursor, err := coll.Find(\n\t\t\tcontext.TODO(),\n\t\t\tbson.D{\n\t\t\t\t{\"dim_cm.1\", bson.D{\n\t\t\t\t\t{\"$gt\", 25},\n\t\t\t\t}},\n\t\t\t})\n\n\t\t// End Example 27\n\n\t\tassert.NoError(t, err)\n\t\trequireCursorLength(t, cursor, 1)\n\t}\n\n\t{\n\t\t// Start Example 28\n\n\t\tcursor, err := coll.Find(\n\t\t\tcontext.TODO(),\n\t\t\tbson.D{\n\t\t\t\t{\"tags\", bson.D{\n\t\t\t\t\t{\"$size\", 3},\n\t\t\t\t}},\n\t\t\t})\n\n\t\t// End Example 28\n\n\t\tassert.NoError(t, err)\n\t\trequireCursorLength(t, cursor, 1)\n\t}\n}\n\n// QueryArrayEmbeddedDocumentsExamples contains examples for querying fields with arrays and embedded documents.\n// Appears at https://www.mongodb.com/docs/manual/tutorial/query-array-of-documents/.\nfunc QueryArrayEmbeddedDocumentsExamples(t *testing.T, db *mongo.Database) {\n\tcoll := db.Collection(\"inventory_query_array_embedded\")\n\n\terr := coll.Drop(context.Background())\n\tassert.NoError(t, err)\n\n\t{\n\t\t// Start Example 29\n\n\t\tdocs := []any{\n\t\t\tbson.D{\n\t\t\t\t{\"item\", \"journal\"},\n\t\t\t\t{\"instock\", bson.A{\n\t\t\t\t\tbson.D{\n\t\t\t\t\t\t{\"warehouse\", \"A\"},\n\t\t\t\t\t\t{\"qty\", 5},\n\t\t\t\t\t},\n\t\t\t\t\tbson.D{\n\t\t\t\t\t\t{\"warehouse\", \"C\"},\n\t\t\t\t\t\t{\"qty\", 15},\n\t\t\t\t\t},\n\t\t\t\t}},\n\t\t\t},\n\t\t\tbson.D{\n\t\t\t\t{\"item\", \"notebook\"},\n\t\t\t\t{\"instock\", bson.A{\n\t\t\t\t\tbson.D{\n\t\t\t\t\t\t{\"warehouse\", \"C\"},\n\t\t\t\t\t\t{\"qty\", 5},\n\t\t\t\t\t},\n\t\t\t\t}},\n\t\t\t},\n\t\t\tbson.D{\n\t\t\t\t{\"item\", \"paper\"},\n\t\t\t\t{\"instock\", bson.A{\n\t\t\t\t\tbson.D{\n\t\t\t\t\t\t{\"warehouse\", \"A\"},\n\t\t\t\t\t\t{\"qty\", 60},\n\t\t\t\t\t},\n\t\t\t\t\tbson.D{\n\t\t\t\t\t\t{\"warehouse\", \"B\"},\n\t\t\t\t\t\t{\"qty\", 15},\n\t\t\t\t\t},\n\t\t\t\t}},\n\t\t\t},\n\t\t\tbson.D{\n\t\t\t\t{\"item\", \"planner\"},\n\t\t\t\t{\"instock\", bson.A{\n\t\t\t\t\tbson.D{\n\t\t\t\t\t\t{\"warehouse\", \"A\"},\n\t\t\t\t\t\t{\"qty\", 40},\n\t\t\t\t\t},\n\t\t\t\t\tbson.D{\n\t\t\t\t\t\t{\"warehouse\", \"B\"},\n\t\t\t\t\t\t{\"qty\", 5},\n\t\t\t\t\t},\n\t\t\t\t}},\n\t\t\t},\n\t\t\tbson.D{\n\t\t\t\t{\"item\", \"postcard\"},\n\t\t\t\t{\"instock\", bson.A{\n\t\t\t\t\tbson.D{\n\t\t\t\t\t\t{\"warehouse\", \"B\"},\n\t\t\t\t\t\t{\"qty\", 15},\n\t\t\t\t\t},\n\t\t\t\t\tbson.D{\n\t\t\t\t\t\t{\"warehouse\", \"C\"},\n\t\t\t\t\t\t{\"qty\", 35},\n\t\t\t\t\t},\n\t\t\t\t}},\n\t\t\t},\n\t\t}\n\n\t\tresult, err := coll.InsertMany(context.TODO(), docs)\n\n\t\t// End Example 29\n\n\t\tassert.NoError(t, err)\n\t\tassert.Len(t, result.InsertedIDs, 5)\n\t}\n\n\t{\n\t\t// Start Example 30\n\n\t\tcursor, err := coll.Find(\n\t\t\tcontext.TODO(),\n\t\t\tbson.D{\n\t\t\t\t{\"instock\", bson.D{\n\t\t\t\t\t{\"warehouse\", \"A\"},\n\t\t\t\t\t{\"qty\", 5},\n\t\t\t\t}},\n\t\t\t})\n\n\t\t// End Example 30\n\n\t\tassert.NoError(t, err)\n\t\trequireCursorLength(t, cursor, 1)\n\t}\n\n\t{\n\t\t// Start Example 31\n\n\t\tcursor, err := coll.Find(\n\t\t\tcontext.TODO(),\n\t\t\tbson.D{\n\t\t\t\t{\"instock\", bson.D{\n\t\t\t\t\t{\"qty\", 5},\n\t\t\t\t\t{\"warehouse\", \"A\"},\n\t\t\t\t}},\n\t\t\t})\n\n\t\t// End Example 31\n\n\t\tassert.NoError(t, err)\n\t\trequireCursorLength(t, cursor, 0)\n\t}\n\n\t{\n\t\t// Start Example 32\n\n\t\tcursor, err := coll.Find(\n\t\t\tcontext.TODO(),\n\t\t\tbson.D{\n\t\t\t\t{\"instock.0.qty\", bson.D{\n\t\t\t\t\t{\"$lte\", 20},\n\t\t\t\t}},\n\t\t\t})\n\n\t\t// End Example 32\n\n\t\tassert.NoError(t, err)\n\t\trequireCursorLength(t, cursor, 3)\n\t}\n\n\t{\n\t\t// Start Example 33\n\n\t\tcursor, err := coll.Find(\n\t\t\tcontext.TODO(),\n\t\t\tbson.D{\n\t\t\t\t{\"instock.qty\", bson.D{\n\t\t\t\t\t{\"$lte\", 20},\n\t\t\t\t}},\n\t\t\t})\n\n\t\t// End Example 33\n\n\t\tassert.NoError(t, err)\n\t\trequireCursorLength(t, cursor, 5)\n\t}\n\n\t{\n\t\t// Start Example 34\n\n\t\tcursor, err := coll.Find(\n\t\t\tcontext.TODO(),\n\t\t\tbson.D{\n\t\t\t\t{\"instock\", bson.D{\n\t\t\t\t\t{\"$elemMatch\", bson.D{\n\t\t\t\t\t\t{\"qty\", 5},\n\t\t\t\t\t\t{\"warehouse\", \"A\"},\n\t\t\t\t\t}},\n\t\t\t\t}},\n\t\t\t})\n\n\t\t// End Example 34\n\n\t\tassert.NoError(t, err)\n\t\trequireCursorLength(t, cursor, 1)\n\t}\n\n\t{\n\t\t// Start Example 35\n\n\t\tcursor, err := coll.Find(\n\t\t\tcontext.TODO(),\n\t\t\tbson.D{\n\t\t\t\t{\"instock\", bson.D{\n\t\t\t\t\t{\"$elemMatch\", bson.D{\n\t\t\t\t\t\t{\"qty\", bson.D{\n\t\t\t\t\t\t\t{\"$gt\", 10},\n\t\t\t\t\t\t\t{\"$lte\", 20},\n\t\t\t\t\t\t}},\n\t\t\t\t\t}},\n\t\t\t\t}},\n\t\t\t})\n\n\t\t// End Example 35\n\n\t\tassert.NoError(t, err)\n\t\trequireCursorLength(t, cursor, 3)\n\t}\n\n\t{\n\t\t// Start Example 36\n\n\t\tcursor, err := coll.Find(\n\t\t\tcontext.TODO(),\n\t\t\tbson.D{\n\t\t\t\t{\"instock.qty\", bson.D{\n\t\t\t\t\t{\"$gt\", 10},\n\t\t\t\t\t{\"$lte\", 20},\n\t\t\t\t}},\n\t\t\t})\n\n\t\t// End Example 36\n\n\t\tassert.NoError(t, err)\n\t\trequireCursorLength(t, cursor, 4)\n\t}\n\n\t{\n\t\t// Start Example 37\n\n\t\tcursor, err := coll.Find(\n\t\t\tcontext.TODO(),\n\t\t\tbson.D{\n\t\t\t\t{\"instock.qty\", 5},\n\t\t\t\t{\"instock.warehouse\", \"A\"},\n\t\t\t})\n\n\t\t// End Example 37\n\n\t\tassert.NoError(t, err)\n\t\trequireCursorLength(t, cursor, 2)\n\t}\n}\n\n// QueryNullMissingFieldsExamples contains examples for querying fields that are null or missing.\n// Appears at https://www.mongodb.com/docs/manual/tutorial/query-for-null-fields/.\nfunc QueryNullMissingFieldsExamples(t *testing.T, db *mongo.Database) {\n\tcoll := db.Collection(\"inventory_query_null_missing\")\n\n\terr := coll.Drop(context.Background())\n\tassert.NoError(t, err)\n\n\t{\n\t\t// Start Example 38\n\n\t\tdocs := []any{\n\t\t\tbson.D{\n\t\t\t\t{\"_id\", 1},\n\t\t\t\t{\"item\", nil},\n\t\t\t},\n\t\t\tbson.D{\n\t\t\t\t{\"_id\", 2},\n\t\t\t},\n\t\t}\n\n\t\tresult, err := coll.InsertMany(context.TODO(), docs)\n\n\t\t// End Example 38\n\n\t\tassert.NoError(t, err)\n\t\tassert.Len(t, result.InsertedIDs, 2)\n\t}\n\n\t{\n\t\t// Start Example 39\n\n\t\tcursor, err := coll.Find(\n\t\t\tcontext.TODO(),\n\t\t\tbson.D{\n\t\t\t\t{\"item\", nil},\n\t\t\t})\n\n\t\t// End Example 39\n\n\t\tassert.NoError(t, err)\n\t\trequireCursorLength(t, cursor, 2)\n\t}\n\n\t{\n\t\t// Start Example 40\n\n\t\tcursor, err := coll.Find(\n\t\t\tcontext.TODO(),\n\t\t\tbson.D{\n\t\t\t\t{\"item\", bson.D{\n\t\t\t\t\t{\"$type\", 10},\n\t\t\t\t}},\n\t\t\t})\n\n\t\t// End Example 40\n\n\t\tassert.NoError(t, err)\n\t\trequireCursorLength(t, cursor, 1)\n\t}\n\n\t{\n\t\t// Start Example 41\n\n\t\tcursor, err := coll.Find(\n\t\t\tcontext.TODO(),\n\t\t\tbson.D{\n\t\t\t\t{\"item\", bson.D{\n\t\t\t\t\t{\"$exists\", false},\n\t\t\t\t}},\n\t\t\t})\n\n\t\t// End Example 41\n\n\t\tassert.NoError(t, err)\n\t\trequireCursorLength(t, cursor, 1)\n\t}\n}\n\n// ProjectionExamples contains examples for specifying projections in find operations.\n// Appears at https://www.mongodb.com/docs/manual/tutorial/project-fields-from-query-results/.\nfunc ProjectionExamples(t *testing.T, db *mongo.Database) {\n\tcoll := db.Collection(\"inventory_project\")\n\n\terr := coll.Drop(context.Background())\n\tassert.NoError(t, err)\n\n\t{\n\t\t// Start Example 42\n\n\t\tdocs := []any{\n\t\t\tbson.D{\n\t\t\t\t{\"item\", \"journal\"},\n\t\t\t\t{\"status\", \"A\"},\n\t\t\t\t{\"size\", bson.D{\n\t\t\t\t\t{\"h\", 14},\n\t\t\t\t\t{\"w\", 21},\n\t\t\t\t\t{\"uom\", \"cm\"},\n\t\t\t\t}},\n\t\t\t\t{\"instock\", bson.A{\n\t\t\t\t\tbson.D{\n\t\t\t\t\t\t{\"warehouse\", \"A\"},\n\t\t\t\t\t\t{\"qty\", 5},\n\t\t\t\t\t},\n\t\t\t\t}},\n\t\t\t},\n\t\t\tbson.D{\n\t\t\t\t{\"item\", \"notebook\"},\n\t\t\t\t{\"status\", \"A\"},\n\t\t\t\t{\"size\", bson.D{\n\t\t\t\t\t{\"h\", 8.5},\n\t\t\t\t\t{\"w\", 11},\n\t\t\t\t\t{\"uom\", \"in\"},\n\t\t\t\t}},\n\t\t\t\t{\"instock\", bson.A{\n\t\t\t\t\tbson.D{\n\t\t\t\t\t\t{\"warehouse\", \"EC\"},\n\t\t\t\t\t\t{\"qty\", 5},\n\t\t\t\t\t},\n\t\t\t\t}},\n\t\t\t},\n\t\t\tbson.D{\n\t\t\t\t{\"item\", \"paper\"},\n\t\t\t\t{\"status\", \"D\"},\n\t\t\t\t{\"size\", bson.D{\n\t\t\t\t\t{\"h\", 8.5},\n\t\t\t\t\t{\"w\", 11},\n\t\t\t\t\t{\"uom\", \"in\"},\n\t\t\t\t}},\n\t\t\t\t{\"instock\", bson.A{\n\t\t\t\t\tbson.D{\n\t\t\t\t\t\t{\"warehouse\", \"A\"},\n\t\t\t\t\t\t{\"qty\", 60},\n\t\t\t\t\t},\n\t\t\t\t}},\n\t\t\t},\n\t\t\tbson.D{\n\t\t\t\t{\"item\", \"planner\"},\n\t\t\t\t{\"status\", \"D\"},\n\t\t\t\t{\"size\", bson.D{\n\t\t\t\t\t{\"h\", 22.85},\n\t\t\t\t\t{\"w\", 30},\n\t\t\t\t\t{\"uom\", \"cm\"},\n\t\t\t\t}},\n\t\t\t\t{\"instock\", bson.A{\n\t\t\t\t\tbson.D{\n\t\t\t\t\t\t{\"warehouse\", \"A\"},\n\t\t\t\t\t\t{\"qty\", 40},\n\t\t\t\t\t},\n\t\t\t\t}},\n\t\t\t},\n\t\t\tbson.D{\n\t\t\t\t{\"item\", \"postcard\"},\n\t\t\t\t{\"status\", \"A\"},\n\t\t\t\t{\"size\", bson.D{\n\t\t\t\t\t{\"h\", 10},\n\t\t\t\t\t{\"w\", 15.25},\n\t\t\t\t\t{\"uom\", \"cm\"},\n\t\t\t\t}},\n\t\t\t\t{\"instock\", bson.A{\n\t\t\t\t\tbson.D{\n\t\t\t\t\t\t{\"warehouse\", \"B\"},\n\t\t\t\t\t\t{\"qty\", 15},\n\t\t\t\t\t},\n\t\t\t\t\tbson.D{\n\t\t\t\t\t\t{\"warehouse\", \"EC\"},\n\t\t\t\t\t\t{\"qty\", 35},\n\t\t\t\t\t},\n\t\t\t\t}},\n\t\t\t},\n\t\t}\n\n\t\tresult, err := coll.InsertMany(context.TODO(), docs)\n\n\t\t// End Example 42\n\n\t\tassert.NoError(t, err)\n\t\tassert.Len(t, result.InsertedIDs, 5)\n\t}\n\n\t{\n\t\t// Start Example 43\n\n\t\tcursor, err := coll.Find(\n\t\t\tcontext.TODO(),\n\t\t\tbson.D{{\"status\", \"A\"}},\n\t\t)\n\n\t\t// End Example 43\n\n\t\tassert.NoError(t, err)\n\t\trequireCursorLength(t, cursor, 3)\n\t}\n\n\t{\n\t\t// Start Example 44\n\n\t\tprojection := bson.D{\n\t\t\t{\"item\", 1},\n\t\t\t{\"status\", 1},\n\t\t}\n\n\t\tcursor, err := coll.Find(\n\t\t\tcontext.TODO(),\n\t\t\tbson.D{\n\t\t\t\t{\"status\", \"A\"},\n\t\t\t},\n\t\t\toptions.Find().SetProjection(projection),\n\t\t)\n\n\t\t// End Example 44\n\n\t\tassert.NoError(t, err)\n\n\t\tfor cursor.Next(context.Background()) {\n\t\t\tdoc := cursor.Current\n\n\t\t\tassert.True(t, containsKey(doc, \"_id\"))\n\t\t\tassert.True(t, containsKey(doc, \"item\"))\n\t\t\tassert.True(t, containsKey(doc, \"status\"))\n\t\t\tassert.False(t, containsKey(doc, \"size\"))\n\t\t\tassert.False(t, containsKey(doc, \"instock\"))\n\t\t}\n\n\t\tassert.NoError(t, cursor.Err())\n\t}\n\n\t{\n\t\t// Start Example 45\n\n\t\tprojection := bson.D{\n\t\t\t{\"item\", 1},\n\t\t\t{\"status\", 1},\n\t\t\t{\"_id\", 0},\n\t\t}\n\n\t\tcursor, err := coll.Find(\n\t\t\tcontext.TODO(),\n\t\t\tbson.D{\n\t\t\t\t{\"status\", \"A\"},\n\t\t\t},\n\t\t\toptions.Find().SetProjection(projection),\n\t\t)\n\n\t\t// End Example 45\n\n\t\tassert.NoError(t, err)\n\n\t\tfor cursor.Next(context.Background()) {\n\t\t\tdoc := cursor.Current\n\n\t\t\tassert.False(t, containsKey(doc, \"_id\"))\n\t\t\tassert.True(t, containsKey(doc, \"item\"))\n\t\t\tassert.True(t, containsKey(doc, \"status\"))\n\t\t\tassert.False(t, containsKey(doc, \"size\"))\n\t\t\tassert.False(t, containsKey(doc, \"instock\"))\n\t\t}\n\n\t\tassert.NoError(t, cursor.Err())\n\t}\n\n\t{\n\t\t// Start Example 46\n\n\t\tprojection := bson.D{\n\t\t\t{\"status\", 0},\n\t\t\t{\"instock\", 0},\n\t\t}\n\n\t\tcursor, err := coll.Find(\n\t\t\tcontext.TODO(),\n\t\t\tbson.D{\n\t\t\t\t{\"status\", \"A\"},\n\t\t\t},\n\t\t\toptions.Find().SetProjection(projection),\n\t\t)\n\n\t\t// End Example 46\n\n\t\tassert.NoError(t, err)\n\n\t\tfor cursor.Next(context.Background()) {\n\t\t\tdoc := cursor.Current\n\n\t\t\tassert.True(t, containsKey(doc, \"_id\"))\n\t\t\tassert.True(t, containsKey(doc, \"item\"))\n\t\t\tassert.False(t, containsKey(doc, \"status\"))\n\t\t\tassert.True(t, containsKey(doc, \"size\"))\n\t\t\tassert.False(t, containsKey(doc, \"instock\"))\n\t\t}\n\n\t\tassert.NoError(t, cursor.Err())\n\t}\n\n\t{\n\t\t// Start Example 47\n\n\t\tprojection := bson.D{\n\t\t\t{\"item\", 1},\n\t\t\t{\"status\", 1},\n\t\t\t{\"size.uom\", 1},\n\t\t}\n\n\t\tcursor, err := coll.Find(\n\t\t\tcontext.TODO(),\n\t\t\tbson.D{\n\t\t\t\t{\"status\", \"A\"},\n\t\t\t},\n\t\t\toptions.Find().SetProjection(projection),\n\t\t)\n\n\t\t// End Example 47\n\n\t\tassert.NoError(t, err)\n\n\t\tfor cursor.Next(context.Background()) {\n\t\t\tdoc := cursor.Current\n\n\t\t\tassert.True(t, containsKey(doc, \"_id\"))\n\t\t\tassert.True(t, containsKey(doc, \"item\"))\n\t\t\tassert.True(t, containsKey(doc, \"status\"))\n\t\t\tassert.True(t, containsKey(doc, \"size\"))\n\t\t\tassert.False(t, containsKey(doc, \"instock\"))\n\n\t\t\tassert.True(t, containsKey(doc, \"size\", \"uom\"))\n\t\t\tassert.False(t, containsKey(doc, \"size\", \"h\"))\n\t\t\tassert.False(t, containsKey(doc, \"size\", \"w\"))\n\n\t\t}\n\n\t\tassert.NoError(t, cursor.Err())\n\t}\n\n\t{\n\t\t// Start Example 48\n\n\t\tprojection := bson.D{\n\t\t\t{\"size.uom\", 0},\n\t\t}\n\n\t\tcursor, err := coll.Find(\n\t\t\tcontext.TODO(),\n\t\t\tbson.D{\n\t\t\t\t{\"status\", \"A\"},\n\t\t\t},\n\t\t\toptions.Find().SetProjection(projection),\n\t\t)\n\n\t\t// End Example 48\n\n\t\tassert.NoError(t, err)\n\n\t\tfor cursor.Next(context.Background()) {\n\t\t\tdoc := cursor.Current\n\n\t\t\tassert.True(t, containsKey(doc, \"_id\"))\n\t\t\tassert.True(t, containsKey(doc, \"item\"))\n\t\t\tassert.True(t, containsKey(doc, \"status\"))\n\t\t\tassert.True(t, containsKey(doc, \"size\"))\n\t\t\tassert.True(t, containsKey(doc, \"instock\"))\n\n\t\t\tassert.False(t, containsKey(doc, \"size\", \"uom\"))\n\t\t\tassert.True(t, containsKey(doc, \"size\", \"h\"))\n\t\t\tassert.True(t, containsKey(doc, \"size\", \"w\"))\n\n\t\t}\n\n\t\tassert.NoError(t, cursor.Err())\n\t}\n\n\t{\n\t\t// Start Example 49\n\n\t\tprojection := bson.D{\n\t\t\t{\"item\", 1},\n\t\t\t{\"status\", 1},\n\t\t\t{\"instock.qty\", 1},\n\t\t}\n\n\t\tcursor, err := coll.Find(\n\t\t\tcontext.TODO(),\n\t\t\tbson.D{\n\t\t\t\t{\"status\", \"A\"},\n\t\t\t},\n\t\t\toptions.Find().SetProjection(projection),\n\t\t)\n\n\t\t// End Example 49\n\n\t\tassert.NoError(t, err)\n\n\t\tfor cursor.Next(context.Background()) {\n\t\t\tdoc := cursor.Current\n\n\t\t\tassert.True(t, containsKey(doc, \"_id\"))\n\t\t\tassert.True(t, containsKey(doc, \"item\"))\n\t\t\tassert.True(t, containsKey(doc, \"status\"))\n\t\t\tassert.False(t, containsKey(doc, \"size\"))\n\t\t\tassert.True(t, containsKey(doc, \"instock\"))\n\n\t\t\tinstock, err := doc.LookupErr(\"instock\")\n\t\t\tassert.NoError(t, err)\n\n\t\t\tvals, err := instock.Array().Values()\n\t\t\tassert.NoError(t, err)\n\n\t\t\tfor _, val := range vals {\n\t\t\t\tassert.Equal(t, bson.TypeEmbeddedDocument, val.Type)\n\t\t\t\tsubdoc := val.Document()\n\t\t\t\telems, err := subdoc.Elements()\n\t\t\t\tassert.NoError(t, err)\n\n\t\t\t\tassert.Equal(t, 1, len(elems))\n\t\t\t\t_, err = subdoc.LookupErr(\"qty\")\n\t\t\t\tassert.NoError(t, err)\n\t\t\t}\n\t\t}\n\n\t\tassert.NoError(t, cursor.Err())\n\t}\n\n\t{\n\t\t// Start Example 50\n\n\t\tprojection := bson.D{\n\t\t\t{\"item\", 1},\n\t\t\t{\"status\", 1},\n\t\t\t{\"instock\", bson.D{\n\t\t\t\t{\"$slice\", -1},\n\t\t\t}},\n\t\t}\n\n\t\tcursor, err := coll.Find(\n\t\t\tcontext.TODO(),\n\t\t\tbson.D{\n\t\t\t\t{\"status\", \"A\"},\n\t\t\t},\n\t\t\toptions.Find().SetProjection(projection),\n\t\t)\n\n\t\t// End Example 50\n\n\t\tassert.NoError(t, err)\n\n\t\tfor cursor.Next(context.Background()) {\n\t\t\tdoc := cursor.Current\n\n\t\t\tassert.True(t, containsKey(doc, \"_id\"))\n\t\t\tassert.True(t, containsKey(doc, \"item\"))\n\t\t\tassert.True(t, containsKey(doc, \"status\"))\n\t\t\tassert.False(t, containsKey(doc, \"size\"))\n\t\t\tassert.True(t, containsKey(doc, \"instock\"))\n\n\t\t\tinstock, err := doc.LookupErr(\"instock\")\n\t\t\tassert.NoError(t, err)\n\t\t\tvals, err := instock.Array().Values()\n\t\t\tassert.NoError(t, err)\n\t\t\tassert.Equal(t, len(vals), 1)\n\t\t}\n\n\t\tassert.NoError(t, cursor.Err())\n\t}\n}\n\n// UpdateExamples contains examples of update operations.\n// Appears at https://www.mongodb.com/docs/manual/tutorial/update-documents/.\nfunc UpdateExamples(t *testing.T, db *mongo.Database) {\n\tcoll := db.Collection(\"inventory_update\")\n\n\terr := coll.Drop(context.Background())\n\tassert.NoError(t, err)\n\n\t{\n\t\t// Start Example 51\n\n\t\tdocs := []any{\n\t\t\tbson.D{\n\t\t\t\t{\"item\", \"canvas\"},\n\t\t\t\t{\"qty\", 100},\n\t\t\t\t{\"size\", bson.D{\n\t\t\t\t\t{\"h\", 28},\n\t\t\t\t\t{\"w\", 35.5},\n\t\t\t\t\t{\"uom\", \"cm\"},\n\t\t\t\t}},\n\t\t\t\t{\"status\", \"A\"},\n\t\t\t},\n\t\t\tbson.D{\n\t\t\t\t{\"item\", \"journal\"},\n\t\t\t\t{\"qty\", 25},\n\t\t\t\t{\"size\", bson.D{\n\t\t\t\t\t{\"h\", 14},\n\t\t\t\t\t{\"w\", 21},\n\t\t\t\t\t{\"uom\", \"cm\"},\n\t\t\t\t}},\n\t\t\t\t{\"status\", \"A\"},\n\t\t\t},\n\t\t\tbson.D{\n\t\t\t\t{\"item\", \"mat\"},\n\t\t\t\t{\"qty\", 85},\n\t\t\t\t{\"size\", bson.D{\n\t\t\t\t\t{\"h\", 27.9},\n\t\t\t\t\t{\"w\", 35.5},\n\t\t\t\t\t{\"uom\", \"cm\"},\n\t\t\t\t}},\n\t\t\t\t{\"status\", \"A\"},\n\t\t\t},\n\t\t\tbson.D{\n\t\t\t\t{\"item\", \"mousepad\"},\n\t\t\t\t{\"qty\", 25},\n\t\t\t\t{\"size\", bson.D{\n\t\t\t\t\t{\"h\", 19},\n\t\t\t\t\t{\"w\", 22.85},\n\t\t\t\t\t{\"uom\", \"in\"},\n\t\t\t\t}},\n\t\t\t\t{\"status\", \"P\"},\n\t\t\t},\n\t\t\tbson.D{\n\t\t\t\t{\"item\", \"notebook\"},\n\t\t\t\t{\"qty\", 50},\n\t\t\t\t{\"size\", bson.D{\n\t\t\t\t\t{\"h\", 8.5},\n\t\t\t\t\t{\"w\", 11},\n\t\t\t\t\t{\"uom\", \"in\"},\n\t\t\t\t}},\n\t\t\t\t{\"status\", \"P\"},\n\t\t\t},\n\t\t\tbson.D{\n\t\t\t\t{\"item\", \"paper\"},\n\t\t\t\t{\"qty\", 100},\n\t\t\t\t{\"size\", bson.D{\n\t\t\t\t\t{\"h\", 8.5},\n\t\t\t\t\t{\"w\", 11},\n\t\t\t\t\t{\"uom\", \"in\"},\n\t\t\t\t}},\n\t\t\t\t{\"status\", \"D\"},\n\t\t\t},\n\t\t\tbson.D{\n\t\t\t\t{\"item\", \"planner\"},\n\t\t\t\t{\"qty\", 75},\n\t\t\t\t{\"size\", bson.D{\n\t\t\t\t\t{\"h\", 22.85},\n\t\t\t\t\t{\"w\", 30},\n\t\t\t\t\t{\"uom\", \"cm\"},\n\t\t\t\t}},\n\t\t\t\t{\"status\", \"D\"},\n\t\t\t},\n\t\t\tbson.D{\n\t\t\t\t{\"item\", \"postcard\"},\n\t\t\t\t{\"qty\", 45},\n\t\t\t\t{\"size\", bson.D{\n\t\t\t\t\t{\"h\", 10},\n\t\t\t\t\t{\"w\", 15.25},\n\t\t\t\t\t{\"uom\", \"cm\"},\n\t\t\t\t}},\n\t\t\t\t{\"status\", \"A\"},\n\t\t\t},\n\t\t\tbson.D{\n\t\t\t\t{\"item\", \"sketchbook\"},\n\t\t\t\t{\"qty\", 80},\n\t\t\t\t{\"size\", bson.D{\n\t\t\t\t\t{\"h\", 14},\n\t\t\t\t\t{\"w\", 21},\n\t\t\t\t\t{\"uom\", \"cm\"},\n\t\t\t\t}},\n\t\t\t\t{\"status\", \"A\"},\n\t\t\t},\n\t\t\tbson.D{\n\t\t\t\t{\"item\", \"sketch pad\"},\n\t\t\t\t{\"qty\", 95},\n\t\t\t\t{\"size\", bson.D{\n\t\t\t\t\t{\"h\", 22.85},\n\t\t\t\t\t{\"w\", 30.5},\n\t\t\t\t\t{\"uom\", \"cm\"},\n\t\t\t\t}},\n\t\t\t\t{\"status\", \"A\"},\n\t\t\t},\n\t\t}\n\n\t\tresult, err := coll.InsertMany(context.TODO(), docs)\n\n\t\t// End Example 51\n\n\t\tassert.NoError(t, err)\n\t\tassert.Len(t, result.InsertedIDs, 10)\n\t}\n\n\t{\n\t\t// Start Example 52\n\n\t\tresult, err := coll.UpdateOne(\n\t\t\tcontext.TODO(),\n\t\t\tbson.D{\n\t\t\t\t{\"item\", \"paper\"},\n\t\t\t},\n\t\t\tbson.D{\n\t\t\t\t{\"$set\", bson.D{\n\t\t\t\t\t{\"size.uom\", \"cm\"},\n\t\t\t\t\t{\"status\", \"P\"},\n\t\t\t\t}},\n\t\t\t\t{\"$currentDate\", bson.D{\n\t\t\t\t\t{\"lastModified\", true},\n\t\t\t\t}},\n\t\t\t},\n\t\t)\n\n\t\t// End Example 52\n\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, int64(1), result.MatchedCount)\n\t\tassert.Equal(t, int64(1), result.ModifiedCount)\n\n\t\tcursor, err := coll.Find(\n\t\t\tcontext.Background(),\n\t\t\tbson.D{\n\t\t\t\t{\"item\", \"paper\"},\n\t\t\t})\n\n\t\tassert.NoError(t, err)\n\n\t\tfor cursor.Next(context.Background()) {\n\t\t\tdoc := cursor.Current\n\n\t\t\tuom, err := doc.LookupErr(\"size\", \"uom\")\n\t\t\tassert.NoError(t, err)\n\t\t\tassert.Equal(t, uom.StringValue(), \"cm\")\n\n\t\t\tstatus, err := doc.LookupErr(\"status\")\n\t\t\tassert.NoError(t, err)\n\t\t\tassert.Equal(t, status.StringValue(), \"P\")\n\n\t\t\tassert.True(t, containsKey(doc, \"lastModified\"))\n\t\t}\n\n\t\tassert.NoError(t, cursor.Err())\n\t}\n\n\t{\n\t\t// Start Example 53\n\n\t\tresult, err := coll.UpdateMany(\n\t\t\tcontext.TODO(),\n\t\t\tbson.D{\n\t\t\t\t{\"qty\", bson.D{\n\t\t\t\t\t{\"$lt\", 50},\n\t\t\t\t}},\n\t\t\t},\n\t\t\tbson.D{\n\t\t\t\t{\"$set\", bson.D{\n\t\t\t\t\t{\"size.uom\", \"cm\"},\n\t\t\t\t\t{\"status\", \"P\"},\n\t\t\t\t}},\n\t\t\t\t{\"$currentDate\", bson.D{\n\t\t\t\t\t{\"lastModified\", true},\n\t\t\t\t}},\n\t\t\t},\n\t\t)\n\n\t\t// End Example 53\n\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, int64(3), result.MatchedCount)\n\t\tassert.Equal(t, int64(3), result.ModifiedCount)\n\n\t\tcursor, err := coll.Find(\n\t\t\tcontext.Background(),\n\t\t\tbson.D{\n\t\t\t\t{\"qty\", bson.D{\n\t\t\t\t\t{\"$lt\", 50},\n\t\t\t\t}},\n\t\t\t})\n\n\t\tassert.NoError(t, err)\n\n\t\tfor cursor.Next(context.Background()) {\n\t\t\tdoc := cursor.Current\n\n\t\t\tuom, err := doc.LookupErr(\"size\", \"uom\")\n\t\t\tassert.NoError(t, err)\n\t\t\tassert.Equal(t, uom.StringValue(), \"cm\")\n\n\t\t\tstatus, err := doc.LookupErr(\"status\")\n\t\t\tassert.NoError(t, err)\n\t\t\tassert.Equal(t, status.StringValue(), \"P\")\n\n\t\t\tassert.True(t, containsKey(doc, \"lastModified\"))\n\t\t}\n\n\t\tassert.NoError(t, cursor.Err())\n\t}\n\n\t{\n\t\t// Start Example 54\n\n\t\tresult, err := coll.ReplaceOne(\n\t\t\tcontext.TODO(),\n\t\t\tbson.D{\n\t\t\t\t{\"item\", \"paper\"},\n\t\t\t},\n\t\t\tbson.D{\n\t\t\t\t{\"item\", \"paper\"},\n\t\t\t\t{\"instock\", bson.A{\n\t\t\t\t\tbson.D{\n\t\t\t\t\t\t{\"warehouse\", \"A\"},\n\t\t\t\t\t\t{\"qty\", 60},\n\t\t\t\t\t},\n\t\t\t\t\tbson.D{\n\t\t\t\t\t\t{\"warehouse\", \"B\"},\n\t\t\t\t\t\t{\"qty\", 40},\n\t\t\t\t\t},\n\t\t\t\t}},\n\t\t\t},\n\t\t)\n\n\t\t// End Example 54\n\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, int64(1), result.MatchedCount)\n\t\tassert.Equal(t, int64(1), result.ModifiedCount)\n\n\t\tcursor, err := coll.Find(\n\t\t\tcontext.Background(),\n\t\t\tbson.D{\n\t\t\t\t{\"item\", \"paper\"},\n\t\t\t})\n\n\t\tassert.NoError(t, err)\n\n\t\tfor cursor.Next(context.Background()) {\n\t\t\tassert.True(t, containsKey(cursor.Current, \"_id\"))\n\t\t\tassert.True(t, containsKey(cursor.Current, \"item\"))\n\t\t\tassert.True(t, containsKey(cursor.Current, \"instock\"))\n\n\t\t\tinstock, err := cursor.Current.LookupErr(\"instock\")\n\t\t\tassert.NoError(t, err)\n\t\t\tvals, err := instock.Array().Values()\n\t\t\tassert.NoError(t, err)\n\t\t\tassert.Equal(t, len(vals), 2)\n\n\t\t}\n\n\t\tassert.NoError(t, cursor.Err())\n\t}\n}\n\n// DeleteExamples contains examples of delete operations.\n// Appears at https://www.mongodb.com/docs/manual/tutorial/remove-documents/.\nfunc DeleteExamples(t *testing.T, db *mongo.Database) {\n\tcoll := db.Collection(\"inventory_delete\")\n\n\terr := coll.Drop(context.Background())\n\tassert.NoError(t, err)\n\n\t{\n\t\t// Start Example 55\n\t\tdocs := []any{\n\t\t\tbson.D{\n\t\t\t\t{\"item\", \"journal\"},\n\t\t\t\t{\"qty\", 25},\n\t\t\t\t{\"size\", bson.D{\n\t\t\t\t\t{\"h\", 14},\n\t\t\t\t\t{\"w\", 21},\n\t\t\t\t\t{\"uom\", \"cm\"},\n\t\t\t\t}},\n\t\t\t\t{\"status\", \"A\"},\n\t\t\t},\n\t\t\tbson.D{\n\t\t\t\t{\"item\", \"notebook\"},\n\t\t\t\t{\"qty\", 50},\n\t\t\t\t{\"size\", bson.D{\n\t\t\t\t\t{\"h\", 8.5},\n\t\t\t\t\t{\"w\", 11},\n\t\t\t\t\t{\"uom\", \"in\"},\n\t\t\t\t}},\n\t\t\t\t{\"status\", \"P\"},\n\t\t\t},\n\t\t\tbson.D{\n\t\t\t\t{\"item\", \"paper\"},\n\t\t\t\t{\"qty\", 100},\n\t\t\t\t{\"size\", bson.D{\n\t\t\t\t\t{\"h\", 8.5},\n\t\t\t\t\t{\"w\", 11},\n\t\t\t\t\t{\"uom\", \"in\"},\n\t\t\t\t}},\n\t\t\t\t{\"status\", \"D\"},\n\t\t\t},\n\t\t\tbson.D{\n\t\t\t\t{\"item\", \"planner\"},\n\t\t\t\t{\"qty\", 75},\n\t\t\t\t{\"size\", bson.D{\n\t\t\t\t\t{\"h\", 22.85},\n\t\t\t\t\t{\"w\", 30},\n\t\t\t\t\t{\"uom\", \"cm\"},\n\t\t\t\t}},\n\t\t\t\t{\"status\", \"D\"},\n\t\t\t},\n\t\t\tbson.D{\n\t\t\t\t{\"item\", \"postcard\"},\n\t\t\t\t{\"qty\", 45},\n\t\t\t\t{\"size\", bson.D{\n\t\t\t\t\t{\"h\", 10},\n\t\t\t\t\t{\"w\", 15.25},\n\t\t\t\t\t{\"uom\", \"cm\"},\n\t\t\t\t}},\n\t\t\t\t{\"status\", \"A\"},\n\t\t\t},\n\t\t}\n\n\t\tresult, err := coll.InsertMany(context.TODO(), docs)\n\n\t\t// End Example 55\n\n\t\tassert.NoError(t, err)\n\t\tassert.Len(t, result.InsertedIDs, 5)\n\t}\n\n\t{\n\t\t// Start Example 57\n\n\t\tresult, err := coll.DeleteMany(\n\t\t\tcontext.TODO(),\n\t\t\tbson.D{\n\t\t\t\t{\"status\", \"A\"},\n\t\t\t},\n\t\t)\n\n\t\t// End Example 57\n\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, int64(2), result.DeletedCount)\n\t}\n\n\t{\n\t\t// Start Example 58\n\n\t\tresult, err := coll.DeleteOne(\n\t\t\tcontext.TODO(),\n\t\t\tbson.D{\n\t\t\t\t{\"status\", \"D\"},\n\t\t\t},\n\t\t)\n\n\t\t// End Example 58\n\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, int64(1), result.DeletedCount)\n\n\t}\n\n\t{\n\t\t// Start Example 56\n\n\t\tresult, err := coll.DeleteMany(context.TODO(), bson.D{})\n\n\t\t// End Example 56\n\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, int64(2), result.DeletedCount)\n\t}\n}\n\nvar log = logger.New(ioutil.Discard, \"\", logger.LstdFlags)\n\n// Transactions examples below all appear at https://www.mongodb.com/docs/manual/core/transactions-in-applications/.\n\n// Start Transactions Intro Example 1\n\n// UpdateEmployeeInfo is an example function demonstrating transactions.\nfunc UpdateEmployeeInfo(ctx context.Context, client *mongo.Client) error {\n\temployees := client.Database(\"hr\").Collection(\"employees\")\n\tevents := client.Database(\"reporting\").Collection(\"events\")\n\n\treturn client.UseSession(ctx, func(ctx context.Context) error {\n\t\tsess := mongo.SessionFromContext(ctx)\n\n\t\terr := sess.StartTransaction(options.Transaction().\n\t\t\tSetReadConcern(readconcern.Snapshot()).\n\t\t\tSetWriteConcern(writeconcern.Majority()),\n\t\t)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\t_, err = employees.UpdateOne(ctx, bson.D{{\"employee\", 3}}, bson.D{{\"$set\", bson.D{{\"status\", \"Inactive\"}}}})\n\t\tif err != nil {\n\t\t\tsess.AbortTransaction(ctx)\n\t\t\tlog.Println(\"caught exception during transaction, aborting.\")\n\t\t\treturn err\n\t\t}\n\t\t_, err = events.InsertOne(ctx, bson.D{{\"employee\", 3}, {\"status\", bson.D{{\"new\", \"Inactive\"}, {\"old\", \"Active\"}}}})\n\t\tif err != nil {\n\t\t\tsess.AbortTransaction(ctx)\n\t\t\tlog.Println(\"caught exception during transaction, aborting.\")\n\t\t\treturn err\n\t\t}\n\n\t\tfor {\n\t\t\terr = sess.CommitTransaction(ctx)\n\t\t\tswitch e := err.(type) {\n\t\t\tcase nil:\n\t\t\t\treturn nil\n\t\t\tcase mongo.CommandError:\n\t\t\t\tif e.HasErrorLabel(\"UnknownTransactionCommitResult\") {\n\t\t\t\t\tlog.Println(\"UnknownTransactionCommitResult, retrying commit operation...\")\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tlog.Println(\"Error during commit...\")\n\t\t\t\treturn e\n\t\t\tdefault:\n\t\t\t\tlog.Println(\"Error during commit...\")\n\t\t\t\treturn e\n\t\t\t}\n\t\t}\n\t})\n}\n\n// End Transactions Intro Example 1\n\n// Start Transactions Retry Example 1\n\n// RunTransactionWithRetry is an example function demonstrating transaction retry logic.\nfunc RunTransactionWithRetry(ctx context.Context, txnFn func(context.Context) error) error {\n\tfor {\n\t\terr := txnFn(ctx) // Performs transaction.\n\t\tif err == nil {\n\t\t\treturn nil\n\t\t}\n\n\t\tlog.Println(\"Transaction aborted. Caught exception during transaction.\")\n\n\t\t// If transient error, retry the whole transaction\n\t\tif cmdErr, ok := err.(mongo.CommandError); ok && cmdErr.HasErrorLabel(\"TransientTransactionError\") {\n\t\t\tlog.Println(\"TransientTransactionError, retrying transaction...\")\n\t\t\tcontinue\n\t\t}\n\t\treturn err\n\t}\n}\n\n// End Transactions Retry Example 1\n\n// Start Transactions Retry Example 2\n\n// CommitWithRetry is an example function demonstrating transaction commit with retry logic.\nfunc CommitWithRetry(ctx context.Context) error {\n\tsess := mongo.SessionFromContext(ctx)\n\n\tfor {\n\t\terr := sess.CommitTransaction(ctx)\n\t\tswitch e := err.(type) {\n\t\tcase nil:\n\t\t\tlog.Println(\"Transaction committed.\")\n\t\t\treturn nil\n\t\tcase mongo.CommandError:\n\t\t\t// Can retry commit\n\t\t\tif e.HasErrorLabel(\"UnknownTransactionCommitResult\") {\n\t\t\t\tlog.Println(\"UnknownTransactionCommitResult, retrying commit operation...\")\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tlog.Println(\"Error during commit...\")\n\t\t\treturn e\n\t\tdefault:\n\t\t\tlog.Println(\"Error during commit...\")\n\t\t\treturn e\n\t\t}\n\t}\n}\n\n// End Transactions Retry Example 2\n\n// TransactionsExamples contains examples for transaction operations.\nfunc TransactionsExamples(ctx context.Context, client *mongo.Client) error {\n\t_, err := client.Database(\"hr\").Collection(\"employees\").InsertOne(ctx, bson.D{{\"pi\", 3.14159}})\n\tif err != nil {\n\t\treturn err\n\t}\n\t_, err = client.Database(\"hr\").Collection(\"employees\").DeleteOne(ctx, bson.D{{\"pi\", 3.14159}})\n\tif err != nil {\n\t\treturn err\n\t}\n\t_, err = client.Database(\"reporting\").Collection(\"events\").InsertOne(ctx, bson.D{{\"pi\", 3.14159}})\n\tif err != nil {\n\t\treturn err\n\t}\n\t_, err = client.Database(\"reporting\").Collection(\"events\").DeleteOne(ctx, bson.D{{\"pi\", 3.14159}})\n\tif err != nil {\n\t\treturn err\n\t}\n\t// Start Transactions Retry Example 3\n\n\trunTransactionWithRetry := func(ctx context.Context, txnFn func(context.Context) error) error {\n\t\tfor {\n\t\t\terr := txnFn(ctx) // Performs transaction.\n\t\t\tif err == nil {\n\t\t\t\treturn nil\n\t\t\t}\n\n\t\t\tlog.Println(\"Transaction aborted. Caught exception during transaction.\")\n\n\t\t\t// If transient error, retry the whole transaction\n\t\t\tif cmdErr, ok := err.(mongo.CommandError); ok && cmdErr.HasErrorLabel(\"TransientTransactionError\") {\n\t\t\t\tlog.Println(\"TransientTransactionError, retrying transaction...\")\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\treturn err\n\t\t}\n\t}\n\n\tcommitWithRetry := func(ctx context.Context) error {\n\t\tsess := mongo.SessionFromContext(ctx)\n\n\t\tfor {\n\t\t\terr := sess.CommitTransaction(ctx)\n\t\t\tswitch e := err.(type) {\n\t\t\tcase nil:\n\t\t\t\tlog.Println(\"Transaction committed.\")\n\t\t\t\treturn nil\n\t\t\tcase mongo.CommandError:\n\t\t\t\t// Can retry commit\n\t\t\t\tif e.HasErrorLabel(\"UnknownTransactionCommitResult\") {\n\t\t\t\t\tlog.Println(\"UnknownTransactionCommitResult, retrying commit operation...\")\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tlog.Println(\"Error during commit...\")\n\t\t\t\treturn e\n\t\t\tdefault:\n\t\t\t\tlog.Println(\"Error during commit...\")\n\t\t\t\treturn e\n\t\t\t}\n\t\t}\n\t}\n\n\t// Updates two collections in a transaction.\n\tupdateEmployeeInfo := func(ctx context.Context) error {\n\t\temployees := client.Database(\"hr\").Collection(\"employees\")\n\t\tevents := client.Database(\"reporting\").Collection(\"events\")\n\n\t\tsess := mongo.SessionFromContext(ctx)\n\n\t\terr := sess.StartTransaction(options.Transaction().\n\t\t\tSetReadConcern(readconcern.Snapshot()).\n\t\t\tSetWriteConcern(writeconcern.Majority()),\n\t\t)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\t_, err = employees.UpdateOne(ctx, bson.D{{\"employee\", 3}}, bson.D{{\"$set\", bson.D{{\"status\", \"Inactive\"}}}})\n\t\tif err != nil {\n\t\t\tsess.AbortTransaction(ctx)\n\t\t\tlog.Println(\"caught exception during transaction, aborting.\")\n\t\t\treturn err\n\t\t}\n\t\t_, err = events.InsertOne(ctx, bson.D{{\"employee\", 3}, {\"status\", bson.D{{\"new\", \"Inactive\"}, {\"old\", \"Active\"}}}})\n\t\tif err != nil {\n\t\t\tsess.AbortTransaction(ctx)\n\t\t\tlog.Println(\"caught exception during transaction, aborting.\")\n\t\t\treturn err\n\t\t}\n\n\t\treturn commitWithRetry(ctx)\n\t}\n\n\ttxnOpts := options.Transaction().SetReadPreference(readpref.Primary())\n\n\treturn client.UseSessionWithOptions(\n\t\tctx, options.Session().SetDefaultTransactionOptions(txnOpts),\n\t\tfunc(ctx context.Context) error {\n\t\t\treturn runTransactionWithRetry(ctx, updateEmployeeInfo)\n\t\t},\n\t)\n}\n\n// End Transactions Retry Example 3\n\n// Start Transactions withTxn API Example 1\n\n// WithTransactionExample is an example of using the Session.WithTransaction function.\nfunc WithTransactionExample(ctx context.Context) error {\n\t// For a replica set, include the replica set name and a seedlist of the members in the URI string; e.g.\n\t// uri := \"mongodb://mongodb0.example.com:27017,mongodb1.example.com:27017/?replicaSet=myRepl\"\n\t// For a sharded cluster, connect to the mongos instances; e.g.\n\t// uri := \"mongodb://mongos0.example.com:27017,mongos1.example.com:27017/\"\n\turi := mtest.ClusterURI()\n\n\tclientOpts := options.Client().ApplyURI(uri)\n\tclient, err := mongo.Connect(clientOpts)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer func() { _ = client.Disconnect(ctx) }()\n\n\t// Prereq: Create collections.\n\twcMajority := writeconcern.Majority()\n\twcMajorityCollectionOpts := options.Collection().SetWriteConcern(wcMajority)\n\tfooColl := client.Database(\"mydb1\").Collection(\"foo\", wcMajorityCollectionOpts)\n\tbarColl := client.Database(\"mydb1\").Collection(\"bar\", wcMajorityCollectionOpts)\n\n\t// Step 1: Define the callback that specifies the sequence of operations to perform inside the transaction.\n\tcallback := func(sesctx context.Context) (any, error) {\n\t\t// Important: You must pass sesctx as the Context parameter to the operations for them to be executed in the\n\t\t// transaction.\n\t\tif _, err := fooColl.InsertOne(sesctx, bson.D{{\"abc\", 1}}); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif _, err := barColl.InsertOne(sesctx, bson.D{{\"xyz\", 999}}); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\treturn nil, nil\n\t}\n\n\t// Step 2: Start a session and run the callback using WithTransaction.\n\tsession, err := client.StartSession()\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer session.EndSession(ctx)\n\n\tresult, err := session.WithTransaction(ctx, callback)\n\tif err != nil {\n\t\treturn err\n\t}\n\tlog.Printf(\"result: %v\\n\", result)\n\treturn nil\n}\n\n// End Transactions withTxn API Example 1\n\n// ChangeStreamExamples contains examples of changestream operations.\n// Appears at https://www.mongodb.com/docs/manual/changeStreams/.\nfunc ChangeStreamExamples(t *testing.T, db *mongo.Database) {\n\tctx := context.Background()\n\n\tcoll := db.Collection(\"inventory_changestream\")\n\n\terr := coll.Drop(context.Background())\n\tassert.NoError(t, err)\n\n\t_, err = coll.InsertOne(ctx, bson.D{{\"x\", int32(1)}})\n\tassert.NoError(t, err)\n\n\tvar stop int32\n\n\tdoInserts := func(coll *mongo.Collection) {\n\t\tfor atomic.LoadInt32(&stop) == 0 {\n\t\t\t_, err = coll.InsertOne(ctx, bson.D{{\"x\", 1}})\n\t\t\ttime.Sleep(10 * time.Millisecond)\n\t\t\tcoll.DeleteOne(ctx, bson.D{{\"x\", 1}})\n\t\t}\n\t}\n\n\tgo doInserts(coll)\n\n\t{\n\t\t// Start Changestream Example 1\n\n\t\tcs, err := coll.Watch(ctx, mongo.Pipeline{})\n\t\tassert.NoError(t, err)\n\t\tdefer cs.Close(ctx)\n\n\t\tok := cs.Next(ctx)\n\t\tnext := cs.Current\n\n\t\t// End Changestream Example 1\n\n\t\tassert.True(t, ok)\n\t\tassert.NoError(t, err)\n\t\tassert.NotEqual(t, len(next), 0)\n\t}\n\t{\n\t\t// Start Changestream Example 2\n\n\t\tcs, err := coll.Watch(ctx, mongo.Pipeline{}, options.ChangeStream().SetFullDocument(options.UpdateLookup))\n\t\tassert.NoError(t, err)\n\t\tdefer cs.Close(ctx)\n\n\t\tok := cs.Next(ctx)\n\t\tnext := cs.Current\n\n\t\t// End Changestream Example 2\n\n\t\tassert.True(t, ok)\n\t\tassert.NoError(t, err)\n\t\tassert.NotEqual(t, len(next), 0)\n\t}\n\n\t{\n\t\toriginal, err := coll.Watch(ctx, mongo.Pipeline{})\n\t\tassert.NoError(t, err)\n\t\tdefer original.Close(ctx)\n\n\t\tok := original.Next(ctx)\n\t\tassert.True(t, ok)\n\n\t\t// Start Changestream Example 3\n\t\tresumeToken := original.ResumeToken()\n\n\t\tcs, err := coll.Watch(ctx, mongo.Pipeline{}, options.ChangeStream().SetResumeAfter(resumeToken))\n\t\tassert.NoError(t, err)\n\t\tdefer cs.Close(ctx)\n\n\t\tok = cs.Next(ctx)\n\t\tresult := cs.Current\n\n\t\t// End Changestream Example 3\n\n\t\tassert.True(t, ok)\n\t\tassert.NoError(t, err)\n\t\tassert.NotEqual(t, len(result), 0)\n\t}\n\n\t{\n\t\t// Start Changestream Example 4\n\t\tpipeline := mongo.Pipeline{bson.D{{\n\t\t\t\"$match\", bson.D{{\n\t\t\t\t\"$or\",\n\t\t\t\tbson.A{\n\t\t\t\t\tbson.D{{\"fullDocument.username\", \"alice\"}},\n\t\t\t\t\tbson.D{{\"operationType\", \"delete\"}},\n\t\t\t\t},\n\t\t\t}},\n\t\t}}}\n\t\tcs, err := coll.Watch(ctx, pipeline)\n\t\tassert.NoError(t, err)\n\t\tdefer cs.Close(ctx)\n\n\t\tok := cs.Next(ctx)\n\t\tnext := cs.Current\n\n\t\t// End Changestream Example 4\n\n\t\tassert.True(t, ok)\n\t\tassert.NoError(t, err)\n\t\tassert.NotEqual(t, len(next), 0)\n\t}\n\n\tatomic.StoreInt32(&stop, 1)\n}\n\n// AggregationExamples contains examples of aggregation operations.\n// Appears at https://www.mongodb.com/docs/manual/aggregation/.\nfunc AggregationExamples(t *testing.T, db *mongo.Database) {\n\tctx := context.Background()\n\n\tsalesColl := db.Collection(\"sales\")\n\tairlinesColl := db.Collection(\"airlines\")\n\tairAlliancesColl := db.Collection(\"air_alliances\")\n\n\terr := salesColl.Drop(ctx)\n\tassert.NoError(t, err)\n\terr = airlinesColl.Drop(ctx)\n\tassert.NoError(t, err)\n\terr = airAlliancesColl.Drop(ctx)\n\tassert.NoError(t, err)\n\n\tdate20180208 := parseDate(t, \"2018-02-08T09:00:00.000Z\")\n\tdate20180109 := parseDate(t, \"2018-01-09T07:12:00.000Z\")\n\tdate20180127 := parseDate(t, \"2018-01-27T09:13:00.000Z\")\n\tdate20180203 := parseDate(t, \"2018-02-03T07:58:00.000Z\")\n\tdate20180205 := parseDate(t, \"2018-02-05T06:03:00.000Z\")\n\tdate20180111 := parseDate(t, \"2018-01-11T07:15:00.000Z\")\n\n\tsales := []any{\n\t\tbson.D{\n\t\t\t{\"date\", date20180208},\n\t\t\t{\"items\", bson.A{\n\t\t\t\tbson.D{\n\t\t\t\t\t{\"fruit\", \"kiwi\"},\n\t\t\t\t\t{\"quantity\", 2},\n\t\t\t\t\t{\"price\", 0.5},\n\t\t\t\t},\n\t\t\t\tbson.D{\n\t\t\t\t\t{\"fruit\", \"apple\"},\n\t\t\t\t\t{\"quantity\", 1},\n\t\t\t\t\t{\"price\", 1.0},\n\t\t\t\t},\n\t\t\t}},\n\t\t},\n\t\tbson.D{\n\t\t\t{\"date\", date20180109},\n\t\t\t{\"items\", bson.A{\n\t\t\t\tbson.D{\n\t\t\t\t\t{\"fruit\", \"banana\"},\n\t\t\t\t\t{\"quantity\", 8},\n\t\t\t\t\t{\"price\", 1.0},\n\t\t\t\t},\n\t\t\t\tbson.D{\n\t\t\t\t\t{\"fruit\", \"apple\"},\n\t\t\t\t\t{\"quantity\", 1},\n\t\t\t\t\t{\"price\", 1.0},\n\t\t\t\t},\n\t\t\t\tbson.D{\n\t\t\t\t\t{\"fruit\", \"papaya\"},\n\t\t\t\t\t{\"quantity\", 1},\n\t\t\t\t\t{\"price\", 4.0},\n\t\t\t\t},\n\t\t\t}},\n\t\t},\n\t\tbson.D{\n\t\t\t{\"date\", date20180127},\n\t\t\t{\"items\", bson.A{\n\t\t\t\tbson.D{\n\t\t\t\t\t{\"fruit\", \"banana\"},\n\t\t\t\t\t{\"quantity\", 1},\n\t\t\t\t\t{\"price\", 1.0},\n\t\t\t\t},\n\t\t\t}},\n\t\t},\n\t\tbson.D{\n\t\t\t{\"date\", date20180203},\n\t\t\t{\"items\", bson.A{\n\t\t\t\tbson.D{\n\t\t\t\t\t{\"fruit\", \"banana\"},\n\t\t\t\t\t{\"quantity\", 1},\n\t\t\t\t\t{\"price\", 1.0},\n\t\t\t\t},\n\t\t\t}},\n\t\t},\n\t\tbson.D{\n\t\t\t{\"date\", date20180205},\n\t\t\t{\"items\", bson.A{\n\t\t\t\tbson.D{\n\t\t\t\t\t{\"fruit\", \"banana\"},\n\t\t\t\t\t{\"quantity\", 1},\n\t\t\t\t\t{\"price\", 1.0},\n\t\t\t\t},\n\t\t\t\tbson.D{\n\t\t\t\t\t{\"fruit\", \"mango\"},\n\t\t\t\t\t{\"quantity\", 2},\n\t\t\t\t\t{\"price\", 2.0},\n\t\t\t\t},\n\t\t\t\tbson.D{\n\t\t\t\t\t{\"fruit\", \"apple\"},\n\t\t\t\t\t{\"quantity\", 1},\n\t\t\t\t\t{\"price\", 1.0},\n\t\t\t\t},\n\t\t\t}},\n\t\t},\n\t\tbson.D{\n\t\t\t{\"date\", date20180111},\n\t\t\t{\"items\", bson.A{\n\t\t\t\tbson.D{\n\t\t\t\t\t{\"fruit\", \"banana\"},\n\t\t\t\t\t{\"quantity\", 1},\n\t\t\t\t\t{\"price\", 1.0},\n\t\t\t\t},\n\t\t\t\tbson.D{\n\t\t\t\t\t{\"fruit\", \"apple\"},\n\t\t\t\t\t{\"quantity\", 1},\n\t\t\t\t\t{\"price\", 1.0},\n\t\t\t\t},\n\t\t\t\tbson.D{\n\t\t\t\t\t{\"fruit\", \"papaya\"},\n\t\t\t\t\t{\"quantity\", 3},\n\t\t\t\t\t{\"price\", 4.0},\n\t\t\t\t},\n\t\t\t}},\n\t\t},\n\t}\n\tairlines := []any{\n\t\tbson.D{\n\t\t\t{\"airline\", 17},\n\t\t\t{\"name\", \"Air Canada\"},\n\t\t\t{\"alias\", \"AC\"},\n\t\t\t{\"iata\", \"ACA\"},\n\t\t\t{\"icao\", \"AIR CANADA\"},\n\t\t\t{\"active\", \"Y\"},\n\t\t\t{\"country\", \"Canada\"},\n\t\t\t{\"base\", \"TAL\"},\n\t\t},\n\t\tbson.D{\n\t\t\t{\"airline\", 18},\n\t\t\t{\"name\", \"Turkish Airlines\"},\n\t\t\t{\"alias\", \"YK\"},\n\t\t\t{\"iata\", \"TRK\"},\n\t\t\t{\"icao\", \"TURKISH\"},\n\t\t\t{\"active\", \"Y\"},\n\t\t\t{\"country\", \"Turkey\"},\n\t\t\t{\"base\", \"AET\"},\n\t\t},\n\t\tbson.D{\n\t\t\t{\"airline\", 22},\n\t\t\t{\"name\", \"Saudia\"},\n\t\t\t{\"alias\", \"SV\"},\n\t\t\t{\"iata\", \"SVA\"},\n\t\t\t{\"icao\", \"SAUDIA\"},\n\t\t\t{\"active\", \"Y\"},\n\t\t\t{\"country\", \"Saudi Arabia\"},\n\t\t\t{\"base\", \"JSU\"},\n\t\t},\n\t\tbson.D{\n\t\t\t{\"airline\", 29},\n\t\t\t{\"name\", \"Finnair\"},\n\t\t\t{\"alias\", \"AY\"},\n\t\t\t{\"iata\", \"FIN\"},\n\t\t\t{\"icao\", \"FINNAIR\"},\n\t\t\t{\"active\", \"Y\"},\n\t\t\t{\"country\", \"Finland\"},\n\t\t\t{\"base\", \"JMZ\"},\n\t\t},\n\t\tbson.D{\n\t\t\t{\"airline\", 34},\n\t\t\t{\"name\", \"Afric'air Express\"},\n\t\t\t{\"alias\", \"\"},\n\t\t\t{\"iata\", \"AAX\"},\n\t\t\t{\"icao\", \"AFREX\"},\n\t\t\t{\"active\", \"N\"},\n\t\t\t{\"country\", \"Ivory Coast\"},\n\t\t\t{\"base\", \"LOK\"},\n\t\t},\n\t\tbson.D{\n\t\t\t{\"airline\", 37},\n\t\t\t{\"name\", \"Artem-Avia\"},\n\t\t\t{\"alias\", \"\"},\n\t\t\t{\"iata\", \"ABA\"},\n\t\t\t{\"icao\", \"ARTEM-AVIA\"},\n\t\t\t{\"active\", \"N\"},\n\t\t\t{\"country\", \"Ukraine\"},\n\t\t\t{\"base\", \"JBR\"},\n\t\t},\n\t\tbson.D{\n\t\t\t{\"airline\", 38},\n\t\t\t{\"name\", \"Lufthansa\"},\n\t\t\t{\"alias\", \"LH\"},\n\t\t\t{\"iata\", \"DLH\"},\n\t\t\t{\"icao\", \"LUFTHANSA\"},\n\t\t\t{\"active\", \"Y\"},\n\t\t\t{\"country\", \"Germany\"},\n\t\t\t{\"base\", \"CYS\"},\n\t\t},\n\t}\n\tairAlliances := []any{\n\t\tbson.D{\n\t\t\t{\"name\", \"Star Alliance\"},\n\t\t\t{\"airlines\", bson.A{\n\t\t\t\t\"Air Canada\",\n\t\t\t\t\"Avianca\",\n\t\t\t\t\"Air China\",\n\t\t\t\t\"Air New Zealand\",\n\t\t\t\t\"Asiana Airlines\",\n\t\t\t\t\"Brussels Airlines\",\n\t\t\t\t\"Copa Airlines\",\n\t\t\t\t\"Croatia Airlines\",\n\t\t\t\t\"EgyptAir\",\n\t\t\t\t\"TAP Portugal\",\n\t\t\t\t\"United Airlines\",\n\t\t\t\t\"Turkish Airlines\",\n\t\t\t\t\"Swiss International Air Lines\",\n\t\t\t\t\"Lufthansa\",\n\t\t\t}},\n\t\t},\n\t\tbson.D{\n\t\t\t{\"name\", \"SkyTeam\"},\n\t\t\t{\"airlines\", bson.A{\n\t\t\t\t\"Aerolinias Argentinas\",\n\t\t\t\t\"Aeromexico\",\n\t\t\t\t\"Air Europa\",\n\t\t\t\t\"Air France\",\n\t\t\t\t\"Alitalia\",\n\t\t\t\t\"Delta Air Lines\",\n\t\t\t\t\"Garuda Indonesia\",\n\t\t\t\t\"Kenya Airways\",\n\t\t\t\t\"KLM\",\n\t\t\t\t\"Korean Air\",\n\t\t\t\t\"Middle East Airlines\",\n\t\t\t\t\"Saudia\",\n\t\t\t}},\n\t\t},\n\t\tbson.D{\n\t\t\t{\"name\", \"OneWorld\"},\n\t\t\t{\"airlines\", bson.A{\n\t\t\t\t\"Air Berlin\",\n\t\t\t\t\"American Airlines\",\n\t\t\t\t\"British Airways\",\n\t\t\t\t\"Cathay Pacific\",\n\t\t\t\t\"Finnair\",\n\t\t\t\t\"Iberia Airlines\",\n\t\t\t\t\"Japan Airlines\",\n\t\t\t\t\"LATAM Chile\",\n\t\t\t\t\"LATAM Brasil\",\n\t\t\t\t\"Malasya Airlines\",\n\t\t\t\t\"Canadian Airlines\",\n\t\t\t}},\n\t\t},\n\t}\n\n\tsalesResult, salesErr := salesColl.InsertMany(ctx, sales)\n\tairlinesResult, airlinesErr := airlinesColl.InsertMany(ctx, airlines)\n\tairAlliancesResult, airAlliancesErr := airAlliancesColl.InsertMany(ctx, airAlliances)\n\n\tassert.NoError(t, salesErr)\n\tassert.Len(t, salesResult.InsertedIDs, 6)\n\tassert.NoError(t, airlinesErr)\n\tassert.Len(t, airlinesResult.InsertedIDs, 7)\n\tassert.NoError(t, airAlliancesErr)\n\tassert.Len(t, airAlliancesResult.InsertedIDs, 3)\n\n\t{\n\t\t// Start Aggregation Example 1\n\t\tpipeline := mongo.Pipeline{\n\t\t\t{\n\t\t\t\t{\"$match\", bson.D{\n\t\t\t\t\t{\"items.fruit\", \"banana\"},\n\t\t\t\t}},\n\t\t\t},\n\t\t\t{\n\t\t\t\t{\"$sort\", bson.D{\n\t\t\t\t\t{\"date\", 1},\n\t\t\t\t}},\n\t\t\t},\n\t\t}\n\n\t\tcursor, err := salesColl.Aggregate(ctx, pipeline)\n\n\t\t// End Aggregation Example 1\n\n\t\tassert.NoError(t, err)\n\t\tdefer cursor.Close(ctx)\n\t\trequireCursorLength(t, cursor, 5)\n\t}\n\t{\n\t\t// Start Aggregation Example 2\n\t\tpipeline := mongo.Pipeline{\n\t\t\t{\n\t\t\t\t{\"$unwind\", \"$items\"},\n\t\t\t},\n\t\t\t{\n\t\t\t\t{\"$match\", bson.D{\n\t\t\t\t\t{\"items.fruit\", \"banana\"},\n\t\t\t\t}},\n\t\t\t},\n\t\t\t{\n\t\t\t\t{\"$group\", bson.D{\n\t\t\t\t\t{\"_id\", bson.D{\n\t\t\t\t\t\t{\"day\", bson.D{\n\t\t\t\t\t\t\t{\"$dayOfWeek\", \"$date\"},\n\t\t\t\t\t\t}},\n\t\t\t\t\t}},\n\t\t\t\t\t{\"count\", bson.D{\n\t\t\t\t\t\t{\"$sum\", \"$items.quantity\"},\n\t\t\t\t\t}},\n\t\t\t\t}},\n\t\t\t},\n\t\t\t{\n\t\t\t\t{\"$project\", bson.D{\n\t\t\t\t\t{\"dayOfWeek\", \"$_id.day\"},\n\t\t\t\t\t{\"numberSold\", \"$count\"},\n\t\t\t\t\t{\"_id\", 0},\n\t\t\t\t}},\n\t\t\t},\n\t\t\t{\n\t\t\t\t{\"$sort\", bson.D{\n\t\t\t\t\t{\"numberSold\", 1},\n\t\t\t\t}},\n\t\t\t},\n\t\t}\n\n\t\tcursor, err := salesColl.Aggregate(ctx, pipeline)\n\n\t\t// End Aggregation Example 2\n\n\t\tassert.NoError(t, err)\n\t\tdefer cursor.Close(ctx)\n\t\trequireCursorLength(t, cursor, 4)\n\t}\n\t{\n\t\t// Start Aggregation Example 3\n\t\tpipeline := mongo.Pipeline{\n\t\t\t{\n\t\t\t\t{\"$unwind\", \"$items\"},\n\t\t\t},\n\t\t\t{\n\t\t\t\t{\"$group\", bson.D{\n\t\t\t\t\t{\"_id\", bson.D{\n\t\t\t\t\t\t{\"day\", bson.D{\n\t\t\t\t\t\t\t{\"$dayOfWeek\", \"$date\"},\n\t\t\t\t\t\t}},\n\t\t\t\t\t}},\n\t\t\t\t\t{\"items_sold\", bson.D{\n\t\t\t\t\t\t{\"$sum\", \"$items.quantity\"},\n\t\t\t\t\t}},\n\t\t\t\t\t{\"revenue\", bson.D{\n\t\t\t\t\t\t{\"$sum\", bson.D{\n\t\t\t\t\t\t\t{\"$multiply\", bson.A{\"$items.quantity\", \"$items.price\"}},\n\t\t\t\t\t\t}},\n\t\t\t\t\t}},\n\t\t\t\t}},\n\t\t\t},\n\t\t\t{\n\t\t\t\t{\"$project\", bson.D{\n\t\t\t\t\t{\"day\", \"$_id.day\"},\n\t\t\t\t\t{\"revenue\", 1},\n\t\t\t\t\t{\"items_sold\", 1},\n\t\t\t\t\t{\"discount\", bson.D{\n\t\t\t\t\t\t{\"$cond\", bson.D{\n\t\t\t\t\t\t\t{\"if\", bson.D{\n\t\t\t\t\t\t\t\t{\"$lte\", bson.A{\"$revenue\", 250}},\n\t\t\t\t\t\t\t}},\n\t\t\t\t\t\t\t{\"then\", 25},\n\t\t\t\t\t\t\t{\"else\", 0},\n\t\t\t\t\t\t}},\n\t\t\t\t\t}},\n\t\t\t\t}},\n\t\t\t},\n\t\t}\n\n\t\tcursor, err := salesColl.Aggregate(ctx, pipeline)\n\n\t\t// End Aggregation Example 3\n\n\t\tassert.NoError(t, err)\n\t\tdefer cursor.Close(ctx)\n\t\trequireCursorLength(t, cursor, 4)\n\t}\n\t{\n\t\t// Start Aggregation Example 4\n\t\tpipeline := mongo.Pipeline{\n\t\t\t{\n\t\t\t\t{\"$lookup\", bson.D{\n\t\t\t\t\t{\"from\", \"air_airlines\"},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"let\", bson.D{\n\t\t\t\t\t\t\t{\"constituents\", \"$airlines\"},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\t{\"pipeline\", bson.A{bson.D{\n\t\t\t\t\t\t{\"$match\", bson.D{\n\t\t\t\t\t\t\t{\"$expr\", bson.D{\n\t\t\t\t\t\t\t\t{\"$in\", bson.A{\"$name\", \"$$constituents\"}},\n\t\t\t\t\t\t\t}},\n\t\t\t\t\t\t}},\n\t\t\t\t\t}}},\n\t\t\t\t\t{\"as\", \"airlines\"},\n\t\t\t\t}},\n\t\t\t},\n\t\t\t{\n\t\t\t\t{\"$project\", bson.D{\n\t\t\t\t\t{\"_id\", 0},\n\t\t\t\t\t{\"name\", 1},\n\t\t\t\t\t{\"airlines\", bson.D{\n\t\t\t\t\t\t{\"$filter\", bson.D{\n\t\t\t\t\t\t\t{\"input\", \"$airlines\"},\n\t\t\t\t\t\t\t{\"as\", \"airline\"},\n\t\t\t\t\t\t\t{\"cond\", bson.D{\n\t\t\t\t\t\t\t\t{\"$eq\", bson.A{\"$$airline.country\", \"Canada\"}},\n\t\t\t\t\t\t\t}},\n\t\t\t\t\t\t}},\n\t\t\t\t\t}},\n\t\t\t\t}},\n\t\t\t},\n\t\t}\n\n\t\tcursor, err := airAlliancesColl.Aggregate(ctx, pipeline)\n\n\t\t// End Aggregation Example 4\n\n\t\tassert.NoError(t, err)\n\t\tdefer cursor.Close(ctx)\n\t\trequireCursorLength(t, cursor, 3)\n\t}\n}\n\n// CausalConsistencyExamples contains examples of causal consistency usage.\n// Appears at https://www.mongodb.com/docs/manual/core/read-isolation-consistency-recency/.\nfunc CausalConsistencyExamples(client *mongo.Client) error {\n\tctx := context.Background()\n\tcoll := client.Database(\"test\").Collection(\"items\")\n\n\tcurrentDate := time.Now()\n\n\terr := coll.Drop(ctx)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// Start Causal Consistency Example 1\n\n\trc := readconcern.Majority()\n\twc := writeconcern.Majority()\n\n\ttxnOpts := options.Transaction().SetReadConcern(rc).SetWriteConcern(wc)\n\n\t// Use a causally-consistent session to run some operations\n\topts := options.Session().SetDefaultTransactionOptions(txnOpts)\n\tsession1, err := client.StartSession(opts)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer session1.EndSession(context.TODO())\n\n\terr = client.UseSessionWithOptions(context.TODO(), opts, func(ctx context.Context) error {\n\t\t// Run an update with our causally-consistent session\n\t\t_, err = coll.UpdateOne(ctx, bson.D{{\"sku\", 111}}, bson.D{{\"$set\", bson.D{{\"end\", currentDate}}}})\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\t// Run an insert with our causally-consistent session\n\t\t_, err = coll.InsertOne(ctx, bson.D{{\"sku\", \"nuts-111\"}, {\"name\", \"Pecans\"}, {\"start\", currentDate}})\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\treturn nil\n\t})\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// End Causal Consistency Example 1\n\n\t// Start Causal Consistency Example 2\n\n\ttxnOpts.SetReadPreference(readpref.Secondary())\n\n\t// Make a new session that is causally consistent with session1 so session2 reads what session1 writes\n\topts = options.Session().SetDefaultTransactionOptions(txnOpts)\n\tsession2, err := client.StartSession(opts)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer session2.EndSession(context.TODO())\n\n\terr = client.UseSessionWithOptions(context.TODO(), opts, func(ctx context.Context) error {\n\t\t// Set cluster time of session2 to session1's cluster time\n\t\tclusterTime := session1.ClusterTime()\n\t\tsession2.AdvanceClusterTime(clusterTime)\n\n\t\t// Set operation time of session2 to session1's operation time\n\t\toperationTime := session1.OperationTime()\n\t\tsession2.AdvanceOperationTime(operationTime)\n\t\t// Run a find on session2, which should find all the writes from session1\n\t\tcursor, err := coll.Find(ctx, bson.D{{\"end\", nil}})\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tfor cursor.Next(ctx) {\n\t\t\tdoc := cursor.Current\n\t\t\tfmt.Printf(\"Document: %v\\n\", doc.String())\n\t\t}\n\n\t\treturn cursor.Err()\n\t})\n\tif err != nil {\n\t\treturn err\n\t}\n\t// End Causal Consistency Example 2\n\n\treturn nil\n}\n\n// RunCommandExamples contains examples of RunCommand operations.\n// Appears at https://www.mongodb.com/docs/manual/reference/command/buildInfo/.\nfunc RunCommandExamples(t *testing.T, db *mongo.Database) {\n\tctx := context.Background()\n\n\tcoll := db.Collection(\"restaurants\")\n\n\terr := coll.Drop(ctx)\n\tassert.NoError(t, err)\n\n\t{\n\t\t// Start RunCommand Example 1\n\t\tres := db.RunCommand(ctx, bson.D{{\"buildInfo\", 1}})\n\n\t\t// End RunCommand Example 1\n\n\t\terr := res.Err()\n\t\tassert.NoError(t, err)\n\t}\n}\n\n// IndexExamples contains examples of Index operations.\n// Appears at https://www.mongodb.com/docs/manual/indexes/.\nfunc IndexExamples(t *testing.T, db *mongo.Database) {\n\tctx := context.Background()\n\n\trecordsColl := db.Collection(\"records\")\n\trestaurantsColl := db.Collection(\"restaurants\")\n\n\terr := recordsColl.Drop(ctx)\n\tassert.NoError(t, err)\n\terr = restaurantsColl.Drop(ctx)\n\tassert.NoError(t, err)\n\n\trecords := []any{\n\t\tbson.D{\n\t\t\t{\"student\", \"Marty McFly\"},\n\t\t\t{\"classYear\", 1986},\n\t\t\t{\"school\", \"Hill Valley High\"},\n\t\t\t{\"score\", 56.5},\n\t\t},\n\t\tbson.D{\n\t\t\t{\"student\", \"Ferris F. Bueller\"},\n\t\t\t{\"classYear\", 1987},\n\t\t\t{\"school\", \"Glenbrook North High\"},\n\t\t\t{\"status\", \"Suspended\"},\n\t\t\t{\"score\", 76.0},\n\t\t},\n\t\tbson.D{\n\t\t\t{\"student\", \"Reynard Muldoon\"},\n\t\t\t{\"classYear\", 2007},\n\t\t\t{\"school\", \"Stonetown Middle\"},\n\t\t\t{\"score\", 99.9},\n\t\t},\n\t}\n\trestaurants := []any{\n\t\tbson.D{\n\t\t\t{\"name\", \"Chez Panisse\"},\n\t\t\t{\"cuisine\", \"American/French\"},\n\t\t\t{\"city\", \"Oakland\"},\n\t\t\t{\"state\", \"California\"},\n\t\t\t{\"country\", \"United States\"},\n\t\t\t{\"rating\", 4.9},\n\t\t},\n\t\tbson.D{\n\t\t\t{\"name\", \"Central\"},\n\t\t\t{\"cuisine\", \"Peruvian\"},\n\t\t\t{\"city\", \"Lima\"},\n\t\t\t{\"country\", \"Peru\"},\n\t\t\t{\"rating\", 5.8},\n\t\t},\n\t\tbson.D{\n\t\t\t{\"name\", \"Eleven Madison Park\"},\n\t\t\t{\"cuisine\", \"French\"},\n\t\t\t{\"city\", \"New York City\"},\n\t\t\t{\"state\", \"New York\"},\n\t\t\t{\"country\", \"United States\"},\n\t\t\t{\"rating\", 7.1},\n\t\t},\n\t\tbson.D{\n\t\t\t{\"name\", \"Gaggan\"},\n\t\t\t{\"cuisine\", \"Thai Fusion\"},\n\t\t\t{\"city\", \"Bangkok\"},\n\t\t\t{\"country\", \"Thailand\"},\n\t\t\t{\"rating\", 9.2},\n\t\t},\n\t\tbson.D{\n\t\t\t{\"name\", \"Dad's Grill\"},\n\t\t\t{\"cuisine\", \"BBQ\"},\n\t\t\t{\"city\", \"Oklahoma City\"},\n\t\t\t{\"state\", \"Oklahoma\"},\n\t\t\t{\"country\", \"United States\"},\n\t\t\t{\"rating\", 2.1},\n\t\t},\n\t}\n\n\trecordsResult, recordsErr := recordsColl.InsertMany(ctx, records)\n\trestaurantsResult, restaurantsErr := restaurantsColl.InsertMany(ctx, restaurants)\n\n\tassert.NoError(t, recordsErr)\n\tassert.Len(t, recordsResult.InsertedIDs, 3)\n\tassert.NoError(t, restaurantsErr)\n\tassert.Len(t, restaurantsResult.InsertedIDs, 5)\n\n\t{\n\t\t// Start Index Example 1\n\t\tindexModel := mongo.IndexModel{\n\t\t\tKeys: bson.D{\n\t\t\t\t{\"score\", 1},\n\t\t\t},\n\t\t}\n\t\t_, err := recordsColl.Indexes().CreateOne(ctx, indexModel)\n\n\t\t// End Index Example 1\n\n\t\tassert.NoError(t, err)\n\t}\n\t{\n\t\t// Start Index Example 2\n\t\tpartialFilterExpression := bson.D{\n\t\t\t{\"rating\", bson.D{\n\t\t\t\t{\"$gt\", 5},\n\t\t\t}},\n\t\t}\n\t\tindexModel := mongo.IndexModel{\n\t\t\tKeys: bson.D{\n\t\t\t\t{\"cuisine\", 1},\n\t\t\t\t{\"name\", 1},\n\t\t\t},\n\t\t\tOptions: options.Index().SetPartialFilterExpression(partialFilterExpression),\n\t\t}\n\n\t\t_, err := restaurantsColl.Indexes().CreateOne(ctx, indexModel)\n\n\t\t// End Index Example 2\n\n\t\tassert.NoError(t, err)\n\t}\n}\n\n// Start Versioned API Example 1\n\n// StableAPIExample is an example of creating a client with stable API.\nfunc StableAPIExample() {\n\tctx := context.TODO()\n\t// For a replica set, include the replica set name and a seedlist of the members in the URI string; e.g.\n\t// uri := \"mongodb://mongodb0.example.com:27017,mongodb1.example.com:27017/?replicaSet=myRepl\"\n\t// For a sharded cluster, connect to the mongos instances; e.g.\n\t// uri := \"mongodb://mongos0.example.com:27017,mongos1.example.com:27017/\"\n\turi := mtest.ClusterURI()\n\n\tserverAPIOptions := options.ServerAPI(options.ServerAPIVersion1)\n\tclientOpts := options.Client().ApplyURI(uri).SetServerAPIOptions(serverAPIOptions)\n\tclient, err := mongo.Connect(clientOpts)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tdefer func() { _ = client.Disconnect(ctx) }()\n}\n\n// End Versioned API Example 1\n\n// Start Versioned API Example 2\n\n// StableAPIStrictExample is an example of creating a client with strict stable API.\nfunc StableAPIStrictExample() {\n\tctx := context.TODO()\n\t// For a replica set, include the replica set name and a seedlist of the members in the URI string; e.g.\n\t// uri := \"mongodb://mongodb0.example.com:27017,mongodb1.example.com:27017/?replicaSet=myRepl\"\n\t// For a sharded cluster, connect to the mongos instances; e.g.\n\t// uri := \"mongodb://mongos0.example.com:27017,mongos1.example.com:27017/\"\n\turi := mtest.ClusterURI()\n\n\tserverAPIOptions := options.ServerAPI(options.ServerAPIVersion1).SetStrict(true)\n\tclientOpts := options.Client().ApplyURI(uri).SetServerAPIOptions(serverAPIOptions)\n\tclient, err := mongo.Connect(clientOpts)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tdefer func() { _ = client.Disconnect(ctx) }()\n}\n\n// End Versioned API Example 2\n\n// Start Versioned API Example 3\n\n// StableAPINonStrictExample is an example of creating a client with non-strict stable API.\nfunc StableAPINonStrictExample() {\n\tctx := context.TODO()\n\t// For a replica set, include the replica set name and a seedlist of the members in the URI string; e.g.\n\t// uri := \"mongodb://mongodb0.example.com:27017,mongodb1.example.com:27017/?replicaSet=myRepl\"\n\t// For a sharded cluster, connect to the mongos instances; e.g.\n\t// uri := \"mongodb://mongos0.example.com:27017,mongos1.example.com:27017/\"\n\turi := mtest.ClusterURI()\n\n\tserverAPIOptions := options.ServerAPI(options.ServerAPIVersion1).SetStrict(false)\n\tclientOpts := options.Client().ApplyURI(uri).SetServerAPIOptions(serverAPIOptions)\n\tclient, err := mongo.Connect(clientOpts)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tdefer func() { _ = client.Disconnect(ctx) }()\n}\n\n// End Versioned API Example 3\n\n// Start Versioned API Example 4\n\n// StableAPIDeprecationErrorsExample is an example of creating a client with stable API\n// with deprecation errors.\nfunc StableAPIDeprecationErrorsExample() {\n\tctx := context.TODO()\n\t// For a replica set, include the replica set name and a seedlist of the members in the URI string; e.g.\n\t// uri := \"mongodb://mongodb0.example.com:27017,mongodb1.example.com:27017/?replicaSet=myRepl\"\n\t// For a sharded cluster, connect to the mongos instances; e.g.\n\t// uri := \"mongodb://mongos0.example.com:27017,mongos1.example.com:27017/\"\n\turi := mtest.ClusterURI()\n\n\tserverAPIOptions := options.ServerAPI(options.ServerAPIVersion1).SetDeprecationErrors(true)\n\tclientOpts := options.Client().ApplyURI(uri).SetServerAPIOptions(serverAPIOptions)\n\tclient, err := mongo.Connect(clientOpts)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tdefer func() { _ = client.Disconnect(ctx) }()\n}\n\n// End Versioned API Example 4\n\n// StableAPIStrictCountExample is an example of using CountDocuments instead of a traditional count\n// with a strict stable API since the count command does not belong to API version 1.\nfunc StableAPIStrictCountExample(t *testing.T) {\n\t// TODO(GODRIVER-2482): The \"count\" command is now part of Stable API v1 in MongoDB v5.0.x and\n\t// TODO v6.x, so this example no longer works correctly in any CI tested server version. Rewrite\n\t// TODO this example with a command that is not part of Stable API v1. For now, this example\n\t// TODO test is always skipped.\n\turi := mtest.ClusterURI()\n\n\tserverAPIOptions := options.ServerAPI(options.ServerAPIVersion1).SetStrict(true)\n\tclientOpts := options.Client().ApplyURI(uri).SetServerAPIOptions(serverAPIOptions)\n\n\tclient, err := mongo.Connect(clientOpts)\n\tassert.Nil(t, err, \"Connect error: %v\", err)\n\tdefer func() { _ = client.Disconnect(context.TODO()) }()\n\n\t// Start Versioned API Example 5\n\n\tcoll := client.Database(\"db\").Collection(\"sales\")\n\tdocs := []any{\n\t\tbson.D{{\"_id\", 1}, {\"item\", \"abc\"}, {\"price\", 10}, {\"quantity\", 2}, {\"date\", \"2021-01-01T08:00:00Z\"}},\n\t\tbson.D{{\"_id\", 2}, {\"item\", \"jkl\"}, {\"price\", 20}, {\"quantity\", 1}, {\"date\", \"2021-02-03T09:00:00Z\"}},\n\t\tbson.D{{\"_id\", 3}, {\"item\", \"xyz\"}, {\"price\", 5}, {\"quantity\", 5}, {\"date\", \"2021-02-03T09:05:00Z\"}},\n\t\tbson.D{{\"_id\", 4}, {\"item\", \"abc\"}, {\"price\", 10}, {\"quantity\", 10}, {\"date\", \"2021-02-15T08:00:00Z\"}},\n\t\tbson.D{{\"_id\", 5}, {\"item\", \"xyz\"}, {\"price\", 5}, {\"quantity\", 10}, {\"date\", \"2021-02-15T09:05:00Z\"}},\n\t\tbson.D{{\"_id\", 6}, {\"item\", \"xyz\"}, {\"price\", 5}, {\"quantity\", 5}, {\"date\", \"2021-02-15T12:05:10Z\"}},\n\t\tbson.D{{\"_id\", 7}, {\"item\", \"xyz\"}, {\"price\", 5}, {\"quantity\", 10}, {\"date\", \"2021-02-15T14:12:12Z\"}},\n\t\tbson.D{{\"_id\", 8}, {\"item\", \"abc\"}, {\"price\", 10}, {\"quantity\", 5}, {\"date\", \"2021-03-16T20:20:13Z\"}},\n\t}\n\t_, err = coll.InsertMany(context.TODO(), docs)\n\n\t// End Versioned API Example 5\n\tdefer func() { _ = coll.Drop(context.TODO()) }()\n\tassert.Nil(t, err, \"InsertMany error: %v\", err)\n\n\tres := client.Database(\"db\").RunCommand(context.TODO(), bson.D{{\"count\", \"sales\"}})\n\tassert.NotNil(t, res.Err(), \"expected RunCommand error, got nil\")\n\texpectedErr := \"Provided apiStrict:true, but the command count is not in API Version 1\"\n\tassert.True(t, strings.Contains(res.Err().Error(), expectedErr),\n\t\t\"expected RunCommand error to contain %q, got %q\", expectedErr, res.Err().Error())\n\n\t// Start Versioned API Example 6\n\n\t// (APIStrictError) Provided apiStrict:true, but the command count is not in API Version 1.\n\n\t// End Versioned API Example 6\n\n\t// Start Versioned API Example 7\n\n\tcount, err := coll.CountDocuments(context.TODO(), bson.D{})\n\n\t// End Versioned API Example 7\n\tassert.Nil(t, err, \"CountDocuments error: %v\", err)\n\tassert.Equal(t, count, int64(8), \"expected count to be 8, got %v\", count)\n\n\t// Start Versioned API Example 8\n\n\t// 8\n\n\t// End Versioned API Example 8\n}\n\n// StableAPIExamples runs all stable API examples.\n// These appear at https://www.mongodb.com/docs/manual/reference/stable-api/.\nfunc StableAPIExamples() {\n\tStableAPIExample()\n\tStableAPIStrictExample()\n\tStableAPINonStrictExample()\n\tStableAPIDeprecationErrorsExample()\n}\n\nfunc insertSnapshotQueryTestData(mt *mtest.T) {\n\tcatColl := mt.CreateCollection(mtest.Collection{Name: \"cats\"}, true)\n\t_, err := catColl.InsertMany(context.Background(), []any{\n\t\tbson.D{\n\t\t\t{\"adoptable\", false},\n\t\t\t{\"name\", \"Miyagi\"},\n\t\t\t{\"color\", \"grey-white\"},\n\t\t\t{\"age\", 14},\n\t\t},\n\t\tbson.D{\n\t\t\t{\"adoptable\", true},\n\t\t\t{\"name\", \"Joyce\"},\n\t\t\t{\"color\", \"black\"},\n\t\t\t{\"age\", 10},\n\t\t},\n\t})\n\tassert.NoError(mt, err)\n\n\tdogColl := mt.CreateCollection(mtest.Collection{Name: \"dogs\"}, true)\n\t_, err = dogColl.InsertMany(context.Background(), []any{\n\t\tbson.D{\n\t\t\t{\"adoptable\", true},\n\t\t\t{\"name\", \"Cormac\"},\n\t\t\t{\"color\", \"rust\"},\n\t\t\t{\"age\", 7},\n\t\t},\n\t\tbson.D{\n\t\t\t{\"adoptable\", true},\n\t\t\t{\"name\", \"Frank\"},\n\t\t\t{\"color\", \"yellow\"},\n\t\t\t{\"age\", 2},\n\t\t},\n\t})\n\tassert.NoError(mt, err)\n\n\tsalesColl := mt.CreateCollection(mtest.Collection{Name: \"sales\"}, true)\n\t_, err = salesColl.InsertMany(context.Background(), []any{\n\t\tbson.D{\n\t\t\t{\"shoeType\", \"hiking boot\"},\n\t\t\t{\"price\", 30.0},\n\t\t\t{\"saleDate\", time.Now()},\n\t\t},\n\t})\n\tassert.NoError(mt, err)\n}\n\nfunc snapshotQueryPetExample(mt *mtest.T) error {\n\tclient := mt.Client\n\tdb := mt.DB\n\n\t// Start Snapshot Query Example 1\n\tctx := context.TODO()\n\n\tsess, err := client.StartSession(options.Session().SetSnapshot(true))\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer sess.EndSession(ctx)\n\n\tvar adoptablePetsCount int32\n\terr = mongo.WithSession(ctx, sess, func(ctx context.Context) error {\n\t\t// Count the adoptable cats\n\t\tconst adoptableCatsOutput = \"adoptableCatsCount\"\n\t\tcursor, err := db.Collection(\"cats\").Aggregate(ctx, mongo.Pipeline{\n\t\t\tbson.D{{\"$match\", bson.D{{\"adoptable\", true}}}},\n\t\t\tbson.D{{\"$count\", adoptableCatsOutput}},\n\t\t})\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif !cursor.Next(ctx) {\n\t\t\treturn fmt.Errorf(\"expected aggregate to return a document, but got none\")\n\t\t}\n\n\t\tresp := cursor.Current.Lookup(adoptableCatsOutput)\n\t\tadoptableCatsCount, ok := resp.Int32OK()\n\t\tif !ok {\n\t\t\treturn fmt.Errorf(\"failed to find int32 field %q in document %v\", adoptableCatsOutput, cursor.Current)\n\t\t}\n\t\tadoptablePetsCount += adoptableCatsCount\n\n\t\t// Count the adoptable dogs\n\t\tconst adoptableDogsOutput = \"adoptableDogsCount\"\n\t\tcursor, err = db.Collection(\"dogs\").Aggregate(ctx, mongo.Pipeline{\n\t\t\tbson.D{{\"$match\", bson.D{{\"adoptable\", true}}}},\n\t\t\tbson.D{{\"$count\", adoptableDogsOutput}},\n\t\t})\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif !cursor.Next(ctx) {\n\t\t\treturn fmt.Errorf(\"expected aggregate to return a document, but got none\")\n\t\t}\n\n\t\tresp = cursor.Current.Lookup(adoptableDogsOutput)\n\t\tadoptableDogsCount, ok := resp.Int32OK()\n\t\tif !ok {\n\t\t\treturn fmt.Errorf(\"failed to find int32 field %q in document %v\", adoptableDogsOutput, cursor.Current)\n\t\t}\n\t\tadoptablePetsCount += adoptableDogsCount\n\t\treturn nil\n\t})\n\tif err != nil {\n\t\treturn err\n\t}\n\t// End Snapshot Query Example 1\n\tassert.Equal(mt, int32(3), adoptablePetsCount, \"expected 3 total adoptable pets\")\n\treturn nil\n}\n\nfunc snapshotQueryRetailExample(mt *mtest.T) error {\n\tclient := mt.Client\n\tdb := mt.DB\n\n\t// Start Snapshot Query Example 2\n\tctx := context.TODO()\n\n\tsess, err := client.StartSession(options.Session().SetSnapshot(true))\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer sess.EndSession(ctx)\n\n\tvar totalDailySales int32\n\terr = mongo.WithSession(ctx, sess, func(ctx context.Context) error {\n\t\t// Count the total daily sales\n\t\tconst totalDailySalesOutput = \"totalDailySales\"\n\t\tcursor, err := db.Collection(\"sales\").Aggregate(ctx, mongo.Pipeline{\n\t\t\tbson.D{{\n\t\t\t\t\"$match\",\n\t\t\t\tbson.D{{\n\t\t\t\t\t\"$expr\",\n\t\t\t\t\tbson.D{{\n\t\t\t\t\t\t\"$gt\",\n\t\t\t\t\t\tbson.A{\n\t\t\t\t\t\t\t\"$saleDate\",\n\t\t\t\t\t\t\tbson.D{{\n\t\t\t\t\t\t\t\t\"$dateSubtract\",\n\t\t\t\t\t\t\t\tbson.D{\n\t\t\t\t\t\t\t\t\t{\"startDate\", \"$$NOW\"},\n\t\t\t\t\t\t\t\t\t{\"unit\", \"day\"},\n\t\t\t\t\t\t\t\t\t{\"amount\", 1},\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t}},\n\t\t\t\t\t\t},\n\t\t\t\t\t}},\n\t\t\t\t}},\n\t\t\t}},\n\t\t\tbson.D{{\"$count\", totalDailySalesOutput}},\n\t\t})\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif !cursor.Next(ctx) {\n\t\t\treturn fmt.Errorf(\"expected aggregate to return a document, but got none\")\n\t\t}\n\n\t\tresp := cursor.Current.Lookup(totalDailySalesOutput)\n\n\t\tvar ok bool\n\t\ttotalDailySales, ok = resp.Int32OK()\n\t\tif !ok {\n\t\t\treturn fmt.Errorf(\"failed to find int32 field %q in document %v\", totalDailySalesOutput, cursor.Current)\n\t\t}\n\t\treturn nil\n\t})\n\tif err != nil {\n\t\treturn err\n\t}\n\t// End Snapshot Query Example 2\n\tassert.Equal(mt, int32(1), totalDailySales, \"expected 1 total daily sale\")\n\treturn nil\n}\n\n// SnapshotQueryExamples runs examples of using sessions with Snapshot enabled.\n// These appear at https://www.mongodb.com/docs/manual/tutorial/long-running-queries/.\nfunc SnapshotQueryExamples(mt *mtest.T) {\n\tinsertSnapshotQueryTestData(mt)\n\tassert.NoError(mt, snapshotQueryPetExample(mt))\n\tassert.NoError(mt, snapshotQueryRetailExample(mt))\n}\n"
  },
  {
    "path": "internal/docexamples/examples_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage docexamples_test\n\nimport (\n\t\"context\"\n\t\"flag\"\n\t\"log\"\n\t\"os\"\n\t\"testing\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/docexamples\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/integration/mtest\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n)\n\nfunc TestMain(m *testing.M) {\n\t// All tests that use mtest.Setup() are expected to be integration tests, so skip them when the\n\t// -short flag is included in the \"go test\" command. Also, we have to parse flags here to use\n\t// testing.Short() because flags aren't parsed before TestMain() is called.\n\tflag.Parse()\n\tif testing.Short() {\n\t\tlog.Print(\"skipping mtest integration test in short mode\")\n\t\treturn\n\t}\n\n\tif err := mtest.Setup(); err != nil {\n\t\tlog.Panic(err)\n\t}\n\tdefer os.Exit(m.Run())\n\tif err := mtest.Teardown(); err != nil {\n\t\tlog.Panic(err)\n\t}\n}\n\nfunc TestDocumentationExamples(t *testing.T) {\n\tctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)\n\tdefer cancel()\n\n\tclient, err := mongo.Connect(options.Client().ApplyURI(mtest.ClusterURI()))\n\tassert.NoError(t, err)\n\tdefer client.Disconnect(ctx)\n\n\tdb := client.Database(\"docexamples\")\n\n\tt.Run(\"InsertExamples\", func(t *testing.T) {\n\t\tdocexamples.InsertExamples(t, db)\n\t})\n\tt.Run(\"QueryArraysExamples\", func(t *testing.T) {\n\t\tdocexamples.QueryArraysExamples(t, db)\n\t})\n\tt.Run(\"ProjectionExamples\", func(t *testing.T) {\n\t\tdocexamples.ProjectionExamples(t, db)\n\t})\n\tt.Run(\"UpdateExamples\", func(t *testing.T) {\n\t\tdocexamples.UpdateExamples(t, db)\n\t})\n\tt.Run(\"DeleteExamples\", func(t *testing.T) {\n\t\tdocexamples.DeleteExamples(t, db)\n\t})\n\tt.Run(\"RunCommandExamples\", func(t *testing.T) {\n\t\tdocexamples.RunCommandExamples(t, db)\n\t})\n\tt.Run(\"IndexExamples\", func(t *testing.T) {\n\t\tdocexamples.IndexExamples(t, db)\n\t})\n\tt.Run(\"StableAPExamples\", func(*testing.T) {\n\t\tdocexamples.StableAPIExamples()\n\t})\n\tt.Run(\"QueryToplevelFieldsExamples\", func(t *testing.T) {\n\t\tdocexamples.QueryToplevelFieldsExamples(t, db)\n\t})\n\tt.Run(\"QueryEmbeddedDocumentsExamples\", func(t *testing.T) {\n\t\tdocexamples.QueryEmbeddedDocumentsExamples(t, db)\n\t})\n\tt.Run(\"QueryArrayEmbeddedDocumentsExamples\", func(t *testing.T) {\n\t\tdocexamples.QueryArrayEmbeddedDocumentsExamples(t, db)\n\t})\n\tt.Run(\"QueryNullMissingFieldsExamples\", func(t *testing.T) {\n\t\tdocexamples.QueryNullMissingFieldsExamples(t, db)\n\t})\n\n\tmt := mtest.New(t)\n\n\t// Stable API is supported in 5.0+\n\tmtOpts := mtest.NewOptions().MinServerVersion(\"5.0\")\n\tmt.RunOpts(\"StableAPIStrictCountExample\", mtOpts, func(mt *mtest.T) {\n\t\t// TODO(GODRIVER-2482): Unskip when this test is rewritten to work with Stable API v1.\n\t\tmt.Skip(`skipping because \"count\" is now part of Stable API v1; see GODRIVER-2482`)\n\n\t\tdocexamples.StableAPIStrictCountExample(mt.T)\n\t})\n\n\t// Snapshot queries can only run on 5.0+ non-sharded and sharded replicasets.\n\tmtOpts = mtest.NewOptions().MinServerVersion(\"5.0\").Topologies(mtest.ReplicaSet, mtest.Sharded)\n\tmt.RunOpts(\"SnapshotQueryExamples\", mtOpts, func(mt *mtest.T) {\n\t\tdocexamples.SnapshotQueryExamples(mt)\n\t})\n\n\t// Only 3.6+ supports $lookup in aggregations as is used in aggregation examples. No\n\t// collection needs to be created, and collection creation can sometimes cause failures\n\t// on sharded clusters.\n\tmtOpts = mtest.NewOptions().MinServerVersion(\"3.6\").CreateCollection(false)\n\tmt.RunOpts(\"AggregationExamples\", mtOpts, func(mt *mtest.T) {\n\t\tdocexamples.AggregationExamples(mt.T, db)\n\t})\n\n\t// Transaction examples can only run on replica sets on 4.0+.\n\tmtOpts = mtest.NewOptions().MinServerVersion(\"4.0\").Topologies(mtest.ReplicaSet)\n\tmt.RunOpts(\"TransactionsExamples\", mtOpts, func(mt *mtest.T) {\n\t\tmt.ResetClient(options.Client().ApplyURI(mtest.ClusterURI()))\n\t\tdocexamples.TransactionsExamples(ctx, mt.Client)\n\t})\n\tmt.RunOpts(\"WithTransactionExample\", mtOpts, func(mt *mtest.T) {\n\t\tmt.ResetClient(options.Client().ApplyURI(mtest.ClusterURI()))\n\t\tdocexamples.WithTransactionExample(ctx)\n\t})\n\n\t// Change stream examples can only run on replica sets on 3.6+.\n\tmtOpts = mtest.NewOptions().MinServerVersion(\"3.6\").Topologies(mtest.ReplicaSet)\n\tmt.RunOpts(\"ChangeStreamExamples\", mtOpts, func(mt *mtest.T) {\n\t\tmt.ResetClient(options.Client().ApplyURI(mtest.ClusterURI()))\n\t\tcsdb := client.Database(\"changestream_examples\")\n\t\tdocexamples.ChangeStreamExamples(mt.T, csdb)\n\t})\n\n\tmtOpts = mtest.NewOptions().Topologies(mtest.ReplicaSet)\n\tmt.RunOpts(\"CausalConsistencyExamples\", mtOpts, func(mt *mtest.T) {\n\t\tdocexamples.CausalConsistencyExamples(mt.Client)\n\t})\n}\n"
  },
  {
    "path": "internal/driverutil/description.go",
    "content": "// Copyright (C) MongoDB, Inc. 2024-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage driverutil\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/bsonutil\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/handshake\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/ptrutil\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/address\"\n\t\"go.mongodb.org/mongo-driver/v2/tag\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/description\"\n)\n\nconst (\n\tMinWireVersion = 8\n\tMaxWireVersion = 25\n)\n\nfunc equalWireVersion(wv1, wv2 *description.VersionRange) bool {\n\tif wv1 == nil && wv2 == nil {\n\t\treturn true\n\t}\n\n\tif wv1 == nil || wv2 == nil {\n\t\treturn false\n\t}\n\n\treturn wv1.Min == wv2.Min && wv1.Max == wv2.Max\n}\n\n// EqualServers compares two server descriptions and returns true if they are\n// equal.\nfunc EqualServers(srv1, srv2 description.Server) bool {\n\tif srv1.CanonicalAddr.String() != srv2.CanonicalAddr.String() {\n\t\treturn false\n\t}\n\n\tif !sliceStringEqual(srv1.Arbiters, srv2.Arbiters) {\n\t\treturn false\n\t}\n\n\tif !sliceStringEqual(srv1.Hosts, srv2.Hosts) {\n\t\treturn false\n\t}\n\n\tif !sliceStringEqual(srv1.Passives, srv2.Passives) {\n\t\treturn false\n\t}\n\n\tif srv1.Primary != srv2.Primary {\n\t\treturn false\n\t}\n\n\tif srv1.SetName != srv2.SetName {\n\t\treturn false\n\t}\n\n\tif srv1.Kind != srv2.Kind {\n\t\treturn false\n\t}\n\n\tif srv1.LastError != nil || srv2.LastError != nil {\n\t\tif srv1.LastError == nil || srv2.LastError == nil {\n\t\t\treturn false\n\t\t}\n\t\tif srv1.LastError.Error() != srv2.LastError.Error() {\n\t\t\treturn false\n\t\t}\n\t}\n\n\tif !equalWireVersion(srv1.WireVersion, srv2.WireVersion) {\n\t\treturn false\n\t}\n\n\tif len(srv1.Tags) != len(srv2.Tags) || !srv1.Tags.ContainsAll(srv2.Tags) {\n\t\treturn false\n\t}\n\n\tif srv1.SetVersion != srv2.SetVersion {\n\t\treturn false\n\t}\n\n\tif srv1.ElectionID != srv2.ElectionID {\n\t\treturn false\n\t}\n\n\tif ptrutil.CompareInt64(srv1.SessionTimeoutMinutes, srv2.SessionTimeoutMinutes) != 0 {\n\t\treturn false\n\t}\n\n\t// If TopologyVersion is nil for both servers, CompareToIncoming will return -1 because it assumes that the\n\t// incoming response is newer. We want the descriptions to be considered equal in this case, though, so an\n\t// explicit check is required.\n\tif srv1.TopologyVersion == nil && srv2.TopologyVersion == nil {\n\t\treturn true\n\t}\n\n\treturn CompareTopologyVersions(srv1.TopologyVersion, srv2.TopologyVersion) == 0\n}\n\n// IsServerLoadBalanced checks if a description.Server describes a server that\n// is load balanced.\nfunc IsServerLoadBalanced(srv description.Server) bool {\n\treturn srv.Kind == description.ServerKindLoadBalancer || srv.ServiceID != nil\n}\n\n// stringSliceFromRawElement decodes the provided BSON element into a []string.\n// This internally calls StringSliceFromRawValue on the element's value. The\n// error conditions outlined in that function's documentation apply for this\n// function as well.\nfunc stringSliceFromRawElement(element bson.RawElement) ([]string, error) {\n\treturn bsonutil.StringSliceFromRawValue(element.Key(), element.Value())\n}\n\nfunc decodeStringMap(element bson.RawElement, name string) (map[string]string, error) {\n\tdoc, ok := element.Value().DocumentOK()\n\tif !ok {\n\t\treturn nil, fmt.Errorf(\"expected '%s' to be a document but it's a BSON %s\", name, element.Value().Type)\n\t}\n\telements, err := doc.Elements()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tm := make(map[string]string)\n\tfor _, element := range elements {\n\t\tkey := element.Key()\n\t\tvalue, ok := element.Value().StringValueOK()\n\t\tif !ok {\n\t\t\treturn nil, fmt.Errorf(\"expected '%s' to be a document of strings, but found a BSON %s\", name, element.Value().Type)\n\t\t}\n\t\tm[key] = value\n\t}\n\treturn m, nil\n}\n\n// NewTopologyVersion creates a TopologyVersion based on doc\nfunc NewTopologyVersion(doc bson.Raw) (*description.TopologyVersion, error) {\n\telements, err := doc.Elements()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar tv description.TopologyVersion\n\tvar ok bool\n\tfor _, element := range elements {\n\t\tswitch element.Key() {\n\t\tcase \"processId\":\n\t\t\ttv.ProcessID, ok = element.Value().ObjectIDOK()\n\t\t\tif !ok {\n\t\t\t\treturn nil, fmt.Errorf(\"expected 'processId' to be a objectID but it's a BSON %s\", element.Value().Type)\n\t\t\t}\n\t\tcase \"counter\":\n\t\t\ttv.Counter, ok = element.Value().Int64OK()\n\t\t\tif !ok {\n\t\t\t\treturn nil, fmt.Errorf(\"expected 'counter' to be an int64 but it's a BSON %s\", element.Value().Type)\n\t\t\t}\n\t\t}\n\t}\n\treturn &tv, nil\n}\n\n// NewVersionRange creates a new VersionRange given a min and a max.\nfunc NewVersionRange(min, max int32) description.VersionRange {\n\treturn description.VersionRange{Min: min, Max: max}\n}\n\n// VersionRangeIncludes returns a bool indicating whether the supplied integer\n// is included in the range.\nfunc VersionRangeIncludes(versionRange description.VersionRange, v int32) bool {\n\treturn v >= versionRange.Min && v <= versionRange.Max\n}\n\n// CompareTopologyVersions compares the receiver, which represents the currently\n// known TopologyVersion for a server, to an incoming TopologyVersion extracted\n// from a server command response.\n//\n// This returns -1 if the receiver version is less than the response, 0 if the\n// versions are equal, and 1 if the receiver version is greater than the\n// response. This comparison is not commutative.\nfunc CompareTopologyVersions(receiver, response *description.TopologyVersion) int {\n\tif receiver == nil || response == nil {\n\t\treturn -1\n\t}\n\tif receiver.ProcessID != response.ProcessID {\n\t\treturn -1\n\t}\n\tif receiver.Counter == response.Counter {\n\t\treturn 0\n\t}\n\tif receiver.Counter < response.Counter {\n\t\treturn -1\n\t}\n\treturn 1\n}\n\n// NewServerDescription creates a new server description from the given hello\n// command response.\nfunc NewServerDescription(addr address.Address, response bson.Raw) description.Server {\n\tdesc := description.Server{Addr: addr, CanonicalAddr: addr, LastUpdateTime: time.Now().UTC()}\n\telements, err := response.Elements()\n\tif err != nil {\n\t\tdesc.LastError = err\n\t\treturn desc\n\t}\n\tvar ok bool\n\tvar isReplicaSet, isWritablePrimary, hidden, secondary, arbiterOnly bool\n\tvar msg string\n\tvar versionRange description.VersionRange\n\tfor _, element := range elements {\n\t\tswitch element.Key() {\n\t\tcase \"arbiters\":\n\t\t\tvar err error\n\t\t\tdesc.Arbiters, err = stringSliceFromRawElement(element)\n\t\t\tif err != nil {\n\t\t\t\tdesc.LastError = err\n\t\t\t\treturn desc\n\t\t\t}\n\t\tcase \"arbiterOnly\":\n\t\t\tarbiterOnly, ok = element.Value().BooleanOK()\n\t\t\tif !ok {\n\t\t\t\tdesc.LastError = fmt.Errorf(\"expected 'arbiterOnly' to be a boolean but it's a BSON %s\", element.Value().Type)\n\t\t\t\treturn desc\n\t\t\t}\n\t\tcase \"compression\":\n\t\t\tvar err error\n\t\t\tdesc.Compression, err = stringSliceFromRawElement(element)\n\t\t\tif err != nil {\n\t\t\t\tdesc.LastError = err\n\t\t\t\treturn desc\n\t\t\t}\n\t\tcase \"electionId\":\n\t\t\tdesc.ElectionID, ok = element.Value().ObjectIDOK()\n\t\t\tif !ok {\n\t\t\t\tdesc.LastError = fmt.Errorf(\"expected 'electionId' to be a objectID but it's a BSON %s\", element.Value().Type)\n\t\t\t\treturn desc\n\t\t\t}\n\t\tcase \"iscryptd\":\n\t\t\tdesc.IsCryptd, ok = element.Value().BooleanOK()\n\t\t\tif !ok {\n\t\t\t\tdesc.LastError = fmt.Errorf(\"expected 'iscryptd' to be a boolean but it's a BSON %s\", element.Value().Type)\n\t\t\t\treturn desc\n\t\t\t}\n\t\tcase \"helloOk\":\n\t\t\tdesc.HelloOK, ok = element.Value().BooleanOK()\n\t\t\tif !ok {\n\t\t\t\tdesc.LastError = fmt.Errorf(\"expected 'helloOk' to be a boolean but it's a BSON %s\", element.Value().Type)\n\t\t\t\treturn desc\n\t\t\t}\n\t\tcase \"hidden\":\n\t\t\thidden, ok = element.Value().BooleanOK()\n\t\t\tif !ok {\n\t\t\t\tdesc.LastError = fmt.Errorf(\"expected 'hidden' to be a boolean but it's a BSON %s\", element.Value().Type)\n\t\t\t\treturn desc\n\t\t\t}\n\t\tcase \"hosts\":\n\t\t\tvar err error\n\t\t\tdesc.Hosts, err = stringSliceFromRawElement(element)\n\t\t\tif err != nil {\n\t\t\t\tdesc.LastError = err\n\t\t\t\treturn desc\n\t\t\t}\n\t\tcase \"isWritablePrimary\":\n\t\t\tisWritablePrimary, ok = element.Value().BooleanOK()\n\t\t\tif !ok {\n\t\t\t\tdesc.LastError = fmt.Errorf(\"expected 'isWritablePrimary' to be a boolean but it's a BSON %s\", element.Value().Type)\n\t\t\t\treturn desc\n\t\t\t}\n\t\tcase handshake.LegacyHelloLowercase:\n\t\t\tisWritablePrimary, ok = element.Value().BooleanOK()\n\t\t\tif !ok {\n\t\t\t\tdesc.LastError = fmt.Errorf(\"expected legacy hello to be a boolean but it's a BSON %s\", element.Value().Type)\n\t\t\t\treturn desc\n\t\t\t}\n\t\tcase \"isreplicaset\":\n\t\t\tisReplicaSet, ok = element.Value().BooleanOK()\n\t\t\tif !ok {\n\t\t\t\tdesc.LastError = fmt.Errorf(\"expected 'isreplicaset' to be a boolean but it's a BSON %s\", element.Value().Type)\n\t\t\t\treturn desc\n\t\t\t}\n\t\tcase \"lastWrite\":\n\t\t\tlastWrite, ok := element.Value().DocumentOK()\n\t\t\tif !ok {\n\t\t\t\tdesc.LastError = fmt.Errorf(\"expected 'lastWrite' to be a document but it's a BSON %s\", element.Value().Type)\n\t\t\t\treturn desc\n\t\t\t}\n\t\t\tdateTime, err := lastWrite.LookupErr(\"lastWriteDate\")\n\t\t\tif err == nil {\n\t\t\t\tdt, ok := dateTime.DateTimeOK()\n\t\t\t\tif !ok {\n\t\t\t\t\tdesc.LastError = fmt.Errorf(\"expected 'lastWriteDate' to be a datetime but it's a BSON %s\", dateTime.Type)\n\t\t\t\t\treturn desc\n\t\t\t\t}\n\t\t\t\tdesc.LastWriteTime = time.Unix(dt/1000, dt%1000*1000000).UTC()\n\t\t\t}\n\t\tcase \"logicalSessionTimeoutMinutes\":\n\t\t\ti64, ok := element.Value().AsInt64OK()\n\t\t\tif !ok {\n\t\t\t\tdesc.LastError = fmt.Errorf(\"expected 'logicalSessionTimeoutMinutes' to be an integer but it's a BSON %s\", element.Value().Type)\n\t\t\t\treturn desc\n\t\t\t}\n\n\t\t\tdesc.SessionTimeoutMinutes = &i64\n\t\tcase \"maxBsonObjectSize\":\n\t\t\ti64, ok := element.Value().AsInt64OK()\n\t\t\tif !ok {\n\t\t\t\tdesc.LastError = fmt.Errorf(\"expected 'maxBsonObjectSize' to be an integer but it's a BSON %s\", element.Value().Type)\n\t\t\t\treturn desc\n\t\t\t}\n\t\t\tdesc.MaxDocumentSize = uint32(i64)\n\t\tcase \"maxMessageSizeBytes\":\n\t\t\ti64, ok := element.Value().AsInt64OK()\n\t\t\tif !ok {\n\t\t\t\tdesc.LastError = fmt.Errorf(\"expected 'maxMessageSizeBytes' to be an integer but it's a BSON %s\", element.Value().Type)\n\t\t\t\treturn desc\n\t\t\t}\n\t\t\tdesc.MaxMessageSize = uint32(i64)\n\t\tcase \"maxWriteBatchSize\":\n\t\t\ti64, ok := element.Value().AsInt64OK()\n\t\t\tif !ok {\n\t\t\t\tdesc.LastError = fmt.Errorf(\"expected 'maxWriteBatchSize' to be an integer but it's a BSON %s\", element.Value().Type)\n\t\t\t\treturn desc\n\t\t\t}\n\t\t\tdesc.MaxBatchCount = uint32(i64)\n\t\tcase \"me\":\n\t\t\tme, ok := element.Value().StringValueOK()\n\t\t\tif !ok {\n\t\t\t\tdesc.LastError = fmt.Errorf(\"expected 'me' to be a string but it's a BSON %s\", element.Value().Type)\n\t\t\t\treturn desc\n\t\t\t}\n\t\t\tdesc.CanonicalAddr = address.Address(me).Canonicalize()\n\t\tcase \"maxWireVersion\":\n\t\t\tverMax, ok := element.Value().AsInt64OK()\n\t\t\tversionRange.Max = int32(verMax)\n\t\t\tif !ok {\n\t\t\t\tdesc.LastError = fmt.Errorf(\"expected 'maxWireVersion' to be an integer but it's a BSON %s\", element.Value().Type)\n\t\t\t\treturn desc\n\t\t\t}\n\t\tcase \"minWireVersion\":\n\t\t\tverMin, ok := element.Value().AsInt64OK()\n\t\t\tversionRange.Min = int32(verMin)\n\t\t\tif !ok {\n\t\t\t\tdesc.LastError = fmt.Errorf(\"expected 'minWireVersion' to be an integer but it's a BSON %s\", element.Value().Type)\n\t\t\t\treturn desc\n\t\t\t}\n\t\tcase \"msg\":\n\t\t\tmsg, ok = element.Value().StringValueOK()\n\t\t\tif !ok {\n\t\t\t\tdesc.LastError = fmt.Errorf(\"expected 'msg' to be a string but it's a BSON %s\", element.Value().Type)\n\t\t\t\treturn desc\n\t\t\t}\n\t\tcase \"ok\":\n\t\t\tokay, ok := element.Value().AsInt64OK()\n\t\t\tif !ok {\n\t\t\t\tdesc.LastError = fmt.Errorf(\"expected 'ok' to be a boolean but it's a BSON %s\", element.Value().Type)\n\t\t\t\treturn desc\n\t\t\t}\n\t\t\tif okay != 1 {\n\t\t\t\tdesc.LastError = errors.New(\"not ok\")\n\t\t\t\treturn desc\n\t\t\t}\n\t\tcase \"passives\":\n\t\t\tvar err error\n\t\t\tdesc.Passives, err = stringSliceFromRawElement(element)\n\t\t\tif err != nil {\n\t\t\t\tdesc.LastError = err\n\t\t\t\treturn desc\n\t\t\t}\n\t\tcase \"passive\":\n\t\t\tdesc.Passive, ok = element.Value().BooleanOK()\n\t\t\tif !ok {\n\t\t\t\tdesc.LastError = fmt.Errorf(\"expected 'passive' to be a boolean but it's a BSON %s\", element.Value().Type)\n\t\t\t\treturn desc\n\t\t\t}\n\t\tcase \"primary\":\n\t\t\tprimary, ok := element.Value().StringValueOK()\n\t\t\tif !ok {\n\t\t\t\tdesc.LastError = fmt.Errorf(\"expected 'primary' to be a string but it's a BSON %s\", element.Value().Type)\n\t\t\t\treturn desc\n\t\t\t}\n\t\t\tdesc.Primary = address.Address(primary)\n\t\tcase \"readOnly\":\n\t\t\tdesc.ReadOnly, ok = element.Value().BooleanOK()\n\t\t\tif !ok {\n\t\t\t\tdesc.LastError = fmt.Errorf(\"expected 'readOnly' to be a boolean but it's a BSON %s\", element.Value().Type)\n\t\t\t\treturn desc\n\t\t\t}\n\t\tcase \"secondary\":\n\t\t\tsecondary, ok = element.Value().BooleanOK()\n\t\t\tif !ok {\n\t\t\t\tdesc.LastError = fmt.Errorf(\"expected 'secondary' to be a boolean but it's a BSON %s\", element.Value().Type)\n\t\t\t\treturn desc\n\t\t\t}\n\t\tcase \"serviceId\":\n\t\t\toid, ok := element.Value().ObjectIDOK()\n\t\t\tif !ok {\n\t\t\t\tdesc.LastError = fmt.Errorf(\"expected 'serviceId' to be an ObjectId but it's a BSON %s\", element.Value().Type)\n\t\t\t}\n\t\t\tdesc.ServiceID = &oid\n\t\tcase \"setName\":\n\t\t\tdesc.SetName, ok = element.Value().StringValueOK()\n\t\t\tif !ok {\n\t\t\t\tdesc.LastError = fmt.Errorf(\"expected 'setName' to be a string but it's a BSON %s\", element.Value().Type)\n\t\t\t\treturn desc\n\t\t\t}\n\t\tcase \"setVersion\":\n\t\t\ti64, ok := element.Value().AsInt64OK()\n\t\t\tif !ok {\n\t\t\t\tdesc.LastError = fmt.Errorf(\"expected 'setVersion' to be an integer but it's a BSON %s\", element.Value().Type)\n\t\t\t\treturn desc\n\t\t\t}\n\t\t\tdesc.SetVersion = uint32(i64)\n\t\tcase \"tags\":\n\t\t\tm, err := decodeStringMap(element, \"tags\")\n\t\t\tif err != nil {\n\t\t\t\tdesc.LastError = err\n\t\t\t\treturn desc\n\t\t\t}\n\t\t\tdesc.Tags = tag.NewTagSetFromMap(m)\n\t\tcase \"topologyVersion\":\n\t\t\tdoc, ok := element.Value().DocumentOK()\n\t\t\tif !ok {\n\t\t\t\tdesc.LastError = fmt.Errorf(\"expected 'topologyVersion' to be a document but it's a BSON %s\", element.Value().Type)\n\t\t\t\treturn desc\n\t\t\t}\n\n\t\t\tdesc.TopologyVersion, err = NewTopologyVersion(doc)\n\t\t\tif err != nil {\n\t\t\t\tdesc.LastError = err\n\t\t\t\treturn desc\n\t\t\t}\n\t\t}\n\t}\n\n\tfor _, host := range desc.Hosts {\n\t\tdesc.Members = append(desc.Members, address.Address(host).Canonicalize())\n\t}\n\n\tfor _, passive := range desc.Passives {\n\t\tdesc.Members = append(desc.Members, address.Address(passive).Canonicalize())\n\t}\n\n\tfor _, arbiter := range desc.Arbiters {\n\t\tdesc.Members = append(desc.Members, address.Address(arbiter).Canonicalize())\n\t}\n\n\tdesc.Kind = description.ServerKindStandalone\n\n\tswitch {\n\tcase isReplicaSet:\n\t\tdesc.Kind = description.ServerKindRSGhost\n\tcase desc.SetName != \"\":\n\t\tswitch {\n\t\tcase isWritablePrimary:\n\t\t\tdesc.Kind = description.ServerKindRSPrimary\n\t\tcase hidden:\n\t\t\tdesc.Kind = description.ServerKindRSMember\n\t\tcase secondary:\n\t\t\tdesc.Kind = description.ServerKindRSSecondary\n\t\tcase arbiterOnly:\n\t\t\tdesc.Kind = description.ServerKindRSArbiter\n\t\tdefault:\n\t\t\tdesc.Kind = description.ServerKindRSMember\n\t\t}\n\tcase msg == \"isdbgrid\":\n\t\tdesc.Kind = description.ServerKindMongos\n\t}\n\n\tdesc.WireVersion = &versionRange\n\n\treturn desc\n}\n\nfunc sliceStringEqual(a []string, b []string) bool {\n\tif len(a) != len(b) {\n\t\treturn false\n\t}\n\n\tfor i, v := range a {\n\t\tif v != b[i] {\n\t\t\treturn false\n\t\t}\n\t}\n\n\treturn true\n}\n"
  },
  {
    "path": "internal/driverutil/hello.go",
    "content": "// Copyright (C) MongoDB, Inc. 2023-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage driverutil\n\nimport (\n\t\"os\"\n\t\"strings\"\n)\n\nconst AwsLambdaPrefix = \"AWS_Lambda_\"\n\nconst (\n\t// FaaS environment variable names\n\n\t// EnvVarAWSExecutionEnv is the AWS Execution environment variable.\n\tEnvVarAWSExecutionEnv = \"AWS_EXECUTION_ENV\"\n\t// EnvVarAWSLambdaRuntimeAPI is the AWS Lambda runtime API variable.\n\tEnvVarAWSLambdaRuntimeAPI = \"AWS_LAMBDA_RUNTIME_API\"\n\t// EnvVarFunctionsWorkerRuntime is the functions worker runtime variable.\n\tEnvVarFunctionsWorkerRuntime = \"FUNCTIONS_WORKER_RUNTIME\"\n\t// EnvVarKService is the K Service variable.\n\tEnvVarKService = \"K_SERVICE\"\n\t// EnvVarFunctionName is the function name variable.\n\tEnvVarFunctionName = \"FUNCTION_NAME\"\n\t// EnvVarVercel is the Vercel variable.\n\tEnvVarVercel = \"VERCEL\"\n\t// EnvVarK8s is the K8s variable.\n\tEnvVarK8s = \"KUBERNETES_SERVICE_HOST\"\n)\n\nconst (\n\t// FaaS environment variable names\n\n\t// EnvVarAWSRegion is the AWS region variable.\n\tEnvVarAWSRegion = \"AWS_REGION\"\n\t// EnvVarAWSLambdaFunctionMemorySize is the AWS Lambda function memory size variable.\n\tEnvVarAWSLambdaFunctionMemorySize = \"AWS_LAMBDA_FUNCTION_MEMORY_SIZE\"\n\t// EnvVarFunctionMemoryMB is the function memory in megabytes variable.\n\tEnvVarFunctionMemoryMB = \"FUNCTION_MEMORY_MB\"\n\t// EnvVarFunctionTimeoutSec is the function timeout in seconds variable.\n\tEnvVarFunctionTimeoutSec = \"FUNCTION_TIMEOUT_SEC\"\n\t// EnvVarFunctionRegion is the function region variable.\n\tEnvVarFunctionRegion = \"FUNCTION_REGION\"\n\t// EnvVarVercelRegion is the Vercel region variable.\n\tEnvVarVercelRegion = \"VERCEL_REGION\"\n)\n\nconst (\n\t// FaaS environment names used by the client\n\n\t// EnvNameAWSLambda is the AWS Lambda environment name.\n\tEnvNameAWSLambda = \"aws.lambda\"\n\t// EnvNameAzureFunc is the Azure Function environment name.\n\tEnvNameAzureFunc = \"azure.func\"\n\t// EnvNameGCPFunc is the Google Cloud Function environment name.\n\tEnvNameGCPFunc = \"gcp.func\"\n\t// EnvNameVercel is the Vercel environment name.\n\tEnvNameVercel = \"vercel\"\n)\n\n// GetFaasEnvName parses the FaaS environment variable name and returns the\n// corresponding name used by the client. If none of the variables or variables\n// for multiple names are populated the client.env value MUST be entirely\n// omitted. When variables for multiple \"client.env.name\" values are present,\n// \"vercel\" takes precedence over \"aws.lambda\"; any other combination MUST cause\n// \"client.env\" to be entirely omitted.\nfunc GetFaasEnvName() string {\n\tenvVars := []string{\n\t\tEnvVarAWSExecutionEnv,\n\t\tEnvVarAWSLambdaRuntimeAPI,\n\t\tEnvVarFunctionsWorkerRuntime,\n\t\tEnvVarKService,\n\t\tEnvVarFunctionName,\n\t\tEnvVarVercel,\n\t}\n\n\t// If none of the variables are populated the client.env value MUST be\n\t// entirely omitted.\n\tnames := make(map[string]struct{})\n\n\tfor _, envVar := range envVars {\n\t\tval := os.Getenv(envVar)\n\t\tif val == \"\" {\n\t\t\tcontinue\n\t\t}\n\n\t\tvar name string\n\n\t\tswitch envVar {\n\t\tcase EnvVarAWSExecutionEnv:\n\t\t\tif !strings.HasPrefix(val, AwsLambdaPrefix) {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tname = EnvNameAWSLambda\n\t\tcase EnvVarAWSLambdaRuntimeAPI:\n\t\t\tname = EnvNameAWSLambda\n\t\tcase EnvVarFunctionsWorkerRuntime:\n\t\t\tname = EnvNameAzureFunc\n\t\tcase EnvVarKService, EnvVarFunctionName:\n\t\t\tname = EnvNameGCPFunc\n\t\tcase EnvVarVercel:\n\t\t\t// \"vercel\" takes precedence over \"aws.lambda\".\n\t\t\tdelete(names, EnvNameAWSLambda)\n\n\t\t\tname = EnvNameVercel\n\t\t}\n\n\t\tnames[name] = struct{}{}\n\t\tif len(names) > 1 {\n\t\t\t// If multiple names are populated the client.env value\n\t\t\t// MUST be entirely omitted.\n\t\t\tnames = nil\n\n\t\t\tbreak\n\t\t}\n\t}\n\n\tfor name := range names {\n\t\treturn name\n\t}\n\n\treturn \"\"\n}\n"
  },
  {
    "path": "internal/driverutil/operation.go",
    "content": "// Copyright (C) MongoDB, Inc. 2023-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage driverutil\n\nimport (\n\t\"context\"\n\t\"math\"\n\t\"time\"\n)\n\n// Operation Names should be sourced from the command reference documentation:\n// https://www.mongodb.com/docs/manual/reference/command/\nconst (\n\tAbortTransactionOp  = \"abortTransaction\"  // AbortTransactionOp is the name for aborting a transaction\n\tAggregateOp         = \"aggregate\"         // AggregateOp is the name for aggregating\n\tCommitTransactionOp = \"commitTransaction\" // CommitTransactionOp is the name for committing a transaction\n\tCountOp             = \"count\"             // CountOp is the name for counting\n\tCreateOp            = \"create\"            // CreateOp is the name for creating\n\tCreateIndexesOp     = \"createIndexes\"     // CreateIndexesOp is the name for creating indexes\n\tDeleteOp            = \"delete\"            // DeleteOp is the name for deleting\n\tDistinctOp          = \"distinct\"          // DistinctOp is the name for distinct\n\tDropOp              = \"drop\"              // DropOp is the name for dropping\n\tDropDatabaseOp      = \"dropDatabase\"      // DropDatabaseOp is the name for dropping a database\n\tDropIndexesOp       = \"dropIndexes\"       // DropIndexesOp is the name for dropping indexes\n\tEndSessionsOp       = \"endSessions\"       // EndSessionsOp is the name for ending sessions\n\tFindAndModifyOp     = \"findAndModify\"     // FindAndModifyOp is the name for finding and modifying\n\tFindOp              = \"find\"              // FindOp is the name for finding\n\tInsertOp            = \"insert\"            // InsertOp is the name for inserting\n\tListCollectionsOp   = \"listCollections\"   // ListCollectionsOp is the name for listing collections\n\tListIndexesOp       = \"listIndexes\"       // ListIndexesOp is the name for listing indexes\n\tListDatabasesOp     = \"listDatabases\"     // ListDatabasesOp is the name for listing databases\n\tUpdateOp            = \"update\"            // UpdateOp is the name for updating\n\tBulkWriteOp         = \"bulkWrite\"         // BulkWriteOp is the name for client-level bulk write\n)\n\n// CalculateMaxTimeMS calculates the maxTimeMS value to send to the server\n// based on the context deadline and the minimum round trip time. If the\n// calculated maxTimeMS is likely to cause a socket timeout, then this function\n// will return 0 and false.\nfunc CalculateMaxTimeMS(ctx context.Context, rttMin time.Duration) (int64, bool) {\n\tdeadline, ok := ctx.Deadline()\n\tif !ok {\n\t\treturn 0, true\n\t}\n\n\tremainingTimeout := time.Until(deadline)\n\n\t// Always round up to the next millisecond value so we never truncate the calculated\n\t// maxTimeMS value (e.g. 400 microseconds evaluates to 1ms, not 0ms).\n\tmaxTimeMS := int64((remainingTimeout - rttMin + time.Millisecond - 1) / time.Millisecond)\n\tif maxTimeMS <= 0 {\n\t\treturn 0, false\n\t}\n\n\t// The server will return a \"BadValue\" error if maxTimeMS is greater\n\t// than the maximum positive int32 value (about 24.9 days). If the\n\t// user specified a timeout value greater than that,  omit maxTimeMS\n\t// and let the client-side timeout handle cancelling the op if the\n\t// timeout is ever reached.\n\tif maxTimeMS > math.MaxInt32 {\n\t\treturn 0, true\n\t}\n\n\treturn maxTimeMS, true\n}\n"
  },
  {
    "path": "internal/driverutil/operation_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2025-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage driverutil\n\nimport (\n\t\"context\"\n\t\"math\"\n\t\"testing\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n)\n\nfunc TestCalculateMaxTimeMS(t *testing.T) {\n\ttests := []struct {\n\t\tname         string\n\t\tctx          context.Context\n\t\trttMin       time.Duration\n\t\twantZero     bool\n\t\twantOk       bool\n\t\twantPositive bool\n\t\twantExact    int64\n\t}{\n\t\t{\n\t\t\tname:         \"no deadline\",\n\t\t\tctx:          context.Background(),\n\t\t\trttMin:       10 * time.Millisecond,\n\t\t\twantZero:     true,\n\t\t\twantOk:       true,\n\t\t\twantPositive: false,\n\t\t},\n\t\t{\n\t\t\tname: \"deadline expired\",\n\t\t\tctx: func() context.Context {\n\t\t\t\tctx, _ := context.WithDeadline(context.Background(), time.Now().Add(-1*time.Second)) //nolint:govet\n\t\t\t\treturn ctx\n\t\t\t}(),\n\t\t\twantZero:     true,\n\t\t\twantOk:       false,\n\t\t\twantPositive: false,\n\t\t},\n\t\t{\n\t\t\tname: \"remaining timeout < rttMin\",\n\t\t\tctx: func() context.Context {\n\t\t\t\tctx, _ := context.WithDeadline(context.Background(), time.Now().Add(1*time.Millisecond)) //nolint:govet\n\t\t\t\treturn ctx\n\t\t\t}(),\n\t\t\trttMin:       10 * time.Millisecond,\n\t\t\twantZero:     true,\n\t\t\twantOk:       false,\n\t\t\twantPositive: false,\n\t\t},\n\t\t{\n\t\t\tname: \"normal positive result\",\n\t\t\tctx: func() context.Context {\n\t\t\t\tctx, _ := context.WithDeadline(context.Background(), time.Now().Add(100*time.Millisecond)) //nolint:govet\n\t\t\t\treturn ctx\n\t\t\t}(),\n\t\t\twantZero:     false,\n\t\t\twantOk:       true,\n\t\t\twantPositive: true,\n\t\t},\n\t\t{\n\t\t\tname: \"beyond maxInt32\",\n\t\t\tctx: func() context.Context {\n\t\t\t\tdur := time.Now().Add(time.Duration(math.MaxInt32+1000) * time.Millisecond)\n\t\t\t\tctx, _ := context.WithDeadline(context.Background(), dur) //nolint:govet\n\t\t\t\treturn ctx\n\t\t\t}(),\n\t\t\twantZero:     true,\n\t\t\twantOk:       true,\n\t\t\twantPositive: false,\n\t\t},\n\t\t{\n\t\t\tname: \"round up to 1ms\",\n\t\t\tctx: func() context.Context {\n\t\t\t\tctx, _ := context.WithDeadline(context.Background(), time.Now().Add(999*time.Microsecond)) //nolint:govet\n\t\t\t\treturn ctx\n\t\t\t}(),\n\t\t\twantOk:    true,\n\t\t\twantExact: 1,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tgot, got1 := CalculateMaxTimeMS(tt.ctx, tt.rttMin)\n\n\t\t\tassert.Equal(t, tt.wantOk, got1)\n\n\t\t\tif tt.wantExact > 0 && got != tt.wantExact {\n\t\t\t\tt.Errorf(\"CalculateMaxTimeMS() got = %v, want %v\", got, tt.wantExact)\n\t\t\t}\n\n\t\t\tif tt.wantZero && got != 0 {\n\t\t\t\tt.Errorf(\"CalculateMaxTimeMS() got = %v, want 0\", got)\n\t\t\t}\n\n\t\t\tif !tt.wantZero && got == 0 {\n\t\t\t\tt.Errorf(\"CalculateMaxTimeMS() got = %v, want > 0\", got)\n\t\t\t}\n\n\t\t\tif !tt.wantZero && tt.wantPositive && got <= 0 {\n\t\t\t\tt.Errorf(\"CalculateMaxTimeMS() got = %v, want > 0\", got)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "internal/errutil/join.go",
    "content": "// Copyright (C) MongoDB, Inc. 2023-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage errutil\n\nimport \"errors\"\n\n// join is a Go 1.13-1.19 compatible version of [errors.Join]. It is only called\n// by Join in join_go1.19.go. It is included here in a file without build\n// constraints only for testing purposes.\n//\n// It is heavily based on Join from\n// https://cs.opensource.google/go/go/+/refs/tags/go1.21.0:src/errors/join.go\nfunc join(errs ...error) error {\n\tn := 0\n\tfor _, err := range errs {\n\t\tif err != nil {\n\t\t\tn++\n\t\t}\n\t}\n\tif n == 0 {\n\t\treturn nil\n\t}\n\te := &joinError{\n\t\terrs: make([]error, 0, n),\n\t}\n\tfor _, err := range errs {\n\t\tif err != nil {\n\t\t\te.errs = append(e.errs, err)\n\t\t}\n\t}\n\treturn e\n}\n\n// joinError is a Go 1.13-1.19 compatible joinable error type. Its error\n// message is identical to [errors.Join], but it implements \"Unwrap() error\"\n// instead of \"Unwrap() []error\".\n//\n// It is heavily based on the joinError from\n// https://cs.opensource.google/go/go/+/refs/tags/go1.21.0:src/errors/join.go\ntype joinError struct {\n\terrs []error\n}\n\nfunc (e *joinError) Error() string {\n\tvar b []byte\n\tfor i, err := range e.errs {\n\t\tif i > 0 {\n\t\t\tb = append(b, '\\n')\n\t\t}\n\t\tb = append(b, err.Error()...)\n\t}\n\treturn string(b)\n}\n\n// Unwrap returns another joinError with the same errors as the current\n// joinError except the first error in the slice. Continuing to call Unwrap\n// on each returned error will increment through every error in the slice. The\n// resulting behavior when using [errors.Is] and [errors.As] is similar to an\n// error created using [errors.Join] in Go 1.20+.\nfunc (e *joinError) Unwrap() error {\n\tif len(e.errs) == 1 {\n\t\treturn e.errs[0]\n\t}\n\treturn &joinError{errs: e.errs[1:]}\n}\n\n// Is calls [errors.Is] with the first error in the slice.\nfunc (e *joinError) Is(target error) bool {\n\tif len(e.errs) == 0 {\n\t\treturn false\n\t}\n\treturn errors.Is(e.errs[0], target)\n}\n\n// As calls [errors.As] with the first error in the slice.\nfunc (e *joinError) As(target any) bool {\n\tif len(e.errs) == 0 {\n\t\treturn false\n\t}\n\treturn errors.As(e.errs[0], target)\n}\n"
  },
  {
    "path": "internal/errutil/join_go1.19.go",
    "content": "// Copyright (C) MongoDB, Inc. 2023-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\n//go:build !go1.20\n\npackage errutil\n\n// Join returns an error that wraps the given errors. Any nil error values are\n// discarded. Join returns nil if every value in errs is nil. The error formats\n// as the concatenation of the strings obtained by calling the Error method of\n// each element of errs, with a newline between each string.\n//\n// A non-nil error returned by Join implements the \"Unwrap() error\" method.\nfunc Join(errs ...error) error {\n\treturn join(errs...)\n}\n"
  },
  {
    "path": "internal/errutil/join_go1.20.go",
    "content": "// Copyright (C) MongoDB, Inc. 2023-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\n//go:build go1.20\n\npackage errutil\n\nimport \"errors\"\n\n// Join calls [errors.Join].\nfunc Join(errs ...error) error {\n\treturn errors.Join(errs...)\n}\n"
  },
  {
    "path": "internal/errutil/join_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2023-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage errutil\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"testing\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n)\n\n// TestJoin_Nil asserts that join returns a nil error for the same inputs that\n// [errors.Join] returns a nil error.\nfunc TestJoin_Nil(t *testing.T) {\n\tt.Parallel()\n\n\tassert.Equal(t, errors.Join(), join(), \"errors.Join() != join()\")\n\tassert.Equal(t, errors.Join(nil), join(nil), \"errors.Join(nil) != join(nil)\")\n\tassert.Equal(t, errors.Join(nil, nil), join(nil, nil), \"errors.Join(nil, nil) != join(nil, nil)\")\n}\n\n// TestJoin_Error asserts that join returns an error with the same error message\n// as the error returned by [errors.Join].\nfunc TestJoin_Error(t *testing.T) {\n\tt.Parallel()\n\n\terr1 := errors.New(\"err1\")\n\terr2 := errors.New(\"err2\")\n\n\ttests := []struct {\n\t\tdesc string\n\t\terrs []error\n\t}{{\n\t\tdesc: \"single error\",\n\t\terrs: []error{err1},\n\t}, {\n\t\tdesc: \"two errors\",\n\t\terrs: []error{err1, err2},\n\t}, {\n\t\tdesc: \"two errors and a nil value\",\n\t\terrs: []error{err1, nil, err2},\n\t}}\n\n\tfor _, test := range tests {\n\t\ttest := test // Capture range variable.\n\n\t\tt.Run(test.desc, func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\twant := errors.Join(test.errs...).Error()\n\t\t\tgot := join(test.errs...).Error()\n\t\t\tassert.Equal(t,\n\t\t\t\twant,\n\t\t\t\tgot,\n\t\t\t\t\"errors.Join().Error() != join().Error() for input %v\",\n\t\t\t\ttest.errs)\n\t\t})\n\t}\n}\n\n// TestJoin_ErrorsIs asserts that join returns an error that behaves identically\n// to the error returned by [errors.Join] when passed to [errors.Is].\nfunc TestJoin_ErrorsIs(t *testing.T) {\n\tt.Parallel()\n\n\terr1 := errors.New(\"err1\")\n\terr2 := errors.New(\"err2\")\n\n\ttests := []struct {\n\t\tdesc   string\n\t\terrs   []error\n\t\ttarget error\n\t}{{\n\t\tdesc:   \"one error with a matching target\",\n\t\terrs:   []error{err1},\n\t\ttarget: err1,\n\t}, {\n\t\tdesc:   \"one error with a non-matching target\",\n\t\terrs:   []error{err1},\n\t\ttarget: err2,\n\t}, {\n\t\tdesc:   \"nil error\",\n\t\terrs:   []error{nil},\n\t\ttarget: err1,\n\t}, {\n\t\tdesc:   \"no errors\",\n\t\terrs:   []error{},\n\t\ttarget: err1,\n\t}, {\n\t\tdesc:   \"two different errors with a matching target\",\n\t\terrs:   []error{err1, err2},\n\t\ttarget: err2,\n\t}, {\n\t\tdesc:   \"two identical errors with a matching target\",\n\t\terrs:   []error{err1, err1},\n\t\ttarget: err1,\n\t}, {\n\t\tdesc:   \"wrapped error with a matching target\",\n\t\terrs:   []error{fmt.Errorf(\"error: %w\", err1)},\n\t\ttarget: err1,\n\t}, {\n\t\tdesc:   \"nested joined error with a matching target\",\n\t\terrs:   []error{err1, join(err2, errors.New(\"nope\"))},\n\t\ttarget: err2,\n\t}, {\n\t\tdesc:   \"nested joined error with no matching targets\",\n\t\terrs:   []error{err1, join(errors.New(\"nope\"), errors.New(\"nope 2\"))},\n\t\ttarget: err2,\n\t}, {\n\t\tdesc:   \"nested joined error with a wrapped matching target\",\n\t\terrs:   []error{join(fmt.Errorf(\"error: %w\", err1), errors.New(\"nope\")), err2},\n\t\ttarget: err1,\n\t}, {\n\t\tdesc:   \"context.DeadlineExceeded\",\n\t\terrs:   []error{err1, nil, context.DeadlineExceeded, err2},\n\t\ttarget: context.DeadlineExceeded,\n\t}, {\n\t\tdesc:   \"wrapped context.DeadlineExceeded\",\n\t\terrs:   []error{err1, nil, fmt.Errorf(\"error: %w\", context.DeadlineExceeded), err2},\n\t\ttarget: context.DeadlineExceeded,\n\t}}\n\n\tfor _, test := range tests {\n\t\ttest := test // Capture range variable.\n\n\t\tt.Run(test.desc, func(t *testing.T) {\n\t\t\t// Assert that top-level errors returned by errors.Join and join\n\t\t\t// behave the same with errors.Is.\n\t\t\twant := errors.Join(test.errs...)\n\t\t\tgot := join(test.errs...)\n\t\t\tassert.Equal(t,\n\t\t\t\terrors.Is(want, test.target),\n\t\t\t\terrors.Is(got, test.target),\n\t\t\t\t\"errors.Join() and join() behave differently with errors.Is\")\n\n\t\t\t// Assert that wrapped errors returned by errors.Join and join\n\t\t\t// behave the same with errors.Is.\n\t\t\twant = fmt.Errorf(\"error: %w\", errors.Join(test.errs...))\n\t\t\tgot = fmt.Errorf(\"error: %w\", join(test.errs...))\n\t\t\tassert.Equal(t,\n\t\t\t\terrors.Is(want, test.target),\n\t\t\t\terrors.Is(got, test.target),\n\t\t\t\t\"errors.Join() and join(), when wrapped, behave differently with errors.Is\")\n\t\t})\n\t}\n}\n\ntype errType1 struct{}\n\nfunc (errType1) Error() string { return \"\" }\n\ntype errType2 struct{}\n\nfunc (errType2) Error() string { return \"\" }\n\n// TestJoin_ErrorsIs asserts that join returns an error that behaves identically\n// to the error returned by [errors.Join] when passed to [errors.As].\nfunc TestJoin_ErrorsAs(t *testing.T) {\n\tt.Parallel()\n\n\terr1 := errType1{}\n\terr2 := errType2{}\n\n\ttests := []struct {\n\t\tdesc   string\n\t\terrs   []error\n\t\ttarget any\n\t}{{\n\t\tdesc:   \"one error with a matching target\",\n\t\terrs:   []error{err1},\n\t\ttarget: &errType1{},\n\t}, {\n\t\tdesc:   \"one error with a non-matching target\",\n\t\terrs:   []error{err1},\n\t\ttarget: &errType2{},\n\t}, {\n\t\tdesc:   \"nil error\",\n\t\terrs:   []error{nil},\n\t\ttarget: &errType1{},\n\t}, {\n\t\tdesc:   \"no errors\",\n\t\terrs:   []error{},\n\t\ttarget: &errType1{},\n\t}, {\n\t\tdesc:   \"two different errors with a matching target\",\n\t\terrs:   []error{err1, err2},\n\t\ttarget: &errType2{},\n\t}, {\n\t\tdesc:   \"two identical errors with a matching target\",\n\t\terrs:   []error{err1, err1},\n\t\ttarget: &errType1{},\n\t}, {\n\t\tdesc:   \"wrapped error with a matching target\",\n\t\terrs:   []error{fmt.Errorf(\"error: %w\", err1)},\n\t\ttarget: &errType1{},\n\t}, {\n\t\tdesc:   \"nested joined error with a matching target\",\n\t\terrs:   []error{err1, join(err2, errors.New(\"nope\"))},\n\t\ttarget: &errType2{},\n\t}, {\n\t\tdesc:   \"nested joined error with no matching targets\",\n\t\terrs:   []error{err1, join(errors.New(\"nope\"), errors.New(\"nope 2\"))},\n\t\ttarget: &errType2{},\n\t}, {\n\t\tdesc:   \"nested joined error with a wrapped matching target\",\n\t\terrs:   []error{join(fmt.Errorf(\"error: %w\", err1), errors.New(\"nope\")), err2},\n\t\ttarget: &errType1{},\n\t}, {\n\t\tdesc:   \"context.DeadlineExceeded\",\n\t\terrs:   []error{err1, nil, context.DeadlineExceeded, err2},\n\t\ttarget: &errType2{},\n\t}}\n\n\tfor _, test := range tests {\n\t\ttest := test // Capture range variable.\n\n\t\tt.Run(test.desc, func(t *testing.T) {\n\t\t\t// Assert that top-level errors returned by errors.Join and join\n\t\t\t// behave the same with errors.As.\n\t\t\twant := errors.Join(test.errs...)\n\t\t\tgot := join(test.errs...)\n\t\t\tassert.Equal(t,\n\t\t\t\terrors.As(want, test.target),\n\t\t\t\terrors.As(got, test.target),\n\t\t\t\t\"errors.Join() and join() behave differently with errors.As\")\n\n\t\t\t// Assert that wrapped errors returned by errors.Join and join\n\t\t\t// behave the same with errors.As.\n\t\t\twant = fmt.Errorf(\"error: %w\", errors.Join(test.errs...))\n\t\t\tgot = fmt.Errorf(\"error: %w\", join(test.errs...))\n\t\t\tassert.Equal(t,\n\t\t\t\terrors.As(want, test.target),\n\t\t\t\terrors.As(got, test.target),\n\t\t\t\t\"errors.Join() and join(), when wrapped, behave differently with errors.As\")\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "internal/eventtest/eventtest.go",
    "content": "// Copyright (C) MongoDB, Inc. 2022-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\n// Package eventtest provides test types that are used to monitor client state and actions via the\n// various monitor types supported by the driver.\npackage eventtest\n\nimport (\n\t\"sync\"\n\n\t\"go.mongodb.org/mongo-driver/v2/event\"\n)\n\n// TestPoolMonitor exposes an *event.TestPoolMonitor and collects all events logged to that\n// *event.TestPoolMonitor. It is safe to use from multiple concurrent goroutines.\ntype TestPoolMonitor struct {\n\t*event.PoolMonitor\n\n\tevents []*event.PoolEvent\n\tmu     sync.RWMutex\n}\n\nfunc NewTestPoolMonitor() *TestPoolMonitor {\n\ttpm := &TestPoolMonitor{\n\t\tevents: make([]*event.PoolEvent, 0),\n\t}\n\ttpm.PoolMonitor = &event.PoolMonitor{\n\t\tEvent: func(evt *event.PoolEvent) {\n\t\t\ttpm.mu.Lock()\n\t\t\tdefer tpm.mu.Unlock()\n\t\t\ttpm.events = append(tpm.events, evt)\n\t\t},\n\t}\n\treturn tpm\n}\n\n// Events returns a copy of the events collected by the testPoolMonitor. Filters can optionally be\n// applied to the returned events set and are applied using AND logic (i.e. all filters must return\n// true to include the event in the result).\nfunc (tpm *TestPoolMonitor) Events(filters ...func(*event.PoolEvent) bool) []*event.PoolEvent {\n\ttpm.mu.RLock()\n\tdefer tpm.mu.RUnlock()\n\n\tfiltered := make([]*event.PoolEvent, 0, len(tpm.events))\n\tfor _, evt := range tpm.events {\n\t\tkeep := true\n\t\tfor _, filter := range filters {\n\t\t\tif !filter(evt) {\n\t\t\t\tkeep = false\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tif keep {\n\t\t\tfiltered = append(filtered, evt)\n\t\t}\n\t}\n\n\treturn filtered\n}\n\n// ClearEvents will reset the events collected by the testPoolMonitor.\nfunc (tpm *TestPoolMonitor) ClearEvents() {\n\ttpm.mu.Lock()\n\tdefer tpm.mu.Unlock()\n\ttpm.events = tpm.events[:0]\n}\n\n// IsPoolCleared returns true if there are any events of type \"event.PoolCleared\" in the events\n// recorded by the testPoolMonitor.\nfunc (tpm *TestPoolMonitor) IsPoolCleared() bool {\n\tpoolClearedEvents := tpm.Events(func(evt *event.PoolEvent) bool {\n\t\treturn evt.Type == event.ConnectionPoolCleared\n\t})\n\treturn len(poolClearedEvents) > 0\n}\n\n// Interruptions returns the number of interruptions in the events recorded by the testPoolMonitor.\nfunc (tpm *TestPoolMonitor) Interruptions() int {\n\tinterruptions := tpm.Events(func(evt *event.PoolEvent) bool {\n\t\treturn evt.Interruption\n\t})\n\treturn len(interruptions)\n}\n"
  },
  {
    "path": "internal/failpoint/failpoint.go",
    "content": "// Copyright (C) MongoDB, Inc. 2024-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage failpoint\n\nimport (\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n)\n\nconst (\n\t// ModeAlwaysOn is the fail point mode that enables the fail point for an\n\t// indefinite number of matching commands.\n\tModeAlwaysOn = \"alwaysOn\"\n\n\t// ModeOff is the fail point mode that disables the fail point.\n\tModeOff = \"off\"\n)\n\n// FailPoint is used to configure a server fail point. It is intended to be\n// passed as the command argument to RunCommand.\n//\n// For more information about fail points, see\n// https://github.com/mongodb/specifications/tree/HEAD/source/transactions/tests#server-fail-point\ntype FailPoint struct {\n\tConfigureFailPoint string `bson:\"configureFailPoint\"`\n\t// Mode should be a string, FailPointMode, or map[string]any\n\tMode any  `bson:\"mode\"`\n\tData Data `bson:\"data\"`\n}\n\n// Mode configures when a fail point will be enabled. It is used to set the\n// FailPoint.Mode field.\ntype Mode struct {\n\tTimes int32 `bson:\"times\"`\n\tSkip  int32 `bson:\"skip\"`\n}\n\n// Data configures how a fail point will behave. It is used to set the\n// FailPoint.Data field.\ntype Data struct {\n\tFailCommands                  []string           `bson:\"failCommands,omitempty\"`\n\tCloseConnection               bool               `bson:\"closeConnection,omitempty\"`\n\tErrorCode                     int32              `bson:\"errorCode,omitempty\"`\n\tFailBeforeCommitExceptionCode int32              `bson:\"failBeforeCommitExceptionCode,omitempty\"`\n\tErrorLabels                   *[]string          `bson:\"errorLabels,omitempty\"`\n\tWriteConcernError             *WriteConcernError `bson:\"writeConcernError,omitempty\"`\n\tBlockConnection               bool               `bson:\"blockConnection,omitempty\"`\n\tBlockTimeMS                   int32              `bson:\"blockTimeMS,omitempty\"`\n\tAppName                       string             `bson:\"appName,omitempty\"`\n}\n\n// WriteConcernError is the write concern error to return when the fail point is\n// triggered. It is used to set the FailPoint.Data.WriteConcernError field.\ntype WriteConcernError struct {\n\tCode        int32     `bson:\"code\"`\n\tName        string    `bson:\"codeName\"`\n\tErrmsg      string    `bson:\"errmsg\"`\n\tErrorLabels *[]string `bson:\"errorLabels,omitempty\"`\n\tErrInfo     bson.Raw  `bson:\"errInfo,omitempty\"`\n}\n"
  },
  {
    "path": "internal/handshake/handshake.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage handshake\n\n// LegacyHello is the legacy version of the hello command.\nvar LegacyHello = \"isMaster\"\n\n// LegacyHelloLowercase is the lowercase, legacy version of the hello command.\nvar LegacyHelloLowercase = \"ismaster\"\n"
  },
  {
    "path": "internal/httputil/httputil.go",
    "content": "// Copyright (C) MongoDB, Inc. 2022-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage httputil\n\nimport (\n\t\"net/http\"\n)\n\nvar DefaultHTTPClient = &http.Client{}\n\n// NewHTTPClient will return the globally-defined DefaultHTTPClient, updating\n// the transport if it differs from the http package DefaultTransport.\nfunc NewHTTPClient() *http.Client {\n\tclient := DefaultHTTPClient\n\tif _, ok := http.DefaultTransport.(*http.Transport); !ok {\n\t\tclient.Transport = http.DefaultTransport\n\t}\n\n\treturn client\n}\n\n// CloseIdleHTTPConnections closes any connections which were previously\n// connected from previous requests but are now sitting idle in a \"keep-alive\"\n// state. It does not interrupt any connections currently in use.\n//\n// Borrowed from the Go standard library.\nfunc CloseIdleHTTPConnections(client *http.Client) {\n\ttype closeIdler interface {\n\t\tCloseIdleConnections()\n\t}\n\tif tr, ok := client.Transport.(closeIdler); ok {\n\t\ttr.CloseIdleConnections()\n\t}\n}\n"
  },
  {
    "path": "internal/httputil/httputil_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2022-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage httputil\n\nimport (\n\t\"net/http\"\n\t\"testing\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n)\n\ntype nonDefaultTransport struct{}\n\nfunc (*nonDefaultTransport) RoundTrip(*http.Request) (*http.Response, error) { return nil, nil }\n\nfunc TestDefaultHTTPClientTransport(t *testing.T) {\n\tt.Run(\"default\", func(t *testing.T) {\n\t\tclient := NewHTTPClient()\n\n\t\tval := assert.ObjectsAreEqual(http.DefaultClient, client)\n\n\t\tassert.True(t, val)\n\t\tassert.Equal(t, DefaultHTTPClient, client)\n\t})\n\n\tt.Run(\"non-default global transport\", func(t *testing.T) {\n\t\thttp.DefaultTransport = &nonDefaultTransport{}\n\n\t\tclient := NewHTTPClient()\n\n\t\tval := assert.ObjectsAreEqual(&nonDefaultTransport{}, client.Transport)\n\n\t\tassert.True(t, val)\n\t\tassert.Equal(t, DefaultHTTPClient, client)\n\t\tassert.NotEqual(t, http.DefaultClient, client) // Sanity Check\n\t})\n}\n"
  },
  {
    "path": "internal/integration/causal_consistency_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage integration\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/integration/mtest\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/readconcern\"\n)\n\n// set of operations that support read concerns taken from read/write concern spec.\n// the spec also lists \"count\" but that has been deprecated and removed from the driver.\nvar readConcernOperations = map[string]struct{}{\n\t\"Aggregate\": {},\n\t\"Distinct\":  {},\n\t\"Find\":      {},\n}\n\nfunc TestCausalConsistency_Supported(t *testing.T) {\n\tmt := mtest.New(t, mtest.NewOptions().MinServerVersion(\"3.6\").Topologies(mtest.ReplicaSet, mtest.Sharded).CreateClient(false))\n\n\tmt.Run(\"operation time nil\", func(mt *mtest.T) {\n\t\t// when a ClientSession is first created, the operation time is nil\n\n\t\tsess, err := mt.Client.StartSession()\n\t\tassert.Nil(mt, err, \"StartSession error: %v\", err)\n\t\tdefer sess.EndSession(context.Background())\n\t\tassert.Nil(mt, sess.OperationTime(), \"expected nil operation time, got %v\", sess.OperationTime())\n\t})\n\tmt.Run(\"no cluster time on first command\", func(mt *mtest.T) {\n\t\t// first read in a causally consistent session must not send afterClusterTime to the server\n\n\t\tccOpts := options.Session().SetCausalConsistency(true)\n\t\t_ = mt.Client.UseSessionWithOptions(context.Background(), ccOpts, func(ctx context.Context) error {\n\t\t\t_, _ = mt.Coll.Find(ctx, bson.D{})\n\t\t\treturn nil\n\t\t})\n\n\t\tevt := mt.GetStartedEvent()\n\t\tassert.Equal(mt, \"find\", evt.CommandName, \"expected 'find' event, got '%v'\", evt.CommandName)\n\t\tcheckOperationTime(mt, evt.Command, false)\n\t})\n\tmt.Run(\"operation time updated\", func(mt *mtest.T) {\n\t\t// first read or write on a ClientSession should update the operationTime of the session, even if there is an error\n\n\t\tsess, err := mt.Client.StartSession()\n\t\tassert.Nil(mt, err, \"StartSession error: %v\", err)\n\t\tdefer sess.EndSession(context.Background())\n\n\t\t_ = mongo.WithSession(context.Background(), sess, func(ctx context.Context) error {\n\t\t\t_, _ = mt.Coll.Find(ctx, bson.D{})\n\t\t\treturn nil\n\t\t})\n\n\t\tevt := mt.GetSucceededEvent()\n\t\tassert.Equal(mt, \"find\", evt.CommandName, \"expected 'find' event, got '%v'\", evt.CommandName)\n\t\tserverT, serverI := evt.Reply.Lookup(\"operationTime\").Timestamp()\n\t\tserverTS := &bson.Timestamp{serverT, serverI}\n\t\tsessionTS := sess.OperationTime()\n\t\tassert.NotNil(mt, sessionTS, \"expected session operation time, got nil\")\n\t\tassert.True(mt, serverTS.Equal(*sessionTS), \"expected operation time %v, got %v\", serverTS, sessionTS)\n\t})\n\tmt.RunOpts(\"operation time sent\", noClientOpts, func(mt *mtest.T) {\n\t\t// findOne followed by another read operation should include operationTime returned by server for the first\n\t\t// operation as the afterClusterTime field of the second operation\n\n\t\tfor _, sf := range createFunctionsSlice() {\n\t\t\t// skip write operations\n\t\t\tif _, ok := readConcernOperations[sf.fnName]; !ok {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tmt.Run(sf.name, func(mt *mtest.T) {\n\t\t\t\tsess, err := mt.Client.StartSession()\n\t\t\t\tassert.Nil(mt, err, \"StartSession error: %v\", err)\n\t\t\t\tdefer sess.EndSession(context.Background())\n\n\t\t\t\t_ = mongo.WithSession(context.Background(), sess, func(ctx context.Context) error {\n\t\t\t\t\t_ = mt.Coll.FindOne(ctx, bson.D{})\n\t\t\t\t\treturn nil\n\t\t\t\t})\n\t\t\t\tcurrOptime := sess.OperationTime()\n\t\t\t\tassert.NotNil(mt, currOptime, \"expected session operation time, got nil\")\n\n\t\t\t\tmt.ClearEvents()\n\t\t\t\t_ = sf.execute(mt, sess)\n\t\t\t\t_, sentOptime := getReadConcernFields(mt, mt.GetStartedEvent().Command)\n\t\t\t\tassert.NotNil(mt, sentOptime, \"expected operation time on command, got nil\")\n\t\t\t\tassert.True(mt, currOptime.Equal(*sentOptime), \"expected operation time %v, got %v\", currOptime, sentOptime)\n\t\t\t})\n\t\t}\n\t})\n\tmt.RunOpts(\"write then read\", noClientOpts, func(mt *mtest.T) {\n\t\t// any write operation followed by a findOne should include operationTime of the first operation as afterClusterTime\n\t\t// in the second operation\n\n\t\tfor _, sf := range createFunctionsSlice() {\n\t\t\t// skip read operations\n\t\t\tif _, ok := readConcernOperations[sf.fnName]; ok {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tmt.Run(sf.name, func(mt *mtest.T) {\n\t\t\t\tsess, err := mt.Client.StartSession()\n\t\t\t\tassert.Nil(mt, err, \"StartSession error: %v\", err)\n\t\t\t\tdefer sess.EndSession(context.Background())\n\n\t\t\t\t_ = sf.execute(mt, sess)\n\t\t\t\tcurrOptime := sess.OperationTime()\n\t\t\t\tassert.NotNil(mt, currOptime, \"expected session operation time, got nil\")\n\n\t\t\t\tmt.ClearEvents()\n\t\t\t\t_ = mongo.WithSession(context.Background(), sess, func(ctx context.Context) error {\n\t\t\t\t\t_ = mt.Coll.FindOne(ctx, bson.D{})\n\t\t\t\t\treturn nil\n\t\t\t\t})\n\t\t\t\t_, sentOptime := getReadConcernFields(mt, mt.GetStartedEvent().Command)\n\t\t\t\tassert.NotNil(mt, sentOptime, \"expected operation time on command, got nil\")\n\t\t\t\tassert.True(mt, currOptime.Equal(*sentOptime), \"expected operation time %v, got %v\", currOptime, sentOptime)\n\t\t\t})\n\t\t}\n\t})\n\tmt.Run(\"non-consistent read\", func(mt *mtest.T) {\n\t\t// a read operation in a non causally-consistent session should not include afterClusterTime\n\n\t\tsessOpts := options.Session().SetCausalConsistency(false)\n\t\t_ = mt.Client.UseSessionWithOptions(context.Background(), sessOpts, func(ctx context.Context) error {\n\t\t\t_, _ = mt.Coll.Find(ctx, bson.D{})\n\t\t\tmt.ClearEvents()\n\t\t\t_, _ = mt.Coll.Find(ctx, bson.D{})\n\t\t\treturn nil\n\t\t})\n\t\tevt := mt.GetStartedEvent()\n\t\tassert.Equal(mt, \"find\", evt.CommandName, \"expected 'find' command, got '%v'\", evt.CommandName)\n\t\tcheckOperationTime(mt, evt.Command, false)\n\t})\n\tmt.Run(\"default read concern\", func(mt *mtest.T) {\n\t\t// when using the default server read concern, the readConcern parameter in the command sent to the server should\n\t\t// not include a level field\n\n\t\tsess, err := mt.Client.StartSession()\n\t\tassert.Nil(mt, err, \"StartSession error: %v\", err)\n\t\tdefer sess.EndSession(context.Background())\n\n\t\t_ = mongo.WithSession(context.Background(), sess, func(ctx context.Context) error {\n\t\t\t_ = mt.Coll.FindOne(ctx, bson.D{})\n\t\t\treturn nil\n\t\t})\n\t\tcurrOptime := sess.OperationTime()\n\t\tmt.ClearEvents()\n\t\t_ = mongo.WithSession(context.Background(), sess, func(ctx context.Context) error {\n\t\t\t_ = mt.Coll.FindOne(ctx, bson.D{})\n\t\t\treturn nil\n\t\t})\n\n\t\tlevel, sentOptime := getReadConcernFields(mt, mt.GetStartedEvent().Command)\n\t\tassert.Equal(mt, \"\", level, \"expected command to not have read concern level, got %s\", level)\n\t\tassert.NotNil(mt, sentOptime, \"expected operation time on command, got nil\")\n\t\tassert.True(mt, currOptime.Equal(*sentOptime), \"expected operation time %v, got %v\", currOptime, sentOptime)\n\t})\n\tlocalRcOpts := options.Client().SetReadConcern(readconcern.Local())\n\tmt.RunOpts(\"custom read concern\", mtest.NewOptions().ClientOptions(localRcOpts), func(mt *mtest.T) {\n\t\tsess, err := mt.Client.StartSession()\n\t\tassert.Nil(mt, err, \"StartSession error: %v\", err)\n\t\tdefer sess.EndSession(context.Background())\n\n\t\t_ = mongo.WithSession(context.Background(), sess, func(ctx context.Context) error {\n\t\t\t_ = mt.Coll.FindOne(ctx, bson.D{})\n\t\t\treturn nil\n\t\t})\n\t\tcurrOptime := sess.OperationTime()\n\t\tmt.ClearEvents()\n\t\t_ = mongo.WithSession(context.Background(), sess, func(ctx context.Context) error {\n\t\t\t_ = mt.Coll.FindOne(ctx, bson.D{})\n\t\t\treturn nil\n\t\t})\n\n\t\tlevel, sentOptime := getReadConcernFields(mt, mt.GetStartedEvent().Command)\n\t\tassert.Equal(mt, \"local\", level, \"expected read concern level 'local', got %s\", level)\n\t\tassert.NotNil(mt, sentOptime, \"expected operation time on command, got nil\")\n\t\tassert.True(mt, currOptime.Equal(*sentOptime), \"expected operation time %v, got %v\", currOptime, sentOptime)\n\t})\n\tmt.Run(\"clusterTime included\", func(mt *mtest.T) {\n\t\t// $clusterTime should be included in commands if the deployment supports cluster times\n\n\t\t_ = mt.Coll.FindOne(context.Background(), bson.D{})\n\t\tevt := mt.GetStartedEvent()\n\t\tassert.Equal(mt, \"find\", evt.CommandName, \"expected command 'find', got '%v'\", evt.CommandName)\n\t\t_, err := evt.Command.LookupErr(\"$clusterTime\")\n\t\tassert.Nil(mt, err, \"expected $clusterTime in command, got nil\")\n\t})\n}\n\nfunc checkOperationTime(mt *mtest.T, cmd bson.Raw, shouldInclude bool) {\n\tmt.Helper()\n\n\t_, optime := getReadConcernFields(mt, cmd)\n\tif shouldInclude {\n\t\tassert.NotNil(mt, optime, \"expected operation time, got nil\")\n\t\treturn\n\t}\n\tassert.Nil(mt, optime, \"did not expect operation time, got %v\", optime)\n}\n\nfunc getReadConcernFields(mt *mtest.T, cmd bson.Raw) (string, *bson.Timestamp) {\n\tmt.Helper()\n\n\trc, err := cmd.LookupErr(\"readConcern\")\n\tif err != nil {\n\t\treturn \"\", nil\n\t}\n\trcDoc := rc.Document()\n\n\tvar level string\n\tvar clusterTime *bson.Timestamp\n\n\tif levelVal, err := rcDoc.LookupErr(\"level\"); err == nil {\n\t\tlevel = levelVal.StringValue()\n\t}\n\tif ctVal, err := rcDoc.LookupErr(\"afterClusterTime\"); err == nil {\n\t\tt, i := ctVal.Timestamp()\n\t\tclusterTime = &bson.Timestamp{t, i}\n\t}\n\treturn level, clusterTime\n}\n"
  },
  {
    "path": "internal/integration/change_stream_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage integration\n\nimport (\n\t\"context\"\n\t\"strings\"\n\t\"sync\"\n\t\"testing\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/event\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/eventtest\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/failpoint\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/integration/mtest\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/require\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n)\n\ntype (\n\tresumeType int\n\tstreamType int\n)\n\nconst (\n\tminChangeStreamVersion = \"3.6.0\"\n\tminPbrtVersion         = \"4.0.7\"\n\tminStartAfterVersion   = \"4.1.1\"\n\n\tstartAfter resumeType = iota\n\tresumeAfter\n\toperationTime\n\n\tclient streamType = iota\n\tdatabase\n\tcollection\n\n\terrorInterrupted     int32 = 11601\n\terrorHostUnreachable int32 = 6\n\n\tresumableChangeStreamError = \"ResumableChangeStreamError\"\n)\n\nfunc TestChangeStream_Standalone(t *testing.T) {\n\tmtOpts := mtest.NewOptions().MinServerVersion(minChangeStreamVersion).CreateClient(false).Topologies(mtest.Single)\n\tmt := mtest.New(t, mtOpts)\n\n\tmt.Run(\"no custom standalone error\", func(mt *mtest.T) {\n\t\t_, err := mt.Coll.Watch(context.Background(), mongo.Pipeline{})\n\t\t_, ok := err.(mongo.CommandError)\n\t\tassert.True(mt, ok, \"expected error type %T, got %T\", mongo.CommandError{}, err)\n\t})\n}\n\nfunc TestChangeStream_ReplicaSet(t *testing.T) {\n\tmtOpts := mtest.NewOptions().MinServerVersion(minChangeStreamVersion).CreateClient(false).Topologies(mtest.ReplicaSet)\n\tmt := mtest.New(t, mtOpts)\n\n\tmt.Run(\"first stage is $changeStream\", func(mt *mtest.T) {\n\t\t// first stage in the aggregate pipeline must be $changeStream\n\n\t\tcs, err := mt.Coll.Watch(context.Background(), mongo.Pipeline{})\n\t\tassert.Nil(mt, err, \"Watch error: %v\", err)\n\t\tdefer closeStream(cs)\n\t\tstarted := mt.GetStartedEvent()\n\t\tassert.NotNil(mt, started, \"expected started event for aggregate, got nil\")\n\n\t\t// pipeline is array of documents. first value of first element in array is the first stage document\n\t\tfirstStage := started.Command.Lookup(\"pipeline\").Array().Index(0).Document()\n\t\telems, _ := firstStage.Elements()\n\t\tassert.Equal(mt, 1, len(elems), \"expected first stage document to have 1 element, got %v\", len(elems))\n\t\tfirstKey := elems[0].Key()\n\t\twant := \"$changeStream\"\n\t\tassert.Equal(mt, want, firstKey, \"expected first stage to be %v, got %v\", want, firstKey)\n\t})\n\tmt.Run(\"track resume token\", func(mt *mtest.T) {\n\t\t// ChangeStream must continuously track the last seen resumeToken\n\n\t\tcs, err := mt.Coll.Watch(context.Background(), mongo.Pipeline{})\n\t\tassert.Nil(mt, err, \"Watch error: %v\", err)\n\t\tdefer closeStream(cs)\n\n\t\tgenerateEvents(mt, 1)\n\t\tassert.True(mt, cs.Next(context.Background()), \"expected next to return true, got false\")\n\t\tassert.NotNil(mt, cs.ResumeToken(), \"expected resume token, got nil\")\n\t})\n\tmt.RunOpts(\"resume token updated on empty batch\", mtest.NewOptions().MinServerVersion(\"4.0.7\"), func(mt *mtest.T) {\n\t\t// The resume token is updated when an empty batch is returned using the server's post batch resume token.\n\n\t\tcs, err := mt.Coll.Watch(context.Background(), mongo.Pipeline{})\n\t\tassert.Nil(mt, err, \"Watch error: %v\", err)\n\t\tdefer closeStream(cs)\n\n\t\t// cause an event to occur so the resume token is updated\n\t\tgenerateEvents(mt, 1)\n\t\tassert.True(mt, cs.Next(context.Background()), \"expected next to return true, got false\")\n\t\tassert.Equal(mt, 0, cs.RemainingBatchLength())\n\t\tfirstToken := cs.ResumeToken()\n\n\t\t// cause an event on a different collection than the one being watched so the server's PBRT is updated\n\t\tdiffColl := mt.CreateCollection(mtest.Collection{Name: \"diffCollUpdatePbrt\"}, false)\n\t\t_, err = diffColl.InsertOne(context.Background(), bson.D{{\"x\", 1}})\n\t\tassert.Nil(mt, err, \"InsertOne error: %v\", err)\n\n\t\t// verify that the resume token is updated using the PBRT from an empty batch\n\t\tmt.ClearEvents()\n\t\tassert.False(mt, cs.TryNext(context.Background()), \"unexpected event document: %v\", cs.Current)\n\t\tassert.Nil(mt, cs.Err(), \"change stream error getting new batch: %v\", cs.Err())\n\t\tnewToken := cs.ResumeToken()\n\t\tassert.NotEqual(mt, newToken, firstToken, \"resume token was not updated after an empty batch was returned\")\n\n\t\tevt := mt.GetSucceededEvent()\n\t\tassert.Equal(mt, \"getMore\", evt.CommandName, \"expected event for 'getMore', got '%v'\", evt.CommandName)\n\t\tgetMorePbrt := evt.Reply.Lookup(\"cursor\", \"postBatchResumeToken\").Document()\n\t\tassert.Equal(mt, newToken, getMorePbrt, \"expected resume token %v, got %v\", getMorePbrt, newToken)\n\t})\n\tmt.Run(\"missing resume token\", func(mt *mtest.T) {\n\t\t// ChangeStream will throw an exception if the server response is missing the resume token\n\n\t\tprojectDoc := bson.D{\n\t\t\t{\"$project\", bson.D{\n\t\t\t\t{\"_id\", 0},\n\t\t\t}},\n\t\t}\n\t\tcs, err := mt.Coll.Watch(context.Background(), mongo.Pipeline{projectDoc})\n\t\tassert.Nil(mt, err, \"Watch error: %v\", err)\n\t\tdefer closeStream(cs)\n\n\t\tgenerateEvents(mt, 2)\n\t\tassert.False(mt, cs.Next(context.Background()), \"expected Next to return false, got true\")\n\t\tassert.NotNil(mt, cs.Err(), \"expected error, got nil\")\n\t})\n\tmt.RunOpts(\"resume once\", mtest.NewOptions().ClientType(mtest.Mock), func(mt *mtest.T) {\n\t\t// ChangeStream will automatically resume one time on a resumable error\n\n\t\t// aggregateRes: create change stream with ID 1 and a batch of size 1 so the resume token will be recorded\n\t\t// failureGetMoreRes: resumable error\n\t\t// killCursorsRes: success\n\t\t// resumedAggregateRes: create new change stream with ID 2 and a batch of size 1 so the resume token will be\n\t\t// updated\n\t\tns := mt.Coll.Database().Name() + \".\" + mt.Coll.Name()\n\t\taggregateRes := mtest.CreateCursorResponse(1, ns, mtest.FirstBatch, bson.D{\n\t\t\t{\"_id\", bson.D{{\"first\", \"resume token\"}}},\n\t\t})\n\t\tfailureGetMoreRes := mtest.CreateCommandErrorResponse(mtest.CommandError{\n\t\t\tCode:    errorHostUnreachable,\n\t\t\tName:    \"foo\",\n\t\t\tMessage: \"bar\",\n\t\t\tLabels:  []string{resumableChangeStreamError},\n\t\t})\n\t\tkillCursorsRes := mtest.CreateSuccessResponse()\n\t\tnewResumeToken := bson.D{{\"second\", \"resume token\"}}\n\t\tresumedAggregateRes := mtest.CreateCursorResponse(2, ns, mtest.FirstBatch, bson.D{\n\t\t\t{\"_id\", newResumeToken},\n\t\t})\n\t\tmt.AddMockResponses(\n\t\t\taggregateRes,\n\t\t\tfailureGetMoreRes,\n\t\t\tkillCursorsRes,\n\t\t\tresumedAggregateRes,\n\t\t)\n\n\t\tcs, err := mt.Coll.Watch(context.Background(), mongo.Pipeline{})\n\t\tassert.Nil(mt, err, \"Watch error: %v\", err)\n\t\tdefer closeStream(cs)\n\t\t// Consume the first document\n\t\tassert.True(mt, cs.Next(context.Background()), \"expected Next to return true, got false\")\n\n\t\t// Clear existing events and expect a resume attempt to happen.\n\t\tmt.ClearEvents()\n\t\tassert.True(mt, cs.Next(context.Background()), \"expected Next to return true, got false\")\n\n\t\t// Next should cause getMore, killCursors, and aggregate to run\n\t\tassert.NotNil(mt, mt.GetStartedEvent(), \"expected getMore event, got nil\")\n\t\tassert.NotNil(mt, mt.GetStartedEvent(), \"expected killCursors event, got nil\")\n\t\taggEvent := mt.GetStartedEvent()\n\t\tassert.NotNil(mt, aggEvent, \"expected aggregate event, got nil\")\n\t\tassert.Equal(mt, \"aggregate\", aggEvent.CommandName, \"expected command name 'aggregate', got '%v'\", aggEvent.CommandName)\n\n\t\t// Assert that the change stream has updated it's ID and resume token.\n\t\tassert.Equal(mt, cs.ID(), int64(2), \"expected change stream ID to be 2, got %d\", cs.ID())\n\t\tnewResumeTokenRaw, err := bson.Marshal(newResumeToken)\n\t\tassert.Nil(mt, err, \"Marshal error: %v\", err)\n\t\tcomparisonErr := compareDocs(mt, newResumeTokenRaw, cs.ResumeToken())\n\t\tassert.Nil(mt, comparisonErr, \"expected resume token %s, got %s\", newResumeTokenRaw, cs.ResumeToken())\n\t})\n\tmt.RunOpts(\"no resume for aggregate errors\", mtest.NewOptions().ClientType(mtest.Mock), func(mt *mtest.T) {\n\t\t// ChangeStream will not attempt to resume on any error encountered while executing an aggregate command\n\n\t\t// aggregate response: empty batch but valid cursor ID\n\t\t// getMore response: resumable error\n\t\t// killCursors response: success\n\t\t// resumed aggregate response: resumable error\n\t\tns := mt.Coll.Database().Name() + \".\" + mt.Coll.Name()\n\t\taggRes := mtest.CreateCursorResponse(1, ns, mtest.FirstBatch)\n\t\tgetMoreRes := mtest.CreateCommandErrorResponse(mtest.CommandError{\n\t\t\tCode:    errorHostUnreachable,\n\t\t\tName:    \"foo\",\n\t\t\tMessage: \"bar\",\n\t\t\tLabels:  []string{resumableChangeStreamError},\n\t\t})\n\t\tkillCursorsRes := mtest.CreateSuccessResponse()\n\t\tresumedAggRes := mtest.CreateCommandErrorResponse(mtest.CommandError{\n\t\t\tCode:    errorHostUnreachable,\n\t\t\tName:    \"foo\",\n\t\t\tMessage: \"bar\",\n\t\t\tLabels:  []string{resumableChangeStreamError},\n\t\t})\n\t\tmt.AddMockResponses(aggRes, getMoreRes, killCursorsRes, resumedAggRes)\n\n\t\tcs, err := mt.Coll.Watch(context.Background(), mongo.Pipeline{})\n\t\tassert.Nil(mt, err, \"Watch error: %v\", err)\n\t\tdefer closeStream(cs)\n\n\t\tassert.False(mt, cs.Next(context.Background()), \"expected Next to return false, got true\")\n\t})\n\tmt.RunOpts(\"server selection before resume\", mtest.NewOptions().CreateClient(false), func(mt *mtest.T) {\n\t\t// ChangeStream will perform server selection before attempting to resume, using initial readPreference\n\t\tmt.Skip(\"skipping for lack of SDAM monitoring\")\n\t})\n\tmt.Run(\"empty batch cursor not closed\", func(mt *mtest.T) {\n\t\t// Ensure that a cursor returned from an aggregate command with a cursor id and an initial empty batch is not closed\n\n\t\tcs, err := mt.Coll.Watch(context.Background(), mongo.Pipeline{})\n\t\tassert.Nil(mt, err, \"Watch error: %v\", err)\n\t\tdefer closeStream(cs)\n\t\tassert.True(mt, cs.ID() > 0, \"expected non-zero ID, got 0\")\n\t})\n\tmt.RunOpts(\"ignore errors from killCursors\", mtest.NewOptions().ClientType(mtest.Mock), func(mt *mtest.T) {\n\t\t// The killCursors command sent during the \"Resume Process\" must not be allowed to throw an exception.\n\n\t\tns := mt.Coll.Database().Name() + \".\" + mt.Coll.Name()\n\t\taggRes := mtest.CreateCursorResponse(1, ns, mtest.FirstBatch)\n\t\tgetMoreRes := mtest.CreateCommandErrorResponse(mtest.CommandError{\n\t\t\tCode:    errorHostUnreachable,\n\t\t\tName:    \"foo\",\n\t\t\tMessage: \"bar\",\n\t\t\tLabels:  []string{\"ResumableChangeStreamError\"},\n\t\t})\n\t\tkillCursorsRes := mtest.CreateCommandErrorResponse(mtest.CommandError{\n\t\t\tCode:    errorInterrupted,\n\t\t\tName:    \"foo\",\n\t\t\tMessage: \"bar\",\n\t\t})\n\t\tchangeDoc := bson.D{{\"_id\", bson.D{{\"x\", 1}}}}\n\t\tresumedAggRes := mtest.CreateCursorResponse(1, ns, mtest.FirstBatch, changeDoc)\n\t\tmt.AddMockResponses(aggRes, getMoreRes, killCursorsRes, resumedAggRes)\n\n\t\tcs, err := mt.Coll.Watch(context.Background(), mongo.Pipeline{})\n\t\tassert.Nil(mt, err, \"Watch error: %v\", err)\n\t\tdefer closeStream(cs)\n\n\t\tassert.True(mt, cs.Next(context.Background()), \"expected Next to return true, got false\")\n\t\tassert.Nil(mt, cs.Err(), \"change stream error: %v\", cs.Err())\n\t})\n\n\tmt.RunOpts(\"decode does not panic\", noClientOpts, func(mt *mtest.T) {\n\t\ttestCases := []struct {\n\t\t\tname             string\n\t\t\tst               streamType\n\t\t\tminServerVersion string\n\t\t}{\n\t\t\t{\"client\", client, \"4.0\"},\n\t\t\t{\"database\", database, \"4.0\"},\n\t\t\t{\"collection\", collection, \"\"},\n\t\t}\n\t\tfor _, tc := range testCases {\n\t\t\ttcOpts := mtest.NewOptions()\n\t\t\tif tc.minServerVersion != \"\" {\n\t\t\t\ttcOpts.MinServerVersion(tc.minServerVersion)\n\t\t\t}\n\t\t\tmt.RunOpts(tc.name, tcOpts, func(mt *mtest.T) {\n\t\t\t\tvar cs *mongo.ChangeStream\n\t\t\t\tvar err error\n\t\t\t\tswitch tc.st {\n\t\t\t\tcase client:\n\t\t\t\t\tcs, err = mt.Client.Watch(context.Background(), mongo.Pipeline{})\n\t\t\t\tcase database:\n\t\t\t\t\tcs, err = mt.DB.Watch(context.Background(), mongo.Pipeline{})\n\t\t\t\tcase collection:\n\t\t\t\t\tcs, err = mt.Coll.Watch(context.Background(), mongo.Pipeline{})\n\t\t\t\t}\n\t\t\t\tassert.Nil(mt, err, \"Watch error: %v\", err)\n\t\t\t\tdefer closeStream(cs)\n\n\t\t\t\tgenerateEvents(mt, 1)\n\t\t\t\tassert.True(mt, cs.Next(context.Background()), \"expected Next true, got false\")\n\t\t\t\tvar res bson.D\n\t\t\t\terr = cs.Decode(&res)\n\t\t\t\tassert.Nil(mt, err, \"Decode error: %v\", err)\n\t\t\t\tassert.True(mt, len(res) > 0, \"expected non-empty document, got empty\")\n\t\t\t})\n\t\t}\n\t})\n\tmt.Run(\"maxAwaitTimeMS\", func(mt *mtest.T) {\n\t\t// maxAwaitTimeMS option should be sent as maxTimeMS on getMore\n\n\t\topts := options.ChangeStream().SetMaxAwaitTime(100 * time.Millisecond)\n\t\tcs, err := mt.Coll.Watch(context.Background(), mongo.Pipeline{}, opts)\n\t\tassert.Nil(mt, err, \"Watch error: %v\", err)\n\t\tdefer closeStream(cs)\n\n\t\t_, err = mt.Coll.InsertOne(context.Background(), bson.D{{\"x\", 1}})\n\t\tassert.Nil(mt, err, \"InsertOne error: %v\", err)\n\t\tmt.ClearEvents()\n\t\tassert.True(mt, cs.Next(context.Background()), \"expected Next true, got false\")\n\n\t\te := mt.GetStartedEvent()\n\t\tassert.NotNil(mt, e, \"expected getMore event, got nil\")\n\t\t_, err = e.Command.LookupErr(\"maxTimeMS\")\n\t\tassert.Nil(mt, err, \"field maxTimeMS not found in command %v\", e.Command)\n\t})\n\tmt.RunOpts(\"resume token\", noClientOpts, func(mt *mtest.T) {\n\t\t// Prose tests to make assertions on resume tokens for change streams that have not done a getMore yet\n\t\tmt.RunOpts(\"no getMore\", noClientOpts, func(mt *mtest.T) {\n\t\t\tpbrtOpts := mtest.NewOptions().MinServerVersion(minPbrtVersion).CreateClient(false)\n\t\t\tmt.RunOpts(\"with PBRT support\", pbrtOpts, func(mt *mtest.T) {\n\t\t\t\ttestCases := []struct {\n\t\t\t\t\tname             string\n\t\t\t\t\trt               resumeType\n\t\t\t\t\tminServerVersion string\n\t\t\t\t}{\n\t\t\t\t\t{\"startAfter\", startAfter, minStartAfterVersion},\n\t\t\t\t\t{\"resumeAfter\", resumeAfter, minPbrtVersion},\n\t\t\t\t\t{\"neither\", operationTime, minPbrtVersion},\n\t\t\t\t}\n\n\t\t\t\tfor _, tc := range testCases {\n\t\t\t\t\ttcOpts := mtest.NewOptions().MinServerVersion(tc.minServerVersion)\n\t\t\t\t\tmt.RunOpts(tc.name, tcOpts, func(mt *mtest.T) {\n\t\t\t\t\t\t// create temp stream to get a resume token\n\t\t\t\t\t\tmt.ClearEvents()\n\t\t\t\t\t\tcs, err := mt.Coll.Watch(context.Background(), mongo.Pipeline{})\n\t\t\t\t\t\tassert.Nil(mt, err, \"Watch error: %v\", err)\n\n\t\t\t\t\t\t// Initial resume token should equal the PBRT in the aggregate command\n\t\t\t\t\t\tpbrt, opTime := getAggregateResponseInfo(mt)\n\t\t\t\t\t\tcompareResumeTokens(mt, cs, pbrt)\n\n\t\t\t\t\t\tnumEvents := 5\n\t\t\t\t\t\tgenerateEvents(mt, numEvents)\n\n\t\t\t\t\t\t// Iterate over one event to get resume token\n\t\t\t\t\t\tassert.True(mt, cs.Next(context.Background()), \"expected Next to return true, got false\")\n\t\t\t\t\t\tassert.Equal(mt, numEvents-1, cs.RemainingBatchLength())\n\t\t\t\t\t\ttoken := cs.ResumeToken()\n\t\t\t\t\t\tcloseStream(cs)\n\n\t\t\t\t\t\tvar numExpectedEvents int\n\t\t\t\t\t\tvar initialToken bson.Raw\n\t\t\t\t\t\tvar opts *options.ChangeStreamOptionsBuilder\n\t\t\t\t\t\tswitch tc.rt {\n\t\t\t\t\t\tcase startAfter:\n\t\t\t\t\t\t\tnumExpectedEvents = numEvents - 1\n\t\t\t\t\t\t\tinitialToken = token\n\t\t\t\t\t\t\topts = options.ChangeStream().SetStartAfter(token)\n\t\t\t\t\t\tcase resumeAfter:\n\t\t\t\t\t\t\tnumExpectedEvents = numEvents - 1\n\t\t\t\t\t\t\tinitialToken = token\n\t\t\t\t\t\t\topts = options.ChangeStream().SetResumeAfter(token)\n\t\t\t\t\t\tcase operationTime:\n\t\t\t\t\t\t\tnumExpectedEvents = numEvents\n\t\t\t\t\t\t\topts = options.ChangeStream().SetStartAtOperationTime(&opTime)\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// clear slate and create new change stream\n\t\t\t\t\t\tmt.ClearEvents()\n\t\t\t\t\t\tcs, err = mt.Coll.Watch(context.Background(), mongo.Pipeline{}, opts)\n\t\t\t\t\t\tassert.Nil(mt, err, \"Watch error: %v\", err)\n\t\t\t\t\t\tdefer closeStream(cs)\n\n\t\t\t\t\t\taggPbrt, _ := getAggregateResponseInfo(mt)\n\t\t\t\t\t\tcompareResumeTokens(mt, cs, initialToken)\n\n\t\t\t\t\t\tfor i := 0; i < numExpectedEvents; i++ {\n\t\t\t\t\t\t\tassert.True(mt, cs.Next(context.Background()), \"expected Next to return true, got false\")\n\t\t\t\t\t\t\t// while we're not at the last doc in the batch, the resume token should be the _id of the\n\t\t\t\t\t\t\t// document\n\t\t\t\t\t\t\tif i != numExpectedEvents-1 {\n\t\t\t\t\t\t\t\tcompareResumeTokens(mt, cs, cs.Current.Lookup(\"_id\").Document())\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// at end of batch, the resume token should equal the PBRT of the aggregate\n\t\t\t\t\t\tcompareResumeTokens(mt, cs, aggPbrt)\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t})\n\t\t})\n\t})\n\tmt.RunOpts(\"try next\", noClientOpts, func(mt *mtest.T) {\n\t\tmt.Run(\"existing non-empty batch\", func(mt *mtest.T) {\n\t\t\t// If there's already documents in the current batch, TryNext should return true without doing a getMore\n\n\t\t\tcs, err := mt.Coll.Watch(context.Background(), mongo.Pipeline{})\n\t\t\tassert.Nil(mt, err, \"Watch error: %v\", err)\n\t\t\tdefer closeStream(cs)\n\t\t\tgenerateEvents(mt, 5)\n\t\t\t// call Next to make sure a batch is retrieved\n\t\t\tassert.True(mt, cs.Next(context.Background()), \"expected Next to return true, got false\")\n\t\t\ttryNextExistingBatchTest(mt, cs)\n\t\t})\n\t\tmt.Run(\"one getMore sent\", func(mt *mtest.T) {\n\t\t\t// If the current batch is empty, TryNext should send one getMore and return.\n\n\t\t\tcs, err := mt.Coll.Watch(context.Background(), mongo.Pipeline{})\n\t\t\tassert.Nil(mt, err, \"Watch error: %v\", err)\n\t\t\tdefer closeStream(cs)\n\n\t\t\tmt.ClearEvents()\n\t\t\t// first call to TryNext should return false because first batch was empty so batch cursor returns false\n\t\t\t// without doing a getMore\n\t\t\t// next call to TryNext should attempt a getMore\n\t\t\tfor i := 0; i < 2; i++ {\n\t\t\t\tassert.False(mt, cs.TryNext(context.Background()), \"TryNext returned true on iteration %v\", i)\n\t\t\t}\n\t\t\tverifyOneGetmoreSent(mt)\n\t\t})\n\t\tmt.RunOpts(\"getMore error\", mtest.NewOptions().ClientType(mtest.Mock), func(mt *mtest.T) {\n\t\t\t// If the getMore attempt errors with a non-resumable error, TryNext returns false\n\n\t\t\taggRes := mtest.CreateCursorResponse(50, \"foo.bar\", mtest.FirstBatch)\n\t\t\tmt.AddMockResponses(aggRes)\n\t\t\tcs, err := mt.Coll.Watch(context.Background(), mongo.Pipeline{})\n\t\t\tassert.Nil(mt, err, \"Watch error: %v\", err)\n\t\t\tdefer closeStream(cs)\n\t\t\ttryNextGetmoreError(mt, cs)\n\t\t})\n\t})\n\n\tcustomDeploymentOpts := mtest.NewOptions().\n\t\tTopologies(mtest.ReplicaSet). // Avoid complexity of sharded fail points.\n\t\tMinServerVersion(\"4.0\").      // 4.0 is needed to use replica set fail points.\n\t\tCreateClient(false)\n\tmt.RunOpts(\"custom deployment\", customDeploymentOpts, func(mt *mtest.T) {\n\t\t// Tests for the changeStreamDeployment type. These are written as integration tests for ChangeStream rather\n\t\t// than unit/integration tests for changeStreamDeployment to ensure that the deployment is correctly wired\n\t\t// by ChangeStream when executing an aggregate.\n\n\t\tmt.Run(\"errors are processed for SDAM on initial aggregate\", func(mt *mtest.T) {\n\t\t\ttpm := eventtest.NewTestPoolMonitor()\n\t\t\tmt.ResetClient(options.Client().\n\t\t\t\tSetPoolMonitor(tpm.PoolMonitor).\n\t\t\t\tSetWriteConcern(mtest.MajorityWc).\n\t\t\t\tSetReadConcern(mtest.MajorityRc).\n\t\t\t\tSetRetryReads(false))\n\n\t\t\tmt.SetFailPoint(failpoint.FailPoint{\n\t\t\t\tConfigureFailPoint: \"failCommand\",\n\t\t\t\tMode: failpoint.Mode{\n\t\t\t\t\tTimes: 1,\n\t\t\t\t},\n\t\t\t\tData: failpoint.Data{\n\t\t\t\t\tFailCommands:    []string{\"aggregate\"},\n\t\t\t\t\tCloseConnection: true,\n\t\t\t\t},\n\t\t\t})\n\n\t\t\t_, err := mt.Coll.Watch(context.Background(), mongo.Pipeline{})\n\t\t\tassert.NotNil(mt, err, \"expected Watch error, got nil\")\n\t\t\tassert.True(mt, tpm.IsPoolCleared(), \"expected pool to be cleared after non-timeout network error but was not\")\n\t\t})\n\t\tmt.Run(\"errors are processed for SDAM on getMore\", func(mt *mtest.T) {\n\t\t\ttpm := eventtest.NewTestPoolMonitor()\n\t\t\tmt.ResetClient(options.Client().\n\t\t\t\tSetPoolMonitor(tpm.PoolMonitor).\n\t\t\t\tSetWriteConcern(mtest.MajorityWc).\n\t\t\t\tSetReadConcern(mtest.MajorityRc).\n\t\t\t\tSetRetryReads(false))\n\n\t\t\tmt.SetFailPoint(failpoint.FailPoint{\n\t\t\t\tConfigureFailPoint: \"failCommand\",\n\t\t\t\tMode: failpoint.Mode{\n\t\t\t\t\tTimes: 1,\n\t\t\t\t},\n\t\t\t\tData: failpoint.Data{\n\t\t\t\t\tFailCommands:    []string{\"getMore\"},\n\t\t\t\t\tCloseConnection: true,\n\t\t\t\t},\n\t\t\t})\n\n\t\t\tcs, err := mt.Coll.Watch(context.Background(), mongo.Pipeline{})\n\t\t\tassert.Nil(mt, err, \"Watch error: %v\", err)\n\t\t\tdefer closeStream(cs)\n\n\t\t\t_, err = mt.Coll.InsertOne(context.Background(), bson.D{{\"x\", 1}})\n\t\t\tassert.Nil(mt, err, \"InsertOne error: %v\", err)\n\n\t\t\tassert.True(mt, cs.Next(context.Background()), \"expected Next to return true, got false (iteration error %v)\",\n\t\t\t\tcs.Err())\n\t\t\tassert.True(mt, tpm.IsPoolCleared(), \"expected pool to be cleared after non-timeout network error but was not\")\n\t\t})\n\t\tmt.Run(\"errors are processed for SDAM on retried aggregate\", func(mt *mtest.T) {\n\t\t\ttpm := eventtest.NewTestPoolMonitor()\n\t\t\tmt.ResetClient(options.Client().\n\t\t\t\tSetPoolMonitor(tpm.PoolMonitor).\n\t\t\t\tSetRetryReads(true))\n\n\t\t\tmt.SetFailPoint(failpoint.FailPoint{\n\t\t\t\tConfigureFailPoint: \"failCommand\",\n\t\t\t\tMode: failpoint.Mode{\n\t\t\t\t\tTimes: 2,\n\t\t\t\t},\n\t\t\t\tData: failpoint.Data{\n\t\t\t\t\tFailCommands:    []string{\"aggregate\"},\n\t\t\t\t\tCloseConnection: true,\n\t\t\t\t},\n\t\t\t})\n\n\t\t\t_, err := mt.Coll.Watch(context.Background(), mongo.Pipeline{})\n\t\t\tassert.NotNil(mt, err, \"expected Watch error, got nil\")\n\n\t\t\tclearedEvents := tpm.Events(func(evt *event.PoolEvent) bool {\n\t\t\t\treturn evt.Type == event.ConnectionPoolCleared\n\t\t\t})\n\t\t\tassert.Equal(mt, 2, len(clearedEvents), \"expected two PoolCleared events, got %d\", len(clearedEvents))\n\t\t})\n\t})\n\t// Setting min server version as 4.0 since v3.6 does not send a \"dropEvent\"\n\tmt.RunOpts(\"call to cursor.Next after cursor closed\", mtest.NewOptions().MinServerVersion(\"4.0\"), func(mt *mtest.T) {\n\t\tcs, err := mt.Coll.Watch(context.Background(), mongo.Pipeline{})\n\t\tassert.Nil(mt, err, \"Watch error: %v\", err)\n\t\tdefer closeStream(cs)\n\n\t\t// Generate insert events\n\t\tgenerateEvents(mt, 5)\n\t\t// Call Coll.Drop to generate drop and invalidate event\n\t\terr = mt.Coll.Drop(context.Background())\n\t\tassert.Nil(mt, err, \"Drop error: %v\", err)\n\n\t\t// Test that all events were successful\n\t\tfor i := 0; i < 7; i++ {\n\t\t\tassert.True(mt, cs.Next(context.Background()), \"Next returned false at index %d; iteration error: %v\", i, cs.Err())\n\t\t}\n\n\t\toperationType := cs.Current.Lookup(\"operationType\").StringValue()\n\t\tassert.Equal(mt, operationType, \"invalidate\", \"expected invalidate event but returned %q event\", operationType)\n\t\t// next call to cs.Next should return False since cursor is closed\n\t\tassert.False(mt, cs.Next(context.Background()), \"expected to return false, but returned true\")\n\t})\n\tmt.Run(\"getMore commands are monitored\", func(mt *mtest.T) {\n\t\tcs, err := mt.Coll.Watch(context.Background(), mongo.Pipeline{})\n\t\tassert.Nil(mt, err, \"Watch error: %v\", err)\n\t\tdefer closeStream(cs)\n\n\t\t_, err = mt.Coll.InsertOne(context.Background(), bson.M{\"x\": 1})\n\t\tassert.Nil(mt, err, \"InsertOne error: %v\", err)\n\n\t\tmt.ClearEvents()\n\t\tassert.True(mt, cs.Next(context.Background()), \"Next returned false with error %v\", cs.Err())\n\t\tevt := mt.GetStartedEvent()\n\t\tassert.Equal(mt, \"getMore\", evt.CommandName, \"expected command 'getMore', got %q\", evt.CommandName)\n\t})\n\tmt.Run(\"killCursors commands are monitored\", func(mt *mtest.T) {\n\t\tcs, err := mt.Coll.Watch(context.Background(), mongo.Pipeline{})\n\t\tassert.Nil(mt, err, \"Watch error: %v\", err)\n\t\tdefer closeStream(cs)\n\n\t\tmt.ClearEvents()\n\t\terr = cs.Close(context.Background())\n\t\tassert.Nil(mt, err, \"Close error: %v\", err)\n\t\tevt := mt.GetStartedEvent()\n\t\tassert.Equal(mt, \"killCursors\", evt.CommandName, \"expected command 'killCursors', got %q\", evt.CommandName)\n\t})\n\tmt.Run(\"Custom\", func(mt *mtest.T) {\n\t\t// Custom options should be a BSON map of option names to Marshalable option values.\n\t\t// We use \"allowDiskUse\" as an example.\n\t\tcustomOpts := bson.M{\"allowDiskUse\": true}\n\t\topts := options.ChangeStream().SetCustom(customOpts)\n\n\t\t// Create change stream with custom options set.\n\t\tmt.ClearEvents()\n\t\tcs, err := mt.Coll.Watch(context.Background(), mongo.Pipeline{}, opts)\n\t\tassert.Nil(mt, err, \"Watch error: %v\", err)\n\t\tdefer closeStream(cs)\n\n\t\t// Assert that custom option is passed to the initial aggregate.\n\t\tevt := mt.GetStartedEvent()\n\t\tassert.Equal(mt, \"aggregate\", evt.CommandName, \"expected command 'aggregate' got, %q\", evt.CommandName)\n\n\t\taduVal, err := evt.Command.LookupErr(\"allowDiskUse\")\n\t\tassert.Nil(mt, err, \"expected field 'allowDiskUse' in started command not found\")\n\t\tadu, ok := aduVal.BooleanOK()\n\t\tassert.True(mt, ok, \"expected field 'allowDiskUse' to be boolean, got %v\", aduVal.Type.String())\n\t\tassert.True(mt, adu, \"expected field 'allowDiskUse' to be true, got false\")\n\t})\n\tmt.RunOpts(\"CustomPipeline\", mtest.NewOptions().MinServerVersion(\"4.0\"), func(mt *mtest.T) {\n\t\t// Custom pipeline options should be a BSON map of option names to Marshalable option values.\n\t\t// We use \"allChangesForCluster\" as an example.\n\t\tcustomPipelineOpts := bson.M{\"allChangesForCluster\": false}\n\t\topts := options.ChangeStream().SetCustomPipeline(customPipelineOpts)\n\n\t\t// Create change stream with custom pipeline options set.\n\t\tmt.ClearEvents()\n\t\tcs, err := mt.Coll.Watch(context.Background(), mongo.Pipeline{}, opts)\n\t\tassert.Nil(mt, err, \"Watch error: %v\", err)\n\t\tdefer closeStream(cs)\n\n\t\t// Assert that custom pipeline option is included in the $changeStream stage.\n\t\tevt := mt.GetStartedEvent()\n\t\tassert.Equal(mt, \"aggregate\", evt.CommandName, \"expected command 'aggregate' got, %q\", evt.CommandName)\n\n\t\tacfcVal, err := evt.Command.LookupErr(\"pipeline\", \"0\", \"$changeStream\", \"allChangesForCluster\")\n\t\tassert.Nil(mt, err, \"expected field 'allChangesForCluster' in $changeStream stage not found\")\n\t\tacfc, ok := acfcVal.BooleanOK()\n\t\tassert.True(mt, ok, \"expected field 'allChangesForCluster' to be boolean, got %v\", acfcVal.Type.String())\n\t\tassert.False(mt, acfc, \"expected field 'allChangesForCluster' to be false, got %v\", acfc)\n\t})\n\n\twithBSONOpts := mtest.NewOptions().ClientOptions(\n\t\toptions.Client().SetBSONOptions(&options.BSONOptions{\n\t\t\tUseJSONStructTags: true,\n\t\t}))\n\tmt.RunOpts(\"with BSONOptions\", withBSONOpts, func(mt *mtest.T) {\n\t\tcs, err := mt.Coll.Watch(context.Background(), mongo.Pipeline{})\n\t\trequire.NoError(mt, err, \"Watch error\")\n\t\tdefer closeStream(cs)\n\n\t\ttype myDocument struct {\n\t\t\tA string `json:\"x\"`\n\t\t}\n\n\t\tvar wg sync.WaitGroup\n\t\twg.Add(1)\n\t\tgo func() {\n\t\t\tdefer wg.Done()\n\t\t\t_, err := mt.Coll.InsertOne(context.Background(), myDocument{A: \"foo\"})\n\t\t\trequire.NoError(mt, err, \"InsertOne error\")\n\t\t}()\n\n\t\tcs.Next(context.Background())\n\n\t\tvar got struct {\n\t\t\tFullDocument myDocument `bson:\"fullDocument\"`\n\t\t}\n\t\terr = cs.Decode(&got)\n\t\trequire.NoError(mt, err, \"Decode error\")\n\n\t\twant := myDocument{\n\t\t\tA: \"foo\",\n\t\t}\n\t\tassert.Equal(mt, want, got.FullDocument, \"expected and actual Decode results are different\")\n\n\t\twg.Wait()\n\t})\n\n\tsplitLargeChangesCollOpts := options.\n\t\tCreateCollection().\n\t\tSetChangeStreamPreAndPostImages(bson.M{\"enabled\": true})\n\n\tsplitLargeChangesOpts := mtOpts.\n\t\tMinServerVersion(\"6.0.9\").\n\t\tCreateClient(true).\n\t\tCollectionCreateOptions(splitLargeChangesCollOpts)\n\n\tmt.RunOpts(\"split large changes\", splitLargeChangesOpts, func(mt *mtest.T) {\n\t\ttype idValue struct {\n\t\t\tID    int32  `bson:\"_id\"`\n\t\t\tValue string `bson:\"value\"`\n\t\t}\n\n\t\tdoc := idValue{\n\t\t\tID:    1,\n\t\t\tValue: \"q\" + strings.Repeat(\"q\", 10*1024*1024),\n\t\t}\n\n\t\t// Insert the document\n\t\t_, err := mt.Coll.InsertOne(context.Background(), doc)\n\t\trequire.NoError(t, err, \"failed to insert idValue\")\n\n\t\t// Watch for change events\n\t\tpipeline := mongo.Pipeline{\n\t\t\t{{\"$changeStreamSplitLargeEvent\", bson.D{}}},\n\t\t}\n\n\t\topts := options.ChangeStream().SetFullDocument(options.Required)\n\n\t\tcs, err := mt.Coll.Watch(context.Background(), pipeline, opts)\n\t\trequire.NoError(t, err, \"failed to watch collection\")\n\n\t\tdefer closeStream(cs)\n\n\t\tvar wg sync.WaitGroup\n\t\twg.Add(1)\n\n\t\tgo func() {\n\t\t\tdefer wg.Done()\n\n\t\t\tfilter := bson.D{{\"_id\", int32(1)}}\n\t\t\tupdate := bson.D{{\"$set\", bson.D{{\"value\", \"z\" + strings.Repeat(\"q\", 10*1024*1024)}}}}\n\n\t\t\t_, err := mt.Coll.UpdateOne(context.Background(), filter, update)\n\t\t\trequire.NoError(mt, err, \"failed to update idValue\")\n\t\t}()\n\n\t\tnextCtx, cancel := context.WithTimeout(context.Background(), 10*time.Second)\n\t\tt.Cleanup(cancel)\n\n\t\ttype splitEvent struct {\n\t\t\tFragment int32 `bson:\"fragment\"`\n\t\t\tOf       int32 `bson:\"of\"`\n\t\t}\n\n\t\tgot := struct {\n\t\t\tSplitEvent splitEvent `bson:\"splitEvent\"`\n\t\t}{}\n\n\t\tcs.Next(nextCtx)\n\n\t\terr = cs.Decode(&got)\n\t\trequire.NoError(mt, err, \"failed to decode first iteration\")\n\n\t\twant := splitEvent{\n\t\t\tFragment: 1,\n\t\t\tOf:       2,\n\t\t}\n\n\t\tassert.Equal(mt, want, got.SplitEvent, \"expected and actual Decode results are different\")\n\n\t\tcs.Next(nextCtx)\n\n\t\terr = cs.Decode(&got)\n\t\trequire.NoError(mt, err, \"failed to decoded second iteration\")\n\n\t\twant = splitEvent{\n\t\t\tFragment: 2,\n\t\t\tOf:       2,\n\t\t}\n\n\t\tassert.Equal(mt, want, got.SplitEvent, \"expected and actual decode results are different\")\n\n\t\twg.Wait()\n\t})\n}\n\nfunc closeStream(cs *mongo.ChangeStream) {\n\t_ = cs.Close(context.Background())\n}\n\nfunc generateEvents(mt *mtest.T, numEvents int) {\n\tmt.Helper()\n\n\tfor i := 0; i < numEvents; i++ {\n\t\tdoc := bson.D{{\"x\", i}}\n\t\t_, err := mt.Coll.InsertOne(context.Background(), doc)\n\t\tassert.Nil(mt, err, \"InsertOne error on document %v: %v\", doc, err)\n\t}\n}\n\n// returns pbrt, operationTime from aggregate command response\nfunc getAggregateResponseInfo(mt *mtest.T) (bson.Raw, bson.Timestamp) {\n\tmt.Helper()\n\n\tsucceeded := mt.GetSucceededEvent()\n\tassert.NotNil(mt, succeeded, \"expected success event for aggregate, got nil\")\n\tassert.Equal(mt, \"aggregate\", succeeded.CommandName, \"expected command name 'aggregate', got '%v'\", succeeded.CommandName)\n\n\tpbrt := succeeded.Reply.Lookup(\"cursor\", \"postBatchResumeToken\").Document()\n\toptimeT, optimeI := succeeded.Reply.Lookup(\"operationTime\").Timestamp()\n\treturn pbrt, bson.Timestamp{T: optimeT, I: optimeI}\n}\n\nfunc compareResumeTokens(mt *mtest.T, cs *mongo.ChangeStream, expected bson.Raw) {\n\tmt.Helper()\n\tassert.Equal(mt, expected, cs.ResumeToken(), \"expected resume token %v, got %v\", expected, cs.ResumeToken())\n}\n"
  },
  {
    "path": "internal/integration/clam_prose_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2023-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage integration\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"testing\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/integration/mtest\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/integtest\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/logger\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n)\n\nvar ErrInvalidTruncation = fmt.Errorf(\"invalid truncation\")\n\nfunc clamTruncErr(mt *mtest.T, op string, want, got int) error {\n\tmt.Helper()\n\n\treturn fmt.Errorf(\"%w: expected length %s %d, got %d\", ErrInvalidTruncation, op, want, got)\n}\n\nfunc clamDefaultTruncLimitOp(ctx context.Context, mt *mtest.T, coll *mongo.Collection) {\n\tmt.Helper()\n\n\tconst documentsSize = 100\n\n\t// Construct an array of docs containing the document {\"x\" : \"y\"}\n\t// repeated \"documentSize\" times.\n\tdocs := []any{}\n\tfor i := 0; i < documentsSize; i++ {\n\t\tdocs = append(docs, bson.D{{\"x\", \"y\"}})\n\t}\n\n\t// Insert docs to a the collection.\n\t_, err := coll.InsertMany(ctx, docs)\n\tassert.Nil(mt, err, \"InsertMany error: %v\", err)\n\n\t// Run find() on the collection.\n\t_, err = coll.Find(ctx, bson.D{})\n\tassert.Nil(mt, err, \"Find error: %v\", err)\n}\n\nfunc clamDefaultTruncLimitLogs(mt *mtest.T) []truncValidator {\n\tmt.Helper()\n\n\tconst cmd = \"command\"\n\tconst rpl = \"reply\"\n\n\texpTruncLen := len(logger.TruncationSuffix) + logger.DefaultMaxDocumentLength\n\tvalidators := make([]truncValidator, 4)\n\n\t// Insert started.\n\tvalidators[0] = newTruncValidator(mt, cmd, func(cmd string) error {\n\t\tif len(cmd) != expTruncLen {\n\t\t\treturn clamTruncErr(mt, \"=\", expTruncLen, len(cmd))\n\t\t}\n\n\t\treturn nil\n\t})\n\n\t// Insert succeeded.\n\tvalidators[1] = newTruncValidator(mt, rpl, func(cmd string) error {\n\t\tif len(cmd) > expTruncLen {\n\t\t\treturn clamTruncErr(mt, \"<=\", expTruncLen, len(cmd))\n\t\t}\n\n\t\treturn nil\n\t})\n\n\t// Find started, nothing to validate.\n\tvalidators[2] = nil\n\n\t// Find succeeded.\n\tvalidators[3] = newTruncValidator(mt, rpl, func(cmd string) error {\n\t\tif len(cmd) != expTruncLen {\n\t\t\treturn clamTruncErr(mt, \"=\", expTruncLen, len(cmd))\n\t\t}\n\n\t\treturn nil\n\t})\n\n\treturn validators\n}\n\nfunc clamExplicitTruncLimitOp(ctx context.Context, mt *mtest.T, coll *mongo.Collection) {\n\tmt.Helper()\n\n\tresult := coll.Database().RunCommand(ctx, bson.D{{\"hello\", true}})\n\tassert.Nil(mt, result.Err(), \"RunCommand error: %v\", result.Err())\n}\n\nfunc clamExplicitTruncLimitLogs(mt *mtest.T) []truncValidator {\n\tmt.Helper()\n\n\tconst cmd = \"command\"\n\tconst rpl = \"reply\"\n\n\texpTruncLen := len(logger.TruncationSuffix) + 5\n\tvalidators := make([]truncValidator, 2)\n\n\t// Hello started.\n\tvalidators[0] = newTruncValidator(mt, cmd, func(cmd string) error {\n\t\tif len(cmd) != expTruncLen {\n\t\t\treturn clamTruncErr(mt, \"=\", expTruncLen, len(cmd))\n\t\t}\n\n\t\treturn nil\n\t})\n\n\t// Hello succeeded.\n\tvalidators[1] = newTruncValidator(mt, rpl, func(cmd string) error {\n\t\tif len(cmd) != expTruncLen {\n\t\t\treturn clamTruncErr(mt, \"=\", expTruncLen, len(cmd))\n\t\t}\n\n\t\treturn nil\n\t})\n\n\treturn validators\n}\n\nfunc clamExplicitTruncLimitFailOp(ctx context.Context, mt *mtest.T, coll *mongo.Collection) {\n\tmt.Helper()\n\n\tresult := coll.Database().RunCommand(ctx, bson.D{{\"notARealCommand\", true}})\n\tassert.NotNil(mt, result.Err(), \"expected RunCommand error, got: %v\", result.Err())\n}\n\nfunc clamExplicitTruncLimitFailLogs(mt *mtest.T) []truncValidator {\n\tmt.Helper()\n\n\tconst fail = \"failure\"\n\n\texpTruncLen := len(logger.TruncationSuffix) + 5\n\tvalidators := make([]truncValidator, 2)\n\n\t// Hello started, nothing to validate.\n\tvalidators[0] = nil\n\n\t// Hello failed.\n\tvalidators[1] = newTruncValidator(mt, fail, func(cmd string) error {\n\t\tif len(cmd) != expTruncLen {\n\t\t\treturn clamTruncErr(mt, \"=\", expTruncLen, len(cmd))\n\t\t}\n\n\t\treturn nil\n\t})\n\n\treturn validators\n}\n\n// clamMultiByteTrunc runs an operation to insert a very large document with the\n// multi-byte character \"界\" repeated a large number of times. This repetition\n// is done to categorically ensure that the truncation point is made somewhere\n// within the multi-byte character. For example a typical insertion reply may\n// look something like this:\n//\n// {\"insert\": \"setuptest\",\"ordered\": true,\"lsid\": {\"id\": ...\n//\n// We have no control over how the \"header\" portion of this reply is formatted.\n// Over time the server might support newer fields or change the formatting of\n// existing fields. This means that the truncation point could be anywhere in\n// the \"header\" portion of the reply. A large document lowers the likelihood of\n// the truncation point being in the \"header\" portion of the reply.\nfunc clamMultiByteTrunc(ctx context.Context, mt *mtest.T, coll *mongo.Collection) {\n\tmt.Helper()\n\n\tconst multiByteCharStrLen = 50_000\n\tconst strToRepeat = \"界\"\n\n\t// Repeat the string \"strToRepeat\" \"multiByteCharStrLen\" times.\n\tmultiByteCharStr := \"\"\n\tfor i := 0; i < multiByteCharStrLen; i++ {\n\t\tmultiByteCharStr += strToRepeat\n\t}\n\n\t_, err := coll.InsertOne(ctx, bson.D{{\"x\", multiByteCharStr}})\n\tassert.Nil(mt, err, \"InsertOne error: %v\", err)\n}\n\nfunc clamMultiByteTruncLogs(mt *mtest.T) []truncValidator {\n\tmt.Helper()\n\n\tconst cmd = \"command\"\n\tconst strToRepeat = \"界\"\n\n\tvalidators := make([]truncValidator, 2)\n\n\t// Insert started.\n\tvalidators[0] = newTruncValidator(mt, cmd, func(cmd string) error {\n\t\t// Remove the suffix from the command string.\n\t\tcmd = cmd[:len(cmd)-len(logger.TruncationSuffix)]\n\n\t\t// Get the last 3 bytes of the command string.\n\t\tlast3Bytes := cmd[len(cmd)-3:]\n\n\t\t// Make sure the last 3 bytes are the multi-byte character.\n\t\tif last3Bytes != strToRepeat {\n\t\t\treturn fmt.Errorf(\"expected last 3 bytes to be %q, got %q\", strToRepeat, last3Bytes)\n\t\t}\n\n\t\treturn nil\n\t})\n\n\treturn validators\n}\n\nfunc TestCommandLoggingAndMonitoringProse(t *testing.T) {\n\tt.Parallel()\n\n\tmt := mtest.New(t, mtest.NewOptions().CreateClient(false))\n\n\tfor _, tcase := range []struct {\n\t\t// name is the name of the test case\n\t\tname string\n\n\t\t// collectionName is the name to assign the collection for\n\t\t// processing the operations. This should be unique across test\n\t\t// cases.\n\t\tcollectionName string\n\n\t\t// maxDocumentLength is the maximum document length for a\n\t\t// command message.\n\t\tmaxDocumentLength uint\n\n\t\t// orderedLogValidators is a slice of log validators that should\n\t\t// be 1-1 with the actual logs that are propagated by the\n\t\t// LogSink. The order here matters, the first log will be\n\t\t// validated by the 0th validator, the second log will be\n\t\t// validated by the 1st validator, etc.\n\t\torderedLogValidators []truncValidator\n\n\t\t// operation is the operation to perform on the collection that\n\t\t// will result in log propagation. The logs created by\n\t\t// \"operation\" will be validated against the\n\t\t// \"orderedLogValidators.\"\n\t\toperation func(context.Context, *mtest.T, *mongo.Collection)\n\n\t\t// Setup is a function that will be run before the test case.\n\t\t// Operations performed in this function will not be logged.\n\t\tsetup func(context.Context, *mtest.T, *mongo.Collection)\n\t}{\n\t\t{\n\t\t\tname:                 \"1 Default truncation limit\",\n\t\t\tcollectionName:       \"46a624c57c72463d90f88a733e7b28b4\",\n\t\t\toperation:            clamDefaultTruncLimitOp,\n\t\t\torderedLogValidators: clamDefaultTruncLimitLogs(mt),\n\t\t},\n\t\t{\n\t\t\tname:                 \"2 Explicitly configured truncation limit\",\n\t\t\tcollectionName:       \"540baa64dc854ca2a639627e2f0918df\",\n\t\t\tmaxDocumentLength:    5,\n\t\t\toperation:            clamExplicitTruncLimitOp,\n\t\t\torderedLogValidators: clamExplicitTruncLimitLogs(mt),\n\t\t},\n\t\t{\n\t\t\tname:                 \"2 Explicitly configured truncation limit for failures\",\n\t\t\tcollectionName:       \"aff43dfcaa1a4014b58aaa9606f5bd44\",\n\t\t\tmaxDocumentLength:    5,\n\t\t\toperation:            clamExplicitTruncLimitFailOp,\n\t\t\torderedLogValidators: clamExplicitTruncLimitFailLogs(mt),\n\t\t},\n\n\t\t// The third test case is to ensure that a truncation point made\n\t\t// within a multi-byte character is handled correctly. The\n\t\t// chosen multi-byte character for this test is \"界\" (U+754C).\n\t\t// This character is repeated a large number of times (50,000).\n\t\t// We need to run this test 3 times to ensure that the\n\t\t// truncation occurs at a middle point within the multi-byte\n\t\t// character at least once (at most twice).\n\t\t{\n\t\t\tname:                 \"3.1 Truncation with multi-byte codepoints\",\n\t\t\tcollectionName:       \"5ed6d1b7-e358-438a-b067-e1d1dd10fee1\",\n\t\t\tmaxDocumentLength:    20_000,\n\t\t\toperation:            clamMultiByteTrunc,\n\t\t\torderedLogValidators: clamMultiByteTruncLogs(mt),\n\t\t},\n\t\t{\n\t\t\tname:                 \"3.2 Truncation with multi-byte codepoints\",\n\t\t\tcollectionName:       \"5ed6d1b7-e358-438a-b067-e1d1dd10fee1\",\n\t\t\tmaxDocumentLength:    20_001,\n\t\t\toperation:            clamMultiByteTrunc,\n\t\t\torderedLogValidators: clamMultiByteTruncLogs(mt),\n\t\t},\n\t\t{\n\t\t\tname:                 \"3.3 Truncation with multi-byte codepoints\",\n\t\t\tcollectionName:       \"5ed6d1b7-e358-438a-b067-e1d1dd10fee1\",\n\t\t\tmaxDocumentLength:    20_002,\n\t\t\toperation:            clamMultiByteTrunc,\n\t\t\torderedLogValidators: clamMultiByteTruncLogs(mt),\n\t\t},\n\t} {\n\t\ttcase := tcase\n\n\t\tmt.Run(tcase.name, func(mt *mtest.T) {\n\t\t\tmt.Parallel()\n\n\t\t\tconst deadline = 10 * time.Second\n\t\t\tctx := context.Background()\n\n\t\t\t// Before the test case, we need to see if there is a\n\t\t\t// setup function to run.\n\t\t\tif tcase.setup != nil {\n\t\t\t\tclientOpts := options.Client().ApplyURI(mtest.ClusterURI())\n\n\t\t\t\t// Create a context with a deadline so that the\n\t\t\t\t// test setup doesn't hang forever.\n\t\t\t\tctx, cancel := context.WithTimeout(ctx, deadline)\n\t\t\t\tdefer cancel()\n\n\t\t\t\tintegtest.AddTestServerAPIVersion(clientOpts)\n\n\t\t\t\tclient, err := mongo.Connect(clientOpts)\n\t\t\t\tassert.Nil(mt, err, \"Connect error in setup: %v\", err)\n\n\t\t\t\tcoll := mt.CreateCollection(mtest.Collection{\n\t\t\t\t\tName:   tcase.collectionName,\n\t\t\t\t\tClient: client,\n\t\t\t\t}, false)\n\n\t\t\t\ttcase.setup(ctx, mt, coll)\n\t\t\t}\n\n\t\t\t// If there is no operation, then we don't need to run\n\t\t\t// the test case.\n\t\t\tif tcase.operation == nil {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\t// If there are no log validators, then we should error.\n\t\t\tif len(tcase.orderedLogValidators) == 0 {\n\t\t\t\tmt.Fatalf(\"no log validators provided\")\n\t\t\t}\n\n\t\t\tsinkCtx, sinkCancel := context.WithDeadline(ctx, time.Now().Add(deadline))\n\t\t\tdefer sinkCancel()\n\n\t\t\tvalidator := func(order int, _ int, _ string, keysAndValues ...any) error {\n\t\t\t\t// If the order exceeds the length of the\n\t\t\t\t// \"orderedCaseValidators,\" then throw an error.\n\t\t\t\tif order >= len(tcase.orderedLogValidators) {\n\t\t\t\t\treturn fmt.Errorf(\"not enough expected cases to validate\")\n\t\t\t\t}\n\n\t\t\t\tcaseValidator := tcase.orderedLogValidators[order]\n\t\t\t\tif caseValidator == nil {\n\t\t\t\t\treturn nil\n\t\t\t\t}\n\n\t\t\t\treturn tcase.orderedLogValidators[order](keysAndValues...)\n\t\t\t}\n\n\t\t\tsink := newTestLogSink(sinkCtx, mt, len(tcase.orderedLogValidators), validator)\n\n\t\t\t// Configure logging with a minimum severity level of\n\t\t\t// \"debug\" for the \"command\" component without\n\t\t\t// explicitly configuring the max document length.\n\t\t\tloggerOpts := options.Logger().SetSink(sink).\n\t\t\t\tSetComponentLevel(options.LogComponentCommand, options.LogLevelDebug)\n\n\t\t\t// If the test case requires a maximum document length,\n\t\t\t// then configure it.\n\t\t\tif mdl := tcase.maxDocumentLength; mdl != 0 {\n\t\t\t\tloggerOpts.SetMaxDocumentLength(mdl)\n\t\t\t}\n\n\t\t\tclientOpts := options.Client().SetLoggerOptions(loggerOpts).ApplyURI(mtest.ClusterURI())\n\n\t\t\tintegtest.AddTestServerAPIVersion(clientOpts)\n\n\t\t\tclient, err := mongo.Connect(clientOpts)\n\t\t\tassert.Nil(mt, err, \"Connect error: %v\", err)\n\n\t\t\tcoll := mt.CreateCollection(mtest.Collection{\n\t\t\t\tName:   tcase.collectionName,\n\t\t\t\tClient: client,\n\t\t\t}, false)\n\n\t\t\ttcase.operation(ctx, mt, coll)\n\n\t\t\t// Verify the logs.\n\t\t\tif err := <-sink.errs(); err != nil {\n\t\t\t\tmt.Fatalf(\"unexpected error: %v\", err)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "internal/integration/client_options_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage integration\n\nimport (\n\t\"context\"\n\t\"net\"\n\t\"sync/atomic\"\n\t\"testing\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/integtest\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/require\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n)\n\nfunc TestClientOptions_CustomDialer(t *testing.T) {\n\ttd := &testDialer{d: &net.Dialer{}}\n\tcs := integtest.ConnString(t)\n\topts := options.Client().ApplyURI(cs.String()).SetDialer(td)\n\tintegtest.AddTestServerAPIVersion(opts)\n\tclient, err := mongo.Connect(opts)\n\trequire.NoError(t, err)\n\t_, err = client.ListDatabases(context.Background(), bson.D{})\n\trequire.NoError(t, err)\n\tgot := atomic.LoadInt32(&td.called)\n\tif got < 1 {\n\t\tt.Errorf(\"Custom dialer was not used when dialing new connections\")\n\t}\n}\n\ntype testDialer struct {\n\tcalled int32\n\td      mongo.Dialer\n}\n\nfunc (td *testDialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) {\n\tatomic.AddInt32(&td.called, 1)\n\treturn td.d.DialContext(ctx, network, address)\n}\n"
  },
  {
    "path": "internal/integration/client_side_encryption_prose_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\n//go:build cse\n\npackage integration\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"crypto/tls\"\n\t\"encoding/base64\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io/ioutil\"\n\t\"net\"\n\t\"net/http\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/event\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/handshake\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/integration/mtest\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/integtest\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/require\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/writeconcern\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/mongocrypt\"\n\tmongocryptopts \"go.mongodb.org/mongo-driver/v2/x/mongo/driver/mongocrypt/options\"\n)\n\nvar (\n\tlocalMasterKey      = []byte(\"2x44+xduTaBBkY16Er5DuADaghvS4vwdkg8tpPp3tz6gV01A1CwbD9itQ2HFDgPWOp8eMaC1Oi766JzXZBdBdbdMurdonJ1d\")\n\tfullKmsProvidersMap = map[string]map[string]any{\n\t\t\"aws\": {\n\t\t\t\"accessKeyId\":     awsAccessKeyID,\n\t\t\t\"secretAccessKey\": awsSecretAccessKey,\n\t\t},\n\t\t\"azure\": {\n\t\t\t\"tenantId\":     azureTenantID,\n\t\t\t\"clientId\":     azureClientID,\n\t\t\t\"clientSecret\": azureClientSecret,\n\t\t},\n\t\t\"gcp\": {\n\t\t\t\"email\":      gcpEmail,\n\t\t\t\"privateKey\": gcpPrivateKey,\n\t\t},\n\t\t\"local\": {\"key\": localMasterKey},\n\t\t\"kmip\": {\n\t\t\t\"endpoint\": \"localhost:5698\",\n\t\t},\n\t}\n)\n\nconst (\n\tclientEncryptionProseDir      = \"../../testdata/client-side-encryption-prose\"\n\tdeterministicAlgorithm        = \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\"\n\trandomAlgorithm               = \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\"\n\tkvNamespace                   = \"keyvault.datakeys\" // default namespace for the key vault collection\n\tkeySubtype               byte = 4                   // expected subtype for data keys\n\tencryptedValueSubtype    byte = 6                   // expected subtypes for encrypted values\n\tcryptMaxBatchSizeBytes        = 2097152             // max bytes in write batch when auto encryption is enabled\n\tmaxBsonObjSize                = 16777216            // max bytes in BSON object\n)\n\nfunc newCSE_T(t *testing.T, opts *mtest.Options) *mtest.T {\n\tt.Helper()\n\n\tverifyClientSideEncryptionVarsSet(t)\n\tmt := mtest.New(t, opts.Enterprise(true))\n\n\tdefaultKvClientOptions := options.Client().ApplyURI(mtest.ClusterURI())\n\tintegtest.AddTestServerAPIVersion(defaultKvClientOptions)\n\n\treturn mt\n}\n\nfunc newNoClientOpts() *mtest.Options {\n\treturn mtest.NewOptions().CreateClient(false)\n}\n\nfunc TestClientSideEncryptionProse_1_custom_key_material_test(t *testing.T) {\n\tmt := newCSE_T(t, mtest.NewOptions())\n\tmt.Setup()\n\n\tconst (\n\t\tdkCollection = \"datakeys\"\n\t\tidKey        = \"_id\"\n\t\tkvDatabase   = \"keyvault\"\n\t)\n\n\t// Create a ClientEncryption object (referred to as client_encryption) with client set as the keyVaultClient.\n\t// Using client, drop the collection keyvault.datakeys.\n\tdefaultKvClientOptions := options.Client().ApplyURI(mtest.ClusterURI())\n\tcse := setup(mt, nil, defaultKvClientOptions, options.ClientEncryption().\n\t\tSetKmsProviders(fullKmsProvidersMap).\n\t\tSetKeyVaultNamespace(kvNamespace))\n\n\terr := cse.kvClient.Database(kvDatabase).Collection(dkCollection).Drop(context.Background())\n\tassert.Nil(mt, err, \"error dropping %q namespace: %v\", kvNamespace, err)\n\n\t// Using client_encryption, create a data key with a local KMS provider and the declared b64 custom key material\n\t// (given as base64).\n\tconst b641 = `xPTAjBRG5JiPm+d3fj6XLi2q5DMXUS/f1f+SMAlhhwkhDRL0kr8r9GDLIGTAGlvC+HVjSIgdL+RKwZCvpXSyxTICWSXT` +\n\t\t`UYsWYPyu3IoHbuBZdmw2faM3WhcRIgbMReU5`\n\n\t// Decode the base64-encoded keyMaterial string.\n\tkm, err := base64.StdEncoding.DecodeString(b641)\n\tassert.Nil(mt, err, \"error decoding b64: %v\", err)\n\n\t_, err = cse.clientEnc.CreateDataKey(context.Background(), \"local\", options.DataKey().SetKeyMaterial(km))\n\tassert.Nil(mt, err, \"error creating data key: %v\", err)\n\n\t// Find the resulting key document in keyvault.datakeys, save a copy of the key document, then remove the key\n\t// document from the collection.\n\tcoll := cse.kvClient.Database(kvDatabase).Collection(dkCollection)\n\n\tkeydoc, err := coll.FindOne(context.Background(), bson.D{}).Raw()\n\tassert.Nil(mt, err, \"error in decoding bytes: %v\", err)\n\n\t// Remove the key document from the collection.\n\tid, err := keydoc.LookupErr(idKey)\n\tassert.Nil(mt, err, \"error looking up %s: %v\", idKey, err)\n\n\t_, err = coll.DeleteOne(context.Background(), bson.D{{idKey, id}})\n\tassert.Nil(mt, err, \"error deleting key document: %v\", err)\n\n\t// Replace the _id field in the copied key document with a UUID with base64 value AAAAAAAAAAAAAAAAAAAAAA== (16\n\t// bytes all equal to 0x00) and insert the modified key document into keyvault.datakeys with majority write\n\t// concern.\n\tcidx, alteredKeydoc := bsoncore.AppendDocumentStart(nil)\n\tdocElems, _ := keydoc.Elements()\n\tfor _, element := range docElems {\n\t\tif key := element.Key(); key != idKey {\n\t\t\talteredKeydoc = bsoncore.AppendValueElement(alteredKeydoc, key, rawValueToCoreValue(element.Value()))\n\t\t}\n\t}\n\tempty := [16]byte{}\n\tuuidSubtype, _ := keydoc.Lookup(idKey).Binary()\n\talteredKeydoc = bsoncore.AppendBinaryElement(alteredKeydoc, idKey, uuidSubtype, empty[:])\n\talteredKeydoc, _ = bsoncore.AppendDocumentEnd(alteredKeydoc, cidx)\n\n\t// Insert the copied key document into keyvault.datakeys with majority write concern.\n\twcMajority := writeconcern.Majority()\n\twcMajorityCollectionOpts := options.Collection().SetWriteConcern(wcMajority)\n\twcmColl := cse.kvClient.Database(kvDatabase).Collection(dkCollection, wcMajorityCollectionOpts)\n\t_, err = wcmColl.InsertOne(context.Background(), alteredKeydoc)\n\tassert.Nil(mt, err, \"error inserting altered key document: %v\", err)\n\n\t// Using client_encryption, encrypt the string \"test\" with the modified data key using the\n\t// AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic algorithm and assert the resulting value is equal to the\n\t// declared b64 constant.\n\tconst b642 = `AQAAAAAAAAAAAAAAAAAAAAACz0ZOLuuhEYi807ZXTdhbqhLaS2/t9wLifJnnNYwiw79d75QYIZ6M/aYC1h9nCzCjZ7pG` +\n\t\t`UpAuNnkUhnIXM3PjrA==`\n\n\tempty = [16]byte{}\n\tkeyid := bson.Binary{Subtype: 0x04, Data: empty[:]}\n\n\tencOpts := options.Encrypt().SetAlgorithm(deterministicAlgorithm).SetKeyID(keyid)\n\ttestVal := bson.RawValue{\n\t\tType:  bson.TypeString,\n\t\tValue: bsoncore.AppendString(nil, \"test\"),\n\t}\n\tactual, err := cse.clientEnc.Encrypt(context.Background(), testVal, encOpts)\n\tassert.Nil(mt, err, \"error encrypting data: %v\", err)\n\n\texpected := bson.Binary{Subtype: 0x06}\n\texpected.Data, _ = base64.StdEncoding.DecodeString(b642)\n\n\tassert.Equal(mt, actual, expected, \"expected: %v, got: %v\", actual, expected)\n}\n\nfunc TestClientSideEncryptionProse_2_data_key_and_double_encryption(t *testing.T) {\n\tmt := newCSE_T(t, newNoClientOpts())\n\tmt.Setup()\n\n\t// set up options structs\n\tschema := bson.D{\n\t\t{\"bsonType\", \"object\"},\n\t\t{\"properties\", bson.D{\n\t\t\t{\"encrypted_placeholder\", bson.D{\n\t\t\t\t{\"encrypt\", bson.D{\n\t\t\t\t\t{\"keyId\", \"/placeholder\"},\n\t\t\t\t\t{\"bsonType\", \"string\"},\n\t\t\t\t\t{\"algorithm\", \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\"},\n\t\t\t\t}},\n\t\t\t}},\n\t\t}},\n\t}\n\tschemaMap := map[string]any{\"db.coll\": schema}\n\ttlsConfig := make(map[string]*tls.Config)\n\tif tlsCAFileKMIP != \"\" && tlsClientCertificateKeyFileKMIP != \"\" {\n\t\ttlsOpts := map[string]any{\n\t\t\t\"tlsCertificateKeyFile\": tlsClientCertificateKeyFileKMIP,\n\t\t\t\"tlsCAFile\":             tlsCAFileKMIP,\n\t\t}\n\t\tkmipConfig, err := options.BuildTLSConfig(tlsOpts)\n\t\tassert.Nil(mt, err, \"BuildTLSConfig error: %v\", err)\n\t\ttlsConfig[\"kmip\"] = kmipConfig\n\t}\n\n\taeo := options.AutoEncryption().\n\t\tSetKmsProviders(fullKmsProvidersMap).\n\t\tSetKeyVaultNamespace(kvNamespace).\n\t\tSetSchemaMap(schemaMap).\n\t\tSetTLSConfig(tlsConfig).\n\t\tSetExtraOptions(getCryptSharedLibExtraOptions())\n\n\tceo := options.ClientEncryption().\n\t\tSetKmsProviders(fullKmsProvidersMap).\n\t\tSetKeyVaultNamespace(kvNamespace).\n\t\tSetTLSConfig(tlsConfig)\n\n\tawsMasterKey := bson.D{\n\t\t{\"region\", \"us-east-1\"},\n\t\t{\"key\", \"arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0\"},\n\t}\n\tazureMasterKey := bson.D{\n\t\t{\"keyVaultEndpoint\", \"key-vault-csfle.vault.azure.net\"},\n\t\t{\"keyName\", \"key-name-csfle\"},\n\t}\n\tgcpMasterKey := bson.D{\n\t\t{\"projectId\", \"devprod-drivers\"},\n\t\t{\"location\", \"global\"},\n\t\t{\"keyRing\", \"key-ring-csfle\"},\n\t\t{\"keyName\", \"key-name-csfle\"},\n\t}\n\tkmipMasterKey := bson.D{}\n\ttestCases := []struct {\n\t\tprovider  string\n\t\tmasterKey any\n\t}{\n\t\t{\"local\", nil},\n\t\t{\"aws\", awsMasterKey},\n\t\t{\"azure\", azureMasterKey},\n\t\t{\"gcp\", gcpMasterKey},\n\t\t{\"kmip\", kmipMasterKey},\n\t}\n\tfor _, tc := range testCases {\n\t\tmt.Run(tc.provider, func(mt *mtest.T) {\n\t\t\tif tc.provider == \"kmip\" && \"\" == os.Getenv(\"KMS_MOCK_SERVERS_RUNNING\") {\n\t\t\t\tmt.Skipf(\"Skipping test as KMS_MOCK_SERVERS_RUNNING is not set\")\n\t\t\t}\n\t\t\tvar startedEvents []*event.CommandStartedEvent\n\t\t\tmonitor := &event.CommandMonitor{\n\t\t\t\tStarted: func(_ context.Context, evt *event.CommandStartedEvent) {\n\t\t\t\t\tstartedEvents = append(startedEvents, evt)\n\t\t\t\t},\n\t\t\t}\n\t\t\tkvClientOpts := options.Client().ApplyURI(mtest.ClusterURI()).SetMonitor(monitor)\n\t\t\tintegtest.AddTestServerAPIVersion(kvClientOpts)\n\t\t\tcpt := setup(mt, aeo, kvClientOpts, ceo)\n\t\t\tdefer cpt.teardown(mt)\n\n\t\t\t// create data key\n\t\t\tkeyAltName := fmt.Sprintf(\"%s_altname\", tc.provider)\n\t\t\tdataKeyOpts := options.DataKey().SetKeyAltNames([]string{keyAltName})\n\t\t\tif tc.masterKey != nil {\n\t\t\t\tdataKeyOpts.SetMasterKey(tc.masterKey)\n\t\t\t}\n\t\t\tdataKeyID, err := cpt.clientEnc.CreateDataKey(context.Background(), tc.provider, dataKeyOpts)\n\t\t\tassert.Nil(mt, err, \"CreateDataKey error: %v\", err)\n\t\t\tassert.Equal(mt, keySubtype, dataKeyID.Subtype,\n\t\t\t\t\"expected data key subtype %v, got %v\", keySubtype, dataKeyID.Subtype)\n\n\t\t\t// assert that the key exists in the key vault\n\t\t\tcursor, err := cpt.keyVaultColl.Find(context.Background(), bson.D{{\"_id\", dataKeyID}})\n\t\t\tassert.Nil(mt, err, \"key vault Find error: %v\", err)\n\t\t\tassert.True(mt, cursor.Next(context.Background()), \"no keys found in key vault\")\n\t\t\tprovider := cursor.Current.Lookup(\"masterKey\", \"provider\").StringValue()\n\t\t\tassert.Equal(mt, tc.provider, provider, \"expected provider %v, got %v\", tc.provider, provider)\n\t\t\tassert.False(mt, cursor.Next(context.Background()), \"unexpected document in key vault: %v\", cursor.Current)\n\n\t\t\t// verify that the key was inserted using write concern majority\n\t\t\tassert.Equal(mt, 1, len(startedEvents), \"expected 1 CommandStartedEvent, got %v\", len(startedEvents))\n\t\t\tevt := startedEvents[0]\n\t\t\tassert.Equal(mt, \"insert\", evt.CommandName, \"expected command 'insert', got '%v'\", evt.CommandName)\n\t\t\twriteConcernVal, err := evt.Command.LookupErr(\"writeConcern\")\n\t\t\tassert.Nil(mt, err, \"expected writeConcern in command %s\", evt.Command)\n\t\t\twString := writeConcernVal.Document().Lookup(\"w\").StringValue()\n\t\t\tassert.Equal(mt, \"majority\", wString, \"expected write concern 'majority', got %v\", wString)\n\n\t\t\t// encrypt a value with the new key by ID\n\t\t\tvalueToEncrypt := fmt.Sprintf(\"hello %s\", tc.provider)\n\t\t\trawVal := bson.RawValue{Type: bson.TypeString, Value: bsoncore.AppendString(nil, valueToEncrypt)}\n\t\t\tencrypted, err := cpt.clientEnc.Encrypt(context.Background(), rawVal,\n\t\t\t\toptions.Encrypt().SetAlgorithm(deterministicAlgorithm).SetKeyID(dataKeyID))\n\t\t\tassert.Nil(mt, err, \"Encrypt error while encrypting value by ID: %v\", err)\n\t\t\tassert.Equal(mt, encryptedValueSubtype, encrypted.Subtype,\n\t\t\t\t\"expected encrypted value subtype %v, got %v\", encryptedValueSubtype, encrypted.Subtype)\n\n\t\t\t// insert an encrypted value. the value shouldn't be encrypted again because it's not in the schema.\n\t\t\t_, err = cpt.cseColl.InsertOne(context.Background(), bson.D{{\"_id\", tc.provider}, {\"value\", encrypted}})\n\t\t\tassert.Nil(mt, err, \"InsertOne error: %v\", err)\n\n\t\t\t// find the inserted document. the value should be decrypted automatically\n\t\t\tresBytes, err := cpt.cseColl.FindOne(context.Background(), bson.D{{\"_id\", tc.provider}}).Raw()\n\t\t\tassert.Nil(mt, err, \"Find error: %v\", err)\n\t\t\tfoundVal := resBytes.Lookup(\"value\").StringValue()\n\t\t\tassert.Equal(mt, valueToEncrypt, foundVal, \"expected value %v, got %v\", valueToEncrypt, foundVal)\n\n\t\t\t// encrypt a value with an alternate name for the new key\n\t\t\taltEncrypted, err := cpt.clientEnc.Encrypt(context.Background(), rawVal,\n\t\t\t\toptions.Encrypt().SetAlgorithm(deterministicAlgorithm).SetKeyAltName(keyAltName))\n\t\t\tassert.Nil(mt, err, \"Encrypt error while encrypting value by alt key name: %v\", err)\n\t\t\tassert.Equal(mt, encryptedValueSubtype, altEncrypted.Subtype,\n\t\t\t\t\"expected encrypted value subtype %v, got %v\", encryptedValueSubtype, altEncrypted.Subtype)\n\t\t\tassert.Equal(mt, encrypted.Data, altEncrypted.Data,\n\t\t\t\t\"expected data %v, got %v\", encrypted.Data, altEncrypted.Data)\n\n\t\t\t// insert an encrypted value for an auto-encrypted field\n\t\t\t_, err = cpt.cseColl.InsertOne(context.Background(), bson.D{{\"encrypted_placeholder\", encrypted}})\n\t\t\tassert.NotNil(mt, err, \"expected InsertOne error, got nil\")\n\t\t})\n\t}\n}\n\nfunc TestClientSideEncryptionProse_3_external_key_vault_test(t *testing.T) {\n\tmt := newCSE_T(t, newNoClientOpts())\n\tmt.Setup()\n\n\ttestCases := []struct {\n\t\tname          string\n\t\texternalVault bool\n\t}{\n\t\t{\"with external vault\", true},\n\t\t{\"without external vault\", false},\n\t}\n\n\tfor _, tc := range testCases {\n\t\tmt.Run(tc.name, func(mt *mtest.T) {\n\t\t\t// setup options structs\n\t\t\tkmsProviders := map[string]map[string]any{\n\t\t\t\t\"local\": {\n\t\t\t\t\t\"key\": localMasterKey,\n\t\t\t\t},\n\t\t\t}\n\t\t\tschemaMap := map[string]any{\"db.coll\": readJSONFile(mt, \"external-schema.json\")}\n\t\t\taeo := options.AutoEncryption().\n\t\t\t\tSetKmsProviders(kmsProviders).\n\t\t\t\tSetKeyVaultNamespace(kvNamespace).\n\t\t\t\tSetSchemaMap(schemaMap).\n\t\t\t\tSetExtraOptions(getCryptSharedLibExtraOptions())\n\t\t\tceo := options.ClientEncryption().\n\t\t\t\tSetKmsProviders(kmsProviders).\n\t\t\t\tSetKeyVaultNamespace(kvNamespace)\n\t\t\tkvClientOpts := options.Client().ApplyURI(mtest.ClusterURI())\n\n\t\t\tif tc.externalVault {\n\t\t\t\texternalKvOpts := options.Client().ApplyURI(mtest.ClusterURI()).SetAuth(options.Credential{\n\t\t\t\t\tUsername: \"fake-user\",\n\t\t\t\t\tPassword: \"fake-password\",\n\t\t\t\t})\n\t\t\t\tintegtest.AddTestServerAPIVersion(externalKvOpts)\n\t\t\t\taeo.SetKeyVaultClientOptions(externalKvOpts)\n\t\t\t\tkvClientOpts = externalKvOpts\n\t\t\t}\n\t\t\tcpt := setup(mt, aeo, kvClientOpts, ceo)\n\t\t\tdefer cpt.teardown(mt)\n\n\t\t\t// manually insert data key\n\t\t\tkey := readJSONFile(mt, \"external-key.json\")\n\t\t\t_, err := cpt.keyVaultColl.InsertOne(context.Background(), key)\n\t\t\tassert.Nil(mt, err, \"InsertOne error for data key: %v\", err)\n\t\t\tsubtype, data := key.Lookup(\"_id\").Binary()\n\t\t\tdataKeyID := bson.Binary{Subtype: subtype, Data: data}\n\n\t\t\tdoc := bson.D{{\"encrypted\", \"test\"}}\n\t\t\t_, insertErr := cpt.cseClient.Database(\"db\").Collection(\"coll\").InsertOne(context.Background(), doc)\n\t\t\trawVal := bson.RawValue{Type: bson.TypeString, Value: bsoncore.AppendString(nil, \"test\")}\n\t\t\t_, encErr := cpt.clientEnc.Encrypt(context.Background(), rawVal,\n\t\t\t\toptions.Encrypt().SetKeyID(dataKeyID).SetAlgorithm(deterministicAlgorithm))\n\n\t\t\tif tc.externalVault {\n\t\t\t\tassert.NotNil(mt, insertErr, \"expected InsertOne auth error, got nil\")\n\t\t\t\tassert.NotNil(mt, encErr, \"expected Encrypt auth error, got nil\")\n\t\t\t\tassert.True(mt, strings.Contains(insertErr.Error(), \"auth error\"),\n\t\t\t\t\t\"expected InsertOne auth error, got %v\", insertErr)\n\t\t\t\tassert.True(mt, strings.Contains(encErr.Error(), \"auth error\"),\n\t\t\t\t\t\"expected Encrypt auth error, got %v\", insertErr)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tassert.Nil(mt, insertErr, \"InsertOne error: %v\", insertErr)\n\t\t\tassert.Nil(mt, encErr, \"Encrypt error: %v\", err)\n\t\t})\n\t}\n}\n\nfunc TestClientSideEncryptionProse_4_bson_size_limits_and_batch_splitting(t *testing.T) {\n\tmt := newCSE_T(t, mtest.NewOptions())\n\tmt.Setup()\n\n\tkmsProviders := map[string]map[string]any{\n\t\t\"local\": {\n\t\t\t\"key\": localMasterKey,\n\t\t},\n\t}\n\taeo := options.AutoEncryption().\n\t\tSetKmsProviders(kmsProviders).\n\t\tSetKeyVaultNamespace(kvNamespace).\n\t\tSetExtraOptions(getCryptSharedLibExtraOptions())\n\tcpt := setup(mt, aeo, nil, nil)\n\tdefer cpt.teardown(mt)\n\n\t// create coll with JSON schema\n\terr := mt.Client.Database(\"db\").RunCommand(context.Background(), bson.D{\n\t\t{\"create\", \"coll\"},\n\t\t{\"validator\", bson.D{\n\t\t\t{\"$jsonSchema\", readJSONFile(mt, \"limits-schema.json\")},\n\t\t}},\n\t}).Err()\n\tassert.Nil(mt, err, \"create error with validator: %v\", err)\n\n\t// insert key\n\tkey := readJSONFile(mt, \"limits-key.json\")\n\t_, err = cpt.keyVaultColl.InsertOne(context.Background(), key)\n\tassert.Nil(mt, err, \"InsertOne error for key: %v\", err)\n\n\tvar builder2mb, builder16mb strings.Builder\n\tfor i := 0; i < cryptMaxBatchSizeBytes; i++ {\n\t\tbuilder2mb.WriteByte('a')\n\t}\n\tfor i := 0; i < maxBsonObjSize; i++ {\n\t\tbuilder16mb.WriteByte('a')\n\t}\n\tcomplete2mbStr := builder2mb.String()\n\tcomplete16mbStr := builder16mb.String()\n\n\t// insert a document over 2MiB\n\tdoc := bson.D{{\"over_2mib_under_16mib\", complete2mbStr}}\n\t_, err = cpt.cseColl.InsertOne(context.Background(), doc)\n\tassert.Nil(mt, err, \"InsertOne error for 2MiB document: %v\", err)\n\n\tstr := complete2mbStr[:cryptMaxBatchSizeBytes-2000] // remove last 2000 bytes\n\tlimitsDoc := readJSONFile(mt, \"limits-doc.json\")\n\n\t// insert a doc smaller than 2MiB that is bigger than 2MiB after encryption\n\tvar extendedLimitsDoc []byte\n\textendedLimitsDoc = append(extendedLimitsDoc, limitsDoc...)\n\textendedLimitsDoc = extendedLimitsDoc[:len(extendedLimitsDoc)-1] // remove last byte to add new fields\n\textendedLimitsDoc = bsoncore.AppendStringElement(extendedLimitsDoc, \"_id\", \"encryption_exceeds_2mib\")\n\textendedLimitsDoc = bsoncore.AppendStringElement(extendedLimitsDoc, \"unencrypted\", str)\n\textendedLimitsDoc, _ = bsoncore.AppendDocumentEnd(extendedLimitsDoc, 0)\n\t_, err = cpt.cseColl.InsertOne(context.Background(), extendedLimitsDoc)\n\tassert.Nil(mt, err, \"error inserting extended limits document: %v\", err)\n\n\t// bulk insert two 2MiB documents, each over 2 MiB\n\t// each document should be split into its own batch because the documents are bigger than 2MiB but smaller\n\t// than 16MiB\n\tcpt.cseStarted = cpt.cseStarted[:0]\n\tfirstDoc := bson.D{{\"_id\", \"over_2mib_1\"}, {\"unencrypted\", complete2mbStr}}\n\tsecondDoc := bson.D{{\"_id\", \"over_2mib_2\"}, {\"unencrypted\", complete2mbStr}}\n\t_, err = cpt.cseColl.InsertMany(context.Background(), []any{firstDoc, secondDoc})\n\tassert.Nil(mt, err, \"InsertMany error for small documents: %v\", err)\n\tassert.Equal(mt, 2, len(cpt.cseStarted), \"expected 2 insert events, got %d\", len(cpt.cseStarted))\n\n\t// bulk insert two documents\n\tstr = complete2mbStr[:cryptMaxBatchSizeBytes-20000]\n\tfirstBulkDoc := make([]byte, len(limitsDoc))\n\tcopy(firstBulkDoc, limitsDoc)\n\tfirstBulkDoc = firstBulkDoc[:len(firstBulkDoc)-1] // remove last byte to append new fields\n\tfirstBulkDoc = bsoncore.AppendStringElement(firstBulkDoc, \"_id\", \"encryption_exceeds_2mib_1\")\n\tfirstBulkDoc = bsoncore.AppendStringElement(firstBulkDoc, \"unencrypted\", string(str))\n\tfirstBulkDoc, _ = bsoncore.AppendDocumentEnd(firstBulkDoc, 0)\n\n\tsecondBulkDoc := make([]byte, len(limitsDoc))\n\tcopy(secondBulkDoc, limitsDoc)\n\tsecondBulkDoc = secondBulkDoc[:len(secondBulkDoc)-1] // remove last byte to append new fields\n\tsecondBulkDoc = bsoncore.AppendStringElement(secondBulkDoc, \"_id\", \"encryption_exceeds_2mib_2\")\n\tsecondBulkDoc = bsoncore.AppendStringElement(secondBulkDoc, \"unencrypted\", string(str))\n\tsecondBulkDoc, _ = bsoncore.AppendDocumentEnd(secondBulkDoc, 0)\n\n\tcpt.cseStarted = cpt.cseStarted[:0]\n\t_, err = cpt.cseColl.InsertMany(context.Background(), []any{firstBulkDoc, secondBulkDoc})\n\tassert.Nil(mt, err, \"InsertMany error for large documents: %v\", err)\n\tassert.Equal(mt, 2, len(cpt.cseStarted), \"expected 2 insert events, got %d\", len(cpt.cseStarted))\n\n\t// insert a document slightly smaller than 16MiB and expect the operation to succeed\n\tdoc = bson.D{{\"_id\", \"under_16mib\"}, {\"unencrypted\", complete16mbStr[:maxBsonObjSize-2000]}}\n\t_, err = cpt.cseColl.InsertOne(context.Background(), doc)\n\tassert.Nil(mt, err, \"InsertOne error: %v\", err)\n\n\t// insert a document over 16MiB and expect the operation to fail\n\tvar over16mb []byte\n\tover16mb = append(over16mb, limitsDoc...)\n\tover16mb = over16mb[:len(over16mb)-1] // remove last byte\n\tover16mb = bsoncore.AppendStringElement(over16mb, \"_id\", \"encryption_exceeds_16mib\")\n\tover16mb = bsoncore.AppendStringElement(over16mb, \"unencrypted\", complete16mbStr[:maxBsonObjSize-2000])\n\tover16mb, _ = bsoncore.AppendDocumentEnd(over16mb, 0)\n\t_, err = cpt.cseColl.InsertOne(context.Background(), over16mb)\n\tassert.NotNil(mt, err, \"expected InsertOne error for document over 16MiB, got nil\")\n}\n\nfunc TestClientSideEncryptionProse_5_views_are_prohibited(t *testing.T) {\n\tt.Parallel()\n\n\tmt := newCSE_T(t, mtest.NewOptions())\n\tmt.Setup()\n\n\tkmsProviders := map[string]map[string]any{\n\t\t\"local\": {\n\t\t\t\"key\": localMasterKey,\n\t\t},\n\t}\n\taeo := options.AutoEncryption().\n\t\tSetKmsProviders(kmsProviders).\n\t\tSetKeyVaultNamespace(kvNamespace).\n\t\tSetExtraOptions(getCryptSharedLibExtraOptions())\n\tcpt := setup(mt, aeo, nil, nil)\n\tdefer cpt.teardown(mt)\n\n\t// create view on db.coll\n\tmt.CreateCollection(mtest.Collection{\n\t\tName:         \"view\",\n\t\tDB:           cpt.cseColl.Database().Name(),\n\t\tViewOn:       \"coll\",\n\t\tViewPipeline: mongo.Pipeline{},\n\t}, true)\n\n\tview := cpt.cseColl.Database().Collection(\"view\")\n\t_, err := view.InsertOne(context.Background(), bson.D{{\"_id\", \"insert_on_view\"}})\n\tassert.NotNil(mt, err, \"expected InsertOne error on view, got nil\")\n\terrStr := strings.ToLower(err.Error())\n\tviewErrSubstr := \"cannot auto encrypt a view\"\n\tassert.True(mt, strings.Contains(errStr, viewErrSubstr),\n\t\t\"expected error '%v' to contain substring '%v'\", errStr, viewErrSubstr)\n}\n\nfunc TestClientSideEncryptionProse_6_corpus_test(t *testing.T) {\n\tmt := newCSE_T(t, newNoClientOpts())\n\tmt.Setup()\n\n\tif \"\" == os.Getenv(\"KMS_MOCK_SERVERS_RUNNING\") {\n\t\tmt.Skipf(\"Skipping test as KMS_MOCK_SERVERS_RUNNING is not set\")\n\t}\n\tcorpusSchema := readJSONFile(mt, \"corpus-schema.json\")\n\tlocalSchemaMap := map[string]any{\n\t\t\"db.coll\": corpusSchema,\n\t}\n\n\tdefaultKvClientOptions := options.Client().ApplyURI(mtest.ClusterURI())\n\n\ttlsConfig := make(map[string]*tls.Config)\n\tif tlsCAFileKMIP != \"\" && tlsClientCertificateKeyFileKMIP != \"\" {\n\t\ttlsOpts := map[string]any{\n\t\t\t\"tlsCertificateKeyFile\": tlsClientCertificateKeyFileKMIP,\n\t\t\t\"tlsCAFile\":             tlsCAFileKMIP,\n\t\t}\n\t\tkmipConfig, err := options.BuildTLSConfig(tlsOpts)\n\t\tassert.Nil(mt, err, \"BuildTLSConfig error: %v\", err)\n\t\ttlsConfig[\"kmip\"] = kmipConfig\n\t}\n\n\tgetBaseAutoEncryptionOpts := func() *options.AutoEncryptionOptions {\n\t\treturn options.AutoEncryption().\n\t\t\tSetKmsProviders(fullKmsProvidersMap).\n\t\t\tSetKeyVaultNamespace(kvNamespace).\n\t\t\tSetTLSConfig(tlsConfig).\n\t\t\tSetExtraOptions(getCryptSharedLibExtraOptions())\n\t}\n\n\ttestCases := []struct {\n\t\tname   string\n\t\taeo    *options.AutoEncryptionOptions\n\t\tschema bson.Raw // the schema to create the collection. if nil, the collection won't be explicitly created\n\t}{\n\t\t{\"remote schema\", getBaseAutoEncryptionOpts(), corpusSchema},\n\t\t{\"local schema\", getBaseAutoEncryptionOpts().SetSchemaMap(localSchemaMap), nil},\n\t}\n\n\tfor _, tc := range testCases {\n\t\tmt.Run(tc.name, func(mt *mtest.T) {\n\t\t\tceo := options.ClientEncryption().\n\t\t\t\tSetKmsProviders(fullKmsProvidersMap).\n\t\t\t\tSetKeyVaultNamespace(kvNamespace).\n\t\t\t\tSetTLSConfig(tlsConfig)\n\n\t\t\tcpt := setup(mt, tc.aeo, defaultKvClientOptions, ceo)\n\t\t\tdefer cpt.teardown(mt)\n\n\t\t\t// create collection with JSON schema\n\t\t\tif tc.schema != nil {\n\t\t\t\tdb := cpt.coll.Database()\n\t\t\t\terr := db.RunCommand(context.Background(), bson.D{\n\t\t\t\t\t{\"create\", \"coll\"},\n\t\t\t\t\t{\"validator\", bson.D{\n\t\t\t\t\t\t{\"$jsonSchema\", readJSONFile(mt, \"corpus-schema.json\")},\n\t\t\t\t\t}},\n\t\t\t\t}).Err()\n\t\t\t\tassert.Nil(mt, err, \"create error with validator: %v\", err)\n\t\t\t}\n\n\t\t\t// Manually insert keys for each KMS provider into the key vault.\n\t\t\t_, err := cpt.keyVaultColl.InsertMany(context.Background(), []any{\n\t\t\t\treadJSONFile(mt, \"corpus-key-local.json\"),\n\t\t\t\treadJSONFile(mt, \"corpus-key-aws.json\"),\n\t\t\t\treadJSONFile(mt, \"corpus-key-azure.json\"),\n\t\t\t\treadJSONFile(mt, \"corpus-key-gcp.json\"),\n\t\t\t\treadJSONFile(mt, \"corpus-key-kmip.json\"),\n\t\t\t})\n\t\t\tassert.Nil(mt, err, \"InsertMany error for key vault: %v\", err)\n\n\t\t\t// read original corpus and recursively copy over each value to new corpus, encrypting certain values\n\t\t\t// when needed\n\t\t\tcorpus := readJSONFile(mt, \"corpus.json\")\n\t\t\tcidx, copied := bsoncore.AppendDocumentStart(nil)\n\t\t\telems, _ := corpus.Elements()\n\n\t\t\t// Keys for top-level non-document elements that should be copied directly.\n\t\t\tcopiedKeys := map[string]struct{}{\n\t\t\t\t\"_id\":           {},\n\t\t\t\t\"altname_aws\":   {},\n\t\t\t\t\"altname_local\": {},\n\t\t\t\t\"altname_azure\": {},\n\t\t\t\t\"altname_gcp\":   {},\n\t\t\t\t\"altname_kmip\":  {},\n\t\t\t}\n\n\t\t\tfor _, elem := range elems {\n\t\t\t\tkey := elem.Key()\n\t\t\t\tval := elem.Value()\n\n\t\t\t\tif _, ok := copiedKeys[key]; ok {\n\t\t\t\t\tcopied = bsoncore.AppendStringElement(copied, key, val.StringValue())\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\tdoc := val.Document()\n\t\t\t\tswitch method := doc.Lookup(\"method\").StringValue(); method {\n\t\t\t\tcase \"auto\":\n\t\t\t\t\t// Copy the value directly because it will be auto-encrypted later.\n\t\t\t\t\tcopied = bsoncore.AppendDocumentElement(copied, key, doc)\n\t\t\t\t\tcontinue\n\t\t\t\tcase \"explicit\":\n\t\t\t\t\t// Handled below.\n\t\t\t\tdefault:\n\t\t\t\t\tmt.Fatalf(\"unrecognized 'method' value %q\", method)\n\t\t\t\t}\n\n\t\t\t\t// explicitly encrypt value\n\t\t\t\talgorithm := deterministicAlgorithm\n\t\t\t\tif doc.Lookup(\"algo\").StringValue() == \"rand\" {\n\t\t\t\t\talgorithm = randomAlgorithm\n\t\t\t\t}\n\t\t\t\teo := options.Encrypt().SetAlgorithm(algorithm)\n\n\t\t\t\tidentifier := doc.Lookup(\"identifier\").StringValue()\n\t\t\t\tkms := doc.Lookup(\"kms\").StringValue()\n\t\t\t\tswitch identifier {\n\t\t\t\tcase \"id\":\n\t\t\t\t\tvar keyID string\n\t\t\t\t\tswitch kms {\n\t\t\t\t\tcase \"local\":\n\t\t\t\t\t\tkeyID = \"LOCALAAAAAAAAAAAAAAAAA==\"\n\t\t\t\t\tcase \"aws\":\n\t\t\t\t\t\tkeyID = \"AWSAAAAAAAAAAAAAAAAAAA==\"\n\t\t\t\t\tcase \"azure\":\n\t\t\t\t\t\tkeyID = \"AZUREAAAAAAAAAAAAAAAAA==\"\n\t\t\t\t\tcase \"gcp\":\n\t\t\t\t\t\tkeyID = \"GCPAAAAAAAAAAAAAAAAAAA==\"\n\t\t\t\t\tcase \"kmip\":\n\t\t\t\t\t\tkeyID = \"KMIPAAAAAAAAAAAAAAAAAA==\"\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tmt.Fatalf(\"unrecognized KMS provider %q\", kms)\n\t\t\t\t\t}\n\n\t\t\t\t\tkeyIDBytes, err := base64.StdEncoding.DecodeString(keyID)\n\t\t\t\t\tassert.Nil(mt, err, \"base64 DecodeString error: %v\", err)\n\t\t\t\t\teo.SetKeyID(bson.Binary{Subtype: 4, Data: keyIDBytes})\n\t\t\t\tcase \"altname\":\n\t\t\t\t\teo.SetKeyAltName(kms) // alt name for a key is the same as the KMS name\n\t\t\t\tdefault:\n\t\t\t\t\tmt.Fatalf(\"unrecognized identifier: %v\", identifier)\n\t\t\t\t}\n\n\t\t\t\t// iterate over all elements in the document. copy elements directly, except for ones that need to\n\t\t\t\t// be encrypted, which should be copied after encryption.\n\t\t\t\tvar nestedIdx int32\n\t\t\t\tnestedIdx, copied = bsoncore.AppendDocumentElementStart(copied, key)\n\t\t\t\tdocElems, _ := doc.Elements()\n\t\t\t\tfor _, de := range docElems {\n\t\t\t\t\tdeKey := de.Key()\n\t\t\t\t\tdeVal := de.Value()\n\n\t\t\t\t\t// element to encrypt has key \"value\"\n\t\t\t\t\tif deKey != \"value\" {\n\t\t\t\t\t\tcopied = bsoncore.AppendValueElement(copied, deKey, rawValueToCoreValue(deVal))\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\n\t\t\t\t\tencrypted, err := cpt.clientEnc.Encrypt(context.Background(), deVal, eo)\n\t\t\t\t\tif !doc.Lookup(\"allowed\").Boolean() {\n\t\t\t\t\t\t// if allowed is false, encryption should error. in this case, the unencrypted value should be\n\t\t\t\t\t\t// copied over\n\t\t\t\t\t\tassert.NotNil(mt, err, \"expected error encrypting value for key %v, got nil\", key)\n\t\t\t\t\t\tcopied = bsoncore.AppendValueElement(copied, deKey, rawValueToCoreValue(deVal))\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\n\t\t\t\t\t// copy encrypted value\n\t\t\t\t\tassert.Nil(mt, err, \"Encrypt error for key %v: %v\", key, err)\n\t\t\t\t\tcopied = bsoncore.AppendBinaryElement(copied, deKey, encrypted.Subtype, encrypted.Data)\n\t\t\t\t}\n\t\t\t\tcopied, _ = bsoncore.AppendDocumentEnd(copied, nestedIdx)\n\t\t\t}\n\t\t\tcopied, _ = bsoncore.AppendDocumentEnd(copied, cidx)\n\n\t\t\t// insert document with encrypted values\n\t\t\t_, err = cpt.cseColl.InsertOne(context.Background(), copied)\n\t\t\tassert.Nil(mt, err, \"InsertOne error for corpus document: %v\", err)\n\n\t\t\t// find document using client with encryption and assert it matches original\n\t\t\tdecryptedDoc, err := cpt.cseColl.FindOne(context.Background(), bson.D{}).Raw()\n\t\t\tassert.Nil(mt, err, \"Find error with encrypted client: %v\", err)\n\t\t\tassert.Equal(mt, corpus, decryptedDoc, \"expected document %v, got %v\", corpus, decryptedDoc)\n\n\t\t\t// find document using a client without encryption enabled and assert fields remain encrypted\n\t\t\tcorpusEncrypted := readJSONFile(mt, \"corpus-encrypted.json\")\n\t\t\tfoundDoc, err := cpt.coll.FindOne(context.Background(), bson.D{}).Raw()\n\t\t\tassert.Nil(mt, err, \"Find error with unencrypted client: %v\", err)\n\n\t\t\tencryptedElems, _ := corpusEncrypted.Elements()\n\t\t\tfor _, encryptedElem := range encryptedElems {\n\t\t\t\t// skip non-document fields\n\t\t\t\tencryptedDoc, ok := encryptedElem.Value().DocumentOK()\n\t\t\t\tif !ok {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\tallowed := encryptedDoc.Lookup(\"allowed\").Boolean()\n\t\t\t\texpectedKey := encryptedElem.Key()\n\t\t\t\texpectedVal := encryptedDoc.Lookup(\"value\")\n\t\t\t\tfoundVal := foundDoc.Lookup(expectedKey).Document().Lookup(\"value\")\n\n\t\t\t\t// for deterministic encryption, the value should be exactly equal\n\t\t\t\t// for random encryption, the value should not be equal if allowed is true\n\t\t\t\talgo := encryptedDoc.Lookup(\"algo\").StringValue()\n\t\t\t\tswitch algo {\n\t\t\t\tcase \"det\":\n\t\t\t\t\tassert.True(mt, expectedVal.Equal(foundVal),\n\t\t\t\t\t\t\"expected value %v for key %v, got %v\", expectedVal, expectedKey, foundVal)\n\t\t\t\tcase \"rand\":\n\t\t\t\t\tif allowed {\n\t\t\t\t\t\tassert.False(mt, expectedVal.Equal(foundVal),\n\t\t\t\t\t\t\t\"expected values for key %v to be different but were %v\", expectedKey, expectedVal)\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// if allowed is true, decrypt both values with clientEnc and validate equality\n\t\t\t\tif allowed {\n\t\t\t\t\tsub, data := expectedVal.Binary()\n\t\t\t\t\texpectedDecrypted, err := cpt.clientEnc.Decrypt(context.Background(), bson.Binary{Subtype: sub, Data: data})\n\t\t\t\t\tassert.Nil(mt, err, \"Decrypt error: %v\", err)\n\t\t\t\t\tsub, data = foundVal.Binary()\n\t\t\t\t\tactualDecrypted, err := cpt.clientEnc.Decrypt(context.Background(), bson.Binary{Subtype: sub, Data: data})\n\t\t\t\t\tassert.Nil(mt, err, \"Decrypt error: %v\", err)\n\n\t\t\t\t\tassert.True(mt, expectedDecrypted.Equal(actualDecrypted),\n\t\t\t\t\t\t\"expected decrypted value %v for key %v, got %v\", expectedDecrypted, expectedKey, actualDecrypted)\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\t// if allowed is false, validate found value equals the original value in corpus\n\t\t\t\tcorpusVal := corpus.Lookup(expectedKey).Document().Lookup(\"value\")\n\t\t\t\tassert.True(mt, corpusVal.Equal(foundVal),\n\t\t\t\t\t\"expected value %v for key %v, got %v\", corpusVal, expectedKey, foundVal)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestClientSideEncryptionProse_7_custom_endpoint_test(t *testing.T) {\n\tmt := newCSE_T(t, mtest.NewOptions())\n\tmt.Setup()\n\n\tdefaultKvClientOptions := options.Client().ApplyURI(mtest.ClusterURI())\n\n\tvalidKmsProviders := map[string]map[string]any{\n\t\t\"aws\": {\n\t\t\t\"accessKeyId\":     awsAccessKeyID,\n\t\t\t\"secretAccessKey\": awsSecretAccessKey,\n\t\t},\n\t\t\"azure\": {\n\t\t\t\"tenantId\":                 azureTenantID,\n\t\t\t\"clientId\":                 azureClientID,\n\t\t\t\"clientSecret\":             azureClientSecret,\n\t\t\t\"identityPlatformEndpoint\": \"login.microsoftonline.com:443\",\n\t\t},\n\t\t\"gcp\": {\n\t\t\t\"email\":      gcpEmail,\n\t\t\t\"privateKey\": gcpPrivateKey,\n\t\t\t\"endpoint\":   \"oauth2.googleapis.com:443\",\n\t\t},\n\t\t\"kmip\": {\n\t\t\t\"endpoint\": \"localhost:5698\",\n\t\t},\n\t}\n\n\ttlsConfig := make(map[string]*tls.Config)\n\tif tlsCAFileKMIP != \"\" && tlsClientCertificateKeyFileKMIP != \"\" {\n\t\ttlsOpts := map[string]any{\n\t\t\t\"tlsCertificateKeyFile\": tlsClientCertificateKeyFileKMIP,\n\t\t\t\"tlsCAFile\":             tlsCAFileKMIP,\n\t\t}\n\t\tkmipConfig, err := options.BuildTLSConfig(tlsOpts)\n\t\tassert.Nil(mt, err, \"BuildTLSConfig error: %v\", err)\n\t\ttlsConfig[\"kmip\"] = kmipConfig\n\t}\n\n\tvalidClientEncryptionOptions := options.ClientEncryption().\n\t\tSetKmsProviders(validKmsProviders).\n\t\tSetKeyVaultNamespace(kvNamespace).\n\t\tSetTLSConfig(tlsConfig)\n\n\tinvalidKmsProviders := map[string]map[string]any{\n\t\t\"azure\": {\n\t\t\t\"tenantId\":                 azureTenantID,\n\t\t\t\"clientId\":                 azureClientID,\n\t\t\t\"clientSecret\":             azureClientSecret,\n\t\t\t\"identityPlatformEndpoint\": \"doesnotexist.invalid:443\",\n\t\t},\n\t\t\"gcp\": {\n\t\t\t\"email\":      gcpEmail,\n\t\t\t\"privateKey\": gcpPrivateKey,\n\t\t\t\"endpoint\":   \"doesnotexist.invalid:443\",\n\t\t},\n\t\t\"kmip\": {\n\t\t\t\"endpoint\": \"doesnotexist.invalid:5698\",\n\t\t},\n\t}\n\n\tinvalidClientEncryptionOptions := options.ClientEncryption().\n\t\tSetKmsProviders(invalidKmsProviders).\n\t\tSetKeyVaultNamespace(kvNamespace).\n\t\tSetTLSConfig(tlsConfig)\n\n\tawsSuccessWithoutEndpoint := map[string]any{\n\t\t\"region\": \"us-east-1\",\n\t\t\"key\":    \"arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0\",\n\t}\n\tawsSuccessWithEndpoint := map[string]any{\n\t\t\"region\":   \"us-east-1\",\n\t\t\"key\":      \"arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0\",\n\t\t\"endpoint\": \"kms.us-east-1.amazonaws.com\",\n\t}\n\tawsSuccessWithHTTPSEndpoint := map[string]any{\n\t\t\"region\":   \"us-east-1\",\n\t\t\"key\":      \"arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0\",\n\t\t\"endpoint\": \"kms.us-east-1.amazonaws.com:443\",\n\t}\n\tawsFailureConnectionError := map[string]any{\n\t\t\"keyId\":    \"1\",\n\t\t\"endpoint\": \"localhost:12345\",\n\t}\n\tawsFailureInvalidEndpoint := map[string]any{\n\t\t\"region\":   \"us-east-1\",\n\t\t\"key\":      \"arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0\",\n\t\t\"endpoint\": \"kms.us-east-2.amazonaws.com\",\n\t}\n\tawsFailureParseError := map[string]any{\n\t\t\"region\":   \"us-east-1\",\n\t\t\"key\":      \"arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0\",\n\t\t\"endpoint\": \"doesnotexist.invalid\",\n\t}\n\tazure := map[string]any{\n\t\t\"keyVaultEndpoint\": \"key-vault-csfle.vault.azure.net\",\n\t\t\"keyName\":          \"key-name-csfle\",\n\t}\n\tgcpSuccess := map[string]any{\n\t\t\"projectId\": \"devprod-drivers\",\n\t\t\"location\":  \"global\",\n\t\t\"keyRing\":   \"key-ring-csfle\",\n\t\t\"keyName\":   \"key-name-csfle\",\n\t\t\"endpoint\":  \"cloudkms.googleapis.com:443\",\n\t}\n\tgcpFailure := map[string]any{\n\t\t\"projectId\": \"devprod-drivers\",\n\t\t\"location\":  \"global\",\n\t\t\"keyRing\":   \"key-ring-csfle\",\n\t\t\"keyName\":   \"key-name-csfle\",\n\t\t\"endpoint\":  \"doesnotexist.invalid:443\",\n\t}\n\tkmipSuccessWithoutEndpoint := map[string]any{\n\t\t\"keyId\": \"1\",\n\t}\n\tkmipSuccessWithEndpoint := map[string]any{\n\t\t\"keyId\":    \"1\",\n\t\t\"endpoint\": \"localhost:5698\",\n\t}\n\tkmipFailureInvalidEndpoint := map[string]any{\n\t\t\"keyId\":    \"1\",\n\t\t\"endpoint\": \"doesnotexist.invalid:5698\",\n\t}\n\n\tconst (\n\t\terrConnectionRefused           = \"connection refused\"\n\t\terrInvalidKMSResponse          = \"Invalid KMS response\"\n\t\terrMongocryptError             = \"mongocrypt error\"\n\t\terrNoSuchHost                  = \"no such host\"\n\t\terrServerMisbehaving           = \"server misbehaving\"\n\t\terrWindowsTLSConnectionRefused = \"No connection could be made because the target machine actively refused it\"\n\t)\n\n\ttestCases := []struct {\n\t\tname                                  string\n\t\tprovider                              string\n\t\tmasterKey                             any\n\t\terrorSubstring                        []string\n\t\ttestInvalidClientEncryption           bool\n\t\tinvalidClientEncryptionErrorSubstring []string\n\t}{\n\t\t{\n\t\t\tname:                                  \"Case 1: aws success without endpoint\",\n\t\t\tprovider:                              \"aws\",\n\t\t\tmasterKey:                             awsSuccessWithoutEndpoint,\n\t\t\terrorSubstring:                        []string{},\n\t\t\ttestInvalidClientEncryption:           false,\n\t\t\tinvalidClientEncryptionErrorSubstring: []string{},\n\t\t},\n\t\t{\n\t\t\tname:                                  \"Case 2: aws success with endpoint\",\n\t\t\tprovider:                              \"aws\",\n\t\t\tmasterKey:                             awsSuccessWithEndpoint,\n\t\t\terrorSubstring:                        []string{},\n\t\t\ttestInvalidClientEncryption:           false,\n\t\t\tinvalidClientEncryptionErrorSubstring: []string{},\n\t\t},\n\t\t{\n\t\t\tname:                                  \"Case 3: aws success with https endpoint\",\n\t\t\tprovider:                              \"aws\",\n\t\t\tmasterKey:                             awsSuccessWithHTTPSEndpoint,\n\t\t\terrorSubstring:                        []string{},\n\t\t\ttestInvalidClientEncryption:           false,\n\t\t\tinvalidClientEncryptionErrorSubstring: []string{},\n\t\t},\n\t\t{\n\t\t\tname:                                  \"Case 4: aws failure with connection error\",\n\t\t\tprovider:                              \"kmip\",\n\t\t\tmasterKey:                             awsFailureConnectionError,\n\t\t\terrorSubstring:                        []string{errConnectionRefused, errWindowsTLSConnectionRefused},\n\t\t\ttestInvalidClientEncryption:           false,\n\t\t\tinvalidClientEncryptionErrorSubstring: []string{},\n\t\t},\n\t\t{\n\t\t\tname:                                  \"Case 5: aws failure with wrong endpoint\",\n\t\t\tprovider:                              \"aws\",\n\t\t\tmasterKey:                             awsFailureInvalidEndpoint,\n\t\t\terrorSubstring:                        []string{errMongocryptError},\n\t\t\ttestInvalidClientEncryption:           false,\n\t\t\tinvalidClientEncryptionErrorSubstring: []string{},\n\t\t},\n\t\t{\n\t\t\tname:                                  \"Case 6: aws failure with parse error\",\n\t\t\tprovider:                              \"aws\",\n\t\t\tmasterKey:                             awsFailureParseError,\n\t\t\terrorSubstring:                        []string{errNoSuchHost, errServerMisbehaving},\n\t\t\ttestInvalidClientEncryption:           false,\n\t\t\tinvalidClientEncryptionErrorSubstring: []string{},\n\t\t},\n\t\t{\n\t\t\tname:                                  \"Case 7: azure success\",\n\t\t\tprovider:                              \"azure\",\n\t\t\tmasterKey:                             azure,\n\t\t\terrorSubstring:                        []string{},\n\t\t\ttestInvalidClientEncryption:           true,\n\t\t\tinvalidClientEncryptionErrorSubstring: []string{errNoSuchHost, errServerMisbehaving},\n\t\t},\n\t\t{\n\t\t\tname:                                  \"Case 8: gcp success\",\n\t\t\tprovider:                              \"gcp\",\n\t\t\tmasterKey:                             gcpSuccess,\n\t\t\terrorSubstring:                        []string{},\n\t\t\ttestInvalidClientEncryption:           true,\n\t\t\tinvalidClientEncryptionErrorSubstring: []string{errNoSuchHost, errServerMisbehaving},\n\t\t},\n\t\t{\n\t\t\tname:                                  \"Case 9: gcp failure\",\n\t\t\tprovider:                              \"gcp\",\n\t\t\tmasterKey:                             gcpFailure,\n\t\t\terrorSubstring:                        []string{errInvalidKMSResponse},\n\t\t\ttestInvalidClientEncryption:           false,\n\t\t\tinvalidClientEncryptionErrorSubstring: []string{},\n\t\t},\n\t\t{\n\t\t\tname:                                  \"Case 10: kmip success without endpoint\",\n\t\t\tprovider:                              \"kmip\",\n\t\t\tmasterKey:                             kmipSuccessWithoutEndpoint,\n\t\t\terrorSubstring:                        []string{},\n\t\t\ttestInvalidClientEncryption:           true,\n\t\t\tinvalidClientEncryptionErrorSubstring: []string{errNoSuchHost, errServerMisbehaving},\n\t\t},\n\t\t{\n\t\t\tname:                                  \"Case 11: kmip success with endpoint\",\n\t\t\tprovider:                              \"kmip\",\n\t\t\tmasterKey:                             kmipSuccessWithEndpoint,\n\t\t\terrorSubstring:                        []string{},\n\t\t\ttestInvalidClientEncryption:           false,\n\t\t\tinvalidClientEncryptionErrorSubstring: []string{},\n\t\t},\n\t\t{\n\t\t\tname:                                  \"Case 12: kmip failure with invalid endpoint\",\n\t\t\tprovider:                              \"kmip\",\n\t\t\tmasterKey:                             kmipFailureInvalidEndpoint,\n\t\t\terrorSubstring:                        []string{errNoSuchHost, errServerMisbehaving},\n\t\t\ttestInvalidClientEncryption:           false,\n\t\t\tinvalidClientEncryptionErrorSubstring: []string{},\n\t\t},\n\t}\n\tfor _, tc := range testCases {\n\t\tmt.Run(tc.name, func(mt *mtest.T) {\n\t\t\tif strings.Contains(tc.name, \"kmip\") && \"\" == os.Getenv(\"KMS_MOCK_SERVERS_RUNNING\") {\n\t\t\t\tmt.Skipf(\"Skipping test as KMS_MOCK_SERVERS_RUNNING is not set\")\n\t\t\t}\n\t\t\tcpt := setup(mt, nil, defaultKvClientOptions, validClientEncryptionOptions)\n\t\t\tdefer cpt.teardown(mt)\n\n\t\t\tdkOpts := options.DataKey().SetMasterKey(tc.masterKey)\n\t\t\tcreatedKey, err := cpt.clientEnc.CreateDataKey(context.Background(), tc.provider, dkOpts)\n\t\t\tif len(tc.errorSubstring) > 0 {\n\t\t\t\tassert.NotNil(mt, err, \"expected error, got nil\")\n\n\t\t\t\tassert.True(mt, containsPattern(tc.errorSubstring, err.Error()),\n\t\t\t\t\t\"expected tc.errorSubstring=%v to contain %v, but it didn't\", tc.errorSubstring, err.Error())\n\n\t\t\t\treturn\n\t\t\t}\n\t\t\tassert.Nil(mt, err, \"CreateDataKey error: %v\", err)\n\n\t\t\tencOpts := options.Encrypt().SetKeyID(createdKey).SetAlgorithm(deterministicAlgorithm)\n\t\t\ttestVal := bson.RawValue{\n\t\t\t\tType:  bson.TypeString,\n\t\t\t\tValue: bsoncore.AppendString(nil, \"test\"),\n\t\t\t}\n\t\t\tencrypted, err := cpt.clientEnc.Encrypt(context.Background(), testVal, encOpts)\n\t\t\tassert.Nil(mt, err, \"Encrypt error: %v\", err)\n\t\t\tdecrypted, err := cpt.clientEnc.Decrypt(context.Background(), encrypted)\n\t\t\tassert.Nil(mt, err, \"Decrypt error: %v\", err)\n\t\t\tassert.Equal(mt, testVal, decrypted, \"expected value %s, got %s\", testVal, decrypted)\n\n\t\t\tif !tc.testInvalidClientEncryption {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tinvalidClientEncryption, err := mongo.NewClientEncryption(cpt.kvClient, invalidClientEncryptionOptions)\n\t\t\tassert.Nil(mt, err, \"error creating invalidClientEncryption object: %v\", err)\n\t\t\tdefer invalidClientEncryption.Close(context.Background())\n\n\t\t\tinvalidKeyOpts := options.DataKey().SetMasterKey(tc.masterKey)\n\t\t\t_, err = invalidClientEncryption.CreateDataKey(context.Background(), tc.provider, invalidKeyOpts)\n\t\t\tassert.NotNil(mt, err, \"expected CreateDataKey error, got nil\")\n\n\t\t\tassert.True(mt, containsPattern(tc.invalidClientEncryptionErrorSubstring, err.Error()),\n\t\t\t\t\"expected tc.invalidClientEncryptionErrorSubstring=%v to contain %v, but it didn't\",\n\t\t\t\ttc.invalidClientEncryptionErrorSubstring, err.Error())\n\t\t})\n\t}\n}\n\nfunc TestClientSideEncryptionProse_8_bypass_spawning_mongocryptd(t *testing.T) {\n\tt.Parallel()\n\n\tmt := newCSE_T(t, newNoClientOpts())\n\tmt.Setup()\n\n\tkmsProviders := map[string]map[string]any{\n\t\t\"local\": {\n\t\t\t\"key\": localMasterKey,\n\t\t},\n\t}\n\tschemaMap := map[string]any{\n\t\t\"db.coll\": readJSONFile(mt, \"external-schema.json\"),\n\t}\n\n\t// All mongocryptd options use port 27021 instead of the default 27020 to avoid interference\n\t// with mongocryptd instances spawned by previous tests. Explicitly disable loading the\n\t// crypt_shared library to make sure we're testing mongocryptd spawning behavior that is not\n\t// influenced by loading the crypt_shared library.\n\tmongocryptdBypassSpawnTrue := map[string]any{\n\t\t\"mongocryptdBypassSpawn\":              true,\n\t\t\"mongocryptdURI\":                      \"mongodb://localhost:27021/db?serverSelectionTimeoutMS=1000\",\n\t\t\"mongocryptdSpawnArgs\":                []string{\"--pidfilepath=bypass-spawning-mongocryptd.pid\", \"--port=27021\"},\n\t\t\"__cryptSharedLibDisabledForTestOnly\": true, // Disable loading the crypt_shared library.\n\t}\n\tmongocryptdBypassSpawnFalse := map[string]any{\n\t\t\"mongocryptdBypassSpawn\":              false,\n\t\t\"mongocryptdSpawnArgs\":                []string{\"--pidfilepath=bypass-spawning-mongocryptd.pid\", \"--port=27021\"},\n\t\t\"__cryptSharedLibDisabledForTestOnly\": true, // Disable loading the crypt_shared library.\n\t}\n\tmongocryptdBypassSpawnNotSet := map[string]any{\n\t\t\"mongocryptdSpawnArgs\":                []string{\"--pidfilepath=bypass-spawning-mongocryptd.pid\", \"--port=27021\"},\n\t\t\"__cryptSharedLibDisabledForTestOnly\": true, // Disable loading the crypt_shared library.\n\t}\n\n\ttestCases := []struct {\n\t\tname                    string\n\t\tmongocryptdOpts         map[string]any\n\t\tsetBypassAutoEncryption bool\n\t\tbypassAutoEncryption    bool\n\t\tbypassQueryAnalysis     bool\n\t\tuseSharedLib            bool\n\t}{\n\t\t{\n\t\t\tname:            \"mongocryptdBypassSpawn only\",\n\t\t\tmongocryptdOpts: mongocryptdBypassSpawnTrue,\n\t\t},\n\t\t{\n\t\t\tname:                    \"bypassAutoEncryption only\",\n\t\t\tmongocryptdOpts:         mongocryptdBypassSpawnNotSet,\n\t\t\tsetBypassAutoEncryption: true,\n\t\t\tbypassAutoEncryption:    true,\n\t\t},\n\t\t{\n\t\t\tname:                    \"mongocryptdBypassSpawn false, bypassAutoEncryption true\",\n\t\t\tmongocryptdOpts:         mongocryptdBypassSpawnFalse,\n\t\t\tsetBypassAutoEncryption: true,\n\t\t\tbypassAutoEncryption:    true,\n\t\t},\n\t\t{\n\t\t\tname:                    \"mongocryptdBypassSpawn true, bypassAutoEncryption false\",\n\t\t\tmongocryptdOpts:         mongocryptdBypassSpawnTrue,\n\t\t\tsetBypassAutoEncryption: true,\n\t\t\tbypassAutoEncryption:    false,\n\t\t},\n\t\t{\n\t\t\tname:                \"bypassQueryAnalysis only\",\n\t\t\tmongocryptdOpts:     mongocryptdBypassSpawnNotSet,\n\t\t\tbypassQueryAnalysis: true,\n\t\t},\n\t\t{\n\t\t\tname:         \"use shared library\",\n\t\t\tuseSharedLib: true,\n\t\t\tmongocryptdOpts: map[string]any{\n\t\t\t\t\"mongocryptdURI\":       \"mongodb://localhost:27021/db?serverSelectionTimeoutMS=1000\",\n\t\t\t\t\"mongocryptdSpawnArgs\": []string{\"--pidfilepath=bypass-spawning-mongocryptd.pid\", \"--port=27021\"},\n\t\t\t\t\"cryptSharedLibPath\":   os.Getenv(\"CRYPT_SHARED_LIB_PATH\"),\n\t\t\t\t\"cryptSharedRequired\":  true,\n\t\t\t},\n\t\t},\n\t}\n\tfor _, tc := range testCases {\n\t\tmt.Run(tc.name, func(mt *mtest.T) {\n\t\t\tif tc.useSharedLib && os.Getenv(\"CRYPT_SHARED_LIB_PATH\") == \"\" {\n\t\t\t\tmt.Skip(\"CRYPT_SHARED_LIB_PATH not set, skipping\")\n\t\t\t\treturn\n\t\t\t}\n\t\t\taeo := options.AutoEncryption().\n\t\t\t\tSetKmsProviders(kmsProviders).\n\t\t\t\tSetKeyVaultNamespace(kvNamespace).\n\t\t\t\tSetSchemaMap(schemaMap).\n\t\t\t\tSetExtraOptions(tc.mongocryptdOpts)\n\t\t\tif tc.setBypassAutoEncryption {\n\t\t\t\taeo.SetBypassAutoEncryption(tc.bypassAutoEncryption)\n\t\t\t}\n\t\t\taeo.SetBypassQueryAnalysis(tc.bypassQueryAnalysis)\n\t\t\tcpt := setup(mt, aeo, nil, nil)\n\t\t\tdefer cpt.teardown(mt)\n\n\t\t\t_, err := cpt.cseColl.InsertOne(context.Background(), bson.D{{\"unencrypted\", \"test\"}})\n\n\t\t\t// Check for mongocryptd server selection error if auto encryption needed mongocryptd.\n\t\t\tif !(tc.setBypassAutoEncryption && tc.bypassAutoEncryption) && !tc.bypassQueryAnalysis && !tc.useSharedLib {\n\t\t\t\tassert.NotNil(mt, err, \"expected InsertOne error, got nil\")\n\t\t\t\tmcryptErr, ok := err.(mongo.MongocryptdError)\n\t\t\t\tassert.True(mt, ok, \"expected error type %T, got %v of type %T\", mongo.MongocryptdError{}, err, err)\n\t\t\t\tassert.True(mt, strings.Contains(mcryptErr.Error(), \"server selection error\"),\n\t\t\t\t\t\"expected mongocryptd server selection error, got %v\", err)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\t// If mongocryptd was not needed, the command should succeed. Create a new client to connect to\n\t\t\t// mongocryptd and verify it is not running.\n\t\t\tassert.Nil(mt, err, \"InsertOne error: %v\", err)\n\n\t\t\tmcryptOpts := options.Client().ApplyURI(\"mongodb://localhost:27021\").\n\t\t\t\tSetServerSelectionTimeout(1 * time.Second)\n\t\t\tintegtest.AddTestServerAPIVersion(mcryptOpts)\n\t\t\tmcryptClient, err := mongo.Connect(mcryptOpts)\n\t\t\tassert.Nil(mt, err, \"mongocryptd Connect error: %v\", err)\n\n\t\t\terr = mcryptClient.Database(\"admin\").RunCommand(context.Background(), bson.D{{handshake.LegacyHelloLowercase, 1}}).Err()\n\t\t\tassert.NotNil(mt, err, \"expected mongocryptd legacy hello error, got nil\")\n\t\t\tassert.True(mt, strings.Contains(err.Error(), \"server selection error\"),\n\t\t\t\t\"expected mongocryptd server selection error, got %v\", err)\n\t\t})\n\t}\n}\n\nfunc TestClientSideEncryptionProse_9_deadlock_tests(t *testing.T) {\n\tmt := newCSE_T(t, newNoClientOpts())\n\tmt.Setup()\n\n\ttestcases := []struct {\n\t\tdescription                            string\n\t\tmaxPoolSize                            uint64\n\t\tbypassAutoEncryption                   bool\n\t\tkeyVaultClientSet                      bool\n\t\tclientEncryptedTopologyOpeningExpected int\n\t\tclientEncryptedCommandStartedExpected  []startedEvent\n\t\tclientKeyVaultCommandStartedExpected   []startedEvent\n\t}{\n\t\t// In the following comments, \"special auto encryption options\" refers to the \"bypassAutoEncryption\" and\n\t\t// \"keyVaultClient\" options\n\t\t{\n\t\t\t// If the client has a limited maxPoolSize, and no special auto-encryption options are set, the\n\t\t\t// driver should create an internal Client for metadata/keyVault operations.\n\t\t\t\"deadlock case 1\", 1, false, false, 2,\n\t\t\t[]startedEvent{{\"listCollections\", \"db\"}, {\"find\", \"keyvault\"}, {\"insert\", \"db\"}, {\"find\", \"db\"}},\n\t\t\tnil,\n\t\t},\n\t\t{\n\t\t\t// If the client has a limited maxPoolSize, and a keyVaultClient is set, the driver should create\n\t\t\t// an internal Client for metadata operations.\n\t\t\t\"deadlock case 2\", 1, false, true, 2,\n\t\t\t[]startedEvent{{\"listCollections\", \"db\"}, {\"insert\", \"db\"}, {\"find\", \"db\"}},\n\t\t\t[]startedEvent{{\"find\", \"keyvault\"}},\n\t\t},\n\t\t{\n\t\t\t// If the client has a limited maxPoolSize, and a bypassAutomaticEncryption=true, the driver should\n\t\t\t// create an internal Client for keyVault operations.\n\t\t\t\"deadlock case 3\", 1, true, false, 2,\n\t\t\t[]startedEvent{{\"find\", \"db\"}, {\"find\", \"keyvault\"}},\n\t\t\tnil,\n\t\t},\n\t\t{\n\t\t\t// If the client has a limited maxPoolSize, bypassAutomaticEncryption=true, and a keyVaultClient is set,\n\t\t\t// the driver should not create an internal Client.\n\t\t\t\"deadlock case 4\", 1, true, true, 1,\n\t\t\t[]startedEvent{{\"find\", \"db\"}},\n\t\t\t[]startedEvent{{\"find\", \"keyvault\"}},\n\t\t},\n\t\t{\n\t\t\t// If the client has an unlimited maxPoolSize, and no special auto-encryption options are set,  the\n\t\t\t// driver should reuse the client for metadata/keyVault operations\n\t\t\t\"deadlock case 5\", 0, false, false, 1,\n\t\t\t[]startedEvent{{\"listCollections\", \"db\"}, {\"listCollections\", \"keyvault\"}, {\"find\", \"keyvault\"}, {\"insert\", \"db\"}, {\"find\", \"db\"}},\n\t\t\tnil,\n\t\t},\n\t\t{\n\t\t\t// If the client has an unlimited maxPoolSize, and a keyVaultClient is set, the driver should reuse the\n\t\t\t// client for metadata operations.\n\t\t\t\"deadlock case 6\", 0, false, true, 1,\n\t\t\t[]startedEvent{{\"listCollections\", \"db\"}, {\"insert\", \"db\"}, {\"find\", \"db\"}},\n\t\t\t[]startedEvent{{\"find\", \"keyvault\"}},\n\t\t},\n\t\t{\n\t\t\t// If the client has an unlimited maxPoolSize, and bypassAutomaticEncryption=true, the driver should\n\t\t\t// reuse the client for keyVault operations\n\t\t\t\"deadlock case 7\", 0, true, false, 1,\n\t\t\t[]startedEvent{{\"find\", \"db\"}, {\"find\", \"keyvault\"}},\n\t\t\tnil,\n\t\t},\n\t\t{\n\t\t\t// If the client has an unlimited maxPoolSize, bypassAutomaticEncryption=true, and a keyVaultClient is\n\t\t\t// set, the driver should not create an internal Client.\n\t\t\t\"deadlock case 8\", 0, true, true, 1,\n\t\t\t[]startedEvent{{\"find\", \"db\"}},\n\t\t\t[]startedEvent{{\"find\", \"keyvault\"}},\n\t\t},\n\t}\n\n\tfor _, tc := range testcases {\n\t\tmt.Run(tc.description, func(mt *mtest.T) {\n\t\t\tvar clientEncryptedEvents []startedEvent\n\t\t\tvar clientEncryptedTopologyOpening int\n\n\t\t\td := newDeadlockTest(mt)\n\t\t\tdefer d.disconnect(mt)\n\n\t\t\tkmsProviders := map[string]map[string]any{\n\t\t\t\t\"local\": {\"key\": localMasterKey},\n\t\t\t}\n\t\t\taeOpts := options.AutoEncryption()\n\t\t\taeOpts.SetKeyVaultNamespace(\"keyvault.datakeys\").\n\t\t\t\tSetKmsProviders(kmsProviders).\n\t\t\t\tSetBypassAutoEncryption(tc.bypassAutoEncryption)\n\n\t\t\t// Only set the crypt_shared library extra options if bypassAutoEncryption isn't\n\t\t\t// true because it's invalid to set cryptSharedLibRequired=true and\n\t\t\t// bypassAutoEncryption=true together.\n\t\t\tif !tc.bypassAutoEncryption {\n\t\t\t\taeOpts.SetExtraOptions(getCryptSharedLibExtraOptions())\n\t\t\t}\n\n\t\t\tif tc.keyVaultClientSet {\n\t\t\t\tintegtest.AddTestServerAPIVersion(d.clientKeyVaultOpts)\n\t\t\t\taeOpts.SetKeyVaultClientOptions(d.clientKeyVaultOpts)\n\t\t\t}\n\n\t\t\tceOpts := options.Client().ApplyURI(mtest.ClusterURI()).\n\t\t\t\tSetMonitor(&event.CommandMonitor{\n\t\t\t\t\tStarted: func(ctx context.Context, event *event.CommandStartedEvent) {\n\t\t\t\t\t\tclientEncryptedEvents = append(clientEncryptedEvents, startedEvent{event.CommandName, event.DatabaseName})\n\t\t\t\t\t},\n\t\t\t\t}).\n\t\t\t\tSetServerMonitor(&event.ServerMonitor{\n\t\t\t\t\tTopologyOpening: func(event *event.TopologyOpeningEvent) {\n\t\t\t\t\t\tclientEncryptedTopologyOpening++\n\t\t\t\t\t},\n\t\t\t\t}).\n\t\t\t\tSetMaxPoolSize(tc.maxPoolSize).\n\t\t\t\tSetAutoEncryptionOptions(aeOpts)\n\n\t\t\tintegtest.AddTestServerAPIVersion(ceOpts)\n\t\t\tclientEncrypted, err := mongo.Connect(ceOpts)\n\t\t\tassert.Nil(mt, err, \"Connect error: %v\", err)\n\t\t\tdefer clientEncrypted.Disconnect(context.Background())\n\n\t\t\tcoll := clientEncrypted.Database(\"db\").Collection(\"coll\")\n\t\t\tif !tc.bypassAutoEncryption {\n\t\t\t\t_, err = coll.InsertOne(context.Background(), bson.M{\"_id\": 0, \"encrypted\": \"string0\"})\n\t\t\t} else {\n\t\t\t\tunencryptedColl := d.clientTest.Database(\"db\").Collection(\"coll\")\n\t\t\t\t_, err = unencryptedColl.InsertOne(context.Background(), bson.M{\"_id\": 0, \"encrypted\": d.ciphertext})\n\t\t\t}\n\t\t\tassert.Nil(mt, err, \"InsertOne error: %v\", err)\n\n\t\t\traw, err := coll.FindOne(context.Background(), bson.M{\"_id\": 0}).Raw()\n\t\t\tassert.Nil(mt, err, \"FindOne error: %v\", err)\n\n\t\t\texpected := bsoncore.NewDocumentBuilder().\n\t\t\t\tAppendInt32(\"_id\", 0).\n\t\t\t\tAppendString(\"encrypted\", \"string0\").\n\t\t\t\tBuild()\n\t\t\tassert.Equal(mt, bson.Raw(expected), raw, \"returned value unequal, expected: %v, got: %v\", expected, raw)\n\n\t\t\tassert.Equal(mt, clientEncryptedEvents, tc.clientEncryptedCommandStartedExpected, \"mismatched events for clientEncrypted. Expected %v, got %v\", clientEncryptedEvents, tc.clientEncryptedCommandStartedExpected)\n\t\t\tassert.Equal(mt, d.clientKeyVaultEvents, tc.clientKeyVaultCommandStartedExpected, \"mismatched events for clientKeyVault. Expected %v, got %v\", d.clientKeyVaultEvents, tc.clientKeyVaultCommandStartedExpected)\n\t\t\tassert.Equal(mt, clientEncryptedTopologyOpening, tc.clientEncryptedTopologyOpeningExpected, \"wrong number of TopologyOpening events. Expected %v, got %v\", tc.clientEncryptedTopologyOpeningExpected, clientEncryptedTopologyOpening)\n\t\t})\n\t}\n}\n\nfunc TestClientSideEncryptionProse_10_kms_tls_tests(t *testing.T) {\n\tmt := newCSE_T(t, newNoClientOpts())\n\tmt.Setup()\n\n\tif os.Getenv(\"KMS_MOCK_SERVERS_RUNNING\") == \"\" {\n\t\tmt.Skipf(\"Skipping test as KMS_MOCK_SERVERS_RUNNING is not set\")\n\t}\n\n\tdefaultKvClientOptions := options.Client().ApplyURI(mtest.ClusterURI())\n\n\ttestcases := []struct {\n\t\tname       string\n\t\tport       int\n\t\terrMessage string\n\t}{\n\t\t{\n\t\t\t\"invalid certificate\",\n\t\t\t9000,\n\t\t\t\"expired\",\n\t\t},\n\t\t{\n\t\t\t\"invalid hostname\",\n\t\t\t9001,\n\t\t\t\"SANs\",\n\t\t},\n\t}\n\n\tfor _, tc := range testcases {\n\t\tmt.Run(tc.name, func(mt *mtest.T) {\n\t\t\tceo := options.ClientEncryption().\n\t\t\t\tSetKmsProviders(fullKmsProvidersMap).\n\t\t\t\tSetKeyVaultNamespace(kvNamespace)\n\t\t\tcpt := setup(mt, nil, defaultKvClientOptions, ceo)\n\t\t\tdefer cpt.teardown(mt)\n\n\t\t\t_, err := cpt.clientEnc.CreateDataKey(context.Background(), \"aws\", options.DataKey().SetMasterKey(\n\t\t\t\tbson.D{\n\t\t\t\t\t{\"region\", \"us-east-1\"},\n\t\t\t\t\t{\"key\", \"arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0\"},\n\t\t\t\t\t{\"endpoint\", fmt.Sprintf(\"127.0.0.1:%d\", tc.port)},\n\t\t\t\t},\n\t\t\t))\n\t\t\tassert.ErrorContains(mt, err, tc.errMessage)\n\t\t})\n\t}\n}\n\nfunc TestClientSideEncryptionProse_11_kms_tls_options_tests(t *testing.T) {\n\tmt := newCSE_T(t, newNoClientOpts())\n\tmt.Setup()\n\n\tif os.Getenv(\"KMS_MOCK_SERVERS_RUNNING\") == \"\" {\n\t\tmt.Skipf(\"Skipping test as KMS_MOCK_SERVERS_RUNNING is not set\")\n\t}\n\tif tlsCAFileKMIP == \"\" || tlsClientCertificateKeyFileKMIP == \"\" {\n\t\tmt.Fatal(\"Env vars CSFLE_TLS_CA_FILE and CSFLE_TLS_CLIENT_CERT_FILE must be set\")\n\t}\n\n\tdefaultKvClientOptions := options.Client().ApplyURI(mtest.ClusterURI())\n\n\tvalidKmsProviders := map[string]map[string]any{\n\t\t\"aws\": {\n\t\t\t\"accessKeyId\":     awsAccessKeyID,\n\t\t\t\"secretAccessKey\": awsSecretAccessKey,\n\t\t},\n\t\t\"azure\": {\n\t\t\t\"tenantId\":                 azureTenantID,\n\t\t\t\"clientId\":                 azureClientID,\n\t\t\t\"clientSecret\":             azureClientSecret,\n\t\t\t\"identityPlatformEndpoint\": \"127.0.0.1:9002\",\n\t\t},\n\t\t\"gcp\": {\n\t\t\t\"email\":      gcpEmail,\n\t\t\t\"privateKey\": gcpPrivateKey,\n\t\t\t\"endpoint\":   \"127.0.0.1:9002\",\n\t\t},\n\t\t\"kmip\": {\n\t\t\t\"endpoint\": \"127.0.0.1:5698\",\n\t\t},\n\t}\n\n\texpiredKmsProviders := map[string]map[string]any{\n\t\t\"aws\": {\n\t\t\t\"accessKeyId\":     awsAccessKeyID,\n\t\t\t\"secretAccessKey\": awsSecretAccessKey,\n\t\t},\n\t\t\"azure\": {\n\t\t\t\"tenantId\":                 azureTenantID,\n\t\t\t\"clientId\":                 azureClientID,\n\t\t\t\"clientSecret\":             azureClientSecret,\n\t\t\t\"identityPlatformEndpoint\": \"127.0.0.1:9000\",\n\t\t},\n\t\t\"gcp\": {\n\t\t\t\"email\":      gcpEmail,\n\t\t\t\"privateKey\": gcpPrivateKey,\n\t\t\t\"endpoint\":   \"127.0.0.1:9000\",\n\t\t},\n\t\t\"kmip\": {\n\t\t\t\"endpoint\": \"127.0.0.1:9000\",\n\t\t},\n\t}\n\n\tinvalidKmsProviders := map[string]map[string]any{\n\t\t\"aws\": {\n\t\t\t\"accessKeyId\":     awsAccessKeyID,\n\t\t\t\"secretAccessKey\": awsSecretAccessKey,\n\t\t},\n\t\t\"azure\": {\n\t\t\t\"tenantId\":                 azureTenantID,\n\t\t\t\"clientId\":                 azureClientID,\n\t\t\t\"clientSecret\":             azureClientSecret,\n\t\t\t\"identityPlatformEndpoint\": \"127.0.0.1:9001\",\n\t\t},\n\t\t\"gcp\": {\n\t\t\t\"email\":      gcpEmail,\n\t\t\t\"privateKey\": gcpPrivateKey,\n\t\t\t\"endpoint\":   \"127.0.0.1:9001\",\n\t\t},\n\t\t\"kmip\": {\n\t\t\t\"endpoint\": \"127.0.0.1:9001\",\n\t\t},\n\t}\n\n\t// create valid Client Encryption options without a client certificate\n\tvalidClientEncryptionOptionsWithoutClientCert := options.ClientEncryption().\n\t\tSetKmsProviders(validKmsProviders).\n\t\tSetKeyVaultNamespace(kvNamespace)\n\n\t// make TLS opts containing client certificate and CA file\n\tclientAndCATLSConfig, err := options.BuildTLSConfig(map[string]any{\n\t\t\"tlsCertificateKeyFile\": tlsClientCertificateKeyFileKMIP,\n\t\t\"tlsCAFile\":             tlsCAFileKMIP,\n\t})\n\tassert.Nil(mt, err, \"BuildTLSConfig error: %v\", err)\n\n\t// create valid Client Encryption options and set valid TLS options\n\tvalidClientEncryptionOptionsWithTLS := options.ClientEncryption().\n\t\tSetKmsProviders(validKmsProviders).\n\t\tSetKeyVaultNamespace(kvNamespace).\n\t\tSetTLSConfig(map[string]*tls.Config{\n\t\t\t\"aws\":   clientAndCATLSConfig,\n\t\t\t\"azure\": clientAndCATLSConfig,\n\t\t\t\"gcp\":   clientAndCATLSConfig,\n\t\t\t\"kmip\":  clientAndCATLSConfig,\n\t\t})\n\n\t// make TLS opts containing only CA file\n\tcaTLSConfig, err := options.BuildTLSConfig(map[string]any{\n\t\t\"tlsCAFile\": tlsCAFileKMIP,\n\t})\n\tassert.Nil(mt, err, \"BuildTLSConfig error: %v\", err)\n\n\t// create invalid Client Encryption options with expired credentials\n\texpiredClientEncryptionOptions := options.ClientEncryption().\n\t\tSetKmsProviders(expiredKmsProviders).\n\t\tSetKeyVaultNamespace(kvNamespace).\n\t\tSetTLSConfig(map[string]*tls.Config{\n\t\t\t\"aws\":   caTLSConfig,\n\t\t\t\"azure\": caTLSConfig,\n\t\t\t\"gcp\":   caTLSConfig,\n\t\t\t\"kmip\":  caTLSConfig,\n\t\t})\n\n\t// create invalid Client Encryption options with invalid hostnames\n\tinvalidHostnameClientEncryptionOptions := options.ClientEncryption().\n\t\tSetKmsProviders(invalidKmsProviders).\n\t\tSetKeyVaultNamespace(kvNamespace).\n\t\tSetTLSConfig(map[string]*tls.Config{\n\t\t\t\"aws\":   caTLSConfig,\n\t\t\t\"azure\": caTLSConfig,\n\t\t\t\"gcp\":   caTLSConfig,\n\t\t\t\"kmip\":  caTLSConfig,\n\t\t})\n\n\tawsMasterKeyNoClientCert := map[string]any{\n\t\t\"region\":   \"us-east-1\",\n\t\t\"key\":      \"arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0\",\n\t\t\"endpoint\": \"127.0.0.1:9002\",\n\t}\n\tawsMasterKeyWithTLS := map[string]any{\n\t\t\"region\":   \"us-east-1\",\n\t\t\"key\":      \"arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0\",\n\t\t\"endpoint\": \"127.0.0.1:9002\",\n\t}\n\tawsMasterKeyExpired := map[string]any{\n\t\t\"region\":   \"us-east-1\",\n\t\t\"key\":      \"arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0\",\n\t\t\"endpoint\": \"127.0.0.1:9000\",\n\t}\n\tawsMasterKeyInvalidHostname := map[string]any{\n\t\t\"region\":   \"us-east-1\",\n\t\t\"key\":      \"arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0\",\n\t\t\"endpoint\": \"127.0.0.1:9001\",\n\t}\n\tazureMasterKey := map[string]any{\n\t\t\"keyVaultEndpoint\": \"doesnotexist.invalid\",\n\t\t\"keyName\":          \"foo\",\n\t}\n\tgcpMasterKey := map[string]any{\n\t\t\"projectId\": \"foo\",\n\t\t\"location\":  \"bar\",\n\t\t\"keyRing\":   \"baz\",\n\t\t\"keyName\":   \"foo\",\n\t}\n\tkmipMasterKey := map[string]any{}\n\n\ttestCases := []struct {\n\t\tname                     string\n\t\tmasterKeyNoClientCert    any\n\t\tmasterKeyWithTLS         any\n\t\tmasterKeyExpired         any\n\t\tmasterKeyInvalidHostname any\n\t\ttlsError                 string\n\t\texpiredError             string\n\t\tinvalidHostnameError     string\n\t}{\n\t\t{\"aws\", awsMasterKeyNoClientCert, awsMasterKeyWithTLS, awsMasterKeyExpired, awsMasterKeyInvalidHostname, \"parse error\", \"certificate has expired\", \"cannot validate certificate\"},\n\t\t{\"azure\", azureMasterKey, azureMasterKey, azureMasterKey, azureMasterKey, \"HTTP status=404\", \"certificate has expired\", \"cannot validate certificate\"},\n\t\t{\"gcp\", gcpMasterKey, gcpMasterKey, gcpMasterKey, gcpMasterKey, \"HTTP status=404\", \"certificate has expired\", \"cannot validate certificate\"},\n\t\t{\"kmip\", kmipMasterKey, kmipMasterKey, kmipMasterKey, kmipMasterKey, \"\", \"certificate has expired\", \"cannot validate certificate\"},\n\t}\n\n\tfor _, tc := range testCases {\n\t\tmt.Run(tc.name, func(mt *mtest.T) {\n\t\t\tif tc.name == \"kmip\" && \"\" == os.Getenv(\"KMS_MOCK_SERVERS_RUNNING\") {\n\t\t\t\tmt.Skipf(\"Skipping test as KMS_MOCK_SERVERS_RUNNING is not set\")\n\t\t\t}\n\t\t\t// call CreateDataKey with CEO no TLS with each provider and corresponding master key\n\t\t\tcpt := setup(mt, nil, defaultKvClientOptions, validClientEncryptionOptionsWithoutClientCert)\n\t\t\tdefer cpt.teardown(mt)\n\n\t\t\tdkOpts := options.DataKey().SetMasterKey(tc.masterKeyNoClientCert)\n\t\t\t_, err := cpt.clientEnc.CreateDataKey(context.Background(), tc.name, dkOpts)\n\n\t\t\tassert.NotNil(mt, err, \"expected error, got nil\")\n\n\t\t\tpossibleErrors := []string{\n\t\t\t\t\"x509: certificate signed by unknown authority\",                   // Windows\n\t\t\t\t\"x509: “valid.testing.golang.invalid” certificate is not trusted\", // macOS\n\t\t\t\t\"x509: “server” certificate is not standards compliant\",           // macOS\n\t\t\t\t\"x509: certificate is not authorized to sign other certificates\",  // All others\n\t\t\t}\n\n\t\t\tassert.True(mt, containsPattern(possibleErrors, err.Error()),\n\t\t\t\t\"expected possibleErrors=%v to contain %v, but it didn't\",\n\t\t\t\tpossibleErrors, err.Error())\n\n\t\t\t// call CreateDataKey with CEO & TLS with each provider and corresponding master key\n\t\t\tcpt = setup(mt, nil, defaultKvClientOptions, validClientEncryptionOptionsWithTLS)\n\n\t\t\tdkOpts = options.DataKey().SetMasterKey(tc.masterKeyWithTLS)\n\t\t\t_, err = cpt.clientEnc.CreateDataKey(context.Background(), tc.name, dkOpts)\n\t\t\t// check if current test case is KMIP, which should pass\n\t\t\tif tc.name == \"kmip\" {\n\t\t\t\tassert.Nil(mt, err, \"expected no error, got err: %v\", err)\n\t\t\t} else {\n\t\t\t\tassert.NotNil(mt, err, \"expected error, got nil\")\n\t\t\t\tassert.True(mt, strings.Contains(err.Error(), tc.tlsError),\n\t\t\t\t\t\"expected error '%s' to contain '%s'\", err.Error(), tc.tlsError)\n\t\t\t}\n\n\t\t\t// call CreateDataKey with expired CEO each provider and same masterKey\n\t\t\tcpt = setup(mt, nil, defaultKvClientOptions, expiredClientEncryptionOptions)\n\n\t\t\tdkOpts = options.DataKey().SetMasterKey(tc.masterKeyExpired)\n\t\t\t_, err = cpt.clientEnc.CreateDataKey(context.Background(), tc.name, dkOpts)\n\t\t\tassert.NotNil(mt, err, \"expected error, got nil\")\n\t\t\tassert.True(mt, strings.Contains(err.Error(), tc.expiredError),\n\t\t\t\t\"expected error '%s' to contain '%s'\", err.Error(), tc.expiredError)\n\n\t\t\t// call CreateDataKey with invalid hostname CEO with each provider and same masterKey\n\t\t\tcpt = setup(mt, nil, defaultKvClientOptions, invalidHostnameClientEncryptionOptions)\n\n\t\t\tdkOpts = options.DataKey().SetMasterKey(tc.masterKeyInvalidHostname)\n\t\t\t_, err = cpt.clientEnc.CreateDataKey(context.Background(), tc.name, dkOpts)\n\t\t\tassert.NotNil(mt, err, \"expected error, got nil\")\n\t\t\tassert.True(mt, strings.Contains(err.Error(), tc.invalidHostnameError),\n\t\t\t\t\"expected error '%s' to contain '%s'\", err.Error(), tc.invalidHostnameError)\n\t\t})\n\t}\n}\n\nfunc newTopoOpts() *mtest.Options {\n\treturn mtest.NewOptions().Topologies(mtest.ReplicaSet, mtest.LoadBalanced, mtest.ShardedReplicaSet)\n}\n\nfunc TestClientSideEncryptionProse_12_explicit_encryption(t *testing.T) {\n\tmt := newCSE_T(t, newTopoOpts().MinServerVersion(\"7.0\"))\n\tmt.Setup()\n\n\t// Test Setup ... begin\n\tencryptedFields := readJSONFile(mt, \"encrypted-fields.json\")\n\tkey1Document := readJSONFile(mt, \"key1-document.json\")\n\tvar key1ID bson.Binary\n\t{\n\t\tsubtype, data := key1Document.Lookup(\"_id\").Binary()\n\t\tkey1ID = bson.Binary{Subtype: subtype, Data: data}\n\t}\n\n\ttestSetup := func() (*mongo.Client, *mongo.ClientEncryption) {\n\t\tmtest.DropEncryptedCollection(mt, mt.Client.Database(\"db\").Collection(\"explicit_encryption\"), encryptedFields)\n\t\tcco := options.CreateCollection().SetEncryptedFields(encryptedFields)\n\t\terr := mt.Client.Database(\"db\").CreateCollection(context.Background(), \"explicit_encryption\", cco)\n\t\tassert.Nil(mt, err, \"error on CreateCollection: %v\", err)\n\t\terr = mt.Client.Database(\"keyvault\").Collection(\"datakeys\").Drop(context.Background())\n\t\tassert.Nil(mt, err, \"error on Drop: %v\", err)\n\t\topts := options.Client().ApplyURI(mtest.ClusterURI())\n\t\tintegtest.AddTestServerAPIVersion(opts)\n\t\tkeyVaultClient, err := mongo.Connect(opts)\n\t\tassert.Nil(mt, err, \"error on Connect: %v\", err)\n\t\tdatakeysColl := keyVaultClient.Database(\"keyvault\").Collection(\"datakeys\", options.Collection().SetWriteConcern(mtest.MajorityWc))\n\t\t_, err = datakeysColl.InsertOne(context.Background(), key1Document)\n\t\tassert.Nil(mt, err, \"error on InsertOne: %v\", err)\n\t\t// Create a ClientEncryption.\n\t\tceo := options.ClientEncryption().\n\t\t\tSetKeyVaultNamespace(\"keyvault.datakeys\").\n\t\t\tSetKmsProviders(fullKmsProvidersMap)\n\t\tclientEncryption, err := mongo.NewClientEncryption(keyVaultClient, ceo)\n\t\tassert.Nil(mt, err, \"error on NewClientEncryption: %v\", err)\n\n\t\t// Create a MongoClient with AutoEncryptionOpts and bypassQueryAnalysis=true.\n\t\taeo := options.AutoEncryption().\n\t\t\tSetKeyVaultNamespace(\"keyvault.datakeys\").\n\t\t\tSetKmsProviders(fullKmsProvidersMap).\n\t\t\tSetBypassQueryAnalysis(true)\n\t\tco := options.Client().SetAutoEncryptionOptions(aeo).ApplyURI(mtest.ClusterURI())\n\t\tintegtest.AddTestServerAPIVersion(co)\n\t\tencryptedClient, err := mongo.Connect(co)\n\t\tassert.Nil(mt, err, \"error on Connect: %v\", err)\n\t\treturn encryptedClient, clientEncryption\n\t}\n\t// Test Setup ... end\n\n\tmt.Run(\"case 1: can insert encrypted indexed and find\", func(mt *mtest.T) {\n\t\tencryptedClient, clientEncryption := testSetup()\n\t\tdefer clientEncryption.Close(context.Background())\n\t\tdefer encryptedClient.Disconnect(context.Background())\n\n\t\t// Explicit encrypt the value \"encrypted indexed value\" with algorithm: \"Indexed\".\n\t\teo := options.Encrypt().SetAlgorithm(\"Indexed\").SetKeyID(key1ID).SetContentionFactor(0)\n\t\tvalueToEncrypt := \"encrypted indexed value\"\n\t\trawVal := bson.RawValue{Type: bson.TypeString, Value: bsoncore.AppendString(nil, valueToEncrypt)}\n\t\tinsertPayload, err := clientEncryption.Encrypt(context.Background(), rawVal, eo)\n\t\tassert.Nil(mt, err, \"error in Encrypt: %v\", err)\n\t\t// Insert.\n\t\tcoll := encryptedClient.Database(\"db\").Collection(\"explicit_encryption\")\n\t\t_, err = coll.InsertOne(context.Background(), bson.D{{\"_id\", 1}, {\"encryptedIndexed\", insertPayload}})\n\t\tassert.Nil(mt, err, \"Error in InsertOne: %v\", err)\n\t\t// Explicit encrypt an indexed value to find.\n\t\teo = options.Encrypt().SetAlgorithm(\"Indexed\").SetKeyID(key1ID).SetQueryType(options.QueryTypeEquality).SetContentionFactor(0)\n\t\tfindPayload, err := clientEncryption.Encrypt(context.Background(), rawVal, eo)\n\t\tassert.Nil(mt, err, \"error in Encrypt: %v\", err)\n\t\t// Find.\n\t\tres := coll.FindOne(context.Background(), bson.D{{\"encryptedIndexed\", findPayload}})\n\t\tassert.Nil(mt, res.Err(), \"Error in FindOne: %v\", res.Err())\n\t\tgot, err := res.Raw()\n\t\tassert.Nil(mt, err, \"error in Raw: %v\", err)\n\t\tgotValue, err := got.LookupErr(\"encryptedIndexed\")\n\t\tassert.Nil(mt, err, \"error in LookupErr: %v\", err)\n\t\tassert.Equal(mt, gotValue.StringValue(), valueToEncrypt, \"expected %q, got %q\", valueToEncrypt, gotValue.StringValue())\n\t})\n\tmt.Run(\"case 2: can insert encrypted indexed and find with non-zero contention\", func(mt *mtest.T) {\n\t\tencryptedClient, clientEncryption := testSetup()\n\t\tdefer clientEncryption.Close(context.Background())\n\t\tdefer encryptedClient.Disconnect(context.Background())\n\n\t\tcoll := encryptedClient.Database(\"db\").Collection(\"explicit_encryption\")\n\t\tvalueToEncrypt := \"encrypted indexed value\"\n\t\trawVal := bson.RawValue{Type: bson.TypeString, Value: bsoncore.AppendString(nil, valueToEncrypt)}\n\n\t\tfor i := 0; i < 10; i++ {\n\t\t\t// Explicit encrypt the value \"encrypted indexed value\" with algorithm: \"Indexed\".\n\t\t\teo := options.Encrypt().SetAlgorithm(\"Indexed\").SetKeyID(key1ID).SetContentionFactor(10)\n\t\t\tinsertPayload, err := clientEncryption.Encrypt(context.Background(), rawVal, eo)\n\t\t\tassert.Nil(mt, err, \"error in Encrypt: %v\", err)\n\t\t\t// Insert.\n\t\t\t_, err = coll.InsertOne(context.Background(), bson.D{{\"_id\", i}, {\"encryptedIndexed\", insertPayload}})\n\t\t\tassert.Nil(mt, err, \"Error in InsertOne: %v\", err)\n\t\t}\n\n\t\t// Explicit encrypt an indexed value to find with default contentionFactor 0.\n\t\t{\n\t\t\teo := options.Encrypt().SetAlgorithm(\"Indexed\").SetKeyID(key1ID).SetQueryType(options.QueryTypeEquality).SetContentionFactor(0)\n\t\t\tfindPayload, err := clientEncryption.Encrypt(context.Background(), rawVal, eo)\n\t\t\tassert.Nil(mt, err, \"error in Encrypt: %v\", err)\n\t\t\t// Find with contentionFactor=0.\n\t\t\tcursor, err := coll.Find(context.Background(), bson.D{{\"encryptedIndexed\", findPayload}})\n\t\t\tassert.Nil(mt, err, \"error in Find: %v\", err)\n\t\t\tvar got []bson.Raw\n\t\t\terr = cursor.All(context.Background(), &got)\n\t\t\tassert.Nil(mt, err, \"error in All: %v\", err)\n\t\t\tassert.True(mt, len(got) < 10, \"expected len(got) < 10, got: %v\", len(got))\n\t\t\tfor _, doc := range got {\n\t\t\t\tgotValue, err := doc.LookupErr(\"encryptedIndexed\")\n\t\t\t\tassert.Nil(mt, err, \"error in LookupErr: %v\", err)\n\t\t\t\tassert.Equal(mt, gotValue.StringValue(), valueToEncrypt, \"expected %q, got %q\", valueToEncrypt, gotValue.StringValue())\n\t\t\t}\n\t\t}\n\n\t\t// Explicit encrypt an indexed value to find with contentionFactor 10.\n\t\t{\n\t\t\teo := options.Encrypt().SetAlgorithm(\"Indexed\").SetKeyID(key1ID).SetQueryType(options.QueryTypeEquality).SetContentionFactor(10)\n\t\t\tfindPayload2, err := clientEncryption.Encrypt(context.Background(), rawVal, eo)\n\t\t\tassert.Nil(mt, err, \"error in Encrypt: %v\", err)\n\t\t\t// Find with contentionFactor=10.\n\t\t\tcursor, err := coll.Find(context.Background(), bson.D{{\"encryptedIndexed\", findPayload2}})\n\t\t\tassert.Nil(mt, err, \"error in Find: %v\", err)\n\t\t\tvar got []bson.Raw\n\t\t\terr = cursor.All(context.Background(), &got)\n\t\t\tassert.Nil(mt, err, \"error in All: %v\", err)\n\t\t\tassert.True(mt, len(got) == 10, \"expected len(got) == 10, got: %v\", len(got))\n\t\t\tfor _, doc := range got {\n\t\t\t\tgotValue, err := doc.LookupErr(\"encryptedIndexed\")\n\t\t\t\tassert.Nil(mt, err, \"error in LookupErr: %v\", err)\n\t\t\t\tassert.Equal(mt, gotValue.StringValue(), valueToEncrypt, \"expected %q, got %q\", valueToEncrypt, gotValue.StringValue())\n\t\t\t}\n\t\t}\n\t})\n\tmt.Run(\"case 3: can insert encrypted unindexed\", func(mt *mtest.T) {\n\t\tencryptedClient, clientEncryption := testSetup()\n\t\tdefer clientEncryption.Close(context.Background())\n\t\tdefer encryptedClient.Disconnect(context.Background())\n\n\t\t// Explicit encrypt the value \"encrypted indexed value\" with algorithm: \"Indexed\".\n\t\teo := options.Encrypt().SetAlgorithm(\"Unindexed\").SetKeyID(key1ID)\n\t\tvalueToEncrypt := \"encrypted unindexed value\"\n\t\trawVal := bson.RawValue{Type: bson.TypeString, Value: bsoncore.AppendString(nil, valueToEncrypt)}\n\t\tinsertPayload, err := clientEncryption.Encrypt(context.Background(), rawVal, eo)\n\t\tassert.Nil(mt, err, \"error in Encrypt: %v\", err)\n\t\t// Insert.\n\t\tcoll := encryptedClient.Database(\"db\").Collection(\"explicit_encryption\")\n\t\t_, err = coll.InsertOne(context.Background(), bson.D{{\"_id\", 1}, {\"encryptedUnindexed\", insertPayload}})\n\t\tassert.Nil(mt, err, \"Error in InsertOne: %v\", err)\n\t\t// Find.\n\t\tres := coll.FindOne(context.Background(), bson.D{{\"_id\", 1}})\n\t\tassert.Nil(mt, res.Err(), \"Error in FindOne: %v\", res.Err())\n\t\tgot, err := res.Raw()\n\t\tassert.Nil(mt, err, \"error in Raw: %v\", err)\n\t\tgotValue, err := got.LookupErr(\"encryptedUnindexed\")\n\t\tassert.Nil(mt, err, \"error in LookupErr: %v\", err)\n\t\tassert.Equal(mt, gotValue.StringValue(), valueToEncrypt, \"expected %q, got %q\", valueToEncrypt, gotValue.StringValue())\n\t})\n\tmt.Run(\"case 4: can roundtrip encrypted indexed\", func(mt *mtest.T) {\n\t\tencryptedClient, clientEncryption := testSetup()\n\t\tdefer clientEncryption.Close(context.Background())\n\t\tdefer encryptedClient.Disconnect(context.Background())\n\n\t\t// Explicit encrypt the value \"encrypted indexed value\" with algorithm: \"Indexed\".\n\t\teo := options.Encrypt().SetAlgorithm(\"Indexed\").SetKeyID(key1ID).SetContentionFactor(0)\n\t\tvalueToEncrypt := \"encrypted indexed value\"\n\t\trawVal := bson.RawValue{Type: bson.TypeString, Value: bsoncore.AppendString(nil, valueToEncrypt)}\n\t\tpayload, err := clientEncryption.Encrypt(context.Background(), rawVal, eo)\n\t\tassert.Nil(mt, err, \"error in Encrypt: %v\", err)\n\t\tgotValue, err := clientEncryption.Decrypt(context.Background(), payload)\n\t\tassert.Nil(mt, err, \"error in Decrypt: %v\", err)\n\t\tassert.Equal(mt, gotValue.StringValue(), valueToEncrypt, \"expected %q, got %q\", valueToEncrypt, gotValue.StringValue())\n\t})\n\tmt.Run(\"case 5: can roundtrip encrypted unindexed\", func(mt *mtest.T) {\n\t\tencryptedClient, clientEncryption := testSetup()\n\t\tdefer clientEncryption.Close(context.Background())\n\t\tdefer encryptedClient.Disconnect(context.Background())\n\n\t\t// Explicit encrypt the value \"encrypted indexed value\" with algorithm: \"Indexed\".\n\t\teo := options.Encrypt().SetAlgorithm(\"Unindexed\").SetKeyID(key1ID)\n\t\tvalueToEncrypt := \"encrypted unindexed value\"\n\t\trawVal := bson.RawValue{Type: bson.TypeString, Value: bsoncore.AppendString(nil, valueToEncrypt)}\n\t\tpayload, err := clientEncryption.Encrypt(context.Background(), rawVal, eo)\n\t\tassert.Nil(mt, err, \"error in Encrypt: %v\", err)\n\t\tgotValue, err := clientEncryption.Decrypt(context.Background(), payload)\n\t\tassert.Nil(mt, err, \"error in Decrypt: %v\", err)\n\t\tassert.Equal(mt, gotValue.StringValue(), valueToEncrypt, \"expected %q, got %q\", valueToEncrypt, gotValue.StringValue())\n\t})\n}\n\nfunc TestClientSideEncryptionProse_13_unique_index_on_keyAltNames(t *testing.T) {\n\tmt := newCSE_T(t, newTopoOpts().MinServerVersion(\"4.2\"))\n\tmt.Setup()\n\n\tconst (\n\t\tdkCollection  = \"datakeys\"\n\t\tidKey         = \"_id\"\n\t\tkvDatabase    = \"keyvault\"\n\t\tdefKeyAltName = \"def\"\n\t\tabcKeyAltName = \"abc\"\n\t)\n\n\tvar cse *cseProseTest\n\n\tinitialize := func() bson.Binary {\n\t\t// Create a ClientEncryption object (referred to as client_encryption) with client set as the keyVaultClient.\n\t\t// Using client, drop the collection keyvault.datakeys.\n\t\tdefaultKvClientOptions := options.Client().ApplyURI(mtest.ClusterURI())\n\t\tcse = setup(mt, nil, defaultKvClientOptions, options.ClientEncryption().\n\t\t\tSetKmsProviders(fullKmsProvidersMap).\n\t\t\tSetKeyVaultNamespace(kvNamespace))\n\n\t\terr := cse.kvClient.Database(kvDatabase).Collection(dkCollection).Drop(context.Background())\n\t\tassert.Nil(mt, err, \"error dropping %q namespace: %v\", kvNamespace, err)\n\n\t\t// Using client, create a unique index on keyAltNames with a partial index filter for only documents where\n\t\t// keyAltNames exists using writeConcern \"majority\".\n\t\tkeyVaultIndex := mongo.IndexModel{\n\t\t\tKeys: bson.D{{\"keyAltNames\", 1}},\n\t\t\tOptions: options.Index().\n\t\t\t\tSetUnique(true).\n\t\t\t\tSetName(\"keyAltNames_1\").\n\t\t\t\tSetPartialFilterExpression(bson.D{\n\t\t\t\t\t{\"keyAltNames\", bson.D{\n\t\t\t\t\t\t{\"$exists\", true},\n\t\t\t\t\t}},\n\t\t\t\t}),\n\t\t}\n\n\t\twcMajority := writeconcern.Majority()\n\t\twcMajorityCollectionOpts := options.Collection().SetWriteConcern(wcMajority)\n\t\twcmColl := cse.kvClient.Database(kvDatabase).Collection(dkCollection, wcMajorityCollectionOpts)\n\t\t_, err = wcmColl.Indexes().CreateOne(context.Background(), keyVaultIndex)\n\t\tassert.Nil(mt, err, \"error creating keyAltNames index: %v\", err)\n\n\t\t// Using client_encryption, create a data key with a local KMS provider and the keyAltName \"def\".\n\t\topts := options.DataKey().SetKeyAltNames([]string{defKeyAltName})\n\t\tdefKeyID, err := cse.clientEnc.CreateDataKey(context.Background(), \"local\", opts)\n\t\tassert.Nil(mt, err, \"error creating %q data key: %v\", defKeyAltName, err)\n\t\treturn defKeyID\n\t}\n\n\tvalidateAddKeyAltName := func(mt *mtest.T, cse *cseProseTest, res *mongo.SingleResult, expected ...string) {\n\t\tassert.Nil(mt, res.Err(), \"error adding key alt name: %v\", res.Err())\n\n\t\tresbytes, err := res.Raw()\n\t\tassert.Nil(mt, err, \"error decoding result bytes: %v\", err)\n\n\t\tidsubtype, iddata := bson.RawValue{Type: bson.TypeEmbeddedDocument, Value: resbytes}.\n\t\t\tDocument().Lookup(\"_id\").Binary()\n\t\tfilter := bsoncore.NewDocumentBuilder().AppendBinary(\"_id\", idsubtype, iddata).Build()\n\n\t\tctx := context.Background()\n\t\tupdatedData, err := cse.keyVaultColl.FindOne(ctx, filter).Raw()\n\t\tassert.Nil(mt, err, \"error decoding result bytes: %v\", err)\n\n\t\tupdated := bson.RawValue{Type: bson.TypeEmbeddedDocument, Value: updatedData}\n\t\tupdatedKeyAltNames, err := updated.Document().Lookup(\"keyAltNames\").Array().Values()\n\t\tassert.Nil(mt, err, \"error looking up raw keyAltNames: %v\", err)\n\t\tassert.Equal(mt, len(updatedKeyAltNames), len(expected), \"expected raw keyAltNames length to be 1\")\n\n\t\tfor idx, keyAltName := range updatedKeyAltNames {\n\t\t\tstr := keyAltName.StringValue()\n\t\t\tassert.Equal(mt, str, expected[idx], \"expected keyAltName to be %q, got: %q\", expected[idx], str)\n\t\t}\n\t}\n\n\tmt.Run(\"case 1: createKey()\", func(mt *mtest.T) {\n\t\tinitialize()\n\n\t\t// Use client_encryption to create a new local data key with a keyAltName \"abc\" and assert the operation\n\t\t// does not fail.\n\t\topts := options.DataKey().SetKeyAltNames([]string{abcKeyAltName})\n\t\t_, err := cse.clientEnc.CreateDataKey(context.Background(), \"local\", opts)\n\t\tassert.Nil(mt, err, \"error creating %q data key: %v\", abcKeyAltName, err)\n\n\t\t// Repeat Step 1 and assert the operation fails due to a duplicate key server error (error code 11000).\n\t\topts = options.DataKey().SetKeyAltNames([]string{abcKeyAltName})\n\t\t_, err = cse.clientEnc.CreateDataKey(context.Background(), \"local\", opts)\n\t\tassert.NotNil(mt, err, \"duplicate %q key did not propagate expected error\", abcKeyAltName)\n\n\t\te110000 := \"E11000 duplicate key\"\n\t\tcorrectError := strings.Contains(err.Error(), e110000)\n\t\tassert.True(mt, correctError, \"expected error to contain %q, got: %v\", e110000, err)\n\n\t\t// Use client_encryption to create a new local data key with a keyAltName \"def\" and assert the operation\n\t\t// fails due to a duplicate key server error (error code 11000).\n\t\topts = options.DataKey().SetKeyAltNames([]string{defKeyAltName})\n\t\t_, err = cse.clientEnc.CreateDataKey(context.Background(), \"local\", opts)\n\t\tassert.NotNil(mt, err, \"duplicate %q key did not propagate expected error\", defKeyAltName)\n\n\t\te110000 = \"E11000 duplicate key\"\n\t\tcorrectError = strings.Contains(err.Error(), e110000)\n\t\tassert.True(mt, correctError, \"expected error to contain %q, got: %v\", e110000, err)\n\t})\n\n\tmt.Run(\"case 2: addKeyAltName()\", func(mt *mtest.T) {\n\t\tdefKeyID := initialize()\n\n\t\tvar someNewKeyID bson.Binary\n\t\t// Use client_encryption to create a new local data key and assert the operation does not fail.\n\t\tvar err error\n\t\tsomeNewKeyID, err = cse.clientEnc.CreateDataKey(context.Background(), \"local\")\n\t\tassert.Nil(mt, err, \"error creating data key: %v\", err)\n\n\t\t// Use client_encryption to add a keyAltName \"abc\" to the key created in Step 1 and assert the operation\n\t\t// does not fail.\n\t\tres := cse.clientEnc.AddKeyAltName(context.Background(), someNewKeyID, abcKeyAltName)\n\t\tvalidateAddKeyAltName(mt, cse, res, abcKeyAltName)\n\n\t\t// Repeat Step 2, assert the operation does not fail, and assert the returned key document contains the\n\t\t// keyAltName \"abc\" added in Step 2.\n\t\tres = cse.clientEnc.AddKeyAltName(context.Background(), someNewKeyID, abcKeyAltName)\n\t\tvalidateAddKeyAltName(mt, cse, res, abcKeyAltName)\n\n\t\t// Use client_encryption to add a keyAltName \"def\" to the key created in Step 1 and assert the operation\n\t\t// fails due to a duplicate key server error (error code 11000).\n\t\tres = cse.clientEnc.AddKeyAltName(context.Background(), someNewKeyID, defKeyAltName)\n\t\tassert.NotNil(mt, res.Err(), \"duplicate %q key did not propagate expected error\", defKeyAltName)\n\n\t\te110000 := \"E11000 duplicate key\"\n\t\tcorrectError := strings.Contains(res.Err().Error(), e110000)\n\t\tassert.True(mt, correctError, \"expected error to contain %q, got: %v\", e110000, res.Err())\n\n\t\t// Use client_encryption to add a keyAltName \"def\" to the existing key, assert the operation does not fail,\n\t\t// and assert the returned key document contains the keyAltName \"def\" added during Setup.\n\t\tres = cse.clientEnc.AddKeyAltName(context.Background(), defKeyID, defKeyAltName)\n\t\tvalidateAddKeyAltName(mt, cse, res, defKeyAltName)\n\t})\n}\n\nfunc TestClientSideEncryptionProse_16_rewrap(t *testing.T) {\n\tmt := newCSE_T(t, newTopoOpts().MinServerVersion(\"4.2\"))\n\tmt.Setup()\n\n\tmt.Run(\"Case 1: Rewrap with separate ClientEncryption\", func(mt *mtest.T) {\n\t\tdataKeyMap := map[string]bson.M{\n\t\t\t\"aws\": {\n\t\t\t\t\"region\": \"us-east-1\",\n\t\t\t\t\"key\":    \"arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0\",\n\t\t\t},\n\t\t\t\"azure\": {\n\t\t\t\t\"keyVaultEndpoint\": \"key-vault-csfle.vault.azure.net\",\n\t\t\t\t\"keyName\":          \"key-name-csfle\",\n\t\t\t},\n\t\t\t\"gcp\": {\n\t\t\t\t\"projectId\": \"devprod-drivers\",\n\t\t\t\t\"location\":  \"global\",\n\t\t\t\t\"keyRing\":   \"key-ring-csfle\",\n\t\t\t\t\"keyName\":   \"key-name-csfle\",\n\t\t\t},\n\t\t\t\"kmip\": {},\n\t\t}\n\n\t\ttlsConfig := make(map[string]*tls.Config)\n\t\tif tlsCAFileKMIP != \"\" && tlsClientCertificateKeyFileKMIP != \"\" {\n\t\t\ttlsOpts := map[string]any{\n\t\t\t\t\"tlsCertificateKeyFile\": tlsClientCertificateKeyFileKMIP,\n\t\t\t\t\"tlsCAFile\":             tlsCAFileKMIP,\n\t\t\t}\n\t\t\tkmipConfig, err := options.BuildTLSConfig(tlsOpts)\n\t\t\tassert.Nil(mt, err, \"BuildTLSConfig error: %v\", err)\n\t\t\ttlsConfig[\"kmip\"] = kmipConfig\n\t\t}\n\n\t\tkmsProviders := []string{\"local\", \"aws\", \"gcp\", \"azure\", \"kmip\"}\n\t\tfor _, srcProvider := range kmsProviders {\n\t\t\tfor _, dstProvider := range kmsProviders {\n\t\t\t\tmt.Run(fmt.Sprintf(\"%s to %s\", srcProvider, dstProvider), func(mt *mtest.T) {\n\t\t\t\t\tvar err error\n\t\t\t\t\t// Drop the collection ``keyvault.datakeys``.\n\t\t\t\t\t{\n\t\t\t\t\t\terr = mt.Client.Database(\"keyvault\").Collection(\"datakeys\").Drop(context.Background())\n\t\t\t\t\t\tassert.Nil(mt, err, \"error on Drop: %v\", err)\n\t\t\t\t\t}\n\n\t\t\t\t\t// Create a ``ClientEncryption`` object named ``clientEncryption1``.\n\t\t\t\t\tvar clientEncryption1 *mongo.ClientEncryption\n\t\t\t\t\t{\n\t\t\t\t\t\tvar keyVaultClient *mongo.Client\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tco := options.Client().ApplyURI(mtest.ClusterURI())\n\t\t\t\t\t\t\tintegtest.AddTestServerAPIVersion(co)\n\t\t\t\t\t\t\tkeyVaultClient, err = mongo.Connect(co)\n\t\t\t\t\t\t\tdefer keyVaultClient.Disconnect(context.Background())\n\t\t\t\t\t\t\tintegtest.AddTestServerAPIVersion(co)\n\t\t\t\t\t\t\tassert.Nil(mt, err, \"error on Connect: %v\", err)\n\t\t\t\t\t\t}\n\t\t\t\t\t\tceOpts := options.ClientEncryption().\n\t\t\t\t\t\t\tSetKeyVaultNamespace(\"keyvault.datakeys\").\n\t\t\t\t\t\t\tSetKmsProviders(fullKmsProvidersMap).\n\t\t\t\t\t\t\tSetTLSConfig(tlsConfig)\n\t\t\t\t\t\tclientEncryption1, err = mongo.NewClientEncryption(keyVaultClient, ceOpts)\n\t\t\t\t\t\tassert.Nil(mt, err, \"error in NewClientEncryption: %v\", err)\n\t\t\t\t\t\tdefer clientEncryption1.Close(context.Background())\n\t\t\t\t\t}\n\n\t\t\t\t\t// Call ``clientEncryption1.createDataKey``.\n\t\t\t\t\tvar keyID bson.Binary\n\t\t\t\t\t{\n\t\t\t\t\t\tdkOpts := options.DataKey()\n\t\t\t\t\t\tif val, ok := dataKeyMap[srcProvider]; ok {\n\t\t\t\t\t\t\tdkOpts.SetMasterKey(val)\n\t\t\t\t\t\t}\n\t\t\t\t\t\tkeyID, err = clientEncryption1.CreateDataKey(context.Background(), srcProvider, dkOpts)\n\t\t\t\t\t\tassert.Nil(mt, err, \"error in CreateDataKey: %v\", err)\n\t\t\t\t\t}\n\n\t\t\t\t\t// Call ``clientEncryption1.encrypt`` with the value \"test\".\n\t\t\t\t\tvar ciphertext bson.Binary\n\t\t\t\t\t{\n\t\t\t\t\t\tt, value, err := bson.MarshalValue(\"test\")\n\t\t\t\t\t\tassert.Nil(mt, err, \"error in MarshalValue: %v\", err)\n\t\t\t\t\t\tplaintext := bson.RawValue{Type: t, Value: value}\n\t\t\t\t\t\teOpts := options.Encrypt().SetKeyID(keyID).SetAlgorithm(\"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\")\n\t\t\t\t\t\tciphertext, err = clientEncryption1.Encrypt(context.Background(), plaintext, eOpts)\n\t\t\t\t\t\tassert.Nil(mt, err, \"error in Encrypt: %v\", err)\n\t\t\t\t\t}\n\n\t\t\t\t\t// Create a ``ClientEncryption`` object named ``clientEncryption2``.\n\t\t\t\t\tvar clientEncryption2 *mongo.ClientEncryption\n\t\t\t\t\t{\n\t\t\t\t\t\tvar keyVaultClient *mongo.Client\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tco := options.Client().ApplyURI(mtest.ClusterURI())\n\t\t\t\t\t\t\tintegtest.AddTestServerAPIVersion(co)\n\t\t\t\t\t\t\tkeyVaultClient, err = mongo.Connect(co)\n\t\t\t\t\t\t\tdefer keyVaultClient.Disconnect(context.Background())\n\t\t\t\t\t\t\tintegtest.AddTestServerAPIVersion(co)\n\t\t\t\t\t\t\tassert.Nil(mt, err, \"error on Connect: %v\", err)\n\t\t\t\t\t\t}\n\t\t\t\t\t\tceOpts := options.ClientEncryption().\n\t\t\t\t\t\t\tSetKeyVaultNamespace(\"keyvault.datakeys\").\n\t\t\t\t\t\t\tSetKmsProviders(fullKmsProvidersMap).\n\t\t\t\t\t\t\tSetTLSConfig(tlsConfig)\n\t\t\t\t\t\tclientEncryption2, err = mongo.NewClientEncryption(keyVaultClient, ceOpts)\n\t\t\t\t\t\tassert.Nil(mt, err, \"error in NewClientEncryption: %v\", err)\n\t\t\t\t\t\tdefer clientEncryption2.Close(context.Background())\n\t\t\t\t\t}\n\n\t\t\t\t\t// Call ``clientEncryption2.rewrapManyDataKey`` with an empty ``filter``.\n\t\t\t\t\t{\n\t\t\t\t\t\trwOpts := options.RewrapManyDataKey().SetProvider(dstProvider)\n\t\t\t\t\t\tif val, ok := dataKeyMap[dstProvider]; ok {\n\t\t\t\t\t\t\trwOpts.SetMasterKey(val)\n\t\t\t\t\t\t}\n\t\t\t\t\t\tres, err := clientEncryption2.RewrapManyDataKey(context.Background(), bson.D{}, rwOpts)\n\t\t\t\t\t\tassert.Nil(mt, err, \"error in RewrapManyDataKey: %v\", err)\n\t\t\t\t\t\tassert.Equal(mt, res.BulkWriteResult.ModifiedCount, int64(1), \"expected ModifiedCount of 1, got %v\", res.BulkWriteResult.ModifiedCount)\n\t\t\t\t\t}\n\n\t\t\t\t\t// Call ``clientEncryption1.decrypt`` with the ``ciphertext``.\n\t\t\t\t\t{\n\t\t\t\t\t\tplaintext, err := clientEncryption1.Decrypt(context.Background(), ciphertext)\n\t\t\t\t\t\tassert.Nil(mt, err, \"error in Decrypt: %v\", err)\n\t\t\t\t\t\tassert.Equal(mt, plaintext.StringValue(), \"test\", \"expected plaintext 'test', got %q\", plaintext.StringValue())\n\t\t\t\t\t}\n\n\t\t\t\t\t// Call ``clientEncryption2.decrypt`` with the ``ciphertext``.\n\t\t\t\t\t{\n\t\t\t\t\t\tplaintext, err := clientEncryption2.Decrypt(context.Background(), ciphertext)\n\t\t\t\t\t\tassert.Nil(mt, err, \"error in Decrypt: %v\", err)\n\t\t\t\t\t\tassert.Equal(mt, plaintext.StringValue(), \"test\", \"expected plaintext 'test', got %q\", plaintext.StringValue())\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\t})\n\n\tmt.Run(\"Case 2: RewrapManyDataKeyOpts.provider is not optional\", func(mt *mtest.T) {\n\t\tvar err error\n\t\tvar clientEncryption *mongo.ClientEncryption\n\t\t{\n\t\t\tvar keyVaultClient *mongo.Client\n\t\t\t{\n\t\t\t\tco := options.Client().ApplyURI(mtest.ClusterURI())\n\t\t\t\tintegtest.AddTestServerAPIVersion(co)\n\t\t\t\tkeyVaultClient, err = mongo.Connect(co)\n\t\t\t\tdefer keyVaultClient.Disconnect(context.Background())\n\t\t\t\tintegtest.AddTestServerAPIVersion(co)\n\t\t\t\tassert.Nil(mt, err, \"error on Connect: %v\", err)\n\t\t\t}\n\t\t\tceOpts := options.ClientEncryption().\n\t\t\t\tSetKeyVaultNamespace(\"keyvault.datakeys\").\n\t\t\t\tSetKmsProviders(fullKmsProvidersMap)\n\t\t\tclientEncryption, err = mongo.NewClientEncryption(keyVaultClient, ceOpts)\n\t\t\tassert.Nil(mt, err, \"error in NewClientEncryption: %v\", err)\n\t\t\tdefer clientEncryption.Close(context.Background())\n\t\t}\n\n\t\t_, err = clientEncryption.RewrapManyDataKey(context.Background(), bson.D{}, options.RewrapManyDataKey().SetMasterKey(bson.D{}))\n\t\tassert.ErrorContains(mt, err, \"expected 'Provider' to be set to identify type of 'MasterKey'\")\n\t})\n}\n\nfunc TestClientSideEncryptionProse_18_azure_imds_credentials(t *testing.T) {\n\tmt := newCSE_T(t, newNoClientOpts())\n\tmt.Setup()\n\n\tbuf := new(bytes.Buffer)\n\tkmsProvidersMap := map[string]map[string]any{\n\t\t\"azure\": {},\n\t}\n\tvw := bson.NewDocumentWriter(buf)\n\terr := bson.NewEncoder(vw).Encode(kmsProvidersMap)\n\tassert.Nil(mt, err, \"error in Encode: %v\", err)\n\n\tgetClient := func(header http.Header) *http.Client {\n\t\tlt := &localTransport{\n\t\t\theader: header,\n\t\t\trt:     http.DefaultTransport,\n\t\t}\n\t\treturn &http.Client{\n\t\t\tTimeout:   30 * time.Second,\n\t\t\tTransport: lt,\n\t\t}\n\t}\n\n\tmt.Run(\"Case 1: Success\", func(mt *mtest.T) {\n\t\topts := &mongocryptopts.MongoCryptOptions{\n\t\t\tKmsProviders: buf.Bytes(),\n\t\t\tHTTPClient:   getClient(nil),\n\t\t}\n\t\tcrypt, err := mongocrypt.NewMongoCrypt(opts)\n\t\tassert.Nil(mt, err, \"error in NewMongoCrypt: %v\", err)\n\t\tdoc, err := crypt.GetKmsProviders(context.Background())\n\t\tassert.Nil(mt, err, \"error in GetKmsProviders: %v\", err)\n\t\tval, err := doc.LookupErr(\"azure\")\n\t\tassert.Nil(mt, err, \"error in LookupErr: %v\", err)\n\t\tassert.Equal(mt, `{\"accessToken\": \"magic-cookie\"}`, val.String(), \"expected accessToken, got %s\", val.String())\n\t})\n\tmt.Run(\"Case 2: Empty JSON\", func(mt *mtest.T) {\n\t\theader := make(http.Header)\n\t\theader.Set(\"X-MongoDB-HTTP-TestParams\", \"case=empty-json\")\n\t\topts := &mongocryptopts.MongoCryptOptions{\n\t\t\tKmsProviders: buf.Bytes(),\n\t\t\tHTTPClient:   getClient(header),\n\t\t}\n\t\tcrypt, err := mongocrypt.NewMongoCrypt(opts)\n\t\tassert.Nil(mt, err, \"error in NewMongoCrypt: %v\", err)\n\t\t_, err = crypt.GetKmsProviders(context.Background())\n\t\tassert.ErrorContains(mt, err, \"got unexpected empty accessToken\")\n\t})\n\tmt.Run(\"Case 3: Bad JSON\", func(mt *mtest.T) {\n\t\theader := make(http.Header)\n\t\theader.Set(\"X-MongoDB-HTTP-TestParams\", \"case=bad-json\")\n\t\topts := &mongocryptopts.MongoCryptOptions{\n\t\t\tKmsProviders: buf.Bytes(),\n\t\t\tHTTPClient:   getClient(header),\n\t\t}\n\t\tcrypt, err := mongocrypt.NewMongoCrypt(opts)\n\t\tassert.Nil(mt, err, \"error in NewMongoCrypt: %v\", err)\n\t\t_, err = crypt.GetKmsProviders(context.Background())\n\t\tassert.ErrorContains(mt, err, \"error reading body JSON\")\n\t})\n\tmt.Run(\"Case 4: HTTP 404\", func(mt *mtest.T) {\n\t\theader := make(http.Header)\n\t\theader.Set(\"X-MongoDB-HTTP-TestParams\", \"case=404\")\n\t\topts := &mongocryptopts.MongoCryptOptions{\n\t\t\tKmsProviders: buf.Bytes(),\n\t\t\tHTTPClient:   getClient(header),\n\t\t}\n\t\tcrypt, err := mongocrypt.NewMongoCrypt(opts)\n\t\tassert.Nil(mt, err, \"error in NewMongoCrypt: %v\", err)\n\t\t_, err = crypt.GetKmsProviders(context.Background())\n\t\tassert.ErrorContains(mt, err, \"got StatusCode: 404\")\n\t})\n\tmt.Run(\"Case 5: HTTP 500\", func(mt *mtest.T) {\n\t\theader := make(http.Header)\n\t\theader.Set(\"X-MongoDB-HTTP-TestParams\", \"case=500\")\n\t\topts := &mongocryptopts.MongoCryptOptions{\n\t\t\tKmsProviders: buf.Bytes(),\n\t\t\tHTTPClient:   getClient(header),\n\t\t}\n\t\tcrypt, err := mongocrypt.NewMongoCrypt(opts)\n\t\tassert.Nil(mt, err, \"error in NewMongoCrypt: %v\", err)\n\t\t_, err = crypt.GetKmsProviders(context.Background())\n\t\tassert.ErrorContains(mt, err, \"got StatusCode: 500\")\n\t})\n\tmt.Run(\"Case 6: Slow Response\", func(mt *mtest.T) {\n\t\theader := make(http.Header)\n\t\theader.Set(\"X-MongoDB-HTTP-TestParams\", \"case=slow\")\n\t\topts := &mongocryptopts.MongoCryptOptions{\n\t\t\tKmsProviders: buf.Bytes(),\n\t\t\tHTTPClient:   getClient(header),\n\t\t}\n\t\tcrypt, err := mongocrypt.NewMongoCrypt(opts)\n\t\tassert.Nil(mt, err, \"error in NewMongoCrypt: %v\", err)\n\t\t_, err = crypt.GetKmsProviders(context.Background())\n\n\t\tpossibleErrors := []string{\n\t\t\t\"error reading response body: context deadline exceeded\",    // <= 1.19 + RHEL & macOS\n\t\t\t\"Client.Timeout or context cancellation while reading body\", // > 1.20 on all OS\n\t\t}\n\n\t\tassert.True(mt, containsPattern(possibleErrors, err.Error()),\n\t\t\t\"expected possibleErrors=%v to contain %v, but it didn't\",\n\t\t\tpossibleErrors, err.Error())\n\t})\n}\n\nfunc TestClientSideEncryptionProse_20_bypass_creating_mongocryptd_client_when_shared_library_is_loaded(t *testing.T) {\n\tmt := newCSE_T(t, newTopoOpts())\n\tmt.Setup()\n\n\tcryptSharedLibPath := os.Getenv(\"CRYPT_SHARED_LIB_PATH\")\n\tif cryptSharedLibPath == \"\" {\n\t\tmt.Skip(\"CRYPT_SHARED_LIB_PATH not set, skipping\")\n\t\treturn\n\t}\n\n\tkmsProviders := map[string]map[string]any{\n\t\t\"local\": {\n\t\t\t\"key\": localMasterKey,\n\t\t},\n\t}\n\n\t// Listen for connections in a separate goroutine.\n\tlistener := listenForConnections(mt.T, func(c net.Conn) {\n\t\tmt.Fatalf(\"Unexpected connection created to mock mongocryptd goroutine\")\n\t\tc.Close()\n\t})\n\tdefer listener.Close()\n\n\turi := \"mongodb://\" + listener.Addr().String()\n\tmongocryptdSpawnArgs := map[string]any{\n\t\t\"mongocryptdURI\":     uri,\n\t\t\"cryptSharedLibPath\": cryptSharedLibPath,\n\t}\n\n\taeo := options.AutoEncryption().SetKmsProviders(kmsProviders).\n\t\tSetExtraOptions(mongocryptdSpawnArgs)\n\tcliOpts := options.Client().ApplyURI(mtest.ClusterURI()).SetAutoEncryptionOptions(aeo)\n\tintegtest.AddTestServerAPIVersion(cliOpts)\n\tencClient, err := mongo.Connect(cliOpts)\n\tassert.Nil(mt, err, \"Connect error: %v\", err)\n\tdefer func() {\n\t\terr = encClient.Disconnect(context.Background())\n\t\tassert.Nil(mt, err, \"encrypted client Disconnect error: %v\", err)\n\t}()\n\n\t// Run an Insert through the encrypted client to make sure mongocryptd is started ('insert' uses the\n\t// mongocryptd and will wait for it to be active).\n\t_, err = encClient.Database(\"db\").Collection(\"coll\").InsertOne(context.Background(), bson.D{{\"unencrypted\", \"test\"}})\n\tassert.Nil(mt, err, \"InsertOne error: %v\", err)\n}\n\n// newQEOpts creates Options for Queryable Encryption.\nfunc newQEOpts() *mtest.Options {\n\treturn mtest.NewOptions().Topologies(mtest.ReplicaSet, mtest.Sharded, mtest.LoadBalanced, mtest.ShardedReplicaSet)\n}\n\nfunc TestClientSideEncryptionProse_21_automatic_data_encryption_keys(t *testing.T) {\n\tmt := newCSE_T(t, newQEOpts().MinServerVersion(\"7.0\"))\n\tmt.Setup()\n\n\tsetup := func() (*mongo.Client, *mongo.ClientEncryption, error) {\n\t\topts := options.Client().ApplyURI(mtest.ClusterURI())\n\t\tintegtest.AddTestServerAPIVersion(opts)\n\t\tclient, err := mongo.Connect(opts)\n\t\tif err != nil {\n\t\t\treturn nil, nil, err\n\t\t}\n\t\tclient.Database(\"keyvault\").Collection(\"datakeys\").Drop(context.Background())\n\t\tclient.Database(\"db\").Drop(context.Background())\n\t\tceo := options.ClientEncryption().\n\t\t\tSetKmsProviders(fullKmsProvidersMap).\n\t\t\tSetKeyVaultNamespace(kvNamespace)\n\t\tclientEnc, err := mongo.NewClientEncryption(client, ceo)\n\t\tif err != nil {\n\t\t\treturn nil, nil, err\n\t\t}\n\t\treturn client, clientEnc, nil\n\t}\n\n\ttype KMSProviderTestcase struct {\n\t\tkmsProvider string\n\t\tmasterKey   *bson.D\n\t}\n\n\ttestcases := []KMSProviderTestcase{\n\t\t{\n\t\t\tkmsProvider: \"local\",\n\t\t\tmasterKey:   nil,\n\t\t},\n\t\t{\n\t\t\tkmsProvider: \"aws\",\n\t\t\tmasterKey: &bson.D{\n\t\t\t\t{\"region\", \"us-east-1\"},\n\t\t\t\t{\"key\", \"arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0\"},\n\t\t\t},\n\t\t},\n\t}\n\n\tfor _, tc := range testcases {\n\t\tmt.Run(tc.kmsProvider, func(mt *mtest.T) {\n\t\t\tmt.Run(\"case 1: simple creation and validation\", func(mt *mtest.T) {\n\t\t\t\tclient, clientEnc, err := setup()\n\t\t\t\tassert.Nil(mt, err, \"setup error: %v\", err)\n\t\t\t\tdefer func() {\n\t\t\t\t\terr := clientEnc.Close(context.Background())\n\t\t\t\t\tassert.Nil(mt, err, \"error in Close\")\n\t\t\t\t}()\n\n\t\t\t\tvar encryptedFields bson.Raw\n\t\t\t\terr = bson.UnmarshalExtJSON([]byte(`{\n\t\t\"fields\": [{\n\t\t\t\"path\": \"ssn\",\n\t\t\t\"bsonType\": \"string\",\n\t\t\t\"keyId\": null\n\t\t}]\n\t}`), true /* canonical */, &encryptedFields)\n\t\t\t\tassert.Nil(mt, err, \"Unmarshal error: %v\", err)\n\n\t\t\t\tcoll, _, err := clientEnc.CreateEncryptedCollection(\n\t\t\t\t\tcontext.Background(),\n\t\t\t\t\tclient.Database(\"db\"),\n\t\t\t\t\t\"testing1\", options.CreateCollection().SetEncryptedFields(encryptedFields),\n\t\t\t\t\t\"local\", nil,\n\t\t\t\t)\n\t\t\t\tassert.Nil(mt, err, \"CreateCollection error: %v\", err)\n\n\t\t\t\t_, err = coll.InsertOne(context.Background(), bson.D{{\"ssn\", \"123-45-6789\"}})\n\t\t\t\tassert.ErrorContains(mt, err, \"Document failed validation\")\n\t\t\t})\n\t\t\tmt.Run(\"case 2: missing encryptedFields\", func(mt *mtest.T) {\n\t\t\t\tclient, clientEnc, err := setup()\n\t\t\t\tassert.Nil(mt, err, \"setup error: %v\", err)\n\t\t\t\tdefer func() {\n\t\t\t\t\terr := clientEnc.Close(context.Background())\n\t\t\t\t\tassert.Nil(mt, err, \"error in Close\")\n\t\t\t\t}()\n\n\t\t\t\tcoll, _, err := clientEnc.CreateEncryptedCollection(\n\t\t\t\t\tcontext.Background(),\n\t\t\t\t\tclient.Database(\"db\"),\n\t\t\t\t\t\"testing1\", options.CreateCollection(),\n\t\t\t\t\t\"local\", nil,\n\t\t\t\t)\n\t\t\t\tassert.Nil(mt, coll, \"expect nil collection\")\n\t\t\t\tassert.EqualError(mt, err, \"no EncryptedFields defined for the collection\")\n\t\t\t})\n\t\t\tmt.Run(\"case 3: invalid keyId\", func(mt *mtest.T) {\n\t\t\t\tclient, clientEnc, err := setup()\n\t\t\t\tassert.Nil(mt, err, \"setup error: %v\", err)\n\t\t\t\tdefer func() {\n\t\t\t\t\terr := clientEnc.Close(context.Background())\n\t\t\t\t\tassert.Nil(mt, err, \"error in Close\")\n\t\t\t\t}()\n\n\t\t\t\tvar encryptedFields bson.Raw\n\t\t\t\terr = bson.UnmarshalExtJSON([]byte(`{\n\t\t\"fields\": [{\n\t\t\t\"path\": \"ssn\",\n\t\t\t\"bsonType\": \"string\",\n\t\t\t\"keyId\": false\n\t\t}]\n\t}`), true /* canonical */, &encryptedFields)\n\t\t\t\tassert.Nil(mt, err, \"Unmarshal error: %v\", err)\n\n\t\t\t\t_, _, err = clientEnc.CreateEncryptedCollection(\n\t\t\t\t\tcontext.Background(),\n\t\t\t\t\tclient.Database(\"db\"),\n\t\t\t\t\t\"testing1\", options.CreateCollection().SetEncryptedFields(encryptedFields),\n\t\t\t\t\t\"local\", nil,\n\t\t\t\t)\n\t\t\t\tassert.ErrorContains(mt, err, \"BSON field 'create.encryptedFields.fields.keyId' is the wrong type 'bool', expected type 'binData'\")\n\t\t\t})\n\t\t\tmt.Run(\"case 4: insert encrypted value\", func(mt *mtest.T) {\n\t\t\t\tclient, clientEnc, err := setup()\n\t\t\t\tassert.Nil(mt, err, \"setup error: %v\", err)\n\t\t\t\tdefer func() {\n\t\t\t\t\terr := clientEnc.Close(context.Background())\n\t\t\t\t\tassert.Nil(mt, err, \"error in Close\")\n\t\t\t\t}()\n\n\t\t\t\tvar encryptedFields bson.Raw\n\t\t\t\terr = bson.UnmarshalExtJSON([]byte(`{\n\t\t\"fields\": [{\n\t\t\t\"path\": \"ssn\",\n\t\t\t\"bsonType\": \"string\",\n\t\t\t\"keyId\": null\n\t\t}]\n\t}`), true /* canonical */, &encryptedFields)\n\t\t\t\tassert.Nil(mt, err, \"Unmarshal error: %v\", err)\n\n\t\t\t\tcoll, ef, err := clientEnc.CreateEncryptedCollection(\n\t\t\t\t\tcontext.Background(),\n\t\t\t\t\tclient.Database(\"db\"),\n\t\t\t\t\t\"testing1\", options.CreateCollection().SetEncryptedFields(encryptedFields),\n\t\t\t\t\t\"local\", nil,\n\t\t\t\t)\n\t\t\t\tassert.Nil(mt, err, \"CreateCollection error: %v\", err)\n\n\t\t\t\tkeyid := ef[\"fields\"].(bson.A)[0].(bson.M)[\"keyId\"].(bson.Binary)\n\t\t\t\trawValueType, rawValueData, err := bson.MarshalValue(\"123-45-6789\")\n\t\t\t\tassert.Nil(mt, err, \"MarshalValue error: %v\", err)\n\t\t\t\trawValue := bson.RawValue{Type: rawValueType, Value: rawValueData}\n\t\t\t\tencryptionOpts := options.Encrypt().\n\t\t\t\t\tSetAlgorithm(\"Unindexed\").\n\t\t\t\t\tSetKeyID(keyid)\n\t\t\t\tencryptedField, err := clientEnc.Encrypt(\n\t\t\t\t\tcontext.Background(),\n\t\t\t\t\trawValue,\n\t\t\t\t\tencryptionOpts)\n\t\t\t\tassert.Nil(mt, err, \"Encrypt error: %v\", err)\n\n\t\t\t\t_, err = coll.InsertOne(context.Background(), bson.D{{\"ssn\", encryptedField}})\n\t\t\t\tassert.Nil(mt, err, \"InsertOne error: %v\", err)\n\t\t\t})\n\t\t})\n\t}\n}\n\nfunc TestClientSideEncryptionProse_22_range_explicit_encryption(t *testing.T) {\n\tmt := newCSE_T(t, newQEOpts().MinServerVersion(\"8.0\"))\n\tmt.Setup()\n\n\ttype testcase struct {\n\t\ttypeStr       string\n\t\tfield         string\n\t\ttypeBson      bson.Type\n\t\trangeOpts     *options.RangeOptionsBuilder\n\t\tzero          bson.RawValue\n\t\tsix           bson.RawValue\n\t\tthirty        bson.RawValue\n\t\ttwoHundred    bson.RawValue\n\t\ttwoHundredOne bson.RawValue\n\t}\n\n\ttrimFactor := int32(1)\n\tsparsity := int64(1)\n\tprecision := int32(2)\n\n\td128_0, err := bson.ParseDecimal128(\"0\")\n\tassert.Nil(mt, err)\n\td128_0h, d128_0l := d128_0.GetBytes()\n\n\td128_6, err := bson.ParseDecimal128(\"6\")\n\tassert.Nil(mt, err)\n\td128_6h, d128_6l := d128_6.GetBytes()\n\n\td128_30, err := bson.ParseDecimal128(\"30\")\n\tassert.Nil(mt, err)\n\td128_30h, d128_30l := d128_30.GetBytes()\n\n\td128_200, err := bson.ParseDecimal128(\"200\")\n\tassert.Nil(mt, err)\n\td128_200h, d128_200l := d128_200.GetBytes()\n\n\td128_201, err := bson.ParseDecimal128(\"201\")\n\tassert.Nil(mt, err)\n\td128_201h, d128_201l := d128_201.GetBytes()\n\n\ttests := []testcase{\n\t\t{\n\t\t\ttypeStr:       \"DecimalNoPrecision\",\n\t\t\tfield:         \"encryptedDecimalNoPrecision\",\n\t\t\ttypeBson:      bson.TypeDecimal128,\n\t\t\trangeOpts:     options.Range().SetTrimFactor(trimFactor).SetSparsity(sparsity),\n\t\t\tzero:          bson.RawValue{Type: bson.TypeDecimal128, Value: bsoncore.AppendDecimal128(nil, d128_0h, d128_0l)},\n\t\t\tsix:           bson.RawValue{Type: bson.TypeDecimal128, Value: bsoncore.AppendDecimal128(nil, d128_6h, d128_6l)},\n\t\t\tthirty:        bson.RawValue{Type: bson.TypeDecimal128, Value: bsoncore.AppendDecimal128(nil, d128_30h, d128_30l)},\n\t\t\ttwoHundred:    bson.RawValue{Type: bson.TypeDecimal128, Value: bsoncore.AppendDecimal128(nil, d128_200h, d128_200l)},\n\t\t\ttwoHundredOne: bson.RawValue{Type: bson.TypeDecimal128, Value: bsoncore.AppendDecimal128(nil, d128_201h, d128_201l)},\n\t\t},\n\t\t{\n\t\t\ttypeStr:  \"DecimalPrecision\",\n\t\t\tfield:    \"encryptedDecimalPrecision\",\n\t\t\ttypeBson: bson.TypeDecimal128,\n\t\t\trangeOpts: options.Range().\n\t\t\t\tSetMin(bson.RawValue{Type: bson.TypeDecimal128, Value: bsoncore.AppendDecimal128(nil, d128_0h, d128_0l)}).\n\t\t\t\tSetMax(bson.RawValue{Type: bson.TypeDecimal128, Value: bsoncore.AppendDecimal128(nil, d128_200h, d128_200l)}).\n\t\t\t\tSetTrimFactor(trimFactor).\n\t\t\t\tSetSparsity(sparsity).\n\t\t\t\tSetPrecision(precision),\n\t\t\tzero:          bson.RawValue{Type: bson.TypeDecimal128, Value: bsoncore.AppendDecimal128(nil, d128_0h, d128_0l)},\n\t\t\tsix:           bson.RawValue{Type: bson.TypeDecimal128, Value: bsoncore.AppendDecimal128(nil, d128_6h, d128_6l)},\n\t\t\tthirty:        bson.RawValue{Type: bson.TypeDecimal128, Value: bsoncore.AppendDecimal128(nil, d128_30h, d128_30l)},\n\t\t\ttwoHundred:    bson.RawValue{Type: bson.TypeDecimal128, Value: bsoncore.AppendDecimal128(nil, d128_200h, d128_200l)},\n\t\t\ttwoHundredOne: bson.RawValue{Type: bson.TypeDecimal128, Value: bsoncore.AppendDecimal128(nil, d128_201h, d128_201l)},\n\t\t},\n\t\t{\n\t\t\ttypeStr:       \"DoubleNoPrecision\",\n\t\t\tfield:         \"encryptedDoubleNoPrecision\",\n\t\t\ttypeBson:      bson.TypeDouble,\n\t\t\trangeOpts:     options.Range().SetTrimFactor(trimFactor).SetSparsity(sparsity),\n\t\t\tzero:          bson.RawValue{Type: bson.TypeDouble, Value: bsoncore.AppendDouble(nil, 0)},\n\t\t\tsix:           bson.RawValue{Type: bson.TypeDouble, Value: bsoncore.AppendDouble(nil, 6)},\n\t\t\tthirty:        bson.RawValue{Type: bson.TypeDouble, Value: bsoncore.AppendDouble(nil, 30)},\n\t\t\ttwoHundred:    bson.RawValue{Type: bson.TypeDouble, Value: bsoncore.AppendDouble(nil, 200)},\n\t\t\ttwoHundredOne: bson.RawValue{Type: bson.TypeDouble, Value: bsoncore.AppendDouble(nil, 201)},\n\t\t},\n\t\t{\n\t\t\ttypeStr:  \"DoublePrecision\",\n\t\t\tfield:    \"encryptedDoublePrecision\",\n\t\t\ttypeBson: bson.TypeDouble,\n\t\t\trangeOpts: options.Range().\n\t\t\t\tSetMin(bson.RawValue{Type: bson.TypeDouble, Value: bsoncore.AppendDouble(nil, 0)}).\n\t\t\t\tSetMax(bson.RawValue{Type: bson.TypeDouble, Value: bsoncore.AppendDouble(nil, 200)}).\n\t\t\t\tSetTrimFactor(trimFactor).\n\t\t\t\tSetSparsity(sparsity).\n\t\t\t\tSetPrecision(precision),\n\t\t\tzero:          bson.RawValue{Type: bson.TypeDouble, Value: bsoncore.AppendDouble(nil, 0)},\n\t\t\tsix:           bson.RawValue{Type: bson.TypeDouble, Value: bsoncore.AppendDouble(nil, 6)},\n\t\t\tthirty:        bson.RawValue{Type: bson.TypeDouble, Value: bsoncore.AppendDouble(nil, 30)},\n\t\t\ttwoHundred:    bson.RawValue{Type: bson.TypeDouble, Value: bsoncore.AppendDouble(nil, 200)},\n\t\t\ttwoHundredOne: bson.RawValue{Type: bson.TypeDouble, Value: bsoncore.AppendDouble(nil, 201)},\n\t\t},\n\t\t{\n\t\t\ttypeStr:  \"Date\",\n\t\t\tfield:    \"encryptedDate\",\n\t\t\ttypeBson: bson.TypeDateTime,\n\t\t\trangeOpts: options.Range().\n\t\t\t\tSetMin(bson.RawValue{Type: bson.TypeDateTime, Value: bsoncore.AppendDateTime(nil, 0)}).\n\t\t\t\tSetMax(bson.RawValue{Type: bson.TypeDateTime, Value: bsoncore.AppendDateTime(nil, 200)}).\n\t\t\t\tSetTrimFactor(trimFactor).\n\t\t\t\tSetSparsity(sparsity),\n\t\t\tzero:          bson.RawValue{Type: bson.TypeDateTime, Value: bsoncore.AppendDateTime(nil, 0)},\n\t\t\tsix:           bson.RawValue{Type: bson.TypeDateTime, Value: bsoncore.AppendDateTime(nil, 6)},\n\t\t\tthirty:        bson.RawValue{Type: bson.TypeDateTime, Value: bsoncore.AppendDateTime(nil, 30)},\n\t\t\ttwoHundred:    bson.RawValue{Type: bson.TypeDateTime, Value: bsoncore.AppendDateTime(nil, 200)},\n\t\t\ttwoHundredOne: bson.RawValue{Type: bson.TypeDateTime, Value: bsoncore.AppendDateTime(nil, 201)},\n\t\t},\n\t\t{\n\t\t\ttypeStr:  \"Int\",\n\t\t\tfield:    \"encryptedInt\",\n\t\t\ttypeBson: bson.TypeInt32,\n\t\t\trangeOpts: options.Range().\n\t\t\t\tSetMin(bson.RawValue{Type: bson.TypeInt32, Value: bsoncore.AppendInt32(nil, 0)}).\n\t\t\t\tSetMax(bson.RawValue{Type: bson.TypeInt32, Value: bsoncore.AppendInt32(nil, 200)}).\n\t\t\t\tSetTrimFactor(trimFactor).\n\t\t\t\tSetSparsity(sparsity),\n\t\t\tzero:          bson.RawValue{Type: bson.TypeInt32, Value: bsoncore.AppendInt32(nil, 0)},\n\t\t\tsix:           bson.RawValue{Type: bson.TypeInt32, Value: bsoncore.AppendInt32(nil, 6)},\n\t\t\tthirty:        bson.RawValue{Type: bson.TypeInt32, Value: bsoncore.AppendInt32(nil, 30)},\n\t\t\ttwoHundred:    bson.RawValue{Type: bson.TypeInt32, Value: bsoncore.AppendInt32(nil, 200)},\n\t\t\ttwoHundredOne: bson.RawValue{Type: bson.TypeInt32, Value: bsoncore.AppendInt32(nil, 201)},\n\t\t},\n\t\t{\n\t\t\ttypeStr:  \"Long\",\n\t\t\tfield:    \"encryptedLong\",\n\t\t\ttypeBson: bson.TypeInt64,\n\t\t\trangeOpts: options.Range().\n\t\t\t\tSetMin(bson.RawValue{Type: bson.TypeInt64, Value: bsoncore.AppendInt64(nil, 0)}).\n\t\t\t\tSetMax(bson.RawValue{Type: bson.TypeInt64, Value: bsoncore.AppendInt64(nil, 200)}).\n\t\t\t\tSetTrimFactor(trimFactor).\n\t\t\t\tSetSparsity(sparsity),\n\t\t\tzero:          bson.RawValue{Type: bson.TypeInt64, Value: bsoncore.AppendInt64(nil, 0)},\n\t\t\tsix:           bson.RawValue{Type: bson.TypeInt64, Value: bsoncore.AppendInt64(nil, 6)},\n\t\t\tthirty:        bson.RawValue{Type: bson.TypeInt64, Value: bsoncore.AppendInt64(nil, 30)},\n\t\t\ttwoHundred:    bson.RawValue{Type: bson.TypeInt64, Value: bsoncore.AppendInt64(nil, 200)},\n\t\t\ttwoHundredOne: bson.RawValue{Type: bson.TypeInt64, Value: bsoncore.AppendInt64(nil, 201)},\n\t\t},\n\t}\n\n\tfor _, test := range tests {\n\t\tmt.Run(test.typeStr, func(mt *mtest.T) {\n\t\t\tif test.typeStr == \"DecimalNoPrecision\" && mtest.ClusterTopologyKind() != mtest.ReplicaSet {\n\t\t\t\tmt.Skipf(\"Skipping DecimalNoPrecision tests on a non ReplicaSet topology. DecimalNoPrecision queries are expected to take a long time and may exceed the default mongos timeout\")\n\t\t\t}\n\n\t\t\t// Test Setup ... begin\n\t\t\tencryptedFields := readJSONFile(mt, fmt.Sprintf(\"range-encryptedFields-%v.json\", test.typeStr))\n\t\t\tkey1Document := readJSONFile(mt, \"key1-document.json\")\n\t\t\tvar key1ID bson.Binary\n\t\t\t{\n\t\t\t\tsubtype, data := key1Document.Lookup(\"_id\").Binary()\n\t\t\t\tkey1ID = bson.Binary{Subtype: subtype, Data: data}\n\t\t\t}\n\n\t\t\ttestSetup := func() (*mongo.Client, *mongo.ClientEncryption) {\n\t\t\t\tmtest.DropEncryptedCollection(mt, mt.Client.Database(\"db\").Collection(\"explicit_encryption\"), encryptedFields)\n\t\t\t\tcco := options.CreateCollection().SetEncryptedFields(encryptedFields)\n\t\t\t\terr := mt.Client.Database(\"db\").CreateCollection(context.Background(), \"explicit_encryption\", cco)\n\t\t\t\tassert.Nil(mt, err, \"error on CreateCollection: %v\", err)\n\t\t\t\terr = mt.Client.Database(\"keyvault\").Collection(\"datakeys\").Drop(context.Background())\n\t\t\t\tassert.Nil(mt, err, \"error on Drop: %v\", err)\n\t\t\t\topts := options.Client().ApplyURI(mtest.ClusterURI())\n\t\t\t\tintegtest.AddTestServerAPIVersion(opts)\n\t\t\t\tkeyVaultClient, err := mongo.Connect(opts)\n\t\t\t\tassert.Nil(mt, err, \"error on Connect: %v\", err)\n\t\t\t\tdatakeysColl := keyVaultClient.Database(\"keyvault\").Collection(\"datakeys\", options.Collection().SetWriteConcern(mtest.MajorityWc))\n\t\t\t\t_, err = datakeysColl.InsertOne(context.Background(), key1Document)\n\t\t\t\tassert.Nil(mt, err, \"error on InsertOne: %v\", err)\n\t\t\t\t// Create a ClientEncryption.\n\t\t\t\tceo := options.ClientEncryption().\n\t\t\t\t\tSetKeyVaultNamespace(\"keyvault.datakeys\").\n\t\t\t\t\tSetKmsProviders(fullKmsProvidersMap)\n\t\t\t\tclientEncryption, err := mongo.NewClientEncryption(keyVaultClient, ceo)\n\t\t\t\tassert.Nil(mt, err, \"error on NewClientEncryption: %v\", err)\n\n\t\t\t\t// Create a MongoClient with AutoEncryptionOpts and bypassQueryAnalysis=true.\n\t\t\t\taeo := options.AutoEncryption().\n\t\t\t\t\tSetKeyVaultNamespace(\"keyvault.datakeys\").\n\t\t\t\t\tSetKmsProviders(fullKmsProvidersMap).\n\t\t\t\t\tSetBypassQueryAnalysis(true)\n\t\t\t\tco := options.Client().SetAutoEncryptionOptions(aeo).ApplyURI(mtest.ClusterURI())\n\t\t\t\tintegtest.AddTestServerAPIVersion(co)\n\t\t\t\tencryptedClient, err := mongo.Connect(co)\n\t\t\t\tassert.Nil(mt, err, \"error on Connect: %v\", err)\n\n\t\t\t\t// Insert 0, 6, 30, and 200.\n\t\t\t\tcoll := encryptedClient.Database(\"db\").Collection(\"explicit_encryption\")\n\t\t\t\teo := options.Encrypt().\n\t\t\t\t\tSetAlgorithm(\"Range\").\n\t\t\t\t\tSetKeyID(key1ID).\n\t\t\t\t\tSetContentionFactor(0).\n\t\t\t\t\tSetRangeOptions(test.rangeOpts)\n\t\t\t\t// Insert 0.\n\t\t\t\tinsertPayloadZero, err := clientEncryption.Encrypt(context.Background(), test.zero, eo)\n\t\t\t\tassert.Nil(mt, err, \"error in Encrypt: %v\", err)\n\t\t\t\t_, err = coll.InsertOne(context.Background(), bson.D{{\"_id\", 0}, {test.field, insertPayloadZero}})\n\t\t\t\tassert.Nil(mt, err, \"error in InsertOne: %v\", err)\n\t\t\t\t// Insert 6.\n\t\t\t\tinsertPayloadSix, err := clientEncryption.Encrypt(context.Background(), test.six, eo)\n\t\t\t\tassert.Nil(mt, err, \"error in Encrypt: %v\", err)\n\t\t\t\t_, err = coll.InsertOne(context.Background(), bson.D{{\"_id\", 1}, {test.field, insertPayloadSix}})\n\t\t\t\tassert.Nil(mt, err, \"error in InsertOne: %v\", err)\n\t\t\t\t// Insert 30.\n\t\t\t\tinsertPayloadThirty, err := clientEncryption.Encrypt(context.Background(), test.thirty, eo)\n\t\t\t\tassert.Nil(mt, err, \"error in Encrypt: %v\", err)\n\t\t\t\t_, err = coll.InsertOne(context.Background(), bson.D{{\"_id\", 2}, {test.field, insertPayloadThirty}})\n\t\t\t\tassert.Nil(mt, err, \"error in InsertOne: %v\", err)\n\t\t\t\t// Insert 200.\n\t\t\t\tinsertPayloadTwoHundred, err := clientEncryption.Encrypt(context.Background(), test.twoHundred, eo)\n\t\t\t\tassert.Nil(mt, err, \"error in Encrypt: %v\", err)\n\t\t\t\t_, err = coll.InsertOne(context.Background(), bson.D{{\"_id\", 3}, {test.field, insertPayloadTwoHundred}})\n\t\t\t\tassert.Nil(mt, err, \"error in InsertOne: %v\", err)\n\n\t\t\t\treturn encryptedClient, clientEncryption\n\t\t\t}\n\t\t\t// Test Setup ... end\n\n\t\t\t// checkCursorResults checks documents returned by a cursor.\n\t\t\t// Expects document i to have field `field` with value `values[i]`.\n\t\t\t// Expects exactly len(values) documents to be returned.\n\t\t\tcheckCursorResults := func(cursor *mongo.Cursor, field string, values ...bson.RawValue) {\n\t\t\t\tfor i, v := range values {\n\t\t\t\t\tassert.True(mt, cursor.Next(context.Background()), \"expected Next true, got false. Expected document %v with value: %v\", i, v)\n\t\t\t\t\tgot, err := cursor.Current.LookupErr(test.field)\n\t\t\t\t\tassert.Nil(mt, err, \"%v not found in document %v: %v\", test.field, i, cursor.Current)\n\t\t\t\t\tassert.Equal(mt, v, got, \"expected %v, got %v in document %v\", v, got, i)\n\t\t\t\t}\n\t\t\t\tassert.False(mt, cursor.Next(context.Background()), \"expected Next false, got true with document: %v\", cursor.Current)\n\t\t\t}\n\n\t\t\tmt.Run(\"Case 1: can decrypt a payload\", func(mt *mtest.T) {\n\t\t\t\tencryptedClient, clientEncryption := testSetup()\n\t\t\t\tdefer clientEncryption.Close(context.Background())\n\t\t\t\tdefer encryptedClient.Disconnect(context.Background())\n\t\t\t\teo := options.Encrypt().\n\t\t\t\t\tSetAlgorithm(\"Range\").\n\t\t\t\t\tSetKeyID(key1ID).\n\t\t\t\t\tSetContentionFactor(0).\n\t\t\t\t\tSetRangeOptions(test.rangeOpts)\n\t\t\t\tinsertPayloadSix, err := clientEncryption.Encrypt(context.Background(), test.six, eo)\n\t\t\t\tassert.Nil(mt, err, \"error in Encrypt: %v\", err)\n\t\t\t\tgot, err := clientEncryption.Decrypt(context.Background(), insertPayloadSix)\n\t\t\t\tassert.Nil(mt, err, \"error in Decrypt: %v\", err)\n\t\t\t\tassert.Equal(mt, test.six, got, \"expected %v, got %v\", test.six, got)\n\t\t\t})\n\n\t\t\tmt.Run(\"Case 2: can find encrypted range and return the maximum\", func(mt *mtest.T) {\n\t\t\t\tencryptedClient, clientEncryption := testSetup()\n\t\t\t\tdefer clientEncryption.Close(context.Background())\n\t\t\t\tdefer encryptedClient.Disconnect(context.Background())\n\t\t\t\teo := options.Encrypt().\n\t\t\t\t\tSetAlgorithm(\"Range\").\n\t\t\t\t\tSetKeyID(key1ID).\n\t\t\t\t\tSetContentionFactor(0).\n\t\t\t\t\tSetQueryType(\"range\").\n\t\t\t\t\tSetRangeOptions(test.rangeOpts)\n\n\t\t\t\texpr := bson.M{\n\t\t\t\t\t\"$and\": bson.A{\n\t\t\t\t\t\tbson.M{\n\t\t\t\t\t\t\ttest.field: bson.M{\n\t\t\t\t\t\t\t\t\"$gte\": test.six,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t\tbson.M{\n\t\t\t\t\t\t\ttest.field: bson.M{\n\t\t\t\t\t\t\t\t\"$lte\": test.twoHundred,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t}\n\n\t\t\t\t// Encrypt.\n\t\t\t\tvar encryptedExpr bson.Raw\n\t\t\t\t{\n\t\t\t\t\terr := clientEncryption.EncryptExpression(context.Background(), expr, &encryptedExpr, eo)\n\t\t\t\t\tassert.Nil(mt, err, \"error in EncryptExpression: %v\", err)\n\t\t\t\t}\n\n\t\t\t\tcoll := encryptedClient.Database(\"db\").Collection(\"explicit_encryption\")\n\t\t\t\topts := options.Find().SetSort(bson.D{{\"_id\", 1}})\n\t\t\t\tcursor, err := coll.Find(context.Background(), encryptedExpr, opts)\n\t\t\t\tassert.Nil(mt, err, \"error in coll.Find: %v\", err)\n\t\t\t\tdefer cursor.Close(context.Background())\n\n\t\t\t\tcheckCursorResults(cursor, test.field, test.six, test.thirty, test.twoHundred)\n\t\t\t})\n\n\t\t\tmt.Run(\"Case 3: can find encrypted range and return the minimum\", func(mt *mtest.T) {\n\t\t\t\tencryptedClient, clientEncryption := testSetup()\n\t\t\t\tdefer clientEncryption.Close(context.Background())\n\t\t\t\tdefer encryptedClient.Disconnect(context.Background())\n\t\t\t\teo := options.Encrypt().\n\t\t\t\t\tSetAlgorithm(\"Range\").\n\t\t\t\t\tSetKeyID(key1ID).\n\t\t\t\t\tSetContentionFactor(0).\n\t\t\t\t\tSetQueryType(\"range\").\n\t\t\t\t\tSetRangeOptions(test.rangeOpts)\n\n\t\t\t\texpr := bson.M{\n\t\t\t\t\t\"$and\": bson.A{\n\t\t\t\t\t\tbson.M{\n\t\t\t\t\t\t\ttest.field: bson.M{\n\t\t\t\t\t\t\t\t\"$gte\": test.zero,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t\tbson.M{\n\t\t\t\t\t\t\ttest.field: bson.M{\n\t\t\t\t\t\t\t\t\"$lte\": test.six,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t}\n\n\t\t\t\t// Encrypt.\n\t\t\t\tvar encryptedExpr bson.Raw\n\t\t\t\t{\n\t\t\t\t\terr := clientEncryption.EncryptExpression(context.Background(), expr, &encryptedExpr, eo)\n\t\t\t\t\tassert.Nil(mt, err, \"error in EncryptExpression: %v\", err)\n\t\t\t\t}\n\n\t\t\t\tcoll := encryptedClient.Database(\"db\").Collection(\"explicit_encryption\")\n\t\t\t\topts := options.Find().SetSort(bson.D{{\"_id\", 1}})\n\t\t\t\tcursor, err := coll.Find(context.Background(), encryptedExpr, opts)\n\t\t\t\tassert.Nil(mt, err, \"error in coll.Find: %v\", err)\n\t\t\t\tdefer cursor.Close(context.Background())\n\n\t\t\t\tcheckCursorResults(cursor, test.field, test.zero, test.six)\n\t\t\t})\n\n\t\t\tmt.Run(\"Case 4: can find encrypted range with an open range query\", func(mt *mtest.T) {\n\t\t\t\tencryptedClient, clientEncryption := testSetup()\n\t\t\t\tdefer clientEncryption.Close(context.Background())\n\t\t\t\tdefer encryptedClient.Disconnect(context.Background())\n\t\t\t\teo := options.Encrypt().\n\t\t\t\t\tSetAlgorithm(\"Range\").\n\t\t\t\t\tSetKeyID(key1ID).\n\t\t\t\t\tSetContentionFactor(0).\n\t\t\t\t\tSetQueryType(\"range\").\n\t\t\t\t\tSetRangeOptions(test.rangeOpts)\n\n\t\t\t\texpr := bson.M{\n\t\t\t\t\t\"$and\": bson.A{\n\t\t\t\t\t\tbson.M{\n\t\t\t\t\t\t\ttest.field: bson.M{\n\t\t\t\t\t\t\t\t\"$gt\": test.thirty,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t}\n\n\t\t\t\t// Encrypt.\n\t\t\t\tvar encryptedExpr bson.Raw\n\t\t\t\t{\n\t\t\t\t\terr := clientEncryption.EncryptExpression(context.Background(), expr, &encryptedExpr, eo)\n\t\t\t\t\tassert.Nil(mt, err, \"error in EncryptExpression: %v\", err)\n\t\t\t\t}\n\n\t\t\t\tcoll := encryptedClient.Database(\"db\").Collection(\"explicit_encryption\")\n\t\t\t\topts := options.Find().SetSort(bson.D{{\"_id\", 1}})\n\t\t\t\tcursor, err := coll.Find(context.Background(), encryptedExpr, opts)\n\t\t\t\tassert.Nil(mt, err, \"error in coll.Find: %v\", err)\n\t\t\t\tdefer cursor.Close(context.Background())\n\n\t\t\t\tcheckCursorResults(cursor, test.field, test.twoHundred)\n\t\t\t})\n\n\t\t\tmt.Run(\"Case 5: can run an aggregation expression inside $expr\", func(mt *mtest.T) {\n\t\t\t\tencryptedClient, clientEncryption := testSetup()\n\t\t\t\tdefer clientEncryption.Close(context.Background())\n\t\t\t\tdefer encryptedClient.Disconnect(context.Background())\n\t\t\t\teo := options.Encrypt().\n\t\t\t\t\tSetAlgorithm(\"Range\").\n\t\t\t\t\tSetKeyID(key1ID).\n\t\t\t\t\tSetContentionFactor(0).\n\t\t\t\t\tSetQueryType(\"range\").\n\t\t\t\t\tSetRangeOptions(test.rangeOpts)\n\n\t\t\t\texpr := bson.M{\n\t\t\t\t\t\"$and\": bson.A{\n\t\t\t\t\t\tbson.M{\n\t\t\t\t\t\t\t\"$lt\": bson.A{\n\t\t\t\t\t\t\t\tfmt.Sprintf(\"$%v\", test.field),\n\t\t\t\t\t\t\t\ttest.thirty,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t}\n\n\t\t\t\t// Encrypt.\n\t\t\t\tvar encryptedExpr bson.Raw\n\t\t\t\t{\n\t\t\t\t\terr := clientEncryption.EncryptExpression(context.Background(), expr, &encryptedExpr, eo)\n\t\t\t\t\tassert.Nil(mt, err, \"error in EncryptExpression: %v\", err)\n\t\t\t\t}\n\n\t\t\t\tcoll := encryptedClient.Database(\"db\").Collection(\"explicit_encryption\")\n\t\t\t\topts := options.Find().SetSort(bson.D{{\"_id\", 1}})\n\t\t\t\tcursor, err := coll.Find(context.Background(), bson.M{\"$expr\": encryptedExpr}, opts)\n\t\t\t\tassert.Nil(mt, err, \"error in coll.Find: %v\", err)\n\t\t\t\tdefer cursor.Close(context.Background())\n\n\t\t\t\tcheckCursorResults(cursor, test.field, test.zero, test.six)\n\t\t\t})\n\n\t\t\tif test.field != \"encryptedDoubleNoPrecision\" && test.field != \"encryptedDecimalNoPrecision\" {\n\t\t\t\tmt.Run(\"Case 6: encrypting a document greater than the maximum errors\", func(mt *mtest.T) {\n\t\t\t\t\tencryptedClient, clientEncryption := testSetup()\n\t\t\t\t\tdefer clientEncryption.Close(context.Background())\n\t\t\t\t\tdefer encryptedClient.Disconnect(context.Background())\n\t\t\t\t\teo := options.Encrypt().\n\t\t\t\t\t\tSetAlgorithm(\"Range\").\n\t\t\t\t\t\tSetKeyID(key1ID).\n\t\t\t\t\t\tSetContentionFactor(0).\n\t\t\t\t\t\tSetRangeOptions(test.rangeOpts)\n\n\t\t\t\t\t_, err := clientEncryption.Encrypt(context.Background(), test.twoHundredOne, eo)\n\t\t\t\t\tassert.NotNil(mt, err, \"expected error, but got none\")\n\t\t\t\t})\n\n\t\t\t\tmt.Run(\"Case 7: encrypting a document of a different type errors\", func(mt *mtest.T) {\n\t\t\t\t\tencryptedClient, clientEncryption := testSetup()\n\t\t\t\t\tdefer clientEncryption.Close(context.Background())\n\t\t\t\t\tdefer encryptedClient.Disconnect(context.Background())\n\t\t\t\t\teo := options.Encrypt().\n\t\t\t\t\t\tSetAlgorithm(\"Range\").\n\t\t\t\t\t\tSetKeyID(key1ID).\n\t\t\t\t\t\tSetContentionFactor(0).\n\t\t\t\t\t\tSetRangeOptions(test.rangeOpts)\n\n\t\t\t\t\tvar val bson.RawValue\n\t\t\t\t\tif test.field == \"encryptedInt\" {\n\t\t\t\t\t\tval = bson.RawValue{Type: bson.TypeDouble, Value: bsoncore.AppendDouble(nil, 6)}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tval = bson.RawValue{Type: bson.TypeInt32, Value: bsoncore.AppendInt32(nil, 6)}\n\t\t\t\t\t}\n\n\t\t\t\t\t_, err := clientEncryption.Encrypt(context.Background(), val, eo)\n\t\t\t\t\tassert.NotNil(mt, err, \"expected error, but got none\")\n\t\t\t\t})\n\t\t\t}\n\n\t\t\tif test.field != \"encryptedDoubleNoPrecision\" && test.field != \"encryptedDoublePrecision\" && test.field != \"encryptedDecimalNoPrecision\" && test.field != \"encryptedDecimalPrecision\" {\n\t\t\t\tmt.Run(\"Case 8: setting precision errors if the type is not a double\", func(mt *mtest.T) {\n\t\t\t\t\tencryptedClient, clientEncryption := testSetup()\n\t\t\t\t\tdefer clientEncryption.Close(context.Background())\n\t\t\t\t\tdefer encryptedClient.Disconnect(context.Background())\n\n\t\t\t\t\t// Copy rangeOpts and set precision.\n\t\t\t\t\tro := test.rangeOpts\n\t\t\t\t\tro.SetPrecision(2)\n\t\t\t\t\teo := options.Encrypt().\n\t\t\t\t\t\tSetAlgorithm(\"Range\").\n\t\t\t\t\t\tSetKeyID(key1ID).\n\t\t\t\t\t\tSetContentionFactor(0).\n\t\t\t\t\t\tSetRangeOptions(ro)\n\n\t\t\t\t\t_, err := clientEncryption.Encrypt(context.Background(), test.six, eo)\n\t\t\t\t\tassert.NotNil(mt, err, \"expected error, but got none\")\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestClientSideEncryptionProse_23_range_explicit_encryption_applies_defaults(t *testing.T) {\n\tmt := newCSE_T(t, newQEOpts().MinServerVersion(\"8.0\"))\n\tmt.Setup()\n\n\terr := mt.Client.Database(\"keyvault\").Collection(\"datakeys\").Drop(context.Background())\n\tassert.Nil(mt, err, \"error on Drop: %v\", err)\n\n\ttestVal := bson.RawValue{Type: bson.TypeInt32, Value: bsoncore.AppendInt32(nil, 123)}\n\n\topts := options.Client().ApplyURI(mtest.ClusterURI())\n\tintegtest.AddTestServerAPIVersion(opts)\n\tkeyVaultClient, err := mongo.Connect(opts)\n\tassert.Nil(mt, err, \"error on Connect: %v\", err)\n\n\tceo := options.ClientEncryption().\n\t\tSetKeyVaultNamespace(\"keyvault.datakeys\").\n\t\tSetKmsProviders(fullKmsProvidersMap)\n\tclientEncryption, err := mongo.NewClientEncryption(keyVaultClient, ceo)\n\tassert.Nil(mt, err, \"error on NewClientEncryption: %v\", err)\n\n\tdkOpts := options.DataKey()\n\tkeyID, err := clientEncryption.CreateDataKey(context.Background(), \"local\", dkOpts)\n\tassert.Nil(mt, err, \"error in CreateDataKey: %v\", err)\n\n\teo := options.Encrypt().\n\t\tSetAlgorithm(\"Range\").\n\t\tSetKeyID(keyID).\n\t\tSetContentionFactor(0).\n\t\tSetRangeOptions(options.Range().\n\t\t\tSetMin(bson.RawValue{Type: bson.TypeInt32, Value: bsoncore.AppendInt32(nil, 0)}).\n\t\t\tSetMax(bson.RawValue{Type: bson.TypeInt32, Value: bsoncore.AppendInt32(nil, 1000)}))\n\tpayloadDefaults, err := clientEncryption.Encrypt(context.Background(), testVal, eo)\n\tassert.Nil(mt, err, \"error in Encrypt: %v\", err)\n\n\tmt.Run(\"Case 1: uses libmongocrypt defaults\", func(mt *mtest.T) {\n\t\ttrimFactor := int32(6)\n\t\tsparsity := int64(2)\n\t\teo := options.Encrypt().\n\t\t\tSetAlgorithm(\"Range\").\n\t\t\tSetKeyID(keyID).\n\t\t\tSetContentionFactor(0).\n\t\t\tSetRangeOptions(options.Range().\n\t\t\t\tSetMin(bson.RawValue{Type: bson.TypeInt32, Value: bsoncore.AppendInt32(nil, 0)}).\n\t\t\t\tSetMax(bson.RawValue{Type: bson.TypeInt32, Value: bsoncore.AppendInt32(nil, 1000)}).\n\t\t\t\tSetTrimFactor(trimFactor).\n\t\t\t\tSetSparsity(sparsity))\n\t\tpayload, err := clientEncryption.Encrypt(context.Background(), testVal, eo)\n\t\tassert.Nil(mt, err, \"error in Encrypt: %v\", err)\n\t\tassert.Equalf(mt, len(payload.Data), len(payloadDefaults.Data), \"the returned payload size is expected to be %d\", len(payloadDefaults.Data))\n\t})\n\n\tmt.Run(\"Case 2: accepts trimFactor 0\", func(mt *mtest.T) {\n\t\ttrimFactor := int32(0)\n\t\teo := options.Encrypt().\n\t\t\tSetAlgorithm(\"Range\").\n\t\t\tSetKeyID(keyID).\n\t\t\tSetContentionFactor(0).\n\t\t\tSetRangeOptions(options.Range().\n\t\t\t\tSetMin(bson.RawValue{Type: bson.TypeInt32, Value: bsoncore.AppendInt32(nil, 0)}).\n\t\t\t\tSetMax(bson.RawValue{Type: bson.TypeInt32, Value: bsoncore.AppendInt32(nil, 1000)}).\n\t\t\t\tSetTrimFactor(trimFactor))\n\t\tpayload, err := clientEncryption.Encrypt(context.Background(), testVal, eo)\n\t\tassert.Nil(mt, err, \"error in Encrypt: %v\", err)\n\t\tassert.Greater(mt, len(payload.Data), len(payloadDefaults.Data), \"the returned payload size is expected to be greater than %d\", len(payloadDefaults.Data))\n\t})\n}\n\nfunc TestClientSideEncryptionProse_24_kmw_retry_tests(t *testing.T) {\n\tt.Parallel()\n\n\tmt := newCSE_T(t, newNoClientOpts())\n\tmt.Setup()\n\n\tkmsTlsTestcase := os.Getenv(\"KMS_FAILPOINT_SERVER_RUNNING\")\n\tif kmsTlsTestcase == \"\" {\n\t\tmt.Skipf(\"Skipping test as KMS_FAILPOINT_SERVER_RUNNING is not set\")\n\t}\n\n\ttlsCAFile := os.Getenv(\"KMS_FAILPOINT_CA_FILE\")\n\trequire.NotEqual(mt, tlsCAFile, \"\", \"failed to load CA file\")\n\n\tclientAndCATlsMap := map[string]any{\n\t\t\"tlsCAFile\": tlsCAFile,\n\t}\n\ttlsCfg, err := options.BuildTLSConfig(clientAndCATlsMap)\n\trequire.NoError(mt, err, \"BuildTLSConfig error: %v\", err)\n\n\tsetFailPoint := func(failure string, count int) error {\n\t\turl := fmt.Sprintf(\"https://localhost:9003/set_failpoint/%s\", failure)\n\t\tvar payloadBuf bytes.Buffer\n\t\tbody := map[string]int{\"count\": count}\n\t\tjson.NewEncoder(&payloadBuf).Encode(body)\n\t\treq, err := http.NewRequest(http.MethodPost, url, &payloadBuf)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tclient := &http.Client{\n\t\t\tTransport: &http.Transport{TLSClientConfig: tlsCfg},\n\t\t}\n\t\tres, err := client.Do(req)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn res.Body.Close()\n\t}\n\n\tkmsProviders := map[string]map[string]any{\n\t\t\"aws\": {\n\t\t\t\"accessKeyId\":     awsAccessKeyID,\n\t\t\t\"secretAccessKey\": awsSecretAccessKey,\n\t\t},\n\t\t\"azure\": {\n\t\t\t\"tenantId\":                 azureTenantID,\n\t\t\t\"clientId\":                 azureClientID,\n\t\t\t\"clientSecret\":             azureClientSecret,\n\t\t\t\"identityPlatformEndpoint\": \"127.0.0.1:9003\",\n\t\t},\n\t\t\"gcp\": {\n\t\t\t\"email\":      gcpEmail,\n\t\t\t\"privateKey\": gcpPrivateKey,\n\t\t\t\"endpoint\":   \"127.0.0.1:9003\",\n\t\t},\n\t}\n\n\tdataKeys := []struct {\n\t\tprovider  string\n\t\tmasterKey any\n\t}{\n\t\t{\"aws\", bson.D{\n\t\t\t{\"region\", \"foo\"},\n\t\t\t{\"key\", \"bar\"},\n\t\t\t{\"endpoint\", \"127.0.0.1:9003\"},\n\t\t}},\n\t\t{\"azure\", bson.D{\n\t\t\t{\"keyVaultEndpoint\", \"127.0.0.1:9003\"},\n\t\t\t{\"keyName\", \"foo\"},\n\t\t}},\n\t\t{\"gcp\", bson.D{\n\t\t\t{\"projectId\", \"foo\"},\n\t\t\t{\"location\", \"bar\"},\n\t\t\t{\"keyRing\", \"baz\"},\n\t\t\t{\"keyName\", \"qux\"},\n\t\t\t{\"endpoint\", \"127.0.0.1:9003\"},\n\t\t}},\n\t}\n\n\ttestCases := []struct {\n\t\tname    string\n\t\tfailure string\n\t}{\n\t\t{\"Case 1: createDataKey and encrypt with TCP retry\", \"network\"},\n\t\t{\"Case 2: createDataKey and encrypt with HTTP retry\", \"http\"},\n\t}\n\n\tfor _, tc := range testCases {\n\t\tfor _, dataKey := range dataKeys {\n\t\t\tmt.Run(fmt.Sprintf(\"%s_%s\", tc.name, dataKey.provider), func(mt *mtest.T) {\n\t\t\t\topts := options.Client().ApplyURI(mtest.ClusterURI())\n\t\t\t\tintegtest.AddTestServerAPIVersion(opts)\n\t\t\t\tkeyVaultClient, err := mongo.Connect(opts)\n\t\t\t\trequire.NoError(mt, err, \"error on Connect: %v\", err)\n\n\t\t\t\tceo := options.ClientEncryption().\n\t\t\t\t\tSetKeyVaultNamespace(kvNamespace).\n\t\t\t\t\tSetKmsProviders(kmsProviders).\n\t\t\t\t\tSetTLSConfig(map[string]*tls.Config{dataKey.provider: tlsCfg})\n\t\t\t\tclientEncryption, err := mongo.NewClientEncryption(keyVaultClient, ceo)\n\t\t\t\trequire.NoError(mt, err, \"error on NewClientEncryption: %v\", err)\n\n\t\t\t\terr = setFailPoint(tc.failure, 1)\n\t\t\t\trequire.NoError(mt, err, \"mock server error: %v\", err)\n\n\t\t\t\tdkOpts := options.DataKey().SetMasterKey(dataKey.masterKey)\n\t\t\t\tvar keyID bson.Binary\n\t\t\t\tkeyID, err = clientEncryption.CreateDataKey(context.Background(), dataKey.provider, dkOpts)\n\t\t\t\trequire.NoError(mt, err, \"error in CreateDataKey: %v\", err)\n\n\t\t\t\terr = setFailPoint(tc.failure, 1)\n\t\t\t\trequire.NoError(mt, err, \"mock server error: %v\", err)\n\n\t\t\t\ttestVal := bson.RawValue{Type: bson.TypeInt32, Value: bsoncore.AppendInt32(nil, 123)}\n\t\t\t\teo := options.Encrypt().\n\t\t\t\t\tSetKeyID(keyID).\n\t\t\t\t\tSetAlgorithm(\"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\")\n\t\t\t\t_, err = clientEncryption.Encrypt(context.Background(), testVal, eo)\n\t\t\t\trequire.NoError(mt, err, \"error in Encrypt: %v\", err)\n\t\t\t})\n\t\t}\n\t}\n\n\tfor _, dataKey := range dataKeys {\n\t\tmt.Run(fmt.Sprintf(\"Case 3: createDataKey fails after too many retries_%s\", dataKey.provider), func(mt *mtest.T) {\n\t\t\topts := options.Client().ApplyURI(mtest.ClusterURI())\n\t\t\tintegtest.AddTestServerAPIVersion(opts)\n\t\t\tkeyVaultClient, err := mongo.Connect(opts)\n\n\t\t\trequire.NoError(mt, err, \"error on Connect: %v\", err)\n\n\t\t\tceo := options.ClientEncryption().\n\t\t\t\tSetKeyVaultNamespace(kvNamespace).\n\t\t\t\tSetKmsProviders(kmsProviders).\n\t\t\t\tSetTLSConfig(map[string]*tls.Config{dataKey.provider: tlsCfg})\n\t\t\tclientEncryption, err := mongo.NewClientEncryption(keyVaultClient, ceo)\n\t\t\trequire.NoError(mt, err, \"error on NewClientEncryption: %v\", err)\n\n\t\t\terr = setFailPoint(\"network\", 4)\n\t\t\trequire.NoError(mt, err, \"mock server error: %v\", err)\n\n\t\t\tdkOpts := options.DataKey().SetMasterKey(dataKey.masterKey)\n\t\t\t_, err = clientEncryption.CreateDataKey(context.Background(), dataKey.provider, dkOpts)\n\t\t\trequire.ErrorContains(mt, err, \"KMS request failed after 3 retries due to a network error\")\n\t\t})\n\t}\n}\n\nfunc TestClientSideEncryptionProse_27_text_explicit_encryption(t *testing.T) {\n\tmt := newCSE_T(t, newQEOpts().MinServerVersion(\"8.2\"))\n\tmt.Setup()\n\n\tencryptedFields := readJSONFile(mt, \"encryptedFields-prefix-suffix.json\")\n\tkey1Document := readJSONFile(mt, \"key1-document.json\")\n\tsubtype, data := key1Document.Lookup(\"_id\").Binary()\n\tkey1ID := bson.Binary{Subtype: subtype, Data: data}\n\n\ttestSetup := func() (*mongo.Client, *mongo.ClientEncryption) {\n\t\tfor _, collName := range []string{\"prefix-suffix\", \"substring\"} {\n\t\t\tmtest.DropEncryptedCollection(mt, mt.Client.Database(\"db\").Collection(collName), encryptedFields)\n\t\t\tcco := options.CreateCollection().SetEncryptedFields(encryptedFields)\n\t\t\terr := mt.Client.Database(\"db\").CreateCollection(context.Background(), collName, cco)\n\t\t\trequire.NoError(mt, err, \"error on CreateCollection: %v\", err)\n\t\t}\n\t\terr := mt.Client.Database(\"keyvault\").Collection(\"datakeys\").Drop(context.Background())\n\t\trequire.NoError(mt, err, \"error on Drop: %v\", err)\n\t\topts := options.Client().ApplyURI(mtest.ClusterURI())\n\t\tintegtest.AddTestServerAPIVersion(opts)\n\t\tkeyVaultClient, err := mongo.Connect(opts)\n\t\trequire.NoError(mt, err, \"error on Connect: %v\", err)\n\t\tdatakeysColl := keyVaultClient.Database(\"keyvault\").Collection(\"datakeys\", options.Collection().SetWriteConcern(mtest.MajorityWc))\n\t\t_, err = datakeysColl.InsertOne(context.Background(), key1Document)\n\t\trequire.NoError(mt, err, \"error on InsertOne: %v\", err)\n\t\tkmsProvidersMap := map[string]map[string]any{\n\t\t\t\"local\": {\"key\": localMasterKey},\n\t\t}\n\t\t// Create a ClientEncryption.\n\t\tceo := options.ClientEncryption().\n\t\t\tSetKeyVaultNamespace(\"keyvault.datakeys\").\n\t\t\tSetKmsProviders(kmsProvidersMap)\n\t\tclientEncryption, err := mongo.NewClientEncryption(keyVaultClient, ceo)\n\t\trequire.NoError(mt, err, \"error on NewClientEncryption: %v\", err)\n\n\t\t// Create a MongoClient with AutoEncryptionOpts and bypassQueryAnalysis=true.\n\t\taeo := options.AutoEncryption().\n\t\t\tSetKeyVaultNamespace(\"keyvault.datakeys\").\n\t\t\tSetKmsProviders(kmsProvidersMap).\n\t\t\tSetBypassQueryAnalysis(true)\n\t\tco := options.Client().SetAutoEncryptionOptions(aeo).ApplyURI(mtest.ClusterURI())\n\t\tintegtest.AddTestServerAPIVersion(co)\n\t\tencryptedClient, err := mongo.Connect(co)\n\t\trequire.NoError(mt, err, \"error on Connect: %v\", err)\n\n\t\tfoobarbaz := bson.RawValue{Type: bson.TypeString, Value: bsoncore.AppendString(nil, \"foobarbaz\")}\n\t\tfor _, c := range []struct {\n\t\t\tcollection string\n\t\t\ttextOpts   *options.TextOptionsBuilder\n\t\t}{\n\t\t\t{\n\t\t\t\tcollection: \"prefix-suffix\",\n\t\t\t\ttextOpts: options.Text().\n\t\t\t\t\tSetCaseSensitive(true).\n\t\t\t\t\tSetDiacriticSensitive(true).\n\t\t\t\t\tSetPrefix(options.PrefixOptions{\n\t\t\t\t\t\tStrMaxQueryLength: 10,\n\t\t\t\t\t\tStrMinQueryLength: 2,\n\t\t\t\t\t}).\n\t\t\t\t\tSetSuffix(options.SuffixOptions{\n\t\t\t\t\t\tStrMaxQueryLength: 10,\n\t\t\t\t\t\tStrMinQueryLength: 2,\n\t\t\t\t\t}),\n\t\t\t},\n\t\t\t{\n\t\t\t\tcollection: \"substring\",\n\t\t\t\ttextOpts: options.Text().\n\t\t\t\t\tSetCaseSensitive(true).\n\t\t\t\t\tSetDiacriticSensitive(true).\n\t\t\t\t\tSetSubstring(options.SubstringOptions{\n\t\t\t\t\t\tStrMaxLength:      10,\n\t\t\t\t\t\tStrMaxQueryLength: 10,\n\t\t\t\t\t\tStrMinQueryLength: 2,\n\t\t\t\t\t}),\n\t\t\t},\n\t\t} {\n\t\t\tcoll := encryptedClient.Database(\"db\").Collection(c.collection, options.Collection().SetWriteConcern(mtest.MajorityWc))\n\t\t\teo := options.Encrypt().\n\t\t\t\tSetKeyID(key1ID).\n\t\t\t\tSetAlgorithm(\"TextPreview\").\n\t\t\t\tSetContentionFactor(0).\n\t\t\t\tSetTextOptions(c.textOpts)\n\t\t\tinsertPayload, err := clientEncryption.Encrypt(context.Background(), foobarbaz, eo)\n\t\t\trequire.NoError(mt, err, \"error in Encrypt: %v\", err)\n\t\t\t_, err = coll.InsertOne(context.Background(), bson.D{{\"_id\", 0}, {\"encryptedText\", insertPayload}})\n\t\t\trequire.NoError(mt, err, \"error in InsertOne: %v\", err)\n\t\t}\n\n\t\treturn encryptedClient, clientEncryption\n\t}\n\n\tfoo := bson.RawValue{Type: bson.TypeString, Value: bsoncore.AppendString(nil, \"foo\")}\n\tbar := bson.RawValue{Type: bson.TypeString, Value: bsoncore.AppendString(nil, \"bar\")}\n\tbaz := bson.RawValue{Type: bson.TypeString, Value: bsoncore.AppendString(nil, \"baz\")}\n\n\tmt.Run(\"Case 1: can find a document by prefix\", func(mt *mtest.T) {\n\t\tencryptedClient, clientEncryption := testSetup()\n\t\tdefer clientEncryption.Close(context.Background())\n\t\tdefer encryptedClient.Disconnect(context.Background())\n\n\t\teo := options.Encrypt().\n\t\t\tSetKeyID(key1ID).\n\t\t\tSetAlgorithm(\"TextPreview\").\n\t\t\tSetQueryType(\"prefixPreview\").\n\t\t\tSetContentionFactor(0).\n\t\t\tSetTextOptions(options.Text().\n\t\t\t\tSetCaseSensitive(true).\n\t\t\t\tSetDiacriticSensitive(true).\n\t\t\t\tSetPrefix(options.PrefixOptions{\n\t\t\t\t\tStrMaxQueryLength: 10,\n\t\t\t\t\tStrMinQueryLength: 2,\n\t\t\t\t}))\n\t\tpayload, err := clientEncryption.Encrypt(context.Background(), foo, eo)\n\t\trequire.NoError(mt, err, \"error in Encrypt: %v\", err)\n\t\tcoll := encryptedClient.Database(\"db\").Collection(\"prefix-suffix\")\n\t\tres := coll.FindOne(context.Background(), bson.D{\n\t\t\t{\"$expr\", bson.D{\n\t\t\t\t{\"$encStrStartsWith\", bson.D{\n\t\t\t\t\t{\"input\", \"$encryptedText\"},\n\t\t\t\t\t{\"prefix\", payload},\n\t\t\t\t}},\n\t\t\t}},\n\t\t})\n\t\tvar got struct {\n\t\t\tId            int    `bson:\"_id\"`\n\t\t\tEncryptedText string `bson:\"encryptedText\"`\n\t\t}\n\t\terr = res.Decode(&got)\n\t\trequire.NoError(mt, err, \"error decoding result: %v\", err)\n\t\trequire.Equal(mt, 0, got.Id)\n\t\trequire.Equal(mt, \"foobarbaz\", got.EncryptedText)\n\t})\n\tmt.Run(\"Case 2: find a document by suffix\", func(mt *mtest.T) {\n\t\tencryptedClient, clientEncryption := testSetup()\n\t\tdefer clientEncryption.Close(context.Background())\n\t\tdefer encryptedClient.Disconnect(context.Background())\n\n\t\teo := options.Encrypt().\n\t\t\tSetKeyID(key1ID).\n\t\t\tSetAlgorithm(\"TextPreview\").\n\t\t\tSetQueryType(\"suffixPreview\").\n\t\t\tSetContentionFactor(0).\n\t\t\tSetTextOptions(options.Text().\n\t\t\t\tSetCaseSensitive(true).\n\t\t\t\tSetDiacriticSensitive(true).\n\t\t\t\tSetSuffix(options.SuffixOptions{\n\t\t\t\t\tStrMaxQueryLength: 10,\n\t\t\t\t\tStrMinQueryLength: 2,\n\t\t\t\t}))\n\t\tpayload, err := clientEncryption.Encrypt(context.Background(), baz, eo)\n\t\trequire.NoError(mt, err, \"error in Encrypt: %v\", err)\n\t\tcoll := encryptedClient.Database(\"db\").Collection(\"prefix-suffix\")\n\t\tres := coll.FindOne(context.Background(), bson.D{\n\t\t\t{\"$expr\", bson.D{\n\t\t\t\t{\"$encStrEndsWith\", bson.D{\n\t\t\t\t\t{\"input\", \"$encryptedText\"},\n\t\t\t\t\t{\"suffix\", payload},\n\t\t\t\t}},\n\t\t\t}},\n\t\t})\n\t\tvar got struct {\n\t\t\tId            int    `bson:\"_id\"`\n\t\t\tEncryptedText string `bson:\"encryptedText\"`\n\t\t}\n\t\terr = res.Decode(&got)\n\t\trequire.NoError(mt, err, \"error decoding result: %v\", err)\n\t\trequire.Equal(mt, 0, got.Id)\n\t\trequire.Equal(mt, \"foobarbaz\", got.EncryptedText)\n\t})\n\tmt.Run(\"Case 3: assert no document found by prefix\", func(mt *mtest.T) {\n\t\tencryptedClient, clientEncryption := testSetup()\n\t\tdefer clientEncryption.Close(context.Background())\n\t\tdefer encryptedClient.Disconnect(context.Background())\n\n\t\teo := options.Encrypt().\n\t\t\tSetKeyID(key1ID).\n\t\t\tSetAlgorithm(\"TextPreview\").\n\t\t\tSetQueryType(\"prefixPreview\").\n\t\t\tSetContentionFactor(0).\n\t\t\tSetTextOptions(options.Text().\n\t\t\t\tSetCaseSensitive(true).\n\t\t\t\tSetDiacriticSensitive(true).\n\t\t\t\tSetPrefix(options.PrefixOptions{\n\t\t\t\t\tStrMaxQueryLength: 10,\n\t\t\t\t\tStrMinQueryLength: 2,\n\t\t\t\t}))\n\t\tpayload, err := clientEncryption.Encrypt(context.Background(), baz, eo)\n\t\trequire.NoError(mt, err, \"error in Encrypt: %v\", err)\n\t\tcoll := encryptedClient.Database(\"db\").Collection(\"prefix-suffix\")\n\t\t_, err = coll.FindOne(context.Background(), bson.D{\n\t\t\t{\"$expr\", bson.D{\n\t\t\t\t{\"$encStrStartsWith\", bson.D{\n\t\t\t\t\t{\"input\", \"$encryptedText\"},\n\t\t\t\t\t{\"prefix\", payload},\n\t\t\t\t}},\n\t\t\t}},\n\t\t}).Raw()\n\t\trequire.Equal(mt, err, mongo.ErrNoDocuments)\n\t})\n\tmt.Run(\"Case 4: assert no document found by suffix\", func(mt *mtest.T) {\n\t\tencryptedClient, clientEncryption := testSetup()\n\t\tdefer clientEncryption.Close(context.Background())\n\t\tdefer encryptedClient.Disconnect(context.Background())\n\n\t\teo := options.Encrypt().\n\t\t\tSetKeyID(key1ID).\n\t\t\tSetAlgorithm(\"TextPreview\").\n\t\t\tSetQueryType(\"suffixPreview\").\n\t\t\tSetContentionFactor(0).\n\t\t\tSetTextOptions(options.Text().\n\t\t\t\tSetCaseSensitive(true).\n\t\t\t\tSetDiacriticSensitive(true).\n\t\t\t\tSetSuffix(options.SuffixOptions{\n\t\t\t\t\tStrMaxQueryLength: 10,\n\t\t\t\t\tStrMinQueryLength: 2,\n\t\t\t\t}))\n\t\tpayload, err := clientEncryption.Encrypt(context.Background(), foo, eo)\n\t\trequire.NoError(mt, err, \"error in Encrypt: %v\", err)\n\t\tcoll := encryptedClient.Database(\"db\").Collection(\"prefix-suffix\")\n\t\t_, err = coll.FindOne(context.Background(), bson.D{\n\t\t\t{\"$expr\", bson.D{\n\t\t\t\t{\"$encStrEndsWith\", bson.D{\n\t\t\t\t\t{\"input\", \"$encryptedText\"},\n\t\t\t\t\t{\"suffix\", payload},\n\t\t\t\t}},\n\t\t\t}},\n\t\t}).Raw()\n\t\trequire.Equal(mt, err, mongo.ErrNoDocuments)\n\t})\n\tmt.Run(\"Case 5: can find a document by substring\", func(mt *mtest.T) {\n\t\tencryptedClient, clientEncryption := testSetup()\n\t\tdefer clientEncryption.Close(context.Background())\n\t\tdefer encryptedClient.Disconnect(context.Background())\n\n\t\teo := options.Encrypt().\n\t\t\tSetKeyID(key1ID).\n\t\t\tSetAlgorithm(\"TextPreview\").\n\t\t\tSetQueryType(\"substringPreview\").\n\t\t\tSetContentionFactor(0).\n\t\t\tSetTextOptions(options.Text().\n\t\t\t\tSetCaseSensitive(true).\n\t\t\t\tSetDiacriticSensitive(true).\n\t\t\t\tSetSubstring(options.SubstringOptions{\n\t\t\t\t\tStrMaxLength:      10,\n\t\t\t\t\tStrMaxQueryLength: 10,\n\t\t\t\t\tStrMinQueryLength: 2,\n\t\t\t\t}))\n\t\tpayload, err := clientEncryption.Encrypt(context.Background(), bar, eo)\n\t\trequire.NoError(mt, err, \"error in Encrypt: %v\", err)\n\t\tcoll := encryptedClient.Database(\"db\").Collection(\"substring\")\n\t\tres := coll.FindOne(context.Background(), bson.D{\n\t\t\t{\"$expr\", bson.D{\n\t\t\t\t{\"$encStrContains\", bson.D{\n\t\t\t\t\t{\"input\", \"$encryptedText\"},\n\t\t\t\t\t{\"substring\", payload},\n\t\t\t\t}},\n\t\t\t}},\n\t\t})\n\t\tvar got struct {\n\t\t\tId            int    `bson:\"_id\"`\n\t\t\tEncryptedText string `bson:\"encryptedText\"`\n\t\t}\n\t\terr = res.Decode(&got)\n\t\trequire.NoError(mt, err, \"error decoding result: %v\", err)\n\t\trequire.Equal(mt, 0, got.Id)\n\t\trequire.Equal(mt, \"foobarbaz\", got.EncryptedText)\n\t})\n\tmt.Run(\"Case 6: assert no document found by substring\", func(mt *mtest.T) {\n\t\tencryptedClient, clientEncryption := testSetup()\n\t\tdefer clientEncryption.Close(context.Background())\n\t\tdefer encryptedClient.Disconnect(context.Background())\n\n\t\tqux := bson.RawValue{Type: bson.TypeString, Value: bsoncore.AppendString(nil, \"qux\")}\n\t\teo := options.Encrypt().\n\t\t\tSetKeyID(key1ID).\n\t\t\tSetAlgorithm(\"TextPreview\").\n\t\t\tSetQueryType(\"substringPreview\").\n\t\t\tSetContentionFactor(0).\n\t\t\tSetTextOptions(options.Text().\n\t\t\t\tSetCaseSensitive(true).\n\t\t\t\tSetDiacriticSensitive(true).\n\t\t\t\tSetSubstring(options.SubstringOptions{\n\t\t\t\t\tStrMaxLength:      10,\n\t\t\t\t\tStrMaxQueryLength: 10,\n\t\t\t\t\tStrMinQueryLength: 2,\n\t\t\t\t}))\n\t\tpayload, err := clientEncryption.Encrypt(context.Background(), qux, eo)\n\t\trequire.NoError(mt, err, \"error in Encrypt: %v\", err)\n\t\tcoll := encryptedClient.Database(\"db\").Collection(\"substring\")\n\t\t_, err = coll.FindOne(context.Background(), bson.D{\n\t\t\t{\"$expr\", bson.D{\n\t\t\t\t{\"$encStrContains\", bson.D{\n\t\t\t\t\t{\"input\", \"$encryptedText\"},\n\t\t\t\t\t{\"substring\", payload},\n\t\t\t\t}},\n\t\t\t}},\n\t\t}).Raw()\n\t\trequire.Equal(mt, err, mongo.ErrNoDocuments)\n\t})\n\tmt.Run(\"Case 7: assert contentionFactor is required\", func(mt *mtest.T) {\n\t\tencryptedClient, clientEncryption := testSetup()\n\t\tdefer clientEncryption.Close(context.Background())\n\t\tdefer encryptedClient.Disconnect(context.Background())\n\n\t\teo := options.Encrypt().\n\t\t\tSetKeyID(key1ID).\n\t\t\tSetAlgorithm(\"TextPreview\").\n\t\t\tSetQueryType(\"prefixPreview\").\n\t\t\tSetTextOptions(options.Text().\n\t\t\t\tSetCaseSensitive(true).\n\t\t\t\tSetDiacriticSensitive(true).\n\t\t\t\tSetPrefix(options.PrefixOptions{\n\t\t\t\t\tStrMaxQueryLength: 10,\n\t\t\t\t\tStrMinQueryLength: 2,\n\t\t\t\t}))\n\t\t_, err := clientEncryption.Encrypt(context.Background(), baz, eo)\n\t\trequire.ErrorContains(mt, err, \"contention factor is required for textPreview algorithm\")\n\t})\n}\n\nfunc TestChangeStreams(t *testing.T) {\n\tmt := newCSE_T(t, mtest.NewOptions().CreateClient(false).Topologies(mtest.ReplicaSet))\n\tmt.Setup()\n\n\t// Change streams can't easily fit into the spec test format because of their tailable nature, so there are two\n\t// prose tests for them instead:\n\t//\n\t// 1. Auto-encryption errors for Watch operations. Collection-level change streams error because the\n\t// $changeStream aggregation stage is not valid for encryption. Client and database-level streams error because\n\t// only collection-level operations are valid for encryption.\n\t//\n\t// 2. Events are automatically decrypted: If the Watch() is done with BypassAutoEncryption=true, the Watch\n\t// should succeed and subsequent getMore calls should decrypt documents when necessary.\n\n\tvar testConfig struct {\n\t\tJSONSchema        bson.Raw   `bson:\"json_schema\"`\n\t\tKeyVaultData      []bson.Raw `bson:\"key_vault_data\"`\n\t\tEncryptedDocument bson.Raw   `bson:\"encrypted_document\"`\n\t\tDecryptedDocument bson.Raw   `bson:\"decrytped_document\"`\n\t}\n\tdecodeJSONFile(mt, \"change-streams-test.json\", &testConfig)\n\n\tschemaMap := map[string]any{\n\t\t\"db.coll\": testConfig.JSONSchema,\n\t}\n\tkmsProviders := map[string]map[string]any{\n\t\t\"aws\": {\n\t\t\t\"accessKeyId\":     awsAccessKeyID,\n\t\t\t\"secretAccessKey\": awsSecretAccessKey,\n\t\t},\n\t}\n\n\ttestCases := []struct {\n\t\tname       string\n\t\tstreamType mongo.StreamType\n\t}{\n\t\t{\"client\", mongo.ClientStream},\n\t\t{\"database\", mongo.DatabaseStream},\n\t\t{\"collection\", mongo.CollectionStream},\n\t}\n\tmt.RunOpts(\"auto encryption errors\", noClientOpts, func(mt *mtest.T) {\n\t\tmt.Parallel()\n\n\t\tfor _, tc := range testCases {\n\t\t\tmt.Run(tc.name, func(mt *mtest.T) {\n\t\t\t\tautoEncryptionOpts := options.AutoEncryption().\n\t\t\t\t\tSetKmsProviders(kmsProviders).\n\t\t\t\t\tSetKeyVaultNamespace(kvNamespace).\n\t\t\t\t\tSetSchemaMap(schemaMap).\n\t\t\t\t\tSetExtraOptions(getCryptSharedLibExtraOptions())\n\t\t\t\tcpt := setup(mt, autoEncryptionOpts, nil, nil)\n\t\t\t\tdefer cpt.teardown(mt)\n\n\t\t\t\t_, err := getWatcher(mt, tc.streamType, cpt).Watch(context.Background(), mongo.Pipeline{})\n\t\t\t\tassert.NotNil(mt, err, \"expected Watch error: %v\", err)\n\t\t\t})\n\t\t}\n\t})\n\tmt.RunOpts(\"events are automatically decrypted\", noClientOpts, func(mt *mtest.T) {\n\t\tfor _, tc := range testCases {\n\t\t\tmt.Run(tc.name, func(mt *mtest.T) {\n\t\t\t\tautoEncryptionOpts := options.AutoEncryption().\n\t\t\t\t\tSetKmsProviders(kmsProviders).\n\t\t\t\t\tSetKeyVaultNamespace(kvNamespace).\n\t\t\t\t\tSetSchemaMap(schemaMap).\n\t\t\t\t\tSetBypassAutoEncryption(true)\n\t\t\t\tcpt := setup(mt, autoEncryptionOpts, nil, nil)\n\t\t\t\tdefer cpt.teardown(mt)\n\n\t\t\t\t// Insert key vault data so the key can be accessed when starting the change stream.\n\t\t\t\tinsertDocuments(mt, cpt.keyVaultColl, testConfig.KeyVaultData)\n\n\t\t\t\tstream, err := getWatcher(mt, tc.streamType, cpt).Watch(context.Background(), mongo.Pipeline{})\n\t\t\t\tassert.Nil(mt, err, \"Watch error: %v\", err)\n\t\t\t\tdefer stream.Close(context.Background())\n\n\t\t\t\t// Insert already encrypted data and verify that it is automatically decrypted by Next().\n\t\t\t\tinsertDocuments(mt, cpt.coll, []bson.Raw{testConfig.EncryptedDocument})\n\t\t\t\tassert.True(mt, stream.Next(context.Background()), \"expected Next to return true, got false\")\n\t\t\t\tgotValue, err := stream.Current.LookupErr(\"fullDocument\")\n\t\t\t\tassert.Nil(mt, err, \"did not find fullDocument in stream.Current: %v\", stream.Current)\n\t\t\t\tgotDocument := gotValue.Document()\n\t\t\t\terr = compareDocs(mt, testConfig.DecryptedDocument, gotDocument)\n\t\t\t\tassert.Nil(mt, err, \"compareDocs error: %v\", err)\n\t\t\t})\n\t\t}\n\t})\n}\n\nfunc getWatcher(mt *mtest.T, streamType mongo.StreamType, cpt *cseProseTest) watcher {\n\tmt.Helper()\n\n\tswitch streamType {\n\tcase mongo.ClientStream:\n\t\treturn cpt.cseClient\n\tcase mongo.DatabaseStream:\n\t\treturn cpt.cseColl.Database()\n\tcase mongo.CollectionStream:\n\t\treturn cpt.cseColl\n\tdefault:\n\t\tmt.Fatalf(\"unknown stream type %v\", streamType)\n\t}\n\treturn nil\n}\n\ntype cseProseTest struct {\n\tcoll         *mongo.Collection // collection db.coll\n\tkvClient     *mongo.Client\n\tkeyVaultColl *mongo.Collection\n\tcseClient    *mongo.Client     // encrypted client\n\tcseColl      *mongo.Collection // db.coll with encrypted client\n\tclientEnc    *mongo.ClientEncryption\n\tcseStarted   []*event.CommandStartedEvent\n}\n\nfunc setup(mt *mtest.T, aeo *options.AutoEncryptionOptions, kvClientOpts *options.ClientOptions,\n\tceo options.Lister[options.ClientEncryptionOptions],\n) *cseProseTest {\n\tmt.Helper()\n\tvar cpt cseProseTest\n\tvar err error\n\tcpt.coll = mt.CreateCollection(mtest.Collection{\n\t\tName: \"coll\",\n\t\tDB:   \"db\",\n\t\tOpts: options.Collection().SetWriteConcern(mtest.MajorityWc),\n\t}, false)\n\tcpt.keyVaultColl = mt.CreateCollection(mtest.Collection{\n\t\tName: \"datakeys\",\n\t\tDB:   \"keyvault\",\n\t\tOpts: options.Collection().SetWriteConcern(mtest.MajorityWc),\n\t}, false)\n\n\tif aeo != nil {\n\t\tcseMonitor := &event.CommandMonitor{\n\t\t\tStarted: func(_ context.Context, evt *event.CommandStartedEvent) {\n\t\t\t\tcpt.cseStarted = append(cpt.cseStarted, evt)\n\t\t\t},\n\t\t}\n\t\topts := options.Client().ApplyURI(mtest.ClusterURI()).SetWriteConcern(mtest.MajorityWc).\n\t\t\tSetReadPreference(mtest.PrimaryRp).SetAutoEncryptionOptions(aeo).SetMonitor(cseMonitor)\n\t\tintegtest.AddTestServerAPIVersion(opts)\n\t\tcpt.cseClient, err = mongo.Connect(opts)\n\t\tassert.Nil(mt, err, \"Connect error for encrypted client: %v\", err)\n\t\tcpt.cseColl = cpt.cseClient.Database(\"db\").Collection(\"coll\")\n\t}\n\tif ceo != nil {\n\t\tintegtest.AddTestServerAPIVersion(kvClientOpts)\n\t\tcpt.kvClient, err = mongo.Connect(kvClientOpts)\n\t\tassert.Nil(mt, err, \"Connect error for ClientEncryption key vault client: %v\", err)\n\t\tcpt.clientEnc, err = mongo.NewClientEncryption(cpt.kvClient, ceo)\n\t\tassert.Nil(mt, err, \"NewClientEncryption error: %v\", err)\n\t}\n\treturn &cpt\n}\n\nfunc (cpt *cseProseTest) teardown(mt *mtest.T) {\n\tmt.Helper()\n\n\tif cpt.cseClient != nil {\n\t\t_ = cpt.cseClient.Disconnect(context.Background())\n\t}\n\tif cpt.clientEnc != nil {\n\t\t_ = cpt.clientEnc.Close(context.Background())\n\t}\n}\n\nfunc readJSONFile(mt *mtest.T, file string) bson.Raw {\n\tmt.Helper()\n\n\tcontent, err := ioutil.ReadFile(filepath.Join(clientEncryptionProseDir, file))\n\tassert.Nil(mt, err, \"ReadFile error for %v: %v\", file, err)\n\n\tvar doc bson.Raw\n\terr = bson.UnmarshalExtJSON(content, true, &doc)\n\tassert.Nil(mt, err, \"UnmarshalExtJSON error for file %v: %v\", file, err)\n\treturn doc\n}\n\nfunc decodeJSONFile(mt *mtest.T, file string, val any) bson.Raw {\n\tmt.Helper()\n\n\tcontent, err := ioutil.ReadFile(filepath.Join(clientEncryptionProseDir, file))\n\tassert.Nil(mt, err, \"ReadFile error for %v: %v\", file, err)\n\n\tvar doc bson.Raw\n\terr = bson.UnmarshalExtJSON(content, true, val)\n\tassert.Nil(mt, err, \"UnmarshalExtJSON error for file %v: %v\", file, err)\n\treturn doc\n}\n\nfunc rawValueToCoreValue(rv bson.RawValue) bsoncore.Value {\n\treturn bsoncore.Value{Type: bsoncore.Type(rv.Type), Data: rv.Value}\n}\n\ntype deadlockTest struct {\n\tclientTest           *mongo.Client\n\tclientKeyVaultOpts   *options.ClientOptions\n\tclientKeyVaultEvents []startedEvent\n\tclientEncryption     *mongo.ClientEncryption\n\tciphertext           bson.Binary\n}\n\ntype startedEvent struct {\n\tCommand  string\n\tDatabase string\n}\n\nfunc newDeadlockTest(mt *mtest.T) *deadlockTest {\n\tmt.Helper()\n\n\tvar d deadlockTest\n\tvar err error\n\n\tclientTestOpts := options.Client().ApplyURI(mtest.ClusterURI()).SetWriteConcern(mtest.MajorityWc)\n\tintegtest.AddTestServerAPIVersion(clientTestOpts)\n\tif d.clientTest, err = mongo.Connect(clientTestOpts); err != nil {\n\t\tmt.Fatalf(\"Connect error: %v\", err)\n\t}\n\n\tclientKeyVaultMonitor := &event.CommandMonitor{\n\t\tStarted: func(ctx context.Context, event *event.CommandStartedEvent) {\n\t\t\tstartedEvent := startedEvent{event.CommandName, event.DatabaseName}\n\t\t\td.clientKeyVaultEvents = append(d.clientKeyVaultEvents, startedEvent)\n\t\t},\n\t}\n\n\td.clientKeyVaultOpts = options.Client().ApplyURI(mtest.ClusterURI()).\n\t\tSetMaxPoolSize(1).SetMonitor(clientKeyVaultMonitor)\n\n\tkeyvaultColl := d.clientTest.Database(\"keyvault\").Collection(\"datakeys\")\n\tdataColl := d.clientTest.Database(\"db\").Collection(\"coll\")\n\terr = dataColl.Drop(context.Background())\n\tassert.Nil(mt, err, \"Drop error for collection db.coll: %v\", err)\n\n\terr = keyvaultColl.Drop(context.Background())\n\tassert.Nil(mt, err, \"Drop error for key vault collection: %v\", err)\n\n\tkeyDoc := readJSONFile(mt, \"external-key.json\")\n\t_, err = keyvaultColl.InsertOne(context.Background(), keyDoc)\n\tassert.Nil(mt, err, \"InsertOne error into key vault collection: %v\", err)\n\n\tschemaDoc := readJSONFile(mt, \"external-schema.json\")\n\tcreateOpts := options.CreateCollection().SetValidator(bson.M{\"$jsonSchema\": schemaDoc})\n\terr = d.clientTest.Database(\"db\").CreateCollection(context.Background(), \"coll\", createOpts)\n\tassert.Nil(mt, err, \"CreateCollection error: %v\", err)\n\n\tkmsProviders := map[string]map[string]any{\n\t\t\"local\": {\"key\": localMasterKey},\n\t}\n\tceOpts := options.ClientEncryption().SetKmsProviders(kmsProviders).SetKeyVaultNamespace(\"keyvault.datakeys\")\n\td.clientEncryption, err = mongo.NewClientEncryption(d.clientTest, ceOpts)\n\tassert.Nil(mt, err, \"NewClientEncryption error: %v\", err)\n\n\tt, value, err := bson.MarshalValue(\"string0\")\n\tassert.Nil(mt, err, \"MarshalValue error: %v\", err)\n\tin := bson.RawValue{Type: t, Value: value}\n\teopts := options.Encrypt().SetAlgorithm(\"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\").SetKeyAltName(\"local\")\n\td.ciphertext, err = d.clientEncryption.Encrypt(context.Background(), in, eopts)\n\tassert.Nil(mt, err, \"Encrypt error: %v\", err)\n\n\treturn &d\n}\n\nfunc (d *deadlockTest) disconnect(mt *mtest.T) {\n\tmt.Helper()\n\terr := d.clientEncryption.Close(context.Background())\n\tassert.Nil(mt, err, \"clientEncryption Close error: %v\", err)\n\td.clientTest.Disconnect(context.Background())\n\tassert.Nil(mt, err, \"clientTest Disconnect error: %v\", err)\n}\n\n// listenForConnections creates a listener that will listen for connections.\n// The user provided run function will be called with the accepted\n// connection. The user is responsible for calling Close on the returned listener.\nfunc listenForConnections(t *testing.T, run func(net.Conn)) net.Listener {\n\tt.Helper()\n\n\tl, err := net.Listen(\"tcp\", \"localhost:0\")\n\tif err != nil {\n\t\tt.Errorf(\"Could not set up a listener: %v\", err)\n\t\tt.FailNow()\n\t}\n\tgo func() {\n\t\tfor {\n\t\t\t// Ignore errors due to closing the listener connection.\n\t\t\tc, _ := l.Accept()\n\t\t\tif c == nil {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tgo run(c)\n\t\t}\n\t}()\n\treturn l\n}\n\ntype localTransport struct {\n\trt http.RoundTripper\n\n\theader http.Header\n}\n\nfunc (t *localTransport) RoundTrip(req *http.Request) (*http.Response, error) {\n\tr := req.Clone(req.Context())\n\tr.URL.Host = \"localhost:8080\"\n\tfor key, vals := range t.header {\n\t\tfor _, val := range vals {\n\t\t\tr.Header.Add(key, val)\n\t\t}\n\t}\n\treturn t.rt.RoundTrip(r)\n}\n"
  },
  {
    "path": "internal/integration/client_side_encryption_spec_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\n//go:build cse\n\npackage integration\n\nimport (\n\t\"os\"\n\t\"path/filepath\"\n\t\"regexp\"\n\t\"testing\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/spectest\"\n)\n\nvar encryptionSpecDir = spectest.Path(\"client-side-encryption/tests/legacy\")\n\nfunc verifyClientSideEncryptionVarsSet(t *testing.T) {\n\tt.Helper()\n\n\t// Existence of temporary AWS credentials (awsTempAccessKeyID, awsTempSessionToken and\n\t// awsTempSessionToken) is verified when the variables are used in json_helpers_test\n\t// because temporary AWS credentials are not always set.\n\n\tif awsAccessKeyID == \"\" {\n\t\tt.Fatal(\"AWS access key ID not set\")\n\t}\n\tif awsSecretAccessKey == \"\" {\n\t\tt.Fatal(\"AWS secret access key not set\")\n\t}\n\tif azureTenantID == \"\" {\n\t\tt.Fatal(\"azure tenant ID not set\")\n\t}\n\tif azureClientID == \"\" {\n\t\tt.Fatal(\"azure client ID not set\")\n\t}\n\tif azureClientSecret == \"\" {\n\t\tt.Fatal(\"azure client secret not set\")\n\t}\n\tif gcpEmail == \"\" {\n\t\tt.Fatal(\"GCP email not set\")\n\t}\n\tif gcpPrivateKey == \"\" {\n\t\tt.Fatal(\"GCP private key not set\")\n\t}\n}\n\nfunc TestClientSideEncryptionSpec(t *testing.T) {\n\tverifyClientSideEncryptionVarsSet(t)\n\n\tfor _, fileName := range jsonFilesInDir(t, encryptionSpecDir) {\n\t\tt.Run(fileName, func(t *testing.T) {\n\t\t\tre := regexp.MustCompile(`fle2\\-Range\\-.*\\-Correctness`)\n\t\t\tif re.Match([]byte(fileName)) {\n\t\t\t\tt.Skipf(\"skipping test on macOS due to slow runtime\")\n\t\t\t}\n\t\t\tif fileName == \"kmipKMS.json\" && \"\" == os.Getenv(\"KMS_MOCK_SERVERS_RUNNING\") {\n\t\t\t\tt.Skipf(\"Skipping test as KMS_MOCK_SERVERS_RUNNING is not set\")\n\t\t\t}\n\t\t\trunSpecTestFile(t, filepath.Join(encryptionSpecDir, fileName))\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "internal/integration/client_side_encryption_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2021-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\n//go:build cse\n\npackage integration\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/event\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/integration/mtest\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/integtest\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver\"\n\tmcopts \"go.mongodb.org/mongo-driver/v2/x/mongo/driver/mongocrypt/options\"\n)\n\n// createDataKeyAndEncrypt creates a data key with the alternate name @keyName.\n// Returns a ciphertext encrypted with the data key as test data.\nfunc createDataKeyAndEncrypt(mt *mtest.T, keyName string) bson.Binary {\n\tmt.Helper()\n\n\tkvClientOpts := options.Client().\n\t\tApplyURI(mtest.ClusterURI()).\n\t\tSetReadConcern(mtest.MajorityRc).\n\t\tSetWriteConcern(mtest.MajorityWc)\n\n\tintegtest.AddTestServerAPIVersion(kvClientOpts)\n\n\tkmsProvidersMap := map[string]map[string]any{\n\t\t\"local\": {\"key\": localMasterKey},\n\t}\n\n\tkvClient, err := mongo.Connect(kvClientOpts)\n\tdefer kvClient.Disconnect(context.Background())\n\tassert.Nil(mt, err, \"Connect error: %v\", err)\n\n\terr = kvClient.Database(\"keyvault\").Collection(\"datakeys\").Drop(context.Background())\n\tassert.Nil(mt, err, \"Drop error: %v\", err)\n\n\tceOpts := options.ClientEncryption().\n\t\tSetKmsProviders(kmsProvidersMap).\n\t\tSetKeyVaultNamespace(\"keyvault.datakeys\")\n\n\tce, err := mongo.NewClientEncryption(kvClient, ceOpts)\n\tassert.Nil(mt, err, \"NewClientEncryption error: %v\", err)\n\n\tdkOpts := options.DataKey().SetKeyAltNames([]string{keyName})\n\t_, err = ce.CreateDataKey(context.Background(), \"local\", dkOpts)\n\tassert.Nil(mt, err, \"CreateDataKey error: %v\", err)\n\n\tin := bson.RawValue{Type: bson.TypeString, Value: bsoncore.AppendString(nil, \"test\")}\n\teOpts := options.Encrypt().\n\t\tSetAlgorithm(\"AEAD_AES_256_CBC_HMAC_SHA_512-Random\").\n\t\tSetKeyAltName(keyName)\n\n\tciphertext, err := ce.Encrypt(context.Background(), in, eOpts)\n\tassert.Nil(mt, err, \"Encrypt error: %v\", err)\n\treturn ciphertext\n}\n\nfunc getLsid(mt *mtest.T, doc bson.Raw) bson.Raw {\n\tmt.Helper()\n\n\tlsid, err := doc.LookupErr(\"lsid\")\n\tassert.Nil(mt, err, \"expected lsid in document: %v\", doc)\n\tlsidDoc, ok := lsid.DocumentOK()\n\tassert.True(mt, ok, \"expected lsid to be document, but got: %v\", lsid)\n\treturn lsidDoc\n}\n\nfunc makeMonitor(mt *mtest.T, captured *[]event.CommandStartedEvent) *event.CommandMonitor {\n\tmt.Helper()\n\tassert.NotNil(mt, captured, \"captured is nil\")\n\n\treturn &event.CommandMonitor{\n\t\tStarted: func(_ context.Context, cse *event.CommandStartedEvent) {\n\t\t\tassert.NotNil(mt, cse, \"expected non-Nil CommandStartedEvent\")\n\t\t\t*captured = append(*captured, *cse)\n\t\t},\n\t}\n}\n\nfunc TestClientSideEncryptionWithExplicitSessions(t *testing.T) {\n\tverifyClientSideEncryptionVarsSet(t)\n\tmt := mtest.New(t, mtest.NewOptions().MinServerVersion(\"4.2\").Enterprise(true).CreateClient(false))\n\n\tkmsProvidersMap := map[string]map[string]any{\n\t\t\"local\": {\"key\": localMasterKey},\n\t}\n\n\tschema := bson.D{\n\t\t{\"bsonType\", \"object\"},\n\t\t{\"properties\", bson.D{\n\t\t\t{\"encryptMe\", bson.D{\n\t\t\t\t{\"encrypt\", bson.D{\n\t\t\t\t\t{\"keyId\", \"/keyName\"},\n\t\t\t\t\t{\"bsonType\", \"string\"},\n\t\t\t\t\t{\"algorithm\", \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\"},\n\t\t\t\t}},\n\t\t\t}},\n\t\t}},\n\t}\n\tschemaMap := map[string]any{\"db.coll\": schema}\n\n\tmt.Run(\"automatic encryption\", func(mt *mtest.T) {\n\t\tcreateDataKeyAndEncrypt(mt, \"myKey\")\n\n\t\taeOpts := options.AutoEncryption().\n\t\t\tSetKmsProviders(kmsProvidersMap).\n\t\t\tSetKeyVaultNamespace(\"keyvault.datakeys\").\n\t\t\tSetSchemaMap(schemaMap).\n\t\t\tSetExtraOptions(getCryptSharedLibExtraOptions())\n\n\t\tvar capturedEvents []event.CommandStartedEvent\n\n\t\tclientOpts := options.Client().\n\t\t\tApplyURI(mtest.ClusterURI()).\n\t\t\tSetReadConcern(mtest.MajorityRc).\n\t\t\tSetWriteConcern(mtest.MajorityWc).\n\t\t\tSetAutoEncryptionOptions(aeOpts).\n\t\t\tSetMonitor(makeMonitor(mt, &capturedEvents))\n\n\t\tintegtest.AddTestServerAPIVersion(clientOpts)\n\n\t\tclient, err := mongo.Connect(clientOpts)\n\t\tassert.Nil(mt, err, \"Connect error: %v\", err)\n\t\tdefer client.Disconnect(context.Background())\n\n\t\tcoll := client.Database(\"db\").Collection(\"coll\")\n\t\terr = coll.Drop(context.Background())\n\t\tassert.Nil(mt, err, \"Drop error: %v\", err)\n\n\t\tsession, err := client.StartSession()\n\t\tassert.Nil(mt, err, \"StartSession error: %v\", err)\n\t\tsessionCtx := mongo.NewSessionContext(context.Background(), session)\n\n\t\tcapturedEvents = make([]event.CommandStartedEvent, 0)\n\t\t_, err = coll.InsertOne(sessionCtx, bson.D{{\"encryptMe\", \"test\"}, {\"keyName\", \"myKey\"}})\n\t\tassert.Nil(mt, err, \"InsertOne error: %v\", err)\n\n\t\tassert.Equal(mt, len(capturedEvents), 2, \"expected 2 events, got %v\", len(capturedEvents))\n\n\t\t// Assert the first event is a find on the keyvault.datakeys collection.\n\t\tevent := capturedEvents[0]\n\t\tassert.Equal(mt, event.CommandName, \"find\", \"expected command find, got %q\", event.CommandName)\n\t\tassert.Equal(mt, event.DatabaseName, \"keyvault\", \"expected find on keyvault, got %q\", event.DatabaseName)\n\n\t\t// Assert the find used an implicit session with an lsid != session.ID()\n\t\tlsid := getLsid(mt, event.Command)\n\t\tassert.Nil(mt, err, \"lsid not found in %v\", event.Command)\n\t\tassert.NotEqual(mt, lsid, session.ID(), \"expected different lsid, but got %v\", lsid)\n\n\t\t// Assert the second event is the original insert.\n\t\tevent = capturedEvents[1]\n\t\tassert.Equal(mt, event.CommandName, \"insert\", \"expected command insert, got %q\", event.CommandName)\n\n\t\t// Assert the insert used the explicit session.\n\t\tlsid = getLsid(mt, event.Command)\n\t\tassert.Nil(mt, err, \"lsid not found on %v\", event.Command)\n\t\tassert.Equal(mt, lsid, session.ID(), \"expected lsid %v, but got %v\", session.ID(), lsid)\n\n\t\t// Check that encryptMe is encrypted.\n\t\tencryptMe, err := event.Command.LookupErr(\"documents\", \"0\", \"encryptMe\")\n\t\tassert.Nil(mt, err, \"could not find encryptMe in %v\", event.Command)\n\t\tassert.Equal(mt, encryptMe.Type, bson.TypeBinary, \"expected Binary, got %v\", encryptMe.Type)\n\t})\n\n\tmt.Run(\"automatic decryption\", func(mt *mtest.T) {\n\t\tciphertext := createDataKeyAndEncrypt(mt, \"myKey\")\n\n\t\taeOpts := options.AutoEncryption().\n\t\t\tSetKmsProviders(kmsProvidersMap).\n\t\t\tSetKeyVaultNamespace(\"keyvault.datakeys\").\n\t\t\tSetBypassAutoEncryption(true)\n\n\t\tvar capturedEvents []event.CommandStartedEvent\n\n\t\tclientOpts := options.Client().\n\t\t\tApplyURI(mtest.ClusterURI()).\n\t\t\tSetReadConcern(mtest.MajorityRc).\n\t\t\tSetWriteConcern(mtest.MajorityWc).\n\t\t\tSetAutoEncryptionOptions(aeOpts).\n\t\t\tSetMonitor(makeMonitor(mt, &capturedEvents))\n\n\t\tintegtest.AddTestServerAPIVersion(clientOpts)\n\n\t\tclient, err := mongo.Connect(clientOpts)\n\t\tassert.Nil(mt, err, \"Connect error: %v\", err)\n\t\tdefer client.Disconnect(context.Background())\n\n\t\tcoll := client.Database(\"db\").Collection(\"coll\")\n\t\terr = coll.Drop(context.Background())\n\t\tassert.Nil(mt, err, \"Drop error: %v\", err)\n\t\t_, err = coll.InsertOne(context.Background(), bson.D{{\"encryptMe\", ciphertext}})\n\t\tassert.Nil(mt, err, \"InsertOne error: %v\", err)\n\n\t\tsession, err := client.StartSession()\n\t\tassert.Nil(mt, err, \"StartSession error: %v\", err)\n\t\tsessionCtx := mongo.NewSessionContext(context.Background(), session)\n\n\t\tcapturedEvents = make([]event.CommandStartedEvent, 0)\n\t\tres := coll.FindOne(sessionCtx, bson.D{{}})\n\t\tassert.Nil(mt, res.Err(), \"FindOne error: %v\", res.Err())\n\n\t\tassert.Equal(mt, len(capturedEvents), 2, \"expected 2 events, got %v\", len(capturedEvents))\n\n\t\t// Assert the first event is the original find.\n\t\tevent := capturedEvents[0]\n\t\tassert.Equal(mt, event.CommandName, \"find\", \"expected command find, got %q\", event.CommandName)\n\t\tassert.Equal(mt, event.DatabaseName, \"db\", \"expected find on db, got %q\", event.DatabaseName)\n\n\t\t// Assert the find used the explicit session\n\t\tlsid := getLsid(mt, event.Command)\n\t\tassert.Nil(mt, err, \"lsid not found on %v\", event.Command)\n\t\tassert.Equal(mt, lsid, session.ID(), \"expected lsid %v, but got %v\", session.ID(), lsid)\n\n\t\t// Assert the second event is the find on the keyvault.datakeys collection.\n\t\tevent = capturedEvents[1]\n\t\tassert.Equal(mt, event.CommandName, \"find\", \"expected command find, got %q\", event.CommandName)\n\t\tassert.Equal(mt, event.DatabaseName, \"keyvault\", \"expected find on keyvault, got %q\", event.DatabaseName)\n\n\t\t// Assert the find used an implicit session with an lsid != session.ID()\n\t\tlsid = getLsid(mt, event.Command)\n\t\tassert.Nil(mt, err, \"lsid not found on %v\", event.Command)\n\t\tassert.NotEqual(mt, lsid, session.ID(), \"expected different lsid, but got %v\", lsid)\n\t})\n}\n\n// customCrypt is a test implementation of the driver.Crypt interface. It keeps track of the number of times its\n// methods have been called.\ntype customCrypt struct {\n\tnumEncryptCalls                   int\n\tnumDecryptCalls                   int\n\tnumCreateDataKeyCalls             int\n\tnumEncryptExplicitCalls           int\n\tnumEncryptExplicitExpressionCalls int\n\tnumDecryptExplicitCalls           int\n\tnumCloseCalls                     int\n\tnumBypassAutoEncryptionCalls      int\n\tnumRewrapDataKeyCalls             int\n}\n\nvar (\n\t_     driver.Crypt = (*customCrypt)(nil)\n\tmySSN              = \"123456789\"\n)\n\n// Encrypt encrypts the given command.\nfunc (c *customCrypt) Encrypt(_ context.Context, _ string, cmd bsoncore.Document) (bsoncore.Document, error) {\n\tc.numEncryptCalls++\n\telems, err := cmd.Elements()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tencryptedCmd := bsoncore.NewDocumentBuilder()\n\tfor _, elem := range elems {\n\t\t// \"encrypt\" ssn element as \"hidden\"\n\t\tif elem.Key() == \"ssn\" {\n\t\t\tencryptedCmd = encryptedCmd.AppendString(\"ssn\", \"hidden\")\n\t\t} else {\n\t\t\tencryptedCmd = encryptedCmd.AppendValue(elem.Key(), elem.Value())\n\t\t}\n\t}\n\treturn encryptedCmd.Build(), nil\n}\n\n// Decrypt decrypts the given command response.\nfunc (c *customCrypt) Decrypt(_ context.Context, cmdResponse bsoncore.Document) (bsoncore.Document, error) {\n\tc.numDecryptCalls++\n\telems, err := cmdResponse.Elements()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tdecryptedCmdResponse := bsoncore.NewDocumentBuilder()\n\tfor _, elem := range elems {\n\t\t// \"decrypt\" ssn element as mySSN\n\t\tif elem.Key() == \"ssn\" {\n\t\t\tdecryptedCmdResponse = decryptedCmdResponse.AppendString(\"ssn\", mySSN)\n\t\t} else {\n\t\t\tdecryptedCmdResponse = decryptedCmdResponse.AppendValue(elem.Key(), elem.Value())\n\t\t}\n\t}\n\treturn decryptedCmdResponse.Build(), nil\n}\n\n// CreateDataKey implements the driver.Crypt interface.\nfunc (c *customCrypt) CreateDataKey(_ context.Context, _ string, _ *mcopts.DataKeyOptions) (bsoncore.Document, error) {\n\tc.numCreateDataKeyCalls++\n\treturn nil, nil\n}\n\n// EncryptExplicit implements the driver.Crypt interface.\nfunc (c *customCrypt) EncryptExplicit(_ context.Context, _ bsoncore.Value, _ *mcopts.ExplicitEncryptionOptions) (byte, []byte, error) {\n\tc.numEncryptExplicitCalls++\n\treturn 0, nil, nil\n}\n\n// EncryptExplicit implements the driver.Crypt interface.\nfunc (c *customCrypt) EncryptExplicitExpression(_ context.Context, _ bsoncore.Document, _ *mcopts.ExplicitEncryptionOptions) (bsoncore.Document, error) {\n\tc.numEncryptExplicitExpressionCalls++\n\treturn nil, nil\n}\n\n// DecryptExplicit implements the driver.Crypt interface.\nfunc (c *customCrypt) DecryptExplicit(_ context.Context, _ byte, _ []byte) (bsoncore.Value, error) {\n\tc.numDecryptExplicitCalls++\n\treturn bsoncore.Value{}, nil\n}\n\n// Close implements the driver.Crypt interface.\nfunc (c *customCrypt) Close() {\n\tc.numCloseCalls++\n}\n\n// BypassAutoEncryption implements the driver.Crypt interface.\nfunc (c *customCrypt) BypassAutoEncryption() bool {\n\tc.numBypassAutoEncryptionCalls++\n\treturn false\n}\n\n// RewrapDataKey attempts to rewrap the document data keys matching the filter, preparing the re-wrapped documents to\n// be returned as a slice of bsoncore.Document.\nfunc (c *customCrypt) RewrapDataKey(_ context.Context, _ []byte,\n\t_ *mcopts.RewrapManyDataKeyOptions,\n) ([]bsoncore.Document, error) {\n\tc.numRewrapDataKeyCalls++\n\treturn nil, nil\n}\n\nfunc TestClientSideEncryptionCustomCrypt(t *testing.T) {\n\tmt := mtest.New(t, mtest.NewOptions().MinServerVersion(\"4.2\").Enterprise(true).CreateClient(false))\n\n\tkmsProvidersMap := map[string]map[string]any{\n\t\t\"local\": {\"key\": localMasterKey},\n\t}\n\n\tmt.Run(\"auto encryption and decryption\", func(mt *mtest.T) {\n\t\taeOpts := options.AutoEncryption().\n\t\t\tSetKmsProviders(kmsProvidersMap).\n\t\t\tSetKeyVaultNamespace(\"keyvault.datakeys\").\n\t\t\tSetExtraOptions(getCryptSharedLibExtraOptions())\n\t\tclientOpts := options.Client().\n\t\t\tApplyURI(mtest.ClusterURI()).\n\t\t\tSetAutoEncryptionOptions(aeOpts)\n\t\tcc := &customCrypt{}\n\t\tclientOpts.Crypt = cc\n\t\tintegtest.AddTestServerAPIVersion(clientOpts)\n\n\t\tclient, err := mongo.Connect(clientOpts)\n\t\tdefer client.Disconnect(context.Background())\n\t\tassert.Nil(mt, err, \"Connect error: %v\", err)\n\n\t\tcoll := client.Database(\"db\").Collection(\"coll\")\n\t\tdefer func() { _ = coll.Drop(context.Background()) }()\n\n\t\tdoc := bson.D{{\"foo\", \"bar\"}, {\"ssn\", mySSN}}\n\t\t_, err = coll.InsertOne(context.Background(), doc)\n\t\tassert.Nil(mt, err, \"InsertOne error: %v\", err)\n\n\t\tres := coll.FindOne(context.Background(), bson.D{{\"foo\", \"bar\"}})\n\t\tassert.Nil(mt, res.Err(), \"FindOne error: %v\", err)\n\n\t\trawRes, err := res.Raw()\n\t\tassert.Nil(mt, err, \"Raw error: %v\", err)\n\t\tssn, ok := rawRes.Lookup(\"ssn\").StringValueOK()\n\t\tassert.True(mt, ok, \"expected 'ssn' value to be type string, got %T\", ssn)\n\t\tassert.Equal(mt, ssn, mySSN, \"expected 'ssn' value %q, got %q\", mySSN, ssn)\n\n\t\t// Assert customCrypt methods are called the correct number of times.\n\t\tassert.Equal(mt, cc.numEncryptCalls, 1,\n\t\t\t\"expected 1 call to Encrypt, got %v\", cc.numEncryptCalls)\n\t\tassert.Equal(mt, cc.numDecryptCalls, 1,\n\t\t\t\"expected 1 call to Decrypt, got %v\", cc.numDecryptCalls)\n\t\tassert.Equal(mt, cc.numCreateDataKeyCalls, 0,\n\t\t\t\"expected 0 calls to CreateDataKey, got %v\", cc.numCreateDataKeyCalls)\n\t\tassert.Equal(mt, cc.numEncryptExplicitCalls, 0,\n\t\t\t\"expected 0 calls to EncryptExplicit, got %v\", cc.numEncryptExplicitCalls)\n\t\tassert.Equal(mt, cc.numEncryptExplicitExpressionCalls, 0,\n\t\t\t\"expected 0 calls to EncryptExplicitExpression, got %v\", cc.numEncryptExplicitExpressionCalls)\n\t\tassert.Equal(mt, cc.numDecryptExplicitCalls, 0,\n\t\t\t\"expected 0 calls to DecryptExplicit, got %v\", cc.numDecryptExplicitCalls)\n\t\tassert.Equal(mt, cc.numCloseCalls, 0,\n\t\t\t\"expected 0 calls to Close, got %v\", cc.numCloseCalls)\n\t\tassert.Equal(mt, cc.numBypassAutoEncryptionCalls, 1,\n\t\t\t\"expected 1 call to BypassAutoEncryption, got %v\", cc.numBypassAutoEncryptionCalls)\n\t})\n}\n\nfunc TestFLE2CreateCollection(t *testing.T) {\n\t// FLE 2 (aka Queryable Encryption) is not supported on Standalone topology.\n\tmtOpts := mtest.NewOptions().\n\t\tMinServerVersion(\"7.0\").\n\t\tEnterprise(true).\n\t\tCreateClient(false).\n\t\tTopologies(mtest.ReplicaSet,\n\t\t\tmtest.Sharded,\n\t\t\tmtest.LoadBalanced,\n\t\t\tmtest.ShardedReplicaSet)\n\tmt := mtest.New(t, mtOpts)\n\n\tefJSON := `\n\t{\n\t\t\"escCollection\": \"enxcol_.encryptedCollection.esc\",\n\t\t\"ecocCollection\": \"enxcol_.encryptedCollection.ecoc\",\n\t\t\"fields\": [\n\t\t  {\n\t\t\t\"path\": \"firstName\",\n\t\t\t\"bsonType\": \"string\",\n\t\t\t\"keyId\": {\n\t\t\t  \"$binary\": {\n\t\t\t\t\"subType\": \"04\",\n\t\t\t\t\"base64\": \"AAAAAAAAAAAAAAAAAAAAAA==\"\n\t\t\t  }\n\t\t\t}\n\t\t  }\n\t\t]\n\t  }\n\t`\n\tvar efBSON bson.Raw\n\terr := bson.UnmarshalExtJSON([]byte(efJSON), true /* canonical */, &efBSON)\n\tassert.Nil(mt, err, \"UnmarshalExtJSON error: %v\", err)\n\n\t// Test the behavior in the specification test fle2-CreateCollection.json: \"CreateCollection from encryptedFields.\".\n\t// The Go driver does not support encryptedFields as an option to Drop. See: GODRIVER-2413.\n\tmt.Run(\"CreateCollection from encryptedFields\", func(mt *mtest.T) {\n\t\t// Drop data and state collections to clean up from a prior test run.\n\t\t{\n\t\t\terr = mt.DB.Collection(\"enxcol_.encryptedCollection.esc\").Drop(context.Background())\n\t\t\tassert.Nil(mt, err, \"error in Drop: %v\", err)\n\t\t\terr = mt.DB.Collection(\"enxcol_.encryptedCollection.ecoc\").Drop(context.Background())\n\t\t\tassert.Nil(mt, err, \"error in Drop: %v\", err)\n\t\t\terr := mt.DB.Collection(\"coll\").Drop(context.Background())\n\t\t\tassert.Nil(mt, err, \"error in Drop: %v\", err)\n\t\t}\n\n\t\tmt.DB.CreateCollection(context.Background(), \"coll\", options.CreateCollection().SetEncryptedFields(efBSON))\n\n\t\t// Check expected collections and index exist.\n\t\t{\n\t\t\tgot, err := mt.DB.ListCollectionNames(context.Background(), bson.D{{\"name\", \"coll\"}})\n\t\t\tassert.Nil(mt, err, \"error in ListCollectionNames\")\n\t\t\tassert.Equal(mt, got, []string{\"coll\"}, \"expected ['coll'], got: %v\", got)\n\n\t\t\tgot, err = mt.DB.ListCollectionNames(context.Background(), bson.D{{\"name\", \"enxcol_.encryptedCollection.esc\"}})\n\t\t\tassert.Nil(mt, err, \"error in ListCollectionNames\")\n\t\t\tassert.Equal(mt, got, []string{\"enxcol_.encryptedCollection.esc\"}, \"expected ['encryptedCollection.esc'], got: %v\", got)\n\n\t\t\tgot, err = mt.DB.ListCollectionNames(context.Background(), bson.D{{\"name\", \"enxcol_.encryptedCollection.ecoc\"}})\n\t\t\tassert.Nil(mt, err, \"error in ListCollectionNames\")\n\t\t\tassert.Equal(mt, got, []string{\"enxcol_.encryptedCollection.ecoc\"}, \"expected ['encryptedCollection.ecoc'], got: %v\", got)\n\n\t\t\tindexSpecs, err := mt.DB.Collection(\"coll\").Indexes().ListSpecifications(context.Background())\n\t\t\tassert.Nil(mt, err, \"error in Indexes().ListSpecifications: %v\", err)\n\t\t\tassert.Equal(mt, len(indexSpecs), 2, \"expected two indexes on 'coll', got: %v\", indexSpecs)\n\t\t\tassert.Equal(mt, indexSpecs[1].Name, \"__safeContent___1\", \"expected second index to be '__safeContent___1', got %v\", indexSpecs[1].Name)\n\t\t}\n\t})\n}\n\nfunc TestFLE2DocsExample(t *testing.T) {\n\t// FLE 2 is not supported on Standalone topology.\n\t// Only test MongoDB Server 7.0+. MongoDB Server 7.0 introduced a backwards breaking change to the Queryable Encryption (QE) protocol: QEv2.\n\t// libmongocrypt is configured to use the QEv2 protocol.\n\tmtOpts := mtest.NewOptions().\n\t\tMinServerVersion(\"7.0\").\n\t\tEnterprise(true).\n\t\tCreateClient(false).\n\t\tTopologies(mtest.ReplicaSet,\n\t\t\tmtest.Sharded,\n\t\t\tmtest.LoadBalanced,\n\t\t\tmtest.ShardedReplicaSet)\n\tmt := mtest.New(t, mtOpts)\n\n\tmt.Run(\"Auto Encryption\", func(mt *mtest.T) {\n\t\t// Drop data from prior test runs.\n\t\t{\n\t\t\terr := mt.Client.Database(\"keyvault\").Collection(\"datakeys\").Drop(context.Background())\n\t\t\tassert.Nil(mt, err, \"error in Drop: %v\", err)\n\t\t\terr = mt.Client.Database(\"docsExamples\").Drop(context.Background())\n\t\t\tassert.Nil(mt, err, \"error in Drop: %v\", err)\n\t\t}\n\n\t\tkmsProvidersMap := map[string]map[string]any{\n\t\t\t\"local\": {\"key\": localMasterKey},\n\t\t}\n\n\t\tvar key1ID bson.Binary\n\t\tvar key2ID bson.Binary\n\n\t\t// Create two data keys.\n\t\t{\n\t\t\tcOpts := options.Client().ApplyURI(mtest.ClusterURI())\n\t\t\tintegtest.AddTestServerAPIVersion(cOpts)\n\t\t\tkeyVaultClient, err := mongo.Connect(cOpts)\n\t\t\tassert.Nil(mt, err, \"error in Connect: %v\", err)\n\t\t\tdefer keyVaultClient.Disconnect(context.Background())\n\t\t\tceOpts := options.ClientEncryption().SetKmsProviders(kmsProvidersMap).SetKeyVaultNamespace(\"keyvault.datakeys\")\n\t\t\tce, err := mongo.NewClientEncryption(keyVaultClient, ceOpts)\n\t\t\tassert.Nil(mt, err, \"error in NewClientEncryption: %v\", err)\n\t\t\tdefer ce.Close(context.Background())\n\t\t\tkey1ID, err = ce.CreateDataKey(context.Background(), \"local\")\n\t\t\tassert.Nil(mt, err, \"error in CreateDataKey: %v\", err)\n\t\t\tkey2ID, err = ce.CreateDataKey(context.Background(), \"local\")\n\t\t\tassert.Nil(mt, err, \"error in CreateDataKey: %v\", err)\n\t\t}\n\n\t\t// Create an encryptedFieldsMap.\n\t\tencryptedFieldsMap := bson.M{\n\t\t\t\"docsExamples.encrypted\": bson.M{\n\t\t\t\t\"fields\": []bson.M{\n\t\t\t\t\t{\n\t\t\t\t\t\t\"path\":     \"encryptedIndexed\",\n\t\t\t\t\t\t\"bsonType\": \"string\",\n\t\t\t\t\t\t\"keyId\":    key1ID,\n\t\t\t\t\t\t\"queries\": []bson.M{\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"queryType\": \"equality\",\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"path\":     \"encryptedUnindexed\",\n\t\t\t\t\t\t\"bsonType\": \"string\",\n\t\t\t\t\t\t\"keyId\":    key2ID,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t}\n\n\t\t// Create an FLE 2 collection.\n\t\tvar encryptedColl *mongo.Collection\n\t\t{\n\t\t\tcOpts := options.Client().ApplyURI(mtest.ClusterURI())\n\t\t\tintegtest.AddTestServerAPIVersion(cOpts)\n\t\t\taeOpts := options.AutoEncryption().SetKmsProviders(kmsProvidersMap).SetKeyVaultNamespace(\"keyvault.datakeys\").SetEncryptedFieldsMap(encryptedFieldsMap).SetExtraOptions(getCryptSharedLibExtraOptions())\n\t\t\tcOpts.SetAutoEncryptionOptions(aeOpts)\n\t\t\tencryptedClient, err := mongo.Connect(cOpts)\n\t\t\tdefer encryptedClient.Disconnect(context.Background())\n\t\t\tassert.Nil(mt, err, \"error in Connect: %v\", err)\n\t\t\t// Create the FLE 2 collection docsExample.encrypted.\n\t\t\tdb := encryptedClient.Database(\"docsExamples\")\n\t\t\t// Because docsExample.encrypted is in encryptedFieldsMap, it is created with FLE 2 support.\n\t\t\terr = db.CreateCollection(context.Background(), \"encrypted\")\n\t\t\tassert.Nil(mt, err, \"error in CreateCollection\")\n\t\t\tencryptedColl = db.Collection(\"encrypted\")\n\t\t}\n\n\t\t// Auto encrypt an insert and find.\n\t\t{\n\t\t\t// Encrypt an insert.\n\t\t\t_, err := encryptedColl.InsertOne(context.Background(), bson.M{\n\t\t\t\t\"_id\":                1,\n\t\t\t\t\"encryptedIndexed\":   \"indexedValue\",\n\t\t\t\t\"encryptedUnindexed\": \"unindexedValue\",\n\t\t\t})\n\t\t\tassert.Nil(mt, err, \"error in InsertOne\")\n\n\t\t\t// Encrypt a find.\n\t\t\tres := encryptedColl.FindOne(context.Background(), bson.M{\n\t\t\t\t\"encryptedIndexed\": \"indexedValue\",\n\t\t\t})\n\t\t\tassert.Nil(mt, res.Err(), \"error in FindOne: %v\", res.Err())\n\t\t\tvar resBSON bson.M\n\t\t\terr = res.Decode(&resBSON)\n\t\t\tassert.Nil(mt, err, \"error in Decode: %v\", err)\n\t\t\tassert.Equal(mt, resBSON[\"encryptedIndexed\"], \"indexedValue\", \"expected 'indexedValue', got %q\", resBSON[\"encryptedIndexed\"])\n\t\t\tassert.Equal(mt, resBSON[\"encryptedUnindexed\"], \"unindexedValue\", \"expected 'unindexedValue', got %q\", resBSON[\"encryptedUnindexed\"])\n\t\t}\n\n\t\t// Find documents without decryption.\n\t\t{\n\t\t\tunencryptedColl := mt.Client.Database(\"docsExamples\").Collection(\"encrypted\")\n\t\t\tres := unencryptedColl.FindOne(context.Background(), bson.M{\"_id\": 1})\n\t\t\tassert.Nil(mt, res.Err(), \"error in FindOne: %v\", res.Err())\n\t\t\tresBSON, err := res.Raw()\n\t\t\tassert.Nil(mt, err, \"error in Raw: %v\", err)\n\n\t\t\tval := resBSON.Lookup(\"encryptedIndexed\")\n\t\t\tassert.Equal(mt, val.Type, bson.TypeBinary, \"expected encryptedIndexed to be Binary, got %v\", val.Type)\n\t\t\tval = resBSON.Lookup(\"encryptedUnindexed\")\n\t\t\tassert.Equal(mt, val.Type, bson.TypeBinary, \"expected encryptedUnindexed to be Binary, got %v\", val.Type)\n\t\t}\n\t})\n}\n\n// `TestFLE2CreateCollectionWithAutoEncryption` is a regression test for a bug fixed in GODRIVER-2413.\n// Prior to GODRIVER-2413, the `IndexView.CreateMany` operation was not processed for automatic encryption. This resulted in no \"listCollections\" command sent.\nfunc TestFLE2CreateCollectionWithAutoEncryption(t *testing.T) {\n\tmtOpts := mtest.NewOptions().\n\t\tMinServerVersion(\"7.0\").\n\t\tEnterprise(true).\n\t\tCreateClient(false).\n\t\tTopologies(mtest.ReplicaSet,\n\t\t\tmtest.Sharded,\n\t\t\tmtest.LoadBalanced,\n\t\t\tmtest.ShardedReplicaSet)\n\tmt := mtest.New(t, mtOpts)\n\n\tmt.Run(\"TestFLE2CreateCollectionWithAutoEncryption\", func(mt *mtest.T) {\n\t\t// Drop data from prior test runs.\n\t\t{\n\t\t\terr := mt.Client.Database(\"keyvault\").Collection(\"datakeys\").Drop(context.Background())\n\t\t\tassert.Nil(mt, err, \"error in Drop: %v\", err)\n\t\t\terr = mt.Client.Database(\"db\").Drop(context.Background())\n\t\t\tassert.Nil(mt, err, \"error in Drop: %v\", err)\n\t\t}\n\n\t\tkmsProvidersMap := map[string]map[string]any{\n\t\t\t\"local\": {\"key\": localMasterKey},\n\t\t}\n\n\t\t// Use an empty encryptedFields.\n\t\tencryptedFields := bson.M{\n\t\t\t\"fields\": []bson.M{},\n\t\t}\n\n\t\t// Store names of started commands.\n\t\tstartedCommands := make([]string, 0)\n\t\tcmdMonitor := &event.CommandMonitor{\n\t\t\tStarted: func(_ context.Context, evt *event.CommandStartedEvent) {\n\t\t\t\tstartedCommands = append(startedCommands, evt.CommandName)\n\t\t\t},\n\t\t}\n\n\t\t// Create a Client with Auto Encryption enabled.\n\t\tvar encryptedClient *mongo.Client\n\t\t{\n\t\t\taeOpts := options.AutoEncryption().\n\t\t\t\tSetKmsProviders(kmsProvidersMap).\n\t\t\t\tSetKeyVaultNamespace(\"keyvault.datakeys\").\n\t\t\t\tSetExtraOptions(getCryptSharedLibExtraOptions())\n\n\t\t\tcOpts := options.Client().\n\t\t\t\tApplyURI(mtest.ClusterURI()).\n\t\t\t\tSetMonitor(cmdMonitor).\n\t\t\t\tSetAutoEncryptionOptions(aeOpts)\n\n\t\t\tintegtest.AddTestServerAPIVersion(cOpts)\n\n\t\t\tvar err error\n\t\t\tencryptedClient, err = mongo.Connect(cOpts)\n\t\t\tdefer encryptedClient.Disconnect(context.Background())\n\t\t\tassert.Nil(mt, err, \"error in Connect: %v\", err)\n\t\t}\n\n\t\t// Create a collection with the encrypted fields.\n\t\tencryptedClient.Database(\"db\").CreateCollection(context.Background(), \"coll\", options.CreateCollection().SetEncryptedFields(encryptedFields))\n\n\t\t// Check resulting events sent.\n\t\tassert.Equal(mt, []string{\n\t\t\t\"create\",          // Create ESC collection.\n\t\t\t\"create\",          // Create ECOC collection.\n\t\t\t\"create\",          // Create 'coll' collection.\n\t\t\t\"listCollections\", // Run listCollections when processing `createIndexes` command for automatic encryption.\n\t\t\t\"createIndexes\",\n\t\t}, startedCommands)\n\t})\n}\n\nfunc TestFLEIndexView(t *testing.T) {\n\tverifyClientSideEncryptionVarsSet(t)\n\n\tmt := mtest.New(t, mtest.NewOptions().MinServerVersion(\"4.2\").Enterprise(true).CreateClient(false))\n\n\topts := options.Client().ApplyURI(mtest.ClusterURI()).SetWriteConcern(mtest.MajorityWc).\n\t\tSetReadPreference(mtest.PrimaryRp)\n\n\tcc := &customCrypt{}\n\topts.Crypt = cc\n\n\tintegtest.AddTestServerAPIVersion(opts)\n\n\tclient, err := mongo.Connect(opts)\n\tassert.NoError(mt, err)\n\n\tmt.Cleanup(func() { client.Disconnect(context.Background()) })\n\n\tcoll := client.Database(\"db\").Collection(\"coll\")\n\n\terr = coll.Drop(context.Background())\n\tassert.Nil(mt, err, \"Drop error: %v\", err)\n\n\tmt.Run(\"create many\", func(mt *mtest.T) {\n\t\tcreateIndexes(mt, coll, 2)\n\n\t\tassert.Equal(mt, cc.numEncryptCalls, 2, \"expected 2 calls to Encrypt, got %v\", cc.numEncryptCalls)\n\t})\n\n\tmt.Run(\"list\", func(mt *mtest.T) {\n\t\tcc.numEncryptCalls = 0 // Reset Encrypt calls from createIndexes\n\n\t\t_, err := coll.Indexes().List(context.Background(), options.ListIndexes().SetBatchSize(2))\n\t\tassert.NoError(mt, err, \"error creating list cursor: %v\", err)\n\n\t\tassert.Equal(mt, cc.numEncryptCalls, 1, \"expected 1 call to Encrypt, got %v\", cc.numEncryptCalls)\n\t})\n\n\tmt.Run(\"list specifications\", func(mt *mtest.T) {\n\t\tcc.numEncryptCalls = 0 // Reset Encrypt calls from createIndexes\n\n\t\t_, err := coll.Indexes().ListSpecifications(context.Background())\n\t\tassert.NoError(mt, err, \"error listing specifications : %v\", err)\n\n\t\tassert.Equal(mt, cc.numEncryptCalls, 1, \"expected 1 call to Encrypt, got %v\", cc.numEncryptCalls)\n\t})\n\n\tmt.Run(\"drop one\", func(mt *mtest.T) {\n\t\tcreateIndexes(mt, coll, 1)\n\n\t\tcc.numEncryptCalls = 0 // Reset Encrypt calls from createIndexes\n\n\t\terr := coll.Indexes().DropOne(context.Background(), \"a_1\")\n\t\tassert.NoError(mt, err, \"error dropping one index: %v\", err)\n\n\t\tassert.Equal(mt, cc.numEncryptCalls, 1, \"expected 1 call to Encrypt, got %v\", cc.numEncryptCalls)\n\t})\n\n\tmt.Run(\"drop all\", func(mt *mtest.T) {\n\t\tcreateIndexes(mt, coll, 2)\n\n\t\tcc.numEncryptCalls = 0 // Reset Encrypt calls from createIndexes\n\n\t\terr := coll.Indexes().DropAll(context.Background())\n\t\tassert.NoError(mt, err, \"error dropping all indexes: %v\", err)\n\n\t\tassert.Equal(mt, cc.numEncryptCalls, 1, \"expected 1 call to Encrypt, got %v\", cc.numEncryptCalls)\n\t})\n}\n"
  },
  {
    "path": "internal/integration/client_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage integration\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"net\"\n\t\"os\"\n\t\"reflect\"\n\t\"runtime\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/event\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert/assertbson\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/eventtest\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/failpoint\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/integration/mtest\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/integtest\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/require\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/testutil\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/readpref\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/writeconcern\"\n\t\"go.mongodb.org/mongo-driver/v2/version\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/session\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/wiremessage\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/xoptions\"\n\t\"golang.org/x/sync/errgroup\"\n)\n\nvar noClientOpts = mtest.NewOptions().CreateClient(false)\n\ntype negateCodec struct {\n\tID int64 `bson:\"_id\"`\n}\n\nfunc (e *negateCodec) EncodeValue(_ bson.EncodeContext, vw bson.ValueWriter, val reflect.Value) error {\n\treturn vw.WriteInt64(val.Int())\n}\n\n// DecodeValue negates the value of ID when reading\nfunc (e *negateCodec) DecodeValue(_ bson.DecodeContext, vr bson.ValueReader, val reflect.Value) error {\n\ti, err := vr.ReadInt64()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tval.SetInt(i * -1)\n\treturn nil\n}\n\ntype intKey int\n\nfunc (i intKey) MarshalKey() (string, error) {\n\treturn fmt.Sprintf(\"key_%d\", i), nil\n}\n\nvar _ options.ContextDialer = &slowConnDialer{}\n\n// A slowConnDialer dials connections that delay network round trips by the given delay duration.\ntype slowConnDialer struct {\n\tdialer *net.Dialer\n\tdelay  time.Duration\n}\n\nvar (\n\tslowConnDialerDelay      = 300 * time.Millisecond\n\treducedHeartbeatInterval = 500 * time.Millisecond\n)\n\nfunc newSlowConnDialer(delay time.Duration) *slowConnDialer {\n\treturn &slowConnDialer{\n\t\tdialer: &net.Dialer{},\n\t\tdelay:  delay,\n\t}\n}\n\nfunc (scd *slowConnDialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) {\n\tconn, err := scd.dialer.DialContext(ctx, network, address)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn &slowConn{\n\t\tConn:  conn,\n\t\tdelay: scd.delay,\n\t}, nil\n}\n\nvar _ net.Conn = &slowConn{}\n\n// slowConn is a net.Conn that delays all calls to Read() by given delay durations. All other\n// net.Conn functions behave identically to the embedded net.Conn.\ntype slowConn struct {\n\tnet.Conn\n\tdelay time.Duration\n}\n\nfunc (sc *slowConn) Read(b []byte) (n int, err error) {\n\ttime.Sleep(sc.delay)\n\treturn sc.Conn.Read(b)\n}\n\nfunc TestClient(t *testing.T) {\n\tmt := mtest.New(t, noClientOpts)\n\n\treg := bson.NewRegistry()\n\treg.RegisterTypeEncoder(reflect.TypeOf(int64(0)), &negateCodec{})\n\treg.RegisterTypeDecoder(reflect.TypeOf(int64(0)), &negateCodec{})\n\tregistryOpts := options.Client().\n\t\tSetRegistry(reg)\n\tmt.RunOpts(\"registry passed to cursors\", mtest.NewOptions().ClientOptions(registryOpts), func(mt *mtest.T) {\n\t\t_, err := mt.Coll.InsertOne(context.Background(), negateCodec{ID: 10})\n\t\tassert.Nil(mt, err, \"InsertOne error: %v\", err)\n\t\tvar got negateCodec\n\t\terr = mt.Coll.FindOne(context.Background(), bson.D{}).Decode(&got)\n\t\tassert.Nil(mt, err, \"Find error: %v\", err)\n\n\t\tassert.Equal(mt, int64(-10), got.ID, \"expected ID -10, got %v\", got.ID)\n\t})\n\tmt.RunOpts(\"tls connection\", mtest.NewOptions().MinServerVersion(\"3.0\").SSL(true), func(mt *mtest.T) {\n\t\tvar result bson.Raw\n\t\terr := mt.Coll.Database().RunCommand(context.Background(), bson.D{\n\t\t\t{\"serverStatus\", 1},\n\t\t}).Decode(&result)\n\t\tassert.Nil(mt, err, \"serverStatus error: %v\", err)\n\n\t\tsecurity := result.Lookup(\"security\")\n\t\tassert.Equal(mt, bson.TypeEmbeddedDocument, security.Type,\n\t\t\t\"expected security field to be type %v, got %v\", bson.TypeMaxKey, security.Type)\n\t\t_, found := security.Document().LookupErr(\"SSLServerSubjectName\")\n\t\tassert.Nil(mt, found, \"SSLServerSubjectName not found in result\")\n\t})\n\tmt.RunOpts(\"x509\", mtest.NewOptions().Auth(true).SSL(true), func(mt *mtest.T) {\n\t\ttestCases := []struct {\n\t\t\tcertificate string\n\t\t\tpassword    string\n\t\t}{\n\t\t\t{\n\t\t\t\t\"MONGO_GO_DRIVER_KEY_FILE\",\n\t\t\t\t\"\",\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"MONGO_GO_DRIVER_PKCS8_ENCRYPTED_KEY_FILE\",\n\t\t\t\t\"&sslClientCertificateKeyPassword=password\",\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"MONGO_GO_DRIVER_PKCS8_UNENCRYPTED_KEY_FILE\",\n\t\t\t\t\"\",\n\t\t\t},\n\t\t}\n\t\tfor _, tc := range testCases {\n\t\t\tmt.Run(tc.certificate, func(mt *mtest.T) {\n\t\t\t\tconst user = \"C=US,ST=New York,L=New York City,O=MDB,OU=Drivers,CN=client\"\n\t\t\t\tdb := mt.Client.Database(\"$external\")\n\n\t\t\t\t// We don't care if the user doesn't already exist.\n\t\t\t\t_ = db.RunCommand(\n\t\t\t\t\tcontext.Background(),\n\t\t\t\t\tbson.D{{\"dropUser\", user}},\n\t\t\t\t)\n\t\t\t\terr := db.RunCommand(\n\t\t\t\t\tcontext.Background(),\n\t\t\t\t\tbson.D{\n\t\t\t\t\t\t{\"createUser\", user},\n\t\t\t\t\t\t{\"roles\", bson.A{\n\t\t\t\t\t\t\tbson.D{{\"role\", \"readWrite\"}, {\"db\", \"test\"}},\n\t\t\t\t\t\t}},\n\t\t\t\t\t},\n\t\t\t\t).Err()\n\t\t\t\tassert.Nil(mt, err, \"createUser error: %v\", err)\n\n\t\t\t\tbaseConnString := mtest.ClusterURI()\n\t\t\t\t// remove username/password from base conn string\n\t\t\t\trevisedConnString := \"mongodb://\"\n\t\t\t\tsplit := strings.Split(baseConnString, \"@\")\n\t\t\t\tassert.Equal(t, 2, len(split), \"expected 2 parts after split, got %v (connstring %v)\", split, baseConnString)\n\t\t\t\trevisedConnString += split[1]\n\n\t\t\t\tcs := fmt.Sprintf(\n\t\t\t\t\t\"%s&sslClientCertificateKeyFile=%s&authMechanism=MONGODB-X509&authSource=$external%s\",\n\t\t\t\t\trevisedConnString,\n\t\t\t\t\tos.Getenv(tc.certificate),\n\t\t\t\t\ttc.password,\n\t\t\t\t)\n\t\t\t\tauthClientOpts := options.Client().ApplyURI(cs)\n\t\t\t\tintegtest.AddTestServerAPIVersion(authClientOpts)\n\t\t\t\tauthClient, err := mongo.Connect(authClientOpts)\n\t\t\t\tassert.Nil(mt, err, \"authClient Connect error: %v\", err)\n\t\t\t\tdefer func() { _ = authClient.Disconnect(context.Background()) }()\n\n\t\t\t\trdr, err := authClient.Database(\"test\").RunCommand(context.Background(), bson.D{\n\t\t\t\t\t{\"connectionStatus\", 1},\n\t\t\t\t}).Raw()\n\t\t\t\tassert.Nil(mt, err, \"connectionStatus error: %v\", err)\n\t\t\t\tusers, err := rdr.LookupErr(\"authInfo\", \"authenticatedUsers\")\n\t\t\t\tassert.Nil(mt, err, \"authenticatedUsers not found in response\")\n\t\t\t\telems, err := bson.Raw(users.Array()).Elements()\n\t\t\t\tassert.Nil(mt, err, \"error getting users elements: %v\", err)\n\n\t\t\t\tfor _, userElem := range elems {\n\t\t\t\t\trdr := userElem.Value().Document()\n\t\t\t\t\tvar u struct {\n\t\t\t\t\t\tUser string\n\t\t\t\t\t\tDB   string\n\t\t\t\t\t}\n\n\t\t\t\t\tif err := bson.Unmarshal(rdr, &u); err != nil {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tif u.User == user && u.DB == \"$external\" {\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tmt.Fatal(\"unable to find authenticated user\")\n\t\t\t})\n\t\t}\n\t})\n\tmt.RunOpts(\"list databases\", noClientOpts, func(mt *mtest.T) {\n\t\tmt.RunOpts(\"filter\", noClientOpts, func(mt *mtest.T) {\n\t\t\ttestCases := []struct {\n\t\t\t\tname             string\n\t\t\t\tfilter           bson.D\n\t\t\t\thasTestDB        bool\n\t\t\t\tminServerVersion string\n\t\t\t}{\n\t\t\t\t{\"empty\", bson.D{}, true, \"\"},\n\t\t\t\t{\"non-empty\", bson.D{{\"name\", \"foobar\"}}, false, \"3.6\"},\n\t\t\t}\n\n\t\t\tfor _, tc := range testCases {\n\t\t\t\topts := mtest.NewOptions()\n\t\t\t\tif tc.minServerVersion != \"\" {\n\t\t\t\t\topts.MinServerVersion(tc.minServerVersion)\n\t\t\t\t}\n\n\t\t\t\tmt.RunOpts(tc.name, opts, func(mt *mtest.T) {\n\t\t\t\t\tres, err := mt.Client.ListDatabases(context.Background(), tc.filter)\n\t\t\t\t\tassert.Nil(mt, err, \"ListDatabases error: %v\", err)\n\n\t\t\t\t\tvar found bool\n\t\t\t\t\tfor _, db := range res.Databases {\n\t\t\t\t\t\tif db.Name == mtest.TestDB {\n\t\t\t\t\t\t\tfound = true\n\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tassert.Equal(mt, tc.hasTestDB, found, \"expected to find test db: %v, found: %v\", tc.hasTestDB, found)\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\t\tmt.Run(\"options\", func(mt *mtest.T) {\n\t\t\tallOpts := options.ListDatabases().SetNameOnly(true).SetAuthorizedDatabases(true)\n\t\t\tmt.ClearEvents()\n\n\t\t\t_, err := mt.Client.ListDatabases(context.Background(), bson.D{}, allOpts)\n\t\t\tassert.Nil(mt, err, \"ListDatabases error: %v\", err)\n\n\t\t\tevt := mt.GetStartedEvent()\n\t\t\tassert.Equal(mt, \"listDatabases\", evt.CommandName, \"expected \")\n\n\t\t\texpectedDoc := bsoncore.BuildDocumentFromElements(nil,\n\t\t\t\tbsoncore.AppendBooleanElement(nil, \"nameOnly\", true),\n\t\t\t\tbsoncore.AppendBooleanElement(nil, \"authorizedDatabases\", true),\n\t\t\t)\n\t\t\terr = compareDocs(mt, expectedDoc, evt.Command)\n\t\t\tassert.Nil(mt, err, \"compareDocs error: %v\", err)\n\t\t})\n\t})\n\tmt.RunOpts(\"list database names\", noClientOpts, func(mt *mtest.T) {\n\t\tmt.RunOpts(\"filter\", noClientOpts, func(mt *mtest.T) {\n\t\t\ttestCases := []struct {\n\t\t\t\tname             string\n\t\t\t\tfilter           bson.D\n\t\t\t\thasTestDB        bool\n\t\t\t\tminServerVersion string\n\t\t\t}{\n\t\t\t\t{\"no filter\", bson.D{}, true, \"\"},\n\t\t\t\t{\"filter\", bson.D{{\"name\", \"foobar\"}}, false, \"3.6\"},\n\t\t\t}\n\n\t\t\tfor _, tc := range testCases {\n\t\t\t\topts := mtest.NewOptions()\n\t\t\t\tif tc.minServerVersion != \"\" {\n\t\t\t\t\topts.MinServerVersion(tc.minServerVersion)\n\t\t\t\t}\n\n\t\t\t\tmt.RunOpts(tc.name, opts, func(mt *mtest.T) {\n\t\t\t\t\tdbs, err := mt.Client.ListDatabaseNames(context.Background(), tc.filter)\n\t\t\t\t\tassert.Nil(mt, err, \"ListDatabaseNames error: %v\", err)\n\n\t\t\t\t\tvar found bool\n\t\t\t\t\tfor _, db := range dbs {\n\t\t\t\t\t\tif db == mtest.TestDB {\n\t\t\t\t\t\t\tfound = true\n\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tassert.Equal(mt, tc.hasTestDB, found, \"expected to find test db: %v, found: %v\", tc.hasTestDB, found)\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\t\tmt.Run(\"options\", func(mt *mtest.T) {\n\t\t\tallOpts := options.ListDatabases().SetNameOnly(true).SetAuthorizedDatabases(true)\n\t\t\tmt.ClearEvents()\n\n\t\t\t_, err := mt.Client.ListDatabaseNames(context.Background(), bson.D{}, allOpts)\n\t\t\tassert.Nil(mt, err, \"ListDatabaseNames error: %v\", err)\n\n\t\t\tevt := mt.GetStartedEvent()\n\t\t\tassert.Equal(mt, \"listDatabases\", evt.CommandName, \"expected \")\n\n\t\t\texpectedDoc := bsoncore.BuildDocumentFromElements(nil,\n\t\t\t\tbsoncore.AppendBooleanElement(nil, \"nameOnly\", true),\n\t\t\t\tbsoncore.AppendBooleanElement(nil, \"authorizedDatabases\", true),\n\t\t\t)\n\t\t\terr = compareDocs(mt, expectedDoc, evt.Command)\n\t\t\tassert.Nil(mt, err, \"compareDocs error: %v\", err)\n\t\t})\n\t})\n\tmt.RunOpts(\"ping\", noClientOpts, func(mt *mtest.T) {\n\t\tmt.Run(\"default read preference\", func(mt *mtest.T) {\n\t\t\terr := mt.Client.Ping(context.Background(), nil)\n\t\t\tassert.Nil(mt, err, \"Ping error: %v\", err)\n\t\t})\n\t\tmt.Run(\"invalid host\", func(mt *mtest.T) {\n\t\t\t// manually create client rather than using RunOpts with ClientOptions because the testing lib will\n\t\t\t// apply the correct URI.\n\t\t\tinvalidClientOpts := options.Client().\n\t\t\t\tSetServerSelectionTimeout(100 * time.Millisecond).SetHosts([]string{\"invalid:123\"}).\n\t\t\t\tSetConnectTimeout(500 * time.Millisecond).SetTimeout(500 * time.Millisecond)\n\t\t\tintegtest.AddTestServerAPIVersion(invalidClientOpts)\n\t\t\tclient, err := mongo.Connect(invalidClientOpts)\n\t\t\tassert.Nil(mt, err, \"Connect error: %v\", err)\n\t\t\terr = client.Ping(context.Background(), readpref.Primary())\n\t\t\tassert.NotNil(mt, err, \"expected error for pinging invalid host, got nil\")\n\t\t\t_ = client.Disconnect(context.Background())\n\t\t})\n\t})\n\tmt.RunOpts(\"disconnect\", noClientOpts, func(mt *mtest.T) {\n\t\tmt.Run(\"nil context\", func(mt *mtest.T) {\n\t\t\terr := mt.Client.Disconnect(nil)\n\t\t\tassert.Nil(mt, err, \"Disconnect error: %v\", err)\n\t\t})\n\t})\n\tmt.RunOpts(\"end sessions\", mtest.NewOptions().MinServerVersion(\"3.6\"), func(mt *mtest.T) {\n\t\t_, err := mt.Client.ListDatabases(context.Background(), bson.D{})\n\t\tassert.Nil(mt, err, \"ListDatabases error: %v\", err)\n\n\t\tmt.ClearEvents()\n\t\terr = mt.Client.Disconnect(context.Background())\n\t\tassert.Nil(mt, err, \"Disconnect error: %v\", err)\n\n\t\tstarted := mt.GetStartedEvent()\n\t\tassert.Equal(mt, \"endSessions\", started.CommandName, \"expected cmd name endSessions, got %v\", started.CommandName)\n\t})\n\tmt.RunOpts(\"hello lastWriteDate\", mtest.NewOptions().Topologies(mtest.ReplicaSet), func(mt *mtest.T) {\n\t\t_, err := mt.Coll.InsertOne(context.Background(), bson.D{{\"x\", 1}})\n\t\tassert.Nil(mt, err, \"InsertOne error: %v\", err)\n\t})\n\tsessionOpts := mtest.NewOptions().MinServerVersion(\"3.6.0\").CreateClient(false)\n\tmt.RunOpts(\"causal consistency\", sessionOpts, func(mt *mtest.T) {\n\t\ttestCases := []struct {\n\t\t\tname       string\n\t\t\topts       *options.SessionOptionsBuilder\n\t\t\tconsistent bool\n\t\t}{\n\t\t\t{\"default\", options.Session(), true},\n\t\t\t{\"true\", options.Session().SetCausalConsistency(true), true},\n\t\t\t{\"false\", options.Session().SetCausalConsistency(false), false},\n\t\t}\n\n\t\tfor _, tc := range testCases {\n\t\t\tmt.Run(tc.name, func(mt *mtest.T) {\n\t\t\t\tsess, err := mt.Client.StartSession(tc.opts)\n\t\t\t\tassert.Nil(mt, err, \"StartSession error: %v\", err)\n\t\t\t\tdefer sess.EndSession(context.Background())\n\t\t\t\tclientSession := testutil.GetUnexportedFieldAs[*session.Client](sess, \"clientSession\")\n\n\t\t\t\tconsistent := clientSession.Consistent\n\t\t\t\tassert.Equal(mt, tc.consistent, consistent, \"expected consistent to be %v, got %v\", tc.consistent, consistent)\n\t\t\t})\n\t\t}\n\t})\n\tretryOpts := mtest.NewOptions().MinServerVersion(\"3.6.0\").ClientType(mtest.Mock)\n\tmt.RunOpts(\"retry writes error 20 wrapped\", retryOpts, func(mt *mtest.T) {\n\t\twriteErrorCode20 := mtest.CreateWriteErrorsResponse(mtest.WriteError{\n\t\t\tMessage: \"Transaction numbers\",\n\t\t\tCode:    20,\n\t\t})\n\t\twriteErrorCode19 := mtest.CreateWriteErrorsResponse(mtest.WriteError{\n\t\t\tMessage: \"Transaction numbers\",\n\t\t\tCode:    19,\n\t\t})\n\t\twriteErrorCode20WrongMsg := mtest.CreateWriteErrorsResponse(mtest.WriteError{\n\t\t\tMessage: \"Not transaction numbers\",\n\t\t\tCode:    20,\n\t\t})\n\t\tcmdErrCode20 := mtest.CreateCommandErrorResponse(mtest.CommandError{\n\t\t\tMessage: \"Transaction numbers\",\n\t\t\tCode:    20,\n\t\t})\n\t\tcmdErrCode19 := mtest.CreateCommandErrorResponse(mtest.CommandError{\n\t\t\tMessage: \"Transaction numbers\",\n\t\t\tCode:    19,\n\t\t})\n\t\tcmdErrCode20WrongMsg := mtest.CreateCommandErrorResponse(mtest.CommandError{\n\t\t\tMessage: \"Not transaction numbers\",\n\t\t\tCode:    20,\n\t\t})\n\n\t\ttestCases := []struct {\n\t\t\tname                 string\n\t\t\terrResponse          bson.D\n\t\t\texpectUnsupportedMsg bool\n\t\t}{\n\t\t\t{\"write error code 20\", writeErrorCode20, true},\n\t\t\t{\"write error code 20 wrong msg\", writeErrorCode20WrongMsg, false},\n\t\t\t{\"write error code 19 right msg\", writeErrorCode19, false},\n\t\t\t{\"command error code 20\", cmdErrCode20, true},\n\t\t\t{\"command error code 20 wrong msg\", cmdErrCode20WrongMsg, false},\n\t\t\t{\"command error code 19 right msg\", cmdErrCode19, false},\n\t\t}\n\t\tfor _, tc := range testCases {\n\t\t\tmt.Run(tc.name, func(mt *mtest.T) {\n\t\t\t\tmt.ClearMockResponses()\n\t\t\t\tmt.AddMockResponses(tc.errResponse)\n\n\t\t\t\tsess, err := mt.Client.StartSession()\n\t\t\t\tassert.Nil(mt, err, \"StartSession error: %v\", err)\n\t\t\t\tdefer sess.EndSession(context.Background())\n\n\t\t\t\t_, err = mt.Coll.InsertOne(context.Background(), bson.D{{\"x\", 1}})\n\t\t\t\tassert.NotNil(mt, err, \"expected err but got nil\")\n\t\t\t\tif tc.expectUnsupportedMsg {\n\t\t\t\t\tassert.Equal(mt, driver.ErrUnsupportedStorageEngine.Error(), err.Error(),\n\t\t\t\t\t\t\"expected error %v, got %v\", driver.ErrUnsupportedStorageEngine, err)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tassert.NotEqual(mt, driver.ErrUnsupportedStorageEngine.Error(), err.Error(),\n\t\t\t\t\t\"got ErrUnsupportedStorageEngine but wanted different error\")\n\t\t\t})\n\t\t}\n\t})\n\n\ttestAppName := \"foo\"\n\tappNameClientOpts := options.Client().\n\t\tSetAppName(testAppName)\n\tappNameMtOpts := mtest.NewOptions().\n\t\tClientType(mtest.Proxy).\n\t\tClientOptions(appNameClientOpts).\n\t\tTopologies(mtest.Single)\n\tmt.RunOpts(\"app name is always sent\", appNameMtOpts, func(mt *mtest.T) {\n\t\terr := mt.Client.Ping(context.Background(), mtest.PrimaryRp)\n\t\tassert.Nil(mt, err, \"Ping error: %v\", err)\n\n\t\twant := mustMarshalBSON(bson.D{\n\t\t\t{Key: \"application\", Value: bson.D{\n\t\t\t\tbson.E{Key: \"name\", Value: \"foo\"},\n\t\t\t}},\n\t\t\t{Key: \"driver\", Value: bson.D{\n\t\t\t\t{Key: \"name\", Value: \"mongo-go-driver\"},\n\t\t\t\t{Key: \"version\", Value: version.Driver},\n\t\t\t}},\n\t\t\t{Key: \"os\", Value: bson.D{\n\t\t\t\t{Key: \"type\", Value: runtime.GOOS},\n\t\t\t\t{Key: \"architecture\", Value: runtime.GOARCH},\n\t\t\t}},\n\t\t\t{Key: \"platform\", Value: runtime.Version()},\n\t\t})\n\n\t\tfor i := 0; i < 2; i++ {\n\t\t\tmessage := mt.GetProxyCapture().TryNext()\n\t\t\trequire.NotNil(mt, message, \"expected handshake message, got nil\")\n\n\t\t\tclientMetadata := clientMetadataFromHandshake(mt, message.Sent.Command)\n\t\t\tassertbson.EqualDocument(mt, want, clientMetadata)\n\t\t}\n\t})\n\n\t// Test that direct connections work as expected.\n\tfirstServerAddr := mtest.GlobalTopology().Description().Servers[0].Addr\n\tdirectConnectionOpts := options.Client().\n\t\tApplyURI(fmt.Sprintf(\"mongodb://%s\", firstServerAddr)).\n\t\tSetReadPreference(readpref.Primary()).\n\t\tSetDirect(true)\n\tmtOpts := mtest.NewOptions().\n\t\tClientOptions(directConnectionOpts).\n\t\tCreateCollection(false).\n\t\tMinServerVersion(\"3.6\").     // Minimum server version 3.6 to force OP_MSG.\n\t\tTopologies(mtest.ReplicaSet) // Read preference isn't sent to standalones so we can test on replica sets.\n\tmt.RunOpts(\"direct connection made\", mtOpts, func(mt *mtest.T) {\n\t\t_, err := mt.Coll.Find(context.Background(), bson.D{})\n\t\tassert.Nil(mt, err, \"Find error: %v\", err)\n\n\t\t// When connected directly, the primary read preference should be overwritten to primaryPreferred.\n\t\tevt := mt.GetStartedEvent()\n\t\tassert.Equal(mt, \"find\", evt.CommandName, \"expected 'find' event, got '%s'\", evt.CommandName)\n\n\t\t// A direct connection will result in a single topology, and so\n\t\t// the default readPreference mode should be \"primaryPrefered\".\n\t\tmodeVal, err := evt.Command.LookupErr(\"$readPreference\", \"mode\")\n\t\tassert.Nil(mt, err, \"expected command %s to include $readPreference\", evt.Command)\n\n\t\tmode := modeVal.StringValue()\n\t\tassert.Equal(mt, mode, \"primaryPreferred\", \"expected read preference mode primaryPreferred, got %v\", mode)\n\t})\n\n\t// Test that using a client with minPoolSize set doesn't cause a data race.\n\tmtOpts = mtest.NewOptions().ClientOptions(options.Client().SetMinPoolSize(5))\n\tmt.RunOpts(\"minPoolSize\", mtOpts, func(mt *mtest.T) {\n\t\terr := mt.Client.Ping(context.Background(), readpref.Primary())\n\t\tassert.Nil(t, err, \"unexpected error calling Ping: %v\", err)\n\t})\n\n\tmt.Run(\"minimum RTT is monitored\", func(mt *mtest.T) {\n\t\tmt.Parallel()\n\n\t\t// Reset the client with a dialer that delays all network round trips by 300ms and set the\n\t\t// heartbeat interval to 500ms to reduce the time it takes to collect RTT samples.\n\t\tmt.ResetClient(options.Client().\n\t\t\tSetDialer(newSlowConnDialer(slowConnDialerDelay)).\n\t\t\tSetHeartbeatInterval(reducedHeartbeatInterval))\n\n\t\t// Assert that the minimum RTT is eventually >250ms.\n\t\ttopo := getTopologyFromClient(mt.Client)\n\t\tcallback := func() bool {\n\t\t\t// Wait for all of the server's minimum RTTs to be >250ms.\n\t\t\tfor _, desc := range topo.Description().Servers {\n\t\t\t\tserver, err := topo.FindServer(desc)\n\t\t\t\tassert.NoError(mt, err, \"FindServer error: %v\", err)\n\t\t\t\tif server.RTTMonitor().Min() <= 250*time.Millisecond {\n\t\t\t\t\treturn false // the tick should wait for 100ms in this case\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn true\n\t\t}\n\t\tassert.Eventually(t,\n\t\t\tcallback,\n\t\t\t10*time.Second,\n\t\t\t100*time.Millisecond,\n\t\t\t\"expected that the minimum RTT is eventually >250ms\")\n\t})\n\n\t// Test that if the minimum RTT is greater than the remaining timeout for an operation, the\n\t// operation is not sent to the server and no connections are closed.\n\tmt.Run(\"minimum RTT used to prevent sending requests\", func(mt *mtest.T) {\n\t\tmt.Parallel()\n\n\t\t// Assert that we can call Ping with a 250ms timeout.\n\t\tctx, cancel := context.WithTimeout(context.Background(), 250*time.Millisecond)\n\t\tdefer cancel()\n\t\terr := mt.Client.Ping(ctx, nil)\n\t\tassert.Nil(mt, err, \"Ping error: %v\", err)\n\n\t\t// Reset the client with a dialer that delays all network round trips by 300ms and set the\n\t\t// heartbeat interval to 500ms to reduce the time it takes to collect RTT samples.\n\t\ttpm := eventtest.NewTestPoolMonitor()\n\t\tmt.ResetClient(options.Client().\n\t\t\tSetPoolMonitor(tpm.PoolMonitor).\n\t\t\tSetDialer(newSlowConnDialer(slowConnDialerDelay)).\n\t\t\tSetHeartbeatInterval(reducedHeartbeatInterval))\n\n\t\t// Assert that the minimum RTT is eventually >250ms.\n\t\ttopo := getTopologyFromClient(mt.Client)\n\t\tcallback := func() bool {\n\t\t\t// Wait for all of the server's minimum RTTs to be >250ms.\n\t\t\tfor _, desc := range topo.Description().Servers {\n\t\t\t\tserver, err := topo.FindServer(desc)\n\t\t\t\tassert.NoError(mt, err, \"FindServer error: %v\", err)\n\t\t\t\tif server.RTTMonitor().Min() <= 250*time.Millisecond {\n\t\t\t\t\treturn false\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn true\n\t\t}\n\t\tassert.Eventually(t,\n\t\t\tcallback,\n\t\t\t10*time.Second,\n\t\t\t100*time.Millisecond,\n\t\t\t\"expected that the minimum RTT is eventually >250ms\")\n\n\t\t// Once we've waited for the minimum RTT for the single server to be >250ms, run a bunch of\n\t\t// Ping operations with a timeout of 250ms and expect that they return errors.\n\t\tfor i := 0; i < 10; i++ {\n\t\t\tctx, cancel = context.WithTimeout(context.Background(), 250*time.Millisecond)\n\t\t\terr := mt.Client.Ping(ctx, nil)\n\t\t\tcancel()\n\t\t\tassert.NotNil(mt, err, \"expected Ping to return an error\")\n\t\t}\n\n\t\t// Assert that the Ping timeouts result in no connections being closed.\n\t\tclosed := len(tpm.Events(func(e *event.PoolEvent) bool { return e.Type == event.ConnectionClosed }))\n\t\tassert.Equal(t, 0, closed, \"expected no connections to be closed\")\n\t})\n\n\t// Test that OP_MSG is used for authentication-related commands on 3.6+ (WV 6+). Do not test when API version is\n\t// set, as handshakes will always use OP_MSG.\n\topMsgOpts := mtest.NewOptions().ClientType(mtest.Proxy).MinServerVersion(\"3.6\").Auth(true).RequireAPIVersion(false)\n\tmt.RunOpts(\"OP_MSG used for authentication on 3.6+\", opMsgOpts, func(mt *mtest.T) {\n\t\terr := mt.Client.Ping(context.Background(), mtest.PrimaryRp)\n\t\tassert.Nil(mt, err, \"Ping error: %v\", err)\n\n\t\tproxyCapture := mt.GetProxyCapture()\n\n\t\t// The first message should be a connection handshake.\n\t\tfirstMessage := proxyCapture.TryNext()\n\t\trequire.NotNil(mt, firstMessage, \"expected handshake message, got nil\")\n\n\t\tassert.True(t, firstMessage.IsHandshake())\n\n\t\topCode := firstMessage.Sent.OpCode\n\t\tassert.Equal(mt, wiremessage.OpQuery, opCode,\n\t\t\t\"expected 'OP_MSG' OpCode in wire message, got %q\", opCode.String())\n\n\t\t// Look for a saslContinue in the remaining proxied messages and assert that\n\t\t// it uses the OP_MSG OpCode, as wire version is now known to be >= 6.\n\t\tvar saslContinueFound bool\n\t\tfor {\n\t\t\tmessage := proxyCapture.TryNext()\n\t\t\tif message == nil {\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\tif message.CommandName == \"saslContinue\" {\n\t\t\t\tsaslContinueFound = true\n\t\t\t\topCode := message.Sent.OpCode\n\t\t\t\tassert.Equal(mt, wiremessage.OpMsg, opCode,\n\t\t\t\t\t\"expected 'OP_MSG' OpCode in wire message, got %q\", opCode.String())\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tassert.True(mt, saslContinueFound, \"did not find 'saslContinue' command in proxied messages\")\n\t})\n\n\t// Test that OP_MSG is used for handshakes when API version is declared.\n\topMsgSAPIOpts := mtest.NewOptions().ClientType(mtest.Proxy).MinServerVersion(\"5.0\").RequireAPIVersion(true)\n\tmt.RunOpts(\"OP_MSG used for handshakes when API version declared\", opMsgSAPIOpts, func(mt *mtest.T) {\n\t\terr := mt.Client.Ping(context.Background(), mtest.PrimaryRp)\n\t\tassert.Nil(mt, err, \"Ping error: %v\", err)\n\n\t\t// First three messages should be connection handshakes: one for the heartbeat connection, another for the\n\t\t// application connection, and a final one for the RTT monitor connection.\n\t\tfor idx := 0; idx < 3; idx++ {\n\t\t\tmessage := mt.GetProxyCapture().TryNext()\n\t\t\trequire.NotNil(mt, message, \"expected handshake message, got nil\")\n\n\t\t\tassert.True(t, message.IsHandshake())\n\n\t\t\t// Assert that appended OpCode is OP_MSG when API version is set.\n\t\t\topCode := message.Sent.OpCode\n\t\t\tassert.Equal(mt, wiremessage.OpMsg, opCode,\n\t\t\t\t\"expected 'OP_MSG' OpCode in wire message, got %q\", opCode.String())\n\t\t}\n\t})\n\n\topts := mtest.NewOptions().\n\t\t// Blocking failpoints don't work on pre-4.2 and sharded clusters.\n\t\tTopologies(mtest.Single, mtest.ReplicaSet).\n\t\tMinServerVersion(\"4.2\").\n\t\t// Expliticly enable retryable reads and retryable writes.\n\t\tClientOptions(options.Client().SetRetryReads(true).SetRetryWrites(true))\n\tmt.RunOpts(\"operations don't retry after a context timeout\", opts, func(mt *mtest.T) {\n\t\ttestCases := []struct {\n\t\t\tdesc      string\n\t\t\toperation func(context.Context, *mongo.Collection) error\n\t\t}{\n\t\t\t{\n\t\t\t\tdesc: \"read op\",\n\t\t\t\toperation: func(ctx context.Context, coll *mongo.Collection) error {\n\t\t\t\t\treturn coll.FindOne(ctx, bson.D{}).Err()\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tdesc: \"write op\",\n\t\t\t\toperation: func(ctx context.Context, coll *mongo.Collection) error {\n\t\t\t\t\t_, err := coll.InsertOne(ctx, bson.D{})\n\t\t\t\t\treturn err\n\t\t\t\t},\n\t\t\t},\n\t\t}\n\n\t\tfor _, tc := range testCases {\n\t\t\tmt.Run(tc.desc, func(mt *mtest.T) {\n\t\t\t\t_, err := mt.Coll.InsertOne(context.Background(), bson.D{})\n\t\t\t\trequire.NoError(mt, err)\n\n\t\t\t\tmt.SetFailPoint(failpoint.FailPoint{\n\t\t\t\t\tConfigureFailPoint: \"failCommand\",\n\t\t\t\t\tMode:               failpoint.ModeAlwaysOn,\n\t\t\t\t\tData: failpoint.Data{\n\t\t\t\t\t\tFailCommands:    []string{\"find\", \"insert\"},\n\t\t\t\t\t\tBlockConnection: true,\n\t\t\t\t\t\tBlockTimeMS:     500,\n\t\t\t\t\t},\n\t\t\t\t})\n\n\t\t\t\tmt.ClearEvents()\n\n\t\t\t\tfor i := 0; i < 50; i++ {\n\t\t\t\t\t// Run 50 operations, each with a timeout of 50ms. Expect\n\t\t\t\t\t// them to all return a timeout error because the failpoint\n\t\t\t\t\t// blocks find operations for 500ms. Run 50 to increase the\n\t\t\t\t\t// probability that an operation will time out in a way that\n\t\t\t\t\t// can cause a retry.\n\t\t\t\t\tctx, cancel := context.WithTimeout(context.Background(), 50*time.Millisecond)\n\t\t\t\t\terr = tc.operation(ctx, mt.Coll)\n\t\t\t\t\tcancel()\n\t\t\t\t\tassert.ErrorIs(mt, err, context.DeadlineExceeded)\n\t\t\t\t\tassert.True(mt, mongo.IsTimeout(err), \"expected mongo.IsTimeout(err) to be true\")\n\n\t\t\t\t\t// Assert that each operation reported exactly one command\n\t\t\t\t\t// started events, which means the operation did not retry\n\t\t\t\t\t// after the context timeout.\n\t\t\t\t\tevts := mt.GetAllStartedEvents()\n\t\t\t\t\trequire.Len(mt,\n\t\t\t\t\t\tmt.GetAllStartedEvents(),\n\t\t\t\t\t\t1,\n\t\t\t\t\t\t\"expected exactly 1 command started event per operation, but got %d after %d iterations\",\n\t\t\t\t\t\tlen(evts),\n\t\t\t\t\t\ti)\n\t\t\t\t\tmt.ClearEvents()\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t})\n}\n\nfunc TestClient_BulkWrite(t *testing.T) {\n\tmt := mtest.New(t, noClientOpts)\n\n\tmtBulkWriteOpts := mtest.NewOptions().MinServerVersion(\"8.0\").ClientType(mtest.Pinned)\n\tmt.RunOpts(\"bulk write with nil filter\", mtBulkWriteOpts, func(mt *mtest.T) {\n\t\tmt.Parallel()\n\n\t\ttestCases := []struct {\n\t\t\tname        string\n\t\t\twrites      []mongo.ClientBulkWrite\n\t\t\terrorString string\n\t\t}{\n\t\t\t{\n\t\t\t\tname: \"DeleteOne\",\n\t\t\t\twrites: []mongo.ClientBulkWrite{{\n\t\t\t\t\tDatabase:   \"foo\",\n\t\t\t\t\tCollection: \"bar\",\n\t\t\t\t\tModel:      mongo.NewClientDeleteOneModel(),\n\t\t\t\t}},\n\t\t\t\terrorString: \"delete filter cannot be nil\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"DeleteMany\",\n\t\t\t\twrites: []mongo.ClientBulkWrite{{\n\t\t\t\t\tDatabase:   \"foo\",\n\t\t\t\t\tCollection: \"bar\",\n\t\t\t\t\tModel:      mongo.NewClientDeleteManyModel(),\n\t\t\t\t}},\n\t\t\t\terrorString: \"delete filter cannot be nil\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"UpdateOne\",\n\t\t\t\twrites: []mongo.ClientBulkWrite{{\n\t\t\t\t\tDatabase:   \"foo\",\n\t\t\t\t\tCollection: \"bar\",\n\t\t\t\t\tModel:      mongo.NewClientUpdateOneModel(),\n\t\t\t\t}},\n\t\t\t\terrorString: \"update filter cannot be nil\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"UpdateMany\",\n\t\t\t\twrites: []mongo.ClientBulkWrite{{\n\t\t\t\t\tDatabase:   \"foo\",\n\t\t\t\t\tCollection: \"bar\",\n\t\t\t\t\tModel:      mongo.NewClientUpdateManyModel(),\n\t\t\t\t}},\n\t\t\t\terrorString: \"update filter cannot be nil\",\n\t\t\t},\n\t\t}\n\t\tfor _, tc := range testCases {\n\t\t\ttc := tc\n\n\t\t\tmt.Run(tc.name, func(mt *mtest.T) {\n\t\t\t\tmt.Parallel()\n\n\t\t\t\t_, err := mt.Client.BulkWrite(context.Background(), tc.writes)\n\t\t\t\trequire.EqualError(mt, err, tc.errorString)\n\t\t\t})\n\t\t}\n\t})\n\tmt.RunOpts(\"bulk write with write concern\", mtBulkWriteOpts, func(mt *mtest.T) {\n\t\tmt.Parallel()\n\n\t\ttestCases := []struct {\n\t\t\tname string\n\t\t\topts *options.ClientBulkWriteOptionsBuilder\n\t\t\twant bool\n\t\t}{\n\t\t\t{\n\t\t\t\tname: \"unacknowledged\",\n\t\t\t\topts: options.ClientBulkWrite().SetWriteConcern(writeconcern.Unacknowledged()).SetOrdered(false),\n\t\t\t\twant: false,\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"acknowledged\",\n\t\t\t\twant: true,\n\t\t\t},\n\t\t}\n\t\tfor _, tc := range testCases {\n\t\t\ttc := tc\n\n\t\t\tmt.Run(tc.name, func(mt *mtest.T) {\n\t\t\t\tmt.Parallel()\n\n\t\t\t\tinsertOneModel := mongo.NewClientInsertOneModel().SetDocument(bson.D{{\"x\", 1}})\n\t\t\t\twrites := []mongo.ClientBulkWrite{{\n\t\t\t\t\tDatabase:   \"foo\",\n\t\t\t\t\tCollection: \"bar\",\n\t\t\t\t\tModel:      insertOneModel,\n\t\t\t\t}}\n\t\t\t\tres, err := mt.Client.BulkWrite(context.Background(), writes, tc.opts)\n\t\t\t\trequire.NoError(mt, err, \"BulkWrite error: %v\", err)\n\t\t\t\trequire.NotNil(mt, res, \"expected a ClientBulkWriteResult\")\n\t\t\t\tassert.Equal(mt, res.Acknowledged, tc.want, \"expected Acknowledged: %v, got: %v\", tc.want, res.Acknowledged)\n\t\t\t})\n\t\t}\n\t})\n\tvar bulkWrites int\n\tcmdMonitor := &event.CommandMonitor{\n\t\tStarted: func(_ context.Context, evt *event.CommandStartedEvent) {\n\t\t\tif evt.CommandName == \"bulkWrite\" {\n\t\t\t\tbulkWrites++\n\t\t\t}\n\t\t},\n\t}\n\tclientOpts := options.Client().SetMonitor(cmdMonitor)\n\tmt.RunOpts(\"bulk write with large messages\", mtBulkWriteOpts.ClientOptions(clientOpts), func(mt *mtest.T) {\n\t\tmt.Parallel()\n\n\t\tdocument := bson.D{{\"largeField\", strings.Repeat(\"a\", 16777216-100)}} // Adjust size to account for BSON overhead\n\t\twrites := []mongo.ClientBulkWrite{\n\t\t\t{\"db\", \"x\", mongo.NewClientInsertOneModel().SetDocument(document)},\n\t\t\t{\"db\", \"x\", mongo.NewClientInsertOneModel().SetDocument(document)},\n\t\t\t{\"db\", \"x\", mongo.NewClientInsertOneModel().SetDocument(document)},\n\t\t}\n\n\t\t_, err := mt.Client.BulkWrite(context.Background(), writes)\n\t\trequire.NoError(mt, err)\n\t\tassert.Equal(mt, 2, bulkWrites, \"expected %d bulkWrites, got %d\", 2, bulkWrites)\n\t})\n}\n\nfunc TestClient_BulkWrite_AddCommandFields(t *testing.T) {\n\tnewOpts := func(option bson.D) *options.ClientBulkWriteOptionsBuilder {\n\t\topts := options.ClientBulkWrite()\n\t\terr := xoptions.SetInternalClientBulkWriteOptions(opts, \"addCommandFields\", option)\n\t\trequire.NoError(t, err, \"unexpected error: %v\", err)\n\t\treturn opts\n\t}\n\n\tmarshalValue := func(val interface{}) bson.RawValue {\n\t\tt.Helper()\n\n\t\tvalType, data, err := bson.MarshalValue(val)\n\t\trequire.NoError(t, err, \"MarshalValue error: %v\", err)\n\t\treturn bson.RawValue{\n\t\t\tType:  valType,\n\t\t\tValue: data,\n\t\t}\n\t}\n\n\tmodels := []struct {\n\t\tname  string\n\t\tmodel mongo.ClientWriteModel\n\t}{\n\t\t{\n\t\t\tname:  \"insert one\",\n\t\t\tmodel: mongo.NewClientInsertOneModel().SetDocument(bson.D{{\"x\", 1}}),\n\t\t},\n\t\t{\n\t\t\tname:  \"update one\",\n\t\t\tmodel: mongo.NewClientUpdateOneModel().SetFilter(bson.D{{\"x\", 1}}).SetUpdate(bson.D{{\"$set\", bson.D{{\"x\", 3.14159}}}}),\n\t\t},\n\t\t{\n\t\t\tname:  \"update many\",\n\t\t\tmodel: mongo.NewClientUpdateManyModel().SetFilter(bson.D{{\"x\", 1}}).SetUpdate(bson.D{{\"$set\", bson.D{{\"x\", 3.14159}}}}),\n\t\t},\n\t\t{\n\t\t\tname:  \"replace one\",\n\t\t\tmodel: mongo.NewClientReplaceOneModel().SetFilter(bson.D{{\"x\", 1}}).SetReplacement(bson.D{{\"x\", 3.14159}}),\n\t\t},\n\t}\n\n\ttestCases := []struct {\n\t\tname     string\n\t\topts     *options.ClientBulkWriteOptionsBuilder\n\t\texpected bson.RawValue\n\t}{\n\t\t{\n\t\t\tname:     \"empty\",\n\t\t\topts:     options.ClientBulkWrite(),\n\t\t\texpected: bson.RawValue{},\n\t\t},\n\t\t{\n\t\t\tname:     \"false\",\n\t\t\topts:     newOpts(bson.D{{\"bypassEmptyTsReplacement\", false}}),\n\t\t\texpected: marshalValue(false),\n\t\t},\n\t\t{\n\t\t\tname:     \"true\",\n\t\t\topts:     newOpts(bson.D{{\"bypassEmptyTsReplacement\", true}}),\n\t\t\texpected: marshalValue(true),\n\t\t},\n\t}\n\n\tmtBulkWriteOpts := mtest.NewOptions().MinServerVersion(\"8.0\").ClientType(mtest.Pinned)\n\tmt := mtest.New(t, noClientOpts)\n\tfor _, m := range models {\n\t\tfor _, tc := range testCases {\n\t\t\tmt.RunOpts(fmt.Sprintf(\"%s %s\", m.name, tc.name), mtBulkWriteOpts, func(mt *mtest.T) {\n\t\t\t\tmt.Parallel()\n\n\t\t\t\twrites := []mongo.ClientBulkWrite{{\n\t\t\t\t\tDatabase:   \"foo\",\n\t\t\t\t\tCollection: \"bar\",\n\t\t\t\t\tModel:      m.model,\n\t\t\t\t}}\n\t\t\t\t_, err := mt.Client.BulkWrite(context.Background(), writes, tc.opts)\n\t\t\t\trequire.NoError(mt, err, \"BulkWrite error: %v\", err)\n\t\t\t\tevt := mt.GetStartedEvent()\n\t\t\t\tval := evt.Command.Lookup(\"bypassEmptyTsReplacement\")\n\t\t\t\tassert.Equal(mt, tc.expected, val, \"expected bypassEmptyTsReplacement to be %s\", tc.expected.String())\n\t\t\t})\n\t\t}\n\t}\n}\n\nfunc TestClient_BSONOptions(t *testing.T) {\n\tt.Parallel()\n\n\tmt := mtest.New(t, noClientOpts)\n\n\ttype jsonTagsTest struct {\n\t\tA string\n\t\tB string `json:\"x\"`\n\t\tC string `json:\"y\" bson:\"3\"`\n\t}\n\n\ttype omitemptyTest struct {\n\t\tX jsonTagsTest `bson:\"x,omitempty\"`\n\t}\n\n\ttype truncatingDoublesTest struct {\n\t\tX int\n\t}\n\n\ttype timeZoneTest struct {\n\t\tX time.Time\n\t}\n\n\ttimestamp, _ := time.Parse(time.RFC3339, \"2006-01-02T15:04:05+07:00\")\n\n\ttestCases := []struct {\n\t\tname       string\n\t\tbsonOpts   *options.BSONOptions\n\t\tdoc        any\n\t\tdecodeInto func() any\n\t\twant       any\n\t\twantRaw    bson.Raw\n\t}{\n\t\t{\n\t\t\tname: \"UseJSONStructTags\",\n\t\t\tbsonOpts: &options.BSONOptions{\n\t\t\t\tUseJSONStructTags: true,\n\t\t\t},\n\t\t\tdoc: jsonTagsTest{\n\t\t\t\tA: \"apple\",\n\t\t\t\tB: \"banana\",\n\t\t\t\tC: \"carrot\",\n\t\t\t},\n\t\t\tdecodeInto: func() any { return &jsonTagsTest{} },\n\t\t\twant: &jsonTagsTest{\n\t\t\t\tA: \"apple\",\n\t\t\t\tB: \"banana\",\n\t\t\t\tC: \"carrot\",\n\t\t\t},\n\t\t\twantRaw: bson.Raw(bsoncore.NewDocumentBuilder().\n\t\t\t\tAppendString(\"a\", \"apple\").\n\t\t\t\tAppendString(\"x\", \"banana\").\n\t\t\t\tAppendString(\"3\", \"carrot\").\n\t\t\t\tBuild()),\n\t\t},\n\t\t{\n\t\t\tname: \"IntMinSize\",\n\t\t\tbsonOpts: &options.BSONOptions{\n\t\t\t\tIntMinSize: true,\n\t\t\t},\n\t\t\tdoc:        bson.D{{Key: \"x\", Value: int64(1)}},\n\t\t\tdecodeInto: func() any { return &bson.D{} },\n\t\t\twant:       &bson.D{{Key: \"x\", Value: int32(1)}},\n\t\t\twantRaw: bson.Raw(bsoncore.NewDocumentBuilder().\n\t\t\t\tAppendInt32(\"x\", 1).\n\t\t\t\tBuild()),\n\t\t},\n\t\t{\n\t\t\tname: \"NilMapAsEmpty\",\n\t\t\tbsonOpts: &options.BSONOptions{\n\t\t\t\tNilMapAsEmpty: true,\n\t\t\t},\n\t\t\tdoc:        bson.D{{Key: \"x\", Value: map[string]string(nil)}},\n\t\t\tdecodeInto: func() any { return &bson.D{} },\n\t\t\twant:       &bson.D{{Key: \"x\", Value: bson.D{}}},\n\t\t\twantRaw: bson.Raw(bsoncore.NewDocumentBuilder().\n\t\t\t\tAppendDocument(\"x\", bsoncore.NewDocumentBuilder().Build()).\n\t\t\t\tBuild()),\n\t\t},\n\t\t{\n\t\t\tname: \"NilSliceAsEmpty\",\n\t\t\tbsonOpts: &options.BSONOptions{\n\t\t\t\tNilSliceAsEmpty: true,\n\t\t\t},\n\t\t\tdoc:        bson.D{{Key: \"x\", Value: []int(nil)}},\n\t\t\tdecodeInto: func() any { return &bson.D{} },\n\t\t\twant:       &bson.D{{Key: \"x\", Value: bson.A{}}},\n\t\t\twantRaw: bson.Raw(bsoncore.NewDocumentBuilder().\n\t\t\t\tAppendArray(\"x\", bsoncore.NewDocumentBuilder().Build()).\n\t\t\t\tBuild()),\n\t\t},\n\t\t{\n\t\t\tname: \"NilByteSliceAsEmpty\",\n\t\t\tbsonOpts: &options.BSONOptions{\n\t\t\t\tNilByteSliceAsEmpty: true,\n\t\t\t},\n\t\t\tdoc:        bson.D{{Key: \"x\", Value: []byte(nil)}},\n\t\t\tdecodeInto: func() any { return &bson.D{} },\n\t\t\twant:       &bson.D{{Key: \"x\", Value: bson.Binary{Data: []byte{}}}},\n\t\t\twantRaw: bson.Raw(bsoncore.NewDocumentBuilder().\n\t\t\t\tAppendBinary(\"x\", 0, nil).\n\t\t\t\tBuild()),\n\t\t},\n\t\t{\n\t\t\tname: \"OmitZeroStruct\",\n\t\t\tbsonOpts: &options.BSONOptions{\n\t\t\t\tOmitZeroStruct: true,\n\t\t\t},\n\t\t\tdoc:        omitemptyTest{},\n\t\t\tdecodeInto: func() any { return &bson.D{} },\n\t\t\twant:       &bson.D{},\n\t\t\twantRaw:    bson.Raw(bsoncore.NewDocumentBuilder().Build()),\n\t\t},\n\t\t{\n\t\t\tname: \"OmitEmpty with non-zeroer struct\",\n\t\t\tbsonOpts: &options.BSONOptions{\n\t\t\t\tOmitZeroStruct: true,\n\t\t\t\tOmitEmpty:      true,\n\t\t\t},\n\t\t\tdoc: struct {\n\t\t\t\tX jsonTagsTest `bson:\"x\"`\n\t\t\t}{},\n\t\t\tdecodeInto: func() any { return &bson.D{} },\n\t\t\twant:       &bson.D{},\n\t\t\twantRaw:    bson.Raw(bsoncore.NewDocumentBuilder().Build()),\n\t\t},\n\t\t{\n\t\t\tname: \"StringifyMapKeysWithFmt\",\n\t\t\tbsonOpts: &options.BSONOptions{\n\t\t\t\tStringifyMapKeysWithFmt: true,\n\t\t\t},\n\t\t\tdoc:        map[intKey]string{intKey(42): \"foo\"},\n\t\t\tdecodeInto: func() any { return &bson.D{} },\n\t\t\twant:       &bson.D{{\"42\", \"foo\"}},\n\t\t\twantRaw: bson.Raw(bsoncore.NewDocumentBuilder().\n\t\t\t\tAppendString(\"42\", \"foo\").\n\t\t\t\tBuild()),\n\t\t},\n\t\t{\n\t\t\tname: \"AllowTruncatingDoubles\",\n\t\t\tbsonOpts: &options.BSONOptions{\n\t\t\t\tAllowTruncatingDoubles: true,\n\t\t\t},\n\t\t\tdoc:        bson.D{{Key: \"x\", Value: 3.14}},\n\t\t\tdecodeInto: func() any { return &truncatingDoublesTest{} },\n\t\t\twant:       &truncatingDoublesTest{3},\n\t\t\twantRaw: bson.Raw(bsoncore.NewDocumentBuilder().\n\t\t\t\tAppendDouble(\"x\", 3.14).\n\t\t\t\tBuild()),\n\t\t},\n\t\t{\n\t\t\tname: \"BinaryAsSlice\",\n\t\t\tbsonOpts: &options.BSONOptions{\n\t\t\t\tBinaryAsSlice: true,\n\t\t\t},\n\t\t\tdoc:        bson.D{{Key: \"x\", Value: []byte{42}}},\n\t\t\tdecodeInto: func() any { return &bson.D{} },\n\t\t\twant:       &bson.D{{Key: \"x\", Value: []byte{42}}},\n\t\t\twantRaw: bson.Raw(bsoncore.NewDocumentBuilder().\n\t\t\t\tAppendBinary(\"x\", 0, []byte{42}).\n\t\t\t\tBuild()),\n\t\t},\n\t\t{\n\t\t\tname: \"DefaultDocumentM\",\n\t\t\tbsonOpts: &options.BSONOptions{\n\t\t\t\tDefaultDocumentM: true,\n\t\t\t},\n\t\t\tdoc:        bson.D{{Key: \"doc\", Value: bson.D{{Key: \"a\", Value: int64(1)}}}},\n\t\t\tdecodeInto: func() any { return &bson.D{} },\n\t\t\twant:       &bson.D{{Key: \"doc\", Value: bson.M{\"a\": int64(1)}}},\n\t\t},\n\t\t{\n\t\t\tname: \"DefaultDocumentMap\",\n\t\t\tbsonOpts: &options.BSONOptions{\n\t\t\t\tDefaultDocumentMap: true,\n\t\t\t},\n\t\t\tdoc:        bson.D{{Key: \"doc\", Value: bson.D{{Key: \"a\", Value: int64(1)}}}},\n\t\t\tdecodeInto: func() any { return &bson.D{} },\n\t\t\twant:       &bson.D{{Key: \"doc\", Value: map[string]any{\"a\": int64(1)}}},\n\t\t},\n\t\t{\n\t\t\tname: \"UseLocalTimeZone\",\n\t\t\tbsonOpts: &options.BSONOptions{\n\t\t\t\tUseLocalTimeZone: true,\n\t\t\t},\n\t\t\tdoc:        bson.D{{Key: \"x\", Value: timestamp}},\n\t\t\tdecodeInto: func() any { return &timeZoneTest{} },\n\t\t\twant:       &timeZoneTest{timestamp.In(time.Local)},\n\t\t},\n\t\t{\n\t\t\tname: \"ZeroMaps\",\n\t\t\tbsonOpts: &options.BSONOptions{\n\t\t\t\tZeroMaps: true,\n\t\t\t},\n\t\t\tdoc: bson.D{{\"a\", \"apple\"}, {\"b\", \"banana\"}},\n\t\t\tdecodeInto: func() any {\n\t\t\t\treturn &map[string]string{\n\t\t\t\t\t\"b\": \"berry\",\n\t\t\t\t\t\"c\": \"carrot\",\n\t\t\t\t}\n\t\t\t},\n\t\t\twant: &map[string]string{\n\t\t\t\t\"a\": \"apple\",\n\t\t\t\t\"b\": \"banana\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"ZeroStructs\",\n\t\t\tbsonOpts: &options.BSONOptions{\n\t\t\t\tZeroStructs: true,\n\t\t\t},\n\t\t\tdoc: bson.D{{\"a\", \"apple\"}, {\"x\", \"broccoli\"}},\n\t\t\tdecodeInto: func() any {\n\t\t\t\treturn &jsonTagsTest{\n\t\t\t\t\tB: \"banana\",\n\t\t\t\t\tC: \"carrot\",\n\t\t\t\t}\n\t\t\t},\n\t\t\twant: &jsonTagsTest{\n\t\t\t\tA: \"apple\",\n\t\t\t\tB: \"broccoli\",\n\t\t\t\tC: \"\",\n\t\t\t},\n\t\t},\n\t}\n\n\tfor _, tc := range testCases {\n\t\topts := mtest.NewOptions().ClientOptions(\n\t\t\toptions.Client().SetBSONOptions(tc.bsonOpts))\n\t\tmt.RunOpts(tc.name, opts, func(mt *mtest.T) {\n\t\t\tres, err := mt.Coll.InsertOne(context.Background(), tc.doc)\n\t\t\trequire.NoError(mt, err, \"InsertOne error\")\n\n\t\t\tsr := mt.Coll.FindOne(\n\t\t\t\tcontext.Background(),\n\t\t\t\tbson.D{{Key: \"_id\", Value: res.InsertedID}},\n\t\t\t\t// Exclude the auto-generated \"_id\" field so we can make simple\n\t\t\t\t// assertions on the return value.\n\t\t\t\toptions.FindOne().SetProjection(bson.D{{Key: \"_id\", Value: 0}}))\n\n\t\t\tif tc.want != nil {\n\t\t\t\tgot := tc.decodeInto()\n\t\t\t\terr := sr.Decode(got)\n\t\t\t\trequire.NoError(mt, err, \"Decode error\")\n\n\t\t\t\tassert.Equal(mt, tc.want, got, \"expected and actual decoded result are different\")\n\t\t\t}\n\n\t\t\tif tc.wantRaw != nil {\n\t\t\t\tgot, err := sr.Raw()\n\t\t\t\trequire.NoError(mt, err, \"Raw error\")\n\n\t\t\t\tassertbson.EqualDocument(mt, tc.wantRaw, got)\n\t\t\t}\n\t\t})\n\t}\n\n\topts := mtest.NewOptions().ClientOptions(\n\t\toptions.Client().SetBSONOptions(&options.BSONOptions{\n\t\t\tObjectIDAsHexString: true,\n\t\t}))\n\tmt.RunOpts(\"ObjectIDAsHexString\", opts, func(mt *mtest.T) {\n\t\tres, err := mt.Coll.InsertOne(context.Background(), bson.D{{\"x\", 42}})\n\t\trequire.NoError(mt, err, \"InsertOne error\")\n\n\t\tsr := mt.Coll.FindOne(\n\t\t\tcontext.Background(),\n\t\t\tbson.D{{Key: \"_id\", Value: res.InsertedID}},\n\t\t)\n\n\t\ttype data struct {\n\t\t\tID string `bson:\"_id\"`\n\t\t\tX  int    `bson:\"x\"`\n\t\t}\n\t\tvar got data\n\n\t\terr = sr.Decode(&got)\n\t\trequire.NoError(mt, err, \"Decode error\")\n\n\t\twant := data{\n\t\t\tID: res.InsertedID.(bson.ObjectID).Hex(),\n\t\t\tX:  42,\n\t\t}\n\t\tassert.Equal(mt, want, got, \"expected and actual decoded result are different\")\n\t})\n\n\topts = mtest.NewOptions().ClientOptions(\n\t\toptions.Client().SetBSONOptions(&options.BSONOptions{\n\t\t\tErrorOnInlineDuplicates: true,\n\t\t}))\n\tmt.RunOpts(\"ErrorOnInlineDuplicates\", opts, func(mt *mtest.T) {\n\t\ttype inlineDupInner struct {\n\t\t\tA string\n\t\t}\n\n\t\ttype inlineDupOuter struct {\n\t\t\tA string\n\t\t\tB *inlineDupInner `bson:\"b,inline\"`\n\t\t}\n\n\t\t_, err := mt.Coll.InsertOne(context.Background(), inlineDupOuter{\n\t\t\tA: \"outer\",\n\t\t\tB: &inlineDupInner{\n\t\t\t\tA: \"inner\",\n\t\t\t},\n\t\t})\n\t\trequire.Error(mt, err, \"expected InsertOne to return an error\")\n\t})\n}\n\nfunc TestClientStress(t *testing.T) {\n\tmtOpts := mtest.NewOptions().CreateClient(false)\n\tmt := mtest.New(t, mtOpts)\n\n\t// Test that a Client can recover from a massive traffic spike after the traffic spike is over.\n\tmt.Run(\"Client recovers from traffic spike\", func(mt *mtest.T) {\n\t\toid := bson.NewObjectID()\n\t\tdoc := bson.D{{Key: \"_id\", Value: oid}, {Key: \"key\", Value: \"value\"}}\n\t\t_, err := mt.Coll.InsertOne(context.Background(), doc)\n\t\tassert.Nil(mt, err, \"InsertOne error: %v\", err)\n\n\t\t// findOne calls FindOne(\"_id\": oid) on the given collection and with the given timeout. It\n\t\t// returns any errors.\n\t\tfindOne := func(coll *mongo.Collection, timeout time.Duration) error {\n\t\t\tctx, cancel := context.WithTimeout(context.Background(), timeout)\n\t\t\tdefer cancel()\n\t\t\tvar res map[string]any\n\t\t\treturn coll.FindOne(ctx, bson.D{{Key: \"_id\", Value: oid}}).Decode(&res)\n\t\t}\n\n\t\t// findOneFor calls FindOne on the given collection and with the given timeout in a loop for\n\t\t// the given duration and returns any errors returned by FindOne.\n\t\tfindOneFor := func(coll *mongo.Collection, timeout time.Duration, d time.Duration) []error {\n\t\t\terrs := make([]error, 0)\n\t\t\tstart := time.Now()\n\t\t\tfor time.Since(start) <= d {\n\t\t\t\terr := findOne(coll, timeout)\n\t\t\t\tif err != nil {\n\t\t\t\t\terrs = append(errs, err)\n\t\t\t\t}\n\t\t\t\ttime.Sleep(10 * time.Microsecond)\n\t\t\t}\n\t\t\treturn errs\n\t\t}\n\n\t\t// Calculate the maximum observed round-trip time by measuring the duration of some FindOne\n\t\t// operations and picking the max.\n\t\tvar maxRTT time.Duration\n\t\tfor i := 0; i < 50; i++ {\n\t\t\tstart := time.Now()\n\t\t\terr := findOne(mt.Coll, 10*time.Second)\n\t\t\tassert.Nil(t, err, \"FindOne error: %v\", err)\n\t\t\tduration := time.Since(start)\n\t\t\tif duration > maxRTT {\n\t\t\t\tmaxRTT = duration\n\t\t\t}\n\t\t}\n\t\tassert.True(mt, maxRTT > 0, \"RTT must be greater than 0\")\n\n\t\t// Run tests with various \"maxPoolSize\" values, including 1-connection pools and the default\n\t\t// size of 100, to test how the client handles traffic spikes using different connection\n\t\t// pool configurations.\n\t\tmaxPoolSizes := []uint64{1, 10, 100}\n\t\tfor _, maxPoolSize := range maxPoolSizes {\n\t\t\ttpm := eventtest.NewTestPoolMonitor()\n\t\t\tmaxPoolSizeOpt := mtest.NewOptions().ClientOptions(\n\t\t\t\toptions.Client().\n\t\t\t\t\tSetPoolMonitor(tpm.PoolMonitor).\n\t\t\t\t\tSetMaxPoolSize(maxPoolSize))\n\t\t\tmt.RunOpts(fmt.Sprintf(\"maxPoolSize %d\", maxPoolSize), maxPoolSizeOpt, func(mt *mtest.T) {\n\t\t\t\t// Print the count of connection created, connection closed, and pool clear events\n\t\t\t\t// collected during the test to help with debugging.\n\t\t\t\tdefer func() {\n\t\t\t\t\tcreated := len(tpm.Events(func(e *event.PoolEvent) bool { return e.Type == event.ConnectionCreated }))\n\t\t\t\t\tclosed := len(tpm.Events(func(e *event.PoolEvent) bool { return e.Type == event.ConnectionClosed }))\n\t\t\t\t\tpoolCleared := len(tpm.Events(func(e *event.PoolEvent) bool { return e.Type == event.ConnectionPoolCleared }))\n\t\t\t\t\tmt.Logf(\"Connections created: %d, connections closed: %d, pool clears: %d\", created, closed, poolCleared)\n\t\t\t\t}()\n\n\t\t\t\tdoc := bson.D{{Key: \"_id\", Value: oid}, {Key: \"key\", Value: \"value\"}}\n\t\t\t\t_, err := mt.Coll.InsertOne(context.Background(), doc)\n\t\t\t\tassert.Nil(mt, err, \"InsertOne error: %v\", err)\n\n\t\t\t\t// Set the timeout to be 100x the maximum observed RTT. Use a minimum 100ms timeout to\n\t\t\t\t// prevent spurious test failures due to extremely low timeouts.\n\t\t\t\ttimeout := maxRTT * 100\n\t\t\t\tminTimeout := 100 * time.Millisecond\n\t\t\t\tif timeout < minTimeout {\n\t\t\t\t\ttimeout = minTimeout\n\t\t\t\t}\n\t\t\t\tmt.Logf(\"Max RTT %v; using a timeout of %v\", maxRTT, timeout)\n\n\t\t\t\t// Warm up the client for 1 second to allow connections to be established. Ignore\n\t\t\t\t// any errors.\n\t\t\t\t_ = findOneFor(mt.Coll, timeout, 1*time.Second)\n\n\t\t\t\t// Simulate normal traffic by running one FindOne loop for 1 second and assert that there\n\t\t\t\t// are no errors.\n\t\t\t\terrs := findOneFor(mt.Coll, timeout, 1*time.Second)\n\t\t\t\tassert.True(mt, len(errs) == 0, \"expected no errors, but got %d (%v)\", len(errs), errs)\n\n\t\t\t\t// Simulate an extreme traffic spike by running 1,000 FindOne loops in parallel for 10\n\t\t\t\t// seconds and expect at least some errors to occur.\n\t\t\t\tg := new(errgroup.Group)\n\t\t\t\tfor i := 0; i < 1000; i++ {\n\t\t\t\t\tg.Go(func() error {\n\t\t\t\t\t\terrs := findOneFor(mt.Coll, timeout, 10*time.Second)\n\t\t\t\t\t\tif len(errs) == 0 {\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn errs[len(errs)-1]\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t\terr = g.Wait()\n\t\t\t\tmt.Logf(\"Error from extreme traffic spike (errors are expected): %v\", err)\n\n\t\t\t\t// Simulate normal traffic again for 10 seconds. Ignore any errors to allow any outstanding\n\t\t\t\t// connection errors to stop.\n\t\t\t\t_ = findOneFor(mt.Coll, timeout, 10*time.Second)\n\n\t\t\t\t// Simulate normal traffic again for 1 second and assert that there are no errors.\n\t\t\t\terrs = findOneFor(mt.Coll, timeout, 1*time.Second)\n\t\t\t\tassert.True(mt, len(errs) == 0, \"expected no errors, but got %d (%v)\", len(errs), errs)\n\t\t\t})\n\t\t}\n\t})\n}\n"
  },
  {
    "path": "internal/integration/cmd_monitoring_helpers_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage integration\n\nimport (\n\t\"bytes\"\n\t\"encoding/hex\"\n\t\"errors\"\n\t\"fmt\"\n\t\"strings\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/event\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/integration/mtest\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n)\n\n// Helper functions to compare BSON values and command monitoring expectations.\n\nfunc numberFromValue(mt *mtest.T, val bson.RawValue) int64 {\n\tmt.Helper()\n\n\tswitch val.Type {\n\tcase bson.TypeInt32:\n\t\treturn int64(val.Int32())\n\tcase bson.TypeInt64:\n\t\treturn val.Int64()\n\tcase bson.TypeDouble:\n\t\treturn int64(val.Double())\n\tdefault:\n\t\tmt.Fatalf(\"unexpected type for number: %v\", val.Type)\n\t}\n\n\treturn 0\n}\n\nfunc compareNumberValues(mt *mtest.T, key string, expected, actual bson.RawValue) error {\n\tmt.Helper()\n\n\teInt := numberFromValue(mt, expected)\n\tif eInt == 42 {\n\t\tif actual.Type == bson.TypeNull {\n\t\t\treturn fmt.Errorf(\"expected non-null value for key %s, got null\", key)\n\t\t}\n\t\treturn nil\n\t}\n\n\taInt := numberFromValue(mt, actual)\n\tif eInt != aInt {\n\t\treturn fmt.Errorf(\"value mismatch for key %s; expected %s, got %s\", key, expected, actual)\n\t}\n\treturn nil\n}\n\n// compare BSON values and fail if they are not equal. the key parameter is used for error strings.\n// if the expected value is a numeric type (int32, int64, or double) and the value is 42, the function only asserts that\n// the actual value is non-null.\nfunc compareValues(mt *mtest.T, key string, expected, actual bson.RawValue) error {\n\tmt.Helper()\n\n\tswitch expected.Type {\n\tcase bson.TypeInt32, bson.TypeInt64, bson.TypeDouble:\n\t\tif err := compareNumberValues(mt, key, expected, actual); err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn nil\n\tcase bson.TypeString:\n\t\tval := expected.StringValue()\n\t\tif val == \"42\" {\n\t\t\tif actual.Type == bson.TypeNull {\n\t\t\t\treturn fmt.Errorf(\"expected non-null value for key %s, got null\", key)\n\t\t\t}\n\t\t\treturn nil\n\t\t}\n\t\t// Don't return. Compare bytes for expected.Value and actual.Value outside of the switch.\n\tcase bson.TypeEmbeddedDocument:\n\t\te := expected.Document()\n\t\tif typeVal, err := e.LookupErr(\"$$type\"); err == nil {\n\t\t\t// $$type represents a type assertion\n\t\t\t// for example {field: {$$type: \"binData\"}} should assert that \"field\" is an element with a binary value\n\t\t\ttypes := []string{}\n\t\t\tswitch typeVal.Type {\n\t\t\tcase bson.TypeArray:\n\t\t\t\telems, err := typeVal.Array().Values()\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn fmt.Errorf(\"error getting expected types: %v\", err)\n\t\t\t\t}\n\n\t\t\t\tfor _, elem := range elems {\n\t\t\t\t\ttypes = append(types, elem.StringValue())\n\t\t\t\t}\n\t\t\tcase bson.TypeString:\n\t\t\t\ttypes = append(types, typeVal.StringValue())\n\t\t\t}\n\n\t\t\t// If at least one of the types does not return an error, then the test\n\t\t\t// has passed.\n\t\t\tfor _, t := range types {\n\t\t\t\tif err := checkValueType(mt, key, actual.Type, t); err == nil {\n\t\t\t\t\treturn nil\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn fmt.Errorf(\"BSON type mismatch for key %s; expected one of %v, got %s\", key, types, actual.Type)\n\t\t}\n\n\t\ta := actual.Document()\n\t\treturn compareDocsHelper(mt, e, a, key)\n\tcase bson.TypeArray:\n\t\te := expected.Array()\n\t\ta := actual.Array()\n\t\treturn compareDocsHelper(mt, bson.Raw(e), bson.Raw(a), key)\n\t}\n\n\tif expected.Type != actual.Type {\n\t\treturn fmt.Errorf(\"type mismatch for key %s; expected %s, got %s\", key, expected.Type, actual.Type)\n\t}\n\tif !bytes.Equal(expected.Value, actual.Value) {\n\t\treturn fmt.Errorf(\n\t\t\t\"value mismatch for key %s; expected %s (hex=%s), got %s (hex=%s)\",\n\t\t\tkey,\n\t\t\texpected.Value,\n\t\t\thex.EncodeToString(expected.Value),\n\t\t\tactual.Value,\n\t\t\thex.EncodeToString(actual.Value))\n\t}\n\treturn nil\n}\n\n// helper for $$type assertions\nfunc checkValueType(mt *mtest.T, key string, actual bson.Type, typeStr string) error {\n\tmt.Helper()\n\n\tvar expected bson.Type\n\tswitch typeStr {\n\tcase \"double\":\n\t\texpected = bson.TypeDouble\n\tcase \"string\":\n\t\texpected = bson.TypeString\n\tcase \"object\":\n\t\texpected = bson.TypeEmbeddedDocument\n\tcase \"array\":\n\t\texpected = bson.TypeArray\n\tcase \"binData\":\n\t\texpected = bson.TypeBinary\n\tcase \"undefined\":\n\t\texpected = bson.TypeUndefined\n\tcase \"objectId\":\n\t\texpected = bson.TypeObjectID\n\tcase \"boolean\":\n\t\texpected = bson.TypeBoolean\n\tcase \"date\":\n\t\texpected = bson.TypeDateTime\n\tcase \"null\":\n\t\texpected = bson.TypeNull\n\tcase \"regex\":\n\t\texpected = bson.TypeRegex\n\tcase \"dbPointer\":\n\t\texpected = bson.TypeDBPointer\n\tcase \"javascript\":\n\t\texpected = bson.TypeJavaScript\n\tcase \"symbol\":\n\t\texpected = bson.TypeSymbol\n\tcase \"javascriptWithScope\":\n\t\texpected = bson.TypeCodeWithScope\n\tcase \"int\":\n\t\texpected = bson.TypeInt32\n\tcase \"timestamp\":\n\t\texpected = bson.TypeTimestamp\n\tcase \"long\":\n\t\texpected = bson.TypeInt64\n\tcase \"decimal\":\n\t\texpected = bson.TypeDecimal128\n\tcase \"minKey\":\n\t\texpected = bson.TypeMinKey\n\tcase \"maxKey\":\n\t\texpected = bson.TypeMaxKey\n\tdefault:\n\t\tmt.Fatalf(\"unrecognized type string: %v\", typeStr)\n\t}\n\n\tif expected != actual {\n\t\treturn fmt.Errorf(\"BSON type mismatch for key %s; expected %s, got %s\", key, expected, actual)\n\t}\n\treturn nil\n}\n\n// compare expected and actual BSON documents. comparison succeeds if actual contains each element in expected.\nfunc compareDocsHelper(mt *mtest.T, expected, actual bson.Raw, prefix string) error {\n\tmt.Helper()\n\n\teElems, err := expected.Elements()\n\tassert.Nil(mt, err, \"error getting expected elements: %v\", err)\n\n\tfor _, e := range eElems {\n\t\teKey := e.Key()\n\t\tfullKeyName := eKey\n\t\tif prefix != \"\" {\n\t\t\tfullKeyName = prefix + \".\" + eKey\n\t\t}\n\n\t\taVal, err := actual.LookupErr(eKey)\n\t\tif e.Value().Type == bson.TypeNull {\n\t\t\t// Expected value is BSON null. Expect the actual field to be omitted.\n\t\t\tif errors.Is(err, bsoncore.ErrElementNotFound) {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"expected key %q to be omitted but got error: %w\", eKey, err)\n\t\t\t}\n\t\t\treturn fmt.Errorf(\"expected key %q to be omitted but got %q\", eKey, aVal)\n\t\t}\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"key %s not found in result\", fullKeyName)\n\t\t}\n\n\t\tif err := compareValues(mt, fullKeyName, e.Value(), aVal); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc compareDocs(mt *mtest.T, expected, actual bson.Raw) error {\n\tmt.Helper()\n\treturn compareDocsHelper(mt, expected, actual, \"\")\n}\n\nfunc checkExpectations(mt *mtest.T, expectations *[]*expectation, id0, id1 bson.Raw) {\n\tmt.Helper()\n\n\t// If the expectations field in the test JSON is null, we want to skip all command monitoring assertions.\n\tif expectations == nil {\n\t\treturn\n\t}\n\n\t// Filter out events that shouldn't show up in monitoring expectations.\n\tignoredEvents := map[string]struct{}{\n\t\t\"configureFailPoint\": {},\n\t}\n\tmt.FilterStartedEvents(func(evt *event.CommandStartedEvent) bool {\n\t\t// ok is true if the command should be ignored, so return !ok\n\t\t_, ok := ignoredEvents[evt.CommandName]\n\t\treturn !ok\n\t})\n\tmt.FilterSucceededEvents(func(evt *event.CommandSucceededEvent) bool {\n\t\t// ok is true if the command should be ignored, so return !ok\n\t\t_, ok := ignoredEvents[evt.CommandName]\n\t\treturn !ok\n\t})\n\tmt.FilterFailedEvents(func(evt *event.CommandFailedEvent) bool {\n\t\t// ok is true if the command should be ignored, so return !ok\n\t\t_, ok := ignoredEvents[evt.CommandName]\n\t\treturn !ok\n\t})\n\n\t// If the expectations field in the test JSON is non-null but is empty, we want to assert that no events were\n\t// emitted.\n\tif len(*expectations) == 0 {\n\t\t// One of the bulkWrite spec tests expects update and updateMany to be grouped together into a single batch,\n\t\t// but this isn't the case because of GODRIVER-1157. To work around this, we expect one event to be emitted for\n\t\t// that test rather than 0. This assertion should be changed when GODRIVER-1157 is done.\n\t\tnumExpectedEvents := 0\n\t\tbulkWriteTestName := \"BulkWrite_on_server_that_doesn't_support_arrayFilters_with_arrayFilters_on_second_op\"\n\t\tif strings.HasSuffix(mt.Name(), bulkWriteTestName) {\n\t\t\tnumExpectedEvents = 1\n\t\t}\n\n\t\tnumActualEvents := len(mt.GetAllStartedEvents())\n\t\tassert.Equal(mt, numExpectedEvents, numActualEvents, \"expected %d events to be sent, but got %d events\",\n\t\t\tnumExpectedEvents, numActualEvents)\n\t\treturn\n\t}\n\n\tfor idx, expectation := range *expectations {\n\t\tvar err error\n\n\t\tif expectation.CommandStartedEvent != nil {\n\t\t\terr = compareStartedEvent(mt, expectation, id0, id1)\n\t\t}\n\t\tif expectation.CommandSucceededEvent != nil {\n\t\t\terr = compareSucceededEvent(mt, expectation)\n\t\t}\n\t\tif expectation.CommandFailedEvent != nil {\n\t\t\terr = compareFailedEvent(mt, expectation)\n\t\t}\n\n\t\tassert.Nil(mt, err, \"expectation comparison error at index %v: %s\", idx, err)\n\t}\n}\n\n// newMatchError appends `expected` and `actual` BSON data to an error.\nfunc newMatchError(mt *mtest.T, expected bson.Raw, actual bson.Raw, format string, args ...any) error {\n\tmt.Helper()\n\tmsg := fmt.Sprintf(format, args...)\n\texpectedJSON, err := bson.MarshalExtJSON(expected, true, false)\n\tassert.Nil(mt, err, \"error in MarshalExtJSON: %v\", err)\n\tactualJSON, err := bson.MarshalExtJSON(actual, true, false)\n\tassert.Nil(mt, err, \"error in MarshalExtJSON: %v\", err)\n\treturn fmt.Errorf(\"%s\\nExpected %s\\nGot: %s\", msg, string(expectedJSON), string(actualJSON))\n}\n\nfunc compareStartedEvent(mt *mtest.T, expectation *expectation, id0, id1 bson.Raw) error {\n\tmt.Helper()\n\n\texpected := expectation.CommandStartedEvent\n\n\tif len(expected.Extra) > 0 {\n\t\treturn fmt.Errorf(\"unrecognized fields for CommandStartedEvent: %v\", expected.Extra)\n\t}\n\n\tevt := mt.GetStartedEvent()\n\tif evt == nil {\n\t\treturn errors.New(\"expected CommandStartedEvent, got nil\")\n\t}\n\n\tif expected.CommandName != \"\" && expected.CommandName != evt.CommandName {\n\t\treturn fmt.Errorf(\"command name mismatch; expected %s, got %s\", expected.CommandName, evt.CommandName)\n\t}\n\tif expected.DatabaseName != \"\" && expected.DatabaseName != evt.DatabaseName {\n\t\treturn fmt.Errorf(\"database name mismatch; expected %s, got %s\", expected.DatabaseName, evt.DatabaseName)\n\t}\n\n\teElems, err := expected.Command.Elements()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"error getting expected command elements: %s\", err)\n\t}\n\n\tfor _, elem := range eElems {\n\t\tkey := elem.Key()\n\t\tval := elem.Value()\n\n\t\tactualVal, err := evt.Command.LookupErr(key)\n\n\t\t// Keys that may be nil\n\t\tif val.Type == bson.TypeNull {\n\t\t\t// Expected value is BSON null. Expect the actual field to be omitted.\n\t\t\tif errors.Is(err, bsoncore.ErrElementNotFound) {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif err != nil {\n\t\t\t\treturn newMatchError(mt, expected.Command, evt.Command, \"expected key %q to be omitted but got error: %v\", key, err)\n\t\t\t}\n\t\t\treturn newMatchError(mt, expected.Command, evt.Command, \"expected key %q to be omitted but got %q\", key, actualVal)\n\t\t}\n\t\tassert.Nil(mt, err, \"expected command to contain key %q\", key)\n\n\t\tif key == \"batchSize\" {\n\t\t\t// Some command monitoring tests expect that the driver will send a lower batch size if the required batch\n\t\t\t// size is lower than the operation limit. We only do this for legacy servers <= 3.0 because those server\n\t\t\t// versions do not support the limit option, but not for 3.2+. We've already validated that the command\n\t\t\t// contains a batchSize field above and we can skip the actual value comparison below.\n\t\t\tcontinue\n\t\t}\n\n\t\tswitch key {\n\t\tcase \"lsid\":\n\t\t\tsessName := val.StringValue()\n\t\t\tvar expectedID bson.Raw\n\t\t\tactualID := actualVal.Document()\n\n\t\t\tswitch sessName {\n\t\t\tcase \"session0\":\n\t\t\t\texpectedID = id0\n\t\t\tcase \"session1\":\n\t\t\t\texpectedID = id1\n\t\t\tdefault:\n\t\t\t\treturn newMatchError(mt, expected.Command, evt.Command, \"unrecognized session identifier in command document: %s\", sessName)\n\t\t\t}\n\n\t\t\tif !bytes.Equal(expectedID, actualID) {\n\t\t\t\treturn newMatchError(mt, expected.Command, evt.Command, \"session ID mismatch for session %s; expected %s, got %s\", sessName, expectedID,\n\t\t\t\t\tactualID)\n\t\t\t}\n\t\tdefault:\n\t\t\tif err := compareValues(mt, key, val, actualVal); err != nil {\n\t\t\t\treturn newMatchError(mt, expected.Command, evt.Command, \"%s\", err)\n\t\t\t}\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc compareWriteErrors(mt *mtest.T, expected, actual bson.Raw) error {\n\tmt.Helper()\n\n\texpectedErrors, _ := expected.Values()\n\tactualErrors, _ := actual.Values()\n\n\tfor i, expectedErrVal := range expectedErrors {\n\t\texpectedErr := expectedErrVal.Document()\n\t\tactualErr := actualErrors[i].Document()\n\n\t\teIdx := expectedErr.Lookup(\"index\").Int32()\n\t\taIdx := actualErr.Lookup(\"index\").Int32()\n\t\tif eIdx != aIdx {\n\t\t\treturn fmt.Errorf(\"write error index mismatch at index %d; expected %d, got %d\", i, eIdx, aIdx)\n\t\t}\n\n\t\teCode := expectedErr.Lookup(\"code\").Int32()\n\t\taCode := actualErr.Lookup(\"code\").Int32()\n\t\tif eCode != 42 && eCode != aCode {\n\t\t\treturn fmt.Errorf(\"write error code mismatch at index %d; expected %d, got %d\", i, eCode, aCode)\n\t\t}\n\n\t\teMsg := expectedErr.Lookup(\"errmsg\").StringValue()\n\t\taMsg := actualErr.Lookup(\"errmsg\").StringValue()\n\t\tif eMsg == \"\" {\n\t\t\tif aMsg == \"\" {\n\t\t\t\treturn fmt.Errorf(\"write error message mismatch at index %d; expected non-empty message, got empty\", i)\n\t\t\t}\n\t\t\treturn nil\n\t\t}\n\t\tif eMsg != aMsg {\n\t\t\treturn fmt.Errorf(\"write error message mismatch at index %d, expected %s, got %s\", i, eMsg, aMsg)\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc compareSucceededEvent(mt *mtest.T, expectation *expectation) error {\n\tmt.Helper()\n\n\texpected := expectation.CommandSucceededEvent\n\tif len(expected.Extra) > 0 {\n\t\treturn fmt.Errorf(\"unrecognized fields for CommandSucceededEvent: %v\", expected.Extra)\n\t}\n\tevt := mt.GetSucceededEvent()\n\tif evt == nil {\n\t\treturn errors.New(\"expected CommandSucceededEvent, got nil\")\n\t}\n\n\tif expected.CommandName != \"\" && expected.CommandName != evt.CommandName {\n\t\treturn fmt.Errorf(\"command name mismatch; expected %s, got %s\", expected.CommandName, evt.CommandName)\n\t}\n\n\teElems, err := expected.Reply.Elements()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"error getting expected reply elements: %s\", err)\n\t}\n\n\tfor _, elem := range eElems {\n\t\tkey := elem.Key()\n\t\tval := elem.Value()\n\t\tactualVal := evt.Reply.Lookup(key)\n\n\t\tswitch key {\n\t\tcase \"writeErrors\":\n\t\t\tif err = compareWriteErrors(mt, bson.Raw(val.Array()), bson.Raw(actualVal.Array())); err != nil {\n\t\t\t\treturn newMatchError(mt, expected.Reply, evt.Reply, \"%s\", err)\n\t\t\t}\n\t\tdefault:\n\t\t\tif err := compareValues(mt, key, val, actualVal); err != nil {\n\t\t\t\treturn newMatchError(mt, expected.Reply, evt.Reply, \"%s\", err)\n\t\t\t}\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc compareFailedEvent(mt *mtest.T, expectation *expectation) error {\n\tmt.Helper()\n\n\texpected := expectation.CommandFailedEvent\n\tif len(expected.Extra) > 0 {\n\t\treturn fmt.Errorf(\"unrecognized fields for CommandFailedEvent: %v\", expected.Extra)\n\t}\n\tevt := mt.GetFailedEvent()\n\tif evt == nil {\n\t\treturn errors.New(\"expected CommandFailedEvent, got nil\")\n\t}\n\n\tif expected.CommandName != \"\" && expected.CommandName != evt.CommandName {\n\t\treturn fmt.Errorf(\"command name mismatch; expected %s, got %s\", expected.CommandName, evt.CommandName)\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "internal/integration/collection_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage integration\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/event\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/integration/mtest\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/require\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/writeconcern\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/drivertest\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/xoptions\"\n)\n\nconst (\n\terrorDuplicateKey           = 11000\n\terrorCappedCollDeleteLegacy = 10101\n\terrorCappedCollDelete       = 20\n\terrorModifiedIDLegacy       = 16837\n\terrorModifiedID             = 66\n)\n\n// impossibleWc is a write concern that can't be satisfied and is used to test write concern errors\n// for various operations. It includes a timeout because legacy servers will wait for all W nodes to respond,\n// causing tests to hang.\nvar impossibleWc = &writeconcern.WriteConcern{\n\tW: 30,\n}\n\nfunc TestCollection(t *testing.T) {\n\tmt := mtest.New(t, mtest.NewOptions().CreateClient(false))\n\n\tmt.RunOpts(\"insert one\", noClientOpts, func(mt *mtest.T) {\n\t\tmt.Run(\"success\", func(mt *mtest.T) {\n\t\t\tid := bson.NewObjectID()\n\t\t\tdoc := bson.D{{\"_id\", id}, {\"x\", 1}}\n\t\t\tres, err := mt.Coll.InsertOne(context.Background(), doc)\n\t\t\trequire.NoError(mt, err, \"InsertOne error: %v\", err)\n\t\t\tassert.Equal(mt, id, res.InsertedID, \"expected inserted ID %v, got %v\", id, res.InsertedID)\n\t\t})\n\t\tmt.Run(\"write error\", func(mt *mtest.T) {\n\t\t\tdoc := bson.D{{\"_id\", 1}}\n\t\t\t_, err := mt.Coll.InsertOne(context.Background(), doc)\n\t\t\trequire.NoError(mt, err, \"InsertOne error: %v\", err)\n\n\t\t\t_, err = mt.Coll.InsertOne(context.Background(), doc)\n\t\t\twe, ok := err.(mongo.WriteException)\n\t\t\tassert.True(mt, ok, \"expected error type %T, got %T\", mongo.WriteException{}, err)\n\t\t\tassert.Equal(mt, 1, len(we.WriteErrors), \"expected 1 write error, got %v\", len(we.WriteErrors))\n\t\t\twriteErr := we.WriteErrors[0]\n\t\t\tassert.Equal(mt, errorDuplicateKey, writeErr.Code, \"expected code %v, got %v\", errorDuplicateKey, writeErr.Code)\n\t\t})\n\n\t\twcCollOpts := options.Collection().SetWriteConcern(impossibleWc)\n\t\twcTestOpts := mtest.NewOptions().CollectionOptions(wcCollOpts).Topologies(mtest.ReplicaSet)\n\t\tmt.RunOpts(\"write concern error\", wcTestOpts, func(mt *mtest.T) {\n\t\t\tdoc := bson.D{{\"_id\", 1}}\n\t\t\t_, err := mt.Coll.InsertOne(context.Background(), doc)\n\t\t\twe, ok := err.(mongo.WriteException)\n\t\t\tassert.True(mt, ok, \"expected error type %v, got %v\", mongo.WriteException{}, err)\n\t\t\tassert.NotNil(mt, we.WriteConcernError, \"expected write concern error, got %+v\", we)\n\t\t})\n\n\t\t// Require 3.2 servers for bypassDocumentValidation support.\n\t\tconvertedOptsOpts := mtest.NewOptions().MinServerVersion(\"3.2\")\n\t\tmt.RunOpts(\"options are converted\", convertedOptsOpts, func(mt *mtest.T) {\n\t\t\tnilOptsTestCases := []struct {\n\t\t\t\tname            string\n\t\t\t\topts            []options.Lister[options.InsertOneOptions]\n\t\t\t\texpectOptionSet bool\n\t\t\t}{\n\t\t\t\t{\n\t\t\t\t\t\"only nil is passed\",\n\t\t\t\t\t[]options.Lister[options.InsertOneOptions]{nil},\n\t\t\t\t\tfalse,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"non-nil options is passed before nil\",\n\t\t\t\t\t[]options.Lister[options.InsertOneOptions]{options.InsertOne().SetBypassDocumentValidation(true), nil},\n\t\t\t\t\ttrue,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"non-nil options is passed after nil\",\n\t\t\t\t\t[]options.Lister[options.InsertOneOptions]{nil, options.InsertOne().SetBypassDocumentValidation(true)},\n\t\t\t\t\ttrue,\n\t\t\t\t},\n\t\t\t}\n\n\t\t\tfor _, testCase := range nilOptsTestCases {\n\t\t\t\tmt.Run(testCase.name, func(mt *mtest.T) {\n\t\t\t\t\tdoc := bson.D{{\"x\", 1}}\n\t\t\t\t\t_, err := mt.Coll.InsertOne(context.Background(), doc, testCase.opts...)\n\t\t\t\t\trequire.NoError(mt, err, \"InsertOne error: %v\", err)\n\t\t\t\t\toptName := \"bypassDocumentValidation\"\n\t\t\t\t\tevt := mt.GetStartedEvent()\n\n\t\t\t\t\tval, err := evt.Command.LookupErr(optName)\n\t\t\t\t\tif testCase.expectOptionSet {\n\t\t\t\t\t\trequire.NoError(mt, err, \"expected %v to be set but got: %v\", optName, err)\n\t\t\t\t\t\tassert.True(mt, val.Boolean(), \"expected %v to be true but got: %v\", optName, val.Boolean())\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t\tassert.NotNil(mt, err, \"expected %v to be unset but got nil\", optName)\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\t})\n\tmt.RunOpts(\"insert many\", noClientOpts, func(mt *mtest.T) {\n\t\tmt.Parallel()\n\n\t\tmt.Run(\"success\", func(mt *mtest.T) {\n\t\t\tmt.Parallel()\n\n\t\t\twant1 := int32(11)\n\t\t\twant2 := int32(12)\n\t\t\tdocs := []any{\n\t\t\t\tbson.D{{\"_id\", want1}},\n\t\t\t\tbson.D{{\"x\", 6}},\n\t\t\t\tbson.D{{\"_id\", want2}},\n\t\t\t}\n\n\t\t\tres, err := mt.Coll.InsertMany(context.Background(), docs)\n\t\t\trequire.NoError(mt, err, \"InsertMany error: %v\", err)\n\t\t\tassert.Equal(mt, 3, len(res.InsertedIDs), \"expected 3 inserted IDs, got %v\", len(res.InsertedIDs))\n\t\t\tassert.Equal(mt, want1, res.InsertedIDs[0], \"expected inserted ID %v, got %v\", want1, res.InsertedIDs[0])\n\t\t\tassert.NotNil(mt, res.InsertedIDs[1], \"expected ID but got nil\")\n\t\t\tassert.Equal(mt, want2, res.InsertedIDs[2], \"expected inserted ID %v, got %v\", want2, res.InsertedIDs[2])\n\t\t})\n\t\tmt.Run(\"batches\", func(mt *mtest.T) {\n\t\t\tmt.Parallel()\n\n\t\t\tconst (\n\t\t\t\tmegabyte = 10 * 10 * 10 * 10 * 10 * 10\n\t\t\t\tnumDocs  = 700000\n\t\t\t)\n\t\t\tvar docs []any\n\t\t\ttotal := uint32(0)\n\t\t\texpectedDocSize := uint32(26)\n\t\t\tfor i := 0; i < numDocs; i++ {\n\t\t\t\td := bson.D{\n\t\t\t\t\t{\"a\", int32(i)},\n\t\t\t\t\t{\"b\", int32(i * 2)},\n\t\t\t\t\t{\"c\", int32(i * 3)},\n\t\t\t\t}\n\t\t\t\tb, _ := bson.Marshal(d)\n\t\t\t\tassert.Equal(mt, int(expectedDocSize), len(b), \"expected doc len %v, got %v\", expectedDocSize, len(b))\n\t\t\t\tdocs = append(docs, d)\n\t\t\t\ttotal += uint32(len(b))\n\t\t\t}\n\t\t\tassert.True(mt, total > 16*megabyte, \"expected total greater than 16mb but got %v\", total)\n\t\t\tres, err := mt.Coll.InsertMany(context.Background(), docs)\n\t\t\trequire.NoError(mt, err, \"InsertMany error: %v\", err)\n\t\t\tassert.Equal(mt, numDocs, len(res.InsertedIDs), \"expected %v inserted IDs, got %v\", numDocs, len(res.InsertedIDs))\n\t\t})\n\t\tmt.Run(\"large document batches\", func(mt *mtest.T) {\n\t\t\tmt.Parallel()\n\n\t\t\tdocs := []any{create16MBDocument(mt), create16MBDocument(mt), create16MBDocument(mt)}\n\t\t\t_, err := mt.Coll.InsertMany(context.Background(), docs)\n\t\t\trequire.NoError(mt, err, \"InsertMany error: %v\", err)\n\t\t\tevt := mt.GetStartedEvent()\n\t\t\tassert.Equal(mt, \"insert\", evt.CommandName, \"expected 'insert' event, got '%v'\", evt.CommandName)\n\t\t\tevt = mt.GetStartedEvent()\n\t\t\tassert.Equal(mt, \"insert\", evt.CommandName, \"expected 'insert' event, got '%v'\", evt.CommandName)\n\t\t})\n\t\tmt.RunOpts(\"write error\", noClientOpts, func(mt *mtest.T) {\n\t\t\tmt.Parallel()\n\n\t\t\tdocs := []any{\n\t\t\t\tbson.D{{\"_id\", bson.NewObjectID()}},\n\t\t\t\tbson.D{{\"_id\", bson.NewObjectID()}},\n\t\t\t\tbson.D{{\"_id\", bson.NewObjectID()}},\n\t\t\t}\n\n\t\t\ttestCases := []struct {\n\t\t\t\tname      string\n\t\t\t\tordered   bool\n\t\t\t\tnumErrors int\n\t\t\t}{\n\t\t\t\t{\"unordered\", false, 3},\n\t\t\t\t{\"ordered\", true, 1},\n\t\t\t}\n\t\t\tfor _, tc := range testCases {\n\t\t\t\tmt.Run(tc.name, func(mt *mtest.T) {\n\t\t\t\t\t_, err := mt.Coll.InsertMany(context.Background(), docs)\n\t\t\t\t\trequire.NoError(mt, err, \"InsertMany error: %v\", err)\n\t\t\t\t\t_, err = mt.Coll.InsertMany(context.Background(), docs, options.InsertMany().SetOrdered(tc.ordered))\n\n\t\t\t\t\twe, ok := err.(mongo.BulkWriteException)\n\t\t\t\t\tassert.True(mt, ok, \"expected error type %T, got %T\", mongo.BulkWriteException{}, err)\n\t\t\t\t\tnumErrors := len(we.WriteErrors)\n\t\t\t\t\tassert.Equal(mt, tc.numErrors, numErrors, \"expected %v write errors, got %v\", tc.numErrors, numErrors)\n\t\t\t\t\tgotCode := we.WriteErrors[0].Code\n\t\t\t\t\tassert.Equal(mt, errorDuplicateKey, gotCode, \"expected error code %v, got %v\", errorDuplicateKey, gotCode)\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\t\tmt.Run(\"return only inserted ids\", func(mt *mtest.T) {\n\t\t\tmt.Parallel()\n\n\t\t\tid := int32(15)\n\t\t\tdocs := []any{\n\t\t\t\tbson.D{{\"_id\", id}},\n\t\t\t\tbson.D{{\"_id\", id}},\n\t\t\t\tbson.D{{\"x\", 6}},\n\t\t\t\tbson.D{{\"_id\", id}},\n\t\t\t}\n\n\t\t\ttestCases := []struct {\n\t\t\t\tname        string\n\t\t\t\tordered     bool\n\t\t\t\tnumInserted int\n\t\t\t\tnumErrors   int\n\t\t\t}{\n\t\t\t\t{\"unordered\", false, 2, 2},\n\t\t\t\t{\"ordered\", true, 1, 1},\n\t\t\t}\n\t\t\tfor _, tc := range testCases {\n\t\t\t\tmt.Run(tc.name, func(mt *mtest.T) {\n\t\t\t\t\tres, err := mt.Coll.InsertMany(context.Background(), docs, options.InsertMany().SetOrdered(tc.ordered))\n\n\t\t\t\t\twe, ok := err.(mongo.BulkWriteException)\n\t\t\t\t\tassert.True(mt, ok, \"expected error type %T, got %T\", mongo.BulkWriteException{}, err)\n\t\t\t\t\tnumErrors := len(we.WriteErrors)\n\t\t\t\t\tassert.Equal(mt, tc.numErrors, numErrors, \"expected %v write errors, got %v\", tc.numErrors, numErrors)\n\t\t\t\t\tgotCode := we.WriteErrors[0].Code\n\t\t\t\t\tassert.Equal(mt, errorDuplicateKey, gotCode, \"expected error code %v, got %v\", errorDuplicateKey, gotCode)\n\n\t\t\t\t\trequire.Greater(mt, len(res.InsertedIDs), 0, \"expected at least one inserted ID\")\n\t\t\t\t\tassert.Equal(mt,\n\t\t\t\t\t\ttc.numInserted,\n\t\t\t\t\t\tlen(res.InsertedIDs),\n\t\t\t\t\t\t\"expected %v inserted IDs, got %v\",\n\t\t\t\t\t\ttc.numInserted,\n\t\t\t\t\t\tlen(res.InsertedIDs))\n\t\t\t\t\tassert.Equal(mt,\n\t\t\t\t\t\tid,\n\t\t\t\t\t\tres.InsertedIDs[0],\n\t\t\t\t\t\t\"expected ID to match\")\n\t\t\t\t\tif tc.numInserted > 1 {\n\t\t\t\t\t\tassert.NotNil(mt, res.InsertedIDs[1], \"expected ID but got nil\")\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\t\tmt.Run(\"writeError index\", func(mt *mtest.T) {\n\t\t\tmt.Parallel()\n\n\t\t\t// force multiple batches\n\t\t\tnumDocs := 700000\n\t\t\tvar docs []any\n\t\t\tfor i := 0; i < numDocs; i++ {\n\t\t\t\td := bson.D{\n\t\t\t\t\t{\"a\", int32(i)},\n\t\t\t\t\t{\"b\", int32(i * 2)},\n\t\t\t\t\t{\"c\", int32(i * 3)},\n\t\t\t\t}\n\t\t\t\tdocs = append(docs, d)\n\t\t\t}\n\t\t\trepeated := bson.D{{\"_id\", int32(11)}}\n\t\t\tdocs = append(docs, repeated, repeated)\n\n\t\t\t_, err := mt.Coll.InsertMany(context.Background(), docs)\n\t\t\tassert.NotNil(mt, err, \"expected InsertMany error, got nil\")\n\n\t\t\twe, ok := err.(mongo.BulkWriteException)\n\t\t\tassert.True(mt, ok, \"expected error type %T, got %T\", mongo.BulkWriteException{}, err)\n\t\t\tnumErrors := len(we.WriteErrors)\n\t\t\tassert.Equal(mt, 1, numErrors, \"expected 1 write error, got %v\", numErrors)\n\t\t\tgotIndex := we.WriteErrors[0].Index\n\t\t\tassert.Equal(mt, numDocs+1, gotIndex, \"expected index %v, got %v\", numDocs+1, gotIndex)\n\t\t})\n\t\twcCollOpts := options.Collection().SetWriteConcern(impossibleWc)\n\t\twcTestOpts := mtest.NewOptions().CollectionOptions(wcCollOpts).Topologies(mtest.ReplicaSet)\n\t\tmt.RunOpts(\"write concern error\", wcTestOpts, func(mt *mtest.T) {\n\t\t\t_, err := mt.Coll.InsertMany(context.Background(), []any{bson.D{{\"_id\", 1}}})\n\t\t\twe, ok := err.(mongo.BulkWriteException)\n\t\t\tassert.True(mt, ok, \"expected error type %v, got %v\", mongo.BulkWriteException{}, err)\n\t\t\tassert.NotNil(mt, we.WriteConcernError, \"expected write concern error, got %+v\", err)\n\t\t})\n\t})\n\tmt.RunOpts(\"delete one\", noClientOpts, func(mt *mtest.T) {\n\t\tmt.Run(\"found\", func(mt *mtest.T) {\n\t\t\tinitCollection(mt, mt.Coll)\n\t\t\tres, err := mt.Coll.DeleteOne(context.Background(), bson.D{{\"x\", 1}})\n\t\t\trequire.NoError(mt, err, \"DeleteOne error: %v\", err)\n\t\t\tassert.Equal(mt, int64(1), res.DeletedCount, \"expected DeletedCount 1, got %v\", res.DeletedCount)\n\t\t})\n\t\tmt.Run(\"not found\", func(mt *mtest.T) {\n\t\t\tinitCollection(mt, mt.Coll)\n\t\t\tres, err := mt.Coll.DeleteOne(context.Background(), bson.D{{\"x\", 0}})\n\t\t\trequire.NoError(mt, err, \"DeleteOne error: %v\", err)\n\t\t\tassert.Equal(mt, int64(0), res.DeletedCount, \"expected DeletedCount 0, got %v\", res.DeletedCount)\n\t\t})\n\t\tmt.RunOpts(\"not found with options\", mtest.NewOptions().MinServerVersion(\"3.4\"), func(mt *mtest.T) {\n\t\t\tinitCollection(mt, mt.Coll)\n\t\t\topts := options.DeleteOne().SetCollation(&options.Collation{Locale: \"en_US\"})\n\t\t\tres, err := mt.Coll.DeleteOne(context.Background(), bson.D{{\"x\", 0}}, opts)\n\t\t\trequire.NoError(mt, err, \"DeleteOne error: %v\", err)\n\t\t\tassert.Equal(mt, int64(0), res.DeletedCount, \"expected DeletedCount 0, got %v\", res.DeletedCount)\n\t\t})\n\t\tmt.RunOpts(\"write error\", mtest.NewOptions().MaxServerVersion(\"5.0.7\"), func(mt *mtest.T) {\n\t\t\t// Deletes are not allowed on capped collections on MongoDB 5.0.6-. We use this\n\t\t\t// behavior to test the processing of write errors.\n\t\t\tcappedOpts := options.CreateCollection().SetCapped(true).SetSizeInBytes(64 * 1024)\n\t\t\tcapped := mt.CreateCollection(mtest.Collection{\n\t\t\t\tName:       \"deleteOne_capped\",\n\t\t\t\tCreateOpts: cappedOpts,\n\t\t\t}, true)\n\t\t\t_, err := capped.DeleteOne(context.Background(), bson.D{{\"x\", 1}})\n\n\t\t\twe, ok := err.(mongo.WriteException)\n\t\t\tassert.True(mt, ok, \"expected error type %T, got %T\", mongo.WriteException{}, err)\n\t\t\tnumWriteErrors := len(we.WriteErrors)\n\t\t\tassert.Equal(mt, 1, numWriteErrors, \"expected 1 write error, got %v\", numWriteErrors)\n\t\t\tgotCode := we.WriteErrors[0].Code\n\t\t\tassert.True(mt, gotCode == errorCappedCollDeleteLegacy || gotCode == errorCappedCollDelete,\n\t\t\t\t\"expected error code %v or %v, got %v\", errorCappedCollDeleteLegacy, errorCappedCollDelete, gotCode)\n\t\t})\n\t\tmt.RunOpts(\"write concern error\", mtest.NewOptions().Topologies(mtest.ReplicaSet), func(mt *mtest.T) {\n\t\t\t// 2.6 returns right away if the document doesn't exist\n\t\t\tfilter := bson.D{{\"x\", 1}}\n\t\t\t_, err := mt.Coll.InsertOne(context.Background(), filter)\n\t\t\trequire.NoError(mt, err, \"InsertOne error: %v\", err)\n\n\t\t\tmt.CloneCollection(options.Collection().SetWriteConcern(impossibleWc))\n\t\t\t_, err = mt.Coll.DeleteOne(context.Background(), filter)\n\t\t\twe, ok := err.(mongo.WriteException)\n\t\t\tassert.True(mt, ok, \"expected error type %T, got %T\", mongo.WriteException{}, err)\n\t\t\tassert.NotNil(mt, we.WriteConcernError, \"expected write concern error, got nil\")\n\t\t})\n\t\tmt.RunOpts(\"single key map index\", mtest.NewOptions().MinServerVersion(\"4.4\"), func(mt *mtest.T) {\n\t\t\tinitCollection(mt, mt.Coll)\n\t\t\tindexView := mt.Coll.Indexes()\n\t\t\t_, err := indexView.CreateOne(context.Background(), mongo.IndexModel{\n\t\t\t\tKeys: bson.D{{\"x\", 1}},\n\t\t\t})\n\t\t\trequire.NoError(mt, err, \"CreateOne error: %v\", err)\n\n\t\t\topts := options.DeleteOne().SetHint(bson.M{\"x\": 1})\n\t\t\tres, err := mt.Coll.DeleteOne(context.Background(), bson.D{{\"x\", 1}}, opts)\n\t\t\trequire.NoError(mt, err, \"DeleteOne error: %v\", err)\n\t\t\tassert.Equal(mt, int64(1), res.DeletedCount, \"expected DeletedCount 1, got %v\", res.DeletedCount)\n\t\t})\n\t\tmt.RunOpts(\"multikey map index\", mtest.NewOptions().MinServerVersion(\"4.4\"), func(mt *mtest.T) {\n\t\t\topts := options.DeleteOne().SetHint(bson.M{\"x\": 1, \"y\": 1})\n\t\t\t_, err := mt.Coll.DeleteOne(context.Background(), bson.D{{\"x\", 0}}, opts)\n\t\t\tassert.Equal(mt, mongo.ErrMapForOrderedArgument{\"hint\"}, err, \"expected error %v, got %v\", mongo.ErrMapForOrderedArgument{\"hint\"}, err)\n\t\t})\n\t})\n\tmt.RunOpts(\"delete many\", noClientOpts, func(mt *mtest.T) {\n\t\tmt.Run(\"found\", func(mt *mtest.T) {\n\t\t\tinitCollection(mt, mt.Coll)\n\t\t\tres, err := mt.Coll.DeleteMany(context.Background(), bson.D{{\"x\", bson.D{{\"$gte\", 3}}}})\n\t\t\trequire.NoError(mt, err, \"DeleteMany error: %v\", err)\n\t\t\tassert.Equal(mt, int64(3), res.DeletedCount, \"expected DeletedCount 3, got %v\", res.DeletedCount)\n\t\t})\n\t\tmt.Run(\"not found\", func(mt *mtest.T) {\n\t\t\tinitCollection(mt, mt.Coll)\n\t\t\tres, err := mt.Coll.DeleteMany(context.Background(), bson.D{{\"x\", bson.D{{\"$lt\", 1}}}})\n\t\t\trequire.NoError(mt, err, \"DeleteMany error: %v\", err)\n\t\t\tassert.Equal(mt, int64(0), res.DeletedCount, \"expected DeletedCount 0, got %v\", res.DeletedCount)\n\t\t})\n\t\tmt.RunOpts(\"not found with options\", mtest.NewOptions().MinServerVersion(\"3.4\"), func(mt *mtest.T) {\n\t\t\tinitCollection(mt, mt.Coll)\n\t\t\topts := options.DeleteMany().SetCollation(&options.Collation{Locale: \"en_US\"})\n\t\t\tres, err := mt.Coll.DeleteMany(context.Background(), bson.D{{\"x\", bson.D{{\"$lt\", 1}}}}, opts)\n\t\t\trequire.NoError(mt, err, \"DeleteMany error: %v\", err)\n\t\t\tassert.Equal(mt, int64(0), res.DeletedCount, \"expected DeletedCount 0, got %v\", res.DeletedCount)\n\t\t})\n\t\tmt.RunOpts(\"write error\", mtest.NewOptions().MaxServerVersion(\"5.0.7\"), func(mt *mtest.T) {\n\t\t\t// Deletes are not allowed on capped collections on MongoDB 5.0.6-. We use this\n\t\t\t// behavior to test the processing of write errors.\n\t\t\tcappedOpts := options.CreateCollection().SetCapped(true).SetSizeInBytes(64 * 1024)\n\t\t\tcapped := mt.CreateCollection(mtest.Collection{\n\t\t\t\tName:       \"deleteMany_capped\",\n\t\t\t\tCreateOpts: cappedOpts,\n\t\t\t}, true)\n\t\t\t_, err := capped.DeleteMany(context.Background(), bson.D{{\"x\", 1}})\n\n\t\t\twe, ok := err.(mongo.WriteException)\n\t\t\tassert.True(mt, ok, \"expected error type %v, got %v\", mongo.WriteException{}, err)\n\t\t\tnumWriteErrors := len(we.WriteErrors)\n\t\t\tassert.Equal(mt, 1, len(we.WriteErrors), \"expected 1 write error, got %v\", numWriteErrors)\n\t\t\tgotCode := we.WriteErrors[0].Code\n\t\t\tassert.True(mt, gotCode == errorCappedCollDeleteLegacy || gotCode == errorCappedCollDelete,\n\t\t\t\t\"expected error code %v or %v, got %v\", errorCappedCollDeleteLegacy, errorCappedCollDelete, gotCode)\n\t\t})\n\t\tmt.RunOpts(\"write concern error\", mtest.NewOptions().Topologies(mtest.ReplicaSet), func(mt *mtest.T) {\n\t\t\t// 2.6 server returns right away if the document doesn't exist\n\t\t\tfilter := bson.D{{\"x\", 1}}\n\t\t\t_, err := mt.Coll.InsertOne(context.Background(), filter)\n\t\t\trequire.NoError(mt, err, \"InsertOne error: %v\", err)\n\n\t\t\tmt.CloneCollection(options.Collection().SetWriteConcern(impossibleWc))\n\t\t\t_, err = mt.Coll.DeleteMany(context.Background(), filter)\n\t\t\twe, ok := err.(mongo.WriteException)\n\t\t\tassert.True(mt, ok, \"expected error type %v, got %v\", mongo.WriteException{}, err)\n\t\t\tassert.NotNil(mt, we.WriteConcernError, \"expected write concern error, got %+v\", err)\n\t\t})\n\t\tmt.RunOpts(\"single key map index\", mtest.NewOptions().MinServerVersion(\"4.4\"), func(mt *mtest.T) {\n\t\t\tinitCollection(mt, mt.Coll)\n\t\t\tindexView := mt.Coll.Indexes()\n\t\t\t_, err := indexView.CreateOne(context.Background(), mongo.IndexModel{\n\t\t\t\tKeys: bson.D{{\"x\", 1}},\n\t\t\t})\n\t\t\trequire.NoError(mt, err, \"index CreateOne error: %v\", err)\n\n\t\t\topts := options.DeleteOne().SetHint(bson.M{\"x\": 1})\n\t\t\tres, err := mt.Coll.DeleteOne(context.Background(), bson.D{{\"x\", 1}}, opts)\n\t\t\trequire.NoError(mt, err, \"DeleteOne error: %v\", err)\n\t\t\tassert.Equal(mt, int64(1), res.DeletedCount, \"expected DeletedCount 1, got %v\", res.DeletedCount)\n\t\t})\n\t\tmt.RunOpts(\"multikey map index\", mtest.NewOptions().MinServerVersion(\"4.4\"), func(mt *mtest.T) {\n\t\t\topts := options.DeleteMany().SetHint(bson.M{\"x\": 1, \"y\": 1})\n\t\t\t_, err := mt.Coll.DeleteMany(context.Background(), bson.D{{\"x\", 0}}, opts)\n\t\t\tassert.Equal(mt, mongo.ErrMapForOrderedArgument{\"hint\"}, err, \"expected error %v, got %v\", mongo.ErrMapForOrderedArgument{\"hint\"}, err)\n\t\t})\n\t})\n\tmt.RunOpts(\"update one\", noClientOpts, func(mt *mtest.T) {\n\t\tmt.Run(\"empty update\", func(mt *mtest.T) {\n\t\t\t_, err := mt.Coll.UpdateOne(context.Background(), bson.D{}, bson.D{})\n\t\t\tassert.NotNil(mt, err, \"expected error, got nil\")\n\t\t})\n\t\tmt.Run(\"found\", func(mt *mtest.T) {\n\t\t\tinitCollection(mt, mt.Coll)\n\t\t\tfilter := bson.D{{\"x\", 1}}\n\t\t\tupdate := bson.D{{\"$inc\", bson.D{{\"x\", 1}}}}\n\n\t\t\tres, err := mt.Coll.UpdateOne(context.Background(), filter, update)\n\t\t\trequire.NoError(mt, err, \"UpdateOne error: %v\", err)\n\t\t\tassert.Equal(mt, int64(1), res.MatchedCount, \"expected matched count 1, got %v\", res.MatchedCount)\n\t\t\tassert.Equal(mt, int64(1), res.ModifiedCount, \"expected matched count 1, got %v\", res.ModifiedCount)\n\t\t\tassert.Nil(mt, res.UpsertedID, \"expected upserted ID nil, got %v\", res.UpsertedID)\n\t\t})\n\t\tmt.Run(\"not found\", func(mt *mtest.T) {\n\t\t\tinitCollection(mt, mt.Coll)\n\t\t\tfilter := bson.D{{\"x\", 0}}\n\t\t\tupdate := bson.D{{\"$inc\", bson.D{{\"x\", 1}}}}\n\n\t\t\tres, err := mt.Coll.UpdateOne(context.Background(), filter, update)\n\t\t\trequire.NoError(mt, err, \"UpdateOne error: %v\", err)\n\t\t\tassert.Equal(mt, int64(0), res.MatchedCount, \"expected matched count 0, got %v\", res.MatchedCount)\n\t\t\tassert.Equal(mt, int64(0), res.ModifiedCount, \"expected matched count 0, got %v\", res.ModifiedCount)\n\t\t\tassert.Nil(mt, res.UpsertedID, \"expected upserted ID nil, got %v\", res.UpsertedID)\n\t\t})\n\t\tmt.Run(\"upsert\", func(mt *mtest.T) {\n\t\t\tinitCollection(mt, mt.Coll)\n\t\t\tfilter := bson.D{{\"x\", 0}}\n\t\t\tupdate := bson.D{{\"$inc\", bson.D{{\"x\", 1}}}}\n\n\t\t\tres, err := mt.Coll.UpdateOne(context.Background(), filter, update, options.UpdateOne().SetUpsert(true))\n\t\t\trequire.NoError(mt, err, \"UpdateOne error: %v\", err)\n\t\t\tassert.Equal(mt, int64(0), res.MatchedCount, \"expected matched count 0, got %v\", res.MatchedCount)\n\t\t\tassert.Equal(mt, int64(0), res.ModifiedCount, \"expected matched count 0, got %v\", res.ModifiedCount)\n\t\t\tassert.NotNil(mt, res.UpsertedID, \"expected upserted ID, got nil\")\n\t\t})\n\t\tmt.Run(\"write error\", func(mt *mtest.T) {\n\t\t\tfilter := bson.D{{\"_id\", \"foo\"}}\n\t\t\tupdate := bson.D{{\"$set\", bson.D{{\"_id\", 3.14159}}}}\n\t\t\t_, err := mt.Coll.InsertOne(context.Background(), filter)\n\t\t\trequire.NoError(mt, err, \"InsertOne error: %v\", err)\n\n\t\t\t_, err = mt.Coll.UpdateOne(context.Background(), filter, update)\n\t\t\twe, ok := err.(mongo.WriteException)\n\t\t\tassert.True(mt, ok, \"expected error type %v, got %v\", mongo.WriteException{}, err)\n\t\t\tnumWriteErrors := len(we.WriteErrors)\n\t\t\tassert.Equal(mt, 1, numWriteErrors, \"expected 1 write error, got %v\", numWriteErrors)\n\t\t\tgotCode := we.WriteErrors[0].Code\n\t\t\tassert.Equal(mt, errorModifiedID, gotCode, \"expected error code %v, got %v\", errorModifiedID, gotCode)\n\t\t})\n\t\tmt.RunOpts(\"write concern error\", mtest.NewOptions().Topologies(mtest.ReplicaSet), func(mt *mtest.T) {\n\t\t\t// 2.6 returns right away if the document doesn't exist\n\t\t\tfilter := bson.D{{\"_id\", \"foo\"}}\n\t\t\tupdate := bson.D{{\"$set\", bson.D{{\"pi\", 3.14159}}}}\n\t\t\t_, err := mt.Coll.InsertOne(context.Background(), filter)\n\t\t\trequire.NoError(mt, err, \"InsertOne error: %v\", err)\n\n\t\t\tmt.CloneCollection(options.Collection().SetWriteConcern(impossibleWc))\n\t\t\t_, err = mt.Coll.UpdateOne(context.Background(), filter, update)\n\t\t\twe, ok := err.(mongo.WriteException)\n\t\t\tassert.True(mt, ok, \"expected error type %v, got %v\", mongo.WriteException{}, err)\n\t\t\tassert.NotNil(mt, we.WriteConcernError, \"expected write concern error, got %+v\", err)\n\t\t})\n\t\tmt.RunOpts(\"special slice types\", noClientOpts, func(mt *mtest.T) {\n\t\t\t// test special types that should be converted to a document for updates even though the underlying type is\n\t\t\t// a slice/array\n\t\t\tdoc := bson.D{{\"$set\", bson.D{{\"x\", 2}}}}\n\t\t\tdocBytes, err := bson.Marshal(doc)\n\t\t\trequire.NoError(mt, err, \"Marshal error: %v\", err)\n\n\t\t\ttestCases := []struct {\n\t\t\t\tname   string\n\t\t\t\tupdate any\n\t\t\t}{\n\t\t\t\t{\"bson Document\", bsoncore.Document(docBytes)},\n\t\t\t\t{\"bson Raw\", bson.Raw(docBytes)},\n\t\t\t\t{\"bson D\", doc},\n\t\t\t\t{\"byte slice\", docBytes},\n\t\t\t}\n\t\t\tfor _, tc := range testCases {\n\t\t\t\tmt.Run(tc.name, func(mt *mtest.T) {\n\t\t\t\t\tfilter := bson.D{{\"x\", 1}}\n\t\t\t\t\t_, err := mt.Coll.InsertOne(context.Background(), filter)\n\t\t\t\t\trequire.NoError(mt, err, \"InsertOne error: %v\", err)\n\n\t\t\t\t\tres, err := mt.Coll.UpdateOne(context.Background(), filter, tc.update)\n\t\t\t\t\trequire.NoError(mt, err, \"UpdateOne error: %v\", err)\n\t\t\t\t\tassert.Equal(mt, int64(1), res.MatchedCount, \"expected matched count 1, got %v\", res.MatchedCount)\n\t\t\t\t\tassert.Equal(mt, int64(1), res.ModifiedCount, \"expected modified count 1, got %v\", res.ModifiedCount)\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\t})\n\tmt.RunOpts(\"update by id\", noClientOpts, func(mt *mtest.T) {\n\t\tmt.Run(\"empty update\", func(mt *mtest.T) {\n\t\t\t_, err := mt.Coll.UpdateByID(context.Background(), \"foo\", bson.D{})\n\t\t\tassert.NotNil(mt, err, \"expected error, got nil\")\n\t\t})\n\t\tmt.Run(\"nil id\", func(mt *mtest.T) {\n\t\t\t_, err := mt.Coll.UpdateByID(context.Background(), nil, bson.D{{\"$inc\", bson.D{{\"x\", 1}}}})\n\t\t\tassert.True(mt, errors.Is(err, mongo.ErrNilValue), \"expected %v, got %v\", mongo.ErrNilValue, err)\n\t\t})\n\t\tmt.RunOpts(\"found\", noClientOpts, func(mt *mtest.T) {\n\t\t\ttestCases := []struct {\n\t\t\t\tname string\n\t\t\t\tid   any\n\t\t\t}{\n\t\t\t\t{\"objectID\", bson.NewObjectID()},\n\t\t\t\t{\"string\", \"foo\"},\n\t\t\t\t{\"int\", 11},\n\t\t\t}\n\t\t\tfor _, tc := range testCases {\n\t\t\t\tmt.Run(tc.name, func(mt *mtest.T) {\n\t\t\t\t\tdoc := bson.D{{\"_id\", tc.id}, {\"x\", 1}}\n\t\t\t\t\t_, err := mt.Coll.InsertOne(context.Background(), doc)\n\t\t\t\t\trequire.NoError(mt, err, \"InsertOne error: %v\", err)\n\n\t\t\t\t\tupdate := bson.D{{\"$inc\", bson.D{{\"x\", 1}}}}\n\n\t\t\t\t\tres, err := mt.Coll.UpdateByID(context.Background(), tc.id, update)\n\t\t\t\t\trequire.NoError(mt, err, \"UpdateByID error: %v\", err)\n\t\t\t\t\tassert.Equal(mt, int64(1), res.MatchedCount, \"expected matched count 1, got %v\", res.MatchedCount)\n\t\t\t\t\tassert.Equal(mt, int64(1), res.ModifiedCount, \"expected modified count 1, got %v\", res.ModifiedCount)\n\t\t\t\t\tassert.Nil(mt, res.UpsertedID, \"expected upserted ID nil, got %v\", res.UpsertedID)\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\t\tmt.Run(\"not found\", func(mt *mtest.T) {\n\t\t\tid := bson.NewObjectID()\n\t\t\tdoc := bson.D{{\"_id\", id}, {\"x\", 1}}\n\t\t\t_, err := mt.Coll.InsertOne(context.Background(), doc)\n\t\t\trequire.NoError(mt, err, \"InsertOne error: %v\", err)\n\n\t\t\tupdate := bson.D{{\"$inc\", bson.D{{\"x\", 1}}}}\n\n\t\t\tres, err := mt.Coll.UpdateByID(context.Background(), 0, update)\n\t\t\trequire.NoError(mt, err, \"UpdateByID error: %v\", err)\n\t\t\tassert.Equal(mt, int64(0), res.MatchedCount, \"expected matched count 0, got %v\", res.MatchedCount)\n\t\t\tassert.Equal(mt, int64(0), res.ModifiedCount, \"expected modified count 0, got %v\", res.ModifiedCount)\n\t\t\tassert.Nil(mt, res.UpsertedID, \"expected upserted ID nil, got %v\", res.UpsertedID)\n\t\t})\n\t\tmt.Run(\"upsert\", func(mt *mtest.T) {\n\t\t\tdoc := bson.D{{\"_id\", bson.NewObjectID()}, {\"x\", 1}}\n\t\t\t_, err := mt.Coll.InsertOne(context.Background(), doc)\n\t\t\trequire.NoError(mt, err, \"InsertOne error: %v\", err)\n\n\t\t\tupdate := bson.D{{\"$inc\", bson.D{{\"x\", 1}}}}\n\n\t\t\tid := \"blah\"\n\t\t\tres, err := mt.Coll.UpdateByID(context.Background(), id, update, options.UpdateOne().SetUpsert(true))\n\t\t\trequire.NoError(mt, err, \"UpdateByID error: %v\", err)\n\t\t\tassert.Equal(mt, int64(0), res.MatchedCount, \"expected matched count 0, got %v\", res.MatchedCount)\n\t\t\tassert.Equal(mt, int64(0), res.ModifiedCount, \"expected modified count 0, got %v\", res.ModifiedCount)\n\t\t\tassert.Equal(mt, res.UpsertedID, id, \"expected upserted ID %v, got %v\", id, res.UpsertedID)\n\t\t})\n\t\tmt.Run(\"write error\", func(mt *mtest.T) {\n\t\t\tid := \"foo\"\n\t\t\tupdate := bson.D{{\"$set\", bson.D{{\"_id\", 3.14159}}}}\n\t\t\t_, err := mt.Coll.InsertOne(context.Background(), bson.D{{\"_id\", id}})\n\t\t\trequire.NoError(mt, err, \"InsertOne error: %v\", err)\n\n\t\t\t_, err = mt.Coll.UpdateByID(context.Background(), id, update)\n\t\t\tse, ok := err.(mongo.ServerError)\n\t\t\tassert.True(mt, ok, \"expected ServerError, got %v\", err)\n\t\t\tassert.True(mt, se.HasErrorCode(errorModifiedID), \"expected error code %v, got %v\", errorModifiedID, err)\n\t\t})\n\t\tmt.RunOpts(\"write concern error\", mtest.NewOptions().Topologies(mtest.ReplicaSet), func(mt *mtest.T) {\n\t\t\t// 2.6 returns right away if the document doesn't exist\n\t\t\tid := \"foo\"\n\t\t\tupdate := bson.D{{\"$set\", bson.D{{\"pi\", 3.14159}}}}\n\t\t\t_, err := mt.Coll.InsertOne(context.Background(), bson.D{{\"_id\", id}})\n\t\t\trequire.NoError(mt, err, \"InsertOne error: %v\", err)\n\n\t\t\tmt.CloneCollection(options.Collection().SetWriteConcern(impossibleWc))\n\t\t\t_, err = mt.Coll.UpdateByID(context.Background(), id, update)\n\t\t\twe, ok := err.(mongo.WriteException)\n\t\t\tassert.True(mt, ok, \"expected error type %v, got %v\", mongo.WriteException{}, err)\n\t\t\tassert.NotNil(mt, we.WriteConcernError, \"expected write concern error, got %+v\", we)\n\t\t})\n\t})\n\tmt.RunOpts(\"update many\", noClientOpts, func(mt *mtest.T) {\n\t\tmt.Run(\"empty update\", func(mt *mtest.T) {\n\t\t\t_, err := mt.Coll.UpdateMany(context.Background(), bson.D{}, bson.D{})\n\t\t\tassert.NotNil(mt, err, \"expected error, got nil\")\n\t\t})\n\t\tmt.Run(\"found\", func(mt *mtest.T) {\n\t\t\tinitCollection(mt, mt.Coll)\n\t\t\tfilter := bson.D{{\"x\", bson.D{{\"$gte\", 3}}}}\n\t\t\tupdate := bson.D{{\"$inc\", bson.D{{\"x\", 1}}}}\n\n\t\t\tres, err := mt.Coll.UpdateMany(context.Background(), filter, update)\n\t\t\trequire.NoError(mt, err, \"UpdateMany error: %v\", err)\n\t\t\tassert.Equal(mt, int64(3), res.MatchedCount, \"expected matched count 3, got %v\", res.MatchedCount)\n\t\t\tassert.Equal(mt, int64(3), res.ModifiedCount, \"expected modified count 3, got %v\", res.ModifiedCount)\n\t\t\tassert.Nil(mt, res.UpsertedID, \"expected upserted ID nil, got %v\", res.UpsertedID)\n\t\t})\n\t\tmt.Run(\"not found\", func(mt *mtest.T) {\n\t\t\tinitCollection(mt, mt.Coll)\n\t\t\tfilter := bson.D{{\"x\", bson.D{{\"$lt\", 1}}}}\n\t\t\tupdate := bson.D{{\"$inc\", bson.D{{\"x\", 1}}}}\n\n\t\t\tres, err := mt.Coll.UpdateMany(context.Background(), filter, update)\n\t\t\trequire.NoError(mt, err, \"UpdateMany error: %v\", err)\n\t\t\tassert.Equal(mt, int64(0), res.MatchedCount, \"expected matched count 0, got %v\", res.MatchedCount)\n\t\t\tassert.Equal(mt, int64(0), res.ModifiedCount, \"expected modified count 0, got %v\", res.ModifiedCount)\n\t\t\tassert.Nil(mt, res.UpsertedID, \"expected upserted ID nil, got %v\", res.UpsertedID)\n\t\t})\n\t\tmt.Run(\"upsert\", func(mt *mtest.T) {\n\t\t\tinitCollection(mt, mt.Coll)\n\t\t\tfilter := bson.D{{\"x\", bson.D{{\"$lt\", 1}}}}\n\t\t\tupdate := bson.D{{\"$inc\", bson.D{{\"x\", 1}}}}\n\n\t\t\tres, err := mt.Coll.UpdateMany(context.Background(), filter, update, options.UpdateMany().SetUpsert(true))\n\t\t\trequire.NoError(mt, err, \"UpdateMany error: %v\", err)\n\t\t\tassert.Equal(mt, int64(0), res.MatchedCount, \"expected matched count 0, got %v\", res.MatchedCount)\n\t\t\tassert.Equal(mt, int64(0), res.ModifiedCount, \"expected modified count 0, got %v\", res.ModifiedCount)\n\t\t\tassert.NotNil(mt, res.UpsertedID, \"expected upserted ID, got nil\")\n\t\t})\n\t\tmt.Run(\"write error\", func(mt *mtest.T) {\n\t\t\tfilter := bson.D{{\"_id\", \"foo\"}}\n\t\t\tupdate := bson.D{{\"$set\", bson.D{{\"_id\", 3.14159}}}}\n\t\t\t_, err := mt.Coll.InsertOne(context.Background(), filter)\n\t\t\trequire.NoError(mt, err, \"InsertOne error: %v\", err)\n\n\t\t\t_, err = mt.Coll.UpdateMany(context.Background(), filter, update)\n\t\t\twe, ok := err.(mongo.WriteException)\n\t\t\tassert.True(mt, ok, \"expected error type %v, got %v\", mongo.WriteException{}, err)\n\t\t\tnumWriteErrors := len(we.WriteErrors)\n\t\t\tassert.Equal(mt, 1, numWriteErrors, \"expected 1 write error, got %v\", numWriteErrors)\n\t\t\tgotCode := we.WriteErrors[0].Code\n\t\t\tassert.Equal(mt, errorModifiedID, gotCode, \"expected error code %v, got %v\", errorModifiedID, gotCode)\n\t\t})\n\t\tmt.RunOpts(\"write concern error\", mtest.NewOptions().Topologies(mtest.ReplicaSet), func(mt *mtest.T) {\n\t\t\t// 2.6 returns right away if the document doesn't exist\n\t\t\tfilter := bson.D{{\"_id\", \"foo\"}}\n\t\t\tupdate := bson.D{{\"$set\", bson.D{{\"pi\", 3.14159}}}}\n\t\t\t_, err := mt.Coll.InsertOne(context.Background(), filter)\n\t\t\trequire.NoError(mt, err, \"InsertOne error: %v\", err)\n\n\t\t\tmt.CloneCollection(options.Collection().SetWriteConcern(impossibleWc))\n\t\t\t_, err = mt.Coll.UpdateMany(context.Background(), filter, update)\n\t\t\twe, ok := err.(mongo.WriteException)\n\t\t\tassert.True(mt, ok, \"expected error type %v, got %v\", mongo.WriteException{}, err)\n\t\t\tassert.NotNil(mt, we.WriteConcernError, \"expected write concern error, got %+v\", we)\n\t\t})\n\t})\n\tmt.RunOpts(\"replace one\", noClientOpts, func(mt *mtest.T) {\n\t\tmt.Run(\"found\", func(mt *mtest.T) {\n\t\t\tinitCollection(mt, mt.Coll)\n\t\t\tfilter := bson.D{{\"x\", 1}}\n\t\t\treplacement := bson.D{{\"y\", 1}}\n\n\t\t\tres, err := mt.Coll.ReplaceOne(context.Background(), filter, replacement)\n\t\t\trequire.NoError(mt, err, \"ReplaceOne error: %v\", err)\n\t\t\tassert.Equal(mt, int64(1), res.MatchedCount, \"expected matched count 1, got %v\", res.MatchedCount)\n\t\t\tassert.Equal(mt, int64(1), res.ModifiedCount, \"expected modified count 1, got %v\", res.ModifiedCount)\n\t\t\tassert.Nil(mt, res.UpsertedID, \"expected upserted ID nil, got %v\", res.UpsertedID)\n\t\t})\n\t\tmt.Run(\"not found\", func(mt *mtest.T) {\n\t\t\tinitCollection(mt, mt.Coll)\n\t\t\tfilter := bson.D{{\"x\", 0}}\n\t\t\treplacement := bson.D{{\"y\", 1}}\n\n\t\t\tres, err := mt.Coll.ReplaceOne(context.Background(), filter, replacement)\n\t\t\trequire.NoError(mt, err, \"ReplaceOne error: %v\", err)\n\t\t\tassert.Equal(mt, int64(0), res.MatchedCount, \"expected matched count 0, got %v\", res.MatchedCount)\n\t\t\tassert.Equal(mt, int64(0), res.ModifiedCount, \"expected modified count 0, got %v\", res.ModifiedCount)\n\t\t\tassert.Nil(mt, res.UpsertedID, \"expected upserted ID nil, got %v\", res.UpsertedID)\n\t\t})\n\t\tmt.Run(\"upsert\", func(mt *mtest.T) {\n\t\t\tinitCollection(mt, mt.Coll)\n\t\t\tfilter := bson.D{{\"x\", 0}}\n\t\t\treplacement := bson.D{{\"y\", 1}}\n\n\t\t\tres, err := mt.Coll.ReplaceOne(context.Background(), filter, replacement, options.Replace().SetUpsert(true))\n\t\t\trequire.NoError(mt, err, \"ReplaceOne error: %v\", err)\n\t\t\tassert.Equal(mt, int64(0), res.MatchedCount, \"expected matched count 0, got %v\", res.MatchedCount)\n\t\t\tassert.Equal(mt, int64(0), res.ModifiedCount, \"expected modified count 0, got %v\", res.ModifiedCount)\n\t\t\tassert.NotNil(mt, res.UpsertedID, \"expected upserted ID, got nil\")\n\t\t})\n\t\tmt.Run(\"write error\", func(mt *mtest.T) {\n\t\t\tfilter := bson.D{{\"_id\", \"foo\"}}\n\t\t\treplacement := bson.D{{\"_id\", 3.14159}}\n\t\t\t_, err := mt.Coll.InsertOne(context.Background(), filter)\n\t\t\trequire.NoError(mt, err, \"InsertOne error: %v\", err)\n\n\t\t\t_, err = mt.Coll.ReplaceOne(context.Background(), filter, replacement)\n\t\t\twe, ok := err.(mongo.WriteException)\n\t\t\tassert.True(mt, ok, \"expected error type %v, got %v\", mongo.WriteException{}, err)\n\t\t\tnumWriteErrors := len(we.WriteErrors)\n\t\t\tassert.Equal(mt, 1, numWriteErrors, \"expected 1 write error, got %v\", numWriteErrors)\n\t\t\tgotCode := we.WriteErrors[0].Code\n\t\t\tassert.True(mt, gotCode == errorModifiedID || gotCode == errorModifiedIDLegacy,\n\t\t\t\t\"expected error code %v or %v, got %v\", errorModifiedID, errorModifiedIDLegacy, gotCode)\n\t\t})\n\t\tmt.RunOpts(\"write concern error\", mtest.NewOptions().Topologies(mtest.ReplicaSet), func(mt *mtest.T) {\n\t\t\t// 2.6 returns right away if document doesn't exist\n\t\t\tfilter := bson.D{{\"_id\", \"foo\"}}\n\t\t\treplacement := bson.D{{\"pi\", 3.14159}}\n\t\t\t_, err := mt.Coll.InsertOne(context.Background(), filter)\n\t\t\trequire.NoError(mt, err, \"InsertOne error: %v\", err)\n\n\t\t\tmt.CloneCollection(options.Collection().SetWriteConcern(impossibleWc))\n\t\t\t_, err = mt.Coll.ReplaceOne(context.Background(), filter, replacement)\n\t\t\twe, ok := err.(mongo.WriteException)\n\t\t\tassert.True(mt, ok, \"expected error type %v, got %v\", mongo.WriteException{}, err)\n\t\t\tassert.NotNil(mt, we.WriteConcernError, \"expected write concern error, got nil\")\n\t\t})\n\t})\n\tmt.RunOpts(\"aggregate\", noClientOpts, func(mt *mtest.T) {\n\t\tmt.Run(\"success\", func(mt *mtest.T) {\n\t\t\tinitCollection(mt, mt.Coll)\n\t\t\tpipeline := bson.A{\n\t\t\t\tbson.D{{\"$match\", bson.D{{\"x\", bson.D{{\"$gte\", 2}}}}}},\n\t\t\t\tbson.D{{\"$project\", bson.D{\n\t\t\t\t\t{\"_id\", 0},\n\t\t\t\t\t{\"x\", 1},\n\t\t\t\t}}},\n\t\t\t\tbson.D{{\"$sort\", bson.D{{\"x\", 1}}}},\n\t\t\t}\n\t\t\tcursor, err := mt.Coll.Aggregate(context.Background(), pipeline)\n\t\t\trequire.NoError(mt, err, \"Aggregate error: %v\", err)\n\n\t\t\tfor i := 2; i < 5; i++ {\n\t\t\t\tassert.True(mt, cursor.Next(context.Background()), \"expected Next true, got false (i=%v)\", i)\n\t\t\t\telems, _ := cursor.Current.Elements()\n\t\t\t\tassert.Equal(mt, 1, len(elems), \"expected doc with 1 element, got %v\", cursor.Current)\n\n\t\t\t\tnum, err := cursor.Current.LookupErr(\"x\")\n\t\t\t\trequire.NoError(mt, err, \"x not found in document %v\", cursor.Current)\n\t\t\t\tassert.Equal(mt, bson.TypeInt32, num.Type, \"expected 'x' type %v, got %v\", bson.TypeInt32, num.Type)\n\t\t\t\tassert.Equal(mt, int32(i), num.Int32(), \"expected x value %v, got %v\", i, num.Int32())\n\t\t\t}\n\t\t})\n\t\tmt.RunOpts(\"index hint\", mtest.NewOptions().MinServerVersion(\"3.6\"), func(mt *mtest.T) {\n\t\t\thint := bson.D{{\"x\", 1}}\n\t\t\ttestAggregateWithOptions(mt, true, options.Aggregate().SetHint(hint))\n\t\t})\n\t\tmt.Run(\"options\", func(mt *mtest.T) {\n\t\t\ttestAggregateWithOptions(mt, false, options.Aggregate().SetAllowDiskUse(true))\n\t\t})\n\t\tmt.RunOpts(\"single key map hint\", mtest.NewOptions().MinServerVersion(\"3.6\"), func(mt *mtest.T) {\n\t\t\thint := bson.M{\"x\": 1}\n\t\t\ttestAggregateWithOptions(mt, true, options.Aggregate().SetHint(hint))\n\t\t})\n\t\tmt.RunOpts(\"multikey map hint\", mtest.NewOptions().MinServerVersion(\"3.6\"), func(mt *mtest.T) {\n\t\t\tpipeline := mongo.Pipeline{bson.D{{\"$out\", mt.Coll.Name()}}}\n\t\t\tcursor, err := mt.Coll.Aggregate(context.Background(), pipeline, options.Aggregate().SetHint(bson.M{\"x\": 1, \"y\": 1}))\n\t\t\tassert.Nil(mt, cursor, \"expected cursor nil, got %v\", cursor)\n\t\t\tassert.Equal(mt, mongo.ErrMapForOrderedArgument{\"hint\"}, err, \"expected error %v, got %v\", mongo.ErrMapForOrderedArgument{\"hint\"}, err)\n\t\t})\n\t\twcCollOpts := options.Collection().SetWriteConcern(impossibleWc)\n\t\twcTestOpts := mtest.NewOptions().Topologies(mtest.ReplicaSet).MinServerVersion(\"3.6\").CollectionOptions(wcCollOpts)\n\t\tmt.RunOpts(\"write concern error\", wcTestOpts, func(mt *mtest.T) {\n\t\t\tpipeline := mongo.Pipeline{{{\"$out\", mt.Coll.Name()}}}\n\t\t\tcursor, err := mt.Coll.Aggregate(context.Background(), pipeline)\n\t\t\tassert.Nil(mt, cursor, \"expected cursor nil, got %v\", cursor)\n\t\t\t_, ok := err.(mongo.WriteConcernError)\n\t\t\tassert.True(mt, ok, \"expected error type %v, got %v\", mongo.WriteConcernError{}, err)\n\t\t})\n\t\tmt.Run(\"getMore commands are monitored\", func(mt *mtest.T) {\n\t\t\tinitCollection(mt, mt.Coll)\n\t\t\tassertGetMoreCommandsAreMonitored(mt, \"aggregate\", func() (*mongo.Cursor, error) {\n\t\t\t\treturn mt.Coll.Aggregate(context.Background(), mongo.Pipeline{}, options.Aggregate().SetBatchSize(3))\n\t\t\t})\n\t\t})\n\t\tmt.Run(\"killCursors commands are monitored\", func(mt *mtest.T) {\n\t\t\tinitCollection(mt, mt.Coll)\n\t\t\tassertKillCursorsCommandsAreMonitored(mt, \"aggregate\", func() (*mongo.Cursor, error) {\n\t\t\t\treturn mt.Coll.Aggregate(context.Background(), mongo.Pipeline{}, options.Aggregate().SetBatchSize(3))\n\t\t\t})\n\t\t})\n\t\tmt.Run(\"Custom\", func(mt *mtest.T) {\n\t\t\t// Custom options should be a BSON map of option names to Marshalable option values.\n\t\t\t// We use \"allowDiskUse\" as an example.\n\t\t\tcustomOpts := bson.M{\"allowDiskUse\": true}\n\t\t\topts := options.Aggregate().SetCustom(customOpts)\n\n\t\t\t// Run aggregate with custom options set.\n\t\t\tmt.ClearEvents()\n\t\t\t_, err := mt.Coll.Aggregate(context.Background(), mongo.Pipeline{}, opts)\n\t\t\trequire.NoError(mt, err, \"Aggregate error: %v\", err)\n\n\t\t\t// Assert that custom option is passed to the aggregate expression.\n\t\t\tevt := mt.GetStartedEvent()\n\t\t\tassert.Equal(mt, \"aggregate\", evt.CommandName, \"expected command 'aggregate' got, %q\", evt.CommandName)\n\n\t\t\taduVal, err := evt.Command.LookupErr(\"allowDiskUse\")\n\t\t\trequire.NoError(mt, err, \"expected field 'allowDiskUse' in started command not found\")\n\t\t\tadu, ok := aduVal.BooleanOK()\n\t\t\tassert.True(mt, ok, \"expected field 'allowDiskUse' to be boolean, got %v\", aduVal.Type.String())\n\t\t\tassert.True(mt, adu, \"expected field 'allowDiskUse' to be true, got false\")\n\t\t})\n\t})\n\tmt.RunOpts(\"count documents\", noClientOpts, func(mt *mtest.T) {\n\t\tmt.Run(\"success\", func(mt *mtest.T) {\n\t\t\ttestCases := []struct {\n\t\t\t\tname     string\n\t\t\t\tfilter   bson.D\n\t\t\t\topts     *options.CountOptionsBuilder\n\t\t\t\tcount    int64\n\t\t\t\ttestOpts *mtest.Options\n\t\t\t}{\n\t\t\t\t{\"no filter\", bson.D{}, nil, 5, mtest.NewOptions()},\n\t\t\t\t{\"filter\", bson.D{{\"x\", bson.D{{\"$gt\", 2}}}}, nil, 3, mtest.NewOptions()},\n\t\t\t\t{\"limit\", bson.D{}, options.Count().SetLimit(3), 3, mtest.NewOptions()},\n\t\t\t\t{\"skip\", bson.D{}, options.Count().SetSkip(3), 2, mtest.NewOptions()},\n\t\t\t\t{\n\t\t\t\t\t\"single key map hint\",\n\t\t\t\t\tbson.D{},\n\t\t\t\t\toptions.Count().SetHint(bson.M{\"x\": 1}), 5,\n\t\t\t\t\tmtest.NewOptions().MinServerVersion(\"3.6\"),\n\t\t\t\t},\n\t\t\t}\n\t\t\tfor _, tc := range testCases {\n\t\t\t\tmt.RunOpts(tc.name, tc.testOpts, func(mt *mtest.T) {\n\t\t\t\t\tinitCollection(mt, mt.Coll)\n\t\t\t\t\tindexView := mt.Coll.Indexes()\n\t\t\t\t\t_, err := indexView.CreateOne(context.Background(), mongo.IndexModel{\n\t\t\t\t\t\tKeys: bson.D{{\"x\", 1}},\n\t\t\t\t\t})\n\t\t\t\t\trequire.NoError(mt, err, \"CreateOne error: %v\", err)\n\n\t\t\t\t\tcount, err := mt.Coll.CountDocuments(context.Background(), tc.filter, tc.opts)\n\t\t\t\t\trequire.NoError(mt, err, \"CountDocuments error: %v\", err)\n\t\t\t\t\tassert.Equal(mt, tc.count, count, \"expected count %v, got %v\", tc.count, count)\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\t\tmt.Run(\"multikey map hint\", func(mt *mtest.T) {\n\t\t\topts := options.Count().SetHint(bson.M{\"x\": 1, \"y\": 1})\n\t\t\t_, err := mt.Coll.CountDocuments(context.Background(), bson.D{}, opts)\n\t\t\tassert.Equal(mt, mongo.ErrMapForOrderedArgument{\"hint\"}, err, \"expected error %v, got %v\", mongo.ErrMapForOrderedArgument{\"hint\"}, err)\n\t\t})\n\t})\n\tmt.RunOpts(\"estimated document count\", noClientOpts, func(mt *mtest.T) {\n\t\ttestCases := []struct {\n\t\t\tname  string\n\t\t\topts  *options.EstimatedDocumentCountOptionsBuilder\n\t\t\tcount int64\n\t\t}{\n\t\t\t{\"no options\", nil, 5},\n\t\t\t{\"options\", options.EstimatedDocumentCount().SetComment(\"1\"), 5},\n\t\t}\n\t\tfor _, tc := range testCases {\n\t\t\tmt.Run(tc.name, func(mt *mtest.T) {\n\t\t\t\tinitCollection(mt, mt.Coll)\n\t\t\t\tcount, err := mt.Coll.EstimatedDocumentCount(context.Background(), tc.opts)\n\t\t\t\trequire.NoError(mt, err, \"EstimatedDocumentCount error: %v\", err)\n\t\t\t\tassert.Equal(mt, tc.count, count, \"expected count %v, got %v\", tc.count, count)\n\t\t\t})\n\t\t}\n\t})\n\tmt.RunOpts(\"distinct\", noClientOpts, func(mt *mtest.T) {\n\t\tall := []int32{1, 2, 3, 4, 5}\n\n\t\ttestCases := []struct {\n\t\t\tname   string\n\t\t\tfilter bson.D\n\t\t\topts   *options.DistinctOptionsBuilder\n\t\t\twant   []int32\n\t\t}{\n\t\t\t{\"no options\", bson.D{}, nil, all},\n\t\t\t{\"filter\", bson.D{{\"x\", bson.D{{\"$gt\", 2}}}}, nil, all[2:]},\n\t\t\t{\"options\", bson.D{}, options.Distinct().SetComment(\"1\"), all},\n\t\t}\n\t\tfor _, tc := range testCases {\n\t\t\tmt.Run(tc.name, func(mt *mtest.T) {\n\t\t\t\tinitCollection(mt, mt.Coll)\n\t\t\t\tres := mt.Coll.Distinct(context.Background(), \"x\", tc.filter, tc.opts)\n\t\t\t\tassert.Nil(mt, res.Err(), \"Distinct error: %v\", res.Err())\n\n\t\t\t\tvar got []int32\n\t\t\t\terr := res.Decode(&got)\n\t\t\t\tassert.NoError(t, err)\n\n\t\t\t\tassert.EqualValues(mt, tc.want, got, \"expected result %v, got %v\", tc.want, got)\n\t\t\t})\n\t\t}\n\n\t\tcollOpts := options.Collection().SetBSONOptions(&options.BSONOptions{AllowTruncatingDoubles: true})\n\t\topts := mtest.NewOptions().CollectionOptions(collOpts)\n\n\t\tmt.RunOpts(\"distinct with bson options\", opts, func(mt *mtest.T) {\n\t\t\t_, err := mt.Coll.InsertOne(context.Background(), bson.D{{\"y\", 1.7}})\n\t\t\tassert.NoError(mt, err, \"failed to insert double\")\n\n\t\t\tfilter := bson.D{{\"y\", bson.D{{\"$gt\", 1}}}}\n\n\t\t\tres := mt.Coll.Distinct(context.Background(), \"y\", filter)\n\t\t\tassert.Nil(mt, res.Err(), \"Distinct error: %v\", res.Err())\n\n\t\t\tvar got []int32\n\t\t\tassert.NoError(t, res.Decode(&got))\n\n\t\t\tassert.EqualValues(mt, []int32{1}, got)\n\t\t})\n\t})\n\tmt.RunOpts(\"find\", noClientOpts, func(mt *mtest.T) {\n\t\tmt.Run(\"found\", func(mt *mtest.T) {\n\t\t\tinitCollection(mt, mt.Coll)\n\t\t\tcursor, err := mt.Coll.Find(context.Background(), bson.D{}, options.Find().SetSort(bson.D{{\"x\", 1}}))\n\t\t\trequire.NoError(mt, err, \"Find error: %v\", err)\n\n\t\t\tresults := make([]int, 0, 5)\n\t\t\tfor cursor.Next(context.Background()) {\n\t\t\t\tx, err := cursor.Current.LookupErr(\"x\")\n\t\t\t\trequire.NoError(mt, err, \"x not found in document %v\", cursor.Current)\n\t\t\t\tassert.Equal(mt, bson.TypeInt32, x.Type, \"expected x type %v, got %v\", bson.TypeInt32, x.Type)\n\t\t\t\tresults = append(results, int(x.Int32()))\n\t\t\t}\n\t\t\tassert.Equal(mt, 5, len(results), \"expected 5 results, got %v\", len(results))\n\t\t\texpected := []int{1, 2, 3, 4, 5}\n\t\t\tassert.Equal(mt, expected, results, \"expected results %v, got %v\", expected, results)\n\t\t})\n\t\tmt.Run(\"limit and batch size\", func(mt *mtest.T) {\n\t\t\tinitCollection(mt, mt.Coll)\n\t\t\tfor _, batchSize := range []int32{2, 3, 4} {\n\t\t\t\tcursor, err := mt.Coll.Find(context.Background(), bson.D{}, options.Find().SetLimit(3).SetBatchSize(batchSize))\n\t\t\t\trequire.NoError(mt, err, \"Find error: %v\", err)\n\n\t\t\t\tnumReceived := 0\n\t\t\t\tfor cursor.Next(context.Background()) {\n\t\t\t\t\tnumReceived++\n\t\t\t\t}\n\t\t\t\terr = cursor.Err()\n\t\t\t\trequire.NoError(mt, err, \"cursor error: %v\", err)\n\t\t\t\tassert.Equal(mt, 3, numReceived, \"expected 3 results, got %v\", numReceived)\n\t\t\t}\n\t\t})\n\t\tmt.Run(\"not found\", func(mt *mtest.T) {\n\t\t\tinitCollection(mt, mt.Coll)\n\t\t\tcursor, err := mt.Coll.Find(context.Background(), bson.D{{\"x\", 6}})\n\t\t\trequire.NoError(mt, err, \"Find error: %v\", err)\n\t\t\tassert.False(mt, cursor.Next(context.Background()), \"expected no documents, found %v\", cursor.Current)\n\t\t})\n\t\tmt.Run(\"invalid identifier error\", func(mt *mtest.T) {\n\t\t\tcursor, err := mt.Coll.Find(context.Background(), bson.D{{\"$foo\", 1}})\n\t\t\tassert.NotNil(mt, err, \"expected error for invalid identifier, got nil\")\n\t\t\tassert.Nil(mt, cursor, \"expected nil cursor, got %v\", cursor)\n\t\t})\n\t\tmt.Run(\"negative limit\", func(mt *mtest.T) {\n\t\t\tinitCollection(mt, mt.Coll)\n\t\t\tc, err := mt.Coll.Find(context.Background(), bson.D{}, options.Find().SetLimit(-2))\n\t\t\trequire.NoError(mt, err, \"Find error: %v\", err)\n\t\t\t// single batch returned so cursor should have ID 0\n\t\t\tassert.Equal(mt, int64(0), c.ID(), \"expected cursor ID 0, got %v\", c.ID())\n\n\t\t\tvar numDocs int\n\t\t\tfor c.Next(context.Background()) {\n\t\t\t\tnumDocs++\n\t\t\t}\n\t\t\tassert.Equal(mt, 2, numDocs, \"expected 2 documents, got %v\", numDocs)\n\t\t})\n\t\tmt.Run(\"exhaust cursor\", func(mt *mtest.T) {\n\t\t\tinitCollection(mt, mt.Coll)\n\t\t\tc, err := mt.Coll.Find(context.Background(), bson.D{})\n\t\t\trequire.NoError(mt, err, \"Find error: %v\", err)\n\n\t\t\tvar numDocs int\n\t\t\tfor c.Next(context.Background()) {\n\t\t\t\tnumDocs++\n\t\t\t}\n\t\t\tassert.Equal(mt, 5, numDocs, \"expected 5 documents, got %v\", numDocs)\n\t\t\terr = c.Close(context.Background())\n\t\t\trequire.NoError(mt, err, \"Close error: %v\", err)\n\t\t})\n\t\tmt.Run(\"hint\", func(mt *mtest.T) {\n\t\t\t_, err := mt.Coll.Find(context.Background(), bson.D{}, options.Find().SetHint(\"_id_\"))\n\t\t\trequire.NoError(mt, err, \"Find error with string hint: %v\", err)\n\n\t\t\t_, err = mt.Coll.Find(context.Background(), bson.D{}, options.Find().SetHint(bson.D{{\"_id\", 1}}))\n\t\t\trequire.NoError(mt, err, \"Find error with document hint: %v\", err)\n\n\t\t\t_, err = mt.Coll.Find(context.Background(), bson.D{}, options.Find().SetHint(\"foobar\"))\n\t\t\t_, ok := err.(mongo.CommandError)\n\t\t\tassert.True(mt, ok, \"expected error type %v, got %v\", mongo.CommandError{}, err)\n\n\t\t\t_, err = mt.Coll.Find(context.Background(), bson.D{}, options.Find().SetHint(bson.M{\"_id\": 1}))\n\t\t\trequire.NoError(mt, err, \"Find error with single key map hint: %v\", err)\n\n\t\t\t_, err = mt.Coll.Find(context.Background(), bson.D{}, options.Find().SetHint(bson.M{\"_id\": 1, \"x\": 1}))\n\t\t\tassert.Equal(mt, mongo.ErrMapForOrderedArgument{\"hint\"}, err, \"expected error %v, got %v\", mongo.ErrMapForOrderedArgument{\"hint\"}, err)\n\t\t})\n\t\tmt.Run(\"sort\", func(mt *mtest.T) {\n\t\t\t_, err := mt.Coll.Find(context.Background(), bson.D{}, options.Find().SetSort(bson.M{\"_id\": 1}))\n\t\t\trequire.NoError(mt, err, \"Find error with single key map sort: %v\", err)\n\t\t\t_, err = mt.Coll.Find(context.Background(), bson.D{}, options.Find().SetSort(bson.M{\"_id\": 1, \"x\": 1}))\n\t\t\tassert.Equal(mt, mongo.ErrMapForOrderedArgument{\"sort\"}, err, \"expected error %v, got %v\", mongo.ErrMapForOrderedArgument{\"sort\"}, err)\n\t\t})\n\t\tmt.Run(\"limit and batch size and skip\", func(mt *mtest.T) {\n\t\t\ttestCases := []struct {\n\t\t\t\tlimit     int64\n\t\t\t\tbatchSize int32\n\t\t\t\tskip      int64\n\t\t\t\tname      string\n\t\t\t}{\n\t\t\t\t{\n\t\t\t\t\t99, 100, 10,\n\t\t\t\t\t\"case 1\",\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t100, 100, 20,\n\t\t\t\t\t\"case 2\",\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t80, 20, 90,\n\t\t\t\t\t\"case 3\",\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t201, 201, 0,\n\t\t\t\t\t\"case 4\",\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t100, 200, 120,\n\t\t\t\t\t\"case 5\",\n\t\t\t\t},\n\t\t\t}\n\t\t\tfor _, tc := range testCases {\n\t\t\t\tmt.Run(tc.name, func(mt *mtest.T) {\n\t\t\t\t\tvar insertDocs []any\n\t\t\t\t\tfor i := 1; i <= 201; i++ {\n\t\t\t\t\t\tinsertDocs = append(insertDocs, bson.D{{\"x\", int32(i)}})\n\t\t\t\t\t}\n\n\t\t\t\t\t_, err := mt.Coll.InsertMany(context.Background(), insertDocs)\n\t\t\t\t\trequire.NoError(mt, err, \"InsertMany error for initial data: %v\", err)\n\n\t\t\t\t\tfindOptions := options.Find().SetLimit(tc.limit).SetBatchSize(tc.batchSize).\n\t\t\t\t\t\tSetSkip(tc.skip)\n\t\t\t\t\tcursor, err := mt.Coll.Find(context.Background(), bson.D{}, findOptions)\n\t\t\t\t\trequire.NoError(mt, err, \"Find error: %v\", err)\n\n\t\t\t\t\tvar docs []any\n\t\t\t\t\terr = cursor.All(context.Background(), &docs)\n\t\t\t\t\trequire.NoError(mt, err, \"All error: %v\", err)\n\t\t\t\t\tif (201 - tc.skip) < tc.limit {\n\t\t\t\t\t\tassert.Equal(mt, int(201-tc.skip), len(docs), \"expected number of docs to be %v, got %v\", int(201-tc.skip), len(docs))\n\t\t\t\t\t} else {\n\t\t\t\t\t\tassert.Equal(mt, int(tc.limit), len(docs), \"expected number of docs to be %v, got %v\", tc.limit, len(docs))\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\t\tmt.Run(\"unset batch size does not surpass limit\", func(mt *mtest.T) {\n\t\t\ttestCases := []struct {\n\t\t\t\tlimit int64\n\t\t\t\tname  string\n\t\t\t}{\n\t\t\t\t{\n\t\t\t\t\t99,\n\t\t\t\t\t\"99\",\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t100,\n\t\t\t\t\t\"100\",\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t101,\n\t\t\t\t\t\"101\",\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t200,\n\t\t\t\t\t\"200\",\n\t\t\t\t},\n\t\t\t}\n\t\t\tfor _, tc := range testCases {\n\t\t\t\tmt.Run(tc.name, func(mt *mtest.T) {\n\t\t\t\t\tvar insertDocs []any\n\t\t\t\t\tfor i := 1; i <= 201; i++ {\n\t\t\t\t\t\tinsertDocs = append(insertDocs, bson.D{{\"x\", int32(i)}})\n\t\t\t\t\t}\n\n\t\t\t\t\t_, err := mt.Coll.InsertMany(context.Background(), insertDocs)\n\t\t\t\t\trequire.NoError(mt, err, \"InsertMany error for initial data: %v\", err)\n\t\t\t\t\topts := options.Find().SetSkip(0).SetLimit(tc.limit)\n\t\t\t\t\tcursor, err := mt.Coll.Find(context.Background(), bson.D{}, opts)\n\t\t\t\t\trequire.NoError(mt, err, \"Find error with limit %v: %v\", tc.limit, err)\n\n\t\t\t\t\tvar docs []any\n\t\t\t\t\terr = cursor.All(context.Background(), &docs)\n\t\t\t\t\trequire.NoError(mt, err, \"All error with limit %v: %v\", tc.limit, err)\n\n\t\t\t\t\tassert.Equal(mt, int(tc.limit), len(docs), \"expected number of docs to be %v, got %v\", tc.limit, len(docs))\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\t\tmt.Run(\"getMore commands are monitored\", func(mt *mtest.T) {\n\t\t\tinitCollection(mt, mt.Coll)\n\t\t\tassertGetMoreCommandsAreMonitored(mt, \"find\", func() (*mongo.Cursor, error) {\n\t\t\t\treturn mt.Coll.Find(context.Background(), bson.D{}, options.Find().SetBatchSize(3))\n\t\t\t})\n\t\t})\n\t\tmt.Run(\"killCursors commands are monitored\", func(mt *mtest.T) {\n\t\t\tinitCollection(mt, mt.Coll)\n\t\t\tassertKillCursorsCommandsAreMonitored(mt, \"find\", func() (*mongo.Cursor, error) {\n\t\t\t\treturn mt.Coll.Find(context.Background(), bson.D{}, options.Find().SetBatchSize(3))\n\t\t\t})\n\t\t})\n\t})\n\tmt.RunOpts(\"find one\", noClientOpts, func(mt *mtest.T) {\n\t\tmt.Run(\"limit\", func(mt *mtest.T) {\n\t\t\terr := mt.Coll.FindOne(context.Background(), bson.D{}).Err()\n\t\t\tassert.Equal(mt, mongo.ErrNoDocuments, err, \"expected error %v, got %v\", mongo.ErrNoDocuments, err)\n\n\t\t\tstarted := mt.GetStartedEvent()\n\t\t\tassert.NotNil(mt, started, \"expected CommandStartedEvent, got nil\")\n\t\t\tlimitVal, err := started.Command.LookupErr(\"limit\")\n\t\t\trequire.NoError(mt, err, \"limit not found in command\")\n\t\t\tlimit := limitVal.Int64()\n\t\t\tassert.Equal(mt, int64(1), limit, \"expected limit 1, got %v\", limit)\n\t\t})\n\t\tmt.Run(\"found\", func(mt *mtest.T) {\n\t\t\tinitCollection(mt, mt.Coll)\n\t\t\tres, err := mt.Coll.FindOne(context.Background(), bson.D{{\"x\", 1}}).Raw()\n\t\t\trequire.NoError(mt, err, \"FindOne error: %v\", err)\n\n\t\t\tx, err := res.LookupErr(\"x\")\n\t\t\trequire.NoError(mt, err, \"x not found in document %v\", res)\n\t\t\tassert.Equal(mt, bson.TypeInt32, x.Type, \"expected x type %v, got %v\", bson.TypeInt32, x.Type)\n\t\t\tgot := x.Int32()\n\t\t\tassert.Equal(mt, int32(1), got, \"expected x value 1, got %v\", got)\n\t\t})\n\t\tmt.RunOpts(\"options\", mtest.NewOptions().MinServerVersion(\"3.4\"), func(mt *mtest.T) {\n\t\t\tinitCollection(mt, mt.Coll)\n\n\t\t\t// Create an index for Hint, Max, and Min options\n\t\t\tindexView := mt.Coll.Indexes()\n\t\t\tindexName, err := indexView.CreateOne(context.Background(), mongo.IndexModel{\n\t\t\t\tKeys: bson.D{{\"x\", 1}},\n\t\t\t})\n\t\t\trequire.NoError(mt, err, \"CreateOne error: %v\", err)\n\n\t\t\tmt.ClearEvents()\n\t\t\texpectedComment := \"here's a query for ya\"\n\t\t\t// SetCursorType is excluded because tailable cursors can't be used with limit -1,\n\t\t\t// which all FindOne operations set, and the nontailable setting isn't passed to the\n\t\t\t// operation layer\n\t\t\t// SetMaxAwaitTime affects the cursor and not the server command, so it can't be checked\n\t\t\t// SetCursorTime and setMaxAwaitTime will be deprecated in GODRIVER-1775\n\t\t\topts := options.FindOne().\n\t\t\t\tSetAllowPartialResults(true).\n\t\t\t\tSetCollation(&options.Collation{Locale: \"en_US\"}).\n\t\t\t\tSetComment(expectedComment).\n\t\t\t\tSetHint(indexName).\n\t\t\t\tSetMax(bson.D{{\"x\", int32(5)}}).\n\t\t\t\tSetMin(bson.D{{\"x\", int32(0)}}).\n\t\t\t\tSetOplogReplay(false).\n\t\t\t\tSetProjection(bson.D{{\"x\", int32(1)}}).\n\t\t\t\tSetReturnKey(false).\n\t\t\t\tSetShowRecordID(false).\n\t\t\t\tSetSkip(0).\n\t\t\t\tSetSort(bson.D{{\"x\", int32(1)}})\n\t\t\tres, err := mt.Coll.FindOne(context.Background(), bson.D{}, opts).Raw()\n\t\t\trequire.NoError(mt, err, \"FindOne error: %v\", err)\n\n\t\t\tx, err := res.LookupErr(\"x\")\n\t\t\trequire.NoError(mt, err, \"x not found in document %v\", res)\n\t\t\tassert.Equal(mt, bson.TypeInt32, x.Type, \"expected x type %v, got %v\", bson.TypeInt32, x.Type)\n\t\t\tgot := x.Int32()\n\t\t\tassert.Equal(mt, int32(1), got, \"expected x value 1, got %v\", got)\n\n\t\t\toptionsDoc := bsoncore.NewDocumentBuilder().\n\t\t\t\tAppendBoolean(\"allowPartialResults\", true).\n\t\t\t\tStartDocument(\"collation\").AppendString(\"locale\", \"en_US\").FinishDocument().\n\t\t\t\tAppendString(\"comment\", expectedComment).\n\t\t\t\tAppendString(\"hint\", indexName).\n\t\t\t\tStartDocument(\"max\").AppendInt32(\"x\", 5).FinishDocument().\n\t\t\t\tStartDocument(\"min\").AppendInt32(\"x\", 0).FinishDocument().\n\t\t\t\tAppendBoolean(\"oplogReplay\", false).\n\t\t\t\tStartDocument(\"projection\").AppendInt32(\"x\", 1).FinishDocument().\n\t\t\t\tAppendBoolean(\"returnKey\", false).\n\t\t\t\tAppendBoolean(\"showRecordId\", false).\n\t\t\t\tAppendInt64(\"skip\", 0).\n\t\t\t\tStartDocument(\"sort\").AppendInt32(\"x\", 1).FinishDocument().\n\t\t\t\tBuild()\n\n\t\t\tstarted := mt.GetStartedEvent()\n\t\t\tassert.NotNil(mt, started, \"expected CommandStartedEvent, got nil\")\n\n\t\t\tif err := compareDocs(mt, bson.Raw(optionsDoc), started.Command); err != nil {\n\t\t\t\tmt.Fatalf(\"options mismatch: %v\", err)\n\t\t\t}\n\t\t})\n\t\tmt.Run(\"not found\", func(mt *mtest.T) {\n\t\t\tinitCollection(mt, mt.Coll)\n\t\t\terr := mt.Coll.FindOne(context.Background(), bson.D{{\"x\", 6}}).Err()\n\t\t\tassert.Equal(mt, mongo.ErrNoDocuments, err, \"expected error %v, got %v\", mongo.ErrNoDocuments, err)\n\t\t})\n\t\tmt.RunOpts(\"maps for sorted opts\", noClientOpts, func(mt *mtest.T) {\n\t\t\ttestCases := []struct {\n\t\t\t\tname     string\n\t\t\t\topts     *options.FindOneOptionsBuilder\n\t\t\t\terrParam string\n\t\t\t}{\n\t\t\t\t{\"single key hint\", options.FindOne().SetHint(bson.M{\"x\": 1}), \"\"},\n\t\t\t\t{\"multikey hint\", options.FindOne().SetHint(bson.M{\"x\": 1, \"y\": 1}), \"hint\"},\n\t\t\t\t{\"single key sort\", options.FindOne().SetSort(bson.M{\"x\": 1}), \"\"},\n\t\t\t\t{\"multikey sort\", options.FindOne().SetSort(bson.M{\"x\": 1, \"y\": 1}), \"sort\"},\n\t\t\t}\n\t\t\tfor _, tc := range testCases {\n\t\t\t\tmt.Run(tc.name, func(mt *mtest.T) {\n\t\t\t\t\tinitCollection(mt, mt.Coll)\n\t\t\t\t\tindexView := mt.Coll.Indexes()\n\t\t\t\t\t_, err := indexView.CreateOne(context.Background(), mongo.IndexModel{\n\t\t\t\t\t\tKeys: bson.D{{\"x\", 1}},\n\t\t\t\t\t})\n\t\t\t\t\trequire.NoError(mt, err, \"CreateOne error: %v\", err)\n\n\t\t\t\t\tres, err := mt.Coll.FindOne(context.Background(), bson.D{{\"x\", 1}}, tc.opts).Raw()\n\n\t\t\t\t\tif tc.errParam != \"\" {\n\t\t\t\t\t\texpErr := mongo.ErrMapForOrderedArgument{tc.errParam}\n\t\t\t\t\t\tassert.Equal(mt, expErr, err, \"expected error %v, got %v\", expErr, err)\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\n\t\t\t\t\trequire.NoError(mt, err, \"FindOne error: %v\", err)\n\t\t\t\t\tx, err := res.LookupErr(\"x\")\n\t\t\t\t\trequire.NoError(mt, err, \"x not found in document %v\", res)\n\t\t\t\t\tgot, ok := x.Int32OK()\n\t\t\t\t\tassert.True(mt, ok, \"expected x type int32, got %v\", x.Type)\n\t\t\t\t\tassert.Equal(mt, int32(1), got, \"expected x value 1, got %v\", got)\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\t})\n\tmt.RunOpts(\"find one and delete\", noClientOpts, func(mt *mtest.T) {\n\t\tmt.Run(\"found\", func(mt *mtest.T) {\n\t\t\tinitCollection(mt, mt.Coll)\n\t\t\tres, err := mt.Coll.FindOneAndDelete(context.Background(), bson.D{{\"x\", 3}}).Raw()\n\t\t\trequire.NoError(mt, err, \"FindOneAndDelete error: %v\", err)\n\n\t\t\telem, err := res.LookupErr(\"x\")\n\t\t\trequire.NoError(mt, err, \"x not found in result %v\", res)\n\t\t\tassert.Equal(mt, bson.TypeInt32, elem.Type, \"expected x type %v, got %v\", bson.TypeInt32, elem.Type)\n\t\t\tx := elem.Int32()\n\t\t\tassert.Equal(mt, int32(3), x, \"expected x value 3, got %v\", x)\n\t\t})\n\t\tmt.Run(\"found ignore result\", func(mt *mtest.T) {\n\t\t\tinitCollection(mt, mt.Coll)\n\t\t\terr := mt.Coll.FindOneAndDelete(context.Background(), bson.D{{\"x\", 3}}).Err()\n\t\t\trequire.NoError(mt, err, \"FindOneAndDelete error: %v\", err)\n\t\t})\n\t\tmt.Run(\"not found\", func(mt *mtest.T) {\n\t\t\tinitCollection(mt, mt.Coll)\n\t\t\terr := mt.Coll.FindOneAndDelete(context.Background(), bson.D{{\"x\", 6}}).Err()\n\t\t\tassert.Equal(mt, mongo.ErrNoDocuments, err, \"expected error %v, got %v\", mongo.ErrNoDocuments, err)\n\t\t})\n\t\tmt.RunOpts(\"maps for sorted opts\", noClientOpts, func(mt *mtest.T) {\n\t\t\ttestCases := []struct {\n\t\t\t\tname     string\n\t\t\t\topts     *options.FindOneAndDeleteOptionsBuilder\n\t\t\t\terrParam string\n\t\t\t}{\n\t\t\t\t{\"single key hint\", options.FindOneAndDelete().SetHint(bson.M{\"x\": 1}), \"\"},\n\t\t\t\t{\"multikey hint\", options.FindOneAndDelete().SetHint(bson.M{\"x\": 1, \"y\": 1}), \"hint\"},\n\t\t\t\t{\"single key sort\", options.FindOneAndDelete().SetSort(bson.M{\"x\": 1}), \"\"},\n\t\t\t\t{\"multikey sort\", options.FindOneAndDelete().SetSort(bson.M{\"x\": 1, \"y\": 1}), \"sort\"},\n\t\t\t}\n\t\t\tfor _, tc := range testCases {\n\t\t\t\tmt.RunOpts(tc.name, mtest.NewOptions().MinServerVersion(\"4.4\"), func(mt *mtest.T) {\n\t\t\t\t\tinitCollection(mt, mt.Coll)\n\t\t\t\t\tindexView := mt.Coll.Indexes()\n\t\t\t\t\t_, err := indexView.CreateOne(context.Background(), mongo.IndexModel{\n\t\t\t\t\t\tKeys: bson.D{{\"x\", 1}},\n\t\t\t\t\t})\n\t\t\t\t\trequire.NoError(mt, err, \"CreateOne error: %v\", err)\n\n\t\t\t\t\tres, err := mt.Coll.FindOneAndDelete(context.Background(), bson.D{{\"x\", 1}}, tc.opts).Raw()\n\n\t\t\t\t\tif tc.errParam != \"\" {\n\t\t\t\t\t\texpErr := mongo.ErrMapForOrderedArgument{tc.errParam}\n\t\t\t\t\t\tassert.Equal(mt, expErr, err, \"expected error %v, got %v\", expErr, err)\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\n\t\t\t\t\trequire.NoError(mt, err, \"FindOneAndDelete error: %v\", err)\n\t\t\t\t\tx, err := res.LookupErr(\"x\")\n\t\t\t\t\trequire.NoError(mt, err, \"x not found in document %v\", res)\n\t\t\t\t\tgot, ok := x.Int32OK()\n\t\t\t\t\tassert.True(mt, ok, \"expected x type int32, got %v\", x.Type)\n\t\t\t\t\tassert.Equal(mt, int32(1), got, \"expected x value 1, got %v\", got)\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\t\twcCollOpts := options.Collection().SetWriteConcern(impossibleWc)\n\t\twcTestOpts := mtest.NewOptions().CollectionOptions(wcCollOpts).Topologies(mtest.ReplicaSet).MinServerVersion(\"3.2\")\n\t\tmt.RunOpts(\"write concern error\", wcTestOpts, func(mt *mtest.T) {\n\t\t\terr := mt.Coll.FindOneAndDelete(context.Background(), bson.D{{\"x\", 3}}).Err()\n\t\t\twe, ok := err.(mongo.WriteException)\n\t\t\tassert.True(mt, ok, \"expected error type %v, got %v\", mongo.WriteException{}, err)\n\t\t\tassert.NotNil(mt, we.WriteConcernError, \"expected write concern error, got %v\", err)\n\t\t})\n\t})\n\tmt.RunOpts(\"find one and replace\", noClientOpts, func(mt *mtest.T) {\n\t\tmt.Run(\"found\", func(mt *mtest.T) {\n\t\t\tinitCollection(mt, mt.Coll)\n\t\t\tfilter := bson.D{{\"x\", 3}}\n\t\t\treplacement := bson.D{{\"y\", 3}}\n\n\t\t\tres, err := mt.Coll.FindOneAndReplace(context.Background(), filter, replacement).Raw()\n\t\t\trequire.NoError(mt, err, \"FindOneAndReplace error: %v\", err)\n\t\t\telem, err := res.LookupErr(\"x\")\n\t\t\trequire.NoError(mt, err, \"x not found in result %v\", res)\n\t\t\tassert.Equal(mt, bson.TypeInt32, elem.Type, \"expected x type %v, got %v\", bson.TypeInt32, elem.Type)\n\t\t\tx := elem.Int32()\n\t\t\tassert.Equal(mt, int32(3), x, \"expected x value 3, got %v\", x)\n\t\t})\n\t\tmt.Run(\"found ignore result\", func(mt *mtest.T) {\n\t\t\tinitCollection(mt, mt.Coll)\n\t\t\tfilter := bson.D{{\"x\", 3}}\n\t\t\treplacement := bson.D{{\"y\", 3}}\n\n\t\t\terr := mt.Coll.FindOneAndReplace(context.Background(), filter, replacement).Err()\n\t\t\trequire.NoError(mt, err, \"FindOneAndReplace error: %v\", err)\n\t\t})\n\t\tmt.Run(\"not found\", func(mt *mtest.T) {\n\t\t\tinitCollection(mt, mt.Coll)\n\t\t\tfilter := bson.D{{\"x\", 6}}\n\t\t\treplacement := bson.D{{\"y\", 6}}\n\n\t\t\terr := mt.Coll.FindOneAndReplace(context.Background(), filter, replacement).Err()\n\t\t\tassert.Equal(mt, mongo.ErrNoDocuments, err, \"expected error %v, got %v\", mongo.ErrNoDocuments, err)\n\t\t})\n\t\tmt.RunOpts(\"maps for sorted opts\", noClientOpts, func(mt *mtest.T) {\n\t\t\ttestCases := []struct {\n\t\t\t\tname     string\n\t\t\t\topts     *options.FindOneAndReplaceOptionsBuilder\n\t\t\t\terrParam string\n\t\t\t}{\n\t\t\t\t{\"single key hint\", options.FindOneAndReplace().SetHint(bson.M{\"x\": 1}), \"\"},\n\t\t\t\t{\"multikey hint\", options.FindOneAndReplace().SetHint(bson.M{\"x\": 1, \"y\": 1}), \"hint\"},\n\t\t\t\t{\"single key sort\", options.FindOneAndReplace().SetSort(bson.M{\"x\": 1}), \"\"},\n\t\t\t\t{\"multikey sort\", options.FindOneAndReplace().SetSort(bson.M{\"x\": 1, \"y\": 1}), \"sort\"},\n\t\t\t}\n\t\t\tfor _, tc := range testCases {\n\t\t\t\tmt.RunOpts(tc.name, mtest.NewOptions().MinServerVersion(\"4.4\"), func(mt *mtest.T) {\n\t\t\t\t\tinitCollection(mt, mt.Coll)\n\t\t\t\t\tindexView := mt.Coll.Indexes()\n\t\t\t\t\t_, err := indexView.CreateOne(context.Background(), mongo.IndexModel{\n\t\t\t\t\t\tKeys: bson.D{{\"x\", 1}},\n\t\t\t\t\t})\n\t\t\t\t\trequire.NoError(mt, err, \"CreateOne error: %v\", err)\n\n\t\t\t\t\tres, err := mt.Coll.FindOneAndReplace(context.Background(), bson.D{{\"x\", 1}}, bson.D{{\"y\", 3}}, tc.opts).Raw()\n\n\t\t\t\t\tif tc.errParam != \"\" {\n\t\t\t\t\t\texpErr := mongo.ErrMapForOrderedArgument{tc.errParam}\n\t\t\t\t\t\tassert.Equal(mt, expErr, err, \"expected error %v, got %v\", expErr, err)\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\n\t\t\t\t\trequire.NoError(mt, err, \"FindOneAndReplace error: %v\", err)\n\t\t\t\t\tx, err := res.LookupErr(\"x\")\n\t\t\t\t\trequire.NoError(mt, err, \"x not found in document %v\", res)\n\t\t\t\t\tgot, ok := x.Int32OK()\n\t\t\t\t\tassert.True(mt, ok, \"expected x type int32, got %v\", x.Type)\n\t\t\t\t\tassert.Equal(mt, int32(1), got, \"expected x value 1, got %v\", got)\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\t\twcCollOpts := options.Collection().SetWriteConcern(impossibleWc)\n\t\twcTestOpts := mtest.NewOptions().CollectionOptions(wcCollOpts).Topologies(mtest.ReplicaSet).MinServerVersion(\"3.2\")\n\t\tmt.RunOpts(\"write concern error\", wcTestOpts, func(mt *mtest.T) {\n\t\t\tfilter := bson.D{{\"x\", 3}}\n\t\t\treplacement := bson.D{{\"y\", 3}}\n\t\t\terr := mt.Coll.FindOneAndReplace(context.Background(), filter, replacement).Err()\n\t\t\twe, ok := err.(mongo.WriteException)\n\t\t\tassert.True(mt, ok, \"expected error type %v, got %v\", mongo.WriteException{}, err)\n\t\t\tassert.NotNil(mt, we.WriteConcernError, \"expected write concern error, got %v\", err)\n\t\t})\n\t})\n\tmt.RunOpts(\"find one and update\", noClientOpts, func(mt *mtest.T) {\n\t\tmt.Run(\"found\", func(mt *mtest.T) {\n\t\t\tinitCollection(mt, mt.Coll)\n\t\t\tfilter := bson.D{{\"x\", 3}}\n\t\t\tupdate := bson.D{{\"$set\", bson.D{{\"x\", 6}}}}\n\n\t\t\tres, err := mt.Coll.FindOneAndUpdate(context.Background(), filter, update).Raw()\n\t\t\trequire.NoError(mt, err, \"FindOneAndUpdate error: %v\", err)\n\t\t\telem, err := res.LookupErr(\"x\")\n\t\t\trequire.NoError(mt, err, \"x not found in result %v\", res)\n\t\t\tassert.Equal(mt, bson.TypeInt32, elem.Type, \"expected x type %v, got %v\", bson.TypeInt32, elem.Type)\n\t\t\tx := elem.Int32()\n\t\t\tassert.Equal(mt, int32(3), x, \"expected x value 3, got %v\", x)\n\t\t})\n\t\tmt.Run(\"empty update\", func(mt *mtest.T) {\n\t\t\terr := mt.Coll.FindOneAndUpdate(context.Background(), bson.D{}, bson.D{})\n\t\t\tassert.NotNil(mt, err, \"expected error, got nil\")\n\t\t})\n\t\tmt.Run(\"found ignore result\", func(mt *mtest.T) {\n\t\t\tinitCollection(mt, mt.Coll)\n\t\t\tfilter := bson.D{{\"x\", 3}}\n\t\t\tupdate := bson.D{{\"$set\", bson.D{{\"x\", 6}}}}\n\n\t\t\terr := mt.Coll.FindOneAndUpdate(context.Background(), filter, update).Err()\n\t\t\trequire.NoError(mt, err, \"FindOneAndUpdate error: %v\", err)\n\t\t})\n\t\tmt.Run(\"not found\", func(mt *mtest.T) {\n\t\t\tinitCollection(mt, mt.Coll)\n\t\t\tfilter := bson.D{{\"x\", 6}}\n\t\t\tupdate := bson.D{{\"$set\", bson.D{{\"y\", 6}}}}\n\n\t\t\terr := mt.Coll.FindOneAndUpdate(context.Background(), filter, update).Err()\n\t\t\tassert.Equal(mt, mongo.ErrNoDocuments, err, \"expected error %v, got %v\", mongo.ErrNoDocuments, err)\n\t\t})\n\t\tmt.RunOpts(\"maps for sorted opts\", noClientOpts, func(mt *mtest.T) {\n\t\t\ttestCases := []struct {\n\t\t\t\tname     string\n\t\t\t\topts     *options.FindOneAndUpdateOptionsBuilder\n\t\t\t\terrParam string\n\t\t\t}{\n\t\t\t\t{\"single key hint\", options.FindOneAndUpdate().SetHint(bson.M{\"x\": 1}), \"\"},\n\t\t\t\t{\"multikey hint\", options.FindOneAndUpdate().SetHint(bson.M{\"x\": 1, \"y\": 1}), \"hint\"},\n\t\t\t\t{\"single key sort\", options.FindOneAndUpdate().SetSort(bson.M{\"x\": 1}), \"\"},\n\t\t\t\t{\"multikey sort\", options.FindOneAndUpdate().SetSort(bson.M{\"x\": 1, \"y\": 1}), \"sort\"},\n\t\t\t}\n\t\t\tfor _, tc := range testCases {\n\t\t\t\tmt.RunOpts(tc.name, mtest.NewOptions().MinServerVersion(\"4.4\"), func(mt *mtest.T) {\n\t\t\t\t\tinitCollection(mt, mt.Coll)\n\t\t\t\t\tindexView := mt.Coll.Indexes()\n\t\t\t\t\t_, err := indexView.CreateOne(context.Background(), mongo.IndexModel{\n\t\t\t\t\t\tKeys: bson.D{{\"x\", 1}},\n\t\t\t\t\t})\n\t\t\t\t\trequire.NoError(mt, err, \"CreateOne error: %v\", err)\n\n\t\t\t\t\tres, err := mt.Coll.FindOneAndUpdate(context.Background(), bson.D{{\"x\", 1}}, bson.D{{\"$set\", bson.D{{\"x\", 6}}}}, tc.opts).Raw()\n\n\t\t\t\t\tif tc.errParam != \"\" {\n\t\t\t\t\t\texpErr := mongo.ErrMapForOrderedArgument{tc.errParam}\n\t\t\t\t\t\tassert.Equal(mt, expErr, err, \"expected error %v, got %v\", expErr, err)\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\n\t\t\t\t\trequire.NoError(mt, err, \"FindOneAndUpdate error: %v\", err)\n\t\t\t\t\tx, err := res.LookupErr(\"x\")\n\t\t\t\t\trequire.NoError(mt, err, \"x not found in document %v\", res)\n\t\t\t\t\tgot, ok := x.Int32OK()\n\t\t\t\t\tassert.True(mt, ok, \"expected x type int32, got %v\", x.Type)\n\t\t\t\t\tassert.Equal(mt, int32(1), got, \"expected x value 1, got %v\", got)\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\t\twcCollOpts := options.Collection().SetWriteConcern(impossibleWc)\n\t\twcTestOpts := mtest.NewOptions().CollectionOptions(wcCollOpts).Topologies(mtest.ReplicaSet).MinServerVersion(\"3.2\")\n\t\tmt.RunOpts(\"write concern error\", wcTestOpts, func(mt *mtest.T) {\n\t\t\tfilter := bson.D{{\"x\", 3}}\n\t\t\tupdate := bson.D{{\"$set\", bson.D{{\"x\", 6}}}}\n\t\t\terr := mt.Coll.FindOneAndUpdate(context.Background(), filter, update).Err()\n\t\t\twe, ok := err.(mongo.WriteException)\n\t\t\tassert.True(mt, ok, \"expected error type %v, got %v\", mongo.WriteException{}, err)\n\t\t\tassert.NotNil(mt, we.WriteConcernError, \"expected write concern error, got %v\", err)\n\t\t})\n\t})\n\n\tunackClientOpts := options.Client().\n\t\tSetWriteConcern(writeconcern.Unacknowledged())\n\tunackMtOpts := mtest.NewOptions().\n\t\tClientOptions(unackClientOpts).\n\t\tMinServerVersion(\"3.6\")\n\tmt.RunOpts(\"unacknowledged writes\", unackMtOpts, func(mt *mtest.T) {\n\t\tmt.Run(\"bulk write\", func(mt *mtest.T) {\n\t\t\tmodels := []mongo.WriteModel{\n\t\t\t\tmongo.NewInsertOneModel().SetDocument(bson.D{{\"x\", 1}}),\n\t\t\t}\n\n\t\t\tres, err := mt.Coll.BulkWrite(context.Background(), models)\n\n\t\t\tassert.NoError(mt, err)\n\t\t\tassert.False(mt, res.Acknowledged)\n\t\t})\n\n\t\tmt.Run(\"insert one\", func(mt *mtest.T) {\n\t\t\tres, err := mt.Coll.InsertOne(context.Background(), bson.D{{\"x\", 1}})\n\n\t\t\tassert.NoError(mt, err)\n\t\t\tassert.False(mt, res.Acknowledged)\n\t\t})\n\n\t\tmt.Run(\"insert many\", func(mt *mtest.T) {\n\t\t\tdocs := []any{\n\t\t\t\tbson.D{{\"x\", 1}},\n\t\t\t\tbson.D{{\"y\", 1}},\n\t\t\t}\n\n\t\t\tres, err := mt.Coll.InsertMany(context.Background(), docs)\n\n\t\t\tassert.NoError(mt, err)\n\t\t\tassert.False(mt, res.Acknowledged)\n\t\t})\n\n\t\tmt.Run(\"delete\", func(mt *mtest.T) {\n\t\t\tres, err := mt.Coll.DeleteOne(context.Background(), bson.D{{\"x\", 1}})\n\n\t\t\tassert.NoError(mt, err)\n\t\t\tassert.False(mt, res.Acknowledged)\n\t\t})\n\n\t\tmt.Run(\"update\", func(mt *mtest.T) {\n\t\t\tres, err := mt.Coll.UpdateOne(context.Background(), bson.D{{\"x\", 1}}, bson.D{{\"$set\", bson.D{{\"x\", \"2\"}}}})\n\n\t\t\tassert.NoError(mt, err)\n\t\t\tassert.False(mt, res.Acknowledged)\n\t\t})\n\n\t\tmt.Run(\"find and modify\", func(mt *mtest.T) {\n\t\t\tres := mt.Coll.FindOneAndDelete(context.Background(), bson.D{{\"x\", 1}})\n\n\t\t\tassert.ErrorIs(mt, res.Err(), mongo.ErrNoDocuments)\n\t\t\tassert.False(mt, res.Acknowledged)\n\t\t})\n\n\t\tmt.Run(\"dropping a collection\", func(mt *mtest.T) {\n\t\t\terr := mt.Coll.Drop(context.Background())\n\t\t\tassert.NoError(mt, err)\n\t\t})\n\n\t\tmt.Run(\"creating a collection\", func(mt *mtest.T) {\n\t\t\terr := mt.DB.CreateCollection(context.Background(), \"test coll\")\n\t\t\tassert.NoError(mt, err)\n\t\t})\n\n\t\tmt.Run(\"creating an index\", func(mt *mtest.T) {\n\t\t\tindexModel := mongo.IndexModel{\n\t\t\t\tKeys:    bson.M{\"username\": 1},\n\t\t\t\tOptions: options.Index().SetUnique(true),\n\t\t\t}\n\n\t\t\t_, err := mt.Coll.Indexes().CreateMany(context.Background(), []mongo.IndexModel{indexModel})\n\t\t\tassert.NoError(mt, err)\n\t\t})\n\n\t\tmt.Run(\"creating an index view\", func(mt *mtest.T) {\n\t\t\tprojectStage := bson.D{\n\t\t\t\t{\"$project\", bson.D{\n\t\t\t\t\t{\"_id\", 0},\n\t\t\t\t\t{\"fullName\", bson.D{\n\t\t\t\t\t\t{\"$concat\", []string{\"$firstName\", \" \", \"$lastName\"}},\n\t\t\t\t\t}},\n\t\t\t\t}},\n\t\t\t}\n\n\t\t\tpipeline := mongo.Pipeline{projectStage}\n\n\t\t\terr := mt.DB.CreateView(context.Background(), \"testview\", \"coll\", pipeline, nil)\n\t\t\tassert.NoError(mt, err)\n\t\t})\n\n\t\tmt.Run(\"dropping a database\", func(mt *mtest.T) {\n\t\t\tdb := mt.Client.Database(\"bd7b09e4-7d12-4bcb-9fc6-9852ad93715a\")\n\n\t\t\terr := db.Drop(context.Background())\n\t\t\tassert.NoError(mt, err)\n\t\t})\n\n\t\tmt.Run(\"dropping an index\", func(mt *mtest.T) {\n\t\t\tindexModel := mongo.IndexModel{\n\t\t\t\tKeys:    bson.M{\"username\": 1},\n\t\t\t\tOptions: options.Index().SetUnique(true).SetName(\"username_1\"),\n\t\t\t}\n\n\t\t\t_, err := mt.Coll.Indexes().CreateOne(context.TODO(), indexModel)\n\t\t\tassert.NoError(mt, err, \"failed to create index\")\n\n\t\t\t_ = mt.Coll.Indexes().DropOne(context.Background(), \"username_1\")\n\t\t})\n\t})\n\n\tmt.RunOpts(\"bulk write\", noClientOpts, func(mt *mtest.T) {\n\t\twcCollOpts := options.Collection().SetWriteConcern(impossibleWc)\n\t\twcTestOpts := mtest.NewOptions().CollectionOptions(wcCollOpts).Topologies(mtest.ReplicaSet).CreateClient(false)\n\t\tmt.RunOpts(\"write concern error\", wcTestOpts, func(mt *mtest.T) {\n\t\t\tfilter := bson.D{{\"foo\", \"bar\"}}\n\t\t\tupdate := bson.D{{\"$set\", bson.D{{\"foo\", 10}}}}\n\t\t\tinsertModel := mongo.NewInsertOneModel().SetDocument(bson.D{{\"foo\", 1}})\n\t\t\tupdateModel := mongo.NewUpdateOneModel().SetFilter(filter).SetUpdate(update)\n\t\t\tdeleteModel := mongo.NewDeleteOneModel().SetFilter(filter)\n\n\t\t\ttestCases := []struct {\n\t\t\t\tname   string\n\t\t\t\tmodels []mongo.WriteModel\n\t\t\t}{\n\t\t\t\t{\"insert\", []mongo.WriteModel{insertModel}},\n\t\t\t\t{\"update\", []mongo.WriteModel{updateModel}},\n\t\t\t\t{\"delete\", []mongo.WriteModel{deleteModel}},\n\t\t\t}\n\t\t\tfor _, tc := range testCases {\n\t\t\t\tmt.Run(tc.name, func(mt *mtest.T) {\n\t\t\t\t\t_, err := mt.Coll.BulkWrite(context.Background(), tc.models)\n\t\t\t\t\tbwe, ok := err.(mongo.BulkWriteException)\n\t\t\t\t\tassert.True(mt, ok, \"expected error type %v, got %v\", mongo.BulkWriteException{}, err)\n\t\t\t\t\tnumWriteErrors := len(bwe.WriteErrors)\n\t\t\t\t\tassert.Equal(mt, 0, numWriteErrors, \"expected 0 write errors, got %v\", numWriteErrors)\n\t\t\t\t\tassert.NotNil(mt, bwe.WriteConcernError, \"expected write concern error, got %v\", err)\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\n\t\tmt.RunOpts(\"insert write errors\", noClientOpts, func(mt *mtest.T) {\n\t\t\tdoc1 := mongo.NewInsertOneModel().SetDocument(bson.D{{\"_id\", \"x\"}})\n\t\t\tdoc2 := mongo.NewInsertOneModel().SetDocument(bson.D{{\"_id\", \"y\"}})\n\t\t\tmodels := []mongo.WriteModel{doc1, doc1, doc2, doc2}\n\n\t\t\ttestCases := []struct {\n\t\t\t\tname           string\n\t\t\t\tordered        bool\n\t\t\t\tinsertedCount  int64\n\t\t\t\tnumWriteErrors int\n\t\t\t}{\n\t\t\t\t{\"ordered\", true, 1, 1},\n\t\t\t\t{\"unordered\", false, 2, 2},\n\t\t\t}\n\t\t\tfor _, tc := range testCases {\n\t\t\t\tmt.Run(tc.name, func(mt *mtest.T) {\n\t\t\t\t\tres, err := mt.Coll.BulkWrite(context.Background(), models, options.BulkWrite().SetOrdered(tc.ordered))\n\t\t\t\t\tassert.Equal(mt, tc.insertedCount, res.InsertedCount,\n\t\t\t\t\t\t\"expected inserted count %v, got %v\", tc.insertedCount, res.InsertedCount)\n\n\t\t\t\t\tbwe, ok := err.(mongo.BulkWriteException)\n\t\t\t\t\tassert.True(mt, ok, \"expected error type %v, got %v\", mongo.BulkWriteException{}, err)\n\t\t\t\t\tnumWriteErrors := len(bwe.WriteErrors)\n\t\t\t\t\tassert.Equal(mt, tc.numWriteErrors, numWriteErrors,\n\t\t\t\t\t\t\"expected %v write errors, got %v\", tc.numWriteErrors, numWriteErrors)\n\t\t\t\t\tgotCode := bwe.WriteErrors[0].Code\n\t\t\t\t\tassert.Equal(mt, errorDuplicateKey, gotCode, \"expected error code %v, got %v\", errorDuplicateKey, gotCode)\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\t\tmt.RunOpts(\"delete write errors\", mtest.NewOptions().MaxServerVersion(\"5.0.7\"), func(mt *mtest.T) {\n\t\t\t// Deletes are not allowed on capped collections on MongoDB 5.0.6-. We use this\n\t\t\t// behavior to test the processing of write errors.\n\t\t\tdoc := mongo.NewDeleteOneModel().SetFilter(bson.D{{\"x\", 1}})\n\t\t\tmodels := []mongo.WriteModel{doc, doc}\n\t\t\tcappedOpts := options.CreateCollection().SetCapped(true).SetSizeInBytes(64 * 1024)\n\t\t\tcapped := mt.CreateCollection(mtest.Collection{\n\t\t\t\tName:       \"delete_write_errors\",\n\t\t\t\tCreateOpts: cappedOpts,\n\t\t\t}, true)\n\n\t\t\ttestCases := []struct {\n\t\t\t\tname           string\n\t\t\t\tordered        bool\n\t\t\t\tnumWriteErrors int\n\t\t\t}{\n\t\t\t\t{\"ordered\", true, 1},\n\t\t\t\t{\"unordered\", false, 2},\n\t\t\t}\n\t\t\tfor _, tc := range testCases {\n\t\t\t\tmt.Run(tc.name, func(mt *mtest.T) {\n\t\t\t\t\t_, err := capped.BulkWrite(context.Background(), models, options.BulkWrite().SetOrdered(tc.ordered))\n\t\t\t\t\tbwe, ok := err.(mongo.BulkWriteException)\n\t\t\t\t\tassert.True(mt, ok, \"expected error type %v, got %v\", mongo.BulkWriteException{}, err)\n\t\t\t\t\tnumWriteErrors := len(bwe.WriteErrors)\n\t\t\t\t\tassert.Equal(mt, tc.numWriteErrors, numWriteErrors,\n\t\t\t\t\t\t\"expected %v write errors, got %v\", tc.numWriteErrors, numWriteErrors)\n\t\t\t\t\tgotCode := bwe.WriteErrors[0].Code\n\t\t\t\t\tassert.True(mt, gotCode == errorCappedCollDeleteLegacy || gotCode == errorCappedCollDelete,\n\t\t\t\t\t\t\"expected error code %v or %v, got %v\", errorCappedCollDeleteLegacy, errorCappedCollDelete, gotCode)\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\t\tmt.RunOpts(\"update write errors\", noClientOpts, func(mt *mtest.T) {\n\t\t\tfilter := bson.D{{\"_id\", \"foo\"}}\n\t\t\tdoc1 := mongo.NewUpdateOneModel().SetFilter(filter).SetUpdate(bson.D{{\"$set\", bson.D{{\"_id\", 3.14159}}}})\n\t\t\tdoc2 := mongo.NewUpdateOneModel().SetFilter(filter).SetUpdate(bson.D{{\"$set\", bson.D{{\"x\", \"fa\"}}}})\n\t\t\tmodels := []mongo.WriteModel{doc1, doc1, doc2}\n\n\t\t\ttestCases := []struct {\n\t\t\t\tname           string\n\t\t\t\tordered        bool\n\t\t\t\tmodifiedCount  int64\n\t\t\t\tnumWriteErrors int\n\t\t\t}{\n\t\t\t\t{\"ordered\", true, 0, 1},\n\t\t\t\t{\"unordered\", false, 1, 2},\n\t\t\t}\n\t\t\tfor _, tc := range testCases {\n\t\t\t\tmt.Run(tc.name, func(mt *mtest.T) {\n\t\t\t\t\t_, err := mt.Coll.InsertOne(context.Background(), filter)\n\t\t\t\t\trequire.NoError(mt, err, \"InsertOne error: %v\", err)\n\t\t\t\t\tres, err := mt.Coll.BulkWrite(context.Background(), models, options.BulkWrite().SetOrdered(tc.ordered))\n\t\t\t\t\tassert.Equal(mt, tc.modifiedCount, res.ModifiedCount,\n\t\t\t\t\t\t\"expected modified count %v, got %v\", tc.modifiedCount, res.ModifiedCount)\n\n\t\t\t\t\tbwe, ok := err.(mongo.BulkWriteException)\n\t\t\t\t\tassert.True(mt, ok, \"expected error type %v, got %v\", mongo.BulkWriteException{}, err)\n\t\t\t\t\tnumWriteErrors := len(bwe.WriteErrors)\n\t\t\t\t\tassert.Equal(mt, tc.numWriteErrors, numWriteErrors,\n\t\t\t\t\t\t\"expected %v write errors, got %v\", tc.numWriteErrors, numWriteErrors)\n\t\t\t\t\tgotCode := bwe.WriteErrors[0].Code\n\t\t\t\t\tassert.Equal(mt, errorModifiedID, gotCode, \"expected error code %v, got %v\", errorModifiedID, gotCode)\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\t\tmt.Run(\"error on nil filter\", func(mt *mtest.T) {\n\t\t\tmt.Parallel()\n\n\t\t\ttestCases := []struct {\n\t\t\t\tname        string\n\t\t\t\tmodel       mongo.WriteModel\n\t\t\t\terrorString string\n\t\t\t}{\n\t\t\t\t{\n\t\t\t\t\tname:        \"DeleteOne\",\n\t\t\t\t\tmodel:       mongo.NewDeleteOneModel(),\n\t\t\t\t\terrorString: \"delete filter cannot be nil\",\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tname:        \"DeleteMany\",\n\t\t\t\t\tmodel:       mongo.NewDeleteManyModel(),\n\t\t\t\t\terrorString: \"delete filter cannot be nil\",\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tname:        \"UpdateOne\",\n\t\t\t\t\tmodel:       mongo.NewUpdateOneModel().SetUpdate(bson.D{{\"$set\", bson.D{{\"x\", 1}}}}),\n\t\t\t\t\terrorString: \"update filter cannot be nil\",\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tname:        \"UpdateMany\",\n\t\t\t\t\tmodel:       mongo.NewUpdateManyModel().SetUpdate(bson.D{{\"$set\", bson.D{{\"x\", 1}}}}),\n\t\t\t\t\terrorString: \"update filter cannot be nil\",\n\t\t\t\t},\n\t\t\t}\n\t\t\tfor _, tc := range testCases {\n\t\t\t\ttc := tc\n\n\t\t\t\tmt.Run(tc.name, func(mt *mtest.T) {\n\t\t\t\t\tmt.Parallel()\n\n\t\t\t\t\t_, err := mt.Coll.BulkWrite(context.Background(), []mongo.WriteModel{tc.model})\n\t\t\t\t\tassert.EqualError(mt, err, tc.errorString)\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\t\tmt.Run(\"correct model in errors\", func(mt *mtest.T) {\n\t\t\tmodels := []mongo.WriteModel{\n\t\t\t\tmongo.NewUpdateOneModel().SetFilter(bson.M{}).SetUpdate(bson.M{\n\t\t\t\t\t\"$set\": bson.M{\n\t\t\t\t\t\t\"x\": 1,\n\t\t\t\t\t},\n\t\t\t\t}),\n\t\t\t\tmongo.NewInsertOneModel().SetDocument(bson.M{\n\t\t\t\t\t\"_id\": \"notduplicate\",\n\t\t\t\t}),\n\t\t\t\tmongo.NewInsertOneModel().SetDocument(bson.M{\n\t\t\t\t\t\"_id\": \"duplicate1\",\n\t\t\t\t}),\n\t\t\t\tmongo.NewInsertOneModel().SetDocument(bson.M{\n\t\t\t\t\t\"_id\": \"duplicate1\",\n\t\t\t\t}),\n\t\t\t\tmongo.NewInsertOneModel().SetDocument(bson.M{\n\t\t\t\t\t\"_id\": \"duplicate2\",\n\t\t\t\t}),\n\t\t\t\tmongo.NewInsertOneModel().SetDocument(bson.M{\n\t\t\t\t\t\"_id\": \"duplicate2\",\n\t\t\t\t}),\n\t\t\t}\n\n\t\t\t_, err := mt.Coll.BulkWrite(context.Background(), models)\n\t\t\tbwException, ok := err.(mongo.BulkWriteException)\n\t\t\tassert.True(mt, ok, \"expected error of type %T, got %T\", mongo.BulkWriteException{}, err)\n\n\t\t\texpectedModel := models[3]\n\t\t\tactualModel := bwException.WriteErrors[0].Request\n\t\t\tassert.Equal(mt, expectedModel, actualModel, \"expected model %v in BulkWriteException, got %v\",\n\t\t\t\texpectedModel, actualModel)\n\t\t})\n\t\tmt.RunOpts(\"unordered writeError index\", mtest.NewOptions().MaxServerVersion(\"5.0.7\"), func(mt *mtest.T) {\n\t\t\t// Deletes are not allowed on capped collections on MongoDB 5.0.6-. We use this\n\t\t\t// behavior to test the processing of write errors.\n\t\t\tcappedOpts := options.CreateCollection().SetCapped(true).SetSizeInBytes(64 * 1024)\n\t\t\tcapped := mt.CreateCollection(mtest.Collection{\n\t\t\t\tName:       \"deleteOne_capped\",\n\t\t\t\tCreateOpts: cappedOpts,\n\t\t\t}, true)\n\t\t\tmodels := []mongo.WriteModel{\n\t\t\t\tmongo.NewInsertOneModel().SetDocument(bson.D{{\"_id\", \"id1\"}}),\n\t\t\t\tmongo.NewInsertOneModel().SetDocument(bson.D{{\"_id\", \"id3\"}}),\n\t\t\t}\n\t\t\t_, err := capped.BulkWrite(context.Background(), models, options.BulkWrite())\n\t\t\tassert.Nil(t, err, \"BulkWrite error: %v\", err)\n\n\t\t\t// UpdateOne and ReplaceOne models are batched together, so they each appear once\n\t\t\tmodels = []mongo.WriteModel{\n\t\t\t\tmongo.NewDeleteOneModel().SetFilter(bson.D{{\"_id\", \"id0\"}}),\n\t\t\t\tmongo.NewDeleteManyModel().SetFilter(bson.D{{\"_id\", \"id0\"}}),\n\t\t\t\tmongo.NewUpdateOneModel().SetFilter(bson.D{{\"_id\", \"id3\"}}).SetUpdate(bson.D{{\"$set\", bson.D{{\"_id\", 3.14159}}}}),\n\t\t\t\tmongo.NewInsertOneModel().SetDocument(bson.D{{\"_id\", \"id1\"}}),\n\t\t\t\tmongo.NewDeleteManyModel().SetFilter(bson.D{{\"_id\", \"id0\"}}),\n\t\t\t\tmongo.NewUpdateManyModel().SetFilter(bson.D{{\"_id\", \"id3\"}}).SetUpdate(bson.D{{\"$set\", bson.D{{\"_id\", 3.14159}}}}),\n\t\t\t\tmongo.NewDeleteOneModel().SetFilter(bson.D{{\"_id\", \"id0\"}}),\n\t\t\t\tmongo.NewInsertOneModel().SetDocument(bson.D{{\"_id\", \"id1\"}}),\n\t\t\t\tmongo.NewReplaceOneModel().SetFilter(bson.D{{\"_id\", \"id3\"}}).SetReplacement(bson.D{{\"_id\", 3.14159}}),\n\t\t\t\tmongo.NewUpdateManyModel().SetFilter(bson.D{{\"_id\", \"id3\"}}).SetUpdate(bson.D{{\"$set\", bson.D{{\"_id\", 3.14159}}}}),\n\t\t\t}\n\t\t\t_, err = capped.BulkWrite(context.Background(), models, options.BulkWrite().SetOrdered(false))\n\t\t\tbwException, ok := err.(mongo.BulkWriteException)\n\t\t\tassert.True(mt, ok, \"expected error of type %T, got %T\", mongo.BulkWriteException{}, err)\n\n\t\t\tassert.Equal(mt, len(bwException.WriteErrors), 10, \"expected 10 writeErrors, got %v\", len(bwException.WriteErrors))\n\t\t\tfor _, writeErr := range bwException.WriteErrors {\n\t\t\t\tswitch writeErr.Request.(type) {\n\t\t\t\tcase *mongo.DeleteOneModel:\n\t\t\t\t\tassert.True(mt, writeErr.Index == 0 || writeErr.Index == 6,\n\t\t\t\t\t\t\"expected index 0 or 6, got %v\", writeErr.Index)\n\t\t\t\tcase *mongo.DeleteManyModel:\n\t\t\t\t\tassert.True(mt, writeErr.Index == 1 || writeErr.Index == 4,\n\t\t\t\t\t\t\"expected index 1 or 4, got %v\", writeErr.Index)\n\t\t\t\tcase *mongo.UpdateManyModel:\n\t\t\t\t\tassert.True(mt, writeErr.Index == 5 || writeErr.Index == 9,\n\t\t\t\t\t\t\"expected index 5 or 9, got %v\", writeErr.Index)\n\t\t\t\tcase *mongo.InsertOneModel:\n\t\t\t\t\tassert.True(mt, writeErr.Index == 3 || writeErr.Index == 7,\n\t\t\t\t\t\t\"expected index 3 or 7, got %v\", writeErr.Index)\n\t\t\t\tcase *mongo.UpdateOneModel:\n\t\t\t\t\tassert.Equal(mt, writeErr.Index, 2, \"expected index 2, got %v\", writeErr.Index)\n\t\t\t\tcase *mongo.ReplaceOneModel:\n\t\t\t\t\tassert.Equal(mt, writeErr.Index, 8, \"expected index 8, got %v\", writeErr.Index)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t\tmt.Run(\"unordered upsertID index\", func(mt *mtest.T) {\n\t\t\tid1 := \"id1\"\n\t\t\tid3 := \"id3\"\n\t\t\tmodels := []mongo.WriteModel{\n\t\t\t\tmongo.NewDeleteOneModel().SetFilter(bson.D{{\"_id\", \"id0\"}}),\n\t\t\t\tmongo.NewReplaceOneModel().SetFilter(bson.D{{\"_id\", id1}}).SetReplacement(bson.D{{\"_id\", id1}}).SetUpsert(true),\n\t\t\t\tmongo.NewDeleteOneModel().SetFilter(bson.D{{\"_id\", \"id2\"}}),\n\t\t\t\tmongo.NewReplaceOneModel().SetFilter(bson.D{{\"_id\", id3}}).SetReplacement(bson.D{{\"_id\", id3}}).SetUpsert(true),\n\t\t\t\tmongo.NewDeleteOneModel().SetFilter(bson.D{{\"_id\", \"id4\"}}),\n\t\t\t}\n\t\t\tres, err := mt.Coll.BulkWrite(context.Background(), models, options.BulkWrite().SetOrdered(false))\n\t\t\trequire.NoError(mt, err, \"bulkwrite error: %v\", err)\n\n\t\t\tassert.Equal(mt, len(res.UpsertedIDs), 2, \"expected 2 UpsertedIDs, got %v\", len(res.UpsertedIDs))\n\t\t\tassert.Equal(mt, res.UpsertedIDs[1].(string), id1, \"expected UpsertedIDs[1] to be %v, got %v\", id1, res.UpsertedIDs[1])\n\t\t\tassert.Equal(mt, res.UpsertedIDs[3].(string), id3, \"expected UpsertedIDs[3] to be %v, got %v\", id3, res.UpsertedIDs[3])\n\t\t})\n\t\tmt.RunOpts(\"insert and delete with batches\", mtest.NewOptions().ClientType(mtest.Mock), func(mt *mtest.T) {\n\t\t\t// grouped together because delete requires the documents to be inserted\n\t\t\tmaxBatchCount := int(drivertest.MockDescription.MaxBatchCount)\n\t\t\tnumDocs := maxBatchCount + 50\n\t\t\tvar insertModels []mongo.WriteModel\n\t\t\tvar deleteModels []mongo.WriteModel\n\t\t\tfor i := 0; i < numDocs; i++ {\n\t\t\t\td := bson.D{\n\t\t\t\t\t{\"a\", int32(i)},\n\t\t\t\t\t{\"b\", int32(i * 2)},\n\t\t\t\t\t{\"c\", int32(i * 3)},\n\t\t\t\t}\n\t\t\t\tinsertModels = append(insertModels, mongo.NewInsertOneModel().SetDocument(d))\n\t\t\t\tdeleteModels = append(deleteModels, mongo.NewDeleteOneModel().SetFilter(bson.D{}))\n\t\t\t}\n\n\t\t\t// Seed mock responses. Both insert and delete responses look like {ok: 1, n: <inserted/deleted count>}.\n\t\t\t// This loop only creates one set of responses, but the sets for insert and delete should be equivalent,\n\t\t\t// so we can duplicate the generated set before calling mt.AddMockResponses().\n\t\t\tvar responses []bson.D\n\t\t\tfor i := numDocs; i > 0; i -= maxBatchCount {\n\t\t\t\tcount := maxBatchCount\n\t\t\t\tif i < maxBatchCount {\n\t\t\t\t\tcount = i\n\t\t\t\t}\n\t\t\t\tres := mtest.CreateSuccessResponse(bson.E{\"n\", count})\n\t\t\t\tresponses = append(responses, res)\n\t\t\t}\n\t\t\tmt.AddMockResponses(append(responses, responses...)...)\n\n\t\t\tmt.ClearEvents()\n\t\t\tres, err := mt.Coll.BulkWrite(context.Background(), insertModels)\n\t\t\trequire.NoError(mt, err, \"BulkWrite error: %v\", err)\n\t\t\tassert.Equal(mt, int64(numDocs), res.InsertedCount, \"expected %v inserted documents, got %v\", numDocs, res.InsertedCount)\n\t\t\tmt.FilterStartedEvents(func(evt *event.CommandStartedEvent) bool {\n\t\t\t\treturn evt.CommandName == \"insert\"\n\t\t\t})\n\t\t\t// MaxWriteBatchSize changed between 3.4 and 3.6, so there isn't a given number of batches that this will be split into\n\t\t\tinserts := len(mt.GetAllStartedEvents())\n\t\t\tassert.True(mt, inserts > 1, \"expected multiple batches, got %v\", inserts)\n\n\t\t\tmt.ClearEvents()\n\t\t\tres, err = mt.Coll.BulkWrite(context.Background(), deleteModels)\n\t\t\trequire.NoError(mt, err, \"BulkWrite error: %v\", err)\n\t\t\tassert.Equal(mt, int64(numDocs), res.DeletedCount, \"expected %v deleted documents, got %v\", numDocs, res.DeletedCount)\n\t\t\tmt.FilterStartedEvents(func(evt *event.CommandStartedEvent) bool {\n\t\t\t\treturn evt.CommandName == \"delete\"\n\t\t\t})\n\t\t\t// MaxWriteBatchSize changed between 3.4 and 3.6, so there isn't a given number of batches that this will be split into\n\t\t\tdeletes := len(mt.GetAllStartedEvents())\n\t\t\tassert.True(mt, deletes > 1, \"expected multiple batches, got %v\", deletes)\n\t\t})\n\t\tmt.RunOpts(\"update with batches\", mtest.NewOptions().ClientType(mtest.Mock), func(mt *mtest.T) {\n\t\t\tmaxBatchCount := int(drivertest.MockDescription.MaxBatchCount)\n\t\t\tnumModels := maxBatchCount + 50\n\t\t\tvar models []mongo.WriteModel\n\n\t\t\t// It's significantly faster to upsert the first model and only modify the rest than to upsert all of them.\n\t\t\tfor i := 0; i < numModels-1; i++ {\n\t\t\t\tupdate := bson.D{\n\t\t\t\t\t{\"$set\", bson.D{\n\t\t\t\t\t\t{\"a\", int32(i + 1)},\n\t\t\t\t\t\t{\"b\", int32(i * 2)},\n\t\t\t\t\t\t{\"c\", int32(i * 3)},\n\t\t\t\t\t}},\n\t\t\t\t}\n\t\t\t\tmodel := mongo.NewUpdateOneModel().\n\t\t\t\t\tSetFilter(bson.D{{\"a\", int32(i)}}).\n\t\t\t\t\tSetUpdate(update).SetUpsert(true)\n\t\t\t\tmodels = append(models, model)\n\t\t\t}\n\t\t\t// Add one last upsert for second batch.\n\t\t\tmodels = append(models, mongo.NewUpdateOneModel().\n\t\t\t\tSetFilter(bson.D{{\"x\", int32(1)}}).\n\t\t\t\tSetUpdate(bson.D{{\"$set\", bson.D{{\"x\", int32(1)}}}}).\n\t\t\t\tSetUpsert(true),\n\t\t\t)\n\n\t\t\t// Seed mock responses for the BulkWrite.\n\t\t\t//\n\t\t\t// The response from the first batch should look like:\n\t\t\t// {ok: 1, n: 100000, nModified: 99999, upserted: [{index: 0, _id: <id>}]}\n\t\t\tfirstBatchUpserted := bson.A{bson.D{{\"index\", 0}, {\"_id\", bson.NewObjectID()}}}\n\t\t\tfirstBatchResponse := mtest.CreateSuccessResponse(\n\t\t\t\tbson.E{\"n\", 100000},\n\t\t\t\tbson.E{\"nModified\", 99999},\n\t\t\t\tbson.E{\"upserted\", firstBatchUpserted},\n\t\t\t)\n\t\t\t// The response from the second batch should look like:\n\t\t\t// {ok: 1, n: 50, nModified: 49, upserted: [{index: 49, _id: <id>}]}\n\t\t\tsecondBatchUpserted := bson.A{bson.D{{\"index\", 49}, {\"_id\", bson.NewObjectID()}}}\n\t\t\tsecondBatchResponse := mtest.CreateSuccessResponse(\n\t\t\t\tbson.E{\"n\", 50},\n\t\t\t\tbson.E{\"nModified\", 49},\n\t\t\t\tbson.E{\"upserted\", secondBatchUpserted},\n\t\t\t)\n\t\t\tmt.AddMockResponses([]bson.D{firstBatchResponse, secondBatchResponse}...)\n\n\t\t\tmt.ClearEvents()\n\t\t\tres, err := mt.Coll.BulkWrite(context.Background(), models)\n\t\t\trequire.NoError(mt, err, \"BulkWrite error: %v\", err)\n\n\t\t\tmt.FilterStartedEvents(func(evt *event.CommandStartedEvent) bool {\n\t\t\t\treturn evt.CommandName == \"update\"\n\t\t\t})\n\t\t\t// MaxWriteBatchSize changed between 3.4 and 3.6, so there isn't a given number of batches that\n\t\t\t// this will be split into.\n\t\t\tupdates := len(mt.GetAllStartedEvents())\n\t\t\tassert.True(mt, updates > 1, \"expected multiple batches, got %v\", updates)\n\n\t\t\tassert.Equal(mt, int64(numModels-2), res.ModifiedCount, \"expected %v modified documents, got %v\", numModels-2, res.ModifiedCount)\n\t\t\tassert.Equal(mt, int64(numModels-2), res.MatchedCount, \"expected %v matched documents, got %v\", numModels-2, res.ModifiedCount)\n\t\t\tassert.Equal(mt, int64(2), res.UpsertedCount, \"expected %v upserted documents, got %v\", 2, res.UpsertedCount)\n\t\t\tassert.Equal(mt, 2, len(res.UpsertedIDs), \"expected %v upserted ids, got %v\", 2, len(res.UpsertedIDs))\n\n\t\t\t// Check that IDs exist in result for upserted documents.\n\t\t\t_, ok := res.UpsertedIDs[0]\n\t\t\tassert.True(mt, ok, \"expected id at key 0\")\n\t\t\t_, ok = res.UpsertedIDs[int64(numModels-1)]\n\t\t\tassert.True(mt, ok, \"expected id at key %v\", numModels-1)\n\t\t})\n\t\tmt.RunOpts(\"map hint\", noClientOpts, func(mt *mtest.T) {\n\t\t\tfilter := bson.D{{\"_id\", \"foo\"}}\n\t\t\ttestCases := []struct {\n\t\t\t\tname     string\n\t\t\t\tmodels   []mongo.WriteModel\n\t\t\t\terrParam string\n\t\t\t}{\n\t\t\t\t{\"updateOne/multi key\", []mongo.WriteModel{mongo.NewUpdateOneModel().SetFilter(filter).SetUpdate(bson.D{{\"$set\", bson.D{{\"x\", \"fa\"}}}}).SetHint(bson.M{\"_id\": 1, \"x\": 1})}, \"hint\"},\n\t\t\t\t{\"updateMany/multi key\", []mongo.WriteModel{mongo.NewUpdateManyModel().SetFilter(filter).SetUpdate(bson.D{{\"$set\", bson.D{{\"x\", \"fa\"}}}}).SetHint(bson.M{\"_id\": 1, \"x\": 1})}, \"hint\"},\n\t\t\t\t{\"replaceOne/multi key\", []mongo.WriteModel{mongo.NewReplaceOneModel().SetFilter(filter).SetReplacement(bson.D{{\"x\", \"bar\"}}).SetHint(bson.M{\"_id\": 1, \"x\": 1})}, \"hint\"},\n\t\t\t\t{\"deleteOne/multi key\", []mongo.WriteModel{mongo.NewDeleteOneModel().SetFilter(filter).SetHint(bson.M{\"_id\": 1, \"x\": 1})}, \"hint\"},\n\t\t\t\t{\"deleteMany/multi key\", []mongo.WriteModel{mongo.NewDeleteManyModel().SetFilter(filter).SetHint(bson.M{\"_id\": 1, \"x\": 1})}, \"hint\"},\n\n\t\t\t\t{\"updateOne/one key\", []mongo.WriteModel{mongo.NewUpdateOneModel().SetFilter(filter).SetUpdate(bson.D{{\"$set\", bson.D{{\"x\", \"fa\"}}}}).SetHint(bson.M{\"_id\": 1})}, \"\"},\n\t\t\t\t{\"updateMany/one key\", []mongo.WriteModel{mongo.NewUpdateManyModel().SetFilter(filter).SetUpdate(bson.D{{\"$set\", bson.D{{\"x\", \"fa\"}}}}).SetHint(bson.M{\"_id\": 1})}, \"\"},\n\t\t\t\t{\"replaceOne/one key\", []mongo.WriteModel{mongo.NewReplaceOneModel().SetFilter(filter).SetReplacement(bson.D{{\"x\", \"bar\"}}).SetHint(bson.M{\"_id\": 1})}, \"\"},\n\t\t\t\t{\"deleteOne/one key\", []mongo.WriteModel{mongo.NewDeleteOneModel().SetFilter(filter).SetHint(bson.M{\"_id\": 1})}, \"\"},\n\t\t\t\t{\"deleteMany/one key\", []mongo.WriteModel{mongo.NewDeleteManyModel().SetFilter(filter).SetHint(bson.M{\"_id\": 1})}, \"\"},\n\t\t\t}\n\t\t\tfor _, tc := range testCases {\n\t\t\t\tmt.RunOpts(tc.name, mtest.NewOptions().MinServerVersion(\"4.4\"), func(mt *mtest.T) {\n\t\t\t\t\t_, err := mt.Coll.InsertOne(context.Background(), filter)\n\t\t\t\t\trequire.NoError(mt, err, \"InsertOne error: %v\", err)\n\t\t\t\t\t_, err = mt.Coll.BulkWrite(context.Background(), tc.models)\n\t\t\t\t\tif tc.errParam == \"\" {\n\t\t\t\t\t\trequire.NoError(mt, err, \"expected nil error, got %v\", err)\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t\texpErr := mongo.ErrMapForOrderedArgument{tc.errParam}\n\t\t\t\t\tassert.Equal(mt, expErr, err, \"expected error %v, got %v\", expErr, err)\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\t})\n}\n\nfunc TestBypassEmptyTsReplacement(t *testing.T) {\n\tmt := mtest.New(t, mtest.NewOptions().CreateClient(false).MinServerVersion(\"5.0\"))\n\n\tmarshalValue := func(val interface{}) bson.RawValue {\n\t\tt.Helper()\n\n\t\tvalType, data, err := bson.MarshalValue(val)\n\t\trequire.NoError(t, err, \"MarshalValue error: %v\", err)\n\t\treturn bson.RawValue{\n\t\t\tType:  valType,\n\t\t\tValue: data,\n\t\t}\n\t}\n\n\tmt.Run(\"insert one\", func(mt *mtest.T) {\n\t\tdoc := bson.D{{\"x\", 42}}\n\n\t\tnewOpts := func(option bson.D) *options.InsertOneOptionsBuilder {\n\t\t\topts := options.InsertOne()\n\t\t\terr := xoptions.SetInternalInsertOneOptions(opts, \"addCommandFields\", option)\n\t\t\trequire.NoError(mt, err, \"unexpected error: %v\", err)\n\t\t\treturn opts\n\t\t}\n\n\t\ttestCases := []struct {\n\t\t\tname     string\n\t\t\topts     *options.InsertOneOptionsBuilder\n\t\t\texpected bson.RawValue\n\t\t}{\n\t\t\t{\n\t\t\t\tname:     \"empty\",\n\t\t\t\topts:     nil,\n\t\t\t\texpected: bson.RawValue{},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname:     \"false\",\n\t\t\t\topts:     newOpts(bson.D{{\"bypassEmptyTsReplacement\", false}}),\n\t\t\t\texpected: marshalValue(false),\n\t\t\t},\n\t\t\t{\n\t\t\t\tname:     \"true\",\n\t\t\t\topts:     newOpts(bson.D{{\"bypassEmptyTsReplacement\", true}}),\n\t\t\t\texpected: marshalValue(true),\n\t\t\t},\n\t\t}\n\t\tfor _, tc := range testCases {\n\t\t\tmt.Run(tc.name, func(mt *mtest.T) {\n\t\t\t\t_, err := mt.Coll.InsertOne(context.Background(), doc, tc.opts)\n\t\t\t\trequire.NoError(mt, err, \"InsertOne error: %v\", err)\n\t\t\t\tevt := mt.GetStartedEvent()\n\t\t\t\tval := evt.Command.Lookup(\"bypassEmptyTsReplacement\")\n\t\t\t\tassert.Equal(mt, tc.expected, val, \"expected bypassEmptyTsReplacement to be %s in %v\", tc.expected.String())\n\t\t\t})\n\t\t}\n\t})\n\tmt.Run(\"insert many\", func(mt *mtest.T) {\n\t\tdocs := []interface{}{\n\t\t\tbson.D{{\"x\", 42}},\n\t\t\tbson.D{{\"y\", \"foo\"}},\n\t\t}\n\n\t\tnewOpts := func(option bson.D) *options.InsertManyOptionsBuilder {\n\t\t\topts := options.InsertMany()\n\t\t\terr := xoptions.SetInternalInsertManyOptions(opts, \"addCommandFields\", option)\n\t\t\trequire.NoError(mt, err, \"unexpected error: %v\", err)\n\t\t\treturn opts\n\t\t}\n\n\t\ttestCases := []struct {\n\t\t\tname     string\n\t\t\topts     *options.InsertManyOptionsBuilder\n\t\t\texpected bson.RawValue\n\t\t}{\n\t\t\t{\n\t\t\t\tname:     \"empty\",\n\t\t\t\topts:     nil,\n\t\t\t\texpected: bson.RawValue{},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname:     \"false\",\n\t\t\t\topts:     newOpts(bson.D{{\"bypassEmptyTsReplacement\", false}}),\n\t\t\t\texpected: marshalValue(false),\n\t\t\t},\n\t\t\t{\n\t\t\t\tname:     \"true\",\n\t\t\t\topts:     newOpts(bson.D{{\"bypassEmptyTsReplacement\", true}}),\n\t\t\t\texpected: marshalValue(true),\n\t\t\t},\n\t\t}\n\t\tfor _, tc := range testCases {\n\t\t\tmt.Run(tc.name, func(mt *mtest.T) {\n\t\t\t\t_, err := mt.Coll.InsertMany(context.Background(), docs, tc.opts)\n\t\t\t\trequire.NoError(mt, err, \"InsertMany error: %v\", err)\n\t\t\t\tevt := mt.GetStartedEvent()\n\t\t\t\tval := evt.Command.Lookup(\"bypassEmptyTsReplacement\")\n\t\t\t\tassert.Equal(mt, tc.expected, val, \"expected bypassEmptyTsReplacement to be %s\", tc.expected.String())\n\t\t\t})\n\t\t}\n\t})\n\tmt.Run(\"update one\", func(mt *mtest.T) {\n\t\tfilter := bson.D{{\"x\", 42}}\n\t\tupdate := bson.D{{\"$inc\", bson.D{{\"x\", 1}}}}\n\n\t\tnewOpts := func(option bson.D) *options.UpdateOneOptionsBuilder {\n\t\t\topts := options.UpdateOne()\n\t\t\terr := xoptions.SetInternalUpdateOneOptions(opts, \"addCommandFields\", option)\n\t\t\trequire.NoError(mt, err, \"unexpected error: %v\", err)\n\t\t\treturn opts\n\t\t}\n\n\t\ttestCases := []struct {\n\t\t\tname     string\n\t\t\topts     *options.UpdateOneOptionsBuilder\n\t\t\texpected bson.RawValue\n\t\t}{\n\t\t\t{\n\t\t\t\tname:     \"empty\",\n\t\t\t\topts:     nil,\n\t\t\t\texpected: bson.RawValue{},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname:     \"false\",\n\t\t\t\topts:     newOpts(bson.D{{\"bypassEmptyTsReplacement\", false}}),\n\t\t\t\texpected: marshalValue(false),\n\t\t\t},\n\t\t\t{\n\t\t\t\tname:     \"true\",\n\t\t\t\topts:     newOpts(bson.D{{\"bypassEmptyTsReplacement\", true}}),\n\t\t\t\texpected: marshalValue(true),\n\t\t\t},\n\t\t}\n\t\tfor _, tc := range testCases {\n\t\t\tmt.Run(tc.name, func(mt *mtest.T) {\n\t\t\t\t_, err := mt.Coll.UpdateOne(context.Background(), filter, update, tc.opts)\n\t\t\t\trequire.NoError(mt, err, \"UpdateOne error: %v\", err)\n\t\t\t\tevt := mt.GetStartedEvent()\n\t\t\t\tval := evt.Command.Lookup(\"bypassEmptyTsReplacement\")\n\t\t\t\tassert.Equal(mt, tc.expected, val, \"expected bypassEmptyTsReplacement to be %s\", tc.expected.String())\n\t\t\t})\n\t\t}\n\t})\n\tmt.Run(\"update many\", func(mt *mtest.T) {\n\t\tfilter := bson.D{{\"x\", 42}}\n\t\tupdate := bson.D{{\"$inc\", bson.D{{\"x\", 1}}}}\n\n\t\tnewOpts := func(option bson.D) *options.UpdateManyOptionsBuilder {\n\t\t\topts := options.UpdateMany()\n\t\t\terr := xoptions.SetInternalUpdateManyOptions(opts, \"addCommandFields\", option)\n\t\t\trequire.NoError(mt, err, \"unexpected error: %v\", err)\n\t\t\treturn opts\n\t\t}\n\n\t\ttestCases := []struct {\n\t\t\tname     string\n\t\t\topts     *options.UpdateManyOptionsBuilder\n\t\t\texpected bson.RawValue\n\t\t}{\n\t\t\t{\n\t\t\t\tname:     \"empty\",\n\t\t\t\topts:     nil,\n\t\t\t\texpected: bson.RawValue{},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname:     \"false\",\n\t\t\t\topts:     newOpts(bson.D{{\"bypassEmptyTsReplacement\", false}}),\n\t\t\t\texpected: marshalValue(false),\n\t\t\t},\n\t\t\t{\n\t\t\t\tname:     \"true\",\n\t\t\t\topts:     newOpts(bson.D{{\"bypassEmptyTsReplacement\", true}}),\n\t\t\t\texpected: marshalValue(true),\n\t\t\t},\n\t\t}\n\t\tfor _, tc := range testCases {\n\t\t\tmt.Run(tc.name, func(mt *mtest.T) {\n\t\t\t\t_, err := mt.Coll.UpdateMany(context.Background(), filter, update, tc.opts)\n\t\t\t\trequire.NoError(mt, err, \"UpdateMany error: %v\", err)\n\t\t\t\tevt := mt.GetStartedEvent()\n\t\t\t\tval := evt.Command.Lookup(\"bypassEmptyTsReplacement\")\n\t\t\t\tassert.Equal(mt, tc.expected, val, \"expected bypassEmptyTsReplacement to be %s\", tc.expected.String())\n\t\t\t})\n\t\t}\n\t})\n\tmt.Run(\"replace one\", func(mt *mtest.T) {\n\t\tfilter := bson.D{{\"x\", 42}}\n\t\treplacement := bson.D{{\"y\", \"foo\"}}\n\n\t\tnewOpts := func(option bson.D) *options.ReplaceOptionsBuilder {\n\t\t\topts := options.Replace()\n\t\t\terr := xoptions.SetInternalReplaceOptions(opts, \"addCommandFields\", option)\n\t\t\trequire.NoError(mt, err, \"unexpected error: %v\", err)\n\t\t\treturn opts\n\t\t}\n\n\t\ttestCases := []struct {\n\t\t\tname     string\n\t\t\topts     *options.ReplaceOptionsBuilder\n\t\t\texpected bson.RawValue\n\t\t}{\n\t\t\t{\n\t\t\t\tname:     \"empty\",\n\t\t\t\topts:     nil,\n\t\t\t\texpected: bson.RawValue{},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname:     \"false\",\n\t\t\t\topts:     newOpts(bson.D{{\"bypassEmptyTsReplacement\", false}}),\n\t\t\t\texpected: marshalValue(false),\n\t\t\t},\n\t\t\t{\n\t\t\t\tname:     \"true\",\n\t\t\t\topts:     newOpts(bson.D{{\"bypassEmptyTsReplacement\", true}}),\n\t\t\t\texpected: marshalValue(true),\n\t\t\t},\n\t\t}\n\t\tfor _, tc := range testCases {\n\t\t\tmt.Run(tc.name, func(mt *mtest.T) {\n\t\t\t\t_, err := mt.Coll.ReplaceOne(context.Background(), filter, replacement, tc.opts)\n\t\t\t\trequire.NoError(mt, err, \"ReplaceOne error: %v\", err)\n\t\t\t\tevt := mt.GetStartedEvent()\n\t\t\t\tval := evt.Command.Lookup(\"bypassEmptyTsReplacement\")\n\t\t\t\tassert.Equal(mt, tc.expected, val, \"expected bypassEmptyTsReplacement to be %s\", tc.expected.String())\n\t\t\t})\n\t\t}\n\t})\n\tmt.Run(\"find one and update\", func(mt *mtest.T) {\n\t\tfilter := bson.D{{\"x\", 1}}\n\t\tupdate := bson.D{{\"$inc\", bson.D{{\"x\", 1}}}}\n\n\t\tnewOpts := func(option bson.D) *options.FindOneAndUpdateOptionsBuilder {\n\t\t\topts := options.FindOneAndUpdate()\n\t\t\terr := xoptions.SetInternalFindOneAndUpdateOptions(opts, \"addCommandFields\", option)\n\t\t\trequire.NoError(mt, err, \"unexpected error: %v\", err)\n\t\t\treturn opts\n\t\t}\n\n\t\ttestCases := []struct {\n\t\t\tname     string\n\t\t\topts     *options.FindOneAndUpdateOptionsBuilder\n\t\t\texpected bson.RawValue\n\t\t}{\n\t\t\t{\n\t\t\t\tname:     \"empty\",\n\t\t\t\topts:     nil,\n\t\t\t\texpected: bson.RawValue{},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname:     \"false\",\n\t\t\t\topts:     newOpts(bson.D{{\"bypassEmptyTsReplacement\", false}}),\n\t\t\t\texpected: marshalValue(false),\n\t\t\t},\n\t\t\t{\n\t\t\t\tname:     \"true\",\n\t\t\t\topts:     newOpts(bson.D{{\"bypassEmptyTsReplacement\", true}}),\n\t\t\t\texpected: marshalValue(true),\n\t\t\t},\n\t\t}\n\t\tfor _, tc := range testCases {\n\t\t\tmt.Run(tc.name, func(mt *mtest.T) {\n\t\t\t\tinitCollection(mt, mt.Coll)\n\t\t\t\tmt.ClearEvents()\n\n\t\t\t\t_, err := mt.Coll.FindOneAndUpdate(context.Background(), filter, update, tc.opts).Raw()\n\t\t\t\trequire.NoError(mt, err, \"FindOneAndUpdate error: %v\", err)\n\t\t\t\tevt := mt.GetStartedEvent()\n\t\t\t\tval := evt.Command.Lookup(\"bypassEmptyTsReplacement\")\n\t\t\t\tassert.Equal(mt, tc.expected, val, \"expected bypassEmptyTsReplacement to be %s\", tc.expected.String())\n\t\t\t})\n\t\t}\n\t})\n\tmt.Run(\"find one and replace\", func(mt *mtest.T) {\n\t\tfilter := bson.D{{\"x\", 1}}\n\t\treplacement := bson.D{{\"y\", \"foo\"}}\n\n\t\tnewOpts := func(option bson.D) *options.FindOneAndReplaceOptionsBuilder {\n\t\t\topts := options.FindOneAndReplace()\n\t\t\terr := xoptions.SetInternalFindOneAndReplaceOptions(opts, \"addCommandFields\", option)\n\t\t\trequire.NoError(mt, err, \"unexpected error: %v\", err)\n\t\t\treturn opts\n\t\t}\n\n\t\ttestCases := []struct {\n\t\t\tname     string\n\t\t\topts     *options.FindOneAndReplaceOptionsBuilder\n\t\t\texpected bson.RawValue\n\t\t}{\n\t\t\t{\n\t\t\t\tname:     \"empty\",\n\t\t\t\topts:     nil,\n\t\t\t\texpected: bson.RawValue{},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname:     \"false\",\n\t\t\t\topts:     newOpts(bson.D{{\"bypassEmptyTsReplacement\", false}}),\n\t\t\t\texpected: marshalValue(false),\n\t\t\t},\n\t\t\t{\n\t\t\t\tname:     \"true\",\n\t\t\t\topts:     newOpts(bson.D{{\"bypassEmptyTsReplacement\", true}}),\n\t\t\t\texpected: marshalValue(true),\n\t\t\t},\n\t\t}\n\t\tfor _, tc := range testCases {\n\t\t\tmt.Run(tc.name, func(mt *mtest.T) {\n\t\t\t\tinitCollection(mt, mt.Coll)\n\t\t\t\tmt.ClearEvents()\n\n\t\t\t\t_, err := mt.Coll.FindOneAndReplace(context.Background(), filter, replacement, tc.opts).Raw()\n\t\t\t\trequire.NoError(mt, err, \"FindOneAndReplace error: %v\", err)\n\t\t\t\tevt := mt.GetStartedEvent()\n\t\t\t\tval := evt.Command.Lookup(\"bypassEmptyTsReplacement\")\n\t\t\t\tassert.Equal(mt, tc.expected, val, \"expected bypassEmptyTsReplacement to be %s\", tc.expected.String())\n\t\t\t})\n\t\t}\n\t})\n\tmt.Run(\"bulk write\", func(mt *mtest.T) {\n\t\tnewOpts := func(option bson.D) *options.BulkWriteOptionsBuilder {\n\t\t\topts := options.BulkWrite()\n\t\t\terr := xoptions.SetInternalBulkWriteOptions(opts, \"addCommandFields\", option)\n\t\t\trequire.NoError(mt, err, \"unexpected error: %v\", err)\n\t\t\treturn opts\n\t\t}\n\n\t\tmodels := []struct {\n\t\t\tname  string\n\t\t\tmodel mongo.WriteModel\n\t\t}{\n\t\t\t{\n\t\t\t\tname:  \"insert one\",\n\t\t\t\tmodel: mongo.NewInsertOneModel().SetDocument(bson.D{{\"_id\", \"id1\"}}),\n\t\t\t},\n\t\t\t{\n\t\t\t\tname:  \"update one\",\n\t\t\t\tmodel: mongo.NewUpdateOneModel().SetFilter(bson.D{{\"_id\", \"id3\"}}).SetUpdate(bson.D{{\"$set\", bson.D{{\"_id\", 3.14159}}}}),\n\t\t\t},\n\t\t\t{\n\t\t\t\tname:  \"update many\",\n\t\t\t\tmodel: mongo.NewUpdateManyModel().SetFilter(bson.D{{\"_id\", \"id3\"}}).SetUpdate(bson.D{{\"$set\", bson.D{{\"_id\", 3.14159}}}}),\n\t\t\t},\n\t\t\t{\n\t\t\t\tname:  \"replace one\",\n\t\t\t\tmodel: mongo.NewReplaceOneModel().SetFilter(bson.D{{\"_id\", \"id3\"}}).SetReplacement(bson.D{{\"_id\", 3.14159}}),\n\t\t\t},\n\t\t}\n\n\t\ttestCases := []struct {\n\t\t\tname     string\n\t\t\topts     *options.BulkWriteOptionsBuilder\n\t\t\texpected bson.RawValue\n\t\t}{\n\t\t\t{\n\t\t\t\tname:     \"empty\",\n\t\t\t\topts:     options.BulkWrite(),\n\t\t\t\texpected: bson.RawValue{},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname:     \"false\",\n\t\t\t\topts:     newOpts(bson.D{{\"bypassEmptyTsReplacement\", false}}),\n\t\t\t\texpected: marshalValue(false),\n\t\t\t},\n\t\t\t{\n\t\t\t\tname:     \"true\",\n\t\t\t\topts:     newOpts(bson.D{{\"bypassEmptyTsReplacement\", true}}),\n\t\t\t\texpected: marshalValue(true),\n\t\t\t},\n\t\t}\n\t\tfor _, m := range models {\n\t\t\tfor _, tc := range testCases {\n\t\t\t\tmt.Run(fmt.Sprintf(\"%s %s\", m.name, tc.name), func(mt *mtest.T) {\n\t\t\t\t\t_, err := mt.Coll.BulkWrite(context.Background(), []mongo.WriteModel{m.model}, tc.opts)\n\t\t\t\t\trequire.NoError(mt, err, \"BulkWrite error: %v\", err)\n\t\t\t\t\tevt := mt.GetStartedEvent()\n\t\t\t\t\tval := evt.Command.Lookup(\"bypassEmptyTsReplacement\")\n\t\t\t\t\tassert.Equal(mt, tc.expected, val, \"expected bypassEmptyTsReplacement to be %s\", tc.expected.String())\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc initCollection(tb testing.TB, coll *mongo.Collection) {\n\ttb.Helper()\n\n\tvar docs []any\n\tfor i := 1; i <= 5; i++ {\n\t\tdocs = append(docs, bson.D{{\"x\", int32(i)}})\n\t}\n\n\t_, err := coll.InsertMany(context.Background(), docs)\n\tassert.NoError(tb, err, \"InsertMany error for initial data: %v\", err)\n}\n\nfunc testAggregateWithOptions(mt *mtest.T, createIndex bool, opts options.Lister[options.AggregateOptions]) {\n\tmt.Helper()\n\tinitCollection(mt, mt.Coll)\n\n\tif createIndex {\n\t\tindexView := mt.Coll.Indexes()\n\t\t_, err := indexView.CreateOne(context.Background(), mongo.IndexModel{\n\t\t\tKeys: bson.D{{\"x\", 1}},\n\t\t})\n\t\trequire.NoError(mt, err, \"CreateOne error: %v\", err)\n\t}\n\n\tpipeline := mongo.Pipeline{\n\t\t{{\"$match\", bson.D{{\"x\", bson.D{{\"$gte\", 2}}}}}},\n\t\t{{\"$project\", bson.D{{\"_id\", 0}, {\"x\", 1}}}},\n\t\t{{\"$sort\", bson.D{{\"x\", 1}}}},\n\t}\n\n\tcursor, err := mt.Coll.Aggregate(context.Background(), pipeline, opts)\n\trequire.NoError(mt, err, \"Aggregate error: %v\", err)\n\n\tfor i := 2; i < 5; i++ {\n\t\tassert.True(mt, cursor.Next(context.Background()), \"expected Next true, got false\")\n\t\telems, _ := cursor.Current.Elements()\n\t\tassert.Equal(mt, 1, len(elems), \"expected doc with 1 element, got %v\", cursor.Current)\n\n\t\tnum, err := cursor.Current.LookupErr(\"x\")\n\t\trequire.NoError(mt, err, \"x not found in document %v\", cursor.Current)\n\t\tassert.Equal(mt, bson.TypeInt32, num.Type, \"expected 'x' type %v, got %v\", bson.TypeInt32, num.Type)\n\t\tassert.Equal(mt, int32(i), num.Int32(), \"expected x value %v, got %v\", i, num.Int32())\n\t}\n}\n\nfunc create16MBDocument(mt *mtest.T) bsoncore.Document {\n\t// 4 bytes = document length\n\t// 1 byte = element type (ObjectID = \\x07)\n\t// 4 bytes = key name (\"_id\" + \\x00)\n\t// 12 bytes = ObjectID value\n\t// 1 byte = element type (string = \\x02)\n\t// 4 bytes = key name (\"key\" + \\x00)\n\t// 4 bytes = string length\n\t// X bytes = string of length X bytes\n\t// 1 byte = \\x00\n\t// 1 byte = \\x00\n\t//\n\t// Therefore the string length should be: 1024*1024*16 - 32\n\n\ttargetDocSize := 1024 * 1024 * 16\n\tstrSize := targetDocSize - 32\n\tvar b strings.Builder\n\tb.Grow(strSize)\n\tfor i := 0; i < strSize; i++ {\n\t\tb.WriteByte('A')\n\t}\n\n\tidx, doc := bsoncore.AppendDocumentStart(nil)\n\tdoc = bsoncore.AppendObjectIDElement(doc, \"_id\", bson.NewObjectID())\n\tdoc = bsoncore.AppendStringElement(doc, \"key\", b.String())\n\tdoc, _ = bsoncore.AppendDocumentEnd(doc, idx)\n\tassert.Equal(mt, targetDocSize, len(doc), \"expected document length %v, got %v\", targetDocSize, len(doc))\n\treturn doc\n}\n\n// This is a helper function to ensure that sending getMore commands for a cursor results in command monitoring events\n// being published. The cursorFn parameter should be a function that yields a cursor which is open on the server and\n// requires at least one getMore to be fully iterated.\nfunc assertGetMoreCommandsAreMonitored(mt *mtest.T, cmdName string, cursorFn func() (*mongo.Cursor, error)) {\n\tmt.Helper()\n\tmt.ClearEvents()\n\n\tcursor, err := cursorFn()\n\trequire.NoError(mt, err, \"error creating cursor: %v\", err)\n\tvar docs []bson.D\n\terr = cursor.All(context.Background(), &docs)\n\trequire.NoError(mt, err, \"All error: %v\", err)\n\n\t// Only assert that the initial command and at least one getMore were sent. The exact number of getMore's required\n\t// is not important.\n\tevt := mt.GetStartedEvent()\n\tassert.Equal(mt, cmdName, evt.CommandName, \"expected command %q, got %q\", cmdName, evt.CommandName)\n\tevt = mt.GetStartedEvent()\n\tassert.Equal(mt, \"getMore\", evt.CommandName, \"expected command 'getMore', got %q\", evt.CommandName)\n}\n\n// This is a helper function to ensure that sending killCursors commands for a cursor results in command monitoring\n// events being published. The cursorFn parameter should be a function that yields a cursor which is open on the server.\nfunc assertKillCursorsCommandsAreMonitored(mt *mtest.T, cmdName string, cursorFn func() (*mongo.Cursor, error)) {\n\tmt.Helper()\n\tmt.ClearEvents()\n\n\tcursor, err := cursorFn()\n\trequire.NoError(mt, err, \"error creating cursor: %v\", err)\n\terr = cursor.Close(context.Background())\n\trequire.NoError(mt, err, \"Close error: %v\", err)\n\n\tevt := mt.GetStartedEvent()\n\tassert.Equal(mt, cmdName, evt.CommandName, \"expected command %q, got %q\", cmdName, evt.CommandName)\n\tevt = mt.GetStartedEvent()\n\tassert.Equal(mt, \"killCursors\", evt.CommandName, \"expected command 'killCursors', got %q\", evt.CommandName)\n}\n"
  },
  {
    "path": "internal/integration/crud_helpers_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage integration\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"fmt\"\n\t\"math\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/bsonutil\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/integration/mtest\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/integration/unified\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/integtest\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/mongoutil\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/require\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/readconcern\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/readpref\"\n)\n\n// Helper functions to execute and verify results from CRUD methods.\n\nvar (\n\temptyDoc                        = []byte{5, 0, 0, 0, 0}\n\terrorCommandNotFound      int32 = 59\n\terrorLockTimeout          int32 = 24\n\terrorCommandNotSupported  int32 = 115\n\tkillAllSessionsErrorCodes       = map[int32]struct{}{\n\t\terrorInterrupted:         {}, // the command interrupts itself\n\t\terrorCommandNotFound:     {}, // the killAllSessions command does not exist on server versions < 3.6\n\t\terrorCommandNotSupported: {}, // the command is not supported on Atlas Data Lake\n\t}\n)\n\n// create an update document or pipeline from a bson.RawValue\nfunc createUpdate(mt *mtest.T, updateVal bson.RawValue) any {\n\tswitch updateVal.Type {\n\tcase bson.TypeEmbeddedDocument:\n\t\treturn updateVal.Document()\n\tcase bson.TypeArray:\n\t\tvar updateDocs []bson.Raw\n\t\tdocs, _ := updateVal.Array().Values()\n\t\tfor _, doc := range docs {\n\t\t\tupdateDocs = append(updateDocs, doc.Document())\n\t\t}\n\n\t\treturn updateDocs\n\tdefault:\n\t\tmt.Fatalf(\"unrecognized update type: %v\", updateVal.Type)\n\t}\n\n\treturn nil\n}\n\n// create a hint string or document from a bson.RawValue\nfunc createHint(mt *mtest.T, val bson.RawValue) any {\n\tmt.Helper()\n\n\tvar hint any\n\tswitch val.Type {\n\tcase bson.TypeString:\n\t\thint = val.StringValue()\n\tcase bson.TypeEmbeddedDocument:\n\t\thint = val.Document()\n\tdefault:\n\t\tmt.Fatalf(\"unrecognized hint value type: %s\\n\", val.Type)\n\t}\n\treturn hint\n}\n\n// returns true if err is a mongo.CommandError containing a code that is expected from a killAllSessions command.\nfunc isExpectedKillAllSessionsError(err error) bool {\n\tcmdErr, ok := err.(mongo.CommandError)\n\tif !ok {\n\t\treturn false\n\t}\n\n\t_, ok = killAllSessionsErrorCodes[cmdErr.Code]\n\t// for SERVER-54216 on atlas\n\tatlasUnauthorized := strings.Contains(err.Error(), \"(AtlasError) (Unauthorized)\")\n\treturn ok || atlasUnauthorized\n}\n\n// kill all open sessions on the server. This function uses mt.GlobalClient() because killAllSessions is not allowed\n// for clients configured with specific options (e.g. in-use encryption).\nfunc killSessions(mt *mtest.T) {\n\tmt.Helper()\n\n\tcmd := bson.D{\n\t\t{\"killAllSessions\", bson.A{}},\n\t}\n\trunCmdOpts := options.RunCmd().SetReadPreference(mtest.PrimaryRp)\n\n\t// killAllSessions has to be run against each mongos in a sharded cluster, so we use the runCommandOnAllServers\n\t// helper.\n\terr := runCommandOnAllServers(func(client *mongo.Client) error {\n\t\treturn client.Database(\"admin\").RunCommand(context.Background(), cmd, runCmdOpts).Err()\n\t})\n\n\tif err == nil {\n\t\treturn\n\t}\n\tif !isExpectedKillAllSessionsError(err) {\n\t\tmt.Fatalf(\"killAllSessions error: %v\", err)\n\t}\n}\n\n// Utility function to run a command on all servers. For standalones, the command is run against the one server. For\n// replica sets, the command is run against the primary. sharded clusters, the command is run against each mongos.\nfunc runCommandOnAllServers(commandFn func(client *mongo.Client) error) error {\n\topts := options.Client().ApplyURI(mtest.ClusterURI())\n\tintegtest.AddTestServerAPIVersion(opts)\n\n\tif mtest.ClusterTopologyKind() != mtest.Sharded {\n\t\tclient, err := mongo.Connect(opts)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"error creating replica set client: %w\", err)\n\t\t}\n\t\tdefer func() { _ = client.Disconnect(context.Background()) }()\n\n\t\treturn commandFn(client)\n\t}\n\n\thosts, err := mongoutil.HostsFromURI(mtest.ClusterURI())\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to construct options from builder: %v\", err)\n\t}\n\n\tfor _, host := range hosts {\n\t\tshardClient, err := mongo.Connect(opts.SetHosts([]string{host}))\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"error creating client for mongos %v: %w\", host, err)\n\t\t}\n\n\t\terr = commandFn(shardClient)\n\t\t_ = shardClient.Disconnect(context.Background())\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// aggregator is an interface used to run collection and database-level aggregations\ntype aggregator interface {\n\tAggregate(context.Context, any, ...options.Lister[options.AggregateOptions]) (*mongo.Cursor, error)\n}\n\n// watcher is an interface used to create client, db, and collection-level change streams\ntype watcher interface {\n\tWatch(context.Context, any, ...options.Lister[options.ChangeStreamOptions]) (*mongo.ChangeStream, error)\n}\n\nfunc executeAggregate(mt *mtest.T, agg aggregator, sess *mongo.Session, args bson.Raw) (*mongo.Cursor, error) {\n\tmt.Helper()\n\n\tvar pipeline []any\n\topts := options.Aggregate()\n\n\telems, _ := args.Elements()\n\tfor _, elem := range elems {\n\t\tkey := elem.Key()\n\t\tval := elem.Value()\n\n\t\tswitch key {\n\t\tcase \"pipeline\":\n\t\t\tpipeline = bsonutil.RawToInterfaces(bsonutil.RawArrayToDocuments(val.Array())...)\n\t\tcase \"batchSize\":\n\t\t\topts.SetBatchSize(val.Int32())\n\t\tcase \"collation\":\n\t\t\topts.SetCollation(createCollation(mt, val.Document()))\n\t\tcase \"allowDiskUse\":\n\t\t\topts.SetAllowDiskUse(val.Boolean())\n\t\tcase \"session\":\n\t\tdefault:\n\t\t\tmt.Fatalf(\"unrecognized aggregate option: %v\", key)\n\t\t}\n\t}\n\n\tif sess != nil {\n\t\tvar cur *mongo.Cursor\n\t\terr := mongo.WithSession(context.Background(), sess, func(sc context.Context) error {\n\t\t\tvar aerr error\n\t\t\tcur, aerr = agg.Aggregate(sc, pipeline, opts)\n\t\t\treturn aerr\n\t\t})\n\t\treturn cur, err\n\t}\n\treturn agg.Aggregate(context.Background(), pipeline, opts)\n}\n\nfunc executeWatch(mt *mtest.T, w watcher, sess *mongo.Session, args bson.Raw) (*mongo.ChangeStream, error) {\n\tmt.Helper()\n\n\tpipeline := []any{}\n\telems, _ := args.Elements()\n\tfor _, elem := range elems {\n\t\tkey := elem.Key()\n\t\tval := elem.Value()\n\n\t\tswitch key {\n\t\tcase \"pipeline\":\n\t\t\tpipeline = bsonutil.RawToInterfaces(bsonutil.RawArrayToDocuments(val.Array())...)\n\t\tdefault:\n\t\t\tmt.Fatalf(\"unrecognized watch option: %v\", key)\n\t\t}\n\t}\n\n\tif sess != nil {\n\t\tvar stream *mongo.ChangeStream\n\t\terr := mongo.WithSession(context.Background(), sess, func(sc context.Context) error {\n\t\t\tvar csErr error\n\t\t\tstream, csErr = w.Watch(sc, pipeline)\n\t\t\treturn csErr\n\t\t})\n\t\treturn stream, err\n\t}\n\treturn w.Watch(context.Background(), pipeline)\n}\n\nfunc executeCountDocuments(mt *mtest.T, sess *mongo.Session, args bson.Raw) (int64, error) {\n\tmt.Helper()\n\n\tfilter := emptyDoc\n\topts := options.Count()\n\n\telems, _ := args.Elements()\n\tfor _, elem := range elems {\n\t\tname := elem.Key()\n\t\topt := elem.Value()\n\n\t\tswitch name {\n\t\tcase \"filter\":\n\t\t\tfilter = opt.Document()\n\t\tcase \"skip\":\n\t\t\topts = opts.SetSkip(int64(opt.Int32()))\n\t\tcase \"limit\":\n\t\t\topts = opts.SetLimit(int64(opt.Int32()))\n\t\tcase \"collation\":\n\t\t\topts = opts.SetCollation(createCollation(mt, opt.Document()))\n\t\tcase \"session\":\n\t\tdefault:\n\t\t\tmt.Fatalf(\"unrecognized count option: %v\", name)\n\t\t}\n\t}\n\n\tif sess != nil {\n\t\tvar count int64\n\t\terr := mongo.WithSession(context.Background(), sess, func(sc context.Context) error {\n\t\t\tvar countErr error\n\t\t\tcount, countErr = mt.Coll.CountDocuments(sc, filter, opts)\n\t\t\treturn countErr\n\t\t})\n\t\treturn count, err\n\t}\n\treturn mt.Coll.CountDocuments(context.Background(), filter, opts)\n}\n\nfunc executeInsertOne(mt *mtest.T, sess *mongo.Session, args bson.Raw) (*mongo.InsertOneResult, error) {\n\tmt.Helper()\n\n\tdoc := emptyDoc\n\topts := options.InsertOne()\n\n\telems, _ := args.Elements()\n\tfor _, elem := range elems {\n\t\tkey := elem.Key()\n\t\tval := elem.Value()\n\n\t\tswitch key {\n\t\tcase \"document\":\n\t\t\tdoc = val.Document()\n\t\tcase \"bypassDocumentValidation\":\n\t\t\topts.SetBypassDocumentValidation(val.Boolean())\n\t\tcase \"session\":\n\t\tdefault:\n\t\t\tmt.Fatalf(\"unrecognized insertOne option: %v\", key)\n\t\t}\n\t}\n\n\tif sess != nil {\n\t\tvar res *mongo.InsertOneResult\n\t\terr := mongo.WithSession(context.Background(), sess, func(sc context.Context) error {\n\t\t\tvar insertErr error\n\t\t\tres, insertErr = mt.Coll.InsertOne(sc, doc, opts)\n\t\t\treturn insertErr\n\t\t})\n\t\treturn res, err\n\t}\n\treturn mt.Coll.InsertOne(context.Background(), doc, opts)\n}\n\nfunc executeInsertMany(mt *mtest.T, sess *mongo.Session, args bson.Raw) (*mongo.InsertManyResult, error) {\n\tmt.Helper()\n\n\tvar docs []any\n\topts := options.InsertMany()\n\n\telems, _ := args.Elements()\n\tfor _, elem := range elems {\n\t\tkey := elem.Key()\n\t\tval := elem.Value()\n\n\t\tswitch key {\n\t\tcase \"documents\":\n\t\t\tdocs = bsonutil.RawToInterfaces(bsonutil.RawArrayToDocuments(val.Array())...)\n\t\tcase \"options\":\n\t\t\t// Some of the older tests use this to set the \"ordered\" option\n\t\t\toptsDoc := val.Document()\n\t\t\toptsElems, _ := optsDoc.Elements()\n\t\t\tassert.Equal(mt, 1, len(optsElems), \"expected 1 options element, got %v\", len(optsElems))\n\t\t\topts.SetOrdered(optsDoc.Lookup(\"ordered\").Boolean())\n\t\tcase \"session\":\n\t\tdefault:\n\t\t\tmt.Fatalf(\"unrecognized insertMany option: %v\", key)\n\t\t}\n\t}\n\n\tif sess != nil {\n\t\tvar res *mongo.InsertManyResult\n\t\terr := mongo.WithSession(context.Background(), sess, func(sc context.Context) error {\n\t\t\tvar insertErr error\n\t\t\tres, insertErr = mt.Coll.InsertMany(sc, docs, opts)\n\t\t\treturn insertErr\n\t\t})\n\t\treturn res, err\n\t}\n\treturn mt.Coll.InsertMany(context.Background(), docs, opts)\n}\n\nfunc setFindModifiers(modifiersDoc bson.Raw, opts *options.FindOptionsBuilder) {\n\telems, _ := modifiersDoc.Elements()\n\tfor _, elem := range elems {\n\t\tkey := elem.Key()\n\t\tval := elem.Value()\n\n\t\tswitch key {\n\t\tcase \"$comment\":\n\t\t\topts.SetComment(val.StringValue())\n\t\tcase \"$hint\":\n\t\t\topts.SetHint(val.Document())\n\t\tcase \"$max\":\n\t\t\topts.SetMax(val.Document())\n\t\tcase \"$min\":\n\t\t\topts.SetMin(val.Document())\n\t\tcase \"$returnKey\":\n\t\t\topts.SetReturnKey(val.Boolean())\n\t\tcase \"$showDiskLoc\":\n\t\t\topts.SetShowRecordID(val.Boolean())\n\t\t}\n\t}\n}\n\nfunc executeFind(mt *mtest.T, sess *mongo.Session, args bson.Raw) (*mongo.Cursor, error) {\n\tmt.Helper()\n\n\tfilter := emptyDoc\n\topts := options.Find()\n\n\telems, _ := args.Elements()\n\tfor _, elem := range elems {\n\t\tkey := elem.Key()\n\t\tval := elem.Value()\n\n\t\tswitch key {\n\t\tcase \"filter\":\n\t\t\tfilter = val.Document()\n\t\tcase \"sort\":\n\t\t\topts = opts.SetSort(val.Document())\n\t\tcase \"skip\":\n\t\t\topts = opts.SetSkip(numberFromValue(mt, val))\n\t\tcase \"limit\":\n\t\t\topts = opts.SetLimit(numberFromValue(mt, val))\n\t\tcase \"batchSize\":\n\t\t\topts = opts.SetBatchSize(int32(numberFromValue(mt, val)))\n\t\tcase \"collation\":\n\t\t\topts = opts.SetCollation(createCollation(mt, val.Document()))\n\t\tcase \"modifiers\":\n\t\t\tsetFindModifiers(val.Document(), opts)\n\t\tcase \"allowDiskUse\":\n\t\t\topts = opts.SetAllowDiskUse(val.Boolean())\n\t\tcase \"projection\":\n\t\t\topts = opts.SetProjection(val.Document())\n\t\tcase \"session\":\n\t\tdefault:\n\t\t\tmt.Fatalf(\"unrecognized find option: %v\", key)\n\t\t}\n\t}\n\n\tif sess != nil {\n\t\tvar c *mongo.Cursor\n\t\terr := mongo.WithSession(context.Background(), sess, func(sc context.Context) error {\n\t\t\tvar findErr error\n\t\t\tc, findErr = mt.Coll.Find(sc, filter, opts)\n\t\t\treturn findErr\n\t\t})\n\t\treturn c, err\n\t}\n\treturn mt.Coll.Find(context.Background(), filter, opts)\n}\n\nfunc executeRunCommand(mt *mtest.T, sess *mongo.Session, args bson.Raw) *mongo.SingleResult {\n\tmt.Helper()\n\n\tcmd := emptyDoc\n\topts := options.RunCmd()\n\n\telems, _ := args.Elements()\n\tfor _, elem := range elems {\n\t\tkey := elem.Key()\n\t\tval := elem.Value()\n\n\t\tswitch key {\n\t\tcase \"command\":\n\t\t\tcmd = val.Document()\n\t\tcase \"readPreference\":\n\t\t\topts.SetReadPreference(createReadPref(val))\n\t\tcase \"session\":\n\t\tdefault:\n\t\t\tmt.Fatalf(\"unrecognized runCommand option: %v\", key)\n\t\t}\n\t}\n\n\tif sess != nil {\n\t\tvar sr *mongo.SingleResult\n\t\t_ = mongo.WithSession(context.Background(), sess, func(sc context.Context) error {\n\t\t\tsr = mt.DB.RunCommand(sc, cmd, opts)\n\t\t\treturn nil\n\t\t})\n\t\treturn sr\n\t}\n\treturn mt.DB.RunCommand(context.Background(), cmd, opts)\n}\n\nfunc executeListCollections(mt *mtest.T, sess *mongo.Session, args bson.Raw) (*mongo.Cursor, error) {\n\tmt.Helper()\n\n\tfilter := emptyDoc\n\telems, _ := args.Elements()\n\tfor _, elem := range elems {\n\t\tkey := elem.Key()\n\t\tval := elem.Value()\n\n\t\tswitch key {\n\t\tcase \"filter\":\n\t\t\tfilter = val.Document()\n\t\tdefault:\n\t\t\tmt.Fatalf(\"unrecognized listCollectionNames option: %v\", key)\n\t\t}\n\t}\n\n\tif sess != nil {\n\t\tvar c *mongo.Cursor\n\t\terr := mongo.WithSession(context.Background(), sess, func(sc context.Context) error {\n\t\t\tvar lcErr error\n\t\t\tc, lcErr = mt.DB.ListCollections(sc, filter)\n\t\t\treturn lcErr\n\t\t})\n\t\treturn c, err\n\t}\n\treturn mt.DB.ListCollections(context.Background(), filter)\n}\n\nfunc executeListCollectionNames(mt *mtest.T, sess *mongo.Session, args bson.Raw) ([]string, error) {\n\tmt.Helper()\n\n\tfilter := emptyDoc\n\telems, _ := args.Elements()\n\tfor _, elem := range elems {\n\t\tkey := elem.Key()\n\t\tval := elem.Value()\n\n\t\tswitch key {\n\t\tcase \"filter\":\n\t\t\tfilter = val.Document()\n\t\tdefault:\n\t\t\tmt.Fatalf(\"unrecognized listCollectionNames option: %v\", key)\n\t\t}\n\t}\n\n\tif sess != nil {\n\t\tvar res []string\n\t\terr := mongo.WithSession(context.Background(), sess, func(sc context.Context) error {\n\t\t\tvar lcErr error\n\t\t\tres, lcErr = mt.DB.ListCollectionNames(sc, filter)\n\t\t\treturn lcErr\n\t\t})\n\t\treturn res, err\n\t}\n\treturn mt.DB.ListCollectionNames(context.Background(), filter)\n}\n\nfunc executeListDatabaseNames(mt *mtest.T, sess *mongo.Session, args bson.Raw) ([]string, error) {\n\tmt.Helper()\n\n\tfilter := emptyDoc\n\telems, _ := args.Elements()\n\tfor _, elem := range elems {\n\t\tkey := elem.Key()\n\t\tval := elem.Value()\n\n\t\tswitch key {\n\t\tcase \"filter\":\n\t\t\tfilter = val.Document()\n\t\tdefault:\n\t\t\tmt.Fatalf(\"unrecognized listCollectionNames option: %v\", key)\n\t\t}\n\t}\n\n\tif sess != nil {\n\t\tvar res []string\n\t\terr := mongo.WithSession(context.Background(), sess, func(sc context.Context) error {\n\t\t\tvar ldErr error\n\t\t\tres, ldErr = mt.Client.ListDatabaseNames(sc, filter)\n\t\t\treturn ldErr\n\t\t})\n\t\treturn res, err\n\t}\n\treturn mt.Client.ListDatabaseNames(context.Background(), filter)\n}\n\nfunc executeListDatabases(mt *mtest.T, sess *mongo.Session, args bson.Raw) (mongo.ListDatabasesResult, error) {\n\tmt.Helper()\n\n\tfilter := emptyDoc\n\telems, _ := args.Elements()\n\tfor _, elem := range elems {\n\t\tkey := elem.Key()\n\t\tval := elem.Value()\n\n\t\tswitch key {\n\t\tcase \"filter\":\n\t\t\tfilter = val.Document()\n\t\tdefault:\n\t\t\tmt.Fatalf(\"unrecognized listCollectionNames option: %v\", key)\n\t\t}\n\t}\n\n\tif sess != nil {\n\t\tvar res mongo.ListDatabasesResult\n\t\terr := mongo.WithSession(context.Background(), sess, func(sc context.Context) error {\n\t\t\tvar ldErr error\n\t\t\tres, ldErr = mt.Client.ListDatabases(sc, filter)\n\t\t\treturn ldErr\n\t\t})\n\t\treturn res, err\n\t}\n\treturn mt.Client.ListDatabases(context.Background(), filter)\n}\n\nfunc executeFindOne(mt *mtest.T, sess *mongo.Session, args bson.Raw) *mongo.SingleResult {\n\tmt.Helper()\n\n\tfilter := emptyDoc\n\telems, _ := args.Elements()\n\tfor _, elem := range elems {\n\t\tkey := elem.Key()\n\t\tval := elem.Value()\n\n\t\tswitch key {\n\t\tcase \"filter\":\n\t\t\tfilter = val.Document()\n\t\tdefault:\n\t\t\tmt.Fatalf(\"unrecognized findOne option: %v\", key)\n\t\t}\n\t}\n\n\tif sess != nil {\n\t\tvar res *mongo.SingleResult\n\t\t_ = mongo.WithSession(context.Background(), sess, func(sc context.Context) error {\n\t\t\tres = mt.Coll.FindOne(sc, filter)\n\t\t\treturn nil\n\t\t})\n\t\treturn res\n\t}\n\treturn mt.Coll.FindOne(context.Background(), filter)\n}\n\nfunc executeListIndexes(mt *mtest.T, sess *mongo.Session, args bson.Raw) (*mongo.Cursor, error) {\n\tmt.Helper()\n\n\t// no arguments expected. add a Fatal in case arguments are added in the future\n\tassert.Equal(mt, 0, len(args), \"unexpected listIndexes arguments: %v\", args)\n\tif sess != nil {\n\t\tvar cursor *mongo.Cursor\n\t\terr := mongo.WithSession(context.Background(), sess, func(sc context.Context) error {\n\t\t\tvar listErr error\n\t\t\tcursor, listErr = mt.Coll.Indexes().List(sc)\n\t\t\treturn listErr\n\t\t})\n\t\treturn cursor, err\n\t}\n\treturn mt.Coll.Indexes().List(context.Background())\n}\n\nfunc executeDistinct(mt *mtest.T, sess *mongo.Session, args bson.Raw) (bson.RawArray, error) {\n\tmt.Helper()\n\n\tvar fieldName string\n\tfilter := emptyDoc\n\topts := options.Distinct()\n\n\telems, _ := args.Elements()\n\tfor _, elem := range elems {\n\t\tkey := elem.Key()\n\t\tval := elem.Value()\n\n\t\tswitch key {\n\t\tcase \"filter\":\n\t\t\tfilter = val.Document()\n\t\tcase \"fieldName\":\n\t\t\tfieldName = val.StringValue()\n\t\tcase \"collation\":\n\t\t\topts = opts.SetCollation(createCollation(mt, val.Document()))\n\t\tcase \"session\":\n\t\tdefault:\n\t\t\tmt.Fatalf(\"unrecognized distinct option: %v\", key)\n\t\t}\n\t}\n\n\tvar res *mongo.DistinctResult\n\tif sess != nil {\n\t\terr := mongo.WithSession(context.Background(), sess, func(ctx context.Context) error {\n\t\t\tres = mt.Coll.Distinct(ctx, fieldName, filter, opts)\n\n\t\t\treturn res.Err()\n\t\t})\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t} else {\n\t\tres = mt.Coll.Distinct(context.Background(), fieldName, filter, opts)\n\t}\n\n\treturn res.Raw()\n}\n\nfunc executeFindOneAndDelete(mt *mtest.T, sess *mongo.Session, args bson.Raw) *mongo.SingleResult {\n\tmt.Helper()\n\n\tfilter := emptyDoc\n\topts := options.FindOneAndDelete()\n\n\telems, _ := args.Elements()\n\tfor _, elem := range elems {\n\t\tkey := elem.Key()\n\t\tval := elem.Value()\n\n\t\tswitch key {\n\t\tcase \"filter\":\n\t\t\tfilter = val.Document()\n\t\tcase \"sort\":\n\t\t\topts = opts.SetSort(val.Document())\n\t\tcase \"projection\":\n\t\t\topts = opts.SetProjection(val.Document())\n\t\tcase \"collation\":\n\t\t\topts = opts.SetCollation(createCollation(mt, val.Document()))\n\t\tcase \"hint\":\n\t\t\topts = opts.SetHint(createHint(mt, val))\n\t\tcase \"session\":\n\t\tdefault:\n\t\t\tmt.Fatalf(\"unrecognized findOneAndDelete option: %v\", key)\n\t\t}\n\t}\n\n\tif sess != nil {\n\t\tvar res *mongo.SingleResult\n\t\t_ = mongo.WithSession(context.Background(), sess, func(sc context.Context) error {\n\t\t\tres = mt.Coll.FindOneAndDelete(sc, filter, opts)\n\t\t\treturn nil\n\t\t})\n\t\treturn res\n\t}\n\treturn mt.Coll.FindOneAndDelete(context.Background(), filter, opts)\n}\n\nfunc executeFindOneAndUpdate(mt *mtest.T, sess *mongo.Session, args bson.Raw) *mongo.SingleResult {\n\tmt.Helper()\n\n\tfilter := emptyDoc\n\tvar update any = emptyDoc\n\topts := options.FindOneAndUpdate()\n\n\telems, _ := args.Elements()\n\tfor _, elem := range elems {\n\t\tkey := elem.Key()\n\t\tval := elem.Value()\n\n\t\tswitch key {\n\t\tcase \"filter\":\n\t\t\tfilter = val.Document()\n\t\tcase \"update\":\n\t\t\tupdate = createUpdate(mt, val)\n\t\tcase \"arrayFilters\":\n\t\t\topts = opts.SetArrayFilters(\n\t\t\t\tbsonutil.RawToInterfaces(bsonutil.RawArrayToDocuments(val.Array())...),\n\t\t\t)\n\t\tcase \"sort\":\n\t\t\topts = opts.SetSort(val.Document())\n\t\tcase \"projection\":\n\t\t\topts = opts.SetProjection(val.Document())\n\t\tcase \"upsert\":\n\t\t\topts = opts.SetUpsert(val.Boolean())\n\t\tcase \"returnDocument\":\n\t\t\tswitch vstr := val.StringValue(); vstr {\n\t\t\tcase \"After\":\n\t\t\t\topts = opts.SetReturnDocument(options.After)\n\t\t\tcase \"Before\":\n\t\t\t\topts = opts.SetReturnDocument(options.Before)\n\t\t\tdefault:\n\t\t\t\tmt.Fatalf(\"unrecognized returnDocument value: %v\", vstr)\n\t\t\t}\n\t\tcase \"collation\":\n\t\t\topts = opts.SetCollation(createCollation(mt, val.Document()))\n\t\tcase \"hint\":\n\t\t\topts = opts.SetHint(createHint(mt, val))\n\t\tcase \"session\":\n\t\tdefault:\n\t\t\tmt.Fatalf(\"unrecognized findOneAndUpdate option: %v\", key)\n\t\t}\n\t}\n\n\tif sess != nil {\n\t\tvar res *mongo.SingleResult\n\t\t_ = mongo.WithSession(context.Background(), sess, func(sc context.Context) error {\n\t\t\tres = mt.Coll.FindOneAndUpdate(sc, filter, update, opts)\n\t\t\treturn nil\n\t\t})\n\t\treturn res\n\t}\n\treturn mt.Coll.FindOneAndUpdate(context.Background(), filter, update, opts)\n}\n\nfunc executeFindOneAndReplace(mt *mtest.T, sess *mongo.Session, args bson.Raw) *mongo.SingleResult {\n\tmt.Helper()\n\n\tfilter := emptyDoc\n\treplacement := emptyDoc\n\topts := options.FindOneAndReplace()\n\n\telems, _ := args.Elements()\n\tfor _, elem := range elems {\n\t\tkey := elem.Key()\n\t\tval := elem.Value()\n\n\t\tswitch key {\n\t\tcase \"filter\":\n\t\t\tfilter = val.Document()\n\t\tcase \"replacement\":\n\t\t\treplacement = val.Document()\n\t\tcase \"sort\":\n\t\t\topts = opts.SetSort(val.Document())\n\t\tcase \"projection\":\n\t\t\topts = opts.SetProjection(val.Document())\n\t\tcase \"upsert\":\n\t\t\topts = opts.SetUpsert(val.Boolean())\n\t\tcase \"returnDocument\":\n\t\t\tswitch vstr := val.StringValue(); vstr {\n\t\t\tcase \"After\":\n\t\t\t\topts = opts.SetReturnDocument(options.After)\n\t\t\tcase \"Before\":\n\t\t\t\topts = opts.SetReturnDocument(options.Before)\n\t\t\tdefault:\n\t\t\t\tmt.Fatalf(\"unrecognized returnDocument value: %v\", vstr)\n\t\t\t}\n\t\tcase \"collation\":\n\t\t\topts = opts.SetCollation(createCollation(mt, val.Document()))\n\t\tcase \"hint\":\n\t\t\topts = opts.SetHint(createHint(mt, val))\n\t\tcase \"session\":\n\t\tdefault:\n\t\t\tmt.Fatalf(\"unrecognized findOneAndReplace option: %v\", key)\n\t\t}\n\t}\n\n\tif sess != nil {\n\t\tvar res *mongo.SingleResult\n\t\t_ = mongo.WithSession(context.Background(), sess, func(sc context.Context) error {\n\t\t\tres = mt.Coll.FindOneAndReplace(sc, filter, replacement, opts)\n\t\t\treturn nil\n\t\t})\n\t\treturn res\n\t}\n\treturn mt.Coll.FindOneAndReplace(context.Background(), filter, replacement, opts)\n}\n\nfunc executeDeleteOne(mt *mtest.T, sess *mongo.Session, args bson.Raw) (*mongo.DeleteResult, error) {\n\tmt.Helper()\n\n\tfilter := emptyDoc\n\topts := options.DeleteOne()\n\n\telems, _ := args.Elements()\n\tfor _, elem := range elems {\n\t\tkey := elem.Key()\n\t\tval := elem.Value()\n\n\t\tswitch key {\n\t\tcase \"filter\":\n\t\t\tfilter = val.Document()\n\t\tcase \"collation\":\n\t\t\topts = opts.SetCollation(createCollation(mt, val.Document()))\n\t\tcase \"hint\":\n\t\t\topts = opts.SetHint(createHint(mt, val))\n\t\tcase \"session\":\n\t\tdefault:\n\t\t\tmt.Fatalf(\"unrecognized deleteOne option: %v\", key)\n\t\t}\n\t}\n\n\tif sess != nil {\n\t\tvar res *mongo.DeleteResult\n\t\terr := mongo.WithSession(context.Background(), sess, func(sc context.Context) error {\n\t\t\tvar derr error\n\t\t\tres, derr = mt.Coll.DeleteOne(sc, filter, opts)\n\t\t\treturn derr\n\t\t})\n\t\treturn res, err\n\t}\n\treturn mt.Coll.DeleteOne(context.Background(), filter, opts)\n}\n\nfunc executeDeleteMany(mt *mtest.T, sess *mongo.Session, args bson.Raw) (*mongo.DeleteResult, error) {\n\tmt.Helper()\n\n\tfilter := emptyDoc\n\topts := options.DeleteMany()\n\n\telems, _ := args.Elements()\n\tfor _, elem := range elems {\n\t\tkey := elem.Key()\n\t\tval := elem.Value()\n\n\t\tswitch key {\n\t\tcase \"filter\":\n\t\t\tfilter = val.Document()\n\t\tcase \"collation\":\n\t\t\topts = opts.SetCollation(createCollation(mt, val.Document()))\n\t\tcase \"hint\":\n\t\t\topts = opts.SetHint(createHint(mt, val))\n\t\tcase \"session\":\n\t\tdefault:\n\t\t\tmt.Fatalf(\"unrecognized deleteMany option: %v\", key)\n\t\t}\n\t}\n\n\tif sess != nil {\n\t\tvar res *mongo.DeleteResult\n\t\terr := mongo.WithSession(context.Background(), sess, func(sc context.Context) error {\n\t\t\tvar derr error\n\t\t\tres, derr = mt.Coll.DeleteMany(sc, filter, opts)\n\t\t\treturn derr\n\t\t})\n\t\treturn res, err\n\t}\n\treturn mt.Coll.DeleteMany(context.Background(), filter, opts)\n}\n\nfunc executeUpdateOne(mt *mtest.T, sess *mongo.Session, args bson.Raw) (*mongo.UpdateResult, error) {\n\tmt.Helper()\n\n\tfilter := emptyDoc\n\tvar update any = emptyDoc\n\topts := options.UpdateOne()\n\n\telems, _ := args.Elements()\n\tfor _, elem := range elems {\n\t\tkey := elem.Key()\n\t\tval := elem.Value()\n\n\t\tswitch key {\n\t\tcase \"filter\":\n\t\t\tfilter = val.Document()\n\t\tcase \"update\":\n\t\t\tupdate = createUpdate(mt, val)\n\t\tcase \"arrayFilters\":\n\t\t\topts = opts.SetArrayFilters(\n\t\t\t\tbsonutil.RawToInterfaces(bsonutil.RawArrayToDocuments(val.Array())...),\n\t\t\t)\n\t\tcase \"upsert\":\n\t\t\topts = opts.SetUpsert(val.Boolean())\n\t\tcase \"collation\":\n\t\t\topts = opts.SetCollation(createCollation(mt, val.Document()))\n\t\tcase \"hint\":\n\t\t\topts = opts.SetHint(createHint(mt, val))\n\t\tcase \"session\":\n\t\tdefault:\n\t\t\tmt.Fatalf(\"unrecognized updateOne option: %v\", key)\n\t\t}\n\t}\n\n\tupdateArgs, err := mongoutil.NewOptions[options.UpdateOneOptions](opts)\n\trequire.NoError(mt, err, \"failed to construct options from builder\")\n\n\tif updateArgs.Upsert == nil {\n\t\topts = opts.SetUpsert(false)\n\t}\n\n\tif sess != nil {\n\t\tvar res *mongo.UpdateResult\n\t\terr := mongo.WithSession(context.Background(), sess, func(sc context.Context) error {\n\t\t\tvar uerr error\n\t\t\tres, uerr = mt.Coll.UpdateOne(sc, filter, update, opts)\n\t\t\treturn uerr\n\t\t})\n\t\treturn res, err\n\t}\n\treturn mt.Coll.UpdateOne(context.Background(), filter, update, opts)\n}\n\nfunc executeUpdateMany(mt *mtest.T, sess *mongo.Session, args bson.Raw) (*mongo.UpdateResult, error) {\n\tmt.Helper()\n\n\tfilter := emptyDoc\n\tvar update any = emptyDoc\n\topts := options.UpdateMany()\n\n\telems, _ := args.Elements()\n\tfor _, elem := range elems {\n\t\tkey := elem.Key()\n\t\tval := elem.Value()\n\n\t\tswitch key {\n\t\tcase \"filter\":\n\t\t\tfilter = val.Document()\n\t\tcase \"update\":\n\t\t\tupdate = createUpdate(mt, val)\n\t\tcase \"arrayFilters\":\n\t\t\topts = opts.SetArrayFilters(\n\t\t\t\tbsonutil.RawToInterfaces(bsonutil.RawArrayToDocuments(val.Array())...),\n\t\t\t)\n\t\tcase \"upsert\":\n\t\t\topts = opts.SetUpsert(val.Boolean())\n\t\tcase \"collation\":\n\t\t\topts = opts.SetCollation(createCollation(mt, val.Document()))\n\t\tcase \"hint\":\n\t\t\topts = opts.SetHint(createHint(mt, val))\n\t\tcase \"session\":\n\t\tdefault:\n\t\t\tmt.Fatalf(\"unrecognized updateMany option: %v\", key)\n\t\t}\n\t}\n\n\tupdateArgs, err := mongoutil.NewOptions[options.UpdateManyOptions](opts)\n\trequire.NoError(mt, err, \"failed to construct options from builder\")\n\n\tif updateArgs.Upsert == nil {\n\t\topts = opts.SetUpsert(false)\n\t}\n\n\tif sess != nil {\n\t\tvar res *mongo.UpdateResult\n\t\terr := mongo.WithSession(context.Background(), sess, func(sc context.Context) error {\n\t\t\tvar uerr error\n\t\t\tres, uerr = mt.Coll.UpdateMany(sc, filter, update, opts)\n\t\t\treturn uerr\n\t\t})\n\t\treturn res, err\n\t}\n\treturn mt.Coll.UpdateMany(context.Background(), filter, update, opts)\n}\n\nfunc executeReplaceOne(mt *mtest.T, sess *mongo.Session, args bson.Raw) (*mongo.UpdateResult, error) {\n\tmt.Helper()\n\n\tfilter := emptyDoc\n\treplacement := emptyDoc\n\topts := options.Replace()\n\n\telems, _ := args.Elements()\n\tfor _, elem := range elems {\n\t\tkey := elem.Key()\n\t\tval := elem.Value()\n\n\t\tswitch key {\n\t\tcase \"filter\":\n\t\t\tfilter = val.Document()\n\t\tcase \"replacement\":\n\t\t\treplacement = val.Document()\n\t\tcase \"upsert\":\n\t\t\topts = opts.SetUpsert(val.Boolean())\n\t\tcase \"collation\":\n\t\t\topts = opts.SetCollation(createCollation(mt, val.Document()))\n\t\tcase \"hint\":\n\t\t\topts = opts.SetHint(createHint(mt, val))\n\t\tcase \"session\":\n\t\tdefault:\n\t\t\tmt.Fatalf(\"unrecognized replaceOne option: %v\", key)\n\t\t}\n\t}\n\n\tupdateArgs, err := mongoutil.NewOptions[options.ReplaceOptions](opts)\n\trequire.NoError(mt, err, \"failed to construct options from builder\")\n\n\tif updateArgs.Upsert == nil {\n\t\topts = opts.SetUpsert(false)\n\t}\n\n\tif sess != nil {\n\t\tvar res *mongo.UpdateResult\n\t\terr := mongo.WithSession(context.Background(), sess, func(sc context.Context) error {\n\t\t\tvar uerr error\n\t\t\tres, uerr = mt.Coll.ReplaceOne(sc, filter, replacement, opts)\n\t\t\treturn uerr\n\t\t})\n\t\treturn res, err\n\t}\n\treturn mt.Coll.ReplaceOne(context.Background(), filter, replacement, opts)\n}\n\ntype withTransactionArgs struct {\n\tCallback *struct {\n\t\tOperations []*operation `bson:\"operations\"`\n\t} `bson:\"callback\"`\n\tOptions bson.Raw `bson:\"options\"`\n}\n\nfunc runWithTransactionOperations(mt *mtest.T, operations []*operation, sess *mongo.Session) error {\n\tmt.Helper()\n\n\tfor _, op := range operations {\n\t\tif op.Name == \"count\" {\n\t\t\tmt.Skip(\"count has been deprecated\")\n\t\t}\n\n\t\t// create collection with default read preference Primary (needed to prevent server selection fail)\n\t\tmt.CloneCollection(options.Collection().SetReadPreference(readpref.Primary()).SetReadConcern(readconcern.Local()))\n\n\t\t// execute the command on given object\n\t\tvar err error\n\t\tswitch op.Object {\n\t\tcase \"session0\":\n\t\t\terr = executeSessionOperation(mt, op, sess)\n\t\tcase \"collection\":\n\t\t\terr = executeCollectionOperation(mt, op, sess)\n\t\tdefault:\n\t\t\tmt.Fatalf(\"unrecognized withTransaction operation object: %v\", op.Object)\n\t\t}\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc executeWithTransaction(mt *mtest.T, sess *mongo.Session, args bson.Raw) error {\n\tmt.Helper()\n\n\tvar testArgs withTransactionArgs\n\tdec := bson.NewDecoder(bson.NewDocumentReader(bytes.NewReader(args)))\n\tdec.SetRegistry(specTestRegistry)\n\terr := dec.Decode(&testArgs)\n\tassert.Nil(mt, err, \"error creating withTransactionArgs: %v\", err)\n\topts := createTransactionOptions(mt, testArgs.Options)\n\n\t_, err = sess.WithTransaction(context.Background(), func(_ context.Context) (any, error) {\n\t\terr := runWithTransactionOperations(mt, testArgs.Callback.Operations, sess)\n\t\treturn nil, err\n\t}, opts)\n\treturn err\n}\n\nfunc executeBulkWrite(mt *mtest.T, sess *mongo.Session, args bson.Raw) (*mongo.BulkWriteResult, error) {\n\tmt.Helper()\n\n\tmodels := createBulkWriteModels(mt, bson.Raw(args.Lookup(\"requests\").Array()))\n\topts := options.BulkWrite()\n\n\trawOpts, err := args.LookupErr(\"options\")\n\tif err == nil {\n\t\telems, _ := rawOpts.Document().Elements()\n\t\tfor _, elem := range elems {\n\t\t\tname := elem.Key()\n\t\t\topt := elem.Value()\n\n\t\t\tswitch name {\n\t\t\tcase \"ordered\":\n\t\t\t\topts.SetOrdered(opt.Boolean())\n\t\t\tdefault:\n\t\t\t\tmt.Fatalf(\"unrecognized bulk write option: %v\", name)\n\t\t\t}\n\t\t}\n\t}\n\n\tif sess != nil {\n\t\tvar res *mongo.BulkWriteResult\n\t\terr := mongo.WithSession(context.Background(), sess, func(sc context.Context) error {\n\t\t\tvar bwerr error\n\t\t\tres, bwerr = mt.Coll.BulkWrite(sc, models, opts)\n\t\t\treturn bwerr\n\t\t})\n\t\treturn res, err\n\t}\n\treturn mt.Coll.BulkWrite(context.Background(), models, opts)\n}\n\nfunc createBulkWriteModels(mt *mtest.T, rawModels bson.Raw) []mongo.WriteModel {\n\tvals, _ := rawModels.Values()\n\tmodels := make([]mongo.WriteModel, len(vals))\n\n\tfor i, val := range vals {\n\t\tmodels[i] = createBulkWriteModel(mt, val.Document())\n\t}\n\treturn models\n}\n\nfunc createBulkWriteModel(mt *mtest.T, rawModel bson.Raw) mongo.WriteModel {\n\tname := rawModel.Lookup(\"name\").StringValue()\n\targs := rawModel.Lookup(\"arguments\").Document()\n\n\tswitch name {\n\tcase \"insertOne\":\n\t\treturn mongo.NewInsertOneModel().SetDocument(args.Lookup(\"document\").Document())\n\tcase \"updateOne\":\n\t\tuom := mongo.NewUpdateOneModel()\n\t\tuom.SetFilter(args.Lookup(\"filter\").Document())\n\t\tuom.SetUpdate(createUpdate(mt, args.Lookup(\"update\")))\n\t\tif upsert, err := args.LookupErr(\"upsert\"); err == nil {\n\t\t\tuom.SetUpsert(upsert.Boolean())\n\t\t}\n\t\tif collation, err := args.LookupErr(\"collation\"); err == nil {\n\t\t\tuom.SetCollation(createCollation(mt, collation.Document()))\n\t\t}\n\t\tif arrayFilters, err := args.LookupErr(\"arrayFilters\"); err == nil {\n\t\t\tuom.SetArrayFilters(\n\t\t\t\tbsonutil.RawToInterfaces(bsonutil.RawArrayToDocuments(arrayFilters.Array())...),\n\t\t\t)\n\t\t}\n\t\tif hintVal, err := args.LookupErr(\"hint\"); err == nil {\n\t\t\tuom.SetHint(createHint(mt, hintVal))\n\t\t}\n\t\tif uom.Upsert == nil {\n\t\t\tuom.SetUpsert(false)\n\t\t}\n\n\t\treturn uom\n\tcase \"updateMany\":\n\t\tumm := mongo.NewUpdateManyModel()\n\t\tumm.SetFilter(args.Lookup(\"filter\").Document())\n\t\tumm.SetUpdate(createUpdate(mt, args.Lookup(\"update\")))\n\t\tif upsert, err := args.LookupErr(\"upsert\"); err == nil {\n\t\t\tumm.SetUpsert(upsert.Boolean())\n\t\t}\n\t\tif collation, err := args.LookupErr(\"collation\"); err == nil {\n\t\t\tumm.SetCollation(createCollation(mt, collation.Document()))\n\t\t}\n\t\tif arrayFilters, err := args.LookupErr(\"arrayFilters\"); err == nil {\n\t\t\tumm.SetArrayFilters(\n\t\t\t\tbsonutil.RawToInterfaces(bsonutil.RawArrayToDocuments(arrayFilters.Array())...),\n\t\t\t)\n\t\t}\n\t\tif hintVal, err := args.LookupErr(\"hint\"); err == nil {\n\t\t\tumm.SetHint(createHint(mt, hintVal))\n\t\t}\n\t\tif umm.Upsert == nil {\n\t\t\tumm.SetUpsert(false)\n\t\t}\n\n\t\treturn umm\n\tcase \"deleteOne\":\n\t\tdom := mongo.NewDeleteOneModel()\n\t\tdom.SetFilter(args.Lookup(\"filter\").Document())\n\t\tif collation, err := args.LookupErr(\"collation\"); err == nil {\n\t\t\tdom.SetCollation(createCollation(mt, collation.Document()))\n\t\t}\n\t\tif hint, err := args.LookupErr(\"hint\"); err == nil {\n\t\t\tdom.SetHint(createHint(mt, hint))\n\t\t}\n\n\t\treturn dom\n\tcase \"deleteMany\":\n\t\tdmm := mongo.NewDeleteManyModel()\n\t\tdmm.SetFilter(args.Lookup(\"filter\").Document())\n\t\tif collation, err := args.LookupErr(\"collation\"); err == nil {\n\t\t\tdmm.SetCollation(createCollation(mt, collation.Document()))\n\t\t}\n\t\tif hint, err := args.LookupErr(\"hint\"); err == nil {\n\t\t\tdmm.SetHint(createHint(mt, hint))\n\t\t}\n\n\t\treturn dmm\n\tcase \"replaceOne\":\n\t\trom := mongo.NewReplaceOneModel()\n\t\trom.SetFilter(args.Lookup(\"filter\").Document())\n\t\trom.SetReplacement(args.Lookup(\"replacement\").Document())\n\t\tif upsert, err := args.LookupErr(\"upsert\"); err == nil {\n\t\t\trom.SetUpsert(upsert.Boolean())\n\t\t}\n\t\tif collation, err := args.LookupErr(\"collation\"); err == nil {\n\t\t\trom.SetCollation(createCollation(mt, collation.Document()))\n\t\t}\n\t\tif hintVal, err := args.LookupErr(\"hint\"); err == nil {\n\t\t\trom.SetHint(createHint(mt, hintVal))\n\t\t}\n\t\tif rom.Upsert == nil {\n\t\t\trom.SetUpsert(false)\n\t\t}\n\n\t\treturn rom\n\tdefault:\n\t\tmt.Fatalf(\"unrecognized model type: %v\", name)\n\t}\n\n\treturn nil\n}\n\nfunc executeEstimatedDocumentCount(mt *mtest.T, sess *mongo.Session, args bson.Raw) (int64, error) {\n\tmt.Helper()\n\n\t// no arguments expected. add a Fatal in case arguments are added in the future\n\telems, _ := args.Elements()\n\tassert.Equal(mt, 0, len(elems), \"unexpected estimatedDocumentCount arguments %v\", args)\n\n\tif sess != nil {\n\t\tvar res int64\n\t\terr := mongo.WithSession(context.Background(), sess, func(sc context.Context) error {\n\t\t\tvar countErr error\n\t\t\tres, countErr = mt.Coll.EstimatedDocumentCount(sc)\n\t\t\treturn countErr\n\t\t})\n\t\treturn res, err\n\t}\n\treturn mt.Coll.EstimatedDocumentCount(context.Background())\n}\n\nfunc executeGridFSDownload(mt *mtest.T, bucket *mongo.GridFSBucket, args bson.Raw) (int64, error) {\n\tmt.Helper()\n\n\tvar fileID bson.ObjectID\n\telems, _ := args.Elements()\n\tfor _, elem := range elems {\n\t\tkey := elem.Key()\n\t\tval := elem.Value()\n\n\t\tswitch key {\n\t\tcase \"id\":\n\t\t\tfileID = val.ObjectID()\n\t\tdefault:\n\t\t\tmt.Fatalf(\"unrecognized download option: %v\", key)\n\t\t}\n\t}\n\n\treturn bucket.DownloadToStream(context.Background(), fileID, new(bytes.Buffer))\n}\n\nfunc executeGridFSDownloadByName(mt *mtest.T, bucket *mongo.GridFSBucket, args bson.Raw) (int64, error) {\n\tmt.Helper()\n\n\tvar file string\n\telems, _ := args.Elements()\n\tfor _, elem := range elems {\n\t\tkey := elem.Key()\n\t\tval := elem.Value()\n\n\t\tswitch key {\n\t\tcase \"filename\":\n\t\t\tfile = val.StringValue()\n\t\tdefault:\n\t\t\tmt.Fatalf(\"unrecognized download by name option: %v\", key)\n\t\t}\n\t}\n\n\treturn bucket.DownloadToStreamByName(context.Background(), file, new(bytes.Buffer))\n}\n\nfunc executeCreateIndex(mt *mtest.T, sess *mongo.Session, args bson.Raw) (string, error) {\n\tmt.Helper()\n\n\tmodel := mongo.IndexModel{\n\t\tOptions: options.Index(),\n\t}\n\telems, _ := args.Elements()\n\tfor _, elem := range elems {\n\t\tkey := elem.Key()\n\t\tval := elem.Value()\n\n\t\tswitch key {\n\t\tcase \"keys\":\n\t\t\tmodel.Keys = val.Document()\n\t\tcase \"name\":\n\t\t\tmodel.Options.SetName(val.StringValue())\n\t\tcase \"session\":\n\t\tdefault:\n\t\t\tmt.Fatalf(\"unrecognized createIndex option %v\", key)\n\t\t}\n\t}\n\n\tif sess != nil {\n\t\tvar indexName string\n\t\terr := mongo.WithSession(context.Background(), sess, func(sc context.Context) error {\n\t\t\tvar indexErr error\n\t\t\tindexName, indexErr = mt.Coll.Indexes().CreateOne(sc, model)\n\t\t\treturn indexErr\n\t\t})\n\t\treturn indexName, err\n\t}\n\treturn mt.Coll.Indexes().CreateOne(context.Background(), model)\n}\n\nfunc executeDropIndex(mt *mtest.T, sess *mongo.Session, args bson.Raw) error {\n\tmt.Helper()\n\n\tvar name string\n\telems, _ := args.Elements()\n\tfor _, elem := range elems {\n\t\tkey := elem.Key()\n\t\tval := elem.Value()\n\n\t\tswitch key {\n\t\tcase \"name\":\n\t\t\tname = val.StringValue()\n\t\tdefault:\n\t\t\tmt.Fatalf(\"unrecognized dropIndex option %v\", key)\n\t\t}\n\t}\n\n\tif sess != nil {\n\t\treturn mongo.WithSession(context.Background(), sess, func(sc context.Context) error {\n\t\t\treturn mt.Coll.Indexes().DropOne(sc, name)\n\t\t})\n\t}\n\n\treturn mt.Coll.Indexes().DropOne(context.Background(), name)\n}\n\nfunc executeDropCollection(mt *mtest.T, sess *mongo.Session, args bson.Raw) error {\n\tmt.Helper()\n\n\tvar collName string\n\telems, _ := args.Elements()\n\tdco := options.DropCollection()\n\tfor _, elem := range elems {\n\t\tkey := elem.Key()\n\t\tval := elem.Value()\n\n\t\tswitch key {\n\t\tcase \"encryptedFields\":\n\t\t\tdco.SetEncryptedFields(val.Document())\n\t\tcase \"collection\":\n\t\t\tcollName = val.StringValue()\n\t\tdefault:\n\t\t\tmt.Fatalf(\"unrecognized dropCollection option %v\", key)\n\t\t}\n\t}\n\n\tcoll := mt.DB.Collection(collName)\n\tif sess != nil {\n\t\terr := mongo.WithSession(context.Background(), sess, func(sc context.Context) error {\n\t\t\treturn coll.Drop(sc, dco)\n\t\t})\n\t\treturn err\n\t}\n\treturn coll.Drop(context.Background(), dco)\n}\n\nfunc executeCreateCollection(mt *mtest.T, sess *mongo.Session, args bson.Raw) error {\n\tmt.Helper()\n\n\tcco := options.CreateCollection()\n\n\tvar collName string\n\telems, _ := args.Elements()\n\tfor _, elem := range elems {\n\t\tkey := elem.Key()\n\t\tval := elem.Value()\n\n\t\tswitch key {\n\t\tcase \"encryptedFields\":\n\t\t\tcco.SetEncryptedFields(val.Document())\n\t\tcase \"collection\":\n\t\t\tcollName = val.StringValue()\n\t\tcase \"validator\":\n\t\t\tcco.SetValidator(val.Document())\n\t\tcase \"session\":\n\t\tdefault:\n\t\t\tmt.Fatalf(\"unrecognized createCollection option %v\", key)\n\t\t}\n\t}\n\n\tif sess != nil {\n\t\terr := mongo.WithSession(context.Background(), sess, func(sc context.Context) error {\n\t\t\treturn mt.DB.CreateCollection(sc, collName, cco)\n\t\t})\n\t\treturn err\n\t}\n\treturn mt.DB.CreateCollection(context.Background(), collName, cco)\n}\n\nfunc executeAdminCommand(mt *mtest.T, op *operation) {\n\t// Per the streamable hello test format description, a separate client must be used to execute this operation.\n\tclientOpts := options.Client().ApplyURI(mtest.ClusterURI())\n\tintegtest.AddTestServerAPIVersion(clientOpts)\n\tclient, err := mongo.Connect(clientOpts)\n\tassert.Nil(mt, err, \"Connect error: %v\", err)\n\tdefer func() {\n\t\t_ = client.Disconnect(context.Background())\n\t}()\n\n\tcmd := op.Arguments.Lookup(\"command\").Document()\n\tif op.CommandName == \"replSetStepDown\" {\n\t\t// replSetStepDown can fail with transient errors, so we use executeAdminCommandWithRetry to handle them and\n\t\t// retry until a timeout is hit.\n\t\texecuteAdminCommandWithRetry(mt, client, cmd)\n\t\treturn\n\t}\n\n\trco := options.RunCmd()\n\trpVal, err := op.Arguments.LookupErr(\"readPreference\")\n\tif err == nil {\n\t\tvar temp unified.ReadPreference\n\t\terr = bson.Unmarshal(rpVal.Document(), &temp)\n\t\tassert.Nil(mt, err, \"error unmarshalling readPreference option: %v\", err)\n\n\t\trp, err := temp.ToReadPrefOption()\n\t\tassert.Nil(mt, err, \"error creating readpref.ReadPref object: %v\", err)\n\t\trco.SetReadPreference(rp)\n\t}\n\n\tdb := client.Database(\"admin\")\n\terr = db.RunCommand(context.Background(), cmd, rco).Err()\n\tassert.Nil(mt, err, \"RunCommand error for command %q: %v\", op.CommandName, err)\n}\n\nfunc executeAdminCommandWithRetry(\n\tmt *mtest.T,\n\tclient *mongo.Client,\n\tcmd any,\n\topts ...options.Lister[options.RunCmdOptions],\n) {\n\tmt.Helper()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)\n\tdefer cancel()\n\n\tfor {\n\t\terr := client.Database(\"admin\").RunCommand(ctx, cmd, opts...).Err()\n\t\tif err == nil {\n\t\t\treturn\n\t\t}\n\n\t\tif ce, ok := err.(mongo.CommandError); ok && ce.Code == errorLockTimeout {\n\t\t\tcontinue\n\t\t}\n\t\tmt.Fatalf(\"error executing command: %v\", err)\n\t}\n}\n\n// verification function to use for all count operations\nfunc verifyCountResult(mt *mtest.T, actualResult int64, expectedResult any) {\n\tmt.Helper()\n\tif expectedResult == nil {\n\t\treturn\n\t}\n\n\texpected := getIntFromInterface(expectedResult)\n\tassert.NotNil(mt, expected, \"unexpected type for estimatedDocumentCount result: %T\", expectedResult)\n\tassert.Equal(mt, *expected, actualResult, \"count mismatch; expected %v, got %v\", *expected, actualResult)\n}\n\nfunc verifyBulkWriteResult(mt *mtest.T, actualResult *mongo.BulkWriteResult, expectedResult any) {\n\tmt.Helper()\n\n\tif expectedResult == nil {\n\t\treturn\n\t}\n\n\tvar expected struct {\n\t\tInsertedCount int64          `bson:\"insertedCount\"`\n\t\tMatchedCount  int64          `bson:\"matchedCount\"`\n\t\tModifiedCount int64          `bson:\"modifiedCount\"`\n\t\tDeletedCount  int64          `bson:\"deletedCount\"`\n\t\tUpsertedCount int64          `bson:\"upsertedCount\"`\n\t\tUpsertedIDs   map[string]any `bson:\"upsertedIds\"`\n\t}\n\terr := bson.Unmarshal(expectedResult.(bson.Raw), &expected)\n\tassert.Nil(mt, err, \"error creating BulkWriteResult: %v\", err)\n\n\tassert.Equal(mt, expected.InsertedCount, actualResult.InsertedCount,\n\t\t\"InsertedCount mismatch; expected %v, got %v\", expected.InsertedCount, actualResult.InsertedCount)\n\tassert.Equal(mt, expected.MatchedCount, actualResult.MatchedCount,\n\t\t\"MatchedCount mismatch; expected %v, got %v\", expected.MatchedCount, actualResult.MatchedCount)\n\tassert.Equal(mt, expected.ModifiedCount, actualResult.ModifiedCount,\n\t\t\"ModifiedCount mismatch; expected %v, got %v\", expected.ModifiedCount, actualResult.ModifiedCount)\n\tassert.Equal(mt, expected.DeletedCount, actualResult.DeletedCount,\n\t\t\"DeletedCount mismatch; expected %v, got %v\", expected.DeletedCount, actualResult.DeletedCount)\n\tassert.Equal(mt, expected.UpsertedCount, actualResult.UpsertedCount,\n\t\t\"UpsertedCount mismatch; expected %v, got %v\", expected.UpsertedCount, actualResult.UpsertedCount)\n\n\tfor idxStr, expectedID := range expected.UpsertedIDs {\n\t\tidx, err := strconv.Atoi(idxStr)\n\t\tassert.Nil(mt, err, \"error converted index %v to int\", idxStr)\n\n\t\tactualID, ok := actualResult.UpsertedIDs[int64(idx)]\n\t\tassert.True(mt, ok, \"operation index %v not found in actual upserted IDs map\", idx)\n\t\tassert.Equal(mt, expectedID, actualID,\n\t\t\t\"upserted ID mismatch for key %v; expected %v, got %v\", idx, expectedID, actualID)\n\t}\n}\n\nfunc verifyUpdateResult(mt *mtest.T, res *mongo.UpdateResult, result any) {\n\tmt.Helper()\n\n\tif result == nil {\n\t\treturn\n\t}\n\n\tvar expected struct {\n\t\tMatchedCount  int64 `bson:\"matchedCount\"`\n\t\tModifiedCount int64 `bson:\"modifiedCount\"`\n\t\tUpsertedCount int64 `bson:\"upsertedCount\"`\n\t}\n\terr := bson.Unmarshal(result.(bson.Raw), &expected)\n\tassert.Nil(mt, err, \"error creating UpdateResult: %v\", err)\n\n\tassert.Equal(mt, expected.MatchedCount, res.MatchedCount,\n\t\t\"matched count mismatch; expected %v, got %v\", expected.MatchedCount, res.MatchedCount)\n\tassert.Equal(mt, expected.ModifiedCount, res.ModifiedCount,\n\t\t\"modified count mismatch; expected %v, got %v\", expected.ModifiedCount, res.ModifiedCount)\n\n\tactualUpsertedCount := int64(0)\n\tif res.UpsertedID != nil {\n\t\tactualUpsertedCount = 1\n\t}\n\tassert.Equal(mt, expected.UpsertedCount, actualUpsertedCount,\n\t\t\"upserted count mismatch; expected %v, got %v\", expected.UpsertedCount, actualUpsertedCount)\n}\n\nfunc verifyDeleteResult(mt *mtest.T, res *mongo.DeleteResult, result any) {\n\tmt.Helper()\n\n\tif result == nil {\n\t\treturn\n\t}\n\n\tvar expected struct {\n\t\tDeletedCount int64 `bson:\"deletedCount\"`\n\t}\n\terr := bson.Unmarshal(result.(bson.Raw), &expected)\n\tassert.Nil(mt, err, \"error creating Delete result: %v\", err)\n\tassert.Equal(mt, expected.DeletedCount, res.DeletedCount,\n\t\t\"deleted count mismatch; expected %v, got %v\", expected.DeletedCount, res.DeletedCount)\n}\n\nfunc verifyDistinctResult(\n\tmt *mtest.T,\n\tgot bson.RawArray,\n\twant any,\n) {\n\tmt.Helper()\n\n\tif got == nil {\n\t\treturn\n\t}\n\n\tassert.NotNil(mt, want, \"expected want to be non-nil\")\n\n\tarr, ok := want.(bson.A)\n\tassert.True(mt, ok, \"expected want to be a BSON array\")\n\n\tfor i, iwant := range arr {\n\t\tgotRawValue := got.Index(uint(i))\n\n\t\tiwantType, iwantBytes, err := bson.MarshalValue(iwant)\n\t\tassert.NoError(mt, err)\n\n\t\twantRawValue := bson.RawValue{\n\t\t\tType:  iwantType,\n\t\t\tValue: iwantBytes,\n\t\t}\n\n\t\tassert.EqualValues(mt, wantRawValue, gotRawValue, \"expected value %v but got %v\", wantRawValue, gotRawValue)\n\t}\n}\n\nfunc verifyInsertOneResult(mt *mtest.T, actualResult *mongo.InsertOneResult, expectedResult any) {\n\tmt.Helper()\n\n\tif expectedResult == nil {\n\t\treturn\n\t}\n\n\tvar expected mongo.InsertOneResult\n\terr := bson.Unmarshal(expectedResult.(bson.Raw), &expected)\n\tassert.Nil(mt, err, \"error creating InsertOne result: %v\", err)\n\n\texpectedID := expected.InsertedID\n\tif f, ok := expectedID.(float64); ok && f == math.Floor(f) {\n\t\texpectedID = int32(f)\n\t}\n\n\tif expectedID != nil {\n\t\tassert.NotNil(mt, actualResult, \"expected result but got nil\")\n\t\tassert.Equal(mt, expectedID, actualResult.InsertedID,\n\t\t\t\"inserted ID mismatch; expected %v, got %v\", expectedID, actualResult.InsertedID)\n\t}\n}\n\nfunc verifyInsertManyResult(mt *mtest.T, actualResult *mongo.InsertManyResult, expectedResult any) {\n\tmt.Helper()\n\n\tif expectedResult == nil {\n\t\treturn\n\t}\n\n\tassert.NotNil(mt, actualResult, \"expected InsertMany result %v but got nil\", expectedResult)\n\tvar expected struct{ InsertedIDs map[string]any }\n\terr := bson.Unmarshal(expectedResult.(bson.Raw), &expected)\n\tassert.Nil(mt, err, \"error creating expected InsertMany result: %v\", err)\n\n\tfor _, val := range expected.InsertedIDs {\n\t\tvar found bool\n\t\tfor _, inserted := range actualResult.InsertedIDs {\n\t\t\tif val == inserted {\n\t\t\t\tfound = true\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\n\t\tassert.True(mt, found, \"expected to find ID %v in %v\", val, actualResult.InsertedIDs)\n\t}\n}\n\nfunc verifyListDatabasesResult(mt *mtest.T, actualResult mongo.ListDatabasesResult, expectedResult any) {\n\tmt.Helper()\n\n\tif expectedResult == nil {\n\t\treturn\n\t}\n\n\tvar expected mongo.ListDatabasesResult\n\terr := bson.Unmarshal(expectedResult.(bson.Raw), &expected)\n\tassert.Nil(mt, err, \"error creating ListDatabasesResult result: %v\", err)\n\n\tassert.Equal(mt, expected, actualResult, \"ListDatabasesResult mismatch; expected %v, got %v\", expected, actualResult)\n}\n\nfunc verifyCursorResult(mt *mtest.T, cur *mongo.Cursor, result any) {\n\tmt.Helper()\n\n\t// The Atlas Data Lake tests expect a getMore to be sent even though the operation does not have a Result field.\n\t// To account for this, we fetch all documents via cursor.All and then compare them to the result if it's non-nil.\n\tassert.NotNil(mt, cur, \"expected cursor to not be nil\")\n\tvar actual []bson.Raw\n\terr := cur.All(context.Background(), &actual)\n\tassert.Nil(mt, err, \"All error: %v\", err)\n\n\tif result == nil {\n\t\treturn\n\t}\n\n\tresultsArray := result.(bson.A)\n\tassert.Equal(mt, len(resultsArray), len(actual), \"expected %d documents from cursor, got %d\", len(resultsArray),\n\t\tlen(actual))\n\tfor i, expected := range resultsArray {\n\t\terr := compareDocs(mt, expected.(bson.Raw), actual[i])\n\t\tassert.Nil(mt, err, \"cursor document mismatch at index %d: %v\", i, err)\n\t}\n}\n\nfunc verifySingleResult(\n\tmt *mtest.T,\n\tactualResult *mongo.SingleResult,\n\texpectedResult any,\n) {\n\tmt.Helper()\n\n\tif expectedResult == nil {\n\t\treturn\n\t}\n\n\texpected := expectedResult.(bson.Raw)\n\tactual, _ := actualResult.Raw()\n\tif err := compareDocs(mt, expected, actual); err != nil {\n\t\tmt.Fatalf(\"SingleResult document mismatch: %s\", err)\n\t}\n}\n"
  },
  {
    "path": "internal/integration/crud_prose_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage integration\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"errors\"\n\t\"os\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/event\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/failpoint\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/integration/mtest\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/require\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/writeconcern\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver\"\n)\n\nfunc TestWriteErrorsWithLabels(t *testing.T) {\n\tclientOpts := options.Client().SetRetryWrites(false).SetWriteConcern(mtest.MajorityWc).\n\t\tSetReadConcern(mtest.MajorityRc)\n\tmtOpts := mtest.NewOptions().ClientOptions(clientOpts).MinServerVersion(\"4.0\").Topologies(mtest.ReplicaSet).\n\t\tCreateClient(false)\n\tmt := mtest.New(t, mtOpts)\n\n\tlabel := \"ExampleError\"\n\tmt.Run(\"InsertMany errors with label\", func(mt *mtest.T) {\n\t\tmt.SetFailPoint(failpoint.FailPoint{\n\t\t\tConfigureFailPoint: \"failCommand\",\n\t\t\tMode: failpoint.Mode{\n\t\t\t\tTimes: 1,\n\t\t\t},\n\t\t\tData: failpoint.Data{\n\t\t\t\tFailCommands: []string{\"insert\"},\n\t\t\t\tWriteConcernError: &failpoint.WriteConcernError{\n\t\t\t\t\tCode:        100,\n\t\t\t\t\tErrorLabels: &[]string{label},\n\t\t\t\t},\n\t\t\t},\n\t\t})\n\n\t\t_, err := mt.Coll.InsertMany(context.Background(),\n\t\t\t[]any{\n\t\t\t\tbson.D{\n\t\t\t\t\t{\"a\", 1},\n\t\t\t\t},\n\t\t\t\tbson.D{\n\t\t\t\t\t{\"a\", 2},\n\t\t\t\t},\n\t\t\t})\n\t\tassert.NotNil(mt, err, \"expected non-nil error, got nil\")\n\n\t\twe, ok := err.(mongo.BulkWriteException)\n\t\tassert.True(mt, ok, \"expected mongo.BulkWriteException, got %T\", err)\n\t\tassert.True(mt, we.HasErrorLabel(label), \"expected error to have label: %v\", label)\n\t})\n\n\tmt.Run(\"WriteException with label\", func(mt *mtest.T) {\n\t\tmt.SetFailPoint(failpoint.FailPoint{\n\t\t\tConfigureFailPoint: \"failCommand\",\n\t\t\tMode: failpoint.Mode{\n\t\t\t\tTimes: 1,\n\t\t\t},\n\t\t\tData: failpoint.Data{\n\t\t\t\tFailCommands: []string{\"delete\"},\n\t\t\t\tWriteConcernError: &failpoint.WriteConcernError{\n\t\t\t\t\tCode:        100,\n\t\t\t\t\tErrorLabels: &[]string{label},\n\t\t\t\t},\n\t\t\t},\n\t\t})\n\n\t\t_, err := mt.Coll.DeleteMany(context.Background(), bson.D{{\"a\", 1}})\n\t\tassert.NotNil(mt, err, \"expected non-nil error, got nil\")\n\n\t\twe, ok := err.(mongo.WriteException)\n\t\tassert.True(mt, ok, \"expected mongo.WriteException, got %T\", err)\n\t\tassert.True(mt, we.HasErrorLabel(label), \"expected error to have label: %v\", label)\n\t})\n\n\tmt.Run(\"BulkWriteException with label\", func(mt *mtest.T) {\n\t\tmt.SetFailPoint(failpoint.FailPoint{\n\t\t\tConfigureFailPoint: \"failCommand\",\n\t\t\tMode: failpoint.Mode{\n\t\t\t\tTimes: 1,\n\t\t\t},\n\t\t\tData: failpoint.Data{\n\t\t\t\tFailCommands: []string{\"delete\"},\n\t\t\t\tWriteConcernError: &failpoint.WriteConcernError{\n\t\t\t\t\tCode:        100,\n\t\t\t\t\tErrorLabels: &[]string{label},\n\t\t\t\t},\n\t\t\t},\n\t\t})\n\n\t\tmodels := []mongo.WriteModel{\n\t\t\t&mongo.InsertOneModel{bson.D{{\"a\", 2}}},\n\t\t\t&mongo.DeleteOneModel{bson.D{{\"a\", 2}}, nil, nil},\n\t\t}\n\t\t_, err := mt.Coll.BulkWrite(context.Background(), models)\n\t\tassert.NotNil(mt, err, \"expected non-nil error, got nil\")\n\n\t\twe, ok := err.(mongo.BulkWriteException)\n\t\tassert.True(mt, ok, \"expected mongo.BulkWriteException, got %T\", err)\n\t\tassert.True(mt, we.HasErrorLabel(label), \"expected error to have label: %v\", label)\n\t})\n}\n\nfunc TestWriteErrorsDetails(t *testing.T) {\n\tclientOpts := options.Client().\n\t\tSetRetryWrites(false).\n\t\tSetWriteConcern(mtest.MajorityWc).\n\t\tSetReadConcern(mtest.MajorityRc)\n\tmtOpts := mtest.NewOptions().\n\t\tClientOptions(clientOpts).\n\t\tMinServerVersion(\"5.0\").\n\t\tTopologies(mtest.ReplicaSet, mtest.Single).\n\t\tCreateClient(false)\n\n\tmt := mtest.New(t, mtOpts)\n\n\tmt.Run(\"JSON Schema validation\", func(mt *mtest.T) {\n\t\t// Create a JSON Schema validator document that requires properties \"a\" and \"b\". Use it in\n\t\t// the collection creation options so that collections created for subtests have the JSON\n\t\t// Schema validator applied.\n\t\tvalidator := bson.M{\n\t\t\t\"$jsonSchema\": bson.M{\n\t\t\t\t\"bsonType\": \"object\",\n\t\t\t\t\"required\": []string{\"a\", \"b\"},\n\t\t\t\t\"properties\": bson.M{\n\t\t\t\t\t\"a\": bson.M{\"bsonType\": \"string\"},\n\t\t\t\t\t\"b\": bson.M{\"bsonType\": \"int\"},\n\t\t\t\t},\n\t\t\t},\n\t\t}\n\n\t\tcco := options.CreateCollection().SetValidator(validator)\n\t\tvalidatorOpts := mtest.NewOptions().CollectionCreateOptions(cco)\n\n\t\tcases := []struct {\n\t\t\tdesc                string\n\t\t\toperation           func(*mongo.Collection) error\n\t\t\texpectBulkError     bool\n\t\t\texpectedCommandName string\n\t\t}{\n\t\t\t{\n\t\t\t\tdesc: \"InsertOne schema validation errors should include Details\",\n\t\t\t\toperation: func(coll *mongo.Collection) error {\n\t\t\t\t\t// Try to insert a document that doesn't contain the required properties.\n\t\t\t\t\t_, err := coll.InsertOne(context.Background(), bson.D{{\"nope\", 1}})\n\t\t\t\t\treturn err\n\t\t\t\t},\n\t\t\t\texpectBulkError:     false,\n\t\t\t\texpectedCommandName: \"insert\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tdesc: \"InsertMany schema validation errors should include Details\",\n\t\t\t\toperation: func(coll *mongo.Collection) error {\n\t\t\t\t\t// Try to insert a document that doesn't contain the required properties.\n\t\t\t\t\t_, err := coll.InsertMany(context.Background(), []any{bson.D{{\"nope\", 1}}})\n\t\t\t\t\treturn err\n\t\t\t\t},\n\t\t\t\texpectBulkError:     true,\n\t\t\t\texpectedCommandName: \"insert\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tdesc: \"UpdateOne schema validation errors should include Details\",\n\t\t\t\toperation: func(coll *mongo.Collection) error {\n\t\t\t\t\t// Try to set \"a\" to be an int, which violates the string type requirement.\n\t\t\t\t\t_, err := coll.UpdateOne(\n\t\t\t\t\t\tcontext.Background(),\n\t\t\t\t\t\tbson.D{},\n\t\t\t\t\t\tbson.D{{\"$set\", bson.D{{\"a\", 1}}}})\n\t\t\t\t\treturn err\n\t\t\t\t},\n\t\t\t\texpectBulkError:     false,\n\t\t\t\texpectedCommandName: \"update\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tdesc: \"UpdateMany schema validation errors should include Details\",\n\t\t\t\toperation: func(coll *mongo.Collection) error {\n\t\t\t\t\t// Try to set \"a\" to be an int in all documents in the collection, which violates\n\t\t\t\t\t// the string type requirement.\n\t\t\t\t\t_, err := coll.UpdateMany(\n\t\t\t\t\t\tcontext.Background(),\n\t\t\t\t\t\tbson.D{},\n\t\t\t\t\t\tbson.D{{\"$set\", bson.D{{\"a\", 1}}}})\n\t\t\t\t\treturn err\n\t\t\t\t},\n\t\t\t\texpectBulkError:     false,\n\t\t\t\texpectedCommandName: \"update\",\n\t\t\t},\n\t\t}\n\n\t\tfor _, tc := range cases {\n\t\t\tmt.RunOpts(tc.desc, validatorOpts, func(mt *mtest.T) {\n\t\t\t\t// Insert two valid documents so that the Update* tests can try to update them.\n\t\t\t\t{\n\t\t\t\t\t_, err := mt.Coll.InsertMany(\n\t\t\t\t\t\tcontext.Background(),\n\t\t\t\t\t\t[]any{\n\t\t\t\t\t\t\tbson.D{{\"a\", \"str1\"}, {\"b\", 1}},\n\t\t\t\t\t\t\tbson.D{{\"a\", \"str2\"}, {\"b\", 2}},\n\t\t\t\t\t\t})\n\t\t\t\t\tassert.Nil(mt, err, \"unexpected error inserting valid documents: %s\", err)\n\t\t\t\t}\n\n\t\t\t\terr := tc.operation(mt.Coll)\n\t\t\t\tassert.NotNil(mt, err, \"expected an error from calling the operation\")\n\t\t\t\tsErr := err.(mongo.ServerError)\n\t\t\t\tassert.True(\n\t\t\t\t\tmt,\n\t\t\t\t\tsErr.HasErrorCode(121),\n\t\t\t\t\t\"expected mongo.ServerError to have error code 121 (DocumentValidationFailure)\")\n\n\t\t\t\tvar details bson.Raw\n\t\t\t\tif tc.expectBulkError {\n\t\t\t\t\tbwe, ok := err.(mongo.BulkWriteException)\n\t\t\t\t\tassert.True(\n\t\t\t\t\t\tmt,\n\t\t\t\t\t\tok,\n\t\t\t\t\t\t\"expected error to be type mongo.BulkWriteException, got type %T (error %q)\",\n\t\t\t\t\t\terr,\n\t\t\t\t\t\terr)\n\t\t\t\t\t// Assert that there is one WriteError and that the Details field is populated.\n\t\t\t\t\tassert.Equal(\n\t\t\t\t\t\tmt,\n\t\t\t\t\t\t1,\n\t\t\t\t\t\tlen(bwe.WriteErrors),\n\t\t\t\t\t\t\"expected exactly 1 write error, but got %d write errors (error %q)\",\n\t\t\t\t\t\tlen(bwe.WriteErrors),\n\t\t\t\t\t\terr)\n\t\t\t\t\tdetails = bwe.WriteErrors[0].Details\n\t\t\t\t} else {\n\t\t\t\t\twe, ok := err.(mongo.WriteException)\n\t\t\t\t\tassert.True(\n\t\t\t\t\t\tmt,\n\t\t\t\t\t\tok,\n\t\t\t\t\t\t\"expected error to be type mongo.WriteException, got type %T (error %q)\",\n\t\t\t\t\t\terr,\n\t\t\t\t\t\terr)\n\t\t\t\t\t// Assert that there is one WriteError and that the Details field is populated.\n\t\t\t\t\tassert.Equal(\n\t\t\t\t\t\tmt,\n\t\t\t\t\t\t1,\n\t\t\t\t\t\tlen(we.WriteErrors),\n\t\t\t\t\t\t\"expected exactly 1 write error, but got %d write errors (error %q)\",\n\t\t\t\t\t\tlen(we.WriteErrors),\n\t\t\t\t\t\terr)\n\t\t\t\t\tdetails = we.WriteErrors[0].Details\n\t\t\t\t}\n\n\t\t\t\tassert.True(\n\t\t\t\t\tmt,\n\t\t\t\t\tlen(details) > 0,\n\t\t\t\t\t\"expected WriteError.Details to be populated, but is empty\")\n\n\t\t\t\t// Assert that the most recent CommandSucceededEvent was triggered by the expected\n\t\t\t\t// operation and contains the resulting write errors and that\n\t\t\t\t// \"writeErrors[0].errInfo\" is the same as \"WriteException.WriteErrors[0].Details\".\n\t\t\t\tevts := mt.GetAllSucceededEvents()\n\t\t\t\tassert.True(\n\t\t\t\t\tmt,\n\t\t\t\t\tlen(evts) >= 2,\n\t\t\t\t\t\"expected there to be at least 2 CommandSucceededEvent recorded\")\n\t\t\t\tevt := evts[len(evts)-1]\n\t\t\t\tassert.Equal(\n\t\t\t\t\tmt,\n\t\t\t\t\ttc.expectedCommandName,\n\t\t\t\t\tevt.CommandName,\n\t\t\t\t\t\"expected the last CommandSucceededEvent to be for %q, was %q\",\n\t\t\t\t\ttc.expectedCommandName,\n\t\t\t\t\tevt.CommandName)\n\t\t\t\terrInfo, ok := evt.Reply.Lookup(\"writeErrors\", \"0\", \"errInfo\").DocumentOK()\n\t\t\t\tassert.True(\n\t\t\t\t\tmt,\n\t\t\t\t\tok,\n\t\t\t\t\t\"expected evt.Reply to contain writeErrors[0].errInfo but doesn't (evt.Reply = %v)\",\n\t\t\t\t\tevt.Reply)\n\t\t\t\tassert.Equal(mt, details, errInfo, \"want %v, got %v\", details, errInfo)\n\t\t\t})\n\t\t}\n\t})\n}\n\nfunc TestHintErrors(t *testing.T) {\n\tmtOpts := mtest.NewOptions().MaxServerVersion(\"3.2\").CreateClient(false)\n\tmt := mtest.New(t, mtOpts)\n\n\texpected := errors.New(\"the 'hint' command parameter requires a minimum server wire version of 5\")\n\tmt.Run(\"UpdateMany\", func(mt *mtest.T) {\n\t\t_, got := mt.Coll.UpdateMany(context.Background(), bson.D{{\"a\", 1}}, bson.D{{\"$inc\", bson.D{{\"a\", 1}}}},\n\t\t\toptions.UpdateMany().SetHint(\"_id_\"))\n\t\tassert.NotNil(mt, got, \"expected non-nil error, got nil\")\n\t\tassert.Equal(mt, got, expected, \"expected: %v got: %v\", expected, got)\n\t})\n\n\tmt.Run(\"ReplaceOne\", func(mt *mtest.T) {\n\t\t_, got := mt.Coll.ReplaceOne(context.Background(), bson.D{{\"a\", 1}}, bson.D{{\"a\", 2}},\n\t\t\toptions.Replace().SetHint(\"_id_\"))\n\t\tassert.NotNil(mt, got, \"expected non-nil error, got nil\")\n\t\tassert.Equal(mt, got, expected, \"expected: %v got: %v\", expected, got)\n\t})\n\n\tmt.Run(\"BulkWrite\", func(mt *mtest.T) {\n\t\tmodels := []mongo.WriteModel{\n\t\t\t&mongo.InsertOneModel{bson.D{{\"_id\", 2}}},\n\t\t\t&mongo.ReplaceOneModel{Filter: bson.D{{\"_id\", 2}}, Replacement: bson.D{{\"a\", 2}}, Hint: \"_id_\"},\n\t\t}\n\t\t_, got := mt.Coll.BulkWrite(context.Background(), models)\n\t\tassert.NotNil(mt, got, \"expected non-nil error, got nil\")\n\t\tassert.Equal(mt, got, expected, \"expected: %v got: %v\", expected, got)\n\t})\n}\n\nfunc TestWriteConcernError(t *testing.T) {\n\tmt := mtest.New(t, noClientOpts)\n\n\terrInfoOpts := mtest.NewOptions().MinServerVersion(\"4.0\").Topologies(mtest.ReplicaSet)\n\tmt.RunOpts(\"errInfo is propagated\", errInfoOpts, func(mt *mtest.T) {\n\t\twcDoc := bsoncore.BuildDocumentFromElements(nil,\n\t\t\tbsoncore.AppendInt32Element(nil, \"w\", 2),\n\t\t\tbsoncore.AppendInt32Element(nil, \"wtimeout\", 0),\n\t\t\tbsoncore.AppendStringElement(nil, \"provenance\", \"clientSupplied\"),\n\t\t)\n\t\terrInfoDoc := bsoncore.BuildDocumentFromElements(nil,\n\t\t\tbsoncore.AppendDocumentElement(nil, \"writeConcern\", wcDoc),\n\t\t)\n\t\tfp := failpoint.FailPoint{\n\t\t\tConfigureFailPoint: \"failCommand\",\n\t\t\tMode: failpoint.Mode{\n\t\t\t\tTimes: 1,\n\t\t\t},\n\t\t\tData: failpoint.Data{\n\t\t\t\tFailCommands: []string{\"insert\"},\n\t\t\t\tWriteConcernError: &failpoint.WriteConcernError{\n\t\t\t\t\tCode:    100,\n\t\t\t\t\tName:    \"UnsatisfiableWriteConcern\",\n\t\t\t\t\tErrmsg:  \"Not enough data-bearing nodes\",\n\t\t\t\t\tErrInfo: errInfoDoc,\n\t\t\t\t},\n\t\t\t},\n\t\t}\n\t\tmt.SetFailPoint(fp)\n\n\t\t_, err := mt.Coll.InsertOne(context.Background(), bson.D{{\"x\", 1}})\n\t\tassert.NotNil(mt, err, \"expected InsertOne error, got nil\")\n\t\twriteException, ok := err.(mongo.WriteException)\n\t\tassert.True(mt, ok, \"expected WriteException, got error %v of type %T\", err, err)\n\t\twcError := writeException.WriteConcernError\n\t\tassert.NotNil(mt, wcError, \"expected write-concern error, got %v\", err)\n\t\tassert.True(mt, bytes.Equal(wcError.Details, errInfoDoc), \"expected errInfo document %v, got %v\",\n\t\t\tbson.Raw(errInfoDoc), wcError.Details)\n\t})\n}\n\nfunc TestErrorsCodeNamePropagated(t *testing.T) {\n\t// Ensure the codeName field is propagated for both command and write concern errors.\n\n\tmtOpts := mtest.NewOptions().\n\t\tTopologies(mtest.ReplicaSet).\n\t\tCreateClient(false)\n\tmt := mtest.New(t, mtOpts)\n\n\tmt.RunOpts(\"command error\", mtest.NewOptions().MinServerVersion(\"3.4\"), func(mt *mtest.T) {\n\t\t// codeName is propagated in an ok:0 error.\n\n\t\tcmd := bson.D{\n\t\t\t{\"insert\", mt.Coll.Name()},\n\t\t\t{\"documents\", []bson.D{}},\n\t\t}\n\t\terr := mt.DB.RunCommand(context.Background(), cmd).Err()\n\t\tassert.NotNil(mt, err, \"expected RunCommand error, got nil\")\n\n\t\tce, ok := err.(mongo.CommandError)\n\t\tassert.True(mt, ok, \"expected error of type %T, got %v of type %T\", mongo.CommandError{}, err, err)\n\t\texpectedCodeName := \"InvalidLength\"\n\t\tassert.Equal(mt, expectedCodeName, ce.Name, \"expected error code name %q, got %q\", expectedCodeName, ce.Name)\n\t})\n\n\twcCollOpts := options.Collection().\n\t\tSetWriteConcern(impossibleWc)\n\twcMtOpts := mtest.NewOptions().\n\t\tCollectionOptions(wcCollOpts)\n\tmt.RunOpts(\"write concern error\", wcMtOpts, func(mt *mtest.T) {\n\t\t// codeName is propagated for write concern errors.\n\n\t\t_, err := mt.Coll.InsertOne(context.Background(), bson.D{})\n\t\tassert.NotNil(mt, err, \"expected InsertOne error, got nil\")\n\n\t\twe, ok := err.(mongo.WriteException)\n\t\tassert.True(mt, ok, \"expected error of type %T, got %v of type %T\", mongo.WriteException{}, err, err)\n\t\twce := we.WriteConcernError\n\t\tassert.NotNil(mt, wce, \"expected write concern error, got %v\", we)\n\n\t\tvar expectedCodeName string\n\t\tif codeNameVal, err := mt.GetSucceededEvent().Reply.LookupErr(\"writeConcernError\", \"codeName\"); err == nil {\n\t\t\texpectedCodeName = codeNameVal.StringValue()\n\t\t}\n\n\t\tassert.Equal(mt, expectedCodeName, wce.Name, \"expected code name %q, got %q\", expectedCodeName, wce.Name)\n\t})\n}\n\nfunc TestClientBulkWriteProse(t *testing.T) {\n\tmtOpts := mtest.NewOptions().MinServerVersion(\"8.0\").ClientType(mtest.Pinned)\n\tmt := mtest.New(t, mtOpts)\n\n\tmt.Run(\"3. MongoClient.bulkWrite batch splits a writeModels input with greater than maxWriteBatchSize operations\", func(mt *mtest.T) {\n\t\tvar opsCnt []int\n\t\tmonitor := &event.CommandMonitor{\n\t\t\tStarted: func(_ context.Context, e *event.CommandStartedEvent) {\n\t\t\t\tif e.CommandName == \"bulkWrite\" {\n\t\t\t\t\tvar c struct {\n\t\t\t\t\t\tOps []bson.D\n\t\t\t\t\t}\n\t\t\t\t\terr := bson.Unmarshal(e.Command, &c)\n\t\t\t\t\trequire.NoError(mt, err)\n\t\t\t\t\topsCnt = append(opsCnt, len(c.Ops))\n\t\t\t\t}\n\t\t\t},\n\t\t}\n\t\tmt.ResetClient(options.Client().SetMonitor(monitor))\n\t\tvar hello struct {\n\t\t\tMaxWriteBatchSize int\n\t\t}\n\t\terr := mt.DB.RunCommand(context.Background(), bson.D{{\"hello\", 1}}).Decode(&hello)\n\t\trequire.NoError(mt, err, \"Hello error: %v\", err)\n\t\tvar writes []mongo.ClientBulkWrite\n\t\tnum := hello.MaxWriteBatchSize + 1\n\t\tfor i := 0; i < num; i++ {\n\t\t\twrites = append(writes, mongo.ClientBulkWrite{\n\t\t\t\tDatabase:   \"db\",\n\t\t\t\tCollection: \"coll\",\n\t\t\t\tModel: &mongo.ClientInsertOneModel{\n\t\t\t\t\tDocument: bson.D{{\"a\", \"b\"}},\n\t\t\t\t},\n\t\t\t})\n\t\t}\n\t\tresult, err := mt.Client.BulkWrite(context.Background(), writes)\n\t\trequire.NoError(mt, err, \"BulkWrite error: %v\", err)\n\t\tassert.Equal(mt, num, int(result.InsertedCount), \"expected InsertedCount: %d, got %d\", num, result.InsertedCount)\n\t\trequire.Len(mt, opsCnt, 2, \"expected %d bulkWrite commands, got: %d\", 2, len(opsCnt))\n\t\tassert.Equal(mt, num-1, opsCnt[0], \"expected %d firstEvent.command.ops, got: %d\", num-1, opsCnt[0])\n\t\tassert.Equal(mt, 1, opsCnt[1], \"expected %d secondEvent.command.ops, got: %d\", 1, opsCnt[1])\n\t})\n\n\tmt.Run(\"4. MongoClient.bulkWrite batch splits when an ops payload exceeds maxMessageSizeBytes\", func(mt *mtest.T) {\n\t\tvar opsCnt []int\n\t\tmonitor := &event.CommandMonitor{\n\t\t\tStarted: func(_ context.Context, e *event.CommandStartedEvent) {\n\t\t\t\tif e.CommandName == \"bulkWrite\" {\n\t\t\t\t\tvar c struct {\n\t\t\t\t\t\tOps []bson.D\n\t\t\t\t\t}\n\t\t\t\t\terr := bson.Unmarshal(e.Command, &c)\n\t\t\t\t\trequire.NoError(mt, err)\n\t\t\t\t\topsCnt = append(opsCnt, len(c.Ops))\n\t\t\t\t}\n\t\t\t},\n\t\t}\n\t\tmt.ResetClient(options.Client().SetMonitor(monitor))\n\t\tvar hello struct {\n\t\t\tMaxBsonObjectSize   int\n\t\t\tMaxMessageSizeBytes int\n\t\t}\n\t\terr := mt.DB.RunCommand(context.Background(), bson.D{{\"hello\", 1}}).Decode(&hello)\n\t\trequire.NoError(mt, err, \"Hello error: %v\", err)\n\t\tvar writes []mongo.ClientBulkWrite\n\t\tnum := hello.MaxMessageSizeBytes/hello.MaxBsonObjectSize + 1\n\t\tfor i := 0; i < num; i++ {\n\t\t\twrites = append(writes, mongo.ClientBulkWrite{\n\t\t\t\tDatabase:   \"db\",\n\t\t\t\tCollection: \"coll\",\n\t\t\t\tModel: &mongo.ClientInsertOneModel{\n\t\t\t\t\tDocument: bson.D{{\"a\", strings.Repeat(\"b\", hello.MaxBsonObjectSize-500)}},\n\t\t\t\t},\n\t\t\t})\n\t\t}\n\t\tresult, err := mt.Client.BulkWrite(context.Background(), writes)\n\t\trequire.NoError(mt, err, \"BulkWrite error: %v\", err)\n\t\tassert.Equal(mt, num, int(result.InsertedCount), \"expected InsertedCount: %d, got: %d\", num, result.InsertedCount)\n\t\trequire.Len(mt, opsCnt, 2, \"expected %d bulkWrite commands, got: %d\", 2, len(opsCnt))\n\t\tassert.Equal(mt, num-1, opsCnt[0], \"expected %d firstEvent.command.ops, got: %d\", num-1, opsCnt[0])\n\t\tassert.Equal(mt, 1, opsCnt[1], \"expected %d secondEvent.command.ops, got: %d\", 1, opsCnt[1])\n\t})\n\n\t// TODO(GODRIVER-3328): FailPoints are not currently reliable on sharded\n\t// topologies. Allow running on sharded topologies once that is fixed.\n\tnoShardedOpts := mtest.NewOptions().Topologies(mtest.Single, mtest.ReplicaSet, mtest.LoadBalanced)\n\tmt.RunOpts(\"5. MongoClient.bulkWrite collects WriteConcernErrors across batches\", noShardedOpts, func(mt *mtest.T) {\n\t\tvar eventCnt int\n\t\tmonitor := &event.CommandMonitor{\n\t\t\tStarted: func(_ context.Context, e *event.CommandStartedEvent) {\n\t\t\t\tif e.CommandName == \"bulkWrite\" {\n\t\t\t\t\teventCnt++\n\t\t\t\t}\n\t\t\t},\n\t\t}\n\t\tmt.ResetClient(options.Client().SetRetryWrites(false).SetMonitor(monitor))\n\t\tvar hello struct {\n\t\t\tMaxWriteBatchSize int\n\t\t}\n\t\terr := mt.DB.RunCommand(context.Background(), bson.D{{\"hello\", 1}}).Decode(&hello)\n\t\trequire.NoError(mt, err, \"Hello error: %v\", err)\n\n\t\tmt.SetFailPoint(failpoint.FailPoint{\n\t\t\tConfigureFailPoint: \"failCommand\",\n\t\t\tMode: failpoint.Mode{\n\t\t\t\tTimes: 2,\n\t\t\t},\n\t\t\tData: failpoint.Data{\n\t\t\t\tFailCommands: []string{\"bulkWrite\"},\n\t\t\t\tWriteConcernError: &failpoint.WriteConcernError{\n\t\t\t\t\tCode:   91,\n\t\t\t\t\tErrmsg: \"Replication is being shut down\",\n\t\t\t\t},\n\t\t\t},\n\t\t})\n\n\t\tvar writes []mongo.ClientBulkWrite\n\t\tnum := hello.MaxWriteBatchSize + 1\n\t\tfor i := 0; i < num; i++ {\n\t\t\twrites = append(writes, mongo.ClientBulkWrite{\n\t\t\t\tDatabase:   \"db\",\n\t\t\t\tCollection: \"coll\",\n\t\t\t\tModel: &mongo.ClientInsertOneModel{\n\t\t\t\t\tDocument: bson.D{{\"a\", \"b\"}},\n\t\t\t\t},\n\t\t\t})\n\t\t}\n\t\t_, err = mt.Client.BulkWrite(context.Background(), writes)\n\t\trequire.Error(mt, err, \"expected a BulkWrite error\")\n\t\tbwe, ok := err.(mongo.ClientBulkWriteException)\n\t\trequire.True(mt, ok, \"expected a BulkWriteException, got %T: %v\", err, err)\n\t\tassert.Len(mt, bwe.WriteConcernErrors, 2, \"expected %d writeConcernErrors, got: %d\", 2, len(bwe.WriteConcernErrors))\n\t\trequire.NotNil(mt, bwe.PartialResult)\n\t\tassert.Equal(mt, num, int(bwe.PartialResult.InsertedCount),\n\t\t\t\"expected InsertedCount: %d, got: %d\", num, bwe.PartialResult.InsertedCount)\n\t\trequire.Equal(mt, 2, eventCnt, \"expected %d bulkWrite commands, got: %d\", 2, eventCnt)\n\t})\n\n\tmt.Run(\"6. MongoClient.bulkWrite handles individual WriteErrors across batches\", func(mt *mtest.T) {\n\t\tvar eventCnt int\n\t\tmonitor := &event.CommandMonitor{\n\t\t\tStarted: func(_ context.Context, e *event.CommandStartedEvent) {\n\t\t\t\tif e.CommandName == \"bulkWrite\" {\n\t\t\t\t\teventCnt++\n\t\t\t\t}\n\t\t\t},\n\t\t}\n\n\t\tmt.ResetClient(options.Client())\n\t\tvar hello struct {\n\t\t\tMaxWriteBatchSize int\n\t\t}\n\t\terr := mt.DB.RunCommand(context.Background(), bson.D{{\"hello\", 1}}).Decode(&hello)\n\t\trequire.NoError(mt, err, \"Hello error: %v\", err)\n\n\t\tcoll := mt.CreateCollection(mtest.Collection{DB: \"db\", Name: \"coll\"}, false)\n\t\terr = coll.Drop(context.Background())\n\t\trequire.NoError(mt, err, \"Drop error: %v\", err)\n\t\t_, err = coll.InsertOne(context.Background(), bson.D{{\"_id\", 1}})\n\t\trequire.NoError(mt, err, \"InsertOne error: %v\", err)\n\n\t\tvar writes []mongo.ClientBulkWrite\n\t\tnumModels := hello.MaxWriteBatchSize + 1\n\t\tfor i := 0; i < numModels; i++ {\n\t\t\twrites = append(writes, mongo.ClientBulkWrite{\n\t\t\t\tDatabase:   \"db\",\n\t\t\t\tCollection: \"coll\",\n\t\t\t\tModel: &mongo.ClientInsertOneModel{\n\t\t\t\t\tDocument: bson.D{{\"_id\", 1}},\n\t\t\t\t},\n\t\t\t})\n\t\t}\n\n\t\tmt.Run(\"unordered\", func(mt *mtest.T) {\n\t\t\teventCnt = 0\n\t\t\tmt.ResetClient(options.Client().SetMonitor(monitor))\n\t\t\t_, err := mt.Client.BulkWrite(context.Background(), writes, options.ClientBulkWrite().SetOrdered(false))\n\t\t\trequire.Error(mt, err, \"expected a BulkWrite error\")\n\t\t\tbwe, ok := err.(mongo.ClientBulkWriteException)\n\t\t\trequire.True(mt, ok, \"expected a BulkWriteException, got %T: %v\", err, err)\n\t\t\tassert.Len(mt, bwe.WriteErrors, numModels, \"expected %d writeErrors, got %d\", numModels, len(bwe.WriteErrors))\n\t\t\trequire.Equal(mt, 2, eventCnt, \"expected %d bulkWrite commands, got: %d\", 2, eventCnt)\n\t\t})\n\t\tmt.Run(\"ordered\", func(mt *mtest.T) {\n\t\t\teventCnt = 0\n\t\t\tmt.ResetClient(options.Client().SetMonitor(monitor))\n\t\t\t_, err := mt.Client.BulkWrite(context.Background(), writes, options.ClientBulkWrite().SetOrdered(true))\n\t\t\trequire.Error(mt, err, \"expected a BulkWrite error\")\n\t\t\tbwe, ok := err.(mongo.ClientBulkWriteException)\n\t\t\trequire.True(mt, ok, \"expected a BulkWriteException, got %T: %v\", err, err)\n\t\t\tassert.Len(mt, bwe.WriteErrors, 1, \"expected %d writeErrors, got: %d\", 1, len(bwe.WriteErrors))\n\t\t\trequire.Equal(mt, 1, eventCnt, \"expected %d bulkWrite commands, got: %d\", 1, eventCnt)\n\t\t})\n\t})\n\n\tmt.Run(\"7. MongoClient.bulkWrite handles a cursor requiring a getMore\", func(mt *mtest.T) {\n\t\tvar getMoreCalled int\n\t\tmonitor := &event.CommandMonitor{\n\t\t\tStarted: func(_ context.Context, e *event.CommandStartedEvent) {\n\t\t\t\tif e.CommandName == \"getMore\" {\n\t\t\t\t\tgetMoreCalled++\n\t\t\t\t}\n\t\t\t},\n\t\t}\n\t\tmt.ResetClient(options.Client().SetMonitor(monitor))\n\t\tvar hello struct {\n\t\t\tMaxBsonObjectSize int\n\t\t}\n\t\terr := mt.DB.RunCommand(context.Background(), bson.D{{\"hello\", 1}}).Decode(&hello)\n\t\trequire.NoError(mt, err, \"Hello error: %v\", err)\n\n\t\tcoll := mt.CreateCollection(mtest.Collection{DB: \"db\", Name: \"coll\"}, false)\n\t\terr = coll.Drop(context.Background())\n\t\trequire.NoError(mt, err, \"Drop error: %v\", err)\n\n\t\tupsert := true\n\t\tmodels := []mongo.ClientBulkWrite{\n\t\t\t{\n\t\t\t\tDatabase:   \"db\",\n\t\t\t\tCollection: \"coll\",\n\t\t\t\tModel: &mongo.ClientUpdateOneModel{\n\t\t\t\t\tFilter: bson.D{{\"_id\", strings.Repeat(\"a\", hello.MaxBsonObjectSize/2)}},\n\t\t\t\t\tUpdate: bson.D{{\"$set\", bson.D{{\"x\", 1}}}},\n\t\t\t\t\tUpsert: &upsert,\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tDatabase:   \"db\",\n\t\t\t\tCollection: \"coll\",\n\t\t\t\tModel: &mongo.ClientUpdateOneModel{\n\t\t\t\t\tFilter: bson.D{{\"_id\", strings.Repeat(\"b\", hello.MaxBsonObjectSize/2)}},\n\t\t\t\t\tUpdate: bson.D{{\"$set\", bson.D{{\"x\", 1}}}},\n\t\t\t\t\tUpsert: &upsert,\n\t\t\t\t},\n\t\t\t},\n\t\t}\n\t\tresult, err := mt.Client.BulkWrite(context.Background(), models, options.ClientBulkWrite().SetVerboseResults(true))\n\t\trequire.NoError(mt, err, \"BulkWrite error: %v\", err)\n\t\tassert.Equal(mt, int64(2), result.UpsertedCount, \"expected InsertedCount: %d, got: %d\", 2, result.UpsertedCount)\n\t\tassert.Len(mt, result.UpdateResults, 2, \"expected %d UpdateResults, got: %d\", 2, len(result.UpdateResults))\n\t\tassert.Equal(mt, 1, getMoreCalled, \"expected %d getMore call, got: %d\", 1, getMoreCalled)\n\t})\n\n\tmt.RunOpts(\"8. MongoClient.bulkWrite handles a cursor requiring getMore within a transaction\",\n\t\tmtest.NewOptions().MinServerVersion(\"8.0\").ClientType(mtest.Pinned).\n\t\t\tTopologies(mtest.ReplicaSet, mtest.Sharded, mtest.LoadBalanced, mtest.ShardedReplicaSet),\n\t\tfunc(mt *mtest.T) {\n\t\t\tvar getMoreCalled int\n\t\t\tmonitor := &event.CommandMonitor{\n\t\t\t\tStarted: func(_ context.Context, e *event.CommandStartedEvent) {\n\t\t\t\t\tif e.CommandName == \"getMore\" {\n\t\t\t\t\t\tgetMoreCalled++\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t}\n\t\t\tmt.ResetClient(options.Client().SetMonitor(monitor))\n\t\t\tvar hello struct {\n\t\t\t\tMaxBsonObjectSize int\n\t\t\t}\n\t\t\terr := mt.DB.RunCommand(context.Background(), bson.D{{\"hello\", 1}}).Decode(&hello)\n\t\t\trequire.NoError(mt, err, \"Hello error: %v\", err)\n\n\t\t\tcoll := mt.CreateCollection(mtest.Collection{DB: \"db\", Name: \"coll\"}, false)\n\t\t\terr = coll.Drop(context.Background())\n\t\t\trequire.NoError(mt, err, \"Drop error: %v\", err)\n\n\t\t\tsession, err := mt.Client.StartSession()\n\t\t\trequire.NoError(mt, err, \"StartSession error: %v\", err)\n\t\t\tdefer session.EndSession(context.Background())\n\n\t\t\tupsert := true\n\t\t\tmodels := []mongo.ClientBulkWrite{\n\t\t\t\t{\n\t\t\t\t\tDatabase:   \"db\",\n\t\t\t\t\tCollection: \"coll\",\n\t\t\t\t\tModel: &mongo.ClientUpdateOneModel{\n\t\t\t\t\t\tFilter: bson.D{{\"_id\", strings.Repeat(\"a\", hello.MaxBsonObjectSize/2)}},\n\t\t\t\t\t\tUpdate: bson.D{{\"$set\", bson.D{{\"x\", 1}}}},\n\t\t\t\t\t\tUpsert: &upsert,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tDatabase:   \"db\",\n\t\t\t\t\tCollection: \"coll\",\n\t\t\t\t\tModel: &mongo.ClientUpdateOneModel{\n\t\t\t\t\t\tFilter: bson.D{{\"_id\", strings.Repeat(\"b\", hello.MaxBsonObjectSize/2)}},\n\t\t\t\t\t\tUpdate: bson.D{{\"$set\", bson.D{{\"x\", 1}}}},\n\t\t\t\t\t\tUpsert: &upsert,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t}\n\t\t\tresult, err := session.WithTransaction(context.Background(), func(ctx context.Context) (any, error) {\n\t\t\t\treturn mt.Client.BulkWrite(ctx, models, options.ClientBulkWrite().SetVerboseResults(true))\n\t\t\t})\n\t\t\trequire.NoError(mt, err, \"BulkWrite error: %v\", err)\n\t\t\tcbwResult, ok := result.(*mongo.ClientBulkWriteResult)\n\t\t\trequire.True(mt, ok, \"expected a ClientBulkWriteResult, got %T\", result)\n\t\t\tassert.Equal(mt, int64(2), cbwResult.UpsertedCount, \"expected InsertedCount: %d, got: %d\", 2, cbwResult.UpsertedCount)\n\t\t\tassert.Len(mt, cbwResult.UpdateResults, 2, \"expected %d UpdateResults, got: %d\", 2, len(cbwResult.UpdateResults))\n\t\t\tassert.Equal(mt, 1, getMoreCalled, \"expected %d getMore call, got: %d\", 1, getMoreCalled)\n\t\t})\n\n\t// TODO(GODRIVER-3328): FailPoints are not currently reliable on sharded\n\t// topologies. Allow running on sharded topologies once that is fixed.\n\tmt.RunOpts(\"9. MongoClient.bulkWrite handles a getMore error\", noShardedOpts, func(mt *mtest.T) {\n\t\tvar getMoreCalled int\n\t\tvar killCursorsCalled int\n\t\tmonitor := &event.CommandMonitor{\n\t\t\tStarted: func(_ context.Context, e *event.CommandStartedEvent) {\n\t\t\t\tswitch e.CommandName {\n\t\t\t\tcase \"getMore\":\n\t\t\t\t\tgetMoreCalled++\n\t\t\t\tcase \"killCursors\":\n\t\t\t\t\tkillCursorsCalled++\n\t\t\t\t}\n\t\t\t},\n\t\t}\n\t\tmt.ResetClient(options.Client().SetMonitor(monitor))\n\t\tvar hello struct {\n\t\t\tMaxBsonObjectSize int\n\t\t}\n\t\terr := mt.DB.RunCommand(context.Background(), bson.D{{\"hello\", 1}}).Decode(&hello)\n\t\trequire.NoError(mt, err, \"Hello error: %v\", err)\n\n\t\tmt.SetFailPoint(failpoint.FailPoint{\n\t\t\tConfigureFailPoint: \"failCommand\",\n\t\t\tMode: failpoint.Mode{\n\t\t\t\tTimes: 1,\n\t\t\t},\n\t\t\tData: failpoint.Data{\n\t\t\t\tFailCommands: []string{\"getMore\"},\n\t\t\t\tErrorCode:    8,\n\t\t\t},\n\t\t})\n\n\t\tcoll := mt.CreateCollection(mtest.Collection{DB: \"db\", Name: \"coll\"}, false)\n\t\terr = coll.Drop(context.Background())\n\t\trequire.NoError(mt, err, \"Drop error: %v\", err)\n\n\t\tupsert := true\n\t\tmodels := []mongo.ClientBulkWrite{\n\t\t\t{\n\t\t\t\tDatabase:   \"db\",\n\t\t\t\tCollection: \"coll\",\n\t\t\t\tModel: &mongo.ClientUpdateOneModel{\n\t\t\t\t\tFilter: bson.D{{\"_id\", strings.Repeat(\"a\", hello.MaxBsonObjectSize/2)}},\n\t\t\t\t\tUpdate: bson.D{{\"$set\", bson.D{{\"x\", 1}}}},\n\t\t\t\t\tUpsert: &upsert,\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tDatabase:   \"db\",\n\t\t\t\tCollection: \"coll\",\n\t\t\t\tModel: &mongo.ClientUpdateOneModel{\n\t\t\t\t\tFilter: bson.D{{\"_id\", strings.Repeat(\"b\", hello.MaxBsonObjectSize/2)}},\n\t\t\t\t\tUpdate: bson.D{{\"$set\", bson.D{{\"x\", 1}}}},\n\t\t\t\t\tUpsert: &upsert,\n\t\t\t\t},\n\t\t\t},\n\t\t}\n\t\t_, err = mt.Client.BulkWrite(context.Background(), models, options.ClientBulkWrite().SetVerboseResults(true))\n\t\tassert.Error(mt, err, \"expected a BulkWrite error\")\n\t\tbwe, ok := err.(mongo.ClientBulkWriteException)\n\t\trequire.True(mt, ok, \"expected a BulkWriteException, got %T: %v\", err, err)\n\t\trequire.NotNil(mt, bwe.WriteError)\n\t\tassert.Equal(mt, 8, bwe.WriteError.Code, \"expected top level error code: %d, got; %d\", 8, bwe.WriteError.Code)\n\t\trequire.NotNil(mt, bwe.PartialResult)\n\t\tassert.Equal(mt, int64(2), bwe.PartialResult.UpsertedCount, \"expected UpsertedCount: %d, got: %d\", 2, bwe.PartialResult.UpsertedCount)\n\t\tassert.Len(mt, bwe.PartialResult.UpdateResults, 1, \"expected %d UpdateResults, got: %d\", 1, len(bwe.PartialResult.UpdateResults))\n\t\tassert.Equal(mt, 1, getMoreCalled, \"expected %d getMore call, got: %d\", 1, getMoreCalled)\n\t\tassert.Equal(mt, 1, killCursorsCalled, \"expected %d killCursors call, got: %d\", 1, killCursorsCalled)\n\t})\n\n\tmt.Run(\"11. MongoClient.bulkWrite batch splits when the addition of a new namespace exceeds the maximum message size\", func(mt *mtest.T) {\n\t\ttype cmd struct {\n\t\t\tOps    []bson.D\n\t\t\tNsInfo []struct {\n\t\t\t\tNs string\n\t\t\t}\n\t\t}\n\t\tvar bwCmd []cmd\n\t\tmonitor := &event.CommandMonitor{\n\t\t\tStarted: func(_ context.Context, e *event.CommandStartedEvent) {\n\t\t\t\tif e.CommandName == \"bulkWrite\" {\n\t\t\t\t\tvar c cmd\n\t\t\t\t\terr := bson.Unmarshal(e.Command, &c)\n\t\t\t\t\trequire.NoError(mt, err, \"Unmarshal error: %v\", err)\n\t\t\t\t\tbwCmd = append(bwCmd, c)\n\t\t\t\t}\n\t\t\t},\n\t\t}\n\t\tmt.ResetClient(options.Client())\n\t\tvar hello struct {\n\t\t\tMaxBsonObjectSize   int\n\t\t\tMaxMessageSizeBytes int\n\t\t}\n\t\terr := mt.DB.RunCommand(context.Background(), bson.D{{\"hello\", 1}}).Decode(&hello)\n\t\trequire.NoError(mt, err, \"Hello error: %v\", err)\n\n\t\tnewWrites := func() (int, []mongo.ClientBulkWrite) {\n\t\t\tmaxBsonObjectSize := hello.MaxBsonObjectSize\n\t\t\topsBytes := hello.MaxMessageSizeBytes - 1122\n\t\t\tnum := opsBytes / maxBsonObjectSize\n\n\t\t\tvar writes []mongo.ClientBulkWrite\n\t\t\tfor i := 0; i < num; i++ {\n\t\t\t\twrites = append(writes, mongo.ClientBulkWrite{\n\t\t\t\t\tDatabase:   \"db\",\n\t\t\t\t\tCollection: \"coll\",\n\t\t\t\t\tModel: &mongo.ClientInsertOneModel{\n\t\t\t\t\t\tDocument: bson.D{{\"a\", strings.Repeat(\"b\", maxBsonObjectSize-57)}},\n\t\t\t\t\t},\n\t\t\t\t})\n\t\t\t}\n\t\t\tif remainderBytes := opsBytes % maxBsonObjectSize; remainderBytes > 217 {\n\t\t\t\tnum++\n\t\t\t\twrites = append(writes, mongo.ClientBulkWrite{\n\t\t\t\t\tDatabase:   \"db\",\n\t\t\t\t\tCollection: \"coll\",\n\t\t\t\t\tModel: &mongo.ClientInsertOneModel{\n\t\t\t\t\t\tDocument: bson.D{{\"a\", strings.Repeat(\"b\", remainderBytes-57)}},\n\t\t\t\t\t},\n\t\t\t\t})\n\t\t\t}\n\t\t\treturn num, writes\n\t\t}\n\t\tmt.Run(\"Case 1: No batch-splitting required\", func(mt *mtest.T) {\n\t\t\tbwCmd = bwCmd[:0]\n\t\t\tmt.ResetClient(options.Client().SetMonitor(monitor))\n\n\t\t\tnum, writes := newWrites()\n\t\t\twrites = append(writes, mongo.ClientBulkWrite{\n\t\t\t\tDatabase:   \"db\",\n\t\t\t\tCollection: \"coll\",\n\t\t\t\tModel: &mongo.ClientInsertOneModel{\n\t\t\t\t\tDocument: bson.D{{\"a\", \"b\"}},\n\t\t\t\t},\n\t\t\t})\n\t\t\tresult, err := mt.Client.BulkWrite(context.Background(), writes)\n\t\t\trequire.NoError(mt, err, \"BulkWrite error: %v\", err)\n\t\t\tassert.Equal(mt, num+1, int(result.InsertedCount), \"expected insertedCound: %d, got: %d\", num+1, result.InsertedCount)\n\t\t\trequire.Len(mt, bwCmd, 1, \"expected %d bulkWrite call, got: %d\", 1, len(bwCmd))\n\n\t\t\tassert.Len(mt, bwCmd[0].Ops, num+1, \"expected %d ops, got: %d\", num+1, len(bwCmd[0].Ops))\n\t\t\trequire.Len(mt, bwCmd[0].NsInfo, 1, \"expected %d nsInfo, got: %d\", 1, len(bwCmd[0].NsInfo))\n\t\t\tassert.Equal(mt, \"db.coll\", bwCmd[0].NsInfo[0].Ns, \"expected namespace: %s, got: %s\", \"db.coll\", bwCmd[0].NsInfo[0].Ns)\n\t\t})\n\t\tmt.Run(\"Case 2: Batch-splitting required\", func(mt *mtest.T) {\n\t\t\tbwCmd = bwCmd[:0]\n\t\t\tmt.ResetClient(options.Client().SetMonitor(monitor))\n\n\t\t\tcoll := strings.Repeat(\"c\", 200)\n\t\t\tnum, writes := newWrites()\n\t\t\twrites = append(writes, mongo.ClientBulkWrite{\n\t\t\t\tDatabase:   \"db\",\n\t\t\t\tCollection: coll,\n\t\t\t\tModel: &mongo.ClientInsertOneModel{\n\t\t\t\t\tDocument: bson.D{{\"a\", \"b\"}},\n\t\t\t\t},\n\t\t\t})\n\t\t\tresult, err := mt.Client.BulkWrite(context.Background(), writes)\n\t\t\trequire.NoError(mt, err, \"BulkWrite error: %v\", err)\n\t\t\tassert.Equal(mt, num+1, int(result.InsertedCount), \"expected insertedCound: %d, got: %d\", num+1, result.InsertedCount)\n\t\t\trequire.Len(mt, bwCmd, 2, \"expected %d bulkWrite calls, got: %d\", 2, len(bwCmd))\n\n\t\t\tassert.Len(mt, bwCmd[0].Ops, num, \"expected %d ops, got: %d\", num, len(bwCmd[0].Ops))\n\t\t\trequire.Len(mt, bwCmd[0].NsInfo, 1, \"expected %d nsInfo, got: %d\", 1, len(bwCmd[0].NsInfo))\n\t\t\tassert.Equal(mt, \"db.coll\", bwCmd[0].NsInfo[0].Ns, \"expected namespace: %s, got: %s\", \"db.coll\", bwCmd[0].NsInfo[0].Ns)\n\n\t\t\tassert.Len(mt, bwCmd[1].Ops, 1, \"expected %d ops, got: %d\", 1, len(bwCmd[1].Ops))\n\t\t\trequire.Len(mt, bwCmd[1].NsInfo, 1, \"expected %d nsInfo, got: %d\", 1, len(bwCmd[1].NsInfo))\n\t\t\tassert.Equal(mt, \"db.\"+coll, bwCmd[1].NsInfo[0].Ns, \"expected namespace: %s, got: %s\", \"db.\"+coll, bwCmd[1].NsInfo[0].Ns)\n\t\t})\n\t})\n\n\tmt.Run(\"12. MongoClient.bulkWrite returns an error if no operations can be added to ops\", func(mt *mtest.T) {\n\t\tmt.ResetClient(options.Client())\n\t\tvar hello struct {\n\t\t\tMaxMessageSizeBytes int\n\t\t}\n\t\terr := mt.DB.RunCommand(context.Background(), bson.D{{\"hello\", 1}}).Decode(&hello)\n\t\trequire.NoError(mt, err, \"Hello error: %v\", err)\n\t\tmt.Run(\"Case 1: document too large\", func(mt *mtest.T) {\n\t\t\twrites := []mongo.ClientBulkWrite{{\n\t\t\t\tDatabase:   \"db\",\n\t\t\t\tCollection: \"coll\",\n\t\t\t\tModel: &mongo.ClientInsertOneModel{\n\t\t\t\t\tDocument: bson.D{{\"a\", strings.Repeat(\"b\", hello.MaxMessageSizeBytes)}},\n\t\t\t\t},\n\t\t\t}}\n\t\t\t_, err := mt.Client.BulkWrite(context.Background(), writes)\n\t\t\trequire.EqualError(mt, err, driver.ErrDocumentTooLarge.Error())\n\t\t\tvar cbwe mongo.ClientBulkWriteException\n\t\t\tif errors.As(err, &cbwe) {\n\t\t\t\tassert.Nil(mt, cbwe.PartialResult, \"expected nil PartialResult in ClientBulkWriteException\")\n\t\t\t}\n\t\t})\n\t\tmt.Run(\"Case 2: namespace too large\", func(mt *mtest.T) {\n\t\t\twrites := []mongo.ClientBulkWrite{{\n\t\t\t\tDatabase:   \"db\",\n\t\t\t\tCollection: strings.Repeat(\"c\", hello.MaxMessageSizeBytes),\n\t\t\t\tModel: &mongo.ClientInsertOneModel{\n\t\t\t\t\tDocument: bson.D{{\"a\", \"b\"}},\n\t\t\t\t},\n\t\t\t}}\n\t\t\t_, err := mt.Client.BulkWrite(context.Background(), writes)\n\t\t\trequire.EqualError(mt, err, driver.ErrDocumentTooLarge.Error())\n\t\t\tvar cbwe mongo.ClientBulkWriteException\n\t\t\tif errors.As(err, &cbwe) {\n\t\t\t\tassert.Nil(mt, cbwe.PartialResult, \"expected nil PartialResult in ClientBulkWriteException\")\n\t\t\t}\n\t\t})\n\t})\n\n\tmt.Run(\"13. MongoClient.bulkWrite returns an error if auto-encryption is configured\", func(mt *mtest.T) {\n\t\tif !mtest.IsCSFLEEnabled() {\n\t\t\tmt.Skip(\"CSFLE is not enabled\")\n\t\t}\n\t\tif os.Getenv(\"DOCKER_RUNNING\") != \"\" {\n\t\t\tmt.Skip(\"skipping test in docker environment\")\n\t\t}\n\n\t\tautoEncryptionOpts := options.AutoEncryption().\n\t\t\tSetKeyVaultNamespace(\"db.coll\").\n\t\t\tSetKmsProviders(map[string]map[string]any{\n\t\t\t\t\"aws\": {\n\t\t\t\t\t\"accessKeyId\":     \"foo\",\n\t\t\t\t\t\"secretAccessKey\": \"bar\",\n\t\t\t\t},\n\t\t\t})\n\t\tmt.ResetClient(options.Client().SetAutoEncryptionOptions(autoEncryptionOpts))\n\t\twrites := []mongo.ClientBulkWrite{{\n\t\t\tDatabase:   \"db\",\n\t\t\tCollection: \"coll\",\n\t\t\tModel: &mongo.ClientInsertOneModel{\n\t\t\t\tDocument: bson.D{{\"a\", \"b\"}},\n\t\t\t},\n\t\t}}\n\t\t_, err := mt.Client.BulkWrite(context.Background(), writes)\n\t\trequire.ErrorContains(mt, err, \"bulkWrite does not currently support automatic encryption\")\n\t\tvar cbwe mongo.ClientBulkWriteException\n\t\tif errors.As(err, &cbwe) {\n\t\t\tassert.Nil(mt, cbwe.PartialResult, \"expected nil PartialResult in ClientBulkWriteException\")\n\t\t}\n\t})\n\n\tmt.Run(\"15. MongoClient.bulkWrite with unacknowledged write concern uses w:0 for all batches\", func(mt *mtest.T) {\n\t\ttype cmd struct {\n\t\t\tOps          []bson.D\n\t\t\tWriteConcern struct {\n\t\t\t\tW any\n\t\t\t}\n\t\t}\n\t\tvar bwCmd []cmd\n\t\tmonitor := &event.CommandMonitor{\n\t\t\tStarted: func(_ context.Context, e *event.CommandStartedEvent) {\n\t\t\t\tif e.CommandName == \"bulkWrite\" {\n\t\t\t\t\tvar c cmd\n\t\t\t\t\terr := bson.Unmarshal(e.Command, &c)\n\t\t\t\t\trequire.NoError(mt, err, \"Unmarshal error: %v\", err)\n\n\t\t\t\t\tbwCmd = append(bwCmd, c)\n\t\t\t\t}\n\t\t\t},\n\t\t}\n\t\tmt.ResetClient(options.Client().SetMonitor(monitor))\n\t\tvar hello struct {\n\t\t\tMaxBsonObjectSize   int\n\t\t\tMaxMessageSizeBytes int\n\t\t}\n\t\terr := mt.DB.RunCommand(context.Background(), bson.D{{\"hello\", 1}}).Decode(&hello)\n\t\trequire.NoError(mt, err, \"Hello error: %v\", err)\n\n\t\tcoll := mt.CreateCollection(mtest.Collection{DB: \"db\", Name: \"coll\"}, false)\n\t\terr = coll.Drop(context.Background())\n\t\trequire.NoError(mt, err, \"Drop error: %v\", err)\n\n\t\tnum := hello.MaxMessageSizeBytes/hello.MaxBsonObjectSize + 1\n\t\tvar writes []mongo.ClientBulkWrite\n\t\tfor i := 0; i < num; i++ {\n\t\t\twrites = append(writes, mongo.ClientBulkWrite{\n\t\t\t\tDatabase:   \"db\",\n\t\t\t\tCollection: \"coll\",\n\t\t\t\tModel: &mongo.ClientInsertOneModel{\n\t\t\t\t\tDocument: bson.D{{\"a\", strings.Repeat(\"b\", hello.MaxBsonObjectSize-500)}},\n\t\t\t\t},\n\t\t\t})\n\t\t}\n\t\tresult, err := mt.Client.BulkWrite(context.Background(), writes, options.ClientBulkWrite().SetOrdered(false).SetWriteConcern(writeconcern.Unacknowledged()))\n\t\trequire.NoError(mt, err, \"BulkWrite error: %v\", err)\n\t\tassert.False(mt, result.Acknowledged)\n\t\trequire.Len(mt, bwCmd, 2, \"expected %d bulkWrite calls, got: %d\", 2, len(bwCmd))\n\n\t\tassert.Len(mt, bwCmd[0].Ops, num-1, \"expected %d ops, got: %d\", num-1, len(bwCmd[0].Ops))\n\t\tassert.Equal(mt, int32(0), bwCmd[0].WriteConcern.W, \"expected writeConcern: %d, got: %v\", 0, bwCmd[0].WriteConcern.W)\n\n\t\tassert.Len(mt, bwCmd[1].Ops, 1, \"expected %d ops, got: %d\", 1, len(bwCmd[1].Ops))\n\t\tassert.Equal(mt, int32(0), bwCmd[1].WriteConcern.W, \"expected writeConcern: %d, got: %v\", 0, bwCmd[1].WriteConcern.W)\n\n\t\tn, err := coll.CountDocuments(context.Background(), bson.D{})\n\t\trequire.NoError(mt, err, \"CountDocuments error: %v\", err)\n\t\tassert.Equal(mt, num, int(n), \"expected %d documents, got: %d\", num, n)\n\t})\n}\n"
  },
  {
    "path": "internal/integration/csot_cse_prose_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2022-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\n//go:build cse\n\npackage integration\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/event\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/integration/mtest\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/integtest\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n)\n\n// CSOT prose tests that require the 'cse' Go build tag.\nfunc TestCSOTClientSideEncryptionProse(t *testing.T) {\n\tverifyClientSideEncryptionVarsSet(t)\n\tmt := mtest.New(t, mtest.NewOptions().MinServerVersion(\"4.2\").CreateClient(false))\n\n\tmt.RunOpts(\"2. maxTimeMS is not set for commands sent to mongocryptd\",\n\t\tnoClientOpts, func(mt *mtest.T) {\n\t\t\tkmsProviders := map[string]map[string]any{\n\t\t\t\t\"local\": {\n\t\t\t\t\t\"key\": localMasterKey,\n\t\t\t\t},\n\t\t\t}\n\t\t\tmongocryptdSpawnArgs := map[string]any{\n\t\t\t\t// Pass a custom pidfilepath to ensure a new mongocryptd process is spawned.\n\t\t\t\t\"mongocryptdSpawnArgs\": []string{\"--port=23000\", \"--pidfilepath=TestCSOTClientSideEncryptionProse_1.pid\"},\n\t\t\t\t\"mongocryptdURI\":       \"mongodb://localhost:23000\",\n\t\t\t\t// Do not use the shared library to ensure mongocryptd is spawned.\n\t\t\t\t\"__cryptSharedLibDisabledForTestOnly\": true,\n\t\t\t}\n\n\t\t\t// Setup encrypted client to cause spawning of mongocryptd on port 23000.\n\t\t\taeo := options.AutoEncryption().SetKmsProviders(kmsProviders).\n\t\t\t\tSetExtraOptions(mongocryptdSpawnArgs)\n\t\t\tcliOpts := options.Client().ApplyURI(mtest.ClusterURI()).SetAutoEncryptionOptions(aeo)\n\t\t\tintegtest.AddTestServerAPIVersion(cliOpts)\n\t\t\tencClient, err := mongo.Connect(cliOpts)\n\t\t\tassert.Nil(mt, err, \"Connect error: %v\", err)\n\t\t\tdefer func() {\n\t\t\t\terr = encClient.Disconnect(context.Background())\n\t\t\t\tassert.Nil(mt, err, \"encrypted client Disconnect error: %v\", err)\n\t\t\t}()\n\n\t\t\t// Run a Find through the encrypted client to make sure mongocryptd is started ('find' uses the\n\t\t\t// mongocryptd and will wait for it to be active).\n\t\t\t_, err = encClient.Database(\"test\").Collection(\"test\").Find(context.Background(), bson.D{})\n\t\t\tassert.Nil(mt, err, \"Find error: %v\", err)\n\n\t\t\t// Use a new Client to connect to 23000 where mongocryptd should be running. Use a custom\n\t\t\t// command monitor to examine the eventual 'ping'.\n\t\t\tvar started *event.CommandStartedEvent\n\t\t\tmcryptMonitor := &event.CommandMonitor{\n\t\t\t\tStarted: func(_ context.Context, evt *event.CommandStartedEvent) {\n\t\t\t\t\tstarted = evt\n\t\t\t\t},\n\t\t\t}\n\t\t\tmcryptOpts := options.Client().SetMonitor(mcryptMonitor).\n\t\t\t\tApplyURI(\"mongodb://localhost:23000/?timeoutMS=1000\")\n\t\t\tintegtest.AddTestServerAPIVersion(mcryptOpts)\n\t\t\tmcryptClient, err := mongo.Connect(mcryptOpts)\n\t\t\tassert.Nil(mt, err, \"mongocryptd Connect error: %v\", err)\n\t\t\tdefer func() {\n\t\t\t\terr = mcryptClient.Disconnect(context.Background())\n\t\t\t\tassert.Nil(mt, err, \"mongocryptd Disconnect error: %v\", err)\n\t\t\t}()\n\n\t\t\t// Run Ping and assert that sent command does not contain 'maxTimeMS'. The 'ping' command\n\t\t\t// does not exist on mongocryptd, so ignore the CommandNotFound error.\n\t\t\t_ = mcryptClient.Ping(context.Background(), nil)\n\t\t\tassert.NotNil(mt, started, \"expected a CommandStartedEvent, got nil\")\n\t\t\tassert.Equal(mt, started.CommandName, \"ping\", \"expected 'ping', got %q\", started.CommandName)\n\t\t\tcommandElems, err := started.Command.Elements()\n\t\t\tassert.Nil(mt, err, \"Elements error: %v\", err)\n\t\t\tfor _, elem := range commandElems {\n\t\t\t\tassert.NotEqual(mt, elem.Key(), \"maxTimeMS\",\n\t\t\t\t\t\"expected no 'maxTimeMS' field in ping to mongocryptd\")\n\t\t\t}\n\t\t})\n}\n"
  },
  {
    "path": "internal/integration/csot_prose_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2022-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage integration\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"os\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/event\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/failpoint\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/integration/mtest\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/integtest\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/mongoutil\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/require\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n)\n\nfunc TestCSOTProse(t *testing.T) {\n\t// Skip CSOT tests when SKIP_CSOT_TESTS=true. In Evergreen, we typically set\n\t// that environment variable on Windows and macOS because the CSOT spec\n\t// tests are unreliable on those hosts.\n\tif os.Getenv(\"SKIP_CSOT_TESTS\") == \"true\" {\n\t\tt.Skip(\"Skipping CSOT test because SKIP_CSOT_TESTS=true\")\n\t}\n\n\tmt := mtest.New(t, mtest.NewOptions().CreateClient(false))\n\n\tmt.RunOpts(\"1. multi-batch writes\", mtest.NewOptions().MinServerVersion(\"4.4\").\n\t\tTopologies(mtest.Single), func(mt *mtest.T) {\n\t\t// Test that multi-batch writes do not refresh the Timeout between batches.\n\n\t\terr := mt.Client.Database(\"db\").Collection(\"coll\").Drop(context.Background())\n\t\tassert.Nil(mt, err, \"Drop error: %v\", err)\n\n\t\t// Configure a fail point to block both inserts of the multi-write for 1010ms (2020ms total).\n\t\tmt.SetFailPoint(failpoint.FailPoint{\n\t\t\tConfigureFailPoint: \"failCommand\",\n\t\t\tMode: failpoint.Mode{\n\t\t\t\tTimes: 2,\n\t\t\t},\n\t\t\tData: failpoint.Data{\n\t\t\t\tFailCommands:    []string{\"insert\"},\n\t\t\t\tBlockConnection: true,\n\t\t\t\tBlockTimeMS:     1010,\n\t\t\t},\n\t\t})\n\n\t\t// Use a separate client with 2s Timeout and a separate command monitor to run a multi-batch\n\t\t// insert against db.coll.\n\t\tvar started []*event.CommandStartedEvent\n\t\tcm := &event.CommandMonitor{\n\t\t\tStarted: func(_ context.Context, evt *event.CommandStartedEvent) {\n\t\t\t\tstarted = append(started, evt)\n\t\t\t},\n\t\t}\n\t\tcliOptions := options.Client().\n\t\t\tSetTimeout(2 * time.Second).\n\t\t\tSetMonitor(cm).\n\t\t\tApplyURI(mtest.ClusterURI())\n\t\tintegtest.AddTestServerAPIVersion(cliOptions)\n\t\tcli, err := mongo.Connect(cliOptions)\n\t\tassert.Nil(mt, err, \"Connect error: %v\", err)\n\n\t\t// Insert 50 1MB documents (OP_MSG payloads can only fit 48MB in one batch).\n\t\tvar bigStringBuilder strings.Builder\n\t\tfor i := 0; i < 1024*1024; i++ {\n\t\t\tbigStringBuilder.WriteByte('a')\n\t\t}\n\t\tbigString := bigStringBuilder.String()\n\t\tvar docs []any\n\t\tfor i := 0; i < 50; i++ {\n\t\t\tdocs = append(docs, bson.D{{\"1mb\", bigString}})\n\t\t}\n\n\t\t// Expect a timeout error from InsertMany (from the second batch).\n\t\t_, err = cli.Database(\"db\").Collection(\"coll\").InsertMany(context.Background(), docs)\n\t\tassert.NotNil(mt, err, \"expected error from InsertMany, got nil\")\n\t\tassert.True(mt, mongo.IsTimeout(err), \"expected error to be a timeout, got %v\", err)\n\n\t\t// Expect that two 'insert's were sent.\n\t\tassert.True(mt, len(started) == 2, \"expected two started events, got %d\", len(started))\n\t\tassert.Equal(mt, started[0].CommandName,\n\t\t\t\"insert\", \"expected an insert event, got %v\", started[0].CommandName)\n\t\tassert.Equal(mt, started[1].CommandName,\n\t\t\t\"insert\", \"expected a second insert event, got %v\", started[1].CommandName)\n\t})\n\n\tmt.Run(\"8. server selection\", func(mt *mtest.T) {\n\t\tcliOpts := options.Client().ApplyURI(\"mongodb://invalid/?serverSelectionTimeoutMS=100\")\n\t\tmtOpts := mtest.NewOptions().ClientOptions(cliOpts).CreateCollection(false)\n\t\tmt.RunOpts(\"serverSelectionTimeoutMS honored if timeoutMS is not set\", mtOpts, func(mt *mtest.T) {\n\t\t\t// TODO(GODRIVER-3266): Why do parallel tests fail on windows builds?\n\t\t\t// mt.Parallel()\n\n\t\t\tcallback := func() bool {\n\t\t\t\terr := mt.Client.Ping(context.Background(), nil)\n\t\t\t\tassert.Error(mt, err, \"expected Ping error, got nil\")\n\t\t\t\treturn true\n\t\t\t}\n\n\t\t\t// Assert that Ping fails within 150ms due to server selection timeout.\n\t\t\tassert.Eventually(mt,\n\t\t\t\tcallback,\n\t\t\t\t150*time.Millisecond,\n\t\t\t\ttime.Millisecond,\n\t\t\t\t\"expected ping to fail within 150ms\")\n\t\t})\n\n\t\tcliOpts = options.Client().ApplyURI(\"mongodb://invalid/?timeoutMS=100&serverSelectionTimeoutMS=200\")\n\t\tmtOpts = mtest.NewOptions().ClientOptions(cliOpts).CreateCollection(false)\n\t\tmt.RunOpts(\"timeoutMS honored for server selection if it's lower than serverSelectionTimeoutMS\", mtOpts, func(mt *mtest.T) {\n\t\t\t// TODO(GODRIVER-3266): Why do parallel tests fail on windows builds?\n\t\t\t// mt.Parallel()\n\n\t\t\tcallback := func() bool {\n\t\t\t\terr := mt.Client.Ping(context.Background(), nil)\n\t\t\t\tassert.Error(mt, err, \"expected Ping error, got nil\")\n\t\t\t\treturn true\n\t\t\t}\n\n\t\t\t// Assert that Ping fails within 150ms due to timeout.\n\t\t\tassert.Eventually(mt,\n\t\t\t\tcallback,\n\t\t\t\t150*time.Millisecond,\n\t\t\t\ttime.Millisecond,\n\t\t\t\t\"expected ping to fail within 150ms\")\n\t\t})\n\n\t\tcliOpts = options.Client().ApplyURI(\"mongodb://invalid/?timeoutMS=200&serverSelectionTimeoutMS=100\")\n\t\tmtOpts = mtest.NewOptions().ClientOptions(cliOpts).CreateCollection(false)\n\t\tmt.RunOpts(\"serverSelectionTimeoutMS honored for server selection if it's lower than timeoutMS\", mtOpts, func(mt *mtest.T) {\n\t\t\t// TODO(GODRIVER-3266): Why do parallel tests fail on windows builds?\n\t\t\t// mt.Parallel()\n\n\t\t\tcallback := func() bool {\n\t\t\t\terr := mt.Client.Ping(context.Background(), nil)\n\t\t\t\tassert.Error(mt, err, \"expected Ping error, got nil\")\n\t\t\t\treturn true\n\t\t\t}\n\n\t\t\t// Assert that Ping fails within 150ms due to server selection timeout.\n\t\t\tassert.Eventually(mt,\n\t\t\t\tcallback,\n\t\t\t\t150*time.Millisecond,\n\t\t\t\ttime.Millisecond,\n\t\t\t\t\"expected ping to fail within 150ms\")\n\t\t})\n\n\t\tcliOpts = options.Client().ApplyURI(\"mongodb://invalid/?timeoutMS=0&serverSelectionTimeoutMS=100\")\n\t\tmtOpts = mtest.NewOptions().ClientOptions(cliOpts).CreateCollection(false)\n\t\tmt.RunOpts(\"serverSelectionTimeoutMS honored for server selection if timeoutMS=0\", mtOpts, func(mt *mtest.T) {\n\t\t\t// TODO(GODRIVER-3266): Why do parallel tests fail on windows builds?\n\t\t\t// mt.Parallel()\n\n\t\t\tcallback := func() bool {\n\t\t\t\terr := mt.Client.Ping(context.Background(), nil)\n\t\t\t\tassert.Error(mt, err, \"expected Ping error, got nil\")\n\t\t\t\treturn true\n\t\t\t}\n\n\t\t\t// Assert that Ping fails within 150ms due to server selection timeout.\n\t\t\tassert.Eventually(mt,\n\t\t\t\tcallback,\n\t\t\t\t150*time.Millisecond,\n\t\t\t\ttime.Millisecond,\n\t\t\t\t\"expected ping to fail within 150ms\")\n\t\t})\n\t})\n\n\tmt.RunOpts(\"11. multi-batch bulkWrites\", mtest.NewOptions().MinServerVersion(\"8.0\").\n\t\tTopologies(mtest.Single), func(mt *mtest.T) {\n\t\tcoll := mt.CreateCollection(mtest.Collection{DB: \"db\", Name: \"coll\"}, false)\n\t\terr := coll.Drop(context.Background())\n\t\trequire.NoError(mt, err, \"Drop error: %v\", err)\n\n\t\tmt.SetFailPoint(failpoint.FailPoint{\n\t\t\tConfigureFailPoint: \"failCommand\",\n\t\t\tMode: failpoint.Mode{\n\t\t\t\tTimes: 2,\n\t\t\t},\n\t\t\tData: failpoint.Data{\n\t\t\t\tFailCommands:    []string{\"bulkWrite\"},\n\t\t\t\tBlockConnection: true,\n\t\t\t\tBlockTimeMS:     1010,\n\t\t\t},\n\t\t})\n\n\t\tvar hello struct {\n\t\t\tMaxBsonObjectSize   int\n\t\t\tMaxMessageSizeBytes int\n\t\t}\n\t\terr = mt.DB.RunCommand(context.Background(), bson.D{{\"hello\", 1}}).Decode(&hello)\n\t\trequire.NoError(mt, err, \"Hello error: %v\", err)\n\n\t\tvar writes []mongo.ClientBulkWrite\n\t\tn := hello.MaxMessageSizeBytes/hello.MaxBsonObjectSize + 1\n\t\tfor i := 0; i < n; i++ {\n\t\t\twrites = append(writes, mongo.ClientBulkWrite{\n\t\t\t\tDatabase:   \"db\",\n\t\t\t\tCollection: \"coll\",\n\t\t\t\tModel: &mongo.ClientInsertOneModel{\n\t\t\t\t\tDocument: bson.D{{\"a\", strings.Repeat(\"b\", hello.MaxBsonObjectSize-500)}},\n\t\t\t\t},\n\t\t\t})\n\t\t}\n\n\t\tvar cnt int\n\t\tcm := &event.CommandMonitor{\n\t\t\tStarted: func(_ context.Context, evt *event.CommandStartedEvent) {\n\t\t\t\tif evt.CommandName == \"bulkWrite\" {\n\t\t\t\t\tcnt++\n\t\t\t\t}\n\t\t\t},\n\t\t}\n\t\tcliOptions := options.Client().\n\t\t\tSetTimeout(2 * time.Second).\n\t\t\tSetMonitor(cm).\n\t\t\tApplyURI(mtest.ClusterURI())\n\t\tmt.ResetClient(cliOptions)\n\t\t_, err = mt.Client.BulkWrite(context.Background(), writes)\n\t\tassert.ErrorIs(mt, err, context.DeadlineExceeded, \"expected a timeout error, got: %v\", err)\n\t\tassert.Equal(mt, 2, cnt, \"expected bulkWrite calls: %d, got: %d\", 2, cnt)\n\t})\n}\n\nfunc TestCSOTProse_GridFS(t *testing.T) {\n\tmt := mtest.New(t, mtest.NewOptions().CreateClient(false))\n\n\tmt.RunOpts(\"6. gridfs - upload\", mtest.NewOptions().MinServerVersion(\"4.4\"), func(mt *mtest.T) {\n\t\t// TODO(GODRIVER-3328): FailPoints are not currently reliable on sharded\n\t\t// topologies. Allow running on sharded topologies once that is fixed.\n\t\tnoShardedOpts := mtest.NewOptions().Topologies(mtest.Single, mtest.ReplicaSet, mtest.LoadBalanced)\n\t\tmt.RunOpts(\"uploads via openUploadStream can be timed out\", noShardedOpts, func(mt *mtest.T) {\n\t\t\t// Drop and re-create the db.fs.files and db.fs.chunks collections.\n\t\t\terr := mt.Client.Database(\"db\").Collection(\"fs.files\").Drop(context.Background())\n\t\t\tassert.NoError(mt, err, \"failed to drop files\")\n\n\t\t\terr = mt.Client.Database(\"db\").Collection(\"fs.chunks\").Drop(context.Background())\n\t\t\tassert.NoError(mt, err, \"failed to drop chunks\")\n\n\t\t\thosts, err := mongoutil.HostsFromURI(mtest.ClusterURI())\n\t\t\trequire.NoError(mt, err)\n\n\t\t\tfailpointHost := hosts[0]\n\n\t\t\tmt.ResetClient(options.Client().\n\t\t\t\tSetHosts([]string{failpointHost}))\n\n\t\t\t// Set a blocking \"insert\" fail point.\n\t\t\tmt.SetFailPoint(failpoint.FailPoint{\n\t\t\t\tConfigureFailPoint: \"failCommand\",\n\t\t\t\tMode: failpoint.Mode{\n\t\t\t\t\tTimes: 1,\n\t\t\t\t},\n\t\t\t\tData: failpoint.Data{\n\t\t\t\t\tFailCommands:    []string{\"insert\"},\n\t\t\t\t\tBlockConnection: true,\n\t\t\t\t\tBlockTimeMS:     1250,\n\t\t\t\t},\n\t\t\t})\n\n\t\t\t// The automatic failpoint clearing may not clear failpoints set on\n\t\t\t// specific hosts, so manually clear the failpoint we set on the specific\n\t\t\t// mongos when the test is done.\n\t\t\tdefer func() {\n\t\t\t\tmt.ResetClient(options.Client().\n\t\t\t\t\tSetHosts([]string{failpointHost}))\n\t\t\t\tmt.ClearFailPoints()\n\t\t\t}()\n\n\t\t\t// Create a new MongoClient with timeoutMS=1000.\n\t\t\tcliOptions := options.Client().SetTimeout(1000 * time.Millisecond).ApplyURI(mtest.ClusterURI()).\n\t\t\t\tSetHosts([]string{failpointHost})\n\n\t\t\tintegtest.AddTestServerAPIVersion(cliOptions)\n\n\t\t\tclient, err := mongo.Connect(cliOptions)\n\t\t\tassert.NoError(mt, err, \"failed to connect to server\")\n\n\t\t\t// Create a GridFS bucket that wraps the db database.\n\t\t\tbucket := client.Database(\"db\").GridFSBucket()\n\n\t\t\tuploadStream, err := bucket.OpenUploadStream(context.Background(), \"filename\")\n\t\t\trequire.NoError(mt, err, \"failed to open upload stream\")\n\n\t\t\t_, err = uploadStream.Write([]byte{0x12})\n\t\t\trequire.NoError(mt, err, \"failed to write to upload stream\")\n\n\t\t\terr = uploadStream.Close()\n\t\t\tassert.Error(t, err, context.DeadlineExceeded)\n\t\t})\n\n\t\t// TODO(GODRIVER-3328): FailPoints are not currently reliable on sharded\n\t\t// topologies. Allow running on sharded topologies once that is fixed.\n\t\tmt.RunOpts(\"Aborting an upload stream can be timed out\", noShardedOpts, func(mt *mtest.T) {\n\t\t\t// Drop and re-create the db.fs.files and db.fs.chunks collections.\n\t\t\terr := mt.Client.Database(\"db\").Collection(\"fs.files\").Drop(context.Background())\n\t\t\tassert.NoError(mt, err, \"failed to drop files\")\n\n\t\t\terr = mt.Client.Database(\"db\").Collection(\"fs.chunks\").Drop(context.Background())\n\t\t\tassert.NoError(mt, err, \"failed to drop chunks\")\n\n\t\t\thosts, err := mongoutil.HostsFromURI(mtest.ClusterURI())\n\t\t\trequire.NoError(mt, err)\n\n\t\t\tfailpointHost := hosts[0]\n\n\t\t\tmt.ResetClient(options.Client().\n\t\t\t\tSetHosts([]string{failpointHost}))\n\n\t\t\t// Set a blocking \"delete\" fail point.\n\t\t\tmt.SetFailPoint(failpoint.FailPoint{\n\t\t\t\tConfigureFailPoint: \"failCommand\",\n\t\t\t\tMode: failpoint.Mode{\n\t\t\t\t\tTimes: 1,\n\t\t\t\t},\n\t\t\t\tData: failpoint.Data{\n\t\t\t\t\tFailCommands:    []string{\"delete\"},\n\t\t\t\t\tBlockConnection: true,\n\t\t\t\t\tBlockTimeMS:     1250,\n\t\t\t\t},\n\t\t\t})\n\n\t\t\t// The automatic failpoint clearing may not clear failpoints set on\n\t\t\t// specific hosts, so manually clear the failpoint we set on the specific\n\t\t\t// mongos when the test is done.\n\t\t\tdefer func() {\n\t\t\t\tmt.ResetClient(options.Client().\n\t\t\t\t\tSetHosts([]string{failpointHost}))\n\t\t\t\tmt.ClearFailPoints()\n\t\t\t}()\n\n\t\t\t// Create a new MongoClient with timeoutMS=1000.\n\t\t\tcliOptions := options.Client().SetTimeout(1000 * time.Millisecond).ApplyURI(mtest.ClusterURI()).\n\t\t\t\tSetHosts([]string{failpointHost})\n\t\t\tintegtest.AddTestServerAPIVersion(cliOptions)\n\n\t\t\tclient, err := mongo.Connect(cliOptions)\n\t\t\tassert.NoError(mt, err, \"failed to connect to server\")\n\n\t\t\t// Create a GridFS bucket that wraps the db database.\n\t\t\tbucket := client.Database(\"db\").GridFSBucket(options.GridFSBucket().SetChunkSizeBytes(2))\n\n\t\t\t// Call bucket.open_upload_stream() with the filename filename to create\n\t\t\t// an upload stream (referred to as uploadStream).\n\t\t\tuploadStream, err := bucket.OpenUploadStream(context.Background(), \"filename\")\n\t\t\trequire.NoError(mt, err)\n\n\t\t\t// Using uploadStream, upload the bytes [0x01, 0x02, 0x03, 0x04].\n\t\t\t_, err = uploadStream.Write([]byte{0x01, 0x02, 0x03, 0x04})\n\t\t\trequire.NoError(mt, err)\n\n\t\t\terr = uploadStream.Abort()\n\t\t\tassert.Error(mt, err, context.DeadlineExceeded)\n\t\t})\n\t})\n\n\tconst test61 = \"6.1 gridfs - upload and download with non-expiring client-level timeout\"\n\tmt.RunOpts(test61, mtest.NewOptions().MinServerVersion(\"4.4\"), func(mt *mtest.T) {\n\t\t// Drop and re-create the db.fs.files and db.fs.chunks collections.\n\t\terr := mt.Client.Database(\"db\").Collection(\"fs.files\").Drop(context.Background())\n\t\tassert.NoError(mt, err, \"failed to drop files\")\n\n\t\terr = mt.Client.Database(\"db\").Collection(\"fs.chunks\").Drop(context.Background())\n\t\tassert.NoError(mt, err, \"failed to drop chunks\")\n\n\t\t// Create a new MongoClient with timeoutMS=500.\n\t\tcliOptions := options.Client().SetTimeout(500 * time.Millisecond).ApplyURI(mtest.ClusterURI())\n\t\tintegtest.AddTestServerAPIVersion(cliOptions)\n\n\t\tclient, err := mongo.Connect(cliOptions)\n\t\tassert.NoError(mt, err, \"failed to connect to server\")\n\n\t\t// Create a GridFS bucket that wraps the db database.\n\t\tbucket := client.Database(\"db\").GridFSBucket()\n\n\t\tmt.Run(\"UploadFromStream\", func(mt *mtest.T) {\n\t\t\t// Upload file and ensure it uploaded correctly.\n\t\t\tfileID, err := bucket.UploadFromStream(context.Background(), \"filename\", bytes.NewReader([]byte{0x12}))\n\t\t\tassert.NoError(mt, err, \"failed to upload stream\")\n\n\t\t\tbuf := bytes.Buffer{}\n\n\t\t\t_, err = bucket.DownloadToStream(context.Background(), fileID, &buf)\n\t\t\tassert.NoError(mt, err, \"failed to download stream\")\n\t\t\tassert.Equal(mt, buf.Len(), 1)\n\t\t\tassert.Equal(mt, buf.Bytes(), []byte{0x12})\n\t\t})\n\n\t\tmt.Run(\"OpenUploadStream\", func(mt *mtest.T) {\n\t\t\t// Upload file and ensure it uploaded correctly.\n\t\t\tuploadStream, err := bucket.OpenUploadStream(context.Background(), \"filename2\")\n\t\t\trequire.NoError(mt, err, \"failed to open upload stream\")\n\n\t\t\t_, err = uploadStream.Write([]byte{0x13})\n\t\t\trequire.NoError(mt, err, \"failed to write data to upload stream\")\n\n\t\t\terr = uploadStream.Close()\n\t\t\trequire.NoError(mt, err, \"failed to close upload stream\")\n\n\t\t\tbuf := bytes.Buffer{}\n\n\t\t\t_, err = bucket.DownloadToStream(context.Background(), uploadStream.FileID, &buf)\n\t\t\tassert.NoError(mt, err, \"failed to download stream\")\n\t\t\tassert.Equal(mt, buf.Len(), 1)\n\t\t\tassert.Equal(mt, buf.Bytes(), []byte{0x13})\n\t\t})\n\t})\n\n\tconst test62 = \"6.2 gridfs - upload with operation-level timeout\"\n\tmtOpts := mtest.NewOptions().\n\t\tMinServerVersion(\"4.4\").\n\t\t// TODO(GODRIVER-3328): FailPoints are not currently reliable on sharded\n\t\t// topologies. Allow running on sharded topologies once that is fixed.\n\t\tTopologies(mtest.Single, mtest.ReplicaSet, mtest.LoadBalanced)\n\tmt.RunOpts(test62, mtOpts, func(mt *mtest.T) {\n\t\t// Drop and re-create the db.fs.files and db.fs.chunks collections.\n\t\terr := mt.Client.Database(\"db\").Collection(\"fs.files\").Drop(context.Background())\n\t\tassert.NoError(mt, err, \"failed to drop files\")\n\n\t\terr = mt.Client.Database(\"db\").Collection(\"fs.chunks\").Drop(context.Background())\n\t\tassert.NoError(mt, err, \"failed to drop chunks\")\n\n\t\thosts, err := mongoutil.HostsFromURI(mtest.ClusterURI())\n\t\trequire.NoError(mt, err)\n\n\t\tfailpointHost := hosts[0]\n\n\t\tmt.ResetClient(options.Client().\n\t\t\tSetHosts([]string{failpointHost}))\n\n\t\t// Set a blocking \"insert\" fail point.\n\t\tmt.SetFailPoint(failpoint.FailPoint{\n\t\t\tConfigureFailPoint: \"failCommand\",\n\t\t\tMode: failpoint.Mode{\n\t\t\t\tTimes: 1,\n\t\t\t},\n\t\t\tData: failpoint.Data{\n\t\t\t\tFailCommands:    []string{\"insert\"},\n\t\t\t\tBlockConnection: true,\n\t\t\t\tBlockTimeMS:     200,\n\t\t\t},\n\t\t})\n\n\t\t// The automatic failpoint clearing may not clear failpoints set on\n\t\t// specific hosts, so manually clear the failpoint we set on the specific\n\t\t// mongos when the test is done.\n\t\tdefer func() {\n\t\t\tmt.ResetClient(options.Client().\n\t\t\t\tSetHosts([]string{failpointHost}))\n\t\t\tmt.ClearFailPoints()\n\t\t}()\n\n\t\tcliOptions := options.Client().SetTimeout(100 * time.Millisecond).ApplyURI(mtest.ClusterURI())\n\t\tintegtest.AddTestServerAPIVersion(cliOptions)\n\n\t\tclient, err := mongo.Connect(cliOptions)\n\t\tassert.NoError(mt, err, \"failed to connect to server\")\n\n\t\t// Create a GridFS bucket that wraps the db database.\n\t\tbucket := client.Database(\"db\").GridFSBucket()\n\n\t\tmt.Run(\"UploadFromStream\", func(mt *mtest.T) {\n\t\t\t// If the operation-level context is not respected, then the client-level\n\t\t\t// timeout will exceed deadline.\n\t\t\tctx, cancel := context.WithTimeout(context.Background(), 1000*time.Millisecond)\n\t\t\tdefer cancel()\n\n\t\t\t// Upload file and ensure it uploaded correctly.\n\t\t\tfileID, err := bucket.UploadFromStream(ctx, \"filename\", bytes.NewReader([]byte{0x12}))\n\t\t\trequire.NoError(mt, err, \"failed to upload stream\")\n\n\t\t\tbuf := bytes.Buffer{}\n\n\t\t\t_, err = bucket.DownloadToStream(context.Background(), fileID, &buf)\n\t\t\tassert.NoError(mt, err, \"failed to download stream\")\n\t\t\tassert.Equal(mt, buf.Len(), 1)\n\t\t\tassert.Equal(mt, buf.Bytes(), []byte{0x12})\n\t\t})\n\n\t\tmt.Run(\"OpenUploadStream\", func(mt *mtest.T) {\n\t\t\t// If the operation-level context is not respected, then the client-level\n\t\t\t// timeout will exceed deadline.\n\t\t\tctx, cancel := context.WithTimeout(context.Background(), 1000*time.Millisecond)\n\t\t\tdefer cancel()\n\n\t\t\t// Upload file and ensure it uploaded correctly.\n\t\t\tuploadStream, err := bucket.OpenUploadStream(ctx, \"filename2\")\n\t\t\trequire.NoError(mt, err, \"failed to open upload stream\")\n\n\t\t\t_, err = uploadStream.Write([]byte{0x13})\n\t\t\trequire.NoError(mt, err, \"failed to write data to upload stream\")\n\n\t\t\terr = uploadStream.Close()\n\t\t\trequire.NoError(mt, err, \"failed to close upload stream\")\n\n\t\t\tbuf := bytes.Buffer{}\n\n\t\t\t_, err = bucket.DownloadToStream(context.Background(), uploadStream.FileID, &buf)\n\t\t\tassert.NoError(mt, err, \"failed to download stream\")\n\t\t\tassert.Equal(mt, buf.Len(), 1)\n\t\t\tassert.Equal(mt, buf.Bytes(), []byte{0x13})\n\t\t})\n\t})\n\n\tconst test63 = \"6.3 gridfs - cancel context mid-stream\"\n\tmt.RunOpts(test63, mtest.NewOptions().MinServerVersion(\"4.4\"), func(mt *mtest.T) {\n\t\t// Drop and re-create the db.fs.files and db.fs.chunks collections.\n\t\terr := mt.Client.Database(\"db\").Collection(\"fs.files\").Drop(context.Background())\n\t\tassert.NoError(mt, err, \"failed to drop files\")\n\n\t\terr = mt.Client.Database(\"db\").Collection(\"fs.chunks\").Drop(context.Background())\n\t\tassert.NoError(mt, err, \"failed to drop chunks\")\n\n\t\tcliOptions := options.Client().ApplyURI(mtest.ClusterURI())\n\t\tintegtest.AddTestServerAPIVersion(cliOptions)\n\n\t\tclient, err := mongo.Connect(cliOptions)\n\t\tassert.NoError(mt, err, \"failed to connect to server\")\n\n\t\t// Create a GridFS bucket that wraps the db database.\n\t\tbucket := client.Database(\"db\").GridFSBucket()\n\n\t\tmt.Run(\"Upload#Close\", func(mt *mtest.T) {\n\t\t\tuploadStream, err := bucket.OpenUploadStream(context.Background(), \"filename\")\n\t\t\trequire.NoError(mt, err)\n\n\t\t\t_ = uploadStream.Close()\n\n\t\t\t_, err = uploadStream.Write([]byte{0x13})\n\t\t\tassert.Error(mt, err, context.Canceled)\n\t\t})\n\n\t\tmt.Run(\"Upload#Abort\", func(mt *mtest.T) {\n\t\t\tuploadStream, err := bucket.OpenUploadStream(context.Background(), \"filename2\")\n\t\t\trequire.NoError(mt, err)\n\n\t\t\t_ = uploadStream.Abort()\n\n\t\t\t_, err = uploadStream.Write([]byte{0x13})\n\t\t\tassert.Error(mt, err, context.Canceled)\n\t\t})\n\n\t\tmt.Run(\"Download#Close\", func(mt *mtest.T) {\n\t\t\tfileID, err := bucket.UploadFromStream(context.Background(), \"filename3\", bytes.NewReader([]byte{0x12}))\n\t\t\trequire.NoError(mt, err, \"failed to upload stream\")\n\n\t\t\tdownloadStream, err := bucket.OpenDownloadStream(context.Background(), fileID)\n\t\t\tassert.NoError(mt, err)\n\n\t\t\t_ = downloadStream.Close()\n\n\t\t\t_, err = downloadStream.Read([]byte{})\n\t\t\tassert.Error(mt, err, context.Canceled)\n\t\t})\n\t})\n}\n"
  },
  {
    "path": "internal/integration/csot_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2024-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage integration\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"os\"\n\t\"testing\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/event\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/eventtest\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/failpoint\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/integration/mtest\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/require\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver\"\n)\n\n// Test automatic \"maxTimeMS\" appending and connection closing behavior.\nfunc TestCSOT_maxTimeMS(t *testing.T) {\n\t// Skip CSOT tests when SKIP_CSOT_TESTS=true. In Evergreen, we typically set\n\t// that environment variable on Windows and macOS because the CSOT spec\n\t// tests are unreliable on those hosts.\n\tif os.Getenv(\"SKIP_CSOT_TESTS\") == \"true\" {\n\t\tt.Skip(\"Skipping CSOT test because SKIP_CSOT_TESTS=true\")\n\t}\n\n\tmt := mtest.New(t, mtest.NewOptions().CreateClient(false))\n\n\ttestCases := []struct {\n\t\tdesc           string\n\t\tcommandName    string\n\t\tsetup          func(coll *mongo.Collection) error\n\t\toperation      func(ctx context.Context, coll *mongo.Collection) error\n\t\tsendsMaxTimeMS bool\n\t\ttopologies     []mtest.TopologyKind\n\t}{\n\t\t{\n\t\t\tdesc:        \"FindOne\",\n\t\t\tcommandName: \"find\",\n\t\t\tsetup: func(coll *mongo.Collection) error {\n\t\t\t\t_, err := coll.InsertOne(context.Background(), bson.D{})\n\t\t\t\treturn err\n\t\t\t},\n\t\t\toperation: func(ctx context.Context, coll *mongo.Collection) error {\n\t\t\t\treturn coll.FindOne(ctx, bson.D{}).Err()\n\t\t\t},\n\t\t\tsendsMaxTimeMS: true,\n\t\t},\n\t\t{\n\t\t\tdesc:        \"Find\",\n\t\t\tcommandName: \"find\",\n\t\t\tsetup: func(coll *mongo.Collection) error {\n\t\t\t\t_, err := coll.InsertOne(context.Background(), bson.D{})\n\t\t\t\treturn err\n\t\t\t},\n\t\t\toperation: func(ctx context.Context, coll *mongo.Collection) error {\n\t\t\t\t_, err := coll.Find(ctx, bson.D{})\n\t\t\t\treturn err\n\t\t\t},\n\t\t\tsendsMaxTimeMS: false,\n\t\t},\n\t\t{\n\t\t\tdesc:        \"FindOneAndDelete\",\n\t\t\tcommandName: \"findAndModify\",\n\t\t\tsetup: func(coll *mongo.Collection) error {\n\t\t\t\t_, err := coll.InsertOne(context.Background(), bson.D{})\n\t\t\t\treturn err\n\t\t\t},\n\t\t\toperation: func(ctx context.Context, coll *mongo.Collection) error {\n\t\t\t\treturn coll.FindOneAndDelete(ctx, bson.D{}).Err()\n\t\t\t},\n\t\t\tsendsMaxTimeMS: true,\n\t\t},\n\t\t{\n\t\t\tdesc:        \"FindOneAndUpdate\",\n\t\t\tcommandName: \"findAndModify\",\n\t\t\tsetup: func(coll *mongo.Collection) error {\n\t\t\t\t_, err := coll.InsertOne(context.Background(), bson.D{})\n\t\t\t\treturn err\n\t\t\t},\n\t\t\toperation: func(ctx context.Context, coll *mongo.Collection) error {\n\t\t\t\treturn coll.FindOneAndUpdate(ctx, bson.D{}, bson.M{\"$set\": bson.M{\"key\": \"value\"}}).Err()\n\t\t\t},\n\t\t\tsendsMaxTimeMS: true,\n\t\t},\n\t\t{\n\t\t\tdesc:        \"FindOneAndReplace\",\n\t\t\tcommandName: \"findAndModify\",\n\t\t\tsetup: func(coll *mongo.Collection) error {\n\t\t\t\t_, err := coll.InsertOne(context.Background(), bson.D{})\n\t\t\t\treturn err\n\t\t\t},\n\t\t\toperation: func(ctx context.Context, coll *mongo.Collection) error {\n\t\t\t\treturn coll.FindOneAndReplace(ctx, bson.D{}, bson.D{}).Err()\n\t\t\t},\n\t\t\tsendsMaxTimeMS: true,\n\t\t},\n\t\t{\n\t\t\tdesc:        \"InsertOne\",\n\t\t\tcommandName: \"insert\",\n\t\t\toperation: func(ctx context.Context, coll *mongo.Collection) error {\n\t\t\t\t_, err := coll.InsertOne(ctx, bson.D{})\n\t\t\t\treturn err\n\t\t\t},\n\t\t\tsendsMaxTimeMS: true,\n\t\t},\n\t\t{\n\t\t\tdesc:        \"InsertMany\",\n\t\t\tcommandName: \"insert\",\n\t\t\toperation: func(ctx context.Context, coll *mongo.Collection) error {\n\t\t\t\t_, err := coll.InsertMany(ctx, []any{bson.D{}})\n\t\t\t\treturn err\n\t\t\t},\n\t\t\tsendsMaxTimeMS: true,\n\t\t},\n\t\t{\n\t\t\tdesc:        \"UpdateOne\",\n\t\t\tcommandName: \"update\",\n\t\t\toperation: func(ctx context.Context, coll *mongo.Collection) error {\n\t\t\t\t_, err := coll.UpdateOne(ctx, bson.D{}, bson.M{\"$set\": bson.M{\"key\": \"value\"}})\n\t\t\t\treturn err\n\t\t\t},\n\t\t\tsendsMaxTimeMS: true,\n\t\t},\n\t\t{\n\t\t\tdesc:        \"UpdateMany\",\n\t\t\tcommandName: \"update\",\n\t\t\toperation: func(ctx context.Context, coll *mongo.Collection) error {\n\t\t\t\t_, err := coll.UpdateMany(ctx, bson.D{}, bson.M{\"$set\": bson.M{\"key\": \"value\"}})\n\t\t\t\treturn err\n\t\t\t},\n\t\t\tsendsMaxTimeMS: true,\n\t\t},\n\t\t{\n\t\t\tdesc:        \"ReplaceOne\",\n\t\t\tcommandName: \"update\",\n\t\t\toperation: func(ctx context.Context, coll *mongo.Collection) error {\n\t\t\t\t_, err := coll.ReplaceOne(ctx, bson.D{}, bson.D{})\n\t\t\t\treturn err\n\t\t\t},\n\t\t\tsendsMaxTimeMS: true,\n\t\t},\n\t\t{\n\t\t\tdesc:        \"DeleteOne\",\n\t\t\tcommandName: \"delete\",\n\t\t\toperation: func(ctx context.Context, coll *mongo.Collection) error {\n\t\t\t\t_, err := coll.DeleteOne(ctx, bson.D{})\n\t\t\t\treturn err\n\t\t\t},\n\t\t\tsendsMaxTimeMS: true,\n\t\t},\n\t\t{\n\t\t\tdesc:        \"DeleteMany\",\n\t\t\tcommandName: \"delete\",\n\t\t\toperation: func(ctx context.Context, coll *mongo.Collection) error {\n\t\t\t\t_, err := coll.DeleteMany(ctx, bson.D{})\n\t\t\t\treturn err\n\t\t\t},\n\t\t\tsendsMaxTimeMS: true,\n\t\t},\n\t\t{\n\t\t\tdesc:        \"Distinct\",\n\t\t\tcommandName: \"distinct\",\n\t\t\toperation: func(ctx context.Context, coll *mongo.Collection) error {\n\t\t\t\treturn coll.Distinct(ctx, \"name\", bson.D{}).Err()\n\t\t\t},\n\t\t\tsendsMaxTimeMS: true,\n\t\t},\n\t\t{\n\t\t\tdesc:        \"Aggregate\",\n\t\t\tcommandName: \"aggregate\",\n\t\t\toperation: func(ctx context.Context, coll *mongo.Collection) error {\n\t\t\t\t_, err := coll.Aggregate(ctx, mongo.Pipeline{})\n\t\t\t\treturn err\n\t\t\t},\n\t\t\tsendsMaxTimeMS: false,\n\t\t},\n\t\t{\n\t\t\tdesc:        \"Watch\",\n\t\t\tcommandName: \"aggregate\",\n\t\t\toperation: func(ctx context.Context, coll *mongo.Collection) error {\n\t\t\t\tcs, err := coll.Watch(ctx, mongo.Pipeline{})\n\t\t\t\tif cs != nil {\n\t\t\t\t\tcs.Close(context.Background())\n\t\t\t\t}\n\t\t\t\treturn err\n\t\t\t},\n\t\t\tsendsMaxTimeMS: true,\n\t\t\t// Change Streams aren't supported on standalone topologies.\n\t\t\ttopologies: []mtest.TopologyKind{\n\t\t\t\tmtest.ReplicaSet,\n\t\t\t\tmtest.Sharded,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tdesc:        \"Cursor getMore\",\n\t\t\tcommandName: \"getMore\",\n\t\t\tsetup: func(coll *mongo.Collection) error {\n\t\t\t\t_, err := coll.InsertMany(context.Background(), []any{bson.D{}, bson.D{}})\n\t\t\t\treturn err\n\t\t\t},\n\t\t\toperation: func(ctx context.Context, coll *mongo.Collection) error {\n\t\t\t\tcursor, err := coll.Find(ctx, bson.D{}, options.Find().SetBatchSize(1))\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tvar res []bson.D\n\t\t\t\treturn cursor.All(ctx, &res)\n\t\t\t},\n\t\t\tsendsMaxTimeMS: false,\n\t\t},\n\t}\n\n\t// getStartedEvent returns the first command started event that matches the\n\t// specified command name.\n\tgetStartedEvent := func(mt *mtest.T, command string) *event.CommandStartedEvent {\n\t\tfor {\n\t\t\tevt := mt.GetStartedEvent()\n\t\t\tif evt == nil {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\t_, err := evt.Command.LookupErr(command)\n\t\t\tif errors.Is(err, bsoncore.ErrElementNotFound) {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\treturn evt\n\t\t}\n\n\t\tmt.Errorf(\"could not find command started event for command %q\", command)\n\t\tmt.FailNow()\n\t\treturn nil\n\t}\n\n\t// assertMaxTimeMSIsSet asserts that \"maxTimeMS\" is set to a positive value\n\t// on the given command document.\n\tassertMaxTimeMSIsSet := func(mt *mtest.T, command bson.Raw) {\n\t\tmt.Helper()\n\n\t\tmaxTimeVal := command.Lookup(\"maxTimeMS\")\n\n\t\trequire.Greater(mt,\n\t\t\tlen(maxTimeVal.Value),\n\t\t\t0,\n\t\t\t\"expected maxTimeMS BSON value to be non-empty\")\n\t\trequire.Equal(mt,\n\t\t\tmaxTimeVal.Type,\n\t\t\tbson.TypeInt64,\n\t\t\t\"expected maxTimeMS BSON value to be type Int64\")\n\t\tassert.Greater(mt,\n\t\t\tmaxTimeVal.Int64(),\n\t\t\tint64(0),\n\t\t\t\"expected maxTimeMS value to be greater than 0\")\n\t}\n\n\t// assertMaxTimeMSIsSet asserts that \"maxTimeMS\" is not set on the given\n\t// command document.\n\tassertMaxTimeMSNotSet := func(mt *mtest.T, command bson.Raw) {\n\t\tmt.Helper()\n\n\t\t_, err := command.LookupErr(\"maxTimeMS\")\n\t\tassert.ErrorIs(mt,\n\t\t\terr,\n\t\t\tbsoncore.ErrElementNotFound,\n\t\t\t\"expected maxTimeMS BSON value to be missing, but is present\")\n\t}\n\n\tfor _, tc := range testCases {\n\t\tmt.RunOpts(tc.desc, mtest.NewOptions().Topologies(tc.topologies...), func(mt *mtest.T) {\n\t\t\tmt.Run(\"timeoutMS not set\", func(mt *mtest.T) {\n\t\t\t\tif tc.setup != nil {\n\t\t\t\t\terr := tc.setup(mt.Coll)\n\t\t\t\t\trequire.NoError(mt, err)\n\t\t\t\t}\n\n\t\t\t\terr := tc.operation(context.Background(), mt.Coll)\n\t\t\t\trequire.NoError(mt, err)\n\n\t\t\t\tevt := getStartedEvent(mt, tc.commandName)\n\t\t\t\tassertMaxTimeMSNotSet(mt, evt.Command)\n\t\t\t})\n\n\t\t\tmt.Run(\"Context with deadline\", func(mt *mtest.T) {\n\t\t\t\tif tc.setup != nil {\n\t\t\t\t\terr := tc.setup(mt.Coll)\n\t\t\t\t\trequire.NoError(mt, err)\n\t\t\t\t}\n\n\t\t\t\tctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)\n\t\t\t\tdefer cancel()\n\n\t\t\t\terr := tc.operation(ctx, mt.Coll)\n\t\t\t\trequire.NoError(mt, err)\n\n\t\t\t\tevt := getStartedEvent(mt, tc.commandName)\n\t\t\t\tif tc.sendsMaxTimeMS {\n\t\t\t\t\tassertMaxTimeMSIsSet(mt, evt.Command)\n\t\t\t\t} else {\n\t\t\t\t\tassertMaxTimeMSNotSet(mt, evt.Command)\n\t\t\t\t}\n\t\t\t})\n\n\t\t\tcsotOpts := mtest.NewOptions().\n\t\t\t\tClientOptions(options.Client().SetTimeout(10 * time.Second))\n\t\t\tmt.RunOpts(\"timeoutMS and context.Background\", csotOpts, func(mt *mtest.T) {\n\t\t\t\tif tc.setup != nil {\n\t\t\t\t\terr := tc.setup(mt.Coll)\n\t\t\t\t\trequire.NoError(mt, err)\n\t\t\t\t}\n\n\t\t\t\terr := tc.operation(context.Background(), mt.Coll)\n\t\t\t\trequire.NoError(mt, err)\n\n\t\t\t\tevt := getStartedEvent(mt, tc.commandName)\n\t\t\t\tif tc.sendsMaxTimeMS {\n\t\t\t\t\tassertMaxTimeMSIsSet(mt, evt.Command)\n\t\t\t\t} else {\n\t\t\t\t\tassertMaxTimeMSNotSet(mt, evt.Command)\n\t\t\t\t}\n\t\t\t})\n\n\t\t\tmt.RunOpts(\"timeoutMS and Context with deadline\", csotOpts, func(mt *mtest.T) {\n\t\t\t\tif tc.setup != nil {\n\t\t\t\t\terr := tc.setup(mt.Coll)\n\t\t\t\t\trequire.NoError(mt, err)\n\t\t\t\t}\n\n\t\t\t\tctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)\n\t\t\t\tdefer cancel()\n\n\t\t\t\terr := tc.operation(ctx, mt.Coll)\n\t\t\t\trequire.NoError(mt, err)\n\n\t\t\t\tevt := getStartedEvent(mt, tc.commandName)\n\t\t\t\tif tc.sendsMaxTimeMS {\n\t\t\t\t\tassertMaxTimeMSIsSet(mt, evt.Command)\n\t\t\t\t} else {\n\t\t\t\t\tassertMaxTimeMSNotSet(mt, evt.Command)\n\t\t\t\t}\n\t\t\t})\n\n\t\t\topts := mtest.NewOptions().\n\t\t\t\t// Blocking failpoints don't work on pre-4.2 and sharded\n\t\t\t\t// clusters.\n\t\t\t\tTopologies(mtest.Single, mtest.ReplicaSet).\n\t\t\t\tMinServerVersion(\"4.2\")\n\t\t\tmt.RunOpts(\"prevents connection closure\", opts, func(mt *mtest.T) {\n\t\t\t\tif tc.setup != nil {\n\t\t\t\t\terr := tc.setup(mt.Coll)\n\t\t\t\t\trequire.NoError(mt, err)\n\t\t\t\t}\n\n\t\t\t\tmt.SetFailPoint(failpoint.FailPoint{\n\t\t\t\t\tConfigureFailPoint: \"failCommand\",\n\t\t\t\t\tMode:               failpoint.ModeAlwaysOn,\n\t\t\t\t\tData: failpoint.Data{\n\t\t\t\t\t\tFailCommands:    []string{tc.commandName},\n\t\t\t\t\t\tBlockConnection: true,\n\t\t\t\t\t\t// Note that some operations (currently Find and\n\t\t\t\t\t\t// Aggregate) do not send maxTimeMS by default, meaning\n\t\t\t\t\t\t// that the server will only respond after BlockTimeMS\n\t\t\t\t\t\t// is elapsed. If the amount of time that the driver\n\t\t\t\t\t\t// waits for responses after a timeout is significantly\n\t\t\t\t\t\t// lower than BlockTimeMS, this test will start failing\n\t\t\t\t\t\t// for those operations.\n\t\t\t\t\t\tBlockTimeMS: 500,\n\t\t\t\t\t},\n\t\t\t\t})\n\n\t\t\t\ttpm := eventtest.NewTestPoolMonitor()\n\t\t\t\tmt.ResetClient(options.Client().\n\t\t\t\t\tSetPoolMonitor(tpm.PoolMonitor))\n\n\t\t\t\t// Run 5 operations that time out, then assert that no\n\t\t\t\t// connections were closed.\n\t\t\t\tfor i := 0; i < 5; i++ {\n\t\t\t\t\tctx, cancel := context.WithTimeout(context.Background(), 15*time.Millisecond)\n\t\t\t\t\terr := tc.operation(ctx, mt.Coll)\n\t\t\t\t\tcancel()\n\n\t\t\t\t\tif !mongo.IsTimeout(err) {\n\t\t\t\t\t\tt.Logf(\"Operation %d returned a non-timeout error: %v\", i, err)\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tclosedEvents := tpm.Events(func(pe *event.PoolEvent) bool {\n\t\t\t\t\treturn pe.Type == event.ConnectionClosed\n\t\t\t\t})\n\t\t\t\tassert.Len(mt, closedEvents, 0, \"expected no connection closed event\")\n\t\t\t})\n\t\t})\n\t}\n\n\tcsotOpts := mtest.NewOptions().ClientOptions(options.Client().SetTimeout(10 * time.Second))\n\tmt.RunOpts(\"omitted for values greater than 2147483647ms\", csotOpts, func(mt *mtest.T) {\n\t\tctx, cancel := context.WithTimeout(context.Background(), (2147483647+1000)*time.Millisecond)\n\t\tdefer cancel()\n\t\t_, err := mt.Coll.InsertOne(ctx, bson.D{})\n\t\trequire.NoError(t, err)\n\n\t\tevt := mt.GetStartedEvent()\n\t\t_, err = evt.Command.LookupErr(\"maxTimeMS\")\n\t\tassert.ErrorIs(mt,\n\t\t\terr,\n\t\t\tbsoncore.ErrElementNotFound,\n\t\t\t\"expected maxTimeMS BSON value to be missing, but is present\")\n\t})\n}\n\nfunc TestCSOT_errors(t *testing.T) {\n\t// Skip CSOT tests when SKIP_CSOT_TESTS=true. In Evergreen, we typically set\n\t// that environment variable on Windows and macOS because the CSOT spec\n\t// tests are unreliable on those hosts.\n\tif os.Getenv(\"SKIP_CSOT_TESTS\") == \"true\" {\n\t\tt.Skip(\"Skipping CSOT test because SKIP_CSOT_TESTS=true\")\n\t}\n\n\tmt := mtest.New(t, mtest.NewOptions().\n\t\tCreateClient(false).\n\t\t// Blocking failpoints don't work on pre-4.2 and sharded clusters.\n\t\tTopologies(mtest.Single, mtest.ReplicaSet).\n\t\tMinServerVersion(\"4.2\").\n\t\t// Enable CSOT.\n\t\tClientOptions(options.Client().SetTimeout(10*time.Second)))\n\n\t// Test that, when CSOT is enabled, the error returned when the database\n\t// returns a MaxTimeMSExceeded error (error code 50) wraps\n\t// \"context.DeadlineExceeded\".\n\tmt.Run(\"MaxTimeMSExceeded wraps context.DeadlineExceeded\", func(mt *mtest.T) {\n\t\t_, err := mt.Coll.InsertOne(context.Background(), bson.D{})\n\t\trequire.NoError(mt, err, \"InsertOne error\")\n\n\t\tmt.SetFailPoint(failpoint.FailPoint{\n\t\t\tConfigureFailPoint: \"failCommand\",\n\t\t\tMode: failpoint.Mode{\n\t\t\t\tTimes: 1,\n\t\t\t},\n\t\t\tData: failpoint.Data{\n\t\t\t\tFailCommands: []string{\"find\"},\n\t\t\t\tErrorCode:    50, // MaxTimeMSExceeded\n\t\t\t},\n\t\t})\n\n\t\terr = mt.Coll.FindOne(context.Background(), bson.D{}).Err()\n\n\t\tassert.True(mt,\n\t\t\terrors.Is(err, context.DeadlineExceeded),\n\t\t\t\"expected error %[1]T(%[1]q) to wrap context.DeadlineExceeded\",\n\t\t\terr)\n\t\tassert.True(mt,\n\t\t\tmongo.IsTimeout(err),\n\t\t\t\"expected error %[1]T(%[1]q) to be a timeout error\",\n\t\t\terr)\n\t})\n\n\t// Test that, when CSOT is enabled, the error returned when a context\n\t// deadline is exceeded during a network operation wraps\n\t// \"context.DeadlineExceeded\".\n\tmt.Run(\"Context timeout wraps context.DeadlineExceeded\", func(mt *mtest.T) {\n\t\t_, err := mt.Coll.InsertOne(context.Background(), bson.D{})\n\t\trequire.NoError(mt, err, \"InsertOne error\")\n\n\t\tmt.SetFailPoint(failpoint.FailPoint{\n\t\t\tConfigureFailPoint: \"failCommand\",\n\t\t\tMode: failpoint.Mode{\n\t\t\t\tTimes: 1,\n\t\t\t},\n\t\t\tData: failpoint.Data{\n\t\t\t\tFailCommands:    []string{\"find\"},\n\t\t\t\tBlockConnection: true,\n\t\t\t\tBlockTimeMS:     500,\n\t\t\t},\n\t\t})\n\n\t\tctx, cancel := context.WithTimeout(context.Background(), 15*time.Millisecond)\n\t\tdefer cancel()\n\t\terr = mt.Coll.FindOne(ctx, bson.D{}).Err()\n\n\t\tassert.False(mt,\n\t\t\terrors.Is(err, driver.ErrDeadlineWouldBeExceeded),\n\t\t\t\"expected error %[1]T(%[1]q) to not wrap driver.ErrDeadlineWouldBeExceeded\",\n\t\t\terr)\n\t\tassert.True(mt,\n\t\t\terrors.Is(err, context.DeadlineExceeded),\n\t\t\t\"expected error %[1]T(%[1]q) to wrap context.DeadlineExceeded\",\n\t\t\terr)\n\t\tassert.True(mt,\n\t\t\tmongo.IsTimeout(err),\n\t\t\t\"expected error %[1]T(%[1]q) to be a timeout error\",\n\t\t\terr)\n\t})\n\n\tmt.Run(\"timeoutMS timeout wraps context.DeadlineExceeded\", func(mt *mtest.T) {\n\t\t_, err := mt.Coll.InsertOne(context.Background(), bson.D{})\n\t\trequire.NoError(mt, err, \"InsertOne error\")\n\n\t\tmt.SetFailPoint(failpoint.FailPoint{\n\t\t\tConfigureFailPoint: \"failCommand\",\n\t\t\tMode: failpoint.Mode{\n\t\t\t\tTimes: 1,\n\t\t\t},\n\t\t\tData: failpoint.Data{\n\t\t\t\tFailCommands:    []string{\"find\"},\n\t\t\t\tBlockConnection: true,\n\t\t\t\tBlockTimeMS:     100,\n\t\t\t},\n\t\t})\n\n\t\t// Set timeoutMS=10 to run the FindOne, then unset it so the mtest\n\t\t// cleanup operations pass successfully (e.g. unsetting failpoints).\n\t\tmt.ResetClient(options.Client().SetTimeout(10 * time.Millisecond))\n\t\tdefer mt.ResetClient(options.Client())\n\t\terr = mt.Coll.FindOne(context.Background(), bson.D{}).Err()\n\n\t\tassert.False(mt,\n\t\t\terrors.Is(err, driver.ErrDeadlineWouldBeExceeded),\n\t\t\t\"expected error %[1]T(%[1]q) to not wrap driver.ErrDeadlineWouldBeExceeded\",\n\t\t\terr)\n\t\tassert.True(mt,\n\t\t\terrors.Is(err, context.DeadlineExceeded),\n\t\t\t\"expected error %[1]T(%[1]q) to wrap context.DeadlineExceeded\",\n\t\t\terr)\n\t\tassert.True(mt,\n\t\t\tmongo.IsTimeout(err),\n\t\t\t\"expected error %[1]T(%[1]q) to be a timeout error\",\n\t\t\terr)\n\t})\n}\n"
  },
  {
    "path": "internal/integration/cursor_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage integration\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"os\"\n\t\"testing\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/event\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/failpoint\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/integration/mtest\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/require\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n)\n\nconst (\n\terrorCursorNotFound = 43\n)\n\nfunc TestCursor(t *testing.T) {\n\tmt := mtest.New(t, mtest.NewOptions().CreateClient(false))\n\n\tmt.Run(\"cursor is killed on server\", func(mt *mtest.T) {\n\t\tinitCollection(mt, mt.Coll)\n\t\tc, err := mt.Coll.Find(context.Background(), bson.D{}, options.Find().SetBatchSize(2))\n\t\tassert.Nil(mt, err, \"Find error: %v\", err)\n\n\t\tid := c.ID()\n\t\tassert.True(mt, c.Next(context.Background()), \"expected Next true, got false\")\n\t\terr = c.Close(context.Background())\n\t\tassert.Nil(mt, err, \"Close error: %v\", err)\n\n\t\terr = mt.DB.RunCommand(context.Background(), bson.D{\n\t\t\t{\"getMore\", id},\n\t\t\t{\"collection\", mt.Coll.Name()},\n\t\t}).Err()\n\t\tce := err.(mongo.CommandError)\n\t\tassert.Equal(mt, int32(errorCursorNotFound), ce.Code, \"expected error code %v, got %v\", errorCursorNotFound, ce.Code)\n\t})\n\n\tmt.Run(\"set batchSize\", func(mt *mtest.T) {\n\t\tinitCollection(mt, mt.Coll)\n\t\tmt.ClearEvents()\n\n\t\t// create cursor with batchSize 0\n\t\tcursor, err := mt.Coll.Find(context.Background(), bson.D{}, options.Find().SetBatchSize(0))\n\t\tassert.Nil(mt, err, \"Find error: %v\", err)\n\t\tdefer cursor.Close(context.Background())\n\t\tevt := mt.GetStartedEvent()\n\t\tassert.Equal(mt, \"find\", evt.CommandName, \"expected 'find' event, got '%v'\", evt.CommandName)\n\t\tsizeVal, err := evt.Command.LookupErr(\"batchSize\")\n\t\tassert.Nil(mt, err, \"expected find command to have batchSize\")\n\t\tbatchSize := sizeVal.Int32()\n\t\tassert.Equal(mt, int32(0), batchSize, \"expected batchSize 0, got %v\", batchSize)\n\n\t\t// make sure that the getMore sends the new batchSize\n\t\tbatchCursor := mongo.BatchCursorFromCursor(cursor)\n\t\tbatchCursor.SetBatchSize(4)\n\t\tassert.True(mt, cursor.Next(context.Background()), \"expected Next true, got false\")\n\t\tevt = mt.GetStartedEvent()\n\t\tassert.NotNil(mt, evt, \"expected getMore event, got nil\")\n\t\tassert.Equal(mt, \"getMore\", evt.CommandName, \"expected 'getMore' event, got '%v'\", evt.CommandName)\n\t\tsizeVal, err = evt.Command.LookupErr(\"batchSize\")\n\t\tassert.Nil(mt, err, \"expected getMore command to have batchSize\")\n\t\tbatchSize = sizeVal.Int32()\n\t\tassert.Equal(mt, int32(4), batchSize, \"expected batchSize 4, got %v\", batchSize)\n\t})\n}\n\nfunc TestCursor_TryNext(t *testing.T) {\n\tmt := mtest.New(t, mtest.NewOptions().CreateClient(false))\n\n\t// Skip tests if running against serverless, as capped collections are banned.\n\tif os.Getenv(\"SERVERLESS\") == \"serverless\" {\n\t\tmt.Skip(\"skipping as serverless forbids capped collections\")\n\t}\n\n\tmt.Run(\"existing non-empty batch\", func(mt *mtest.T) {\n\t\t// If there's already documents in the current batch, TryNext should return true without doing a getMore\n\n\t\tinitCollection(mt, mt.Coll)\n\t\tcursor, err := mt.Coll.Find(context.Background(), bson.D{})\n\t\trequire.NoError(mt, err, \"Find error: %v\", err)\n\t\tdefer cursor.Close(context.Background())\n\t\ttryNextExistingBatchTest(mt, cursor)\n\t})\n\n\tcappedCollectionOpts := options.CreateCollection().SetCapped(true).SetSizeInBytes(64 * 1024)\n\tmt.RunOpts(\"one getMore sent\", mtest.NewOptions().CollectionCreateOptions(cappedCollectionOpts), func(mt *mtest.T) {\n\t\t// If the current batch is empty, TryNext should send one getMore and return.\n\n\t\t// insert a document because a tailable cursor will only have a non-zero ID if the initial Find matches\n\t\t// at least one document\n\t\t_, err := mt.Coll.InsertOne(context.Background(), bson.D{{\"x\", 1}})\n\t\trequire.NoError(mt, err, \"InsertOne error: %v\", err)\n\n\t\tcursor, err := mt.Coll.Find(context.Background(), bson.D{}, options.Find().SetCursorType(options.Tailable))\n\t\trequire.NoError(mt, err, \"Find error: %v\", err)\n\t\tdefer cursor.Close(context.Background())\n\n\t\t// first call to TryNext should return 1 document\n\t\tassert.True(mt, cursor.TryNext(context.Background()), \"expected Next to return true, got false\")\n\t\t// TryNext should attempt one getMore\n\t\tmt.ClearEvents()\n\t\tassert.False(mt, cursor.TryNext(context.Background()), \"unexpected document %v\", cursor.Current)\n\t\tverifyOneGetmoreSent(mt)\n\t})\n\tmt.RunOpts(\"getMore error\", mtest.NewOptions().ClientType(mtest.Mock), func(mt *mtest.T) {\n\t\tfindRes := mtest.CreateCursorResponse(50, \"foo.bar\", mtest.FirstBatch)\n\t\tmt.AddMockResponses(findRes)\n\t\tcursor, err := mt.Coll.Find(context.Background(), bson.D{})\n\t\trequire.NoError(mt, err, \"Find error: %v\", err)\n\t\tdefer cursor.Close(context.Background())\n\t\ttryNextGetmoreError(mt, cursor)\n\t})\n}\n\nfunc TestCursor_RemainingBatchLength(t *testing.T) {\n\tmt := mtest.New(t, mtest.NewOptions().CreateClient(false))\n\n\tcappedCollectionOpts := options.CreateCollection().SetCapped(true).SetSizeInBytes(64 * 1024)\n\tcappedMtOpts := mtest.NewOptions().CollectionCreateOptions(cappedCollectionOpts)\n\t// Skip tests if running against serverless, as capped collections are banned.\n\tif os.Getenv(\"SERVERLESS\") == \"serverless\" {\n\t\tmt.Skip(\"skipping as serverless forbids capped collections\")\n\t}\n\n\tmt.RunOpts(\"first batch is non empty\", cappedMtOpts, func(mt *mtest.T) {\n\t\t// Test that the cursor reports the correct value for RemainingBatchLength at various execution points if\n\t\t// the first batch from the server is non-empty.\n\n\t\tinitCollection(mt, mt.Coll)\n\n\t\t// Create a tailable await cursor with a low cursor timeout.\n\t\tbatchSize := 2\n\t\tfindOpts := options.Find().\n\t\t\tSetBatchSize(int32(batchSize)).\n\t\t\tSetCursorType(options.TailableAwait).\n\t\t\tSetMaxAwaitTime(100 * time.Millisecond)\n\t\tcursor, err := mt.Coll.Find(context.Background(), bson.D{}, findOpts)\n\t\trequire.NoError(mt, err, \"Find error: %v\", err)\n\t\tdefer cursor.Close(context.Background())\n\n\t\tmt.ClearEvents()\n\n\t\t// The initial batch length should be equal to the batchSize. Do batchSize Next calls to exhaust the current\n\t\t// batch and assert that no getMore was done.\n\t\tassert.Equal(mt,\n\t\t\tbatchSize,\n\t\t\tcursor.RemainingBatchLength(),\n\t\t\t\"expected remaining batch length to match\")\n\t\tfor i := 0; i < batchSize; i++ {\n\t\t\tprevLength := cursor.RemainingBatchLength()\n\t\t\tif !cursor.Next(context.Background()) {\n\t\t\t\tmt.Fatalf(\"expected Next to return true on index %d; cursor err: %v\", i, cursor.Err())\n\t\t\t}\n\n\t\t\t// Each successful Next call should decrement batch length by 1.\n\t\t\tassert.Equal(mt,\n\t\t\t\tprevLength-1,\n\t\t\t\tcursor.RemainingBatchLength(),\n\t\t\t\t\"expected remaining batch length to match\")\n\t\t}\n\t\tevt := mt.GetStartedEvent()\n\t\tassert.Nil(mt, evt, \"expected no events, got %v\", evt)\n\n\t\t// The batch is exhausted, so the batch length should be 0. Do one Next call, which should do a getMore and\n\t\t// fetch batchSize more documents. The batch length after the call should be (batchSize-1) because Next consumes\n\t\t// one document.\n\t\tassert.Equal(mt,\n\t\t\t0,\n\t\t\tcursor.RemainingBatchLength(),\n\t\t\t\"expected remaining batch length to match\")\n\n\t\tassert.True(mt, cursor.Next(context.Background()), \"expected Next to return true; cursor err: %v\", cursor.Err())\n\t\tevt = mt.GetStartedEvent()\n\t\tassert.NotNil(mt, evt, \"expected CommandStartedEvent, got nil\")\n\t\tassert.Equal(mt, \"getMore\", evt.CommandName, \"expected command %q, got %q\", \"getMore\", evt.CommandName)\n\n\t\tassert.Equal(mt,\n\t\t\tbatchSize-1,\n\t\t\tcursor.RemainingBatchLength(),\n\t\t\t\"expected remaining batch length to match\")\n\t})\n\tmt.RunOpts(\"first batch is empty\", mtest.NewOptions().ClientType(mtest.Mock), func(mt *mtest.T) {\n\t\t// Test that the cursor reports the correct value for RemainingBatchLength if the first batch is empty.\n\t\t// Using a mock deployment simplifies this test because the server won't create a valid cursor if the\n\t\t// collection is empty when the find is run.\n\n\t\tcursorID := int64(50)\n\t\tns := mt.DB.Name() + \".\" + mt.Coll.Name()\n\t\tgetMoreBatch := []bson.D{\n\t\t\t{{\"x\", 1}},\n\t\t\t{{\"x\", 2}},\n\t\t}\n\n\t\t// Create mock responses.\n\t\tfind := mtest.CreateCursorResponse(cursorID, ns, mtest.FirstBatch)\n\t\tgetMore := mtest.CreateCursorResponse(cursorID, ns, mtest.NextBatch, getMoreBatch...)\n\t\tkillCursors := mtest.CreateSuccessResponse()\n\t\tmt.AddMockResponses(find, getMore, killCursors)\n\n\t\tcursor, err := mt.Coll.Find(context.Background(), bson.D{})\n\t\tassert.Nil(mt, err, \"Find error: %v\", err)\n\t\tdefer cursor.Close(context.Background())\n\t\tmt.ClearEvents()\n\n\t\tfor !cursor.TryNext(context.Background()) {\n\n\t\t\tassert.Nil(mt, cursor.Err(), \"cursor error: %v\", err)\n\t\t\tassert.Equal(mt,\n\t\t\t\t0,\n\t\t\t\tcursor.RemainingBatchLength(),\n\t\t\t\t\"expected remaining batch length to match\")\n\t\t}\n\t\t// TryNext consumes one document so the remaining batch size should be len(getMoreBatch)-1.\n\t\tassert.Equal(mt,\n\t\t\tlen(getMoreBatch)-1,\n\t\t\tcursor.RemainingBatchLength(),\n\t\t\t\"expected remaining batch length to match\")\n\t})\n}\n\nfunc TestCursor_All(t *testing.T) {\n\tmt := mtest.New(t, mtest.NewOptions().CreateClient(false))\n\n\tfailpointOpts := mtest.NewOptions().Topologies(mtest.ReplicaSet).MinServerVersion(\"4.0\")\n\tmt.RunOpts(\"getMore error\", failpointOpts, func(mt *mtest.T) {\n\t\tfailpointData := failpoint.Data{\n\t\t\tFailCommands: []string{\"getMore\"},\n\t\t\tErrorCode:    100,\n\t\t}\n\t\tmt.SetFailPoint(failpoint.FailPoint{\n\t\t\tConfigureFailPoint: \"failCommand\",\n\t\t\tMode:               failpoint.ModeAlwaysOn,\n\t\t\tData:               failpointData,\n\t\t})\n\t\tinitCollection(mt, mt.Coll)\n\t\tcursor, err := mt.Coll.Find(context.Background(), bson.D{}, options.Find().SetBatchSize(2))\n\t\tassert.Nil(mt, err, \"Find error: %v\", err)\n\t\tdefer cursor.Close(context.Background())\n\n\t\tvar docs []bson.D\n\t\terr = cursor.All(context.Background(), &docs)\n\t\trequire.Error(mt, err, \"expected change stream error, got nil\")\n\n\t\t// make sure that a mongo.CommandError is returned instead of a driver.Error\n\t\tmongoErr, ok := err.(mongo.CommandError)\n\t\tassert.True(mt, ok, \"expected mongo.CommandError, got: %T\", err)\n\t\tassert.Equal(mt, failpointData.ErrorCode, mongoErr.Code, \"expected code %v, got: %v\", failpointData.ErrorCode, mongoErr.Code)\n\t})\n\n\tmt.Run(\"deferred Close uses context.Background\", func(mt *mtest.T) {\n\t\tinitCollection(mt, mt.Coll)\n\n\t\t// Find with batchSize 2 so All will run getMore for next 3 docs and error.\n\t\tcur, err := mt.Coll.Find(context.Background(), bson.D{},\n\t\t\toptions.Find().SetBatchSize(2))\n\t\trequire.NoError(mt, err, \"Find error: %v\", err)\n\n\t\t// Create a context and immediately cancel it.\n\t\tcanceledCtx, cancel := context.WithCancel(context.Background())\n\t\tcancel()\n\n\t\t// Clear \"insert\" and \"find\" events.\n\t\tmt.ClearEvents()\n\n\t\t// Call All with the canceled context and expect context.Canceled.\n\t\tvar docs []bson.D\n\t\terr = cur.All(canceledCtx, &docs)\n\t\trequire.Error(mt, err, \"expected error for All, got nil\")\n\t\tassert.True(mt, errors.Is(err, context.Canceled),\n\t\t\t\"expected context.Canceled error, got %v\", err)\n\n\t\t// Assert that a \"getMore\" command was sent and failed (Next used the\n\t\t// canceled context).\n\t\tstEvt := mt.GetStartedEvent()\n\t\tassert.NotNil(mt, stEvt, `expected a \"getMore\" started event, got no event`)\n\t\tassert.Equal(mt, stEvt.CommandName, \"getMore\",\n\t\t\t`expected a \"getMore\" started event, got %q`, stEvt.CommandName)\n\t\tfEvt := mt.GetFailedEvent()\n\t\tassert.NotNil(mt, fEvt, `expected a failed \"getMore\" event, got no event`)\n\t\tassert.Equal(mt, fEvt.CommandName, \"getMore\",\n\t\t\t`expected a failed \"getMore\" event, got %q`, fEvt.CommandName)\n\n\t\t// Assert that a \"killCursors\" command was sent and was successful (Close\n\t\t// used the 2 second Client Timeout).\n\t\tstEvt = mt.GetStartedEvent()\n\t\tassert.NotNil(mt, stEvt, `expected a \"killCursors\" started event, got no event`)\n\t\tassert.Equal(mt, stEvt.CommandName, \"killCursors\",\n\t\t\t`expected a \"killCursors\" started event, got %q`, stEvt.CommandName)\n\t\tsuEvt := mt.GetSucceededEvent()\n\t\tassert.NotNil(mt, suEvt, `expected a successful \"killCursors\" event, got no event`)\n\t\tassert.Equal(mt, suEvt.CommandName, \"killCursors\",\n\t\t\t`expected a successful \"killCursors\" event, got %q`, suEvt.CommandName)\n\t})\n}\n\nfunc TestCursor_Close(t *testing.T) {\n\tmt := mtest.New(t, mtest.NewOptions().CreateClient(false))\n\n\tfailpointOpts := mtest.NewOptions().Topologies(mtest.ReplicaSet).MinServerVersion(\"4.0\")\n\tmt.RunOpts(\"killCursors error\", failpointOpts, func(mt *mtest.T) {\n\t\tfailpointData := failpoint.Data{\n\t\t\tFailCommands: []string{\"killCursors\"},\n\t\t\tErrorCode:    100,\n\t\t}\n\t\tmt.SetFailPoint(failpoint.FailPoint{\n\t\t\tConfigureFailPoint: \"failCommand\",\n\t\t\tMode:               failpoint.ModeAlwaysOn,\n\t\t\tData:               failpointData,\n\t\t})\n\t\tinitCollection(mt, mt.Coll)\n\t\tcursor, err := mt.Coll.Find(context.Background(), bson.D{}, options.Find().SetBatchSize(2))\n\t\trequire.NoError(mt, err, \"Find error: %v\", err)\n\n\t\terr = cursor.Close(context.Background())\n\t\trequire.Error(mt, err, \"expected change stream error, got nil\")\n\n\t\t// make sure that a mongo.CommandError is returned instead of a driver.Error\n\t\tmongoErr, ok := err.(mongo.CommandError)\n\t\tassert.True(mt, ok, \"expected mongo.CommandError, got: %T\", err)\n\t\tassert.Equal(mt, failpointData.ErrorCode, mongoErr.Code, \"expected code %v, got: %v\", failpointData.ErrorCode, mongoErr.Code)\n\t})\n}\n\nfunc parseMaxAwaitTime(mt *mtest.T, evt *event.CommandStartedEvent) int64 {\n\tmt.Helper()\n\n\tmaxTimeMSRaw, err := evt.Command.LookupErr(\"maxTimeMS\")\n\trequire.NoError(mt, err)\n\n\tgot, ok := maxTimeMSRaw.AsInt64OK()\n\trequire.True(mt, ok)\n\n\treturn got\n}\n\nfunc tadcFindFactory(ctx context.Context, mt *mtest.T, coll mongo.Collection) *mongo.Cursor {\n\tmt.Helper()\n\n\tinitCollection(mt, &coll)\n\tcur, err := coll.Find(ctx, bson.D{{\"__nomatch\", 1}},\n\t\toptions.Find().SetBatchSize(1).SetCursorType(options.TailableAwait))\n\trequire.NoError(mt, err, \"Find error: %v\", err)\n\n\treturn cur\n}\n\nfunc tadcAggregateFactory(ctx context.Context, mt *mtest.T, coll mongo.Collection) *mongo.Cursor {\n\tmt.Helper()\n\n\tinitCollection(mt, &coll)\n\topts := options.Aggregate()\n\tpipeline := mongo.Pipeline{\n\t\t{{\"$changeStream\", bson.D{{\"fullDocument\", \"default\"}}}},\n\t\t{{\"$match\", bson.D{\n\t\t\t{\"operationType\", \"insert\"},\n\t\t\t{\"fullDocment.__nomatch\", 1},\n\t\t}}},\n\t}\n\n\tcursor, err := coll.Aggregate(ctx, pipeline, opts)\n\trequire.NoError(mt, err, \"Aggregate error: %v\", err)\n\n\treturn cursor\n}\n\nfunc tadcRunCommandCursorFactory(ctx context.Context, mt *mtest.T, coll mongo.Collection) *mongo.Cursor {\n\tmt.Helper()\n\n\tinitCollection(mt, &coll)\n\n\tcur, err := coll.Database().RunCommandCursor(ctx, bson.D{\n\t\t{\"find\", coll.Name()},\n\t\t{\"filter\", bson.D{{\"__nomatch\", 1}}},\n\t\t{\"tailable\", true},\n\t\t{\"awaitData\", true},\n\t\t{\"batchSize\", int32(1)},\n\t})\n\trequire.NoError(mt, err, \"RunCommandCursor error: %v\", err)\n\n\treturn cur\n}\n\n// For tailable awaitData cursors, the maxTimeMS for a getMore should be\n// min(maxAwaitTimeMS, remaining timeoutMS - minRoundTripTime) to allow the\n// server more opportunities to respond with an empty batch before a\n// client-side timeout.\nfunc TestCursor_tailableAwaitData_applyRemainingTimeout(t *testing.T) {\n\t// These values reflect what is used in the unified spec tests, see\n\t// DRIVERS-2868.\n\tconst timeoutMS = 200\n\tconst maxAwaitTimeMS = 100\n\tconst blockTimeMS = 30\n\tconst getMoreBound = 71\n\n\t// TODO(GODRIVER-3328): mongos doesn't honor a failpoint's full blockTimeMS.\n\tbaseTopologies := []mtest.TopologyKind{mtest.Single, mtest.LoadBalanced, mtest.ReplicaSet}\n\n\ttype testCase struct {\n\t\tname       string\n\t\tfactory    func(ctx context.Context, mt *mtest.T, coll mongo.Collection) *mongo.Cursor\n\t\topTimeout  bool\n\t\ttopologies []mtest.TopologyKind\n\t}\n\n\tcases := []testCase{\n\t\t// TODO(GODRIVER-2944): \"find\" cursors are tested in the CSOT unified spec\n\t\t// tests for tailable/awaitData cursors and so these tests can be removed\n\t\t// once the driver supports timeoutMode.\n\t\t{\n\t\t\tname:       \"find client-level timeout\",\n\t\t\tfactory:    tadcFindFactory,\n\t\t\ttopologies: baseTopologies,\n\t\t\topTimeout:  false,\n\t\t},\n\t\t{\n\t\t\tname:       \"find operation-level timeout\",\n\t\t\tfactory:    tadcFindFactory,\n\t\t\ttopologies: baseTopologies,\n\t\t\topTimeout:  true,\n\t\t},\n\n\t\t// There is no analogue to tailable/awaiData cursor unified spec tests for\n\t\t// aggregate and runnCommand.\n\t\t{\n\t\t\tname:       \"aggregate with changeStream client-level timeout\",\n\t\t\tfactory:    tadcAggregateFactory,\n\t\t\ttopologies: []mtest.TopologyKind{mtest.ReplicaSet, mtest.LoadBalanced},\n\t\t\topTimeout:  false,\n\t\t},\n\t\t{\n\t\t\tname:       \"aggregate with changeStream operation-level timeout\",\n\t\t\tfactory:    tadcAggregateFactory,\n\t\t\ttopologies: []mtest.TopologyKind{mtest.ReplicaSet, mtest.LoadBalanced},\n\t\t\topTimeout:  true,\n\t\t},\n\t\t{\n\t\t\tname:       \"runCommandCursor client-level timeout\",\n\t\t\tfactory:    tadcRunCommandCursorFactory,\n\t\t\ttopologies: baseTopologies,\n\t\t\topTimeout:  false,\n\t\t},\n\t\t{\n\t\t\tname:       \"runCommandCursor operation-level timeout\",\n\t\t\tfactory:    tadcRunCommandCursorFactory,\n\t\t\ttopologies: baseTopologies,\n\t\t\topTimeout:  true,\n\t\t},\n\t}\n\n\tmt := mtest.New(t, mtest.NewOptions().CreateClient(false).MinServerVersion(\"4.2\"))\n\n\tfor _, tc := range cases {\n\t\t// Reset the collection between test cases to avoid leaking timeouts\n\t\t// between tests.\n\t\tcappedOpts := options.CreateCollection().SetCapped(true).SetSizeInBytes(1024 * 64)\n\t\tcaseOpts := mtest.NewOptions().\n\t\t\tCollectionCreateOptions(cappedOpts).\n\t\t\tTopologies(tc.topologies...).\n\t\t\tCreateClient(true)\n\n\t\tif !tc.opTimeout {\n\t\t\tcaseOpts = caseOpts.ClientOptions(options.Client().SetTimeout(timeoutMS * time.Millisecond))\n\t\t}\n\n\t\tmt.RunOpts(tc.name, caseOpts, func(mt *mtest.T) {\n\t\t\tmt.SetFailPoint(failpoint.FailPoint{\n\t\t\t\tConfigureFailPoint: \"failCommand\",\n\t\t\t\tMode:               failpoint.Mode{Times: 1},\n\t\t\t\tData: failpoint.Data{\n\t\t\t\t\tFailCommands:    []string{\"getMore\"},\n\t\t\t\t\tBlockConnection: true,\n\t\t\t\t\tBlockTimeMS:     int32(blockTimeMS),\n\t\t\t\t},\n\t\t\t})\n\n\t\t\tctx := context.Background()\n\n\t\t\tvar cancel context.CancelFunc\n\t\t\tif tc.opTimeout {\n\t\t\t\tctx, cancel = context.WithTimeout(ctx, timeoutMS*time.Millisecond)\n\t\t\t\tdefer cancel()\n\t\t\t}\n\n\t\t\tcur := tc.factory(ctx, mt, *mt.Coll)\n\t\t\tdefer func() { assert.NoError(mt, cur.Close(context.Background())) }()\n\n\t\t\trequire.NoError(mt, cur.Err())\n\n\t\t\tcur.SetMaxAwaitTime(maxAwaitTimeMS * time.Millisecond)\n\n\t\t\tmt.ClearEvents()\n\n\t\t\tassert.False(mt, cur.Next(ctx))\n\n\t\t\trequire.Error(mt, cur.Err(), \"expected error from cursor.Next\")\n\t\t\tassert.ErrorIs(mt, cur.Err(), context.DeadlineExceeded, \"expected context deadline exceeded error\")\n\n\t\t\tgetMoreEvts := []*event.CommandStartedEvent{}\n\t\t\tfor _, evt := range mt.GetAllStartedEvents() {\n\t\t\t\tif evt.CommandName == \"getMore\" {\n\t\t\t\t\tgetMoreEvts = append(getMoreEvts, evt)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// It's possible that three getMore events are called: 100ms, 70ms, and\n\t\t\t// then some small leftover of remaining time (e.g. 20µs).\n\t\t\trequire.GreaterOrEqual(mt, len(getMoreEvts), 2)\n\n\t\t\t// The first getMore should have a maxTimeMS of <= 100ms but greater\n\t\t\t// than 71ms, indicating that the maxAwaitTimeMS was used.\n\t\t\tassert.LessOrEqual(mt, parseMaxAwaitTime(mt, getMoreEvts[0]), int64(maxAwaitTimeMS))\n\t\t\tassert.Greater(mt, parseMaxAwaitTime(mt, getMoreEvts[0]), int64(getMoreBound))\n\n\t\t\t// The second getMore should have a maxTimeMS of <=71, indicating that we\n\t\t\t// are using the time remaining in the context rather than the\n\t\t\t// maxAwaitTimeMS.\n\t\t\tassert.LessOrEqual(mt, parseMaxAwaitTime(mt, getMoreEvts[1]), int64(getMoreBound))\n\t\t})\n\t}\n}\n\n// For tailable awaitData cursors, the maxTimeMS for a getMore should be\nfunc TestCursor_tailableAwaitData_ShortCircuitingGetMore(t *testing.T) {\n\tmt := mtest.New(t, mtest.NewOptions().CreateClient(false))\n\n\tcappedOpts := options.CreateCollection().SetCapped(true).\n\t\tSetSizeInBytes(1024 * 64)\n\n\tmtOpts := mtest.NewOptions().CollectionCreateOptions(cappedOpts)\n\ttests := []struct {\n\t\tname             string\n\t\tdeadline         time.Duration\n\t\tmaxAwaitTime     time.Duration\n\t\twantShortCircuit bool\n\t}{\n\t\t{\n\t\t\tname:             \"maxAwaitTime less than operation timeout\",\n\t\t\tdeadline:         200 * time.Millisecond,\n\t\t\tmaxAwaitTime:     100 * time.Millisecond,\n\t\t\twantShortCircuit: false,\n\t\t},\n\t\t{\n\t\t\tname:             \"maxAwaitTime equal to operation timeout\",\n\t\t\tdeadline:         200 * time.Millisecond,\n\t\t\tmaxAwaitTime:     200 * time.Millisecond,\n\t\t\twantShortCircuit: true,\n\t\t},\n\t\t{\n\t\t\tname:             \"maxAwaitTime greater than operation timeout\",\n\t\t\tdeadline:         200 * time.Millisecond,\n\t\t\tmaxAwaitTime:     300 * time.Millisecond,\n\t\t\twantShortCircuit: true,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tmt.Run(tt.name, func(mt *mtest.T) {\n\t\t\tmt.RunOpts(\"find\", mtOpts, func(mt *mtest.T) {\n\t\t\t\tinitCollection(mt, mt.Coll)\n\n\t\t\t\t// Create a find cursor\n\t\t\t\topts := options.Find().\n\t\t\t\t\tSetBatchSize(1).\n\t\t\t\t\tSetMaxAwaitTime(tt.maxAwaitTime).\n\t\t\t\t\tSetCursorType(options.TailableAwait)\n\n\t\t\t\tctx, cancel := context.WithTimeout(context.Background(), tt.deadline)\n\t\t\t\tdefer cancel()\n\n\t\t\t\tcur, err := mt.Coll.Find(ctx, bson.D{{Key: \"x\", Value: 3}}, opts)\n\t\t\t\trequire.NoError(mt, err, \"Find error: %v\", err)\n\n\t\t\t\t// Close to return the session to the pool.\n\t\t\t\tdefer cur.Close(context.Background())\n\n\t\t\t\tok := cur.Next(ctx)\n\t\t\t\tif tt.wantShortCircuit {\n\t\t\t\t\tassert.False(mt, ok, \"expected Next to return false, got true\")\n\t\t\t\t\tassert.EqualError(t, cur.Err(), \"MaxAwaitTime must be less than the operation timeout\")\n\t\t\t\t} else {\n\t\t\t\t\tassert.True(mt, ok, \"expected Next to return true, got false\")\n\t\t\t\t\tassert.NoError(mt, cur.Err(), \"expected no error, got %v\", cur.Err())\n\t\t\t\t}\n\t\t\t})\n\n\t\t\tmt.RunOpts(\"aggregate\", mtOpts, func(mt *mtest.T) {\n\t\t\t\tinitCollection(mt, mt.Coll)\n\n\t\t\t\t// Create a find cursor\n\t\t\t\topts := options.Aggregate().\n\t\t\t\t\tSetBatchSize(1).\n\t\t\t\t\tSetMaxAwaitTime(tt.maxAwaitTime)\n\n\t\t\t\tctx, cancel := context.WithTimeout(context.Background(), tt.deadline)\n\t\t\t\tdefer cancel()\n\n\t\t\t\tcur, err := mt.Coll.Aggregate(ctx, []bson.D{}, opts)\n\t\t\t\trequire.NoError(mt, err, \"Aggregate error: %v\", err)\n\n\t\t\t\t// Close to return the session to the pool.\n\t\t\t\tdefer cur.Close(context.Background())\n\n\t\t\t\tok := cur.Next(ctx)\n\t\t\t\tif tt.wantShortCircuit {\n\t\t\t\t\tassert.False(mt, ok, \"expected Next to return false, got true\")\n\t\t\t\t\tassert.EqualError(t, cur.Err(), \"MaxAwaitTime must be less than the operation timeout\")\n\t\t\t\t} else {\n\t\t\t\t\tassert.True(mt, ok, \"expected Next to return true, got false\")\n\t\t\t\t\tassert.NoError(mt, cur.Err(), \"expected no error, got %v\", cur.Err())\n\t\t\t\t}\n\t\t\t})\n\n\t\t\t// The $changeStream stage is only supported on replica sets.\n\t\t\twatchOpts := mtOpts.Topologies(mtest.ReplicaSet, mtest.Sharded)\n\t\t\tmt.RunOpts(\"watch\", watchOpts, func(mt *mtest.T) {\n\t\t\t\tinitCollection(mt, mt.Coll)\n\n\t\t\t\t// Create a find cursor\n\t\t\t\topts := options.ChangeStream().SetMaxAwaitTime(tt.maxAwaitTime)\n\n\t\t\t\tctx, cancel := context.WithTimeout(context.Background(), tt.deadline)\n\t\t\t\tdefer cancel()\n\n\t\t\t\tcur, err := mt.Coll.Watch(ctx, []bson.D{}, opts)\n\t\t\t\trequire.NoError(mt, err, \"Watch error: %v\", err)\n\n\t\t\t\t// Close to return the session to the pool.\n\t\t\t\tdefer cur.Close(context.Background())\n\n\t\t\t\tif tt.wantShortCircuit {\n\t\t\t\t\tok := cur.Next(ctx)\n\n\t\t\t\t\tassert.False(mt, ok, \"expected Next to return false, got true\")\n\t\t\t\t\tassert.EqualError(mt, cur.Err(), \"MaxAwaitTime must be less than the operation timeout\")\n\t\t\t\t}\n\t\t\t})\n\t\t})\n\t}\n}\n\ntype tryNextCursor interface {\n\tTryNext(context.Context) bool\n\tErr() error\n}\n\nfunc tryNextExistingBatchTest(mt *mtest.T, cursor tryNextCursor) {\n\tmt.Helper()\n\n\tmt.ClearEvents()\n\tassert.True(mt, cursor.TryNext(context.Background()), \"expected TryNext to return true, got false\")\n\tevt := mt.GetStartedEvent()\n\tif evt != nil {\n\t\tmt.Fatalf(\"unexpected event sent during TryNext: %v\", evt.CommandName)\n\t}\n}\n\n// use command monitoring to verify that a single getMore was sent\nfunc verifyOneGetmoreSent(mt *mtest.T) {\n\tmt.Helper()\n\n\tevt := mt.GetStartedEvent()\n\tassert.NotNil(mt, evt, \"expected getMore event, got nil\")\n\tassert.Equal(mt, \"getMore\", evt.CommandName, \"expected 'getMore' event, got '%v'\", evt.CommandName)\n\tevt = mt.GetStartedEvent()\n\tif evt != nil {\n\t\tmt.Fatalf(\"unexpected event sent during TryNext: %v\", evt.CommandName)\n\t}\n}\n\n// should be called in a test run with a mock deployment\nfunc tryNextGetmoreError(mt *mtest.T, cursor tryNextCursor) {\n\ttestErr := mtest.CommandError{\n\t\tCode:    100,\n\t\tMessage: \"getMore error\",\n\t\tName:    \"CursorError\",\n\t\tLabels:  []string{\"NonResumableChangeStreamError\"},\n\t}\n\tgetMoreRes := mtest.CreateCommandErrorResponse(testErr)\n\tmt.AddMockResponses(getMoreRes)\n\n\t// first call to TryNext should return false because first batch was empty so batch cursor returns false\n\t// without doing a getMore\n\t// next call to TryNext should attempt a getMore\n\tfor i := 0; i < 2; i++ {\n\t\tassert.False(mt, cursor.TryNext(context.Background()), \"TryNext returned true on iteration %v\", i)\n\t}\n\n\terr := cursor.Err()\n\tassert.NotNil(mt, err, \"expected change stream error, got nil\")\n\n\t// make sure that a mongo.CommandError is returned instead of a driver.Error\n\tmongoErr, ok := err.(mongo.CommandError)\n\tassert.True(mt, ok, \"expected mongo.CommandError, got: %T\", err)\n\tassert.Equal(mt, testErr.Code, mongoErr.Code, \"expected code %v, got: %v\", testErr.Code, mongoErr.Code)\n\tassert.Equal(mt, testErr.Message, mongoErr.Message, \"expected message %v, got: %v\", testErr.Message, mongoErr.Message)\n\tassert.Equal(mt, testErr.Name, mongoErr.Name, \"expected name %v, got: %v\", testErr.Name, mongoErr.Name)\n\tassert.Equal(mt, testErr.Labels, mongoErr.Labels, \"expected labels %v, got: %v\", testErr.Labels, mongoErr.Labels)\n}\n"
  },
  {
    "path": "internal/integration/database_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage integration\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"reflect\"\n\t\"testing\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/failpoint\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/handshake\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/integration/mtest\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/readpref\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n)\n\nconst (\n\tlistCollCapped   = \"listcoll_capped\"\n\tlistCollUncapped = \"listcoll_uncapped\"\n)\n\nvar interfaceAsMapRegistry = func() *bson.Registry {\n\treg := bson.NewRegistry()\n\treg.RegisterTypeMapEntry(bson.TypeEmbeddedDocument, reflect.TypeOf(bson.M{}))\n\treturn reg\n}()\n\nfunc TestDatabase(t *testing.T) {\n\tmt := mtest.New(t, mtest.NewOptions().CreateClient(false))\n\n\tmt.RunOpts(\"run command\", noClientOpts, func(mt *mtest.T) {\n\t\tmt.Run(\"decode raw\", func(mt *mtest.T) {\n\t\t\tres, err := mt.DB.RunCommand(context.Background(), bson.D{{handshake.LegacyHello, 1}}).Raw()\n\t\t\tassert.Nil(mt, err, \"RunCommand error: %v\", err)\n\n\t\t\tok, err := res.LookupErr(\"ok\")\n\t\t\tassert.Nil(mt, err, \"ok field not found in result\")\n\t\t\tassert.Equal(mt, bson.TypeDouble, ok.Type, \"expected ok type %v, got %v\", bson.TypeDouble, ok.Type)\n\t\t\tassert.Equal(mt, 1.0, ok.Double(), \"expected ok value 1.0, got %v\", ok.Double())\n\n\t\t\thello, err := res.LookupErr(handshake.LegacyHelloLowercase)\n\t\t\tassert.Nil(mt, err, \"legacy hello response field not found in result\")\n\t\t\tassert.Equal(mt, bson.TypeBoolean, hello.Type, \"expected hello type %v, got %v\", bson.TypeBoolean, hello.Type)\n\t\t\tassert.True(mt, hello.Boolean(), \"expected hello value true, got false\")\n\t\t})\n\t\tmt.Run(\"decode struct\", func(mt *mtest.T) {\n\t\t\tresult := struct {\n\t\t\t\tOk float64 `bson:\"ok\"`\n\t\t\t}{}\n\t\t\terr := mt.DB.RunCommand(context.Background(), bson.D{{\"ping\", 1}}).Decode(&result)\n\t\t\tassert.Nil(mt, err, \"RunCommand error: %v\", err)\n\t\t\tassert.Equal(mt, 1.0, result.Ok, \"expected ok value 1.0, got %v\", result.Ok)\n\t\t})\n\n\t\t// We set min server version 3.6 because pre-3.6 servers use OP_QUERY, so the command document will look like\n\t\t// {$query: {...}, $readPreference: {...}}. Per the command monitoring spec, the $query subdocument is unwrapped\n\t\t// and the $readPreference is dropped for monitoring purposes, so we can't examine it.\n\t\treadPrefOpts := mtest.NewOptions().\n\t\t\tTopologies(mtest.Sharded).\n\t\t\tMinServerVersion(\"3.6\")\n\t\tmt.RunOpts(\"read pref passed to mongos\", readPrefOpts, func(mt *mtest.T) {\n\t\t\t// When communicating with a mongos, the supplied read preference should be passed down to the operations\n\t\t\t// layer, which should add a top-level $readPreference field to the command.\n\n\t\t\trunCmdOpts := options.RunCmd().\n\t\t\t\tSetReadPreference(readpref.SecondaryPreferred())\n\t\t\terr := mt.DB.RunCommand(context.Background(), bson.D{{handshake.LegacyHello, 1}}, runCmdOpts).Err()\n\t\t\tassert.Nil(mt, err, \"RunCommand error: %v\", err)\n\n\t\t\texpected := bson.Raw(bsoncore.NewDocumentBuilder().\n\t\t\t\tAppendString(\"mode\", \"secondaryPreferred\").\n\t\t\t\tBuild())\n\t\t\tevt := mt.GetStartedEvent()\n\t\t\tassert.Equal(mt, handshake.LegacyHello, evt.CommandName, \"expected legacy hello command to be sent, got %q\", evt.CommandName)\n\t\t\tactual, ok := evt.Command.Lookup(\"$readPreference\").DocumentOK()\n\t\t\tassert.True(mt, ok, \"expected command %v to contain a $readPreference document\", evt.Command)\n\t\t\tassert.Equal(mt, expected, actual, \"expected $readPreference document %v, got %v\", expected, actual)\n\t\t})\n\t\tfailpointOpts := mtest.NewOptions().MinServerVersion(\"4.0\").Topologies(mtest.ReplicaSet)\n\t\tmt.RunOpts(\"gets result and error\", failpointOpts, func(mt *mtest.T) {\n\t\t\tmt.SetFailPoint(failpoint.FailPoint{\n\t\t\t\tConfigureFailPoint: \"failCommand\",\n\t\t\t\tMode: failpoint.Mode{\n\t\t\t\t\tTimes: 1,\n\t\t\t\t},\n\t\t\t\tData: failpoint.Data{\n\t\t\t\t\tFailCommands: []string{\"insert\"},\n\t\t\t\t\tWriteConcernError: &failpoint.WriteConcernError{\n\t\t\t\t\t\tCode: 100,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t})\n\t\t\tcmd := bson.D{\n\t\t\t\t{\"insert\", \"test\"},\n\t\t\t\t{\"documents\", bson.A{bson.D{{\"a\", 1}}}},\n\t\t\t}\n\t\t\tres, gotErr := mt.DB.RunCommand(context.Background(), cmd).Raw()\n\n\t\t\tn, ok := res.Lookup(\"n\").Int32OK()\n\t\t\tassert.True(mt, ok, \"expected n in response\")\n\t\t\tassert.Equal(mt, int32(1), n, \"expected n value 1, got %v\", n)\n\n\t\t\twriteExcept, ok := gotErr.(mongo.WriteException)\n\t\t\tassert.True(mt, ok, \"expected WriteCommandError, got %T\", gotErr)\n\t\t\tassert.NotNil(mt, writeExcept.WriteConcernError, \"expected WriteConcernError to be non-nil\")\n\t\t\tassert.Equal(mt, writeExcept.WriteConcernError.Code, 100, \"expected error code 100, got %v\", writeExcept.WriteConcernError.Code)\n\t\t})\n\t\tmt.Run(\"multi key map command\", func(mt *mtest.T) {\n\t\t\terr := mt.DB.RunCommand(context.Background(), bson.M{\"insert\": \"test\", \"documents\": bson.A{bson.D{{\"a\", 1}}}}).Err()\n\t\t\tassert.Equal(mt, mongo.ErrMapForOrderedArgument{\"cmd\"}, err, \"expected error %v, got %v\", mongo.ErrMapForOrderedArgument{\"cmd\"}, err)\n\t\t})\n\t})\n\n\tdropOpts := mtest.NewOptions().DatabaseName(\"dropDb\")\n\tmt.RunOpts(\"drop\", dropOpts, func(mt *mtest.T) {\n\t\terr := mt.DB.Drop(context.Background())\n\t\tassert.Nil(mt, err, \"Drop error: %v\", err)\n\n\t\tlist, err := mt.Client.ListDatabaseNames(context.Background(), bson.D{})\n\t\tassert.Nil(mt, err, \"ListDatabaseNames error: %v\", err)\n\t\tfor _, db := range list {\n\t\t\tif db == \"dropDb\" {\n\t\t\t\tmt.Fatal(\"dropped database 'dropDb' found in database names\")\n\t\t\t}\n\t\t}\n\t})\n\n\tlcNamesOpts := mtest.NewOptions().MinServerVersion(\"4.0\")\n\tmt.RunOpts(\"list collection names\", lcNamesOpts, func(mt *mtest.T) {\n\t\tcollName := \"lcNamesCollection\"\n\t\tmt.CreateCollection(mtest.Collection{Name: collName}, true)\n\n\t\ttestCases := []struct {\n\t\t\tname   string\n\t\t\tfilter bson.D\n\t\t\tfound  bool\n\t\t}{\n\t\t\t{\"no filter\", bson.D{}, true},\n\t\t\t{\"filter\", bson.D{{\"name\", \"lcNamesCollection\"}}, true},\n\t\t\t{\"filter not found\", bson.D{{\"name\", \"123\"}}, false},\n\t\t}\n\t\tfor _, tc := range testCases {\n\t\t\tmt.Run(tc.name, func(mt *mtest.T) {\n\t\t\t\tcolls, err := mt.DB.ListCollectionNames(context.Background(), tc.filter)\n\t\t\t\tassert.Nil(mt, err, \"ListCollectionNames error: %v\", err)\n\n\t\t\t\tvar found bool\n\t\t\t\tfor _, coll := range colls {\n\t\t\t\t\tif coll == collName {\n\t\t\t\t\t\tfound = true\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tassert.Equal(mt, tc.found, found, \"expected to find collection: %v, found collection: %v\", tc.found, found)\n\t\t\t})\n\t\t}\n\t})\n\n\tmt.RunOpts(\"list collections\", noClientOpts, func(mt *mtest.T) {\n\t\tcreateCollections := func(mt *mtest.T, numCollections int) {\n\t\t\tmt.Helper()\n\n\t\t\tfor i := 0; i < numCollections; i++ {\n\t\t\t\tmt.CreateCollection(mtest.Collection{\n\t\t\t\t\tName: fmt.Sprintf(\"list-collections-test-%d\", i),\n\t\t\t\t}, true)\n\t\t\t}\n\t\t}\n\n\t\tmt.RunOpts(\"verify results\", noClientOpts, func(mt *mtest.T) {\n\t\t\ttestCases := []struct {\n\t\t\t\tname             string\n\t\t\t\texpectedTopology mtest.TopologyKind\n\t\t\t\tcappedOnly       bool\n\t\t\t}{\n\t\t\t\t{\"standalone no filter\", mtest.Single, false},\n\t\t\t\t{\"standalone filter\", mtest.Single, true},\n\t\t\t\t{\"replica set no filter\", mtest.ReplicaSet, false},\n\t\t\t\t{\"replica set filter\", mtest.ReplicaSet, true},\n\t\t\t\t{\"sharded no filter\", mtest.Sharded, false},\n\t\t\t\t{\"sharded filter\", mtest.Sharded, true},\n\t\t\t}\n\t\t\tfor _, tc := range testCases {\n\t\t\t\ttcOpts := mtest.NewOptions().Topologies(tc.expectedTopology)\n\t\t\t\tmt.RunOpts(tc.name, tcOpts, func(mt *mtest.T) {\n\t\t\t\t\tmt.CreateCollection(mtest.Collection{Name: listCollUncapped}, true)\n\t\t\t\t\tmt.CreateCollection(mtest.Collection{\n\t\t\t\t\t\tName:       listCollCapped,\n\t\t\t\t\t\tCreateOpts: options.CreateCollection().SetCapped(true).SetSizeInBytes(64 * 1024),\n\t\t\t\t\t}, true)\n\n\t\t\t\t\tfilter := bson.D{}\n\t\t\t\t\tif tc.cappedOnly {\n\t\t\t\t\t\tfilter = bson.D{{\"options.capped\", true}}\n\t\t\t\t\t}\n\n\t\t\t\t\tvar err error\n\t\t\t\t\tfor i := 0; i < 1; i++ {\n\t\t\t\t\t\tcursor, err := mt.DB.ListCollections(context.Background(), filter)\n\t\t\t\t\t\tassert.Nil(mt, err, \"ListCollections error (iteration %v): %v\", i, err)\n\n\t\t\t\t\t\terr = verifyListCollections(cursor, tc.cappedOnly)\n\t\t\t\t\t\tif err == nil {\n\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tmt.Fatalf(\"error verifying list collections result: %v\", err)\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\n\t\t// For server versions below 3.0, we internally execute ListCollections() as a legacy\n\t\t// OP_QUERY against the system.namespaces collection. Command monitoring upconversions\n\t\t// translate this to a \"find\" command rather than \"listCollections\".\n\t\tcmdMonitoringCmdName := \"listCollections\"\n\t\tif mtest.CompareServerVersions(mtest.ServerVersion(), \"3.0\") < 0 {\n\t\t\tcmdMonitoringCmdName = \"find\"\n\t\t}\n\t\tmt.Run(\"batch size\", func(mt *mtest.T) {\n\t\t\t// Create two new collections so there will be three total.\n\t\t\tcreateCollections(mt, 2)\n\n\t\t\tmt.ClearEvents()\n\t\t\tlcOpts := options.ListCollections().SetBatchSize(2)\n\t\t\t_, err := mt.DB.ListCollectionNames(context.Background(), bson.D{}, lcOpts)\n\t\t\tassert.Nil(mt, err, \"ListCollectionNames error: %v\", err)\n\n\t\t\tevt := mt.GetStartedEvent()\n\t\t\tassert.Equal(\n\t\t\t\tmt,\n\t\t\t\tcmdMonitoringCmdName,\n\t\t\t\tevt.CommandName,\n\t\t\t\t\"expected %q command to be sent, got %q\",\n\t\t\t\tcmdMonitoringCmdName,\n\t\t\t\tevt.CommandName)\n\t\t\t_, err = evt.Command.LookupErr(\"cursor\", \"batchSize\")\n\t\t\tassert.Nil(mt, err, \"expected command %s to contain key 'batchSize'\", evt.Command)\n\t\t})\n\t\tmt.RunOpts(\"authorizedCollections\", mtest.NewOptions().MinServerVersion(\"4.0\"), func(mt *mtest.T) {\n\t\t\tmt.ClearEvents()\n\t\t\tlcOpts := options.ListCollections().SetAuthorizedCollections(true)\n\t\t\t_, err := mt.DB.ListCollections(context.Background(), bson.D{}, lcOpts)\n\t\t\tassert.Nil(mt, err, \"ListCollections error: %v\", err)\n\n\t\t\tevt := mt.GetStartedEvent()\n\t\t\tassert.Equal(mt, \"listCollections\", evt.CommandName,\n\t\t\t\t\"expected 'listCollections' command to be sent, got %q\", evt.CommandName)\n\t\t\t_, err = evt.Command.LookupErr(\"authorizedCollections\")\n\t\t\tassert.Nil(mt, err, \"expected command to contain key 'authorizedCollections'\")\n\t\t})\n\t\tmt.Run(\"getMore commands are monitored\", func(mt *mtest.T) {\n\t\t\tcreateCollections(mt, 2)\n\t\t\tassertGetMoreCommandsAreMonitored(mt, cmdMonitoringCmdName, func() (*mongo.Cursor, error) {\n\t\t\t\treturn mt.DB.ListCollections(context.Background(), bson.D{}, options.ListCollections().SetBatchSize(2))\n\t\t\t})\n\t\t})\n\t\tmt.Run(\"killCursors commands are monitored\", func(mt *mtest.T) {\n\t\t\tcreateCollections(mt, 2)\n\t\t\tassertKillCursorsCommandsAreMonitored(mt, cmdMonitoringCmdName, func() (*mongo.Cursor, error) {\n\t\t\t\treturn mt.DB.ListCollections(context.Background(), bson.D{}, options.ListCollections().SetBatchSize(2))\n\t\t\t})\n\t\t})\n\t})\n\n\tmt.RunOpts(\"list collection specifications\", noClientOpts, func(mt *mtest.T) {\n\t\tmt.Run(\"filter passed to listCollections\", func(mt *mtest.T) {\n\t\t\t// Test that ListCollectionSpecifications correctly uses the supplied filter.\n\t\t\tcappedName := \"list-collection-specs-capped\"\n\t\t\tmt.CreateCollection(mtest.Collection{\n\t\t\t\tName:       cappedName,\n\t\t\t\tCreateOpts: options.CreateCollection().SetCapped(true).SetSizeInBytes(4096),\n\t\t\t}, true)\n\n\t\t\tfilter := bson.M{\n\t\t\t\t\"options.capped\": true,\n\t\t\t}\n\t\t\tcursor, err := mt.DB.ListCollections(context.Background(), filter)\n\t\t\tassert.Nil(mt, err, \"ListCollections error: %v\", err)\n\t\t\tdefer cursor.Close(context.Background())\n\t\t\tassert.True(mt, cursor.Next(context.Background()), \"expected Next to return true, got false; cursor error: %v\",\n\t\t\t\tcursor.Err())\n\n\t\t\toptionsDoc := bsoncore.NewDocumentBuilder().\n\t\t\t\tAppendBoolean(\"capped\", true).\n\t\t\t\tAppendInt32(\"size\", 4096).\n\t\t\t\tBuild()\n\n\t\t\texpectedSpec := mongo.CollectionSpecification{\n\t\t\t\tName:     cappedName,\n\t\t\t\tType:     \"collection\",\n\t\t\t\tReadOnly: false,\n\t\t\t\tOptions:  bson.Raw(optionsDoc),\n\t\t\t}\n\t\t\tif mtest.CompareServerVersions(mtest.ServerVersion(), \"3.6\") >= 0 {\n\t\t\t\tuuidSubtype, uuidData := cursor.Current.Lookup(\"info\", \"uuid\").Binary()\n\t\t\t\texpectedSpec.UUID = &bson.Binary{Subtype: uuidSubtype, Data: uuidData}\n\t\t\t}\n\t\t\tif mtest.CompareServerVersions(mtest.ServerVersion(), \"3.4\") >= 0 {\n\t\t\t\tkeysDoc := bsoncore.NewDocumentBuilder().\n\t\t\t\t\tAppendInt32(\"_id\", 1).\n\t\t\t\t\tBuild()\n\t\t\t\texpectedSpec.IDIndex = mongo.IndexSpecification{\n\t\t\t\t\tName:         \"_id_\",\n\t\t\t\t\tNamespace:    mt.DB.Name() + \".\" + cappedName,\n\t\t\t\t\tKeysDocument: bson.Raw(keysDoc),\n\t\t\t\t\tVersion:      2,\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tspecs, err := mt.DB.ListCollectionSpecifications(context.Background(), filter)\n\t\t\tassert.Nil(mt, err, \"ListCollectionSpecifications error: %v\", err)\n\t\t\tassert.Equal(mt, 1, len(specs), \"expected 1 CollectionSpecification, got %d\", len(specs))\n\t\t\tassert.Equal(mt, expectedSpec, specs[0], \"expected specification %v, got %v\", expectedSpec, specs[0])\n\t\t})\n\n\t\tmt.RunOpts(\"options passed to listCollections\", mtest.NewOptions().MinServerVersion(\"3.0\"), func(mt *mtest.T) {\n\t\t\t// Test that ListCollectionSpecifications correctly uses the supplied options.\n\n\t\t\topts := options.ListCollections().SetNameOnly(true)\n\t\t\t_, err := mt.DB.ListCollectionSpecifications(context.Background(), bson.D{}, opts)\n\t\t\tassert.Nil(mt, err, \"ListCollectionSpecifications error: %v\", err)\n\n\t\t\tevt := mt.GetStartedEvent()\n\t\t\tassert.Equal(mt, \"listCollections\", evt.CommandName, \"expected %q command to be sent, got %q\",\n\t\t\t\t\"listCollections\", evt.CommandName)\n\t\t\tnameOnly, ok := evt.Command.Lookup(\"nameOnly\").BooleanOK()\n\t\t\tassert.True(mt, ok, \"expected command %v to contain %q field\", evt.Command, \"nameOnly\")\n\t\t\tassert.True(mt, nameOnly, \"expected nameOnly value to be true, got false\")\n\t\t})\n\t})\n\n\tmt.RunOpts(\"run command cursor\", noClientOpts, func(mt *mtest.T) {\n\t\tvar data []any\n\t\tfor i := 0; i < 5; i++ {\n\t\t\tdata = append(data, bson.D{{\"x\", i}})\n\t\t}\n\t\tfindCollName := \"runcommandcursor_find\"\n\t\tfindCmd := bson.D{{\"find\", findCollName}}\n\t\taggCollName := \"runcommandcursor_agg\"\n\t\taggCmd := bson.D{\n\t\t\t{\"aggregate\", aggCollName},\n\t\t\t{\"pipeline\", bson.A{}},\n\t\t\t{\"cursor\", bson.D{}},\n\t\t}\n\t\tpingCmd := bson.D{{\"ping\", 1}}\n\t\tpingErr := errors.New(\"database response does not contain a cursor; try using RunCommand instead\")\n\n\t\ttestCases := []struct {\n\t\t\tname        string\n\t\t\tcollName    string\n\t\t\tcmd         any\n\t\t\ttoInsert    []any\n\t\t\texpectedErr error\n\t\t\tnumExpected int\n\t\t\tminVersion  string\n\t\t}{\n\t\t\t{\"success find\", findCollName, findCmd, data, nil, 5, \"3.2\"},\n\t\t\t{\"success aggregate\", aggCollName, aggCmd, data, nil, 5, \"\"},\n\t\t\t{\"failures\", \"runcommandcursor_ping\", pingCmd, nil, pingErr, 0, \"\"},\n\t\t}\n\t\tfor _, tc := range testCases {\n\t\t\ttcOpts := mtest.NewOptions().CollectionName(tc.collName)\n\t\t\tif tc.minVersion != \"\" {\n\t\t\t\ttcOpts.MinServerVersion(tc.minVersion)\n\t\t\t}\n\n\t\t\tmt.RunOpts(tc.name, tcOpts, func(mt *mtest.T) {\n\t\t\t\tif len(tc.toInsert) > 0 {\n\t\t\t\t\t_, err := mt.Coll.InsertMany(context.Background(), tc.toInsert)\n\t\t\t\t\tassert.Nil(mt, err, \"InsertMany error: %v\", err)\n\t\t\t\t}\n\n\t\t\t\tcursor, err := mt.DB.RunCommandCursor(context.Background(), tc.cmd)\n\t\t\t\tassert.Equal(mt, tc.expectedErr, err, \"expected error %v, got %v\", tc.expectedErr, err)\n\t\t\t\tif tc.expectedErr != nil {\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tvar count int\n\t\t\t\tfor cursor.Next(context.Background()) {\n\t\t\t\t\tcount++\n\t\t\t\t}\n\t\t\t\tassert.Equal(mt, tc.numExpected, count, \"expected document count %v, got %v\", tc.numExpected, count)\n\t\t\t})\n\t\t}\n\n\t\t// The find command does not exist on server versions below 3.2.\n\t\tcmdMonitoringMtOpts := mtest.NewOptions().MinServerVersion(\"3.2\")\n\t\tmt.RunOpts(\"getMore commands are monitored\", cmdMonitoringMtOpts, func(mt *mtest.T) {\n\t\t\tinitCollection(mt, mt.Coll)\n\t\t\tassertGetMoreCommandsAreMonitored(mt, \"find\", func() (*mongo.Cursor, error) {\n\t\t\t\tfindCmd := bson.D{\n\t\t\t\t\t{\"find\", mt.Coll.Name()},\n\t\t\t\t\t{\"batchSize\", 2},\n\t\t\t\t}\n\t\t\t\treturn mt.DB.RunCommandCursor(context.Background(), findCmd)\n\t\t\t})\n\t\t})\n\t\tmt.RunOpts(\"killCursors commands are monitored\", cmdMonitoringMtOpts, func(mt *mtest.T) {\n\t\t\tinitCollection(mt, mt.Coll)\n\t\t\tassertKillCursorsCommandsAreMonitored(mt, \"find\", func() (*mongo.Cursor, error) {\n\t\t\t\tfindCmd := bson.D{\n\t\t\t\t\t{\"find\", mt.Coll.Name()},\n\t\t\t\t\t{\"batchSize\", 2},\n\t\t\t\t}\n\t\t\t\treturn mt.DB.RunCommandCursor(context.Background(), findCmd)\n\t\t\t})\n\t\t})\n\t})\n\n\tmt.RunOpts(\"create collection\", noClientOpts, func(mt *mtest.T) {\n\t\tcollectionName := \"create-collection-test\"\n\n\t\tmt.RunOpts(\"options\", noClientOpts, func(mt *mtest.T) {\n\t\t\t// Tests for various options combinations. The test creates a collection with some options and then verifies\n\t\t\t// the result using the options document reported by listCollections.\n\n\t\t\t// All possible options except collation and changeStreamPreAndPostImages. The collation is omitted here and tested below because the\n\t\t\t// collation document reported by listCollections fills in extra fields and includes a \"version\" field that's not described in\n\t\t\t// https://www.mongodb.com/docs/manual/reference/collation/. changeStreamPreAndPostImages is omitted here and tested in another testcase\n\t\t\t// because it is only an available option on 6.0+.\n\t\t\tstorageEngine := bson.M{\n\t\t\t\t\"wiredTiger\": bson.M{\n\t\t\t\t\t\"configString\": \"block_compressor=zlib\",\n\t\t\t\t},\n\t\t\t}\n\t\t\tdefaultIndexOpts := options.DefaultIndex().SetStorageEngine(storageEngine)\n\t\t\tvalidator := bson.M{\n\t\t\t\t\"$or\": bson.A{\n\t\t\t\t\tbson.M{\n\t\t\t\t\t\t\"phone\": bson.M{\"$type\": \"string\"},\n\t\t\t\t\t},\n\t\t\t\t\tbson.M{\n\t\t\t\t\t\t\"email\": bson.M{\"$type\": \"string\"},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t}\n\t\t\tnonCollationOpts := options.CreateCollection().\n\t\t\t\tSetCapped(true).\n\t\t\t\tSetDefaultIndexOptions(defaultIndexOpts).\n\t\t\t\tSetMaxDocuments(100).\n\t\t\t\tSetSizeInBytes(1024).\n\t\t\t\tSetStorageEngine(storageEngine).\n\t\t\t\tSetValidator(validator).\n\t\t\t\tSetValidationAction(\"warn\").\n\t\t\t\tSetValidationLevel(\"moderate\")\n\t\t\tnonCollationExpected := bson.M{\n\t\t\t\t\"capped\": true,\n\t\t\t\t\"indexOptionDefaults\": bson.M{\n\t\t\t\t\t\"storageEngine\": storageEngine,\n\t\t\t\t},\n\t\t\t\t\"max\":              int32(100),\n\t\t\t\t\"size\":             int32(1024),\n\t\t\t\t\"storageEngine\":    storageEngine,\n\t\t\t\t\"validator\":        validator,\n\t\t\t\t\"validationAction\": \"warn\",\n\t\t\t\t\"validationLevel\":  \"moderate\",\n\t\t\t}\n\n\t\t\tcsppiOpts := options.CreateCollection().SetChangeStreamPreAndPostImages(bson.M{\"enabled\": true})\n\t\t\tcsppiExpected := bson.M{\n\t\t\t\t\"changeStreamPreAndPostImages\": bson.M{\n\t\t\t\t\t\"enabled\": true,\n\t\t\t\t},\n\t\t\t}\n\n\t\t\ttestCases := []struct {\n\t\t\t\tname             string\n\t\t\t\tminServerVersion string\n\t\t\t\tmaxServerVersion string\n\t\t\t\tcreateOpts       *options.CreateCollectionOptionsBuilder\n\t\t\t\texpectedOpts     bson.M\n\t\t\t}{\n\t\t\t\t{\"all options except collation and csppi\", \"3.2\", \"\", nonCollationOpts, nonCollationExpected},\n\t\t\t\t{\"changeStreamPreAndPostImages\", \"6.0\", \"\", csppiOpts, csppiExpected},\n\t\t\t}\n\n\t\t\tfor _, tc := range testCases {\n\t\t\t\tmtOpts := mtest.NewOptions().\n\t\t\t\t\tMinServerVersion(tc.minServerVersion).\n\t\t\t\t\tMaxServerVersion(tc.maxServerVersion)\n\n\t\t\t\tmt.RunOpts(tc.name, mtOpts, func(mt *mtest.T) {\n\t\t\t\t\tmt.CreateCollection(mtest.Collection{\n\t\t\t\t\t\tName: collectionName,\n\t\t\t\t\t}, false)\n\n\t\t\t\t\terr := mt.DB.CreateCollection(context.Background(), collectionName, tc.createOpts)\n\t\t\t\t\tassert.Nil(mt, err, \"CreateCollection error: %v\", err)\n\n\t\t\t\t\tactualOpts := getCollectionOptions(mt, collectionName)\n\t\t\t\t\tassert.Equal(mt, tc.expectedOpts, actualOpts, \"options mismatch; expected %v, got %v\",\n\t\t\t\t\t\ttc.expectedOpts, actualOpts)\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\t\tmt.RunOpts(\"collation\", mtest.NewOptions().MinServerVersion(\"3.4\"), func(mt *mtest.T) {\n\t\t\tmt.CreateCollection(mtest.Collection{\n\t\t\t\tName: collectionName,\n\t\t\t}, false)\n\n\t\t\tlocale := \"en_US\"\n\t\t\tcreateOpts := options.CreateCollection().SetCollation(&options.Collation{\n\t\t\t\tLocale: locale,\n\t\t\t})\n\t\t\terr := mt.DB.CreateCollection(context.Background(), collectionName, createOpts)\n\t\t\tassert.Nil(mt, err, \"CreateCollection error: %v\", err)\n\n\t\t\tactualOpts := getCollectionOptions(mt, collectionName)\n\t\t\tcollationVal, ok := actualOpts[\"collation\"]\n\t\t\tassert.True(mt, ok, \"expected key 'collation' in collection options %v\", actualOpts)\n\t\t\tcollation := collationVal.(bson.M)\n\t\t\tassert.Equal(mt, locale, collation[\"locale\"], \"expected locale %v, got %v\", locale, collation[\"locale\"])\n\t\t})\n\t\tmt.Run(\"write concern\", func(mt *mtest.T) {\n\t\t\tmt.CreateCollection(mtest.Collection{\n\t\t\t\tName: collectionName,\n\t\t\t}, false)\n\n\t\t\terr := mt.DB.CreateCollection(context.Background(), collectionName)\n\t\t\tassert.Nil(mt, err, \"CreateCollection error: %v\", err)\n\n\t\t\tevt := mt.GetStartedEvent()\n\t\t\tassert.Equal(mt, evt.CommandName, \"create\", \"expected event for 'create', got '%v'\", evt.CommandName)\n\t\t\t_, err = evt.Command.LookupErr(\"writeConcern\")\n\t\t\tassert.Nil(mt, err, \"expected write concern to be included in command %v\", evt.Command)\n\t\t})\n\t})\n\n\tmt.RunOpts(\"create view\", mtest.NewOptions().CreateClient(false).MinServerVersion(\"3.4\"), func(mt *mtest.T) {\n\t\tsourceCollectionName := \"create-view-test-collection\"\n\t\tviewName := \"create-view-test-view\"\n\t\tprojectStage := bson.M{\n\t\t\t\"$project\": bson.M{\n\t\t\t\t\"projectedField\": \"foo\",\n\t\t\t},\n\t\t}\n\t\tpipeline := []bson.M{projectStage}\n\n\t\tmt.Run(\"function parameters are translated into options\", func(mt *mtest.T) {\n\t\t\tmt.CreateCollection(mtest.Collection{\n\t\t\t\tName: viewName,\n\t\t\t}, false)\n\n\t\t\terr := mt.DB.CreateView(context.Background(), viewName, sourceCollectionName, pipeline)\n\t\t\tassert.Nil(mt, err, \"CreateView error: %v\", err)\n\n\t\t\texpectedOpts := bson.M{\n\t\t\t\t\"viewOn\":   sourceCollectionName,\n\t\t\t\t\"pipeline\": bson.A{projectStage},\n\t\t\t}\n\t\t\tactualOpts := getCollectionOptions(mt, viewName)\n\t\t\tassert.Equal(mt, expectedOpts, actualOpts, \"options mismatch; expected %v, got %v\", expectedOpts,\n\t\t\t\tactualOpts)\n\t\t})\n\t\tmt.Run(\"collation\", func(mt *mtest.T) {\n\t\t\tmt.CreateCollection(mtest.Collection{\n\t\t\t\tName: viewName,\n\t\t\t}, false)\n\n\t\t\tlocale := \"en_US\"\n\t\t\tviewOpts := options.CreateView().SetCollation(&options.Collation{\n\t\t\t\tLocale: locale,\n\t\t\t})\n\t\t\terr := mt.DB.CreateView(context.Background(), viewName, sourceCollectionName, mongo.Pipeline{}, viewOpts)\n\t\t\tassert.Nil(mt, err, \"CreateView error: %v\", err)\n\n\t\t\tactualOpts := getCollectionOptions(mt, viewName)\n\t\t\tcollationVal, ok := actualOpts[\"collation\"]\n\t\t\tassert.True(mt, ok, \"expected key 'collation' in view options %v\", actualOpts)\n\t\t\tcollation := collationVal.(bson.M)\n\t\t\tassert.Equal(mt, locale, collation[\"locale\"], \"expected locale %v, got %v\", locale, collation[\"locale\"])\n\t\t})\n\t})\n}\n\nfunc getCollectionOptions(mt *mtest.T, collectionName string) bson.M {\n\tmt.Helper()\n\n\tfilter := bson.M{\n\t\t\"name\": collectionName,\n\t}\n\tcursor, err := mt.DB.ListCollections(context.Background(), filter)\n\tassert.Nil(mt, err, \"ListCollections error: %v\", err)\n\tdefer cursor.Close(context.Background())\n\tassert.True(mt, cursor.Next(context.Background()), \"expected Next to return true, got false\")\n\n\tvar actualOpts bson.M\n\tdocBytes := cursor.Current.Lookup(\"options\").Document()\n\tdec := bson.NewDecoder(bson.NewDocumentReader(bytes.NewReader(docBytes)))\n\tdec.SetRegistry(interfaceAsMapRegistry)\n\terr = dec.Decode(&actualOpts)\n\tassert.Nil(mt, err, \"UnmarshalWithRegistry error: %v\", err)\n\n\treturn actualOpts\n}\n\nfunc verifyListCollections(cursor *mongo.Cursor, cappedOnly bool) error {\n\tvar cappedFound, uncappedFound bool\n\n\tfor cursor.Next(context.Background()) {\n\t\tnameElem, err := cursor.Current.LookupErr(\"name\")\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"name element not found in document %v\", cursor.Current)\n\t\t}\n\t\tif nameElem.Type != bson.TypeString {\n\t\t\treturn fmt.Errorf(\"expected name type %v, got %v\", bson.TypeString, nameElem.Type)\n\t\t}\n\n\t\tname := nameElem.StringValue()\n\t\t// legacy servers can return an indexes collection that shouldn't be considered here\n\t\tif name != listCollUncapped && name != listCollCapped {\n\t\t\tcontinue\n\t\t}\n\n\t\tif name == listCollUncapped && !uncappedFound {\n\t\t\tif cappedOnly {\n\t\t\t\treturn fmt.Errorf(\"found uncapped collection %v but expected only capped collections\", listCollUncapped)\n\t\t\t}\n\n\t\t\tuncappedFound = true\n\t\t\tcontinue\n\t\t}\n\t\tif name == listCollCapped && !cappedFound {\n\t\t\tcappedFound = true\n\t\t\tcontinue\n\t\t}\n\n\t\t// duplicate found\n\t\treturn fmt.Errorf(\"found duplicate collection %v\", name)\n\t}\n\n\tif !cappedFound {\n\t\treturn fmt.Errorf(\"capped collection %v not found\", listCollCapped)\n\t}\n\tif !cappedOnly && !uncappedFound {\n\t\treturn fmt.Errorf(\"uncapped collection %v not found\", listCollUncapped)\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "internal/integration/errors_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\n//go:build go1.13\n\npackage integration\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"io\"\n\t\"regexp\"\n\t\"testing\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/failpoint\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/integration/mtest\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/integtest\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n)\n\nfunc containsPattern(patterns []string, str string) bool {\n\tfor _, pattern := range patterns {\n\t\tre := regexp.MustCompile(pattern)\n\t\tif re.MatchString(str) {\n\t\t\treturn true\n\t\t}\n\t}\n\n\treturn false\n}\n\nfunc TestErrors(t *testing.T) {\n\tmt := mtest.New(t, noClientOpts)\n\n\tmt.RunOpts(\"errors are wrapped\", noClientOpts, func(mt *mtest.T) {\n\t\tmt.Run(\"network error during application operation\", func(mt *mtest.T) {\n\t\t\tctx, cancel := context.WithCancel(context.Background())\n\t\t\tcancel()\n\n\t\t\terr := mt.Client.Ping(ctx, mtest.PrimaryRp)\n\t\t\tassert.True(mt, errors.Is(err, context.Canceled), \"expected error %v, got %v\", context.Canceled, err)\n\t\t})\n\n\t\tauthOpts := mtest.NewOptions().Auth(true).Topologies(mtest.ReplicaSet, mtest.Single).MinServerVersion(\"4.0\")\n\t\tmt.RunOpts(\"network error during auth\", authOpts, func(mt *mtest.T) {\n\t\t\tmt.SetFailPoint(failpoint.FailPoint{\n\t\t\t\tConfigureFailPoint: \"failCommand\",\n\t\t\t\tMode: failpoint.Mode{\n\t\t\t\t\tTimes: 1,\n\t\t\t\t},\n\t\t\t\tData: failpoint.Data{\n\t\t\t\t\t// Set the fail point for saslContinue rather than saslStart because the driver will use speculative\n\t\t\t\t\t// auth on 4.4+ so there won't be an explicit saslStart command.\n\t\t\t\t\tFailCommands:    []string{\"saslContinue\"},\n\t\t\t\t\tCloseConnection: true,\n\t\t\t\t},\n\t\t\t})\n\n\t\t\tclientOpts := options.Client().ApplyURI(mtest.ClusterURI())\n\t\t\tintegtest.AddTestServerAPIVersion(clientOpts)\n\t\t\tclient, err := mongo.Connect(clientOpts)\n\t\t\tassert.Nil(mt, err, \"Connect error: %v\", err)\n\t\t\tdefer func() { _ = client.Disconnect(context.Background()) }()\n\n\t\t\t// A connection getting closed should manifest as an io.EOF error.\n\t\t\terr = client.Ping(context.Background(), mtest.PrimaryRp)\n\t\t\tassert.True(mt, errors.Is(err, io.EOF), \"expected error %v, got %v\", io.EOF, err)\n\t\t})\n\t})\n\n\tmt.RunOpts(\"network timeouts\", noClientOpts, func(mt *mtest.T) {\n\t\tmt.Run(\"context timeouts return DeadlineExceeded\", func(mt *mtest.T) {\n\t\t\t_, err := mt.Coll.InsertOne(context.Background(), bson.D{{\"x\", 1}})\n\t\t\tassert.Nil(mt, err, \"InsertOne error: %v\", err)\n\n\t\t\tmt.ClearEvents()\n\t\t\tfilter := bson.M{\n\t\t\t\t\"$where\": \"function() { sleep(1000); return false; }\",\n\t\t\t}\n\t\t\ttimeoutCtx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)\n\t\t\tdefer cancel()\n\n\t\t\t_, err = mt.Coll.Find(timeoutCtx, filter)\n\n\t\t\tassert.Error(mt, err)\n\n\t\t\terrPatterns := []string{\n\t\t\t\tcontext.DeadlineExceeded.Error(),\n\t\t\t\t`^\\(MaxTimeMSExpired\\) Executor error during find command.*:: caused by :: operation exceeded time limit$`,\n\t\t\t}\n\n\t\t\tassert.True(t, containsPattern(errPatterns, err.Error()),\n\t\t\t\t\"expected possibleErrors=%v to contain %v, but it didn't\",\n\t\t\t\terrPatterns, err.Error())\n\n\t\t\tevt := mt.GetStartedEvent()\n\t\t\tassert.Equal(mt, \"find\", evt.CommandName, \"expected command 'find', got %q\", evt.CommandName)\n\t\t})\n\t})\n\tmt.Run(\"ServerError\", func(mt *mtest.T) {\n\t\tmtOpts := mtest.NewOptions().MinServerVersion(\"4.0\").Topologies(mtest.ReplicaSet)\n\t\tmt.RunOpts(\"Raw response\", mtOpts, func(mt *mtest.T) {\n\t\t\tmt.Run(\"CommandError\", func(mt *mtest.T) {\n\t\t\t\t// Mock a CommandError via failpoint with an arbitrary code.\n\t\t\t\tmt.SetFailPoint(failpoint.FailPoint{\n\t\t\t\t\tConfigureFailPoint: \"failCommand\",\n\t\t\t\t\tMode: failpoint.Mode{\n\t\t\t\t\t\tTimes: 1,\n\t\t\t\t\t},\n\t\t\t\t\tData: failpoint.Data{\n\t\t\t\t\t\tFailCommands: []string{\"find\"},\n\t\t\t\t\t\tErrorCode:    123,\n\t\t\t\t\t},\n\t\t\t\t})\n\n\t\t\t\tres := mt.Coll.FindOne(context.Background(), bson.D{})\n\t\t\t\tassert.NotNil(mt, res.Err(), \"expected FindOne error, got nil\")\n\t\t\t\tce, ok := res.Err().(mongo.CommandError)\n\t\t\t\tassert.True(mt, ok, \"expected FindOne error to be CommandError, got %T\", res.Err())\n\n\t\t\t\t// Assert that raw response exists and contains error code 123.\n\t\t\t\tassert.NotNil(mt, ce.Raw, \"ce.Raw is nil\")\n\t\t\t\tval, err := ce.Raw.LookupErr(\"code\")\n\t\t\t\tassert.Nil(mt, err, \"expected 'code' field in ce.Raw, got %v\", ce.Raw)\n\t\t\t\tcode, ok := val.AsInt64OK()\n\t\t\t\tassert.True(mt, ok, \"expected 'code' to be int64, got %v\", val)\n\t\t\t\tassert.Equal(mt, code, int64(123), \"expected 'code' 123, got %d\", code)\n\t\t\t})\n\t\t\tmt.Run(\"WriteError\", func(mt *mtest.T) {\n\t\t\t\t// Mock a WriteError by inserting documents with duplicate _id fields.\n\t\t\t\t_, err := mt.Coll.InsertOne(context.Background(), bson.D{{\"_id\", 1}})\n\t\t\t\tassert.Nil(mt, err, \"InsertOne error: %v\", err)\n\n\t\t\t\t_, err = mt.Coll.InsertOne(context.Background(), bson.D{{\"_id\", 1}})\n\t\t\t\tassert.NotNil(mt, err, \"expected InsertOne error, got nil\")\n\t\t\t\twe, ok := err.(mongo.WriteException)\n\t\t\t\tassert.True(mt, ok, \"expected InsertOne error to be WriteException, got %T\", err)\n\n\t\t\t\tassert.NotNil(mt, we.WriteErrors, \"expected we.WriteErrors, got nil\")\n\t\t\t\tassert.NotNil(mt, we.WriteErrors[0], \"expected at least one WriteError\")\n\n\t\t\t\t// Assert that raw response exists for the WriteError and contains error code 11000.\n\t\t\t\traw := we.WriteErrors[0].Raw\n\t\t\t\tassert.NotNil(mt, raw, \"Raw of WriteError is nil\")\n\t\t\t\tval, err := raw.LookupErr(\"code\")\n\t\t\t\tassert.Nil(mt, err, \"expected 'code' field in Raw field, got %v\", raw)\n\t\t\t\tcode, ok := val.AsInt64OK()\n\t\t\t\tassert.True(mt, ok, \"expected 'code' to be int64, got %v\", val)\n\t\t\t\tassert.Equal(mt, code, int64(11000), \"expected 'code' 11000, got %d\", code)\n\t\t\t})\n\t\t\tmt.Run(\"WriteException\", func(mt *mtest.T) {\n\t\t\t\t// Mock a WriteException via failpoint with an arbitrary WriteConcernError.\n\t\t\t\tmt.SetFailPoint(failpoint.FailPoint{\n\t\t\t\t\tConfigureFailPoint: \"failCommand\",\n\t\t\t\t\tMode: failpoint.Mode{\n\t\t\t\t\t\tTimes: 1,\n\t\t\t\t\t},\n\t\t\t\t\tData: failpoint.Data{\n\t\t\t\t\t\tFailCommands: []string{\"delete\"},\n\t\t\t\t\t\tWriteConcernError: &failpoint.WriteConcernError{\n\t\t\t\t\t\t\tCode: 123,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t})\n\n\t\t\t\t_, err := mt.Coll.DeleteMany(context.Background(), bson.D{})\n\t\t\t\tassert.NotNil(mt, err, \"expected DeleteMany error, got nil\")\n\t\t\t\twe, ok := err.(mongo.WriteException)\n\t\t\t\tassert.True(mt, ok, \"expected DeleteMany error to be WriteException, got %T\", err)\n\n\t\t\t\t// Assert that raw response exists and contains error code 123.\n\t\t\t\tassert.NotNil(mt, we.Raw, \"expected RawResponse, got nil\")\n\t\t\t\tval, err := we.Raw.LookupErr(\"writeConcernError\", \"code\")\n\t\t\t\tassert.Nil(mt, err, \"expected 'code' field in RawResponse, got %v\", we.Raw)\n\t\t\t\tcode, ok := val.AsInt64OK()\n\t\t\t\tassert.True(mt, ok, \"expected 'code' to be int64, got %v\", val)\n\t\t\t\tassert.Equal(mt, code, int64(123), \"expected 'code' 123, got %d\", code)\n\t\t\t})\n\t\t})\n\t})\n}\n"
  },
  {
    "path": "internal/integration/gridfs_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage integration\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"io\"\n\t\"math/rand\"\n\t\"runtime\"\n\t\"testing\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/event\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/integration/mtest\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/israce\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/require\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n)\n\nfunc TestGridFS(x *testing.T) {\n\tmt := mtest.New(x, noClientOpts)\n\n\tmt.Run(\"skipping download\", func(mt *mtest.T) {\n\t\tdata := []byte(\"abc.def.ghi\")\n\t\tvar chunkSize int32 = 4\n\n\t\ttestcases := []struct {\n\t\t\tname string\n\n\t\t\tread              int\n\t\t\tskip              int64\n\t\t\texpectedSkipN     int64\n\t\t\texpectedSkipErr   error\n\t\t\texpectedRemaining int\n\t\t}{\n\t\t\t{\n\t\t\t\t\"read 0, skip 0\", 0, 0, 0, nil, 11,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"read 0, skip to end of chunk\", 0, 4, 4, nil, 7,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"read 0, skip 1\", 0, 1, 1, nil, 10,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"read 1, skip to end of chunk\", 1, 3, 3, nil, 7,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"read all, skip beyond\", 11, 1, 0, nil, 0,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"skip all\", 0, 11, 11, nil, 0,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"read 1, skip to last chunk\", 1, 8, 8, nil, 2,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"read to last chunk, skip to end\", 9, 2, 2, nil, 0,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"read to last chunk, skip beyond\", 9, 4, 2, nil, 0,\n\t\t\t},\n\t\t}\n\n\t\tfor _, tc := range testcases {\n\t\t\tmt.Run(tc.name, func(mt *mtest.T) {\n\t\t\t\tbucket := mt.DB.GridFSBucket(options.GridFSBucket().SetChunkSizeBytes(chunkSize))\n\n\t\t\t\tustream, err := bucket.OpenUploadStream(context.Background(), \"foo\")\n\t\t\t\trequire.NoError(mt, err, \"OpenUploadStream error: %v\", err)\n\n\t\t\t\tid := ustream.FileID\n\t\t\t\t_, err = ustream.Write(data)\n\t\t\t\trequire.NoError(mt, err, \"Write error: %v\", err)\n\t\t\t\terr = ustream.Close()\n\t\t\t\trequire.NoError(mt, err, \"Close error: %v\", err)\n\n\t\t\t\tdstream, err := bucket.OpenDownloadStream(context.Background(), id)\n\t\t\t\trequire.NoError(mt, err, \"OpenDownloadStream error\")\n\t\t\t\tdst := make([]byte, tc.read)\n\t\t\t\t_, err = dstream.Read(dst)\n\t\t\t\trequire.NoError(mt, err, \"Read error: %v\", err)\n\n\t\t\t\tn, err := dstream.Skip(tc.skip)\n\t\t\t\tassert.Equal(mt, tc.expectedSkipErr, err, \"expected error on Skip: %v, got %v\", tc.expectedSkipErr, err)\n\t\t\t\tassert.Equal(mt, tc.expectedSkipN, n, \"expected Skip to return: %v, got %v\", tc.expectedSkipN, n)\n\n\t\t\t\t// Read the rest.\n\t\t\t\tdst = make([]byte, len(data))\n\t\t\t\tremaining, err := dstream.Read(dst)\n\t\t\t\tif err != nil {\n\t\t\t\t\tassert.Equal(mt, err, io.EOF, \"unexpected Read error: %v\", err)\n\t\t\t\t}\n\t\t\t\tassert.Equal(mt, tc.expectedRemaining, remaining, \"expected remaining data to be: %v, got %v\", tc.expectedRemaining, remaining)\n\t\t\t})\n\t\t}\n\t})\n\n\tmt.Run(\"index creation\", func(mt *mtest.T) {\n\t\t// Unit tests showing that UploadFromStream creates indexes on the chunks and files collections.\n\t\tbucket := mt.DB.GridFSBucket()\n\n\t\tbyteData := []byte(\"Hello, world!\")\n\t\tr := bytes.NewReader(byteData)\n\n\t\tuploadCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)\n\t\tmt.Cleanup(cancel)\n\n\t\t_, err := bucket.UploadFromStream(uploadCtx, \"filename\", r)\n\t\trequire.NoError(mt, err, \"UploadFromStream error: %v\", err)\n\n\t\tfindCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)\n\t\tmt.Cleanup(cancel)\n\n\t\tfindIndex(findCtx, mt, mt.DB.Collection(\"fs.files\"), false, \"key\", \"filename\")\n\t\tfindIndex(findCtx, mt, mt.DB.Collection(\"fs.chunks\"), true, \"key\", \"files_id\")\n\t})\n\t// should not create a new index if index is numerically the same\n\tmt.Run(\"equivalent indexes\", func(mt *mtest.T) {\n\t\ttests := []struct {\n\t\t\tname        string\n\t\t\tfilesIndex  bson.D\n\t\t\tchunksIndex bson.D\n\t\t\tnewIndexes  bool\n\t\t}{\n\t\t\t{\n\t\t\t\t\"numerically equal\",\n\t\t\t\tbson.D{\n\t\t\t\t\t{\"key\", bson.D{{\"filename\", float64(1.0)}, {\"uploadDate\", float64(1.0)}}},\n\t\t\t\t\t{\"name\", \"filename_1_uploadDate_1\"},\n\t\t\t\t},\n\t\t\t\tbson.D{\n\t\t\t\t\t{\"key\", bson.D{{\"files_id\", float64(1.0)}, {\"n\", float64(1.0)}}},\n\t\t\t\t\t{\"name\", \"files_id_1_n_1\"},\n\t\t\t\t\t{\"unique\", true},\n\t\t\t\t},\n\t\t\t\tfalse,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"numerically inequal\",\n\t\t\t\tbson.D{\n\t\t\t\t\t{\"key\", bson.D{{\"filename\", float64(-1.0)}, {\"uploadDate\", float64(1.0)}}},\n\t\t\t\t\t{\"name\", \"filename_-1_uploadDate_1\"},\n\t\t\t\t},\n\t\t\t\tbson.D{\n\t\t\t\t\t{\"key\", bson.D{{\"files_id\", float64(1.0)}, {\"n\", float64(-1.0)}}},\n\t\t\t\t\t{\"name\", \"files_id_1_n_-1\"},\n\t\t\t\t\t{\"unique\", true},\n\t\t\t\t},\n\t\t\t\ttrue,\n\t\t\t},\n\t\t}\n\t\tfor _, test := range tests {\n\t\t\tmt.Run(test.name, func(mt *mtest.T) {\n\t\t\t\tmt.Run(\"OpenUploadStream\", func(mt *mtest.T) {\n\t\t\t\t\t// add indexes with floats to collections manually\n\t\t\t\t\tres := mt.DB.RunCommand(context.Background(),\n\t\t\t\t\t\tbson.D{\n\t\t\t\t\t\t\t{\"createIndexes\", \"fs.files\"},\n\t\t\t\t\t\t\t{\"indexes\", bson.A{\n\t\t\t\t\t\t\t\ttest.filesIndex,\n\t\t\t\t\t\t\t}},\n\t\t\t\t\t\t},\n\t\t\t\t\t)\n\t\t\t\t\trequire.NoError(mt, res.Err(), \"createIndexes error: %v\", res.Err())\n\n\t\t\t\t\tres = mt.DB.RunCommand(context.Background(),\n\t\t\t\t\t\tbson.D{\n\t\t\t\t\t\t\t{\"createIndexes\", \"fs.chunks\"},\n\t\t\t\t\t\t\t{\"indexes\", bson.A{\n\t\t\t\t\t\t\t\ttest.chunksIndex,\n\t\t\t\t\t\t\t}},\n\t\t\t\t\t\t},\n\t\t\t\t\t)\n\t\t\t\t\trequire.NoError(mt, res.Err(), \"createIndexes error: %v\", res.Err())\n\n\t\t\t\t\tmt.ClearEvents()\n\n\t\t\t\t\tbucket := mt.DB.GridFSBucket()\n\t\t\t\t\tdefer func() {\n\t\t\t\t\t\t_ = bucket.Drop(context.Background())\n\t\t\t\t\t}()\n\n\t\t\t\t\t_, err := bucket.OpenUploadStream(context.Background(), \"filename\")\n\t\t\t\t\trequire.NoError(mt, err, \"OpenUploadStream error: %v\", err)\n\n\t\t\t\t\tmt.FilterStartedEvents(func(evt *event.CommandStartedEvent) bool {\n\t\t\t\t\t\treturn evt.CommandName == \"createIndexes\"\n\t\t\t\t\t})\n\t\t\t\t\tevt := mt.GetStartedEvent()\n\t\t\t\t\tif test.newIndexes {\n\t\t\t\t\t\tif evt == nil {\n\t\t\t\t\t\t\tmt.Fatalf(\"expected createIndexes events but got none\")\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif evt != nil {\n\t\t\t\t\t\t\tmt.Fatalf(\"expected no createIndexes events but got %v\", evt.Command)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t\tmt.Run(\"UploadFromStream\", func(mt *mtest.T) {\n\t\t\t\t\t// add indexes with floats to collections manually\n\t\t\t\t\tres := mt.DB.RunCommand(context.Background(),\n\t\t\t\t\t\tbson.D{\n\t\t\t\t\t\t\t{\"createIndexes\", \"fs.files\"},\n\t\t\t\t\t\t\t{\"indexes\", bson.A{\n\t\t\t\t\t\t\t\ttest.filesIndex,\n\t\t\t\t\t\t\t}},\n\t\t\t\t\t\t},\n\t\t\t\t\t)\n\t\t\t\t\trequire.NoError(mt, res.Err(), \"createIndexes error: %v\", res.Err())\n\n\t\t\t\t\tres = mt.DB.RunCommand(context.Background(),\n\t\t\t\t\t\tbson.D{\n\t\t\t\t\t\t\t{\"createIndexes\", \"fs.chunks\"},\n\t\t\t\t\t\t\t{\"indexes\", bson.A{\n\t\t\t\t\t\t\t\ttest.chunksIndex,\n\t\t\t\t\t\t\t}},\n\t\t\t\t\t\t},\n\t\t\t\t\t)\n\t\t\t\t\trequire.NoError(mt, res.Err(), \"createIndexes error: %v\", res.Err())\n\n\t\t\t\t\tmt.ClearEvents()\n\t\t\t\t\tvar fileContent []byte\n\t\t\t\t\tbucket := mt.DB.GridFSBucket()\n\n\t\t\t\t\tdefer func() {\n\t\t\t\t\t\t_ = bucket.Drop(context.Background())\n\t\t\t\t\t}()\n\n\t\t\t\t\t_, err := bucket.UploadFromStream(context.Background(), \"filename\", bytes.NewBuffer(fileContent))\n\t\t\t\t\trequire.NoError(mt, err, \"UploadFromStream error: %v\", err)\n\n\t\t\t\t\tmt.FilterStartedEvents(func(evt *event.CommandStartedEvent) bool {\n\t\t\t\t\t\treturn evt.CommandName == \"createIndexes\"\n\t\t\t\t\t})\n\t\t\t\t\tevt := mt.GetStartedEvent()\n\t\t\t\t\tif test.newIndexes {\n\t\t\t\t\t\tif evt == nil {\n\t\t\t\t\t\t\tmt.Fatalf(\"expected createIndexes events but got none\")\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif evt != nil {\n\t\t\t\t\t\t\tmt.Fatalf(\"expected no createIndexes events but got %v\", evt.Command)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t})\n\t\t}\n\t})\n\n\tmt.RunOpts(\"download\", noClientOpts, func(mt *mtest.T) {\n\t\tmt.RunOpts(\"get file data\", noClientOpts, func(mt *mtest.T) {\n\t\t\t// Tests for the DownloadStream.GetFile method.\n\n\t\t\tfileName := \"get-file-data-test\"\n\t\t\tfileData := []byte{1, 2, 3, 4}\n\t\t\tfileMetadata := bson.D{{\"k1\", \"v1\"}, {\"k2\", \"v2\"}}\n\t\t\trawMetadata, err := bson.Marshal(fileMetadata)\n\t\t\trequire.NoError(mt, err, \"Marshal error: %v\", err)\n\t\t\tuploadOpts := options.GridFSUpload().SetMetadata(fileMetadata)\n\n\t\t\ttestCases := []struct {\n\t\t\t\tname   string\n\t\t\t\tfileID any\n\t\t\t}{\n\t\t\t\t{\"default ID\", nil},\n\t\t\t\t{\"custom ID type\", \"customID\"},\n\t\t\t}\n\t\t\tfor _, tc := range testCases {\n\t\t\t\tmt.Run(tc.name, func(mt *mtest.T) {\n\t\t\t\t\t// Create a new GridFS bucket.\n\t\t\t\t\tbucket := mt.DB.GridFSBucket()\n\t\t\t\t\tdefer func() { _ = bucket.Drop(context.Background()) }()\n\n\t\t\t\t\t// Upload the file and store the uploaded file ID.\n\t\t\t\t\tuploadedFileID := tc.fileID\n\t\t\t\t\tdataReader := bytes.NewReader(fileData)\n\t\t\t\t\tif uploadedFileID == nil {\n\t\t\t\t\t\tuploadedFileID, err = bucket.UploadFromStream(context.Background(), fileName, dataReader, uploadOpts)\n\t\t\t\t\t} else {\n\t\t\t\t\t\terr = bucket.UploadFromStreamWithID(context.Background(), tc.fileID, fileName, dataReader, uploadOpts)\n\t\t\t\t\t}\n\t\t\t\t\trequire.NoError(mt, err, \"error uploading file: %v\", err)\n\n\t\t\t\t\t// The uploadDate field is calculated when the upload is complete. Manually fetch it from the\n\t\t\t\t\t// fs.files collection to use in assertions.\n\t\t\t\t\tfilesColl := mt.DB.Collection(\"fs.files\")\n\t\t\t\t\tuploadedFileDoc, err := filesColl.FindOne(context.Background(), bson.D{}).Raw()\n\t\t\t\t\trequire.NoError(mt, err, \"FindOne error: %v\", err)\n\t\t\t\t\tuploadTime := uploadedFileDoc.Lookup(\"uploadDate\").Time().UTC()\n\n\t\t\t\t\texpectedFile := &mongo.GridFSFile{\n\t\t\t\t\t\tID:         uploadedFileID,\n\t\t\t\t\t\tLength:     int64(len(fileData)),\n\t\t\t\t\t\tChunkSize:  mongo.DefaultGridFSChunkSize,\n\t\t\t\t\t\tUploadDate: uploadTime,\n\t\t\t\t\t\tName:       fileName,\n\t\t\t\t\t\tMetadata:   rawMetadata,\n\t\t\t\t\t}\n\t\t\t\t\t// For both methods that create a DownloadStream, open a stream and compare the file given by the\n\t\t\t\t\t// stream to the expected File object.\n\t\t\t\t\tmt.RunOpts(\"OpenDownloadStream\", noClientOpts, func(mt *mtest.T) {\n\t\t\t\t\t\tdownloadStream, err := bucket.OpenDownloadStream(context.Background(), uploadedFileID)\n\t\t\t\t\t\trequire.NoError(mt, err, \"OpenDownloadStream error: %v\", err)\n\t\t\t\t\t\tactualFile := downloadStream.GetFile()\n\t\t\t\t\t\tassert.Equal(mt, expectedFile, actualFile, \"expected file %v, got %v\", expectedFile, actualFile)\n\t\t\t\t\t})\n\t\t\t\t\tmt.RunOpts(\"OpenDownloadStreamByName\", noClientOpts, func(mt *mtest.T) {\n\t\t\t\t\t\tdownloadStream, err := bucket.OpenDownloadStreamByName(context.Background(), fileName)\n\t\t\t\t\t\trequire.NoError(mt, err, \"OpenDownloadStream error: %v\", err)\n\t\t\t\t\t\tactualFile := downloadStream.GetFile()\n\t\t\t\t\t\tassert.Equal(mt, expectedFile, actualFile, \"expected file %v, got %v\", expectedFile, actualFile)\n\t\t\t\t\t})\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\t\tmt.Run(\"chunk size determined by files collection document\", func(mt *mtest.T) {\n\t\t\t// Test that the chunk size for a file download is determined by the chunkSize field in the files\n\t\t\t// collection document, not the bucket's chunk size.\n\n\t\t\tbucket := mt.DB.GridFSBucket()\n\t\t\tdefer func() { _ = bucket.Drop(context.Background()) }()\n\n\t\t\tfileData := []byte(\"hello world\")\n\t\t\tuploadOpts := options.GridFSUpload().SetChunkSizeBytes(4)\n\t\t\tfileID, err := bucket.UploadFromStream(context.Background(), \"file\", bytes.NewReader(fileData), uploadOpts)\n\t\t\trequire.NoError(mt, err, \"UploadFromStream error: %v\", err)\n\n\t\t\t// If the bucket's chunk size was used, this would error because the actual chunk size is 4 and the bucket\n\t\t\t// chunk size is 255 KB.\n\t\t\tvar downloadBuffer bytes.Buffer\n\t\t\t_, err = bucket.DownloadToStream(context.Background(), fileID, &downloadBuffer)\n\t\t\trequire.NoError(mt, err, \"DownloadToStream error: %v\", err)\n\n\t\t\tdownloadedBytes := downloadBuffer.Bytes()\n\t\t\tassert.Equal(mt, fileData, downloadedBytes, \"expected bytes %s, got %s\", fileData, downloadedBytes)\n\t\t})\n\t\tmt.Run(\"error if files collection document does not have a chunkSize field\", func(mt *mtest.T) {\n\t\t\t// Test that opening a download returns ErrMissingChunkSize if the files collection document has no\n\t\t\t// chunk size field.\n\n\t\t\toid := bson.NewObjectID()\n\t\t\tfilesDoc := bson.D{\n\t\t\t\t{\"_id\", oid},\n\t\t\t\t{\"length\", 10},\n\t\t\t\t{\"filename\", \"filename\"},\n\t\t\t}\n\t\t\t_, err := mt.DB.Collection(\"fs.files\").InsertOne(context.Background(), filesDoc)\n\t\t\trequire.NoError(mt, err, \"InsertOne error for files collection: %v\", err)\n\n\t\t\tbucket := mt.DB.GridFSBucket()\n\t\t\tdefer func() { _ = bucket.Drop(context.Background()) }()\n\n\t\t\t_, err = bucket.OpenDownloadStream(context.Background(), oid)\n\t\t\tassert.Equal(mt, mongo.ErrMissingGridFSChunkSize, err,\n\t\t\t\t\"expected error %v, got %v\", mongo.ErrMissingGridFSChunkSize, err)\n\t\t})\n\t\tmt.Run(\"cursor error during read after downloading\", func(mt *mtest.T) {\n\t\t\t// To simulate a cursor error we upload a file larger than the 16MB default batch size,\n\t\t\t// so the underlying cursor remains open on the server. Since the ReadDeadline is\n\t\t\t// set in the past, Read should cause a timeout.\n\n\t\t\tfileName := \"read-error-test\"\n\t\t\tfileData := make([]byte, 17000000)\n\n\t\t\tbucket := mt.DB.GridFSBucket()\n\t\t\tdefer func() { _ = bucket.Drop(context.Background()) }()\n\n\t\t\tdataReader := bytes.NewReader(fileData)\n\t\t\t_, err := bucket.UploadFromStream(context.Background(), fileName, dataReader)\n\t\t\trequire.NoError(mt, err, \"UploadFromStream error: %v\", err)\n\n\t\t\tctx, cancel := context.WithCancel(context.Background())\n\n\t\t\tds, err := bucket.OpenDownloadStreamByName(ctx, fileName)\n\t\t\tassert.NoError(mt, err, \"OpenDownloadStreamByName error: %v\", err)\n\n\t\t\tcancel()\n\n\t\t\tp := make([]byte, len(fileData))\n\t\t\t_, err = ds.Read(p)\n\t\t\tassert.NotNil(mt, err, \"expected error from Read, got nil\")\n\t\t\tassert.ErrorIs(mt, context.Canceled, err)\n\t\t})\n\t\tmt.Run(\"cursor error during skip after downloading\", func(mt *mtest.T) {\n\t\t\t// To simulate a cursor error we upload a file larger than the 16MB default batch size,\n\t\t\t// so the underlying cursor remains open on the server. Since the ReadDeadline is\n\t\t\t// set in the past, Skip should cause a timeout.\n\n\t\t\tfileName := \"skip-error-test\"\n\t\t\tfileData := make([]byte, 17000000)\n\n\t\t\tbucket := mt.DB.GridFSBucket()\n\t\t\tdefer func() { _ = bucket.Drop(context.Background()) }()\n\n\t\t\tdataReader := bytes.NewReader(fileData)\n\t\t\t_, err := bucket.UploadFromStream(context.Background(), fileName, dataReader)\n\t\t\trequire.NoError(mt, err, \"UploadFromStream error: %v\", err)\n\n\t\t\tctx, cancel := context.WithCancel(context.Background())\n\n\t\t\tds, err := bucket.OpenDownloadStreamByName(ctx, fileName)\n\t\t\trequire.NoError(mt, err, \"OpenDownloadStreamByName error: %v\", err)\n\n\t\t\tcancel()\n\n\t\t\t_, err = ds.Skip(int64(len(fileData)))\n\t\t\tassert.Error(mt, err, \"expected error from Skip\")\n\t\t\tassert.ErrorIs(mt, context.Canceled, err)\n\t\t})\n\t})\n\n\tmt.RunOpts(\"bucket collection accessors\", noClientOpts, func(mt *mtest.T) {\n\t\t// Tests for the GetFilesCollection and GetChunksCollection accessors.\n\n\t\tfileData := []byte{1, 2, 3, 4}\n\t\tvar chunkSize int32 = 2\n\n\t\ttestCases := []struct {\n\t\t\tname       string\n\t\t\tbucketName string // defaults to \"fs\"\n\t\t}{\n\t\t\t{\"default bucket name\", \"\"},\n\t\t\t{\"custom bucket name\", \"bucket\"},\n\t\t}\n\t\tfor _, tc := range testCases {\n\t\t\tmt.Run(tc.name, func(mt *mtest.T) {\n\t\t\t\tbucketOpts := options.GridFSBucket().SetChunkSizeBytes(chunkSize)\n\t\t\t\tif tc.bucketName != \"\" {\n\t\t\t\t\tbucketOpts.SetName(tc.bucketName)\n\t\t\t\t}\n\t\t\t\tbucket := mt.DB.GridFSBucket(bucketOpts)\n\t\t\t\tdefer func() { _ = bucket.Drop(context.Background()) }()\n\n\t\t\t\t_, err := bucket.UploadFromStream(context.Background(), \"accessors-test-file\", bytes.NewReader(fileData))\n\t\t\t\trequire.NoError(mt, err, \"UploadFromStream error: %v\", err)\n\n\t\t\t\tbucketName := tc.bucketName\n\t\t\t\tif bucketName == \"\" {\n\t\t\t\t\tbucketName = \"fs\"\n\t\t\t\t}\n\t\t\t\tassertGridFSCollectionState(mt, bucket.GetFilesCollection(), bucketName+\".files\", 1)\n\t\t\t\tassertGridFSCollectionState(mt, bucket.GetChunksCollection(), bucketName+\".chunks\", 2)\n\t\t\t})\n\t\t}\n\t})\n\n\tmt.RunOpts(\"round trip\", mtest.NewOptions().MaxServerVersion(\"3.6\"), func(mt *mtest.T) {\n\t\tskipRoundTripTest(mt)\n\n\t\tconst oneK = 1024\n\t\tconst smallBuffSize = 100\n\n\t\ttests := []struct {\n\t\t\tname      string\n\t\t\tchunkSize int32 // make -1 for no capacity for no chunkSize\n\t\t\tfileSize  int\n\t\t\tbufSize   int // make -1 for no capacity for no bufSize\n\t\t}{\n\t\t\t{\"RoundTrip: original\", -1, oneK, -1},\n\t\t\t{\"RoundTrip: chunk size multiple of file\", oneK, oneK * 16, -1},\n\t\t\t{\"RoundTrip: chunk size is file size\", oneK, oneK, -1},\n\t\t\t{\"RoundTrip: chunk size multiple of file size and with strict buffer size\", oneK, oneK * 16, smallBuffSize},\n\t\t\t{\"RoundTrip: chunk size multiple of file size and buffer size\", oneK, oneK * 16, oneK * 16},\n\t\t\t{\"RoundTrip: chunk size, file size, buffer size all the same\", oneK, oneK, oneK},\n\t\t}\n\n\t\tfor _, test := range tests {\n\t\t\tmt.Run(test.name, func(mt *mtest.T) {\n\t\t\t\topts := options.GridFSBucket()\n\t\t\t\tif test.chunkSize != -1 {\n\t\t\t\t\topts.SetChunkSizeBytes(test.chunkSize)\n\t\t\t\t}\n\n\t\t\t\tbucket := mt.DB.GridFSBucket(opts)\n\n\t\t\t\ttimeout := 5 * time.Second\n\t\t\t\tif israce.Enabled {\n\t\t\t\t\ttimeout = 20 * time.Second // race detector causes 2-20x slowdown\n\t\t\t\t}\n\n\t\t\t\t// Test that Upload works when the buffer to write is longer than the upload stream's internal buffer.\n\t\t\t\t// This requires multiple calls to uploadChunks.\n\t\t\t\tsize := test.fileSize\n\t\t\t\tp := make([]byte, size)\n\t\t\t\tfor i := 0; i < size; i++ {\n\t\t\t\t\tp[i] = byte(rand.Intn(100))\n\t\t\t\t}\n\n\t\t\t\tctx, cancel := context.WithTimeout(context.Background(), timeout)\n\t\t\t\tmt.Cleanup(cancel)\n\n\t\t\t\t_, err := bucket.UploadFromStream(ctx, \"filename\", bytes.NewReader(p))\n\t\t\t\trequire.NoError(mt, err, \"UploadFromStream error: %v\", err)\n\n\t\t\t\tvar w *bytes.Buffer\n\t\t\t\tif test.bufSize == -1 {\n\t\t\t\t\tw = bytes.NewBuffer(make([]byte, 0))\n\t\t\t\t} else {\n\t\t\t\t\tw = bytes.NewBuffer(make([]byte, 0, test.bufSize))\n\t\t\t\t}\n\n\t\t\t\t_, err = bucket.DownloadToStreamByName(ctx, \"filename\", w)\n\t\t\t\trequire.NoError(mt, err, \"DownloadToStreamByName error: %v\", err)\n\t\t\t\tassert.Equal(mt, p, w.Bytes(), \"downloaded file did not match p\")\n\t\t\t})\n\t\t}\n\t})\n\n\t// Regression test for a bug introduced in GODRIVER-2346.\n\tmt.Run(\"Find\", func(mt *mtest.T) {\n\t\tbucket := mt.DB.GridFSBucket()\n\t\t// Find the file back.\n\t\tcursor, err := bucket.Find(context.Background(), bson.D{{\"foo\", \"bar\"}})\n\t\tdefer func() {\n\t\t\t_ = cursor.Close(context.Background())\n\t\t}()\n\n\t\tassert.NoError(mt, err, \"Find error: %v\", err)\n\t})\n}\n\nfunc assertGridFSCollectionState(mt *mtest.T, coll *mongo.Collection, expectedName string, expectedNumDocuments int64) {\n\tmt.Helper()\n\n\tassert.Equal(mt, expectedName, coll.Name(), \"expected collection name %v, got %v\", expectedName, coll.Name())\n\tcount, err := coll.CountDocuments(context.Background(), bson.D{})\n\trequire.NoError(mt, err, \"CountDocuments error: %v\", err)\n\tassert.Equal(mt, expectedNumDocuments, count, \"expected %d documents in collection, got %d\", expectedNumDocuments,\n\t\tcount)\n}\n\nfunc findIndex(ctx context.Context, mt *mtest.T, coll *mongo.Collection, unique bool, keys ...string) {\n\tmt.Helper()\n\tcur, err := coll.Indexes().List(ctx)\n\trequire.NoError(mt, err, \"Indexes List error: %v\", err)\n\n\tfoundIndex := false\n\tfor cur.Next(ctx) {\n\t\tif _, err := cur.Current.LookupErr(keys...); err == nil {\n\t\t\tif uVal, err := cur.Current.LookupErr(\"unique\"); (unique && err == nil && uVal.Boolean() == true) ||\n\t\t\t\t(!unique && (err != nil || uVal.Boolean() == false)) {\n\n\t\t\t\tfoundIndex = true\n\t\t\t}\n\t\t}\n\t}\n\tassert.True(mt, foundIndex, \"index %v not found\", keys)\n}\n\nfunc skipRoundTripTest(mt *mtest.T) {\n\tif runtime.GOOS != \"darwin\" {\n\t\treturn\n\t}\n\n\tvar serverStatus bson.Raw\n\terr := mt.DB.RunCommand(\n\t\tcontext.Background(),\n\t\tbson.D{{\"serverStatus\", 1}},\n\t).Decode(&serverStatus)\n\trequire.NoError(mt, err, \"serverStatus error %v\", err)\n\n\t// can run on non-sharded clusters or on sharded cluster with auth/ssl disabled\n\t_, err = serverStatus.LookupErr(\"sharding\")\n\tif err != nil {\n\t\treturn\n\t}\n\t_, err = serverStatus.LookupErr(\"security\")\n\tif err != nil {\n\t\treturn\n\t}\n\tmt.Skip(\"skipping round trip test\")\n}\n"
  },
  {
    "path": "internal/integration/handshake_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2023-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage integration\n\nimport (\n\t\"context\"\n\t\"os\"\n\t\"runtime\"\n\t\"testing\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert/assertbson\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/handshake\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/integration/mtest\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/require\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n\t\"go.mongodb.org/mongo-driver/v2/version\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/wiremessage\"\n)\n\nfunc TestHandshakeProse(t *testing.T) {\n\tmt := mtest.New(t)\n\n\tif len(os.Getenv(\"DOCKER_RUNNING\")) > 0 {\n\t\tt.Skip(\"These tests gives different results when run in Docker due to extra environment data.\")\n\t}\n\n\topts := mtest.NewOptions().\n\t\tCreateCollection(false).\n\t\tClientType(mtest.Proxy)\n\n\tdriverInfo := &options.DriverInfo{\n\t\tName:     \"outer-library-name\",\n\t\tVersion:  \"outer-library-version\",\n\t\tPlatform: \"outer-library-platform\",\n\t}\n\n\t// Reset the environment variables to avoid environment namespace\n\t// collision.\n\tt.Setenv(\"AWS_EXECUTION_ENV\", \"\")\n\tt.Setenv(\"FUNCTIONS_WORKER_RUNTIME\", \"\")\n\tt.Setenv(\"K_SERVICE\", \"\")\n\tt.Setenv(\"VERCEL\", \"\")\n\tt.Setenv(\"AWS_REGION\", \"\")\n\tt.Setenv(\"AWS_LAMBDA_FUNCTION_MEMORY_SIZE\", \"\")\n\tt.Setenv(\"FUNCTION_MEMORY_MB\", \"\")\n\tt.Setenv(\"FUNCTION_TIMEOUT_SEC\", \"\")\n\tt.Setenv(\"FUNCTION_REGION\", \"\")\n\tt.Setenv(\"VERCEL_REGION\", \"\")\n\n\ttestCases := []struct {\n\t\tname string\n\t\tenv  map[string]string\n\t\topts *options.ClientOptions\n\t\twant []byte\n\t}{\n\t\t{\n\t\t\tname: \"1. valid AWS\",\n\t\t\tenv: map[string]string{\n\t\t\t\t\"AWS_EXECUTION_ENV\":               \"AWS_Lambda_java8\",\n\t\t\t\t\"AWS_REGION\":                      \"us-east-2\",\n\t\t\t\t\"AWS_LAMBDA_FUNCTION_MEMORY_SIZE\": \"1024\",\n\t\t\t},\n\t\t\topts: nil,\n\t\t\twant: mustMarshalBSON(bson.D{\n\t\t\t\t{Key: \"driver\", Value: bson.D{\n\t\t\t\t\t{Key: \"name\", Value: \"mongo-go-driver\"},\n\t\t\t\t\t{Key: \"version\", Value: version.Driver},\n\t\t\t\t}},\n\t\t\t\t{Key: \"os\", Value: bson.D{\n\t\t\t\t\t{Key: \"type\", Value: runtime.GOOS},\n\t\t\t\t\t{Key: \"architecture\", Value: runtime.GOARCH},\n\t\t\t\t}},\n\t\t\t\t{Key: \"platform\", Value: runtime.Version()},\n\t\t\t\t{Key: \"env\", Value: bson.D{\n\t\t\t\t\tbson.E{Key: \"name\", Value: \"aws.lambda\"},\n\t\t\t\t\tbson.E{Key: \"memory_mb\", Value: 1024},\n\t\t\t\t\tbson.E{Key: \"region\", Value: \"us-east-2\"},\n\t\t\t\t}},\n\t\t\t}),\n\t\t},\n\t\t{\n\t\t\tname: \"2. valid Azure\",\n\t\t\tenv: map[string]string{\n\t\t\t\t\"FUNCTIONS_WORKER_RUNTIME\": \"node\",\n\t\t\t},\n\t\t\topts: nil,\n\t\t\twant: mustMarshalBSON(bson.D{\n\t\t\t\t{Key: \"driver\", Value: bson.D{\n\t\t\t\t\t{Key: \"name\", Value: \"mongo-go-driver\"},\n\t\t\t\t\t{Key: \"version\", Value: version.Driver},\n\t\t\t\t}},\n\t\t\t\t{Key: \"os\", Value: bson.D{\n\t\t\t\t\t{Key: \"type\", Value: runtime.GOOS},\n\t\t\t\t\t{Key: \"architecture\", Value: runtime.GOARCH},\n\t\t\t\t}},\n\t\t\t\t{Key: \"platform\", Value: runtime.Version()},\n\t\t\t\t{Key: \"env\", Value: bson.D{\n\t\t\t\t\tbson.E{Key: \"name\", Value: \"azure.func\"},\n\t\t\t\t}},\n\t\t\t}),\n\t\t},\n\t\t{\n\t\t\tname: \"3. valid GCP\",\n\t\t\tenv: map[string]string{\n\t\t\t\t\"K_SERVICE\":            \"servicename\",\n\t\t\t\t\"FUNCTION_MEMORY_MB\":   \"1024\",\n\t\t\t\t\"FUNCTION_TIMEOUT_SEC\": \"60\",\n\t\t\t\t\"FUNCTION_REGION\":      \"us-central1\",\n\t\t\t},\n\t\t\topts: nil,\n\t\t\twant: mustMarshalBSON(bson.D{\n\t\t\t\t{Key: \"driver\", Value: bson.D{\n\t\t\t\t\t{Key: \"name\", Value: \"mongo-go-driver\"},\n\t\t\t\t\t{Key: \"version\", Value: version.Driver},\n\t\t\t\t}},\n\t\t\t\t{Key: \"os\", Value: bson.D{\n\t\t\t\t\t{Key: \"type\", Value: runtime.GOOS},\n\t\t\t\t\t{Key: \"architecture\", Value: runtime.GOARCH},\n\t\t\t\t}},\n\t\t\t\t{Key: \"platform\", Value: runtime.Version()},\n\t\t\t\t{Key: \"env\", Value: bson.D{\n\t\t\t\t\tbson.E{Key: \"name\", Value: \"gcp.func\"},\n\t\t\t\t\tbson.E{Key: \"memory_mb\", Value: 1024},\n\t\t\t\t\tbson.E{Key: \"region\", Value: \"us-central1\"},\n\t\t\t\t\tbson.E{Key: \"timeout_sec\", Value: int32(60)},\n\t\t\t\t}},\n\t\t\t}),\n\t\t},\n\t\t{\n\t\t\tname: \"4. valid Vercel\",\n\t\t\tenv: map[string]string{\n\t\t\t\t\"VERCEL\":        \"1\",\n\t\t\t\t\"VERCEL_REGION\": \"cdg1\",\n\t\t\t},\n\t\t\topts: nil,\n\t\t\twant: mustMarshalBSON(bson.D{\n\t\t\t\t{Key: \"driver\", Value: bson.D{\n\t\t\t\t\t{Key: \"name\", Value: \"mongo-go-driver\"},\n\t\t\t\t\t{Key: \"version\", Value: version.Driver},\n\t\t\t\t}},\n\t\t\t\t{Key: \"os\", Value: bson.D{\n\t\t\t\t\t{Key: \"type\", Value: runtime.GOOS},\n\t\t\t\t\t{Key: \"architecture\", Value: runtime.GOARCH},\n\t\t\t\t}},\n\t\t\t\t{Key: \"platform\", Value: runtime.Version()},\n\t\t\t\t{Key: \"env\", Value: bson.D{\n\t\t\t\t\tbson.E{Key: \"name\", Value: \"vercel\"},\n\t\t\t\t\tbson.E{Key: \"region\", Value: \"cdg1\"},\n\t\t\t\t}},\n\t\t\t}),\n\t\t},\n\t\t{\n\t\t\tname: \"5. invalid multiple providers\",\n\t\t\tenv: map[string]string{\n\t\t\t\t\"AWS_EXECUTION_ENV\":        \"AWS_Lambda_java8\",\n\t\t\t\t\"FUNCTIONS_WORKER_RUNTIME\": \"node\",\n\t\t\t},\n\t\t\topts: nil,\n\t\t\twant: mustMarshalBSON(bson.D{\n\t\t\t\t{Key: \"driver\", Value: bson.D{\n\t\t\t\t\t{Key: \"name\", Value: \"mongo-go-driver\"},\n\t\t\t\t\t{Key: \"version\", Value: version.Driver},\n\t\t\t\t}},\n\t\t\t\t{Key: \"os\", Value: bson.D{\n\t\t\t\t\t{Key: \"type\", Value: runtime.GOOS},\n\t\t\t\t\t{Key: \"architecture\", Value: runtime.GOARCH},\n\t\t\t\t}},\n\t\t\t\t{Key: \"platform\", Value: runtime.Version()},\n\t\t\t}),\n\t\t},\n\t\t{\n\t\t\tname: \"6. invalid long string\",\n\t\t\tenv: map[string]string{\n\t\t\t\t\"AWS_EXECUTION_ENV\": \"AWS_Lambda_java8\",\n\t\t\t\t\"AWS_REGION\": func() string {\n\t\t\t\t\tvar s string\n\t\t\t\t\tfor i := 0; i < 512; i++ {\n\t\t\t\t\t\ts += \"a\"\n\t\t\t\t\t}\n\t\t\t\t\treturn s\n\t\t\t\t}(),\n\t\t\t},\n\t\t\topts: nil,\n\t\t\twant: mustMarshalBSON(bson.D{\n\t\t\t\t{Key: \"driver\", Value: bson.D{\n\t\t\t\t\t{Key: \"name\", Value: \"mongo-go-driver\"},\n\t\t\t\t\t{Key: \"version\", Value: version.Driver},\n\t\t\t\t}},\n\t\t\t\t{Key: \"os\", Value: bson.D{\n\t\t\t\t\t{Key: \"type\", Value: runtime.GOOS},\n\t\t\t\t\t{Key: \"architecture\", Value: runtime.GOARCH},\n\t\t\t\t}},\n\t\t\t\t{Key: \"platform\", Value: runtime.Version()},\n\t\t\t\t{Key: \"env\", Value: bson.D{\n\t\t\t\t\t{Key: \"name\", Value: \"aws.lambda\"},\n\t\t\t\t}},\n\t\t\t}),\n\t\t},\n\t\t{\n\t\t\tname: \"7. invalid wrong types\",\n\t\t\tenv: map[string]string{\n\t\t\t\t\"AWS_EXECUTION_ENV\":               \"AWS_Lambda_java8\",\n\t\t\t\t\"AWS_LAMBDA_FUNCTION_MEMORY_SIZE\": \"big\",\n\t\t\t},\n\t\t\topts: nil,\n\t\t\twant: mustMarshalBSON(bson.D{\n\t\t\t\t{Key: \"driver\", Value: bson.D{\n\t\t\t\t\t{Key: \"name\", Value: \"mongo-go-driver\"},\n\t\t\t\t\t{Key: \"version\", Value: version.Driver},\n\t\t\t\t}},\n\t\t\t\t{Key: \"os\", Value: bson.D{\n\t\t\t\t\t{Key: \"type\", Value: runtime.GOOS},\n\t\t\t\t\t{Key: \"architecture\", Value: runtime.GOARCH},\n\t\t\t\t}},\n\t\t\t\t{Key: \"platform\", Value: runtime.Version()},\n\t\t\t\t{Key: \"env\", Value: bson.D{\n\t\t\t\t\t{Key: \"name\", Value: \"aws.lambda\"},\n\t\t\t\t}},\n\t\t\t}),\n\t\t},\n\t\t{\n\t\t\tname: \"8. Invalid - AWS_EXECUTION_ENV does not start with \\\"AWS_Lambda_\\\"\",\n\t\t\tenv: map[string]string{\n\t\t\t\t\"AWS_EXECUTION_ENV\": \"EC2\",\n\t\t\t},\n\t\t\topts: nil,\n\t\t\twant: mustMarshalBSON(bson.D{\n\t\t\t\t{Key: \"driver\", Value: bson.D{\n\t\t\t\t\t{Key: \"name\", Value: \"mongo-go-driver\"},\n\t\t\t\t\t{Key: \"version\", Value: version.Driver},\n\t\t\t\t}},\n\t\t\t\t{Key: \"os\", Value: bson.D{\n\t\t\t\t\t{Key: \"type\", Value: runtime.GOOS},\n\t\t\t\t\t{Key: \"architecture\", Value: runtime.GOARCH},\n\t\t\t\t}},\n\t\t\t\t{Key: \"platform\", Value: runtime.Version()},\n\t\t\t}),\n\t\t},\n\t\t{\n\t\t\tname: \"driver info included\",\n\t\t\topts: options.Client().SetDriverInfo(driverInfo),\n\t\t\twant: mustMarshalBSON(bson.D{\n\t\t\t\t{Key: \"driver\", Value: bson.D{\n\t\t\t\t\t{Key: \"name\", Value: \"mongo-go-driver|outer-library-name\"},\n\t\t\t\t\t{Key: \"version\", Value: version.Driver + \"|outer-library-version\"},\n\t\t\t\t}},\n\t\t\t\t{Key: \"os\", Value: bson.D{\n\t\t\t\t\t{Key: \"type\", Value: runtime.GOOS},\n\t\t\t\t\t{Key: \"architecture\", Value: runtime.GOARCH},\n\t\t\t\t}},\n\t\t\t\t{Key: \"platform\", Value: runtime.Version() + \"|outer-library-platform\"},\n\t\t\t}),\n\t\t},\n\t}\n\n\tfor _, tc := range testCases {\n\t\ttc := tc // Avoid implicit memory aliasing in for loop.\n\n\t\tmt.RunOpts(tc.name, opts, func(mt *mtest.T) {\n\t\t\tfor k, v := range tc.env {\n\t\t\t\tmt.Setenv(k, v)\n\t\t\t}\n\n\t\t\tif tc.opts != nil {\n\t\t\t\tmt.ResetClient(tc.opts)\n\t\t\t}\n\n\t\t\t// Ping the server to ensure the handshake has completed.\n\t\t\terr := mt.Client.Ping(context.Background(), nil)\n\t\t\trequire.NoError(mt, err, \"Ping error: %v\", err)\n\n\t\t\tfirstMessage := mt.GetProxyCapture().TryNext()\n\t\t\trequire.NotNil(mt, firstMessage, \"expected to capture a proxied message\")\n\n\t\t\tassert.True(mt, firstMessage.IsHandshake(), \"expected first message to be a handshake\")\n\n\t\t\tclientMetadata := clientMetadataFromHandshake(mt, firstMessage.Sent.Command)\n\t\t\tassertbson.EqualDocument(mt, tc.want, clientMetadata)\n\t\t})\n\t}\n}\n\nfunc TestLoadBalancedConnectionHandshake(t *testing.T) {\n\tmt := mtest.New(t)\n\n\tlbopts := mtest.NewOptions().ClientType(mtest.Proxy).Topologies(\n\t\tmtest.LoadBalanced)\n\n\tmt.RunOpts(\"LB connection handshake uses OP_MSG\", lbopts, func(mt *mtest.T) {\n\t\t// Ping the server to ensure the handshake has completed.\n\t\terr := mt.Client.Ping(context.Background(), nil)\n\t\trequire.NoError(mt, err, \"Ping error: %v\", err)\n\n\t\tfirstMessage := mt.GetProxyCapture().TryNext()\n\t\trequire.NotNil(mt, firstMessage, \"expected to capture a proxied message\")\n\n\t\t// Per the specifications, if loadBalanced=true, drivers MUST use the hello\n\t\t// command for the initial handshake and use the OP_MSG protocol.\n\t\tassert.Equal(mt, \"hello\", firstMessage.CommandName)\n\t\tassert.Equal(mt, wiremessage.OpMsg, firstMessage.Sent.OpCode)\n\t})\n\n\topts := mtest.NewOptions().ClientType(mtest.Proxy).Topologies(\n\t\tmtest.ReplicaSet,\n\t\tmtest.Sharded,\n\t\tmtest.Single,\n\t\tmtest.ShardedReplicaSet)\n\n\tmt.RunOpts(\"non-LB connection handshake uses OP_QUERY\", opts, func(mt *mtest.T) {\n\t\t// Ping the server to ensure the handshake has completed.\n\t\terr := mt.Client.Ping(context.Background(), nil)\n\t\trequire.NoError(mt, err, \"Ping error: %v\", err)\n\n\t\tfirstMessage := mt.GetProxyCapture().TryNext()\n\t\trequire.NotNil(mt, firstMessage, \"expected to capture a proxied message\")\n\n\t\twant := wiremessage.OpQuery\n\n\t\thello := handshake.LegacyHello\n\t\tif os.Getenv(\"REQUIRE_API_VERSION\") == \"true\" {\n\t\t\thello = \"hello\"\n\n\t\t\t// If the server API version is requested, then we should use OP_MSG\n\t\t\t// regardless of the topology\n\t\t\twant = wiremessage.OpMsg\n\t\t}\n\n\t\tassert.Equal(mt, hello, firstMessage.CommandName, \"expected first message to be a handshake\")\n\t\tassert.Equal(mt, want, firstMessage.Sent.OpCode)\n\t})\n}\n\n// Test 1: Test that the driver updates metadata\n// Test 2: Multiple Successive Metadata Updates\n// Test 3: Multiple Successive Metadata Updates with Duplicate Data\nfunc TestHandshakeProse_AppendMetadata_Test1_Test2_Test3(t *testing.T) {\n\tmt := mtest.New(t)\n\n\tinitialDriverInfo := options.DriverInfo{\n\t\tName:     \"library\",\n\t\tVersion:  \"1.2\",\n\t\tPlatform: \"Library Platform\",\n\t}\n\n\ttestCases := []struct {\n\t\tname       string\n\t\tdriverInfo options.DriverInfo\n\t\twant       []byte\n\n\t\t// append initialDriverInfo using client.AppendDriverInfo instead of as a\n\t\t// client-level constructor.\n\t\tappend bool\n\t}{\n\t\t{\n\t\t\tname: \"test1.1: append new driver info\",\n\t\t\tdriverInfo: options.DriverInfo{\n\t\t\t\tName:     \"framework\",\n\t\t\t\tVersion:  \"2.0\",\n\t\t\t\tPlatform: \"Framework Platform\",\n\t\t\t},\n\t\t\twant: mustMarshalBSON(bson.D{\n\t\t\t\t{Key: \"driver\", Value: bson.D{\n\t\t\t\t\t{Key: \"name\", Value: \"mongo-go-driver|library|framework\"},\n\t\t\t\t\t{Key: \"version\", Value: version.Driver + \"|1.2|2.0\"},\n\t\t\t\t}},\n\t\t\t\t{Key: \"os\", Value: bson.D{\n\t\t\t\t\t{Key: \"type\", Value: runtime.GOOS},\n\t\t\t\t\t{Key: \"architecture\", Value: runtime.GOARCH},\n\t\t\t\t}},\n\t\t\t\t{Key: \"platform\", Value: runtime.Version() + \"|Library Platform|Framework Platform\"},\n\t\t\t}),\n\t\t\tappend: false,\n\t\t},\n\t\t{\n\t\t\tname: \"test1.2: append with no platform\",\n\t\t\tdriverInfo: options.DriverInfo{\n\t\t\t\tName:     \"framework\",\n\t\t\t\tVersion:  \"2.0\",\n\t\t\t\tPlatform: \"\",\n\t\t\t},\n\t\t\twant: mustMarshalBSON(bson.D{\n\t\t\t\t{Key: \"driver\", Value: bson.D{\n\t\t\t\t\t{Key: \"name\", Value: \"mongo-go-driver|library|framework\"},\n\t\t\t\t\t{Key: \"version\", Value: version.Driver + \"|1.2|2.0\"},\n\t\t\t\t}},\n\t\t\t\t{Key: \"os\", Value: bson.D{\n\t\t\t\t\t{Key: \"type\", Value: runtime.GOOS},\n\t\t\t\t\t{Key: \"architecture\", Value: runtime.GOARCH},\n\t\t\t\t}},\n\t\t\t\t{Key: \"platform\", Value: runtime.Version() + \"|Library Platform\"},\n\t\t\t}),\n\t\t\tappend: false,\n\t\t},\n\t\t{\n\t\t\tname: \"test1.3: append with no version\",\n\t\t\tdriverInfo: options.DriverInfo{\n\t\t\t\tName:     \"framework\",\n\t\t\t\tVersion:  \"\",\n\t\t\t\tPlatform: \"Framework Platform\",\n\t\t\t},\n\t\t\twant: mustMarshalBSON(bson.D{\n\t\t\t\t{Key: \"driver\", Value: bson.D{\n\t\t\t\t\t{Key: \"name\", Value: \"mongo-go-driver|library|framework\"},\n\t\t\t\t\t{Key: \"version\", Value: version.Driver + \"|1.2\"},\n\t\t\t\t}},\n\t\t\t\t{Key: \"os\", Value: bson.D{\n\t\t\t\t\t{Key: \"type\", Value: runtime.GOOS},\n\t\t\t\t\t{Key: \"architecture\", Value: runtime.GOARCH},\n\t\t\t\t}},\n\t\t\t\t{Key: \"platform\", Value: runtime.Version() + \"|Library Platform|Framework Platform\"},\n\t\t\t}),\n\t\t\tappend: false,\n\t\t},\n\t\t{\n\t\t\tname: \"test1.4: append with name only\",\n\t\t\tdriverInfo: options.DriverInfo{\n\t\t\t\tName:     \"framework\",\n\t\t\t\tVersion:  \"\",\n\t\t\t\tPlatform: \"\",\n\t\t\t},\n\t\t\twant: mustMarshalBSON(bson.D{\n\t\t\t\t{Key: \"driver\", Value: bson.D{\n\t\t\t\t\t{Key: \"name\", Value: \"mongo-go-driver|library|framework\"},\n\t\t\t\t\t{Key: \"version\", Value: version.Driver + \"|1.2\"},\n\t\t\t\t}},\n\t\t\t\t{Key: \"os\", Value: bson.D{\n\t\t\t\t\t{Key: \"type\", Value: runtime.GOOS},\n\t\t\t\t\t{Key: \"architecture\", Value: runtime.GOARCH},\n\t\t\t\t}},\n\t\t\t\t{Key: \"platform\", Value: runtime.Version() + \"|Library Platform\"},\n\t\t\t}),\n\t\t\tappend: false,\n\t\t},\n\t\t{\n\t\t\tname: \"test2.1: append new driver info after appending\",\n\t\t\tdriverInfo: options.DriverInfo{\n\t\t\t\tName:     \"framework\",\n\t\t\t\tVersion:  \"2.0\",\n\t\t\t\tPlatform: \"Framework Platform\",\n\t\t\t},\n\t\t\twant: mustMarshalBSON(bson.D{\n\t\t\t\t{Key: \"driver\", Value: bson.D{\n\t\t\t\t\t{Key: \"name\", Value: \"mongo-go-driver|library|framework\"},\n\t\t\t\t\t{Key: \"version\", Value: version.Driver + \"|1.2|2.0\"},\n\t\t\t\t}},\n\t\t\t\t{Key: \"os\", Value: bson.D{\n\t\t\t\t\t{Key: \"type\", Value: runtime.GOOS},\n\t\t\t\t\t{Key: \"architecture\", Value: runtime.GOARCH},\n\t\t\t\t}},\n\t\t\t\t{Key: \"platform\", Value: runtime.Version() + \"|Library Platform|Framework Platform\"},\n\t\t\t}),\n\t\t\tappend: true,\n\t\t},\n\t\t{\n\t\t\tname: \"test2.2: append with no platform after appending\",\n\t\t\tdriverInfo: options.DriverInfo{\n\t\t\t\tName:     \"framework\",\n\t\t\t\tVersion:  \"2.0\",\n\t\t\t\tPlatform: \"\",\n\t\t\t},\n\t\t\twant: mustMarshalBSON(bson.D{\n\t\t\t\t{Key: \"driver\", Value: bson.D{\n\t\t\t\t\t{Key: \"name\", Value: \"mongo-go-driver|library|framework\"},\n\t\t\t\t\t{Key: \"version\", Value: version.Driver + \"|1.2|2.0\"},\n\t\t\t\t}},\n\t\t\t\t{Key: \"os\", Value: bson.D{\n\t\t\t\t\t{Key: \"type\", Value: runtime.GOOS},\n\t\t\t\t\t{Key: \"architecture\", Value: runtime.GOARCH},\n\t\t\t\t}},\n\t\t\t\t{Key: \"platform\", Value: runtime.Version() + \"|Library Platform\"},\n\t\t\t}),\n\t\t\tappend: true,\n\t\t},\n\t\t{\n\t\t\tname: \"test2.3: append with no version after appending\",\n\t\t\tdriverInfo: options.DriverInfo{\n\t\t\t\tName:     \"framework\",\n\t\t\t\tVersion:  \"\",\n\t\t\t\tPlatform: \"Framework Platform\",\n\t\t\t},\n\t\t\twant: mustMarshalBSON(bson.D{\n\t\t\t\t{Key: \"driver\", Value: bson.D{\n\t\t\t\t\t{Key: \"name\", Value: \"mongo-go-driver|library|framework\"},\n\t\t\t\t\t{Key: \"version\", Value: version.Driver + \"|1.2\"},\n\t\t\t\t}},\n\t\t\t\t{Key: \"os\", Value: bson.D{\n\t\t\t\t\t{Key: \"type\", Value: runtime.GOOS},\n\t\t\t\t\t{Key: \"architecture\", Value: runtime.GOARCH},\n\t\t\t\t}},\n\t\t\t\t{Key: \"platform\", Value: runtime.Version() + \"|Library Platform|Framework Platform\"},\n\t\t\t}),\n\t\t\tappend: true,\n\t\t},\n\t\t{\n\t\t\tname: \"test2.4: append with name only after appending\",\n\t\t\tdriverInfo: options.DriverInfo{\n\t\t\t\tName:     \"framework\",\n\t\t\t\tVersion:  \"\",\n\t\t\t\tPlatform: \"\",\n\t\t\t},\n\t\t\twant: mustMarshalBSON(bson.D{\n\t\t\t\t{Key: \"driver\", Value: bson.D{\n\t\t\t\t\t{Key: \"name\", Value: \"mongo-go-driver|library|framework\"},\n\t\t\t\t\t{Key: \"version\", Value: version.Driver + \"|1.2\"},\n\t\t\t\t}},\n\t\t\t\t{Key: \"os\", Value: bson.D{\n\t\t\t\t\t{Key: \"type\", Value: runtime.GOOS},\n\t\t\t\t\t{Key: \"architecture\", Value: runtime.GOARCH},\n\t\t\t\t}},\n\t\t\t\t{Key: \"platform\", Value: runtime.Version() + \"|Library Platform\"},\n\t\t\t}),\n\t\t\tappend: true,\n\t\t},\n\t\t{\n\t\t\tname: \"test3.1: same driver info after appending\",\n\t\t\tdriverInfo: options.DriverInfo{\n\t\t\t\tName:     \"library\",\n\t\t\t\tVersion:  \"1.2\",\n\t\t\t\tPlatform: \"Library Platform\",\n\t\t\t},\n\t\t\twant: mustMarshalBSON(bson.D{\n\t\t\t\t{Key: \"driver\", Value: bson.D{\n\t\t\t\t\t{Key: \"name\", Value: \"mongo-go-driver|library\"},\n\t\t\t\t\t{Key: \"version\", Value: version.Driver + \"|1.2\"},\n\t\t\t\t}},\n\t\t\t\t{Key: \"os\", Value: bson.D{\n\t\t\t\t\t{Key: \"type\", Value: runtime.GOOS},\n\t\t\t\t\t{Key: \"architecture\", Value: runtime.GOARCH},\n\t\t\t\t}},\n\t\t\t\t{Key: \"platform\", Value: runtime.Version() + \"|Library Platform\"},\n\t\t\t}),\n\t\t\tappend: true,\n\t\t},\n\t\t{\n\t\t\tname: \"test3.2: same version and platform after appending\",\n\t\t\tdriverInfo: options.DriverInfo{\n\t\t\t\tName:     \"framework\",\n\t\t\t\tVersion:  \"1.2\",\n\t\t\t\tPlatform: \"Library Platform\",\n\t\t\t},\n\t\t\twant: mustMarshalBSON(bson.D{\n\t\t\t\t{Key: \"driver\", Value: bson.D{\n\t\t\t\t\t{Key: \"name\", Value: \"mongo-go-driver|library|framework\"},\n\t\t\t\t\t{Key: \"version\", Value: version.Driver + \"|1.2\"},\n\t\t\t\t}},\n\t\t\t\t{Key: \"os\", Value: bson.D{\n\t\t\t\t\t{Key: \"type\", Value: runtime.GOOS},\n\t\t\t\t\t{Key: \"architecture\", Value: runtime.GOARCH},\n\t\t\t\t}},\n\t\t\t\t{Key: \"platform\", Value: runtime.Version() + \"|Library Platform\"},\n\t\t\t}),\n\t\t\tappend: true,\n\t\t},\n\t\t{\n\t\t\tname: \"test3.3: same name and platform after appending\",\n\t\t\tdriverInfo: options.DriverInfo{\n\t\t\t\tName:     \"library\",\n\t\t\t\tVersion:  \"2.0\",\n\t\t\t\tPlatform: \"Library Platform\",\n\t\t\t},\n\t\t\twant: mustMarshalBSON(bson.D{\n\t\t\t\t{Key: \"driver\", Value: bson.D{\n\t\t\t\t\t{Key: \"name\", Value: \"mongo-go-driver|library\"},\n\t\t\t\t\t{Key: \"version\", Value: version.Driver + \"|1.2|2.0\"},\n\t\t\t\t}},\n\t\t\t\t{Key: \"os\", Value: bson.D{\n\t\t\t\t\t{Key: \"type\", Value: runtime.GOOS},\n\t\t\t\t\t{Key: \"architecture\", Value: runtime.GOARCH},\n\t\t\t\t}},\n\t\t\t\t{Key: \"platform\", Value: runtime.Version() + \"|Library Platform\"},\n\t\t\t}),\n\t\t\tappend: true,\n\t\t},\n\t\t{\n\t\t\tname: \"test3.4: same name and version after appending\",\n\t\t\tdriverInfo: options.DriverInfo{\n\t\t\t\tName:     \"library\",\n\t\t\t\tVersion:  \"1.2\",\n\t\t\t\tPlatform: \"Framework Platform\",\n\t\t\t},\n\t\t\twant: mustMarshalBSON(bson.D{\n\t\t\t\t{Key: \"driver\", Value: bson.D{\n\t\t\t\t\t{Key: \"name\", Value: \"mongo-go-driver|library\"},\n\t\t\t\t\t{Key: \"version\", Value: version.Driver + \"|1.2\"},\n\t\t\t\t}},\n\t\t\t\t{Key: \"os\", Value: bson.D{\n\t\t\t\t\t{Key: \"type\", Value: runtime.GOOS},\n\t\t\t\t\t{Key: \"architecture\", Value: runtime.GOARCH},\n\t\t\t\t}},\n\t\t\t\t{Key: \"platform\", Value: runtime.Version() + \"|Library Platform|Framework Platform\"},\n\t\t\t}),\n\t\t\tappend: true,\n\t\t},\n\t\t{\n\t\t\tname: \"test3.5: same platform after appending\",\n\t\t\tdriverInfo: options.DriverInfo{\n\t\t\t\tName:     \"framework\",\n\t\t\t\tVersion:  \"2.0\",\n\t\t\t\tPlatform: \"Library Platform\",\n\t\t\t},\n\t\t\twant: mustMarshalBSON(bson.D{\n\t\t\t\t{Key: \"driver\", Value: bson.D{\n\t\t\t\t\t{Key: \"name\", Value: \"mongo-go-driver|library|framework\"},\n\t\t\t\t\t{Key: \"version\", Value: version.Driver + \"|1.2|2.0\"},\n\t\t\t\t}},\n\t\t\t\t{Key: \"os\", Value: bson.D{\n\t\t\t\t\t{Key: \"type\", Value: runtime.GOOS},\n\t\t\t\t\t{Key: \"architecture\", Value: runtime.GOARCH},\n\t\t\t\t}},\n\t\t\t\t{Key: \"platform\", Value: runtime.Version() + \"|Library Platform\"},\n\t\t\t}),\n\t\t\tappend: true,\n\t\t},\n\t\t{\n\t\t\tname: \"test3.6: same version after appending\",\n\t\t\tdriverInfo: options.DriverInfo{\n\t\t\t\tName:     \"framework\",\n\t\t\t\tVersion:  \"1.2\",\n\t\t\t\tPlatform: \"Framework Platform\",\n\t\t\t},\n\t\t\twant: mustMarshalBSON(bson.D{\n\t\t\t\t{Key: \"driver\", Value: bson.D{\n\t\t\t\t\t{Key: \"name\", Value: \"mongo-go-driver|library|framework\"},\n\t\t\t\t\t{Key: \"version\", Value: version.Driver + \"|1.2\"},\n\t\t\t\t}},\n\t\t\t\t{Key: \"os\", Value: bson.D{\n\t\t\t\t\t{Key: \"type\", Value: runtime.GOOS},\n\t\t\t\t\t{Key: \"architecture\", Value: runtime.GOARCH},\n\t\t\t\t}},\n\t\t\t\t{Key: \"platform\", Value: runtime.Version() + \"|Library Platform|Framework Platform\"},\n\t\t\t}),\n\t\t\tappend: true,\n\t\t},\n\t\t{\n\t\t\tname: \"test3.7: same name after appending\",\n\t\t\tdriverInfo: options.DriverInfo{\n\t\t\t\tName:     \"library\",\n\t\t\t\tVersion:  \"2.0\",\n\t\t\t\tPlatform: \"Framework Platform\",\n\t\t\t},\n\t\t\twant: mustMarshalBSON(bson.D{\n\t\t\t\t{Key: \"driver\", Value: bson.D{\n\t\t\t\t\t{Key: \"name\", Value: \"mongo-go-driver|library\"},\n\t\t\t\t\t{Key: \"version\", Value: version.Driver + \"|1.2|2.0\"},\n\t\t\t\t}},\n\t\t\t\t{Key: \"os\", Value: bson.D{\n\t\t\t\t\t{Key: \"type\", Value: runtime.GOOS},\n\t\t\t\t\t{Key: \"architecture\", Value: runtime.GOARCH},\n\t\t\t\t}},\n\t\t\t\t{Key: \"platform\", Value: runtime.Version() + \"|Library Platform|Framework Platform\"},\n\t\t\t}),\n\t\t\tappend: true,\n\t\t},\n\t}\n\n\tfor _, tc := range testCases {\n\t\ttc := tc // Avoid implicit memory aliasing in for loop.\n\n\t\t// Create a top-level client that can be shared among sub-tests. This is\n\t\t// necessary to test appending driver info to an existing client.\n\t\topts := mtest.NewOptions().CreateClient(false).ClientType(mtest.Proxy)\n\n\t\tmt.RunOpts(tc.name, opts, func(mt *mtest.T) {\n\t\t\tclientOpts := options.Client().\n\t\t\t\t// Set idle timeout to 1ms to force new connections to be created\n\t\t\t\t// throughout the lifetime of the test.\n\t\t\t\tSetMaxConnIdleTime(1 * time.Millisecond)\n\n\t\t\tif !tc.append {\n\t\t\t\tclientOpts = clientOpts.SetDriverInfo(&initialDriverInfo)\n\t\t\t}\n\n\t\t\tmt.ResetClient(clientOpts)\n\n\t\t\tif tc.append {\n\t\t\t\tmt.Client.AppendDriverInfo(initialDriverInfo)\n\t\t\t}\n\n\t\t\t// Send a ping command to the server and verify that the command succeeded.\n\t\t\terr := mt.Client.Ping(context.Background(), nil)\n\t\t\trequire.NoError(mt, err, \"Ping error: %v\", err)\n\n\t\t\t// Save intercepted `client` document as `initialClientMetadata`.\n\t\t\tinitialClientMetadata := mt.GetProxyCapture().TryNext()\n\n\t\t\trequire.NotNil(mt, initialClientMetadata, \"expected to capture a proxied message\")\n\t\t\tassert.True(mt, initialClientMetadata.IsHandshake(), \"expected first message to be a handshake\")\n\n\t\t\t// Wait 5ms for the connection to become idle.\n\t\t\ttime.Sleep(5 * time.Millisecond)\n\n\t\t\tmt.Client.AppendDriverInfo(tc.driverInfo)\n\n\t\t\t// Drain the proxy\n\t\t\tmt.GetProxyCapture().Drain()\n\n\t\t\t// Send a ping command to the server and verify that the command succeeded.\n\t\t\terr = mt.Client.Ping(context.Background(), nil)\n\t\t\trequire.NoError(mt, err, \"Ping error: %v\", err)\n\n\t\t\t// Capture the first message sent after appending driver info.\n\t\t\tgotMessage := mt.GetProxyCapture().TryNext()\n\t\t\trequire.NotNil(mt, gotMessage, \"expected to capture a proxied message\")\n\t\t\tassert.True(mt, gotMessage.IsHandshake(), \"expected first message to be a handshake\")\n\n\t\t\tclientMetadata := clientMetadataFromHandshake(mt, gotMessage.Sent.Command)\n\t\t\tassertbson.EqualDocument(mt, tc.want, clientMetadata)\n\t\t})\n\t}\n}\n\n// Test 4: Multiple Metadata Updates with Duplicate Data.\nfunc TestHandshakeProse_AppendMetadata_MultipleUpdatesWithDuplicateFields(t *testing.T) {\n\topts := mtest.NewOptions().ClientType(mtest.Proxy)\n\tmt := mtest.New(t, opts)\n\n\tclientOpts := options.Client().\n\t\t// Set idle timeout to 1ms to force new connections to be created\n\t\t// throughout the lifetime of the test.\n\t\tSetMaxConnIdleTime(1 * time.Millisecond)\n\n\t// 1. Create a top-level client that can be shared among sub-tests. This is\n\t// necessary to test appending driver info to an existing client.\n\tmt.ResetClient(clientOpts)\n\n\toriginalDriverInfo := options.DriverInfo{\n\t\tName:     \"library\",\n\t\tVersion:  \"1.2\",\n\t\tPlatform: \"Library Platform\",\n\t}\n\n\t// 2. Append initial driver info using client.AppendDriverInfo.\n\tmt.Client.AppendDriverInfo(originalDriverInfo)\n\n\t// 3. Send a ping command to the server and verify that the command succeeded.\n\terr := mt.Client.Ping(context.Background(), nil)\n\trequire.NoError(mt, err, \"Ping error: %v\", err)\n\n\t// 4. Wait 5ms for the connection to become idle.\n\ttime.Sleep(5 * time.Millisecond)\n\n\t// 5. Append new driver info.\n\tmt.Client.AppendDriverInfo(options.DriverInfo{\n\t\tName:     \"framework\",\n\t\tVersion:  \"2.0\",\n\t\tPlatform: \"Framework Platform\",\n\t})\n\n\t// Drain the proxy to ensure we only capture messages after appending.\n\tmt.GetProxyCapture().Drain()\n\n\t// 6. Send a ping command to the server and verify that the command succeeded.\n\terr = mt.Client.Ping(context.Background(), nil)\n\trequire.NoError(mt, err, \"Ping error: %v\", err)\n\n\t// 7. Save intercepted `client` document as `clientMetadata`.\n\t//\n\t// NOTE: The Go Driver statically defineds the expected client\n\t// metadata value to make the tests more reliable and prevent\n\t// false-positive assertion results. That deviates from the prose\n\t// test.\n\twant := mustMarshalBSON(bson.D{\n\t\t{Key: \"driver\", Value: bson.D{\n\t\t\t{Key: \"name\", Value: \"mongo-go-driver|library|framework\"},\n\t\t\t{Key: \"version\", Value: version.Driver + \"|1.2|2.0\"},\n\t\t}},\n\t\t{Key: \"os\", Value: bson.D{\n\t\t\t{Key: \"type\", Value: runtime.GOOS},\n\t\t\t{Key: \"architecture\", Value: runtime.GOARCH},\n\t\t}},\n\t\t{Key: \"platform\", Value: runtime.Version() + \"|Library Platform|Framework Platform\"},\n\t})\n\n\t// 8. Wait 5ms for the connection to become idle.\n\ttime.Sleep(5 * time.Millisecond)\n\n\t// Drain the proxy to ensure we only capture messages after appending.\n\tmt.GetProxyCapture().Drain()\n\n\t// 9. Append the original driver info again.\n\tmt.Client.AppendDriverInfo(originalDriverInfo)\n\n\t// 10. Send a ping command to the server and verify that the command succeeded.\n\terr = mt.Client.Ping(context.Background(), nil)\n\trequire.NoError(mt, err, \"Ping error: %v\", err)\n\n\t// 11. Save intercepted `client` document as `clientMetadata`.\n\tupdatedClientMetadata := mt.GetProxyCapture().TryNext()\n\n\trequire.NotNil(mt, updatedClientMetadata, \"expected to capture a proxied message\")\n\tassert.True(mt, updatedClientMetadata.IsHandshake(), \"expected first message to be a handshake\")\n\n\tclientMetadata := clientMetadataFromHandshake(mt, updatedClientMetadata.Sent.Command)\n\tassertbson.EqualDocument(mt, want, clientMetadata)\n}\n\n// Test 5: Metadata is not appended if identical to initial metadata\nfunc TestHandshakeProse_AppendMetadata_NotAppendedIfIdentical(t *testing.T) {\n\topts := mtest.NewOptions().ClientType(mtest.Proxy)\n\tmt := mtest.New(t, opts)\n\n\toriginalDriverInfo := options.DriverInfo{\n\t\tName:     \"library\",\n\t\tVersion:  \"1.2\",\n\t\tPlatform: \"Library Platform\",\n\t}\n\n\tclientOpts := options.Client().\n\t\t// Set idle timeout to 1ms to force new connections to be created\n\t\t// throughout the lifetime of the test.\n\t\tSetMaxConnIdleTime(1 * time.Millisecond).\n\t\tSetDriverInfo(&originalDriverInfo)\n\n\t// 1. Create a top-level client that can be shared among sub-tests. This is\n\t// necessary to test appending driver info to an existing client.\n\tmt.ResetClient(clientOpts)\n\n\t// Drain the proxy to ensure we only capture messages after appending.\n\tmt.GetProxyCapture().Drain()\n\n\t// 2. Send a ping command to the server and verify that the command succeeded.\n\terr := mt.Client.Ping(context.Background(), nil)\n\trequire.NoError(mt, err, \"Ping error: %v\", err)\n\n\t// NOTE: The Go Driver statically defineds the expected client\n\t// metadata value to make the tests more reliable and prevent\n\t// false-positive assertion results. That deviates from the prose\n\t// test.\n\twant := mustMarshalBSON(bson.D{\n\t\t{Key: \"driver\", Value: bson.D{\n\t\t\t{Key: \"name\", Value: \"mongo-go-driver|library\"},\n\t\t\t{Key: \"version\", Value: version.Driver + \"|1.2\"},\n\t\t}},\n\t\t{Key: \"os\", Value: bson.D{\n\t\t\t{Key: \"type\", Value: runtime.GOOS},\n\t\t\t{Key: \"architecture\", Value: runtime.GOARCH},\n\t\t}},\n\t\t{Key: \"platform\", Value: runtime.Version() + \"|Library Platform\"},\n\t})\n\n\t// 3. Wait 5ms for the connection to become idle.\n\ttime.Sleep(5 * time.Millisecond)\n\n\t// 5. Append new driver info.\n\tmt.Client.AppendDriverInfo(options.DriverInfo{\n\t\tName:     \"library\",\n\t\tVersion:  \"1.2\",\n\t\tPlatform: \"Library Platform\",\n\t})\n\n\t// Drain the proxy to ensure we only capture messages after appending.\n\tmt.GetProxyCapture().Drain()\n\n\t// 6. Send a ping command to the server and verify that the command succeeded.\n\terr = mt.Client.Ping(context.Background(), nil)\n\trequire.NoError(mt, err, \"Ping error: %v\", err)\n\n\t// 7.  Save intercepted `client` document as `updatedClientMetadata`.\n\tupdatedClientMetadata := mt.GetProxyCapture().TryNext()\n\trequire.NotNil(mt, updatedClientMetadata, \"expected to capture a proxied message\")\n\tassert.True(mt, updatedClientMetadata.IsHandshake(), \"expected first message to be a handshake\")\n\n\tclientMetadata := clientMetadataFromHandshake(mt, updatedClientMetadata.Sent.Command)\n\tassertbson.EqualDocument(mt, want, clientMetadata)\n}\n\n// Test 6: Metadata is not appended if identical to initial metadata (separated\n// by non-identical metadata)\nfunc TestHandshakeProse_AppendMetadata_NotAppendedIfIdentical_NonSequential(t *testing.T) {\n\topts := mtest.NewOptions().ClientType(mtest.Proxy)\n\tmt := mtest.New(t, opts)\n\n\toriginalDriverInfo := options.DriverInfo{\n\t\tName:     \"library\",\n\t\tVersion:  \"1.2\",\n\t\tPlatform: \"Library Platform\",\n\t}\n\n\tclientOpts := options.Client().\n\t\t// Set idle timeout to 1ms to force new connections to be created\n\t\t// throughout the lifetime of the test.\n\t\tSetMaxConnIdleTime(1 * time.Millisecond).\n\t\tSetDriverInfo(&originalDriverInfo)\n\n\t// 1. Create a top-level client that can be shared among sub-tests. This is\n\t// necessary to test appending driver info to an existing client.\n\tmt.ResetClient(clientOpts)\n\n\t// Drain the proxy to ensure we only capture messages after appending.\n\tmt.GetProxyCapture().Drain()\n\n\t// 2. Send a ping command to the server and verify that the command succeeded.\n\terr := mt.Client.Ping(context.Background(), nil)\n\trequire.NoError(mt, err, \"Ping error: %v\", err)\n\n\t// 3. Wait 5ms for the connection to become idle.\n\ttime.Sleep(5 * time.Millisecond)\n\n\t// 4. Append new driver info.\n\tmt.Client.AppendDriverInfo(options.DriverInfo{\n\t\tName:     \"framework\",\n\t\tVersion:  \"1.2\",\n\t\tPlatform: \"Framework Platform\",\n\t})\n\n\t// Drain the proxy to ensure we only capture messages after appending.\n\tmt.GetProxyCapture().Drain()\n\n\t// 5. Send a ping command to the server and verify that the command succeeded.\n\terr = mt.Client.Ping(context.Background(), nil)\n\trequire.NoError(mt, err, \"Ping error: %v\", err)\n\n\t// 6. Save intercepted `client` document as `clientMetadata`.\n\t//\n\t// NOTE: The Go Driver statically defineds the expected client\n\t// metadata value to make the tests more reliable and prevent\n\t// false-positive assertion results. That deviates from the prose\n\t// test.\n\twant := mustMarshalBSON(bson.D{\n\t\t{Key: \"driver\", Value: bson.D{\n\t\t\t{Key: \"name\", Value: \"mongo-go-driver|library|framework\"},\n\t\t\t{Key: \"version\", Value: version.Driver + \"|1.2\"},\n\t\t}},\n\t\t{Key: \"os\", Value: bson.D{\n\t\t\t{Key: \"type\", Value: runtime.GOOS},\n\t\t\t{Key: \"architecture\", Value: runtime.GOARCH},\n\t\t}},\n\t\t{Key: \"platform\", Value: runtime.Version() + \"|Library Platform|Framework Platform\"},\n\t})\n\n\t// 7. Wait 5ms for the connection to become idle.\n\ttime.Sleep(5 * time.Millisecond)\n\n\t// 8. Append new driver info.\n\tmt.Client.AppendDriverInfo(options.DriverInfo{\n\t\tName:     \"library\",\n\t\tVersion:  \"1.2\",\n\t\tPlatform: \"Library Platform\",\n\t})\n\n\t// Drain the proxy to ensure we only capture messages after appending.\n\tmt.GetProxyCapture().Drain()\n\n\t// 9. Send a `ping` command to the server and verify that the command\n\t// succeeds.\n\terr = mt.Client.Ping(context.Background(), nil)\n\trequire.NoError(mt, err, \"Ping error: %v\", err)\n\n\t// 10. Save intercepted `client` document as `updatedClientMetadata`.\n\tupdatedClientMetadata := mt.GetProxyCapture().TryNext()\n\trequire.NotNil(mt, updatedClientMetadata, \"expected to capture a proxied message\")\n\tassert.True(mt, updatedClientMetadata.IsHandshake(), \"expected first message to be a handshake\")\n\n\tclientMetadata := clientMetadataFromHandshake(mt, updatedClientMetadata.Sent.Command)\n\tassertbson.EqualDocument(mt, want, clientMetadata)\n}\n\n// Test 7: Empty strings are considered unset when appending duplicate metadata.\nfunc TestHandshakeProse_AppendMetadata_EmptyStrings(t *testing.T) {\n\tmt := mtest.New(t)\n\n\ttestCases := []struct {\n\t\tname               string\n\t\tinitialDriverInfo  options.DriverInfo\n\t\ttoAppendDriverInfo options.DriverInfo\n\t\twant               []byte\n\t}{\n\t\t{\n\t\t\tname: \"name empty\",\n\t\t\tinitialDriverInfo: options.DriverInfo{\n\t\t\t\tName:     \"\",\n\t\t\t\tVersion:  \"1.2\",\n\t\t\t\tPlatform: \"Library Platform\",\n\t\t\t},\n\t\t\ttoAppendDriverInfo: options.DriverInfo{\n\t\t\t\tName:     \"\",\n\t\t\t\tVersion:  \"1.2\",\n\t\t\t\tPlatform: \"Library Platform\",\n\t\t\t},\n\t\t\twant: mustMarshalBSON(bson.D{\n\t\t\t\t{Key: \"driver\", Value: bson.D{\n\t\t\t\t\t{Key: \"name\", Value: \"mongo-go-driver\"},\n\t\t\t\t\t{Key: \"version\", Value: version.Driver + \"|1.2\"},\n\t\t\t\t}},\n\t\t\t\t{Key: \"os\", Value: bson.D{\n\t\t\t\t\t{Key: \"type\", Value: runtime.GOOS},\n\t\t\t\t\t{Key: \"architecture\", Value: runtime.GOARCH},\n\t\t\t\t}},\n\t\t\t\t{Key: \"platform\", Value: runtime.Version() + \"|Library Platform\"},\n\t\t\t}),\n\t\t},\n\t\t{\n\t\t\tname: \"version empty\",\n\t\t\tinitialDriverInfo: options.DriverInfo{\n\t\t\t\tName:     \"library\",\n\t\t\t\tVersion:  \"\",\n\t\t\t\tPlatform: \"Library Platform\",\n\t\t\t},\n\t\t\ttoAppendDriverInfo: options.DriverInfo{\n\t\t\t\tName:     \"library\",\n\t\t\t\tVersion:  \"\",\n\t\t\t\tPlatform: \"Library Platform\",\n\t\t\t},\n\t\t\twant: mustMarshalBSON(bson.D{\n\t\t\t\t{Key: \"driver\", Value: bson.D{\n\t\t\t\t\t{Key: \"name\", Value: \"mongo-go-driver|library\"},\n\t\t\t\t\t{Key: \"version\", Value: version.Driver},\n\t\t\t\t}},\n\t\t\t\t{Key: \"os\", Value: bson.D{\n\t\t\t\t\t{Key: \"type\", Value: runtime.GOOS},\n\t\t\t\t\t{Key: \"architecture\", Value: runtime.GOARCH},\n\t\t\t\t}},\n\t\t\t\t{Key: \"platform\", Value: runtime.Version() + \"|Library Platform\"},\n\t\t\t}),\n\t\t},\n\t\t{\n\t\t\tname: \"platform empty\",\n\t\t\tinitialDriverInfo: options.DriverInfo{\n\t\t\t\tName:     \"library\",\n\t\t\t\tVersion:  \"1.2\",\n\t\t\t\tPlatform: \"\",\n\t\t\t},\n\t\t\ttoAppendDriverInfo: options.DriverInfo{\n\t\t\t\tName:     \"library\",\n\t\t\t\tVersion:  \"1.2\",\n\t\t\t\tPlatform: \"\",\n\t\t\t},\n\t\t\twant: mustMarshalBSON(bson.D{\n\t\t\t\t{Key: \"driver\", Value: bson.D{\n\t\t\t\t\t{Key: \"name\", Value: \"mongo-go-driver|library\"},\n\t\t\t\t\t{Key: \"version\", Value: version.Driver + \"|1.2\"},\n\t\t\t\t}},\n\t\t\t\t{Key: \"os\", Value: bson.D{\n\t\t\t\t\t{Key: \"type\", Value: runtime.GOOS},\n\t\t\t\t\t{Key: \"architecture\", Value: runtime.GOARCH},\n\t\t\t\t}},\n\t\t\t\t{Key: \"platform\", Value: runtime.Version()},\n\t\t\t}),\n\t\t},\n\t}\n\n\tfor _, tc := range testCases {\n\t\ttc := tc // Avoid implicit memory aliasing in for loop.\n\n\t\t// Create a top-level client that can be shared among sub-tests. This is\n\t\t// necessary to test appending driver info to an existing client.\n\t\topts := mtest.NewOptions().CreateClient(false).ClientType(mtest.Proxy)\n\t\tmt.RunOpts(tc.name, opts, func(mt *mtest.T) {\n\t\t\t// 1. Create a `MongoClient` instance.\n\t\t\tclientOpts := options.Client().\n\t\t\t\t// Set idle timeout to 1ms to force new connections to be created\n\t\t\t\t// throughout the lifetime of the test.\n\t\t\t\tSetMaxConnIdleTime(1 * time.Millisecond)\n\n\t\t\tmt.ResetClient(clientOpts)\n\n\t\t\t// 2. Append the `DriverInfoOptions` from the selected test case from\n\t\t\t// the initial metadata section.\n\t\t\tmt.Client.AppendDriverInfo(tc.initialDriverInfo)\n\n\t\t\tmt.GetProxyCapture().Drain()\n\n\t\t\t// 3. Send a `ping` command to the server and verify that the command\n\t\t\t// succeeds.\n\t\t\terr := mt.Client.Ping(context.Background(), nil)\n\t\t\trequire.NoError(mt, err, \"Ping error: %v\", err)\n\n\t\t\t// 4. Save intercepted `client` document as `initialClientMetadata`.\n\t\t\t//\n\t\t\t// NOTE: The Go Driver statically defineds the expected client\n\t\t\t// metadata value to make the tests more reliable and prevent\n\t\t\t// false-positive assertion results. That deviates from the prose\n\t\t\t// test.\n\t\t\t//\n\t\t\t// See the \"want\" field in each test case for the expected client\n\t\t\t// metadata value.\n\n\t\t\t// 5. Wait 5ms for the connection to become idle.\n\t\t\ttime.Sleep(5 * time.Millisecond)\n\n\t\t\t// 6. Append the `DriverInfoOptions` from the selected test case from\n\t\t\t// the appended metadata section.\n\t\t\tmt.Client.AppendDriverInfo(tc.toAppendDriverInfo)\n\n\t\t\t// Drain the proxy\n\t\t\tmt.GetProxyCapture().Drain()\n\n\t\t\t// 7. Send a `ping` command to the server and verify the command\n\t\t\t// succeeds.\n\t\t\terr = mt.Client.Ping(context.Background(), nil)\n\t\t\trequire.NoError(mt, err, \"Ping error: %v\", err)\n\n\t\t\t// Capture the first message sent after appending driver info.\n\t\t\tupdatedClientMetadata := mt.GetProxyCapture().TryNext()\n\t\t\trequire.NotNil(mt, updatedClientMetadata, \"expected to capture a proxied message\")\n\t\t\tassert.True(mt, updatedClientMetadata.IsHandshake(), \"expected first message to be a handshake\")\n\n\t\t\tclientMetadata := clientMetadataFromHandshake(mt, updatedClientMetadata.Sent.Command)\n\t\t\tassertbson.EqualDocument(mt, tc.want, clientMetadata)\n\t\t})\n\t}\n}\n\n// Test 8: Empty strings are considered unset when appending metadata identical\n// to initial metadata\nfunc TestHandshakeProse_AppendMetadata_EmptyStrings_InitializedClient(t *testing.T) {\n\tmt := mtest.New(t)\n\n\ttestCases := []struct {\n\t\tname               string\n\t\tinitialDriverInfo  options.DriverInfo\n\t\ttoAppendDriverInfo options.DriverInfo\n\t\twant               []byte\n\t}{\n\t\t{\n\t\t\tname: \"name empty\",\n\t\t\tinitialDriverInfo: options.DriverInfo{\n\t\t\t\tName:     \"\",\n\t\t\t\tVersion:  \"1.2\",\n\t\t\t\tPlatform: \"Library Platform\",\n\t\t\t},\n\t\t\ttoAppendDriverInfo: options.DriverInfo{\n\t\t\t\tName:     \"\",\n\t\t\t\tVersion:  \"1.2\",\n\t\t\t\tPlatform: \"Library Platform\",\n\t\t\t},\n\t\t\twant: mustMarshalBSON(bson.D{\n\t\t\t\t{Key: \"driver\", Value: bson.D{\n\t\t\t\t\t{Key: \"name\", Value: \"mongo-go-driver\"},\n\t\t\t\t\t{Key: \"version\", Value: version.Driver + \"|1.2\"},\n\t\t\t\t}},\n\t\t\t\t{Key: \"os\", Value: bson.D{\n\t\t\t\t\t{Key: \"type\", Value: runtime.GOOS},\n\t\t\t\t\t{Key: \"architecture\", Value: runtime.GOARCH},\n\t\t\t\t}},\n\t\t\t\t{Key: \"platform\", Value: runtime.Version() + \"|Library Platform\"},\n\t\t\t}),\n\t\t},\n\t\t{\n\t\t\tname: \"version empty\",\n\t\t\tinitialDriverInfo: options.DriverInfo{\n\t\t\t\tName:     \"library\",\n\t\t\t\tVersion:  \"\",\n\t\t\t\tPlatform: \"Library Platform\",\n\t\t\t},\n\t\t\ttoAppendDriverInfo: options.DriverInfo{\n\t\t\t\tName:     \"library\",\n\t\t\t\tVersion:  \"\",\n\t\t\t\tPlatform: \"Library Platform\",\n\t\t\t},\n\t\t\twant: mustMarshalBSON(bson.D{\n\t\t\t\t{Key: \"driver\", Value: bson.D{\n\t\t\t\t\t{Key: \"name\", Value: \"mongo-go-driver|library\"},\n\t\t\t\t\t{Key: \"version\", Value: version.Driver},\n\t\t\t\t}},\n\t\t\t\t{Key: \"os\", Value: bson.D{\n\t\t\t\t\t{Key: \"type\", Value: runtime.GOOS},\n\t\t\t\t\t{Key: \"architecture\", Value: runtime.GOARCH},\n\t\t\t\t}},\n\t\t\t\t{Key: \"platform\", Value: runtime.Version() + \"|Library Platform\"},\n\t\t\t}),\n\t\t},\n\t\t{\n\t\t\tname: \"platform empty\",\n\t\t\tinitialDriverInfo: options.DriverInfo{\n\t\t\t\tName:     \"library\",\n\t\t\t\tVersion:  \"1.2\",\n\t\t\t\tPlatform: \"\",\n\t\t\t},\n\t\t\ttoAppendDriverInfo: options.DriverInfo{\n\t\t\t\tName:     \"library\",\n\t\t\t\tVersion:  \"1.2\",\n\t\t\t\tPlatform: \"\",\n\t\t\t},\n\t\t\twant: mustMarshalBSON(bson.D{\n\t\t\t\t{Key: \"driver\", Value: bson.D{\n\t\t\t\t\t{Key: \"name\", Value: \"mongo-go-driver|library\"},\n\t\t\t\t\t{Key: \"version\", Value: version.Driver + \"|1.2\"},\n\t\t\t\t}},\n\t\t\t\t{Key: \"os\", Value: bson.D{\n\t\t\t\t\t{Key: \"type\", Value: runtime.GOOS},\n\t\t\t\t\t{Key: \"architecture\", Value: runtime.GOARCH},\n\t\t\t\t}},\n\t\t\t\t{Key: \"platform\", Value: runtime.Version()},\n\t\t\t}),\n\t\t},\n\t}\n\n\tfor _, tc := range testCases {\n\t\ttc := tc // Avoid implicit memory aliasing in for loop.\n\n\t\t// Create a top-level client that can be shared among sub-tests. This is\n\t\t// necessary to test appending driver info to an existing client.\n\t\topts := mtest.NewOptions().CreateClient(false).ClientType(mtest.Proxy)\n\t\tmt.RunOpts(tc.name, opts, func(mt *mtest.T) {\n\t\t\t// 1. Create a `MongoClient` instance.\n\t\t\tclientOpts := options.Client().\n\t\t\t\t// Set idle timeout to 1ms to force new connections to be created\n\t\t\t\t// throughout the lifetime of the test.\n\t\t\t\tSetMaxConnIdleTime(1 * time.Millisecond).\n\t\t\t\tSetDriverInfo(&tc.initialDriverInfo)\n\n\t\t\tmt.ResetClient(clientOpts)\n\n\t\t\t// 2. Send a `ping` command to the server and verify that the command\n\t\t\t// succeeds.\n\t\t\terr := mt.Client.Ping(context.Background(), nil)\n\t\t\trequire.NoError(mt, err, \"Ping error: %v\", err)\n\n\t\t\t// 3. Save intercepted `client` document as `initialClientMetadata`.\n\t\t\t//\n\t\t\t// NOTE: The Go Driver statically defineds the expected client\n\t\t\t// metadata value to make the tests more reliable and prevent\n\t\t\t// false-positive assertion results. That deviates from the prose\n\t\t\t// test.\n\t\t\t//\n\t\t\t// See the \"want\" field in each test case for the expected client\n\t\t\t// metadata value.\n\n\t\t\t// 4. Wait 5ms for the connection to become idle.\n\t\t\ttime.Sleep(5 * time.Millisecond)\n\n\t\t\t// 5. Append the `DriverInfoOptions` from the selected test case from\n\t\t\t// the appended metadata section.\n\t\t\tmt.Client.AppendDriverInfo(tc.toAppendDriverInfo)\n\n\t\t\t// Drain the proxy\n\t\t\tmt.GetProxyCapture().Drain()\n\n\t\t\t// 6. Send a `ping` command to the server and verify the command\n\t\t\t// succeeds.\n\t\t\terr = mt.Client.Ping(context.Background(), nil)\n\t\t\trequire.NoError(mt, err, \"Ping error: %v\", err)\n\n\t\t\t// 7. Store the response as `updatedClientMetadata`.\n\t\t\tupdatedClientMetadata := mt.GetProxyCapture().TryNext()\n\t\t\trequire.NotNil(mt, updatedClientMetadata, \"expected to capture a proxied message\")\n\t\t\tassert.True(mt, updatedClientMetadata.IsHandshake(), \"expected first message to be a handshake\")\n\n\t\t\t// 8. Assert that `initialClientMetadata` is identical to `updatedClientMetadata`.\n\t\t\tclientMetadata := clientMetadataFromHandshake(mt, updatedClientMetadata.Sent.Command)\n\t\t\tassertbson.EqualDocument(mt, tc.want, clientMetadata)\n\t\t})\n\t}\n}\n\n// mustMarshalBSON marshals a value to BSON. It panics if any error occurs.\nfunc mustMarshalBSON(val interface{}) []byte {\n\tbytes, err := bson.Marshal(val)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\treturn bytes\n}\n\n// clientMetadataFromHandshake returns the BSON document from the \"client\" field\n// of the command document.\nfunc clientMetadataFromHandshake(mt *mtest.T, cmd bsoncore.Document) []byte {\n\tmt.Helper()\n\n\tclient, err := cmd.LookupErr(\"client\")\n\trequire.NoError(mt, err, \"no client field in handshake command document\")\n\n\tclientDoc, ok := client.DocumentOK()\n\trequire.True(mt, ok, \"the client field is not a BSON document\")\n\n\treturn clientDoc\n}\n"
  },
  {
    "path": "internal/integration/index_view_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage integration\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"github.com/google/go-cmp/cmp\"\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/failpoint\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/integration/mtest\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/writeconcern\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n)\n\ntype index struct {\n\tKey  bson.D\n\tName string\n}\n\nfunc TestIndexView(t *testing.T) {\n\tmt := mtest.New(t, noClientOpts)\n\n\tpbool := func(b bool) *bool { return &b }\n\tpint32 := func(i int32) *int32 { return &i }\n\n\tmt.Run(\"list\", func(mt *mtest.T) {\n\t\t// For server versions below 3.0, we internally execute List() as a legacy OP_QUERY against the system.indexes\n\t\t// collection. Command monitoring upconversions translate this to a \"find\" command rather than \"listIndexes\".\n\t\tcmdName := \"listIndexes\"\n\t\tif mtest.CompareServerVersions(mtest.ServerVersion(), \"3.0\") < 0 {\n\t\t\tcmdName = \"find\"\n\t\t}\n\n\t\tmt.Run(\"_id index is always listed\", func(mt *mtest.T) {\n\t\t\tverifyIndexExists(mt, mt.Coll.Indexes(), index{\n\t\t\t\tKey:  bson.D{{\"_id\", int32(1)}},\n\t\t\t\tName: \"_id_\",\n\t\t\t})\n\t\t})\n\t\tmt.Run(\"getMore commands are monitored\", func(mt *mtest.T) {\n\t\t\tcreateIndexes(mt, mt.Coll, 2)\n\t\t\tassertGetMoreCommandsAreMonitored(mt, cmdName, func() (*mongo.Cursor, error) {\n\t\t\t\treturn mt.Coll.Indexes().List(context.Background(), options.ListIndexes().SetBatchSize(2))\n\t\t\t})\n\t\t})\n\t\tmt.Run(\"killCursors commands are monitored\", func(mt *mtest.T) {\n\t\t\tcreateIndexes(mt, mt.Coll, 2)\n\t\t\tassertKillCursorsCommandsAreMonitored(mt, cmdName, func() (*mongo.Cursor, error) {\n\t\t\t\treturn mt.Coll.Indexes().List(context.Background(), options.ListIndexes().SetBatchSize(2))\n\t\t\t})\n\t\t})\n\t})\n\tmt.RunOpts(\"create one\", noClientOpts, func(mt *mtest.T) {\n\t\tmt.Run(\"name not specified\", func(mt *mtest.T) {\n\t\t\tiv := mt.Coll.Indexes()\n\t\t\tkeysDoc := bson.D{\n\t\t\t\t{\"foo\", int32(1)},\n\t\t\t\t{\"bar\", int32(-1)},\n\t\t\t}\n\t\t\texpectedName := \"foo_1_bar_-1\"\n\n\t\t\tindexName, err := iv.CreateOne(context.Background(), mongo.IndexModel{\n\t\t\t\tKeys: keysDoc,\n\t\t\t})\n\t\t\tassert.Nil(mt, err, \"CreateOne error: %v\", err)\n\t\t\tassert.Equal(mt, expectedName, indexName, \"expected name %q, got %q\", expectedName, indexName)\n\n\t\t\tverifyIndexExists(mt, iv, index{\n\t\t\t\tKey:  keysDoc,\n\t\t\t\tName: indexName,\n\t\t\t})\n\t\t})\n\t\tmt.Run(\"specify name\", func(mt *mtest.T) {\n\t\t\tiv := mt.Coll.Indexes()\n\t\t\tkeysDoc := bson.D{{\"foo\", int32(-1)}}\n\t\t\tname := \"testname\"\n\n\t\t\tindexName, err := iv.CreateOne(context.Background(), mongo.IndexModel{\n\t\t\t\tKeys:    keysDoc,\n\t\t\t\tOptions: options.Index().SetName(name),\n\t\t\t})\n\t\t\tassert.Nil(mt, err, \"CreateOne error: %v\", err)\n\t\t\tassert.Equal(mt, name, indexName, \"expected returned name %q, got %q\", name, indexName)\n\n\t\t\tverifyIndexExists(mt, iv, index{\n\t\t\t\tKey:  keysDoc,\n\t\t\t\tName: indexName,\n\t\t\t})\n\t\t})\n\t\tmt.Run(\"all options\", func(mt *mtest.T) {\n\t\t\topts := options.Index().\n\t\t\t\tSetExpireAfterSeconds(10).\n\t\t\t\tSetName(\"a\").\n\t\t\t\tSetSparse(false).\n\t\t\t\tSetUnique(false).\n\t\t\t\tSetVersion(1).\n\t\t\t\tSetDefaultLanguage(\"english\").\n\t\t\t\tSetLanguageOverride(\"english\").\n\t\t\t\tSetTextVersion(1).\n\t\t\t\tSetWeights(bson.D{}).\n\t\t\t\tSetSphereVersion(1).\n\t\t\t\tSetBits(2).\n\t\t\t\tSetMax(10).\n\t\t\t\tSetMin(1).\n\t\t\t\tSetPartialFilterExpression(bson.D{}).\n\t\t\t\tSetStorageEngine(bson.D{\n\t\t\t\t\t{\"wiredTiger\", bson.D{\n\t\t\t\t\t\t{\"configString\", \"block_compressor=zlib\"},\n\t\t\t\t\t}},\n\t\t\t\t})\n\n\t\t\t// Only check SetBucketSize if version is less than 4.9\n\t\t\tif mtest.CompareServerVersions(mtest.ServerVersion(), \"4.9\") < 0 {\n\t\t\t\topts.SetBucketSize(1)\n\t\t\t}\n\t\t\t// Omits collation option because it's incompatible with version option\n\t\t\t_, err := mt.Coll.Indexes().CreateOne(context.Background(), mongo.IndexModel{\n\t\t\t\tKeys:    bson.D{{\"foo\", \"text\"}},\n\t\t\t\tOptions: opts,\n\t\t\t})\n\t\t\tassert.Nil(mt, err, \"CreateOne error: %v\", err)\n\t\t})\n\t\tmt.RunOpts(\"collation\", mtest.NewOptions().MinServerVersion(\"3.4\"), func(mt *mtest.T) {\n\t\t\t// collation invalid for server versions < 3.4\n\t\t\t_, err := mt.Coll.Indexes().CreateOne(context.Background(), mongo.IndexModel{\n\t\t\t\tKeys: bson.D{{\"bar\", \"text\"}},\n\t\t\t\tOptions: options.Index().SetCollation(&options.Collation{\n\t\t\t\t\tLocale: \"simple\",\n\t\t\t\t}),\n\t\t\t})\n\t\t\tassert.Nil(mt, err, \"CreateOne error: %v\", err)\n\t\t})\n\t\tmt.RunOpts(\"wildcard\", mtest.NewOptions().MinServerVersion(\"4.1\").CreateClient(false), func(mt *mtest.T) {\n\t\t\tkeysDoc := bson.D{{\"$**\", int32(1)}}\n\n\t\t\tmt.Run(\"no options\", func(mt *mtest.T) {\n\t\t\t\tiv := mt.Coll.Indexes()\n\t\t\t\tindexName, err := iv.CreateOne(context.Background(), mongo.IndexModel{\n\t\t\t\t\tKeys: keysDoc,\n\t\t\t\t})\n\t\t\t\tassert.Nil(mt, err, \"CreateOne error: %v\", err)\n\t\t\t\tverifyIndexExists(mt, iv, index{\n\t\t\t\t\tKey:  keysDoc,\n\t\t\t\t\tName: indexName,\n\t\t\t\t})\n\t\t\t})\n\t\t\tmt.Run(\"wildcard projection\", func(mt *mtest.T) {\n\t\t\t\tiv := mt.Coll.Indexes()\n\n\t\t\t\t// Create an index with a wildcard projection document. The \"_id: false\" isn't needed to create the\n\t\t\t\t// index. We use the listIndexes command below to assert that the created index has this projection\n\t\t\t\t// document and the format of the document returned by listIndexes was changed in 4.5.x to explicitly\n\t\t\t\t// include \"_id: false\", so we include it here too.\n\t\t\t\tproj := bson.D{{\"a\", true}, {\"_id\", false}}\n\t\t\t\t_, err := iv.CreateOne(context.Background(), mongo.IndexModel{\n\t\t\t\t\tKeys:    keysDoc,\n\t\t\t\t\tOptions: options.Index().SetWildcardProjection(proj),\n\t\t\t\t})\n\t\t\t\tassert.Nil(mt, err, \"CreateOne error: %v\", err)\n\n\t\t\t\tindexDoc := getIndexDoc(mt, iv, keysDoc)\n\t\t\t\tassert.NotNil(mt, indexDoc, \"expected to find keys document %v but was not found\", keysDoc)\n\t\t\t\tcheckIndexDocContains(mt, indexDoc, bson.E{\n\t\t\t\t\tKey:   \"wildcardProjection\",\n\t\t\t\t\tValue: proj,\n\t\t\t\t})\n\t\t\t})\n\t\t})\n\t\tmt.RunOpts(\"hidden\", mtest.NewOptions().MinServerVersion(\"4.4\"), func(mt *mtest.T) {\n\t\t\tiv := mt.Coll.Indexes()\n\t\t\tkeysDoc := bson.D{{\"x\", int32(1)}}\n\t\t\tmodel := mongo.IndexModel{\n\t\t\t\tKeys:    keysDoc,\n\t\t\t\tOptions: options.Index().SetHidden(true),\n\t\t\t}\n\n\t\t\t_, err := iv.CreateOne(context.Background(), model)\n\t\t\tassert.Nil(mt, err, \"CreateOne error: %v\", err)\n\n\t\t\tindexDoc := getIndexDoc(mt, iv, keysDoc)\n\t\t\tassert.NotNil(mt, indexDoc, \"index with keys document %v was not found\", keysDoc)\n\t\t\tcheckIndexDocContains(mt, indexDoc, bson.E{\n\t\t\t\tKey:   \"hidden\",\n\t\t\t\tValue: true,\n\t\t\t})\n\t\t})\n\t\tmt.Run(\"nil keys\", func(mt *mtest.T) {\n\t\t\t_, err := mt.Coll.Indexes().CreateOne(context.Background(), mongo.IndexModel{\n\t\t\t\tKeys: nil,\n\t\t\t})\n\t\t\tassert.NotNil(mt, err, \"expected CreateOne error, got nil\")\n\t\t})\n\t\t// Only run on replica sets as commitQuorum is not supported on standalones.\n\t\tmt.RunOpts(\"commit quorum\", mtest.NewOptions().Topologies(mtest.ReplicaSet).CreateClient(false), func(mt *mtest.T) {\n\t\t\tintVal := options.CreateIndexes().SetCommitQuorumInt(1)\n\t\t\tstringVal := options.CreateIndexes().SetCommitQuorumString(\"majority\")\n\t\t\tmajority := options.CreateIndexes().SetCommitQuorumMajority()\n\t\t\tvotingMembers := options.CreateIndexes().SetCommitQuorumVotingMembers()\n\n\t\t\tindexModel := mongo.IndexModel{\n\t\t\t\tKeys: bson.D{{\"x\", 1}},\n\t\t\t}\n\n\t\t\ttestCases := []struct {\n\t\t\t\tname             string\n\t\t\t\topts             *options.CreateIndexesOptionsBuilder\n\t\t\t\texpectError      bool\n\t\t\t\texpectedValue    any // ignored if expectError is true\n\t\t\t\tminServerVersion string\n\t\t\t\tmaxServerVersion string\n\t\t\t}{\n\t\t\t\t{\"error on server versions before 4.4\", majority, true, nil, \"\", \"4.2\"},\n\t\t\t\t{\"integer value\", intVal, false, int32(1), \"4.4\", \"\"},\n\t\t\t\t{\"string value\", stringVal, false, \"majority\", \"4.4\", \"\"},\n\t\t\t\t{\"majority\", majority, false, \"majority\", \"4.4\", \"\"},\n\t\t\t\t{\"votingMembers\", votingMembers, false, \"votingMembers\", \"4.4\", \"\"},\n\t\t\t}\n\t\t\tfor _, tc := range testCases {\n\t\t\t\tmtOpts := mtest.NewOptions().MinServerVersion(tc.minServerVersion).MaxServerVersion(tc.maxServerVersion)\n\t\t\t\tmt.RunOpts(tc.name, mtOpts, func(mt *mtest.T) {\n\t\t\t\t\tmt.ClearEvents()\n\t\t\t\t\t_, err := mt.Coll.Indexes().CreateOne(context.Background(), indexModel, tc.opts)\n\t\t\t\t\tif tc.expectError {\n\t\t\t\t\t\tassert.NotNil(mt, err, \"expected CreateOne error, got nil\")\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\n\t\t\t\t\tassert.Nil(mt, err, \"CreateOne error: %v\", err)\n\t\t\t\t\tcmd := mt.GetStartedEvent().Command\n\t\t\t\t\tsentBSONValue, err := cmd.LookupErr(\"commitQuorum\")\n\t\t\t\t\tassert.Nil(mt, err, \"expected commitQuorum in command %s\", cmd)\n\n\t\t\t\t\tvar sentValue any\n\t\t\t\t\terr = sentBSONValue.Unmarshal(&sentValue)\n\t\t\t\t\tassert.Nil(mt, err, \"Unmarshal error: %v\", err)\n\n\t\t\t\t\tassert.Equal(mt, tc.expectedValue, sentValue, \"expected commitQuorum value %v, got %v\",\n\t\t\t\t\t\ttc.expectedValue, sentValue)\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\t\t// Needs to run on these versions for failpoints\n\t\tmt.RunOpts(\"replace error\", mtest.NewOptions().Topologies(mtest.ReplicaSet).MinServerVersion(\"4.0\"), func(mt *mtest.T) {\n\t\t\tmt.SetFailPoint(failpoint.FailPoint{\n\t\t\t\tConfigureFailPoint: \"failCommand\",\n\t\t\t\tMode:               failpoint.ModeAlwaysOn,\n\t\t\t\tData: failpoint.Data{\n\t\t\t\t\tFailCommands: []string{\"createIndexes\"},\n\t\t\t\t\tErrorCode:    100,\n\t\t\t\t},\n\t\t\t})\n\n\t\t\t_, err := mt.Coll.Indexes().CreateOne(context.Background(), mongo.IndexModel{Keys: bson.D{{\"x\", 1}}})\n\t\t\tassert.NotNil(mt, err, \"expected CreateOne error, got nil\")\n\t\t\tcmdErr, ok := err.(mongo.CommandError)\n\t\t\tassert.True(mt, ok, \"expected mongo.CommandError, got %T\", err)\n\t\t\tassert.Equal(mt, int32(100), cmdErr.Code, \"expected error code 100, got %v\", cmdErr.Code)\n\t\t})\n\t\tmt.Run(\"multi-key map\", func(mt *mtest.T) {\n\t\t\tiv := mt.Coll.Indexes()\n\n\t\t\t_, err := iv.CreateOne(context.Background(), mongo.IndexModel{\n\t\t\t\tKeys: bson.M{\"foo\": 1, \"bar\": -1},\n\t\t\t})\n\t\t\tassert.NotNil(mt, err, \"expected CreateOne error, got nil\")\n\t\t\tassert.Equal(mt, mongo.ErrMapForOrderedArgument{\"keys\"}, err, \"expected error %v, got %v\", mongo.ErrMapForOrderedArgument{\"key\"}, err)\n\t\t})\n\t\tmt.Run(\"single key map\", func(mt *mtest.T) {\n\t\t\tiv := mt.Coll.Indexes()\n\t\t\texpectedName := \"foo_1\"\n\n\t\t\tindexName, err := iv.CreateOne(context.Background(), mongo.IndexModel{\n\t\t\t\tKeys: bson.M{\"foo\": 1},\n\t\t\t})\n\t\t\tassert.Nil(mt, err, \"CreateOne error: %v\", err)\n\t\t\tassert.Equal(mt, expectedName, indexName, \"expected name %q, got %q\", expectedName, indexName)\n\n\t\t\tverifyIndexExists(mt, iv, index{\n\t\t\t\tKey:  bson.D{{\"foo\", int32(1)}},\n\t\t\t\tName: indexName,\n\t\t\t})\n\t\t})\n\t})\n\tmt.Run(\"create many\", func(mt *mtest.T) {\n\t\tmt.Run(\"success\", func(mt *mtest.T) {\n\t\t\tiv := mt.Coll.Indexes()\n\t\t\tfirstKeysDoc := bson.D{{\"foo\", int32(-1)}}\n\t\t\tsecondKeysDoc := bson.D{{\"bar\", int32(1)}, {\"baz\", int32(-1)}}\n\t\t\texpectedNames := []string{\"foo_-1\", \"bar_1_baz_-1\"}\n\t\t\tindexNames, err := iv.CreateMany(context.Background(), []mongo.IndexModel{\n\t\t\t\t{\n\t\t\t\t\tKeys: firstKeysDoc,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tKeys: secondKeysDoc,\n\t\t\t\t},\n\t\t\t})\n\t\t\tassert.Nil(mt, err, \"CreateMany error: %v\", err)\n\t\t\tassert.Equal(mt, expectedNames, indexNames, \"expected returned names %v, got %v\", expectedNames, indexNames)\n\n\t\t\tverifyIndexExists(mt, iv, index{\n\t\t\t\tKey:  firstKeysDoc,\n\t\t\t\tName: indexNames[0],\n\t\t\t})\n\t\t\tverifyIndexExists(mt, iv, index{\n\t\t\t\tKey:  secondKeysDoc,\n\t\t\t\tName: indexNames[1],\n\t\t\t})\n\t\t})\n\t\twc := writeconcern.W1()\n\t\twcMtOpts := mtest.NewOptions().CollectionOptions(options.Collection().SetWriteConcern(wc))\n\t\tmt.RunOpts(\"uses writeconcern\", wcMtOpts, func(mt *mtest.T) {\n\t\t\tiv := mt.Coll.Indexes()\n\t\t\t_, err := iv.CreateMany(context.Background(), []mongo.IndexModel{\n\t\t\t\t{\n\t\t\t\t\tKeys: bson.D{{\"foo\", -1}},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tKeys: bson.D{{\"bar\", 1}, {\"baz\", -1}},\n\t\t\t\t},\n\t\t\t})\n\t\t\tassert.Nil(mt, err, \"CreateMany error: %v\", err)\n\n\t\t\tevt := mt.GetStartedEvent()\n\t\t\tassert.NotNil(mt, evt, \"expected CommandStartedEvent, got nil\")\n\n\t\t\tassert.Equal(mt, \"createIndexes\", evt.CommandName, \"command name mismatch; expected createIndexes, got %s\", evt.CommandName)\n\n\t\t\tactual, err := evt.Command.LookupErr(\"writeConcern\", \"w\")\n\t\t\tassert.Nil(mt, err, \"error getting writeConcern.w: %s\", err)\n\n\t\t\twcVal := numberFromValue(mt, actual)\n\t\t\tassert.Equal(mt, int64(1), wcVal, \"expected writeConcern to be 1, got: %v\", wcVal)\n\t\t})\n\t\t// Only run on replica sets as commitQuorum is not supported on standalones.\n\t\tmt.RunOpts(\"commit quorum\", mtest.NewOptions().Topologies(mtest.ReplicaSet).CreateClient(false), func(mt *mtest.T) {\n\t\t\tintVal := options.CreateIndexes().SetCommitQuorumInt(1)\n\t\t\tstringVal := options.CreateIndexes().SetCommitQuorumString(\"majority\")\n\t\t\tmajority := options.CreateIndexes().SetCommitQuorumMajority()\n\t\t\tvotingMembers := options.CreateIndexes().SetCommitQuorumVotingMembers()\n\n\t\t\tindexModel1 := mongo.IndexModel{\n\t\t\t\tKeys: bson.D{{\"x\", 1}},\n\t\t\t}\n\t\t\tindexModel2 := mongo.IndexModel{\n\t\t\t\tKeys: bson.D{{\"y\", 1}},\n\t\t\t}\n\n\t\t\ttestCases := []struct {\n\t\t\t\tname             string\n\t\t\t\topts             *options.CreateIndexesOptionsBuilder\n\t\t\t\texpectError      bool\n\t\t\t\texpectedValue    any // ignored if expectError is true\n\t\t\t\tminServerVersion string\n\t\t\t\tmaxServerVersion string\n\t\t\t}{\n\t\t\t\t{\"error on server versions before 4.4\", majority, true, nil, \"\", \"4.2\"},\n\t\t\t\t{\"integer value\", intVal, false, int32(1), \"4.4\", \"\"},\n\t\t\t\t{\"string value\", stringVal, false, \"majority\", \"4.4\", \"\"},\n\t\t\t\t{\"majority\", majority, false, \"majority\", \"4.4\", \"\"},\n\t\t\t\t{\"votingMembers\", votingMembers, false, \"votingMembers\", \"4.4\", \"\"},\n\t\t\t}\n\t\t\tfor _, tc := range testCases {\n\t\t\t\tmtOpts := mtest.NewOptions().MinServerVersion(tc.minServerVersion).MaxServerVersion(tc.maxServerVersion)\n\t\t\t\tmt.RunOpts(tc.name, mtOpts, func(mt *mtest.T) {\n\t\t\t\t\tmt.ClearEvents()\n\t\t\t\t\t_, err := mt.Coll.Indexes().CreateMany(context.Background(), []mongo.IndexModel{indexModel1, indexModel2}, tc.opts)\n\t\t\t\t\tif tc.expectError {\n\t\t\t\t\t\tassert.NotNil(mt, err, \"expected CreateMany error, got nil\")\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\n\t\t\t\t\tassert.Nil(mt, err, \"CreateMany error: %v\", err)\n\t\t\t\t\tcmd := mt.GetStartedEvent().Command\n\t\t\t\t\tsentBSONValue, err := cmd.LookupErr(\"commitQuorum\")\n\t\t\t\t\tassert.Nil(mt, err, \"expected commitQuorum in command %s\", cmd)\n\n\t\t\t\t\tvar sentValue any\n\t\t\t\t\terr = sentBSONValue.Unmarshal(&sentValue)\n\t\t\t\t\tassert.Nil(mt, err, \"Unmarshal error: %v\", err)\n\n\t\t\t\t\tassert.Equal(mt, tc.expectedValue, sentValue, \"expected commitQuorum value %v, got %v\",\n\t\t\t\t\t\ttc.expectedValue, sentValue)\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\t\t// Needs to run on these versions for failpoints\n\t\tmt.RunOpts(\"replace error\", mtest.NewOptions().Topologies(mtest.ReplicaSet).MinServerVersion(\"4.0\"), func(mt *mtest.T) {\n\t\t\tmt.SetFailPoint(failpoint.FailPoint{\n\t\t\t\tConfigureFailPoint: \"failCommand\",\n\t\t\t\tMode:               failpoint.ModeAlwaysOn,\n\t\t\t\tData: failpoint.Data{\n\t\t\t\t\tFailCommands: []string{\"createIndexes\"},\n\t\t\t\t\tErrorCode:    100,\n\t\t\t\t},\n\t\t\t})\n\n\t\t\t_, err := mt.Coll.Indexes().CreateMany(context.Background(), []mongo.IndexModel{\n\t\t\t\t{\n\t\t\t\t\tKeys: bson.D{{\"foo\", int32(-1)}},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tKeys: bson.D{{\"bar\", int32(1)}, {\"baz\", int32(-1)}},\n\t\t\t\t},\n\t\t\t})\n\t\t\tassert.NotNil(mt, err, \"expected CreateMany error, got nil\")\n\t\t\tcmdErr, ok := err.(mongo.CommandError)\n\t\t\tassert.True(mt, ok, \"expected mongo.CommandError, got %T\", err)\n\t\t\tassert.Equal(mt, int32(100), cmdErr.Code, \"expected error code 100, got %v\", cmdErr.Code)\n\t\t})\n\t\tmt.Run(\"multi-key map\", func(mt *mtest.T) {\n\t\t\tiv := mt.Coll.Indexes()\n\t\t\t_, err := iv.CreateMany(context.Background(), []mongo.IndexModel{\n\t\t\t\t{\n\t\t\t\t\tKeys: bson.M{\"foo\": 1, \"bar\": -1},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tKeys: bson.D{{\"bar\", int32(1)}, {\"baz\", int32(-1)}},\n\t\t\t\t},\n\t\t\t})\n\t\t\tassert.NotNil(mt, err, \"expected CreateOne error, got nil\")\n\t\t\tassert.Equal(mt, mongo.ErrMapForOrderedArgument{\"keys\"}, err, \"expected error %v, got %v\", mongo.ErrMapForOrderedArgument{\"keys\"}, err)\n\t\t})\n\t\tmt.Run(\"single key map\", func(mt *mtest.T) {\n\t\t\tiv := mt.Coll.Indexes()\n\t\t\tfirstKeysDoc := bson.M{\"foo\": -1}\n\t\t\tsecondKeysDoc := bson.D{{\"bar\", int32(1)}, {\"baz\", int32(-1)}}\n\t\t\texpectedNames := []string{\"foo_-1\", \"bar_1_baz_-1\"}\n\t\t\tindexNames, err := iv.CreateMany(context.Background(), []mongo.IndexModel{\n\t\t\t\t{\n\t\t\t\t\tKeys: firstKeysDoc,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tKeys: secondKeysDoc,\n\t\t\t\t},\n\t\t\t})\n\t\t\tassert.Nil(mt, err, \"CreateMany error: %v\", err)\n\t\t\tassert.Equal(mt, expectedNames, indexNames, \"expected returned names %v, got %v\", expectedNames, indexNames)\n\n\t\t\tverifyIndexExists(mt, iv, index{\n\t\t\t\tKey:  bson.D{{\"foo\", int32(-1)}},\n\t\t\t\tName: indexNames[0],\n\t\t\t})\n\t\t\tverifyIndexExists(mt, iv, index{\n\t\t\t\tKey:  secondKeysDoc,\n\t\t\t\tName: indexNames[1],\n\t\t\t})\n\t\t})\n\t})\n\tmt.RunOpts(\"list specifications\", noClientOpts, func(mt *mtest.T) {\n\t\tmt.Run(\"verify results\", func(mt *mtest.T) {\n\t\t\t// Create a handful of indexes\n\t\t\t_, err := mt.Coll.Indexes().CreateMany(context.Background(), []mongo.IndexModel{\n\t\t\t\t{\n\t\t\t\t\tKeys:    bson.D{{\"foo\", int32(-1)}},\n\t\t\t\t\tOptions: options.Index().SetUnique(true),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tKeys:    bson.D{{\"bar\", int32(1)}},\n\t\t\t\t\tOptions: options.Index().SetExpireAfterSeconds(120),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tKeys:    bson.D{{\"baz\", int32(1)}},\n\t\t\t\t\tOptions: options.Index().SetSparse(true),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tKeys: bson.D{{\"bar\", int32(1)}, {\"baz\", int32(-1)}},\n\t\t\t\t},\n\t\t\t})\n\t\t\tassert.Nil(mt, err, \"CreateMany error: %v\", err)\n\n\t\t\texpectedSpecs := []mongo.IndexSpecification{\n\t\t\t\t{\n\t\t\t\t\tName:               \"_id_\",\n\t\t\t\t\tNamespace:          mt.DB.Name() + \".\" + mt.Coll.Name(),\n\t\t\t\t\tKeysDocument:       bson.Raw(bsoncore.NewDocumentBuilder().AppendInt32(\"_id\", 1).Build()),\n\t\t\t\t\tVersion:            2,\n\t\t\t\t\tExpireAfterSeconds: nil,\n\t\t\t\t\tSparse:             nil,\n\t\t\t\t\t// ID index is special and does not return 'true', despite being unique.\n\t\t\t\t\tUnique: nil,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tName:               \"foo_-1\",\n\t\t\t\t\tNamespace:          mt.DB.Name() + \".\" + mt.Coll.Name(),\n\t\t\t\t\tKeysDocument:       bson.Raw(bsoncore.NewDocumentBuilder().AppendInt32(\"foo\", -1).Build()),\n\t\t\t\t\tVersion:            2,\n\t\t\t\t\tExpireAfterSeconds: nil,\n\t\t\t\t\tSparse:             nil,\n\t\t\t\t\tUnique:             pbool(true),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tName:               \"bar_1\",\n\t\t\t\t\tNamespace:          mt.DB.Name() + \".\" + mt.Coll.Name(),\n\t\t\t\t\tKeysDocument:       bson.Raw(bsoncore.NewDocumentBuilder().AppendInt32(\"bar\", 1).Build()),\n\t\t\t\t\tVersion:            2,\n\t\t\t\t\tExpireAfterSeconds: pint32(120),\n\t\t\t\t\tSparse:             nil,\n\t\t\t\t\tUnique:             nil,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tName:               \"baz_1\",\n\t\t\t\t\tNamespace:          mt.DB.Name() + \".\" + mt.Coll.Name(),\n\t\t\t\t\tKeysDocument:       bson.Raw(bsoncore.NewDocumentBuilder().AppendInt32(\"baz\", 1).Build()),\n\t\t\t\t\tVersion:            2,\n\t\t\t\t\tExpireAfterSeconds: nil,\n\t\t\t\t\tSparse:             pbool(true),\n\t\t\t\t\tUnique:             nil,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tName:               \"bar_1_baz_-1\",\n\t\t\t\t\tNamespace:          mt.DB.Name() + \".\" + mt.Coll.Name(),\n\t\t\t\t\tKeysDocument:       bson.Raw(bsoncore.NewDocumentBuilder().AppendInt32(\"bar\", 1).AppendInt32(\"baz\", -1).Build()),\n\t\t\t\t\tVersion:            2,\n\t\t\t\t\tExpireAfterSeconds: nil,\n\t\t\t\t\tSparse:             nil,\n\t\t\t\t\tUnique:             nil,\n\t\t\t\t},\n\t\t\t}\n\n\t\t\tspecs, err := mt.Coll.Indexes().ListSpecifications(context.Background())\n\t\t\tassert.Nil(mt, err, \"ListSpecifications error: %v\", err)\n\t\t\tassert.Equal(mt, len(expectedSpecs), len(specs), \"expected %d specification, got %d\", len(expectedSpecs), len(specs))\n\t\t\tassert.True(mt, cmp.Equal(specs, expectedSpecs), \"expected specifications to match: %v\", cmp.Diff(specs, expectedSpecs))\n\t\t})\n\t\tmt.RunOpts(\"options passed to listIndexes\", mtest.NewOptions().MinServerVersion(\"3.0\"), func(mt *mtest.T) {\n\t\t\topts := options.ListIndexes().SetBatchSize(1)\n\t\t\t_, err := mt.Coll.Indexes().ListSpecifications(context.Background(), opts)\n\t\t\tassert.Nil(mt, err, \"ListSpecifications error: %v\", err)\n\n\t\t\tevt := mt.GetStartedEvent()\n\t\t\tassert.Equal(mt, evt.CommandName, \"listIndexes\", \"expected %q command to be sent, got %q\", \"listIndexes\",\n\t\t\t\tevt.CommandName)\n\n\t\t\tcursorDoc, ok := evt.Command.Lookup(\"cursor\").DocumentOK()\n\t\t\tassert.True(mt, ok, \"expected command: %v to contain a cursor document\", evt.Command)\n\n\t\t\tbatchSize, ok := cursorDoc.Lookup(\"batchSize\").Int32OK()\n\t\t\tassert.True(mt, ok, \"expected command %v to contain %q field\", evt.Command, \"batchSize\")\n\t\t\tassert.Equal(mt, int32(1), batchSize, \"expected batchSize value to be 1, got %d\", batchSize)\n\t\t})\n\t})\n\tmt.Run(\"drop one\", func(mt *mtest.T) {\n\t\tiv := mt.Coll.Indexes()\n\t\tindexNames, err := iv.CreateMany(context.Background(), []mongo.IndexModel{\n\t\t\t{\n\t\t\t\tKeys: bson.D{{\"foo\", -1}},\n\t\t\t},\n\t\t\t{\n\t\t\t\tKeys: bson.D{{\"bar\", 1}, {\"baz\", -1}},\n\t\t\t},\n\t\t})\n\t\tassert.Nil(mt, err, \"CreateMany error: %v\", err)\n\t\tassert.Equal(mt, 2, len(indexNames), \"expected 2 index names, got %v\", len(indexNames))\n\n\t\terr = iv.DropOne(context.Background(), indexNames[1])\n\t\tassert.Nil(mt, err, \"DropOne error: %v\", err)\n\n\t\tcursor, err := iv.List(context.Background())\n\t\tassert.Nil(mt, err, \"List error: %v\", err)\n\t\tfor cursor.Next(context.Background()) {\n\t\t\tvar idx index\n\t\t\terr = cursor.Decode(&idx)\n\t\t\tassert.Nil(mt, err, \"Decode error: %v (document %v)\", err, cursor.Current)\n\t\t\tassert.NotEqual(mt, indexNames[1], idx.Name, \"found index %v after dropping\", indexNames[1])\n\t\t}\n\t\tassert.Nil(mt, cursor.Err(), \"cursor error: %v\", cursor.Err())\n\t})\n\tmt.Run(\"drop with key\", func(mt *mtest.T) {\n\t\ttests := []struct {\n\t\t\tname   string\n\t\t\tmodels []mongo.IndexModel\n\t\t\tindex  any\n\t\t\twant   string\n\t\t}{\n\t\t\t{\n\t\t\t\tname: \"custom index name and unique indexes\",\n\t\t\t\tmodels: []mongo.IndexModel{\n\t\t\t\t\t{\n\t\t\t\t\t\tKeys:    bson.D{{\"username\", int32(1)}},\n\t\t\t\t\t\tOptions: options.Index().SetUnique(true).SetName(\"myidx\"),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tindex: bson.D{{\"username\", int32(1)}},\n\t\t\t\twant:  \"myidx\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"normal generated index name\",\n\t\t\t\tmodels: []mongo.IndexModel{\n\t\t\t\t\t{\n\t\t\t\t\t\tKeys: bson.D{{\"foo\", int32(-1)}},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tindex: bson.D{{\"foo\", int32(-1)}},\n\t\t\t\twant:  \"foo_-1\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"compound index\",\n\t\t\t\tmodels: []mongo.IndexModel{\n\t\t\t\t\t{\n\t\t\t\t\t\tKeys: bson.D{{\"foo\", int32(1)}, {\"bar\", int32(1)}},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tindex: bson.D{{\"foo\", int32(1)}, {\"bar\", int32(1)}},\n\t\t\t\twant:  \"foo_1_bar_1\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"text index\",\n\t\t\t\tmodels: []mongo.IndexModel{\n\t\t\t\t\t{\n\t\t\t\t\t\tKeys: bson.D{{\"plot1\", \"text\"}, {\"plot2\", \"text\"}},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t// Key is automatically set to Full Text Search for any text index\n\t\t\t\tindex: bson.D{{\"_fts\", \"text\"}, {\"_ftsx\", int32(1)}},\n\t\t\t\twant:  \"plot1_text_plot2_text\",\n\t\t\t},\n\t\t}\n\n\t\tfor _, test := range tests {\n\t\t\tmt.Run(test.name, func(mt *mtest.T) {\n\t\t\t\tiv := mt.Coll.Indexes()\n\t\t\t\tindexNames, err := iv.CreateMany(context.Background(), test.models)\n\n\t\t\t\ts, _ := test.index.(bson.D)\n\t\t\t\tfor _, name := range indexNames {\n\t\t\t\t\tverifyIndexExists(mt, iv, index{\n\t\t\t\t\t\tKey:  s,\n\t\t\t\t\t\tName: name,\n\t\t\t\t\t})\n\t\t\t\t}\n\n\t\t\t\tassert.NoError(mt, err)\n\t\t\t\tassert.Equal(mt, len(test.models), len(indexNames), \"expected %v index names, got %v\", len(test.models), len(indexNames))\n\n\t\t\t\terr = iv.DropWithKey(context.Background(), test.index)\n\t\t\t\tassert.Nil(mt, err, \"DropOne error: %v\", err)\n\n\t\t\t\tcursor, err := iv.List(context.Background())\n\t\t\t\tassert.Nil(mt, err, \"List error: %v\", err)\n\t\t\t\tfor cursor.Next(context.Background()) {\n\t\t\t\t\tvar idx index\n\t\t\t\t\terr = cursor.Decode(&idx)\n\t\t\t\t\tassert.Nil(mt, err, \"Decode error: %v (document %v)\", err, cursor.Current)\n\t\t\t\t\tassert.NotEqual(mt, test.want, idx.Name, \"found index %v after dropping\", test.want)\n\t\t\t\t}\n\t\t\t\tassert.Nil(mt, cursor.Err(), \"cursor error: %v\", cursor.Err())\n\t\t\t})\n\t\t}\n\t})\n\tmt.Run(\"drop all\", func(mt *mtest.T) {\n\t\tiv := mt.Coll.Indexes()\n\t\tnames, err := iv.CreateMany(context.Background(), []mongo.IndexModel{\n\t\t\t{\n\t\t\t\tKeys: bson.D{{\"foo\", -1}},\n\t\t\t},\n\t\t\t{\n\t\t\t\tKeys: bson.D{{\"bar\", 1}, {\"baz\", -1}},\n\t\t\t},\n\t\t})\n\t\tassert.Nil(mt, err, \"CreateMany error: %v\", err)\n\t\tassert.Equal(mt, 2, len(names), \"expected 2 index names, got %v\", len(names))\n\t\terr = iv.DropAll(context.Background())\n\t\tassert.Nil(mt, err, \"DropAll error: %v\", err)\n\n\t\tcursor, err := iv.List(context.Background())\n\t\tassert.Nil(mt, err, \"List error: %v\", err)\n\t\tfor cursor.Next(context.Background()) {\n\t\t\tvar idx index\n\t\t\terr = cursor.Decode(&idx)\n\t\t\tassert.Nil(mt, err, \"Decode error: %v (document %v)\", err, cursor.Current)\n\t\t\tassert.NotEqual(mt, names[0], idx.Name, \"found index %v, after dropping\", names[0])\n\t\t\tassert.NotEqual(mt, names[1], idx.Name, \"found index %v, after dropping\", names[1])\n\t\t}\n\t\tassert.Nil(mt, cursor.Err(), \"cursor error: %v\", cursor.Err())\n\t})\n\tmt.RunOpts(\"clustered indexes\", mtest.NewOptions().MinServerVersion(\"5.3\"), func(mt *mtest.T) {\n\t\tconst name = \"clustered\"\n\t\tclustered := mt.CreateCollection(mtest.Collection{\n\t\t\tName:       name,\n\t\t\tCreateOpts: options.CreateCollection().SetClusteredIndex(bson.D{{\"key\", bson.D{{\"_id\", 1}}}, {\"unique\", true}}),\n\t\t}, true)\n\t\tmt.Run(\"create one\", func(mt *mtest.T) {\n\t\t\t_, err := clustered.Indexes().CreateOne(context.Background(), mongo.IndexModel{\n\t\t\t\tKeys: bson.D{{\"foo\", int32(-1)}},\n\t\t\t})\n\t\t\tassert.Nil(mt, err, \"CreateOne error: %v\", err)\n\t\t\tspecs, err := clustered.Indexes().ListSpecifications(context.Background())\n\t\t\tassert.Nil(mt, err, \"ListSpecifications error: %v\", err)\n\t\t\texpectedSpecs := []mongo.IndexSpecification{\n\t\t\t\t{\n\t\t\t\t\tName:         \"_id_\",\n\t\t\t\t\tNamespace:    mt.DB.Name() + \".\" + name,\n\t\t\t\t\tKeysDocument: bson.Raw(bsoncore.NewDocumentBuilder().AppendInt32(\"_id\", 1).Build()),\n\t\t\t\t\tVersion:      2,\n\t\t\t\t\tUnique:       func(b bool) *bool { return &b }(true),\n\t\t\t\t\tClustered:    func(b bool) *bool { return &b }(true),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tName:         \"foo_-1\",\n\t\t\t\t\tNamespace:    mt.DB.Name() + \".\" + name,\n\t\t\t\t\tKeysDocument: bson.Raw(bsoncore.NewDocumentBuilder().AppendInt32(\"foo\", -1).Build()),\n\t\t\t\t\tVersion:      2,\n\t\t\t\t},\n\t\t\t}\n\t\t\tassert.True(mt, cmp.Equal(specs, expectedSpecs), \"expected specifications to match: %v\", cmp.Diff(specs, expectedSpecs))\n\t\t})\n\t})\n}\n\nfunc getIndexDoc(mt *mtest.T, iv mongo.IndexView, expectedKeyDoc bson.D) bson.D {\n\tc, err := iv.List(context.Background())\n\tassert.Nil(mt, err, \"List error: %v\", err)\n\n\tfor c.Next(context.Background()) {\n\t\tvar index bson.D\n\t\terr = c.Decode(&index)\n\t\tassert.Nil(mt, err, \"Decode error: %v\", err)\n\n\t\tfor _, elem := range index {\n\t\t\tif elem.Key != \"key\" {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tif cmp.Equal(expectedKeyDoc, elem.Value.(bson.D)) {\n\t\t\t\treturn index\n\t\t\t}\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc checkIndexDocContains(mt *mtest.T, indexDoc bson.D, expectedElem bson.E) {\n\tfor _, elem := range indexDoc {\n\t\tif elem.Key != expectedElem.Key {\n\t\t\tcontinue\n\t\t}\n\n\t\tassert.Equal(mt, expectedElem, elem, \"expected element %v, got %v\", expectedElem, elem)\n\t\treturn\n\t}\n\n\tmt.Fatalf(\"no element matching %v found\", expectedElem)\n}\n\nfunc verifyIndexExists(mt *mtest.T, iv mongo.IndexView, expected index) {\n\tmt.Helper()\n\n\tcursor, err := iv.List(context.Background())\n\tassert.Nil(mt, err, \"List error: %v\", err)\n\n\tvar found bool\n\tfor cursor.Next(context.Background()) {\n\t\tvar idx index\n\t\terr = cursor.Decode(&idx)\n\t\tassert.Nil(mt, err, \"Decode error: %v\", err)\n\n\t\tif idx.Name == expected.Name {\n\t\t\tif expected.Key != nil {\n\t\t\t\tassert.Equal(mt, expected.Key, idx.Key, \"key document mismatch; expected %v, got %v\", expected.Key, idx.Key)\n\t\t\t}\n\t\t\tfound = true\n\t\t}\n\t}\n\tassert.Nil(mt, cursor.Err(), \"cursor error: %v\", err)\n\tassert.True(mt, found, \"expected to find index %v but was not found\", expected.Name)\n}\n\nfunc createIndexes(mt *mtest.T, coll *mongo.Collection, numIndexes int) {\n\tmt.Helper()\n\n\tmodels := make([]mongo.IndexModel, 0, numIndexes)\n\tfor i, key := 0, 'a'; i < numIndexes; i, key = i+1, key+1 {\n\t\tmodels = append(models, mongo.IndexModel{\n\t\t\tKeys: bson.M{string(key): 1},\n\t\t})\n\t}\n\n\t_, err := coll.Indexes().CreateMany(context.Background(), models)\n\tassert.Nil(mt, err, \"CreateMany error: %v\", err)\n}\n"
  },
  {
    "path": "internal/integration/initial_dns_seedlist_discovery_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage integration\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"crypto/tls\"\n\t\"os\"\n\t\"path\"\n\t\"runtime\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/integration/mtest\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/serverselector\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/spectest\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/readpref\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/connstring\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/description\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/topology\"\n)\n\nvar seedlistDiscoveryTestsBaseDir = spectest.Path(\"initial-dns-seedlist-discovery/tests\")\n\ntype seedlistTest struct {\n\tURI      string   `bson:\"uri\"`\n\tSeeds    []string `bson:\"seeds\"`\n\tNumSeeds *int     `bson:\"numSeeds\"`\n\tHosts    []string `bson:\"hosts\"`\n\tNumHosts *int     `bson:\"numHosts\"`\n\tError    bool     `bson:\"error\"`\n\tOptions  bson.Raw `bson:\"options\"`\n\tPing     *bool    `bson:\"ping\"`\n}\n\nfunc TestInitialDNSSeedlistDiscoverySpec(t *testing.T) {\n\tmt := mtest.New(t, noClientOpts)\n\n\tmt.RunOpts(\"replica set\", mtest.NewOptions().Topologies(mtest.ReplicaSet).CreateClient(false), func(mt *mtest.T) {\n\t\tmt.Parallel()\n\n\t\trunSeedlistDiscoveryDirectory(mt, \"replica-set\")\n\t})\n\tmt.RunOpts(\"sharded\", mtest.NewOptions().Topologies(mtest.Sharded).CreateClient(false), func(mt *mtest.T) {\n\t\tmt.Parallel()\n\n\t\trunSeedlistDiscoveryDirectory(mt, \"sharded\")\n\t})\n\tmt.RunOpts(\"load balanced\", mtest.NewOptions().Topologies(mtest.LoadBalanced).CreateClient(false), func(mt *mtest.T) {\n\t\tmt.Parallel()\n\n\t\trunSeedlistDiscoveryDirectory(mt, \"load-balanced\")\n\t})\n}\n\nfunc runSeedlistDiscoveryDirectory(mt *mtest.T, subdirectory string) {\n\tdirectoryPath := path.Join(seedlistDiscoveryTestsBaseDir, subdirectory)\n\tfor _, file := range jsonFilesInDir(mt, directoryPath) {\n\t\tmt.RunOpts(file, noClientOpts, func(mt *mtest.T) {\n\t\t\trunSeedlistDiscoveryTest(mt, path.Join(directoryPath, file))\n\t\t})\n\t}\n}\n\n// runSeedlistDiscoveryPingTest will create a new connection using the test URI and attempt to \"ping\" the server.\nfunc runSeedlistDiscoveryPingTest(mt *mtest.T, clientOpts *options.ClientOptions) {\n\tctx := context.Background()\n\n\tclient, err := mongo.Connect(clientOpts)\n\tassert.Nil(mt, err, \"Connect error: %v\", err)\n\n\tdefer func() { _ = client.Disconnect(ctx) }()\n\n\t// Create a context with a timeout to prevent the ping operation from blocking indefinitely.\n\tpingCtx, cancel := context.WithTimeout(ctx, 1*time.Second)\n\tdefer cancel()\n\n\t// Ping the server.\n\terr = client.Ping(pingCtx, readpref.Nearest())\n\tassert.Nil(mt, err, \"Ping error: %v\", err)\n}\n\nfunc runSeedlistDiscoveryTest(mt *mtest.T, file string) {\n\tcontent, err := os.ReadFile(file)\n\tassert.Nil(mt, err, \"ReadFile error for %v: %v\", file, err)\n\n\tvar test seedlistTest\n\tvr, err := bson.NewExtJSONValueReader(bytes.NewReader(content), false)\n\tassert.Nil(mt, err, \"NewExtJSONValueReader error: %v\", err)\n\tdec := bson.NewDecoder(vr)\n\tdec.SetRegistry(specTestRegistry)\n\terr = dec.Decode(&test)\n\tassert.Nil(mt, err, \"decode error: %v\", err)\n\n\tif runtime.GOOS == \"windows\" && strings.HasSuffix(file, \"/two-txt-records.json\") {\n\t\tmt.Skip(\"skipping to avoid windows multiple TXT record lookup bug\")\n\t}\n\tif strings.HasPrefix(runtime.Version(), \"go1.11\") && strings.HasSuffix(file, \"/one-txt-record-multiple-strings.json\") {\n\t\tmt.Skip(\"skipping to avoid go1.11 problem with multiple strings in one TXT record\")\n\t}\n\n\tcs, err := connstring.ParseAndValidate(test.URI)\n\tif test.Error {\n\t\tassert.NotNil(mt, err, \"expected URI parsing error, got nil\")\n\t\treturn\n\t}\n\n\tassert.Nil(mt, err, \"ParseAndValidate error: %v\", err)\n\tassert.Equal(mt, connstring.SchemeMongoDBSRV, cs.Scheme,\n\t\t\"expected scheme %v, got %v\", connstring.SchemeMongoDBSRV, cs.Scheme)\n\n\t// DNS records may be out of order from the test file's ordering.\n\tactualSeedlist := buildSet(cs.Hosts)\n\t// If NumSeeds is set, check number of seeds in seedlist.\n\tif test.NumSeeds != nil {\n\t\tassert.Equal(mt, len(actualSeedlist), *test.NumSeeds,\n\t\t\t\"expected %v seeds, got %v\", *test.NumSeeds, len(actualSeedlist))\n\t}\n\n\t// If Seeds is set, check contents of seedlist.\n\tif test.Seeds != nil {\n\t\texpectedSeedlist := buildSet(test.Seeds)\n\t\tassert.Equal(mt, expectedSeedlist, actualSeedlist, \"expected seedlist %v, got %v\", expectedSeedlist, actualSeedlist)\n\t}\n\tverifyConnstringOptions(mt, test.Options, cs)\n\n\ttlsConfig := getSSLSettings(mt, test)\n\n\t// Make a topology from the options.\n\topts := options.Client().ApplyURI(test.URI)\n\tif tlsConfig != nil {\n\t\topts.SetTLSConfig(tlsConfig)\n\t}\n\n\tcfg, err := topology.NewConfig(opts, nil)\n\tassert.Nil(mt, err, \"error constructing toplogy config: %v\", err)\n\n\ttopo, err := topology.New(cfg)\n\tassert.Nil(mt, err, \"topology.New error: %v\", err)\n\n\terr = topo.Connect()\n\tassert.Nil(mt, err, \"topology.Connect error: %v\", err)\n\tdefer func() { _ = topo.Disconnect(context.Background()) }()\n\n\t// If NumHosts is set, check number of hosts currently stored on the Topology.\n\tif test.NumHosts != nil {\n\t\tactualNumHosts := len(topo.Description().Servers)\n\t\tassert.Equal(mt, *test.NumHosts, actualNumHosts, \"expected to find %v hosts, found %v\",\n\t\t\t*test.NumHosts, actualNumHosts)\n\t}\n\tfor _, host := range test.Hosts {\n\t\t_, err := getServerByAddress(host, topo)\n\t\tassert.Nil(mt, err, \"error finding host %q: %v\", host, err)\n\t}\n\n\tif ping := test.Ping; ping == nil || *ping {\n\t\trunSeedlistDiscoveryPingTest(mt, opts)\n\t}\n}\n\nfunc buildSet(list []string) map[string]struct{} {\n\tset := make(map[string]struct{})\n\tfor _, s := range list {\n\t\tset[s] = struct{}{}\n\t}\n\treturn set\n}\n\nfunc verifyConnstringOptions(mt *mtest.T, expected bson.Raw, cs *connstring.ConnString) {\n\tmt.Helper()\n\n\telems, _ := expected.Elements()\n\tfor _, elem := range elems {\n\t\tkey := elem.Key()\n\t\topt := elem.Value()\n\n\t\tswitch key {\n\t\tcase \"replicaSet\":\n\t\t\trs := opt.StringValue()\n\t\t\tassert.Equal(mt, rs, cs.ReplicaSet, \"expected replicaSet value %v, got %v\", rs, cs.ReplicaSet)\n\t\tcase \"ssl\":\n\t\t\tssl := opt.Boolean()\n\t\t\tassert.Equal(mt, ssl, cs.SSL, \"expected ssl value %v, got %v\", ssl, cs.SSL)\n\t\tcase \"authSource\":\n\t\t\tsource := opt.StringValue()\n\t\t\tassert.Equal(mt, source, cs.AuthSource, \"expected auth source value %v, got %v\", source, cs.AuthSource)\n\t\tcase \"directConnection\":\n\t\t\tdc := opt.Boolean()\n\t\t\tassert.True(mt, cs.DirectConnectionSet, \"expected cs.DirectConnectionSet to be true, got false\")\n\t\t\tassert.Equal(mt, dc, cs.DirectConnection, \"expected cs.DirectConnection to be %v, got %v\", dc, cs.DirectConnection)\n\t\tcase \"loadBalanced\":\n\t\t\tlb := opt.Boolean()\n\t\t\tassert.True(mt, cs.LoadBalancedSet, \"expected cs.LoadBalancedSet set to be true, got false\")\n\t\t\tassert.Equal(mt, lb, cs.LoadBalanced, \"expected cs.LoadBalanced to be %v, got %v\", lb, cs.LoadBalanced)\n\t\tcase \"srvMaxHosts\":\n\t\t\tsrvMaxHosts := opt.Int32()\n\t\t\tassert.Equal(mt, srvMaxHosts, int32(cs.SRVMaxHosts), \"expected cs.SRVMaxHosts to be %v, got %v\", srvMaxHosts, cs.SRVMaxHosts)\n\t\tcase \"srvServiceName\":\n\t\t\tsrvName := opt.StringValue()\n\t\t\tassert.Equal(mt, srvName, cs.SRVServiceName, \"expected cs.SRVServiceName to be %q, got %q\", srvName, cs.SRVServiceName)\n\t\tdefault:\n\t\t\tmt.Fatalf(\"unrecognized connstring option %v\", key)\n\t\t}\n\t}\n}\n\n// Because the Go driver tests can be run either against a server with SSL enabled or without, a\n// number of configurations have to be checked to ensure that the SRV tests are run properly.\n//\n// First, the \"ssl\" option in the JSON test description has to be checked. If this option is not\n// present, we assume that the test will assert an error, so we proceed with the test as normal.\n// If the option is false, then we skip the test if the server is running with SSL enabled.\n// If the option is true, then we skip the test if the server is running without SSL enabled; if\n// the server is running with SSL enabled, then we return a tls.Config with InsecureSkipVerify\n// set to true.\nfunc getSSLSettings(mt *mtest.T, test seedlistTest) *tls.Config {\n\tssl, err := test.Options.LookupErr(\"ssl\")\n\tif err != nil {\n\t\t// No \"ssl\" option is specified\n\t\treturn nil\n\t}\n\ttestCaseExpectsSSL := ssl.Boolean()\n\tenvSSL := os.Getenv(\"SSL\") == \"ssl\"\n\n\t// Skip non-SSL tests if the server is running with SSL.\n\tif !testCaseExpectsSSL && envSSL {\n\t\tmt.Skip(\"skipping test that does not expect ssl in an ssl environment\")\n\t}\n\n\t// Skip SSL tests if the server is running without SSL.\n\tif testCaseExpectsSSL && !envSSL {\n\t\tmt.Skip(\"skipping test that expects ssl in a non-ssl environment\")\n\t}\n\n\t// If SSL tests are running, set the CA file.\n\tif testCaseExpectsSSL && envSSL {\n\t\ttlsConfig := new(tls.Config)\n\t\ttlsConfig.InsecureSkipVerify = true\n\t\treturn tlsConfig\n\t}\n\treturn nil\n}\n\nfunc getServerByAddress(address string, topo *topology.Topology) (description.Server, error) {\n\tselectByName := serverselector.Func(\n\t\tfunc(_ description.Topology, servers []description.Server) ([]description.Server, error) {\n\t\t\tfor _, s := range servers {\n\t\t\t\tif s.Addr.String() == address {\n\t\t\t\t\treturn []description.Server{s}, nil\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn []description.Server{}, nil\n\t\t})\n\n\tselectedServer, err := topo.SelectServer(context.Background(), selectByName)\n\tif err != nil {\n\t\treturn description.Server{}, err\n\t}\n\n\t// If the selected server is a topology.SelectedServer, then we can get the description without creating a\n\t// connect pool.\n\ttopologySelectedServer, ok := selectedServer.(*topology.SelectedServer)\n\tif ok {\n\t\treturn topologySelectedServer.Description().Server, nil\n\t}\n\n\tselectedServerConnection, err := selectedServer.Connection(context.Background())\n\tif err != nil {\n\t\treturn description.Server{}, err\n\t}\n\tdefer selectedServerConnection.Close()\n\treturn selectedServerConnection.Description(), nil\n}\n"
  },
  {
    "path": "internal/integration/json_helpers_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage integration\n\nimport (\n\t\"crypto/tls\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io/ioutil\"\n\t\"math\"\n\t\"os\"\n\t\"path\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/readconcern\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/readpref\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/writeconcern\"\n)\n\nvar (\n\tawsAccessKeyID                  = os.Getenv(\"FLE_AWS_KEY\")\n\tawsSecretAccessKey              = os.Getenv(\"FLE_AWS_SECRET\")\n\tawsTempAccessKeyID              = os.Getenv(\"CSFLE_AWS_TEMP_ACCESS_KEY_ID\")\n\tawsTempSecretAccessKey          = os.Getenv(\"CSFLE_AWS_TEMP_SECRET_ACCESS_KEY\")\n\tawsTempSessionToken             = os.Getenv(\"CSFLE_AWS_TEMP_SESSION_TOKEN\")\n\tazureTenantID                   = os.Getenv(\"FLE_AZURE_TENANTID\")\n\tazureClientID                   = os.Getenv(\"FLE_AZURE_CLIENTID\")\n\tazureClientSecret               = os.Getenv(\"FLE_AZURE_CLIENTSECRET\")\n\tgcpEmail                        = os.Getenv(\"FLE_GCP_EMAIL\")\n\tgcpPrivateKey                   = os.Getenv(\"FLE_GCP_PRIVATEKEY\")\n\ttlsCAFileKMIP                   = os.Getenv(\"CSFLE_TLS_CA_FILE\")\n\ttlsClientCertificateKeyFileKMIP = os.Getenv(\"CSFLE_TLS_CLIENT_CERT_FILE\")\n)\n\n// Helper functions to do read JSON spec test files and convert JSON objects into the appropriate driver types.\n// Functions in this file should take testing.TB rather than testing.T/mtest.T for generality because they\n// do not do any database communication.\n\n// generate a slice of all JSON file names in a directory\nfunc jsonFilesInDir(t testing.TB, dir string) []string {\n\tt.Helper()\n\n\tfiles := make([]string, 0)\n\n\tentries, err := ioutil.ReadDir(dir)\n\tassert.Nil(t, err, \"unable to read json file: %v\", err)\n\n\tfor _, entry := range entries {\n\t\tif entry.IsDir() || path.Ext(entry.Name()) != \".json\" {\n\t\t\tcontinue\n\t\t}\n\n\t\tfiles = append(files, entry.Name())\n\t}\n\n\treturn files\n}\n\n// create client options from a map\nfunc createClientOptions(t testing.TB, opts bson.Raw) *options.ClientOptions {\n\tt.Helper()\n\n\tclientOpts := options.Client()\n\telems, _ := opts.Elements()\n\tfor _, elem := range elems {\n\t\tname := elem.Key()\n\t\topt := elem.Value()\n\n\t\tswitch name {\n\t\tcase \"retryWrites\":\n\t\t\tclientOpts.SetRetryWrites(opt.Boolean())\n\t\tcase \"w\":\n\t\t\tswitch opt.Type {\n\t\t\tcase bson.TypeInt32:\n\t\t\t\tw := int(opt.Int32())\n\t\t\t\tclientOpts.SetWriteConcern(&writeconcern.WriteConcern{W: w})\n\t\t\tcase bson.TypeDouble:\n\t\t\t\tw := int(opt.Double())\n\t\t\t\tclientOpts.SetWriteConcern(&writeconcern.WriteConcern{W: w})\n\t\t\tcase bson.TypeString:\n\t\t\t\tclientOpts.SetWriteConcern(writeconcern.Majority())\n\t\t\tdefault:\n\t\t\t\tt.Fatalf(\"unrecognized type for w client option: %v\", opt.Type)\n\t\t\t}\n\t\tcase \"readConcernLevel\":\n\t\t\tclientOpts.SetReadConcern(&readconcern.ReadConcern{Level: opt.StringValue()})\n\t\tcase \"readPreference\":\n\t\t\tclientOpts.SetReadPreference(readPrefFromString(opt.StringValue()))\n\t\tcase \"heartbeatFrequencyMS\":\n\t\t\thf := convertValueToMilliseconds(t, opt)\n\t\t\tclientOpts.SetHeartbeatInterval(hf)\n\t\tcase \"retryReads\":\n\t\t\tclientOpts.SetRetryReads(opt.Boolean())\n\t\tcase \"autoEncryptOpts\":\n\t\t\tclientOpts.SetAutoEncryptionOptions(createAutoEncryptionOptions(t, opt.Document()))\n\t\tcase \"appname\":\n\t\t\tclientOpts.SetAppName(opt.StringValue())\n\t\tcase \"connectTimeoutMS\":\n\t\t\tct := convertValueToMilliseconds(t, opt)\n\t\t\tclientOpts.SetConnectTimeout(ct)\n\t\tcase \"serverSelectionTimeoutMS\":\n\t\t\tsst := convertValueToMilliseconds(t, opt)\n\t\t\tclientOpts.SetServerSelectionTimeout(sst)\n\t\tcase \"minPoolSize\":\n\t\t\tclientOpts.SetMinPoolSize(uint64(opt.AsInt64()))\n\t\tcase \"maxPoolSize\":\n\t\t\tclientOpts.SetMaxPoolSize(uint64(opt.AsInt64()))\n\t\tcase \"directConnection\":\n\t\t\tclientOpts.SetDirect(opt.Boolean())\n\t\tdefault:\n\t\t\tt.Fatalf(\"unrecognized client option: %v\", name)\n\t\t}\n\t}\n\n\treturn clientOpts\n}\n\nfunc createAutoEncryptionOptions(t testing.TB, opts bson.Raw) *options.AutoEncryptionOptions {\n\tt.Helper()\n\n\taeo := options.AutoEncryption()\n\tvar kvnsFound bool\n\telems, _ := opts.Elements()\n\n\tfor _, elem := range elems {\n\t\tname := elem.Key()\n\t\topt := elem.Value()\n\n\t\tswitch name {\n\t\tcase \"kmsProviders\":\n\t\t\ttlsConfigs := createTLSOptsMap(t, opt.Document())\n\t\t\taeo.SetKmsProviders(createKmsProvidersMap(t, opt.Document())).SetTLSConfig(tlsConfigs)\n\t\tcase \"schemaMap\":\n\t\t\tvar schemaMap map[string]any\n\t\t\terr := bson.Unmarshal(opt.Document(), &schemaMap)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"error creating schema map: %v\", err)\n\t\t\t}\n\n\t\t\taeo.SetSchemaMap(schemaMap)\n\t\tcase \"keyVaultNamespace\":\n\t\t\tkvnsFound = true\n\t\t\taeo.SetKeyVaultNamespace(opt.StringValue())\n\t\tcase \"bypassAutoEncryption\":\n\t\t\taeo.SetBypassAutoEncryption(opt.Boolean())\n\t\tcase \"encryptedFieldsMap\":\n\t\t\tvar encryptedFieldsMap map[string]any\n\t\t\terr := bson.Unmarshal(opt.Document(), &encryptedFieldsMap)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"error creating encryptedFieldsMap: %v\", err)\n\t\t\t}\n\t\t\taeo.SetEncryptedFieldsMap(encryptedFieldsMap)\n\t\tcase \"bypassQueryAnalysis\":\n\t\t\taeo.SetBypassQueryAnalysis(opt.Boolean())\n\t\tcase \"keyExpirationMS\":\n\t\t\taeo.SetKeyExpiration(time.Duration(opt.Int32()) * time.Millisecond)\n\t\tdefault:\n\t\t\tt.Fatalf(\"unrecognized auto encryption option: %v\", name)\n\t\t}\n\t}\n\tif !kvnsFound {\n\t\taeo.SetKeyVaultNamespace(\"keyvault.datakeys\")\n\t}\n\n\treturn aeo\n}\n\nfunc createTLSOptsMap(t testing.TB, opts bson.Raw) map[string]*tls.Config {\n\tt.Helper()\n\n\ttlsMap := make(map[string]*tls.Config)\n\telems, _ := opts.Elements()\n\n\tfor _, elem := range elems {\n\t\tprovider := elem.Key()\n\n\t\tif provider == \"kmip\" {\n\t\t\ttlsOptsMap := map[string]any{\n\t\t\t\t\"tlsCertificateKeyFile\": tlsClientCertificateKeyFileKMIP,\n\t\t\t\t\"tlsCAFile\":             tlsCAFileKMIP,\n\t\t\t}\n\n\t\t\tcfg, err := options.BuildTLSConfig(tlsOptsMap)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"error building TLS config map: %v\", err)\n\t\t\t}\n\n\t\t\ttlsMap[\"kmip\"] = cfg\n\t\t}\n\t}\n\treturn tlsMap\n}\n\nfunc createKmsProvidersMap(t testing.TB, opts bson.Raw) map[string]map[string]any {\n\tt.Helper()\n\n\tkmsMap := make(map[string]map[string]any)\n\telems, _ := opts.Elements()\n\n\tfor _, elem := range elems {\n\t\tprovider := elem.Key()\n\t\tproviderOpt := elem.Value()\n\n\t\tswitch provider {\n\t\tcase \"aws\":\n\t\t\tawsMap := map[string]any{\n\t\t\t\t\"accessKeyId\":     awsAccessKeyID,\n\t\t\t\t\"secretAccessKey\": awsSecretAccessKey,\n\t\t\t}\n\t\t\tkmsMap[\"aws\"] = awsMap\n\t\tcase \"azure\":\n\t\t\tkmsMap[\"azure\"] = map[string]any{\n\t\t\t\t\"tenantId\":     azureTenantID,\n\t\t\t\t\"clientId\":     azureClientID,\n\t\t\t\t\"clientSecret\": azureClientSecret,\n\t\t\t}\n\t\tcase \"gcp\":\n\t\t\tkmsMap[\"gcp\"] = map[string]any{\n\t\t\t\t\"email\":      gcpEmail,\n\t\t\t\t\"privateKey\": gcpPrivateKey,\n\t\t\t}\n\t\tcase \"local\":\n\t\t\t_, key := providerOpt.Document().Lookup(\"key\").Binary()\n\t\t\tlocalMap := map[string]any{\n\t\t\t\t\"key\": key,\n\t\t\t}\n\t\t\tkmsMap[\"local\"] = localMap\n\t\tcase \"awsTemporary\":\n\t\t\tif awsTempAccessKeyID == \"\" {\n\t\t\t\tt.Fatal(\"AWS temp access key ID not set\")\n\t\t\t}\n\t\t\tif awsTempSecretAccessKey == \"\" {\n\t\t\t\tt.Fatal(\"AWS temp secret access key not set\")\n\t\t\t}\n\t\t\tif awsTempSessionToken == \"\" {\n\t\t\t\tt.Fatal(\"AWS temp session token not set\")\n\t\t\t}\n\t\t\tawsMap := map[string]any{\n\t\t\t\t\"accessKeyId\":     awsTempAccessKeyID,\n\t\t\t\t\"secretAccessKey\": awsTempSecretAccessKey,\n\t\t\t\t\"sessionToken\":    awsTempSessionToken,\n\t\t\t}\n\t\t\tkmsMap[\"aws\"] = awsMap\n\t\tcase \"awsTemporaryNoSessionToken\":\n\t\t\tif awsTempAccessKeyID == \"\" {\n\t\t\t\tt.Fatal(\"AWS temp access key ID not set\")\n\t\t\t}\n\t\t\tif awsTempSecretAccessKey == \"\" {\n\t\t\t\tt.Fatal(\"AWS temp secret access key not set\")\n\t\t\t}\n\t\t\tawsMap := map[string]any{\n\t\t\t\t\"accessKeyId\":     awsTempAccessKeyID,\n\t\t\t\t\"secretAccessKey\": awsTempSecretAccessKey,\n\t\t\t}\n\t\t\tkmsMap[\"aws\"] = awsMap\n\t\tcase \"kmip\":\n\t\t\tkmipMap := map[string]any{\n\t\t\t\t\"endpoint\": \"localhost:5698\",\n\t\t\t}\n\t\t\tkmsMap[\"kmip\"] = kmipMap\n\t\tdefault:\n\t\t\tt.Fatalf(\"unrecognized KMS provider: %v\", provider)\n\t\t}\n\t}\n\n\treturn kmsMap\n}\n\n// create session options from a map\nfunc createSessionOptions(t testing.TB, opts bson.Raw) *options.SessionOptionsBuilder {\n\tt.Helper()\n\n\tsessOpts := options.Session()\n\telems, _ := opts.Elements()\n\tfor _, elem := range elems {\n\t\tname := elem.Key()\n\t\topt := elem.Value()\n\n\t\tswitch name {\n\t\tcase \"causalConsistency\":\n\t\t\tsessOpts = sessOpts.SetCausalConsistency(opt.Boolean())\n\t\tcase \"defaultTransactionOptions\":\n\t\t\tsessOpts.SetDefaultTransactionOptions(createTransactionOptions(t, opt.Document()))\n\t\tdefault:\n\t\t\tt.Fatalf(\"unrecognized session option: %v\", name)\n\t\t}\n\t}\n\n\treturn sessOpts\n}\n\n// create database options from a BSON document.\nfunc createDatabaseOptions(t testing.TB, opts bson.Raw) *options.DatabaseOptionsBuilder {\n\tt.Helper()\n\n\tdo := options.Database()\n\telems, _ := opts.Elements()\n\tfor _, elem := range elems {\n\t\tname := elem.Key()\n\t\topt := elem.Value()\n\n\t\tswitch name {\n\t\tcase \"readConcern\":\n\t\t\tdo.SetReadConcern(createReadConcern(opt))\n\t\tcase \"writeConcern\":\n\t\t\tdo.SetWriteConcern(createWriteConcern(t, opt))\n\t\tdefault:\n\t\t\tt.Fatalf(\"unrecognized database option: %v\", name)\n\t\t}\n\t}\n\n\treturn do\n}\n\n// create collection options from a map\nfunc createCollectionOptions(t testing.TB, opts bson.Raw) *options.CollectionOptionsBuilder {\n\tt.Helper()\n\n\tco := options.Collection()\n\telems, _ := opts.Elements()\n\tfor _, elem := range elems {\n\t\tname := elem.Key()\n\t\topt := elem.Value()\n\n\t\tswitch name {\n\t\tcase \"readConcern\":\n\t\t\tco.SetReadConcern(createReadConcern(opt))\n\t\tcase \"writeConcern\":\n\t\t\tco.SetWriteConcern(createWriteConcern(t, opt))\n\t\tcase \"readPreference\":\n\t\t\tco.SetReadPreference(createReadPref(opt))\n\t\tdefault:\n\t\t\tt.Fatalf(\"unrecognized collection option: %v\", name)\n\t\t}\n\t}\n\n\treturn co\n}\n\n// create transaction options from a map\nfunc createTransactionOptions(t testing.TB, opts bson.Raw) *options.TransactionOptionsBuilder {\n\tt.Helper()\n\n\ttxnOpts := options.Transaction()\n\telems, _ := opts.Elements()\n\tfor _, elem := range elems {\n\t\tname := elem.Key()\n\t\topt := elem.Value()\n\n\t\tswitch name {\n\t\tcase \"writeConcern\":\n\t\t\ttxnOpts.SetWriteConcern(createWriteConcern(t, opt))\n\t\tcase \"readPreference\":\n\t\t\ttxnOpts.SetReadPreference(createReadPref(opt))\n\t\tcase \"readConcern\":\n\t\t\ttxnOpts.SetReadConcern(createReadConcern(opt))\n\t\tcase \"maxCommitTimeMS\":\n\t\t\tt.Skip(\"GODRIVER-2348: maxCommitTimeMS is deprecated\")\n\t\tdefault:\n\t\t\tt.Fatalf(\"unrecognized transaction option: %v\", opt)\n\t\t}\n\t}\n\treturn txnOpts\n}\n\n// create a read concern from a map\nfunc createReadConcern(opt bson.RawValue) *readconcern.ReadConcern {\n\treturn &readconcern.ReadConcern{Level: opt.Document().Lookup(\"level\").StringValue()}\n}\n\n// create a read concern from a map\nfunc createWriteConcern(t testing.TB, opt bson.RawValue) *writeconcern.WriteConcern {\n\twcDoc, ok := opt.DocumentOK()\n\tif !ok {\n\t\treturn nil\n\t}\n\n\twc := &writeconcern.WriteConcern{}\n\telems, _ := wcDoc.Elements()\n\tfor _, elem := range elems {\n\t\tkey := elem.Key()\n\t\tval := elem.Value()\n\n\t\tswitch key {\n\t\tcase \"j\":\n\t\t\tj := val.Boolean()\n\t\t\twc.Journal = &j\n\t\tcase \"w\":\n\t\t\tswitch val.Type {\n\t\t\tcase bson.TypeString:\n\t\t\t\tif val.StringValue() != \"majority\" {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\twc.W = \"majority\"\n\t\t\tcase bson.TypeInt32:\n\t\t\t\tw := int(val.Int32())\n\t\t\t\twc.W = w\n\t\t\tdefault:\n\t\t\t\tt.Fatalf(\"unrecognized type for w: %v\", val.Type)\n\t\t\t}\n\t\tdefault:\n\t\t\tt.Fatalf(\"unrecognized write concern option: %v\", key)\n\t\t}\n\t}\n\treturn wc\n}\n\n// create a read preference from a string.\n// returns readpref.Primary() if the string doesn't match any known read preference modes.\nfunc readPrefFromString(s string) *readpref.ReadPref {\n\tswitch strings.ToLower(s) {\n\tcase \"primary\":\n\t\treturn readpref.Primary()\n\tcase \"primarypreferred\":\n\t\treturn readpref.PrimaryPreferred()\n\tcase \"secondary\":\n\t\treturn readpref.Secondary()\n\tcase \"secondarypreferred\":\n\t\treturn readpref.SecondaryPreferred()\n\tcase \"nearest\":\n\t\treturn readpref.Nearest()\n\t}\n\treturn readpref.Primary()\n}\n\n// create a read preference from a map.\nfunc createReadPref(opt bson.RawValue) *readpref.ReadPref {\n\tmode := opt.Document().Lookup(\"mode\").StringValue()\n\treturn readPrefFromString(mode)\n}\n\n// retrieve the error associated with a result.\nfunc errorFromResult(t testing.TB, result any) *operationError {\n\tt.Helper()\n\n\t// embedded doc will be unmarshalled as Raw\n\traw, ok := result.(bson.Raw)\n\tif !ok {\n\t\treturn nil\n\t}\n\n\tvar expected operationError\n\terr := bson.Unmarshal(raw, &expected)\n\tif err != nil {\n\t\treturn nil\n\t}\n\tif expected.ErrorCodeName == nil && expected.ErrorContains == nil && len(expected.ErrorLabelsOmit) == 0 &&\n\t\tlen(expected.ErrorLabelsContain) == 0 {\n\t\treturn nil\n\t}\n\n\treturn &expected\n}\n\n// errorDetails is a helper type that holds information that can be returned by driver functions in different error\n// types.\ntype errorDetails struct {\n\tname   string\n\tlabels []string\n}\n\n// extractErrorDetails creates an errorDetails instance based on the provided error. It returns the details and an \"ok\"\n// value which is true if the provided error is of a known type that can be processed.\nfunc extractErrorDetails(err error) (errorDetails, bool) {\n\tvar details errorDetails\n\n\tswitch converted := err.(type) {\n\tcase mongo.CommandError:\n\t\tdetails.name = converted.Name\n\t\tdetails.labels = converted.Labels\n\tcase mongo.WriteException:\n\t\tif converted.WriteConcernError != nil {\n\t\t\tdetails.name = converted.WriteConcernError.Name\n\t\t}\n\t\tdetails.labels = converted.Labels\n\tcase mongo.BulkWriteException:\n\t\tif converted.WriteConcernError != nil {\n\t\t\tdetails.name = converted.WriteConcernError.Name\n\t\t}\n\t\tdetails.labels = converted.Labels\n\tdefault:\n\t\treturn errorDetails{}, false\n\t}\n\n\treturn details, true\n}\n\n// verify that an error returned by an operation matches the expected error.\nfunc verifyError(expected *operationError, actual error) error {\n\t// The spec test format doesn't treat ErrNoDocuments as an errors, so set actual to nil\n\t// to indicate that no error occurred.\n\tif errors.Is(actual, mongo.ErrNoDocuments) {\n\t\tactual = nil\n\t}\n\n\tif expected == nil && actual != nil {\n\t\treturn fmt.Errorf(\"did not expect error but got %w\", actual)\n\t}\n\tif expected != nil && actual == nil {\n\t\treturn fmt.Errorf(\"expected error but got nil\")\n\t}\n\tif expected == nil {\n\t\treturn nil\n\t}\n\n\t// check ErrorContains for all error types\n\tif expected.ErrorContains != nil {\n\t\temsg := strings.ToLower(*expected.ErrorContains)\n\t\tamsg := strings.ToLower(actual.Error())\n\t\tif !strings.Contains(amsg, emsg) {\n\t\t\treturn fmt.Errorf(\"expected error message %q to contain %q\", amsg, emsg)\n\t\t}\n\t}\n\n\t// Get an errorDetails instance for the error. If this fails but the test has expectations about the error name or\n\t// labels, fail because we can't verify them.\n\tdetails, ok := extractErrorDetails(actual)\n\tif !ok {\n\t\tif expected.ErrorCodeName != nil || len(expected.ErrorLabelsContain) > 0 || len(expected.ErrorLabelsOmit) > 0 {\n\t\t\treturn fmt.Errorf(\"failed to extract details from error %v of type %T\", actual, actual)\n\t\t}\n\t\treturn nil\n\t}\n\n\tif expected.ErrorCodeName != nil {\n\t\tif *expected.ErrorCodeName != details.name {\n\t\t\treturn fmt.Errorf(\"expected error name %v, got %v\", *expected.ErrorCodeName, details.name)\n\t\t}\n\t}\n\tfor _, label := range expected.ErrorLabelsContain {\n\t\tif !stringSliceContains(details.labels, label) {\n\t\t\treturn fmt.Errorf(\"expected error %w to contain label %q\", actual, label)\n\t\t}\n\t}\n\tfor _, label := range expected.ErrorLabelsOmit {\n\t\tif stringSliceContains(details.labels, label) {\n\t\t\treturn fmt.Errorf(\"expected error %w to not contain label %q\", actual, label)\n\t\t}\n\t}\n\treturn nil\n}\n\n// get the underlying value of i as an int64. returns nil if i is not an int, int32, or int64 type.\nfunc getIntFromInterface(i any) *int64 {\n\tvar out int64\n\n\tswitch v := i.(type) {\n\tcase int:\n\t\tout = int64(v)\n\tcase int32:\n\t\tout = int64(v)\n\tcase int64:\n\t\tout = v\n\tcase float32:\n\t\tf := float64(v)\n\t\tif math.Floor(f) != f || f > float64(math.MaxInt64) {\n\t\t\tbreak\n\t\t}\n\n\t\tout = int64(f)\n\tcase float64:\n\t\tif math.Floor(v) != v || v > float64(math.MaxInt64) {\n\t\t\tbreak\n\t\t}\n\n\t\tout = int64(v)\n\tdefault:\n\t\treturn nil\n\t}\n\n\treturn &out\n}\n\nfunc createCollation(t testing.TB, m bson.Raw) *options.Collation {\n\tvar collation options.Collation\n\telems, _ := m.Elements()\n\n\tfor _, elem := range elems {\n\t\tswitch elem.Key() {\n\t\tcase \"locale\":\n\t\t\tcollation.Locale = elem.Value().StringValue()\n\t\tcase \"caseLevel\":\n\t\t\tcollation.CaseLevel = elem.Value().Boolean()\n\t\tcase \"caseFirst\":\n\t\t\tcollation.CaseFirst = elem.Value().StringValue()\n\t\tcase \"strength\":\n\t\t\tcollation.Strength = int(elem.Value().Int32())\n\t\tcase \"numericOrdering\":\n\t\t\tcollation.NumericOrdering = elem.Value().Boolean()\n\t\tcase \"alternate\":\n\t\t\tcollation.Alternate = elem.Value().StringValue()\n\t\tcase \"maxVariable\":\n\t\t\tcollation.MaxVariable = elem.Value().StringValue()\n\t\tcase \"normalization\":\n\t\t\tcollation.Normalization = elem.Value().Boolean()\n\t\tcase \"backwards\":\n\t\t\tcollation.Backwards = elem.Value().Boolean()\n\t\tdefault:\n\t\t\tt.Fatalf(\"unrecognized collation option: %v\", elem.Key())\n\t\t}\n\t}\n\treturn &collation\n}\n\nfunc convertValueToMilliseconds(t testing.TB, val bson.RawValue) time.Duration {\n\tt.Helper()\n\n\tint32Val, ok := val.Int32OK()\n\tif !ok {\n\t\tt.Fatalf(\"failed to convert value of type %s to int32\", val.Type)\n\t}\n\treturn time.Duration(int32Val) * time.Millisecond\n}\n\nfunc stringSliceContains(stringSlice []string, target string) bool {\n\tfor _, str := range stringSlice {\n\t\tif str == target {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n"
  },
  {
    "path": "internal/integration/load_balancer_prose_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage integration\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/integration/mtest\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/require\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n)\n\nfunc TestLoadBalancerSupport(t *testing.T) {\n\tmt := mtest.New(t, mtest.NewOptions().Topologies(mtest.LoadBalanced).CreateClient(false))\n\n\tmt.Run(\"RunCommandCursor pins to a connection\", func(mt *mtest.T) {\n\t\t// The LB spec tests cover the behavior for cursors created by CRUD operations, but RunCommandCursor is\n\t\t// Go-specific so there is no spec test coverage for it.\n\n\t\tinitCollection(mt, mt.Coll)\n\t\tfindCmd := bson.D{\n\t\t\t{\"find\", mt.Coll.Name()},\n\t\t\t{\"filter\", bson.D{}},\n\t\t\t{\"batchSize\", 2},\n\t\t}\n\t\tcursor, err := mt.DB.RunCommandCursor(context.Background(), findCmd)\n\t\tassert.Nil(mt, err, \"RunCommandCursor error: %v\", err)\n\t\tdefer func() {\n\t\t\t_ = cursor.Close(context.Background())\n\t\t}()\n\n\t\tassert.True(mt, cursor.ID() > 0, \"expected cursor ID to be non-zero\")\n\t\tassert.Equal(mt, 1, mt.NumberConnectionsCheckedOut(),\n\t\t\t\"expected one connection to be checked out, got %d\", mt.NumberConnectionsCheckedOut())\n\t})\n\n\tmt.RunOpts(\"wait queue timeout errors include extra information\", noClientOpts, func(mt *mtest.T) {\n\t\t// There are spec tests to assert this behavior, but they rely on the waitQueueTimeoutMS Client option, which is\n\t\t// not supported in Go, so we have to skip them. These prose tests make the same assertions, but use context\n\t\t// deadlines to force wait queue timeout errors.\n\n\t\tassertErrorHasInfo := func(mt *mtest.T, err error, numCursorConns, numTxnConns, numOtherConns int) {\n\t\t\tmt.Helper()\n\n\t\t\tassert.NotNil(mt, err, \"expected wait queue timeout error, got nil\")\n\t\t\texpectedMsg := fmt.Sprintf(\"maxPoolSize: 1, \"+\n\t\t\t\t\"connections in use by cursors: %d, \"+\n\t\t\t\t\"connections in use by transactions: %d, \"+\n\t\t\t\t\"connections in use by other operations: %d\",\n\t\t\t\tnumCursorConns, numTxnConns, numOtherConns,\n\t\t\t)\n\t\t\tassert.True(mt, strings.Contains(err.Error(), expectedMsg),\n\t\t\t\t\"expected error %q to contain substring %q\", err, expectedMsg)\n\t\t}\n\t\tmaxPoolSizeMtOpts := mtest.NewOptions().\n\t\t\tClientOptions(options.Client().SetMaxPoolSize(1))\n\n\t\tmt.RunOpts(\"cursors\", maxPoolSizeMtOpts, func(mt *mtest.T) {\n\t\t\tinitCollection(mt, mt.Coll)\n\t\t\tfindOpts := options.Find().SetBatchSize(2)\n\t\t\tcursor, err := mt.Coll.Find(context.Background(), bson.M{}, findOpts)\n\t\t\tassert.Nil(mt, err, \"Find error: %v\", err)\n\t\t\tdefer func() {\n\t\t\t\t_ = cursor.Close(context.Background())\n\t\t\t}()\n\n\t\t\tctx, cancel := context.WithTimeout(context.Background(), 5*time.Millisecond)\n\t\t\tdefer cancel()\n\t\t\t_, err = mt.Coll.InsertOne(ctx, bson.M{\"x\": 1})\n\t\t\tassertErrorHasInfo(mt, err, 1, 0, 0)\n\t\t})\n\t\tmt.RunOpts(\"transactions\", maxPoolSizeMtOpts, func(mt *mtest.T) {\n\t\t\t{\n\t\t\t\tsess, err := mt.Client.StartSession()\n\t\t\t\tassert.Nil(mt, err, \"StartSession error: %v\", err)\n\t\t\t\tdefer sess.EndSession(context.Background())\n\t\t\t\tctx := mongo.NewSessionContext(context.Background(), sess)\n\n\t\t\t\t// Start a transaction and perform one transactional operation to pin a connection.\n\t\t\t\terr = sess.StartTransaction()\n\t\t\t\tassert.Nil(mt, err, \"StartTransaction error: %v\", err)\n\t\t\t\t_, err = mt.Coll.InsertOne(ctx, bson.M{\"x\": 1})\n\t\t\t\tassert.Nil(mt, err, \"InsertOne error: %v\", err)\n\t\t\t}\n\n\t\t\tctx, cancel := context.WithTimeout(context.Background(), 5*time.Millisecond)\n\t\t\tdefer cancel()\n\t\t\t_, err := mt.Coll.InsertOne(ctx, bson.M{\"x\": 1})\n\t\t\tassertErrorHasInfo(mt, err, 0, 1, 0)\n\t\t})\n\n\t\t// GODRIVER-2867: Test that connections are unpinned from transactions\n\t\t// when the transaction session is ended. Create a Client with\n\t\t// maxPoolSize=1 and expect that it can start and commit 5 transactions\n\t\t// with that 1 connection.\n\t\tmt.RunOpts(\"transaction connections are unpinned\", maxPoolSizeMtOpts, func(mt *mtest.T) {\n\t\t\t{\n\t\t\t\tctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute)\n\t\t\t\tdefer cancel()\n\n\t\t\t\tfor i := 0; i < 5; i++ {\n\t\t\t\t\tsess, err := mt.Client.StartSession()\n\t\t\t\t\trequire.NoError(mt, err, \"StartSession error\")\n\n\t\t\t\t\terr = sess.StartTransaction()\n\t\t\t\t\trequire.NoError(mt, err, \"StartTransaction error\")\n\n\t\t\t\t\tctx := mongo.NewSessionContext(ctx, sess)\n\t\t\t\t\t_, err = mt.Coll.InsertOne(ctx, bson.M{\"x\": 1})\n\t\t\t\t\tassert.NoError(mt, err, \"InsertOne error\")\n\n\t\t\t\t\terr = sess.CommitTransaction(ctx)\n\t\t\t\t\tassert.NoError(mt, err, \"CommitTransaction error\")\n\n\t\t\t\t\tsess.EndSession(ctx)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t})\n}\n"
  },
  {
    "path": "internal/integration/log_helpers_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2023-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage integration\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/integration/mtest\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/logger\"\n)\n\ntype testLogSink struct {\n\tlogs       chan func() (int, string, []any)\n\tbufferSize int\n\tlogsCount  int\n\terrsCh     chan error\n}\n\ntype logValidator func(order int, lvl int, msg string, kv ...any) error\n\nfunc newTestLogSink(ctx context.Context, mt *mtest.T, bufferSize int, validator logValidator) *testLogSink {\n\tmt.Helper()\n\n\tsink := &testLogSink{\n\t\tlogs:       make(chan func() (int, string, []any), bufferSize),\n\t\terrsCh:     make(chan error, bufferSize),\n\t\tbufferSize: bufferSize,\n\t}\n\n\tgo func() {\n\t\torder := 0\n\t\tfor log := range sink.logs {\n\t\t\tselect {\n\t\t\tcase <-ctx.Done():\n\t\t\t\tsink.errsCh <- ctx.Err()\n\n\t\t\t\treturn\n\t\t\tdefault:\n\t\t\t}\n\n\t\t\tlevel, msg, args := log()\n\t\t\tif err := validator(order, level, msg, args...); err != nil {\n\t\t\t\tsink.errsCh <- fmt.Errorf(\"invalid log at position %d, level %d, and msg %q: %w\", order,\n\t\t\t\t\tlevel, msg, err)\n\t\t\t}\n\n\t\t\torder++\n\t\t}\n\n\t\tclose(sink.errsCh)\n\t}()\n\n\treturn sink\n}\n\nfunc (sink *testLogSink) Info(level int, msg string, keysAndValues ...any) {\n\tsink.logs <- func() (int, string, []any) {\n\t\treturn level, msg, keysAndValues\n\t}\n\n\tif sink.logsCount++; sink.logsCount == sink.bufferSize {\n\t\tclose(sink.logs)\n\t}\n}\n\nfunc (sink *testLogSink) Error(err error, msg string, keysAndValues ...any) {\n\tkeysAndValues = append(keysAndValues, \"error\", err)\n\tsink.Info(int(logger.LevelInfo), msg, keysAndValues)\n}\n\nfunc (sink *testLogSink) errs() <-chan error {\n\treturn sink.errsCh\n}\n\nfunc findLogValue(mt *mtest.T, key string, values ...any) any {\n\tmt.Helper()\n\n\tfor i := 0; i < len(values); i += 2 {\n\t\tif values[i] == key {\n\t\t\treturn values[i+1]\n\t\t}\n\t}\n\n\treturn nil\n}\n\ntype truncValidator func(values ...any) error\n\n// newTruncValidator will return a logger validator for validating truncated\n// messages. It takes the key for the portion of the document to validate\n// (e.g. \"command\" for started events, \"reply\" for finished events, etc), and\n// returns an anonymous function that can be used to validate the truncated\n// message.\nfunc newTruncValidator(mt *mtest.T, key string, cond func(string) error) truncValidator {\n\tmt.Helper()\n\n\treturn func(values ...any) error {\n\t\tcmd := findLogValue(mt, key, values...)\n\t\tif cmd == nil {\n\t\t\treturn fmt.Errorf(\"%q not found in keys and values\", key)\n\t\t}\n\n\t\tcmdStr, ok := cmd.(string)\n\n\t\tif !ok {\n\t\t\treturn fmt.Errorf(\"command is not a string\")\n\t\t}\n\n\t\treturn cond(cmdStr)\n\t}\n}\n"
  },
  {
    "path": "internal/integration/main.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage integration\n\n// This file exists to allow the build scripts (and standard Go builds for some early Go versions)\n// to succeed. Without it, the build may encounter an error like:\n//\n//   go build go.mongodb.org/mongo-driver/mongo/integration: build constraints exclude all Go files in ./go.mongodb.org/mongo-driver/mongo/integration\n//\n"
  },
  {
    "path": "internal/integration/main_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage integration\n\nimport (\n\t\"flag\"\n\t\"log\"\n\t\"os\"\n\t\"testing\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/integration/mtest\"\n)\n\nfunc TestMain(m *testing.M) {\n\t// All tests that use mtest.Setup() are expected to be integration tests, so skip them when the\n\t// -short flag is included in the \"go test\" command. Also, we have to parse flags here to use\n\t// testing.Short() because flags aren't parsed before TestMain() is called.\n\tflag.Parse()\n\tif testing.Short() {\n\t\tlog.Print(\"skipping mtest integration test in short mode\")\n\t\treturn\n\t}\n\n\tif err := mtest.Setup(); err != nil {\n\t\tlog.Panic(err)\n\t}\n\tdefer os.Exit(m.Run())\n\tif err := mtest.Teardown(); err != nil {\n\t\tlog.Panic(err)\n\t}\n}\n"
  },
  {
    "path": "internal/integration/mock_find_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2021-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage integration\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/integration/mtest\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n)\n\n// finder is an object that implements FindOne and Find.\ntype finder interface {\n\tFindOne(context.Context, any, ...options.Lister[options.FindOneOptions]) *mongo.SingleResult\n\tFind(context.Context, any, ...options.Lister[options.FindOptions]) (*mongo.Cursor, error)\n}\n\n// mockFinder implements finder.\ntype mockFinder struct {\n\tdocs     []any\n\terr      error\n\tregistry *bson.Registry\n}\n\n// FindOne mocks a findOne operation using NewSingleResultFromDocument.\nfunc (mf *mockFinder) FindOne(_ context.Context, _ any, _ ...options.Lister[options.FindOneOptions]) *mongo.SingleResult {\n\treturn mongo.NewSingleResultFromDocument(mf.docs[0], mf.err, mf.registry)\n}\n\n// Find mocks a find operation using NewCursorFromDocuments.\nfunc (mf *mockFinder) Find(context.Context, any, ...options.Lister[options.FindOptions]) (*mongo.Cursor, error) {\n\treturn mongo.NewCursorFromDocuments(mf.docs, mf.err, mf.registry)\n}\n\n// ShopItem is an item with an associated ID and price.\ntype ShopItem struct {\n\tID    int     `bson:\"id\"`\n\tPrice float64 `bson:\"price\"`\n}\n\n// getItem is an example function using the interface finder to test the mocking of SingleResult.\nfunc getItem(f finder, id int) (*ShopItem, error) {\n\tres := f.FindOne(context.Background(), bson.D{{\"id\", id}})\n\tvar item ShopItem\n\terr := res.Decode(&item)\n\treturn &item, err\n}\n\n// getItems is an example function using the interface finder to test the mocking of Cursor.\nfunc getItems(f finder) ([]ShopItem, error) {\n\tcur, err := f.Find(context.Background(), bson.D{})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar items []ShopItem\n\terr = cur.All(context.Background(), &items)\n\treturn items, err\n}\n\nfunc TestMockFind(t *testing.T) {\n\tmt := mtest.New(t, mtest.NewOptions().CreateClient(false))\n\n\tinsertItems := []any{\n\t\tShopItem{ID: 0, Price: 1.5},\n\t\tShopItem{ID: 1, Price: 5.7},\n\t\tShopItem{ID: 2, Price: 0.25},\n\t}\n\tinsertItem := []any{ShopItem{ID: 1, Price: 5.7}}\n\n\tmt.Run(\"mongo.Collection can be passed as interface\", func(mt *mtest.T) {\n\t\t// Actually insert documents to collection.\n\t\t_, err := mt.Coll.InsertMany(context.Background(), insertItems)\n\t\tassert.Nil(mt, err, \"InsertMany error: %v\", err)\n\n\t\t// Assert that FindOne behaves as expected.\n\t\tshopItem, err := getItem(mt.Coll, 1)\n\t\tassert.Nil(mt, err, \"getItem error: %v\", err)\n\t\tassert.Equal(mt, 1, shopItem.ID)\n\t\tassert.Equal(mt, 5.7, shopItem.Price)\n\n\t\t// Assert that Find behaves as expected.\n\t\tshopItems, err := getItems(mt.Coll)\n\t\tassert.Nil(mt, err, \"getItems error: %v\", err)\n\t\tfor i, shopItem := range shopItems {\n\t\t\texpectedItem := insertItems[i].(ShopItem)\n\t\t\tassert.Equal(mt, expectedItem.ID, shopItem.ID)\n\t\t\tassert.Equal(mt, expectedItem.Price, shopItem.Price)\n\t\t}\n\t})\n\n\tmt.Run(\"FindOne can be mocked\", func(mt *mtest.T) {\n\t\t// Mock a FindOne result with mockFinder.\n\t\tmf := &mockFinder{docs: insertItem, err: nil, registry: nil}\n\n\t\t// Assert that FindOne behaves as expected.\n\t\tshopItem, err := getItem(mf, 1)\n\t\tassert.Nil(mt, err, \"getItem error: %v\", err)\n\t\tassert.Equal(mt, 1, shopItem.ID)\n\t\tassert.Equal(mt, 5.7, shopItem.Price)\n\t})\n\n\tmt.Run(\"Find can be mocked\", func(mt *mtest.T) {\n\t\t// Mock a Find result with mockFinder.\n\t\tmf := &mockFinder{docs: insertItems, err: nil, registry: nil}\n\n\t\t// Assert that Find behaves as expected.\n\t\tshopItems, err := getItems(mf)\n\t\tassert.Nil(mt, err, \"getItems error: %v\", err)\n\t\tfor i, shopItem := range shopItems {\n\t\t\texpectedItem := insertItems[i].(ShopItem)\n\t\t\tassert.Equal(mt, expectedItem.ID, shopItem.ID)\n\t\t\tassert.Equal(mt, expectedItem.Price, shopItem.Price)\n\t\t}\n\t})\n}\n"
  },
  {
    "path": "internal/integration/mongointernal_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2025-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\n//go:build mongointernal\n\npackage integration\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/integration/mtest\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/require\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n)\n\nfunc TestNewSessionWithLSID(t *testing.T) {\n\tmt := mtest.New(t)\n\n\tmt.Run(\"can be used to pass a specific session ID to CRUD commands\", func(mt *mtest.T) {\n\t\tmt.Parallel()\n\n\t\t// Create a session ID document, which is a BSON document with field\n\t\t// \"id\" containing a 16-byte UUID (binary subtype 4).\n\t\tsessionID := bson.Raw(bsoncore.NewDocumentBuilder().\n\t\t\tAppendBinary(\"id\", 4, []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}).\n\t\t\tBuild())\n\n\t\tsess := mongo.NewSessionWithLSID(mt.Client, sessionID)\n\n\t\tctx := mongo.NewSessionContext(context.Background(), sess)\n\t\t_, err := mt.Coll.InsertOne(ctx, bson.D{{\"foo\", \"bar\"}})\n\t\trequire.NoError(mt, err)\n\n\t\tevt := mt.GetStartedEvent()\n\t\tval, err := evt.Command.LookupErr(\"lsid\")\n\t\trequire.NoError(mt, err, \"lsid should be present in the command document\")\n\n\t\tdoc, ok := val.DocumentOK()\n\t\trequire.True(mt, ok, \"lsid should be a document\")\n\n\t\tassert.Equal(mt, sessionID, doc)\n\t})\n\n\tmt.Run(\"EndSession panics\", func(mt *mtest.T) {\n\t\tmt.Parallel()\n\n\t\tsessionID := bson.Raw(bsoncore.NewDocumentBuilder().\n\t\t\tAppendBinary(\"id\", 4, []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}).\n\t\t\tBuild())\n\t\tsess := mongo.NewSessionWithLSID(mt.Client, sessionID)\n\n\t\t// Use a defer-recover block to catch the expected panic and assert that\n\t\t// the recovered error is not nil.\n\t\tdefer func() {\n\t\t\terr := recover()\n\t\t\tassert.NotNil(mt, err, \"expected EndSession to panic\")\n\t\t}()\n\n\t\t// Expect this call to panic.\n\t\tsess.EndSession(context.Background())\n\n\t\t// We expect that calling EndSession on a Session returned by\n\t\t// NewSessionWithLSID panics. This code will only be reached if EndSession\n\t\t// doesn't panic.\n\t\tt.Errorf(\"expected EndSession to panic\")\n\t})\n}\n"
  },
  {
    "path": "internal/integration/mongos_pinning_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage integration\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"testing\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/integration/mtest\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/mongoutil\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/require\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n)\n\nfunc TestMongosPinning(t *testing.T) {\n\tclientOpts := options.Client().SetLocalThreshold(1 * time.Second).SetWriteConcern(mtest.MajorityWc)\n\tmtOpts := mtest.NewOptions().Topologies(mtest.Sharded).MinServerVersion(\"4.1\").CreateClient(false).\n\t\tClientOptions(clientOpts)\n\tmt := mtest.New(t, mtOpts)\n\n\thosts, err := mongoutil.HostsFromURI(mtest.ClusterURI())\n\trequire.NoError(t, err)\n\n\tif len(hosts) < 2 {\n\t\tmt.Skip(\"skipping because at least 2 mongoses are required\")\n\t}\n\n\tmt.Run(\"unpin for next transaction\", func(mt *mtest.T) {\n\t\taddresses := map[string]struct{}{}\n\t\t_ = mt.Client.UseSession(context.Background(), func(sctx context.Context) error {\n\t\t\tsess := mongo.SessionFromContext(sctx)\n\t\t\t// Insert a document in a transaction to pin session to a mongos\n\t\t\terr := sess.StartTransaction()\n\t\t\tassert.Nil(mt, err, \"StartTransaction error: %v\", err)\n\t\t\t_, err = mt.Coll.InsertOne(sctx, bson.D{{\"x\", 1}})\n\t\t\tassert.Nil(mt, err, \"InsertOne error: %v\", err)\n\t\t\terr = sess.CommitTransaction(sctx)\n\t\t\tassert.Nil(mt, err, \"CommitTransaction error: %v\", err)\n\n\t\t\tfor i := 0; i < 50; i++ {\n\t\t\t\t// Call Find in a new transaction to unpin from the old mongos and select a new one\n\t\t\t\terr = sess.StartTransaction()\n\t\t\t\tassert.Nil(mt, err, iterationErrmsg(\"StartTransaction\", i, err))\n\n\t\t\t\tcursor, err := mt.Coll.Find(sctx, bson.D{})\n\t\t\t\tassert.Nil(mt, err, iterationErrmsg(\"Find\", i, err))\n\t\t\t\tassert.True(mt, cursor.Next(context.Background()), \"Next returned false on iteration %v\", i)\n\n\t\t\t\tdescConn, err := mongo.BatchCursorFromCursor(cursor).Server().Connection(context.Background())\n\t\t\t\tassert.Nil(mt, err, iterationErrmsg(\"Connection\", i, err))\n\t\t\t\taddresses[descConn.Description().Addr.String()] = struct{}{}\n\t\t\t\terr = descConn.Close()\n\t\t\t\tassert.Nil(mt, err, iterationErrmsg(\"connection Close\", i, err))\n\n\t\t\t\terr = sess.CommitTransaction(sctx)\n\t\t\t\tassert.Nil(mt, err, iterationErrmsg(\"CommitTransaction\", i, err))\n\t\t\t}\n\t\t\treturn nil\n\t\t})\n\t\tassert.True(mt, len(addresses) > 1, \"expected more than 1 address, got %v\", addresses)\n\t})\n\tmt.Run(\"unpin for non transaction operation\", func(mt *mtest.T) {\n\t\taddresses := map[string]struct{}{}\n\t\t_ = mt.Client.UseSession(context.Background(), func(sctx context.Context) error {\n\t\t\tsess := mongo.SessionFromContext(sctx)\n\n\t\t\t// Insert a document in a transaction to pin session to a mongos\n\t\t\terr := sess.StartTransaction()\n\t\t\tassert.Nil(mt, err, \"StartTransaction error: %v\", err)\n\t\t\t_, err = mt.Coll.InsertOne(sctx, bson.D{{\"x\", 1}})\n\t\t\tassert.Nil(mt, err, \"InsertOne error: %v\", err)\n\t\t\terr = sess.CommitTransaction(sctx)\n\t\t\tassert.Nil(mt, err, \"CommitTransaction error: %v\", err)\n\n\t\t\tfor i := 0; i < 50; i++ {\n\t\t\t\t// Call Find with the session but outside of a transaction\n\t\t\t\tcursor, err := mt.Coll.Find(sctx, bson.D{})\n\t\t\t\tassert.Nil(mt, err, iterationErrmsg(\"Find\", i, err))\n\t\t\t\tassert.True(mt, cursor.Next(context.Background()), \"Next returned false on iteration %v\", i)\n\n\t\t\t\tdescConn, err := mongo.BatchCursorFromCursor(cursor).Server().Connection(context.Background())\n\t\t\t\tassert.Nil(mt, err, iterationErrmsg(\"Connection\", i, err))\n\t\t\t\taddresses[descConn.Description().Addr.String()] = struct{}{}\n\t\t\t\terr = descConn.Close()\n\t\t\t\tassert.Nil(mt, err, iterationErrmsg(\"connection Close\", i, err))\n\t\t\t}\n\t\t\treturn nil\n\t\t})\n\t\tassert.True(mt, len(addresses) > 1, \"expected more than 1 address, got %v\", addresses)\n\t})\n}\n\nfunc iterationErrmsg(op string, i int, wrapped error) string {\n\treturn fmt.Sprintf(\"%v error on iteration %v: %v\", op, i, wrapped)\n}\n"
  },
  {
    "path": "internal/integration/mtest/csfle_enabled.go",
    "content": "// Copyright (C) MongoDB, Inc. 2022-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\n//go:build cse\n\npackage mtest\n\n// IsCSFLEEnabled returns true if driver is built with Client Side Field Level Encryption support.\n// Client Side Field Level Encryption support is enabled with the cse build tag.\nfunc IsCSFLEEnabled() bool {\n\treturn true\n}\n"
  },
  {
    "path": "internal/integration/mtest/csfle_not_enabled.go",
    "content": "// Copyright (C) MongoDB, Inc. 2022-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\n//go:build !cse\n\npackage mtest\n\n// IsCSFLEEnabled returns true if driver is built with Client Side Field Level Encryption support.\n// Client Side Field Level Encryption support is enabled with the cse build tag.\nfunc IsCSFLEEnabled() bool {\n\treturn false\n}\n"
  },
  {
    "path": "internal/integration/mtest/deployment_helpers.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage mtest\n\nimport (\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n)\n\n// BatchIdentifier specifies the keyword to identify the batch in a cursor response.\ntype BatchIdentifier string\n\n// These constants specify valid values for BatchIdentifier.\nconst (\n\tFirstBatch BatchIdentifier = \"firstBatch\"\n\tNextBatch  BatchIdentifier = \"nextBatch\"\n)\n\n// CommandError is a representation of a command error from the server.\ntype CommandError struct {\n\tCode    int32\n\tMessage string\n\tName    string\n\tLabels  []string\n}\n\n// WriteError is a representation of a write error from the server.\ntype WriteError struct {\n\tIndex   int\n\tCode    int\n\tMessage string\n}\n\n// WriteConcernError is a representation of a write concern error from the server.\ntype WriteConcernError struct {\n\tName    string   `bson:\"codeName\"`\n\tCode    int      `bson:\"code\"`\n\tMessage string   `bson:\"errmsg\"`\n\tDetails bson.Raw `bson:\"errInfo\"`\n}\n\n// CreateCursorResponse creates a response for a cursor command.\nfunc CreateCursorResponse(cursorID int64, ns string, identifier BatchIdentifier, batch ...bson.D) bson.D {\n\tbatchArr := bson.A{}\n\tfor _, doc := range batch {\n\t\tbatchArr = append(batchArr, doc)\n\t}\n\n\treturn bson.D{\n\t\t{\"ok\", 1},\n\t\t{\"cursor\", bson.D{\n\t\t\t{\"id\", cursorID},\n\t\t\t{\"ns\", ns},\n\t\t\t{string(identifier), batchArr},\n\t\t}},\n\t}\n}\n\n// CreateCommandErrorResponse creates a response with a command error.\nfunc CreateCommandErrorResponse(ce CommandError) bson.D {\n\tres := bson.D{\n\t\t{\"ok\", 0},\n\t\t{\"code\", ce.Code},\n\t\t{\"errmsg\", ce.Message},\n\t\t{\"codeName\", ce.Name},\n\t}\n\tif len(ce.Labels) > 0 {\n\t\tvar labelsArr bson.A\n\t\tfor _, label := range ce.Labels {\n\t\t\tlabelsArr = append(labelsArr, label)\n\t\t}\n\t\tres = append(res, bson.E{Key: \"errorLabels\", Value: labelsArr})\n\t}\n\treturn res\n}\n\n// CreateWriteErrorsResponse creates a response with one or more write errors.\nfunc CreateWriteErrorsResponse(writeErrorrs ...WriteError) bson.D {\n\tarr := make(bson.A, len(writeErrorrs))\n\tfor idx, we := range writeErrorrs {\n\t\tarr[idx] = bson.D{\n\t\t\t{\"index\", we.Index},\n\t\t\t{\"code\", we.Code},\n\t\t\t{\"errmsg\", we.Message},\n\t\t}\n\t}\n\n\treturn bson.D{\n\t\t{\"ok\", 1},\n\t\t{\"writeErrors\", arr},\n\t}\n}\n\n// CreateWriteConcernErrorResponse creates a response with a write concern error.\nfunc CreateWriteConcernErrorResponse(wce WriteConcernError) bson.D {\n\twceDoc := bson.D{\n\t\t{\"code\", wce.Code},\n\t\t{\"codeName\", wce.Name},\n\t\t{\"errmsg\", wce.Message},\n\t}\n\tif len(wce.Details) > 0 {\n\t\twceDoc = append(wceDoc, bson.E{Key: \"errInfo\", Value: wce.Details})\n\t}\n\n\treturn bson.D{\n\t\t{\"ok\", 1},\n\t\t{\"writeConcernError\", wceDoc},\n\t}\n}\n\n// CreateSuccessResponse creates a response for a successful operation with the given elements.\nfunc CreateSuccessResponse(elems ...bson.E) bson.D {\n\tres := bson.D{\n\t\t{\"ok\", 1},\n\t}\n\treturn append(res, elems...)\n}\n"
  },
  {
    "path": "internal/integration/mtest/doc.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\n// Package mtest is unstable and there is no backward compatibility guarantee.\n// It is experimental and subject to change.\npackage mtest\n"
  },
  {
    "path": "internal/integration/mtest/global_state.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage mtest\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/failpoint\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/connstring\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/topology\"\n)\n\n// AuthEnabled returns whether or not the cluster requires auth.\nfunc AuthEnabled() bool {\n\treturn testContext.authEnabled\n}\n\n// SSLEnabled returns whether or not the cluster requires SSL.\nfunc SSLEnabled() bool {\n\treturn testContext.sslEnabled\n}\n\n// ClusterTopologyKind returns the topology kind of the cluster under test.\nfunc ClusterTopologyKind() TopologyKind {\n\treturn testContext.topoKind\n}\n\n// ClusterURI returns the connection string for the cluster.\nfunc ClusterURI() string {\n\treturn testContext.connString.Original\n}\n\n// Serverless returns whether the test is running against a serverless instance.\nfunc Serverless() bool {\n\treturn testContext.serverless\n}\n\n// SingleMongosLoadBalancerURI returns the URI for a load balancer fronting a single mongos. This will only be set\n// if the cluster is load balanced.\nfunc SingleMongosLoadBalancerURI() string {\n\treturn testContext.singleMongosLoadBalancerURI\n}\n\n// MultiMongosLoadBalancerURI returns the URI for a load balancer fronting multiple mongoses. This will only be set\n// if the cluster is load balanced.\nfunc MultiMongosLoadBalancerURI() string {\n\treturn testContext.multiMongosLoadBalancerURI\n}\n\n// ClusterConnString returns the parsed ConnString for the cluster.\nfunc ClusterConnString() *connstring.ConnString {\n\treturn testContext.connString\n}\n\n// GlobalClient returns a Client connected to the cluster configured with read concern majority, write concern majority,\n// and read preference primary.\nfunc GlobalClient() *mongo.Client {\n\treturn testContext.client\n}\n\n// GlobalTopology returns a Topology that's connected to the cluster.\nfunc GlobalTopology() *topology.Topology {\n\treturn testContext.topo\n}\n\n// ServerVersion returns the server version of the cluster. This assumes that all nodes in the cluster have the same\n// version.\nfunc ServerVersion() string {\n\treturn testContext.serverVersion\n}\n\n// SetFailPoint configures the provided fail point on the cluster under test using the provided Client.\nfunc SetFailPoint(fp failpoint.FailPoint, client *mongo.Client) error {\n\tadmin := client.Database(\"admin\")\n\tif err := admin.RunCommand(context.Background(), fp).Err(); err != nil {\n\t\treturn fmt.Errorf(\"error creating fail point: %w\", err)\n\t}\n\treturn nil\n}\n\n// SetRawFailPoint configures the fail point represented by the fp parameter on the cluster under test using the\n// provided Client\nfunc SetRawFailPoint(fp bson.Raw, client *mongo.Client) error {\n\tadmin := client.Database(\"admin\")\n\tif err := admin.RunCommand(context.Background(), fp).Err(); err != nil {\n\t\treturn fmt.Errorf(\"error creating fail point: %w\", err)\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "internal/integration/mtest/mongotest.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage mtest\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"strings\"\n\t\"sync\"\n\t\"sync/atomic\"\n\t\"testing\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/event\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/csfle\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/failpoint\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/mongoutil\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/require\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/readconcern\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/readpref\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/writeconcern\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/drivertest\"\n)\n\nvar (\n\t// MajorityWc is the majority write concern.\n\tMajorityWc = writeconcern.Majority()\n\t// PrimaryRp is the primary read preference.\n\tPrimaryRp = readpref.Primary()\n\t// SecondaryRp is the secondary read preference.\n\tSecondaryRp = readpref.Secondary()\n\t// LocalRc is the local read concern\n\tLocalRc = readconcern.Local()\n\t// MajorityRc is the majority read concern\n\tMajorityRc = readconcern.Majority()\n)\n\nconst (\n\tnamespaceExistsErrCode int32 = 48\n)\n\n// T is a wrapper around testing.T.\ntype T struct {\n\t// connsCheckedOut is the net number of connections checked out during test execution.\n\t// It must be accessed using the atomic package and should be at the beginning of the struct.\n\t// - atomic bug: https://pkg.go.dev/sync/atomic#pkg-note-BUG\n\t// - suggested layout: https://go101.org/article/memory-layout.html\n\tconnsCheckedOut int64\n\n\t*testing.T\n\n\t// members for only this T instance\n\tcreateClient      *bool\n\tcreateCollection  *bool\n\trunOn             []RunOnBlock\n\tmockDeployment    *drivertest.MockDeployment // nil if the test is not being run against a mock\n\tmockResponses     []bson.D\n\tcreatedColls      []*Collection // collections created in this test\n\tproxyDialer       *proxyDialer\n\tdbName, collName  string\n\tfailPointNames    []string\n\tminServerVersion  string\n\tmaxServerVersion  string\n\tvalidTopologies   []TopologyKind\n\tauth              *bool\n\tenterprise        *bool\n\tssl               *bool\n\tcollCreateOpts    *options.CreateCollectionOptionsBuilder\n\trequireAPIVersion *bool\n\n\t// options copied to sub-tests\n\tclientType               ClientType\n\tclientOpts               *options.ClientOptions\n\tcollOpts                 *options.CollectionOptionsBuilder\n\tallowFailPointsOnSharded bool\n\n\tbaseOpts *Options // used to create subtests\n\n\t// command monitoring channels\n\tmonitorLock sync.Mutex\n\tstarted     []*event.CommandStartedEvent\n\tsucceeded   []*event.CommandSucceededEvent\n\tfailed      []*event.CommandFailedEvent\n\n\tClient *mongo.Client\n\tDB     *mongo.Database\n\tColl   *mongo.Collection\n}\n\nfunc newT(wrapped *testing.T, opts ...*Options) *T {\n\tt := &T{\n\t\tT: wrapped,\n\t}\n\tfor _, opt := range opts {\n\t\tfor _, optFn := range opt.optFuncs {\n\t\t\toptFn(t)\n\t\t}\n\t}\n\n\tif err := t.verifyConstraints(); err != nil {\n\t\tt.Skipf(\"skipping due to environmental constraints: %v\", err)\n\t}\n\n\tif t.collName == \"\" {\n\t\tt.collName = t.Name()\n\t}\n\tif t.dbName == \"\" {\n\t\tt.dbName = TestDB\n\t}\n\tt.collName = sanitizeCollectionName(t.dbName, t.collName)\n\n\t// create a set of base options for sub-tests\n\tt.baseOpts = NewOptions().ClientOptions(t.clientOpts).CollectionOptions(t.collOpts).ClientType(t.clientType)\n\tif t.allowFailPointsOnSharded {\n\t\tt.baseOpts.AllowFailPointsOnSharded()\n\t}\n\n\treturn t\n}\n\n// New creates a new T instance with the given options. If the current environment does not satisfy constraints\n// specified in the options, the test will be skipped automatically.\nfunc New(wrapped *testing.T, opts ...*Options) *T {\n\t// All tests that use mtest.New() are expected to be integration tests, so skip them when the\n\t// -short flag is included in the \"go test\" command.\n\tif testing.Short() {\n\t\twrapped.Skip(\"skipping mtest integration test in short mode\")\n\t}\n\n\tt := newT(wrapped, opts...)\n\n\twrapped.Cleanup(t.cleanup)\n\n\treturn t\n}\n\n// cleanup cleans up any resources associated with a T. It is intended to be\n// called by [testing.T.Cleanup].\nfunc (t *T) cleanup() {\n\tif t.Client == nil {\n\t\treturn\n\t}\n\n\t// only clear collections and fail points if the test is not running against a mock\n\tif t.clientType != Mock {\n\t\tt.ClearCollections()\n\t\tt.ClearFailPoints()\n\t}\n\n\t// always disconnect the client regardless of clientType because Client.Disconnect will work against\n\t// all deployments\n\t_ = t.Client.Disconnect(context.Background())\n}\n\n// Run creates a new T instance for a sub-test and runs the given callback. It also creates a new collection using the\n// given name which is available to the callback through the T.Coll variable and is dropped after the callback\n// returns.\nfunc (t *T) Run(name string, callback func(mt *T)) {\n\tt.RunOpts(name, NewOptions(), callback)\n}\n\n// Setup initializes the test client and collection for this T instance. This is\n// automatically called by RunOpts but can be called manually when using New()\n// directly.\nfunc (t *T) Setup() {\n\tt.Cleanup(t.teardown)\n\n\t// add any mock responses for this test\n\tif t.clientType == Mock && len(t.mockResponses) > 0 {\n\t\tt.AddMockResponses(t.mockResponses...)\n\t}\n\n\tif t.createClient == nil || *t.createClient {\n\t\tt.createTestClient()\n\t}\n\n\t// create a collection for this test\n\tif t.Client != nil {\n\t\tt.createTestCollection()\n\t}\n\n\t// clear any events that may have happened during setup\n\tt.ClearEvents()\n}\n\n// teardown cleans up test resources and asserts that all sessions and\n// connections are closed. When using New() directly, this should be called via\n// defer after Setup().\nfunc (t *T) teardown() {\n\tif t.Client == nil {\n\t\treturn\n\t}\n\n\t// store number of sessions and connections checked out here but assert that\n\t// they're equal to 0 after cleaning up test resources to make sure resources\n\t// are always cleared.\n\tsessions := t.Client.NumberSessionsInProgress()\n\tconns := t.NumberConnectionsCheckedOut()\n\n\tif t.clientType != Mock {\n\t\tt.ClearFailPoints()\n\t\tt.ClearCollections()\n\t}\n\n\t_ = t.Client.Disconnect(context.Background())\n\tassert.Equal(t, 0, sessions, \"%v sessions checked out\", sessions)\n\tassert.Equal(t, 0, conns, \"%v connections checked out\", conns)\n}\n\n// RunOpts creates a new T instance for a sub-test with the given options. If\n// the current environment does not satisfy constraints specified in the\n// options, the new sub-test will be skipped automatically. If the test is not\n// skipped, the callback will be run with the new T instance. RunOpts creates a\n// new collection with the given name which is available to the callback through\n// the T.Coll variable and is dropped after the callback returns.\nfunc (t *T) RunOpts(name string, opts *Options, callback func(mt *T)) {\n\tt.T.Run(name, func(wrapped *testing.T) {\n\t\tsub := newT(wrapped, t.baseOpts, opts)\n\n\t\tsub.Setup()\n\n\t\tcallback(sub)\n\t})\n}\n\n// AddMockResponses adds responses to be returned by the mock deployment. This should only be used if T is being run\n// against a mock deployment.\nfunc (t *T) AddMockResponses(responses ...bson.D) {\n\tt.mockDeployment.AddResponses(responses...)\n}\n\n// ClearMockResponses clears all responses in the mock deployment.\nfunc (t *T) ClearMockResponses() {\n\tt.mockDeployment.ClearResponses()\n}\n\n// GetStartedEvent returns the least recent CommandStartedEvent, or nil if one is not present.\n// This can only be called once per event.\nfunc (t *T) GetStartedEvent() *event.CommandStartedEvent {\n\tif len(t.started) == 0 {\n\t\treturn nil\n\t}\n\te := t.started[0]\n\tt.started = t.started[1:]\n\treturn e\n}\n\n// GetSucceededEvent returns the least recent CommandSucceededEvent, or nil if one is not present.\n// This can only be called once per event.\nfunc (t *T) GetSucceededEvent() *event.CommandSucceededEvent {\n\tif len(t.succeeded) == 0 {\n\t\treturn nil\n\t}\n\te := t.succeeded[0]\n\tt.succeeded = t.succeeded[1:]\n\treturn e\n}\n\n// GetFailedEvent returns the least recent CommandFailedEvent, or nil if one is not present.\n// This can only be called once per event.\nfunc (t *T) GetFailedEvent() *event.CommandFailedEvent {\n\tif len(t.failed) == 0 {\n\t\treturn nil\n\t}\n\te := t.failed[0]\n\tt.failed = t.failed[1:]\n\treturn e\n}\n\n// GetAllStartedEvents returns a slice of all CommandStartedEvent instances for this test. This can be called multiple\n// times.\nfunc (t *T) GetAllStartedEvents() []*event.CommandStartedEvent {\n\treturn t.started\n}\n\n// GetAllSucceededEvents returns a slice of all CommandSucceededEvent instances for this test. This can be called multiple\n// times.\nfunc (t *T) GetAllSucceededEvents() []*event.CommandSucceededEvent {\n\treturn t.succeeded\n}\n\n// GetAllFailedEvents returns a slice of all CommandFailedEvent instances for this test. This can be called multiple\n// times.\nfunc (t *T) GetAllFailedEvents() []*event.CommandFailedEvent {\n\treturn t.failed\n}\n\n// FilterStartedEvents filters the existing CommandStartedEvent instances for this test using the provided filter\n// callback. An event will be retained if the filter returns true. The list of filtered events will be used to overwrite\n// the list of events for this test and will therefore change the output of t.GetAllStartedEvents().\nfunc (t *T) FilterStartedEvents(filter func(*event.CommandStartedEvent) bool) {\n\tvar newEvents []*event.CommandStartedEvent\n\tfor _, evt := range t.started {\n\t\tif filter(evt) {\n\t\t\tnewEvents = append(newEvents, evt)\n\t\t}\n\t}\n\tt.started = newEvents\n}\n\n// FilterSucceededEvents filters the existing CommandSucceededEvent instances for this test using the provided filter\n// callback. An event will be retained if the filter returns true. The list of filtered events will be used to overwrite\n// the list of events for this test and will therefore change the output of t.GetAllSucceededEvents().\nfunc (t *T) FilterSucceededEvents(filter func(*event.CommandSucceededEvent) bool) {\n\tvar newEvents []*event.CommandSucceededEvent\n\tfor _, evt := range t.succeeded {\n\t\tif filter(evt) {\n\t\t\tnewEvents = append(newEvents, evt)\n\t\t}\n\t}\n\tt.succeeded = newEvents\n}\n\n// FilterFailedEvents filters the existing CommandFailedEVent instances for this test using the provided filter\n// callback. An event will be retained if the filter returns true. The list of filtered events will be used to overwrite\n// the list of events for this test and will therefore change the output of t.GetAllFailedEvents().\nfunc (t *T) FilterFailedEvents(filter func(*event.CommandFailedEvent) bool) {\n\tvar newEvents []*event.CommandFailedEvent\n\tfor _, evt := range t.failed {\n\t\tif filter(evt) {\n\t\t\tnewEvents = append(newEvents, evt)\n\t\t}\n\t}\n\tt.failed = newEvents\n}\n\n// GetProxyCapture returns the ProxyCapture used by the test. If the client\n// type is not Proxy, this returns nil.\nfunc (t *T) GetProxyCapture() *ProxyCapture {\n\tif t.proxyDialer == nil {\n\t\treturn nil\n\t}\n\treturn t.proxyDialer.proxyCapture\n}\n\n// NumberConnectionsCheckedOut returns the number of connections checked out from the test Client.\nfunc (t *T) NumberConnectionsCheckedOut() int {\n\treturn int(atomic.LoadInt64(&t.connsCheckedOut))\n}\n\n// ClearEvents clears the existing command monitoring events.\nfunc (t *T) ClearEvents() {\n\tt.started = t.started[:0]\n\tt.succeeded = t.succeeded[:0]\n\tt.failed = t.failed[:0]\n}\n\n// ResetClient resets the existing client with the given options. If opts is nil, the existing options will be used.\n// If t.Coll is not-nil, it will be reset to use the new client. Should only be called if the existing client is\n// not nil. This will Disconnect the existing client but will not drop existing collections. To do so, ClearCollections\n// must be called before calling ResetClient.\nfunc (t *T) ResetClient(opts *options.ClientOptions) {\n\tif opts != nil {\n\t\tt.clientOpts = opts\n\t}\n\n\tif t.Client != nil {\n\t\t_ = t.Client.Disconnect(context.Background())\n\t}\n\tt.createTestClient()\n\tt.DB = t.Client.Database(t.dbName)\n\tt.Coll = t.DB.Collection(t.collName, t.collOpts)\n\n\tfor _, coll := range t.createdColls {\n\t\t// If the collection was created using a different Client, it doesn't need to be reset.\n\t\tif coll.hasDifferentClient {\n\t\t\tcontinue\n\t\t}\n\n\t\t// If the namespace is the same as t.Coll, we can use t.Coll.\n\t\tif coll.created.Name() == t.collName && coll.created.Database().Name() == t.dbName {\n\t\t\tcoll.created = t.Coll\n\t\t\tcontinue\n\t\t}\n\n\t\t// Otherwise, reset the collection to use the new Client.\n\t\tcoll.created = t.Client.Database(coll.DB).Collection(coll.Name, coll.Opts)\n\t}\n}\n\n// Collection is used to configure a new collection created during a test.\ntype Collection struct {\n\tName               string\n\tDB                 string        // defaults to mt.DB.Name() if not specified\n\tClient             *mongo.Client // defaults to mt.Client if not specified\n\tOpts               *options.CollectionOptionsBuilder\n\tCreateOpts         *options.CreateCollectionOptionsBuilder\n\tViewOn             string\n\tViewPipeline       any\n\thasDifferentClient bool\n\tcreated            *mongo.Collection // the actual collection that was created\n}\n\n// CreateCollection creates a new collection with the given configuration. The collection will be dropped after the test\n// finishes running. If createOnServer is true, the function ensures that the collection has been created server-side\n// by running the create command. The create command will appear in command monitoring channels.\nfunc (t *T) CreateCollection(coll Collection, createOnServer bool) *mongo.Collection {\n\tif coll.DB == \"\" {\n\t\tcoll.DB = t.DB.Name()\n\t}\n\tif coll.Client == nil {\n\t\tcoll.Client = t.Client\n\t}\n\tcoll.hasDifferentClient = coll.Client != t.Client\n\n\tdb := coll.Client.Database(coll.DB)\n\n\topts, err := mongoutil.NewOptions[options.CreateCollectionOptions](coll.CreateOpts)\n\trequire.NoError(t, err, \"failed to construct options from builder\")\n\n\tif coll.CreateOpts != nil && opts.EncryptedFields != nil {\n\t\t// An encrypted collection consists of a data collection and three state collections.\n\t\t// Aborted test runs may leave these collections.\n\t\t// Drop all four collections to avoid a quiet failure to create all collections.\n\t\tDropEncryptedCollection(t, db.Collection(coll.Name), opts.EncryptedFields)\n\t}\n\n\tif createOnServer && t.clientType != Mock {\n\t\tvar err error\n\t\tif coll.ViewOn != \"\" {\n\t\t\terr = db.CreateView(context.Background(), coll.Name, coll.ViewOn, coll.ViewPipeline)\n\t\t} else {\n\t\t\terr = db.CreateCollection(context.Background(), coll.Name, coll.CreateOpts)\n\t\t}\n\n\t\t// ignore ErrUnacknowledgedWrite. Client may be configured with unacknowledged write concern.\n\t\tif err != nil && !errors.Is(err, driver.ErrUnacknowledgedWrite) {\n\t\t\t// ignore NamespaceExists errors for idempotency\n\n\t\t\tvar cmdErr mongo.CommandError\n\t\t\tif !errors.As(err, &cmdErr) || cmdErr.Code != namespaceExistsErrCode {\n\t\t\t\tt.Fatalf(\"error creating collection or view: %v on server: %v\", coll.Name, err)\n\t\t\t}\n\t\t}\n\t}\n\n\tcoll.created = db.Collection(coll.Name, coll.Opts)\n\tt.createdColls = append(t.createdColls, &coll)\n\treturn coll.created\n}\n\n// DropEncryptedCollection drops a collection with EncryptedFields.\n// The EncryptedFields option is not supported in Collection.Drop(). See GODRIVER-2413.\nfunc DropEncryptedCollection(t *T, coll *mongo.Collection, encryptedFields any) {\n\tt.Helper()\n\n\tvar efBSON bsoncore.Document\n\tefBSON, err := bson.Marshal(encryptedFields)\n\tassert.Nil(t, err, \"error in Marshal: %v\", err)\n\n\t// Drop the two encryption-related, associated collections: `escCollection` and `ecocCollection`.\n\t// Drop ESCCollection.\n\tescCollection, err := csfle.GetEncryptedStateCollectionName(efBSON, coll.Name(), csfle.EncryptedStateCollection)\n\tassert.Nil(t, err, \"error in getEncryptedStateCollectionName: %v\", err)\n\terr = coll.Database().Collection(escCollection).Drop(context.Background())\n\tassert.Nil(t, err, \"error in Drop: %v\", err)\n\n\t// Drop ECOCCollection.\n\tecocCollection, err := csfle.GetEncryptedStateCollectionName(efBSON, coll.Name(), csfle.EncryptedCompactionCollection)\n\tassert.Nil(t, err, \"error in getEncryptedStateCollectionName: %v\", err)\n\terr = coll.Database().Collection(ecocCollection).Drop(context.Background())\n\tassert.Nil(t, err, \"error in Drop: %v\", err)\n\n\t// Drop the data collection.\n\terr = coll.Drop(context.Background())\n\tassert.Nil(t, err, \"error in Drop: %v\", err)\n}\n\n// ClearCollections drops all collections previously created by this test.\nfunc (t *T) ClearCollections() {\n\t// Collections should not be dropped when testing against Atlas Data Lake because the data is pre-inserted.\n\tif !testContext.dataLake {\n\t\tfor _, coll := range t.createdColls {\n\t\t\topts, err := mongoutil.NewOptions[options.CreateCollectionOptions](coll.CreateOpts)\n\t\t\trequire.NoError(t, err, \"failed to construct options from builder\")\n\n\t\t\tif coll.CreateOpts != nil && opts.EncryptedFields != nil {\n\t\t\t\tDropEncryptedCollection(t, coll.created, opts.EncryptedFields)\n\t\t\t}\n\n\t\t\t// It's possible that a collection could have an unacknowledged write\n\t\t\t// concern, which could prevent it from being dropped for sharded\n\t\t\t// clusters. We can resolve this by  re-instantiating the collection with\n\t\t\t// a majority write concern before dropping.\n\t\t\tclonedColl := coll.created.Clone(options.Collection().SetWriteConcern(writeconcern.Majority()))\n\n\t\t\t_ = clonedColl.Drop(context.Background())\n\t\t}\n\t}\n\tt.createdColls = t.createdColls[:0]\n}\n\n// SetFailPoint sets a fail point for the client associated with T. Commands to create the failpoint will appear\n// in command monitoring channels. The fail point will automatically be disabled after this test has run.\nfunc (t *T) SetFailPoint(fp failpoint.FailPoint) {\n\t// Do not allow failpoints to be used on sharded topologies unless\n\t// specifically configured to allow it.\n\t//\n\t// On sharded topologies, failpoints are applied to only a single mongoS. If\n\t// the driver is connected to multiple mongoS instances, there's a\n\t// possibility a different mongoS will be selected for a subsequent command.\n\t// In that case, the failpoint is effectively ignored, leading to a test\n\t// failure that is extremely difficult to diagnose.\n\t//\n\t// TODO(GODRIVER-3328): Remove this once we set failpoints on every mongoS\n\t// in sharded topologies.\n\tif testContext.topoKind == Sharded && !t.allowFailPointsOnSharded {\n\t\tt.Fatalf(\"cannot use failpoints with sharded topologies unless AllowFailPointsOnSharded is set\")\n\t}\n\n\t// ensure mode fields are int32\n\tif modeMap, ok := fp.Mode.(map[string]any); ok {\n\t\tvar key string\n\t\tvar err error\n\n\t\tif times, ok := modeMap[\"times\"]; ok {\n\t\t\tkey = \"times\"\n\t\t\tmodeMap[\"times\"], err = t.interfaceToInt32(times)\n\t\t}\n\t\tif skip, ok := modeMap[\"skip\"]; ok {\n\t\t\tkey = \"skip\"\n\t\t\tmodeMap[\"skip\"], err = t.interfaceToInt32(skip)\n\t\t}\n\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"error converting %s to int32: %v\", key, err)\n\t\t}\n\t}\n\n\tif err := SetFailPoint(fp, t.Client); err != nil {\n\t\tt.Fatal(err)\n\t}\n\tt.failPointNames = append(t.failPointNames, fp.ConfigureFailPoint)\n}\n\n// SetFailPointFromDocument sets the fail point represented by the given document for the client associated with T. This\n// method assumes that the given document is in the form {configureFailPoint: <failPointName>, ...}. Commands to create\n// the failpoint will appear in command monitoring channels. The fail point will be automatically disabled after this\n// test has run.\nfunc (t *T) SetFailPointFromDocument(fp bson.Raw) {\n\tif err := SetRawFailPoint(fp, t.Client); err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tname := fp.Index(0).Value().StringValue()\n\tt.failPointNames = append(t.failPointNames, name)\n}\n\n// TrackFailPoint adds the given fail point to the list of fail points to be disabled when the current test finishes.\n// This function does not create a fail point on the server.\nfunc (t *T) TrackFailPoint(fpName string) {\n\tt.failPointNames = append(t.failPointNames, fpName)\n}\n\n// ClearFailPoints disables all previously set failpoints for this test.\nfunc (t *T) ClearFailPoints() {\n\tdb := t.Client.Database(\"admin\")\n\tfor _, fp := range t.failPointNames {\n\t\tcmd := failpoint.FailPoint{\n\t\t\tConfigureFailPoint: fp,\n\t\t\tMode:               failpoint.ModeOff,\n\t\t}\n\t\terr := db.RunCommand(context.Background(), cmd).Err()\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"error clearing fail point %s: %v\", fp, err)\n\t\t}\n\t}\n\tt.failPointNames = t.failPointNames[:0]\n}\n\n// CloneDatabase modifies the default database for this test to match the given options.\nfunc (t *T) CloneDatabase(opts *options.DatabaseOptionsBuilder) {\n\tt.DB = t.Client.Database(t.dbName, opts)\n}\n\n// CloneCollection modifies the default collection for this test to match the given options.\nfunc (t *T) CloneCollection(opts *options.CollectionOptionsBuilder) {\n\tt.Coll = t.Coll.Clone(opts)\n}\n\nfunc sanitizeCollectionName(db string, coll string) string {\n\t// Collections can't have \"$\" in their names, so we substitute it with \"%\".\n\tcoll = strings.ReplaceAll(coll, \"$\", \"%\")\n\n\t// Namespaces can only have 120 bytes max.\n\tif len(db+\".\"+coll) >= 120 {\n\t\t// coll len must be <= remaining\n\t\tremaining := 120 - (len(db) + 1) // +1 for \".\"\n\t\tcoll = coll[len(coll)-remaining:]\n\t}\n\treturn coll\n}\n\nfunc (t *T) createTestClient() {\n\tclientOpts := t.clientOpts\n\n\tif t.clientOpts == nil {\n\t\t// default opts\n\t\tclientOpts = options.Client().SetWriteConcern(MajorityWc).SetReadPreference(PrimaryRp)\n\t}\n\n\t// set ServerAPIOptions to latest version if required\n\tif clientOpts.Deployment == nil && t.clientType != Mock && clientOpts.ServerAPIOptions == nil && testContext.requireAPIVersion {\n\t\tclientOpts.SetServerAPIOptions(options.ServerAPI(driver.TestServerAPIVersion))\n\t}\n\n\t// Setup command monitor\n\tcustomMonitor := clientOpts.Monitor\n\tclientOpts.SetMonitor(&event.CommandMonitor{\n\t\tStarted: func(ctx context.Context, cse *event.CommandStartedEvent) {\n\t\t\tif customMonitor != nil && customMonitor.Started != nil {\n\t\t\t\tcustomMonitor.Started(ctx, cse)\n\t\t\t}\n\t\t\tt.monitorLock.Lock()\n\t\t\tdefer t.monitorLock.Unlock()\n\t\t\tt.started = append(t.started, cse)\n\t\t},\n\t\tSucceeded: func(ctx context.Context, cse *event.CommandSucceededEvent) {\n\t\t\tif customMonitor != nil && customMonitor.Succeeded != nil {\n\t\t\t\tcustomMonitor.Succeeded(ctx, cse)\n\t\t\t}\n\t\t\tt.monitorLock.Lock()\n\t\t\tdefer t.monitorLock.Unlock()\n\t\t\tt.succeeded = append(t.succeeded, cse)\n\t\t},\n\t\tFailed: func(ctx context.Context, cfe *event.CommandFailedEvent) {\n\t\t\tif customMonitor != nil && customMonitor.Failed != nil {\n\t\t\t\tcustomMonitor.Failed(ctx, cfe)\n\t\t\t}\n\t\t\tt.monitorLock.Lock()\n\t\t\tdefer t.monitorLock.Unlock()\n\t\t\tt.failed = append(t.failed, cfe)\n\t\t},\n\t})\n\t// only specify connection pool monitor if no deployment is given\n\tif clientOpts.Deployment == nil {\n\t\tpreviousPoolMonitor := clientOpts.PoolMonitor\n\n\t\tclientOpts.SetPoolMonitor(&event.PoolMonitor{\n\t\t\tEvent: func(evt *event.PoolEvent) {\n\t\t\t\tif previousPoolMonitor != nil {\n\t\t\t\t\tpreviousPoolMonitor.Event(evt)\n\t\t\t\t}\n\n\t\t\t\tswitch evt.Type {\n\t\t\t\tcase event.ConnectionCheckedOut:\n\t\t\t\t\tatomic.AddInt64(&t.connsCheckedOut, 1)\n\t\t\t\tcase event.ConnectionCheckedIn:\n\t\t\t\t\tatomic.AddInt64(&t.connsCheckedOut, -1)\n\t\t\t\t}\n\t\t\t},\n\t\t})\n\t}\n\n\tvar err error\n\tswitch t.clientType {\n\tcase Pinned:\n\t\t// pin to first mongos\n\t\tpinnedHostList := []string{testContext.connString.Hosts[0]}\n\t\turiOpts := options.Client().ApplyURI(testContext.connString.Original).SetHosts(pinnedHostList)\n\t\tt.Client, err = mongo.Connect(uriOpts, clientOpts)\n\tcase Mock:\n\t\t// clear pool monitor to avoid configuration error\n\n\t\tclientOpts.PoolMonitor = nil\n\n\t\tt.mockDeployment = drivertest.NewMockDeployment()\n\t\tclientOpts.Deployment = t.mockDeployment\n\n\t\tt.Client, err = mongo.Connect(clientOpts)\n\tcase Proxy:\n\t\tt.proxyDialer = newProxyDialer()\n\t\tclientOpts.SetDialer(t.proxyDialer)\n\n\t\t// After setting the Dialer, fall-through to the Default case to apply the correct URI\n\t\tfallthrough\n\tcase Default:\n\t\t// Use a different set of options to specify the URI because clientOpts may already have a URI or host seedlist\n\t\t// specified.\n\t\tvar uriOpts *options.ClientOptions\n\t\tif clientOpts.Deployment == nil {\n\t\t\t// Only specify URI if the deployment is not set to avoid setting topology/server options along with the\n\t\t\t// deployment.\n\t\t\turiOpts = options.Client().ApplyURI(testContext.connString.Original)\n\t\t}\n\n\t\tt.Client, err = mongo.Connect(uriOpts, clientOpts)\n\t}\n\tif err != nil {\n\t\tt.Fatalf(\"error creating client: %v\", err)\n\t}\n}\n\nfunc (t *T) createTestCollection() {\n\tt.DB = t.Client.Database(t.dbName)\n\tt.createdColls = t.createdColls[:0]\n\n\t// Collections should not be explicitly created when testing against Atlas Data Lake because they already exist in\n\t// the server with pre-seeded data.\n\tcreateOnServer := (t.createCollection == nil || *t.createCollection) && !testContext.dataLake\n\tt.Coll = t.CreateCollection(Collection{\n\t\tName:       t.collName,\n\t\tCreateOpts: t.collCreateOpts,\n\t\tOpts:       t.collOpts,\n\t}, createOnServer)\n}\n\n// verifyVersionConstraints returns an error if the cluster's server version is not in the range [min, max]. Server\n// versions will only be checked if they are non-empty.\nfunc verifyVersionConstraints(min, max string) error {\n\tif min != \"\" && CompareServerVersions(testContext.serverVersion, min) < 0 {\n\t\treturn fmt.Errorf(\"server version %q is lower than min required version %q\", testContext.serverVersion, min)\n\t}\n\tif max != \"\" && CompareServerVersions(testContext.serverVersion, max) > 0 {\n\t\treturn fmt.Errorf(\"server version %q is higher than max version %q\", testContext.serverVersion, max)\n\t}\n\treturn nil\n}\n\n// verifyTopologyConstraints returns an error if the cluster's topology kind does not match one of the provided\n// kinds. If the topologies slice is empty, nil is returned without any additional checks.\nfunc verifyTopologyConstraints(topologies []TopologyKind) error {\n\tif len(topologies) == 0 {\n\t\treturn nil\n\t}\n\n\tfor _, topo := range topologies {\n\t\t// For ShardedReplicaSet, we won't get an exact match because testContext.topoKind will be Sharded so we do an\n\t\t// additional comparison with the testContext.shardedReplicaSet field.\n\t\tif topo == testContext.topoKind || (topo == ShardedReplicaSet && testContext.shardedReplicaSet) {\n\t\t\treturn nil\n\t\t}\n\t}\n\treturn fmt.Errorf(\"topology kind %q does not match any of the required kinds %q\", testContext.topoKind, topologies)\n}\n\nfunc verifyServerParametersConstraints(serverParameters map[string]bson.RawValue) error {\n\tfor param, expected := range serverParameters {\n\t\tactual, err := testContext.serverParameters.LookupErr(param)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"server does not support parameter %q\", param)\n\t\t}\n\t\tif !expected.Equal(actual) {\n\t\t\treturn fmt.Errorf(\"mismatched values for server parameter %q; expected %s, got %s\", param, expected, actual)\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc verifyAuthConstraint(expected *bool) error {\n\tif expected != nil && *expected != testContext.authEnabled {\n\t\treturn fmt.Errorf(\"test requires auth value: %v, cluster auth value: %v\", *expected, testContext.authEnabled)\n\t}\n\treturn nil\n}\n\nfunc verifyServerlessConstraint(expected string) error {\n\tswitch expected {\n\tcase \"require\":\n\t\tif !testContext.serverless {\n\t\t\treturn fmt.Errorf(\"test requires serverless\")\n\t\t}\n\tcase \"forbid\":\n\t\tif testContext.serverless {\n\t\t\treturn fmt.Errorf(\"test forbids serverless\")\n\t\t}\n\tcase \"allow\", \"\":\n\tdefault:\n\t\treturn fmt.Errorf(\"invalid value for serverless: %s\", expected)\n\t}\n\treturn nil\n}\n\n// verifyRunOnBlockConstraint returns an error if the current environment does not match the provided RunOnBlock.\nfunc verifyRunOnBlockConstraint(rob RunOnBlock) error {\n\tif err := verifyVersionConstraints(rob.MinServerVersion, rob.MaxServerVersion); err != nil {\n\t\treturn err\n\t}\n\tif err := verifyTopologyConstraints(rob.Topology); err != nil {\n\t\treturn err\n\t}\n\n\t// Tests in the unified test format have runOn.auth to indicate whether the\n\t// test should be run against an auth-enabled configuration. SDAM integration\n\t// spec tests have runOn.authEnabled to indicate the same thing. Use whichever\n\t// is set for verifyAuthConstraint().\n\tauth := rob.Auth\n\tif rob.AuthEnabled != nil {\n\t\tif auth != nil {\n\t\t\treturn fmt.Errorf(\"runOnBlock cannot specify both auth and authEnabled\")\n\t\t}\n\t\tauth = rob.AuthEnabled\n\t}\n\tif err := verifyAuthConstraint(auth); err != nil {\n\t\treturn err\n\t}\n\n\tif err := verifyServerlessConstraint(rob.Serverless); err != nil {\n\t\treturn err\n\t}\n\tif err := verifyServerParametersConstraints(rob.ServerParameters); err != nil {\n\t\treturn err\n\t}\n\n\tif rob.CSFLEEnabled() && !IsCSFLEEnabled() {\n\t\treturn fmt.Errorf(\"runOnBlock requires CSFLE to be enabled. Build with the cse tag to enable\")\n\t}\n\tif rob.CSFLEDisabled() && IsCSFLEEnabled() {\n\t\treturn fmt.Errorf(\"runOnBlock requires CSFLE to be disabled. Build without the cse tag to disable\")\n\t}\n\tif rob.CSFLEEnabled() {\n\t\tif err := verifyVersionConstraints(\"4.2\", \"\"); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// verifyConstraints returns an error if the current environment does not match the constraints specified for the test.\nfunc (t *T) verifyConstraints() error {\n\t// Check constraints not specified as runOn blocks\n\tif err := verifyVersionConstraints(t.minServerVersion, t.maxServerVersion); err != nil {\n\t\treturn err\n\t}\n\tif err := verifyTopologyConstraints(t.validTopologies); err != nil {\n\t\treturn err\n\t}\n\tif err := verifyAuthConstraint(t.auth); err != nil {\n\t\treturn err\n\t}\n\tif t.ssl != nil && *t.ssl != testContext.sslEnabled {\n\t\treturn fmt.Errorf(\"test requires ssl value: %v, cluster ssl value: %v\", *t.ssl, testContext.sslEnabled)\n\t}\n\tif t.enterprise != nil && *t.enterprise != testContext.enterpriseServer {\n\t\treturn fmt.Errorf(\"test requires enterprise value: %v, cluster enterprise value: %v\", *t.enterprise,\n\t\t\ttestContext.enterpriseServer)\n\t}\n\tif t.requireAPIVersion != nil && *t.requireAPIVersion != testContext.requireAPIVersion {\n\t\treturn fmt.Errorf(\"test requires RequireAPIVersion value: %v, local RequireAPIVersion value: %v\", *t.requireAPIVersion,\n\t\t\ttestContext.requireAPIVersion)\n\t}\n\n\t// Check runOn blocks. The test can be executed if there are no blocks or at least block matches the current test\n\t// setup.\n\tif len(t.runOn) == 0 {\n\t\treturn nil\n\t}\n\n\t// Stop once we find a RunOnBlock that matches the current environment. Record all errors as we go because if we\n\t// don't find any matching blocks, we want to report the comparison errors for each block.\n\trunOnErrors := make([]error, 0, len(t.runOn))\n\tfor _, runOn := range t.runOn {\n\t\terr := verifyRunOnBlockConstraint(runOn)\n\t\tif err == nil {\n\t\t\treturn nil\n\t\t}\n\n\t\trunOnErrors = append(runOnErrors, err)\n\t}\n\treturn fmt.Errorf(\"no matching RunOnBlock; comparison errors: %v\", runOnErrors)\n}\n\nfunc (t *T) interfaceToInt32(i any) (int32, error) {\n\tswitch conv := i.(type) {\n\tcase int:\n\t\treturn int32(conv), nil\n\tcase int32:\n\t\treturn conv, nil\n\tcase int64:\n\t\treturn int32(conv), nil\n\tcase float64:\n\t\treturn int32(conv), nil\n\t}\n\n\treturn 0, fmt.Errorf(\"type %T cannot be converted to int32\", i)\n}\n"
  },
  {
    "path": "internal/integration/mtest/options.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage mtest\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n)\n\n// TopologyKind describes the topology that a test is run on.\ntype TopologyKind string\n\n// These constants specify valid values for TopologyKind\nconst (\n\tReplicaSet   TopologyKind = \"replicaset\"\n\tSharded      TopologyKind = \"sharded\"\n\tSingle       TopologyKind = \"single\"\n\tLoadBalanced TopologyKind = \"load-balanced\"\n\t// ShardedReplicaSet is a special case of sharded that requires each shard to be a replica set rather than a\n\t// standalone server.\n\tShardedReplicaSet TopologyKind = \"sharded-replicaset\"\n)\n\n// ClientType specifies the type of Client that should be created for a test.\ntype ClientType int\n\n// These constants specify valid values for ClientType\nconst (\n\t// Default specifies a client to the connection string in the MONGODB_URI env variable with command monitoring\n\t// enabled.\n\tDefault ClientType = iota\n\t// Pinned specifies a client that is pinned to a single mongos in a sharded cluster.\n\tPinned\n\t// Mock specifies a client that communicates with a mock deployment.\n\tMock\n\t// Proxy specifies a client that proxies messages to the server and also stores parsed copies. The proxied\n\t// messages can be retrieved via T.GetProxiedMessages or T.GetRawProxiedMessages.\n\tProxy\n)\n\nvar falseBool = false\n\n// CSFLEOptions holds configuration for Client-Side Field Level Encryption\n// (CSFLE).\ntype CSFLEOptions struct {\n\tMinVer string `bson:\"minLibmongocryptVersion\"`\n}\n\n// CSFLE models the runOnRequirements.csfle field in Unified Test Format tests.\n//\n// The csfle field accepts either:\n//   - a boolean: true enables CSFLE with no options; false disables CSFLE\n//     (Options is nil).\n//   - an object: Options is populated from the document and Boolean is set to\n//     false.\ntype CSFLE struct {\n\tBoolean bool\n\tOptions *CSFLEOptions\n}\n\n// UnmarshalBSON implements custom BSON unmarshalling for CSFLE, accepting\n// either a boolean or an embedded document. If a document is provided, Options\n// is set and Boolean is true. If a boolean is provided, Boolean is set and\n// Options is nil.\nfunc (csfle *CSFLE) UnmarshalBSON(data []byte) error {\n\tembRawValue := bson.RawValue{Type: bson.TypeEmbeddedDocument, Value: data}\n\tif err := embRawValue.Unmarshal(&csfle.Options); err == nil {\n\t\tcsfle.Boolean = true\n\n\t\treturn nil\n\t}\n\n\trawValue := bson.RawValue{Type: bson.TypeBoolean, Value: data}\n\tif b, ok := rawValue.BooleanOK(); ok {\n\t\tcsfle.Boolean = b\n\t\tcsfle.Options = nil\n\n\t\treturn nil\n\t}\n\n\treturn fmt.Errorf(\"error unmarshalling CSFLE: %s\", data)\n}\n\n// RunOnBlock describes a constraint for a test.\ntype RunOnBlock struct {\n\tMinServerVersion string                   `bson:\"minServerVersion\"`\n\tMaxServerVersion string                   `bson:\"maxServerVersion\"`\n\tTopology         []TopologyKind           `bson:\"topology\"`\n\tServerless       string                   `bson:\"serverless\"`\n\tServerParameters map[string]bson.RawValue `bson:\"serverParameters\"`\n\tAuth             *bool                    `bson:\"auth\"`\n\tAuthEnabled      *bool                    `bson:\"authEnabled\"`\n\tCSFLE            *CSFLE                   `bson:\"csfleConfiguration\"`\n}\n\n// CSFLEEnabled returns true if CSFLE support is explicitly required in the\n// \"runOnRequirement\" block. It returns false if the CSFLE requirement is\n// unspecified or explicitly false.\nfunc (r *RunOnBlock) CSFLEEnabled() bool {\n\treturn r.CSFLE != nil && (r.CSFLE.Boolean || r.CSFLE.Options != nil)\n}\n\n// CSFLEDisabled returns true if CSFLE support is explicitly forbidden in the\n// \"runOnRequirement\" block. It returns false if the CSFLE requirement is\n// unspecified or explicitly true.\nfunc (r *RunOnBlock) CSFLEDisabled() bool {\n\treturn r.CSFLE != nil && !r.CSFLE.Boolean\n}\n\n// UnmarshalBSON implements custom BSON unmarshalling behavior for RunOnBlock because some test formats use the\n// \"topology\" key while the unified test format uses \"topologies\".\nfunc (r *RunOnBlock) UnmarshalBSON(data []byte) error {\n\tvar temp struct {\n\t\tMinServerVersion string                   `bson:\"minServerVersion\"`\n\t\tMaxServerVersion string                   `bson:\"maxServerVersion\"`\n\t\tTopology         []TopologyKind           `bson:\"topology\"`\n\t\tTopologies       []TopologyKind           `bson:\"topologies\"`\n\t\tServerless       string                   `bson:\"serverless\"`\n\t\tServerParameters map[string]bson.RawValue `bson:\"serverParameters\"`\n\t\tAuth             *bool                    `bson:\"auth\"`\n\t\tAuthEnabled      *bool                    `bson:\"authEnabled\"`\n\t\tCSFLE            *CSFLE                   `bson:\"csfle\"`\n\t\tExtra            map[string]any           `bson:\",inline\"`\n\t}\n\tif err := bson.Unmarshal(data, &temp); err != nil {\n\t\treturn fmt.Errorf(\"error unmarshalling to temporary RunOnBlock object: %w\", err)\n\t}\n\tif len(temp.Extra) > 0 {\n\t\treturn fmt.Errorf(\"unrecognized fields for RunOnBlock: %v\", temp.Extra)\n\t}\n\n\tr.MinServerVersion = temp.MinServerVersion\n\tr.MaxServerVersion = temp.MaxServerVersion\n\tr.Serverless = temp.Serverless\n\tr.ServerParameters = temp.ServerParameters\n\tr.Auth = temp.Auth\n\tr.AuthEnabled = temp.AuthEnabled\n\tr.CSFLE = temp.CSFLE\n\n\tif temp.Topology != nil {\n\t\tr.Topology = temp.Topology\n\t}\n\tif temp.Topologies != nil {\n\t\tif r.Topology != nil {\n\t\t\treturn errors.New(\"both 'topology' and 'topologies' keys cannot be specified for a RunOnBlock\")\n\t\t}\n\n\t\tr.Topology = temp.Topologies\n\t}\n\treturn nil\n}\n\n// optionFunc is a function type that configures a T instance.\ntype optionFunc func(*T)\n\n// Options is the type used to configure a new T instance.\ntype Options struct {\n\toptFuncs []optionFunc\n}\n\n// NewOptions creates an empty Options instance.\nfunc NewOptions() *Options {\n\treturn &Options{}\n}\n\n// CollectionCreateOptions sets the options to pass to Database.CreateCollection() when creating a collection for a test.\nfunc (op *Options) CollectionCreateOptions(opts *options.CreateCollectionOptionsBuilder) *Options {\n\top.optFuncs = append(op.optFuncs, func(t *T) {\n\t\tt.collCreateOpts = opts\n\t})\n\treturn op\n}\n\n// CollectionOptions sets the options to use when creating a collection for a test.\nfunc (op *Options) CollectionOptions(opts *options.CollectionOptionsBuilder) *Options {\n\top.optFuncs = append(op.optFuncs, func(t *T) {\n\t\tt.collOpts = opts\n\t})\n\treturn op\n}\n\n// ClientOptions sets the options to use when creating a client for a test.\nfunc (op *Options) ClientOptions(opts *options.ClientOptions) *Options {\n\top.optFuncs = append(op.optFuncs, func(t *T) {\n\t\tt.clientOpts = opts\n\t})\n\treturn op\n}\n\n// CreateClient specifies whether or not a client should be created for a test. This should be set to false when running\n// a test that only runs other tests.\nfunc (op *Options) CreateClient(create bool) *Options {\n\top.optFuncs = append(op.optFuncs, func(t *T) {\n\t\tt.createClient = &create\n\t})\n\treturn op\n}\n\n// CreateCollection specifies whether or not a collection should be created for a test. The default value is true.\nfunc (op *Options) CreateCollection(create bool) *Options {\n\top.optFuncs = append(op.optFuncs, func(t *T) {\n\t\tt.createCollection = &create\n\t})\n\treturn op\n}\n\n// CollectionName specifies the name for the collection for the test.\nfunc (op *Options) CollectionName(collName string) *Options {\n\top.optFuncs = append(op.optFuncs, func(t *T) {\n\t\tt.collName = collName\n\t})\n\treturn op\n}\n\n// DatabaseName specifies the name of the database for the test.\nfunc (op *Options) DatabaseName(dbName string) *Options {\n\top.optFuncs = append(op.optFuncs, func(t *T) {\n\t\tt.dbName = dbName\n\t})\n\treturn op\n}\n\n// ClientType specifies the type of client that should be created for a test. This option will be propagated to all\n// sub-tests. If the provided ClientType is Proxy, the SSL(false) option will be also be added because the internal\n// proxy dialer and connection types do not support SSL.\nfunc (op *Options) ClientType(ct ClientType) *Options {\n\top.optFuncs = append(op.optFuncs, func(t *T) {\n\t\tt.clientType = ct\n\n\t\tif ct == Proxy {\n\t\t\tt.ssl = &falseBool\n\t\t}\n\t})\n\treturn op\n}\n\n// MockResponses specifies the responses returned by a mock deployment. This should only be used if the current test\n// is being run with MockDeployment(true). Responses can also be added after a sub-test has already been created.\nfunc (op *Options) MockResponses(responses ...bson.D) *Options {\n\top.optFuncs = append(op.optFuncs, func(t *T) {\n\t\tt.mockResponses = responses\n\t})\n\treturn op\n}\n\n// RunOn specifies run-on blocks used to determine if a test should run. If a test's environment meets at least one of the\n// given constraints, it will be run. Otherwise, it will be skipped.\nfunc (op *Options) RunOn(blocks ...RunOnBlock) *Options {\n\top.optFuncs = append(op.optFuncs, func(t *T) {\n\t\tt.runOn = append(t.runOn, blocks...)\n\t})\n\treturn op\n}\n\n// MinServerVersion specifies the minimum server version for the test.\nfunc (op *Options) MinServerVersion(version string) *Options {\n\top.optFuncs = append(op.optFuncs, func(t *T) {\n\t\tt.minServerVersion = version\n\t})\n\treturn op\n}\n\n// MaxServerVersion specifies the maximum server version for the test.\nfunc (op *Options) MaxServerVersion(version string) *Options {\n\top.optFuncs = append(op.optFuncs, func(t *T) {\n\t\tt.maxServerVersion = version\n\t})\n\treturn op\n}\n\n// Topologies specifies a list of topologies that the test can run on.\nfunc (op *Options) Topologies(topos ...TopologyKind) *Options {\n\top.optFuncs = append(op.optFuncs, func(t *T) {\n\t\tt.validTopologies = topos\n\t})\n\treturn op\n}\n\n// Auth specifies whether or not auth should be enabled for this test to run. By default, a test will run regardless\n// of whether or not auth is enabled.\nfunc (op *Options) Auth(auth bool) *Options {\n\top.optFuncs = append(op.optFuncs, func(t *T) {\n\t\tt.auth = &auth\n\t})\n\treturn op\n}\n\n// SSL specifies whether or not SSL should be enabled for this test to run. By default, a test will run regardless\n// of whether or not SSL is enabled.\nfunc (op *Options) SSL(ssl bool) *Options {\n\top.optFuncs = append(op.optFuncs, func(t *T) {\n\t\tt.ssl = &ssl\n\t})\n\treturn op\n}\n\n// Enterprise specifies whether or not this test should only be run on enterprise server variants. Defaults to false.\nfunc (op *Options) Enterprise(ent bool) *Options {\n\top.optFuncs = append(op.optFuncs, func(t *T) {\n\t\tt.enterprise = &ent\n\t})\n\treturn op\n}\n\n// RequireAPIVersion specifies whether this test should only be run when REQUIRE_API_VERSION is true. Defaults to false.\nfunc (op *Options) RequireAPIVersion(rav bool) *Options {\n\top.optFuncs = append(op.optFuncs, func(t *T) {\n\t\tt.requireAPIVersion = &rav\n\t})\n\treturn op\n}\n\n// AllowFailPointsOnSharded bypasses the check for failpoints used on sharded\n// topologies.\n//\n// Failpoints are generally unreliable on sharded topologies, but can be used if\n// the failpoint is explicitly applied to every mongoS node in the cluster.\n//\n// TODO(GODRIVER-3328): Remove this option once we set failpoints on every\n// mongoS in sharded topologies.\nfunc (op *Options) AllowFailPointsOnSharded() *Options {\n\top.optFuncs = append(op.optFuncs, func(t *T) {\n\t\tt.allowFailPointsOnSharded = true\n\t})\n\treturn op\n}\n"
  },
  {
    "path": "internal/integration/mtest/proxy_capture.go",
    "content": "// Copyright (C) MongoDB, Inc. 2025-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage mtest\n\nimport (\n\t\"sync\"\n)\n\n// ProxyCapture provides a FIFO channel for handshake messages passed\n// through the mtest proxyDialer.\ntype ProxyCapture struct {\n\tmessages chan *ProxyMessage\n\tmu       sync.Mutex\n}\n\nfunc newProxyCapture(bufferSize int) *ProxyCapture {\n\treturn &ProxyCapture{\n\t\tmessages: make(chan *ProxyMessage, bufferSize),\n\t}\n}\n\nfunc (hc *ProxyCapture) Capture(msg *ProxyMessage) {\n\thc.mu.Lock()\n\tdefer hc.mu.Unlock()\n\n\thc.messages <- msg\n}\n\nfunc (hc *ProxyCapture) TryNext() *ProxyMessage {\n\tselect {\n\tcase msg := <-hc.messages:\n\t\treturn msg\n\tdefault:\n\t\treturn nil\n\t}\n}\n\n// Drain removes all messages from the channel and returns them as a slice.\nfunc (hc *ProxyCapture) Drain() []*ProxyMessage {\n\tmessages := []*ProxyMessage{}\n\tfor {\n\t\tselect {\n\t\tcase msg := <-hc.messages:\n\t\t\tmessages = append(messages, msg)\n\t\tdefault:\n\t\t\treturn messages\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "internal/integration/mtest/proxy_dialer.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage mtest\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"net\"\n\t\"os\"\n\t\"sync\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/handshake\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n)\n\n// ProxyMessage represents a sent/received pair of parsed wire messages.\ntype ProxyMessage struct {\n\tServerAddress string\n\tCommandName   string\n\tSent          *SentMessage\n\tReceived      *ReceivedMessage\n}\n\n// proxyDialer is a ContextDialer implementation that wraps a net.Dialer and records the messages sent and received\n// using connections created through it.\ntype proxyDialer struct {\n\t*net.Dialer\n\tsync.Mutex\n\n\t// sentMap temporarily stores the message sent to the server using the requestID so it can map requests to their\n\t// responses.\n\tsentMap sync.Map\n\t// addressTranslations maps dialed addresses to the remote addresses reported by the created connections if they\n\t// differ. This can happen if a connection is dialed to a host name, in which case the reported remote address will\n\t// be the resolved IP address.\n\taddressTranslations sync.Map\n\n\tproxyCapture *ProxyCapture\n}\n\nvar _ options.ContextDialer = (*proxyDialer)(nil)\n\nfunc newProxyDialer() *proxyDialer {\n\treturn &proxyDialer{\n\t\tDialer:       &net.Dialer{Timeout: 30 * time.Second},\n\t\tproxyCapture: newProxyCapture(100),\n\t}\n}\n\nfunc newProxyErrorWithWireMsg(wm []byte, err error) error {\n\treturn fmt.Errorf(\"proxy error for wiremessage %v: %w\", wm, err)\n}\n\n// DialContext creates a new proxyConnection.\nfunc (p *proxyDialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) {\n\tnetConn, err := p.Dialer.DialContext(ctx, network, address)\n\tif err != nil {\n\t\treturn netConn, err\n\t}\n\n\t// If the connection's remote address does not match the dialed address, store it in the translations map for\n\t// future look-up. Use the remote address as they key because that's what we'll have access to in the connection's\n\t// Read/Write functions.\n\tif remoteAddress := netConn.RemoteAddr().String(); remoteAddress != address {\n\t\tp.addressTranslations.Store(remoteAddress, address)\n\t}\n\n\tproxy := &proxyConn{\n\t\tConn:   netConn,\n\t\tdialer: p,\n\t}\n\treturn proxy, nil\n}\n\nfunc (p *proxyDialer) storeSentMessage(wm []byte) error {\n\tp.Lock()\n\tdefer p.Unlock()\n\n\t// Create a copy of the wire message so it can be parsed/stored and will not be affected if the wm slice is\n\t// changed by the driver.\n\twmCopy := copyBytes(wm)\n\tparsed, err := parseSentMessage(wmCopy)\n\tif err != nil {\n\t\treturn err\n\t}\n\tp.sentMap.Store(parsed.RequestID, parsed)\n\treturn nil\n}\n\nfunc (p *proxyDialer) storeReceivedMessage(wm []byte, addr string) error {\n\tp.Lock()\n\tdefer p.Unlock()\n\n\tserverAddress := addr\n\tif translated, ok := p.addressTranslations.Load(addr); ok {\n\t\tserverAddress = translated.(string)\n\t}\n\n\t// Create a copy of the wire message so it can be parsed/stored and will not be affected if the wm slice is\n\t// changed by the driver. Parse the incoming message and get the corresponding outgoing message.\n\twmCopy := copyBytes(wm)\n\tparsed, err := parseReceivedMessage(wmCopy)\n\tif err != nil {\n\t\treturn err\n\t}\n\tmapValue, ok := p.sentMap.Load(parsed.ResponseTo)\n\tif !ok {\n\t\treturn errors.New(\"no sent message found\")\n\t}\n\tsent := mapValue.(*SentMessage)\n\tp.sentMap.Delete(parsed.ResponseTo)\n\n\t// Store the parsed message pair.\n\tmsgPair := &ProxyMessage{\n\t\t// The command name is always the first key in the command document.\n\t\tCommandName:   sent.Command.Index(0).Key(),\n\t\tServerAddress: serverAddress,\n\t\tSent:          sent,\n\t\tReceived:      parsed,\n\t}\n\tp.proxyCapture.Capture(msgPair)\n\treturn nil\n}\n\n// proxyConn is a net.Conn that wraps a network connection. All messages sent/received through a proxyConn are stored\n// in the associated proxyDialer and are forwarded over the wrapped connection. Errors encountered when parsing and\n// storing wire messages are wrapped to add context, while errors returned from the underlying network connection are\n// forwarded without wrapping.\ntype proxyConn struct {\n\tnet.Conn\n\tdialer *proxyDialer\n}\n\n// Write stores the given message in the proxyDialer associated with this connection and forwards the message to the\n// server.\nfunc (pc *proxyConn) Write(wm []byte) (n int, err error) {\n\tif err := pc.dialer.storeSentMessage(wm); err != nil {\n\t\twrapped := fmt.Errorf(\"error storing sent message: %w\", err)\n\t\treturn 0, newProxyErrorWithWireMsg(wm, wrapped)\n\t}\n\n\treturn pc.Conn.Write(wm)\n}\n\n// Read reads the message from the server into the given buffer and stores the read message in the proxyDialer\n// associated with this connection.\nfunc (pc *proxyConn) Read(buffer []byte) (int, error) {\n\tn, err := pc.Conn.Read(buffer)\n\tif err != nil {\n\t\treturn n, err\n\t}\n\n\t// The driver reads wire messages in two phases: a four-byte read to get the length of the incoming wire message\n\t// and a (length-4) byte read to get the message itself. There's nothing to be stored during the initial four-byte\n\t// read because we can calculate the length from the rest of the message.\n\tif len(buffer) == 4 {\n\t\treturn 4, nil\n\t}\n\n\t// The buffer contains the entire wire message except for the length bytes. Re-create the full message by appending\n\t// buffer to the end of a four-byte slice and using UpdateLength to set the length bytes.\n\tidx, wm := bsoncore.ReserveLength(nil)\n\twm = append(wm, buffer...)\n\twm = bsoncore.UpdateLength(wm, idx, int32(len(wm[idx:])))\n\n\tif err := pc.dialer.storeReceivedMessage(wm, pc.RemoteAddr().String()); err != nil {\n\t\twrapped := fmt.Errorf(\"error storing received message: %w\", err)\n\t\treturn 0, newProxyErrorWithWireMsg(wm, wrapped)\n\t}\n\n\treturn n, nil\n}\n\nfunc (msg *ProxyMessage) IsHandshake() bool {\n\thello := handshake.LegacyHello\n\tif os.Getenv(\"REQUIRE_API_VERSION\") == \"true\" {\n\t\thello = \"hello\"\n\t}\n\n\treturn hello == msg.CommandName\n}\n"
  },
  {
    "path": "internal/integration/mtest/received_message.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage mtest\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/wiremessage\"\n)\n\n// ReceivedMessage represents a message received from the server.\ntype ReceivedMessage struct {\n\tResponseTo int32\n\tRawMessage wiremessage.WireMessage\n\tResponse   bsoncore.Document\n}\n\ntype receivedMsgParseFn func([]byte) (*ReceivedMessage, error)\n\nfunc getReceivedMessageParser(opcode wiremessage.OpCode) (receivedMsgParseFn, bool) {\n\tswitch opcode {\n\tcase wiremessage.OpReply:\n\t\treturn parseOpReply, true\n\tcase wiremessage.OpMsg:\n\t\treturn parseReceivedOpMsg, true\n\tcase wiremessage.OpCompressed:\n\t\treturn parseReceivedOpCompressed, true\n\tdefault:\n\t\treturn nil, false\n\t}\n}\n\nfunc parseReceivedMessage(wm []byte) (*ReceivedMessage, error) {\n\t// Re-assign the wire message to \"remaining\" so \"wm\" continues to point to the entire message after parsing.\n\t_, _, responseTo, opcode, remaining, ok := wiremessage.ReadHeader(wm)\n\tif !ok {\n\t\treturn nil, errors.New(\"failed to read wiremessage header\")\n\t}\n\n\tparseFn, ok := getReceivedMessageParser(opcode)\n\tif !ok {\n\t\treturn nil, fmt.Errorf(\"unknown opcode: %s\", opcode)\n\t}\n\treceived, err := parseFn(remaining)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"error parsing wiremessage with opcode %s: %w\", opcode, err)\n\t}\n\n\treceived.ResponseTo = responseTo\n\treceived.RawMessage = wm\n\treturn received, nil\n}\n\nfunc parseOpReply(wm []byte) (*ReceivedMessage, error) {\n\tvar ok bool\n\n\tif _, wm, ok = wiremessage.ReadReplyFlags(wm); !ok {\n\t\treturn nil, errors.New(\"failed to read reply flags\")\n\t}\n\tif _, wm, ok = wiremessage.ReadReplyCursorID(wm); !ok {\n\t\treturn nil, errors.New(\"failed to read cursor ID\")\n\t}\n\tif _, wm, ok = wiremessage.ReadReplyStartingFrom(wm); !ok {\n\t\treturn nil, errors.New(\"failed to read starting from\")\n\t}\n\tif _, wm, ok = wiremessage.ReadReplyNumberReturned(wm); !ok {\n\t\treturn nil, errors.New(\"failed to read number returned\")\n\t}\n\n\tvar replyDocuments []bsoncore.Document\n\treplyDocuments, _, ok = wiremessage.ReadReplyDocuments(wm)\n\tif !ok {\n\t\treturn nil, errors.New(\"failed to read reply documents\")\n\t}\n\tif len(replyDocuments) == 0 {\n\t\treturn nil, errors.New(\"no documents in response\")\n\t}\n\n\trm := &ReceivedMessage{\n\t\tResponse: replyDocuments[0],\n\t}\n\treturn rm, nil\n}\n\nfunc parseReceivedOpMsg(wm []byte) (*ReceivedMessage, error) {\n\tvar ok bool\n\tvar err error\n\n\tif _, wm, ok = wiremessage.ReadMsgFlags(wm); !ok {\n\t\treturn nil, errors.New(\"failed to read flags\")\n\t}\n\n\tif wm, err = assertMsgSectionType(wm, wiremessage.SingleDocument); err != nil {\n\t\treturn nil, fmt.Errorf(\"error verifying section type for response document: %w\", err)\n\t}\n\n\tresponse, _, ok := wiremessage.ReadMsgSectionSingleDocument(wm)\n\tif !ok {\n\t\treturn nil, errors.New(\"failed to read response document\")\n\t}\n\trm := &ReceivedMessage{\n\t\tResponse: response,\n\t}\n\treturn rm, nil\n}\n\nfunc parseReceivedOpCompressed(wm []byte) (*ReceivedMessage, error) {\n\toriginalOpcode, wm, err := parseOpCompressed(wm)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tparser, ok := getReceivedMessageParser(originalOpcode)\n\tif !ok {\n\t\treturn nil, fmt.Errorf(\"unknown original opcode %v\", originalOpcode)\n\t}\n\treturn parser(wm)\n}\n"
  },
  {
    "path": "internal/integration/mtest/sent_message.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage mtest\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/wiremessage\"\n)\n\n// SentMessage represents a message sent by the driver to the server.\ntype SentMessage struct {\n\tRequestID  int32\n\tRawMessage wiremessage.WireMessage\n\tCommand    bsoncore.Document\n\tOpCode     wiremessage.OpCode\n\n\t// The $readPreference document. This is separated into its own field even though it's included in the larger\n\t// command document in both OP_QUERY and OP_MSG because OP_QUERY separates the command into a $query sub-document\n\t// if there is a read preference. To unify OP_QUERY and OP_MSG, we pull this out into a separate field and set\n\t// the Command field to the $query sub-document.\n\tReadPreference bsoncore.Document\n\n\t// The documents sent for an insert, update, or delete command. This is separated into its own field because it's\n\t// sent as part of the command document in OP_QUERY and as a document sequence outside the command document in\n\t// OP_MSG.\n\tBatch *bsoncore.Iterator\n}\n\ntype sentMsgParseFn func([]byte) (*SentMessage, error)\n\nfunc getSentMessageParser(opcode wiremessage.OpCode) (sentMsgParseFn, bool) {\n\tswitch opcode {\n\tcase wiremessage.OpQuery:\n\t\treturn parseOpQuery, true\n\tcase wiremessage.OpMsg:\n\t\treturn parseSentOpMsg, true\n\tcase wiremessage.OpCompressed:\n\t\treturn parseSentOpCompressed, true\n\tdefault:\n\t\treturn nil, false\n\t}\n}\n\nfunc parseOpQuery(wm []byte) (*SentMessage, error) {\n\tvar ok bool\n\n\tif _, wm, ok = wiremessage.ReadQueryFlags(wm); !ok {\n\t\treturn nil, errors.New(\"failed to read query flags\")\n\t}\n\tif _, wm, ok = wiremessage.ReadQueryFullCollectionName(wm); !ok {\n\t\treturn nil, errors.New(\"failed to read full collection name\")\n\t}\n\tif _, wm, ok = wiremessage.ReadQueryNumberToSkip(wm); !ok {\n\t\treturn nil, errors.New(\"failed to read number to skip\")\n\t}\n\tif _, wm, ok = wiremessage.ReadQueryNumberToReturn(wm); !ok {\n\t\treturn nil, errors.New(\"failed to read number to return\")\n\t}\n\n\tquery, _, ok := wiremessage.ReadQueryQuery(wm)\n\tif !ok {\n\t\treturn nil, errors.New(\"failed to read query\")\n\t}\n\n\t// If there is no read preference document, the command document is query.\n\t// Otherwise, query is in the format {$query: <command document>, $readPreference: <read preference document>}.\n\tcommandDoc := query\n\tvar rpDoc bsoncore.Document\n\n\tdollarQueryVal, err := query.LookupErr(\"$query\")\n\tif err == nil {\n\t\tcommandDoc = dollarQueryVal.Document()\n\n\t\trpVal, err := query.LookupErr(\"$readPreference\")\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"query %s contains $query but not $readPreference fields\", query)\n\t\t}\n\t\trpDoc = rpVal.Document()\n\t}\n\n\t// For OP_QUERY, inserts, updates, and deletes are sent as a BSON array of documents inside the main command\n\t// document. Pull these sequences out into an ArrayStyle DocumentSequence.\n\tvar batch *bsoncore.Iterator\n\tcmdElems, _ := commandDoc.Elements()\n\tfor _, elem := range cmdElems {\n\t\tswitch elem.Key() {\n\t\tcase \"documents\", \"updates\", \"deletes\":\n\t\t\tbatch = &bsoncore.Iterator{\n\t\t\t\tList: elem.Value().Array(),\n\t\t\t}\n\t\t}\n\t\tif batch != nil {\n\t\t\t// There can only be one of these arrays in a well-formed command, so we exit the loop once one is found.\n\t\t\tbreak\n\t\t}\n\t}\n\n\tsm := &SentMessage{\n\t\tCommand:        commandDoc,\n\t\tReadPreference: rpDoc,\n\t\tBatch:          batch,\n\t}\n\treturn sm, nil\n}\n\nfunc parseSentMessage(wm []byte) (*SentMessage, error) {\n\t// Re-assign the wire message to \"remaining\" so \"wm\" continues to point to the entire message after parsing.\n\t_, requestID, _, opcode, remaining, ok := wiremessage.ReadHeader(wm)\n\tif !ok {\n\t\treturn nil, errors.New(\"failed to read wiremessage header\")\n\t}\n\n\tparseFn, ok := getSentMessageParser(opcode)\n\tif !ok {\n\t\treturn nil, fmt.Errorf(\"unknown opcode: %v\", opcode)\n\t}\n\tsent, err := parseFn(remaining)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"error parsing wiremessage with opcode %s: %w\", opcode, err)\n\t}\n\n\tsent.RequestID = requestID\n\tsent.RawMessage = wm\n\tsent.OpCode = opcode\n\treturn sent, nil\n}\n\nfunc parseSentOpMsg(wm []byte) (*SentMessage, error) {\n\tvar ok bool\n\tvar err error\n\n\tif _, wm, ok = wiremessage.ReadMsgFlags(wm); !ok {\n\t\treturn nil, errors.New(\"failed to read flags\")\n\t}\n\n\tif wm, err = assertMsgSectionType(wm, wiremessage.SingleDocument); err != nil {\n\t\treturn nil, fmt.Errorf(\"error verifying section type for command document: %w\", err)\n\t}\n\n\tvar commandDoc bsoncore.Document\n\tcommandDoc, wm, ok = wiremessage.ReadMsgSectionSingleDocument(wm)\n\tif !ok {\n\t\treturn nil, errors.New(\"failed to read command document\")\n\t}\n\n\tvar rpDoc bsoncore.Document\n\tif rpVal, err := commandDoc.LookupErr(\"$readPreference\"); err == nil {\n\t\trpDoc = rpVal.Document()\n\t}\n\n\tvar batch *bsoncore.Iterator\n\tif len(wm) != 0 {\n\t\t// If there are bytes remaining in the wire message, they must correspond to a DocumentSequence section.\n\t\tif wm, err = assertMsgSectionType(wm, wiremessage.DocumentSequence); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"error verifying section type for document sequence: %w\", err)\n\t\t}\n\n\t\tvar data []byte\n\t\t_, data, _, ok = wiremessage.ReadMsgSectionRawDocumentSequence(wm)\n\t\tif !ok {\n\t\t\treturn nil, errors.New(\"failed to read document sequence\")\n\t\t}\n\n\t\tbatch = &bsoncore.Iterator{\n\t\t\tList: data,\n\t\t}\n\t}\n\n\tsm := &SentMessage{\n\t\tCommand:        commandDoc,\n\t\tReadPreference: rpDoc,\n\t\tBatch:          batch,\n\t}\n\treturn sm, nil\n}\n\nfunc parseSentOpCompressed(wm []byte) (*SentMessage, error) {\n\toriginalOpcode, wm, err := parseOpCompressed(wm)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tparser, ok := getSentMessageParser(originalOpcode)\n\tif !ok {\n\t\treturn nil, fmt.Errorf(\"unknown original opcode %v\", originalOpcode)\n\t}\n\treturn parser(wm)\n}\n"
  },
  {
    "path": "internal/integration/mtest/setup.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage mtest\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"math\"\n\t\"os\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/integtest\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/readpref\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/writeconcern\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/connstring\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/description\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/topology\"\n)\n\nconst (\n\t// TestDB specifies the name of default test database.\n\tTestDB = \"test\"\n)\n\n// testContext holds the global context for the integration tests. The testContext members should only be initialized\n// once during the global setup in TestMain. These variables should only be accessed indirectly through MongoTest\n// instances.\nvar testContext struct {\n\tconnString *connstring.ConnString\n\ttopo       *topology.Topology\n\ttopoKind   TopologyKind\n\t// shardedReplicaSet will be true if we're connected to a sharded cluster and each shard is backed by a replica set.\n\t// We track this as a separate boolean rather than setting topoKind to ShardedReplicaSet because a general\n\t// \"Sharded\" constraint in a test should match both Sharded and ShardedReplicaSet.\n\tshardedReplicaSet           bool\n\tclient                      *mongo.Client // client used for setup and teardown\n\tserverVersion               string\n\tauthEnabled                 bool\n\tsslEnabled                  bool\n\tenterpriseServer            bool\n\tdataLake                    bool\n\trequireAPIVersion           bool\n\tserverParameters            bson.Raw\n\tsingleMongosLoadBalancerURI string\n\tmultiMongosLoadBalancerURI  string\n\tserverless                  bool\n}\n\nfunc setupClient(opts *options.ClientOptions) (*mongo.Client, error) {\n\twcMajority := writeconcern.Majority()\n\t// set ServerAPIOptions to latest version if required\n\tif opts.ServerAPIOptions == nil && testContext.requireAPIVersion {\n\t\topts.SetServerAPIOptions(options.ServerAPI(driver.TestServerAPIVersion))\n\t}\n\t// for sharded clusters, pin to one host. Due to how the cache is implemented on 4.0 and 4.2, behavior\n\t// can be inconsistent when multiple mongoses are used\n\treturn mongo.Connect(opts.SetWriteConcern(wcMajority).SetHosts(opts.Hosts[:1]))\n}\n\n// Setup initializes the current testing context.\n// This function must only be called one time and must be called before any tests run.\nfunc Setup(setupOpts ...*SetupOptions) error {\n\topts := NewSetupOptions()\n\tfor _, opt := range setupOpts {\n\t\tif opt == nil {\n\t\t\tcontinue\n\t\t}\n\t\tif opt.URI != nil {\n\t\t\topts.URI = opt.URI\n\t\t}\n\t}\n\n\tvar uri string\n\tvar err error\n\n\tswitch {\n\tcase opts.URI != nil:\n\t\turi = *opts.URI\n\tdefault:\n\t\tvar err error\n\t\turi, err = integtest.MongoDBURI()\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"error getting uri: %w\", err)\n\t\t}\n\t}\n\n\ttestContext.connString, err = connstring.ParseAndValidate(uri)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"error parsing and validating connstring: %w\", err)\n\t}\n\n\ttestContext.dataLake = os.Getenv(\"ATLAS_DATA_LAKE_INTEGRATION_TEST\") == \"true\"\n\ttestContext.requireAPIVersion = os.Getenv(\"REQUIRE_API_VERSION\") == \"true\"\n\n\tclientOpts := options.Client().ApplyURI(uri)\n\tintegtest.AddTestServerAPIVersion(clientOpts)\n\n\tcfg, err := topology.NewConfig(clientOpts, nil)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"error constructing topology config: %w\", err)\n\t}\n\n\ttestContext.topo, err = topology.New(cfg)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"error creating topology: %w\", err)\n\t}\n\tif err = testContext.topo.Connect(); err != nil {\n\t\treturn fmt.Errorf(\"error connecting topology: %w\", err)\n\t}\n\n\ttestContext.client, err = setupClient(options.Client().ApplyURI(uri))\n\tif err != nil {\n\t\treturn fmt.Errorf(\"error connecting test client: %w\", err)\n\t}\n\n\tpingCtx, cancel := context.WithTimeout(context.Background(), 2*time.Second)\n\tdefer cancel()\n\tif err := testContext.client.Ping(pingCtx, readpref.Primary()); err != nil {\n\t\treturn fmt.Errorf(\"ping error: %w; make sure the deployment is running on URI %v\", err,\n\t\t\ttestContext.connString.Original)\n\t}\n\n\tif testContext.serverVersion, err = getServerVersion(); err != nil {\n\t\treturn fmt.Errorf(\"error getting server version: %w\", err)\n\t}\n\n\tswitch testContext.topo.Kind() {\n\tcase description.TopologyKindSingle:\n\t\ttestContext.topoKind = Single\n\tcase description.TopologyKindReplicaSet, description.TopologyKindReplicaSetWithPrimary, description.TopologyKindReplicaSetNoPrimary:\n\t\ttestContext.topoKind = ReplicaSet\n\tcase description.TopologyKindSharded:\n\t\ttestContext.topoKind = Sharded\n\tcase description.TopologyKindLoadBalanced:\n\t\ttestContext.topoKind = LoadBalanced\n\tdefault:\n\t\treturn fmt.Errorf(\"could not detect topology kind; current topology: %s\", testContext.topo.String())\n\t}\n\n\t// If we're connected to a sharded cluster, determine if the cluster is backed by replica sets.\n\tif testContext.topoKind == Sharded {\n\t\t// Run a find against config.shards and get each document in the collection.\n\t\tcursor, err := testContext.client.Database(\"config\").Collection(\"shards\").Find(context.Background(), bson.D{})\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"error running find against config.shards: %w\", err)\n\t\t}\n\t\tdefer cursor.Close(context.Background())\n\n\t\tvar shards []struct {\n\t\t\tHost string `bson:\"host\"`\n\t\t}\n\t\tif err := cursor.All(context.Background(), &shards); err != nil {\n\t\t\treturn fmt.Errorf(\"error getting results find against config.shards: %w\", err)\n\t\t}\n\n\t\t// Each document's host field will contain a single hostname if the shard is a standalone. If it's a replica\n\t\t// set, the host field will be in the format \"replicaSetName/host1,host2,...\". Therefore, we can determine that\n\t\t// the shard is a standalone if the \"/\" character isn't present.\n\t\tvar foundStandalone bool\n\t\tfor _, shard := range shards {\n\t\t\tif !strings.Contains(shard.Host, \"/\") {\n\t\t\t\tfoundStandalone = true\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tif !foundStandalone {\n\t\t\ttestContext.shardedReplicaSet = true\n\t\t}\n\t}\n\n\t// For non-serverless, load balanced clusters, retrieve the required LB URIs and add additional information (e.g. TLS options) to\n\t// them if necessary.\n\ttestContext.serverless = os.Getenv(\"SERVERLESS\") == \"serverless\"\n\tif !testContext.serverless && testContext.topoKind == LoadBalanced {\n\t\tsingleMongosURI := os.Getenv(\"SINGLE_MONGOS_LB_URI\")\n\t\tif singleMongosURI == \"\" {\n\t\t\treturn errors.New(\"SINGLE_MONGOS_LB_URI must be set when running against load balanced clusters\")\n\t\t}\n\t\ttestContext.singleMongosLoadBalancerURI, err = addNecessaryParamsToURI(singleMongosURI)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"error getting single mongos load balancer uri: %w\", err)\n\t\t}\n\n\t\tmultiMongosURI := os.Getenv(\"MULTI_MONGOS_LB_URI\")\n\t\tif multiMongosURI == \"\" {\n\t\t\treturn errors.New(\"MULTI_MONGOS_LB_URI must be set when running against load balanced clusters\")\n\t\t}\n\t\ttestContext.multiMongosLoadBalancerURI, err = addNecessaryParamsToURI(multiMongosURI)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"error getting multi mongos load balancer uri: %w\", err)\n\t\t}\n\t}\n\n\ttestContext.authEnabled = os.Getenv(\"AUTH\") == \"auth\"\n\ttestContext.sslEnabled = os.Getenv(\"SSL\") == \"ssl\"\n\tbiRes, err := testContext.client.Database(\"admin\").RunCommand(context.Background(), bson.D{{\"buildInfo\", 1}}).Raw()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"buildInfo error: %w\", err)\n\t}\n\tmodulesRaw, err := biRes.LookupErr(\"modules\")\n\tif err == nil {\n\t\t// older server versions don't report \"modules\" field in buildInfo result\n\t\tmodules, _ := modulesRaw.Array().Values()\n\t\tfor _, module := range modules {\n\t\t\tif module.StringValue() == \"enterprise\" {\n\t\t\t\ttestContext.enterpriseServer = true\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t}\n\n\t// Get server parameters if test is not running against ADL; ADL does not have \"getParameter\" command.\n\tif !testContext.dataLake {\n\t\tdb := testContext.client.Database(\"admin\")\n\t\ttestContext.serverParameters, err = db.RunCommand(context.Background(), bson.D{{\"getParameter\", \"*\"}}).Raw()\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"error getting serverParameters: %w\", err)\n\t\t}\n\t}\n\treturn nil\n}\n\n// Teardown cleans up resources initialized by Setup.\n// This function must be called once after all tests have finished running.\nfunc Teardown() error {\n\t// Dropping the test database causes an error against Atlas Data Lake.\n\tif !testContext.dataLake {\n\t\tif err := testContext.client.Database(TestDB).Drop(context.Background()); err != nil {\n\t\t\treturn fmt.Errorf(\"error dropping test database: %w\", err)\n\t\t}\n\t}\n\tif err := testContext.client.Disconnect(context.Background()); err != nil {\n\t\treturn fmt.Errorf(\"error disconnecting test client: %w\", err)\n\t}\n\tif err := testContext.topo.Disconnect(context.Background()); err != nil {\n\t\treturn fmt.Errorf(\"error disconnecting test topology: %w\", err)\n\t}\n\treturn nil\n}\n\nfunc getServerVersion() (string, error) {\n\tvar serverStatus bson.Raw\n\terr := testContext.client.Database(TestDB).RunCommand(\n\t\tcontext.Background(),\n\t\tbson.D{{\"buildInfo\", 1}},\n\t).Decode(&serverStatus)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\tversion, err := serverStatus.LookupErr(\"version\")\n\tif err != nil {\n\t\treturn \"\", errors.New(\"no version string in serverStatus response\")\n\t}\n\n\treturn version.StringValue(), nil\n}\n\n// addOptions appends connection string options to a URI.\nfunc addOptions(uri string, opts ...string) string {\n\tif !strings.ContainsRune(uri, '?') {\n\t\tif uri[len(uri)-1] != '/' {\n\t\t\turi += \"/\"\n\t\t}\n\n\t\turi += \"?\"\n\t} else {\n\t\turi += \"&\"\n\t}\n\n\tfor _, opt := range opts {\n\t\turi += opt\n\t}\n\n\treturn uri\n}\n\n// addTLSConfig checks for the environmental variable indicating that the tests are being run\n// on an SSL-enabled server, and if so, returns a new URI with the necessary configuration.\nfunc addTLSConfig(uri string) string {\n\tif os.Getenv(\"SSL\") == \"ssl\" {\n\t\turi = addOptions(uri, \"ssl=\", \"true\")\n\t}\n\tcaFile := os.Getenv(\"MONGO_GO_DRIVER_CA_FILE\")\n\tif len(caFile) == 0 {\n\t\treturn uri\n\t}\n\n\treturn addOptions(uri, \"sslCertificateAuthorityFile=\", caFile)\n}\n\n// addCompressors checks for the environment variable indicating that the tests are being run with compression\n// enabled. If so, it returns a new URI with the necessary configuration\nfunc addCompressors(uri string) string {\n\tcomp := os.Getenv(\"MONGO_GO_DRIVER_COMPRESSOR\")\n\tif len(comp) == 0 {\n\t\treturn uri\n\t}\n\n\treturn addOptions(uri, \"compressors=\", comp)\n}\n\nfunc addServerlessAuthCredentials(uri string) (string, error) {\n\tif os.Getenv(\"SERVERLESS\") != \"serverless\" {\n\t\treturn uri, nil\n\t}\n\tuser := os.Getenv(\"SERVERLESS_ATLAS_USER\")\n\tif user == \"\" {\n\t\treturn \"\", fmt.Errorf(\"serverless expects SERVERLESS_ATLAS_USER to be set\")\n\t}\n\tpassword := os.Getenv(\"SERVERLESS_ATLAS_PASSWORD\")\n\tif password == \"\" {\n\t\treturn \"\", fmt.Errorf(\"serverless expects SERVERLESS_ATLAS_PASSWORD to be set\")\n\t}\n\n\tvar scheme string\n\t// remove the scheme\n\tswitch {\n\tcase strings.HasPrefix(uri, \"mongodb+srv://\"):\n\t\tscheme = \"mongodb+srv://\"\n\tcase strings.HasPrefix(uri, \"mongodb://\"):\n\t\tscheme = \"mongodb://\"\n\tdefault:\n\t\treturn \"\", errors.New(`scheme must be \"mongodb\" or \"mongodb+srv\"`)\n\t}\n\n\turi = scheme + user + \":\" + password + \"@\" + uri[len(scheme):]\n\treturn uri, nil\n}\n\nfunc addNecessaryParamsToURI(uri string) (string, error) {\n\turi = addTLSConfig(uri)\n\turi = addCompressors(uri)\n\treturn addServerlessAuthCredentials(uri)\n}\n\n// CompareServerVersions compares two version number strings (i.e. positive integers separated by\n// periods). Comparisons are done to the lesser precision of the two versions. For example, 3.2 is\n// considered equal to 3.2.11, whereas 3.2.0 is considered less than 3.2.11.\n//\n// Returns a positive int if version1 is greater than version2, a negative int if version1 is less\n// than version2, and 0 if version1 is equal to version2.\nfunc CompareServerVersions(v1 string, v2 string) int {\n\tn1 := strings.Split(v1, \".\")\n\tn2 := strings.Split(v2, \".\")\n\n\tfor i := 0; i < int(math.Min(float64(len(n1)), float64(len(n2)))); i++ {\n\t\ti1, err := strconv.Atoi(n1[i])\n\t\tif err != nil {\n\t\t\treturn 1\n\t\t}\n\n\t\ti2, err := strconv.Atoi(n2[i])\n\t\tif err != nil {\n\t\t\treturn -1\n\t\t}\n\n\t\tdifference := i1 - i2\n\t\tif difference != 0 {\n\t\t\treturn difference\n\t\t}\n\t}\n\n\treturn 0\n}\n"
  },
  {
    "path": "internal/integration/mtest/setup_options.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage mtest\n\n// SetupOptions is the type used to configure mtest setup\ntype SetupOptions struct {\n\t// Specifies the URI to connect to. Defaults to URI based on the environment variables MONGODB_URI,\n\t// MONGO_GO_DRIVER_CA_FILE, and MONGO_GO_DRIVER_COMPRESSOR\n\tURI *string\n}\n\n// NewSetupOptions creates an empty SetupOptions struct\nfunc NewSetupOptions() *SetupOptions {\n\treturn &SetupOptions{}\n}\n\n// SetURI sets the uri to connect to\nfunc (so *SetupOptions) SetURI(uri string) *SetupOptions {\n\tso.URI = &uri\n\treturn so\n}\n"
  },
  {
    "path": "internal/integration/mtest/wiremessage_helpers.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage mtest\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/wiremessage\"\n)\n\nfunc copyBytes(original []byte) []byte {\n\tnewSlice := make([]byte, len(original))\n\tcopy(newSlice, original)\n\treturn newSlice\n}\n\n// assertMsgSectionType asserts that the next section type in the OP_MSG wire message is equal to the provided type.\n// It returns the remainder of the wire message and an error if the section type could not be read or was not equal\n// to the expected type.\nfunc assertMsgSectionType(wm []byte, expected wiremessage.SectionType) ([]byte, error) {\n\tvar actual wiremessage.SectionType\n\tvar ok bool\n\n\tactual, wm, ok = wiremessage.ReadMsgSectionType(wm)\n\tif !ok {\n\t\treturn wm, errors.New(\"failed to read section type\")\n\t}\n\tif expected != actual {\n\t\treturn wm, fmt.Errorf(\"unexpected section type %v; expected %v\", actual, expected)\n\t}\n\treturn wm, nil\n}\n\nfunc parseOpCompressed(wm []byte) (wiremessage.OpCode, []byte, error) {\n\t// Store the original opcode to forward to another parser later.\n\toriginalOpcode, wm, ok := wiremessage.ReadCompressedOriginalOpCode(wm)\n\tif !ok {\n\t\treturn originalOpcode, nil, errors.New(\"failed to read original opcode\")\n\t}\n\n\tuncompressedSize, wm, ok := wiremessage.ReadCompressedUncompressedSize(wm)\n\tif !ok {\n\t\treturn originalOpcode, nil, errors.New(\"failed to read uncompressed size\")\n\t}\n\n\tcompressorID, compressedMsg, ok := wiremessage.ReadCompressedCompressorID(wm)\n\tif !ok {\n\t\treturn originalOpcode, nil, errors.New(\"failed to read compressor ID\")\n\t}\n\n\topts := driver.CompressionOpts{\n\t\tCompressor:       compressorID,\n\t\tUncompressedSize: uncompressedSize,\n\t}\n\tdecompressed, err := driver.DecompressPayload(compressedMsg, opts)\n\tif err != nil {\n\t\treturn originalOpcode, nil, fmt.Errorf(\"error decompressing payload: %w\", err)\n\t}\n\n\treturn originalOpcode, decompressed, nil\n}\n"
  },
  {
    "path": "internal/integration/primary_stepdown_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage integration\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/eventtest\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/failpoint\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/integration/mtest\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n)\n\nconst (\n\terrorNotPrimary            int32 = 10107\n\terrorShutdownInProgress    int32 = 91\n\terrorInterruptedAtShutdown int32 = 11600\n)\n\nfunc TestConnectionsSurvivePrimaryStepDown(t *testing.T) {\n\tmt := mtest.New(t, mtest.NewOptions().Topologies(mtest.ReplicaSet).CreateClient(false))\n\n\tgetMoreOpts := mtest.NewOptions().MinServerVersion(\"4.2\")\n\tmt.RunOpts(\"getMore iteration\", getMoreOpts, func(mt *mtest.T) {\n\t\ttpm := eventtest.NewTestPoolMonitor()\n\t\tmt.ResetClient(options.Client().\n\t\t\tSetRetryWrites(false).\n\t\t\tSetPoolMonitor(tpm.PoolMonitor))\n\n\t\tinitCollection(mt, mt.Coll)\n\t\tcur, err := mt.Coll.Find(context.Background(), bson.D{}, options.Find().SetBatchSize(2))\n\t\tassert.Nil(mt, err, \"Find error: %v\", err)\n\t\tdefer cur.Close(context.Background())\n\t\tassert.True(mt, cur.Next(context.Background()), \"expected Next true, got false\")\n\n\t\t// replSetStepDown can fail with transient errors, so we use executeAdminCommandWithRetry to handle them and\n\t\t// retry until a timeout is hit.\n\t\tstepDownCmd := bson.D{\n\t\t\t{\"replSetStepDown\", 5},\n\t\t\t{\"force\", true},\n\t\t}\n\t\tstepDownOpts := options.RunCmd().SetReadPreference(mtest.PrimaryRp)\n\t\texecuteAdminCommandWithRetry(mt, mt.Client, stepDownCmd, stepDownOpts)\n\n\t\tassert.True(mt, cur.Next(context.Background()), \"expected Next true, got false\")\n\t\tassert.False(mt, tpm.IsPoolCleared(), \"expected pool to not be cleared but was\")\n\t})\n\tmt.RunOpts(\"server errors\", noClientOpts, func(mt *mtest.T) {\n\t\ttestCases := []struct {\n\t\t\tname        string\n\t\t\terrCode     int32\n\t\t\tpoolCleared bool\n\t\t}{\n\t\t\t{\"notPrimary keep pool\", errorNotPrimary, false},\n\t\t\t{\"shutdown in progress reset pool\", errorShutdownInProgress, true},\n\t\t\t{\"interrupted at shutdown reset pool\", errorInterruptedAtShutdown, true},\n\t\t}\n\t\tfor _, tc := range testCases {\n\t\t\tmt.Run(tc.name, func(mt *mtest.T) {\n\t\t\t\ttpm := eventtest.NewTestPoolMonitor()\n\t\t\t\tmt.ResetClient(options.Client().\n\t\t\t\t\tSetRetryWrites(false).\n\t\t\t\t\tSetPoolMonitor(tpm.PoolMonitor).\n\t\t\t\t\t// Use a low heartbeat frequency so the Client will quickly recover when using\n\t\t\t\t\t// failpoints that cause SDAM state changes.\n\t\t\t\t\tSetHeartbeatInterval(defaultHeartbeatInterval))\n\n\t\t\t\tmt.SetFailPoint(failpoint.FailPoint{\n\t\t\t\t\tConfigureFailPoint: \"failCommand\",\n\t\t\t\t\tMode: failpoint.Mode{\n\t\t\t\t\t\tTimes: 1,\n\t\t\t\t\t},\n\t\t\t\t\tData: failpoint.Data{\n\t\t\t\t\t\tFailCommands: []string{\"insert\"},\n\t\t\t\t\t\tErrorCode:    tc.errCode,\n\t\t\t\t\t},\n\t\t\t\t})\n\n\t\t\t\t_, err := mt.Coll.InsertOne(context.Background(), bson.D{{\"test\", 1}})\n\t\t\t\tassert.NotNil(mt, err, \"expected InsertOne error, got nil\")\n\t\t\t\tcerr, ok := err.(mongo.CommandError)\n\t\t\t\tassert.True(mt, ok, \"expected error type %v, got %v\", mongo.CommandError{}, err)\n\t\t\t\tassert.Equal(mt, tc.errCode, cerr.Code, \"expected error code %v, got %v\", tc.errCode, cerr.Code)\n\n\t\t\t\tif tc.poolCleared {\n\t\t\t\t\tassert.True(mt, tpm.IsPoolCleared(), \"expected pool to be cleared but was not\")\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\t// if pool shouldn't be cleared, another operation should succeed\n\t\t\t\t_, err = mt.Coll.InsertOne(context.Background(), bson.D{{\"test\", 1}})\n\t\t\t\tassert.Nil(mt, err, \"InsertOne error: %v\", err)\n\t\t\t\tassert.False(mt, tpm.IsPoolCleared(), \"expected pool to not be cleared but was\")\n\t\t\t})\n\t\t}\n\t})\n}\n"
  },
  {
    "path": "internal/integration/retryable_reads_prose_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2022-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage integration\n\nimport (\n\t\"context\"\n\t\"sync\"\n\t\"testing\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/event\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/eventtest\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/failpoint\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/integration/mtest\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/mongoutil\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/require\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/readpref\"\n)\n\nfunc TestRetryableReadsProse(t *testing.T) {\n\ttpm := eventtest.NewTestPoolMonitor()\n\n\t// Client options with MaxPoolSize of 1 and RetryReads used per the test description.\n\t// Lower HeartbeatInterval used to speed the test up for any server that uses streaming\n\t// heartbeats. Only connect to first host in list for sharded clusters.\n\thosts := mtest.ClusterConnString().Hosts\n\tclientOpts := options.Client().SetMaxPoolSize(1).SetRetryReads(true).\n\t\tSetPoolMonitor(tpm.PoolMonitor).SetHeartbeatInterval(500 * time.Millisecond).\n\t\tSetHosts(hosts[:1])\n\n\tmt := mtest.New(t, mtest.NewOptions().ClientOptions(clientOpts))\n\n\tmtOpts := mtest.NewOptions().\n\t\tMinServerVersion(\"4.3\").\n\t\t// Load-balanced topologies have a different behavior for clearing the\n\t\t// pool, so don't run the test on load-balanced topologies\n\t\t//\n\t\t// TODO(GODRIVER-3328): FailPoints are not currently reliable on sharded\n\t\t// topologies. Allow running on sharded topologies once that is fixed.\n\t\tTopologies(mtest.Single, mtest.ReplicaSet)\n\tmt.RunOpts(\"PoolClearedError retryability\", mtOpts, func(mt *mtest.T) {\n\t\t// Insert a document to test collection.\n\t\t_, err := mt.Coll.InsertOne(context.Background(), bson.D{{\"x\", 1}})\n\t\tassert.Nil(mt, err, \"InsertOne error: %v\", err)\n\n\t\t// Force Find to block for 1 second once.\n\t\tmt.SetFailPoint(failpoint.FailPoint{\n\t\t\tConfigureFailPoint: \"failCommand\",\n\t\t\tMode: failpoint.Mode{\n\t\t\t\tTimes: 1,\n\t\t\t},\n\t\t\tData: failpoint.Data{\n\t\t\t\tFailCommands:    []string{\"find\"},\n\t\t\t\tErrorCode:       91,\n\t\t\t\tBlockConnection: true,\n\t\t\t\tBlockTimeMS:     1000,\n\t\t\t},\n\t\t})\n\n\t\t// Clear CMAP and command events.\n\t\ttpm.ClearEvents()\n\t\tmt.ClearEvents()\n\n\t\t// Perform a FindOne on two different threads and assert both operations are\n\t\t// successful.\n\t\tvar wg sync.WaitGroup\n\t\tfor i := 0; i < 2; i++ {\n\t\t\twg.Add(1)\n\t\t\tgo func() {\n\t\t\t\tdefer wg.Done()\n\t\t\t\tres := mt.Coll.FindOne(context.Background(), bson.D{})\n\t\t\t\tassert.Nil(mt, res.Err())\n\t\t\t}()\n\t\t}\n\t\twg.Wait()\n\n\t\t// Gather ConnectionCheckedOut, ConnectionCheckOutFailed and PoolCleared pool events.\n\t\tevents := tpm.Events(func(e *event.PoolEvent) bool {\n\t\t\tconnectionCheckedOut := e.Type == event.ConnectionCheckedOut\n\t\t\tconnectionCheckOutFailed := e.Type == event.ConnectionCheckOutFailed\n\t\t\tpoolCleared := e.Type == event.ConnectionPoolCleared\n\t\t\treturn connectionCheckedOut || connectionCheckOutFailed || poolCleared\n\t\t})\n\n\t\t// Assert that first check out succeeds, pool is cleared, and second check\n\t\t// out fails due to connection error.\n\t\tassert.True(mt, len(events) >= 3, \"expected at least 3 events, got %v\", len(events))\n\t\tassert.Equal(mt, event.ConnectionCheckedOut, events[0].Type,\n\t\t\t\"expected ConnectionCheckedOut event, got %v\", events[0].Type)\n\t\tassert.Equal(mt, event.ConnectionPoolCleared, events[1].Type,\n\t\t\t\"expected ConnectionPoolCleared event, got %v\", events[1].Type)\n\t\tassert.Equal(mt, event.ConnectionCheckOutFailed, events[2].Type,\n\t\t\t\"expected ConnectionCheckedOutFailed event, got %v\", events[2].Type)\n\t\tassert.Equal(mt, event.ReasonConnectionErrored, events[2].Reason,\n\t\t\t\"expected check out failure due to connection error, failed due to %q\", events[2].Reason)\n\n\t\t// Assert that three find CommandStartedEvents were observed.\n\t\tfor i := 0; i < 3; i++ {\n\t\t\tcmdEvt := mt.GetStartedEvent()\n\t\t\tassert.NotNil(mt, cmdEvt, \"expected a find event, got nil\")\n\t\t\tassert.Equal(mt, cmdEvt.CommandName, \"find\",\n\t\t\t\t\"expected a find event, got a(n) %v event\", cmdEvt.CommandName)\n\t\t}\n\t})\n\n\tmtOpts = mtest.NewOptions().Topologies(mtest.Sharded).MinServerVersion(\"4.2\").AllowFailPointsOnSharded()\n\tmt.RunOpts(\"retrying in sharded cluster\", mtOpts, func(mt *mtest.T) {\n\t\ttests := []struct {\n\t\t\tname string\n\n\t\t\t// Note that setting this value greater than 2 will result in false\n\t\t\t// negatives. The current specification does not account for CSOT, which\n\t\t\t// might allow for an \"infinite\" number of retries over a period of time.\n\t\t\t// Because of this, we only track the \"previous server\".\n\t\t\thostCount            int\n\t\t\tfailpointErrorCode   int32\n\t\t\texpectedFailCount    int\n\t\t\texpectedSuccessCount int\n\t\t}{\n\t\t\t{\n\t\t\t\tname:                 \"retry on different mongos\",\n\t\t\t\thostCount:            2,\n\t\t\t\tfailpointErrorCode:   6, // HostUnreachable\n\t\t\t\texpectedFailCount:    2,\n\t\t\t\texpectedSuccessCount: 0,\n\t\t\t},\n\t\t\t{\n\t\t\t\tname:                 \"retry on same mongos\",\n\t\t\t\thostCount:            1,\n\t\t\t\tfailpointErrorCode:   6, // HostUnreachable\n\t\t\t\texpectedFailCount:    1,\n\t\t\t\texpectedSuccessCount: 1,\n\t\t\t},\n\t\t}\n\n\t\tfor _, tc := range tests {\n\t\t\tmt.Run(tc.name, func(mt *mtest.T) {\n\t\t\t\thosts, err := mongoutil.HostsFromURI(mtest.ClusterURI())\n\n\t\t\t\trequire.NoError(mt, err)\n\t\t\t\trequire.GreaterOrEqualf(mt, len(hosts), tc.hostCount,\n\t\t\t\t\t\"test cluster must have at least %v mongos hosts\", tc.hostCount)\n\n\t\t\t\t// Configure the failpoint options for each mongos.\n\t\t\t\tfailPoint := failpoint.FailPoint{\n\t\t\t\t\tConfigureFailPoint: \"failCommand\",\n\t\t\t\t\tMode: failpoint.Mode{\n\t\t\t\t\t\tTimes: 1,\n\t\t\t\t\t},\n\t\t\t\t\tData: failpoint.Data{\n\t\t\t\t\t\tFailCommands:    []string{\"find\"},\n\t\t\t\t\t\tErrorCode:       tc.failpointErrorCode,\n\t\t\t\t\t\tCloseConnection: false,\n\t\t\t\t\t},\n\t\t\t\t}\n\n\t\t\t\t// In order to ensure that each mongos in the hostCount-many mongos\n\t\t\t\t// hosts are tried at least once (i.e. failures are deprioritized), we\n\t\t\t\t// set a failpoint on all mongos hosts. The idea is that if we get\n\t\t\t\t// hostCount-many failures, then by the pigeonhole principal all mongos\n\t\t\t\t// hosts must have been tried.\n\t\t\t\tfor i := 0; i < tc.hostCount; i++ {\n\t\t\t\t\tmt.ResetClient(options.Client().SetHosts([]string{hosts[i]}))\n\t\t\t\t\tmt.SetFailPoint(failPoint)\n\n\t\t\t\t\t// The automatic failpoint clearing may not clear failpoints set on\n\t\t\t\t\t// specific hosts, so manually clear the failpoint we set on the\n\t\t\t\t\t// specific mongos when the test is done.\n\t\t\t\t\tdefer mt.ResetClient(options.Client().SetHosts([]string{hosts[i]}))\n\t\t\t\t\tdefer mt.ClearFailPoints()\n\t\t\t\t}\n\n\t\t\t\tfailCount := 0\n\t\t\t\tsuccessCount := 0\n\n\t\t\t\tcommandMonitor := &event.CommandMonitor{\n\t\t\t\t\tFailed: func(context.Context, *event.CommandFailedEvent) {\n\t\t\t\t\t\tfailCount++\n\t\t\t\t\t},\n\t\t\t\t\tSucceeded: func(context.Context, *event.CommandSucceededEvent) {\n\t\t\t\t\t\tsuccessCount++\n\t\t\t\t\t},\n\t\t\t\t}\n\n\t\t\t\t// Reset the client with exactly hostCount-many mongos hosts.\n\t\t\t\tmt.ResetClient(options.Client().\n\t\t\t\t\tSetHosts(hosts[:tc.hostCount]).\n\t\t\t\t\tSetRetryReads(true).\n\t\t\t\t\tSetMonitor(commandMonitor))\n\n\t\t\t\tmt.Coll.FindOne(context.Background(), bson.D{})\n\n\t\t\t\tassert.Equal(mt, tc.expectedFailCount, failCount)\n\t\t\t\tassert.Equal(mt, tc.expectedSuccessCount, successCount)\n\t\t\t})\n\t\t}\n\t})\n\n\tmtOpts = mtest.NewOptions().Topologies(mtest.ReplicaSet).MinServerVersion(\"4.4\").CreateClient(false)\n\tmt.RunOpts(\"retrying reads in a replica set\", mtOpts, func(mt *mtest.T) {\n\t\ttests := []struct {\n\t\t\tname              string\n\t\t\terrLabels         []string\n\t\t\tisServerIdentical bool\n\t\t}{\n\t\t\t{\n\t\t\t\tname:              \"overload errors\",\n\t\t\t\terrLabels:         []string{\"RetryableError\", \"SystemOverloadedError\"},\n\t\t\t\tisServerIdentical: false,\n\t\t\t},\n\t\t\t{\n\t\t\t\tname:              \"non-overload errors\",\n\t\t\t\terrLabels:         []string{\"RetryableError\"},\n\t\t\t\tisServerIdentical: true,\n\t\t\t},\n\t\t}\n\n\t\tclientOpts := options.Client().SetRetryReads(true).SetReadPreference(readpref.PrimaryPreferred())\n\t\tfor _, tc := range tests {\n\t\t\tmt.RunOpts(tc.name, mtest.NewOptions().ClientOptions(clientOpts), func(mt *mtest.T) {\n\t\t\t\tfailPoint := failpoint.FailPoint{\n\t\t\t\t\tConfigureFailPoint: \"failCommand\",\n\t\t\t\t\tMode: failpoint.Mode{\n\t\t\t\t\t\tTimes: 1,\n\t\t\t\t\t},\n\t\t\t\t\tData: failpoint.Data{\n\t\t\t\t\t\tFailCommands: []string{\"find\"},\n\t\t\t\t\t\tErrorLabels:  &tc.errLabels,\n\t\t\t\t\t\tErrorCode:    6,\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t\tmt.SetFailPoint(failPoint)\n\t\t\t\tmt.ClearEvents()\n\n\t\t\t\t_ = mt.Coll.FindOne(context.Background(), bson.D{})\n\t\t\t\tsucceeded := mt.GetAllSucceededEvents()\n\t\t\t\trequire.Len(mt, succeeded, 1, \"expected exactly one succeeded attempt\")\n\t\t\t\tfailed := mt.GetAllFailedEvents()\n\t\t\t\trequire.Len(mt, failed, 1, \"expected exactly one failed attempt\")\n\t\t\t\twanted := \"different\"\n\t\t\t\tif tc.isServerIdentical {\n\t\t\t\t\twanted = \"identical\"\n\t\t\t\t}\n\t\t\t\tassert.Equal(mt, tc.isServerIdentical, failed[0].ConnectionID == succeeded[0].ConnectionID,\n\t\t\t\t\t\"expected failed and succeeded events to have %s connection IDs, got %v and %v\", wanted,\n\t\t\t\t\tfailed[0].ConnectionID, succeeded[0].ConnectionID)\n\t\t\t})\n\t\t}\n\t})\n}\n"
  },
  {
    "path": "internal/integration/retryable_writes_prose_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage integration\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"fmt\"\n\t\"strings\"\n\t\"sync\"\n\t\"testing\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/event\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/eventtest\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/failpoint\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/integration/mtest\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/mongoutil\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/require\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver\"\n)\n\nfunc TestRetryableWritesProse(t *testing.T) {\n\tclientOpts := options.Client().SetRetryWrites(true).SetWriteConcern(mtest.MajorityWc).\n\t\tSetReadConcern(mtest.MajorityRc)\n\tmtOpts := mtest.NewOptions().ClientOptions(clientOpts).MinServerVersion(\"3.6\").CreateClient(false)\n\tmt := mtest.New(t, mtOpts)\n\n\tincludeOpts := mtest.NewOptions().Topologies(mtest.ReplicaSet, mtest.Sharded).CreateClient(false)\n\tmt.RunOpts(\"txn number included\", includeOpts, func(mt *mtest.T) {\n\t\tupdateDoc := bson.D{{\"$inc\", bson.D{{\"x\", 1}}}}\n\t\tinsertOneDoc := bson.D{{\"x\", 1}}\n\t\tinsertManyOrderedArgs := bson.D{\n\t\t\t{\"options\", bson.D{{\"ordered\", true}}},\n\t\t\t{\"documents\", []any{insertOneDoc}},\n\t\t}\n\t\tinsertManyUnorderedArgs := bson.D{\n\t\t\t{\"options\", bson.D{{\"ordered\", true}}},\n\t\t\t{\"documents\", []any{insertOneDoc}},\n\t\t}\n\n\t\ttestCases := []struct {\n\t\t\toperationName   string\n\t\t\targs            bson.D\n\t\t\texpectTxnNumber bool\n\t\t}{\n\t\t\t{\"deleteOne\", bson.D{}, true},\n\t\t\t{\"deleteMany\", bson.D{}, false},\n\t\t\t{\"updateOne\", bson.D{{\"update\", updateDoc}}, true},\n\t\t\t{\"updateMany\", bson.D{{\"update\", updateDoc}}, false},\n\t\t\t{\"replaceOne\", bson.D{}, true},\n\t\t\t{\"insertOne\", bson.D{{\"document\", insertOneDoc}}, true},\n\t\t\t{\"insertMany\", insertManyOrderedArgs, true},\n\t\t\t{\"insertMany\", insertManyUnorderedArgs, true},\n\t\t\t{\"findOneAndReplace\", bson.D{}, true},\n\t\t\t{\"findOneAndUpdate\", bson.D{{\"update\", updateDoc}}, true},\n\t\t\t{\"findOneAndDelete\", bson.D{}, true},\n\t\t}\n\t\tfor _, tc := range testCases {\n\t\t\tmt.Run(tc.operationName, func(mt *mtest.T) {\n\t\t\t\ttcArgs, err := bson.Marshal(tc.args)\n\t\t\t\tassert.Nil(mt, err, \"Marshal error: %v\", err)\n\t\t\t\tcrudOp := crudOperation{\n\t\t\t\t\tName:      tc.operationName,\n\t\t\t\t\tArguments: tcArgs,\n\t\t\t\t}\n\n\t\t\t\tmt.ClearEvents()\n\t\t\t\trunCrudOperation(mt, \"\", crudOp, crudOutcome{})\n\t\t\t\tstarted := mt.GetStartedEvent()\n\t\t\t\tassert.NotNil(mt, started, \"expected CommandStartedEvent, got nil\")\n\t\t\t\t_, err = started.Command.LookupErr(\"txnNumber\")\n\t\t\t\tif tc.expectTxnNumber {\n\t\t\t\t\tassert.Nil(mt, err, \"expected txnNumber in command %v\", started.Command)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tassert.NotNil(mt, err, \"did not expect txnNumber in command %v\", started.Command)\n\t\t\t})\n\t\t}\n\t})\n\terrorOpts := mtest.NewOptions().Topologies(mtest.ReplicaSet, mtest.Sharded)\n\tmt.RunOpts(\"wrap mmapv1 error\", errorOpts, func(mt *mtest.T) {\n\t\tres, err := mt.DB.RunCommand(context.Background(), bson.D{{\"serverStatus\", 1}}).Raw()\n\t\tassert.Nil(mt, err, \"serverStatus error: %v\", err)\n\t\tstorageEngine, ok := res.Lookup(\"storageEngine\", \"name\").StringValueOK()\n\t\tif !ok || storageEngine != \"mmapv1\" {\n\t\t\tmt.Skip(\"skipping because storage engine is not mmapv1\")\n\t\t}\n\n\t\t_, err = mt.Coll.InsertOne(context.Background(), bson.D{{\"x\", 1}})\n\t\tassert.Equal(mt, driver.ErrUnsupportedStorageEngine, err,\n\t\t\t\"expected error %v, got %v\", driver.ErrUnsupportedStorageEngine, err)\n\t})\n\n\tstandaloneOpts := mtest.NewOptions().Topologies(mtest.Single).CreateClient(false)\n\tmt.RunOpts(\"transaction number not sent on writes\", standaloneOpts, func(mt *mtest.T) {\n\t\tmt.Run(\"explicit session\", func(mt *mtest.T) {\n\t\t\t// Standalones do not support retryable writes and will error if a transaction number is sent\n\n\t\t\tsess, err := mt.Client.StartSession()\n\t\t\tassert.Nil(mt, err, \"StartSession error: %v\", err)\n\t\t\tdefer sess.EndSession(context.Background())\n\n\t\t\tmt.ClearEvents()\n\n\t\t\terr = mongo.WithSession(context.Background(), sess, func(ctx context.Context) error {\n\t\t\t\tdoc := bson.D{{\"foo\", 1}}\n\t\t\t\t_, err := mt.Coll.InsertOne(ctx, doc)\n\t\t\t\treturn err\n\t\t\t})\n\t\t\tassert.Nil(mt, err, \"InsertOne error: %v\", err)\n\n\t\t\t_, wantID := sess.ID().Lookup(\"id\").Binary()\n\t\t\tcommand := mt.GetStartedEvent().Command\n\t\t\tlsid, err := command.LookupErr(\"lsid\")\n\t\t\tassert.Nil(mt, err, \"Error getting lsid: %v\", err)\n\t\t\t_, gotID := lsid.Document().Lookup(\"id\").Binary()\n\t\t\tassert.True(mt, bytes.Equal(wantID, gotID), \"expected session ID %v, got %v\", wantID, gotID)\n\t\t\ttxnNumber, err := command.LookupErr(\"txnNumber\")\n\t\t\tassert.NotNil(mt, err, \"expected no txnNumber, got %v\", txnNumber)\n\t\t})\n\t\tmt.Run(\"implicit session\", func(mt *mtest.T) {\n\t\t\t// Standalones do not support retryable writes and will error if a transaction number is sent\n\n\t\t\tmt.ClearEvents()\n\n\t\t\tdoc := bson.D{{\"foo\", 1}}\n\t\t\t_, err := mt.Coll.InsertOne(context.Background(), doc)\n\t\t\tassert.Nil(mt, err, \"InsertOne error: %v\", err)\n\n\t\t\tcommand := mt.GetStartedEvent().Command\n\t\t\tlsid, err := command.LookupErr(\"lsid\")\n\t\t\tassert.Nil(mt, err, \"Error getting lsid: %v\", err)\n\t\t\t_, gotID := lsid.Document().Lookup(\"id\").Binary()\n\t\t\tassert.NotNil(mt, gotID, \"expected session ID, got nil\")\n\t\t\ttxnNumber, err := command.LookupErr(\"txnNumber\")\n\t\t\tassert.NotNil(mt, err, \"expected no txnNumber, got %v\", txnNumber)\n\t\t})\n\t})\n\n\ttpm := eventtest.NewTestPoolMonitor()\n\t// Client options with MaxPoolSize of 1 and RetryWrites used per the test description.\n\t// Lower HeartbeatInterval used to speed the test up for any server that uses streaming\n\t// heartbeats. Only connect to first host in list for sharded clusters.\n\thosts := mtest.ClusterConnString().Hosts\n\tpceOpts := options.Client().SetMaxPoolSize(1).SetRetryWrites(true).\n\t\tSetPoolMonitor(tpm.PoolMonitor).SetHeartbeatInterval(500 * time.Millisecond).\n\t\tSetHosts(hosts[:1])\n\n\tmtPceOpts := mtest.NewOptions().\n\t\tClientOptions(pceOpts).\n\t\tMinServerVersion(\"4.3\").\n\t\t// TODO(GODRIVER-3328): FailPoints are not currently reliable on sharded\n\t\t// topologies. Allow running on sharded topologies once that is fixed.\n\t\tTopologies(mtest.ReplicaSet)\n\tmt.RunOpts(\"PoolClearedError retryability\", mtPceOpts, func(mt *mtest.T) {\n\t\t// Force Find to block for 1 second once.\n\t\tmt.SetFailPoint(failpoint.FailPoint{\n\t\t\tConfigureFailPoint: \"failCommand\",\n\t\t\tMode: failpoint.Mode{\n\t\t\t\tTimes: 1,\n\t\t\t},\n\t\t\tData: failpoint.Data{\n\t\t\t\tFailCommands:    []string{\"insert\"},\n\t\t\t\tErrorCode:       91,\n\t\t\t\tBlockConnection: true,\n\t\t\t\tBlockTimeMS:     1000,\n\t\t\t\tErrorLabels:     &[]string{\"RetryableWriteError\"},\n\t\t\t},\n\t\t})\n\n\t\t// Clear CMAP and command events.\n\t\ttpm.ClearEvents()\n\t\tmt.ClearEvents()\n\n\t\t// Perform an InsertOne on two different threads and assert both operations are\n\t\t// successful.\n\t\tvar wg sync.WaitGroup\n\t\tfor i := 0; i < 2; i++ {\n\t\t\twg.Add(1)\n\t\t\tgo func() {\n\t\t\t\tdefer wg.Done()\n\t\t\t\t_, err := mt.Coll.InsertOne(context.Background(), bson.D{{\"x\", 1}})\n\t\t\t\tassert.Nil(mt, err, \"InsertOne error: %v\", err)\n\t\t\t}()\n\t\t}\n\t\twg.Wait()\n\n\t\t// Gather ConnectionCheckedOut, ConnectionCheckOutFailed and PoolCleared pool events.\n\t\tevents := tpm.Events(func(e *event.PoolEvent) bool {\n\t\t\tconnectionCheckedOut := e.Type == event.ConnectionCheckedOut\n\t\t\tconnectionCheckOutFailed := e.Type == event.ConnectionCheckOutFailed\n\t\t\tpoolCleared := e.Type == event.ConnectionPoolCleared\n\t\t\treturn connectionCheckedOut || connectionCheckOutFailed || poolCleared\n\t\t})\n\n\t\t// Assert that first check out succeeds, pool is cleared, and second check\n\t\t// out fails due to connection error.\n\t\tassert.True(mt, len(events) >= 3, \"expected at least 3 events, got %v\", len(events))\n\t\tassert.Equal(mt, event.ConnectionCheckedOut, events[0].Type,\n\t\t\t\"expected ConnectionCheckedOut event, got %v\", events[0].Type)\n\t\tassert.Equal(mt, event.ConnectionPoolCleared, events[1].Type,\n\t\t\t\"expected ConnectionPoolCleared event, got %v\", events[1].Type)\n\t\tassert.Equal(mt, event.ConnectionCheckOutFailed, events[2].Type,\n\t\t\t\"expected ConnectionCheckedOutFailed event, got %v\", events[2].Type)\n\t\tassert.Equal(mt, event.ReasonConnectionErrored, events[2].Reason,\n\t\t\t\"expected check out failure due to connection error, failed due to %q\", events[2].Reason)\n\n\t\t// Assert that three insert CommandStartedEvents were observed.\n\t\tfor i := 0; i < 3; i++ {\n\t\t\tcmdEvt := mt.GetStartedEvent()\n\t\t\tassert.NotNil(mt, cmdEvt, \"expected an insert event, got nil\")\n\t\t\tassert.Equal(mt, cmdEvt.CommandName, \"insert\",\n\t\t\t\t\"expected an insert event, got a(n) %v event\", cmdEvt.CommandName)\n\t\t}\n\t})\n\n\tmtNWPOpts := mtest.NewOptions().MinServerVersion(\"6.0\").Topologies(mtest.ReplicaSet)\n\tmt.RunOpts(fmt.Sprintf(\"%s label returns original error\", driver.NoWritesPerformed), mtNWPOpts,\n\t\tfunc(mt *mtest.T) {\n\t\t\tconst shutdownInProgressErrorCode int32 = 91\n\t\t\tconst notWritablePrimaryErrorCode int32 = 10107\n\n\t\t\tmonitor := new(event.CommandMonitor)\n\t\t\tmt.ResetClient(options.Client().SetRetryWrites(true).SetMonitor(monitor))\n\n\t\t\t// Configure a fail point for a \"ShutdownInProgress\" error.\n\t\t\tmt.SetFailPoint(failpoint.FailPoint{\n\t\t\t\tConfigureFailPoint: \"failCommand\",\n\t\t\t\tMode:               failpoint.Mode{Times: 1},\n\t\t\t\tData: failpoint.Data{\n\t\t\t\t\tWriteConcernError: &failpoint.WriteConcernError{\n\t\t\t\t\t\tCode: shutdownInProgressErrorCode,\n\t\t\t\t\t},\n\t\t\t\t\tFailCommands: []string{\"insert\"},\n\t\t\t\t},\n\t\t\t})\n\n\t\t\t// secondFailPointConfigured is used to determine if the conditions from the\n\t\t\t// shutdownInProgressErrorCode actually configures the \"NoWritablePrimary\" fail command.\n\t\t\tvar secondFailPointConfigured bool\n\n\t\t\t// Set a command monitor on the client that configures a failpoint with a \"NoWritesPerformed\"\n\t\t\tmonitor.Succeeded = func(_ context.Context, evt *event.CommandSucceededEvent) {\n\t\t\t\tvar errorCode int32\n\t\t\t\tif wce := evt.Reply.Lookup(\"writeConcernError\"); wce.Type == bson.TypeEmbeddedDocument {\n\t\t\t\t\tvar ok bool\n\t\t\t\t\terrorCode, ok = wce.Document().Lookup(\"code\").Int32OK()\n\t\t\t\t\tif !ok {\n\t\t\t\t\t\tt.Fatalf(\"expected code to be an int32, got %v\",\n\t\t\t\t\t\t\twce.Document().Lookup(\"code\").Type)\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Do not set a fail point if event was not a writeConcernError with an error code for\n\t\t\t\t// \"ShutdownInProgress\".\n\t\t\t\tif errorCode != shutdownInProgressErrorCode {\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tmt.SetFailPoint(failpoint.FailPoint{\n\t\t\t\t\tConfigureFailPoint: \"failCommand\",\n\t\t\t\t\tMode:               failpoint.Mode{Times: 1},\n\t\t\t\t\tData: failpoint.Data{\n\t\t\t\t\t\tErrorCode: notWritablePrimaryErrorCode,\n\t\t\t\t\t\tErrorLabels: &[]string{\n\t\t\t\t\t\t\tdriver.NoWritesPerformed,\n\t\t\t\t\t\t\tdriver.RetryableWriteError,\n\t\t\t\t\t\t},\n\t\t\t\t\t\tFailCommands: []string{\"insert\"},\n\t\t\t\t\t},\n\t\t\t\t})\n\t\t\t\tsecondFailPointConfigured = true\n\t\t\t}\n\n\t\t\t// Attempt to insert a document.\n\t\t\t_, err := mt.Coll.InsertOne(context.Background(), bson.D{{\"x\", 1}})\n\n\t\t\trequire.True(mt, secondFailPointConfigured)\n\n\t\t\t// Assert that the \"ShutdownInProgress\" error is returned.\n\t\t\trequire.True(mt, err.(mongo.WriteException).HasErrorCode(int(shutdownInProgressErrorCode)))\n\t\t})\n\n\tmtOpts = mtest.NewOptions().Topologies(mtest.Sharded).MinServerVersion(\"4.2\").AllowFailPointsOnSharded()\n\tmt.RunOpts(\"retrying in sharded cluster\", mtOpts, func(mt *mtest.T) {\n\t\ttests := []struct {\n\t\t\tname string\n\n\t\t\t// Note that setting this value greater than 2 will result in false\n\t\t\t// negatives. The current specification does not account for CSOT, which\n\t\t\t// might allow for an \"infinite\" number of retries over a period of time.\n\t\t\t// Because of this, we only track the \"previous server\".\n\t\t\thostCount            int\n\t\t\tfailpointErrorCode   int32\n\t\t\texpectedFailCount    int\n\t\t\texpectedSuccessCount int\n\t\t}{\n\t\t\t{\n\t\t\t\tname:                 \"retry on different mongos\",\n\t\t\t\thostCount:            2,\n\t\t\t\tfailpointErrorCode:   6, // HostUnreachable\n\t\t\t\texpectedFailCount:    2,\n\t\t\t\texpectedSuccessCount: 0,\n\t\t\t},\n\t\t\t{\n\t\t\t\tname:                 \"retry on same mongos\",\n\t\t\t\thostCount:            1,\n\t\t\t\tfailpointErrorCode:   6, // HostUnreachable\n\t\t\t\texpectedFailCount:    1,\n\t\t\t\texpectedSuccessCount: 1,\n\t\t\t},\n\t\t}\n\n\t\tfor _, tc := range tests {\n\t\t\tmt.Run(tc.name, func(mt *mtest.T) {\n\t\t\t\thosts, err := mongoutil.HostsFromURI(mtest.ClusterURI())\n\n\t\t\t\trequire.NoError(mt, err)\n\t\t\t\trequire.GreaterOrEqualf(mt, len(hosts), tc.hostCount,\n\t\t\t\t\t\"test cluster must have at least %v mongos hosts\", tc.hostCount)\n\n\t\t\t\t// Configure the failpoint options for each mongos.\n\t\t\t\tfailPoint := failpoint.FailPoint{\n\t\t\t\t\tConfigureFailPoint: \"failCommand\",\n\t\t\t\t\tMode: failpoint.Mode{\n\t\t\t\t\t\tTimes: 1,\n\t\t\t\t\t},\n\t\t\t\t\tData: failpoint.Data{\n\t\t\t\t\t\tFailCommands:    []string{\"insert\"},\n\t\t\t\t\t\tErrorLabels:     &[]string{\"RetryableWriteError\"},\n\t\t\t\t\t\tErrorCode:       tc.failpointErrorCode,\n\t\t\t\t\t\tCloseConnection: false,\n\t\t\t\t\t},\n\t\t\t\t}\n\n\t\t\t\t// In order to ensure that each mongos in the hostCount-many mongos\n\t\t\t\t// hosts are tried at least once (i.e. failures are deprioritized), we\n\t\t\t\t// set a failpoint on all mongos hosts. The idea is that if we get\n\t\t\t\t// hostCount-many failures, then by the pigeonhole principal all mongos\n\t\t\t\t// hosts must have been tried.\n\t\t\t\tfor i := 0; i < tc.hostCount; i++ {\n\t\t\t\t\tmt.ResetClient(options.Client().SetHosts([]string{hosts[i]}))\n\t\t\t\t\tmt.SetFailPoint(failPoint)\n\n\t\t\t\t\t// The automatic failpoint clearing may not clear failpoints set on\n\t\t\t\t\t// specific hosts, so manually clear the failpoint we set on the\n\t\t\t\t\t// specific mongos when the test is done.\n\t\t\t\t\tdefer mt.ResetClient(options.Client().SetHosts([]string{hosts[i]}))\n\t\t\t\t\tdefer mt.ClearFailPoints()\n\t\t\t\t}\n\n\t\t\t\tfailCount := 0\n\t\t\t\tsuccessCount := 0\n\n\t\t\t\tcommandMonitor := &event.CommandMonitor{\n\t\t\t\t\tFailed: func(context.Context, *event.CommandFailedEvent) {\n\t\t\t\t\t\tfailCount++\n\t\t\t\t\t},\n\t\t\t\t\tSucceeded: func(context.Context, *event.CommandSucceededEvent) {\n\t\t\t\t\t\tsuccessCount++\n\t\t\t\t\t},\n\t\t\t\t}\n\n\t\t\t\t// Reset the client with exactly hostCount-many mongos hosts.\n\t\t\t\tmt.ResetClient(options.Client().\n\t\t\t\t\tSetHosts(hosts[:tc.hostCount]).\n\t\t\t\t\tSetRetryWrites(true).\n\t\t\t\t\tSetMonitor(commandMonitor))\n\n\t\t\t\t_, _ = mt.Coll.InsertOne(context.Background(), bson.D{})\n\n\t\t\t\tassert.Equal(mt, tc.expectedFailCount, failCount)\n\t\t\t\tassert.Equal(mt, tc.expectedSuccessCount, successCount)\n\t\t\t})\n\t\t}\n\t})\n}\n\ntype crudOperation struct {\n\tName      string   `bson:\"name\"`\n\tArguments bson.Raw `bson:\"arguments\"`\n}\n\ntype crudOutcome struct {\n\tError      bool               `bson:\"error\"` // only used by retryable writes tests\n\tResult     any                `bson:\"result\"`\n\tCollection *outcomeCollection `bson:\"collection\"`\n}\n\n// run a CRUD operation and verify errors and outcomes.\n// the test description is needed to see determine if the test is an aggregate with $out\nfunc runCrudOperation(mt *mtest.T, testDescription string, operation crudOperation, outcome crudOutcome) {\n\tswitch operation.Name {\n\tcase \"aggregate\":\n\t\tcursor, err := executeAggregate(mt, mt.Coll, nil, operation.Arguments)\n\t\tif outcome.Error {\n\t\t\tassert.NotNil(mt, err, \"expected Aggregate error, got nil\")\n\t\t\tverifyCrudError(mt, outcome, err)\n\t\t\tbreak\n\t\t}\n\t\tassert.Nil(mt, err, \"Aggregate error: %v\", err)\n\t\t// only verify cursor contents for pipelines without $out\n\t\tif !strings.Contains(testDescription, \"$out\") {\n\t\t\tverifyCursorResult(mt, cursor, outcome.Result)\n\t\t}\n\tcase \"bulkWrite\":\n\t\tres, err := executeBulkWrite(mt, nil, operation.Arguments)\n\t\tif outcome.Error {\n\t\t\tassert.NotNil(mt, err, \"expected BulkWrite error, got nil\")\n\t\t\tverifyCrudError(mt, outcome, err)\n\t\t\tbreak\n\t\t}\n\t\tassert.Nil(mt, err, \"BulkWrite error: %v\", err)\n\t\tverifyBulkWriteResult(mt, res, outcome.Result)\n\tcase \"count\":\n\t\tres, err := executeCountDocuments(mt, nil, operation.Arguments)\n\t\tif outcome.Error {\n\t\t\tassert.NotNil(mt, err, \"expected CountDocuments error, got nil\")\n\t\t\tverifyCrudError(mt, outcome, err)\n\t\t\tbreak\n\t\t}\n\t\tassert.Nil(mt, err, \"CountDocuments error: %v\", err)\n\t\tverifyCountResult(mt, res, outcome.Result)\n\tcase \"distinct\":\n\t\tres, err := executeDistinct(mt, nil, operation.Arguments)\n\t\tif outcome.Error {\n\t\t\tassert.NotNil(mt, err, \"expected Distinct error, got nil\")\n\t\t\tverifyCrudError(mt, outcome, err)\n\t\t\tbreak\n\t\t}\n\t\tassert.Nil(mt, err, \"Distinct error: %v\", err)\n\t\tverifyDistinctResult(mt, res, outcome.Result)\n\tcase \"find\":\n\t\tcursor, err := executeFind(mt, nil, operation.Arguments)\n\t\tif outcome.Error {\n\t\t\tassert.NotNil(mt, err, \"expected Find error, got nil\")\n\t\t\tverifyCrudError(mt, outcome, err)\n\t\t\tbreak\n\t\t}\n\t\tassert.Nil(mt, err, \"Find error: %v\", err)\n\t\tverifyCursorResult(mt, cursor, outcome.Result)\n\tcase \"deleteOne\":\n\t\tres, err := executeDeleteOne(mt, nil, operation.Arguments)\n\t\tif outcome.Error {\n\t\t\tassert.NotNil(mt, err, \"expected DeleteOne error, got nil\")\n\t\t\tverifyCrudError(mt, outcome, err)\n\t\t\tbreak\n\t\t}\n\t\tassert.Nil(mt, err, \"DeleteOne error: %v\", err)\n\t\tverifyDeleteResult(mt, res, outcome.Result)\n\tcase \"deleteMany\":\n\t\tres, err := executeDeleteMany(mt, nil, operation.Arguments)\n\t\tif outcome.Error {\n\t\t\tassert.NotNil(mt, err, \"expected DeleteMany error, got nil\")\n\t\t\tverifyCrudError(mt, outcome, err)\n\t\t\tbreak\n\t\t}\n\t\tassert.Nil(mt, err, \"DeleteMany error: %v\", err)\n\t\tverifyDeleteResult(mt, res, outcome.Result)\n\tcase \"findOneAndDelete\":\n\t\tres := executeFindOneAndDelete(mt, nil, operation.Arguments)\n\t\terr := res.Err()\n\t\tif outcome.Error {\n\t\t\tassert.NotNil(mt, err, \"expected FindOneAndDelete error, got nil\")\n\t\t\tverifyCrudError(mt, outcome, err)\n\t\t\tbreak\n\t\t}\n\t\tif outcome.Result == nil {\n\t\t\tassert.Equal(mt, mongo.ErrNoDocuments, err, \"expected error %v, got %v\", mongo.ErrNoDocuments, err)\n\t\t\tbreak\n\t\t}\n\t\tassert.Nil(mt, err, \"FindOneAndDelete error: %v\", err)\n\t\tverifySingleResult(mt, res, outcome.Result)\n\tcase \"findOneAndReplace\":\n\t\tres := executeFindOneAndReplace(mt, nil, operation.Arguments)\n\t\terr := res.Err()\n\t\tif outcome.Error {\n\t\t\tassert.NotNil(mt, err, \"expected FindOneAndReplace error, got nil\")\n\t\t\tverifyCrudError(mt, outcome, err)\n\t\t\tbreak\n\t\t}\n\t\tif outcome.Result == nil {\n\t\t\tassert.Equal(mt, mongo.ErrNoDocuments, err, \"expected error %v, got %v\", mongo.ErrNoDocuments, err)\n\t\t\tbreak\n\t\t}\n\t\tassert.Nil(mt, err, \"FindOneAndReplace error: %v\", err)\n\t\tverifySingleResult(mt, res, outcome.Result)\n\tcase \"findOneAndUpdate\":\n\t\tres := executeFindOneAndUpdate(mt, nil, operation.Arguments)\n\t\terr := res.Err()\n\t\tif outcome.Error {\n\t\t\tassert.NotNil(mt, err, \"expected FindOneAndUpdate error, got nil\")\n\t\t\tverifyCrudError(mt, outcome, err)\n\t\t\tbreak\n\t\t}\n\t\tif outcome.Result == nil {\n\t\t\tassert.Equal(mt, mongo.ErrNoDocuments, err, \"expected error %v, got %v\", mongo.ErrNoDocuments, err)\n\t\t\tbreak\n\t\t}\n\t\tassert.Nil(mt, err, \"FindOneAndUpdate error: %v\", err)\n\t\tverifySingleResult(mt, res, outcome.Result)\n\tcase \"insertOne\":\n\t\tres, err := executeInsertOne(mt, nil, operation.Arguments)\n\t\tif outcome.Error {\n\t\t\tassert.NotNil(mt, err, \"expected InsertOne error, got nil\")\n\t\t\tverifyCrudError(mt, outcome, err)\n\t\t\tbreak\n\t\t}\n\t\tassert.Nil(mt, err, \"InsertOne error: %v\", err)\n\t\tverifyInsertOneResult(mt, res, outcome.Result)\n\tcase \"insertMany\":\n\t\tres, err := executeInsertMany(mt, nil, operation.Arguments)\n\t\tif outcome.Error {\n\t\t\tassert.NotNil(mt, err, \"expected InsertMany error, got nil\")\n\t\t\tverifyCrudError(mt, outcome, err)\n\t\t\tbreak\n\t\t}\n\t\tassert.Nil(mt, err, \"InsertMany error: %v\", err)\n\t\tverifyInsertManyResult(mt, res, outcome.Result)\n\tcase \"replaceOne\":\n\t\tres, err := executeReplaceOne(mt, nil, operation.Arguments)\n\t\tif outcome.Error {\n\t\t\tassert.NotNil(mt, err, \"expected ReplaceOne error, got nil\")\n\t\t\tverifyCrudError(mt, outcome, err)\n\t\t\tbreak\n\t\t}\n\t\tassert.Nil(mt, err, \"ReplaceOne error: %v\", err)\n\t\tverifyUpdateResult(mt, res, outcome.Result)\n\tcase \"updateOne\":\n\t\tres, err := executeUpdateOne(mt, nil, operation.Arguments)\n\t\tif outcome.Error {\n\t\t\tassert.NotNil(mt, err, \"expected UpdateOne error, got nil\")\n\t\t\tverifyCrudError(mt, outcome, err)\n\t\t\tbreak\n\t\t}\n\t\tassert.Nil(mt, err, \"UpdateOne error: %v\", err)\n\t\tverifyUpdateResult(mt, res, outcome.Result)\n\tcase \"updateMany\":\n\t\tres, err := executeUpdateMany(mt, nil, operation.Arguments)\n\t\tif outcome.Error {\n\t\t\tassert.NotNil(mt, err, \"expected UpdateMany error, got nil\")\n\t\t\tverifyCrudError(mt, outcome, err)\n\t\t\tbreak\n\t\t}\n\t\tassert.Nil(mt, err, \"UpdateMany error: %v\", err)\n\t\tverifyUpdateResult(mt, res, outcome.Result)\n\tcase \"estimatedDocumentCount\":\n\t\tres, err := executeEstimatedDocumentCount(mt, nil, operation.Arguments)\n\t\tif outcome.Error {\n\t\t\tassert.NotNil(mt, err, \"expected EstimatedDocumentCount error, got nil\")\n\t\t\tverifyCrudError(mt, outcome, err)\n\t\t\tbreak\n\t\t}\n\t\tassert.Nil(mt, err, \"EstimatedDocumentCount error: %v\", err)\n\t\tverifyCountResult(mt, res, outcome.Result)\n\tcase \"countDocuments\":\n\t\tres, err := executeCountDocuments(mt, nil, operation.Arguments)\n\t\tif outcome.Error {\n\t\t\tassert.NotNil(mt, err, \"expected CountDocuments error, got nil\")\n\t\t\tverifyCrudError(mt, outcome, err)\n\t\t\tbreak\n\t\t}\n\t\tassert.Nil(mt, err, \"CountDocuments error: %v\", err)\n\t\tverifyCountResult(mt, res, outcome.Result)\n\tdefault:\n\t\tmt.Fatalf(\"unrecognized operation: %v\", operation.Name)\n\t}\n\n\tif outcome.Collection != nil {\n\t\tverifyTestOutcome(mt, outcome.Collection)\n\t}\n}\n\nfunc verifyCrudError(mt *mtest.T, outcome crudOutcome, err error) {\n\topError := errorFromResult(mt, outcome.Result)\n\tif opError == nil {\n\t\treturn\n\t}\n\tverificationErr := verifyError(opError, err)\n\tassert.Nil(mt, verificationErr, \"error mismatch: %v\", verificationErr)\n}\n"
  },
  {
    "path": "internal/integration/sdam_error_handling_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\n//go:build go1.13\n\npackage integration\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"testing\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/eventtest\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/failpoint\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/integration/mtest\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n)\n\nfunc TestSDAMErrorHandling(t *testing.T) {\n\tmt := mtest.New(t, noClientOpts)\n\tbaseClientOpts := func() *options.ClientOptions {\n\t\treturn options.Client().\n\t\t\tApplyURI(mtest.ClusterURI()).\n\t\t\tSetRetryWrites(false).\n\t\t\tSetWriteConcern(mtest.MajorityWc)\n\t}\n\tbaseMtOpts := func() *mtest.Options {\n\t\tmtOpts := mtest.NewOptions().\n\t\t\tTopologies(mtest.ReplicaSet, mtest.Single). // Don't run on sharded clusters to avoid complexity of sharded failpoints.\n\t\t\tClientOptions(baseClientOpts())\n\n\t\tif mtest.ClusterTopologyKind() == mtest.Sharded {\n\t\t\t// Pin to a single mongos because the tests use failpoints.\n\t\t\tmtOpts.ClientType(mtest.Pinned)\n\t\t}\n\t\treturn mtOpts\n\t}\n\n\t// Set min server version of 4.4 because the during-handshake tests use failpoint features introduced in 4.4 like\n\t// blockConnection and appName.\n\tmt.RunOpts(\"before handshake completes\", baseMtOpts().Auth(true).MinServerVersion(\"4.4\"), func(mt *mtest.T) {\n\t\tmt.RunOpts(\"network errors\", noClientOpts, func(mt *mtest.T) {\n\t\t\tmt.Run(\"pool cleared on network timeout\", func(mt *mtest.T) {\n\t\t\t\t// Assert that the pool is cleared when a connection created by an application\n\t\t\t\t// operation thread encounters a timeout caused by socketTimeoutMS during\n\t\t\t\t// handshaking.\n\n\t\t\t\tappName := \"authConnectTimeoutTest\"\n\t\t\t\t// Set failpoint on saslContinue instead of saslStart because saslStart isn't done when using\n\t\t\t\t// speculative auth.\n\t\t\t\tmt.SetFailPoint(failpoint.FailPoint{\n\t\t\t\t\tConfigureFailPoint: \"failCommand\",\n\t\t\t\t\tMode: failpoint.Mode{\n\t\t\t\t\t\tTimes: 1,\n\t\t\t\t\t},\n\t\t\t\t\tData: failpoint.Data{\n\t\t\t\t\t\tFailCommands:    []string{\"saslContinue\"},\n\t\t\t\t\t\tBlockConnection: true,\n\t\t\t\t\t\tBlockTimeMS:     150,\n\t\t\t\t\t\tAppName:         appName,\n\t\t\t\t\t},\n\t\t\t\t})\n\n\t\t\t\t// Reset the client with the appName specified in the failpoint and the pool monitor.\n\t\t\t\ttpm := eventtest.NewTestPoolMonitor()\n\t\t\t\tmt.ResetClient(baseClientOpts().\n\t\t\t\t\tSetAppName(appName).\n\t\t\t\t\tSetPoolMonitor(tpm.PoolMonitor).\n\t\t\t\t\t// Set a 100ms connect timeout so that the saslContinue delay of 150ms\n\t\t\t\t\t// causes a timeout during a heartbeat (i.e. a timeout not caused by\n\t\t\t\t\t// the InsertOne context).\n\t\t\t\t\tSetConnectTimeout(100 * time.Millisecond))\n\n\t\t\t\t// Use context.Background() so that the new connection will not time out due to an\n\t\t\t\t// operation-scoped timeout.\n\t\t\t\t_, err := mt.Coll.InsertOne(context.Background(), bson.D{{\"test\", 1}})\n\t\t\t\tassert.NotNil(mt, err, \"expected InsertOne error, got nil\")\n\t\t\t\tassert.True(mt, mongo.IsTimeout(err), \"expected timeout error, got %v\", err)\n\t\t\t\tassert.True(mt, mongo.IsNetworkError(err), \"expected network error, got %v\", err)\n\n\t\t\t\t// Assert that the pool is cleared within 2 seconds.\n\t\t\t\tassert.Eventually(t,\n\t\t\t\t\ttpm.IsPoolCleared,\n\t\t\t\t\t2*time.Second,\n\t\t\t\t\t100*time.Millisecond,\n\t\t\t\t\t\"expected pool is cleared within 2 seconds\")\n\t\t\t})\n\n\t\t\tmt.RunOpts(\"pool cleared on non-timeout network error\", noClientOpts, func(mt *mtest.T) {\n\t\t\t\tmt.Run(\"background\", func(mt *mtest.T) {\n\t\t\t\t\t// Assert that the pool is cleared when a connection created by the background pool maintenance\n\t\t\t\t\t// routine encounters a non-timeout network error during handshaking.\n\t\t\t\t\tappName := \"authNetworkErrorTestBackground\"\n\n\t\t\t\t\tmt.SetFailPoint(failpoint.FailPoint{\n\t\t\t\t\t\tConfigureFailPoint: \"failCommand\",\n\t\t\t\t\t\tMode: failpoint.Mode{\n\t\t\t\t\t\t\tTimes: 1,\n\t\t\t\t\t\t},\n\t\t\t\t\t\tData: failpoint.Data{\n\t\t\t\t\t\t\tFailCommands:    []string{\"saslContinue\"},\n\t\t\t\t\t\t\tCloseConnection: true,\n\t\t\t\t\t\t\tAppName:         appName,\n\t\t\t\t\t\t},\n\t\t\t\t\t})\n\n\t\t\t\t\t// Reset the client with the appName specified in the failpoint.\n\t\t\t\t\ttpm := eventtest.NewTestPoolMonitor()\n\t\t\t\t\tmt.ResetClient(baseClientOpts().\n\t\t\t\t\t\tSetAppName(appName).\n\t\t\t\t\t\tSetPoolMonitor(tpm.PoolMonitor).\n\t\t\t\t\t\t// Set minPoolSize to enable the background pool maintenance goroutine.\n\t\t\t\t\t\tSetMinPoolSize(5))\n\n\t\t\t\t\t// Assert that the pool is cleared within 2 seconds.\n\t\t\t\t\tassert.Eventually(t,\n\t\t\t\t\t\ttpm.IsPoolCleared,\n\t\t\t\t\t\t2*time.Second,\n\t\t\t\t\t\t100*time.Millisecond,\n\t\t\t\t\t\t\"expected pool is cleared within 2 seconds\")\n\t\t\t\t})\n\n\t\t\t\tmt.Run(\"foreground\", func(mt *mtest.T) {\n\t\t\t\t\t// Assert that the pool is cleared when a connection created by an application thread connection\n\t\t\t\t\t// checkout encounters a non-timeout network error during handshaking.\n\t\t\t\t\tappName := \"authNetworkErrorTestForeground\"\n\n\t\t\t\t\tmt.SetFailPoint(failpoint.FailPoint{\n\t\t\t\t\t\tConfigureFailPoint: \"failCommand\",\n\t\t\t\t\t\tMode: failpoint.Mode{\n\t\t\t\t\t\t\tTimes: 1,\n\t\t\t\t\t\t},\n\t\t\t\t\t\tData: failpoint.Data{\n\t\t\t\t\t\t\tFailCommands:    []string{\"saslContinue\"},\n\t\t\t\t\t\t\tCloseConnection: true,\n\t\t\t\t\t\t\tAppName:         appName,\n\t\t\t\t\t\t},\n\t\t\t\t\t})\n\n\t\t\t\t\t// Reset the client with the appName specified in the failpoint.\n\t\t\t\t\ttpm := eventtest.NewTestPoolMonitor()\n\t\t\t\t\tmt.ResetClient(baseClientOpts().SetAppName(appName).SetPoolMonitor(tpm.PoolMonitor))\n\n\t\t\t\t\t_, err := mt.Coll.InsertOne(context.Background(), bson.D{{\"x\", 1}})\n\t\t\t\t\tassert.NotNil(mt, err, \"expected InsertOne error, got nil\")\n\t\t\t\t\tassert.False(mt, mongo.IsTimeout(err), \"expected non-timeout error, got %v\", err)\n\n\t\t\t\t\t// Assert that the pool is cleared within 2 seconds.\n\t\t\t\t\tassert.Eventually(t,\n\t\t\t\t\t\ttpm.IsPoolCleared,\n\t\t\t\t\t\t2*time.Second,\n\t\t\t\t\t\t100*time.Millisecond,\n\t\t\t\t\t\t\"expected pool is cleared within 2 seconds\")\n\t\t\t\t})\n\t\t\t})\n\t\t})\n\t})\n\tmt.RunOpts(\"after handshake completes\", baseMtOpts(), func(mt *mtest.T) {\n\t\tmt.RunOpts(\"network errors\", noClientOpts, func(mt *mtest.T) {\n\t\t\tmt.Run(\"pool cleared on non-timeout network error\", func(mt *mtest.T) {\n\t\t\t\tappName := \"afterHandshakeNetworkError\"\n\n\t\t\t\tmt.SetFailPoint(failpoint.FailPoint{\n\t\t\t\t\tConfigureFailPoint: \"failCommand\",\n\t\t\t\t\tMode: failpoint.Mode{\n\t\t\t\t\t\tTimes: 1,\n\t\t\t\t\t},\n\t\t\t\t\tData: failpoint.Data{\n\t\t\t\t\t\tFailCommands:    []string{\"insert\"},\n\t\t\t\t\t\tCloseConnection: true,\n\t\t\t\t\t\tAppName:         appName,\n\t\t\t\t\t},\n\t\t\t\t})\n\n\t\t\t\t// Reset the client with the appName specified in the failpoint.\n\t\t\t\ttpm := eventtest.NewTestPoolMonitor()\n\t\t\t\tmt.ResetClient(baseClientOpts().SetAppName(appName).SetPoolMonitor(tpm.PoolMonitor))\n\n\t\t\t\t_, err := mt.Coll.InsertOne(context.Background(), bson.D{{\"test\", 1}})\n\t\t\t\tassert.NotNil(mt, err, \"expected InsertOne error, got nil\")\n\t\t\t\tassert.False(mt, mongo.IsTimeout(err), \"expected non-timeout error, got %v\", err)\n\t\t\t\tassert.True(mt, tpm.IsPoolCleared(), \"expected pool to be cleared but was not\")\n\t\t\t})\n\t\t\tmt.Run(\"pool not cleared on timeout network error\", func(mt *mtest.T) {\n\t\t\t\ttpm := eventtest.NewTestPoolMonitor()\n\t\t\t\tmt.ResetClient(baseClientOpts().SetPoolMonitor(tpm.PoolMonitor))\n\n\t\t\t\t_, err := mt.Coll.InsertOne(context.Background(), bson.D{{\"x\", 1}})\n\t\t\t\tassert.Nil(mt, err, \"InsertOne error: %v\", err)\n\n\t\t\t\tfilter := bson.M{\n\t\t\t\t\t\"$where\": \"function() { sleep(1000); return false; }\",\n\t\t\t\t}\n\t\t\t\ttimeoutCtx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)\n\t\t\t\tdefer cancel()\n\t\t\t\t_, err = mt.Coll.Find(timeoutCtx, filter)\n\t\t\t\tassert.NotNil(mt, err, \"expected Find error, got %v\", err)\n\t\t\t\tassert.True(mt, mongo.IsTimeout(err), \"expected timeout error, got %v\", err)\n\t\t\t\tassert.False(mt, tpm.IsPoolCleared(), \"expected pool to not be cleared but was\")\n\t\t\t})\n\t\t\tmt.Run(\"pool not cleared on context cancellation\", func(mt *mtest.T) {\n\t\t\t\ttpm := eventtest.NewTestPoolMonitor()\n\t\t\t\tmt.ResetClient(baseClientOpts().SetPoolMonitor(tpm.PoolMonitor))\n\n\t\t\t\t_, err := mt.Coll.InsertOne(context.Background(), bson.D{{\"x\", 1}})\n\t\t\t\tassert.Nil(mt, err, \"InsertOne error: %v\", err)\n\n\t\t\t\tfindCtx, cancel := context.WithCancel(context.Background())\n\t\t\t\tgo func() {\n\t\t\t\t\ttime.Sleep(100 * time.Millisecond)\n\t\t\t\t\tcancel()\n\t\t\t\t}()\n\n\t\t\t\tfilter := bson.M{\n\t\t\t\t\t\"$where\": \"function() { sleep(1000); return false; }\",\n\t\t\t\t}\n\t\t\t\t_, err = mt.Coll.Find(findCtx, filter)\n\t\t\t\tassert.NotNil(mt, err, \"expected Find error, got nil\")\n\t\t\t\tassert.False(mt, mongo.IsTimeout(err), \"expected non-timeout error, got %v\", err)\n\t\t\t\tassert.True(mt, mongo.IsNetworkError(err), \"expected network error, got %v\", err)\n\t\t\t\tassert.True(mt, errors.Is(err, context.Canceled), \"expected error %v to be context.Canceled\", err)\n\t\t\t\tassert.False(mt, tpm.IsPoolCleared(), \"expected pool to not be cleared but was\")\n\t\t\t})\n\t\t})\n\t\tmt.RunOpts(\"server errors\", noClientOpts, func(mt *mtest.T) {\n\t\t\t// Integration tests for the SDAM error handling code path for errors in server response documents. These\n\t\t\t// errors can be part of the top-level document in ok:0 responses or in a nested writeConcernError document.\n\n\t\t\t// On 4.4, some state change errors include a topologyVersion field. Because we're triggering these errors\n\t\t\t// via failCommand, the topologyVersion does not actually change as it would in an actual state change.\n\t\t\t// This causes the SDAM error handling code path to think we've already handled this state change and\n\t\t\t// ignore the error because it's stale. To avoid this altogether, we cap the test to <= 4.2.\n\t\t\tserverErrorsMtOpts := baseMtOpts().\n\t\t\t\tMaxServerVersion(\"4.2\").\n\t\t\t\tClientOptions(baseClientOpts().SetRetryWrites(false))\n\n\t\t\ttestCases := []struct {\n\t\t\t\tname      string\n\t\t\t\terrorCode int32\n\n\t\t\t\t// For shutdown errors, the pool is always cleared. For non-shutdown errors, the pool is only cleared\n\t\t\t\t// for pre-4.2 servers.\n\t\t\t\tisShutdownError bool\n\t\t\t}{\n\t\t\t\t// \"node is recovering\" errors\n\t\t\t\t{\"InterruptedAtShutdown\", 11600, true},\n\t\t\t\t{\"InterruptedDueToReplStateChange, not shutdown\", 11602, false},\n\t\t\t\t{\"NotPrimaryOrSecondary\", 13436, false},\n\t\t\t\t{\"PrimarySteppedDown\", 189, false},\n\t\t\t\t{\"ShutdownInProgress\", 91, true},\n\n\t\t\t\t// \"not primary\" errors\n\t\t\t\t{\"NotPrimary\", 10107, false},\n\t\t\t\t{\"NotPrimaryNoSecondaryOk\", 13435, false},\n\t\t\t}\n\t\t\tfor _, tc := range testCases {\n\t\t\t\tmt.RunOpts(fmt.Sprintf(\"command error - %s\", tc.name), serverErrorsMtOpts, func(mt *mtest.T) {\n\t\t\t\t\tappName := fmt.Sprintf(\"command_error_%s\", tc.name)\n\n\t\t\t\t\t// Cause the next insert to fail with an ok:0 response.\n\t\t\t\t\tmt.SetFailPoint(failpoint.FailPoint{\n\t\t\t\t\t\tConfigureFailPoint: \"failCommand\",\n\t\t\t\t\t\tMode: failpoint.Mode{\n\t\t\t\t\t\t\tTimes: 1,\n\t\t\t\t\t\t},\n\t\t\t\t\t\tData: failpoint.Data{\n\t\t\t\t\t\t\tFailCommands: []string{\"insert\"},\n\t\t\t\t\t\t\tErrorCode:    tc.errorCode,\n\t\t\t\t\t\t\tAppName:      appName,\n\t\t\t\t\t\t},\n\t\t\t\t\t})\n\n\t\t\t\t\t// Reset the client with the appName specified in the failpoint.\n\t\t\t\t\ttpm := eventtest.NewTestPoolMonitor()\n\t\t\t\t\tmt.ResetClient(baseClientOpts().SetAppName(appName).SetPoolMonitor(tpm.PoolMonitor))\n\n\t\t\t\t\trunServerErrorsTest(mt, tc.isShutdownError, tpm)\n\t\t\t\t})\n\t\t\t\tmt.RunOpts(fmt.Sprintf(\"write concern error - %s\", tc.name), serverErrorsMtOpts, func(mt *mtest.T) {\n\t\t\t\t\tappName := fmt.Sprintf(\"write_concern_error_%s\", tc.name)\n\n\t\t\t\t\t// Cause the next insert to fail with a write concern error.\n\t\t\t\t\tmt.SetFailPoint(failpoint.FailPoint{\n\t\t\t\t\t\tConfigureFailPoint: \"failCommand\",\n\t\t\t\t\t\tMode: failpoint.Mode{\n\t\t\t\t\t\t\tTimes: 1,\n\t\t\t\t\t\t},\n\t\t\t\t\t\tData: failpoint.Data{\n\t\t\t\t\t\t\tFailCommands: []string{\"insert\"},\n\t\t\t\t\t\t\tWriteConcernError: &failpoint.WriteConcernError{\n\t\t\t\t\t\t\t\tCode: tc.errorCode,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tAppName: appName,\n\t\t\t\t\t\t},\n\t\t\t\t\t})\n\n\t\t\t\t\t// Reset the client with the appName specified in the failpoint.\n\t\t\t\t\ttpm := eventtest.NewTestPoolMonitor()\n\t\t\t\t\tmt.ResetClient(baseClientOpts().SetAppName(appName).SetPoolMonitor(tpm.PoolMonitor))\n\n\t\t\t\t\trunServerErrorsTest(mt, tc.isShutdownError, tpm)\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\t})\n}\n\nfunc runServerErrorsTest(mt *mtest.T, isShutdownError bool, tpm *eventtest.TestPoolMonitor) {\n\tmt.Helper()\n\n\t_, err := mt.Coll.InsertOne(context.Background(), bson.D{{\"x\", 1}})\n\tassert.NotNil(mt, err, \"expected InsertOne error, got nil\")\n\n\t// The pool should always be cleared for shutdown errors, regardless of server version.\n\tif isShutdownError {\n\t\tassert.True(mt, tpm.IsPoolCleared(), \"expected pool to be cleared, but was not\")\n\t\treturn\n\t}\n\n\t// For non-shutdown errors, the pool is only cleared if the error is from a pre-4.2 server.\n\twantCleared := mtest.CompareServerVersions(mtest.ServerVersion(), \"4.2\") < 0\n\tgotCleared := tpm.IsPoolCleared()\n\tassert.Equal(mt, wantCleared, gotCleared, \"expected pool to be cleared: %t; pool was cleared: %t\",\n\t\twantCleared, gotCleared)\n}\n"
  },
  {
    "path": "internal/integration/sdam_prose_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage integration\n\nimport (\n\t\"context\"\n\t\"net\"\n\t\"os\"\n\t\"runtime\"\n\t\"sync\"\n\t\"sync/atomic\"\n\t\"testing\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/event\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/eventtest\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/failpoint\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/handshake\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/integration/mtest\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/mongoutil\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/require\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/address\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/description\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/topology\"\n)\n\nfunc TestSDAMProse(t *testing.T) {\n\tmt := mtest.New(t)\n\n\t// Server limits non-streaming heartbeats and explicit server transition checks to at most one\n\t// per 500ms. Set the test interval to 500ms to minimize the difference between the behavior of\n\t// streaming and non-streaming heartbeat intervals.\n\theartbeatInterval := 500 * time.Millisecond\n\theartbeatIntervalClientOpts := options.Client().\n\t\tSetHeartbeatInterval(heartbeatInterval)\n\theartbeatIntervalMtOpts := mtest.NewOptions().\n\t\tClientOptions(heartbeatIntervalClientOpts).\n\t\tCreateCollection(false).\n\t\tClientType(mtest.Proxy).\n\t\tMinServerVersion(\"4.4\") // RTT Monitor / Streaming protocol is not supported for versions < 4.4.\n\tmt.RunOpts(\"heartbeats processed more frequently\", heartbeatIntervalMtOpts, func(mt *mtest.T) {\n\t\t// Test that setting heartbeat interval to 500ms causes the client to process heartbeats\n\t\t// approximately every 500ms instead of the default 10s. Note that a Client doesn't\n\t\t// guarantee that it will process heartbeats exactly every 500ms, just that it will wait at\n\t\t// least 500ms between heartbeats (and should process heartbeats more frequently for shorter\n\t\t// interval settings).\n\t\t//\n\t\t// For number of nodes N, interval I, and duration D, a Client should process at most X\n\t\t// operations:\n\t\t//\n\t\t//   X = (N * (2 handshakes + D/I heartbeats + D/I RTTs))\n\t\t//\n\t\t// Assert that a Client processes the expected number of operations for heartbeats sent at\n\t\t// an interval between I and 2*I to account for different actual heartbeat intervals under\n\t\t// different runtime conditions.\n\n\t\t// Measure the actual amount of time between the start of the test and when we inspect the\n\t\t// sent messages. The sleep duration will be at least the specified duration but\n\t\t// possibly longer, which could lead to extra heartbeat messages, so account for that in\n\t\t// the assertions.\n\t\tif len(os.Getenv(\"DOCKER_RUNNING\")) > 0 {\n\t\t\tmt.Skip(\"skipping test in docker environment\")\n\t\t}\n\t\tstart := time.Now()\n\t\ttime.Sleep(2 * time.Second)\n\t\tmessages := mt.GetProxyCapture().Drain()\n\t\tduration := time.Since(start)\n\n\t\thosts, err := mongoutil.HostsFromURI(mtest.ClusterURI())\n\t\trequire.NoError(mt, err)\n\n\t\tnumNodes := len(hosts)\n\t\tmaxExpected := numNodes * (2 + 2*int(duration/heartbeatInterval))\n\t\tminExpected := numNodes * (2 + 2*int(duration/(heartbeatInterval*2)))\n\n\t\tassert.True(\n\t\t\tmt,\n\t\t\tlen(messages) >= minExpected && len(messages) <= maxExpected,\n\t\t\t\"expected number of messages to be in range [%d, %d], got %d\"+\n\t\t\t\t\" (num nodes = %d, duration = %v, interval = %v)\",\n\t\t\tminExpected,\n\t\t\tmaxExpected,\n\t\t\tlen(messages),\n\t\t\tnumNodes,\n\t\t\tduration,\n\t\t\theartbeatInterval)\n\t})\n\n\tmt.RunOpts(\"rtt tests\", noClientOpts, func(mt *mtest.T) {\n\t\tclientOpts := options.Client().\n\t\t\tSetHeartbeatInterval(500 * time.Millisecond).\n\t\t\tSetAppName(\"streamingRttTest\")\n\t\tmtOpts := mtest.NewOptions().\n\t\t\tMinServerVersion(\"4.4\").\n\t\t\tClientOptions(clientOpts).\n\t\t\t// TODO(GODRIVER-3328): FailPoints are not currently reliable on sharded\n\t\t\t// clusters. Remove this exclusion once we fix that.\n\t\t\tTopologies(mtest.Single, mtest.ReplicaSet, mtest.LoadBalanced)\n\t\tmt.RunOpts(\"rtt is continuously updated\", mtOpts, func(mt *mtest.T) {\n\t\t\t// Test that the RTT monitor updates the RTT for server descriptions.\n\n\t\t\t// The server has been discovered by the create command issued by mtest. Sleep for two seconds to allow\n\t\t\t// multiple heartbeats to finish.\n\t\t\ttestTopology := getTopologyFromClient(mt.Client)\n\t\t\ttime.Sleep(2 * time.Second)\n\t\t\tfor _, serverDesc := range testTopology.Description().Servers {\n\t\t\t\tassert.NotEqual(mt, description.Unknown, serverDesc.Kind, \"server %v is Unknown\", serverDesc)\n\t\t\t\tassert.True(mt, serverDesc.AverageRTTSet, \"AverageRTTSet for server description %v is false\", serverDesc)\n\n\t\t\t\tif runtime.GOOS != \"windows\" {\n\t\t\t\t\t// Windows has a lower time resolution than other platforms, which causes the reported RTT to be\n\t\t\t\t\t// 0 if it's below some threshold. The assertion above already confirms that the RTT is set to\n\t\t\t\t\t// a value, so we can skip this assertion on Windows.\n\t\t\t\t\tassert.True(mt, serverDesc.AverageRTT > 0, \"server description %v has 0 RTT\", serverDesc)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Force hello requests to block for 500ms and wait until a server's average RTT goes over 250ms.\n\t\t\tmt.SetFailPoint(failpoint.FailPoint{\n\t\t\t\tConfigureFailPoint: \"failCommand\",\n\t\t\t\tMode: failpoint.Mode{\n\t\t\t\t\tTimes: 1000,\n\t\t\t\t},\n\t\t\t\tData: failpoint.Data{\n\t\t\t\t\tFailCommands:    []string{handshake.LegacyHello, \"hello\"},\n\t\t\t\t\tBlockConnection: true,\n\t\t\t\t\tBlockTimeMS:     500,\n\t\t\t\t\tAppName:         \"streamingRttTest\",\n\t\t\t\t},\n\t\t\t})\n\t\t\tcallback := func() bool {\n\t\t\t\t// We don't know which server received the failpoint command, so we wait until any of the server\n\t\t\t\t// RTTs cross the threshold.\n\t\t\t\tfor _, serverDesc := range testTopology.Description().Servers {\n\t\t\t\t\tif serverDesc.AverageRTT > 250*time.Millisecond {\n\t\t\t\t\t\treturn true\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// The next update will be in ~500ms.\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tassert.Eventually(mt,\n\t\t\t\tcallback,\n\t\t\t\tdefaultCallbackTimeout,\n\t\t\t\t500*time.Millisecond,\n\t\t\t\t\"expected average rtt heartbeats at least within every 500 ms period\")\n\t\t})\n\t})\n\n\tmt.RunOpts(\"client waits between failed Hellos\", mtest.NewOptions().MinServerVersion(\"4.9\").Topologies(mtest.Single), func(mt *mtest.T) {\n\t\t// Force hello requests to fail 5 times.\n\t\tmt.SetFailPoint(failpoint.FailPoint{\n\t\t\tConfigureFailPoint: \"failCommand\",\n\t\t\tMode: failpoint.Mode{\n\t\t\t\tTimes: 5,\n\t\t\t},\n\t\t\tData: failpoint.Data{\n\t\t\t\tFailCommands: []string{handshake.LegacyHello, \"hello\"},\n\t\t\t\tErrorCode:    1234,\n\t\t\t\tAppName:      \"SDAMMinHeartbeatFrequencyTest\",\n\t\t\t},\n\t\t})\n\n\t\t// Reset client options to use direct connection, app name, and 5s SS timeout.\n\t\tclientOpts := options.Client().SetDirect(true).\n\t\t\tSetAppName(\"SDAMMinHeartbeatFrequencyTest\").\n\t\t\tSetServerSelectionTimeout(5 * time.Second)\n\t\tmt.ResetClient(clientOpts)\n\n\t\t// Assert that Ping completes successfully within 2 to 3.5 seconds.\n\t\tstart := time.Now()\n\t\terr := mt.Client.Ping(context.Background(), nil)\n\t\tassert.Nil(mt, err, \"Ping error: %v\", err)\n\t\tpingTime := time.Since(start)\n\t\tassert.True(mt, pingTime > 2000*time.Millisecond && pingTime < 3500*time.Millisecond,\n\t\t\t\"expected Ping to take between 2 and 3.5 seconds, took %v seconds\", pingTime.Seconds())\n\t})\n}\n\nfunc TestServerHeartbeatStartedEvent(t *testing.T) {\n\tt.Run(\"emits the first HeartbeatStartedEvent before the monitoring socket was created\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tconst address = address.Address(\"localhost:9999\")\n\t\texpectedEvents := []string{\n\t\t\t\"serverHeartbeatStartedEvent\",\n\t\t\t\"client connected\",\n\t\t\t\"client hello received\",\n\t\t\t\"serverHeartbeatFailedEvent\",\n\t\t}\n\n\t\tevents := make(chan string)\n\n\t\tlistener, err := net.Listen(\"tcp\", address.String())\n\t\tassert.NoError(t, err)\n\t\tdefer listener.Close()\n\t\tgo func() {\n\t\t\tconn, err := listener.Accept()\n\t\t\tassert.NoError(t, err)\n\t\t\tdefer conn.Close()\n\n\t\t\tevents <- \"client connected\"\n\t\t\t_, _ = conn.Read(nil)\n\t\t\tevents <- \"client hello received\"\n\t\t}()\n\n\t\tserver := topology.NewServer(\n\t\t\taddress,\n\t\t\tbson.NewObjectID(),\n\t\t\t1*time.Second,\n\t\t\ttopology.WithServerMonitor(func(*event.ServerMonitor) *event.ServerMonitor {\n\t\t\t\treturn &event.ServerMonitor{\n\t\t\t\t\tServerHeartbeatStarted: func(*event.ServerHeartbeatStartedEvent) {\n\t\t\t\t\t\tevents <- \"serverHeartbeatStartedEvent\"\n\t\t\t\t\t},\n\t\t\t\t\tServerHeartbeatFailed: func(*event.ServerHeartbeatFailedEvent) {\n\t\t\t\t\t\tevents <- \"serverHeartbeatFailedEvent\"\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t}),\n\t\t)\n\t\trequire.NoError(t, server.Connect(nil))\n\n\t\tticker := time.NewTicker(5 * time.Second)\n\t\tdefer ticker.Stop()\n\n\t\tactualEvents := make([]string, 0, len(expectedEvents))\n\t\tfor len(actualEvents) < len(expectedEvents) {\n\t\t\tselect {\n\t\t\tcase event := <-events:\n\t\t\t\tactualEvents = append(actualEvents, event)\n\t\t\tcase <-ticker.C:\n\t\t\t\tassert.FailNow(t, \"timed out for incoming event\")\n\t\t\t}\n\t\t}\n\t\tassert.Equal(t, expectedEvents, actualEvents)\n\t})\n\n\tmt := mtest.New(t)\n\n\tmt.Run(\"polling must await frequency\", func(mt *mtest.T) {\n\t\tvar heartbeatStartedCount atomic.Int64\n\n\t\tservers := map[string]bool{}\n\t\tserversMu := sync.RWMutex{} // Guard the servers set\n\n\t\tserverMonitor := &event.ServerMonitor{\n\t\t\tServerHeartbeatStarted: func(*event.ServerHeartbeatStartedEvent) {\n\t\t\t\theartbeatStartedCount.Add(1)\n\t\t\t},\n\t\t\tTopologyDescriptionChanged: func(evt *event.TopologyDescriptionChangedEvent) {\n\t\t\t\tserversMu.Lock()\n\t\t\t\tdefer serversMu.Unlock()\n\n\t\t\t\tfor _, srv := range evt.NewDescription.Servers {\n\t\t\t\t\tservers[srv.Addr.String()] = true\n\t\t\t\t}\n\t\t\t},\n\t\t}\n\n\t\t// Create a client with  heartbeatFrequency=100ms,\n\t\t// serverMonitoringMode=poll. Use SDAM to record the number of times the\n\t\t// a heartbeat is started and the number of servers discovered.\n\t\tmt.ResetClient(options.Client().\n\t\t\tSetServerMonitor(serverMonitor).\n\t\t\tSetServerMonitoringMode(options.ServerMonitoringModePoll))\n\n\t\t// Per specifications, minHeartbeatFrequencyMS=500ms. So, within the first\n\t\t// 500ms the heartbeatStartedCount should be LEQ to the number of discovered\n\t\t// servers.\n\t\ttime.Sleep(500 * time.Millisecond)\n\n\t\tserversMu.Lock()\n\t\tserverCount := int64(len(servers))\n\t\tserversMu.Unlock()\n\n\t\tassert.LessOrEqual(mt, heartbeatStartedCount.Load(), serverCount)\n\t})\n}\n\nfunc TestConnectionPoolBackpressure(t *testing.T) {\n\tmt := mtest.New(t, mtest.NewOptions().\n\t\tMinServerVersion(\"7.0\").\n\t\tTopologies(mtest.Single, mtest.ReplicaSet))\n\n\ttpm := eventtest.NewTestPoolMonitor()\n\tmt.ResetClient(options.Client().\n\t\tSetMaxConnecting(100).\n\t\tSetPoolMonitor(tpm.PoolMonitor))\n\n\tadmin := mt.Client.Database(\"admin\")\n\tparameters := []struct {\n\t\tname string\n\t\tval  any\n\t}{\n\t\t{\"ingressConnectionEstablishmentRateLimiterEnabled\", true},\n\t\t{\"ingressConnectionEstablishmentRatePerSec\", 20},\n\t\t{\"ingressConnectionEstablishmentBurstCapacitySecs\", 1},\n\t\t{\"ingressConnectionEstablishmentMaxQueueDepth\", 1},\n\t}\n\tfor _, p := range parameters {\n\t\terr := admin.RunCommand(context.Background(), bson.D{{\"setParameter\", 1}, {p.name, p.val}}).Err()\n\t\trequire.NoError(mt, err)\n\t}\n\tdefer func() {\n\t\ttime.Sleep(1 * time.Second)\n\t\t_ = admin.RunCommand(context.Background(), bson.D{{\"setParameter\", 1}, {\"ingressConnectionEstablishmentRateLimiterEnabled\", false}})\n\t}()\n\n\t_, err := mt.Coll.InsertOne(context.Background(), bson.D{})\n\trequire.NoError(mt, err)\n\n\tvar wg sync.WaitGroup\n\tfilter := bson.D{{Key: \"$where\", Value: \"function() { sleep(2000); return true; }\"}}\n\tfor i := 0; i < 100; i++ {\n\t\twg.Add(1)\n\t\tgo func() {\n\t\t\tdefer wg.Done()\n\t\t\t// Ignore errors raised by the command.\n\t\t\t_ = mt.Coll.FindOne(context.Background(), filter).Err()\n\t\t}()\n\t}\n\twg.Wait()\n\n\tcheckOutFailedEvents := tpm.Events(func(evt *event.PoolEvent) bool {\n\t\treturn evt.Type == event.ConnectionCheckOutFailed\n\t})\n\tassert.GreaterOrEqual(mt, len(checkOutFailedEvents), 10,\n\t\t\"Expected >= 10 failures, got %d\", len(checkOutFailedEvents))\n\n\tassert.False(mt, tpm.IsPoolCleared(), \"Pool should not have cleared\")\n}\n"
  },
  {
    "path": "internal/integration/search_index_prose_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2023-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage integration\n\nimport (\n\t\"context\"\n\t\"os\"\n\t\"sync\"\n\t\"testing\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/integration/mtest\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/mongoutil\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/require\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/uuid\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/readconcern\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/writeconcern\"\n)\n\nfunc TestSearchIndexProse(t *testing.T) {\n\tt.Parallel()\n\n\tconst timeout = 5 * time.Minute\n\n\turi := os.Getenv(\"SEARCH_INDEX_URI\")\n\tif uri == \"\" {\n\t\tt.Skip(\"skipping\")\n\t}\n\n\topts := options.Client().ApplyURI(uri).SetTimeout(timeout)\n\tmt := mtest.New(t, mtest.NewOptions().ClientOptions(opts).MinServerVersion(\"7.0\").Topologies(mtest.ReplicaSet))\n\n\tmt.Run(\"case 1: Driver can successfully create and list search indexes\", func(mt *mtest.T) {\n\t\tctx := context.Background()\n\n\t\t_, err := mt.Coll.InsertOne(ctx, bson.D{})\n\t\trequire.NoError(mt, err, \"failed to insert\")\n\n\t\tview := mt.Coll.SearchIndexes()\n\n\t\tdefinition := bson.D{{\"mappings\", bson.D{{\"dynamic\", false}}}}\n\t\tsearchName := \"test-search-index\"\n\t\topts := options.SearchIndexes().SetName(searchName)\n\t\tindex, err := view.CreateOne(ctx, mongo.SearchIndexModel{\n\t\t\tDefinition: definition,\n\t\t\tOptions:    opts,\n\t\t})\n\t\trequire.NoError(mt, err, \"failed to create index\")\n\t\trequire.Equal(mt, searchName, index, \"unmatched name\")\n\n\t\tawaitCtx, cancel := context.WithTimeout(context.Background(), 1*time.Minute)\n\t\tdefer cancel()\n\n\t\tvar doc bson.Raw\n\t\tfor doc == nil {\n\t\t\tcursor, err := view.List(awaitCtx, opts)\n\t\t\trequire.NoError(mt, err, \"failed to list\")\n\n\t\t\tif !cursor.Next(awaitCtx) {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tname := cursor.Current.Lookup(\"name\").StringValue()\n\t\t\tqueryable := cursor.Current.Lookup(\"queryable\").Boolean()\n\t\t\tif name == searchName && queryable {\n\t\t\t\tdoc = cursor.Current\n\t\t\t} else {\n\t\t\t\tmt.Logf(\"cursor: %s, sleep 5 seconds...\", cursor.Current.String())\n\t\t\t\ttime.Sleep(5 * time.Second)\n\t\t\t}\n\t\t}\n\t\trequire.NotNil(mt, doc, \"got empty document\")\n\t\tactual := doc.Lookup(\"latestDefinition\", \"mappings\", \"dynamic\").Boolean()\n\t\tassert.False(mt, actual, \"expected latestDefinition.mappings.dynamic to be false\")\n\t})\n\n\tmt.Run(\"case 2: Driver can successfully create multiple indexes in batch\", func(mt *mtest.T) {\n\t\tctx := context.Background()\n\n\t\t_, err := mt.Coll.InsertOne(ctx, bson.D{})\n\t\trequire.NoError(mt, err, \"failed to insert\")\n\n\t\tview := mt.Coll.SearchIndexes()\n\n\t\tdefinition := bson.D{{\"mappings\", bson.D{{\"dynamic\", false}}}}\n\t\tmodels := []mongo.SearchIndexModel{\n\t\t\t{\n\t\t\t\tDefinition: definition,\n\t\t\t\tOptions:    options.SearchIndexes().SetName(\"test-search-index-1\"),\n\t\t\t},\n\t\t\t{\n\t\t\t\tDefinition: definition,\n\t\t\t\tOptions:    options.SearchIndexes().SetName(\"test-search-index-2\"),\n\t\t\t},\n\t\t}\n\t\tindexes, err := view.CreateMany(ctx, models)\n\t\trequire.NoError(mt, err, \"failed to create index\")\n\t\trequire.Equal(mt, len(indexes), 2, \"expected 2 indexes\")\n\t\tfor _, model := range models {\n\t\t\targs, err := mongoutil.NewOptions[options.SearchIndexesOptions](model.Options)\n\t\t\trequire.NoError(mt, err, \"failed to construct options from builder\")\n\n\t\t\trequire.Contains(mt, indexes, *args.Name)\n\t\t}\n\n\t\tgetDocument := func(ctx context.Context, opts *options.SearchIndexesOptionsBuilder) bson.Raw {\n\t\t\tfor {\n\t\t\t\tcursor, err := view.List(ctx, opts)\n\t\t\t\trequire.NoError(mt, err, \"failed to list\")\n\n\t\t\t\tif !cursor.Next(ctx) {\n\t\t\t\t\treturn nil\n\t\t\t\t}\n\t\t\t\tname := cursor.Current.Lookup(\"name\").StringValue()\n\t\t\t\tqueryable := cursor.Current.Lookup(\"queryable\").Boolean()\n\n\t\t\t\targs, err := mongoutil.NewOptions[options.SearchIndexesOptions](opts)\n\t\t\t\trequire.NoError(mt, err, \"failed to construct options from builder\")\n\n\t\t\t\tif name == *args.Name && queryable {\n\t\t\t\t\treturn cursor.Current\n\t\t\t\t}\n\t\t\t\tmt.Logf(\"cursor: %s, sleep 5 seconds...\", cursor.Current.String())\n\t\t\t\ttime.Sleep(5 * time.Second)\n\t\t\t}\n\t\t}\n\n\t\tvar wg sync.WaitGroup\n\t\twg.Add(len(models))\n\t\tfor i := range models {\n\t\t\tgo func(opts *options.SearchIndexesOptionsBuilder) {\n\t\t\t\tdefer wg.Done()\n\n\t\t\t\tctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute)\n\t\t\t\tdefer cancel()\n\n\t\t\t\tdoc := getDocument(ctx, opts)\n\t\t\t\trequire.NotNil(mt, doc, \"got empty document\")\n\n\t\t\t\targs, err := mongoutil.NewOptions[options.SearchIndexesOptions](opts)\n\t\t\t\trequire.NoError(mt, err, \"failed to construct options from builder\")\n\n\t\t\t\tassert.Equal(mt, *args.Name, doc.Lookup(\"name\").StringValue(), \"unmatched name\")\n\n\t\t\t\tactual := doc.Lookup(\"latestDefinition\", \"mappings\", \"dynamic\").Boolean()\n\t\t\t\tassert.False(mt, actual, \"expected latestDefinition.mappings.dynamic to be false\")\n\t\t\t}(models[i].Options)\n\t\t}\n\t\twg.Wait()\n\t})\n\n\tmt.Run(\"case 3: Driver can successfully drop search indexes\", func(mt *mtest.T) {\n\t\tctx := context.Background()\n\n\t\t_, err := mt.Coll.InsertOne(ctx, bson.D{})\n\t\trequire.NoError(mt, err, \"failed to insert\")\n\n\t\tview := mt.Coll.SearchIndexes()\n\n\t\tdefinition := bson.D{{\"mappings\", bson.D{{\"dynamic\", false}}}}\n\t\tsearchName := \"test-search-index\"\n\t\topts := options.SearchIndexes().SetName(searchName)\n\t\tindex, err := view.CreateOne(ctx, mongo.SearchIndexModel{\n\t\t\tDefinition: definition,\n\t\t\tOptions:    opts,\n\t\t})\n\t\trequire.NoError(mt, err, \"failed to create index\")\n\t\trequire.Equal(mt, searchName, index, \"unmatched name\")\n\n\t\tcreateOneCtx, createOneCancel := context.WithTimeout(context.Background(), 1*time.Minute)\n\t\tdefer createOneCancel()\n\t\tvar doc bson.Raw\n\t\tfor doc == nil {\n\t\t\tcursor, err := view.List(createOneCtx, opts)\n\t\t\trequire.NoError(mt, err, \"failed to list\")\n\n\t\t\tif !cursor.Next(createOneCtx) {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tname := cursor.Current.Lookup(\"name\").StringValue()\n\t\t\tqueryable := cursor.Current.Lookup(\"queryable\").Boolean()\n\t\t\tif name == searchName && queryable {\n\t\t\t\tdoc = cursor.Current\n\t\t\t} else {\n\t\t\t\tmt.Logf(\"cursor: %s, sleep 5 seconds...\", cursor.Current.String())\n\t\t\t\ttime.Sleep(5 * time.Second)\n\t\t\t}\n\t\t}\n\t\trequire.NotNil(mt, doc, \"got empty document\")\n\n\t\terr = view.DropOne(ctx, searchName)\n\t\trequire.NoError(mt, err, \"failed to drop index\")\n\t\tdropOneCtx, dropOneCancel := context.WithTimeout(context.Background(), 1*time.Minute)\n\t\tdefer dropOneCancel()\n\t\tfor {\n\t\t\tcursor, err := view.List(dropOneCtx, opts)\n\t\t\trequire.NoError(mt, err, \"failed to list\")\n\n\t\t\tif !cursor.Next(dropOneCtx) {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tmt.Logf(\"cursor: %s, sleep 5 seconds...\", cursor.Current.String())\n\t\t\ttime.Sleep(5 * time.Second)\n\t\t}\n\t})\n\n\tmt.Run(\"case 4: Driver can update a search index\", func(mt *mtest.T) {\n\t\tctx := context.Background()\n\n\t\t_, err := mt.Coll.InsertOne(ctx, bson.D{})\n\t\trequire.NoError(mt, err, \"failed to insert\")\n\n\t\tview := mt.Coll.SearchIndexes()\n\n\t\tdefinition := bson.D{{\"mappings\", bson.D{{\"dynamic\", false}}}}\n\t\tsearchName := \"test-search-index\"\n\t\topts := options.SearchIndexes().SetName(searchName)\n\t\tindex, err := view.CreateOne(ctx, mongo.SearchIndexModel{\n\t\t\tDefinition: definition,\n\t\t\tOptions:    opts,\n\t\t})\n\t\trequire.NoError(mt, err, \"failed to create index\")\n\t\trequire.Equal(mt, searchName, index, \"unmatched name\")\n\n\t\tcreateOneCtx, createOneCancel := context.WithTimeout(context.Background(), 1*time.Minute)\n\t\tdefer createOneCancel()\n\t\tvar doc bson.Raw\n\t\tfor doc == nil {\n\t\t\tcursor, err := view.List(createOneCtx, opts)\n\t\t\trequire.NoError(mt, err, \"failed to list\")\n\n\t\t\tif !cursor.Next(createOneCtx) {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tname := cursor.Current.Lookup(\"name\").StringValue()\n\t\t\tqueryable := cursor.Current.Lookup(\"queryable\").Boolean()\n\t\t\tif name == searchName && queryable {\n\t\t\t\tdoc = cursor.Current\n\t\t\t} else {\n\t\t\t\tmt.Logf(\"cursor: %s, sleep 5 seconds...\", cursor.Current.String())\n\t\t\t\ttime.Sleep(5 * time.Second)\n\t\t\t}\n\t\t}\n\t\trequire.NotNil(mt, doc, \"got empty document\")\n\n\t\tdefinition = bson.D{{\"mappings\", bson.D{{\"dynamic\", true}}}}\n\t\terr = view.UpdateOne(ctx, searchName, definition)\n\t\trequire.NoError(mt, err, \"failed to update index\")\n\t\tupdateOneCtx, updateOneCancel := context.WithTimeout(context.Background(), 2*time.Minute)\n\t\tdefer updateOneCancel()\n\t\tdoc = nil\n\t\tfor doc == nil {\n\t\t\tcursor, err := view.List(updateOneCtx, opts)\n\t\t\trequire.NoError(mt, err, \"failed to list\")\n\n\t\t\tif !cursor.Next(updateOneCtx) {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tname := cursor.Current.Lookup(\"name\").StringValue()\n\t\t\tqueryable := cursor.Current.Lookup(\"queryable\").Boolean()\n\t\t\tstatus := cursor.Current.Lookup(\"status\").StringValue()\n\t\t\tif name == searchName && queryable && status == \"READY\" {\n\t\t\t\tdoc = cursor.Current\n\t\t\t} else {\n\t\t\t\tmt.Logf(\"cursor: %s, sleep 5 seconds...\", cursor.Current.String())\n\t\t\t\ttime.Sleep(5 * time.Second)\n\t\t\t}\n\t\t}\n\t\trequire.NotNil(mt, doc, \"got empty document\")\n\t\trequire.True(mt, doc.Lookup(\"latestDefinition\", \"mappings\", \"dynamic\").Boolean(),\n\t\t\t\"expected latestDefinition.mappings.dynamic to be true\")\n\t})\n\n\tmt.Run(\"case 5: dropSearchIndex suppresses namespace not found errors\", func(mt *mtest.T) {\n\t\tctx := context.Background()\n\n\t\tid, err := uuid.New()\n\t\trequire.NoError(mt, err)\n\n\t\tcollection := mt.CreateCollection(mtest.Collection{\n\t\t\tName: id.String(),\n\t\t}, false)\n\n\t\terr = collection.SearchIndexes().DropOne(ctx, \"foo\")\n\t\trequire.NoError(mt, err)\n\t})\n\n\tmt.RunOpts(\"case 6: Driver can successfully create and list search indexes with non-default readConcern and writeConcern\",\n\t\tmtest.NewOptions().CollectionOptions(options.Collection().SetWriteConcern(writeconcern.W1()).SetReadConcern(readconcern.Majority())),\n\t\tfunc(mt *mtest.T) {\n\t\t\tctx := context.Background()\n\n\t\t\t_, err := mt.Coll.InsertOne(ctx, bson.D{})\n\t\t\trequire.NoError(mt, err, \"failed to insert\")\n\n\t\t\tview := mt.Coll.SearchIndexes()\n\n\t\t\tdefinition := bson.D{{\"mappings\", bson.D{{\"dynamic\", false}}}}\n\t\t\tconst searchName = \"test-search-index-case6\"\n\t\t\topts := options.SearchIndexes().SetName(searchName)\n\t\t\tindex, err := view.CreateOne(ctx, mongo.SearchIndexModel{\n\t\t\t\tDefinition: definition,\n\t\t\t\tOptions:    opts,\n\t\t\t})\n\t\t\trequire.NoError(mt, err, \"failed to create index\")\n\t\t\trequire.Equal(mt, searchName, index, \"unmatched name\")\n\t\t\tawaitCtx, cancel := context.WithTimeout(context.Background(), 1*time.Minute)\n\t\t\tdefer cancel()\n\t\t\tvar doc bson.Raw\n\t\t\tfor doc == nil {\n\t\t\t\tcursor, err := view.List(awaitCtx, opts)\n\t\t\t\trequire.NoError(mt, err, \"failed to list\")\n\n\t\t\t\tif !cursor.Next(awaitCtx) {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tname := cursor.Current.Lookup(\"name\").StringValue()\n\t\t\t\tqueryable := cursor.Current.Lookup(\"queryable\").Boolean()\n\t\t\t\tif name == searchName && queryable {\n\t\t\t\t\tdoc = cursor.Current\n\t\t\t\t} else {\n\t\t\t\t\tmt.Logf(\"cursor: %s, sleep 5 seconds...\", cursor.Current.String())\n\t\t\t\t\ttime.Sleep(5 * time.Second)\n\t\t\t\t}\n\t\t\t}\n\t\t\trequire.NotNil(mt, doc, \"got empty document\")\n\t\t\tactual := doc.Lookup(\"latestDefinition\", \"mappings\", \"dynamic\").Boolean()\n\t\t\tassert.False(mt, actual, \"expected latestDefinition.mappings.dynamic to be false\")\n\t\t})\n\n\tcase7CollName, err := uuid.New()\n\tassert.NoError(mt, err, \"failed to create random collection name for case #7\")\n\n\tmt.RunOpts(\"case 7: Driver can successfully handle search index types when creating indexes\",\n\t\tmtest.NewOptions().CollectionName(case7CollName.String()),\n\t\tfunc(mt *mtest.T) {\n\t\t\tctx := context.Background()\n\n\t\t\t_, err := mt.Coll.InsertOne(ctx, bson.D{})\n\t\t\trequire.NoError(mt, err, \"failed to insert\")\n\n\t\t\tview := mt.Coll.SearchIndexes()\n\n\t\t\tdefinition := bson.D{{\"mappings\", bson.D{{\"dynamic\", false}}}}\n\t\t\tindexName := \"test-search-index-case7-implicit\"\n\t\t\topts := options.SearchIndexes().SetName(indexName)\n\t\t\tindex, err := view.CreateOne(ctx, mongo.SearchIndexModel{\n\t\t\t\tDefinition: definition,\n\t\t\t\tOptions:    opts,\n\t\t\t})\n\t\t\trequire.NoError(mt, err, \"failed to create index\")\n\t\t\trequire.Equal(mt, indexName, index, \"unmatched name\")\n\t\t\timplicitCtx, implicitCancel := context.WithTimeout(context.Background(), 1*time.Minute)\n\t\t\tdefer implicitCancel()\n\t\t\tvar doc bson.Raw\n\t\t\tfor doc == nil {\n\t\t\t\tcursor, err := view.List(implicitCtx, opts)\n\t\t\t\trequire.NoError(mt, err, \"failed to list\")\n\n\t\t\t\tif !cursor.Next(implicitCtx) {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tname := cursor.Current.Lookup(\"name\").StringValue()\n\t\t\t\tqueryable := cursor.Current.Lookup(\"queryable\").Boolean()\n\t\t\t\tindexType := cursor.Current.Lookup(\"type\").StringValue()\n\t\t\t\tif name == indexName && queryable {\n\t\t\t\t\tdoc = cursor.Current\n\t\t\t\t\tassert.Equal(mt, indexType, \"search\")\n\t\t\t\t} else {\n\t\t\t\t\tmt.Logf(\"cursor: %s, sleep 5 seconds...\", cursor.Current.String())\n\t\t\t\t\ttime.Sleep(5 * time.Second)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tindexName = \"test-search-index-case7-explicit\"\n\t\t\topts = options.SearchIndexes().SetName(indexName).SetType(\"search\")\n\t\t\tindex, err = view.CreateOne(ctx, mongo.SearchIndexModel{\n\t\t\t\tDefinition: definition,\n\t\t\t\tOptions:    opts,\n\t\t\t})\n\t\t\trequire.NoError(mt, err, \"failed to create index\")\n\t\t\trequire.Equal(mt, indexName, index, \"unmatched name\")\n\t\t\texplicitCtx, explicitCancel := context.WithTimeout(context.Background(), 1*time.Minute)\n\t\t\tdefer explicitCancel()\n\t\t\tdoc = nil\n\t\t\tfor doc == nil {\n\t\t\t\tcursor, err := view.List(explicitCtx, opts)\n\t\t\t\trequire.NoError(mt, err, \"failed to list\")\n\n\t\t\t\tif !cursor.Next(explicitCtx) {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tname := cursor.Current.Lookup(\"name\").StringValue()\n\t\t\t\tqueryable := cursor.Current.Lookup(\"queryable\").Boolean()\n\t\t\t\tindexType := cursor.Current.Lookup(\"type\").StringValue()\n\t\t\t\tif name == indexName && queryable {\n\t\t\t\t\tdoc = cursor.Current\n\t\t\t\t\tassert.Equal(mt, indexType, \"search\")\n\t\t\t\t} else {\n\t\t\t\t\tmt.Logf(\"cursor: %s, sleep 5 seconds...\", cursor.Current.String())\n\t\t\t\t\ttime.Sleep(5 * time.Second)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tindexName = \"test-search-index-case7-vector\"\n\t\t\ttype vectorDefinitionField struct {\n\t\t\t\tType          string `bson:\"type\"`\n\t\t\t\tPath          string `bson:\"path\"`\n\t\t\t\tNumDimensions int    `bson:\"numDimensions\"`\n\t\t\t\tSimilarity    string `bson:\"similarity\"`\n\t\t\t}\n\n\t\t\ttype vectorDefinition struct {\n\t\t\t\tFields []vectorDefinitionField `bson:\"fields\"`\n\t\t\t}\n\n\t\t\topts = options.SearchIndexes().SetName(indexName).SetType(\"vectorSearch\")\n\t\t\tindex, err = view.CreateOne(ctx, mongo.SearchIndexModel{\n\t\t\t\tDefinition: vectorDefinition{\n\t\t\t\t\tFields: []vectorDefinitionField{{\"vector\", \"path\", 1536, \"euclidean\"}},\n\t\t\t\t},\n\t\t\t\tOptions: opts,\n\t\t\t})\n\t\t\trequire.NoError(mt, err, \"failed to create index\")\n\t\t\trequire.Equal(mt, indexName, index, \"unmatched name\")\n\t\t\tvectorCtx, vectorCancel := context.WithTimeout(context.Background(), 1*time.Minute)\n\t\t\tdefer vectorCancel()\n\t\t\tdoc = nil\n\t\t\tfor doc == nil {\n\t\t\t\tcursor, err := view.List(vectorCtx, opts)\n\t\t\t\trequire.NoError(mt, err, \"failed to list\")\n\n\t\t\t\tif !cursor.Next(vectorCtx) {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tname := cursor.Current.Lookup(\"name\").StringValue()\n\t\t\t\tqueryable := cursor.Current.Lookup(\"queryable\").Boolean()\n\t\t\t\tindexType := cursor.Current.Lookup(\"type\").StringValue()\n\t\t\t\tif name == indexName && queryable {\n\t\t\t\t\tdoc = cursor.Current\n\t\t\t\t\tassert.Equal(mt, indexType, \"vectorSearch\")\n\t\t\t\t} else {\n\t\t\t\t\tmt.Logf(\"cursor: %s, sleep 5 seconds...\", cursor.Current.String())\n\t\t\t\t\ttime.Sleep(5 * time.Second)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\n\tcase8CollName, err := uuid.New()\n\tassert.NoError(mt, err, \"failed to create random collection name for case #8\")\n\n\tmt.RunOpts(\"case 8: Driver requires explicit type to create a vector search index\",\n\t\tmtest.NewOptions().CollectionName(case8CollName.String()),\n\t\tfunc(mt *mtest.T) {\n\t\t\tctx := context.Background()\n\n\t\t\t_, err := mt.Coll.InsertOne(ctx, bson.D{})\n\t\t\trequire.NoError(mt, err, \"failed to insert\")\n\n\t\t\tview := mt.Coll.SearchIndexes()\n\n\t\t\ttype vectorDefinitionField struct {\n\t\t\t\tType          string `bson:\"type\"`\n\t\t\t\tPath          string `bson:\"path\"`\n\t\t\t\tNumDimensions int    `bson:\"numDimensions\"`\n\t\t\t\tSimilarity    string `bson:\"similarity\"`\n\t\t\t}\n\n\t\t\ttype vectorDefinition struct {\n\t\t\t\tFields []vectorDefinitionField `bson:\"fields\"`\n\t\t\t}\n\n\t\t\tconst indexName = \"test-search-index-case7-vector\"\n\t\t\topts := options.SearchIndexes().SetName(indexName)\n\t\t\t_, err = view.CreateOne(ctx, mongo.SearchIndexModel{\n\t\t\t\tDefinition: vectorDefinition{\n\t\t\t\t\tFields: []vectorDefinitionField{{\"vector\", \"plot_embedding\", 1536, \"euclidean\"}},\n\t\t\t\t},\n\t\t\t\tOptions: opts,\n\t\t\t})\n\t\t\tassert.ErrorContains(mt, err, \"Attribute mappings missing\")\n\t\t})\n\n\tmt.Run(\"case 9: Drivers use server default for unspecified name (`default`) and type (`search`)\", func(mt *mtest.T) {\n\t\tcases := []struct {\n\t\t\tname string\n\t\t\topts *options.SearchIndexesOptionsBuilder\n\t\t}{\n\t\t\t{name: \"empty options\", opts: options.SearchIndexes()},\n\t\t\t{name: \"nil options\", opts: nil},\n\t\t}\n\n\t\tfor _, tc := range cases {\n\t\t\tmt.Run(tc.name, func(mt *mtest.T) {\n\t\t\t\tview := mt.Coll.SearchIndexes()\n\t\t\t\tdefinition := bson.D{\n\t\t\t\t\t{\"mappings\", bson.D{\n\t\t\t\t\t\t{\"dynamic\", true},\n\t\t\t\t\t}},\n\t\t\t\t}\n\n\t\t\t\tindexName, err := view.CreateOne(context.Background(), mongo.SearchIndexModel{\n\t\t\t\t\tDefinition: definition,\n\t\t\t\t\tOptions:    tc.opts,\n\t\t\t\t})\n\t\t\t\trequire.NoError(mt, err, \"failed to create index\")\n\t\t\t\trequire.Equal(mt, \"default\", indexName)\n\n\t\t\t\tctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute)\n\t\t\t\tdefer cancel()\n\n\t\t\t\tvar doc bson.Raw\n\t\t\t\tfor doc == nil {\n\t\t\t\t\tcursor, err := view.List(ctx, tc.opts)\n\t\t\t\t\trequire.NoError(mt, err, \"failed to list\")\n\n\t\t\t\t\tif !cursor.Next(ctx) {\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t\tname := cursor.Current.Lookup(\"name\").StringValue()\n\t\t\t\t\tqueryable := cursor.Current.Lookup(\"queryable\").Boolean()\n\t\t\t\t\tindexType := cursor.Current.Lookup(\"type\").StringValue()\n\t\t\t\t\tif name == indexName && queryable {\n\t\t\t\t\t\tdoc = cursor.Current\n\t\t\t\t\t\trequire.Equal(mt, indexType, \"search\")\n\t\t\t\t\t} else {\n\t\t\t\t\t\tmt.Logf(\"cursor: %s, sleep 5 seconds...\", cursor.Current.String())\n\t\t\t\t\t\ttime.Sleep(5 * time.Second)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t})\n}\n"
  },
  {
    "path": "internal/integration/server_selection_prose_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2022-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage integration\n\nimport (\n\t\"context\"\n\t\"sync\"\n\t\"testing\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/event\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/eventtest\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/failpoint\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/integration/mtest\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/mongoutil\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/require\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/description\"\n)\n\ntype saturatedConnections map[int64]bool\n\n// saturatedHosts is used to maintain information about events with specific host+pool combinations.\ntype saturatedHosts map[string]saturatedConnections\n\nfunc (set saturatedHosts) add(host string, connectionID int64) {\n\tif set[host] == nil {\n\t\tset[host] = make(saturatedConnections)\n\t}\n\tset[host][connectionID] = true\n}\n\n// isSaturated returns true when each client on the cluster URI has a tolerable number of ready connections.\nfunc (set saturatedHosts) isSaturated(tolerance uint64) bool {\n\thosts, _ := mongoutil.HostsFromURI(mtest.ClusterURI())\n\n\tfor _, host := range hosts {\n\t\tif cxns := set[host]; cxns == nil || uint64(len(cxns)) < tolerance {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n\n// awaitSaturation uses CMAP events to ensure that the client's connection pools for N-mongoses have been saturated.\n// The qualification for a host to be \"saturated\" is for each host on the client to have a tolerable number of ready\n// connections.\nfunc awaitSaturation(ctx context.Context, mt *mtest.T, monitor *eventtest.TestPoolMonitor, tolerance uint64) error {\n\tset := make(saturatedHosts)\n\tvar err error\n\tfor !set.isSaturated(tolerance) {\n\t\tif err = ctx.Err(); err != nil {\n\t\t\tbreak\n\t\t}\n\t\tif err = mt.Coll.FindOne(ctx, bson.D{}).Err(); err != nil {\n\t\t\tbreak\n\t\t}\n\t\tmonitor.Events(func(evt *event.PoolEvent) bool {\n\t\t\t// Add host only when the connection is ready for use.\n\t\t\tif evt.Type == event.ConnectionReady {\n\t\t\t\tset.add(evt.Address, evt.ConnectionID)\n\t\t\t}\n\t\t\treturn true\n\t\t})\n\t}\n\treturn err\n}\n\n// runsServerSelection will run opCount-many `FindOne` operations within threadCount-many go routines.  The purpose of\n// this is to test the reliability of the server selection algorithm, which can be verified with the `counts` map and\n// `event.PoolEvent` slice.\nfunc runsServerSelection(mt *mtest.T, monitor *eventtest.TestPoolMonitor,\n\tthreadCount, opCount int,\n) (map[string]int, []*event.PoolEvent) {\n\tvar wg sync.WaitGroup\n\tfor i := 0; i < threadCount; i++ {\n\t\twg.Add(1)\n\t\tgo func() {\n\t\t\tdefer wg.Done()\n\n\t\t\tfor i := 0; i < opCount; i++ {\n\t\t\t\tres := mt.Coll.FindOne(context.Background(), bson.D{})\n\t\t\t\tassert.NoError(mt.T, res.Err(), \"FindOne() error for Collection '%s'\", mt.Coll.Name())\n\t\t\t}\n\t\t}()\n\t}\n\twg.Wait()\n\n\t// Get all checkOut events and calculate the number of times each server was selected. The prose test spec says to\n\t// use command monitoring events, but those don't include the server address, so use checkOut events instead.\n\tcheckOutEvents := monitor.Events(func(evt *event.PoolEvent) bool {\n\t\treturn evt.Type == event.ConnectionCheckOutStarted\n\t})\n\tcounts := make(map[string]int)\n\tfor _, evt := range checkOutEvents {\n\t\tcounts[evt.Address]++\n\t}\n\tassert.Equal(mt, 2, len(counts), \"expected exactly 2 server addresses\")\n\treturn counts, checkOutEvents\n}\n\n// TestServerSelectionProse implements the Server Selection prose tests:\n// https://github.com/mongodb/specifications/blob/master/source/server-selection/server-selection-tests.md\nfunc TestServerSelectionProse(t *testing.T) {\n\tconst maxPoolSize = 10\n\tconst localThreshold = 30 * time.Second\n\n\tmt := mtest.New(t, mtest.NewOptions().CreateClient(false))\n\n\tmtOpts := mtest.NewOptions().Topologies(mtest.Sharded).MinServerVersion(\"4.9\").AllowFailPointsOnSharded()\n\tmt.RunOpts(\"operationCount-based selection within latency window, with failpoint\", mtOpts, func(mt *mtest.T) {\n\t\t_, err := mt.Coll.InsertOne(context.Background(), bson.D{})\n\t\trequire.NoError(mt, err, \"InsertOne() error\")\n\n\t\thosts, err := mongoutil.HostsFromURI(mtest.ClusterURI())\n\n\t\trequire.NoError(mt, err)\n\t\trequire.GreaterOrEqualf(mt, len(hosts), 2, \"test cluster must have at least 2 mongos hosts\")\n\n\t\t// Set a failpoint on a specific mongos host that delays all \"find\" commands for 500ms. We\n\t\t// need to know which mongos we set the failpoint on for our assertions later.\n\t\tfailpointHost := hosts[0]\n\t\tmt.ResetClient(options.Client().\n\t\t\tSetHosts([]string{failpointHost}))\n\t\tmt.SetFailPoint(failpoint.FailPoint{\n\t\t\tConfigureFailPoint: \"failCommand\",\n\t\t\tMode: failpoint.Mode{\n\t\t\t\tTimes: 10000,\n\t\t\t},\n\t\t\tData: failpoint.Data{\n\t\t\t\tFailCommands:    []string{\"find\"},\n\t\t\t\tBlockConnection: true,\n\t\t\t\tBlockTimeMS:     500,\n\t\t\t\tAppName:         \"loadBalancingTest\",\n\t\t\t},\n\t\t})\n\t\t// The automatic failpoint clearing may not clear failpoints set on specific hosts, so\n\t\t// manually clear the failpoint we set on the specific mongos when the test is done.\n\t\tdefer func() {\n\t\t\tmt.ResetClient(options.Client().\n\t\t\t\tSetHosts([]string{failpointHost}))\n\t\t\tmt.ClearFailPoints()\n\t\t}()\n\n\t\t// Reset the client with exactly 2 mongos hosts. Use a ServerMonitor to wait for both mongos\n\t\t// host descriptions to move from kind \"Unknown\" to kind \"Mongos\".\n\t\ttopologyEvents := make(chan *event.TopologyDescriptionChangedEvent, 10)\n\t\ttpm := eventtest.NewTestPoolMonitor()\n\t\tmt.ResetClient(options.Client().\n\t\t\tSetLocalThreshold(localThreshold).\n\t\t\tSetMaxPoolSize(maxPoolSize).\n\t\t\tSetMinPoolSize(maxPoolSize).\n\t\t\tSetHosts(hosts[:2]).\n\t\t\tSetPoolMonitor(tpm.PoolMonitor).\n\t\t\tSetAppName(\"loadBalancingTest\").\n\t\t\tSetServerMonitor(&event.ServerMonitor{\n\t\t\t\tTopologyDescriptionChanged: func(evt *event.TopologyDescriptionChangedEvent) {\n\t\t\t\t\ttopologyEvents <- evt\n\t\t\t\t},\n\t\t\t}))\n\t\tfor evt := range topologyEvents {\n\t\t\tservers := evt.NewDescription.Servers\n\t\t\tif len(servers) == 2 && servers[0].Kind == description.ServerKindMongos.String() &&\n\t\t\t\tservers[1].Kind == description.ServerKindMongos.String() {\n\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)\n\t\tdefer cancel()\n\n\t\tif err := awaitSaturation(ctx, mt, tpm, maxPoolSize); err != nil {\n\t\t\tmt.Fatalf(\"Error awaiting saturation: %v\", err.Error())\n\t\t}\n\n\t\tcounts, checkOutEvents := runsServerSelection(mt, tpm, 10, 10)\n\t\t// Calculate the frequency that the server with the failpoint was selected. Assert that it\n\t\t// was selected less than 25% of the time.\n\t\tfrequency := float64(counts[failpointHost]) / float64(len(checkOutEvents))\n\t\tassert.Lessf(mt,\n\t\t\tfrequency,\n\t\t\t0.25,\n\t\t\t\"expected failpoint host %q to be selected less than 25%% of the time\",\n\t\t\tfailpointHost)\n\t})\n\n\tmtOpts = mtest.NewOptions().Topologies(mtest.Sharded)\n\tmt.RunOpts(\"operationCount-based selection within latency window, no failpoint\", mtOpts, func(mt *mtest.T) {\n\t\t// TODO(GODRIVER-2842): Fix and unskip this test case.\n\t\tmt.Skip(\"Test fails frequently, skipping. See GODRIVER-2842\")\n\n\t\t_, err := mt.Coll.InsertOne(context.Background(), bson.D{})\n\t\trequire.NoError(mt, err, \"InsertOne() error\")\n\n\t\thosts, err := mongoutil.HostsFromURI(mtest.ClusterURI())\n\n\t\trequire.NoError(mt, err)\n\t\trequire.GreaterOrEqualf(mt, len(hosts), 2, \"test cluster must have at least 2 mongos hosts\")\n\n\t\t// Reset the client with exactly 2 mongos hosts. Use a ServerMonitor to wait for both mongos\n\t\t// host descriptions to move from kind \"Unknown\" to kind \"Mongos\".\n\t\ttopologyEvents := make(chan *event.TopologyDescriptionChangedEvent, 10)\n\t\ttpm := eventtest.NewTestPoolMonitor()\n\t\tmt.ResetClient(options.Client().\n\t\t\tSetHosts(hosts[:2]).\n\t\t\tSetPoolMonitor(tpm.PoolMonitor).\n\t\t\tSetLocalThreshold(localThreshold).\n\t\t\tSetMaxPoolSize(maxPoolSize).\n\t\t\tSetMinPoolSize(maxPoolSize).\n\t\t\tSetServerMonitor(&event.ServerMonitor{\n\t\t\t\tTopologyDescriptionChanged: func(evt *event.TopologyDescriptionChangedEvent) {\n\t\t\t\t\ttopologyEvents <- evt\n\t\t\t\t},\n\t\t\t}))\n\t\tfor evt := range topologyEvents {\n\t\t\tservers := evt.NewDescription.Servers\n\t\t\tif len(servers) == 2 && servers[0].Kind == description.ServerKindMongos.String() &&\n\t\t\t\tservers[1].Kind == description.ServerKindMongos.String() {\n\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)\n\t\tdefer cancel()\n\n\t\tif err := awaitSaturation(ctx, mt, tpm, maxPoolSize); err != nil {\n\t\t\tmt.Fatalf(\"Error awaiting saturation: %v\", err.Error())\n\t\t}\n\n\t\tcounts, checkOutEvents := runsServerSelection(mt, tpm, 10, 100)\n\t\t// Calculate the frequency that each server was selected. Assert that each server was\n\t\t// selected 50% (+/- 10%) of the time.\n\t\tfor addr, count := range counts {\n\t\t\tfrequency := float64(count) / float64(len(checkOutEvents))\n\t\t\tassert.InDeltaf(mt,\n\t\t\t\t0.5,\n\t\t\t\tfrequency,\n\t\t\t\t0.1,\n\t\t\t\t\"expected server %q to be selected 50%% (+/- 10%%) of the time, but was selected %v%% of the time\",\n\t\t\t\taddr, frequency*100)\n\t\t}\n\t})\n}\n"
  },
  {
    "path": "internal/integration/sessions_mongocryptd_prose_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2023-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage integration\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"net\"\n\t\"net/url\"\n\t\"os/exec\"\n\t\"runtime\"\n\t\"strconv\"\n\t\"testing\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/event\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/integration/mtest\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/require\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n)\n\ntype mongocryptdProcess struct {\n\tcmd *exec.Cmd\n}\n\n// start will start a mongocryptd server in the background on the OS.\nfunc (p *mongocryptdProcess) start(port int) error {\n\targs := []string{\n\t\t\"mongocryptd\",\n\t\t\"--port\", strconv.Itoa(port),\n\t}\n\n\tp.cmd = exec.Command(args[0], args[1:]...) //nolint:gosec\n\tp.cmd.Stderr = p.cmd.Stdout\n\n\treturn p.cmd.Start()\n}\n\n// close will kill the underlying process on the command.\nfunc (p *mongocryptdProcess) close() error {\n\tif p.cmd.Process == nil {\n\t\treturn nil\n\t}\n\n\tif err := p.cmd.Process.Kill(); err != nil {\n\t\treturn fmt.Errorf(\"failed to terminate mongocryptd process: %w\", err)\n\t}\n\n\t// Release instead of wait to avoid blocking in CI.\n\terr := p.cmd.Process.Release()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to release mongocryptd: %w\", err)\n\t}\n\n\treturn nil\n}\n\n// newTestSessionMongocryptdProseClient will Create the client using the mongo\n// API rather than mtest. mtest will attempt to create a collection as\n// a database operation, which will not work on a mongocryptd server. A\n// mongocryptd server does not support operations on a database.\nfunc newTestSessionMongocryptdProseClient(mt *mtest.T) *mongo.Client {\n\tconst mongocryptdPort = 27022\n\n\t// Monitor the lsid value on commands. If an operation run in any\n\t// subtests contains an lsid, then the Go Driver wire message\n\t// construction has incorrectly interpreted that\n\t// LogicalSessionTimeoutMinutes was returned by the server on handshake.\n\tcmdMonitor := &event.CommandMonitor{\n\t\tStarted: func(_ context.Context, evt *event.CommandStartedEvent) {\n\t\t\t_, err := evt.Command.LookupErr(\"lsid\")\n\t\t\tassert.ErrorIs(mt, err, bsoncore.ErrElementNotFound)\n\t\t},\n\t}\n\n\turi := &url.URL{\n\t\tScheme: \"mongodb\",\n\t\tHost:   net.JoinHostPort(\"localhost\", strconv.Itoa(mongocryptdPort)),\n\t}\n\n\tproc := mongocryptdProcess{}\n\n\t// Start a mongocryptd server.\n\terr := proc.start(mongocryptdPort)\n\trequire.NoError(mt, err, \"failed to create a mongocryptd process: %v\", err)\n\n\tmt.Cleanup(func() {\n\t\terr := proc.close()\n\t\trequire.NoError(mt, err, \"failed to close mongocryptd: %v\", err)\n\t})\n\n\tclientOpts := options.\n\t\tClient().\n\t\tApplyURI(uri.String()).\n\t\tSetMonitor(cmdMonitor)\n\n\tclient, err := mongo.Connect(clientOpts)\n\trequire.NoError(mt, err, \"could not connect to mongocryptd: %v\", err)\n\n\treturn client\n}\n\nfunc TestSessionsMongocryptdProse(t *testing.T) {\n\tmtOpts := mtest.NewOptions().\n\t\tMinServerVersion(\"4.2\").\n\t\tTopologies(mtest.ReplicaSet, mtest.Sharded).\n\t\tCreateCollection(false).\n\t\tCreateClient(false)\n\n\t// Create a new instance of mtest (MongoDB testing framework) for this\n\t// test and configure it to control server versions.\n\tmt := mtest.New(t, mtOpts)\n\n\t// TODO(GODRIVER-3529): 'TerminateProcess: Access is denied' error when\n\t// killing mongocryptd process on Windows. It's unclear what is causing this.\n\tif runtime.GOOS == \"windows\" {\n\t\tmt.Skip(\"skipping to avoid 'TerminateProcess: Access is denied' error when killing mongocryptd process\")\n\t}\n\n\tproseTest18 := \"18. implicit session is ignored if connection does not support sessions\"\n\tmt.RunOpts(proseTest18, mtOpts, func(mt *mtest.T) {\n\t\tclient := newTestSessionMongocryptdProseClient(mt)\n\n\t\tmt.Cleanup(func() {\n\t\t\terr := client.Disconnect(context.Background())\n\t\t\trequire.NoError(mt, err, \"mongocryptd client could not disconnect: %v\", err)\n\t\t})\n\n\t\tcoll := client.Database(\"db\").Collection(\"coll\")\n\n\t\t// Send a read command to the server (e.g., findOne), ignoring\n\t\t// any errors from the server response\n\t\tmt.RunOpts(\"read\", mtOpts, func(_ *mtest.T) {\n\t\t\t_ = coll.FindOne(context.Background(), bson.D{{\"x\", 1}})\n\t\t})\n\n\t\t// Send a write command to the server (e.g., insertOne),\n\t\t// ignoring any errors from the server response\n\t\tmt.RunOpts(\"write\", mtOpts, func(_ *mtest.T) {\n\t\t\t_, _ = coll.InsertOne(context.Background(), bson.D{{\"x\", 1}})\n\t\t})\n\t})\n\n\tproseTest19 := \"19. explicit session raises an error if connection does not support sessions\"\n\tmt.RunOpts(proseTest19, mtOpts, func(mt *mtest.T) {\n\t\tclient := newTestSessionMongocryptdProseClient(mt)\n\n\t\tmt.Cleanup(func() {\n\t\t\terr := client.Disconnect(context.Background())\n\t\t\trequire.NoError(mt, err, \"mongocryptd client could not disconnect: %v\", err)\n\t\t})\n\n\t\t// Create a new explicit session by calling startSession (this\n\t\t// MUST NOT error).\n\t\tsession, err := client.StartSession()\n\t\trequire.NoError(mt, err, \"expected error to be nil, got %v\", err)\n\n\t\tdefer session.EndSession(context.Background())\n\n\t\tsessionCtx := mongo.NewSessionContext(context.TODO(), session)\n\n\t\terr = session.StartTransaction()\n\t\trequire.NoError(mt, err, \"expected error to be nil, got %v\", err)\n\n\t\tcoll := client.Database(\"db\").Collection(\"coll\")\n\n\t\t// Attempt to send a read command to the server (e.g., findOne)\n\t\t// with the explicit session passed in.\n\t\tmt.RunOpts(\"read\", mtOpts, func(mt *mtest.T) {\n\t\t\t// Assert that a client-side error is generated\n\t\t\t// indicating that sessions are not supported\n\t\t\tres := coll.FindOne(sessionCtx, bson.D{{\"x\", 1}})\n\t\t\tassert.EqualError(mt, res.Err(), \"current topology does not support sessions\")\n\t\t})\n\n\t\t// Attempt to send a write command to the server (e.g.,\n\t\t// ``insertOne``) with the explicit session passed in.\n\t\tmt.RunOpts(\"write\", mtOpts, func(mt *mtest.T) {\n\t\t\t// Assert that a client-side error is generated\n\t\t\t// indicating that sessions are not supported.\n\t\t\tres := coll.FindOne(sessionCtx, bson.D{{\"x\", 1}})\n\t\t\tassert.EqualError(mt, res.Err(), \"current topology does not support sessions\")\n\t\t})\n\t})\n}\n"
  },
  {
    "path": "internal/integration/sessions_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage integration\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"reflect\"\n\t\"sync\"\n\t\"sync/atomic\"\n\t\"testing\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/event\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/integration/mtest\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/mongoutil\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/require\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/testutil\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/readpref\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/writeconcern\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/session\"\n\t\"golang.org/x/sync/errgroup\"\n)\n\nfunc TestSessionPool(t *testing.T) {\n\tmt := mtest.New(t, mtest.NewOptions().MinServerVersion(\"3.6\").CreateClient(false))\n\n\tmt.Run(\"last use time updated\", func(mt *mtest.T) {\n\t\tsess, err := mt.Client.StartSession()\n\t\tassert.Nil(mt, err, \"StartSession error: %v\", err)\n\t\tdefer sess.EndSession(context.Background())\n\t\tinitialLastUsedTime := testutil.GetUnexportedFieldAs[*session.Client](sess, \"clientSession\").LastUsed\n\n\t\terr = mongo.WithSession(context.Background(), sess, func(sc context.Context) error {\n\t\t\treturn mt.Client.Ping(sc, readpref.Primary())\n\t\t})\n\t\tassert.Nil(mt, err, \"WithSession error: %v\", err)\n\n\t\tnewLastUsedTime := testutil.GetUnexportedFieldAs[*session.Client](sess, \"clientSession\").LastUsed\n\t\tassert.True(mt, newLastUsedTime.After(initialLastUsedTime),\n\t\t\t\"last used time %s is not after the initial last used time %s\", newLastUsedTime, initialLastUsedTime)\n\t})\n}\n\nfunc TestSessions(t *testing.T) {\n\tmtOpts := mtest.NewOptions().MinServerVersion(\"3.6\").Topologies(mtest.ReplicaSet, mtest.Sharded).\n\t\tCreateClient(false)\n\tmt := mtest.New(t, mtOpts)\n\n\tmt.Run(\"imperative API\", func(mt *mtest.T) {\n\t\tmt.Run(\"round trip Session object\", func(mt *mtest.T) {\n\t\t\t// Roundtrip a Session object through NewSessionContext/ContextFromSession and assert that it is correctly\n\t\t\t// stored/retrieved.\n\n\t\t\tsess, err := mt.Client.StartSession()\n\t\t\tassert.Nil(mt, err, \"StartSession error: %v\", err)\n\t\t\tdefer sess.EndSession(context.Background())\n\n\t\t\tctx := mongo.NewSessionContext(context.Background(), sess)\n\n\t\t\tgotSess := mongo.SessionFromContext(ctx)\n\t\t\tassert.NotNil(mt, gotSess, \"expected SessionFromContext to return non-nil value, got nil\")\n\t\t\tassert.Equal(mt, sess.ID(), gotSess.ID(), \"expected Session ID %v, got %v\", sess.ID(), gotSess.ID())\n\t\t})\n\n\t\ttxnOpts := mtest.NewOptions().RunOn(\n\t\t\tmtest.RunOnBlock{Topology: []mtest.TopologyKind{mtest.ReplicaSet}, MinServerVersion: \"4.0\"},\n\t\t\tmtest.RunOnBlock{Topology: []mtest.TopologyKind{mtest.Sharded}, MinServerVersion: \"4.2\"},\n\t\t)\n\t\tmt.RunOpts(\"run transaction\", txnOpts, func(mt *mtest.T) {\n\t\t\t// Test that the imperative sessions API can be used to run a transaction.\n\n\t\t\tcreateSessionContext := func(mt *mtest.T) context.Context {\n\t\t\t\tsess, err := mt.Client.StartSession()\n\t\t\t\tassert.Nil(mt, err, \"StartSession error: %v\", err)\n\n\t\t\t\treturn mongo.NewSessionContext(context.Background(), sess)\n\t\t\t}\n\n\t\t\tctx := createSessionContext(mt)\n\t\t\tsess := mongo.SessionFromContext(ctx)\n\t\t\tassert.NotNil(mt, sess, \"expected SessionFromContext to return non-nil value, got nil\")\n\t\t\tdefer sess.EndSession(context.Background())\n\n\t\t\terr := sess.StartTransaction()\n\t\t\tassert.Nil(mt, err, \"StartTransaction error: %v\", err)\n\n\t\t\tnumDocs := 2\n\t\t\tfor i := 0; i < numDocs; i++ {\n\t\t\t\t_, err = mt.Coll.InsertOne(ctx, bson.D{{\"x\", 1}})\n\t\t\t\tassert.Nil(mt, err, \"InsertOne error at index %d: %v\", i, err)\n\t\t\t}\n\n\t\t\t// Assert that the collection count is 0 before committing and numDocs after. This tests that the InsertOne\n\t\t\t// calls were actually executed in the transaction because the pre-commit count does not include them.\n\t\t\tassertCollectionCount(mt, 0)\n\t\t\terr = sess.CommitTransaction(ctx)\n\t\t\tassert.Nil(mt, err, \"CommitTransaction error: %v\", err)\n\t\t\tassertCollectionCount(mt, int64(numDocs))\n\t\t})\n\t})\n\n\tunackWcOpts := options.Collection().SetWriteConcern(writeconcern.Unacknowledged())\n\tmt.RunOpts(\"unacknowledged write\", mtest.NewOptions().CollectionOptions(unackWcOpts), func(mt *mtest.T) {\n\t\t// unacknowledged write during a session should result in an error\n\t\tsess, err := mt.Client.StartSession()\n\t\tassert.Nil(mt, err, \"StartSession error: %v\", err)\n\t\tdefer sess.EndSession(context.Background())\n\n\t\tvar res *mongo.InsertOneResult\n\n\t\terr = mongo.WithSession(context.Background(), sess, func(sc context.Context) error {\n\t\t\tres, err = mt.Coll.InsertOne(sc, bson.D{{\"x\", 1}})\n\n\t\t\treturn err\n\t\t})\n\n\t\tassert.NoError(mt, err)\n\t\tassert.False(mt, res.Acknowledged)\n\t})\n\n\t// Regression test for GODRIVER-2533. Note that this test assumes the race\n\t// detector is enabled (GODRIVER-2072).\n\tmt.Run(\"NumberSessionsInProgress data race\", func(mt *mtest.T) {\n\t\t// Use two goroutines to execute a few simultaneous runs of NumberSessionsInProgress\n\t\t// and a basic collection operation (CountDocuments).\n\t\tvar wg sync.WaitGroup\n\t\twg.Add(2)\n\n\t\tgo func() {\n\t\t\tdefer wg.Done()\n\n\t\t\tfor i := 0; i < 100; i++ {\n\t\t\t\ttime.Sleep(100 * time.Microsecond)\n\t\t\t\t_ = mt.Client.NumberSessionsInProgress()\n\t\t\t}\n\t\t}()\n\t\tgo func() {\n\t\t\tdefer wg.Done()\n\n\t\t\tfor i := 0; i < 100; i++ {\n\t\t\t\ttime.Sleep(100 * time.Microsecond)\n\t\t\t\t_, err := mt.Coll.CountDocuments(context.Background(), bson.D{})\n\t\t\t\tassert.Nil(mt, err, \"CountDocument error: %v\", err)\n\t\t\t}\n\t\t}()\n\n\t\twg.Wait()\n\t})\n}\n\nfunc TestSessionsProse(t *testing.T) {\n\tmtOpts := mtest.\n\t\tNewOptions().\n\t\tMinServerVersion(\"3.6\").\n\t\tTopologies(mtest.ReplicaSet, mtest.Sharded).\n\t\tCreateClient(false)\n\n\tmt := mtest.New(t, mtOpts)\n\n\thosts, err := mongoutil.HostsFromURI(mtest.ClusterURI())\n\trequire.NoError(t, err)\n\n\tmt.Run(\"1 setting both snapshot and causalConsistency to true is not allowed\", func(mt *mtest.T) {\n\t\t// causalConsistency and snapshot are mutually exclusive\n\t\tsessOpts := options.Session().SetCausalConsistency(true).SetSnapshot(true)\n\t\t_, err := mt.Client.StartSession(sessOpts)\n\t\tassert.NotNil(mt, err, \"expected StartSession error, got nil\")\n\t\texpectedErr := errors.New(\"causal consistency and snapshot cannot both be set for a session\")\n\t\tassert.Equal(mt, expectedErr, err, \"expected error %v, got %v\", expectedErr, err)\n\t})\n\n\tmt.Run(\"2 pool is LIFO\", func(mt *mtest.T) {\n\t\taSess, err := mt.Client.StartSession()\n\t\tassert.Nil(mt, err, \"StartSession error: %v\", err)\n\t\tbSess, err := mt.Client.StartSession()\n\t\tassert.Nil(mt, err, \"StartSession error: %v\", err)\n\n\t\t// end the sessions to return them to the pool\n\t\taSess.EndSession(context.Background())\n\t\tbSess.EndSession(context.Background())\n\n\t\tfirstSess, err := mt.Client.StartSession()\n\t\tassert.Nil(mt, err, \"StartSession error: %v\", err)\n\t\tdefer firstSess.EndSession(context.Background())\n\t\twant := bSess.ID()\n\t\tgot := firstSess.ID()\n\t\tassert.True(mt, sessionIDsEqual(mt, want, got), \"expected session ID %v, got %v\", want, got)\n\n\t\tsecondSess, err := mt.Client.StartSession()\n\t\tassert.Nil(mt, err, \"StartSession error: %v\", err)\n\t\tdefer secondSess.EndSession(context.Background())\n\t\twant = aSess.ID()\n\t\tgot = secondSess.ID()\n\t\tassert.True(mt, sessionIDsEqual(mt, want, got), \"expected session ID %v, got %v\", want, got)\n\t})\n\n\t// Pin to a single mongos so heartbeats/handshakes to other mongoses\n\t// won't cause errors.\n\tclusterTimeOpts := mtest.NewOptions().\n\t\tClientOptions(options.Client().SetHeartbeatInterval(50 * time.Second)).\n\t\tClientType(mtest.Pinned).\n\t\tCreateClient(false)\n\n\tmt.RunOpts(\"3 clusterTime in commands\", clusterTimeOpts, func(mt *mtest.T) {\n\t\tserverStatus := sessionFunction{\"server status\", \"database\", \"RunCommand\", []any{bson.D{{\"serverStatus\", 1}}}}\n\t\tinsert := sessionFunction{\"insert one\", \"collection\", \"InsertOne\", []any{bson.D{{\"x\", 1}}}}\n\t\tagg := sessionFunction{\"aggregate\", \"collection\", \"Aggregate\", []any{mongo.Pipeline{}}}\n\t\tfind := sessionFunction{\"find\", \"collection\", \"Find\", []any{bson.D{}}}\n\n\t\tsessionFunctions := []sessionFunction{serverStatus, insert, agg, find}\n\t\tfor _, sf := range sessionFunctions {\n\t\t\tmt.Run(sf.name, func(mt *mtest.T) {\n\t\t\t\terr := sf.execute(mt, nil)\n\t\t\t\tassert.Nil(mt, err, \"%v error: %v\", sf.name, err)\n\n\t\t\t\t// assert $clusterTime was sent to server\n\t\t\t\tstarted := mt.GetStartedEvent()\n\t\t\t\tassert.NotNil(mt, started, \"expected started event, got nil\")\n\t\t\t\t_, err = started.Command.LookupErr(\"$clusterTime\")\n\t\t\t\tassert.Nil(mt, err, \"$clusterTime not sent\")\n\n\t\t\t\t// record response cluster time\n\t\t\t\tsucceeded := mt.GetSucceededEvent()\n\t\t\t\tassert.NotNil(mt, succeeded, \"expected succeeded event, got nil\")\n\t\t\t\treplyClusterTimeVal, err := succeeded.Reply.LookupErr(\"$clusterTime\")\n\t\t\t\tassert.Nil(mt, err, \"$clusterTime not found in response\")\n\n\t\t\t\t// call function again\n\t\t\t\terr = sf.execute(mt, nil)\n\t\t\t\tassert.Nil(mt, err, \"%v error: %v\", sf.name, err)\n\n\t\t\t\t// find cluster time sent to server and assert it is the same as the one in the previous response\n\t\t\t\tsentClusterTimeVal, err := mt.GetStartedEvent().Command.LookupErr(\"$clusterTime\")\n\t\t\t\tassert.Nil(mt, err, \"$clusterTime not sent\")\n\t\t\t\treplyClusterTimeDoc := replyClusterTimeVal.Document()\n\t\t\t\tsentClusterTimeDoc := sentClusterTimeVal.Document()\n\t\t\t\tassert.Equal(mt, replyClusterTimeDoc, sentClusterTimeDoc,\n\t\t\t\t\t\"expected cluster time %v, got %v\", replyClusterTimeDoc, sentClusterTimeDoc)\n\t\t\t})\n\t\t}\n\t})\n\n\tmt.RunOpts(\"4 explicit and implicit session arguments\", noClientOpts, func(mt *mtest.T) {\n\t\t// lsid is included in commands with explicit and implicit sessions\n\n\t\tsessionFunctions := createFunctionsSlice()\n\t\tfor _, sf := range sessionFunctions {\n\t\t\tmt.Run(sf.name, func(mt *mtest.T) {\n\t\t\t\t// explicit session\n\t\t\t\tsess, err := mt.Client.StartSession()\n\t\t\t\tassert.Nil(mt, err, \"StartSession error: %v\", err)\n\t\t\t\tdefer sess.EndSession(context.Background())\n\t\t\t\tmt.ClearEvents()\n\n\t\t\t\t_ = sf.execute(mt, sess) // don't check error because we only care about lsid\n\t\t\t\t_, wantID := sess.ID().Lookup(\"id\").Binary()\n\t\t\t\tgotID := extractSentSessionID(mt)\n\t\t\t\tassert.True(mt, bytes.Equal(wantID, gotID), \"expected session ID %v, got %v\", wantID, gotID)\n\n\t\t\t\t// implicit session\n\t\t\t\t_ = sf.execute(mt, nil)\n\t\t\t\tgotID = extractSentSessionID(mt)\n\t\t\t\tassert.NotNil(mt, gotID, \"expected lsid, got nil\")\n\t\t\t})\n\t\t}\n\t})\n\n\tmt.Run(\"5 session argument is for the right client\", func(mt *mtest.T) {\n\t\t// a session can only be used in commands associated with the client that created it\n\n\t\tsessionFunctions := createFunctionsSlice()\n\t\tsess, err := mt.Client.StartSession()\n\t\tassert.Nil(mt, err, \"StartSession error: %v\", err)\n\t\tdefer sess.EndSession(context.Background())\n\n\t\tfor _, sf := range sessionFunctions {\n\t\t\tmt.Run(sf.name, func(mt *mtest.T) {\n\t\t\t\terr = sf.execute(mt, sess)\n\t\t\t\tassert.Equal(mt, mongo.ErrWrongClient, err, \"expected error %v, got %v\", mongo.ErrWrongClient, err)\n\t\t\t})\n\t\t}\n\t})\n\n\tconst proseTest6 = \"6 no further operations can be performed using a session after endSession has been called\"\n\tmt.RunOpts(proseTest6, noClientOpts, func(mt *mtest.T) {\n\t\t// an ended session cannot be used in commands\n\n\t\tsessionFunctions := createFunctionsSlice()\n\t\tfor _, sf := range sessionFunctions {\n\t\t\tmt.Run(sf.name, func(mt *mtest.T) {\n\t\t\t\tsess, err := mt.Client.StartSession()\n\t\t\t\tassert.Nil(mt, err, \"StartSession error: %v\", err)\n\t\t\t\tsess.EndSession(context.Background())\n\n\t\t\t\terr = sf.execute(mt, sess)\n\t\t\t\tassert.Equal(mt, session.ErrSessionEnded, err, \"expected error %v, got %v\", session.ErrSessionEnded, err)\n\t\t\t})\n\t\t}\n\t})\n\n\tmt.Run(\"7 authenticating as multiple users suppresses implicit sessions\", func(mt *mtest.T) {\n\t\tmt.Skip(\"Go Driver does not allow simultaneous authentication with multiple users.\")\n\t})\n\n\tmt.Run(\"8 client side cursor that exhausts the results on the initial query immediately returns the implicit session to the pool\",\n\t\tfunc(mt *mtest.T) {\n\t\t\t// implicit sessions are returned to the server session pool\n\n\t\t\tdoc := bson.D{{\"x\", 1}}\n\t\t\t_, err := mt.Coll.InsertOne(context.Background(), doc)\n\t\t\tassert.Nil(mt, err, \"InsertOne error: %v\", err)\n\t\t\t_, err = mt.Coll.InsertOne(context.Background(), doc)\n\t\t\tassert.Nil(mt, err, \"InsertOne error: %v\", err)\n\n\t\t\t// create a cursor that will hold onto an implicit session and record the sent session ID\n\t\t\tmt.ClearEvents()\n\t\t\tcursor, err := mt.Coll.Find(context.Background(), bson.D{})\n\t\t\tassert.Nil(mt, err, \"Find error: %v\", err)\n\t\t\tfindID := extractSentSessionID(mt)\n\t\t\tassert.True(mt, cursor.Next(context.Background()), \"expected Next true, got false\")\n\n\t\t\t// execute another operation and verify the find session ID was reused\n\t\t\t_, err = mt.Coll.DeleteOne(context.Background(), bson.D{})\n\t\t\tassert.Nil(mt, err, \"DeleteOne error: %v\", err)\n\t\t\tdeleteID := extractSentSessionID(mt)\n\t\t\tassert.Equal(mt, findID, deleteID, \"expected session ID %v, got %v\", findID, deleteID)\n\t\t})\n\n\tmt.Run(\"9 client side cursor that exhausts the results after a getMore immediately returns the implicit session to the pool\",\n\t\tfunc(mt *mtest.T) {\n\t\t\t// Client-side cursor that exhausts the results after a getMore immediately returns the implicit session to the pool.\n\n\t\t\tvar docs []any\n\t\t\tfor i := 0; i < 5; i++ {\n\t\t\t\tdocs = append(docs, bson.D{{\"x\", i}})\n\t\t\t}\n\n\t\t\t_, err := mt.Coll.InsertMany(context.Background(), docs)\n\t\t\tassert.Nil(mt, err, \"InsertMany error: %v\", err)\n\n\t\t\t// run a find that will hold onto the implicit session and record the session ID\n\t\t\tmt.ClearEvents()\n\t\t\tcursor, err := mt.Coll.Find(context.Background(), bson.D{}, options.Find().SetBatchSize(3))\n\t\t\tassert.Nil(mt, err, \"Find error: %v\", err)\n\t\t\tfindID := extractSentSessionID(mt)\n\n\t\t\t// iterate past 4 documents, forcing a getMore. session should be returned to pool after getMore\n\t\t\tfor i := 0; i < 4; i++ {\n\t\t\t\tassert.True(mt, cursor.Next(context.Background()), \"Next returned false on iteration %v\", i)\n\t\t\t}\n\n\t\t\t// execute another operation and verify the find session ID was reused\n\t\t\t_, err = mt.Coll.DeleteOne(context.Background(), bson.D{})\n\t\t\tassert.Nil(mt, err, \"DeleteOne error: %v\", err)\n\t\t\tdeleteID := extractSentSessionID(mt)\n\t\t\tassert.Equal(mt, findID, deleteID, \"expected session ID %v, got %v\", findID, deleteID)\n\t\t})\n\n\tmt.Run(\"10 no remaining sessions are checked out after each functional test\", func(mt *mtest.T) {\n\t\tmt.Skip(\"This is tested individually in each functional test.\")\n\t})\n\n\tmt.Run(\"11 for every combination of topology and readPreference, ensure that find and getMore both send the same session id\", func(mt *mtest.T) {\n\t\tvar docs []any\n\t\tfor i := 0; i < 3; i++ {\n\t\t\tdocs = append(docs, bson.D{{\"x\", i}})\n\t\t}\n\t\t_, err := mt.Coll.InsertMany(context.Background(), docs)\n\t\tassert.Nil(mt, err, \"InsertMany error: %v\", err)\n\n\t\t// run a find that will hold onto an implicit session and record the session ID\n\t\tmt.ClearEvents()\n\t\tcursor, err := mt.Coll.Find(context.Background(), bson.D{}, options.Find().SetBatchSize(2))\n\t\tassert.Nil(mt, err, \"Find error: %v\", err)\n\t\tfindID := extractSentSessionID(mt)\n\t\tassert.NotNil(mt, findID, \"expected session ID for find, got nil\")\n\n\t\t// iterate over all documents and record the session ID of the getMore\n\t\tfor i := 0; i < 3; i++ {\n\t\t\tassert.True(mt, cursor.Next(context.Background()), \"Next returned false on iteration %v\", i)\n\t\t}\n\t\tgetMoreID := extractSentSessionID(mt)\n\t\tassert.Equal(mt, findID, getMoreID, \"expected session ID %v, got %v\", findID, getMoreID)\n\t})\n\n\tsessallocopts := mtest.NewOptions().ClientOptions(options.Client().SetMaxPoolSize(1).SetRetryWrites(true).\n\t\tSetHosts(hosts[:1]))\n\tmt.RunOpts(\"14 implicit session allocation\", sessallocopts, func(mt *mtest.T) {\n\t\t// TODO(GODRIVER-2844): Fix and unskip this test case.\n\t\tmt.Skip(\"Test fails frequently, skipping. See GODRIVER-2844\")\n\n\t\tops := map[string]func(ctx context.Context) error{\n\t\t\t\"insert\": func(ctx context.Context) error {\n\t\t\t\t_, err := mt.Coll.InsertOne(ctx, bson.D{})\n\t\t\t\treturn err\n\t\t\t},\n\t\t\t\"delete\": func(ctx context.Context) error {\n\t\t\t\t_, err := mt.Coll.DeleteOne(ctx, bson.D{})\n\t\t\t\treturn err\n\t\t\t},\n\t\t\t\"update\": func(ctx context.Context) error {\n\t\t\t\t_, err := mt.Coll.UpdateOne(ctx, bson.D{}, bson.D{{\"$set\", bson.D{{\"a\", 1}}}})\n\t\t\t\treturn err\n\t\t\t},\n\t\t\t\"bulkWrite\": func(ctx context.Context) error {\n\t\t\t\tmodel := mongo.NewUpdateOneModel().\n\t\t\t\t\tSetFilter(bson.D{}).\n\t\t\t\t\tSetUpdate(bson.D{{\"$set\", bson.D{{\"a\", 1}}}})\n\t\t\t\t_, err := mt.Coll.BulkWrite(ctx, []mongo.WriteModel{model})\n\t\t\t\treturn err\n\t\t\t},\n\t\t\t\"findOneAndDelete\": func(ctx context.Context) error {\n\t\t\t\tresult := mt.Coll.FindOneAndDelete(ctx, bson.D{})\n\t\t\t\tif err := result.Err(); err != nil && !errors.Is(err, mongo.ErrNoDocuments) {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\treturn nil\n\t\t\t},\n\t\t\t\"findOneAndUpdate\": func(ctx context.Context) error {\n\t\t\t\tresult := mt.Coll.FindOneAndUpdate(ctx, bson.D{},\n\t\t\t\t\tbson.D{{\"$set\", bson.D{{\"a\", 1}}}})\n\n\t\t\t\tif err := result.Err(); err != nil && !errors.Is(err, mongo.ErrNoDocuments) {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\treturn nil\n\t\t\t},\n\t\t\t\"findOneAndReplace\": func(ctx context.Context) error {\n\t\t\t\tresult := mt.Coll.FindOneAndReplace(ctx, bson.D{}, bson.D{{\"a\", 1}})\n\t\t\t\tif err := result.Err(); err != nil && !errors.Is(err, mongo.ErrNoDocuments) {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\treturn nil\n\t\t\t},\n\t\t\t\"find\": func(ctx context.Context) error {\n\t\t\t\tcursor, err := mt.Coll.Find(ctx, bson.D{})\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\treturn cursor.All(ctx, &bson.A{})\n\t\t\t},\n\t\t}\n\n\t\t// maintainedOneSession asserts that exactly one session is used for all operations at least once\n\t\t// across the retries of this test.\n\t\tvar maintainedOneSession bool\n\n\t\t// minimumSessionCount asserts the least amount of sessions used over all the retries of the\n\t\t// operations. For example, if we retry 5 times we could result in session use { 1, 2, 1, 1, 6 }. In\n\t\t// this case, minimumSessionCount should be 1.\n\t\tvar minimumSessionCount int\n\n\t\t// limitedSessionUse asserts that the number of allocated sessions is strictly less than the number of\n\t\t// concurrent operations in every retry of this test. In this instance it would be less than (but NOT\n\t\t// equal to the number of operations).\n\t\tlimitedSessionUse := true\n\n\t\tretrycount := 5\n\t\tfor i := 1; i <= retrycount; i++ {\n\t\t\terrs, ctx := errgroup.WithContext(context.Background())\n\n\t\t\t// Execute the ops list concurrently.\n\t\t\tfor cmd, op := range ops {\n\t\t\t\top := op\n\t\t\t\tcmd := cmd\n\t\t\t\terrs.Go(func() error {\n\t\t\t\t\tif err := op(ctx); err != nil {\n\t\t\t\t\t\treturn fmt.Errorf(\"error running %s operation: %w\", cmd, err)\n\t\t\t\t\t}\n\t\t\t\t\treturn nil\n\t\t\t\t})\n\t\t\t}\n\t\t\terr := errs.Wait()\n\t\t\tassert.Nil(mt, err, \"expected no error, got: %v\", err)\n\n\t\t\t// Get all started events and collect them by the session ID.\n\t\t\tset := make(map[string]bool)\n\t\t\tfor _, event := range mt.GetAllStartedEvents() {\n\t\t\t\tlsid := event.Command.Lookup(\"lsid\")\n\t\t\t\tset[lsid.String()] = true\n\t\t\t}\n\n\t\t\tsetSize := len(set)\n\t\t\tif setSize == 1 {\n\t\t\t\tmaintainedOneSession = true\n\t\t\t} else if setSize < minimumSessionCount || minimumSessionCount == 0 {\n\t\t\t\t// record the minimum number of sessions we used over all retries.\n\t\t\t\tminimumSessionCount = setSize\n\t\t\t}\n\n\t\t\tif setSize >= len(ops) {\n\t\t\t\tlimitedSessionUse = false\n\t\t\t}\n\t\t}\n\n\t\toneSessMsg := \"expected one session across all %v operations for at least 1/%v retries, got: %v\"\n\t\tassert.True(mt, maintainedOneSession, oneSessMsg, len(ops), retrycount, minimumSessionCount)\n\n\t\tlimitedSessMsg := \"expected session count to be less than the number of operations: %v\"\n\t\tassert.True(mt, limitedSessionUse, limitedSessMsg, len(ops))\n\t})\n\n\tmt.ResetClient(options.Client())\n\tclient := mt.Client\n\theartbeatStarted := make(chan struct{}, 1)\n\theartbeatSucceeded := make(chan struct{}, 1)\n\tvar clusterTimeAdvanced uint32\n\tserverMonitor := &event.ServerMonitor{\n\t\tServerHeartbeatStarted: func(*event.ServerHeartbeatStartedEvent) {\n\t\t\tif atomic.LoadUint32(&clusterTimeAdvanced) == 1 {\n\t\t\t\tselect {\n\t\t\t\tcase heartbeatStarted <- struct{}{}:\n\t\t\t\t\t// NOOP\n\t\t\t\tdefault:\n\t\t\t\t\t// NOOP\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\tServerHeartbeatSucceeded: func(*event.ServerHeartbeatSucceededEvent) {\n\t\t\tif atomic.LoadUint32(&clusterTimeAdvanced) == 1 {\n\t\t\t\tselect {\n\t\t\t\tcase heartbeatSucceeded <- struct{}{}:\n\t\t\t\t\t// NOOP\n\t\t\t\tdefault:\n\t\t\t\t\t// NOOP\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t}\n\tpingOpts := mtest.NewOptions().\n\t\tCreateCollection(false).\n\t\tClientOptions(options.Client().\n\t\t\tSetServerMonitor(serverMonitor).\n\t\t\tSetHeartbeatInterval(500 * time.Millisecond). // Minimum interval\n\t\t\tSetDirect(true)).\n\t\tClientType(mtest.Pinned)\n\tmt.RunOpts(\"20 Drivers do not gossip $clusterTime on SDAM commands\", pingOpts, func(mt *mtest.T) {\n\t\twait := func(mt *mtest.T, ch <-chan struct{}, label string) {\n\t\t\tmt.Helper()\n\n\t\t\tselect {\n\t\t\tcase <-ch:\n\t\t\tcase <-time.After(5 * time.Second):\n\t\t\t\tmt.Fatalf(\"timed out waiting for %s\", label)\n\t\t\t}\n\t\t}\n\n\t\terr := mt.Client.Ping(context.Background(), readpref.Primary())\n\t\tassert.NoError(mt, err, \"expected no error, got: %v\", err)\n\n\t\t_, err = client.Database(\"test\").Collection(\"test\").InsertOne(context.Background(), bson.D{{\"advance\", \"$clusterTime\"}})\n\t\trequire.NoError(mt, err, \"expected no error inserting document, got: %v\", err)\n\n\t\tatomic.StoreUint32(&clusterTimeAdvanced, 1)\n\t\twait(mt, heartbeatStarted, \"ServerHeartbeatStartedEvent\")\n\t\twait(mt, heartbeatSucceeded, \"ServerHeartbeatSucceededEvent\")\n\n\t\terr = mt.Client.Ping(context.Background(), readpref.Primary())\n\t\trequire.NoError(mt, err, \"expected no error, got: %v\", err)\n\n\t\tsucceededEvents := mt.GetAllSucceededEvents()\n\t\trequire.Len(mt, succeededEvents, 2, \"expected 2 succeeded events, got: %v\", len(succeededEvents))\n\t\trequire.Equal(mt, \"ping\", succeededEvents[0].CommandName, \"expected first command to be ping, got: %v\", succeededEvents[0].CommandName)\n\t\tinitialClusterTime, err := succeededEvents[0].Reply.LookupErr(\"$clusterTime\")\n\t\trequire.NoError(mt, err, \"$clusterTime not found in response\")\n\n\t\tstartedEvents := mt.GetAllStartedEvents()\n\t\trequire.Len(mt, startedEvents, 2, \"expected 2 started events, got: %v\", len(startedEvents))\n\t\trequire.Equal(mt, \"ping\", startedEvents[1].CommandName, \"expected second command to be ping, got: %v\", startedEvents[1].CommandName)\n\t\tcurrentClusterTime, err := startedEvents[1].Command.LookupErr(\"$clusterTime\")\n\t\trequire.NoError(mt, err, \"$clusterTime not found in command\")\n\t\tassert.Equal(mt, initialClusterTime, currentClusterTime, \"expected same cluster time, got %v and %v\", initialClusterTime, currentClusterTime)\n\t})\n}\n\nfunc TestSessionsProse_21_SettingSnapshotTimeWithoutSnapshot(t *testing.T) {\n\t// 21. Having snapshotTime set and snapshot set to false is not allowed.\n\tmtOpts := mtest.\n\t\tNewOptions().\n\t\tMinServerVersion(\"5.0\").\n\t\tTopologies(mtest.ReplicaSet, mtest.Sharded)\n\n\tmt := mtest.New(t, mtOpts)\n\n\tmt.Setup()\n\n\t// Start a session by calling startSession with snapshot = false and\n\t// snapshotTime = new Timestamp(1).\n\tsessOpts := options.Session().SetSnapshot(false).SetSnapshotTime(bson.Timestamp{T: 1})\n\n\t_, err := mt.Client.StartSession(sessOpts)\n\trequire.Error(t, err)\n\trequire.Contains(t, err.Error(), \"snapshotTime cannot be set when snapshot is false\")\n}\n\nfunc TestSessionsProse_22_SnapshotTimeGetterReturnsErrorForNonSnapshotSessions(t *testing.T) {\n\t// 22. Retrieving `snapshotTime` on a non-snapshot session raises an error\n\tt.Skip(\"Skipping test for prose 22; Go driver does not have a getter that raises an error.\")\n}\n\nfunc TestSessionsProse_23_EnsureSnapshotTimeIsImmutable(t *testing.T) {\n\t// 23. Ensure `snapshotTime` is Read-Only\n\n\tmtOpts := mtest.\n\t\tNewOptions().\n\t\tMinServerVersion(\"5.0\").\n\t\tTopologies(mtest.ReplicaSet, mtest.Sharded)\n\n\tmt := mtest.New(t, mtOpts)\n\n\tmt.Setup()\n\n\tsess, err := mt.Client.StartSession(options.Session().SetSnapshot(false))\n\trequire.NoError(mt, err)\n\tdefer sess.EndSession(context.Background())\n\n\t// Verify initial state\n\trequire.Empty(mt, sess.SnapshotTime())\n\n\t// Attempt mutation through one GetClientSessionFromSession call\n\tsnapshotTime := sess.SnapshotTime()\n\tsnapshotTime.I = 1\n\n\t// Second GetClientSessionFromSession call should return independent copy\n\trequire.Empty(mt, sess.SnapshotTime())\n}\n\nfunc TestSession_TransactionRunning(t *testing.T) {\n\tmtOpts := mtest.\n\t\tNewOptions().\n\t\tTopologies(mtest.ReplicaSet, mtest.Sharded)\n\n\tmt := mtest.New(t, mtOpts)\n\n\tmt.Run(\"empty session returns false\", func(mt *mtest.T) {\n\t\trequire.False(mt, (&mongo.Session{}).TransactionRunning())\n\t})\n\n\tmt.Run(\"no transaction returns false\", func(mt *mtest.T) {\n\t\tsess, err := mt.Client.StartSession()\n\t\trequire.NoError(mt, err)\n\n\t\tdefer sess.EndSession(context.Background())\n\n\t\trequire.False(mt, sess.TransactionRunning())\n\t})\n\n\tmt.Run(\"transaction returns true\", func(mt *mtest.T) {\n\t\tsess, err := mt.Client.StartSession()\n\t\trequire.NoError(mt, err)\n\n\t\tdefer sess.EndSession(context.Background())\n\n\t\trequire.NoError(mt, sess.StartTransaction())\n\t\trequire.True(mt, sess.TransactionRunning())\n\t})\n\n\tmt.Run(\"after commit returns false\", func(mt *mtest.T) {\n\t\tsess, err := mt.Client.StartSession()\n\t\trequire.NoError(mt, err)\n\n\t\tdefer sess.EndSession(context.Background())\n\n\t\trequire.NoError(mt, sess.StartTransaction())\n\t\trequire.NoError(mt, sess.CommitTransaction(context.Background()))\n\t\trequire.False(mt, sess.TransactionRunning())\n\t})\n\n\tmt.Run(\"after abort returns false\", func(mt *mtest.T) {\n\t\tsess, err := mt.Client.StartSession()\n\t\trequire.NoError(mt, err)\n\n\t\tdefer sess.EndSession(context.Background())\n\n\t\trequire.NoError(mt, sess.StartTransaction())\n\t\trequire.NoError(mt, sess.AbortTransaction(context.Background()))\n\t\trequire.False(mt, sess.TransactionRunning())\n\t})\n}\n\ntype sessionFunction struct {\n\tname   string\n\ttarget string\n\tfnName string\n\tparams []any // should not include context\n}\n\nfunc (sf sessionFunction) execute(mt *mtest.T, sess *mongo.Session) error {\n\tvar target reflect.Value\n\tswitch sf.target {\n\tcase \"client\":\n\t\ttarget = reflect.ValueOf(mt.Client)\n\tcase \"database\":\n\t\t// use a different database for drops because any executed after the drop will get \"database not found\"\n\t\t// errors on sharded clusters\n\t\tif sf.name != \"drop database\" {\n\t\t\ttarget = reflect.ValueOf(mt.DB)\n\t\t\tbreak\n\t\t}\n\t\ttarget = reflect.ValueOf(mt.Client.Database(\"sessionsTestsDropDatabase\"))\n\tcase \"collection\":\n\t\ttarget = reflect.ValueOf(mt.Coll)\n\tcase \"indexView\":\n\t\ttarget = reflect.ValueOf(mt.Coll.Indexes())\n\tdefault:\n\t\tmt.Fatalf(\"unrecognized target: %v\", sf.target)\n\t}\n\n\tfn := target.MethodByName(sf.fnName)\n\tparamsValues := interfaceSliceToValueSlice(sf.params)\n\n\tif sess != nil {\n\t\treturn mongo.WithSession(context.Background(), sess, func(sc context.Context) error {\n\t\t\tvalueArgs := []reflect.Value{reflect.ValueOf(sc)}\n\t\t\tvalueArgs = append(valueArgs, paramsValues...)\n\t\t\treturnValues := fn.Call(valueArgs)\n\t\t\treturn extractReturnError(returnValues)\n\t\t})\n\t}\n\tvalueArgs := []reflect.Value{reflect.ValueOf(context.Background())}\n\tvalueArgs = append(valueArgs, paramsValues...)\n\treturnValues := fn.Call(valueArgs)\n\treturn extractReturnError(returnValues)\n}\n\nfunc createFunctionsSlice() []sessionFunction {\n\tinsertManyDocs := []any{bson.D{{\"x\", 1}}}\n\tfooIndex := mongo.IndexModel{\n\t\tKeys:    bson.D{{\"foo\", -1}},\n\t\tOptions: options.Index().SetName(\"fooIndex\"),\n\t}\n\tmanyIndexes := []mongo.IndexModel{fooIndex}\n\tupdateDoc := bson.D{{\"$inc\", bson.D{{\"x\", 1}}}}\n\n\treturn []sessionFunction{\n\t\t{\"list databases\", \"client\", \"ListDatabases\", []any{bson.D{}}},\n\t\t{\"insert one\", \"collection\", \"InsertOne\", []any{bson.D{{\"x\", 1}}}},\n\t\t{\"insert many\", \"collection\", \"InsertMany\", []any{insertManyDocs}},\n\t\t{\"delete one\", \"collection\", \"DeleteOne\", []any{bson.D{}}},\n\t\t{\"delete many\", \"collection\", \"DeleteMany\", []any{bson.D{}}},\n\t\t{\"update one\", \"collection\", \"UpdateOne\", []any{bson.D{}, updateDoc}},\n\t\t{\"update many\", \"collection\", \"UpdateMany\", []any{bson.D{}, updateDoc}},\n\t\t{\"replace one\", \"collection\", \"ReplaceOne\", []any{bson.D{}, bson.D{}}},\n\t\t{\"aggregate\", \"collection\", \"Aggregate\", []any{mongo.Pipeline{}}},\n\t\t{\"estimated document count\", \"collection\", \"EstimatedDocumentCount\", nil},\n\t\t{\"distinct\", \"collection\", \"Distinct\", []any{\"field\", bson.D{}}},\n\t\t{\"find\", \"collection\", \"Find\", []any{bson.D{}}},\n\t\t{\"find one and delete\", \"collection\", \"FindOneAndDelete\", []any{bson.D{}}},\n\t\t{\"find one and replace\", \"collection\", \"FindOneAndReplace\", []any{bson.D{}, bson.D{}}},\n\t\t{\"find one and update\", \"collection\", \"FindOneAndUpdate\", []any{bson.D{}, updateDoc}},\n\t\t{\"drop collection\", \"collection\", \"Drop\", nil},\n\t\t{\"list collections\", \"database\", \"ListCollections\", []any{bson.D{}}},\n\t\t{\"drop database\", \"database\", \"Drop\", nil},\n\t\t{\"create one index\", \"indexView\", \"CreateOne\", []any{fooIndex}},\n\t\t{\"create many indexes\", \"indexView\", \"CreateMany\", []any{manyIndexes}},\n\t\t{\"drop one index\", \"indexView\", \"DropOne\", []any{\"barIndex\"}},\n\t\t{\"drop all indexes\", \"indexView\", \"DropAll\", nil},\n\t\t{\"list indexes\", \"indexView\", \"List\", nil},\n\t}\n}\n\nfunc assertCollectionCount(mt *mtest.T, expectedCount int64) {\n\tmt.Helper()\n\n\tcount, err := mt.Coll.CountDocuments(context.Background(), bson.D{})\n\trequire.NoError(mt, err, \"CountDocuments error\")\n\tassert.Equal(mt, expectedCount, count, \"expected CountDocuments result %v, got %v\", expectedCount, count)\n}\n\nfunc sessionIDsEqual(mt *mtest.T, id1, id2 bson.Raw) bool {\n\tfirst, err := id1.LookupErr(\"id\")\n\tassert.Nil(mt, err, \"id not found in document %v\", id1)\n\tsecond, err := id2.LookupErr(\"id\")\n\tassert.Nil(mt, err, \"id not found in document %v\", id2)\n\n\t_, firstUUID := first.Binary()\n\t_, secondUUID := second.Binary()\n\treturn bytes.Equal(firstUUID, secondUUID)\n}\n\nfunc interfaceSliceToValueSlice(args []any) []reflect.Value {\n\tvals := make([]reflect.Value, 0, len(args))\n\tfor _, arg := range args {\n\t\tvals = append(vals, reflect.ValueOf(arg))\n\t}\n\treturn vals\n}\n\nfunc extractReturnError(returnValues []reflect.Value) error {\n\terrVal := returnValues[len(returnValues)-1]\n\tswitch converted := errVal.Interface().(type) {\n\tcase error:\n\t\treturn converted\n\tcase *mongo.SingleResult:\n\t\treturn converted.Err()\n\tcase *mongo.DistinctResult:\n\t\treturn converted.Err()\n\tdefault:\n\t\treturn nil\n\t}\n}\n\nfunc extractSentSessionID(mt *mtest.T) []byte {\n\tevent := mt.GetStartedEvent()\n\tif event == nil {\n\t\treturn nil\n\t}\n\tlsid, err := event.Command.LookupErr(\"lsid\")\n\tif err != nil {\n\t\treturn nil\n\t}\n\n\t_, data := lsid.Document().Lookup(\"id\").Binary()\n\treturn data\n}\n"
  },
  {
    "path": "internal/integration/unified/admin_helpers.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage unified\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"strings\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/integration/mtest\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/integtest\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n)\n\nvar ignoredKillAllSessionsErrors = []int{\n\t11601, // Interrupted, for SERVER-38335 on server versions below 4.2\n\t13,    // Unauthorized, for SERVER-54216 on atlas\n}\n\n// terminateOpenSessions executes a killAllSessions command to ensure that sessions left open on the server by a test\n// do not cause future tests to hang.\nfunc terminateOpenSessions(ctx context.Context) error {\n\tif mtest.CompareServerVersions(mtest.ServerVersion(), \"3.6\") < 0 {\n\t\treturn nil\n\t}\n\n\tcommandFn := func(ctx context.Context, client *mongo.Client) error {\n\t\tcmd := bson.D{\n\t\t\t{\"killAllSessions\", bson.A{}},\n\t\t}\n\n\t\terr := client.Database(\"admin\").RunCommand(ctx, cmd).Err()\n\t\tif se, ok := err.(mongo.ServerError); ok {\n\t\t\tfor _, code := range ignoredKillAllSessionsErrors {\n\t\t\t\tif se.HasErrorCode(code) {\n\t\t\t\t\terr = nil\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// for SERVER-54216 on atlas\n\t\tif err != nil && strings.Contains(err.Error(), \"(AtlasError) (Unauthorized)\") {\n\t\t\terr = nil\n\t\t}\n\n\t\treturn err\n\t}\n\n\t// For sharded clusters, this has to run against all mongos nodes. Otherwise, it can just against on the primary.\n\tif mtest.ClusterTopologyKind() != mtest.Sharded {\n\t\treturn commandFn(ctx, mtest.GlobalClient())\n\t}\n\treturn runAgainstAllMongoses(ctx, commandFn)\n}\n\n// performDistinctWorkaround executes a non-transactional \"distinct\" command against each mongos in a sharded cluster.\nfunc performDistinctWorkaround(ctx context.Context) error {\n\tcommandFn := func(ctx context.Context, client *mongo.Client) error {\n\t\tfor _, coll := range entities(ctx).collections() {\n\t\t\tnewColl := client.Database(coll.Database().Name()).Collection(coll.Name())\n\t\t\terr := newColl.Distinct(ctx, \"x\", bson.D{}).Err()\n\t\t\tif err != nil {\n\t\t\t\tns := fmt.Sprintf(\"%s.%s\", coll.Database().Name(), coll.Name())\n\t\t\t\treturn fmt.Errorf(\"error running distinct for collection %q: %w\", ns, err)\n\t\t\t}\n\t\t}\n\n\t\treturn nil\n\t}\n\n\treturn runAgainstAllMongoses(ctx, commandFn)\n}\n\nfunc runCommandOnHost(ctx context.Context, host string, commandFn func(context.Context, *mongo.Client) error) error {\n\tclientOpts := options.Client().\n\t\tApplyURI(mtest.ClusterURI()).\n\t\tSetHosts([]string{host})\n\tintegtest.AddTestServerAPIVersion(clientOpts)\n\n\tclient, err := mongo.Connect(clientOpts)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"error creating client to host %q: %w\", host, err)\n\t}\n\tdefer func() { _ = client.Disconnect(ctx) }()\n\n\treturn commandFn(ctx, client)\n}\n\nfunc runAgainstAllMongoses(ctx context.Context, commandFn func(context.Context, *mongo.Client) error) error {\n\tfor _, host := range mtest.ClusterConnString().Hosts {\n\t\tif err := runCommandOnHost(ctx, host, commandFn); err != nil {\n\t\t\treturn fmt.Errorf(\"error executing callback against host %q: %w\", host, err)\n\t\t}\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "internal/integration/unified/bsonutil.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage unified\n\nimport (\n\t\"sort\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n)\n\nvar (\n\temptyCoreDocument = bsoncore.NewDocumentBuilder().Build()\n\temptyDocument     = bson.Raw(emptyCoreDocument)\n\temptyRawValue     = bson.RawValue{}\n)\n\nfunc documentToRawValue(doc bson.Raw) bson.RawValue {\n\treturn bson.RawValue{\n\t\tType:  bson.TypeEmbeddedDocument,\n\t\tValue: doc,\n\t}\n}\n\nfunc removeFieldsFromDocument(doc bson.Raw, keys ...string) bson.Raw {\n\tnewDoc := bsoncore.NewDocumentBuilder()\n\telems, _ := doc.Elements()\n\n\tkeysMap := make(map[string]struct{})\n\tfor _, key := range keys {\n\t\tkeysMap[key] = struct{}{}\n\t}\n\n\tfor _, elem := range elems {\n\t\tif _, ok := keysMap[elem.Key()]; ok {\n\t\t\tcontinue\n\t\t}\n\n\t\tval := elem.Value()\n\t\tnewDoc.AppendValue(elem.Key(), bsoncore.Value{Type: bsoncore.Type(val.Type), Data: val.Value})\n\t}\n\treturn bson.Raw(newDoc.Build())\n}\n\nfunc sortDocument(doc bson.Raw) bson.Raw {\n\telems, _ := doc.Elements()\n\tkeys := make([]string, 0, len(elems))\n\tvaluesMap := make(map[string]bson.RawValue)\n\n\tfor _, elem := range elems {\n\t\tkeys = append(keys, elem.Key())\n\t\tvaluesMap[elem.Key()] = elem.Value()\n\t}\n\n\tsort.Strings(keys)\n\tsorted := bsoncore.NewDocumentBuilder()\n\tfor _, key := range keys {\n\t\tval := valuesMap[key]\n\t\tsorted.AppendValue(key, bsoncore.Value{Type: bsoncore.Type(val.Type), Data: val.Value})\n\t}\n\treturn bson.Raw(sorted.Build())\n}\n\nfunc lookupString(doc bson.Raw, key string) string {\n\treturn doc.Lookup(key).StringValue()\n}\n\nfunc lookupInteger(doc bson.Raw, key string) int64 {\n\treturn doc.Lookup(key).AsInt64()\n}\n\nfunc mapKeys(m map[string]any) []string {\n\tkeys := make([]string, 0, len(m))\n\tfor k := range m {\n\t\tkeys = append(keys, k)\n\t}\n\treturn keys\n}\n"
  },
  {
    "path": "internal/integration/unified/bucket_options.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage unified\n\nimport (\n\t\"fmt\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n)\n\n// gridFSBucketOptions is a wrapper for *options.BucketOptionsBuilder. This type\n// implements the bson.Unmarshaler interface to convert BSON documents to a\n// BucketOptionsBuilder instance.\ntype gridFSBucketOptions struct {\n\t*options.BucketOptionsBuilder\n}\n\nvar _ bson.Unmarshaler = (*gridFSBucketOptions)(nil)\n\nfunc (bo *gridFSBucketOptions) UnmarshalBSON(data []byte) error {\n\tvar temp struct {\n\t\tName      *string         `bson:\"name\"`\n\t\tChunkSize *int32          `bson:\"chunkSizeBytes\"`\n\t\tRC        *readConcern    `bson:\"readConcern\"`\n\t\tRP        *ReadPreference `bson:\"readPreference\"`\n\t\tWC        *writeConcern   `bson:\"writeConcern\"`\n\t\tExtra     map[string]any  `bson:\",inline\"`\n\t}\n\tif err := bson.Unmarshal(data, &temp); err != nil {\n\t\treturn fmt.Errorf(\"error unmarshalling to temporary gridFSBucketOptions object: %w\", err)\n\t}\n\tif len(temp.Extra) > 0 {\n\t\treturn fmt.Errorf(\"unrecognized fields for gridFSBucketOptions: %v\", mapKeys(temp.Extra))\n\t}\n\n\tbo.BucketOptionsBuilder = options.GridFSBucket()\n\tif temp.Name != nil {\n\t\tbo.SetName(*temp.Name)\n\t}\n\tif temp.ChunkSize != nil {\n\t\tbo.SetChunkSizeBytes(*temp.ChunkSize)\n\t}\n\tif temp.RC != nil {\n\t\tbo.SetReadConcern(temp.RC.toReadConcernOption())\n\t}\n\tif temp.RP != nil {\n\t\trp, err := temp.RP.ToReadPrefOption()\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"error parsing read preference document: %w\", err)\n\t\t}\n\t\tbo.SetReadPreference(rp)\n\t}\n\tif temp.WC != nil {\n\t\twc, err := temp.WC.toWriteConcernOption()\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"error parsing write concern document: %w\", err)\n\t\t}\n\t\tbo.SetWriteConcern(wc)\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "internal/integration/unified/bulkwrite_helpers.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage unified\n\nimport (\n\t\"fmt\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/bsonutil\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo\"\n)\n\n// This file provides helper functions to convert BSON documents to WriteModel\n// instances.\n\n// createBulkWriteModels converts an bson raw array to a slice of WriteModel.\n// Each value in the array must be a document in the form\n//\n//\t{ requestType: { optionKey1: optionValue1, ... } }.\n//\n// For example, the document\n//\n//\t{ insertOne: { document: { x: 1 } } }\n//\n// would be translated to an InsertOneModel to insert the document { x: 1 }.\nfunc createBulkWriteModels(rawModels bson.RawArray) ([]mongo.WriteModel, error) {\n\tvals, _ := rawModels.Values()\n\tmodels := make([]mongo.WriteModel, 0, len(vals))\n\n\tfor idx, val := range vals {\n\t\tmodel, err := createBulkWriteModel(val.Document())\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"error creating model at index %d: %w\", idx, err)\n\t\t}\n\t\tmodels = append(models, model)\n\t}\n\treturn models, nil\n}\n\n// createBulkWriteModel converts the provided BSON document to a WriteModel.\nfunc createBulkWriteModel(rawModel bson.Raw) (mongo.WriteModel, error) {\n\tfirstElem := rawModel.Index(0)\n\trequestType := firstElem.Key()\n\targs := firstElem.Value().Document()\n\n\tswitch requestType {\n\tcase \"insertOne\":\n\t\tvar document bson.Raw\n\t\telems, _ := args.Elements()\n\t\tfor _, elem := range elems {\n\t\t\tkey := elem.Key()\n\t\t\tval := elem.Value()\n\n\t\t\tswitch key {\n\t\t\tcase \"document\":\n\t\t\t\tdocument = val.Document()\n\t\t\tdefault:\n\t\t\t\treturn nil, fmt.Errorf(\"unrecognized insertOne option %q\", key)\n\t\t\t}\n\t\t}\n\t\tif document == nil {\n\t\t\treturn nil, newMissingArgumentError(\"document\")\n\t\t}\n\n\t\treturn mongo.NewInsertOneModel().SetDocument(document), nil\n\tcase \"updateOne\":\n\t\tuom := mongo.NewUpdateOneModel()\n\t\tvar filter bson.Raw\n\t\tvar update any\n\t\tvar err error\n\n\t\telems, _ := args.Elements()\n\t\tfor _, elem := range elems {\n\t\t\tkey := elem.Key()\n\t\t\tval := elem.Value()\n\n\t\t\tswitch key {\n\t\t\tcase \"arrayFilters\":\n\t\t\t\tuom.SetArrayFilters(\n\t\t\t\t\tbsonutil.RawToInterfaces(bsonutil.RawArrayToDocuments(val.Array())...),\n\t\t\t\t)\n\t\t\tcase \"collation\":\n\t\t\t\tcollation, err := createCollation(val.Document())\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, fmt.Errorf(\"error creating collation: %w\", err)\n\t\t\t\t}\n\t\t\t\tuom.SetCollation(collation)\n\t\t\tcase \"filter\":\n\t\t\t\tfilter = val.Document()\n\t\t\tcase \"hint\":\n\t\t\t\thint, err := createHint(val)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, fmt.Errorf(\"error creating hint: %w\", err)\n\t\t\t\t}\n\t\t\t\tuom.SetHint(hint)\n\t\t\tcase \"update\":\n\t\t\t\tupdate, err = createUpdateValue(val)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, fmt.Errorf(\"error creating update: %w\", err)\n\t\t\t\t}\n\t\t\tcase \"sort\":\n\t\t\t\tuom.SetSort(val.Document())\n\t\t\tcase \"upsert\":\n\t\t\t\tuom.SetUpsert(val.Boolean())\n\t\t\tdefault:\n\t\t\t\treturn nil, fmt.Errorf(\"unrecognized updateOne option %q\", key)\n\t\t\t}\n\t\t}\n\t\tif filter == nil {\n\t\t\treturn nil, newMissingArgumentError(\"filter\")\n\t\t}\n\t\tif update == nil {\n\t\t\treturn nil, newMissingArgumentError(\"update\")\n\t\t}\n\n\t\treturn uom.SetFilter(filter).SetUpdate(update), nil\n\tcase \"updateMany\":\n\t\tumm := mongo.NewUpdateManyModel()\n\t\tvar filter bson.Raw\n\t\tvar update any\n\t\tvar err error\n\n\t\telems, _ := args.Elements()\n\t\tfor _, elem := range elems {\n\t\t\tkey := elem.Key()\n\t\t\tval := elem.Value()\n\n\t\t\tswitch key {\n\t\t\tcase \"arrayFilters\":\n\t\t\t\tumm.SetArrayFilters(\n\t\t\t\t\tbsonutil.RawToInterfaces(bsonutil.RawArrayToDocuments(val.Array())...),\n\t\t\t\t)\n\t\t\tcase \"collation\":\n\t\t\t\tcollation, err := createCollation(val.Document())\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, fmt.Errorf(\"error creating collation: %w\", err)\n\t\t\t\t}\n\t\t\t\tumm.SetCollation(collation)\n\t\t\tcase \"filter\":\n\t\t\t\tfilter = val.Document()\n\t\t\tcase \"hint\":\n\t\t\t\thint, err := createHint(val)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, fmt.Errorf(\"error creating hint: %w\", err)\n\t\t\t\t}\n\t\t\t\tumm.SetHint(hint)\n\t\t\tcase \"update\":\n\t\t\t\tupdate, err = createUpdateValue(val)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, fmt.Errorf(\"error creating update: %w\", err)\n\t\t\t\t}\n\t\t\tcase \"upsert\":\n\t\t\t\tumm.SetUpsert(val.Boolean())\n\t\t\tdefault:\n\t\t\t\treturn nil, fmt.Errorf(\"unrecognized updateMany option %q\", key)\n\t\t\t}\n\t\t}\n\t\tif filter == nil {\n\t\t\treturn nil, newMissingArgumentError(\"filter\")\n\t\t}\n\t\tif update == nil {\n\t\t\treturn nil, newMissingArgumentError(\"update\")\n\t\t}\n\n\t\treturn umm.SetFilter(filter).SetUpdate(update), nil\n\tcase \"deleteOne\":\n\t\tdom := mongo.NewDeleteOneModel()\n\t\tvar filter bson.Raw\n\n\t\telems, _ := args.Elements()\n\t\tfor _, elem := range elems {\n\t\t\tkey := elem.Key()\n\t\t\tval := elem.Value()\n\n\t\t\tswitch key {\n\t\t\tcase \"filter\":\n\t\t\t\tfilter = val.Document()\n\t\t\tcase \"hint\":\n\t\t\t\thint, err := createHint(val)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, fmt.Errorf(\"error creating hint: %w\", err)\n\t\t\t\t}\n\t\t\t\tdom.SetHint(hint)\n\t\t\tdefault:\n\t\t\t\treturn nil, fmt.Errorf(\"unrecognized deleteOne option %q\", key)\n\t\t\t}\n\t\t}\n\t\tif filter == nil {\n\t\t\treturn nil, newMissingArgumentError(\"filter\")\n\t\t}\n\n\t\treturn dom.SetFilter(filter), nil\n\tcase \"deleteMany\":\n\t\tdmm := mongo.NewDeleteManyModel()\n\t\tvar filter bson.Raw\n\n\t\telems, _ := args.Elements()\n\t\tfor _, elem := range elems {\n\t\t\tkey := elem.Key()\n\t\t\tval := elem.Value()\n\n\t\t\tswitch key {\n\t\t\tcase \"collation\":\n\t\t\t\tcollation, err := createCollation(val.Document())\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, fmt.Errorf(\"error creating collation: %w\", err)\n\t\t\t\t}\n\t\t\t\tdmm.SetCollation(collation)\n\t\t\tcase \"filter\":\n\t\t\t\tfilter = val.Document()\n\t\t\tcase \"hint\":\n\t\t\t\thint, err := createHint(val)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, fmt.Errorf(\"error creating hint: %w\", err)\n\t\t\t\t}\n\t\t\t\tdmm.SetHint(hint)\n\t\t\tdefault:\n\t\t\t\treturn nil, fmt.Errorf(\"unrecognized deleteMany option %q\", key)\n\t\t\t}\n\t\t}\n\t\tif filter == nil {\n\t\t\treturn nil, newMissingArgumentError(\"filter\")\n\t\t}\n\n\t\treturn dmm.SetFilter(filter), nil\n\tcase \"replaceOne\":\n\t\trom := mongo.NewReplaceOneModel()\n\t\tvar filter, replacement bson.Raw\n\n\t\telems, _ := args.Elements()\n\t\tfor _, elem := range elems {\n\t\t\tkey := elem.Key()\n\t\t\tval := elem.Value()\n\n\t\t\tswitch key {\n\t\t\tcase \"collation\":\n\t\t\t\tcollation, err := createCollation(val.Document())\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, fmt.Errorf(\"error creating collation: %w\", err)\n\t\t\t\t}\n\t\t\t\trom.SetCollation(collation)\n\t\t\tcase \"filter\":\n\t\t\t\tfilter = val.Document()\n\t\t\tcase \"hint\":\n\t\t\t\thint, err := createHint(val)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, fmt.Errorf(\"error creating hint: %w\", err)\n\t\t\t\t}\n\t\t\t\trom.SetHint(hint)\n\t\t\tcase \"sort\":\n\t\t\t\trom.SetSort(val.Document())\n\t\t\tcase \"replacement\":\n\t\t\t\treplacement = val.Document()\n\t\t\tcase \"upsert\":\n\t\t\t\trom.SetUpsert(val.Boolean())\n\t\t\tdefault:\n\t\t\t\treturn nil, fmt.Errorf(\"unrecognized replaceOne option %q\", key)\n\t\t\t}\n\t\t}\n\t\tif filter == nil {\n\t\t\treturn nil, newMissingArgumentError(\"filter\")\n\t\t}\n\t\tif replacement == nil {\n\t\t\treturn nil, newMissingArgumentError(\"replacement\")\n\t\t}\n\n\t\treturn rom.SetFilter(filter).SetReplacement(replacement), nil\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"unrecognized request type: %v\", requestType)\n\t}\n}\n\n// createUpdateValue converts the provided RawValue to a value that can be passed to UpdateOne/UpdateMany functions.\n// This helper handles both document and pipeline-style updates.\nfunc createUpdateValue(updateVal bson.RawValue) (any, error) {\n\tswitch updateVal.Type {\n\tcase bson.TypeEmbeddedDocument:\n\t\treturn updateVal.Document(), nil\n\tcase bson.TypeArray:\n\t\tvar updateDocs []bson.Raw\n\t\tdocs, _ := updateVal.Array().Values()\n\t\tfor _, doc := range docs {\n\t\t\tupdateDocs = append(updateDocs, doc.Document())\n\t\t}\n\n\t\treturn updateDocs, nil\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"unrecognized update type: %s\", updateVal.Type)\n\t}\n}\n"
  },
  {
    "path": "internal/integration/unified/client_encryption_operation_execution.go",
    "content": "// Copyright (C) MongoDB, Inc. 2022-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage unified\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n)\n\n// parseDataKeyOptions will parse an options document and return an options.DataKeyOptions instance.\nfunc parseDataKeyOptions(opts bson.Raw) (*options.DataKeyOptionsBuilder, error) {\n\telems, err := opts.Elements()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdko := options.DataKey()\n\tfor _, elem := range elems {\n\t\tkey := elem.Key()\n\t\tval := elem.Value()\n\t\tswitch key {\n\t\tcase \"masterKey\":\n\t\t\tmasterKey := make(map[string]any)\n\t\t\tif err := val.Unmarshal(&masterKey); err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"error unmarshaling 'masterKey': %w\", err)\n\t\t\t}\n\t\t\tdko.SetMasterKey(masterKey)\n\t\tcase \"keyAltNames\":\n\t\t\tkeyAltNames := []string{}\n\t\t\tif err := val.Unmarshal(&keyAltNames); err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"error unmarshaling 'keyAltNames': %w\", err)\n\t\t\t}\n\t\t\tdko.SetKeyAltNames(keyAltNames)\n\t\tcase \"keyMaterial\":\n\t\t\tbin := bson.Binary{}\n\t\t\tif err := val.Unmarshal(&bin); err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"error unmarshaling 'keyMaterial': %w\", err)\n\t\t\t}\n\t\t\tdko.SetKeyMaterial(bin.Data)\n\t\tdefault:\n\t\t\treturn nil, fmt.Errorf(\"unrecognized DataKeyOptions arg: %q\", key)\n\t\t}\n\t}\n\treturn dko, nil\n}\n\n// executeAddKeyAltName adds a keyAltName to the keyAltNames array of the key document in the key vault collection with\n// the given UUID (BSON binary subtype 0x04). Returns the previous version of the key document.\nfunc executeAddKeyAltName(ctx context.Context, operation *operation) (*operationResult, error) {\n\tcee, err := entities(ctx).clientEncryption(operation.Object)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar id bson.Binary\n\tvar keyAltName string\n\n\telems, err := operation.Arguments.Elements()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tfor _, elem := range elems {\n\t\tkey := elem.Key()\n\t\tval := elem.Value()\n\n\t\tswitch key {\n\t\tcase \"id\":\n\t\t\tsubtype, data := val.Binary()\n\t\t\tid = bson.Binary{Subtype: subtype, Data: data}\n\t\tcase \"keyAltName\":\n\t\t\tkeyAltName = val.StringValue()\n\t\tdefault:\n\t\t\treturn nil, fmt.Errorf(\"unrecognized AddKeyAltName arg: %q\", key)\n\t\t}\n\t}\n\n\tres, err := cee.AddKeyAltName(ctx, id, keyAltName).Raw()\n\t// Ignore ErrNoDocuments errors from Raw. In the event that the cursor returned in a find operation has no\n\t// associated documents, Raw will return ErrNoDocuments.\n\tif errors.Is(err, mongo.ErrNoDocuments) {\n\t\terr = nil\n\t}\n\treturn newDocumentResult(res, err), nil\n}\n\n// executeCreateDataKey will attempt to create a client-encrypted key for a unified operation.\nfunc executeCreateDataKey(ctx context.Context, operation *operation) (*operationResult, error) {\n\tcee, err := entities(ctx).clientEncryption(operation.Object)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar kmsProvider string\n\tvar dko *options.DataKeyOptionsBuilder\n\n\telems, err := operation.Arguments.Elements()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tfor _, elem := range elems {\n\t\tkey := elem.Key()\n\t\tval := elem.Value()\n\n\t\tswitch key {\n\t\tcase \"kmsProvider\":\n\t\t\tkmsProvider = val.StringValue()\n\t\tcase \"opts\":\n\t\t\tdko, err = parseDataKeyOptions(val.Document())\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\tdefault:\n\t\t\treturn nil, fmt.Errorf(\"unrecognized CreateDataKey arg: %q\", key)\n\t\t}\n\t}\n\tif kmsProvider == \"\" {\n\t\treturn nil, newMissingArgumentError(\"kmsProvider\")\n\t}\n\n\tbin, err := cee.CreateDataKey(ctx, kmsProvider, dko)\n\tif bin.Data != nil {\n\t\tbsonType, bsonData, err := bson.MarshalValue(bin)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\treturn newValueResult(bsonType, bsonData, err), nil\n\t}\n\treturn newErrorResult(err), nil\n}\n\n// executeDeleteKey removes the key document with the given UUID (BSON binary subtype 0x04) from the key vault\n// collection. Returns the result of the internal deleteOne() operation on the key vault collection.\nfunc executeDeleteKey(ctx context.Context, operation *operation) (*operationResult, error) {\n\tcee, err := entities(ctx).clientEncryption(operation.Object)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar id bson.Binary\n\n\telems, err := operation.Arguments.Elements()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tfor _, elem := range elems {\n\t\tkey := elem.Key()\n\t\tval := elem.Value()\n\n\t\tswitch key {\n\t\tcase \"id\":\n\t\t\tsubtype, data := val.Binary()\n\t\t\tid = bson.Binary{Subtype: subtype, Data: data}\n\t\tdefault:\n\t\t\treturn nil, fmt.Errorf(\"unrecognized DeleteKey arg: %q\", key)\n\t\t}\n\t}\n\n\tres, err := cee.DeleteKey(ctx, id)\n\traw := emptyCoreDocument\n\tif res != nil {\n\t\traw = bsoncore.NewDocumentBuilder().\n\t\t\tAppendInt64(\"deletedCount\", res.DeletedCount).\n\t\t\tBuild()\n\t}\n\treturn newDocumentResult(raw, err), nil\n}\n\n// executeGetKeyByAltName returns a key document in the key vault collection with the given keyAltName.\nfunc executeGetKeyByAltName(ctx context.Context, operation *operation) (*operationResult, error) {\n\tcee, err := entities(ctx).clientEncryption(operation.Object)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar keyAltName string\n\n\telems, err := operation.Arguments.Elements()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tfor _, elem := range elems {\n\t\tkey := elem.Key()\n\t\tval := elem.Value()\n\n\t\tswitch key {\n\t\tcase \"keyAltName\":\n\t\t\tkeyAltName = val.StringValue()\n\t\tdefault:\n\t\t\treturn nil, fmt.Errorf(\"unrecognized GetKeyByAltName arg: %q\", key)\n\t\t}\n\t}\n\n\tres, err := cee.GetKeyByAltName(ctx, keyAltName).Raw()\n\t// Ignore ErrNoDocuments errors from Raw. In the event that the cursor returned in a find operation has no\n\t// associated documents, Raw will return ErrNoDocuments.\n\tif errors.Is(err, mongo.ErrNoDocuments) {\n\t\terr = nil\n\t}\n\treturn newDocumentResult(res, err), nil\n}\n\n// executeGetKey finds a single key document with the given UUID (BSON binary subtype 0x04). Returns the result of the\n// internal find() operation on the key vault collection.\nfunc executeGetKey(ctx context.Context, operation *operation) (*operationResult, error) {\n\tcee, err := entities(ctx).clientEncryption(operation.Object)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar id bson.Binary\n\n\telems, err := operation.Arguments.Elements()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tfor _, elem := range elems {\n\t\tkey := elem.Key()\n\t\tval := elem.Value()\n\n\t\tswitch key {\n\t\tcase \"id\":\n\t\t\tsubtype, data := val.Binary()\n\t\t\tid = bson.Binary{Subtype: subtype, Data: data}\n\t\tdefault:\n\t\t\treturn nil, fmt.Errorf(\"unrecognized GetKey arg: %q\", key)\n\t\t}\n\t}\n\n\tres, err := cee.GetKey(ctx, id).Raw()\n\t// Ignore ErrNoDocuments errors from Raw. In the event that the cursor returned in a find operation has no\n\t// associated documents, Raw will return ErrNoDocuments.\n\tif errors.Is(err, mongo.ErrNoDocuments) {\n\t\terr = nil\n\t}\n\treturn newDocumentResult(res, err), nil\n}\n\n// executeGetKeys finds all documents in the key vault collection. Returns the result of the internal find() operation\n// on the key vault collection.\nfunc executeGetKeys(ctx context.Context, operation *operation) (*operationResult, error) {\n\tcee, err := entities(ctx).clientEncryption(operation.Object)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tcursor, err := cee.GetKeys(ctx)\n\tif err != nil {\n\t\treturn newErrorResult(err), nil\n\t}\n\tvar docs []bson.Raw\n\tif err := cursor.All(ctx, &docs); err != nil {\n\t\treturn newErrorResult(err), nil\n\t}\n\treturn newCursorResult(docs), nil\n}\n\n// executeRemoveKeyAltName will remove keyAltName from the data key if it is present on the data key.\nfunc executeRemoveKeyAltName(ctx context.Context, operation *operation) (*operationResult, error) {\n\tcee, err := entities(ctx).clientEncryption(operation.Object)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar id bson.Binary\n\tvar keyAltName string\n\n\telems, err := operation.Arguments.Elements()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tfor _, elem := range elems {\n\t\tkey := elem.Key()\n\t\tval := elem.Value()\n\n\t\tswitch key {\n\t\tcase \"id\":\n\t\t\tsubtype, data := val.Binary()\n\t\t\tid = bson.Binary{Subtype: subtype, Data: data}\n\t\tcase \"keyAltName\":\n\t\t\tkeyAltName = val.StringValue()\n\t\tdefault:\n\t\t\treturn nil, fmt.Errorf(\"unrecognized RemoveKeyAltName arg: %q\", key)\n\t\t}\n\t}\n\n\tres, err := cee.RemoveKeyAltName(ctx, id, keyAltName).Raw()\n\t// Ignore ErrNoDocuments errors from Raw. In the event that the cursor returned in a find operation has no\n\t// associated documents, Raw will return ErrNoDocuments.\n\tif errors.Is(err, mongo.ErrNoDocuments) {\n\t\terr = nil\n\t}\n\treturn newDocumentResult(res, err), nil\n}\n\n// parseRewrapManyDataKeyOptions will parse an options document and return an\n// options.RewrapManyDataKeyOptions instance.\nfunc parseRewrapManyDataKeyOptions(opts bson.Raw) (*options.RewrapManyDataKeyOptionsBuilder, error) {\n\telems, err := opts.Elements()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\trmdko := options.RewrapManyDataKey()\n\tfor _, elem := range elems {\n\t\tkey := elem.Key()\n\t\tval := elem.Value()\n\t\tswitch key {\n\t\tcase \"provider\":\n\t\t\trmdko.SetProvider(val.StringValue())\n\t\tcase \"masterKey\":\n\t\t\trmdko.SetMasterKey(val.Document())\n\t\tdefault:\n\t\t\treturn nil, fmt.Errorf(\"unrecognized RewrapManyDataKeyOptions arg: %q\", key)\n\t\t}\n\t}\n\treturn rmdko, nil\n}\n\n// rewrapManyDataKeyResultsOpResult will wrap the result of rewrapping a data key into an operation result for test\n// validation.\nfunc rewrapManyDataKeyResultsOpResult(result *mongo.RewrapManyDataKeyResult) (*operationResult, error) {\n\traw := bsoncore.NewDocumentBuilder()\n\tif res := result.BulkWriteResult; res != nil {\n\t\trawUpsertedIDs := emptyDocument\n\t\tvar marshalErr error\n\t\tif res.UpsertedIDs != nil {\n\t\t\trawUpsertedIDs, marshalErr = bson.Marshal(res.UpsertedIDs)\n\t\t\tif marshalErr != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"error marshalling UpsertedIDs map to BSON: %w\", marshalErr)\n\t\t\t}\n\t\t}\n\t\tbulkWriteResult := bsoncore.NewDocumentBuilder()\n\t\tbulkWriteResult.\n\t\t\tAppendInt64(\"insertedCount\", res.InsertedCount).\n\t\t\tAppendInt64(\"deletedCount\", res.DeletedCount).\n\t\t\tAppendInt64(\"matchedCount\", res.MatchedCount).\n\t\t\tAppendInt64(\"modifiedCount\", res.ModifiedCount).\n\t\t\tAppendInt64(\"upsertedCount\", res.UpsertedCount).\n\t\t\tAppendDocument(\"upsertedIds\", rawUpsertedIDs)\n\t\traw.AppendDocument(\"bulkWriteResult\", bulkWriteResult.Build())\n\t}\n\treturn newDocumentResult(raw.Build(), nil), nil\n}\n\n// executeRewrapManyDataKey will attempt to re-wrap a number of data keys given a new provider, master key, and filter.\nfunc executeRewrapManyDataKey(ctx context.Context, operation *operation) (*operationResult, error) {\n\tcee, err := entities(ctx).clientEncryption(operation.Object)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar filter bson.Raw\n\tvar rmdko *options.RewrapManyDataKeyOptionsBuilder\n\n\telems, err := operation.Arguments.Elements()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tfor _, elem := range elems {\n\t\tkey := elem.Key()\n\t\tval := elem.Value()\n\n\t\tswitch key {\n\t\tcase \"filter\":\n\t\t\tfilter = val.Document()\n\t\tcase \"opts\":\n\t\t\trmdko, err = parseRewrapManyDataKeyOptions(val.Document())\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\tdefault:\n\t\t\treturn nil, fmt.Errorf(\"unrecognized RewrapManyDataKey arg: %q\", key)\n\t\t}\n\t}\n\n\tif filter == nil {\n\t\treturn nil, newMissingArgumentError(\"filter\")\n\t}\n\n\tresult, err := cee.RewrapManyDataKey(ctx, filter, rmdko)\n\tif err != nil {\n\t\treturn newErrorResult(err), nil\n\t}\n\treturn rewrapManyDataKeyResultsOpResult(result)\n}\n\n// executeDecrypt will decrypt the given value.\nfunc executeDecrypt(ctx context.Context, operation *operation) (*operationResult, error) {\n\tcee, err := entities(ctx).clientEncryption(operation.Object)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\trawValue, err := operation.Arguments.LookupErr(\"value\")\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tt, d, ok := rawValue.BinaryOK()\n\tif !ok {\n\t\treturn nil, errors.New(\"'value' argument is not a BSON binary\")\n\t}\n\n\trawValue, err = cee.Decrypt(ctx, bson.Binary{Subtype: t, Data: d})\n\tif err != nil {\n\t\treturn newErrorResult(err), nil\n\t}\n\treturn newValueResult(rawValue.Type, rawValue.Value, err), nil\n}\n"
  },
  {
    "path": "internal/integration/unified/client_entity.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage unified\n\nimport (\n\t\"context\"\n\t\"crypto/tls\"\n\t\"fmt\"\n\t\"strings\"\n\t\"sync\"\n\t\"sync/atomic\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/event\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/handshake\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/integration/mtest\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/integtest\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/logger\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/readconcern\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/description\"\n)\n\n// There are no automated tests for truncation. Given that, setting the\n// \"MaxDocumentLength\" to 10_000 will ensure that the default truncation\n// length does not interfere with tests with commands/replies that\n// exceed the default truncation length.\nconst defaultMaxDocumentLen = 10_000\n\n// Security-sensitive commands that should be ignored in command monitoring by default.\nvar securitySensitiveCommands = []string{\n\t\"authenticate\", \"saslStart\", \"saslContinue\", \"getnonce\",\n\t\"createUser\", \"updateUser\", \"copydbgetnonce\", \"copydbsaslstart\", \"copydb\",\n}\n\n// clientEntity is a wrapper for a mongo.Client object that also holds additional information required during test\n// execution.\ntype clientEntity struct {\n\t*mongo.Client\n\tdisconnected bool\n\n\trecordEvents                atomic.Value\n\tstarted                     []*event.CommandStartedEvent\n\tsucceeded                   []*event.CommandSucceededEvent\n\tfailed                      []*event.CommandFailedEvent\n\tpooled                      []*event.PoolEvent\n\tserverDescriptionChanged    []*event.ServerDescriptionChangedEvent\n\tserverHeartbeatFailedEvent  []*event.ServerHeartbeatFailedEvent\n\tserverHeartbeatStartedEvent []*event.ServerHeartbeatStartedEvent\n\tserverHeartbeatSucceeded    []*event.ServerHeartbeatSucceededEvent\n\ttopologyDescriptionChanged  []*event.TopologyDescriptionChangedEvent\n\ttopologyOpening             []*event.TopologyOpeningEvent\n\ttopologyClosed              []*event.TopologyClosedEvent\n\tignoredCommands             map[string]struct{}\n\tobserveSensitiveCommands    *bool\n\tnumConnsCheckedOut          int32\n\tlatestDesc                  event.TopologyDescription\n\tconnsPerServer              map[string]int\n\n\t// These should not be changed after the clientEntity is initialized\n\tobservedEvents                      map[monitoringEventType]struct{}\n\tstoredEvents                        map[monitoringEventType][]string\n\teventsCount                         map[monitoringEventType]int32\n\tserverDescriptionChangedEventsCount map[serverDescriptionChangedEventInfo]int32\n\n\teventsCountLock                         sync.RWMutex\n\tserverDescriptionChangedEventsCountLock sync.RWMutex\n\teventProcessMu                          sync.RWMutex\n\n\tentityMap *EntityMap\n\n\tlogQueue chan orderedLogMessage\n}\n\nfunc newClientEntity(ctx context.Context, em *EntityMap, entityOptions *entityOptions) (*clientEntity, error) {\n\t// The \"configureFailPoint\" command should always be ignored.\n\tignoredCommands := map[string]struct{}{\n\t\t\"configureFailPoint\": {},\n\t}\n\t// If not observing sensitive commands, add security-sensitive commands\n\t// to ignoredCommands by default.\n\tif entityOptions.ObserveSensitiveCommands == nil || !*entityOptions.ObserveSensitiveCommands {\n\t\tfor _, cmd := range securitySensitiveCommands {\n\t\t\tignoredCommands[cmd] = struct{}{}\n\t\t}\n\t}\n\tentity := &clientEntity{\n\t\tignoredCommands:                     ignoredCommands,\n\t\tobservedEvents:                      make(map[monitoringEventType]struct{}),\n\t\tstoredEvents:                        make(map[monitoringEventType][]string),\n\t\teventsCount:                         make(map[monitoringEventType]int32),\n\t\tserverDescriptionChangedEventsCount: make(map[serverDescriptionChangedEventInfo]int32),\n\t\tentityMap:                           em,\n\t\tobserveSensitiveCommands:            entityOptions.ObserveSensitiveCommands,\n\t\tconnsPerServer:                      make(map[string]int),\n\t}\n\tentity.setRecordEvents(true)\n\n\t// Construct a ClientOptions instance by first applying the cluster URI and then the URIOptions map to ensure that\n\t// the options specified in the test file take precedence.\n\turi := getURIForClient(entityOptions)\n\tclientOpts := options.Client().ApplyURI(uri)\n\tif entityOptions.URIOptions != nil {\n\t\tif err := setClientOptionsFromURIOptions(clientOpts, entityOptions.URIOptions); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"error parsing URI options: %w\", err)\n\t\t}\n\t}\n\n\tif olm := entityOptions.ObserveLogMessages; olm != nil {\n\t\texpectedLogMessagesCount := expectedLogMessagesCount(ctx, entityOptions.ID)\n\t\tignoreLogMessages := ignoreLogMessages(ctx, entityOptions.ID)\n\n\t\tclientLogger := newLogger(olm, expectedLogMessagesCount, ignoreLogMessages)\n\n\t\twrap := func(str string) options.LogLevel {\n\t\t\treturn options.LogLevel(logger.ParseLevel(str))\n\t\t}\n\n\t\t// Assign the log queue to the entity so that it can be used to\n\t\t// retrieve log messages.\n\t\tentity.logQueue = clientLogger.logQueue\n\n\t\t// Update the client options to add the clientLogger.\n\t\tloggerOptions := options.Logger().\n\t\t\tSetComponentLevel(options.LogComponentCommand, wrap(olm.Command)).\n\t\t\tSetComponentLevel(options.LogComponentTopology, wrap(olm.Topology)).\n\t\t\tSetComponentLevel(options.LogComponentServerSelection, wrap(olm.ServerSelection)).\n\t\t\tSetComponentLevel(options.LogComponentConnection, wrap(olm.Connection)).\n\t\t\tSetMaxDocumentLength(defaultMaxDocumentLen).\n\t\t\tSetSink(clientLogger)\n\n\t\tclientOpts.SetLoggerOptions(loggerOptions)\n\t}\n\n\t// UseMultipleMongoses requires validation when connecting to a sharded cluster. Options changes and validation are\n\t// only required if the option is explicitly set. If it's unset, we make no changes because the cluster URI already\n\t// includes all nodes and we don't enforce any limits on the number of nodes.\n\tif mtest.ClusterTopologyKind() == mtest.Sharded && entityOptions.UseMultipleMongoses != nil {\n\t\tif err := evaluateUseMultipleMongoses(clientOpts, *entityOptions.UseMultipleMongoses); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\tif entityOptions.ObserveEvents != nil || entityOptions.StoreEventsAsEntities != nil {\n\t\t// Configure a command monitor that listens for the specified event types. We don't take the IgnoredCommands\n\t\t// option into account here because it can be overridden at the test level after the entity has already been\n\t\t// created, so we store the events for now but account for it when iterating over them later.\n\t\tcommandMonitor := &event.CommandMonitor{\n\t\t\tStarted:   entity.processStartedEvent,\n\t\t\tSucceeded: entity.processSucceededEvent,\n\t\t\tFailed:    entity.processFailedEvent,\n\t\t}\n\n\t\tpoolMonitor := &event.PoolMonitor{\n\t\t\tEvent: entity.processPoolEvent,\n\t\t}\n\n\t\tserverMonitor := &event.ServerMonitor{\n\t\t\tServerDescriptionChanged:   entity.processServerDescriptionChangedEvent,\n\t\t\tServerHeartbeatFailed:      entity.processServerHeartbeatFailedEvent,\n\t\t\tServerHeartbeatStarted:     entity.processServerHeartbeatStartedEvent,\n\t\t\tServerHeartbeatSucceeded:   entity.processServerHeartbeatSucceededEvent,\n\t\t\tTopologyDescriptionChanged: entity.processTopologyDescriptionChangedEvent,\n\t\t\tTopologyOpening:            entity.processTopologyOpeningEvent,\n\t\t\tTopologyClosed:             entity.processTopologyClosedEvent,\n\t\t}\n\n\t\tclientOpts.SetMonitor(commandMonitor).SetPoolMonitor(poolMonitor).SetServerMonitor(serverMonitor)\n\n\t\tfor _, eventTypeStr := range entityOptions.ObserveEvents {\n\t\t\teventType, ok := monitoringEventTypeFromString(eventTypeStr)\n\t\t\tif !ok {\n\t\t\t\treturn nil, fmt.Errorf(\"unrecognized observed event type %q\", eventTypeStr)\n\t\t\t}\n\t\t\tentity.observedEvents[eventType] = struct{}{}\n\t\t}\n\t\tfor _, eventsAsEntity := range entityOptions.StoreEventsAsEntities {\n\t\t\tfor _, eventTypeStr := range eventsAsEntity.Events {\n\t\t\t\teventType, ok := monitoringEventTypeFromString(eventTypeStr)\n\t\t\t\tif !ok {\n\t\t\t\t\treturn nil, fmt.Errorf(\"unrecognized stored event type %q\", eventTypeStr)\n\t\t\t\t}\n\t\t\t\tentity.storedEvents[eventType] = append(entity.storedEvents[eventType], eventsAsEntity.EventListID)\n\t\t\t}\n\t\t}\n\t}\n\tif entityOptions.ServerAPIOptions != nil {\n\t\tif err := entityOptions.ServerAPIOptions.ServerAPIVersion.Validate(); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tclientOpts.SetServerAPIOptions(entityOptions.ServerAPIOptions.ServerAPIOptions)\n\t} else {\n\t\tintegtest.AddTestServerAPIVersion(clientOpts)\n\t}\n\tif entityOptions.AutoEncryptOpts != nil {\n\t\taeo, err := createAutoEncryptionOptions(entityOptions.AutoEncryptOpts)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"error parsing auto encryption options: %w\", err)\n\t\t}\n\t\tclientOpts.SetAutoEncryptionOptions(aeo)\n\t}\n\tfor _, cmd := range entityOptions.IgnoredCommands {\n\t\tentity.ignoredCommands[cmd] = struct{}{}\n\t}\n\n\tclient, err := mongo.Connect(clientOpts)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"error creating mongo.Client: %w\", err)\n\t}\n\n\tif entityOptions.AwaitMinPoolSizeMS != nil && *entityOptions.AwaitMinPoolSizeMS > 0 &&\n\t\tclientOpts.MinPoolSize != nil && *clientOpts.MinPoolSize > 0 {\n\n\t\tif err := func() error {\n\t\t\tawaitDur := time.Duration(*entityOptions.AwaitMinPoolSizeMS) * time.Millisecond\n\n\t\t\tawaitCtx, cancel := context.WithTimeout(ctx, awaitDur)\n\t\t\tdefer cancel()\n\n\t\t\treturn awaitMinimumPoolSize(awaitCtx, entity, *clientOpts.MinPoolSize)\n\t\t}(); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\tentity.Client = client\n\treturn entity, nil\n}\n\nfunc getURIForClient(opts *entityOptions) string {\n\tif mtest.Serverless() || mtest.ClusterTopologyKind() != mtest.LoadBalanced {\n\t\treturn mtest.ClusterURI()\n\t}\n\n\t// For non-serverless load-balanced deployments, UseMultipleMongoses is used to determine the load balancer URI. If set to false,\n\t// the LB fronts a single server. If unset or explicitly true, the LB fronts multiple mongos servers.\n\tswitch {\n\tcase opts.UseMultipleMongoses != nil && !*opts.UseMultipleMongoses:\n\t\treturn mtest.SingleMongosLoadBalancerURI()\n\tdefault:\n\t\treturn mtest.MultiMongosLoadBalancerURI()\n\t}\n}\n\n// TODO(GODRIVER-3726): Need to update the logic to an Unmarshal method.\nfunc createAutoEncryptionOptions(opts bson.Raw) (*options.AutoEncryptionOptions, error) {\n\taeo := options.AutoEncryption()\n\tvar kvnsFound bool\n\telems, err := opts.Elements()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tfor _, elem := range elems {\n\t\tname := elem.Key()\n\t\topt := elem.Value()\n\n\t\tswitch name {\n\t\tcase \"kmsProviders\":\n\t\t\tproviders := make(map[string]map[string]any)\n\t\t\telems, err := opt.Document().Elements()\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tfor _, elem := range elems {\n\t\t\t\tkey := elem.Key()\n\t\t\t\topt := elem.Value().Document()\n\t\t\t\tprovider, err := getKmsProvider(key, opt)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t\tif provider == nil {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tproviders[key] = provider\n\t\t\t\tif key == \"kmip\" && tlsClientCertificateKeyFile != \"\" && tlsCAFile != \"\" {\n\t\t\t\t\tcfg, err := options.BuildTLSConfig(map[string]any{\n\t\t\t\t\t\t\"tlsCertificateKeyFile\": tlsClientCertificateKeyFile,\n\t\t\t\t\t\t\"tlsCAFile\":             tlsCAFile,\n\t\t\t\t\t})\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\treturn nil, fmt.Errorf(\"error constructing tls config: %w\", err)\n\t\t\t\t\t}\n\t\t\t\t\taeo.SetTLSConfig(map[string]*tls.Config{\n\t\t\t\t\t\t\"kmip\": cfg,\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t}\n\t\t\taeo.SetKmsProviders(providers)\n\t\tcase \"schemaMap\":\n\t\t\tvar schemaMap map[string]any\n\t\t\terr := bson.Unmarshal(opt.Document(), &schemaMap)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"error creating schema map: %v\", err)\n\t\t\t}\n\t\t\taeo.SetSchemaMap(schemaMap)\n\t\tcase \"keyVaultNamespace\":\n\t\t\tkvnsFound = true\n\t\t\taeo.SetKeyVaultNamespace(opt.StringValue())\n\t\tcase \"bypassAutoEncryption\":\n\t\t\taeo.SetBypassAutoEncryption(opt.Boolean())\n\t\tcase \"encryptedFieldsMap\":\n\t\t\tvar encryptedFieldsMap map[string]any\n\t\t\terr := bson.Unmarshal(opt.Document(), &encryptedFieldsMap)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"error creating encryptedFieldsMap: %v\", err)\n\t\t\t}\n\t\t\taeo.SetEncryptedFieldsMap(encryptedFieldsMap)\n\t\tcase \"bypassQueryAnalysis\":\n\t\t\taeo.SetBypassQueryAnalysis(opt.Boolean())\n\t\tdefault:\n\t\t\treturn nil, fmt.Errorf(\"unrecognized option: %v\", name)\n\t\t}\n\t}\n\tif !kvnsFound {\n\t\taeo.SetKeyVaultNamespace(\"keyvault.datakeys\")\n\t}\n\n\treturn aeo, nil\n}\n\n// disconnect disconnects the client associated with this entity. It is an\n// idempotent operation, unlike the mongo client's disconnect method. This\n// property will help avoid unnecessary errors when calling disconnect on a\n// client that has already been disconnected, such as the case when the test\n// runner is required to run the closure as part of an operation.\nfunc (c *clientEntity) disconnect(ctx context.Context) error {\n\tif c.disconnected {\n\t\treturn nil\n\t}\n\n\tif err := c.Disconnect(ctx); err != nil {\n\t\treturn err\n\t}\n\n\tc.disconnected = true\n\n\treturn nil\n}\n\nfunc (c *clientEntity) stopListeningForEvents() {\n\tc.setRecordEvents(false)\n}\n\nfunc (c *clientEntity) isIgnoredEvent(commandName string, eventDoc bson.Raw) bool {\n\t// Check if command is in ignoredCommands.\n\tif _, ok := c.ignoredCommands[commandName]; ok {\n\t\treturn true\n\t}\n\n\tif commandName == \"hello\" || strings.ToLower(commandName) == handshake.LegacyHelloLowercase {\n\t\t// If observeSensitiveCommands is false (or unset) and hello command has been\n\t\t// redacted at operation level, hello command should be ignored as it contained\n\t\t// speculativeAuthenticate.\n\t\tsensitiveCommandsIgnored := c.observeSensitiveCommands == nil || !*c.observeSensitiveCommands\n\t\tredacted := len(eventDoc) == 0\n\t\tif sensitiveCommandsIgnored && redacted {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\nfunc (c *clientEntity) startedEvents() []*event.CommandStartedEvent {\n\tvar events []*event.CommandStartedEvent\n\tfor _, evt := range c.started {\n\t\tif !c.isIgnoredEvent(evt.CommandName, evt.Command) {\n\t\t\tevents = append(events, evt)\n\t\t}\n\t}\n\n\treturn events\n}\n\nfunc (c *clientEntity) succeededEvents() []*event.CommandSucceededEvent {\n\tvar events []*event.CommandSucceededEvent\n\tfor _, evt := range c.succeeded {\n\t\tif !c.isIgnoredEvent(evt.CommandName, evt.Reply) {\n\t\t\tevents = append(events, evt)\n\t\t}\n\t}\n\n\treturn events\n}\n\nfunc (c *clientEntity) failedEvents() []*event.CommandFailedEvent {\n\tvar events []*event.CommandFailedEvent\n\tfor _, evt := range c.failed {\n\t\tif _, ok := c.ignoredCommands[evt.CommandName]; !ok {\n\t\t\tevents = append(events, evt)\n\t\t}\n\t}\n\n\treturn events\n}\n\nfunc (c *clientEntity) poolEvents() []*event.PoolEvent {\n\treturn c.pooled\n}\n\nfunc (c *clientEntity) numberConnectionsCheckedOut() int32 {\n\treturn c.numConnsCheckedOut\n}\n\nfunc (c *clientEntity) addEventsCount(eventType monitoringEventType) {\n\tc.eventsCountLock.Lock()\n\tdefer c.eventsCountLock.Unlock()\n\n\tc.eventsCount[eventType]++\n}\n\nfunc (c *clientEntity) addServerDescriptionChangedEventCount(evt serverDescriptionChangedEventInfo) {\n\tc.serverDescriptionChangedEventsCountLock.Lock()\n\tdefer c.serverDescriptionChangedEventsCountLock.Unlock()\n\n\tc.serverDescriptionChangedEventsCount[evt]++\n}\n\nfunc (c *clientEntity) getEventCount(eventType monitoringEventType) int32 {\n\tc.eventsCountLock.RLock()\n\tdefer c.eventsCountLock.RUnlock()\n\n\treturn c.eventsCount[eventType]\n}\n\nfunc (c *clientEntity) getServerDescriptionChangedEventCount(expected serverDescriptionChangedEventInfo) int32 {\n\tc.serverDescriptionChangedEventsCountLock.Lock()\n\tdefer c.serverDescriptionChangedEventsCountLock.Unlock()\n\n\t// If the previousDescription or newDescription is \"nil\" in the expected case,\n\t// then those fields can be anything.\n\tfor actual, count := range c.serverDescriptionChangedEventsCount {\n\t\tactPrevD := actual.PreviousDescription\n\t\texpPrevD := expected.PreviousDescription\n\n\t\tif expPrevD != nil && expPrevD.Type != actPrevD.Type {\n\t\t\tcontinue\n\t\t}\n\n\t\tactNewD := actual.NewDescription\n\t\texpNewD := expected.NewDescription\n\n\t\tif expNewD != nil && expNewD.Type != actNewD.Type {\n\t\t\tcontinue\n\t\t}\n\n\t\treturn count\n\t}\n\n\treturn 0\n}\n\nfunc getSecondsSinceEpoch() float64 {\n\treturn float64(time.Now().UnixNano()) / float64(time.Second/time.Nanosecond)\n}\n\nfunc (c *clientEntity) processStartedEvent(_ context.Context, evt *event.CommandStartedEvent) {\n\tif !c.getRecordEvents() {\n\t\treturn\n\t}\n\tif _, ok := c.observedEvents[commandStartedEvent]; ok {\n\t\tc.started = append(c.started, evt)\n\t}\n\n\tc.addEventsCount(commandStartedEvent)\n\n\teventListIDs, ok := c.storedEvents[commandStartedEvent]\n\tif !ok {\n\t\treturn\n\t}\n\tbsonBuilder := bsoncore.NewDocumentBuilder().\n\t\tAppendString(\"name\", \"CommandStartedEvent\").\n\t\tAppendDouble(\"observedAt\", getSecondsSinceEpoch()).\n\t\tAppendString(\"databaseName\", evt.DatabaseName).\n\t\tAppendString(\"commandName\", evt.CommandName).\n\t\tAppendInt64(\"requestId\", evt.RequestID).\n\t\tAppendString(\"connectionId\", evt.ConnectionID)\n\tif evt.ServiceID != nil {\n\t\tbsonBuilder.AppendString(\"serviceId\", evt.ServiceID.String())\n\t}\n\tdoc := bson.Raw(bsonBuilder.Build())\n\tfor _, id := range eventListIDs {\n\t\tc.entityMap.appendEventsEntity(id, doc)\n\t}\n}\n\nfunc (c *clientEntity) processSucceededEvent(_ context.Context, evt *event.CommandSucceededEvent) {\n\tif !c.getRecordEvents() {\n\t\treturn\n\t}\n\tif _, ok := c.observedEvents[commandSucceededEvent]; ok {\n\t\tc.succeeded = append(c.succeeded, evt)\n\t}\n\n\tc.addEventsCount(commandSucceededEvent)\n\n\teventListIDs, ok := c.storedEvents[\"CommandSucceededEvent\"]\n\tif !ok {\n\t\treturn\n\t}\n\tbsonBuilder := bsoncore.NewDocumentBuilder().\n\t\tAppendString(\"name\", \"CommandSucceededEvent\").\n\t\tAppendDouble(\"observedAt\", getSecondsSinceEpoch()).\n\t\tAppendString(\"commandName\", evt.CommandName).\n\t\tAppendInt64(\"requestId\", evt.RequestID).\n\t\tAppendString(\"connectionId\", evt.ConnectionID)\n\tif evt.ServiceID != nil {\n\t\tbsonBuilder.AppendString(\"serviceId\", evt.ServiceID.String())\n\t}\n\tdoc := bson.Raw(bsonBuilder.Build())\n\tfor _, id := range eventListIDs {\n\t\tc.entityMap.appendEventsEntity(id, doc)\n\t}\n}\n\nfunc (c *clientEntity) processFailedEvent(_ context.Context, evt *event.CommandFailedEvent) {\n\tif !c.getRecordEvents() {\n\t\treturn\n\t}\n\tif _, ok := c.observedEvents[commandFailedEvent]; ok {\n\t\tc.failed = append(c.failed, evt)\n\t}\n\n\tc.addEventsCount(commandFailedEvent)\n\n\teventListIDs, ok := c.storedEvents[\"CommandFailedEvent\"]\n\tif !ok {\n\t\treturn\n\t}\n\tbsonBuilder := bsoncore.NewDocumentBuilder().\n\t\tAppendString(\"name\", \"CommandFailedEvent\").\n\t\tAppendDouble(\"observedAt\", getSecondsSinceEpoch()).\n\t\tAppendInt64(\"durationNanos\", evt.Duration.Nanoseconds()).\n\t\tAppendString(\"commandName\", evt.CommandName).\n\t\tAppendInt64(\"requestId\", evt.RequestID).\n\t\tAppendString(\"connectionId\", evt.ConnectionID).\n\t\tAppendString(\"failure\", evt.Failure.Error())\n\tif evt.ServiceID != nil {\n\t\tbsonBuilder.AppendString(\"serviceId\", evt.ServiceID.String())\n\t}\n\tdoc := bson.Raw(bsonBuilder.Build())\n\tfor _, id := range eventListIDs {\n\t\tc.entityMap.appendEventsEntity(id, doc)\n\t}\n}\n\nfunc (c *clientEntity) resetEventHistory() {\n\tc.eventProcessMu.Lock()\n\tdefer c.eventProcessMu.Unlock()\n\n\tc.pooled = nil\n\tc.serverDescriptionChanged = nil\n\tc.serverHeartbeatStartedEvent = nil\n\tc.serverHeartbeatSucceeded = nil\n\tc.serverHeartbeatFailedEvent = nil\n\tc.topologyDescriptionChanged = nil\n\tc.topologyOpening = nil\n\tc.topologyClosed = nil\n}\n\nfunc (c *clientEntity) latestTopology() event.TopologyDescription {\n\tc.eventProcessMu.RLock()\n\tdefer c.eventProcessMu.RUnlock()\n\n\treturn c.latestDesc\n}\n\nfunc getPoolEventDocument(evt *event.PoolEvent, eventType monitoringEventType) bson.Raw {\n\tbsonBuilder := bsoncore.NewDocumentBuilder().\n\t\tAppendString(\"name\", string(eventType)).\n\t\tAppendDouble(\"observedAt\", getSecondsSinceEpoch()).\n\t\tAppendString(\"address\", evt.Address)\n\tif evt.ConnectionID != 0 {\n\t\tbsonBuilder.AppendString(\"connectionId\", fmt.Sprint(evt.ConnectionID))\n\t}\n\tif evt.PoolOptions != nil {\n\t\toptionsDoc := bsoncore.NewDocumentBuilder().\n\t\t\tAppendString(\"maxPoolSize\", fmt.Sprint(evt.PoolOptions.MaxPoolSize)).\n\t\t\tAppendString(\"minPoolSize\", fmt.Sprint(evt.PoolOptions.MinPoolSize)).\n\t\t\tAppendString(\"maxIdleTimeMS\", fmt.Sprint(evt.PoolOptions.WaitQueueTimeoutMS)).\n\t\t\tBuild()\n\t\tbsonBuilder.AppendDocument(\"poolOptions\", optionsDoc)\n\t}\n\tif evt.Reason != \"\" {\n\t\tbsonBuilder.AppendString(\"reason\", evt.Reason)\n\t}\n\tif evt.ServiceID != nil {\n\t\tbsonBuilder.AppendString(\"serviceId\", evt.ServiceID.String())\n\t}\n\treturn bson.Raw(bsonBuilder.Build())\n}\n\nfunc (c *clientEntity) processPoolEvent(evt *event.PoolEvent) {\n\tif !c.getRecordEvents() {\n\t\treturn\n\t}\n\n\t// Update the connection counter. This happens even if we're not storing any\n\t// events.\n\tswitch evt.Type {\n\tcase event.ConnectionCheckedOut:\n\t\tatomic.AddInt32(&c.numConnsCheckedOut, 1)\n\tcase event.ConnectionCheckedIn:\n\t\tatomic.AddInt32(&c.numConnsCheckedOut, -1)\n\tcase event.ConnectionReady:\n\t\tc.eventProcessMu.Lock()\n\t\tc.connsPerServer[evt.Address]++\n\t\tc.eventProcessMu.Unlock()\n\tcase event.ConnectionClosed:\n\t\tc.eventProcessMu.Lock()\n\t\tc.connsPerServer[evt.Address]--\n\t\tc.eventProcessMu.Unlock()\n\t}\n\n\teventType := monitoringEventTypeFromPoolEvent(evt)\n\tif _, ok := c.observedEvents[eventType]; ok {\n\t\tc.pooled = append(c.pooled, evt)\n\t}\n\n\tc.addEventsCount(eventType)\n\n\tif eventListIDs, ok := c.storedEvents[eventType]; ok {\n\t\teventBSON := getPoolEventDocument(evt, eventType)\n\t\tfor _, id := range eventListIDs {\n\t\t\tc.entityMap.appendEventsEntity(id, eventBSON)\n\t\t}\n\t}\n}\n\n// connsReady returns the number of ready connections for the given server\n// address. If the server is not data-bearing, this method will return -1.\nfunc (c *clientEntity) connsReady(server event.ServerDescription) int {\n\tc.eventProcessMu.RLock()\n\tdefer c.eventProcessMu.RUnlock()\n\n\tif server.Kind == description.ServerKindRSArbiter.String() ||\n\t\tserver.Kind == description.ServerKindRSGhost.String() {\n\t\treturn -1\n\t}\n\n\treturn c.connsPerServer[server.Addr.String()]\n}\n\nfunc (c *clientEntity) processServerDescriptionChangedEvent(evt *event.ServerDescriptionChangedEvent) {\n\tc.eventProcessMu.Lock()\n\tdefer c.eventProcessMu.Unlock()\n\n\tif !c.getRecordEvents() {\n\t\treturn\n\t}\n\n\tif _, ok := c.observedEvents[serverDescriptionChangedEvent]; ok {\n\t\tc.serverDescriptionChanged = append(c.serverDescriptionChanged, evt)\n\t}\n\n\t// Record object-specific unified spec test data on an event.\n\tc.addServerDescriptionChangedEventCount(*newServerDescriptionChangedEventInfo(evt))\n\n\t// Record the event generally.\n\tc.addEventsCount(serverDescriptionChangedEvent)\n}\n\nfunc (c *clientEntity) processServerHeartbeatFailedEvent(evt *event.ServerHeartbeatFailedEvent) {\n\tc.eventProcessMu.Lock()\n\tdefer c.eventProcessMu.Unlock()\n\n\tif !c.getRecordEvents() {\n\t\treturn\n\t}\n\n\tif _, ok := c.observedEvents[serverHeartbeatFailedEvent]; ok {\n\t\tc.serverHeartbeatFailedEvent = append(c.serverHeartbeatFailedEvent, evt)\n\t}\n\n\tc.addEventsCount(serverHeartbeatFailedEvent)\n}\n\nfunc (c *clientEntity) processServerHeartbeatStartedEvent(evt *event.ServerHeartbeatStartedEvent) {\n\tc.eventProcessMu.Lock()\n\tdefer c.eventProcessMu.Unlock()\n\n\tif !c.getRecordEvents() {\n\t\treturn\n\t}\n\n\tif _, ok := c.observedEvents[serverHeartbeatStartedEvent]; ok {\n\t\tc.serverHeartbeatStartedEvent = append(c.serverHeartbeatStartedEvent, evt)\n\t}\n\n\tc.addEventsCount(serverHeartbeatStartedEvent)\n}\n\nfunc (c *clientEntity) processServerHeartbeatSucceededEvent(evt *event.ServerHeartbeatSucceededEvent) {\n\tc.eventProcessMu.Lock()\n\tdefer c.eventProcessMu.Unlock()\n\n\tif !c.getRecordEvents() {\n\t\treturn\n\t}\n\n\tif _, ok := c.observedEvents[serverHeartbeatSucceededEvent]; ok {\n\t\tc.serverHeartbeatSucceeded = append(c.serverHeartbeatSucceeded, evt)\n\t}\n\n\tc.addEventsCount(serverHeartbeatSucceededEvent)\n}\n\nfunc (c *clientEntity) processTopologyDescriptionChangedEvent(evt *event.TopologyDescriptionChangedEvent) {\n\tc.eventProcessMu.Lock()\n\tdefer c.eventProcessMu.Unlock()\n\n\tif !c.getRecordEvents() {\n\t\treturn\n\t}\n\n\tc.latestDesc = evt.NewDescription\n\n\tif _, ok := c.observedEvents[topologyDescriptionChangedEvent]; ok {\n\t\tc.topologyDescriptionChanged = append(c.topologyDescriptionChanged, evt)\n\t}\n\n\tc.addEventsCount(topologyDescriptionChangedEvent)\n}\n\nfunc (c *clientEntity) processTopologyOpeningEvent(evt *event.TopologyOpeningEvent) {\n\tc.eventProcessMu.Lock()\n\tdefer c.eventProcessMu.Unlock()\n\n\tif !c.getRecordEvents() {\n\t\treturn\n\t}\n\n\tif _, ok := c.observedEvents[topologyOpeningEvent]; ok {\n\t\tc.topologyOpening = append(c.topologyOpening, evt)\n\t}\n\n\tc.addEventsCount(topologyOpeningEvent)\n}\n\nfunc (c *clientEntity) processTopologyClosedEvent(evt *event.TopologyClosedEvent) {\n\tc.eventProcessMu.Lock()\n\tdefer c.eventProcessMu.Unlock()\n\n\tif !c.getRecordEvents() {\n\t\treturn\n\t}\n\n\tif _, ok := c.observedEvents[topologyClosedEvent]; ok {\n\t\tc.topologyClosed = append(c.topologyClosed, evt)\n\t}\n\n\tc.addEventsCount(topologyClosedEvent)\n}\n\nfunc (c *clientEntity) setRecordEvents(record bool) {\n\tc.recordEvents.Store(record)\n}\n\nfunc (c *clientEntity) getRecordEvents() bool {\n\treturn c.recordEvents.Load().(bool)\n}\n\nfunc setClientOptionsFromURIOptions(clientOpts *options.ClientOptions, uriOpts bson.M) error {\n\t// A write concern can be constructed across multiple URI options (e.g. \"w\", \"j\", and \"wTimeoutMS\") so we declare an\n\t// empty writeConcern instance here that can be populated in the loop below.\n\tvar wc writeConcern\n\tvar wcSet bool\n\n\tfor key, value := range uriOpts {\n\t\tswitch strings.ToLower(key) {\n\t\tcase \"appname\":\n\t\t\tclientOpts.SetAppName(value.(string))\n\t\tcase \"connecttimeoutms\":\n\t\t\tclientOpts.SetConnectTimeout(time.Duration(value.(int32)) * time.Millisecond)\n\t\tcase \"heartbeatfrequencyms\":\n\t\t\tclientOpts.SetHeartbeatInterval(time.Duration(value.(int32)) * time.Millisecond)\n\t\tcase \"loadbalanced\":\n\t\t\tclientOpts.SetLoadBalanced(value.(bool))\n\t\tcase \"maxidletimems\":\n\t\t\tclientOpts.SetMaxConnIdleTime(time.Duration(value.(int32)) * time.Millisecond)\n\t\tcase \"minpoolsize\":\n\t\t\tclientOpts.SetMinPoolSize(uint64(value.(int32)))\n\t\tcase \"maxpoolsize\":\n\t\t\tclientOpts.SetMaxPoolSize(uint64(value.(int32)))\n\t\tcase \"maxconnecting\":\n\t\t\tclientOpts.SetMaxConnecting(uint64(value.(int32)))\n\t\tcase \"readconcernlevel\":\n\t\t\tclientOpts.SetReadConcern(&readconcern.ReadConcern{Level: value.(string)})\n\t\tcase \"retryreads\":\n\t\t\tclientOpts.SetRetryReads(value.(bool))\n\t\tcase \"retrywrites\":\n\t\t\tclientOpts.SetRetryWrites(value.(bool))\n\t\tcase \"sockettimeoutms\":\n\t\t\t// TODO(DRIVERS-2829): Error here instead of skip to ensure that if new\n\t\t\t// tests are added containing socketTimeoutMS (a legacy timeout option\n\t\t\t// that we have removed as of v2), then a CSOT analogue exists. Once we\n\t\t\t// have ensured an analogue exists, extend \"skippedTestDescriptions\" to\n\t\t\t// avoid this error.\n\t\t\treturn newSkipTestError(\"the socketTimeoutMS client option is not supported\")\n\t\tcase \"w\":\n\t\t\twc.W = value\n\t\t\twcSet = true\n\t\tcase \"waitqueuetimeoutms\":\n\t\t\treturn newSkipTestError(\"the waitQueueTimeoutMS client option is not supported\")\n\t\tcase \"waitqueuesize\":\n\t\t\treturn newSkipTestError(\"the waitQueueSize client option is not supported\")\n\t\tcase \"timeoutms\":\n\t\t\tclientOpts.SetTimeout(time.Duration(value.(int32)) * time.Millisecond)\n\t\tcase \"serverselectiontimeoutms\":\n\t\t\tclientOpts.SetServerSelectionTimeout(time.Duration(value.(int32)) * time.Millisecond)\n\t\tcase \"servermonitoringmode\":\n\t\t\tclientOpts.SetServerMonitoringMode(value.(string))\n\t\tdefault:\n\t\t\treturn fmt.Errorf(\"unrecognized URI option %s\", key)\n\t\t}\n\t}\n\n\tif wcSet {\n\t\tconverted, err := wc.toWriteConcernOption()\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"error creating write concern: %w\", err)\n\t\t}\n\t\tclientOpts.SetWriteConcern(converted)\n\t}\n\treturn nil\n}\n\nfunc evaluateUseMultipleMongoses(clientOpts *options.ClientOptions, useMultipleMongoses bool) error {\n\thosts := mtest.ClusterConnString().Hosts\n\n\tif !useMultipleMongoses {\n\t\tclientOpts.SetHosts(hosts[:1])\n\t\treturn nil\n\t}\n\n\tif len(hosts) < 2 {\n\t\treturn fmt.Errorf(\"multiple mongoses required but cluster URI %q only contains one host\", mtest.ClusterURI())\n\t}\n\treturn nil\n}\n\n// awaitMinimumPoolSize waits for the client's connection pool to reach the\n// specified minimum size. This is a best effort operation that times out after\n// some predefined amount of time to avoid blocking tests indefinitely.\nfunc awaitMinimumPoolSize(ctx context.Context, entity *clientEntity, minPoolSize uint64) error {\n\tticker := time.NewTicker(100 * time.Millisecond)\n\tdefer ticker.Stop()\n\n\tfor {\n\t\tselect {\n\t\tcase <-ctx.Done():\n\t\t\treturn fmt.Errorf(\"timed out waiting for client to reach minPoolSize\")\n\t\tcase <-ticker.C:\n\t\t\tready := true\n\t\t\tfor _, server := range entity.latestTopology().Servers {\n\t\t\t\tif r := entity.connsReady(server); r >= 0 && r < int(minPoolSize) {\n\t\t\t\t\tready = false\n\n\t\t\t\t\t// If any server has less than minPoolSize connections, continue\n\t\t\t\t\t// waiting.\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif ready {\n\t\t\t\tentity.resetEventHistory()\n\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "internal/integration/unified/client_operation_execution.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage unified\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/bsonutil\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/xoptions\"\n)\n\n// This file contains helpers to execute client operations.\n\nfunc executeCreateChangeStream(ctx context.Context, operation *operation) (*operationResult, error) {\n\tvar watcher interface {\n\t\tWatch(context.Context, any, ...options.Lister[options.ChangeStreamOptions]) (*mongo.ChangeStream, error)\n\t}\n\tvar err error\n\n\twatcher, err = entities(ctx).client(operation.Object)\n\tif err != nil {\n\t\twatcher, err = entities(ctx).database(operation.Object)\n\t}\n\tif err != nil {\n\t\twatcher, err = entities(ctx).collection(operation.Object)\n\t}\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"no client, database, or collection entity found with ID %q\", operation.Object)\n\t}\n\n\tvar pipeline []any\n\topts := options.ChangeStream()\n\n\telems, _ := operation.Arguments.Elements()\n\tfor _, elem := range elems {\n\t\tkey := elem.Key()\n\t\tval := elem.Value()\n\n\t\tswitch key {\n\t\tcase \"batchSize\":\n\t\t\topts.SetBatchSize(val.Int32())\n\t\tcase \"collation\":\n\t\t\tcollation, err := createCollation(val.Document())\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"error creating collation: %w\", err)\n\t\t\t}\n\t\t\topts.SetCollation(*collation)\n\t\tcase \"comment\":\n\t\t\topts.SetComment(val)\n\t\tcase \"fullDocument\":\n\t\t\tswitch fd := val.StringValue(); fd {\n\t\t\tcase \"default\":\n\t\t\t\topts.SetFullDocument(options.Default)\n\t\t\tcase \"required\":\n\t\t\t\topts.SetFullDocument(options.Required)\n\t\t\tcase \"updateLookup\":\n\t\t\t\topts.SetFullDocument(options.UpdateLookup)\n\t\t\tcase \"whenAvailable\":\n\t\t\t\topts.SetFullDocument(options.WhenAvailable)\n\t\t\tdefault:\n\t\t\t\treturn nil, fmt.Errorf(\"unrecognized fullDocument value %q\", fd)\n\t\t\t}\n\t\tcase \"fullDocumentBeforeChange\":\n\t\t\tswitch fdbc := val.StringValue(); fdbc {\n\t\t\tcase \"off\":\n\t\t\t\topts.SetFullDocumentBeforeChange(options.Off)\n\t\t\tcase \"required\":\n\t\t\t\topts.SetFullDocumentBeforeChange(options.Required)\n\t\t\tcase \"whenAvailable\":\n\t\t\t\topts.SetFullDocumentBeforeChange(options.WhenAvailable)\n\t\t\t}\n\t\tcase \"maxAwaitTimeMS\":\n\t\t\topts.SetMaxAwaitTime(time.Duration(val.Int32()) * time.Millisecond)\n\t\tcase \"pipeline\":\n\t\t\tpipeline = bsonutil.RawToInterfaces(bsonutil.RawArrayToDocuments(val.Array())...)\n\t\tcase \"resumeAfter\":\n\t\t\topts.SetResumeAfter(val.Document())\n\t\tcase \"showExpandedEvents\":\n\t\t\topts.SetShowExpandedEvents(val.Boolean())\n\t\tcase \"startAfter\":\n\t\t\topts.SetStartAfter(val.Document())\n\t\tcase \"startAtOperationTime\":\n\t\t\tt, i := val.Timestamp()\n\t\t\topts.SetStartAtOperationTime(&bson.Timestamp{T: t, I: i})\n\t\tdefault:\n\t\t\treturn nil, fmt.Errorf(\"unrecognized createChangeStream option %q\", key)\n\t\t}\n\t}\n\tif pipeline == nil {\n\t\treturn nil, newMissingArgumentError(\"pipeline\")\n\t}\n\n\tstream, err := watcher.Watch(ctx, pipeline, opts)\n\tif err != nil {\n\t\treturn newErrorResult(err), nil\n\t}\n\n\t// createChangeStream is sometimes used with no corresponding saveResultAsEntity field. Return an\n\t// empty result in this case.\n\tif operation.ResultEntityID != nil {\n\t\tif err := entities(ctx).addCursorEntity(*operation.ResultEntityID, stream); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"error storing result as cursor entity: %w\", err)\n\t\t}\n\t}\n\treturn newEmptyResult(), nil\n}\n\nfunc executeListDatabases(ctx context.Context, operation *operation, nameOnly bool) (*operationResult, error) {\n\tclient, err := entities(ctx).client(operation.Object)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// We set a default filter rather than erroring if the Arguments doc doesn't have a \"filter\" field because the\n\t// spec says drivers should support this field, not must.\n\tfilter := emptyDocument\n\topts := options.ListDatabases().SetNameOnly(nameOnly)\n\n\telems, _ := operation.Arguments.Elements()\n\tfor _, elem := range elems {\n\t\tkey := elem.Key()\n\t\tval := elem.Value()\n\n\t\tswitch key {\n\t\tcase \"authorizedDatabases\":\n\t\t\topts.SetAuthorizedDatabases(val.Boolean())\n\t\tcase \"filter\":\n\t\t\tfilter = val.Document()\n\t\tcase \"nameOnly\":\n\t\t\topts.SetNameOnly(val.Boolean())\n\t\tdefault:\n\t\t\treturn nil, fmt.Errorf(\"unrecognized listDatabases option %q\", key)\n\t\t}\n\t}\n\n\tres, err := client.ListDatabases(ctx, filter, opts)\n\tif err != nil {\n\t\treturn newErrorResult(err), nil\n\t}\n\n\tspecsArray := bsoncore.NewArrayBuilder()\n\tfor _, spec := range res.Databases {\n\t\trawSpec := bsoncore.NewDocumentBuilder().\n\t\t\tAppendString(\"name\", spec.Name).\n\t\t\tAppendInt64(\"sizeOnDisk\", spec.SizeOnDisk).\n\t\t\tAppendBoolean(\"empty\", spec.Empty).\n\t\t\tBuild()\n\n\t\tspecsArray.AppendDocument(rawSpec)\n\t}\n\traw := bsoncore.NewDocumentBuilder().\n\t\tAppendArray(\"databases\", specsArray.Build()).\n\t\tAppendInt64(\"totalSize\", res.TotalSize).\n\t\tBuild()\n\treturn newDocumentResult(raw, nil), nil\n}\n\nfunc executeClientBulkWrite(ctx context.Context, operation *operation) (*operationResult, error) {\n\tclient, err := entities(ctx).client(operation.Object)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar writes []mongo.ClientBulkWrite\n\topts := options.ClientBulkWrite()\n\n\telems, err := operation.Arguments.Elements()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tfor _, elem := range elems {\n\t\tkey := elem.Key()\n\t\tval := elem.Value()\n\n\t\tswitch key {\n\t\tcase \"models\":\n\t\t\tmodels, err := val.Array().Values()\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tfor _, m := range models {\n\t\t\t\tmodel := m.Document().Index(0)\n\t\t\t\tvar op *mongo.ClientBulkWrite\n\t\t\t\tswitch key := model.Key(); key {\n\t\t\t\tcase \"insertOne\":\n\t\t\t\t\top, err = createClientInsertOneModel(model.Value().Document())\n\t\t\t\tcase \"updateOne\":\n\t\t\t\t\top, err = createClientUpdateOneModel(model.Value().Document())\n\t\t\t\tcase \"updateMany\":\n\t\t\t\t\top, err = createClientUpdateManyModel(model.Value().Document())\n\t\t\t\tcase \"replaceOne\":\n\t\t\t\t\top, err = createClientReplaceOneModel(model.Value().Document())\n\t\t\t\tcase \"deleteOne\":\n\t\t\t\t\top, err = createClientDeleteOneModel(model.Value().Document())\n\t\t\t\tcase \"deleteMany\":\n\t\t\t\t\top, err = createClientDeleteManyModel(model.Value().Document())\n\t\t\t\tdefault:\n\t\t\t\t\terr = fmt.Errorf(\"unrecognized bulkWrite model %q\", key)\n\t\t\t\t}\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t\twrites = append(writes, *op)\n\t\t\t}\n\t\tcase \"bypassDocumentValidation\":\n\t\t\topts.SetBypassDocumentValidation(val.Boolean())\n\t\tcase \"comment\":\n\t\t\topts.SetComment(val)\n\t\tcase \"let\":\n\t\t\topts.SetLet(val.Document())\n\t\tcase \"ordered\":\n\t\t\topts.SetOrdered(val.Boolean())\n\t\tcase \"verboseResults\":\n\t\t\topts.SetVerboseResults(val.Boolean())\n\t\tcase \"writeConcern\":\n\t\t\tvar wc writeConcern\n\t\t\terr := bson.Unmarshal(val.Value, &wc)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tc, err := wc.toWriteConcernOption()\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\topts.SetWriteConcern(c)\n\t\tcase \"rawData\":\n\t\t\terr = xoptions.SetInternalClientBulkWriteOptions(opts, key, val.Boolean())\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\tdefault:\n\t\t\treturn nil, fmt.Errorf(\"unrecognized bulkWrite option %q\", key)\n\t\t}\n\t}\n\n\tres, err := client.BulkWrite(ctx, writes, opts)\n\tvar bwe mongo.ClientBulkWriteException\n\tif errors.As(err, &bwe) {\n\t\tres = bwe.PartialResult\n\t}\n\tif res == nil {\n\t\treturn newDocumentResult(emptyCoreDocument, err), nil\n\t}\n\trawBuilder := bsoncore.NewDocumentBuilder().\n\t\tAppendBoolean(\"acknowledged\", res.Acknowledged).\n\t\tAppendInt64(\"deletedCount\", res.DeletedCount).\n\t\tAppendInt64(\"insertedCount\", res.InsertedCount).\n\t\tAppendInt64(\"matchedCount\", res.MatchedCount).\n\t\tAppendInt64(\"modifiedCount\", res.ModifiedCount).\n\t\tAppendInt64(\"upsertedCount\", res.UpsertedCount)\n\n\tvar resBuilder *bsoncore.DocumentBuilder\n\n\tresBuilder = bsoncore.NewDocumentBuilder()\n\tfor k, v := range res.DeleteResults {\n\t\tresBuilder.AppendDocument(strconv.Itoa(k),\n\t\t\tbsoncore.NewDocumentBuilder().\n\t\t\t\tAppendInt64(\"deletedCount\", v.DeletedCount).\n\t\t\t\tBuild(),\n\t\t)\n\t}\n\trawBuilder.AppendDocument(\"deleteResults\", resBuilder.Build())\n\n\tresBuilder = bsoncore.NewDocumentBuilder()\n\tfor k, v := range res.InsertResults {\n\t\tt, d, err := bson.MarshalValue(v.InsertedID)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tresBuilder.AppendDocument(strconv.Itoa(k),\n\t\t\tbsoncore.NewDocumentBuilder().\n\t\t\t\tAppendValue(\"insertedId\", bsoncore.Value{Type: bsoncore.Type(t), Data: d}).\n\t\t\t\tBuild(),\n\t\t)\n\t}\n\trawBuilder.AppendDocument(\"insertResults\", resBuilder.Build())\n\n\tresBuilder = bsoncore.NewDocumentBuilder()\n\tfor k, v := range res.UpdateResults {\n\t\tb := bsoncore.NewDocumentBuilder().\n\t\t\tAppendInt64(\"matchedCount\", v.MatchedCount).\n\t\t\tAppendInt64(\"modifiedCount\", v.ModifiedCount)\n\t\tif v.UpsertedID != nil {\n\t\t\tt, d, err := bson.MarshalValue(v.UpsertedID)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tb.AppendValue(\"upsertedId\", bsoncore.Value{Type: bsoncore.Type(t), Data: d})\n\t\t}\n\t\tresBuilder.AppendDocument(strconv.Itoa(k), b.Build())\n\t}\n\trawBuilder.AppendDocument(\"updateResults\", resBuilder.Build())\n\n\treturn newDocumentResult(rawBuilder.Build(), err), nil\n}\n\nfunc executeAppendMetadata(ctx context.Context, op *operation) (*operationResult, error) {\n\tclient, err := entities(ctx).client(op.Object)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"error getting client entity: %w\", err)\n\t}\n\n\telems, err := op.Arguments.Elements()\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"error getting appendMetadata arguments: %w\", err)\n\t}\n\n\tdriverInfo := options.DriverInfo{}\n\tfor _, elem := range elems {\n\t\tkey := elem.Key()\n\t\tval := elem.Value()\n\n\t\tif key == \"driverInfoOptions\" {\n\t\t\tif err = bson.Unmarshal(val.Value, &driverInfo); err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"error unmarshaling driverInfoOptions: %w\", err)\n\t\t\t}\n\t\t}\n\t}\n\n\tclient.AppendDriverInfo(driverInfo)\n\n\treturn newEmptyResult(), nil\n}\n\nfunc createClientInsertOneModel(value bson.Raw) (*mongo.ClientBulkWrite, error) {\n\tvar v struct {\n\t\tNamespace string\n\t\tDocument  bson.Raw\n\t}\n\terr := bson.Unmarshal(value, &v)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tns := strings.SplitN(v.Namespace, \".\", 2)\n\treturn &mongo.ClientBulkWrite{\n\t\tDatabase:   ns[0],\n\t\tCollection: ns[1],\n\t\tModel: &mongo.ClientInsertOneModel{\n\t\t\tDocument: v.Document,\n\t\t},\n\t}, nil\n}\n\nfunc createClientUpdateOneModel(value bson.Raw) (*mongo.ClientBulkWrite, error) {\n\tvar v struct {\n\t\tNamespace    string\n\t\tFilter       bson.Raw\n\t\tUpdate       any\n\t\tArrayFilters []any\n\t\tCollation    *options.Collation\n\t\tHint         *bson.RawValue\n\t\tUpsert       *bool\n\t\tSort         *bson.RawValue\n\t}\n\terr := bson.Unmarshal(value, &v)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar hint any\n\tif v.Hint != nil {\n\t\thint, err = createHint(*v.Hint)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\tvar sort any\n\tif v.Sort != nil {\n\t\tsort = v.Sort.Document()\n\t}\n\tmodel := &mongo.ClientUpdateOneModel{\n\t\tFilter:    v.Filter,\n\t\tUpdate:    v.Update,\n\t\tCollation: v.Collation,\n\t\tHint:      hint,\n\t\tUpsert:    v.Upsert,\n\t\tSort:      sort,\n\t}\n\tif len(v.ArrayFilters) > 0 {\n\t\tmodel.ArrayFilters = v.ArrayFilters\n\t}\n\tns := strings.SplitN(v.Namespace, \".\", 2)\n\treturn &mongo.ClientBulkWrite{\n\t\tDatabase:   ns[0],\n\t\tCollection: ns[1],\n\t\tModel:      model,\n\t}, nil\n}\n\nfunc createClientUpdateManyModel(value bson.Raw) (*mongo.ClientBulkWrite, error) {\n\tvar v struct {\n\t\tNamespace    string\n\t\tFilter       bson.Raw\n\t\tUpdate       any\n\t\tArrayFilters []any\n\t\tCollation    *options.Collation\n\t\tHint         *bson.RawValue\n\t\tUpsert       *bool\n\t}\n\terr := bson.Unmarshal(value, &v)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar hint any\n\tif v.Hint != nil {\n\t\thint, err = createHint(*v.Hint)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\tmodel := &mongo.ClientUpdateManyModel{\n\t\tFilter:    v.Filter,\n\t\tUpdate:    v.Update,\n\t\tCollation: v.Collation,\n\t\tHint:      hint,\n\t\tUpsert:    v.Upsert,\n\t}\n\tif len(v.ArrayFilters) > 0 {\n\t\tmodel.ArrayFilters = v.ArrayFilters\n\t}\n\tns := strings.SplitN(v.Namespace, \".\", 2)\n\treturn &mongo.ClientBulkWrite{\n\t\tDatabase:   ns[0],\n\t\tCollection: ns[1],\n\t\tModel:      model,\n\t}, nil\n}\n\nfunc createClientReplaceOneModel(value bson.Raw) (*mongo.ClientBulkWrite, error) {\n\tvar v struct {\n\t\tNamespace   string\n\t\tFilter      bson.Raw\n\t\tReplacement bson.Raw\n\t\tCollation   *options.Collation\n\t\tHint        *bson.RawValue\n\t\tUpsert      *bool\n\t\tSort        *bson.RawValue\n\t}\n\terr := bson.Unmarshal(value, &v)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar hint any\n\tif v.Hint != nil {\n\t\thint, err = createHint(*v.Hint)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\tvar sort any\n\tif v.Sort != nil {\n\t\tsort = v.Sort.Document()\n\t}\n\tns := strings.SplitN(v.Namespace, \".\", 2)\n\treturn &mongo.ClientBulkWrite{\n\t\tDatabase:   ns[0],\n\t\tCollection: ns[1],\n\t\tModel: &mongo.ClientReplaceOneModel{\n\t\t\tFilter:      v.Filter,\n\t\t\tReplacement: v.Replacement,\n\t\t\tCollation:   v.Collation,\n\t\t\tHint:        hint,\n\t\t\tUpsert:      v.Upsert,\n\t\t\tSort:        sort,\n\t\t},\n\t}, nil\n}\n\nfunc createClientDeleteOneModel(value bson.Raw) (*mongo.ClientBulkWrite, error) {\n\tvar v struct {\n\t\tNamespace string\n\t\tFilter    bson.Raw\n\t\tCollation *options.Collation\n\t\tHint      *bson.RawValue\n\t}\n\terr := bson.Unmarshal(value, &v)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar hint any\n\tif v.Hint != nil {\n\t\thint, err = createHint(*v.Hint)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\tns := strings.SplitN(v.Namespace, \".\", 2)\n\treturn &mongo.ClientBulkWrite{\n\t\tDatabase:   ns[0],\n\t\tCollection: ns[1],\n\t\tModel: &mongo.ClientDeleteOneModel{\n\t\t\tFilter:    v.Filter,\n\t\t\tCollation: v.Collation,\n\t\t\tHint:      hint,\n\t\t},\n\t}, nil\n}\n\nfunc createClientDeleteManyModel(value bson.Raw) (*mongo.ClientBulkWrite, error) {\n\tvar v struct {\n\t\tNamespace string\n\t\tFilter    bson.Raw\n\t\tCollation *options.Collation\n\t\tHint      *bson.RawValue\n\t}\n\terr := bson.Unmarshal(value, &v)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar hint any\n\tif v.Hint != nil {\n\t\thint, err = createHint(*v.Hint)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\tns := strings.SplitN(v.Namespace, \".\", 2)\n\treturn &mongo.ClientBulkWrite{\n\t\tDatabase:   ns[0],\n\t\tCollection: ns[1],\n\t\tModel: &mongo.ClientDeleteManyModel{\n\t\t\tFilter:    v.Filter,\n\t\t\tCollation: v.Collation,\n\t\t\tHint:      hint,\n\t\t},\n\t}, nil\n}\n"
  },
  {
    "path": "internal/integration/unified/collection_data.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage unified\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"fmt\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/bsonutil\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/integration/mtest\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/readconcern\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/readpref\"\n)\n\ntype collectionData struct {\n\tDatabaseName   string         `bson:\"databaseName\"`\n\tCollectionName string         `bson:\"collectionName\"`\n\tDocuments      []bson.Raw     `bson:\"documents\"`\n\tOptions        *createOptions `bson:\"createOptions\"`\n}\n\ntype createOptions struct {\n\tCapped          *bool    `bson:\"capped\"`\n\tSizeInBytes     *int64   `bson:\"size\"`\n\tEncryptedFields bson.Raw `bson:\"encryptedFields\"`\n\tValidator       bson.Raw `bson:\"validator\"`\n}\n\n// createCollection configures the collection represented by the receiver using the internal client. This function\n// first drops the collection and then creates it with specified options (if any) and inserts the seed data if needed.\nfunc (c *collectionData) createCollection(ctx context.Context) error {\n\tdb := mtest.GlobalClient().Database(c.DatabaseName, options.Database().SetWriteConcern(mtest.MajorityWc))\n\tcoll := db.Collection(c.CollectionName)\n\tif err := coll.Drop(ctx); err != nil {\n\t\treturn fmt.Errorf(\"error dropping collection: %w\", err)\n\t}\n\n\t// Explicitly create collection if Options are specified.\n\tif c.Options != nil {\n\t\tcreateOpts := options.CreateCollection()\n\t\tif c.Options.Capped != nil {\n\t\t\tcreateOpts = createOpts.SetCapped(*c.Options.Capped)\n\t\t}\n\t\tif c.Options.SizeInBytes != nil {\n\t\t\tcreateOpts = createOpts.SetSizeInBytes(*c.Options.SizeInBytes)\n\t\t}\n\t\tif c.Options.EncryptedFields != nil {\n\t\t\tcreateOpts = createOpts.SetEncryptedFields(c.Options.EncryptedFields)\n\t\t}\n\t\tif c.Options.Validator != nil {\n\t\t\tcreateOpts = createOpts.SetValidator(c.Options.Validator)\n\t\t}\n\n\t\tif err := db.CreateCollection(ctx, c.CollectionName, createOpts); err != nil {\n\t\t\treturn fmt.Errorf(\"error creating collection: %w\", err)\n\t\t}\n\t} else {\n\t\t// If no options are provided, still create the collection with write concern \"majority\".\n\t\t// The write concern has to be manually specified in the command document because RunCommand does not honor\n\t\t// the database's write concern.\n\t\tcreate := bson.D{\n\t\t\t{\"create\", coll.Name()},\n\t\t\t{\"writeConcern\", bson.D{\n\t\t\t\t{\"w\", \"majority\"},\n\t\t\t}},\n\t\t}\n\t\tif err := db.RunCommand(ctx, create).Err(); err != nil {\n\t\t\treturn fmt.Errorf(\"error creating collection: %w\", err)\n\t\t}\n\t}\n\n\tif len(c.Documents) != 0 {\n\t\tdocs := bsonutil.RawToInterfaces(c.Documents...)\n\t\tif _, err := coll.InsertMany(ctx, docs); err != nil {\n\t\t\treturn fmt.Errorf(\"error inserting data: %w\", err)\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// verifyContents asserts that the collection on the server represented by this collectionData instance contains the\n// expected documents.\nfunc (c *collectionData) verifyContents(ctx context.Context) error {\n\tcollOpts := options.Collection().\n\t\tSetReadPreference(readpref.Primary()).\n\t\tSetReadConcern(readconcern.Local())\n\tcoll := mtest.GlobalClient().Database(c.DatabaseName).Collection(c.CollectionName, collOpts)\n\n\tcursor, err := coll.Find(ctx, bson.D{}, options.Find().SetSort(bson.M{\"_id\": 1}))\n\tif err != nil {\n\t\treturn fmt.Errorf(\"find error: %w\", err)\n\t}\n\tdefer cursor.Close(ctx)\n\n\tvar docs []bson.Raw\n\tif err := cursor.All(ctx, &docs); err != nil {\n\t\treturn fmt.Errorf(\"cursor iteration error: %w\", err)\n\t}\n\n\t// Verify the slice lengths are equal. This also covers the case of asserting that the collection is empty if\n\t// c.Documents is an empty slice.\n\tif len(c.Documents) != len(docs) {\n\t\treturn fmt.Errorf(\"expected %d documents but found %d: %v\", len(c.Documents), len(docs), docs)\n\t}\n\n\t// We can't use verifyValuesMatch here because the rules for evaluating matches (e.g. flexible numeric comparisons\n\t// and special $$ operators) do not apply when verifying collection outcomes. We have to permit variations in key\n\t// order, though, so we sort documents before doing a byte-wise comparison.\n\tfor idx, expected := range c.Documents {\n\t\texpected = sortDocument(expected)\n\t\tactual := sortDocument(docs[idx])\n\n\t\tif !bytes.Equal(expected, actual) {\n\t\t\treturn fmt.Errorf(\"document comparison error at index %d: expected %s, got %s\", idx, expected, actual)\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (c *collectionData) namespace() string {\n\treturn fmt.Sprintf(\"%s.%s\", c.DatabaseName, c.CollectionName)\n}\n"
  },
  {
    "path": "internal/integration/unified/collection_operation_execution.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage unified\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"strings\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/bsonutil\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/mongoutil\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/xoptions\"\n)\n\n// This file contains helpers to execute collection operations.\n\nfunc executeAggregate(ctx context.Context, operation *operation) (*operationResult, error) {\n\tvar aggregator interface {\n\t\tAggregate(context.Context, any, ...options.Lister[options.AggregateOptions]) (*mongo.Cursor, error)\n\t}\n\tvar err error\n\n\taggregator, err = entities(ctx).collection(operation.Object)\n\tif err != nil {\n\t\taggregator, err = entities(ctx).database(operation.Object)\n\t}\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"no database or collection entity found with ID %q\", operation.Object)\n\t}\n\n\tvar pipeline []any\n\topts := options.Aggregate()\n\n\telems, err := operation.Arguments.Elements()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tfor _, elem := range elems {\n\t\tkey := elem.Key()\n\t\tval := elem.Value()\n\n\t\tswitch key {\n\t\tcase \"allowDiskUse\":\n\t\t\topts.SetAllowDiskUse(val.Boolean())\n\t\tcase \"batchSize\":\n\t\t\topts.SetBatchSize(val.Int32())\n\t\tcase \"bypassDocumentValidation\":\n\t\t\topts.SetBypassDocumentValidation(val.Boolean())\n\t\tcase \"collation\":\n\t\t\tcollation, err := createCollation(val.Document())\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"error creating collation: %w\", err)\n\t\t\t}\n\t\t\topts.SetCollation(collation)\n\t\tcase \"comment\":\n\t\t\topts.SetComment(val)\n\t\tcase \"hint\":\n\t\t\thint, err := createHint(val)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"error creating hint: %w\", err)\n\t\t\t}\n\t\t\topts.SetHint(hint)\n\t\tcase \"maxAwaitTimeMS\":\n\t\t\topts.SetMaxAwaitTime(time.Duration(val.Int32()) * time.Millisecond)\n\t\tcase \"pipeline\":\n\t\t\tpipeline = bsonutil.RawToInterfaces(bsonutil.RawArrayToDocuments(val.Array())...)\n\t\tcase \"let\":\n\t\t\topts.SetLet(val.Document())\n\t\tcase \"rawData\":\n\t\t\terr = xoptions.SetInternalAggregateOptions(opts, key, val.Boolean())\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\tdefault:\n\t\t\treturn nil, fmt.Errorf(\"unrecognized aggregate option %q\", key)\n\t\t}\n\t}\n\tif pipeline == nil {\n\t\treturn nil, newMissingArgumentError(\"pipeline\")\n\t}\n\n\tcursor, err := aggregator.Aggregate(ctx, pipeline, opts)\n\tif err != nil {\n\t\treturn newErrorResult(err), nil\n\t}\n\tdefer cursor.Close(ctx)\n\n\tvar docs []bson.Raw\n\tif err := cursor.All(ctx, &docs); err != nil {\n\t\treturn newErrorResult(err), nil\n\t}\n\treturn newCursorResult(docs), nil\n}\n\nfunc executeBulkWrite(ctx context.Context, operation *operation) (*operationResult, error) {\n\tcoll, err := entities(ctx).collection(operation.Object)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar models []mongo.WriteModel\n\topts := options.BulkWrite()\n\n\telems, err := operation.Arguments.Elements()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tfor _, elem := range elems {\n\t\tkey := elem.Key()\n\t\tval := elem.Value()\n\n\t\tswitch key {\n\t\tcase \"comment\":\n\t\t\topts.SetComment(val)\n\t\tcase \"ordered\":\n\t\t\topts.SetOrdered(val.Boolean())\n\t\tcase \"requests\":\n\t\t\tmodels, err = createBulkWriteModels(val.Array())\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"error creating write models: %w\", err)\n\t\t\t}\n\t\tcase \"let\":\n\t\t\topts.SetLet(val.Document())\n\t\tcase \"rawData\":\n\t\t\terr = xoptions.SetInternalBulkWriteOptions(opts, key, val.Boolean())\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\tdefault:\n\t\t\treturn nil, fmt.Errorf(\"unrecognized bulkWrite option %q\", key)\n\t\t}\n\t}\n\tif models == nil {\n\t\treturn nil, newMissingArgumentError(\"requests\")\n\t}\n\n\tres, err := coll.BulkWrite(ctx, models, opts)\n\traw := emptyCoreDocument\n\tif res != nil {\n\t\trawUpsertedIDs := emptyDocument\n\t\tvar marshalErr error\n\t\tif res.UpsertedIDs != nil {\n\t\t\trawUpsertedIDs, marshalErr = bson.Marshal(res.UpsertedIDs)\n\t\t\tif marshalErr != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"error marshalling UpsertedIDs map to BSON: %w\", marshalErr)\n\t\t\t}\n\t\t}\n\n\t\traw = bsoncore.NewDocumentBuilder().\n\t\t\tAppendInt64(\"insertedCount\", res.InsertedCount).\n\t\t\tAppendInt64(\"deletedCount\", res.DeletedCount).\n\t\t\tAppendInt64(\"matchedCount\", res.MatchedCount).\n\t\t\tAppendInt64(\"modifiedCount\", res.ModifiedCount).\n\t\t\tAppendInt64(\"upsertedCount\", res.UpsertedCount).\n\t\t\tAppendDocument(\"upsertedIds\", rawUpsertedIDs).\n\t\t\tBuild()\n\t}\n\treturn newDocumentResult(raw, err), nil\n}\n\nfunc executeCountDocuments(ctx context.Context, operation *operation) (*operationResult, error) {\n\tcoll, err := entities(ctx).collection(operation.Object)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar filter bson.Raw\n\topts := options.Count()\n\n\telems, err := operation.Arguments.Elements()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tfor _, elem := range elems {\n\t\tkey := elem.Key()\n\t\tval := elem.Value()\n\n\t\tswitch key {\n\t\tcase \"collation\":\n\t\t\tcollation, err := createCollation(val.Document())\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"error creating collation: %w\", err)\n\t\t\t}\n\t\t\topts.SetCollation(collation)\n\t\tcase \"comment\":\n\t\t\topts.SetComment(val)\n\t\tcase \"filter\":\n\t\t\tfilter = val.Document()\n\t\tcase \"hint\":\n\t\t\thint, err := createHint(val)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"error creating hint: %w\", err)\n\t\t\t}\n\t\t\topts.SetHint(hint)\n\t\tcase \"limit\":\n\t\t\topts.SetLimit(val.Int64())\n\t\tcase \"maxTimeMS\":\n\t\t\t// TODO(DRIVERS-2829): Error here instead of skip to ensure that if new\n\t\t\t// tests are added containing maxTimeMS (a legacy timeout option that we\n\t\t\t// have removed as of v2), then a CSOT analogue exists. Once we have\n\t\t\t// ensured an analogue exists, extend \"skippedTestDescriptions\" to avoid\n\t\t\t// this error.\n\t\t\treturn nil, fmt.Errorf(\"the maxTimeMS collection option is not supported\")\n\t\tcase \"skip\":\n\t\t\topts.SetSkip(int64(val.Int32()))\n\t\tcase \"rawData\":\n\t\t\terr = xoptions.SetInternalCountOptions(opts, key, val.Boolean())\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\tdefault:\n\t\t\treturn nil, fmt.Errorf(\"unrecognized countDocuments option %q\", key)\n\t\t}\n\t}\n\tif filter == nil {\n\t\treturn nil, newMissingArgumentError(\"filter\")\n\t}\n\n\tcount, err := coll.CountDocuments(ctx, filter, opts)\n\tif err != nil {\n\t\treturn newErrorResult(err), nil\n\t}\n\treturn newValueResult(bson.TypeInt64, bsoncore.AppendInt64(nil, count), nil), nil\n}\n\nfunc executeCreateIndex(ctx context.Context, operation *operation) (*operationResult, error) {\n\tcoll, err := entities(ctx).collection(operation.Object)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar keys bson.Raw\n\tindexOpts := options.Index()\n\topts := options.CreateIndexes()\n\n\telems, err := operation.Arguments.Elements()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tfor _, elem := range elems {\n\t\tkey := elem.Key()\n\t\tval := elem.Value()\n\n\t\tswitch key {\n\t\tcase \"2dsphereIndexVersion\":\n\t\t\tindexOpts.SetSphereVersion(val.Int32())\n\t\tcase \"bits\":\n\t\t\tindexOpts.SetBits(val.Int32())\n\t\tcase \"bucketSize\":\n\t\t\tindexOpts.SetBucketSize(val.Int32())\n\t\tcase \"collation\":\n\t\t\tcollation, err := createCollation(val.Document())\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"error creating collation: %w\", err)\n\t\t\t}\n\t\t\tindexOpts.SetCollation(collation)\n\t\tcase \"defaultLanguage\":\n\t\t\tindexOpts.SetDefaultLanguage(val.StringValue())\n\t\tcase \"expireAfterSeconds\":\n\t\t\tindexOpts.SetExpireAfterSeconds(val.Int32())\n\t\tcase \"hidden\":\n\t\t\tindexOpts.SetHidden(val.Boolean())\n\t\tcase \"keys\":\n\t\t\tkeys = val.Document()\n\t\tcase \"languageOverride\":\n\t\t\tindexOpts.SetLanguageOverride(val.StringValue())\n\t\tcase \"max\":\n\t\t\tindexOpts.SetMax(val.Double())\n\t\tcase \"min\":\n\t\t\tindexOpts.SetMin(val.Double())\n\t\tcase \"name\":\n\t\t\tindexOpts.SetName(val.StringValue())\n\t\tcase \"partialFilterExpression\":\n\t\t\tindexOpts.SetPartialFilterExpression(val.Document())\n\t\tcase \"sparse\":\n\t\t\tindexOpts.SetSparse(val.Boolean())\n\t\tcase \"storageEngine\":\n\t\t\tindexOpts.SetStorageEngine(val.Document())\n\t\tcase \"unique\":\n\t\t\tindexOpts.SetUnique(val.Boolean())\n\t\tcase \"version\":\n\t\t\tindexOpts.SetVersion(val.Int32())\n\t\tcase \"textIndexVersion\":\n\t\t\tindexOpts.SetTextVersion(val.Int32())\n\t\tcase \"weights\":\n\t\t\tindexOpts.SetWeights(val.Document())\n\t\tcase \"wildcardProjection\":\n\t\t\tindexOpts.SetWildcardProjection(val.Document())\n\t\tcase \"rawData\":\n\t\t\terr = xoptions.SetInternalCreateIndexesOptions(opts, key, val.Boolean())\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\tdefault:\n\t\t\treturn nil, fmt.Errorf(\"unrecognized createIndex option %q\", key)\n\t\t}\n\t}\n\tif keys == nil {\n\t\treturn nil, newMissingArgumentError(\"keys\")\n\t}\n\n\tmodel := mongo.IndexModel{\n\t\tKeys:    keys,\n\t\tOptions: indexOpts,\n\t}\n\n\tname, err := coll.Indexes().CreateOne(ctx, model, opts)\n\treturn newValueResult(bson.TypeString, bsoncore.AppendString(nil, name), err), nil\n}\n\nfunc executeCreateSearchIndex(ctx context.Context, operation *operation) (*operationResult, error) {\n\tcoll, err := entities(ctx).collection(operation.Object)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar model mongo.SearchIndexModel\n\n\telems, err := operation.Arguments.Elements()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tfor _, elem := range elems {\n\t\tkey := elem.Key()\n\t\tval := elem.Value()\n\n\t\tswitch key {\n\t\tcase \"model\":\n\t\t\tvar m struct {\n\t\t\t\tDefinition any\n\t\t\t\tName       *string\n\t\t\t\tType       *string\n\t\t\t}\n\t\t\terr = bson.Unmarshal(val.Document(), &m)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tmodel.Definition = m.Definition\n\t\t\tmodel.Options = options.SearchIndexes()\n\t\t\tif m.Name != nil {\n\t\t\t\tmodel.Options.SetName(*m.Name)\n\t\t\t}\n\n\t\t\tif m.Type != nil {\n\t\t\t\tmodel.Options.SetType(*m.Type)\n\t\t\t}\n\t\tdefault:\n\t\t\treturn nil, fmt.Errorf(\"unrecognized createSearchIndex option %q\", key)\n\t\t}\n\t}\n\n\tname, err := coll.SearchIndexes().CreateOne(ctx, model)\n\treturn newValueResult(bson.TypeString, bsoncore.AppendString(nil, name), err), nil\n}\n\nfunc executeCreateSearchIndexes(ctx context.Context, operation *operation) (*operationResult, error) {\n\tcoll, err := entities(ctx).collection(operation.Object)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar models []mongo.SearchIndexModel\n\n\telems, err := operation.Arguments.Elements()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tfor _, elem := range elems {\n\t\tkey := elem.Key()\n\t\tval := elem.Value()\n\n\t\tswitch key {\n\t\tcase \"models\":\n\t\t\tvals, err := val.Array().Values()\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tfor _, val := range vals {\n\t\t\t\tvar m struct {\n\t\t\t\t\tDefinition any\n\t\t\t\t\tName       *string\n\t\t\t\t\tType       *string\n\t\t\t\t}\n\t\t\t\terr = bson.Unmarshal(val.Value, &m)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t\tmodel := mongo.SearchIndexModel{\n\t\t\t\t\tDefinition: m.Definition,\n\t\t\t\t\tOptions:    options.SearchIndexes(),\n\t\t\t\t}\n\t\t\t\tif m.Name != nil {\n\t\t\t\t\tmodel.Options.SetName(*m.Name)\n\t\t\t\t}\n\t\t\t\tif m.Type != nil {\n\t\t\t\t\tmodel.Options.SetType(*m.Type)\n\t\t\t\t}\n\t\t\t\tmodels = append(models, model)\n\t\t\t}\n\t\tdefault:\n\t\t\treturn nil, fmt.Errorf(\"unrecognized createSearchIndexes option %q\", key)\n\t\t}\n\t}\n\n\tnames, err := coll.SearchIndexes().CreateMany(ctx, models)\n\tbuilder := bsoncore.NewArrayBuilder()\n\tfor _, name := range names {\n\t\tbuilder.AppendString(name)\n\t}\n\treturn newValueResult(bson.TypeArray, builder.Build(), err), nil\n}\n\nfunc executeDeleteOne(ctx context.Context, operation *operation) (*operationResult, error) {\n\tcoll, err := entities(ctx).collection(operation.Object)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar filter bson.Raw\n\topts := options.DeleteOne()\n\n\telems, err := operation.Arguments.Elements()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tfor _, elem := range elems {\n\t\tkey := elem.Key()\n\t\tval := elem.Value()\n\n\t\tswitch key {\n\t\tcase \"collation\":\n\t\t\tcollation, err := createCollation(val.Document())\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"error creating collation: %w\", err)\n\t\t\t}\n\t\t\topts.SetCollation(collation)\n\t\tcase \"comment\":\n\t\t\topts.SetComment(val)\n\t\tcase \"filter\":\n\t\t\tfilter = val.Document()\n\t\tcase \"hint\":\n\t\t\thint, err := createHint(val)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"error creating hint: %w\", err)\n\t\t\t}\n\t\t\topts.SetHint(hint)\n\t\tcase \"let\":\n\t\t\topts.SetLet(val.Document())\n\t\tcase \"rawData\":\n\t\t\terr = xoptions.SetInternalDeleteOneOptions(opts, key, val.Boolean())\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\tdefault:\n\t\t\treturn nil, fmt.Errorf(\"unrecognized deleteOne option %q\", key)\n\t\t}\n\t}\n\tif filter == nil {\n\t\treturn nil, newMissingArgumentError(\"filter\")\n\t}\n\n\tres, err := coll.DeleteOne(ctx, filter, opts)\n\traw := emptyCoreDocument\n\tif res != nil {\n\t\traw = bsoncore.NewDocumentBuilder().\n\t\t\tAppendInt64(\"deletedCount\", res.DeletedCount).\n\t\t\tBuild()\n\t}\n\treturn newDocumentResult(raw, err), nil\n}\n\nfunc executeDeleteMany(ctx context.Context, operation *operation) (*operationResult, error) {\n\tcoll, err := entities(ctx).collection(operation.Object)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar filter bson.Raw\n\topts := options.DeleteMany()\n\n\telems, err := operation.Arguments.Elements()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tfor _, elem := range elems {\n\t\tkey := elem.Key()\n\t\tval := elem.Value()\n\n\t\tswitch key {\n\t\tcase \"comment\":\n\t\t\topts.SetComment(val)\n\t\tcase \"collation\":\n\t\t\tcollation, err := createCollation(val.Document())\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"error creating collation: %w\", err)\n\t\t\t}\n\t\t\topts.SetCollation(collation)\n\t\tcase \"filter\":\n\t\t\tfilter = val.Document()\n\t\tcase \"hint\":\n\t\t\thint, err := createHint(val)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"error creating hint: %w\", err)\n\t\t\t}\n\t\t\topts.SetHint(hint)\n\t\tcase \"let\":\n\t\t\topts.SetLet(val.Document())\n\t\tcase \"rawData\":\n\t\t\terr = xoptions.SetInternalDeleteManyOptions(opts, key, val.Boolean())\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\tdefault:\n\t\t\treturn nil, fmt.Errorf(\"unrecognized deleteMany option %q\", key)\n\t\t}\n\t}\n\tif filter == nil {\n\t\treturn nil, newMissingArgumentError(\"filter\")\n\t}\n\n\tres, err := coll.DeleteMany(ctx, filter, opts)\n\traw := emptyCoreDocument\n\tif res != nil {\n\t\traw = bsoncore.NewDocumentBuilder().\n\t\t\tAppendInt64(\"deletedCount\", res.DeletedCount).\n\t\t\tBuild()\n\t}\n\treturn newDocumentResult(raw, err), nil\n}\n\nfunc executeDistinct(ctx context.Context, operation *operation) (*operationResult, error) {\n\tcoll, err := entities(ctx).collection(operation.Object)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar fieldName string\n\tvar filter bson.Raw\n\topts := options.Distinct()\n\n\telems, err := operation.Arguments.Elements()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tfor _, elem := range elems {\n\t\tkey := elem.Key()\n\t\tval := elem.Value()\n\n\t\tswitch key {\n\t\tcase \"hint\":\n\t\t\topts.SetHint(val)\n\t\tcase \"collation\":\n\t\t\tcollation, err := createCollation(val.Document())\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"error creating collation: %w\", err)\n\t\t\t}\n\t\t\topts.SetCollation(collation)\n\t\tcase \"comment\":\n\t\t\topts.SetComment(val)\n\t\tcase \"fieldName\":\n\t\t\tfieldName = val.StringValue()\n\t\tcase \"filter\":\n\t\t\tfilter = val.Document()\n\t\tcase \"maxTimeMS\":\n\t\t\t// TODO(DRIVERS-2829): Error here instead of skip to ensure that if new\n\t\t\t// tests are added containing maxTimeMS (a legacy timeout option that we\n\t\t\t// have removed as of v2), then a CSOT analogue exists. Once we have\n\t\t\t// ensured an analogue exists, extend \"skippedTestDescriptions\" to avoid\n\t\t\t// this error.\n\t\t\treturn nil, fmt.Errorf(\"the maxTimeMS collection option is not supported\")\n\t\tcase \"rawData\":\n\t\t\terr = xoptions.SetInternalDistinctOptions(opts, key, val.Boolean())\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\tdefault:\n\t\t\treturn nil, fmt.Errorf(\"unrecognized distinct option %q\", key)\n\t\t}\n\t}\n\tif fieldName == \"\" {\n\t\treturn nil, newMissingArgumentError(\"fieldName\")\n\t}\n\tif filter == nil {\n\t\treturn nil, newMissingArgumentError(\"filter\")\n\t}\n\n\tres := coll.Distinct(ctx, fieldName, filter, opts)\n\tif err := res.Err(); err != nil {\n\t\treturn newErrorResult(err), nil\n\t}\n\n\tarr, err := res.Raw()\n\tif err != nil {\n\t\treturn newErrorResult(err), nil\n\t}\n\n\treturn newValueResult(bson.TypeArray, arr, nil), nil\n}\n\nfunc executeDropIndex(ctx context.Context, operation *operation) (*operationResult, error) {\n\tcoll, err := entities(ctx).collection(operation.Object)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar name string\n\tdropIndexOpts := options.DropIndexes()\n\n\telems, _ := operation.Arguments.Elements()\n\tfor _, elem := range elems {\n\t\tkey := elem.Key()\n\t\tval := elem.Value()\n\n\t\tswitch key {\n\t\tcase \"name\":\n\t\t\tname = val.StringValue()\n\t\tcase \"maxTimeMS\":\n\t\t\t// TODO(DRIVERS-2829): Error here instead of skip to ensure that if new\n\t\t\t// tests are added containing maxTimeMS (a legacy timeout option that we\n\t\t\t// have removed as of v2), then a CSOT analogue exists. Once we have\n\t\t\t// ensured an analogue exists, extend \"skippedTestDescriptions\" to avoid\n\t\t\t// this error.\n\t\t\treturn nil, fmt.Errorf(\"the maxTimeMS collection option is not supported\")\n\t\tcase \"rawData\":\n\t\t\terr = xoptions.SetInternalDropIndexesOptions(dropIndexOpts, key, val.Boolean())\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\tdefault:\n\t\t\treturn nil, fmt.Errorf(\"unrecognized dropIndex option %q\", key)\n\t\t}\n\t}\n\tif name == \"\" {\n\t\treturn nil, newMissingArgumentError(\"name\")\n\t}\n\n\terr = coll.Indexes().DropOne(ctx, name, dropIndexOpts)\n\treturn newDocumentResult(nil, err), nil\n}\n\nfunc executeDropIndexes(ctx context.Context, operation *operation) (*operationResult, error) {\n\tcoll, err := entities(ctx).collection(operation.Object)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tdropIndexOpts := options.DropIndexes()\n\telems, _ := operation.Arguments.Elements()\n\tfor _, elem := range elems {\n\t\tkey := elem.Key()\n\n\t\tswitch key {\n\t\tcase \"maxTimeMS\":\n\t\t\t// TODO(DRIVERS-2829): Error here instead of skip to ensure that if new\n\t\t\t// tests are added containing maxTimeMS (a legacy timeout option that we\n\t\t\t// have removed as of v2), then a CSOT analogue exists. Once we have\n\t\t\t// ensured an analogue exists, extend \"skippedTestDescriptions\" to avoid\n\t\t\t// this error.\n\t\t\treturn nil, fmt.Errorf(\"the maxTimeMS collection option is not supported\")\n\t\tdefault:\n\t\t\treturn nil, fmt.Errorf(\"unrecognized dropIndexes option %q\", key)\n\t\t}\n\t}\n\n\terr = coll.Indexes().DropAll(ctx, dropIndexOpts)\n\treturn newDocumentResult(nil, err), nil\n}\n\nfunc executeDropSearchIndex(ctx context.Context, operation *operation) (*operationResult, error) {\n\tcoll, err := entities(ctx).collection(operation.Object)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar name string\n\n\telems, err := operation.Arguments.Elements()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tfor _, elem := range elems {\n\t\tkey := elem.Key()\n\t\tval := elem.Value()\n\n\t\tswitch key {\n\t\tcase \"name\":\n\t\t\tname = val.StringValue()\n\t\tdefault:\n\t\t\treturn nil, fmt.Errorf(\"unrecognized dropSearchIndex option %q\", key)\n\t\t}\n\t}\n\n\terr = coll.SearchIndexes().DropOne(ctx, name)\n\treturn newValueResult(bson.TypeNull, nil, err), nil\n}\n\nfunc executeEstimatedDocumentCount(ctx context.Context, operation *operation) (*operationResult, error) {\n\tcoll, err := entities(ctx).collection(operation.Object)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\topts := options.EstimatedDocumentCount()\n\tvar elems []bson.RawElement\n\t// Some estimatedDocumentCount operations in the unified test format have no arguments.\n\tif operation.Arguments != nil {\n\t\telems, err = operation.Arguments.Elements()\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\tfor _, elem := range elems {\n\t\tkey := elem.Key()\n\t\tval := elem.Value()\n\n\t\tswitch key {\n\t\tcase \"comment\":\n\t\t\topts.SetComment(val)\n\t\tcase \"maxTimeMS\":\n\t\t\t// TODO(DRIVERS-2829): Error here instead of skip to ensure that if new\n\t\t\t// tests are added containing maxTimeMS (a legacy timeout option that we\n\t\t\t// have removed as of v2), then a CSOT analogue exists. Once we have\n\t\t\t// ensured an analogue exists, extend \"skippedTestDescriptions\" to avoid\n\t\t\t// this error.\n\t\t\treturn nil, fmt.Errorf(\"the maxTimeMS collection option is not supported\")\n\t\tcase \"rawData\":\n\t\t\terr = xoptions.SetInternalEstimatedDocumentCountOptions(opts, key, val.Boolean())\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\tdefault:\n\t\t\treturn nil, fmt.Errorf(\"unrecognized estimatedDocumentCount option %q\", key)\n\t\t}\n\t}\n\n\tcount, err := coll.EstimatedDocumentCount(ctx, opts)\n\tif err != nil {\n\t\treturn newErrorResult(err), nil\n\t}\n\treturn newValueResult(bson.TypeInt64, bsoncore.AppendInt64(nil, count), nil), nil\n}\n\nfunc executeCreateFindCursor(ctx context.Context, operation *operation) (*operationResult, error) {\n\tresult, err := createFindCursor(ctx, operation)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif result.err != nil {\n\t\treturn newErrorResult(result.err), nil\n\t}\n\n\tif operation.ResultEntityID == nil {\n\t\treturn nil, fmt.Errorf(\"no entity name provided to store executeCreateFindCursor result\")\n\t}\n\tif err := entities(ctx).addCursorEntity(*operation.ResultEntityID, result.cursor); err != nil {\n\t\treturn nil, fmt.Errorf(\"error storing result as cursor entity: %w\", err)\n\t}\n\treturn newEmptyResult(), nil\n}\n\nfunc executeFind(ctx context.Context, operation *operation) (*operationResult, error) {\n\tresult, err := createFindCursor(ctx, operation)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif result.err != nil {\n\t\treturn newErrorResult(result.err), nil\n\t}\n\n\tvar docs []bson.Raw\n\tif err := result.cursor.All(ctx, &docs); err != nil {\n\t\treturn newErrorResult(err), nil\n\t}\n\treturn newCursorResult(docs), nil\n}\n\nfunc executeFindOne(ctx context.Context, operation *operation) (*operationResult, error) {\n\tcoll, err := entities(ctx).collection(operation.Object)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar filter bson.Raw\n\topts := options.FindOne()\n\n\telems, _ := operation.Arguments.Elements()\n\tfor _, elem := range elems {\n\t\tkey := elem.Key()\n\t\tval := elem.Value()\n\n\t\tswitch key {\n\t\tcase \"collation\":\n\t\t\tcollation, err := createCollation(val.Document())\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"error creating collation: %w\", err)\n\t\t\t}\n\t\t\topts.SetCollation(collation)\n\t\tcase \"filter\":\n\t\t\tfilter = val.Document()\n\t\tcase \"hint\":\n\t\t\thint, err := createHint(val)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"error creating hint: %w\", err)\n\t\t\t}\n\t\t\topts.SetHint(hint)\n\t\tcase \"maxTimeMS\":\n\t\t\t// TODO(DRIVERS-2829): Error here instead of skip to ensure that if new\n\t\t\t// tests are added containing maxTimeMS (a legacy timeout option that we\n\t\t\t// have removed as of v2), then a CSOT analogue exists. Once we have\n\t\t\t// ensured an analogue exists, extend \"skippedTestDescriptions\" to avoid\n\t\t\t// this error.\n\t\t\treturn nil, fmt.Errorf(\"the maxTimeMS collection option is not supported\")\n\t\tcase \"projection\":\n\t\t\topts.SetProjection(val.Document())\n\t\tcase \"sort\":\n\t\t\topts.SetSort(val.Document())\n\t\tdefault:\n\t\t\treturn nil, fmt.Errorf(\"unrecognized findOne option %q\", key)\n\t\t}\n\t}\n\tif filter == nil {\n\t\treturn nil, newMissingArgumentError(\"filter\")\n\t}\n\n\tres, err := coll.FindOne(ctx, filter, opts).Raw()\n\t// Ignore ErrNoDocuments errors from Raw. In the event that the cursor\n\t// returned in a find operation has no associated documents, Raw will\n\t// return ErrNoDocuments.\n\tif errors.Is(err, mongo.ErrNoDocuments) {\n\t\terr = nil\n\t}\n\n\treturn newDocumentResult(res, err), nil\n}\n\nfunc executeFindOneAndDelete(ctx context.Context, operation *operation) (*operationResult, error) {\n\tcoll, err := entities(ctx).collection(operation.Object)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar filter bson.Raw\n\topts := options.FindOneAndDelete()\n\n\telems, err := operation.Arguments.Elements()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tfor _, elem := range elems {\n\t\tkey := elem.Key()\n\t\tval := elem.Value()\n\n\t\tswitch key {\n\t\tcase \"collation\":\n\t\t\tcollation, err := createCollation(val.Document())\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"error creating collation: %w\", err)\n\t\t\t}\n\t\t\topts.SetCollation(collation)\n\t\tcase \"comment\":\n\t\t\topts.SetComment(val)\n\t\tcase \"filter\":\n\t\t\tfilter = val.Document()\n\t\tcase \"hint\":\n\t\t\thint, err := createHint(val)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"error creating hint: %w\", err)\n\t\t\t}\n\t\t\topts.SetHint(hint)\n\t\tcase \"maxTimeMS\":\n\t\t\t// TODO(DRIVERS-2829): Error here instead of skip to ensure that if new\n\t\t\t// tests are added containing maxTimeMS (a legacy timeout option that we\n\t\t\t// have removed as of v2), then a CSOT analogue exists. Once we have\n\t\t\t// ensured an analogue exists, extend \"skippedTestDescriptions\" to avoid\n\t\t\t// this error.\n\t\t\treturn nil, fmt.Errorf(\"the maxTimeMS collection option is not supported\")\n\t\tcase \"projection\":\n\t\t\topts.SetProjection(val.Document())\n\t\tcase \"sort\":\n\t\t\topts.SetSort(val.Document())\n\t\tcase \"let\":\n\t\t\topts.SetLet(val.Document())\n\t\tcase \"rawData\":\n\t\t\terr = xoptions.SetInternalFindOneAndDeleteOptions(opts, key, val.Boolean())\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\tdefault:\n\t\t\treturn nil, fmt.Errorf(\"unrecognized findOneAndDelete option %q\", key)\n\t\t}\n\t}\n\tif filter == nil {\n\t\treturn nil, newMissingArgumentError(\"filter\")\n\t}\n\n\tres, err := coll.FindOneAndDelete(ctx, filter, opts).Raw()\n\t// Ignore ErrNoDocuments errors from Raw. In the event that the cursor\n\t// returned in a find operation has no associated documents, Raw will\n\t// return ErrNoDocuments.\n\tif errors.Is(err, mongo.ErrNoDocuments) {\n\t\terr = nil\n\t}\n\n\treturn newDocumentResult(res, err), nil\n}\n\nfunc executeFindOneAndReplace(ctx context.Context, operation *operation) (*operationResult, error) {\n\tcoll, err := entities(ctx).collection(operation.Object)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar filter bson.Raw\n\tvar replacement bson.Raw\n\topts := options.FindOneAndReplace()\n\n\telems, err := operation.Arguments.Elements()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tfor _, elem := range elems {\n\t\tkey := elem.Key()\n\t\tval := elem.Value()\n\n\t\tswitch key {\n\t\tcase \"bypassDocumentValidation\":\n\t\t\topts.SetBypassDocumentValidation(val.Boolean())\n\t\tcase \"collation\":\n\t\t\tcollation, err := createCollation(val.Document())\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"error creating collation: %w\", err)\n\t\t\t}\n\t\t\topts.SetCollation(collation)\n\t\tcase \"comment\":\n\t\t\topts.SetComment(val)\n\t\tcase \"filter\":\n\t\t\tfilter = val.Document()\n\t\tcase \"hint\":\n\t\t\thint, err := createHint(val)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"error creating hint: %w\", err)\n\t\t\t}\n\t\t\topts.SetHint(hint)\n\t\tcase \"let\":\n\t\t\topts.SetLet(val.Document())\n\t\tcase \"maxTimeMS\":\n\t\t\t// TODO(DRIVERS-2829): Error here instead of skip to ensure that if new\n\t\t\t// tests are added containing maxTimeMS (a legacy timeout option that we\n\t\t\t// have removed as of v2), then a CSOT analogue exists. Once we have\n\t\t\t// ensured an analogue exists, extend \"skippedTestDescriptions\" to avoid\n\t\t\t// this error.\n\t\t\treturn nil, fmt.Errorf(\"the maxTimeMS collection option is not supported\")\n\t\tcase \"projection\":\n\t\t\topts.SetProjection(val.Document())\n\t\tcase \"replacement\":\n\t\t\treplacement = val.Document()\n\t\tcase \"returnDocument\":\n\t\t\tswitch rd := val.StringValue(); rd {\n\t\t\tcase \"After\":\n\t\t\t\topts.SetReturnDocument(options.After)\n\t\t\tcase \"Before\":\n\t\t\t\topts.SetReturnDocument(options.Before)\n\t\t\tdefault:\n\t\t\t\treturn nil, fmt.Errorf(\"unrecognized returnDocument value %q\", rd)\n\t\t\t}\n\t\tcase \"sort\":\n\t\t\topts.SetSort(val.Document())\n\t\tcase \"upsert\":\n\t\t\topts.SetUpsert(val.Boolean())\n\t\tcase \"rawData\":\n\t\t\terr = xoptions.SetInternalFindOneAndReplaceOptions(opts, key, val.Boolean())\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\tdefault:\n\t\t\treturn nil, fmt.Errorf(\"unrecognized findOneAndReplace option %q\", key)\n\t\t}\n\t}\n\tif filter == nil {\n\t\treturn nil, newMissingArgumentError(\"filter\")\n\t}\n\tif replacement == nil {\n\t\treturn nil, newMissingArgumentError(\"replacement\")\n\t}\n\n\tres, err := coll.FindOneAndReplace(ctx, filter, replacement, opts).Raw()\n\t// Ignore ErrNoDocuments errors from Raw. In the event that the cursor\n\t// returned in a find operation has no associated documents, Raw will\n\t// return ErrNoDocuments.\n\tif errors.Is(err, mongo.ErrNoDocuments) {\n\t\terr = nil\n\t}\n\n\treturn newDocumentResult(res, err), nil\n}\n\nfunc executeFindOneAndUpdate(ctx context.Context, operation *operation) (*operationResult, error) {\n\tcoll, err := entities(ctx).collection(operation.Object)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar filter bson.Raw\n\tvar update any\n\topts := options.FindOneAndUpdate()\n\n\telems, err := operation.Arguments.Elements()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tfor _, elem := range elems {\n\t\tkey := elem.Key()\n\t\tval := elem.Value()\n\n\t\tswitch key {\n\t\tcase \"arrayFilters\":\n\t\t\topts.SetArrayFilters(\n\t\t\t\tbsonutil.RawToInterfaces(bsonutil.RawArrayToDocuments(val.Array())...),\n\t\t\t)\n\t\tcase \"bypassDocumentValidation\":\n\t\t\topts.SetBypassDocumentValidation(val.Boolean())\n\t\tcase \"collation\":\n\t\t\tcollation, err := createCollation(val.Document())\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"error creating collation: %w\", err)\n\t\t\t}\n\t\t\topts.SetCollation(collation)\n\t\tcase \"comment\":\n\t\t\topts.SetComment(val)\n\t\tcase \"filter\":\n\t\t\tfilter = val.Document()\n\t\tcase \"hint\":\n\t\t\thint, err := createHint(val)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"error creating hint: %w\", err)\n\t\t\t}\n\t\t\topts.SetHint(hint)\n\t\tcase \"let\":\n\t\t\topts.SetLet(val.Document())\n\t\tcase \"maxTimeMS\":\n\t\t\t// TODO(DRIVERS-2829): Error here instead of skip to ensure that if new\n\t\t\t// tests are added containing maxTimeMS (a legacy timeout option that we\n\t\t\t// have removed as of v2), then a CSOT analogue exists. Once we have\n\t\t\t// ensured an analogue exists, extend \"skippedTestDescriptions\" to avoid\n\t\t\t// this error.\n\t\t\treturn nil, fmt.Errorf(\"the maxTimeMS collection option is not supported\")\n\t\tcase \"projection\":\n\t\t\topts.SetProjection(val.Document())\n\t\tcase \"returnDocument\":\n\t\t\tswitch rd := val.StringValue(); rd {\n\t\t\tcase \"After\":\n\t\t\t\topts.SetReturnDocument(options.After)\n\t\t\tcase \"Before\":\n\t\t\t\topts.SetReturnDocument(options.Before)\n\t\t\tdefault:\n\t\t\t\treturn nil, fmt.Errorf(\"unrecognized returnDocument value %q\", rd)\n\t\t\t}\n\t\tcase \"sort\":\n\t\t\topts.SetSort(val.Document())\n\t\tcase \"update\":\n\t\t\tupdate, err = createUpdateValue(val)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"error processing update value: %q\", err)\n\t\t\t}\n\t\tcase \"upsert\":\n\t\t\topts.SetUpsert(val.Boolean())\n\t\tcase \"rawData\":\n\t\t\terr = xoptions.SetInternalFindOneAndUpdateOptions(opts, key, val.Boolean())\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\tdefault:\n\t\t\treturn nil, fmt.Errorf(\"unrecognized findOneAndUpdate option %q\", key)\n\t\t}\n\t}\n\tif filter == nil {\n\t\treturn nil, newMissingArgumentError(\"filter\")\n\t}\n\tif update == nil {\n\t\treturn nil, newMissingArgumentError(\"update\")\n\t}\n\n\tres, err := coll.FindOneAndUpdate(ctx, filter, update, opts).Raw()\n\t// Ignore ErrNoDocuments errors from Raw. In the event that the cursor\n\t// returned in a find operation has no associated documents, Raw will\n\t// return ErrNoDocuments.\n\tif errors.Is(err, mongo.ErrNoDocuments) {\n\t\terr = nil\n\t}\n\n\treturn newDocumentResult(res, err), nil\n}\n\nfunc executeInsertMany(ctx context.Context, operation *operation) (*operationResult, error) {\n\tcoll, err := entities(ctx).collection(operation.Object)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar documents []any\n\topts := options.InsertMany()\n\n\telems, err := operation.Arguments.Elements()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tfor _, elem := range elems {\n\t\tkey := elem.Key()\n\t\tval := elem.Value()\n\n\t\tswitch key {\n\t\tcase \"comment\":\n\t\t\topts.SetComment(val)\n\t\tcase \"documents\":\n\t\t\tdocuments = bsonutil.RawToInterfaces(bsonutil.RawArrayToDocuments(val.Array())...)\n\t\tcase \"ordered\":\n\t\t\topts.SetOrdered(val.Boolean())\n\t\tcase \"rawData\":\n\t\t\terr = xoptions.SetInternalInsertManyOptions(opts, key, val.Boolean())\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\tdefault:\n\t\t\treturn nil, fmt.Errorf(\"unrecognized insertMany option %q\", key)\n\t\t}\n\t}\n\tif documents == nil {\n\t\treturn nil, newMissingArgumentError(\"documents\")\n\t}\n\n\tres, err := coll.InsertMany(ctx, documents, opts)\n\traw := emptyCoreDocument\n\tif res != nil {\n\t\t// We return InsertedIDs as []any but the CRUD spec documents it as a map[int64]any, so\n\t\t// comparisons will fail if we include it in the result document. This is marked as an optional field and is\n\t\t// always surrounded in an $$unsetOrMatches assertion, so we leave it out of the document.\n\t\traw = bsoncore.NewDocumentBuilder().\n\t\t\tAppendInt32(\"insertedCount\", int32(len(res.InsertedIDs))).\n\t\t\tAppendInt32(\"deletedCount\", 0).\n\t\t\tAppendInt32(\"matchedCount\", 0).\n\t\t\tAppendInt32(\"modifiedCount\", 0).\n\t\t\tAppendInt32(\"upsertedCount\", 0).\n\t\t\tAppendDocument(\"upsertedIds\", bsoncore.NewDocumentBuilder().Build()).\n\t\t\tBuild()\n\t}\n\treturn newDocumentResult(raw, err), nil\n}\n\nfunc executeInsertOne(ctx context.Context, operation *operation) (*operationResult, error) {\n\tcoll, err := entities(ctx).collection(operation.Object)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar document bson.Raw\n\topts := options.InsertOne()\n\n\telems, err := operation.Arguments.Elements()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tfor _, elem := range elems {\n\t\tkey := elem.Key()\n\t\tval := elem.Value()\n\n\t\tswitch key {\n\t\tcase \"document\":\n\t\t\tdocument = val.Document()\n\t\tcase \"bypassDocumentValidation\":\n\t\t\topts.SetBypassDocumentValidation(val.Boolean())\n\t\tcase \"comment\":\n\t\t\topts.SetComment(val)\n\t\tcase \"rawData\":\n\t\t\terr = xoptions.SetInternalInsertOneOptions(opts, key, val.Boolean())\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\tdefault:\n\t\t\treturn nil, fmt.Errorf(\"unrecognized insertOne option %q\", key)\n\t\t}\n\t}\n\tif document == nil {\n\t\treturn nil, newMissingArgumentError(\"documents\")\n\t}\n\n\tres, err := coll.InsertOne(ctx, document, opts)\n\traw := emptyCoreDocument\n\tif res != nil {\n\t\tt, data, err := bson.MarshalValue(res.InsertedID)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"error converting InsertedID field to BSON: %w\", err)\n\t\t}\n\t\traw = bsoncore.NewDocumentBuilder().\n\t\t\tAppendValue(\"insertedId\", bsoncore.Value{Type: bsoncore.Type(t), Data: data}).\n\t\t\tBuild()\n\t}\n\treturn newDocumentResult(raw, err), nil\n}\n\nfunc executeListIndexes(ctx context.Context, operation *operation) (*operationResult, error) {\n\tcoll, err := entities(ctx).collection(operation.Object)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar elems []bson.RawElement\n\t// Some listIndexes operations in the unified test format have no arguments.\n\tif operation.Arguments != nil {\n\t\telems, err = operation.Arguments.Elements()\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\topts := options.ListIndexes()\n\tfor _, elem := range elems {\n\t\tkey := elem.Key()\n\t\tval := elem.Value()\n\n\t\tswitch key {\n\t\tcase \"batchSize\":\n\t\t\topts.SetBatchSize(val.Int32())\n\t\tcase \"rawData\":\n\t\t\terr = xoptions.SetInternalListIndexesOptions(opts, key, val.Boolean())\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\tdefault:\n\t\t\treturn nil, fmt.Errorf(\"unrecognized listIndexes option: %q\", key)\n\t\t}\n\t}\n\n\tcursor, err := coll.Indexes().List(ctx, opts)\n\tif err != nil {\n\t\treturn newErrorResult(err), nil\n\t}\n\n\tvar docs []bson.Raw\n\tif err := cursor.All(ctx, &docs); err != nil {\n\t\treturn newErrorResult(err), nil\n\t}\n\treturn newCursorResult(docs), nil\n}\n\nfunc executeListSearchIndexes(ctx context.Context, operation *operation) (*operationResult, error) {\n\tcoll, err := entities(ctx).collection(operation.Object)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tsearchIdxOpts := options.SearchIndexes()\n\tvar opts []options.Lister[options.ListSearchIndexesOptions]\n\n\telems, err := operation.Arguments.Elements()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tfor _, elem := range elems {\n\t\tkey := elem.Key()\n\t\tval := elem.Value()\n\n\t\tswitch key {\n\t\tcase \"name\":\n\t\t\tsearchIdxOpts.SetName(val.StringValue())\n\t\tcase \"aggregationOptions\":\n\t\t\t// Unmarshal the document into the AggregateOptions embedded object.\n\t\t\tlsiOpts := &options.ListSearchIndexesOptions{}\n\t\t\tlsiOptsCallback := func(args *options.ListSearchIndexesOptions) error {\n\t\t\t\targs.AggregateOptions = &options.AggregateOptions{}\n\n\t\t\t\treturn bson.Unmarshal(val.Document(), args.AggregateOptions)\n\t\t\t}\n\n\t\t\topts = append(opts, mongoutil.NewOptionsLister(lsiOpts, lsiOptsCallback))\n\t\tdefault:\n\t\t\treturn nil, fmt.Errorf(\"unrecognized listSearchIndexes option %q\", key)\n\t\t}\n\t}\n\n\taggregateOpts := make([]options.Lister[options.ListSearchIndexesOptions], len(opts))\n\tcopy(aggregateOpts, opts)\n\n\t_, err = coll.SearchIndexes().List(ctx, searchIdxOpts, aggregateOpts...)\n\treturn newValueResult(bson.TypeNull, nil, err), nil\n}\n\nfunc executeRenameCollection(ctx context.Context, operation *operation) (*operationResult, error) {\n\tcoll, err := entities(ctx).collection(operation.Object)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar toName string\n\tvar dropTarget bool\n\telems, err := operation.Arguments.Elements()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tfor _, elem := range elems {\n\t\tkey := elem.Key()\n\t\tval := elem.Value()\n\n\t\tswitch key {\n\t\tcase \"dropTarget\":\n\t\t\tdropTarget = val.Boolean()\n\t\tcase \"to\":\n\t\t\ttoName = val.StringValue()\n\t\tdefault:\n\t\t\treturn nil, fmt.Errorf(\"unrecognized rename option %q\", key)\n\t\t}\n\t}\n\tif toName == \"\" {\n\t\treturn nil, newMissingArgumentError(\"to\")\n\t}\n\n\trenameCmd := bson.D{\n\t\t{\"renameCollection\", coll.Database().Name() + \".\" + coll.Name()},\n\t\t{\"to\", coll.Database().Name() + \".\" + toName},\n\t}\n\tif dropTarget {\n\t\trenameCmd = append(renameCmd, bson.E{\"dropTarget\", dropTarget})\n\t}\n\t// rename can only be run on the 'admin' database.\n\tadmin := coll.Database().Client().Database(\"admin\")\n\tres, err := admin.RunCommand(context.Background(), renameCmd).Raw()\n\treturn newDocumentResult(res, err), nil\n}\n\nfunc executeReplaceOne(ctx context.Context, operation *operation) (*operationResult, error) {\n\tcoll, err := entities(ctx).collection(operation.Object)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tfilter := emptyDocument\n\treplacement := emptyDocument\n\topts := options.Replace()\n\n\telems, err := operation.Arguments.Elements()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tfor _, elem := range elems {\n\t\tkey := elem.Key()\n\t\tval := elem.Value()\n\n\t\tswitch key {\n\t\tcase \"bypassDocumentValidation\":\n\t\t\topts.SetBypassDocumentValidation(val.Boolean())\n\t\tcase \"collation\":\n\t\t\tcollation, err := createCollation(val.Document())\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"error creating collation: %w\", err)\n\t\t\t}\n\t\t\topts.SetCollation(collation)\n\t\tcase \"comment\":\n\t\t\topts.SetComment(val)\n\t\tcase \"filter\":\n\t\t\tfilter = val.Document()\n\t\tcase \"hint\":\n\t\t\thint, err := createHint(val)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"error creating hint: %w\", err)\n\t\t\t}\n\t\t\topts.SetHint(hint)\n\t\tcase \"sort\":\n\t\t\topts.SetSort(val.Document())\n\t\tcase \"replacement\":\n\t\t\treplacement = val.Document()\n\t\tcase \"upsert\":\n\t\t\topts.SetUpsert(val.Boolean())\n\t\tcase \"let\":\n\t\t\topts.SetLet(val.Document())\n\t\tcase \"rawData\":\n\t\t\terr = xoptions.SetInternalReplaceOptions(opts, key, val.Boolean())\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\tdefault:\n\t\t\treturn nil, fmt.Errorf(\"unrecognized replaceOne option %q\", key)\n\t\t}\n\t}\n\n\tres, err := coll.ReplaceOne(ctx, filter, replacement, opts)\n\traw, buildErr := buildUpdateResultDocument(res)\n\tif buildErr != nil {\n\t\treturn nil, buildErr\n\t}\n\treturn newDocumentResult(raw, err), nil\n}\n\nfunc executeUpdateOne(ctx context.Context, operation *operation) (*operationResult, error) {\n\tcoll, err := entities(ctx).collection(operation.Object)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tupdateArgs, updateOpts, err := createUpdateOneArguments(operation.Arguments)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tres, err := coll.UpdateOne(ctx, updateArgs.filter, updateArgs.update, updateOpts)\n\traw, buildErr := buildUpdateResultDocument(res)\n\tif buildErr != nil {\n\t\treturn nil, buildErr\n\t}\n\treturn newDocumentResult(raw, err), nil\n}\n\nfunc executeUpdateMany(ctx context.Context, operation *operation) (*operationResult, error) {\n\tcoll, err := entities(ctx).collection(operation.Object)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tupdateArgs, updateOpts, err := createUpdateManyArguments(operation.Arguments)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tres, err := coll.UpdateMany(ctx, updateArgs.filter, updateArgs.update, updateOpts)\n\traw, buildErr := buildUpdateResultDocument(res)\n\tif buildErr != nil {\n\t\treturn nil, buildErr\n\t}\n\treturn newDocumentResult(raw, err), nil\n}\n\nfunc executeUpdateSearchIndex(ctx context.Context, operation *operation) (*operationResult, error) {\n\tcoll, err := entities(ctx).collection(operation.Object)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar name string\n\tvar definition any\n\n\telems, err := operation.Arguments.Elements()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tfor _, elem := range elems {\n\t\tkey := elem.Key()\n\t\tval := elem.Value()\n\n\t\tswitch key {\n\t\tcase \"name\":\n\t\t\tname = val.StringValue()\n\t\tcase \"definition\":\n\t\t\terr = bson.Unmarshal(val.Value, &definition)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\tdefault:\n\t\t\treturn nil, fmt.Errorf(\"unrecognized updateSearchIndex option %q\", key)\n\t\t}\n\t}\n\n\terr = coll.SearchIndexes().UpdateOne(ctx, name, definition)\n\treturn newValueResult(bson.TypeNull, nil, err), nil\n}\n\nfunc buildUpdateResultDocument(res *mongo.UpdateResult) (bsoncore.Document, error) {\n\tif res == nil {\n\t\treturn emptyCoreDocument, nil\n\t}\n\n\tbuilder := bsoncore.NewDocumentBuilder().\n\t\tAppendInt64(\"matchedCount\", res.MatchedCount).\n\t\tAppendInt64(\"modifiedCount\", res.ModifiedCount).\n\t\tAppendInt64(\"upsertedCount\", res.UpsertedCount)\n\n\tif res.UpsertedID != nil {\n\t\tt, data, err := bson.MarshalValue(res.UpsertedID)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"error converting UpsertedID to BSON: %w\", err)\n\t\t}\n\t\tbuilder.AppendValue(\"upsertedId\", bsoncore.Value{Type: bsoncore.Type(t), Data: data})\n\t}\n\treturn builder.Build(), nil\n}\n\ntype cursorResult struct {\n\tcursor *mongo.Cursor\n\terr    error\n}\n\nfunc createFindCursor(ctx context.Context, operation *operation) (*cursorResult, error) {\n\tcoll, err := entities(ctx).collection(operation.Object)\n\t// Find operations can also be run against GridFS buckets. Check for a bucket entity of the\n\t// same name and run createBucketFindCursor if an entity is found.\n\tif err != nil {\n\t\tif _, bucketOk := entities(ctx).gridFSBucket(operation.Object); bucketOk == nil {\n\t\t\treturn createBucketFindCursor(ctx, operation)\n\t\t}\n\t\treturn nil, err\n\t}\n\n\tvar filter bson.Raw\n\topts := options.Find()\n\n\telems, err := operation.Arguments.Elements()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tfor _, elem := range elems {\n\t\tkey := elem.Key()\n\t\tval := elem.Value()\n\n\t\tswitch key {\n\t\tcase \"allowDiskUse\":\n\t\t\topts.SetAllowDiskUse(val.Boolean())\n\t\tcase \"allowPartialResults\":\n\t\t\topts.SetAllowPartialResults(val.Boolean())\n\t\tcase \"batchSize\":\n\t\t\topts.SetBatchSize(val.Int32())\n\t\tcase \"collation\":\n\t\t\tcollation, err := createCollation(val.Document())\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"error creating collation: %w\", err)\n\t\t\t}\n\t\t\topts.SetCollation(collation)\n\t\tcase \"comment\":\n\t\t\topts.SetComment(val)\n\t\tcase \"filter\":\n\t\t\tfilter = val.Document()\n\t\tcase \"hint\":\n\t\t\thint, err := createHint(val)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"error creating hint: %w\", err)\n\t\t\t}\n\t\t\topts.SetHint(hint)\n\t\tcase \"let\":\n\t\t\topts.SetLet(val.Document())\n\t\tcase \"limit\":\n\t\t\topts.SetLimit(int64(val.Int32()))\n\t\tcase \"max\":\n\t\t\topts.SetMax(val.Document())\n\t\tcase \"maxTimeMS\":\n\t\t\t// TODO(DRIVERS-2829): Error here instead of skip to ensure that if new\n\t\t\t// tests are added containing maxTimeMS (a legacy timeout option that we\n\t\t\t// have removed as of v2), then a CSOT analogue exists. Once we have\n\t\t\t// ensured an analogue exists, extend \"skippedTestDescriptions\" to avoid\n\t\t\t// this error.\n\t\t\treturn nil, fmt.Errorf(\"the maxTimeMS collection option is not supported\")\n\t\tcase \"min\":\n\t\t\topts.SetMin(val.Document())\n\t\tcase \"noCursorTimeout\":\n\t\t\topts.SetNoCursorTimeout(val.Boolean())\n\t\tcase \"oplogReplay\":\n\t\t\topts.SetOplogReplay(val.Boolean())\n\t\tcase \"projection\":\n\t\t\topts.SetProjection(val.Document())\n\t\tcase \"returnKey\":\n\t\t\topts.SetReturnKey(val.Boolean())\n\t\tcase \"showRecordId\":\n\t\t\topts.SetShowRecordID(val.Boolean())\n\t\tcase \"skip\":\n\t\t\topts.SetSkip(int64(val.Int32()))\n\t\tcase \"sort\":\n\t\t\topts.SetSort(val.Document())\n\t\tcase \"timeoutMode\":\n\t\t\treturn nil, newSkipTestError(\"timeoutMode is not supported\")\n\t\tcase \"cursorType\":\n\t\t\tswitch strings.ToLower(val.StringValue()) {\n\t\t\tcase \"tailable\":\n\t\t\t\topts.SetCursorType(options.Tailable)\n\t\t\tcase \"tailableawait\":\n\t\t\t\topts.SetCursorType(options.TailableAwait)\n\t\t\tcase \"nontailable\":\n\t\t\t\topts.SetCursorType(options.NonTailable)\n\t\t\t}\n\t\tcase \"maxAwaitTimeMS\":\n\t\t\tmaxAwaitTime := time.Duration(val.Int32()) * time.Millisecond\n\t\t\topts.SetMaxAwaitTime(maxAwaitTime)\n\t\tcase \"rawData\":\n\t\t\terr = xoptions.SetInternalFindOptions(opts, key, val.Boolean())\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\tdefault:\n\t\t\treturn nil, fmt.Errorf(\"unrecognized find option %q\", key)\n\t\t}\n\t}\n\tif filter == nil {\n\t\treturn nil, newMissingArgumentError(\"filter\")\n\t}\n\n\tcursor, err := coll.Find(ctx, filter, opts)\n\tres := &cursorResult{\n\t\tcursor: cursor,\n\t\terr:    err,\n\t}\n\treturn res, nil\n}\n"
  },
  {
    "path": "internal/integration/unified/common_options.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage unified\n\nimport (\n\t\"fmt\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/readconcern\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/readpref\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/writeconcern\"\n\t\"go.mongodb.org/mongo-driver/v2/tag\"\n)\n\n// This file defines helper types to convert BSON documents to ReadConcern, WriteConcern, and ReadPref objects.\n\ntype readConcern struct {\n\tLevel string `bson:\"level\"`\n}\n\nfunc (rc *readConcern) toReadConcernOption() *readconcern.ReadConcern {\n\treturn &readconcern.ReadConcern{Level: rc.Level}\n}\n\ntype writeConcern struct {\n\tJournal *bool `bson:\"journal\"`\n\tW       any   `bson:\"w\"`\n}\n\nfunc (wc *writeConcern) toWriteConcernOption() (*writeconcern.WriteConcern, error) {\n\tc := &writeconcern.WriteConcern{}\n\tif wc.Journal != nil {\n\t\tc.Journal = wc.Journal\n\t}\n\tif wc.W != nil {\n\t\tswitch converted := wc.W.(type) {\n\t\tcase string:\n\t\t\tif converted != \"majority\" {\n\t\t\t\treturn nil, fmt.Errorf(\"invalid write concern 'w' string value %q\", converted)\n\t\t\t}\n\t\t\tc.W = \"majority\"\n\t\tcase int32:\n\t\t\tc.W = int(converted)\n\t\tdefault:\n\t\t\treturn nil, fmt.Errorf(\"invalid type for write concern 'w' field %T\", wc.W)\n\t\t}\n\t}\n\n\treturn c, nil\n}\n\n// ReadPreference is a representation of BSON readPreference objects in tests.\ntype ReadPreference struct {\n\tMode                string              `bson:\"mode\"`\n\tTagSets             []map[string]string `bson:\"tagSets\"`\n\tMaxStalenessSeconds *int64              `bson:\"maxStalenessSeconds\"`\n\tHedge               bson.M              `bson:\"hedge\"`\n}\n\n// ToReadPrefOption converts a ReadPreference into a readpref.ReadPref object and will\n// error if the original ReadPreference is malformed.\nfunc (rp *ReadPreference) ToReadPrefOption() (*readpref.ReadPref, error) {\n\tmode, err := readpref.ModeFromString(rp.Mode)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"invalid read preference mode %q\", rp.Mode)\n\t}\n\n\tvar rpOptions []readpref.Option\n\tif rp.TagSets != nil {\n\t\t// Each item in the TagSets slice is a document that represents one set.\n\t\tsets := make([]tag.Set, 0, len(rp.TagSets))\n\t\tfor _, rawSet := range rp.TagSets {\n\t\t\tparsed := make(tag.Set, 0, len(rawSet))\n\t\t\tfor k, v := range rawSet {\n\t\t\t\tparsed = append(parsed, tag.Tag{Name: k, Value: v})\n\t\t\t}\n\t\t\tsets = append(sets, parsed)\n\t\t}\n\n\t\trpOptions = append(rpOptions, readpref.WithTagSets(sets...))\n\t}\n\tif rp.MaxStalenessSeconds != nil {\n\t\tmaxStaleness := time.Duration(*rp.MaxStalenessSeconds) * time.Second\n\t\trpOptions = append(rpOptions, readpref.WithMaxStaleness(maxStaleness))\n\t}\n\tif rp.Hedge != nil {\n\t\tif len(rp.Hedge) > 1 {\n\t\t\treturn nil, fmt.Errorf(\"invalid read preference hedge document: length cannot be greater than 1\")\n\t\t}\n\t\tif enabled, ok := rp.Hedge[\"enabled\"]; ok {\n\t\t\trpOptions = append(rpOptions, readpref.WithHedgeEnabled(enabled.(bool)))\n\t\t}\n\t}\n\n\treturn readpref.New(mode, rpOptions...)\n}\n"
  },
  {
    "path": "internal/integration/unified/context.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage unified\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"go.mongodb.org/mongo-driver/v2/mongo\"\n)\n\n// ctxKey is used to define keys for values stored in context.Context objects.\ntype ctxKey string\n\nconst (\n\t// operationalFailPoint indicates that the test case contains an\n\t// operation that sets a failpoint. This information is useful in\n\t// determining if a client entity (file or operational level) should\n\t// specify a reduced value for heartbeatFrequencyMS.\n\toperationalFailPointKey ctxKey = \"operational-fail-point\"\n\t// entitiesKey is used to store an entityMap instance in a Context.\n\tentitiesKey ctxKey = \"test-entities\"\n\t// failPointsKey is used to store a map from a fail point name to the Client instance used to configure it.\n\tfailPointsKey ctxKey = \"test-failpoints\"\n\t// targetedFailPointsKey is used to store a map from a fail point name to the host on which the fail point is set.\n\ttargetedFailPointsKey ctxKey = \"test-targeted-failpoints\"\n\tclientLogMessagesKey  ctxKey = \"test-expected-log-message-count\"\n\tignoreLogMessagesKey  ctxKey = \"test-ignore-log-message-count\"\n)\n\n// newTestContext creates a new Context derived from ctx with values initialized to store the state required for test\n// execution.\nfunc newTestContext(\n\tctx context.Context,\n\tentityMap *EntityMap,\n\tclientLogMessages []*clientLogMessages,\n\thasOperationalFailPoint bool,\n) context.Context {\n\tctx = context.WithValue(ctx, operationalFailPointKey, hasOperationalFailPoint)\n\tctx = context.WithValue(ctx, entitiesKey, entityMap)\n\tctx = context.WithValue(ctx, failPointsKey, make(map[string]*mongo.Client))\n\tctx = context.WithValue(ctx, targetedFailPointsKey, make(map[string]string))\n\tctx = context.WithValue(ctx, clientLogMessagesKey, clientLogMessages)\n\treturn ctx\n}\n\nfunc addFailPoint(ctx context.Context, failPoint string, client *mongo.Client) {\n\tfailPoints := ctx.Value(failPointsKey).(map[string]*mongo.Client)\n\tfailPoints[failPoint] = client\n}\n\nfunc addTargetedFailPoint(ctx context.Context, failPoint string, host string) error {\n\tfailPoints := ctx.Value(targetedFailPointsKey).(map[string]string)\n\tif _, ok := failPoints[failPoint]; ok {\n\t\treturn fmt.Errorf(\"fail point %q already exists in tracked targeted fail points map\", failPoint)\n\t}\n\n\tfailPoints[failPoint] = host\n\treturn nil\n}\n\nfunc failPoints(ctx context.Context) map[string]*mongo.Client {\n\treturn ctx.Value(failPointsKey).(map[string]*mongo.Client)\n}\n\nfunc hasOperationalFailpoint(ctx context.Context) bool {\n\treturn ctx.Value(operationalFailPointKey).(bool)\n}\n\nfunc targetedFailPoints(ctx context.Context) map[string]string {\n\treturn ctx.Value(targetedFailPointsKey).(map[string]string)\n}\n\nfunc entities(ctx context.Context) *EntityMap {\n\treturn ctx.Value(entitiesKey).(*EntityMap)\n}\n\nfunc expectedLogMessagesCount(ctx context.Context, clientID string) int {\n\tmessages := ctx.Value(clientLogMessagesKey).([]*clientLogMessages)\n\n\tcount := 0\n\tfor _, message := range messages {\n\t\tif message.Client == clientID {\n\t\t\tcount += len(message.LogMessages)\n\t\t}\n\t}\n\n\treturn count\n}\n\nfunc ignoreLogMessages(ctx context.Context, clientID string) []*logMessage {\n\tmessages := ctx.Value(clientLogMessagesKey).([]*clientLogMessages)\n\n\tignoreMessages := []*logMessage{}\n\tfor _, message := range messages {\n\t\tif message.Client == clientID {\n\t\t\tignoreMessages = append(ignoreMessages, message.IgnoreMessages...)\n\t\t}\n\t}\n\n\treturn ignoreMessages\n}\n"
  },
  {
    "path": "internal/integration/unified/crud_helpers.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage unified\n\nimport (\n\t\"fmt\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/bsonutil\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/xoptions\"\n)\n\n// newMissingArgumentError creates an error to convey that an argument that is required to run an operation is missing\n// from the operation's arguments document.\nfunc newMissingArgumentError(arg string) error {\n\treturn fmt.Errorf(\"operation arguments document is missing required field %q\", arg)\n}\n\ntype updateArguments struct {\n\tfilter bson.Raw\n\tupdate any\n}\n\nfunc createUpdateManyArguments(args bson.Raw) (*updateArguments, *options.UpdateManyOptionsBuilder, error) {\n\tua := &updateArguments{}\n\topts := options.UpdateMany()\n\n\telems, _ := args.Elements()\n\tfor _, elem := range elems {\n\t\tkey := elem.Key()\n\t\tval := elem.Value()\n\n\t\tswitch key {\n\t\tcase \"arrayFilters\":\n\t\t\topts.SetArrayFilters(\n\t\t\t\tbsonutil.RawToInterfaces(bsonutil.RawArrayToDocuments(val.Array())...),\n\t\t\t)\n\t\tcase \"bypassDocumentValidation\":\n\t\t\topts.SetBypassDocumentValidation(val.Boolean())\n\t\tcase \"collation\":\n\t\t\tcollation, err := createCollation(val.Document())\n\t\t\tif err != nil {\n\t\t\t\treturn nil, nil, fmt.Errorf(\"error creating collation: %w\", err)\n\t\t\t}\n\t\t\topts.SetCollation(collation)\n\t\tcase \"comment\":\n\t\t\topts.SetComment(val)\n\t\tcase \"filter\":\n\t\t\tua.filter = val.Document()\n\t\tcase \"hint\":\n\t\t\thint, err := createHint(val)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, nil, fmt.Errorf(\"error creating hint: %w\", err)\n\t\t\t}\n\t\t\topts.SetHint(hint)\n\t\tcase \"let\":\n\t\t\topts.SetLet(val.Document())\n\t\tcase \"update\":\n\t\t\tvar err error\n\t\t\tua.update, err = createUpdateValue(val)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, nil, fmt.Errorf(\"error processing update value: %w\", err)\n\t\t\t}\n\t\tcase \"upsert\":\n\t\t\topts.SetUpsert(val.Boolean())\n\t\tcase \"rawData\":\n\t\t\terr := xoptions.SetInternalUpdateManyOptions(opts, key, val.Boolean())\n\t\t\tif err != nil {\n\t\t\t\treturn nil, nil, err\n\t\t\t}\n\t\tdefault:\n\t\t\treturn nil, nil, fmt.Errorf(\"unrecognized update option %q\", key)\n\t\t}\n\t}\n\tif ua.filter == nil {\n\t\treturn nil, nil, newMissingArgumentError(\"filter\")\n\t}\n\tif ua.update == nil {\n\t\treturn nil, nil, newMissingArgumentError(\"update\")\n\t}\n\n\treturn ua, opts, nil\n}\n\nfunc createUpdateOneArguments(args bson.Raw) (*updateArguments, *options.UpdateOneOptionsBuilder, error) {\n\tua := &updateArguments{}\n\topts := options.UpdateOne()\n\n\telems, _ := args.Elements()\n\tfor _, elem := range elems {\n\t\tkey := elem.Key()\n\t\tval := elem.Value()\n\n\t\tswitch key {\n\t\tcase \"arrayFilters\":\n\t\t\topts.SetArrayFilters(\n\t\t\t\tbsonutil.RawToInterfaces(bsonutil.RawArrayToDocuments(val.Array())...),\n\t\t\t)\n\t\tcase \"bypassDocumentValidation\":\n\t\t\topts.SetBypassDocumentValidation(val.Boolean())\n\t\tcase \"collation\":\n\t\t\tcollation, err := createCollation(val.Document())\n\t\t\tif err != nil {\n\t\t\t\treturn nil, nil, fmt.Errorf(\"error creating collation: %w\", err)\n\t\t\t}\n\t\t\topts.SetCollation(collation)\n\t\tcase \"comment\":\n\t\t\topts.SetComment(val)\n\t\tcase \"filter\":\n\t\t\tua.filter = val.Document()\n\t\tcase \"hint\":\n\t\t\thint, err := createHint(val)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, nil, fmt.Errorf(\"error creating hint: %w\", err)\n\t\t\t}\n\t\t\topts.SetHint(hint)\n\t\tcase \"let\":\n\t\t\topts.SetLet(val.Document())\n\t\tcase \"update\":\n\t\t\tvar err error\n\t\t\tua.update, err = createUpdateValue(val)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, nil, fmt.Errorf(\"error processing update value: %w\", err)\n\t\t\t}\n\t\tcase \"upsert\":\n\t\t\topts.SetUpsert(val.Boolean())\n\t\tcase \"sort\":\n\t\t\topts.SetSort(val.Document())\n\t\tcase \"rawData\":\n\t\t\terr := xoptions.SetInternalUpdateOneOptions(opts, key, val.Boolean())\n\t\t\tif err != nil {\n\t\t\t\treturn nil, nil, err\n\t\t\t}\n\t\tdefault:\n\t\t\treturn nil, nil, fmt.Errorf(\"unrecognized update option %q\", key)\n\t\t}\n\t}\n\tif ua.filter == nil {\n\t\treturn nil, nil, newMissingArgumentError(\"filter\")\n\t}\n\tif ua.update == nil {\n\t\treturn nil, nil, newMissingArgumentError(\"update\")\n\t}\n\n\treturn ua, opts, nil\n}\n\ntype listCollectionsArguments struct {\n\tfilter bson.Raw\n\topts   *options.ListCollectionsOptionsBuilder\n}\n\nfunc createListCollectionsArguments(args bson.Raw) (*listCollectionsArguments, error) {\n\tlca := &listCollectionsArguments{\n\t\topts: options.ListCollections(),\n\t}\n\n\tlca.filter = emptyDocument\n\telems, _ := args.Elements()\n\tfor _, elem := range elems {\n\t\tkey := elem.Key()\n\t\tval := elem.Value()\n\n\t\tswitch key {\n\t\tcase \"batchSize\":\n\t\t\tlca.opts.SetBatchSize(val.Int32())\n\t\tcase \"filter\":\n\t\t\tlca.filter = val.Document()\n\t\tcase \"nameOnly\":\n\t\t\tlca.opts.SetNameOnly(val.Boolean())\n\t\tcase \"rawData\":\n\t\t\terr := xoptions.SetInternalListCollectionsOptions(lca.opts, key, val.Boolean())\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\tdefault:\n\t\t\treturn nil, fmt.Errorf(\"unrecognized listCollections option %q\", key)\n\t\t}\n\t}\n\n\treturn lca, nil\n}\n\nfunc createCollation(args bson.Raw) (*options.Collation, error) {\n\tvar collation options.Collation\n\telems, _ := args.Elements()\n\n\tfor _, elem := range elems {\n\t\tswitch elem.Key() {\n\t\tcase \"locale\":\n\t\t\tcollation.Locale = elem.Value().StringValue()\n\t\tcase \"caseLevel\":\n\t\t\tcollation.CaseLevel = elem.Value().Boolean()\n\t\tcase \"caseFirst\":\n\t\t\tcollation.CaseFirst = elem.Value().StringValue()\n\t\tcase \"strength\":\n\t\t\tcollation.Strength = int(elem.Value().Int32())\n\t\tcase \"numericOrdering\":\n\t\t\tcollation.NumericOrdering = elem.Value().Boolean()\n\t\tcase \"alternate\":\n\t\t\tcollation.Alternate = elem.Value().StringValue()\n\t\tcase \"maxVariable\":\n\t\t\tcollation.MaxVariable = elem.Value().StringValue()\n\t\tcase \"normalization\":\n\t\t\tcollation.Normalization = elem.Value().Boolean()\n\t\tcase \"backwards\":\n\t\t\tcollation.Backwards = elem.Value().Boolean()\n\t\tdefault:\n\t\t\treturn nil, fmt.Errorf(\"unrecognized collation option %q\", elem.Key())\n\t\t}\n\t}\n\treturn &collation, nil\n}\n\nfunc createHint(val bson.RawValue) (any, error) {\n\tvar hint any\n\n\tswitch val.Type {\n\tcase bson.TypeString:\n\t\thint = val.StringValue()\n\tcase bson.TypeEmbeddedDocument:\n\t\thint = val.Document()\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"unrecognized hint value type %s\", val.Type)\n\t}\n\treturn hint, nil\n}\n"
  },
  {
    "path": "internal/integration/unified/cursor_entity.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage unified\n\nimport (\n\t\"context\"\n)\n\n// The cursor interface defines the methods that must be implemented by iterable types that can be stored in an\n// EntityMap. This type exists so that we can store multiple concrete types returned by functions from the mongo package\n// under a single interface (e.g. mongo.Cursor and mongo.ChangeStream).\ntype cursor interface {\n\tClose(context.Context) error\n\tDecode(any) error\n\tErr() error\n\tNext(context.Context) bool\n\tTryNext(context.Context) bool\n}\n"
  },
  {
    "path": "internal/integration/unified/cursor_operation_execution.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage unified\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n)\n\nfunc executeIterateOnce(ctx context.Context, operation *operation) (*operationResult, error) {\n\tcursor, err := entities(ctx).cursor(operation.Object)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// TryNext will attempt to get the next document, potentially issuing a single 'getMore'.\n\tif cursor.TryNext(ctx) {\n\t\t// We don't expect the server to return malformed documents, so any errors from Decode here are treated\n\t\t// as fatal.\n\t\tvar res bson.Raw\n\t\tif err := cursor.Decode(&res); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"error decoding cursor result: %w\", err)\n\t\t}\n\n\t\treturn newDocumentResult(res, nil), nil\n\t}\n\treturn newErrorResult(cursor.Err()), nil\n}\n\nfunc executeIterateUntilDocumentOrError(ctx context.Context, operation *operation) (*operationResult, error) {\n\tcursor, err := entities(ctx).cursor(operation.Object)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// Next will loop until there is either a result or an error.\n\tif cursor.Next(ctx) {\n\t\t// We don't expect the server to return malformed documents, so any errors from Decode are treated as fatal.\n\t\tvar res bson.Raw\n\t\tif err := cursor.Decode(&res); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"error decoding cursor result: %w\", err)\n\t\t}\n\n\t\treturn newDocumentResult(res, nil), nil\n\t}\n\treturn newErrorResult(cursor.Err()), nil\n}\n"
  },
  {
    "path": "internal/integration/unified/database_operation_execution.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage unified\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/bsonutil\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n)\n\n// This file contains helpers to execute database operations.\n\nfunc executeCreateView(ctx context.Context, operation *operation) (*operationResult, error) {\n\tdb, err := entities(ctx).database(operation.Object)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar collName string\n\tvar cvo options.CreateViewOptionsBuilder\n\tvar viewOn string\n\tpipeline := make([]any, 0)\n\n\telems, err := operation.Arguments.Elements()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tfor _, elem := range elems {\n\t\tkey := elem.Key()\n\t\tval := elem.Value()\n\n\t\tswitch key {\n\t\tcase \"collection\":\n\t\t\tcollName = val.StringValue()\n\t\tcase \"pipeline\":\n\t\t\tpipeline = bsonutil.RawToInterfaces(bsonutil.RawArrayToDocuments(val.Array())...)\n\t\tcase \"viewOn\":\n\t\t\tviewOn = val.StringValue()\n\t\tdefault:\n\t\t\treturn nil, fmt.Errorf(\"unrecognized createView option %q\", key)\n\t\t}\n\t}\n\tif collName == \"\" {\n\t\treturn nil, newMissingArgumentError(\"collection\")\n\t}\n\tif viewOn == \"\" {\n\t\treturn nil, newMissingArgumentError(\"viewOn\")\n\t}\n\n\terr = db.CreateView(ctx, collName, viewOn, pipeline, &cvo)\n\treturn newErrorResult(err), nil\n}\n\nfunc executeCreateCollection(ctx context.Context, operation *operation) (*operationResult, error) {\n\t// In the Go driver there is a separate method for creating views.  However, the unified test CRUD format does not\n\t// make this distinction.  If necessary, here we branch to create a view.\n\tcreateView, err := operation.isCreateView()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif createView {\n\t\treturn executeCreateView(ctx, operation)\n\t}\n\n\tdb, err := entities(ctx).database(operation.Object)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar collName string\n\tvar cco options.CreateCollectionOptionsBuilder\n\telems, _ := operation.Arguments.Elements()\n\tfor _, elem := range elems {\n\t\tkey := elem.Key()\n\t\tval := elem.Value()\n\n\t\tswitch key {\n\t\tcase \"collection\":\n\t\t\tcollName = val.StringValue()\n\t\tcase \"changeStreamPreAndPostImages\":\n\t\t\tcco.SetChangeStreamPreAndPostImages(val.Document())\n\t\tcase \"expireAfterSeconds\":\n\t\t\tcco.SetExpireAfterSeconds(int64(val.Int32()))\n\t\tcase \"capped\":\n\t\t\tcco.SetCapped(val.Boolean())\n\t\tcase \"size\":\n\t\t\tcco.SetSizeInBytes(val.AsInt64())\n\t\tcase \"max\":\n\t\t\tcco.SetMaxDocuments(val.AsInt64())\n\t\tcase \"timeseries\":\n\t\t\ttsElems, err := elem.Value().Document().Elements()\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\n\t\t\ttso := options.TimeSeries()\n\t\t\tfor _, elem := range tsElems {\n\t\t\t\tkey := elem.Key()\n\t\t\t\tval := elem.Value()\n\n\t\t\t\tswitch key {\n\t\t\t\tcase \"timeField\":\n\t\t\t\t\ttso.SetTimeField(val.StringValue())\n\t\t\t\tcase \"metaField\":\n\t\t\t\t\ttso.SetMetaField(val.StringValue())\n\t\t\t\tcase \"granularity\":\n\t\t\t\t\ttso.SetGranularity(val.StringValue())\n\t\t\t\tcase \"bucketMaxSpanSeconds\":\n\t\t\t\t\ttso.SetBucketMaxSpan(time.Duration(val.Int32()) * time.Second)\n\t\t\t\tcase \"bucketRoundingSeconds\":\n\t\t\t\t\ttso.SetBucketRounding(time.Duration(val.Int32()) * time.Second)\n\t\t\t\tdefault:\n\t\t\t\t\treturn nil, fmt.Errorf(\"unrecognized timeseries option %q\", key)\n\t\t\t\t}\n\t\t\t}\n\t\t\tcco.SetTimeSeriesOptions(tso)\n\t\tcase \"clusteredIndex\":\n\t\t\tcco.SetClusteredIndex(val.Document())\n\t\tcase \"validator\":\n\t\t\tcco.SetValidator(val.Document())\n\t\tcase \"encryptedFields\":\n\t\t\tcco.SetEncryptedFields(val.Document())\n\t\tdefault:\n\t\t\treturn nil, fmt.Errorf(\"unrecognized createCollection option %q\", key)\n\t\t}\n\t}\n\tif collName == \"\" {\n\t\treturn nil, newMissingArgumentError(\"collection\")\n\t}\n\n\terr = db.CreateCollection(ctx, collName, &cco)\n\tif err != nil {\n\t\treturn newErrorResult(err), nil\n\t}\n\n\tif collID := operation.ResultEntityID; collID != nil {\n\t\tcollEntityOpts := newCollectionEntityOptions(*collID, operation.Object, collName, nil)\n\n\t\terr := entities(ctx).addCollectionEntity(collEntityOpts)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"failed to save collection as entity: %w\", err)\n\t\t}\n\t}\n\n\treturn newEmptyResult(), nil\n}\n\nfunc executeDropCollection(ctx context.Context, operation *operation) (*operationResult, error) {\n\tdb, err := entities(ctx).database(operation.Object)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tdco := options.DropCollection()\n\n\tvar collName string\n\telems, _ := operation.Arguments.Elements()\n\tfor _, elem := range elems {\n\t\tkey := elem.Key()\n\t\tval := elem.Value()\n\n\t\tswitch key {\n\t\tcase \"encryptedFields\":\n\t\t\tdco.SetEncryptedFields(val.Document())\n\t\tcase \"collection\":\n\t\t\tcollName = val.StringValue()\n\t\tdefault:\n\t\t\treturn nil, fmt.Errorf(\"unrecognized dropCollection option %q\", key)\n\t\t}\n\t}\n\tif collName == \"\" {\n\t\treturn nil, newMissingArgumentError(\"collection\")\n\t}\n\n\terr = db.Collection(collName).Drop(ctx, dco)\n\treturn newErrorResult(err), nil\n}\n\nfunc executeListCollections(ctx context.Context, operation *operation) (*operationResult, error) {\n\tdb, err := entities(ctx).database(operation.Object)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tlistCollArgs, err := createListCollectionsArguments(operation.Arguments)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tcursor, err := db.ListCollections(ctx, listCollArgs.filter, listCollArgs.opts)\n\tif err != nil {\n\t\treturn newErrorResult(err), nil\n\t}\n\tdefer cursor.Close(ctx)\n\n\tvar docs []bson.Raw\n\tif err := cursor.All(ctx, &docs); err != nil {\n\t\treturn newErrorResult(err), nil\n\t}\n\treturn newCursorResult(docs), nil\n}\n\nfunc executeListCollectionNames(ctx context.Context, operation *operation) (*operationResult, error) {\n\tdb, err := entities(ctx).database(operation.Object)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tlistCollArgs, err := createListCollectionsArguments(operation.Arguments)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tnames, err := db.ListCollectionNames(ctx, listCollArgs.filter, listCollArgs.opts)\n\tif err != nil {\n\t\treturn newErrorResult(err), nil\n\t}\n\t_, data, err := bson.MarshalValue(names)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"error converting collection names slice to BSON: %w\", err)\n\t}\n\treturn newValueResult(bson.TypeArray, data, nil), nil\n}\n\nfunc executeModifyCollection(ctx context.Context, operation *operation) (*operationResult, error) {\n\tdb, err := entities(ctx).database(operation.Object)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tcollModCmd := bson.D{}\n\n\telems, _ := operation.Arguments.Elements()\n\tfor _, elem := range elems {\n\t\tkey := elem.Key()\n\t\tval := elem.Value()\n\n\t\tswitch key {\n\t\tcase \"collection\":\n\t\t\tcollModCmd = append(collModCmd, bson.E{\"collMod\", val.StringValue()})\n\t\tcase \"changeStreamPreAndPostImages\":\n\t\t\tcollModCmd = append(collModCmd, bson.E{\"changeStreamPreAndPostImages\", val.Document()})\n\t\tcase \"validator\":\n\t\t\tcollModCmd = append(collModCmd, bson.E{\"validator\", val.Document()})\n\t\tcase \"index\":\n\t\t\tcollModCmd = append(collModCmd, bson.E{\"index\", val.Document()})\n\t\tdefault:\n\t\t\treturn nil, fmt.Errorf(\"unrecognized modifyCollection option %q\", key)\n\t\t}\n\t}\n\n\tres, err := db.RunCommand(ctx, collModCmd).Raw()\n\treturn newDocumentResult(res, err), nil\n}\n\nfunc executeRunCommand(ctx context.Context, operation *operation) (*operationResult, error) {\n\tdb, err := entities(ctx).database(operation.Object)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar command bson.Raw\n\topts := options.RunCmd()\n\n\telems, _ := operation.Arguments.Elements()\n\tfor _, elem := range elems {\n\t\tkey := elem.Key()\n\t\tval := elem.Value()\n\n\t\tswitch key {\n\t\tcase \"command\":\n\t\t\tcommand = val.Document()\n\t\tcase \"commandName\":\n\t\t\t// This is only necessary for languages that cannot preserve key order in the command document, so we can\n\t\t\t// ignore it.\n\t\tcase \"readConcern\":\n\t\t\t// GODRIVER-1774: We currently don't support overriding read concern for RunCommand.\n\t\t\treturn nil, fmt.Errorf(\"readConcern in runCommand not supported\")\n\t\tcase \"readPreference\":\n\t\t\tvar temp ReadPreference\n\t\t\tif err := bson.Unmarshal(val.Document(), &temp); err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"error unmarshalling readPreference option: %w\", err)\n\t\t\t}\n\n\t\t\trp, err := temp.ToReadPrefOption()\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"error creating readpref.ReadPref object: %w\", err)\n\t\t\t}\n\t\t\topts.SetReadPreference(rp)\n\t\tcase \"writeConcern\":\n\t\t\t// GODRIVER-1774: We currently don't support overriding write concern for RunCommand.\n\t\t\treturn nil, fmt.Errorf(\"writeConcern in runCommand not supported\")\n\t\tdefault:\n\t\t\treturn nil, fmt.Errorf(\"unrecognized runCommand option %q\", key)\n\t\t}\n\t}\n\tif command == nil {\n\t\treturn nil, newMissingArgumentError(\"command\")\n\t}\n\n\tres, err := db.RunCommand(ctx, command, opts).Raw()\n\treturn newDocumentResult(res, err), nil\n}\n\n// executeRunCursorCommand proxies the database's runCursorCommand method and\n// supports the same arguments and options.\nfunc executeRunCursorCommand(ctx context.Context, operation *operation) (*operationResult, error) {\n\tdb, err := entities(ctx).database(operation.Object)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar (\n\t\tbatchSize int32\n\t\tcommand   bson.Raw\n\t\tcomment   bson.Raw\n\t)\n\n\topts := options.RunCmd()\n\n\telems, _ := operation.Arguments.Elements()\n\tfor _, elem := range elems {\n\t\tkey := elem.Key()\n\t\tval := elem.Value()\n\n\t\tswitch key {\n\t\tcase \"batchSize\":\n\t\t\tbatchSize = val.Int32()\n\t\tcase \"command\":\n\t\t\tcommand = val.Document()\n\t\tcase \"commandName\":\n\t\t\t// This is only necessary for languages that cannot\n\t\t\t// preserve key order in the command document, so we can\n\t\t\t// ignore it.\n\t\tcase \"comment\":\n\t\t\tcomment = val.Document()\n\t\tcase \"maxTimeMS\":\n\t\t\t// TODO(DRIVERS-2829): Error here instead of skip to ensure that if new\n\t\t\t// tests are added containing maxTimeMS (a legacy timeout option that we\n\t\t\t// have removed as of v2), then a CSOT analogue exists. Once we have\n\t\t\t// ensured an analogue exists, extend \"skippedTestDescriptions\" to avoid\n\t\t\t// this error.\n\t\t\treturn nil, fmt.Errorf(\"the maxTimeMS database option is not supported\")\n\t\tcase \"cursorTimeout\":\n\t\t\treturn nil, newSkipTestError(\"cursorTimeout not supported\")\n\t\tcase \"timeoutMode\":\n\t\t\treturn nil, newSkipTestError(\"timeoutMode not supported\")\n\t\tdefault:\n\t\t\treturn nil, fmt.Errorf(\"unrecognized runCursorCommand option: %q\", key)\n\t\t}\n\t}\n\n\tif command == nil {\n\t\treturn nil, newMissingArgumentError(\"command\")\n\t}\n\n\tcursor, err := db.RunCommandCursor(ctx, command, opts)\n\tif err != nil {\n\t\treturn newErrorResult(err), nil\n\t}\n\n\tif batchSize > 0 {\n\t\tcursor.SetBatchSize(batchSize)\n\t}\n\n\tif len(comment) > 0 {\n\t\tcursor.SetComment(comment)\n\t}\n\n\t// When executing the provided command, the test runner MUST fully\n\t// iterate the cursor. This will ensure consistent behavior between\n\t// drivers that eagerly create a server-side cursor and those that do\n\t// so lazily when iteration begins.\n\tvar docs []bson.Raw\n\tif err := cursor.All(ctx, &docs); err != nil {\n\t\treturn newErrorResult(err), nil\n\t}\n\n\treturn newCursorResult(docs), nil\n}\n\n// executeCreateRunCursorCommand proxies the database's runCursorCommand method\n// and supports the same arguments and options.\nfunc executeCreateRunCursorCommand(ctx context.Context, operation *operation) (*operationResult, error) {\n\tdb, err := entities(ctx).database(operation.Object)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar (\n\t\tbatchSize int32\n\t\tcommand   bson.Raw\n\t)\n\n\topts := options.RunCmd()\n\n\telems, _ := operation.Arguments.Elements()\n\tfor _, elem := range elems {\n\t\tkey := elem.Key()\n\t\tval := elem.Value()\n\n\t\tswitch key {\n\t\tcase \"batchSize\":\n\t\t\tbatchSize = val.Int32()\n\t\tcase \"command\":\n\t\t\tcommand = val.Document()\n\t\tcase \"commandName\":\n\t\t\t// This is only necessary for languages that cannot\n\t\t\t// preserve key order in the command document, so we can\n\t\t\t// ignore it.\n\t\tcase \"cursorType\":\n\t\t\treturn nil, newSkipTestError(\"cursorType not supported\")\n\t\tcase \"timeoutMode\":\n\t\t\treturn nil, newSkipTestError(\"timeoutMode not supported\")\n\t\tdefault:\n\t\t\treturn nil, fmt.Errorf(\"unrecognized createRunCursorCommand option: %q\", key)\n\t\t}\n\t}\n\n\tif command == nil {\n\t\treturn nil, newMissingArgumentError(\"command\")\n\t}\n\n\t// Test runners MUST ensure that the server-side cursor is created (i.e.\n\t// the command document has executed) as part of this operation.\n\tcursor, err := db.RunCommandCursor(ctx, command, opts)\n\tif err != nil {\n\t\treturn newErrorResult(err), nil\n\t}\n\n\tif batchSize > 0 {\n\t\tcursor.SetBatchSize(batchSize)\n\t}\n\n\tif cursorID := operation.ResultEntityID; cursorID != nil {\n\t\terr := entities(ctx).addCursorEntity(*cursorID, cursor)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"failed to store result as cursor entity: %w\", err)\n\t\t}\n\t}\n\n\treturn newEmptyResult(), nil\n}\n"
  },
  {
    "path": "internal/integration/unified/db_collection_options.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage unified\n\nimport (\n\t\"fmt\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n)\n\ntype dbOrCollectionOptions struct {\n\tDBOptions         *options.DatabaseOptionsBuilder\n\tCollectionOptions *options.CollectionOptionsBuilder\n}\n\nvar _ bson.Unmarshaler = (*dbOrCollectionOptions)(nil)\n\n// UnmarshalBSON specifies custom BSON unmarshalling behavior to convert db/collection options from BSON/JSON documents\n// to their corresponding Go objects.\nfunc (d *dbOrCollectionOptions) UnmarshalBSON(data []byte) error {\n\tvar temp struct {\n\t\tRC    *readConcern    `bson:\"readConcern\"`\n\t\tRP    *ReadPreference `bson:\"readPreference\"`\n\t\tWC    *writeConcern   `bson:\"writeConcern\"`\n\t\tExtra map[string]any  `bson:\",inline\"`\n\t}\n\tif err := bson.Unmarshal(data, &temp); err != nil {\n\t\treturn fmt.Errorf(\"error unmarshalling to temporary dbOrCollectionOptions object: %w\", err)\n\t}\n\tif len(temp.Extra) > 0 {\n\t\treturn fmt.Errorf(\"unrecognized fields for dbOrCollectionOptions: %v\", mapKeys(temp.Extra))\n\t}\n\n\td.DBOptions = options.Database()\n\td.CollectionOptions = options.Collection()\n\tif temp.RC != nil {\n\t\trc := temp.RC.toReadConcernOption()\n\t\td.DBOptions.SetReadConcern(rc)\n\t\td.CollectionOptions.SetReadConcern(rc)\n\t}\n\tif temp.RP != nil {\n\t\trp, err := temp.RP.ToReadPrefOption()\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"error parsing read preference document: %w\", err)\n\t\t}\n\n\t\td.DBOptions.SetReadPreference(rp)\n\t\td.CollectionOptions.SetReadPreference(rp)\n\t}\n\tif temp.WC != nil {\n\t\twc, err := temp.WC.toWriteConcernOption()\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"error parsing write concern document: %w\", err)\n\t\t}\n\n\t\td.DBOptions.SetWriteConcern(wc)\n\t\td.CollectionOptions.SetWriteConcern(wc)\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "internal/integration/unified/entity.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage unified\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"crypto/tls\"\n\t\"errors\"\n\t\"fmt\"\n\t\"os\"\n\t\"regexp\"\n\t\"sync\"\n\t\"sync/atomic\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n)\n\n// ErrEntityMapOpen is returned when a slice entity is accessed while the EntityMap is open\nvar ErrEntityMapOpen = errors.New(\"slices cannot be accessed while EntityMap is open\")\n\nvar (\n\ttlsCAFile                   = os.Getenv(\"CSFLE_TLS_CA_FILE\")\n\ttlsClientCertificateKeyFile = os.Getenv(\"CSFLE_TLS_CLIENT_CERT_FILE\")\n)\n\nvar (\n\t// qeCollectionPattern matches collections automatically created for\n\t// queryable encryption.\n\tqeCollectionPattern = regexp.MustCompile(\"^enxcol_.*.e(sc|coc)$\")\n\n\tplaceholderDoc = bsoncore.NewDocumentBuilder().AppendInt32(\"$$placeholder\", 1).Build()\n)\n\nconst defaultLocalKeyBase64 = \"Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk\"\n\ntype storeEventsAsEntitiesConfig struct {\n\tEventListID string   `bson:\"id\"`\n\tEvents      []string `bson:\"events\"`\n}\n\ntype observeLogMessages struct {\n\tCommand         string `bson:\"command\"`\n\tTopology        string `bson:\"topology\"`\n\tServerSelection string `bson:\"serverSelection\"`\n\tConnection      string `bson:\"connection\"`\n}\n\n// entityOptions represents all options that can be used to configure an entity. Because there are multiple entity\n// types, only a subset of the options that this type contains apply to any given entity.\ntype entityOptions struct {\n\t// Options that apply to all entity types.\n\tID string `bson:\"id\"`\n\n\t// Options for client entities.\n\tAutoEncryptOpts          bson.Raw                      `bson:\"autoEncryptOpts\"`\n\tURIOptions               bson.M                        `bson:\"uriOptions\"`\n\tUseMultipleMongoses      *bool                         `bson:\"useMultipleMongoses\"`\n\tObserveEvents            []string                      `bson:\"observeEvents\"`\n\tIgnoredCommands          []string                      `bson:\"ignoreCommandMonitoringEvents\"`\n\tObserveSensitiveCommands *bool                         `bson:\"observeSensitiveCommands\"`\n\tStoreEventsAsEntities    []storeEventsAsEntitiesConfig `bson:\"storeEventsAsEntities\"`\n\tServerAPIOptions         *serverAPIOptions             `bson:\"serverApi\"`\n\n\t// Options for logger entities.\n\tObserveLogMessages *observeLogMessages `bson:\"observeLogMessages\"`\n\n\t// Options for database entities.\n\tDatabaseName    string                 `bson:\"databaseName\"`\n\tDatabaseOptions *dbOrCollectionOptions `bson:\"databaseOptions\"`\n\n\t// Options for collection entities.\n\tCollectionName    string                 `bson:\"collectionName\"`\n\tCollectionOptions *dbOrCollectionOptions `bson:\"collectionOptions\"`\n\n\t// Options for session entities.\n\tSessionOptions *sessionOptions `bson:\"sessionOptions\"`\n\n\t// Options for GridFS bucket entities.\n\tGridFSBucketOptions *gridFSBucketOptions `bson:\"bucketOptions\"`\n\n\t// Options that reference other entities.\n\tClientID   string `bson:\"client\"`\n\tDatabaseID string `bson:\"database\"`\n\n\tClientEncryptionOpts *clientEncryptionOpts `bson:\"clientEncryptionOpts\"`\n\n\t// Maximum duration (in milliseconds) that the test runner MUST wait for each\n\t// connection pool to be populated with minPoolSize. Any CMAP and SDAM events\n\t// that occur before the pool is populated will be ignored.\n\tAwaitMinPoolSizeMS *int `bson:\"awaitMinPoolSizeMS\"`\n}\n\nfunc (eo *entityOptions) setHeartbeatFrequencyMS(freq time.Duration) {\n\tif eo.URIOptions == nil {\n\t\teo.URIOptions = make(bson.M)\n\t}\n\n\tif _, ok := eo.URIOptions[\"heartbeatFrequencyMS\"]; !ok {\n\t\t// The UST values for heartbeatFrequencyMS are given as int32,\n\t\t// so we need to cast the frequency as int32 before setting it\n\t\t// on the URIOptions map.\n\t\teo.URIOptions[\"heartbeatFrequencyMS\"] = int32(freq.Milliseconds())\n\t}\n}\n\n// newCollectionEntityOptions constructs an entity options object for a\n// collection.\nfunc newCollectionEntityOptions(id string, databaseID string, collectionName string,\n\topts *dbOrCollectionOptions,\n) *entityOptions {\n\toptions := &entityOptions{\n\t\tID:                id,\n\t\tDatabaseID:        databaseID,\n\t\tCollectionName:    collectionName,\n\t\tCollectionOptions: opts,\n\t}\n\n\treturn options\n}\n\ntype task struct {\n\tname    string\n\texecute func() error\n}\n\ntype backgroundRoutine struct {\n\ttasks chan *task\n\twg    sync.WaitGroup\n\terr   error\n}\n\nfunc (b *backgroundRoutine) start() {\n\tb.wg.Add(1)\n\n\tgo func() {\n\t\tdefer b.wg.Done()\n\n\t\tfor t := range b.tasks {\n\t\t\tif b.err != nil {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tch := make(chan error)\n\t\t\tgo func(task *task) {\n\t\t\t\tch <- task.execute()\n\t\t\t}(t)\n\t\t\tselect {\n\t\t\tcase err := <-ch:\n\t\t\t\tif err != nil {\n\t\t\t\t\tb.err = fmt.Errorf(\"error running operation %s: %v\", t.name, err)\n\t\t\t\t}\n\t\t\tcase <-time.After(10 * time.Second):\n\t\t\t\tb.err = fmt.Errorf(\"timed out after 10 seconds\")\n\t\t\t}\n\t\t}\n\t}()\n}\n\nfunc (b *backgroundRoutine) stop() error {\n\tclose(b.tasks)\n\tb.wg.Wait()\n\treturn b.err\n}\n\nfunc (b *backgroundRoutine) addTask(name string, execute func() error) bool {\n\tselect {\n\tcase b.tasks <- &task{\n\t\tname:    name,\n\t\texecute: execute,\n\t}:\n\t\treturn true\n\tdefault:\n\t\treturn false\n\t}\n}\n\nfunc newBackgroundRoutine() *backgroundRoutine {\n\troutine := &backgroundRoutine{\n\t\ttasks: make(chan *task, 10),\n\t}\n\n\treturn routine\n}\n\ntype clientEncryptionOpts struct {\n\tKeyVaultClient    string              `bson:\"keyVaultClient\"`\n\tKeyVaultNamespace string              `bson:\"keyVaultNamespace\"`\n\tKmsProviders      map[string]bson.Raw `bson:\"kmsProviders\"`\n\tKeyExpirationMS   *int64              `bson:\"keyExpirationMS\"`\n}\n\n// EntityMap is used to store entities during tests. This type enforces uniqueness so no two entities can have the same\n// ID, even if they are of different types. It also enforces referential integrity so construction of an entity that\n// references another (e.g. a database entity references a client) will fail if the referenced entity does not exist.\n// Accessors are available for the BSON entities.\ntype EntityMap struct {\n\tallEntities              map[string]struct{}\n\tcursorEntities           map[string]cursor\n\tclientEntities           map[string]*clientEntity\n\tdbEntites                map[string]*mongo.Database\n\tcollEntities             map[string]*mongo.Collection\n\tsessions                 map[string]*mongo.Session\n\tgridfsBuckets            map[string]*mongo.GridFSBucket\n\tbsonValues               map[string]bson.RawValue\n\teventListEntities        map[string][]bson.Raw\n\tbsonArrayEntities        map[string][]bson.Raw // for storing errors and failures from a loop operation\n\tsuccessValues            map[string]int32\n\titerationValues          map[string]int32\n\tclientEncryptionEntities map[string]*mongo.ClientEncryption\n\troutinesMap              sync.Map // maps thread name to *backgroundRoutine\n\tevtLock                  sync.Mutex\n\tclosed                   atomic.Value\n\t// keyVaultClientIDs tracks IDs of clients used as a keyVaultClient in ClientEncryption objects.\n\t// ClientEncryption.Close() calls Disconnect on the keyVaultClient.\n\t// EntityMap.close() must skip calling Disconnect on any client entity referenced in keyVaultClientIDs.\n\tkeyVaultClientIDs map[string]bool\n}\n\nfunc (em *EntityMap) isClosed() bool {\n\treturn em.closed.Load().(bool)\n}\n\nfunc (em *EntityMap) setClosed(val bool) {\n\tem.closed.Store(val)\n}\n\nfunc newEntityMap() *EntityMap {\n\tem := &EntityMap{\n\t\tallEntities:              make(map[string]struct{}),\n\t\tgridfsBuckets:            make(map[string]*mongo.GridFSBucket),\n\t\tbsonValues:               make(map[string]bson.RawValue),\n\t\tcursorEntities:           make(map[string]cursor),\n\t\tclientEntities:           make(map[string]*clientEntity),\n\t\tcollEntities:             make(map[string]*mongo.Collection),\n\t\tdbEntites:                make(map[string]*mongo.Database),\n\t\tsessions:                 make(map[string]*mongo.Session),\n\t\teventListEntities:        make(map[string][]bson.Raw),\n\t\tbsonArrayEntities:        make(map[string][]bson.Raw),\n\t\tsuccessValues:            make(map[string]int32),\n\t\titerationValues:          make(map[string]int32),\n\t\tclientEncryptionEntities: make(map[string]*mongo.ClientEncryption),\n\t\tkeyVaultClientIDs:        make(map[string]bool),\n\t}\n\tem.setClosed(false)\n\treturn em\n}\n\nfunc (em *EntityMap) addBSONEntity(id string, val any) error {\n\tif err := em.verifyEntityDoesNotExist(id); err != nil {\n\t\treturn err\n\t}\n\n\tem.allEntities[id] = struct{}{}\n\n\t// If val is already a bson.RawValue, use it directly to preserve the original\n\t// type and bytes. If not, construct a new bson.RawValue.\n\trv, ok := val.(bson.RawValue)\n\tif !ok {\n\t\ttyp, bytes, err := bson.MarshalValue(val)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"error marshaling BSON value for entity ID %q: %w\", id, err)\n\t\t}\n\n\t\trv = bson.RawValue{\n\t\t\tType:  typ,\n\t\t\tValue: bytes,\n\t\t}\n\t}\n\n\tem.bsonValues[id] = rv\n\n\treturn nil\n}\n\nfunc (em *EntityMap) addCursorEntity(id string, cursor cursor) error {\n\tif err := em.verifyEntityDoesNotExist(id); err != nil {\n\t\treturn err\n\t}\n\n\tem.allEntities[id] = struct{}{}\n\tem.cursorEntities[id] = cursor\n\treturn nil\n}\n\nfunc (em *EntityMap) addBSONArrayEntity(id string) error {\n\t// Error if a non-BSON array entity exists with the same name\n\tif _, ok := em.allEntities[id]; ok {\n\t\tif _, ok := em.bsonArrayEntities[id]; !ok {\n\t\t\treturn fmt.Errorf(\"non-BSON array entity with ID %q already exists\", id)\n\t\t}\n\t\treturn nil\n\t}\n\n\tem.allEntities[id] = struct{}{}\n\tem.bsonArrayEntities[id] = []bson.Raw{}\n\treturn nil\n}\n\nfunc (em *EntityMap) addSuccessesEntity(id string) error {\n\tif err := em.verifyEntityDoesNotExist(id); err != nil {\n\t\treturn err\n\t}\n\n\tem.allEntities[id] = struct{}{}\n\tem.successValues[id] = 0\n\treturn nil\n}\n\nfunc (em *EntityMap) addIterationsEntity(id string) error {\n\tif err := em.verifyEntityDoesNotExist(id); err != nil {\n\t\treturn err\n\t}\n\n\tem.allEntities[id] = struct{}{}\n\tem.iterationValues[id] = 0\n\treturn nil\n}\n\nfunc (em *EntityMap) addEventsEntity(id string) error {\n\tif err := em.verifyEntityDoesNotExist(id); err != nil {\n\t\treturn err\n\t}\n\tem.allEntities[id] = struct{}{}\n\tem.eventListEntities[id] = []bson.Raw{}\n\treturn nil\n}\n\nfunc (em *EntityMap) incrementSuccesses(id string) error {\n\tif _, ok := em.successValues[id]; !ok {\n\t\treturn newEntityNotFoundError(\"successes\", id)\n\t}\n\tem.successValues[id]++\n\treturn nil\n}\n\nfunc (em *EntityMap) incrementIterations(id string) error {\n\tif _, ok := em.iterationValues[id]; !ok {\n\t\treturn newEntityNotFoundError(\"iterations\", id)\n\t}\n\tem.iterationValues[id]++\n\treturn nil\n}\n\nfunc (em *EntityMap) appendEventsEntity(id string, doc bson.Raw) {\n\tem.evtLock.Lock()\n\tdefer em.evtLock.Unlock()\n\tif _, ok := em.eventListEntities[id]; ok {\n\t\tem.eventListEntities[id] = append(em.eventListEntities[id], doc)\n\t}\n}\n\nfunc (em *EntityMap) appendBSONArrayEntity(id string, doc bson.Raw) error {\n\tif _, ok := em.bsonArrayEntities[id]; !ok {\n\t\treturn newEntityNotFoundError(\"BSON array\", id)\n\t}\n\tem.bsonArrayEntities[id] = append(em.bsonArrayEntities[id], doc)\n\treturn nil\n}\n\nfunc (em *EntityMap) addEntity(ctx context.Context, entityType string, entityOptions *entityOptions) error {\n\tif err := em.verifyEntityDoesNotExist(entityOptions.ID); err != nil {\n\t\treturn err\n\t}\n\n\tvar err error\n\tswitch entityType {\n\tcase \"client\":\n\t\terr = em.addClientEntity(ctx, entityOptions)\n\tcase \"database\":\n\t\terr = em.addDatabaseEntity(entityOptions)\n\tcase \"collection\":\n\t\terr = em.addCollectionEntity(entityOptions)\n\tcase \"session\":\n\t\terr = em.addSessionEntity(entityOptions)\n\tcase \"thread\":\n\t\troutine := newBackgroundRoutine()\n\t\tem.routinesMap.Store(entityOptions.ID, routine)\n\t\troutine.start()\n\tcase \"bucket\":\n\t\terr = em.addGridFSBucketEntity(entityOptions)\n\tcase \"clientEncryption\":\n\t\terr = em.addClientEncryptionEntity(entityOptions)\n\tdefault:\n\t\treturn fmt.Errorf(\"unrecognized entity type %q\", entityType)\n\t}\n\n\tif err != nil {\n\t\treturn fmt.Errorf(\"error constructing entity of type %q: %w\", entityType, err)\n\t}\n\tem.allEntities[entityOptions.ID] = struct{}{}\n\treturn nil\n}\n\nfunc (em *EntityMap) gridFSBucket(id string) (*mongo.GridFSBucket, error) {\n\tbucket, ok := em.gridfsBuckets[id]\n\tif !ok {\n\t\treturn nil, newEntityNotFoundError(\"gridfs bucket\", id)\n\t}\n\treturn bucket, nil\n}\n\nfunc (em *EntityMap) cursor(id string) (cursor, error) {\n\tcursor, ok := em.cursorEntities[id]\n\tif !ok {\n\t\treturn nil, newEntityNotFoundError(\"cursor\", id)\n\t}\n\treturn cursor, nil\n}\n\nfunc (em *EntityMap) client(id string) (*clientEntity, error) {\n\tclient, ok := em.clientEntities[id]\n\tif !ok {\n\t\treturn nil, newEntityNotFoundError(\"client\", id)\n\t}\n\treturn client, nil\n}\n\nfunc (em *EntityMap) clientEncryption(id string) (*mongo.ClientEncryption, error) {\n\tcee, ok := em.clientEncryptionEntities[id]\n\tif !ok {\n\t\treturn nil, newEntityNotFoundError(\"client\", id)\n\t}\n\treturn cee, nil\n}\n\nfunc (em *EntityMap) clients() map[string]*clientEntity {\n\treturn em.clientEntities\n}\n\nfunc (em *EntityMap) collections() map[string]*mongo.Collection {\n\treturn em.collEntities\n}\n\nfunc (em *EntityMap) collection(id string) (*mongo.Collection, error) {\n\tcoll, ok := em.collEntities[id]\n\tif !ok {\n\t\treturn nil, newEntityNotFoundError(\"collection\", id)\n\t}\n\treturn coll, nil\n}\n\nfunc (em *EntityMap) database(id string) (*mongo.Database, error) {\n\tdb, ok := em.dbEntites[id]\n\tif !ok {\n\t\treturn nil, newEntityNotFoundError(\"database\", id)\n\t}\n\treturn db, nil\n}\n\nfunc (em *EntityMap) session(id string) (*mongo.Session, error) {\n\tsess, ok := em.sessions[id]\n\tif !ok {\n\t\treturn nil, newEntityNotFoundError(\"session\", id)\n\t}\n\treturn sess, nil\n}\n\n// BSONValue returns the bson.RawValue associated with id\nfunc (em *EntityMap) BSONValue(id string) (bson.RawValue, error) {\n\tval, ok := em.bsonValues[id]\n\tif !ok {\n\t\treturn emptyRawValue, newEntityNotFoundError(\"BSON\", id)\n\t}\n\treturn val, nil\n}\n\n// EventList returns the array of event documents associated with id. This should only be accessed\n// after the test is finished running\nfunc (em *EntityMap) EventList(id string) ([]bson.Raw, error) {\n\tif !em.isClosed() {\n\t\treturn nil, ErrEntityMapOpen\n\t}\n\tval, ok := em.eventListEntities[id]\n\tif !ok {\n\t\treturn nil, newEntityNotFoundError(\"event list\", id)\n\t}\n\treturn val, nil\n}\n\n// BSONArray returns the BSON document array associated with id. This should only be accessed\n// after the test is finished running\nfunc (em *EntityMap) BSONArray(id string) ([]bson.Raw, error) {\n\tif !em.isClosed() {\n\t\treturn nil, ErrEntityMapOpen\n\t}\n\tval, ok := em.bsonArrayEntities[id]\n\tif !ok {\n\t\treturn nil, newEntityNotFoundError(\"BSON array\", id)\n\t}\n\treturn val, nil\n}\n\n// Successes returns the number of successes associated with id\nfunc (em *EntityMap) Successes(id string) (int32, error) {\n\tval, ok := em.successValues[id]\n\tif !ok {\n\t\treturn 0, newEntityNotFoundError(\"successes\", id)\n\t}\n\treturn val, nil\n}\n\n// Iterations returns the number of iterations associated with id\nfunc (em *EntityMap) Iterations(id string) (int32, error) {\n\tval, ok := em.iterationValues[id]\n\tif !ok {\n\t\treturn 0, newEntityNotFoundError(\"iterations\", id)\n\t}\n\treturn val, nil\n}\n\n// close disposes of the session and client entities associated with this map.\nfunc (em *EntityMap) close(ctx context.Context) []error {\n\tfor _, sess := range em.sessions {\n\t\tsess.EndSession(ctx)\n\t}\n\n\tvar errs []error\n\tfor id, cursor := range em.cursorEntities {\n\t\tif err := cursor.Close(ctx); err != nil {\n\t\t\terrs = append(errs, fmt.Errorf(\"error closing cursor with ID %q: %w\", id, err))\n\t\t}\n\t}\n\n\t// Clear automatically created collections used for queryable encryption\n\tfor id, db := range em.dbEntites {\n\t\tcolls, err := db.ListCollectionNames(ctx, bson.D{})\n\t\tif err != nil {\n\t\t\terrs = append(errs, fmt.Errorf(\"error listing collections in database with ID %q: %w\", id, err))\n\t\t\tcontinue\n\t\t}\n\t\tfor _, coll := range colls {\n\t\t\tif qeCollectionPattern.MatchString(coll) {\n\t\t\t\terr = db.Collection(coll).Drop(ctx)\n\t\t\t\tif err != nil {\n\t\t\t\t\terrs = append(errs, fmt.Errorf(\"error clearing collection %q: %w\", coll, err))\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tfor id, client := range em.clientEntities {\n\t\tif ok := em.keyVaultClientIDs[id]; ok {\n\t\t\t// Client will be closed in clientEncryption.Close()\n\t\t\tcontinue\n\t\t}\n\n\t\tif err := client.disconnect(ctx); err != nil {\n\t\t\terrs = append(errs, fmt.Errorf(\"error closing client with ID %q: %w\", id, err))\n\t\t}\n\t}\n\n\tfor id, clientEncryption := range em.clientEncryptionEntities {\n\t\tif err := clientEncryption.Close(ctx); err != nil {\n\t\t\terrs = append(errs, fmt.Errorf(\"error closing clientEncryption with ID: %q: %w\", id, err))\n\t\t}\n\t}\n\n\tem.setClosed(true)\n\treturn errs\n}\n\nfunc (em *EntityMap) addClientEntity(ctx context.Context, entityOptions *entityOptions) error {\n\tvar client *clientEntity\n\n\tfor _, eventsAsEntity := range entityOptions.StoreEventsAsEntities {\n\t\tif entityOptions.ID == eventsAsEntity.EventListID {\n\t\t\treturn fmt.Errorf(\"entity with ID %q already exists\", entityOptions.ID)\n\t\t}\n\t\tif err := em.addEventsEntity(eventsAsEntity.EventListID); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tclient, err := newClientEntity(ctx, em, entityOptions)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"error creating client entity: %w\", err)\n\t}\n\n\tem.clientEntities[entityOptions.ID] = client\n\treturn nil\n}\n\nfunc (em *EntityMap) addDatabaseEntity(entityOptions *entityOptions) error {\n\tclient, ok := em.clientEntities[entityOptions.ClientID]\n\tif !ok {\n\t\treturn newEntityNotFoundError(\"client\", entityOptions.ClientID)\n\t}\n\n\tdbOpts := options.Database()\n\tif entityOptions.DatabaseOptions != nil {\n\t\tdbOpts = entityOptions.DatabaseOptions.DBOptions\n\t}\n\n\tem.dbEntites[entityOptions.ID] = client.Database(entityOptions.DatabaseName, dbOpts)\n\treturn nil\n}\n\n// getKmsCredential processes a value of an input KMS provider credential.\n// An empty document returns from the environment.\n// A string is returned as-is.\nfunc getKmsCredential(kmsDocument bson.Raw, credentialName string, envVar string, defaultValue string) (any, error) {\n\tcredentialVal, err := kmsDocument.LookupErr(credentialName)\n\tif errors.Is(err, bsoncore.ErrElementNotFound) {\n\t\treturn nil, nil\n\t}\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif str, ok := credentialVal.StringValueOK(); ok {\n\t\treturn str, nil\n\t}\n\n\tvar ok bool\n\tvar doc bson.Raw\n\tif doc, ok = credentialVal.DocumentOK(); !ok {\n\t\treturn nil, fmt.Errorf(\"expected String or Document for %v, got: %v\", credentialName, credentialVal)\n\t}\n\n\t// Check if document is a placeholder.\n\tif !bytes.Equal(doc, placeholderDoc) {\n\t\treturn nil, fmt.Errorf(\"unexpected non-empty document for %v: %v\", credentialName, doc)\n\t}\n\n\tif envVar == \"\" {\n\t\treturn defaultValue, nil\n\t}\n\tif value := os.Getenv(envVar); value != \"\" {\n\t\treturn value, nil\n\t}\n\tif defaultValue != \"\" {\n\t\treturn defaultValue, nil\n\t}\n\treturn nil, fmt.Errorf(\"unable to get environment value for %v. Please set the CSFLE environment variable: %v\", credentialName, envVar)\n}\n\nfunc getKmsProvider(key string, opt bson.Raw) (map[string]any, error) {\n\tprovider := make(map[string]any)\n\tswitch key {\n\tcase \"aws\":\n\t\taccessKeyID := \"FLE_AWS_KEY\"\n\t\tsecretAccessKey := \"FLE_AWS_SECRET\"\n\n\t\t// replace with temporary access, if sessionToken placeholder exists\n\t\tv, err := getKmsCredential(opt, \"sessionToken\", \"\", \"$$placeholder\")\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif v == \"$$placeholder\" {\n\t\t\tprovider[\"sessionToken\"] = os.Getenv(\"CSFLE_AWS_TEMP_SESSION_TOKEN\")\n\t\t\taccessKeyID = \"CSFLE_AWS_TEMP_ACCESS_KEY_ID\"\n\t\t\tsecretAccessKey = \"CSFLE_AWS_TEMP_SECRET_ACCESS_KEY\"\n\t\t} else if v != nil {\n\t\t\tprovider[\"sessionToken\"] = v\n\t\t}\n\n\t\tfor _, e := range []struct {\n\t\t\tkey    string\n\t\t\tenvVar string\n\t\t}{\n\t\t\t{key: \"accessKeyId\", envVar: accessKeyID},\n\t\t\t{key: \"secretAccessKey\", envVar: secretAccessKey},\n\t\t} {\n\t\t\tv, err = getKmsCredential(opt, e.key, e.envVar, \"\")\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tif v != nil {\n\t\t\t\tprovider[e.key] = v\n\t\t\t}\n\t\t}\n\tcase \"azure\":\n\t\tfor _, e := range []struct {\n\t\t\tkey    string\n\t\t\tenvVar string\n\t\t}{\n\t\t\t{key: \"tenantId\", envVar: \"FLE_AZURE_TENANTID\"},\n\t\t\t{key: \"clientId\", envVar: \"FLE_AZURE_CLIENTID\"},\n\t\t\t{key: \"clientSecret\", envVar: \"FLE_AZURE_CLIENTSECRET\"},\n\t\t} {\n\t\t\tv, err := getKmsCredential(opt, e.key, e.envVar, \"\")\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tif v != nil {\n\t\t\t\tprovider[e.key] = v\n\t\t\t}\n\t\t}\n\tcase \"gcp\":\n\t\tfor _, e := range []struct {\n\t\t\tkey    string\n\t\t\tenvVar string\n\t\t}{\n\t\t\t{key: \"email\", envVar: \"FLE_GCP_EMAIL\"},\n\t\t\t{key: \"privateKey\", envVar: \"FLE_GCP_PRIVATEKEY\"},\n\t\t} {\n\t\t\tv, err := getKmsCredential(opt, e.key, e.envVar, \"\")\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tif v != nil {\n\t\t\t\tprovider[e.key] = v\n\t\t\t}\n\t\t}\n\tcase \"kmip\":\n\t\tv, err := getKmsCredential(opt, \"endpoint\", \"\", \"localhost:5698\")\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif v != nil {\n\t\t\tprovider[\"endpoint\"] = v\n\t\t}\n\tcase \"local\", \"local:name2\":\n\t\tv, err := getKmsCredential(opt, \"key\", \"\", defaultLocalKeyBase64)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif v != nil {\n\t\t\tprovider[\"key\"] = v\n\t\t}\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"unrecognized KMS provider: %s\", key)\n\t}\n\treturn provider, nil\n}\n\nfunc (em *EntityMap) addClientEncryptionEntity(entityOptions *entityOptions) error {\n\t// Construct KMS providers.\n\tkmsProviders := make(map[string]map[string]any)\n\tceo := entityOptions.ClientEncryptionOpts\n\ttlsconf := make(map[string]*tls.Config)\n\tfor key, opt := range ceo.KmsProviders {\n\t\tprovider, err := getKmsProvider(key, opt)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif len(provider) == 0 {\n\t\t\tcontinue\n\t\t}\n\t\tkmsProviders[key] = provider\n\t\tif key == \"kmip\" && tlsClientCertificateKeyFile != \"\" && tlsCAFile != \"\" {\n\t\t\tcfg, err := options.BuildTLSConfig(map[string]any{\n\t\t\t\t\"tlsCertificateKeyFile\": tlsClientCertificateKeyFile,\n\t\t\t\t\"tlsCAFile\":             tlsCAFile,\n\t\t\t})\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"error constructing tls config: %w\", err)\n\t\t\t}\n\t\t\ttlsconf[\"kmip\"] = cfg\n\t\t}\n\t}\n\n\tem.keyVaultClientIDs[ceo.KeyVaultClient] = true\n\tkeyVaultClient, ok := em.clientEntities[ceo.KeyVaultClient]\n\tif !ok {\n\t\treturn newEntityNotFoundError(\"client\", ceo.KeyVaultClient)\n\t}\n\n\topts := options.ClientEncryption().\n\t\tSetKeyVaultNamespace(ceo.KeyVaultNamespace).\n\t\tSetTLSConfig(tlsconf).\n\t\tSetKmsProviders(kmsProviders)\n\tif ceo.KeyExpirationMS != nil {\n\t\topts.SetKeyExpiration(time.Duration(*ceo.KeyExpirationMS) * time.Millisecond)\n\t}\n\n\tce, err := mongo.NewClientEncryption(keyVaultClient.Client, opts)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tem.clientEncryptionEntities[entityOptions.ID] = ce\n\n\treturn nil\n}\n\nfunc (em *EntityMap) addCollectionEntity(entityOptions *entityOptions) error {\n\tdb, ok := em.dbEntites[entityOptions.DatabaseID]\n\tif !ok {\n\t\treturn newEntityNotFoundError(\"database\", entityOptions.DatabaseID)\n\t}\n\n\tcollOpts := options.Collection()\n\tif entityOptions.CollectionOptions != nil {\n\t\tcollOpts = entityOptions.CollectionOptions.CollectionOptions\n\t}\n\n\tem.collEntities[entityOptions.ID] = db.Collection(entityOptions.CollectionName, collOpts)\n\treturn nil\n}\n\nfunc (em *EntityMap) addSessionEntity(entityOptions *entityOptions) error {\n\tclient, ok := em.clientEntities[entityOptions.ClientID]\n\tif !ok {\n\t\treturn newEntityNotFoundError(\"client\", entityOptions.ClientID)\n\t}\n\n\tsessionOpts := options.Session()\n\tif entityOptions.SessionOptions != nil {\n\t\tsessionOpts = entityOptions.SessionOptions.SessionOptionsBuilder\n\n\t\t// Resolve snapshot time from EntityMap if specified\n\t\tif entityOptions.SessionOptions.snapshotTimeID != nil {\n\t\t\tsnapshotTimeID := *entityOptions.SessionOptions.snapshotTimeID\n\t\t\tRawTS, err := em.BSONValue(snapshotTimeID)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"error retrieving snapshot time for entity %q: %w\", snapshotTimeID, err)\n\t\t\t}\n\n\t\t\tt, i, ok := RawTS.TimestampOK()\n\t\t\tif !ok {\n\t\t\t\treturn fmt.Errorf(\"snapshot time entity %q is not a timestamp\", snapshotTimeID)\n\t\t\t}\n\n\t\t\tsessionOpts.SetSnapshotTime(bson.Timestamp{T: t, I: i})\n\t\t}\n\t}\n\n\tsess, err := client.StartSession(sessionOpts)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"error starting session: %w\", err)\n\t}\n\n\tem.sessions[entityOptions.ID] = sess\n\treturn nil\n}\n\nfunc (em *EntityMap) addGridFSBucketEntity(entityOptions *entityOptions) error {\n\tdb, ok := em.dbEntites[entityOptions.DatabaseID]\n\tif !ok {\n\t\treturn newEntityNotFoundError(\"database\", entityOptions.DatabaseID)\n\t}\n\n\tbucketOpts := options.GridFSBucket()\n\tif entityOptions.GridFSBucketOptions != nil {\n\t\tbucketOpts = entityOptions.GridFSBucketOptions.BucketOptionsBuilder\n\t}\n\n\tem.gridfsBuckets[entityOptions.ID] = db.GridFSBucket(bucketOpts)\n\treturn nil\n}\n\nfunc (em *EntityMap) verifyEntityDoesNotExist(id string) error {\n\tif _, ok := em.allEntities[id]; ok {\n\t\treturn fmt.Errorf(\"entity with ID %q already exists\", id)\n\t}\n\treturn nil\n}\n\nfunc newEntityNotFoundError(entityType, entityID string) error {\n\treturn fmt.Errorf(\"no %s entity found with ID %q\", entityType, entityID)\n}\n"
  },
  {
    "path": "internal/integration/unified/entity_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage unified\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n)\n\nfunc TestEntityMap(t *testing.T) {\n\trecord := bson.D{{\"foo\", 1}}\n\tdoc, err := bson.Marshal(record)\n\tassert.Nil(t, err, \"error marshaling example doc %v\", err)\n\tt.Run(\"bson array entity\", func(t *testing.T) {\n\t\tname := \"errors\"\n\t\tnotFoundName := \"failures\"\n\t\tnotFoundErr := newEntityNotFoundError(\"BSON array\", \"failures\")\n\t\tem := newEntityMap()\n\n\t\terr = em.addBSONArrayEntity(name)\n\t\tassert.Nil(t, err, \"addBSONArrayEntity error: %v\", err)\n\t\t// adding an existing bson array entity twice shouldn't error\n\t\terr = em.addBSONArrayEntity(name)\n\t\tassert.Nil(t, err, \"addBSONArrayEntity error: %v\", err)\n\n\t\terr = em.appendBSONArrayEntity(name, doc)\n\t\tassert.Nil(t, err, \"appendBSONArrayEntity error: %v\", err)\n\n\t\terr = em.appendBSONArrayEntity(notFoundName, doc)\n\t\tassert.Equal(t, err, notFoundErr, \"expected error %v, got %v\", notFoundErr, err)\n\n\t\t// bson array can't be retrieved until the map is closed\n\t\t_, err = em.BSONArray(name)\n\t\tassert.Equal(t, err, ErrEntityMapOpen, \"expected error %v, got %v\", ErrEntityMapOpen, err)\n\n\t\tem.close(context.Background())\n\n\t\tretDocs, err := em.BSONArray(name)\n\t\tassert.Nil(t, err, \"BSONArray error: %v\", err)\n\t\tassert.Equal(t, bson.Raw(doc), retDocs[0], \"expected %s, got %s\", bson.Raw(doc), retDocs[0])\n\n\t\t_, err = em.BSONArray(notFoundName)\n\t\tassert.Equal(t, err, notFoundErr, \"expected error %v, got %v\", notFoundErr, err)\n\t})\n\tt.Run(\"events entity\", func(t *testing.T) {\n\t\tname := \"events\"\n\t\tem := newEntityMap()\n\t\terr = em.addEventsEntity(name)\n\t\tassert.Nil(t, err, \"addEventsEntity error: %v\", err)\n\t\terr = em.addEventsEntity(name)\n\t\tassert.NotNil(t, err, \"expected error for duplicate entity name\")\n\n\t\tem.appendEventsEntity(name, doc)\n\n\t\t// Events can't be retrieved until the map is closed\n\t\t_, err := em.EventList(name)\n\t\tassert.Equal(t, err, ErrEntityMapOpen, \"expected error %v, got %v\", ErrEntityMapOpen, err)\n\n\t\tem.close(context.Background())\n\n\t\tretDocs, err := em.EventList(name)\n\t\tassert.Nil(t, err, \"EventList error: %v\", err)\n\t\tassert.Equal(t, bson.Raw(doc), retDocs[0], \"expected %s, got %s\", bson.Raw(doc), retDocs[0])\n\n\t\t_, err = em.EventList(\"bar\")\n\t\tnotFoundErr := newEntityNotFoundError(\"event list\", \"bar\")\n\t\tassert.Equal(t, err, notFoundErr, \"expected error %v, got %v\", notFoundErr, err)\n\t})\n\tt.Run(\"iterations entity\", func(t *testing.T) {\n\t\tname := \"iters\"\n\t\tnotFoundName := \"bar\"\n\t\tnotFoundErr := newEntityNotFoundError(\"iterations\", \"bar\")\n\t\tem := newEntityMap()\n\t\terr = em.addIterationsEntity(name)\n\t\tassert.Nil(t, err, \"addIterationsEntity error: %v\", err)\n\t\terr = em.addIterationsEntity(name)\n\t\tassert.NotNil(t, err, \"expected error for duplicate entity name\")\n\n\t\terr = em.incrementIterations(name)\n\t\tassert.Nil(t, err, \"incrementIterations error: %v\", err)\n\n\t\terr = em.incrementIterations(notFoundName)\n\t\tassert.Equal(t, err, notFoundErr, \"expected error %v, got %v\", notFoundErr, err)\n\n\t\tretVal, err := em.Iterations(name)\n\t\tassert.Nil(t, err, \"expected nil error, got %v\", err)\n\t\tassert.Equal(t, int32(1), retVal, \"expected %v, got %v\", int32(1), retVal)\n\n\t\t_, err = em.Iterations(notFoundName)\n\t\tassert.Equal(t, err, notFoundErr, \"expected error %v, got %v\", notFoundErr, err)\n\t})\n\tt.Run(\"successes entity\", func(t *testing.T) {\n\t\tname := \"successes\"\n\t\tnotFoundName := \"bar\"\n\t\tnotFoundErr := newEntityNotFoundError(\"successes\", \"bar\")\n\t\tem := newEntityMap()\n\t\terr = em.addSuccessesEntity(name)\n\t\tassert.Nil(t, err, \"addSuccessesEntity error: %v\", err)\n\t\terr = em.addSuccessesEntity(name)\n\t\tassert.NotNil(t, err, \"expected error for duplicate entity name\")\n\n\t\terr = em.incrementSuccesses(name)\n\t\tassert.Nil(t, err, \"incrementSuccesses error: %v\", err)\n\n\t\terr = em.incrementSuccesses(notFoundName)\n\t\tassert.Equal(t, err, notFoundErr, \"expected error %v, got %v\", notFoundErr, err)\n\n\t\tretVal, err := em.Successes(name)\n\t\tassert.Nil(t, err, \"Successes error: %v\", err)\n\t\tassert.Equal(t, int32(1), retVal, \"expected %v, got %v\", int32(1), retVal)\n\n\t\t_, err = em.Successes(notFoundName)\n\t\tassert.Equal(t, err, notFoundErr, \"expected error %v, got %v\", notFoundErr, err)\n\t})\n}\n"
  },
  {
    "path": "internal/integration/unified/error.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage unified\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"strings\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo\"\n)\n\n// expectedError represents an error that is expected to occur during a test. This type ignores the \"isError\" field in\n// test files because it is always true if it is specified, so the runner can simply assert that an error occurred.\ntype expectedError struct {\n\tIsClientError      *bool                            `bson:\"isClientError\"`\n\tIsTimeoutError     *bool                            `bson:\"isTimeoutError\"`\n\tErrorSubstring     *string                          `bson:\"errorContains\"`\n\tCode               *int32                           `bson:\"errorCode\"`\n\tCodeName           *string                          `bson:\"errorCodeName\"`\n\tIncludedLabels     []string                         `bson:\"errorLabelsContain\"`\n\tOmittedLabels      []string                         `bson:\"errorLabelsOmit\"`\n\tExpectedResult     *bson.RawValue                   `bson:\"expectResult\"`\n\tErrorResponse      *bson.Raw                        `bson:\"errorResponse\"`\n\tWriteErrors        map[int]clientBulkWriteException `bson:\"writeErrors\"`\n\tWriteConcernErrors []clientBulkWriteException       `bson:\"writeConcernErrors\"`\n}\n\ntype clientBulkWriteException struct {\n\tCode    *int    `bson:\"code\"`\n\tMessage *string `bson:\"message\"`\n}\n\n// verifyOperationError compares the expected error to the actual operation result. If the expected parameter is nil,\n// this function will only check that result.Err is also nil. Otherwise, it will check that result.Err is non-nil and\n// will perform any other assertions required by the expectedError object. An error is returned if any checks fail.\nfunc verifyOperationError(ctx context.Context, expected *expectedError, result *operationResult) error {\n\tif expected == nil {\n\t\tif result != nil && result.Err != nil {\n\t\t\treturn fmt.Errorf(\"expected no error, but got %w\", result.Err)\n\t\t}\n\t\treturn nil\n\t}\n\n\tif result.Err == nil {\n\t\treturn fmt.Errorf(\"expected error, got nil\")\n\t}\n\n\t// Check ErrorSubstring for both client and server-side errors.\n\tif expected.ErrorSubstring != nil {\n\t\t// Lowercase the error messages because Go error messages always start with lowercase letters, so they may\n\t\t// not match the casing used in specs.\n\t\texpectedErrMsg := strings.ToLower(*expected.ErrorSubstring)\n\t\tactualErrMsg := strings.ToLower(result.Err.Error())\n\t\tif !strings.Contains(actualErrMsg, expectedErrMsg) {\n\t\t\treturn fmt.Errorf(\"expected error %w to contain substring %s\", result.Err, *expected.ErrorSubstring)\n\t\t}\n\t}\n\n\t// extractErrorDetails will only succeed for server errors, so it's \"ok\" return value can be used to determine\n\t// if we got a server or client-side error.\n\tdetails, serverError := extractErrorDetails(result.Err)\n\tif expected.IsClientError != nil {\n\t\t// The unified test format spec considers network errors to be client-side errors.\n\t\tisClientError := !serverError || mongo.IsNetworkError(result.Err)\n\t\tif *expected.IsClientError != isClientError {\n\t\t\treturn fmt.Errorf(\"expected error %w to be a client error: %v, is client error: %v\", result.Err,\n\t\t\t\t*expected.IsClientError, isClientError)\n\t\t}\n\t}\n\tif expected.IsTimeoutError != nil {\n\t\tisTimeoutError := mongo.IsTimeout(result.Err)\n\t\tif *expected.IsTimeoutError != isTimeoutError {\n\t\t\treturn fmt.Errorf(\"expected error %w to be a timeout error: %v, is timeout error: %v\", result.Err,\n\t\t\t\t*expected.IsTimeoutError, isTimeoutError)\n\t\t}\n\t}\n\tif !serverError {\n\t\t// Error if extractErrorDetails failed but the test requires assertions about server error details.\n\t\tif expected.Code != nil || expected.CodeName != nil || expected.IncludedLabels != nil || expected.OmittedLabels != nil {\n\t\t\treturn fmt.Errorf(\"failed to extract details from error %v of type %T\", result.Err, result.Err)\n\t\t}\n\t}\n\n\tif expected.Code != nil {\n\t\tvar found bool\n\t\tfor _, code := range details.codes {\n\t\t\tif code == *expected.Code {\n\t\t\t\tfound = true\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tif !found {\n\t\t\treturn fmt.Errorf(\"expected error %w to have code %d\", result.Err, *expected.Code)\n\t\t}\n\t}\n\tif expected.CodeName != nil {\n\t\tvar found bool\n\t\tfor _, codeName := range details.codeNames {\n\t\t\tif codeName == *expected.CodeName {\n\t\t\t\tfound = true\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tif !found {\n\t\t\treturn fmt.Errorf(\"expected error %w to have code name %q\", result.Err, *expected.CodeName)\n\t\t}\n\t}\n\tfor _, label := range expected.IncludedLabels {\n\t\tif !stringSliceContains(details.labels, label) {\n\t\t\treturn fmt.Errorf(\"expected error %w to contain label %q\", result.Err, label)\n\t\t}\n\t}\n\tfor _, label := range expected.OmittedLabels {\n\t\tif stringSliceContains(details.labels, label) {\n\t\t\treturn fmt.Errorf(\"expected error %w to not contain label %q\", result.Err, label)\n\t\t}\n\t}\n\n\tif expected.ExpectedResult != nil {\n\t\tif err := verifyOperationResult(ctx, *expected.ExpectedResult, result); err != nil {\n\t\t\treturn fmt.Errorf(\"result comparison error: %w\", err)\n\t\t}\n\t}\n\n\tif expected.ErrorResponse != nil {\n\t\tif details.raw == nil {\n\t\t\treturn fmt.Errorf(\"expected error response from the server, got none\")\n\t\t}\n\n\t\t// Allow extra keys as 'errorResponse' functions like a root-level document.\n\t\tgotValue := documentToRawValue(details.raw)\n\t\texpectedValue := documentToRawValue(*expected.ErrorResponse)\n\t\tif err := verifyValuesMatch(ctx, expectedValue, gotValue, true); err != nil {\n\t\t\treturn fmt.Errorf(\"error response comparison error: %w\", err)\n\t\t}\n\t}\n\tif expected.WriteErrors != nil {\n\t\tvar exception mongo.ClientBulkWriteException\n\t\tif !errors.As(result.Err, &exception) {\n\t\t\treturn fmt.Errorf(\"expected a ClientBulkWriteException, got %T: %v\", result.Err, result.Err)\n\t\t}\n\t\tif len(expected.WriteErrors) != len(exception.WriteErrors) {\n\t\t\treturn fmt.Errorf(\"expected errors: %v, got: %v\", expected.WriteErrors, exception.WriteErrors)\n\t\t}\n\t\tfor k, e := range expected.WriteErrors {\n\t\t\tif e.Code != nil && *e.Code != exception.WriteErrors[k].Code {\n\t\t\t\treturn fmt.Errorf(\"expected errors: %v, got: %v\", expected.WriteConcernErrors, exception.WriteConcernErrors)\n\t\t\t}\n\t\t\tif e.Message != nil && *e.Message != exception.WriteErrors[k].Message {\n\t\t\t\treturn fmt.Errorf(\"expected errors: %v, got: %v\", expected.WriteConcernErrors, exception.WriteConcernErrors)\n\t\t\t}\n\t\t}\n\t}\n\tif expected.WriteConcernErrors != nil {\n\t\tvar exception mongo.ClientBulkWriteException\n\t\tif !errors.As(result.Err, &exception) {\n\t\t\treturn fmt.Errorf(\"expected a ClientBulkWriteException, got %T: %v\", result.Err, result.Err)\n\t\t}\n\t\tif len(expected.WriteConcernErrors) != len(exception.WriteConcernErrors) {\n\t\t\treturn fmt.Errorf(\"expected errors: %v, got: %v\", expected.WriteConcernErrors, exception.WriteConcernErrors)\n\t\t}\n\t\tfor i, e := range expected.WriteConcernErrors {\n\t\t\tif e.Code != nil && *e.Code != exception.WriteConcernErrors[i].Code {\n\t\t\t\treturn fmt.Errorf(\"expected errors: %v, got: %v\", expected.WriteConcernErrors, exception.WriteConcernErrors)\n\t\t\t}\n\t\t\tif e.Message != nil && *e.Message != exception.WriteConcernErrors[i].Message {\n\t\t\t\treturn fmt.Errorf(\"expected errors: %v, got: %v\", expected.WriteConcernErrors, exception.WriteConcernErrors)\n\t\t\t}\n\t\t}\n\t}\n\treturn nil\n}\n\n// errorDetails consolidates information from different server error types.\ntype errorDetails struct {\n\tcodes     []int32\n\tcodeNames []string\n\tlabels    []string\n\traw       bson.Raw\n}\n\n// extractErrorDetails creates an errorDetails instance based on the provided error. It returns the details and an \"ok\"\n// value which is true if the provided error is of a known type that can be processed.\nfunc extractErrorDetails(err error) (errorDetails, bool) {\n\tvar details errorDetails\n\n\tswitch converted := err.(type) {\n\tcase mongo.CommandError:\n\t\tdetails.codes = []int32{converted.Code}\n\t\tdetails.codeNames = []string{converted.Name}\n\t\tdetails.labels = converted.Labels\n\t\tdetails.raw = converted.Raw\n\tcase mongo.WriteException:\n\t\tif converted.WriteConcernError != nil {\n\t\t\tdetails.codes = append(details.codes, int32(converted.WriteConcernError.Code))\n\t\t\tdetails.codeNames = append(details.codeNames, converted.WriteConcernError.Name)\n\t\t}\n\t\tfor _, we := range converted.WriteErrors {\n\t\t\tdetails.codes = append(details.codes, int32(we.Code))\n\t\t}\n\t\tdetails.labels = converted.Labels\n\t\tdetails.raw = converted.Raw\n\tcase mongo.BulkWriteException:\n\t\tif converted.WriteConcernError != nil {\n\t\t\tdetails.codes = append(details.codes, int32(converted.WriteConcernError.Code))\n\t\t\tdetails.codeNames = append(details.codeNames, converted.WriteConcernError.Name)\n\t\t}\n\t\tfor _, we := range converted.WriteErrors {\n\t\t\tdetails.codes = append(details.codes, int32(we.Code))\n\t\t\tdetails.raw = we.Raw\n\t\t}\n\t\tdetails.labels = converted.Labels\n\tcase mongo.ClientBulkWriteException:\n\t\tif converted.WriteError != nil {\n\t\t\tdetails.raw = converted.WriteError.Raw\n\t\t\tdetails.codes = append(details.codes, int32(converted.WriteError.Code))\n\t\t}\n\tdefault:\n\t\treturn errorDetails{}, false\n\t}\n\n\treturn details, true\n}\n\nfunc stringSliceContains(arr []string, target string) bool {\n\tfor _, val := range arr {\n\t\tif val == target {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n"
  },
  {
    "path": "internal/integration/unified/event.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage unified\n\nimport (\n\t\"strings\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/event\"\n)\n\ntype monitoringEventType string\n\nconst (\n\tcommandStartedEvent             monitoringEventType = \"CommandStartedEvent\"\n\tcommandSucceededEvent           monitoringEventType = \"CommandSucceededEvent\"\n\tcommandFailedEvent              monitoringEventType = \"CommandFailedEvent\"\n\tpoolCreatedEvent                monitoringEventType = \"PoolCreatedEvent\"\n\tpoolReadyEvent                  monitoringEventType = \"PoolReadyEvent\"\n\tpoolClearedEvent                monitoringEventType = \"PoolClearedEvent\"\n\tpoolClosedEvent                 monitoringEventType = \"PoolClosedEvent\"\n\tconnectionCreatedEvent          monitoringEventType = \"ConnectionCreatedEvent\"\n\tconnectionReadyEvent            monitoringEventType = \"ConnectionReadyEvent\"\n\tconnectionClosedEvent           monitoringEventType = \"ConnectionClosedEvent\"\n\tconnectionCheckOutStartedEvent  monitoringEventType = \"ConnectionCheckOutStartedEvent\"\n\tconnectionCheckOutFailedEvent   monitoringEventType = \"ConnectionCheckOutFailedEvent\"\n\tconnectionCheckedOutEvent       monitoringEventType = \"ConnectionCheckedOutEvent\"\n\tconnectionCheckedInEvent        monitoringEventType = \"ConnectionCheckedInEvent\"\n\tserverDescriptionChangedEvent   monitoringEventType = \"ServerDescriptionChangedEvent\"\n\tserverHeartbeatFailedEvent      monitoringEventType = \"ServerHeartbeatFailedEvent\"\n\tserverHeartbeatStartedEvent     monitoringEventType = \"ServerHeartbeatStartedEvent\"\n\tserverHeartbeatSucceededEvent   monitoringEventType = \"ServerHeartbeatSucceededEvent\"\n\ttopologyDescriptionChangedEvent monitoringEventType = \"TopologyDescriptionChangedEvent\"\n\ttopologyOpeningEvent            monitoringEventType = \"TopologyOpeningEvent\"\n\ttopologyClosedEvent             monitoringEventType = \"TopologyClosedEvent\"\n)\n\nfunc monitoringEventTypeFromString(eventStr string) (monitoringEventType, bool) {\n\tswitch strings.ToLower(eventStr) {\n\tcase \"commandstartedevent\":\n\t\treturn commandStartedEvent, true\n\tcase \"commandsucceededevent\":\n\t\treturn commandSucceededEvent, true\n\tcase \"commandfailedevent\":\n\t\treturn commandFailedEvent, true\n\tcase \"poolcreatedevent\":\n\t\treturn poolCreatedEvent, true\n\tcase \"poolreadyevent\":\n\t\treturn poolReadyEvent, true\n\tcase \"poolclearedevent\":\n\t\treturn poolClearedEvent, true\n\tcase \"poolclosedevent\":\n\t\treturn poolClosedEvent, true\n\tcase \"connectioncreatedevent\":\n\t\treturn connectionCreatedEvent, true\n\tcase \"connectionreadyevent\":\n\t\treturn connectionReadyEvent, true\n\tcase \"connectionclosedevent\":\n\t\treturn connectionClosedEvent, true\n\tcase \"connectioncheckoutstartedevent\":\n\t\treturn connectionCheckOutStartedEvent, true\n\tcase \"connectioncheckoutfailedevent\":\n\t\treturn connectionCheckOutFailedEvent, true\n\tcase \"connectioncheckedoutevent\":\n\t\treturn connectionCheckedOutEvent, true\n\tcase \"connectioncheckedinevent\":\n\t\treturn connectionCheckedInEvent, true\n\tcase \"serverdescriptionchangedevent\":\n\t\treturn serverDescriptionChangedEvent, true\n\tcase \"serverheartbeatfailedevent\":\n\t\treturn serverHeartbeatFailedEvent, true\n\tcase \"serverheartbeatstartedevent\":\n\t\treturn serverHeartbeatStartedEvent, true\n\tcase \"serverheartbeatsucceededevent\":\n\t\treturn serverHeartbeatSucceededEvent, true\n\tcase \"topologydescriptionchangedevent\":\n\t\treturn topologyDescriptionChangedEvent, true\n\tcase \"topologyopeningevent\":\n\t\treturn topologyOpeningEvent, true\n\tcase \"topologyclosedevent\":\n\t\treturn topologyClosedEvent, true\n\tdefault:\n\t\treturn \"\", false\n\t}\n}\n\nfunc monitoringEventTypeFromPoolEvent(evt *event.PoolEvent) monitoringEventType {\n\tswitch evt.Type {\n\tcase event.ConnectionPoolCreated:\n\t\treturn poolCreatedEvent\n\tcase event.ConnectionPoolReady:\n\t\treturn poolReadyEvent\n\tcase event.ConnectionPoolCleared:\n\t\treturn poolClearedEvent\n\tcase event.ConnectionPoolClosed:\n\t\treturn poolClosedEvent\n\tcase event.ConnectionCreated:\n\t\treturn connectionCreatedEvent\n\tcase event.ConnectionReady:\n\t\treturn connectionReadyEvent\n\tcase event.ConnectionClosed:\n\t\treturn connectionClosedEvent\n\tcase event.ConnectionCheckOutStarted:\n\t\treturn connectionCheckOutStartedEvent\n\tcase event.ConnectionCheckOutFailed:\n\t\treturn connectionCheckOutFailedEvent\n\tcase event.ConnectionCheckedOut:\n\t\treturn connectionCheckedOutEvent\n\tcase event.ConnectionCheckedIn:\n\t\treturn connectionCheckedInEvent\n\tdefault:\n\t\treturn \"\"\n\t}\n}\n\n// serverDescription represents a description of a server.\ntype serverDescription struct {\n\t// Type is the type of the server in the description. Test runners MUST\n\t// assert that the type in the published event matches this value.\n\tType string\n}\n\n// serverDescriptionChangedEventInfo represents an event generated when the server\n// description changes.\ntype serverDescriptionChangedEventInfo struct {\n\t// NewDescription  corresponds to the server description as it was after\n\t// the change that triggered this event.\n\tNewDescription *serverDescription\n\n\t// PreviousDescription corresponds to the server description as it was\n\t// before the change that triggered this event\n\tPreviousDescription *serverDescription\n}\n\n// newServerDescriptionChangedEventInfo returns a new serverDescriptionChangedEvent\n// instance for the given event.\nfunc newServerDescriptionChangedEventInfo(evt *event.ServerDescriptionChangedEvent) *serverDescriptionChangedEventInfo {\n\treturn &serverDescriptionChangedEventInfo{\n\t\tNewDescription: &serverDescription{\n\t\t\tType: evt.NewDescription.Kind,\n\t\t},\n\t\tPreviousDescription: &serverDescription{\n\t\t\tType: evt.PreviousDescription.Kind,\n\t\t},\n\t}\n}\n\n// UnmarshalBSON unmarshals the event from BSON, used when trying to create the\n// expected event from a unified spec test.\nfunc (evt *serverDescriptionChangedEventInfo) UnmarshalBSON(data []byte) error {\n\tif len(data) == 0 {\n\t\treturn nil\n\t}\n\n\tvar raw bson.Raw\n\tif err := bson.Unmarshal(data, &raw); err != nil {\n\t\treturn err\n\t}\n\n\t// Lookup the previous description, if any.\n\tif newDescription, err := raw.LookupErr(\"newDescription\"); err == nil {\n\t\tevt.NewDescription = &serverDescription{\n\t\t\tType: newDescription.Document().Lookup(\"type\").StringValue(),\n\t\t}\n\t}\n\n\t// Lookup the previous description, if any.\n\tif previousDescription, err := raw.LookupErr(\"previousDescription\"); err == nil {\n\t\tevt.PreviousDescription = &serverDescription{\n\t\t\tType: previousDescription.Document().Lookup(\"type\").StringValue(),\n\t\t}\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "internal/integration/unified/event_verification.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage unified\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/event\"\n)\n\ntype commandMonitoringEvent struct {\n\tCommandStartedEvent *struct {\n\t\tCommand               bson.Raw `bson:\"command\"`\n\t\tCommandName           *string  `bson:\"commandName\"`\n\t\tDatabaseName          *string  `bson:\"databaseName\"`\n\t\tHasServerConnectionID *bool    `bson:\"hasServerConnectionId\"`\n\t\tHasServiceID          *bool    `bson:\"hasServiceId\"`\n\t} `bson:\"commandStartedEvent\"`\n\n\tCommandSucceededEvent *struct {\n\t\tCommandName           *string  `bson:\"commandName\"`\n\t\tDatabaseName          *string  `bson:\"databaseName\"`\n\t\tReply                 bson.Raw `bson:\"reply\"`\n\t\tHasServerConnectionID *bool    `bson:\"hasServerConnectionId\"`\n\t\tHasServiceID          *bool    `bson:\"hasServiceId\"`\n\t} `bson:\"commandSucceededEvent\"`\n\n\tCommandFailedEvent *struct {\n\t\tCommandName           *string `bson:\"commandName\"`\n\t\tDatabaseName          *string `bson:\"databaseName\"`\n\t\tHasServerConnectionID *bool   `bson:\"hasServerConnectionId\"`\n\t\tHasServiceID          *bool   `bson:\"hasServiceId\"`\n\t} `bson:\"commandFailedEvent\"`\n}\n\ntype cmapEvent struct {\n\tConnectionCreatedEvent *struct{} `bson:\"connectionCreatedEvent\"`\n\n\tConnectionReadyEvent *struct{} `bson:\"connectionReadyEvent\"`\n\n\tConnectionClosedEvent *struct {\n\t\tReason *string `bson:\"reason\"`\n\t} `bson:\"connectionClosedEvent\"`\n\n\tConnectionCheckedOutEvent *struct{} `bson:\"connectionCheckedOutEvent\"`\n\n\tConnectionCheckOutFailedEvent *struct {\n\t\tReason *string `bson:\"reason\"`\n\t} `bson:\"connectionCheckOutFailedEvent\"`\n\n\tConnectionCheckedInEvent *struct{} `bson:\"connectionCheckedInEvent\"`\n\n\tPoolClearedEvent *struct {\n\t\tHasServiceID              *bool `bson:\"hasServiceId\"`\n\t\tInterruptInUseConnections *bool `bson:\"interruptInUseConnections\"`\n\t} `bson:\"poolClearedEvent\"`\n}\n\ntype sdamEvent struct {\n\tServerDescriptionChangedEvent *struct {\n\t\tNewDescription *struct {\n\t\t\tType *string `bson:\"type\"`\n\t\t} `bson:\"newDescription\"`\n\n\t\tPreviousDescription *struct {\n\t\t\tType *string `bson:\"type\"`\n\t\t} `bson:\"previousDescription\"`\n\t} `bson:\"serverDescriptionChangedEvent\"`\n\n\tServerHeartbeatStartedEvent *struct {\n\t\tAwaited *bool `bson:\"awaited\"`\n\t} `bson:\"serverHeartbeatStartedEvent\"`\n\n\tServerHeartbeatSucceededEvent *struct {\n\t\tAwaited *bool `bson:\"awaited\"`\n\t} `bson:\"serverHeartbeatSucceededEvent\"`\n\n\tServerHeartbeatFailedEvent *struct {\n\t\tAwaited *bool `bson:\"awaited\"`\n\t} `bson:\"serverHeartbeatFailedEvent\"`\n\n\tTopologyDescriptionChangedEvent *struct {\n\t\tPreviousDescription *struct {\n\t\t\tType *string `bson:\"type\"`\n\t\t} `bson:\"previousDescription\"`\n\t\tNewDescription *struct {\n\t\t\tType *string `bson:\"type\"`\n\t\t} `bson:\"newDescription\"`\n\t} `bson:\"topologyDescriptionChangedEvent\"`\n\tTopologyOpeningEvent *struct{} `bson:\"topologyOpeningEvent\"`\n\tTopologyClosedEvent  *struct{} `bson:\"topologyClosedEvent\"`\n}\n\ntype expectedEvents struct {\n\tClientID          string `bson:\"client\"`\n\tCommandEvents     []commandMonitoringEvent\n\tCMAPEvents        []cmapEvent\n\tSDAMEvents        []sdamEvent\n\tIgnoreExtraEvents *bool\n}\n\nvar _ bson.Unmarshaler = (*expectedEvents)(nil)\n\nfunc (e *expectedEvents) UnmarshalBSON(data []byte) error {\n\t// The data to be unmarshalled looks like {client: <client ID>, eventType: <string>, events: [event0, event1, ...]}.\n\t// We use the \"eventType\" value to determine which struct field should be used to deserialize the \"events\" array.\n\n\tvar temp struct {\n\t\tClientID          string         `bson:\"client\"`\n\t\tEventType         string         `bson:\"eventType\"`\n\t\tEvents            bson.RawValue  `bson:\"events\"`\n\t\tIgnoreExtraEvents *bool          `bson:\"ignoreExtraEvents\"`\n\t\tExtra             map[string]any `bson:\",inline\"`\n\t}\n\tif err := bson.Unmarshal(data, &temp); err != nil {\n\t\treturn fmt.Errorf(\"error unmarshalling to temporary expectedEvents object: %w\", err)\n\t}\n\tif len(temp.Extra) > 0 {\n\t\treturn fmt.Errorf(\"unrecognized fields for expectedEvents: %v\", temp.Extra)\n\t}\n\n\te.ClientID = temp.ClientID\n\tif temp.Events.Type != bson.TypeArray {\n\t\treturn fmt.Errorf(\"expected 'events' to be an array but got a %q\", temp.Events.Type)\n\t}\n\n\tvar target any\n\tswitch temp.EventType {\n\tcase \"command\", \"\":\n\t\ttarget = &e.CommandEvents\n\tcase \"cmap\":\n\t\ttarget = &e.CMAPEvents\n\tcase \"sdam\":\n\t\ttarget = &e.SDAMEvents\n\tdefault:\n\t\treturn fmt.Errorf(\"unrecognized 'eventType' value for expectedEvents: %q\", temp.EventType)\n\t}\n\n\tif err := temp.Events.Unmarshal(target); err != nil {\n\t\treturn fmt.Errorf(\"error unmarshalling events array: %w\", err)\n\t}\n\n\tif temp.IgnoreExtraEvents != nil {\n\t\te.IgnoreExtraEvents = temp.IgnoreExtraEvents\n\t}\n\treturn nil\n}\n\nfunc verifyEvents(ctx context.Context, expectedEvents *expectedEvents) error {\n\tclient, err := entities(ctx).client(expectedEvents.ClientID)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tswitch {\n\tcase expectedEvents.CommandEvents != nil:\n\t\treturn verifyCommandEvents(ctx, client, expectedEvents)\n\tcase expectedEvents.CMAPEvents != nil:\n\t\treturn verifyCMAPEvents(client, expectedEvents)\n\tcase expectedEvents.SDAMEvents != nil:\n\t\treturn verifySDAMEvents(client, expectedEvents)\n\t}\n\treturn nil\n}\n\nfunc verifyCommandEvents(ctx context.Context, client *clientEntity, expectedEvents *expectedEvents) error {\n\tstarted := client.startedEvents()\n\tsucceeded := client.succeededEvents()\n\tfailed := client.failedEvents()\n\n\t// If the Events array is nil, verify that no events were sent.\n\tif len(expectedEvents.CommandEvents) == 0 && (len(started)+len(succeeded)+len(failed) != 0) {\n\t\treturn fmt.Errorf(\"expected no events to be sent but got %s\", stringifyEventsForClient(client))\n\t}\n\n\tfor idx, evt := range expectedEvents.CommandEvents {\n\t\tswitch {\n\t\tcase evt.CommandStartedEvent != nil:\n\t\t\tif len(started) == 0 {\n\t\t\t\treturn newEventVerificationError(idx, client, \"no CommandStartedEvent published\")\n\t\t\t}\n\n\t\t\tactual := started[0]\n\t\t\tstarted = started[1:]\n\n\t\t\texpected := evt.CommandStartedEvent\n\t\t\tif expected.CommandName != nil && *expected.CommandName != actual.CommandName {\n\t\t\t\treturn newEventVerificationError(idx, client, \"expected command name %q, got %q\", *expected.CommandName,\n\t\t\t\t\tactual.CommandName)\n\t\t\t}\n\t\t\tif expected.DatabaseName != nil && *expected.DatabaseName != actual.DatabaseName {\n\t\t\t\treturn newEventVerificationError(idx, client, \"expected database name %q, got %q\", *expected.DatabaseName,\n\t\t\t\t\tactual.DatabaseName)\n\t\t\t}\n\t\t\tif expected.Command != nil {\n\t\t\t\texpectedDoc := documentToRawValue(expected.Command)\n\t\t\t\tactualDoc := documentToRawValue(actual.Command)\n\n\t\t\t\t// If actual.Command is empty, as is the case with redacted commands,\n\t\t\t\t// verifyValuesMatch will return an error from DocumentOK() because\n\t\t\t\t// there are not enough bytes to read a document from bson.RawValue{}.\n\t\t\t\t// In the case of an empty Command, hardcode an empty bson.RawValue document.\n\t\t\t\tif len(actual.Command) == 0 {\n\t\t\t\t\temptyDoc := []byte{5, 0, 0, 0, 0}\n\t\t\t\t\tactualDoc = bson.RawValue{Type: bson.TypeEmbeddedDocument, Value: emptyDoc}\n\t\t\t\t}\n\n\t\t\t\tif err := verifyValuesMatch(ctx, expectedDoc, actualDoc, true); err != nil {\n\t\t\t\t\treturn newEventVerificationError(idx, client, \"error comparing command documents: %v\", err)\n\t\t\t\t}\n\t\t\t}\n\t\t\tif expected.HasServiceID != nil {\n\t\t\t\tif err := verifyServiceID(*expected.HasServiceID, actual.ServiceID); err != nil {\n\t\t\t\t\treturn newEventVerificationError(idx, client, \"error verifying serviceID: %v\", err)\n\t\t\t\t}\n\t\t\t}\n\t\t\tif expected.HasServerConnectionID != nil {\n\t\t\t\tif err := verifyServerConnectionID(*expected.HasServerConnectionID, actual.ServerConnectionID); err != nil {\n\t\t\t\t\treturn newEventVerificationError(idx, client, \"error verifying serverConnectionID: %v\", err)\n\t\t\t\t}\n\t\t\t}\n\t\tcase evt.CommandSucceededEvent != nil:\n\t\t\tif len(succeeded) == 0 {\n\t\t\t\treturn newEventVerificationError(idx, client, \"no CommandSucceededEvent published\")\n\t\t\t}\n\n\t\t\tactual := succeeded[0]\n\t\t\tsucceeded = succeeded[1:]\n\n\t\t\texpected := evt.CommandSucceededEvent\n\t\t\tif expected.CommandName != nil && *expected.CommandName != actual.CommandName {\n\t\t\t\treturn newEventVerificationError(idx, client, \"expected command name %q, got %q\", *expected.CommandName,\n\t\t\t\t\tactual.CommandName)\n\t\t\t}\n\t\t\tif expected.DatabaseName != nil && *expected.DatabaseName != actual.DatabaseName {\n\t\t\t\treturn newEventVerificationError(idx, client, \"expected database name %q, got %q\", *expected.DatabaseName,\n\t\t\t\t\tactual.DatabaseName)\n\t\t\t}\n\t\t\tif expected.Reply != nil {\n\t\t\t\texpectedDoc := documentToRawValue(expected.Reply)\n\t\t\t\tactualDoc := documentToRawValue(actual.Reply)\n\n\t\t\t\t// If actual.Reply is empty, as is the case with redacted replies,\n\t\t\t\t// verifyValuesMatch will return an error from DocumentOK() because\n\t\t\t\t// there are not enough bytes to read a document from bson.RawValue{}.\n\t\t\t\t// In the case of an empty Reply, hardcode an empty bson.RawValue document.\n\t\t\t\tif len(actual.Reply) == 0 {\n\t\t\t\t\temptyDoc := []byte{5, 0, 0, 0, 0}\n\t\t\t\t\tactualDoc = bson.RawValue{Type: bson.TypeEmbeddedDocument, Value: emptyDoc}\n\t\t\t\t}\n\n\t\t\t\tif err := verifyValuesMatch(ctx, expectedDoc, actualDoc, true); err != nil {\n\t\t\t\t\treturn newEventVerificationError(idx, client, \"error comparing reply documents: %v\", err)\n\t\t\t\t}\n\t\t\t}\n\t\t\tif expected.HasServiceID != nil {\n\t\t\t\tif err := verifyServiceID(*expected.HasServiceID, actual.ServiceID); err != nil {\n\t\t\t\t\treturn newEventVerificationError(idx, client, \"error verifying serviceID: %v\", err)\n\t\t\t\t}\n\t\t\t}\n\t\t\tif expected.HasServerConnectionID != nil {\n\t\t\t\tif err := verifyServerConnectionID(*expected.HasServerConnectionID, actual.ServerConnectionID); err != nil {\n\t\t\t\t\treturn newEventVerificationError(idx, client, \"error verifying serverConnectionID: %v\", err)\n\t\t\t\t}\n\t\t\t}\n\t\tcase evt.CommandFailedEvent != nil:\n\t\t\tif len(failed) == 0 {\n\t\t\t\treturn newEventVerificationError(idx, client, \"no CommandFailedEvent published\")\n\t\t\t}\n\n\t\t\tactual := failed[0]\n\t\t\tfailed = failed[1:]\n\n\t\t\texpected := evt.CommandFailedEvent\n\t\t\tif expected.CommandName != nil && *expected.CommandName != actual.CommandName {\n\t\t\t\treturn newEventVerificationError(idx, client, \"expected command name %q, got %q\", *expected.CommandName,\n\t\t\t\t\tactual.CommandName)\n\t\t\t}\n\t\t\tif expected.DatabaseName != nil && *expected.DatabaseName != actual.DatabaseName {\n\t\t\t\treturn newEventVerificationError(idx, client, \"expected database name %q, got %q\", *expected.DatabaseName,\n\t\t\t\t\tactual.DatabaseName)\n\t\t\t}\n\t\t\tif expected.HasServiceID != nil {\n\t\t\t\tif err := verifyServiceID(*expected.HasServiceID, actual.ServiceID); err != nil {\n\t\t\t\t\treturn newEventVerificationError(idx, client, \"error verifying serviceID: %v\", err)\n\t\t\t\t}\n\t\t\t}\n\t\t\tif expected.HasServerConnectionID != nil {\n\t\t\t\tif err := verifyServerConnectionID(*expected.HasServerConnectionID, actual.ServerConnectionID); err != nil {\n\t\t\t\t\treturn newEventVerificationError(idx, client, \"error verifying serverConnectionID: %v\", err)\n\t\t\t\t}\n\t\t\t}\n\t\tdefault:\n\t\t\treturn newEventVerificationError(idx, client, \"no expected event set on commandMonitoringEvent instance\")\n\t\t}\n\t}\n\n\t// Verify that there are no remaining events if IgnoreExtraEvents is unset or false.\n\tignoreExtraEvents := expectedEvents.IgnoreExtraEvents != nil && *expectedEvents.IgnoreExtraEvents\n\tif !ignoreExtraEvents && (len(started) > 0 || len(succeeded) > 0 || len(failed) > 0) {\n\t\treturn fmt.Errorf(\"extra events published; all events for client: %s\", stringifyEventsForClient(client))\n\t}\n\treturn nil\n}\n\nfunc verifyCMAPEvents(client *clientEntity, expectedEvents *expectedEvents) error {\n\tpooled := client.poolEvents()\n\tif len(expectedEvents.CMAPEvents) == 0 && len(pooled) != 0 {\n\t\treturn fmt.Errorf(\"expected no cmap events to be sent but got %s\", stringifyEventsForClient(client))\n\t}\n\n\tfor idx, evt := range expectedEvents.CMAPEvents {\n\t\tvar err error\n\n\t\tswitch {\n\t\tcase evt.ConnectionCreatedEvent != nil:\n\t\t\tif _, pooled, err = getNextPoolEvent(pooled, event.ConnectionCreated); err != nil {\n\t\t\t\treturn newEventVerificationError(idx, client, \"failed to get next pool event: %v\", err.Error())\n\t\t\t}\n\t\tcase evt.ConnectionReadyEvent != nil:\n\t\t\tif _, pooled, err = getNextPoolEvent(pooled, event.ConnectionReady); err != nil {\n\t\t\t\treturn newEventVerificationError(idx, client, \"failed to get next pool event: %v\", err.Error())\n\t\t\t}\n\t\tcase evt.ConnectionClosedEvent != nil:\n\t\t\tvar actual *event.PoolEvent\n\t\t\tif actual, pooled, err = getNextPoolEvent(pooled, event.ConnectionClosed); err != nil {\n\t\t\t\treturn newEventVerificationError(idx, client, \"failed to get next pool event: %v\", err.Error())\n\t\t\t}\n\n\t\t\tif expectedReason := evt.ConnectionClosedEvent.Reason; expectedReason != nil {\n\t\t\t\tif *expectedReason != actual.Reason {\n\t\t\t\t\treturn newEventVerificationError(idx, client, \"expected reason %q, got %q\", *expectedReason, actual.Reason)\n\t\t\t\t}\n\t\t\t}\n\t\tcase evt.ConnectionCheckedOutEvent != nil:\n\t\t\tif _, pooled, err = getNextPoolEvent(pooled, event.ConnectionCheckedOut); err != nil {\n\t\t\t\treturn newEventVerificationError(idx, client, \"failed to get next pool event: %v\", err.Error())\n\t\t\t}\n\t\tcase evt.ConnectionCheckOutFailedEvent != nil:\n\t\t\tvar actual *event.PoolEvent\n\t\t\tif actual, pooled, err = getNextPoolEvent(pooled, event.ConnectionCheckOutFailed); err != nil {\n\t\t\t\treturn newEventVerificationError(idx, client, \"failed to get next pool event: %v\", err.Error())\n\t\t\t}\n\n\t\t\tif expectedReason := evt.ConnectionCheckOutFailedEvent.Reason; expectedReason != nil {\n\t\t\t\tif *expectedReason != actual.Reason {\n\t\t\t\t\treturn newEventVerificationError(idx, client, \"expected reason %q, got %q\", *expectedReason, actual.Reason)\n\t\t\t\t}\n\t\t\t}\n\t\tcase evt.ConnectionCheckedInEvent != nil:\n\t\t\tif _, pooled, err = getNextPoolEvent(pooled, event.ConnectionCheckedIn); err != nil {\n\t\t\t\treturn newEventVerificationError(idx, client, \"failed to get next pool event: %v\", err.Error())\n\t\t\t}\n\t\tcase evt.PoolClearedEvent != nil:\n\t\t\tvar actual *event.PoolEvent\n\t\t\tif actual, pooled, err = getNextPoolEvent(pooled, event.ConnectionPoolCleared); err != nil {\n\t\t\t\treturn newEventVerificationError(idx, client, \"failed to get next pool event: %v\", err.Error())\n\t\t\t}\n\t\t\tif expectServiceID := evt.PoolClearedEvent.HasServiceID; expectServiceID != nil {\n\t\t\t\tif err := verifyServiceID(*expectServiceID, actual.ServiceID); err != nil {\n\t\t\t\t\treturn newEventVerificationError(idx, client, \"error verifying serviceID: %v\", err)\n\t\t\t\t}\n\t\t\t}\n\t\t\tif expectInterruption := evt.PoolClearedEvent.InterruptInUseConnections; expectInterruption != nil && *expectInterruption != actual.Interruption {\n\t\t\t\treturn newEventVerificationError(idx, client, \"expected interruptInUseConnections %v, got %v\",\n\t\t\t\t\texpectInterruption, actual.Interruption)\n\t\t\t}\n\t\tdefault:\n\t\t\treturn newEventVerificationError(idx, client, \"no expected event set on cmapEvent instance\")\n\t\t}\n\t}\n\n\t// Verify that there are no remaining events if ignoreExtraEvents is unset or false.\n\tignoreExtraEvents := expectedEvents.IgnoreExtraEvents != nil && *expectedEvents.IgnoreExtraEvents\n\tif !ignoreExtraEvents && len(pooled) > 0 {\n\t\treturn fmt.Errorf(\"extra events published; all events for client: %s\", stringifyEventsForClient(client))\n\t}\n\treturn nil\n}\n\nfunc getNextPoolEvent(events []*event.PoolEvent, expectedType string) (*event.PoolEvent, []*event.PoolEvent, error) {\n\tif len(events) == 0 {\n\t\treturn nil, nil, fmt.Errorf(\"no %q event published\", expectedType)\n\t}\n\n\tevt := events[0]\n\tif evt.Type != expectedType {\n\t\treturn nil, nil, fmt.Errorf(\"expected pool event of type %q, got %q\", expectedType, evt.Type)\n\t}\n\treturn evt, events[1:], nil\n}\n\nfunc verifyServiceID(expectServiceID bool, serviceID *bson.ObjectID) error {\n\tif eventHasID := serviceID != nil; expectServiceID != eventHasID {\n\t\treturn fmt.Errorf(\"expected event to have server ID: %v, event has server ID %v\", expectServiceID, serviceID)\n\t}\n\treturn nil\n}\n\nfunc verifyServerConnectionID(expectedHasSCID bool, scid *int64) error {\n\tif actualHasSCID := scid != nil; expectedHasSCID != actualHasSCID {\n\t\tif expectedHasSCID {\n\t\t\treturn fmt.Errorf(\"expected event to have server connection ID, event has none\")\n\t\t}\n\t\treturn fmt.Errorf(\"expected event to have no server connection ID, got %d\", *scid)\n\t}\n\tif expectedHasSCID && *scid <= 0 {\n\t\treturn fmt.Errorf(\"expected event to have a positive server connection ID, got %d\", *scid)\n\t}\n\treturn nil\n}\n\nfunc newEventVerificationError(idx int, client *clientEntity, msg string, args ...any) error {\n\tfullMsg := fmt.Sprintf(msg, args...)\n\treturn fmt.Errorf(\"event comparison failed at index %d: %s; all events found for client: %s\", idx, fullMsg,\n\t\tstringifyEventsForClient(client))\n}\n\nfunc stringifyEventsForClient(client *clientEntity) string {\n\tstr := bytes.NewBuffer(nil)\n\n\tstr.WriteString(\"\\n\\nStarted Events\\n\\n\")\n\tfor _, evt := range client.startedEvents() {\n\t\tfmt.Fprintf(str, \"[%s] %s\\n\", evt.ConnectionID, evt.Command)\n\t}\n\n\tstr.WriteString(\"\\nSucceeded Events\\n\\n\")\n\tfor _, evt := range client.succeededEvents() {\n\t\tfmt.Fprintf(str, \"[%s] CommandName: %s, Reply: %s\\n\", evt.ConnectionID, evt.CommandName, evt.Reply)\n\t}\n\n\tstr.WriteString(\"\\nFailed Events\\n\\n\")\n\tfor _, evt := range client.failedEvents() {\n\t\tfmt.Fprintf(str, \"[%s] CommandName: %s, Failure: %s\\n\", evt.ConnectionID, evt.CommandName, evt.Failure)\n\t}\n\n\tstr.WriteString(\"\\nPool Events\\n\\n\")\n\tfor _, evt := range client.poolEvents() {\n\t\tfmt.Fprintf(str, \"[%s] Event Type: %q\\n\", evt.Address, evt.Type)\n\t}\n\n\treturn str.String()\n}\n\nfunc getNextServerDescriptionChangedEvent(\n\tevents []*event.ServerDescriptionChangedEvent,\n) (*event.ServerDescriptionChangedEvent, []*event.ServerDescriptionChangedEvent, error) {\n\tif len(events) == 0 {\n\t\treturn nil, nil, errors.New(\"no server changed event published\")\n\t}\n\n\treturn events[0], events[1:], nil\n}\n\nfunc getNextServerHeartbeatStartedEvent(\n\tevents []*event.ServerHeartbeatStartedEvent,\n) (*event.ServerHeartbeatStartedEvent, []*event.ServerHeartbeatStartedEvent, error) {\n\tif len(events) == 0 {\n\t\treturn nil, nil, errors.New(\"no heartbeat started event published\")\n\t}\n\n\treturn events[0], events[1:], nil\n}\n\nfunc getNextServerHeartbeatSucceededEvent(\n\tevents []*event.ServerHeartbeatSucceededEvent,\n) (*event.ServerHeartbeatSucceededEvent, []*event.ServerHeartbeatSucceededEvent, error) {\n\tif len(events) == 0 {\n\t\treturn nil, nil, errors.New(\"no heartbeat succeeded event published\")\n\t}\n\n\treturn events[0], events[1:], nil\n}\n\nfunc getNextServerHeartbeatFailedEvent(\n\tevents []*event.ServerHeartbeatFailedEvent,\n) (*event.ServerHeartbeatFailedEvent, []*event.ServerHeartbeatFailedEvent, error) {\n\tif len(events) == 0 {\n\t\treturn nil, nil, errors.New(\"no heartbeat failed event published\")\n\t}\n\n\treturn events[0], events[1:], nil\n}\n\nfunc getNextTopologyDescriptionChangedEvent(\n\tevents []*event.TopologyDescriptionChangedEvent,\n) (*event.TopologyDescriptionChangedEvent, []*event.TopologyDescriptionChangedEvent, error) {\n\tif len(events) == 0 {\n\t\treturn nil, nil, errors.New(\"no topology description changed event published\")\n\t}\n\n\treturn events[0], events[1:], nil\n}\n\nfunc getNextTopologyOpeningEvent(\n\tevents []*event.TopologyOpeningEvent,\n) (*event.TopologyOpeningEvent, []*event.TopologyOpeningEvent, error) {\n\tif len(events) == 0 {\n\t\treturn nil, nil, errors.New(\"no topology opening event published\")\n\t}\n\n\treturn events[0], events[1:], nil\n}\n\nfunc getNextTopologyClosedEvent(\n\tevents []*event.TopologyClosedEvent,\n) (*event.TopologyClosedEvent, []*event.TopologyClosedEvent, error) {\n\tif len(events) == 0 {\n\t\treturn nil, nil, errors.New(\"no topology closed event published\")\n\t}\n\n\treturn events[0], events[1:], nil\n}\n\nfunc verifySDAMEvents(client *clientEntity, expectedEvents *expectedEvents) error {\n\tvar (\n\t\tchanged   = client.serverDescriptionChanged\n\t\tstarted   = client.serverHeartbeatStartedEvent\n\t\tsucceeded = client.serverHeartbeatSucceeded\n\t\tfailed    = client.serverHeartbeatFailedEvent\n\t\ttchanged  = client.topologyDescriptionChanged\n\t\ttopening  = client.topologyOpening\n\t\ttclosed   = client.topologyClosed\n\t)\n\n\tvol := func() int {\n\t\tvar count int\n\t\tcount += len(changed)\n\t\tcount += len(started)\n\t\tcount += len(succeeded)\n\t\tcount += len(failed)\n\t\tcount += len(tchanged)\n\t\tcount += len(topening)\n\t\tcount += len(tclosed)\n\t\treturn count\n\t}\n\n\tif len(expectedEvents.SDAMEvents) == 0 && vol() != 0 {\n\t\treturn fmt.Errorf(\"expected no sdam events to be sent but got %s\", stringifyEventsForClient(client))\n\t}\n\n\tfor idx, evt := range expectedEvents.SDAMEvents {\n\t\tvar err error\n\n\t\tswitch {\n\t\tcase evt.ServerDescriptionChangedEvent != nil:\n\t\t\tvar got *event.ServerDescriptionChangedEvent\n\t\t\tif got, changed, err = getNextServerDescriptionChangedEvent(changed); err != nil {\n\t\t\t\treturn newEventVerificationError(idx, client, \"failed to get next server description changed event: %v\", err.Error())\n\t\t\t}\n\n\t\t\t// Only verify description types when explicitly specified in the test.\n\t\t\t// An empty `serverDescriptionChangedEvent{}` matches any description change.\n\n\t\t\tprevDesc := evt.ServerDescriptionChangedEvent.PreviousDescription\n\t\t\tif prevDesc != nil && prevDesc.Type != nil {\n\t\t\t\twantPrevDesc := *prevDesc.Type\n\t\t\t\tgotPrevDesc := got.PreviousDescription.Kind\n\t\t\t\tif gotPrevDesc != wantPrevDesc {\n\t\t\t\t\treturn newEventVerificationError(idx, client,\n\t\t\t\t\t\t\"expected previous server description %q, got %q\", wantPrevDesc, gotPrevDesc)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tnewDesc := evt.ServerDescriptionChangedEvent.NewDescription\n\t\t\tif newDesc != nil && newDesc.Type != nil {\n\t\t\t\twantNewDesc := *newDesc.Type\n\t\t\t\tgotNewDesc := got.NewDescription.Kind\n\t\t\t\tif gotNewDesc != wantNewDesc {\n\t\t\t\t\treturn newEventVerificationError(idx, client,\n\t\t\t\t\t\t\"expected new server description %q, got %q\", wantNewDesc, gotNewDesc)\n\t\t\t\t}\n\t\t\t}\n\t\tcase evt.ServerHeartbeatStartedEvent != nil:\n\t\t\tvar got *event.ServerHeartbeatStartedEvent\n\t\t\tif got, started, err = getNextServerHeartbeatStartedEvent(started); err != nil {\n\t\t\t\treturn newEventVerificationError(idx, client, \"failed to get next server heartbeat started event: %v\", err.Error())\n\t\t\t}\n\n\t\t\tif want := evt.ServerHeartbeatStartedEvent.Awaited; want != nil && *want != got.Awaited {\n\t\t\t\treturn newEventVerificationError(idx, client, \"want awaited %v, got %v\", *want, got.Awaited)\n\t\t\t}\n\t\tcase evt.ServerHeartbeatSucceededEvent != nil:\n\t\t\tvar got *event.ServerHeartbeatSucceededEvent\n\t\t\tif got, succeeded, err = getNextServerHeartbeatSucceededEvent(succeeded); err != nil {\n\t\t\t\treturn newEventVerificationError(idx, client, \"failed to get next server heartbeat succeeded event: %v\", err.Error())\n\t\t\t}\n\n\t\t\tif want := evt.ServerHeartbeatSucceededEvent.Awaited; want != nil && *want != got.Awaited {\n\t\t\t\treturn newEventVerificationError(idx, client, \"want awaited %v, got %v\", *want, got.Awaited)\n\t\t\t}\n\t\tcase evt.ServerHeartbeatFailedEvent != nil:\n\t\t\tvar got *event.ServerHeartbeatFailedEvent\n\t\t\tif got, failed, err = getNextServerHeartbeatFailedEvent(failed); err != nil {\n\t\t\t\treturn newEventVerificationError(idx, client, \"failed to get next server heartbeat failed event: %v\", err.Error())\n\t\t\t}\n\n\t\t\tif want := evt.ServerHeartbeatFailedEvent.Awaited; want != nil && *want != got.Awaited {\n\t\t\t\treturn newEventVerificationError(idx, client, \"want awaited %v, got %v\", *want, got.Awaited)\n\t\t\t}\n\t\tcase evt.TopologyDescriptionChangedEvent != nil:\n\t\t\tvar got *event.TopologyDescriptionChangedEvent\n\t\t\tif got, tchanged, err = getNextTopologyDescriptionChangedEvent(tchanged); err != nil {\n\t\t\t\treturn newEventVerificationError(idx, client, \"failed to get next description changed event: %v\", err.Error())\n\t\t\t}\n\n\t\t\tif want := evt.TopologyDescriptionChangedEvent.PreviousDescription; want != nil && want.Type != nil && *want.Type != got.PreviousDescription.Kind {\n\t\t\t\treturn newEventVerificationError(idx, client, \"want previous description %v, got %v\", *want.Type, got.PreviousDescription.Kind)\n\t\t\t}\n\t\t\tif want := evt.TopologyDescriptionChangedEvent.NewDescription; want != nil && want.Type != nil && *want.Type != got.NewDescription.Kind {\n\t\t\t\treturn newEventVerificationError(idx, client, \"want new description %v, got %v\", *want.Type, got.NewDescription.Kind)\n\t\t\t}\n\t\tcase evt.TopologyOpeningEvent != nil:\n\t\t\tif _, topening, err = getNextTopologyOpeningEvent(topening); err != nil {\n\t\t\t\treturn newEventVerificationError(idx, client, \"failed to get next description changed event: %v\", err.Error())\n\t\t\t}\n\t\tcase evt.TopologyClosedEvent != nil:\n\t\t\tif _, tclosed, err = getNextTopologyClosedEvent(tclosed); err != nil {\n\t\t\t\treturn newEventVerificationError(idx, client, \"failed to get next description changed event: %v\", err.Error())\n\t\t\t}\n\t\t}\n\t}\n\n\t// Verify that there are no remaining events if ignoreExtraEvents is unset or false.\n\tignoreExtraEvents := expectedEvents.IgnoreExtraEvents != nil && *expectedEvents.IgnoreExtraEvents\n\tif !ignoreExtraEvents && vol() > 0 {\n\t\treturn fmt.Errorf(\"extra sdam events published; all events for client: %s\", stringifyEventsForClient(client))\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "internal/integration/unified/gridfs_bucket_operation_execution.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage unified\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"encoding/hex\"\n\t\"fmt\"\n\t\"io\"\n\t\"math\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n)\n\nfunc createBucketFindCursor(ctx context.Context, operation *operation) (*cursorResult, error) {\n\tbucket, err := entities(ctx).gridFSBucket(operation.Object)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar filter bson.Raw\n\topts := options.GridFSFind()\n\n\telems, err := operation.Arguments.Elements()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tfor _, elem := range elems {\n\t\tkey := elem.Key()\n\t\tval := elem.Value()\n\n\t\tswitch key {\n\t\tcase \"maxTimeMS\":\n\t\t\t// TODO(DRIVERS-2829): Error here instead of skip to ensure that if new\n\t\t\t// tests are added containing maxTimeMS (a legacy timeout option that we\n\t\t\t// have removed as of v2), then a CSOT analogue exists. Once we have\n\t\t\t// ensured an analogue exists, extend \"skippedTestDescriptions\" to avoid\n\t\t\t// this error.\n\t\t\treturn nil, fmt.Errorf(\"the maxTimeMS gridfs option is not supported\")\n\t\tcase \"filter\":\n\t\t\tfilter = val.Document()\n\t\tdefault:\n\t\t\treturn nil, fmt.Errorf(\"unrecognized bucket find option %q\", key)\n\t\t}\n\t}\n\tif filter == nil {\n\t\treturn nil, newMissingArgumentError(\"filter\")\n\t}\n\n\tcursor, err := bucket.Find(ctx, filter, opts)\n\tres := &cursorResult{\n\t\tcursor: cursor,\n\t\terr:    err,\n\t}\n\treturn res, nil\n}\n\nfunc executeBucketDelete(ctx context.Context, operation *operation) (*operationResult, error) {\n\tbucket, err := entities(ctx).gridFSBucket(operation.Object)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar id *bson.RawValue\n\n\telems, err := operation.Arguments.Elements()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tfor _, elem := range elems {\n\t\tkey := elem.Key()\n\t\tval := elem.Value()\n\n\t\tswitch key {\n\t\tcase \"id\":\n\t\t\tid = &val\n\t\tdefault:\n\t\t\treturn nil, fmt.Errorf(\"unrecognized bucket delete option %q\", key)\n\t\t}\n\t}\n\tif id == nil {\n\t\treturn nil, newMissingArgumentError(\"id\")\n\t}\n\n\treturn newErrorResult(bucket.Delete(ctx, *id)), nil\n}\n\nfunc executeBucketDownload(ctx context.Context, operation *operation) (*operationResult, error) {\n\tbucket, err := entities(ctx).gridFSBucket(operation.Object)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar id *bson.RawValue\n\telems, err := operation.Arguments.Elements()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tfor _, elem := range elems {\n\t\tkey := elem.Key()\n\t\tval := elem.Value()\n\n\t\tswitch key {\n\t\tcase \"id\":\n\t\t\tid = &val\n\t\tdefault:\n\t\t\treturn nil, fmt.Errorf(\"unrecognized bucket download option %q\", key)\n\t\t}\n\t}\n\tif id == nil {\n\t\treturn nil, newMissingArgumentError(\"id\")\n\t}\n\n\tstream, err := bucket.OpenDownloadStream(ctx, *id)\n\tif err != nil {\n\t\treturn newErrorResult(err), nil\n\t}\n\n\tvar buffer bytes.Buffer\n\tif _, err := io.Copy(&buffer, stream); err != nil {\n\t\treturn newErrorResult(err), nil\n\t}\n\n\treturn newValueResult(bson.TypeBinary, bsoncore.AppendBinary(nil, 0, buffer.Bytes()), nil), nil\n}\n\nfunc executeBucketDownloadByName(ctx context.Context, operation *operation) (*operationResult, error) {\n\tbucket, err := entities(ctx).gridFSBucket(operation.Object)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\telems, err := operation.Arguments.Elements()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar filename string\n\topts := options.GridFSName()\n\tfor _, elem := range elems {\n\t\tkey := elem.Key()\n\t\tval := elem.Value()\n\n\t\tswitch key {\n\t\tcase \"filename\":\n\t\t\tfilename = val.StringValue()\n\t\tcase \"revision\":\n\t\t\trevision := val.AsInt64()\n\t\t\tif revision < math.MinInt32 || revision > math.MaxInt32 {\n\t\t\t\treturn nil, fmt.Errorf(\"revision overflows int32: %d\", revision)\n\t\t\t}\n\t\t\topts.SetRevision(int32(revision))\n\t\tdefault:\n\t\t\treturn nil, fmt.Errorf(\"unrecognized bucket download option %q\", key)\n\t\t}\n\t}\n\tif filename == \"\" {\n\t\treturn nil, newMissingArgumentError(\"filename\")\n\t}\n\n\tvar buf bytes.Buffer\n\t_, err = bucket.DownloadToStreamByName(ctx, filename, &buf, opts)\n\tif err != nil {\n\t\treturn newErrorResult(err), nil\n\t}\n\n\treturn newValueResult(bson.TypeBinary, bsoncore.AppendBinary(nil, 0, buf.Bytes()), nil), nil\n}\n\nfunc executeBucketDrop(ctx context.Context, operation *operation) (*operationResult, error) {\n\tbucket, err := entities(ctx).gridFSBucket(operation.Object)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn newErrorResult(bucket.Drop(ctx)), nil\n}\n\nfunc executeBucketRename(ctx context.Context, operation *operation) (*operationResult, error) {\n\tbucket, err := entities(ctx).gridFSBucket(operation.Object)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar id *bson.RawValue\n\tvar newFilename string\n\telems, err := operation.Arguments.Elements()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tfor _, elem := range elems {\n\t\tkey := elem.Key()\n\t\tval := elem.Value()\n\n\t\tswitch key {\n\t\tcase \"id\":\n\t\t\tid = &val\n\t\tcase \"newFilename\":\n\t\t\tnewFilename = val.StringValue()\n\t\tdefault:\n\t\t\treturn nil, fmt.Errorf(\"unrecognized bucket rename option %q\", key)\n\t\t}\n\t}\n\tif id == nil {\n\t\treturn nil, newMissingArgumentError(\"id\")\n\t}\n\n\treturn newErrorResult(bucket.Rename(ctx, id, newFilename)), nil\n}\n\nfunc executeBucketUpload(ctx context.Context, operation *operation) (*operationResult, error) {\n\tbucket, err := entities(ctx).gridFSBucket(operation.Object)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar filename string\n\tvar fileBytes []byte\n\topts := options.GridFSUpload()\n\n\telems, err := operation.Arguments.Elements()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tfor _, elem := range elems {\n\t\tkey := elem.Key()\n\t\tval := elem.Value()\n\n\t\tswitch key {\n\t\tcase \"chunkSizeBytes\":\n\t\t\topts.SetChunkSizeBytes(val.Int32())\n\t\tcase \"filename\":\n\t\t\tfilename = val.StringValue()\n\t\tcase \"metadata\":\n\t\t\topts.SetMetadata(val.Document())\n\t\tcase \"source\":\n\t\t\tfileBytes, err = hex.DecodeString(val.Document().Lookup(\"$$hexBytes\").StringValue())\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"error converting source string to bytes: %w\", err)\n\t\t\t}\n\t\tcase \"contentType\":\n\t\t\treturn nil, newSkipTestError(\"the deprecated contentType file option is not supported\")\n\t\tcase \"disableMD5\":\n\t\t\treturn nil, newSkipTestError(\"the deprecated disableMD5 file option is not supported\")\n\t\tdefault:\n\t\t\treturn nil, fmt.Errorf(\"unrecognized bucket upload option %q\", key)\n\t\t}\n\t}\n\tif filename == \"\" {\n\t\treturn nil, newMissingArgumentError(\"filename\")\n\t}\n\tif fileBytes == nil {\n\t\treturn nil, newMissingArgumentError(\"source\")\n\t}\n\n\tfileID, err := bucket.UploadFromStream(ctx, filename, bytes.NewReader(fileBytes), opts)\n\tif err != nil {\n\t\treturn newErrorResult(err), nil\n\t}\n\n\tif operation.ResultEntityID != nil {\n\t\tfileIDValue := bson.RawValue{\n\t\t\tType:  bson.TypeObjectID,\n\t\t\tValue: fileID[:],\n\t\t}\n\t\tif err := entities(ctx).addBSONEntity(*operation.ResultEntityID, fileIDValue); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"error storing result as BSON entity: %w\", err)\n\t\t}\n\t}\n\n\treturn newValueResult(bson.TypeObjectID, fileID[:], nil), nil\n}\n"
  },
  {
    "path": "internal/integration/unified/logger.go",
    "content": "// Copyright (C) MongoDB, Inc. 2023-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage unified\n\nimport (\n\t\"context\"\n\t\"sync\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/logger\"\n)\n\n// orderedLogMessage is a logMessage with an \"order\" field representing the\n// order in which the log message was observed.\ntype orderedLogMessage struct {\n\t*logMessage\n\torder int\n}\n\n// Logger is the Sink used to captured log messages for logger verification in\n// the unified spec tests.\ntype Logger struct {\n\t// bufSize is the number of logs expected to be sent to the logger for a\n\t// unified spec test.\n\tbufSize int\n\n\t// lastOrder increments each time the \"Info\" method is called, and is used to\n\t// determine when to close the logQueue.\n\tlastOrder int\n\n\t// orderMu guards the order value, which increments each time the \"Info\"\n\t// method is called. This is necessary since \"Info\" could be called from\n\t// multiple go routines, e.g. SDAM logs.\n\torderMu        sync.RWMutex\n\tlogQueue       chan orderedLogMessage\n\tignoreMessages []*logMessage\n}\n\nfunc newLogger(olm *observeLogMessages, bufSize int, ignoreMessages []*logMessage) *Logger {\n\tif olm == nil {\n\t\treturn nil\n\t}\n\n\treturn &Logger{\n\t\tlastOrder:      1,\n\t\tlogQueue:       make(chan orderedLogMessage, bufSize),\n\t\tbufSize:        bufSize,\n\t\tignoreMessages: ignoreMessages,\n\t}\n}\n\n// Info implements the logger.Sink interface's \"Info\" method for printing log\n// messages.\nfunc (log *Logger) Info(level int, msg string, args ...any) {\n\tlog.orderMu.Lock()\n\tdefer log.orderMu.Unlock()\n\n\tif log.logQueue == nil {\n\t\treturn\n\t}\n\n\t// If the order is greater than the buffer size, we must return. This\n\t// would indicate that the logQueue channel has been closed.\n\tif log.lastOrder > log.bufSize {\n\t\treturn\n\t}\n\n\t// Add the Diff back to the level, as there is no need to create a\n\t// logging offset.\n\tlevel += logger.DiffToInfo\n\n\tlogMessage, err := newLogMessage(level, msg, args...)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfor _, ignoreMessage := range log.ignoreMessages {\n\t\tif err := verifyLogMatch(context.Background(), ignoreMessage, logMessage); err == nil {\n\t\t\treturn\n\t\t}\n\t}\n\n\tdefer func() { log.lastOrder++ }()\n\n\t// Send the log message to the \"orderedLogMessage\" channel for\n\t// validation.\n\tlog.logQueue <- orderedLogMessage{\n\t\torder:      log.lastOrder + 1,\n\t\tlogMessage: logMessage,\n\t}\n\n\t// If the order has reached the buffer size, then close the channel.\n\tif log.lastOrder == log.bufSize {\n\t\tclose(log.logQueue)\n\t}\n}\n\n// Error implements the logger.Sink interface's \"Error\" method for printing log\n// errors. In this case, if an error occurs we will simply treat it as\n// informational.\nfunc (log *Logger) Error(err error, msg string, args ...any) {\n\targs = append(args, \"error\", err)\n\tlog.Info(int(logger.LevelInfo), msg, args)\n}\n"
  },
  {
    "path": "internal/integration/unified/logger_verification.go",
    "content": "// Copyright (C) MongoDB, Inc. 2023-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage unified\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"sync\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/logger\"\n)\n\n// errLoggerVerification is use to wrap errors associated with validating the\n// correctness of logs while testing operations.\nvar errLoggerVerification = fmt.Errorf(\"logger verification failed\")\n\n// logMessage is a log message that is expected to be observed by the driver.\ntype logMessage struct {\n\tLevelLiteral      string   `bson:\"level\"`\n\tComponentLiteral  string   `bson:\"component\"`\n\tData              bson.Raw `bson:\"data\"`\n\tFailureIsRedacted bool     `bson:\"failureIsRedacted\"`\n}\n\n// newLogMessage will create a \"logMessage\" from the level and a slice of\n// arguments.\nfunc newLogMessage(level int, msg string, args ...any) (*logMessage, error) {\n\tlogMessage := new(logMessage)\n\n\t// Iterate over the literal levels until we get the first\n\t// \"LevelLiteral\" that matches the level of the \"LogMessage\". It doesn't\n\t// matter which literal is chose so long as the mapping results in the\n\t// correct level.\n\tfor literal, logLevel := range logger.LevelLiteralMap {\n\t\tif level == int(logLevel) {\n\t\t\tlogMessage.LevelLiteral = literal\n\n\t\t\tbreak\n\t\t}\n\t}\n\n\t// The argument slice must have an even number of elements, otherwise it\n\t// would not maintain the key-value structure of the document.\n\tif len(args)%2 != 0 {\n\t\treturn nil, fmt.Errorf(\"%w: invalid arguments: %v\", errLoggerVerification, args)\n\t}\n\n\t// Create a new document from the arguments.\n\tactualD := bson.D{{\"message\", msg}}\n\tfor i := 0; i < len(args); i += 2 {\n\t\tactualD = append(actualD, bson.E{\n\t\t\tKey:   args[i].(string),\n\t\t\tValue: args[i+1],\n\t\t})\n\t}\n\n\t// Marshal the document into a raw value and assign it to the\n\t// logMessage.\n\tbytes, err := bson.Marshal(actualD)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"%w: failed to marshal: %v\", errLoggerVerification, err)\n\t}\n\n\tlogMessage.Data = bson.Raw(bytes)\n\n\treturn logMessage, nil\n}\n\n// clientLogMessages is a struct representing the expected \"LogMessages\" for a\n// client.\ntype clientLogMessages struct {\n\tClient         string        `bson:\"client\"`\n\tIgnoreMessages []*logMessage `bson:\"ignoreMessages\"`\n\tLogMessages    []*logMessage `bson:\"messages\"`\n}\n\n// logMessageValidator defines the expectation for log messages across all\n// clients.\ntype logMessageValidator struct {\n\ttestCase   *TestCase\n\tclientErrs map[string]chan error\n}\n\n// newLogMessageValidator will create a new logMessageValidator.\nfunc newLogMessageValidator(testCase *TestCase) *logMessageValidator {\n\tvalidator := &logMessageValidator{testCase: testCase}\n\tvalidator.clientErrs = make(map[string]chan error)\n\n\t// Make the error channels for the clients.\n\tfor _, exp := range testCase.ExpectLogMessages {\n\t\tvalidator.clientErrs[exp.Client] = make(chan error)\n\t}\n\n\treturn validator\n}\n\nfunc logQueue(ctx context.Context, exp *clientLogMessages) <-chan orderedLogMessage {\n\tclients := entities(ctx).clients()\n\n\tclientEntity, ok := clients[exp.Client]\n\tif !ok {\n\t\treturn nil\n\t}\n\n\treturn clientEntity.logQueue\n}\n\n// verifyLogMatch will verify that the actual log match the expected log.\nfunc verifyLogMatch(ctx context.Context, exp, act *logMessage) error {\n\tif act == nil && exp == nil {\n\t\treturn nil\n\t}\n\n\tif act == nil || exp == nil {\n\t\treturn fmt.Errorf(\"%w: document mismatch\", errLoggerVerification)\n\t}\n\n\tlevelExp := logger.ParseLevel(exp.LevelLiteral)\n\tlevelAct := logger.ParseLevel(act.LevelLiteral)\n\n\t// The levels of the expected log message and the actual log message\n\t// must match, upto logger.Level.\n\tif levelExp != levelAct {\n\t\treturn fmt.Errorf(\"%w: level mismatch: want %v, got %v\",\n\t\t\terrLoggerVerification, levelExp, levelAct)\n\t}\n\n\trawExp := documentToRawValue(exp.Data)\n\trawAct := documentToRawValue(act.Data)\n\n\t// Top level data does not have to be 1-1 with the expectation, there\n\t// are a number of unrequired fields that may not be present on the\n\t// expected document.\n\tif err := verifyValuesMatch(ctx, rawExp, rawAct, true); err != nil {\n\t\treturn fmt.Errorf(\"%w: document length mismatch: %v\", errLoggerVerification, err)\n\t}\n\n\treturn nil\n}\n\n// isUnorderedLog will return true if the log is/should be unordered in the Go\n// Driver.\nfunc isUnorderedLog(log *logMessage) bool {\n\tmsg, err := log.Data.LookupErr(logger.KeyMessage)\n\tif err != nil {\n\t\treturn false\n\t}\n\n\tmsgStr := msg.StringValue()\n\n\t// There is a race condition in the connection pool's workflow where it\n\t// is non-deterministic whether the connection pool will fail a checkout\n\t// or close a connection first. Because of this, either log may be\n\t// received in any order. To account for this behavior, we considered\n\t// both logs to be \"unordered\".\n\t//\n\t// The connection pool must clear before the connection is closed.\n\t// However, either of these conditions are valid:\n\t//\n\t//   1. connection checkout failed > connection pool cleared\n\t//   2. connection pool cleared > connection checkout failed\n\t//\n\t// Therefore, the ConnectionPoolCleared literal is added to the\n\t// unordered list. The check for cleared > closed is made in the\n\t// matching logic.\n\treturn msgStr == logger.ConnectionCheckoutFailed ||\n\t\tmsgStr == logger.ConnectionClosed ||\n\t\tmsgStr == logger.ConnectionPoolCleared\n}\n\ntype logQueues struct {\n\texpected  *clientLogMessages\n\tordered   <-chan *logMessage\n\tunordered <-chan *logMessage\n}\n\n// partitionLogQueue will partition the expected logs into \"unordered\" and\n// \"ordered\" log channels.\nfunc partitionLogQueue(ctx context.Context, exp *clientLogMessages) logQueues {\n\torderedLogCh := make(chan *logMessage, len(exp.LogMessages))\n\tunorderedLogCh := make(chan *logMessage, len(exp.LogMessages))\n\n\t// Get the unordered indices from the expected log messages.\n\tunorderedIndices := make(map[int]struct{})\n\tfor i, log := range exp.LogMessages {\n\t\tif isUnorderedLog(log) {\n\t\t\tunorderedIndices[i] = struct{}{}\n\t\t}\n\t}\n\n\tgo func() {\n\t\tdefer close(orderedLogCh)\n\t\tdefer close(unorderedLogCh)\n\n\t\tfor actual := range logQueue(ctx, exp) {\n\t\t\tmsg := actual.logMessage\n\t\t\tif _, ok := unorderedIndices[actual.order-2]; ok {\n\t\t\t\tunorderedLogCh <- msg\n\t\t\t} else {\n\t\t\t\torderedLogCh <- msg\n\t\t\t}\n\t\t}\n\t}()\n\n\treturn logQueues{\n\t\texpected:  exp,\n\t\tordered:   orderedLogCh,\n\t\tunordered: unorderedLogCh,\n\t}\n}\n\nfunc matchOrderedLogs(ctx context.Context, logs logQueues) <-chan error {\n\t// Remove all of the unordered log messages from the expected.\n\texpLogMessages := make([]*logMessage, 0, len(logs.expected.LogMessages))\n\tfor _, log := range logs.expected.LogMessages {\n\t\tif !isUnorderedLog(log) {\n\t\t\texpLogMessages = append(expLogMessages, log)\n\t\t}\n\t}\n\n\terrs := make(chan error, 1)\n\n\tgo func() {\n\t\tdefer close(errs)\n\n\t\tfor actual := range logs.ordered {\n\t\t\texpected := expLogMessages[0]\n\t\t\tif expected == nil {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\terr := verifyLogMatch(ctx, expected, actual)\n\t\t\tif err != nil {\n\t\t\t\terrs <- err\n\t\t\t}\n\n\t\t\t// Remove the first element from the expected log.\n\t\t\texpLogMessages = expLogMessages[1:]\n\t\t}\n\t}()\n\n\treturn errs\n}\n\nfunc matchUnorderedLogs(ctx context.Context, logs logQueues) <-chan error {\n\tunordered := make(map[*logMessage]struct{}, len(logs.expected.LogMessages))\n\n\tfor _, log := range logs.expected.LogMessages {\n\t\tif isUnorderedLog(log) {\n\t\t\tunordered[log] = struct{}{}\n\t\t}\n\t}\n\n\terrs := make(chan error, 1)\n\n\tgo func() {\n\t\tdefer close(errs)\n\n\t\t// Record the message literals as they occur.\n\t\tactualMessageSet := map[string]bool{}\n\n\t\tfor actual := range logs.unordered {\n\t\t\tmsg, err := actual.Data.LookupErr(logger.KeyMessage)\n\t\t\tif err != nil {\n\t\t\t\terrs <- fmt.Errorf(\"could not lookup message from unordered log: %w\", err)\n\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\tmsgStr := msg.StringValue()\n\t\t\tif msgStr == logger.ConnectionPoolCleared && actualMessageSet[logger.ConnectionClosed] {\n\t\t\t\terrs <- fmt.Errorf(\"connection has been closed before the pool could clear\")\n\t\t\t}\n\n\t\t\t// Iterate over the unordered log messages and verify\n\t\t\t// that at least one of them matches the actual log\n\t\t\t// message.\n\t\t\tfor expected := range unordered {\n\t\t\t\terr = verifyLogMatch(ctx, expected, actual)\n\t\t\t\tif err == nil {\n\t\t\t\t\t// Remove the matched unordered log\n\t\t\t\t\t// message from the unordered map.\n\t\t\t\t\tdelete(unordered, expected)\n\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// If there was no match, return an error.\n\t\t\tif err != nil {\n\t\t\t\terrs <- err\n\t\t\t}\n\n\t\t\tactualMessageSet[msgStr] = true\n\t\t}\n\t}()\n\n\treturn errs\n}\n\n// startLogValidators will start a goroutine for each client's expected log\n// messages, listening to the channel of actual log messages and comparing them\n// to the expected log messages.\nfunc startLogValidators(ctx context.Context, validator *logMessageValidator) {\n\tfor _, expected := range validator.testCase.ExpectLogMessages {\n\t\tlogs := partitionLogQueue(ctx, expected)\n\n\t\twg := &sync.WaitGroup{}\n\t\twg.Add(2)\n\n\t\tgo func(expected *clientLogMessages) {\n\t\t\tdefer wg.Done()\n\n\t\t\terrCh := matchOrderedLogs(ctx, logs)\n\t\t\tif errCh == nil {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tif errs := <-errCh; errs != nil {\n\t\t\t\tvalidator.clientErrs[expected.Client] <- errs\n\t\t\t}\n\t\t}(expected)\n\n\t\tgo func(expected *clientLogMessages) {\n\t\t\tdefer wg.Done()\n\n\t\t\terrCh := matchUnorderedLogs(ctx, logs)\n\t\t\tif errCh == nil {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tif errs := <-errCh; errs != nil {\n\t\t\t\tvalidator.clientErrs[expected.Client] <- errs\n\t\t\t}\n\t\t}(expected)\n\n\t\tgo func(expected *clientLogMessages) {\n\t\t\twg.Wait()\n\n\t\t\tclose(validator.clientErrs[expected.Client])\n\t\t}(expected)\n\t}\n}\n\nfunc stopLogValidatorsErr(clientName string, err error) error {\n\treturn fmt.Errorf(\"%w: %s: %v\", errLoggerVerification, clientName, err)\n}\n\n// stopLogValidators will gracefully validate all log messages received by all\n// clients and return the first error encountered.\nfunc stopLogValidators(ctx context.Context, validator *logMessageValidator) error {\n\tfor clientName, errChan := range validator.clientErrs {\n\t\tselect {\n\t\tcase err := <-errChan:\n\t\t\tif err != nil {\n\t\t\t\treturn stopLogValidatorsErr(clientName, err)\n\t\t\t}\n\t\tcase <-ctx.Done():\n\t\t\treturn stopLogValidatorsErr(clientName, ctx.Err())\n\t\t}\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "internal/integration/unified/main_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage unified\n\nimport (\n\t\"flag\"\n\t\"log\"\n\t\"os\"\n\t\"testing\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/integration/mtest\"\n)\n\nfunc TestMain(m *testing.M) {\n\t// All tests that use mtest.Setup() are expected to be integration tests, so skip them when the\n\t// -short flag is included in the \"go test\" command. Also, we have to parse flags here to use\n\t// testing.Short() because flags aren't parsed before TestMain() is called.\n\tflag.Parse()\n\tif testing.Short() {\n\t\tlog.Print(\"skipping mtest integration test in short mode\")\n\t\treturn\n\t}\n\n\tif err := mtest.Setup(); err != nil {\n\t\tlog.Panic(err)\n\t}\n\tdefer os.Exit(m.Run())\n\tif err := mtest.Teardown(); err != nil {\n\t\tlog.Panic(err)\n\t}\n}\n"
  },
  {
    "path": "internal/integration/unified/matches.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage unified\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"encoding/hex\"\n\t\"fmt\"\n\t\"strings\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n)\n\n// keyPathCtxKey is used as a key for a Context object. The value conveys the BSON key path that is currently being\n// compared.\ntype keyPathCtxKey struct{}\n\n// extraKeysAllowedCtxKey is used as a key for a Context object. The value conveys whether or not the document under\n// test can contain extra keys. For example, if the expected document is {x: 1}, the document {x: 1, y: 1} would match\n// if the value for this key is true.\ntype (\n\textraKeysAllowedCtxKey          struct{}\n\textraKeysAllowedRootMatchCtxKey struct{}\n)\n\nfunc makeMatchContext(ctx context.Context, keyPath string, extraKeysAllowed bool) context.Context {\n\tctx = context.WithValue(ctx, keyPathCtxKey{}, keyPath)\n\tctx = context.WithValue(ctx, extraKeysAllowedCtxKey{}, extraKeysAllowed)\n\n\t// The Root Match Context should be persisted once set.\n\tif _, ok := ctx.Value(extraKeysAllowedRootMatchCtxKey{}).(bool); !ok {\n\t\tctx = context.WithValue(ctx, extraKeysAllowedRootMatchCtxKey{}, extraKeysAllowed)\n\t}\n\n\treturn ctx\n}\n\n// verifyValuesMatch compares the provided BSON values and returns an error if they do not match. If the values are\n// documents and extraKeysAllowed is true, the actual value will be allowed to have additional keys at the top-level.\n// For example, an expected document {x: 1} would match the actual document {x: 1, y: 1}.\nfunc verifyValuesMatch(ctx context.Context, expected, actual bson.RawValue, extraKeysAllowed bool) error {\n\treturn verifyValuesMatchInner(makeMatchContext(ctx, \"\", extraKeysAllowed), expected, actual)\n}\n\nfunc verifyValuesMatchInner(ctx context.Context, expected, actual bson.RawValue) error {\n\tkeyPath := ctx.Value(keyPathCtxKey{}).(string)\n\textraKeysAllowed := ctx.Value(extraKeysAllowedCtxKey{}).(bool)\n\n\tif expectedDoc, ok := expected.DocumentOK(); ok {\n\t\t// If the root document only has one element and the key is a special matching operator, the actual value might\n\t\t// not actually be a document. In this case, evaluate the special operator with the actual value rather than\n\t\t// doing an element-wise document comparison.\n\t\tif requiresSpecialMatching(expectedDoc) {\n\t\t\tif err := evaluateSpecialComparison(ctx, expectedDoc, actual); err != nil {\n\t\t\t\treturn newMatchingError(keyPath, \"error doing special matching assertion: %v\", err)\n\t\t\t}\n\t\t\treturn nil\n\t\t}\n\n\t\tactualDoc, ok := actual.DocumentOK()\n\t\tif !ok {\n\t\t\treturn newMatchingError(keyPath, \"expected value to be a document but got a %s\", actual.Type)\n\t\t}\n\n\t\t// Perform element-wise comparisons.\n\t\texpectedElems, _ := expectedDoc.Elements()\n\t\tfor _, expectedElem := range expectedElems {\n\t\t\texpectedKey := expectedElem.Key()\n\t\t\texpectedValue := expectedElem.Value()\n\n\t\t\tfullKeyPath := expectedKey\n\t\t\tif keyPath != \"\" {\n\t\t\t\tfullKeyPath = keyPath + \".\" + expectedKey\n\t\t\t}\n\n\t\t\t// Get the value from actualDoc here but don't check the error until later because some of the special\n\t\t\t// matching operators can assert that the value isn't present in the document (e.g. $$exists).\n\t\t\tactualValue, err := actualDoc.LookupErr(expectedKey)\n\t\t\tif specialDoc, ok := expectedValue.DocumentOK(); ok && requiresSpecialMatching(specialDoc) {\n\t\t\t\t// Reset the key path so any errors returned from the function will only have the key path for the\n\t\t\t\t// target value. Also unconditionally set extraKeysAllowed to false because an assertion like\n\t\t\t\t// $$unsetOrMatches could recurse back into this function. In that case, the target document is nested\n\t\t\t\t// and should not have extra keys.\n\t\t\t\tctx = makeMatchContext(ctx, \"\", false)\n\t\t\t\tif err := evaluateSpecialComparison(ctx, specialDoc, actualValue); err != nil {\n\t\t\t\t\treturn newMatchingError(fullKeyPath, \"error doing special matching assertion: %v\", err)\n\t\t\t\t}\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\t// This isn't a special comparison. Assert that the value exists in the actual document.\n\t\t\tif err != nil {\n\t\t\t\treturn newMatchingError(fullKeyPath, \"key not found in actual document\")\n\t\t\t}\n\n\t\t\t// Nested documents cannot have extra keys, so we unconditionally pass false for extraKeysAllowed.\n\t\t\tcomparisonCtx := makeMatchContext(ctx, fullKeyPath, false)\n\t\t\tif err := verifyValuesMatchInner(comparisonCtx, expectedValue, actualValue); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\t// If required, verify that the actual document does not have extra elements. We do this by iterating over the\n\t\t// actual and checking for each key in the expected rather than comparing element counts because the presence of\n\t\t// special operators can cause incorrect counts. For example, the document {y: {$$exists: false}} has one\n\t\t// element, but should match the document {}, which has none.\n\t\tif !extraKeysAllowed {\n\t\t\tactualElems, _ := actualDoc.Elements()\n\t\t\tfor _, actualElem := range actualElems {\n\t\t\t\tif _, err := expectedDoc.LookupErr(actualElem.Key()); err != nil {\n\t\t\t\t\treturn newMatchingError(keyPath, \"extra key %q found in actual document %s\", actualElem.Key(),\n\t\t\t\t\t\tactualDoc)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn nil\n\t}\n\tif expectedArr, ok := expected.ArrayOK(); ok {\n\t\tactualArr, ok := actual.ArrayOK()\n\t\tif !ok {\n\t\t\treturn newMatchingError(keyPath, \"expected value to be an array but got a %s\", actual.Type)\n\t\t}\n\n\t\texpectedValues, _ := expectedArr.Values()\n\t\tactualValues, _ := actualArr.Values()\n\n\t\t// Arrays must always have the same number of elements.\n\t\tif len(expectedValues) != len(actualValues) {\n\t\t\treturn newMatchingError(keyPath, \"expected array length %d, got %d\", len(expectedValues),\n\t\t\t\tlen(actualValues))\n\t\t}\n\n\t\tfor idx, expectedValue := range expectedValues {\n\t\t\t// Use the index as the key to augment the key path.\n\t\t\tfullKeyPath := fmt.Sprintf(\"%d\", idx)\n\t\t\tif keyPath != \"\" {\n\t\t\t\tfullKeyPath = keyPath + \".\" + fullKeyPath\n\t\t\t}\n\n\t\t\tcomparisonCtx := makeMatchContext(ctx, fullKeyPath, extraKeysAllowed)\n\t\t\terr := verifyValuesMatchInner(comparisonCtx, expectedValue, actualValues[idx])\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\n\t\treturn nil\n\t}\n\n\tif expected.Type == bson.TypeDecimal128 {\n\t\tif actual.Type != bson.TypeDecimal128 {\n\t\t\treturn newMatchingError(keyPath, \"expected value to be a decimal type but got a %s\", actual.Type)\n\t\t}\n\t\texpectedDecimal := expected.Decimal128()\n\t\tactualDecimal := actual.Decimal128()\n\t\teh, el := expectedDecimal.GetBytes()\n\t\tah, al := actualDecimal.GetBytes()\n\t\tif eh != ah || el != al {\n\t\t\treturn newMatchingError(keyPath, \"expected decimal value %v, got %v\", expectedDecimal, actualDecimal)\n\t\t}\n\t\treturn nil\n\t}\n\t// Numeric values must be considered equal even if their types are different (e.g. if expected is an int32 and\n\t// actual is an int64).\n\tif expected.IsNumber() {\n\t\tif !actual.IsNumber() {\n\t\t\treturn newMatchingError(keyPath, \"expected value to be a number but got a %s\", actual.Type)\n\t\t}\n\n\t\texpectedInt64 := expected.AsInt64()\n\t\tactualInt64 := actual.AsInt64()\n\t\tif expectedInt64 != actualInt64 {\n\t\t\treturn newMatchingError(keyPath, \"expected numeric value %d, got %d\", expectedInt64, actualInt64)\n\t\t}\n\t\treturn nil\n\t}\n\n\t// If expected is not a recursive or numeric type, we can directly call Equal to do the comparison.\n\tif !expected.Equal(actual) {\n\t\treturn newMatchingError(keyPath, \"expected value %s, got %s\", expected, actual)\n\t}\n\treturn nil\n}\n\nfunc evaluateSpecialComparison(ctx context.Context, assertionDoc bson.Raw, actual bson.RawValue) error {\n\tassertionElem := assertionDoc.Index(0)\n\tassertion := assertionElem.Key()\n\tassertionVal := assertionElem.Value()\n\textraKeysAllowed := ctx.Value(extraKeysAllowedCtxKey{}).(bool)\n\textraKeysRootMatchAllowed := ctx.Value(extraKeysAllowedRootMatchCtxKey{}).(bool)\n\n\tswitch assertion {\n\tcase \"$$exists\":\n\t\tshouldExist := assertionVal.Boolean()\n\t\texists := actual.Validate() == nil\n\t\tif shouldExist != exists {\n\t\t\treturn fmt.Errorf(\"expected value to exist: %v; value actually exists: %v\", shouldExist, exists)\n\t\t}\n\tcase \"$$type\":\n\t\tpossibleTypes, err := getTypesArray(assertionVal)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"error getting possible types for a $$type assertion: %v\", err)\n\t\t}\n\n\t\tfor _, possibleType := range possibleTypes {\n\t\t\tif actual.Type == possibleType {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\treturn fmt.Errorf(\"expected type to be one of %v but was %s\", possibleTypes, actual.Type)\n\tcase \"$$matchesEntity\":\n\t\texpected, err := entities(ctx).BSONValue(assertionVal.StringValue())\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\t// $$matchesEntity doesn't modify the nesting level of the key path so we can propagate ctx without changes.\n\t\treturn verifyValuesMatchInner(ctx, expected, actual)\n\tcase \"$$matchesHexBytes\":\n\t\texpectedBytes, err := hex.DecodeString(assertionVal.StringValue())\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"error converting $$matcesHexBytes value to bytes: %v\", err)\n\t\t}\n\n\t\t_, actualBytes, ok := actual.BinaryOK()\n\t\tif !ok {\n\t\t\treturn fmt.Errorf(\"expected binary value for a $$matchesHexBytes assertion, but got a %s\", actual.Type)\n\t\t}\n\t\tif !bytes.Equal(expectedBytes, actualBytes) {\n\t\t\treturn fmt.Errorf(\"expected bytes %v, got %v\", expectedBytes, actualBytes)\n\t\t}\n\tcase \"$$unsetOrMatches\":\n\t\tif actual.Validate() != nil {\n\t\t\treturn nil\n\t\t}\n\n\t\t// $$unsetOrMatches doesn't modify the nesting level or the key path so we can propagate the context to the\n\t\t// comparison function without changing anything.\n\t\treturn verifyValuesMatchInner(ctx, assertionVal, actual)\n\tcase \"$$sessionLsid\":\n\t\tsess, err := entities(ctx).session(assertionVal.StringValue())\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\texpectedID := sess.ID()\n\t\tactualID, ok := actual.DocumentOK()\n\t\tif !ok {\n\t\t\treturn fmt.Errorf(\"expected document value for a $$sessionLsid assertion, but got a %s\", actual.Type)\n\t\t}\n\t\tif !bytes.Equal(expectedID, actualID) {\n\t\t\treturn fmt.Errorf(\"expected lsid %v, got %v\", expectedID, actualID)\n\t\t}\n\tcase \"$$lte\":\n\t\tif assertionVal.Type != bson.TypeInt32 && assertionVal.Type != bson.TypeInt64 && assertionVal.Type != bson.TypeDouble {\n\t\t\treturn fmt.Errorf(\"expected assertionVal to be an Int32, Int64, or Double but got a %s\", assertionVal.Type)\n\t\t}\n\t\tif actual.Type != bson.TypeInt32 && actual.Type != bson.TypeInt64 && actual.Type != bson.TypeDouble {\n\t\t\treturn fmt.Errorf(\"expected value to be an Int32, Int64, or Double but got a %s\", actual.Type)\n\t\t}\n\n\t\t// Numeric values can be compared even if their types are different (e.g. if expected is an int32 and actual\n\t\t// is an int64).\n\t\texpectedF64 := assertionVal.AsFloat64()\n\t\tactualF64 := actual.AsFloat64()\n\n\t\tif actualF64 > expectedF64 {\n\t\t\treturn fmt.Errorf(\"expected numeric value %f to be less than or equal %f\", actualF64, expectedF64)\n\t\t}\n\t\treturn nil\n\tcase \"$$matchAsDocument\":\n\t\tvar actualDoc bson.Raw\n\t\tstr, ok := actual.StringValueOK()\n\t\tif !ok {\n\t\t\treturn fmt.Errorf(\"expected value to be a string but got a %s\", actual.Type)\n\t\t}\n\n\t\tif err := bson.UnmarshalExtJSON([]byte(str), true, &actualDoc); err != nil {\n\t\t\treturn fmt.Errorf(\"error unmarshalling string as document: %v\", err)\n\t\t}\n\n\t\tif err := verifyValuesMatch(ctx, assertionVal, documentToRawValue(actualDoc), extraKeysAllowed); err != nil {\n\t\t\treturn fmt.Errorf(\"error matching $$matchAsRoot assertion: %v\", err)\n\t\t}\n\tcase \"$$matchAsRoot\":\n\t\t// Treat the actual value as a root-level document that can have extra keys that are not subject to\n\t\t// the matching rules.\n\t\tif err := verifyValuesMatch(ctx, assertionVal, actual, extraKeysRootMatchAllowed); err != nil {\n\t\t\treturn fmt.Errorf(\"error matching $$matchAsRoot assertion: %v\", err)\n\t\t}\n\tdefault:\n\t\treturn fmt.Errorf(\"unrecognized special matching assertion %q\", assertion)\n\t}\n\n\treturn nil\n}\n\nfunc requiresSpecialMatching(doc bson.Raw) bool {\n\telems, _ := doc.Elements()\n\treturn len(elems) == 1 && strings.HasPrefix(elems[0].Key(), \"$$\")\n}\n\nfunc getTypesArray(val bson.RawValue) ([]bson.Type, error) {\n\tswitch val.Type {\n\tcase bson.TypeString:\n\t\tconvertedType, err := convertStringToBSONType(val.StringValue())\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\treturn []bson.Type{convertedType}, nil\n\tcase bson.TypeArray:\n\t\tvar typeStrings []string\n\t\tif err := val.Unmarshal(&typeStrings); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"error unmarshalling to slice of strings: %v\", err)\n\t\t}\n\n\t\tvar types []bson.Type\n\t\tfor _, typeStr := range typeStrings {\n\t\t\tconvertedType, err := convertStringToBSONType(typeStr)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\n\t\t\ttypes = append(types, convertedType)\n\t\t}\n\t\treturn types, nil\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"invalid type to convert to bson.Type slice: %s\", val.Type)\n\t}\n}\n\nfunc convertStringToBSONType(typeStr string) (bson.Type, error) {\n\tswitch typeStr {\n\tcase \"double\":\n\t\treturn bson.TypeDouble, nil\n\tcase \"string\":\n\t\treturn bson.TypeString, nil\n\tcase \"object\":\n\t\treturn bson.TypeEmbeddedDocument, nil\n\tcase \"array\":\n\t\treturn bson.TypeArray, nil\n\tcase \"binData\":\n\t\treturn bson.TypeBinary, nil\n\tcase \"undefined\":\n\t\treturn bson.TypeUndefined, nil\n\tcase \"objectId\":\n\t\treturn bson.TypeObjectID, nil\n\tcase \"bool\":\n\t\treturn bson.TypeBoolean, nil\n\tcase \"date\":\n\t\treturn bson.TypeDateTime, nil\n\tcase \"null\":\n\t\treturn bson.TypeNull, nil\n\tcase \"regex\":\n\t\treturn bson.TypeRegex, nil\n\tcase \"dbPointer\":\n\t\treturn bson.TypeDBPointer, nil\n\tcase \"javascript\":\n\t\treturn bson.TypeJavaScript, nil\n\tcase \"symbol\":\n\t\treturn bson.TypeSymbol, nil\n\tcase \"javascriptWithScope\":\n\t\treturn bson.TypeCodeWithScope, nil\n\tcase \"int\":\n\t\treturn bson.TypeInt32, nil\n\tcase \"timestamp\":\n\t\treturn bson.TypeTimestamp, nil\n\tcase \"long\":\n\t\treturn bson.TypeInt64, nil\n\tcase \"decimal\":\n\t\treturn bson.TypeDecimal128, nil\n\tcase \"minKey\":\n\t\treturn bson.TypeMinKey, nil\n\tcase \"maxKey\":\n\t\treturn bson.TypeMaxKey, nil\n\tdefault:\n\t\treturn bson.Type(0), fmt.Errorf(\"unrecognized BSON type string %q\", typeStr)\n\t}\n}\n\n// newMatchingError creates an error to convey that BSON value comparison failed at the provided key path. If the\n// key path is empty (e.g. because the values being compared were not documents), the error message will contain the\n// phrase \"top-level\" instead of the path.\nfunc newMatchingError(keyPath, msg string, args ...any) error {\n\tfullMsg := fmt.Sprintf(msg, args...)\n\tif keyPath == \"\" {\n\t\treturn fmt.Errorf(\"comparison error at top-level: %s\", fullMsg)\n\t}\n\treturn fmt.Errorf(\"comparison error at key %q: %s\", keyPath, fullMsg)\n}\n"
  },
  {
    "path": "internal/integration/unified/matches_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage unified\n\nimport (\n\t\"context\"\n\t\"encoding/hex\"\n\t\"testing\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n)\n\nfunc TestMatches(t *testing.T) {\n\tctx := context.Background()\n\tunmarshalExtJSONValue := func(t *testing.T, str string) bson.RawValue {\n\t\tt.Helper()\n\n\t\tif str == \"\" {\n\t\t\treturn bson.RawValue{}\n\t\t}\n\n\t\tvar val bson.RawValue\n\t\terr := bson.UnmarshalExtJSON([]byte(str), false, &val)\n\t\tassert.Nil(t, err, \"UnmarshalExtJSON error: %v\", err)\n\t\treturn val\n\t}\n\tmarshalValue := func(t *testing.T, val any) bson.RawValue {\n\t\tt.Helper()\n\n\t\tvalType, data, err := bson.MarshalValue(val)\n\t\tassert.Nil(t, err, \"MarshalValue error: %v\", err)\n\t\treturn bson.RawValue{\n\t\t\tType:  valType,\n\t\t\tValue: data,\n\t\t}\n\t}\n\n\tassertMatches := func(t *testing.T, expected, actual bson.RawValue, shouldMatch bool) {\n\t\tt.Helper()\n\n\t\terr := verifyValuesMatch(ctx, expected, actual, true)\n\t\tif shouldMatch {\n\t\t\tassert.Nil(t, err, \"expected values to match, but got comparison error %v\", err)\n\t\t\treturn\n\t\t}\n\t\tassert.NotNil(t, err, \"expected values to not match, but got no error\")\n\t}\n\n\tt.Run(\"documents with extra keys allowed\", func(t *testing.T) {\n\t\texpectedDoc := `{\"x\": 1, \"y\": {\"a\": 1, \"b\": 2}}`\n\t\texpectedVal := unmarshalExtJSONValue(t, expectedDoc)\n\n\t\textraKeysAtRoot := `{\"x\": 1, \"y\": {\"a\": 1, \"b\": 2}, \"z\": 3}`\n\t\textraKeysInEmbedded := `{\"x\": 1, \"y\": {\"a\": 1, \"b\": 2, \"c\": 3}}`\n\n\t\ttestCases := []struct {\n\t\t\tname    string\n\t\t\tactual  string\n\t\t\tmatches bool\n\t\t}{\n\t\t\t{\"exact match\", expectedDoc, true},\n\t\t\t{\"extra keys allowed at root-level\", extraKeysAtRoot, true},\n\t\t\t{\"incorrect type for y\", `{\"x\": 1, \"y\": 2}`, false},\n\t\t\t{\"extra keys prohibited in embedded documents\", extraKeysInEmbedded, false},\n\t\t}\n\t\tfor _, tc := range testCases {\n\t\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\t\tassertMatches(t, expectedVal, unmarshalExtJSONValue(t, tc.actual), tc.matches)\n\t\t\t})\n\t\t}\n\t})\n\tt.Run(\"documents with extra keys not allowed\", func(t *testing.T) {\n\t\texpected := unmarshalExtJSONValue(t, `{\"x\": 1}`)\n\t\tactual := unmarshalExtJSONValue(t, `{\"x\": 1, \"y\": 1}`)\n\t\terr := verifyValuesMatch(ctx, expected, actual, false)\n\t\tassert.NotNil(t, err, \"expected values to not match, but got no error\")\n\t})\n\tt.Run(\"exists operator\", func(t *testing.T) {\n\t\trootExists := unmarshalExtJSONValue(t, `{\"x\": {\"$$exists\": true}}`)\n\t\trootNotExists := unmarshalExtJSONValue(t, `{\"x\": {\"$$exists\": false}}`)\n\t\tembeddedExists := unmarshalExtJSONValue(t, `{\"x\": {\"y\": {\"$$exists\": true}}}`)\n\t\tembeddedNotExists := unmarshalExtJSONValue(t, `{\"x\": {\"y\": {\"$$exists\": false}}}`)\n\n\t\ttestCases := []struct {\n\t\t\tname     string\n\t\t\texpected bson.RawValue\n\t\t\tactual   string\n\t\t\tmatches  bool\n\t\t}{\n\t\t\t{\"root - should exist and does\", rootExists, `{\"x\": 1}`, true},\n\t\t\t{\"root - should exist and does not\", rootExists, `{\"y\": 1}`, false},\n\t\t\t{\"root - should not exist and does\", rootNotExists, `{\"x\": 1}`, false},\n\t\t\t{\"root - should not exist and does not\", rootNotExists, `{\"y\": 1}`, true},\n\t\t\t{\"embedded - should exist and does\", embeddedExists, `{\"x\": {\"y\": 1}}`, true},\n\t\t\t{\"embedded - should exist and does not\", embeddedExists, `{\"x\": {}}`, false},\n\t\t\t{\"embedded - should not exist and does\", embeddedNotExists, `{\"x\": {\"y\": 1}}`, false},\n\t\t\t{\"embedded - should not exist and does not\", embeddedNotExists, `{\"x\": {}}`, true},\n\t\t}\n\t\tfor _, tc := range testCases {\n\t\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\t\tassertMatches(t, tc.expected, unmarshalExtJSONValue(t, tc.actual), tc.matches)\n\t\t\t})\n\t\t}\n\t})\n\tt.Run(\"type operator\", func(t *testing.T) {\n\t\tsingleType := unmarshalExtJSONValue(t, `{\"x\": {\"$$type\": \"string\"}}`)\n\t\tarrayTypes := unmarshalExtJSONValue(t, `{\"x\": {\"$$type\": [\"string\", \"bool\"]}}`)\n\n\t\ttestCases := []struct {\n\t\t\tname     string\n\t\t\texpected bson.RawValue\n\t\t\tactual   string\n\t\t\tmatches  bool\n\t\t}{\n\t\t\t{\"single type matches\", singleType, `{\"x\": \"foo\"}`, true},\n\t\t\t{\"single type does not match\", singleType, `{\"x\": 1}`, false},\n\t\t\t{\"multiple types matches first\", arrayTypes, `{\"x\": \"foo\"}`, true},\n\t\t\t{\"multiple types matches second\", arrayTypes, `{\"x\": true}`, true},\n\t\t\t{\"multiple types does not match\", arrayTypes, `{\"x\": 1}`, false},\n\t\t}\n\t\tfor _, tc := range testCases {\n\t\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\t\tassertMatches(t, tc.expected, unmarshalExtJSONValue(t, tc.actual), tc.matches)\n\t\t\t})\n\t\t}\n\t})\n\tt.Run(\"matchesHexBytes operator\", func(t *testing.T) {\n\t\tsingleValue := unmarshalExtJSONValue(t, `{\"$$matchesHexBytes\": \"DEADBEEF\"}`)\n\t\tdocument := unmarshalExtJSONValue(t, `{\"x\": {\"$$matchesHexBytes\": \"DEADBEEF\"}}`)\n\n\t\tstringToBinary := func(str string) bson.RawValue {\n\t\t\thexBytes, err := hex.DecodeString(str)\n\t\t\tassert.Nil(t, err, \"hex.DecodeString error: %v\", err)\n\t\t\treturn bson.RawValue{\n\t\t\t\tType:  bson.TypeBinary,\n\t\t\t\tValue: bsoncore.AppendBinary(nil, 0, hexBytes),\n\t\t\t}\n\t\t}\n\n\t\ttestCases := []struct {\n\t\t\tname     string\n\t\t\texpected bson.RawValue\n\t\t\tactual   bson.RawValue\n\t\t\tmatches  bool\n\t\t}{\n\t\t\t{\"single value matches\", singleValue, stringToBinary(\"DEADBEEF\"), true},\n\t\t\t{\"single value does not match\", singleValue, stringToBinary(\"BEEF\"), false},\n\t\t\t{\"document matches\", document, marshalValue(t, bson.M{\"x\": stringToBinary(\"DEADBEEF\")}), true},\n\t\t\t{\"document does not match\", document, marshalValue(t, bson.M{\"x\": stringToBinary(\"BEEF\")}), false},\n\t\t}\n\t\tfor _, tc := range testCases {\n\t\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\t\tassertMatches(t, tc.expected, tc.actual, tc.matches)\n\t\t\t})\n\t\t}\n\t})\n\tt.Run(\"unsetOrMatches operator\", func(t *testing.T) {\n\t\ttopLevel := unmarshalExtJSONValue(t, `{\"$$unsetOrMatches\": {\"x\": 1}}`)\n\t\tnested := unmarshalExtJSONValue(t, `{\"x\": {\"$$unsetOrMatches\": {\"y\": 1}}}`)\n\n\t\ttestCases := []struct {\n\t\t\tname     string\n\t\t\texpected bson.RawValue\n\t\t\tactual   string\n\t\t\tmatches  bool\n\t\t}{\n\t\t\t{\"top-level unset\", topLevel, \"\", true},\n\t\t\t{\"top-level matches\", topLevel, `{\"x\": 1}`, true},\n\t\t\t{\"top-level matches with extra keys\", topLevel, `{\"x\": 1, \"y\": 1}`, true},\n\t\t\t{\"top-level does not match\", topLevel, `{\"x\": 2}`, false},\n\t\t\t{\"nested unset\", nested, `{}`, true},\n\t\t\t{\"nested matches\", nested, `{\"x\": {\"y\": 1}}`, true},\n\t\t\t{\"nested field exists but is null\", nested, `{\"x\": null}`, false}, // null should not be considered unset\n\t\t\t{\"nested does not match\", nested, `{\"x\": {\"y\": 2}}`, false},\n\t\t\t{\"nested does not match due to extra keys\", nested, `{\"x\": {\"y\": 1, \"z\": 1}}`, false},\n\t\t}\n\t\tfor _, tc := range testCases {\n\t\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\t\tassertMatches(t, tc.expected, unmarshalExtJSONValue(t, tc.actual), tc.matches)\n\t\t\t})\n\t\t}\n\t})\n}\n"
  },
  {
    "path": "internal/integration/unified/operation.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage unified\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/csot\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo\"\n)\n\ntype operation struct {\n\tName                 string         `bson:\"name\"`\n\tObject               string         `bson:\"object\"`\n\tArguments            bson.Raw       `bson:\"arguments\"`\n\tIgnoreResultAndError bool           `bson:\"ignoreResultAndError\"`\n\tExpectedError        *expectedError `bson:\"expectError\"`\n\tExpectedResult       *bson.RawValue `bson:\"expectResult\"`\n\tResultEntityID       *string        `bson:\"saveResultAsEntity\"`\n}\n\n// execute runs the operation and verifies the returned result and/or error. If the result needs to be saved as\n// an entity, it also updates the entityMap associated with ctx to do so.\nfunc (op *operation) execute(ctx context.Context, loopDone <-chan struct{}) error {\n\tres, err := op.run(ctx, loopDone)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"execution failed: %v\", err)\n\t}\n\n\tif op.IgnoreResultAndError {\n\t\treturn nil\n\t}\n\n\tif err := verifyOperationError(ctx, op.ExpectedError, res); err != nil {\n\t\treturn fmt.Errorf(\"error verification failed: %v\", err)\n\t}\n\n\tif op.ExpectedResult != nil {\n\t\tif err := verifyOperationResult(ctx, *op.ExpectedResult, res); err != nil {\n\t\t\treturn fmt.Errorf(\"result verification failed: %v\", err)\n\t\t}\n\t}\n\treturn nil\n}\n\n// isCreateView will return true if the operation is to create a collection with a view.\nfunc (op *operation) isCreateView() (bool, error) {\n\tif op.Name != \"createCollection\" {\n\t\treturn false, nil\n\t}\n\n\telements, err := op.Arguments.Elements()\n\tif err != nil {\n\t\treturn false, err\n\t}\n\n\tvar has bool\n\tfor _, elem := range elements {\n\t\tif elem.Key() == \"viewOn\" {\n\t\t\thas = true\n\t\t\tbreak\n\t\t}\n\t}\n\treturn has, nil\n}\n\nfunc (op *operation) run(ctx context.Context, loopDone <-chan struct{}) (*operationResult, error) {\n\tif op.Object == \"testRunner\" {\n\t\t// testRunner operations don't have results or expected errors, so we use newEmptyResult to fake a result.\n\t\treturn newEmptyResult(), executeTestRunnerOperation(ctx, op, loopDone)\n\t}\n\n\t// Special handling for the \"session\" field because it applies to all operations.\n\tif id, ok := op.Arguments.Lookup(\"session\").StringValueOK(); ok {\n\t\tsess, err := entities(ctx).session(id)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tctx = mongo.NewSessionContext(ctx, sess)\n\n\t\t// Set op.Arguments to a new document that has the \"session\" field removed so individual operations do\n\t\t// not have to account for it.\n\t\top.Arguments = removeFieldsFromDocument(op.Arguments, \"session\")\n\t}\n\n\t// Special handling for the \"timeoutMS\" field because it applies to (almost) all operations.\n\tif tms, ok := op.Arguments.Lookup(\"timeoutMS\").Int32OK(); ok {\n\t\ttimeout := time.Duration(tms) * time.Millisecond\n\n\t\t// Note that a 0-timeout at the operation level is not actually possible\n\t\t// in Go. This would result in an immediate \"context deadline exceeded\"\n\t\t// error.\n\t\t//\n\t\t// To achieve an \"infinite\" case, users would have to rely on either (1)\n\t\t// defining a 0 timeout at the client-level, or (2) use\n\t\t// context.Background() at the operation-level.\n\t\tnewCtx, cancelFunc := csot.WithTimeout(ctx, &timeout)\n\n\t\t// Redefine ctx to be the new timeout-derived context.\n\t\tctx = newCtx\n\t\t// Cancel the timeout-derived context at the end of run to avoid a context leak.\n\t\tdefer cancelFunc()\n\n\t\t// Set op.Arguments to a new document that has the \"timeoutMS\" field removed\n\t\t// so individual operations do not have to account for it.\n\t\top.Arguments = removeFieldsFromDocument(op.Arguments, \"timeoutMS\")\n\t}\n\n\tswitch op.Name {\n\t// Session operations\n\tcase \"abortTransaction\":\n\t\treturn executeAbortTransaction(ctx, op)\n\tcase \"commitTransaction\":\n\t\treturn executeCommitTransaction(ctx, op)\n\tcase \"endSession\":\n\t\t// The EndSession() method doesn't return a result, so we return a non-nil empty result.\n\t\treturn newEmptyResult(), executeEndSession(ctx, op)\n\tcase \"startTransaction\":\n\t\treturn executeStartTransaction(ctx, op)\n\tcase \"withTransaction\":\n\t\t// executeWithTransaction internally verifies results/errors for each operation, so it doesn't return a result.\n\t\treturn newEmptyResult(), executeWithTransaction(ctx, op, loopDone)\n\tcase \"getSnapshotTime\":\n\t\t// executeGetSnapshotTime stores the snapshot time of the session as on\n\t\t// the entity map for subsequent use.\n\t\treturn executeGetSnapshotTime(ctx, op)\n\n\t\t// Client operations\n\tcase \"appendMetadata\":\n\t\treturn executeAppendMetadata(ctx, op)\n\tcase \"createChangeStream\":\n\t\treturn executeCreateChangeStream(ctx, op)\n\tcase \"listDatabases\":\n\t\treturn executeListDatabases(ctx, op, false)\n\tcase \"listDatabaseNames\":\n\t\treturn executeListDatabases(ctx, op, true)\n\tcase \"clientBulkWrite\":\n\t\treturn executeClientBulkWrite(ctx, op)\n\n\t// Database operations\n\tcase \"createCollection\":\n\t\treturn executeCreateCollection(ctx, op)\n\tcase \"dropCollection\":\n\t\treturn executeDropCollection(ctx, op)\n\tcase \"listCollections\":\n\t\treturn executeListCollections(ctx, op)\n\tcase \"listCollectionNames\":\n\t\treturn executeListCollectionNames(ctx, op)\n\tcase \"modifyCollection\":\n\t\treturn executeModifyCollection(ctx, op)\n\tcase \"runCommand\":\n\t\treturn executeRunCommand(ctx, op)\n\tcase \"runCursorCommand\":\n\t\treturn executeRunCursorCommand(ctx, op)\n\tcase \"createCommandCursor\":\n\t\treturn executeCreateRunCursorCommand(ctx, op)\n\n\t// Collection operations\n\tcase \"aggregate\":\n\t\treturn executeAggregate(ctx, op)\n\tcase \"bulkWrite\":\n\t\treturn executeBulkWrite(ctx, op)\n\tcase \"countDocuments\":\n\t\treturn executeCountDocuments(ctx, op)\n\tcase \"createFindCursor\":\n\t\treturn executeCreateFindCursor(ctx, op)\n\tcase \"createIndex\":\n\t\treturn executeCreateIndex(ctx, op)\n\tcase \"createSearchIndex\":\n\t\treturn executeCreateSearchIndex(ctx, op)\n\tcase \"createSearchIndexes\":\n\t\treturn executeCreateSearchIndexes(ctx, op)\n\tcase \"deleteOne\":\n\t\treturn executeDeleteOne(ctx, op)\n\tcase \"deleteMany\":\n\t\treturn executeDeleteMany(ctx, op)\n\tcase \"distinct\":\n\t\treturn executeDistinct(ctx, op)\n\tcase \"dropIndex\":\n\t\treturn executeDropIndex(ctx, op)\n\tcase \"dropIndexes\":\n\t\treturn executeDropIndexes(ctx, op)\n\tcase \"dropSearchIndex\":\n\t\treturn executeDropSearchIndex(ctx, op)\n\tcase \"estimatedDocumentCount\":\n\t\treturn executeEstimatedDocumentCount(ctx, op)\n\tcase \"find\":\n\t\treturn executeFind(ctx, op) // Can also be a GridFS operation\n\tcase \"findOne\":\n\t\treturn executeFindOne(ctx, op)\n\tcase \"findOneAndDelete\":\n\t\treturn executeFindOneAndDelete(ctx, op)\n\tcase \"findOneAndReplace\":\n\t\treturn executeFindOneAndReplace(ctx, op)\n\tcase \"findOneAndUpdate\":\n\t\treturn executeFindOneAndUpdate(ctx, op)\n\tcase \"insertMany\":\n\t\treturn executeInsertMany(ctx, op)\n\tcase \"insertOne\":\n\t\treturn executeInsertOne(ctx, op)\n\tcase \"listIndexes\":\n\t\treturn executeListIndexes(ctx, op)\n\tcase \"listSearchIndexes\":\n\t\treturn executeListSearchIndexes(ctx, op)\n\tcase \"rename\":\n\t\t// \"rename\" can either target a collection or a GridFS bucket.\n\t\tif _, err := entities(ctx).collection(op.Object); err == nil {\n\t\t\treturn executeRenameCollection(ctx, op)\n\t\t}\n\t\tif _, err := entities(ctx).gridFSBucket(op.Object); err == nil {\n\t\t\treturn executeBucketRename(ctx, op)\n\t\t}\n\t\treturn nil, fmt.Errorf(\"failed to find a collection or GridFS bucket named %q\", op.Object)\n\tcase \"replaceOne\":\n\t\treturn executeReplaceOne(ctx, op)\n\tcase \"updateOne\":\n\t\treturn executeUpdateOne(ctx, op)\n\tcase \"updateMany\":\n\t\treturn executeUpdateMany(ctx, op)\n\tcase \"updateSearchIndex\":\n\t\treturn executeUpdateSearchIndex(ctx, op)\n\n\t// GridFS operations\n\tcase \"delete\":\n\t\treturn executeBucketDelete(ctx, op)\n\tcase \"downloadByName\":\n\t\treturn executeBucketDownloadByName(ctx, op)\n\tcase \"download\":\n\t\treturn executeBucketDownload(ctx, op)\n\tcase \"drop\":\n\t\treturn executeBucketDrop(ctx, op)\n\tcase \"upload\":\n\t\treturn executeBucketUpload(ctx, op)\n\n\t// Cursor operations\n\tcase \"close\":\n\t\tif cursor, err := entities(ctx).cursor(op.Object); err == nil {\n\t\t\t_ = cursor.Close(ctx)\n\n\t\t\treturn newEmptyResult(), nil\n\t\t}\n\n\t\tif clientEntity, err := entities(ctx).client(op.Object); err == nil {\n\t\t\t_ = clientEntity.disconnect(context.Background())\n\n\t\t\treturn newEmptyResult(), nil\n\t\t}\n\n\t\treturn nil, fmt.Errorf(\"failed to find a cursor or client named %q\", op.Object)\n\tcase \"iterateOnce\":\n\t\treturn executeIterateOnce(ctx, op)\n\tcase \"iterateUntilDocumentOrError\":\n\t\treturn executeIterateUntilDocumentOrError(ctx, op)\n\n\t// CSFLE operations\n\tcase \"createDataKey\":\n\t\treturn executeCreateDataKey(ctx, op)\n\tcase \"rewrapManyDataKey\":\n\t\treturn executeRewrapManyDataKey(ctx, op)\n\tcase \"removeKeyAltName\":\n\t\treturn executeRemoveKeyAltName(ctx, op)\n\tcase \"getKeys\":\n\t\treturn executeGetKeys(ctx, op)\n\tcase \"getKeyByAltName\":\n\t\treturn executeGetKeyByAltName(ctx, op)\n\tcase \"getKey\":\n\t\treturn executeGetKey(ctx, op)\n\tcase \"deleteKey\":\n\t\treturn executeDeleteKey(ctx, op)\n\tcase \"addKeyAltName\":\n\t\treturn executeAddKeyAltName(ctx, op)\n\tcase \"decrypt\":\n\t\treturn executeDecrypt(ctx, op)\n\n\tcase \"assertIndexNotExists\":\n\t\tdb := lookupString(op.Arguments, \"databaseName\")\n\t\tcoll := lookupString(op.Arguments, \"collectionName\")\n\t\tindex := lookupString(op.Arguments, \"indexName\")\n\t\treturn newErrorResult(nil), verifyIndexExists(ctx, db, coll, index, false)\n\tcase \"assertIndexExists\":\n\t\tdb := lookupString(op.Arguments, \"databaseName\")\n\t\tcoll := lookupString(op.Arguments, \"collectionName\")\n\t\tindex := lookupString(op.Arguments, \"indexName\")\n\t\treturn newErrorResult(nil), verifyIndexExists(ctx, db, coll, index, true)\n\tcase \"assertCollectionExists\":\n\t\tdb := lookupString(op.Arguments, \"databaseName\")\n\t\tcoll := lookupString(op.Arguments, \"collectionName\")\n\t\treturn newErrorResult(nil), verifyCollectionExists(ctx, db, coll, true)\n\n\t// Unsupported operations\n\tcase \"count\", \"listIndexNames\", \"mapReduce\":\n\t\treturn nil, newSkipTestError(fmt.Sprintf(\"the %q operation is not supported\", op.Name))\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"unrecognized entity operation %q\", op.Name)\n\t}\n}\n"
  },
  {
    "path": "internal/integration/unified/options.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage unified\n\nvar defaultRunKillAllSessionsValue = true\n\n// Options is the type used to configure tests\ntype Options struct {\n\t// Specifies if killAllSessions should be run after the test completes.\n\t// Defaults to true\n\tRunKillAllSessions *bool\n}\n\n// NewOptions creates an empty options interface\nfunc NewOptions() *Options {\n\treturn &Options{&defaultRunKillAllSessionsValue}\n}\n\n// SetRunKillAllSessions sets the value for RunKillAllSessions\nfunc (op *Options) SetRunKillAllSessions(killAllSessions bool) *Options {\n\top.RunKillAllSessions = &killAllSessions\n\treturn op\n}\n"
  },
  {
    "path": "internal/integration/unified/result.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage unified\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n)\n\n// operationResult holds the result and/or error returned by an op.\ntype operationResult struct {\n\t// For operations that return a single result, this field holds a BSON representation.\n\tResult bson.RawValue\n\n\t// CursorResult holds the documents retrieved by iterating a Cursor.\n\tCursorResult []bson.Raw\n\n\t// Err holds the error returned by an operation. This is mutually exclusive with CursorResult but not with Result\n\t// because some operations (e.g. bulkWrite) can return both a result and an error.\n\tErr error\n}\n\n// newEmptyResult returns an operationResult with no fields set. This should be used if the operation does not check\n// results or errors.\nfunc newEmptyResult() *operationResult {\n\treturn &operationResult{}\n}\n\n// newDocumentResult is a helper to create a value result where the value is a BSON document.\nfunc newDocumentResult(result []byte, err error) *operationResult {\n\treturn newValueResult(bson.TypeEmbeddedDocument, result, err)\n}\n\n// newValueResult creates an operationResult where the result is a BSON value of an arbitrary type. Because some\n// operations can return both a result and an error (e.g. bulkWrite), the err parameter should be the error returned\n// by the op, if any.\nfunc newValueResult(valueType bson.Type, data []byte, err error) *operationResult {\n\treturn &operationResult{\n\t\tResult: bson.RawValue{Type: valueType, Value: data},\n\t\tErr:    err,\n\t}\n}\n\n// newCursorResult creates an operationResult that contains documents retrieved by fully iterating a cursor.\nfunc newCursorResult(arr []bson.Raw) *operationResult {\n\t// If the operation returned no documents, the array might be nil. It isn't possible to distinguish between this\n\t// case and the case where there is no cursor result, so we overwrite the result with an non-nil empty slice.\n\tresult := arr\n\tif result == nil {\n\t\tresult = make([]bson.Raw, 0)\n\t}\n\n\treturn &operationResult{\n\t\tCursorResult: result,\n\t}\n}\n\n// newErrorResult creates an operationResult that only holds an error. This should only be used when executing an\n// operation that can return a result or an error, but not both.\nfunc newErrorResult(err error) *operationResult {\n\treturn &operationResult{\n\t\tErr: err,\n\t}\n}\n\n// verifyOperationResult checks that the actual and expected results match\nfunc verifyOperationResult(ctx context.Context, expected bson.RawValue, actual *operationResult) error {\n\tactualVal := actual.Result\n\tif actual.CursorResult != nil {\n\t\t_, data, err := bson.MarshalValue(actual.CursorResult)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"error converting cursorResult array to BSON: %v\", err)\n\t\t}\n\n\t\tactualVal = bson.RawValue{\n\t\t\tType:  bson.TypeArray,\n\t\t\tValue: data,\n\t\t}\n\t}\n\n\t// For document results and arrays of root documents (i.e. cursor results), the actual value can have additional\n\t// top-level keys. Single-value array results (e.g. from distinct) must match exactly, so we set extraKeysAllowed to\n\t// false only for that case.\n\textraKeysAllowed := actual.Result.Type != bson.TypeArray\n\treturn verifyValuesMatch(ctx, expected, actualVal, extraKeysAllowed)\n}\n"
  },
  {
    "path": "internal/integration/unified/schema_version.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage unified\n\nimport (\n\t\"fmt\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/integration/mtest\"\n)\n\nvar supportedSchemaVersions = map[int]string{\n\t1: \"1.26\",\n}\n\n// checkSchemaVersion determines if the provided schema version is supported and returns an error if it is not.\nfunc checkSchemaVersion(version string) error {\n\t// First get the major version number from the schema. The schema version string should be in the format\n\t// \"major.minor.patch\", \"major.minor\", or \"major\".\n\n\tparts := strings.Split(version, \".\")\n\tif len(parts) == 0 {\n\t\treturn fmt.Errorf(\"error splitting schema version %q into parts\", version)\n\t}\n\n\tmajorVersion, err := strconv.Atoi(parts[0])\n\tif err != nil {\n\t\treturn fmt.Errorf(\"error converting major version component %q to an integer: %v\", parts[0], err)\n\t}\n\n\t// Find the latest supported version for the major version and use that to determine if the provided version is\n\t// supported.\n\tsupportedVersion, ok := supportedSchemaVersions[majorVersion]\n\tif !ok {\n\t\treturn fmt.Errorf(\"major version %d not supported\", majorVersion)\n\t}\n\tif mtest.CompareServerVersions(supportedVersion, version) < 0 {\n\t\treturn fmt.Errorf(\n\t\t\t\"latest version supported for major version %d is %q, which is incompatible with specified version %q\",\n\t\t\tmajorVersion, supportedVersion, version,\n\t\t)\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "internal/integration/unified/server_api_options.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage unified\n\nimport (\n\t\"fmt\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n)\n\n// serverAPIOptions is a wrapper for *options.ServerAPIOptions. This type implements the bson.Unmarshaler interface\n// to convert BSON documents to a serverAPIOptions instance.\ntype serverAPIOptions struct {\n\t*options.ServerAPIOptions\n}\n\ntype serverAPIVersion = options.ServerAPIVersion\n\nvar _ bson.Unmarshaler = (*serverAPIOptions)(nil)\n\nfunc (s *serverAPIOptions) UnmarshalBSON(data []byte) error {\n\tvar temp struct {\n\t\tServerAPIVersion  serverAPIVersion `bson:\"version\"`\n\t\tDeprecationErrors *bool            `bson:\"deprecationErrors\"`\n\t\tStrict            *bool            `bson:\"strict\"`\n\t\tExtra             map[string]any   `bson:\",inline\"`\n\t}\n\tif err := bson.Unmarshal(data, &temp); err != nil {\n\t\treturn fmt.Errorf(\"error unmarshalling to temporary serverAPIOptions object: %v\", err)\n\t}\n\tif len(temp.Extra) > 0 {\n\t\treturn fmt.Errorf(\"unrecognized fields for serverAPIOptions: %v\", mapKeys(temp.Extra))\n\t}\n\n\ts.ServerAPIOptions = options.ServerAPI(temp.ServerAPIVersion)\n\tif temp.DeprecationErrors != nil {\n\t\ts.SetDeprecationErrors(*temp.DeprecationErrors)\n\t}\n\tif temp.Strict != nil {\n\t\ts.SetStrict(*temp.Strict)\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "internal/integration/unified/session_operation_execution.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage unified\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/testutil\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/session\"\n)\n\nfunc executeAbortTransaction(ctx context.Context, operation *operation) (*operationResult, error) {\n\tsess, err := entities(ctx).session(operation.Object)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// AbortTransaction takes no options, so the arguments doc must be nil or empty.\n\telems, _ := operation.Arguments.Elements()\n\tif len(elems) > 0 {\n\t\treturn nil, fmt.Errorf(\"unrecognized abortTransaction options %v\", operation.Arguments)\n\t}\n\n\treturn newErrorResult(sess.AbortTransaction(ctx)), nil\n}\n\nfunc executeEndSession(ctx context.Context, operation *operation) error {\n\tsess, err := entities(ctx).session(operation.Object)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// EnsSession takes no options, so the arguments doc must be nil or empty.\n\telems, _ := operation.Arguments.Elements()\n\tif len(elems) > 0 {\n\t\treturn fmt.Errorf(\"unrecognized endSession options %v\", operation.Arguments)\n\t}\n\n\tsess.EndSession(ctx)\n\treturn nil\n}\n\nfunc executeCommitTransaction(ctx context.Context, operation *operation) (*operationResult, error) {\n\tsess, err := entities(ctx).session(operation.Object)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// CommitTransaction takes no options, so the arguments doc must be nil or empty.\n\telems, _ := operation.Arguments.Elements()\n\tif len(elems) > 0 {\n\t\treturn nil, fmt.Errorf(\"unrecognized commitTransaction options %v\", operation.Arguments)\n\t}\n\n\treturn newErrorResult(sess.CommitTransaction(ctx)), nil\n}\n\nfunc executeStartTransaction(ctx context.Context, operation *operation) (*operationResult, error) {\n\tsess, err := entities(ctx).session(operation.Object)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\topts := options.Transaction()\n\tif operation.Arguments != nil {\n\t\tvar temp transactionOptions\n\t\tif err := bson.Unmarshal(operation.Arguments, &temp); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"error unmarshalling arguments to transactionOptions: %v\", err)\n\t\t}\n\n\t\topts = temp.TransactionOptionsBuilder\n\t}\n\n\treturn newErrorResult(sess.StartTransaction(opts)), nil\n}\n\nfunc executeWithTransaction(ctx context.Context, op *operation, loopDone <-chan struct{}) error {\n\tsess, err := entities(ctx).session(op.Object)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// Process the \"callback\" argument. This is an array of operation objects, each of which should be executed inside\n\t// the transaction.\n\tcallback, err := op.Arguments.LookupErr(\"callback\")\n\tif err != nil {\n\t\treturn newMissingArgumentError(\"callback\")\n\t}\n\tvar operations []*operation\n\tif err := callback.Unmarshal(&operations); err != nil {\n\t\treturn fmt.Errorf(\"error transforming callback option to slice of operations: %v\", err)\n\t}\n\n\t// Remove the \"callback\" field and process the other options.\n\tvar temp transactionOptions\n\tif err := bson.Unmarshal(removeFieldsFromDocument(op.Arguments, \"callback\"), &temp); err != nil {\n\t\treturn fmt.Errorf(\"error unmarshalling arguments to transactionOptions: %v\", err)\n\t}\n\n\t_, err = sess.WithTransaction(ctx, func(ctx context.Context) (any, error) {\n\t\tfor idx, oper := range operations {\n\t\t\tif err := oper.execute(ctx, loopDone); err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"error executing operation %q at index %d: %v\", oper.Name, idx, err)\n\t\t\t}\n\t\t}\n\t\treturn nil, nil\n\t}, temp.TransactionOptionsBuilder)\n\treturn err\n}\n\nfunc executeGetSnapshotTime(ctx context.Context, op *operation) (*operationResult, error) {\n\tentityID := op.ResultEntityID\n\tif entityID == nil {\n\t\treturn nil, fmt.Errorf(\"getSnapshotTime operation requires a result entity ID\")\n\t}\n\n\tsess, err := entities(ctx).session(op.Object)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tclientSess := testutil.GetUnexportedFieldAs[*session.Client](sess, \"clientSession\")\n\n\tif !clientSess.SnapshotTimeSet {\n\t\treturn nil, fmt.Errorf(\"session has no snapshot time to store in entity %q\", *entityID)\n\t}\n\n\tif err := entities(ctx).addBSONEntity(*entityID, clientSess.SnapshotTime); err != nil {\n\t\treturn nil, fmt.Errorf(\"error storing result as BSON entity: %w\", err)\n\t}\n\n\treturn newEmptyResult(), nil\n}\n"
  },
  {
    "path": "internal/integration/unified/session_options.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage unified\n\nimport (\n\t\"fmt\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/mongoutil\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n)\n\n// transactionOptions is a wrapper for *options.transactionOptions. This type implements the bson.Unmarshaler interface\n// to convert BSON documents to a transactionOptions instance.\ntype transactionOptions struct {\n\t*options.TransactionOptionsBuilder\n}\n\nvar _ bson.Unmarshaler = (*transactionOptions)(nil)\n\nfunc (to *transactionOptions) UnmarshalBSON(data []byte) error {\n\tvar temp struct {\n\t\tRC    *readConcern    `bson:\"readConcern\"`\n\t\tRP    *ReadPreference `bson:\"readPreference\"`\n\t\tWC    *writeConcern   `bson:\"writeConcern\"`\n\t\tExtra map[string]any  `bson:\",inline\"`\n\t}\n\tif err := bson.Unmarshal(data, &temp); err != nil {\n\t\treturn fmt.Errorf(\"error unmarshalling to temporary transactionOptions object: %v\", err)\n\t}\n\tif len(temp.Extra) > 0 {\n\t\treturn fmt.Errorf(\"unrecognized fields for transactionOptions: %v\", mapKeys(temp.Extra))\n\t}\n\n\tto.TransactionOptionsBuilder = options.Transaction()\n\tif rc := temp.RC; rc != nil {\n\t\tto.SetReadConcern(rc.toReadConcernOption())\n\t}\n\tif rp := temp.RP; rp != nil {\n\t\tconverted, err := rp.ToReadPrefOption()\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"error parsing read preference document: %v\", err)\n\t\t}\n\t\tto.SetReadPreference(converted)\n\t}\n\tif wc := temp.WC; wc != nil {\n\t\tconverted, err := wc.toWriteConcernOption()\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"error parsing write concern document: %v\", err)\n\t\t}\n\t\tto.SetWriteConcern(converted)\n\t}\n\treturn nil\n}\n\n// sessionOptions is a wrapper for *options.sessionOptions. This type implements the bson.Unmarshaler interface to\n// convert BSON documents to a sessionOptions instance.\ntype sessionOptions struct {\n\t*options.SessionOptionsBuilder\n\tsnapshotTimeID *string // Store the ID for later lookup in EntityMap\n}\n\nvar _ bson.Unmarshaler = (*sessionOptions)(nil)\n\nfunc (so *sessionOptions) UnmarshalBSON(data []byte) error {\n\tvar temp struct {\n\t\tCausal       *bool               `bson:\"causalConsistency\"`\n\t\tTxnOptions   *transactionOptions `bson:\"defaultTransactionOptions\"`\n\t\tSnapshot     *bool               `bson:\"snapshot\"`\n\t\tSnapshotTime *string             `bson:\"snapshotTime\"`\n\t\tExtra        map[string]any      `bson:\",inline\"`\n\t}\n\tif err := bson.Unmarshal(data, &temp); err != nil {\n\t\treturn fmt.Errorf(\"error unmarshalling to temporary sessionOptions object: %v\", err)\n\t}\n\tif len(temp.Extra) > 0 {\n\t\treturn fmt.Errorf(\"unrecognized fields for sessionOptions: %v\", mapKeys(temp.Extra))\n\t}\n\n\tso.SessionOptionsBuilder = options.Session()\n\tif temp.Causal != nil {\n\t\tso.SetCausalConsistency(*temp.Causal)\n\t}\n\tif temp.TxnOptions != nil {\n\t\ttxnArgs, err := mongoutil.NewOptions[options.TransactionOptions](temp.TxnOptions)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to construct options from builder: %w\", err)\n\t\t}\n\n\t\ttxnOpts := options.Transaction()\n\t\tif rc := txnArgs.ReadConcern; rc != nil {\n\t\t\ttxnOpts.SetReadConcern(rc)\n\t\t}\n\t\tif rp := txnArgs.ReadPreference; rp != nil {\n\t\t\ttxnOpts.SetReadPreference(rp)\n\t\t}\n\t\tif wc := txnArgs.WriteConcern; wc != nil {\n\t\t\ttxnOpts.SetWriteConcern(wc)\n\t\t}\n\n\t\tso.SetDefaultTransactionOptions(txnOpts)\n\t}\n\tif temp.Snapshot != nil {\n\t\tso.SetSnapshot(*temp.Snapshot)\n\t}\n\n\t// Store the snapshot time ID for later lookup\n\tso.snapshotTimeID = temp.SnapshotTime\n\n\treturn nil\n}\n"
  },
  {
    "path": "internal/integration/unified/testrunner_operation.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage unified\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"strings\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/integration/mtest\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/testutil\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/session\"\n)\n\n// waitForEventTimeout is the amount of time to wait for an event to occur. The\n// maximum amount of time expected for this value is currently 10 seconds, which\n// is the amount of time that the driver will attempt to wait between streamable\n// heartbeats. Increase this value if a new maximum time is expected in another\n// operation.\nvar waitForEventTimeout = 11 * time.Second\n\ntype loopArgs struct {\n\tOperations         []*operation `bson:\"operations\"`\n\tErrorsEntityID     string       `bson:\"storeErrorsAsEntity\"`\n\tFailuresEntityID   string       `bson:\"storeFailuresAsEntity\"`\n\tSuccessesEntityID  string       `bson:\"storeSuccessesAsEntity\"`\n\tIterationsEntityID string       `bson:\"storeIterationsAsEntity\"`\n}\n\nfunc (lp *loopArgs) errorsStored() bool {\n\treturn lp.ErrorsEntityID != \"\"\n}\n\nfunc (lp *loopArgs) failuresStored() bool {\n\treturn lp.FailuresEntityID != \"\"\n}\n\nfunc (lp *loopArgs) successesStored() bool {\n\treturn lp.SuccessesEntityID != \"\"\n}\n\nfunc (lp *loopArgs) iterationsStored() bool {\n\treturn lp.IterationsEntityID != \"\"\n}\n\ntype assertEventCountArguments struct {\n\tClientID string              `bson:\"client\"`\n\tEvent    map[string]bson.Raw `bson:\"event\"`\n\tCount    int32               `bson:\"count\"`\n}\n\nfunc assertEventCount(ctx context.Context, args assertEventCountArguments) error {\n\tclient, err := entities(ctx).client(args.ClientID)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor rawEventType, eventDoc := range args.Event {\n\t\teventType, ok := monitoringEventTypeFromString(rawEventType)\n\t\tif !ok {\n\t\t\tcontinue\n\t\t}\n\n\t\tvar actualCount int32\n\t\tswitch eventType {\n\t\tcase serverDescriptionChangedEvent:\n\t\t\tactualCount = getServerDescriptionChangedEventCount(client, eventDoc)\n\t\t\tif actualCount == args.Count {\n\t\t\t\treturn nil\n\t\t\t}\n\t\tdefault:\n\t\t\tactualCount = client.getEventCount(eventType)\n\t\t\tif actualCount == args.Count {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\n\t\treturn fmt.Errorf(\"expected event %q to have occurred %v times, actual: %v\",\n\t\t\trawEventType, args.Count, actualCount)\n\t}\n\n\treturn nil\n}\n\nfunc executeTestRunnerOperation(ctx context.Context, op *operation, loopDone <-chan struct{}) error {\n\targs := op.Arguments\n\n\tswitch op.Name {\n\tcase \"failPoint\":\n\t\tclientID := lookupString(args, \"client\")\n\t\tclient, err := entities(ctx).client(clientID)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tfpDoc := args.Lookup(\"failPoint\").Document()\n\t\tif err := mtest.SetRawFailPoint(fpDoc, client.Client); err != nil {\n\t\t\treturn err\n\t\t}\n\t\taddFailPoint(ctx, fpDoc.Index(0).Value().StringValue(), client.Client)\n\t\treturn nil\n\tcase \"targetedFailPoint\":\n\t\tsessID := lookupString(args, \"session\")\n\t\tsess, err := entities(ctx).session(sessID)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tclientSession := testutil.GetUnexportedFieldAs[*session.Client](sess, \"clientSession\")\n\t\tif clientSession.PinnedServerAddr == nil {\n\t\t\treturn fmt.Errorf(\"session is not pinned to a server\")\n\t\t}\n\n\t\ttargetHost := clientSession.PinnedServerAddr.String()\n\t\tfpDoc := args.Lookup(\"failPoint\").Document()\n\t\tcommandFn := func(_ context.Context, client *mongo.Client) error {\n\t\t\treturn mtest.SetRawFailPoint(fpDoc, client)\n\t\t}\n\n\t\tif err := runCommandOnHost(ctx, targetHost, commandFn); err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn addTargetedFailPoint(ctx, fpDoc.Index(0).Value().StringValue(), targetHost)\n\tcase \"assertSessionTransactionState\":\n\t\tsessID := lookupString(args, \"session\")\n\t\tsess, err := entities(ctx).session(sessID)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tvar expectedState session.TransactionState\n\t\tswitch stateStr := lookupString(args, \"state\"); stateStr {\n\t\tcase \"none\":\n\t\t\texpectedState = session.None\n\t\tcase \"starting\":\n\t\t\texpectedState = session.Starting\n\t\tcase \"in_progress\":\n\t\t\texpectedState = session.InProgress\n\t\tcase \"committed\":\n\t\t\texpectedState = session.Committed\n\t\tcase \"aborted\":\n\t\t\texpectedState = session.Aborted\n\t\tdefault:\n\t\t\treturn fmt.Errorf(\"unrecognized session state type %q\", stateStr)\n\t\t}\n\n\t\tif actualState := testutil.GetUnexportedFieldAs[*session.Client](sess, \"clientSession\").TransactionState; actualState != expectedState {\n\t\t\treturn fmt.Errorf(\"expected session state %q does not match actual state %q\", expectedState, actualState)\n\t\t}\n\t\treturn nil\n\tcase \"assertSessionPinned\":\n\t\treturn verifySessionPinnedState(ctx, lookupString(args, \"session\"), true)\n\tcase \"assertSessionUnpinned\":\n\t\treturn verifySessionPinnedState(ctx, lookupString(args, \"session\"), false)\n\tcase \"assertSameLsidOnLastTwoCommands\":\n\t\treturn verifyLastTwoLsidsEqual(ctx, lookupString(args, \"client\"), true)\n\tcase \"assertDifferentLsidOnLastTwoCommands\":\n\t\treturn verifyLastTwoLsidsEqual(ctx, lookupString(args, \"client\"), false)\n\tcase \"assertSessionDirty\":\n\t\treturn verifySessionDirtyState(ctx, lookupString(args, \"session\"), true)\n\tcase \"assertSessionNotDirty\":\n\t\treturn verifySessionDirtyState(ctx, lookupString(args, \"session\"), false)\n\tcase \"assertCollectionExists\":\n\t\tdb := lookupString(args, \"databaseName\")\n\t\tcoll := lookupString(args, \"collectionName\")\n\t\treturn verifyCollectionExists(ctx, db, coll, true)\n\tcase \"assertCollectionNotExists\":\n\t\tdb := lookupString(args, \"databaseName\")\n\t\tcoll := lookupString(args, \"collectionName\")\n\t\treturn verifyCollectionExists(ctx, db, coll, false)\n\tcase \"assertIndexExists\":\n\t\tdb := lookupString(args, \"databaseName\")\n\t\tcoll := lookupString(args, \"collectionName\")\n\t\tindex := lookupString(args, \"indexName\")\n\t\treturn verifyIndexExists(ctx, db, coll, index, true)\n\tcase \"assertIndexNotExists\":\n\t\tdb := lookupString(args, \"databaseName\")\n\t\tcoll := lookupString(args, \"collectionName\")\n\t\tindex := lookupString(args, \"indexName\")\n\t\treturn verifyIndexExists(ctx, db, coll, index, false)\n\tcase \"assertEventCount\":\n\t\tvar aecArgs assertEventCountArguments\n\t\tif err := bson.Unmarshal(op.Arguments, &aecArgs); err != nil {\n\t\t\treturn fmt.Errorf(\"error unmarshalling event to assertEventCountArguments: %v\", err)\n\t\t}\n\n\t\treturn assertEventCount(ctx, aecArgs)\n\tcase \"loop\":\n\t\tvar unmarshaledArgs loopArgs\n\t\tif err := bson.Unmarshal(args, &unmarshaledArgs); err != nil {\n\t\t\treturn fmt.Errorf(\"error unmarshalling arguments to loopArgs: %v\", err)\n\t\t}\n\t\treturn executeLoop(ctx, &unmarshaledArgs, loopDone)\n\tcase \"assertNumberConnectionsCheckedOut\":\n\t\tclientID := lookupString(args, \"client\")\n\t\tclient, err := entities(ctx).client(clientID)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\texpected := int32(lookupInteger(args, \"connections\"))\n\t\tactual := client.numberConnectionsCheckedOut()\n\t\tif expected != actual {\n\t\t\treturn fmt.Errorf(\"expected %d connections to be checked out, got %d\", expected, actual)\n\t\t}\n\t\treturn nil\n\tcase \"createEntities\":\n\t\tentitiesRaw, err := args.LookupErr(\"entities\")\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"'entities' argument not found in createEntities operation\")\n\t\t}\n\n\t\tvar createEntities []map[string]*entityOptions\n\t\tif err := entitiesRaw.Unmarshal(&createEntities); err != nil {\n\t\t\treturn fmt.Errorf(\"error unmarshalling 'entities' argument to entityOptions: %v\", err)\n\t\t}\n\n\t\tfor idx, entity := range createEntities {\n\t\t\tfor entityType, entityOptions := range entity {\n\t\t\t\tif entityType == \"client\" && hasOperationalFailpoint(ctx) {\n\t\t\t\t\tentityOptions.setHeartbeatFrequencyMS(lowHeartbeatFrequency)\n\t\t\t\t}\n\n\t\t\t\tif err := entities(ctx).addEntity(ctx, entityType, entityOptions); err != nil {\n\t\t\t\t\treturn fmt.Errorf(\"error creating entity at index %d: %v\", idx, err)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn nil\n\tcase \"wait\":\n\t\twaitDur, err := convertValueToMilliseconds(args.Lookup(\"ms\"))\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\ttime.Sleep(waitDur)\n\n\t\treturn nil\n\tcase \"runOnThread\":\n\t\toperationRaw, err := args.LookupErr(\"operation\")\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"'operation' argument not found in runOnThread operation\")\n\t\t}\n\t\tthreadOp := new(operation)\n\t\tif err := operationRaw.Unmarshal(threadOp); err != nil {\n\t\t\treturn fmt.Errorf(\"error unmarshaling 'operation' argument: %v\", err)\n\t\t}\n\t\tthread := lookupString(args, \"thread\")\n\t\troutine, ok := entities(ctx).routinesMap.Load(thread)\n\t\tif !ok {\n\t\t\treturn fmt.Errorf(\"run on unknown thread: %s\", thread)\n\t\t}\n\t\troutine.(*backgroundRoutine).addTask(threadOp.Name, func() error {\n\t\t\treturn threadOp.execute(ctx, loopDone)\n\t\t})\n\t\treturn nil\n\tcase \"waitForThread\":\n\t\tthread := lookupString(args, \"thread\")\n\t\troutine, ok := entities(ctx).routinesMap.Load(thread)\n\t\tif !ok {\n\t\t\treturn fmt.Errorf(\"wait for unknown thread: %s\", thread)\n\t\t}\n\t\treturn routine.(*backgroundRoutine).stop()\n\tcase \"waitForEvent\":\n\t\tvar wfeArgs waitForEventArguments\n\t\tif err := bson.Unmarshal(op.Arguments, &wfeArgs); err != nil {\n\t\t\treturn fmt.Errorf(\"error unmarshalling event to waitForEventArguments: %v\", err)\n\t\t}\n\n\t\twfeCtx, cancel := context.WithTimeout(ctx, waitForEventTimeout)\n\t\tdefer cancel()\n\n\t\treturn waitForEvent(wfeCtx, wfeArgs)\n\tdefault:\n\t\treturn fmt.Errorf(\"unrecognized testRunner operation %q\", op.Name)\n\t}\n}\n\nfunc executeLoop(ctx context.Context, args *loopArgs, loopDone <-chan struct{}) error {\n\t// setup entities\n\tentityMap := entities(ctx)\n\tif args.errorsStored() {\n\t\tif err := entityMap.addBSONArrayEntity(args.ErrorsEntityID); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif args.failuresStored() {\n\t\tif err := entityMap.addBSONArrayEntity(args.FailuresEntityID); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif args.successesStored() {\n\t\tif err := entityMap.addSuccessesEntity(args.SuccessesEntityID); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif args.iterationsStored() {\n\t\tif err := entityMap.addIterationsEntity(args.IterationsEntityID); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tfor {\n\t\tselect {\n\t\tcase <-loopDone:\n\t\t\treturn nil\n\t\tdefault:\n\t\t\tif args.iterationsStored() {\n\t\t\t\tif err := entityMap.incrementIterations(args.IterationsEntityID); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t}\n\t\t\tvar loopErr error\n\t\t\tfor i, operation := range args.Operations {\n\t\t\t\tif operation.Name == \"loop\" {\n\t\t\t\t\treturn fmt.Errorf(\"loop sub-operations should not include loop\")\n\t\t\t\t}\n\t\t\t\tloopErr = operation.execute(ctx, loopDone)\n\n\t\t\t\t// if the operation errors, stop this loop\n\t\t\t\tif loopErr != nil {\n\t\t\t\t\t// If StoreFailures or StoreErrors is set, continue looping, otherwise break\n\t\t\t\t\tif !args.errorsStored() && !args.failuresStored() {\n\t\t\t\t\t\treturn fmt.Errorf(\"error running loop operation %v : %v\", i, loopErr)\n\t\t\t\t\t}\n\t\t\t\t\terrDoc := bson.Raw(bsoncore.NewDocumentBuilder().\n\t\t\t\t\t\tAppendString(\"error\", loopErr.Error()).\n\t\t\t\t\t\tAppendDouble(\"time\", getSecondsSinceEpoch()).\n\t\t\t\t\t\tBuild())\n\t\t\t\t\tvar appendErr error\n\t\t\t\t\tswitch {\n\t\t\t\t\tcase !args.errorsStored(): // store errors as failures if storeErrorsAsEntity isn't specified\n\t\t\t\t\t\tappendErr = entityMap.appendBSONArrayEntity(args.FailuresEntityID, errDoc)\n\t\t\t\t\tcase !args.failuresStored(): // store failures as errors if storeFailuressAsEntity isn't specified\n\t\t\t\t\t\tappendErr = entityMap.appendBSONArrayEntity(args.ErrorsEntityID, errDoc)\n\t\t\t\t\t// errors are test runner errors\n\t\t\t\t\t// TODO GODRIVER-1950: use error types to determine error vs failure instead of depending on the\n\t\t\t\t\t// TODO fact that operation.execute prepends \"execution failed\" to test runner errors\n\t\t\t\t\tcase strings.Contains(loopErr.Error(), \"execution failed: \"):\n\t\t\t\t\t\tappendErr = entityMap.appendBSONArrayEntity(args.ErrorsEntityID, errDoc)\n\t\t\t\t\t// failures are if an operation returns an incorrect result or error\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tappendErr = entityMap.appendBSONArrayEntity(args.FailuresEntityID, errDoc)\n\t\t\t\t\t}\n\t\t\t\t\tif appendErr != nil {\n\t\t\t\t\t\treturn appendErr\n\t\t\t\t\t}\n\t\t\t\t\t// if a sub-operation errors, restart the loop\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tif args.successesStored() {\n\t\t\t\t\tif err := entityMap.incrementSuccesses(args.SuccessesEntityID); err != nil {\n\t\t\t\t\t\treturn err\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\ntype waitForEventArguments struct {\n\tClientID string              `bson:\"client\"`\n\tEvent    map[string]bson.Raw `bson:\"event\"`\n\tCount    int32               `bson:\"count\"`\n}\n\n// getServerDescriptionChangedEventCount will return \"true\" if a specific\n// server description change event has occurred, up to the description type.\n//\n// If the bson.Raw value is empty, then this function will only consider if a\n// serverDescriptionChangeEvent has occurred at all.\n//\n// If the bson.Raw contains newDescription and/or previousDescription, this\n// function will attempt to compare them to events up to the fields defined in\n// the UST specifications.\nfunc getServerDescriptionChangedEventCount(client *clientEntity, raw bson.Raw) int32 {\n\tif len(raw) == 0 {\n\t\treturn 0\n\t}\n\n\t// If the document has no values, then we assume that the UST only\n\t// intends to check that the event happened.\n\tif values, _ := raw.Values(); len(values) == 0 {\n\t\treturn client.getEventCount(serverDescriptionChangedEvent)\n\t}\n\n\tvar expectedEvt serverDescriptionChangedEventInfo\n\tif err := bson.Unmarshal(raw, &expectedEvt); err != nil {\n\t\treturn 0\n\t}\n\n\treturn client.getServerDescriptionChangedEventCount(expectedEvt)\n}\n\n// eventCompleted will check all of the events in the event map and return true if all of the events have at least the\n// specified number of occurrences. If the event map is empty, it will return true.\nfunc (args waitForEventArguments) eventCompleted(client *clientEntity) bool {\n\tfor rawEventType, eventDoc := range args.Event {\n\t\teventType, ok := monitoringEventTypeFromString(rawEventType)\n\t\tif !ok {\n\t\t\treturn false\n\t\t}\n\n\t\tswitch eventType {\n\t\tcase serverDescriptionChangedEvent:\n\t\t\tif getServerDescriptionChangedEventCount(client, eventDoc) < args.Count {\n\t\t\t\treturn false\n\t\t\t}\n\t\tdefault:\n\t\t\tif client.getEventCount(eventType) < args.Count {\n\t\t\t\treturn false\n\t\t\t}\n\t\t}\n\t}\n\n\treturn true\n}\n\nfunc waitForEvent(ctx context.Context, args waitForEventArguments) error {\n\tclient, err := entities(ctx).client(args.ClientID)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor {\n\t\tselect {\n\t\tcase <-ctx.Done():\n\t\t\treturn fmt.Errorf(\"timed out waiting for event: %v\", ctx.Err())\n\t\tdefault:\n\t\t\tif args.eventCompleted(client) {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\n\t\ttime.Sleep(100 * time.Millisecond)\n\t}\n}\n\nfunc verifySessionPinnedState(ctx context.Context, sessionID string, expectedPinned bool) error {\n\tsess, err := entities(ctx).session(sessionID)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif isPinned := testutil.GetUnexportedFieldAs[*session.Client](sess, \"clientSession\").PinnedServerAddr != nil; expectedPinned != isPinned {\n\t\treturn fmt.Errorf(\"session pinned state mismatch; expected to be pinned: %v, is pinned: %v\", expectedPinned, isPinned)\n\t}\n\treturn nil\n}\n\nfunc verifyLastTwoLsidsEqual(ctx context.Context, clientID string, expectedEqual bool) error {\n\tclient, err := entities(ctx).client(clientID)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tallEvents := client.startedEvents()\n\tif len(allEvents) < 2 {\n\t\treturn fmt.Errorf(\"client has recorded fewer than two command started events\")\n\t}\n\tlastTwoEvents := allEvents[len(allEvents)-2:]\n\n\tfirstID, err := lastTwoEvents[0].Command.LookupErr(\"lsid\")\n\tif err != nil {\n\t\treturn fmt.Errorf(\"first command has no 'lsid' field: %v\", client.started[0].Command)\n\t}\n\tsecondID, err := lastTwoEvents[1].Command.LookupErr(\"lsid\")\n\tif err != nil {\n\t\treturn fmt.Errorf(\"first command has no 'lsid' field: %v\", client.started[1].Command)\n\t}\n\n\tareEqual := firstID.Equal(secondID)\n\tif expectedEqual && !areEqual {\n\t\treturn fmt.Errorf(\"expected last two lsids to be equal, but got %s and %s\", firstID, secondID)\n\t}\n\tif !expectedEqual && areEqual {\n\t\treturn fmt.Errorf(\"expected last two lsids to be different but both were %s\", firstID)\n\t}\n\treturn nil\n}\n\nfunc verifySessionDirtyState(ctx context.Context, sessionID string, expectedDirty bool) error {\n\tsess, err := entities(ctx).session(sessionID)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif isDirty := testutil.GetUnexportedFieldAs[*session.Client](sess, \"clientSession\").Dirty; expectedDirty != isDirty {\n\t\treturn fmt.Errorf(\"session dirty state mismatch; expected to be dirty: %v, is dirty: %v\", expectedDirty, isDirty)\n\t}\n\treturn nil\n}\n\nfunc verifyCollectionExists(ctx context.Context, dbName, collName string, expectedExists bool) error {\n\tdb := mtest.GlobalClient().Database(dbName)\n\tcollections, err := db.ListCollectionNames(ctx, bson.M{\"name\": collName})\n\tif err != nil {\n\t\treturn fmt.Errorf(\"error running ListCollectionNames: %v\", err)\n\t}\n\n\tif exists := len(collections) == 1; expectedExists != exists {\n\t\tns := fmt.Sprintf(\"%s.%s\", dbName, collName)\n\t\treturn fmt.Errorf(\"collection existence mismatch; expected namespace %q to exist: %v, exists: %v\", ns,\n\t\t\texpectedExists, exists)\n\t}\n\treturn nil\n}\n\nfunc verifyIndexExists(ctx context.Context, dbName, collName, indexName string, expectedExists bool) error {\n\tiv := mtest.GlobalClient().Database(dbName).Collection(collName).Indexes()\n\tcursor, err := iv.List(ctx)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"error running IndexView.List: %v\", err)\n\t}\n\tdefer cursor.Close(ctx)\n\n\tvar exists bool\n\tfor cursor.Next(ctx) {\n\t\tif lookupString(cursor.Current, \"name\") == indexName {\n\t\t\texists = true\n\t\t\tbreak\n\t\t}\n\t}\n\tif expectedExists != exists {\n\t\tns := fmt.Sprintf(\"%s.%s\", dbName, collName)\n\t\treturn fmt.Errorf(\"index existence mismatch: expected index %q to exist in namespace %q: %v, exists: %v\",\n\t\t\tindexName, ns, expectedExists, exists)\n\t}\n\treturn nil\n}\n\nfunc convertValueToMilliseconds(val bson.RawValue) (time.Duration, error) {\n\tint32Val, ok := val.Int32OK()\n\tif !ok {\n\t\treturn 0, fmt.Errorf(\"failed to convert value of type %s to int32\", val.Type)\n\t}\n\n\treturn time.Duration(int32Val) * time.Millisecond, nil\n}\n"
  },
  {
    "path": "internal/integration/unified/unified_spec_runner.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage unified\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"io/ioutil\"\n\t\"os\"\n\t\"path\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/integration/mtest\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/spectest\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo\"\n)\n\nvar (\n\tlogMessageValidatorTimeout = 10 * time.Millisecond\n\tlowHeartbeatFrequency      = 500 * time.Millisecond\n)\n\n// TestCase holds and runs a unified spec test case\ntype TestCase struct {\n\tDescription       string               `bson:\"description\"`\n\tRunOnRequirements []mtest.RunOnBlock   `bson:\"runOnRequirements\"`\n\tSkipReason        *string              `bson:\"skipReason\"`\n\tOperations        []*operation         `bson:\"operations\"`\n\tExpectedEvents    []*expectedEvents    `bson:\"expectEvents\"`\n\tExpectLogMessages []*clientLogMessages `bson:\"expectLogMessages\"`\n\tOutcome           []*collectionData    `bson:\"outcome\"`\n\n\tinitialData     []*collectionData\n\tcreateEntities  []map[string]*entityOptions\n\tkillAllSessions bool\n\tschemaVersion   string\n\n\tentities *EntityMap\n\tloopDone chan struct{}\n}\n\nfunc (tc *TestCase) performsDistinct() bool {\n\treturn tc.performsOperation(\"distinct\")\n}\n\nfunc (tc *TestCase) setsFailPoint() bool {\n\treturn tc.performsOperation(\"failPoint\")\n}\n\nfunc (tc *TestCase) startsTransaction() bool {\n\treturn tc.performsOperation(\"startTransaction\")\n}\n\nfunc (tc *TestCase) performsOperation(name string) bool {\n\tfor _, op := range tc.Operations {\n\t\tif op.Name == name {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\n// TestFile holds the contents of a unified spec test file\ntype TestFile struct {\n\tDescription       string                      `bson:\"description\"`\n\tSchemaVersion     string                      `bson:\"schemaVersion\"`\n\tRunOnRequirements []mtest.RunOnBlock          `bson:\"runOnRequirements\"`\n\tCreateEntities    []map[string]*entityOptions `bson:\"createEntities\"`\n\tInitialData       []*collectionData           `bson:\"initialData\"`\n\tTestCases         []*TestCase                 `bson:\"tests\"`\n}\n\n// runTestDirectory runs the files in the given directory, which must be in the unified spec format, with\n// expectValidFail determining whether the tests should expect to pass or fail\nfunc runTestDirectory(t *testing.T, directoryPath string, expectValidFail bool) {\n\tfor _, filename := range spectest.FindJSONFilesInDir(t, directoryPath) {\n\t\tt.Run(filename, func(t *testing.T) {\n\t\t\trunTestFile(t, path.Join(directoryPath, filename), expectValidFail)\n\t\t})\n\t}\n}\n\n// runTestFile runs the tests in the given file, with expectValidFail determining whether the tests should expect to pass or fail\nfunc runTestFile(t *testing.T, filepath string, expectValidFail bool, opts ...*Options) {\n\tspectest.CheckSkip(t)\n\tcontent, err := ioutil.ReadFile(filepath)\n\tassert.Nil(t, err, \"ReadFile error for file %q: %v\", filepath, err)\n\n\tfileReqs, testCases := ParseTestFile(t, content, expectValidFail, opts...)\n\n\tmtOpts := mtest.NewOptions().\n\t\tRunOn(fileReqs...).\n\t\tCreateClient(false)\n\tmt := mtest.New(t, mtOpts)\n\n\tfor _, testCase := range testCases {\n\t\tmtOpts := mtest.NewOptions().\n\t\t\tRunOn(testCase.RunOnRequirements...).\n\t\t\tCreateClient(false)\n\n\t\tmt.RunOpts(testCase.Description, mtOpts, func(mt *mtest.T) {\n\t\t\tspectest.CheckSkip(mt.T)\n\n\t\t\t// Skip CSOT spec tests when SKIP_CSOT_TESTS=true. In Evergreen, we\n\t\t\t// typically set that environment variable on Windows and macOS\n\t\t\t// because the CSOT spec tests are unreliable on those hosts.\n\t\t\tif os.Getenv(\"SKIP_CSOT_TESTS\") == \"true\" && strings.Contains(filepath, \"client-side-operations-timeout\") {\n\t\t\t\tmt.Skip(\"Skipping CSOT spec test because SKIP_CSOT_TESTS=true\")\n\t\t\t}\n\n\t\t\tdefer func() {\n\t\t\t\t// catch panics from looking up elements and fail if it's unexpected\n\t\t\t\tif r := recover(); r != nil {\n\t\t\t\t\tif !expectValidFail {\n\t\t\t\t\t\tmt.Fatal(r)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}()\n\n\t\t\terr := testCase.Run(mt)\n\t\t\tif expectValidFail {\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tmt.Fatalf(\"expected test to error, got nil\")\n\t\t\t}\n\t\t\tif err != nil {\n\t\t\t\tmt.Fatal(err)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc parseTestFile(testJSON []byte, opts ...*Options) ([]mtest.RunOnBlock, []*TestCase, error) {\n\tvar testFile TestFile\n\tif err := bson.UnmarshalExtJSON(testJSON, false, &testFile); err != nil {\n\t\treturn nil, nil, err\n\t}\n\n\top := NewOptions()\n\tfor _, opt := range opts {\n\t\tif opt == nil {\n\t\t\tcontinue\n\t\t}\n\t\tif opt.RunKillAllSessions != nil {\n\t\t\top.RunKillAllSessions = opt.RunKillAllSessions\n\t\t}\n\t}\n\n\tfor _, testCase := range testFile.TestCases {\n\t\ttestCase.initialData = testFile.InitialData\n\t\ttestCase.createEntities = testFile.CreateEntities\n\t\ttestCase.schemaVersion = testFile.SchemaVersion\n\t\ttestCase.entities = newEntityMap()\n\t\ttestCase.loopDone = make(chan struct{})\n\t\ttestCase.killAllSessions = *op.RunKillAllSessions\n\t}\n\n\treturn testFile.RunOnRequirements, testFile.TestCases, nil\n}\n\n// ParseTestFile create an array of TestCases from the testJSON json blob\nfunc ParseTestFile(t *testing.T, testJSON []byte, expectValidFail bool, opts ...*Options) ([]mtest.RunOnBlock, []*TestCase) {\n\tt.Helper()\n\n\trunOnRequirements, testCases, err := parseTestFile(testJSON, opts...)\n\n\tif !expectValidFail {\n\t\tassert.NoError(t, err, \"error parsing test file\")\n\t}\n\n\treturn runOnRequirements, testCases\n}\n\n// GetEntities returns a pointer to the EntityMap for the TestCase. This should not be called until after\n// the test is run\nfunc (tc *TestCase) GetEntities() *EntityMap {\n\treturn tc.entities\n}\n\n// EndLoop will cause the runner to stop a loop operation if one is included in the test. If the test has finished\n// running, this will panic\nfunc (tc *TestCase) EndLoop() {\n\ttc.loopDone <- struct{}{}\n}\n\n// LoggerSkipper is passed to TestCase.Run to allow it to perform logging and skipping operations\ntype LoggerSkipper interface {\n\tLog(args ...any)\n\tLogf(format string, args ...any)\n\tSkip(args ...any)\n\tSkipf(format string, args ...any)\n}\n\n// skipTestError indicates that a test must be skipped because the runner cannot execute it (e.g. the test requires\n// an operation or option that the driver does not support).\ntype skipTestError struct {\n\treason string\n}\n\n// Error implements the error interface.\nfunc (s skipTestError) Error() string {\n\treturn fmt.Sprintf(\"test must be skipped: %q\", s.reason)\n}\n\nfunc newSkipTestError(reason string) error {\n\treturn &skipTestError{reason}\n}\n\nfunc isSkipTestError(err error) bool {\n\treturn err != nil && strings.Contains(err.Error(), \"test must be skipped\")\n}\n\n// Run runs the TestCase and returns an error if it fails\nfunc (tc *TestCase) Run(ls LoggerSkipper) error {\n\tif tc.SkipReason != nil {\n\t\tls.Skipf(\"skipping for reason: %q\", *tc.SkipReason)\n\t}\n\n\t// Validate that we support the schema declared by the test file before attempting to use its contents.\n\tif err := checkSchemaVersion(tc.schemaVersion); err != nil {\n\t\treturn fmt.Errorf(\"schema version %q not supported: %v\", tc.schemaVersion, err)\n\t}\n\n\ttestCtx := newTestContext(context.Background(), tc.entities, tc.ExpectLogMessages, tc.setsFailPoint())\n\n\tdefer func() {\n\t\t// If anything fails while doing test cleanup, we only log the error because the actual test may have already\n\t\t// failed and that failure should be preserved.\n\n\t\tfor _, err := range disableUntargetedFailPoints(testCtx) {\n\t\t\tls.Log(err)\n\t\t}\n\t\tfor _, err := range disableTargetedFailPoints(testCtx) {\n\t\t\tls.Log(err)\n\t\t}\n\t\tfor _, err := range entities(testCtx).close(testCtx) {\n\t\t\tls.Log(err)\n\t\t}\n\t\t// Tests that started a transaction should terminate any sessions left open on the server. This is required even\n\t\t// if the test attempted to commit/abort the transaction because an abortTransaction command can fail if it's\n\t\t// sent to a mongos that isn't aware of the transaction.\n\t\tif tc.startsTransaction() && tc.killAllSessions {\n\t\t\tif err := terminateOpenSessions(context.Background()); err != nil {\n\t\t\t\tls.Logf(\"error terminating open transactions after failed test: %v\", err)\n\t\t\t}\n\t\t}\n\n\t\tclose(tc.loopDone)\n\t}()\n\n\t// Set up collections based on the file-level initialData field.\n\tfor _, collData := range tc.initialData {\n\t\tif err := collData.createCollection(testCtx); err != nil {\n\t\t\treturn fmt.Errorf(\"error setting up collection %q: %v\", collData.namespace(), err)\n\t\t}\n\t}\n\n\t// Set up entities based on the file-level createEntities field. For client entities, if the test will configure\n\t// a fail point, set a low heartbeatFrequencyMS value into the URI options map if one is not already present.\n\t// This speeds up recovery time for the client if the fail point forces the server to return a state change\n\t// error.\n\tfor idx, entity := range tc.createEntities {\n\t\tfor entityType, entityOptions := range entity {\n\t\t\tif entityType == \"client\" && hasOperationalFailpoint(testCtx) {\n\t\t\t\tentityOptions.setHeartbeatFrequencyMS(lowHeartbeatFrequency)\n\t\t\t}\n\n\t\t\tif err := tc.entities.addEntity(testCtx, entityType, entityOptions); err != nil {\n\t\t\t\tif isSkipTestError(err) {\n\t\t\t\t\tls.Skip(err)\n\t\t\t\t}\n\n\t\t\t\treturn fmt.Errorf(\"error creating entity at index %d: %v\", idx, err)\n\t\t\t}\n\t\t}\n\t}\n\n\t// Work around SERVER-39704.\n\tif mtest.ClusterTopologyKind() == mtest.Sharded && tc.performsDistinct() {\n\t\tif err := performDistinctWorkaround(testCtx); err != nil {\n\t\t\treturn fmt.Errorf(\"error performing \\\"distinct\\\" workaround: %v\", err)\n\t\t}\n\t}\n\n\tfor idx, operation := range tc.Operations {\n\t\tif err := operation.execute(testCtx, tc.loopDone); err != nil {\n\t\t\tif isSkipTestError(err) {\n\t\t\t\tls.Skip(err)\n\t\t\t}\n\n\t\t\treturn fmt.Errorf(\"error running operation %q at index %d: %v\", operation.Name, idx, err)\n\t\t}\n\t}\n\n\t// Create a validator for log messages and start the workers that will\n\t// observe log messages as they occur operationally.\n\tlogMessageValidator := newLogMessageValidator(tc)\n\tgo startLogValidators(testCtx, logMessageValidator)\n\n\tfor _, client := range tc.entities.clients() {\n\t\tclient.stopListeningForEvents()\n\t}\n\n\t// One of the bulkWrite spec tests expects update and updateMany to be grouped together into a single batch,\n\t// but this isn't the case because of GODRIVER-1157. To work around this, we skip event verification for this test.\n\t// This guard should be removed when GODRIVER-1157 is done.\n\tif tc.Description != \"BulkWrite on server that doesn't support arrayFilters with arrayFilters on second op\" {\n\t\tfor idx, expectedEvents := range tc.ExpectedEvents {\n\t\t\tif err := verifyEvents(testCtx, expectedEvents); err != nil {\n\t\t\t\treturn fmt.Errorf(\"events verification failed at index %d: %v\", idx, err)\n\t\t\t}\n\t\t}\n\t}\n\n\tfor idx, collData := range tc.Outcome {\n\t\tif err := collData.verifyContents(testCtx); err != nil {\n\t\t\treturn fmt.Errorf(\"error verifying outcome for collection %q at index %d: %v\",\n\t\t\t\tcollData.namespace(), idx, err)\n\t\t}\n\t}\n\n\t{\n\t\t// Create a context with a deadline to use for log message\n\t\t// validation. This will prevent any blocking from test cases\n\t\t// with N messages where only N - K (0 < K < N) messages are\n\t\t// observed.\n\t\tctx, cancel := context.WithTimeout(testCtx, logMessageValidatorTimeout)\n\t\tdefer cancel()\n\n\t\t// For each client, verify that all expected log messages were\n\t\t// received.\n\t\tif err := stopLogValidators(ctx, logMessageValidator); err != nil {\n\t\t\treturn fmt.Errorf(\"error verifying log messages: %w\", err)\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc disableUntargetedFailPoints(ctx context.Context) []error {\n\tvar errs []error\n\tfor fpName, client := range failPoints(ctx) {\n\t\tif err := disableFailPointWithClient(ctx, fpName, client); err != nil {\n\t\t\terrs = append(errs, fmt.Errorf(\"error disabling fail point %q: %v\", fpName, err))\n\t\t}\n\t}\n\treturn errs\n}\n\nfunc disableTargetedFailPoints(ctx context.Context) []error {\n\tvar errs []error\n\tfor fpName, host := range targetedFailPoints(ctx) {\n\t\tcommandFn := func(ctx context.Context, client *mongo.Client) error {\n\t\t\treturn disableFailPointWithClient(ctx, fpName, client)\n\t\t}\n\t\tif err := runCommandOnHost(ctx, host, commandFn); err != nil {\n\t\t\terrs = append(errs, fmt.Errorf(\"error disabling targeted fail point %q on host %q: %v\", fpName, host, err))\n\t\t}\n\t}\n\treturn errs\n}\n\nfunc disableFailPointWithClient(ctx context.Context, fpName string, client *mongo.Client) error {\n\tcmd := bson.D{\n\t\t{\"configureFailPoint\", fpName},\n\t\t{\"mode\", \"off\"},\n\t}\n\treturn client.Database(\"admin\").RunCommand(ctx, cmd).Err()\n}\n"
  },
  {
    "path": "internal/integration/unified/unified_spec_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage unified\n\nimport (\n\t\"context\"\n\t\"os\"\n\t\"testing\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/spectest\"\n)\n\nvar (\n\tpassDirectories = []string{\n\t\t\"unified-test-format/tests/valid-pass\",\n\t\t\"transactions-convenient-api/tests/unified\",\n\t\t\"versioned-api/tests\",\n\t\t\"crud/tests/unified\",\n\t\t\"change-streams/tests/unified\",\n\t\t\"load-balancers/tests\",\n\t\t\"collection-management/tests\",\n\t\t\"command-logging-and-monitoring/tests/monitoring\",\n\t\t\"command-logging-and-monitoring/tests/logging\",\n\t\t\"connection-monitoring-and-pooling/tests/logging\",\n\t\t\"sessions/tests\",\n\t\t\"retryable-reads/tests/unified\",\n\t\t\"retryable-writes/tests/unified\",\n\t\t\"client-side-encryption/tests/unified\",\n\t\t\"client-side-operations-timeout/tests\",\n\t\t\"gridfs/tests\",\n\t\t\"server-selection/tests/logging\",\n\t\t\"server-discovery-and-monitoring/tests/unified\",\n\t\t\"run-command/tests/unified\",\n\t\t\"index-management/tests\",\n\t\t\"mongodb-handshake/tests/unified\",\n\t}\n\tfailDirectories = []string{\n\t\t\"unified-test-format/tests/valid-fail\",\n\t}\n)\n\nfunc TestUnifiedSpec(t *testing.T) {\n\t// Ensure the cluster is in a clean state before test execution begins.\n\t// Don't run for Data Lake tests because it doesn't support\n\t// \"killAllSessions\".\n\tif os.Getenv(\"ATLAS_DATA_LAKE_INTEGRATION_TEST\") != \"true\" {\n\t\tif err := terminateOpenSessions(context.Background()); err != nil {\n\t\t\tt.Fatalf(\"error terminating open transactions: %v\", err)\n\t\t}\n\t}\n\n\tfor _, testDir := range passDirectories {\n\t\tt.Run(testDir, func(t *testing.T) {\n\t\t\trunTestDirectory(t, spectest.Path(testDir), false)\n\t\t})\n\t}\n\n\tfor _, testDir := range failDirectories {\n\t\tt.Run(testDir, func(t *testing.T) {\n\t\t\trunTestDirectory(t, spectest.Path(testDir), true)\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "internal/integration/unified_runner_events_helper_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage integration\n\nimport (\n\t\"context\"\n\t\"sync\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/event\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/integration/mtest\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/serverselector\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/address\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/readpref\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/description\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/topology\"\n)\n\n// Helper functions for the operations in the unified spec test runner that require assertions about SDAM and connection\n// pool events.\n\nvar (\n\tpoolEventTypesMap = map[string]string{\n\t\t\"PoolClearedEvent\": event.ConnectionPoolCleared,\n\t}\n\tdefaultCallbackTimeout = 10 * time.Second\n)\n\n// unifiedRunnerEventMonitor monitors connection pool-related events.\ntype unifiedRunnerEventMonitor struct {\n\tpoolEventCount               map[string]int\n\tpoolEventCountLock           sync.Mutex\n\tsdamMonitor                  *event.ServerMonitor\n\tserverMarkedUnknownCount     int\n\tserverMarkedUnknownCountLock sync.Mutex\n}\n\nfunc newUnifiedRunnerEventMonitor() *unifiedRunnerEventMonitor {\n\turem := unifiedRunnerEventMonitor{\n\t\tpoolEventCount: make(map[string]int),\n\t}\n\turem.sdamMonitor = &event.ServerMonitor{\n\t\tServerDescriptionChanged: (func(e *event.ServerDescriptionChangedEvent) {\n\t\t\turem.serverMarkedUnknownCountLock.Lock()\n\t\t\tdefer urem.serverMarkedUnknownCountLock.Unlock()\n\n\t\t\t// Spec tests only ever handle ServerMarkedUnknown ServerDescriptionChangedEvents\n\t\t\t// for the time being.\n\t\t\tif e.NewDescription.Kind == description.UnknownStr {\n\t\t\t\turem.serverMarkedUnknownCount++\n\t\t\t}\n\t\t}),\n\t}\n\treturn &urem\n}\n\n// handlePoolEvent can be used as the event handler for a connection pool monitor.\nfunc (u *unifiedRunnerEventMonitor) handlePoolEvent(evt *event.PoolEvent) {\n\tu.poolEventCountLock.Lock()\n\tdefer u.poolEventCountLock.Unlock()\n\n\tu.poolEventCount[evt.Type]++\n}\n\n// getPoolEventCount returns the number of pool events of the given type, or 0 if no events were recorded.\nfunc (u *unifiedRunnerEventMonitor) getPoolEventCount(eventType string) int {\n\tu.poolEventCountLock.Lock()\n\tdefer u.poolEventCountLock.Unlock()\n\n\tmappedType := poolEventTypesMap[eventType]\n\treturn u.poolEventCount[mappedType]\n}\n\n// getServerMarkedUnknownCount returns the number of ServerMarkedUnknownEvents, or 0 if none were recorded.\nfunc (u *unifiedRunnerEventMonitor) getServerMarkedUnknownCount() int {\n\tu.serverMarkedUnknownCountLock.Lock()\n\tdefer u.serverMarkedUnknownCountLock.Unlock()\n\n\treturn u.serverMarkedUnknownCount\n}\n\nfunc waitForEvent(mt *mtest.T, test *testCase, op *operation) {\n\teventType := op.Arguments.Lookup(\"event\").StringValue()\n\texpectedCount := int(op.Arguments.Lookup(\"count\").Int32())\n\n\tcallback := func() bool {\n\t\tvar count int\n\t\t// Spec tests only ever wait for ServerMarkedUnknown SDAM events for the time being.\n\t\tif eventType == \"ServerMarkedUnknownEvent\" {\n\t\t\tcount = test.monitor.getServerMarkedUnknownCount()\n\t\t} else {\n\t\t\tcount = test.monitor.getPoolEventCount(eventType)\n\t\t}\n\n\t\treturn count >= expectedCount\n\t}\n\n\tassert.Eventually(mt,\n\t\tcallback,\n\t\tdefaultCallbackTimeout,\n\t\t100*time.Millisecond,\n\t\t\"expected spec tests to only wait for Server Marked Unknown SDAM events\")\n}\n\nfunc assertEventCount(mt *mtest.T, testCase *testCase, op *operation) {\n\teventType := op.Arguments.Lookup(\"event\").StringValue()\n\texpectedCount := int(op.Arguments.Lookup(\"count\").Int32())\n\n\tvar gotCount int\n\t// Spec tests only ever assert ServerMarkedUnknown SDAM events for the time being.\n\tif eventType == \"ServerMarkedUnknownEvent\" {\n\t\tgotCount = testCase.monitor.getServerMarkedUnknownCount()\n\t} else {\n\t\tgotCount = testCase.monitor.getPoolEventCount(eventType)\n\t}\n\tassert.Equal(mt, expectedCount, gotCount, \"expected count %d for event %s, got %d\", expectedCount, eventType,\n\t\tgotCount)\n}\n\nfunc recordPrimary(mt *mtest.T, testCase *testCase) {\n\ttestCase.recordedPrimary = getPrimaryAddress(mt, testCase.testTopology, true)\n}\n\nfunc waitForPrimaryChange(mt *mtest.T, testCase *testCase, op *operation) {\n\tcallback := func() bool {\n\t\treturn getPrimaryAddress(mt, testCase.testTopology, false) != testCase.recordedPrimary\n\t}\n\n\ttimeout := convertValueToMilliseconds(mt, op.Arguments.Lookup(\"timeoutMS\"))\n\tassert.Eventually(mt,\n\t\tcallback,\n\t\ttimeout,\n\t\t100*time.Millisecond,\n\t\t\"expected primary address to be different within the timeout period\")\n}\n\n// getPrimaryAddress returns the address of the current primary. If failFast is true, the server selection fast path\n// is used and the function will fail if the fast path doesn't return a server.\nfunc getPrimaryAddress(mt *mtest.T, topo *topology.Topology, failFast bool) address.Address {\n\tmt.Helper()\n\n\tctx, cancel := context.WithCancel(context.Background())\n\tdefer cancel()\n\tif failFast {\n\t\tcancel()\n\t}\n\n\tprimary, err := topo.SelectServer(ctx, &serverselector.ReadPref{ReadPref: readpref.Primary()})\n\tassert.Nil(mt, err, \"SelectServer error: %v\", err)\n\treturn primary.(*topology.SelectedServer).Description().Addr\n}\n"
  },
  {
    "path": "internal/integration/unified_runner_thread_helpers_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage integration\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"sync\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/integration/mtest\"\n)\n\n// Helper functions for the operations in the unified spec test runner that require creating and synchronizing\n// background goroutines.\n\n// backgroundRoutine represents a background goroutine that can execute operations. The goroutine reads operations from\n// a channel and executes them in order. It exits when an operation with name exitRoutineOperationName is read. If any\n// of the operations error, all future operations passed to the routine are skipped. The first error is reported by the\n// stop() function.\ntype backgroundRoutine struct {\n\toperations chan *operation\n\tmt         *mtest.T\n\ttestCase   *testCase\n\twg         sync.WaitGroup\n\terr        error\n}\n\nfunc newBackgroundRoutine(mt *mtest.T, testCase *testCase) *backgroundRoutine {\n\troutine := &backgroundRoutine{\n\t\toperations: make(chan *operation, 10),\n\t\tmt:         mt,\n\t\ttestCase:   testCase,\n\t}\n\n\treturn routine\n}\n\nfunc (b *backgroundRoutine) start() {\n\tb.wg.Add(1)\n\n\tgo func() {\n\t\tdefer b.wg.Done()\n\n\t\tfor op := range b.operations {\n\t\t\tif b.err != nil {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tif err := runOperation(b.mt, b.testCase, op, nil, nil); err != nil {\n\t\t\t\tb.err = fmt.Errorf(\"error running operation %s: %w\", op.Name, err)\n\t\t\t}\n\t\t}\n\t}()\n}\n\nfunc (b *backgroundRoutine) stop() error {\n\tclose(b.operations)\n\tb.wg.Wait()\n\treturn b.err\n}\n\nfunc (b *backgroundRoutine) addOperation(op *operation) bool {\n\tselect {\n\tcase b.operations <- op:\n\t\treturn true\n\tdefault:\n\t\treturn false\n\t}\n}\n\nfunc startThread(mt *mtest.T, testCase *testCase, op *operation) {\n\troutine := newBackgroundRoutine(mt, testCase)\n\ttestCase.routinesMap.Store(getThreadName(op), routine)\n\troutine.start()\n}\n\nfunc runOnThread(mt *mtest.T, testCase *testCase, op *operation) {\n\troutineName := getThreadName(op)\n\troutineVal, ok := testCase.routinesMap.Load(routineName)\n\tassert.True(mt, ok, \"no background routine found with name %s\", routineName)\n\troutine := routineVal.(*backgroundRoutine)\n\n\tvar routineOperation operation\n\toperationDoc := op.Arguments.Lookup(\"operation\")\n\tdec := bson.NewDecoder(bson.NewDocumentReader(bytes.NewReader(operationDoc.Document())))\n\tdec.SetRegistry(specTestRegistry)\n\terr := dec.Decode(&routineOperation)\n\tassert.Nil(mt, err, \"error creating operation for runOnThread: %v\", err)\n\n\tok = routine.addOperation(&routineOperation)\n\tassert.True(mt, ok, \"failed to add operation %s to routine %s\", routineOperation.Name, routineName)\n}\n\nfunc waitForThread(mt *mtest.T, testCase *testCase, op *operation) {\n\tname := getThreadName(op)\n\troutineVal, ok := testCase.routinesMap.Load(name)\n\tassert.True(mt, ok, \"no background routine found with name %s\", name)\n\n\terr := routineVal.(*backgroundRoutine).stop()\n\ttestCase.routinesMap.Delete(name)\n\tassert.Nil(mt, err, \"error on background routine %s: %v\", name, err)\n}\n\nfunc getThreadName(op *operation) string {\n\treturn op.Arguments.Lookup(\"name\").StringValue()\n}\n"
  },
  {
    "path": "internal/integration/unified_spec_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage integration\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io/ioutil\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"reflect\"\n\t\"sync\"\n\t\"testing\"\n\t\"time\"\n\t\"unsafe\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/event\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/bsonutil\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/failpoint\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/integration/mtest\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/integtest\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/spectest\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/testutil\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/address\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/readconcern\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/readpref\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/session\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/topology\"\n)\n\nconst (\n\tgridFSFiles  = \"fs.files\"\n\tgridFSChunks = \"fs.chunks\"\n)\n\nvar defaultHeartbeatInterval = 500 * time.Millisecond\n\ntype testFile struct {\n\tRunOn           []mtest.RunOnBlock `bson:\"runOn\"`\n\tDatabaseName    string             `bson:\"database_name\"`\n\tCollectionName  string             `bson:\"collection_name\"`\n\tBucketName      string             `bson:\"bucket_name\"`\n\tData            testData           `bson:\"data\"`\n\tJSONSchema      bson.Raw           `bson:\"json_schema\"`\n\tKeyVaultData    []bson.Raw         `bson:\"key_vault_data\"`\n\tTests           []*testCase        `bson:\"tests\"`\n\tEncryptedFields bson.Raw           `bson:\"encrypted_fields\"`\n}\n\ntype testData struct {\n\tDocuments  []bson.Raw\n\tGridFSData struct {\n\t\tFiles  []bson.Raw `bson:\"fs.files\"`\n\t\tChunks []bson.Raw `bson:\"fs.chunks\"`\n\t}\n}\n\n// custom decoder for testData type\nfunc decodeTestData(dc bson.DecodeContext, vr bson.ValueReader, val reflect.Value) error {\n\tswitch vr.Type() {\n\tcase bson.TypeArray:\n\t\tdocsVal := val.FieldByName(\"Documents\")\n\t\tdecoder, err := dc.LookupDecoder(docsVal.Type())\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\treturn decoder.DecodeValue(dc, vr, docsVal)\n\tcase bson.TypeEmbeddedDocument:\n\t\tgridfsDataVal := val.FieldByName(\"GridFSData\")\n\t\tdecoder, err := dc.LookupDecoder(gridfsDataVal.Type())\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\treturn decoder.DecodeValue(dc, vr, gridfsDataVal)\n\t}\n\treturn nil\n}\n\ntype testCase struct {\n\tDescription         string          `bson:\"description\"`\n\tSkipReason          string          `bson:\"skipReason\"`\n\tFailPoint           *bson.Raw       `bson:\"failPoint\"`\n\tClientOptions       bson.Raw        `bson:\"clientOptions\"`\n\tSessionOptions      bson.Raw        `bson:\"sessionOptions\"`\n\tOperations          []*operation    `bson:\"operations\"`\n\tExpectations        *[]*expectation `bson:\"expectations\"`\n\tUseMultipleMongoses bool            `bson:\"useMultipleMongoses\"`\n\tOutcome             *outcome        `bson:\"outcome\"`\n\n\t// set in code if the test is a GridFS test\n\tchunkSize int32\n\tbucket    *mongo.GridFSBucket\n\n\t// set in code to track test context\n\ttestTopology    *topology.Topology\n\trecordedPrimary address.Address\n\tmonitor         *unifiedRunnerEventMonitor\n\troutinesMap     sync.Map // maps thread name to *backgroundRoutine\n}\n\ntype operation struct {\n\tName              string   `bson:\"name\"`\n\tObject            string   `bson:\"object\"`\n\tCollectionOptions bson.Raw `bson:\"collectionOptions\"`\n\tDatabaseOptions   bson.Raw `bson:\"databaseOptions\"`\n\tResult            any      `bson:\"result\"`\n\tArguments         bson.Raw `bson:\"arguments\"`\n\tError             bool     `bson:\"error\"`\n\tCommandName       string   `bson:\"command_name\"`\n\n\t// set in code after determining whether or not result represents an error\n\topError *operationError\n}\n\ntype expectation struct {\n\tCommandStartedEvent *struct {\n\t\tCommandName  string         `bson:\"command_name\"`\n\t\tDatabaseName string         `bson:\"database_name\"`\n\t\tCommand      bson.Raw       `bson:\"command\"`\n\t\tExtra        map[string]any `bson:\",inline\"`\n\t} `bson:\"command_started_event\"`\n\tCommandSucceededEvent *struct {\n\t\tCommandName string         `bson:\"command_name\"`\n\t\tReply       bson.Raw       `bson:\"reply\"`\n\t\tExtra       map[string]any `bson:\",inline\"`\n\t} `bson:\"command_succeeded_event\"`\n\tCommandFailedEvent *struct {\n\t\tCommandName string         `bson:\"command_name\"`\n\t\tExtra       map[string]any `bson:\",inline\"`\n\t} `bson:\"command_failed_event\"`\n}\n\ntype outcome struct {\n\tCollection *outcomeCollection `bson:\"collection\"`\n}\n\ntype outcomeCollection struct {\n\tName string `bson:\"name\"`\n\tData any    `bson:\"data\"`\n}\n\ntype operationError struct {\n\tErrorContains      *string  `bson:\"errorContains\"`\n\tErrorCodeName      *string  `bson:\"errorCodeName\"`\n\tErrorLabelsContain []string `bson:\"errorLabelsContain\"`\n\tErrorLabelsOmit    []string `bson:\"errorLabelsOmit\"`\n}\n\nvar directories = []string{\n\t\"read-write-concern/tests/operation\",\n}\n\nvar (\n\tcheckOutcomeOpts = options.Collection().SetReadPreference(readpref.Primary()).SetReadConcern(readconcern.Local())\n\tspecTestRegistry = func() *bson.Registry {\n\t\treg := bson.NewRegistry()\n\t\treg.RegisterTypeMapEntry(bson.TypeEmbeddedDocument, reflect.TypeOf(bson.Raw{}))\n\t\treg.RegisterTypeDecoder(reflect.TypeOf(testData{}), bson.ValueDecoderFunc(decodeTestData))\n\t\treturn reg\n\t}()\n)\n\nfunc TestUnifiedSpecs(t *testing.T) {\n\tfor _, specDir := range directories {\n\t\tt.Run(specDir, func(t *testing.T) {\n\t\t\tfor _, fileName := range jsonFilesInDir(t, spectest.Path(specDir)) {\n\t\t\t\tt.Run(fileName, func(t *testing.T) {\n\t\t\t\t\trunSpecTestFile(t, filepath.Join(specDir, fileName))\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\t}\n}\n\n// specDir: name of directory for a spec in the data/ folder\n// fileName: name of test file in specDir\nfunc runSpecTestFile(t *testing.T, filePath string) {\n\t// It's possible that a JSON blob does not conform to our spec test format.\n\tspectest.CheckSkip(t)\n\n\tcontent, err := ioutil.ReadFile(filePath)\n\tassert.Nil(t, err, \"unable to read spec test file %v: %v\", filePath, err)\n\n\tvar testFile testFile\n\tvr, err := bson.NewExtJSONValueReader(bytes.NewReader(content), false)\n\tassert.Nil(t, err, \"NewExtJSONValueReader error: %v\", err)\n\tdec := bson.NewDecoder(vr)\n\tdec.SetRegistry(specTestRegistry)\n\terr = dec.Decode(&testFile)\n\tassert.Nil(t, err, \"unable to unmarshal spec test file at %v: %v\", filePath, err)\n\n\t// create mtest wrapper and skip if needed\n\tmtOpts := mtest.NewOptions().\n\t\tRunOn(testFile.RunOn...).\n\t\tCreateClient(false)\n\tmt := mtest.New(t, mtOpts)\n\n\tfor _, test := range testFile.Tests {\n\t\trunSpecTestCase(mt, test, testFile)\n\t}\n}\n\nfunc runSpecTestCase(mt *mtest.T, test *testCase, testFile testFile) {\n\topts := mtest.NewOptions().DatabaseName(testFile.DatabaseName).CollectionName(testFile.CollectionName)\n\tif mtest.ClusterTopologyKind() == mtest.Sharded && !test.UseMultipleMongoses {\n\t\t// pin to a single mongos\n\t\topts = opts.ClientType(mtest.Pinned)\n\t}\n\n\tcco := options.CreateCollection()\n\tif len(testFile.JSONSchema) > 0 {\n\t\tvalidator := bson.D{\n\t\t\t{\"$jsonSchema\", testFile.JSONSchema},\n\t\t}\n\t\tcco.SetValidator(validator)\n\t}\n\n\tif len(testFile.EncryptedFields) > 0 {\n\t\tcco.SetEncryptedFields(testFile.EncryptedFields)\n\t}\n\n\topts.CollectionCreateOptions(cco)\n\n\t// Start the test without setting client options so the setup will be done with a default client.\n\tmt.RunOpts(test.Description, opts, func(mt *mtest.T) {\n\t\tspectest.CheckSkip(mt.T)\n\n\t\tif len(test.SkipReason) > 0 {\n\t\t\tmt.Skip(test.SkipReason)\n\t\t}\n\n\t\t// work around for SERVER-39704: run a non-transactional distinct against each shard in a sharded cluster\n\t\tif mtest.ClusterTopologyKind() == mtest.Sharded && test.Description == \"distinct\" {\n\t\t\terr := runCommandOnAllServers(func(mongosClient *mongo.Client) error {\n\t\t\t\tcoll := mongosClient.Database(mt.DB.Name()).Collection(mt.Coll.Name())\n\n\t\t\t\treturn coll.Distinct(context.Background(), \"x\", bson.D{}).Err()\n\t\t\t})\n\t\t\tassert.Nil(mt, err, \"error running distinct against all mongoses: %v\", err)\n\t\t}\n\n\t\t// Defer killSessions to ensure it runs regardless of the state of the test because the client has already\n\t\t// been created and the collection drop in mongotest will hang for transactions to be aborted (60 seconds)\n\t\t// in error cases.\n\t\tdefer killSessions(mt)\n\n\t\t// Test setup: create collections that are tracked by mtest, insert test data, and set the failpoint.\n\t\tsetupTest(mt, &testFile, test)\n\t\tif test.FailPoint != nil {\n\t\t\tmt.SetFailPointFromDocument(*test.FailPoint)\n\t\t}\n\n\t\t// Reset the client using the client options specified in the test.\n\t\ttestClientOpts := createClientOptions(mt, test.ClientOptions)\n\n\t\t// If AutoEncryptionOptions is set and AutoEncryption isn't disabled (neither\n\t\t// bypassAutoEncryption nor bypassQueryAnalysis are true), then add extra options to load\n\t\t// the crypt_shared library.\n\t\tif testClientOpts.AutoEncryptionOptions != nil {\n\t\t\taeOpts := testClientOpts.AutoEncryptionOptions\n\n\t\t\tbypassAutoEncryption := aeOpts.BypassAutoEncryption != nil && *aeOpts.BypassAutoEncryption\n\t\t\tbypassQueryAnalysis := aeOpts.BypassQueryAnalysis != nil && *aeOpts.BypassQueryAnalysis\n\n\t\t\tif !bypassAutoEncryption && !bypassQueryAnalysis {\n\t\t\t\tif aeOpts.ExtraOptions == nil {\n\t\t\t\t\taeOpts.ExtraOptions = make(map[string]any)\n\t\t\t\t}\n\n\t\t\t\tfor k, v := range getCryptSharedLibExtraOptions() {\n\t\t\t\t\taeOpts.ExtraOptions[k] = v\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\ttest.monitor = newUnifiedRunnerEventMonitor()\n\t\ttestClientOpts.SetPoolMonitor(&event.PoolMonitor{\n\t\t\tEvent: test.monitor.handlePoolEvent,\n\t\t})\n\t\ttestClientOpts.SetServerMonitor(test.monitor.sdamMonitor)\n\t\tif testClientOpts.HeartbeatInterval == nil {\n\t\t\t// If one isn't specified in the test, use a low heartbeat frequency so the Client will quickly recover when\n\t\t\t// using failpoints that cause SDAM state changes.\n\t\t\ttestClientOpts.SetHeartbeatInterval(defaultHeartbeatInterval)\n\t\t}\n\n\t\tmt.ResetClient(testClientOpts)\n\n\t\t// Record the underlying topology for the test's Client.\n\t\ttest.testTopology = getTopologyFromClient(mt.Client)\n\n\t\t// Create the GridFS bucket and sessions after resetting the client so it will be created with a connected\n\t\t// client.\n\t\tcreateBucket(mt, testFile, test)\n\t\tsess0, sess1 := setupSessions(mt, test)\n\t\tif sess0 != nil {\n\t\t\tdefer func() {\n\t\t\t\tsess0.EndSession(context.Background())\n\t\t\t\tsess1.EndSession(context.Background())\n\t\t\t}()\n\t\t}\n\n\t\t// run operations\n\t\tmt.ClearEvents()\n\t\tfor idx, op := range test.Operations {\n\t\t\terr := runOperation(mt, test, op, sess0, sess1)\n\t\t\tassert.Nil(mt, err, \"error running operation %q at index %d: %v\", op.Name, idx, err)\n\t\t}\n\n\t\t// Needs to be done here (in spite of defer) because some tests\n\t\t// require end session to be called before we check expectation\n\t\tsess0.EndSession(context.Background())\n\t\tsess1.EndSession(context.Background())\n\t\tmt.ClearFailPoints()\n\n\t\tcheckExpectations(mt, test.Expectations, sess0.ID(), sess1.ID())\n\n\t\tif test.Outcome != nil {\n\t\t\tverifyTestOutcome(mt, test.Outcome.Collection)\n\t\t}\n\t})\n}\n\nfunc createBucket(mt *mtest.T, testFile testFile, testCase *testCase) {\n\tif testFile.BucketName == \"\" {\n\t\treturn\n\t}\n\n\tbucketOpts := options.GridFSBucket()\n\tif testFile.BucketName != \"\" {\n\t\tbucketOpts.SetName(testFile.BucketName)\n\t}\n\tchunkSize := testCase.chunkSize\n\tif chunkSize == 0 {\n\t\tchunkSize = mongo.DefaultGridFSChunkSize\n\t}\n\tbucketOpts.SetChunkSizeBytes(chunkSize)\n\n\ttestCase.bucket = mt.DB.GridFSBucket(bucketOpts)\n}\n\nfunc runOperation(mt *mtest.T, testCase *testCase, op *operation, sess0, sess1 *mongo.Session) error {\n\tif op.Name == \"count\" {\n\t\tmt.Skip(\"count has been deprecated\")\n\t}\n\n\tvar sess *mongo.Session\n\tif sessVal, err := op.Arguments.LookupErr(\"session\"); err == nil {\n\t\tsessStr := sessVal.StringValue()\n\t\tswitch sessStr {\n\t\tcase \"session0\":\n\t\t\tsess = sess0\n\t\tcase \"session1\":\n\t\t\tsess = sess1\n\t\tdefault:\n\t\t\treturn fmt.Errorf(\"unrecognized session identifier: %v\", sessStr)\n\t\t}\n\t}\n\n\tif op.Object == \"testRunner\" {\n\t\treturn executeTestRunnerOperation(mt, testCase, op, sess)\n\t}\n\n\tif op.DatabaseOptions != nil {\n\t\tmt.CloneDatabase(createDatabaseOptions(mt, op.DatabaseOptions))\n\t}\n\tif op.CollectionOptions != nil {\n\t\tmt.CloneCollection(createCollectionOptions(mt, op.CollectionOptions))\n\t}\n\n\t// execute the command on the given object\n\tvar err error\n\tswitch op.Object {\n\tcase \"session0\":\n\t\terr = executeSessionOperation(mt, op, sess0)\n\tcase \"session1\":\n\t\terr = executeSessionOperation(mt, op, sess1)\n\tcase \"\", \"collection\":\n\t\t// object defaults to \"collection\" if not specified\n\t\terr = executeCollectionOperation(mt, op, sess)\n\tcase \"database\":\n\t\terr = executeDatabaseOperation(mt, op, sess)\n\tcase \"gridfsbucket\":\n\t\terr = executeGridFSOperation(mt, testCase.bucket, op)\n\tcase \"client\":\n\t\terr = executeClientOperation(mt, op, sess)\n\tdefault:\n\t\treturn fmt.Errorf(\"unrecognized operation object: %v\", op.Object)\n\t}\n\n\top.opError = errorFromResult(mt, op.Result)\n\t// Some tests (e.g. crud/v2) only specify that an error should occur via the op.Error field but do not specify\n\t// which error via the op.Result field. In this case, pass in an empty non-nil operationError so verifyError will\n\t// make the right assertions.\n\tif op.Error && op.Result == nil {\n\t\top.opError = &operationError{}\n\t}\n\treturn verifyError(op.opError, err)\n}\n\nfunc executeGridFSOperation(mt *mtest.T, bucket *mongo.GridFSBucket, op *operation) error {\n\t// no results for GridFS operations\n\tassert.Nil(mt, op.Result, \"unexpected result for GridFS operation\")\n\n\tswitch op.Name {\n\tcase \"download\":\n\t\t_, err := executeGridFSDownload(mt, bucket, op.Arguments)\n\t\treturn err\n\tcase \"download_by_name\":\n\t\t_, err := executeGridFSDownloadByName(mt, bucket, op.Arguments)\n\t\treturn err\n\tdefault:\n\t\tmt.Fatalf(\"unrecognized gridfs operation: %v\", op.Name)\n\t}\n\treturn nil\n}\n\nfunc executeTestRunnerOperation(mt *mtest.T, testCase *testCase, op *operation, sess *mongo.Session) error {\n\tvar (\n\t\tclientSession *session.Client\n\t\thasSession    bool\n\t)\n\n\tif sess != nil {\n\t\tclientSession = testutil.GetUnexportedFieldAs[*session.Client](sess, \"clientSession\")\n\t\thasSession = true\n\t}\n\n\trequireSession := func(opName string) error {\n\t\tif !hasSession {\n\t\t\treturn fmt.Errorf(\"%s requires a session\", opName)\n\t\t}\n\n\t\treturn nil\n\t}\n\n\tswitch op.Name {\n\tcase \"targetedFailPoint\":\n\t\tif err := requireSession(op.Name); err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tif clientSession.PinnedServerAddr == nil {\n\t\t\treturn fmt.Errorf(\"%s requires pinned session\", op.Name)\n\t\t}\n\n\t\tfpDoc := op.Arguments.Lookup(\"failPoint\")\n\n\t\tvar fp failpoint.FailPoint\n\t\tif err := bson.Unmarshal(fpDoc.Document(), &fp); err != nil {\n\t\t\treturn fmt.Errorf(\"Unmarshal error: %w\", err)\n\t\t}\n\n\t\ttargetHost := clientSession.PinnedServerAddr.String()\n\t\topts := options.Client().ApplyURI(mtest.ClusterURI()).SetHosts([]string{targetHost})\n\t\tintegtest.AddTestServerAPIVersion(opts)\n\t\tclient, err := mongo.Connect(opts)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"Connect error for targeted client: %w\", err)\n\t\t}\n\t\tdefer func() { _ = client.Disconnect(context.Background()) }()\n\n\t\tif err = client.Database(\"admin\").RunCommand(context.Background(), fp).Err(); err != nil {\n\t\t\treturn fmt.Errorf(\"error setting targeted fail point: %w\", err)\n\t\t}\n\t\tmt.TrackFailPoint(fp.ConfigureFailPoint)\n\tcase \"configureFailPoint\":\n\t\tfp, err := op.Arguments.LookupErr(\"failPoint\")\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"unable to find 'failPoint' in arguments: %w\", err)\n\t\t}\n\t\tmt.SetFailPointFromDocument(fp.Document())\n\tcase \"assertSessionTransactionState\":\n\t\tif err := requireSession(op.Name); err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tstateVal, err := op.Arguments.LookupErr(\"state\")\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"unable to find 'state' in arguments: %w\", err)\n\t\t}\n\t\texpectedState, ok := stateVal.StringValueOK()\n\t\tif !ok {\n\t\t\treturn errors.New(\"expected 'state' argument to be string\")\n\t\t}\n\n\t\tactualState := clientSession.TransactionState.String()\n\n\t\t// actualState should match expectedState, but \"in progress\" is the same as\n\t\t// \"in_progress\".\n\t\tstateMatch := actualState == expectedState ||\n\t\t\tactualState == \"in progress\" && expectedState == \"in_progress\"\n\t\tif !stateMatch {\n\t\t\treturn fmt.Errorf(\"expected transaction state %v, got %v\", expectedState, actualState)\n\t\t}\n\tcase \"assertSessionPinned\":\n\t\tif err := requireSession(op.Name); err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tif clientSession.PinnedServerAddr == nil {\n\t\t\treturn errors.New(\"expected pinned server, got nil\")\n\t\t}\n\tcase \"assertSessionUnpinned\":\n\t\tif err := requireSession(op.Name); err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\t// We don't use a combined helper for assertSessionPinned and assertSessionUnpinned because the unpinned\n\t\t// case provides the pinned server address in the error msg for debugging.\n\t\tif clientSession.PinnedServerAddr != nil {\n\t\t\treturn fmt.Errorf(\"expected pinned server to be nil but got %q\", clientSession.PinnedServerAddr.String())\n\t\t}\n\tcase \"assertSameLsidOnLastTwoCommands\":\n\t\tfirst, second := lastTwoIDs(mt)\n\t\tif !first.Equal(second) {\n\t\t\treturn fmt.Errorf(\"expected last two lsids to be equal but got %v and %v\", first, second)\n\t\t}\n\tcase \"assertDifferentLsidOnLastTwoCommands\":\n\t\tfirst, second := lastTwoIDs(mt)\n\t\tif first.Equal(second) {\n\t\t\treturn fmt.Errorf(\"expected last two lsids to be not equal but both were %v\", first)\n\t\t}\n\tcase \"assertCollectionExists\":\n\t\treturn verifyCollectionState(op, true)\n\tcase \"assertCollectionNotExists\":\n\t\treturn verifyCollectionState(op, false)\n\tcase \"assertIndexExists\":\n\t\treturn verifyIndexState(op, true)\n\tcase \"assertIndexNotExists\":\n\t\treturn verifyIndexState(op, false)\n\tcase \"wait\":\n\t\ttime.Sleep(convertValueToMilliseconds(mt, op.Arguments.Lookup(\"ms\")))\n\tcase \"waitForEvent\":\n\t\twaitForEvent(mt, testCase, op)\n\tcase \"assertEventCount\":\n\t\tassertEventCount(mt, testCase, op)\n\tcase \"recordPrimary\":\n\t\trecordPrimary(mt, testCase)\n\tcase \"runAdminCommand\":\n\t\texecuteAdminCommand(mt, op)\n\tcase \"waitForPrimaryChange\":\n\t\twaitForPrimaryChange(mt, testCase, op)\n\tcase \"startThread\":\n\t\tstartThread(mt, testCase, op)\n\tcase \"runOnThread\":\n\t\trunOnThread(mt, testCase, op)\n\tcase \"waitForThread\":\n\t\twaitForThread(mt, testCase, op)\n\tdefault:\n\t\treturn fmt.Errorf(\"unrecognized testRunner operation %v\", op.Name)\n\t}\n\n\treturn nil\n}\n\nfunc verifyIndexState(op *operation, shouldExist bool) error {\n\tdb := op.Arguments.Lookup(\"database\").StringValue()\n\tcoll := op.Arguments.Lookup(\"collection\").StringValue()\n\tindex := op.Arguments.Lookup(\"index\").StringValue()\n\n\texists, err := indexExists(db, coll, index)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif exists != shouldExist {\n\t\treturn fmt.Errorf(\"index state mismatch for index %s in namespace %s.%s; should exist: %v, exists: %v\",\n\t\t\tindex, db, coll, shouldExist, exists)\n\t}\n\treturn nil\n}\n\nfunc indexExists(dbName, collName, indexName string) (bool, error) {\n\t// Use global client because listIndexes cannot be executed inside a transaction.\n\tiv := mtest.GlobalClient().Database(dbName).Collection(collName).Indexes()\n\tcursor, err := iv.List(context.Background())\n\tif err != nil {\n\t\treturn false, fmt.Errorf(\"IndexView.List error: %w\", err)\n\t}\n\tdefer cursor.Close(context.Background())\n\n\tfor cursor.Next(context.Background()) {\n\t\tif cursor.Current.Lookup(\"name\").StringValue() == indexName {\n\t\t\treturn true, nil\n\t\t}\n\t}\n\treturn false, cursor.Err()\n}\n\nfunc verifyCollectionState(op *operation, shouldExist bool) error {\n\tdb := op.Arguments.Lookup(\"database\").StringValue()\n\tcoll := op.Arguments.Lookup(\"collection\").StringValue()\n\n\texists, err := collectionExists(db, coll)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif exists != shouldExist {\n\t\treturn fmt.Errorf(\"collection state mismatch for %s.%s; should exist %v, exists: %v\", db, coll, shouldExist,\n\t\t\texists)\n\t}\n\treturn nil\n}\n\nfunc collectionExists(dbName, collName string) (bool, error) {\n\tfilter := bson.D{\n\t\t{\"name\", collName},\n\t}\n\n\t// Use global client because listCollections cannot be executed inside a transaction.\n\tcollections, err := mtest.GlobalClient().Database(dbName).ListCollectionNames(context.Background(), filter)\n\tif err != nil {\n\t\treturn false, fmt.Errorf(\"ListCollectionNames error: %w\", err)\n\t}\n\n\treturn len(collections) > 0, nil\n}\n\nfunc lastTwoIDs(mt *mtest.T) (bson.RawValue, bson.RawValue) {\n\tevents := mt.GetAllStartedEvents()\n\tlastTwoEvents := events[len(events)-2:]\n\n\tfirst := lastTwoEvents[0].Command.Lookup(\"lsid\")\n\tsecond := lastTwoEvents[1].Command.Lookup(\"lsid\")\n\treturn first, second\n}\n\nfunc executeSessionOperation(mt *mtest.T, op *operation, sess *mongo.Session) error {\n\tswitch op.Name {\n\tcase \"startTransaction\":\n\t\tvar txnOpts *options.TransactionOptionsBuilder\n\t\tif opts, err := op.Arguments.LookupErr(\"options\"); err == nil {\n\t\t\ttxnOpts = createTransactionOptions(mt, opts.Document())\n\t\t}\n\t\treturn sess.StartTransaction(txnOpts)\n\tcase \"commitTransaction\":\n\t\treturn sess.CommitTransaction(context.Background())\n\tcase \"abortTransaction\":\n\t\treturn sess.AbortTransaction(context.Background())\n\tcase \"withTransaction\":\n\t\treturn executeWithTransaction(mt, sess, op.Arguments)\n\tdefault:\n\t\treturn fmt.Errorf(\"unrecognized session operation: %v\", op.Name)\n\t}\n}\n\nfunc executeCollectionOperation(mt *mtest.T, op *operation, sess *mongo.Session) error {\n\tswitch op.Name {\n\tcase \"countDocuments\":\n\t\t// no results to verify with count\n\t\tres, err := executeCountDocuments(mt, sess, op.Arguments)\n\t\tif op.opError == nil && err == nil {\n\t\t\tverifyCountResult(mt, res, op.Result)\n\t\t}\n\t\treturn err\n\tcase \"distinct\":\n\t\tres, err := executeDistinct(mt, sess, op.Arguments)\n\t\tif op.opError == nil && err == nil {\n\t\t\tverifyDistinctResult(mt, res, op.Result)\n\t\t}\n\t\treturn err\n\tcase \"insertOne\":\n\t\tres, err := executeInsertOne(mt, sess, op.Arguments)\n\t\tif op.opError == nil && err == nil {\n\t\t\tverifyInsertOneResult(mt, res, op.Result)\n\t\t}\n\t\treturn err\n\tcase \"insertMany\":\n\t\tres, err := executeInsertMany(mt, sess, op.Arguments)\n\t\tif op.opError == nil && err == nil {\n\t\t\tverifyInsertManyResult(mt, res, op.Result)\n\t\t}\n\t\treturn err\n\tcase \"find\":\n\t\tcursor, err := executeFind(mt, sess, op.Arguments)\n\t\tif op.opError == nil && err == nil {\n\t\t\tverifyCursorResult(mt, cursor, op.Result)\n\t\t\t_ = cursor.Close(context.Background())\n\t\t}\n\t\treturn err\n\tcase \"findOneAndDelete\":\n\t\tres := executeFindOneAndDelete(mt, sess, op.Arguments)\n\t\tif op.opError == nil && res.Err() == nil {\n\t\t\tverifySingleResult(mt, res, op.Result)\n\t\t}\n\t\treturn res.Err()\n\tcase \"findOneAndUpdate\":\n\t\tres := executeFindOneAndUpdate(mt, sess, op.Arguments)\n\t\tif op.opError == nil && res.Err() == nil {\n\t\t\tverifySingleResult(mt, res, op.Result)\n\t\t}\n\t\treturn res.Err()\n\tcase \"findOneAndReplace\":\n\t\tres := executeFindOneAndReplace(mt, sess, op.Arguments)\n\t\tif op.opError == nil && res.Err() == nil {\n\t\t\tverifySingleResult(mt, res, op.Result)\n\t\t}\n\t\treturn res.Err()\n\tcase \"deleteOne\":\n\t\tres, err := executeDeleteOne(mt, sess, op.Arguments)\n\t\tif op.opError == nil && err == nil {\n\t\t\tverifyDeleteResult(mt, res, op.Result)\n\t\t}\n\t\treturn err\n\tcase \"deleteMany\":\n\t\tres, err := executeDeleteMany(mt, sess, op.Arguments)\n\t\tif op.opError == nil && err == nil {\n\t\t\tverifyDeleteResult(mt, res, op.Result)\n\t\t}\n\t\treturn err\n\tcase \"updateOne\":\n\t\tres, err := executeUpdateOne(mt, sess, op.Arguments)\n\t\tif op.opError == nil && err == nil {\n\t\t\tverifyUpdateResult(mt, res, op.Result)\n\t\t}\n\t\treturn err\n\tcase \"updateMany\":\n\t\tres, err := executeUpdateMany(mt, sess, op.Arguments)\n\t\tif op.opError == nil && err == nil {\n\t\t\tverifyUpdateResult(mt, res, op.Result)\n\t\t}\n\t\treturn err\n\tcase \"replaceOne\":\n\t\tres, err := executeReplaceOne(mt, sess, op.Arguments)\n\t\tif op.opError == nil && err == nil {\n\t\t\tverifyUpdateResult(mt, res, op.Result)\n\t\t}\n\t\treturn err\n\tcase \"aggregate\":\n\t\tcursor, err := executeAggregate(mt, mt.Coll, sess, op.Arguments)\n\t\tif op.opError == nil && err == nil {\n\t\t\tverifyCursorResult(mt, cursor, op.Result)\n\t\t\t_ = cursor.Close(context.Background())\n\t\t}\n\t\treturn err\n\tcase \"bulkWrite\":\n\t\tres, err := executeBulkWrite(mt, sess, op.Arguments)\n\t\tif op.opError == nil && err == nil {\n\t\t\tverifyBulkWriteResult(mt, res, op.Result)\n\t\t}\n\t\treturn err\n\tcase \"estimatedDocumentCount\":\n\t\tres, err := executeEstimatedDocumentCount(mt, sess, op.Arguments)\n\t\tif op.opError == nil && err == nil {\n\t\t\tverifyCountResult(mt, res, op.Result)\n\t\t}\n\t\treturn err\n\tcase \"findOne\":\n\t\tres := executeFindOne(mt, sess, op.Arguments)\n\t\tif op.opError == nil && res.Err() == nil {\n\t\t\tverifySingleResult(mt, res, op.Result)\n\t\t}\n\t\treturn res.Err()\n\tcase \"listIndexes\":\n\t\tcursor, err := executeListIndexes(mt, sess, op.Arguments)\n\t\tif op.opError == nil && err == nil {\n\t\t\tverifyCursorResult(mt, cursor, op.Result)\n\t\t\t_ = cursor.Close(context.Background())\n\t\t}\n\t\treturn err\n\tcase \"watch\":\n\t\tstream, err := executeWatch(mt, mt.Coll, sess, op.Arguments)\n\t\tif op.opError == nil && err == nil {\n\t\t\tassert.Nil(mt, op.Result, \"unexpected result for watch: %v\", op.Result)\n\t\t\t_ = stream.Close(context.Background())\n\t\t}\n\t\treturn err\n\tcase \"createIndex\":\n\t\tindexName, err := executeCreateIndex(mt, sess, op.Arguments)\n\t\tif op.opError == nil && err == nil {\n\t\t\tassert.Nil(mt, op.Result, \"unexpected result for createIndex: %v\", op.Result)\n\t\t\tassert.True(mt, len(indexName) > 0, \"expected valid index name, got empty string\")\n\t\t\tassert.True(mt, len(indexName) > 0, \"created index has empty name\")\n\t\t}\n\t\treturn err\n\tcase \"dropIndex\":\n\t\terr := executeDropIndex(mt, sess, op.Arguments)\n\t\tif op.opError == nil && err == nil {\n\t\t\tassert.Nil(mt, op.Result, \"unexpected result for dropIndex: %v\", op.Result)\n\t\t}\n\t\treturn err\n\tcase \"listIndexNames\", \"mapReduce\":\n\t\tmt.Skipf(\"operation %v not implemented\", op.Name)\n\tdefault:\n\t\tmt.Fatalf(\"unrecognized collection operation: %v\", op.Name)\n\t}\n\treturn nil\n}\n\nfunc executeDatabaseOperation(mt *mtest.T, op *operation, sess *mongo.Session) error {\n\tswitch op.Name {\n\tcase \"runCommand\":\n\t\tres := executeRunCommand(mt, sess, op.Arguments)\n\t\tif op.opError == nil && res.Err() == nil {\n\t\t\tverifySingleResult(mt, res, op.Result)\n\t\t}\n\t\treturn res.Err()\n\tcase \"aggregate\":\n\t\tcursor, err := executeAggregate(mt, mt.DB, sess, op.Arguments)\n\t\tif op.opError == nil && err == nil {\n\t\t\tverifyCursorResult(mt, cursor, op.Result)\n\t\t\t_ = cursor.Close(context.Background())\n\t\t}\n\t\treturn err\n\tcase \"listCollections\":\n\t\tcursor, err := executeListCollections(mt, sess, op.Arguments)\n\t\tif op.opError == nil && err == nil {\n\t\t\tassert.Nil(mt, op.Result, \"unexpected result for listCollections: %v\", op.Result)\n\t\t\t_ = cursor.Close(context.Background())\n\t\t}\n\t\treturn err\n\tcase \"listCollectionNames\":\n\t\t_, err := executeListCollectionNames(mt, sess, op.Arguments)\n\t\tif op.opError == nil && err == nil {\n\t\t\tassert.Nil(mt, op.Result, \"unexpected result for listCollectionNames: %v\", op.Result)\n\t\t}\n\t\treturn err\n\tcase \"watch\":\n\t\tstream, err := executeWatch(mt, mt.DB, sess, op.Arguments)\n\t\tif op.opError == nil && err == nil {\n\t\t\tassert.Nil(mt, op.Result, \"unexpected result for watch: %v\", op.Result)\n\t\t\t_ = stream.Close(context.Background())\n\t\t}\n\t\treturn err\n\tcase \"dropCollection\":\n\t\terr := executeDropCollection(mt, sess, op.Arguments)\n\t\tif op.opError == nil && err == nil {\n\t\t\tassert.Nil(mt, op.Result, \"unexpected result for dropCollection: %v\", op.Result)\n\t\t}\n\t\treturn err\n\tcase \"createCollection\":\n\t\terr := executeCreateCollection(mt, sess, op.Arguments)\n\t\tif op.opError == nil && err == nil {\n\t\t\tassert.Nil(mt, op.Result, \"unexpected result for createCollection: %v\", op.Result)\n\t\t}\n\t\treturn err\n\tcase \"listCollectionObjects\":\n\t\tmt.Skipf(\"operation %v not implemented\", op.Name)\n\tdefault:\n\t\tmt.Fatalf(\"unrecognized database operation: %v\", op.Name)\n\t}\n\treturn nil\n}\n\nfunc executeClientOperation(mt *mtest.T, op *operation, sess *mongo.Session) error {\n\tswitch op.Name {\n\tcase \"listDatabaseNames\":\n\t\t_, err := executeListDatabaseNames(mt, sess, op.Arguments)\n\t\tif op.opError == nil && err == nil {\n\t\t\tassert.Nil(mt, op.Result, \"unexpected result for countDocuments: %v\", op.Result)\n\t\t}\n\t\treturn err\n\tcase \"listDatabases\":\n\t\tres, err := executeListDatabases(mt, sess, op.Arguments)\n\t\tif op.opError == nil && err == nil {\n\t\t\tverifyListDatabasesResult(mt, res, op.Result)\n\t\t}\n\t\treturn err\n\tcase \"watch\":\n\t\tstream, err := executeWatch(mt, mt.Client, sess, op.Arguments)\n\t\tif op.opError == nil && err == nil {\n\t\t\tassert.Nil(mt, op.Result, \"unexpected result for watch: %v\", op.Result)\n\t\t\t_ = stream.Close(context.Background())\n\t\t}\n\t\treturn err\n\tcase \"listDatabaseObjects\":\n\t\tmt.Skipf(\"operation %v not implemented\", op.Name)\n\tdefault:\n\t\tmt.Fatalf(\"unrecognized client operation: %v\", op.Name)\n\t}\n\treturn nil\n}\n\nfunc setupSessions(mt *mtest.T, test *testCase) (*mongo.Session, *mongo.Session) {\n\tmt.Helper()\n\n\tvar sess0Opts, sess1Opts *options.SessionOptionsBuilder\n\tif opts, err := test.SessionOptions.LookupErr(\"session0\"); err == nil {\n\t\tsess0Opts = createSessionOptions(mt, opts.Document())\n\t}\n\tif opts, err := test.SessionOptions.LookupErr(\"session1\"); err == nil {\n\t\tsess1Opts = createSessionOptions(mt, opts.Document())\n\t}\n\n\tsess0, err := mt.Client.StartSession(sess0Opts)\n\tassert.Nil(mt, err, \"error creating session0: %v\", err)\n\tsess1, err := mt.Client.StartSession(sess1Opts)\n\tassert.Nil(mt, err, \"error creating session1: %v\", err)\n\n\treturn sess0, sess1\n}\n\nfunc insertDocuments(mt *mtest.T, coll *mongo.Collection, rawDocs []bson.Raw) {\n\tmt.Helper()\n\n\tdocsToInsert := bsonutil.RawToInterfaces(rawDocs...)\n\tif len(docsToInsert) == 0 {\n\t\treturn\n\t}\n\n\t_, err := coll.InsertMany(context.Background(), docsToInsert)\n\tassert.Nil(mt, err, \"InsertMany error for collection %v: %v\", coll.Name(), err)\n}\n\n// load initial data into appropriate collections and set chunkSize for the test case if necessary\nfunc setupTest(mt *mtest.T, testFile *testFile, testCase *testCase) {\n\tmt.Helper()\n\n\t// key vault data\n\tif len(testFile.KeyVaultData) > 0 {\n\t\t// Drop the key vault collection in case it exists from a prior test run.\n\t\terr := mt.Client.Database(\"keyvault\").Collection(\"datakeys\").Drop(context.Background())\n\t\tassert.Nil(mt, err, \"error dropping key vault collection\")\n\n\t\tkeyVaultColl := mt.CreateCollection(mtest.Collection{\n\t\t\tName: \"datakeys\",\n\t\t\tDB:   \"keyvault\",\n\t\t}, false)\n\n\t\tinsertDocuments(mt, keyVaultColl, testFile.KeyVaultData)\n\t}\n\n\t// regular documents\n\tif testFile.Data.Documents != nil {\n\t\tinsertDocuments(mt, mt.Coll, testFile.Data.Documents)\n\t\treturn\n\t}\n\n\t// GridFS data\n\tgfsData := testFile.Data.GridFSData\n\n\tif gfsData.Chunks != nil {\n\t\tchunks := mt.CreateCollection(mtest.Collection{\n\t\t\tName: gridFSChunks,\n\t\t}, false)\n\t\tinsertDocuments(mt, chunks, gfsData.Chunks)\n\t}\n\tif gfsData.Files != nil {\n\t\tfiles := mt.CreateCollection(mtest.Collection{\n\t\t\tName: gridFSFiles,\n\t\t}, false)\n\t\tinsertDocuments(mt, files, gfsData.Files)\n\n\t\tcsVal, err := gfsData.Files[0].LookupErr(\"chunkSize\")\n\t\tif err == nil {\n\t\t\ttestCase.chunkSize = csVal.Int32()\n\t\t}\n\t}\n}\n\nfunc verifyTestOutcome(mt *mtest.T, outcomeColl *outcomeCollection) {\n\t// Outcome needs to be verified using the global client instead of the test client because certain client\n\t// configurations will cause outcome checking to fail. For example, a client configured with auto encryption\n\t// will decrypt results, causing comparisons to fail.\n\n\tcollName := mt.Coll.Name()\n\tif outcomeColl.Name != \"\" {\n\t\tcollName = outcomeColl.Name\n\t}\n\tcoll := mtest.GlobalClient().Database(mt.DB.Name()).Collection(collName, checkOutcomeOpts)\n\n\tfindOpts := options.Find().\n\t\tSetSort(bson.M{\"_id\": 1})\n\tcursor, err := coll.Find(context.Background(), bson.D{}, findOpts)\n\tassert.Nil(mt, err, \"Find error: %v\", err)\n\tverifyCursorResult(mt, cursor, outcomeColl.Data)\n}\n\nfunc getTopologyFromClient(client *mongo.Client) *topology.Topology {\n\tclientElem := reflect.ValueOf(client).Elem()\n\tdeploymentField := clientElem.FieldByName(\"deployment\")\n\tdeploymentField = reflect.NewAt(deploymentField.Type(), unsafe.Pointer(deploymentField.UnsafeAddr())).Elem()\n\treturn deploymentField.Interface().(*topology.Topology)\n}\n\n// getCryptSharedLibExtraOptions returns an AutoEncryption extra options map with crypt_shared\n// library path information if the CRYPT_SHARED_LIB_PATH environment variable is set.\nfunc getCryptSharedLibExtraOptions() map[string]any {\n\tpath := os.Getenv(\"CRYPT_SHARED_LIB_PATH\")\n\tif path == \"\" {\n\t\treturn nil\n\t}\n\treturn map[string]any{\n\t\t\"cryptSharedLibRequired\": true,\n\t\t\"cryptSharedLibPath\":     path,\n\t}\n}\n"
  },
  {
    "path": "internal/integtest/integtest.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage integtest\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"math\"\n\t\"os\"\n\t\"reflect\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\t\"testing\"\n\n\t\"go.mongodb.org/mongo-driver/v2/event\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/require\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/serverselector\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/connstring\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/operation\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/topology\"\n)\n\nvar (\n\tconnectionString     *connstring.ConnString\n\tconnectionStringOnce sync.Once\n\tconnectionStringErr  error\n\tliveTopology         *topology.Topology\n\tliveTopologyOnce     sync.Once\n\tliveTopologyErr      error\n)\n\n// AddOptionsToURI appends connection string options to a URI.\nfunc AddOptionsToURI(uri string, opts ...string) string {\n\tif !strings.ContainsRune(uri, '?') {\n\t\tif uri[len(uri)-1] != '/' {\n\t\t\turi += \"/\"\n\t\t}\n\n\t\turi += \"?\"\n\t} else {\n\t\turi += \"&\"\n\t}\n\n\tfor _, opt := range opts {\n\t\turi += opt\n\t}\n\n\treturn uri\n}\n\n// AddTLSConfigToURI checks for the environmental variable indicating that the tests are being run\n// on an SSL-enabled server, and if so, returns a new URI with the necessary configuration.\nfunc AddTLSConfigToURI(uri string) string {\n\tcaFile := os.Getenv(\"MONGO_GO_DRIVER_CA_FILE\")\n\tif len(caFile) == 0 {\n\t\treturn uri\n\t}\n\n\treturn AddOptionsToURI(uri, \"ssl=true&sslCertificateAuthorityFile=\", caFile)\n}\n\n// AddCompressorToURI checks for the environment variable indicating that the tests are being run with compression\n// enabled. If so, it returns a new URI with the necessary configuration\nfunc AddCompressorToURI(uri string) string {\n\tcomp := os.Getenv(\"MONGO_GO_DRIVER_COMPRESSOR\")\n\tif len(comp) == 0 {\n\t\treturn uri\n\t}\n\n\treturn AddOptionsToURI(uri, \"compressors=\", comp)\n}\n\n// AddTestServerAPIVersion adds the latest server API version in a ServerAPIOptions to passed-in opts.\nfunc AddTestServerAPIVersion(opts *options.ClientOptions) {\n\tif os.Getenv(\"REQUIRE_API_VERSION\") == \"true\" {\n\t\topts.SetServerAPIOptions(options.ServerAPI(driver.TestServerAPIVersion))\n\t}\n}\n\n// MonitoredTopology returns a new topology with the command monitor attached\nfunc MonitoredTopology(t *testing.T, dbName string, monitor *event.CommandMonitor) *topology.Topology {\n\turi, err := MongoDBURI()\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\topts := options.Client().ApplyURI(uri).SetMonitor(monitor)\n\n\tcfg, err := topology.NewConfig(opts, nil)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tmonitoredTopology, err := topology.New(cfg)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t} else {\n\t\t_ = monitoredTopology.Connect()\n\n\t\terr = operation.NewCommand(bsoncore.BuildDocument(nil, bsoncore.AppendInt32Element(nil, \"dropDatabase\", 1))).\n\t\t\tDatabase(dbName).ServerSelector(&serverselector.Write{}).Deployment(monitoredTopology).Execute(context.Background())\n\n\t\trequire.NoError(t, err)\n\t}\n\n\treturn monitoredTopology\n}\n\n// Topology gets the globally configured topology.\nfunc Topology(t *testing.T) *topology.Topology {\n\turi, err := MongoDBURI()\n\trequire.NoError(t, err, \"error constructing mongodb URI: %v\", err)\n\n\topts := options.Client().ApplyURI(uri)\n\n\tcfg, err := topology.NewConfig(opts, nil)\n\trequire.NoError(t, err, \"error constructing topology config: %v\", err)\n\n\tliveTopologyOnce.Do(func() {\n\t\tvar err error\n\t\tliveTopology, err = topology.New(cfg)\n\t\tif err != nil {\n\t\t\tliveTopologyErr = err\n\t\t} else {\n\t\t\t_ = liveTopology.Connect()\n\n\t\t\terr = operation.NewCommand(bsoncore.BuildDocument(nil, bsoncore.AppendInt32Element(nil, \"dropDatabase\", 1))).\n\t\t\t\tDatabase(DBName(t)).ServerSelector(&serverselector.Write{}).\n\t\t\t\tDeployment(liveTopology).Execute(context.Background())\n\t\t\trequire.NoError(t, err)\n\t\t}\n\t})\n\n\tif liveTopologyErr != nil {\n\t\tt.Fatal(liveTopologyErr)\n\t}\n\n\treturn liveTopology\n}\n\n// TopologyWithCredential takes an \"options.Credential\" object and returns a connected topology.\nfunc TopologyWithCredential(t *testing.T, credential options.Credential) *topology.Topology {\n\turi, err := MongoDBURI()\n\tif err != nil {\n\t\tt.Fatalf(\"error constructing mongodb URI: %v\", err)\n\t}\n\n\topts := options.Client().ApplyURI(uri).SetAuth(credential)\n\n\tcfg, err := topology.NewConfig(opts, nil)\n\tif err != nil {\n\t\tt.Fatalf(\"error constructing topology config: %v\", err)\n\t}\n\ttopology, err := topology.New(cfg)\n\tif err != nil {\n\t\tt.Fatal(\"Could not construct topology\")\n\t}\n\terr = topology.Connect()\n\tif err != nil {\n\t\tt.Fatal(\"Could not start topology connection\")\n\t}\n\treturn topology\n}\n\n// ColName gets a collection name that should be unique\n// to the currently executing test.\nfunc ColName(t *testing.T) string {\n\t// Get this indirectly to avoid copying a mutex\n\tv := reflect.Indirect(reflect.ValueOf(t))\n\tname := v.FieldByName(\"name\")\n\treturn name.String()\n}\n\n// MongoDBURI will construct the MongoDB URI from the MONGODB_URI environment variable for testing. The default host is\n// \"localhost\" and the default port is \"27017\"\nfunc MongoDBURI() (string, error) {\n\turi := os.Getenv(\"MONGODB_URI\")\n\tif uri == \"\" {\n\t\turi = \"mongodb://localhost:27017\"\n\t}\n\n\turi = AddTLSConfigToURI(uri)\n\turi = AddCompressorToURI(uri)\n\turi, err := AddServerlessAuthCredentials(uri)\n\treturn uri, err\n}\n\n// AddServerlessAuthCredentials will attempt to construct the serverless auth credentials for a URI.\nfunc AddServerlessAuthCredentials(uri string) (string, error) {\n\tif os.Getenv(\"SERVERLESS\") != \"serverless\" {\n\t\treturn uri, nil\n\t}\n\tuser := os.Getenv(\"SERVERLESS_ATLAS_USER\")\n\tif user == \"\" {\n\t\treturn \"\", fmt.Errorf(\"serverless expects SERVERLESS_ATLAS_USER to be set\")\n\t}\n\tpassword := os.Getenv(\"SERVERLESS_ATLAS_PASSWORD\")\n\tif password == \"\" {\n\t\treturn \"\", fmt.Errorf(\"serverless expects SERVERLESS_ATLAS_PASSWORD to be set\")\n\t}\n\n\tvar scheme string\n\t// remove the scheme\n\tswitch {\n\tcase strings.HasPrefix(uri, \"mongodb+srv://\"):\n\t\tscheme = \"mongodb+srv://\"\n\tcase strings.HasPrefix(uri, \"mongodb://\"):\n\t\tscheme = \"mongodb://\"\n\tdefault:\n\t\treturn \"\", errors.New(`scheme must be \"mongodb\" or \"mongodb+srv\"`)\n\t}\n\n\turi = scheme + user + \":\" + password + \"@\" + uri[len(scheme):]\n\treturn uri, nil\n}\n\n// ConnString gets the globally configured connection string.\nfunc ConnString(t *testing.T) *connstring.ConnString {\n\tif testing.Short() {\n\t\tt.Skip(\"skipping integration test in short mode\")\n\t}\n\n\tconnectionStringOnce.Do(func() {\n\t\turi, err := MongoDBURI()\n\t\trequire.NoError(t, err, \"error constructing mongodb URI: %v\", err)\n\n\t\tconnectionString, err = connstring.ParseAndValidate(uri)\n\t\tif err != nil {\n\t\t\tconnectionStringErr = err\n\t\t}\n\t})\n\tif connectionStringErr != nil {\n\t\tt.Fatal(connectionStringErr)\n\t}\n\n\treturn connectionString\n}\n\nfunc GetConnString() (*connstring.ConnString, error) {\n\tmongodbURI := os.Getenv(\"MONGODB_URI\")\n\tif mongodbURI == \"\" {\n\t\tmongodbURI = \"mongodb://localhost:27017\"\n\t}\n\n\tmongodbURI = AddTLSConfigToURI(mongodbURI)\n\n\tcs, err := connstring.ParseAndValidate(mongodbURI)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn cs, nil\n}\n\n// DBName gets the globally configured database name.\nfunc DBName(t *testing.T) string {\n\treturn GetDBName(ConnString(t))\n}\n\nfunc GetDBName(cs *connstring.ConnString) string {\n\tif cs.Database != \"\" {\n\t\treturn cs.Database\n\t}\n\n\treturn fmt.Sprintf(\"mongo-go-driver-%d\", os.Getpid())\n}\n\n// CompareVersions compares two version number strings (i.e. positive integers separated by\n// periods). Comparisons are done to the lesser precision of the two versions. For example, 3.2 is\n// considered equal to 3.2.11, whereas 3.2.0 is considered less than 3.2.11.\n//\n// Returns a positive int if version1 is greater than version2, a negative int if version1 is less\n// than version2, and 0 if version1 is equal to version2.\nfunc CompareVersions(t *testing.T, v1 string, v2 string) int {\n\tn1 := strings.Split(v1, \".\")\n\tn2 := strings.Split(v2, \".\")\n\n\tfor i := 0; i < int(math.Min(float64(len(n1)), float64(len(n2)))); i++ {\n\t\ti1, err := strconv.Atoi(n1[i])\n\t\trequire.NoError(t, err)\n\n\t\ti2, err := strconv.Atoi(n2[i])\n\t\trequire.NoError(t, err)\n\n\t\tdifference := i1 - i2\n\t\tif difference != 0 {\n\t\t\treturn difference\n\t\t}\n\t}\n\n\treturn 0\n}\n"
  },
  {
    "path": "internal/israce/norace.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\n//go:build !race\n\n// Package israce reports if the Go race detector is enabled.\npackage israce\n\n// Enabled reports if the race detector is enabled.\nconst Enabled = false\n"
  },
  {
    "path": "internal/israce/race.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\n//go:build race\n\n// Package israce reports if the Go race detector is enabled.\npackage israce\n\n// Enabled reports if the race detector is enabled.\nconst Enabled = true\n"
  },
  {
    "path": "internal/logger/component.go",
    "content": "// Copyright (C) MongoDB, Inc. 2023-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage logger\n\nimport (\n\t\"os\"\n\t\"strconv\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n)\n\nconst (\n\tCommandFailed                    = \"Command failed\"\n\tCommandStarted                   = \"Command started\"\n\tCommandSucceeded                 = \"Command succeeded\"\n\tConnectionPoolCreated            = \"Connection pool created\"\n\tConnectionPoolReady              = \"Connection pool ready\"\n\tConnectionPoolCleared            = \"Connection pool cleared\"\n\tConnectionPoolClosed             = \"Connection pool closed\"\n\tConnectionCreated                = \"Connection created\"\n\tConnectionReady                  = \"Connection ready\"\n\tConnectionClosed                 = \"Connection closed\"\n\tConnectionCheckoutStarted        = \"Connection checkout started\"\n\tConnectionCheckoutFailed         = \"Connection checkout failed\"\n\tConnectionCheckedOut             = \"Connection checked out\"\n\tConnectionCheckedIn              = \"Connection checked in\"\n\tServerSelectionFailed            = \"Server selection failed\"\n\tServerSelectionStarted           = \"Server selection started\"\n\tServerSelectionSucceeded         = \"Server selection succeeded\"\n\tServerSelectionWaiting           = \"Waiting for suitable server to become available\"\n\tTopologyClosed                   = \"Stopped topology monitoring\"\n\tTopologyDescriptionChanged       = \"Topology description changed\"\n\tTopologyOpening                  = \"Starting topology monitoring\"\n\tTopologyServerClosed             = \"Stopped server monitoring\"\n\tTopologyServerHeartbeatFailed    = \"Server heartbeat failed\"\n\tTopologyServerHeartbeatStarted   = \"Server heartbeat started\"\n\tTopologyServerHeartbeatSucceeded = \"Server heartbeat succeeded\"\n\tTopologyServerOpening            = \"Starting server monitoring\"\n)\n\nconst (\n\tKeyAwaited             = \"awaited\"\n\tKeyCommand             = \"command\"\n\tKeyCommandName         = \"commandName\"\n\tKeyDatabaseName        = \"databaseName\"\n\tKeyDriverConnectionID  = \"driverConnectionId\"\n\tKeyDurationMS          = \"durationMS\"\n\tKeyError               = \"error\"\n\tKeyFailure             = \"failure\"\n\tKeyMaxConnecting       = \"maxConnecting\"\n\tKeyMaxIdleTimeMS       = \"maxIdleTimeMS\"\n\tKeyMaxPoolSize         = \"maxPoolSize\"\n\tKeyMessage             = \"message\"\n\tKeyMinPoolSize         = \"minPoolSize\"\n\tKeyNewDescription      = \"newDescription\"\n\tKeyOperation           = \"operation\"\n\tKeyOperationID         = \"operationId\"\n\tKeyPreviousDescription = \"previousDescription\"\n\tKeyRemainingTimeMS     = \"remainingTimeMS\"\n\tKeyReason              = \"reason\"\n\tKeyReply               = \"reply\"\n\tKeyRequestID           = \"requestId\"\n\tKeySelector            = \"selector\"\n\tKeyServerConnectionID  = \"serverConnectionId\"\n\tKeyServerHost          = \"serverHost\"\n\tKeyServerPort          = \"serverPort\"\n\tKeyServiceID           = \"serviceId\"\n\tKeyTimestamp           = \"timestamp\"\n\tKeyTopologyDescription = \"topologyDescription\"\n\tKeyTopologyID          = \"topologyId\"\n)\n\n// KeyValues is a list of key-value pairs.\ntype KeyValues []any\n\n// Add adds a key-value pair to an instance of a KeyValues list.\nfunc (kvs *KeyValues) Add(key string, value any) {\n\t*kvs = append(*kvs, key, value)\n}\n\nconst (\n\tReasonConnClosedStale              = \"Connection became stale because the pool was cleared\"\n\tReasonConnClosedIdle               = \"Connection has been available but unused for longer than the configured max idle time\"\n\tReasonConnClosedError              = \"An error occurred while using the connection\"\n\tReasonConnClosedPoolClosed         = \"Connection pool was closed\"\n\tReasonConnCheckoutFailedTimout     = \"Wait queue timeout elapsed without a connection becoming available\"\n\tReasonConnCheckoutFailedError      = \"An error occurred while trying to establish a new connection\"\n\tReasonConnCheckoutFailedPoolClosed = \"Connection pool was closed\"\n)\n\n// Component is an enumeration representing the \"components\" which can be\n// logged against. A LogLevel can be configured on a per-component basis.\ntype Component int\n\nconst (\n\t// ComponentAll enables logging for all components.\n\tComponentAll Component = iota\n\n\t// ComponentCommand enables command monitor logging.\n\tComponentCommand\n\n\t// ComponentTopology enables topology logging.\n\tComponentTopology\n\n\t// ComponentServerSelection enables server selection logging.\n\tComponentServerSelection\n\n\t// ComponentConnection enables connection services logging.\n\tComponentConnection\n)\n\nconst (\n\tmongoDBLogAllEnvVar             = \"MONGODB_LOG_ALL\"\n\tmongoDBLogCommandEnvVar         = \"MONGODB_LOG_COMMAND\"\n\tmongoDBLogTopologyEnvVar        = \"MONGODB_LOG_TOPOLOGY\"\n\tmongoDBLogServerSelectionEnvVar = \"MONGODB_LOG_SERVER_SELECTION\"\n\tmongoDBLogConnectionEnvVar      = \"MONGODB_LOG_CONNECTION\"\n)\n\nvar componentEnvVarMap = map[string]Component{\n\tmongoDBLogAllEnvVar:             ComponentAll,\n\tmongoDBLogCommandEnvVar:         ComponentCommand,\n\tmongoDBLogTopologyEnvVar:        ComponentTopology,\n\tmongoDBLogServerSelectionEnvVar: ComponentServerSelection,\n\tmongoDBLogConnectionEnvVar:      ComponentConnection,\n}\n\n// EnvHasComponentVariables returns true if the environment contains any of the\n// component environment variables.\nfunc EnvHasComponentVariables() bool {\n\tfor envVar := range componentEnvVarMap {\n\t\tif os.Getenv(envVar) != \"\" {\n\t\t\treturn true\n\t\t}\n\t}\n\n\treturn false\n}\n\n// Command is a struct defining common fields that must be included in all\n// commands.\ntype Command struct {\n\tDriverConnectionID int64          // Driver's ID for the connection\n\tName               string         // Command name\n\tDatabaseName       string         // Database name\n\tMessage            string         // Message associated with the command\n\tOperationID        int32          // Driver-generated operation ID\n\tRequestID          int64          // Driver-generated request ID\n\tServerConnectionID *int64         // Server's ID for the connection used for the command\n\tServerHost         string         // Hostname or IP address for the server\n\tServerPort         string         // Port for the server\n\tServiceID          *bson.ObjectID // ID for the command  in load balancer mode\n}\n\n// SerializeCommand takes a command and a variable number of key-value pairs and\n// returns a slice of any that can be passed to the logger for\n// structured logging.\nfunc SerializeCommand(cmd Command, extraKeysAndValues ...any) KeyValues {\n\t// Initialize the boilerplate keys and values.\n\tkeysAndValues := KeyValues{\n\t\tKeyCommandName, cmd.Name,\n\t\tKeyDatabaseName, cmd.DatabaseName,\n\t\tKeyDriverConnectionID, cmd.DriverConnectionID,\n\t\tKeyMessage, cmd.Message,\n\t\tKeyOperationID, cmd.OperationID,\n\t\tKeyRequestID, cmd.RequestID,\n\t\tKeyServerHost, cmd.ServerHost,\n\t}\n\n\t// Add the extra keys and values.\n\tfor i := 0; i < len(extraKeysAndValues); i += 2 {\n\t\tkeysAndValues.Add(extraKeysAndValues[i].(string), extraKeysAndValues[i+1])\n\t}\n\n\tport, err := strconv.ParseInt(cmd.ServerPort, 10, 32)\n\tif err == nil {\n\t\tkeysAndValues.Add(KeyServerPort, port)\n\t}\n\n\t// Add the \"serverConnectionId\" if it is not nil.\n\tif cmd.ServerConnectionID != nil {\n\t\tkeysAndValues.Add(KeyServerConnectionID, *cmd.ServerConnectionID)\n\t}\n\n\t// Add the \"serviceId\" if it is not nil.\n\tif cmd.ServiceID != nil {\n\t\tkeysAndValues.Add(KeyServiceID, cmd.ServiceID.Hex())\n\t}\n\n\treturn keysAndValues\n}\n\n// Connection contains data that all connection log messages MUST contain.\ntype Connection struct {\n\tMessage    string // Message associated with the connection\n\tServerHost string // Hostname or IP address for the server\n\tServerPort string // Port for the server\n}\n\n// SerializeConnection serializes a Connection message into a slice of keys and\n// values that can be passed to a logger.\nfunc SerializeConnection(conn Connection, extraKeysAndValues ...any) KeyValues {\n\t// Initialize the boilerplate keys and values.\n\tkeysAndValues := KeyValues{\n\t\tKeyMessage, conn.Message,\n\t\tKeyServerHost, conn.ServerHost,\n\t}\n\n\t// Add the optional keys and values.\n\tfor i := 0; i < len(extraKeysAndValues); i += 2 {\n\t\tkeysAndValues.Add(extraKeysAndValues[i].(string), extraKeysAndValues[i+1])\n\t}\n\n\tport, err := strconv.ParseInt(conn.ServerPort, 10, 32)\n\tif err == nil {\n\t\tkeysAndValues.Add(KeyServerPort, port)\n\t}\n\n\treturn keysAndValues\n}\n\n// Server contains data that all server messages MAY contain.\ntype Server struct {\n\tDriverConnectionID int64         // Driver's ID for the connection\n\tTopologyID         bson.ObjectID // Driver's unique ID for this topology\n\tMessage            string        // Message associated with the topology\n\tServerConnectionID *int64        // Server's ID for the connection\n\tServerHost         string        // Hostname or IP address for the server\n\tServerPort         string        // Port for the server\n}\n\n// SerializeServer serializes a Server message into a slice of keys and\n// values that can be passed to a logger.\nfunc SerializeServer(srv Server, extraKV ...any) KeyValues {\n\t// Initialize the boilerplate keys and values.\n\tkeysAndValues := KeyValues{\n\t\tKeyDriverConnectionID, srv.DriverConnectionID,\n\t\tKeyMessage, srv.Message,\n\t\tKeyServerHost, srv.ServerHost,\n\t\tKeyTopologyID, srv.TopologyID.Hex(),\n\t}\n\n\tif connID := srv.ServerConnectionID; connID != nil {\n\t\tkeysAndValues.Add(KeyServerConnectionID, *connID)\n\t}\n\n\tport, err := strconv.ParseInt(srv.ServerPort, 10, 32)\n\tif err == nil {\n\t\tkeysAndValues.Add(KeyServerPort, port)\n\t}\n\n\t// Add the optional keys and values.\n\tfor i := 0; i < len(extraKV); i += 2 {\n\t\tkeysAndValues.Add(extraKV[i].(string), extraKV[i+1])\n\t}\n\n\treturn keysAndValues\n}\n\n// ServerSelection contains data that all server selection messages MUST\n// contain.\ntype ServerSelection struct {\n\tSelector            string\n\tOperationID         *int32\n\tOperation           string\n\tTopologyDescription string\n}\n\n// SerializeServerSelection serializes a Topology message into a slice of keys\n// and values that can be passed to a logger.\nfunc SerializeServerSelection(srvSelection ServerSelection, extraKV ...any) KeyValues {\n\tkeysAndValues := KeyValues{\n\t\tKeySelector, srvSelection.Selector,\n\t\tKeyOperation, srvSelection.Operation,\n\t\tKeyTopologyDescription, srvSelection.TopologyDescription,\n\t}\n\n\tif srvSelection.OperationID != nil {\n\t\tkeysAndValues.Add(KeyOperationID, *srvSelection.OperationID)\n\t}\n\n\t// Add the optional keys and values.\n\tfor i := 0; i < len(extraKV); i += 2 {\n\t\tkeysAndValues.Add(extraKV[i].(string), extraKV[i+1])\n\t}\n\n\treturn keysAndValues\n}\n\n// Topology contains data that all topology messages MAY contain.\ntype Topology struct {\n\tID      bson.ObjectID // Driver's unique ID for this topology\n\tMessage string        // Message associated with the topology\n}\n\n// SerializeTopology serializes a Topology message into a slice of keys and\n// values that can be passed to a logger.\nfunc SerializeTopology(topo Topology, extraKV ...any) KeyValues {\n\tkeysAndValues := KeyValues{\n\t\tKeyTopologyID, topo.ID.Hex(),\n\t}\n\n\t// Add the optional keys and values.\n\tfor i := 0; i < len(extraKV); i += 2 {\n\t\tkeysAndValues.Add(extraKV[i].(string), extraKV[i+1])\n\t}\n\n\treturn keysAndValues\n}\n"
  },
  {
    "path": "internal/logger/component_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2023-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage logger\n\nimport (\n\t\"testing\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n)\n\nfunc verifySerialization(t *testing.T, got, want KeyValues) {\n\tt.Helper()\n\n\tfor i := 0; i < len(got); i += 2 {\n\t\tassert.Equal(t, want[i], got[i], \"key position mismatch\")\n\t\tassert.Equal(t, want[i+1], got[i+1], \"value position mismatch for %q\", want[i])\n\t}\n}\n\nfunc TestSerializeCommand(t *testing.T) {\n\tt.Parallel()\n\n\tserverConnectionID := int64(100)\n\tserviceID := bson.NewObjectID()\n\n\ttests := []struct {\n\t\tname               string\n\t\tcmd                Command\n\t\textraKeysAndValues []any\n\t\twant               KeyValues\n\t}{\n\t\t{\n\t\t\tname: \"empty\",\n\t\t\twant: KeyValues{\n\t\t\t\tKeyCommandName, \"\",\n\t\t\t\tKeyDatabaseName, \"\",\n\t\t\t\tKeyDriverConnectionID, int64(0),\n\t\t\t\tKeyMessage, \"\",\n\t\t\t\tKeyOperationID, int32(0),\n\t\t\t\tKeyRequestID, int64(0),\n\t\t\t\tKeyServerHost, \"\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"complete Command object\",\n\t\t\tcmd: Command{\n\t\t\t\tDriverConnectionID: 1,\n\t\t\t\tName:               \"foo\",\n\t\t\t\tDatabaseName:       \"db\",\n\t\t\t\tMessage:            \"bar\",\n\t\t\t\tOperationID:        2,\n\t\t\t\tRequestID:          3,\n\t\t\t\tServerHost:         \"localhost\",\n\t\t\t\tServerPort:         \"27017\",\n\t\t\t\tServerConnectionID: &serverConnectionID,\n\t\t\t\tServiceID:          &serviceID,\n\t\t\t},\n\t\t\twant: KeyValues{\n\t\t\t\tKeyCommandName, \"foo\",\n\t\t\t\tKeyDatabaseName, \"db\",\n\t\t\t\tKeyDriverConnectionID, int64(1),\n\t\t\t\tKeyMessage, \"bar\",\n\t\t\t\tKeyOperationID, int32(2),\n\t\t\t\tKeyRequestID, int64(3),\n\t\t\t\tKeyServerHost, \"localhost\",\n\t\t\t\tKeyServerPort, int64(27017),\n\t\t\t\tKeyServerConnectionID, serverConnectionID,\n\t\t\t\tKeyServiceID, serviceID.Hex(),\n\t\t\t},\n\t\t},\n\t}\n\n\tfor _, test := range tests {\n\t\ttest := test\n\n\t\tt.Run(test.name, func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tgot := SerializeCommand(test.cmd, test.extraKeysAndValues...)\n\t\t\tverifySerialization(t, got, test.want)\n\t\t})\n\t}\n}\n\nfunc TestSerializeConnection(t *testing.T) {\n\tt.Parallel()\n\n\ttests := []struct {\n\t\tname               string\n\t\tconn               Connection\n\t\textraKeysAndValues []any\n\t\twant               KeyValues\n\t}{\n\t\t{\n\t\t\tname: \"empty\",\n\t\t\twant: KeyValues{\n\t\t\t\tKeyMessage, \"\",\n\t\t\t\tKeyServerHost, \"\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"complete Connection object\",\n\t\t\tconn: Connection{\n\t\t\t\tMessage:    \"foo\",\n\t\t\t\tServerHost: \"localhost\",\n\t\t\t\tServerPort: \"27017\",\n\t\t\t},\n\t\t\twant: KeyValues{\n\t\t\t\t\"message\", \"foo\",\n\t\t\t\t\"serverHost\", \"localhost\",\n\t\t\t\t\"serverPort\", int64(27017),\n\t\t\t},\n\t\t},\n\t}\n\n\tfor _, test := range tests {\n\t\ttest := test\n\n\t\tt.Run(test.name, func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tgot := SerializeConnection(test.conn, test.extraKeysAndValues...)\n\t\t\tverifySerialization(t, got, test.want)\n\t\t})\n\t}\n}\n\nfunc TestSerializeServer(t *testing.T) {\n\tt.Parallel()\n\n\ttopologyID := bson.NewObjectID()\n\tserverConnectionID := int64(100)\n\n\ttests := []struct {\n\t\tname               string\n\t\tsrv                Server\n\t\textraKeysAndValues []any\n\t\twant               KeyValues\n\t}{\n\t\t{\n\t\t\tname: \"empty\",\n\t\t\twant: KeyValues{\n\t\t\t\tKeyDriverConnectionID, int64(0),\n\t\t\t\tKeyMessage, \"\",\n\t\t\t\tKeyServerHost, \"\",\n\t\t\t\tKeyTopologyID, bson.ObjectID{}.Hex(),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"complete Server object\",\n\t\t\tsrv: Server{\n\t\t\t\tDriverConnectionID: 1,\n\t\t\t\tTopologyID:         topologyID,\n\t\t\t\tMessage:            \"foo\",\n\t\t\t\tServerConnectionID: &serverConnectionID,\n\t\t\t\tServerHost:         \"localhost\",\n\t\t\t\tServerPort:         \"27017\",\n\t\t\t},\n\t\t\twant: KeyValues{\n\t\t\t\tKeyDriverConnectionID, int64(1),\n\t\t\t\tKeyMessage, \"foo\",\n\t\t\t\tKeyServerHost, \"localhost\",\n\t\t\t\tKeyTopologyID, topologyID.Hex(),\n\t\t\t\tKeyServerConnectionID, serverConnectionID,\n\t\t\t\tKeyServerPort, int64(27017),\n\t\t\t},\n\t\t},\n\t}\n\n\tfor _, test := range tests {\n\t\ttest := test\n\n\t\tt.Run(test.name, func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tgot := SerializeServer(test.srv, test.extraKeysAndValues...)\n\t\t\tverifySerialization(t, got, test.want)\n\t\t})\n\t}\n}\n\nfunc TestSerializeTopology(t *testing.T) {\n\tt.Parallel()\n\n\ttopologyID := bson.NewObjectID()\n\n\ttests := []struct {\n\t\tname               string\n\t\ttopo               Topology\n\t\textraKeysAndValues []any\n\t\twant               KeyValues\n\t}{\n\t\t{\n\t\t\tname: \"empty\",\n\t\t\twant: KeyValues{\n\t\t\t\tKeyTopologyID, bson.ObjectID{}.Hex(),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"complete Server object\",\n\t\t\ttopo: Topology{\n\t\t\t\tID:      topologyID,\n\t\t\t\tMessage: \"foo\",\n\t\t\t},\n\t\t\twant: KeyValues{\n\t\t\t\tKeyTopologyID, topologyID.Hex(),\n\t\t\t\tKeyMessage, \"foo\",\n\t\t\t},\n\t\t},\n\t}\n\n\tfor _, test := range tests {\n\t\ttest := test\n\n\t\tt.Run(test.name, func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tgot := SerializeTopology(test.topo, test.extraKeysAndValues...)\n\t\t\tverifySerialization(t, got, test.want)\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "internal/logger/context.go",
    "content": "// Copyright (C) MongoDB, Inc. 2023-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage logger\n\nimport \"context\"\n\n// contextKey is a custom type used to prevent key collisions when using the\n// context package.\ntype contextKey string\n\nconst (\n\tcontextKeyOperation   contextKey = \"operation\"\n\tcontextKeyOperationID contextKey = \"operationID\"\n)\n\n// WithOperationName adds the operation name to the context.\nfunc WithOperationName(ctx context.Context, operation string) context.Context {\n\treturn context.WithValue(ctx, contextKeyOperation, operation)\n}\n\n// WithOperationID adds the operation ID to the context.\nfunc WithOperationID(ctx context.Context, operationID int32) context.Context {\n\treturn context.WithValue(ctx, contextKeyOperationID, operationID)\n}\n\n// OperationName returns the operation name from the context.\nfunc OperationName(ctx context.Context) (string, bool) {\n\toperationName := ctx.Value(contextKeyOperation)\n\tif operationName == nil {\n\t\treturn \"\", false\n\t}\n\n\treturn operationName.(string), true\n}\n\n// OperationID returns the operation ID from the context.\nfunc OperationID(ctx context.Context) (int32, bool) {\n\toperationID := ctx.Value(contextKeyOperationID)\n\tif operationID == nil {\n\t\treturn 0, false\n\t}\n\n\treturn operationID.(int32), true\n}\n"
  },
  {
    "path": "internal/logger/context_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2023-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage logger_test\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/logger\"\n)\n\nfunc TestContext_WithOperationName(t *testing.T) {\n\tt.Parallel()\n\n\ttests := []struct {\n\t\tname   string\n\t\tctx    context.Context\n\t\topName string\n\t\tok     bool\n\t}{\n\t\t{\n\t\t\tname:   \"simple\",\n\t\t\tctx:    context.Background(),\n\t\t\topName: \"foo\",\n\t\t\tok:     true,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\ttt := tt // Capture the range variable.\n\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tctx := logger.WithOperationName(tt.ctx, tt.opName)\n\n\t\t\topName, ok := logger.OperationName(ctx)\n\t\t\tassert.Equal(t, tt.ok, ok)\n\n\t\t\tif ok {\n\t\t\t\tassert.Equal(t, tt.opName, opName)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestContext_OperationName(t *testing.T) {\n\tt.Parallel()\n\n\ttests := []struct {\n\t\tname   string\n\t\tctx    context.Context\n\t\topName any\n\t\tok     bool\n\t}{\n\t\t{\n\t\t\tname:   \"nil\",\n\t\t\tctx:    context.Background(),\n\t\t\topName: nil,\n\t\t\tok:     false,\n\t\t},\n\t\t{\n\t\t\tname:   \"string type\",\n\t\t\tctx:    context.Background(),\n\t\t\topName: \"foo\",\n\t\t\tok:     true,\n\t\t},\n\t\t{\n\t\t\tname:   \"non-string type\",\n\t\t\tctx:    context.Background(),\n\t\t\topName: int32(1),\n\t\t\tok:     false,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\ttt := tt // Capture the range variable.\n\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tctx := context.Background()\n\n\t\t\tif opNameStr, ok := tt.opName.(string); ok {\n\t\t\t\tctx = logger.WithOperationName(tt.ctx, opNameStr)\n\t\t\t}\n\n\t\t\topName, ok := logger.OperationName(ctx)\n\t\t\tassert.Equal(t, tt.ok, ok)\n\n\t\t\tif ok {\n\t\t\t\tassert.Equal(t, tt.opName, opName)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestContext_WithOperationID(t *testing.T) {\n\tt.Parallel()\n\n\ttests := []struct {\n\t\tname string\n\t\tctx  context.Context\n\t\topID int32\n\t\tok   bool\n\t}{\n\t\t{\n\t\t\tname: \"non-zero\",\n\t\t\tctx:  context.Background(),\n\t\t\topID: 1,\n\t\t\tok:   true,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\ttt := tt // Capture the range variable.\n\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tctx := logger.WithOperationID(tt.ctx, tt.opID)\n\n\t\t\topID, ok := logger.OperationID(ctx)\n\t\t\tassert.Equal(t, tt.ok, ok)\n\n\t\t\tif ok {\n\t\t\t\tassert.Equal(t, tt.opID, opID)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestContext_OperationID(t *testing.T) {\n\tt.Parallel()\n\n\ttests := []struct {\n\t\tname string\n\t\tctx  context.Context\n\t\topID any\n\t\tok   bool\n\t}{\n\t\t{\n\t\t\tname: \"nil\",\n\t\t\tctx:  context.Background(),\n\t\t\topID: nil,\n\t\t\tok:   false,\n\t\t},\n\t\t{\n\t\t\tname: \"i32 type\",\n\t\t\tctx:  context.Background(),\n\t\t\topID: int32(1),\n\t\t\tok:   true,\n\t\t},\n\t\t{\n\t\t\tname: \"non-i32 type\",\n\t\t\tctx:  context.Background(),\n\t\t\topID: \"foo\",\n\t\t\tok:   false,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\ttt := tt // Capture the range variable.\n\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tctx := context.Background()\n\n\t\t\tif opIDI32, ok := tt.opID.(int32); ok {\n\t\t\t\tctx = logger.WithOperationID(tt.ctx, opIDI32)\n\t\t\t}\n\n\t\t\topName, ok := logger.OperationID(ctx)\n\t\t\tassert.Equal(t, tt.ok, ok)\n\n\t\t\tif ok {\n\t\t\t\tassert.Equal(t, tt.opID, opName)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "internal/logger/io_sink.go",
    "content": "// Copyright (C) MongoDB, Inc. 2023-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage logger\n\nimport (\n\t\"encoding/json\"\n\t\"io\"\n\t\"math\"\n\t\"sync\"\n\t\"time\"\n)\n\n// IOSink writes a JSON-encoded message to the io.Writer.\ntype IOSink struct {\n\tenc *json.Encoder\n\n\t// encMu protects the encoder from concurrent writes. While the logger\n\t// itself does not concurrently write to the sink, the sink may be used\n\t// concurrently within the driver.\n\tencMu sync.Mutex\n}\n\n// Compile-time check to ensure IOSink implements the LogSink interface.\nvar _ LogSink = &IOSink{}\n\n// NewIOSink will create an IOSink object that writes JSON messages to the\n// provided io.Writer.\nfunc NewIOSink(out io.Writer) *IOSink {\n\treturn &IOSink{\n\t\tenc: json.NewEncoder(out),\n\t}\n}\n\n// Info will write a JSON-encoded message to the io.Writer.\nfunc (sink *IOSink) Info(_ int, msg string, keysAndValues ...any) {\n\tmapSize := len(keysAndValues) / 2\n\tif math.MaxInt-mapSize >= 2 {\n\t\tmapSize += 2\n\t}\n\tkvMap := make(map[string]any, mapSize)\n\n\tkvMap[KeyTimestamp] = time.Now().UnixNano()\n\tkvMap[KeyMessage] = msg\n\n\tfor i := 0; i < len(keysAndValues); i += 2 {\n\t\tkvMap[keysAndValues[i].(string)] = keysAndValues[i+1]\n\t}\n\n\tsink.encMu.Lock()\n\tdefer sink.encMu.Unlock()\n\n\t_ = sink.enc.Encode(kvMap)\n}\n\n// Error will write a JSON-encoded error message to the io.Writer.\nfunc (sink *IOSink) Error(err error, msg string, kv ...any) {\n\tkv = append(kv, KeyError, err.Error())\n\tsink.Info(0, msg, kv...)\n}\n"
  },
  {
    "path": "internal/logger/level.go",
    "content": "// Copyright (C) MongoDB, Inc. 2023-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage logger\n\nimport \"strings\"\n\n// DiffToInfo is the number of levels in the Go Driver that come before the\n// \"Info\" level. This should ensure that \"Info\" is the 0th level passed to the\n// sink.\nconst DiffToInfo = 1\n\n// Level is an enumeration representing the log severity levels supported by\n// the driver. The order of the logging levels is important. The driver expects\n// that a user will likely use the \"logr\" package to create a LogSink, which\n// defaults InfoLevel as 0. Any additions to the Level enumeration before the\n// InfoLevel will need to also update the \"diffToInfo\" constant.\ntype Level int\n\nconst (\n\t// LevelOff suppresses logging.\n\tLevelOff Level = iota\n\n\t// LevelInfo enables logging of informational messages. These logs are\n\t// high-level information about normal driver behavior.\n\tLevelInfo\n\n\t// LevelDebug enables logging of debug messages. These logs can be\n\t// voluminous and are intended for detailed information that may be\n\t// helpful when debugging an application.\n\tLevelDebug\n)\n\nconst (\n\tlevelLiteralOff       = \"off\"\n\tlevelLiteralEmergency = \"emergency\"\n\tlevelLiteralAlert     = \"alert\"\n\tlevelLiteralCritical  = \"critical\"\n\tlevelLiteralError     = \"error\"\n\tlevelLiteralWarning   = \"warning\"\n\tlevelLiteralNotice    = \"notice\"\n\tlevelLiteralInfo      = \"info\"\n\tlevelLiteralDebug     = \"debug\"\n\tlevelLiteralTrace     = \"trace\"\n)\n\nvar LevelLiteralMap = map[string]Level{\n\tlevelLiteralOff:       LevelOff,\n\tlevelLiteralEmergency: LevelInfo,\n\tlevelLiteralAlert:     LevelInfo,\n\tlevelLiteralCritical:  LevelInfo,\n\tlevelLiteralError:     LevelInfo,\n\tlevelLiteralWarning:   LevelInfo,\n\tlevelLiteralNotice:    LevelInfo,\n\tlevelLiteralInfo:      LevelInfo,\n\tlevelLiteralDebug:     LevelDebug,\n\tlevelLiteralTrace:     LevelDebug,\n}\n\n// ParseLevel will check if the given string is a valid environment variable\n// for a logging severity level. If it is, then it will return the associated\n// driver's Level. The default Level is “LevelOff”.\nfunc ParseLevel(str string) Level {\n\tfor literal, level := range LevelLiteralMap {\n\t\tif strings.EqualFold(literal, str) {\n\t\t\treturn level\n\t\t}\n\t}\n\n\treturn LevelOff\n}\n"
  },
  {
    "path": "internal/logger/logger.go",
    "content": "// Copyright (C) MongoDB, Inc. 2023-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\n// Package logger provides the internal logging solution for the MongoDB Go\n// Driver.\npackage logger\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/bsoncoreutil\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n)\n\n// DefaultMaxDocumentLength is the default maximum number of bytes that can be\n// logged for a stringified BSON document.\nconst DefaultMaxDocumentLength = 1000\n\n// TruncationSuffix are trailing ellipsis \"...\" appended to a message to\n// indicate to the user that truncation occurred. This constant does not count\n// toward the max document length.\nconst TruncationSuffix = \"...\"\n\nconst (\n\tlogSinkPathEnvVar       = \"MONGODB_LOG_PATH\"\n\tmaxDocumentLengthEnvVar = \"MONGODB_LOG_MAX_DOCUMENT_LENGTH\"\n)\n\n// LogSink represents a logging implementation, this interface should be 1-1\n// with the exported \"LogSink\" interface in the mongo/options package.\ntype LogSink interface {\n\t// Info logs a non-error message with the given key/value pairs. The\n\t// level argument is provided for optional logging.\n\tInfo(level int, msg string, keysAndValues ...any)\n\n\t// Error logs an error, with the given message and key/value pairs.\n\tError(err error, msg string, keysAndValues ...any)\n}\n\n// Logger represents the configuration for the internal logger.\ntype Logger struct {\n\tComponentLevels   map[Component]Level // Log levels for each component.\n\tSink              LogSink             // LogSink for log printing.\n\tMaxDocumentLength uint                // Command truncation width.\n\tlogFile           *os.File            // File to write logs to.\n}\n\n// New will construct a new logger. If any of the given options are the\n// zero-value of the argument type, then the constructor will attempt to\n// source the data from the environment. If the environment has not been set,\n// then the constructor will the respective default values.\nfunc New(sink LogSink, maxDocLen uint, compLevels map[Component]Level) (*Logger, error) {\n\tlogger := &Logger{\n\t\tComponentLevels:   selectComponentLevels(compLevels),\n\t\tMaxDocumentLength: selectMaxDocumentLength(maxDocLen),\n\t}\n\n\tsink, logFile, err := selectLogSink(sink)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tlogger.Sink = sink\n\tlogger.logFile = logFile\n\n\treturn logger, nil\n}\n\n// Close will close the logger's log file, if it exists.\nfunc (logger *Logger) Close() error {\n\tif logger.logFile != nil {\n\t\treturn logger.logFile.Close()\n\t}\n\n\treturn nil\n}\n\n// LevelComponentEnabled will return true if the given LogLevel is enabled for\n// the given LogComponent. If the ComponentLevels on the logger are enabled for\n// \"ComponentAll\", then this function will return true for any level bound by\n// the level assigned to \"ComponentAll\".\n//\n// If the level is not enabled (i.e. LevelOff), then false is returned. This is\n// to avoid false positives, such as returning \"true\" for a component that is\n// not enabled. For example, without this condition, an empty LevelComponent\n// would be considered \"enabled\" for \"LevelOff\".\nfunc (logger *Logger) LevelComponentEnabled(level Level, component Component) bool {\n\tif level == LevelOff {\n\t\treturn false\n\t}\n\n\tif logger.ComponentLevels == nil {\n\t\treturn false\n\t}\n\n\treturn logger.ComponentLevels[component] >= level ||\n\t\tlogger.ComponentLevels[ComponentAll] >= level\n}\n\n// Print will synchronously print the given message to the configured LogSink.\n// If the LogSink is nil, then this method will do nothing. Future work could be done to make\n// this method asynchronous, see buffer management in libraries such as log4j.\n//\n// It's worth noting that many structured logs defined by DBX-wide\n// specifications include a \"message\" field, which is often shared with the\n// message arguments passed to this print function. The \"Info\" method used by\n// this function is implemented based on the go-logr/logr LogSink interface,\n// which is why \"Print\" has a message parameter. Any duplication in code is\n// intentional to adhere to the logr pattern.\nfunc (logger *Logger) Print(level Level, component Component, msg string, keysAndValues ...any) {\n\t// If the level is not enabled for the component, then\n\t// skip the message.\n\tif !logger.LevelComponentEnabled(level, component) {\n\t\treturn\n\t}\n\n\t// If the sink is nil, then skip the message.\n\tif logger.Sink == nil {\n\t\treturn\n\t}\n\n\tlogger.Sink.Info(int(level)-DiffToInfo, msg, keysAndValues...)\n}\n\n// Error logs an error, with the given message and key/value pairs.\n// It functions similarly to Print, but may have unique behavior, and should be\n// preferred for logging errors.\nfunc (logger *Logger) Error(err error, msg string, keysAndValues ...any) {\n\tif logger.Sink == nil {\n\t\treturn\n\t}\n\n\tlogger.Sink.Error(err, msg, keysAndValues...)\n}\n\n// selectMaxDocumentLength will return the integer value of the first non-zero\n// function, with the user-defined function taking priority over the environment\n// variables. For the environment, the function will attempt to get the value of\n// \"MONGODB_LOG_MAX_DOCUMENT_LENGTH\" and parse it as an unsigned integer. If the\n// environment variable is not set or is not an unsigned integer, then this\n// function will return the default max document length.\nfunc selectMaxDocumentLength(maxDocLen uint) uint {\n\tif maxDocLen != 0 {\n\t\treturn maxDocLen\n\t}\n\n\tmaxDocLenEnv := os.Getenv(maxDocumentLengthEnvVar)\n\tif maxDocLenEnv != \"\" {\n\t\tmaxDocLenEnvInt, err := strconv.ParseUint(maxDocLenEnv, 10, 32)\n\t\tif err == nil {\n\t\t\treturn uint(maxDocLenEnvInt)\n\t\t}\n\t}\n\n\treturn DefaultMaxDocumentLength\n}\n\nconst (\n\tlogSinkPathStdout = \"stdout\"\n\tlogSinkPathStderr = \"stderr\"\n)\n\n// selectLogSink will return the first non-nil LogSink, with the user-defined\n// LogSink taking precedence over the environment-defined LogSink. If no LogSink\n// is defined, then this function will return a LogSink that writes to stderr.\nfunc selectLogSink(sink LogSink) (LogSink, *os.File, error) {\n\tif sink != nil {\n\t\treturn sink, nil, nil\n\t}\n\n\tpath := os.Getenv(logSinkPathEnvVar)\n\tlowerPath := strings.ToLower(path)\n\n\tif lowerPath == string(logSinkPathStderr) {\n\t\treturn NewIOSink(os.Stderr), nil, nil\n\t}\n\n\tif lowerPath == string(logSinkPathStdout) {\n\t\treturn NewIOSink(os.Stdout), nil, nil\n\t}\n\n\tif path != \"\" {\n\t\tlogFile, err := os.OpenFile(path, os.O_APPEND|os.O_CREATE|os.O_RDWR, 0o666)\n\t\tif err != nil {\n\t\t\treturn nil, nil, fmt.Errorf(\"unable to open log file: %w\", err)\n\t\t}\n\n\t\treturn NewIOSink(logFile), logFile, nil\n\t}\n\n\treturn NewIOSink(os.Stderr), nil, nil\n}\n\n// selectComponentLevels returns a new map of LogComponents to LogLevels that is\n// the result of merging the user-defined data with the environment, with the\n// user-defined data taking priority.\nfunc selectComponentLevels(componentLevels map[Component]Level) map[Component]Level {\n\tselected := make(map[Component]Level)\n\n\t// Determine if the \"MONGODB_LOG_ALL\" environment variable is set.\n\tvar globalEnvLevel *Level\n\tif all := os.Getenv(mongoDBLogAllEnvVar); all != \"\" {\n\t\tlevel := ParseLevel(all)\n\t\tglobalEnvLevel = &level\n\t}\n\n\tfor envVar, component := range componentEnvVarMap {\n\t\t// If the component already has a level, then skip it.\n\t\tif _, ok := componentLevels[component]; ok {\n\t\t\tselected[component] = componentLevels[component]\n\n\t\t\tcontinue\n\t\t}\n\n\t\t// If the \"MONGODB_LOG_ALL\" environment variable is set, then\n\t\t// set the level for the component to the value of the\n\t\t// environment variable.\n\t\tif globalEnvLevel != nil {\n\t\t\tselected[component] = *globalEnvLevel\n\n\t\t\tcontinue\n\t\t}\n\n\t\t// Otherwise, set the level for the component to the value of\n\t\t// the environment variable.\n\t\tselected[component] = ParseLevel(os.Getenv(envVar))\n\t}\n\n\treturn selected\n}\n\n// FormatDocument formats a BSON document or RawValue for logging. The document is truncated\n// to the given width.\nfunc FormatDocument(msg bson.Raw, width uint) string {\n\tif len(msg) == 0 {\n\t\treturn \"{}\"\n\t}\n\n\tstr, truncated := bsoncore.Document(msg).StringN(int(width))\n\n\tif truncated {\n\t\tstr += TruncationSuffix\n\t}\n\n\treturn str\n}\n\n// FormatString formats a String for logging. The string is truncated\n// to the given width.\nfunc FormatString(str string, width uint) string {\n\tstrTrunc := bsoncoreutil.Truncate(str, int(width))\n\n\t// Checks if the string was truncating by comparing the lengths of the two strings.\n\tif len(strTrunc) < len(str) {\n\t\tstrTrunc += TruncationSuffix\n\t}\n\n\treturn strTrunc\n}\n"
  },
  {
    "path": "internal/logger/logger_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2023-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage logger\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"os\"\n\t\"reflect\"\n\t\"strings\"\n\t\"sync\"\n\t\"testing\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n)\n\ntype mockLogSink struct{}\n\nfunc (mockLogSink) Info(int, string, ...any)    {}\nfunc (mockLogSink) Error(error, string, ...any) {}\n\nfunc BenchmarkLoggerWithLargeDocuments(b *testing.B) {\n\t// Define the large document test cases\n\ttestCases := []struct {\n\t\tname   string\n\t\tcreate func() bson.D\n\t}{\n\t\t{\n\t\t\tname:   \"LargeStrings\",\n\t\t\tcreate: func() bson.D { return createLargeStringsDocument(10) },\n\t\t},\n\t\t{\n\t\t\tname:   \"MassiveArrays\",\n\t\t\tcreate: func() bson.D { return createMassiveArraysDocument(100000) },\n\t\t},\n\t\t{\n\t\t\tname:   \"VeryVoluminousDocument\",\n\t\t\tcreate: func() bson.D { return createVoluminousDocument(100000) },\n\t\t},\n\t}\n\n\tfor _, tc := range testCases {\n\t\ttc := tc\n\t\tb.Run(tc.name, func(b *testing.B) {\n\t\t\t// Run benchmark with logging and truncation enabled\n\t\t\tb.Run(\"LoggingWithTruncation\", func(b *testing.B) {\n\t\t\t\tlogger, err := New(mockLogSink{}, 0, map[Component]Level{\n\t\t\t\t\tComponentCommand: LevelDebug,\n\t\t\t\t})\n\t\t\t\tif err != nil {\n\t\t\t\t\tb.Fatal(err)\n\t\t\t\t}\n\t\t\t\tbs, err := bson.Marshal(tc.create())\n\t\t\t\tif err != nil {\n\t\t\t\t\tb.Fatal(err)\n\t\t\t\t}\n\t\t\t\tb.ResetTimer()\n\t\t\t\tfor i := 0; i < b.N; i++ {\n\t\t\t\t\tlogger.Print(LevelInfo, ComponentCommand, FormatDocument(bs, 1024), \"foo\", \"bar\", \"baz\")\n\t\t\t\t}\n\t\t\t})\n\n\t\t\t// Run benchmark with logging enabled without truncation\n\t\t\tb.Run(\"LoggingWithoutTruncation\", func(b *testing.B) {\n\t\t\t\tlogger, err := New(mockLogSink{}, 0, map[Component]Level{\n\t\t\t\t\tComponentCommand: LevelDebug,\n\t\t\t\t})\n\t\t\t\tif err != nil {\n\t\t\t\t\tb.Fatal(err)\n\t\t\t\t}\n\t\t\t\tbs, err := bson.Marshal(tc.create())\n\t\t\t\tif err != nil {\n\t\t\t\t\tb.Fatal(err)\n\t\t\t\t}\n\t\t\t\tb.ResetTimer()\n\t\t\t\tfor i := 0; i < b.N; i++ {\n\t\t\t\t\tmsg := bsoncore.Document(bs).String()\n\t\t\t\t\tlogger.Print(LevelInfo, ComponentCommand, msg, \"foo\", \"bar\", \"baz\")\n\n\t\t\t\t}\n\t\t\t})\n\n\t\t\t// Run benchmark without logging or truncation\n\t\t\tb.Run(\"WithoutLoggingOrTruncation\", func(b *testing.B) {\n\t\t\t\tbs, err := bson.Marshal(tc.create())\n\t\t\t\tif err != nil {\n\t\t\t\t\tb.Fatal(err)\n\t\t\t\t}\n\t\t\t\tb.ResetTimer()\n\t\t\t\tfor i := 0; i < b.N; i++ {\n\t\t\t\t\t_ = bsoncore.Document(bs).String()\n\t\t\t\t}\n\t\t\t})\n\t\t})\n\t}\n}\n\n// Helper functions to create large documents\nfunc createVoluminousDocument(numKeys int) bson.D {\n\td := make(bson.D, numKeys)\n\tfor i := 0; i < numKeys; i++ {\n\t\td = append(d, bson.E{Key: fmt.Sprintf(\"key%d\", i), Value: \"value\"})\n\t}\n\treturn d\n}\n\nfunc createLargeStringsDocument(sizeMB int) bson.D {\n\tlargeString := strings.Repeat(\"a\", sizeMB*1024*1024)\n\treturn bson.D{\n\t\t{Key: \"largeString1\", Value: largeString},\n\t\t{Key: \"largeString2\", Value: largeString},\n\t\t{Key: \"largeString3\", Value: largeString},\n\t\t{Key: \"largeString4\", Value: largeString},\n\t}\n}\n\nfunc createMassiveArraysDocument(arraySize int) bson.D {\n\tmassiveArray := make([]string, arraySize)\n\tfor i := 0; i < arraySize; i++ {\n\t\tmassiveArray[i] = \"value\"\n\t}\n\treturn bson.D{\n\t\t{Key: \"massiveArray1\", Value: massiveArray},\n\t\t{Key: \"massiveArray2\", Value: massiveArray},\n\t\t{Key: \"massiveArray3\", Value: massiveArray},\n\t\t{Key: \"massiveArray4\", Value: massiveArray},\n\t}\n}\n\nfunc BenchmarkLogger(b *testing.B) {\n\tb.ReportAllocs()\n\tb.ResetTimer()\n\n\tb.Run(\"Print\", func(b *testing.B) {\n\t\tb.ReportAllocs()\n\t\tb.ResetTimer()\n\n\t\tlogger, err := New(mockLogSink{}, 0, map[Component]Level{\n\t\t\tComponentCommand: LevelDebug,\n\t\t})\n\t\tif err != nil {\n\t\t\tb.Fatal(err)\n\t\t}\n\n\t\tb.RunParallel(func(pb *testing.PB) {\n\t\t\tfor pb.Next() {\n\t\t\t\tlogger.Print(LevelInfo, ComponentCommand, \"foo\", \"bar\", \"baz\")\n\t\t\t}\n\t\t})\n\t})\n}\n\nfunc mockKeyValues(length int) (KeyValues, map[string]any) {\n\tkeysAndValues := KeyValues{}\n\tm := map[string]any{}\n\n\tfor i := 0; i < length; i++ {\n\t\tkeyName := fmt.Sprintf(\"key%d\", i)\n\t\tvalueName := fmt.Sprintf(\"value%d\", i)\n\n\t\tkeysAndValues.Add(keyName, valueName)\n\t\tm[keyName] = valueName\n\t}\n\n\treturn keysAndValues, m\n}\n\nfunc BenchmarkIOSinkInfo(b *testing.B) {\n\tkeysAndValues, _ := mockKeyValues(10)\n\n\tb.ReportAllocs()\n\tb.ResetTimer()\n\n\tsink := NewIOSink(bytes.NewBuffer(nil))\n\n\tb.RunParallel(func(pb *testing.PB) {\n\t\tfor pb.Next() {\n\t\t\tsink.Info(0, \"foo\", keysAndValues...)\n\t\t}\n\t})\n}\n\nfunc TestIOSinkInfo(t *testing.T) {\n\tt.Parallel()\n\n\tconst threshold = 1000\n\n\tmockKeyValues, kvmap := mockKeyValues(10)\n\n\tbuf := new(bytes.Buffer)\n\tsink := NewIOSink(buf)\n\n\twg := sync.WaitGroup{}\n\twg.Add(threshold)\n\n\tfor i := 0; i < threshold; i++ {\n\t\tgo func() {\n\t\t\tdefer wg.Done()\n\n\t\t\tsink.Info(0, \"foo\", mockKeyValues...)\n\t\t}()\n\t}\n\n\twg.Wait()\n\n\tdec := json.NewDecoder(buf)\n\tfor dec.More() {\n\t\tvar m map[string]any\n\t\tif err := dec.Decode(&m); err != nil {\n\t\t\tt.Fatalf(\"error unmarshaling JSON: %v\", err)\n\t\t}\n\n\t\tdelete(m, KeyTimestamp)\n\t\tdelete(m, KeyMessage)\n\n\t\tif !reflect.DeepEqual(m, kvmap) {\n\t\t\tt.Fatalf(\"expected %v, got %v\", kvmap, m)\n\t\t}\n\t}\n}\n\nfunc TestSelectMaxDocumentLength(t *testing.T) {\n\tfor _, tcase := range []struct {\n\t\tname     string\n\t\targ      uint\n\t\texpected uint\n\t\tenv      map[string]string\n\t}{\n\t\t{\n\t\t\tname:     \"default\",\n\t\t\targ:      0,\n\t\t\texpected: DefaultMaxDocumentLength,\n\t\t},\n\t\t{\n\t\t\tname:     \"non-zero\",\n\t\t\targ:      100,\n\t\t\texpected: 100,\n\t\t},\n\t\t{\n\t\t\tname:     \"valid env\",\n\t\t\targ:      0,\n\t\t\texpected: 100,\n\t\t\tenv: map[string]string{\n\t\t\t\tmaxDocumentLengthEnvVar: \"100\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:     \"invalid env\",\n\t\t\targ:      0,\n\t\t\texpected: DefaultMaxDocumentLength,\n\t\t\tenv: map[string]string{\n\t\t\t\tmaxDocumentLengthEnvVar: \"foo\",\n\t\t\t},\n\t\t},\n\t} {\n\t\ttcase := tcase\n\n\t\tt.Run(tcase.name, func(t *testing.T) {\n\t\t\tfor k, v := range tcase.env {\n\t\t\t\tt.Setenv(k, v)\n\t\t\t}\n\n\t\t\tactual := selectMaxDocumentLength(tcase.arg)\n\t\t\tif actual != tcase.expected {\n\t\t\t\tt.Errorf(\"expected %d, got %d\", tcase.expected, actual)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestSelectLogSink(t *testing.T) {\n\tfor _, tcase := range []struct {\n\t\tname     string\n\t\targ      LogSink\n\t\texpected LogSink\n\t\tenv      map[string]string\n\t}{\n\t\t{\n\t\t\tname:     \"default\",\n\t\t\targ:      nil,\n\t\t\texpected: NewIOSink(os.Stderr),\n\t\t},\n\t\t{\n\t\t\tname:     \"non-nil\",\n\t\t\targ:      mockLogSink{},\n\t\t\texpected: mockLogSink{},\n\t\t},\n\t\t{\n\t\t\tname:     \"stdout\",\n\t\t\targ:      nil,\n\t\t\texpected: NewIOSink(os.Stdout),\n\t\t\tenv: map[string]string{\n\t\t\t\tlogSinkPathEnvVar: logSinkPathStdout,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:     \"stderr\",\n\t\t\targ:      nil,\n\t\t\texpected: NewIOSink(os.Stderr),\n\t\t\tenv: map[string]string{\n\t\t\t\tlogSinkPathEnvVar: logSinkPathStderr,\n\t\t\t},\n\t\t},\n\t} {\n\t\ttcase := tcase\n\n\t\tt.Run(tcase.name, func(t *testing.T) {\n\t\t\tfor k, v := range tcase.env {\n\t\t\t\tt.Setenv(k, v)\n\t\t\t}\n\n\t\t\tactual, _, _ := selectLogSink(tcase.arg)\n\t\t\tif !reflect.DeepEqual(actual, tcase.expected) {\n\t\t\t\tt.Errorf(\"expected %+v, got %+v\", tcase.expected, actual)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestSelectedComponentLevels(t *testing.T) {\n\tfor _, tcase := range []struct {\n\t\tname     string\n\t\targ      map[Component]Level\n\t\texpected map[Component]Level\n\t\tenv      map[string]string\n\t}{\n\t\t{\n\t\t\tname: \"default\",\n\t\t\targ:  nil,\n\t\t\texpected: map[Component]Level{\n\t\t\t\tComponentCommand:         LevelOff,\n\t\t\t\tComponentTopology:        LevelOff,\n\t\t\t\tComponentServerSelection: LevelOff,\n\t\t\t\tComponentConnection:      LevelOff,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"non-nil\",\n\t\t\targ: map[Component]Level{\n\t\t\t\tComponentCommand: LevelDebug,\n\t\t\t},\n\t\t\texpected: map[Component]Level{\n\t\t\t\tComponentCommand:         LevelDebug,\n\t\t\t\tComponentTopology:        LevelOff,\n\t\t\t\tComponentServerSelection: LevelOff,\n\t\t\t\tComponentConnection:      LevelOff,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"valid env\",\n\t\t\targ:  nil,\n\t\t\texpected: map[Component]Level{\n\t\t\t\tComponentCommand:         LevelDebug,\n\t\t\t\tComponentTopology:        LevelInfo,\n\t\t\t\tComponentServerSelection: LevelOff,\n\t\t\t\tComponentConnection:      LevelOff,\n\t\t\t},\n\t\t\tenv: map[string]string{\n\t\t\t\tmongoDBLogCommandEnvVar:  levelLiteralDebug,\n\t\t\t\tmongoDBLogTopologyEnvVar: levelLiteralInfo,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"invalid env\",\n\t\t\targ:  nil,\n\t\t\texpected: map[Component]Level{\n\t\t\t\tComponentCommand:         LevelOff,\n\t\t\t\tComponentTopology:        LevelOff,\n\t\t\t\tComponentServerSelection: LevelOff,\n\t\t\t\tComponentConnection:      LevelOff,\n\t\t\t},\n\t\t\tenv: map[string]string{\n\t\t\t\tmongoDBLogCommandEnvVar:  \"foo\",\n\t\t\t\tmongoDBLogTopologyEnvVar: \"bar\",\n\t\t\t},\n\t\t},\n\t} {\n\t\ttcase := tcase\n\n\t\tt.Run(tcase.name, func(t *testing.T) {\n\t\t\tfor k, v := range tcase.env {\n\t\t\t\tt.Setenv(k, v)\n\t\t\t}\n\n\t\t\tactual := selectComponentLevels(tcase.arg)\n\t\t\tfor k, v := range tcase.expected {\n\t\t\t\tif actual[k] != v {\n\t\t\t\t\tt.Errorf(\"expected %d, got %d\", v, actual[k])\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestLogger_LevelComponentEnabled(t *testing.T) {\n\tt.Parallel()\n\n\ttests := []struct {\n\t\tname      string\n\t\tlogger    Logger\n\t\tlevel     Level\n\t\tcomponent Component\n\t\twant      bool\n\t}{\n\t\t{\n\t\t\tname:      \"zero\",\n\t\t\tlogger:    Logger{},\n\t\t\tlevel:     LevelOff,\n\t\t\tcomponent: ComponentCommand,\n\t\t\twant:      false,\n\t\t},\n\t\t{\n\t\t\tname: \"empty\",\n\t\t\tlogger: Logger{\n\t\t\t\tComponentLevels: map[Component]Level{},\n\t\t\t},\n\t\t\tlevel:     LevelOff,\n\t\t\tcomponent: ComponentCommand,\n\t\t\twant:      false, // LevelOff should never be considered enabled.\n\t\t},\n\t\t{\n\t\t\tname: \"one level below\",\n\t\t\tlogger: Logger{\n\t\t\t\tComponentLevels: map[Component]Level{\n\t\t\t\t\tComponentCommand: LevelDebug,\n\t\t\t\t},\n\t\t\t},\n\t\t\tlevel:     LevelInfo,\n\t\t\tcomponent: ComponentCommand,\n\t\t\twant:      true,\n\t\t},\n\t\t{\n\t\t\tname: \"equal levels\",\n\t\t\tlogger: Logger{\n\t\t\t\tComponentLevels: map[Component]Level{\n\t\t\t\t\tComponentCommand: LevelDebug,\n\t\t\t\t},\n\t\t\t},\n\t\t\tlevel:     LevelDebug,\n\t\t\tcomponent: ComponentCommand,\n\t\t\twant:      true,\n\t\t},\n\t\t{\n\t\t\tname: \"one level above\",\n\t\t\tlogger: Logger{\n\t\t\t\tComponentLevels: map[Component]Level{\n\t\t\t\t\tComponentCommand: LevelInfo,\n\t\t\t\t},\n\t\t\t},\n\t\t\tlevel:     LevelDebug,\n\t\t\tcomponent: ComponentCommand,\n\t\t\twant:      false,\n\t\t},\n\t\t{\n\t\t\tname: \"component mismatch\",\n\t\t\tlogger: Logger{\n\t\t\t\tComponentLevels: map[Component]Level{\n\t\t\t\t\tComponentCommand: LevelDebug,\n\t\t\t\t},\n\t\t\t},\n\t\t\tlevel:     LevelDebug,\n\t\t\tcomponent: ComponentTopology,\n\t\t\twant:      false,\n\t\t},\n\t\t{\n\t\t\tname: \"component all enables with topology\",\n\t\t\tlogger: Logger{\n\t\t\t\tComponentLevels: map[Component]Level{\n\t\t\t\t\tComponentAll: LevelDebug,\n\t\t\t\t},\n\t\t\t},\n\t\t\tlevel:     LevelDebug,\n\t\t\tcomponent: ComponentTopology,\n\t\t\twant:      true,\n\t\t},\n\t\t{\n\t\t\tname: \"component all enables with server selection\",\n\t\t\tlogger: Logger{\n\t\t\t\tComponentLevels: map[Component]Level{\n\t\t\t\t\tComponentAll: LevelDebug,\n\t\t\t\t},\n\t\t\t},\n\t\t\tlevel:     LevelDebug,\n\t\t\tcomponent: ComponentServerSelection,\n\t\t\twant:      true,\n\t\t},\n\t\t{\n\t\t\tname: \"component all enables with connection\",\n\t\t\tlogger: Logger{\n\t\t\t\tComponentLevels: map[Component]Level{\n\t\t\t\t\tComponentAll: LevelDebug,\n\t\t\t\t},\n\t\t\t},\n\t\t\tlevel:     LevelDebug,\n\t\t\tcomponent: ComponentConnection,\n\t\t\twant:      true,\n\t\t},\n\t\t{\n\t\t\tname: \"component all enables with command\",\n\t\t\tlogger: Logger{\n\t\t\t\tComponentLevels: map[Component]Level{\n\t\t\t\t\tComponentAll: LevelDebug,\n\t\t\t\t},\n\t\t\t},\n\t\t\tlevel:     LevelDebug,\n\t\t\tcomponent: ComponentCommand,\n\t\t\twant:      true,\n\t\t},\n\t\t{\n\t\t\tname: \"component all enables with all\",\n\t\t\tlogger: Logger{\n\t\t\t\tComponentLevels: map[Component]Level{\n\t\t\t\t\tComponentAll: LevelDebug,\n\t\t\t\t},\n\t\t\t},\n\t\t\tlevel:     LevelDebug,\n\t\t\tcomponent: ComponentAll,\n\t\t\twant:      true,\n\t\t},\n\t\t{\n\t\t\tname: \"component all does not enable with lower level\",\n\t\t\tlogger: Logger{\n\t\t\t\tComponentLevels: map[Component]Level{\n\t\t\t\t\tComponentAll: LevelInfo,\n\t\t\t\t},\n\t\t\t},\n\t\t\tlevel:     LevelDebug,\n\t\t\tcomponent: ComponentCommand,\n\t\t\twant:      false,\n\t\t},\n\t\t{\n\t\t\tname: \"component all has a lower log level than command\",\n\t\t\tlogger: Logger{\n\t\t\t\tComponentLevels: map[Component]Level{\n\t\t\t\t\tComponentAll:     LevelInfo,\n\t\t\t\t\tComponentCommand: LevelDebug,\n\t\t\t\t},\n\t\t\t},\n\t\t\tlevel:     LevelDebug,\n\t\t\tcomponent: ComponentCommand,\n\t\t\twant:      true,\n\t\t},\n\t\t{\n\t\t\tname: \"component all has a higher log level than command\",\n\t\t\tlogger: Logger{\n\t\t\t\tComponentLevels: map[Component]Level{\n\t\t\t\t\tComponentAll:     LevelDebug,\n\t\t\t\t\tComponentCommand: LevelInfo,\n\t\t\t\t},\n\t\t\t},\n\t\t\tlevel:     LevelDebug,\n\t\t\tcomponent: ComponentCommand,\n\t\t\twant:      true,\n\t\t},\n\t}\n\n\tfor _, tcase := range tests {\n\t\ttcase := tcase // Capture the range variable.\n\n\t\tt.Run(tcase.name, func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tgot := tcase.logger.LevelComponentEnabled(tcase.level, tcase.component)\n\t\t\tassert.Equal(t, tcase.want, got, \"unexpected result for LevelComponentEnabled\")\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "internal/mongoutil/mongoutil.go",
    "content": "// Copyright (C) MongoDB, Inc. 2024-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage mongoutil\n\nimport (\n\t\"context\"\n\t\"reflect\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n)\n\n// NewOptions will functionally merge a slice of mongo.Options in a\n// \"last-one-wins\" manner, where nil options are ignored.\nfunc NewOptions[T any](opts ...options.Lister[T]) (*T, error) {\n\targs := new(T)\n\tfor _, opt := range opts {\n\t\tif opt == nil || reflect.ValueOf(opt).IsNil() {\n\t\t\t// Do nothing if the option is nil or if opt is nil but implicitly cast as\n\t\t\t// an Options interface by the NewArgsFromOptions function. The latter\n\t\t\t// case would look something like this:\n\t\t\tcontinue\n\t\t}\n\n\t\tfor _, setArgs := range opt.List() {\n\t\t\tif setArgs == nil {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tif err := setArgs(args); err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t}\n\t}\n\treturn args, nil\n}\n\n// OptionsLister implements an options.SetterLister object for an arbitrary\n// options type.\ntype OptionsLister[T any] struct {\n\tOptions  *T             // Arguments to set on the option type\n\tCallback func(*T) error // A callback for further modification\n}\n\n// List will re-assign the entire argument option to the Args field\n// defined on opts. If a callback exists, that function will be executed to\n// further modify the arguments.\nfunc (opts *OptionsLister[T]) List() []func(*T) error {\n\treturn []func(*T) error{\n\t\tfunc(args *T) error {\n\t\t\tif opts.Options != nil {\n\t\t\t\t*args = *opts.Options\n\t\t\t}\n\n\t\t\tif opts.Callback != nil {\n\t\t\t\treturn opts.Callback(args)\n\t\t\t}\n\n\t\t\treturn nil\n\t\t},\n\t}\n}\n\n// NewOptionsLister will construct a SetterLister from the provided Options\n// object.\nfunc NewOptionsLister[T any](args *T, callback func(*T) error) *OptionsLister[T] {\n\treturn &OptionsLister[T]{Options: args, Callback: callback}\n}\n\n// AuthFromURI will create a Credentials object given the provided URI.\nfunc AuthFromURI(uri string) (*options.Credential, error) {\n\topts := options.Client().ApplyURI(uri)\n\n\treturn opts.Auth, nil\n}\n\n// HostsFromURI will parse the hosts in the URI and return them as a slice of\n// strings.\nfunc HostsFromURI(uri string) ([]string, error) {\n\topts := options.Client().ApplyURI(uri)\n\n\treturn opts.Hosts, nil\n}\n\n// TimeoutWithinContext will return true if the provided timeout is nil or if\n// it is less than the context deadline. If the context does not have a\n// deadline, it will return true.\nfunc TimeoutWithinContext(ctx context.Context, timeout time.Duration) bool {\n\tdeadline, ok := ctx.Deadline()\n\tif !ok {\n\t\treturn true\n\t}\n\n\tctxTimeout := time.Until(deadline)\n\n\treturn ctxTimeout <= 0 || timeout < ctxTimeout\n}\n"
  },
  {
    "path": "internal/mongoutil/mongoutil_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2024-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage mongoutil\n\nimport (\n\t\"context\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/ptrutil\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n)\n\nfunc BenchmarkNewOptions(b *testing.B) {\n\tb.Run(\"reflect.ValueOf is always called\", func(b *testing.B) {\n\t\topts := make([]options.Lister[options.FindOptions], b.N)\n\n\t\t// Create a huge string to see if we can force reflect.ValueOf to use heap\n\t\t// over stack.\n\t\tsize := 16 * 1024 * 1024\n\t\tstr := strings.Repeat(\"a\", size)\n\n\t\tfor i := 0; i < b.N; i++ {\n\t\t\topts[i] = options.Find().SetComment(str).SetHint(\"y\").SetMin(1).SetMax(2)\n\t\t}\n\n\t\tb.ReportAllocs()\n\t\tfor i := 0; i < b.N; i++ {\n\t\t\t_, _ = NewOptions[options.FindOptions](opts...)\n\t\t}\n\t})\n}\n\nfunc TestValidChangeStreamTimeouts(t *testing.T) {\n\ttests := []struct {\n\t\tname        string\n\t\tctxTimeout  *time.Duration\n\t\ttimeout     time.Duration\n\t\twantTimeout time.Duration\n\t\twant        bool\n\t}{\n\t\t{\n\t\t\tname:       \"Timeout shorter than context deadline\",\n\t\t\tctxTimeout: ptrutil.Ptr(10 * time.Second),\n\t\t\ttimeout:    1 * time.Second,\n\t\t\twant:       true,\n\t\t},\n\t\t{\n\t\t\tname:       \"Timeout equal to context deadline\",\n\t\t\tctxTimeout: ptrutil.Ptr(1 * time.Second),\n\t\t\ttimeout:    1 * time.Second,\n\t\t\twant:       false,\n\t\t},\n\t\t{\n\t\t\tname:       \"Timeout greater than context deadline\",\n\t\t\tctxTimeout: ptrutil.Ptr(1 * time.Second),\n\t\t\ttimeout:    10 * time.Second,\n\t\t\twant:       false,\n\t\t},\n\t\t{\n\t\t\tname:       \"Context deadline already expired\",\n\t\t\tctxTimeout: ptrutil.Ptr(-1 * time.Second),\n\t\t\ttimeout:    1 * time.Second,\n\t\t\twant:       true, // *timeout <= 0 branch in code\n\t\t},\n\t\t{\n\t\t\tname:       \"Timeout is zero, context deadline in future\",\n\t\t\tctxTimeout: ptrutil.Ptr(10 * time.Second),\n\t\t\ttimeout:    0 * time.Second,\n\t\t\twant:       true,\n\t\t},\n\t\t{\n\t\t\tname:       \"Timeout is negative, context deadline in future\",\n\t\t\tctxTimeout: ptrutil.Ptr(10 * time.Second),\n\t\t\ttimeout:    -1 * time.Second,\n\t\t\twant:       true,\n\t\t},\n\t\t{\n\t\t\tname:       \"Timeout provided, context has no deadline\",\n\t\t\tctxTimeout: nil,\n\t\t\ttimeout:    1 * time.Second,\n\t\t\twant:       true,\n\t\t},\n\t}\n\n\tfor _, test := range tests {\n\t\tt.Run(test.name, func(t *testing.T) {\n\t\t\tctx := context.Background()\n\t\t\tif test.ctxTimeout != nil {\n\t\t\t\tvar cancel context.CancelFunc\n\n\t\t\t\tctx, cancel = context.WithTimeout(ctx, *test.ctxTimeout)\n\t\t\t\tdefer cancel()\n\t\t\t}\n\n\t\t\tgot := TimeoutWithinContext(ctx, test.timeout)\n\t\t\tassert.Equal(t, test.want, got)\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "internal/optionsutil/options.go",
    "content": "// Copyright (C) MongoDB, Inc. 2025-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage optionsutil\n\n// Options stores internal options.\ntype Options struct {\n\tvalues map[string]any\n}\n\n// WithValue sets an option value with the associated key.\nfunc WithValue(opts Options, key string, option any) Options {\n\tif opts.values == nil {\n\t\topts.values = make(map[string]any)\n\t}\n\topts.values[key] = option\n\treturn opts\n}\n\n// Value returns the value associated with the options for key.\nfunc Value(opts Options, key string) any {\n\tif opts.values == nil {\n\t\treturn nil\n\t}\n\tif val, ok := opts.values[key]; ok {\n\t\treturn val\n\t}\n\treturn nil\n}\n\n// Equal compares two Options instances for equality.\nfunc Equal(opts1, opts2 Options) bool {\n\tif len(opts1.values) != len(opts2.values) {\n\t\treturn false\n\t}\n\tfor key, val1 := range opts1.values {\n\t\tif val2, ok := opts2.values[key]; !ok || val1 != val2 {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n"
  },
  {
    "path": "internal/ptrutil/int64.go",
    "content": "// Copyright (C) MongoDB, Inc. 2023-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage ptrutil\n\n// CompareInt64 is a piecewise function with the following return conditions:\n//\n// (1)  2, ptr1 != nil AND ptr2 == nil\n// (2)  1, *ptr1 > *ptr2\n// (3)  0, ptr1 == ptr2 or *ptr1 == *ptr2\n// (4) -1, *ptr1 < *ptr2\n// (5) -2, ptr1 == nil AND ptr2 != nil\nfunc CompareInt64(ptr1, ptr2 *int64) int {\n\tif ptr1 == ptr2 {\n\t\t// This will catch the double nil or same-pointer cases.\n\t\treturn 0\n\t}\n\n\tif ptr1 == nil && ptr2 != nil {\n\t\treturn -2\n\t}\n\n\tif ptr1 != nil && ptr2 == nil {\n\t\treturn 2\n\t}\n\n\tif *ptr1 > *ptr2 {\n\t\treturn 1\n\t}\n\n\tif *ptr1 < *ptr2 {\n\t\treturn -1\n\t}\n\n\treturn 0\n}\n"
  },
  {
    "path": "internal/ptrutil/int64_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2023-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage ptrutil\n\nimport (\n\t\"testing\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n)\n\nfunc TestCompareInt64(t *testing.T) {\n\tt.Parallel()\n\n\tint64ToPtr := func(i64 int64) *int64 { return &i64 }\n\tint64Ptr := int64ToPtr(1)\n\n\ttests := []struct {\n\t\tname       string\n\t\tptr1, ptr2 *int64\n\t\twant       int\n\t}{\n\t\t{\n\t\t\tname: \"empty\",\n\t\t\twant: 0,\n\t\t},\n\t\t{\n\t\t\tname: \"ptr1 nil\",\n\t\t\tptr2: int64ToPtr(1),\n\t\t\twant: -2,\n\t\t},\n\t\t{\n\t\t\tname: \"ptr2 nil\",\n\t\t\tptr1: int64ToPtr(1),\n\t\t\twant: 2,\n\t\t},\n\t\t{\n\t\t\tname: \"ptr1 and ptr2 have same value, different address\",\n\t\t\tptr1: int64ToPtr(1),\n\t\t\tptr2: int64ToPtr(1),\n\t\t\twant: 0,\n\t\t},\n\t\t{\n\t\t\tname: \"ptr1 and ptr2 have the same address\",\n\t\t\tptr1: int64Ptr,\n\t\t\tptr2: int64Ptr,\n\t\t\twant: 0,\n\t\t},\n\t\t{\n\t\t\tname: \"ptr1 GT ptr2\",\n\t\t\tptr1: int64ToPtr(1),\n\t\t\tptr2: int64ToPtr(0),\n\t\t\twant: 1,\n\t\t},\n\t\t{\n\t\t\tname: \"ptr1 LT ptr2\",\n\t\t\tptr1: int64ToPtr(0),\n\t\t\tptr2: int64ToPtr(1),\n\t\t\twant: -1,\n\t\t},\n\t}\n\n\tfor _, test := range tests {\n\t\ttest := test // capture the range variable\n\n\t\tt.Run(test.name, func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tgot := CompareInt64(test.ptr1, test.ptr2)\n\t\t\tassert.Equal(t, test.want, got, \"compareInt64() = %v, wanted %v\", got, test.want)\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "internal/ptrutil/ptr.go",
    "content": "// Copyright (C) MongoDB, Inc. 2024-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage ptrutil\n\n// Ptr will return the memory location of the given value.\nfunc Ptr[T any](val T) *T {\n\treturn &val\n}\n"
  },
  {
    "path": "internal/rand/arith128_test.go",
    "content": "// Copied from https://cs.opensource.google/go/x/exp/+/24438e51023af3bfc1db8aed43c1342817e8cfcd:rand/arith128_test.go\n\n// Copyright 2017 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage rand\n\nimport (\n\t\"math/big\"\n\t\"math/rand\"\n\t\"testing\"\n)\n\nvar bigMaxUint64 = big.NewInt(0).SetUint64(maxUint64)\n\nfunc bigInt(xHi, xLo uint64) *big.Int {\n\tb := big.NewInt(0).SetUint64(xHi)\n\tb.Lsh(b, 64)\n\tb.Or(b, big.NewInt(0).SetUint64(xLo))\n\treturn b\n}\n\nfunc splitBigInt(b *big.Int) (outHi, outLo uint64) {\n\toutHi = big.NewInt(0).Rsh(b, 64).Uint64()\n\toutLo = big.NewInt(0).And(b, bigMaxUint64).Uint64()\n\treturn\n}\n\nfunc bigMulMod128bits(xHi, xLo, yHi, yLo uint64) (outHi, outLo uint64) {\n\tbigX := bigInt(xHi, xLo)\n\tbigY := bigInt(yHi, yLo)\n\treturn splitBigInt(bigX.Mul(bigX, bigY))\n}\n\nfunc bigAddMod128bits(xHi, xLo, yHi, yLo uint64) (outHi, outLo uint64) {\n\tbigX := bigInt(xHi, xLo)\n\tbigY := bigInt(yHi, yLo)\n\treturn splitBigInt(bigX.Add(bigX, bigY))\n}\n\ntype arithTest struct {\n\txHi, xLo uint64\n}\n\nconst (\n\tiLo = increment & maxUint64\n\tiHi = (increment >> 64) & maxUint64\n)\n\nvar arithTests = []arithTest{\n\t{0, 0},\n\t{0, 1},\n\t{1, 0},\n\t{0, maxUint64},\n\t{maxUint64, 0},\n\t{maxUint64, maxUint64},\n\t// Randomly generated 64-bit integers.\n\t{3757956613005209672, 17983933746665545631},\n\t{511324141977587414, 5626651684620191081},\n\t{1534313104606153588, 2415006486399353367},\n\t{6873586429837825902, 13854394671140464137},\n\t{6617134480561088940, 18421520694158684312},\n}\n\nfunc TestPCGAdd(t *testing.T) {\n\tfor i, test := range arithTests {\n\t\tp := &PCGSource{\n\t\t\tlow:  test.xLo,\n\t\t\thigh: test.xHi,\n\t\t}\n\t\tp.add()\n\t\texpectHi, expectLo := bigAddMod128bits(test.xHi, test.xLo, iHi, iLo)\n\t\tif p.low != expectLo || p.high != expectHi {\n\t\t\tt.Errorf(\"%d: got hi=%d lo=%d; expect hi=%d lo=%d\", i, p.high, p.low, expectHi, expectLo)\n\t\t}\n\t}\n}\n\nconst (\n\tmLo = multiplier & maxUint64\n\tmHi = (multiplier >> 64) & maxUint64\n)\n\nfunc TestPCGMultiply(t *testing.T) {\n\tfor i, test := range arithTests {\n\t\tp := &PCGSource{\n\t\t\tlow:  test.xLo,\n\t\t\thigh: test.xHi,\n\t\t}\n\t\tp.multiply()\n\t\texpectHi, expectLo := bigMulMod128bits(test.xHi, test.xLo, mHi, mLo)\n\t\tif p.low != expectLo || p.high != expectHi {\n\t\t\tt.Errorf(\"%d: got hi=%d lo=%d; expect hi=%d lo=%d\", i, p.high, p.low, expectHi, expectLo)\n\t\t}\n\t}\n}\n\nfunc TestPCGMultiplyLong(t *testing.T) {\n\tif testing.Short() {\n\t\treturn\n\t}\n\tfor i := 0; i < 1e6; i++ {\n\t\tlow := rand.Uint64()\n\t\thigh := rand.Uint64()\n\t\tp := &PCGSource{\n\t\t\tlow:  low,\n\t\t\thigh: high,\n\t\t}\n\t\tp.multiply()\n\t\texpectHi, expectLo := bigMulMod128bits(high, low, mHi, mLo)\n\t\tif p.low != expectLo || p.high != expectHi {\n\t\t\tt.Fatalf(\"%d: (%d,%d): got hi=%d lo=%d; expect hi=%d lo=%d\", i, high, low, p.high, p.low, expectHi, expectLo)\n\t\t}\n\t}\n}\n\nfunc BenchmarkPCGMultiply(b *testing.B) {\n\tlow := rand.Uint64()\n\thigh := rand.Uint64()\n\tp := &PCGSource{\n\t\tlow:  low,\n\t\thigh: high,\n\t}\n\tfor i := 0; i < b.N; i++ {\n\t\tp.multiply()\n\t}\n}\n"
  },
  {
    "path": "internal/rand/bits.go",
    "content": "// Copied from https://cs.opensource.google/go/go/+/946b4baaf6521d521928500b2b57429c149854e7:src/math/bits.go\n\n// Copyright 2017 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage rand\n\n// Add64 returns the sum with carry of x, y and carry: sum = x + y + carry.\n// The carry input must be 0 or 1; otherwise the behavior is undefined.\n// The carryOut output is guaranteed to be 0 or 1.\nfunc Add64(x, y, carry uint64) (sum, carryOut uint64) {\n\tyc := y + carry\n\tsum = x + yc\n\tif sum < x || yc < y {\n\t\tcarryOut = 1\n\t}\n\treturn\n}\n\n// Mul64 returns the 128-bit product of x and y: (hi, lo) = x * y\n// with the product bits' upper half returned in hi and the lower\n// half returned in lo.\nfunc Mul64(x, y uint64) (hi, lo uint64) {\n\tconst mask32 = 1<<32 - 1\n\tx0 := x & mask32\n\tx1 := x >> 32\n\ty0 := y & mask32\n\ty1 := y >> 32\n\tw0 := x0 * y0\n\tt := x1*y0 + w0>>32\n\tw1 := t & mask32\n\tw2 := t >> 32\n\tw1 += x0 * y1\n\thi = x1*y1 + w2 + w1>>32\n\tlo = x * y\n\treturn\n}\n"
  },
  {
    "path": "internal/rand/example_test.go",
    "content": "// Copied from https://cs.opensource.google/go/x/exp/+/24438e51023af3bfc1db8aed43c1342817e8cfcd:rand/example_test.go\n\n// Copyright 2012 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage rand_test\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"strings\"\n\t\"text/tabwriter\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/rand\"\n)\n\n// These tests serve as an example but also make sure we don't change\n// the output of the random number generator when given a fixed seed.\n\nfunc Example() {\n\trand.Seed(42) // Try changing this number!\n\tanswers := []string{\n\t\t\"It is certain\",\n\t\t\"It is decidedly so\",\n\t\t\"Without a doubt\",\n\t\t\"Yes definitely\",\n\t\t\"You may rely on it\",\n\t\t\"As I see it yes\",\n\t\t\"Most likely\",\n\t\t\"Outlook good\",\n\t\t\"Yes\",\n\t\t\"Signs point to yes\",\n\t\t\"Reply hazy try again\",\n\t\t\"Ask again later\",\n\t\t\"Better not tell you now\",\n\t\t\"Cannot predict now\",\n\t\t\"Concentrate and ask again\",\n\t\t\"Don't count on it\",\n\t\t\"My reply is no\",\n\t\t\"My sources say no\",\n\t\t\"Outlook not so good\",\n\t\t\"Very doubtful\",\n\t}\n\tfmt.Println(\"Magic 8-Ball says:\", answers[rand.Intn(len(answers))])\n\t// Output: Magic 8-Ball says: Most likely\n}\n\n// This example shows the use of each of the methods on a *Rand.\n// The use of the global functions is the same, without the receiver.\nfunc Example_rand() {\n\t// Create and seed the generator.\n\t// Typically a non-fixed seed should be used, such as time.Now().UnixNano().\n\t// Using a fixed seed will produce the same output on every run.\n\tr := rand.New(rand.NewSource(1234))\n\n\t// The tabwriter here helps us generate aligned output.\n\tw := tabwriter.NewWriter(os.Stdout, 1, 1, 1, ' ', 0)\n\tdefer w.Flush()\n\tshow := func(name string, v1, v2, v3 interface{}) {\n\t\tfmt.Fprintf(w, \"%s\\t%v\\t%v\\t%v\\n\", name, v1, v2, v3)\n\t}\n\n\t// Float32 and Float64 values are in [0, 1).\n\tshow(\"Float32\", r.Float32(), r.Float32(), r.Float32())\n\tshow(\"Float64\", r.Float64(), r.Float64(), r.Float64())\n\n\t// ExpFloat64 values have an average of 1 but decay exponentially.\n\tshow(\"ExpFloat64\", r.ExpFloat64(), r.ExpFloat64(), r.ExpFloat64())\n\n\t// NormFloat64 values have an average of 0 and a standard deviation of 1.\n\tshow(\"NormFloat64\", r.NormFloat64(), r.NormFloat64(), r.NormFloat64())\n\n\t// Int31, Int63, and Uint32 generate values of the given width.\n\t// The Int method (not shown) is like either Int31 or Int63\n\t// depending on the size of 'int'.\n\tshow(\"Int31\", r.Int31(), r.Int31(), r.Int31())\n\tshow(\"Int63\", r.Int63(), r.Int63(), r.Int63())\n\tshow(\"Uint32\", r.Uint32(), r.Uint32(), r.Uint32())\n\tshow(\"Uint64\", r.Uint64(), r.Uint64(), r.Uint64())\n\n\t// Intn, Int31n, Int63n and Uint64n limit their output to be < n.\n\t// They do so more carefully than using r.Int()%n.\n\tshow(\"Intn(10)\", r.Intn(10), r.Intn(10), r.Intn(10))\n\tshow(\"Int31n(10)\", r.Int31n(10), r.Int31n(10), r.Int31n(10))\n\tshow(\"Int63n(10)\", r.Int63n(10), r.Int63n(10), r.Int63n(10))\n\tshow(\"Uint64n(10)\", r.Uint64n(10), r.Uint64n(10), r.Uint64n(10))\n\n\t// Perm generates a random permutation of the numbers [0, n).\n\tshow(\"Perm\", r.Perm(5), r.Perm(5), r.Perm(5))\n\t// Output:\n\t// Float32     0.030719291          0.47512934           0.031019364\n\t// Float64     0.6906635660087743   0.9898818576905045   0.2683634639782333\n\t// ExpFloat64  1.24979080914592     0.3451975160045876   0.5456817760595064\n\t// NormFloat64 0.879221333732727    -0.01508980368383761 -1.962250558270421\n\t// Int31       2043816560           1870670250           1334960143\n\t// Int63       7860766611810691572  1466711535823962239  3836585920276818709\n\t// Uint32      2051241581           751073909            1353986074\n\t// Uint64      10802154207635843641 14398820303406316826 11052107950969057042\n\t// Intn(10)    3                    0                    1\n\t// Int31n(10)  3                    8                    1\n\t// Int63n(10)  4                    6                    0\n\t// Uint64n(10) 2                    9                    4\n\t// Perm        [1 3 4 0 2]          [2 4 0 3 1]          [3 2 0 4 1]\n}\n\nfunc ExampleShuffle() {\n\twords := strings.Fields(\"ink runs from the corners of my mouth\")\n\trand.Shuffle(len(words), func(i, j int) {\n\t\twords[i], words[j] = words[j], words[i]\n\t})\n\tfmt.Println(words)\n\n\t// Output:\n\t// [ink corners of from mouth runs the my]\n}\n\nfunc ExampleShuffle_slicesInUnison() {\n\tnumbers := []byte(\"12345\")\n\tletters := []byte(\"ABCDE\")\n\t// Shuffle numbers, swapping corresponding entries in letters at the same time.\n\trand.Shuffle(len(numbers), func(i, j int) {\n\t\tnumbers[i], numbers[j] = numbers[j], numbers[i]\n\t\tletters[i], letters[j] = letters[j], letters[i]\n\t})\n\tfor i := range numbers {\n\t\tfmt.Printf(\"%c: %c\\n\", letters[i], numbers[i])\n\t}\n\n\t// Output:\n\t// D: 4\n\t// A: 1\n\t// E: 5\n\t// B: 2\n\t// C: 3\n}\n\nfunc ExampleLockedSource() {\n\tr := rand.New(new(rand.LockedSource))\n\tr.Seed(42) // Try changing this number!\n\tanswers := []string{\n\t\t\"It is certain\",\n\t\t\"It is decidedly so\",\n\t\t\"Without a doubt\",\n\t\t\"Yes definitely\",\n\t\t\"You may rely on it\",\n\t\t\"As I see it yes\",\n\t\t\"Most likely\",\n\t\t\"Outlook good\",\n\t\t\"Yes\",\n\t\t\"Signs point to yes\",\n\t\t\"Reply hazy try again\",\n\t\t\"Ask again later\",\n\t\t\"Better not tell you now\",\n\t\t\"Cannot predict now\",\n\t\t\"Concentrate and ask again\",\n\t\t\"Don't count on it\",\n\t\t\"My reply is no\",\n\t\t\"My sources say no\",\n\t\t\"Outlook not so good\",\n\t\t\"Very doubtful\",\n\t}\n\tfmt.Println(\"Magic 8-Ball says:\", answers[r.Intn(len(answers))])\n\t// Output: Magic 8-Ball says: Most likely\n}\n"
  },
  {
    "path": "internal/rand/exp.go",
    "content": "// Copied from https://cs.opensource.google/go/x/exp/+/24438e51023af3bfc1db8aed43c1342817e8cfcd:rand/exp.go\n\n// Copyright 2009 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage rand\n\nimport (\n\t\"math\"\n)\n\n/*\n * Exponential distribution\n *\n * See \"The Ziggurat Method for Generating Random Variables\"\n * (Marsaglia & Tsang, 2000)\n * http://www.jstatsoft.org/v05/i08/paper [pdf]\n */\n\nconst (\n\tre = 7.69711747013104972\n)\n\n// ExpFloat64 returns an exponentially distributed float64 in the range\n// (0, +math.MaxFloat64] with an exponential distribution whose rate parameter\n// (lambda) is 1 and whose mean is 1/lambda (1).\n// To produce a distribution with a different rate parameter,\n// callers can adjust the output using:\n//\n//\tsample = ExpFloat64() / desiredRateParameter\nfunc (r *Rand) ExpFloat64() float64 {\n\tfor {\n\t\tj := r.Uint32()\n\t\ti := j & 0xFF\n\t\tx := float64(j) * float64(we[i])\n\t\tif j < ke[i] {\n\t\t\treturn x\n\t\t}\n\t\tif i == 0 {\n\t\t\treturn re - math.Log(r.Float64())\n\t\t}\n\t\tif fe[i]+float32(r.Float64())*(fe[i-1]-fe[i]) < float32(math.Exp(-x)) {\n\t\t\treturn x\n\t\t}\n\t}\n}\n\nvar ke = [256]uint32{\n\t0xe290a139, 0x0, 0x9beadebc, 0xc377ac71, 0xd4ddb990,\n\t0xde893fb8, 0xe4a8e87c, 0xe8dff16a, 0xebf2deab, 0xee49a6e8,\n\t0xf0204efd, 0xf19bdb8e, 0xf2d458bb, 0xf3da104b, 0xf4b86d78,\n\t0xf577ad8a, 0xf61de83d, 0xf6afb784, 0xf730a573, 0xf7a37651,\n\t0xf80a5bb6, 0xf867189d, 0xf8bb1b4f, 0xf9079062, 0xf94d70ca,\n\t0xf98d8c7d, 0xf9c8928a, 0xf9ff175b, 0xfa319996, 0xfa6085f8,\n\t0xfa8c3a62, 0xfab5084e, 0xfadb36c8, 0xfaff0410, 0xfb20a6ea,\n\t0xfb404fb4, 0xfb5e2951, 0xfb7a59e9, 0xfb95038c, 0xfbae44ba,\n\t0xfbc638d8, 0xfbdcf892, 0xfbf29a30, 0xfc0731df, 0xfc1ad1ed,\n\t0xfc2d8b02, 0xfc3f6c4d, 0xfc5083ac, 0xfc60ddd1, 0xfc708662,\n\t0xfc7f8810, 0xfc8decb4, 0xfc9bbd62, 0xfca9027c, 0xfcb5c3c3,\n\t0xfcc20864, 0xfccdd70a, 0xfcd935e3, 0xfce42ab0, 0xfceebace,\n\t0xfcf8eb3b, 0xfd02c0a0, 0xfd0c3f59, 0xfd156b7b, 0xfd1e48d6,\n\t0xfd26daff, 0xfd2f2552, 0xfd372af7, 0xfd3eeee5, 0xfd4673e7,\n\t0xfd4dbc9e, 0xfd54cb85, 0xfd5ba2f2, 0xfd62451b, 0xfd68b415,\n\t0xfd6ef1da, 0xfd750047, 0xfd7ae120, 0xfd809612, 0xfd8620b4,\n\t0xfd8b8285, 0xfd90bcf5, 0xfd95d15e, 0xfd9ac10b, 0xfd9f8d36,\n\t0xfda43708, 0xfda8bf9e, 0xfdad2806, 0xfdb17141, 0xfdb59c46,\n\t0xfdb9a9fd, 0xfdbd9b46, 0xfdc170f6, 0xfdc52bd8, 0xfdc8ccac,\n\t0xfdcc542d, 0xfdcfc30b, 0xfdd319ef, 0xfdd6597a, 0xfdd98245,\n\t0xfddc94e5, 0xfddf91e6, 0xfde279ce, 0xfde54d1f, 0xfde80c52,\n\t0xfdeab7de, 0xfded5034, 0xfdefd5be, 0xfdf248e3, 0xfdf4aa06,\n\t0xfdf6f984, 0xfdf937b6, 0xfdfb64f4, 0xfdfd818d, 0xfdff8dd0,\n\t0xfe018a08, 0xfe03767a, 0xfe05536c, 0xfe07211c, 0xfe08dfc9,\n\t0xfe0a8fab, 0xfe0c30fb, 0xfe0dc3ec, 0xfe0f48b1, 0xfe10bf76,\n\t0xfe122869, 0xfe1383b4, 0xfe14d17c, 0xfe1611e7, 0xfe174516,\n\t0xfe186b2a, 0xfe19843e, 0xfe1a9070, 0xfe1b8fd6, 0xfe1c8289,\n\t0xfe1d689b, 0xfe1e4220, 0xfe1f0f26, 0xfe1fcfbc, 0xfe2083ed,\n\t0xfe212bc3, 0xfe21c745, 0xfe225678, 0xfe22d95f, 0xfe234ffb,\n\t0xfe23ba4a, 0xfe241849, 0xfe2469f2, 0xfe24af3c, 0xfe24e81e,\n\t0xfe25148b, 0xfe253474, 0xfe2547c7, 0xfe254e70, 0xfe25485a,\n\t0xfe25356a, 0xfe251586, 0xfe24e88f, 0xfe24ae64, 0xfe2466e1,\n\t0xfe2411df, 0xfe23af34, 0xfe233eb4, 0xfe22c02c, 0xfe22336b,\n\t0xfe219838, 0xfe20ee58, 0xfe20358c, 0xfe1f6d92, 0xfe1e9621,\n\t0xfe1daef0, 0xfe1cb7ac, 0xfe1bb002, 0xfe1a9798, 0xfe196e0d,\n\t0xfe1832fd, 0xfe16e5fe, 0xfe15869d, 0xfe141464, 0xfe128ed3,\n\t0xfe10f565, 0xfe0f478c, 0xfe0d84b1, 0xfe0bac36, 0xfe09bd73,\n\t0xfe07b7b5, 0xfe059a40, 0xfe03644c, 0xfe011504, 0xfdfeab88,\n\t0xfdfc26e9, 0xfdf98629, 0xfdf6c83b, 0xfdf3ec01, 0xfdf0f04a,\n\t0xfdedd3d1, 0xfdea953d, 0xfde7331e, 0xfde3abe9, 0xfddffdfb,\n\t0xfddc2791, 0xfdd826cd, 0xfdd3f9a8, 0xfdcf9dfc, 0xfdcb1176,\n\t0xfdc65198, 0xfdc15bb3, 0xfdbc2ce2, 0xfdb6c206, 0xfdb117be,\n\t0xfdab2a63, 0xfda4f5fd, 0xfd9e7640, 0xfd97a67a, 0xfd908192,\n\t0xfd8901f2, 0xfd812182, 0xfd78d98e, 0xfd7022bb, 0xfd66f4ed,\n\t0xfd5d4732, 0xfd530f9c, 0xfd48432b, 0xfd3cd59a, 0xfd30b936,\n\t0xfd23dea4, 0xfd16349e, 0xfd07a7a3, 0xfcf8219b, 0xfce7895b,\n\t0xfcd5c220, 0xfcc2aadb, 0xfcae1d5e, 0xfc97ed4e, 0xfc7fe6d4,\n\t0xfc65ccf3, 0xfc495762, 0xfc2a2fc8, 0xfc07ee19, 0xfbe213c1,\n\t0xfbb8051a, 0xfb890078, 0xfb5411a5, 0xfb180005, 0xfad33482,\n\t0xfa839276, 0xfa263b32, 0xf9b72d1c, 0xf930a1a2, 0xf889f023,\n\t0xf7b577d2, 0xf69c650c, 0xf51530f0, 0xf2cb0e3c, 0xeeefb15d,\n\t0xe6da6ecf,\n}\n\nvar we = [256]float32{\n\t2.0249555e-09, 1.486674e-11, 2.4409617e-11, 3.1968806e-11,\n\t3.844677e-11, 4.4228204e-11, 4.9516443e-11, 5.443359e-11,\n\t5.905944e-11, 6.344942e-11, 6.7643814e-11, 7.1672945e-11,\n\t7.556032e-11, 7.932458e-11, 8.298079e-11, 8.654132e-11,\n\t9.0016515e-11, 9.3415074e-11, 9.674443e-11, 1.0001099e-10,\n\t1.03220314e-10, 1.06377254e-10, 1.09486115e-10, 1.1255068e-10,\n\t1.1557435e-10, 1.1856015e-10, 1.2151083e-10, 1.2442886e-10,\n\t1.2731648e-10, 1.3017575e-10, 1.3300853e-10, 1.3581657e-10,\n\t1.3860142e-10, 1.4136457e-10, 1.4410738e-10, 1.4683108e-10,\n\t1.4953687e-10, 1.5222583e-10, 1.54899e-10, 1.5755733e-10,\n\t1.6020171e-10, 1.6283301e-10, 1.6545203e-10, 1.6805951e-10,\n\t1.7065617e-10, 1.732427e-10, 1.7581973e-10, 1.7838787e-10,\n\t1.8094774e-10, 1.8349985e-10, 1.8604476e-10, 1.8858298e-10,\n\t1.9111498e-10, 1.9364126e-10, 1.9616223e-10, 1.9867835e-10,\n\t2.0119004e-10, 2.0369768e-10, 2.0620168e-10, 2.087024e-10,\n\t2.1120022e-10, 2.136955e-10, 2.1618855e-10, 2.1867974e-10,\n\t2.2116936e-10, 2.2365775e-10, 2.261452e-10, 2.2863202e-10,\n\t2.311185e-10, 2.3360494e-10, 2.360916e-10, 2.3857874e-10,\n\t2.4106667e-10, 2.4355562e-10, 2.4604588e-10, 2.485377e-10,\n\t2.5103128e-10, 2.5352695e-10, 2.560249e-10, 2.585254e-10,\n\t2.6102867e-10, 2.6353494e-10, 2.6604446e-10, 2.6855745e-10,\n\t2.7107416e-10, 2.7359479e-10, 2.761196e-10, 2.7864877e-10,\n\t2.8118255e-10, 2.8372119e-10, 2.8626485e-10, 2.888138e-10,\n\t2.9136826e-10, 2.939284e-10, 2.9649452e-10, 2.9906677e-10,\n\t3.016454e-10, 3.0423064e-10, 3.0682268e-10, 3.0942177e-10,\n\t3.1202813e-10, 3.1464195e-10, 3.1726352e-10, 3.19893e-10,\n\t3.2253064e-10, 3.251767e-10, 3.2783135e-10, 3.3049485e-10,\n\t3.3316744e-10, 3.3584938e-10, 3.3854083e-10, 3.4124212e-10,\n\t3.4395342e-10, 3.46675e-10, 3.4940711e-10, 3.5215003e-10,\n\t3.5490397e-10, 3.5766917e-10, 3.6044595e-10, 3.6323455e-10,\n\t3.660352e-10, 3.6884823e-10, 3.7167386e-10, 3.745124e-10,\n\t3.773641e-10, 3.802293e-10, 3.8310827e-10, 3.860013e-10,\n\t3.8890866e-10, 3.918307e-10, 3.9476775e-10, 3.9772008e-10,\n\t4.0068804e-10, 4.0367196e-10, 4.0667217e-10, 4.09689e-10,\n\t4.1272286e-10, 4.1577405e-10, 4.1884296e-10, 4.2192994e-10,\n\t4.250354e-10, 4.281597e-10, 4.313033e-10, 4.3446652e-10,\n\t4.3764986e-10, 4.408537e-10, 4.4407847e-10, 4.4732465e-10,\n\t4.5059267e-10, 4.5388301e-10, 4.571962e-10, 4.6053267e-10,\n\t4.6389292e-10, 4.6727755e-10, 4.70687e-10, 4.741219e-10,\n\t4.7758275e-10, 4.810702e-10, 4.845848e-10, 4.8812715e-10,\n\t4.9169796e-10, 4.9529775e-10, 4.989273e-10, 5.0258725e-10,\n\t5.0627835e-10, 5.100013e-10, 5.1375687e-10, 5.1754584e-10,\n\t5.21369e-10, 5.2522725e-10, 5.2912136e-10, 5.330522e-10,\n\t5.370208e-10, 5.4102806e-10, 5.45075e-10, 5.491625e-10,\n\t5.532918e-10, 5.5746385e-10, 5.616799e-10, 5.6594107e-10,\n\t5.7024857e-10, 5.746037e-10, 5.7900773e-10, 5.834621e-10,\n\t5.8796823e-10, 5.925276e-10, 5.971417e-10, 6.018122e-10,\n\t6.065408e-10, 6.113292e-10, 6.1617933e-10, 6.2109295e-10,\n\t6.260722e-10, 6.3111916e-10, 6.3623595e-10, 6.4142497e-10,\n\t6.4668854e-10, 6.5202926e-10, 6.5744976e-10, 6.6295286e-10,\n\t6.6854156e-10, 6.742188e-10, 6.79988e-10, 6.858526e-10,\n\t6.9181616e-10, 6.978826e-10, 7.04056e-10, 7.103407e-10,\n\t7.167412e-10, 7.2326256e-10, 7.2990985e-10, 7.366886e-10,\n\t7.4360473e-10, 7.5066453e-10, 7.5787476e-10, 7.6524265e-10,\n\t7.7277595e-10, 7.80483e-10, 7.883728e-10, 7.9645507e-10,\n\t8.047402e-10, 8.1323964e-10, 8.219657e-10, 8.309319e-10,\n\t8.401528e-10, 8.496445e-10, 8.594247e-10, 8.6951274e-10,\n\t8.799301e-10, 8.9070046e-10, 9.018503e-10, 9.134092e-10,\n\t9.254101e-10, 9.378904e-10, 9.508923e-10, 9.644638e-10,\n\t9.786603e-10, 9.935448e-10, 1.0091913e-09, 1.025686e-09,\n\t1.0431306e-09, 1.0616465e-09, 1.08138e-09, 1.1025096e-09,\n\t1.1252564e-09, 1.1498986e-09, 1.1767932e-09, 1.206409e-09,\n\t1.2393786e-09, 1.276585e-09, 1.3193139e-09, 1.3695435e-09,\n\t1.4305498e-09, 1.508365e-09, 1.6160854e-09, 1.7921248e-09,\n}\n\nvar fe = [256]float32{\n\t1, 0.9381437, 0.90046996, 0.87170434, 0.8477855, 0.8269933,\n\t0.8084217, 0.7915276, 0.77595687, 0.7614634, 0.7478686,\n\t0.7350381, 0.72286767, 0.71127474, 0.70019263, 0.6895665,\n\t0.67935055, 0.6695063, 0.66000086, 0.65080583, 0.6418967,\n\t0.63325197, 0.6248527, 0.6166822, 0.60872537, 0.60096896,\n\t0.5934009, 0.58601034, 0.5787874, 0.57172304, 0.5648092,\n\t0.5580383, 0.5514034, 0.5448982, 0.5385169, 0.53225386,\n\t0.5261042, 0.52006316, 0.5141264, 0.50828975, 0.5025495,\n\t0.496902, 0.49134386, 0.485872, 0.48048335, 0.4751752,\n\t0.46994483, 0.46478975, 0.45970762, 0.45469615, 0.44975325,\n\t0.44487688, 0.44006512, 0.43531612, 0.43062815, 0.42599955,\n\t0.42142874, 0.4169142, 0.41245446, 0.40804818, 0.403694,\n\t0.3993907, 0.39513698, 0.39093173, 0.38677382, 0.38266218,\n\t0.37859577, 0.37457356, 0.37059465, 0.3666581, 0.362763,\n\t0.35890847, 0.35509375, 0.351318, 0.3475805, 0.34388044,\n\t0.34021714, 0.3365899, 0.33299807, 0.32944095, 0.32591796,\n\t0.3224285, 0.3189719, 0.31554767, 0.31215525, 0.30879408,\n\t0.3054636, 0.3021634, 0.29889292, 0.2956517, 0.29243928,\n\t0.28925523, 0.28609908, 0.28297043, 0.27986884, 0.27679393,\n\t0.2737453, 0.2707226, 0.2677254, 0.26475343, 0.26180625,\n\t0.25888354, 0.25598502, 0.2531103, 0.25025907, 0.24743107,\n\t0.24462597, 0.24184346, 0.23908329, 0.23634516, 0.23362878,\n\t0.23093392, 0.2282603, 0.22560766, 0.22297576, 0.22036438,\n\t0.21777324, 0.21520215, 0.21265087, 0.21011916, 0.20760682,\n\t0.20511365, 0.20263945, 0.20018397, 0.19774707, 0.19532852,\n\t0.19292815, 0.19054577, 0.1881812, 0.18583426, 0.18350479,\n\t0.1811926, 0.17889754, 0.17661946, 0.17435817, 0.17211354,\n\t0.1698854, 0.16767362, 0.16547804, 0.16329853, 0.16113494,\n\t0.15898713, 0.15685499, 0.15473837, 0.15263714, 0.15055119,\n\t0.14848037, 0.14642459, 0.14438373, 0.14235765, 0.14034624,\n\t0.13834943, 0.13636707, 0.13439907, 0.13244532, 0.13050574,\n\t0.1285802, 0.12666863, 0.12477092, 0.12288698, 0.12101672,\n\t0.119160056, 0.1173169, 0.115487166, 0.11367077, 0.11186763,\n\t0.11007768, 0.10830083, 0.10653701, 0.10478614, 0.10304816,\n\t0.101323, 0.09961058, 0.09791085, 0.09622374, 0.09454919,\n\t0.09288713, 0.091237515, 0.08960028, 0.087975375, 0.08636274,\n\t0.08476233, 0.083174095, 0.081597984, 0.08003395, 0.07848195,\n\t0.076941945, 0.07541389, 0.07389775, 0.072393484, 0.07090106,\n\t0.069420435, 0.06795159, 0.066494495, 0.06504912, 0.063615434,\n\t0.062193416, 0.060783047, 0.059384305, 0.057997175,\n\t0.05662164, 0.05525769, 0.053905312, 0.052564494, 0.051235236,\n\t0.049917534, 0.048611384, 0.047316793, 0.046033762, 0.0447623,\n\t0.043502413, 0.042254124, 0.041017443, 0.039792392,\n\t0.038578995, 0.037377283, 0.036187284, 0.035009038,\n\t0.033842582, 0.032687962, 0.031545233, 0.030414443, 0.02929566,\n\t0.02818895, 0.027094385, 0.026012046, 0.024942026, 0.023884421,\n\t0.022839336, 0.021806888, 0.020787204, 0.019780423, 0.0187867,\n\t0.0178062, 0.016839107, 0.015885621, 0.014945968, 0.014020392,\n\t0.013109165, 0.012212592, 0.011331013, 0.01046481, 0.009614414,\n\t0.008780315, 0.007963077, 0.0071633533, 0.006381906,\n\t0.0056196423, 0.0048776558, 0.004157295, 0.0034602648,\n\t0.0027887989, 0.0021459677, 0.0015362998, 0.0009672693,\n\t0.00045413437,\n}\n"
  },
  {
    "path": "internal/rand/modulo_test.go",
    "content": "// Copied from https://cs.opensource.google/go/x/exp/+/24438e51023af3bfc1db8aed43c1342817e8cfcd:rand/modulo_test.go\n\n// Copyright 2017 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\n// This file validates that the calculation in Uint64n corrects for\n// possible bias.\n\npackage rand\n\nimport (\n\t\"testing\"\n)\n\n// modSource is used to probe the upper region of uint64 space. It\n// generates values sequentially in [maxUint64-15,maxUint64]. With\n// modEdge == 15 and maxUint64 == 1<<64-1 == 18446744073709551615,\n// this means that Uint64n(10) will repeatedly probe the top range.\n// We thus expect a bias to result unless the calculation in Uint64n\n// gets the edge condition right. We test this by calling Uint64n 100\n// times; the results should be perfectly evenly distributed across\n// [0,10).\ntype modSource uint64\n\nconst modEdge = 15\n\nfunc (m *modSource) Seed(uint64) {}\n\n// Uint64 returns a non-pseudo-random 64-bit unsigned integer as a uint64.\nfunc (m *modSource) Uint64() uint64 {\n\tif *m > modEdge {\n\t\t*m = 0\n\t}\n\tr := maxUint64 - *m\n\t*m++\n\treturn uint64(r)\n}\n\nfunc TestUint64Modulo(t *testing.T) {\n\tvar src modSource\n\trng := New(&src)\n\tvar result [10]uint64\n\tfor i := 0; i < 100; i++ {\n\t\tresult[rng.Uint64n(10)]++\n\t}\n\tfor _, r := range result {\n\t\tif r != 10 {\n\t\t\tt.Fatal(result)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "internal/rand/normal.go",
    "content": "// Copied from https://cs.opensource.google/go/x/exp/+/24438e51023af3bfc1db8aed43c1342817e8cfcd:rand/normal.go\n\n// Copyright 2009 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage rand\n\nimport (\n\t\"math\"\n)\n\n/*\n * Normal distribution\n *\n * See \"The Ziggurat Method for Generating Random Variables\"\n * (Marsaglia & Tsang, 2000)\n * http://www.jstatsoft.org/v05/i08/paper [pdf]\n */\n\nconst (\n\trn = 3.442619855899\n)\n\nfunc absInt32(i int32) uint32 {\n\tif i < 0 {\n\t\treturn uint32(-i)\n\t}\n\treturn uint32(i)\n}\n\n// NormFloat64 returns a normally distributed float64 in the range\n// [-math.MaxFloat64, +math.MaxFloat64] with\n// standard normal distribution (mean = 0, stddev = 1).\n// To produce a different normal distribution, callers can\n// adjust the output using:\n//\n//\tsample = NormFloat64() * desiredStdDev + desiredMean\nfunc (r *Rand) NormFloat64() float64 {\n\tfor {\n\t\tj := int32(r.Uint32()) // Possibly negative\n\t\ti := j & 0x7F\n\t\tx := float64(j) * float64(wn[i])\n\t\tif absInt32(j) < kn[i] {\n\t\t\t// This case should be hit better than 99% of the time.\n\t\t\treturn x\n\t\t}\n\n\t\tif i == 0 {\n\t\t\t// This extra work is only required for the base strip.\n\t\t\tfor {\n\t\t\t\tx = -math.Log(r.Float64()) * (1.0 / rn)\n\t\t\t\ty := -math.Log(r.Float64())\n\t\t\t\tif y+y >= x*x {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t\tif j > 0 {\n\t\t\t\treturn rn + x\n\t\t\t}\n\t\t\treturn -rn - x\n\t\t}\n\t\tif fn[i]+float32(r.Float64())*(fn[i-1]-fn[i]) < float32(math.Exp(-.5*x*x)) {\n\t\t\treturn x\n\t\t}\n\t}\n}\n\nvar kn = [128]uint32{\n\t0x76ad2212, 0x0, 0x600f1b53, 0x6ce447a6, 0x725b46a2,\n\t0x7560051d, 0x774921eb, 0x789a25bd, 0x799045c3, 0x7a4bce5d,\n\t0x7adf629f, 0x7b5682a6, 0x7bb8a8c6, 0x7c0ae722, 0x7c50cce7,\n\t0x7c8cec5b, 0x7cc12cd6, 0x7ceefed2, 0x7d177e0b, 0x7d3b8883,\n\t0x7d5bce6c, 0x7d78dd64, 0x7d932886, 0x7dab0e57, 0x7dc0dd30,\n\t0x7dd4d688, 0x7de73185, 0x7df81cea, 0x7e07c0a3, 0x7e163efa,\n\t0x7e23b587, 0x7e303dfd, 0x7e3beec2, 0x7e46db77, 0x7e51155d,\n\t0x7e5aabb3, 0x7e63abf7, 0x7e6c222c, 0x7e741906, 0x7e7b9a18,\n\t0x7e82adfa, 0x7e895c63, 0x7e8fac4b, 0x7e95a3fb, 0x7e9b4924,\n\t0x7ea0a0ef, 0x7ea5b00d, 0x7eaa7ac3, 0x7eaf04f3, 0x7eb3522a,\n\t0x7eb765a5, 0x7ebb4259, 0x7ebeeafd, 0x7ec2620a, 0x7ec5a9c4,\n\t0x7ec8c441, 0x7ecbb365, 0x7ece78ed, 0x7ed11671, 0x7ed38d62,\n\t0x7ed5df12, 0x7ed80cb4, 0x7eda175c, 0x7edc0005, 0x7eddc78e,\n\t0x7edf6ebf, 0x7ee0f647, 0x7ee25ebe, 0x7ee3a8a9, 0x7ee4d473,\n\t0x7ee5e276, 0x7ee6d2f5, 0x7ee7a620, 0x7ee85c10, 0x7ee8f4cd,\n\t0x7ee97047, 0x7ee9ce59, 0x7eea0eca, 0x7eea3147, 0x7eea3568,\n\t0x7eea1aab, 0x7ee9e071, 0x7ee98602, 0x7ee90a88, 0x7ee86d08,\n\t0x7ee7ac6a, 0x7ee6c769, 0x7ee5bc9c, 0x7ee48a67, 0x7ee32efc,\n\t0x7ee1a857, 0x7edff42f, 0x7ede0ffa, 0x7edbf8d9, 0x7ed9ab94,\n\t0x7ed7248d, 0x7ed45fae, 0x7ed1585c, 0x7ece095f, 0x7eca6ccb,\n\t0x7ec67be2, 0x7ec22eee, 0x7ebd7d1a, 0x7eb85c35, 0x7eb2c075,\n\t0x7eac9c20, 0x7ea5df27, 0x7e9e769f, 0x7e964c16, 0x7e8d44ba,\n\t0x7e834033, 0x7e781728, 0x7e6b9933, 0x7e5d8a1a, 0x7e4d9ded,\n\t0x7e3b737a, 0x7e268c2f, 0x7e0e3ff5, 0x7df1aa5d, 0x7dcf8c72,\n\t0x7da61a1e, 0x7d72a0fb, 0x7d30e097, 0x7cd9b4ab, 0x7c600f1a,\n\t0x7ba90bdc, 0x7a722176, 0x77d664e5,\n}\n\nvar wn = [128]float32{\n\t1.7290405e-09, 1.2680929e-10, 1.6897518e-10, 1.9862688e-10,\n\t2.2232431e-10, 2.4244937e-10, 2.601613e-10, 2.7611988e-10,\n\t2.9073963e-10, 3.042997e-10, 3.1699796e-10, 3.289802e-10,\n\t3.4035738e-10, 3.5121603e-10, 3.616251e-10, 3.7164058e-10,\n\t3.8130857e-10, 3.9066758e-10, 3.9975012e-10, 4.08584e-10,\n\t4.1719309e-10, 4.2559822e-10, 4.338176e-10, 4.418672e-10,\n\t4.497613e-10, 4.5751258e-10, 4.651324e-10, 4.7263105e-10,\n\t4.8001775e-10, 4.87301e-10, 4.944885e-10, 5.015873e-10,\n\t5.0860405e-10, 5.155446e-10, 5.2241467e-10, 5.2921934e-10,\n\t5.359635e-10, 5.426517e-10, 5.4928817e-10, 5.5587696e-10,\n\t5.624219e-10, 5.6892646e-10, 5.753941e-10, 5.818282e-10,\n\t5.882317e-10, 5.946077e-10, 6.00959e-10, 6.072884e-10,\n\t6.135985e-10, 6.19892e-10, 6.2617134e-10, 6.3243905e-10,\n\t6.386974e-10, 6.449488e-10, 6.511956e-10, 6.5744005e-10,\n\t6.6368433e-10, 6.699307e-10, 6.7618144e-10, 6.824387e-10,\n\t6.8870465e-10, 6.949815e-10, 7.012715e-10, 7.075768e-10,\n\t7.1389966e-10, 7.202424e-10, 7.266073e-10, 7.329966e-10,\n\t7.394128e-10, 7.4585826e-10, 7.5233547e-10, 7.58847e-10,\n\t7.653954e-10, 7.719835e-10, 7.7861395e-10, 7.852897e-10,\n\t7.920138e-10, 7.987892e-10, 8.0561924e-10, 8.125073e-10,\n\t8.194569e-10, 8.2647167e-10, 8.3355556e-10, 8.407127e-10,\n\t8.479473e-10, 8.55264e-10, 8.6266755e-10, 8.7016316e-10,\n\t8.777562e-10, 8.8545243e-10, 8.932582e-10, 9.0117996e-10,\n\t9.09225e-10, 9.174008e-10, 9.2571584e-10, 9.341788e-10,\n\t9.427997e-10, 9.515889e-10, 9.605579e-10, 9.697193e-10,\n\t9.790869e-10, 9.88676e-10, 9.985036e-10, 1.0085882e-09,\n\t1.0189509e-09, 1.0296151e-09, 1.0406069e-09, 1.0519566e-09,\n\t1.063698e-09, 1.0758702e-09, 1.0885183e-09, 1.1016947e-09,\n\t1.1154611e-09, 1.1298902e-09, 1.1450696e-09, 1.1611052e-09,\n\t1.1781276e-09, 1.1962995e-09, 1.2158287e-09, 1.2369856e-09,\n\t1.2601323e-09, 1.2857697e-09, 1.3146202e-09, 1.347784e-09,\n\t1.3870636e-09, 1.4357403e-09, 1.5008659e-09, 1.6030948e-09,\n}\n\nvar fn = [128]float32{\n\t1, 0.9635997, 0.9362827, 0.9130436, 0.89228165, 0.87324303,\n\t0.8555006, 0.8387836, 0.8229072, 0.8077383, 0.793177,\n\t0.7791461, 0.7655842, 0.7524416, 0.73967725, 0.7272569,\n\t0.7151515, 0.7033361, 0.69178915, 0.68049186, 0.6694277,\n\t0.658582, 0.6479418, 0.63749546, 0.6272325, 0.6171434,\n\t0.6072195, 0.5974532, 0.58783704, 0.5783647, 0.56903,\n\t0.5598274, 0.5507518, 0.54179835, 0.5329627, 0.52424055,\n\t0.5156282, 0.50712204, 0.49871865, 0.49041483, 0.48220766,\n\t0.4740943, 0.46607214, 0.4581387, 0.45029163, 0.44252872,\n\t0.43484783, 0.427247, 0.41972435, 0.41227803, 0.40490642,\n\t0.39760786, 0.3903808, 0.3832238, 0.37613547, 0.36911446,\n\t0.3621595, 0.35526937, 0.34844297, 0.34167916, 0.33497685,\n\t0.3283351, 0.3217529, 0.3152294, 0.30876362, 0.30235484,\n\t0.29600215, 0.28970486, 0.2834622, 0.2772735, 0.27113807,\n\t0.2650553, 0.25902456, 0.2530453, 0.24711695, 0.241239,\n\t0.23541094, 0.22963232, 0.2239027, 0.21822165, 0.21258877,\n\t0.20700371, 0.20146611, 0.19597565, 0.19053204, 0.18513499,\n\t0.17978427, 0.17447963, 0.1692209, 0.16400786, 0.15884037,\n\t0.15371831, 0.14864157, 0.14361008, 0.13862377, 0.13368265,\n\t0.12878671, 0.12393598, 0.119130544, 0.11437051, 0.10965602,\n\t0.104987256, 0.10036444, 0.095787846, 0.0912578, 0.08677467,\n\t0.0823389, 0.077950984, 0.073611505, 0.06932112, 0.06508058,\n\t0.06089077, 0.056752663, 0.0526674, 0.048636295, 0.044660863,\n\t0.040742867, 0.03688439, 0.033087887, 0.029356318,\n\t0.025693292, 0.022103304, 0.018592102, 0.015167298,\n\t0.011839478, 0.008624485, 0.005548995, 0.0026696292,\n}\n"
  },
  {
    "path": "internal/rand/race_test.go",
    "content": "// Copied from https://cs.opensource.google/go/x/exp/+/24438e51023af3bfc1db8aed43c1342817e8cfcd:rand/race_test.go\n\n// Copyright 2016 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage rand\n\nimport (\n\t\"sync\"\n\t\"testing\"\n)\n\n// TestConcurrent exercises the rand API concurrently, triggering situations\n// where the race detector is likely to detect issues.\nfunc TestConcurrent(t *testing.T) {\n\tconst (\n\t\tnumRoutines = 10\n\t\tnumCycles   = 10\n\t)\n\tvar wg sync.WaitGroup\n\tdefer wg.Wait()\n\twg.Add(numRoutines)\n\tfor i := 0; i < numRoutines; i++ {\n\t\tgo func(i int) {\n\t\t\tdefer wg.Done()\n\t\t\tbuf := make([]byte, 997)\n\t\t\tfor j := 0; j < numCycles; j++ {\n\t\t\t\tvar seed uint64\n\t\t\t\tseed += uint64(ExpFloat64())\n\t\t\t\tseed += uint64(Float32())\n\t\t\t\tseed += uint64(Float64())\n\t\t\t\tseed += uint64(Intn(Int()))\n\t\t\t\tseed += uint64(Int31n(Int31()))\n\t\t\t\tseed += uint64(Int63n(Int63()))\n\t\t\t\tseed += uint64(NormFloat64())\n\t\t\t\tseed += uint64(Uint32())\n\t\t\t\tseed += uint64(Uint64())\n\t\t\t\tfor _, p := range Perm(10) {\n\t\t\t\t\tseed += uint64(p)\n\t\t\t\t}\n\t\t\t\tRead(buf)\n\t\t\t\tfor _, b := range buf {\n\t\t\t\t\tseed += uint64(b)\n\t\t\t\t}\n\t\t\t\tSeed(uint64(i*j) * seed)\n\t\t\t}\n\t\t}(i)\n\t}\n}\n"
  },
  {
    "path": "internal/rand/rand.go",
    "content": "// Copied from https://cs.opensource.google/go/x/exp/+/24438e51023af3bfc1db8aed43c1342817e8cfcd:rand/rand.go\n\n// Copyright 2009 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\n// Package rand implements pseudo-random number generators.\n//\n// Random numbers are generated by a Source. Top-level functions, such as\n// Float64 and Int, use a default shared Source that produces a deterministic\n// sequence of values each time a program is run. Use the Seed function to\n// initialize the default Source if different behavior is required for each run.\n// The default Source, a LockedSource, is safe for concurrent use by multiple\n// goroutines, but Sources created by NewSource are not. However, Sources are small\n// and it is reasonable to have a separate Source for each goroutine, seeded\n// differently, to avoid locking.\n//\n// For random numbers suitable for security-sensitive work, see the crypto/rand\n// package.\npackage rand\n\nimport \"sync\"\n\n// A Source represents a source of uniformly-distributed\n// pseudo-random int64 values in the range [0, 1<<64).\ntype Source interface {\n\tUint64() uint64\n\tSeed(seed uint64)\n}\n\n// NewSource returns a new pseudo-random Source seeded with the given value.\nfunc NewSource(seed uint64) Source {\n\tvar rng PCGSource\n\trng.Seed(seed)\n\treturn &rng\n}\n\n// A Rand is a source of random numbers.\ntype Rand struct {\n\tsrc Source\n\n\t// readVal contains remainder of 64-bit integer used for bytes\n\t// generation during most recent Read call.\n\t// It is saved so next Read call can start where the previous\n\t// one finished.\n\treadVal uint64\n\t// readPos indicates the number of low-order bytes of readVal\n\t// that are still valid.\n\treadPos int8\n}\n\n// New returns a new Rand that uses random values from src\n// to generate other random values.\nfunc New(src Source) *Rand {\n\treturn &Rand{src: src}\n}\n\n// Seed uses the provided seed value to initialize the generator to a deterministic state.\n// Seed should not be called concurrently with any other Rand method.\nfunc (r *Rand) Seed(seed uint64) {\n\tif lk, ok := r.src.(*LockedSource); ok {\n\t\tlk.seedPos(seed, &r.readPos)\n\t\treturn\n\t}\n\n\tr.src.Seed(seed)\n\tr.readPos = 0\n}\n\n// Uint64 returns a pseudo-random 64-bit integer as a uint64.\nfunc (r *Rand) Uint64() uint64 { return r.src.Uint64() }\n\n// Int63 returns a non-negative pseudo-random 63-bit integer as an int64.\nfunc (r *Rand) Int63() int64 { return int64(r.src.Uint64() &^ (1 << 63)) }\n\n// Uint32 returns a pseudo-random 32-bit value as a uint32.\nfunc (r *Rand) Uint32() uint32 { return uint32(r.Uint64() >> 32) }\n\n// Int31 returns a non-negative pseudo-random 31-bit integer as an int32.\nfunc (r *Rand) Int31() int32 { return int32(r.Uint64() >> 33) }\n\n// Int returns a non-negative pseudo-random int.\nfunc (r *Rand) Int() int {\n\tu := uint(r.Uint64())\n\treturn int(u << 1 >> 1) // clear sign bit.\n}\n\nconst maxUint64 = (1 << 64) - 1\n\n// Uint64n returns, as a uint64, a pseudo-random number in [0,n).\n// It is guaranteed more uniform than taking a Source value mod n\n// for any n that is not a power of 2.\nfunc (r *Rand) Uint64n(n uint64) uint64 {\n\tif n&(n-1) == 0 { // n is power of two, can mask\n\t\tif n == 0 {\n\t\t\tpanic(\"invalid argument to Uint64n\")\n\t\t}\n\t\treturn r.Uint64() & (n - 1)\n\t}\n\t// If n does not divide v, to avoid bias we must not use\n\t// a v that is within maxUint64%n of the top of the range.\n\tv := r.Uint64()\n\tif v > maxUint64-n { // Fast check.\n\t\tceiling := maxUint64 - maxUint64%n\n\t\tfor v >= ceiling {\n\t\t\tv = r.Uint64()\n\t\t}\n\t}\n\n\treturn v % n\n}\n\n// Int63n returns, as an int64, a non-negative pseudo-random number in [0,n).\n// It panics if n <= 0.\nfunc (r *Rand) Int63n(n int64) int64 {\n\tif n <= 0 {\n\t\tpanic(\"invalid argument to Int63n\")\n\t}\n\treturn int64(r.Uint64n(uint64(n)))\n}\n\n// Int31n returns, as an int32, a non-negative pseudo-random number in [0,n).\n// It panics if n <= 0.\nfunc (r *Rand) Int31n(n int32) int32 {\n\tif n <= 0 {\n\t\tpanic(\"invalid argument to Int31n\")\n\t}\n\t// TODO: Avoid some 64-bit ops to make it more efficient on 32-bit machines.\n\treturn int32(r.Uint64n(uint64(n)))\n}\n\n// Intn returns, as an int, a non-negative pseudo-random number in [0,n).\n// It panics if n <= 0.\nfunc (r *Rand) Intn(n int) int {\n\tif n <= 0 {\n\t\tpanic(\"invalid argument to Intn\")\n\t}\n\t// TODO: Avoid some 64-bit ops to make it more efficient on 32-bit machines.\n\treturn int(r.Uint64n(uint64(n)))\n}\n\n// Float64 returns, as a float64, a pseudo-random number in [0.0,1.0).\nfunc (r *Rand) Float64() float64 {\n\t// There is one bug in the value stream: r.Int63() may be so close\n\t// to 1<<63 that the division rounds up to 1.0, and we've guaranteed\n\t// that the result is always less than 1.0.\n\t//\n\t// We tried to fix this by mapping 1.0 back to 0.0, but since float64\n\t// values near 0 are much denser than near 1, mapping 1 to 0 caused\n\t// a theoretically significant overshoot in the probability of returning 0.\n\t// Instead of that, if we round up to 1, just try again.\n\t// Getting 1 only happens 1/2⁵³ of the time, so most clients\n\t// will not observe it anyway.\nagain:\n\tf := float64(r.Uint64n(1<<53)) / (1 << 53)\n\tif f == 1.0 {\n\t\tgoto again // resample; this branch is taken O(never)\n\t}\n\treturn f\n}\n\n// Float32 returns, as a float32, a pseudo-random number in [0.0,1.0).\nfunc (r *Rand) Float32() float32 {\n\t// We do not want to return 1.0.\n\t// This only happens 1/2²⁴ of the time (plus the 1/2⁵³ of the time in Float64).\nagain:\n\tf := float32(r.Float64())\n\tif f == 1 {\n\t\tgoto again // resample; this branch is taken O(very rarely)\n\t}\n\treturn f\n}\n\n// Perm returns, as a slice of n ints, a pseudo-random permutation of the integers [0,n).\nfunc (r *Rand) Perm(n int) []int {\n\tm := make([]int, n)\n\t// In the following loop, the iteration when i=0 always swaps m[0] with m[0].\n\t// A change to remove this useless iteration is to assign 1 to i in the init\n\t// statement. But Perm also effects r. Making this change will affect\n\t// the final state of r. So this change can't be made for compatibility\n\t// reasons for Go 1.\n\tfor i := 0; i < n; i++ {\n\t\tj := r.Intn(i + 1)\n\t\tm[i] = m[j]\n\t\tm[j] = i\n\t}\n\treturn m\n}\n\n// Shuffle pseudo-randomizes the order of elements.\n// n is the number of elements. Shuffle panics if n < 0.\n// swap swaps the elements with indexes i and j.\nfunc (r *Rand) Shuffle(n int, swap func(i, j int)) {\n\tif n < 0 {\n\t\tpanic(\"invalid argument to Shuffle\")\n\t}\n\n\t// Fisher-Yates shuffle: https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle\n\t// Shuffle really ought not be called with n that doesn't fit in 32 bits.\n\t// Not only will it take a very long time, but with 2³¹! possible permutations,\n\t// there's no way that any PRNG can have a big enough internal state to\n\t// generate even a minuscule percentage of the possible permutations.\n\t// Nevertheless, the right API signature accepts an int n, so handle it as best we can.\n\ti := n - 1\n\tfor ; i > 1<<31-1-1; i-- {\n\t\tj := int(r.Int63n(int64(i + 1)))\n\t\tswap(i, j)\n\t}\n\tfor ; i > 0; i-- {\n\t\tj := int(r.Int31n(int32(i + 1)))\n\t\tswap(i, j)\n\t}\n}\n\n// Read generates len(p) random bytes and writes them into p. It\n// always returns len(p) and a nil error.\n// Read should not be called concurrently with any other Rand method unless\n// the underlying source is a LockedSource.\nfunc (r *Rand) Read(p []byte) (n int, err error) {\n\tif lk, ok := r.src.(*LockedSource); ok {\n\t\treturn lk.Read(p, &r.readVal, &r.readPos)\n\t}\n\treturn read(p, r.src, &r.readVal, &r.readPos)\n}\n\nfunc read(p []byte, src Source, readVal *uint64, readPos *int8) (n int, err error) {\n\tpos := *readPos\n\tval := *readVal\n\trng, _ := src.(*PCGSource)\n\tfor n = 0; n < len(p); n++ {\n\t\tif pos == 0 {\n\t\t\tif rng != nil {\n\t\t\t\tval = rng.Uint64()\n\t\t\t} else {\n\t\t\t\tval = src.Uint64()\n\t\t\t}\n\t\t\tpos = 8\n\t\t}\n\t\tp[n] = byte(val)\n\t\tval >>= 8\n\t\tpos--\n\t}\n\t*readPos = pos\n\t*readVal = val\n\treturn\n}\n\n/*\n * Top-level convenience functions\n */\n\nvar globalRand = New(&LockedSource{src: *NewSource(1).(*PCGSource)})\n\n// Type assert that globalRand's source is a LockedSource whose src is a PCGSource.\nvar _ PCGSource = globalRand.src.(*LockedSource).src\n\n// Seed uses the provided seed value to initialize the default Source to a\n// deterministic state. If Seed is not called, the generator behaves as\n// if seeded by Seed(1).\n// Seed, unlike the Rand.Seed method, is safe for concurrent use.\nfunc Seed(seed uint64) { globalRand.Seed(seed) }\n\n// Int63 returns a non-negative pseudo-random 63-bit integer as an int64\n// from the default Source.\nfunc Int63() int64 { return globalRand.Int63() }\n\n// Uint32 returns a pseudo-random 32-bit value as a uint32\n// from the default Source.\nfunc Uint32() uint32 { return globalRand.Uint32() }\n\n// Uint64 returns a pseudo-random 64-bit value as a uint64\n// from the default Source.\nfunc Uint64() uint64 { return globalRand.Uint64() }\n\n// Int31 returns a non-negative pseudo-random 31-bit integer as an int32\n// from the default Source.\nfunc Int31() int32 { return globalRand.Int31() }\n\n// Int returns a non-negative pseudo-random int from the default Source.\nfunc Int() int { return globalRand.Int() }\n\n// Int63n returns, as an int64, a non-negative pseudo-random number in [0,n)\n// from the default Source.\n// It panics if n <= 0.\nfunc Int63n(n int64) int64 { return globalRand.Int63n(n) }\n\n// Int31n returns, as an int32, a non-negative pseudo-random number in [0,n)\n// from the default Source.\n// It panics if n <= 0.\nfunc Int31n(n int32) int32 { return globalRand.Int31n(n) }\n\n// Intn returns, as an int, a non-negative pseudo-random number in [0,n)\n// from the default Source.\n// It panics if n <= 0.\nfunc Intn(n int) int { return globalRand.Intn(n) }\n\n// Float64 returns, as a float64, a pseudo-random number in [0.0,1.0)\n// from the default Source.\nfunc Float64() float64 { return globalRand.Float64() }\n\n// Float32 returns, as a float32, a pseudo-random number in [0.0,1.0)\n// from the default Source.\nfunc Float32() float32 { return globalRand.Float32() }\n\n// Perm returns, as a slice of n ints, a pseudo-random permutation of the integers [0,n)\n// from the default Source.\nfunc Perm(n int) []int { return globalRand.Perm(n) }\n\n// Shuffle pseudo-randomizes the order of elements using the default Source.\n// n is the number of elements. Shuffle panics if n < 0.\n// swap swaps the elements with indexes i and j.\nfunc Shuffle(n int, swap func(i, j int)) { globalRand.Shuffle(n, swap) }\n\n// Read generates len(p) random bytes from the default Source and\n// writes them into p. It always returns len(p) and a nil error.\n// Read, unlike the Rand.Read method, is safe for concurrent use.\nfunc Read(p []byte) (n int, err error) { return globalRand.Read(p) }\n\n// NormFloat64 returns a normally distributed float64 in the range\n// [-math.MaxFloat64, +math.MaxFloat64] with\n// standard normal distribution (mean = 0, stddev = 1)\n// from the default Source.\n// To produce a different normal distribution, callers can\n// adjust the output using:\n//\n//\tsample = NormFloat64() * desiredStdDev + desiredMean\nfunc NormFloat64() float64 { return globalRand.NormFloat64() }\n\n// ExpFloat64 returns an exponentially distributed float64 in the range\n// (0, +math.MaxFloat64] with an exponential distribution whose rate parameter\n// (lambda) is 1 and whose mean is 1/lambda (1) from the default Source.\n// To produce a distribution with a different rate parameter,\n// callers can adjust the output using:\n//\n//\tsample = ExpFloat64() / desiredRateParameter\nfunc ExpFloat64() float64 { return globalRand.ExpFloat64() }\n\n// LockedSource is an implementation of Source that is concurrency-safe.\n// A Rand using a LockedSource is safe for concurrent use.\n//\n// The zero value of LockedSource is valid, but should be seeded before use.\ntype LockedSource struct {\n\tlk  sync.Mutex\n\tsrc PCGSource\n}\n\nfunc (s *LockedSource) Uint64() (n uint64) {\n\ts.lk.Lock()\n\tn = s.src.Uint64()\n\ts.lk.Unlock()\n\treturn\n}\n\nfunc (s *LockedSource) Seed(seed uint64) {\n\ts.lk.Lock()\n\ts.src.Seed(seed)\n\ts.lk.Unlock()\n}\n\n// seedPos implements Seed for a LockedSource without a race condition.\nfunc (s *LockedSource) seedPos(seed uint64, readPos *int8) {\n\ts.lk.Lock()\n\ts.src.Seed(seed)\n\t*readPos = 0\n\ts.lk.Unlock()\n}\n\n// Read implements Read for a LockedSource.\nfunc (s *LockedSource) Read(p []byte, readVal *uint64, readPos *int8) (n int, err error) {\n\ts.lk.Lock()\n\tn, err = read(p, &s.src, readVal, readPos)\n\ts.lk.Unlock()\n\treturn\n}\n"
  },
  {
    "path": "internal/rand/rand_test.go",
    "content": "// Copied from https://cs.opensource.google/go/x/exp/+/24438e51023af3bfc1db8aed43c1342817e8cfcd:rand/rand_test.go\n\n// Copyright 2009 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage rand\n\nimport (\n\t\"bytes\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"math\"\n\t\"os\"\n\t\"runtime\"\n\t\"testing\"\n\t\"testing/iotest\"\n\t\"time\"\n)\n\nconst (\n\tnumTestSamples = 10000\n)\n\ntype statsResults struct {\n\tmean        float64\n\tstddev      float64\n\tcloseEnough float64\n\tmaxError    float64\n}\n\nfunc max(a, b float64) float64 {\n\tif a > b {\n\t\treturn a\n\t}\n\treturn b\n}\n\nfunc nearEqual(a, b, closeEnough, maxError float64) bool {\n\tabsDiff := math.Abs(a - b)\n\tif absDiff < closeEnough { // Necessary when one value is zero and one value is close to zero.\n\t\treturn true\n\t}\n\treturn absDiff/max(math.Abs(a), math.Abs(b)) < maxError\n}\n\nvar testSeeds = []uint64{1, 1754801282, 1698661970, 1550503961}\n\n// checkSimilarDistribution returns success if the mean and stddev of the\n// two statsResults are similar.\nfunc (this *statsResults) checkSimilarDistribution(expected *statsResults) error {\n\tif !nearEqual(this.mean, expected.mean, expected.closeEnough, expected.maxError) {\n\t\ts := fmt.Sprintf(\"mean %v != %v (allowed error %v, %v)\", this.mean, expected.mean, expected.closeEnough, expected.maxError)\n\t\tfmt.Println(s)\n\t\treturn errors.New(s)\n\t}\n\tif !nearEqual(this.stddev, expected.stddev, 0, expected.maxError) {\n\t\ts := fmt.Sprintf(\"stddev %v != %v (allowed error %v, %v)\", this.stddev, expected.stddev, expected.closeEnough, expected.maxError)\n\t\tfmt.Println(s)\n\t\treturn errors.New(s)\n\t}\n\treturn nil\n}\n\nfunc getStatsResults(samples []float64) *statsResults {\n\tres := new(statsResults)\n\tvar sum, squaresum float64\n\tfor _, s := range samples {\n\t\tsum += s\n\t\tsquaresum += s * s\n\t}\n\tres.mean = sum / float64(len(samples))\n\tres.stddev = math.Sqrt(squaresum/float64(len(samples)) - res.mean*res.mean)\n\treturn res\n}\n\nfunc checkSampleDistribution(t *testing.T, samples []float64, expected *statsResults) {\n\tt.Helper()\n\tactual := getStatsResults(samples)\n\terr := actual.checkSimilarDistribution(expected)\n\tif err != nil {\n\t\tt.Errorf(err.Error())\n\t}\n}\n\nfunc checkSampleSliceDistributions(t *testing.T, samples []float64, nslices int, expected *statsResults) {\n\tt.Helper()\n\tchunk := len(samples) / nslices\n\tfor i := 0; i < nslices; i++ {\n\t\tlow := i * chunk\n\t\tvar high int\n\t\tif i == nslices-1 {\n\t\t\thigh = len(samples) - 1\n\t\t} else {\n\t\t\thigh = (i + 1) * chunk\n\t\t}\n\t\tcheckSampleDistribution(t, samples[low:high], expected)\n\t}\n}\n\n//\n// Normal distribution tests\n//\n\nfunc generateNormalSamples(nsamples int, mean, stddev float64, seed uint64) []float64 {\n\tr := New(NewSource(seed))\n\tsamples := make([]float64, nsamples)\n\tfor i := range samples {\n\t\tsamples[i] = r.NormFloat64()*stddev + mean\n\t}\n\treturn samples\n}\n\nfunc testNormalDistribution(t *testing.T, nsamples int, mean, stddev float64, seed uint64) {\n\t// fmt.Printf(\"testing nsamples=%v mean=%v stddev=%v seed=%v\\n\", nsamples, mean, stddev, seed);\n\n\tsamples := generateNormalSamples(nsamples, mean, stddev, seed)\n\terrorScale := max(1.0, stddev) // Error scales with stddev\n\texpected := &statsResults{mean, stddev, 0.10 * errorScale, 0.08 * errorScale}\n\n\t// Make sure that the entire set matches the expected distribution.\n\tcheckSampleDistribution(t, samples, expected)\n\n\t// Make sure that each half of the set matches the expected distribution.\n\tcheckSampleSliceDistributions(t, samples, 2, expected)\n\n\t// Make sure that each 7th of the set matches the expected distribution.\n\tcheckSampleSliceDistributions(t, samples, 7, expected)\n}\n\n// Actual tests\n\nfunc TestStandardNormalValues(t *testing.T) {\n\tfor _, seed := range testSeeds {\n\t\ttestNormalDistribution(t, numTestSamples, 0, 1, seed)\n\t}\n}\n\nfunc TestNonStandardNormalValues(t *testing.T) {\n\tsdmax := 1000.0\n\tmmax := 1000.0\n\tif testing.Short() {\n\t\tsdmax = 5\n\t\tmmax = 5\n\t}\n\tfor sd := 0.5; sd < sdmax; sd *= 2 {\n\t\tfor m := 0.5; m < mmax; m *= 2 {\n\t\t\tfor _, seed := range testSeeds {\n\t\t\t\ttestNormalDistribution(t, numTestSamples, m, sd, seed)\n\t\t\t\tif testing.Short() {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n//\n// Exponential distribution tests\n//\n\nfunc generateExponentialSamples(nsamples int, rate float64, seed uint64) []float64 {\n\tr := New(NewSource(seed))\n\tsamples := make([]float64, nsamples)\n\tfor i := range samples {\n\t\tsamples[i] = r.ExpFloat64() / rate\n\t}\n\treturn samples\n}\n\nfunc testExponentialDistribution(t *testing.T, nsamples int, rate float64, seed uint64) {\n\t// fmt.Printf(\"testing nsamples=%v rate=%v seed=%v\\n\", nsamples, rate, seed)\n\n\tmean := 1 / rate\n\tstddev := mean\n\n\tsamples := generateExponentialSamples(nsamples, rate, seed)\n\terrorScale := max(1.0, 1/rate) // Error scales with the inverse of the rate\n\texpected := &statsResults{mean, stddev, 0.10 * errorScale, 0.20 * errorScale}\n\n\t// Make sure that the entire set matches the expected distribution.\n\tcheckSampleDistribution(t, samples, expected)\n\n\t// Make sure that each half of the set matches the expected distribution.\n\tcheckSampleSliceDistributions(t, samples, 2, expected)\n\n\t// Make sure that each 7th of the set matches the expected distribution.\n\tcheckSampleSliceDistributions(t, samples, 7, expected)\n}\n\n// Actual tests\n\nfunc TestStandardExponentialValues(t *testing.T) {\n\tfor _, seed := range testSeeds {\n\t\ttestExponentialDistribution(t, numTestSamples, 1, seed)\n\t}\n}\n\nfunc TestNonStandardExponentialValues(t *testing.T) {\n\tfor rate := 0.05; rate < 10; rate *= 2 {\n\t\tfor _, seed := range testSeeds {\n\t\t\ttestExponentialDistribution(t, numTestSamples, rate, seed)\n\t\t\tif testing.Short() {\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t}\n}\n\n//\n// Table generation tests\n//\n\nfunc initNorm() (testKn []uint32, testWn, testFn []float32) {\n\tconst m1 = 1 << 31\n\tvar (\n\t\tdn float64 = rn\n\t\ttn         = dn\n\t\tvn float64 = 9.91256303526217e-3\n\t)\n\n\ttestKn = make([]uint32, 128)\n\ttestWn = make([]float32, 128)\n\ttestFn = make([]float32, 128)\n\n\tq := vn / math.Exp(-0.5*dn*dn)\n\ttestKn[0] = uint32((dn / q) * m1)\n\ttestKn[1] = 0\n\ttestWn[0] = float32(q / m1)\n\ttestWn[127] = float32(dn / m1)\n\ttestFn[0] = 1.0\n\ttestFn[127] = float32(math.Exp(-0.5 * dn * dn))\n\tfor i := 126; i >= 1; i-- {\n\t\tdn = math.Sqrt(-2.0 * math.Log(vn/dn+math.Exp(-0.5*dn*dn)))\n\t\ttestKn[i+1] = uint32((dn / tn) * m1)\n\t\ttn = dn\n\t\ttestFn[i] = float32(math.Exp(-0.5 * dn * dn))\n\t\ttestWn[i] = float32(dn / m1)\n\t}\n\treturn\n}\n\nfunc initExp() (testKe []uint32, testWe, testFe []float32) {\n\tconst m2 = 1 << 32\n\tvar (\n\t\tde float64 = re\n\t\tte         = de\n\t\tve float64 = 3.9496598225815571993e-3\n\t)\n\n\ttestKe = make([]uint32, 256)\n\ttestWe = make([]float32, 256)\n\ttestFe = make([]float32, 256)\n\n\tq := ve / math.Exp(-de)\n\ttestKe[0] = uint32((de / q) * m2)\n\ttestKe[1] = 0\n\ttestWe[0] = float32(q / m2)\n\ttestWe[255] = float32(de / m2)\n\ttestFe[0] = 1.0\n\ttestFe[255] = float32(math.Exp(-de))\n\tfor i := 254; i >= 1; i-- {\n\t\tde = -math.Log(ve/de + math.Exp(-de))\n\t\ttestKe[i+1] = uint32((de / te) * m2)\n\t\tte = de\n\t\ttestFe[i] = float32(math.Exp(-de))\n\t\ttestWe[i] = float32(de / m2)\n\t}\n\treturn\n}\n\n// compareUint32Slices returns the first index where the two slices\n// disagree, or <0 if the lengths are the same and all elements\n// are identical.\nfunc compareUint32Slices(s1, s2 []uint32) int {\n\tif len(s1) != len(s2) {\n\t\tif len(s1) > len(s2) {\n\t\t\treturn len(s2) + 1\n\t\t}\n\t\treturn len(s1) + 1\n\t}\n\tfor i := range s1 {\n\t\tif s1[i] != s2[i] {\n\t\t\treturn i\n\t\t}\n\t}\n\treturn -1\n}\n\n// compareFloat32Slices returns the first index where the two slices\n// disagree, or <0 if the lengths are the same and all elements\n// are identical.\nfunc compareFloat32Slices(s1, s2 []float32) int {\n\tif len(s1) != len(s2) {\n\t\tif len(s1) > len(s2) {\n\t\t\treturn len(s2) + 1\n\t\t}\n\t\treturn len(s1) + 1\n\t}\n\tfor i := range s1 {\n\t\tif !nearEqual(float64(s1[i]), float64(s2[i]), 0, 1e-7) {\n\t\t\treturn i\n\t\t}\n\t}\n\treturn -1\n}\n\nfunc TestNormTables(t *testing.T) {\n\ttestKn, testWn, testFn := initNorm()\n\tif i := compareUint32Slices(kn[0:], testKn); i >= 0 {\n\t\tt.Errorf(\"kn disagrees at index %v; %v != %v\", i, kn[i], testKn[i])\n\t}\n\tif i := compareFloat32Slices(wn[0:], testWn); i >= 0 {\n\t\tt.Errorf(\"wn disagrees at index %v; %v != %v\", i, wn[i], testWn[i])\n\t}\n\tif i := compareFloat32Slices(fn[0:], testFn); i >= 0 {\n\t\tt.Errorf(\"fn disagrees at index %v; %v != %v\", i, fn[i], testFn[i])\n\t}\n}\n\nfunc TestExpTables(t *testing.T) {\n\ttestKe, testWe, testFe := initExp()\n\tif i := compareUint32Slices(ke[0:], testKe); i >= 0 {\n\t\tt.Errorf(\"ke disagrees at index %v; %v != %v\", i, ke[i], testKe[i])\n\t}\n\tif i := compareFloat32Slices(we[0:], testWe); i >= 0 {\n\t\tt.Errorf(\"we disagrees at index %v; %v != %v\", i, we[i], testWe[i])\n\t}\n\tif i := compareFloat32Slices(fe[0:], testFe); i >= 0 {\n\t\tt.Errorf(\"fe disagrees at index %v; %v != %v\", i, fe[i], testFe[i])\n\t}\n}\n\nfunc hasSlowFloatingPoint() bool {\n\tswitch runtime.GOARCH {\n\tcase \"arm\":\n\t\treturn os.Getenv(\"GOARM\") == \"5\"\n\tcase \"mips\", \"mipsle\", \"mips64\", \"mips64le\":\n\t\t// Be conservative and assume that all mips boards\n\t\t// have emulated floating point.\n\t\t// TODO: detect what it actually has.\n\t\treturn true\n\t}\n\treturn false\n}\n\nfunc TestFloat32(t *testing.T) {\n\t// For issue 6721, the problem came after 7533753 calls, so check 10e6.\n\tnum := int(10e6)\n\t// But do the full amount only on builders (not locally).\n\t// But ARM5 floating point emulation is slow (Issue 10749), so\n\t// do less for that builder:\n\tif testing.Short() && hasSlowFloatingPoint() { // TODO: (testenv.Builder() == \"\" || hasSlowFloatingPoint())\n\t\tnum /= 100 // 1.72 seconds instead of 172 seconds\n\t}\n\n\tr := New(NewSource(1))\n\tfor ct := 0; ct < num; ct++ {\n\t\tf := r.Float32()\n\t\tif f >= 1 {\n\t\t\tt.Fatal(\"Float32() should be in range [0,1). ct:\", ct, \"f:\", f)\n\t\t}\n\t}\n}\n\nfunc testReadUniformity(t *testing.T, n int, seed uint64) {\n\tr := New(NewSource(seed))\n\tbuf := make([]byte, n)\n\tnRead, err := r.Read(buf)\n\tif err != nil {\n\t\tt.Errorf(\"Read err %v\", err)\n\t}\n\tif nRead != n {\n\t\tt.Errorf(\"Read returned unexpected n; %d != %d\", nRead, n)\n\t}\n\n\t// Expect a uniform distribution of byte values, which lie in [0, 255].\n\tvar (\n\t\tmean       = 255.0 / 2\n\t\tstddev     = 256.0 / math.Sqrt(12.0)\n\t\terrorScale = stddev / math.Sqrt(float64(n))\n\t)\n\n\texpected := &statsResults{mean, stddev, 0.10 * errorScale, 0.08 * errorScale}\n\n\t// Cast bytes as floats to use the common distribution-validity checks.\n\tsamples := make([]float64, n)\n\tfor i, val := range buf {\n\t\tsamples[i] = float64(val)\n\t}\n\t// Make sure that the entire set matches the expected distribution.\n\tcheckSampleDistribution(t, samples, expected)\n}\n\nfunc TestReadUniformity(t *testing.T) {\n\ttestBufferSizes := []int{\n\t\t2, 4, 7, 64, 1024, 1 << 16, 1 << 20,\n\t}\n\tfor _, seed := range testSeeds {\n\t\tfor _, n := range testBufferSizes {\n\t\t\ttestReadUniformity(t, n, seed)\n\t\t}\n\t}\n}\n\nfunc TestReadEmpty(t *testing.T) {\n\tr := New(NewSource(1))\n\tbuf := make([]byte, 0)\n\tn, err := r.Read(buf)\n\tif err != nil {\n\t\tt.Errorf(\"Read err into empty buffer; %v\", err)\n\t}\n\tif n != 0 {\n\t\tt.Errorf(\"Read into empty buffer returned unexpected n of %d\", n)\n\t}\n}\n\nfunc TestReadByOneByte(t *testing.T) {\n\tr := New(NewSource(1))\n\tb1 := make([]byte, 100)\n\t_, err := io.ReadFull(iotest.OneByteReader(r), b1)\n\tif err != nil {\n\t\tt.Errorf(\"read by one byte: %v\", err)\n\t}\n\tr = New(NewSource(1))\n\tb2 := make([]byte, 100)\n\t_, err = r.Read(b2)\n\tif err != nil {\n\t\tt.Errorf(\"read: %v\", err)\n\t}\n\tif !bytes.Equal(b1, b2) {\n\t\tt.Errorf(\"read by one byte vs single read:\\n%x\\n%x\", b1, b2)\n\t}\n}\n\nfunc TestReadSeedReset(t *testing.T) {\n\tr := New(NewSource(42))\n\tb1 := make([]byte, 128)\n\t_, err := r.Read(b1)\n\tif err != nil {\n\t\tt.Errorf(\"read: %v\", err)\n\t}\n\tr.Seed(42)\n\tb2 := make([]byte, 128)\n\t_, err = r.Read(b2)\n\tif err != nil {\n\t\tt.Errorf(\"read: %v\", err)\n\t}\n\tif !bytes.Equal(b1, b2) {\n\t\tt.Errorf(\"mismatch after re-seed:\\n%x\\n%x\", b1, b2)\n\t}\n}\n\nfunc TestShuffleSmall(t *testing.T) {\n\t// Check that Shuffle allows n=0 and n=1, but that swap is never called for them.\n\tr := New(NewSource(1))\n\tfor n := 0; n <= 1; n++ {\n\t\tr.Shuffle(n, func(i, j int) { t.Fatalf(\"swap called, n=%d i=%d j=%d\", n, i, j) })\n\t}\n}\n\nfunc TestPCGSourceRoundTrip(t *testing.T) {\n\tvar src PCGSource\n\tsrc.Seed(uint64(time.Now().Unix()))\n\n\tsrc.Uint64() // Step PRNG once to makes sure high and low are different.\n\n\tbuf, err := src.MarshalBinary()\n\tif err != nil {\n\t\tt.Errorf(\"unexpected error marshaling state: %v\", err)\n\t}\n\n\tvar dst PCGSource\n\t// Get dst into a non-zero state.\n\tdst.Seed(1)\n\tfor i := 0; i < 10; i++ {\n\t\tdst.Uint64()\n\t}\n\n\terr = dst.UnmarshalBinary(buf)\n\tif err != nil {\n\t\tt.Errorf(\"unexpected error unmarshaling state: %v\", err)\n\t}\n\n\tif dst != src {\n\t\tt.Errorf(\"mismatch between generator states: got:%+v want:%+v\", dst, src)\n\t}\n}\n\n// Benchmarks\n\nfunc BenchmarkSource(b *testing.B) {\n\trng := NewSource(0)\n\tfor n := b.N; n > 0; n-- {\n\t\trng.Uint64()\n\t}\n}\n\nfunc BenchmarkInt63Threadsafe(b *testing.B) {\n\tfor n := b.N; n > 0; n-- {\n\t\tInt63()\n\t}\n}\n\nfunc BenchmarkInt63ThreadsafeParallel(b *testing.B) {\n\tb.RunParallel(func(pb *testing.PB) {\n\t\tfor pb.Next() {\n\t\t\tInt63()\n\t\t}\n\t})\n}\n\nfunc BenchmarkInt63Unthreadsafe(b *testing.B) {\n\tr := New(NewSource(1))\n\tfor n := b.N; n > 0; n-- {\n\t\tr.Int63()\n\t}\n}\n\nfunc BenchmarkIntn1000(b *testing.B) {\n\tr := New(NewSource(1))\n\tfor n := b.N; n > 0; n-- {\n\t\tr.Intn(1000)\n\t}\n}\n\nfunc BenchmarkInt63n1000(b *testing.B) {\n\tr := New(NewSource(1))\n\tfor n := b.N; n > 0; n-- {\n\t\tr.Int63n(1000)\n\t}\n}\n\nfunc BenchmarkInt31n1000(b *testing.B) {\n\tr := New(NewSource(1))\n\tfor n := b.N; n > 0; n-- {\n\t\tr.Int31n(1000)\n\t}\n}\n\nfunc BenchmarkFloat32(b *testing.B) {\n\tr := New(NewSource(1))\n\tfor n := b.N; n > 0; n-- {\n\t\tr.Float32()\n\t}\n}\n\nfunc BenchmarkFloat64(b *testing.B) {\n\tr := New(NewSource(1))\n\tfor n := b.N; n > 0; n-- {\n\t\tr.Float64()\n\t}\n}\n\nfunc BenchmarkPerm3(b *testing.B) {\n\tr := New(NewSource(1))\n\tfor n := b.N; n > 0; n-- {\n\t\tr.Perm(3)\n\t}\n}\n\nfunc BenchmarkPerm30(b *testing.B) {\n\tr := New(NewSource(1))\n\tfor n := b.N; n > 0; n-- {\n\t\tr.Perm(30)\n\t}\n}\n\nfunc BenchmarkPerm30ViaShuffle(b *testing.B) {\n\tr := New(NewSource(1))\n\tfor n := b.N; n > 0; n-- {\n\t\tp := make([]int, 30)\n\t\tfor i := range p {\n\t\t\tp[i] = i\n\t\t}\n\t\tr.Shuffle(30, func(i, j int) { p[i], p[j] = p[j], p[i] })\n\t}\n}\n\n// BenchmarkShuffleOverhead uses a minimal swap function\n// to measure just the shuffling overhead.\nfunc BenchmarkShuffleOverhead(b *testing.B) {\n\tr := New(NewSource(1))\n\tfor n := b.N; n > 0; n-- {\n\t\tr.Shuffle(52, func(i, j int) {\n\t\t\tif i < 0 || i >= 52 || j < 0 || j >= 52 {\n\t\t\t\tb.Fatalf(\"bad swap(%d, %d)\", i, j)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc BenchmarkRead3(b *testing.B) {\n\tr := New(NewSource(1))\n\tbuf := make([]byte, 3)\n\tb.ResetTimer()\n\tfor n := b.N; n > 0; n-- {\n\t\tr.Read(buf)\n\t}\n}\n\nfunc BenchmarkRead64(b *testing.B) {\n\tr := New(NewSource(1))\n\tbuf := make([]byte, 64)\n\tb.ResetTimer()\n\tfor n := b.N; n > 0; n-- {\n\t\tr.Read(buf)\n\t}\n}\n\nfunc BenchmarkRead1000(b *testing.B) {\n\tr := New(NewSource(1))\n\tbuf := make([]byte, 1000)\n\tb.ResetTimer()\n\tfor n := b.N; n > 0; n-- {\n\t\tr.Read(buf)\n\t}\n}\n"
  },
  {
    "path": "internal/rand/regress_test.go",
    "content": "// Copied from https://cs.opensource.google/go/x/exp/+/24438e51023af3bfc1db8aed43c1342817e8cfcd:rand/regress_test.go\n\n// Copyright 2014 The Go Authors.  All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\n// Test that random number sequences generated by a specific seed\n// do not change from version to version.\n//\n// If the generator changes, the golden outputs need updating, and\n// client programs may break. Although the desire for compatibility\n// is not as stringent as in the original math/rand package,\n// when possible avoid changing the generator.\n\npackage rand_test\n\nimport (\n\t\"flag\"\n\t\"fmt\"\n\t\"reflect\"\n\t\"testing\"\n\n\t. \"go.mongodb.org/mongo-driver/v2/internal/rand\"\n)\n\nvar printgolden = flag.Bool(\"printgolden\", false, \"print golden results for regression test\")\n\n// TestSource verifies that the output of the default Source is locked down.\nfunc TestSourceRegress(t *testing.T) {\n\tsrc := NewSource(1)\n\tvar got [20]uint64\n\tfor i := range got {\n\t\tgot[i] = src.Uint64()\n\t}\n\twant := [20]uint64{\n\t\t0x34e936394905d167,\n\t\t0x817c0ef62fe4c731,\n\t\t0x937987e6e24f5a40,\n\t\t0x0c0a8307fe226199,\n\t\t0xf96363568d8bab56,\n\t\t0xbaef3af36bd02620,\n\t\t0x8f18e416eb6b936b,\n\t\t0x05a43fc149f3a67a,\n\t\t0xdab012eb3ce01697,\n\t\t0xf76c495a133c6aa9,\n\t\t0x304b24c5040ce457,\n\t\t0x47d77e0abb413159,\n\t\t0x52a810fa9e452f04,\n\t\t0x2d24b66380cf4780,\n\t\t0x5ec7691b92018ef5,\n\t\t0x5076dfa749261ea0,\n\t\t0xac8f11ad3941d213,\n\t\t0x13fa8d67de91db25,\n\t\t0xb50883a9893274eb,\n\t\t0xeb8f59263f9109ac,\n\t}\n\tif got != want {\n\t\tt.Errorf(\"got:\\n\\t%#016x\\nwant:\\n\\t%#016x\", got, want)\n\t\tif *printgolden {\n\t\t\tfor _, x := range got {\n\t\t\t\tfmt.Printf(\"\\t\\t%#016x,\\n\", x)\n\t\t\t}\n\t\t}\n\t}\n}\n\n// TestRegress validates that the output stream is locked down, for instance so\n// optimizations do not change the output. It iterates over methods of the\n// Rand type to find functions to evaluate and checks the first 20 results\n// against the golden results.\nfunc TestRegress(t *testing.T) {\n\tint32s := []int32{1, 10, 32, 1 << 20, 1<<20 + 1, 1000000000, 1 << 30, 1<<31 - 2, 1<<31 - 1}\n\tint64s := []int64{1, 10, 32, 1 << 20, 1<<20 + 1, 1000000000, 1 << 30, 1<<31 - 2, 1<<31 - 1, 1000000000000000000, 1 << 60, 1<<63 - 2, 1<<63 - 1}\n\tuint64s := []uint64{1, 10, 32, 1 << 20, 1<<20 + 1, 1000000000, 1 << 30, 1<<31 - 2, 1<<31 - 1, 1000000000000000000, 1 << 60, 1<<64 - 2, 1<<64 - 1}\n\tpermSizes := []int{0, 1, 5, 8, 9, 10, 16}\n\treadBufferSizes := []int{1, 7, 8, 9, 10}\n\tr := New(NewSource(0))\n\n\trv := reflect.ValueOf(r)\n\tn := rv.NumMethod()\n\tp := 0\n\tif *printgolden {\n\t\tfmt.Printf(\"var regressGolden = []interface{}{\\n\")\n\t}\n\tfor i := 0; i < n; i++ {\n\t\tm := rv.Type().Method(i)\n\t\tmv := rv.Method(i)\n\t\tmt := mv.Type()\n\t\tif mt.NumOut() == 0 {\n\t\t\tcontinue\n\t\t}\n\t\tr.Seed(0)\n\t\tif *printgolden && i > 0 {\n\t\t\tfmt.Println()\n\t\t}\n\t\tfor repeat := 0; repeat < 20; repeat++ {\n\t\t\tvar args []reflect.Value\n\t\t\tvar argstr string\n\t\t\tif mt.NumIn() == 1 {\n\t\t\t\tvar x interface{}\n\t\t\t\tswitch mt.In(0).Kind() {\n\t\t\t\tdefault:\n\t\t\t\t\tt.Fatalf(\"unexpected argument type for r.%s\", m.Name)\n\n\t\t\t\tcase reflect.Int:\n\t\t\t\t\tif m.Name == \"Perm\" {\n\t\t\t\t\t\tx = permSizes[repeat%len(permSizes)]\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t\tbig := int64s[repeat%len(int64s)]\n\t\t\t\t\tif int64(int(big)) != big {\n\t\t\t\t\t\tr.Int63n(big) // what would happen on 64-bit machine, to keep stream in sync\n\t\t\t\t\t\tif *printgolden {\n\t\t\t\t\t\t\tfmt.Printf(\"\\tskipped, // must run printgolden on 64-bit machine\\n\")\n\t\t\t\t\t\t}\n\t\t\t\t\t\tp++\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tx = int(big)\n\n\t\t\t\tcase reflect.Int32:\n\t\t\t\t\tx = int32s[repeat%len(int32s)]\n\n\t\t\t\tcase reflect.Int64:\n\t\t\t\t\tx = int64s[repeat%len(int64s)]\n\n\t\t\t\tcase reflect.Uint64:\n\t\t\t\t\tx = uint64s[repeat%len(uint64s)]\n\n\t\t\t\tcase reflect.Slice:\n\t\t\t\t\tif m.Name == \"Read\" {\n\t\t\t\t\t\tn := readBufferSizes[repeat%len(readBufferSizes)]\n\t\t\t\t\t\tx = make([]byte, n)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\targstr = fmt.Sprint(x)\n\t\t\t\targs = append(args, reflect.ValueOf(x))\n\t\t\t}\n\n\t\t\tvar out interface{}\n\t\t\tout = mv.Call(args)[0].Interface()\n\t\t\tif m.Name == \"Int\" || m.Name == \"Intn\" {\n\t\t\t\tout = int64(out.(int))\n\t\t\t}\n\t\t\tif m.Name == \"Read\" {\n\t\t\t\tout = args[0].Interface().([]byte)\n\t\t\t}\n\t\t\tif *printgolden {\n\t\t\t\tvar val string\n\t\t\t\tbig := int64(1 << 60)\n\t\t\t\tif int64(int(big)) != big && (m.Name == \"Int\" || m.Name == \"Intn\") {\n\t\t\t\t\t// 32-bit machine cannot print 64-bit results\n\t\t\t\t\tval = \"truncated\"\n\t\t\t\t} else if reflect.TypeOf(out).Kind() == reflect.Slice {\n\t\t\t\t\tval = fmt.Sprintf(\"%#v\", out)\n\t\t\t\t} else {\n\t\t\t\t\tval = fmt.Sprintf(\"%T(%v)\", out, out)\n\t\t\t\t}\n\t\t\t\tfmt.Printf(\"\\t%s, // %s(%s)\\n\", val, m.Name, argstr)\n\t\t\t} else {\n\t\t\t\twant := regressGolden[p]\n\t\t\t\tif m.Name == \"Int\" {\n\t\t\t\t\twant = int64(int(uint(want.(int64)) << 1 >> 1))\n\t\t\t\t}\n\t\t\t\tif !reflect.DeepEqual(out, want) {\n\t\t\t\t\tt.Errorf(\"r.%s(%s) = %v, want %v\", m.Name, argstr, out, want)\n\t\t\t\t}\n\t\t\t}\n\t\t\tp++\n\t\t}\n\t}\n\tif *printgolden {\n\t\tfmt.Printf(\"}\\n\")\n\t}\n}\n\nvar regressGolden = []interface{}{\n\tfloat64(0.6279600685109523),   // ExpFloat64()\n\tfloat64(0.16198826513357806),  // ExpFloat64()\n\tfloat64(0.007880404652650552), // ExpFloat64()\n\tfloat64(0.41649788761745654),  // ExpFloat64()\n\tfloat64(1.6958707787276301),   // ExpFloat64()\n\tfloat64(2.7227327706138036),   // ExpFloat64()\n\tfloat64(2.4235600263079657),   // ExpFloat64()\n\tfloat64(1.277967771105418),    // ExpFloat64()\n\tfloat64(0.7111660437031769),   // ExpFloat64()\n\tfloat64(0.23090401427981888),  // ExpFloat64()\n\tfloat64(1.4746763588379928),   // ExpFloat64()\n\tfloat64(1.4868726779832278),   // ExpFloat64()\n\tfloat64(0.1686257242078103),   // ExpFloat64()\n\tfloat64(0.2732721816228957),   // ExpFloat64()\n\tfloat64(0.4644536065869748),   // ExpFloat64()\n\tfloat64(0.01319850986379164),  // ExpFloat64()\n\tfloat64(0.7184492551742854),   // ExpFloat64()\n\tfloat64(0.1913536422195827),   // ExpFloat64()\n\tfloat64(0.16034475958495667),  // ExpFloat64()\n\tfloat64(0.40599859014785644),  // ExpFloat64()\n\n\tfloat32(0.7979972),   // Float32()\n\tfloat32(0.7725961),   // Float32()\n\tfloat32(0.21894403),  // Float32()\n\tfloat32(0.96194494),  // Float32()\n\tfloat32(0.2915732),   // Float32()\n\tfloat32(0.59569645),  // Float32()\n\tfloat32(0.99596655),  // Float32()\n\tfloat32(0.4979039),   // Float32()\n\tfloat32(0.98148686),  // Float32()\n\tfloat32(0.01380035),  // Float32()\n\tfloat32(0.086487144), // Float32()\n\tfloat32(0.6114401),   // Float32()\n\tfloat32(0.71081316),  // Float32()\n\tfloat32(0.6342346),   // Float32()\n\tfloat32(0.008082573), // Float32()\n\tfloat32(0.33020085),  // Float32()\n\tfloat32(0.032625034), // Float32()\n\tfloat32(0.9278005),   // Float32()\n\tfloat32(0.34497985),  // Float32()\n\tfloat32(0.66506875),  // Float32()\n\n\tfloat64(0.797997151016231),    // Float64()\n\tfloat64(0.7725961454373316),   // Float64()\n\tfloat64(0.21894402538580782),  // Float64()\n\tfloat64(0.9619449481780457),   // Float64()\n\tfloat64(0.2915731877602916),   // Float64()\n\tfloat64(0.5956964580775652),   // Float64()\n\tfloat64(0.9959665347028619),   // Float64()\n\tfloat64(0.49790390966591147),  // Float64()\n\tfloat64(0.9814868602566785),   // Float64()\n\tfloat64(0.013800350332924483), // Float64()\n\tfloat64(0.08648714463652596),  // Float64()\n\tfloat64(0.6114401479210267),   // Float64()\n\tfloat64(0.7108131531183706),   // Float64()\n\tfloat64(0.6342346133706837),   // Float64()\n\tfloat64(0.008082572853887138), // Float64()\n\tfloat64(0.3302008651926287),   // Float64()\n\tfloat64(0.03262503454637655),  // Float64()\n\tfloat64(0.9278004634858956),   // Float64()\n\tfloat64(0.3449798628384906),   // Float64()\n\tfloat64(0.665068719316529),    // Float64()\n\n\tint64(5474557666971700975), // Int()\n\tint64(5591422465364813936), // Int()\n\tint64(74029666500212977),   // Int()\n\tint64(8088122161323000979), // Int()\n\tint64(7298457654139700474), // Int()\n\tint64(1590632625527662686), // Int()\n\tint64(9052198920789078554), // Int()\n\tint64(7381380909356947872), // Int()\n\tint64(1738222704626512495), // Int()\n\tint64(3278744831230954970), // Int()\n\tint64(7062423222661652521), // Int()\n\tint64(6715870808026712034), // Int()\n\tint64(528819992478005418),  // Int()\n\tint64(2284534088986354339), // Int()\n\tint64(945828723091990082),  // Int()\n\tint64(3813019469742317492), // Int()\n\tint64(1369388146907482806), // Int()\n\tint64(7367238674766648970), // Int()\n\tint64(8217673022687244206), // Int()\n\tint64(3185531743396549562), // Int()\n\n\tint32(1711064216), // Int31()\n\tint32(650927245),  // Int31()\n\tint32(8618187),    // Int31()\n\tint32(941581344),  // Int31()\n\tint32(1923394120), // Int31()\n\tint32(1258915833), // Int31()\n\tint32(1053814650), // Int31()\n\tint32(859305834),  // Int31()\n\tint32(1276097579), // Int31()\n\tint32(1455437958), // Int31()\n\tint32(1895916096), // Int31()\n\tint32(781830261),  // Int31()\n\tint32(61562749),   // Int31()\n\tint32(265954771),  // Int31()\n\tint32(1183850779), // Int31()\n\tint32(443893888),  // Int31()\n\tint32(1233159585), // Int31()\n\tint32(857659461),  // Int31()\n\tint32(956663049),  // Int31()\n\tint32(370844703),  // Int31()\n\n\tint32(0),          // Int31n(1)\n\tint32(6),          // Int31n(10)\n\tint32(17),         // Int31n(32)\n\tint32(1000595),    // Int31n(1048576)\n\tint32(424333),     // Int31n(1048577)\n\tint32(382438494),  // Int31n(1000000000)\n\tint32(902738458),  // Int31n(1073741824)\n\tint32(1204933878), // Int31n(2147483646)\n\tint32(1376191263), // Int31n(2147483647)\n\tint32(0),          // Int31n(1)\n\tint32(9),          // Int31n(10)\n\tint32(2),          // Int31n(32)\n\tint32(440490),     // Int31n(1048576)\n\tint32(176312),     // Int31n(1048577)\n\tint32(946765890),  // Int31n(1000000000)\n\tint32(665034676),  // Int31n(1073741824)\n\tint32(1947285452), // Int31n(2147483646)\n\tint32(1702344608), // Int31n(2147483647)\n\tint32(0),          // Int31n(1)\n\tint32(2),          // Int31n(10)\n\n\tint64(5474557666971700975), // Int63()\n\tint64(5591422465364813936), // Int63()\n\tint64(74029666500212977),   // Int63()\n\tint64(8088122161323000979), // Int63()\n\tint64(7298457654139700474), // Int63()\n\tint64(1590632625527662686), // Int63()\n\tint64(9052198920789078554), // Int63()\n\tint64(7381380909356947872), // Int63()\n\tint64(1738222704626512495), // Int63()\n\tint64(3278744831230954970), // Int63()\n\tint64(7062423222661652521), // Int63()\n\tint64(6715870808026712034), // Int63()\n\tint64(528819992478005418),  // Int63()\n\tint64(2284534088986354339), // Int63()\n\tint64(945828723091990082),  // Int63()\n\tint64(3813019469742317492), // Int63()\n\tint64(1369388146907482806), // Int63()\n\tint64(7367238674766648970), // Int63()\n\tint64(8217673022687244206), // Int63()\n\tint64(3185531743396549562), // Int63()\n\n\tint64(0),                   // Int63n(1)\n\tint64(6),                   // Int63n(10)\n\tint64(17),                  // Int63n(32)\n\tint64(1000595),             // Int63n(1048576)\n\tint64(424333),              // Int63n(1048577)\n\tint64(382438494),           // Int63n(1000000000)\n\tint64(902738458),           // Int63n(1073741824)\n\tint64(1204933878),          // Int63n(2147483646)\n\tint64(1376191263),          // Int63n(2147483647)\n\tint64(502116868085730778),  // Int63n(1000000000000000000)\n\tint64(144894195020570665),  // Int63n(1152921504606846976)\n\tint64(6715870808026712034), // Int63n(9223372036854775806)\n\tint64(528819992478005418),  // Int63n(9223372036854775807)\n\tint64(0),                   // Int63n(1)\n\tint64(0),                   // Int63n(10)\n\tint64(20),                  // Int63n(32)\n\tint64(854710),              // Int63n(1048576)\n\tint64(649893),              // Int63n(1048577)\n\tint64(687244206),           // Int63n(1000000000)\n\tint64(836883386),           // Int63n(1073741824)\n\n\tint64(0),                   // Intn(1)\n\tint64(6),                   // Intn(10)\n\tint64(17),                  // Intn(32)\n\tint64(1000595),             // Intn(1048576)\n\tint64(424333),              // Intn(1048577)\n\tint64(382438494),           // Intn(1000000000)\n\tint64(902738458),           // Intn(1073741824)\n\tint64(1204933878),          // Intn(2147483646)\n\tint64(1376191263),          // Intn(2147483647)\n\tint64(502116868085730778),  // Intn(1000000000000000000)\n\tint64(144894195020570665),  // Intn(1152921504606846976)\n\tint64(6715870808026712034), // Intn(9223372036854775806)\n\tint64(528819992478005418),  // Intn(9223372036854775807)\n\tint64(0),                   // Intn(1)\n\tint64(0),                   // Intn(10)\n\tint64(20),                  // Intn(32)\n\tint64(854710),              // Intn(1048576)\n\tint64(649893),              // Intn(1048577)\n\tint64(687244206),           // Intn(1000000000)\n\tint64(836883386),           // Intn(1073741824)\n\n\tfloat64(-0.5410658516792047),  // NormFloat64()\n\tfloat64(0.615296849055287),    // NormFloat64()\n\tfloat64(0.007477442280032887), // NormFloat64()\n\tfloat64(1.3443892057169684),   // NormFloat64()\n\tfloat64(-0.17508902754863512), // NormFloat64()\n\tfloat64(-2.03494397556937),    // NormFloat64()\n\tfloat64(2.5213558871972306),   // NormFloat64()\n\tfloat64(1.4572921639613627),   // NormFloat64()\n\tfloat64(-1.5164961164210644),  // NormFloat64()\n\tfloat64(-0.4861150771891445),  // NormFloat64()\n\tfloat64(-0.8699409548614199),  // NormFloat64()\n\tfloat64(1.6271559815452794),   // NormFloat64()\n\tfloat64(0.1659465769926195),   // NormFloat64()\n\tfloat64(0.2921716191987018),   // NormFloat64()\n\tfloat64(-1.2550269636927838),  // NormFloat64()\n\tfloat64(0.11257973349467548),  // NormFloat64()\n\tfloat64(0.5437525915836436),   // NormFloat64()\n\tfloat64(0.781754430770282),    // NormFloat64()\n\tfloat64(0.5201256313962235),   // NormFloat64()\n\tfloat64(1.3826174159276245),   // NormFloat64()\n\n\t[]int{},                             // Perm(0)\n\t[]int{0},                            // Perm(1)\n\t[]int{0, 2, 3, 1, 4},                // Perm(5)\n\t[]int{5, 6, 3, 7, 4, 2, 0, 1},       // Perm(8)\n\t[]int{8, 4, 5, 2, 7, 3, 0, 6, 1},    // Perm(9)\n\t[]int{6, 1, 5, 3, 2, 9, 7, 0, 8, 4}, // Perm(10)\n\t[]int{12, 5, 1, 9, 15, 7, 13, 6, 10, 11, 8, 0, 4, 2, 14, 3}, // Perm(16)\n\t[]int{},                             // Perm(0)\n\t[]int{0},                            // Perm(1)\n\t[]int{0, 2, 3, 4, 1},                // Perm(5)\n\t[]int{3, 2, 7, 4, 0, 6, 5, 1},       // Perm(8)\n\t[]int{0, 6, 2, 1, 3, 7, 5, 8, 4},    // Perm(9)\n\t[]int{2, 5, 6, 4, 7, 3, 0, 8, 1, 9}, // Perm(10)\n\t[]int{3, 6, 5, 4, 9, 15, 13, 7, 1, 11, 10, 8, 12, 0, 2, 14}, // Perm(16)\n\t[]int{},                             // Perm(0)\n\t[]int{0},                            // Perm(1)\n\t[]int{2, 4, 3, 1, 0},                // Perm(5)\n\t[]int{1, 6, 7, 5, 4, 3, 2, 0},       // Perm(8)\n\t[]int{7, 6, 8, 2, 0, 1, 3, 4, 5},    // Perm(9)\n\t[]int{2, 9, 7, 1, 5, 4, 0, 6, 8, 3}, // Perm(10)\n\n\t[]byte{0xef}, // Read([0])\n\t[]byte{0x4e, 0x3d, 0x52, 0x31, 0x89, 0xf9, 0xcb},                   // Read([0 0 0 0 0 0 0])\n\t[]byte{0x70, 0x68, 0x35, 0x8d, 0x1b, 0xb9, 0x98, 0x4d},             // Read([0 0 0 0 0 0 0 0])\n\t[]byte{0xf1, 0xf8, 0x95, 0xe6, 0x96, 0x1, 0x7, 0x1, 0x93},          // Read([0 0 0 0 0 0 0 0 0])\n\t[]byte{0x44, 0x9f, 0xc5, 0x40, 0xc8, 0x3e, 0x70, 0xfa, 0x44, 0x3a}, // Read([0 0 0 0 0 0 0 0 0 0])\n\t[]byte{0x4b}, // Read([0])\n\t[]byte{0x91, 0x54, 0x49, 0xe5, 0x5e, 0x28, 0xb9},                   // Read([0 0 0 0 0 0 0])\n\t[]byte{0x4, 0xf2, 0xf, 0x13, 0x96, 0x1a, 0xb2, 0xce},               // Read([0 0 0 0 0 0 0 0])\n\t[]byte{0x35, 0xf5, 0xde, 0x9f, 0x7d, 0xa0, 0x19, 0x12, 0x2e},       // Read([0 0 0 0 0 0 0 0 0])\n\t[]byte{0xd4, 0xee, 0x6f, 0x66, 0x6f, 0x32, 0xc8, 0x21, 0x57, 0x68}, // Read([0 0 0 0 0 0 0 0 0 0])\n\t[]byte{0x1f}, // Read([0])\n\t[]byte{0x98, 0xda, 0x4d, 0xab, 0x6e, 0xd, 0x71},                   // Read([0 0 0 0 0 0 0])\n\t[]byte{0x80, 0xad, 0x29, 0xa0, 0x37, 0xb0, 0x80, 0xc4},            // Read([0 0 0 0 0 0 0 0])\n\t[]byte{0x2, 0xe2, 0xe2, 0x7, 0xd9, 0xed, 0xea, 0x90, 0x33},        // Read([0 0 0 0 0 0 0 0 0])\n\t[]byte{0x5d, 0xaa, 0xb8, 0xc6, 0x39, 0xfb, 0xbe, 0x56, 0x7, 0xa3}, // Read([0 0 0 0 0 0 0 0 0 0])\n\t[]byte{0x62}, // Read([0])\n\t[]byte{0x4d, 0x63, 0xa6, 0x4b, 0xb4, 0x1f, 0x42},                // Read([0 0 0 0 0 0 0])\n\t[]byte{0x66, 0x42, 0x62, 0x36, 0x42, 0x20, 0x8d, 0xb4},          // Read([0 0 0 0 0 0 0 0])\n\t[]byte{0x9f, 0xa3, 0x67, 0x1, 0x91, 0xea, 0x34, 0xb6, 0xa},      // Read([0 0 0 0 0 0 0 0 0])\n\t[]byte{0xd, 0xa8, 0x43, 0xb, 0x1, 0x93, 0x8a, 0x56, 0xfc, 0x98}, // Read([0 0 0 0 0 0 0 0 0 0])\n\n\tuint32(3422128433), // Uint32()\n\tuint32(1301854491), // Uint32()\n\tuint32(17236374),   // Uint32()\n\tuint32(1883162688), // Uint32()\n\tuint32(3846788241), // Uint32()\n\tuint32(2517831666), // Uint32()\n\tuint32(2107629301), // Uint32()\n\tuint32(1718611668), // Uint32()\n\tuint32(2552195159), // Uint32()\n\tuint32(2910875917), // Uint32()\n\tuint32(3791832192), // Uint32()\n\tuint32(1563660522), // Uint32()\n\tuint32(123125499),  // Uint32()\n\tuint32(531909542),  // Uint32()\n\tuint32(2367701558), // Uint32()\n\tuint32(887787777),  // Uint32()\n\tuint32(2466319171), // Uint32()\n\tuint32(1715318922), // Uint32()\n\tuint32(1913326099), // Uint32()\n\tuint32(741689406),  // Uint32()\n\n\tuint64(14697929703826476783), // Uint64()\n\tuint64(5591422465364813936),  // Uint64()\n\tuint64(74029666500212977),    // Uint64()\n\tuint64(8088122161323000979),  // Uint64()\n\tuint64(16521829690994476282), // Uint64()\n\tuint64(10814004662382438494), // Uint64()\n\tuint64(9052198920789078554),  // Uint64()\n\tuint64(7381380909356947872),  // Uint64()\n\tuint64(10961594741481288303), // Uint64()\n\tuint64(12502116868085730778), // Uint64()\n\tuint64(16285795259516428329), // Uint64()\n\tuint64(6715870808026712034),  // Uint64()\n\tuint64(528819992478005418),   // Uint64()\n\tuint64(2284534088986354339),  // Uint64()\n\tuint64(10169200759946765890), // Uint64()\n\tuint64(3813019469742317492),  // Uint64()\n\tuint64(10592760183762258614), // Uint64()\n\tuint64(7367238674766648970),  // Uint64()\n\tuint64(8217673022687244206),  // Uint64()\n\tuint64(3185531743396549562),  // Uint64()\n\n\tuint64(0),                   // Uint64n(1)\n\tuint64(6),                   // Uint64n(10)\n\tuint64(17),                  // Uint64n(32)\n\tuint64(1000595),             // Uint64n(1048576)\n\tuint64(424333),              // Uint64n(1048577)\n\tuint64(382438494),           // Uint64n(1000000000)\n\tuint64(902738458),           // Uint64n(1073741824)\n\tuint64(1204933878),          // Uint64n(2147483646)\n\tuint64(1376191263),          // Uint64n(2147483647)\n\tuint64(502116868085730778),  // Uint64n(1000000000000000000)\n\tuint64(144894195020570665),  // Uint64n(1152921504606846976)\n\tuint64(6715870808026712034), // Uint64n(18446744073709551614)\n\tuint64(528819992478005418),  // Uint64n(18446744073709551615)\n\tuint64(0),                   // Uint64n(1)\n\tuint64(0),                   // Uint64n(10)\n\tuint64(20),                  // Uint64n(32)\n\tuint64(854710),              // Uint64n(1048576)\n\tuint64(649893),              // Uint64n(1048577)\n\tuint64(687244206),           // Uint64n(1000000000)\n\tuint64(836883386),           // Uint64n(1073741824)\n}\n"
  },
  {
    "path": "internal/rand/rng.go",
    "content": "// Copied from https://cs.opensource.google/go/x/exp/+/24438e51023af3bfc1db8aed43c1342817e8cfcd:rand/rng.go\n\n// Copyright 2017 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage rand\n\nimport (\n\t\"encoding/binary\"\n\t\"io\"\n\t\"math/bits\"\n)\n\n// PCGSource is an implementation of a 64-bit permuted congruential\n// generator as defined in\n//\n//\tPCG: A Family of Simple Fast Space-Efficient Statistically Good\n//\tAlgorithms for Random Number Generation\n//\tMelissa E. O’Neill, Harvey Mudd College\n//\thttp://www.pcg-random.org/pdf/toms-oneill-pcg-family-v1.02.pdf\n//\n// The generator here is the congruential generator PCG XSL RR 128/64 (LCG)\n// as found in the software available at http://www.pcg-random.org/.\n// It has period 2^128 with 128 bits of state, producing 64-bit values.\n// Is state is represented by two uint64 words.\ntype PCGSource struct {\n\tlow  uint64\n\thigh uint64\n}\n\nconst (\n\tmaxUint32 = (1 << 32) - 1\n\n\tmultiplier = 47026247687942121848144207491837523525\n\tmulHigh    = multiplier >> 64\n\tmulLow     = multiplier & maxUint64\n\n\tincrement = 117397592171526113268558934119004209487\n\tincHigh   = increment >> 64\n\tincLow    = increment & maxUint64\n\n\t// TODO: Use these?\n\tinitializer = 245720598905631564143578724636268694099\n\tinitHigh    = initializer >> 64\n\tinitLow     = initializer & maxUint64\n)\n\n// Seed uses the provided seed value to initialize the generator to a deterministic state.\nfunc (pcg *PCGSource) Seed(seed uint64) {\n\tpcg.low = seed\n\tpcg.high = seed // TODO: What is right?\n}\n\n// Uint64 returns a pseudo-random 64-bit unsigned integer as a uint64.\nfunc (pcg *PCGSource) Uint64() uint64 {\n\tpcg.multiply()\n\tpcg.add()\n\t// XOR high and low 64 bits together and rotate right by high 6 bits of state.\n\treturn bits.RotateLeft64(pcg.high^pcg.low, -int(pcg.high>>58))\n}\n\nfunc (pcg *PCGSource) add() {\n\tvar carry uint64\n\tpcg.low, carry = Add64(pcg.low, incLow, 0)\n\tpcg.high, _ = Add64(pcg.high, incHigh, carry)\n}\n\nfunc (pcg *PCGSource) multiply() {\n\thi, lo := Mul64(pcg.low, mulLow)\n\thi += pcg.high * mulLow\n\thi += pcg.low * mulHigh\n\tpcg.low = lo\n\tpcg.high = hi\n}\n\n// MarshalBinary returns the binary representation of the current state of the generator.\nfunc (pcg *PCGSource) MarshalBinary() ([]byte, error) {\n\tvar buf [16]byte\n\tbinary.BigEndian.PutUint64(buf[:8], pcg.high)\n\tbinary.BigEndian.PutUint64(buf[8:], pcg.low)\n\treturn buf[:], nil\n}\n\n// UnmarshalBinary sets the state of the generator to the state represented in data.\nfunc (pcg *PCGSource) UnmarshalBinary(data []byte) error {\n\tif len(data) < 16 {\n\t\treturn io.ErrUnexpectedEOF\n\t}\n\tpcg.low = binary.BigEndian.Uint64(data[8:])\n\tpcg.high = binary.BigEndian.Uint64(data[:8])\n\treturn nil\n}\n"
  },
  {
    "path": "internal/randutil/randutil.go",
    "content": "// Copyright (C) MongoDB, Inc. 2022-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\n// Package randutil provides common random number utilities.\npackage randutil\n\nimport (\n\tcrand \"crypto/rand\"\n\t\"fmt\"\n\t\"io\"\n\n\txrand \"go.mongodb.org/mongo-driver/v2/internal/rand\"\n)\n\n// NewLockedRand returns a new \"x/exp/rand\" pseudo-random number generator seeded with a\n// cryptographically-secure random number.\n// It is safe to use from multiple goroutines.\nfunc NewLockedRand() *xrand.Rand {\n\trandSrc := new(xrand.LockedSource)\n\trandSrc.Seed(cryptoSeed())\n\treturn xrand.New(randSrc)\n}\n\n// cryptoSeed returns a random uint64 read from the \"crypto/rand\" random number generator. It is\n// intended to be used to seed pseudorandom number generators at package initialization. It panics\n// if it encounters any errors.\nfunc cryptoSeed() uint64 {\n\tvar b [8]byte\n\t_, err := io.ReadFull(crand.Reader, b[:])\n\tif err != nil {\n\t\tpanic(fmt.Errorf(\"failed to read 8 bytes from a \\\"crypto/rand\\\".Reader: %v\", err))\n\t}\n\n\treturn (uint64(b[0]) << 0) | (uint64(b[1]) << 8) | (uint64(b[2]) << 16) | (uint64(b[3]) << 24) |\n\t\t(uint64(b[4]) << 32) | (uint64(b[5]) << 40) | (uint64(b[6]) << 48) | (uint64(b[7]) << 56)\n}\n"
  },
  {
    "path": "internal/randutil/randutil_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2022-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage randutil\n\nimport (\n\t\"testing\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/require\"\n)\n\nfunc TestCryptoSeed(t *testing.T) {\n\tseeds := make(map[uint64]bool)\n\tfor i := 1; i < 1000000; i++ {\n\t\ts := cryptoSeed()\n\t\trequire.False(t, seeds[s], \"cryptoSeed returned a duplicate value %d\", s)\n\t\tseeds[s] = true\n\t}\n}\n"
  },
  {
    "path": "internal/require/require.go",
    "content": "// Copied from https://github.com/stretchr/testify/blob/1333b5d3bda8cf5aedcf3e1aaa95cac28aaab892/require/require.go\n\n// Copyright 2020 Mat Ryer, Tyler Bunnell and all contributors. All rights reserved.\n// Use of this source code is governed by an MIT-style license that can be found in\n// the THIRD-PARTY-NOTICES file.\n\npackage require\n\nimport (\n\ttime \"time\"\n\n\tassert \"go.mongodb.org/mongo-driver/v2/internal/assert\"\n)\n\n// TestingT is an interface wrapper around *testing.T\ntype TestingT interface {\n\tErrorf(format string, args ...interface{})\n\tFailNow()\n}\n\ntype tHelper interface {\n\tHelper()\n}\n\n// Contains asserts that the specified string, list(array, slice...) or map contains the\n// specified substring or element.\n//\n//\tassert.Contains(t, \"Hello World\", \"World\")\n//\tassert.Contains(t, [\"Hello\", \"World\"], \"World\")\n//\tassert.Contains(t, {\"Hello\": \"World\"}, \"Hello\")\nfunc Contains(t TestingT, s interface{}, contains interface{}, msgAndArgs ...interface{}) {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\tif assert.Contains(t, s, contains, msgAndArgs...) {\n\t\treturn\n\t}\n\tt.FailNow()\n}\n\n// Containsf asserts that the specified string, list(array, slice...) or map contains the\n// specified substring or element.\n//\n//\tassert.Containsf(t, \"Hello World\", \"World\", \"error message %s\", \"formatted\")\n//\tassert.Containsf(t, [\"Hello\", \"World\"], \"World\", \"error message %s\", \"formatted\")\n//\tassert.Containsf(t, {\"Hello\": \"World\"}, \"Hello\", \"error message %s\", \"formatted\")\nfunc Containsf(t TestingT, s interface{}, contains interface{}, msg string, args ...interface{}) {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\tif assert.Containsf(t, s, contains, msg, args...) {\n\t\treturn\n\t}\n\tt.FailNow()\n}\n\n// ElementsMatch asserts that the specified listA(array, slice...) is equal to specified\n// listB(array, slice...) ignoring the order of the elements. If there are duplicate elements,\n// the number of appearances of each of them in both lists should match.\n//\n// assert.ElementsMatch(t, [1, 3, 2, 3], [1, 3, 3, 2])\nfunc ElementsMatch(t TestingT, listA interface{}, listB interface{}, msgAndArgs ...interface{}) {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\tif assert.ElementsMatch(t, listA, listB, msgAndArgs...) {\n\t\treturn\n\t}\n\tt.FailNow()\n}\n\n// ElementsMatchf asserts that the specified listA(array, slice...) is equal to specified\n// listB(array, slice...) ignoring the order of the elements. If there are duplicate elements,\n// the number of appearances of each of them in both lists should match.\n//\n// assert.ElementsMatchf(t, [1, 3, 2, 3], [1, 3, 3, 2], \"error message %s\", \"formatted\")\nfunc ElementsMatchf(t TestingT, listA interface{}, listB interface{}, msg string, args ...interface{}) {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\tif assert.ElementsMatchf(t, listA, listB, msg, args...) {\n\t\treturn\n\t}\n\tt.FailNow()\n}\n\n// Equal asserts that two objects are equal.\n//\n//\tassert.Equal(t, 123, 123)\n//\n// Pointer variable equality is determined based on the equality of the\n// referenced values (as opposed to the memory addresses). Function equality\n// cannot be determined and will always fail.\nfunc Equal(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...interface{}) {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\tif assert.Equal(t, expected, actual, msgAndArgs...) {\n\t\treturn\n\t}\n\tt.FailNow()\n}\n\n// EqualError asserts that a function returned an error (i.e. not `nil`)\n// and that it is equal to the provided error.\n//\n//\tactualObj, err := SomeFunction()\n//\tassert.EqualError(t, err,  expectedErrorString)\nfunc EqualError(t TestingT, theError error, errString string, msgAndArgs ...interface{}) {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\tif assert.EqualError(t, theError, errString, msgAndArgs...) {\n\t\treturn\n\t}\n\tt.FailNow()\n}\n\n// EqualErrorf asserts that a function returned an error (i.e. not `nil`)\n// and that it is equal to the provided error.\n//\n//\tactualObj, err := SomeFunction()\n//\tassert.EqualErrorf(t, err,  expectedErrorString, \"error message %s\", \"formatted\")\nfunc EqualErrorf(t TestingT, theError error, errString string, msg string, args ...interface{}) {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\tif assert.EqualErrorf(t, theError, errString, msg, args...) {\n\t\treturn\n\t}\n\tt.FailNow()\n}\n\n// EqualValues asserts that two objects are equal or convertible to the same types\n// and equal.\n//\n//\tassert.EqualValues(t, uint32(123), int32(123))\nfunc EqualValues(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...interface{}) {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\tif assert.EqualValues(t, expected, actual, msgAndArgs...) {\n\t\treturn\n\t}\n\tt.FailNow()\n}\n\n// EqualValuesf asserts that two objects are equal or convertible to the same types\n// and equal.\n//\n//\tassert.EqualValuesf(t, uint32(123), int32(123), \"error message %s\", \"formatted\")\nfunc EqualValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\tif assert.EqualValuesf(t, expected, actual, msg, args...) {\n\t\treturn\n\t}\n\tt.FailNow()\n}\n\n// Equalf asserts that two objects are equal.\n//\n//\tassert.Equalf(t, 123, 123, \"error message %s\", \"formatted\")\n//\n// Pointer variable equality is determined based on the equality of the\n// referenced values (as opposed to the memory addresses). Function equality\n// cannot be determined and will always fail.\nfunc Equalf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\tif assert.Equalf(t, expected, actual, msg, args...) {\n\t\treturn\n\t}\n\tt.FailNow()\n}\n\n// Error asserts that a function returned an error (i.e. not `nil`).\n//\n//\t  actualObj, err := SomeFunction()\n//\t  if assert.Error(t, err) {\n//\t\t   assert.Equal(t, expectedError, err)\n//\t  }\nfunc Error(t TestingT, err error, msgAndArgs ...interface{}) {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\tif assert.Error(t, err, msgAndArgs...) {\n\t\treturn\n\t}\n\tt.FailNow()\n}\n\n// ErrorContains asserts that a function returned an error (i.e. not `nil`)\n// and that the error contains the specified substring.\n//\n//\tactualObj, err := SomeFunction()\n//\tassert.ErrorContains(t, err,  expectedErrorSubString)\nfunc ErrorContains(t TestingT, theError error, contains string, msgAndArgs ...interface{}) {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\tif assert.ErrorContains(t, theError, contains, msgAndArgs...) {\n\t\treturn\n\t}\n\tt.FailNow()\n}\n\n// ErrorContainsf asserts that a function returned an error (i.e. not `nil`)\n// and that the error contains the specified substring.\n//\n//\tactualObj, err := SomeFunction()\n//\tassert.ErrorContainsf(t, err,  expectedErrorSubString, \"error message %s\", \"formatted\")\nfunc ErrorContainsf(t TestingT, theError error, contains string, msg string, args ...interface{}) {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\tif assert.ErrorContainsf(t, theError, contains, msg, args...) {\n\t\treturn\n\t}\n\tt.FailNow()\n}\n\n// Errorf asserts that a function returned an error (i.e. not `nil`).\n//\n//\t  actualObj, err := SomeFunction()\n//\t  if assert.Errorf(t, err, \"error message %s\", \"formatted\") {\n//\t\t   assert.Equal(t, expectedErrorf, err)\n//\t  }\nfunc Errorf(t TestingT, err error, msg string, args ...interface{}) {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\tif assert.Errorf(t, err, msg, args...) {\n\t\treturn\n\t}\n\tt.FailNow()\n}\n\n// Eventually asserts that given condition will be met in waitFor time,\n// periodically checking target function each tick.\n//\n//\tassert.Eventually(t, func() bool { return true; }, time.Second, 10*time.Millisecond)\nfunc Eventually(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\tif assert.Eventually(t, condition, waitFor, tick, msgAndArgs...) {\n\t\treturn\n\t}\n\tt.FailNow()\n}\n\n// Eventuallyf asserts that given condition will be met in waitFor time,\n// periodically checking target function each tick.\n//\n//\tassert.Eventuallyf(t, func() bool { return true; }, time.Second, 10*time.Millisecond, \"error message %s\", \"formatted\")\nfunc Eventuallyf(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\tif assert.Eventuallyf(t, condition, waitFor, tick, msg, args...) {\n\t\treturn\n\t}\n\tt.FailNow()\n}\n\n// Fail reports a failure through\nfunc Fail(t TestingT, failureMessage string, msgAndArgs ...interface{}) {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\tif assert.Fail(t, failureMessage, msgAndArgs...) {\n\t\treturn\n\t}\n\tt.FailNow()\n}\n\n// FailNow fails test\nfunc FailNow(t TestingT, failureMessage string, msgAndArgs ...interface{}) {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\tif assert.FailNow(t, failureMessage, msgAndArgs...) {\n\t\treturn\n\t}\n\tt.FailNow()\n}\n\n// FailNowf fails test\nfunc FailNowf(t TestingT, failureMessage string, msg string, args ...interface{}) {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\tif assert.FailNowf(t, failureMessage, msg, args...) {\n\t\treturn\n\t}\n\tt.FailNow()\n}\n\n// Failf reports a failure through\nfunc Failf(t TestingT, failureMessage string, msg string, args ...interface{}) {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\tif assert.Failf(t, failureMessage, msg, args...) {\n\t\treturn\n\t}\n\tt.FailNow()\n}\n\n// False asserts that the specified value is false.\n//\n//\tassert.False(t, myBool)\nfunc False(t TestingT, value bool, msgAndArgs ...interface{}) {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\tif assert.False(t, value, msgAndArgs...) {\n\t\treturn\n\t}\n\tt.FailNow()\n}\n\n// Falsef asserts that the specified value is false.\n//\n//\tassert.Falsef(t, myBool, \"error message %s\", \"formatted\")\nfunc Falsef(t TestingT, value bool, msg string, args ...interface{}) {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\tif assert.Falsef(t, value, msg, args...) {\n\t\treturn\n\t}\n\tt.FailNow()\n}\n\n// Greater asserts that the first element is greater than the second\n//\n//\tassert.Greater(t, 2, 1)\n//\tassert.Greater(t, float64(2), float64(1))\n//\tassert.Greater(t, \"b\", \"a\")\nfunc Greater(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\tif assert.Greater(t, e1, e2, msgAndArgs...) {\n\t\treturn\n\t}\n\tt.FailNow()\n}\n\n// GreaterOrEqual asserts that the first element is greater than or equal to the second\n//\n//\tassert.GreaterOrEqual(t, 2, 1)\n//\tassert.GreaterOrEqual(t, 2, 2)\n//\tassert.GreaterOrEqual(t, \"b\", \"a\")\n//\tassert.GreaterOrEqual(t, \"b\", \"b\")\nfunc GreaterOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\tif assert.GreaterOrEqual(t, e1, e2, msgAndArgs...) {\n\t\treturn\n\t}\n\tt.FailNow()\n}\n\n// GreaterOrEqualf asserts that the first element is greater than or equal to the second\n//\n//\tassert.GreaterOrEqualf(t, 2, 1, \"error message %s\", \"formatted\")\n//\tassert.GreaterOrEqualf(t, 2, 2, \"error message %s\", \"formatted\")\n//\tassert.GreaterOrEqualf(t, \"b\", \"a\", \"error message %s\", \"formatted\")\n//\tassert.GreaterOrEqualf(t, \"b\", \"b\", \"error message %s\", \"formatted\")\nfunc GreaterOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\tif assert.GreaterOrEqualf(t, e1, e2, msg, args...) {\n\t\treturn\n\t}\n\tt.FailNow()\n}\n\n// Greaterf asserts that the first element is greater than the second\n//\n//\tassert.Greaterf(t, 2, 1, \"error message %s\", \"formatted\")\n//\tassert.Greaterf(t, float64(2), float64(1), \"error message %s\", \"formatted\")\n//\tassert.Greaterf(t, \"b\", \"a\", \"error message %s\", \"formatted\")\nfunc Greaterf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\tif assert.Greaterf(t, e1, e2, msg, args...) {\n\t\treturn\n\t}\n\tt.FailNow()\n}\n\n// InDelta asserts that the two numerals are within delta of each other.\n//\n//\tassert.InDelta(t, math.Pi, 22/7.0, 0.01)\nfunc InDelta(t TestingT, expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\tif assert.InDelta(t, expected, actual, delta, msgAndArgs...) {\n\t\treturn\n\t}\n\tt.FailNow()\n}\n\n// InDeltaf asserts that the two numerals are within delta of each other.\n//\n//\tassert.InDeltaf(t, math.Pi, 22/7.0, 0.01, \"error message %s\", \"formatted\")\nfunc InDeltaf(t TestingT, expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\tif assert.InDeltaf(t, expected, actual, delta, msg, args...) {\n\t\treturn\n\t}\n\tt.FailNow()\n}\n\n// IsType asserts that the specified objects are of the same type.\nfunc IsType(t TestingT, expectedType interface{}, object interface{}, msgAndArgs ...interface{}) {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\tif assert.IsType(t, expectedType, object, msgAndArgs...) {\n\t\treturn\n\t}\n\tt.FailNow()\n}\n\n// IsTypef asserts that the specified objects are of the same type.\nfunc IsTypef(t TestingT, expectedType interface{}, object interface{}, msg string, args ...interface{}) {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\tif assert.IsTypef(t, expectedType, object, msg, args...) {\n\t\treturn\n\t}\n\tt.FailNow()\n}\n\n// Len asserts that the specified object has specific length.\n// Len also fails if the object has a type that len() not accept.\n//\n//\tassert.Len(t, mySlice, 3)\nfunc Len(t TestingT, object interface{}, length int, msgAndArgs ...interface{}) {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\tif assert.Len(t, object, length, msgAndArgs...) {\n\t\treturn\n\t}\n\tt.FailNow()\n}\n\n// Lenf asserts that the specified object has specific length.\n// Lenf also fails if the object has a type that len() not accept.\n//\n//\tassert.Lenf(t, mySlice, 3, \"error message %s\", \"formatted\")\nfunc Lenf(t TestingT, object interface{}, length int, msg string, args ...interface{}) {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\tif assert.Lenf(t, object, length, msg, args...) {\n\t\treturn\n\t}\n\tt.FailNow()\n}\n\n// Less asserts that the first element is less than the second\n//\n//\tassert.Less(t, 1, 2)\n//\tassert.Less(t, float64(1), float64(2))\n//\tassert.Less(t, \"a\", \"b\")\nfunc Less(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\tif assert.Less(t, e1, e2, msgAndArgs...) {\n\t\treturn\n\t}\n\tt.FailNow()\n}\n\n// LessOrEqual asserts that the first element is less than or equal to the second\n//\n//\tassert.LessOrEqual(t, 1, 2)\n//\tassert.LessOrEqual(t, 2, 2)\n//\tassert.LessOrEqual(t, \"a\", \"b\")\n//\tassert.LessOrEqual(t, \"b\", \"b\")\nfunc LessOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\tif assert.LessOrEqual(t, e1, e2, msgAndArgs...) {\n\t\treturn\n\t}\n\tt.FailNow()\n}\n\n// LessOrEqualf asserts that the first element is less than or equal to the second\n//\n//\tassert.LessOrEqualf(t, 1, 2, \"error message %s\", \"formatted\")\n//\tassert.LessOrEqualf(t, 2, 2, \"error message %s\", \"formatted\")\n//\tassert.LessOrEqualf(t, \"a\", \"b\", \"error message %s\", \"formatted\")\n//\tassert.LessOrEqualf(t, \"b\", \"b\", \"error message %s\", \"formatted\")\nfunc LessOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\tif assert.LessOrEqualf(t, e1, e2, msg, args...) {\n\t\treturn\n\t}\n\tt.FailNow()\n}\n\n// Lessf asserts that the first element is less than the second\n//\n//\tassert.Lessf(t, 1, 2, \"error message %s\", \"formatted\")\n//\tassert.Lessf(t, float64(1), float64(2), \"error message %s\", \"formatted\")\n//\tassert.Lessf(t, \"a\", \"b\", \"error message %s\", \"formatted\")\nfunc Lessf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\tif assert.Lessf(t, e1, e2, msg, args...) {\n\t\treturn\n\t}\n\tt.FailNow()\n}\n\n// Negative asserts that the specified element is negative\n//\n//\tassert.Negative(t, -1)\n//\tassert.Negative(t, -1.23)\nfunc Negative(t TestingT, e interface{}, msgAndArgs ...interface{}) {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\tif assert.Negative(t, e, msgAndArgs...) {\n\t\treturn\n\t}\n\tt.FailNow()\n}\n\n// Negativef asserts that the specified element is negative\n//\n//\tassert.Negativef(t, -1, \"error message %s\", \"formatted\")\n//\tassert.Negativef(t, -1.23, \"error message %s\", \"formatted\")\nfunc Negativef(t TestingT, e interface{}, msg string, args ...interface{}) {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\tif assert.Negativef(t, e, msg, args...) {\n\t\treturn\n\t}\n\tt.FailNow()\n}\n\n// Nil asserts that the specified object is nil.\n//\n//\tassert.Nil(t, err)\nfunc Nil(t TestingT, object interface{}, msgAndArgs ...interface{}) {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\tif assert.Nil(t, object, msgAndArgs...) {\n\t\treturn\n\t}\n\tt.FailNow()\n}\n\n// Nilf asserts that the specified object is nil.\n//\n//\tassert.Nilf(t, err, \"error message %s\", \"formatted\")\nfunc Nilf(t TestingT, object interface{}, msg string, args ...interface{}) {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\tif assert.Nilf(t, object, msg, args...) {\n\t\treturn\n\t}\n\tt.FailNow()\n}\n\n// NoError asserts that a function returned no error (i.e. `nil`).\n//\n//\t  actualObj, err := SomeFunction()\n//\t  if assert.NoError(t, err) {\n//\t\t   assert.Equal(t, expectedObj, actualObj)\n//\t  }\nfunc NoError(t TestingT, err error, msgAndArgs ...interface{}) {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\tif assert.NoError(t, err, msgAndArgs...) {\n\t\treturn\n\t}\n\tt.FailNow()\n}\n\n// NoErrorf asserts that a function returned no error (i.e. `nil`).\n//\n//\t  actualObj, err := SomeFunction()\n//\t  if assert.NoErrorf(t, err, \"error message %s\", \"formatted\") {\n//\t\t   assert.Equal(t, expectedObj, actualObj)\n//\t  }\nfunc NoErrorf(t TestingT, err error, msg string, args ...interface{}) {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\tif assert.NoErrorf(t, err, msg, args...) {\n\t\treturn\n\t}\n\tt.FailNow()\n}\n\n// NotContains asserts that the specified string, list(array, slice...) or map does NOT contain the\n// specified substring or element.\n//\n//\tassert.NotContains(t, \"Hello World\", \"Earth\")\n//\tassert.NotContains(t, [\"Hello\", \"World\"], \"Earth\")\n//\tassert.NotContains(t, {\"Hello\": \"World\"}, \"Earth\")\nfunc NotContains(t TestingT, s interface{}, contains interface{}, msgAndArgs ...interface{}) {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\tif assert.NotContains(t, s, contains, msgAndArgs...) {\n\t\treturn\n\t}\n\tt.FailNow()\n}\n\n// NotContainsf asserts that the specified string, list(array, slice...) or map does NOT contain the\n// specified substring or element.\n//\n//\tassert.NotContainsf(t, \"Hello World\", \"Earth\", \"error message %s\", \"formatted\")\n//\tassert.NotContainsf(t, [\"Hello\", \"World\"], \"Earth\", \"error message %s\", \"formatted\")\n//\tassert.NotContainsf(t, {\"Hello\": \"World\"}, \"Earth\", \"error message %s\", \"formatted\")\nfunc NotContainsf(t TestingT, s interface{}, contains interface{}, msg string, args ...interface{}) {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\tif assert.NotContainsf(t, s, contains, msg, args...) {\n\t\treturn\n\t}\n\tt.FailNow()\n}\n\n// NotEqual asserts that the specified values are NOT equal.\n//\n//\tassert.NotEqual(t, obj1, obj2)\n//\n// Pointer variable equality is determined based on the equality of the\n// referenced values (as opposed to the memory addresses).\nfunc NotEqual(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...interface{}) {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\tif assert.NotEqual(t, expected, actual, msgAndArgs...) {\n\t\treturn\n\t}\n\tt.FailNow()\n}\n\n// NotEqualValues asserts that two objects are not equal even when converted to the same type\n//\n//\tassert.NotEqualValues(t, obj1, obj2)\nfunc NotEqualValues(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...interface{}) {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\tif assert.NotEqualValues(t, expected, actual, msgAndArgs...) {\n\t\treturn\n\t}\n\tt.FailNow()\n}\n\n// NotEqualValuesf asserts that two objects are not equal even when converted to the same type\n//\n//\tassert.NotEqualValuesf(t, obj1, obj2, \"error message %s\", \"formatted\")\nfunc NotEqualValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\tif assert.NotEqualValuesf(t, expected, actual, msg, args...) {\n\t\treturn\n\t}\n\tt.FailNow()\n}\n\n// NotEqualf asserts that the specified values are NOT equal.\n//\n//\tassert.NotEqualf(t, obj1, obj2, \"error message %s\", \"formatted\")\n//\n// Pointer variable equality is determined based on the equality of the\n// referenced values (as opposed to the memory addresses).\nfunc NotEqualf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\tif assert.NotEqualf(t, expected, actual, msg, args...) {\n\t\treturn\n\t}\n\tt.FailNow()\n}\n\n// NotNil asserts that the specified object is not nil.\n//\n//\tassert.NotNil(t, err)\nfunc NotNil(t TestingT, object interface{}, msgAndArgs ...interface{}) {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\tif assert.NotNil(t, object, msgAndArgs...) {\n\t\treturn\n\t}\n\tt.FailNow()\n}\n\n// NotNilf asserts that the specified object is not nil.\n//\n//\tassert.NotNilf(t, err, \"error message %s\", \"formatted\")\nfunc NotNilf(t TestingT, object interface{}, msg string, args ...interface{}) {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\tif assert.NotNilf(t, object, msg, args...) {\n\t\treturn\n\t}\n\tt.FailNow()\n}\n\n// Positive asserts that the specified element is positive\n//\n//\tassert.Positive(t, 1)\n//\tassert.Positive(t, 1.23)\nfunc Positive(t TestingT, e interface{}, msgAndArgs ...interface{}) {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\tif assert.Positive(t, e, msgAndArgs...) {\n\t\treturn\n\t}\n\tt.FailNow()\n}\n\n// Positivef asserts that the specified element is positive\n//\n//\tassert.Positivef(t, 1, \"error message %s\", \"formatted\")\n//\tassert.Positivef(t, 1.23, \"error message %s\", \"formatted\")\nfunc Positivef(t TestingT, e interface{}, msg string, args ...interface{}) {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\tif assert.Positivef(t, e, msg, args...) {\n\t\treturn\n\t}\n\tt.FailNow()\n}\n\n// True asserts that the specified value is true.\n//\n//\tassert.True(t, myBool)\nfunc True(t TestingT, value bool, msgAndArgs ...interface{}) {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\tif assert.True(t, value, msgAndArgs...) {\n\t\treturn\n\t}\n\tt.FailNow()\n}\n\n// Truef asserts that the specified value is true.\n//\n//\tassert.Truef(t, myBool, \"error message %s\", \"formatted\")\nfunc Truef(t TestingT, value bool, msg string, args ...interface{}) {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\tif assert.Truef(t, value, msg, args...) {\n\t\treturn\n\t}\n\tt.FailNow()\n}\n\n// WithinDuration asserts that the two times are within duration delta of each other.\n//\n//\tassert.WithinDuration(t, time.Now(), time.Now(), 10*time.Second)\nfunc WithinDuration(t TestingT, expected time.Time, actual time.Time, delta time.Duration, msgAndArgs ...interface{}) {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\tif assert.WithinDuration(t, expected, actual, delta, msgAndArgs...) {\n\t\treturn\n\t}\n\tt.FailNow()\n}\n\n// WithinDurationf asserts that the two times are within duration delta of each other.\n//\n//\tassert.WithinDurationf(t, time.Now(), time.Now(), 10*time.Second, \"error message %s\", \"formatted\")\nfunc WithinDurationf(t TestingT, expected time.Time, actual time.Time, delta time.Duration, msg string, args ...interface{}) {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\tif assert.WithinDurationf(t, expected, actual, delta, msg, args...) {\n\t\treturn\n\t}\n\tt.FailNow()\n}\n\n// NotEmpty asserts that the specified object is NOT [Empty].\n//\n//\tif require.NotEmpty(t, obj) {\n//\t  require.Equal(t, \"two\", obj[1])\n//\t}\nfunc NotEmpty(t TestingT, object interface{}, msgAndArgs ...interface{}) {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\tif assert.NotEmpty(t, object, msgAndArgs...) {\n\t\treturn\n\t}\n\tt.FailNow()\n}\n\n// Empty asserts that the given value is \"empty\".\n//\n// [Zero values] are \"empty\".\n//\n// Arrays are \"empty\" if every element is the zero value of the type (stricter than \"empty\").\n//\n// Slices, maps and channels with zero length are \"empty\".\n//\n// Pointer values are \"empty\" if the pointer is nil or if the pointed value is \"empty\".\n//\n//\trequire.Empty(t, obj)\n//\n// [Zero values]: https://go.dev/ref/spec#The_zero_value\nfunc Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) {\n\tif h, ok := t.(tHelper); ok {\n\t\th.Helper()\n\t}\n\tif assert.Empty(t, object, msgAndArgs...) {\n\t\treturn\n\t}\n\tt.FailNow()\n}\n"
  },
  {
    "path": "internal/serverselector/server_selector.go",
    "content": "// Copyright (C) MongoDB, Inc. 2024-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage serverselector\n\nimport (\n\t\"fmt\"\n\t\"math\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/mongo/address\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/readpref\"\n\t\"go.mongodb.org/mongo-driver/v2/tag\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/description\"\n)\n\n// Composite combines multiple selectors into a single selector by applying them\n// in order to the candidates list.\n//\n// For example, if the initial candidates list is [s0, s1, s2, s3] and two\n// selectors are provided where the first matches s0 and s1 and the second\n// matches s1 and s2, the following would occur during server selection:\n//\n// 1. firstSelector([s0, s1, s2, s3]) -> [s0, s1]\n// 2. secondSelector([s0, s1]) -> [s1]\n//\n// The final list of candidates returned by the composite selector would be\n// [s1].\ntype Composite struct {\n\tSelectors []description.ServerSelector\n}\n\nvar _ description.ServerSelector = &Composite{}\n\n// SelectServer combines multiple selectors into a single selector.\nfunc (selector *Composite) SelectServer(\n\ttopo description.Topology,\n\tcandidates []description.Server,\n) ([]description.Server, error) {\n\tvar err error\n\tfor _, sel := range selector.Selectors {\n\t\tcandidates, err = sel.SelectServer(topo, candidates)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\treturn candidates, nil\n}\n\n// Latency creates a ServerSelector which selects servers based on their average\n// RTT values.\ntype Latency struct {\n\tLatency time.Duration\n}\n\nvar _ description.ServerSelector = &Latency{}\n\n// SelectServer selects servers based on average RTT.\nfunc (selector *Latency) SelectServer(\n\ttopo description.Topology,\n\tcandidates []description.Server,\n) ([]description.Server, error) {\n\tif selector.Latency < 0 {\n\t\treturn candidates, nil\n\t}\n\tif topo.Kind == description.TopologyKindLoadBalanced {\n\t\t// In LoadBalanced mode, there should only be one server in the topology and\n\t\t// it must be selected.\n\t\treturn candidates, nil\n\t}\n\n\tswitch len(candidates) {\n\tcase 0, 1:\n\t\treturn candidates, nil\n\tdefault:\n\t\tmin := time.Duration(math.MaxInt64)\n\t\tfor _, candidate := range candidates {\n\t\t\tif candidate.AverageRTTSet {\n\t\t\t\tif candidate.AverageRTT < min {\n\t\t\t\t\tmin = candidate.AverageRTT\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif min == math.MaxInt64 {\n\t\t\treturn candidates, nil\n\t\t}\n\n\t\tmax := min + selector.Latency\n\n\t\tviableIndexes := make([]int, 0, len(candidates))\n\t\tfor i, candidate := range candidates {\n\t\t\tif candidate.AverageRTTSet {\n\t\t\t\tif candidate.AverageRTT <= max {\n\t\t\t\t\tviableIndexes = append(viableIndexes, i)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif len(viableIndexes) == len(candidates) {\n\t\t\treturn candidates, nil\n\t\t}\n\t\tresult := make([]description.Server, len(viableIndexes))\n\t\tfor i, idx := range viableIndexes {\n\t\t\tresult[i] = candidates[idx]\n\t\t}\n\t\treturn result, nil\n\t}\n}\n\n// ReadPref selects servers based on the provided read preference.\ntype ReadPref struct {\n\tReadPref          *readpref.ReadPref\n\tIsOutputAggregate bool\n}\n\nvar _ description.ServerSelector = &ReadPref{}\n\n// SelectServer selects servers based on read preference.\nfunc (selector *ReadPref) SelectServer(\n\ttopo description.Topology,\n\tcandidates []description.Server,\n) ([]description.Server, error) {\n\tif topo.Kind == description.TopologyKindLoadBalanced {\n\t\t// In LoadBalanced mode, there should only be one server in the topology and\n\t\t// it must be selected. We check this before checking MaxStaleness support\n\t\t// because there's no monitoring in this mode, so the candidate server\n\t\t// wouldn't have a wire version set, which would result in an error.\n\t\treturn candidates, nil\n\t}\n\n\tswitch topo.Kind {\n\tcase description.TopologyKindSingle:\n\t\treturn candidates, nil\n\tcase description.TopologyKindReplicaSetNoPrimary, description.TopologyKindReplicaSetWithPrimary:\n\t\treturn selectForReplicaSet(selector.ReadPref, selector.IsOutputAggregate, topo, candidates)\n\tcase description.TopologyKindSharded:\n\t\treturn selectByKind(candidates, description.ServerKindMongos), nil\n\t}\n\n\treturn nil, nil\n}\n\n// Write selects all the writable servers.\ntype Write struct{}\n\nvar _ description.ServerSelector = &Write{}\n\n// SelectServer selects all writable servers.\nfunc (selector *Write) SelectServer(\n\ttopo description.Topology,\n\tcandidates []description.Server,\n) ([]description.Server, error) {\n\tswitch topo.Kind {\n\tcase description.TopologyKindSingle, description.TopologyKindLoadBalanced:\n\t\treturn candidates, nil\n\tdefault:\n\t\t// Determine the capacity of the results slice.\n\t\tselected := 0\n\t\tfor _, candidate := range candidates {\n\t\t\tswitch candidate.Kind {\n\t\t\tcase description.ServerKindMongos, description.ServerKindRSPrimary, description.ServerKindStandalone:\n\t\t\t\tselected++\n\t\t\t}\n\t\t}\n\n\t\t// Append candidates to the results slice.\n\t\tresult := make([]description.Server, 0, selected)\n\t\tfor _, candidate := range candidates {\n\t\t\tswitch candidate.Kind {\n\t\t\tcase description.ServerKindMongos, description.ServerKindRSPrimary, description.ServerKindStandalone:\n\t\t\t\tresult = append(result, candidate)\n\t\t\t}\n\t\t}\n\t\treturn result, nil\n\t}\n}\n\n// Deprioritized filters out deprioritized servers from candidates.\n// If all candidates are deprioritized, returns all candidates as fallback.\ntype Deprioritized struct {\n\tdeprioritizedServers []description.Server\n\tinnerSelector        description.ServerSelector\n}\n\nvar _ description.ServerSelector = &Deprioritized{}\n\n// SelectServer filters out deprioritized servers from candidates.\nfunc (d *Deprioritized) SelectServer(\n\ttopo description.Topology,\n\tcandidates []description.Server,\n) ([]description.Server, error) {\n\tif len(d.deprioritizedServers) == 0 {\n\t\treturn d.innerSelector.SelectServer(topo, candidates)\n\t}\n\n\tdeprioritizedAddrs := make(map[address.Address]struct{})\n\tfor _, srv := range d.deprioritizedServers {\n\t\tdeprioritizedAddrs[srv.Addr] = struct{}{}\n\t}\n\n\tallowed := []description.Server{}\n\n\t// Iterate over the candidates and append them to the allowed slice if\n\t// they are not in the deprioritizedServers list.\n\tfor _, candidate := range candidates {\n\t\tif _, ok := deprioritizedAddrs[candidate.Addr]; !ok {\n\t\t\tallowed = append(allowed, candidate)\n\t\t}\n\t}\n\n\tif len(allowed) > 0 {\n\t\tresult, err := d.innerSelector.SelectServer(topo, allowed)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif len(result) > 0 {\n\t\t\treturn result, nil\n\t\t}\n\t}\n\treturn d.innerSelector.SelectServer(topo, candidates)\n}\n\n// NewDeprioritized wraps an inner selector to filter out deprioritized servers.\nfunc NewDeprioritized(inner description.ServerSelector, deprioritized []description.Server) description.ServerSelector {\n\treturn &Deprioritized{\n\t\tdeprioritizedServers: deprioritized,\n\t\tinnerSelector:        inner,\n\t}\n}\n\n// Func is a function that can be used as a ServerSelector.\ntype Func func(description.Topology, []description.Server) ([]description.Server, error)\n\n// SelectServer implements the ServerSelector interface.\nfunc (ssf Func) SelectServer(\n\tt description.Topology,\n\ts []description.Server,\n) ([]description.Server, error) {\n\treturn ssf(t, s)\n}\n\nfunc verifyMaxStaleness(rp *readpref.ReadPref, topo description.Topology) error {\n\tmaxStaleness, set := rp.MaxStaleness()\n\tif !set {\n\t\treturn nil\n\t}\n\n\tif maxStaleness < 90*time.Second {\n\t\treturn fmt.Errorf(\"max staleness (%s) must be greater than or equal to 90s\", maxStaleness)\n\t}\n\n\tif len(topo.Servers) < 1 {\n\t\t// Maybe we should return an error here instead?\n\t\treturn nil\n\t}\n\n\t// we'll assume all candidates have the same heartbeat interval.\n\ts := topo.Servers[0]\n\tidleWritePeriod := 10 * time.Second\n\n\tif maxStaleness < s.HeartbeatInterval+idleWritePeriod {\n\t\treturn fmt.Errorf(\n\t\t\t\"max staleness (%s) must be greater than or equal to the heartbeat interval (%s) plus idle write period (%s)\",\n\t\t\tmaxStaleness, s.HeartbeatInterval, idleWritePeriod,\n\t\t)\n\t}\n\n\treturn nil\n}\n\nfunc selectByKind(candidates []description.Server, kind description.ServerKind) []description.Server {\n\t// Record the indices of viable candidates first and then append those to the returned slice\n\t// to avoid appending costly Server structs directly as an optimization.\n\tviableIndexes := make([]int, 0, len(candidates))\n\tfor i, s := range candidates {\n\t\tif s.Kind == kind {\n\t\t\tviableIndexes = append(viableIndexes, i)\n\t\t}\n\t}\n\tif len(viableIndexes) == len(candidates) {\n\t\treturn candidates\n\t}\n\tresult := make([]description.Server, len(viableIndexes))\n\tfor i, idx := range viableIndexes {\n\t\tresult[i] = candidates[idx]\n\t}\n\treturn result\n}\n\nfunc selectSecondaries(rp *readpref.ReadPref, candidates []description.Server) []description.Server {\n\tsecondaries := selectByKind(candidates, description.ServerKindRSSecondary)\n\tif len(secondaries) == 0 {\n\t\treturn secondaries\n\t}\n\tif maxStaleness, set := rp.MaxStaleness(); set {\n\t\tprimaries := selectByKind(candidates, description.ServerKindRSPrimary)\n\t\tif len(primaries) == 0 {\n\t\t\tbaseTime := secondaries[0].LastWriteTime\n\t\t\tfor i := 1; i < len(secondaries); i++ {\n\t\t\t\tif secondaries[i].LastWriteTime.After(baseTime) {\n\t\t\t\t\tbaseTime = secondaries[i].LastWriteTime\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tvar selected []description.Server\n\t\t\tfor _, secondary := range secondaries {\n\t\t\t\testimatedStaleness := baseTime.Sub(secondary.LastWriteTime) + secondary.HeartbeatInterval\n\t\t\t\tif estimatedStaleness <= maxStaleness {\n\t\t\t\t\tselected = append(selected, secondary)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn selected\n\t\t}\n\n\t\tprimary := primaries[0]\n\n\t\tvar selected []description.Server\n\t\tfor _, secondary := range secondaries {\n\t\t\testimatedStaleness := secondary.LastUpdateTime.Sub(secondary.LastWriteTime) -\n\t\t\t\tprimary.LastUpdateTime.Sub(primary.LastWriteTime) + secondary.HeartbeatInterval\n\t\t\tif estimatedStaleness <= maxStaleness {\n\t\t\t\tselected = append(selected, secondary)\n\t\t\t}\n\t\t}\n\t\treturn selected\n\t}\n\n\treturn secondaries\n}\n\nfunc selectByTagSet(candidates []description.Server, tagSets []tag.Set) []description.Server {\n\tif len(tagSets) == 0 {\n\t\treturn candidates\n\t}\n\n\tfor _, ts := range tagSets {\n\t\t// If this tag set is empty, we can take a fast path because the empty list\n\t\t// is a subset of all tag sets, so all candidate servers will be selected.\n\t\tif len(ts) == 0 {\n\t\t\treturn candidates\n\t\t}\n\n\t\tvar results []description.Server\n\t\tfor _, s := range candidates {\n\t\t\t// ts is non-empty, so only servers with a non-empty set of tags need to be checked.\n\t\t\tif len(s.Tags) > 0 && s.Tags.ContainsAll(ts) {\n\t\t\t\tresults = append(results, s)\n\t\t\t}\n\t\t}\n\n\t\tif len(results) > 0 {\n\t\t\treturn results\n\t\t}\n\t}\n\n\treturn []description.Server{}\n}\n\nfunc selectForReplicaSet(\n\trp *readpref.ReadPref,\n\tisOutputAggregate bool,\n\ttopo description.Topology,\n\tcandidates []description.Server,\n) ([]description.Server, error) {\n\tif err := verifyMaxStaleness(rp, topo); err != nil {\n\t\treturn nil, err\n\t}\n\n\t// If underlying operation is an aggregate with an output stage, only apply read preference\n\t// if all candidates are 5.0+. Otherwise, operate under primary read preference.\n\tif isOutputAggregate {\n\t\tfor _, s := range candidates {\n\t\t\tif s.WireVersion.Max < 13 {\n\t\t\t\treturn selectByKind(candidates, description.ServerKindRSPrimary), nil\n\t\t\t}\n\t\t}\n\t}\n\n\tswitch rp.Mode() {\n\tcase readpref.PrimaryMode:\n\t\treturn selectByKind(candidates, description.ServerKindRSPrimary), nil\n\tcase readpref.PrimaryPreferredMode:\n\t\tselected := selectByKind(candidates, description.ServerKindRSPrimary)\n\n\t\tif len(selected) == 0 {\n\t\t\tselected = selectSecondaries(rp, candidates)\n\t\t\treturn selectByTagSet(selected, rp.TagSets()), nil\n\t\t}\n\n\t\treturn selected, nil\n\tcase readpref.SecondaryPreferredMode:\n\t\tselected := selectSecondaries(rp, candidates)\n\t\tselected = selectByTagSet(selected, rp.TagSets())\n\t\tif len(selected) > 0 {\n\t\t\treturn selected, nil\n\t\t}\n\t\treturn selectByKind(candidates, description.ServerKindRSPrimary), nil\n\tcase readpref.SecondaryMode:\n\t\tselected := selectSecondaries(rp, candidates)\n\t\treturn selectByTagSet(selected, rp.TagSets()), nil\n\tcase readpref.NearestMode:\n\t\tselected := selectByKind(candidates, description.ServerKindRSPrimary)\n\t\tselected = append(selected, selectSecondaries(rp, candidates)...)\n\t\treturn selectByTagSet(selected, rp.TagSets()), nil\n\t}\n\n\treturn nil, fmt.Errorf(\"unsupported mode: %d\", rp.Mode())\n}\n"
  },
  {
    "path": "internal/serverselector/server_selector_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2024-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage serverselector\n\nimport (\n\t\"errors\"\n\t\"os\"\n\t\"path\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/google/go-cmp/cmp\"\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/driverutil\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/require\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/spectest\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/address\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/readpref\"\n\t\"go.mongodb.org/mongo-driver/v2/tag\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/description\"\n)\n\ntype lastWriteDate struct {\n\tLastWriteDate int64 `bson:\"lastWriteDate\"`\n}\n\ntype serverDesc struct {\n\tAddress        string            `bson:\"address\"`\n\tAverageRTTMS   *int              `bson:\"avg_rtt_ms\"`\n\tMaxWireVersion *int32            `bson:\"maxWireVersion\"`\n\tLastUpdateTime *int              `bson:\"lastUpdateTime\"`\n\tLastWrite      *lastWriteDate    `bson:\"lastWrite\"`\n\tType           string            `bson:\"type\"`\n\tTags           map[string]string `bson:\"tags\"`\n}\n\ntype topDesc struct {\n\tType    string        `bson:\"type\"`\n\tServers []*serverDesc `bson:\"servers\"`\n}\n\ntype readPref struct {\n\tMaxStaleness *int                `bson:\"maxStalenessSeconds\"`\n\tMode         string              `bson:\"mode\"`\n\tTagSets      []map[string]string `bson:\"tag_sets\"`\n}\n\ntype testCase struct {\n\tTopologyDescription  topDesc       `bson:\"topology_description\"`\n\tOperation            string        `bson:\"operation\"`\n\tReadPreference       readPref      `bson:\"read_preference\"`\n\tSuitableServers      []*serverDesc `bson:\"suitable_servers\"`\n\tInLatencyWindow      []*serverDesc `bson:\"in_latency_window\"`\n\tHeartbeatFrequencyMS *int          `bson:\"heartbeatFrequencyMS\"`\n\tDeprioritizedServers []*serverDesc `bson:\"deprioritized_servers\"`\n\tError                *bool\n}\n\nfunc serverKindFromString(t *testing.T, s string) description.ServerKind {\n\tt.Helper()\n\n\tswitch s {\n\tcase \"Standalone\":\n\t\treturn description.ServerKindStandalone\n\tcase \"RSOther\":\n\t\treturn description.ServerKindRSMember\n\tcase \"RSPrimary\":\n\t\treturn description.ServerKindRSPrimary\n\tcase \"RSSecondary\":\n\t\treturn description.ServerKindRSSecondary\n\tcase \"RSArbiter\":\n\t\treturn description.ServerKindRSArbiter\n\tcase \"RSGhost\":\n\t\treturn description.ServerKindRSGhost\n\tcase \"Mongos\":\n\t\treturn description.ServerKindMongos\n\tcase \"LoadBalancer\":\n\t\treturn description.ServerKindLoadBalancer\n\tcase \"PossiblePrimary\", \"Unknown\":\n\t\t// Go does not have a PossiblePrimary server type and per the SDAM spec, this type is synonymous with Unknown.\n\t\treturn description.Unknown\n\tdefault:\n\t\tt.Fatalf(\"unrecognized server kind: %q\", s)\n\t}\n\n\treturn description.Unknown\n}\n\nfunc topologyKindFromString(t *testing.T, s string) description.TopologyKind {\n\tt.Helper()\n\n\tswitch s {\n\tcase \"Single\":\n\t\treturn description.TopologyKindSingle\n\tcase \"ReplicaSet\":\n\t\treturn description.TopologyKindReplicaSet\n\tcase \"ReplicaSetNoPrimary\":\n\t\treturn description.TopologyKindReplicaSetNoPrimary\n\tcase \"ReplicaSetWithPrimary\":\n\t\treturn description.TopologyKindReplicaSetWithPrimary\n\tcase \"Sharded\":\n\t\treturn description.TopologyKindSharded\n\tcase \"LoadBalanced\":\n\t\treturn description.TopologyKindLoadBalanced\n\tcase \"Unknown\":\n\t\treturn description.Unknown\n\tdefault:\n\t\tt.Fatalf(\"unrecognized topology kind: %q\", s)\n\t}\n\n\treturn description.Unknown\n}\n\nfunc anyTagsInSets(sets []tag.Set) bool {\n\tfor _, set := range sets {\n\t\tif len(set) > 0 {\n\t\t\treturn true\n\t\t}\n\t}\n\n\treturn false\n}\n\nfunc findServerByAddress(servers []description.Server, address string) description.Server {\n\tfor _, server := range servers {\n\t\tif server.Addr.String() == address {\n\t\t\treturn server\n\t\t}\n\t}\n\n\treturn description.Server{}\n}\n\nfunc compareServers(t *testing.T, expected []*serverDesc, actual []description.Server) {\n\trequire.Equal(t, len(expected), len(actual))\n\n\tfor _, expectedServer := range expected {\n\t\tactualServer := findServerByAddress(actual, expectedServer.Address)\n\t\trequire.NotNil(t, actualServer)\n\n\t\tif expectedServer.AverageRTTMS != nil {\n\t\t\trequire.Equal(t, *expectedServer.AverageRTTMS, int(actualServer.AverageRTT/time.Millisecond))\n\t\t}\n\n\t\trequire.Equal(t, expectedServer.Type, actualServer.Kind.String())\n\n\t\trequire.Equal(t, len(expectedServer.Tags), len(actualServer.Tags))\n\t\tfor _, actualTag := range actualServer.Tags {\n\t\t\texpectedTag, ok := expectedServer.Tags[actualTag.Name]\n\t\t\trequire.True(t, ok)\n\t\t\trequire.Equal(t, expectedTag, actualTag.Value)\n\t\t}\n\t}\n}\n\nvar maxStalenessTestsDir = spectest.Path(\"max-staleness/tests\")\n\n// Test case for all max staleness spec tests.\nfunc TestMaxStalenessSpec(t *testing.T) {\n\tfor _, topology := range [...]string{\n\t\t\"ReplicaSetNoPrimary\",\n\t\t\"ReplicaSetWithPrimary\",\n\t\t\"Sharded\",\n\t\t\"Single\",\n\t\t\"Unknown\",\n\t} {\n\t\tfor _, file := range spectest.FindJSONFilesInDir(t,\n\t\t\tpath.Join(maxStalenessTestsDir, topology)) {\n\t\t\trunTest(t, maxStalenessTestsDir, topology, file)\n\t\t}\n\t}\n}\n\nvar selectorTestsDir = spectest.Path(\"server-selection/tests\")\n\nfunc convertServerDesc(t *testing.T, serverDescription *serverDesc, baseTime time.Time, heartbeatFreqMS *int) description.Server {\n\tt.Helper()\n\n\tserver := description.Server{\n\t\tAddr: address.Address(serverDescription.Address),\n\t\tKind: serverKindFromString(t, serverDescription.Type),\n\t}\n\n\tif serverDescription.AverageRTTMS != nil {\n\t\tserver.AverageRTT = time.Duration(*serverDescription.AverageRTTMS) * time.Millisecond\n\t\tserver.AverageRTTSet = true\n\t}\n\n\tif heartbeatFreqMS != nil {\n\t\tserver.HeartbeatInterval = time.Duration(*heartbeatFreqMS) * time.Millisecond\n\t}\n\n\tif serverDescription.LastUpdateTime != nil {\n\t\tms := int64(*serverDescription.LastUpdateTime)\n\t\tserver.LastUpdateTime = time.Unix(ms/1e3, ms%1e3/1e6)\n\t}\n\n\tif serverDescription.LastWrite != nil {\n\t\ti := serverDescription.LastWrite.LastWriteDate\n\t\ttimeWithOffset := baseTime.Add(time.Duration(i) * time.Millisecond)\n\t\tserver.LastWriteTime = timeWithOffset\n\t}\n\n\tif serverDescription.MaxWireVersion != nil {\n\t\tversionRange := driverutil.NewVersionRange(0, *serverDescription.MaxWireVersion)\n\t\tserver.WireVersion = &versionRange\n\t}\n\n\tif serverDescription.Tags != nil {\n\t\tserver.Tags = tag.NewTagSetFromMap(serverDescription.Tags)\n\t}\n\n\treturn server\n}\n\nfunc selectServers(t *testing.T, test *testCase) error {\n\tservers := make([]description.Server, 0, len(test.TopologyDescription.Servers))\n\n\t// Times in the JSON files are given as offsets from an unspecified time, but the driver\n\t// stores the lastWrite field as a timestamp, so we arbitrarily choose the current time\n\t// as the base to offset from.\n\tbaseTime := time.Now()\n\n\tfor _, serverDescription := range test.TopologyDescription.Servers {\n\t\tserver := convertServerDesc(t, serverDescription, baseTime, test.HeartbeatFrequencyMS)\n\n\t\tif test.ReadPreference.MaxStaleness != nil && server.WireVersion == nil {\n\t\t\tserver.WireVersion = &description.VersionRange{Max: 21}\n\t\t}\n\n\t\tservers = append(servers, server)\n\t}\n\n\tc := description.Topology{\n\t\tKind:    topologyKindFromString(t, test.TopologyDescription.Type),\n\t\tServers: servers,\n\t}\n\n\t// Convert deprioritized servers from test JSON to description.Server\n\tvar deprioritizedServers []description.Server\n\tfor _, srvDesc := range test.DeprioritizedServers {\n\t\tdeprioritizedServers = append(deprioritizedServers, convertServerDesc(t, srvDesc, baseTime, test.HeartbeatFrequencyMS))\n\t}\n\n\tif len(test.ReadPreference.Mode) == 0 {\n\t\ttest.ReadPreference.Mode = \"Primary\"\n\t}\n\n\treadprefMode, err := readpref.ModeFromString(test.ReadPreference.Mode)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\toptions := make([]readpref.Option, 0, 1)\n\n\ttagSets := tag.NewTagSetsFromMaps(test.ReadPreference.TagSets)\n\tif anyTagsInSets(tagSets) {\n\t\toptions = append(options, readpref.WithTagSets(tagSets...))\n\t}\n\n\tif test.ReadPreference.MaxStaleness != nil {\n\t\ts := time.Duration(*test.ReadPreference.MaxStaleness) * time.Second\n\t\toptions = append(options, readpref.WithMaxStaleness(s))\n\t}\n\n\trp, err := readpref.New(readprefMode, options...)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tvar baseSelector description.ServerSelector\n\n\tbaseSelector = &ReadPref{ReadPref: rp}\n\tif test.Operation == \"write\" {\n\t\tbaseSelector = &Composite{\n\t\t\tSelectors: []description.ServerSelector{&Write{}, baseSelector},\n\t\t}\n\t}\n\n\t// Deprioritized selector with read/write selector if there are deprioritized servers.\n\tselector := NewDeprioritized(baseSelector, deprioritizedServers)\n\n\tresult, err := selector.SelectServer(c, servers)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tcompareServers(t, test.SuitableServers, result)\n\n\tlatencySelector := &Latency{Latency: time.Duration(15) * time.Millisecond}\n\n\t// Build selector chain for latency window: Deprioritized -> baseSelector -> Latency\n\tbaseWithLatency := &Composite{\n\t\tSelectors: []description.ServerSelector{baseSelector, latencySelector},\n\t}\n\tlatencySelectorChain := NewDeprioritized(baseWithLatency, deprioritizedServers)\n\n\tresult, err = latencySelectorChain.SelectServer(c, servers)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tcompareServers(t, test.InLatencyWindow, result)\n\n\treturn nil\n}\n\nfunc runTest(t *testing.T, testsDir string, directory string, filename string) {\n\tfilepath := path.Join(testsDir, directory, filename)\n\tcontent, err := os.ReadFile(filepath)\n\tif err != nil {\n\t\tt.Fatalf(\"failed to read file %s: %v\", filepath, err)\n\t}\n\n\tt.Run(directory+\"/\"+filename, func(t *testing.T) {\n\t\tspectest.CheckSkip(t)\n\n\t\tvar test testCase\n\t\trequire.NoError(t, bson.UnmarshalExtJSON(content, true, &test))\n\n\t\terr := selectServers(t, &test)\n\n\t\tif test.Error == nil || !*test.Error {\n\t\t\trequire.NoError(t, err)\n\t\t} else {\n\t\t\trequire.Error(t, err)\n\t\t}\n\t})\n}\n\n// Test case for all SDAM spec tests.\nfunc TestServerSelectionSpec(t *testing.T) {\n\tfor _, topology := range [...]string{\n\t\t\"ReplicaSetNoPrimary\",\n\t\t\"ReplicaSetWithPrimary\",\n\t\t\"Sharded\",\n\t\t\"Single\",\n\t\t\"Unknown\",\n\t\t\"LoadBalanced\",\n\t} {\n\t\tfor _, subdir := range [...]string{\"read\", \"write\"} {\n\t\t\tsubdirPath := path.Join(\"server_selection\", topology, subdir)\n\n\t\t\tfor _, file := range spectest.FindJSONFilesInDir(t,\n\t\t\t\tpath.Join(selectorTestsDir, subdirPath)) {\n\t\t\t\trunTest(t, selectorTestsDir, subdirPath, file)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc TestServerSelection(t *testing.T) {\n\tnoerr := func(t *testing.T, err error) {\n\t\tif err != nil {\n\t\t\tt.Errorf(\"Unepexted error: %v\", err)\n\t\t\tt.FailNow()\n\t\t}\n\t}\n\n\tt.Run(\"WriteSelector\", func(t *testing.T) {\n\t\ttestCases := []struct {\n\t\t\tname  string\n\t\t\tdesc  description.Topology\n\t\t\tstart int\n\t\t\tend   int\n\t\t}{\n\t\t\t{\n\t\t\t\tname: \"ReplicaSetWithPrimary\",\n\t\t\t\tdesc: description.Topology{\n\t\t\t\t\tKind: description.TopologyKindReplicaSetWithPrimary,\n\t\t\t\t\tServers: []description.Server{\n\t\t\t\t\t\t{Addr: address.Address(\"localhost:27017\"), Kind: description.ServerKindRSPrimary},\n\t\t\t\t\t\t{Addr: address.Address(\"localhost:27018\"), Kind: description.ServerKindRSSecondary},\n\t\t\t\t\t\t{Addr: address.Address(\"localhost:27019\"), Kind: description.ServerKindRSSecondary},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tstart: 0,\n\t\t\t\tend:   1,\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"ReplicaSetNoPrimary\",\n\t\t\t\tdesc: description.Topology{\n\t\t\t\t\tKind: description.TopologyKindReplicaSetNoPrimary,\n\t\t\t\t\tServers: []description.Server{\n\t\t\t\t\t\t{Addr: address.Address(\"localhost:27018\"), Kind: description.ServerKindRSSecondary},\n\t\t\t\t\t\t{Addr: address.Address(\"localhost:27019\"), Kind: description.ServerKindRSSecondary},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tstart: 0,\n\t\t\t\tend:   0,\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"Sharded\",\n\t\t\t\tdesc: description.Topology{\n\t\t\t\t\tKind: description.TopologyKindSharded,\n\t\t\t\t\tServers: []description.Server{\n\t\t\t\t\t\t{Addr: address.Address(\"localhost:27018\"), Kind: description.ServerKindMongos},\n\t\t\t\t\t\t{Addr: address.Address(\"localhost:27019\"), Kind: description.ServerKindMongos},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tstart: 0,\n\t\t\t\tend:   2,\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"Single\",\n\t\t\t\tdesc: description.Topology{\n\t\t\t\t\tKind: description.TopologyKindSingle,\n\t\t\t\t\tServers: []description.Server{\n\t\t\t\t\t\t{Addr: address.Address(\"localhost:27018\"), Kind: description.ServerKindStandalone},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tstart: 0,\n\t\t\t\tend:   1,\n\t\t\t},\n\t\t}\n\n\t\tfor _, tc := range testCases {\n\t\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\t\tresult, err := (&Write{}).SelectServer(tc.desc, tc.desc.Servers)\n\t\t\t\tnoerr(t, err)\n\t\t\t\tif len(result) != tc.end-tc.start {\n\t\t\t\t\tt.Errorf(\"Incorrect number of servers selected. got %d; want %d\", len(result), tc.end-tc.start)\n\t\t\t\t}\n\t\t\t\tif diff := cmp.Diff(result, tc.desc.Servers[tc.start:tc.end]); diff != \"\" {\n\t\t\t\t\tt.Errorf(\"Incorrect servers selected (-got +want):\\n%s\", diff)\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t})\n\tt.Run(\"LatencySelector\", func(t *testing.T) {\n\t\ttestCases := []struct {\n\t\t\tname  string\n\t\t\tdesc  description.Topology\n\t\t\tstart int\n\t\t\tend   int\n\t\t}{\n\t\t\t{\n\t\t\t\tname: \"NoRTTSet\",\n\t\t\t\tdesc: description.Topology{\n\t\t\t\t\tServers: []description.Server{\n\t\t\t\t\t\t{Addr: address.Address(\"localhost:27017\")},\n\t\t\t\t\t\t{Addr: address.Address(\"localhost:27018\")},\n\t\t\t\t\t\t{Addr: address.Address(\"localhost:27019\")},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tstart: 0,\n\t\t\t\tend:   3,\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"MultipleServers PartialNoRTTSet\",\n\t\t\t\tdesc: description.Topology{\n\t\t\t\t\tServers: []description.Server{\n\t\t\t\t\t\t{Addr: address.Address(\"localhost:27017\"), AverageRTT: 5 * time.Second, AverageRTTSet: true},\n\t\t\t\t\t\t{Addr: address.Address(\"localhost:27018\"), AverageRTT: 10 * time.Second, AverageRTTSet: true},\n\t\t\t\t\t\t{Addr: address.Address(\"localhost:27019\")},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tstart: 0,\n\t\t\t\tend:   2,\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"MultipleServers\",\n\t\t\t\tdesc: description.Topology{\n\t\t\t\t\tServers: []description.Server{\n\t\t\t\t\t\t{Addr: address.Address(\"localhost:27017\"), AverageRTT: 5 * time.Second, AverageRTTSet: true},\n\t\t\t\t\t\t{Addr: address.Address(\"localhost:27018\"), AverageRTT: 10 * time.Second, AverageRTTSet: true},\n\t\t\t\t\t\t{Addr: address.Address(\"localhost:27019\"), AverageRTT: 26 * time.Second, AverageRTTSet: true},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tstart: 0,\n\t\t\t\tend:   2,\n\t\t\t},\n\t\t\t{\n\t\t\t\tname:  \"No Servers\",\n\t\t\t\tdesc:  description.Topology{Servers: []description.Server{}},\n\t\t\t\tstart: 0,\n\t\t\t\tend:   0,\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"1 Server\",\n\t\t\t\tdesc: description.Topology{\n\t\t\t\t\tServers: []description.Server{\n\t\t\t\t\t\t{Addr: address.Address(\"localhost:27017\"), AverageRTT: 26 * time.Second, AverageRTTSet: true},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tstart: 0,\n\t\t\t\tend:   1,\n\t\t\t},\n\t\t}\n\n\t\tfor _, tc := range testCases {\n\t\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\t\tresult, err := (&Latency{Latency: 20 * time.Second}).SelectServer(tc.desc, tc.desc.Servers)\n\t\t\t\tnoerr(t, err)\n\t\t\t\tif len(result) != tc.end-tc.start {\n\t\t\t\t\tt.Errorf(\"Incorrect number of servers selected. got %d; want %d\", len(result), tc.end-tc.start)\n\t\t\t\t}\n\t\t\t\tif diff := cmp.Diff(result, tc.desc.Servers[tc.start:tc.end]); diff != \"\" {\n\t\t\t\t\tt.Errorf(\"Incorrect servers selected (-got +want):\\n%s\", diff)\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t})\n}\n\nvar readPrefTestPrimary = description.Server{\n\tAddr:              address.Address(\"localhost:27017\"),\n\tHeartbeatInterval: time.Duration(10) * time.Second,\n\tLastWriteTime:     time.Date(2017, 2, 11, 14, 0, 0, 0, time.UTC),\n\tLastUpdateTime:    time.Date(2017, 2, 11, 14, 0, 2, 0, time.UTC),\n\tKind:              description.ServerKindRSPrimary,\n\tTags:              tag.Set{tag.Tag{Name: \"a\", Value: \"1\"}},\n\tWireVersion:       &description.VersionRange{Min: 6, Max: 21},\n}\n\nvar readPrefTestSecondary1 = description.Server{\n\tAddr:              address.Address(\"localhost:27018\"),\n\tHeartbeatInterval: time.Duration(10) * time.Second,\n\tLastWriteTime:     time.Date(2017, 2, 11, 13, 58, 0, 0, time.UTC),\n\tLastUpdateTime:    time.Date(2017, 2, 11, 14, 0, 2, 0, time.UTC),\n\tKind:              description.ServerKindRSSecondary,\n\tTags:              tag.Set{tag.Tag{Name: \"a\", Value: \"1\"}},\n\tWireVersion:       &description.VersionRange{Min: 6, Max: 21},\n}\n\nvar readPrefTestSecondary2 = description.Server{\n\tAddr:              address.Address(\"localhost:27018\"),\n\tHeartbeatInterval: time.Duration(10) * time.Second,\n\tLastWriteTime:     time.Date(2017, 2, 11, 14, 0, 0, 0, time.UTC),\n\tLastUpdateTime:    time.Date(2017, 2, 11, 14, 0, 2, 0, time.UTC),\n\tKind:              description.ServerKindRSSecondary,\n\tTags:              tag.Set{tag.Tag{Name: \"a\", Value: \"2\"}},\n\tWireVersion:       &description.VersionRange{Min: 6, Max: 21},\n}\n\nvar readPrefTestTopology = description.Topology{\n\tKind:    description.TopologyKindReplicaSetWithPrimary,\n\tServers: []description.Server{readPrefTestPrimary, readPrefTestSecondary1, readPrefTestSecondary2},\n}\n\nfunc TestSelector_Sharded(t *testing.T) {\n\tt.Parallel()\n\n\tsubject := readpref.Primary()\n\n\ts := description.Server{\n\t\tAddr:              address.Address(\"localhost:27017\"),\n\t\tHeartbeatInterval: time.Duration(10) * time.Second,\n\t\tLastWriteTime:     time.Date(2017, 2, 11, 14, 0, 0, 0, time.UTC),\n\t\tLastUpdateTime:    time.Date(2017, 2, 11, 14, 0, 2, 0, time.UTC),\n\t\tKind:              description.ServerKindMongos,\n\t\tWireVersion:       &description.VersionRange{Min: 6, Max: 21},\n\t}\n\tc := description.Topology{\n\t\tKind:    description.TopologyKindSharded,\n\t\tServers: []description.Server{s},\n\t}\n\n\tresult, err := (&ReadPref{ReadPref: subject}).SelectServer(c, c.Servers)\n\n\trequire.NoError(t, err)\n\trequire.Len(t, result, 1)\n\trequire.Equal(t, []description.Server{s}, result)\n}\n\nfunc BenchmarkLatencySelector(b *testing.B) {\n\tfor _, bcase := range []struct {\n\t\tname        string\n\t\tserversHook func(servers []description.Server)\n\t}{\n\t\t{\n\t\t\tname:        \"AllFit\",\n\t\t\tserversHook: func([]description.Server) {},\n\t\t},\n\t\t{\n\t\t\tname: \"AllButOneFit\",\n\t\t\tserversHook: func(servers []description.Server) {\n\t\t\t\tservers[0].AverageRTT = 2 * time.Second\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"HalfFit\",\n\t\t\tserversHook: func(servers []description.Server) {\n\t\t\t\tfor i := 0; i < len(servers); i += 2 {\n\t\t\t\t\tservers[i].AverageRTT = 2 * time.Second\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"OneFit\",\n\t\t\tserversHook: func(servers []description.Server) {\n\t\t\t\tfor i := 1; i < len(servers); i++ {\n\t\t\t\t\tservers[i].AverageRTT = 2 * time.Second\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t} {\n\t\tbcase := bcase\n\n\t\tb.Run(bcase.name, func(b *testing.B) {\n\t\t\ts := description.Server{\n\t\t\t\tAddr:              address.Address(\"localhost:27017\"),\n\t\t\t\tHeartbeatInterval: time.Duration(10) * time.Second,\n\t\t\t\tLastWriteTime:     time.Date(2017, 2, 11, 14, 0, 0, 0, time.UTC),\n\t\t\t\tLastUpdateTime:    time.Date(2017, 2, 11, 14, 0, 2, 0, time.UTC),\n\t\t\t\tKind:              description.ServerKindMongos,\n\t\t\t\tWireVersion:       &description.VersionRange{Min: 6, Max: 21},\n\t\t\t\tAverageRTTSet:     true,\n\t\t\t\tAverageRTT:        time.Second,\n\t\t\t}\n\t\t\tservers := make([]description.Server, 100)\n\t\t\tfor i := 0; i < len(servers); i++ {\n\t\t\t\tservers[i] = s\n\t\t\t}\n\t\t\tbcase.serversHook(servers)\n\t\t\t// this will make base 1 sec latency < min (0.5) + conf (1)\n\t\t\t// and high latency 2 higher than the threshold\n\t\t\tservers[99].AverageRTT = 500 * time.Millisecond\n\t\t\tc := description.Topology{\n\t\t\t\tKind:    description.TopologyKindSharded,\n\t\t\t\tServers: servers,\n\t\t\t}\n\n\t\t\tb.ResetTimer()\n\t\t\tb.RunParallel(func(p *testing.PB) {\n\t\t\t\tb.ReportAllocs()\n\t\t\t\tfor p.Next() {\n\t\t\t\t\t_, _ = (&Latency{Latency: time.Second}).SelectServer(c, c.Servers)\n\t\t\t\t}\n\t\t\t})\n\t\t})\n\t}\n}\n\nfunc BenchmarkSelector_Sharded(b *testing.B) {\n\tfor _, bcase := range []struct {\n\t\tname        string\n\t\tserversHook func(servers []description.Server)\n\t}{\n\t\t{\n\t\t\tname:        \"AllFit\",\n\t\t\tserversHook: func([]description.Server) {},\n\t\t},\n\t\t{\n\t\t\tname: \"AllButOneFit\",\n\t\t\tserversHook: func(servers []description.Server) {\n\t\t\t\tservers[0].Kind = description.ServerKindLoadBalancer\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"HalfFit\",\n\t\t\tserversHook: func(servers []description.Server) {\n\t\t\t\tfor i := 0; i < len(servers); i += 2 {\n\t\t\t\t\tservers[i].Kind = description.ServerKindLoadBalancer\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"OneFit\",\n\t\t\tserversHook: func(servers []description.Server) {\n\t\t\t\tfor i := 1; i < len(servers); i++ {\n\t\t\t\t\tservers[i].Kind = description.ServerKindLoadBalancer\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t} {\n\t\tbcase := bcase\n\n\t\tb.Run(bcase.name, func(b *testing.B) {\n\t\t\tsubject := readpref.Primary()\n\n\t\t\ts := description.Server{\n\t\t\t\tAddr:              address.Address(\"localhost:27017\"),\n\t\t\t\tHeartbeatInterval: time.Duration(10) * time.Second,\n\t\t\t\tLastWriteTime:     time.Date(2017, 2, 11, 14, 0, 0, 0, time.UTC),\n\t\t\t\tLastUpdateTime:    time.Date(2017, 2, 11, 14, 0, 2, 0, time.UTC),\n\t\t\t\tKind:              description.ServerKindMongos,\n\t\t\t\tWireVersion:       &description.VersionRange{Min: 6, Max: 21},\n\t\t\t}\n\t\t\tservers := make([]description.Server, 100)\n\t\t\tfor i := 0; i < len(servers); i++ {\n\t\t\t\tservers[i] = s\n\t\t\t}\n\t\t\tbcase.serversHook(servers)\n\t\t\tc := description.Topology{\n\t\t\t\tKind:    description.TopologyKindSharded,\n\t\t\t\tServers: servers,\n\t\t\t}\n\n\t\t\tb.ResetTimer()\n\t\t\tb.RunParallel(func(p *testing.PB) {\n\t\t\t\tb.ReportAllocs()\n\t\t\t\tfor p.Next() {\n\t\t\t\t\t_, _ = (&ReadPref{ReadPref: subject}).SelectServer(c, c.Servers)\n\t\t\t\t}\n\t\t\t})\n\t\t})\n\t}\n}\n\nfunc Benchmark_SelectServer_SelectServer(b *testing.B) {\n\ttopology := description.Topology{Kind: description.TopologyKindReplicaSet} // You can change the topology as needed\n\tcandidates := []description.Server{\n\t\t{Kind: description.ServerKindMongos},\n\t\t{Kind: description.ServerKindRSPrimary},\n\t\t{Kind: description.ServerKindStandalone},\n\t}\n\n\tselector := &Write{} // Assuming this is the receiver type\n\n\tb.ReportAllocs()\n\tb.ResetTimer()\n\n\tfor i := 0; i < b.N; i++ {\n\t\t_, err := selector.SelectServer(topology, candidates)\n\t\tif err != nil {\n\t\t\tb.Fatalf(\"Error selecting server: %v\", err)\n\t\t}\n\t}\n}\n\nfunc TestSelector_Single(t *testing.T) {\n\tt.Parallel()\n\n\tsubject := readpref.Primary()\n\n\ts := description.Server{\n\t\tAddr:              address.Address(\"localhost:27017\"),\n\t\tHeartbeatInterval: time.Duration(10) * time.Second,\n\t\tLastWriteTime:     time.Date(2017, 2, 11, 14, 0, 0, 0, time.UTC),\n\t\tLastUpdateTime:    time.Date(2017, 2, 11, 14, 0, 2, 0, time.UTC),\n\t\tKind:              description.ServerKindMongos,\n\t\tWireVersion:       &description.VersionRange{Min: 6, Max: 21},\n\t}\n\tc := description.Topology{\n\t\tKind:    description.TopologyKindSingle,\n\t\tServers: []description.Server{s},\n\t}\n\n\tresult, err := (&ReadPref{ReadPref: subject}).SelectServer(c, c.Servers)\n\n\trequire.NoError(t, err)\n\trequire.Len(t, result, 1)\n\trequire.Equal(t, []description.Server{s}, result)\n}\n\nfunc TestSelector_Primary(t *testing.T) {\n\tt.Parallel()\n\n\tsubject := readpref.Primary()\n\n\tresult, err := (&ReadPref{ReadPref: subject}).SelectServer(readPrefTestTopology, readPrefTestTopology.Servers)\n\n\trequire.NoError(t, err)\n\trequire.Len(t, result, 1)\n\trequire.Equal(t, []description.Server{readPrefTestPrimary}, result)\n}\n\nfunc TestSelector_Primary_with_no_primary(t *testing.T) {\n\tt.Parallel()\n\n\tsubject := readpref.Primary()\n\n\tresult, err := (&ReadPref{ReadPref: subject}).\n\t\tSelectServer(readPrefTestTopology, []description.Server{readPrefTestSecondary1, readPrefTestSecondary2})\n\n\trequire.NoError(t, err)\n\trequire.Len(t, result, 0)\n}\n\nfunc TestSelector_PrimaryPreferred(t *testing.T) {\n\tt.Parallel()\n\n\tsubject := readpref.PrimaryPreferred()\n\n\tresult, err := (&ReadPref{ReadPref: subject}).\n\t\tSelectServer(readPrefTestTopology, readPrefTestTopology.Servers)\n\n\trequire.NoError(t, err)\n\trequire.Len(t, result, 1)\n\trequire.Equal(t, []description.Server{readPrefTestPrimary}, result)\n}\n\nfunc TestSelector_PrimaryPreferred_ignores_tags(t *testing.T) {\n\tt.Parallel()\n\n\tsubject := readpref.PrimaryPreferred(\n\t\treadpref.WithTags(\"a\", \"2\"),\n\t)\n\n\tresult, err := (&ReadPref{ReadPref: subject}).\n\t\tSelectServer(readPrefTestTopology, readPrefTestTopology.Servers)\n\n\trequire.NoError(t, err)\n\trequire.Len(t, result, 1)\n\trequire.Equal(t, []description.Server{readPrefTestPrimary}, result)\n}\n\nfunc TestSelector_PrimaryPreferred_with_no_primary(t *testing.T) {\n\tt.Parallel()\n\n\tsubject := readpref.PrimaryPreferred()\n\n\tresult, err := (&ReadPref{ReadPref: subject}).\n\t\tSelectServer(readPrefTestTopology, []description.Server{readPrefTestSecondary1, readPrefTestSecondary2})\n\n\trequire.NoError(t, err)\n\trequire.Len(t, result, 2)\n\trequire.Equal(t, []description.Server{readPrefTestSecondary1, readPrefTestSecondary2}, result)\n}\n\nfunc TestSelector_PrimaryPreferred_with_no_primary_and_tags(t *testing.T) {\n\tt.Parallel()\n\n\tsubject := readpref.PrimaryPreferred(\n\t\treadpref.WithTags(\"a\", \"2\"),\n\t)\n\n\tresult, err := (&ReadPref{ReadPref: subject}).\n\t\tSelectServer(readPrefTestTopology, []description.Server{readPrefTestSecondary1, readPrefTestSecondary2})\n\n\trequire.NoError(t, err)\n\trequire.Len(t, result, 1)\n\trequire.Equal(t, []description.Server{readPrefTestSecondary2}, result)\n}\n\nfunc TestSelector_PrimaryPreferred_with_maxStaleness(t *testing.T) {\n\tt.Parallel()\n\n\tsubject := readpref.PrimaryPreferred(\n\t\treadpref.WithMaxStaleness(time.Duration(90) * time.Second),\n\t)\n\n\tresult, err := (&ReadPref{ReadPref: subject}).\n\t\tSelectServer(readPrefTestTopology, readPrefTestTopology.Servers)\n\n\trequire.NoError(t, err)\n\trequire.Len(t, result, 1)\n\trequire.Equal(t, []description.Server{readPrefTestPrimary}, result)\n}\n\nfunc TestSelector_PrimaryPreferred_with_maxStaleness_and_no_primary(t *testing.T) {\n\tt.Parallel()\n\n\tsubject := readpref.PrimaryPreferred(\n\t\treadpref.WithMaxStaleness(time.Duration(90) * time.Second),\n\t)\n\n\tresult, err := (&ReadPref{ReadPref: subject}).\n\t\tSelectServer(readPrefTestTopology, []description.Server{readPrefTestSecondary1, readPrefTestSecondary2})\n\n\trequire.NoError(t, err)\n\trequire.Len(t, result, 1)\n\trequire.Equal(t, []description.Server{readPrefTestSecondary2}, result)\n}\n\nfunc TestSelector_SecondaryPreferred(t *testing.T) {\n\tt.Parallel()\n\n\tsubject := readpref.SecondaryPreferred()\n\n\tresult, err := (&ReadPref{ReadPref: subject}).SelectServer(readPrefTestTopology, readPrefTestTopology.Servers)\n\n\trequire.NoError(t, err)\n\trequire.Len(t, result, 2)\n\trequire.Equal(t, []description.Server{readPrefTestSecondary1, readPrefTestSecondary2}, result)\n}\n\nfunc TestSelector_SecondaryPreferred_with_tags(t *testing.T) {\n\tt.Parallel()\n\n\tsubject := readpref.SecondaryPreferred(\n\t\treadpref.WithTags(\"a\", \"2\"),\n\t)\n\n\tresult, err := (&ReadPref{ReadPref: subject}).SelectServer(readPrefTestTopology, readPrefTestTopology.Servers)\n\n\trequire.NoError(t, err)\n\trequire.Len(t, result, 1)\n\trequire.Equal(t, []description.Server{readPrefTestSecondary2}, result)\n}\n\nfunc TestSelector_SecondaryPreferred_with_tags_that_do_not_match(t *testing.T) {\n\tt.Parallel()\n\n\tsubject := readpref.SecondaryPreferred(\n\t\treadpref.WithTags(\"a\", \"3\"),\n\t)\n\n\tresult, err := (&ReadPref{ReadPref: subject}).SelectServer(readPrefTestTopology, readPrefTestTopology.Servers)\n\n\trequire.NoError(t, err)\n\trequire.Len(t, result, 1)\n\trequire.Equal(t, []description.Server{readPrefTestPrimary}, result)\n}\n\nfunc TestSelector_SecondaryPreferred_with_tags_that_do_not_match_and_no_primary(t *testing.T) {\n\tt.Parallel()\n\n\tsubject := readpref.SecondaryPreferred(\n\t\treadpref.WithTags(\"a\", \"3\"),\n\t)\n\n\tresult, err := (&ReadPref{ReadPref: subject}).SelectServer(readPrefTestTopology, []description.Server{readPrefTestSecondary1, readPrefTestSecondary2})\n\n\trequire.NoError(t, err)\n\trequire.Len(t, result, 0)\n}\n\nfunc TestSelector_SecondaryPreferred_with_no_secondaries(t *testing.T) {\n\tt.Parallel()\n\n\tsubject := readpref.SecondaryPreferred()\n\n\tresult, err := (&ReadPref{ReadPref: subject}).SelectServer(readPrefTestTopology, []description.Server{readPrefTestPrimary})\n\n\trequire.NoError(t, err)\n\trequire.Len(t, result, 1)\n\trequire.Equal(t, []description.Server{readPrefTestPrimary}, result)\n}\n\nfunc TestSelector_SecondaryPreferred_with_no_secondaries_or_primary(t *testing.T) {\n\tt.Parallel()\n\n\tsubject := readpref.SecondaryPreferred()\n\n\tresult, err := (&ReadPref{ReadPref: subject}).SelectServer(readPrefTestTopology, []description.Server{})\n\n\trequire.NoError(t, err)\n\trequire.Len(t, result, 0)\n}\n\nfunc TestSelector_SecondaryPreferred_with_maxStaleness(t *testing.T) {\n\tt.Parallel()\n\n\tsubject := readpref.SecondaryPreferred(\n\t\treadpref.WithMaxStaleness(time.Duration(90) * time.Second),\n\t)\n\n\tresult, err := (&ReadPref{ReadPref: subject}).SelectServer(readPrefTestTopology, readPrefTestTopology.Servers)\n\n\trequire.NoError(t, err)\n\trequire.Len(t, result, 1)\n\trequire.Equal(t, []description.Server{readPrefTestSecondary2}, result)\n}\n\nfunc TestSelector_SecondaryPreferred_with_maxStaleness_and_no_primary(t *testing.T) {\n\tt.Parallel()\n\n\tsubject := readpref.SecondaryPreferred(\n\t\treadpref.WithMaxStaleness(time.Duration(90) * time.Second),\n\t)\n\n\tresult, err := (&ReadPref{ReadPref: subject}).SelectServer(readPrefTestTopology, []description.Server{readPrefTestSecondary1, readPrefTestSecondary2})\n\n\trequire.NoError(t, err)\n\trequire.Len(t, result, 1)\n\trequire.Equal(t, []description.Server{readPrefTestSecondary2}, result)\n}\n\nfunc TestSelector_Secondary(t *testing.T) {\n\tt.Parallel()\n\n\tsubject := readpref.Secondary()\n\n\tresult, err := (&ReadPref{ReadPref: subject}).SelectServer(readPrefTestTopology, readPrefTestTopology.Servers)\n\n\trequire.NoError(t, err)\n\trequire.Len(t, result, 2)\n\trequire.Equal(t, []description.Server{readPrefTestSecondary1, readPrefTestSecondary2}, result)\n}\n\nfunc TestSelector_Secondary_with_tags(t *testing.T) {\n\tt.Parallel()\n\n\tsubject := readpref.Secondary(\n\t\treadpref.WithTags(\"a\", \"2\"),\n\t)\n\n\tresult, err := (&ReadPref{ReadPref: subject}).SelectServer(readPrefTestTopology, readPrefTestTopology.Servers)\n\n\trequire.NoError(t, err)\n\trequire.Len(t, result, 1)\n\trequire.Equal(t, []description.Server{readPrefTestSecondary2}, result)\n}\n\nfunc TestSelector_Secondary_with_empty_tag_set(t *testing.T) {\n\tt.Parallel()\n\n\tprimaryNoTags := description.Server{\n\t\tAddr:        address.Address(\"localhost:27017\"),\n\t\tKind:        description.ServerKindRSPrimary,\n\t\tWireVersion: &description.VersionRange{Min: 6, Max: 21},\n\t}\n\tfirstSecondaryNoTags := description.Server{\n\t\tAddr:        address.Address(\"localhost:27018\"),\n\t\tKind:        description.ServerKindRSSecondary,\n\t\tWireVersion: &description.VersionRange{Min: 6, Max: 21},\n\t}\n\tsecondSecondaryNoTags := description.Server{\n\t\tAddr:        address.Address(\"localhost:27019\"),\n\t\tKind:        description.ServerKindRSSecondary,\n\t\tWireVersion: &description.VersionRange{Min: 6, Max: 21},\n\t}\n\ttopologyNoTags := description.Topology{\n\t\tKind:    description.TopologyKindReplicaSetWithPrimary,\n\t\tServers: []description.Server{primaryNoTags, firstSecondaryNoTags, secondSecondaryNoTags},\n\t}\n\n\tnonMatchingSet := tag.Set{\n\t\t{Name: \"foo\", Value: \"bar\"},\n\t}\n\temptyTagSet := tag.Set{}\n\trp := readpref.Secondary(\n\t\treadpref.WithTagSets(nonMatchingSet, emptyTagSet),\n\t)\n\n\tresult, err := (&ReadPref{ReadPref: rp}).SelectServer(topologyNoTags, topologyNoTags.Servers)\n\tassert.Nil(t, err, \"SelectServer error: %v\", err)\n\texpectedResult := []description.Server{firstSecondaryNoTags, secondSecondaryNoTags}\n\tassert.Equal(t, expectedResult, result, \"expected result %v, got %v\", expectedResult, result)\n}\n\nfunc TestSelector_Secondary_with_tags_that_do_not_match(t *testing.T) {\n\tt.Parallel()\n\n\tsubject := readpref.Secondary(\n\t\treadpref.WithTags(\"a\", \"3\"),\n\t)\n\n\tresult, err := (&ReadPref{ReadPref: subject}).SelectServer(readPrefTestTopology, readPrefTestTopology.Servers)\n\n\trequire.NoError(t, err)\n\trequire.Len(t, result, 0)\n}\n\nfunc TestSelector_Secondary_with_no_secondaries(t *testing.T) {\n\tt.Parallel()\n\n\tsubject := readpref.Secondary()\n\n\tresult, err := (&ReadPref{ReadPref: subject}).SelectServer(readPrefTestTopology, []description.Server{readPrefTestPrimary})\n\n\trequire.NoError(t, err)\n\trequire.Len(t, result, 0)\n}\n\nfunc TestSelector_Secondary_with_maxStaleness(t *testing.T) {\n\tt.Parallel()\n\n\tsubject := readpref.Secondary(\n\t\treadpref.WithMaxStaleness(time.Duration(90) * time.Second),\n\t)\n\n\tresult, err := (&ReadPref{ReadPref: subject}).SelectServer(readPrefTestTopology, readPrefTestTopology.Servers)\n\n\trequire.NoError(t, err)\n\trequire.Len(t, result, 1)\n\trequire.Equal(t, []description.Server{readPrefTestSecondary2}, result)\n}\n\nfunc TestSelector_Secondary_with_maxStaleness_and_no_primary(t *testing.T) {\n\tt.Parallel()\n\n\tsubject := readpref.Secondary(\n\t\treadpref.WithMaxStaleness(time.Duration(90) * time.Second),\n\t)\n\n\tresult, err := (&ReadPref{ReadPref: subject}).SelectServer(readPrefTestTopology, []description.Server{readPrefTestSecondary1, readPrefTestSecondary2})\n\n\trequire.NoError(t, err)\n\trequire.Len(t, result, 1)\n\trequire.Equal(t, []description.Server{readPrefTestSecondary2}, result)\n}\n\nfunc TestSelector_Nearest(t *testing.T) {\n\tt.Parallel()\n\n\tsubject := readpref.Nearest()\n\n\tresult, err := (&ReadPref{ReadPref: subject}).SelectServer(readPrefTestTopology, readPrefTestTopology.Servers)\n\n\trequire.NoError(t, err)\n\trequire.Len(t, result, 3)\n\trequire.Equal(t, []description.Server{readPrefTestPrimary, readPrefTestSecondary1, readPrefTestSecondary2}, result)\n}\n\nfunc TestSelector_Nearest_with_tags(t *testing.T) {\n\tt.Parallel()\n\n\tsubject := readpref.Nearest(\n\t\treadpref.WithTags(\"a\", \"1\"),\n\t)\n\n\tresult, err := (&ReadPref{ReadPref: subject}).SelectServer(readPrefTestTopology, readPrefTestTopology.Servers)\n\n\trequire.NoError(t, err)\n\trequire.Len(t, result, 2)\n\trequire.Equal(t, []description.Server{readPrefTestPrimary, readPrefTestSecondary1}, result)\n}\n\nfunc TestSelector_Nearest_with_tags_that_do_not_match(t *testing.T) {\n\tt.Parallel()\n\n\tsubject := readpref.Nearest(\n\t\treadpref.WithTags(\"a\", \"3\"),\n\t)\n\n\tresult, err := (&ReadPref{ReadPref: subject}).SelectServer(readPrefTestTopology, readPrefTestTopology.Servers)\n\n\trequire.NoError(t, err)\n\trequire.Len(t, result, 0)\n}\n\nfunc TestSelector_Nearest_with_no_primary(t *testing.T) {\n\tt.Parallel()\n\n\tsubject := readpref.Nearest()\n\n\tresult, err := (&ReadPref{ReadPref: subject}).SelectServer(readPrefTestTopology, []description.Server{readPrefTestSecondary1, readPrefTestSecondary2})\n\n\trequire.NoError(t, err)\n\trequire.Len(t, result, 2)\n\trequire.Equal(t, []description.Server{readPrefTestSecondary1, readPrefTestSecondary2}, result)\n}\n\nfunc TestSelector_Nearest_with_no_secondaries(t *testing.T) {\n\tt.Parallel()\n\n\tsubject := readpref.Nearest()\n\n\tresult, err := (&ReadPref{ReadPref: subject}).SelectServer(readPrefTestTopology, []description.Server{readPrefTestPrimary})\n\n\trequire.NoError(t, err)\n\trequire.Len(t, result, 1)\n\trequire.Equal(t, []description.Server{readPrefTestPrimary}, result)\n}\n\nfunc TestSelector_Nearest_with_maxStaleness(t *testing.T) {\n\tt.Parallel()\n\n\tsubject := readpref.Nearest(\n\t\treadpref.WithMaxStaleness(time.Duration(90) * time.Second),\n\t)\n\n\tresult, err := (&ReadPref{ReadPref: subject}).SelectServer(readPrefTestTopology, readPrefTestTopology.Servers)\n\n\trequire.NoError(t, err)\n\trequire.Len(t, result, 2)\n\trequire.Equal(t, []description.Server{readPrefTestPrimary, readPrefTestSecondary2}, result)\n}\n\nfunc TestSelector_Nearest_with_maxStaleness_and_no_primary(t *testing.T) {\n\tt.Parallel()\n\n\tsubject := readpref.Nearest(\n\t\treadpref.WithMaxStaleness(time.Duration(90) * time.Second),\n\t)\n\n\tresult, err := (&ReadPref{ReadPref: subject}).SelectServer(readPrefTestTopology, []description.Server{readPrefTestSecondary1, readPrefTestSecondary2})\n\n\trequire.NoError(t, err)\n\trequire.Len(t, result, 1)\n\trequire.Equal(t, []description.Server{readPrefTestSecondary2}, result)\n}\n\nfunc TestSelector_Max_staleness_is_less_than_90_seconds(t *testing.T) {\n\tt.Parallel()\n\n\tsubject := readpref.Nearest(\n\t\treadpref.WithMaxStaleness(time.Duration(50) * time.Second),\n\t)\n\n\ts := description.Server{\n\t\tAddr:              address.Address(\"localhost:27017\"),\n\t\tHeartbeatInterval: time.Duration(10) * time.Second,\n\t\tLastWriteTime:     time.Date(2017, 2, 11, 14, 0, 0, 0, time.UTC),\n\t\tLastUpdateTime:    time.Date(2017, 2, 11, 14, 0, 2, 0, time.UTC),\n\t\tKind:              description.ServerKindRSPrimary,\n\t\tWireVersion:       &description.VersionRange{Min: 6, Max: 21},\n\t}\n\tc := description.Topology{\n\t\tKind:    description.TopologyKindReplicaSetWithPrimary,\n\t\tServers: []description.Server{s},\n\t}\n\n\t_, err := (&ReadPref{ReadPref: subject}).SelectServer(c, c.Servers)\n\n\trequire.Error(t, err)\n}\n\nfunc TestSelector_Max_staleness_is_too_low(t *testing.T) {\n\tt.Parallel()\n\n\tsubject := readpref.Nearest(\n\t\treadpref.WithMaxStaleness(time.Duration(100) * time.Second),\n\t)\n\n\ts := description.Server{\n\t\tAddr:              address.Address(\"localhost:27017\"),\n\t\tHeartbeatInterval: time.Duration(100) * time.Second,\n\t\tLastWriteTime:     time.Date(2017, 2, 11, 14, 0, 0, 0, time.UTC),\n\t\tLastUpdateTime:    time.Date(2017, 2, 11, 14, 0, 2, 0, time.UTC),\n\t\tKind:              description.ServerKindRSPrimary,\n\t\tWireVersion:       &description.VersionRange{Min: 6, Max: 21},\n\t}\n\tc := description.Topology{\n\t\tKind:    description.TopologyKindReplicaSetWithPrimary,\n\t\tServers: []description.Server{s},\n\t}\n\n\t_, err := (&ReadPref{ReadPref: subject}).SelectServer(c, c.Servers)\n\n\trequire.Error(t, err)\n}\n\nfunc TestEqualServers(t *testing.T) {\n\tint64ToPtr := func(i64 int64) *int64 { return &i64 }\n\n\tt.Run(\"equals\", func(t *testing.T) {\n\t\tdefaultServer := description.Server{}\n\t\t// Only some of the Server fields affect equality\n\t\ttestCases := []struct {\n\t\t\tname   string\n\t\t\tserver description.Server\n\t\t\tequal  bool\n\t\t}{\n\t\t\t{\"empty\", description.Server{}, true},\n\t\t\t{\"address\", description.Server{Addr: address.Address(\"foo\")}, true},\n\t\t\t{\"arbiters\", description.Server{Arbiters: []string{\"foo\"}}, false},\n\t\t\t{\"rtt\", description.Server{AverageRTT: time.Second}, true},\n\t\t\t{\"compression\", description.Server{Compression: []string{\"foo\"}}, true},\n\t\t\t{\"canonicalAddr\", description.Server{CanonicalAddr: address.Address(\"foo\")}, false},\n\t\t\t{\"electionID\", description.Server{ElectionID: bson.NewObjectID()}, false},\n\t\t\t{\"heartbeatInterval\", description.Server{HeartbeatInterval: time.Second}, true},\n\t\t\t{\"hosts\", description.Server{Hosts: []string{\"foo\"}}, false},\n\t\t\t{\"lastError\", description.Server{LastError: errors.New(\"foo\")}, false},\n\t\t\t{\"lastUpdateTime\", description.Server{LastUpdateTime: time.Now()}, true},\n\t\t\t{\"lastWriteTime\", description.Server{LastWriteTime: time.Now()}, true},\n\t\t\t{\"maxBatchCount\", description.Server{MaxBatchCount: 1}, true},\n\t\t\t{\"maxDocumentSize\", description.Server{MaxDocumentSize: 1}, true},\n\t\t\t{\"maxMessageSize\", description.Server{MaxMessageSize: 1}, true},\n\t\t\t{\"members\", description.Server{Members: []address.Address{address.Address(\"foo\")}}, true},\n\t\t\t{\"passives\", description.Server{Passives: []string{\"foo\"}}, false},\n\t\t\t{\"passive\", description.Server{Passive: true}, true},\n\t\t\t{\"primary\", description.Server{Primary: address.Address(\"foo\")}, false},\n\t\t\t{\"readOnly\", description.Server{ReadOnly: true}, true},\n\t\t\t{\n\t\t\t\t\"sessionTimeoutMinutes\",\n\t\t\t\tdescription.Server{\n\t\t\t\t\tSessionTimeoutMinutes: int64ToPtr(1),\n\t\t\t\t},\n\t\t\t\tfalse,\n\t\t\t},\n\t\t\t{\"setName\", description.Server{SetName: \"foo\"}, false},\n\t\t\t{\"setVersion\", description.Server{SetVersion: 1}, false},\n\t\t\t{\"tags\", description.Server{Tags: tag.Set{tag.Tag{\"foo\", \"bar\"}}}, false},\n\t\t\t{\"topologyVersion\", description.Server{TopologyVersion: &description.TopologyVersion{bson.NewObjectID(), 0}}, false},\n\t\t\t{\"kind\", description.Server{Kind: description.ServerKindStandalone}, false},\n\t\t\t{\"wireVersion\", description.Server{WireVersion: &description.VersionRange{1, 2}}, false},\n\t\t}\n\t\tfor _, tc := range testCases {\n\t\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\t\tactual := driverutil.EqualServers(defaultServer, tc.server)\n\t\t\t\tassert.Equal(t, actual, tc.equal, \"expected %v, got %v\", tc.equal, actual)\n\t\t\t})\n\t\t}\n\t})\n}\n\nfunc TestVersionRangeIncludes(t *testing.T) {\n\tt.Parallel()\n\n\tsubject := driverutil.NewVersionRange(1, 3)\n\n\ttests := []struct {\n\t\tn        int32\n\t\texpected bool\n\t}{\n\t\t{0, false},\n\t\t{1, true},\n\t\t{2, true},\n\t\t{3, true},\n\t\t{4, false},\n\t\t{10, false},\n\t}\n\n\tfor _, test := range tests {\n\t\tactual := driverutil.VersionRangeIncludes(subject, test.n)\n\t\tif actual != test.expected {\n\t\t\tt.Fatalf(\"expected %v to be %t\", test.n, test.expected)\n\t\t}\n\t}\n}\n\nfunc TestDeprioritizedSelector(t *testing.T) {\n\tt.Parallel()\n\n\ttests := []struct {\n\t\tname          string\n\t\tdeprioritized []description.Server\n\t\tcandidates    []description.Server\n\t\twant          []description.Server\n\t}{\n\t\t{\n\t\t\tname:       \"empty\",\n\t\t\tcandidates: []description.Server{},\n\t\t\twant:       []description.Server{},\n\t\t},\n\t\t{\n\t\t\tname:       \"nil candidates\",\n\t\t\tcandidates: nil,\n\t\t\twant:       []description.Server{},\n\t\t},\n\t\t{\n\t\t\tname: \"nil deprioritized server list\",\n\t\t\tcandidates: []description.Server{\n\t\t\t\t{\n\t\t\t\t\tAddr: address.Address(\"mongodb://localhost:27017\"),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: []description.Server{\n\t\t\t\t{\n\t\t\t\t\tAddr: address.Address(\"mongodb://localhost:27017\"),\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"deprioritize single server candidate list\",\n\t\t\tcandidates: []description.Server{\n\t\t\t\t{\n\t\t\t\t\tAddr: address.Address(\"mongodb://localhost:27017\"),\n\t\t\t\t},\n\t\t\t},\n\t\t\tdeprioritized: []description.Server{\n\t\t\t\t{\n\t\t\t\t\tAddr: address.Address(\"mongodb://localhost:27017\"),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: []description.Server{\n\t\t\t\t// Since all available servers were deprioritized, then the selector\n\t\t\t\t// should return all candidates.\n\t\t\t\t{\n\t\t\t\t\tAddr: address.Address(\"mongodb://localhost:27017\"),\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"deprioritize one server in multi-server candidate list\",\n\t\t\tcandidates: []description.Server{\n\t\t\t\t{\n\t\t\t\t\tAddr: address.Address(\"mongodb://localhost:27017\"),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tAddr: address.Address(\"mongodb://localhost:27018\"),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tAddr: address.Address(\"mongodb://localhost:27019\"),\n\t\t\t\t},\n\t\t\t},\n\t\t\tdeprioritized: []description.Server{\n\t\t\t\t{\n\t\t\t\t\tAddr: address.Address(\"mongodb://localhost:27017\"),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: []description.Server{\n\t\t\t\t{\n\t\t\t\t\tAddr: address.Address(\"mongodb://localhost:27018\"),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tAddr: address.Address(\"mongodb://localhost:27019\"),\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"deprioritize multiple servers in multi-server candidate list\",\n\t\t\tdeprioritized: []description.Server{\n\t\t\t\t{\n\t\t\t\t\tAddr: address.Address(\"mongodb://localhost:27017\"),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tAddr: address.Address(\"mongodb://localhost:27018\"),\n\t\t\t\t},\n\t\t\t},\n\t\t\tcandidates: []description.Server{\n\t\t\t\t{\n\t\t\t\t\tAddr: address.Address(\"mongodb://localhost:27017\"),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tAddr: address.Address(\"mongodb://localhost:27018\"),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tAddr: address.Address(\"mongodb://localhost:27019\"),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: []description.Server{\n\t\t\t\t{\n\t\t\t\t\tAddr: address.Address(\"mongodb://localhost:27019\"),\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"deprioritize server with state change\",\n\t\t\t// Server was RSPrimary when deprioritized, but has since stepped down to RSSecondary.\n\t\t\t// It should still be filtered out based on address alone.\n\t\t\tdeprioritized: []description.Server{\n\t\t\t\t{\n\t\t\t\t\tAddr: address.Address(\"mongodb://localhost:27017\"),\n\t\t\t\t\tKind: description.ServerKindRSPrimary,\n\t\t\t\t},\n\t\t\t},\n\t\t\tcandidates: []description.Server{\n\t\t\t\t{\n\t\t\t\t\tAddr: address.Address(\"mongodb://localhost:27017\"),\n\t\t\t\t\tKind: description.ServerKindRSSecondary, // State changed\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tAddr: address.Address(\"mongodb://localhost:27018\"),\n\t\t\t\t\tKind: description.ServerKindRSSecondary,\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: []description.Server{\n\t\t\t\t{\n\t\t\t\t\tAddr: address.Address(\"mongodb://localhost:27018\"),\n\t\t\t\t\tKind: description.ServerKindRSSecondary,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"deprioritize server with multiple property changes\",\n\t\t\t// Server had different RTT and tags when deprioritized.\n\t\t\t// It should still be filtered out based on address alone.\n\t\t\tdeprioritized: []description.Server{\n\t\t\t\t{\n\t\t\t\t\tAddr:       address.Address(\"mongodb://localhost:27017\"),\n\t\t\t\t\tKind:       description.ServerKindRSPrimary,\n\t\t\t\t\tAverageRTT: 50 * time.Millisecond,\n\t\t\t\t},\n\t\t\t},\n\t\t\tcandidates: []description.Server{\n\t\t\t\t{\n\t\t\t\t\tAddr:       address.Address(\"mongodb://localhost:27017\"),\n\t\t\t\t\tKind:       description.ServerKindRSSecondary,\n\t\t\t\t\tAverageRTT: 5 * time.Millisecond,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tAddr:       address.Address(\"mongodb://localhost:27018\"),\n\t\t\t\t\tKind:       description.ServerKindRSPrimary,\n\t\t\t\t\tAverageRTT: 25 * time.Millisecond,\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: []description.Server{\n\t\t\t\t{\n\t\t\t\t\tAddr:       address.Address(\"mongodb://localhost:27018\"),\n\t\t\t\t\tKind:       description.ServerKindRSPrimary,\n\t\t\t\t\tAverageRTT: 25 * time.Millisecond,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n\n\tfor _, tc := range tests {\n\t\ttc := tc // Capture the range variable.\n\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\t// Use a pass-through selector as InnerSelector to test deprioritization logic\n\t\t\tpassthrough := Func(func(_ description.Topology, s []description.Server) ([]description.Server, error) {\n\t\t\t\treturn s, nil\n\t\t\t})\n\t\t\tselector := NewDeprioritized(passthrough, tc.deprioritized)\n\t\t\tgot, err := selector.SelectServer(description.Topology{}, tc.candidates)\n\t\t\trequire.NoError(t, err)\n\t\t\tassert.ElementsMatch(t, got, tc.want)\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "internal/spectest/skip.go",
    "content": "// Copyright (C) MongoDB, Inc. 2025-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage spectest\n\nimport \"testing\"\n\n// skipTests is a map of \"fully-qualified test name\" to \"the reason for skipping\n// the test\".\nvar skipTests = map[string][]string{\n\t// SPEC-1403: This test checks to see if the correct error is thrown when auto\n\t// encrypting with a server < 4.2. Currently, the test will fail because a\n\t// server < 4.2 wouldn't have mongocryptd, so Client construction would fail\n\t// with a mongocryptd spawn error.\n\t\"Servers less than 4.2 do not have mongocryptd; see SPEC-1403\": {\n\t\t\"TestClientSideEncryptionSpec/maxWireVersion.json/operation_fails_with_maxWireVersion_<_8\",\n\t},\n\n\t// TODO(GODRIVER-1826): Tests for incompatible event ordering in load-balancer\n\t// SDAM spec tests.\n\t\"Event ordering is incompatible with load-balancer SDAM spec test (DRIVERS-1785)\": {\n\t\t\"TestCMAPSpec/pool-create-min-size-error.json/error_during_minPoolSize_population_clears_pool\",\n\t},\n\n\t// TODO(GODRIVER-1826): Race condition prevents the \"threads blocked by\n\t// maxConnecting\" test from passing.\n\t\"Test requires that connections established by minPoolSize are immediately used to satisfy check-out requests (DRIVERS-2225)\": {\n\t\t\"TestCMAPSpec/pool-checkout-minPoolSize-connection-maxConnecting.json/threads_blocked_by_maxConnecting_check_out_minPoolSize_connections\",\n\t},\n\n\t// TODO(GODRIVER-1826): The Go connection pool behavior for check-in requests\n\t// is incompatible with expected test behavior.\n\t\"Test requires a checked-in connections cannot satisfy a check-out waiting on a new connection (DRIVERS-2223)\": {\n\t\t\"TestCMAPSpec/pool-checkout-returned-connection-maxConnecting.json/threads_blocked_by_maxConnecting_check_out_returned_connections\",\n\t},\n\n\t// TODO(GODRIVER-2129): Re-enable this test once the feature is implemented.\n\t// TODO(GODRIVER-2129): Support for\n\t// sspiHostnamecanonicalization=none/forward/forwardAndReverse for Kerberos\n\t\"Requires sspiHostnamecanonicalization=none/forward/forwardAndReverse support for Kerberos (GODRIVER-2129)\": {\n\t\t\"TestAuthSpec/connection-string.json/must_raise_an_error_when_the_hostname_canonicalization_is_invalid\",\n\t},\n\n\t// TODO(GODRIVER-3614): Remove support for specifying MONGODB-AWS authentication properties explicitly\n\t\"Should throw an exception if username provided (MONGODB-AWS) (GODRIVER-3614)\": {\n\t\t\"TestAuthSpec/connection-string.json/should_throw_an_exception_if_username_and_password_provided_(MONGODB-AWS)\",\n\t},\n\n\t// TODO(GODRIVER-2183): Implementation of Socks5 Proxy Support is pending.\n\t\"Requires Socks5 Proxy Support (GODRIVER-2183)\": {\n\t\t\"TestURIOptionsSpec/proxy-options.json/proxyPort_without_proxyHost\",\n\t\t\"TestURIOptionsSpec/proxy-options.json/proxyUsername_without_proxyHost\",\n\t\t\"TestURIOptionsSpec/proxy-options.json/proxyPassword_without_proxyHost\",\n\t\t\"TestURIOptionsSpec/proxy-options.json/all_other_proxy_options_without_proxyHost\",\n\t\t\"TestURIOptionsSpec/proxy-options.json/proxyUsername_without_proxyPassword\",\n\t\t\"TestURIOptionsSpec/proxy-options.json/proxyPassword_without_proxyUsername\",\n\t\t\"TestURIOptionsSpec/proxy-options.json/multiple_proxyHost_parameters\",\n\t\t\"TestURIOptionsSpec/proxy-options.json/multiple_proxyPort_parameters\",\n\t\t\"TestURIOptionsSpec/proxy-options.json/multiple_proxyUsername_parameters\",\n\t\t\"TestURIOptionsSpec/proxy-options.json/multiple_proxyPassword_parameters\",\n\t},\n\n\t// The wtimeoutMS option for write concern is deprecated.\n\t\"wtimeoutMS is deprecated\": {\n\t\t\"TestURIOptionsSpec/concern-options.json/Valid_read_and_write_concern_are_parsed_correctly\",\n\t\t\"TestURIOptionsSpec/concern-options.json/Non-numeric_wTimeoutMS_causes_a_warning\",\n\t\t\"TestURIOptionsSpec/concern-options.json/Too_low_wTimeoutMS_causes_a_warning\",\n\t\t\"TestReadWriteConcernSpec/connstring/write-concern.json/wtimeoutMS_as_an_invalid_number\",\n\t},\n\n\t// Unsupported TLS behavior in connection strings.\n\t\"unsupported connstring behavior\": {\n\t\t\"TestURIOptionsSpec/tls-options.json/tlsAllowInvalidCertificates_and_tlsDisableCertificateRevocationCheck_both_present_(and_true)_raises_an_error\",\n\t\t\"TestURIOptionsSpec/tls-options.json/tlsAllowInvalidCertificates=true_and_tlsDisableCertificateRevocationCheck=false_raises_an_error\",\n\t\t\"TestURIOptionsSpec/tls-options.json/tlsAllowInvalidCertificates=false_and_tlsDisableCertificateRevocationCheck=true_raises_an_error\",\n\t\t\"TestURIOptionsSpec/tls-options.json/tlsAllowInvalidCertificates_and_tlsDisableCertificateRevocationCheck_both_present_(and_false)_raises_an_error\",\n\t\t\"TestURIOptionsSpec/tls-options.json/tlsDisableCertificateRevocationCheck_and_tlsAllowInvalidCertificates_both_present_(and_true)_raises_an_error\",\n\t\t\"TestURIOptionsSpec/tls-options.json/tlsDisableCertificateRevocationCheck=true_and_tlsAllowInvalidCertificates=false_raises_an_error\",\n\t\t\"TestURIOptionsSpec/tls-options.json/tlsDisableCertificateRevocationCheck=false_and_tlsAllowInvalidCertificates=true_raises_an_error\",\n\t\t\"TestURIOptionsSpec/tls-options.json/tlsDisableCertificateRevocationCheck_and_tlsAllowInvalidCertificates_both_present_(and_false)_raises_an_error\",\n\t\t\"TestURIOptionsSpec/tls-options.json/tlsInsecure_and_tlsDisableCertificateRevocationCheck_both_present_(and_true)_raises_an_error\",\n\t\t\"TestURIOptionsSpec/tls-options.json/tlsInsecure=true_and_tlsDisableCertificateRevocationCheck=false_raises_an_error\",\n\t\t\"TestURIOptionsSpec/tls-options.json/tlsInsecure=false_and_tlsDisableCertificateRevocationCheck=true_raises_an_error\",\n\t\t\"TestURIOptionsSpec/tls-options.json/tlsInsecure_and_tlsDisableCertificateRevocationCheck_both_present_(and_false)_raises_an_error\",\n\t\t\"TestURIOptionsSpec/tls-options.json/tlsDisableCertificateRevocationCheck_and_tlsInsecure_both_present_(and_true)_raises_an_error\",\n\t\t\"TestURIOptionsSpec/tls-options.json/tlsDisableCertificateRevocationCheck=true_and_tlsInsecure=false_raises_an_error\",\n\t\t\"TestURIOptionsSpec/tls-options.json/tlsDisableCertificateRevocationCheck=false_and_tlsInsecure=true_raises_an_error\",\n\t\t\"TestURIOptionsSpec/tls-options.json/tlsDisableCertificateRevocationCheck_and_tlsInsecure_both_present_(and_false)_raises_an_error\",\n\t\t\"TestURIOptionsSpec/tls-options.json/tlsDisableCertificateRevocationCheck_and_tlsDisableOCSPEndpointCheck_both_present_(and_true)_raises_an_error\",\n\t\t\"TestURIOptionsSpec/tls-options.json/tlsDisableCertificateRevocationCheck=true_and_tlsDisableOCSPEndpointCheck=false_raises_an_error\",\n\t\t\"TestURIOptionsSpec/tls-options.json/tlsDisableCertificateRevocationCheck=false_and_tlsDisableOCSPEndpointCheck=true_raises_an_error\",\n\t\t\"TestURIOptionsSpec/tls-options.json/tlsDisableCertificateRevocationCheck_and_tlsDisableOCSPEndpointCheck_both_present_(and_false)_raises_an_error\",\n\t\t\"TestURIOptionsSpec/tls-options.json/tlsDisableOCSPEndpointCheck_and_tlsDisableCertificateRevocationCheck_both_present_(and_true)_raises_an_error\",\n\t\t\"TestURIOptionsSpec/tls-options.json/tlsDisableOCSPEndpointCheck=true_and_tlsDisableCertificateRevocationCheck=false_raises_an_error\",\n\t\t\"TestURIOptionsSpec/tls-options.json/tlsDisableOCSPEndpointCheck=false_and_tlsDisableCertificateRevocationCheck=true_raises_an_error\",\n\t\t\"TestURIOptionsSpec/tls-options.json/tlsDisableOCSPEndpointCheck_and_tlsDisableCertificateRevocationCheck_both_present_(and_false)_raises_an_error\",\n\t\t\"TestURIOptionsSpec/tls-options.json/tlsAllowInvalidCertificates_and_tlsDisableOCSPEndpointCheck_both_present_(and_true)_raises_an_error\",\n\t\t\"TestURIOptionsSpec/tls-options.json/tlsAllowInvalidCertificates=true_and_tlsDisableOCSPEndpointCheck=false_raises_an_error\",\n\t\t\"TestURIOptionsSpec/tls-options.json/tlsAllowInvalidCertificates=false_and_tlsDisableOCSPEndpointCheck=true_raises_an_error\",\n\t\t\"TestURIOptionsSpec/tls-options.json/tlsAllowInvalidCertificates_and_tlsDisableOCSPEndpointCheck_both_present_(and_false)_raises_an_error\",\n\t\t\"TestURIOptionsSpec/tls-options.json/tlsDisableOCSPEndpointCheck_and_tlsAllowInvalidCertificates_both_present_(and_true)_raises_an_error\",\n\t\t\"TestURIOptionsSpec/tls-options.json/tlsDisableOCSPEndpointCheck=true_and_tlsAllowInvalidCertificates=false_raises_an_error\",\n\t\t\"TestURIOptionsSpec/tls-options.json/tlsDisableOCSPEndpointCheck=false_and_tlsAllowInvalidCertificates=true_raises_an_error\",\n\t\t\"TestURIOptionsSpec/tls-options.json/tlsDisableOCSPEndpointCheck_and_tlsAllowInvalidCertificates_both_present_(and_false)_raises_an_error\",\n\t\t\"TestURIOptionsSpec/tls-options.json/Invalid_tlsAllowInvalidCertificates_causes_a_warning\",\n\t\t\"TestURIOptionsSpec/tls-options.json/tlsAllowInvalidCertificates_is_parsed_correctly\",\n\t\t\"TestURIOptionsSpec/tls-options.json/tlsAllowInvalidHostnames_is_parsed_correctly\",\n\t\t\"TestURIOptionsSpec/tls-options.json/Invalid_tlsAllowInvalidHostnames_causes_a_warning\",\n\t\t\"TestURIOptionsSpec/tls-options.json/tlsInsecure_and_tlsAllowInvalidCertificates_both_present_(and_true)_raises_an_error\",\n\t\t\"TestURIOptionsSpec/tls-options.json/tlsInsecure_and_tlsAllowInvalidCertificates_both_present_(and_false)_raises_an_error\",\n\t\t\"TestURIOptionsSpec/tls-options.json/tlsAllowInvalidCertificates_and_tlsInsecure_both_present_(and_true)_raises_an_error\",\n\t\t\"TestURIOptionsSpec/tls-options.json/tlsAllowInvalidCertificates_and_tlsInsecure_both_present_(and_false)_raises_an_error\",\n\t\t\"TestURIOptionsSpec/tls-options.json/tlsInsecure_and_tlsAllowInvalidHostnames_both_present_(and_true)_raises_an_error\",\n\t\t\"TestURIOptionsSpec/tls-options.json/tlsInsecure_and_tlsAllowInvalidHostnames_both_present_(and_false)_raises_an_error\",\n\t\t\"TestURIOptionsSpec/tls-options.json/tlsAllowInvalidHostnames_and_tlsInsecure_both_present_(and_true)_raises_an_error\",\n\t\t\"TestURIOptionsSpec/tls-options.json/tlsAllowInvalidHostnames_and_tlsInsecure_both_present_(and_false)_raises_an_error\",\n\t},\n\n\t// TODO(GODRIVER-2991): Make delimiting slash between hosts and options\n\t// optional.\n\t\"Requires making delimiting slash between hosts and options optional (GODRIVERS-2991)\": {\n\t\t\"TestConnStringSpec/valid-options.json/Missing_delimiting_slash_between_hosts_and_options\",\n\t},\n\n\t// Connstring tests violate current Go Driver behavior.\n\t\"unsupported behavior\": {\n\t\t\"TestURIOptionsSpec/connection-pool-options.json/maxConnecting=0_causes_a_warning\",\n\t\t\"TestURIOptionsSpec/single-threaded-options.json/Invalid_serverSelectionTryOnce_causes_a_warning\",\n\t\t\"TestConnStringSpec/valid-warnings.json/Empty_integer_option_values_are_ignored\",\n\t\t\"TestConnStringSpec/valid-warnings.json/Empty_boolean_option_value_are_ignored\",\n\t\t\"TestConnStringSpec/valid-warnings.json/Comma_in_a_key_value_pair_causes_a_warning\",\n\t},\n\n\t// TODO(GODRIVER-3167): Support assertions on topologyDescriptionChangedEvent\n\t// in expectEvents.\n\t\"Support assertions on topologyDescriptionChangedEvent in expectEvents (GODRIVER-3167)\": {\n\t\t\"TestUnifiedSpec/unified-test-format/tests/valid-pass/expectedEventsForClient-topologyDescriptionChangedEvent.json/can_assert_on_values_of_newDescription_and_previousDescription_fields\",\n\t},\n\n\t// TODO(GODRIVER-3409): Add regression test for \"number\" alias in $$type\n\t// operator.\n\t\"Regression test for 'number' alias in $$type operator (GODRIVER-3409)\": {\n\t\t\"TestUnifiedSpec/unified-test-format/tests/valid-pass/operator-type-number_alias.json/type_number_alias_matches_int32\",\n\t\t\"TestUnifiedSpec/unified-test-format/tests/valid-pass/operator-type-number_alias.json/type_number_alias_matches_int64\",\n\t\t\"TestUnifiedSpec/unified-test-format/tests/valid-pass/operator-type-number_alias.json/type_number_alias_matches_double\",\n\t\t\"TestUnifiedSpec/unified-test-format/tests/valid-pass/operator-type-number_alias.json/type_number_alias_matches_decimal128\",\n\t},\n\n\t// TODO(GODRIVER-3143): Convert CRUD v1 spec tests to unified test format.\n\t\"Convert CRUD v1 spec tests to unified test format (GODRIVER-3143)\": {\n\t\t\"TestUnifiedSpec/crud/tests/unified/bulkWrite-collation.json/BulkWrite_with_delete_operations_and_collation\",\n\t\t\"TestUnifiedSpec/crud/tests/unified/count.json/Count_documents_with_skip_and_limit\",\n\t\t\"TestUnifiedSpec/crud/tests/unified/findOne.json/FindOne_with_filter,_sort,_and_skip\",\n\t},\n\n\t// TODO(GODRIVER-2125): Allow hint for unacknowledged writes using OP_MSG when\n\t// supported by the server.\n\t\"Allow hint for unacknowledged writes using OP_MSG (GODRIVER-2125)\": {\n\t\t\"TestUnifiedSpec/crud/tests/unified/bulkWrite-deleteMany-hint-unacknowledged.json/Unacknowledged_deleteMany_with_hint_string_on_4.4+_server\",\n\t\t\"TestUnifiedSpec/crud/tests/unified/bulkWrite-deleteMany-hint-unacknowledged.json/Unacknowledged_deleteMany_with_hint_document_on_4.4+_server\",\n\t\t\"TestUnifiedSpec/crud/tests/unified/bulkWrite-deleteOne-hint-unacknowledged.json/Unacknowledged_deleteOne_with_hint_string_on_4.4+_server\",\n\t\t\"TestUnifiedSpec/crud/tests/unified/bulkWrite-deleteOne-hint-unacknowledged.json/Unacknowledged_deleteOne_with_hint_document_on_4.4+_server\",\n\t\t\"TestUnifiedSpec/crud/tests/unified/bulkWrite-replaceOne-hint-unacknowledged.json/Unacknowledged_replaceOne_with_hint_string_on_4.2+_server\",\n\t\t\"TestUnifiedSpec/crud/tests/unified/bulkWrite-replaceOne-hint-unacknowledged.json/Unacknowledged_replaceOne_with_hint_document_on_4.2+_server\",\n\t\t\"TestUnifiedSpec/crud/tests/unified/bulkWrite-updateMany-hint-unacknowledged.json/Unacknowledged_updateMany_with_hint_string_on_4.2+_server\",\n\t\t\"TestUnifiedSpec/crud/tests/unified/bulkWrite-updateMany-hint-unacknowledged.json/Unacknowledged_updateMany_with_hint_document_on_4.2+_server\",\n\t\t\"TestUnifiedSpec/crud/tests/unified/bulkWrite-updateOne-hint-unacknowledged.json/Unacknowledged_updateOne_with_hint_string_on_4.2+_server\",\n\t\t\"TestUnifiedSpec/crud/tests/unified/bulkWrite-updateOne-hint-unacknowledged.json/Unacknowledged_updateOne_with_hint_document_on_4.2+_server\",\n\t},\n\n\t// TODO(GODRIVER-3407): Allow drivers to set bypassDocumentValidation: false\n\t// on write commands.\n\t\"Allow drivers to set bypassDocumentValidation: false on write commands (GODRIVER-3407)\": {\n\t\t\"TestUnifiedSpec/crud/tests/unified/bypassDocumentValidation.json/Aggregate_with_$out_passes_bypassDocumentValidation:_false\",\n\t\t\"TestUnifiedSpec/crud/tests/unified/bypassDocumentValidation.json/BulkWrite_passes_bypassDocumentValidation:_false\",\n\t\t\"TestUnifiedSpec/crud/tests/unified/bypassDocumentValidation.json/FindOneAndReplace_passes_bypassDocumentValidation:_false\",\n\t\t\"TestUnifiedSpec/crud/tests/unified/bypassDocumentValidation.json/FindOneAndUpdate_passes_bypassDocumentValidation:_fals\",\n\t\t\"TestUnifiedSpec/crud/tests/unified/bypassDocumentValidation.json/FindOneAndUpdate_passes_bypassDocumentValidation:_false\",\n\t\t\"TestUnifiedSpec/crud/tests/unified/bypassDocumentValidation.json/InsertMany_passes_bypassDocumentValidation:_false\",\n\t\t\"TestUnifiedSpec/crud/tests/unified/bypassDocumentValidation.json/InsertOne_passes_bypassDocumentValidation:_false\",\n\t\t\"TestUnifiedSpec/crud/tests/unified/bypassDocumentValidation.json/ReplaceOne_passes_bypassDocumentValidation:_false\",\n\t\t\"TestUnifiedSpec/crud/tests/unified/bypassDocumentValidation.json/UpdateMany_passes_bypassDocumentValidation:_false\",\n\t\t\"TestUnifiedSpec/crud/tests/unified/bypassDocumentValidation.json/UpdateOne_passes_bypassDocumentValidation:_false\",\n\t\t\"TestUnifiedSpec/crud/tests/unified/deleteMany-hint-unacknowledged.json/Unacknowledged_deleteMany_with_hint_string_on_4.4+_server\",\n\t\t\"TestUnifiedSpec/crud/tests/unified/deleteMany-hint-unacknowledged.json/Unacknowledged_deleteMany_with_hint_document_on_4.4+_server\",\n\t\t\"TestUnifiedSpec/crud/tests/unified/deleteOne-hint-unacknowledged.json/Unacknowledged_deleteOne_with_hint_string_on_4.4+_server\",\n\t\t\"TestUnifiedSpec/crud/tests/unified/deleteOne-hint-unacknowledged.json/Unacknowledged_deleteOne_with_hint_document_on_4.4+_server\",\n\t\t\"TestUnifiedSpec/crud/tests/unified/findOneAndDelete-hint-unacknowledged.json/Unacknowledged_findOneAndDelete_with_hint_string_on_4.4+_server\",\n\t\t\"TestUnifiedSpec/crud/tests/unified/findOneAndDelete-hint-unacknowledged.json/Unacknowledged_findOneAndDelete_with_hint_document_on_4.4+_server\",\n\t\t\"TestUnifiedSpec/crud/tests/unified/findOneAndReplace-hint-unacknowledged.json/Unacknowledged_findOneAndReplace_with_hint_string_on_4.4+_server\",\n\t\t\"TestUnifiedSpec/crud/tests/unified/findOneAndReplace-hint-unacknowledged.json/Unacknowledged_findOneAndReplace_with_hint_document_on_4.4+_server\",\n\t\t\"TestUnifiedSpec/crud/tests/unified/findOneAndUpdate-hint-unacknowledged.json/Unacknowledged_findOneAndUpdate_with_hint_string_on_4.4+_server\",\n\t\t\"TestUnifiedSpec/crud/tests/unified/findOneAndUpdate-hint-unacknowledged.json/Unacknowledged_findOneAndUpdate_with_hint_document_on_4.4+_server\",\n\t\t\"TestUnifiedSpec/crud/tests/unified/replaceOne-hint-unacknowledged.json/Unacknowledged_replaceOne_with_hint_string_on_4.2+_server\",\n\t\t\"TestUnifiedSpec/crud/tests/unified/replaceOne-hint-unacknowledged.json/Unacknowledged_replaceOne_with_hint_document_on_4.2+_server\",\n\t\t\"TestUnifiedSpec/crud/tests/unified/updateMany-hint-unacknowledged.json/Unacknowledged_updateMany_with_hint_string_on_4.2+_server\",\n\t\t\"TestUnifiedSpec/crud/tests/unified/updateMany-hint-unacknowledged.json/Unacknowledged_updateMany_with_hint_document_on_4.2+_server\",\n\t\t\"TestUnifiedSpec/crud/tests/unified/updateOne-hint-unacknowledged.json/Unacknowledged_updateOne_with_hint_string_on_4.2+_server\",\n\t\t\"TestUnifiedSpec/crud/tests/unified/updateOne-hint-unacknowledged.json/Unacknowledged_updateOne_with_hint_document_on_4.2+_server\",\n\t},\n\n\t// TODO(GODRIVER-3392): Test that inserts and upserts respect null _id values.\n\t\"Test inserts and upserts respect null _id values (GODRIVER-3392)\": {\n\t\t\"TestUnifiedSpec/crud/tests/unified/create-null-ids.json/inserting__id_with_type_null_via_insertOne\",\n\t\t\"TestUnifiedSpec/crud/tests/unified/create-null-ids.json/inserting__id_with_type_null_via_clientBulkWrite\",\n\t},\n\n\t// TODO(GODRIVER-3395): Ensure findOne does not set batchSize=1.\n\t\"Ensure findOne does not set batchSize=1 (GODRIVER-3395)\": {\n\t\t\"TestUnifiedSpec/crud/tests/unified/find.json/Find_with_batchSize_equal_to_limit\",\n\t},\n\n\t// TODO(GODRIVER-2016): Convert transactions spec tests to unified test\n\t// format.\n\t\"Convert transactions spec tests to unified test format (GODRIVER-2016)\": {\n\t\t\"TestUnifiedSpec/transactions-convenient-api/tests/unified/callback-retry.json/callback_is_not_retried_after_non-transient_error_(DuplicateKeyError)\",\n\t\t\"TestUnifiedSpec/transactions-convenient-api/tests/unified/callback-retry.json/callback_succeeds_after_multiple_connection_errors\",\n\t\t\"TestUnifiedSpec/transactions-convenient-api/tests/unified/commit-retry.json/commitTransaction_retry_only_overwrites_write_concern_w_option\",\n\t\t\"TestUnifiedSpec/transactions-convenient-api/tests/unified/commit-retry.json/commit_is_not_retried_after_MaxTimeMSExpired_error\",\n\t\t\"TestUnifiedSpec/transactions-convenient-api/tests/unified/commit-writeconcernerror.json/commitTransaction_is_not_retried_after_UnknownReplWriteConcern_error\",\n\t\t\"TestUnifiedSpec/transactions-convenient-api/tests/unified/commit-writeconcernerror.json/commitTransaction_is_not_retried_after_UnsatisfiableWriteConcern_error\",\n\t\t\"TestUnifiedSpec/transactions-convenient-api/tests/unified/commit-writeconcernerror.json/commitTransaction_is_not_retried_after_MaxTimeMSExpired_error\",\n\t},\n\n\t// TODO(GODRIVER-1773): Tests related to batch size expectation in \"find\" and\n\t// \"getMore\" events.\n\t\"Tests for batch size expectation in 'find' and 'getMore' events (GODRIVER-1773)\": {\n\t\t\"TestUnifiedSpec/command-logging-and-monitoring/tests/monitoring/find.json/A_successful_find_event_with_a_getmore_and_the_server_kills_the_cursor_(<=_4.4)\",\n\t\t\"TestUnifiedSpec/unified-test-format/tests/valid-pass/poc-command-monitoring.json/A_successful_find_event_with_a_getmore_and_the_server_kills_the_cursor_(<=_4.4)\",\n\t},\n\n\t// TODO(GODRIVER-2577): Tests require immediate operation canceling,\n\t// incompatible with current pool clearing logic.\n\t\"Require immediate operation canceling for pool clearing (GODRIVER-2577)\": {\n\t\t\"TestUnifiedSpec/server-discovery-and-monitoring/tests/unified/interruptInUse-pool-clear.json/Connection_pool_clear_uses_interruptInUseConnections=true_after_monitor_timeout\",\n\t\t\"TestUnifiedSpec/server-discovery-and-monitoring/tests/unified/interruptInUse-pool-clear.json/Error_returned_from_connection_pool_clear_with_interruptInUseConnections=true_is_retryable\",\n\t\t\"TestUnifiedSpec/server-discovery-and-monitoring/tests/unified/interruptInUse-pool-clear.json/Error_returned_from_connection_pool_clear_with_interruptInUseConnections=true_is_retryable_for_write\",\n\t},\n\n\t// TODO(GODRIVER-3043): Avoid Appending Write/Read Concern in Atlas Search\n\t// Index Helper Commands.\n\t\"Sync tests but avoid write/read concern bug (GODRIVER-3043)\": {\n\t\t\"TestUnifiedSpec/index-management/tests/searchIndexIgnoresReadWriteConcern.json/dropSearchIndex_ignores_read_and_write_concern\",\n\t\t\"TestUnifiedSpec/index-management/tests/searchIndexIgnoresReadWriteConcern.json/listSearchIndexes_ignores_read_and_write_concern\",\n\t\t\"TestUnifiedSpec/index-management/tests/searchIndexIgnoresReadWriteConcern.json/updateSearchIndex_ignores_the_read_and_write_concern\",\n\t},\n\n\t// TODO(DRIVERS-2829): Create CSOT Legacy Timeout Analogues and Compatibility\n\t// Field.\n\t\"Handles socketTimeoutMS instead of CSOT (DRIVERS-2829)\": {\n\t\t\"TestUnifiedSpec/server-discovery-and-monitoring/tests/unified/auth-network-timeout-error.json/Reset_server_and_pool_after_network_timeout_error_during_authentication\",\n\t\t\"TestUnifiedSpec/server-discovery-and-monitoring/tests/unified/find-network-timeout-error.json/Ignore_network_timeout_error_on_find\",\n\t\t\"TestUnifiedSpec/command-logging-and-monitoring/tests/monitoring/find.json/A_successful_find_with_options\",\n\t\t\"TestUnifiedSpec/crud/tests/unified/estimatedDocumentCount.json/estimatedDocumentCount_with_maxTimeMS\",\n\t\t\"TestUnifiedSpec/run-command/tests/unified/runCursorCommand.json/supports_configuring_getMore_maxTimeMS\",\n\t},\n\n\t// TODO(GODRIVER-3034): Drivers should unpin connections when ending a\n\t// session.\n\t\"Unpin connections at session end (GODRIVER-3034)\": {\n\t\t\"TestUnifiedSpec/transactions/unified/mongos-unpin.json/unpin_on_successful_abort\",\n\t\t\"TestUnifiedSpec/transactions/unified/mongos-unpin.json/unpin_after_non-transient_error_on_abort\",\n\t\t\"TestUnifiedSpec/transactions/unified/mongos-unpin.json/unpin_after_TransientTransactionError_error_on_abort\",\n\t\t\"TestUnifiedSpec/transactions/unified/mongos-unpin.json/unpin_when_a_new_transaction_is_started\",\n\t\t\"TestUnifiedSpec/transactions/unified/mongos-unpin.json/unpin_when_a_non-transaction_write_operation_uses_a_session\",\n\t\t\"TestUnifiedSpec/transactions/unified/mongos-unpin.json/unpin_when_a_non-transaction_read_operation_uses_a_session\",\n\t},\n\n\t// TODO(GODRIVER-3146): Convert retryable reads spec tests to unified test format.\n\t\"Convert retryable reads spec tests to unified test format (GODRIVER-3146)\": {\n\t\t\"TestUnifiedSpec/retryable-reads/tests/unified/listCollectionObjects-serverErrors.json/ListCollectionObjects_succeeds_after_InterruptedAtShutdown\",\n\t\t\"TestUnifiedSpec/retryable-reads/tests/unified/listCollectionObjects-serverErrors.json/ListCollectionObjects_succeeds_after_InterruptedDueToReplStateChange\",\n\t\t\"TestUnifiedSpec/retryable-reads/tests/unified/listCollectionObjects-serverErrors.json/ListCollectionObjects_succeeds_after_NotWritablePrimary\",\n\t\t\"TestUnifiedSpec/retryable-reads/tests/unified/listCollectionObjects-serverErrors.json/ListCollectionObjects_succeeds_after_NotPrimaryNoSecondaryOk\",\n\t\t\"TestUnifiedSpec/retryable-reads/tests/unified/listCollectionObjects-serverErrors.json/ListCollectionObjects_succeeds_after_NotPrimaryOrSecondary\",\n\t\t\"TestUnifiedSpec/retryable-reads/tests/unified/listCollectionObjects-serverErrors.json/ListCollectionObjects_succeeds_after_PrimarySteppedDown\",\n\t\t\"TestUnifiedSpec/retryable-reads/tests/unified/listCollectionObjects-serverErrors.json/ListCollectionObjects_succeeds_after_ShutdownInProgress\",\n\t\t\"TestUnifiedSpec/retryable-reads/tests/unified/listCollectionObjects-serverErrors.json/ListCollectionObjects_succeeds_after_HostNotFound\",\n\t\t\"TestUnifiedSpec/retryable-reads/tests/unified/listCollectionObjects-serverErrors.json/ListCollectionObjects_succeeds_after_HostUnreachable\",\n\t\t\"TestUnifiedSpec/retryable-reads/tests/unified/listCollectionObjects-serverErrors.json/ListCollectionObjects_succeeds_after_NetworkTimeout\",\n\t\t\"TestUnifiedSpec/retryable-reads/tests/unified/listCollectionObjects-serverErrors.json/ListCollectionObjects_succeeds_after_SocketException\",\n\t\t\"TestUnifiedSpec/retryable-reads/tests/unified/listCollectionObjects-serverErrors.json/ListCollectionObjects_fails_after_two_NotWritablePrimary_errors\",\n\t\t\"TestUnifiedSpec/retryable-reads/tests/unified/listCollectionObjects-serverErrors.json/ListCollectionObjects_fails_after_NotWritablePrimary_when_retryReads_is_false\",\n\t\t\"TestUnifiedSpec/retryable-reads/tests/unified/listCollectionObjects.json/ListCollectionObjects_succeeds_on_first_attempt\",\n\t\t\"TestUnifiedSpec/retryable-reads/tests/unified/listCollectionObjects.json/ListCollectionObjects_succeeds_on_second_attempt\",\n\t\t\"TestUnifiedSpec/retryable-reads/tests/unified/listCollectionObjects.json/ListCollectionObjects_fails_on_first_attempt\",\n\t\t\"TestUnifiedSpec/retryable-reads/tests/unified/listCollectionObjects.json/ListCollectionObjects_fails_on_second_attempt\",\n\t\t\"TestUnifiedSpec/retryable-reads/tests/unified/listDatabaseObjects-serverErrors.json/ListDatabaseObjects_succeeds_after_InterruptedAtShutdown\",\n\t\t\"TestUnifiedSpec/retryable-reads/tests/unified/listDatabaseObjects-serverErrors.json/ListDatabaseObjects_succeeds_after_InterruptedDueToReplStateChange\",\n\t\t\"TestUnifiedSpec/retryable-reads/tests/unified/listDatabaseObjects-serverErrors.json/ListDatabaseObjects_succeeds_after_NotWritablePrimary\",\n\t\t\"TestUnifiedSpec/retryable-reads/tests/unified/listDatabaseObjects-serverErrors.json/ListDatabaseObjects_succeeds_after_NotPrimaryNoSecondaryOk\",\n\t\t\"TestUnifiedSpec/retryable-reads/tests/unified/listDatabaseObjects-serverErrors.json/ListDatabaseObjects_succeeds_after_NotPrimaryOrSecondary\",\n\t\t\"TestUnifiedSpec/retryable-reads/tests/unified/listDatabaseObjects-serverErrors.json/ListDatabaseObjects_succeeds_after_PrimarySteppedDown\",\n\t\t\"TestUnifiedSpec/retryable-reads/tests/unified/listDatabaseObjects-serverErrors.json/ListDatabaseObjects_succeeds_after_ShutdownInProgress\",\n\t\t\"TestUnifiedSpec/retryable-reads/tests/unified/listDatabaseObjects-serverErrors.json/ListDatabaseObjects_succeeds_after_HostNotFound\",\n\t\t\"TestUnifiedSpec/retryable-reads/tests/unified/listDatabaseObjects-serverErrors.json/ListDatabaseObjects_succeeds_after_HostUnreachable\",\n\t\t\"TestUnifiedSpec/retryable-reads/tests/unified/listDatabaseObjects-serverErrors.json/ListDatabaseObjects_succeeds_after_NetworkTimeout\",\n\t\t\"TestUnifiedSpec/retryable-reads/tests/unified/listDatabaseObjects-serverErrors.json/ListDatabaseObjects_succeeds_after_SocketException\",\n\t\t\"TestUnifiedSpec/retryable-reads/tests/unified/listDatabaseObjects-serverErrors.json/ListDatabaseObjects_fails_after_two_NotWritablePrimary_errors\",\n\t\t\"TestUnifiedSpec/retryable-reads/tests/unified/listDatabaseObjects-serverErrors.json/ListDatabaseObjects_fails_after_NotWritablePrimary_when_retryReads_is_false\",\n\t\t\"TestUnifiedSpec/retryable-reads/tests/unified/listDatabaseObjects.json/ListDatabaseObjects_succeeds_on_first_attempt\",\n\t\t\"TestUnifiedSpec/retryable-reads/tests/unified/listDatabaseObjects.json/ListDatabaseObjects_succeeds_on_second_attempt\",\n\t\t\"TestUnifiedSpec/retryable-reads/tests/unified/listDatabaseObjects.json/ListDatabaseObjects_fails_on_first_attempt\",\n\t\t\"TestUnifiedSpec/retryable-reads/tests/unified/listDatabaseObjects.json/ListDatabaseObjects_fails_on_second_attempt\",\n\t\t\"TestUnifiedSpec/retryable-reads/tests/unified/mapReduce.json/MapReduce_succeeds_with_retry_on\",\n\t\t\"TestUnifiedSpec/retryable-reads/tests/unified/mapReduce.json/MapReduce_fails_with_retry_on\",\n\t\t\"TestUnifiedSpec/retryable-reads/tests/unified/mapReduce.json/MapReduce_fails_with_retry_off\",\n\t},\n\n\t// TODO(GODRIVER-2944): Setting \"maxTimeMS\" on a command that creates a cursor\n\t// may be surprising to users, so omit this from operations that return\n\t// user-managed cursors.\n\t\"Omit maxTimeMS from user-managed cursor operations (DRIVERS-2722)\": {\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/gridfs-find.json/timeoutMS_can_be_overridden_for_a_find\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-operation-timeoutMS.json/timeoutMS_can_be_configured_for_an_operation_-_find_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-operation-timeoutMS.json/timeoutMS_can_be_configured_for_an_operation_-_aggregate_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-operation-timeoutMS.json/timeoutMS_can_be_configured_for_an_operation_-_aggregate_on_database\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/global-timeoutMS.json/timeoutMS_can_be_configured_on_a_MongoClient_-_find_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/global-timeoutMS.json/timeoutMS_can_be_configured_on_a_MongoClient_-_aggregate_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/global-timeoutMS.json/timeoutMS_can_be_configured_on_a_MongoClient_-_aggregate_on_database\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/retryability-timeoutMS.json/operation_is_retried_multiple_times_for_non-zero_timeoutMS_-_find_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/retryability-timeoutMS.json/operation_is_retried_multiple_times_for_non-zero_timeoutMS_-_aggregate_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/retryability-timeoutMS.json/operation_is_retried_multiple_times_for_non-zero_timeoutMS_-_aggregate_on_database\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/gridfs-find.json/timeoutMS_applied_to_find_command\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/tailable-awaitData.json/timeoutMS_applied_to_find\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/tailable-awaitData.json/timeoutMS_is_refreshed_for_getMore_if_maxAwaitTimeMS_is_not_set\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/tailable-awaitData.json/timeoutMS_is_refreshed_for_getMore_if_maxAwaitTimeMS_is_set\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/tailable-awaitData.json/timeoutMS_is_refreshed_for_getMore_-_failure\",\n\t},\n\n\t// TODO(GODRIVER-3411): Tests require \"getMore\" with \"maxTimeMS\" settings. Not\n\t// supported for non-awaitData cursors.\n\t\"Omit maxTimeMS from a getMore (DRIVERS-2953)\": {\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/runCursorCommand.json/Non-tailable_cursor_lifetime_remaining_timeoutMS_applied_to_getMore_if_timeoutMode_is_unset\",\n\t},\n\n\t// TODO(GODRIVER-2466): Convert SDAM integration spec tests to unified test format.\n\t\"Convert SDAM integration tests to unified format (GODRIVER-2466)\": {\n\t\t\"TestUnifiedSpec/server-discovery-and-monitoring/tests/unified/rediscover-quickly-after-step-down.json/Rediscover_quickly_after_replSetStepDown\",\n\t},\n\n\t// TODO(GODRIVER-2967): Implement TopologyChangedEvent on topology close.\n\t\"Implement TopologyChangedEvent on close (GODRIVER-2967)\": {\n\t\t\"TestUnifiedSpec/server-discovery-and-monitoring/tests/unified/logging-loadbalanced.json/Topology_lifecycle\",\n\t\t\"TestUnifiedSpec/server-discovery-and-monitoring/tests/unified/logging-sharded.json/Topology_lifecycle\",\n\t\t\"TestUnifiedSpec/server-discovery-and-monitoring/tests/unified/logging-replicaset.json/Topology_lifecycle\",\n\t\t\"TestUnifiedSpec/server-discovery-and-monitoring/tests/unified/logging-standalone.json/Topology_lifecycle\",\n\t\t\"TestUnifiedSpec/server-discovery-and-monitoring/tests/unified/loadbalanced-emit-topology-changed-before-close.json/Topology_lifecycle\",\n\t\t\"TestUnifiedSpec/server-discovery-and-monitoring/tests/unified/sharded-emit-topology-changed-before-close.json/Topology_lifecycle\",\n\t\t\"TestUnifiedSpec/server-discovery-and-monitoring/tests/unified/replicaset-emit-topology-changed-before-close.json/Topology_lifecycle\",\n\t\t\"TestUnifiedSpec/server-discovery-and-monitoring/tests/unified/standalone-emit-topology-changed-before-close.json/Topology_lifecycle\",\n\t},\n\n\t// Unknown BSON format.\n\t\"Unsupported format\": {\n\t\t\"TestBsonBinaryVectorSpec/Tests_of_Binary_subtype_9,_Vectors,_with_dtype_FLOAT32/Infinity_Vector_FLOAT32/Marshaling\",\n\t\t\"TestBsonBinaryVectorSpec/Tests_of_Binary_subtype_9,_Vectors,_with_dtype_FLOAT32/Infinity_Vector_FLOAT32/Unmarshaling\",\n\t},\n\n\t// Unsupported BSON binary vector tests.\n\t\"compile-time restriction\": {\n\t\t\"TestBsonBinaryVectorSpec/Tests_of_Binary_subtype_9,_Vectors,_with_dtype_PACKED_BIT/Overflow_Vector_PACKED_BIT/Marshaling\",\n\t\t\"TestBsonBinaryVectorSpec/Tests_of_Binary_subtype_9,_Vectors,_with_dtype_INT8/Underflow_Vector_INT8/Marshaling\",\n\t\t\"TestBsonBinaryVectorSpec/Tests_of_Binary_subtype_9,_Vectors,_with_dtype_INT8/Overflow_Vector_INT8/Marshaling\",\n\t\t\"TestBsonBinaryVectorSpec/Tests_of_Binary_subtype_9,_Vectors,_with_dtype_PACKED_BIT/Negative_padding_PACKED_BIT/Marshaling\",\n\t\t\"TestBsonBinaryVectorSpec/Tests_of_Binary_subtype_9,_Vectors,_with_dtype_PACKED_BIT/Vector_with_float_values_PACKED_BIT/Marshaling\",\n\t\t\"TestBsonBinaryVectorSpec/Tests_of_Binary_subtype_9,_Vectors,_with_dtype_PACKED_BIT/Underflow_Vector_PACKED_BIT/Marshaling\",\n\t\t\"TestBsonBinaryVectorSpec/Tests_of_Binary_subtype_9,_Vectors,_with_dtype_INT8/INT8_with_float_inputs/Marshaling\",\n\t},\n\n\t// Unsupported BSON binary vector padding.\n\t\"private padding field\": {\n\t\t\"TestBsonBinaryVectorSpec/Tests_of_Binary_subtype_9,_Vectors,_with_dtype_INT8/INT8_with_padding/Marshaling\",\n\t\t\"TestBsonBinaryVectorSpec/Tests_of_Binary_subtype_9,_Vectors,_with_dtype_FLOAT32/FLOAT32_with_padding/Marshaling\",\n\t},\n\n\t// Invalid BSON vector cases.\n\t\"invalid case\": {\n\t\t\"TestBsonBinaryVectorSpec/Tests_of_Binary_subtype_9,_Vectors,_with_dtype_FLOAT32/Insufficient_vector_data_with_3_bytes_FLOAT32/Marshaling\",\n\t\t\"TestBsonBinaryVectorSpec/Tests_of_Binary_subtype_9,_Vectors,_with_dtype_FLOAT32/Insufficient_vector_data_with_5_bytes_FLOAT32/Marshaling\",\n\t},\n\n\t// TODO(GODRIVER-3521): Extend Legacy Unified Spec Runner to include\n\t// client-side-encryption timeoutMS.\n\t\"Extend Legacy Unified Spec Runner for client-side-encryption timeoutMS (GODRIVER-3521)\": {\n\t\t\"TestClientSideEncryptionSpec/timeoutMS.json/remaining_timeoutMS_applied_to_find_to_get_keyvault_data\",\n\t\t\"TestClientSideEncryptionSpec/timeoutMS.json/timeoutMS_applied_to_listCollections_to_get_collection_schema\",\n\t\t\"TestUnifiedSpec/client-side-encryption/tests/unified/timeoutMS.json/remaining_timeoutMS_applied_to_find_to_get_keyvault_data\",\n\t\t\"TestUnifiedSpec/client-side-encryption/tests/unified/timeoutMS.json/timeoutMS_applied_to_listCollections_to_get_collection_schema\",\n\t},\n\n\t// TODO(GODRIVER-3076): CSFLE/QE Support for more than 1 KMS provider per\n\t// type.\n\t\"Support multiple KMS providers per type (GODRIVER-3076)\": {\n\t\t\"TestUnifiedSpec/client-side-encryption/tests/unified/namedKMS-createDataKey.json/create_datakey_with_named_Azure_KMS_provider\",\n\t\t\"TestUnifiedSpec/client-side-encryption/tests/unified/namedKMS-createDataKey.json/create_datakey_with_named_GCP_KMS_provider\",\n\t\t\"TestUnifiedSpec/client-side-encryption/tests/unified/namedKMS-createDataKey.json/create_datakey_with_named_KMIP_KMS_provider\",\n\t\t\"TestUnifiedSpec/client-side-encryption/tests/unified/namedKMS-createDataKey.json/create_datakey_with_named_local_KMS_provider\",\n\t\t\"TestUnifiedSpec/client-side-encryption/tests/unified/namedKMS-explicit.json/can_explicitly_encrypt_with_a_named_KMS_provider\",\n\t\t\"TestUnifiedSpec/client-side-encryption/tests/unified/namedKMS-explicit.json/can_explicitly_decrypt_with_a_named_KMS_provider\",\n\t\t\"TestUnifiedSpec/client-side-encryption/tests/unified/namedKMS-rewrapManyDataKey.json/rewrap_to_aws:name1\",\n\t\t\"TestUnifiedSpec/client-side-encryption/tests/unified/namedKMS-rewrapManyDataKey.json/rewrap_to_azure:name1\",\n\t\t\"TestUnifiedSpec/client-side-encryption/tests/unified/namedKMS-rewrapManyDataKey.json/rewrap_to_gcp:name1\",\n\t\t\"TestUnifiedSpec/client-side-encryption/tests/unified/namedKMS-rewrapManyDataKey.json/rewrap_to_kmip:name1\",\n\t\t\"TestUnifiedSpec/client-side-encryption/tests/unified/namedKMS-rewrapManyDataKey.json/rewrap_to_local:name1\",\n\t\t\"TestUnifiedSpec/client-side-encryption/tests/unified/namedKMS-rewrapManyDataKey.json/rewrap_from_local:name1_to_local:name2\",\n\t\t\"TestUnifiedSpec/client-side-encryption/tests/unified/namedKMS-rewrapManyDataKey.json/rewrap_from_aws:name1_to_aws:name2\",\n\t\t\"TestUnifiedSpec/client-side-encryption/tests/unified/namedKMS-createDataKey.json/create_data_key_with_named_AWS_KMS_provider\",\n\t\t\"TestClientSideEncryptionSpec/namedKMS.json/Automatically_encrypt_and_decrypt_with_a_named_KMS_provider\",\n\t},\n\n\t// TODO(GODRIVER-3380): Change stream should resume with CSOT failure.\n\t\"Ensure change streams resume with CSOT failure (GODRIVER-3380)\": {\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/change-streams.json/timeoutMS_is_refreshed_for_getMore_if_maxAwaitTimeMS_is_not_set\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/change-streams.json/timeoutMS_is_refreshed_for_getMore_if_maxAwaitTimeMS_is_set\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/change-streams.json/timeoutMS_applies_to_full_resume_attempt_in_a_next_call\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/change-streams.json/change_stream_can_be_iterated_again_if_previous_iteration_times_out\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/change-streams.json/timeoutMS_is_refreshed_for_getMore_-_failure\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/change-streams.json/error_if_maxAwaitTimeMS_is_greater_than_timeoutMS\",\n\t},\n\n\t// Unknown CSOT:\n\t\"CSOT test not implemented\": {\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/close-cursors.json/timeoutMS_is_refreshed_for_close\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/convenient-transactions.json/withTransaction_raises_a_client-side_error_if_timeoutMS_is_overridden_inside_the_callback\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/convenient-transactions.json/timeoutMS_is_not_refreshed_for_each_operation_in_the_callback\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/convenient-transactions.json/withTransaction_surfaces_a_timeout_after_exhausting_transient_transaction_retries,_retaining_the_last_transient_error_as_the_timeout_cause.\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/cursors.json/find_errors_if_timeoutMode_is_set_and_timeoutMS_is_not\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/cursors.json/collection_aggregate_errors_if_timeoutMode_is_set_and_timeoutMS_is_not\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/cursors.json/database_aggregate_errors_if_timeoutMode_is_set_and_timeoutMS_is_not\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/cursors.json/listCollections_errors_if_timeoutMode_is_set_and_timeoutMS_is_not\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/cursors.json/listIndexes_errors_if_timeoutMode_is_set_and_timeoutMS_is_not\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/non-tailable-cursors.json/timeoutMS_applied_to_find_if_timeoutMode_is_cursor_lifetime\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/non-tailable-cursors.json/remaining_timeoutMS_applied_to_getMore_if_timeoutMode_is_unset\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/non-tailable-cursors.json/remaining_timeoutMS_applied_to_getMore_if_timeoutMode_is_cursor_lifetime\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/non-tailable-cursors.json/timeoutMS_applied_to_find_if_timeoutMode_is_iteration\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/non-tailable-cursors.json/timeoutMS_is_refreshed_for_getMore_if_timeoutMode_is_iteration_-_success\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/non-tailable-cursors.json/timeoutMS_is_refreshed_for_getMore_if_timeoutMode_is_iteration_-_failure\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/non-tailable-cursors.json/aggregate_with_$out_errors_if_timeoutMode_is_iteration\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/non-tailable-cursors.json/aggregate_with_$merge_errors_if_timeoutMode_is_iteration\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/gridfs-download.json/timeoutMS_applied_to_find_to_get_chunks\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/gridfs-download.json/timeoutMS_applied_to_entire_download,_not_individual_parts\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-collection-timeoutMS.json/timeoutMS_can_be_configured_on_a_MongoCollection_-_aggregate_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-collection-timeoutMS.json/timeoutMS_can_be_set_to_0_on_a_MongoCollection_-_aggregate_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-collection-timeoutMS.json/timeoutMS_can_be_configured_on_a_MongoCollection_-_count_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-collection-timeoutMS.json/timeoutMS_can_be_set_to_0_on_a_MongoCollection_-_count_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-collection-timeoutMS.json/timeoutMS_can_be_configured_on_a_MongoCollection_-_countDocuments_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-collection-timeoutMS.json/timeoutMS_can_be_set_to_0_on_a_MongoCollection_-_countDocuments_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-collection-timeoutMS.json/timeoutMS_can_be_configured_on_a_MongoCollection_-_estimatedDocumentCount_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-collection-timeoutMS.json/timeoutMS_can_be_set_to_0_on_a_MongoCollection_-_estimatedDocumentCount_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-collection-timeoutMS.json/timeoutMS_can_be_configured_on_a_MongoCollection_-_distinct_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-collection-timeoutMS.json/timeoutMS_can_be_set_to_0_on_a_MongoCollection_-_distinct_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-collection-timeoutMS.json/timeoutMS_can_be_configured_on_a_MongoCollection_-_find_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-collection-timeoutMS.json/timeoutMS_can_be_set_to_0_on_a_MongoCollection_-_find_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-collection-timeoutMS.json/timeoutMS_can_be_configured_on_a_MongoCollection_-_findOne_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-collection-timeoutMS.json/timeoutMS_can_be_set_to_0_on_a_MongoCollection_-_findOne_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-collection-timeoutMS.json/timeoutMS_can_be_configured_on_a_MongoCollection_-_listIndexes_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-collection-timeoutMS.json/timeoutMS_can_be_set_to_0_on_a_MongoCollection_-_listIndexes_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-collection-timeoutMS.json/timeoutMS_can_be_configured_on_a_MongoCollection_-_listIndexNames_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-collection-timeoutMS.json/timeoutMS_can_be_set_to_0_on_a_MongoCollection_-_listIndexNames_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-collection-timeoutMS.json/timeoutMS_can_be_configured_on_a_MongoCollection_-_createChangeStream_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-collection-timeoutMS.json/timeoutMS_can_be_set_to_0_on_a_MongoCollection_-_createChangeStream_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-collection-timeoutMS.json/timeoutMS_can_be_configured_on_a_MongoCollection_-_insertOne_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-collection-timeoutMS.json/timeoutMS_can_be_set_to_0_on_a_MongoCollection_-_insertOne_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-collection-timeoutMS.json/timeoutMS_can_be_configured_on_a_MongoCollection_-_insertMany_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-collection-timeoutMS.json/timeoutMS_can_be_set_to_0_on_a_MongoCollection_-_insertMany_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-collection-timeoutMS.json/timeoutMS_can_be_configured_on_a_MongoCollection_-_deleteOne_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-collection-timeoutMS.json/timeoutMS_can_be_set_to_0_on_a_MongoCollection_-_deleteOne_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-collection-timeoutMS.json/timeoutMS_can_be_configured_on_a_MongoCollection_-_deleteMany_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-collection-timeoutMS.json/timeoutMS_can_be_set_to_0_on_a_MongoCollection_-_deleteMany_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-collection-timeoutMS.json/timeoutMS_can_be_configured_on_a_MongoCollection_-_replaceOne_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-collection-timeoutMS.json/timeoutMS_can_be_set_to_0_on_a_MongoCollection_-_replaceOne_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-collection-timeoutMS.json/timeoutMS_can_be_configured_on_a_MongoCollection_-_updateOne_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-collection-timeoutMS.json/timeoutMS_can_be_set_to_0_on_a_MongoCollection_-_updateOne_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-collection-timeoutMS.json/timeoutMS_can_be_configured_on_a_MongoCollection_-_updateMany_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-collection-timeoutMS.json/timeoutMS_can_be_set_to_0_on_a_MongoCollection_-_updateMany_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-collection-timeoutMS.json/timeoutMS_can_be_configured_on_a_MongoCollection_-_findOneAndDelete_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-collection-timeoutMS.json/timeoutMS_can_be_set_to_0_on_a_MongoCollection_-_findOneAndDelete_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-collection-timeoutMS.json/timeoutMS_can_be_configured_on_a_MongoCollection_-_findOneAndReplace_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-collection-timeoutMS.json/timeoutMS_can_be_set_to_0_on_a_MongoCollection_-_findOneAndReplace_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-collection-timeoutMS.json/timeoutMS_can_be_configured_on_a_MongoCollection_-_findOneAndUpdate_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-collection-timeoutMS.json/timeoutMS_can_be_set_to_0_on_a_MongoCollection_-_findOneAndUpdate_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-collection-timeoutMS.json/timeoutMS_can_be_configured_on_a_MongoCollection_-_bulkWrite_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-collection-timeoutMS.json/timeoutMS_can_be_set_to_0_on_a_MongoCollection_-_bulkWrite_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-collection-timeoutMS.json/timeoutMS_can_be_configured_on_a_MongoCollection_-_createIndex_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-collection-timeoutMS.json/timeoutMS_can_be_set_to_0_on_a_MongoCollection_-_createIndex_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-collection-timeoutMS.json/timeoutMS_can_be_configured_on_a_MongoCollection_-_dropIndex_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-collection-timeoutMS.json/timeoutMS_can_be_set_to_0_on_a_MongoCollection_-_dropIndex_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-collection-timeoutMS.json/timeoutMS_can_be_configured_on_a_MongoCollection_-_dropIndexes_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-collection-timeoutMS.json/timeoutMS_can_be_set_to_0_on_a_MongoCollection_-_dropIndexes_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-database-timeoutMS.json/timeoutMS_can_be_configured_on_a_MongoDatabase_-_aggregate_on_database\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-database-timeoutMS.json/timeoutMS_can_be_set_to_0_on_a_MongoDatabase_-_aggregate_on_database\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-database-timeoutMS.json/timeoutMS_can_be_configured_on_a_MongoDatabase_-_listCollections_on_database\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-database-timeoutMS.json/timeoutMS_can_be_set_to_0_on_a_MongoDatabase_-_listCollections_on_database\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-database-timeoutMS.json/timeoutMS_can_be_configured_on_a_MongoDatabase_-_listCollectionNames_on_database\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-database-timeoutMS.json/timeoutMS_can_be_set_to_0_on_a_MongoDatabase_-_listCollectionNames_on_database\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-database-timeoutMS.json/timeoutMS_can_be_configured_on_a_MongoDatabase_-_runCommand_on_database\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-database-timeoutMS.json/timeoutMS_can_be_set_to_0_on_a_MongoDatabase_-_runCommand_on_database\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-database-timeoutMS.json/timeoutMS_can_be_configured_on_a_MongoDatabase_-_createChangeStream_on_database\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-database-timeoutMS.json/timeoutMS_can_be_set_to_0_on_a_MongoDatabase_-_createChangeStream_on_database\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-database-timeoutMS.json/timeoutMS_can_be_configured_on_a_MongoDatabase_-_aggregate_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-database-timeoutMS.json/timeoutMS_can_be_set_to_0_on_a_MongoDatabase_-_aggregate_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-database-timeoutMS.json/timeoutMS_can_be_configured_on_a_MongoDatabase_-_count_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-database-timeoutMS.json/timeoutMS_can_be_set_to_0_on_a_MongoDatabase_-_count_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-database-timeoutMS.json/timeoutMS_can_be_configured_on_a_MongoDatabase_-_countDocuments_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-database-timeoutMS.json/timeoutMS_can_be_set_to_0_on_a_MongoDatabase_-_countDocuments_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-database-timeoutMS.json/timeoutMS_can_be_configured_on_a_MongoDatabase_-_estimatedDocumentCount_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-database-timeoutMS.json/timeoutMS_can_be_set_to_0_on_a_MongoDatabase_-_estimatedDocumentCount_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-database-timeoutMS.json/timeoutMS_can_be_configured_on_a_MongoDatabase_-_distinct_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-database-timeoutMS.json/timeoutMS_can_be_set_to_0_on_a_MongoDatabase_-_distinct_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-database-timeoutMS.json/timeoutMS_can_be_configured_on_a_MongoDatabase_-_find_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-database-timeoutMS.json/timeoutMS_can_be_set_to_0_on_a_MongoDatabase_-_find_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-database-timeoutMS.json/timeoutMS_can_be_configured_on_a_MongoDatabase_-_findOne_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-database-timeoutMS.json/timeoutMS_can_be_set_to_0_on_a_MongoDatabase_-_findOne_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-database-timeoutMS.json/timeoutMS_can_be_configured_on_a_MongoDatabase_-_listIndexes_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-database-timeoutMS.json/timeoutMS_can_be_set_to_0_on_a_MongoDatabase_-_listIndexes_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-database-timeoutMS.json/timeoutMS_can_be_configured_on_a_MongoDatabase_-_listIndexNames_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-database-timeoutMS.json/timeoutMS_can_be_set_to_0_on_a_MongoDatabase_-_listIndexNames_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-database-timeoutMS.json/timeoutMS_can_be_configured_on_a_MongoDatabase_-_createChangeStream_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-database-timeoutMS.json/timeoutMS_can_be_set_to_0_on_a_MongoDatabase_-_createChangeStream_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-database-timeoutMS.json/timeoutMS_can_be_configured_on_a_MongoDatabase_-_insertOne_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-database-timeoutMS.json/timeoutMS_can_be_set_to_0_on_a_MongoDatabase_-_insertOne_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-database-timeoutMS.json/timeoutMS_can_be_configured_on_a_MongoDatabase_-_insertMany_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-database-timeoutMS.json/timeoutMS_can_be_set_to_0_on_a_MongoDatabase_-_insertMany_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-database-timeoutMS.json/timeoutMS_can_be_configured_on_a_MongoDatabase_-_deleteOne_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-database-timeoutMS.json/timeoutMS_can_be_set_to_0_on_a_MongoDatabase_-_deleteOne_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-database-timeoutMS.json/timeoutMS_can_be_configured_on_a_MongoDatabase_-_deleteMany_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-database-timeoutMS.json/timeoutMS_can_be_set_to_0_on_a_MongoDatabase_-_deleteMany_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-database-timeoutMS.json/timeoutMS_can_be_configured_on_a_MongoDatabase_-_replaceOne_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-database-timeoutMS.json/timeoutMS_can_be_set_to_0_on_a_MongoDatabase_-_replaceOne_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-database-timeoutMS.json/timeoutMS_can_be_configured_on_a_MongoDatabase_-_updateOne_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-database-timeoutMS.json/timeoutMS_can_be_set_to_0_on_a_MongoDatabase_-_updateOne_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-database-timeoutMS.json/timeoutMS_can_be_configured_on_a_MongoDatabase_-_updateMany_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-database-timeoutMS.json/timeoutMS_can_be_set_to_0_on_a_MongoDatabase_-_updateMany_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-database-timeoutMS.json/timeoutMS_can_be_configured_on_a_MongoDatabase_-_findOneAndDelete_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-database-timeoutMS.json/timeoutMS_can_be_set_to_0_on_a_MongoDatabase_-_findOneAndDelete_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-database-timeoutMS.json/timeoutMS_can_be_configured_on_a_MongoDatabase_-_findOneAndReplace_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-database-timeoutMS.json/timeoutMS_can_be_set_to_0_on_a_MongoDatabase_-_findOneAndReplace_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-database-timeoutMS.json/timeoutMS_can_be_configured_on_a_MongoDatabase_-_findOneAndUpdate_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-database-timeoutMS.json/timeoutMS_can_be_set_to_0_on_a_MongoDatabase_-_findOneAndUpdate_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-database-timeoutMS.json/timeoutMS_can_be_configured_on_a_MongoDatabase_-_bulkWrite_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-database-timeoutMS.json/timeoutMS_can_be_set_to_0_on_a_MongoDatabase_-_bulkWrite_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-database-timeoutMS.json/timeoutMS_can_be_configured_on_a_MongoDatabase_-_createIndex_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-database-timeoutMS.json/timeoutMS_can_be_set_to_0_on_a_MongoDatabase_-_createIndex_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-database-timeoutMS.json/timeoutMS_can_be_configured_on_a_MongoDatabase_-_dropIndex_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-database-timeoutMS.json/timeoutMS_can_be_set_to_0_on_a_MongoDatabase_-_dropIndex_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-database-timeoutMS.json/timeoutMS_can_be_configured_on_a_MongoDatabase_-_dropIndexes_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/override-database-timeoutMS.json/timeoutMS_can_be_set_to_0_on_a_MongoDatabase_-_dropIndexes_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/sessions-inherit-timeoutMS.json/timeoutMS_applied_to_commitTransaction\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/sessions-inherit-timeoutMS.json/timeoutMS_applied_to_abortTransaction\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/sessions-inherit-timeoutMS.json/timeoutMS_applied_to_withTransaction\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/sessions-override-operation-timeoutMS.json/timeoutMS_applied_to_withTransaction\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/sessions-override-timeoutMS.json\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/tailable-awaitData.json/error_if_timeoutMode_is_cursor_lifetime\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/tailable-awaitData.json/timeoutMS_applied_to_find\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/tailable-awaitData.json/timeoutMS_is_refreshed_for_getMore_if_maxAwaitTimeMS_is_not_set\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/tailable-awaitData.json/timeoutMS_is_refreshed_for_getMore_if_maxAwaitTimeMS_is_set\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/tailable-awaitData.json/timeoutMS_is_refreshed_for_getMore_-_failure\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/tailable-awaitData.json/apply_maxAwaitTimeMS_if_less_than_remaining_timeout\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/tailable-non-awaitData.json/error_if_timeoutMode_is_cursor_lifetime\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/tailable-non-awaitData.json/timeoutMS_applied_to_find\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/tailable-non-awaitData.json/timeoutMS_is_refreshed_for_getMore_-_success\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/tailable-non-awaitData.json/timeoutMS_is_refreshed_for_getMore_-_failure\",\n\t},\n\n\t\"CSOT deprecated options test skipped\": {\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/deprecated-options.json/commitTransaction_ignores_socketTimeoutMS_if_timeoutMS_is_set\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/deprecated-options.json/commitTransaction_ignores_wTimeoutMS_if_timeoutMS_is_set\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/deprecated-options.json/commitTransaction_ignores_maxCommitTimeMS_if_timeoutMS_is_set\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/deprecated-options.json/abortTransaction_ignores_socketTimeoutMS_if_timeoutMS_is_set\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/deprecated-options.json/abortTransaction_ignores_wTimeoutMS_if_timeoutMS_is_set\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/deprecated-options.json/withTransaction_ignores_socketTimeoutMS_if_timeoutMS_is_set\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/deprecated-options.json/withTransaction_ignores_wTimeoutMS_if_timeoutMS_is_set\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/deprecated-options.json/withTransaction_ignores_maxCommitTimeMS_if_timeoutMS_is_set\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/deprecated-options.json/socketTimeoutMS_is_ignored_if_timeoutMS_is_set_-_listDatabases_on_client\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/deprecated-options.json/wTimeoutMS_is_ignored_if_timeoutMS_is_set_-_listDatabases_on_client\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/deprecated-options.json/socketTimeoutMS_is_ignored_if_timeoutMS_is_set_-_listDatabaseNames_on_client\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/deprecated-options.json/wTimeoutMS_is_ignored_if_timeoutMS_is_set_-_listDatabaseNames_on_client\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/deprecated-options.json/socketTimeoutMS_is_ignored_if_timeoutMS_is_set_-_createChangeStream_on_client\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/deprecated-options.json/wTimeoutMS_is_ignored_if_timeoutMS_is_set_-_createChangeStream_on_client\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/deprecated-options.json/socketTimeoutMS_is_ignored_if_timeoutMS_is_set_-_aggregate_on_database\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/deprecated-options.json/wTimeoutMS_is_ignored_if_timeoutMS_is_set_-_aggregate_on_database\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/deprecated-options.json/maxTimeMS_is_ignored_if_timeoutMS_is_set_-_aggregate_on_database\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/deprecated-options.json/socketTimeoutMS_is_ignored_if_timeoutMS_is_set_-_listCollections_on_database\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/deprecated-options.json/wTimeoutMS_is_ignored_if_timeoutMS_is_set_-_listCollections_on_database\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/deprecated-options.json/socketTimeoutMS_is_ignored_if_timeoutMS_is_set_-_listCollectionNames_on_database\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/deprecated-options.json/wTimeoutMS_is_ignored_if_timeoutMS_is_set_-_listCollectionNames_on_database\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/deprecated-options.json/socketTimeoutMS_is_ignored_if_timeoutMS_is_set_-_runCommand_on_database\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/deprecated-options.json/wTimeoutMS_is_ignored_if_timeoutMS_is_set_-_runCommand_on_database\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/deprecated-options.json/socketTimeoutMS_is_ignored_if_timeoutMS_is_set_-_createChangeStream_on_database\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/deprecated-options.json/wTimeoutMS_is_ignored_if_timeoutMS_is_set_-_createChangeStream_on_database\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/deprecated-options.json/socketTimeoutMS_is_ignored_if_timeoutMS_is_set_-_aggregate_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/deprecated-options.json/wTimeoutMS_is_ignored_if_timeoutMS_is_set_-_aggregate_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/deprecated-options.json/maxTimeMS_is_ignored_if_timeoutMS_is_set_-_aggregate_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/deprecated-options.json/socketTimeoutMS_is_ignored_if_timeoutMS_is_set_-_count_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/deprecated-options.json/wTimeoutMS_is_ignored_if_timeoutMS_is_set_-_count_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/deprecated-options.json/maxTimeMS_is_ignored_if_timeoutMS_is_set_-_count_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/deprecated-options.json/socketTimeoutMS_is_ignored_if_timeoutMS_is_set_-_countDocuments_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/deprecated-options.json/wTimeoutMS_is_ignored_if_timeoutMS_is_set_-_countDocuments_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/deprecated-options.json/socketTimeoutMS_is_ignored_if_timeoutMS_is_set_-_estimatedDocumentCount_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/deprecated-options.json/wTimeoutMS_is_ignored_if_timeoutMS_is_set_-_estimatedDocumentCount_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/deprecated-options.json/maxTimeMS_is_ignored_if_timeoutMS_is_set_-_estimatedDocumentCount_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/deprecated-options.json/socketTimeoutMS_is_ignored_if_timeoutMS_is_set_-_distinct_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/deprecated-options.json/wTimeoutMS_is_ignored_if_timeoutMS_is_set_-_distinct_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/deprecated-options.json/maxTimeMS_is_ignored_if_timeoutMS_is_set_-_distinct_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/deprecated-options.json/socketTimeoutMS_is_ignored_if_timeoutMS_is_set_-_find_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/deprecated-options.json/wTimeoutMS_is_ignored_if_timeoutMS_is_set_-_find_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/deprecated-options.json/maxTimeMS_is_ignored_if_timeoutMS_is_set_-_find_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/deprecated-options.json/socketTimeoutMS_is_ignored_if_timeoutMS_is_set_-_findOne_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/deprecated-options.json/wTimeoutMS_is_ignored_if_timeoutMS_is_set_-_findOne_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/deprecated-options.json/maxTimeMS_is_ignored_if_timeoutMS_is_set_-_findOne_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/deprecated-options.json/socketTimeoutMS_is_ignored_if_timeoutMS_is_set_-_listIndexes_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/deprecated-options.json/wTimeoutMS_is_ignored_if_timeoutMS_is_set_-_listIndexes_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/deprecated-options.json/socketTimeoutMS_is_ignored_if_timeoutMS_is_set_-_listIndexNames_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/deprecated-options.json/wTimeoutMS_is_ignored_if_timeoutMS_is_set_-_listIndexNames_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/deprecated-options.json/socketTimeoutMS_is_ignored_if_timeoutMS_is_set_-_createChangeStream_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/deprecated-options.json/wTimeoutMS_is_ignored_if_timeoutMS_is_set_-_createChangeStream_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/deprecated-options.json/socketTimeoutMS_is_ignored_if_timeoutMS_is_set_-_insertOne_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/deprecated-options.json/wTimeoutMS_is_ignored_if_timeoutMS_is_set_-_insertOne_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/deprecated-options.json/socketTimeoutMS_is_ignored_if_timeoutMS_is_set_-_insertMany_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/deprecated-options.json/wTimeoutMS_is_ignored_if_timeoutMS_is_set_-_insertMany_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/deprecated-options.json/socketTimeoutMS_is_ignored_if_timeoutMS_is_set_-_deleteOne_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/deprecated-options.json/wTimeoutMS_is_ignored_if_timeoutMS_is_set_-_deleteOne_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/deprecated-options.json/socketTimeoutMS_is_ignored_if_timeoutMS_is_set_-_deleteMany_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/deprecated-options.json/wTimeoutMS_is_ignored_if_timeoutMS_is_set_-_deleteMany_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/deprecated-options.json/socketTimeoutMS_is_ignored_if_timeoutMS_is_set_-_replaceOne_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/deprecated-options.json/wTimeoutMS_is_ignored_if_timeoutMS_is_set_-_replaceOne_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/deprecated-options.json/socketTimeoutMS_is_ignored_if_timeoutMS_is_set_-_updateOne_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/deprecated-options.json/wTimeoutMS_is_ignored_if_timeoutMS_is_set_-_updateOne_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/deprecated-options.json/socketTimeoutMS_is_ignored_if_timeoutMS_is_set_-_updateMany_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/deprecated-options.json/wTimeoutMS_is_ignored_if_timeoutMS_is_set_-_updateMany_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/deprecated-options.json/socketTimeoutMS_is_ignored_if_timeoutMS_is_set_-_findOneAndDelete_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/deprecated-options.json/wTimeoutMS_is_ignored_if_timeoutMS_is_set_-_findOneAndDelete_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/deprecated-options.json/maxTimeMS_is_ignored_if_timeoutMS_is_set_-_findOneAndDelete_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/deprecated-options.json/socketTimeoutMS_is_ignored_if_timeoutMS_is_set_-_findOneAndReplace_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/deprecated-options.json/wTimeoutMS_is_ignored_if_timeoutMS_is_set_-_findOneAndReplace_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/deprecated-options.json/maxTimeMS_is_ignored_if_timeoutMS_is_set_-_findOneAndReplace_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/deprecated-options.json/socketTimeoutMS_is_ignored_if_timeoutMS_is_set_-_findOneAndUpdate_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/deprecated-options.json/wTimeoutMS_is_ignored_if_timeoutMS_is_set_-_findOneAndUpdate_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/deprecated-options.json/maxTimeMS_is_ignored_if_timeoutMS_is_set_-_findOneAndUpdate_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/deprecated-options.json/socketTimeoutMS_is_ignored_if_timeoutMS_is_set_-_bulkWrite_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/deprecated-options.json/wTimeoutMS_is_ignored_if_timeoutMS_is_set_-_bulkWrite_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/deprecated-options.json/socketTimeoutMS_is_ignored_if_timeoutMS_is_set_-_createIndex_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/deprecated-options.json/wTimeoutMS_is_ignored_if_timeoutMS_is_set_-_createIndex_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/deprecated-options.json/maxTimeMS_is_ignored_if_timeoutMS_is_set_-_createIndex_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/deprecated-options.json/socketTimeoutMS_is_ignored_if_timeoutMS_is_set_-_dropIndex_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/deprecated-options.json/wTimeoutMS_is_ignored_if_timeoutMS_is_set_-_dropIndex_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/deprecated-options.json/maxTimeMS_is_ignored_if_timeoutMS_is_set_-_dropIndex_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/deprecated-options.json/socketTimeoutMS_is_ignored_if_timeoutMS_is_set_-_dropIndexes_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/deprecated-options.json/wTimeoutMS_is_ignored_if_timeoutMS_is_set_-_dropIndexes_on_collection\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/deprecated-options.json/maxTimeMS_is_ignored_if_timeoutMS_is_set_-_dropIndexes_on_collection\",\n\t},\n\n\t\"Go Driver does not support legacy CSOT\": {\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/legacy-timeouts.json/wTimeoutMS_is_not_used_to_derive_a_maxTimeMS_command_field\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/legacy-timeouts.json/maxTimeMS_option_is_used_directly_as_the_maxTimeMS_field_on_a_command\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/legacy-timeouts.json/maxCommitTimeMS_option_is_used_directly_as_the_maxTimeMS_field_on_a_commitTransaction_command\",\n\t},\n\n\t// TODO(GODRIVER-3106): \"hello\" failpoint in CSOT command-execution UST is premature.\n\t\"Address premature 'hello' failpoint in CSOT command-execution (GODRIVER-3106)\": {\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/command-execution.json/maxTimeMS_value_in_the_command_is_less_than_timeoutMS\",\n\t},\n\n\t// TODO(GODRIVER-3415): Add performant \"rename all revisions by filename\"\n\t// feature - rename_by_name.\n\t\"Add performant 'rename all revisions by filename' feature (GODRIVER-3415)\": {\n\t\t\"TestUnifiedSpec/gridfs/tests/deleteByName.json/delete_when_multiple_revisions_of_the_file_exist\",\n\t\t\"TestUnifiedSpec/gridfs/tests/deleteByName.json/delete_when_file_name_does_not_exist\",\n\t\t\"TestUnifiedSpec/gridfs/tests/renameByName.json/rename_when_multiple_revisions_of_the_file_exist\",\n\t\t\"TestUnifiedSpec/gridfs/tests/renameByName.json/rename_when_file_name_does_not_exist\",\n\t},\n\n\t// TODO(GODRIVER-3393): Add test that PoolClearedEvent is emitted before\n\t// ConnectionCheckedInEvent/ConnectionCheckOutFailedEvent.\n\t\"Ensure PoolClearedEvent is emitted before ConnectionCheckedIn/ConnectionCheckOutFailed (GODRIVER-3393)\": {\n\t\t\"TestUnifiedSpec/server-discovery-and-monitoring/tests/unified/pool-clear-checkout-error.json/Pool_is_cleared_before_connection_is_closed_(authentication_error)\",\n\t\t\"TestUnifiedSpec/server-discovery-and-monitoring/tests/unified/pool-clear-checkout-error.json/Pool_is_cleared_before_connection_is_closed_(handshake_error)\",\n\t\t\"TestUnifiedSpec/server-discovery-and-monitoring/tests/unified/pool-clear-min-pool-size-error.json/Pool_is_cleared_on_authentication_error_during_minPoolSize_population\",\n\t\t\"TestUnifiedSpec/server-discovery-and-monitoring/tests/unified/pool-clear-min-pool-size-error.json/Pool_is_cleared_on_handshake_error_during_minPoolSize_population\",\n\t},\n\n\t// TODO(GODRIVER-3155): Convert read/write concern spec tests to unified test\n\t// format.\n\t\"Convert read/write concern spec tests to unified format (GODRIVER-3155)\": {\n\t\t\"TestUnifiedSpecs/read-write-concern/tests/operation/default-write-concern-2.6.json\",\n\t\t\"TestUnifiedSpecs/read-write-concern/tests/operation/default-write-concern-3.2.json\",\n\t\t\"TestUnifiedSpecs/read-write-concern/tests/operation/default-write-concern-3.4.json\",\n\t\t\"TestUnifiedSpecs/read-write-concern/tests/operation/default-write-concern-4.2.json\",\n\t},\n\n\t// TODO(GODRIVER-2191): Drivers should retry operations if connection\n\t// handshake fails.\n\t\"Retry operations on handshake failure (GODRIVER-2191)\": {\n\t\t\"TestUnifiedSpec/retryable-reads/tests/unified/handshakeError.json/client.listDatabases_succeeds_after_retryable_handshake_network_error\",\n\t\t\"TestUnifiedSpec/retryable-reads/tests/unified/handshakeError.json/client.listDatabases_succeeds_after_retryable_handshake_server_error_(ShutdownInProgress)\",\n\t\t\"TestUnifiedSpec/retryable-reads/tests/unified/handshakeError.json/client.listDatabaseNames_succeeds_after_retryable_handshake_network_error\",\n\t\t\"TestUnifiedSpec/retryable-reads/tests/unified/handshakeError.json/client.listDatabaseNames_succeeds_after_retryable_handshake_server_error_(ShutdownInProgress)\",\n\t\t\"TestUnifiedSpec/retryable-reads/tests/unified/handshakeError.json/client.createChangeStream_succeeds_after_retryable_handshake_network_error\",\n\t\t\"TestUnifiedSpec/retryable-reads/tests/unified/handshakeError.json/client.createChangeStream_succeeds_after_retryable_handshake_server_error_(ShutdownInProgress)\",\n\t\t\"TestUnifiedSpec/retryable-reads/tests/unified/handshakeError.json/database.aggregate_succeeds_after_retryable_handshake_network_error\",\n\t\t\"TestUnifiedSpec/retryable-reads/tests/unified/handshakeError.json/database.aggregate_succeeds_after_retryable_handshake_server_error_(ShutdownInProgress)\",\n\t\t\"TestUnifiedSpec/retryable-reads/tests/unified/handshakeError.json/database.listCollections_succeeds_after_retryable_handshake_network_error\",\n\t\t\"TestUnifiedSpec/retryable-reads/tests/unified/handshakeError.json/database.listCollections_succeeds_after_retryable_handshake_server_error_(ShutdownInProgress)\",\n\t\t\"TestUnifiedSpec/retryable-reads/tests/unified/handshakeError.json/database.listCollectionNames_succeeds_after_retryable_handshake_network_error\",\n\t\t\"TestUnifiedSpec/retryable-reads/tests/unified/handshakeError.json/database.listCollectionNames_succeeds_after_retryable_handshake_server_error_(ShutdownInProgress)\",\n\t\t\"TestUnifiedSpec/retryable-reads/tests/unified/handshakeError.json/database.createChangeStream_succeeds_after_retryable_handshake_network_error\",\n\t\t\"TestUnifiedSpec/retryable-reads/tests/unified/handshakeError.json/database.createChangeStream_succeeds_after_retryable_handshake_server_error_(ShutdownInProgress)\",\n\t\t\"TestUnifiedSpec/retryable-reads/tests/unified/handshakeError.json/collection.aggregate_succeeds_after_retryable_handshake_network_error\",\n\t\t\"TestUnifiedSpec/retryable-reads/tests/unified/handshakeError.json/collection.aggregate_succeeds_after_retryable_handshake_server_error_(ShutdownInProgress)\",\n\t\t\"TestUnifiedSpec/retryable-reads/tests/unified/handshakeError.json/collection.countDocuments_succeeds_after_retryable_handshake_network_error\",\n\t\t\"TestUnifiedSpec/retryable-reads/tests/unified/handshakeError.json/collection.countDocuments_succeeds_after_retryable_handshake_server_error_(ShutdownInProgress)\",\n\t\t\"TestUnifiedSpec/retryable-reads/tests/unified/handshakeError.json/collection.estimatedDocumentCount_succeeds_after_retryable_handshake_network_error\",\n\t\t\"TestUnifiedSpec/retryable-reads/tests/unified/handshakeError.json/collection.estimatedDocumentCount_succeeds_after_retryable_handshake_server_error_(ShutdownInProgress)\",\n\t\t\"TestUnifiedSpec/retryable-reads/tests/unified/handshakeError.json/collection.distinct_succeeds_after_retryable_handshake_network_error\",\n\t\t\"TestUnifiedSpec/retryable-reads/tests/unified/handshakeError.json/collection.distinct_succeeds_after_retryable_handshake_server_error_(ShutdownInProgress)\",\n\t\t\"TestUnifiedSpec/retryable-reads/tests/unified/handshakeError.json/collection.find_succeeds_after_retryable_handshake_network_error\",\n\t\t\"TestUnifiedSpec/retryable-reads/tests/unified/handshakeError.json/collection.find_succeeds_after_retryable_handshake_server_error_(ShutdownInProgress)\",\n\t\t\"TestUnifiedSpec/retryable-reads/tests/unified/handshakeError.json/collection.findOne_succeeds_after_retryable_handshake_network_error\",\n\t\t\"TestUnifiedSpec/retryable-reads/tests/unified/handshakeError.json/collection.findOne_succeeds_after_retryable_handshake_server_error_(ShutdownInProgress)\",\n\t\t\"TestUnifiedSpec/retryable-reads/tests/unified/handshakeError.json/collection.listIndexes_succeeds_after_retryable_handshake_network_error\",\n\t\t\"TestUnifiedSpec/retryable-reads/tests/unified/handshakeError.json/collection.listIndexes_succeeds_after_retryable_handshake_server_error_(ShutdownInProgress)\",\n\t\t\"TestUnifiedSpec/retryable-reads/tests/unified/handshakeError.json/collection.createChangeStream_succeeds_after_retryable_handshake_network_error\",\n\t\t\"TestUnifiedSpec/retryable-reads/tests/unified/handshakeError.json/collection.createChangeStream_succeeds_after_retryable_handshake_server_error_(ShutdownInProgress)\",\n\t\t\"TestUnifiedSpec/retryable-writes/tests/unified/handshakeError.json\",\n\t\t\"TestUnifiedSpec/retryable-writes/tests/unified/handshakeError.json/client.clientBulkWrite_succeeds_after_retryable_handshake_network_error\",\n\t\t\"TestUnifiedSpec/retryable-writes/tests/unified/handshakeError.json/client.clientBulkWrite_succeeds_after_retryable_handshake_server_error_(ShutdownInProgress)\",\n\t\t\"TestUnifiedSpec/retryable-writes/tests/unified/handshakeError.json/collection.insertOne_succeeds_after_retryable_handshake_network_error\",\n\t\t\"TestUnifiedSpec/retryable-writes/tests/unified/handshakeError.json/collection.insertOne_succeeds_after_retryable_handshake_server_error_(ShutdownInProgress)\",\n\t\t\"TestUnifiedSpec/retryable-writes/tests/unified/handshakeError.json/collection.insertMany_succeeds_after_retryable_handshake_network_error\",\n\t\t\"TestUnifiedSpec/retryable-writes/tests/unified/handshakeError.json/collection.insertMany_succeeds_after_retryable_handshake_server_error_(ShutdownInProgress)\",\n\t\t\"TestUnifiedSpec/retryable-writes/tests/unified/handshakeError.json/collection.deleteOne_succeeds_after_retryable_handshake_network_error\",\n\t\t\"TestUnifiedSpec/retryable-writes/tests/unified/handshakeError.json/collection.deleteOne_succeeds_after_retryable_handshake_server_error_(ShutdownInProgress)\",\n\t\t\"TestUnifiedSpec/retryable-writes/tests/unified/handshakeError.json/collection.replaceOne_succeeds_after_retryable_handshake_network_error\",\n\t\t\"TestUnifiedSpec/retryable-writes/tests/unified/handshakeError.json/collection.replaceOne_succeeds_after_retryable_handshake_server_error_(ShutdownInProgress)\",\n\t\t\"TestUnifiedSpec/retryable-writes/tests/unified/handshakeError.json/collection.updateOne_succeeds_after_retryable_handshake_network_error\",\n\t\t\"TestUnifiedSpec/retryable-writes/tests/unified/handshakeError.json/collection.updateOne_succeeds_after_retryable_handshake_server_error_(ShutdownInProgress)\",\n\t\t\"TestUnifiedSpec/retryable-writes/tests/unified/handshakeError.json/collection.findOneAndDelete_succeeds_after_retryable_handshake_network_error\",\n\t\t\"TestUnifiedSpec/retryable-writes/tests/unified/handshakeError.json/collection.findOneAndDelete_succeeds_after_retryable_handshake_server_error_(ShutdownInProgress)\",\n\t\t\"TestUnifiedSpec/retryable-writes/tests/unified/handshakeError.json/collection.findOneAndReplace_succeeds_after_retryable_handshake_network_error\",\n\t\t\"TestUnifiedSpec/retryable-writes/tests/unified/handshakeError.json/collection.findOneAndReplace_succeeds_after_retryable_handshake_server_error_(ShutdownInProgress)\",\n\t\t\"TestUnifiedSpec/retryable-writes/tests/unified/handshakeError.json/collection.findOneAndUpdate_succeeds_after_retryable_handshake_network_error\",\n\t\t\"TestUnifiedSpec/retryable-writes/tests/unified/handshakeError.json/collection.findOneAndUpdate_succeeds_after_retryable_handshake_server_error_(ShutdownInProgress)\",\n\t\t\"TestUnifiedSpec/retryable-writes/tests/unified/handshakeError.json/collection.bulkWrite_succeeds_after_retryable_handshake_network_error\",\n\t\t\"TestUnifiedSpec/retryable-writes/tests/unified/handshakeError.json/collection.bulkWrite_succeeds_after_retryable_handshake_server_error_(ShutdownInProgress)\",\n\t},\n\n\t// TODO(GODRIVER-3524): Change streams expanded events present by default in\n\t// 8.2+.\n\t\"Change streams expanded events for MongoDB 8.2+ (GODRIVER-3524)\": {\n\t\t\"TestUnifiedSpec/change-streams/tests/unified/change-streams-disambiguatedPaths.json/disambiguatedPaths_is_not_present_when_showExpandedEvents_is_false/unset\",\n\t\t\"TestUnifiedSpec/change-streams/tests/unified/change-streams.json/Test_insert,_update,_replace,_and_delete_event_types\",\n\t\t\"TestUnifiedSpec/change-streams/tests/unified/change-streams.json/Test_array_truncation\",\n\t},\n\n\t// TODO(GODRIVER-3137): Gossip cluster time from internal MongoClient to\n\t// session entities.\n\t\"Must advance cluster times in unified spec runner (GODRIVER-3137)\": {\n\t\t\"TestUnifiedSpec/transactions/unified/mongos-unpin.json/unpin_after_TransientTransactionError_error_on_commit\",\n\t\t// This test fails with the same error as GODRIVER-3137, but is not\n\t\t// directly referenced as an impacted test case by DRIVERS-2816. It\n\t\t// seems likely that the same change will resolve the failure, so I'm\n\t\t// including it here.\n\t\t\"TestUnifiedSpec/unified-test-format/tests/valid-pass/poc-transactions-convenient-api.json/withTransaction_and_no_transaction_options_set\",\n\t\t\"TestUnifiedSpec/unified-test-format/tests/valid-pass/poc-transactions-convenient-api.json/withTransaction_inherits_transaction_options_from_client\",\n\t\t\"TestUnifiedSpec/unified-test-format/tests/valid-pass/poc-transactions-convenient-api.json/withTransaction_inherits_transaction_options_from_defaultTransactionOptions\",\n\t\t\"TestUnifiedSpec/unified-test-format/tests/valid-pass/poc-transactions-convenient-api.json/withTransaction_explicit_transaction_options\",\n\t\t\"TestUnifiedSpec/unified-test-format/tests/valid-pass/poc-transactions-mongos-pin-auto.json/remain_pinned_after_non-transient_Interrupted_error_on_insertOne\",\n\t\t\"TestUnifiedSpec/unified-test-format/tests/valid-pass/poc-transactions-mongos-pin-auto.json/unpin_after_transient_error_within_a_transaction\",\n\t\t\"TestUnifiedSpec/transactions-convenient-api/tests/unified/callback-aborts.json/withTransaction_succeeds_if_callback_aborts\",\n\t\t\"TestUnifiedSpec/transactions-convenient-api/tests/unified/callback-commits.json/withTransaction_succeeds_if_callback_commits\",\n\t\t\"TestUnifiedSpec/transactions-convenient-api/tests/unified/callback-commits.json/withTransaction_still_succeeds_if_callback_commits_and_runs_extra_op\",\n\t\t\"TestUnifiedSpec/transactions-convenient-api/tests/unified/callback-aborts.json/withTransaction_still_succeeds_if_callback_aborts_and_runs_extra_op\",\n\t\t\"TestUnifiedSpec/transactions-convenient-api/tests/unified/commit.json/withTransaction_commits_after_callback_returns_(second_transaction)\",\n\t\t\"TestUnifiedSpec/transactions-convenient-api/tests/unified/transaction-options.json/withTransaction_and_no_transaction_options_set\",\n\t\t\"TestUnifiedSpec/transactions-convenient-api/tests/unified/transaction-options.json/withTransaction_inherits_transaction_options_from_client\",\n\t\t\"TestUnifiedSpec/transactions-convenient-api/tests/unified/transaction-options.json/withTransaction_inherits_transaction_options_from_defaultTransactionOptions\",\n\t\t\"TestUnifiedSpec/transactions-convenient-api/tests/unified/transaction-options.json/withTransaction_explicit_transaction_options\",\n\t\t\"TestUnifiedSpec/transactions-convenient-api/tests/unified/transaction-options.json/withTransaction_explicit_transaction_options_override_defaultTransactionOptions\",\n\t\t\"TestUnifiedSpec/transactions-convenient-api/tests/unified/transaction-options.json/withTransaction_explicit_transaction_options_override_client_options\",\n\t\t\"TestUnifiedSpec/transactions-convenient-api/tests/unified/commit.json/withTransaction_commits_after_callback_returns\",\n\t},\n\n\t\"Address CSOT Compliance Issue in Timeout Handling for Cursor Constructors (GODRIVER-3480)\": {\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/tailable-awaitData.json/apply_remaining_timeoutMS_if_less_than_maxAwaitTimeMS\",\n\t},\n\n\t// The Go Driver does not support \"iteration\" mode for cursors. That is,\n\t// we do not apply the timeout used to construct the cursor when using the\n\t// cursor, rather we apply the context-level timeout if one is provided. It's\n\t// doubtful that we will ever support this mode, so we skip these tests.\n\t//\n\t// If we do ever support this mode, it will be done as part of DRIVERS-2722\n\t// which does not currently have driver-specific tickets.\n\t//\n\t// Note that we have integration tests that cover the cases described in these\n\t// tests upto what is supported in the Go Driver. See GODRIVER-3473\n\t\"Change CSOT default cursor timeout mode to ITERATION (DRIVERS-2772)\": {\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/tailable-awaitData.json/apply_remaining_timeoutMS_if_less_than_maxAwaitTimeMS\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/tailable-awaitData.json/error_if_maxAwaitTimeMS_is_equal_to_timeoutMS\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/change-streams.json/error_if_maxAwaitTimeMS_is_equal_to_timeoutMS\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/tailable-awaitData.json/error_if_maxAwaitTimeMS_is_greater_than_timeoutMS\",\n\t},\n\n\t// TODO(GODRIVER-3641): Ensure Driver Errors for Tailable AwaitData Cursors on\n\t// Invalid maxAwaitTimeMS.\n\t\"Ensure Driver Errors for Tailable AwaitData Cursors on Invalid maxAwaitTimeMS (GODRIVER-3641)\": {\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/tailable-awaitData.json/error_on_find_if_maxAwaitTimeMS_is_greater_than_timeoutMS\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/tailable-awaitData.json/error_on_aggregate_if_maxAwaitTimeMS_is_greater_than_timeoutMS\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/tailable-awaitData.json/error_on_watch_if_maxAwaitTimeMS_is_greater_than_timeoutMS\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/tailable-awaitData.json/error_on_find_if_maxAwaitTimeMS_is_equal_to_timeoutMS\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/tailable-awaitData.json/error_on_aggregate_if_maxAwaitTimeMS_is_equal_to_timeoutMS\",\n\t\t\"TestUnifiedSpec/client-side-operations-timeout/tests/tailable-awaitData.json/error_on_watch_if_maxAwaitTimeMS_is_equal_to_timeoutMS\",\n\t},\n\n\t// TODO(GODRIVER-3403): Support queryable encryption in Client.BulkWrite.\n\t\"Support queryable encryption in Client.BulkWrite (GODRIVER-3403)\": {\n\t\t\"TestUnifiedSpec/crud/tests/unified/client-bulkWrite-qe.json\",\n\t\t\"TestUnifiedSpec/client-side-encryption/tests/unified/client-bulkWrite-qe.json\",\n\t},\n\n\t// Pre-4.2 SDAM tests\n\t\"Pre-4.2 SDAM tests\": {\n\t\t\"TestSDAMSpec/errors/pre-42-InterruptedAtShutdown.json\",\n\t\t\"TestSDAMSpec/errors/pre-42-InterruptedDueToReplStateChange.json\",\n\t\t\"TestSDAMSpec/errors/pre-42-LegacyNotPrimary.json\",\n\t\t\"TestSDAMSpec/errors/pre-42-NotPrimaryNoSecondaryOk.json\",\n\t\t\"TestSDAMSpec/errors/pre-42-NotPrimaryOrSecondary.json\",\n\t\t\"TestSDAMSpec/errors/pre-42-NotWritablePrimary.json\",\n\t\t\"TestSDAMSpec/errors/pre-42-PrimarySteppedDown.json\",\n\t\t\"TestSDAMSpec/errors/pre-42-ShutdownInProgress.json\",\n\t},\n\n\t// TODO(DRIVERS-3356): Unskip this test when the spec test bug is fixed.\n\t\"Handshake spec test 'metadata-not-propagated.yml' fails on sharded clusters (DRIVERS-3356)\": {\n\t\t\"TestUnifiedSpec/mongodb-handshake/tests/unified/metadata-not-propagated.json/metadata_append_does_not_create_new_connections_or_close_existing_ones_and_no_hello_command_is_sent\",\n\t},\n\n\t// TODO(GODRIVER-1826): Race condition between monitor and pool causes pool to be cleared.\n\t\"Backpressure SDAM test 'pool-clear-min-pool-size-error.yml' fails on standalone deployments\": {\n\t\t\"TestUnifiedSpec/server-discovery-and-monitoring/tests/unified/pool-clear-min-pool-size-error.json/Pool_is_not_cleared_on_handshake_error_during_minPoolSize_population\",\n\t},\n}\n\n// CheckSkip checks if the fully-qualified test name matches a list of skipped test names for a given reason.\n// If the test name matches any from a list, the reason is logged and the test is skipped.\nfunc CheckSkip(t *testing.T) {\n\tfor reason, tests := range skipTests {\n\t\tfor _, testName := range tests {\n\t\t\tif t.Name() == testName {\n\t\t\t\tt.Skipf(\"Skipping due to known failure: %q\", reason)\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "internal/spectest/spectest.go",
    "content": "// Copyright (C) MongoDB, Inc. 2023-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage spectest\n\nimport (\n\t\"os\"\n\t\"path\"\n\t\"path/filepath\"\n\t\"runtime\"\n\t\"testing\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/require\"\n)\n\n// FindJSONFilesInDir finds the JSON files in a directory.\nfunc FindJSONFilesInDir(t *testing.T, dir string) []string {\n\tt.Helper()\n\n\tfiles := make([]string, 0)\n\n\tentries, err := os.ReadDir(dir)\n\trequire.NoError(t, err)\n\n\tfor _, entry := range entries {\n\t\tif entry.IsDir() || path.Ext(entry.Name()) != \".json\" {\n\t\t\tcontinue\n\t\t}\n\n\t\tfiles = append(files, entry.Name())\n\t}\n\n\tif len(files) == 0 {\n\t\tt.Fatalf(\"no JSON files found in %q\", dir)\n\t}\n\n\treturn files\n}\n\n// Path returns the absolute path to the given specifications repo file or\n// subdirectory.\n//\n// If the PROJECT_DIRECTORY environment variable is set, Path uses that to find\n// the repo root path. Otherwise, it falls back to using the call stack to find\n// the repo root path. Path panics if it can't find the repo root path.\nfunc Path(subdir string) string {\n\troot := os.Getenv(\"PROJECT_DIRECTORY\")\n\tif root == \"\" {\n\t\t_, file, _, ok := runtime.Caller(0)\n\t\tif !ok {\n\t\t\tpanic(\"unable to get current file path from call stack\")\n\t\t}\n\n\t\t// Get the repository root path from the current Go file path.\n\t\troot = filepath.Dir(filepath.Dir(filepath.Dir(file)))\n\t}\n\n\treturn filepath.Join(root, \"testdata\", \"specifications\", \"source\", subdir)\n}\n"
  },
  {
    "path": "internal/test/aws/aws_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2025-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage main\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"os\"\n\t\"testing\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/require\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n)\n\nfunc TestAWS(t *testing.T) {\n\turi := os.Getenv(\"MONGODB_URI\")\n\tif uri == \"\" {\n\t\tt.Skip(\"Skipping test: MONGODB_URI environment variable is not set\")\n\t}\n\n\tclient, err := mongo.Connect(options.Client().ApplyURI(uri))\n\n\tdefer func() {\n\t\terr = client.Disconnect(context.Background())\n\t\trequire.NoError(t, err)\n\t}()\n\n\tcoll := client.Database(\"aws\").Collection(\"test\")\n\n\terr = coll.FindOne(context.Background(), bson.D{{Key: \"x\", Value: 1}}).Err()\n\tif err != nil && !errors.Is(err, mongo.ErrNoDocuments) {\n\t\tt.Logf(\"FindOne error: %v\", err)\n\t}\n}\n"
  },
  {
    "path": "internal/test/compilecheck/compile_check_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2025-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/require\"\n\t\"github.com/testcontainers/testcontainers-go\"\n)\n\nconst mainGo = `package main\n\nimport (\n\t\"fmt\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n)\n\nfunc main() {\n\t_, _ = mongo.Connect(options.Client())\n\tfmt.Println(bson.D{{Key: \"key\", Value: \"value\"}})\n}\n`\n\n// goVersions is the list of Go versions to test compilation against.\n// To run tests for specific version(s), use the -run flag:\n//\n//\tgo test -v -run '^TestCompileCheck/go:1.19$'\n//\tgo test -v -run '^TestCompileCheck/go:1\\.(19|20)$'\nvar goVersions = []string{\n\t\"1.19\", // Minimum supported Go version for mongo-driver v2\n\t\"1.20\",\n\t\"1.21\",\n\t\"1.22\",\n\t\"1.23\",\n\t\"1.24\",\n\t\"1.25\", // Test suite Go Version\n}\n\nvar architectures = []string{\n\t\"386\",\n\t\"amd64\",\n\t\"arm\",\n\t\"arm64\",\n\t\"mips\",\n\t\"mips64\",\n\t\"mips64le\",\n\t\"mipsle\",\n\t\"ppc64\",\n\t\"ppc64le\",\n\t\"riscv64\",\n\t\"s390x\",\n}\n\n// goExecConfig contains optional configuration for execGo.\ntype goExecConfig struct {\n\tversion string            // Optional: Go version to use with GOTOOLCHAIN. If empty, uses default.\n\tenv     map[string]string // Optional: Additional environment variables.\n}\n\n// execContainer executes a shell command in the container and validates its output.\nfunc execContainer(t *testing.T, c testcontainers.Container, cmd string) string {\n\tt.Helper()\n\n\texit, out, err := c.Exec(context.Background(), []string{\"bash\", \"-lc\", cmd})\n\trequire.NoError(t, err)\n\n\tb, err := io.ReadAll(out)\n\trequire.NoError(t, err)\n\trequire.Equal(t, 0, exit, \"command failed: %s\", b)\n\n\ts := string(b)\n\t// Strip leading non-printable bytes (some Docker/TTY combos emit these).\n\tfor len(s) > 0 && s[0] < 0x20 {\n\t\ts = s[1:]\n\t}\n\treturn s\n}\n\n// execGo runs a Go command, trying GOTOOLCHAIN=goX.Y.0 first, then goX.Y.\nfunc execGo(t *testing.T, c testcontainers.Container, cfg *goExecConfig, args ...string) string {\n\tt.Helper()\n\n\tif cfg == nil {\n\t\tcfg = &goExecConfig{}\n\t}\n\n\tenvParts := []string{\"PATH=/usr/local/go/bin:$PATH\"}\n\tfor k, v := range cfg.env {\n\t\tenvParts = append(envParts, fmt.Sprintf(\"%s=%s\", k, v))\n\t}\n\tenvStr := strings.Join(envParts, \" \")\n\tgoArgs := strings.Join(args, \" \")\n\n\tvar cmd string\n\tif cfg.version != \"\" {\n\t\tprimaryCmd := fmt.Sprintf(\"%s GOTOOLCHAIN=go%s.0 go %s 2>&1\", envStr, cfg.version, goArgs)\n\t\tfallbackCmd := fmt.Sprintf(\"%s GOTOOLCHAIN=go%s go %s 2>&1\", envStr, cfg.version, goArgs)\n\t\tcmd = fmt.Sprintf(\"%s || %s\", primaryCmd, fallbackCmd)\n\t} else {\n\t\tcmd = fmt.Sprintf(\"%s go %s 2>&1\", envStr, goArgs)\n\t}\n\n\treturn execContainer(t, c, cmd)\n}\n\nfunc TestCompileCheck(t *testing.T) {\n\tcwd, err := os.Getwd()\n\trequire.NoError(t, err)\n\n\trootDir := filepath.Dir(filepath.Dir(filepath.Dir(cwd)))\n\n\t// Build the image and start one container we can reuse for all subtests.\n\treq := testcontainers.ContainerRequest{\n\t\tFromDockerfile: testcontainers.FromDockerfile{\n\t\t\tContext:       rootDir,\n\t\t\tDockerfile:    \"Dockerfile\",\n\t\t\tPrintBuildLog: true,\n\t\t},\n\t\tFiles: []testcontainers.ContainerFile{\n\t\t\t{\n\t\t\t\tReader:            strings.NewReader(mainGo),\n\t\t\t\tContainerFilePath: \"/workspace/main.go\",\n\t\t\t\tFileMode:          0o644,\n\t\t\t},\n\t\t},\n\t\t// Entrypoint is set to \"tail -f /dev/null\" so the container stays running and available to execute multiple shell commands as needed during tests.\n\t\t// This keeps the container alive and ready for exec calls, rather than immediately exiting.\n\t\tEntrypoint: []string{\"tail\", \"-f\", \"/dev/null\"},\n\t\tWorkingDir: \"/workspace\",\n\t}\n\n\tgenReq := testcontainers.GenericContainerRequest{ContainerRequest: req, Started: true}\n\n\tcontainer, err := testcontainers.GenericContainer(context.Background(), genReq)\n\trequire.NoError(t, err)\n\n\tt.Cleanup(func() {\n\t\trequire.NoError(t, container.Terminate(context.Background()))\n\t})\n\n\ttestSuiteVersion := goVersions[len(goVersions)-1]\n\n\t// Initialize Go module and download dependencies using the test suite Go version.\n\t_ = execGo(t, container, &goExecConfig{version: testSuiteVersion}, \"mod\", \"init\", \"compilecheck\")\n\t_ = execGo(t, container, nil, \"mod\", \"edit\", \"-replace=go.mongodb.org/mongo-driver/v2=/mongo-go-driver\")\n\t_ = execGo(t, container, &goExecConfig{version: testSuiteVersion}, \"mod\", \"tidy\")\n\n\t// Set minimum Go version to what the driver claims (first version in our test list).\n\t_ = execGo(t, container, nil, \"mod\", \"edit\", \"-go=\"+goVersions[0])\n\n\tfor _, ver := range goVersions {\n\t\tver := ver // capture\n\t\tt.Run(\"go:\"+ver, func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tversionCfg := &goExecConfig{version: ver}\n\n\t\t\t// Verify the Go version is available.\n\t\t\tversionOutput := execGo(t, container, versionCfg, \"version\")\n\t\t\trequire.Contains(t, versionOutput, \"go\"+ver, \"unexpected go version: %s\", versionOutput)\n\n\t\t\t_ = execGo(t, container, versionCfg, \"build\", \"-buildvcs=false\", \"-o\", \"/dev/null\", \"main.go\")\n\n\t\t\t// Dynamic linking build.\n\t\t\t_ = execGo(t, container, versionCfg, \"build\", \"-buildvcs=false\", \"-buildmode=plugin\", \"-o\", \"/dev/null\", \"main.go\")\n\n\t\t\t// Build with build tags.\n\t\t\t_ = execGo(t, container, &goExecConfig{\n\t\t\t\tversion: ver,\n\t\t\t\tenv: map[string]string{\n\t\t\t\t\t\"PKG_CONFIG_PATH\": \"/root/install/libmongocrypt/lib/pkgconfig\",\n\t\t\t\t\t\"CGO_CFLAGS\":      \"'-I/root/install/libmongocrypt/include'\",\n\t\t\t\t\t\"CGO_LDFLAGS\":     \"'-L/root/install/libmongocrypt/lib -Wl,-rpath,/root/install/libmongocrypt/lib'\",\n\t\t\t\t},\n\t\t\t}, \"build\", \"-buildvcs=false\", \"-tags=cse,gssapi,mongointernal\", \"-o\", \"/dev/null\", \"main.go\")\n\n\t\t\t// Build for each architecture.\n\t\t\tfor _, architecture := range architectures {\n\t\t\t\tarchitecture := architecture // capture\n\t\t\t\tt.Run(\"arch:\"+architecture, func(t *testing.T) {\n\t\t\t\t\tt.Parallel()\n\n\t\t\t\t\t// Standard build.\n\t\t\t\t\t_ = execGo(t, container, &goExecConfig{\n\t\t\t\t\t\tversion: ver,\n\t\t\t\t\t\tenv: map[string]string{\n\t\t\t\t\t\t\t\"GOOS\":   \"linux\",\n\t\t\t\t\t\t\t\"GOARCH\": architecture,\n\t\t\t\t\t\t},\n\t\t\t\t\t}, \"build\", \"-buildvcs=false\", \"-o\", \"/dev/null\", \"main.go\")\n\n\t\t\t\t\tt.Logf(\"compilation checks passed for go%s on %s\", ver, architecture)\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "internal/test/compilecheck/go.mod",
    "content": "module go.mongodb.go/mongo-driver/v2/internal/test/compilecheck\n\ngo 1.25.0\n\ntoolchain go1.25.3\n\nrequire (\n\tgithub.com/stretchr/testify v1.11.1\n\tgithub.com/testcontainers/testcontainers-go v0.35.0\n)\n\nrequire (\n\tdario.cat/mergo v1.0.0 // indirect\n\tgithub.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 // indirect\n\tgithub.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect\n\tgithub.com/Microsoft/go-winio v0.6.2 // indirect\n\tgithub.com/cenkalti/backoff/v4 v4.2.1 // indirect\n\tgithub.com/cespare/xxhash/v2 v2.3.0 // indirect\n\tgithub.com/containerd/log v0.1.0 // indirect\n\tgithub.com/containerd/platforms v0.2.1 // indirect\n\tgithub.com/cpuguy83/dockercfg v0.3.2 // indirect\n\tgithub.com/davecgh/go-spew v1.1.1 // indirect\n\tgithub.com/distribution/reference v0.6.0 // indirect\n\tgithub.com/docker/docker v28.0.0+incompatible // indirect\n\tgithub.com/docker/go-connections v0.5.0 // indirect\n\tgithub.com/docker/go-units v0.5.0 // indirect\n\tgithub.com/felixge/httpsnoop v1.0.4 // indirect\n\tgithub.com/go-logr/logr v1.4.3 // indirect\n\tgithub.com/go-logr/stdr v1.2.2 // indirect\n\tgithub.com/go-ole/go-ole v1.2.6 // indirect\n\tgithub.com/gogo/protobuf v1.3.2 // indirect\n\tgithub.com/google/uuid v1.6.0 // indirect\n\tgithub.com/klauspost/compress v1.17.6 // indirect\n\tgithub.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect\n\tgithub.com/magiconair/properties v1.8.7 // indirect\n\tgithub.com/moby/docker-image-spec v1.3.1 // indirect\n\tgithub.com/moby/patternmatcher v0.6.0 // indirect\n\tgithub.com/moby/sys/sequential v0.5.0 // indirect\n\tgithub.com/moby/sys/user v0.3.0 // indirect\n\tgithub.com/moby/sys/userns v0.1.0 // indirect\n\tgithub.com/moby/term v0.5.0 // indirect\n\tgithub.com/morikuni/aec v1.0.0 // indirect\n\tgithub.com/opencontainers/go-digest v1.0.0 // indirect\n\tgithub.com/opencontainers/image-spec v1.1.0 // indirect\n\tgithub.com/pkg/errors v0.9.1 // indirect\n\tgithub.com/pmezard/go-difflib v1.0.0 // indirect\n\tgithub.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect\n\tgithub.com/shirou/gopsutil/v3 v3.23.12 // indirect\n\tgithub.com/shoenig/go-m1cpu v0.1.6 // indirect\n\tgithub.com/sirupsen/logrus v1.9.3 // indirect\n\tgithub.com/tklauser/go-sysconf v0.3.12 // indirect\n\tgithub.com/tklauser/numcpus v0.6.1 // indirect\n\tgithub.com/yusufpapurcu/wmi v1.2.3 // indirect\n\tgo.opentelemetry.io/auto/sdk v1.2.1 // indirect\n\tgo.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect\n\tgo.opentelemetry.io/otel v1.40.0 // indirect\n\tgo.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0 // indirect\n\tgo.opentelemetry.io/otel/metric v1.40.0 // indirect\n\tgo.opentelemetry.io/otel/sdk v1.40.0 // indirect\n\tgo.opentelemetry.io/otel/trace v1.40.0 // indirect\n\tgolang.org/x/crypto v0.45.0 // indirect\n\tgolang.org/x/sys v0.40.0 // indirect\n\tgolang.org/x/time v0.0.0-20220210224613-90d013bbcef8 // indirect\n\tgoogle.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237 // indirect\n\tgoogle.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda // indirect\n\tgoogle.golang.org/protobuf v1.35.2 // indirect\n\tgopkg.in/yaml.v3 v3.0.1 // indirect\n)\n"
  },
  {
    "path": "internal/test/compilecheck/go.sum",
    "content": "dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk=\ndario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=\ngithub.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU=\ngithub.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8=\ngithub.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=\ngithub.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=\ngithub.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=\ngithub.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=\ngithub.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM=\ngithub.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=\ngithub.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=\ngithub.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=\ngithub.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=\ngithub.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=\ngithub.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A=\ngithub.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw=\ngithub.com/cpuguy83/dockercfg v0.3.2 h1:DlJTyZGBDlXqUZ2Dk2Q3xHs/FtnooJJVaad2S9GKorA=\ngithub.com/cpuguy83/dockercfg v0.3.2/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc=\ngithub.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=\ngithub.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=\ngithub.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=\ngithub.com/docker/docker v28.0.0+incompatible h1:Olh0KS820sJ7nPsBKChVhk5pzqcwDR15fumfAd/p9hM=\ngithub.com/docker/docker v28.0.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=\ngithub.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=\ngithub.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=\ngithub.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=\ngithub.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=\ngithub.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=\ngithub.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=\ngithub.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=\ngithub.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=\ngithub.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=\ngithub.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=\ngithub.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=\ngithub.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=\ngithub.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=\ngithub.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=\ngithub.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=\ngithub.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=\ngithub.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=\ngithub.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=\ngithub.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=\ngithub.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=\ngithub.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 h1:YBftPWNWd4WwGqtY2yeZL2ef8rHAxPBD8KFhJpmcqms=\ngithub.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0/go.mod h1:YN5jB8ie0yfIUg6VvR9Kz84aCaG7AsGZnLjhHbUqwPg=\ngithub.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=\ngithub.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=\ngithub.com/klauspost/compress v1.17.6 h1:60eq2E/jlfwQXtvZEeBUYADs+BwKBWURIY+Gj2eRGjI=\ngithub.com/klauspost/compress v1.17.6/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM=\ngithub.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=\ngithub.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=\ngithub.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=\ngithub.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=\ngithub.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=\ngithub.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=\ngithub.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=\ngithub.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=\ngithub.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=\ngithub.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=\ngithub.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk=\ngithub.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc=\ngithub.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc=\ngithub.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo=\ngithub.com/moby/sys/user v0.3.0 h1:9ni5DlcW5an3SvRSx4MouotOygvzaXbaSrc/wGDFWPo=\ngithub.com/moby/sys/user v0.3.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs=\ngithub.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g=\ngithub.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28=\ngithub.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=\ngithub.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=\ngithub.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=\ngithub.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=\ngithub.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=\ngithub.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=\ngithub.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug=\ngithub.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM=\ngithub.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=\ngithub.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw=\ngithub.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=\ngithub.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=\ngithub.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=\ngithub.com/shirou/gopsutil/v3 v3.23.12 h1:z90NtUkp3bMtmICZKpC4+WaknU1eXtp5vtbQ11DgpE4=\ngithub.com/shirou/gopsutil/v3 v3.23.12/go.mod h1:1FrWgea594Jp7qmjHUUPlJDTPgcsb9mGnXDxavtikzM=\ngithub.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=\ngithub.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ=\ngithub.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU=\ngithub.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k=\ngithub.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=\ngithub.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=\ngithub.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=\ngithub.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=\ngithub.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=\ngithub.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=\ngithub.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=\ngithub.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=\ngithub.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=\ngithub.com/testcontainers/testcontainers-go v0.35.0 h1:uADsZpTKFAtp8SLK+hMwSaa+X+JiERHtd4sQAFmXeMo=\ngithub.com/testcontainers/testcontainers-go v0.35.0/go.mod h1:oEVBj5zrfJTrgjwONs1SsRbnBtH9OKl+IGl3UMcr2B4=\ngithub.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU=\ngithub.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=\ngithub.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk=\ngithub.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY=\ngithub.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngithub.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngithub.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw=\ngithub.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=\ngo.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=\ngo.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=\ngo.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk=\ngo.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw=\ngo.opentelemetry.io/otel v1.40.0 h1:oA5YeOcpRTXq6NN7frwmwFR0Cn3RhTVZvXsP4duvCms=\ngo.opentelemetry.io/otel v1.40.0/go.mod h1:IMb+uXZUKkMXdPddhwAHm6UfOwJyh4ct1ybIlV14J0g=\ngo.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 h1:Mne5On7VWdx7omSrSSZvM4Kw7cS7NQkOOmLcgscI51U=\ngo.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0/go.mod h1:IPtUMKL4O3tH5y+iXVyAXqpAwMuzC1IrxVS81rummfE=\ngo.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0 h1:IeMeyr1aBvBiPVYihXIaeIZba6b8E1bYp7lbdxK8CQg=\ngo.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0/go.mod h1:oVdCUtjq9MK9BlS7TtucsQwUcXcymNiEDjgDD2jMtZU=\ngo.opentelemetry.io/otel/metric v1.40.0 h1:rcZe317KPftE2rstWIBitCdVp89A2HqjkxR3c11+p9g=\ngo.opentelemetry.io/otel/metric v1.40.0/go.mod h1:ib/crwQH7N3r5kfiBZQbwrTge743UDc7DTFVZrrXnqc=\ngo.opentelemetry.io/otel/sdk v1.40.0 h1:KHW/jUzgo6wsPh9At46+h4upjtccTmuZCFAc9OJ71f8=\ngo.opentelemetry.io/otel/sdk v1.40.0/go.mod h1:Ph7EFdYvxq72Y8Li9q8KebuYUr2KoeyHx0DRMKrYBUE=\ngo.opentelemetry.io/otel/trace v1.40.0 h1:WA4etStDttCSYuhwvEa8OP8I5EWu24lkOzp+ZYblVjw=\ngo.opentelemetry.io/otel/trace v1.40.0/go.mod h1:zeAhriXecNGP/s2SEG3+Y8X9ujcJOTqQ5RgdEJcawiA=\ngo.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I=\ngo.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=\ngolang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=\ngolang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=\ngolang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=\ngolang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=\ngolang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=\ngolang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=\ngolang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=\ngolang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU=\ngolang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=\ngolang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=\ngolang.org/x/time v0.0.0-20220210224613-90d013bbcef8 h1:vVKdlvoWBphwdxWKrFZEuM0kGgGLxUOYcY4U/2Vjg44=\ngolang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=\ngolang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngoogle.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237 h1:RFiFrvy37/mpSpdySBDrUdipW/dHwsRwh3J3+A9VgT4=\ngoogle.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237/go.mod h1:Z5Iiy3jtmioajWHDGFk7CeugTyHtPvMHA4UTmUkyalE=\ngoogle.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda h1:LI5DOvAxUPMv/50agcLLoo+AdWc1irS9Rzz4vPuD1V4=\ngoogle.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY=\ngoogle.golang.org/grpc v1.64.1 h1:LKtvyfbX3UGVPFcGqJ9ItpVWW6oN/2XqTxfAnwRRXiA=\ngoogle.golang.org/grpc v1.64.1/go.mod h1:hiQF4LFZelK2WKaP6W0L92zGHtiQdZxk8CrSdvyjeP0=\ngoogle.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io=\ngoogle.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=\ngopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU=\ngotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU=\n"
  },
  {
    "path": "internal/test/goleak/go.mod",
    "content": "module go.mongodb.go/mongo-driver/v2/internal/test/goleak\n\ngo 1.24.0\n\nreplace go.mongodb.org/mongo-driver/v2 => ../../../\n\nrequire (\n\tgithub.com/stretchr/testify v1.9.0\n\tgo.mongodb.org/mongo-driver/v2 v2.0.0-beta2\n\tgo.uber.org/goleak v1.3.0\n)\n\nrequire (\n\tgithub.com/davecgh/go-spew v1.1.1 // indirect\n\tgithub.com/klauspost/compress v1.17.6 // indirect\n\tgithub.com/kr/pretty v0.3.0 // indirect\n\tgithub.com/pmezard/go-difflib v1.0.0 // indirect\n\tgithub.com/rogpeppe/go-internal v1.8.1 // indirect\n\tgithub.com/xdg-go/pbkdf2 v1.0.0 // indirect\n\tgithub.com/xdg-go/scram v1.2.0 // indirect\n\tgithub.com/xdg-go/stringprep v1.0.4 // indirect\n\tgithub.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect\n\tgolang.org/x/crypto v0.45.0 // indirect\n\tgolang.org/x/sync v0.18.0 // indirect\n\tgolang.org/x/text v0.31.0 // indirect\n\tgopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect\n\tgopkg.in/yaml.v3 v3.0.1 // indirect\n)\n"
  },
  {
    "path": "internal/test/goleak/go.sum",
    "content": "github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=\ngithub.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=\ngithub.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=\ngithub.com/klauspost/compress v1.17.6 h1:60eq2E/jlfwQXtvZEeBUYADs+BwKBWURIY+Gj2eRGjI=\ngithub.com/klauspost/compress v1.17.6/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM=\ngithub.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=\ngithub.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=\ngithub.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=\ngithub.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=\ngithub.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=\ngithub.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=\ngithub.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=\ngithub.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=\ngithub.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=\ngithub.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=\ngithub.com/rogpeppe/go-internal v1.8.1 h1:geMPLpDpQOgVyCg5z5GoRwLHepNdb71NXb67XFkP+Eg=\ngithub.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o=\ngithub.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=\ngithub.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=\ngithub.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=\ngithub.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=\ngithub.com/xdg-go/scram v1.2.0 h1:bYKF2AEwG5rqd1BumT4gAnvwU/M9nBp2pTSxeZw7Wvs=\ngithub.com/xdg-go/scram v1.2.0/go.mod h1:3dlrS0iBaWKYVt2ZfA4cj48umJZ+cAEbR6/SjLA88I8=\ngithub.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8=\ngithub.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=\ngithub.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM=\ngithub.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78/go.mod h1:aL8wCCfTfSfmXjznFBSZNN13rSJjlIOI1fUNAtF7rmI=\ngithub.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=\ngo.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=\ngo.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=\ngolang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=\ngolang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=\ngolang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=\ngolang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I=\ngolang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=\ngolang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=\ngolang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=\ngolang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=\ngolang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=\ngolang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=\ngopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=\ngopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=\ngopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\n"
  },
  {
    "path": "internal/test/goleak/goleak_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2024-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"os\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/require\"\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n\t\"go.uber.org/goleak\"\n)\n\nvar dbName = fmt.Sprintf(\"goleak-%d\", time.Now().Unix())\n\n// TestGoroutineLeak creates clients with various client configurations, runs\n// some operations with each one, then disconnects the client. It asserts that\n// no goroutines were leaked after the client is disconnected.\nfunc TestGoroutineLeak(t *testing.T) {\n\ttestCases := []struct {\n\t\tdesc string\n\t\topts *options.ClientOptions\n\t}{\n\t\t{\n\t\t\tdesc: \"base\",\n\t\t\topts: options.Client(),\n\t\t},\n\t\t{\n\t\t\tdesc: \"compressors=snappy\",\n\t\t\topts: options.Client().SetCompressors([]string{\"snappy\"}),\n\t\t},\n\t\t{\n\t\t\tdesc: \"compressors=zlib\",\n\t\t\topts: options.Client().SetCompressors([]string{\"zlib\"}),\n\t\t},\n\t\t{\n\t\t\tdesc: \"compressors=zstd\",\n\t\t\topts: options.Client().SetCompressors([]string{\"zstd\"}),\n\t\t},\n\t\t{\n\t\t\tdesc: \"minPoolSize=10\",\n\t\t\topts: options.Client().SetMinPoolSize(10),\n\t\t},\n\t\t{\n\t\t\tdesc: \"serverMonitoringMode=poll\",\n\t\t\topts: options.Client().SetServerMonitoringMode(options.ServerMonitoringModePoll),\n\t\t},\n\t}\n\n\tfor _, tc := range testCases {\n\t\t// These can't be run in parallel because goleak currently can't filter\n\t\t// out goroutines from other parallel subtests.\n\t\tt.Run(tc.desc, func(t *testing.T) {\n\t\t\tdefer goleak.VerifyNone(t)\n\n\t\t\tbase := options.Client()\n\t\t\tif u := os.Getenv(\"MONGODB_URI\"); u != \"\" {\n\t\t\t\tbase.ApplyURI(u)\n\t\t\t}\n\t\t\tclient, err := mongo.Connect(base, tc.opts)\n\t\t\trequire.NoError(t, err)\n\n\t\t\tdefer func() {\n\t\t\t\terr = client.Disconnect(context.Background())\n\t\t\t\trequire.NoError(t, err)\n\t\t\t}()\n\n\t\t\tdb := client.Database(dbName)\n\t\t\tdefer func() {\n\t\t\t\terr := db.Drop(context.Background())\n\t\t\t\trequire.NoError(t, err)\n\t\t\t}()\n\n\t\t\tcoll := db.Collection(collectionName(t))\n\n\t\t\t// Start a change stream to simulate a change listener workload.\n\t\t\tcs, err := coll.Watch(context.Background(), mongo.Pipeline{})\n\t\t\trequire.NoError(t, err)\n\t\t\tdefer cs.Close(context.Background())\n\n\t\t\t// Run some Insert and FindOne operations to simulate a writing and\n\t\t\t// reading workload. Run 50 iterations to increase the probability\n\t\t\t// that a goroutine leak will happen if a problem exists.\n\t\t\tfor i := 0; i < 50; i++ {\n\t\t\t\t_, err = coll.InsertOne(context.Background(), bson.M{\"x\": 123})\n\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\tvar res bson.D\n\t\t\t\terr = coll.FindOne(context.Background(), bson.D{}).Decode(&res)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t}\n\n\t\t\t// Intentionally cause some timeouts. Ignore any errors.\n\t\t\tfor i := 0; i < 50; i++ {\n\t\t\t\tctx, cancel := context.WithTimeout(context.Background(), 10*time.Microsecond)\n\t\t\t\tcoll.FindOne(ctx, bson.D{}).Err()\n\t\t\t\tcancel()\n\t\t\t}\n\n\t\t\t// Finish simulating the change listener workload. Use \"Next\" to\n\t\t\t// fetch at least one change stream document batch and decode the\n\t\t\t// first document.\n\t\t\tcs.Next(context.Background())\n\t\t\tvar res bson.D\n\t\t\terr = cs.Decode(&res)\n\t\t\trequire.NoError(t, err)\n\t\t})\n\t}\n}\n\nfunc collectionName(t *testing.T) string {\n\treturn fmt.Sprintf(\"%s-%d\", t.Name(), time.Now().Unix())\n}\n"
  },
  {
    "path": "internal/test/oidcauth/oidcauth_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2022-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage oidcauth\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"os\"\n\t\"path\"\n\t\"reflect\"\n\t\"strings\"\n\t\"sync\"\n\t\"testing\"\n\t\"time\"\n\t\"unsafe\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/event\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/require\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/auth\"\n)\n\nvar (\n\turiAdmin     = os.Getenv(\"MONGODB_URI\")\n\turiSingle    = os.Getenv(\"MONGODB_URI_SINGLE\")\n\turiMulti     = os.Getenv(\"MONGODB_URI_MULTI\")\n\toidcTokenDir = os.Getenv(\"OIDC_TOKEN_DIR\")\n)\n\nvar oidcDomain = os.Getenv(\"OIDC_DOMAIN\")\n\nfunc explicitUser(user string) string {\n\treturn fmt.Sprintf(\"%s@%s\", user, oidcDomain)\n}\n\nfunc tokenFile(user string) string {\n\treturn path.Join(oidcTokenDir, user)\n}\n\nfunc connectAdminClient() (*mongo.Client, error) {\n\treturn mongo.Connect(options.Client().ApplyURI(uriAdmin))\n}\n\nfunc connectWithMachineCB(uri string, cb options.OIDCCallback) (*mongo.Client, error) {\n\tcred := options.Credential{\n\t\tAuthMechanism:       \"MONGODB-OIDC\",\n\t\tOIDCMachineCallback: cb,\n\t}\n\toptsBuilder := options.Client().ApplyURI(uri).SetAuth(cred)\n\treturn mongo.Connect(optsBuilder)\n}\n\nfunc connectWithHumanCB(uri string, cb options.OIDCCallback) (*mongo.Client, error) {\n\tcred := options.Credential{\n\t\tAuthMechanism:     \"MONGODB-OIDC\",\n\t\tOIDCHumanCallback: cb,\n\t}\n\topts := options.Client().ApplyURI(uri).SetAuth(cred)\n\treturn mongo.Connect(opts)\n}\n\nfunc connectWithHumanCBAndUser(uri string, principal string, cb options.OIDCCallback) (*mongo.Client, error) {\n\tusername := principal\n\tswitch principal {\n\tcase \"test_user1\", \"test_user2\":\n\t\tusername = explicitUser(principal)\n\t}\n\tcred := options.Credential{\n\t\tAuthMechanism:       \"MONGODB-OIDC\",\n\t\tOIDCMachineCallback: cb,\n\t\tUsername:            username,\n\t}\n\topts := options.Client().ApplyURI(uri).SetAuth(cred)\n\treturn mongo.Connect(opts)\n}\n\nfunc connectWithHumanCBAndMonitor(uri string, cb options.OIDCCallback, m *event.CommandMonitor) (*mongo.Client, error) {\n\tcred := options.Credential{\n\t\tAuthMechanism:     \"MONGODB-OIDC\",\n\t\tOIDCHumanCallback: cb,\n\t}\n\topts := options.Client().ApplyURI(uri).SetMonitor(m).SetAuth(cred)\n\treturn mongo.Connect(opts)\n}\n\nfunc connectWithMachineCBAndProperties(uri string, cb options.OIDCCallback, props map[string]string) (*mongo.Client, error) {\n\tcred := options.Credential{\n\t\tAuthMechanism:           \"MONGODB-OIDC\",\n\t\tOIDCMachineCallback:     cb,\n\t\tAuthMechanismProperties: props,\n\t}\n\toptsBuilder := options.Client().ApplyURI(uri).SetAuth(cred)\n\treturn mongo.Connect(optsBuilder)\n}\n\n// isCallbackTest returns true if running with a callback (not built-in integration)\nfunc isCallbackTest() bool {\n\tenv := os.Getenv(\"OIDC_ENV\")\n\t// Empty OIDC_ENV or \"test\" uses callbacks\n\treturn env == \"\" || env == \"test\"\n}\n\n// connectWithOIDC creates an OIDC client appropriate for the current environment\n// When using built-in integrations (azure, gcp, k8s), no callback is used\n// When using test environment, uses the provided callback\nfunc connectWithOIDC(uri string, cb options.OIDCCallback) (*mongo.Client, error) {\n\tif isCallbackTest() {\n\t\treturn connectWithMachineCB(uri, cb)\n\t}\n\treturn mongo.Connect(options.Client().ApplyURI(uri))\n}\n\nfunc TestMain(m *testing.M) {\n\t// Skip all tests if OIDC environment is not configured.\n\t// These tests require specific OIDC infrastructure and environment variables.\n\t// This skip is added to prevent the tests from running on go test ./... evergreen tasks\n\tif os.Getenv(\"MONGODB_URI_SINGLE\") == \"\" {\n\t\tfmt.Println(\"Skipping OIDC tests: MONGODB_URI_SINGLE not set\")\n\t\tos.Exit(0)\n\t}\n\tos.Exit(m.Run())\n}\n\nfunc TestMachine_1_1_CallbackIsCalled(t *testing.T) {\n\tcallbackCount := 0\n\tvar callbackFailed error\n\tcountMutex := sync.Mutex{}\n\n\tclient, err := connectWithOIDC(uriSingle, func(context.Context, *options.OIDCArgs) (*options.OIDCCredential, error) {\n\t\tcountMutex.Lock()\n\t\tdefer countMutex.Unlock()\n\t\tcallbackCount++\n\t\texpiry := time.Now().Add(time.Hour)\n\t\ttokenFile := tokenFile(\"test_user1\")\n\t\taccessToken, err := os.ReadFile(tokenFile)\n\t\tif err != nil {\n\t\t\tcallbackFailed = fmt.Errorf(\"failed reading token file: %v\", err)\n\t\t}\n\t\treturn &options.OIDCCredential{\n\t\t\tAccessToken:  string(accessToken),\n\t\t\tExpiresAt:    &expiry,\n\t\t\tRefreshToken: nil,\n\t\t}, nil\n\t})\n\trequire.NoError(t, err, \"failed connecting client\")\n\tt.Cleanup(func() { _ = client.Disconnect(context.Background()) })\n\n\tcoll := client.Database(\"test\").Collection(\"test\")\n\n\t_, err = coll.Find(context.Background(), bson.D{})\n\trequire.NoError(t, err, \"failed executing Find\")\n\tcountMutex.Lock()\n\tdefer countMutex.Unlock()\n\tif isCallbackTest() {\n\t\trequire.Equal(t, 1, callbackCount, \"expected callback count to be 1\")\n\t\trequire.NoError(t, callbackFailed, \"callback failed\")\n\t}\n}\n\nfunc TestMachine_1_2_CallbackIsCalledOnlyOnce(t *testing.T) {\n\tcallbackCount := 0\n\tvar callbackFailed error\n\tcountMutex := sync.Mutex{}\n\n\tclient, err := connectWithOIDC(uriSingle, func(context.Context, *options.OIDCArgs) (*options.OIDCCredential, error) {\n\t\tcountMutex.Lock()\n\t\tdefer countMutex.Unlock()\n\t\tcallbackCount++\n\t\texpiry := time.Now().Add(time.Hour)\n\t\ttokenFile := tokenFile(\"test_user1\")\n\t\taccessToken, err := os.ReadFile(tokenFile)\n\t\tif err != nil {\n\t\t\tcallbackFailed = fmt.Errorf(\"failed reading token file: %v\", err)\n\t\t}\n\t\treturn &options.OIDCCredential{\n\t\t\tAccessToken:  string(accessToken),\n\t\t\tExpiresAt:    &expiry,\n\t\t\tRefreshToken: nil,\n\t\t}, nil\n\t})\n\trequire.NoError(t, err, \"failed connecting client\")\n\tt.Cleanup(func() { _ = client.Disconnect(context.Background()) })\n\n\tvar wg sync.WaitGroup\n\n\tvar findFailed error\n\tfor i := 0; i < 10; i++ {\n\t\twg.Add(1)\n\t\tgo func() {\n\t\t\tdefer wg.Done()\n\t\t\tcoll := client.Database(\"test\").Collection(\"test\")\n\t\t\t_, err := coll.Find(context.Background(), bson.D{})\n\t\t\tif err != nil {\n\t\t\t\tfindFailed = fmt.Errorf(\"failed executing Find: %v\", err)\n\t\t\t}\n\t\t}()\n\t}\n\n\twg.Wait()\n\tcountMutex.Lock()\n\tdefer countMutex.Unlock()\n\tif isCallbackTest() {\n\t\trequire.Equal(t, 1, callbackCount, \"expected callback count to be 1\")\n\t\trequire.NoError(t, callbackFailed, \"callback failed\")\n\t}\n\trequire.NoError(t, findFailed, \"find failed\")\n}\n\nfunc TestMachine_2_1_ValidCallbackInputs(t *testing.T) {\n\tif !isCallbackTest() {\n\t\tt.Skip(\"Skipping: [callback-only] test\")\n\t}\n\n\tcallbackCount := 0\n\tvar callbackFailed error\n\tcountMutex := sync.Mutex{}\n\n\tclient, err := connectWithMachineCB(uriSingle, func(ctx context.Context, args *options.OIDCArgs) (*options.OIDCCredential, error) {\n\t\tif args.RefreshToken != nil {\n\t\t\tcallbackFailed = fmt.Errorf(\"expected RefreshToken to be nil, got %v\", args.RefreshToken)\n\t\t}\n\t\ttimeout, ok := ctx.Deadline()\n\t\tif !ok {\n\t\t\tcallbackFailed = fmt.Errorf(\"expected context to have deadline, got %v\", ctx)\n\t\t}\n\t\tif timeout.Before(time.Now()) {\n\t\t\tcallbackFailed = fmt.Errorf(\"expected timeout to be in the future, got %v\", timeout)\n\t\t}\n\t\tif args.Version < 1 {\n\t\t\tcallbackFailed = fmt.Errorf(\"expected Version to be at least 1, got %d\", args.Version)\n\t\t}\n\t\tif args.IDPInfo != nil {\n\t\t\tcallbackFailed = fmt.Errorf(\"expected IdpID to be nil for Machine flow, got %v\", args.IDPInfo)\n\t\t}\n\t\tcountMutex.Lock()\n\t\tdefer countMutex.Unlock()\n\t\tcallbackCount++\n\t\texpiry := time.Now().Add(time.Hour)\n\t\ttokenFile := tokenFile(\"test_user1\")\n\t\taccessToken, err := os.ReadFile(tokenFile)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"failed reading token file: %w\", err)\n\t\t}\n\t\treturn &options.OIDCCredential{\n\t\t\tAccessToken:  string(accessToken),\n\t\t\tExpiresAt:    &expiry,\n\t\t\tRefreshToken: nil,\n\t\t}, nil\n\t})\n\trequire.NoError(t, err, \"failed connecting client\")\n\tt.Cleanup(func() { _ = client.Disconnect(context.Background()) })\n\n\tcoll := client.Database(\"test\").Collection(\"test\")\n\n\t_, err = coll.Find(context.Background(), bson.D{})\n\trequire.NoError(t, err, \"failed executing Find\")\n\tcountMutex.Lock()\n\tdefer countMutex.Unlock()\n\trequire.Equal(t, 1, callbackCount, \"expected callback count to be 1\")\n\trequire.NoError(t, callbackFailed, \"callback failed\")\n}\n\nfunc TestMachine_2_3_OIDCCallbackReturnMissingData(t *testing.T) {\n\tif !isCallbackTest() {\n\t\tt.Skip(\"Skipping: [callback-only] test\")\n\t}\n\n\tcallbackCount := 0\n\tcountMutex := sync.Mutex{}\n\n\tclient, err := connectWithMachineCB(uriSingle, func(context.Context, *options.OIDCArgs) (*options.OIDCCredential, error) {\n\t\tcountMutex.Lock()\n\t\tdefer countMutex.Unlock()\n\t\tcallbackCount++\n\t\texpiry := time.Now().Add(time.Hour)\n\t\treturn &options.OIDCCredential{\n\t\t\tAccessToken:  \"\",\n\t\t\tExpiresAt:    &expiry,\n\t\t\tRefreshToken: nil,\n\t\t}, nil\n\t})\n\trequire.NoError(t, err, \"failed connecting client\")\n\tt.Cleanup(func() { _ = client.Disconnect(context.Background()) })\n\n\tcoll := client.Database(\"test\").Collection(\"test\")\n\n\t_, err = coll.Find(context.Background(), bson.D{})\n\trequire.Error(t, err, \"should have failed to execute Find, but succeeded\")\n\tcountMutex.Lock()\n\tdefer countMutex.Unlock()\n\trequire.Equal(t, 1, callbackCount, \"expected callback count to be 1\")\n}\n\nfunc TestMachine_2_4_InvalidClientConfigurationWithCallback(t *testing.T) {\n\tif !isCallbackTest() {\n\t\tt.Skip(\"Skipping: [callback-only] test\")\n\t}\n\n\t_, err := connectWithMachineCBAndProperties(uriSingle, func(context.Context, *options.OIDCArgs) (*options.OIDCCredential, error) {\n\t\texpiry := time.Now().Add(time.Hour)\n\t\treturn &options.OIDCCredential{\n\t\t\tAccessToken:  \"\",\n\t\t\tExpiresAt:    &expiry,\n\t\t\tRefreshToken: nil,\n\t\t}, nil\n\t},\n\t\tmap[string]string{\"ENVIRONMENT\": \"test\"},\n\t)\n\trequire.Error(t, err, \"should have failed to build client when it should fail\")\n}\n\nfunc TestMachine_2_5_InvalidUseOfAllowedHosts(t *testing.T) {\n\tif os.Getenv(\"OIDC_ENV\") != \"azure\" {\n\t\tt.Skip(\"Skipping: test only runs when OIDC_ENV=azure\")\n\t}\n\n\t_, err := connectWithMachineCBAndProperties(uriSingle, func(_ context.Context, _ *options.OIDCArgs) (*options.OIDCCredential, error) {\n\t\texpiry := time.Now().Add(time.Hour)\n\t\treturn &options.OIDCCredential{\n\t\t\tAccessToken:  \"\",\n\t\t\tExpiresAt:    &expiry,\n\t\t\tRefreshToken: nil,\n\t\t}, nil\n\t},\n\t\tmap[string]string{\n\t\t\t\"ENVIRONMENT\":   \"azure\",\n\t\t\t\"ALLOWED_HOSTS\": \"\",\n\t\t},\n\t)\n\trequire.Error(t, err, \"should have failed to build client when it should fail\")\n}\n\nfunc TestMachine_3_1_FailureWithCachedTokens(t *testing.T) {\n\tcallbackCount := 0\n\tvar callbackFailed error\n\tcountMutex := sync.Mutex{}\n\n\tclient, err := connectWithOIDC(uriSingle, func(context.Context, *options.OIDCArgs) (*options.OIDCCredential, error) {\n\t\tcountMutex.Lock()\n\t\tdefer countMutex.Unlock()\n\t\tcallbackCount++\n\t\texpiry := time.Now().Add(time.Hour)\n\t\ttokenFile := tokenFile(\"test_user1\")\n\t\taccessToken, err := os.ReadFile(tokenFile)\n\t\tif err != nil {\n\t\t\tcallbackFailed = fmt.Errorf(\"failed reading token file: %v\", err)\n\t\t}\n\t\treturn &options.OIDCCredential{\n\t\t\tAccessToken:  string(accessToken),\n\t\t\tExpiresAt:    &expiry,\n\t\t\tRefreshToken: nil,\n\t\t}, nil\n\t})\n\trequire.NoError(t, err, \"failed connecting client\")\n\tt.Cleanup(func() { _ = client.Disconnect(context.Background()) })\n\n\t// Poison the cache with a random token\n\tclientElem := reflect.ValueOf(client).Elem()\n\tauthenticatorField := clientElem.FieldByName(\"authenticator\")\n\tauthenticatorField = reflect.NewAt(\n\t\tauthenticatorField.Type(),\n\t\tunsafe.Pointer(authenticatorField.UnsafeAddr())).Elem()\n\t// this is the only usage of the x packages in the test, showing the public interface is\n\t// correct.\n\tauthenticatorField.Interface().(*auth.OIDCAuthenticator).SetAccessToken(\"some random happy sunshine string\")\n\n\tcoll := client.Database(\"test\").Collection(\"test\")\n\n\t_, err = coll.Find(context.Background(), bson.D{})\n\trequire.NoError(t, err, \"failed executing Find\")\n\tcountMutex.Lock()\n\tdefer countMutex.Unlock()\n\tif isCallbackTest() {\n\t\trequire.Equal(t, 1, callbackCount, \"expected callback count to be 1\")\n\t\trequire.NoError(t, callbackFailed, \"callback failed\")\n\t}\n}\n\nfunc TestMachine_3_2_AuthFailuresWithoutCachedTokens(t *testing.T) {\n\tif !isCallbackTest() {\n\t\tt.Skip(\"Skipping: [callback-only] test\")\n\t}\n\n\tcallbackCount := 0\n\tvar callbackFailed error\n\tcountMutex := sync.Mutex{}\n\n\tclient, err := connectWithMachineCB(uriSingle, func(context.Context, *options.OIDCArgs) (*options.OIDCCredential, error) {\n\t\tcountMutex.Lock()\n\t\tdefer countMutex.Unlock()\n\t\tcallbackCount++\n\t\texpiry := time.Now().Add(time.Hour)\n\t\treturn &options.OIDCCredential{\n\t\t\tAccessToken:  \"this is a bad, bad token\",\n\t\t\tExpiresAt:    &expiry,\n\t\t\tRefreshToken: nil,\n\t\t}, nil\n\t})\n\trequire.NoError(t, err, \"failed connecting client\")\n\tt.Cleanup(func() { _ = client.Disconnect(context.Background()) })\n\n\tcoll := client.Database(\"test\").Collection(\"test\")\n\t_, err = coll.Find(context.Background(), bson.D{})\n\trequire.Error(t, err, \"should have failed to execute Find, but succeeded\")\n\tcountMutex.Lock()\n\tdefer countMutex.Unlock()\n\trequire.Equal(t, 1, callbackCount, \"expected callback count to be 1\")\n\trequire.NoError(t, callbackFailed, \"callback failed\")\n}\n\nfunc TestMachine_3_3_UnexpectedErrorCodeDoesNotClearCache(t *testing.T) {\n\tcallbackCount := 0\n\tvar callbackFailed error\n\tcountMutex := sync.Mutex{}\n\n\tadminClient, err := connectAdminClient()\n\trequire.NoError(t, err, \"failed connecting admin client\")\n\tt.Cleanup(func() { _ = adminClient.Disconnect(context.Background()) })\n\n\tclient, err := connectWithOIDC(uriSingle, func(context.Context, *options.OIDCArgs) (*options.OIDCCredential, error) {\n\t\tcountMutex.Lock()\n\t\tdefer countMutex.Unlock()\n\t\tcallbackCount++\n\t\texpiry := time.Now().Add(time.Hour)\n\t\ttokenFile := tokenFile(\"test_user1\")\n\t\taccessToken, err := os.ReadFile(tokenFile)\n\t\tif err != nil {\n\t\t\tcallbackFailed = fmt.Errorf(\"failed reading token file: %v\", err)\n\t\t}\n\t\treturn &options.OIDCCredential{\n\t\t\tAccessToken:  string(accessToken),\n\t\t\tExpiresAt:    &expiry,\n\t\t\tRefreshToken: nil,\n\t\t}, nil\n\t})\n\trequire.NoError(t, err, \"failed connecting client\")\n\tt.Cleanup(func() { _ = client.Disconnect(context.Background()) })\n\n\tcoll := client.Database(\"test\").Collection(\"test\")\n\n\tres := adminClient.Database(\"admin\").RunCommand(context.Background(), bson.D{\n\t\t{Key: \"configureFailPoint\", Value: \"failCommand\"},\n\t\t{Key: \"mode\", Value: bson.D{\n\t\t\t{Key: \"times\", Value: 1},\n\t\t}},\n\t\t{Key: \"data\", Value: bson.D{\n\t\t\t{Key: \"failCommands\", Value: bson.A{\n\t\t\t\t\"saslStart\",\n\t\t\t}},\n\t\t\t{Key: \"errorCode\", Value: 20},\n\t\t}},\n\t})\n\n\trequire.NoError(t, res.Err(), \"failed setting failpoint\")\n\n\t_, err = coll.Find(context.Background(), bson.D{})\n\trequire.Error(t, err, \"should have failed to execute Find, but succeeded\")\n\n\tcountMutex.Lock()\n\tdefer countMutex.Unlock()\n\tif isCallbackTest() {\n\t\trequire.Equal(t, 1, callbackCount, \"expected callback count to be 1\")\n\t}\n\n\t_, err = coll.Find(context.Background(), bson.D{})\n\trequire.NoError(t, err, \"failed executing Find\")\n\tif isCallbackTest() {\n\t\trequire.Equal(t, 1, callbackCount, \"expected callback count to be 1\")\n\t\trequire.NoError(t, callbackFailed, \"callback failed\")\n\t}\n}\n\nfunc TestMachine_4_1_ReauthenticationSucceeds(t *testing.T) {\n\tcallbackCount := 0\n\tvar callbackFailed error\n\tcountMutex := sync.Mutex{}\n\n\tadminClient, err := connectAdminClient()\n\trequire.NoError(t, err, \"failed connecting admin client\")\n\tt.Cleanup(func() { _ = adminClient.Disconnect(context.Background()) })\n\n\tclient, err := connectWithOIDC(uriSingle, func(context.Context, *options.OIDCArgs) (*options.OIDCCredential, error) {\n\t\tcountMutex.Lock()\n\t\tdefer countMutex.Unlock()\n\t\tcallbackCount++\n\t\texpiry := time.Now().Add(time.Hour)\n\t\ttokenFile := tokenFile(\"test_user1\")\n\t\taccessToken, err := os.ReadFile(tokenFile)\n\t\tif err != nil {\n\t\t\tcallbackFailed = fmt.Errorf(\"failed reading token file: %v\", err)\n\t\t}\n\t\treturn &options.OIDCCredential{\n\t\t\tAccessToken:  string(accessToken),\n\t\t\tExpiresAt:    &expiry,\n\t\t\tRefreshToken: nil,\n\t\t}, nil\n\t})\n\trequire.NoError(t, err, \"failed connecting client\")\n\tt.Cleanup(func() { _ = client.Disconnect(context.Background()) })\n\n\tcoll := client.Database(\"test\").Collection(\"test\")\n\tres := adminClient.Database(\"admin\").RunCommand(context.Background(), bson.D{\n\t\t{Key: \"configureFailPoint\", Value: \"failCommand\"},\n\t\t{Key: \"mode\", Value: bson.D{\n\t\t\t{Key: \"times\", Value: 1},\n\t\t}},\n\t\t{Key: \"data\", Value: bson.D{\n\t\t\t{Key: \"failCommands\", Value: bson.A{\n\t\t\t\t\"find\",\n\t\t\t}},\n\t\t\t{Key: \"errorCode\", Value: 391},\n\t\t}},\n\t})\n\n\trequire.NoError(t, res.Err(), \"failed setting failpoint\")\n\n\t_, err = coll.Find(context.Background(), bson.D{})\n\trequire.NoError(t, err, \"failed executing Find\")\n\tcountMutex.Lock()\n\tdefer countMutex.Unlock()\n\tif isCallbackTest() {\n\t\trequire.Equal(t, 2, callbackCount, \"expected callback count to be 2\")\n\t\trequire.NoError(t, callbackFailed, \"callback failed\")\n\t}\n}\n\nfunc TestMachine_4_2_ReadCommandsFailIfReauthenticationFails(t *testing.T) {\n\tif !isCallbackTest() {\n\t\tt.Skip(\"Skipping: [callback-only] test\")\n\t}\n\n\tcallbackCount := 0\n\tvar callbackFailed error\n\tfirstCall := true\n\tcountMutex := sync.Mutex{}\n\n\tadminClient, err := connectAdminClient()\n\trequire.NoError(t, err, \"failed connecting admin client\")\n\tt.Cleanup(func() { _ = adminClient.Disconnect(context.Background()) })\n\n\tclient, err := connectWithMachineCB(uriSingle, func(context.Context, *options.OIDCArgs) (*options.OIDCCredential, error) {\n\t\tcountMutex.Lock()\n\t\tdefer countMutex.Unlock()\n\t\tcallbackCount++\n\t\texpiry := time.Now().Add(time.Hour)\n\t\tif firstCall {\n\t\t\tfirstCall = false\n\t\t\ttokenFile := tokenFile(\"test_user1\")\n\t\t\taccessToken, err := os.ReadFile(tokenFile)\n\t\t\tif err != nil {\n\t\t\t\tcallbackFailed = fmt.Errorf(\"failed reading token file: %v\", err)\n\t\t\t}\n\t\t\treturn &options.OIDCCredential{\n\t\t\t\tAccessToken:  string(accessToken),\n\t\t\t\tExpiresAt:    &expiry,\n\t\t\t\tRefreshToken: nil,\n\t\t\t}, nil\n\t\t}\n\t\treturn &options.OIDCCredential{\n\t\t\tAccessToken:  \"this is a bad, bad token\",\n\t\t\tExpiresAt:    &expiry,\n\t\t\tRefreshToken: nil,\n\t\t}, nil\n\t})\n\trequire.NoError(t, err, \"failed connecting client\")\n\tt.Cleanup(func() { _ = client.Disconnect(context.Background()) })\n\n\tcoll := client.Database(\"test\").Collection(\"test\")\n\t_, err = coll.Find(context.Background(), bson.D{})\n\trequire.NoError(t, err, \"failed executing Find\")\n\n\tres := adminClient.Database(\"admin\").RunCommand(context.Background(), bson.D{\n\t\t{Key: \"configureFailPoint\", Value: \"failCommand\"},\n\t\t{Key: \"mode\", Value: bson.D{\n\t\t\t{Key: \"times\", Value: 1},\n\t\t}},\n\t\t{Key: \"data\", Value: bson.D{\n\t\t\t{Key: \"failCommands\", Value: bson.A{\n\t\t\t\t\"find\",\n\t\t\t}},\n\t\t\t{Key: \"errorCode\", Value: 391},\n\t\t}},\n\t})\n\n\trequire.NoError(t, res.Err(), \"failed setting failpoint\")\n\n\t_, err = coll.Find(context.Background(), bson.D{})\n\trequire.Error(t, err, \"should have failed to execute Find, but succeeded\")\n\n\tcountMutex.Lock()\n\tdefer countMutex.Unlock()\n\trequire.Equal(t, 2, callbackCount, \"expected callback count to be 2\")\n\trequire.NoError(t, callbackFailed, \"callback failed\")\n}\n\nfunc TestMachine_4_3_WriteCommandsFailIfReauthenticationFails(t *testing.T) {\n\tif !isCallbackTest() {\n\t\tt.Skip(\"Skipping: [callback-only] test\")\n\t}\n\n\tcallbackCount := 0\n\tvar callbackFailed error\n\tfirstCall := true\n\tcountMutex := sync.Mutex{}\n\n\tadminClient, err := connectAdminClient()\n\trequire.NoError(t, err, \"failed connecting admin client\")\n\tt.Cleanup(func() { _ = adminClient.Disconnect(context.Background()) })\n\n\tclient, err := connectWithMachineCB(uriSingle, func(context.Context, *options.OIDCArgs) (*options.OIDCCredential, error) {\n\t\tcountMutex.Lock()\n\t\tdefer countMutex.Unlock()\n\t\tcallbackCount++\n\t\texpiry := time.Now().Add(time.Hour)\n\t\tif firstCall {\n\t\t\tfirstCall = false\n\t\t\ttokenFile := tokenFile(\"test_user1\")\n\t\t\taccessToken, err := os.ReadFile(tokenFile)\n\t\t\tif err != nil {\n\t\t\t\tcallbackFailed = fmt.Errorf(\"failed reading token file: %v\", err)\n\t\t\t}\n\t\t\treturn &options.OIDCCredential{\n\t\t\t\tAccessToken:  string(accessToken),\n\t\t\t\tExpiresAt:    &expiry,\n\t\t\t\tRefreshToken: nil,\n\t\t\t}, nil\n\t\t}\n\t\treturn &options.OIDCCredential{\n\t\t\tAccessToken:  \"this is a bad, bad token\",\n\t\t\tExpiresAt:    &expiry,\n\t\t\tRefreshToken: nil,\n\t\t}, nil\n\t})\n\trequire.NoError(t, err, \"failed connecting client\")\n\tt.Cleanup(func() { _ = client.Disconnect(context.Background()) })\n\n\tcoll := client.Database(\"test\").Collection(\"test\")\n\t_, err = coll.InsertOne(context.Background(), bson.D{})\n\trequire.NoError(t, err, \"failed executing Insert\")\n\n\tres := adminClient.Database(\"admin\").RunCommand(context.Background(), bson.D{\n\t\t{Key: \"configureFailPoint\", Value: \"failCommand\"},\n\t\t{Key: \"mode\", Value: bson.D{\n\t\t\t{Key: \"times\", Value: 1},\n\t\t}},\n\t\t{Key: \"data\", Value: bson.D{\n\t\t\t{Key: \"failCommands\", Value: bson.A{\n\t\t\t\t\"insert\",\n\t\t\t}},\n\t\t\t{Key: \"errorCode\", Value: 391},\n\t\t}},\n\t})\n\n\trequire.NoError(t, res.Err(), \"failed setting failpoint\")\n\n\t_, err = coll.InsertOne(context.Background(), bson.D{})\n\trequire.Error(t, err, \"should have failed to execute Insert, but succeeded\")\n\n\tcountMutex.Lock()\n\tdefer countMutex.Unlock()\n\trequire.Equal(t, 2, callbackCount, \"expected callback count to be 2\")\n\trequire.NoError(t, callbackFailed, \"callback failed\")\n}\n\nfunc TestHuman_1_1_SinglePrincipalImplicitUsername(t *testing.T) {\n\tif os.Getenv(\"OIDC_ENV\") != \"\" {\n\t\tt.Skip(\"Skipping: test only runs when OIDC_ENV is empty\")\n\t}\n\n\tcallbackCount := 0\n\tvar callbackFailed error\n\tcountMutex := sync.Mutex{}\n\n\tclient, err := connectWithHumanCB(uriSingle, func(context.Context, *options.OIDCArgs) (*options.OIDCCredential, error) {\n\t\tcountMutex.Lock()\n\t\tdefer countMutex.Unlock()\n\t\tcallbackCount++\n\t\texpiry := time.Now().Add(time.Hour)\n\t\ttokenFile := tokenFile(\"test_user1\")\n\t\taccessToken, err := os.ReadFile(tokenFile)\n\t\tif err != nil {\n\t\t\tcallbackFailed = fmt.Errorf(\"failed reading token file: %v\", err)\n\t\t}\n\t\treturn &options.OIDCCredential{\n\t\t\tAccessToken:  string(accessToken),\n\t\t\tExpiresAt:    &expiry,\n\t\t\tRefreshToken: nil,\n\t\t}, nil\n\t})\n\trequire.NoError(t, err, \"failed connecting client\")\n\tt.Cleanup(func() { _ = client.Disconnect(context.Background()) })\n\n\tcoll := client.Database(\"test\").Collection(\"test\")\n\n\t_, err = coll.Find(context.Background(), bson.D{})\n\trequire.NoError(t, err, \"failed executing Find\")\n\tcountMutex.Lock()\n\tdefer countMutex.Unlock()\n\trequire.Equal(t, 1, callbackCount, \"expected callback count to be 1\")\n\trequire.NoError(t, callbackFailed, \"callback failed\")\n}\n\nfunc TestHuman_1_2_SinglePrincipalExplicitUsername(t *testing.T) {\n\tif os.Getenv(\"OIDC_ENV\") != \"\" {\n\t\tt.Skip(\"Skipping: test only runs when OIDC_ENV is empty\")\n\t}\n\n\tcallbackCount := 0\n\tvar callbackFailed error\n\tcountMutex := sync.Mutex{}\n\n\tclient, err := connectWithHumanCBAndUser(uriSingle, \"test_user1\", func(_ context.Context, _ *options.OIDCArgs) (*options.OIDCCredential, error) {\n\t\tcountMutex.Lock()\n\t\tdefer countMutex.Unlock()\n\t\tcallbackCount++\n\t\texpiry := time.Now().Add(time.Hour)\n\t\ttokenFile := tokenFile(\"test_user1\")\n\t\taccessToken, err := os.ReadFile(tokenFile)\n\t\tif err != nil {\n\t\t\tcallbackFailed = fmt.Errorf(\"failed reading token file: %v\", err)\n\t\t}\n\t\treturn &options.OIDCCredential{\n\t\t\tAccessToken:  string(accessToken),\n\t\t\tExpiresAt:    &expiry,\n\t\t\tRefreshToken: nil,\n\t\t}, nil\n\t})\n\trequire.NoError(t, err, \"failed connecting client\")\n\tt.Cleanup(func() { _ = client.Disconnect(context.Background()) })\n\n\tcoll := client.Database(\"test\").Collection(\"test\")\n\n\t_, err = coll.Find(context.Background(), bson.D{})\n\trequire.NoError(t, err, \"failed executing Find\")\n\tcountMutex.Lock()\n\tdefer countMutex.Unlock()\n\trequire.Equal(t, 1, callbackCount, \"expected callback count to be 1\")\n\trequire.NoError(t, callbackFailed, \"callback failed\")\n}\n\nfunc TestHuman_1_3_MultiplePrincipalUser1(t *testing.T) {\n\tif os.Getenv(\"OIDC_ENV\") != \"\" {\n\t\tt.Skip(\"Skipping: test only runs when OIDC_ENV is empty\")\n\t}\n\n\tcallbackCount := 0\n\tvar callbackFailed error\n\tcountMutex := sync.Mutex{}\n\tcb := func(context.Context, *options.OIDCArgs) (*options.OIDCCredential, error) {\n\t\tcountMutex.Lock()\n\t\tdefer countMutex.Unlock()\n\t\tcallbackCount++\n\t\texpiry := time.Now().Add(time.Hour)\n\t\ttokenFile := tokenFile(\"test_user1\")\n\t\taccessToken, err := os.ReadFile(tokenFile)\n\t\tif err != nil {\n\t\t\tcallbackFailed = fmt.Errorf(\"failed reading token file: %v\", err)\n\t\t}\n\t\treturn &options.OIDCCredential{\n\t\t\tAccessToken:  string(accessToken),\n\t\t\tExpiresAt:    &expiry,\n\t\t\tRefreshToken: nil,\n\t\t}, nil\n\t}\n\tcred := options.Credential{\n\t\tAuthMechanism:     \"MONGODB-OIDC\",\n\t\tOIDCHumanCallback: cb,\n\t\tUsername:          explicitUser(\"test_user1\"),\n\t}\n\topts := options.Client().ApplyURI(uriMulti).SetAuth(cred)\n\tclient, err := mongo.Connect(opts)\n\trequire.NoError(t, err, \"failed connecting client\")\n\tt.Cleanup(func() { _ = client.Disconnect(context.Background()) })\n\n\tcoll := client.Database(\"test\").Collection(\"test\")\n\n\t_, err = coll.Find(context.Background(), bson.D{})\n\trequire.NoError(t, err, \"failed executing Find\")\n\tcountMutex.Lock()\n\tdefer countMutex.Unlock()\n\trequire.Equal(t, 1, callbackCount, \"expected callback count to be 1\")\n\trequire.NoError(t, callbackFailed, \"callback failed\")\n}\n\nfunc TestHuman_1_4_MultiplePrincipalUser2(t *testing.T) {\n\tif os.Getenv(\"OIDC_ENV\") != \"\" {\n\t\tt.Skip(\"Skipping: test only runs when OIDC_ENV is empty\")\n\t}\n\n\tcallbackCount := 0\n\tvar callbackFailed error\n\tcountMutex := sync.Mutex{}\n\tcb := func(context.Context, *options.OIDCArgs) (*options.OIDCCredential, error) {\n\t\tcountMutex.Lock()\n\t\tdefer countMutex.Unlock()\n\t\tcallbackCount++\n\t\texpiry := time.Now().Add(time.Hour)\n\t\ttokenFile := tokenFile(\"test_user2\")\n\t\taccessToken, err := os.ReadFile(tokenFile)\n\t\tif err != nil {\n\t\t\tcallbackFailed = fmt.Errorf(\"failed reading token file: %v\", err)\n\t\t}\n\t\treturn &options.OIDCCredential{\n\t\t\tAccessToken:  string(accessToken),\n\t\t\tExpiresAt:    &expiry,\n\t\t\tRefreshToken: nil,\n\t\t}, nil\n\t}\n\tcred := options.Credential{\n\t\tAuthMechanism:     \"MONGODB-OIDC\",\n\t\tOIDCHumanCallback: cb,\n\t\tUsername:          explicitUser(\"test_user2\"),\n\t}\n\topts := options.Client().ApplyURI(uriMulti).SetAuth(cred)\n\tclient, err := mongo.Connect(opts)\n\trequire.NoError(t, err, \"failed connecting client\")\n\tt.Cleanup(func() { _ = client.Disconnect(context.Background()) })\n\n\tcoll := client.Database(\"test\").Collection(\"test\")\n\n\t_, err = coll.Find(context.Background(), bson.D{})\n\trequire.NoError(t, err, \"failed executing Find\")\n\tcountMutex.Lock()\n\tdefer countMutex.Unlock()\n\trequire.Equal(t, 1, callbackCount, \"expected callback count to be 1\")\n\trequire.NoError(t, callbackFailed, \"callback failed\")\n}\n\nfunc TestHuman_1_5_MultiplePrincipalNoUser(t *testing.T) {\n\tif os.Getenv(\"OIDC_ENV\") != \"\" {\n\t\tt.Skip(\"Skipping: test only runs when OIDC_ENV is empty\")\n\t}\n\n\tcallbackCount := 0\n\tvar callbackFailed error\n\tcountMutex := sync.Mutex{}\n\n\tclient, err := connectWithHumanCB(uriMulti, func(context.Context, *options.OIDCArgs) (*options.OIDCCredential, error) {\n\t\tcountMutex.Lock()\n\t\tdefer countMutex.Unlock()\n\t\tcallbackCount++\n\t\texpiry := time.Now().Add(time.Hour)\n\t\ttokenFile := tokenFile(\"test_user1\")\n\t\taccessToken, err := os.ReadFile(tokenFile)\n\t\tif err != nil {\n\t\t\tcallbackFailed = fmt.Errorf(\"failed reading token file: %v\", err)\n\t\t}\n\t\treturn &options.OIDCCredential{\n\t\t\tAccessToken:  string(accessToken),\n\t\t\tExpiresAt:    &expiry,\n\t\t\tRefreshToken: nil,\n\t\t}, nil\n\t})\n\trequire.NoError(t, err, \"failed connecting client\")\n\tt.Cleanup(func() { _ = client.Disconnect(context.Background()) })\n\n\tcoll := client.Database(\"test\").Collection(\"test\")\n\n\t_, err = coll.Find(context.Background(), bson.D{})\n\trequire.Error(t, err, \"should have failed to execute Find, but succeeded\")\n\tcountMutex.Lock()\n\tdefer countMutex.Unlock()\n\trequire.Equal(t, 0, callbackCount, \"expected callback count to be 0\")\n\trequire.NoError(t, callbackFailed, \"callback failed\")\n}\n\nfunc TestHuman_1_6_AllowedHostsBlocked(t *testing.T) {\n\tif os.Getenv(\"OIDC_ENV\") != \"\" {\n\t\tt.Skip(\"Skipping: test only runs when OIDC_ENV is empty\")\n\t}\n\n\tvar callbackFailed error\n\n\t{\n\t\tcb := func(context.Context, *options.OIDCArgs) (*options.OIDCCredential, error) {\n\t\t\texpiry := time.Now().Add(time.Hour)\n\t\t\ttokenFile := tokenFile(\"test_user1\")\n\t\t\taccessToken, err := os.ReadFile(tokenFile)\n\t\t\tif err != nil {\n\t\t\t\tcallbackFailed = fmt.Errorf(\"failed reading token file: %v\", err)\n\t\t\t}\n\t\t\treturn &options.OIDCCredential{\n\t\t\t\tAccessToken:  string(accessToken),\n\t\t\t\tExpiresAt:    &expiry,\n\t\t\t\tRefreshToken: nil,\n\t\t\t}, nil\n\t\t}\n\t\tcred := options.Credential{\n\t\t\tAuthMechanism:           \"MONGODB-OIDC\",\n\t\t\tOIDCHumanCallback:       cb,\n\t\t\tAuthMechanismProperties: map[string]string{\"ALLOWED_HOSTS\": \"\"},\n\t\t}\n\t\topts := options.Client().ApplyURI(uriMulti).SetAuth(cred)\n\t\tclient, err := mongo.Connect(opts)\n\t\trequire.NoError(t, err, \"failed connecting client\")\n\t\tdefer func() { _ = client.Disconnect(context.Background()) }()\n\n\t\tcoll := client.Database(\"test\").Collection(\"test\")\n\n\t\t_, err = coll.Find(context.Background(), bson.D{})\n\t\trequire.Error(t, err, \"should have failed to execute Find, but succeeded\")\n\t}\n\n\t{\n\t\tcb := func(context.Context, *options.OIDCArgs) (*options.OIDCCredential, error) {\n\t\t\texpiry := time.Now().Add(time.Hour)\n\t\t\ttokenFile := tokenFile(\"test_user1\")\n\t\t\taccessToken, err := os.ReadFile(tokenFile)\n\t\t\tif err != nil {\n\t\t\t\tcallbackFailed = fmt.Errorf(\"failed reading token file: %v\", err)\n\t\t\t}\n\t\t\treturn &options.OIDCCredential{\n\t\t\t\tAccessToken:  string(accessToken),\n\t\t\t\tExpiresAt:    &expiry,\n\t\t\t\tRefreshToken: nil,\n\t\t\t}, nil\n\t\t}\n\t\tcred := options.Credential{\n\t\t\tAuthMechanism:           \"MONGODB-OIDC\",\n\t\t\tOIDCHumanCallback:       cb,\n\t\t\tAuthMechanismProperties: map[string]string{\"ALLOWED_HOSTS\": \"example.com\"},\n\t\t}\n\t\topts := options.Client().ApplyURI(\"mongodb://localhost/?authMechanism=MONGODB-OIDC&ignored=example.com\").SetAuth(cred)\n\t\tclient, err := mongo.Connect(opts)\n\t\trequire.NoError(t, err, \"failed connecting client\")\n\t\tdefer func() { _ = client.Disconnect(context.Background()) }()\n\n\t\tcoll := client.Database(\"test\").Collection(\"test\")\n\n\t\t_, err = coll.Find(context.Background(), bson.D{})\n\t\trequire.Error(t, err, \"should have failed to execute Find, but succeeded\")\n\t}\n\n\trequire.NoError(t, callbackFailed, \"callback failed\")\n}\n\nfunc TestHuman_1_7_AllowedHostsInConnectionStringIgnored(t *testing.T) {\n\tif os.Getenv(\"OIDC_ENV\") != \"\" {\n\t\tt.Skip(\"Skipping: test only runs when OIDC_ENV is empty\")\n\t}\n\n\turi := \"mongodb+srv://example.com/?authMechanism=MONGODB-OIDC&authMechanismProperties=ALLOWED_HOSTS:%5B%22example.com%22%5D\"\n\topts := options.Client().ApplyURI(uri)\n\terr := opts.Validate()\n\trequire.Error(t, err, \"should have failed to apply URI which should produce an error\")\n}\n\nfunc TestHuman_1_8_MachineIDPHumanCallback(t *testing.T) {\n\tif os.Getenv(\"OIDC_ENV\") != \"\" {\n\t\tt.Skip(\"Skipping: test only runs when OIDC_ENV is empty\")\n\t}\n\n\tif _, ok := os.LookupEnv(\"OIDC_IS_LOCAL\"); !ok {\n\t\tt.Skip(\"Skipping: test only runs when OIDC_IS_LOCAL is set\")\n\t}\n\tcallbackCount := 0\n\n\tvar callbackFailed error\n\tcountMutex := sync.Mutex{}\n\n\tclient, err := connectWithHumanCBAndUser(uriSingle, \"test_machine\", func(_ context.Context, _ *options.OIDCArgs) (*options.OIDCCredential, error) {\n\t\tcountMutex.Lock()\n\t\tdefer countMutex.Unlock()\n\t\tcallbackCount++\n\t\texpiry := time.Now().Add(time.Hour)\n\t\ttokenFile := tokenFile(\"test_machine\")\n\t\taccessToken, err := os.ReadFile(tokenFile)\n\t\tif err != nil {\n\t\t\tcallbackFailed = fmt.Errorf(\"failed reading token file: %v\", err)\n\t\t}\n\t\treturn &options.OIDCCredential{\n\t\t\tAccessToken:  string(accessToken),\n\t\t\tExpiresAt:    &expiry,\n\t\t\tRefreshToken: nil,\n\t\t}, nil\n\t})\n\trequire.NoError(t, err, \"failed connecting client\")\n\tt.Cleanup(func() { _ = client.Disconnect(context.Background()) })\n\n\tcoll := client.Database(\"test\").Collection(\"test\")\n\n\t_, err = coll.Find(context.Background(), bson.D{})\n\trequire.NoError(t, err, \"failed executing Find\")\n\tcountMutex.Lock()\n\tdefer countMutex.Unlock()\n\trequire.Equal(t, 1, callbackCount, \"expected callback count to be 1\")\n\trequire.NoError(t, callbackFailed, \"callback failed\")\n}\n\nfunc TestHuman_2_1_ValidCallbackInputs(t *testing.T) {\n\tif os.Getenv(\"OIDC_ENV\") != \"\" {\n\t\tt.Skip(\"Skipping: test only runs when OIDC_ENV is empty\")\n\t}\n\n\tcallbackCount := 0\n\tvar callbackFailed error\n\tcountMutex := sync.Mutex{}\n\n\tclient, err := connectWithHumanCB(uriSingle, func(_ context.Context, args *options.OIDCArgs) (*options.OIDCCredential, error) {\n\t\tcountMutex.Lock()\n\t\tdefer countMutex.Unlock()\n\t\tcallbackCount++\n\t\texpiry := time.Now().Add(time.Hour)\n\t\tif args.Version != 1 {\n\t\t\tcallbackFailed = fmt.Errorf(\"expected version to be 1, got %d\", args.Version)\n\t\t}\n\t\tif args.IDPInfo == nil {\n\t\t\tcallbackFailed = fmt.Errorf(\"expected IDPInfo to be non-nil, previous error: (%v)\", callbackFailed)\n\t\t}\n\t\ttokenFile := tokenFile(\"test_user1\")\n\t\taccessToken, err := os.ReadFile(tokenFile)\n\t\tif err != nil {\n\t\t\tcallbackFailed = fmt.Errorf(\"failed reading token file: %v, previous error: (%v)\", err, callbackFailed)\n\t\t}\n\t\treturn &options.OIDCCredential{\n\t\t\tAccessToken:  string(accessToken),\n\t\t\tExpiresAt:    &expiry,\n\t\t\tRefreshToken: nil,\n\t\t}, nil\n\t})\n\trequire.NoError(t, err, \"failed connecting client\")\n\tt.Cleanup(func() { _ = client.Disconnect(context.Background()) })\n\n\tcoll := client.Database(\"test\").Collection(\"test\")\n\n\t_, err = coll.Find(context.Background(), bson.D{})\n\trequire.NoError(t, err, \"failed executing Find\")\n\tcountMutex.Lock()\n\tdefer countMutex.Unlock()\n\trequire.Equal(t, 1, callbackCount, \"expected callback count to be 1\")\n\trequire.NoError(t, callbackFailed, \"callback failed\")\n}\n\nfunc TestHuman_2_2_CallbackReturnsMissingData(t *testing.T) {\n\tif os.Getenv(\"OIDC_ENV\") != \"\" {\n\t\tt.Skip(\"Skipping: test only runs when OIDC_ENV is empty\")\n\t}\n\n\tcallbackCount := 0\n\tcountMutex := sync.Mutex{}\n\n\tclient, err := connectWithHumanCB(uriSingle, func(context.Context, *options.OIDCArgs) (*options.OIDCCredential, error) {\n\t\tcountMutex.Lock()\n\t\tdefer countMutex.Unlock()\n\t\tcallbackCount++\n\t\treturn &options.OIDCCredential{}, nil\n\t})\n\trequire.NoError(t, err, \"failed connecting client\")\n\tt.Cleanup(func() { _ = client.Disconnect(context.Background()) })\n\n\tcoll := client.Database(\"test\").Collection(\"test\")\n\n\t_, err = coll.Find(context.Background(), bson.D{})\n\trequire.Error(t, err, \"Find should have failed\")\n\tcountMutex.Lock()\n\tdefer countMutex.Unlock()\n\trequire.Equal(t, 1, callbackCount, \"expected callback count to be 1\")\n}\n\nfunc TestHuman_2_3_RefreshTokenIsPassedToCallback(t *testing.T) {\n\tif os.Getenv(\"OIDC_ENV\") != \"\" {\n\t\tt.Skip(\"Skipping: test only runs when OIDC_ENV is empty\")\n\t}\n\n\tcallbackCount := 0\n\tvar callbackFailed error\n\tcountMutex := sync.Mutex{}\n\n\tadminClient, err := connectAdminClient()\n\trequire.NoError(t, err, \"failed connecting admin client\")\n\tt.Cleanup(func() { _ = adminClient.Disconnect(context.Background()) })\n\n\tclient, err := connectWithHumanCB(uriSingle, func(_ context.Context, args *options.OIDCArgs) (*options.OIDCCredential, error) {\n\t\tcountMutex.Lock()\n\t\tdefer countMutex.Unlock()\n\t\tcallbackCount++\n\t\tif callbackCount == 1 && args.RefreshToken != nil {\n\t\t\tcallbackFailed = fmt.Errorf(\"expected refresh token to be nil first time, got %v, previous error: (%v)\", args.RefreshToken, callbackFailed)\n\t\t}\n\t\tif callbackCount == 2 && args.RefreshToken == nil {\n\t\t\tcallbackFailed = fmt.Errorf(\"expected refresh token to be non-nil second time, got %v, previous error: (%v)\", args.RefreshToken, callbackFailed)\n\t\t}\n\t\texpiry := time.Now().Add(time.Hour)\n\t\ttokenFile := tokenFile(\"test_user1\")\n\t\taccessToken, err := os.ReadFile(tokenFile)\n\t\tif err != nil {\n\t\t\tcallbackFailed = fmt.Errorf(\"failed reading token file: %v\", err)\n\t\t}\n\t\trt := \"this is fake\"\n\t\treturn &options.OIDCCredential{\n\t\t\tAccessToken:  string(accessToken),\n\t\t\tExpiresAt:    &expiry,\n\t\t\tRefreshToken: &rt,\n\t\t}, nil\n\t})\n\trequire.NoError(t, err, \"failed connecting client\")\n\tt.Cleanup(func() { _ = client.Disconnect(context.Background()) })\n\n\tres := adminClient.Database(\"admin\").RunCommand(context.Background(), bson.D{\n\t\t{Key: \"configureFailPoint\", Value: \"failCommand\"},\n\t\t{Key: \"mode\", Value: bson.D{\n\t\t\t{Key: \"times\", Value: 1},\n\t\t}},\n\t\t{Key: \"data\", Value: bson.D{\n\t\t\t{Key: \"failCommands\", Value: bson.A{\n\t\t\t\t\"find\",\n\t\t\t}},\n\t\t\t{Key: \"errorCode\", Value: 391},\n\t\t}},\n\t})\n\n\trequire.NoError(t, res.Err(), \"failed to set failpoint\")\n\n\tcoll := client.Database(\"test\").Collection(\"test\")\n\n\t_, err = coll.Find(context.Background(), bson.D{})\n\trequire.NoError(t, err, \"failed executing Find\")\n\tcountMutex.Lock()\n\tdefer countMutex.Unlock()\n\trequire.Equal(t, 2, callbackCount, \"expected callback count to be 2\")\n\trequire.NoError(t, callbackFailed, \"callback failed\")\n}\n\nfunc TestHuman_3_1_UsesSpeculativeAuth(t *testing.T) {\n\tif os.Getenv(\"OIDC_ENV\") != \"\" {\n\t\tt.Skip(\"Skipping: test only runs when OIDC_ENV is empty\")\n\t}\n\n\tadminClient, err := connectAdminClient()\n\trequire.NoError(t, err, \"failed connecting admin client\")\n\tt.Cleanup(func() { _ = adminClient.Disconnect(context.Background()) })\n\n\tclient, err := connectWithHumanCB(uriSingle, func(context.Context, *options.OIDCArgs) (*options.OIDCCredential, error) {\n\t\t// the callback should not even be called due to spec auth.\n\t\treturn &options.OIDCCredential{}, nil\n\t})\n\n\trequire.NoError(t, err, \"failed connecting client\")\n\tt.Cleanup(func() { _ = client.Disconnect(context.Background()) })\n\n\t// We deviate from the Prose test since the failPoint on find with no error code does not seem to\n\t// work. Rather we put an access token in the cache to force speculative auth.\n\ttokenFile := tokenFile(\"test_user1\")\n\taccessToken, err := os.ReadFile(tokenFile)\n\trequire.NoError(t, err, \"failed reading token file\")\n\tclientElem := reflect.ValueOf(client).Elem()\n\tauthenticatorField := clientElem.FieldByName(\"authenticator\")\n\tauthenticatorField = reflect.NewAt(\n\t\tauthenticatorField.Type(),\n\t\tunsafe.Pointer(authenticatorField.UnsafeAddr())).Elem()\n\t// This is the only usage of the x packages in the test, showing the public interface is\n\t// correct.\n\tauthenticatorField.Interface().(*auth.OIDCAuthenticator).SetAccessToken(string(accessToken))\n\n\tres := adminClient.Database(\"admin\").RunCommand(context.Background(), bson.D{\n\t\t{Key: \"configureFailPoint\", Value: \"failCommand\"},\n\t\t{Key: \"mode\", Value: bson.D{\n\t\t\t{Key: \"times\", Value: 1},\n\t\t}},\n\t\t{Key: \"data\", Value: bson.D{\n\t\t\t{Key: \"failCommands\", Value: bson.A{\n\t\t\t\t\"saslStart\",\n\t\t\t}},\n\t\t\t{Key: \"errorCode\", Value: 18},\n\t\t}},\n\t})\n\n\trequire.NoError(t, res.Err(), \"failed to set failpoint\")\n\n\tcoll := client.Database(\"test\").Collection(\"test\")\n\t_, err = coll.Find(context.Background(), bson.D{})\n\trequire.NoError(t, err, \"failed executing Find\")\n}\n\nfunc TestHuman_3_2_DoesNotUseSpeculativeAuth(t *testing.T) {\n\tif os.Getenv(\"OIDC_ENV\") != \"\" {\n\t\tt.Skip(\"Skipping: test only runs when OIDC_ENV is empty\")\n\t}\n\n\tvar callbackFailed error\n\n\tadminClient, err := connectAdminClient()\n\trequire.NoError(t, err, \"failed connecting admin client\")\n\tt.Cleanup(func() { _ = adminClient.Disconnect(context.Background()) })\n\n\tclient, err := connectWithHumanCB(uriSingle, func(context.Context, *options.OIDCArgs) (*options.OIDCCredential, error) {\n\t\texpiry := time.Now().Add(time.Hour)\n\t\ttokenFile := tokenFile(\"test_user1\")\n\t\taccessToken, err := os.ReadFile(tokenFile)\n\t\tif err != nil {\n\t\t\tcallbackFailed = fmt.Errorf(\"failed reading token file: %v\", err)\n\t\t}\n\t\treturn &options.OIDCCredential{\n\t\t\tAccessToken:  string(accessToken),\n\t\t\tExpiresAt:    &expiry,\n\t\t\tRefreshToken: nil,\n\t\t}, nil\n\t})\n\trequire.NoError(t, err, \"failed connecting client\")\n\tt.Cleanup(func() { _ = client.Disconnect(context.Background()) })\n\n\tres := adminClient.Database(\"admin\").RunCommand(context.Background(), bson.D{\n\t\t{Key: \"configureFailPoint\", Value: \"failCommand\"},\n\t\t{Key: \"mode\", Value: bson.D{\n\t\t\t{Key: \"times\", Value: 1},\n\t\t}},\n\t\t{Key: \"data\", Value: bson.D{\n\t\t\t{Key: \"failCommands\", Value: bson.A{\n\t\t\t\t\"saslStart\",\n\t\t\t}},\n\t\t\t{Key: \"errorCode\", Value: 18},\n\t\t}},\n\t})\n\n\trequire.NoError(t, res.Err(), \"failed to set failpoint\")\n\n\tcoll := client.Database(\"test\").Collection(\"test\")\n\n\t_, err = coll.Find(context.Background(), bson.D{})\n\trequire.Error(t, err, \"Find should have failed\")\n\trequire.NoError(t, callbackFailed, \"callback failed\")\n}\n\nfunc TestHuman_4_1_ReauthenticationSucceeds(t *testing.T) {\n\tif os.Getenv(\"OIDC_ENV\") != \"\" {\n\t\tt.Skip(\"Skipping: test only runs when OIDC_ENV is empty\")\n\t}\n\n\tcallbackCount := 0\n\tvar callbackFailed error\n\tcountMutex := sync.Mutex{}\n\n\tadminClient, err := connectAdminClient()\n\trequire.NoError(t, err, \"failed connecting admin client\")\n\tt.Cleanup(func() { _ = adminClient.Disconnect(context.Background()) })\n\n\tclearChannels := func(s chan *event.CommandStartedEvent, succ chan *event.CommandSucceededEvent, f chan *event.CommandFailedEvent) {\n\t\tfor len(s) > 0 {\n\t\t\t<-s\n\t\t}\n\t\tfor len(succ) > 0 {\n\t\t\t<-succ\n\t\t}\n\t\tfor len(f) > 0 {\n\t\t\t<-f\n\t\t}\n\t}\n\n\tstarted := make(chan *event.CommandStartedEvent, 100)\n\tsucceeded := make(chan *event.CommandSucceededEvent, 100)\n\tfailed := make(chan *event.CommandFailedEvent, 100)\n\n\tmonitor := event.CommandMonitor{\n\t\tStarted: func(_ context.Context, e *event.CommandStartedEvent) {\n\t\t\tstarted <- e\n\t\t},\n\t\tSucceeded: func(_ context.Context, e *event.CommandSucceededEvent) {\n\t\t\tsucceeded <- e\n\t\t},\n\t\tFailed: func(_ context.Context, e *event.CommandFailedEvent) {\n\t\t\tfailed <- e\n\t\t},\n\t}\n\n\tclient, err := connectWithHumanCBAndMonitor(uriSingle, func(_ context.Context, _ *options.OIDCArgs) (*options.OIDCCredential, error) {\n\t\tcountMutex.Lock()\n\t\tdefer countMutex.Unlock()\n\t\tcallbackCount++\n\t\texpiry := time.Now().Add(time.Hour)\n\t\ttokenFile := tokenFile(\"test_user1\")\n\t\taccessToken, err := os.ReadFile(tokenFile)\n\t\tif err != nil {\n\t\t\tcallbackFailed = fmt.Errorf(\"failed reading token file: %v\", err)\n\t\t}\n\t\treturn &options.OIDCCredential{\n\t\t\tAccessToken:  string(accessToken),\n\t\t\tExpiresAt:    &expiry,\n\t\t\tRefreshToken: nil,\n\t\t}, nil\n\t}, &monitor)\n\trequire.NoError(t, err, \"failed connecting client\")\n\tt.Cleanup(func() { _ = client.Disconnect(context.Background()) })\n\tclearChannels(started, succeeded, failed)\n\n\tcoll := client.Database(\"test\").Collection(\"test\")\n\t_, err = coll.Find(context.Background(), bson.D{})\n\trequire.NoError(t, err, \"failed executing Find\")\n\tcountMutex.Lock()\n\trequire.Equal(t, 1, callbackCount, \"expected callback count to be 1\")\n\tcountMutex.Unlock()\n\tclearChannels(started, succeeded, failed)\n\n\tres := adminClient.Database(\"admin\").RunCommand(context.Background(), bson.D{\n\t\t{Key: \"configureFailPoint\", Value: \"failCommand\"},\n\t\t{Key: \"mode\", Value: bson.D{\n\t\t\t{Key: \"times\", Value: 1},\n\t\t}},\n\t\t{Key: \"data\", Value: bson.D{\n\t\t\t{Key: \"failCommands\", Value: bson.A{\n\t\t\t\t\"find\",\n\t\t\t}},\n\t\t\t{Key: \"errorCode\", Value: 391},\n\t\t}},\n\t})\n\n\trequire.NoError(t, res.Err(), \"failed setting failpoint\")\n\n\t_, err = coll.Find(context.Background(), bson.D{})\n\trequire.NoError(t, err, \"failed executing Find\")\n\tcountMutex.Lock()\n\trequire.Equal(t, 2, callbackCount, \"expected callback count to be 2\")\n\tcountMutex.Unlock()\n\n\trequire.Equal(t, 2, len(started), \"expected 2 finds started\")\n\tfor len(started) > 0 {\n\t\tste := <-started\n\t\trequire.Equal(t, \"find\", ste.CommandName, \"found unexpected command started\")\n\t}\n\trequire.Equal(t, 1, len(succeeded), \"expected 1 find to succeed\")\n\tfor len(succeeded) > 0 {\n\t\tsue := <-succeeded\n\t\trequire.Equal(t, \"find\", sue.CommandName, \"found unexpected command succeeded\")\n\t}\n\trequire.Equal(t, 1, len(failed), \"expected 1 find to fail\")\n\tfor len(failed) > 0 {\n\t\tfe := <-failed\n\t\trequire.Equal(t, \"find\", fe.CommandName, \"found unexpected command failed\")\n\t}\n\n\trequire.NoError(t, callbackFailed, \"callback failed\")\n}\n\nfunc TestHuman_4_2_ReauthenticationSucceedsNoRefreshToken(t *testing.T) {\n\tif os.Getenv(\"OIDC_ENV\") != \"\" {\n\t\tt.Skip(\"Skipping: test only runs when OIDC_ENV is empty\")\n\t}\n\n\tcallbackCount := 0\n\tvar callbackFailed error\n\tcountMutex := sync.Mutex{}\n\n\tadminClient, err := connectAdminClient()\n\trequire.NoError(t, err, \"failed connecting admin client\")\n\tt.Cleanup(func() { _ = adminClient.Disconnect(context.Background()) })\n\n\tclient, err := connectWithHumanCB(uriSingle, func(context.Context, *options.OIDCArgs) (*options.OIDCCredential, error) {\n\t\tcountMutex.Lock()\n\t\tdefer countMutex.Unlock()\n\t\tcallbackCount++\n\t\texpiry := time.Now().Add(time.Hour)\n\t\ttokenFile := tokenFile(\"test_user1\")\n\t\taccessToken, err := os.ReadFile(tokenFile)\n\t\tif err != nil {\n\t\t\tcallbackFailed = fmt.Errorf(\"failed reading token file: %v\", err)\n\t\t}\n\t\treturn &options.OIDCCredential{\n\t\t\tAccessToken:  string(accessToken),\n\t\t\tExpiresAt:    &expiry,\n\t\t\tRefreshToken: nil,\n\t\t}, nil\n\t})\n\trequire.NoError(t, err, \"failed connecting client\")\n\tt.Cleanup(func() { _ = client.Disconnect(context.Background()) })\n\n\tcoll := client.Database(\"test\").Collection(\"test\")\n\n\t_, err = coll.Find(context.Background(), bson.D{})\n\trequire.NoError(t, err, \"failed executing Find\")\n\n\tcountMutex.Lock()\n\trequire.Equal(t, 1, callbackCount, \"expected callback count to be 1\")\n\tcountMutex.Unlock()\n\n\tres := adminClient.Database(\"admin\").RunCommand(context.Background(), bson.D{\n\t\t{Key: \"configureFailPoint\", Value: \"failCommand\"},\n\t\t{Key: \"mode\", Value: bson.D{\n\t\t\t{Key: \"times\", Value: 1},\n\t\t}},\n\t\t{Key: \"data\", Value: bson.D{\n\t\t\t{Key: \"failCommands\", Value: bson.A{\n\t\t\t\t\"find\",\n\t\t\t}},\n\t\t\t{Key: \"errorCode\", Value: 391},\n\t\t}},\n\t})\n\n\trequire.NoError(t, res.Err(), \"failed to set failpoint\")\n\n\t_, err = coll.Find(context.Background(), bson.D{})\n\trequire.NoError(t, err, \"failed executing Find\")\n\n\tcountMutex.Lock()\n\trequire.Equal(t, 2, callbackCount, \"expected callback count to be 2\")\n\tcountMutex.Unlock()\n\trequire.NoError(t, callbackFailed, \"callback failed\")\n}\n\nfunc TestHuman_4_3_ReauthenticationSucceedsAfterRefreshFails(t *testing.T) {\n\tif os.Getenv(\"OIDC_ENV\") != \"\" {\n\t\tt.Skip(\"Skipping: test only runs when OIDC_ENV is empty\")\n\t}\n\n\tcallbackCount := 0\n\tvar callbackFailed error\n\tcountMutex := sync.Mutex{}\n\n\tadminClient, err := connectAdminClient()\n\trequire.NoError(t, err, \"failed connecting admin client\")\n\tt.Cleanup(func() { _ = adminClient.Disconnect(context.Background()) })\n\n\tclient, err := connectWithHumanCB(uriSingle, func(context.Context, *options.OIDCArgs) (*options.OIDCCredential, error) {\n\t\tcountMutex.Lock()\n\t\tdefer countMutex.Unlock()\n\t\tcallbackCount++\n\t\texpiry := time.Now().Add(time.Hour)\n\t\ttokenFile := tokenFile(\"test_user1\")\n\t\taccessToken, err := os.ReadFile(tokenFile)\n\t\tif err != nil {\n\t\t\tcallbackFailed = fmt.Errorf(\"failed reading token file: %v\", err)\n\t\t}\n\t\trefreshToken := \"bad token\"\n\t\treturn &options.OIDCCredential{\n\t\t\tAccessToken:  string(accessToken),\n\t\t\tExpiresAt:    &expiry,\n\t\t\tRefreshToken: &refreshToken,\n\t\t}, nil\n\t})\n\trequire.NoError(t, err, \"failed connecting client\")\n\tt.Cleanup(func() { _ = client.Disconnect(context.Background()) })\n\n\tcoll := client.Database(\"test\").Collection(\"test\")\n\n\t_, err = coll.Find(context.Background(), bson.D{})\n\trequire.NoError(t, err, \"failed executing Find\")\n\n\tcountMutex.Lock()\n\trequire.Equal(t, 1, callbackCount, \"expected callback count to be 1\")\n\tcountMutex.Unlock()\n\n\tres := adminClient.Database(\"admin\").RunCommand(context.Background(), bson.D{\n\t\t{Key: \"configureFailPoint\", Value: \"failCommand\"},\n\t\t{Key: \"mode\", Value: bson.D{\n\t\t\t{Key: \"times\", Value: 1},\n\t\t}},\n\t\t{Key: \"data\", Value: bson.D{\n\t\t\t{Key: \"failCommands\", Value: bson.A{\n\t\t\t\t\"find\",\n\t\t\t}},\n\t\t\t{Key: \"errorCode\", Value: 391},\n\t\t}},\n\t})\n\n\trequire.NoError(t, res.Err(), \"failed to set failpoint\")\n\n\t_, err = coll.Find(context.Background(), bson.D{})\n\trequire.NoError(t, err, \"failed executing Find\")\n\n\tcountMutex.Lock()\n\trequire.Equal(t, 2, callbackCount, \"expected callback count to be 2\")\n\tcountMutex.Unlock()\n\trequire.NoError(t, callbackFailed, \"callback failed\")\n}\n\nfunc TestHuman_4_4_ReauthenticationFails(t *testing.T) {\n\tif os.Getenv(\"OIDC_ENV\") != \"\" {\n\t\tt.Skip(\"Skipping: test only runs when OIDC_ENV is empty\")\n\t}\n\n\tcallbackCount := 0\n\tvar callbackFailed error\n\tcountMutex := sync.Mutex{}\n\n\tadminClient, err := connectAdminClient()\n\trequire.NoError(t, err, \"failed connecting admin client\")\n\tt.Cleanup(func() { _ = adminClient.Disconnect(context.Background()) })\n\n\tclient, err := connectWithHumanCB(uriSingle, func(context.Context, *options.OIDCArgs) (*options.OIDCCredential, error) {\n\t\tcountMutex.Lock()\n\t\tdefer countMutex.Unlock()\n\t\tcallbackCount++\n\t\tbadToken := \"bad token\"\n\t\texpiry := time.Now().Add(time.Hour)\n\t\tif callbackCount == 1 {\n\t\t\ttokenFile := tokenFile(\"test_user1\")\n\t\t\taccessToken, err := os.ReadFile(tokenFile)\n\t\t\tif err != nil {\n\t\t\t\tcallbackFailed = fmt.Errorf(\"failed reading token file: %v\", err)\n\t\t\t}\n\t\t\treturn &options.OIDCCredential{\n\t\t\t\tAccessToken:  string(accessToken),\n\t\t\t\tExpiresAt:    &expiry,\n\t\t\t\tRefreshToken: &badToken,\n\t\t\t}, nil\n\t\t}\n\t\treturn &options.OIDCCredential{\n\t\t\tAccessToken:  badToken,\n\t\t\tExpiresAt:    &expiry,\n\t\t\tRefreshToken: &badToken,\n\t\t}, fmt.Errorf(\"failed to refresh token\")\n\t})\n\trequire.NoError(t, err, \"failed connecting client\")\n\tt.Cleanup(func() { _ = client.Disconnect(context.Background()) })\n\n\tcoll := client.Database(\"test\").Collection(\"test\")\n\n\t_, err = coll.Find(context.Background(), bson.D{})\n\trequire.NoError(t, err, \"failed executing Find\")\n\n\tcountMutex.Lock()\n\trequire.Equal(t, 1, callbackCount, \"expected callback count to be 1\")\n\tcountMutex.Unlock()\n\n\tres := adminClient.Database(\"admin\").RunCommand(context.Background(), bson.D{\n\t\t{Key: \"configureFailPoint\", Value: \"failCommand\"},\n\t\t{Key: \"mode\", Value: bson.D{\n\t\t\t{Key: \"times\", Value: 1},\n\t\t}},\n\t\t{Key: \"data\", Value: bson.D{\n\t\t\t{Key: \"failCommands\", Value: bson.A{\n\t\t\t\t\"find\",\n\t\t\t}},\n\t\t\t{Key: \"errorCode\", Value: 391},\n\t\t}},\n\t})\n\n\trequire.NoError(t, res.Err(), \"failed to set failpoint\")\n\n\t_, err = coll.Find(context.Background(), bson.D{})\n\trequire.Error(t, err, \"Find should have failed\")\n\n\tcountMutex.Lock()\n\trequire.Equal(t, 3, callbackCount, \"expected callback count to be 3\")\n\tcountMutex.Unlock()\n\trequire.NoError(t, callbackFailed, \"callback failed\")\n}\n\nfunc TestMachine_5_1_AzureWithNoUsername(t *testing.T) {\n\tif os.Getenv(\"OIDC_ENV\") != \"azure\" {\n\t\tt.Skip(\"Skipping: test only runs when OIDC_ENV=azure\")\n\t}\n\n\topts := options.Client().ApplyURI(uriSingle)\n\trequire.NotNil(t, opts, \"failed parsing uri: %q\", uriSingle)\n\tclient, err := mongo.Connect(opts)\n\trequire.NoError(t, err, \"failed connecting client\")\n\tt.Cleanup(func() { _ = client.Disconnect(context.Background()) })\n\n\tcoll := client.Database(\"test\").Collection(\"test\")\n\n\t_, err = coll.Find(context.Background(), bson.D{})\n\trequire.NoError(t, err, \"failed executing Find\")\n}\n\nfunc TestMachine_5_2_AzureWithBadUsername(t *testing.T) {\n\tif os.Getenv(\"OIDC_ENV\") != \"azure\" {\n\t\tt.Skip(\"Skipping: test only runs when OIDC_ENV=azure\")\n\t}\n\n\topts := options.Client().ApplyURI(uriSingle)\n\n\trequire.NotNil(t, opts, \"failed parsing uri: %q\", uriSingle)\n\trequire.False(\n\t\tt,\n\t\topts.Auth == nil || opts.Auth.AuthMechanism != \"MONGODB-OIDC\",\n\t\t\"expected URI to contain MONGODB-OIDC auth information\",\n\t)\n\n\topts.Auth.Username = \"bad\"\n\n\tclient, err := mongo.Connect(opts)\n\trequire.NoError(t, err, \"failed connecting client\")\n\tt.Cleanup(func() { _ = client.Disconnect(context.Background()) })\n\n\tcoll := client.Database(\"test\").Collection(\"test\")\n\n\t_, err = coll.Find(context.Background(), bson.D{})\n\trequire.Error(t, err, \"Find should have failed\")\n}\n\nfunc TestMachine_6_1_GCPWithNoUsername(t *testing.T) {\n\tif os.Getenv(\"OIDC_ENV\") != \"gcp\" {\n\t\tt.Skip(\"Skipping: test only runs when OIDC_ENV=gcp\")\n\t}\n\n\topts := options.Client().ApplyURI(uriSingle)\n\tclient, err := mongo.Connect(opts)\n\trequire.NoError(t, err, \"failed connecting client\")\n\tt.Cleanup(func() { _ = client.Disconnect(context.Background()) })\n\n\tcoll := client.Database(\"test\").Collection(\"test\")\n\n\t_, err = coll.Find(context.Background(), bson.D{})\n\trequire.NoError(t, err, \"failed executing Find\")\n}\n\n// TestMachine_K8s tests the \"k8s\" Kubernetes OIDC environment. There is no specified\n// prose test for \"k8s\", so this test simply checks that you can run a \"find\"\n// with the provided conn string.\nfunc TestMachine_K8s(t *testing.T) {\n\tif os.Getenv(\"OIDC_ENV\") != \"k8s\" {\n\t\tt.Skip(\"Skipping: test only runs when OIDC_ENV=k8s\")\n\t}\n\n\trequire.True(t, strings.Contains(uriSingle, \"ENVIRONMENT:k8s\"), \"expected MONGODB_URI_SINGLE to specify ENVIRONMENT:k8s for Kubernetes test\")\n\n\topts := options.Client().ApplyURI(uriSingle)\n\tclient, err := mongo.Connect(opts)\n\trequire.NoError(t, err, \"failed connecting client\")\n\tt.Cleanup(func() { _ = client.Disconnect(context.Background()) })\n\n\tcoll := client.Database(\"test\").Collection(\"test\")\n\n\t_, err = coll.Find(context.Background(), bson.D{})\n\trequire.NoError(t, err, \"failed executing Find\")\n}\n"
  },
  {
    "path": "internal/testutil/reflect.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage testutil\n\nimport (\n\t\"reflect\"\n\t\"unsafe\"\n)\n\n// getUnexportedField uses reflection and unsafe to read an unexported field\n// named fieldName from a struct pointer v, and returns it as interface{}.\nfunc getUnexportedField(v any, fieldName string) any {\n\trv := reflect.ValueOf(v)\n\tif rv.Kind() != reflect.Ptr || rv.IsNil() {\n\t\tpanic(\"GetUnexportedField: v must be a non-nil pointer to struct\")\n\t}\n\n\telem := rv.Elem()\n\tif elem.Kind() != reflect.Struct {\n\t\tpanic(\"GetUnexportedField: v must point to a struct\")\n\t}\n\n\tfield := elem.FieldByName(fieldName)\n\tif !field.IsValid() {\n\t\tpanic(\"GetUnexportedField: no such field: \" + fieldName)\n\t}\n\n\t// Rebuild a settable value pointing to this unexported field.\n\tfield = reflect.NewAt(field.Type(), unsafe.Pointer(field.UnsafeAddr())).Elem()\n\treturn field.Interface()\n}\n\n// GetUnexportedFieldAs is a generic wrapper around GetUnexportedField that\n// returns the field value with the correct type.\nfunc GetUnexportedFieldAs[T any](v any, fieldName string) T {\n\treturn getUnexportedField(v, fieldName).(T)\n}\n"
  },
  {
    "path": "internal/uuid/uuid.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage uuid\n\nimport (\n\t\"encoding/hex\"\n\t\"io\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/randutil\"\n)\n\n// UUID represents a UUID.\ntype UUID [16]byte\n\n// A source is a UUID generator that reads random values from a io.Reader.\n// It should be safe to use from multiple goroutines.\ntype source struct {\n\trandom io.Reader\n}\n\n// new returns a random UUIDv4 with bytes read from the source's random number generator.\nfunc (s *source) new() (UUID, error) {\n\tvar uuid UUID\n\t_, err := io.ReadFull(s.random, uuid[:])\n\tif err != nil {\n\t\treturn UUID{}, err\n\t}\n\tuuid[6] = (uuid[6] & 0x0f) | 0x40 // Version 4\n\tuuid[8] = (uuid[8] & 0x3f) | 0x80 // Variant is 10\n\treturn uuid, nil\n}\n\n// newSource returns a source that uses a pseudo-random number generator in reandutil package.\n// It is intended to be used to initialize the package-global UUID generator.\nfunc newSource() *source {\n\treturn &source{\n\t\trandom: randutil.NewLockedRand(),\n\t}\n}\n\n// globalSource is a package-global pseudo-random UUID generator.\nvar globalSource = newSource()\n\n// New returns a random UUIDv4. It uses a global pseudo-random number generator in randutil\n// at package initialization.\n//\n// New should not be used to generate cryptographically-secure random UUIDs.\nfunc New() (UUID, error) {\n\treturn globalSource.new()\n}\n\nfunc (uuid UUID) String() string {\n\tvar str [36]byte\n\thex.Encode(str[:], uuid[:4])\n\tstr[8] = '-'\n\thex.Encode(str[9:13], uuid[4:6])\n\tstr[13] = '-'\n\thex.Encode(str[14:18], uuid[6:8])\n\tstr[18] = '-'\n\thex.Encode(str[19:23], uuid[8:10])\n\tstr[23] = '-'\n\thex.Encode(str[24:], uuid[10:])\n\treturn string(str[:])\n}\n"
  },
  {
    "path": "internal/uuid/uuid_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2022-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage uuid\n\nimport (\n\t\"sync\"\n\t\"testing\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/israce\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/require\"\n)\n\n// GODRIVER-2349\n// Test that initializing many package-global UUID sources concurrently never leads to any duplicate\n// UUIDs being generated.\nfunc TestGlobalSource(t *testing.T) {\n\tt.Parallel()\n\n\tt.Run(\"exp rand 1 UUID x 1,000,000 goroutines using a global source\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tif israce.Enabled {\n\t\t\tt.Skip(\"skipping as race detector is enabled and test exceeds 8128 goroutine limit\")\n\t\t}\n\n\t\t// Read a UUID from each of 1,000,000 goroutines and assert that there is never a duplicate value.\n\t\tconst iterations = 1e6\n\t\tuuids := new(sync.Map)\n\t\tvar wg sync.WaitGroup\n\t\twg.Add(iterations)\n\t\tfor i := 0; i < iterations; i++ {\n\t\t\tgo func(i int) {\n\t\t\t\tdefer wg.Done()\n\t\t\t\tuuid, err := New()\n\t\t\t\trequire.NoError(t, err, \"new() error\")\n\t\t\t\t_, ok := uuids.Load(uuid)\n\t\t\t\trequire.Falsef(t, ok, \"New returned a duplicate UUID on iteration %d: %v\", i, uuid)\n\t\t\t\tuuids.Store(uuid, true)\n\t\t\t}(i)\n\t\t}\n\t\twg.Wait()\n\t})\n\tt.Run(\"exp rand 1 UUID x 1,000,000 goroutines each initializing a new source\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tif israce.Enabled {\n\t\t\tt.Skip(\"skipping as race detector is enabled and test exceeds 8128 goroutine limit\")\n\t\t}\n\n\t\t// Read a UUID from each of 1,000,000 goroutines and assert that there is never a duplicate value.\n\t\t// The goal is to emulate many separate Go driver processes starting at the same time and\n\t\t// initializing the uuid package at the same time.\n\t\tconst iterations = 1e6\n\t\tuuids := new(sync.Map)\n\t\tvar wg sync.WaitGroup\n\t\twg.Add(iterations)\n\t\tfor i := 0; i < iterations; i++ {\n\t\t\tgo func(i int) {\n\t\t\t\tdefer wg.Done()\n\t\t\t\ts := newSource()\n\t\t\t\tuuid, err := s.new()\n\t\t\t\trequire.NoError(t, err, \"new() error\")\n\t\t\t\t_, ok := uuids.Load(uuid)\n\t\t\t\trequire.Falsef(t, ok, \"New returned a duplicate UUID on iteration %d: %v\", i, uuid)\n\t\t\t\tuuids.Store(uuid, true)\n\t\t\t}(i)\n\t\t}\n\t\twg.Wait()\n\t})\n\tt.Run(\"exp rand 1,000 UUIDs x 1,000 goroutines each initializing a new source\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\t// Read 1,000 UUIDs from each goroutine and assert that there is never a duplicate value, either\n\t\t// from the same goroutine or from separate goroutines.\n\t\tconst iterations = 1000\n\t\tuuids := new(sync.Map)\n\t\tvar wg sync.WaitGroup\n\t\twg.Add(iterations)\n\t\tfor i := 0; i < iterations; i++ {\n\t\t\tgo func(i int) {\n\t\t\t\tdefer wg.Done()\n\t\t\t\ts := newSource()\n\t\t\t\tfor j := 0; j < iterations; j++ {\n\t\t\t\t\tuuid, err := s.new()\n\t\t\t\t\trequire.NoError(t, err, \"new() error\")\n\t\t\t\t\t_, ok := uuids.Load(uuid)\n\t\t\t\t\trequire.Falsef(t, ok, \"goroutine %d returned a duplicate UUID on iteration %d: %v\", i, j, uuid)\n\t\t\t\t\tuuids.Store(uuid, true)\n\t\t\t\t}\n\t\t\t}(i)\n\t\t}\n\t\twg.Wait()\n\t})\n}\n\nfunc BenchmarkUuidGeneration(b *testing.B) {\n\tfor i := 0; i < b.N; i++ {\n\t\t_, err := New()\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "mongo/address/addr.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\n// Package address provides structured representations of network addresses.\npackage address\n\nimport (\n\t\"net\"\n\t\"strings\"\n)\n\nconst defaultPort = \"27017\"\n\n// Address is a network address. It can either be an IP address or a DNS name.\ntype Address string\n\n// Network is the network protocol for this address. In most cases this will be\n// \"tcp\" or \"unix\".\nfunc (a Address) Network() string {\n\tif strings.HasSuffix(string(a), \"sock\") {\n\t\treturn \"unix\"\n\t}\n\treturn \"tcp\"\n}\n\n// String is the canonical version of this address, e.g. localhost:27017,\n// 1.2.3.4:27017, example.com:27017.\nfunc (a Address) String() string {\n\ts := string(a)\n\tif a.Network() != \"unix\" {\n\t\t// TODO: unicode case folding?\n\t\ts = strings.ToLower(string(a))\n\t}\n\tif len(s) == 0 {\n\t\treturn \"\"\n\t}\n\tif a.Network() != \"unix\" {\n\t\t_, _, err := net.SplitHostPort(s)\n\t\tif err != nil && strings.Contains(err.Error(), \"missing port in address\") {\n\t\t\ts += \":\" + defaultPort\n\t\t}\n\t}\n\n\treturn s\n}\n\n// Canonicalize creates a canonicalized address.\nfunc (a Address) Canonicalize() Address {\n\treturn Address(a.String())\n}\n"
  },
  {
    "path": "mongo/address/addr_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage address\n\nimport (\n\t\"testing\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/require\"\n)\n\nfunc TestAddress_String(t *testing.T) {\n\ttests := []struct {\n\t\tin       string\n\t\texpected string\n\t}{\n\t\t{\"a\", \"a:27017\"},\n\t\t{\"A\", \"a:27017\"},\n\t\t{\"A:27017\", \"a:27017\"},\n\t\t{\"a:27017\", \"a:27017\"},\n\t\t{\"a.sock\", \"a.sock\"},\n\t\t{\"A.sock\", \"A.sock\"},\n\t}\n\n\tfor _, test := range tests {\n\t\tt.Run(test.in, func(t *testing.T) {\n\t\t\trequire.Equal(t, Address(test.in).String(), test.expected)\n\t\t})\n\t}\n}\n\nfunc TestAddress_Canonicalize(t *testing.T) {\n\ttests := []struct {\n\t\tin       string\n\t\texpected string\n\t}{\n\t\t{\"a\", \"a:27017\"},\n\t\t{\"A\", \"a:27017\"},\n\t\t{\"A:27017\", \"a:27017\"},\n\t\t{\"a:27017\", \"a:27017\"},\n\t\t{\"a.sock\", \"a.sock\"},\n\t}\n\n\tfor _, test := range tests {\n\t\tt.Run(test.in, func(t *testing.T) {\n\t\t\trequire.Equal(t, Address(test.in).Canonicalize(), Address(test.expected))\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "mongo/background_context.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage mongo\n\nimport \"context\"\n\n// backgroundContext is an implementation of the context.Context interface that wraps a child Context. Value requests\n// are forwarded to the child Context but the Done and Err functions are overridden to ensure the new context does not\n// time out or get cancelled.\ntype backgroundContext struct {\n\tcontext.Context\n\tchildValuesCtx context.Context\n}\n\n// newBackgroundContext creates a new Context whose behavior matches that of context.Background(), but Value calls are\n// forwarded to the provided ctx parameter. If ctx is nil, context.Background() is returned.\nfunc newBackgroundContext(ctx context.Context) context.Context {\n\tif ctx == nil {\n\t\treturn context.Background()\n\t}\n\n\treturn &backgroundContext{\n\t\tContext:        context.Background(),\n\t\tchildValuesCtx: ctx,\n\t}\n}\n\nfunc (b *backgroundContext) Value(key any) any {\n\treturn b.childValuesCtx.Value(key)\n}\n"
  },
  {
    "path": "mongo/background_context_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage mongo\n\nimport (\n\t\"context\"\n\t\"testing\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n)\n\nfunc TestBackgroundContext(t *testing.T) {\n\tt.Run(\"newBackgroundContext accepts a nil child\", func(t *testing.T) {\n\t\tctx := newBackgroundContext(nil)\n\t\tassert.Equal(t, context.Background(), ctx, \"expected context.Background() for a nil child\")\n\t})\n\tt.Run(\"Value requests are forwarded\", func(t *testing.T) {\n\t\t// Tests the Value function.\n\n\t\ttype ctxKey struct{}\n\t\texpectedVal := \"value\"\n\t\tchildCtx := context.WithValue(context.Background(), ctxKey{}, expectedVal)\n\n\t\tctx := newBackgroundContext(childCtx)\n\t\tgotVal, ok := ctx.Value(ctxKey{}).(string)\n\t\tassert.True(t, ok, \"expected context to contain a string value for ctxKey\")\n\t\tassert.Equal(t, expectedVal, gotVal, \"expected value for ctxKey to be %q, got %q\", expectedVal, gotVal)\n\t})\n\tt.Run(\"background context does not have a deadline\", func(t *testing.T) {\n\t\t// Tests the Deadline function.\n\n\t\tchildCtx, cancel := context.WithTimeout(context.Background(), time.Second)\n\t\tdefer cancel()\n\n\t\tctx := newBackgroundContext(childCtx)\n\t\tdeadline, ok := ctx.Deadline()\n\t\tassert.False(t, ok, \"expected context to have no deadline, but got %v\", deadline)\n\t})\n\tt.Run(\"background context cannot be cancelled\", func(t *testing.T) {\n\t\t// Tests the Done and Err functions.\n\n\t\tchildCtx, cancel := context.WithCancel(context.Background())\n\t\tcancel()\n\n\t\tctx := newBackgroundContext(childCtx)\n\t\tselect {\n\t\tcase <-ctx.Done():\n\t\t\tt.Fatalf(\"expected context to not expire, but Done channel had a value; ctx error: %v\", ctx.Err())\n\t\tdefault:\n\t\t}\n\t\tassert.Nil(t, ctx.Err(), \"expected context error to be nil, got %v\", ctx.Err())\n\t})\n}\n"
  },
  {
    "path": "mongo/batch_cursor.go",
    "content": "// Copyright (C) MongoDB, Inc. 2022-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage mongo\n\nimport (\n\t\"context\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver\"\n)\n\n// batchCursor is the interface implemented by types that can provide batches of document results.\n// The Cursor type is built on top of this type.\ntype batchCursor interface {\n\t// ID returns the ID of the cursor.\n\tID() int64\n\n\t// Next returns true if there is a batch available.\n\tNext(context.Context) bool\n\n\t// Batch will return a DocumentSequence for the current batch of documents. The returned\n\t// DocumentSequence is only valid until the next call to Next or Close.\n\tBatch() *bsoncore.Iterator\n\n\t// Server returns a pointer to the cursor's server.\n\tServer() driver.Server\n\n\t// Err returns the last error encountered.\n\tErr() error\n\n\t// Close closes the cursor.\n\tClose(context.Context) error\n\n\t// SetBatchSize is a modifier function used to adjust the batch size of\n\t// the cursor that implements it.\n\tSetBatchSize(int32)\n\n\t// SetMaxAwaitTime will set the maximum amount of time the server will allow\n\t// the operations to execute. The server will error if this field is set\n\t// but the cursor is not configured with awaitData=true.\n\t//\n\t// The time.Duration value passed by this setter will be converted and\n\t// rounded down to the nearest millisecond.\n\tSetMaxAwaitTime(time.Duration)\n\n\t// SetComment will set a user-configurable comment that can be used to\n\t// identify the operation in server logs.\n\tSetComment(any)\n\n\t// MaxAwaitTime returns the maximum amount of time the server will allow\n\t// the operations to execute. This is only valid for tailable awaitData\n\t// cursors.\n\tMaxAwaitTime() *time.Duration\n}\n\n// changeStreamCursor is the interface implemented by batch cursors that also provide the functionality for retrieving\n// a postBatchResumeToken from commands and allows for the cursor to be killed rather than closed\ntype changeStreamCursor interface {\n\tbatchCursor\n\t// PostBatchResumeToken returns the latest seen post batch resume token.\n\tPostBatchResumeToken() bsoncore.Document\n\n\t// KillCursor kills cursor on server without closing batch cursor\n\tKillCursor(context.Context) error\n}\n"
  },
  {
    "path": "mongo/bson_helpers_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage mongo\n\nimport (\n\t\"testing\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n)\n\n// compare expected and actual BSON documents. comparison succeeds if actual contains each element in expected.\nfunc compareDocuments(t *testing.T, expected, actual bson.Raw) {\n\tt.Helper()\n\n\teElems, err := expected.Elements()\n\tassert.Nil(t, err, \"error getting expected elements: %v\", err)\n\n\tfor _, e := range eElems {\n\t\teKey := e.Key()\n\t\taVal, err := actual.LookupErr(eKey)\n\t\tassert.Nil(t, err, \"key %s not found in result\", e.Key())\n\t\tcompareBsonValues(t, eKey, e.Value(), aVal)\n\t}\n}\n\nfunc numberFromValue(t *testing.T, val bson.RawValue) int64 {\n\tswitch val.Type {\n\tcase bson.TypeInt32:\n\t\treturn int64(val.Int32())\n\tcase bson.TypeInt64:\n\t\treturn val.Int64()\n\tcase bson.TypeDouble:\n\t\treturn int64(val.Double())\n\tdefault:\n\t\tt.Fatalf(\"unexpected type for number: %v\", val.Type)\n\t}\n\n\treturn 0\n}\n\nfunc compareNumberValues(t *testing.T, key string, expected, actual bson.RawValue) {\n\teInt := numberFromValue(t, expected)\n\taInt := numberFromValue(t, actual)\n\tassert.Equal(t, eInt, aInt, \"value mismatch for key %s; expected %v, got %v\", key, expected, actual)\n}\n\n// compare BSON values and fail if they are not equal. the key parameter is used for error strings.\n// if the expected value is a numeric type (int32, int64, or double) and the value is 42, the function only asserts that\n// the actual value is non-null.\nfunc compareBsonValues(t *testing.T, key string, expected, actual bson.RawValue) {\n\tt.Helper()\n\n\tswitch expected.Type {\n\tcase bson.TypeInt32, bson.TypeInt64, bson.TypeDouble:\n\t\tcompareNumberValues(t, key, expected, actual)\n\tcase bson.TypeEmbeddedDocument:\n\t\tcompareDocuments(t, expected.Document(), actual.Document())\n\tcase bson.TypeArray:\n\t\tcompareDocuments(t, bson.Raw(expected.Array()), bson.Raw(actual.Array()))\n\tdefault:\n\t\tassert.Equal(t, expected.Value, actual.Value,\n\t\t\t\"value mismatch for key %v; expected %v, got %v\", key, expected.Value, actual.Value)\n\t}\n}\n"
  },
  {
    "path": "mongo/bulk_write.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage mongo\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/writeconcern\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/description\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/operation\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/session\"\n)\n\ntype bulkWriteBatch struct {\n\tmodels   []WriteModel\n\tcanRetry bool\n\tindexes  []int\n}\n\n// bulkWrite performs a bulkwrite operation\ntype bulkWrite struct {\n\tcomment                  any\n\tordered                  *bool\n\tbypassDocumentValidation *bool\n\tmodels                   []WriteModel\n\tsession                  *session.Client\n\tcollection               *Collection\n\tselector                 description.ServerSelector\n\twriteConcern             *writeconcern.WriteConcern\n\tresult                   BulkWriteResult\n\tlet                      any\n\trawData                  *bool\n\tadditionalCmd            bson.D\n}\n\nfunc (bw *bulkWrite) execute(ctx context.Context) error {\n\tordered := true\n\tif bw.ordered != nil {\n\t\tordered = *bw.ordered\n\t}\n\n\tbatches := createBatches(bw.models, ordered)\n\tbw.result = BulkWriteResult{\n\t\tUpsertedIDs: make(map[int64]any),\n\t}\n\n\tbwErr := BulkWriteException{\n\t\tWriteErrors: make([]BulkWriteError, 0),\n\t}\n\n\tvar lastErr error\n\tcontinueOnError := !ordered\n\tfor _, batch := range batches {\n\t\tif len(batch.models) == 0 {\n\t\t\tcontinue\n\t\t}\n\n\t\tbatchRes, batchErr, err := bw.runBatch(ctx, batch)\n\n\t\tbw.mergeResults(batchRes)\n\n\t\tbwErr.WriteConcernError = batchErr.WriteConcernError\n\t\tbwErr.Labels = append(bwErr.Labels, batchErr.Labels...)\n\n\t\tbwErr.WriteErrors = append(bwErr.WriteErrors, batchErr.WriteErrors...)\n\n\t\tcommandErrorOccurred := err != nil && !errors.Is(err, driver.ErrUnacknowledgedWrite)\n\t\twriteErrorOccurred := len(batchErr.WriteErrors) > 0 || batchErr.WriteConcernError != nil\n\t\tif !continueOnError && (commandErrorOccurred || writeErrorOccurred) {\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\n\t\t\treturn bwErr\n\t\t}\n\n\t\tif err != nil {\n\t\t\tlastErr = err\n\t\t}\n\t}\n\n\tbw.result.MatchedCount -= bw.result.UpsertedCount\n\n\trr, err := processWriteError(lastErr)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tbw.result.Acknowledged = rr.isAcknowledged()\n\n\tif len(bwErr.WriteErrors) > 0 || bwErr.WriteConcernError != nil {\n\t\treturn bwErr\n\t}\n\treturn nil\n}\n\nfunc (bw *bulkWrite) runBatch(ctx context.Context, batch bulkWriteBatch) (BulkWriteResult, BulkWriteException, error) {\n\tbatchRes := BulkWriteResult{\n\t\tUpsertedIDs: make(map[int64]any),\n\t}\n\tbatchErr := BulkWriteException{}\n\n\tvar writeErrors []driver.WriteError\n\tswitch batch.models[0].(type) {\n\tcase *InsertOneModel:\n\t\tres, err := bw.runInsert(ctx, batch)\n\t\tif err != nil {\n\t\t\tvar writeErr driver.WriteCommandError\n\t\t\tif !errors.As(err, &writeErr) {\n\t\t\t\treturn BulkWriteResult{}, batchErr, err\n\t\t\t}\n\t\t\twriteErrors = writeErr.WriteErrors\n\t\t\tbatchErr.Labels = writeErr.Labels\n\t\t\tbatchErr.WriteConcernError = convertDriverWriteConcernError(writeErr.WriteConcernError)\n\t\t}\n\t\tbatchRes.InsertedCount = res.N\n\tcase *DeleteOneModel, *DeleteManyModel:\n\t\tres, err := bw.runDelete(ctx, batch)\n\t\tif err != nil {\n\t\t\tvar writeErr driver.WriteCommandError\n\t\t\tif !errors.As(err, &writeErr) {\n\t\t\t\treturn BulkWriteResult{}, batchErr, err\n\t\t\t}\n\t\t\twriteErrors = writeErr.WriteErrors\n\t\t\tbatchErr.Labels = writeErr.Labels\n\t\t\tbatchErr.WriteConcernError = convertDriverWriteConcernError(writeErr.WriteConcernError)\n\t\t}\n\t\tbatchRes.DeletedCount = res.N\n\tcase *ReplaceOneModel, *UpdateOneModel, *UpdateManyModel:\n\t\tres, err := bw.runUpdate(ctx, batch)\n\t\tif err != nil {\n\t\t\tvar writeErr driver.WriteCommandError\n\t\t\tif !errors.As(err, &writeErr) {\n\t\t\t\treturn BulkWriteResult{}, batchErr, err\n\t\t\t}\n\t\t\twriteErrors = writeErr.WriteErrors\n\t\t\tbatchErr.Labels = writeErr.Labels\n\t\t\tbatchErr.WriteConcernError = convertDriverWriteConcernError(writeErr.WriteConcernError)\n\t\t}\n\t\tbatchRes.MatchedCount = res.N\n\t\tbatchRes.ModifiedCount = res.NModified\n\t\tbatchRes.UpsertedCount = int64(len(res.Upserted))\n\t\tfor _, upsert := range res.Upserted {\n\t\t\tbatchRes.UpsertedIDs[int64(batch.indexes[upsert.Index])] = upsert.ID\n\t\t}\n\t}\n\n\tbatchErr.WriteErrors = make([]BulkWriteError, 0, len(writeErrors))\n\tconvWriteErrors := writeErrorsFromDriverWriteErrors(writeErrors)\n\tfor _, we := range convWriteErrors {\n\t\trequest := batch.models[we.Index]\n\t\twe.Index = batch.indexes[we.Index]\n\t\tbatchErr.WriteErrors = append(batchErr.WriteErrors, BulkWriteError{\n\t\t\tWriteError: we,\n\t\t\tRequest:    request,\n\t\t})\n\t}\n\treturn batchRes, batchErr, nil\n}\n\nfunc (bw *bulkWrite) runInsert(ctx context.Context, batch bulkWriteBatch) (insertResult, error) {\n\tdocs := make([]bsoncore.Document, len(batch.models))\n\tfor i, model := range batch.models {\n\t\tconverted := model.(*InsertOneModel)\n\t\tdoc, err := marshal(converted.Document, bw.collection.bsonOpts, bw.collection.registry)\n\t\tif err != nil {\n\t\t\treturn insertResult{}, err\n\t\t}\n\t\tdoc, _, err = ensureID(doc, bson.NilObjectID, bw.collection.bsonOpts, bw.collection.registry)\n\t\tif err != nil {\n\t\t\treturn insertResult{}, err\n\t\t}\n\n\t\tdocs[i] = doc\n\t}\n\n\top := insert{\n\t\tdocuments:     docs,\n\t\tsession:       bw.session,\n\t\twriteConcern:  bw.writeConcern,\n\t\tmonitor:       bw.collection.client.monitor,\n\t\tselector:      bw.selector,\n\t\tclock:         bw.collection.client.clock,\n\t\tdatabase:      bw.collection.db.name,\n\t\tcollection:    bw.collection.name,\n\t\tdeployment:    bw.collection.client.deployment,\n\t\tcrypt:         bw.collection.client.cryptFLE,\n\t\tserverAPI:     bw.collection.client.serverAPI,\n\t\ttimeout:       bw.collection.client.timeout,\n\t\tlogger:        bw.collection.client.logger,\n\t\tauthenticator: bw.collection.client.authenticator,\n\t}\n\n\tif bw.comment != nil {\n\t\tcomment, err := marshalValue(bw.comment, bw.collection.bsonOpts, bw.collection.registry)\n\t\tif err != nil {\n\t\t\treturn op.Result(), err\n\t\t}\n\t\top.comment = comment\n\t}\n\tif bw.bypassDocumentValidation != nil && *bw.bypassDocumentValidation {\n\t\top.bypassDocumentValidation = bw.bypassDocumentValidation\n\t}\n\tif bw.ordered != nil {\n\t\top.ordered = bw.ordered\n\t}\n\n\tretry := driver.RetryNone\n\tif bw.collection.client.retryWrites && batch.canRetry {\n\t\tretry = driver.RetryOncePerCommand\n\t}\n\top.retry = &retry\n\n\tif bw.rawData != nil {\n\t\top.rawData = bw.rawData\n\t}\n\tif len(bw.additionalCmd) > 0 {\n\t\top.additionalCmd = bw.additionalCmd\n\t}\n\n\terr := op.Execute(ctx)\n\n\treturn op.Result(), err\n}\n\nfunc (bw *bulkWrite) runDelete(ctx context.Context, batch bulkWriteBatch) (operation.DeleteResult, error) {\n\tdocs := make([]bsoncore.Document, len(batch.models))\n\tvar i int\n\tvar hasHint bool\n\n\tfor _, model := range batch.models {\n\t\tvar doc bsoncore.Document\n\t\tvar err error\n\n\t\tswitch converted := model.(type) {\n\t\tcase *DeleteOneModel:\n\t\t\tdoc, err = createDeleteDoc(\n\t\t\t\tconverted.Filter,\n\t\t\t\tconverted.Collation,\n\t\t\t\tconverted.Hint,\n\t\t\t\ttrue,\n\t\t\t\tbw.collection.bsonOpts,\n\t\t\t\tbw.collection.registry)\n\t\t\thasHint = hasHint || (converted.Hint != nil)\n\t\tcase *DeleteManyModel:\n\t\t\tdoc, err = createDeleteDoc(\n\t\t\t\tconverted.Filter,\n\t\t\t\tconverted.Collation,\n\t\t\t\tconverted.Hint,\n\t\t\t\tfalse,\n\t\t\t\tbw.collection.bsonOpts,\n\t\t\t\tbw.collection.registry)\n\t\t\thasHint = hasHint || (converted.Hint != nil)\n\t\t}\n\n\t\tif err != nil {\n\t\t\treturn operation.DeleteResult{}, err\n\t\t}\n\n\t\tdocs[i] = doc\n\t\ti++\n\t}\n\n\top := operation.NewDelete(docs...).\n\t\tSession(bw.session).WriteConcern(bw.writeConcern).CommandMonitor(bw.collection.client.monitor).\n\t\tServerSelector(bw.selector).ClusterClock(bw.collection.client.clock).\n\t\tDatabase(bw.collection.db.name).Collection(bw.collection.name).\n\t\tDeployment(bw.collection.client.deployment).Crypt(bw.collection.client.cryptFLE).Hint(hasHint).\n\t\tServerAPI(bw.collection.client.serverAPI).Timeout(bw.collection.client.timeout).\n\t\tLogger(bw.collection.client.logger).Authenticator(bw.collection.client.authenticator)\n\tif bw.comment != nil {\n\t\tcomment, err := marshalValue(bw.comment, bw.collection.bsonOpts, bw.collection.registry)\n\t\tif err != nil {\n\t\t\treturn op.Result(), err\n\t\t}\n\t\top.Comment(comment)\n\t}\n\tif bw.let != nil {\n\t\tlet, err := marshal(bw.let, bw.collection.bsonOpts, bw.collection.registry)\n\t\tif err != nil {\n\t\t\treturn operation.DeleteResult{}, err\n\t\t}\n\t\top = op.Let(let)\n\t}\n\tif bw.ordered != nil {\n\t\top = op.Ordered(*bw.ordered)\n\t}\n\tretry := driver.RetryNone\n\tif bw.collection.client.retryWrites && batch.canRetry {\n\t\tretry = driver.RetryOncePerCommand\n\t}\n\top = op.Retry(retry)\n\n\tif bw.rawData != nil {\n\t\top.RawData(*bw.rawData)\n\t}\n\n\terr := op.Execute(ctx)\n\n\treturn op.Result(), err\n}\n\nfunc createDeleteDoc(\n\tfilter any,\n\tcollation *options.Collation,\n\thint any,\n\tdeleteOne bool,\n\tbsonOpts *options.BSONOptions,\n\tregistry *bson.Registry,\n) (bsoncore.Document, error) {\n\tif filter == nil {\n\t\treturn nil, fmt.Errorf(\"delete filter cannot be nil\")\n\t}\n\tf, err := marshal(filter, bsonOpts, registry)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar limit int32\n\tif deleteOne {\n\t\tlimit = 1\n\t}\n\tdidx, doc := bsoncore.AppendDocumentStart(nil)\n\tdoc = bsoncore.AppendDocumentElement(doc, \"q\", f)\n\tdoc = bsoncore.AppendInt32Element(doc, \"limit\", limit)\n\tif collation != nil {\n\t\tdoc = bsoncore.AppendDocumentElement(doc, \"collation\", toDocument(collation))\n\t}\n\tif hint != nil {\n\t\tif isUnorderedMap(hint) {\n\t\t\treturn nil, ErrMapForOrderedArgument{\"hint\"}\n\t\t}\n\t\thintVal, err := marshalValue(hint, bsonOpts, registry)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tdoc = bsoncore.AppendValueElement(doc, \"hint\", hintVal)\n\t}\n\tdoc, _ = bsoncore.AppendDocumentEnd(doc, didx)\n\n\treturn doc, nil\n}\n\nfunc (bw *bulkWrite) runUpdate(ctx context.Context, batch bulkWriteBatch) (operation.UpdateResult, error) {\n\tdocs := make([]bsoncore.Document, len(batch.models))\n\tvar hasHint bool\n\tvar hasArrayFilters bool\n\tfor i, model := range batch.models {\n\t\tvar doc bsoncore.Document\n\t\tvar err error\n\n\t\tswitch converted := model.(type) {\n\t\tcase *ReplaceOneModel:\n\t\t\tdoc, err = updateDoc{\n\t\t\t\tfilter:    converted.Filter,\n\t\t\t\tupdate:    converted.Replacement,\n\t\t\t\thint:      converted.Hint,\n\t\t\t\tsort:      converted.Sort,\n\t\t\t\tcollation: converted.Collation,\n\t\t\t\tupsert:    converted.Upsert,\n\t\t\t}.marshal(bw.collection.bsonOpts, bw.collection.registry)\n\t\t\thasHint = hasHint || (converted.Hint != nil)\n\t\tcase *UpdateOneModel:\n\t\t\tdoc, err = updateDoc{\n\t\t\t\tfilter:         converted.Filter,\n\t\t\t\tupdate:         converted.Update,\n\t\t\t\thint:           converted.Hint,\n\t\t\t\tsort:           converted.Sort,\n\t\t\t\tarrayFilters:   converted.ArrayFilters,\n\t\t\t\tcollation:      converted.Collation,\n\t\t\t\tupsert:         converted.Upsert,\n\t\t\t\tcheckDollarKey: true,\n\t\t\t}.marshal(bw.collection.bsonOpts, bw.collection.registry)\n\t\t\thasHint = hasHint || (converted.Hint != nil)\n\t\t\thasArrayFilters = hasArrayFilters || (converted.ArrayFilters != nil)\n\t\tcase *UpdateManyModel:\n\t\t\tdoc, err = updateDoc{\n\t\t\t\tfilter:         converted.Filter,\n\t\t\t\tupdate:         converted.Update,\n\t\t\t\thint:           converted.Hint,\n\t\t\t\tarrayFilters:   converted.ArrayFilters,\n\t\t\t\tcollation:      converted.Collation,\n\t\t\t\tupsert:         converted.Upsert,\n\t\t\t\tmulti:          true,\n\t\t\t\tcheckDollarKey: true,\n\t\t\t}.marshal(bw.collection.bsonOpts, bw.collection.registry)\n\t\t\thasHint = hasHint || (converted.Hint != nil)\n\t\t\thasArrayFilters = hasArrayFilters || (converted.ArrayFilters != nil)\n\t\t}\n\t\tif err != nil {\n\t\t\treturn operation.UpdateResult{}, err\n\t\t}\n\n\t\tdocs[i] = doc\n\t}\n\n\top := operation.NewUpdate(docs...).\n\t\tSession(bw.session).WriteConcern(bw.writeConcern).CommandMonitor(bw.collection.client.monitor).\n\t\tServerSelector(bw.selector).ClusterClock(bw.collection.client.clock).\n\t\tDatabase(bw.collection.db.name).Collection(bw.collection.name).\n\t\tDeployment(bw.collection.client.deployment).Crypt(bw.collection.client.cryptFLE).Hint(hasHint).\n\t\tArrayFilters(hasArrayFilters).ServerAPI(bw.collection.client.serverAPI).\n\t\tTimeout(bw.collection.client.timeout).Logger(bw.collection.client.logger).\n\t\tAuthenticator(bw.collection.client.authenticator)\n\tif bw.comment != nil {\n\t\tcomment, err := marshalValue(bw.comment, bw.collection.bsonOpts, bw.collection.registry)\n\t\tif err != nil {\n\t\t\treturn op.Result(), err\n\t\t}\n\t\top.Comment(comment)\n\t}\n\tif bw.let != nil {\n\t\tlet, err := marshal(bw.let, bw.collection.bsonOpts, bw.collection.registry)\n\t\tif err != nil {\n\t\t\treturn operation.UpdateResult{}, err\n\t\t}\n\t\top = op.Let(let)\n\t}\n\tif bw.ordered != nil {\n\t\top = op.Ordered(*bw.ordered)\n\t}\n\tif bw.bypassDocumentValidation != nil && *bw.bypassDocumentValidation {\n\t\top = op.BypassDocumentValidation(*bw.bypassDocumentValidation)\n\t}\n\tretry := driver.RetryNone\n\tif bw.collection.client.retryWrites && batch.canRetry {\n\t\tretry = driver.RetryOncePerCommand\n\t}\n\top = op.Retry(retry)\n\n\tif bw.rawData != nil {\n\t\top.RawData(*bw.rawData)\n\t}\n\tif len(bw.additionalCmd) > 0 {\n\t\top.AdditionalCmd(bw.additionalCmd)\n\t}\n\n\terr := op.Execute(ctx)\n\n\treturn op.Result(), err\n}\n\ntype updateDoc struct {\n\tfilter         any\n\tupdate         any\n\thint           any\n\tsort           any\n\tarrayFilters   []any\n\tcollation      *options.Collation\n\tupsert         *bool\n\tmulti          bool\n\tcheckDollarKey bool\n}\n\nfunc (doc updateDoc) marshal(bsonOpts *options.BSONOptions, registry *bson.Registry) (bsoncore.Document, error) {\n\tif doc.filter == nil {\n\t\treturn nil, fmt.Errorf(\"update filter cannot be nil\")\n\t}\n\tf, err := marshal(doc.filter, bsonOpts, registry)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tuidx, updateDoc := bsoncore.AppendDocumentStart(nil)\n\tupdateDoc = bsoncore.AppendDocumentElement(updateDoc, \"q\", f)\n\n\tu, err := marshalUpdateValue(doc.update, bsonOpts, registry, doc.checkDollarKey)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tupdateDoc = bsoncore.AppendValueElement(updateDoc, \"u\", u)\n\n\tif doc.multi {\n\t\tupdateDoc = bsoncore.AppendBooleanElement(updateDoc, \"multi\", doc.multi)\n\t}\n\tif doc.sort != nil {\n\t\tif isUnorderedMap(doc.sort) {\n\t\t\treturn nil, ErrMapForOrderedArgument{\"sort\"}\n\t\t}\n\t\ts, err := marshal(doc.sort, bsonOpts, registry)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tupdateDoc = bsoncore.AppendDocumentElement(updateDoc, \"sort\", s)\n\t}\n\n\tif doc.arrayFilters != nil {\n\t\treg := registry\n\t\tarr, err := marshalValue(doc.arrayFilters, bsonOpts, reg)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tupdateDoc = bsoncore.AppendArrayElement(updateDoc, \"arrayFilters\", arr.Data)\n\t}\n\n\tif doc.collation != nil {\n\t\tupdateDoc = bsoncore.AppendDocumentElement(updateDoc, \"collation\", bsoncore.Document(toDocument(doc.collation)))\n\t}\n\n\tif doc.upsert != nil {\n\t\tupdateDoc = bsoncore.AppendBooleanElement(updateDoc, \"upsert\", *doc.upsert)\n\t}\n\n\tif doc.hint != nil {\n\t\tif isUnorderedMap(doc.hint) {\n\t\t\treturn nil, ErrMapForOrderedArgument{\"hint\"}\n\t\t}\n\t\thintVal, err := marshalValue(doc.hint, bsonOpts, registry)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tupdateDoc = bsoncore.AppendValueElement(updateDoc, \"hint\", hintVal)\n\t}\n\n\tupdateDoc, _ = bsoncore.AppendDocumentEnd(updateDoc, uidx)\n\treturn updateDoc, nil\n}\n\nfunc createBatches(models []WriteModel, ordered bool) []bulkWriteBatch {\n\tif ordered {\n\t\treturn createOrderedBatches(models)\n\t}\n\n\tbatches := make([]bulkWriteBatch, 5)\n\tbatches[insertCommand].canRetry = true\n\tbatches[deleteOneCommand].canRetry = true\n\tbatches[updateOneCommand].canRetry = true\n\n\t// TODO(GODRIVER-1157): fix batching once operation retryability is fixed\n\tfor i, model := range models {\n\t\tswitch model.(type) {\n\t\tcase *InsertOneModel:\n\t\t\tbatches[insertCommand].models = append(batches[insertCommand].models, model)\n\t\t\tbatches[insertCommand].indexes = append(batches[insertCommand].indexes, i)\n\t\tcase *DeleteOneModel:\n\t\t\tbatches[deleteOneCommand].models = append(batches[deleteOneCommand].models, model)\n\t\t\tbatches[deleteOneCommand].indexes = append(batches[deleteOneCommand].indexes, i)\n\t\tcase *DeleteManyModel:\n\t\t\tbatches[deleteManyCommand].models = append(batches[deleteManyCommand].models, model)\n\t\t\tbatches[deleteManyCommand].indexes = append(batches[deleteManyCommand].indexes, i)\n\t\tcase *ReplaceOneModel, *UpdateOneModel:\n\t\t\tbatches[updateOneCommand].models = append(batches[updateOneCommand].models, model)\n\t\t\tbatches[updateOneCommand].indexes = append(batches[updateOneCommand].indexes, i)\n\t\tcase *UpdateManyModel:\n\t\t\tbatches[updateManyCommand].models = append(batches[updateManyCommand].models, model)\n\t\t\tbatches[updateManyCommand].indexes = append(batches[updateManyCommand].indexes, i)\n\t\t}\n\t}\n\n\treturn batches\n}\n\nfunc createOrderedBatches(models []WriteModel) []bulkWriteBatch {\n\tvar batches []bulkWriteBatch\n\tvar prevKind writeCommandKind = -1\n\ti := -1 // batch index\n\n\tfor ind, model := range models {\n\t\tvar createNewBatch bool\n\t\tvar canRetry bool\n\t\tvar newKind writeCommandKind\n\n\t\t// TODO(GODRIVER-1157): fix batching once operation retryability is fixed\n\t\tswitch model.(type) {\n\t\tcase *InsertOneModel:\n\t\t\tcreateNewBatch = prevKind != insertCommand\n\t\t\tcanRetry = true\n\t\t\tnewKind = insertCommand\n\t\tcase *DeleteOneModel:\n\t\t\tcreateNewBatch = prevKind != deleteOneCommand\n\t\t\tcanRetry = true\n\t\t\tnewKind = deleteOneCommand\n\t\tcase *DeleteManyModel:\n\t\t\tcreateNewBatch = prevKind != deleteManyCommand\n\t\t\tnewKind = deleteManyCommand\n\t\tcase *ReplaceOneModel, *UpdateOneModel:\n\t\t\tcreateNewBatch = prevKind != updateOneCommand\n\t\t\tcanRetry = true\n\t\t\tnewKind = updateOneCommand\n\t\tcase *UpdateManyModel:\n\t\t\tcreateNewBatch = prevKind != updateManyCommand\n\t\t\tnewKind = updateManyCommand\n\t\t}\n\n\t\tif createNewBatch {\n\t\t\tbatches = append(batches, bulkWriteBatch{\n\t\t\t\tmodels:   []WriteModel{model},\n\t\t\t\tcanRetry: canRetry,\n\t\t\t\tindexes:  []int{ind},\n\t\t\t})\n\t\t\ti++\n\t\t} else {\n\t\t\tbatches[i].models = append(batches[i].models, model)\n\t\t\tif !canRetry {\n\t\t\t\tbatches[i].canRetry = false // don't make it true if it was already false\n\t\t\t}\n\t\t\tbatches[i].indexes = append(batches[i].indexes, ind)\n\t\t}\n\n\t\tprevKind = newKind\n\t}\n\n\treturn batches\n}\n\nfunc (bw *bulkWrite) mergeResults(newResult BulkWriteResult) {\n\tbw.result.InsertedCount += newResult.InsertedCount\n\tbw.result.MatchedCount += newResult.MatchedCount\n\tbw.result.ModifiedCount += newResult.ModifiedCount\n\tbw.result.DeletedCount += newResult.DeletedCount\n\tbw.result.UpsertedCount += newResult.UpsertedCount\n\n\tfor index, upsertID := range newResult.UpsertedIDs {\n\t\tbw.result.UpsertedIDs[index] = upsertID\n\t}\n}\n\n// WriteCommandKind is the type of command represented by a Write\ntype writeCommandKind int8\n\n// These constants represent the valid types of write commands.\nconst (\n\tinsertCommand writeCommandKind = iota\n\tupdateOneCommand\n\tupdateManyCommand\n\tdeleteOneCommand\n\tdeleteManyCommand\n)\n"
  },
  {
    "path": "mongo/bulk_write_models.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage mongo\n\nimport (\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n)\n\n// WriteModel is an interface implemented by models that can be used in a BulkWrite operation. Each WriteModel\n// represents a write.\n//\n// This interface is implemented by InsertOneModel, DeleteOneModel, DeleteManyModel, ReplaceOneModel, UpdateOneModel,\n// and UpdateManyModel. Custom implementations of this interface must not be used.\ntype WriteModel interface {\n\twriteModel()\n}\n\n// InsertOneModel is used to insert a single document in a BulkWrite operation.\n//\n// See corresponding setter methods for documentation.\ntype InsertOneModel struct {\n\tDocument any\n}\n\n// NewInsertOneModel creates a new InsertOneModel.\nfunc NewInsertOneModel() *InsertOneModel {\n\treturn &InsertOneModel{}\n}\n\n// SetDocument specifies the document to be inserted. The document cannot be nil. If it does not have an _id field when\n// transformed into BSON, one will be added automatically to the marshalled document. The original document will not be\n// modified.\nfunc (iom *InsertOneModel) SetDocument(doc any) *InsertOneModel {\n\tiom.Document = doc\n\treturn iom\n}\n\nfunc (*InsertOneModel) writeModel() {}\n\n// DeleteOneModel is used to delete at most one document in a BulkWriteOperation.\n//\n// See corresponding setter methods for documentation.\ntype DeleteOneModel struct {\n\tFilter    any\n\tCollation *options.Collation\n\tHint      any\n}\n\n// NewDeleteOneModel creates a new DeleteOneModel.\nfunc NewDeleteOneModel() *DeleteOneModel {\n\treturn &DeleteOneModel{}\n}\n\n// SetFilter specifies a filter to use to select the document to delete. The filter must be a document containing query\n// operators. It cannot be nil. If the filter matches multiple documents, one will be selected from the matching\n// documents.\nfunc (dom *DeleteOneModel) SetFilter(filter any) *DeleteOneModel {\n\tdom.Filter = filter\n\treturn dom\n}\n\n// SetCollation specifies a collation to use for string comparisons. The default is nil, meaning no collation will be\n// used.\nfunc (dom *DeleteOneModel) SetCollation(collation *options.Collation) *DeleteOneModel {\n\tdom.Collation = collation\n\treturn dom\n}\n\n// SetHint specifies the index to use for the operation. This should either be\n// the index name as a string or the index specification as a document. This\n// option is only valid for MongoDB versions >= 4.4. Server versions < 4.4 will\n// return an error if this option is specified. The driver will return an error\n// if this option is specified during an unacknowledged write operation. The\n// driver will return an error if the hint parameter is a multi-key map. The\n// default value is nil, which means that no hint will be sent.\nfunc (dom *DeleteOneModel) SetHint(hint any) *DeleteOneModel {\n\tdom.Hint = hint\n\treturn dom\n}\n\nfunc (*DeleteOneModel) writeModel() {}\n\n// DeleteManyModel is used to delete multiple documents in a BulkWrite operation.\n//\n// See corresponding setter methods for documentation.\ntype DeleteManyModel struct {\n\tFilter    any\n\tCollation *options.Collation\n\tHint      any\n}\n\n// NewDeleteManyModel creates a new DeleteManyModel.\nfunc NewDeleteManyModel() *DeleteManyModel {\n\treturn &DeleteManyModel{}\n}\n\n// SetFilter specifies a filter to use to select documents to delete. The filter must be a document containing query\n// operators. It cannot be nil.\nfunc (dmm *DeleteManyModel) SetFilter(filter any) *DeleteManyModel {\n\tdmm.Filter = filter\n\treturn dmm\n}\n\n// SetCollation specifies a collation to use for string comparisons. The default is nil, meaning no collation will be\n// used.\nfunc (dmm *DeleteManyModel) SetCollation(collation *options.Collation) *DeleteManyModel {\n\tdmm.Collation = collation\n\treturn dmm\n}\n\n// SetHint specifies the index to use for the operation. This should either be\n// the index name as a string or the index specification as a document. This\n// option is only valid for MongoDB versions >= 4.4. Server versions < 4.4 will\n// return an error if this option is specified. The driver will return an error\n// if this option is specified during an unacknowledged write operation. The\n// driver will return an error if the hint parameter is a multi-key map. The\n// default value is nil, which means that no hint will be sent.\nfunc (dmm *DeleteManyModel) SetHint(hint any) *DeleteManyModel {\n\tdmm.Hint = hint\n\treturn dmm\n}\n\nfunc (*DeleteManyModel) writeModel() {}\n\n// ReplaceOneModel is used to replace at most one document in a BulkWrite operation.\n//\n// See corresponding setter methods for documentation.\ntype ReplaceOneModel struct {\n\tCollation   *options.Collation\n\tUpsert      *bool\n\tFilter      any\n\tReplacement any\n\tHint        any\n\tSort        any\n}\n\n// NewReplaceOneModel creates a new ReplaceOneModel.\nfunc NewReplaceOneModel() *ReplaceOneModel {\n\treturn &ReplaceOneModel{}\n}\n\n// SetHint specifies the index to use for the operation. This should either be\n// the index name as a string or the index specification as a document. This\n// option is only valid for MongoDB versions >= 4.2. Server versions < 4.2 will\n// return an error if this option is specified. The driver will return an error\n// if this option is specified during an unacknowledged write operation. The\n// driver will return an error if the hint parameter is a multi-key map. The\n// default value is nil, which means that no hint will be sent.\nfunc (rom *ReplaceOneModel) SetHint(hint any) *ReplaceOneModel {\n\trom.Hint = hint\n\treturn rom\n}\n\n// SetFilter specifies a filter to use to select the document to replace. The filter must be a document containing query\n// operators. It cannot be nil. If the filter matches multiple documents, one will be selected from the matching\n// documents.\nfunc (rom *ReplaceOneModel) SetFilter(filter any) *ReplaceOneModel {\n\trom.Filter = filter\n\treturn rom\n}\n\n// SetReplacement specifies a document that will be used to replace the selected document. It cannot be nil and cannot\n// contain any update operators (https://www.mongodb.com/docs/manual/reference/operator/update/).\nfunc (rom *ReplaceOneModel) SetReplacement(rep any) *ReplaceOneModel {\n\trom.Replacement = rep\n\treturn rom\n}\n\n// SetCollation specifies a collation to use for string comparisons. The default is nil, meaning no collation will be\n// used.\nfunc (rom *ReplaceOneModel) SetCollation(collation *options.Collation) *ReplaceOneModel {\n\trom.Collation = collation\n\treturn rom\n}\n\n// SetUpsert specifies whether or not the replacement document should be inserted if no document matching the filter is\n// found. If an upsert is performed, the _id of the upserted document can be retrieved from the UpsertedIDs field of the\n// BulkWriteResult.\nfunc (rom *ReplaceOneModel) SetUpsert(upsert bool) *ReplaceOneModel {\n\trom.Upsert = &upsert\n\treturn rom\n}\n\n// SetSort specifies which document the operation replaces if the query matches multiple documents. The first document\n// matched by the sort order will be replaced. This option is only valid for MongoDB versions >= 8.0. The sort parameter\n// is evaluated sequentially, so the driver will return an error if it is a multi-key map (which is unordeded). The\n// default value is nil.\nfunc (rom *ReplaceOneModel) SetSort(sort any) *ReplaceOneModel {\n\trom.Sort = sort\n\treturn rom\n}\n\nfunc (*ReplaceOneModel) writeModel() {}\n\n// UpdateOneModel is used to update at most one document in a BulkWrite operation.\n//\n// See corresponding setter methods for documentation.\ntype UpdateOneModel struct {\n\tCollation    *options.Collation\n\tUpsert       *bool\n\tFilter       any\n\tUpdate       any\n\tArrayFilters []any\n\tHint         any\n\tSort         any\n}\n\n// NewUpdateOneModel creates a new UpdateOneModel.\nfunc NewUpdateOneModel() *UpdateOneModel {\n\treturn &UpdateOneModel{}\n}\n\n// SetHint specifies the index to use for the operation. This should either be\n// the index name as a string or the index specification as a document. This\n// option is only valid for MongoDB versions >= 4.2. Server versions < 4.2 will\n// return an error if this option is specified. The driver will return an error\n// if this option is specified during an unacknowledged write operation. The\n// driver will return an error if the hint parameter is a multi-key map. The\n// default value is nil, which means that no hint will be sent.\nfunc (uom *UpdateOneModel) SetHint(hint any) *UpdateOneModel {\n\tuom.Hint = hint\n\treturn uom\n}\n\n// SetFilter specifies a filter to use to select the document to update. The filter must be a document containing query\n// operators. It cannot be nil. If the filter matches multiple documents, one will be selected from the matching\n// documents.\nfunc (uom *UpdateOneModel) SetFilter(filter any) *UpdateOneModel {\n\tuom.Filter = filter\n\treturn uom\n}\n\n// SetUpdate specifies the modifications to be made to the selected document. The value must be a document containing\n// update operators (https://www.mongodb.com/docs/manual/reference/operator/update/). It cannot be nil or empty.\nfunc (uom *UpdateOneModel) SetUpdate(update any) *UpdateOneModel {\n\tuom.Update = update\n\treturn uom\n}\n\n// SetArrayFilters specifies a set of filters to determine which elements should be modified when updating an array\n// field.\nfunc (uom *UpdateOneModel) SetArrayFilters(filters []any) *UpdateOneModel {\n\tuom.ArrayFilters = filters\n\treturn uom\n}\n\n// SetCollation specifies a collation to use for string comparisons. The default is nil, meaning no collation will be\n// used.\nfunc (uom *UpdateOneModel) SetCollation(collation *options.Collation) *UpdateOneModel {\n\tuom.Collation = collation\n\treturn uom\n}\n\n// SetUpsert specifies whether or not a new document should be inserted if no document matching the filter is found. If\n// an upsert is performed, the _id of the upserted document can be retrieved from the UpsertedIDs field of the\n// BulkWriteResult.\nfunc (uom *UpdateOneModel) SetUpsert(upsert bool) *UpdateOneModel {\n\tuom.Upsert = &upsert\n\treturn uom\n}\n\n// SetSort specifies which document the operation updates if the query matches multiple documents. The first document\n// matched by the sort order will be updated. This option is only valid for MongoDB versions >= 8.0. The sort parameter\n// is evaluated sequentially, so the driver will return an error if it is a multi-key map (which is unordeded). The\n// default value is nil.\nfunc (uom *UpdateOneModel) SetSort(sort any) *UpdateOneModel {\n\tuom.Sort = sort\n\treturn uom\n}\n\nfunc (*UpdateOneModel) writeModel() {}\n\n// UpdateManyModel is used to update multiple documents in a BulkWrite operation.\n//\n// See corresponding setter methods for documentation.\ntype UpdateManyModel struct {\n\tCollation    *options.Collation\n\tUpsert       *bool\n\tFilter       any\n\tUpdate       any\n\tArrayFilters []any\n\tHint         any\n}\n\n// NewUpdateManyModel creates a new UpdateManyModel.\nfunc NewUpdateManyModel() *UpdateManyModel {\n\treturn &UpdateManyModel{}\n}\n\n// SetHint specifies the index to use for the operation. This should either be\n// the index name as a string or the index specification as a document. This\n// option is only valid for MongoDB versions >= 4.2. Server versions < 4.2 will\n// return an error if this option is specified. The driver will return an error\n// if this option is specified during an unacknowledged write operation. The\n// driver will return an error if the hint parameter is a multi-key map. The\n// default value is nil, which means that no hint will be sent.\nfunc (umm *UpdateManyModel) SetHint(hint any) *UpdateManyModel {\n\tumm.Hint = hint\n\treturn umm\n}\n\n// SetFilter specifies a filter to use to select documents to update. The filter must be a document containing query\n// operators. It cannot be nil.\nfunc (umm *UpdateManyModel) SetFilter(filter any) *UpdateManyModel {\n\tumm.Filter = filter\n\treturn umm\n}\n\n// SetUpdate specifies the modifications to be made to the selected documents. The value must be a document containing\n// update operators (https://www.mongodb.com/docs/manual/reference/operator/update/). It cannot be nil or empty.\nfunc (umm *UpdateManyModel) SetUpdate(update any) *UpdateManyModel {\n\tumm.Update = update\n\treturn umm\n}\n\n// SetArrayFilters specifies a set of filters to determine which elements should be modified when updating an array\n// field.\nfunc (umm *UpdateManyModel) SetArrayFilters(filters []any) *UpdateManyModel {\n\tumm.ArrayFilters = filters\n\treturn umm\n}\n\n// SetCollation specifies a collation to use for string comparisons. The default is nil, meaning no collation will be\n// used.\nfunc (umm *UpdateManyModel) SetCollation(collation *options.Collation) *UpdateManyModel {\n\tumm.Collation = collation\n\treturn umm\n}\n\n// SetUpsert specifies whether or not a new document should be inserted if no document matching the filter is found. If\n// an upsert is performed, the _id of the upserted document can be retrieved from the UpsertedIDs field of the\n// BulkWriteResult.\nfunc (umm *UpdateManyModel) SetUpsert(upsert bool) *UpdateManyModel {\n\tumm.Upsert = &upsert\n\treturn umm\n}\n\nfunc (*UpdateManyModel) writeModel() {}\n"
  },
  {
    "path": "mongo/change_stream.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage mongo\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"reflect\"\n\t\"strconv\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/csot\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/driverutil\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/mongoutil\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/serverselector\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/readconcern\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/readpref\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/description\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/mnet\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/operation\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/session\"\n)\n\nvar (\n\t// ErrMissingResumeToken indicates that a change stream notification from the server did not contain a resume token.\n\tErrMissingResumeToken = errors.New(\"cannot provide resume functionality when the resume token is missing\")\n\t// ErrNilCursor indicates that the underlying cursor for the change stream is nil.\n\tErrNilCursor = errors.New(\"cursor is nil\")\n\n\tminResumableLabelWireVersion int32 = 9 // Wire version at which the server includes the resumable error label\n\tnetworkErrorLabel                  = \"NetworkError\"\n\tresumableErrorLabel                = \"ResumableChangeStreamError\"\n\terrorCursorNotFound          int32 = 43 // CursorNotFound error code\n\n\t// Allowlist of error codes that are considered resumable.\n\tresumableChangeStreamErrors = map[int32]struct{}{\n\t\t6:     {}, // HostUnreachable\n\t\t7:     {}, // HostNotFound\n\t\t89:    {}, // NetworkTimeout\n\t\t91:    {}, // ShutdownInProgress\n\t\t189:   {}, // PrimarySteppedDown\n\t\t262:   {}, // ExceededTimeLimit\n\t\t9001:  {}, // SocketException\n\t\t10107: {}, // NotPrimary\n\t\t11600: {}, // InterruptedAtShutdown\n\t\t11602: {}, // InterruptedDueToReplStateChange\n\t\t13435: {}, // NotPrimaryNoSecondaryOK\n\t\t13436: {}, // NotPrimaryOrSecondary\n\t\t63:    {}, // StaleShardVersion\n\t\t150:   {}, // StaleEpoch\n\t\t13388: {}, // StaleConfig\n\t\t234:   {}, // RetryChangeStream\n\t\t133:   {}, // FailedToSatisfyReadPreference\n\t}\n)\n\n// ChangeStream is used to iterate over a stream of events. Each event can be decoded into a Go type via the Decode\n// method or accessed as raw BSON via the Current field. This type is not goroutine safe and must not be used\n// concurrently by multiple goroutines. For more information about change streams, see\n// https://www.mongodb.com/docs/manual/changeStreams/.\ntype ChangeStream struct {\n\t// Current is the BSON bytes of the current event. This property is only valid until the next call to Next or\n\t// TryNext. If continued access is required, a copy must be made.\n\tCurrent bson.Raw\n\n\taggregate       *operation.Aggregate\n\tpipelineSlice   []bsoncore.Document\n\tpipelineOptions map[string]bsoncore.Value\n\tcursor          changeStreamCursor\n\tcursorOptions   driver.CursorOptions\n\tbatch           []bsoncore.Document\n\tresumeToken     bson.Raw\n\terr             error\n\tsess            *session.Client\n\tclient          *Client\n\tbsonOpts        *options.BSONOptions\n\tregistry        *bson.Registry\n\tstreamType      StreamType\n\toptions         *options.ChangeStreamOptions\n\tselector        description.ServerSelector\n\toperationTime   *bson.Timestamp\n\twireVersion     *description.VersionRange\n}\n\ntype changeStreamConfig struct {\n\treadConcern    *readconcern.ReadConcern\n\treadPreference *readpref.ReadPref\n\tclient         *Client\n\tbsonOpts       *options.BSONOptions\n\tregistry       *bson.Registry\n\tstreamType     StreamType\n\tcollectionName string\n\tdatabaseName   string\n\tcrypt          driver.Crypt\n}\n\nfunc newChangeStream(ctx context.Context, config changeStreamConfig, pipeline any,\n\topts ...options.Lister[options.ChangeStreamOptions],\n) (*ChangeStream, error) {\n\tif ctx == nil {\n\t\tctx = context.Background()\n\t}\n\n\tcursorOpts := config.client.createBaseCursorOptions()\n\n\tcursorOpts.MarshalValueEncoderFn = newEncoderFn(config.bsonOpts, config.registry)\n\n\targs, err := mongoutil.NewOptions[options.ChangeStreamOptions](opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tcs := &ChangeStream{\n\t\tclient:     config.client,\n\t\tbsonOpts:   config.bsonOpts,\n\t\tregistry:   config.registry,\n\t\tstreamType: config.streamType,\n\t\toptions:    args,\n\t\tselector: &serverselector.Composite{\n\t\t\tSelectors: []description.ServerSelector{\n\t\t\t\t&serverselector.ReadPref{ReadPref: config.readPreference},\n\t\t\t\t&serverselector.Latency{Latency: config.client.localThreshold},\n\t\t\t},\n\t\t},\n\t\tcursorOptions: cursorOpts,\n\t}\n\n\tcs.sess = sessionFromContext(ctx)\n\tif cs.sess == nil && cs.client.sessionPool != nil {\n\t\tcs.sess = session.NewImplicitClientSession(cs.client.sessionPool, cs.client.id)\n\t}\n\tif cs.err = cs.client.validSession(cs.sess); cs.err != nil {\n\t\tcloseImplicitSession(cs.sess)\n\t\treturn nil, cs.Err()\n\t}\n\n\tcs.aggregate = operation.NewAggregate(nil).\n\t\tReadPreference(config.readPreference).ReadConcern(config.readConcern).\n\t\tDeployment(cs.client.deployment).ClusterClock(cs.client.clock).\n\t\tCommandMonitor(cs.client.monitor).Session(cs.sess).ServerSelector(cs.selector).Retry(driver.RetryNone).\n\t\tServerAPI(cs.client.serverAPI).Crypt(config.crypt).Timeout(cs.client.timeout).\n\t\tAuthenticator(cs.client.authenticator)\n\n\tif cs.options.Collation != nil {\n\t\tcs.aggregate.Collation(bsoncore.Document(toDocument(cs.options.Collation)))\n\t}\n\tif cs.options.Comment != nil {\n\t\tcomment, err := marshalValue(cs.options.Comment, cs.bsonOpts, cs.registry)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tcs.aggregate.Comment(comment)\n\t\tcs.cursorOptions.Comment = comment\n\t}\n\tif cs.options.BatchSize != nil {\n\t\tcs.aggregate.BatchSize(*cs.options.BatchSize)\n\t\tcs.cursorOptions.BatchSize = *cs.options.BatchSize\n\t}\n\tif cs.options.MaxAwaitTime != nil {\n\t\tcs.cursorOptions.SetMaxAwaitTime(*cs.options.MaxAwaitTime)\n\t}\n\tif cs.options.Custom != nil {\n\t\t// Marshal all custom options before passing to the initial aggregate. Return\n\t\t// any errors from Marshaling.\n\t\tcustomOptions := make(map[string]bsoncore.Value)\n\t\tfor optionName, optionValue := range cs.options.Custom {\n\t\t\toptionValueBSON, err := marshalValue(optionValue, nil, cs.registry)\n\t\t\tif err != nil {\n\t\t\t\tcs.err = err\n\t\t\t\tcloseImplicitSession(cs.sess)\n\t\t\t\treturn nil, cs.Err()\n\t\t\t}\n\t\t\tcustomOptions[optionName] = optionValueBSON\n\t\t}\n\t\tcs.aggregate.CustomOptions(customOptions)\n\t}\n\tif cs.options.CustomPipeline != nil {\n\t\t// Marshal all custom pipeline options before building pipeline slice. Return\n\t\t// any errors from Marshaling.\n\t\tcs.pipelineOptions = make(map[string]bsoncore.Value)\n\t\tfor optionName, optionValue := range cs.options.CustomPipeline {\n\t\t\toptionValueBSON, err := marshalValue(optionValue, nil, cs.registry)\n\t\t\tif err != nil {\n\t\t\t\tcs.err = err\n\t\t\t\tcloseImplicitSession(cs.sess)\n\t\t\t\treturn nil, cs.Err()\n\t\t\t}\n\t\t\tcs.pipelineOptions[optionName] = optionValueBSON\n\t\t}\n\t}\n\n\tswitch cs.streamType {\n\tcase ClientStream:\n\t\tcs.aggregate.Database(\"admin\")\n\tcase DatabaseStream:\n\t\tcs.aggregate.Database(config.databaseName)\n\tcase CollectionStream:\n\t\tcs.aggregate.Collection(config.collectionName).Database(config.databaseName)\n\tdefault:\n\t\tcloseImplicitSession(cs.sess)\n\t\treturn nil, fmt.Errorf(\"must supply a valid StreamType in config, instead of %v\", cs.streamType)\n\t}\n\n\t// When starting a change stream, cache startAfter as the first resume token if it is set. If not, cache\n\t// resumeAfter. If neither is set, do not cache a resume token.\n\tresumeToken := cs.options.StartAfter\n\tif resumeToken == nil {\n\t\tresumeToken = cs.options.ResumeAfter\n\t}\n\tvar marshaledToken bson.Raw\n\tif resumeToken != nil {\n\t\tif marshaledToken, cs.err = bson.Marshal(resumeToken); cs.err != nil {\n\t\t\tcloseImplicitSession(cs.sess)\n\t\t\treturn nil, cs.Err()\n\t\t}\n\t}\n\tcs.resumeToken = marshaledToken\n\n\tif cs.err = cs.buildPipelineSlice(pipeline); cs.err != nil {\n\t\tcloseImplicitSession(cs.sess)\n\t\treturn nil, cs.Err()\n\t}\n\tvar pipelineArr bsoncore.Document\n\tpipelineArr, cs.err = cs.pipelineToBSON()\n\tcs.aggregate.Pipeline(pipelineArr)\n\n\tif cs.err = cs.executeOperation(ctx, false); cs.err != nil {\n\t\tcloseImplicitSession(cs.sess)\n\t\treturn nil, cs.Err()\n\t}\n\n\treturn cs, cs.Err()\n}\n\nfunc (cs *ChangeStream) createOperationDeployment(server driver.Server, connection *mnet.Connection) driver.Deployment {\n\treturn &changeStreamDeployment{\n\t\ttopologyKind: cs.client.deployment.Kind(),\n\t\tserver:       server,\n\t\tconn:         connection,\n\t}\n}\n\nfunc (cs *ChangeStream) executeOperation(ctx context.Context, resuming bool) error {\n\tvar server driver.Server\n\tvar conn *mnet.Connection\n\n\t// Apply the client-level timeout if the operation-level timeout is not set.\n\tctx, cancel := csot.WithTimeout(ctx, cs.client.timeout)\n\tdefer cancel()\n\n\tconnCtx, cancel := csot.WithServerSelectionTimeout(ctx, cs.client.deployment.GetServerSelectionTimeout())\n\tdefer cancel()\n\n\tif server, cs.err = cs.client.deployment.SelectServer(connCtx, cs.selector); cs.err != nil {\n\t\treturn cs.Err()\n\t}\n\n\tif conn, cs.err = server.Connection(connCtx); cs.err != nil {\n\t\treturn cs.Err()\n\t}\n\tdefer conn.Close()\n\tcs.wireVersion = conn.Description().WireVersion\n\n\tcs.aggregate.Deployment(cs.createOperationDeployment(server, conn))\n\n\tif resuming {\n\t\tcs.replaceOptions(cs.wireVersion)\n\n\t\tcsOptDoc, err := cs.createPipelineOptionsDoc()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tpipIdx, pipDoc := bsoncore.AppendDocumentStart(nil)\n\t\tpipDoc = bsoncore.AppendDocumentElement(pipDoc, \"$changeStream\", csOptDoc)\n\t\tif pipDoc, cs.err = bsoncore.AppendDocumentEnd(pipDoc, pipIdx); cs.err != nil {\n\t\t\treturn cs.Err()\n\t\t}\n\t\tcs.pipelineSlice[0] = pipDoc\n\n\t\tvar plArr bsoncore.Document\n\t\tif plArr, cs.err = cs.pipelineToBSON(); cs.err != nil {\n\t\t\treturn cs.Err()\n\t\t}\n\t\tcs.aggregate.Pipeline(plArr)\n\t}\n\n\t// Execute the aggregate, retrying on retryable errors once (1) if retryable reads are enabled and\n\t// infinitely (-1) if context is a Timeout context.\n\tvar retries int\n\tif cs.client.retryReads {\n\t\tretries = 1\n\t}\n\tif csot.IsTimeoutContext(ctx) {\n\t\tretries = -1\n\t}\n\n\tvar err error\nAggregateExecuteLoop:\n\tfor {\n\t\terr = cs.aggregate.Execute(ctx)\n\t\t// If no error or no retries remain, do not retry.\n\t\tif err == nil || retries == 0 {\n\t\t\tbreak AggregateExecuteLoop\n\t\t}\n\n\t\tvar tt driver.Error\n\t\tif errors.As(err, &tt) {\n\t\t\t// If error is not retryable, do not retry.\n\t\t\tif !tt.RetryableRead() {\n\t\t\t\tbreak AggregateExecuteLoop\n\t\t\t}\n\n\t\t\tconnCtx, cancel := csot.WithServerSelectionTimeout(ctx, cs.client.deployment.GetServerSelectionTimeout())\n\t\t\tdefer cancel()\n\n\t\t\t// If error is retryable: subtract 1 from retries, redo server selection, checkout\n\t\t\t// a connection, and restart loop.\n\t\t\tretries--\n\t\t\tserver, err = cs.client.deployment.SelectServer(connCtx, cs.selector)\n\t\t\tif err != nil {\n\t\t\t\tbreak AggregateExecuteLoop\n\t\t\t}\n\n\t\t\tconn.Close()\n\n\t\t\tconn, err = server.Connection(connCtx)\n\t\t\tif err != nil {\n\t\t\t\tbreak AggregateExecuteLoop\n\t\t\t}\n\t\t\tdefer conn.Close()\n\n\t\t\t// Update the wire version with data from the new connection.\n\t\t\tcs.wireVersion = conn.Description().WireVersion\n\n\t\t\t// Reset deployment.\n\t\t\tcs.aggregate.Deployment(cs.createOperationDeployment(server, conn))\n\t\t} else {\n\t\t\t// Do not retry if error is not a driver error.\n\t\t\tbreak AggregateExecuteLoop\n\t\t}\n\t}\n\tif err != nil {\n\t\tcs.err = wrapErrors(err)\n\t\treturn cs.err\n\t}\n\n\tcr := cs.aggregate.ResultCursorResponse()\n\tcr.Server = server\n\n\tcs.cursor, cs.err = driver.NewBatchCursor(cr, cs.sess, cs.client.clock, cs.cursorOptions)\n\tif cs.err = wrapErrors(cs.err); cs.err != nil {\n\t\treturn cs.Err()\n\t}\n\n\tcs.updatePbrtFromCommand()\n\tif cs.options.StartAtOperationTime == nil && cs.options.ResumeAfter == nil &&\n\t\tcs.options.StartAfter == nil && cs.wireVersion.Max >= 7 &&\n\t\tcs.emptyBatch() && cs.resumeToken == nil {\n\t\tcs.operationTime = cs.sess.OperationTime\n\t}\n\n\treturn cs.Err()\n}\n\n// Updates the post batch resume token after a successful aggregate or getMore operation.\nfunc (cs *ChangeStream) updatePbrtFromCommand() {\n\t// Only cache the pbrt if an empty batch was returned and a pbrt was included\n\tif pbrt := cs.cursor.PostBatchResumeToken(); cs.emptyBatch() && pbrt != nil {\n\t\tcs.resumeToken = bson.Raw(pbrt)\n\t}\n}\n\nfunc (cs *ChangeStream) storeResumeToken() error {\n\t// If cs.Current is the last document in the batch and a pbrt is included, cache the pbrt\n\t// Otherwise, cache the _id of the document\n\tvar tokenDoc bson.Raw\n\tif len(cs.batch) == 0 {\n\t\tif pbrt := cs.cursor.PostBatchResumeToken(); pbrt != nil {\n\t\t\ttokenDoc = bson.Raw(pbrt)\n\t\t}\n\t}\n\n\tif tokenDoc == nil {\n\t\tvar ok bool\n\t\ttokenDoc, ok = cs.Current.Lookup(\"_id\").DocumentOK()\n\t\tif !ok {\n\t\t\t_ = cs.Close(context.Background())\n\t\t\treturn ErrMissingResumeToken\n\t\t}\n\t}\n\n\tcs.resumeToken = tokenDoc\n\treturn nil\n}\n\nfunc (cs *ChangeStream) buildPipelineSlice(pipeline any) error {\n\tval := reflect.ValueOf(pipeline)\n\tif !val.IsValid() || (val.Kind() != reflect.Slice) {\n\t\tcs.err = errors.New(\"can only marshal slices and arrays into aggregation pipelines, but got invalid\")\n\t\treturn cs.err\n\t}\n\n\tcs.pipelineSlice = make([]bsoncore.Document, 0, val.Len()+1)\n\n\tcsIdx, csDoc := bsoncore.AppendDocumentStart(nil)\n\n\tcsDocTemp, err := cs.createPipelineOptionsDoc()\n\tif err != nil {\n\t\treturn err\n\t}\n\tcsDoc = bsoncore.AppendDocumentElement(csDoc, \"$changeStream\", csDocTemp)\n\tcsDoc, cs.err = bsoncore.AppendDocumentEnd(csDoc, csIdx)\n\tif cs.err != nil {\n\t\treturn cs.err\n\t}\n\tcs.pipelineSlice = append(cs.pipelineSlice, csDoc)\n\n\tfor i := 0; i < val.Len(); i++ {\n\t\tvar elem []byte\n\t\telem, cs.err = marshal(val.Index(i).Interface(), cs.bsonOpts, cs.registry)\n\t\tif cs.err != nil {\n\t\t\treturn cs.err\n\t\t}\n\n\t\tcs.pipelineSlice = append(cs.pipelineSlice, elem)\n\t}\n\n\treturn cs.err\n}\n\nfunc (cs *ChangeStream) createPipelineOptionsDoc() (bsoncore.Document, error) {\n\tplDocIdx, plDoc := bsoncore.AppendDocumentStart(nil)\n\n\tif cs.streamType == ClientStream {\n\t\tplDoc = bsoncore.AppendBooleanElement(plDoc, \"allChangesForCluster\", true)\n\t}\n\n\tif cs.options.FullDocument != nil && *cs.options.FullDocument != options.Default {\n\t\tplDoc = bsoncore.AppendStringElement(plDoc, \"fullDocument\", string(*cs.options.FullDocument))\n\t}\n\n\tif cs.options.FullDocumentBeforeChange != nil {\n\t\tplDoc = bsoncore.AppendStringElement(plDoc, \"fullDocumentBeforeChange\", string(*cs.options.FullDocumentBeforeChange))\n\t}\n\n\tif cs.options.ResumeAfter != nil {\n\t\tvar raDoc bsoncore.Document\n\t\traDoc, cs.err = marshal(cs.options.ResumeAfter, cs.bsonOpts, cs.registry)\n\t\tif cs.err != nil {\n\t\t\treturn nil, cs.err\n\t\t}\n\n\t\tplDoc = bsoncore.AppendDocumentElement(plDoc, \"resumeAfter\", raDoc)\n\t}\n\n\tif cs.options.ShowExpandedEvents != nil {\n\t\tplDoc = bsoncore.AppendBooleanElement(plDoc, \"showExpandedEvents\", *cs.options.ShowExpandedEvents)\n\t}\n\n\tif cs.options.StartAfter != nil {\n\t\tvar saDoc bsoncore.Document\n\t\tsaDoc, cs.err = marshal(cs.options.StartAfter, cs.bsonOpts, cs.registry)\n\t\tif cs.err != nil {\n\t\t\treturn nil, cs.err\n\t\t}\n\n\t\tplDoc = bsoncore.AppendDocumentElement(plDoc, \"startAfter\", saDoc)\n\t}\n\n\tif cs.options.StartAtOperationTime != nil {\n\t\tplDoc = bsoncore.AppendTimestampElement(plDoc, \"startAtOperationTime\", cs.options.StartAtOperationTime.T, cs.options.StartAtOperationTime.I)\n\t}\n\n\t// Append custom pipeline options.\n\tfor optionName, optionValue := range cs.pipelineOptions {\n\t\tplDoc = bsoncore.AppendValueElement(plDoc, optionName, optionValue)\n\t}\n\n\tif plDoc, cs.err = bsoncore.AppendDocumentEnd(plDoc, plDocIdx); cs.err != nil {\n\t\treturn nil, cs.err\n\t}\n\n\treturn plDoc, nil\n}\n\nfunc (cs *ChangeStream) pipelineToBSON() (bsoncore.Document, error) {\n\tpipelineDocIdx, pipelineArr := bsoncore.AppendArrayStart(nil)\n\tfor i, doc := range cs.pipelineSlice {\n\t\tpipelineArr = bsoncore.AppendDocumentElement(pipelineArr, strconv.Itoa(i), doc)\n\t}\n\tif pipelineArr, cs.err = bsoncore.AppendArrayEnd(pipelineArr, pipelineDocIdx); cs.err != nil {\n\t\treturn nil, cs.err\n\t}\n\treturn pipelineArr, cs.err\n}\n\nfunc (cs *ChangeStream) replaceOptions(wireVersion *description.VersionRange) {\n\t// Cached resume token: use the resume token as the resumeAfter option and set no other resume options\n\tif cs.resumeToken != nil {\n\t\tcs.options.ResumeAfter = cs.resumeToken\n\t\tcs.options.StartAfter = nil\n\t\tcs.options.StartAtOperationTime = nil\n\t\treturn\n\t}\n\n\t// No cached resume token but cached operation time: use the operation time as the startAtOperationTime option and\n\t// set no other resume options\n\tif (cs.sess.OperationTime != nil || cs.options.StartAtOperationTime != nil) && wireVersion.Max >= 7 {\n\t\topTime := cs.options.StartAtOperationTime\n\t\tif cs.operationTime != nil {\n\t\t\topTime = cs.sess.OperationTime\n\t\t}\n\n\t\tcs.options.StartAtOperationTime = opTime\n\t\tcs.options.ResumeAfter = nil\n\t\tcs.options.StartAfter = nil\n\t\treturn\n\t}\n\n\t// No cached resume token or operation time: set none of the resume options\n\tcs.options.ResumeAfter = nil\n\tcs.options.StartAfter = nil\n\tcs.options.StartAtOperationTime = nil\n}\n\n// ID returns the ID for this change stream, or 0 if the cursor has been closed or exhausted.\nfunc (cs *ChangeStream) ID() int64 {\n\tif cs.cursor == nil {\n\t\treturn 0\n\t}\n\treturn cs.cursor.ID()\n}\n\n// RemainingBatchLength returns the number of documents left in the current batch. If this returns zero, the subsequent\n// call to Next or TryNext will do a network request to fetch the next batch.\nfunc (cs *ChangeStream) RemainingBatchLength() int {\n\treturn len(cs.batch)\n}\n\n// SetBatchSize sets the number of documents to fetch from the database with\n// each iteration of the ChangeStream's \"Next\" or \"TryNext\" method. This setting\n// only affects subsequent document batches fetched from the database.\nfunc (cs *ChangeStream) SetBatchSize(size int32) {\n\t// Set batch size on the cursor options also so any \"resumed\" change stream\n\t// cursors will pick up the latest batch size setting.\n\tcs.cursorOptions.BatchSize = size\n\tcs.cursor.SetBatchSize(size)\n}\n\n// Decode will unmarshal the current event document into val and return any errors from the unmarshalling process\n// without any modification. If val is nil or is a typed nil, an error will be returned.\nfunc (cs *ChangeStream) Decode(val any) error {\n\tif cs.cursor == nil {\n\t\treturn ErrNilCursor\n\t}\n\n\tdec := getDecoder(cs.Current, cs.bsonOpts, cs.registry)\n\treturn dec.Decode(val)\n}\n\n// Err returns the last error seen by the change stream, or nil if no errors has occurred.\nfunc (cs *ChangeStream) Err() error {\n\tif cs.err != nil {\n\t\treturn wrapErrors(cs.err)\n\t}\n\tif cs.cursor == nil {\n\t\treturn nil\n\t}\n\n\treturn wrapErrors(cs.cursor.Err())\n}\n\n// Close closes this change stream and the underlying cursor. Next and TryNext must not be called after Close has been\n// called. Close is idempotent. After the first call, any subsequent calls will not change the state.\nfunc (cs *ChangeStream) Close(ctx context.Context) error {\n\tif ctx == nil {\n\t\tctx = context.Background()\n\t}\n\n\tdefer closeImplicitSession(cs.sess)\n\n\tif cs.cursor == nil {\n\t\treturn nil // cursor is already closed\n\t}\n\n\tcs.err = wrapErrors(cs.cursor.Close(ctx))\n\tcs.cursor = nil\n\treturn cs.Err()\n}\n\n// ResumeToken returns the last cached resume token for this change stream, or nil if a resume token has not been\n// stored.\nfunc (cs *ChangeStream) ResumeToken() bson.Raw {\n\treturn cs.resumeToken\n}\n\n// Next gets the next event for this change stream. It returns true if there\n// were no errors and the next event document is available.\n//\n// Next blocks until an event is available, an error occurs, or ctx expires.\n// If ctx expires, the error will be set to ctx.Err(). In an error case, Next\n// will return false.\n//\n// If Next returns false, subsequent calls will also return false.\nfunc (cs *ChangeStream) Next(ctx context.Context) bool {\n\treturn cs.next(ctx, false)\n}\n\n// TryNext attempts to get the next event for this change stream. It returns\n// true if there were no errors and the next event document is available.\n//\n// TryNext returns false if the change stream is closed by the server, an error\n// occurs when getting changes from the server, the next change is not yet\n// available, or ctx expires.\n//\n// If ctx expires, the error will be set to ctx.Err(). Users can either call\n// TryNext again or close the existing change stream and create a new one. It is\n// suggested to close and re-create the stream with ah higher timeout if the\n// timeout occurs before any events have been received, which is a signal that\n// the server is timing out before it can finish processing the existing oplog.\n//\n// If TryNext returns false and an error occurred or the change stream was\n// closed (i.e. cs.Err() != nil || cs.ID() == 0), subsequent attempts will also\n// return false. Otherwise, it is safe to call TryNext again until a change is\n// available.\n//\n// This method requires driver version >= 1.2.0.\nfunc (cs *ChangeStream) TryNext(ctx context.Context) bool {\n\treturn cs.next(ctx, true)\n}\n\nfunc (cs *ChangeStream) next(ctx context.Context, nonBlocking bool) bool {\n\t// return false right away if the change stream has already errored or if cursor is closed.\n\tif cs.err != nil {\n\t\treturn false\n\t}\n\n\tif ctx == nil {\n\t\tctx = context.Background()\n\t}\n\n\tif len(cs.batch) == 0 {\n\t\tcs.loopNext(ctx, nonBlocking)\n\t\tif cs.err != nil {\n\t\t\tcs.err = wrapErrors(cs.err)\n\t\t\treturn false\n\t\t}\n\t\tif len(cs.batch) == 0 {\n\t\t\treturn false\n\t\t}\n\t}\n\n\t// successfully got non-empty batch\n\tcs.Current = bson.Raw(cs.batch[0])\n\tcs.batch = cs.batch[1:]\n\tif cs.err = cs.storeResumeToken(); cs.err != nil {\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc (cs *ChangeStream) loopNext(ctx context.Context, nonBlocking bool) {\n\t// To avoid unnecessary socket timeouts, we attempt to short-circuit tailable\n\t// awaitData \"getMore\" operations by ensuring that the maxAwaitTimeMS is less\n\t// than the operation timeout.\n\t//\n\t// The specifications assume that drivers iteratively apply the timeout\n\t// provided at the constructor level (e.g., (*collection).Find) for tailable\n\t// awaitData cursors:\n\t//\n\t//\tIf set, drivers MUST apply the timeoutMS option to the initial aggregate\n\t//\toperation. Drivers MUST also apply the original timeoutMS value to each\n\t//\tnext call on the change stream but MUST NOT use it to derive a maxTimeMS\n\t//\tfield for getMore commands.\n\t//\n\t// The Go Driver might decide to support the above behavior with DRIVERS-2722.\n\t// The principal concern is that it would be unexpected for users to apply an\n\t// operation-level timeout via contexts to a constructor and then that timeout\n\t// later be applied while working with a resulting cursor. Instead, it is more\n\t// idiomatic to apply the timeout to the context passed to Next or TryNext.\n\tif cs.options != nil && !nonBlocking {\n\t\tmaxAwaitTime := cs.cursorOptions.MaxAwaitTime\n\n\t\t// If maxAwaitTime is not set, this check is unnecessary.\n\t\tif maxAwaitTime != nil && !mongoutil.TimeoutWithinContext(ctx, *maxAwaitTime) {\n\t\t\tcs.err = fmt.Errorf(\"MaxAwaitTime must be less than the operation timeout\")\n\n\t\t\treturn\n\t\t}\n\t}\n\n\t// Apply the client-level timeout if the operation-level timeout is not set.\n\t// This calculation is also done in \"executeOperation\" but cursor.Next is also\n\t// blocking and should honor client-level timeouts.\n\tctx, cancel := csot.WithTimeout(ctx, cs.client.timeout)\n\tdefer cancel()\n\n\tfor {\n\t\tif cs.cursor == nil {\n\t\t\treturn\n\t\t}\n\n\t\tif cs.cursor.Next(ctx) {\n\t\t\t// non-empty batch returned\n\t\t\tcs.batch, cs.err = cs.cursor.Batch().Documents()\n\t\t\treturn\n\t\t}\n\n\t\tcs.err = wrapErrors(cs.cursor.Err())\n\t\tif cs.err == nil {\n\t\t\t// Check if cursor is alive\n\t\t\tif cs.ID() == 0 {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\t// If a getMore was done but the batch was empty, the batch cursor will return false with no error.\n\t\t\t// Update the tracked resume token to catch the post batch resume token from the server response.\n\t\t\tcs.updatePbrtFromCommand()\n\t\t\tif nonBlocking {\n\t\t\t\t// stop after a successful getMore, even though the batch was empty\n\t\t\t\treturn\n\t\t\t}\n\t\t\tcontinue // loop getMore until a non-empty batch is returned or an error occurs\n\t\t}\n\n\t\tif !cs.isResumableError() {\n\t\t\treturn\n\t\t}\n\n\t\t// ignore error from cursor close because if the cursor is deleted or errors we tried to close it and will remake and try to get next batch\n\t\t_ = cs.cursor.Close(ctx)\n\t\tif cs.err = cs.executeOperation(ctx, true); cs.err != nil {\n\t\t\treturn\n\t\t}\n\t}\n}\n\nfunc (cs *ChangeStream) isResumableError() bool {\n\tvar commandErr CommandError\n\tif !errors.As(cs.err, &commandErr) || commandErr.HasErrorLabel(networkErrorLabel) {\n\t\t// All non-server errors or network errors are resumable.\n\t\treturn true\n\t}\n\n\tif commandErr.Code == errorCursorNotFound {\n\t\treturn true\n\t}\n\n\t// For wire versions 9 and above, a server error is resumable if it has the ResumableChangeStreamError label.\n\tif cs.wireVersion != nil && driverutil.VersionRangeIncludes(*cs.wireVersion, minResumableLabelWireVersion) {\n\t\treturn commandErr.HasErrorLabel(resumableErrorLabel)\n\t}\n\n\t// For wire versions below 9, a server error is resumable if its code is on the allowlist.\n\t_, resumable := resumableChangeStreamErrors[commandErr.Code]\n\treturn resumable\n}\n\n// Returns true if the underlying cursor's batch is empty\nfunc (cs *ChangeStream) emptyBatch() bool {\n\treturn cs.cursor.Batch().Empty()\n}\n\n// StreamType represents the cluster type against which a ChangeStream was created.\ntype StreamType uint8\n\n// These constants represent valid change stream types. A change stream can be initialized over a collection, all\n// collections in a database, or over a cluster.\nconst (\n\tCollectionStream StreamType = iota\n\tDatabaseStream\n\tClientStream\n)\n"
  },
  {
    "path": "mongo/change_stream_deployment.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage mongo\n\nimport (\n\t\"context\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/description\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/mnet\"\n)\n\ntype changeStreamDeployment struct {\n\ttopologyKind description.TopologyKind\n\tserver       driver.Server\n\tconn         *mnet.Connection\n}\n\nvar (\n\t_ driver.Deployment     = (*changeStreamDeployment)(nil)\n\t_ driver.Server         = (*changeStreamDeployment)(nil)\n\t_ driver.ErrorProcessor = (*changeStreamDeployment)(nil)\n)\n\nfunc (c *changeStreamDeployment) SelectServer(context.Context, description.ServerSelector) (driver.Server, error) {\n\treturn c, nil\n}\n\nfunc (c *changeStreamDeployment) Kind() description.TopologyKind {\n\treturn c.topologyKind\n}\n\nfunc (c *changeStreamDeployment) Connection(context.Context) (*mnet.Connection, error) {\n\treturn c.conn, nil\n}\n\nfunc (c *changeStreamDeployment) RTTMonitor() driver.RTTMonitor {\n\treturn c.server.RTTMonitor()\n}\n\nfunc (c *changeStreamDeployment) ProcessError(err error, describer mnet.Describer) driver.ProcessErrorResult {\n\tep, ok := c.server.(driver.ErrorProcessor)\n\tif !ok {\n\t\treturn driver.NoChange\n\t}\n\n\treturn ep.ProcessError(err, describer)\n}\n\n// GetServerSelectionTimeout returns zero as a server selection timeout is not\n// applicable for change stream deployments.\nfunc (*changeStreamDeployment) GetServerSelectionTimeout() time.Duration {\n\treturn 0\n}\n"
  },
  {
    "path": "mongo/change_stream_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage mongo\n\nimport (\n\t\"testing\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n)\n\nfunc TestChangeStream(t *testing.T) {\n\tt.Run(\"nil cursor\", func(t *testing.T) {\n\t\tcs := &ChangeStream{client: &Client{}}\n\n\t\tid := cs.ID()\n\t\tassert.Equal(t, int64(0), id, \"expected ID 0, got %v\", id)\n\t\tassert.False(t, cs.Next(bgCtx), \"expected Next to return false, got true\")\n\t\terr := cs.Decode(nil)\n\t\tassert.Equal(t, ErrNilCursor, err, \"expected error %v, got %v\", ErrNilCursor, err)\n\t\terr = cs.Err()\n\t\tassert.Nil(t, err, \"change stream error: %v\", err)\n\t\terr = cs.Close(bgCtx)\n\t\tassert.Nil(t, err, \"Close error: %v\", err)\n\t})\n}\n"
  },
  {
    "path": "mongo/client.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage mongo\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"sync\"\n\t\"sync/atomic\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/event\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/httputil\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/logger\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/mongoutil\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/optionsutil\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/ptrutil\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/serverselector\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/uuid\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/readconcern\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/readpref\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/writeconcern\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/auth\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/description\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/mongocrypt\"\n\tmcopts \"go.mongodb.org/mongo-driver/v2/x/mongo/driver/mongocrypt/options\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/operation\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/session\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/topology\"\n)\n\nconst (\n\tdefaultLocalThreshold = 15 * time.Millisecond\n\tdefaultMaxPoolSize    = 100\n)\n\nvar (\n\t// keyVaultCollOpts specifies options used to communicate with the key vault collection\n\tkeyVaultCollOpts = options.Collection().SetReadConcern(readconcern.Majority()).\n\t\t\t\tSetWriteConcern(writeconcern.Majority())\n\n\tendSessionsBatchSize = 10000\n)\n\n// Client is a handle representing a pool of connections to a MongoDB deployment. It is safe for concurrent use by\n// multiple goroutines.\n//\n// The Client type opens and closes connections automatically and maintains a pool of idle connections. For\n// connection pool configuration options, see documentation for the ClientOptions type in the mongo/options package.\ntype Client struct {\n\tid                uuid.UUID\n\tdeployment        driver.Deployment\n\tlocalThreshold    time.Duration\n\tretryWrites       bool\n\tretryReads        bool\n\tclock             *session.ClusterClock\n\treadPreference    *readpref.ReadPref\n\treadConcern       *readconcern.ReadConcern\n\twriteConcern      *writeconcern.WriteConcern\n\tbsonOpts          *options.BSONOptions\n\tregistry          *bson.Registry\n\tmonitor           *event.CommandMonitor\n\tserverAPI         *driver.ServerAPIOptions\n\tserverMonitor     *event.ServerMonitor\n\tsessionPool       *session.Pool\n\ttimeout           *time.Duration\n\thttpClient        *http.Client\n\tlogger            *logger.Logger\n\tcurrentDriverInfo *atomic.Pointer[options.DriverInfo]\n\tseenDriverInfo    sync.Map\n\n\t// in-use encryption fields\n\tisAutoEncryptionSet bool\n\tkeyVaultClientFLE   *Client\n\tkeyVaultCollFLE     *Collection\n\tmongocryptdFLE      *mongocryptdClient\n\tcryptFLE            driver.Crypt\n\tmetadataClientFLE   *Client\n\tinternalClientFLE   *Client\n\tencryptedFieldsMap  map[string]any\n\tauthenticator       driver.Authenticator\n}\n\n// Connect creates a new Client with the given configuration options.\n//\n// Connect returns an error if the configuration options are invalid, but does\n// not validate that the MongoDB deployment is reachable. To verify that the\n// deployment is reachable, call [Client.Ping].\n//\n// When creating an [options.ClientOptions], the order the methods are called\n// matters. Later option setter calls overwrite the values from previous option\n// setter calls, including the ApplyURI method. This allows callers to\n// determine the order of precedence for setting options. For instance, if\n// ApplyURI is called before SetAuth, the Credential from SetAuth will\n// overwrite the values from the connection string. If ApplyURI is called\n// after SetAuth, then its values will overwrite those from SetAuth.\nfunc Connect(opts ...*options.ClientOptions) (*Client, error) {\n\tc, err := newClient(opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\terr = c.connect()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn c, nil\n}\n\n// newClient creates a new client to connect to a deployment specified by the uri.\n//\n// When creating an options.ClientOptions, the order the methods are called matters. Later Set*\n// methods will overwrite the values from previous Set* method invocations. This includes the\n// ApplyURI method. This allows callers to determine the order of precedence for option\n// application. For instance, if ApplyURI is called before SetAuth, the Credential from\n// SetAuth will overwrite the values from the connection string. If ApplyURI is called\n// after SetAuth, then its values will overwrite those from SetAuth.\n//\n// The opts parameter is processed using options.MergeClientOptions, which will overwrite entire\n// option fields of previous options, there is no partial overwriting. For example, if Username is\n// set in the Auth field for the first option, and Password is set for the second but with no\n// Username, after the merge the Username field will be empty.\nfunc newClient(opts ...*options.ClientOptions) (*Client, error) {\n\tclientOpts := options.MergeClientOptions(opts...)\n\n\tid, err := uuid.New()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tclient := &Client{\n\t\tid:                id,\n\t\tcurrentDriverInfo: &atomic.Pointer[options.DriverInfo]{},\n\t}\n\n\t// ClusterClock\n\tclient.clock = new(session.ClusterClock)\n\n\t// LocalThreshold\n\tclient.localThreshold = defaultLocalThreshold\n\tif clientOpts.LocalThreshold != nil {\n\t\tclient.localThreshold = *clientOpts.LocalThreshold\n\t}\n\t// Monitor\n\tif clientOpts.Monitor != nil {\n\t\tclient.monitor = clientOpts.Monitor\n\t}\n\t// ServerMonitor\n\tif clientOpts.ServerMonitor != nil {\n\t\tclient.serverMonitor = clientOpts.ServerMonitor\n\t}\n\t// ReadConcern\n\tclient.readConcern = &readconcern.ReadConcern{}\n\tif clientOpts.ReadConcern != nil {\n\t\tclient.readConcern = clientOpts.ReadConcern\n\t}\n\t// ReadPreference\n\tclient.readPreference = readpref.Primary()\n\tif clientOpts.ReadPreference != nil {\n\t\tclient.readPreference = clientOpts.ReadPreference\n\t}\n\t// BSONOptions\n\tif clientOpts.BSONOptions != nil {\n\t\tclient.bsonOpts = clientOpts.BSONOptions\n\t}\n\t// Registry\n\tclient.registry = defaultRegistry\n\tif clientOpts.Registry != nil {\n\t\tclient.registry = clientOpts.Registry\n\t}\n\t// RetryWrites\n\tclient.retryWrites = true // retry writes on by default\n\tif clientOpts.RetryWrites != nil {\n\t\tclient.retryWrites = *clientOpts.RetryWrites\n\t}\n\tclient.retryReads = true\n\tif clientOpts.RetryReads != nil {\n\t\tclient.retryReads = *clientOpts.RetryReads\n\t}\n\t// Timeout\n\tclient.timeout = clientOpts.Timeout\n\tclient.httpClient = clientOpts.HTTPClient\n\t// WriteConcern\n\tif clientOpts.WriteConcern != nil {\n\t\tclient.writeConcern = clientOpts.WriteConcern\n\t}\n\t// AutoEncryptionOptions\n\tif clientOpts.AutoEncryptionOptions != nil {\n\t\tclient.isAutoEncryptionSet = true\n\t\tif err := client.configureAutoEncryption(clientOpts); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t} else {\n\t\tclient.cryptFLE = clientOpts.Crypt\n\t}\n\n\t// Deployment\n\tif clientOpts.Deployment != nil {\n\t\tclient.deployment = clientOpts.Deployment\n\t}\n\n\t// Set default options\n\tif clientOpts.MaxPoolSize == nil {\n\t\tdefaultMaxPoolSize := uint64(defaultMaxPoolSize)\n\t\tclientOpts.MaxPoolSize = &defaultMaxPoolSize\n\t}\n\n\tif clientOpts.Auth != nil {\n\t\tclient.authenticator, err = auth.CreateAuthenticator(\n\t\t\tclientOpts.Auth.AuthMechanism,\n\t\t\ttopology.ConvertCreds(clientOpts.Auth),\n\t\t\tclientOpts.HTTPClient,\n\t\t)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"error creating authenticator: %w\", err)\n\t\t}\n\t}\n\n\tif clientOpts.DriverInfo != nil {\n\t\tclient.AppendDriverInfo(*clientOpts.DriverInfo)\n\t}\n\n\tcfg, err := topology.NewAuthenticatorConfig(client.authenticator,\n\t\ttopology.WithAuthConfigClock(client.clock),\n\t\ttopology.WithAuthConfigClientOptions(clientOpts),\n\t\ttopology.WithAuthConfigDriverInfo(client.currentDriverInfo),\n\t)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar connectTimeout time.Duration\n\tif clientOpts.ConnectTimeout != nil {\n\t\tconnectTimeout = *clientOpts.ConnectTimeout\n\t}\n\n\tclient.serverAPI = topology.ServerAPIFromServerOptions(connectTimeout, cfg.ServerOpts)\n\n\tif client.deployment == nil {\n\t\tclient.deployment, err = topology.New(cfg)\n\t\tif err != nil {\n\t\t\treturn nil, wrapErrors(err)\n\t\t}\n\t}\n\n\t// Create a logger for the client.\n\tclient.logger, err = newLogger(clientOpts.LoggerOptions)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"invalid logger options: %w\", err)\n\t}\n\n\treturn client, nil\n}\n\n// Connect initializes the Client by starting background monitoring goroutines.\n// If the Client was created using the NewClient function, this method must be called before a Client can be used.\n//\n// Connect starts background goroutines to monitor the state of the deployment and does not do any I/O in the main\n// goroutine. The Client.Ping method can be used to verify that the connection was created successfully.\nfunc (c *Client) connect() error {\n\tif connector, ok := c.deployment.(driver.Connector); ok {\n\t\terr := connector.Connect()\n\t\tif err != nil {\n\t\t\treturn wrapErrors(err)\n\t\t}\n\t}\n\n\tif c.mongocryptdFLE != nil {\n\t\tif err := c.mongocryptdFLE.connect(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tif c.internalClientFLE != nil {\n\t\tif err := c.internalClientFLE.connect(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tif c.keyVaultClientFLE != nil && c.keyVaultClientFLE != c.internalClientFLE && c.keyVaultClientFLE != c {\n\t\tif err := c.keyVaultClientFLE.connect(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tif c.metadataClientFLE != nil && c.metadataClientFLE != c.internalClientFLE && c.metadataClientFLE != c {\n\t\tif err := c.metadataClientFLE.connect(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tvar updateChan <-chan description.Topology\n\tif subscriber, ok := c.deployment.(driver.Subscriber); ok {\n\t\tsub, err := subscriber.Subscribe()\n\t\tif err != nil {\n\t\t\treturn wrapErrors(err)\n\t\t}\n\t\tupdateChan = sub.Updates\n\t}\n\tc.sessionPool = session.NewPool(updateChan)\n\treturn nil\n}\n\n// AppendDriverInfo appends the provided [options.DriverInfo] to the metadata\n// (e.g. name, version, platform) that will be sent to the server in handshake\n// requests when establishing new connections.\n//\n// Repeated calls to AppendDriverInfo with equivalent DriverInfo is a no-op.\n//\n// Metadata is limited to 512 bytes; any excess will be truncated.\nfunc (c *Client) AppendDriverInfo(info options.DriverInfo) {\n\tif _, loaded := c.seenDriverInfo.LoadOrStore(info, struct{}{}); loaded {\n\t\treturn\n\t}\n\n\tif old := c.currentDriverInfo.Load(); old != nil {\n\t\tif old.Name != \"\" && info.Name != \"\" && old.Name != info.Name {\n\t\t\tinfo.Name = old.Name + \"|\" + info.Name\n\t\t} else if old.Name != \"\" {\n\t\t\tinfo.Name = old.Name\n\t\t}\n\n\t\tif old.Version != \"\" && info.Version != \"\" && old.Version != info.Version {\n\t\t\tinfo.Version = old.Version + \"|\" + info.Version\n\t\t} else if old.Version != \"\" {\n\t\t\tinfo.Version = old.Version\n\t\t}\n\n\t\tif old.Platform != \"\" && info.Platform != \"\" && old.Platform != info.Platform {\n\t\t\tinfo.Platform = old.Platform + \"|\" + info.Platform\n\t\t} else if old.Platform != \"\" {\n\t\t\tinfo.Platform = old.Platform\n\t\t}\n\t}\n\n\t// Copy-on-write so that the info stored in the client is immutable.\n\tinfoCopy := new(options.DriverInfo)\n\t*infoCopy = info\n\n\tc.currentDriverInfo.Store(infoCopy)\n}\n\n// Disconnect closes sockets to the topology referenced by this Client. It will\n// shut down any monitoring goroutines, close the idle connection pool, and will\n// wait until all the in use connections have been returned to the connection\n// pool and closed before returning. If the context expires via cancellation,\n// deadline, or timeout before the in use connections have returned, the in use\n// connections will be closed, resulting in the failure of any in flight read\n// or write operations. If this method returns with no errors, all connections\n// associated with this Client have been closed.\nfunc (c *Client) Disconnect(ctx context.Context) error {\n\tif c.logger != nil {\n\t\tdefer c.logger.Close()\n\t}\n\n\tif ctx == nil {\n\t\tctx = context.Background()\n\t}\n\n\tif c.httpClient == httputil.DefaultHTTPClient {\n\t\tdefer httputil.CloseIdleHTTPConnections(c.httpClient)\n\t}\n\n\tc.endSessions(ctx)\n\tif c.mongocryptdFLE != nil {\n\t\tif err := c.mongocryptdFLE.disconnect(ctx); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tif c.internalClientFLE != nil {\n\t\tif err := c.internalClientFLE.Disconnect(ctx); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tif c.keyVaultClientFLE != nil && c.keyVaultClientFLE != c.internalClientFLE && c.keyVaultClientFLE != c {\n\t\tif err := c.keyVaultClientFLE.Disconnect(ctx); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif c.metadataClientFLE != nil && c.metadataClientFLE != c.internalClientFLE && c.metadataClientFLE != c {\n\t\tif err := c.metadataClientFLE.Disconnect(ctx); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif c.cryptFLE != nil {\n\t\tc.cryptFLE.Close()\n\t}\n\n\tif disconnector, ok := c.deployment.(driver.Disconnector); ok {\n\t\treturn wrapErrors(disconnector.Disconnect(ctx))\n\t}\n\n\treturn nil\n}\n\n// Ping sends a ping command to verify that the client can connect to the deployment.\n//\n// The rp parameter is used to determine which server is selected for the operation.\n// If it is nil, the client's read preference is used.\n//\n// If the server is down, Ping will try to select a server until the client's server selection timeout expires.\n// This can be configured through the ClientOptions.SetServerSelectionTimeout option when creating a new Client.\n// After the timeout expires, a server selection error is returned.\n//\n// Using Ping reduces application resilience because applications starting up will error if the server is temporarily\n// unavailable or is failing over (e.g. during autoscaling due to a load spike).\nfunc (c *Client) Ping(ctx context.Context, rp *readpref.ReadPref) error {\n\tif ctx == nil {\n\t\tctx = context.Background()\n\t}\n\n\tif rp == nil {\n\t\trp = c.readPreference\n\t}\n\n\tdb := c.Database(\"admin\")\n\tres := db.RunCommand(ctx, bson.D{\n\t\t{\"ping\", 1},\n\t}, options.RunCmd().SetReadPreference(rp))\n\n\treturn wrapErrors(res.Err())\n}\n\n// StartSession starts a new session configured with the given options.\n//\n// StartSession does not actually communicate with the server and will not error if the client is\n// disconnected.\n//\n// StartSession is safe to call from multiple goroutines concurrently. However, Sessions returned by StartSession are\n// not safe for concurrent use by multiple goroutines.\n//\n// If the DefaultReadConcern, DefaultWriteConcern, or DefaultReadPreference options are not set, the client's read\n// concern, write concern, or read preference will be used, respectively.\nfunc (c *Client) StartSession(opts ...options.Lister[options.SessionOptions]) (*Session, error) {\n\tsessArgs, err := mongoutil.NewOptions(opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif sessArgs.CausalConsistency == nil && (sessArgs.Snapshot == nil || !*sessArgs.Snapshot) {\n\t\tsessArgs.CausalConsistency = &options.DefaultCausalConsistency\n\t}\n\tcoreOpts := &session.ClientOptions{\n\t\tDefaultReadConcern:    c.readConcern,\n\t\tDefaultReadPreference: c.readPreference,\n\t\tDefaultWriteConcern:   c.writeConcern,\n\t}\n\tif sessArgs.CausalConsistency != nil {\n\t\tcoreOpts.CausalConsistency = sessArgs.CausalConsistency\n\t}\n\tif bldr := sessArgs.DefaultTransactionOptions; bldr != nil {\n\t\ttxnOpts, err := mongoutil.NewOptions[options.TransactionOptions](bldr)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tif rc := txnOpts.ReadConcern; rc != nil {\n\t\t\tcoreOpts.DefaultReadConcern = rc\n\t\t}\n\n\t\tif wc := txnOpts.WriteConcern; wc != nil {\n\t\t\tcoreOpts.DefaultWriteConcern = wc\n\t\t}\n\n\t\tif rp := txnOpts.ReadPreference; rp != nil {\n\t\t\tcoreOpts.DefaultReadPreference = rp\n\t\t}\n\t}\n\n\tif sessArgs.Snapshot != nil {\n\t\tcoreOpts.Snapshot = sessArgs.Snapshot\n\t}\n\n\tif sessArgs.SnapshotTime != nil {\n\t\tcoreOpts.SnapshotTime = sessArgs.SnapshotTime\n\t}\n\n\tsess, err := session.NewClientSession(c.sessionPool, c.id, coreOpts)\n\tif err != nil {\n\t\treturn nil, wrapErrors(err)\n\t}\n\n\treturn &Session{\n\t\tclientSession: sess,\n\t\tclient:        c,\n\t\tdeployment:    c.deployment,\n\t}, nil\n}\n\nfunc (c *Client) endSessions(ctx context.Context) {\n\tsessionIDs := c.sessionPool.IDSlice()\n\top := operation.NewEndSessions(nil).ClusterClock(c.clock).Deployment(c.deployment).\n\t\tServerSelector(&serverselector.ReadPref{ReadPref: readpref.PrimaryPreferred()}).\n\t\tCommandMonitor(c.monitor).Database(\"admin\").Crypt(c.cryptFLE).ServerAPI(c.serverAPI)\n\n\ttotalNumIDs := len(sessionIDs)\n\tvar currentBatch []bsoncore.Document\n\tfor i := 0; i < totalNumIDs; i++ {\n\t\tcurrentBatch = append(currentBatch, sessionIDs[i])\n\n\t\t// If we are at the end of a batch or the end of the overall IDs array, execute the operation.\n\t\tif ((i+1)%endSessionsBatchSize) == 0 || i == totalNumIDs-1 {\n\t\t\t// Ignore all errors when ending sessions.\n\t\t\t_, marshalVal, err := bson.MarshalValue(currentBatch)\n\t\t\tif err == nil {\n\t\t\t\t_ = op.SessionIDs(marshalVal).Execute(ctx)\n\t\t\t}\n\n\t\t\tcurrentBatch = currentBatch[:0]\n\t\t}\n\t}\n}\n\nfunc (c *Client) configureAutoEncryption(args *options.ClientOptions) error {\n\tc.encryptedFieldsMap = args.AutoEncryptionOptions.EncryptedFieldsMap\n\tif err := c.configureKeyVaultClientFLE(args); err != nil {\n\t\treturn err\n\t}\n\n\tif err := c.configureMetadataClientFLE(args); err != nil {\n\t\treturn err\n\t}\n\n\tmc, err := c.newMongoCrypt(args.AutoEncryptionOptions)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// If the crypt_shared library was not loaded, try to spawn and connect to mongocryptd.\n\tif mc.CryptSharedLibVersionString() == \"\" {\n\t\tmongocryptdFLE, err := newMongocryptdClient(args.AutoEncryptionOptions)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tc.mongocryptdFLE = mongocryptdFLE\n\t}\n\n\tc.configureCryptFLE(mc, args.AutoEncryptionOptions)\n\treturn nil\n}\n\nfunc (c *Client) getOrCreateInternalClient(args *options.ClientOptions) (*Client, error) {\n\tif c.internalClientFLE != nil {\n\t\treturn c.internalClientFLE, nil\n\t}\n\n\targsCopy := *args\n\n\targsCopy.AutoEncryptionOptions = nil\n\targsCopy.MinPoolSize = ptrutil.Ptr[uint64](0)\n\n\tvar err error\n\tc.internalClientFLE, err = newClient(&argsCopy)\n\n\treturn c.internalClientFLE, err\n}\n\nfunc (c *Client) configureKeyVaultClientFLE(clientOpts *options.ClientOptions) error {\n\taeOpts := clientOpts.AutoEncryptionOptions\n\n\tvar err error\n\n\tswitch {\n\tcase aeOpts.KeyVaultClientOptions != nil:\n\t\tc.keyVaultClientFLE, err = newClient(aeOpts.KeyVaultClientOptions)\n\tcase clientOpts.MaxPoolSize != nil && *clientOpts.MaxPoolSize == 0:\n\t\tc.keyVaultClientFLE = c\n\tdefault:\n\t\tc.keyVaultClientFLE, err = c.getOrCreateInternalClient(clientOpts)\n\t}\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tdbName, collName := splitNamespace(aeOpts.KeyVaultNamespace)\n\tc.keyVaultCollFLE = c.keyVaultClientFLE.Database(dbName).Collection(collName, keyVaultCollOpts)\n\treturn nil\n}\n\nfunc (c *Client) configureMetadataClientFLE(clientOpts *options.ClientOptions) error {\n\taeOpts := clientOpts.AutoEncryptionOptions\n\n\tif aeOpts.BypassAutoEncryption != nil && *aeOpts.BypassAutoEncryption {\n\t\t// no need for a metadata client.\n\t\treturn nil\n\t}\n\tif clientOpts.MaxPoolSize != nil && *clientOpts.MaxPoolSize == 0 {\n\t\tc.metadataClientFLE = c\n\t\treturn nil\n\t}\n\n\tvar err error\n\tc.metadataClientFLE, err = c.getOrCreateInternalClient(clientOpts)\n\n\treturn err\n}\n\nfunc (c *Client) newMongoCrypt(opts *options.AutoEncryptionOptions) (*mongocrypt.MongoCrypt, error) {\n\t// convert schemas in SchemaMap to bsoncore documents\n\tcryptSchemaMap := make(map[string]bsoncore.Document)\n\tfor k, v := range opts.SchemaMap {\n\t\tschema, err := marshal(v, c.bsonOpts, c.registry)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tcryptSchemaMap[k] = schema\n\t}\n\n\t// convert schemas in EncryptedFieldsMap to bsoncore documents\n\tcryptEncryptedFieldsMap := make(map[string]bsoncore.Document)\n\tfor k, v := range opts.EncryptedFieldsMap {\n\t\tencryptedFields, err := marshal(v, c.bsonOpts, c.registry)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tcryptEncryptedFieldsMap[k] = encryptedFields\n\t}\n\n\tkmsProviders, err := marshal(opts.KmsProviders, c.bsonOpts, c.registry)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"error creating KMS providers document: %w\", err)\n\t}\n\n\t// Set the crypt_shared library override path from the \"cryptSharedLibPath\" extra option if one\n\t// was set.\n\tcryptSharedLibPath := \"\"\n\tif val, ok := opts.ExtraOptions[\"cryptSharedLibPath\"]; ok {\n\t\tstr, ok := val.(string)\n\t\tif !ok {\n\t\t\treturn nil, fmt.Errorf(\n\t\t\t\t`expected AutoEncryption extra option \"cryptSharedLibPath\" to be a string, but is a %T`, val)\n\t\t}\n\t\tcryptSharedLibPath = str\n\t}\n\n\t// Explicitly disable loading the crypt_shared library if requested. Note that this is ONLY\n\t// intended for use from tests; there is no supported public API for explicitly disabling\n\t// loading the crypt_shared library.\n\tcryptSharedLibDisabled := false\n\tif v, ok := opts.ExtraOptions[\"__cryptSharedLibDisabledForTestOnly\"]; ok {\n\t\tcryptSharedLibDisabled = v.(bool)\n\t}\n\n\tbypassAutoEncryption := opts.BypassAutoEncryption != nil && *opts.BypassAutoEncryption\n\tbypassQueryAnalysis := opts.BypassQueryAnalysis != nil && *opts.BypassQueryAnalysis\n\n\tmc, err := mongocrypt.NewMongoCrypt(&mcopts.MongoCryptOptions{\n\t\tKmsProviders:               kmsProviders,\n\t\tLocalSchemaMap:             cryptSchemaMap,\n\t\tBypassQueryAnalysis:        bypassQueryAnalysis,\n\t\tEncryptedFieldsMap:         cryptEncryptedFieldsMap,\n\t\tCryptSharedLibDisabled:     cryptSharedLibDisabled || bypassAutoEncryption,\n\t\tCryptSharedLibOverridePath: cryptSharedLibPath,\n\t\tHTTPClient:                 opts.HTTPClient,\n\t\tKeyExpiration:              opts.KeyExpiration,\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar cryptSharedLibRequired bool\n\tif val, ok := opts.ExtraOptions[\"cryptSharedLibRequired\"]; ok {\n\t\tb, ok := val.(bool)\n\t\tif !ok {\n\t\t\treturn nil, fmt.Errorf(\n\t\t\t\t`expected AutoEncryption extra option \"cryptSharedLibRequired\" to be a bool, but is a %T`, val)\n\t\t}\n\t\tcryptSharedLibRequired = b\n\t}\n\n\t// If the \"cryptSharedLibRequired\" extra option is set to true, check the MongoCrypt version\n\t// string to confirm that the library was successfully loaded. If the version string is empty,\n\t// return an error indicating that we couldn't load the crypt_shared library.\n\tif cryptSharedLibRequired && mc.CryptSharedLibVersionString() == \"\" {\n\t\treturn nil, errors.New(\n\t\t\t`AutoEncryption extra option \"cryptSharedLibRequired\" is true, but we failed to load the crypt_shared library`)\n\t}\n\n\treturn mc, nil\n}\n\n//nolint:unused // the unused linter thinks that this function is unreachable because \"c.newMongoCrypt\" always panics without the \"cse\" build tag set.\nfunc (c *Client) configureCryptFLE(mc *mongocrypt.MongoCrypt, opts *options.AutoEncryptionOptions) {\n\tbypass := opts.BypassAutoEncryption != nil && *opts.BypassAutoEncryption\n\tkr := keyRetriever{coll: c.keyVaultCollFLE}\n\tvar cir collInfoRetriever\n\t// If bypass is true, c.metadataClientFLE is nil and the collInfoRetriever\n\t// will not be used. If bypass is false, to the parent client or the\n\t// internal client.\n\tif !bypass {\n\t\tcir = collInfoRetriever{client: c.metadataClientFLE}\n\t}\n\n\tc.cryptFLE = driver.NewCrypt(&driver.CryptOptions{\n\t\tMongoCrypt:           mc,\n\t\tCollInfoFn:           cir.cryptCollInfo,\n\t\tKeyFn:                kr.cryptKeys,\n\t\tMarkFn:               c.mongocryptdFLE.markCommand,\n\t\tTLSConfig:            opts.TLSConfig,\n\t\tBypassAutoEncryption: bypass,\n\t})\n}\n\n// validSession returns an error if the session doesn't belong to the client\nfunc (c *Client) validSession(sess *session.Client) error {\n\tif sess != nil && sess.ClientID != c.id {\n\t\treturn ErrWrongClient\n\t}\n\treturn nil\n}\n\n// Database returns a handle for a database with the given name configured with the given DatabaseOptions.\nfunc (c *Client) Database(name string, opts ...options.Lister[options.DatabaseOptions]) *Database {\n\treturn newDatabase(c, name, opts...)\n}\n\n// ListDatabases executes a listDatabases command and returns the result.\n//\n// The filter parameter must be a document containing query operators and can be used to select which\n// databases are included in the result. It cannot be nil. An empty document (e.g. bson.D{}) should be used to include\n// all databases.\n//\n// The opts parameter can be used to specify options for this operation (see the options.ListDatabasesOptions documentation).\n//\n// For more information about the command, see https://www.mongodb.com/docs/manual/reference/command/listDatabases/.\nfunc (c *Client) ListDatabases(ctx context.Context, filter any, opts ...options.Lister[options.ListDatabasesOptions]) (ListDatabasesResult, error) {\n\tif ctx == nil {\n\t\tctx = context.Background()\n\t}\n\n\tsess := sessionFromContext(ctx)\n\n\terr := c.validSession(sess)\n\tif err != nil {\n\t\treturn ListDatabasesResult{}, err\n\t}\n\tif sess == nil && c.sessionPool != nil {\n\t\tsess = session.NewImplicitClientSession(c.sessionPool, c.id)\n\t\tdefer sess.EndSession()\n\t}\n\n\terr = c.validSession(sess)\n\tif err != nil {\n\t\treturn ListDatabasesResult{}, err\n\t}\n\n\tfilterDoc, err := marshal(filter, c.bsonOpts, c.registry)\n\tif err != nil {\n\t\treturn ListDatabasesResult{}, err\n\t}\n\n\tvar selector description.ServerSelector\n\n\tselector = &serverselector.Composite{\n\t\tSelectors: []description.ServerSelector{\n\t\t\t&serverselector.ReadPref{ReadPref: readpref.Primary()},\n\t\t\t&serverselector.Latency{Latency: c.localThreshold},\n\t\t},\n\t}\n\n\tselector = makeReadPrefSelector(sess, selector, c.localThreshold)\n\n\tlda, err := mongoutil.NewOptions(opts...)\n\tif err != nil {\n\t\treturn ListDatabasesResult{}, err\n\t}\n\top := operation.NewListDatabases(filterDoc).\n\t\tSession(sess).ReadPreference(c.readPreference).CommandMonitor(c.monitor).\n\t\tServerSelector(selector).ClusterClock(c.clock).Database(\"admin\").Deployment(c.deployment).Crypt(c.cryptFLE).\n\t\tServerAPI(c.serverAPI).Timeout(c.timeout).Authenticator(c.authenticator)\n\n\tif lda.NameOnly != nil {\n\t\top = op.NameOnly(*lda.NameOnly)\n\t}\n\tif lda.AuthorizedDatabases != nil {\n\t\top = op.AuthorizedDatabases(*lda.AuthorizedDatabases)\n\t}\n\n\tretry := driver.RetryNone\n\tif c.retryReads {\n\t\tretry = driver.RetryOncePerCommand\n\t}\n\top.Retry(retry)\n\n\terr = op.Execute(ctx)\n\tif err != nil {\n\t\treturn ListDatabasesResult{}, wrapErrors(err)\n\t}\n\n\treturn newListDatabasesResultFromOperation(op.Result()), nil\n}\n\n// ListDatabaseNames executes a listDatabases command and returns a slice containing the names of all of the databases\n// on the server.\n//\n// The filter parameter must be a document containing query operators and can be used to select which databases\n// are included in the result. It cannot be nil. An empty document (e.g. bson.D{}) should be used to include all\n// databases.\n//\n// The opts parameter can be used to specify options for this operation (see the options.ListDatabasesOptions\n// documentation.)\n//\n// For more information about the command, see https://www.mongodb.com/docs/manual/reference/command/listDatabases/.\nfunc (c *Client) ListDatabaseNames(ctx context.Context, filter any, opts ...options.Lister[options.ListDatabasesOptions]) ([]string, error) {\n\topts = append(opts, options.ListDatabases().SetNameOnly(true))\n\n\tres, err := c.ListDatabases(ctx, filter, opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tnames := make([]string, 0)\n\tfor _, spec := range res.Databases {\n\t\tnames = append(names, spec.Name)\n\t}\n\n\treturn names, nil\n}\n\n// WithSession creates a new session context from the ctx and sess parameters\n// and uses it to call the fn callback.\n//\n// WithSession is safe to call from multiple goroutines concurrently. However,\n// the context passed to the WithSession callback function is not safe for\n// concurrent use by multiple goroutines.\n//\n// If the ctx parameter already contains a Session, that Session will be\n// replaced with the one provided.\n//\n// Any error returned by the fn callback will be returned without any\n// modifications.\nfunc WithSession(ctx context.Context, sess *Session, fn func(context.Context) error) error {\n\treturn fn(NewSessionContext(ctx, sess))\n}\n\n// UseSession creates a new Session and uses it to create a new session context,\n// which is used to call the fn callback. After the callback returns, the\n// created Session is ended, meaning that any in-progress transactions started\n// by fn will be aborted even if fn returns an error.\n//\n// UseSession is safe to call from multiple goroutines concurrently. However,\n// the context passed to the UseSession callback function is not safe for\n// concurrent use by multiple goroutines.\n//\n// If the ctx parameter already contains a Session, that Session will be\n// replaced with the newly created one.\n//\n// Any error returned by the fn callback will be returned without any\n// modifications.\nfunc (c *Client) UseSession(ctx context.Context, fn func(context.Context) error) error {\n\treturn c.UseSessionWithOptions(ctx, options.Session(), fn)\n}\n\n// UseSessionWithOptions operates like UseSession but uses the given\n// SessionOptions to create the Session.\n//\n// UseSessionWithOptions is safe to call from multiple goroutines concurrently.\n// However, the context passed to the UseSessionWithOptions callback function is\n// not safe for concurrent use by multiple goroutines.\nfunc (c *Client) UseSessionWithOptions(\n\tctx context.Context,\n\topts *options.SessionOptionsBuilder,\n\tfn func(context.Context) error,\n) error {\n\tdefaultSess, err := c.StartSession(opts)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tdefer defaultSess.EndSession(ctx)\n\treturn fn(NewSessionContext(ctx, defaultSess))\n}\n\n// Watch returns a change stream for all changes on the deployment. See\n// https://www.mongodb.com/docs/manual/changeStreams/ for more information about change streams.\n//\n// The client must be configured with read concern majority or no read concern for a change stream to be created\n// successfully.\n//\n// The pipeline parameter must be an array of documents, each representing a pipeline stage. The pipeline cannot be\n// nil or empty. The stage documents must all be non-nil. See https://www.mongodb.com/docs/manual/changeStreams/ for a list\n// of pipeline stages that can be used with change streams. For a pipeline of bson.D documents, the mongo.Pipeline{}\n// type can be used.\n//\n// The opts parameter can be used to specify options for change stream creation (see the options.ChangeStreamOptions\n// documentation).\nfunc (c *Client) Watch(ctx context.Context, pipeline any,\n\topts ...options.Lister[options.ChangeStreamOptions],\n) (*ChangeStream, error) {\n\tcsConfig := changeStreamConfig{\n\t\treadConcern:    c.readConcern,\n\t\treadPreference: c.readPreference,\n\t\tclient:         c,\n\t\tbsonOpts:       c.bsonOpts,\n\t\tregistry:       c.registry,\n\t\tstreamType:     ClientStream,\n\t\tcrypt:          c.cryptFLE,\n\t}\n\n\treturn newChangeStream(ctx, csConfig, pipeline, opts...)\n}\n\n// NumberSessionsInProgress returns the number of sessions that have been started for this client but have not been\n// closed (i.e. EndSession has not been called).\nfunc (c *Client) NumberSessionsInProgress() int {\n\t// The underlying session pool uses an int64 for checkedOut to allow atomic\n\t// access. We convert to an int here to maintain backward compatibility with\n\t// older versions of the driver that did not atomically access checkedOut.\n\treturn int(c.sessionPool.CheckedOut())\n}\n\nfunc (c *Client) createBaseCursorOptions() driver.CursorOptions {\n\treturn driver.CursorOptions{\n\t\tCommandMonitor: c.monitor,\n\t\tCrypt:          c.cryptFLE,\n\t\tServerAPI:      c.serverAPI,\n\t}\n}\n\n// ClientBulkWrite is a struct that can be used in a client-level BulkWrite operation.\ntype ClientBulkWrite struct {\n\tDatabase   string\n\tCollection string\n\tModel      ClientWriteModel\n}\n\n// BulkWrite performs a client-level bulk write operation.\nfunc (c *Client) BulkWrite(ctx context.Context, writes []ClientBulkWrite,\n\topts ...options.Lister[options.ClientBulkWriteOptions],\n) (*ClientBulkWriteResult, error) {\n\t// TODO(GODRIVER-3403): Remove after support for QE with Client.bulkWrite.\n\tif c.isAutoEncryptionSet {\n\t\treturn nil, errors.New(\"bulkWrite does not currently support automatic encryption\")\n\t}\n\n\tif len(writes) == 0 {\n\t\treturn nil, fmt.Errorf(\"invalid writes: %w\", ErrEmptySlice)\n\t}\n\tbwo, err := mongoutil.NewOptions(opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif ctx == nil {\n\t\tctx = context.Background()\n\t}\n\n\tsess := sessionFromContext(ctx)\n\tif sess == nil && c.sessionPool != nil {\n\t\tsess = session.NewImplicitClientSession(c.sessionPool, c.id)\n\t\tdefer sess.EndSession()\n\t}\n\n\terr = c.validSession(sess)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\ttransactionRunning := sess.TransactionRunning()\n\twc := c.writeConcern\n\tif transactionRunning {\n\t\twc = nil\n\t}\n\tif bwo.WriteConcern != nil {\n\t\tif transactionRunning {\n\t\t\treturn nil, errors.New(\"cannot set write concern after starting a transaction\")\n\t\t}\n\t\twc = bwo.WriteConcern\n\t}\n\tacknowledged := wc.Acknowledged()\n\tif !acknowledged {\n\t\tif bwo.Ordered == nil || *bwo.Ordered {\n\t\t\treturn nil, errors.New(\"cannot request unacknowledged write concern and ordered writes\")\n\t\t}\n\t\tsess = nil\n\t}\n\n\twriteSelector := &serverselector.Composite{\n\t\tSelectors: []description.ServerSelector{\n\t\t\t&serverselector.Write{},\n\t\t\t&serverselector.Latency{Latency: c.localThreshold},\n\t\t},\n\t}\n\tselector := makePinnedSelector(sess, writeSelector)\n\n\twritePairs := make([]clientBulkWritePair, len(writes))\n\tfor i, w := range writes {\n\t\twritePairs[i] = clientBulkWritePair{\n\t\t\tnamespace: fmt.Sprintf(\"%s.%s\", w.Database, w.Collection),\n\t\t\tmodel:     w.Model,\n\t\t}\n\t}\n\n\top := clientBulkWrite{\n\t\twritePairs:               writePairs,\n\t\tordered:                  bwo.Ordered,\n\t\tbypassDocumentValidation: bwo.BypassDocumentValidation,\n\t\tcomment:                  bwo.Comment,\n\t\tlet:                      bwo.Let,\n\t\tsession:                  sess,\n\t\tclient:                   c,\n\t\tselector:                 selector,\n\t\twriteConcern:             wc,\n\t}\n\tif rawData, ok := optionsutil.Value(bwo.Internal, \"rawData\").(bool); ok {\n\t\top.rawData = &rawData\n\t}\n\tif additionalCmd, ok := optionsutil.Value(bwo.Internal, \"addCommandFields\").(bson.D); ok {\n\t\top.additionalCmd = additionalCmd\n\t}\n\tif bwo.VerboseResults == nil || !(*bwo.VerboseResults) {\n\t\top.errorsOnly = true\n\t} else if !acknowledged {\n\t\treturn nil, errors.New(\"cannot request unacknowledged write concern and verbose results\")\n\t}\n\top.result.Acknowledged = acknowledged\n\top.result.HasVerboseResults = !op.errorsOnly\n\terr = op.execute(ctx)\n\treturn &op.result, wrapErrors(err)\n}\n\n// newLogger will use the LoggerOptions to create an internal logger and publish\n// messages using a LogSink.\nfunc newLogger(opts *options.LoggerOptions) (*logger.Logger, error) {\n\t// If there are no logger options, then create a default logger.\n\tif opts == nil {\n\t\topts = options.Logger()\n\t}\n\n\t// If there are no component-level options and the environment does not\n\t// contain component variables, then do nothing.\n\tif len(opts.ComponentLevels) == 0 && !logger.EnvHasComponentVariables() {\n\t\treturn nil, nil\n\t}\n\n\t// Otherwise, collect the component-level options and create a logger.\n\tcomponentLevels := make(map[logger.Component]logger.Level)\n\tfor component, level := range opts.ComponentLevels {\n\t\tcomponentLevels[logger.Component(component)] = logger.Level(level)\n\t}\n\n\treturn logger.New(opts.Sink, opts.MaxDocumentLength, componentLevels)\n}\n"
  },
  {
    "path": "mongo/client_bulk_write.go",
    "content": "// Copyright (C) MongoDB, Inc. 2024-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage mongo\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"strconv\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/driverutil\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/writeconcern\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/description\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/session\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/wiremessage\"\n)\n\nconst (\n\tdatabase = \"admin\"\n)\n\ntype clientBulkWritePair struct {\n\tnamespace string\n\tmodel     any\n}\n\ntype clientBulkWrite struct {\n\twritePairs               []clientBulkWritePair\n\terrorsOnly               bool\n\tordered                  *bool\n\tbypassDocumentValidation *bool\n\tcomment                  any\n\tlet                      any\n\tsession                  *session.Client\n\tclient                   *Client\n\tselector                 description.ServerSelector\n\twriteConcern             *writeconcern.WriteConcern\n\trawData                  *bool\n\tadditionalCmd            bson.D\n\n\tresult ClientBulkWriteResult\n}\n\nfunc (bw *clientBulkWrite) execute(ctx context.Context) error {\n\tif len(bw.writePairs) == 0 {\n\t\treturn fmt.Errorf(\"invalid writes: %w\", ErrEmptySlice)\n\t}\n\tfor i, m := range bw.writePairs {\n\t\tif m.model == nil {\n\t\t\treturn fmt.Errorf(\"error from model at index %d: %w\", i, ErrNilDocument)\n\t\t}\n\t}\n\tbatches := &modelBatches{\n\t\tsession:    bw.session,\n\t\tclient:     bw.client,\n\t\tordered:    bw.ordered == nil || *bw.ordered,\n\t\twritePairs: bw.writePairs,\n\t\tresult:     &bw.result,\n\t\tretryMode:  driver.RetryOnce,\n\t}\n\terr := driver.Operation{\n\t\tCommandFn:         bw.newCommand(),\n\t\tProcessResponseFn: batches.processResponse,\n\t\tClient:            bw.session,\n\t\tClock:             bw.client.clock,\n\t\tRetryMode:         &batches.retryMode,\n\t\tType:              driver.Write,\n\t\tBatches:           batches,\n\t\tCommandMonitor:    bw.client.monitor,\n\t\tDatabase:          database,\n\t\tDeployment:        bw.client.deployment,\n\t\tSelector:          bw.selector,\n\t\tWriteConcern:      bw.writeConcern,\n\t\tCrypt:             bw.client.cryptFLE,\n\t\tServerAPI:         bw.client.serverAPI,\n\t\tTimeout:           bw.client.timeout,\n\t\tLogger:            bw.client.logger,\n\t\tAuthenticator:     bw.client.authenticator,\n\t\tName:              driverutil.BulkWriteOp,\n\t}.Execute(ctx)\n\tvar exception *ClientBulkWriteException\n\n\tvar ce CommandError\n\tif errors.As(err, &ce) {\n\t\texception = &ClientBulkWriteException{\n\t\t\tWriteError: &WriteError{\n\t\t\t\tCode:    int(ce.Code),\n\t\t\t\tMessage: ce.Message,\n\t\t\t\tRaw:     ce.Raw,\n\t\t\t},\n\t\t}\n\t}\n\tif len(batches.writeConcernErrors) > 0 || len(batches.writeErrors) > 0 {\n\t\tif exception == nil {\n\t\t\texception = new(ClientBulkWriteException)\n\t\t}\n\t\texception.WriteConcernErrors = batches.writeConcernErrors\n\t\texception.WriteErrors = batches.writeErrors\n\t}\n\tif exception != nil {\n\t\tvar hasSuccess bool\n\t\tif batches.ordered {\n\t\t\t_, ok := batches.writeErrors[0]\n\t\t\thasSuccess = !ok\n\t\t} else {\n\t\t\thasSuccess = len(batches.writeErrors) < len(bw.writePairs)\n\t\t}\n\t\tif hasSuccess {\n\t\t\texception.PartialResult = batches.result\n\t\t}\n\t\treturn *exception\n\t}\n\treturn err\n}\n\nfunc (bw *clientBulkWrite) newCommand() func([]byte, description.SelectedServer) ([]byte, error) {\n\treturn func(dst []byte, desc description.SelectedServer) ([]byte, error) {\n\t\tdst = bsoncore.AppendInt32Element(dst, \"bulkWrite\", 1)\n\n\t\tdst = bsoncore.AppendBooleanElement(dst, \"errorsOnly\", bw.errorsOnly)\n\t\tif bw.bypassDocumentValidation != nil && (desc.WireVersion != nil && driverutil.VersionRangeIncludes(*desc.WireVersion, 4)) {\n\t\t\tdst = bsoncore.AppendBooleanElement(dst, \"bypassDocumentValidation\", *bw.bypassDocumentValidation)\n\t\t}\n\t\tif bw.comment != nil {\n\t\t\tcomment, err := marshalValue(bw.comment, bw.client.bsonOpts, bw.client.registry)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tdst = bsoncore.AppendValueElement(dst, \"comment\", comment)\n\t\t}\n\t\tdst = bsoncore.AppendBooleanElement(dst, \"ordered\", bw.ordered == nil || *bw.ordered)\n\t\tif bw.let != nil {\n\t\t\tlet, err := marshal(bw.let, bw.client.bsonOpts, bw.client.registry)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tdst = bsoncore.AppendDocumentElement(dst, \"let\", let)\n\t\t}\n\t\t// Set rawData for 8.2+ servers.\n\t\tif bw.rawData != nil && desc.WireVersion != nil && driverutil.VersionRangeIncludes(*desc.WireVersion, 27) {\n\t\t\tdst = bsoncore.AppendBooleanElement(dst, \"rawData\", *bw.rawData)\n\t\t}\n\t\tif len(bw.additionalCmd) > 0 {\n\t\t\tdoc, err := bson.Marshal(bw.additionalCmd)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tdst = append(dst, doc[4:len(doc)-1]...)\n\t\t}\n\t\treturn dst, nil\n\t}\n}\n\ntype cursorInfo struct {\n\tOk        bool\n\tIdx       int32\n\tCode      *int32\n\tErrmsg    *string\n\tErrInfo   bson.Raw\n\tN         int32\n\tNModified *int32\n\tUpserted  *struct {\n\t\tID any `bson:\"_id\"`\n\t}\n}\n\nfunc (cur *cursorInfo) extractError() *WriteError {\n\tif cur.Ok {\n\t\treturn nil\n\t}\n\terr := &WriteError{\n\t\tIndex:   int(cur.Idx),\n\t\tDetails: cur.ErrInfo,\n\t}\n\tif cur.Code != nil {\n\t\terr.Code = int(*cur.Code)\n\t}\n\tif cur.Errmsg != nil {\n\t\terr.Message = *cur.Errmsg\n\t}\n\treturn err\n}\n\ntype modelBatches struct {\n\tsession *session.Client\n\tclient  *Client\n\n\tordered    bool\n\twritePairs []clientBulkWritePair\n\n\toffset int\n\n\tretryMode      driver.RetryMode // RetryNone by default\n\tcursorHandlers []func(*cursorInfo, bson.Raw) bool\n\tnewIDMap       map[int]any\n\n\tresult             *ClientBulkWriteResult\n\twriteConcernErrors []WriteConcernError\n\twriteErrors        map[int]WriteError\n}\n\nvar _ driver.OperationBatches = &modelBatches{}\n\nfunc (mb *modelBatches) IsOrdered() *bool {\n\treturn &mb.ordered\n}\n\nfunc (mb *modelBatches) AdvanceBatches(n int) {\n\tmb.offset += n\n\tif mb.offset > len(mb.writePairs) {\n\t\tmb.offset = len(mb.writePairs)\n\t}\n}\n\nfunc (mb *modelBatches) Size() int {\n\tif mb.offset > len(mb.writePairs) {\n\t\treturn 0\n\t}\n\treturn len(mb.writePairs) - mb.offset\n}\n\nfunc (mb *modelBatches) AppendBatchSequence(dst []byte, maxCount, totalSize int) (int, []byte, error) {\n\tfn := functionSet{\n\t\tappendStart: func(dst []byte, identifier string) (int32, []byte) {\n\t\t\tvar idx int32\n\t\t\tdst = wiremessage.AppendMsgSectionType(dst, wiremessage.DocumentSequence)\n\t\t\tidx, dst = bsoncore.ReserveLength(dst)\n\t\t\tdst = append(dst, identifier...)\n\t\t\tdst = append(dst, 0x00)\n\t\t\treturn idx, dst\n\t\t},\n\t\tappendDocument: func(dst []byte, _ string, doc []byte) []byte {\n\t\t\tdst = append(dst, doc...)\n\t\t\treturn dst\n\t\t},\n\t\tupdateLength: func(dst []byte, idx, length int32) []byte {\n\t\t\tdst = bsoncore.UpdateLength(dst, idx, length)\n\t\t\treturn dst\n\t\t},\n\t}\n\treturn mb.appendBatches(fn, dst, maxCount, totalSize)\n}\n\nfunc (mb *modelBatches) AppendBatchArray(dst []byte, maxCount, totalSize int) (int, []byte, error) {\n\tfn := functionSet{\n\t\tappendStart:    bsoncore.AppendArrayElementStart,\n\t\tappendDocument: bsoncore.AppendDocumentElement,\n\t\tupdateLength: func(dst []byte, idx, _ int32) []byte {\n\t\t\tdst, _ = bsoncore.AppendArrayEnd(dst, idx)\n\t\t\treturn dst\n\t\t},\n\t}\n\treturn mb.appendBatches(fn, dst, maxCount, totalSize)\n}\n\ntype functionSet struct {\n\tappendStart    func([]byte, string) (int32, []byte)\n\tappendDocument func([]byte, string, []byte) []byte\n\tupdateLength   func([]byte, int32, int32) []byte\n}\n\nfunc (mb *modelBatches) appendBatches(fn functionSet, dst []byte, maxCount, totalSize int) (int, []byte, error) {\n\tif mb.Size() == 0 {\n\t\treturn 0, dst, io.EOF\n\t}\n\n\tmb.cursorHandlers = mb.cursorHandlers[:0]\n\tmb.newIDMap = make(map[int]any)\n\n\tnsMap := make(map[string]int)\n\tgetNsIndex := func(namespace string) (int, bool) {\n\t\tv, ok := nsMap[namespace]\n\t\tif ok {\n\t\t\treturn v, ok\n\t\t}\n\t\tnsIdx := len(nsMap)\n\t\tnsMap[namespace] = nsIdx\n\t\treturn nsIdx, ok\n\t}\n\n\tcanRetry := true\n\tl := len(dst)\n\n\topsIdx, dst := fn.appendStart(dst, \"ops\")\n\tnsIdx, nsDst := fn.appendStart(nil, \"nsInfo\")\n\n\ttotalSize -= 1000\n\tsize := len(dst) + len(nsDst)\n\tvar n int\n\tfor i := mb.offset; i < len(mb.writePairs); i++ {\n\t\tif n == maxCount {\n\t\t\tbreak\n\t\t}\n\n\t\tns := mb.writePairs[i].namespace\n\t\tnsIdx, exists := getNsIndex(ns)\n\n\t\tvar doc bsoncore.Document\n\t\tvar err error\n\t\tswitch model := mb.writePairs[i].model.(type) {\n\t\tcase *ClientInsertOneModel:\n\t\t\tmb.cursorHandlers = append(mb.cursorHandlers, mb.appendInsertResult)\n\t\t\tvar id any\n\t\t\tid, doc, err = (&clientInsertDoc{\n\t\t\t\tnamespace: nsIdx,\n\t\t\t\tdocument:  model.Document,\n\t\t\t}).marshal(mb.client.bsonOpts, mb.client.registry)\n\t\t\tif err != nil {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tmb.newIDMap[i] = id\n\t\tcase *ClientUpdateOneModel:\n\t\t\tmb.cursorHandlers = append(mb.cursorHandlers, mb.appendUpdateResult)\n\t\t\tdoc, err = (&clientUpdateDoc{\n\t\t\t\tnamespace:      nsIdx,\n\t\t\t\tfilter:         model.Filter,\n\t\t\t\tupdate:         model.Update,\n\t\t\t\thint:           model.Hint,\n\t\t\t\tarrayFilters:   model.ArrayFilters,\n\t\t\t\tcollation:      model.Collation,\n\t\t\t\tupsert:         model.Upsert,\n\t\t\t\tsort:           model.Sort,\n\t\t\t\tmulti:          false,\n\t\t\t\tcheckDollarKey: true,\n\t\t\t}).marshal(mb.client.bsonOpts, mb.client.registry)\n\t\tcase *ClientUpdateManyModel:\n\t\t\tcanRetry = false\n\t\t\tmb.cursorHandlers = append(mb.cursorHandlers, mb.appendUpdateResult)\n\t\t\tdoc, err = (&clientUpdateDoc{\n\t\t\t\tnamespace:      nsIdx,\n\t\t\t\tfilter:         model.Filter,\n\t\t\t\tupdate:         model.Update,\n\t\t\t\thint:           model.Hint,\n\t\t\t\tarrayFilters:   model.ArrayFilters,\n\t\t\t\tcollation:      model.Collation,\n\t\t\t\tupsert:         model.Upsert,\n\t\t\t\tmulti:          true,\n\t\t\t\tcheckDollarKey: true,\n\t\t\t}).marshal(mb.client.bsonOpts, mb.client.registry)\n\t\tcase *ClientReplaceOneModel:\n\t\t\tmb.cursorHandlers = append(mb.cursorHandlers, mb.appendUpdateResult)\n\t\t\tdoc, err = (&clientUpdateDoc{\n\t\t\t\tnamespace:      nsIdx,\n\t\t\t\tfilter:         model.Filter,\n\t\t\t\tupdate:         model.Replacement,\n\t\t\t\thint:           model.Hint,\n\t\t\t\tarrayFilters:   nil,\n\t\t\t\tcollation:      model.Collation,\n\t\t\t\tupsert:         model.Upsert,\n\t\t\t\tsort:           model.Sort,\n\t\t\t\tmulti:          false,\n\t\t\t\tcheckDollarKey: false,\n\t\t\t}).marshal(mb.client.bsonOpts, mb.client.registry)\n\t\tcase *ClientDeleteOneModel:\n\t\t\tmb.cursorHandlers = append(mb.cursorHandlers, mb.appendDeleteResult)\n\t\t\tdoc, err = (&clientDeleteDoc{\n\t\t\t\tnamespace: nsIdx,\n\t\t\t\tfilter:    model.Filter,\n\t\t\t\tcollation: model.Collation,\n\t\t\t\thint:      model.Hint,\n\t\t\t\tmulti:     false,\n\t\t\t}).marshal(mb.client.bsonOpts, mb.client.registry)\n\t\tcase *ClientDeleteManyModel:\n\t\t\tcanRetry = false\n\t\t\tmb.cursorHandlers = append(mb.cursorHandlers, mb.appendDeleteResult)\n\t\t\tdoc, err = (&clientDeleteDoc{\n\t\t\t\tnamespace: nsIdx,\n\t\t\t\tfilter:    model.Filter,\n\t\t\t\tcollation: model.Collation,\n\t\t\t\thint:      model.Hint,\n\t\t\t\tmulti:     true,\n\t\t\t}).marshal(mb.client.bsonOpts, mb.client.registry)\n\t\tdefault:\n\t\t\tmb.cursorHandlers = append(mb.cursorHandlers, nil)\n\t\t}\n\t\tif err != nil {\n\t\t\treturn 0, nil, err\n\t\t}\n\t\tlength := len(doc)\n\t\tif !exists {\n\t\t\tlength += len(ns)\n\t\t}\n\t\tsize += length\n\t\tif size >= totalSize {\n\t\t\tbreak\n\t\t}\n\n\t\tdst = fn.appendDocument(dst, strconv.Itoa(n), doc)\n\t\tif !exists {\n\t\t\tidx, doc := bsoncore.AppendDocumentStart(nil)\n\t\t\tdoc = bsoncore.AppendStringElement(doc, \"ns\", ns)\n\t\t\tdoc, _ = bsoncore.AppendDocumentEnd(doc, idx)\n\t\t\tnsDst = fn.appendDocument(nsDst, strconv.Itoa(n), doc)\n\t\t}\n\t\tn++\n\t}\n\tif n == 0 {\n\t\treturn 0, dst[:l], nil\n\t}\n\n\tdst = fn.updateLength(dst, opsIdx, int32(len(dst[opsIdx:])))\n\tnsDst = fn.updateLength(nsDst, nsIdx, int32(len(nsDst[nsIdx:])))\n\tdst = append(dst, nsDst...)\n\n\tmb.retryMode = driver.RetryNone\n\tif mb.client.retryWrites && canRetry {\n\t\tmb.retryMode = driver.RetryOnce\n\t}\n\treturn n, dst, nil\n}\n\nfunc (mb *modelBatches) processResponse(ctx context.Context, resp bsoncore.Document, info driver.ResponseInfo) error {\n\tvar writeCmdErr driver.WriteCommandError\n\tif errors.As(info.Error, &writeCmdErr) && writeCmdErr.WriteConcernError != nil {\n\t\twce := convertDriverWriteConcernError(writeCmdErr.WriteConcernError)\n\t\tif wce != nil {\n\t\t\tmb.writeConcernErrors = append(mb.writeConcernErrors, *wce)\n\t\t}\n\t}\n\tif len(resp) == 0 {\n\t\treturn nil\n\t}\n\tvar res struct {\n\t\tOk        bool\n\t\tCursor    bsoncore.Document\n\t\tNDeleted  int32\n\t\tNInserted int32\n\t\tNMatched  int32\n\t\tNModified int32\n\t\tNUpserted int32\n\t\tNErrors   int32\n\t\tCode      int32\n\t\tErrmsg    string\n\t}\n\terr := bson.Unmarshal(resp, &res)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif !res.Ok {\n\t\treturn ClientBulkWriteException{\n\t\t\tWriteError: &WriteError{\n\t\t\t\tCode:    int(res.Code),\n\t\t\t\tMessage: res.Errmsg,\n\t\t\t\tRaw:     bson.Raw(resp),\n\t\t\t},\n\t\t\tWriteConcernErrors: mb.writeConcernErrors,\n\t\t\tWriteErrors:        mb.writeErrors,\n\t\t\tPartialResult:      mb.result,\n\t\t}\n\t}\n\n\tif mb.result.Acknowledged {\n\t\tmb.result.DeletedCount += int64(res.NDeleted)\n\t\tmb.result.InsertedCount += int64(res.NInserted)\n\t\tmb.result.MatchedCount += int64(res.NMatched)\n\t\tmb.result.ModifiedCount += int64(res.NModified)\n\t\tmb.result.UpsertedCount += int64(res.NUpserted)\n\t}\n\n\tvar cursorRes driver.CursorResponse\n\tcursorRes, err = driver.NewCursorResponse(res.Cursor, info)\n\tif err != nil {\n\t\treturn err\n\t}\n\tvar bCursor *driver.BatchCursor\n\tbCursor, err = driver.NewBatchCursor(cursorRes, mb.session, mb.client.clock,\n\t\tdriver.CursorOptions{\n\t\t\tCommandMonitor:        mb.client.monitor,\n\t\t\tCrypt:                 mb.client.cryptFLE,\n\t\t\tServerAPI:             mb.client.serverAPI,\n\t\t\tMarshalValueEncoderFn: newEncoderFn(mb.client.bsonOpts, mb.client.registry),\n\t\t},\n\t)\n\tif err != nil {\n\t\treturn err\n\t}\n\tvar cursor *Cursor\n\tcursor, err = newCursor(bCursor, mb.client.bsonOpts, mb.client.registry,\n\n\t\t// This op doesn't return a cursor to the user, so setting the client\n\t\t// timeout should be a no-op.\n\t\twithCursorOptionClientTimeout(mb.client.timeout))\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer cursor.Close(ctx)\n\n\tok := true\n\tfor cursor.Next(ctx) {\n\t\tvar cur cursorInfo\n\t\terr = cursor.Decode(&cur)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif int(cur.Idx) >= len(mb.cursorHandlers) {\n\t\t\tcontinue\n\t\t}\n\t\tok = mb.cursorHandlers[int(cur.Idx)](&cur, cursor.Current) && ok\n\t}\n\terr = cursor.Err()\n\tif err != nil {\n\t\treturn err\n\t}\n\tif mb.ordered && (writeCmdErr.WriteConcernError != nil || !ok || !res.Ok || res.NErrors > 0) {\n\t\treturn ClientBulkWriteException{\n\t\t\tWriteConcernErrors: mb.writeConcernErrors,\n\t\t\tWriteErrors:        mb.writeErrors,\n\t\t\tPartialResult:      mb.result,\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (mb *modelBatches) appendDeleteResult(cur *cursorInfo, raw bson.Raw) bool {\n\tidx := int(cur.Idx) + mb.offset\n\tif err := cur.extractError(); err != nil {\n\t\terr.Raw = raw\n\t\tif mb.writeErrors == nil {\n\t\t\tmb.writeErrors = make(map[int]WriteError)\n\t\t}\n\t\tmb.writeErrors[idx] = *err\n\t\treturn false\n\t}\n\n\tif mb.result.Acknowledged {\n\t\tif mb.result.DeleteResults == nil {\n\t\t\tmb.result.DeleteResults = make(map[int]ClientBulkWriteDeleteResult)\n\t\t}\n\t\tmb.result.DeleteResults[idx] = ClientBulkWriteDeleteResult{int64(cur.N)}\n\t}\n\n\treturn true\n}\n\nfunc (mb *modelBatches) appendInsertResult(cur *cursorInfo, raw bson.Raw) bool {\n\tidx := int(cur.Idx) + mb.offset\n\tif err := cur.extractError(); err != nil {\n\t\terr.Raw = raw\n\t\tif mb.writeErrors == nil {\n\t\t\tmb.writeErrors = make(map[int]WriteError)\n\t\t}\n\t\tmb.writeErrors[idx] = *err\n\t\treturn false\n\t}\n\n\tif mb.result.Acknowledged {\n\t\tif mb.result.InsertResults == nil {\n\t\t\tmb.result.InsertResults = make(map[int]ClientBulkWriteInsertResult)\n\t\t}\n\t\tmb.result.InsertResults[idx] = ClientBulkWriteInsertResult{mb.newIDMap[idx]}\n\t}\n\n\treturn true\n}\n\nfunc (mb *modelBatches) appendUpdateResult(cur *cursorInfo, raw bson.Raw) bool {\n\tidx := int(cur.Idx) + mb.offset\n\tif err := cur.extractError(); err != nil {\n\t\terr.Raw = raw\n\t\tif mb.writeErrors == nil {\n\t\t\tmb.writeErrors = make(map[int]WriteError)\n\t\t}\n\t\tmb.writeErrors[idx] = *err\n\t\treturn false\n\t}\n\n\tif mb.result.Acknowledged {\n\t\tif mb.result.UpdateResults == nil {\n\t\t\tmb.result.UpdateResults = make(map[int]ClientBulkWriteUpdateResult)\n\t\t}\n\t\tresult := ClientBulkWriteUpdateResult{\n\t\t\tMatchedCount: int64(cur.N),\n\t\t}\n\t\tif cur.NModified != nil {\n\t\t\tresult.ModifiedCount = int64(*cur.NModified)\n\t\t}\n\t\tif cur.Upserted != nil {\n\t\t\tresult.UpsertedID = cur.Upserted.ID\n\t\t}\n\t\tmb.result.UpdateResults[idx] = result\n\t}\n\n\treturn true\n}\n\ntype clientInsertDoc struct {\n\tnamespace int\n\tdocument  any\n}\n\nfunc (d *clientInsertDoc) marshal(bsonOpts *options.BSONOptions, registry *bson.Registry) (any, bsoncore.Document, error) {\n\tuidx, doc := bsoncore.AppendDocumentStart(nil)\n\n\tdoc = bsoncore.AppendInt32Element(doc, \"insert\", int32(d.namespace))\n\tf, err := marshal(d.document, bsonOpts, registry)\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\tvar id any\n\tf, id, err = ensureID(f, bson.NilObjectID, bsonOpts, registry)\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\tdoc = bsoncore.AppendDocumentElement(doc, \"document\", f)\n\tdoc, err = bsoncore.AppendDocumentEnd(doc, uidx)\n\treturn id, doc, err\n}\n\ntype clientUpdateDoc struct {\n\tnamespace      int\n\tfilter         any\n\tupdate         any\n\thint           any\n\tarrayFilters   []any\n\tcollation      *options.Collation\n\tsort           any\n\tupsert         *bool\n\tmulti          bool\n\tcheckDollarKey bool\n}\n\nfunc (d *clientUpdateDoc) marshal(bsonOpts *options.BSONOptions, registry *bson.Registry) (bsoncore.Document, error) {\n\tuidx, doc := bsoncore.AppendDocumentStart(nil)\n\n\tdoc = bsoncore.AppendInt32Element(doc, \"update\", int32(d.namespace))\n\n\tif d.filter == nil {\n\t\treturn nil, fmt.Errorf(\"update filter cannot be nil\")\n\t}\n\tf, err := marshal(d.filter, bsonOpts, registry)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdoc = bsoncore.AppendDocumentElement(doc, \"filter\", f)\n\n\tu, err := marshalUpdateValue(d.update, bsonOpts, registry, d.checkDollarKey)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdoc = bsoncore.AppendValueElement(doc, \"updateMods\", u)\n\tdoc = bsoncore.AppendBooleanElement(doc, \"multi\", d.multi)\n\n\tif d.arrayFilters != nil {\n\t\treg := registry\n\t\tarr, err := marshalValue(d.arrayFilters, bsonOpts, reg)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tdoc = bsoncore.AppendArrayElement(doc, \"arrayFilters\", arr.Data)\n\t}\n\n\tif d.collation != nil {\n\t\tdoc = bsoncore.AppendDocumentElement(doc, \"collation\", toDocument(d.collation))\n\t}\n\n\tif d.upsert != nil {\n\t\tdoc = bsoncore.AppendBooleanElement(doc, \"upsert\", *d.upsert)\n\t}\n\n\tif d.hint != nil {\n\t\tif isUnorderedMap(d.hint) {\n\t\t\treturn nil, ErrMapForOrderedArgument{\"hint\"}\n\t\t}\n\t\thintVal, err := marshalValue(d.hint, bsonOpts, registry)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tdoc = bsoncore.AppendValueElement(doc, \"hint\", hintVal)\n\t}\n\n\tif d.sort != nil {\n\t\tif isUnorderedMap(d.sort) {\n\t\t\treturn nil, ErrMapForOrderedArgument{\"sort\"}\n\t\t}\n\t\tsortVal, err := marshalValue(d.sort, bsonOpts, registry)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tdoc = bsoncore.AppendValueElement(doc, \"sort\", sortVal)\n\t}\n\n\treturn bsoncore.AppendDocumentEnd(doc, uidx)\n}\n\ntype clientDeleteDoc struct {\n\tnamespace int\n\tfilter    any\n\tcollation *options.Collation\n\thint      any\n\tmulti     bool\n}\n\nfunc (d *clientDeleteDoc) marshal(bsonOpts *options.BSONOptions, registry *bson.Registry) (bsoncore.Document, error) {\n\tdidx, doc := bsoncore.AppendDocumentStart(nil)\n\n\tdoc = bsoncore.AppendInt32Element(doc, \"delete\", int32(d.namespace))\n\n\tif d.filter == nil {\n\t\treturn nil, fmt.Errorf(\"delete filter cannot be nil\")\n\t}\n\tf, err := marshal(d.filter, bsonOpts, registry)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdoc = bsoncore.AppendDocumentElement(doc, \"filter\", f)\n\tdoc = bsoncore.AppendBooleanElement(doc, \"multi\", d.multi)\n\n\tif d.collation != nil {\n\t\tdoc = bsoncore.AppendDocumentElement(doc, \"collation\", toDocument(d.collation))\n\t}\n\tif d.hint != nil {\n\t\tif isUnorderedMap(d.hint) {\n\t\t\treturn nil, ErrMapForOrderedArgument{\"hint\"}\n\t\t}\n\t\thintVal, err := marshalValue(d.hint, bsonOpts, registry)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tdoc = bsoncore.AppendValueElement(doc, \"hint\", hintVal)\n\t}\n\treturn bsoncore.AppendDocumentEnd(doc, didx)\n}\n"
  },
  {
    "path": "mongo/client_bulk_write_models.go",
    "content": "// Copyright (C) MongoDB, Inc. 2024-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage mongo\n\nimport (\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n)\n\n// ClientWriteModel is an interface implemented by models that can be used in a client-level BulkWrite operation. Each\n// ClientWriteModel represents a write.\n//\n// This interface is implemented by ClientDeleteOneModel, ClientDeleteManyModel, ClientInsertOneModel,\n// ClientReplaceOneModel, ClientUpdateOneModel, and ClientUpdateManyModel. Custom implementations of this interface must\n// not be used.\ntype ClientWriteModel interface {\n\tclientWriteModel()\n}\n\n// ClientInsertOneModel is used to insert a single document in a client-level BulkWrite operation.\n//\n// See corresponding setter methods for documentation.\ntype ClientInsertOneModel struct {\n\tDocument any\n}\n\n// NewClientInsertOneModel creates a new ClientInsertOneModel.\nfunc NewClientInsertOneModel() *ClientInsertOneModel {\n\treturn &ClientInsertOneModel{}\n}\n\nfunc (*ClientInsertOneModel) clientWriteModel() {}\n\n// SetDocument specifies the document to be inserted. The document cannot be nil. If it does not have an _id field when\n// transformed into BSON, one will be added automatically to the marshalled document. The original document will not be\n// modified.\nfunc (iom *ClientInsertOneModel) SetDocument(doc any) *ClientInsertOneModel {\n\tiom.Document = doc\n\treturn iom\n}\n\n// ClientUpdateOneModel is used to update at most one document in a client-level BulkWrite operation.\n//\n// See corresponding setter methods for documentation.\ntype ClientUpdateOneModel struct {\n\tCollation    *options.Collation\n\tUpsert       *bool\n\tFilter       any\n\tUpdate       any\n\tArrayFilters []any\n\tHint         any\n\tSort         any\n}\n\n// NewClientUpdateOneModel creates a new ClientUpdateOneModel.\nfunc NewClientUpdateOneModel() *ClientUpdateOneModel {\n\treturn &ClientUpdateOneModel{}\n}\n\nfunc (*ClientUpdateOneModel) clientWriteModel() {}\n\n// SetHint specifies the index to use for the operation. This should either be the index name as a string or the index\n// specification as a document. The default value is nil, which means that no hint will be sent.\nfunc (uom *ClientUpdateOneModel) SetHint(hint any) *ClientUpdateOneModel {\n\tuom.Hint = hint\n\treturn uom\n}\n\n// SetFilter specifies a filter to use to select the document to update. The filter must be a document containing query\n// operators. It cannot be nil. If the filter matches multiple documents, one will be selected from the matching\n// documents.\nfunc (uom *ClientUpdateOneModel) SetFilter(filter any) *ClientUpdateOneModel {\n\tuom.Filter = filter\n\treturn uom\n}\n\n// SetUpdate specifies the modifications to be made to the selected document. The value must be a document containing\n// update operators (https://www.mongodb.com/docs/manual/reference/operator/update/). It cannot be nil or empty.\nfunc (uom *ClientUpdateOneModel) SetUpdate(update any) *ClientUpdateOneModel {\n\tuom.Update = update\n\treturn uom\n}\n\n// SetArrayFilters specifies a set of filters to determine which elements should be modified when updating an array\n// field.\nfunc (uom *ClientUpdateOneModel) SetArrayFilters(filters []any) *ClientUpdateOneModel {\n\tuom.ArrayFilters = filters\n\treturn uom\n}\n\n// SetCollation specifies a collation to use for string comparisons. The default is nil, meaning no collation will be\n// used.\nfunc (uom *ClientUpdateOneModel) SetCollation(collation *options.Collation) *ClientUpdateOneModel {\n\tuom.Collation = collation\n\treturn uom\n}\n\n// SetUpsert specifies whether or not a new document should be inserted if no document matching the filter is found. If\n// an upsert is performed, the _id of the upserted document can be retrieved from the UpdateResults field of the\n// ClientBulkWriteResult.\nfunc (uom *ClientUpdateOneModel) SetUpsert(upsert bool) *ClientUpdateOneModel {\n\tuom.Upsert = &upsert\n\treturn uom\n}\n\n// SetSort specifies which document the operation updates if the query matches multiple documents. The first document\n// matched by the sort order will be updated. This option is only valid for MongoDB versions >= 8.0. The sort parameter\n// is evaluated sequentially, so the driver will return an error if it is a multi-key map (which is unordeded). The\n// default value is nil.\nfunc (uom *ClientUpdateOneModel) SetSort(sort any) *ClientUpdateOneModel {\n\tuom.Sort = sort\n\treturn uom\n}\n\n// ClientUpdateManyModel is used to update multiple documents in a client-level BulkWrite operation.\n//\n// See corresponding setter methods for documentation.\ntype ClientUpdateManyModel struct {\n\tCollation    *options.Collation\n\tUpsert       *bool\n\tFilter       any\n\tUpdate       any\n\tArrayFilters []any\n\tHint         any\n}\n\n// NewClientUpdateManyModel creates a new ClientUpdateManyModel.\nfunc NewClientUpdateManyModel() *ClientUpdateManyModel {\n\treturn &ClientUpdateManyModel{}\n}\n\nfunc (*ClientUpdateManyModel) clientWriteModel() {}\n\n// SetHint specifies the index to use for the operation. This should either be the index name as a string or the index\n// specification as a document. The default value is nil, which means that no hint will be sent.\nfunc (umm *ClientUpdateManyModel) SetHint(hint any) *ClientUpdateManyModel {\n\tumm.Hint = hint\n\treturn umm\n}\n\n// SetFilter specifies a filter to use to select documents to update. The filter must be a document containing query\n// operators. It cannot be nil.\nfunc (umm *ClientUpdateManyModel) SetFilter(filter any) *ClientUpdateManyModel {\n\tumm.Filter = filter\n\treturn umm\n}\n\n// SetUpdate specifies the modifications to be made to the selected documents. The value must be a document containing\n// update operators (https://www.mongodb.com/docs/manual/reference/operator/update/). It cannot be nil or empty.\nfunc (umm *ClientUpdateManyModel) SetUpdate(update any) *ClientUpdateManyModel {\n\tumm.Update = update\n\treturn umm\n}\n\n// SetArrayFilters specifies a set of filters to determine which elements should be modified when updating an array\n// field.\nfunc (umm *ClientUpdateManyModel) SetArrayFilters(filters []any) *ClientUpdateManyModel {\n\tumm.ArrayFilters = filters\n\treturn umm\n}\n\n// SetCollation specifies a collation to use for string comparisons. The default is nil, meaning no collation will be\n// used.\nfunc (umm *ClientUpdateManyModel) SetCollation(collation *options.Collation) *ClientUpdateManyModel {\n\tumm.Collation = collation\n\treturn umm\n}\n\n// SetUpsert specifies whether or not a new document should be inserted if no document matching the filter is found. If\n// an upsert is performed, the _id of the upserted document can be retrieved from the UpdateResults field of the\n// ClientBulkWriteResult.\nfunc (umm *ClientUpdateManyModel) SetUpsert(upsert bool) *ClientUpdateManyModel {\n\tumm.Upsert = &upsert\n\treturn umm\n}\n\n// ClientReplaceOneModel is used to replace at most one document in a client-level BulkWrite operation.\n//\n// See corresponding setter methods for documentation.\ntype ClientReplaceOneModel struct {\n\tCollation   *options.Collation\n\tUpsert      *bool\n\tFilter      any\n\tReplacement any\n\tHint        any\n\tSort        any\n}\n\n// NewClientReplaceOneModel creates a new ClientReplaceOneModel.\nfunc NewClientReplaceOneModel() *ClientReplaceOneModel {\n\treturn &ClientReplaceOneModel{}\n}\n\nfunc (*ClientReplaceOneModel) clientWriteModel() {}\n\n// SetHint specifies the index to use for the operation. This should either be the index name as a string or the index\n// specification as a document. The default value is nil, which means that no hint will be sent.\nfunc (rom *ClientReplaceOneModel) SetHint(hint any) *ClientReplaceOneModel {\n\trom.Hint = hint\n\treturn rom\n}\n\n// SetFilter specifies a filter to use to select the document to replace. The filter must be a document containing query\n// operators. It cannot be nil. If the filter matches multiple documents, one will be selected from the matching\n// documents.\nfunc (rom *ClientReplaceOneModel) SetFilter(filter any) *ClientReplaceOneModel {\n\trom.Filter = filter\n\treturn rom\n}\n\n// SetReplacement specifies a document that will be used to replace the selected document. It cannot be nil and cannot\n// contain any update operators (https://www.mongodb.com/docs/manual/reference/operator/update/).\nfunc (rom *ClientReplaceOneModel) SetReplacement(rep any) *ClientReplaceOneModel {\n\trom.Replacement = rep\n\treturn rom\n}\n\n// SetCollation specifies a collation to use for string comparisons. The default is nil, meaning no collation will be\n// used.\nfunc (rom *ClientReplaceOneModel) SetCollation(collation *options.Collation) *ClientReplaceOneModel {\n\trom.Collation = collation\n\treturn rom\n}\n\n// SetUpsert specifies whether or not the replacement document should be inserted if no document matching the filter is\n// found. If an upsert is performed, the _id of the upserted document can be retrieved from the UpdateResults field of the\n// BulkWriteResult.\nfunc (rom *ClientReplaceOneModel) SetUpsert(upsert bool) *ClientReplaceOneModel {\n\trom.Upsert = &upsert\n\treturn rom\n}\n\n// SetSort specifies which document the operation replaces if the query matches multiple documents. The first document\n// matched by the sort order will be replaced. This option is only valid for MongoDB versions >= 8.0. The sort parameter\n// is evaluated sequentially, so the driver will return an error if it is a multi-key map (which is unordeded). The\n// default value is nil.\nfunc (rom *ClientReplaceOneModel) SetSort(sort any) *ClientReplaceOneModel {\n\trom.Sort = sort\n\treturn rom\n}\n\n// ClientDeleteOneModel is used to delete at most one document in a client-level BulkWriteOperation.\n//\n// See corresponding setter methods for documentation.\ntype ClientDeleteOneModel struct {\n\tFilter    any\n\tCollation *options.Collation\n\tHint      any\n}\n\n// NewClientDeleteOneModel creates a new ClientDeleteOneModel.\nfunc NewClientDeleteOneModel() *ClientDeleteOneModel {\n\treturn &ClientDeleteOneModel{}\n}\n\nfunc (*ClientDeleteOneModel) clientWriteModel() {}\n\n// SetFilter specifies a filter to use to select the document to delete. The filter must be a document containing query\n// operators. It cannot be nil. If the filter matches multiple documents, one will be selected from the matching\n// documents.\nfunc (dom *ClientDeleteOneModel) SetFilter(filter any) *ClientDeleteOneModel {\n\tdom.Filter = filter\n\treturn dom\n}\n\n// SetCollation specifies a collation to use for string comparisons. The default is nil, meaning no collation will be\n// used.\nfunc (dom *ClientDeleteOneModel) SetCollation(collation *options.Collation) *ClientDeleteOneModel {\n\tdom.Collation = collation\n\treturn dom\n}\n\n// SetHint specifies the index to use for the operation. This should either be the index name as a string or the index\n// specification as a document. The default value is nil, which means that no hint will be sent.\nfunc (dom *ClientDeleteOneModel) SetHint(hint any) *ClientDeleteOneModel {\n\tdom.Hint = hint\n\treturn dom\n}\n\n// ClientDeleteManyModel is used to delete multiple documents in a client-level BulkWrite operation.\n//\n// See corresponding setter methods for documentation.\ntype ClientDeleteManyModel struct {\n\tFilter    any\n\tCollation *options.Collation\n\tHint      any\n}\n\n// NewClientDeleteManyModel creates a new ClientDeleteManyModel.\nfunc NewClientDeleteManyModel() *ClientDeleteManyModel {\n\treturn &ClientDeleteManyModel{}\n}\n\nfunc (*ClientDeleteManyModel) clientWriteModel() {}\n\n// SetFilter specifies a filter to use to select documents to delete. The filter must be a document containing query\n// operators. It cannot be nil.\nfunc (dmm *ClientDeleteManyModel) SetFilter(filter any) *ClientDeleteManyModel {\n\tdmm.Filter = filter\n\treturn dmm\n}\n\n// SetCollation specifies a collation to use for string comparisons. The default is nil, meaning no collation will be\n// used.\nfunc (dmm *ClientDeleteManyModel) SetCollation(collation *options.Collation) *ClientDeleteManyModel {\n\tdmm.Collation = collation\n\treturn dmm\n}\n\n// SetHint specifies the index to use for the operation. This should either be the index name as a string or the index\n// specification as a document. The default value is nil, which means that no hint will be sent.\nfunc (dmm *ClientDeleteManyModel) SetHint(hint any) *ClientDeleteManyModel {\n\tdmm.Hint = hint\n\treturn dmm\n}\n"
  },
  {
    "path": "mongo/client_bulk_write_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2024-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage mongo\n\nimport (\n\t\"testing\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/require\"\n)\n\nfunc TestBatches(t *testing.T) {\n\tt.Parallel()\n\n\tbatches := &modelBatches{\n\t\twritePairs: make([]clientBulkWritePair, 2),\n\t}\n\tbatches.AdvanceBatches(3)\n\tsize := batches.Size()\n\tassert.Equal(t, 0, size, \"expected: %d, got: %d\", 1, size)\n}\n\nfunc TestAppendBatchSequence(t *testing.T) {\n\tt.Parallel()\n\n\tnewBatches := func(t *testing.T) *modelBatches {\n\t\tclient, err := newClient()\n\t\trequire.NoError(t, err, \"NewClient error: %v\", err)\n\t\treturn &modelBatches{\n\t\t\tclient: client,\n\t\t\twritePairs: []clientBulkWritePair{\n\t\t\t\t{\"ns0\", nil},\n\t\t\t\t{\"ns1\", &ClientInsertOneModel{\n\t\t\t\t\tDocument: bson.D{{\"foo\", 42}},\n\t\t\t\t}},\n\t\t\t\t{\"ns2\", &ClientReplaceOneModel{\n\t\t\t\t\tFilter:      bson.D{{\"foo\", \"bar\"}},\n\t\t\t\t\tReplacement: bson.D{{\"foo\", \"baz\"}},\n\t\t\t\t}},\n\t\t\t\t{\"ns1\", &ClientDeleteOneModel{\n\t\t\t\t\tFilter: bson.D{{\"qux\", \"quux\"}},\n\t\t\t\t}},\n\t\t\t},\n\t\t\toffset: 1,\n\t\t\tresult: &ClientBulkWriteResult{\n\t\t\t\tAcknowledged: true,\n\t\t\t},\n\t\t}\n\t}\n\tt.Run(\"test appendBatches\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tbatches := newBatches(t)\n\t\tconst limitBigEnough = 16_000\n\t\tn, _, err := batches.AppendBatchSequence(nil, 4, limitBigEnough)\n\t\trequire.NoError(t, err, \"AppendBatchSequence error: %v\", err)\n\t\trequire.Equal(t, 3, n, \"expected %d appendings, got: %d\", 3, n)\n\n\t\t_ = batches.cursorHandlers[0](&cursorInfo{Ok: true, Idx: 0}, nil)\n\t\t_ = batches.cursorHandlers[1](&cursorInfo{Ok: true, Idx: 1}, nil)\n\t\t_ = batches.cursorHandlers[2](&cursorInfo{Ok: true, Idx: 2}, nil)\n\n\t\tins, ok := batches.result.InsertResults[1]\n\t\tassert.True(t, ok, \"expected an insert results\")\n\t\tassert.NotNil(t, ins.InsertedID, \"expected an ID\")\n\n\t\t_, ok = batches.result.UpdateResults[2]\n\t\tassert.True(t, ok, \"expected an insert results\")\n\n\t\t_, ok = batches.result.DeleteResults[3]\n\t\tassert.True(t, ok, \"expected an delete results\")\n\t})\n\tt.Run(\"test appendBatches with maxCount\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tbatches := newBatches(t)\n\t\tconst limitBigEnough = 16_000\n\t\tn, _, err := batches.AppendBatchSequence(nil, 2, limitBigEnough)\n\t\trequire.NoError(t, err, \"AppendBatchSequence error: %v\", err)\n\t\trequire.Equal(t, 2, n, \"expected %d appendings, got: %d\", 2, n)\n\n\t\t_ = batches.cursorHandlers[0](&cursorInfo{Ok: true, Idx: 0}, nil)\n\t\t_ = batches.cursorHandlers[1](&cursorInfo{Ok: true, Idx: 1}, nil)\n\n\t\tins, ok := batches.result.InsertResults[1]\n\t\tassert.True(t, ok, \"expected an insert results\")\n\t\tassert.NotNil(t, ins.InsertedID, \"expected an ID\")\n\n\t\t_, ok = batches.result.UpdateResults[2]\n\t\tassert.True(t, ok, \"expected an insert results\")\n\n\t\t_, ok = batches.result.DeleteResults[3]\n\t\tassert.False(t, ok, \"expected an delete results\")\n\t})\n\tt.Run(\"test appendBatches with totalSize\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tbatches := newBatches(t)\n\t\tconst limit = 1200 // > ( 166 first two batches + 1000 overhead )\n\t\tn, _, err := batches.AppendBatchSequence(nil, 4, limit)\n\t\trequire.NoError(t, err, \"AppendBatchSequence error: %v\", err)\n\t\trequire.Equal(t, 2, n, \"expected %d appendings, got: %d\", 2, n)\n\n\t\t_ = batches.cursorHandlers[0](&cursorInfo{Ok: true, Idx: 0}, nil)\n\t\t_ = batches.cursorHandlers[1](&cursorInfo{Ok: true, Idx: 1}, nil)\n\n\t\tins, ok := batches.result.InsertResults[1]\n\t\tassert.True(t, ok, \"expected an insert results\")\n\t\tassert.NotNil(t, ins.InsertedID, \"expected an ID\")\n\n\t\t_, ok = batches.result.UpdateResults[2]\n\t\tassert.True(t, ok, \"expected an insert results\")\n\n\t\t_, ok = batches.result.DeleteResults[3]\n\t\tassert.False(t, ok, \"expected an delete results\")\n\t})\n}\n"
  },
  {
    "path": "mongo/client_encryption.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage mongo\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"strings\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/mongoutil\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/mongocrypt\"\n\tmcopts \"go.mongodb.org/mongo-driver/v2/x/mongo/driver/mongocrypt/options\"\n)\n\n// ClientEncryption is used to create data keys and explicitly encrypt and decrypt BSON values.\ntype ClientEncryption struct {\n\tcrypt          driver.Crypt\n\tkeyVaultClient *Client\n\tkeyVaultColl   *Collection\n\tclosed         bool\n}\n\n// NewClientEncryption creates a new ClientEncryption instance configured with the given options.\nfunc NewClientEncryption(keyVaultClient *Client, opts ...options.Lister[options.ClientEncryptionOptions]) (*ClientEncryption, error) {\n\tif keyVaultClient == nil {\n\t\treturn nil, errors.New(\"keyVaultClient must not be nil\")\n\t}\n\n\tce := &ClientEncryption{\n\t\tkeyVaultClient: keyVaultClient,\n\t}\n\tcea, err := mongoutil.NewOptions(opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// create keyVaultColl\n\tdb, coll := splitNamespace(cea.KeyVaultNamespace)\n\tce.keyVaultColl = ce.keyVaultClient.Database(db).Collection(coll, keyVaultCollOpts)\n\n\tkmsProviders, err := marshal(cea.KmsProviders, nil, nil)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"error creating KMS providers map: %w\", err)\n\t}\n\n\tmc, err := mongocrypt.NewMongoCrypt(&mcopts.MongoCryptOptions{\n\t\tKmsProviders: kmsProviders,\n\t\t// Explicitly disable loading the crypt_shared library for the Crypt used for\n\t\t// ClientEncryption because it's only needed for AutoEncryption and we don't expect users to\n\t\t// have the crypt_shared library installed if they're using ClientEncryption.\n\t\tCryptSharedLibDisabled: true,\n\t\tHTTPClient:             cea.HTTPClient,\n\t\tKeyExpiration:          cea.KeyExpiration,\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// create Crypt\n\tkr := keyRetriever{coll: ce.keyVaultColl}\n\tcir := collInfoRetriever{client: ce.keyVaultClient}\n\tce.crypt = driver.NewCrypt(&driver.CryptOptions{\n\t\tMongoCrypt: mc,\n\t\tKeyFn:      kr.cryptKeys,\n\t\tCollInfoFn: cir.cryptCollInfo,\n\t\tTLSConfig:  cea.TLSConfig,\n\t})\n\n\treturn ce, nil\n}\n\n// CreateEncryptedCollection creates a new collection for Queryable Encryption with the help of automatic generation of new encryption data keys for null keyIds.\n// It returns the created collection and the encrypted fields document used to create it.\nfunc (ce *ClientEncryption) CreateEncryptedCollection(ctx context.Context,\n\tdb *Database, coll string, createOpts options.Lister[options.CreateCollectionOptions],\n\tkmsProvider string, masterKey any,\n) (*Collection, bson.M, error) {\n\tif ce.closed {\n\t\treturn nil, nil, ErrClientDisconnected\n\t}\n\n\tif createOpts == nil {\n\t\treturn nil, nil, errors.New(\"nil CreateCollectionOptions\")\n\t}\n\n\tcreateArgs, err := mongoutil.NewOptions[options.CreateCollectionOptions](createOpts)\n\tif err != nil {\n\t\treturn nil, nil, fmt.Errorf(\"failed to construct options from builder: %w\", err)\n\t}\n\n\tef := createArgs.EncryptedFields\n\tif ef == nil {\n\t\treturn nil, nil, errors.New(\"no EncryptedFields defined for the collection\")\n\t}\n\n\tefBSON, err := marshal(ef, db.bsonOpts, db.registry)\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\tr := bson.NewDocumentReader(bytes.NewReader(efBSON))\n\tdec := bson.NewDecoder(r)\n\tdec.DefaultDocumentM()\n\tvar m bson.M\n\terr = dec.Decode(&m)\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\n\tif v, ok := m[\"fields\"]; ok {\n\t\tif fields, ok := v.(bson.A); ok {\n\t\t\tfor _, field := range fields {\n\t\t\t\tif f, ok := field.(bson.M); !ok {\n\t\t\t\t\tcontinue\n\t\t\t\t} else if v, ok := f[\"keyId\"]; ok && v == nil {\n\t\t\t\t\tdkOpts := options.DataKey()\n\t\t\t\t\tif masterKey != nil {\n\t\t\t\t\t\tdkOpts.SetMasterKey(masterKey)\n\t\t\t\t\t}\n\t\t\t\t\tkeyid, err := ce.CreateDataKey(ctx, kmsProvider, dkOpts)\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\tcreateArgs.EncryptedFields = m\n\t\t\t\t\t\treturn nil, m, err\n\t\t\t\t\t}\n\t\t\t\t\tf[\"keyId\"] = keyid\n\t\t\t\t}\n\t\t\t}\n\t\t\tcreateArgs.EncryptedFields = m\n\t\t}\n\t}\n\n\tupdatedCreateOpts := mongoutil.NewOptionsLister(createArgs, nil)\n\terr = db.CreateCollection(ctx, coll, updatedCreateOpts)\n\tif err != nil {\n\t\treturn nil, m, err\n\t}\n\treturn db.Collection(coll), m, nil\n}\n\n// AddKeyAltName adds a keyAltName to the keyAltNames array of the key document in the key vault collection with the\n// given UUID (BSON binary subtype 0x04). Returns the previous version of the key document.\nfunc (ce *ClientEncryption) AddKeyAltName(ctx context.Context, id bson.Binary, keyAltName string) *SingleResult {\n\tif ce.closed {\n\t\treturn &SingleResult{err: ErrClientDisconnected}\n\t}\n\n\tfilter := bsoncore.NewDocumentBuilder().AppendBinary(\"_id\", id.Subtype, id.Data).Build()\n\tkeyAltNameDoc := bsoncore.NewDocumentBuilder().AppendString(\"keyAltNames\", keyAltName).Build()\n\tupdate := bsoncore.NewDocumentBuilder().AppendDocument(\"$addToSet\", keyAltNameDoc).Build()\n\treturn ce.keyVaultColl.FindOneAndUpdate(ctx, filter, update)\n}\n\n// CreateDataKey creates a new key document and inserts into the key vault collection. Returns the _id of the created\n// document as a UUID (BSON binary subtype 0x04).\nfunc (ce *ClientEncryption) CreateDataKey(\n\tctx context.Context,\n\tkmsProvider string,\n\topts ...options.Lister[options.DataKeyOptions],\n) (bson.Binary, error) {\n\tif ce.closed {\n\t\treturn bson.Binary{}, ErrClientDisconnected\n\t}\n\n\targs, err := mongoutil.NewOptions[options.DataKeyOptions](opts...)\n\tif err != nil {\n\t\treturn bson.Binary{}, fmt.Errorf(\"failed to construct options from builder: %w\", err)\n\t}\n\n\tco := &mcopts.DataKeyOptions{\n\t\tKeyAltNames: args.KeyAltNames,\n\t\tKeyMaterial: args.KeyMaterial,\n\t}\n\tif args.MasterKey != nil {\n\t\tkeyDoc, err := marshal(\n\t\t\targs.MasterKey,\n\t\t\tce.keyVaultClient.bsonOpts,\n\t\t\tce.keyVaultClient.registry)\n\t\tif err != nil {\n\t\t\treturn bson.Binary{}, err\n\t\t}\n\t\tco.MasterKey = keyDoc\n\t}\n\n\t// create data key document\n\tdataKeyDoc, err := ce.crypt.CreateDataKey(ctx, kmsProvider, co)\n\tif err != nil {\n\t\treturn bson.Binary{}, err\n\t}\n\n\t// insert key into key vault\n\t_, err = ce.keyVaultColl.InsertOne(ctx, dataKeyDoc)\n\tif err != nil {\n\t\treturn bson.Binary{}, err\n\t}\n\n\tsubtype, data := bson.Raw(dataKeyDoc).Lookup(\"_id\").Binary()\n\treturn bson.Binary{Subtype: subtype, Data: data}, nil\n}\n\n// transformExplicitEncryptionOptions creates explicit encryption options to be passed to libmongocrypt.\nfunc transformExplicitEncryptionOptions(opts ...options.Lister[options.EncryptOptions]) (*mcopts.ExplicitEncryptionOptions, error) {\n\targs, err := mongoutil.NewOptions[options.EncryptOptions](opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\ttransformed := &mcopts.ExplicitEncryptionOptions{\n\t\tKeyID:            args.KeyID,\n\t\tKeyAltName:       args.KeyAltName,\n\t\tAlgorithm:        args.Algorithm,\n\t\tQueryType:        args.QueryType,\n\t\tContentionFactor: args.ContentionFactor,\n\t}\n\n\tif args.RangeOptions != nil {\n\t\trangeArgs, err := mongoutil.NewOptions[options.RangeOptions](args.RangeOptions)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tvar transformedRange mcopts.ExplicitRangeOptions\n\t\tif rangeArgs.Min != nil {\n\t\t\ttransformedRange.Min = &bsoncore.Value{Type: bsoncore.Type(rangeArgs.Min.Type), Data: rangeArgs.Min.Value}\n\t\t}\n\t\tif rangeArgs.Max != nil {\n\t\t\ttransformedRange.Max = &bsoncore.Value{Type: bsoncore.Type(rangeArgs.Max.Type), Data: rangeArgs.Max.Value}\n\t\t}\n\t\tif rangeArgs.Precision != nil {\n\t\t\ttransformedRange.Precision = rangeArgs.Precision\n\t\t}\n\t\tif rangeArgs.Sparsity != nil {\n\t\t\ttransformedRange.Sparsity = rangeArgs.Sparsity\n\t\t}\n\t\tif rangeArgs.TrimFactor != nil {\n\t\t\ttransformedRange.TrimFactor = rangeArgs.TrimFactor\n\t\t}\n\t\ttransformed.RangeOptions = &transformedRange\n\t}\n\tif args.TextOptions != nil {\n\t\ttextArgs, err := mongoutil.NewOptions[options.TextOptions](args.TextOptions)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\ttransformedText := mcopts.ExplicitTextOptions{\n\t\t\tCaseSensitive:      textArgs.CaseSensitive,\n\t\t\tDiacriticSensitive: textArgs.DiacriticSensitive,\n\t\t}\n\t\tif textArgs.Substring != nil {\n\t\t\tsubstringOpts := mcopts.SubstringOptions(*textArgs.Substring)\n\t\t\ttransformedText.Substring = &substringOpts\n\t\t}\n\t\tif textArgs.Prefix != nil {\n\t\t\tprefixOpts := mcopts.PrefixOptions(*textArgs.Prefix)\n\t\t\ttransformedText.Prefix = &prefixOpts\n\t\t}\n\t\tif textArgs.Suffix != nil {\n\t\t\tsuffixOpts := mcopts.SuffixOptions(*textArgs.Suffix)\n\t\t\ttransformedText.Suffix = &suffixOpts\n\t\t}\n\t\ttransformed.SetTextOptions(transformedText)\n\t}\n\treturn transformed, nil\n}\n\n// Encrypt encrypts a BSON value with the given key and algorithm. Returns an encrypted value (BSON binary of subtype 6).\nfunc (ce *ClientEncryption) Encrypt(\n\tctx context.Context,\n\tval bson.RawValue,\n\topts ...options.Lister[options.EncryptOptions],\n) (bson.Binary, error) {\n\tif ce.closed {\n\t\treturn bson.Binary{}, ErrClientDisconnected\n\t}\n\n\ttransformed, err := transformExplicitEncryptionOptions(opts...)\n\tif err != nil {\n\t\treturn bson.Binary{}, err\n\t}\n\tsubtype, data, err := ce.crypt.EncryptExplicit(ctx, bsoncore.Value{Type: bsoncore.Type(val.Type), Data: val.Value}, transformed)\n\tif err != nil {\n\t\treturn bson.Binary{}, err\n\t}\n\treturn bson.Binary{Subtype: subtype, Data: data}, nil\n}\n\n// EncryptExpression encrypts an expression to query a range index.\n// On success, `result` is populated with the resulting BSON document.\n// `expr` is expected to be a BSON document of one of the following forms:\n// 1. A Match Expression of this form:\n// {$and: [{<field>: {$gt: <value1>}}, {<field>: {$lt: <value2> }}]}\n// 2. An Aggregate Expression of this form:\n// {$and: [{$gt: [<fieldpath>, <value1>]}, {$lt: [<fieldpath>, <value2>]}]\n// $gt may also be $gte. $lt may also be $lte.\n// Only supported for queryType \"range\"\nfunc (ce *ClientEncryption) EncryptExpression(ctx context.Context, expr any, result any, opts ...options.Lister[options.EncryptOptions]) error {\n\tif ce.closed {\n\t\treturn ErrClientDisconnected\n\t}\n\n\ttransformed, err := transformExplicitEncryptionOptions(opts...)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\texprDoc, err := marshal(expr, nil, nil)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tencryptedExprDoc, err := ce.crypt.EncryptExplicitExpression(ctx, exprDoc, transformed)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif raw, ok := result.(*bson.Raw); ok {\n\t\t// Avoid the cost of Unmarshal.\n\t\t*raw = bson.Raw(encryptedExprDoc)\n\t\treturn nil\n\t}\n\terr = bson.Unmarshal([]byte(encryptedExprDoc), result)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\n// Decrypt decrypts an encrypted value (BSON binary of subtype 6) and returns the original BSON value.\nfunc (ce *ClientEncryption) Decrypt(ctx context.Context, val bson.Binary) (bson.RawValue, error) {\n\tif ce.closed {\n\t\treturn bson.RawValue{}, ErrClientDisconnected\n\t}\n\n\tdecrypted, err := ce.crypt.DecryptExplicit(ctx, val.Subtype, val.Data)\n\tif err != nil {\n\t\treturn bson.RawValue{}, err\n\t}\n\n\treturn bson.RawValue{Type: bson.Type(decrypted.Type), Value: decrypted.Data}, nil\n}\n\n// Close cleans up any resources associated with the ClientEncryption instance. This includes disconnecting the\n// key-vault Client instance.\nfunc (ce *ClientEncryption) Close(ctx context.Context) error {\n\tif ce.closed {\n\t\treturn ErrClientDisconnected\n\t}\n\n\tce.crypt.Close()\n\terr := ce.keyVaultClient.Disconnect(ctx)\n\tif err == nil {\n\t\tce.closed = true\n\t}\n\treturn err\n}\n\n// DeleteKey removes the key document with the given UUID (BSON binary subtype 0x04) from the key vault collection.\n// Returns the result of the internal deleteOne() operation on the key vault collection.\nfunc (ce *ClientEncryption) DeleteKey(ctx context.Context, id bson.Binary) (*DeleteResult, error) {\n\tif ce.closed {\n\t\treturn nil, ErrClientDisconnected\n\t}\n\n\tfilter := bsoncore.NewDocumentBuilder().AppendBinary(\"_id\", id.Subtype, id.Data).Build()\n\treturn ce.keyVaultColl.DeleteOne(ctx, filter)\n}\n\n// GetKeyByAltName returns a key document in the key vault collection with the given keyAltName.\nfunc (ce *ClientEncryption) GetKeyByAltName(ctx context.Context, keyAltName string) *SingleResult {\n\tif ce.closed {\n\t\treturn &SingleResult{err: ErrClientDisconnected}\n\t}\n\n\tfilter := bsoncore.NewDocumentBuilder().AppendString(\"keyAltNames\", keyAltName).Build()\n\treturn ce.keyVaultColl.FindOne(ctx, filter)\n}\n\n// GetKey finds a single key document with the given UUID (BSON binary subtype 0x04). Returns the result of the\n// internal find() operation on the key vault collection.\nfunc (ce *ClientEncryption) GetKey(ctx context.Context, id bson.Binary) *SingleResult {\n\tif ce.closed {\n\t\treturn &SingleResult{err: ErrClientDisconnected}\n\t}\n\n\tfilter := bsoncore.NewDocumentBuilder().AppendBinary(\"_id\", id.Subtype, id.Data).Build()\n\treturn ce.keyVaultColl.FindOne(ctx, filter)\n}\n\n// GetKeys finds all documents in the key vault collection. Returns the result of the internal find() operation on the\n// key vault collection.\nfunc (ce *ClientEncryption) GetKeys(ctx context.Context) (*Cursor, error) {\n\tif ce.closed {\n\t\treturn nil, ErrClientDisconnected\n\t}\n\n\treturn ce.keyVaultColl.Find(ctx, bson.D{})\n}\n\n// RemoveKeyAltName removes a keyAltName from the keyAltNames array of the key document in the key vault collection with\n// the given UUID (BSON binary subtype 0x04). Returns the previous version of the key document.\nfunc (ce *ClientEncryption) RemoveKeyAltName(ctx context.Context, id bson.Binary, keyAltName string) *SingleResult {\n\tif ce.closed {\n\t\treturn &SingleResult{err: ErrClientDisconnected}\n\t}\n\n\tfilter := bsoncore.NewDocumentBuilder().AppendBinary(\"_id\", id.Subtype, id.Data).Build()\n\tupdate := bson.A{bson.D{{\"$set\", bson.D{{\"keyAltNames\", bson.D{{\"$cond\", bson.A{bson.D{{\n\t\t\"$eq\",\n\t\tbson.A{\"$keyAltNames\", bson.A{keyAltName}},\n\t}}, \"$$REMOVE\", bson.D{{\n\t\t\"$filter\",\n\t\tbson.D{{\"input\", \"$keyAltNames\"}, {\"cond\", bson.D{{\"$ne\", bson.A{\"$$this\", keyAltName}}}}},\n\t}}}}}}}}}}\n\treturn ce.keyVaultColl.FindOneAndUpdate(ctx, filter, update)\n}\n\n// setRewrapManyDataKeyWriteModels will prepare the WriteModel slice for a bulk updating rewrapped documents.\nfunc setRewrapManyDataKeyWriteModels(rewrappedDocuments []bsoncore.Document, writeModels *[]WriteModel) error {\n\tconst idKey = \"_id\"\n\tconst keyMaterial = \"keyMaterial\"\n\tconst masterKey = \"masterKey\"\n\n\tif writeModels == nil {\n\t\treturn fmt.Errorf(\"writeModels pointer not set for location referenced\")\n\t}\n\n\t// Append a slice of WriteModel with the update document per each rewrappedDoc _id filter.\n\tfor _, rewrappedDocument := range rewrappedDocuments {\n\t\t// Prepare the new master key for update.\n\t\tmasterKeyValue, err := rewrappedDocument.LookupErr(masterKey)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tmasterKeyDoc := masterKeyValue.Document()\n\n\t\t// Prepare the new material key for update.\n\t\tkeyMaterialValue, err := rewrappedDocument.LookupErr(keyMaterial)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tkeyMaterialSubtype, keyMaterialData := keyMaterialValue.Binary()\n\t\tkeyMaterialBinary := bson.Binary{Subtype: keyMaterialSubtype, Data: keyMaterialData}\n\n\t\t// Prepare the _id filter for documents to update.\n\t\tid, err := rewrappedDocument.LookupErr(idKey)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tidSubtype, idData, ok := id.BinaryOK()\n\t\tif !ok {\n\t\t\treturn fmt.Errorf(\"expected to assert %q as binary, got type %T\", idKey, id)\n\t\t}\n\t\tbinaryID := bson.Binary{Subtype: idSubtype, Data: idData}\n\n\t\t// Append the mutable document to the slice for bulk update.\n\t\t*writeModels = append(*writeModels, NewUpdateOneModel().\n\t\t\tSetFilter(bson.D{{idKey, binaryID}}).\n\t\t\tSetUpdate(\n\t\t\t\tbson.D{\n\t\t\t\t\t{\"$set\", bson.D{{keyMaterial, keyMaterialBinary}, {masterKey, masterKeyDoc}}},\n\t\t\t\t\t{\"$currentDate\", bson.D{{\"updateDate\", true}}},\n\t\t\t\t},\n\t\t\t))\n\t}\n\treturn nil\n}\n\n// RewrapManyDataKey decrypts and encrypts all matching data keys with a possibly new masterKey value. For all\n// matching documents, this method will overwrite the \"masterKey\", \"updateDate\", and \"keyMaterial\". On error, some\n// matching data keys may have been rewrapped.\n// libmongocrypt 1.5.2 is required. An error is returned if the detected version of libmongocrypt is less than 1.5.2.\nfunc (ce *ClientEncryption) RewrapManyDataKey(\n\tctx context.Context,\n\tfilter any,\n\topts ...options.Lister[options.RewrapManyDataKeyOptions],\n) (*RewrapManyDataKeyResult, error) {\n\t// libmongocrypt versions 1.5.0 and 1.5.1 have a severe bug in RewrapManyDataKey.\n\t// Check if the version string starts with 1.5.0 or 1.5.1. This accounts for pre-release versions, like 1.5.0-rc0.\n\tif ce.closed {\n\t\treturn nil, ErrClientDisconnected\n\t}\n\n\tlibmongocryptVersion := mongocrypt.Version()\n\tif strings.HasPrefix(libmongocryptVersion, \"1.5.0\") || strings.HasPrefix(libmongocryptVersion, \"1.5.1\") {\n\t\treturn nil, fmt.Errorf(\"RewrapManyDataKey requires libmongocrypt 1.5.2 or newer. Detected version: %v\", libmongocryptVersion)\n\t}\n\n\tif ctx == nil {\n\t\tctx = context.Background()\n\t}\n\n\targs, err := mongoutil.NewOptions[options.RewrapManyDataKeyOptions](opts...)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to construct options from builder: %w\", err)\n\t}\n\n\t// Transfer rmdko options to /x/ package options to publish the mongocrypt feed.\n\tco := &mcopts.RewrapManyDataKeyOptions{\n\t\tProvider: args.Provider,\n\t}\n\tif args.MasterKey != nil {\n\t\tkeyDoc, err := marshal(\n\t\t\targs.MasterKey,\n\t\t\tce.keyVaultClient.bsonOpts,\n\t\t\tce.keyVaultClient.registry)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tco.MasterKey = keyDoc\n\t}\n\n\t// Prepare the filters and rewrap the data key using mongocrypt.\n\tfilterdoc, err := marshal(filter, ce.keyVaultClient.bsonOpts, ce.keyVaultClient.registry)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\trewrappedDocuments, err := ce.crypt.RewrapDataKey(ctx, filterdoc, co)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif len(rewrappedDocuments) == 0 {\n\t\t// If there are no documents to rewrap, then do nothing.\n\t\treturn new(RewrapManyDataKeyResult), nil\n\t}\n\n\t// Prepare the WriteModel slice for bulk updating the rewrapped data keys.\n\tmodels := []WriteModel{}\n\tif err := setRewrapManyDataKeyWriteModels(rewrappedDocuments, &models); err != nil {\n\t\treturn nil, err\n\t}\n\n\tbulkWriteResults, err := ce.keyVaultColl.BulkWrite(ctx, models)\n\treturn &RewrapManyDataKeyResult{BulkWriteResult: bulkWriteResults}, err\n}\n\n// splitNamespace takes a namespace in the form \"database.collection\" and returns (database name, collection name)\nfunc splitNamespace(ns string) (string, string) {\n\tfirstDot := strings.Index(ns, \".\")\n\tif firstDot == -1 {\n\t\treturn \"\", ns\n\t}\n\n\treturn ns[:firstDot], ns[firstDot+1:]\n}\n"
  },
  {
    "path": "mongo/client_encryption_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2024-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\n//go:build cse\n\npackage mongo\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/mongocrypt\"\n)\n\nfunc TestClientEncryption_ErrClientDisconnected(t *testing.T) {\n\tt.Parallel()\n\n\tclient, _ := Connect(options.Client().ApplyURI(\"mongodb://test\"))\n\tcrypt := driver.NewCrypt(&driver.CryptOptions{MongoCrypt: &mongocrypt.MongoCrypt{}})\n\n\tce := &ClientEncryption{keyVaultClient: client, crypt: crypt}\n\t_ = ce.Close(context.Background())\n\n\tt.Run(\"CreateEncryptedCollection\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\t_, _, err := ce.CreateEncryptedCollection(context.Background(), nil, \"\", options.CreateCollection(), \"\", nil)\n\t\tassert.ErrorIs(t, err, ErrClientDisconnected)\n\t})\n\tt.Run(\"AddKeyAltName\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\terr := ce.AddKeyAltName(context.Background(), bson.Binary{}, \"\").err\n\t\tassert.ErrorIs(t, err, ErrClientDisconnected)\n\t})\n\tt.Run(\"CreateDataKey\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\t_, err := ce.CreateDataKey(context.Background(), \"\", options.DataKey())\n\t\tassert.ErrorIs(t, err, ErrClientDisconnected)\n\t})\n\tt.Run(\"Encrypt\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\t_, err := ce.Encrypt(context.Background(), bson.RawValue{}, options.Encrypt())\n\t\tassert.ErrorIs(t, err, ErrClientDisconnected)\n\t})\n\tt.Run(\"EncryptExpression\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\terr := ce.EncryptExpression(context.Background(), nil, nil, options.Encrypt())\n\t\tassert.ErrorIs(t, err, ErrClientDisconnected)\n\t})\n\tt.Run(\"Decrypt\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\t_, err := ce.Decrypt(context.Background(), bson.Binary{})\n\t\tassert.ErrorIs(t, err, ErrClientDisconnected)\n\t})\n\tt.Run(\"Close\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\terr := ce.Close(context.Background())\n\t\tassert.ErrorIs(t, err, ErrClientDisconnected)\n\t})\n\tt.Run(\"DeleteKey\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\t_, err := ce.DeleteKey(context.Background(), bson.Binary{})\n\t\tassert.ErrorIs(t, err, ErrClientDisconnected)\n\t})\n\tt.Run(\"GetKeyByAltName\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\terr := ce.GetKeyByAltName(context.Background(), \"\").err\n\t\tassert.ErrorIs(t, err, ErrClientDisconnected)\n\t})\n\tt.Run(\"GetKey\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\terr := ce.GetKey(context.Background(), bson.Binary{}).err\n\t\tassert.ErrorIs(t, err, ErrClientDisconnected)\n\t})\n\tt.Run(\"GetKeys\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\t_, err := ce.GetKeys(context.Background())\n\t\tassert.ErrorIs(t, err, ErrClientDisconnected)\n\t})\n\tt.Run(\"RemoveKeyAltName\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\terr := ce.RemoveKeyAltName(context.Background(), bson.Binary{}, \"\").err\n\t\tassert.ErrorIs(t, err, ErrClientDisconnected)\n\t})\n\tt.Run(\"RewrapManyDataKey\", func(t *testing.T) {\n\t\tt.Parallel()\n\t\t_, err := ce.RewrapManyDataKey(context.Background(), nil, options.RewrapManyDataKey())\n\t\tassert.ErrorIs(t, err, ErrClientDisconnected)\n\t})\n}\n"
  },
  {
    "path": "mongo/client_examples_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage mongo_test\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/readpref\"\n)\n\nfunc ExampleClient() {\n\t// Create a Client and execute a ListDatabases operation.\n\n\tclient, err := mongo.Connect(\n\t\toptions.Client().ApplyURI(\"mongodb://localhost:27017\"))\n\tif err != nil {\n\t\tlog.Panic(err)\n\t}\n\tdefer func() {\n\t\tif err = client.Disconnect(context.TODO()); err != nil {\n\t\t\tlog.Panic(err)\n\t\t}\n\t}()\n\n\tcollection := client.Database(\"db\").Collection(\"coll\")\n\tresult, err := collection.InsertOne(context.TODO(), bson.D{{\"x\", 1}})\n\tif err != nil {\n\t\tlog.Panic(err)\n\t}\n\tfmt.Printf(\"inserted ID: %v\\n\", result.InsertedID)\n}\n\nfunc ExampleConnect_ping() {\n\t// Create a Client to a MongoDB server and use Ping to verify that the\n\t// server is running.\n\n\tclientOpts := options.Client().ApplyURI(\"mongodb://localhost:27017\")\n\tclient, err := mongo.Connect(clientOpts)\n\tif err != nil {\n\t\tlog.Panic(err)\n\t}\n\tdefer func() {\n\t\tif err = client.Disconnect(context.TODO()); err != nil {\n\t\t\tlog.Panic(err)\n\t\t}\n\t}()\n\n\t// Call Ping to verify that the deployment is up and the Client was\n\t// configured successfully. As mentioned in the Ping documentation, this\n\t// reduces application resiliency as the server may be temporarily\n\t// unavailable when Ping is called.\n\tif err = client.Ping(context.TODO(), readpref.Primary()); err != nil {\n\t\tlog.Panic(err)\n\t}\n}\n\nfunc ExampleConnect_replicaSet() {\n\t// Create and connect a Client to a replica set deployment.\n\t// Given this URI, the Go driver will first communicate with localhost:27017\n\t// and use the response to discover any other members in the replica set.\n\t// The URI in this example specifies multiple members of the replica set to\n\t// increase resiliency as one of the members may be down when the\n\t// application is started.\n\n\tclientOpts := options.Client().ApplyURI(\n\t\t\"mongodb://localhost:27017,localhost:27018/?replicaSet=replset\")\n\tclient, err := mongo.Connect(clientOpts)\n\tif err != nil {\n\t\tlog.Panic(err)\n\t}\n\t_ = client\n}\n\nfunc ExampleConnect_sharded() {\n\t// Create and connect a Client to a sharded deployment.\n\t// The URI for a sharded deployment should specify the mongos servers that\n\t// the application wants to send messages to.\n\n\tclientOpts := options.Client().ApplyURI(\n\t\t\"mongodb://localhost:27017,localhost:27018\")\n\tclient, err := mongo.Connect(clientOpts)\n\tif err != nil {\n\t\tlog.Panic(err)\n\t}\n\t_ = client\n}\n\nfunc ExampleConnect_sRV() {\n\t// Create and connect a Client using an SRV record.\n\t// SRV records allow administrators to configure a single domain to return a\n\t// list of host names. The driver will resolve SRV records prefixed with\n\t// \"_mongodb_tcp\" and use the returned host names to build its view of the\n\t// deployment.\n\t// See https://www.mongodb.com/docs/manual/reference/connection-string/ for more\n\t// information about SRV. Full support for SRV records with sharded clusters\n\t// requires driver version 1.1.0 or higher.\n\n\tclientOpts := options.Client().ApplyURI(\"mongodb+srv://mongodb.example.com\")\n\tclient, err := mongo.Connect(clientOpts)\n\tif err != nil {\n\t\tlog.Panic(err)\n\t}\n\t_ = client\n}\n\nfunc ExampleConnect_direct() {\n\t// Create a direct connection to a host. The driver will send all requests\n\t// to that host and will not automatically discover other hosts in the\n\t// deployment.\n\n\tclientOpts := options.Client().ApplyURI(\n\t\t\"mongodb://localhost:27017/?connect=direct\")\n\tclient, err := mongo.Connect(clientOpts)\n\tif err != nil {\n\t\tlog.Panic(err)\n\t}\n\t_ = client\n}\n\nfunc ExampleConnect_sCRAM() {\n\t// Configure a Client with SCRAM authentication\n\t// (https://www.mongodb.com/docs/manual/core/security-scram/).\n\t// The default authentication database for SCRAM is \"admin\". This can be\n\t// configured via the authSource query parameter in the URI or the\n\t// AuthSource field in the options.Credential struct. SCRAM is the default\n\t// auth mechanism so specifying a mechanism is not required.\n\n\t// To configure auth via URI instead of a Credential, use\n\t// \"mongodb://user:password@localhost:27017\".\n\tcredential := options.Credential{\n\t\tUsername: \"user\",\n\t\tPassword: \"password\",\n\t}\n\tclientOpts := options.Client().ApplyURI(\"mongodb://localhost:27017\").\n\t\tSetAuth(credential)\n\tclient, err := mongo.Connect(clientOpts)\n\tif err != nil {\n\t\tlog.Panic(err)\n\t}\n\t_ = client\n}\n\nfunc ExampleConnect_x509() {\n\t// Configure a Client with X509 authentication\n\t// (https://www.mongodb.com/docs/manual/core/security-x.509/).\n\n\t// X509 can be configured with different sets of options in the connection\n\t// string:\n\t// 1. tlsCAFile (or SslCertificateAuthorityFile): Path to the file with\n\t// either a single or bundle of certificate authorities to be considered\n\t// trusted when making a TLS connection.\n\t// 2. tlsCertificateKeyFile (or SslClientCertificateKeyFile): Path to the\n\t// client certificate file or the client private key file. In the case that\n\t// both are needed, the files should be concatenated.\n\n\t// The SetAuth client option should also be used. The username field is\n\t// optional. If it is not specified, it will be extracted from the\n\t// certificate key file. The AuthSource is required to be $external.\n\n\tcaFilePath := \"path/to/cafile\"\n\tcertificateKeyFilePath := \"path/to/client-certificate\"\n\n\t// To configure auth via a URI instead of a Credential, append\n\t// \"&authMechanism=MONGODB-X509\" to the URI.\n\turi := \"mongodb://host:port/?tlsCAFile=%s&tlsCertificateKeyFile=%s\"\n\turi = fmt.Sprintf(uri, caFilePath, certificateKeyFilePath)\n\tcredential := options.Credential{\n\t\tAuthMechanism: \"MONGODB-X509\",\n\t}\n\tclientOpts := options.Client().ApplyURI(uri).SetAuth(credential)\n\n\tclient, err := mongo.Connect(clientOpts)\n\tif err != nil {\n\t\tlog.Panic(err)\n\t}\n\t_ = client\n}\n\nfunc ExampleConnect_pLAIN() {\n\t// Configure a Client with LDAP authentication\n\t// (https://www.mongodb.com/docs/manual/core/authentication-mechanisms-enterprise/#security-auth-ldap).\n\t// MongoDB Enterprise supports proxy authentication through an LDAP service\n\t// that can be used through the PLAIN authentication mechanism.\n\t// This auth mechanism sends the password in plaintext and therefore should\n\t// only be used with TLS connections.\n\n\t// To configure auth via a URI instead of a Credential, use\n\t// \"mongodb://ldap-user:ldap-pwd@localhost:27017/?authMechanism=PLAIN\".\n\tcredential := options.Credential{\n\t\tAuthMechanism: \"PLAIN\",\n\t\tUsername:      \"ldap-user\",\n\t\tPassword:      \"ldap-pwd\",\n\t}\n\tclientOpts := options.Client().ApplyURI(\"mongodb://localhost:27017\").\n\t\tSetAuth(credential)\n\n\tclient, err := mongo.Connect(clientOpts)\n\tif err != nil {\n\t\tlog.Panic(err)\n\t}\n\t_ = client\n}\n\nfunc ExampleConnect_kerberos() {\n\t// Configure a Client with GSSAPI/SSPI authentication (https://www.mongodb.com/docs/manual/core/kerberos/).\n\t// MongoDB Enterprise supports proxy authentication through a Kerberos\n\t// service. Using Kerberos authentication requires the \"gssapi\" build tag\n\t// and cgo support during compilation. The default service name for Kerberos\n\t// is \"mongodb\". This can be configured via the AuthMechanismProperties\n\t// field in the options.Credential struct or the authMechanismProperties URI\n\t// parameter.\n\n\t// For Linux, the libkrb5 library is required.\n\t// Users can authenticate in one of two ways:\n\t// 1. Use an explicit password. In this case, a password must be specified\n\t// in the URI or the options.Credential struct and no further setup is\n\t// required.\n\t// 2. Store authentication keys in keytab files. To do this, the kinit\n\t// binary should be used to initialize a credential cache for authenticating\n\t// the user principal. In this example, the invocation would be\n\t// \"kinit drivers@KERBEROS.EXAMPLE.COM\".\n\n\t// To configure auth via a URI instead of a Credential, use\n\t// \"mongodb://drivers%40KERBEROS.EXAMPLE.COM@mongo-server.example.com:27017/?authMechanism=GSSAPI\".\n\tcredential := options.Credential{\n\t\tAuthMechanism: \"GSSAPI\",\n\t\tUsername:      \"drivers@KERBEROS.EXAMPLE.COM\",\n\t}\n\turi := \"mongo-server.example.com:27017\"\n\tclientOpts := options.Client().ApplyURI(uri).SetAuth(credential)\n\n\tclient, err := mongo.Connect(clientOpts)\n\tif err != nil {\n\t\tlog.Panic(err)\n\t}\n\t_ = client\n}\n\nfunc ExampleConnect_aWS() {\n\t// Configure a Client with authentication using the MONGODB-AWS\n\t// authentication mechanism. Credentials for this mechanism can come from\n\t// one of four sources:\n\t//\n\t// 1. AWS IAM credentials (an access key ID and a secret access key)\n\t//\n\t// 2. Temporary AWS IAM credentials\n\t// (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp.html)\n\t// obtained from an AWS Security Token Service (STS) Assume Role request\n\t// (https://docs.aws.amazon.com/STS/latest/APIReference/API_AssumeRole.html)\n\t//\n\t// 3. AWS Lambda environment variables\n\t// (https://docs.aws.amazon.com/lambda/latest/dg/configuration-envvars.html#configuration-envvars-runtime)\n\t//\n\t// 4. Temporary AWS IAM credentials assigned to an EC2 instance or ECS task\n\t// (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_use_switch-role-ec2.html)\n\n\t// The order in which the driver searches for credentials is:\n\t//\n\t// 1. Credentials passed through the URI\n\t// 2. Environment variables\n\t// 3. ECS endpoint if and only if AWS_CONTAINER_CREDENTIALS_RELATIVE_URI is\n\t//    set\n\t// 4. EC2 endpoint\n\t//\n\t// The following examples set the appropriate credentials via the\n\t// ClientOptions.SetAuth method. All of these credentials can be specified\n\t// via the ClientOptions.ApplyURI method as well. If using ApplyURI, both\n\t// the username and password must be URL encoded (see net.URL.QueryEscape())\n\n\t// AWS IAM Credentials\n\n\t// Applications can authenticate using AWS IAM credentials by providing a\n\t// valid access key ID and secret access key pair as the username and\n\t// password, respectively.\n\tvar accessKeyID, secretAccessKey string\n\tawsCredential := options.Credential{\n\t\tAuthMechanism: \"MONGODB-AWS\",\n\t\tUsername:      accessKeyID,\n\t\tPassword:      secretAccessKey,\n\t}\n\tawsIAMClient, err := mongo.Connect(\n\t\toptions.Client().SetAuth(awsCredential))\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\t_ = awsIAMClient\n\n\t// AssumeRole\n\n\t// Applications can authenticate using temporary credentials returned from\n\t// an assume role request. These temporary credentials consist of an access\n\t// key ID, a secret access key, and a security token.\n\tvar sessionToken string\n\tassumeRoleCredential := options.Credential{\n\t\tAuthMechanism: \"MONGODB-AWS\",\n\t\tUsername:      accessKeyID,\n\t\tPassword:      secretAccessKey,\n\t\tAuthMechanismProperties: map[string]string{\n\t\t\t\"AWS_SESSION_TOKEN\": sessionToken,\n\t\t},\n\t}\n\tassumeRoleClient, err := mongo.Connect(\n\t\toptions.Client().SetAuth(assumeRoleCredential))\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\t_ = assumeRoleClient\n\n\t// AWS Lambda (Environment Variables)\n\n\t// When the username and password are not provided and the MONGODB-AWS\n\t// mechanism is set, the client will fallback to using the environment\n\t// variables AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, and AWS_SESSION_TOKEN\n\t// for the access key ID, secret access key, and session token,\n\t// respectively. These environment variables must not be URL encoded.\n\n\t// $ export AWS_ACCESS_KEY_ID=<accessKeyID>\n\t// $ export AWS_SECRET_ACCESS_KEY=<secretAccessKey>\n\t// $ export AWS_SESSION_TOKEN=<sessionToken>\n\tenvVariablesCredential := options.Credential{\n\t\tAuthMechanism: \"MONGODB-AWS\",\n\t}\n\tenvVariablesClient, err := mongo.Connect(\n\t\toptions.Client().SetAuth(envVariablesCredential))\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\t_ = envVariablesClient\n\n\t// ECS Container or EC2 Instance\n\n\t// Applications can authenticate from an ECS container or EC2 instance via\n\t// temporary credentials assigned to the machine. If using an ECS container,\n\t// the \"AWS_CONTAINER_CREDENTIALS_RELATIVE_URI\" environment variable must be\n\t// set to a non-empty value. The driver will query the ECS or EC2 endpoint\n\t// to obtain the relevant credentials.\n\tecCredential := options.Credential{\n\t\tAuthMechanism: \"MONGODB-AWS\",\n\t}\n\tecClient, err := mongo.Connect(options.Client().SetAuth(ecCredential))\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\t_ = ecClient\n}\n\nfunc ExampleConnect_stableAPI() {\n\t// Configure a Client with stable API.\n\t//\n\t// Stable API is a new feature in MongoDB 5.0 that allows user-selectable\n\t// API versions, subsets of MongoDB server semantics, to be declared on a\n\t// Client. During communication with a server, Clients with a declared API\n\t// version will force that server to behave in a manner compatible with the\n\t// API version. Declaring an API version on your Client can be used to\n\t// ensure consistent responses from a server, providing long term API\n\t// stability for an application.\n\t//\n\t// The declared API version is applied to all commands run through the\n\t// Client, including those sent through the generic RunCommand helper.\n\t// Specifying stable API options in the command document AND declaring\n\t// an API version on the Client is not supported and will lead to undefined\n\t// behavior. To run any command with a different API version or without\n\t// declaring one, create a separate Client that declares the appropriate API\n\t// version.\n\n\t// ServerAPIOptions must be declared with an API version. ServerAPIVersion1\n\t// is a constant equal to \"1\".\n\tserverAPI := options.ServerAPI(options.ServerAPIVersion1)\n\tserverAPIClient, err := mongo.Connect(\n\t\toptions.Client().SetServerAPIOptions(serverAPI))\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\t_ = serverAPIClient\n\n\t// ServerAPIOptions can be declared with a Strict option. Declaring a strict\n\t// API version will cause the MongoDB server to reject all commands that are\n\t// not part of the declared API version. This includes command options and\n\t// aggregation pipeline stages. For example, the following Distinct call\n\t// would fail because the distinct command is not part of API version 1:\n\tserverAPIStrict := options.ServerAPI(options.ServerAPIVersion1).\n\t\tSetStrict(true)\n\tserverAPIStrictClient, err := mongo.Connect(\n\t\toptions.Client().SetServerAPIOptions(serverAPIStrict))\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tcoll := serverAPIStrictClient.Database(\"db\").Collection(\"coll\")\n\t// Fails with error: (APIStrictError) Provided apiStrict:true, but the\n\t// command distinct is not in API Version 1\n\terr = coll.Distinct(context.TODO(), \"distinct\", bson.D{}).Err()\n\tlog.Println(err)\n\n\t// ServerAPIOptions can be declared with a DeprecationErrors option.\n\t// DeprecationErrors can be used to enable command failures when using\n\t// functionality that is deprecated in the declared API version. Note that\n\t// at the time of this writing, no deprecations in API version 1 exist.\n\tserverAPIDeprecation := options.ServerAPI(options.ServerAPIVersion1).\n\t\tSetDeprecationErrors(true)\n\tserverAPIDeprecationClient, err := mongo.Connect(\n\t\toptions.Client().SetServerAPIOptions(serverAPIDeprecation))\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\t_ = serverAPIDeprecationClient\n}\n\nfunc ExampleConnect_bSONOptions() {\n\t// Configure a client that customizes the BSON marshal and unmarshal\n\t// behavior.\n\n\t// Specify BSON options that cause the driver to fallback to \"json\"\n\t// struct tags if \"bson\" struct tags are missing, marshal nil Go maps as\n\t// empty BSON documents, and marshals nil Go slices as empty BSON\n\t// arrays.\n\tbsonOpts := &options.BSONOptions{\n\t\tUseJSONStructTags: true,\n\t\tNilMapAsEmpty:     true,\n\t\tNilSliceAsEmpty:   true,\n\t}\n\n\tclientOpts := options.Client().\n\t\tApplyURI(\"mongodb://localhost:27017\").\n\t\tSetBSONOptions(bsonOpts)\n\n\tclient, err := mongo.Connect(clientOpts)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tdefer func() {\n\t\tif err := client.Disconnect(context.TODO()); err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t}()\n\n\tcoll := client.Database(\"db\").Collection(\"coll\")\n\n\t// Define a struct that contains a map and a slice and uses \"json\" struct\n\t// tags to specify field names.\n\ttype myDocument struct {\n\t\tMyMap   map[string]any `json:\"a\"`\n\t\tMySlice []string       `json:\"b\"`\n\t}\n\n\t// Insert an instance of the struct with all empty fields. Expect the\n\t// resulting BSON document to have a structure like {\"a\": {}, \"b\": []}\n\t_, err = coll.InsertOne(context.TODO(), myDocument{})\n\tif err != nil {\n\t\tpanic(err)\n\t}\n}\n\nfunc ExampleConnect_oIDC() {\n\t// The `MONGODB-OIDC authentication mechanism` is available in MongoDB 7.0+\n\t// on Linux platforms.\n\t//\n\t// The MONGODB-OIDC mechanism authenticates using an OpenID Connect (OIDC)\n\t// access token. The driver supports OIDC for workload identity, defined as\n\t// an identity you assign to a software workload (such as an application,\n\t// service, script, or container) to authenticate and access other services\n\t// and resources.\n\t//\n\t// The driver also supports OIDC for workforce identity for a more secure\n\t// flow with a human in the loop.\n\n\t// Credentials can be configured through the MongoDB URI or as arguments in\n\t// the options.ClientOptions struct that is passed into the mongo.Connect\n\t// function.\n\n\t// Built-in Support\n\t// The driver has built-in support for Azure IMDS and GCP\n\t// IMDS environments.  Other environments are supported with `Custom\n\t// Callbacks`.\n\n\t// Azure IMDS\n\t// For an application running on an Azure VM or otherwise using the `Azure\n\t// Internal Metadata Service`, you can use the built-in support for Azure,\n\t// where \"<client_id>\" below is the client id of the Azure managed identity,\n\t// and ``<audience>`` is the url-encoded ``audience`` `configured on your\n\t// MongoDB deployment`.\n\t{\n\t\turi := os.Getenv(\"MONGODB_URI\")\n\t\tprops := map[string]string{\n\t\t\t\"ENVIRONMENT\":    \"azure\",\n\t\t\t\"TOKEN_RESOURCE\": \"<audience>\",\n\t\t}\n\t\topts := options.Client().ApplyURI(uri)\n\t\topts.SetAuth(\n\t\t\toptions.Credential{\n\t\t\t\tUsername:                \"<client_id>\",\n\t\t\t\tAuthMechanism:           \"MONGODB-OIDC\",\n\t\t\t\tAuthMechanismProperties: props,\n\t\t\t},\n\t\t)\n\t\tc, err := mongo.Connect(opts)\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t\tdefer func() { _ = c.Disconnect(context.TODO()) }()\n\t\t_, err = c.Database(\"test\").\n\t\t\tCollection(\"test\").\n\t\t\tInsertOne(context.TODO(), bson.D{})\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t}\n\n\t// If the application is running on an Azure VM and only one managed\n\t// identity is associated with the VM, \"username\" can be omitted.\n\n\t// GCP IMDS\n\n\t// For an application running on an GCP VM or otherwise using the `GCP\n\t// Internal Metadata Service`_, you can use the built-in support for GCP,\n\t// where \"<audience>\" below is the url-encoded \"audience\" `configured on\n\t// your MongoDB deployment`.\n\t{\n\t\turi := os.Getenv(\"MONGODB_URI\")\n\t\tprops := map[string]string{\n\t\t\t\"ENVIRONMENT\":    \"gcp\",\n\t\t\t\"TOKEN_RESOURCE\": \"<audience>\",\n\t\t}\n\t\topts := options.Client().ApplyURI(uri)\n\t\topts.SetAuth(\n\t\t\toptions.Credential{\n\t\t\t\tAuthMechanism:           \"MONGODB-OIDC\",\n\t\t\t\tAuthMechanismProperties: props,\n\t\t\t},\n\t\t)\n\t\tc, err := mongo.Connect(opts)\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t\tdefer func() { _ = c.Disconnect(context.TODO()) }()\n\t\t_, err = c.Database(\"test\").\n\t\t\tCollection(\"test\").\n\t\t\tInsertOne(context.TODO(), bson.D{})\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t}\n\n\t// Custom Callbacks\n\n\t// For environments that are not directly supported by the driver, you can\n\t// use options.OIDCCallback. Some examples are given below.\n\n\t// AWS EKS\n\n\t// For an EKS Cluster with a configured `IAM OIDC provider`, the token can\n\t// be read from a path given by the \"AWS_WEB_IDENTITY_TOKEN_FILE\"\n\t// environment variable.\n\t{\n\t\teksCallback := func(_ context.Context,\n\t\t\t_ *options.OIDCArgs,\n\t\t) (*options.OIDCCredential, error) {\n\t\t\taccessToken, err := os.ReadFile(\n\t\t\t\tos.Getenv(\"AWS_WEB_IDENTITY_TOKEN_FILE\"))\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\treturn &options.OIDCCredential{\n\t\t\t\tAccessToken: string(accessToken),\n\t\t\t}, nil\n\t\t}\n\t\turi := os.Getenv(\"MONGODB_URI\")\n\t\topts := options.Client().ApplyURI(uri)\n\t\topts.SetAuth(\n\t\t\toptions.Credential{\n\t\t\t\tAuthMechanism:       \"MONGODB-OIDC\",\n\t\t\t\tOIDCMachineCallback: eksCallback,\n\t\t\t},\n\t\t)\n\t\tc, err := mongo.Connect(opts)\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t\tdefer func() { _ = c.Disconnect(context.TODO()) }()\n\t\t_, err = c.Database(\"test\").\n\t\t\tCollection(\"test\").\n\t\t\tInsertOne(context.TODO(), bson.D{})\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t}\n\n\t// Other Azure Environments\n\n\t// For applications running on Azure Functions, App Service Environment\n\t// (ASE), or Azure Kubernetes Service (AKS), you can use the `azidentity\n\t// package`\n\t// (https://pkg.go.dev/github.com/Azure/azure-sdk-for-go/sdk/azidentity) to\n\t// fetch the credentials. In each case, the OIDCCallback function should\n\t// return the AccessToken from the azidentity package.\n\n\t// GCP GKE\n\n\t// For a Google Kubernetes Engine cluster with a `configured service\n\t// account`, the token can be read from the standard service account token\n\t// file location.\n\t{\n\t\tgkeCallback := func(_ context.Context,\n\t\t\t_ *options.OIDCArgs,\n\t\t) (*options.OIDCCredential, error) {\n\t\t\taccessToken, err := os.ReadFile(\n\t\t\t\t\"/var/run/secrets/kubernetes.io/serviceaccount/token\")\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\treturn &options.OIDCCredential{\n\t\t\t\tAccessToken: string(accessToken),\n\t\t\t}, nil\n\t\t}\n\t\turi := os.Getenv(\"MONGODB_URI\")\n\t\tprops := map[string]string{\n\t\t\t\"ENVIRONMENT\":    \"gcp\",\n\t\t\t\"TOKEN_RESOURCE\": \"<audience>\",\n\t\t}\n\t\topts := options.Client().ApplyURI(uri)\n\t\topts.SetAuth(\n\t\t\toptions.Credential{\n\t\t\t\tAuthMechanism:           \"MONGODB-OIDC\",\n\t\t\t\tAuthMechanismProperties: props,\n\t\t\t\tOIDCMachineCallback:     gkeCallback,\n\t\t\t},\n\t\t)\n\t\tc, err := mongo.Connect(opts)\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t\tdefer func() { _ = c.Disconnect(context.TODO()) }()\n\t\t_, err = c.Database(\"test\").\n\t\t\tCollection(\"test\").\n\t\t\tInsertOne(context.TODO(), bson.D{})\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t}\n\n\t// For workforce identity, the Client must be configured with the\n\t// OIDCHumanCallback rather than the OIDCMachineCallback. The\n\t// OIDCHumanCallback is used by the driver in a process that is two step. In\n\t// the first step, the driver retrieves the Identity Provider (IDP)\n\t// Information (IDPInfo) for the passed username. The OIDCHumanCallback then\n\t// needs to negotiate with the IDP in order to obtain an AccessToken,\n\t// possible RefreshToken, any timeouts, and return them, similar to the\n\t// OIDCMachineCallbacks seen above. See\n\t// https://docs.hidglobal.com/dev/auth-service/integration/openid-authentication-flows.html\n\t// for more information on various OIDC authentication flows.\n\t{\n\t\thumanCallback := func(ctx context.Context,\n\t\t\topts *options.OIDCArgs,\n\t\t) (*options.OIDCCredential, error) {\n\t\t\t// idpInfo passed from the driver by asking the MongoDB server for\n\t\t\t// the info configured for the username\n\t\t\tidpInfo := opts.IDPInfo\n\t\t\t// negotiateWithIDP must work with the IdP to obtain an access\n\t\t\t// token. In many cases this will involve opening a webbrowser or\n\t\t\t// providing a URL on the command line to a human-in-the-loop who\n\t\t\t// can give permissions to the IdP.\n\t\t\taccessToken, err := negotiateWithIDP(ctx, idpInfo.Issuer)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\treturn &options.OIDCCredential{\n\t\t\t\tAccessToken: accessToken,\n\t\t\t}, nil\n\t\t}\n\t\turi := os.Getenv(\"MONGODB_URI\")\n\t\tprops := map[string]string{\n\t\t\t\"ENVIRONMENT\":    \"gcp\",\n\t\t\t\"TOKEN_RESOURCE\": \"<audience>\",\n\t\t}\n\t\topts := options.Client().ApplyURI(uri)\n\t\topts.SetAuth(\n\t\t\toptions.Credential{\n\t\t\t\tAuthMechanism:           \"MONGODB-OIDC\",\n\t\t\t\tAuthMechanismProperties: props,\n\t\t\t\tOIDCHumanCallback:       humanCallback,\n\t\t\t},\n\t\t)\n\t\tc, err := mongo.Connect(opts)\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t\tdefer func() { _ = c.Disconnect(context.TODO()) }()\n\t\t_, err = c.Database(\"test\").\n\t\t\tCollection(\"test\").\n\t\t\tInsertOne(context.TODO(), bson.D{})\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t}\n\n\t// * MONGODB-OIDC authentication mechanism:\n\t// https://www.mongodb.com/docs/manual/core/security-oidc/\n\t// * OIDC Identity Provider Configuration:\n\t// https://www.mongodb.com/docs/manual/reference/parameters/#mongodb-parameter-param.oidcIdentityProviders\n\t// * Azure Internal Metadata Service:\n\t// https://learn.microsoft.com/en-us/azure/virtual-machines/instance-metadata-service\n\t// * GCP Internal Metadata Service:\n\t// https://cloud.google.com/compute/docs/metadata/querying-metadata\n\t// * IAM OIDC provider:\n\t// https://docs.aws.amazon.com/eks/latest/userguide/enable-iam-roles-for-service-accounts.html\n\t// * azure-identity package:\n\t// https://pkg.go.dev/github.com/Azure/azure-sdk-for-go/sdk/azidentity\n\t// * configured service account:\n\t// https://cloud.google.com/kubernetes-engine/docs/how-to/service-accounts\n}\n\nfunc negotiateWithIDP(_ context.Context, _ string) (string, error) {\n\treturn \"\", nil\n}\n"
  },
  {
    "path": "mongo/client_side_encryption_examples_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage mongo\n\nimport (\n\t\"context\"\n\t\"crypto/rand\"\n\t\"encoding/base64\"\n\t\"fmt\"\n\t\"log\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n)\n\nfunc Example_clientSideEncryption() {\n\t// This would have to be the same master key that was used to create the\n\t// encryption key.\n\tlocalKey := make([]byte, 96)\n\tif _, err := rand.Read(localKey); err != nil {\n\t\tlog.Panic(err)\n\t}\n\tkmsProviders := map[string]map[string]any{\n\t\t\"local\": {\n\t\t\t\"key\": localKey,\n\t\t},\n\t}\n\tkeyVaultNamespace := \"encryption.__keyVault\"\n\n\turi := \"mongodb://localhost:27017\"\n\tautoEncryptionOpts := options.AutoEncryption().\n\t\tSetKeyVaultNamespace(keyVaultNamespace).\n\t\tSetKmsProviders(kmsProviders)\n\tclientOpts := options.Client().\n\t\tApplyURI(uri).\n\t\tSetAutoEncryptionOptions(autoEncryptionOpts)\n\tclient, err := Connect(clientOpts)\n\tif err != nil {\n\t\tlog.Panicf(\"Connect error: %v\", err)\n\t}\n\tdefer func() {\n\t\tif err = client.Disconnect(context.TODO()); err != nil {\n\t\t\tlog.Panicf(\"Disconnect error: %v\", err)\n\t\t}\n\t}()\n\n\tcollection := client.Database(\"test\").Collection(\"coll\")\n\tif err := collection.Drop(context.TODO()); err != nil {\n\t\tlog.Panicf(\"Collection.Drop error: %v\", err)\n\t}\n\n\t_, err = collection.InsertOne(\n\t\tcontext.TODO(),\n\t\tbson.D{{\"encryptedField\", \"123456789\"}})\n\tif err != nil {\n\t\tlog.Panicf(\"InsertOne error: %v\", err)\n\t}\n\tres, err := collection.FindOne(context.TODO(), bson.D{}).Raw()\n\tif err != nil {\n\t\tlog.Panicf(\"FindOne error: %v\", err)\n\t}\n\tfmt.Println(res)\n}\n\nfunc Example_clientSideEncryptionCreateKey() {\n\tkeyVaultNamespace := \"encryption.__keyVault\"\n\turi := \"mongodb://localhost:27017\"\n\t// kmsProviders would have to be populated with the correct KMS provider\n\t// information before it's used.\n\tvar kmsProviders map[string]map[string]any\n\n\t// Create Client and ClientEncryption\n\tclientEncryptionOpts := options.ClientEncryption().\n\t\tSetKeyVaultNamespace(keyVaultNamespace).\n\t\tSetKmsProviders(kmsProviders)\n\tkeyVaultClient, err := Connect(options.Client().ApplyURI(uri))\n\tif err != nil {\n\t\tlog.Panicf(\"Connect error for keyVaultClient: %v\", err)\n\t}\n\tclientEnc, err := NewClientEncryption(keyVaultClient, clientEncryptionOpts)\n\tif err != nil {\n\t\tlog.Panicf(\"NewClientEncryption error: %v\", err)\n\t}\n\tdefer func() {\n\t\t// this will disconnect the keyVaultClient as well\n\t\tif err = clientEnc.Close(context.TODO()); err != nil {\n\t\t\tlog.Panicf(\"Close error: %v\", err)\n\t\t}\n\t}()\n\n\t// Create a new data key and encode it as base64\n\tdataKeyID, err := clientEnc.CreateDataKey(context.TODO(), \"local\")\n\tif err != nil {\n\t\tlog.Panicf(\"CreateDataKey error: %v\", err)\n\t}\n\tdataKeyBase64 := base64.StdEncoding.EncodeToString(dataKeyID.Data)\n\n\t// Create a JSON schema using the new data key. This schema could also be\n\t// written in a separate file and read in using I/O functions.\n\tschema := `{\n\t\t\"properties\": {\n\t\t\t\"encryptedField\": {\n\t\t\t\t\"encrypt\": {\n\t\t\t\t\t\"keyId\": [{\n\t\t\t\t\t\t\"$binary\": {\n\t\t\t\t\t\t\t\"base64\": \"%s\",\n\t\t\t\t\t\t\t\"subType\": \"04\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}],\n\t\t\t\t\t\"bsonType\": \"string\",\n\t\t\t\t\t\"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\"\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"bsonType\": \"object\"\n\t}`\n\tschema = fmt.Sprintf(schema, dataKeyBase64)\n\tvar schemaDoc bson.Raw\n\terr = bson.UnmarshalExtJSON([]byte(schema), true, &schemaDoc)\n\tif err != nil {\n\t\tlog.Panicf(\"UnmarshalExtJSON error: %v\", err)\n\t}\n\n\t// Configure a Client with auto encryption using the new schema\n\tdbName := \"test\"\n\tcollName := \"coll\"\n\tschemaMap := map[string]any{\n\t\tdbName + \".\" + collName: schemaDoc,\n\t}\n\tautoEncryptionOpts := options.AutoEncryption().\n\t\tSetKmsProviders(kmsProviders).\n\t\tSetKeyVaultNamespace(keyVaultNamespace).\n\t\tSetSchemaMap(schemaMap)\n\n\tclientOptions := options.Client().\n\t\tApplyURI(uri).\n\t\tSetAutoEncryptionOptions(autoEncryptionOpts)\n\tclient, err := Connect(clientOptions)\n\tif err != nil {\n\t\tlog.Panicf(\"Connect error for encrypted client: %v\", err)\n\t}\n\tdefer func() {\n\t\t_ = client.Disconnect(context.TODO())\n\t}()\n\n\t// Use client for operations.\n}\n\nfunc Example_explictEncryption() {\n\t// localMasterKey must be the same master key that was used to create the\n\t// encryption key.\n\tvar localMasterKey []byte\n\tkmsProviders := map[string]map[string]any{\n\t\t\"local\": {\n\t\t\t\"key\": localMasterKey,\n\t\t},\n\t}\n\n\t// The MongoDB namespace (db.collection) used to store the encryption data\n\t// keys.\n\tkeyVaultDBName, keyVaultCollName := \"encryption\", \"testKeyVault\"\n\tkeyVaultNamespace := keyVaultDBName + \".\" + keyVaultCollName\n\n\t// The Client used to read/write application data.\n\topts := options.Client().ApplyURI(\"mongodb://localhost:27017\")\n\tclient, err := Connect(opts)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tdefer func() { _ = client.Disconnect(context.TODO()) }()\n\n\t// Get a handle to the application collection and clear existing data.\n\tcoll := client.Database(\"test\").Collection(\"coll\")\n\t_ = coll.Drop(context.TODO())\n\n\t// Set up the key vault for this example.\n\tkeyVaultColl := client.Database(keyVaultDBName).Collection(keyVaultCollName)\n\t_ = keyVaultColl.Drop(context.TODO())\n\t// Ensure that two data keys cannot share the same keyAltName.\n\tkeyVaultIndex := IndexModel{\n\t\tKeys: bson.D{{\"keyAltNames\", 1}},\n\t\tOptions: options.Index().\n\t\t\tSetUnique(true).\n\t\t\tSetPartialFilterExpression(bson.D{\n\t\t\t\t{\"keyAltNames\", bson.D{\n\t\t\t\t\t{\"$exists\", true},\n\t\t\t\t}},\n\t\t\t}),\n\t}\n\t_, err = keyVaultColl.Indexes().CreateOne(context.TODO(), keyVaultIndex)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t// Create the ClientEncryption object to use for explicit\n\t// encryption/decryption. The Client passed to NewClientEncryption is used\n\t// to read/write to the key vault. This can be the same Client used by the\n\t// main application.\n\tclientEncryptionOpts := options.ClientEncryption().\n\t\tSetKmsProviders(kmsProviders).\n\t\tSetKeyVaultNamespace(keyVaultNamespace)\n\tclientEncryption, err := NewClientEncryption(client, clientEncryptionOpts)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tdefer func() { _ = clientEncryption.Close(context.TODO()) }()\n\n\t// Create a new data key for the encrypted field.\n\tdataKeyOpts := options.DataKey().\n\t\tSetKeyAltNames([]string{\"go_encryption_example\"})\n\tdataKeyID, err := clientEncryption.CreateDataKey(\n\t\tcontext.TODO(),\n\t\t\"local\",\n\t\tdataKeyOpts)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t// Create a bson.RawValue to encrypt and encrypt it using the key that was\n\t// just created.\n\trawValueType, rawValueData, err := bson.MarshalValue(\"123456789\")\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\trawValue := bson.RawValue{Type: rawValueType, Value: rawValueData}\n\tencryptionOpts := options.Encrypt().\n\t\tSetAlgorithm(\"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\").\n\t\tSetKeyID(dataKeyID)\n\tencryptedField, err := clientEncryption.Encrypt(\n\t\tcontext.TODO(),\n\t\trawValue,\n\t\tencryptionOpts)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t// Insert a document with the encrypted field and then find it.\n\t_, err = coll.InsertOne(\n\t\tcontext.TODO(),\n\t\tbson.D{{\"encryptedField\", encryptedField}})\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tvar foundDoc bson.M\n\terr = coll.FindOne(context.TODO(), bson.D{}).Decode(&foundDoc)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t// Decrypt the encrypted field in the found document.\n\tdecrypted, err := clientEncryption.Decrypt(\n\t\tcontext.TODO(),\n\t\tfoundDoc[\"encryptedField\"].(bson.Binary))\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tfmt.Printf(\"Decrypted value: %s\\n\", decrypted)\n}\n\nfunc Example_explictEncryptionWithAutomaticDecryption() {\n\t// Automatic encryption requires MongoDB 4.2 enterprise, but automatic\n\t// decryption is supported for all users.\n\n\t// localMasterKey must be the same master key that was used to create the\n\t// encryption key.\n\tvar localMasterKey []byte\n\tkmsProviders := map[string]map[string]any{\n\t\t\"local\": {\n\t\t\t\"key\": localMasterKey,\n\t\t},\n\t}\n\n\t// The MongoDB namespace (db.collection) used to store the encryption data\n\t// keys.\n\tkeyVaultDBName, keyVaultCollName := \"encryption\", \"testKeyVault\"\n\tkeyVaultNamespace := keyVaultDBName + \".\" + keyVaultCollName\n\n\t// Create the Client for reading/writing application data. Configure it with\n\t// BypassAutoEncryption=true to disable automatic encryption but keep\n\t// automatic decryption. Setting BypassAutoEncryption will also bypass\n\t// spawning mongocryptd in the driver.\n\tautoEncryptionOpts := options.AutoEncryption().\n\t\tSetKmsProviders(kmsProviders).\n\t\tSetKeyVaultNamespace(keyVaultNamespace).\n\t\tSetBypassAutoEncryption(true)\n\tclientOpts := options.Client().\n\t\tApplyURI(\"mongodb://localhost:27017\").\n\t\tSetAutoEncryptionOptions(autoEncryptionOpts)\n\tclient, err := Connect(clientOpts)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tdefer func() { _ = client.Disconnect(context.TODO()) }()\n\n\t// Get a handle to the application collection and clear existing data.\n\tcoll := client.Database(\"test\").Collection(\"coll\")\n\t_ = coll.Drop(context.TODO())\n\n\t// Set up the key vault for this example.\n\tkeyVaultColl := client.Database(keyVaultDBName).Collection(keyVaultCollName)\n\t_ = keyVaultColl.Drop(context.TODO())\n\t// Ensure that two data keys cannot share the same keyAltName.\n\tkeyVaultIndex := IndexModel{\n\t\tKeys: bson.D{{\"keyAltNames\", 1}},\n\t\tOptions: options.Index().\n\t\t\tSetUnique(true).\n\t\t\tSetPartialFilterExpression(bson.D{\n\t\t\t\t{\"keyAltNames\", bson.D{\n\t\t\t\t\t{\"$exists\", true},\n\t\t\t\t}},\n\t\t\t}),\n\t}\n\n\t_, err = keyVaultColl.Indexes().CreateOne(context.TODO(), keyVaultIndex)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t// Create the ClientEncryption object to use for explicit\n\t// encryption/decryption. The Client passed to NewClientEncryption is used\n\t// to read/write to the key vault. This can be the same Client used by the\n\t// main application.\n\tclientEncryptionOpts := options.ClientEncryption().\n\t\tSetKmsProviders(kmsProviders).\n\t\tSetKeyVaultNamespace(keyVaultNamespace)\n\tclientEncryption, err := NewClientEncryption(client, clientEncryptionOpts)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tdefer func() { _ = clientEncryption.Close(context.TODO()) }()\n\n\t// Create a new data key for the encrypted field.\n\tdataKeyOpts := options.DataKey().\n\t\tSetKeyAltNames([]string{\"go_encryption_example\"})\n\tdataKeyID, err := clientEncryption.CreateDataKey(\n\t\tcontext.TODO(),\n\t\t\"local\",\n\t\tdataKeyOpts)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t// Create a bson.RawValue to encrypt and encrypt it using the key that was\n\t// just created.\n\trawValueType, rawValueData, err := bson.MarshalValue(\"123456789\")\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\trawValue := bson.RawValue{Type: rawValueType, Value: rawValueData}\n\tencryptionOpts := options.Encrypt().\n\t\tSetAlgorithm(\"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\").\n\t\tSetKeyID(dataKeyID)\n\tencryptedField, err := clientEncryption.Encrypt(\n\t\tcontext.TODO(),\n\t\trawValue,\n\t\tencryptionOpts)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t// Insert a document with the encrypted field and then find it. The FindOne\n\t// call will automatically decrypt the field in the document.\n\t_, err = coll.InsertOne(\n\t\tcontext.TODO(),\n\t\tbson.D{{\"encryptedField\", encryptedField}})\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tvar foundDoc bson.M\n\terr = coll.FindOne(context.TODO(), bson.D{}).Decode(&foundDoc)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tfmt.Printf(\"Decrypted document: %v\\n\", foundDoc)\n}\n"
  },
  {
    "path": "mongo/client_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage mongo\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"math\"\n\t\"os\"\n\t\"testing\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/event\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/integtest\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/require\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/readconcern\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/readpref\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/writeconcern\"\n\t\"go.mongodb.org/mongo-driver/v2/tag\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/mongocrypt\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/topology\"\n)\n\nvar bgCtx = context.Background()\n\nfunc setupClient(opts ...*options.ClientOptions) *Client {\n\tif len(opts) == 0 {\n\t\tclientOpts := options.Client().ApplyURI(\"mongodb://localhost:27017\")\n\t\tintegtest.AddTestServerAPIVersion(clientOpts)\n\t\topts = append(opts, clientOpts)\n\t}\n\tclient, _ := Connect(opts...)\n\treturn client\n}\n\nfunc TestClient(t *testing.T) {\n\tt.Run(\"new client\", func(t *testing.T) {\n\t\tclient := setupClient()\n\t\tassert.NotNil(t, client.deployment, \"expected valid deployment, got nil\")\n\t})\n\tt.Run(\"database\", func(t *testing.T) {\n\t\tdbName := \"foo\"\n\t\tclient := setupClient()\n\t\tdb := client.Database(dbName)\n\t\tassert.Equal(t, dbName, db.Name(), \"expected db name %v, got %v\", dbName, db.Name())\n\t\tassert.Equal(t, client, db.Client(), \"expected client %v, got %v\", client, db.Client())\n\t})\n\tt.Run(\"replaceErrors for disconnected topology\", func(t *testing.T) {\n\t\tclient := setupClient()\n\n\t\ttopo, ok := client.deployment.(*topology.Topology)\n\t\trequire.True(t, ok, \"client deployment is not a topology\")\n\n\t\terr := topo.Disconnect(context.Background())\n\t\trequire.NoError(t, err)\n\n\t\t_, err = client.ListDatabases(bgCtx, bson.D{})\n\t\tassert.Equal(t, ErrClientDisconnected, err, \"expected error %v, got %v\", ErrClientDisconnected, err)\n\n\t\terr = client.Ping(bgCtx, nil)\n\t\tassert.Equal(t, ErrClientDisconnected, err, \"expected error %v, got %v\", ErrClientDisconnected, err)\n\n\t\terr = client.Disconnect(bgCtx)\n\t\tassert.Equal(t, ErrClientDisconnected, err, \"expected error %v, got %v\", ErrClientDisconnected, err)\n\n\t\t_, err = client.Watch(bgCtx, []bson.D{})\n\t\tassert.Equal(t, ErrClientDisconnected, err, \"expected error %v, got %v\", ErrClientDisconnected, err)\n\t})\n\tt.Run(\"nil document error\", func(t *testing.T) {\n\t\tclient := setupClient()\n\n\t\t_, err := client.Watch(bgCtx, nil)\n\t\twatchErr := errors.New(\"can only marshal slices and arrays into aggregation pipelines, but got invalid\")\n\t\tassert.Equal(t, watchErr, err, \"expected error %v, got %v\", watchErr, err)\n\n\t\t_, err = client.ListDatabases(bgCtx, nil)\n\t\tassert.True(t, errors.Is(err, ErrNilDocument), \"expected error %v, got %v\", ErrNilDocument, err)\n\n\t\t_, err = client.ListDatabaseNames(bgCtx, nil)\n\t\tassert.True(t, errors.Is(err, ErrNilDocument), \"expected error %v, got %v\", ErrNilDocument, err)\n\t})\n\tt.Run(\"read preference\", func(t *testing.T) {\n\t\tt.Run(\"absent\", func(t *testing.T) {\n\t\t\tclient := setupClient()\n\t\t\tgotMode := client.readPreference.Mode()\n\t\t\twantMode := readpref.PrimaryMode\n\t\t\tassert.Equal(t, gotMode, wantMode, \"expected mode %v, got %v\", wantMode, gotMode)\n\t\t\t_, flag := client.readPreference.MaxStaleness()\n\t\t\tassert.False(t, flag, \"expected max staleness to not be set but was\")\n\t\t})\n\t\tt.Run(\"specified\", func(t *testing.T) {\n\t\t\ttags := []tag.Set{\n\t\t\t\t{\n\t\t\t\t\ttag.Tag{\n\t\t\t\t\t\tName:  \"one\",\n\t\t\t\t\t\tValue: \"1\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\ttag.Tag{\n\t\t\t\t\t\tName:  \"two\",\n\t\t\t\t\t\tValue: \"2\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t}\n\t\t\tcs := \"mongodb://localhost:27017/\"\n\t\t\tcs += \"?readpreference=secondary&readPreferenceTags=one:1&readPreferenceTags=two:2&maxStaleness=5\"\n\n\t\t\tclient := setupClient(options.Client().ApplyURI(cs))\n\t\t\tgotMode := client.readPreference.Mode()\n\t\t\tassert.Equal(t, gotMode, readpref.SecondaryMode, \"expected mode %v, got %v\", readpref.SecondaryMode, gotMode)\n\t\t\tgotTags := client.readPreference.TagSets()\n\t\t\tassert.Equal(t, gotTags, tags, \"expected tags %v, got %v\", tags, gotTags)\n\t\t\tgotStaleness, flag := client.readPreference.MaxStaleness()\n\t\t\tassert.True(t, flag, \"expected max staleness to be set but was not\")\n\t\t\twantStaleness := time.Duration(5) * time.Second\n\t\t\tassert.Equal(t, gotStaleness, wantStaleness, \"expected staleness %v, got %v\", wantStaleness, gotStaleness)\n\t\t})\n\t})\n\tt.Run(\"localThreshold\", func(t *testing.T) {\n\t\ttestCases := []struct {\n\t\t\tname              string\n\t\t\topts              *options.ClientOptions\n\t\t\texpectedThreshold time.Duration\n\t\t}{\n\t\t\t{\"default\", options.Client(), defaultLocalThreshold},\n\t\t\t{\"custom\", options.Client().SetLocalThreshold(10 * time.Second), 10 * time.Second},\n\t\t}\n\t\tfor _, tc := range testCases {\n\t\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\t\tclient := setupClient(tc.opts)\n\t\t\t\tassert.Equal(t, tc.expectedThreshold, client.localThreshold,\n\t\t\t\t\t\"expected localThreshold %v, got %v\", tc.expectedThreshold, client.localThreshold)\n\t\t\t})\n\t\t}\n\t})\n\tt.Run(\"read concern\", func(t *testing.T) {\n\t\trc := readconcern.Majority()\n\t\tclient := setupClient(options.Client().SetReadConcern(rc))\n\t\tassert.Equal(t, rc, client.readConcern, \"expected read concern %v, got %v\", rc, client.readConcern)\n\t})\n\tt.Run(\"min pool size from Set*PoolSize()\", func(t *testing.T) {\n\t\ttestCases := []struct {\n\t\t\tname string\n\t\t\topts *options.ClientOptions\n\t\t\terr  error\n\t\t}{\n\t\t\t{\n\t\t\t\tname: \"minPoolSize < default maxPoolSize\",\n\t\t\t\topts: options.Client().SetMinPoolSize(64),\n\t\t\t\terr:  nil,\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"minPoolSize > default maxPoolSize\",\n\t\t\t\topts: options.Client().SetMinPoolSize(128),\n\t\t\t\terr:  errors.New(\"minPoolSize must be less than or equal to maxPoolSize, got minPoolSize=128 maxPoolSize=100\"),\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"minPoolSize < maxPoolSize\",\n\t\t\t\topts: options.Client().SetMinPoolSize(128).SetMaxPoolSize(256),\n\t\t\t\terr:  nil,\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"minPoolSize == maxPoolSize\",\n\t\t\t\topts: options.Client().SetMinPoolSize(128).SetMaxPoolSize(128),\n\t\t\t\terr:  nil,\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"minPoolSize > maxPoolSize\",\n\t\t\t\topts: options.Client().SetMinPoolSize(64).SetMaxPoolSize(32),\n\t\t\t\terr:  errors.New(\"minPoolSize must be less than or equal to maxPoolSize, got minPoolSize=64 maxPoolSize=32\"),\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"maxPoolSize == 0\",\n\t\t\t\topts: options.Client().SetMinPoolSize(128).SetMaxPoolSize(0),\n\t\t\t\terr:  nil,\n\t\t\t},\n\t\t}\n\t\tfor _, tc := range testCases {\n\t\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\t\t_, err := newClient(tc.opts)\n\t\t\t\tassert.Equal(t, tc.err, err, \"expected error %v, got %v\", tc.err, err)\n\t\t\t})\n\t\t}\n\t})\n\tt.Run(\"min pool size from ApplyURI()\", func(t *testing.T) {\n\t\ttestCases := []struct {\n\t\t\tname string\n\t\t\topts *options.ClientOptions\n\t\t\terr  error\n\t\t}{\n\t\t\t{\n\t\t\t\tname: \"minPoolSize < default maxPoolSize\",\n\t\t\t\topts: options.Client().ApplyURI(\"mongodb://localhost:27017/?minPoolSize=64\"),\n\t\t\t\terr:  nil,\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"minPoolSize > default maxPoolSize\",\n\t\t\t\topts: options.Client().ApplyURI(\"mongodb://localhost:27017/?minPoolSize=128\"),\n\t\t\t\terr:  errors.New(\"minPoolSize must be less than or equal to maxPoolSize, got minPoolSize=128 maxPoolSize=100\"),\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"minPoolSize < maxPoolSize\",\n\t\t\t\topts: options.Client().ApplyURI(\"mongodb://localhost:27017/?minPoolSize=128&maxPoolSize=256\"),\n\t\t\t\terr:  nil,\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"minPoolSize == maxPoolSize\",\n\t\t\t\topts: options.Client().ApplyURI(\"mongodb://localhost:27017/?minPoolSize=128&maxPoolSize=128\"),\n\t\t\t\terr:  nil,\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"minPoolSize > maxPoolSize\",\n\t\t\t\topts: options.Client().ApplyURI(\"mongodb://localhost:27017/?minPoolSize=64&maxPoolSize=32\"),\n\t\t\t\terr:  errors.New(\"minPoolSize must be less than or equal to maxPoolSize, got minPoolSize=64 maxPoolSize=32\"),\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"maxPoolSize == 0\",\n\t\t\t\topts: options.Client().ApplyURI(\"mongodb://localhost:27017/?minPoolSize=128&maxPoolSize=0\"),\n\t\t\t\terr:  nil,\n\t\t\t},\n\t\t}\n\t\tfor _, tc := range testCases {\n\t\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\t\t_, err := newClient(tc.opts)\n\t\t\t\tassert.Equal(t, tc.err, err, \"expected error %v, got %v\", tc.err, err)\n\t\t\t})\n\t\t}\n\t})\n\tt.Run(\"retry writes\", func(t *testing.T) {\n\t\tretryWritesURI := \"mongodb://localhost:27017/?retryWrites=false\"\n\t\tretryWritesErrorURI := \"mongodb://localhost:27017/?retryWrites=foobar\"\n\n\t\ttestCases := []struct {\n\t\t\tname          string\n\t\t\topts          *options.ClientOptions\n\t\t\texpectErr     bool\n\t\t\texpectedRetry bool\n\t\t}{\n\t\t\t{\"default\", options.Client(), false, true},\n\t\t\t{\"custom options\", options.Client().SetRetryWrites(false), false, false},\n\t\t\t{\"custom URI\", options.Client().ApplyURI(retryWritesURI), false, false},\n\t\t\t{\"custom URI error\", options.Client().ApplyURI(retryWritesErrorURI), true, false},\n\t\t}\n\t\tfor _, tc := range testCases {\n\t\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\t\tclient, err := newClient(tc.opts)\n\t\t\t\tif tc.expectErr {\n\t\t\t\t\tassert.NotNil(t, err, \"expected error, got nil\")\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tassert.Nil(t, err, \"configuration error: %v\", err)\n\t\t\t\tassert.Equal(t, tc.expectedRetry, client.retryWrites, \"expected retryWrites %v, got %v\",\n\t\t\t\t\ttc.expectedRetry, client.retryWrites)\n\t\t\t})\n\t\t}\n\t})\n\tt.Run(\"retry reads\", func(t *testing.T) {\n\t\tretryReadsURI := \"mongodb://localhost:27017/?retryReads=false\"\n\t\tretryReadsErrorURI := \"mongodb://localhost:27017/?retryReads=foobar\"\n\n\t\ttestCases := []struct {\n\t\t\tname          string\n\t\t\topts          *options.ClientOptions\n\t\t\texpectErr     bool\n\t\t\texpectedRetry bool\n\t\t}{\n\t\t\t{\"default\", options.Client(), false, true},\n\t\t\t{\"custom options\", options.Client().SetRetryReads(false), false, false},\n\t\t\t{\"custom URI\", options.Client().ApplyURI(retryReadsURI), false, false},\n\t\t\t{\"custom URI error\", options.Client().ApplyURI(retryReadsErrorURI), true, false},\n\t\t}\n\t\tfor _, tc := range testCases {\n\t\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\t\tclient, err := newClient(tc.opts)\n\t\t\t\tif tc.expectErr {\n\t\t\t\t\tassert.NotNil(t, err, \"expected error, got nil\")\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tassert.Nil(t, err, \"configuration error: %v\", err)\n\t\t\t\tassert.Equal(t, tc.expectedRetry, client.retryReads, \"expected retryReads %v, got %v\",\n\t\t\t\t\ttc.expectedRetry, client.retryReads)\n\t\t\t})\n\t\t}\n\t})\n\tt.Run(\"write concern\", func(t *testing.T) {\n\t\twc := writeconcern.Majority()\n\t\tclient := setupClient(options.Client().SetWriteConcern(wc))\n\t\tassert.Equal(t, wc, client.writeConcern, \"mismatch; expected write concern %v, got %v\", wc, client.writeConcern)\n\t})\n\tt.Run(\"server monitor\", func(t *testing.T) {\n\t\tmonitor := &event.ServerMonitor{}\n\t\tclient := setupClient(options.Client().SetServerMonitor(monitor))\n\t\tassert.Equal(t, monitor, client.serverMonitor, \"expected sdam monitor %v, got %v\", monitor, client.serverMonitor)\n\t})\n\tt.Run(\"GetURI\", func(t *testing.T) {\n\t\tt.Run(\"ApplyURI not called\", func(t *testing.T) {\n\t\t\topts := options.Client().SetHosts([]string{\"localhost:27017\"})\n\t\t\turi := opts.GetURI()\n\t\t\tassert.Equal(t, \"\", uri, \"expected GetURI to return empty string, got %v\", uri)\n\t\t})\n\t\tt.Run(\"ApplyURI called with empty string\", func(t *testing.T) {\n\t\t\topts := options.Client().ApplyURI(\"\")\n\n\t\t\turi := opts.GetURI()\n\t\t\tassert.Equal(t, \"\", uri, \"expected GetURI to return empty string, got %v\", uri)\n\t\t})\n\t\tt.Run(\"ApplyURI called with non-empty string\", func(t *testing.T) {\n\t\t\turi := \"mongodb://localhost:27017/foobar\"\n\t\t\topts := options.Client().ApplyURI(uri)\n\n\t\t\tgot := opts.GetURI()\n\n\t\t\tassert.Equal(t, uri, got, \"expected GetURI to return %v, got %v\", uri, got)\n\t\t})\n\t})\n\tt.Run(\"endSessions\", func(t *testing.T) {\n\t\tcs := integtest.ConnString(t)\n\t\toriginalBatchSize := endSessionsBatchSize\n\t\tendSessionsBatchSize = 2\n\t\tdefer func() {\n\t\t\tendSessionsBatchSize = originalBatchSize\n\t\t}()\n\n\t\ttestCases := []struct {\n\t\t\tname            string\n\t\t\tnumSessions     int\n\t\t\teventBatchSizes []int\n\t\t}{\n\t\t\t{\"number of sessions divides evenly\", endSessionsBatchSize * 2, []int{endSessionsBatchSize, endSessionsBatchSize}},\n\t\t\t{\"number of sessions does not divide evenly\", endSessionsBatchSize + 1, []int{endSessionsBatchSize, 1}},\n\t\t}\n\t\tfor _, tc := range testCases {\n\t\t\tif testing.Short() {\n\t\t\t\tt.Skip(\"skipping integration test in short mode\")\n\t\t\t}\n\t\t\tif os.Getenv(\"DOCKER_RUNNING\") != \"\" {\n\t\t\t\tt.Skip(\"skipping test in docker environment\")\n\t\t\t}\n\n\t\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\t\t// Setup a client and skip the test based on server version.\n\t\t\t\tvar started []*event.CommandStartedEvent\n\t\t\t\tvar failureReasons []error\n\t\t\t\tcmdMonitor := &event.CommandMonitor{\n\t\t\t\t\tStarted: func(_ context.Context, evt *event.CommandStartedEvent) {\n\t\t\t\t\t\tif evt.CommandName == \"endSessions\" {\n\t\t\t\t\t\t\tstarted = append(started, evt)\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\tFailed: func(_ context.Context, evt *event.CommandFailedEvent) {\n\t\t\t\t\t\tif evt.CommandName == \"endSessions\" {\n\t\t\t\t\t\t\tfailureReasons = append(failureReasons, evt.Failure)\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t\tclientOpts := options.Client().ApplyURI(cs.Original).SetReadPreference(readpref.Primary()).\n\t\t\t\t\tSetWriteConcern(writeconcern.Majority()).SetMonitor(cmdMonitor)\n\t\t\t\tintegtest.AddTestServerAPIVersion(clientOpts)\n\t\t\t\tclient, err := Connect(clientOpts)\n\t\t\t\tassert.Nil(t, err, \"Connect error: %v\", err)\n\t\t\t\tdefer func() {\n\t\t\t\t\t_ = client.Disconnect(bgCtx)\n\t\t\t\t}()\n\n\t\t\t\tserverVersion, err := getServerVersion(client.Database(\"admin\"))\n\t\t\t\tassert.Nil(t, err, \"getServerVersion error: %v\", err)\n\t\t\t\tif compareVersions(serverVersion, \"3.6.0\") < 1 {\n\t\t\t\t\tt.Skip(\"skipping server version < 3.6\")\n\t\t\t\t}\n\n\t\t\t\tcoll := client.Database(\"foo\").Collection(\"bar\")\n\t\t\t\tdefer func() {\n\t\t\t\t\t_ = coll.Drop(bgCtx)\n\t\t\t\t}()\n\n\t\t\t\t// Do an application operation and create the number of sessions specified by the test.\n\t\t\t\t_, err = coll.CountDocuments(bgCtx, bson.D{})\n\t\t\t\tassert.Nil(t, err, \"CountDocuments error: %v\", err)\n\t\t\t\tvar sessions []*Session\n\t\t\t\tfor i := 0; i < tc.numSessions; i++ {\n\t\t\t\t\tsess, err := client.StartSession()\n\t\t\t\t\tassert.Nil(t, err, \"StartSession error at index %d: %v\", i, err)\n\t\t\t\t\tsessions = append(sessions, sess)\n\t\t\t\t}\n\t\t\t\tfor _, sess := range sessions {\n\t\t\t\t\tsess.EndSession(bgCtx)\n\t\t\t\t}\n\n\t\t\t\tclient.endSessions(bgCtx)\n\t\t\t\tdivisionResult := float64(tc.numSessions) / float64(endSessionsBatchSize)\n\t\t\t\tnumEventsExpected := int(math.Ceil(divisionResult))\n\t\t\t\tassert.Equal(t, len(started), numEventsExpected, \"expected %d started events, got %d\", numEventsExpected,\n\t\t\t\t\tlen(started))\n\t\t\t\tassert.Equal(t, len(failureReasons), 0, \"endSessions errors: %v\", failureReasons)\n\n\t\t\t\tfor i := 0; i < numEventsExpected; i++ {\n\t\t\t\t\tsentArray := started[i].Command.Lookup(\"endSessions\").Array()\n\t\t\t\t\tvalues, _ := sentArray.Values()\n\t\t\t\t\texpectedNumValues := tc.eventBatchSizes[i]\n\t\t\t\t\tassert.Equal(t, len(values), expectedNumValues,\n\t\t\t\t\t\t\"batch size mismatch at index %d; expected %d sessions in batch, got %d\", i, expectedNumValues,\n\t\t\t\t\t\tlen(values))\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t})\n\tt.Run(\"serverAPI version\", func(t *testing.T) {\n\t\tgetServerAPIOptions := func() *options.ServerAPIOptions {\n\t\t\treturn options.ServerAPI(options.ServerAPIVersion1).\n\t\t\t\tSetStrict(false).SetDeprecationErrors(false)\n\t\t}\n\n\t\tt.Run(\"success with all options\", func(t *testing.T) {\n\t\t\tserverAPIOptions := getServerAPIOptions()\n\t\t\tclient, err := newClient(options.Client().SetServerAPIOptions(serverAPIOptions))\n\t\t\tassert.Nil(t, err, \"unexpected error from NewClient: %v\", err)\n\t\t\tconvertedAPIOptions := topology.ConvertToDriverAPIOptions(serverAPIOptions)\n\t\t\tassert.Equal(t, convertedAPIOptions, client.serverAPI,\n\t\t\t\t\"mismatch in serverAPI; expected %v, got %v\", convertedAPIOptions, client.serverAPI)\n\t\t})\n\t\tt.Run(\"failure with unsupported version\", func(t *testing.T) {\n\t\t\tserverAPIOptions := options.ServerAPI(\"badVersion\")\n\t\t\t_, err := newClient(options.Client().SetServerAPIOptions(serverAPIOptions))\n\t\t\tassert.NotNil(t, err, \"expected error from NewClient, got nil\")\n\t\t\terrmsg := `api version \"badVersion\" not supported; this driver version only supports API version \"1\"`\n\t\t\tassert.Equal(t, errmsg, err.Error(), \"expected error %v, got %v\", errmsg, err.Error())\n\t\t})\n\t\tt.Run(\"cannot modify options after client creation\", func(t *testing.T) {\n\t\t\tserverAPIOptions := getServerAPIOptions()\n\t\t\tclient, err := newClient(options.Client().SetServerAPIOptions(serverAPIOptions))\n\t\t\tassert.Nil(t, err, \"unexpected error from NewClient: %v\", err)\n\n\t\t\texpectedServerAPIOptions := getServerAPIOptions()\n\t\t\t// modify passed-in options\n\t\t\tserverAPIOptions.SetStrict(true).SetDeprecationErrors(true)\n\t\t\tconvertedAPIOptions := topology.ConvertToDriverAPIOptions(expectedServerAPIOptions)\n\t\t\tassert.Equal(t, convertedAPIOptions, client.serverAPI,\n\t\t\t\t\"unexpected modification to serverAPI; expected %v, got %v\", convertedAPIOptions, client.serverAPI)\n\t\t})\n\t})\n\tt.Run(\"mongocryptd or crypt_shared\", func(t *testing.T) {\n\t\tcryptSharedLibPath := os.Getenv(\"CRYPT_SHARED_LIB_PATH\")\n\t\tif cryptSharedLibPath == \"\" {\n\t\t\tt.Skip(\"CRYPT_SHARED_LIB_PATH not set, skipping\")\n\t\t}\n\t\tif len(mongocrypt.Version()) == 0 {\n\t\t\tt.Skip(\"Not built with cse flag\")\n\t\t}\n\n\t\ttestCases := []struct {\n\t\t\tdescription       string\n\t\t\tuseCryptSharedLib bool\n\t\t}{\n\t\t\t{\n\t\t\t\tdescription:       \"when crypt_shared is loaded, should not attempt to spawn mongocryptd\",\n\t\t\t\tuseCryptSharedLib: true,\n\t\t\t},\n\t\t\t{\n\t\t\t\tdescription:       \"when crypt_shared is not loaded, should attempt to spawn mongocryptd\",\n\t\t\t\tuseCryptSharedLib: false,\n\t\t\t},\n\t\t}\n\t\tfor _, tc := range testCases {\n\t\t\tt.Run(tc.description, func(t *testing.T) {\n\t\t\t\textraOptions := map[string]any{\n\t\t\t\t\t// Set a mongocryptd path that does not exist. If Connect() attempts to start\n\t\t\t\t\t// mongocryptd, it will cause an error.\n\t\t\t\t\t\"mongocryptdPath\": \"/does/not/exist\",\n\t\t\t\t}\n\n\t\t\t\t// If we're using the crypt_shared library, set the \"cryptSharedLibRequired\" option\n\t\t\t\t// to true and the \"cryptSharedLibPath\" option to the crypt_shared library path from\n\t\t\t\t// the CRYPT_SHARED_LIB_PATH environment variable. If we're not using the\n\t\t\t\t// crypt_shared library, explicitly disable loading the crypt_shared library.\n\t\t\t\tif tc.useCryptSharedLib {\n\t\t\t\t\textraOptions[\"cryptSharedLibRequired\"] = true\n\t\t\t\t\textraOptions[\"cryptSharedLibPath\"] = cryptSharedLibPath\n\t\t\t\t} else {\n\t\t\t\t\textraOptions[\"__cryptSharedLibDisabledForTestOnly\"] = true\n\t\t\t\t}\n\n\t\t\t\t_, err := newClient(options.Client().\n\t\t\t\t\tSetAutoEncryptionOptions(options.AutoEncryption().\n\t\t\t\t\t\tSetKmsProviders(map[string]map[string]any{\n\t\t\t\t\t\t\t\"local\": {\"key\": make([]byte, 96)},\n\t\t\t\t\t\t}).\n\t\t\t\t\t\tSetExtraOptions(extraOptions)))\n\n\t\t\t\t// If we're using the crypt_shared library, expect that Connect() doesn't attempt to spawn\n\t\t\t\t// mongocryptd and no error is returned. If we're not using the crypt_shared library,\n\t\t\t\t// expect that Connect() tries to spawn mongocryptd and returns an error.\n\t\t\t\tif tc.useCryptSharedLib {\n\t\t\t\t\tassert.Nil(t, err, \"Connect() error: %v\", err)\n\t\t\t\t} else {\n\t\t\t\t\tassert.NotNil(t, err, \"expected Connect() error, but got nil\")\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t})\n\tt.Run(\"negative timeout will err\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tcopts := options.Client().SetTimeout(-1 * time.Second)\n\t\t_, err := Connect(copts)\n\n\t\terrmsg := `invalid value \"-1s\" for \"Timeout\": value must be positive`\n\t\tassert.Equal(t, errmsg, err.Error(), \"expected error %v, got %v\", errmsg, err.Error())\n\t})\n}\n"
  },
  {
    "path": "mongo/collection.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage mongo\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"reflect\"\n\t\"strings\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/csfle\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/mongoutil\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/optionsutil\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/ptrutil\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/serverselector\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/readconcern\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/readpref\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/writeconcern\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/description\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/operation\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/session\"\n)\n\n// Collection is a handle to a MongoDB collection. It is safe for concurrent use by multiple goroutines.\ntype Collection struct {\n\tclient         *Client\n\tdb             *Database\n\tname           string\n\treadConcern    *readconcern.ReadConcern\n\twriteConcern   *writeconcern.WriteConcern\n\treadPreference *readpref.ReadPref\n\treadSelector   description.ServerSelector\n\twriteSelector  description.ServerSelector\n\tbsonOpts       *options.BSONOptions\n\tregistry       *bson.Registry\n}\n\n// aggregateParams is used to store information to configure an Aggregate operation.\ntype aggregateParams struct {\n\tctx            context.Context\n\tpipeline       any\n\tclient         *Client\n\tbsonOpts       *options.BSONOptions\n\tregistry       *bson.Registry\n\treadConcern    *readconcern.ReadConcern\n\twriteConcern   *writeconcern.WriteConcern\n\tretryRead      bool\n\tdb             string\n\tcol            string\n\treadSelector   description.ServerSelector\n\twriteSelector  description.ServerSelector\n\treadPreference *readpref.ReadPref\n}\n\nfunc closeImplicitSession(sess *session.Client) {\n\tif sess != nil && sess.IsImplicit {\n\t\tsess.EndSession()\n\t}\n}\n\nfunc newCollection(db *Database, name string, opts ...options.Lister[options.CollectionOptions]) *Collection {\n\targs, _ := mongoutil.NewOptions[options.CollectionOptions](opts...)\n\n\trc := db.readConcern\n\tif args.ReadConcern != nil {\n\t\trc = args.ReadConcern\n\t}\n\n\twc := db.writeConcern\n\tif args.WriteConcern != nil {\n\t\twc = args.WriteConcern\n\t}\n\n\trp := db.readPreference\n\tif args.ReadPreference != nil {\n\t\trp = args.ReadPreference\n\t}\n\n\tbsonOpts := db.bsonOpts\n\tif args.BSONOptions != nil {\n\t\tbsonOpts = args.BSONOptions\n\t}\n\n\treg := db.registry\n\tif args.Registry != nil {\n\t\treg = args.Registry\n\t}\n\n\treadSelector := &serverselector.Composite{\n\t\tSelectors: []description.ServerSelector{\n\t\t\t&serverselector.ReadPref{ReadPref: rp},\n\t\t\t&serverselector.Latency{Latency: db.client.localThreshold},\n\t\t},\n\t}\n\n\twriteSelector := &serverselector.Composite{\n\t\tSelectors: []description.ServerSelector{\n\t\t\t&serverselector.Write{},\n\t\t\t&serverselector.Latency{Latency: db.client.localThreshold},\n\t\t},\n\t}\n\n\tcoll := &Collection{\n\t\tclient:         db.client,\n\t\tdb:             db,\n\t\tname:           name,\n\t\treadPreference: rp,\n\t\treadConcern:    rc,\n\t\twriteConcern:   wc,\n\t\treadSelector:   readSelector,\n\t\twriteSelector:  writeSelector,\n\t\tbsonOpts:       bsonOpts,\n\t\tregistry:       reg,\n\t}\n\n\treturn coll\n}\n\nfunc (coll *Collection) copy() *Collection {\n\treturn &Collection{\n\t\tclient:         coll.client,\n\t\tdb:             coll.db,\n\t\tname:           coll.name,\n\t\treadConcern:    coll.readConcern,\n\t\twriteConcern:   coll.writeConcern,\n\t\treadPreference: coll.readPreference,\n\t\treadSelector:   coll.readSelector,\n\t\twriteSelector:  coll.writeSelector,\n\t\tregistry:       coll.registry,\n\t}\n}\n\n// Clone creates a copy of the Collection configured with the given CollectionOptions.\n// The specified options are merged with the existing options on the collection, with the specified options taking\n// precedence.\nfunc (coll *Collection) Clone(opts ...options.Lister[options.CollectionOptions]) *Collection {\n\tcopyColl := coll.copy()\n\n\targs, _ := mongoutil.NewOptions[options.CollectionOptions](opts...)\n\n\tif args.ReadConcern != nil {\n\t\tcopyColl.readConcern = args.ReadConcern\n\t}\n\n\tif args.WriteConcern != nil {\n\t\tcopyColl.writeConcern = args.WriteConcern\n\t}\n\n\tif args.ReadPreference != nil {\n\t\tcopyColl.readPreference = args.ReadPreference\n\t}\n\n\tif args.Registry != nil {\n\t\tcopyColl.registry = args.Registry\n\t}\n\n\tcopyColl.readSelector = &serverselector.Composite{\n\t\tSelectors: []description.ServerSelector{\n\t\t\t&serverselector.ReadPref{ReadPref: copyColl.readPreference},\n\t\t\t&serverselector.Latency{Latency: copyColl.client.localThreshold},\n\t\t},\n\t}\n\n\treturn copyColl\n}\n\n// Name returns the name of the collection.\nfunc (coll *Collection) Name() string {\n\treturn coll.name\n}\n\n// Database returns the Database that was used to create the Collection.\nfunc (coll *Collection) Database() *Database {\n\treturn coll.db\n}\n\n// BulkWrite performs a bulk write operation (https://www.mongodb.com/docs/manual/core/bulk-write-operations/).\n//\n// The models parameter must be a slice of operations to be executed in this bulk write. It cannot be nil or empty.\n// All of the models must be non-nil. See the mongo.WriteModel documentation for a list of valid model types and\n// examples of how they should be used.\n//\n// The opts parameter can be used to specify options for the operation (see the options.BulkWriteOptions documentation.)\nfunc (coll *Collection) BulkWrite(ctx context.Context, models []WriteModel,\n\topts ...options.Lister[options.BulkWriteOptions],\n) (*BulkWriteResult, error) {\n\tif len(models) == 0 {\n\t\treturn nil, fmt.Errorf(\"invalid models: %w\", ErrEmptySlice)\n\t}\n\n\tif ctx == nil {\n\t\tctx = context.Background()\n\t}\n\n\tsess := sessionFromContext(ctx)\n\tif sess == nil && coll.client.sessionPool != nil {\n\t\tsess = session.NewImplicitClientSession(coll.client.sessionPool, coll.client.id)\n\t\tdefer sess.EndSession()\n\t}\n\n\terr := coll.client.validSession(sess)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\twc := coll.writeConcern\n\tif sess.TransactionRunning() {\n\t\twc = nil\n\t}\n\tif !wc.Acknowledged() {\n\t\tsess = nil\n\t}\n\n\tselector := makePinnedSelector(sess, coll.writeSelector)\n\n\tfor i, model := range models {\n\t\tif model == nil {\n\t\t\treturn nil, fmt.Errorf(\"invalid model at index %d: %w\", i, ErrNilDocument)\n\t\t}\n\t}\n\n\t// Ensure opts have the default case at the front.\n\topts = append([]options.Lister[options.BulkWriteOptions]{options.BulkWrite()}, opts...)\n\targs, err := mongoutil.NewOptions(opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\top := bulkWrite{\n\t\tcomment:                  args.Comment,\n\t\tordered:                  args.Ordered,\n\t\tbypassDocumentValidation: args.BypassDocumentValidation,\n\t\tmodels:                   models,\n\t\tsession:                  sess,\n\t\tcollection:               coll,\n\t\tselector:                 selector,\n\t\twriteConcern:             wc,\n\t\tlet:                      args.Let,\n\t}\n\tif rawData, ok := optionsutil.Value(args.Internal, \"rawData\").(bool); ok {\n\t\top.rawData = &rawData\n\t}\n\tif additionalCmd, ok := optionsutil.Value(args.Internal, \"addCommandFields\").(bson.D); ok {\n\t\top.additionalCmd = additionalCmd\n\t}\n\n\terr = op.execute(ctx)\n\n\treturn &op.result, wrapErrors(err)\n}\n\nfunc (coll *Collection) insert(\n\tctx context.Context,\n\tdocuments []any,\n\topts ...options.Lister[options.InsertManyOptions],\n) ([]any, error) {\n\tif ctx == nil {\n\t\tctx = context.Background()\n\t}\n\n\tresult := make([]any, len(documents))\n\tdocs := make([]bsoncore.Document, len(documents))\n\n\tfor i, doc := range documents {\n\t\tbsoncoreDoc, err := marshal(doc, coll.bsonOpts, coll.registry)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tbsoncoreDoc, id, err := ensureID(bsoncoreDoc, bson.NilObjectID, coll.bsonOpts, coll.registry)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tdocs[i] = bsoncoreDoc\n\t\tresult[i] = id\n\t}\n\n\tsess := sessionFromContext(ctx)\n\tif sess == nil && coll.client.sessionPool != nil {\n\t\tsess = session.NewImplicitClientSession(coll.client.sessionPool, coll.client.id)\n\t\tdefer sess.EndSession()\n\t}\n\n\terr := coll.client.validSession(sess)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\twc := coll.writeConcern\n\tif sess.TransactionRunning() {\n\t\twc = nil\n\t}\n\tif !wc.Acknowledged() {\n\t\tsess = nil\n\t}\n\n\tselector := makePinnedSelector(sess, coll.writeSelector)\n\n\top := insert{\n\t\tdocuments:     docs,\n\t\tsession:       sess,\n\t\twriteConcern:  wc,\n\t\tmonitor:       coll.client.monitor,\n\t\tselector:      selector,\n\t\tclock:         coll.client.clock,\n\t\tdatabase:      coll.db.name,\n\t\tcollection:    coll.name,\n\t\tdeployment:    coll.client.deployment,\n\t\tcrypt:         coll.client.cryptFLE,\n\t\tordered:       ptrutil.Ptr(true),\n\t\tserverAPI:     coll.client.serverAPI,\n\t\ttimeout:       coll.client.timeout,\n\t\tlogger:        coll.client.logger,\n\t\tauthenticator: coll.client.authenticator,\n\t}\n\n\targs, err := mongoutil.NewOptions[options.InsertManyOptions](opts...)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to construct options from builder: %w\", err)\n\t}\n\n\tif args.BypassDocumentValidation != nil && *args.BypassDocumentValidation {\n\t\top.bypassDocumentValidation = args.BypassDocumentValidation\n\t}\n\tif args.Comment != nil {\n\t\tcomment, err := marshalValue(args.Comment, coll.bsonOpts, coll.registry)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\top.comment = comment\n\t}\n\tif args.Ordered != nil {\n\t\top.ordered = args.Ordered\n\t}\n\tif rawData, ok := optionsutil.Value(args.Internal, \"rawData\").(bool); ok {\n\t\top.rawData = &rawData\n\t}\n\tif additionalCmd, ok := optionsutil.Value(args.Internal, \"addCommandFields\").(bson.D); ok {\n\t\top.additionalCmd = additionalCmd\n\t}\n\tretry := driver.RetryNone\n\tif coll.client.retryWrites {\n\t\tretry = driver.RetryOncePerCommand\n\t}\n\top.retry = &retry\n\n\terr = op.Execute(ctx)\n\tvar wce driver.WriteCommandError\n\tif !errors.As(err, &wce) {\n\t\treturn result, err\n\t}\n\n\t// remove the ids that had writeErrors from result\n\tfor i, we := range wce.WriteErrors {\n\t\t// i indexes have been removed before the current error, so the index is we.Index-i\n\t\tidIndex := int(we.Index) - i\n\t\t// if the insert is ordered, nothing after the error was inserted\n\t\tif args.Ordered == nil || *args.Ordered {\n\t\t\tresult = result[:idIndex]\n\t\t\tbreak\n\t\t}\n\t\tresult = append(result[:idIndex], result[idIndex+1:]...)\n\t}\n\n\treturn result, err\n}\n\n// InsertOne executes an insert command to insert a single document into the collection.\n//\n// The document parameter must be the document to be inserted. It cannot be nil. If the document does not have an _id\n// field when transformed into BSON, one will be added automatically to the marshalled document. The original document\n// will not be modified. The _id can be retrieved from the InsertedID field of the returned InsertOneResult.\n//\n// The opts parameter can be used to specify options for the operation (see the options.InsertOneOptions documentation.)\n//\n// For more information about the command, see https://www.mongodb.com/docs/manual/reference/command/insert/.\nfunc (coll *Collection) InsertOne(ctx context.Context, document any,\n\topts ...options.Lister[options.InsertOneOptions],\n) (*InsertOneResult, error) {\n\targs, err := mongoutil.NewOptions(opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\timOpts := options.InsertMany()\n\n\tif args.BypassDocumentValidation != nil && *args.BypassDocumentValidation {\n\t\timOpts.SetBypassDocumentValidation(*args.BypassDocumentValidation)\n\t}\n\tif args.Comment != nil {\n\t\timOpts.SetComment(args.Comment)\n\t}\n\tif rawDataOpt := optionsutil.Value(args.Internal, \"rawData\"); rawDataOpt != nil {\n\t\timOpts.Opts = append(imOpts.Opts, func(opts *options.InsertManyOptions) error {\n\t\t\topts.Internal = optionsutil.WithValue(opts.Internal, \"rawData\", rawDataOpt)\n\n\t\t\treturn nil\n\t\t})\n\t}\n\tif additionalCmd := optionsutil.Value(args.Internal, \"addCommandFields\"); additionalCmd != nil {\n\t\timOpts.Opts = append(imOpts.Opts, func(opts *options.InsertManyOptions) error {\n\t\t\topts.Internal = optionsutil.WithValue(opts.Internal, \"addCommandFields\", additionalCmd)\n\n\t\t\treturn nil\n\t\t})\n\t}\n\tres, err := coll.insert(ctx, []any{document}, imOpts)\n\n\trr, err := processWriteError(err)\n\tif rr&rrOne == 0 && rr.isAcknowledged() {\n\t\treturn nil, err\n\t}\n\n\treturn &InsertOneResult{\n\t\tInsertedID:   res[0],\n\t\tAcknowledged: rr.isAcknowledged(),\n\t}, err\n}\n\n// InsertMany executes an insert command to insert multiple documents into the collection. If write errors occur\n// during the operation (e.g. duplicate key error), this method returns a BulkWriteException error.\n//\n// The documents parameter must be a slice of documents to insert. The slice cannot be nil or empty. The elements must\n// all be non-nil. For any document that does not have an _id field when transformed into BSON, one will be added\n// automatically to the marshalled document. The original document will not be modified. The _id values for the inserted\n// documents can be retrieved from the InsertedIDs field of the returned InsertManyResult.\n//\n// The opts parameter can be used to specify options for the operation (see the options.InsertManyOptions documentation.)\n//\n// For more information about the command, see https://www.mongodb.com/docs/manual/reference/command/insert/.\nfunc (coll *Collection) InsertMany(\n\tctx context.Context,\n\tdocuments any,\n\topts ...options.Lister[options.InsertManyOptions],\n) (*InsertManyResult, error) {\n\tdv := reflect.ValueOf(documents)\n\tif dv.Kind() != reflect.Slice {\n\t\treturn nil, fmt.Errorf(\"invalid documents: %w\", ErrNotSlice)\n\t}\n\tif dv.Len() == 0 {\n\t\treturn nil, fmt.Errorf(\"invalid documents: %w\", ErrEmptySlice)\n\t}\n\n\tdocSlice := make([]any, 0, dv.Len())\n\tfor i := 0; i < dv.Len(); i++ {\n\t\tdocSlice = append(docSlice, dv.Index(i).Interface())\n\t}\n\n\tresult, err := coll.insert(ctx, docSlice, opts...)\n\trr, err := processWriteError(err)\n\tif rr&rrMany == 0 {\n\t\treturn nil, err\n\t}\n\n\timResult := &InsertManyResult{\n\t\tInsertedIDs:  result,\n\t\tAcknowledged: rr.isAcknowledged(),\n\t}\n\tvar writeException WriteException\n\tif !errors.As(err, &writeException) {\n\t\treturn imResult, err\n\t}\n\n\t// create and return a BulkWriteException\n\tbwErrors := make([]BulkWriteError, 0, len(writeException.WriteErrors))\n\tfor _, we := range writeException.WriteErrors {\n\t\tbwErrors = append(bwErrors, BulkWriteError{\n\t\t\tWriteError: we,\n\t\t\tRequest:    nil,\n\t\t})\n\t}\n\n\treturn imResult, BulkWriteException{\n\t\tWriteErrors:       bwErrors,\n\t\tWriteConcernError: writeException.WriteConcernError,\n\t\tLabels:            writeException.Labels,\n\t}\n}\n\nfunc (coll *Collection) delete(\n\tctx context.Context,\n\tfilter any,\n\tdeleteOne bool,\n\texpectedRr returnResult,\n\targs *options.DeleteManyOptions,\n) (*DeleteResult, error) {\n\tif ctx == nil {\n\t\tctx = context.Background()\n\t}\n\n\tf, err := marshal(filter, coll.bsonOpts, coll.registry)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tsess := sessionFromContext(ctx)\n\tif sess == nil && coll.client.sessionPool != nil {\n\t\tsess = session.NewImplicitClientSession(coll.client.sessionPool, coll.client.id)\n\t\tdefer sess.EndSession()\n\t}\n\n\terr = coll.client.validSession(sess)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\twc := coll.writeConcern\n\tif sess.TransactionRunning() {\n\t\twc = nil\n\t}\n\tif !wc.Acknowledged() {\n\t\tsess = nil\n\t}\n\n\tselector := makePinnedSelector(sess, coll.writeSelector)\n\n\tvar limit int32\n\tif deleteOne {\n\t\tlimit = 1\n\t}\n\n\tdidx, doc := bsoncore.AppendDocumentStart(nil)\n\tdoc = bsoncore.AppendDocumentElement(doc, \"q\", f)\n\tdoc = bsoncore.AppendInt32Element(doc, \"limit\", limit)\n\tif args.Collation != nil {\n\t\tdoc = bsoncore.AppendDocumentElement(doc, \"collation\", toDocument(args.Collation))\n\t}\n\tif args.Hint != nil {\n\t\tif isUnorderedMap(args.Hint) {\n\t\t\treturn nil, ErrMapForOrderedArgument{\"hint\"}\n\t\t}\n\t\thint, err := marshalValue(args.Hint, coll.bsonOpts, coll.registry)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tdoc = bsoncore.AppendValueElement(doc, \"hint\", hint)\n\t}\n\tdoc, _ = bsoncore.AppendDocumentEnd(doc, didx)\n\n\top := operation.NewDelete(doc).\n\t\tSession(sess).WriteConcern(wc).CommandMonitor(coll.client.monitor).\n\t\tServerSelector(selector).ClusterClock(coll.client.clock).\n\t\tDatabase(coll.db.name).Collection(coll.name).\n\t\tDeployment(coll.client.deployment).Crypt(coll.client.cryptFLE).Ordered(true).\n\t\tServerAPI(coll.client.serverAPI).Timeout(coll.client.timeout).Logger(coll.client.logger).Authenticator(coll.client.authenticator)\n\tif args.Comment != nil {\n\t\tcomment, err := marshalValue(args.Comment, coll.bsonOpts, coll.registry)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\top = op.Comment(comment)\n\t}\n\tif args.Hint != nil {\n\t\top = op.Hint(true)\n\t}\n\tif args.Let != nil {\n\t\tlet, err := marshal(args.Let, coll.bsonOpts, coll.registry)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\top = op.Let(let)\n\t}\n\tif rawData, ok := optionsutil.Value(args.Internal, \"rawData\").(bool); ok {\n\t\top = op.RawData(rawData)\n\t}\n\n\t// deleteMany cannot be retried\n\tretryMode := driver.RetryNone\n\tif deleteOne && coll.client.retryWrites {\n\t\tretryMode = driver.RetryOncePerCommand\n\t}\n\top = op.Retry(retryMode)\n\trr, err := processWriteError(op.Execute(ctx))\n\tif rr&expectedRr == 0 {\n\t\treturn nil, err\n\t}\n\treturn &DeleteResult{\n\t\tDeletedCount: op.Result().N,\n\t\tAcknowledged: rr.isAcknowledged(),\n\t}, err\n}\n\n// DeleteOne executes a delete command to delete at most one document from the collection.\n//\n// The filter parameter must be a document containing query operators and can be used to select the document to be\n// deleted. It cannot be nil. If the filter does not match any documents, the operation will succeed and a DeleteResult\n// with a DeletedCount of 0 will be returned. If the filter matches multiple documents, one will be selected from the\n// matched set.\n//\n// The opts parameter can be used to specify options for the operation (see the options.DeleteOptions documentation).\n//\n// For more information about the command, see https://www.mongodb.com/docs/manual/reference/command/delete/.\nfunc (coll *Collection) DeleteOne(\n\tctx context.Context,\n\tfilter any,\n\topts ...options.Lister[options.DeleteOneOptions],\n) (*DeleteResult, error) {\n\targs, err := mongoutil.NewOptions[options.DeleteOneOptions](opts...)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to construct options from builder: %w\", err)\n\t}\n\tdeleteOptions := &options.DeleteManyOptions{\n\t\tCollation: args.Collation,\n\t\tComment:   args.Comment,\n\t\tHint:      args.Hint,\n\t\tLet:       args.Let,\n\t\tInternal:  args.Internal,\n\t}\n\n\treturn coll.delete(ctx, filter, true, rrOne, deleteOptions)\n}\n\n// DeleteMany executes a delete command to delete documents from the collection.\n//\n// The filter parameter must be a document containing query operators and can be used to select the documents to\n// be deleted. It cannot be nil. An empty document (e.g. bson.D{}) should be used to delete all documents in the\n// collection. If the filter does not match any documents, the operation will succeed and a DeleteResult with a\n// DeletedCount of 0 will be returned.\n//\n// The opts parameter can be used to specify options for the operation (see the options.DeleteOptions documentation).\n//\n// For more information about the command, see https://www.mongodb.com/docs/manual/reference/command/delete/.\nfunc (coll *Collection) DeleteMany(\n\tctx context.Context,\n\tfilter any,\n\topts ...options.Lister[options.DeleteManyOptions],\n) (*DeleteResult, error) {\n\targs, err := mongoutil.NewOptions[options.DeleteManyOptions](opts...)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to construct options from builder: %w\", err)\n\t}\n\n\treturn coll.delete(ctx, filter, false, rrMany, args)\n}\n\nfunc (coll *Collection) updateOrReplace(\n\tctx context.Context,\n\tfilter bsoncore.Document,\n\tupdate any,\n\tmulti bool,\n\texpectedRr returnResult,\n\tcheckDollarKey bool,\n\tsort any,\n\targs *options.UpdateManyOptions,\n) (*UpdateResult, error) {\n\tif ctx == nil {\n\t\tctx = context.Background()\n\t}\n\n\t// collation, arrayFilters, upsert, and hint are included on the individual update documents rather than as part of the\n\t// command\n\tupdateDoc, err := updateDoc{\n\t\tfilter:         filter,\n\t\tupdate:         update,\n\t\thint:           args.Hint,\n\t\tsort:           sort,\n\t\tarrayFilters:   args.ArrayFilters,\n\t\tcollation:      args.Collation,\n\t\tupsert:         args.Upsert,\n\t\tmulti:          multi,\n\t\tcheckDollarKey: checkDollarKey,\n\t}.marshal(coll.bsonOpts, coll.registry)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tsess := sessionFromContext(ctx)\n\tif sess == nil && coll.client.sessionPool != nil {\n\t\tsess = session.NewImplicitClientSession(coll.client.sessionPool, coll.client.id)\n\t\tdefer sess.EndSession()\n\t}\n\n\terr = coll.client.validSession(sess)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\twc := coll.writeConcern\n\tif sess.TransactionRunning() {\n\t\twc = nil\n\t}\n\tif !wc.Acknowledged() {\n\t\tsess = nil\n\t}\n\n\tselector := makePinnedSelector(sess, coll.writeSelector)\n\n\top := operation.NewUpdate(updateDoc).\n\t\tSession(sess).WriteConcern(wc).CommandMonitor(coll.client.monitor).\n\t\tServerSelector(selector).ClusterClock(coll.client.clock).\n\t\tDatabase(coll.db.name).Collection(coll.name).\n\t\tDeployment(coll.client.deployment).Crypt(coll.client.cryptFLE).Hint(args.Hint != nil).\n\t\tArrayFilters(args.ArrayFilters != nil).Ordered(true).ServerAPI(coll.client.serverAPI).\n\t\tTimeout(coll.client.timeout).Logger(coll.client.logger).Authenticator(coll.client.authenticator)\n\tif args.Let != nil {\n\t\tlet, err := marshal(args.Let, coll.bsonOpts, coll.registry)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\top = op.Let(let)\n\t}\n\n\tif args.BypassDocumentValidation != nil && *args.BypassDocumentValidation {\n\t\top = op.BypassDocumentValidation(*args.BypassDocumentValidation)\n\t}\n\tif args.Comment != nil {\n\t\tcomment, err := marshalValue(args.Comment, coll.bsonOpts, coll.registry)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\top = op.Comment(comment)\n\t}\n\tif rawData, ok := optionsutil.Value(args.Internal, \"rawData\").(bool); ok {\n\t\top = op.RawData(rawData)\n\t}\n\tif additionalCmd, ok := optionsutil.Value(args.Internal, \"addCommandFields\").(bson.D); ok {\n\t\top = op.AdditionalCmd(additionalCmd)\n\t}\n\tretry := driver.RetryNone\n\t// retryable writes are only enabled updateOne/replaceOne operations\n\tif !multi && coll.client.retryWrites {\n\t\tretry = driver.RetryOncePerCommand\n\t}\n\top = op.Retry(retry)\n\terr = op.Execute(ctx)\n\n\trr, err := processWriteError(err)\n\tif rr&expectedRr == 0 {\n\t\treturn nil, err\n\t}\n\n\topRes := op.Result()\n\tres := &UpdateResult{\n\t\tMatchedCount:  opRes.N,\n\t\tModifiedCount: opRes.NModified,\n\t\tUpsertedCount: int64(len(opRes.Upserted)),\n\t\tAcknowledged:  rr.isAcknowledged(),\n\t}\n\tif len(opRes.Upserted) > 0 {\n\t\tres.UpsertedID = opRes.Upserted[0].ID\n\t\tres.MatchedCount--\n\t}\n\n\treturn res, err\n}\n\n// UpdateByID executes an update command to update the document whose _id value matches the provided ID in the collection.\n// This is equivalent to running UpdateOne(ctx, bson.D{{\"_id\", id}}, update, opts...).\n//\n// The id parameter is the _id of the document to be updated. It cannot be nil. If the ID does not match any documents,\n// the operation will succeed and an UpdateResult with a MatchedCount of 0 will be returned.\n//\n// The update parameter must be a document containing update operators\n// (https://www.mongodb.com/docs/manual/reference/operator/update/) and can be used to specify the modifications to be\n// made to the selected document. It cannot be nil or empty.\n//\n// The opts parameter can be used to specify options for the operation (see the options.UpdateOptions documentation).\n//\n// For more information about the command, see https://www.mongodb.com/docs/manual/reference/command/update/.\nfunc (coll *Collection) UpdateByID(\n\tctx context.Context,\n\tid any,\n\tupdate any,\n\topts ...options.Lister[options.UpdateOneOptions],\n) (*UpdateResult, error) {\n\tif id == nil {\n\t\treturn nil, fmt.Errorf(\"invalid id: %w\", ErrNilValue)\n\t}\n\treturn coll.UpdateOne(ctx, bson.D{{\"_id\", id}}, update, opts...)\n}\n\n// UpdateOne executes an update command to update at most one document in the collection.\n//\n// The filter parameter must be a document containing query operators and can be used to select the document to be\n// updated. It cannot be nil. If the filter does not match any documents, the operation will succeed and an UpdateResult\n// with a MatchedCount of 0 will be returned. If the filter matches multiple documents, one will be selected from the\n// matched set and MatchedCount will equal 1.\n//\n// The update parameter must be a document containing update operators\n// (https://www.mongodb.com/docs/manual/reference/operator/update/) and can be used to specify the modifications to be\n// made to the selected document. It cannot be nil or empty.\n//\n// The opts parameter can be used to specify options for the operation (see the options.UpdateOptions documentation).\n//\n// For more information about the command, see https://www.mongodb.com/docs/manual/reference/command/update/.\nfunc (coll *Collection) UpdateOne(\n\tctx context.Context,\n\tfilter any,\n\tupdate any,\n\topts ...options.Lister[options.UpdateOneOptions],\n) (*UpdateResult, error) {\n\tif ctx == nil {\n\t\tctx = context.Background()\n\t}\n\n\tf, err := marshal(filter, coll.bsonOpts, coll.registry)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\targs, err := mongoutil.NewOptions[options.UpdateOneOptions](opts...)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to construct options from builder: %w\", err)\n\t}\n\tupdateOptions := &options.UpdateManyOptions{\n\t\tArrayFilters:             args.ArrayFilters,\n\t\tBypassDocumentValidation: args.BypassDocumentValidation,\n\t\tCollation:                args.Collation,\n\t\tComment:                  args.Comment,\n\t\tHint:                     args.Hint,\n\t\tUpsert:                   args.Upsert,\n\t\tLet:                      args.Let,\n\t\tInternal:                 args.Internal,\n\t}\n\n\treturn coll.updateOrReplace(ctx, f, update, false, rrOne, true, args.Sort, updateOptions)\n}\n\n// UpdateMany executes an update command to update documents in the collection.\n//\n// The filter parameter must be a document containing query operators and can be used to select the documents to be\n// updated. It cannot be nil. If the filter does not match any documents, the operation will succeed and an UpdateResult\n// with a MatchedCount of 0 will be returned.\n//\n// The update parameter must be a document containing update operators\n// (https://www.mongodb.com/docs/manual/reference/operator/update/) and can be used to specify the modifications to be made\n// to the selected documents. It cannot be nil or empty.\n//\n// The opts parameter can be used to specify options for the operation (see the options.UpdateOptions documentation).\n//\n// For more information about the command, see https://www.mongodb.com/docs/manual/reference/command/update/.\nfunc (coll *Collection) UpdateMany(\n\tctx context.Context,\n\tfilter any,\n\tupdate any,\n\topts ...options.Lister[options.UpdateManyOptions],\n) (*UpdateResult, error) {\n\tif ctx == nil {\n\t\tctx = context.Background()\n\t}\n\n\tf, err := marshal(filter, coll.bsonOpts, coll.registry)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\targs, err := mongoutil.NewOptions[options.UpdateManyOptions](opts...)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to construct options from builder: %w\", err)\n\t}\n\n\treturn coll.updateOrReplace(ctx, f, update, true, rrMany, true, nil, args)\n}\n\n// ReplaceOne executes an update command to replace at most one document in the collection.\n//\n// The filter parameter must be a document containing query operators and can be used to select the document to be\n// replaced. It cannot be nil. If the filter does not match any documents, the operation will succeed and an\n// UpdateResult with a MatchedCount of 0 will be returned. If the filter matches multiple documents, one will be\n// selected from the matched set and MatchedCount will equal 1.\n//\n// The replacement parameter must be a document that will be used to replace the selected document. It cannot be nil\n// and cannot contain any update operators (https://www.mongodb.com/docs/manual/reference/operator/update/).\n//\n// The opts parameter can be used to specify options for the operation (see the options.ReplaceOptions documentation).\n//\n// For more information about the command, see https://www.mongodb.com/docs/manual/reference/command/update/.\nfunc (coll *Collection) ReplaceOne(\n\tctx context.Context,\n\tfilter any,\n\treplacement any,\n\topts ...options.Lister[options.ReplaceOptions],\n) (*UpdateResult, error) {\n\tif ctx == nil {\n\t\tctx = context.Background()\n\t}\n\n\targs, err := mongoutil.NewOptions[options.ReplaceOptions](opts...)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to construct options from builder: %w\", err)\n\t}\n\n\tf, err := marshal(filter, coll.bsonOpts, coll.registry)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tr, err := marshal(replacement, coll.bsonOpts, coll.registry)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif err := ensureNoDollarKey(r); err != nil {\n\t\treturn nil, err\n\t}\n\n\tupdateOptions := &options.UpdateManyOptions{\n\t\tBypassDocumentValidation: args.BypassDocumentValidation,\n\t\tCollation:                args.Collation,\n\t\tUpsert:                   args.Upsert,\n\t\tHint:                     args.Hint,\n\t\tLet:                      args.Let,\n\t\tComment:                  args.Comment,\n\t\tInternal:                 args.Internal,\n\t}\n\n\treturn coll.updateOrReplace(ctx, f, r, false, rrOne, false, args.Sort, updateOptions)\n}\n\n// Aggregate executes an aggregate command against the collection and returns a cursor over the resulting documents.\n//\n// The pipeline parameter must be an array of documents, each representing an aggregation stage. The pipeline cannot\n// be nil but can be empty. The stage documents must all be non-nil. For a pipeline of bson.D documents, the\n// mongo.Pipeline type can be used. See\n// https://www.mongodb.com/docs/manual/reference/operator/aggregation-pipeline/#db-collection-aggregate-stages for a list of\n// valid stages in aggregations.\n//\n// The opts parameter can be used to specify options for the operation (see the options.AggregateOptions documentation.)\n//\n// For more information about the command, see https://www.mongodb.com/docs/manual/reference/command/aggregate/.\nfunc (coll *Collection) Aggregate(\n\tctx context.Context,\n\tpipeline any,\n\topts ...options.Lister[options.AggregateOptions],\n) (*Cursor, error) {\n\ta := aggregateParams{\n\t\tctx:            ctx,\n\t\tpipeline:       pipeline,\n\t\tclient:         coll.client,\n\t\tregistry:       coll.registry,\n\t\treadConcern:    coll.readConcern,\n\t\twriteConcern:   coll.writeConcern,\n\t\tbsonOpts:       coll.bsonOpts,\n\t\tretryRead:      coll.client.retryReads,\n\t\tdb:             coll.db.name,\n\t\tcol:            coll.name,\n\t\treadSelector:   coll.readSelector,\n\t\twriteSelector:  coll.writeSelector,\n\t\treadPreference: coll.readPreference,\n\t}\n\n\treturn aggregate(a, opts...)\n}\n\n// aggregate is the helper method for Aggregate\nfunc aggregate(a aggregateParams, opts ...options.Lister[options.AggregateOptions]) (cur *Cursor, err error) {\n\tif a.ctx == nil {\n\t\ta.ctx = context.Background()\n\t}\n\n\tpipelineArr, hasOutputStage, err := marshalAggregatePipeline(a.pipeline, a.bsonOpts, a.registry)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tsess := sessionFromContext(a.ctx)\n\t// Always close any created implicit sessions if aggregate returns an error.\n\tdefer func() {\n\t\tif err != nil && sess != nil {\n\t\t\tcloseImplicitSession(sess)\n\t\t}\n\t}()\n\tif sess == nil && a.client.sessionPool != nil {\n\t\tsess = session.NewImplicitClientSession(a.client.sessionPool, a.client.id)\n\t}\n\tif err = a.client.validSession(sess); err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar wc *writeconcern.WriteConcern\n\tif hasOutputStage {\n\t\twc = a.writeConcern\n\t}\n\trc := a.readConcern\n\tif sess.TransactionRunning() {\n\t\twc = nil\n\t\trc = nil\n\t}\n\tif !wc.Acknowledged() {\n\t\tcloseImplicitSession(sess)\n\t\tsess = nil\n\t}\n\n\tselector := makeReadPrefSelector(sess, a.readSelector, a.client.localThreshold)\n\tif hasOutputStage {\n\t\tselector = makeOutputAggregateSelector(sess, a.readPreference, a.client.localThreshold)\n\t}\n\n\targs, err := mongoutil.NewOptions(opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tcursorOpts := a.client.createBaseCursorOptions()\n\n\tcursorOpts.MarshalValueEncoderFn = newEncoderFn(a.bsonOpts, a.registry)\n\n\top := operation.NewAggregate(pipelineArr).\n\t\tSession(sess).\n\t\tWriteConcern(wc).\n\t\tReadConcern(rc).\n\t\tReadPreference(a.readPreference).\n\t\tCommandMonitor(a.client.monitor).\n\t\tServerSelector(selector).\n\t\tClusterClock(a.client.clock).\n\t\tDatabase(a.db).\n\t\tCollection(a.col).\n\t\tDeployment(a.client.deployment).\n\t\tCrypt(a.client.cryptFLE).\n\t\tServerAPI(a.client.serverAPI).\n\t\tHasOutputStage(hasOutputStage).\n\t\tTimeout(a.client.timeout).\n\t\tAuthenticator(a.client.authenticator).\n\t\t// Omit \"maxTimeMS\" from operations that return a user-managed cursor to\n\t\t// prevent confusing \"cursor not found\" errors.\n\t\t//\n\t\t// See DRIVERS-2722 for more detail.\n\t\tOmitMaxTimeMS(true)\n\n\tif args.AllowDiskUse != nil {\n\t\top.AllowDiskUse(*args.AllowDiskUse)\n\t}\n\t// ignore batchSize of 0 with $out\n\tif args.BatchSize != nil && (*args.BatchSize != 0 || !hasOutputStage) {\n\t\top.BatchSize(*args.BatchSize)\n\t\tcursorOpts.BatchSize = *args.BatchSize\n\t}\n\tif args.BypassDocumentValidation != nil && *args.BypassDocumentValidation {\n\t\top.BypassDocumentValidation(*args.BypassDocumentValidation)\n\t}\n\tif args.Collation != nil {\n\t\top.Collation(bsoncore.Document(toDocument(args.Collation)))\n\t}\n\tif args.MaxAwaitTime != nil {\n\t\tcursorOpts.SetMaxAwaitTime(*args.MaxAwaitTime)\n\t}\n\tif args.Comment != nil {\n\t\tcomment, err := marshalValue(args.Comment, a.bsonOpts, a.registry)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\top.Comment(comment)\n\t\tcursorOpts.Comment = comment\n\t}\n\tif args.Hint != nil {\n\t\tif isUnorderedMap(args.Hint) {\n\t\t\treturn nil, ErrMapForOrderedArgument{\"hint\"}\n\t\t}\n\t\thintVal, err := marshalValue(args.Hint, a.bsonOpts, a.registry)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\top.Hint(hintVal)\n\t}\n\tif args.Let != nil {\n\t\tlet, err := marshal(args.Let, a.bsonOpts, a.registry)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\top.Let(let)\n\t}\n\tif args.Custom != nil {\n\t\t// Marshal all custom options before passing to the aggregate operation. Return\n\t\t// any errors from Marshaling.\n\t\tcustomOptions := make(map[string]bsoncore.Value)\n\t\tfor optionName, optionValue := range args.Custom {\n\t\t\toptionValueBSON, err := marshalValue(optionValue, nil, a.registry)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tcustomOptions[optionName] = optionValueBSON\n\t\t}\n\t\top.CustomOptions(customOptions)\n\t}\n\tif rawData, ok := optionsutil.Value(args.Internal, \"rawData\").(bool); ok {\n\t\top = op.RawData(rawData)\n\t}\n\n\tretry := driver.RetryNone\n\tif a.retryRead && !hasOutputStage {\n\t\tretry = driver.RetryOncePerCommand\n\t}\n\top = op.Retry(retry)\n\n\terr = op.Execute(a.ctx)\n\tif err != nil {\n\t\tvar wce driver.WriteCommandError\n\t\tif errors.As(err, &wce) && wce.WriteConcernError != nil {\n\t\t\treturn nil, *convertDriverWriteConcernError(wce.WriteConcernError)\n\t\t}\n\t\treturn nil, wrapErrors(err)\n\t}\n\n\tbc, err := op.Result(cursorOpts)\n\tif err != nil {\n\t\treturn nil, wrapErrors(err)\n\t}\n\tcursor, err := newCursorWithSession(bc, a.client.bsonOpts, a.registry, sess,\n\n\t\t// The only way the server will return a tailable/awaitData cursor for an\n\t\t// aggregate operation is for the first stage in the pipeline to\n\t\t// be $changeStream, this is the only time maxAwaitTimeMS should be applied.\n\t\t// For this reason, we pass the client timeout to the cursor.\n\t\twithCursorOptionClientTimeout(a.client.timeout))\n\treturn cursor, wrapErrors(err)\n}\n\n// CountDocuments returns the number of documents in the collection. For a fast count of the documents in the\n// collection, see the EstimatedDocumentCount method.\n//\n// The filter parameter must be a document and can be used to select which documents contribute to the count. It\n// cannot be nil. An empty document (e.g. bson.D{}) should be used to count all documents in the collection. This will\n// result in a full collection scan.\n//\n// The opts parameter can be used to specify options for the operation (see the options.CountOptions documentation).\nfunc (coll *Collection) CountDocuments(ctx context.Context, filter any,\n\topts ...options.Lister[options.CountOptions],\n) (int64, error) {\n\tif ctx == nil {\n\t\tctx = context.Background()\n\t}\n\n\targs, err := mongoutil.NewOptions[options.CountOptions](opts...)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\tpipelineArr, err := countDocumentsAggregatePipeline(filter, coll.bsonOpts, coll.registry, args)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\tsess := sessionFromContext(ctx)\n\tif sess == nil && coll.client.sessionPool != nil {\n\t\tsess = session.NewImplicitClientSession(coll.client.sessionPool, coll.client.id)\n\t\tdefer sess.EndSession()\n\t}\n\tif err = coll.client.validSession(sess); err != nil {\n\t\treturn 0, err\n\t}\n\n\trc := coll.readConcern\n\tif sess.TransactionRunning() {\n\t\trc = nil\n\t}\n\n\tselector := makeReadPrefSelector(sess, coll.readSelector, coll.client.localThreshold)\n\top := operation.NewAggregate(pipelineArr).Session(sess).ReadConcern(rc).ReadPreference(coll.readPreference).\n\t\tCommandMonitor(coll.client.monitor).ServerSelector(selector).ClusterClock(coll.client.clock).Database(coll.db.name).\n\t\tCollection(coll.name).Deployment(coll.client.deployment).Crypt(coll.client.cryptFLE).ServerAPI(coll.client.serverAPI).\n\t\tTimeout(coll.client.timeout).Authenticator(coll.client.authenticator)\n\tif args.Collation != nil {\n\t\top.Collation(bsoncore.Document(toDocument(args.Collation)))\n\t}\n\tif args.Comment != nil {\n\t\tcomment, err := marshalValue(args.Comment, coll.bsonOpts, coll.registry)\n\t\tif err != nil {\n\t\t\treturn 0, err\n\t\t}\n\n\t\top.Comment(comment)\n\t}\n\tif args.Hint != nil {\n\t\tif isUnorderedMap(args.Hint) {\n\t\t\treturn 0, ErrMapForOrderedArgument{\"hint\"}\n\t\t}\n\t\thintVal, err := marshalValue(args.Hint, coll.bsonOpts, coll.registry)\n\t\tif err != nil {\n\t\t\treturn 0, err\n\t\t}\n\t\top.Hint(hintVal)\n\t}\n\tif rawData, ok := optionsutil.Value(args.Internal, \"rawData\").(bool); ok {\n\t\top = op.RawData(rawData)\n\t}\n\tretry := driver.RetryNone\n\tif coll.client.retryReads {\n\t\tretry = driver.RetryOncePerCommand\n\t}\n\top = op.Retry(retry)\n\n\terr = op.Execute(ctx)\n\tif err != nil {\n\t\treturn 0, wrapErrors(err)\n\t}\n\n\tbatch := op.ResultCursorResponse().FirstBatch\n\tif batch == nil {\n\t\treturn 0, errors.New(\"invalid response from server, no 'firstBatch' field\")\n\t}\n\n\tdocs, err := batch.Documents()\n\tif err != nil || len(docs) == 0 {\n\t\treturn 0, nil\n\t}\n\n\tval, ok := docs[0].Lookup(\"n\").AsInt64OK()\n\tif !ok {\n\t\treturn 0, errors.New(\"invalid response from server, no 'n' field\")\n\t}\n\n\treturn val, nil\n}\n\n// EstimatedDocumentCount executes a count command and returns an estimate of the number of documents in the collection\n// using collection metadata.\n//\n// The opts parameter can be used to specify options for the operation (see the options.EstimatedDocumentCountOptions\n// documentation).\n//\n// For more information about the command, see https://www.mongodb.com/docs/manual/reference/command/count/.\nfunc (coll *Collection) EstimatedDocumentCount(\n\tctx context.Context,\n\topts ...options.Lister[options.EstimatedDocumentCountOptions],\n) (int64, error) {\n\tif ctx == nil {\n\t\tctx = context.Background()\n\t}\n\n\tsess := sessionFromContext(ctx)\n\n\tvar err error\n\tif sess == nil && coll.client.sessionPool != nil {\n\t\tsess = session.NewImplicitClientSession(coll.client.sessionPool, coll.client.id)\n\t\tdefer sess.EndSession()\n\t}\n\n\terr = coll.client.validSession(sess)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\trc := coll.readConcern\n\tif sess.TransactionRunning() {\n\t\trc = nil\n\t}\n\n\targs, err := mongoutil.NewOptions[options.EstimatedDocumentCountOptions](opts...)\n\tif err != nil {\n\t\treturn 0, fmt.Errorf(\"failed to construct options from builder: %w\", err)\n\t}\n\n\tselector := makeReadPrefSelector(sess, coll.readSelector, coll.client.localThreshold)\n\top := operation.NewCount().Session(sess).ClusterClock(coll.client.clock).\n\t\tDatabase(coll.db.name).Collection(coll.name).CommandMonitor(coll.client.monitor).\n\t\tDeployment(coll.client.deployment).ReadConcern(rc).ReadPreference(coll.readPreference).\n\t\tServerSelector(selector).Crypt(coll.client.cryptFLE).ServerAPI(coll.client.serverAPI).\n\t\tTimeout(coll.client.timeout).Authenticator(coll.client.authenticator)\n\n\tif args.Comment != nil {\n\t\tcomment, err := marshalValue(args.Comment, coll.bsonOpts, coll.registry)\n\t\tif err != nil {\n\t\t\treturn 0, err\n\t\t}\n\t\top = op.Comment(comment)\n\t}\n\tif rawData, ok := optionsutil.Value(args.Internal, \"rawData\").(bool); ok {\n\t\top = op.RawData(rawData)\n\t}\n\n\tretry := driver.RetryNone\n\tif coll.client.retryReads {\n\t\tretry = driver.RetryOncePerCommand\n\t}\n\top.Retry(retry)\n\n\terr = op.Execute(ctx)\n\treturn op.Result().N, wrapErrors(err)\n}\n\n// Distinct executes a distinct command to find the unique values for a specified field in the collection.\n//\n// The fieldName parameter specifies the field name for which distinct values should be returned.\n//\n// The filter parameter must be a document containing query operators and can be used to select which documents are\n// considered. It cannot be nil. An empty document (e.g. bson.D{}) should be used to select all documents.\n//\n// The opts parameter can be used to specify options for the operation (see the options.DistinctOptions documentation).\n//\n// For more information about the command, see https://www.mongodb.com/docs/manual/reference/command/distinct/.\nfunc (coll *Collection) Distinct(\n\tctx context.Context,\n\tfieldName string,\n\tfilter any,\n\topts ...options.Lister[options.DistinctOptions],\n) *DistinctResult {\n\tif ctx == nil {\n\t\tctx = context.Background()\n\t}\n\n\tf, err := marshal(filter, coll.bsonOpts, coll.registry)\n\tif err != nil {\n\t\treturn &DistinctResult{err: err}\n\t}\n\n\tsess := sessionFromContext(ctx)\n\n\tif sess == nil && coll.client.sessionPool != nil {\n\t\tsess = session.NewImplicitClientSession(coll.client.sessionPool, coll.client.id)\n\t\tdefer sess.EndSession()\n\t}\n\n\terr = coll.client.validSession(sess)\n\tif err != nil {\n\t\treturn &DistinctResult{err: err}\n\t}\n\n\trc := coll.readConcern\n\tif sess.TransactionRunning() {\n\t\trc = nil\n\t}\n\n\tselector := makeReadPrefSelector(sess, coll.readSelector, coll.client.localThreshold)\n\n\targs, err := mongoutil.NewOptions[options.DistinctOptions](opts...)\n\tif err != nil {\n\t\terr = fmt.Errorf(\"failed to construct options from builder: %w\", err)\n\n\t\treturn &DistinctResult{err: err}\n\t}\n\n\top := operation.NewDistinct(fieldName, f).\n\t\tSession(sess).ClusterClock(coll.client.clock).\n\t\tDatabase(coll.db.name).Collection(coll.name).CommandMonitor(coll.client.monitor).\n\t\tDeployment(coll.client.deployment).ReadConcern(rc).ReadPreference(coll.readPreference).\n\t\tServerSelector(selector).Crypt(coll.client.cryptFLE).ServerAPI(coll.client.serverAPI).\n\t\tTimeout(coll.client.timeout).Authenticator(coll.client.authenticator)\n\n\tif args.Collation != nil {\n\t\top.Collation(bsoncore.Document(toDocument(args.Collation)))\n\t}\n\tif args.Comment != nil {\n\t\tcomment, err := marshalValue(args.Comment, coll.bsonOpts, coll.registry)\n\t\tif err != nil {\n\t\t\treturn &DistinctResult{err: err}\n\t\t}\n\t\top.Comment(comment)\n\t}\n\tif args.Hint != nil {\n\t\tif isUnorderedMap(args.Hint) {\n\t\t\treturn &DistinctResult{err: ErrMapForOrderedArgument{\"hint\"}}\n\t\t}\n\t\thint, err := marshalValue(args.Hint, coll.bsonOpts, coll.registry)\n\t\tif err != nil {\n\t\t\treturn &DistinctResult{err: err}\n\t\t}\n\t\top.Hint(hint)\n\t}\n\tif rawData, ok := optionsutil.Value(args.Internal, \"rawData\").(bool); ok {\n\t\top = op.RawData(rawData)\n\t}\n\tretry := driver.RetryNone\n\tif coll.client.retryReads {\n\t\tretry = driver.RetryOncePerCommand\n\t}\n\top = op.Retry(retry)\n\n\terr = op.Execute(ctx)\n\tif err != nil {\n\t\treturn &DistinctResult{err: wrapErrors(err)}\n\t}\n\n\tarr, ok := op.Result().Values.ArrayOK()\n\tif !ok {\n\t\terr := fmt.Errorf(\"response field 'values' is type array, but received BSON type %s\", op.Result().Values.Type)\n\n\t\treturn &DistinctResult{err: err}\n\t}\n\n\treturn &DistinctResult{\n\t\treg:      coll.registry,\n\t\tarr:      bson.RawArray(arr),\n\t\tbsonOpts: coll.bsonOpts,\n\t}\n}\n\n// Find executes a find command and returns a Cursor over the matching documents in the collection.\n//\n// The filter parameter must be a document containing query operators and can be used to select which documents are\n// included in the result. It cannot be nil. An empty document (e.g. bson.D{}) should be used to include all documents.\n//\n// The opts parameter can be used to specify options for the operation (see the options.FindOptions documentation).\n//\n// For more information about the command, see https://www.mongodb.com/docs/manual/reference/command/find/.\nfunc (coll *Collection) Find(ctx context.Context, filter any,\n\topts ...options.Lister[options.FindOptions],\n) (*Cursor, error) {\n\targs, err := mongoutil.NewOptions(opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// Omit \"maxTimeMS\" from operations that return a user-managed cursor to\n\t// prevent confusing \"cursor not found\" errors.\n\t//\n\t// See DRIVERS-2722 for more detail.\n\treturn coll.find(ctx, filter, true, args)\n}\n\nfunc (coll *Collection) find(\n\tctx context.Context,\n\tfilter any,\n\tomitMaxTimeMS bool,\n\targs *options.FindOptions,\n) (cur *Cursor, err error) {\n\tif ctx == nil {\n\t\tctx = context.Background()\n\t}\n\n\tf, err := marshal(filter, coll.bsonOpts, coll.registry)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tsess := sessionFromContext(ctx)\n\t// Always close any created implicit sessions if Find returns an error.\n\tdefer func() {\n\t\tif err != nil && sess != nil {\n\t\t\tcloseImplicitSession(sess)\n\t\t}\n\t}()\n\tif sess == nil && coll.client.sessionPool != nil {\n\t\tsess = session.NewImplicitClientSession(coll.client.sessionPool, coll.client.id)\n\t}\n\n\terr = coll.client.validSession(sess)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\trc := coll.readConcern\n\tif sess.TransactionRunning() {\n\t\trc = nil\n\t}\n\n\tselector := makeReadPrefSelector(sess, coll.readSelector, coll.client.localThreshold)\n\top := operation.NewFind(f).\n\t\tSession(sess).ReadConcern(rc).ReadPreference(coll.readPreference).\n\t\tCommandMonitor(coll.client.monitor).ServerSelector(selector).\n\t\tClusterClock(coll.client.clock).Database(coll.db.name).Collection(coll.name).\n\t\tDeployment(coll.client.deployment).Crypt(coll.client.cryptFLE).ServerAPI(coll.client.serverAPI).\n\t\tTimeout(coll.client.timeout).Logger(coll.client.logger).Authenticator(coll.client.authenticator).\n\t\tOmitMaxTimeMS(omitMaxTimeMS)\n\n\tcursorOpts := coll.client.createBaseCursorOptions()\n\n\tcursorOpts.MarshalValueEncoderFn = newEncoderFn(coll.bsonOpts, coll.registry)\n\n\tif args.AllowDiskUse != nil {\n\t\top.AllowDiskUse(*args.AllowDiskUse)\n\t}\n\tif args.AllowPartialResults != nil {\n\t\top.AllowPartialResults(*args.AllowPartialResults)\n\t}\n\tif args.BatchSize != nil {\n\t\tcursorOpts.BatchSize = *args.BatchSize\n\t\top.BatchSize(*args.BatchSize)\n\t}\n\tif args.Collation != nil {\n\t\top.Collation(bsoncore.Document(toDocument(args.Collation)))\n\t}\n\tif args.Comment != nil {\n\t\tcomment, err := marshalValue(args.Comment, coll.bsonOpts, coll.registry)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\top.Comment(comment)\n\t\tcursorOpts.Comment = comment\n\t}\n\tif args.CursorType != nil {\n\t\tswitch *args.CursorType {\n\t\tcase options.Tailable:\n\t\t\top.Tailable(true)\n\t\tcase options.TailableAwait:\n\t\t\top.Tailable(true)\n\t\t\top.AwaitData(true)\n\t\t}\n\t}\n\tif args.Hint != nil {\n\t\tif isUnorderedMap(args.Hint) {\n\t\t\treturn nil, ErrMapForOrderedArgument{\"hint\"}\n\t\t}\n\t\thint, err := marshalValue(args.Hint, coll.bsonOpts, coll.registry)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\top.Hint(hint)\n\t}\n\tif args.Let != nil {\n\t\tlet, err := marshal(args.Let, coll.bsonOpts, coll.registry)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\top.Let(let)\n\t}\n\tif args.Limit != nil {\n\t\tlimit := *args.Limit\n\t\tif limit < 0 {\n\t\t\tlimit = -1 * limit\n\t\t\top.SingleBatch(true)\n\t\t}\n\t\tcursorOpts.Limit = int32(limit)\n\t\top.Limit(limit)\n\t}\n\tif args.Max != nil {\n\t\tmax, err := marshal(args.Max, coll.bsonOpts, coll.registry)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\top.Max(max)\n\t}\n\tif args.MaxAwaitTime != nil {\n\t\tcursorOpts.SetMaxAwaitTime(*args.MaxAwaitTime)\n\t}\n\tif args.Min != nil {\n\t\tmin, err := marshal(args.Min, coll.bsonOpts, coll.registry)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\top.Min(min)\n\t}\n\tif args.NoCursorTimeout != nil {\n\t\top.NoCursorTimeout(*args.NoCursorTimeout)\n\t}\n\tif args.OplogReplay != nil {\n\t\top.OplogReplay(*args.OplogReplay)\n\t}\n\tif args.Projection != nil {\n\t\tproj, err := marshal(args.Projection, coll.bsonOpts, coll.registry)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\top.Projection(proj)\n\t}\n\tif args.ReturnKey != nil {\n\t\top.ReturnKey(*args.ReturnKey)\n\t}\n\tif args.ShowRecordID != nil {\n\t\top.ShowRecordID(*args.ShowRecordID)\n\t}\n\tif args.Skip != nil {\n\t\top.Skip(*args.Skip)\n\t}\n\tif args.Sort != nil {\n\t\tif isUnorderedMap(args.Sort) {\n\t\t\treturn nil, ErrMapForOrderedArgument{\"sort\"}\n\t\t}\n\t\tsort, err := marshal(args.Sort, coll.bsonOpts, coll.registry)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\top.Sort(sort)\n\t}\n\tif rawData, ok := optionsutil.Value(args.Internal, \"rawData\").(bool); ok {\n\t\top = op.RawData(rawData)\n\t}\n\tretry := driver.RetryNone\n\tif coll.client.retryReads {\n\t\tretry = driver.RetryOncePerCommand\n\t}\n\top = op.Retry(retry)\n\n\tif err = op.Execute(ctx); err != nil {\n\t\treturn nil, wrapErrors(err)\n\t}\n\n\tbc, err := op.Result(cursorOpts)\n\tif err != nil {\n\t\treturn nil, wrapErrors(err)\n\t}\n\n\treturn newCursorWithSession(bc, coll.bsonOpts, coll.registry, sess,\n\t\twithCursorOptionClientTimeout(coll.client.timeout))\n}\n\nfunc newFindArgsFromFindOneArgs(args *options.FindOneOptions) *options.FindOptions {\n\tvar limit int64 = -1\n\tv := &options.FindOptions{Limit: &limit}\n\tif args != nil {\n\t\tv.AllowPartialResults = args.AllowPartialResults\n\t\tv.Collation = args.Collation\n\t\tv.Comment = args.Comment\n\t\tv.Hint = args.Hint\n\t\tv.Max = args.Max\n\t\tv.Min = args.Min\n\t\tv.OplogReplay = args.OplogReplay\n\t\tv.Projection = args.Projection\n\t\tv.ReturnKey = args.ReturnKey\n\t\tv.ShowRecordID = args.ShowRecordID\n\t\tv.Skip = args.Skip\n\t\tv.Sort = args.Sort\n\t\tv.Internal = args.Internal\n\t}\n\treturn v\n}\n\n// FindOne executes a find command and returns a SingleResult for one document in the collection.\n//\n// The filter parameter must be a document containing query operators and can be used to select the document to be\n// returned. It cannot be nil. If the filter does not match any documents, a SingleResult with an error set to\n// ErrNoDocuments will be returned. If the filter matches multiple documents, one will be selected from the matched set.\n//\n// The opts parameter can be used to specify options for this operation (see the options.FindOneOptions documentation).\n//\n// For more information about the command, see https://www.mongodb.com/docs/manual/reference/command/find/.\nfunc (coll *Collection) FindOne(ctx context.Context, filter any,\n\topts ...options.Lister[options.FindOneOptions],\n) *SingleResult {\n\tif ctx == nil {\n\t\tctx = context.Background()\n\t}\n\n\targs, err := mongoutil.NewOptions(opts...)\n\tif err != nil {\n\t\treturn &SingleResult{err: err}\n\t}\n\tcursor, err := coll.find(ctx, filter, false, newFindArgsFromFindOneArgs(args))\n\treturn &SingleResult{\n\t\tctx:      ctx,\n\t\tcur:      cursor,\n\t\tbsonOpts: coll.bsonOpts,\n\t\treg:      coll.registry,\n\t\terr:      wrapErrors(err),\n\t}\n}\n\nfunc (coll *Collection) findAndModify(ctx context.Context, op *operation.FindAndModify) *SingleResult {\n\tif ctx == nil {\n\t\tctx = context.Background()\n\t}\n\n\tsess := sessionFromContext(ctx)\n\tvar err error\n\tif sess == nil && coll.client.sessionPool != nil {\n\t\tsess = session.NewImplicitClientSession(coll.client.sessionPool, coll.client.id)\n\t\tdefer sess.EndSession()\n\t}\n\n\terr = coll.client.validSession(sess)\n\tif err != nil {\n\t\treturn &SingleResult{err: err}\n\t}\n\n\twc := coll.writeConcern\n\tif sess.TransactionRunning() {\n\t\twc = nil\n\t}\n\tif !wc.Acknowledged() {\n\t\tsess = nil\n\t}\n\n\tselector := makePinnedSelector(sess, coll.writeSelector)\n\n\tretry := driver.RetryNone\n\tif coll.client.retryWrites {\n\t\tretry = driver.RetryOnce\n\t}\n\n\top = op.Session(sess).\n\t\tWriteConcern(wc).\n\t\tCommandMonitor(coll.client.monitor).\n\t\tServerSelector(selector).\n\t\tClusterClock(coll.client.clock).\n\t\tDatabase(coll.db.name).\n\t\tCollection(coll.name).\n\t\tDeployment(coll.client.deployment).\n\t\tRetry(retry).\n\t\tCrypt(coll.client.cryptFLE)\n\n\trr, err := processWriteError(op.Execute(ctx))\n\tif err != nil {\n\t\treturn &SingleResult{err: err}\n\t}\n\n\treturn &SingleResult{\n\t\tctx:          ctx,\n\t\trdr:          bson.Raw(op.Result().Value),\n\t\tbsonOpts:     coll.bsonOpts,\n\t\treg:          coll.registry,\n\t\tAcknowledged: rr.isAcknowledged(),\n\t}\n}\n\n// FindOneAndDelete executes a findAndModify command to delete at most one document in the collection. and returns the\n// document as it appeared before deletion.\n//\n// The filter parameter must be a document containing query operators and can be used to select the document to be\n// deleted. It cannot be nil. If the filter does not match any documents, a SingleResult with an error set to\n// ErrNoDocuments wil be returned. If the filter matches multiple documents, one will be selected from the matched set.\n//\n// The opts parameter can be used to specify options for the operation (see the options.FindOneAndDeleteOptions\n// documentation).\n//\n// For more information about the command, see https://www.mongodb.com/docs/manual/reference/command/findAndModify/.\nfunc (coll *Collection) FindOneAndDelete(\n\tctx context.Context,\n\tfilter any,\n\topts ...options.Lister[options.FindOneAndDeleteOptions],\n) *SingleResult {\n\tf, err := marshal(filter, coll.bsonOpts, coll.registry)\n\tif err != nil {\n\t\treturn &SingleResult{err: err}\n\t}\n\n\targs, err := mongoutil.NewOptions[options.FindOneAndDeleteOptions](opts...)\n\tif err != nil {\n\t\treturn &SingleResult{err: fmt.Errorf(\"failed to construct options from builder: %w\", err)}\n\t}\n\n\top := operation.NewFindAndModify(f).Remove(true).ServerAPI(coll.client.serverAPI).Timeout(coll.client.timeout).Authenticator(coll.client.authenticator)\n\tif args.Collation != nil {\n\t\top = op.Collation(bsoncore.Document(toDocument(args.Collation)))\n\t}\n\tif args.Comment != nil {\n\t\tcomment, err := marshalValue(args.Comment, coll.bsonOpts, coll.registry)\n\t\tif err != nil {\n\t\t\treturn &SingleResult{err: err}\n\t\t}\n\t\top = op.Comment(comment)\n\t}\n\tif args.Projection != nil {\n\t\tproj, err := marshal(args.Projection, coll.bsonOpts, coll.registry)\n\t\tif err != nil {\n\t\t\treturn &SingleResult{err: err}\n\t\t}\n\t\top = op.Fields(proj)\n\t}\n\tif args.Sort != nil {\n\t\tif isUnorderedMap(args.Sort) {\n\t\t\treturn &SingleResult{err: ErrMapForOrderedArgument{\"sort\"}}\n\t\t}\n\t\tsort, err := marshal(args.Sort, coll.bsonOpts, coll.registry)\n\t\tif err != nil {\n\t\t\treturn &SingleResult{err: err}\n\t\t}\n\t\top = op.Sort(sort)\n\t}\n\tif args.Hint != nil {\n\t\tif isUnorderedMap(args.Hint) {\n\t\t\treturn &SingleResult{err: ErrMapForOrderedArgument{\"hint\"}}\n\t\t}\n\t\thint, err := marshalValue(args.Hint, coll.bsonOpts, coll.registry)\n\t\tif err != nil {\n\t\t\treturn &SingleResult{err: err}\n\t\t}\n\t\top = op.Hint(hint)\n\t}\n\tif args.Let != nil {\n\t\tlet, err := marshal(args.Let, coll.bsonOpts, coll.registry)\n\t\tif err != nil {\n\t\t\treturn &SingleResult{err: err}\n\t\t}\n\t\top = op.Let(let)\n\t}\n\tif rawData, ok := optionsutil.Value(args.Internal, \"rawData\").(bool); ok {\n\t\top = op.RawData(rawData)\n\t}\n\n\treturn coll.findAndModify(ctx, op)\n}\n\n// FindOneAndReplace executes a findAndModify command to replace at most one document in the collection\n// and returns the document as it appeared before replacement.\n//\n// The filter parameter must be a document containing query operators and can be used to select the document to be\n// replaced. It cannot be nil. If the filter does not match any documents, a SingleResult with an error set to\n// ErrNoDocuments wil be returned. If the filter matches multiple documents, one will be selected from the matched set.\n//\n// The replacement parameter must be a document that will be used to replace the selected document. It cannot be nil\n// and cannot contain any update operators (https://www.mongodb.com/docs/manual/reference/operator/update/).\n//\n// The opts parameter can be used to specify options for the operation (see the options.FindOneAndReplaceOptions\n// documentation).\n//\n// For more information about the command, see https://www.mongodb.com/docs/manual/reference/command/findAndModify/.\nfunc (coll *Collection) FindOneAndReplace(\n\tctx context.Context,\n\tfilter any,\n\treplacement any,\n\topts ...options.Lister[options.FindOneAndReplaceOptions],\n) *SingleResult {\n\tf, err := marshal(filter, coll.bsonOpts, coll.registry)\n\tif err != nil {\n\t\treturn &SingleResult{err: err}\n\t}\n\tr, err := marshal(replacement, coll.bsonOpts, coll.registry)\n\tif err != nil {\n\t\treturn &SingleResult{err: err}\n\t}\n\tif firstElem, err := r.IndexErr(0); err == nil && strings.HasPrefix(firstElem.Key(), \"$\") {\n\t\treturn &SingleResult{err: errors.New(\"replacement document cannot contain keys beginning with '$'\")}\n\t}\n\n\targs, err := mongoutil.NewOptions[options.FindOneAndReplaceOptions](opts...)\n\tif err != nil {\n\t\treturn &SingleResult{err: fmt.Errorf(\"failed to construct options from builder: %w\", err)}\n\t}\n\n\top := operation.NewFindAndModify(f).Update(bsoncore.Value{Type: bsoncore.TypeEmbeddedDocument, Data: r}).\n\t\tServerAPI(coll.client.serverAPI).Timeout(coll.client.timeout).Authenticator(coll.client.authenticator)\n\tif args.BypassDocumentValidation != nil && *args.BypassDocumentValidation {\n\t\top = op.BypassDocumentValidation(*args.BypassDocumentValidation)\n\t}\n\tif args.Collation != nil {\n\t\top = op.Collation(bsoncore.Document(toDocument(args.Collation)))\n\t}\n\tif args.Comment != nil {\n\t\tcomment, err := marshalValue(args.Comment, coll.bsonOpts, coll.registry)\n\t\tif err != nil {\n\t\t\treturn &SingleResult{err: err}\n\t\t}\n\t\top = op.Comment(comment)\n\t}\n\tif args.Projection != nil {\n\t\tproj, err := marshal(args.Projection, coll.bsonOpts, coll.registry)\n\t\tif err != nil {\n\t\t\treturn &SingleResult{err: err}\n\t\t}\n\t\top = op.Fields(proj)\n\t}\n\tif args.ReturnDocument != nil {\n\t\top = op.NewDocument(*args.ReturnDocument == options.After)\n\t}\n\tif args.Sort != nil {\n\t\tif isUnorderedMap(args.Sort) {\n\t\t\treturn &SingleResult{err: ErrMapForOrderedArgument{\"sort\"}}\n\t\t}\n\t\tsort, err := marshal(args.Sort, coll.bsonOpts, coll.registry)\n\t\tif err != nil {\n\t\t\treturn &SingleResult{err: err}\n\t\t}\n\t\top = op.Sort(sort)\n\t}\n\tif args.Upsert != nil {\n\t\top = op.Upsert(*args.Upsert)\n\t}\n\tif args.Hint != nil {\n\t\tif isUnorderedMap(args.Hint) {\n\t\t\treturn &SingleResult{err: ErrMapForOrderedArgument{\"hint\"}}\n\t\t}\n\t\thint, err := marshalValue(args.Hint, coll.bsonOpts, coll.registry)\n\t\tif err != nil {\n\t\t\treturn &SingleResult{err: err}\n\t\t}\n\t\top = op.Hint(hint)\n\t}\n\tif args.Let != nil {\n\t\tlet, err := marshal(args.Let, coll.bsonOpts, coll.registry)\n\t\tif err != nil {\n\t\t\treturn &SingleResult{err: err}\n\t\t}\n\t\top = op.Let(let)\n\t}\n\tif rawData, ok := optionsutil.Value(args.Internal, \"rawData\").(bool); ok {\n\t\top = op.RawData(rawData)\n\t}\n\tif additionalCmd, ok := optionsutil.Value(args.Internal, \"addCommandFields\").(bson.D); ok {\n\t\top = op.AdditionalCmd(additionalCmd)\n\t}\n\n\treturn coll.findAndModify(ctx, op)\n}\n\n// FindOneAndUpdate executes a findAndModify command to update at most one document in the collection and returns the\n// document as it appeared before updating.\n//\n// The filter parameter must be a document containing query operators and can be used to select the document to be\n// updated. It cannot be nil. If the filter does not match any documents, a SingleResult with an error set to\n// ErrNoDocuments wil be returned. If the filter matches multiple documents, one will be selected from the matched set.\n//\n// The update parameter must be a document containing update operators\n// (https://www.mongodb.com/docs/manual/reference/operator/update/) and can be used to specify the modifications to be made\n// to the selected document. It cannot be nil or empty.\n//\n// The opts parameter can be used to specify options for the operation (see the options.FindOneAndUpdateOptions\n// documentation).\n//\n// For more information about the command, see https://www.mongodb.com/docs/manual/reference/command/findAndModify/.\nfunc (coll *Collection) FindOneAndUpdate(\n\tctx context.Context,\n\tfilter any,\n\tupdate any,\n\topts ...options.Lister[options.FindOneAndUpdateOptions],\n) *SingleResult {\n\tif ctx == nil {\n\t\tctx = context.Background()\n\t}\n\n\tf, err := marshal(filter, coll.bsonOpts, coll.registry)\n\tif err != nil {\n\t\treturn &SingleResult{err: err}\n\t}\n\n\targs, err := mongoutil.NewOptions[options.FindOneAndUpdateOptions](opts...)\n\tif err != nil {\n\t\treturn &SingleResult{err: fmt.Errorf(\"failed to construct options from builder: %w\", err)}\n\t}\n\n\top := operation.NewFindAndModify(f).ServerAPI(coll.client.serverAPI).Timeout(coll.client.timeout).Authenticator(coll.client.authenticator)\n\n\tu, err := marshalUpdateValue(update, coll.bsonOpts, coll.registry, true)\n\tif err != nil {\n\t\treturn &SingleResult{err: err}\n\t}\n\top = op.Update(u)\n\n\tif args.ArrayFilters != nil {\n\t\taf := args.ArrayFilters\n\t\treg := coll.registry\n\t\tfiltersDoc, err := marshalValue(af, coll.bsonOpts, reg)\n\t\tif err != nil {\n\t\t\treturn &SingleResult{err: err}\n\t\t}\n\t\top = op.ArrayFilters(filtersDoc.Data)\n\t}\n\tif args.BypassDocumentValidation != nil && *args.BypassDocumentValidation {\n\t\top = op.BypassDocumentValidation(*args.BypassDocumentValidation)\n\t}\n\tif args.Collation != nil {\n\t\top = op.Collation(bsoncore.Document(toDocument(args.Collation)))\n\t}\n\tif args.Comment != nil {\n\t\tcomment, err := marshalValue(args.Comment, coll.bsonOpts, coll.registry)\n\t\tif err != nil {\n\t\t\treturn &SingleResult{err: err}\n\t\t}\n\t\top = op.Comment(comment)\n\t}\n\tif args.Projection != nil {\n\t\tproj, err := marshal(args.Projection, coll.bsonOpts, coll.registry)\n\t\tif err != nil {\n\t\t\treturn &SingleResult{err: err}\n\t\t}\n\t\top = op.Fields(proj)\n\t}\n\tif args.ReturnDocument != nil {\n\t\top = op.NewDocument(*args.ReturnDocument == options.After)\n\t}\n\tif args.Sort != nil {\n\t\tif isUnorderedMap(args.Sort) {\n\t\t\treturn &SingleResult{err: ErrMapForOrderedArgument{\"sort\"}}\n\t\t}\n\t\tsort, err := marshal(args.Sort, coll.bsonOpts, coll.registry)\n\t\tif err != nil {\n\t\t\treturn &SingleResult{err: err}\n\t\t}\n\t\top = op.Sort(sort)\n\t}\n\tif args.Upsert != nil {\n\t\top = op.Upsert(*args.Upsert)\n\t}\n\tif args.Hint != nil {\n\t\tif isUnorderedMap(args.Hint) {\n\t\t\treturn &SingleResult{err: ErrMapForOrderedArgument{\"hint\"}}\n\t\t}\n\t\thint, err := marshalValue(args.Hint, coll.bsonOpts, coll.registry)\n\t\tif err != nil {\n\t\t\treturn &SingleResult{err: err}\n\t\t}\n\t\top = op.Hint(hint)\n\t}\n\tif args.Let != nil {\n\t\tlet, err := marshal(args.Let, coll.bsonOpts, coll.registry)\n\t\tif err != nil {\n\t\t\treturn &SingleResult{err: err}\n\t\t}\n\t\top = op.Let(let)\n\t}\n\tif rawData, ok := optionsutil.Value(args.Internal, \"rawData\").(bool); ok {\n\t\top = op.RawData(rawData)\n\t}\n\tif additionalCmd, ok := optionsutil.Value(args.Internal, \"addCommandFields\").(bson.D); ok {\n\t\top = op.AdditionalCmd(additionalCmd)\n\t}\n\n\treturn coll.findAndModify(ctx, op)\n}\n\n// Watch returns a change stream for all changes on the corresponding collection. See\n// https://www.mongodb.com/docs/manual/changeStreams/ for more information about change streams.\n//\n// The Collection must be configured with read concern majority or no read concern for a change stream to be created\n// successfully.\n//\n// The pipeline parameter must be an array of documents, each representing a pipeline stage. The pipeline cannot be\n// nil but can be empty. The stage documents must all be non-nil. See https://www.mongodb.com/docs/manual/changeStreams/ for\n// a list of pipeline stages that can be used with change streams. For a pipeline of bson.D documents, the\n// mongo.Pipeline{} type can be used.\n//\n// The opts parameter can be used to specify options for change stream creation (see the options.ChangeStreamOptions\n// documentation).\nfunc (coll *Collection) Watch(ctx context.Context, pipeline any,\n\topts ...options.Lister[options.ChangeStreamOptions],\n) (*ChangeStream, error) {\n\tcsConfig := changeStreamConfig{\n\t\treadConcern:    coll.readConcern,\n\t\treadPreference: coll.readPreference,\n\t\tclient:         coll.client,\n\t\tbsonOpts:       coll.bsonOpts,\n\t\tregistry:       coll.registry,\n\t\tstreamType:     CollectionStream,\n\t\tcollectionName: coll.Name(),\n\t\tdatabaseName:   coll.db.Name(),\n\t\tcrypt:          coll.client.cryptFLE,\n\t}\n\treturn newChangeStream(ctx, csConfig, pipeline, opts...)\n}\n\n// Indexes returns an IndexView instance that can be used to perform operations on the indexes for the collection.\nfunc (coll *Collection) Indexes() IndexView {\n\treturn IndexView{coll: coll}\n}\n\n// SearchIndexes returns a SearchIndexView instance that can be used to perform operations on the search indexes for the collection.\nfunc (coll *Collection) SearchIndexes() SearchIndexView {\n\tc := coll.Clone()\n\tc.readConcern = nil\n\tc.writeConcern = nil\n\treturn SearchIndexView{\n\t\tcoll: c,\n\t}\n}\n\n// Drop drops the collection on the server. This method ignores \"namespace not found\" errors so it is safe to drop\n// a collection that does not exist on the server.\nfunc (coll *Collection) Drop(ctx context.Context, opts ...options.Lister[options.DropCollectionOptions]) error {\n\targs, err := mongoutil.NewOptions[options.DropCollectionOptions](opts...)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to construct options from builder: %w\", err)\n\t}\n\n\tef := args.EncryptedFields\n\n\tif ef == nil {\n\t\tef = coll.db.getEncryptedFieldsFromMap(coll.name)\n\t}\n\n\tif ef == nil && coll.db.client.encryptedFieldsMap != nil {\n\t\tvar err error\n\t\tif ef, err = coll.db.getEncryptedFieldsFromServer(ctx, coll.name); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tif ef != nil {\n\t\treturn coll.dropEncryptedCollection(ctx, ef)\n\t}\n\n\treturn coll.drop(ctx)\n}\n\n// dropEncryptedCollection drops a collection with EncryptedFields.\nfunc (coll *Collection) dropEncryptedCollection(ctx context.Context, ef any) error {\n\tefBSON, err := marshal(ef, coll.bsonOpts, coll.registry)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"error transforming document: %w\", err)\n\t}\n\n\t// Drop the two encryption-related, associated collections: `escCollection` and `ecocCollection`.\n\t// Drop ESCCollection.\n\tescCollection, err := csfle.GetEncryptedStateCollectionName(efBSON, coll.name, csfle.EncryptedStateCollection)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif err := coll.db.Collection(escCollection).drop(ctx); err != nil {\n\t\treturn err\n\t}\n\n\t// Drop ECOCCollection.\n\tecocCollection, err := csfle.GetEncryptedStateCollectionName(efBSON, coll.name, csfle.EncryptedCompactionCollection)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif err := coll.db.Collection(ecocCollection).drop(ctx); err != nil {\n\t\treturn err\n\t}\n\n\t// Drop the data collection.\n\treturn coll.drop(ctx)\n}\n\n// drop drops a collection without EncryptedFields.\nfunc (coll *Collection) drop(ctx context.Context) error {\n\tif ctx == nil {\n\t\tctx = context.Background()\n\t}\n\n\tsess := sessionFromContext(ctx)\n\tif sess == nil && coll.client.sessionPool != nil {\n\t\tsess = session.NewImplicitClientSession(coll.client.sessionPool, coll.client.id)\n\t\tdefer sess.EndSession()\n\t}\n\n\terr := coll.client.validSession(sess)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\twc := coll.writeConcern\n\tif sess.TransactionRunning() {\n\t\twc = nil\n\t}\n\tif !wc.Acknowledged() {\n\t\tsess = nil\n\t}\n\n\tselector := makePinnedSelector(sess, coll.writeSelector)\n\n\top := operation.NewDropCollection().\n\t\tSession(sess).WriteConcern(wc).CommandMonitor(coll.client.monitor).\n\t\tServerSelector(selector).ClusterClock(coll.client.clock).\n\t\tDatabase(coll.db.name).Collection(coll.name).\n\t\tDeployment(coll.client.deployment).Crypt(coll.client.cryptFLE).\n\t\tServerAPI(coll.client.serverAPI).Timeout(coll.client.timeout).\n\t\tAuthenticator(coll.client.authenticator)\n\terr = op.Execute(ctx)\n\n\t// ignore namespace not found errors\n\tvar driverErr driver.Error\n\tif !errors.As(err, &driverErr) || !driverErr.NamespaceNotFound() {\n\t\treturn wrapErrors(err)\n\t}\n\treturn nil\n}\n\nfunc toDocument(co *options.Collation) bson.Raw {\n\tidx, doc := bsoncore.AppendDocumentStart(nil)\n\tif co.Locale != \"\" {\n\t\tdoc = bsoncore.AppendStringElement(doc, \"locale\", co.Locale)\n\t}\n\tif co.CaseLevel {\n\t\tdoc = bsoncore.AppendBooleanElement(doc, \"caseLevel\", true)\n\t}\n\tif co.CaseFirst != \"\" {\n\t\tdoc = bsoncore.AppendStringElement(doc, \"caseFirst\", co.CaseFirst)\n\t}\n\tif co.Strength != 0 {\n\t\tdoc = bsoncore.AppendInt32Element(doc, \"strength\", int32(co.Strength))\n\t}\n\tif co.NumericOrdering {\n\t\tdoc = bsoncore.AppendBooleanElement(doc, \"numericOrdering\", true)\n\t}\n\tif co.Alternate != \"\" {\n\t\tdoc = bsoncore.AppendStringElement(doc, \"alternate\", co.Alternate)\n\t}\n\tif co.MaxVariable != \"\" {\n\t\tdoc = bsoncore.AppendStringElement(doc, \"maxVariable\", co.MaxVariable)\n\t}\n\tif co.Normalization {\n\t\tdoc = bsoncore.AppendBooleanElement(doc, \"normalization\", true)\n\t}\n\tif co.Backwards {\n\t\tdoc = bsoncore.AppendBooleanElement(doc, \"backwards\", true)\n\t}\n\tdoc, _ = bsoncore.AppendDocumentEnd(doc, idx)\n\treturn doc\n}\n\ntype pinnedServerSelector struct {\n\tstringer fmt.Stringer\n\tfallback description.ServerSelector\n\tsession  *session.Client\n}\n\nvar _ description.ServerSelector = pinnedServerSelector{}\n\nfunc (pss pinnedServerSelector) String() string {\n\tif pss.stringer == nil {\n\t\treturn \"\"\n\t}\n\n\treturn pss.stringer.String()\n}\n\nfunc (pss pinnedServerSelector) SelectServer(\n\tt description.Topology,\n\tsvrs []description.Server,\n) ([]description.Server, error) {\n\tif pss.session != nil && pss.session.PinnedServerAddr != nil {\n\t\t// If there is a pinned server, try to find it in the list of candidates.\n\t\tfor _, candidate := range svrs {\n\t\t\tif candidate.Addr == *pss.session.PinnedServerAddr {\n\t\t\t\treturn []description.Server{candidate}, nil\n\t\t\t}\n\t\t}\n\n\t\treturn nil, nil\n\t}\n\n\treturn pss.fallback.SelectServer(t, svrs)\n}\n\nfunc makePinnedSelector(sess *session.Client, fallback description.ServerSelector) pinnedServerSelector {\n\tpss := pinnedServerSelector{\n\t\tsession:  sess,\n\t\tfallback: fallback,\n\t}\n\n\tif srvSelectorStringer, ok := fallback.(fmt.Stringer); ok {\n\t\tpss.stringer = srvSelectorStringer\n\t}\n\n\treturn pss\n}\n\nfunc makeReadPrefSelector(\n\tsess *session.Client,\n\tselector description.ServerSelector,\n\tlocalThreshold time.Duration,\n) pinnedServerSelector {\n\tif sess != nil && sess.TransactionRunning() {\n\t\tselector = &serverselector.Composite{\n\t\t\tSelectors: []description.ServerSelector{\n\t\t\t\t&serverselector.ReadPref{ReadPref: sess.CurrentRp},\n\t\t\t\t&serverselector.Latency{Latency: localThreshold},\n\t\t\t},\n\t\t}\n\t}\n\n\treturn makePinnedSelector(sess, selector)\n}\n\nfunc makeOutputAggregateSelector(\n\tsess *session.Client,\n\trp *readpref.ReadPref,\n\tlocalThreshold time.Duration,\n) pinnedServerSelector {\n\tif sess != nil && sess.TransactionRunning() {\n\t\t// Use current transaction's read preference if available\n\t\trp = sess.CurrentRp\n\t}\n\n\tselector := &serverselector.Composite{\n\t\tSelectors: []description.ServerSelector{\n\t\t\t&serverselector.ReadPref{ReadPref: rp, IsOutputAggregate: true},\n\t\t\t&serverselector.Latency{Latency: localThreshold},\n\t\t},\n\t}\n\n\treturn makePinnedSelector(sess, selector)\n}\n\n// isUnorderedMap returns true if val is a map with more than 1 element. It is typically used to\n// check for unordered Go values that are used in nested command documents where different field\n// orders mean different things. Examples are the \"sort\" and \"hint\" fields.\nfunc isUnorderedMap(val any) bool {\n\trefValue := reflect.ValueOf(val)\n\treturn refValue.Kind() == reflect.Map && refValue.Len() > 1\n}\n"
  },
  {
    "path": "mongo/collection_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage mongo\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"errors\"\n\t\"testing\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/ptrutil\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/require\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/readconcern\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/readpref\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/writeconcern\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/topology\"\n)\n\nconst (\n\ttestDBName = \"unitTestDB\"\n)\n\nfunc setupColl(name string, opts ...options.Lister[options.CollectionOptions]) *Collection {\n\tdb := setupDB(testDBName)\n\treturn db.Collection(name, opts...)\n}\n\nfunc compareColls(t *testing.T, expected *Collection, got *Collection) {\n\tassert.Equal(t, expected.readPreference, got.readPreference,\n\t\t\"mismatch; expected read preference %v, got %v\", expected.readPreference, got.readPreference)\n\tassert.Equal(t, expected.readConcern, got.readConcern,\n\t\t\"mismatch; expected read concern %v, got %v\", expected.readConcern, got.readConcern)\n\tassert.Equal(t, expected.writeConcern, got.writeConcern,\n\t\t\"mismatch; expected write concern %v, got %v\", expected.writeConcern, got.writeConcern)\n}\n\nfunc TestCollection(t *testing.T) {\n\tt.Run(\"initialize\", func(t *testing.T) {\n\t\tname := \"foo\"\n\t\tcoll := setupColl(name)\n\t\tassert.Equal(t, name, coll.Name(), \"expected coll name %v, got %v\", name, coll.Name())\n\t\tassert.NotNil(t, coll.Database(), \"expected valid database, got nil\")\n\t})\n\tt.Run(\"specified options\", func(t *testing.T) {\n\t\trpPrimary := readpref.Primary()\n\t\trpSecondary := readpref.Secondary()\n\t\twc1 := &writeconcern.WriteConcern{W: 5}\n\t\twc2 := &writeconcern.WriteConcern{W: 10}\n\t\trcLocal := readconcern.Local()\n\t\trcMajority := readconcern.Majority()\n\n\t\topts := options.Collection().SetReadPreference(rpPrimary).SetReadConcern(rcLocal).SetWriteConcern(wc1).\n\t\t\tSetReadPreference(rpSecondary).SetReadConcern(rcMajority).SetWriteConcern(wc2)\n\t\texpected := &Collection{\n\t\t\treadConcern:    rcMajority,\n\t\t\treadPreference: rpSecondary,\n\t\t\twriteConcern:   wc2,\n\t\t}\n\t\tgot := setupColl(\"foo\", opts)\n\t\tcompareColls(t, expected, got)\n\t})\n\tt.Run(\"inherit options\", func(t *testing.T) {\n\t\trpPrimary := readpref.Primary()\n\t\trcLocal := readconcern.Local()\n\t\twc1 := &writeconcern.WriteConcern{W: 10}\n\n\t\tdb := setupDB(\"foo\", options.Database().SetReadPreference(rpPrimary).SetReadConcern(rcLocal))\n\t\tcoll := db.Collection(\"bar\", options.Collection().SetWriteConcern(wc1))\n\t\texpected := &Collection{\n\t\t\treadPreference: rpPrimary,\n\t\t\treadConcern:    rcLocal,\n\t\t\twriteConcern:   wc1,\n\t\t}\n\t\tcompareColls(t, expected, coll)\n\t})\n\tt.Run(\"replaceErrors for disconnected topology\", func(t *testing.T) {\n\t\tcoll := setupColl(\"foo\")\n\t\tdoc := bson.D{}\n\t\tupdate := bson.D{{\"$update\", bson.D{{\"x\", 1}}}}\n\n\t\ttopo, ok := coll.client.deployment.(*topology.Topology)\n\t\trequire.True(t, ok, \"client deployment is not a topology\")\n\n\t\terr := topo.Disconnect(context.Background())\n\t\trequire.NoError(t, err)\n\n\t\t_, err = coll.InsertOne(bgCtx, doc)\n\t\tassert.Equal(t, ErrClientDisconnected, err, \"expected error %v, got %v\", ErrClientDisconnected, err)\n\n\t\t_, err = coll.InsertMany(bgCtx, []any{doc})\n\t\tassert.Equal(t, ErrClientDisconnected, err, \"expected error %v, got %v\", ErrClientDisconnected, err)\n\n\t\t_, err = coll.DeleteOne(bgCtx, doc)\n\t\tassert.Equal(t, ErrClientDisconnected, err, \"expected error %v, got %v\", ErrClientDisconnected, err)\n\n\t\t_, err = coll.DeleteMany(bgCtx, doc)\n\t\tassert.Equal(t, ErrClientDisconnected, err, \"expected error %v, got %v\", ErrClientDisconnected, err)\n\n\t\t_, err = coll.UpdateOne(bgCtx, doc, update)\n\t\tassert.Equal(t, ErrClientDisconnected, err, \"expected error %v, got %v\", ErrClientDisconnected, err)\n\n\t\t_, err = coll.UpdateMany(bgCtx, doc, update)\n\t\tassert.Equal(t, ErrClientDisconnected, err, \"expected error %v, got %v\", ErrClientDisconnected, err)\n\n\t\t_, err = coll.ReplaceOne(bgCtx, doc, doc)\n\t\tassert.Equal(t, ErrClientDisconnected, err, \"expected error %v, got %v\", ErrClientDisconnected, err)\n\n\t\t_, err = coll.Aggregate(bgCtx, Pipeline{})\n\t\tassert.Equal(t, ErrClientDisconnected, err, \"expected error %v, got %v\", ErrClientDisconnected, err)\n\n\t\t_, err = coll.EstimatedDocumentCount(bgCtx)\n\t\tassert.Equal(t, ErrClientDisconnected, err, \"expected error %v, got %v\", ErrClientDisconnected, err)\n\n\t\t_, err = coll.CountDocuments(bgCtx, doc)\n\t\tassert.Equal(t, ErrClientDisconnected, err, \"expected error %v, got %v\", ErrClientDisconnected, err)\n\n\t\terr = coll.Distinct(bgCtx, \"x\", doc).Err()\n\t\tassert.Equal(t, ErrClientDisconnected, err, \"expected error %v, got %v\", ErrClientDisconnected, err)\n\n\t\t_, err = coll.Find(bgCtx, doc)\n\t\tassert.Equal(t, ErrClientDisconnected, err, \"expected error %v, got %v\", ErrClientDisconnected, err)\n\n\t\terr = coll.FindOne(bgCtx, doc).Err()\n\t\tassert.Equal(t, ErrClientDisconnected, err, \"expected error %v, got %v\", ErrClientDisconnected, err)\n\n\t\terr = coll.FindOneAndDelete(bgCtx, doc).Err()\n\t\tassert.Equal(t, ErrClientDisconnected, err, \"expected error %v, got %v\", ErrClientDisconnected, err)\n\n\t\terr = coll.FindOneAndReplace(bgCtx, doc, doc).Err()\n\t\tassert.Equal(t, ErrClientDisconnected, err, \"expected error %v, got %v\", ErrClientDisconnected, err)\n\n\t\terr = coll.FindOneAndUpdate(bgCtx, doc, update).Err()\n\t\tassert.Equal(t, ErrClientDisconnected, err, \"expected error %v, got %v\", ErrClientDisconnected, err)\n\t})\n\tt.Run(\"database accessor\", func(t *testing.T) {\n\t\tcoll := setupColl(\"bar\")\n\t\tdbName := coll.Database().Name()\n\t\tassert.Equal(t, testDBName, dbName, \"expected db name %v, got %v\", testDBName, dbName)\n\t})\n\tt.Run(\"nil document error\", func(t *testing.T) {\n\t\tcoll := setupColl(\"foo\")\n\t\tdoc := bson.D{}\n\n\t\t_, err := coll.InsertOne(bgCtx, nil)\n\t\tassert.True(t, errors.Is(err, ErrNilDocument), \"expected error %v, got %v\", ErrNilDocument, err)\n\n\t\t_, err = coll.InsertMany(bgCtx, nil)\n\t\tassert.True(t, errors.Is(err, ErrNotSlice), \"expected error %v, got %v\", ErrNotSlice, err)\n\n\t\t_, err = coll.InsertMany(bgCtx, []any{})\n\t\tassert.True(t, errors.Is(err, ErrEmptySlice), \"expected error %v, got %v\", ErrEmptySlice, err)\n\n\t\t_, err = coll.InsertMany(bgCtx, \"x\")\n\t\tassert.True(t, errors.Is(err, ErrNotSlice), \"expected error %v, got %v\", ErrNotSlice, err)\n\n\t\t_, err = coll.DeleteOne(bgCtx, nil)\n\t\tassert.True(t, errors.Is(err, ErrNilDocument), \"expected error %v, got %v\", ErrNilDocument, err)\n\n\t\t_, err = coll.DeleteMany(bgCtx, nil)\n\t\tassert.True(t, errors.Is(err, ErrNilDocument), \"expected error %v, got %v\", ErrNilDocument, err)\n\n\t\t_, err = coll.UpdateOne(bgCtx, nil, doc)\n\t\tassert.True(t, errors.Is(err, ErrNilDocument), \"expected error %v, got %v\", ErrNilDocument, err)\n\n\t\t_, err = coll.UpdateOne(bgCtx, doc, nil)\n\t\tassert.True(t, errors.Is(err, ErrNilDocument), \"expected error %v, got %v\", ErrNilDocument, err)\n\n\t\t_, err = coll.UpdateMany(bgCtx, nil, doc)\n\t\tassert.True(t, errors.Is(err, ErrNilDocument), \"expected error %v, got %v\", ErrNilDocument, err)\n\n\t\t_, err = coll.UpdateMany(bgCtx, doc, nil)\n\t\tassert.True(t, errors.Is(err, ErrNilDocument), \"expected error %v, got %v\", ErrNilDocument, err)\n\n\t\t_, err = coll.ReplaceOne(bgCtx, nil, doc)\n\t\tassert.True(t, errors.Is(err, ErrNilDocument), \"expected error %v, got %v\", ErrNilDocument, err)\n\n\t\t_, err = coll.ReplaceOne(bgCtx, doc, nil)\n\t\tassert.True(t, errors.Is(err, ErrNilDocument), \"expected error %v, got %v\", ErrNilDocument, err)\n\n\t\t_, err = coll.CountDocuments(bgCtx, nil)\n\t\tassert.True(t, errors.Is(err, ErrNilDocument), \"expected error %v, got %v\", ErrNilDocument, err)\n\n\t\terr = coll.Distinct(bgCtx, \"x\", nil).Err()\n\t\tassert.True(t, errors.Is(err, ErrNilDocument), \"expected error %v, got %v\", ErrNilDocument, err)\n\n\t\t_, err = coll.Find(bgCtx, nil)\n\t\tassert.True(t, errors.Is(err, ErrNilDocument), \"expected error %v, got %v\", ErrNilDocument, err)\n\n\t\terr = coll.FindOne(bgCtx, nil).Err()\n\t\tassert.True(t, errors.Is(err, ErrNilDocument), \"expected error %v, got %v\", ErrNilDocument, err)\n\n\t\terr = coll.FindOneAndDelete(bgCtx, nil).Err()\n\t\tassert.True(t, errors.Is(err, ErrNilDocument), \"expected error %v, got %v\", ErrNilDocument, err)\n\n\t\terr = coll.FindOneAndReplace(bgCtx, nil, doc).Err()\n\t\tassert.True(t, errors.Is(err, ErrNilDocument), \"expected error %v, got %v\", ErrNilDocument, err)\n\n\t\terr = coll.FindOneAndReplace(bgCtx, doc, nil).Err()\n\t\tassert.True(t, errors.Is(err, ErrNilDocument), \"expected error %v, got %v\", ErrNilDocument, err)\n\n\t\terr = coll.FindOneAndUpdate(bgCtx, nil, doc).Err()\n\t\tassert.True(t, errors.Is(err, ErrNilDocument), \"expected error %v, got %v\", ErrNilDocument, err)\n\n\t\terr = coll.FindOneAndUpdate(bgCtx, doc, nil).Err()\n\t\tassert.True(t, errors.Is(err, ErrNilDocument), \"expected error %v, got %v\", ErrNilDocument, err)\n\n\t\t_, err = coll.BulkWrite(bgCtx, nil)\n\t\tassert.True(t, errors.Is(err, ErrEmptySlice), \"expected error %v, got %v\", ErrEmptySlice, err)\n\n\t\t_, err = coll.BulkWrite(bgCtx, []WriteModel{})\n\t\tassert.True(t, errors.Is(err, ErrEmptySlice), \"expected error %v, got %v\", ErrEmptySlice, err)\n\n\t\t_, err = coll.BulkWrite(bgCtx, []WriteModel{nil})\n\t\tassert.True(t, errors.Is(err, ErrNilDocument), \"expected error %v, got %v\", ErrNilDocument, err)\n\n\t\taggErr := errors.New(\"can only marshal slices and arrays into aggregation pipelines, but got invalid\")\n\t\t_, err = coll.Aggregate(bgCtx, nil)\n\t\tassert.Equal(t, aggErr, err, \"expected error %v, got %v\", aggErr, err)\n\n\t\t_, err = coll.Watch(bgCtx, nil)\n\t\tassert.Equal(t, aggErr, err, \"expected error %v, got %v\", aggErr, err)\n\t})\n}\n\nfunc TestCollation(t *testing.T) {\n\tt.Run(\"TestCollationToDocument\", func(t *testing.T) {\n\t\tc := &options.Collation{\n\t\t\tLocale:          \"locale\",\n\t\t\tCaseLevel:       true,\n\t\t\tCaseFirst:       \"first\",\n\t\t\tStrength:        1,\n\t\t\tNumericOrdering: true,\n\t\t\tAlternate:       \"alternate\",\n\t\t\tMaxVariable:     \"maxVariable\",\n\t\t\tNormalization:   true,\n\t\t\tBackwards:       true,\n\t\t}\n\n\t\tdoc := toDocument(c)\n\t\texpected := bsoncore.BuildDocumentFromElements(nil,\n\t\t\tbsoncore.AppendStringElement(nil, \"locale\", \"locale\"),\n\t\t\tbsoncore.AppendBooleanElement(nil, \"caseLevel\", (true)),\n\t\t\tbsoncore.AppendStringElement(nil, \"caseFirst\", (\"first\")),\n\t\t\tbsoncore.AppendInt32Element(nil, \"strength\", (1)),\n\t\t\tbsoncore.AppendBooleanElement(nil, \"numericOrdering\", (true)),\n\t\t\tbsoncore.AppendStringElement(nil, \"alternate\", (\"alternate\")),\n\t\t\tbsoncore.AppendStringElement(nil, \"maxVariable\", (\"maxVariable\")),\n\t\t\tbsoncore.AppendBooleanElement(nil, \"normalization\", (true)),\n\t\t\tbsoncore.AppendBooleanElement(nil, \"backwards\", (true)),\n\t\t)\n\n\t\tif !bytes.Equal(doc, expected) {\n\t\t\tt.Fatalf(\"collation did not match expected. got %v; wanted %v\", doc, expected)\n\t\t}\n\t})\n}\n\nfunc TestNewFindArgsFromFindOneArgs(t *testing.T) {\n\tt.Parallel()\n\n\ttests := []struct {\n\t\tname string\n\t\targs *options.FindOneOptions\n\t\twant *options.FindOptions\n\t}{\n\t\t{\n\t\t\tname: \"nil\",\n\t\t\targs: nil,\n\t\t\twant: &options.FindOptions{\n\t\t\t\tLimit: ptrutil.Ptr(int64(-1)),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"empty\",\n\t\t\targs: &options.FindOneOptions{},\n\t\t\twant: &options.FindOptions{\n\t\t\t\tLimit: ptrutil.Ptr(int64(-1)),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"non empty\",\n\t\t\targs: &options.FindOneOptions{\n\t\t\t\tSkip: ptrutil.Ptr(int64(1)),\n\t\t\t},\n\t\t\twant: &options.FindOptions{\n\t\t\t\tSkip:  ptrutil.Ptr(int64(1)),\n\t\t\t\tLimit: ptrutil.Ptr(int64(-1)),\n\t\t\t},\n\t\t},\n\t}\n\n\tfor _, test := range tests {\n\t\ttest := test // Capture the range variable\n\n\t\tt.Run(test.name, func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tassert.Equal(t, test.want, newFindArgsFromFindOneArgs(test.args))\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "mongo/crud_examples_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage mongo_test\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"log\"\n\t\"sync\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/readconcern\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/readpref\"\n)\n\n// Client examples\n\nfunc ExampleClient_ListDatabaseNames() {\n\tvar client *mongo.Client\n\n\t// Use a filter to only select non-empty databases.\n\tresult, err := client.ListDatabaseNames(\n\t\tcontext.TODO(),\n\t\tbson.D{{\"empty\", false}})\n\tif err != nil {\n\t\tlog.Panic(err)\n\t}\n\n\tfor _, db := range result {\n\t\tfmt.Println(db)\n\t}\n}\n\nfunc ExampleClient_Watch() {\n\tvar client *mongo.Client\n\n\t// Specify a pipeline that will only match \"insert\" events.\n\t// Specify the MaxAwaitTimeOption to have each attempt wait two seconds for\n\t// new documents.\n\tmatchStage := bson.D{{\"$match\", bson.D{{\"operationType\", \"insert\"}}}}\n\topts := options.ChangeStream().SetMaxAwaitTime(2 * time.Second)\n\tchangeStream, err := client.Watch(\n\t\tcontext.TODO(),\n\t\tmongo.Pipeline{matchStage},\n\t\topts)\n\tif err != nil {\n\t\tlog.Panic(err)\n\t}\n\n\t// Print out all change stream events in the order they're received.\n\t// See the mongo.ChangeStream documentation for more examples of using\n\t// change streams.\n\tfor changeStream.Next(context.TODO()) {\n\t\tfmt.Println(changeStream.Current)\n\t}\n}\n\n// Database examples\n\nfunc ExampleDatabase_CreateCollection() {\n\tvar db *mongo.Database\n\n\t// Create a \"users\" collection with a JSON schema validator. The validator\n\t// will ensure that each document in the collection has \"name\" and \"age\"\n\t// fields.\n\tjsonSchema := bson.M{\n\t\t\"bsonType\": \"object\",\n\t\t\"required\": []string{\"name\", \"age\"},\n\t\t\"properties\": bson.M{\n\t\t\t\"name\": bson.M{\n\t\t\t\t\"bsonType\": \"string\",\n\t\t\t\t\"description\": \"the name of the user, which is required and \" +\n\t\t\t\t\t\"must be a string\",\n\t\t\t},\n\t\t\t\"age\": bson.M{\n\t\t\t\t\"bsonType\": \"int\",\n\t\t\t\t\"minimum\":  18,\n\t\t\t\t\"description\": \"the age of the user, which is required and \" +\n\t\t\t\t\t\"must be an integer >= 18\",\n\t\t\t},\n\t\t},\n\t}\n\tvalidator := bson.M{\n\t\t\"$jsonSchema\": jsonSchema,\n\t}\n\topts := options.CreateCollection().SetValidator(validator)\n\n\terr := db.CreateCollection(context.TODO(), \"users\", opts)\n\tif err != nil {\n\t\tlog.Panic(err)\n\t}\n}\n\nfunc ExampleDatabase_CreateView() {\n\tvar db *mongo.Database\n\n\t// Create a view on the \"users\" collection called \"usernames\". Specify a\n\t// pipeline that concatenates the \"firstName\" and \"lastName\" fields from\n\t// each document in \"users\" and projects the result into the \"fullName\"\n\t// field in the view.\n\tprojectStage := bson.D{\n\t\t{\"$project\", bson.D{\n\t\t\t{\"_id\", 0},\n\t\t\t{\"fullName\", bson.D{\n\t\t\t\t{\"$concat\", []string{\"$firstName\", \" \", \"$lastName\"}},\n\t\t\t}},\n\t\t}},\n\t}\n\tpipeline := mongo.Pipeline{projectStage}\n\n\t// Specify the Collation option to set a default collation for the view.\n\topts := options.CreateView().SetCollation(&options.Collation{\n\t\tLocale: \"en_US\",\n\t})\n\n\terr := db.CreateView(context.TODO(), \"usernames\", \"users\", pipeline, opts)\n\tif err != nil {\n\t\tlog.Panic(err)\n\t}\n}\n\nfunc ExampleDatabase_ListCollectionNames() {\n\tvar db *mongo.Database\n\n\t// Use a filter to only select capped collections.\n\tresult, err := db.ListCollectionNames(\n\t\tcontext.TODO(),\n\t\tbson.D{{\"options.capped\", true}})\n\tif err != nil {\n\t\tlog.Panic(err)\n\t}\n\n\tfor _, coll := range result {\n\t\tfmt.Println(coll)\n\t}\n}\n\nfunc ExampleDatabase_RunCommand() {\n\tvar db *mongo.Database\n\n\t// Run an explain command to see the query plan for when a \"find\" is\n\t// executed on collection \"bar\" specify the ReadPreference option to\n\t// explicitly set the read preference to primary.\n\tfindCmd := bson.D{{\"find\", \"bar\"}}\n\tcommand := bson.D{{\"explain\", findCmd}}\n\topts := options.RunCmd().SetReadPreference(readpref.Primary())\n\tvar result bson.M\n\terr := db.RunCommand(context.TODO(), command, opts).Decode(&result)\n\tif err != nil {\n\t\tlog.Panic(err)\n\t}\n\tfmt.Println(result)\n}\n\nfunc ExampleDatabase_Watch() {\n\tvar db *mongo.Database\n\n\t// Specify a pipeline that will only match \"insert\" events.\n\t// Specify the MaxAwaitTimeOption to have each attempt wait two seconds for\n\t// new documents.\n\tmatchStage := bson.D{{\"$match\", bson.D{{\"operationType\", \"insert\"}}}}\n\topts := options.ChangeStream().SetMaxAwaitTime(2 * time.Second)\n\tchangeStream, err := db.Watch(\n\t\tcontext.TODO(),\n\t\tmongo.Pipeline{matchStage},\n\t\topts)\n\tif err != nil {\n\t\tlog.Panic(err)\n\t}\n\n\t// Print out all change stream events in the order they're received.\n\t// See the mongo.ChangeStream documentation for more examples of using\n\t// change streams\n\tfor changeStream.Next(context.TODO()) {\n\t\tfmt.Println(changeStream.Current)\n\t}\n}\n\n// Collection examples\n\nfunc ExampleCollection_Aggregate() {\n\tvar coll *mongo.Collection\n\n\t// Specify a pipeline that will return the number of times each name appears\n\t// in the collection.\n\t// Specify the MaxTime option to limit the amount of time the operation can\n\t// run on the server.\n\tgroupStage := bson.D{\n\t\t{\"$group\", bson.D{\n\t\t\t{\"_id\", \"$name\"},\n\t\t\t{\"numTimes\", bson.D{\n\t\t\t\t{\"$sum\", 1},\n\t\t\t}},\n\t\t}},\n\t}\n\topts := options.Aggregate()\n\tcursor, err := coll.Aggregate(\n\t\tcontext.TODO(),\n\t\tmongo.Pipeline{groupStage},\n\t\topts)\n\tif err != nil {\n\t\tlog.Panic(err)\n\t}\n\n\t// Get a list of all returned documents and print them out.\n\t// See the mongo.Cursor documentation for more examples of using cursors.\n\tvar results []bson.M\n\tif err = cursor.All(context.TODO(), &results); err != nil {\n\t\tlog.Panic(err)\n\t}\n\tfor _, result := range results {\n\t\tfmt.Printf(\n\t\t\t\"name %v appears %v times\\n\",\n\t\t\tresult[\"_id\"],\n\t\t\tresult[\"numTimes\"])\n\t}\n}\n\nfunc ExampleCollection_BulkWrite() {\n\tvar coll *mongo.Collection\n\tvar firstID, secondID bson.ObjectID\n\n\t// Update the \"email\" field for two users.\n\t// For each update, specify the Upsert option to insert a new document if a\n\t// document matching the filter isn't found.\n\t// Set the Ordered option to false to allow both operations to happen even\n\t// if one of them errors.\n\tfirstUpdate := bson.D{\n\t\t{\"$set\", bson.D{\n\t\t\t{\"email\", \"firstEmail@example.com\"},\n\t\t}},\n\t}\n\tsecondUpdate := bson.D{\n\t\t{\"$set\", bson.D{\n\t\t\t{\"email\", \"secondEmail@example.com\"},\n\t\t}},\n\t}\n\tmodels := []mongo.WriteModel{\n\t\tmongo.NewUpdateOneModel().SetFilter(bson.D{{\"_id\", firstID}}).\n\t\t\tSetUpdate(firstUpdate).SetUpsert(true),\n\t\tmongo.NewUpdateOneModel().SetFilter(bson.D{{\"_id\", secondID}}).\n\t\t\tSetUpdate(secondUpdate).SetUpsert(true),\n\t}\n\topts := options.BulkWrite().SetOrdered(false)\n\tres, err := coll.BulkWrite(context.TODO(), models, opts)\n\tif err != nil {\n\t\tlog.Panic(err)\n\t}\n\n\tfmt.Printf(\n\t\t\"inserted %v and deleted %v documents\\n\",\n\t\tres.InsertedCount,\n\t\tres.DeletedCount)\n}\n\nfunc ExampleCollection_CountDocuments() {\n\tvar coll *mongo.Collection\n\n\t// Specify a timeout to limit the amount of time the operation can run on\n\t// the server.\n\tctx, cancel := context.WithTimeout(context.TODO(), time.Second)\n\tdefer cancel()\n\n\t// Count the number of times the name \"Bob\" appears in the collection.\n\tcount, err := coll.CountDocuments(ctx, bson.D{{\"name\", \"Bob\"}}, nil)\n\tif err != nil {\n\t\tlog.Panic(err)\n\t}\n\tfmt.Printf(\"name Bob appears in %v documents\", count)\n}\n\nfunc ExampleCollection_DeleteMany() {\n\tvar coll *mongo.Collection\n\n\t// Delete all documents in which the \"name\" field is \"Bob\" or \"bob\".\n\t// Specify the Collation option to provide a collation that will ignore case\n\t// for string comparisons.\n\topts := options.DeleteMany().SetCollation(&options.Collation{\n\t\tLocale:    \"en_US\",\n\t\tStrength:  1,\n\t\tCaseLevel: false,\n\t})\n\tres, err := coll.DeleteMany(context.TODO(), bson.D{{\"name\", \"bob\"}}, opts)\n\tif err != nil {\n\t\tlog.Panic(err)\n\t}\n\tfmt.Printf(\"deleted %v documents\\n\", res.DeletedCount)\n}\n\nfunc ExampleCollection_DeleteOne() {\n\tvar coll *mongo.Collection\n\n\t// Delete at most one document in which the \"name\" field is \"Bob\" or \"bob\".\n\t// Specify the SetCollation option to provide a collation that will ignore\n\t// case for string comparisons.\n\topts := options.DeleteOne().SetCollation(&options.Collation{\n\t\tLocale:    \"en_US\",\n\t\tStrength:  1,\n\t\tCaseLevel: false,\n\t})\n\tres, err := coll.DeleteOne(context.TODO(), bson.D{{\"name\", \"bob\"}}, opts)\n\tif err != nil {\n\t\tlog.Panic(err)\n\t}\n\tfmt.Printf(\"deleted %v documents\\n\", res.DeletedCount)\n}\n\nfunc ExampleCollection_Distinct() {\n\tvar coll *mongo.Collection\n\n\t// Specify a timeout to limit the amount of time the operation can run on\n\t// the server.\n\tctx, cancel := context.WithTimeout(context.TODO(), time.Second)\n\tdefer cancel()\n\n\t// Find all unique values for the \"name\" field for documents in which the\n\t// \"age\" field is greater than 25.\n\tfilter := bson.D{{\"age\", bson.D{{\"$gt\", 25}}}}\n\tres := coll.Distinct(ctx, \"name\", filter)\n\tif err := res.Err(); err != nil {\n\t\tlog.Panic(err)\n\t}\n\n\tvalues, err := res.Raw()\n\tif err != nil {\n\t\tlog.Panic(err)\n\t}\n\n\tfor _, value := range values {\n\t\tfmt.Println(value)\n\t}\n}\n\nfunc ExampleCollection_EstimatedDocumentCount() {\n\tvar coll *mongo.Collection\n\n\t// Specify a timeout to limit the amount of time the operation can run on\n\t// the server.\n\tctx, cancel := context.WithTimeout(context.TODO(), time.Second)\n\tdefer cancel()\n\n\t// Get and print an estimated of the number of documents in the collection.\n\tcount, err := coll.EstimatedDocumentCount(ctx, nil)\n\tif err != nil {\n\t\tlog.Panic(err)\n\t}\n\tfmt.Printf(\"estimated document count: %v\", count)\n}\n\nfunc ExampleCollection_Find() {\n\tvar coll *mongo.Collection\n\n\t// Find all documents in which the \"name\" field is \"Bob\".\n\t// Specify the Sort option to sort the returned documents by age in\n\t// ascending order.\n\topts := options.Find().SetSort(bson.D{{\"age\", 1}})\n\tcursor, err := coll.Find(context.TODO(), bson.D{{\"name\", \"Bob\"}}, opts)\n\tif err != nil {\n\t\tlog.Panic(err)\n\t}\n\n\t// Get a list of all returned documents and print them out.\n\t// See the mongo.Cursor documentation for more examples of using cursors.\n\tvar results []bson.M\n\tif err = cursor.All(context.TODO(), &results); err != nil {\n\t\tlog.Panic(err)\n\t}\n\tfor _, result := range results {\n\t\tfmt.Println(result)\n\t}\n}\n\nfunc ExampleCollection_FindOne() {\n\tvar coll *mongo.Collection\n\tvar id bson.ObjectID\n\n\t// Find the document for which the _id field matches id.\n\t// Specify the Sort option to sort the documents by age.\n\t// The first document in the sorted order will be returned.\n\topts := options.FindOne().SetSort(bson.D{{\"age\", 1}})\n\tvar result bson.M\n\terr := coll.FindOne(\n\t\tcontext.TODO(),\n\t\tbson.D{{\"_id\", id}},\n\t\topts,\n\t).Decode(&result)\n\tif err != nil {\n\t\t// ErrNoDocuments means that the filter did not match any documents in\n\t\t// the collection.\n\t\tif errors.Is(err, mongo.ErrNoDocuments) {\n\t\t\treturn\n\t\t}\n\t\tlog.Panic(err)\n\t}\n\tfmt.Printf(\"found document %v\", result)\n}\n\nfunc ExampleCollection_FindOneAndDelete() {\n\tvar coll *mongo.Collection\n\tvar id bson.ObjectID\n\n\t// Find and delete the document for which the _id field matches id.\n\t// Specify the Projection option to only include the name and age fields in\n\t// the returned document.\n\topts := options.FindOneAndDelete().\n\t\tSetProjection(bson.D{{\"name\", 1}, {\"age\", 1}})\n\tvar deletedDocument bson.M\n\terr := coll.FindOneAndDelete(\n\t\tcontext.TODO(),\n\t\tbson.D{{\"_id\", id}},\n\t\topts,\n\t).Decode(&deletedDocument)\n\tif err != nil {\n\t\t// ErrNoDocuments means that the filter did not match any documents in\n\t\t// the collection.\n\t\tif errors.Is(err, mongo.ErrNoDocuments) {\n\t\t\treturn\n\t\t}\n\t\tlog.Panic(err)\n\t}\n\tfmt.Printf(\"deleted document %v\", deletedDocument)\n}\n\nfunc ExampleCollection_FindOneAndReplace() {\n\tvar coll *mongo.Collection\n\tvar id bson.ObjectID\n\n\t// Find the document for which the _id field matches id and add a field\n\t// called \"location\".\n\t// Specify the Upsert option to insert a new document if a document matching\n\t// the filter isn't found.\n\topts := options.FindOneAndReplace().SetUpsert(true)\n\tfilter := bson.D{{\"_id\", id}}\n\treplacement := bson.D{{\"location\", \"NYC\"}}\n\tvar replacedDocument bson.M\n\terr := coll.FindOneAndReplace(\n\t\tcontext.TODO(),\n\t\tfilter,\n\t\treplacement,\n\t\topts,\n\t).Decode(&replacedDocument)\n\tif err != nil {\n\t\t// ErrNoDocuments means that the filter did not match any documents in\n\t\t// the collection.\n\t\tif errors.Is(err, mongo.ErrNoDocuments) {\n\t\t\treturn\n\t\t}\n\t\tlog.Panic(err)\n\t}\n\tfmt.Printf(\"replaced document %v\", replacedDocument)\n}\n\nfunc ExampleCollection_FindOneAndUpdate() {\n\tvar coll *mongo.Collection\n\tvar id bson.ObjectID\n\n\t// Find the document for which the _id field matches id and set the email to\n\t// \"newemail@example.com\".\n\t// Specify the Upsert option to insert a new document if a document matching\n\t// the filter isn't found.\n\topts := options.FindOneAndUpdate().SetUpsert(true)\n\tfilter := bson.D{{\"_id\", id}}\n\tupdate := bson.D{{\"$set\", bson.D{{\"email\", \"newemail@example.com\"}}}}\n\tvar updatedDocument bson.M\n\terr := coll.FindOneAndUpdate(\n\t\tcontext.TODO(),\n\t\tfilter,\n\t\tupdate,\n\t\topts,\n\t).Decode(&updatedDocument)\n\tif err != nil {\n\t\t// ErrNoDocuments means that the filter did not match any documents in\n\t\t// the collection.\n\t\tif errors.Is(err, mongo.ErrNoDocuments) {\n\t\t\treturn\n\t\t}\n\t\tlog.Panic(err)\n\t}\n\tfmt.Printf(\"updated document %v\", updatedDocument)\n}\n\nfunc ExampleCollection_InsertMany() {\n\tvar coll *mongo.Collection\n\n\t// Insert documents {name: \"Alice\"} and {name: \"Bob\"}.\n\t// Set the Ordered option to false to allow both operations to happen even\n\t// if one of them errors.\n\tdocs := []any{\n\t\tbson.D{{\"name\", \"Alice\"}},\n\t\tbson.D{{\"name\", \"Bob\"}},\n\t}\n\topts := options.InsertMany().SetOrdered(false)\n\tres, err := coll.InsertMany(context.TODO(), docs, opts)\n\tif err != nil {\n\t\tlog.Panic(err)\n\t}\n\tfmt.Printf(\"inserted documents with IDs %v\\n\", res.InsertedIDs)\n}\n\nfunc ExampleCollection_InsertOne() {\n\tvar coll *mongo.Collection\n\n\t// Insert the document {name: \"Alice\"}.\n\tres, err := coll.InsertOne(context.TODO(), bson.D{{\"name\", \"Alice\"}})\n\tif err != nil {\n\t\tlog.Panic(err)\n\t}\n\tfmt.Printf(\"inserted document with ID %v\\n\", res.InsertedID)\n}\n\nfunc ExampleCollection_ReplaceOne() {\n\tvar coll *mongo.Collection\n\tvar id bson.ObjectID\n\n\t// Find the document for which the _id field matches id and add a field\n\t// called \"location\".\n\t// Specify the Upsert option to insert a new document if a document matching\n\t// the filter isn't found.\n\topts := options.Replace().SetUpsert(true)\n\tfilter := bson.D{{\"_id\", id}}\n\treplacement := bson.D{{\"location\", \"NYC\"}}\n\tresult, err := coll.ReplaceOne(context.TODO(), filter, replacement, opts)\n\tif err != nil {\n\t\tlog.Panic(err)\n\t}\n\n\tif result.MatchedCount != 0 {\n\t\tfmt.Println(\"matched and replaced an existing document\")\n\t\treturn\n\t}\n\tif result.UpsertedCount != 0 {\n\t\tfmt.Printf(\"inserted a new document with ID %v\\n\", result.UpsertedID)\n\t}\n}\n\nfunc ExampleCollection_UpdateMany() {\n\tvar coll *mongo.Collection\n\n\t// Increment the age for all users whose birthday is today.\n\ttoday := time.Now().Format(\"01-01-1970\")\n\tfilter := bson.D{{\"birthday\", today}}\n\tupdate := bson.D{{\"$inc\", bson.D{{\"age\", 1}}}}\n\n\tresult, err := coll.UpdateMany(context.TODO(), filter, update)\n\tif err != nil {\n\t\tlog.Panic(err)\n\t}\n\n\tif result.MatchedCount != 0 {\n\t\tfmt.Println(\"matched and replaced an existing document\")\n\t\treturn\n\t}\n}\n\nfunc ExampleCollection_UpdateOne() {\n\tvar coll *mongo.Collection\n\tvar id bson.ObjectID\n\n\t// Find the document for which the _id field matches id and set the email to\n\t// \"newemail@example.com\".\n\t// Specify the Upsert option to insert a new document if a document matching\n\t// the filter isn't found.\n\topts := options.UpdateOne().SetUpsert(true)\n\tfilter := bson.D{{\"_id\", id}}\n\tupdate := bson.D{{\"$set\", bson.D{{\"email\", \"newemail@example.com\"}}}}\n\n\tresult, err := coll.UpdateOne(context.TODO(), filter, update, opts)\n\tif err != nil {\n\t\tlog.Panic(err)\n\t}\n\n\tif result.MatchedCount != 0 {\n\t\tfmt.Println(\"matched and replaced an existing document\")\n\t\treturn\n\t}\n\tif result.UpsertedCount != 0 {\n\t\tfmt.Printf(\"inserted a new document with ID %v\\n\", result.UpsertedID)\n\t}\n}\n\nfunc ExampleCollection_Watch() {\n\tvar collection *mongo.Collection\n\n\t// Specify a pipeline that will only match \"insert\" events.\n\t// Specify the MaxAwaitTimeOption to have each attempt wait two seconds for\n\t// new documents.\n\tmatchStage := bson.D{{\"$match\", bson.D{{\"operationType\", \"insert\"}}}}\n\topts := options.ChangeStream().SetMaxAwaitTime(2 * time.Second)\n\tchangeStream, err := collection.Watch(\n\t\tcontext.TODO(),\n\t\tmongo.Pipeline{matchStage},\n\t\topts)\n\tif err != nil {\n\t\tlog.Panic(err)\n\t}\n\n\t// Print out all change stream events in the order they're received.\n\t// See the mongo.ChangeStream documentation for more examples of using\n\t// change streams.\n\tfor changeStream.Next(context.TODO()) {\n\t\tfmt.Println(changeStream.Current)\n\t}\n}\n\n// Session examples\n\nfunc ExampleWithSession() {\n\t// Assume client is configured with write concern majority and read\n\t// preference primary.\n\tvar client *mongo.Client\n\n\t// Specify the DefaultReadConcern option so any transactions started through\n\t// the session will have read concern majority.\n\t// The DefaultReadPreference and DefaultWriteConcern options aren't\n\t// specified so they will be inheritied from client and be set to primary\n\t// and majority, respectively.\n\ttxnOpts := options.Transaction().SetReadConcern(readconcern.Majority())\n\topts := options.Session().SetDefaultTransactionOptions(txnOpts)\n\tsess, err := client.StartSession(opts)\n\tif err != nil {\n\t\tlog.Panic(err)\n\t}\n\tdefer sess.EndSession(context.TODO())\n\n\t// Call WithSession to start a transaction within the new session.\n\terr = mongo.WithSession(\n\t\tcontext.TODO(),\n\t\tsess,\n\t\tfunc(ctx context.Context) error {\n\t\t\t// Use the context.Context as the Context parameter for\n\t\t\t// InsertOne and FindOne so both operations are run under the new\n\t\t\t// Session.\n\n\t\t\tif err := sess.StartTransaction(); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\n\t\t\tcoll := client.Database(\"db\").Collection(\"coll\")\n\t\t\tres, err := coll.InsertOne(ctx, bson.D{{\"x\", 1}})\n\t\t\tif err != nil {\n\t\t\t\t// Abort the transaction after an error. Use\n\t\t\t\t// context.Background() to ensure that the abort can complete\n\t\t\t\t// successfully even if the context passed to mongo.WithSession\n\t\t\t\t// is changed to have a timeout.\n\t\t\t\t_ = sess.AbortTransaction(context.Background())\n\t\t\t\treturn err\n\t\t\t}\n\n\t\t\tvar result bson.M\n\t\t\terr = coll.FindOne(\n\t\t\t\tctx,\n\t\t\t\tbson.D{{\"_id\", res.InsertedID}},\n\t\t\t).Decode(result)\n\t\t\tif err != nil {\n\t\t\t\t// Abort the transaction after an error. Use\n\t\t\t\t// context.Background() to ensure that the abort can complete\n\t\t\t\t// successfully even if the context passed to mongo.WithSession\n\t\t\t\t// is changed to have a timeout.\n\t\t\t\t_ = sess.AbortTransaction(context.Background())\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tfmt.Println(result)\n\n\t\t\t// Use context.Background() to ensure that the commit can complete\n\t\t\t// successfully even if the context passed to mongo.WithSession is\n\t\t\t// changed to have a timeout.\n\t\t\treturn sess.CommitTransaction(context.Background())\n\t\t})\n\tif err != nil {\n\t\tlog.Panic(err)\n\t}\n}\n\nfunc ExampleClient_UseSessionWithOptions() {\n\tvar client *mongo.Client\n\n\t// Specify the DefaultReadConcern option so any transactions started through\n\t// the session will have read concern majority.\n\t// The DefaultReadPreference and DefaultWriteConcern options aren't\n\t// specified so they will be inheritied from client and be set to primary\n\t// and majority, respectively.\n\ttxnOpts := options.Transaction().SetReadConcern(readconcern.Majority())\n\topts := options.Session().SetDefaultTransactionOptions(txnOpts)\n\terr := client.UseSessionWithOptions(\n\t\tcontext.TODO(),\n\t\topts,\n\t\tfunc(ctx context.Context) error {\n\t\t\tsess := mongo.SessionFromContext(ctx)\n\t\t\t// Use the context.Context as the Context parameter for\n\t\t\t// InsertOne and FindOne so both operations are run under the new\n\t\t\t// Session.\n\n\t\t\tif err := sess.StartTransaction(); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\n\t\t\tcoll := client.Database(\"db\").Collection(\"coll\")\n\t\t\tres, err := coll.InsertOne(ctx, bson.D{{\"x\", 1}})\n\t\t\tif err != nil {\n\t\t\t\t// Abort the transaction after an error. Use\n\t\t\t\t// context.Background() to ensure that the abort can complete\n\t\t\t\t// successfully even if the context passed to mongo.WithSession\n\t\t\t\t// is changed to have a timeout.\n\t\t\t\t_ = sess.AbortTransaction(context.Background())\n\t\t\t\treturn err\n\t\t\t}\n\n\t\t\tvar result bson.M\n\t\t\terr = coll.FindOne(\n\t\t\t\tctx,\n\t\t\t\tbson.D{{\"_id\", res.InsertedID}},\n\t\t\t).Decode(result)\n\t\t\tif err != nil {\n\t\t\t\t// Abort the transaction after an error. Use\n\t\t\t\t// context.Background() to ensure that the abort can complete\n\t\t\t\t// successfully even if the context passed to mongo.WithSession\n\t\t\t\t// is changed to have a timeout.\n\t\t\t\t_ = sess.AbortTransaction(context.Background())\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tfmt.Println(result)\n\n\t\t\t// Use context.Background() to ensure that the commit can complete\n\t\t\t// successfully even if the context passed to mongo.WithSession is\n\t\t\t// changed to have a timeout.\n\t\t\treturn sess.CommitTransaction(context.Background())\n\t\t})\n\tif err != nil {\n\t\tlog.Panic(err)\n\t}\n}\n\nfunc ExampleClient_StartSession_withTransaction() {\n\t// Assume client is configured with write concern majority and read\n\t// preference primary.\n\tvar client *mongo.Client\n\n\t// Specify the DefaultReadConcern option so any transactions started through\n\t// the session will have read concern majority.\n\t// The DefaultReadPreference and DefaultWriteConcern options aren't\n\t// specified so they will be inheritied from client and be set to primary\n\t// and majority, respectively.\n\ttxnOpts := options.Transaction().SetReadConcern(readconcern.Majority())\n\topts := options.Session().SetDefaultTransactionOptions(txnOpts)\n\tsess, err := client.StartSession(opts)\n\tif err != nil {\n\t\tlog.Panic(err)\n\t}\n\tdefer sess.EndSession(context.TODO())\n\n\t// Specify the ReadPreference option to set the read preference to primary\n\t// preferred for this transaction.\n\ttxnOpts.SetReadPreference(readpref.PrimaryPreferred())\n\tresult, err := sess.WithTransaction(\n\t\tcontext.TODO(),\n\t\tfunc(ctx context.Context) (any, error) {\n\t\t\t// Use the context.Context as the Context parameter for\n\t\t\t// InsertOne and FindOne so both operations are run in the same\n\t\t\t// transaction.\n\n\t\t\tcoll := client.Database(\"db\").Collection(\"coll\")\n\t\t\tres, err := coll.InsertOne(ctx, bson.D{{\"x\", 1}})\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\n\t\t\tvar result bson.M\n\t\t\terr = coll.FindOne(\n\t\t\t\tctx,\n\t\t\t\tbson.D{{\"_id\", res.InsertedID}},\n\t\t\t).Decode(result)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\treturn result, err\n\t\t},\n\t\ttxnOpts)\n\tif err != nil {\n\t\tlog.Panic(err)\n\t}\n\tfmt.Printf(\"result: %v\\n\", result)\n}\n\nfunc ExampleNewSessionContext() {\n\tvar client *mongo.Client\n\n\t// Create a new Session and SessionContext.\n\tsess, err := client.StartSession()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tdefer sess.EndSession(context.TODO())\n\tctx := mongo.NewSessionContext(context.TODO(), sess)\n\n\t// Start a transaction and use the context.Context as the Context\n\t// parameter for InsertOne and FindOne so both operations are run in the\n\t// transaction.\n\tif err = sess.StartTransaction(); err != nil {\n\t\tpanic(err)\n\t}\n\n\tcoll := client.Database(\"db\").Collection(\"coll\")\n\tres, err := coll.InsertOne(ctx, bson.D{{\"x\", 1}})\n\tif err != nil {\n\t\t// Abort the transaction after an error. Use context.Background() to\n\t\t// ensure that the abort can complete successfully even if the context\n\t\t// passed to NewSessionContext is changed to have a timeout.\n\t\t_ = sess.AbortTransaction(context.Background())\n\t\tpanic(err)\n\t}\n\n\tvar result bson.M\n\terr = coll.FindOne(\n\t\tctx,\n\t\tbson.D{{\"_id\", res.InsertedID}},\n\t).Decode(&result)\n\tif err != nil {\n\t\t// Abort the transaction after an error. Use context.Background() to\n\t\t// ensure that the abort can complete successfully even if the context\n\t\t// passed to NewSessionContext is changed to have a timeout.\n\t\t_ = sess.AbortTransaction(context.Background())\n\t\tpanic(err)\n\t}\n\tfmt.Printf(\"result: %v\\n\", result)\n\n\t// Commit the transaction so the inserted document will be stored. Use\n\t// context.Background() to ensure that the commit can complete successfully\n\t// even if the context passed to NewSessionContext is changed to have a\n\t// timeout.\n\tif err = sess.CommitTransaction(context.Background()); err != nil {\n\t\tpanic(err)\n\t}\n}\n\n// Cursor examples\n\nfunc ExampleCursor_All() {\n\tvar cursor *mongo.Cursor\n\n\tvar results []bson.M\n\tif err := cursor.All(context.TODO(), &results); err != nil {\n\t\tlog.Panic(err)\n\t}\n\tfmt.Println(results)\n}\n\nfunc ExampleCursor_Next() {\n\tvar cursor *mongo.Cursor\n\tdefer cursor.Close(context.TODO())\n\n\t// Iterate the cursor and print out each document until the cursor is\n\t// exhausted or there is an error getting the next document.\n\tfor cursor.Next(context.TODO()) {\n\t\t// A new result variable should be declared for each document.\n\t\tvar result bson.M\n\t\tif err := cursor.Decode(&result); err != nil {\n\t\t\tlog.Panic(err)\n\t\t}\n\t\tfmt.Println(result)\n\t}\n\tif err := cursor.Err(); err != nil {\n\t\tlog.Panic(err)\n\t}\n}\n\nfunc ExampleCursor_TryNext() {\n\tvar cursor *mongo.Cursor\n\tdefer cursor.Close(context.TODO())\n\n\t// Iterate the cursor and print out each document until the cursor is\n\t// exhausted or there is an error getting the next document.\n\tfor {\n\t\tif cursor.TryNext(context.TODO()) {\n\t\t\t// A new result variable should be declared for each document.\n\t\t\tvar result bson.M\n\t\t\tif err := cursor.Decode(&result); err != nil {\n\t\t\t\tlog.Panic(err)\n\t\t\t}\n\t\t\tfmt.Println(result)\n\t\t\tcontinue\n\t\t}\n\n\t\t// If TryNext returns false, the next document is not yet available, the\n\t\t// cursor was exhausted and was closed, or an error occurred. TryNext\n\t\t// should only be called again for the empty batch case.\n\t\tif err := cursor.Err(); err != nil {\n\t\t\tlog.Panic(err)\n\t\t}\n\t\tif cursor.ID() == 0 {\n\t\t\tbreak\n\t\t}\n\t}\n}\n\nfunc ExampleCursor_RemainingBatchLength() {\n\t// Because we're using a tailable cursor, this must be a handle to a capped\n\t// collection.\n\tvar coll *mongo.Collection\n\n\t// Create a tailable await cursor. Specify the MaxAwaitTime option so\n\t// requests to get more data will return if there are no documents available\n\t// after two seconds.\n\tfindOpts := options.Find().\n\t\tSetCursorType(options.TailableAwait).\n\t\tSetMaxAwaitTime(2 * time.Second)\n\tcursor, err := coll.Find(context.TODO(), bson.D{}, findOpts)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tfor {\n\t\t// Iterate the cursor using TryNext.\n\t\tif cursor.TryNext(context.TODO()) {\n\t\t\tfmt.Println(cursor.Current)\n\t\t}\n\n\t\t// Handle cursor errors or the cursor being closed by the server.\n\t\tif err = cursor.Err(); err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t\tif cursor.ID() == 0 {\n\t\t\tpanic(\"cursor was unexpectedly closed by the server\")\n\t\t}\n\n\t\t// Use the RemainingBatchLength function to rate-limit the number of\n\t\t// network requests the driver does. If the current batch is empty,\n\t\t// sleep for a short amount of time to let documents build up on the\n\t\t// server before the next TryNext call, which will do a network request.\n\t\tif cursor.RemainingBatchLength() == 0 {\n\t\t\ttime.Sleep(100 * time.Millisecond)\n\t\t}\n\t}\n}\n\n// ChangeStream examples\n\nfunc ExampleChangeStream_Next() {\n\tvar stream *mongo.ChangeStream\n\tdefer stream.Close(context.TODO())\n\n\t// Iterate the change stream and print out each event.\n\t// Because the Next call blocks until an event is available, another way to\n\t// iterate the change stream is to call Next in a goroutine and pass in a\n\t// context that can be cancelled to abort the call.\n\n\tfor stream.Next(context.TODO()) {\n\t\t// A new event variable should be declared for each event.\n\t\tvar event bson.M\n\t\tif err := stream.Decode(&event); err != nil {\n\t\t\tlog.Panic(err)\n\t\t}\n\t\tfmt.Println(event)\n\t}\n\tif err := stream.Err(); err != nil {\n\t\tlog.Panic(err)\n\t}\n}\n\nfunc ExampleChangeStream_TryNext() {\n\tvar stream *mongo.ChangeStream\n\tdefer stream.Close(context.TODO())\n\n\t// Iterate the change stream and print out each event until the change\n\t// stream is closed by the server or there is an error getting the next\n\t// event.\n\tfor {\n\t\tif stream.TryNext(context.TODO()) {\n\t\t\t// A new event variable should be declared for each event.\n\t\t\tvar event bson.M\n\t\t\tif err := stream.Decode(&event); err != nil {\n\t\t\t\tlog.Panic(err)\n\t\t\t}\n\t\t\tfmt.Println(event)\n\t\t\tcontinue\n\t\t}\n\n\t\t// If TryNext returns false, the next change is not yet available, the\n\t\t// change stream was closed by the server, or an error occurred. TryNext\n\t\t// should only be called again for the empty batch case.\n\t\tif err := stream.Err(); err != nil {\n\t\t\tlog.Panic(err)\n\t\t}\n\t\tif stream.ID() == 0 {\n\t\t\tbreak\n\t\t}\n\t}\n}\n\nfunc ExampleChangeStream_ResumeToken() {\n\tvar client *mongo.Client\n\n\t// Assume stream was created via client.Watch()\n\tvar stream *mongo.ChangeStream\n\n\tcancelCtx, cancel := context.WithCancel(context.TODO())\n\tdefer cancel()\n\tvar wg sync.WaitGroup\n\twg.Add(1)\n\n\t// Run a goroutine to process events.\n\tgo func() {\n\t\tfor stream.Next(cancelCtx) {\n\t\t\tfmt.Println(stream.Current)\n\t\t}\n\t\twg.Done()\n\t}()\n\n\t// Assume client needs to be disconnected. Cancel the context being used by\n\t// the goroutine to abort any in-progres Next calls and wait for the\n\t// goroutine to exit.\n\tcancel()\n\twg.Wait()\n\n\t// Before disconnecting the client, store the last seen resume token for the\n\t// change stream.\n\tresumeToken := stream.ResumeToken()\n\t_ = client.Disconnect(context.TODO())\n\n\t// Once a new client is created, the change stream can be re-created.\n\t// Specify resumeToken as the ResumeAfter option so only events that\n\t// occurred after resumeToken will be returned.\n\tvar newClient *mongo.Client\n\topts := options.ChangeStream().SetResumeAfter(resumeToken)\n\tnewStream, err := newClient.Watch(context.TODO(), mongo.Pipeline{}, opts)\n\tif err != nil {\n\t\tlog.Panic(err)\n\t}\n\tdefer newStream.Close(context.TODO())\n}\n\n// IndexView examples\n\nfunc ExampleIndexView_CreateMany() {\n\tvar indexView *mongo.IndexView\n\n\t// Create two indexes: {name: 1, email: 1} and {name: 1, age: 1}\n\t// For the first index, specify no options. The name will be generated as\n\t// \"name_1_email_1\" by the driver.\n\t// For the second index, specify the Name option to explicitly set the name\n\t// to \"nameAge\".\n\tmodels := []mongo.IndexModel{\n\t\t{\n\t\t\tKeys: bson.D{{\"name\", 1}, {\"email\", 1}},\n\t\t},\n\t\t{\n\t\t\tKeys:    bson.D{{\"name\", 1}, {\"age\", 1}},\n\t\t\tOptions: options.Index().SetName(\"nameAge\"),\n\t\t},\n\t}\n\n\t// Specify the MaxTime option to limit the amount of time the operation can\n\t// run on the server\n\tnames, err := indexView.CreateMany(context.TODO(), models, nil)\n\tif err != nil {\n\t\tlog.Panic(err)\n\t}\n\n\tfmt.Printf(\"created indexes %v\\n\", names)\n}\n\nfunc ExampleIndexView_List() {\n\tvar indexView *mongo.IndexView\n\n\t// Specify a timeout to limit the amount of time the operation can run on\n\t// the server.\n\tctx, cancel := context.WithTimeout(context.TODO(), time.Second)\n\tdefer cancel()\n\n\tcursor, err := indexView.List(ctx, nil)\n\tif err != nil {\n\t\tlog.Panic(err)\n\t}\n\n\t// Get a slice of all indexes returned and print them out.\n\tvar results []bson.M\n\tif err = cursor.All(ctx, &results); err != nil {\n\t\tlog.Panic(err)\n\t}\n\tfmt.Println(results)\n}\n\nfunc ExampleCollection_Find_primitiveRegex() {\n\tctx := context.TODO()\n\tclientOptions := options.Client().ApplyURI(\"mongodb://localhost:27017\")\n\n\t// Connect to a mongodb server.\n\tclient, err := mongo.Connect(clientOptions)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tdefer func() { _ = client.Disconnect(ctx) }()\n\n\ttype Pet struct {\n\t\tType string `bson:\"type\"`\n\t\tName string `bson:\"name\"`\n\t}\n\n\t// Create a slice of documents to insert. We will lookup a subset of\n\t// these documents using regex.\n\ttoInsert := []any{\n\t\tPet{Type: \"cat\", Name: \"Mo\"},\n\t\tPet{Type: \"dog\", Name: \"Loki\"},\n\t}\n\n\tcoll := client.Database(\"test\").Collection(\"test\")\n\n\tif _, err := coll.InsertMany(ctx, toInsert); err != nil {\n\t\tpanic(err)\n\t}\n\n\t// Create a filter to find a document with key \"name\" and any value that\n\t// starts with letter \"m\". Use the \"i\" option to indicate\n\t// case-insensitivity.\n\tfilter := bson.D{{\"name\", bson.Regex{Pattern: \"^m\", Options: \"i\"}}}\n\n\t_, err = coll.Find(ctx, filter)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n}\n\nfunc ExampleCollection_Find_regex() {\n\tctx := context.TODO()\n\tclientOptions := options.Client().ApplyURI(\"mongodb://localhost:27017\")\n\n\t// Connect to a mongodb server.\n\tclient, err := mongo.Connect(clientOptions)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tdefer func() { _ = client.Disconnect(ctx) }()\n\n\ttype Pet struct {\n\t\tType string `bson:\"type\"`\n\t\tName string `bson:\"name\"`\n\t}\n\n\t// Create a slice of documents to insert. We will lookup a subset of\n\t// these documents using regex.\n\ttoInsert := []any{\n\t\tPet{Type: \"cat\", Name: \"Mo\"},\n\t\tPet{Type: \"dog\", Name: \"Loki\"},\n\t}\n\n\tcoll := client.Database(\"test\").Collection(\"test\")\n\n\tif _, err := coll.InsertMany(ctx, toInsert); err != nil {\n\t\tpanic(err)\n\t}\n\n\t// Create a filter to find a document with key \"name\" and any value that\n\t// starts with letter \"m\". Use the \"i\" option to indicate\n\t// case-insensitivity.\n\tfilter := bson.D{{\"name\", bson.D{{\"$regex\", \"^m\"}, {\"$options\", \"i\"}}}}\n\n\t_, err = coll.Find(ctx, filter)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n}\n"
  },
  {
    "path": "mongo/crypt_retrievers.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage mongo\n\nimport (\n\t\"context\"\n\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n)\n\n// keyRetriever gets keys from the key vault collection.\ntype keyRetriever struct {\n\tcoll *Collection\n}\n\nfunc (kr *keyRetriever) cryptKeys(ctx context.Context, filter bsoncore.Document) ([]bsoncore.Document, error) {\n\t// Remove the explicit session from the context if one is set.\n\t// The explicit session may be from a different client.\n\tctx = NewSessionContext(ctx, nil)\n\tcursor, err := kr.coll.Find(ctx, filter)\n\tif err != nil {\n\t\treturn nil, EncryptionKeyVaultError{Wrapped: err}\n\t}\n\tdefer cursor.Close(ctx)\n\n\tvar results []bsoncore.Document\n\tfor cursor.Next(ctx) {\n\t\tcur := make([]byte, len(cursor.Current))\n\t\tcopy(cur, cursor.Current)\n\t\tresults = append(results, cur)\n\t}\n\tif err = cursor.Err(); err != nil {\n\t\treturn nil, EncryptionKeyVaultError{Wrapped: err}\n\t}\n\n\treturn results, nil\n}\n\n// collInfoRetriever gets info for collections from a database.\ntype collInfoRetriever struct {\n\tclient *Client\n}\n\nfunc (cir *collInfoRetriever) cryptCollInfo(ctx context.Context, db string, filter bsoncore.Document) (bsoncore.Document, error) {\n\t// Remove the explicit session from the context if one is set.\n\t// The explicit session may be from a different client.\n\tctx = NewSessionContext(ctx, nil)\n\tcursor, err := cir.client.Database(db).ListCollections(ctx, filter)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer cursor.Close(ctx)\n\n\tif !cursor.Next(ctx) {\n\t\treturn nil, cursor.Err()\n\t}\n\n\tres := make([]byte, len(cursor.Current))\n\tcopy(res, cursor.Current)\n\treturn res, nil\n}\n"
  },
  {
    "path": "mongo/cursor.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage mongo\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"reflect\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/mongoutil\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/session\"\n)\n\n// Cursor is used to iterate over a stream of documents. Each document can be decoded into a Go type via the Decode\n// method or accessed as raw BSON via the Current field. This type is not goroutine safe and must not be used\n// concurrently by multiple goroutines.\ntype Cursor struct {\n\t// Current contains the BSON bytes of the current change document. This property is only valid until the next call\n\t// to Next or TryNext. If continued access is required, a copy must be made.\n\tCurrent bson.Raw\n\n\tbc               batchCursor\n\tbatch            *bsoncore.Iterator\n\tbatchLength      int\n\tbsonOpts         *options.BSONOptions\n\tregistry         *bson.Registry\n\tclientSession    *session.Client\n\tclientTimeout    time.Duration\n\thasClientTimeout bool\n\n\terr error\n}\n\ntype cursorOptions struct {\n\tclientTimeout    time.Duration\n\thasClientTimeout bool\n}\n\ntype cursorOption func(*cursorOptions)\n\nfunc withCursorOptionClientTimeout(dur *time.Duration) cursorOption {\n\treturn func(opts *cursorOptions) {\n\t\tif dur != nil && *dur > 0 {\n\t\t\topts.clientTimeout = *dur\n\t\t\topts.hasClientTimeout = true\n\t\t}\n\t}\n}\n\nfunc newCursor(\n\tbc batchCursor,\n\tbsonOpts *options.BSONOptions,\n\tregistry *bson.Registry,\n\topts ...cursorOption,\n) (*Cursor, error) {\n\treturn newCursorWithSession(bc, bsonOpts, registry, nil, opts...)\n}\n\nfunc newCursorWithSession(\n\tbc batchCursor,\n\tbsonOpts *options.BSONOptions,\n\tregistry *bson.Registry,\n\tclientSession *session.Client,\n\topts ...cursorOption,\n) (*Cursor, error) {\n\tif registry == nil {\n\t\tregistry = defaultRegistry\n\t}\n\tif bc == nil {\n\t\treturn nil, errors.New(\"batch cursor must not be nil\")\n\t}\n\n\tcursorOpts := &cursorOptions{}\n\tfor _, opt := range opts {\n\t\topt(cursorOpts)\n\t}\n\n\tc := &Cursor{\n\t\tbc:               bc,\n\t\tbsonOpts:         bsonOpts,\n\t\tregistry:         registry,\n\t\tclientSession:    clientSession,\n\t\tclientTimeout:    cursorOpts.clientTimeout,\n\t\thasClientTimeout: cursorOpts.hasClientTimeout,\n\t}\n\tif bc.ID() == 0 {\n\t\tc.closeImplicitSession()\n\t}\n\n\t// Initialize just the batchLength here so RemainingBatchLength will return an\n\t// accurate result. The actual batch will be pulled up by the first\n\t// Next/TryNext call.\n\tc.batchLength = c.bc.Batch().Count()\n\treturn c, nil\n}\n\nfunc newEmptyCursor() *Cursor {\n\treturn &Cursor{bc: driver.NewEmptyBatchCursor()}\n}\n\n// NewCursorFromDocuments creates a new Cursor pre-loaded with the provided documents, error and registry. If no registry is provided,\n// bson.NewRegistry() will be used.\n//\n// The documents parameter must be a slice of documents. The slice may be nil or empty, but all elements must be non-nil.\nfunc NewCursorFromDocuments(documents []any, preloadedErr error, registry *bson.Registry) (*Cursor, error) {\n\tif registry == nil {\n\t\tregistry = defaultRegistry\n\t}\n\n\tbuf := new(bytes.Buffer)\n\tenc := new(bson.Encoder)\n\n\tvalues := make([]bsoncore.Value, len(documents))\n\tfor i, doc := range documents {\n\t\tswitch t := doc.(type) {\n\t\tcase nil:\n\t\t\treturn nil, fmt.Errorf(\"invalid document at index %d: %w\", i, ErrNilDocument)\n\t\tcase []byte:\n\t\t\t// Slight optimization so we'll just use MarshalBSON and not go through the codec machinery.\n\t\t\tdoc = bson.Raw(t)\n\t\t}\n\n\t\tvw := bson.NewDocumentWriter(buf)\n\t\tenc.Reset(vw)\n\t\tenc.SetRegistry(registry)\n\n\t\tif err := enc.Encode(doc); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tdup := make([]byte, len(buf.Bytes()))\n\t\tcopy(dup, buf.Bytes())\n\n\t\tvalues[i] = bsoncore.Value{\n\t\t\tType: bsoncore.TypeEmbeddedDocument,\n\t\t\tData: dup,\n\t\t}\n\n\t\tbuf.Reset()\n\t}\n\n\tc := &Cursor{\n\t\tbc:       driver.NewBatchCursorFromList(bsoncore.BuildArray(nil, values...)),\n\t\tregistry: registry,\n\t\terr:      preloadedErr,\n\t}\n\n\t// Initialize batch and batchLength here. The underlying batch cursor will be preloaded with the\n\t// provided contents, and thus already has a batch before calls to Next/TryNext.\n\tc.batch = c.bc.Batch()\n\tc.batchLength = c.bc.Batch().Count()\n\n\treturn c, nil\n}\n\n// ID returns the ID of this cursor, or 0 if the cursor has been closed or exhausted.\nfunc (c *Cursor) ID() int64 { return c.bc.ID() }\n\n// Next gets the next document for this cursor. It returns true if there were no\n// errors and the cursor has not been exhausted.\n//\n// Next blocks until a document is available or an error occurs. If the context\n// expires, the cursor's error will be set to ctx.Err(). In case of an error,\n// Next will return false.\n//\n// If MaxAwaitTime is set, the operation will be bound by the Context's\n// deadline. If the context does not have a deadline, the operation will be\n// bound by the client-level timeout, if one is set. If MaxAwaitTime is greater\n// than the user-provided timeout, Next will return false.\n//\n// If Next returns false, subsequent calls will also return false.\nfunc (c *Cursor) Next(ctx context.Context) bool {\n\treturn c.next(ctx, false)\n}\n\n// TryNext attempts to get the next document for this cursor. It returns true if there were no errors and the next\n// document is available. This is only recommended for use with tailable cursors as a non-blocking alternative to\n// Next. See https://www.mongodb.com/docs/manual/core/tailable-cursors/ for more information about tailable cursors.\n//\n// TryNext returns false if the cursor is exhausted, an error occurs when getting results from the server, the next\n// document is not yet available, or ctx expires. If the context  expires, the cursor's error will be set to ctx.Err().\n//\n// If TryNext returns false and an error occurred or the cursor has been exhausted (i.e. c.Err() != nil || c.ID() == 0),\n// subsequent attempts will also return false. Otherwise, it is safe to call TryNext again until a document is\n// available.\n//\n// This method requires driver version >= 1.2.0.\nfunc (c *Cursor) TryNext(ctx context.Context) bool {\n\treturn c.next(ctx, true)\n}\n\nfunc (c *Cursor) next(ctx context.Context, nonBlocking bool) bool {\n\t// return false right away if the cursor has already errored.\n\tif c.err != nil {\n\t\treturn false\n\t}\n\n\tif ctx == nil {\n\t\tctx = context.Background()\n\t}\n\n\t// If the context does not have a deadline we defer to a client-level timeout,\n\t// if one is set.\n\tif _, ok := ctx.Deadline(); !ok && c.hasClientTimeout {\n\t\tvar cancel context.CancelFunc\n\t\tctx, cancel = context.WithTimeout(ctx, c.clientTimeout)\n\n\t\tdefer cancel()\n\t}\n\n\t// To avoid unnecessary socket timeouts, we attempt to short-circuit tailable\n\t// awaitData \"getMore\" operations by ensuring that the maxAwaitTimeMS is less\n\t// than the operation timeout.\n\t//\n\t// The specifications assume that drivers iteratively apply the timeout\n\t// provided at the constructor level (e.g., (*collection).Find) for tailable\n\t// awaitData cursors:\n\t//\n\t//   If set, drivers MUST apply the timeoutMS option to the initial aggregate\n\t//   operation. Drivers MUST also apply the original timeoutMS value to each\n\t//   next call on the change stream but MUST NOT use it to derive a maxTimeMS\n\t//   field for getMore commands.\n\t//\n\t// The Go Driver might decide to support the above behavior with DRIVERS-2722.\n\t// The principal concern is that it would be unexpected for users to apply an\n\t// operation-level timeout via contexts to a constructor and then that timeout\n\t// later be applied while working with a resulting cursor. Instead, it is more\n\t// idiomatic to apply the timeout to the context passed to Next or TryNext.\n\tmaxAwaitTime := c.bc.MaxAwaitTime() //\n\tif maxAwaitTime != nil && !nonBlocking && !mongoutil.TimeoutWithinContext(ctx, *maxAwaitTime) {\n\t\tc.err = fmt.Errorf(\"MaxAwaitTime must be less than the operation timeout\")\n\n\t\treturn false\n\t}\n\n\tval, err := c.batch.Next()\n\tswitch {\n\tcase err == nil:\n\t\t// Consume the next document in the current batch.\n\t\tc.batchLength--\n\t\tc.Current = bson.Raw(val.Data)\n\t\treturn true\n\tcase errors.Is(err, io.EOF): // Need to do a getMore\n\tdefault:\n\t\tc.err = err\n\t\treturn false\n\t}\n\n\t// call the Next method in a loop until at least one document is returned in the next batch or\n\t// the context times out.\n\tfor {\n\t\t// If we don't have a next batch\n\t\tif !c.bc.Next(ctx) {\n\t\t\t// Do we have an error? If so we return false.\n\t\t\tc.err = wrapErrors(c.bc.Err())\n\t\t\tif c.err != nil {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\t// Is the cursor ID zero?\n\t\t\tif c.bc.ID() == 0 {\n\t\t\t\tc.closeImplicitSession()\n\t\t\t\treturn false\n\t\t\t}\n\t\t\t// empty batch, but cursor is still valid.\n\t\t\t// use nonBlocking to determine if we should continue or return control to the caller.\n\t\t\tif nonBlocking {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\n\t\t// close the implicit session if this was the last getMore\n\t\tif c.bc.ID() == 0 {\n\t\t\tc.closeImplicitSession()\n\t\t}\n\n\t\t// Use the new batch to update the batch and batchLength fields. Consume the first document in the batch.\n\t\tc.batch = c.bc.Batch()\n\t\tc.batchLength = c.batch.Count()\n\t\tval, err = c.batch.Next()\n\t\tswitch {\n\t\tcase err == nil:\n\t\t\tc.batchLength--\n\t\t\tc.Current = bson.Raw(val.Data)\n\t\t\treturn true\n\t\tcase errors.Is(err, io.EOF): // Empty batch so we continue\n\t\tdefault:\n\t\t\tc.err = err\n\t\t\treturn false\n\t\t}\n\t}\n}\n\nfunc getDecoder(\n\tdata []byte,\n\topts *options.BSONOptions,\n\treg *bson.Registry,\n) *bson.Decoder {\n\tdec := bson.NewDecoder(bson.NewDocumentReader(bytes.NewReader(data)))\n\n\tif opts != nil {\n\t\tif opts.AllowTruncatingDoubles {\n\t\t\tdec.AllowTruncatingDoubles()\n\t\t}\n\t\tif opts.BinaryAsSlice {\n\t\t\tdec.BinaryAsSlice()\n\t\t}\n\t\tif opts.DefaultDocumentM {\n\t\t\tdec.DefaultDocumentM()\n\t\t}\n\t\tif opts.DefaultDocumentMap {\n\t\t\tdec.DefaultDocumentMap()\n\t\t}\n\t\tif opts.ObjectIDAsHexString {\n\t\t\tdec.ObjectIDAsHexString()\n\t\t}\n\t\tif opts.UseJSONStructTags {\n\t\t\tdec.UseJSONStructTags()\n\t\t}\n\t\tif opts.UseLocalTimeZone {\n\t\t\tdec.UseLocalTimeZone()\n\t\t}\n\t\tif opts.ZeroMaps {\n\t\t\tdec.ZeroMaps()\n\t\t}\n\t\tif opts.ZeroStructs {\n\t\t\tdec.ZeroStructs()\n\t\t}\n\t}\n\n\tif reg != nil {\n\t\tdec.SetRegistry(reg)\n\t}\n\n\treturn dec\n}\n\n// Decode will unmarshal the current document into val and return any errors from the unmarshalling process without any\n// modification. If val is nil or is a typed nil, an error will be returned.\nfunc (c *Cursor) Decode(val any) error {\n\tdec := getDecoder(c.Current, c.bsonOpts, c.registry)\n\n\treturn dec.Decode(val)\n}\n\n// Err returns the last error seen by the Cursor, or nil if no error has occurred.\nfunc (c *Cursor) Err() error { return c.err }\n\n// Close closes this cursor. Next and TryNext must not be called after Close has been called. Close is idempotent. After\n// the first call, any subsequent calls will not change the state.\nfunc (c *Cursor) Close(ctx context.Context) error {\n\tdefer c.closeImplicitSession()\n\treturn wrapErrors(c.bc.Close(ctx))\n}\n\n// All iterates the cursor and decodes each document into results. The results parameter must be a pointer to a slice.\n// The slice pointed to by results will be completely overwritten. A nil slice pointer will not be modified if the cursor\n// has been closed, exhausted, or is empty. This method will close the cursor after retrieving all documents. If the\n// cursor has been iterated, any previously iterated documents will not be included in results.\n//\n// This method requires driver version >= 1.1.0.\nfunc (c *Cursor) All(ctx context.Context, results any) error {\n\tresultsVal := reflect.ValueOf(results)\n\tif resultsVal.Kind() != reflect.Ptr {\n\t\treturn fmt.Errorf(\"results argument must be a pointer to a slice, but was a %s\", resultsVal.Kind())\n\t}\n\n\tsliceVal := resultsVal.Elem()\n\tif sliceVal.Kind() == reflect.Interface {\n\t\tsliceVal = sliceVal.Elem()\n\t}\n\n\tif sliceVal.Kind() != reflect.Slice {\n\t\treturn fmt.Errorf(\"results argument must be a pointer to a slice, but was a pointer to %s\", sliceVal.Kind())\n\t}\n\n\telementType := sliceVal.Type().Elem()\n\tvar index int\n\tvar err error\n\n\t// Defer a call to Close to try to clean up the cursor server-side when all\n\t// documents have not been exhausted. Use context.Background() to ensure Close\n\t// completes even if the context passed to All has errored.\n\tdefer c.Close(context.Background())\n\n\tbatch := c.batch // exhaust the current batch before iterating the batch cursor\n\tfor {\n\t\tsliceVal, index, err = c.addFromBatch(sliceVal, elementType, batch, index)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tif !c.bc.Next(ctx) {\n\t\t\tbreak\n\t\t}\n\n\t\tbatch = c.bc.Batch()\n\t}\n\n\tif err = wrapErrors(c.bc.Err()); err != nil {\n\t\treturn err\n\t}\n\n\tresultsVal.Elem().Set(sliceVal.Slice(0, index))\n\treturn nil\n}\n\n// RemainingBatchLength returns the number of documents left in the current batch. If this returns zero, the subsequent\n// call to Next or TryNext will do a network request to fetch the next batch.\nfunc (c *Cursor) RemainingBatchLength() int {\n\treturn c.batchLength\n}\n\n// addFromBatch adds all documents from batch to sliceVal starting at the given index. It returns the new slice value,\n// the next empty index in the slice, and an error if one occurs.\nfunc (c *Cursor) addFromBatch(sliceVal reflect.Value, elemType reflect.Type, batch *bsoncore.Iterator,\n\tindex int,\n) (reflect.Value, int, error) {\n\tdocs, err := batch.Documents()\n\tif err != nil {\n\t\treturn sliceVal, index, err\n\t}\n\n\tfor _, doc := range docs {\n\t\tif sliceVal.Len() == index {\n\t\t\t// slice is full\n\t\t\tnewElem := reflect.New(elemType)\n\t\t\tsliceVal = reflect.Append(sliceVal, newElem.Elem())\n\t\t\tsliceVal = sliceVal.Slice(0, sliceVal.Cap())\n\t\t}\n\n\t\tcurrElem := sliceVal.Index(index).Addr().Interface()\n\t\tdec := getDecoder(doc, c.bsonOpts, c.registry)\n\t\terr = dec.Decode(currElem)\n\t\tif err != nil {\n\t\t\treturn sliceVal, index, err\n\t\t}\n\n\t\tindex++\n\t}\n\n\treturn sliceVal, index, nil\n}\n\nfunc (c *Cursor) closeImplicitSession() {\n\tif c.clientSession != nil && c.clientSession.IsImplicit {\n\t\tc.clientSession.EndSession()\n\t}\n}\n\n// SetBatchSize sets the number of documents to fetch from the database with\n// each iteration of the cursor's \"Next\" method. Note that some operations set\n// an initial cursor batch size, so this setting only affects subsequent\n// document batches fetched from the database.\nfunc (c *Cursor) SetBatchSize(batchSize int32) {\n\tc.bc.SetBatchSize(batchSize)\n}\n\n// SetMaxAwaitTime will set the maximum amount of time the server will allow the\n// operations to execute. The server will error if this field is set but the\n// cursor is not configured with awaitData=true.\n//\n// The time.Duration value passed by this setter will be converted and rounded\n// down to the nearest millisecond.\nfunc (c *Cursor) SetMaxAwaitTime(dur time.Duration) {\n\tc.bc.SetMaxAwaitTime(dur)\n}\n\n// SetComment will set a user-configurable comment that can be used to identify\n// the operation in server logs.\nfunc (c *Cursor) SetComment(comment any) {\n\tc.bc.SetComment(comment)\n}\n\n// BatchCursorFromCursor returns a driver.BatchCursor for the given Cursor. If there is no underlying\n// driver.BatchCursor, nil is returned.\n//\n// Deprecated: This is an unstable function because the driver.BatchCursor type exists in the \"x\" package. Neither this\n// function nor the driver.BatchCursor type should be used by applications and may be changed or removed in any release.\nfunc BatchCursorFromCursor(c *Cursor) *driver.BatchCursor {\n\tbc, _ := c.bc.(*driver.BatchCursor)\n\treturn bc\n}\n"
  },
  {
    "path": "mongo/cursor_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage mongo\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"reflect\"\n\t\"testing\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/require\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver\"\n)\n\ntype testBatchCursor struct {\n\tbatches []*bsoncore.Iterator\n\tbatch   *bsoncore.Iterator\n\tclosed  bool\n}\n\nvar _ batchCursor = (*testBatchCursor)(nil)\n\nfunc newTestBatchCursor(numBatches, batchSize int) *testBatchCursor {\n\tbatches := make([]*bsoncore.Iterator, 0, numBatches)\n\n\tcounter := 0\n\tfor batch := 0; batch < numBatches; batch++ {\n\t\tvar values []bsoncore.Value\n\n\t\tfor doc := 0; doc < batchSize; doc++ {\n\t\t\tvar elem []byte\n\t\t\telem = bsoncore.AppendInt32Element(elem, \"foo\", int32(counter))\n\t\t\tcounter++\n\n\t\t\tvar doc []byte\n\t\t\tdoc = bsoncore.BuildDocumentFromElements(doc, elem)\n\t\t\tval := bsoncore.Value{\n\t\t\t\tType: bsoncore.TypeEmbeddedDocument,\n\t\t\t\tData: doc,\n\t\t\t}\n\n\t\t\tvalues = append(values, val)\n\t\t}\n\n\t\tarr := bsoncore.BuildArray(nil, values...)\n\n\t\tbatches = append(batches, &bsoncore.Iterator{\n\t\t\tList: arr,\n\t\t})\n\t}\n\n\treturn &testBatchCursor{\n\t\tbatches: batches,\n\t}\n}\n\nfunc (tbc *testBatchCursor) ID() int64 {\n\tif len(tbc.batches) == 0 {\n\t\treturn 0 // cursor exhausted\n\t}\n\n\treturn 10\n}\n\nfunc (tbc *testBatchCursor) Next(context.Context) bool {\n\tif len(tbc.batches) == 0 {\n\t\treturn false\n\t}\n\n\ttbc.batch = tbc.batches[0]\n\ttbc.batches = tbc.batches[1:]\n\treturn true\n}\n\nfunc (tbc *testBatchCursor) Batch() *bsoncore.Iterator {\n\treturn tbc.batch\n}\n\nfunc (tbc *testBatchCursor) Server() driver.Server {\n\treturn nil\n}\n\nfunc (tbc *testBatchCursor) Err() error {\n\treturn nil\n}\n\nfunc (tbc *testBatchCursor) Close(context.Context) error {\n\ttbc.closed = true\n\treturn nil\n}\n\nfunc (tbc *testBatchCursor) SetBatchSize(int32)            {}\nfunc (tbc *testBatchCursor) SetComment(any)                {}\nfunc (tbc *testBatchCursor) SetMaxAwaitTime(time.Duration) {}\nfunc (tbc *testBatchCursor) MaxAwaitTime() *time.Duration  { return nil }\n\nfunc TestCursor(t *testing.T) {\n\tt.Run(\"TestAll\", func(t *testing.T) {\n\t\tt.Run(\"errors if argument is not pointer to slice\", func(t *testing.T) {\n\t\t\tcursor, err := newCursor(newTestBatchCursor(1, 5), nil, nil)\n\t\t\trequire.NoError(t, err, \"newCursor error: %v\", err)\n\t\t\terr = cursor.All(context.Background(), []bson.D{})\n\t\t\tassert.Error(t, err, \"expected error, got nil\")\n\t\t})\n\n\t\tt.Run(\"fills slice with all documents\", func(t *testing.T) {\n\t\t\tcursor, err := newCursor(newTestBatchCursor(1, 5), nil, nil)\n\t\t\trequire.NoError(t, err, \"newCursor error: %v\", err)\n\n\t\t\tvar docs []bson.D\n\t\t\terr = cursor.All(context.Background(), &docs)\n\t\t\trequire.NoError(t, err, \"All error: %v\", err)\n\t\t\tassert.Len(t, docs, 5, \"expected 5 docs, got %v\", len(docs))\n\n\t\t\tfor index, doc := range docs {\n\t\t\t\texpected := bson.D{{\"foo\", int32(index)}}\n\t\t\t\tassert.Equal(t, expected, doc, \"expected doc %v, got %v\", expected, doc)\n\t\t\t}\n\t\t})\n\n\t\tt.Run(\"nil slice\", func(t *testing.T) {\n\t\t\tcursor, err := newCursor(newTestBatchCursor(0, 0), nil, nil)\n\t\t\trequire.NoError(t, err, \"newCursor error: %v\", err)\n\n\t\t\tvar docs []bson.D\n\t\t\terr = cursor.All(context.Background(), &docs)\n\t\t\trequire.NoError(t, err, \"All error: %v\", err)\n\t\t\tassert.Nil(t, docs, \"expected nil docs\")\n\t\t})\n\n\t\tt.Run(\"empty slice\", func(t *testing.T) {\n\t\t\tcursor, err := newCursor(newTestBatchCursor(0, 0), nil, nil)\n\t\t\trequire.NoError(t, err, \"newCursor error: %v\", err)\n\n\t\t\tdocs := []bson.D{}\n\t\t\terr = cursor.All(context.Background(), &docs)\n\t\t\trequire.NoError(t, err, \"All error: %v\", err)\n\t\t\tassert.NotNil(t, docs, \"expected non-nil docs\")\n\t\t\tassert.Len(t, docs, 0, \"expected 0 docs, got %v\", len(docs))\n\t\t})\n\n\t\tt.Run(\"empty slice overwritten\", func(t *testing.T) {\n\t\t\tcursor, err := newCursor(newTestBatchCursor(0, 0), nil, nil)\n\t\t\trequire.NoError(t, err, \"newCursor error: %v\", err)\n\n\t\t\tdocs := []bson.D{{{\"foo\", \"bar\"}}, {{\"hello\", \"world\"}, {\"pi\", 3.14159}}}\n\t\t\terr = cursor.All(context.Background(), &docs)\n\t\t\trequire.NoError(t, err, \"All error: %v\", err)\n\t\t\tassert.NotNil(t, docs, \"expected non-nil docs\")\n\t\t\tassert.Len(t, docs, 0, \"expected 0 docs, got %v\", len(docs))\n\t\t})\n\n\t\tt.Run(\"decodes each document into slice type\", func(t *testing.T) {\n\t\t\tcursor, err := newCursor(newTestBatchCursor(1, 5), nil, nil)\n\t\t\trequire.NoError(t, err, \"newCursor error: %v\", err)\n\n\t\t\ttype Document struct {\n\t\t\t\tFoo int32 `bson:\"foo\"`\n\t\t\t}\n\t\t\tvar docs []Document\n\t\t\terr = cursor.All(context.Background(), &docs)\n\t\t\trequire.NoError(t, err, \"All error: %v\", err)\n\t\t\tassert.Len(t, docs, 5, \"expected 5 documents, got %v\", len(docs))\n\n\t\t\tfor index, doc := range docs {\n\t\t\t\texpected := Document{Foo: int32(index)}\n\t\t\t\tassert.Equal(t, expected, doc, \"expected doc %v, got %v\", expected, doc)\n\t\t\t}\n\t\t})\n\n\t\tt.Run(\"multiple batches are included\", func(t *testing.T) {\n\t\t\tcursor, err := newCursor(newTestBatchCursor(2, 5), nil, nil)\n\t\t\trequire.NoError(t, err, \"newCursor error: %v\", err)\n\t\t\tvar docs []bson.D\n\t\t\terr = cursor.All(context.Background(), &docs)\n\t\t\trequire.NoError(t, err, \"All error: %v\", err)\n\t\t\tassert.Len(t, docs, 10, \"expected 10 docs, got %v\", len(docs))\n\n\t\t\tfor index, doc := range docs {\n\t\t\t\texpected := bson.D{{\"foo\", int32(index)}}\n\t\t\t\tassert.Equal(t, expected, doc, \"expected doc %v, got %v\", expected, doc)\n\t\t\t}\n\t\t})\n\n\t\tt.Run(\"cursor is closed after All is called\", func(t *testing.T) {\n\t\t\tvar docs []bson.D\n\n\t\t\ttbc := newTestBatchCursor(1, 5)\n\t\t\tcursor, err := newCursor(tbc, nil, nil)\n\t\t\trequire.NoError(t, err, \"newCursor error: %v\", err)\n\n\t\t\terr = cursor.All(context.Background(), &docs)\n\t\t\trequire.NoError(t, err, \"All error: %v\", err)\n\t\t\tassert.True(t, tbc.closed, \"expected batch cursor to be closed but was not\")\n\t\t})\n\n\t\tt.Run(\"does not error given interface as parameter\", func(t *testing.T) {\n\t\t\tvar docs any = []bson.D{}\n\n\t\t\tcursor, err := newCursor(newTestBatchCursor(1, 5), nil, nil)\n\t\t\trequire.NoError(t, err, \"newCursor error: %v\", err)\n\n\t\t\terr = cursor.All(context.Background(), &docs)\n\t\t\trequire.NoError(t, err, \"All error: %v\", err)\n\t\t\tassert.Len(t, docs.([]bson.D), 5, \"expected 5 documents, got %v\", len(docs.([]bson.D)))\n\t\t})\n\t\tt.Run(\"errors when not given pointer to slice\", func(t *testing.T) {\n\t\t\tvar docs any = \"test\"\n\n\t\t\tcursor, err := newCursor(newTestBatchCursor(1, 5), nil, nil)\n\t\t\trequire.NoError(t, err, \"newCursor error: %v\", err)\n\n\t\t\terr = cursor.All(context.Background(), &docs)\n\t\t\tassert.Error(t, err, \"expected error, got: %v\", err)\n\t\t})\n\t\tt.Run(\"with BSONOptions\", func(t *testing.T) {\n\t\t\tcursor, err := newCursor(\n\t\t\t\tnewTestBatchCursor(1, 5),\n\t\t\t\t&options.BSONOptions{\n\t\t\t\t\tUseJSONStructTags: true,\n\t\t\t\t},\n\t\t\t\tnil)\n\t\t\trequire.NoError(t, err, \"newCursor error: %v\", err)\n\n\t\t\ttype myDocument struct {\n\t\t\t\tA int32 `json:\"foo\"`\n\t\t\t}\n\t\t\tvar got []myDocument\n\n\t\t\terr = cursor.All(context.Background(), &got)\n\t\t\trequire.NoError(t, err, \"All error: %v\", err)\n\n\t\t\twant := []myDocument{{A: 0}, {A: 1}, {A: 2}, {A: 3}, {A: 4}}\n\n\t\t\tassert.Equal(t, want, got, \"expected and actual All results are different\")\n\t\t})\n\t})\n}\n\nfunc TestNewCursorFromDocuments(t *testing.T) {\n\t// Mock documents returned by Find in a Cursor.\n\tt.Run(\"mock Find\", func(t *testing.T) {\n\t\tfindResult := []any{\n\t\t\tbson.D{{\"_id\", 0}, {\"foo\", \"bar\"}},\n\t\t\tbson.D{{\"_id\", 1}, {\"baz\", \"qux\"}},\n\t\t\tbson.D{{\"_id\", 2}, {\"quux\", \"quuz\"}},\n\t\t}\n\t\tcur, err := NewCursorFromDocuments(findResult, nil, nil)\n\t\trequire.NoError(t, err, \"NewCursorFromDocuments error: %v\", err)\n\n\t\t// Assert that decoded documents are as expected.\n\t\tvar i int\n\t\tfor cur.Next(context.Background()) {\n\t\t\tdocBytes, err := bson.Marshal(findResult[i])\n\t\t\trequire.NoError(t, err, \"Marshal error: %v\", err)\n\t\t\texpectedDecoded := bson.Raw(docBytes)\n\n\t\t\tvar decoded bson.Raw\n\t\t\terr = cur.Decode(&decoded)\n\t\t\trequire.NoError(t, err, \"Decode error: %v\", err)\n\t\t\tassert.Equal(t, expectedDecoded, decoded,\n\t\t\t\t\"expected decoded document %v of Cursor to be %v, got %v\",\n\t\t\t\ti, expectedDecoded, decoded)\n\t\t\ti++\n\t\t}\n\t\tassert.Equal(t, 3, i, \"expected 3 calls to cur.Next, got %v\", i)\n\n\t\t// Check for error on Cursor.\n\t\trequire.NoError(t, cur.Err(), \"Cursor error: %v\", cur.Err())\n\n\t\t// Assert that a call to cur.Close will not fail.\n\t\terr = cur.Close(context.Background())\n\t\trequire.NoError(t, err, \"Close error: %v\", err)\n\t})\n\n\t// Mock an error in a Cursor.\n\tt.Run(\"mock Find with error\", func(t *testing.T) {\n\t\tmockErr := fmt.Errorf(\"mock error\")\n\t\tfindResult := []any{bson.D{{\"_id\", 0}, {\"foo\", \"bar\"}}}\n\t\tcur, err := NewCursorFromDocuments(findResult, mockErr, nil)\n\t\trequire.NoError(t, err, \"NewCursorFromDocuments error: %v\", err)\n\n\t\t// Assert that a call to Next will return false because of existing error.\n\t\tnext := cur.Next(context.Background())\n\t\tassert.False(t, next, \"expected call to Next to return false, got true\")\n\n\t\t// Check for error on Cursor.\n\t\tassert.Error(t, cur.Err(), \"expected Cursor error, got nil\")\n\t\tassert.Equal(t, mockErr, cur.Err(), \"expected Cursor error %v, got %v\",\n\t\t\tmockErr, cur.Err())\n\t})\n}\n\nfunc TestGetDecoder(t *testing.T) {\n\tt.Parallel()\n\n\tdecT := reflect.TypeOf((*bson.Decoder)(nil))\n\tctxT := reflect.TypeOf(bson.DecodeContext{})\n\tfor i := 0; i < decT.NumMethod(); i++ {\n\t\tm := decT.Method(i)\n\t\t// Test methods with no input/output parameter.\n\t\tif m.Type.NumIn() != 1 || m.Type.NumOut() != 0 {\n\t\t\tcontinue\n\t\t}\n\t\tt.Run(m.Name, func(t *testing.T) {\n\t\t\tvar opts options.BSONOptions\n\t\t\toptsV := reflect.ValueOf(&opts).Elem()\n\t\t\tf, ok := optsV.Type().FieldByName(m.Name)\n\t\t\trequire.True(t, ok, \"expected %s field in %s\", m.Name, optsV.Type())\n\n\t\t\twantDec := reflect.ValueOf(bson.NewDecoder(nil))\n\t\t\t_ = wantDec.Method(i).Call(nil)\n\t\t\twantCtx := wantDec.Elem().Field(0)\n\t\t\trequire.Equal(t, ctxT, wantCtx.Type())\n\n\t\t\toptsV.FieldByIndex(f.Index).SetBool(true)\n\t\t\tgotDec := getDecoder(nil, &opts, nil)\n\t\t\tgotCtx := reflect.ValueOf(gotDec).Elem().Field(0)\n\t\t\trequire.Equal(t, ctxT, gotCtx.Type())\n\n\t\t\tassert.True(t, gotCtx.Equal(wantCtx), \"expected %v: %v, got: %v\", ctxT, wantCtx, gotCtx)\n\t\t})\n\t}\n}\n\nfunc BenchmarkNewCursorFromDocuments(b *testing.B) {\n\t// Prepare sample data\n\tdocuments := []any{\n\t\tbson.D{{\"_id\", 0}, {\"foo\", \"bar\"}},\n\t\tbson.D{{\"_id\", 1}, {\"baz\", \"qux\"}},\n\t\tbson.D{{\"_id\", 2}, {\"quux\", \"quuz\"}},\n\t}\n\n\tb.ReportAllocs()\n\tb.ResetTimer()\n\n\tfor i := 0; i < b.N; i++ {\n\t\t_, err := NewCursorFromDocuments(documents, nil, nil)\n\t\tif err != nil {\n\t\t\tb.Fatalf(\"Error creating cursor: %v\", err)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "mongo/database.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage mongo\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/csfle\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/csot\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/mongoutil\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/optionsutil\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/serverselector\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/readconcern\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/readpref\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/writeconcern\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/description\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/operation\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/session\"\n)\n\nvar defaultRunCmdOpts = []options.Lister[options.RunCmdOptions]{options.RunCmd().SetReadPreference(readpref.Primary())}\n\n// Database is a handle to a MongoDB database. It is safe for concurrent use by multiple goroutines.\ntype Database struct {\n\tclient         *Client\n\tname           string\n\treadConcern    *readconcern.ReadConcern\n\twriteConcern   *writeconcern.WriteConcern\n\treadPreference *readpref.ReadPref\n\treadSelector   description.ServerSelector\n\twriteSelector  description.ServerSelector\n\tbsonOpts       *options.BSONOptions\n\tregistry       *bson.Registry\n}\n\nfunc newDatabase(client *Client, name string, opts ...options.Lister[options.DatabaseOptions]) *Database {\n\targs, _ := mongoutil.NewOptions[options.DatabaseOptions](opts...)\n\n\trc := client.readConcern\n\tif args.ReadConcern != nil {\n\t\trc = args.ReadConcern\n\t}\n\n\trp := client.readPreference\n\tif args.ReadPreference != nil {\n\t\trp = args.ReadPreference\n\t}\n\n\twc := client.writeConcern\n\tif args.WriteConcern != nil {\n\t\twc = args.WriteConcern\n\t}\n\n\tbsonOpts := client.bsonOpts\n\tif args.BSONOptions != nil {\n\t\tbsonOpts = args.BSONOptions\n\t}\n\n\treg := client.registry\n\tif args.Registry != nil {\n\t\treg = args.Registry\n\t}\n\n\tdb := &Database{\n\t\tclient:         client,\n\t\tname:           name,\n\t\treadPreference: rp,\n\t\treadConcern:    rc,\n\t\twriteConcern:   wc,\n\t\tbsonOpts:       bsonOpts,\n\t\tregistry:       reg,\n\t}\n\n\tdb.readSelector = &serverselector.Composite{\n\t\tSelectors: []description.ServerSelector{\n\t\t\t&serverselector.ReadPref{ReadPref: db.readPreference},\n\t\t\t&serverselector.Latency{Latency: db.client.localThreshold},\n\t\t},\n\t}\n\n\tdb.writeSelector = &serverselector.Composite{\n\t\tSelectors: []description.ServerSelector{\n\t\t\t&serverselector.Write{},\n\t\t\t&serverselector.Latency{Latency: db.client.localThreshold},\n\t\t},\n\t}\n\n\treturn db\n}\n\n// Client returns the Client the Database was created from.\nfunc (db *Database) Client() *Client {\n\treturn db.client\n}\n\n// Name returns the name of the database.\nfunc (db *Database) Name() string {\n\treturn db.name\n}\n\n// Collection returns a handle for a collection with the given name and options.\n//\n// If the collection does not exist on the server, it will be created when a\n// write operation is performed.\nfunc (db *Database) Collection(name string, opts ...options.Lister[options.CollectionOptions]) *Collection {\n\treturn newCollection(db, name, opts...)\n}\n\n// Aggregate executes an aggregate command the database.\n//\n// The pipeline parameter must be a slice of documents, each representing an aggregation stage. The pipeline\n// cannot be nil but can be empty. The stage documents must all be non-nil. For a pipeline of bson.D documents, the\n// mongo.Pipeline type can be used. See\n// https://www.mongodb.com/docs/manual/reference/operator/aggregation-pipeline/#db-aggregate-stages for a list of valid\n// stages in database-level aggregations.\n//\n// The opts parameter can be used to specify options for this operation (see the options.AggregateOptions documentation).\n//\n// For more information about the command, see https://www.mongodb.com/docs/manual/reference/command/aggregate/.\nfunc (db *Database) Aggregate(\n\tctx context.Context,\n\tpipeline any,\n\topts ...options.Lister[options.AggregateOptions],\n) (*Cursor, error) {\n\ta := aggregateParams{\n\t\tctx:            ctx,\n\t\tpipeline:       pipeline,\n\t\tclient:         db.client,\n\t\tregistry:       db.registry,\n\t\treadConcern:    db.readConcern,\n\t\twriteConcern:   db.writeConcern,\n\t\tretryRead:      db.client.retryReads,\n\t\tdb:             db.name,\n\t\treadSelector:   db.readSelector,\n\t\twriteSelector:  db.writeSelector,\n\t\treadPreference: db.readPreference,\n\t}\n\n\treturn aggregate(a, opts...)\n}\n\nfunc (db *Database) processRunCommand(\n\tctx context.Context,\n\tcmd any,\n\tcursorCommand bool,\n\topts ...options.Lister[options.RunCmdOptions],\n) (*operation.Command, *session.Client, error) {\n\targs, err := mongoutil.NewOptions[options.RunCmdOptions](append(defaultRunCmdOpts, opts...)...)\n\tif err != nil {\n\t\treturn nil, nil, fmt.Errorf(\"failed to construct options from builder: %w\", err)\n\t}\n\n\tsess := sessionFromContext(ctx)\n\tif sess == nil && db.client.sessionPool != nil {\n\t\tsess = session.NewImplicitClientSession(db.client.sessionPool, db.client.id)\n\t}\n\n\tif err := db.client.validSession(sess); err != nil {\n\t\treturn nil, sess, err\n\t}\n\n\tif sess != nil && sess.TransactionRunning() && args.ReadPreference != nil && args.ReadPreference.Mode() != readpref.PrimaryMode {\n\t\treturn nil, sess, errors.New(\"read preference in a transaction must be primary\")\n\t}\n\n\tif isUnorderedMap(cmd) {\n\t\treturn nil, sess, ErrMapForOrderedArgument{\"cmd\"}\n\t}\n\n\trunCmdDoc, err := marshal(cmd, db.bsonOpts, db.registry)\n\tif err != nil {\n\t\treturn nil, sess, err\n\t}\n\n\tvar readSelect description.ServerSelector\n\n\treadSelect = &serverselector.Composite{\n\t\tSelectors: []description.ServerSelector{\n\t\t\t&serverselector.ReadPref{ReadPref: args.ReadPreference},\n\t\t\t&serverselector.Latency{Latency: db.client.localThreshold},\n\t\t},\n\t}\n\n\tif sess != nil && sess.PinnedServerAddr != nil {\n\t\treadSelect = makePinnedSelector(sess, readSelect)\n\t}\n\n\tvar op *operation.Command\n\tswitch cursorCommand {\n\tcase true:\n\t\tcursorOpts := db.client.createBaseCursorOptions()\n\n\t\tcursorOpts.MarshalValueEncoderFn = newEncoderFn(db.bsonOpts, db.registry)\n\n\t\top = operation.NewCursorCommand(runCmdDoc, cursorOpts)\n\tdefault:\n\t\top = operation.NewCommand(runCmdDoc)\n\t}\n\n\treturn op.Session(sess).CommandMonitor(db.client.monitor).\n\t\tServerSelector(readSelect).ClusterClock(db.client.clock).\n\t\tDatabase(db.name).Deployment(db.client.deployment).\n\t\tCrypt(db.client.cryptFLE).ReadPreference(args.ReadPreference).ServerAPI(db.client.serverAPI).\n\t\tTimeout(db.client.timeout).Logger(db.client.logger).Authenticator(db.client.authenticator), sess, nil\n}\n\n// RunCommand executes the given command against the database.\n//\n// This function does not obey the Database's readPreference. To specify a read\n// preference, the RunCmdOptions.ReadPreference option must be used.\n//\n// This function does not obey the Database's readConcern or writeConcern. A\n// user must supply these values manually in the user-provided runCommand\n// parameter.\n//\n// The runCommand parameter must be a document for the command to be executed. It cannot be nil.\n// This must be an order-preserving type such as bson.D. Map types such as bson.M are not valid.\n//\n// The opts parameter can be used to specify options for this operation (see the options.RunCmdOptions documentation).\n//\n// The behavior of RunCommand is undefined if the command document contains any of the following:\n// - A session ID or any transaction-specific fields\n// - API versioning options when an API version is already declared on the Client\n// - maxTimeMS when Timeout is set on the Client\nfunc (db *Database) RunCommand(\n\tctx context.Context,\n\trunCommand any,\n\topts ...options.Lister[options.RunCmdOptions],\n) *SingleResult {\n\tif ctx == nil {\n\t\tctx = context.Background()\n\t}\n\n\top, sess, err := db.processRunCommand(ctx, runCommand, false, opts...)\n\tdefer closeImplicitSession(sess)\n\tif err != nil {\n\t\treturn &SingleResult{err: err}\n\t}\n\n\terr = op.Execute(ctx)\n\t// RunCommand can be used to run a write, thus execute may return a write error\n\trr, convErr := processWriteError(err)\n\treturn &SingleResult{\n\t\tctx:          ctx,\n\t\terr:          convErr,\n\t\trdr:          bson.Raw(op.Result()),\n\t\tbsonOpts:     db.bsonOpts,\n\t\treg:          db.registry,\n\t\tAcknowledged: rr.isAcknowledged(),\n\t}\n}\n\n// RunCommandCursor executes the given command against the database and parses the response as a cursor. If the command\n// being executed does not return a cursor (e.g. insert), the command will be executed on the server and an error will\n// be returned because the server response cannot be parsed as a cursor. This function does not obey the Database's read\n// preference. To specify a read preference, the RunCmdOptions.ReadPreference option must be used.\n//\n// The runCommand parameter must be a document for the command to be executed. It cannot be nil.\n// This must be an order-preserving type such as bson.D. Map types such as bson.M are not valid.\n//\n// The opts parameter can be used to specify options for this operation (see the options.RunCmdOptions documentation).\n//\n// The behavior of RunCommandCursor is undefined if the command document contains any of the following:\n// - A session ID or any transaction-specific fields\n// - API versioning options when an API version is already declared on the Client\n// - maxTimeMS when Timeout is set on the Client\nfunc (db *Database) RunCommandCursor(\n\tctx context.Context,\n\trunCommand any,\n\topts ...options.Lister[options.RunCmdOptions],\n) (*Cursor, error) {\n\tif ctx == nil {\n\t\tctx = context.Background()\n\t}\n\n\top, sess, err := db.processRunCommand(ctx, runCommand, true, opts...)\n\tif err != nil {\n\t\tcloseImplicitSession(sess)\n\t\treturn nil, wrapErrors(err)\n\t}\n\n\tif err = op.Execute(ctx); err != nil {\n\t\tcloseImplicitSession(sess)\n\t\tif errors.Is(err, driver.ErrNoCursor) {\n\t\t\treturn nil, errors.New(\n\t\t\t\t\"database response does not contain a cursor; try using RunCommand instead\")\n\t\t}\n\t\treturn nil, wrapErrors(err)\n\t}\n\n\tbc, err := op.ResultCursor()\n\tif err != nil {\n\t\tcloseImplicitSession(sess)\n\t\treturn nil, wrapErrors(err)\n\t}\n\tcursor, err := newCursorWithSession(bc, db.bsonOpts, db.registry, sess,\n\t\twithCursorOptionClientTimeout(db.client.timeout))\n\treturn cursor, wrapErrors(err)\n}\n\n// Drop drops the database on the server. This method ignores \"namespace not found\" errors so it is safe to drop\n// a database that does not exist on the server.\nfunc (db *Database) Drop(ctx context.Context) error {\n\tif ctx == nil {\n\t\tctx = context.Background()\n\t}\n\n\tsess := sessionFromContext(ctx)\n\tif sess == nil && db.client.sessionPool != nil {\n\t\tsess = session.NewImplicitClientSession(db.client.sessionPool, db.client.id)\n\t\tdefer sess.EndSession()\n\t}\n\n\terr := db.client.validSession(sess)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\twc := db.writeConcern\n\tif sess.TransactionRunning() {\n\t\twc = nil\n\t}\n\tif !wc.Acknowledged() {\n\t\tsess = nil\n\t}\n\n\tselector := makePinnedSelector(sess, db.writeSelector)\n\n\top := operation.NewDropDatabase().\n\t\tSession(sess).WriteConcern(wc).CommandMonitor(db.client.monitor).\n\t\tServerSelector(selector).ClusterClock(db.client.clock).\n\t\tDatabase(db.name).Deployment(db.client.deployment).Crypt(db.client.cryptFLE).\n\t\tServerAPI(db.client.serverAPI).Authenticator(db.client.authenticator)\n\n\terr = op.Execute(ctx)\n\n\tvar driverErr driver.Error\n\tif err != nil && (!errors.As(err, &driverErr) || !driverErr.NamespaceNotFound()) {\n\t\treturn wrapErrors(err)\n\t}\n\treturn nil\n}\n\n// ListCollectionSpecifications executes a listCollections command and returns a slice of CollectionSpecification\n// instances representing the collections in the database.\n//\n// The filter parameter must be a document containing query operators and can be used to select which collections\n// are included in the result. It cannot be nil. An empty document (e.g. bson.D{}) should be used to include all\n// collections.\n//\n// The opts parameter can be used to specify options for the operation (see the options.ListCollectionsOptions\n// documentation).\n//\n// For more information about the command, see https://www.mongodb.com/docs/manual/reference/command/listCollections/.\nfunc (db *Database) ListCollectionSpecifications(\n\tctx context.Context,\n\tfilter any,\n\topts ...options.Lister[options.ListCollectionsOptions],\n) ([]CollectionSpecification, error) {\n\tcursor, err := db.ListCollections(ctx, filter, opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar resp []struct {\n\t\tName string `bson:\"name\"`\n\t\tType string `bson:\"type\"`\n\t\tInfo *struct {\n\t\t\tReadOnly bool         `bson:\"readOnly\"`\n\t\t\tUUID     *bson.Binary `bson:\"uuid\"`\n\t\t} `bson:\"info\"`\n\t\tOptions bson.Raw                       `bson:\"options\"`\n\t\tIDIndex indexListSpecificationResponse `bson:\"idIndex\"`\n\t}\n\n\terr = cursor.All(ctx, &resp)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tspecs := make([]CollectionSpecification, len(resp))\n\tfor idx, spec := range resp {\n\t\tspecs[idx] = CollectionSpecification{\n\t\t\tName:    spec.Name,\n\t\t\tType:    spec.Type,\n\t\t\tOptions: spec.Options,\n\t\t\tIDIndex: IndexSpecification(spec.IDIndex),\n\t\t}\n\n\t\tif spec.Info != nil {\n\t\t\tspecs[idx].ReadOnly = spec.Info.ReadOnly\n\t\t\tspecs[idx].UUID = spec.Info.UUID\n\t\t}\n\n\t\t// Pre-4.4 servers report a namespace in their responses, so we only set Namespace manually if it was not in\n\t\t// the response.\n\t\tif specs[idx].IDIndex.Namespace == \"\" {\n\t\t\tspecs[idx].IDIndex.Namespace = db.name + \".\" + specs[idx].Name\n\t\t}\n\t}\n\n\treturn specs, nil\n}\n\n// ListCollections executes a listCollections command and returns a cursor over the collections in the database.\n//\n// The filter parameter must be a document containing query operators and can be used to select which collections\n// are included in the result. It cannot be nil. An empty document (e.g. bson.D{}) should be used to include all\n// collections.\n//\n// The opts parameter can be used to specify options for the operation (see the options.ListCollectionsOptions\n// documentation).\n//\n// For more information about the command, see https://www.mongodb.com/docs/manual/reference/command/listCollections/.\n//\n// BUG(benjirewis): ListCollections prevents listing more than 100 collections per database when running against\n// MongoDB version 2.6.\nfunc (db *Database) ListCollections(\n\tctx context.Context,\n\tfilter any,\n\topts ...options.Lister[options.ListCollectionsOptions],\n) (*Cursor, error) {\n\tif ctx == nil {\n\t\tctx = context.Background()\n\t}\n\n\targs, err := mongoutil.NewOptions[options.ListCollectionsOptions](opts...)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to construct options from builder: %w\", err)\n\t}\n\n\tfilterDoc, err := marshal(filter, db.bsonOpts, db.registry)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tsess := sessionFromContext(ctx)\n\tif sess == nil && db.client.sessionPool != nil {\n\t\tsess = session.NewImplicitClientSession(db.client.sessionPool, db.client.id)\n\t}\n\n\terr = db.client.validSession(sess)\n\tif err != nil {\n\t\tcloseImplicitSession(sess)\n\t\treturn nil, err\n\t}\n\n\tvar selector description.ServerSelector\n\n\tselector = &serverselector.Composite{\n\t\tSelectors: []description.ServerSelector{\n\t\t\t&serverselector.ReadPref{ReadPref: readpref.Primary()},\n\t\t\t&serverselector.Latency{Latency: db.client.localThreshold},\n\t\t},\n\t}\n\n\tselector = makeReadPrefSelector(sess, selector, db.client.localThreshold)\n\n\top := operation.NewListCollections(filterDoc).\n\t\tSession(sess).ReadPreference(db.readPreference).CommandMonitor(db.client.monitor).\n\t\tServerSelector(selector).ClusterClock(db.client.clock).\n\t\tDatabase(db.name).Deployment(db.client.deployment).Crypt(db.client.cryptFLE).\n\t\tServerAPI(db.client.serverAPI).Timeout(db.client.timeout).Authenticator(db.client.authenticator)\n\n\tcursorOpts := db.client.createBaseCursorOptions()\n\n\tcursorOpts.MarshalValueEncoderFn = newEncoderFn(db.bsonOpts, db.registry)\n\n\tif args.NameOnly != nil {\n\t\top = op.NameOnly(*args.NameOnly)\n\t}\n\tif args.BatchSize != nil {\n\t\tcursorOpts.BatchSize = *args.BatchSize\n\t\top = op.BatchSize(*args.BatchSize)\n\t}\n\tif args.AuthorizedCollections != nil {\n\t\top = op.AuthorizedCollections(*args.AuthorizedCollections)\n\t}\n\tif rawData, ok := optionsutil.Value(args.Internal, \"rawData\").(bool); ok {\n\t\top = op.RawData(rawData)\n\t}\n\n\tretry := driver.RetryNone\n\tif db.client.retryReads {\n\t\tretry = driver.RetryOncePerCommand\n\t}\n\top = op.Retry(retry)\n\n\terr = op.Execute(ctx)\n\tif err != nil {\n\t\tcloseImplicitSession(sess)\n\t\treturn nil, wrapErrors(err)\n\t}\n\n\tbc, err := op.Result(cursorOpts)\n\tif err != nil {\n\t\tcloseImplicitSession(sess)\n\t\treturn nil, wrapErrors(err)\n\t}\n\tcursor, err := newCursorWithSession(bc, db.bsonOpts, db.registry, sess,\n\t\twithCursorOptionClientTimeout(db.client.timeout))\n\treturn cursor, wrapErrors(err)\n}\n\n// ListCollectionNames executes a listCollections command and returns a slice containing the names of the collections\n// in the database. This method requires driver version >= 1.1.0.\n//\n// The filter parameter must be a document containing query operators and can be used to select which collections\n// are included in the result. It cannot be nil. An empty document (e.g. bson.D{}) should be used to include all\n// collections.\n//\n// The opts parameter can be used to specify options for the operation (see the options.ListCollectionsOptions\n// documentation).\n//\n// For more information about the command, see https://www.mongodb.com/docs/manual/reference/command/listCollections/.\n//\n// BUG(benjirewis): ListCollectionNames prevents listing more than 100 collections per database when running against\n// MongoDB version 2.6.\nfunc (db *Database) ListCollectionNames(\n\tctx context.Context,\n\tfilter any,\n\topts ...options.Lister[options.ListCollectionsOptions],\n) ([]string, error) {\n\topts = append(opts, options.ListCollections().SetNameOnly(true))\n\n\tres, err := db.ListCollections(ctx, filter, opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tdefer res.Close(ctx)\n\n\tnames := make([]string, 0)\n\tfor res.Next(ctx) {\n\t\telem, err := res.Current.LookupErr(\"name\")\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tif elem.Type != bson.TypeString {\n\t\t\treturn nil, fmt.Errorf(\"incorrect type for 'name'. got %v. want %v\", elem.Type, bson.TypeString)\n\t\t}\n\n\t\telemName := elem.StringValue()\n\t\tnames = append(names, elemName)\n\t}\n\n\tres.Close(ctx)\n\treturn names, nil\n}\n\n// Watch returns a change stream for all changes to the corresponding database. See\n// https://www.mongodb.com/docs/manual/changeStreams/ for more information about change streams.\n//\n// The Database must be configured with read concern majority or no read concern for a change stream to be created\n// successfully.\n//\n// The pipeline parameter must be a slice of documents, each representing a pipeline stage. The pipeline cannot be\n// nil but can be empty. The stage documents must all be non-nil. See https://www.mongodb.com/docs/manual/changeStreams/ for\n// a list of pipeline stages that can be used with change streams. For a pipeline of bson.D documents, the\n// mongo.Pipeline{} type can be used.\n//\n// The opts parameter can be used to specify options for change stream creation (see the options.ChangeStreamOptions\n// documentation).\nfunc (db *Database) Watch(ctx context.Context, pipeline any,\n\topts ...options.Lister[options.ChangeStreamOptions],\n) (*ChangeStream, error) {\n\tcsConfig := changeStreamConfig{\n\t\treadConcern:    db.readConcern,\n\t\treadPreference: db.readPreference,\n\t\tclient:         db.client,\n\t\tbsonOpts:       db.bsonOpts,\n\t\tregistry:       db.registry,\n\t\tstreamType:     DatabaseStream,\n\t\tdatabaseName:   db.Name(),\n\t\tcrypt:          db.client.cryptFLE,\n\t}\n\treturn newChangeStream(ctx, csConfig, pipeline, opts...)\n}\n\n// CreateCollection creates a new collection on the server with the specified\n// name and options.\n//\n// MongoDB versions < 7.0 will return an error if the collection already exists.\n// MongoDB versions >= 7.0 will not return an error if an existing collection\n// created with the same name and options already exists.\n//\n// For more information about the command, see\n// https://www.mongodb.com/docs/manual/reference/command/create/.\nfunc (db *Database) CreateCollection(ctx context.Context, name string, opts ...options.Lister[options.CreateCollectionOptions]) error {\n\targs, err := mongoutil.NewOptions(opts...)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to construct options from builder: %w\", err)\n\t}\n\n\t// Follow In-Use Encryption specification to check for encryptedFields.\n\t// Check for encryptedFields from create options.\n\tef := args.EncryptedFields\n\t// Check for encryptedFields from the client EncryptedFieldsMap.\n\tif ef == nil {\n\t\tef = db.getEncryptedFieldsFromMap(name)\n\t}\n\tif ef != nil {\n\t\treturn db.createCollectionWithEncryptedFields(ctx, name, ef, opts...)\n\t}\n\n\treturn db.createCollection(ctx, name, opts...)\n}\n\n// getEncryptedFieldsFromServer tries to get an \"encryptedFields\" document associated with collectionName by running the \"listCollections\" command.\n// Returns nil and no error if the listCollections command succeeds, but \"encryptedFields\" is not present.\nfunc (db *Database) getEncryptedFieldsFromServer(ctx context.Context, collectionName string) (any, error) {\n\t// Check if collection has an EncryptedFields configured server-side.\n\tcollSpecs, err := db.ListCollectionSpecifications(ctx, bson.D{{\"name\", collectionName}})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif len(collSpecs) == 0 {\n\t\treturn nil, nil\n\t}\n\tif len(collSpecs) > 1 {\n\t\treturn nil, fmt.Errorf(\"expected 1 or 0 results from listCollections, got %v\", len(collSpecs))\n\t}\n\tcollSpec := collSpecs[0]\n\trawValue, err := collSpec.Options.LookupErr(\"encryptedFields\")\n\tif errors.Is(err, bsoncore.ErrElementNotFound) {\n\t\treturn nil, nil\n\t} else if err != nil {\n\t\treturn nil, err\n\t}\n\n\tencryptedFields, ok := rawValue.DocumentOK()\n\tif !ok {\n\t\treturn nil, fmt.Errorf(\"expected encryptedFields of %v to be document, got %v\", collectionName, rawValue.Type)\n\t}\n\n\treturn encryptedFields, nil\n}\n\n// getEncryptedFieldsFromMap tries to get an \"encryptedFields\" document associated with collectionName by checking the client EncryptedFieldsMap.\n// Returns nil and no error if an EncryptedFieldsMap is not configured, or does not contain an entry for collectionName.\nfunc (db *Database) getEncryptedFieldsFromMap(collectionName string) any {\n\t// Check the EncryptedFieldsMap\n\tefMap := db.client.encryptedFieldsMap\n\tif efMap == nil {\n\t\treturn nil\n\t}\n\n\tnamespace := db.name + \".\" + collectionName\n\n\tef, ok := efMap[namespace]\n\tif ok {\n\t\treturn ef\n\t}\n\treturn nil\n}\n\n// createCollectionWithEncryptedFields creates a collection with an EncryptedFields.\nfunc (db *Database) createCollectionWithEncryptedFields(\n\tctx context.Context,\n\tname string,\n\tef any,\n\topts ...options.Lister[options.CreateCollectionOptions],\n) error {\n\tefBSON, err := marshal(ef, db.bsonOpts, db.registry)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"error transforming document: %w\", err)\n\t}\n\n\t// Check the wire version to ensure server is 7.0.0 or newer.\n\t// After the wire version check, and before creating the collections, it is possible the server state changes.\n\t// That is OK. This wire version check is a best effort to inform users earlier if using a QEv2 driver with a QEv1 server.\n\t{\n\t\tconst QEv2WireVersion = 21\n\t\tctx, cancel := csot.WithServerSelectionTimeout(ctx, db.client.deployment.GetServerSelectionTimeout())\n\t\tdefer cancel()\n\n\t\tserver, err := db.client.deployment.SelectServer(ctx, &serverselector.Write{})\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"error selecting server to check maxWireVersion: %w\", err)\n\t\t}\n\n\t\tconn, err := server.Connection(ctx)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"error getting connection to check maxWireVersion: %w\", err)\n\t\t}\n\t\tdefer conn.Close()\n\t\twireVersionRange := conn.Description().WireVersion\n\t\tif wireVersionRange.Max < QEv2WireVersion {\n\t\t\treturn fmt.Errorf(\"driver support of Queryable Encryption is incompatible with server. Upgrade server to use Queryable Encryption. Got maxWireVersion %v but need maxWireVersion >= %v\", wireVersionRange.Max, QEv2WireVersion)\n\t\t}\n\t}\n\n\t// Create the two encryption-related, associated collections: `escCollection` and `ecocCollection`.\n\n\tstateCollectionOpts := options.CreateCollection().\n\t\tSetClusteredIndex(bson.D{{\"key\", bson.D{{\"_id\", 1}}}, {\"unique\", true}})\n\t// Create ESCCollection.\n\tescCollection, err := csfle.GetEncryptedStateCollectionName(efBSON, name, csfle.EncryptedStateCollection)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif err := db.createCollection(ctx, escCollection, stateCollectionOpts); err != nil {\n\t\treturn err\n\t}\n\n\t// Create ECOCCollection.\n\tecocCollection, err := csfle.GetEncryptedStateCollectionName(efBSON, name, csfle.EncryptedCompactionCollection)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif err := db.createCollection(ctx, ecocCollection, stateCollectionOpts); err != nil {\n\t\treturn err\n\t}\n\n\t// Create a data collection with the 'encryptedFields' option.\n\top, err := db.createCollectionOperation(name, opts...)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\top.EncryptedFields(efBSON)\n\tif err := db.executeCreateOperation(ctx, op); err != nil {\n\t\treturn err\n\t}\n\n\t// Create an index on the __safeContent__ field in the collection @collectionName.\n\tif _, err := db.Collection(name).Indexes().CreateOne(ctx, IndexModel{Keys: bson.D{{\"__safeContent__\", 1}}}); err != nil {\n\t\treturn fmt.Errorf(\"error creating safeContent index: %w\", err)\n\t}\n\n\treturn nil\n}\n\n// createCollection creates a collection without EncryptedFields.\nfunc (db *Database) createCollection(\n\tctx context.Context,\n\tname string,\n\topts ...options.Lister[options.CreateCollectionOptions],\n) error {\n\top, err := db.createCollectionOperation(name, opts...)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn db.executeCreateOperation(ctx, op)\n}\n\nfunc (db *Database) createCollectionOperation(\n\tname string,\n\topts ...options.Lister[options.CreateCollectionOptions],\n) (*operation.Create, error) {\n\targs, err := mongoutil.NewOptions[options.CreateCollectionOptions](opts...)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to construct options from builder: %w\", err)\n\t}\n\n\top := operation.NewCreate(name).ServerAPI(db.client.serverAPI).Authenticator(db.client.authenticator)\n\n\tif args.Capped != nil {\n\t\top.Capped(*args.Capped)\n\t}\n\tif args.Collation != nil {\n\t\top.Collation(bsoncore.Document(toDocument(args.Collation)))\n\t}\n\tif args.ChangeStreamPreAndPostImages != nil {\n\t\tcsppi, err := marshal(args.ChangeStreamPreAndPostImages, db.bsonOpts, db.registry)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\top.ChangeStreamPreAndPostImages(csppi)\n\t}\n\tif args.DefaultIndexOptions != nil {\n\t\tdefaultIndexArgs, err := mongoutil.NewOptions[options.DefaultIndexOptions](args.DefaultIndexOptions)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"failed to construct DefaultIndexArgs from options: %w\", err)\n\t\t}\n\n\t\tidx, doc := bsoncore.AppendDocumentStart(nil)\n\t\tif defaultIndexArgs.StorageEngine != nil {\n\t\t\tstorageEngine, err := marshal(defaultIndexArgs.StorageEngine, db.bsonOpts, db.registry)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\n\t\t\tdoc = bsoncore.AppendDocumentElement(doc, \"storageEngine\", storageEngine)\n\t\t}\n\n\t\tdoc, err = bsoncore.AppendDocumentEnd(doc, idx)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\top.IndexOptionDefaults(doc)\n\t}\n\tif args.MaxDocuments != nil {\n\t\top.Max(*args.MaxDocuments)\n\t}\n\tif args.SizeInBytes != nil {\n\t\top.Size(*args.SizeInBytes)\n\t}\n\tif args.StorageEngine != nil {\n\t\tstorageEngine, err := marshal(args.StorageEngine, db.bsonOpts, db.registry)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\top.StorageEngine(storageEngine)\n\t}\n\tif args.ValidationAction != nil {\n\t\top.ValidationAction(*args.ValidationAction)\n\t}\n\tif args.ValidationLevel != nil {\n\t\top.ValidationLevel(*args.ValidationLevel)\n\t}\n\tif args.Validator != nil {\n\t\tvalidator, err := marshal(args.Validator, db.bsonOpts, db.registry)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\top.Validator(validator)\n\t}\n\tif args.ExpireAfterSeconds != nil {\n\t\top.ExpireAfterSeconds(*args.ExpireAfterSeconds)\n\t}\n\tif args.TimeSeriesOptions != nil {\n\t\ttimeSeriesArgs, err := mongoutil.NewOptions[options.TimeSeriesOptions](args.TimeSeriesOptions)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"failed to construct DefaultIndexArgs from options: %w\", err)\n\t\t}\n\n\t\tidx, doc := bsoncore.AppendDocumentStart(nil)\n\t\tdoc = bsoncore.AppendStringElement(doc, \"timeField\", timeSeriesArgs.TimeField)\n\n\t\tif timeSeriesArgs.MetaField != nil {\n\t\t\tdoc = bsoncore.AppendStringElement(doc, \"metaField\", *timeSeriesArgs.MetaField)\n\t\t}\n\t\tif timeSeriesArgs.Granularity != nil {\n\t\t\tdoc = bsoncore.AppendStringElement(doc, \"granularity\", *timeSeriesArgs.Granularity)\n\t\t}\n\n\t\tif timeSeriesArgs.BucketMaxSpan != nil {\n\t\t\tbmss := int64(*timeSeriesArgs.BucketMaxSpan / time.Second)\n\n\t\t\tdoc = bsoncore.AppendInt64Element(doc, \"bucketMaxSpanSeconds\", bmss)\n\t\t}\n\n\t\tif timeSeriesArgs.BucketRounding != nil {\n\t\t\tbrs := int64(*timeSeriesArgs.BucketRounding / time.Second)\n\n\t\t\tdoc = bsoncore.AppendInt64Element(doc, \"bucketRoundingSeconds\", brs)\n\t\t}\n\n\t\tdoc, err = bsoncore.AppendDocumentEnd(doc, idx)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\top.TimeSeries(doc)\n\t}\n\tif args.ClusteredIndex != nil {\n\t\tclusteredIndex, err := marshal(args.ClusteredIndex, db.bsonOpts, db.registry)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\top.ClusteredIndex(clusteredIndex)\n\t}\n\n\treturn op, nil\n}\n\n// CreateView creates a view on the server.\n//\n// The viewName parameter specifies the name of the view to create. The viewOn\n// parameter specifies the name of the collection or view on which this view\n// will be created. The pipeline parameter specifies an aggregation pipeline\n// that will be exececuted against the source collection or view to create this\n// view.\n//\n// MongoDB versions < 7.0 will return an error if the view already exists.\n// MongoDB versions >= 7.0 will not return an error if an existing view created\n// with the same name and options already exists.\n//\n// See https://www.mongodb.com/docs/manual/core/views/ for more information\n// about views.\nfunc (db *Database) CreateView(ctx context.Context, viewName, viewOn string, pipeline any,\n\topts ...options.Lister[options.CreateViewOptions],\n) error {\n\tpipelineArray, _, err := marshalAggregatePipeline(pipeline, db.bsonOpts, db.registry)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\top := operation.NewCreate(viewName).\n\t\tViewOn(viewOn).\n\t\tPipeline(pipelineArray).\n\t\tServerAPI(db.client.serverAPI).Authenticator(db.client.authenticator)\n\targs, err := mongoutil.NewOptions(opts...)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to construct options from builder: %w\", err)\n\t}\n\n\tif args.Collation != nil {\n\t\top.Collation(bsoncore.Document(toDocument(args.Collation)))\n\t}\n\n\treturn db.executeCreateOperation(ctx, op)\n}\n\nfunc (db *Database) executeCreateOperation(ctx context.Context, op *operation.Create) error {\n\tsess := sessionFromContext(ctx)\n\tif sess == nil && db.client.sessionPool != nil {\n\t\tsess = session.NewImplicitClientSession(db.client.sessionPool, db.client.id)\n\t\tdefer sess.EndSession()\n\t}\n\n\terr := db.client.validSession(sess)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\twc := db.writeConcern\n\tif sess.TransactionRunning() {\n\t\twc = nil\n\t}\n\tif !wc.Acknowledged() {\n\t\tsess = nil\n\t}\n\n\tselector := makePinnedSelector(sess, db.writeSelector)\n\top = op.Session(sess).\n\t\tWriteConcern(wc).\n\t\tCommandMonitor(db.client.monitor).\n\t\tServerSelector(selector).\n\t\tClusterClock(db.client.clock).\n\t\tDatabase(db.name).\n\t\tDeployment(db.client.deployment).\n\t\tCrypt(db.client.cryptFLE)\n\n\treturn wrapErrors(op.Execute(ctx))\n}\n\n// GridFSBucket is used to construct a GridFS bucket which can be used as a\n// container for files.\nfunc (db *Database) GridFSBucket(opts ...options.Lister[options.BucketOptions]) *GridFSBucket {\n\tb := &GridFSBucket{\n\t\tname:      \"fs\",\n\t\tchunkSize: DefaultGridFSChunkSize,\n\t\tdb:        db,\n\t}\n\n\tbo, _ := mongoutil.NewOptions[options.BucketOptions](opts...)\n\tif bo.Name != nil {\n\t\tb.name = *bo.Name\n\t}\n\tif bo.ChunkSizeBytes != nil {\n\t\tb.chunkSize = *bo.ChunkSizeBytes\n\t}\n\tif bo.WriteConcern != nil {\n\t\tb.wc = bo.WriteConcern\n\t}\n\tif bo.ReadConcern != nil {\n\t\tb.rc = bo.ReadConcern\n\t}\n\tif bo.ReadPreference != nil {\n\t\tb.rp = bo.ReadPreference\n\t}\n\n\tcollOpts := options.Collection().SetWriteConcern(b.wc).SetReadConcern(b.rc).SetReadPreference(b.rp)\n\n\tb.chunksColl = db.Collection(b.name+\".chunks\", collOpts)\n\tb.filesColl = db.Collection(b.name+\".files\", collOpts)\n\tb.readBuf = make([]byte, b.chunkSize)\n\tb.writeBuf = make([]byte, b.chunkSize)\n\n\treturn b\n}\n"
  },
  {
    "path": "mongo/database_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage mongo\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"testing\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/require\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/readconcern\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/readpref\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/writeconcern\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/topology\"\n)\n\nfunc setupDB(name string, opts ...options.Lister[options.DatabaseOptions]) *Database {\n\tclient := setupClient()\n\treturn client.Database(name, opts...)\n}\n\nfunc compareDbs(t *testing.T, expected, got *Database) {\n\tt.Helper()\n\tassert.Equal(t, expected.readPreference, got.readPreference,\n\t\t\"expected read preference %v, got %v\", expected.readPreference, got.readPreference)\n\tassert.Equal(t, expected.readConcern, got.readConcern,\n\t\t\"expected read concern %v, got %v\", expected.readConcern, got.readConcern)\n\tassert.Equal(t, expected.writeConcern, got.writeConcern,\n\t\t\"expected write concern %v, got %v\", expected.writeConcern, got.writeConcern)\n\tassert.Equal(t, expected.registry, got.registry,\n\t\t\"expected write concern %v, got %v\", expected.registry, got.registry)\n}\n\nfunc TestDatabase(t *testing.T) {\n\tt.Run(\"initialize\", func(t *testing.T) {\n\t\tname := \"foo\"\n\t\tdb := setupDB(name)\n\t\tassert.Equal(t, name, db.Name(), \"expected db name %v, got %v\", name, db.Name())\n\t\tassert.NotNil(t, db.Client(), \"expected valid client, got nil\")\n\t})\n\tt.Run(\"options\", func(t *testing.T) {\n\t\tt.Run(\"custom\", func(t *testing.T) {\n\t\t\trpPrimary := readpref.Primary()\n\t\t\trpSecondary := readpref.Secondary()\n\t\t\twc1 := &writeconcern.WriteConcern{W: 5}\n\t\t\twc2 := &writeconcern.WriteConcern{W: 10}\n\t\t\trcLocal := readconcern.Local()\n\t\t\trcMajority := readconcern.Majority()\n\t\t\treg := bson.NewRegistry()\n\n\t\t\topts := options.Database().SetReadPreference(rpPrimary).SetReadConcern(rcLocal).SetWriteConcern(wc1).\n\t\t\t\tSetReadPreference(rpSecondary).SetReadConcern(rcMajority).SetWriteConcern(wc2).SetRegistry(reg)\n\t\t\texpected := &Database{\n\t\t\t\treadPreference: rpSecondary,\n\t\t\t\treadConcern:    rcMajority,\n\t\t\t\twriteConcern:   wc2,\n\t\t\t\tregistry:       reg,\n\t\t\t}\n\t\t\tgot := setupDB(\"foo\", opts)\n\t\t\tcompareDbs(t, expected, got)\n\t\t})\n\t\tt.Run(\"inherit\", func(t *testing.T) {\n\t\t\trpPrimary := readpref.Primary()\n\t\t\trcLocal := readconcern.Local()\n\t\t\twc1 := &writeconcern.WriteConcern{W: 10}\n\t\t\treg := bson.NewRegistry()\n\n\t\t\tclient := setupClient(options.Client().SetReadPreference(rpPrimary).SetReadConcern(rcLocal).SetRegistry(reg))\n\t\t\tgot := client.Database(\"foo\", options.Database().SetWriteConcern(wc1))\n\t\t\texpected := &Database{\n\t\t\t\treadPreference: rpPrimary,\n\t\t\t\treadConcern:    rcLocal,\n\t\t\t\twriteConcern:   wc1,\n\t\t\t\tregistry:       reg,\n\t\t\t}\n\t\t\tcompareDbs(t, expected, got)\n\t\t})\n\t})\n\tt.Run(\"replaceErrors for disconnected topology\", func(t *testing.T) {\n\t\tdb := setupDB(\"foo\")\n\n\t\ttopo, ok := db.client.deployment.(*topology.Topology)\n\t\trequire.True(t, ok, \"client deployment is not a topology\")\n\n\t\terr := topo.Disconnect(context.Background())\n\t\trequire.NoError(t, err)\n\n\t\terr = db.RunCommand(bgCtx, bson.D{{\"x\", 1}}).Err()\n\t\tassert.Equal(t, ErrClientDisconnected, err, \"expected error %v, got %v\", ErrClientDisconnected, err)\n\n\t\terr = db.Drop(bgCtx)\n\t\tassert.Equal(t, ErrClientDisconnected, err, \"expected error %v, got %v\", ErrClientDisconnected, err)\n\n\t\t_, err = db.ListCollections(bgCtx, bson.D{})\n\t\tassert.Equal(t, ErrClientDisconnected, err, \"expected error %v, got %v\", ErrClientDisconnected, err)\n\t})\n\tt.Run(\"TransientTransactionError label\", func(t *testing.T) {\n\t\tclient := setupClient(options.Client().ApplyURI(\"mongodb://nonexistent\").SetServerSelectionTimeout(3 * time.Second))\n\t\tdefer func() { _ = client.Disconnect(bgCtx) }()\n\n\t\tt.Run(\"negative case of non-transaction\", func(t *testing.T) {\n\t\t\tvar sse topology.ServerSelectionError\n\t\t\tvar le LabeledError\n\n\t\t\terr := client.Ping(bgCtx, nil)\n\t\t\tassert.NotNil(t, err, \"expected error, got nil\")\n\t\t\tassert.True(t, errors.As(err, &sse), `expected error to be a \"topology.ServerSelectionError\"`)\n\t\t\tif errors.As(err, &le) {\n\t\t\t\tassert.False(t, le.HasErrorLabel(\"TransientTransactionError\"), `expected error not to include the \"TransientTransactionError\" label`)\n\t\t\t}\n\t\t})\n\n\t\tt.Run(\"positive case of transaction\", func(t *testing.T) {\n\t\t\tvar sse topology.ServerSelectionError\n\t\t\tvar le LabeledError\n\n\t\t\tsess, err := client.StartSession()\n\t\t\tassert.Nil(t, err, \"expected nil, got %v\", err)\n\t\t\tdefer sess.EndSession(bgCtx)\n\n\t\t\tsessCtx := NewSessionContext(bgCtx, sess)\n\t\t\terr = sess.StartTransaction()\n\t\t\tassert.Nil(t, err, \"expected nil, got %v\", err)\n\n\t\t\terr = client.Ping(sessCtx, nil)\n\t\t\tassert.NotNil(t, err, \"expected error, got nil\")\n\t\t\tassert.True(t, errors.As(err, &sse), `expected error to be a \"topology.ServerSelectionError\"`)\n\t\t\tassert.True(t, errors.As(err, &le), `expected error to implement the \"LabeledError\" interface`)\n\t\t\tassert.True(t, le.HasErrorLabel(\"TransientTransactionError\"), `expected error to include the \"TransientTransactionError\" label`)\n\t\t})\n\t})\n\tt.Run(\"nil document error\", func(t *testing.T) {\n\t\tdb := setupDB(\"foo\")\n\n\t\terr := db.RunCommand(bgCtx, nil).Err()\n\t\tassert.True(t, errors.Is(err, ErrNilDocument), \"expected error %v, got %v\", ErrNilDocument, err)\n\n\t\t_, err = db.Watch(context.Background(), nil)\n\t\twatchErr := errors.New(\"can only marshal slices and arrays into aggregation pipelines, but got invalid\")\n\t\tassert.Equal(t, watchErr, err, \"expected error %v, got %v\", watchErr, err)\n\n\t\t_, err = db.ListCollections(context.Background(), nil)\n\t\tassert.True(t, errors.Is(err, ErrNilDocument), \"expected error %v, got %v\", ErrNilDocument, err)\n\n\t\t_, err = db.ListCollectionNames(context.Background(), nil)\n\t\tassert.True(t, errors.Is(err, ErrNilDocument), \"expected error %v, got %v\", ErrNilDocument, err)\n\t})\n}\n"
  },
  {
    "path": "mongo/doc.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\n// NOTE: This documentation should be kept in line with the Example* test functions.\n\n// Package mongo provides a MongoDB Driver API for Go.\n//\n// Basic usage of the driver starts with creating a Client from a connection\n// string. To do so, call Connect:\n//\n//\tctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)\n//\tdefer cancel()\n//\tclient, err := mongo.Connect( options.Client().ApplyURI(\"mongodb://foo:bar@localhost:27017\"))\n//\tif err != nil { return err }\n//\n// This will create a new client and start monitoring the MongoDB server on localhost.\n// The Database and Collection types can be used to access the database:\n//\n//\tcollection := client.Database(\"baz\").Collection(\"qux\")\n//\n// A Collection can be used to query the database or insert documents:\n//\n//\tres, err := collection.InsertOne(context.Background(), bson.M{\"hello\": \"world\"})\n//\tif err != nil { return err }\n//\tid := res.InsertedID\n//\n// Several methods return a cursor, which can be used like this:\n//\n//\tcur, err := collection.Find(context.Background(), bson.D{})\n//\tif err != nil { log.Fatal(err) }\n//\tdefer cur.Close(context.Background())\n//\tfor cur.Next(context.Background()) {\n//\t  // To decode into a struct, use cursor.Decode()\n//\t  result := struct{\n//\t    Foo string\n//\t    Bar int32\n//\t  }{}\n//\t  err := cur.Decode(&result)\n//\t  if err != nil { log.Fatal(err) }\n//\t  // do something with result...\n//\n//\t  // To get the raw bson bytes use cursor.Current\n//\t  raw := cur.Current\n//\t  // do something with raw...\n//\t}\n//\tif err := cur.Err(); err != nil {\n//\t  return err\n//\t}\n//\n// Cursor.All will decode all of the returned elements at once:\n//\n//\tvar results []struct{\n//\t  Foo string\n//\t  Bar int32\n//\t}\n//\tif err = cur.All(context.Background(), &results); err != nil {\n//\t  log.Fatal(err)\n//\t}\n//\t// do something with results...\n//\n// Methods that only return a single document will return a *SingleResult, which works\n// like a *sql.Row:\n//\n//\tresult := struct{\n//\t  Foo string\n//\t  Bar int32\n//\t}{}\n//\tfilter := bson.D{{\"hello\", \"world\"}}\n//\terr := collection.FindOne(context.Background(), filter).Decode(&result)\n//\tif err != nil { return err }\n//\t// do something with result...\n//\n// All Client, Collection, and Database methods that take parameters of type any\n// will return ErrNilDocument if nil is passed in for an any.\n//\n// Additional examples can be found under the examples directory in the driver's repository and\n// on the MongoDB website.\n//\n// # Error Handling\n//\n// Errors from the MongoDB server will implement the ServerError interface, which has functions to check for specific\n// error codes, labels, and message substrings. These can be used to check for and handle specific errors. Some methods,\n// like InsertMany and BulkWrite, can return an error representing multiple errors, and in those cases the ServerError\n// functions will return true if any of the contained errors satisfy the check.\n//\n// There are also helper functions to check for certain specific types of errors:\n//\n//\tIsDuplicateKeyError(error)\n//\tIsNetworkError(error)\n//\tIsTimeout(error)\n//\n// # Potential DNS Issues\n//\n// Building with Go 1.11+ and using connection strings with the \"mongodb+srv\"[1] scheme is unfortunately\n// incompatible with some DNS servers in the wild due to the change introduced in\n// https://github.com/golang/go/issues/10622. You may receive an error with the message \"cannot unmarshal DNS message\"\n// while running an operation when using DNS servers that non-compliantly compress SRV records. Old versions of kube-dns\n// and the native DNS resolver (systemd-resolver) on Ubuntu 18.04 are known to be non-compliant in this manner. We suggest\n// using a different DNS server (8.8.8.8 is the common default), and, if that's not possible, avoiding the \"mongodb+srv\"\n// scheme.\n//\n// # In-Use Encryption\n//\n// MongoDB provides two approaches to In-Use Encryption: Queryable Encryption (QE) and Client-Side Field Level Encryption (CSFLE).\n//\n// The Queryable Encryption and CSFLE features share much of the same API with some exceptions.\n//\n// - AutoEncryptionOptions.SetEncryptedFieldsMap only applies to Queryable Encryption.\n// - AutoEncryptionOptions.SetSchemaMap only applies to CSFLE.\n//\n// In-use encryption is a new feature in MongoDB 4.2 that allows specific data fields to be encrypted. Using this\n// feature requires specifying the \"cse\" build tag during compilation:\n//\n//\tgo build -tags cse\n//\n// Note: Auto encryption is an enterprise- and Atlas-only feature.\n//\n// The libmongocrypt C library is required when using in-use encryption. Specific versions of libmongocrypt\n// are required for different versions of the Go Driver:\n//\n// - Go Driver v1.2.0 requires libmongocrypt v1.0.0 or higher\n//\n// - Go Driver v1.5.0 requires libmongocrypt v1.1.0 or higher\n//\n// - Go Driver v1.8.0 requires libmongocrypt v1.3.0 or higher\n//\n// - Go Driver v1.10.0 requires libmongocrypt v1.5.0 or higher.\n// There is a severe bug when calling RewrapManyDataKey with libmongocrypt versions less than 1.5.2.\n// This bug may result in data corruption.\n// Please use libmongocrypt 1.5.2 or higher when calling RewrapManyDataKey.\n//\n// - Go Driver v1.12.0 requires libmongocrypt v1.8.0 or higher.\n//\n// To install libmongocrypt, follow the instructions for your\n// operating system:\n//\n// 1. Linux: follow the instructions listed at\n// https://github.com/mongodb/libmongocrypt#installing-libmongocrypt-from-distribution-packages to install the correct\n// deb/rpm package.\n//\n// 2. Mac: Follow the instructions listed at https://github.com/mongodb/libmongocrypt#installing-libmongocrypt-on-macos\n// to install packages via brew and compile the libmongocrypt source code.\n//\n// 3. Windows:\n//\n//\tmkdir -p c:/libmongocrypt/bin\n//\tmkdir -p c:/libmongocrypt/include\n//\n//\t// Run the curl command in an empty directory as it will create new directories when unpacked.\n//\tcurl https://s3.amazonaws.com/mciuploads/libmongocrypt/windows/latest_release/libmongocrypt.tar.gz --output libmongocrypt.tar.gz\n//\ttar -xvzf libmongocrypt.tar.gz\n//\n//\tcp ./bin/mongocrypt.dll c:/libmongocrypt/bin\n//\tcp ./include/mongocrypt/*.h c:/libmongocrypt/include\n//\texport PATH=$PATH:/cygdrive/c/libmongocrypt/bin\n//\n// libmongocrypt communicates with the mongocryptd process or mongo_crypt shared library for automatic encryption.\n// See AutoEncryptionOpts.SetExtraOptions for options to configure use of mongocryptd or mongo_crypt.\n//\n// [1] See https://www.mongodb.com/docs/manual/reference/connection-string/#dns-seedlist-connection-format\npackage mongo\n"
  },
  {
    "path": "mongo/errors.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage mongo\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"net\"\n\t\"reflect\"\n\t\"strings\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/codecutil\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/mongocrypt\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/topology\"\n)\n\n// ErrClientDisconnected is returned when disconnected Client is used to run an operation.\nvar ErrClientDisconnected = errors.New(\"client is disconnected\")\n\n// InvalidArgumentError wraps an invalid argument error.\ntype InvalidArgumentError struct {\n\twrapped error\n}\n\n// Error implements the error interface.\nfunc (e InvalidArgumentError) Error() string {\n\treturn e.wrapped.Error()\n}\n\n// Unwrap returns the underlying error.\nfunc (e InvalidArgumentError) Unwrap() error {\n\treturn e.wrapped\n}\n\n// ErrMultipleIndexDrop is returned if multiple indexes would be dropped from a call to IndexView.DropOne.\nvar ErrMultipleIndexDrop error = InvalidArgumentError{errors.New(\"multiple indexes would be dropped\")}\n\n// ErrNilDocument is returned when a nil document is passed to a CRUD method.\nvar ErrNilDocument error = InvalidArgumentError{errors.New(\"document is nil\")}\n\n// ErrNilValue is returned when a nil value is passed to a CRUD method.\nvar ErrNilValue error = InvalidArgumentError{errors.New(\"value is nil\")}\n\n// ErrEmptySlice is returned when an empty slice is passed to a CRUD method that requires a non-empty slice.\nvar ErrEmptySlice error = InvalidArgumentError{errors.New(\"must provide at least one element in input slice\")}\n\n// ErrNotSlice is returned when a type other than slice is passed to InsertMany.\nvar ErrNotSlice error = InvalidArgumentError{errors.New(\"must provide a non-empty slice\")}\n\n// ErrMapForOrderedArgument is returned when a map with multiple keys is passed to a CRUD method for an ordered parameter\ntype ErrMapForOrderedArgument struct {\n\tParamName string\n}\n\n// Error implements the error interface.\nfunc (e ErrMapForOrderedArgument) Error() string {\n\treturn fmt.Sprintf(\"multi-key map passed in for ordered parameter %v\", e.ParamName)\n}\n\n// wrapErrors wraps error types and values that are defined in \"internal\" and\n// \"x\" packages with error types and values that are defined in this package.\n// That allows users to inspect the errors using errors.Is/errors.As without\n// relying on \"internal\" or \"x\" packages.\nfunc wrapErrors(err error) error {\n\t// Return nil when err is nil to avoid costly reflection logic below.\n\tif err == nil {\n\t\treturn nil\n\t}\n\n\t// Do not propagate the acknowledgement sentinel error. For DDL commands,\n\t// (creating indexes, dropping collections, etc) acknowledgement should be\n\t// ignored. For non-DDL write commands (insert, update, etc), acknowledgement\n\t// should be be propagated at the result-level: e.g.,\n\t// SingleResult.Acknowledged.\n\tif errors.Is(err, driver.ErrUnacknowledgedWrite) {\n\t\treturn nil\n\t}\n\tif errors.Is(err, topology.ErrTopologyClosed) {\n\t\treturn ErrClientDisconnected\n\t}\n\n\tvar de driver.Error\n\tif errors.As(err, &de) {\n\t\treturn CommandError{\n\t\t\tCode:    de.Code,\n\t\t\tMessage: de.Message,\n\t\t\tLabels:  de.Labels,\n\t\t\tName:    de.Name,\n\t\t\tWrapped: err,\n\t\t\tRaw:     bson.Raw(de.Raw),\n\n\t\t\t// Set wrappedMsgOnly=true here so that the Code and Message are not\n\t\t\t// repeated multiple times in the error string. We expect that the\n\t\t\t// wrapped driver.Error already contains that info in the error\n\t\t\t// string.\n\t\t\twrappedMsgOnly: true,\n\t\t}\n\t}\n\n\tvar qe driver.QueryFailureError\n\tif errors.As(err, &qe) {\n\t\t// qe.Message is \"command failure\"\n\t\tce := CommandError{\n\t\t\tName:    qe.Message,\n\t\t\tWrapped: err,\n\t\t\tRaw:     bson.Raw(qe.Response),\n\n\t\t\t// Don't set wrappedMsgOnly=true here because the code below adds\n\t\t\t// additional error context that is not provided by the\n\t\t\t// driver.QueryFailureError. Additionally, driver.QueryFailureError\n\t\t\t// is only returned when parsing OP_QUERY replies (OP_REPLY), so\n\t\t\t// it's unlikely this block will ever be run now that MongoDB 3.6 is\n\t\t\t// no longer supported.\n\t\t}\n\n\t\tdollarErr, err := qe.Response.LookupErr(\"$err\")\n\t\tif err == nil {\n\t\t\tce.Message, _ = dollarErr.StringValueOK()\n\t\t}\n\t\tcode, err := qe.Response.LookupErr(\"code\")\n\t\tif err == nil {\n\t\t\tce.Code, _ = code.Int32OK()\n\t\t}\n\n\t\treturn ce\n\t}\n\n\tvar me mongocrypt.Error\n\tif errors.As(err, &me) {\n\t\treturn MongocryptError{\n\t\t\tCode:    me.Code,\n\t\t\tMessage: me.Message,\n\t\t\twrapped: err,\n\n\t\t\t// Set wrappedMsgOnly=true here so that the Code and Message are not\n\t\t\t// repeated multiple times in the error string. We expect that the\n\t\t\t// wrapped mongocrypt.Error already contains that info in the error\n\t\t\t// string.\n\t\t\twrappedMsgOnly: true,\n\t\t}\n\t}\n\n\tif errors.Is(err, codecutil.ErrNilValue) {\n\t\treturn ErrNilValue\n\t}\n\n\tvar marshalErr codecutil.MarshalError\n\tif errors.As(err, &marshalErr) {\n\t\treturn MarshalError{\n\t\t\tValue: marshalErr.Value,\n\t\t\tErr:   err,\n\n\t\t\t// Set wrappedMsgOnly=true here so that the Value is not repeated\n\t\t\t// multiple times in the error string. We expect that the wrapped\n\t\t\t// codecutil.MarshalError already contains that info in the error\n\t\t\t// string.\n\t\t\twrappedMsgOnly: true,\n\t\t}\n\t}\n\n\treturn err\n}\n\n// IsDuplicateKeyError returns true if err is a duplicate key error. For BulkWriteExceptions,\n// IsDuplicateKeyError returns true if at least one of the errors is a duplicate key error.\nfunc IsDuplicateKeyError(err error) bool {\n\tif se := ServerError(nil); errors.As(err, &se) {\n\t\treturn se.HasErrorCode(11000) || // Duplicate key error.\n\t\t\tse.HasErrorCode(11001) || // Duplicate key error on update.\n\t\t\t// Duplicate key error in a capped collection. See SERVER-7164.\n\t\t\tse.HasErrorCode(12582) ||\n\t\t\t// Mongos insert error caused by a duplicate key error. See\n\t\t\t// SERVER-11493.\n\t\t\tse.HasErrorCodeWithMessage(16460, \" E11000 \")\n\t}\n\treturn false\n}\n\n// timeoutErrs is a list of error values that indicate a timeout happened.\nvar timeoutErrs = [...]error{\n\tcontext.DeadlineExceeded,\n\tdriver.ErrDeadlineWouldBeExceeded,\n}\n\n// IsTimeout returns true if err was caused by a timeout. For error chains,\n// IsTimeout returns true if any error in the chain was caused by a timeout.\nfunc IsTimeout(err error) bool {\n\t// Check if the error chain contains any of the timeout error values.\n\tfor _, target := range timeoutErrs {\n\t\tif errors.Is(err, target) {\n\t\t\treturn true\n\t\t}\n\t}\n\n\t// Check if the error chain contains any error types that can indicate\n\t// timeout.\n\tif errors.As(err, &topology.WaitQueueTimeoutError{}) {\n\t\treturn true\n\t}\n\tif ce := (CommandError{}); errors.As(err, &ce) && ce.IsMaxTimeMSExpiredError() {\n\t\treturn true\n\t}\n\tif we := (WriteException{}); errors.As(err, &we) && we.WriteConcernError != nil && we.WriteConcernError.IsMaxTimeMSExpiredError() {\n\t\treturn true\n\t}\n\tif ne := net.Error(nil); errors.As(err, &ne) {\n\t\treturn ne.Timeout()\n\t}\n\t// Check timeout error labels.\n\tif le := LabeledError(nil); errors.As(err, &le) {\n\t\tif le.HasErrorLabel(\"NetworkTimeoutError\") || le.HasErrorLabel(\"ExceededTimeLimitError\") {\n\t\t\treturn true\n\t\t}\n\t}\n\n\treturn false\n}\n\n// errorHasLabel returns true if err contains the specified label\nfunc errorHasLabel(err error, label string) bool {\n\tvar le LabeledError\n\treturn errors.As(err, &le) && le.HasErrorLabel(label)\n}\n\n// IsNetworkError returns true if err is a network error\nfunc IsNetworkError(err error) bool {\n\treturn errorHasLabel(err, \"NetworkError\")\n}\n\n// MarshalError is returned when attempting to marshal a value into a document\n// results in an error.\ntype MarshalError struct {\n\tValue any\n\tErr   error\n\n\t// If wrappedMsgOnly is true, Error() only returns the error message from\n\t// the \"Err\" error.\n\t//\n\t// This is typically only set by the wrapErrors function, which uses\n\t// MarshalError to wrap codecutil.MarshalError, allowing users to access the\n\t// \"Value\" from the underlying error but preventing duplication in the error\n\t// string.\n\twrappedMsgOnly bool\n}\n\n// Error implements the error interface.\nfunc (me MarshalError) Error() string {\n\t// If the MarshalError was created with wrappedMsgOnly=true, only return the\n\t// error from the wrapped error. See the MarshalError.wrappedMsgOnly docs\n\t// for more info.\n\tif me.wrappedMsgOnly {\n\t\treturn me.Err.Error()\n\t}\n\n\treturn fmt.Sprintf(\"cannot marshal type %s to a BSON Document: %v\", reflect.TypeOf(me.Value), me.Err)\n}\n\nfunc (me MarshalError) Unwrap() error { return me.Err }\n\n// MongocryptError represents an libmongocrypt error during in-use encryption.\ntype MongocryptError struct {\n\tCode    int32\n\tMessage string\n\twrapped error\n\n\t// If wrappedMsgOnly is true, Error() only returns the error message from\n\t// the \"wrapped\" error.\n\t//\n\t// This is typically only set by the wrapErrors function, which uses\n\t// MarshalError to wrap mongocrypt.Error, allowing users to access the\n\t// \"Code\" and \"Message\" from the underlying error but preventing duplication\n\t// in the error string.\n\twrappedMsgOnly bool\n}\n\n// Error implements the error interface.\nfunc (m MongocryptError) Error() string {\n\t// If the MongocryptError was created with wrappedMsgOnly=true, only return\n\t// the error from the wrapped error. See the MongocryptError.wrappedMsgOnly\n\t// docs for more info.\n\tif m.wrappedMsgOnly {\n\t\treturn m.wrapped.Error()\n\t}\n\n\treturn fmt.Sprintf(\"mongocrypt error %d: %v\", m.Code, m.Message)\n}\n\n// Unwrap returns the underlying error.\nfunc (m MongocryptError) Unwrap() error { return m.wrapped }\n\n// EncryptionKeyVaultError represents an error while communicating with the key vault collection during in-use\n// encryption.\ntype EncryptionKeyVaultError struct {\n\tWrapped error\n}\n\n// Error implements the error interface.\nfunc (ekve EncryptionKeyVaultError) Error() string {\n\treturn fmt.Sprintf(\"key vault communication error: %v\", ekve.Wrapped)\n}\n\n// Unwrap returns the underlying error.\nfunc (ekve EncryptionKeyVaultError) Unwrap() error {\n\treturn ekve.Wrapped\n}\n\n// MongocryptdError represents an error while communicating with mongocryptd during in-use encryption.\ntype MongocryptdError struct {\n\tWrapped error\n}\n\n// Error implements the error interface.\nfunc (e MongocryptdError) Error() string {\n\treturn fmt.Sprintf(\"mongocryptd communication error: %v\", e.Wrapped)\n}\n\n// Unwrap returns the underlying error.\nfunc (e MongocryptdError) Unwrap() error {\n\treturn e.Wrapped\n}\n\n// LabeledError is an interface for errors with labels.\ntype LabeledError interface {\n\terror\n\t// HasErrorLabel returns true if the error contains the specified label.\n\tHasErrorLabel(string) bool\n}\n\ntype errorCoder interface {\n\tErrorCodes() []int\n}\n\nvar _ errorCoder = ServerError(nil)\n\n// ServerError is the interface implemented by errors returned from the server. Custom implementations of this\n// interface should not be used in production.\ntype ServerError interface {\n\tLabeledError\n\t// HasErrorCode returns true if the error has the specified code.\n\tHasErrorCode(int) bool\n\t// HasErrorMessage returns true if the error contains the specified message.\n\tHasErrorMessage(string) bool\n\t// HasErrorCodeWithMessage returns true if any of the contained errors have the specified code and message.\n\tHasErrorCodeWithMessage(int, string) bool\n\n\t// ErrorCodes returns all error codes (unsorted) in the server’s response.\n\t// This would include nested errors (e.g., write concern errors) for\n\t// supporting implementations (e.g., BulkWriteException) as well as the\n\t// top-level error code.\n\tErrorCodes() []int\n\n\tserverError()\n}\n\nfunc hasErrorCode(srvErr ServerError, code int) bool {\n\tfor _, srvErrCode := range srvErr.ErrorCodes() {\n\t\tif code == srvErrCode {\n\t\t\treturn true\n\t\t}\n\t}\n\n\treturn false\n}\n\nvar (\n\t_ ServerError = CommandError{}\n\t_ ServerError = WriteError{}\n\t_ ServerError = WriteException{}\n\t_ ServerError = BulkWriteException{}\n)\n\nvar _ error = ClientBulkWriteException{}\n\n// CommandError represents a server error during execution of a command. This can be returned by any operation.\ntype CommandError struct {\n\tCode    int32\n\tMessage string\n\tLabels  []string // Categories to which the error belongs\n\tName    string   // A human-readable name corresponding to the error code\n\tWrapped error    // The underlying error, if one exists.\n\tRaw     bson.Raw // The original server response containing the error.\n\n\t// If wrappedMsgOnly is true, Error() only returns the error message from\n\t// the \"Wrapped\" error.\n\t//\n\t// This is typically only set by the wrapErrors function, which uses\n\t// CommandError to wrap driver.Error, allowing users to access the \"Code\",\n\t// \"Message\", \"Labels\", \"Name\", and \"Raw\" from the underlying error but\n\t// preventing duplication in the error string.\n\twrappedMsgOnly bool\n}\n\n// Error implements the error interface.\nfunc (e CommandError) Error() string {\n\t// If the CommandError was created with wrappedMsgOnly=true, only return the\n\t// error from the wrapped error. See the CommandError.wrappedMsgOnly docs\n\t// for more info.\n\tif e.wrappedMsgOnly {\n\t\treturn e.Wrapped.Error()\n\t}\n\n\tvar msg string\n\tif e.Name != \"\" {\n\t\tmsg += fmt.Sprintf(\"(%v)\", e.Name)\n\t}\n\tif e.Message != \"\" {\n\t\tmsg += \" \" + e.Message\n\t}\n\tif e.Wrapped != nil {\n\t\tmsg += \": \" + e.Wrapped.Error()\n\t}\n\n\treturn msg\n}\n\n// Unwrap returns the underlying error.\nfunc (e CommandError) Unwrap() error {\n\treturn e.Wrapped\n}\n\n// HasErrorCode returns true if the error has the specified code.\nfunc (e CommandError) HasErrorCode(code int) bool {\n\treturn int(e.Code) == code\n}\n\n// ErrorCodes returns a list of error codes returned by the server.\nfunc (e CommandError) ErrorCodes() []int {\n\treturn []int{int(e.Code)}\n}\n\n// HasErrorLabel returns true if the error contains the specified label.\nfunc (e CommandError) HasErrorLabel(label string) bool {\n\tfor _, l := range e.Labels {\n\t\tif l == label {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\n// HasErrorMessage returns true if the error contains the specified message.\nfunc (e CommandError) HasErrorMessage(message string) bool {\n\treturn strings.Contains(e.Message, message)\n}\n\n// HasErrorCodeWithMessage returns true if the error has the specified code and Message contains the specified message.\nfunc (e CommandError) HasErrorCodeWithMessage(code int, message string) bool {\n\treturn int(e.Code) == code && strings.Contains(e.Message, message)\n}\n\n// IsMaxTimeMSExpiredError returns true if the error is a MaxTimeMSExpired error.\nfunc (e CommandError) IsMaxTimeMSExpiredError() bool {\n\treturn e.Code == 50 || e.Name == \"MaxTimeMSExpired\"\n}\n\n// serverError implements the ServerError interface.\nfunc (e CommandError) serverError() {}\n\n// WriteError is an error that occurred during execution of a write operation. This error type is only returned as part\n// of a WriteException or BulkWriteException.\ntype WriteError struct {\n\t// The index of the write in the slice passed to an InsertMany or BulkWrite operation that caused this error.\n\tIndex int\n\n\tCode    int\n\tMessage string\n\tDetails bson.Raw\n\n\t// The original write error from the server response.\n\tRaw bson.Raw\n}\n\nfunc (we WriteError) Error() string {\n\tmsg := we.Message\n\tif len(we.Details) > 0 {\n\t\tmsg = fmt.Sprintf(\"%s: %s\", msg, we.Details.String())\n\t}\n\treturn msg\n}\n\n// HasErrorCode returns true if the error has the specified code.\nfunc (we WriteError) HasErrorCode(code int) bool {\n\treturn we.Code == code\n}\n\n// ErrorCodes returns a list of error codes returned by the server.\nfunc (we WriteError) ErrorCodes() []int {\n\treturn []int{we.Code}\n}\n\n// HasErrorLabel returns true if the error contains the specified label. WriteErrors do not contain labels,\n// so we always return false.\nfunc (we WriteError) HasErrorLabel(string) bool {\n\treturn false\n}\n\n// HasErrorMessage returns true if the error contains the specified message.\nfunc (we WriteError) HasErrorMessage(message string) bool {\n\treturn strings.Contains(we.Message, message)\n}\n\n// HasErrorCodeWithMessage returns true if the error has the specified code and Message contains the specified message.\nfunc (we WriteError) HasErrorCodeWithMessage(code int, message string) bool {\n\treturn we.Code == code && strings.Contains(we.Message, message)\n}\n\n// serverError implements the ServerError interface.\nfunc (we WriteError) serverError() {}\n\n// WriteErrors is a group of write errors that occurred during execution of a write operation.\ntype WriteErrors []WriteError\n\n// Error implements the error interface.\nfunc (we WriteErrors) Error() string {\n\terrs := make([]error, len(we))\n\tfor i := 0; i < len(we); i++ {\n\t\terrs[i] = we[i]\n\t}\n\t// WriteErrors isn't returned from batch operations, but we can still use the same formatter.\n\treturn \"write errors: \" + joinBatchErrors(errs)\n}\n\nfunc writeErrorsFromDriverWriteErrors(errs driver.WriteErrors) WriteErrors {\n\twes := make(WriteErrors, 0, len(errs))\n\tfor _, err := range errs {\n\t\twes = append(wes, WriteError{\n\t\t\tIndex:   int(err.Index),\n\t\t\tCode:    int(err.Code),\n\t\t\tMessage: err.Message,\n\t\t\tDetails: bson.Raw(err.Details),\n\t\t\tRaw:     bson.Raw(err.Raw),\n\t\t})\n\t}\n\treturn wes\n}\n\n// WriteConcernError represents a write concern failure during execution of a write operation. This error type is only\n// returned as part of a WriteException or a BulkWriteException.\ntype WriteConcernError struct {\n\tName    string\n\tCode    int\n\tMessage string\n\tDetails bson.Raw\n\tRaw     bson.Raw // The original write concern error from the server response.\n}\n\n// Error implements the error interface.\nfunc (wce WriteConcernError) Error() string {\n\tif wce.Name != \"\" {\n\t\treturn fmt.Sprintf(\"(%v) %v\", wce.Name, wce.Message)\n\t}\n\treturn wce.Message\n}\n\n// IsMaxTimeMSExpiredError returns true if the error is a MaxTimeMSExpired error.\nfunc (wce WriteConcernError) IsMaxTimeMSExpiredError() bool {\n\treturn wce.Code == 50\n}\n\n// WriteException is the error type returned by the InsertOne, DeleteOne, DeleteMany, UpdateOne, UpdateMany, and\n// ReplaceOne operations.\ntype WriteException struct {\n\t// The write concern error that occurred, or nil if there was none.\n\tWriteConcernError *WriteConcernError\n\n\t// The write errors that occurred during operation execution.\n\tWriteErrors WriteErrors\n\n\t// The categories to which the exception belongs.\n\tLabels []string\n\n\t// The original server response containing the error.\n\tRaw bson.Raw\n}\n\n// Error implements the error interface.\nfunc (mwe WriteException) Error() string {\n\tcauses := make([]string, 0, 2)\n\tif mwe.WriteConcernError != nil {\n\t\tcauses = append(causes, \"write concern error: \"+mwe.WriteConcernError.Error())\n\t}\n\tif len(mwe.WriteErrors) > 0 {\n\t\t// The WriteErrors error message already starts with \"write errors:\", so don't add it to the\n\t\t// error message again.\n\t\tcauses = append(causes, mwe.WriteErrors.Error())\n\t}\n\n\tmessage := \"write exception: \"\n\tif len(causes) == 0 {\n\t\treturn message + \"no causes\"\n\t}\n\treturn message + strings.Join(causes, \", \")\n}\n\n// HasErrorCode returns true if the error has the specified code.\nfunc (mwe WriteException) HasErrorCode(code int) bool {\n\treturn hasErrorCode(mwe, code)\n}\n\n// ErrorCodes returns a list of error codes returned by the server.\nfunc (mwe WriteException) ErrorCodes() []int {\n\terrorCodes := []int{}\n\tfor _, writeError := range mwe.WriteErrors {\n\t\terrorCodes = append(errorCodes, writeError.Code)\n\t}\n\n\tif mwe.WriteConcernError != nil {\n\t\terrorCodes = append(errorCodes, mwe.WriteConcernError.Code)\n\t}\n\n\treturn errorCodes\n}\n\n// HasErrorLabel returns true if the error contains the specified label.\nfunc (mwe WriteException) HasErrorLabel(label string) bool {\n\tfor _, l := range mwe.Labels {\n\t\tif l == label {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\n// HasErrorMessage returns true if the error contains the specified message.\nfunc (mwe WriteException) HasErrorMessage(message string) bool {\n\tif mwe.WriteConcernError != nil && strings.Contains(mwe.WriteConcernError.Message, message) {\n\t\treturn true\n\t}\n\tfor _, we := range mwe.WriteErrors {\n\t\tif strings.Contains(we.Message, message) {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\n// HasErrorCodeWithMessage returns true if any of the contained errors have the specified code and message.\nfunc (mwe WriteException) HasErrorCodeWithMessage(code int, message string) bool {\n\tif mwe.WriteConcernError != nil &&\n\t\tmwe.WriteConcernError.Code == code && strings.Contains(mwe.WriteConcernError.Message, message) {\n\t\treturn true\n\t}\n\tfor _, we := range mwe.WriteErrors {\n\t\tif we.Code == code && strings.Contains(we.Message, message) {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\n// serverError implements the ServerError interface.\nfunc (mwe WriteException) serverError() {}\n\nfunc convertDriverWriteConcernError(wce *driver.WriteConcernError) *WriteConcernError {\n\tif wce == nil {\n\t\treturn nil\n\t}\n\n\treturn &WriteConcernError{\n\t\tName:    wce.Name,\n\t\tCode:    int(wce.Code),\n\t\tMessage: wce.Message,\n\t\tDetails: bson.Raw(wce.Details),\n\t\tRaw:     bson.Raw(wce.Raw),\n\t}\n}\n\n// BulkWriteError is an error that occurred during execution of one operation in a BulkWrite. This error type is only\n// returned as part of a BulkWriteException.\ntype BulkWriteError struct {\n\tWriteError            // The WriteError that occurred.\n\tRequest    WriteModel // The WriteModel that caused this error.\n}\n\n// Error implements the error interface.\nfunc (bwe BulkWriteError) Error() string {\n\treturn bwe.WriteError.Error()\n}\n\n// BulkWriteException is the error type returned by BulkWrite and InsertMany operations.\ntype BulkWriteException struct {\n\t// The write concern error that occurred, or nil if there was none.\n\tWriteConcernError *WriteConcernError\n\n\t// The write errors that occurred during operation execution.\n\tWriteErrors []BulkWriteError\n\n\t// The categories to which the exception belongs.\n\tLabels []string\n}\n\n// Error implements the error interface.\nfunc (bwe BulkWriteException) Error() string {\n\tcauses := make([]string, 0, 2)\n\tif bwe.WriteConcernError != nil {\n\t\tcauses = append(causes, \"write concern error: \"+bwe.WriteConcernError.Error())\n\t}\n\tif len(bwe.WriteErrors) > 0 {\n\t\terrs := make([]error, len(bwe.WriteErrors))\n\t\tfor i := 0; i < len(bwe.WriteErrors); i++ {\n\t\t\terrs[i] = &bwe.WriteErrors[i]\n\t\t}\n\t\tcauses = append(causes, \"write errors: \"+joinBatchErrors(errs))\n\t}\n\n\tmessage := \"bulk write exception: \"\n\tif len(causes) == 0 {\n\t\treturn message + \"no causes\"\n\t}\n\treturn \"bulk write exception: \" + strings.Join(causes, \", \")\n}\n\n// HasErrorCode returns true if any of the errors have the specified code.\nfunc (bwe BulkWriteException) HasErrorCode(code int) bool {\n\treturn hasErrorCode(bwe, code)\n}\n\n// ErrorCodes returns a list of error codes returned by the server.\nfunc (bwe BulkWriteException) ErrorCodes() []int {\n\terrorCodes := []int{}\n\tfor _, writeError := range bwe.WriteErrors {\n\t\terrorCodes = append(errorCodes, writeError.Code)\n\t}\n\n\tif bwe.WriteConcernError != nil {\n\t\terrorCodes = append(errorCodes, bwe.WriteConcernError.Code)\n\t}\n\n\treturn errorCodes\n}\n\n// HasErrorLabel returns true if the error contains the specified label.\nfunc (bwe BulkWriteException) HasErrorLabel(label string) bool {\n\tfor _, l := range bwe.Labels {\n\t\tif l == label {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\n// HasErrorMessage returns true if the error contains the specified message.\nfunc (bwe BulkWriteException) HasErrorMessage(message string) bool {\n\tif bwe.WriteConcernError != nil && strings.Contains(bwe.WriteConcernError.Message, message) {\n\t\treturn true\n\t}\n\tfor _, we := range bwe.WriteErrors {\n\t\tif strings.Contains(we.Message, message) {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\n// HasErrorCodeWithMessage returns true if any of the contained errors have the specified code and message.\nfunc (bwe BulkWriteException) HasErrorCodeWithMessage(code int, message string) bool {\n\tif bwe.WriteConcernError != nil &&\n\t\tbwe.WriteConcernError.Code == code && strings.Contains(bwe.WriteConcernError.Message, message) {\n\t\treturn true\n\t}\n\tfor _, we := range bwe.WriteErrors {\n\t\tif we.Code == code && strings.Contains(we.Message, message) {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\n// serverError implements the ServerError interface.\nfunc (bwe BulkWriteException) serverError() {}\n\n// ClientBulkWriteException is the error type returned by ClientBulkWrite operations.\ntype ClientBulkWriteException struct {\n\t// A top-level error that occurred when attempting to communicate with the server\n\t// or execute the bulk write. This value may not be populated if the exception was\n\t// thrown due to errors occurring on individual writes.\n\tWriteError *WriteError\n\n\t// The write concern errors that occurred.\n\tWriteConcernErrors []WriteConcernError\n\n\t// The write errors that occurred during individual operation execution.\n\t// This map will contain at most one entry if the bulk write was ordered.\n\tWriteErrors map[int]WriteError\n\n\t// The results of any successful operations that were performed before the error\n\t// was encountered.\n\tPartialResult *ClientBulkWriteResult\n}\n\n// Error implements the error interface.\nfunc (bwe ClientBulkWriteException) Error() string {\n\tcauses := make([]string, 0, 4)\n\tif bwe.WriteError != nil {\n\t\tcauses = append(causes, \"top level error: \"+bwe.WriteError.Error())\n\t}\n\tif len(bwe.WriteConcernErrors) > 0 {\n\t\terrs := make([]error, len(bwe.WriteConcernErrors))\n\t\tfor i := 0; i < len(bwe.WriteConcernErrors); i++ {\n\t\t\terrs[i] = bwe.WriteConcernErrors[i]\n\t\t}\n\t\tcauses = append(causes, \"write concern errors: \"+joinBatchErrors(errs))\n\t}\n\tif len(bwe.WriteErrors) > 0 {\n\t\terrs := make([]error, 0, len(bwe.WriteErrors))\n\t\tfor _, v := range bwe.WriteErrors {\n\t\t\terrs = append(errs, v)\n\t\t}\n\t\tcauses = append(causes, \"write errors: \"+joinBatchErrors(errs))\n\t}\n\tif bwe.PartialResult != nil {\n\t\tcauses = append(causes, fmt.Sprintf(\"result: %v\", *bwe.PartialResult))\n\t}\n\n\tmessage := \"bulk write exception: \"\n\tif len(causes) == 0 {\n\t\treturn message + \"no causes\"\n\t}\n\treturn \"bulk write exception: \" + strings.Join(causes, \", \")\n}\n\n// returnResult is used to determine if a function calling processWriteError should return\n// the result or return nil. Since the processWriteError function is used by many different\n// methods, both *One and *Many, we need a way to differentiate if the method should return\n// the result and the error.\ntype returnResult int\n\nconst (\n\trrNone returnResult = 1 << iota // None means do not return the result ever.\n\trrOne                           // One means return the result if this was called by a *One method.\n\trrMany                          // Many means return the result is this was called by a *Many method.\n\trrUnacknowledged\n\n\trrAll               returnResult = rrOne | rrMany           // All means always return the result.\n\trrAllUnacknowledged returnResult = rrAll | rrUnacknowledged // All + unacknowledged write\n)\n\nfunc (rr returnResult) isAcknowledged() bool {\n\treturn rr&rrUnacknowledged == 0\n}\n\n// processWriteError handles processing the result of a write operation. If the retrunResult matches\n// the calling method's type, it should return the result object in addition to the error.\n// This function will wrap the errors from other packages and return them as errors from this package.\n//\n// WriteConcernError will be returned over WriteErrors if both are present.\nfunc processWriteError(err error) (returnResult, error) {\n\tif err == nil {\n\t\treturn rrAll, nil\n\t}\n\t// Do not propagate the acknowledgement sentinel error. For DDL commands,\n\t// (creating indexes, dropping collections, etc) acknowledgement should be\n\t// ignored. For non-DDL write commands (insert, update, etc), acknowledgement\n\t// should be be propagated at the result-level: e.g.,\n\t// SingleResult.Acknowledged.\n\tif errors.Is(err, driver.ErrUnacknowledgedWrite) {\n\t\treturn rrAllUnacknowledged, nil\n\t}\n\n\tvar wce driver.WriteCommandError\n\tif !errors.As(err, &wce) {\n\t\treturn rrNone, wrapErrors(err)\n\t}\n\n\treturn rrMany, WriteException{\n\t\tWriteConcernError: convertDriverWriteConcernError(wce.WriteConcernError),\n\t\tWriteErrors:       writeErrorsFromDriverWriteErrors(wce.WriteErrors),\n\t\tLabels:            wce.Labels,\n\t\tRaw:               bson.Raw(wce.Raw),\n\t}\n}\n\n// batchErrorsTargetLength is the target length of error messages returned by batch operation\n// error types. Try to limit batch error messages to 2kb to prevent problems when printing error\n// messages from large batch operations.\nconst batchErrorsTargetLength = 2000\n\n// joinBatchErrors appends messages from the given errors to a comma-separated string. If the\n// string exceeds 2kb, it stops appending error messages and appends the message \"+N more errors...\"\n// to the end.\n//\n// Example format:\n//\n//\t\"[message 1, message 2, +8 more errors...]\"\nfunc joinBatchErrors(errs []error) string {\n\tvar buf bytes.Buffer\n\tfmt.Fprint(&buf, \"[\")\n\tfor idx, err := range errs {\n\t\tif idx != 0 {\n\t\t\tfmt.Fprint(&buf, \", \")\n\t\t}\n\t\t// If the error message has exceeded the target error message length, stop appending errors\n\t\t// to the message and append the number of remaining errors instead.\n\t\tif buf.Len() > batchErrorsTargetLength {\n\t\t\tfmt.Fprintf(&buf, \"+%d more errors...\", len(errs)-idx)\n\t\t\tbreak\n\t\t}\n\t\tfmt.Fprint(&buf, err.Error())\n\t}\n\tfmt.Fprint(&buf, \"]\")\n\n\treturn buf.String()\n}\n\n// ErrorCodes returns the list of server error codes contained in err.\nfunc ErrorCodes(err error) []int {\n\tif err == nil {\n\t\treturn nil\n\t}\n\n\tvar ec errorCoder\n\t// First check if the error is already wrapped (common case)\n\tif errors.As(err, &ec) {\n\t\treturn ec.ErrorCodes()\n\t}\n\n\t// Only wrap if necessary (for internal errors)\n\tif errors.As(wrapErrors(err), &ec) {\n\t\treturn ec.ErrorCodes()\n\t}\n\n\treturn []int{}\n}\n"
  },
  {
    "path": "mongo/errors_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2022-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage mongo\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"net\"\n\t\"testing\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/require\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/topology\"\n)\n\nfunc TestErrorMessages(t *testing.T) {\n\tdetails, err := bson.Marshal(bson.D{{\"details\", bson.D{{\"operatorName\", \"$jsonSchema\"}}}})\n\trequire.Nil(t, err, \"unexpected error marshaling BSON\")\n\n\tcases := []struct {\n\t\tdesc     string\n\t\terr      error\n\t\texpected string\n\t}{\n\t\t{\n\t\t\tdesc: \"WriteException error message should contain the WriteError Message and Details\",\n\t\t\terr: WriteException{\n\t\t\t\tWriteErrors: WriteErrors{\n\t\t\t\t\t{\n\t\t\t\t\t\tMessage: \"test message 1\",\n\t\t\t\t\t\tDetails: details,\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tMessage: \"test message 2\",\n\t\t\t\t\t\tDetails: details,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\texpected: `write exception: write errors: [test message 1: {\"details\": {\"operatorName\": \"$jsonSchema\"}}, test message 2: {\"details\": {\"operatorName\": \"$jsonSchema\"}}]`,\n\t\t},\n\t\t{\n\t\t\tdesc: \"BulkWriteException error message should contain the WriteError Message and Details\",\n\t\t\terr: BulkWriteException{\n\t\t\t\tWriteErrors: []BulkWriteError{\n\t\t\t\t\t{\n\t\t\t\t\t\tWriteError: WriteError{\n\t\t\t\t\t\t\tMessage: \"test message 1\",\n\t\t\t\t\t\t\tDetails: details,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tWriteError: WriteError{\n\t\t\t\t\t\t\tMessage: \"test message 2\",\n\t\t\t\t\t\t\tDetails: details,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\texpected: `bulk write exception: write errors: [test message 1: {\"details\": {\"operatorName\": \"$jsonSchema\"}}, test message 2: {\"details\": {\"operatorName\": \"$jsonSchema\"}}]`,\n\t\t},\n\t}\n\n\tfor _, tc := range cases {\n\t\ttc := tc\n\t\tt.Run(tc.desc, func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tassert.Equal(t, tc.expected, tc.err.Error())\n\t\t})\n\t}\n}\n\nfunc TestServerError(t *testing.T) {\n\tmatchWrapped := errors.New(\"wrapped err\")\n\totherWrapped := errors.New(\"other err\")\n\tconst matchCode = 100\n\tconst otherCode = 120\n\tconst label = \"testError\"\n\n\ttestCases := []struct {\n\t\tname               string\n\t\terr                ServerError\n\t\thasCode            bool\n\t\thasLabel           bool\n\t\thasMessage         bool\n\t\thasCodeWithMessage bool\n\t\tisResult           bool\n\t}{\n\t\t{\n\t\t\tname: \"CommandError all true\",\n\t\t\terr: CommandError{\n\t\t\t\tCode:    matchCode,\n\t\t\t\tMessage: \"foo\",\n\t\t\t\tLabels:  []string{label},\n\t\t\t\tName:    \"name\",\n\t\t\t\tWrapped: matchWrapped,\n\t\t\t},\n\t\t\thasCode:            true,\n\t\t\thasLabel:           true,\n\t\t\thasMessage:         true,\n\t\t\thasCodeWithMessage: true,\n\t\t\tisResult:           true,\n\t\t},\n\t\t{\n\t\t\tname: \"CommandError all false\",\n\t\t\terr: CommandError{\n\t\t\t\tCode:    otherCode,\n\t\t\t\tMessage: \"bar\",\n\t\t\t\tLabels:  []string{\"otherError\"},\n\t\t\t\tName:    \"name\",\n\t\t\t\tWrapped: otherWrapped,\n\t\t\t},\n\t\t\thasCode:            false,\n\t\t\thasLabel:           false,\n\t\t\thasMessage:         false,\n\t\t\thasCodeWithMessage: false,\n\t\t\tisResult:           false,\n\t\t},\n\t\t{\n\t\t\tname: \"CommandError has code not message\",\n\t\t\terr: CommandError{\n\t\t\t\tCode:    matchCode,\n\t\t\t\tMessage: \"bar\",\n\t\t\t\tLabels:  []string{},\n\t\t\t\tName:    \"name\",\n\t\t\t},\n\t\t\thasCode:            true,\n\t\t\thasLabel:           false,\n\t\t\thasMessage:         false,\n\t\t\thasCodeWithMessage: false,\n\t\t\tisResult:           false,\n\t\t},\n\t\t{\n\t\t\tname: \"WriteException all in writeConcernError\",\n\t\t\terr: WriteException{\n\t\t\t\tWriteConcernError: &WriteConcernError{\n\t\t\t\t\tName:    \"name\",\n\t\t\t\t\tCode:    matchCode,\n\t\t\t\t\tMessage: \"foo\",\n\t\t\t\t},\n\t\t\t\tLabels: []string{label},\n\t\t\t},\n\t\t\thasCode:            true,\n\t\t\thasLabel:           true,\n\t\t\thasMessage:         true,\n\t\t\thasCodeWithMessage: true,\n\t\t\tisResult:           false,\n\t\t},\n\t\t{\n\t\t\tname: \"WriteException all in writeError\",\n\t\t\terr: WriteException{\n\t\t\t\tWriteErrors: WriteErrors{\n\t\t\t\t\tWriteError{\n\t\t\t\t\t\tIndex:   0,\n\t\t\t\t\t\tCode:    otherCode,\n\t\t\t\t\t\tMessage: \"bar\",\n\t\t\t\t\t},\n\t\t\t\t\tWriteError{\n\t\t\t\t\t\tIndex:   0,\n\t\t\t\t\t\tCode:    matchCode,\n\t\t\t\t\t\tMessage: \"foo\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tLabels: []string{\"otherError\"},\n\t\t\t},\n\t\t\thasCode:            true,\n\t\t\thasLabel:           false,\n\t\t\thasMessage:         true,\n\t\t\thasCodeWithMessage: true,\n\t\t\tisResult:           false,\n\t\t},\n\t\t{\n\t\t\tname: \"WriteException all false\",\n\t\t\terr: WriteException{\n\t\t\t\tWriteConcernError: &WriteConcernError{\n\t\t\t\t\tName:    \"name\",\n\t\t\t\t\tCode:    otherCode,\n\t\t\t\t\tMessage: \"bar\",\n\t\t\t\t},\n\t\t\t\tWriteErrors: WriteErrors{\n\t\t\t\t\tWriteError{\n\t\t\t\t\t\tIndex:   0,\n\t\t\t\t\t\tCode:    otherCode,\n\t\t\t\t\t\tMessage: \"baz\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tLabels: []string{\"otherError\"},\n\t\t\t},\n\t\t\thasCode:            false,\n\t\t\thasLabel:           false,\n\t\t\thasMessage:         false,\n\t\t\thasCodeWithMessage: false,\n\t\t\tisResult:           false,\n\t\t},\n\t\t{\n\t\t\tname: \"WriteException HasErrorCodeAndMessage false\",\n\t\t\terr: WriteException{\n\t\t\t\tWriteConcernError: &WriteConcernError{\n\t\t\t\t\tName:    \"name\",\n\t\t\t\t\tCode:    matchCode,\n\t\t\t\t\tMessage: \"bar\",\n\t\t\t\t},\n\t\t\t\tWriteErrors: WriteErrors{\n\t\t\t\t\tWriteError{\n\t\t\t\t\t\tIndex:   0,\n\t\t\t\t\t\tCode:    otherCode,\n\t\t\t\t\t\tMessage: \"foo\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tLabels: []string{\"otherError\"},\n\t\t\t},\n\t\t\thasCode:            true,\n\t\t\thasLabel:           false,\n\t\t\thasMessage:         true,\n\t\t\thasCodeWithMessage: false,\n\t\t\tisResult:           false,\n\t\t},\n\t\t{\n\t\t\tname: \"BulkWriteException all in writeConcernError\",\n\t\t\terr: BulkWriteException{\n\t\t\t\tWriteConcernError: &WriteConcernError{\n\t\t\t\t\tName:    \"name\",\n\t\t\t\t\tCode:    matchCode,\n\t\t\t\t\tMessage: \"foo\",\n\t\t\t\t},\n\t\t\t\tLabels: []string{label},\n\t\t\t},\n\t\t\thasCode:            true,\n\t\t\thasLabel:           true,\n\t\t\thasMessage:         true,\n\t\t\thasCodeWithMessage: true,\n\t\t\tisResult:           false,\n\t\t},\n\t\t{\n\t\t\tname: \"BulkWriteException all in writeError\",\n\t\t\terr: BulkWriteException{\n\t\t\t\tWriteErrors: []BulkWriteError{\n\t\t\t\t\t{\n\t\t\t\t\t\tWriteError: WriteError{\n\t\t\t\t\t\t\tIndex:   0,\n\t\t\t\t\t\t\tCode:    matchCode,\n\t\t\t\t\t\t\tMessage: \"foo\",\n\t\t\t\t\t\t},\n\t\t\t\t\t\tRequest: &InsertOneModel{},\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tWriteError: WriteError{\n\t\t\t\t\t\t\tIndex:   0,\n\t\t\t\t\t\t\tCode:    otherCode,\n\t\t\t\t\t\t\tMessage: \"bar\",\n\t\t\t\t\t\t},\n\t\t\t\t\t\tRequest: &InsertOneModel{},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tLabels: []string{\"otherError\"},\n\t\t\t},\n\t\t\thasCode:            true,\n\t\t\thasLabel:           false,\n\t\t\thasMessage:         true,\n\t\t\thasCodeWithMessage: true,\n\t\t\tisResult:           false,\n\t\t},\n\t\t{\n\t\t\tname: \"BulkWriteException all false\",\n\t\t\terr: BulkWriteException{\n\t\t\t\tWriteConcernError: &WriteConcernError{\n\t\t\t\t\tName:    \"name\",\n\t\t\t\t\tCode:    otherCode,\n\t\t\t\t\tMessage: \"bar\",\n\t\t\t\t},\n\t\t\t\tWriteErrors: []BulkWriteError{\n\t\t\t\t\t{\n\t\t\t\t\t\tWriteError: WriteError{\n\t\t\t\t\t\t\tIndex:   0,\n\t\t\t\t\t\t\tCode:    otherCode,\n\t\t\t\t\t\t\tMessage: \"baz\",\n\t\t\t\t\t\t},\n\t\t\t\t\t\tRequest: &InsertOneModel{},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tLabels: []string{\"otherError\"},\n\t\t\t},\n\t\t\thasCode:            false,\n\t\t\thasLabel:           false,\n\t\t\thasMessage:         false,\n\t\t\thasCodeWithMessage: false,\n\t\t\tisResult:           false,\n\t\t},\n\t\t{\n\t\t\tname: \"BulkWriteException HasErrorCodeAndMessage false\",\n\t\t\terr: BulkWriteException{\n\t\t\t\tWriteConcernError: &WriteConcernError{\n\t\t\t\t\tName:    \"name\",\n\t\t\t\t\tCode:    matchCode,\n\t\t\t\t\tMessage: \"bar\",\n\t\t\t\t},\n\t\t\t\tWriteErrors: []BulkWriteError{\n\t\t\t\t\t{\n\t\t\t\t\t\tWriteError: WriteError{\n\t\t\t\t\t\t\tIndex:   0,\n\t\t\t\t\t\t\tCode:    otherCode,\n\t\t\t\t\t\t\tMessage: \"foo\",\n\t\t\t\t\t\t},\n\t\t\t\t\t\tRequest: &InsertOneModel{},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tLabels: []string{\"otherError\"},\n\t\t\t},\n\t\t\thasCode:            true,\n\t\t\thasLabel:           false,\n\t\t\thasMessage:         true,\n\t\t\thasCodeWithMessage: false,\n\t\t\tisResult:           false,\n\t\t},\n\t}\n\tfor _, tc := range testCases {\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\thas := tc.err.HasErrorCode(matchCode)\n\t\t\tassert.Equal(t, has, tc.hasCode, \"expected HasErrorCode to return %v, got %v\", tc.hasCode, has)\n\t\t\thas = tc.err.HasErrorLabel(label)\n\t\t\tassert.Equal(t, has, tc.hasLabel, \"expected HasErrorLabel to return %v, got %v\", tc.hasLabel, has)\n\n\t\t\t// Check for full message and substring\n\t\t\thas = tc.err.HasErrorMessage(\"foo\")\n\t\t\tassert.Equal(t, has, tc.hasMessage, \"expected HasErrorMessage to return %v, got %v\", tc.hasMessage, has)\n\t\t\thas = tc.err.HasErrorMessage(\"fo\")\n\t\t\tassert.Equal(t, has, tc.hasMessage, \"expected HasErrorMessage to return %v, got %v\", tc.hasMessage, has)\n\t\t\thas = tc.err.HasErrorCodeWithMessage(matchCode, \"foo\")\n\t\t\tassert.Equal(t, has, tc.hasCodeWithMessage, \"expected HasErrorCodeWithMessage to return %v, got %v\", tc.hasCodeWithMessage, has)\n\t\t\thas = tc.err.HasErrorCodeWithMessage(matchCode, \"fo\")\n\t\t\tassert.Equal(t, has, tc.hasCodeWithMessage, \"expected HasErrorCodeWithMessage to return %v, got %v\", tc.hasCodeWithMessage, has)\n\n\t\t\tassert.Equal(t, errors.Is(tc.err, matchWrapped), tc.isResult, \"expected errors.Is result to be %v\", tc.isResult)\n\t\t})\n\t}\n}\n\nfunc TestIsDuplicateKeyError(t *testing.T) {\n\ttestCases := []struct {\n\t\tname   string\n\t\terr    error\n\t\tresult bool\n\t}{\n\t\t{\n\t\t\tname: \"CommandError true\",\n\t\t\terr: CommandError{\n\t\t\t\tCode: 11000,\n\t\t\t\tName: \"blah\",\n\t\t\t},\n\t\t\tresult: true,\n\t\t},\n\t\t{\n\t\t\tname: \"CommandError false\",\n\t\t\terr: CommandError{\n\t\t\t\tCode: 100,\n\t\t\t\tName: \"blah\",\n\t\t\t},\n\t\t\tresult: false,\n\t\t},\n\t\t{\n\t\t\tname: \"WriteError true\",\n\t\t\terr: WriteError{\n\t\t\t\tIndex: 0,\n\t\t\t\tCode:  11000,\n\t\t\t},\n\t\t\tresult: true,\n\t\t},\n\t\t{\n\t\t\tname: \"WriteError false\",\n\t\t\terr: WriteError{\n\t\t\t\tIndex: 0,\n\t\t\t\tCode:  100,\n\t\t\t},\n\t\t\tresult: false,\n\t\t},\n\t\t{\n\t\t\tname: \"WriteException true in writeConcernError\",\n\t\t\terr: WriteException{\n\t\t\t\tWriteConcernError: &WriteConcernError{\n\t\t\t\t\tName:    \"name\",\n\t\t\t\t\tCode:    11001,\n\t\t\t\t\tMessage: \"bar\",\n\t\t\t\t},\n\t\t\t\tWriteErrors: WriteErrors{\n\t\t\t\t\tWriteError{\n\t\t\t\t\t\tIndex:   0,\n\t\t\t\t\t\tCode:    100,\n\t\t\t\t\t\tMessage: \"baz\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\tresult: true,\n\t\t},\n\t\t{\n\t\t\tname: \"WriteException true in writeErrors\",\n\t\t\terr: WriteException{\n\t\t\t\tWriteConcernError: &WriteConcernError{\n\t\t\t\t\tName:    \"name\",\n\t\t\t\t\tCode:    100,\n\t\t\t\t\tMessage: \"bar\",\n\t\t\t\t},\n\t\t\t\tWriteErrors: WriteErrors{\n\t\t\t\t\tWriteError{\n\t\t\t\t\t\tIndex:   0,\n\t\t\t\t\t\tCode:    12582,\n\t\t\t\t\t\tMessage: \"baz\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\tresult: true,\n\t\t},\n\t\t{\n\t\t\tname: \"WriteException false\",\n\t\t\terr: WriteException{\n\t\t\t\tWriteConcernError: &WriteConcernError{\n\t\t\t\t\tName:    \"name\",\n\t\t\t\t\tCode:    16460,\n\t\t\t\t\tMessage: \"bar\",\n\t\t\t\t},\n\t\t\t\tWriteErrors: WriteErrors{\n\t\t\t\t\tWriteError{\n\t\t\t\t\t\tIndex:   0,\n\t\t\t\t\t\tCode:    100,\n\t\t\t\t\t\tMessage: \"blah E11000 blah\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\tresult: false,\n\t\t},\n\t\t{\n\t\t\tname: \"BulkWriteException true\",\n\t\t\terr: BulkWriteException{\n\t\t\t\tWriteConcernError: &WriteConcernError{\n\t\t\t\t\tName:    \"name\",\n\t\t\t\t\tCode:    100,\n\t\t\t\t\tMessage: \"bar\",\n\t\t\t\t},\n\t\t\t\tWriteErrors: []BulkWriteError{\n\t\t\t\t\t{\n\t\t\t\t\t\tWriteError: WriteError{\n\t\t\t\t\t\t\tIndex:   0,\n\t\t\t\t\t\t\tCode:    16460,\n\t\t\t\t\t\t\tMessage: \"blah E11000 blah\",\n\t\t\t\t\t\t},\n\t\t\t\t\t\tRequest: &InsertOneModel{},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tLabels: []string{\"otherError\"},\n\t\t\t},\n\t\t\tresult: true,\n\t\t},\n\t\t{\n\t\t\tname: \"BulkWriteException false\",\n\t\t\terr: BulkWriteException{\n\t\t\t\tWriteConcernError: &WriteConcernError{\n\t\t\t\t\tName:    \"name\",\n\t\t\t\t\tCode:    100,\n\t\t\t\t\tMessage: \"bar\",\n\t\t\t\t},\n\t\t\t\tWriteErrors: []BulkWriteError{\n\t\t\t\t\t{\n\t\t\t\t\t\tWriteError: WriteError{\n\t\t\t\t\t\t\tIndex:   0,\n\t\t\t\t\t\t\tCode:    110,\n\t\t\t\t\t\t\tMessage: \"blah\",\n\t\t\t\t\t\t},\n\t\t\t\t\t\tRequest: &InsertOneModel{},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tLabels: []string{\"otherError\"},\n\t\t\t},\n\t\t\tresult: false,\n\t\t},\n\t\t{\n\t\t\tname:   \"wrapped error\",\n\t\t\terr:    fmt.Errorf(\"%w\", CommandError{Code: 11000, Name: \"blah\"}),\n\t\t\tresult: true,\n\t\t},\n\t\t{\n\t\t\tname:   \"other error type\",\n\t\t\terr:    errors.New(\"foo\"),\n\t\t\tresult: false,\n\t\t},\n\t}\n\tfor _, tc := range testCases {\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\tres := IsDuplicateKeyError(tc.err)\n\t\t\tassert.Equal(t, res, tc.result, \"expected IsDuplicateKeyError %v, got %v\", tc.result, res)\n\t\t})\n\t}\n}\n\nfunc TestIsNetworkError(t *testing.T) {\n\tconst networkLabel = \"NetworkError\"\n\tconst otherLabel = \"other\"\n\ttestCases := []struct {\n\t\tname   string\n\t\terr    error\n\t\tresult bool\n\t}{\n\t\t{\n\t\t\tname: \"ServerError true\",\n\t\t\terr: CommandError{\n\t\t\t\tCode:   100,\n\t\t\t\tLabels: []string{networkLabel},\n\t\t\t\tName:   \"blah\",\n\t\t\t},\n\t\t\tresult: true,\n\t\t},\n\t\t{\n\t\t\tname: \"ServerError false\",\n\t\t\terr: CommandError{\n\t\t\t\tCode:   100,\n\t\t\t\tLabels: []string{otherLabel},\n\t\t\t\tName:   \"blah\",\n\t\t\t},\n\t\t\tresult: false,\n\t\t},\n\t\t{\n\t\t\tname: \"wrapped error\",\n\t\t\terr: fmt.Errorf(\"%w\", CommandError{\n\t\t\t\tCode:   100,\n\t\t\t\tLabels: []string{networkLabel},\n\t\t\t\tName:   \"blah\",\n\t\t\t}),\n\t\t\tresult: true,\n\t\t},\n\t\t{\n\t\t\tname:   \"other error type\",\n\t\t\terr:    errors.New(\"foo\"),\n\t\t\tresult: false,\n\t\t},\n\t}\n\tfor _, tc := range testCases {\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\tres := IsNetworkError(tc.err)\n\t\t\tassert.Equal(t, res, tc.result, \"expected IsNetworkError %v, got %v\", tc.result, res)\n\t\t})\n\t}\n}\n\nfunc TestIsTimeout(t *testing.T) {\n\ttestCases := []struct {\n\t\tname   string\n\t\terr    error\n\t\tresult bool\n\t}{\n\t\t{\n\t\t\tname: \"context timeout\",\n\t\t\terr: CommandError{\n\t\t\t\tCode:    100,\n\t\t\t\tMessage: \"\",\n\t\t\t\tLabels:  []string{\"other\"},\n\t\t\t\tName:    \"blah\",\n\t\t\t\tWrapped: context.DeadlineExceeded,\n\t\t\t\tRaw:     nil,\n\t\t\t},\n\t\t\tresult: true,\n\t\t},\n\t\t{\n\t\t\tname: \"deadline would be exceeded\",\n\t\t\terr: CommandError{\n\t\t\t\tCode:    100,\n\t\t\t\tMessage: \"\",\n\t\t\t\tLabels:  []string{\"other\"},\n\t\t\t\tName:    \"blah\",\n\t\t\t\tWrapped: driver.ErrDeadlineWouldBeExceeded,\n\t\t\t\tRaw:     nil,\n\t\t\t},\n\t\t\tresult: true,\n\t\t},\n\t\t{\n\t\t\tname: \"server selection timeout\",\n\t\t\terr: CommandError{\n\t\t\t\tCode:    100,\n\t\t\t\tMessage: \"\",\n\t\t\t\tLabels:  []string{\"other\"},\n\t\t\t\tName:    \"blah\",\n\t\t\t\tWrapped: context.DeadlineExceeded,\n\t\t\t\tRaw:     nil,\n\t\t\t},\n\t\t\tresult: true,\n\t\t},\n\t\t{\n\t\t\tname: \"wait queue timeout\",\n\t\t\terr: CommandError{\n\t\t\t\tCode:    100,\n\t\t\t\tMessage: \"\",\n\t\t\t\tLabels:  []string{\"other\"},\n\t\t\t\tName:    \"blah\",\n\t\t\t\tWrapped: topology.WaitQueueTimeoutError{},\n\t\t\t\tRaw:     nil,\n\t\t\t},\n\t\t\tresult: true,\n\t\t},\n\t\t{\n\t\t\tname: \"ServerError NetworkTimeoutError\",\n\t\t\terr: CommandError{\n\t\t\t\tCode:    100,\n\t\t\t\tMessage: \"\",\n\t\t\t\tLabels:  []string{\"NetworkTimeoutError\"},\n\t\t\t\tName:    \"blah\",\n\t\t\t\tWrapped: nil,\n\t\t\t\tRaw:     nil,\n\t\t\t},\n\t\t\tresult: true,\n\t\t},\n\t\t{\n\t\t\tname: \"ServerError ExceededTimeLimitError\",\n\t\t\terr: CommandError{\n\t\t\t\tCode:    100,\n\t\t\t\tMessage: \"\",\n\t\t\t\tLabels:  []string{\"ExceededTimeLimitError\"},\n\t\t\t\tName:    \"blah\",\n\t\t\t\tWrapped: nil,\n\t\t\t\tRaw:     nil,\n\t\t\t},\n\t\t\tresult: true,\n\t\t},\n\t\t{\n\t\t\tname: \"ServerError false\",\n\t\t\terr: CommandError{\n\t\t\t\tCode:    100,\n\t\t\t\tMessage: \"\",\n\t\t\t\tLabels:  []string{\"other\"},\n\t\t\t\tName:    \"blah\",\n\t\t\t\tWrapped: nil,\n\t\t\t\tRaw:     nil,\n\t\t\t},\n\t\t\tresult: false,\n\t\t},\n\t\t{\n\t\t\tname: \"net error true\",\n\t\t\terr: CommandError{\n\t\t\t\tCode:    100,\n\t\t\t\tMessage: \"\",\n\t\t\t\tLabels:  []string{\"other\"},\n\t\t\t\tName:    \"blah\",\n\t\t\t\tWrapped: netErr{true},\n\t\t\t\tRaw:     nil,\n\t\t\t},\n\t\t\tresult: true,\n\t\t},\n\t\t{\n\t\t\tname:   \"net error false\",\n\t\t\terr:    netErr{false},\n\t\t\tresult: false,\n\t\t},\n\t\t{\n\t\t\tname: \"wrapped error\",\n\t\t\terr: fmt.Errorf(\"%w\", CommandError{\n\t\t\t\tCode:    100,\n\t\t\t\tMessage: \"\",\n\t\t\t\tLabels:  []string{\"other\"},\n\t\t\t\tName:    \"blah\",\n\t\t\t\tWrapped: context.DeadlineExceeded,\n\t\t\t\tRaw:     nil,\n\t\t\t}),\n\t\t\tresult: true,\n\t\t},\n\t\t{\n\t\t\tname:   \"other error\",\n\t\t\terr:    errors.New(\"foo\"),\n\t\t\tresult: false,\n\t\t},\n\t}\n\tfor _, tc := range testCases {\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\tres := IsTimeout(tc.err)\n\t\t\tassert.Equal(t, res, tc.result, \"expected IsTimeout %v, got %v\", tc.result, res)\n\t\t})\n\t}\n}\n\nfunc TestServerError_ErrorCodes(t *testing.T) {\n\ttests := []struct {\n\t\tname  string\n\t\terror ServerError\n\t\twant  []int\n\t}{\n\t\t{\n\t\t\tname:  \"CommandError\",\n\t\t\terror: CommandError{Code: 1},\n\t\t\twant:  []int{1},\n\t\t},\n\t\t{\n\t\t\tname:  \"WriteError\",\n\t\t\terror: WriteError{Code: 1},\n\t\t\twant:  []int{1},\n\t\t},\n\t\t{\n\t\t\tname:  \"WriteException single\",\n\t\t\terror: WriteException{WriteErrors: []WriteError{{Code: 1}}},\n\t\t\twant:  []int{1},\n\t\t},\n\t\t{\n\t\t\tname:  \"WriteException multiple\",\n\t\t\terror: WriteException{WriteErrors: []WriteError{{Code: 1}, {Code: 2}}},\n\t\t\twant:  []int{1, 2},\n\t\t},\n\t\t{\n\t\t\tname:  \"WriteException duplicates\",\n\t\t\terror: WriteException{WriteErrors: []WriteError{{Code: 1}, {Code: 2}, {Code: 2}}},\n\t\t\twant:  []int{1, 2, 2},\n\t\t},\n\t\t{\n\t\t\tname:  \"BulkWriteException single\",\n\t\t\terror: BulkWriteException{WriteErrors: []BulkWriteError{{WriteError: WriteError{Code: 1}}}},\n\t\t\twant:  []int{1},\n\t\t},\n\t\t{\n\t\t\tname: \"BulkWriteException multiple\",\n\t\t\terror: BulkWriteException{WriteErrors: []BulkWriteError{\n\t\t\t\t{WriteError: WriteError{Code: 1}},\n\t\t\t\t{WriteError: WriteError{Code: 2}},\n\t\t\t}},\n\t\t\twant: []int{1, 2},\n\t\t},\n\t\t{\n\t\t\tname: \"BulkWriteException duplicates\",\n\t\t\terror: BulkWriteException{WriteErrors: []BulkWriteError{\n\t\t\t\t{WriteError: WriteError{Code: 1}},\n\t\t\t\t{WriteError: WriteError{Code: 2}},\n\t\t\t\t{WriteError: WriteError{Code: 2}},\n\t\t\t}},\n\t\t\twant: []int{1, 2, 2},\n\t\t},\n\t}\n\n\tfor _, test := range tests {\n\t\tt.Run(test.name, func(t *testing.T) {\n\t\t\tgot := test.error.ErrorCodes()\n\n\t\t\tassert.ElementsMatch(t, got, test.want)\n\t\t})\n\t}\n}\n\ntype netErr struct {\n\ttimeout bool\n}\n\nfunc (n netErr) Error() string {\n\treturn \"error\"\n}\n\nfunc (n netErr) Timeout() bool {\n\treturn n.timeout\n}\n\nfunc (n netErr) Temporary() bool {\n\treturn false\n}\n\nvar _ net.Error = (*netErr)(nil)\n\nfunc TestErrorCodes(t *testing.T) {\n\ttests := []struct {\n\t\tname  string\n\t\tinput error\n\t\twant  []int\n\t}{\n\t\t{\n\t\t\tname:  \"nil error\",\n\t\t\tinput: nil,\n\t\t\twant:  nil,\n\t\t},\n\t\t{\n\t\t\tname:  \"non-server error\",\n\t\t\tinput: errors.New(\"boom\"),\n\t\t\twant:  []int{},\n\t\t},\n\t\t{\n\t\t\tname:  \"CommandError single code\",\n\t\t\tinput: CommandError{Code: 1},\n\t\t\twant:  []int{1},\n\t\t},\n\t\t{\n\t\t\tname:  \"WriteError single code\",\n\t\t\tinput: WriteError{Code: 1},\n\t\t\twant:  []int{1},\n\t\t},\n\t\t{\n\t\t\tname:  \"WriteException write errors only\",\n\t\t\tinput: WriteException{WriteErrors: WriteErrors{{Code: 1}, {Code: 2}}},\n\t\t\twant:  []int{1, 2},\n\t\t},\n\t\t{\n\t\t\tname:  \"WriteException with write concern error\",\n\t\t\tinput: WriteException{WriteErrors: WriteErrors{{Code: 1}}, WriteConcernError: &WriteConcernError{Code: 2}},\n\t\t\twant:  []int{1, 2},\n\t\t},\n\t\t{\n\t\t\tname: \"BulkWriteException write errors only\",\n\t\t\tinput: BulkWriteException{\n\t\t\t\tWriteErrors: []BulkWriteError{\n\t\t\t\t\t{WriteError: WriteError{Code: 1}},\n\t\t\t\t\t{WriteError: WriteError{Code: 2}},\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: []int{1, 2},\n\t\t},\n\t\t{\n\t\t\tname: \"BulkWriteException with write concern error\",\n\t\t\tinput: BulkWriteException{\n\t\t\t\tWriteErrors: []BulkWriteError{\n\t\t\t\t\t{WriteError: WriteError{Code: 1}},\n\t\t\t\t\t{WriteError: WriteError{Code: 2}},\n\t\t\t\t},\n\t\t\t\tWriteConcernError: &WriteConcernError{Code: 3},\n\t\t\t},\n\t\t\twant: []int{1, 2, 3},\n\t\t},\n\t\t{\n\t\t\tname:  \"driver.Error wraps to CommandError\",\n\t\t\tinput: driver.Error{Code: 1, Message: \"shutdown in progress\"},\n\t\t\twant:  []int{1},\n\t\t},\n\t\t{\n\t\t\tname:  \"wrapped driver.Error\",\n\t\t\tinput: fmt.Errorf(\"context: %w\", driver.Error{Code: 1, Message: \"ExceededTimeLimit\"}),\n\t\t\twant:  []int{1},\n\t\t},\n\t\t{\n\t\t\tinput: wrapErrors(driver.Error{Code: 1, Message: \"Custom error\"}),\n\t\t\tname:  \"double wrapped driver.Error\",\n\t\t\twant:  []int{1},\n\t\t},\n\t\t{\n\t\t\tname:  \"already wrapped CommandError\",\n\t\t\tinput: CommandError{Code: 1},\n\t\t\twant:  []int{1},\n\t\t},\n\t\t{\n\t\t\tname:  \"CommandError wrapped in fmt.Errorf\",\n\t\t\tinput: fmt.Errorf(\"operation failed: %w\", CommandError{Code: 1}),\n\t\t\twant:  []int{1},\n\t\t},\n\t\t{\n\t\t\tname: \"WriteException wrapped in fmt.Errorf\",\n\t\t\tinput: fmt.Errorf(\"batch failed: %w\", WriteException{\n\t\t\t\tWriteErrors: WriteErrors{{Code: 1}, {Code: 2}},\n\t\t\t}),\n\t\t\twant: []int{1, 2},\n\t\t},\n\t\t{\n\t\t\tname: \"BulkWriteException with all error types\",\n\t\t\tinput: BulkWriteException{\n\t\t\t\tWriteErrors: []BulkWriteError{\n\t\t\t\t\t{WriteError: WriteError{Code: 1}},\n\t\t\t\t\t{WriteError: WriteError{Code: 2}},\n\t\t\t\t\t{WriteError: WriteError{Code: 1}},\n\t\t\t\t},\n\t\t\t\tWriteConcernError: &WriteConcernError{Code: 2},\n\t\t\t},\n\t\t\twant: []int{1, 2, 1, 2},\n\t\t},\n\t\t{\n\t\t\tname:  \"driver.Error with multiple fields\",\n\t\t\tinput: driver.Error{Code: 1, Message: \"test\", Name: \"TestError\", Labels: []string{\"label1\"}},\n\t\t\twant:  []int{1},\n\t\t},\n\t\t{\n\t\t\tname:  \"topology.ErrTopologyClosed converts to ErrClientDisconnected\",\n\t\t\tinput: topology.ErrTopologyClosed,\n\t\t\twant:  []int{},\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\trequire.Equal(t, tt.want, ErrorCodes(tt.input))\n\t\t})\n\t}\n}\n\nfunc TestErrorCodesNoDoubleWrapping(t *testing.T) {\n\tdriverErr := driver.Error{Code: 1, Message: \"test error\"}\n\n\t// Wrap it once\n\twrapped := wrapErrors(driverErr)\n\tcmdErr, ok := wrapped.(CommandError)\n\trequire.True(t, ok, \"wrapErrors should return CommandError\")\n\trequire.Equal(t, int32(1), cmdErr.Code)\n\n\t// Call ErrorCodes on the wrapped error\n\tcodes := ErrorCodes(wrapped)\n\trequire.Equal(t, []int{1}, codes)\n\n\t// The wrapped error's structure should not have changed\n\tcmdErrAfter, ok := wrapped.(CommandError)\n\trequire.True(t, ok, \"error should still be CommandError\")\n\trequire.Equal(t, cmdErr.Code, cmdErrAfter.Code)\n\n\t// Verify that calling ErrorCodes again gives same result\n\tcodes2 := ErrorCodes(wrapped)\n\trequire.Equal(t, codes, codes2)\n}\n"
  },
  {
    "path": "mongo/gridfs_bucket.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage mongo\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/csot\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/mongoutil\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/readconcern\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/readpref\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/writeconcern\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n)\n\n// TODO: add sessions options\n\n// DefaultGridFSChunkSize is the default size of each file chunk.\nconst DefaultGridFSChunkSize int32 = 255 * 1024 // 255 KiB\n\n// ErrFileNotFound occurs if a user asks to download a file with a file ID that isn't found in the files collection.\nvar ErrFileNotFound = errors.New(\"file with given parameters not found\")\n\n// ErrMissingGridFSChunkSize occurs when downloading a file if the files\n// collection document is missing the \"chunkSize\" field.\nvar ErrMissingGridFSChunkSize = errors.New(\"files collection document does not contain a 'chunkSize' field\")\n\n// GridFSBucket represents a GridFS bucket.\ntype GridFSBucket struct {\n\tdb         *Database\n\tchunksColl *Collection // collection to store file chunks\n\tfilesColl  *Collection // collection to store file metadata\n\n\tname      string\n\tchunkSize int32\n\twc        *writeconcern.WriteConcern\n\trc        *readconcern.ReadConcern\n\trp        *readpref.ReadPref\n\n\tfirstWriteDone bool\n\treadBuf        []byte\n\twriteBuf       []byte\n}\n\n// upload contains options to upload a file to a bucket.\ntype upload struct {\n\tchunkSize int32\n\tmetadata  bson.D\n}\n\n// OpenUploadStream creates a file ID new upload stream for a file given the\n// filename.\n//\n// The context provided to this method controls the entire lifetime of an\n// upload stream io.Writer. If the context does set a deadline, then the\n// client-level timeout will be used to cap the lifetime of the stream.\nfunc (b *GridFSBucket) OpenUploadStream(\n\tctx context.Context,\n\tfilename string,\n\topts ...options.Lister[options.GridFSUploadOptions],\n) (*GridFSUploadStream, error) {\n\treturn b.OpenUploadStreamWithID(ctx, bson.NewObjectID(), filename, opts...)\n}\n\n// OpenUploadStreamWithID creates a new upload stream for a file given the file\n// ID and filename.\n//\n// The context provided to this method controls the entire lifetime of an\n// upload stream io.Writer. If the context does set a deadline, then the\n// client-level timeout will be used to cap the lifetime of the stream.\nfunc (b *GridFSBucket) OpenUploadStreamWithID(\n\tctx context.Context,\n\tfileID any,\n\tfilename string,\n\topts ...options.Lister[options.GridFSUploadOptions],\n) (*GridFSUploadStream, error) {\n\tctx, cancel := csot.WithTimeout(ctx, b.db.client.timeout)\n\n\tif err := b.checkFirstWrite(ctx); err != nil {\n\t\treturn nil, err\n\t}\n\n\tupload, err := b.parseGridFSUploadOptions(opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn newUploadStream(ctx, cancel, upload, fileID, filename, b.chunksColl, b.filesColl), nil\n}\n\n// UploadFromStream creates a fileID and uploads a file given a source stream.\n//\n// If this upload requires a custom write deadline to be set on the bucket, it\n// cannot be done concurrently with other write operations operations on this\n// bucket that also require a custom deadline.\n//\n// The context provided to this method controls the entire lifetime of an\n// upload stream io.Writer. If the context does set a deadline, then the\n// client-level timeout will be used to cap the lifetime of the stream.\nfunc (b *GridFSBucket) UploadFromStream(\n\tctx context.Context,\n\tfilename string,\n\tsource io.Reader,\n\topts ...options.Lister[options.GridFSUploadOptions],\n) (bson.ObjectID, error) {\n\tfileID := bson.NewObjectID()\n\terr := b.UploadFromStreamWithID(ctx, fileID, filename, source, opts...)\n\treturn fileID, err\n}\n\n// UploadFromStreamWithID uploads a file given a source stream.\n//\n// If this upload requires a custom write deadline to be set on the bucket, it\n// cannot be done concurrently with other write operations operations on this\n// bucket that also require a custom deadline.\n//\n// The context provided to this method controls the entire lifetime of an\n// upload stream io.Writer. If the context does set a deadline, then the\n// client-level timeout will be used to cap the lifetime of the stream.\nfunc (b *GridFSBucket) UploadFromStreamWithID(\n\tctx context.Context,\n\tfileID any,\n\tfilename string,\n\tsource io.Reader,\n\topts ...options.Lister[options.GridFSUploadOptions],\n) error {\n\tctx, cancel := csot.WithTimeout(ctx, b.db.client.timeout)\n\tdefer cancel()\n\n\tus, err := b.OpenUploadStreamWithID(ctx, fileID, filename, opts...)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor {\n\t\tn, err := source.Read(b.readBuf)\n\t\tif err != nil && err != io.EOF {\n\t\t\t_ = us.Abort() // upload considered aborted if source stream returns an error\n\t\t\treturn err\n\t\t}\n\n\t\tif n > 0 {\n\t\t\t_, err := us.Write(b.readBuf[:n])\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\n\t\tif n == 0 || err == io.EOF {\n\t\t\tbreak\n\t\t}\n\t}\n\n\treturn us.Close()\n}\n\n// OpenDownloadStream creates a stream from which the contents of the file can\n// be read.\n//\n// The context provided to this method controls the entire lifetime of an\n// upload stream io.Writer. If the context does set a deadline, then the\n// client-level timeout will be used to cap the lifetime of the stream.\nfunc (b *GridFSBucket) OpenDownloadStream(ctx context.Context, fileID any) (*GridFSDownloadStream, error) {\n\treturn b.openDownloadStream(ctx, bson.D{{\"_id\", fileID}})\n}\n\n// DownloadToStream downloads the file with the specified fileID and writes it\n// to the provided io.Writer. Returns the number of bytes written to the stream\n// and an error, or nil if there was no error.\n//\n// If this download requires a custom read deadline to be set on the bucket, it\n// cannot be done concurrently with other read operations operations on this\n// bucket that also require a custom deadline.\n//\n// The context provided to this method controls the entire lifetime of an\n// upload stream io.Writer. If the context does set a deadline, then the\n// client-level timeout will be used to cap the lifetime of the stream.\nfunc (b *GridFSBucket) DownloadToStream(ctx context.Context, fileID any, stream io.Writer) (int64, error) {\n\tds, err := b.OpenDownloadStream(ctx, fileID)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\treturn b.downloadToStream(ds, stream)\n}\n\n// OpenDownloadStreamByName opens a download stream for the file with the given\n// filename.\n//\n// The context provided to this method controls the entire lifetime of an\n// upload stream io.Writer. If the context does set a deadline, then the\n// client-level timeout will be used to cap the lifetime of the stream.\nfunc (b *GridFSBucket) OpenDownloadStreamByName(\n\tctx context.Context,\n\tfilename string,\n\topts ...options.Lister[options.GridFSNameOptions],\n) (*GridFSDownloadStream, error) {\n\targs, err := mongoutil.NewOptions[options.GridFSNameOptions](opts...)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to construct options from builder: %w\", err)\n\t}\n\n\tnumSkip := options.DefaultRevision\n\tif args.Revision != nil {\n\t\tnumSkip = *args.Revision\n\t}\n\n\tvar sortOrder int32 = 1\n\n\tif numSkip < 0 {\n\t\tsortOrder = -1\n\t\tnumSkip = (-1 * numSkip) - 1\n\t}\n\n\tfindOpts := options.FindOne().SetSkip(int64(numSkip)).SetSort(bson.D{{\"uploadDate\", sortOrder}})\n\n\treturn b.openDownloadStream(ctx, bson.D{{\"filename\", filename}}, findOpts)\n}\n\n// DownloadToStreamByName downloads the file with the given name to the given\n// io.Writer.\n//\n// If this download requires a custom read deadline to be set on the bucket, it\n// cannot be done concurrently with other read operations operations on this\n// bucket that also require a custom deadline.\n//\n// The context provided to this method controls the entire lifetime of an\n// upload stream io.Writer. If the context does set a deadline, then the\n// client-level timeout will be used to cap the lifetime of the stream.\nfunc (b *GridFSBucket) DownloadToStreamByName(\n\tctx context.Context,\n\tfilename string,\n\tstream io.Writer,\n\topts ...options.Lister[options.GridFSNameOptions],\n) (int64, error) {\n\tds, err := b.OpenDownloadStreamByName(ctx, filename, opts...)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\treturn b.downloadToStream(ds, stream)\n}\n\n// Delete deletes all chunks and metadata associated with the file with the\n// given file ID and runs the underlying delete operations with the provided\n// context.\nfunc (b *GridFSBucket) Delete(ctx context.Context, fileID any) error {\n\tctx, cancel := csot.WithTimeout(ctx, b.db.client.timeout)\n\tdefer cancel()\n\n\tres, err := b.filesColl.DeleteOne(ctx, bson.D{{\"_id\", fileID}})\n\tif err == nil && res.DeletedCount == 0 {\n\t\terr = ErrFileNotFound\n\t}\n\tif err != nil {\n\t\t_ = b.deleteChunks(ctx, fileID) // Can attempt to delete chunks even if no docs in files collection matched.\n\t\treturn err\n\t}\n\n\treturn b.deleteChunks(ctx, fileID)\n}\n\n// Find returns the files collection documents that match the given filter and\n// runs the underlying find query with the provided context.\nfunc (b *GridFSBucket) Find(\n\tctx context.Context,\n\tfilter any,\n\topts ...options.Lister[options.GridFSFindOptions],\n) (*Cursor, error) {\n\targs, err := mongoutil.NewOptions[options.GridFSFindOptions](opts...)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to construct options from builder: %w\", err)\n\t}\n\n\tfind := options.Find()\n\tif args.AllowDiskUse != nil {\n\t\tfind.SetAllowDiskUse(*args.AllowDiskUse)\n\t}\n\tif args.BatchSize != nil {\n\t\tfind.SetBatchSize(*args.BatchSize)\n\t}\n\tif args.Limit != nil {\n\t\tfind.SetLimit(int64(*args.Limit))\n\t}\n\tif args.NoCursorTimeout != nil {\n\t\tfind.SetNoCursorTimeout(*args.NoCursorTimeout)\n\t}\n\tif args.Skip != nil {\n\t\tfind.SetSkip(int64(*args.Skip))\n\t}\n\tif args.Sort != nil {\n\t\tfind.SetSort(args.Sort)\n\t}\n\n\treturn b.filesColl.Find(ctx, filter, find)\n}\n\n// Rename renames the stored file with the specified file ID.\nfunc (b *GridFSBucket) Rename(ctx context.Context, fileID any, newFilename string) error {\n\tres, err := b.filesColl.UpdateOne(ctx,\n\t\tbson.D{{\"_id\", fileID}},\n\t\tbson.D{{\"$set\", bson.D{{\"filename\", newFilename}}}},\n\t)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif res.MatchedCount == 0 {\n\t\treturn ErrFileNotFound\n\t}\n\n\treturn nil\n}\n\n// Drop drops the files and chunks collections associated with this bucket and\n// runs the drop operations with the provided context.\nfunc (b *GridFSBucket) Drop(ctx context.Context) error {\n\tctx, cancel := csot.WithTimeout(ctx, b.db.client.timeout)\n\tdefer cancel()\n\n\terr := b.filesColl.Drop(ctx)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn b.chunksColl.Drop(ctx)\n}\n\n// GetFilesCollection returns a handle to the collection that stores the file documents for this bucket.\nfunc (b *GridFSBucket) GetFilesCollection() *Collection {\n\treturn b.filesColl\n}\n\n// GetChunksCollection returns a handle to the collection that stores the file chunks for this bucket.\nfunc (b *GridFSBucket) GetChunksCollection() *Collection {\n\treturn b.chunksColl\n}\n\nfunc (b *GridFSBucket) openDownloadStream(\n\tctx context.Context,\n\tfilter any,\n\topts ...options.Lister[options.FindOneOptions],\n) (*GridFSDownloadStream, error) {\n\tctx, cancel := csot.WithTimeout(ctx, b.db.client.timeout)\n\n\tresult := b.filesColl.FindOne(ctx, filter, opts...)\n\n\t// Unmarshal the data into a File instance, which can be passed to newGridFSDownloadStream. The _id value has to be\n\t// parsed out separately because \"_id\" will not match the File.ID field and we want to avoid exposing BSON tags\n\t// in the File type. After parsing it, use RawValue.Unmarshal to ensure File.ID is set to the appropriate value.\n\tvar resp findFileResponse\n\tif err := result.Decode(&resp); err != nil {\n\t\tif errors.Is(err, ErrNoDocuments) {\n\t\t\treturn nil, ErrFileNotFound\n\t\t}\n\n\t\treturn nil, fmt.Errorf(\"error decoding files collection document: %w\", err)\n\t}\n\n\tfoundFile := newFileFromResponse(resp)\n\n\tif foundFile.Length == 0 {\n\t\treturn newGridFSDownloadStream(ctx, cancel, nil, foundFile.ChunkSize, foundFile), nil\n\t}\n\n\t// For a file with non-zero length, chunkSize must exist so we know what size to expect when downloading chunks.\n\tif foundFile.ChunkSize == 0 {\n\t\treturn nil, ErrMissingGridFSChunkSize\n\t}\n\n\tchunksCursor, err := b.findChunks(ctx, foundFile.ID)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// The chunk size can be overridden for individual files, so the expected chunk size should be the \"chunkSize\"\n\t// field from the files collection document, not the bucket's chunk size.\n\treturn newGridFSDownloadStream(ctx, cancel, chunksCursor, foundFile.ChunkSize, foundFile), nil\n}\n\nfunc (b *GridFSBucket) downloadToStream(ds *GridFSDownloadStream, stream io.Writer) (int64, error) {\n\tcopied, err := io.Copy(stream, ds)\n\tif err != nil {\n\t\t_ = ds.Close()\n\t\treturn 0, err\n\t}\n\n\treturn copied, ds.Close()\n}\n\nfunc (b *GridFSBucket) deleteChunks(ctx context.Context, fileID any) error {\n\t_, err := b.chunksColl.DeleteMany(ctx, bson.D{{\"files_id\", fileID}})\n\treturn err\n}\n\nfunc (b *GridFSBucket) findChunks(ctx context.Context, fileID any) (*Cursor, error) {\n\tchunksCursor, err := b.chunksColl.Find(ctx,\n\t\tbson.D{{\"files_id\", fileID}},\n\t\toptions.Find().SetSort(bson.D{{\"n\", 1}})) // sort by chunk index\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn chunksCursor, nil\n}\n\n// returns true if the 2 index documents are equal\nfunc numericalIndexDocsEqual(expected, actual bsoncore.Document) (bool, error) {\n\tif bytes.Equal(expected, actual) {\n\t\treturn true, nil\n\t}\n\n\tactualElems, err := actual.Elements()\n\tif err != nil {\n\t\treturn false, err\n\t}\n\texpectedElems, err := expected.Elements()\n\tif err != nil {\n\t\treturn false, err\n\t}\n\n\tif len(actualElems) != len(expectedElems) {\n\t\treturn false, nil\n\t}\n\n\tfor idx, expectedElem := range expectedElems {\n\t\tactualElem := actualElems[idx]\n\t\tif actualElem.Key() != expectedElem.Key() {\n\t\t\treturn false, nil\n\t\t}\n\n\t\tactualVal := actualElem.Value()\n\t\texpectedVal := expectedElem.Value()\n\t\tactualInt, actualOK := actualVal.AsInt64OK()\n\t\texpectedInt, expectedOK := expectedVal.AsInt64OK()\n\n\t\t// GridFS indexes always have numeric values\n\t\tif !actualOK || !expectedOK {\n\t\t\treturn false, nil\n\t\t}\n\n\t\tif actualInt != expectedInt {\n\t\t\treturn false, nil\n\t\t}\n\t}\n\treturn true, nil\n}\n\n// Create an index if it doesn't already exist\nfunc createNumericalIndexIfNotExists(ctx context.Context, iv IndexView, model IndexModel) error {\n\tc, err := iv.List(ctx)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer func() {\n\t\t_ = c.Close(ctx)\n\t}()\n\n\tmodelKeysBytes, err := bson.Marshal(model.Keys)\n\tif err != nil {\n\t\treturn err\n\t}\n\tmodelKeysDoc := bsoncore.Document(modelKeysBytes)\n\n\tfor c.Next(ctx) {\n\t\tkeyElem, err := c.Current.LookupErr(\"key\")\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tkeyElemDoc := keyElem.Document()\n\n\t\tfound, err := numericalIndexDocsEqual(modelKeysDoc, bsoncore.Document(keyElemDoc))\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif found {\n\t\t\treturn nil\n\t\t}\n\t}\n\n\t_, err = iv.CreateOne(ctx, model)\n\treturn err\n}\n\n// create indexes on the files and chunks collection if needed\nfunc (b *GridFSBucket) createIndexes(ctx context.Context) error {\n\t// must use primary read pref mode to check if files coll empty\n\tcloned := b.filesColl.Clone(options.Collection().SetReadPreference(readpref.Primary()))\n\n\tdocRes := cloned.FindOne(ctx, bson.D{}, options.FindOne().SetProjection(bson.D{{\"_id\", 1}}))\n\n\t_, err := docRes.Raw()\n\tif !errors.Is(err, ErrNoDocuments) {\n\t\t// nil, or error that occurred during the FindOne operation\n\t\treturn err\n\t}\n\n\tfilesIv := b.filesColl.Indexes()\n\tchunksIv := b.chunksColl.Indexes()\n\n\tfilesModel := IndexModel{\n\t\tKeys: bson.D{\n\t\t\t{\"filename\", int32(1)},\n\t\t\t{\"uploadDate\", int32(1)},\n\t\t},\n\t}\n\n\tchunksModel := IndexModel{\n\t\tKeys: bson.D{\n\t\t\t{\"files_id\", int32(1)},\n\t\t\t{\"n\", int32(1)},\n\t\t},\n\t\tOptions: options.Index().SetUnique(true),\n\t}\n\n\tif err = createNumericalIndexIfNotExists(ctx, filesIv, filesModel); err != nil {\n\t\treturn err\n\t}\n\treturn createNumericalIndexIfNotExists(ctx, chunksIv, chunksModel)\n}\n\nfunc (b *GridFSBucket) checkFirstWrite(ctx context.Context) error {\n\tif !b.firstWriteDone {\n\t\t// before the first write operation, must determine if files collection is empty\n\t\t// if so, create indexes if they do not already exist\n\n\t\tif err := b.createIndexes(ctx); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tb.firstWriteDone = true\n\t}\n\n\treturn nil\n}\n\nfunc (b *GridFSBucket) parseGridFSUploadOptions(opts ...options.Lister[options.GridFSUploadOptions]) (*upload, error) {\n\tupload := &upload{\n\t\tchunkSize: b.chunkSize, // upload chunk size defaults to bucket's value\n\t}\n\n\targs, err := mongoutil.NewOptions[options.GridFSUploadOptions](opts...)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to construct options from builder: %w\", err)\n\t}\n\n\tif args.ChunkSizeBytes != nil {\n\t\tupload.chunkSize = *args.ChunkSizeBytes\n\t}\n\tif args.Registry == nil {\n\t\targs.Registry = defaultRegistry\n\t}\n\tif args.Metadata != nil {\n\t\t// TODO(GODRIVER-2726): Replace with marshal() and unmarshal() once the\n\t\t// TODO gridfs package is merged into the mongo package.\n\t\tbuf := new(bytes.Buffer)\n\t\tvw := bson.NewDocumentWriter(buf)\n\t\tenc := bson.NewEncoder(vw)\n\t\tenc.SetRegistry(args.Registry)\n\t\terr := enc.Encode(args.Metadata)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tvar doc bson.D\n\t\tdec := bson.NewDecoder(bson.NewDocumentReader(bytes.NewReader(buf.Bytes())))\n\t\tdec.SetRegistry(args.Registry)\n\t\tunMarErr := dec.Decode(&doc)\n\t\tif unMarErr != nil {\n\t\t\treturn nil, unMarErr\n\t\t}\n\t\tupload.metadata = doc\n\t}\n\n\treturn upload, nil\n}\n"
  },
  {
    "path": "mongo/gridfs_bucket_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2023-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage mongo\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/integtest\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n)\n\nfunc TestBucket_openDownloadStream(t *testing.T) {\n\ttests := []struct {\n\t\tname   string\n\t\tfilter any\n\t\terr    error\n\t}{\n\t\t{\n\t\t\tname:   \"nil filter\",\n\t\t\tfilter: nil,\n\t\t\terr:    ErrNilDocument,\n\t\t},\n\t\t{\n\t\t\tname:   \"nonmatching filter\",\n\t\t\tfilter: bson.D{{\"x\", 1}},\n\t\t\terr:    ErrFileNotFound,\n\t\t},\n\t}\n\n\tcs := integtest.ConnString(t)\n\tclientOpts := options.Client().ApplyURI(cs.Original)\n\n\tintegtest.AddTestServerAPIVersion(clientOpts)\n\n\tclient, err := Connect(clientOpts)\n\tassert.Nil(t, err, \"Connect error: %v\", err)\n\n\tdb := client.Database(\"bucket\")\n\n\tfor _, test := range tests {\n\t\tt.Run(test.name, func(t *testing.T) {\n\t\t\tbucket := db.GridFSBucket()\n\t\t\t_, err = bucket.openDownloadStream(context.Background(), test.filter)\n\t\t\tassert.ErrorIs(t, err, test.err)\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "mongo/gridfs_download_stream.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage mongo\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"io\"\n\t\"math\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n)\n\n// ErrMissingChunk indicates that the number of chunks read from the server is\n// less than expected. This error is specific to GridFS operations.\nvar ErrMissingChunk = errors.New(\"EOF missing one or more chunks\")\n\n// ErrWrongSize is used when the chunk retrieved from the server does not have\n// the expected size. This error is specific to GridFS operations.\nvar ErrWrongSize = errors.New(\"chunk size does not match expected size\")\n\nvar errNoMoreChunks = errors.New(\"no more chunks remaining\")\n\n// GridFSDownloadStream is a io.Reader that can be used to download a file from a GridFS bucket.\ntype GridFSDownloadStream struct {\n\tnumChunks     int32\n\tchunkSize     int32\n\tcursor        *Cursor\n\tdone          bool\n\tclosed        bool\n\tbuffer        []byte // store up to 1 chunk if the user provided buffer isn't big enough\n\tbufferStart   int\n\tbufferEnd     int\n\texpectedChunk int32 // index of next expected chunk\n\tfileLen       int64\n\tctx           context.Context\n\tcancel        context.CancelFunc\n\n\t// The pointer returned by GetFile. This should not be used in the actual GridFSDownloadStream code outside of the\n\t// newGridFSDownloadStream constructor because the values can be mutated by the user after calling GetFile. Instead,\n\t// any values needed in the code should be stored separately and copied over in the constructor.\n\tfile *GridFSFile\n}\n\n// GridFSFile represents a file stored in GridFS. This type can be used to\n// access file information when downloading using the\n// GridFSDownloadStream.GetFile method.\ntype GridFSFile struct {\n\t// ID is the file's ID. This will match the file ID specified when uploading the file. If an upload helper that\n\t// does not require a file ID was used, this field will be a bson.ObjectID.\n\tID any\n\n\t// Length is the length of this file in bytes.\n\tLength int64\n\n\t// ChunkSize is the maximum number of bytes for each chunk in this file.\n\tChunkSize int32\n\n\t// UploadDate is the time this file was added to GridFS in UTC. This field is set by the driver and is not configurable.\n\t// The Metadata field can be used to store a custom date.\n\tUploadDate time.Time\n\n\t// Name is the name of this file.\n\tName string\n\n\t// Metadata is additional data that was specified when creating this file. This field can be unmarshalled into a\n\t// custom type using the bson.Unmarshal family of functions.\n\tMetadata bson.Raw\n}\n\nvar _ bson.Unmarshaler = &GridFSFile{}\n\n// findFileResponse is a temporary type used to unmarshal documents from the\n// files collection and can be transformed into a File instance. This type\n// exists to avoid adding BSON struct tags to the exported File type.\ntype findFileResponse struct {\n\tID         any       `bson:\"_id\"`\n\tLength     int64     `bson:\"length\"`\n\tChunkSize  int32     `bson:\"chunkSize\"`\n\tUploadDate time.Time `bson:\"uploadDate\"`\n\tName       string    `bson:\"filename\"`\n\tMetadata   bson.Raw  `bson:\"metadata\"`\n}\n\nfunc newFileFromResponse(resp findFileResponse) *GridFSFile {\n\treturn &GridFSFile{\n\t\tID:         resp.ID,\n\t\tLength:     resp.Length,\n\t\tChunkSize:  resp.ChunkSize,\n\t\tUploadDate: resp.UploadDate,\n\t\tName:       resp.Name,\n\t\tMetadata:   resp.Metadata,\n\t}\n}\n\n// UnmarshalBSON implements the bson.Unmarshaler interface.\nfunc (f *GridFSFile) UnmarshalBSON(data []byte) error {\n\tvar temp findFileResponse\n\tif err := bson.Unmarshal(data, &temp); err != nil {\n\t\treturn err\n\t}\n\n\tf.ID = temp.ID\n\tf.Length = temp.Length\n\tf.ChunkSize = temp.ChunkSize\n\tf.UploadDate = temp.UploadDate\n\tf.Name = temp.Name\n\tf.Metadata = temp.Metadata\n\n\treturn nil\n}\n\nfunc newGridFSDownloadStream(\n\tctx context.Context,\n\tcancel context.CancelFunc,\n\tcursor *Cursor,\n\tchunkSize int32,\n\tfile *GridFSFile,\n) *GridFSDownloadStream {\n\tnumChunks := int32(math.Ceil(float64(file.Length) / float64(chunkSize)))\n\n\treturn &GridFSDownloadStream{\n\t\tnumChunks: numChunks,\n\t\tchunkSize: chunkSize,\n\t\tcursor:    cursor,\n\t\tbuffer:    make([]byte, chunkSize),\n\t\tdone:      cursor == nil,\n\t\tfileLen:   file.Length,\n\t\tfile:      file,\n\t\tctx:       ctx,\n\t\tcancel:    cancel,\n\t}\n}\n\n// Close closes this download stream.\nfunc (ds *GridFSDownloadStream) Close() error {\n\tdefer func() {\n\t\tif ds.cancel != nil {\n\t\t\tds.cancel()\n\t\t}\n\t}()\n\n\tif ds.closed {\n\t\treturn ErrStreamClosed\n\t}\n\n\tds.closed = true\n\tif ds.cursor != nil {\n\t\treturn ds.cursor.Close(context.Background())\n\t}\n\treturn nil\n}\n\n// Read reads the file from the server and writes it to a destination byte slice.\nfunc (ds *GridFSDownloadStream) Read(p []byte) (int, error) {\n\tif ds.closed {\n\t\treturn 0, ErrStreamClosed\n\t}\n\n\tif ds.done {\n\t\treturn 0, io.EOF\n\t}\n\n\tbytesCopied := 0\n\tvar err error\n\tfor bytesCopied < len(p) {\n\t\tif ds.bufferStart >= ds.bufferEnd {\n\t\t\t// Buffer is empty and can load in data from new chunk.\n\t\t\terr = ds.fillBuffer(ds.ctx)\n\t\t\tif err != nil {\n\t\t\t\tif errors.Is(err, errNoMoreChunks) {\n\t\t\t\t\tif bytesCopied == 0 {\n\t\t\t\t\t\tds.done = true\n\t\t\t\t\t\treturn 0, io.EOF\n\t\t\t\t\t}\n\t\t\t\t\treturn bytesCopied, nil\n\t\t\t\t}\n\t\t\t\treturn bytesCopied, err\n\t\t\t}\n\t\t}\n\n\t\tcopied := copy(p[bytesCopied:], ds.buffer[ds.bufferStart:ds.bufferEnd])\n\n\t\tbytesCopied += copied\n\t\tds.bufferStart += copied\n\t}\n\n\treturn len(p), nil\n}\n\n// Skip skips a given number of bytes in the file.\nfunc (ds *GridFSDownloadStream) Skip(skip int64) (int64, error) {\n\tif ds.closed {\n\t\treturn 0, ErrStreamClosed\n\t}\n\n\tif ds.done {\n\t\treturn 0, nil\n\t}\n\n\tvar skipped int64\n\tvar err error\n\n\tfor skipped < skip {\n\t\tif ds.bufferStart >= ds.bufferEnd {\n\t\t\t// Buffer is empty and can load in data from new chunk.\n\t\t\terr = ds.fillBuffer(ds.ctx)\n\t\t\tif err != nil {\n\t\t\t\tif errors.Is(err, errNoMoreChunks) {\n\t\t\t\t\treturn skipped, nil\n\t\t\t\t}\n\t\t\t\treturn skipped, err\n\t\t\t}\n\t\t}\n\n\t\ttoSkip := skip - skipped\n\t\t// Cap the amount to skip to the remaining bytes in the buffer to be consumed.\n\t\tbufferRemaining := ds.bufferEnd - ds.bufferStart\n\t\tif toSkip > int64(bufferRemaining) {\n\t\t\ttoSkip = int64(bufferRemaining)\n\t\t}\n\n\t\tskipped += toSkip\n\t\tds.bufferStart += int(toSkip)\n\t}\n\n\treturn skip, nil\n}\n\n// GetFile returns a File object representing the file being downloaded.\nfunc (ds *GridFSDownloadStream) GetFile() *GridFSFile {\n\treturn ds.file\n}\n\nfunc (ds *GridFSDownloadStream) fillBuffer(ctx context.Context) error {\n\tif !ds.cursor.Next(ctx) {\n\t\tds.done = true\n\t\t// Check for cursor error, otherwise there are no more chunks.\n\t\tif ds.cursor.Err() != nil {\n\t\t\t_ = ds.cursor.Close(ctx)\n\t\t\treturn ds.cursor.Err()\n\t\t}\n\t\t// If there are no more chunks, but we didn't read the expected number of chunks, return an\n\t\t// ErrMissingChunk error to indicate that we're missing chunks at the end of the file.\n\t\tif ds.expectedChunk != ds.numChunks {\n\t\t\treturn ErrMissingChunk\n\t\t}\n\t\treturn errNoMoreChunks\n\t}\n\n\tchunkIndex, err := ds.cursor.Current.LookupErr(\"n\")\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tvar chunkIndexInt32 int32\n\tif chunkIndexInt64, ok := chunkIndex.Int64OK(); ok {\n\t\tchunkIndexInt32 = int32(chunkIndexInt64)\n\t} else {\n\t\tchunkIndexInt32 = chunkIndex.Int32()\n\t}\n\n\tif chunkIndexInt32 != ds.expectedChunk {\n\t\treturn ErrMissingChunk\n\t}\n\n\tds.expectedChunk++\n\tdata, err := ds.cursor.Current.LookupErr(\"data\")\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t_, dataBytes := data.Binary()\n\tcopied := copy(ds.buffer, dataBytes)\n\n\tbytesLen := int32(len(dataBytes))\n\tif ds.expectedChunk == ds.numChunks {\n\t\t// final chunk can be fewer than ds.chunkSize bytes\n\t\tbytesDownloaded := int64(ds.chunkSize) * (int64(ds.expectedChunk) - int64(1))\n\t\tbytesRemaining := ds.fileLen - bytesDownloaded\n\n\t\tif int64(bytesLen) != bytesRemaining {\n\t\t\treturn ErrWrongSize\n\t\t}\n\t} else if bytesLen != ds.chunkSize {\n\t\t// all intermediate chunks must have size ds.chunkSize\n\t\treturn ErrWrongSize\n\t}\n\n\tds.bufferStart = 0\n\tds.bufferEnd = copied\n\n\treturn nil\n}\n"
  },
  {
    "path": "mongo/gridfs_examples_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage mongo_test\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"fmt\"\n\t\"io\"\n\t\"log\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n)\n\nfunc ExampleGridFSBucket_OpenUploadStream() {\n\tvar fileContent []byte\n\tvar bucket *mongo.GridFSBucket\n\n\t// Specify the Metadata option to include a \"metadata\" field in the files\n\t// collection document.\n\tuploadOpts := options.GridFSUpload().\n\t\tSetMetadata(bson.D{{\"metadata tag\", \"tag\"}})\n\n\t// Use WithContext to force a timeout if the upload does not succeed in\n\t// 2 seconds.\n\tctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)\n\tdefer cancel()\n\n\tuploadStream, err := bucket.OpenUploadStream(ctx, \"filename\", uploadOpts)\n\tif err != nil {\n\t\tlog.Panic(err)\n\t}\n\tdefer func() {\n\t\tif err = uploadStream.Close(); err != nil {\n\t\t\tlog.Panic(err)\n\t\t}\n\t}()\n\n\tif _, err = uploadStream.Write(fileContent); err != nil {\n\t\tlog.Panic(err)\n\t}\n}\n\nfunc ExampleGridFSBucket_UploadFromStream() {\n\tvar fileContent []byte\n\tvar bucket *mongo.GridFSBucket\n\n\t// Specify the Metadata option to include a \"metadata\" field in the files\n\t// collection document.\n\tuploadOpts := options.GridFSUpload().\n\t\tSetMetadata(bson.D{{\"metadata tag\", \"tag\"}})\n\tfileID, err := bucket.UploadFromStream(\n\t\tcontext.Background(),\n\t\t\"filename\",\n\t\tbytes.NewBuffer(fileContent),\n\t\tuploadOpts)\n\tif err != nil {\n\t\tlog.Panic(err)\n\t}\n\n\tfmt.Printf(\"new file created with ID %s\", fileID)\n}\n\nfunc ExampleGridFSBucket_OpenDownloadStream() {\n\tvar bucket *mongo.GridFSBucket\n\tvar fileID bson.ObjectID\n\n\t// Use WithContext to force a timeout if the download does not succeed in\n\t// 2 seconds.\n\tctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)\n\tdefer cancel()\n\n\tdownloadStream, err := bucket.OpenDownloadStream(ctx, fileID)\n\tif err != nil {\n\t\tlog.Panic(err)\n\t}\n\tdefer func() {\n\t\tif err := downloadStream.Close(); err != nil {\n\t\t\tlog.Panic(err)\n\t\t}\n\t}()\n\n\tfileBuffer := bytes.NewBuffer(nil)\n\tif _, err := io.Copy(fileBuffer, downloadStream); err != nil {\n\t\tlog.Panic(err)\n\t}\n}\n\nfunc ExampleGridFSBucket_DownloadToStream() {\n\tvar bucket *mongo.GridFSBucket\n\tvar fileID bson.ObjectID\n\n\tctx := context.Background()\n\n\tfileBuffer := bytes.NewBuffer(nil)\n\tif _, err := bucket.DownloadToStream(ctx, fileID, fileBuffer); err != nil {\n\t\tlog.Panic(err)\n\t}\n}\n\nfunc ExampleGridFSBucket_Delete() {\n\tvar bucket *mongo.GridFSBucket\n\tvar fileID bson.ObjectID\n\n\tif err := bucket.Delete(context.Background(), fileID); err != nil {\n\t\tlog.Panic(err)\n\t}\n}\n\nfunc ExampleGridFSBucket_Find() {\n\tvar bucket *mongo.GridFSBucket\n\n\t// Specify a filter to find all files with a length greater than 1000 bytes.\n\tfilter := bson.D{\n\t\t{\"length\", bson.D{{\"$gt\", 1000}}},\n\t}\n\tcursor, err := bucket.Find(context.Background(), filter)\n\tif err != nil {\n\t\tlog.Panic(err)\n\t}\n\tdefer func() {\n\t\tif err := cursor.Close(context.TODO()); err != nil {\n\t\t\tlog.Panic(err)\n\t\t}\n\t}()\n\n\ttype gridfsFile struct {\n\t\tName   string `bson:\"filename\"`\n\t\tLength int64  `bson:\"length\"`\n\t}\n\tvar foundFiles []gridfsFile\n\tif err = cursor.All(context.TODO(), &foundFiles); err != nil {\n\t\tlog.Panic(err)\n\t}\n\n\tfor _, file := range foundFiles {\n\t\tfmt.Printf(\"filename: %s, length: %d\\n\", file.Name, file.Length)\n\t}\n}\n\nfunc ExampleGridFSBucket_Rename() {\n\tvar bucket *mongo.GridFSBucket\n\tvar fileID bson.ObjectID\n\n\tctx := context.Background()\n\n\tif err := bucket.Rename(ctx, fileID, \"new file name\"); err != nil {\n\t\tlog.Panic(err)\n\t}\n}\n\nfunc ExampleGridFSBucket_Drop() {\n\tvar bucket *mongo.GridFSBucket\n\n\tif err := bucket.Drop(context.Background()); err != nil {\n\t\tlog.Panic(err)\n\t}\n}\n"
  },
  {
    "path": "mongo/gridfs_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage mongo\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/event\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/integtest\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/mongoutil\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/require\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/readpref\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/writeconcern\"\n)\n\nvar gridfsConnsCheckedOut int\n\nfunc TestGridFS(t *testing.T) {\n\tif testing.Short() {\n\t\tt.Skip(\"skipping integration test in short mode\")\n\t}\n\n\tcs := integtest.ConnString(t)\n\tpoolMonitor := &event.PoolMonitor{\n\t\tEvent: func(evt *event.PoolEvent) {\n\t\t\tswitch evt.Type {\n\t\t\tcase event.ConnectionCheckedOut:\n\t\t\t\tgridfsConnsCheckedOut++\n\t\t\tcase event.ConnectionCheckedIn:\n\t\t\t\tgridfsConnsCheckedOut--\n\t\t\t}\n\t\t},\n\t}\n\tclientOpts := options.Client().\n\t\tApplyURI(cs.Original).\n\t\tSetReadPreference(readpref.Primary()).\n\t\tSetWriteConcern(writeconcern.Majority()).\n\t\tSetPoolMonitor(poolMonitor).\n\t\t// Connect to a single host. For sharded clusters, this will pin to a single mongos, which avoids\n\t\t// non-deterministic versioning errors in the server. This has no effect for replica sets because the driver\n\t\t// will discover the other hosts during SDAM checks.\n\t\tSetHosts(cs.Hosts[:1])\n\n\tintegtest.AddTestServerAPIVersion(clientOpts)\n\n\tclient, err := Connect(clientOpts)\n\tassert.Nil(t, err, \"Connect error: %v\", err)\n\tdb := client.Database(\"gridfs\")\n\tdefer func() {\n\t\tsessions := client.NumberSessionsInProgress()\n\t\tconns := gridfsConnsCheckedOut\n\n\t\t_ = db.Drop(context.Background())\n\t\t_ = client.Disconnect(context.Background())\n\t\tassert.Equal(t, 0, sessions, \"%v sessions checked out\", sessions)\n\t\tassert.Equal(t, 0, conns, \"%v connections checked out\", conns)\n\t}()\n\n\t// Unit tests showing the chunk size is set correctly on the bucket and upload stream objects.\n\tt.Run(\"ChunkSize\", func(t *testing.T) {\n\t\tchunkSizeTests := []struct {\n\t\t\ttestName   string\n\t\t\tbucketOpts *options.BucketOptionsBuilder\n\t\t\tuploadOpts *options.GridFSUploadOptionsBuilder\n\t\t}{\n\t\t\t{\"Default values\", nil, nil},\n\t\t\t{\"Options provided without chunk size\", options.GridFSBucket(), options.GridFSUpload()},\n\t\t\t{\"Bucket chunk size set\", options.GridFSBucket().SetChunkSizeBytes(27), nil},\n\t\t\t{\"Upload stream chunk size set\", nil, options.GridFSUpload().SetChunkSizeBytes(27)},\n\t\t\t{\"Bucket and upload set to different values\", options.GridFSBucket().SetChunkSizeBytes(27), options.GridFSUpload().SetChunkSizeBytes(31)},\n\t\t}\n\n\t\tfor _, tt := range chunkSizeTests {\n\t\t\tt.Run(tt.testName, func(t *testing.T) {\n\t\t\t\tbucket := db.GridFSBucket(tt.bucketOpts)\n\n\t\t\t\tus, err := bucket.OpenUploadStream(context.Background(), \"filename\", tt.uploadOpts)\n\t\t\t\tassert.Nil(t, err, \"OpenUploadStream error: %v\", err)\n\n\t\t\t\tbucketArgs, err := mongoutil.NewOptions[options.BucketOptions](tt.bucketOpts)\n\t\t\t\trequire.NoError(t, err, \"failed to construct options from builder\")\n\n\t\t\t\texpectedBucketChunkSize := DefaultGridFSChunkSize\n\t\t\t\tif tt.bucketOpts != nil && bucketArgs.ChunkSizeBytes != nil {\n\t\t\t\t\texpectedBucketChunkSize = *bucketArgs.ChunkSizeBytes\n\t\t\t\t}\n\t\t\t\tassert.Equal(t, expectedBucketChunkSize, bucket.chunkSize,\n\t\t\t\t\t\"expected chunk size %v, got %v\", expectedBucketChunkSize, bucket.chunkSize)\n\n\t\t\t\tuploadArgs, err := mongoutil.NewOptions[options.GridFSUploadOptions](tt.uploadOpts)\n\t\t\t\trequire.NoError(t, err, \"failed to construct options from builder\")\n\n\t\t\t\texpectedUploadChunkSize := expectedBucketChunkSize\n\t\t\t\tif tt.uploadOpts != nil && uploadArgs.ChunkSizeBytes != nil {\n\t\t\t\t\texpectedUploadChunkSize = *uploadArgs.ChunkSizeBytes\n\t\t\t\t}\n\t\t\t\tassert.Equal(t, expectedUploadChunkSize, us.chunkSize,\n\t\t\t\t\t\"expected chunk size %v, got %v\", expectedUploadChunkSize, us.chunkSize)\n\t\t\t})\n\t\t}\n\t})\n}\n\nfunc TestGridFSFile_UnmarshalBSON(t *testing.T) {\n\tif testing.Short() {\n\t\tt.Skip(\"skipping integration test in short mode\")\n\t}\n\n\tcs := integtest.ConnString(t)\n\n\tclientOpts := options.Client().\n\t\tApplyURI(cs.Original).\n\t\tSetReadPreference(readpref.Primary()).\n\t\tSetWriteConcern(writeconcern.Majority()).\n\t\t// Connect to a single host. For sharded clusters, this will pin to a single mongos, which avoids\n\t\t// non-deterministic versioning errors in the server. This has no effect for replica sets because the driver\n\t\t// will discover the other hosts during SDAM checks.\n\t\tSetHosts(cs.Hosts[:1])\n\n\tintegtest.AddTestServerAPIVersion(clientOpts)\n\n\tclient, err := Connect(clientOpts)\n\trequire.NoError(t, err)\n\n\tdefer func() {\n\t\terr := client.Disconnect(context.Background())\n\t\trequire.NoError(t, err)\n\t}()\n\n\t// Get the database and create a GridFS bucket\n\tdb := client.Database(\"gridfs_test_db\")\n\n\t// Drop the collection\n\terr = db.Collection(\"myfiles.files\").Drop(context.Background())\n\trequire.NoError(t, err)\n\n\terr = db.Collection(\"myfiles.chunks\").Drop(context.Background())\n\trequire.NoError(t, err)\n\n\tbucket := db.GridFSBucket(options.GridFSBucket().SetName(\"myfiles\"))\n\n\t// Data to upload\n\tfileName := \"example-file.txt\"\n\tfileContent := []byte(\"Hello GridFS! This is a test file.\")\n\n\t// Upload data into GridFS\n\tuploadStream, err := bucket.OpenUploadStream(context.Background(), fileName)\n\trequire.NoError(t, err)\n\n\t_, err = uploadStream.Write(fileContent)\n\trequire.NoError(t, err)\n\n\tuploadStream.Close()\n\n\t// Verify the file metadata\n\tfileCursor, err := bucket.Find(context.Background(), bson.D{})\n\trequire.NoError(t, err)\n\n\tfor fileCursor.Next(context.Background()) {\n\t\tvar file GridFSFile\n\t\terr := fileCursor.Decode(&file)\n\t\trequire.NoError(t, err)\n\n\t\tassert.NotNil(t, file.ID)\n\t\tassert.Equal(t, int64(34), file.Length)\n\t\tassert.Equal(t, int32(261120), file.ChunkSize)\n\t\tassert.NotNil(t, file.UploadDate)\n\t\tassert.Equal(t, fileName, file.Name)\n\t}\n}\n"
  },
  {
    "path": "mongo/gridfs_upload_stream.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage mongo\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"math\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n)\n\n// uploadBufferSize is the size in bytes of one stream batch. Chunks will be written to the db after the sum of chunk\n// lengths is equal to the batch size.\nconst uploadBufferSize = 16 * 1024 * 1024 // 16 MiB\n\n// ErrStreamClosed is an error returned if an operation is attempted on a closed/aborted stream.\nvar ErrStreamClosed = errors.New(\"stream is closed or aborted\")\n\n// GridFSUploadStream is used to upload a file in chunks. This type implements the io.Writer interface and a file can be\n// uploaded using the Write method. After an upload is complete, the Close method must be called to write file\n// metadata.\ntype GridFSUploadStream struct {\n\t*upload // chunk size and metadata\n\tFileID  any\n\n\tchunkIndex  int\n\tchunksColl  *Collection // collection to store file chunks\n\tfilename    string\n\tfilesColl   *Collection // collection to store file metadata\n\tclosed      bool\n\tbuffer      []byte\n\tbufferIndex int\n\tfileLen     int64\n\tctx         context.Context\n\tcancel      context.CancelFunc\n}\n\n// NewUploadStream creates a new upload stream.\nfunc newUploadStream(\n\tctx context.Context,\n\tcancel context.CancelFunc,\n\tup *upload,\n\tfileID any,\n\tfilename string,\n\tchunks, files *Collection,\n) *GridFSUploadStream {\n\treturn &GridFSUploadStream{\n\t\tupload: up,\n\t\tFileID: fileID,\n\n\t\tchunksColl: chunks,\n\t\tfilename:   filename,\n\t\tfilesColl:  files,\n\t\tbuffer:     make([]byte, uploadBufferSize),\n\t\tctx:        ctx,\n\t\tcancel:     cancel,\n\t}\n}\n\n// Close writes file metadata to the files collection and cleans up any resources associated with the UploadStream.\nfunc (us *GridFSUploadStream) Close() error {\n\tdefer func() {\n\t\tif us.cancel != nil {\n\t\t\tus.cancel()\n\t\t}\n\t}()\n\n\tif us.closed {\n\t\treturn ErrStreamClosed\n\t}\n\n\tif us.bufferIndex != 0 {\n\t\tif err := us.uploadChunks(us.ctx, true); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tif err := us.createFilesCollDoc(us.ctx); err != nil {\n\t\treturn err\n\t}\n\n\tus.closed = true\n\treturn nil\n}\n\n// Write transfers the contents of a byte slice into this upload stream. If the stream's underlying buffer fills up,\n// the buffer will be uploaded as chunks to the server. Implements the io.Writer interface.\nfunc (us *GridFSUploadStream) Write(p []byte) (int, error) {\n\tif us.closed {\n\t\treturn 0, ErrStreamClosed\n\t}\n\n\torigLen := len(p)\n\tfor len(p) != 0 {\n\n\t\tn := copy(us.buffer[us.bufferIndex:], p) // copy as much as possible\n\t\tp = p[n:]\n\t\tus.bufferIndex += n\n\n\t\tif us.bufferIndex == uploadBufferSize {\n\t\t\terr := us.uploadChunks(us.ctx, false)\n\t\t\tif err != nil {\n\t\t\t\treturn 0, err\n\t\t\t}\n\t\t}\n\t}\n\treturn origLen, nil\n}\n\n// Abort closes the stream and deletes all file chunks that have already been written.\nfunc (us *GridFSUploadStream) Abort() error {\n\tdefer func() {\n\t\tif us.cancel != nil {\n\t\t\tus.cancel()\n\t\t}\n\t}()\n\n\tif us.closed {\n\t\treturn ErrStreamClosed\n\t}\n\n\t_, err := us.chunksColl.DeleteMany(us.ctx, bson.D{{\"files_id\", us.FileID}})\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tus.closed = true\n\treturn nil\n}\n\n// uploadChunks uploads the current buffer as a series of chunks to the bucket\n// if uploadPartial is true, any data at the end of the buffer that is smaller than a chunk will be uploaded as a partial\n// chunk. if it is false, the data will be moved to the front of the buffer.\n// uploadChunks sets us.bufferIndex to the next available index in the buffer after uploading\nfunc (us *GridFSUploadStream) uploadChunks(ctx context.Context, uploadPartial bool) error {\n\tchunks := float64(us.bufferIndex) / float64(us.chunkSize)\n\tnumChunks := int(math.Ceil(chunks))\n\tif !uploadPartial {\n\t\tnumChunks = int(math.Floor(chunks))\n\t}\n\n\tdocs := make([]any, numChunks)\n\n\tbegChunkIndex := us.chunkIndex\n\tfor i := 0; i < us.bufferIndex; i += int(us.chunkSize) {\n\t\tendIndex := i + int(us.chunkSize)\n\t\tif us.bufferIndex-i < int(us.chunkSize) {\n\t\t\t// partial chunk\n\t\t\tif !uploadPartial {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tendIndex = us.bufferIndex\n\t\t}\n\t\tchunkData := us.buffer[i:endIndex]\n\t\tdocs[us.chunkIndex-begChunkIndex] = bson.D{\n\t\t\t{\"_id\", bson.NewObjectID()},\n\t\t\t{\"files_id\", us.FileID},\n\t\t\t{\"n\", int32(us.chunkIndex)},\n\t\t\t{\"data\", bson.Binary{Subtype: 0x00, Data: chunkData}},\n\t\t}\n\t\tus.chunkIndex++\n\t\tus.fileLen += int64(len(chunkData))\n\t}\n\n\t_, err := us.chunksColl.InsertMany(ctx, docs)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// copy any remaining bytes to beginning of buffer and set buffer index\n\tbytesUploaded := numChunks * int(us.chunkSize)\n\tif bytesUploaded != uploadBufferSize && !uploadPartial {\n\t\tcopy(us.buffer[0:], us.buffer[bytesUploaded:us.bufferIndex])\n\t}\n\tus.bufferIndex = uploadBufferSize - bytesUploaded\n\treturn nil\n}\n\nfunc (us *GridFSUploadStream) createFilesCollDoc(ctx context.Context) error {\n\tdoc := bson.D{\n\t\t{\"_id\", us.FileID},\n\t\t{\"length\", us.fileLen},\n\t\t{\"chunkSize\", us.chunkSize},\n\t\t{\"uploadDate\", bson.DateTime(time.Now().UnixNano() / int64(time.Millisecond))},\n\t\t{\"filename\", us.filename},\n\t}\n\n\tif us.metadata != nil {\n\t\tdoc = append(doc, bson.E{\"metadata\", us.metadata})\n\t}\n\n\t_, err := us.filesColl.InsertOne(ctx, doc)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "mongo/index_view.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage mongo\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"strconv\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/mongoutil\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/optionsutil\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/serverselector\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/readpref\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/description\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/operation\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/session\"\n)\n\n// ErrInvalidIndexValue is returned if an index is created with a keys document that has a value that is not a number\n// or string.\nvar ErrInvalidIndexValue = errors.New(\"invalid index value\")\n\n// ErrNonStringIndexName is returned if an index is created with a name that is not a string.\n//\n// Deprecated: it will be removed in the next major release\nvar ErrNonStringIndexName = errors.New(\"index name must be a string\")\n\n// IndexView is a type that can be used to create, drop, and list indexes on a collection. An IndexView for a collection\n// can be created by a call to Collection.Indexes().\ntype IndexView struct {\n\tcoll *Collection\n}\n\n// IndexModel represents a new index to be created.\ntype IndexModel struct {\n\t// A document describing which keys should be used for the index. It cannot be nil. This must be an order-preserving\n\t// type such as bson.D. Map types such as bson.M are not valid. See https://www.mongodb.com/docs/manual/indexes/#indexes\n\t// for examples of valid documents.\n\tKeys any\n\n\t// The options to use to create the index.\n\tOptions *options.IndexOptionsBuilder\n}\n\n// List executes a listIndexes command and returns a cursor over the indexes in the collection.\n//\n// The opts parameter can be used to specify options for this operation (see the options.ListIndexesOptions\n// documentation).\n//\n// For more information about the command, see https://www.mongodb.com/docs/manual/reference/command/listIndexes/.\nfunc (iv IndexView) List(ctx context.Context, opts ...options.Lister[options.ListIndexesOptions]) (*Cursor, error) {\n\tif ctx == nil {\n\t\tctx = context.Background()\n\t}\n\n\tsess := sessionFromContext(ctx)\n\tif sess == nil && iv.coll.client.sessionPool != nil {\n\t\tsess = session.NewImplicitClientSession(iv.coll.client.sessionPool, iv.coll.client.id)\n\t}\n\n\terr := iv.coll.client.validSession(sess)\n\tif err != nil {\n\t\tcloseImplicitSession(sess)\n\t\treturn nil, err\n\t}\n\tvar selector description.ServerSelector\n\n\tselector = &serverselector.Composite{\n\t\tSelectors: []description.ServerSelector{\n\t\t\t&serverselector.ReadPref{ReadPref: readpref.Primary()},\n\t\t\t&serverselector.Latency{Latency: iv.coll.client.localThreshold},\n\t\t},\n\t}\n\n\tselector = makeReadPrefSelector(sess, selector, iv.coll.client.localThreshold)\n\top := operation.NewListIndexes().\n\t\tSession(sess).CommandMonitor(iv.coll.client.monitor).\n\t\tServerSelector(selector).ClusterClock(iv.coll.client.clock).\n\t\tDatabase(iv.coll.db.name).Collection(iv.coll.name).\n\t\tDeployment(iv.coll.client.deployment).ServerAPI(iv.coll.client.serverAPI).\n\t\tTimeout(iv.coll.client.timeout).Crypt(iv.coll.client.cryptFLE).Authenticator(iv.coll.client.authenticator)\n\n\tcursorOpts := iv.coll.client.createBaseCursorOptions()\n\n\tcursorOpts.MarshalValueEncoderFn = newEncoderFn(iv.coll.bsonOpts, iv.coll.registry)\n\n\targs, err := mongoutil.NewOptions[options.ListIndexesOptions](opts...)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to construct options from builder: %w\", err)\n\t}\n\n\tif args.BatchSize != nil {\n\t\top = op.BatchSize(*args.BatchSize)\n\t\tcursorOpts.BatchSize = *args.BatchSize\n\t}\n\tif rawData, ok := optionsutil.Value(args.Internal, \"rawData\").(bool); ok {\n\t\top = op.RawData(rawData)\n\t}\n\n\tretry := driver.RetryNone\n\tif iv.coll.client.retryReads {\n\t\tretry = driver.RetryOncePerCommand\n\t}\n\top.Retry(retry)\n\n\terr = op.Execute(ctx)\n\tif err != nil {\n\t\t// for namespaceNotFound errors, return an empty cursor and do not throw an error\n\t\tcloseImplicitSession(sess)\n\t\tvar de driver.Error\n\t\tif errors.As(err, &de) && de.NamespaceNotFound() {\n\t\t\treturn newEmptyCursor(), nil\n\t\t}\n\n\t\treturn nil, wrapErrors(err)\n\t}\n\n\tbc, err := op.Result(cursorOpts)\n\tif err != nil {\n\t\tcloseImplicitSession(sess)\n\t\treturn nil, wrapErrors(err)\n\t}\n\tcursor, err := newCursorWithSession(bc, iv.coll.bsonOpts, iv.coll.registry, sess,\n\n\t\t// This value is included for completeness, but a server will never return\n\t\t// a tailable awaitData cursor from a listIndexes operation.\n\t\twithCursorOptionClientTimeout(iv.coll.client.timeout))\n\n\treturn cursor, wrapErrors(err)\n}\n\n// ListSpecifications executes a List command and returns a slice of returned IndexSpecifications\nfunc (iv IndexView) ListSpecifications(\n\tctx context.Context,\n\topts ...options.Lister[options.ListIndexesOptions],\n) ([]IndexSpecification, error) {\n\tcursor, err := iv.List(ctx, opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar resp []indexListSpecificationResponse\n\n\tif err := cursor.All(ctx, &resp); err != nil {\n\t\treturn nil, err\n\t}\n\n\tnamespace := iv.coll.db.Name() + \".\" + iv.coll.Name()\n\n\tspecs := make([]IndexSpecification, len(resp))\n\tfor idx, spec := range resp {\n\t\tspecs[idx] = IndexSpecification(spec)\n\t\tspecs[idx].Namespace = namespace\n\t}\n\n\treturn specs, nil\n}\n\n// CreateOne executes a createIndexes command to create an index on the collection and returns the name of the new\n// index. See the IndexView.CreateMany documentation for more information and an example.\nfunc (iv IndexView) CreateOne(\n\tctx context.Context,\n\tmodel IndexModel,\n\topts ...options.Lister[options.CreateIndexesOptions],\n) (string, error) {\n\tnames, err := iv.CreateMany(ctx, []IndexModel{model}, opts...)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\treturn names[0], nil\n}\n\n// CreateMany executes a createIndexes command to create multiple indexes on the collection and returns the names of\n// the new indexes.\n//\n// For each IndexModel in the models parameter, the index name can be specified via the Options field. If a name is not\n// given, it will be generated from the Keys document.\n//\n// The opts parameter can be used to specify options for this operation (see the options.CreateIndexesOptions\n// documentation).\n//\n// For more information about the command, see https://www.mongodb.com/docs/manual/reference/command/createIndexes/.\nfunc (iv IndexView) CreateMany(\n\tctx context.Context,\n\tmodels []IndexModel,\n\topts ...options.Lister[options.CreateIndexesOptions],\n) ([]string, error) {\n\tnames := make([]string, 0, len(models))\n\n\tvar indexes bsoncore.Document\n\taidx, indexes := bsoncore.AppendArrayStart(indexes)\n\n\tfor i, model := range models {\n\t\tif model.Keys == nil {\n\t\t\treturn nil, fmt.Errorf(\"index model keys cannot be nil\")\n\t\t}\n\n\t\tif isUnorderedMap(model.Keys) {\n\t\t\treturn nil, ErrMapForOrderedArgument{\"keys\"}\n\t\t}\n\n\t\tkeys, err := marshal(model.Keys, iv.coll.bsonOpts, iv.coll.registry)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tname, err := getOrGenerateIndexName(keys, model)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tnames = append(names, name)\n\n\t\tvar iidx int32\n\t\tiidx, indexes = bsoncore.AppendDocumentElementStart(indexes, strconv.Itoa(i))\n\t\tindexes = bsoncore.AppendDocumentElement(indexes, \"key\", keys)\n\n\t\tif model.Options == nil {\n\t\t\tmodel.Options = options.Index()\n\t\t}\n\t\tmodel.Options.SetName(name)\n\n\t\toptsDoc, err := iv.createOptionsDoc(model.Options)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tindexes = bsoncore.AppendDocument(indexes, optsDoc)\n\n\t\tindexes, err = bsoncore.AppendDocumentEnd(indexes, iidx)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\tindexes, err := bsoncore.AppendArrayEnd(indexes, aidx)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tsess := sessionFromContext(ctx)\n\n\tif sess == nil && iv.coll.client.sessionPool != nil {\n\t\tsess = session.NewImplicitClientSession(iv.coll.client.sessionPool, iv.coll.client.id)\n\t\tdefer sess.EndSession()\n\t}\n\n\terr = iv.coll.client.validSession(sess)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\twc := iv.coll.writeConcern\n\tif sess.TransactionRunning() {\n\t\twc = nil\n\t}\n\tif !wc.Acknowledged() {\n\t\tsess = nil\n\t}\n\n\tselector := makePinnedSelector(sess, iv.coll.writeSelector)\n\n\targs, err := mongoutil.NewOptions[options.CreateIndexesOptions](opts...)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to construct options from builder: %w\", err)\n\t}\n\n\top := operation.NewCreateIndexes(indexes).\n\t\tSession(sess).WriteConcern(wc).ClusterClock(iv.coll.client.clock).\n\t\tDatabase(iv.coll.db.name).Collection(iv.coll.name).CommandMonitor(iv.coll.client.monitor).\n\t\tDeployment(iv.coll.client.deployment).ServerSelector(selector).ServerAPI(iv.coll.client.serverAPI).\n\t\tTimeout(iv.coll.client.timeout).Crypt(iv.coll.client.cryptFLE).Authenticator(iv.coll.client.authenticator)\n\tif args.CommitQuorum != nil {\n\t\tcommitQuorum, err := marshalValue(args.CommitQuorum, iv.coll.bsonOpts, iv.coll.registry)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\top.CommitQuorum(commitQuorum)\n\t}\n\tif rawData, ok := optionsutil.Value(args.Internal, \"rawData\").(bool); ok {\n\t\top = op.RawData(rawData)\n\t}\n\n\t_, err = processWriteError(op.Execute(ctx))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn names, nil\n}\n\nfunc (iv IndexView) createOptionsDoc(opts options.Lister[options.IndexOptions]) (bsoncore.Document, error) {\n\targs, err := mongoutil.NewOptions[options.IndexOptions](opts)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to construct options from builder: %w\", err)\n\t}\n\n\toptsDoc := bsoncore.Document{}\n\tif args.ExpireAfterSeconds != nil {\n\t\toptsDoc = bsoncore.AppendInt32Element(optsDoc, \"expireAfterSeconds\", *args.ExpireAfterSeconds)\n\t}\n\tif args.Name != nil {\n\t\toptsDoc = bsoncore.AppendStringElement(optsDoc, \"name\", *args.Name)\n\t}\n\tif args.Sparse != nil {\n\t\toptsDoc = bsoncore.AppendBooleanElement(optsDoc, \"sparse\", *args.Sparse)\n\t}\n\tif args.StorageEngine != nil {\n\t\tdoc, err := marshal(args.StorageEngine, iv.coll.bsonOpts, iv.coll.registry)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\toptsDoc = bsoncore.AppendDocumentElement(optsDoc, \"storageEngine\", doc)\n\t}\n\tif args.Unique != nil {\n\t\toptsDoc = bsoncore.AppendBooleanElement(optsDoc, \"unique\", *args.Unique)\n\t}\n\tif args.Version != nil {\n\t\toptsDoc = bsoncore.AppendInt32Element(optsDoc, \"v\", *args.Version)\n\t}\n\tif args.DefaultLanguage != nil {\n\t\toptsDoc = bsoncore.AppendStringElement(optsDoc, \"default_language\", *args.DefaultLanguage)\n\t}\n\tif args.LanguageOverride != nil {\n\t\toptsDoc = bsoncore.AppendStringElement(optsDoc, \"language_override\", *args.LanguageOverride)\n\t}\n\tif args.TextVersion != nil {\n\t\toptsDoc = bsoncore.AppendInt32Element(optsDoc, \"textIndexVersion\", *args.TextVersion)\n\t}\n\tif args.Weights != nil {\n\t\tdoc, err := marshal(args.Weights, iv.coll.bsonOpts, iv.coll.registry)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\toptsDoc = bsoncore.AppendDocumentElement(optsDoc, \"weights\", doc)\n\t}\n\tif args.SphereVersion != nil {\n\t\toptsDoc = bsoncore.AppendInt32Element(optsDoc, \"2dsphereIndexVersion\", *args.SphereVersion)\n\t}\n\tif args.Bits != nil {\n\t\toptsDoc = bsoncore.AppendInt32Element(optsDoc, \"bits\", *args.Bits)\n\t}\n\tif args.Max != nil {\n\t\toptsDoc = bsoncore.AppendDoubleElement(optsDoc, \"max\", *args.Max)\n\t}\n\tif args.Min != nil {\n\t\toptsDoc = bsoncore.AppendDoubleElement(optsDoc, \"min\", *args.Min)\n\t}\n\tif args.BucketSize != nil {\n\t\toptsDoc = bsoncore.AppendInt32Element(optsDoc, \"bucketSize\", *args.BucketSize)\n\t}\n\tif args.PartialFilterExpression != nil {\n\t\tdoc, err := marshal(args.PartialFilterExpression, iv.coll.bsonOpts, iv.coll.registry)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\toptsDoc = bsoncore.AppendDocumentElement(optsDoc, \"partialFilterExpression\", doc)\n\t}\n\tif args.Collation != nil {\n\t\toptsDoc = bsoncore.AppendDocumentElement(optsDoc, \"collation\", bsoncore.Document(toDocument(args.Collation)))\n\t}\n\tif args.WildcardProjection != nil {\n\t\tdoc, err := marshal(args.WildcardProjection, iv.coll.bsonOpts, iv.coll.registry)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\toptsDoc = bsoncore.AppendDocumentElement(optsDoc, \"wildcardProjection\", doc)\n\t}\n\tif args.Hidden != nil {\n\t\toptsDoc = bsoncore.AppendBooleanElement(optsDoc, \"hidden\", *args.Hidden)\n\t}\n\n\treturn optsDoc, nil\n}\n\nfunc (iv IndexView) drop(ctx context.Context, index any, opts ...options.Lister[options.DropIndexesOptions]) error {\n\targs, err := mongoutil.NewOptions[options.DropIndexesOptions](opts...)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to construct options from builder: %w\", err)\n\t}\n\n\tif ctx == nil {\n\t\tctx = context.Background()\n\t}\n\n\tsess := sessionFromContext(ctx)\n\tif sess == nil && iv.coll.client.sessionPool != nil {\n\t\tsess = session.NewImplicitClientSession(iv.coll.client.sessionPool, iv.coll.client.id)\n\t\tdefer sess.EndSession()\n\t}\n\n\terr = iv.coll.client.validSession(sess)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\twc := iv.coll.writeConcern\n\tif sess.TransactionRunning() {\n\t\twc = nil\n\t}\n\tif !wc.Acknowledged() {\n\t\tsess = nil\n\t}\n\n\tselector := makePinnedSelector(sess, iv.coll.writeSelector)\n\n\top := operation.NewDropIndexes(index).Session(sess).WriteConcern(wc).CommandMonitor(iv.coll.client.monitor).\n\t\tServerSelector(selector).ClusterClock(iv.coll.client.clock).\n\t\tDatabase(iv.coll.db.name).Collection(iv.coll.name).\n\t\tDeployment(iv.coll.client.deployment).ServerAPI(iv.coll.client.serverAPI).\n\t\tTimeout(iv.coll.client.timeout).Crypt(iv.coll.client.cryptFLE).Authenticator(iv.coll.client.authenticator)\n\n\tif rawData, ok := optionsutil.Value(args.Internal, \"rawData\").(bool); ok {\n\t\top = op.RawData(rawData)\n\t}\n\n\terr = op.Execute(ctx)\n\tif err != nil {\n\t\treturn wrapErrors(err)\n\t}\n\n\treturn nil\n}\n\n// DropOne executes a dropIndexes operation to drop an index on the collection.\n//\n// The name parameter should be the name of the index to drop. If the name is\n// \"*\", ErrMultipleIndexDrop will be returned without running the command\n// because doing so would drop all indexes.\n//\n// The opts parameter can be used to specify options for this operation (see the\n// options.DropIndexesOptions documentation).\n//\n// For more information about the command, see https://www.mongodb.com/docs/manual/reference/command/dropIndexes/.\nfunc (iv IndexView) DropOne(\n\tctx context.Context,\n\tname string,\n\topts ...options.Lister[options.DropIndexesOptions],\n) error {\n\t// For more information about the command, see\n\t// https://www.mongodb.com/docs/manual/reference/command/dropIndexes/.\n\tif name == \"*\" {\n\t\treturn ErrMultipleIndexDrop\n\t}\n\n\treturn iv.drop(ctx, name, opts...)\n}\n\n// DropWithKey drops a collection index by key using the dropIndexes operation.\n//\n// This function is useful to drop an index using its key specification instead of its name.\nfunc (iv IndexView) DropWithKey(ctx context.Context, keySpecDocument any, opts ...options.Lister[options.DropIndexesOptions]) error {\n\tdoc, err := marshal(keySpecDocument, iv.coll.bsonOpts, iv.coll.registry)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn iv.drop(ctx, doc, opts...)\n}\n\n// DropAll executes a dropIndexes operation to drop all indexes on the collection.\n//\n// The opts parameter can be used to specify options for this operation (see the\n// options.DropIndexesOptions documentation).\n//\n// For more information about the command, see\n// https://www.mongodb.com/docs/manual/reference/command/dropIndexes/.\nfunc (iv IndexView) DropAll(\n\tctx context.Context,\n\topts ...options.Lister[options.DropIndexesOptions],\n) error {\n\treturn iv.drop(ctx, \"*\", opts...)\n}\n\nfunc getOrGenerateIndexName(keySpecDocument bsoncore.Document, model IndexModel) (string, error) {\n\targs, err := mongoutil.NewOptions[options.IndexOptions](model.Options)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"failed to construct options from builder: %w\", err)\n\t}\n\n\tif args != nil && args.Name != nil {\n\t\treturn *args.Name, nil\n\t}\n\n\tname := bytes.NewBufferString(\"\")\n\tfirst := true\n\n\telems, err := keySpecDocument.Elements()\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tfor _, elem := range elems {\n\t\tif !first {\n\t\t\t_, err := name.WriteRune('_')\n\t\t\tif err != nil {\n\t\t\t\treturn \"\", err\n\t\t\t}\n\t\t}\n\n\t\t_, err := name.WriteString(elem.Key())\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\n\t\t_, err = name.WriteRune('_')\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\n\t\tvar value string\n\n\t\tbsonValue := elem.Value()\n\t\tswitch bsonValue.Type {\n\t\tcase bsoncore.TypeInt32:\n\t\t\tvalue = fmt.Sprintf(\"%d\", bsonValue.Int32())\n\t\tcase bsoncore.TypeInt64:\n\t\t\tvalue = fmt.Sprintf(\"%d\", bsonValue.Int64())\n\t\tcase bsoncore.TypeString:\n\t\t\tvalue = bsonValue.StringValue()\n\t\tdefault:\n\t\t\treturn \"\", ErrInvalidIndexValue\n\t\t}\n\n\t\t_, err = name.WriteString(value)\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\n\t\tfirst = false\n\t}\n\n\treturn name.String(), nil\n}\n"
  },
  {
    "path": "mongo/insert.go",
    "content": "// Copyright (C) MongoDB, Inc. 2019-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage mongo\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/event\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/driverutil\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/logger\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/writeconcern\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/description\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/session\"\n)\n\n// insert performs an insert operation.\ntype insert struct {\n\tauthenticator            driver.Authenticator\n\tbypassDocumentValidation *bool\n\tcomment                  bsoncore.Value\n\tdocuments                []bsoncore.Document\n\tordered                  *bool\n\tsession                  *session.Client\n\tclock                    *session.ClusterClock\n\tcollection               string\n\tmonitor                  *event.CommandMonitor\n\tcrypt                    driver.Crypt\n\tdatabase                 string\n\tdeployment               driver.Deployment\n\tselector                 description.ServerSelector\n\twriteConcern             *writeconcern.WriteConcern\n\tretry                    *driver.RetryMode\n\tresult                   insertResult\n\tserverAPI                *driver.ServerAPIOptions\n\ttimeout                  *time.Duration\n\trawData                  *bool\n\tadditionalCmd            bson.D\n\tlogger                   *logger.Logger\n}\n\n// insertResult represents an insert result returned by the server.\ntype insertResult struct {\n\t// Number of documents successfully inserted.\n\tN int64\n}\n\nfunc buildInsertResult(response bsoncore.Document) (insertResult, error) {\n\telements, err := response.Elements()\n\tif err != nil {\n\t\treturn insertResult{}, err\n\t}\n\tir := insertResult{}\n\tfor _, element := range elements {\n\t\tif element.Key() == \"n\" {\n\t\t\tvar ok bool\n\t\t\tir.N, ok = element.Value().AsInt64OK()\n\t\t\tif !ok {\n\t\t\t\treturn ir, fmt.Errorf(\"response field 'n' is type int32 or int64, but received BSON type %s\", element.Value().Type)\n\t\t\t}\n\t\t}\n\t}\n\treturn ir, nil\n}\n\n// Result returns the result of executing this operation.\nfunc (i *insert) Result() insertResult { return i.result }\n\nfunc (i *insert) processResponse(_ context.Context, resp bsoncore.Document, _ driver.ResponseInfo) error {\n\tir, err := buildInsertResult(resp)\n\ti.result.N += ir.N\n\treturn err\n}\n\n// Execute runs this operations and returns an error if the operation did not execute successfully.\nfunc (i *insert) Execute(ctx context.Context) error {\n\tif i.deployment == nil {\n\t\treturn errors.New(\"the Insert operation must have a Deployment set before Execute can be called\")\n\t}\n\tbatches := &driver.Batches{\n\t\tIdentifier: \"documents\",\n\t\tDocuments:  i.documents,\n\t\tOrdered:    i.ordered,\n\t}\n\n\treturn driver.Operation{\n\t\tCommandFn:         i.command,\n\t\tProcessResponseFn: i.processResponse,\n\t\tBatches:           batches,\n\t\tRetryMode:         i.retry,\n\t\tType:              driver.Write,\n\t\tClient:            i.session,\n\t\tClock:             i.clock,\n\t\tCommandMonitor:    i.monitor,\n\t\tCrypt:             i.crypt,\n\t\tDatabase:          i.database,\n\t\tDeployment:        i.deployment,\n\t\tSelector:          i.selector,\n\t\tWriteConcern:      i.writeConcern,\n\t\tServerAPI:         i.serverAPI,\n\t\tTimeout:           i.timeout,\n\t\tLogger:            i.logger,\n\t\tName:              driverutil.InsertOp,\n\t\tAuthenticator:     i.authenticator,\n\t}.Execute(ctx)\n}\n\nfunc (i *insert) command(dst []byte, desc description.SelectedServer) ([]byte, error) {\n\tdst = bsoncore.AppendStringElement(dst, \"insert\", i.collection)\n\tif i.bypassDocumentValidation != nil && (desc.WireVersion != nil &&\n\t\tdriverutil.VersionRangeIncludes(*desc.WireVersion, 4)) {\n\n\t\tdst = bsoncore.AppendBooleanElement(dst, \"bypassDocumentValidation\", *i.bypassDocumentValidation)\n\t}\n\tif i.comment.Type != bsoncore.Type(0) {\n\t\tdst = bsoncore.AppendValueElement(dst, \"comment\", i.comment)\n\t}\n\tif i.ordered != nil {\n\t\tdst = bsoncore.AppendBooleanElement(dst, \"ordered\", *i.ordered)\n\t}\n\t// Set rawData for 8.2+ servers.\n\tif i.rawData != nil && desc.WireVersion != nil && driverutil.VersionRangeIncludes(*desc.WireVersion, 27) {\n\t\tdst = bsoncore.AppendBooleanElement(dst, \"rawData\", *i.rawData)\n\t}\n\tif len(i.additionalCmd) > 0 {\n\t\tdoc, err := bson.Marshal(i.additionalCmd)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tdst = append(dst, doc[4:len(doc)-1]...)\n\t}\n\treturn dst, nil\n}\n"
  },
  {
    "path": "mongo/mongo.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage mongo\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"reflect\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/codecutil\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n)\n\nvar defaultRegistry = bson.NewRegistry()\n\n// Dialer is used to make network connections.\ntype Dialer interface {\n\tDialContext(ctx context.Context, network, address string) (net.Conn, error)\n}\n\n// Pipeline is a type that makes creating aggregation pipelines easier. It is a\n// helper and is intended for serializing to BSON.\n//\n// Example usage:\n//\n//\tmongo.Pipeline{\n//\t\t{{\"$group\", bson.D{{\"_id\", \"$state\"}, {\"totalPop\", bson.D{{\"$sum\", \"$pop\"}}}}}},\n//\t\t{{\"$match\", bson.D{{\"totalPop\", bson.D{{\"$gte\", 10*1000*1000}}}}}},\n//\t}\ntype Pipeline []bson.D\n\n// getEncoder takes a writer, BSON options, and a BSON registry and returns a properly configured\n// bson.Encoder that writes to the given writer.\nfunc getEncoder(\n\tw io.Writer,\n\topts *options.BSONOptions,\n\treg *bson.Registry,\n) *bson.Encoder {\n\tvw := bson.NewDocumentWriter(w)\n\tenc := bson.NewEncoder(vw)\n\n\tif opts != nil {\n\t\tif opts.ErrorOnInlineDuplicates {\n\t\t\tenc.ErrorOnInlineDuplicates()\n\t\t}\n\t\tif opts.IntMinSize {\n\t\t\tenc.IntMinSize()\n\t\t}\n\t\tif opts.NilByteSliceAsEmpty {\n\t\t\tenc.NilByteSliceAsEmpty()\n\t\t}\n\t\tif opts.NilMapAsEmpty {\n\t\t\tenc.NilMapAsEmpty()\n\t\t}\n\t\tif opts.NilSliceAsEmpty {\n\t\t\tenc.NilSliceAsEmpty()\n\t\t}\n\t\tif opts.OmitZeroStruct {\n\t\t\tenc.OmitZeroStruct()\n\t\t}\n\t\tif opts.OmitEmpty {\n\t\t\tenc.OmitEmpty()\n\t\t}\n\t\tif opts.StringifyMapKeysWithFmt {\n\t\t\tenc.StringifyMapKeysWithFmt()\n\t\t}\n\t\tif opts.UseJSONStructTags {\n\t\t\tenc.UseJSONStructTags()\n\t\t}\n\t}\n\n\tif reg != nil {\n\t\tenc.SetRegistry(reg)\n\t}\n\n\treturn enc\n}\n\n// newEncoderFn will return a function for constructing an encoder based on the\n// provided codec options.\nfunc newEncoderFn(opts *options.BSONOptions, registry *bson.Registry) codecutil.EncoderFn {\n\treturn func(w io.Writer) *bson.Encoder {\n\t\treturn getEncoder(w, opts, registry)\n\t}\n}\n\n// marshal marshals the given value as a BSON document. Byte slices are always converted to a\n// bson.Raw before marshaling.\n//\n// If bsonOpts and registry are specified, the encoder is configured with the requested behaviors.\n// If they are nil, the default behaviors are used.\nfunc marshal(\n\tval any,\n\tbsonOpts *options.BSONOptions,\n\tregistry *bson.Registry,\n) (bsoncore.Document, error) {\n\tif registry == nil {\n\t\tregistry = defaultRegistry\n\t}\n\tif val == nil {\n\t\treturn nil, ErrNilDocument\n\t}\n\tif bs, ok := val.([]byte); ok {\n\t\t// Slight optimization so we'll just use MarshalBSON and not go through the codec machinery.\n\t\tval = bson.Raw(bs)\n\t}\n\n\tbuf := new(bytes.Buffer)\n\tenc := getEncoder(buf, bsonOpts, registry)\n\terr := enc.Encode(val)\n\tif err != nil {\n\t\treturn nil, MarshalError{Value: val, Err: err}\n\t}\n\n\treturn buf.Bytes(), nil\n}\n\n// ensureID inserts the given ObjectID as an element named \"_id\" at the\n// beginning of the given BSON document if there is not an \"_id\" already.\n// If the given ObjectID is bson.NilObjectID, a new object ID will be\n// generated with time.Now().\n//\n// If there is already an element named \"_id\", the document is not modified. It\n// returns the resulting document and the decoded Go value of the \"_id\" element.\nfunc ensureID(\n\tdoc bsoncore.Document,\n\toid bson.ObjectID,\n\tbsonOpts *options.BSONOptions,\n\treg *bson.Registry,\n) (bsoncore.Document, any, error) {\n\tif reg == nil {\n\t\treg = defaultRegistry\n\t}\n\n\t// Try to find the \"_id\" element. If it exists, try to unmarshal just the\n\t// \"_id\" field as an any and return it along with the unmodified\n\t// BSON document.\n\tif _, err := doc.LookupErr(\"_id\"); err == nil {\n\t\tvar id struct {\n\t\t\tID any `bson:\"_id\"`\n\t\t}\n\t\tdec := getDecoder(doc, bsonOpts, reg)\n\t\terr = dec.Decode(&id)\n\t\tif err != nil {\n\t\t\treturn nil, nil, fmt.Errorf(\"error unmarshaling BSON document: %w\", err)\n\t\t}\n\n\t\treturn doc, id.ID, nil\n\t}\n\n\t// We couldn't find an \"_id\" element, so add one with the value of the\n\t// provided ObjectID.\n\n\tolddoc := doc\n\n\t// Reserve an extra 17 bytes for the \"_id\" field we're about to add:\n\t// type (1) + \"_id\" (3) + terminator (1) + object ID (12)\n\tconst extraSpace = 17\n\tdoc = make(bsoncore.Document, 0, len(olddoc)+extraSpace)\n\t_, doc = bsoncore.ReserveLength(doc)\n\tif oid.IsZero() {\n\t\toid = bson.NewObjectID()\n\t}\n\tdoc = bsoncore.AppendObjectIDElement(doc, \"_id\", oid)\n\n\t// Remove and re-write the BSON document length header.\n\tconst int32Len = 4\n\tdoc = append(doc, olddoc[int32Len:]...)\n\tdoc = bsoncore.UpdateLength(doc, 0, int32(len(doc)))\n\n\treturn doc, oid, nil\n}\n\nfunc ensureDollarKey(doc bsoncore.Document) error {\n\tfirstElem, err := doc.IndexErr(0)\n\tif err != nil {\n\t\treturn errors.New(\"update document must have at least one element\")\n\t}\n\n\tif !strings.HasPrefix(firstElem.Key(), \"$\") {\n\t\treturn errors.New(\"update document must contain key beginning with '$'\")\n\t}\n\treturn nil\n}\n\nfunc ensureNoDollarKey(doc bsoncore.Document) error {\n\tif elem, err := doc.IndexErr(0); err == nil && strings.HasPrefix(elem.Key(), \"$\") {\n\t\treturn errors.New(\"replacement document cannot contain keys beginning with '$'\")\n\t}\n\n\treturn nil\n}\n\nfunc marshalAggregatePipeline(\n\tpipeline any,\n\tbsonOpts *options.BSONOptions,\n\tregistry *bson.Registry,\n) (bsoncore.Document, bool, error) {\n\tswitch t := pipeline.(type) {\n\tcase bson.ValueMarshaler:\n\t\tbtype, val, err := t.MarshalBSONValue()\n\t\tif err != nil {\n\t\t\treturn nil, false, err\n\t\t}\n\t\tif typ := bson.Type(btype); typ != bson.TypeArray {\n\t\t\treturn nil, false, fmt.Errorf(\"ValueMarshaler returned a %v, but was expecting %v\", typ, bson.TypeArray)\n\t\t}\n\n\t\tvar hasOutputStage bool\n\t\tpipelineDoc := bsoncore.Document(val)\n\t\tvalues, _ := pipelineDoc.Values()\n\t\tif pipelineLen := len(values); pipelineLen > 0 {\n\t\t\tif finalDoc, ok := values[pipelineLen-1].DocumentOK(); ok {\n\t\t\t\tif elem, err := finalDoc.IndexErr(0); err == nil && (elem.Key() == \"$out\" || elem.Key() == \"$merge\") {\n\t\t\t\t\thasOutputStage = true\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn pipelineDoc, hasOutputStage, nil\n\tdefault:\n\t\tval := reflect.ValueOf(t)\n\t\tif !val.IsValid() || (val.Kind() != reflect.Slice && val.Kind() != reflect.Array) {\n\t\t\treturn nil, false, fmt.Errorf(\"can only marshal slices and arrays into aggregation pipelines, but got %v\", val.Kind())\n\t\t}\n\n\t\tvar hasOutputStage bool\n\t\tvalLen := val.Len()\n\n\t\tswitch t := pipeline.(type) {\n\t\t// Explicitly forbid non-empty pipelines that are semantically single documents\n\t\t// and are implemented as slices.\n\t\tcase bson.D, bson.Raw, bsoncore.Document:\n\t\t\tif valLen > 0 {\n\t\t\t\treturn nil, false,\n\t\t\t\t\tfmt.Errorf(\"%T is not an allowed pipeline type as it represents a single document. Use bson.A or mongo.Pipeline instead\", t)\n\t\t\t}\n\t\t// bsoncore.Arrays do not need to be marshaled. Only check validity and presence of output stage.\n\t\tcase bsoncore.Array:\n\t\t\tif err := t.Validate(); err != nil {\n\t\t\t\treturn nil, false, err\n\t\t\t}\n\n\t\t\tvalues, err := t.Values()\n\t\t\tif err != nil {\n\t\t\t\treturn nil, false, err\n\t\t\t}\n\n\t\t\tnumVals := len(values)\n\t\t\tif numVals == 0 {\n\t\t\t\treturn bsoncore.Document(t), false, nil\n\t\t\t}\n\n\t\t\t// If not empty, check if first value of the last stage is $out or $merge.\n\t\t\tif lastStage, ok := values[numVals-1].DocumentOK(); ok {\n\t\t\t\tif elem, err := lastStage.IndexErr(0); err == nil && (elem.Key() == \"$out\" || elem.Key() == \"$merge\") {\n\t\t\t\t\thasOutputStage = true\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn bsoncore.Document(t), hasOutputStage, nil\n\t\t}\n\n\t\taidx, arr := bsoncore.AppendArrayStart(nil)\n\t\tfor idx := 0; idx < valLen; idx++ {\n\t\t\tdoc, err := marshal(val.Index(idx).Interface(), bsonOpts, registry)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, false, err\n\t\t\t}\n\n\t\t\tif idx == valLen-1 {\n\t\t\t\tif elem, err := doc.IndexErr(0); err == nil && (elem.Key() == \"$out\" || elem.Key() == \"$merge\") {\n\t\t\t\t\thasOutputStage = true\n\t\t\t\t}\n\t\t\t}\n\t\t\tarr = bsoncore.AppendDocumentElement(arr, strconv.Itoa(idx), doc)\n\t\t}\n\t\tarr, _ = bsoncore.AppendArrayEnd(arr, aidx)\n\t\treturn arr, hasOutputStage, nil\n\t}\n}\n\nfunc marshalUpdateValue(\n\tupdate any,\n\tbsonOpts *options.BSONOptions,\n\tregistry *bson.Registry,\n\tdollarKeysAllowed bool,\n) (bsoncore.Value, error) {\n\tdocumentCheckerFunc := ensureDollarKey\n\tif !dollarKeysAllowed {\n\t\tdocumentCheckerFunc = ensureNoDollarKey\n\t}\n\n\tvar u bsoncore.Value\n\tvar err error\n\tswitch t := update.(type) {\n\tcase nil:\n\t\treturn u, ErrNilDocument\n\tcase bson.D:\n\t\tu.Type = bsoncore.TypeEmbeddedDocument\n\t\tu.Data, err = marshal(update, bsonOpts, registry)\n\t\tif err != nil {\n\t\t\treturn u, err\n\t\t}\n\n\t\treturn u, documentCheckerFunc(u.Data)\n\tcase bson.Raw:\n\t\tu.Type = bsoncore.TypeEmbeddedDocument\n\t\tu.Data = t\n\t\treturn u, documentCheckerFunc(u.Data)\n\tcase bsoncore.Document:\n\t\tu.Type = bsoncore.TypeEmbeddedDocument\n\t\tu.Data = t\n\t\treturn u, documentCheckerFunc(u.Data)\n\tcase []byte:\n\t\tu.Type = bsoncore.TypeEmbeddedDocument\n\t\tu.Data = t\n\t\treturn u, documentCheckerFunc(u.Data)\n\tcase bson.Marshaler:\n\t\tu.Type = bsoncore.TypeEmbeddedDocument\n\t\tu.Data, err = t.MarshalBSON()\n\t\tif err != nil {\n\t\t\treturn u, err\n\t\t}\n\n\t\treturn u, documentCheckerFunc(u.Data)\n\tcase bson.ValueMarshaler:\n\t\ttt, data, err := t.MarshalBSONValue()\n\t\tu.Type = bsoncore.Type(tt)\n\t\tu.Data = data\n\t\tif err != nil {\n\t\t\treturn u, err\n\t\t}\n\t\tif u.Type != bsoncore.TypeArray && u.Type != bsoncore.TypeEmbeddedDocument {\n\t\t\treturn u, fmt.Errorf(\"ValueMarshaler returned a %v, but was expecting %v or %v\", u.Type, bsoncore.TypeArray, bsoncore.TypeEmbeddedDocument)\n\t\t}\n\t\treturn u, err\n\tdefault:\n\t\tval := reflect.ValueOf(t)\n\t\tif !val.IsValid() {\n\t\t\treturn u, fmt.Errorf(\"can only marshal slices and arrays into update pipelines, but got %v\", val.Kind())\n\t\t}\n\t\tif val.Kind() != reflect.Slice && val.Kind() != reflect.Array {\n\t\t\tu.Type = bsoncore.TypeEmbeddedDocument\n\t\t\tu.Data, err = marshal(update, bsonOpts, registry)\n\t\t\tif err != nil {\n\t\t\t\treturn u, err\n\t\t\t}\n\n\t\t\treturn u, documentCheckerFunc(u.Data)\n\t\t}\n\n\t\tu.Type = bsoncore.TypeArray\n\t\taidx, arr := bsoncore.AppendArrayStart(nil)\n\t\tvalLen := val.Len()\n\t\tfor idx := 0; idx < valLen; idx++ {\n\t\t\tdoc, err := marshal(val.Index(idx).Interface(), bsonOpts, registry)\n\t\t\tif err != nil {\n\t\t\t\treturn u, err\n\t\t\t}\n\n\t\t\tif err := documentCheckerFunc(doc); err != nil {\n\t\t\t\treturn u, err\n\t\t\t}\n\n\t\t\tarr = bsoncore.AppendDocumentElement(arr, strconv.Itoa(idx), doc)\n\t\t}\n\t\tu.Data, _ = bsoncore.AppendArrayEnd(arr, aidx)\n\t\treturn u, err\n\t}\n}\n\nfunc marshalValue(\n\tval any,\n\tbsonOpts *options.BSONOptions,\n\tregistry *bson.Registry,\n) (bsoncore.Value, error) {\n\treturn codecutil.MarshalValue(val, newEncoderFn(bsonOpts, registry))\n}\n\n// Build the aggregation pipeline for the CountDocument command.\nfunc countDocumentsAggregatePipeline(\n\tfilter any,\n\tencOpts *options.BSONOptions,\n\tregistry *bson.Registry,\n\targs *options.CountOptions,\n) (bsoncore.Document, error) {\n\tfilterDoc, err := marshal(filter, encOpts, registry)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\taidx, arr := bsoncore.AppendArrayStart(nil)\n\tdidx, arr := bsoncore.AppendDocumentElementStart(arr, strconv.Itoa(0))\n\tarr = bsoncore.AppendDocumentElement(arr, \"$match\", filterDoc)\n\tarr, _ = bsoncore.AppendDocumentEnd(arr, didx)\n\n\tindex := 1\n\tif args != nil {\n\t\tif args.Skip != nil {\n\t\t\tdidx, arr = bsoncore.AppendDocumentElementStart(arr, strconv.Itoa(index))\n\t\t\tarr = bsoncore.AppendInt64Element(arr, \"$skip\", *args.Skip)\n\t\t\tarr, _ = bsoncore.AppendDocumentEnd(arr, didx)\n\t\t\tindex++\n\t\t}\n\t\tif args.Limit != nil {\n\t\t\tdidx, arr = bsoncore.AppendDocumentElementStart(arr, strconv.Itoa(index))\n\t\t\tarr = bsoncore.AppendInt64Element(arr, \"$limit\", *args.Limit)\n\t\t\tarr, _ = bsoncore.AppendDocumentEnd(arr, didx)\n\t\t\tindex++\n\t\t}\n\t}\n\n\tdidx, arr = bsoncore.AppendDocumentElementStart(arr, strconv.Itoa(index))\n\tiidx, arr := bsoncore.AppendDocumentElementStart(arr, \"$group\")\n\tarr = bsoncore.AppendInt32Element(arr, \"_id\", 1)\n\tiiidx, arr := bsoncore.AppendDocumentElementStart(arr, \"n\")\n\tarr = bsoncore.AppendInt32Element(arr, \"$sum\", 1)\n\tarr, _ = bsoncore.AppendDocumentEnd(arr, iiidx)\n\tarr, _ = bsoncore.AppendDocumentEnd(arr, iidx)\n\tarr, _ = bsoncore.AppendDocumentEnd(arr, didx)\n\n\treturn bsoncore.AppendArrayEnd(arr, aidx)\n}\n"
  },
  {
    "path": "mongo/mongo_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage mongo\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"reflect\"\n\t\"testing\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert/assertbson\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/codecutil\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/require\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n)\n\nfunc TestEnsureID(t *testing.T) {\n\tt.Parallel()\n\n\toid := bson.NewObjectID()\n\n\ttestCases := []struct {\n\t\tdescription string\n\t\t// TODO: Registry? DecodeOptions?\n\t\tdoc    bsoncore.Document\n\t\toid    bson.ObjectID\n\t\twant   bsoncore.Document\n\t\twantID any\n\t}{\n\t\t{\n\t\t\tdescription: \"missing _id should be first element\",\n\t\t\tdoc: bsoncore.NewDocumentBuilder().\n\t\t\t\tAppendString(\"foo\", \"bar\").\n\t\t\t\tAppendString(\"baz\", \"quix\").\n\t\t\t\tAppendString(\"hello\", \"world\").\n\t\t\t\tBuild(),\n\t\t\twant: bsoncore.NewDocumentBuilder().\n\t\t\t\tAppendObjectID(\"_id\", oid).\n\t\t\t\tAppendString(\"foo\", \"bar\").\n\t\t\t\tAppendString(\"baz\", \"quix\").\n\t\t\t\tAppendString(\"hello\", \"world\").\n\t\t\t\tBuild(),\n\t\t\twantID: oid,\n\t\t},\n\t\t{\n\t\t\tdescription: \"existing ObjectID _id as should remain in place\",\n\t\t\tdoc: bsoncore.NewDocumentBuilder().\n\t\t\t\tAppendString(\"foo\", \"bar\").\n\t\t\t\tAppendObjectID(\"_id\", oid).\n\t\t\t\tAppendString(\"baz\", \"quix\").\n\t\t\t\tAppendString(\"hello\", \"world\").\n\t\t\t\tBuild(),\n\t\t\twant: bsoncore.NewDocumentBuilder().\n\t\t\t\tAppendString(\"foo\", \"bar\").\n\t\t\t\tAppendObjectID(\"_id\", oid).\n\t\t\t\tAppendString(\"baz\", \"quix\").\n\t\t\t\tAppendString(\"hello\", \"world\").\n\t\t\t\tBuild(),\n\t\t\twantID: oid,\n\t\t},\n\t\t{\n\t\t\tdescription: \"existing float _id as should remain in place\",\n\t\t\tdoc: bsoncore.NewDocumentBuilder().\n\t\t\t\tAppendString(\"foo\", \"bar\").\n\t\t\t\tAppendDouble(\"_id\", 3.14159).\n\t\t\t\tAppendString(\"baz\", \"quix\").\n\t\t\t\tAppendString(\"hello\", \"world\").\n\t\t\t\tBuild(),\n\t\t\twant: bsoncore.NewDocumentBuilder().\n\t\t\t\tAppendString(\"foo\", \"bar\").\n\t\t\t\tAppendDouble(\"_id\", 3.14159).\n\t\t\t\tAppendString(\"baz\", \"quix\").\n\t\t\t\tAppendString(\"hello\", \"world\").\n\t\t\t\tBuild(),\n\t\t\twantID: 3.14159,\n\t\t},\n\t\t{\n\t\t\tdescription: \"existing float _id as first element should remain first element\",\n\t\t\tdoc: bsoncore.NewDocumentBuilder().\n\t\t\t\tAppendDouble(\"_id\", 3.14159).\n\t\t\t\tAppendString(\"foo\", \"bar\").\n\t\t\t\tAppendString(\"baz\", \"quix\").\n\t\t\t\tAppendString(\"hello\", \"world\").\n\t\t\t\tBuild(),\n\t\t\twant: bsoncore.NewDocumentBuilder().\n\t\t\t\tAppendDouble(\"_id\", 3.14159).\n\t\t\t\tAppendString(\"foo\", \"bar\").\n\t\t\t\tAppendString(\"baz\", \"quix\").\n\t\t\t\tAppendString(\"hello\", \"world\").\n\t\t\t\tBuild(),\n\t\t\twantID: 3.14159,\n\t\t},\n\t\t{\n\t\t\tdescription: \"existing binary _id as first field should not be overwritten\",\n\t\t\tdoc: bsoncore.NewDocumentBuilder().\n\t\t\t\tAppendBinary(\"bin\", 0, []byte{0, 0, 0}).\n\t\t\t\tAppendString(\"_id\", \"LongEnoughIdentifier\").\n\t\t\t\tBuild(),\n\t\t\twant: bsoncore.NewDocumentBuilder().\n\t\t\t\tAppendBinary(\"bin\", 0, []byte{0, 0, 0}).\n\t\t\t\tAppendString(\"_id\", \"LongEnoughIdentifier\").\n\t\t\t\tBuild(),\n\t\t\twantID: \"LongEnoughIdentifier\",\n\t\t},\n\t}\n\n\tfor _, tc := range testCases {\n\t\ttc := tc // Capture range variable.\n\n\t\tt.Run(tc.description, func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tgot, gotID, err := ensureID(tc.doc, oid, nil, nil)\n\t\t\trequire.NoError(t, err, \"ensureID error\")\n\n\t\t\tassert.Equal(t, tc.want, got, \"expected and actual documents are different\")\n\t\t\tassert.Equal(t, tc.wantID, gotID, \"expected and actual IDs are different\")\n\n\t\t\t// Ensure that if the unmarshaled \"_id\" value is a\n\t\t\t// bson.ObjectID that it is a deep copy and does not share any\n\t\t\t// memory with the document byte slice.\n\t\t\tif oid, ok := gotID.(bson.ObjectID); ok {\n\t\t\t\tassert.DifferentAddressRanges(t, tc.doc, oid[:])\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestEnsureID_NilObjectID(t *testing.T) {\n\tt.Parallel()\n\n\tdoc := bsoncore.NewDocumentBuilder().\n\t\tAppendString(\"foo\", \"bar\").\n\t\tBuild()\n\n\tgot, gotIDI, err := ensureID(doc, bson.NilObjectID, nil, nil)\n\tassert.NoError(t, err)\n\n\tgotID, ok := gotIDI.(bson.ObjectID)\n\n\tassert.True(t, ok)\n\tassert.NotEqual(t, bson.NilObjectID, gotID)\n\n\twant := bsoncore.NewDocumentBuilder().\n\t\tAppendObjectID(\"_id\", gotID).\n\t\tAppendString(\"foo\", \"bar\").\n\t\tBuild()\n\n\tassert.Equal(t, want, got)\n}\n\nfunc TestMarshalAggregatePipeline(t *testing.T) {\n\t// []byte of [{{\"$limit\", 12345}}]\n\tindex, arr := bsoncore.AppendArrayStart(nil)\n\tdindex, arr := bsoncore.AppendDocumentElementStart(arr, \"0\")\n\tarr = bsoncore.AppendInt32Element(arr, \"$limit\", 12345)\n\tarr, _ = bsoncore.AppendDocumentEnd(arr, dindex)\n\tarr, _ = bsoncore.AppendArrayEnd(arr, index)\n\n\t// []byte of {{\"x\", 1}}\n\tindex, doc := bsoncore.AppendDocumentStart(nil)\n\tdoc = bsoncore.AppendInt32Element(doc, \"x\", 1)\n\tdoc, _ = bsoncore.AppendDocumentEnd(doc, index)\n\n\t// bsoncore.Array of [{{\"$merge\", {}}}]\n\tmergeStage := bsoncore.NewDocumentBuilder().\n\t\tStartDocument(\"$merge\").\n\t\tFinishDocument().\n\t\tBuild()\n\tarrMergeStage := bsoncore.NewArrayBuilder().AppendDocument(mergeStage).Build()\n\n\tfooStage := bsoncore.NewDocumentBuilder().AppendString(\"foo\", \"bar\").Build()\n\tbazStage := bsoncore.NewDocumentBuilder().AppendString(\"baz\", \"qux\").Build()\n\toutStage := bsoncore.NewDocumentBuilder().AppendString(\"$out\", \"myColl\").Build()\n\n\t// bsoncore.Array of [{{\"foo\", \"bar\"}}, {{\"baz\", \"qux\"}}, {{\"$out\", \"myColl\"}}]\n\tarrOutStage := bsoncore.NewArrayBuilder().\n\t\tAppendDocument(fooStage).\n\t\tAppendDocument(bazStage).\n\t\tAppendDocument(outStage).\n\t\tBuild()\n\n\t// bsoncore.Array of [{{\"foo\", \"bar\"}}, {{\"$out\", \"myColl\"}}, {{\"baz\", \"qux\"}}]\n\tarrMiddleOutStage := bsoncore.NewArrayBuilder().\n\t\tAppendDocument(fooStage).\n\t\tAppendDocument(outStage).\n\t\tAppendDocument(bazStage).\n\t\tBuild()\n\n\ttestCases := []struct {\n\t\tname           string\n\t\tpipeline       any\n\t\tarr            bson.A\n\t\thasOutputStage bool\n\t\terr            error\n\t}{\n\t\t{\n\t\t\t\"Pipeline/error\",\n\t\t\tPipeline{{{\"hello\", func() {}}}},\n\t\t\tnil,\n\t\t\tfalse,\n\t\t\tMarshalError{Value: bson.D{}, Err: errors.New(\"no encoder found for func()\")},\n\t\t},\n\t\t{\n\t\t\t\"Pipeline/success\",\n\t\t\tPipeline{{{\"hello\", \"world\"}}, {{\"pi\", 3.14159}}},\n\t\t\tbson.A{\n\t\t\t\tbson.D{{\"hello\", \"world\"}},\n\t\t\t\tbson.D{{\"pi\", 3.14159}},\n\t\t\t},\n\t\t\tfalse,\n\t\t\tnil,\n\t\t},\n\t\t{\n\t\t\t\"bson.A\",\n\t\t\tbson.A{\n\t\t\t\tbson.D{{\"$limit\", 12345}},\n\t\t\t},\n\t\t\tbson.A{\n\t\t\t\tbson.D{{\"$limit\", 12345}},\n\t\t\t},\n\t\t\tfalse,\n\t\t\tnil,\n\t\t},\n\t\t{\n\t\t\t\"[]bson.D\",\n\t\t\t[]bson.D{{{\"$limit\", 12345}}},\n\t\t\tbson.A{\n\t\t\t\tbson.D{{\"$limit\", 12345}},\n\t\t\t},\n\t\t\tfalse,\n\t\t\tnil,\n\t\t},\n\t\t{\n\t\t\t\"bson.A/error\",\n\t\t\tbson.A{\"5\"},\n\t\t\tnil,\n\t\t\tfalse,\n\t\t\tMarshalError{Value: \"\", Err: errors.New(\"WriteString can only write while positioned on a Element or Value but is positioned on a TopLevel\")},\n\t\t},\n\t\t{\n\t\t\t\"bson.A/success\",\n\t\t\tbson.A{bson.D{{\"$limit\", int32(12345)}}, map[string]any{\"$count\": \"foobar\"}},\n\t\t\tbson.A{\n\t\t\t\tbson.D{{\"$limit\", int(12345)}},\n\t\t\t\tbson.D{{\"$count\", \"foobar\"}},\n\t\t\t},\n\t\t\tfalse,\n\t\t\tnil,\n\t\t},\n\t\t{\n\t\t\t\"bson.A/error\",\n\t\t\tbson.A{\"5\"},\n\t\t\tnil,\n\t\t\tfalse,\n\t\t\tMarshalError{Value: \"\", Err: errors.New(\"WriteString can only write while positioned on a Element or Value but is positioned on a TopLevel\")},\n\t\t},\n\t\t{\n\t\t\t\"bson.A/success\",\n\t\t\tbson.A{bson.D{{\"$limit\", int32(12345)}}, map[string]any{\"$count\": \"foobar\"}},\n\t\t\tbson.A{\n\t\t\t\tbson.D{{\"$limit\", int32(12345)}},\n\t\t\t\tbson.D{{\"$count\", \"foobar\"}},\n\t\t\t},\n\t\t\tfalse,\n\t\t\tnil,\n\t\t},\n\t\t{\n\t\t\t\"[]any/error\",\n\t\t\t[]any{\"5\"},\n\t\t\tnil,\n\t\t\tfalse,\n\t\t\tMarshalError{Value: \"\", Err: errors.New(\"WriteString can only write while positioned on a Element or Value but is positioned on a TopLevel\")},\n\t\t},\n\t\t{\n\t\t\t\"[]any/success\",\n\t\t\t[]any{bson.D{{\"$limit\", int32(12345)}}, map[string]any{\"$count\": \"foobar\"}},\n\t\t\tbson.A{\n\t\t\t\tbson.D{{\"$limit\", int32(12345)}},\n\t\t\t\tbson.D{{\"$count\", \"foobar\"}},\n\t\t\t},\n\t\t\tfalse,\n\t\t\tnil,\n\t\t},\n\t\t{\n\t\t\t\"bson.ValueMarshaler/MarshalBSONValue error\",\n\t\t\tbvMarsh{err: errors.New(\"MarshalBSONValue error\")},\n\t\t\tnil,\n\t\t\tfalse,\n\t\t\terrors.New(\"MarshalBSONValue error\"),\n\t\t},\n\t\t{\n\t\t\t\"bson.ValueMarshaler/not array\",\n\t\t\tbvMarsh{t: bson.TypeString},\n\t\t\tnil,\n\t\t\tfalse,\n\t\t\tfmt.Errorf(\"ValueMarshaler returned a %v, but was expecting %v\", bson.TypeString, bson.TypeArray),\n\t\t},\n\t\t{\n\t\t\t\"bson.ValueMarshaler/UnmarshalBSONValue error\",\n\t\t\tbvMarsh{err: errors.New(\"UnmarshalBSONValue error\")},\n\t\t\tnil,\n\t\t\tfalse,\n\t\t\terrors.New(\"UnmarshalBSONValue error\"),\n\t\t},\n\t\t{\n\t\t\t\"bson.ValueMarshaler/success\",\n\t\t\tbvMarsh{t: bson.TypeArray, data: arr},\n\t\t\tbson.A{\n\t\t\t\tbson.D{{\"$limit\", int32(12345)}},\n\t\t\t},\n\t\t\tfalse,\n\t\t\tnil,\n\t\t},\n\t\t{\n\t\t\t\"bson.ValueMarshaler/success nil\",\n\t\t\tbvMarsh{t: bson.TypeArray},\n\t\t\tnil,\n\t\t\tfalse,\n\t\t\tnil,\n\t\t},\n\t\t{\n\t\t\t\"nil\",\n\t\t\tnil,\n\t\t\tnil,\n\t\t\tfalse,\n\t\t\terrors.New(\"can only marshal slices and arrays into aggregation pipelines, but got invalid\"),\n\t\t},\n\t\t{\n\t\t\t\"not array or slice\",\n\t\t\tint64(42),\n\t\t\tnil,\n\t\t\tfalse,\n\t\t\terrors.New(\"can only marshal slices and arrays into aggregation pipelines, but got int64\"),\n\t\t},\n\t\t{\n\t\t\t\"array/error\",\n\t\t\t[1]any{int64(42)},\n\t\t\tnil,\n\t\t\tfalse,\n\t\t\tMarshalError{Value: int64(0), Err: errors.New(\"WriteInt64 can only write while positioned on a Element or Value but is positioned on a TopLevel\")},\n\t\t},\n\t\t{\n\t\t\t\"array/success\",\n\t\t\t[1]any{bson.D{{\"$limit\", int64(12345)}}},\n\t\t\tbson.A{\n\t\t\t\tbson.D{{\"$limit\", int64(12345)}},\n\t\t\t},\n\t\t\tfalse,\n\t\t\tnil,\n\t\t},\n\t\t{\n\t\t\t\"slice/error\",\n\t\t\t[]any{int64(42)},\n\t\t\tnil,\n\t\t\tfalse,\n\t\t\tMarshalError{Value: int64(0), Err: errors.New(\"WriteInt64 can only write while positioned on a Element or Value but is positioned on a TopLevel\")},\n\t\t},\n\t\t{\n\t\t\t\"slice/success\",\n\t\t\t[]any{bson.D{{\"$limit\", int64(12345)}}},\n\t\t\tbson.A{\n\t\t\t\tbson.D{{\"$limit\", int64(12345)}},\n\t\t\t},\n\t\t\tfalse,\n\t\t\tnil,\n\t\t},\n\t\t{\n\t\t\t\"hasOutputStage/out\",\n\t\t\tbson.A{\n\t\t\t\tbson.D{{\"$out\", bson.D{\n\t\t\t\t\t{\"db\", \"output-db\"},\n\t\t\t\t\t{\"coll\", \"output-collection\"},\n\t\t\t\t}}},\n\t\t\t},\n\t\t\tbson.A{\n\t\t\t\tbson.D{{\"$out\", bson.D{\n\t\t\t\t\t{\"db\", \"output-db\"},\n\t\t\t\t\t{\"coll\", \"output-collection\"},\n\t\t\t\t}}},\n\t\t\t},\n\t\t\ttrue,\n\t\t\tnil,\n\t\t},\n\t\t{\n\t\t\t\"hasOutputStage/merge\",\n\t\t\tbson.A{\n\t\t\t\tbson.D{{\"$merge\", bson.D{\n\t\t\t\t\t{\"into\", bson.D{\n\t\t\t\t\t\t{\"db\", \"output-db\"},\n\t\t\t\t\t\t{\"coll\", \"output-collection\"},\n\t\t\t\t\t}},\n\t\t\t\t}}},\n\t\t\t},\n\t\t\tbson.A{\n\t\t\t\tbson.D{{\"$merge\", bson.D{\n\t\t\t\t\t{\"into\", bson.D{\n\t\t\t\t\t\t{\"db\", \"output-db\"},\n\t\t\t\t\t\t{\"coll\", \"output-collection\"},\n\t\t\t\t\t}},\n\t\t\t\t}}},\n\t\t\t},\n\t\t\ttrue,\n\t\t\tnil,\n\t\t},\n\t\t{\n\t\t\t\"semantic single document/bson.D\",\n\t\t\tbson.D{{\"x\", 1}},\n\t\t\tnil,\n\t\t\tfalse,\n\t\t\terrors.New(\"bson.D is not an allowed pipeline type as it represents a single document. Use bson.A or mongo.Pipeline instead\"),\n\t\t},\n\t\t{\n\t\t\t\"semantic single document/bson.Raw\",\n\t\t\tbson.Raw(doc),\n\t\t\tnil,\n\t\t\tfalse,\n\t\t\terrors.New(\"bson.Raw is not an allowed pipeline type as it represents a single document. Use bson.A or mongo.Pipeline instead\"),\n\t\t},\n\t\t{\n\t\t\t\"semantic single document/bsoncore.Document\",\n\t\t\tbsoncore.Document(doc),\n\t\t\tnil,\n\t\t\tfalse,\n\t\t\terrors.New(\"bsoncore.Document is not an allowed pipeline type as it represents a single document. Use bson.A or mongo.Pipeline instead\"),\n\t\t},\n\t\t{\n\t\t\t\"semantic single document/empty bson.D\",\n\t\t\tbson.D{},\n\t\t\tbson.A{},\n\t\t\tfalse,\n\t\t\tnil,\n\t\t},\n\t\t{\n\t\t\t\"semantic single document/empty bson.Raw\",\n\t\t\tbson.Raw{},\n\t\t\tbson.A{},\n\t\t\tfalse,\n\t\t\tnil,\n\t\t},\n\t\t{\n\t\t\t\"semantic single document/empty bsoncore.Document\",\n\t\t\tbsoncore.Document{},\n\t\t\tbson.A{},\n\t\t\tfalse,\n\t\t\tnil,\n\t\t},\n\t\t{\n\t\t\t\"bsoncore.Array/success\",\n\t\t\tbsoncore.Array(arr),\n\t\t\tbson.A{\n\t\t\t\tbson.D{{\"$limit\", int32(12345)}},\n\t\t\t},\n\t\t\tfalse,\n\t\t\tnil,\n\t\t},\n\t\t{\n\t\t\t\"bsoncore.Array/mergeStage\",\n\t\t\tarrMergeStage,\n\t\t\tbson.A{\n\t\t\t\tbson.D{{\"$merge\", bson.D{}}},\n\t\t\t},\n\t\t\ttrue,\n\t\t\tnil,\n\t\t},\n\t\t{\n\t\t\t\"bsoncore.Array/outStage\",\n\t\t\tarrOutStage,\n\t\t\tbson.A{\n\t\t\t\tbson.D{{\"foo\", \"bar\"}},\n\t\t\t\tbson.D{{\"baz\", \"qux\"}},\n\t\t\t\tbson.D{{\"$out\", \"myColl\"}},\n\t\t\t},\n\t\t\ttrue,\n\t\t\tnil,\n\t\t},\n\t\t{\n\t\t\t\"bsoncore.Array/middleOutStage\",\n\t\t\tarrMiddleOutStage,\n\t\t\tbson.A{\n\t\t\t\tbson.D{{\"foo\", \"bar\"}},\n\t\t\t\tbson.D{{\"$out\", \"myColl\"}},\n\t\t\t\tbson.D{{\"baz\", \"qux\"}},\n\t\t\t},\n\t\t\tfalse,\n\t\t\tnil,\n\t\t},\n\t}\n\n\tfor _, tc := range testCases {\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\tarr, hasOutputStage, err := marshalAggregatePipeline(tc.pipeline, nil, nil)\n\t\t\tassert.Equal(t, tc.hasOutputStage, hasOutputStage, \"expected hasOutputStage %v, got %v\",\n\t\t\t\ttc.hasOutputStage, hasOutputStage)\n\t\t\tif tc.err != nil {\n\t\t\t\tassert.NotNil(t, err)\n\t\t\t\tassert.EqualError(t, err, tc.err.Error())\n\t\t\t} else {\n\t\t\t\tassert.Nil(t, err)\n\t\t\t}\n\n\t\t\tvar expected bsoncore.Document\n\t\t\tif tc.arr != nil {\n\t\t\t\t_, expectedBSON, err := bson.MarshalValue(tc.arr)\n\t\t\t\tassert.Nil(t, err, \"MarshalValue error: %v\", err)\n\t\t\t\texpected = bsoncore.Document(expectedBSON)\n\t\t\t}\n\t\t\tassert.Equal(t, expected, arr, \"expected array %v, got %v\", expected, arr)\n\t\t})\n\t}\n}\n\nfunc TestMarshalValue(t *testing.T) {\n\tt.Parallel()\n\n\tvalueMarshaler := bvMarsh{\n\t\tt:    bson.TypeString,\n\t\tdata: bsoncore.AppendString(nil, \"foo\"),\n\t}\n\n\ttestCases := []struct {\n\t\tname     string\n\t\tvalue    any\n\t\tbsonOpts *options.BSONOptions\n\t\tregistry *bson.Registry\n\t\twant     bsoncore.Value\n\t\twantErr  error\n\t}{\n\t\t{\n\t\t\tname:    \"nil document\",\n\t\t\tvalue:   nil,\n\t\t\twantErr: codecutil.ErrNilValue,\n\t\t},\n\t\t{\n\t\t\tname:  \"value marshaler\",\n\t\t\tvalue: valueMarshaler,\n\t\t\twant: bsoncore.Value{\n\t\t\t\tType: bsoncore.Type(valueMarshaler.t),\n\t\t\t\tData: valueMarshaler.data,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:  \"document\",\n\t\t\tvalue: bson.D{{Key: \"x\", Value: int64(1)}},\n\t\t\twant: bsoncore.Value{\n\t\t\t\tType: bsoncore.TypeEmbeddedDocument,\n\t\t\t\tData: bsoncore.NewDocumentBuilder().\n\t\t\t\t\tAppendInt64(\"x\", 1).\n\t\t\t\t\tBuild(),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"custom encode options\",\n\t\t\tvalue: struct {\n\t\t\t\tInt         int64\n\t\t\t\tNilBytes    []byte\n\t\t\t\tNilMap      map[string]any\n\t\t\t\tNilStrings  []string\n\t\t\t\tZeroStruct  struct{ X int } `bson:\"_,omitempty\"`\n\t\t\t\tStringerMap map[*bson.RawValue]bool\n\t\t\t\tBSONField   string `json:\"jsonField\"`\n\t\t\t}{\n\t\t\t\tInt:         1,\n\t\t\t\tNilBytes:    nil,\n\t\t\t\tNilMap:      nil,\n\t\t\t\tNilStrings:  nil,\n\t\t\t\tStringerMap: map[*bson.RawValue]bool{{}: true},\n\t\t\t},\n\t\t\tbsonOpts: &options.BSONOptions{\n\t\t\t\tIntMinSize:              true,\n\t\t\t\tNilByteSliceAsEmpty:     true,\n\t\t\t\tNilMapAsEmpty:           true,\n\t\t\t\tNilSliceAsEmpty:         true,\n\t\t\t\tOmitZeroStruct:          true,\n\t\t\t\tStringifyMapKeysWithFmt: true,\n\t\t\t\tUseJSONStructTags:       true,\n\t\t\t},\n\t\t\twant: bsoncore.Value{\n\t\t\t\tType: bsoncore.TypeEmbeddedDocument,\n\t\t\t\tData: bsoncore.NewDocumentBuilder().\n\t\t\t\t\tAppendInt32(\"int\", 1).\n\t\t\t\t\tAppendBinary(\"nilbytes\", 0, []byte{}).\n\t\t\t\t\tAppendDocument(\"nilmap\", bsoncore.NewDocumentBuilder().Build()).\n\t\t\t\t\tAppendArray(\"nilstrings\", bsoncore.NewArrayBuilder().Build()).\n\t\t\t\t\tAppendDocument(\"stringermap\", bsoncore.NewDocumentBuilder().\n\t\t\t\t\t\tAppendBoolean(\"\", true).\n\t\t\t\t\t\tBuild()).\n\t\t\t\t\tAppendString(\"jsonField\", \"\").\n\t\t\t\t\tBuild(),\n\t\t\t},\n\t\t},\n\t}\n\tfor _, tc := range testCases {\n\t\ttc := tc // Capture range variable.\n\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tgot, err := marshalValue(tc.value, tc.bsonOpts, tc.registry)\n\t\t\tassertbson.EqualValue(t, tc.want, got)\n\t\t\tassert.Equal(t, tc.wantErr, err, \"expected and actual error do not match\")\n\t\t})\n\t}\n}\n\nfunc TestGetEncoder(t *testing.T) {\n\tt.Parallel()\n\n\tencT := reflect.TypeOf((*bson.Encoder)(nil))\n\tctxT := reflect.TypeOf(bson.EncodeContext{})\n\tfor i := 0; i < encT.NumMethod(); i++ {\n\t\tm := encT.Method(i)\n\t\t// Test methods with no input/output parameter.\n\t\tif m.Type.NumIn() != 1 || m.Type.NumOut() != 0 {\n\t\t\tcontinue\n\t\t}\n\t\tt.Run(m.Name, func(t *testing.T) {\n\t\t\tvar opts options.BSONOptions\n\t\t\toptsV := reflect.ValueOf(&opts).Elem()\n\t\t\tf, ok := optsV.Type().FieldByName(m.Name)\n\t\t\trequire.True(t, ok, \"expected %s field in %s\", m.Name, optsV.Type())\n\n\t\t\twantEnc := reflect.ValueOf(bson.NewEncoder(nil))\n\t\t\t_ = wantEnc.Method(i).Call(nil)\n\t\t\twantCtx := wantEnc.Elem().Field(0)\n\t\t\trequire.Equal(t, ctxT, wantCtx.Type())\n\n\t\t\toptsV.FieldByIndex(f.Index).SetBool(true)\n\t\t\tgotEnc := getEncoder(nil, &opts, nil)\n\t\t\tgotCtx := reflect.ValueOf(gotEnc).Elem().Field(0)\n\t\t\trequire.Equal(t, ctxT, gotCtx.Type())\n\n\t\t\tassert.True(t, gotCtx.Equal(wantCtx), \"expected %v: %v, got: %v\", ctxT, wantCtx, gotCtx)\n\t\t})\n\t}\n}\n\nvar _ bson.ValueMarshaler = bvMarsh{}\n\ntype bvMarsh struct {\n\tt    bson.Type\n\tdata []byte\n\terr  error\n}\n\nfunc (b bvMarsh) MarshalBSONValue() (byte, []byte, error) {\n\treturn byte(b.t), b.data, b.err\n}\n"
  },
  {
    "path": "mongo/mongocryptd.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage mongo\n\nimport (\n\t\"context\"\n\t\"os/exec\"\n\t\"strings\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/readconcern\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/readpref\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n)\n\nconst (\n\tdefaultServerSelectionTimeout = 10 * time.Second\n\tdefaultURI                    = \"mongodb://localhost:27020\"\n\tdefaultPath                   = \"mongocryptd\"\n\tserverSelectionTimeoutStr     = \"server selection error\"\n)\n\nvar (\n\tdefaultTimeoutArgs = []string{\"--idleShutdownTimeoutSecs=60\"}\n\tdatabaseOpts       = options.Database().SetReadConcern(&readconcern.ReadConcern{}).SetReadPreference(readpref.Primary())\n)\n\ntype mongocryptdClient struct {\n\tbypassSpawn bool\n\tclient      *Client\n\tpath        string\n\tspawnArgs   []string\n}\n\n// newMongocryptdClient creates a client to mongocryptd.\n// newMongocryptdClient is expected to not be called if the crypt shared library is available.\n// The crypt shared library replaces all mongocryptd functionality.\nfunc newMongocryptdClient(opts *options.AutoEncryptionOptions) (*mongocryptdClient, error) {\n\t// create mcryptClient instance and spawn process if necessary\n\tvar bypassSpawn bool\n\tvar bypassAutoEncryption bool\n\n\tif bypass, ok := opts.ExtraOptions[\"mongocryptdBypassSpawn\"]; ok {\n\t\tbypassSpawn = bypass.(bool)\n\t}\n\tif opts.BypassAutoEncryption != nil {\n\t\tbypassAutoEncryption = *opts.BypassAutoEncryption\n\t}\n\n\tbypassQueryAnalysis := opts.BypassQueryAnalysis != nil && *opts.BypassQueryAnalysis\n\n\tmc := &mongocryptdClient{\n\t\t// mongocryptd should not be spawned if any of these conditions are true:\n\t\t// - mongocryptdBypassSpawn is passed\n\t\t// - bypassAutoEncryption is true because mongocryptd is not used during decryption\n\t\t// - bypassQueryAnalysis is true because mongocryptd is not used during decryption\n\t\tbypassSpawn: bypassSpawn || bypassAutoEncryption || bypassQueryAnalysis,\n\t}\n\n\tif !mc.bypassSpawn {\n\t\tmc.path, mc.spawnArgs = createSpawnArgs(opts.ExtraOptions)\n\t\tif err := mc.spawnProcess(); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\t// get connection string\n\turi := defaultURI\n\tif u, ok := opts.ExtraOptions[\"mongocryptdURI\"]; ok {\n\t\turi = u.(string)\n\t}\n\n\t// create client\n\tclient, err := newClient(options.Client().ApplyURI(uri).SetServerSelectionTimeout(defaultServerSelectionTimeout))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tmc.client = client\n\n\treturn mc, nil\n}\n\n// markCommand executes the given command on mongocryptd.\nfunc (mc *mongocryptdClient) markCommand(ctx context.Context, dbName string, cmd bsoncore.Document) (bsoncore.Document, error) {\n\t// Remove the explicit session from the context if one is set.\n\t// The explicit session will be from a different client.\n\t// If an explicit session is set, it is applied after automatic encryption.\n\tctx = NewSessionContext(ctx, nil)\n\tdb := mc.client.Database(dbName, databaseOpts)\n\n\tres, err := db.RunCommand(ctx, cmd).Raw()\n\t// propagate original result\n\tif err == nil {\n\t\treturn bsoncore.Document(res), nil\n\t}\n\t// wrap original error\n\tif mc.bypassSpawn || !strings.Contains(err.Error(), serverSelectionTimeoutStr) {\n\t\treturn nil, MongocryptdError{Wrapped: err}\n\t}\n\n\t// re-spawn and retry\n\tif err = mc.spawnProcess(); err != nil {\n\t\treturn nil, err\n\t}\n\tres, err = db.RunCommand(ctx, cmd).Raw()\n\tif err != nil {\n\t\treturn nil, MongocryptdError{Wrapped: err}\n\t}\n\treturn bsoncore.Document(res), nil\n}\n\n// connect connects the underlying Client instance. This must be called before performing any mark operations.\nfunc (mc *mongocryptdClient) connect() error {\n\treturn mc.client.connect()\n}\n\n// disconnect disconnects the underlying Client instance. This should be called after all operations have completed.\nfunc (mc *mongocryptdClient) disconnect(ctx context.Context) error {\n\treturn mc.client.Disconnect(ctx)\n}\n\nfunc (mc *mongocryptdClient) spawnProcess() error {\n\t// Ignore gosec warning about subprocess launched with externally-provided path variable.\n\t/* #nosec G204 */\n\tcmd := exec.Command(mc.path, mc.spawnArgs...)\n\tcmd.Stdout = nil\n\tcmd.Stderr = nil\n\treturn cmd.Start()\n}\n\n// createSpawnArgs creates arguments to spawn mcryptClient. It returns the path and a slice of arguments.\nfunc createSpawnArgs(opts map[string]any) (string, []string) {\n\tvar spawnArgs []string\n\n\t// get command path\n\tpath := defaultPath\n\tif p, ok := opts[\"mongocryptdPath\"]; ok {\n\t\tpath = p.(string)\n\t}\n\n\t// add specified options\n\tif sa, ok := opts[\"mongocryptdSpawnArgs\"]; ok {\n\t\tspawnArgs = append(spawnArgs, sa.([]string)...)\n\t}\n\n\t// add timeout options if necessary\n\tvar foundTimeout bool\n\tfor _, arg := range spawnArgs {\n\t\t// need to use HasPrefix instead of doing an exact equality check because both\n\t\t// mongocryptd supports both [--idleShutdownTimeoutSecs, 0] and [--idleShutdownTimeoutSecs=0]\n\t\tif strings.HasPrefix(arg, \"--idleShutdownTimeoutSecs\") {\n\t\t\tfoundTimeout = true\n\t\t\tbreak\n\t\t}\n\t}\n\tif !foundTimeout {\n\t\tspawnArgs = append(spawnArgs, defaultTimeoutArgs...)\n\t}\n\n\treturn path, spawnArgs\n}\n"
  },
  {
    "path": "mongo/mongointernal.go",
    "content": "// Copyright (C) MongoDB, Inc. 2025-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\n//go:build mongointernal\n\npackage mongo\n\nimport (\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/session\"\n)\n\n// NewSessionWithLSID returns a Session with the given sessionID document. The\n// sessionID is a BSON document with key \"id\" containing a 16-byte UUID (binary\n// subtype 4).\n//\n// Sessions returned by NewSessionWithLSID are never added to the driver's\n// session pool. Calling \"EndSession\" or \"ClientSession.SetServer\" on a Session\n// returned by NewSessionWithLSID will panic.\n//\n// NewSessionWithLSID is intended only for internal use and may be changed or\n// removed at any time.\nfunc NewSessionWithLSID(client *Client, sessionID bson.Raw) *Session {\n\treturn &Session{\n\t\tclientSession: &session.Client{\n\t\t\tServer: &session.Server{\n\t\t\t\tSessionID: bsoncore.Document(sessionID),\n\t\t\t\tLastUsed:  time.Now(),\n\t\t\t},\n\t\t\tClientID: client.id,\n\t\t},\n\t\tclient:     client,\n\t\tdeployment: client.deployment,\n\t}\n}\n"
  },
  {
    "path": "mongo/ocsp_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage mongo\n\nimport (\n\t\"crypto/tls\"\n\t\"os\"\n\t\"runtime\"\n\t\"strconv\"\n\t\"testing\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/integtest\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/readpref\"\n)\n\nfunc TestOCSP(t *testing.T) {\n\tsuccessEnvVar := os.Getenv(\"OCSP_TLS_SHOULD_SUCCEED\")\n\tif successEnvVar == \"\" {\n\t\tt.Skip(\"skipping because OCSP_TLS_SHOULD_SUCCEED not set\")\n\t}\n\tshouldSucceed, err := strconv.ParseBool(successEnvVar)\n\tassert.Nil(t, err, \"invalid value for OCSP_TLS_SHOULD_SUCCEED; expected true or false, got %v\", successEnvVar)\n\n\tcs := integtest.ConnString(t)\n\n\tt.Run(\"tls\", func(t *testing.T) {\n\t\tclientOpts := createOCSPClientOptions(cs.Original)\n\t\tclient, err := Connect(clientOpts)\n\t\tassert.Nil(t, err, \"Connect error: %v\", err)\n\t\tdefer func() { _ = client.Disconnect(bgCtx) }()\n\n\t\terr = client.Ping(bgCtx, readpref.Primary())\n\t\tif shouldSucceed {\n\t\t\tassert.Nil(t, err, \"Ping error: %v\", err)\n\t\t\treturn\n\t\t}\n\t\t// Log the error we got so it's visible in Evergreen and we can verify the tests are running as expected there.\n\t\tt.Logf(\"got Ping error: %v\\n\", err)\n\t\tassert.NotNil(t, err, \"expected Ping error, got nil\")\n\t})\n\tt.Run(\"tlsInsecure\", func(t *testing.T) {\n\t\tclientOpts := createInsecureOCSPClientOptions(cs.Original)\n\t\tclient, err := Connect(clientOpts)\n\t\tassert.Nil(t, err, \"Connect error: %v\", err)\n\t\tdefer func() { _ = client.Disconnect(bgCtx) }()\n\n\t\terr = client.Ping(bgCtx, readpref.Primary())\n\t\tassert.Nil(t, err, \"Ping error: %v\", err)\n\t})\n}\n\nfunc createOCSPClientOptions(uri string) *options.ClientOptions {\n\topts := options.Client().ApplyURI(uri)\n\n\ttimeout := 500 * time.Millisecond\n\tif runtime.GOOS == \"windows\" {\n\t\t// Non-stapled OCSP endpoint checks are slow on Windows.\n\t\ttimeout = 5 * time.Second\n\t}\n\topts.SetServerSelectionTimeout(timeout)\n\treturn opts\n}\n\nfunc createInsecureOCSPClientOptions(uri string) *options.ClientOptions {\n\topts := createOCSPClientOptions(uri)\n\n\tif opts.TLSConfig != nil {\n\t\topts.TLSConfig.InsecureSkipVerify = true\n\t\topts.SetTLSConfig(opts.TLSConfig)\n\n\t\treturn opts\n\t}\n\n\treturn opts.SetTLSConfig(&tls.Config{\n\t\tInsecureSkipVerify: true,\n\t})\n}\n"
  },
  {
    "path": "mongo/options/aggregateoptions.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage options\n\nimport (\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/optionsutil\"\n)\n\n// AggregateOptions represents arguments that can be used to configure an\n// Aggregate operation.\n//\n// See corresponding setter methods for documentation.\ntype AggregateOptions struct {\n\tAllowDiskUse             *bool\n\tBatchSize                *int32\n\tBypassDocumentValidation *bool\n\tCollation                *Collation\n\tMaxAwaitTime             *time.Duration\n\tComment                  any\n\tHint                     any\n\tLet                      any\n\tCustom                   bson.M\n\n\t// Deprecated: This option is for internal use only and should not be set. It may be changed or removed in any\n\t// release.\n\tInternal optionsutil.Options\n}\n\n// AggregateOptionsBuilder contains options to configure aggregate operations.\n// Each option can be set through setter functions. See documentation for each\n// setter function for an explanation of the option.\ntype AggregateOptionsBuilder struct {\n\tOpts []func(*AggregateOptions) error\n}\n\n// Aggregate creates a new AggregateOptions instance.\nfunc Aggregate() *AggregateOptionsBuilder {\n\treturn &AggregateOptionsBuilder{}\n}\n\n// List returns a list of AggergateOptions setter functions.\nfunc (ao *AggregateOptionsBuilder) List() []func(*AggregateOptions) error {\n\treturn ao.Opts\n}\n\n// SetAllowDiskUse sets the value for the AllowDiskUse field. If true, the operation can write to temporary\n// files in the _tmp subdirectory of the database directory path on the server. The default value is false.\nfunc (ao *AggregateOptionsBuilder) SetAllowDiskUse(b bool) *AggregateOptionsBuilder {\n\tao.Opts = append(ao.Opts, func(opts *AggregateOptions) error {\n\t\topts.AllowDiskUse = &b\n\n\t\treturn nil\n\t})\n\n\treturn ao\n}\n\n// SetBatchSize sets the value for the BatchSize field. Specifies the maximum number of documents\n// to be included in each batch returned by the server.\nfunc (ao *AggregateOptionsBuilder) SetBatchSize(i int32) *AggregateOptionsBuilder {\n\tao.Opts = append(ao.Opts, func(opts *AggregateOptions) error {\n\t\topts.BatchSize = &i\n\n\t\treturn nil\n\t})\n\n\treturn ao\n}\n\n// SetBypassDocumentValidation sets the value for the BypassDocumentValidation field. If true, writes\n// executed as part of the operation will opt out of document-level validation on the server. The default value\n// is false. See https://www.mongodb.com/docs/manual/core/schema-validation/ for more information about\n// document validation.\nfunc (ao *AggregateOptionsBuilder) SetBypassDocumentValidation(b bool) *AggregateOptionsBuilder {\n\tao.Opts = append(ao.Opts, func(opts *AggregateOptions) error {\n\t\topts.BypassDocumentValidation = &b\n\n\t\treturn nil\n\t})\n\n\treturn ao\n}\n\n// SetCollation sets the value for the Collation field. Specifies a collation to\n// use for string comparisons during the operation. The default value is nil,\n// which means the default collation of the collection will be used.\nfunc (ao *AggregateOptionsBuilder) SetCollation(c *Collation) *AggregateOptionsBuilder {\n\tao.Opts = append(ao.Opts, func(opts *AggregateOptions) error {\n\t\topts.Collation = c\n\n\t\treturn nil\n\t})\n\n\treturn ao\n}\n\n// SetMaxAwaitTime sets the value for the MaxAwaitTime field. Specifies maximum amount of time\n// that the server should wait for new documents to satisfy a tailable cursor query.\nfunc (ao *AggregateOptionsBuilder) SetMaxAwaitTime(d time.Duration) *AggregateOptionsBuilder {\n\tao.Opts = append(ao.Opts, func(opts *AggregateOptions) error {\n\t\topts.MaxAwaitTime = &d\n\n\t\treturn nil\n\t})\n\n\treturn ao\n}\n\n// SetComment sets the value for the Comment field. Specifies a string or document that will be included in\n// server logs, profiling logs, and currentOp queries to help trace the operation. The default is nil,\n// which means that no comment will be included in the logs.\nfunc (ao *AggregateOptionsBuilder) SetComment(comment any) *AggregateOptionsBuilder {\n\tao.Opts = append(ao.Opts, func(opts *AggregateOptions) error {\n\t\topts.Comment = comment\n\n\t\treturn nil\n\t})\n\n\treturn ao\n}\n\n// SetHint sets the value for the Hint field. Specifies the index to use for the aggregation. This should\n// either be the index name as a string or the index specification as a document. The hint does not apply to\n// $lookup and $graphLookup aggregation stages. The driver will return an error if the hint parameter\n// is a multi-key map. The default value is nil, which means that no hint will be sent.\nfunc (ao *AggregateOptionsBuilder) SetHint(h any) *AggregateOptionsBuilder {\n\tao.Opts = append(ao.Opts, func(opts *AggregateOptions) error {\n\t\topts.Hint = h\n\n\t\treturn nil\n\t})\n\n\treturn ao\n}\n\n// SetLet sets the value for the Let field. Specifies parameters for the aggregate expression. This\n// option is only valid for MongoDB versions >= 5.0. Older servers will report an error for using this\n// option. This must be a document mapping parameter names to values. Values must be constant or closed\n// expressions that do not reference document fields. Parameters can then be accessed as variables in\n// an aggregate expression context (e.g. \"$$var\").\nfunc (ao *AggregateOptionsBuilder) SetLet(let any) *AggregateOptionsBuilder {\n\tao.Opts = append(ao.Opts, func(opts *AggregateOptions) error {\n\t\topts.Let = let\n\n\t\treturn nil\n\t})\n\n\treturn ao\n}\n\n// SetCustom sets the value for the Custom field. Key-value pairs of the BSON map should correlate\n// with desired option names and values. Values must be Marshalable. Custom options may conflict\n// with non-custom options, and custom options bypass client-side validation. Prefer using non-custom\n// options where possible.\nfunc (ao *AggregateOptionsBuilder) SetCustom(c bson.M) *AggregateOptionsBuilder {\n\tao.Opts = append(ao.Opts, func(opts *AggregateOptions) error {\n\t\topts.Custom = c\n\n\t\treturn nil\n\t})\n\n\treturn ao\n}\n"
  },
  {
    "path": "mongo/options/autoencryptionoptions.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage options\n\nimport (\n\t\"crypto/tls\"\n\t\"net/http\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/httputil\"\n)\n\n// AutoEncryptionOptions represents arguments used to configure auto encryption/decryption behavior for a mongo.Client\n// instance.\n//\n// Automatic encryption is an enterprise only feature that only applies to operations on a collection. Automatic\n// encryption is not supported for operations on a database or view, and operations that are not bypassed will result\n// in error. Too bypass automatic encryption for all operations, set BypassAutoEncryption=true.\n//\n// Auto encryption requires the authenticated user to have the listCollections privilege action.\n//\n// If automatic encryption fails on an operation, use a MongoClient configured with bypassAutoEncryption=true and use\n// ClientEncryption.encrypt() to manually encrypt values.\n//\n// Enabling In-Use Encryption reduces the maximum document and message size (using a maxBsonObjectSize of 2MiB and\n// maxMessageSizeBytes of 6MB) and may have a negative performance impact.\n//\n// See corresponding setter methods for documentation.\ntype AutoEncryptionOptions struct {\n\tKeyVaultClientOptions *ClientOptions\n\tKeyVaultNamespace     string\n\tKmsProviders          map[string]map[string]any\n\tSchemaMap             map[string]any\n\tBypassAutoEncryption  *bool\n\tExtraOptions          map[string]any\n\tTLSConfig             map[string]*tls.Config\n\tHTTPClient            *http.Client\n\tEncryptedFieldsMap    map[string]any\n\tBypassQueryAnalysis   *bool\n\tKeyExpiration         *time.Duration\n}\n\n// AutoEncryption creates a new AutoEncryptionOptions configured with default values.\nfunc AutoEncryption() *AutoEncryptionOptions {\n\treturn &AutoEncryptionOptions{\n\t\tHTTPClient: httputil.DefaultHTTPClient,\n\t}\n}\n\n// SetKeyVaultClientOptions specifies options for the client used to communicate with the key vault collection.\n//\n// If this is set, it is used to create an internal mongo.Client.\n// Otherwise, if the target mongo.Client being configured has an unlimited connection pool size (i.e. maxPoolSize=0),\n// it is reused to interact with the key vault collection.\n// Otherwise, if the target mongo.Client has a limited connection pool size, a separate internal mongo.Client is used\n// (and created if necessary). The internal mongo.Client may be shared during automatic encryption (if\n// BypassAutomaticEncryption is false). The internal mongo.Client is configured with the same options as the target\n// mongo.Client except minPoolSize is set to 0 and AutoEncryptionOptions is omitted.\nfunc (a *AutoEncryptionOptions) SetKeyVaultClientOptions(opts *ClientOptions) *AutoEncryptionOptions {\n\ta.KeyVaultClientOptions = opts\n\n\treturn a\n}\n\n// SetKeyVaultNamespace specifies the namespace of the key vault collection. This is required.\nfunc (a *AutoEncryptionOptions) SetKeyVaultNamespace(ns string) *AutoEncryptionOptions {\n\ta.KeyVaultNamespace = ns\n\n\treturn a\n}\n\n// SetKmsProviders specifies options for KMS providers. This is required.\nfunc (a *AutoEncryptionOptions) SetKmsProviders(providers map[string]map[string]any) *AutoEncryptionOptions {\n\ta.KmsProviders = providers\n\n\treturn a\n}\n\n// SetSchemaMap specifies a map from namespace to local schema document. Schemas supplied in the schemaMap only apply\n// to configuring automatic encryption for Client-Side Field Level Encryption. Other validation rules in the JSON schema\n// will not be enforced by the driver and will result in an error.\n//\n// Supplying a schemaMap provides more security than relying on JSON Schemas obtained from the server. It protects\n// against a malicious server advertising a false JSON Schema, which could trick the client into sending unencrypted\n// data that should be encrypted.\nfunc (a *AutoEncryptionOptions) SetSchemaMap(schemaMap map[string]any) *AutoEncryptionOptions {\n\ta.SchemaMap = schemaMap\n\n\treturn a\n}\n\n// SetBypassAutoEncryption specifies whether or not auto encryption should be done.\n//\n// If this is unset or false and target mongo.Client being configured has an unlimited connection pool size\n// (i.e. maxPoolSize=0), it is reused in the process of auto encryption.\n// Otherwise, if the target mongo.Client has a limited connection pool size, a separate internal mongo.Client is used\n// (and created if necessary). The internal mongo.Client may be shared for key vault operations (if KeyVaultClient is\n// unset). The internal mongo.Client is configured with the same options as the target mongo.Client except minPoolSize\n// is set to 0 and AutoEncryptionOptions is omitted.\nfunc (a *AutoEncryptionOptions) SetBypassAutoEncryption(bypass bool) *AutoEncryptionOptions {\n\ta.BypassAutoEncryption = &bypass\n\n\treturn a\n}\n\n// SetExtraOptions specifies a map of options to configure the mongocryptd process or mongo_crypt shared library.\n//\n// # Supported Extra Options\n//\n// \"mongocryptdURI\" - The mongocryptd URI. Allows setting a custom URI used to communicate with the\n// mongocryptd process. The default is \"mongodb://localhost:27020\", which works with the default\n// mongocryptd process spawned by the Client. Must be a string.\n//\n// \"mongocryptdBypassSpawn\" - If set to true, the Client will not attempt to spawn a mongocryptd\n// process. Must be a bool.\n//\n// \"mongocryptdSpawnPath\" - The path used when spawning mongocryptd.\n// Defaults to empty string and spawns mongocryptd from system path. Must be a string.\n//\n// \"mongocryptdSpawnArgs\" - Command line arguments passed when spawning mongocryptd.\n// Defaults to [\"--idleShutdownTimeoutSecs=60\"]. Must be an array of strings.\n//\n// \"cryptSharedLibRequired\" - If set to true, Client creation will return an error if the\n// crypt_shared library is not loaded. If unset or set to false, Client creation will not return an\n// error if the crypt_shared library is not loaded. The default is unset. Must be a bool.\n//\n// \"cryptSharedLibPath\" - The crypt_shared library override path. This must be the path to the\n// crypt_shared dynamic library file (for example, a .so, .dll, or .dylib file), not the directory\n// that contains it. If the override path is a relative path, it will be resolved relative to the\n// working directory of the process. If the override path is a relative path and the first path\n// component is the literal string \"$ORIGIN\", the \"$ORIGIN\" component will be replaced by the\n// absolute path to the directory containing the linked libmongocrypt library. Setting an override\n// path disables the default system library search path. If an override path is specified but the\n// crypt_shared library cannot be loaded, Client creation will return an error. Must be a string.\nfunc (a *AutoEncryptionOptions) SetExtraOptions(extraOpts map[string]any) *AutoEncryptionOptions {\n\ta.ExtraOptions = extraOpts\n\n\treturn a\n}\n\n// SetTLSConfig specifies tls.Config instances for each KMS provider to use to configure TLS on all connections created\n// to the KMS provider.\nfunc (a *AutoEncryptionOptions) SetTLSConfig(cfg map[string]*tls.Config) *AutoEncryptionOptions {\n\t// This should only be used to set custom TLS configurations. By default, the connection will use an empty tls.Config{} with MinVersion set to tls.VersionTLS12.\n\ta.TLSConfig = cfg\n\n\treturn a\n}\n\n// SetEncryptedFieldsMap specifies a map from namespace to local EncryptedFieldsMap document.\n// EncryptedFieldsMap is used for Queryable Encryption.\nfunc (a *AutoEncryptionOptions) SetEncryptedFieldsMap(ef map[string]any) *AutoEncryptionOptions {\n\ta.EncryptedFieldsMap = ef\n\n\treturn a\n}\n\n// SetBypassQueryAnalysis specifies whether or not query analysis should be used for automatic encryption.\n// Use this option when using explicit encryption with Queryable Encryption.\nfunc (a *AutoEncryptionOptions) SetBypassQueryAnalysis(bypass bool) *AutoEncryptionOptions {\n\ta.BypassQueryAnalysis = &bypass\n\n\treturn a\n}\n\n// SetKeyExpiration specifies duration for the key expiration. 0 or negative value means \"never expire\".\n// The granularity is in milliseconds. Any sub-millisecond fraction will be rounded up.\nfunc (a *AutoEncryptionOptions) SetKeyExpiration(expiration time.Duration) *AutoEncryptionOptions {\n\ta.KeyExpiration = &expiration\n\n\treturn a\n}\n"
  },
  {
    "path": "mongo/options/bulkwriteoptions.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage options\n\nimport \"go.mongodb.org/mongo-driver/v2/internal/optionsutil\"\n\n// DefaultOrdered is the default value for the Ordered option in BulkWriteOptions.\nvar DefaultOrdered = true\n\n// BulkWriteOptions represents arguments that can be used to configure a\n// BulkWrite operation.\n//\n// See corresponding setter methods for documentation.\ntype BulkWriteOptions struct {\n\tBypassDocumentValidation *bool\n\tComment                  any\n\tOrdered                  *bool\n\tLet                      any\n\n\t// Deprecated: This option is for internal use only and should not be set. It may be changed or removed in any\n\t// release.\n\tInternal optionsutil.Options\n}\n\n// BulkWriteOptionsBuilder contains options to configure bulk write operations.\n// Each option can be set through setter functions. See documentation for each\n// setter function for an explanation of the option.\ntype BulkWriteOptionsBuilder struct {\n\tOpts []func(*BulkWriteOptions) error\n}\n\n// BulkWrite creates a new *BulkWriteOptions instance.\nfunc BulkWrite() *BulkWriteOptionsBuilder {\n\topts := &BulkWriteOptionsBuilder{}\n\topts = opts.SetOrdered(DefaultOrdered)\n\n\treturn opts\n}\n\n// List returns a list of BulkWriteOptions setter functions.\nfunc (b *BulkWriteOptionsBuilder) List() []func(*BulkWriteOptions) error {\n\treturn b.Opts\n}\n\n// SetComment sets the value for the Comment field. Specifies a string or document that will be included in\n// server logs, profiling logs, and currentOp queries to help tracethe operation.  The default value is nil,\n// which means that no comment will be included in the logs.\nfunc (b *BulkWriteOptionsBuilder) SetComment(comment any) *BulkWriteOptionsBuilder {\n\tb.Opts = append(b.Opts, func(opts *BulkWriteOptions) error {\n\t\topts.Comment = comment\n\n\t\treturn nil\n\t})\n\n\treturn b\n}\n\n// SetOrdered sets the value for the Ordered field. If true, no writes will be executed after one fails.\n// The default value is true.\nfunc (b *BulkWriteOptionsBuilder) SetOrdered(ordered bool) *BulkWriteOptionsBuilder {\n\tb.Opts = append(b.Opts, func(opts *BulkWriteOptions) error {\n\t\topts.Ordered = &ordered\n\n\t\treturn nil\n\t})\n\n\treturn b\n}\n\n// SetBypassDocumentValidation sets the value for the BypassDocumentValidation field. If true, writes\n// executed as part of the operation will opt out of document-level validation on the server. The default value is\n// false. See https://www.mongodb.com/docs/manual/core/schema-validation/ for more information about document\n// validation.\nfunc (b *BulkWriteOptionsBuilder) SetBypassDocumentValidation(bypass bool) *BulkWriteOptionsBuilder {\n\tb.Opts = append(b.Opts, func(opts *BulkWriteOptions) error {\n\t\topts.BypassDocumentValidation = &bypass\n\n\t\treturn nil\n\t})\n\n\treturn b\n}\n\n// SetLet sets the value for the Let field. Let specifies parameters for all update and delete commands in the BulkWrite.\n// This option is only valid for MongoDB versions >= 5.0. Older servers will report an error for using this option.\n// This must be a document mapping parameter names to values. Values must be constant or closed expressions that do not\n// reference document fields. Parameters can then be accessed as variables in an aggregate expression context (e.g. \"$$var\").\nfunc (b *BulkWriteOptionsBuilder) SetLet(let any) *BulkWriteOptionsBuilder {\n\tb.Opts = append(b.Opts, func(opts *BulkWriteOptions) error {\n\t\topts.Let = &let\n\n\t\treturn nil\n\t})\n\n\treturn b\n}\n"
  },
  {
    "path": "mongo/options/changestreamoptions.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage options\n\nimport (\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n)\n\n// ChangeStreamOptions represents arguments that can be used to configure a Watch operation.\n//\n// See corresponding setter methods for documentation.\ntype ChangeStreamOptions struct {\n\tBatchSize                *int32\n\tCollation                *Collation\n\tComment                  any\n\tFullDocument             *FullDocument\n\tFullDocumentBeforeChange *FullDocument\n\tMaxAwaitTime             *time.Duration\n\tResumeAfter              any\n\tShowExpandedEvents       *bool\n\tStartAtOperationTime     *bson.Timestamp\n\tStartAfter               any\n\tCustom                   bson.M\n\tCustomPipeline           bson.M\n}\n\n// ChangeStreamOptionsBuilder contains options to configure change stream\n// operations. Each option can be set through setter functions. See\n// documentation for each setter function for an explanation of the option.\ntype ChangeStreamOptionsBuilder struct {\n\tOpts []func(*ChangeStreamOptions) error\n}\n\n// ChangeStream creates a new ChangeStreamOptions instance.\nfunc ChangeStream() *ChangeStreamOptionsBuilder {\n\treturn &ChangeStreamOptionsBuilder{}\n}\n\n// List returns a list of ChangeStreamOptions setter functions.\nfunc (cso *ChangeStreamOptionsBuilder) List() []func(*ChangeStreamOptions) error {\n\treturn cso.Opts\n}\n\n// SetBatchSize sets the value for the BatchSize field. Specifies the maximum number of documents to\n// be included in each batch returned by the server.\nfunc (cso *ChangeStreamOptionsBuilder) SetBatchSize(i int32) *ChangeStreamOptionsBuilder {\n\tcso.Opts = append(cso.Opts, func(opts *ChangeStreamOptions) error {\n\t\topts.BatchSize = &i\n\t\treturn nil\n\t})\n\treturn cso\n}\n\n// SetCollation sets the value for the Collation field. Specifies a collation to\n// use for string comparisons during the operation. The default value is nil,\n// which means the default collation of the collection will be used.\nfunc (cso *ChangeStreamOptionsBuilder) SetCollation(c Collation) *ChangeStreamOptionsBuilder {\n\tcso.Opts = append(cso.Opts, func(opts *ChangeStreamOptions) error {\n\t\topts.Collation = &c\n\t\treturn nil\n\t})\n\treturn cso\n}\n\n// SetComment sets the value for the Comment field. Specifies a string or document that will be included in\n// server logs, profiling logs, and currentOp queries to help trace the operation. The default is nil,\n// which means that no comment will be included in the logs.\nfunc (cso *ChangeStreamOptionsBuilder) SetComment(comment any) *ChangeStreamOptionsBuilder {\n\tcso.Opts = append(cso.Opts, func(opts *ChangeStreamOptions) error {\n\t\topts.Comment = comment\n\t\treturn nil\n\t})\n\treturn cso\n}\n\n// SetFullDocument sets the value for the FullDocument field. Specifies how the updated document should be\n// returned in change notifications for update operations. The default is options.Default, which means that\n// only partial update deltas will be included in the change notification.\nfunc (cso *ChangeStreamOptionsBuilder) SetFullDocument(fd FullDocument) *ChangeStreamOptionsBuilder {\n\tcso.Opts = append(cso.Opts, func(opts *ChangeStreamOptions) error {\n\t\topts.FullDocument = &fd\n\t\treturn nil\n\t})\n\treturn cso\n}\n\n// SetFullDocumentBeforeChange sets the value for the FullDocumentBeforeChange field. Specifies how the\n// pre-update document should be returned in change notifications for update operations. The default\n// is options.Off, which means that the pre-update document will not be included in the change notification.\nfunc (cso *ChangeStreamOptionsBuilder) SetFullDocumentBeforeChange(fdbc FullDocument) *ChangeStreamOptionsBuilder {\n\tcso.Opts = append(cso.Opts, func(opts *ChangeStreamOptions) error {\n\t\topts.FullDocumentBeforeChange = &fdbc\n\t\treturn nil\n\t})\n\treturn cso\n}\n\n// SetMaxAwaitTime sets the value for the MaxAwaitTime field. The maximum amount of time that the server should\n// wait for new documents to satisfy a tailable cursor query.\nfunc (cso *ChangeStreamOptionsBuilder) SetMaxAwaitTime(d time.Duration) *ChangeStreamOptionsBuilder {\n\tcso.Opts = append(cso.Opts, func(opts *ChangeStreamOptions) error {\n\t\topts.MaxAwaitTime = &d\n\t\treturn nil\n\t})\n\treturn cso\n}\n\n// SetResumeAfter sets the value for the ResumeAfter field. Specifies a document specifying the logical starting\n// point for the change stream. Only changes corresponding to an oplog entry immediately after the resume token\n// will be returned. If this is specified, StartAtOperationTime and StartAfter must not be set.\nfunc (cso *ChangeStreamOptionsBuilder) SetResumeAfter(rt any) *ChangeStreamOptionsBuilder {\n\tcso.Opts = append(cso.Opts, func(opts *ChangeStreamOptions) error {\n\t\topts.ResumeAfter = rt\n\t\treturn nil\n\t})\n\treturn cso\n}\n\n// SetShowExpandedEvents sets the value for the ShowExpandedEvents field. ShowExpandedEvents specifies whether\n// the server will return an expanded list of change stream events. Additional events include: createIndexes,\n// dropIndexes, modify, create, shardCollection, reshardCollection and refineCollectionShardKey. This option\n// is only valid for MongoDB versions >= 6.0.\nfunc (cso *ChangeStreamOptionsBuilder) SetShowExpandedEvents(see bool) *ChangeStreamOptionsBuilder {\n\tcso.Opts = append(cso.Opts, func(opts *ChangeStreamOptions) error {\n\t\topts.ShowExpandedEvents = &see\n\t\treturn nil\n\t})\n\treturn cso\n}\n\n// SetStartAtOperationTime sets the value for the StartAtOperationTime field. If specified, the change stream\n// will only return changes that occurred at or after the given timestamp.\n// If this is specified, ResumeAfter and StartAfter must not be set.\nfunc (cso *ChangeStreamOptionsBuilder) SetStartAtOperationTime(t *bson.Timestamp) *ChangeStreamOptionsBuilder {\n\tcso.Opts = append(cso.Opts, func(opts *ChangeStreamOptions) error {\n\t\topts.StartAtOperationTime = t\n\t\treturn nil\n\t})\n\treturn cso\n}\n\n// SetStartAfter sets the value for the StartAfter field. Sets a document specifying the logical starting\n// point for the change stream. This is similar to the ResumeAfter option, but allows a resume token from\n// an \"invalidate\" notification to be used. This allows a change stream on a collection to be resumed after\n// the collection has been dropped and recreated or renamed. Only changes corresponding to an oplog entry\n// immediately after the specified token will be returned. If this is specified, ResumeAfter and\n// StartAtOperationTime must not be set. This option is only valid for MongoDB versions >= 4.1.1.\nfunc (cso *ChangeStreamOptionsBuilder) SetStartAfter(sa any) *ChangeStreamOptionsBuilder {\n\tcso.Opts = append(cso.Opts, func(opts *ChangeStreamOptions) error {\n\t\topts.StartAfter = sa\n\t\treturn nil\n\t})\n\treturn cso\n}\n\n// SetCustom sets the value for the Custom field. Key-value pairs of the BSON map should correlate\n// with desired option names and values. Values must be Marshalable. Custom options may conflict\n// with non-custom options, and custom options bypass client-side validation. Prefer using non-custom\n// options where possible.\nfunc (cso *ChangeStreamOptionsBuilder) SetCustom(c bson.M) *ChangeStreamOptionsBuilder {\n\tcso.Opts = append(cso.Opts, func(opts *ChangeStreamOptions) error {\n\t\topts.Custom = c\n\t\treturn nil\n\t})\n\treturn cso\n}\n\n// SetCustomPipeline sets the value for the CustomPipeline field. Key-value pairs of the BSON map\n// should correlate with desired option names and values. Values must be Marshalable. Custom pipeline\n// options bypass client-side validation. Prefer using non-custom options where possible.\nfunc (cso *ChangeStreamOptionsBuilder) SetCustomPipeline(cp bson.M) *ChangeStreamOptionsBuilder {\n\tcso.Opts = append(cso.Opts, func(opts *ChangeStreamOptions) error {\n\t\topts.CustomPipeline = cp\n\t\treturn nil\n\t})\n\treturn cso\n}\n"
  },
  {
    "path": "mongo/options/clientbulkwriteoptions.go",
    "content": "// Copyright (C) MongoDB, Inc. 2024-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage options\n\nimport (\n\t\"go.mongodb.org/mongo-driver/v2/internal/optionsutil\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/writeconcern\"\n)\n\n// ClientBulkWriteOptions represents options that can be used to configure a client-level BulkWrite operation.\n//\n// See corresponding setter methods for documentation.\ntype ClientBulkWriteOptions struct {\n\tBypassDocumentValidation *bool\n\tComment                  any\n\tOrdered                  *bool\n\tLet                      any\n\tWriteConcern             *writeconcern.WriteConcern\n\tVerboseResults           *bool\n\n\t// Deprecated: This option is for internal use only and should not be set. It may be changed or removed in any\n\t// release.\n\tInternal optionsutil.Options\n}\n\n// ClientBulkWriteOptionsBuilder contains options to configure client-level bulk\n// write operations.\n// Each option can be set through setter functions. See documentation for each\n// setter function for an explanation of the option.\ntype ClientBulkWriteOptionsBuilder struct {\n\tOpts []func(*ClientBulkWriteOptions) error\n}\n\n// ClientBulkWrite creates a new *ClientBulkWriteOptions instance.\nfunc ClientBulkWrite() *ClientBulkWriteOptionsBuilder {\n\topts := &ClientBulkWriteOptionsBuilder{}\n\topts = opts.SetOrdered(DefaultOrdered)\n\n\treturn opts\n}\n\n// List returns a list of ClientBulkWriteOptions setter functions.\nfunc (b *ClientBulkWriteOptionsBuilder) List() []func(*ClientBulkWriteOptions) error {\n\treturn b.Opts\n}\n\n// SetComment sets the value for the Comment field. Specifies a string or document that will be included in\n// server logs, profiling logs, and currentOp queries to help tracethe operation.  The default value is nil,\n// which means that no comment will be included in the logs.\nfunc (b *ClientBulkWriteOptionsBuilder) SetComment(comment any) *ClientBulkWriteOptionsBuilder {\n\tb.Opts = append(b.Opts, func(opts *ClientBulkWriteOptions) error {\n\t\topts.Comment = comment\n\n\t\treturn nil\n\t})\n\n\treturn b\n}\n\n// SetOrdered sets the value for the Ordered field. If true, no writes will be executed after one fails.\n// The default value is true.\nfunc (b *ClientBulkWriteOptionsBuilder) SetOrdered(ordered bool) *ClientBulkWriteOptionsBuilder {\n\tb.Opts = append(b.Opts, func(opts *ClientBulkWriteOptions) error {\n\t\topts.Ordered = &ordered\n\n\t\treturn nil\n\t})\n\n\treturn b\n}\n\n// SetBypassDocumentValidation sets the value for the BypassDocumentValidation field. If true, writes\n// executed as part of the operation will opt out of document-level validation on the server. The default\n// value is false. See https://www.mongodb.com/docs/manual/core/schema-validation/ for more information\n// about document validation.\nfunc (b *ClientBulkWriteOptionsBuilder) SetBypassDocumentValidation(bypass bool) *ClientBulkWriteOptionsBuilder {\n\tb.Opts = append(b.Opts, func(opts *ClientBulkWriteOptions) error {\n\t\topts.BypassDocumentValidation = &bypass\n\n\t\treturn nil\n\t})\n\n\treturn b\n}\n\n// SetLet sets the value for the Let field. Let specifies parameters for all update and delete commands in the BulkWrite.\n// This must be a document mapping parameter names to values. Values must be constant or closed expressions that do not\n// reference document fields. Parameters can then be accessed as variables in an aggregate expression context (e.g. \"$$var\").\nfunc (b *ClientBulkWriteOptionsBuilder) SetLet(let any) *ClientBulkWriteOptionsBuilder {\n\tb.Opts = append(b.Opts, func(opts *ClientBulkWriteOptions) error {\n\t\topts.Let = &let\n\n\t\treturn nil\n\t})\n\n\treturn b\n}\n\n// SetWriteConcern sets the value for the WriteConcern field. Specifies the write concern for\n// operations in the transaction. The default value is nil, which means that the default\n// write concern of the session used to start the transaction will be used.\nfunc (b *ClientBulkWriteOptionsBuilder) SetWriteConcern(wc *writeconcern.WriteConcern) *ClientBulkWriteOptionsBuilder {\n\tb.Opts = append(b.Opts, func(opts *ClientBulkWriteOptions) error {\n\t\topts.WriteConcern = wc\n\n\t\treturn nil\n\t})\n\n\treturn b\n}\n\n// SetVerboseResults sets the value for the VerboseResults field. Specifies whether detailed\n// results for each successful operation should be included in the returned BulkWriteResult.\n// The defaults value is false.\nfunc (b *ClientBulkWriteOptionsBuilder) SetVerboseResults(verboseResults bool) *ClientBulkWriteOptionsBuilder {\n\tb.Opts = append(b.Opts, func(opts *ClientBulkWriteOptions) error {\n\t\topts.VerboseResults = &verboseResults\n\n\t\treturn nil\n\t})\n\n\treturn b\n}\n"
  },
  {
    "path": "mongo/options/clientencryptionoptions.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage options\n\nimport (\n\t\"crypto/tls\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/httputil\"\n)\n\n// ClientEncryptionOptions represents all possible arguments used to configure a ClientEncryption instance.\n//\n// See corresponding setter methods for documentation.\ntype ClientEncryptionOptions struct {\n\tKeyVaultNamespace string\n\tKmsProviders      map[string]map[string]any\n\tTLSConfig         map[string]*tls.Config\n\tHTTPClient        *http.Client\n\tKeyExpiration     *time.Duration\n}\n\n// ClientEncryptionOptionsBuilder contains options to configure client\n// encryption operations. Each option can be set through setter functions. See\n// documentation for each setter function for an explanation of the option.\ntype ClientEncryptionOptionsBuilder struct {\n\tOpts []func(*ClientEncryptionOptions) error\n}\n\n// ClientEncryption creates a new ClientEncryptionOptions instance.\nfunc ClientEncryption() *ClientEncryptionOptionsBuilder {\n\treturn &ClientEncryptionOptionsBuilder{\n\t\tOpts: []func(*ClientEncryptionOptions) error{\n\t\t\tfunc(arg *ClientEncryptionOptions) error {\n\t\t\t\targ.HTTPClient = httputil.DefaultHTTPClient\n\t\t\t\treturn nil\n\t\t\t},\n\t\t},\n\t}\n}\n\n// List returns a list of ClientEncryptionOptions setter functions.\nfunc (c *ClientEncryptionOptionsBuilder) List() []func(*ClientEncryptionOptions) error {\n\treturn c.Opts\n}\n\n// SetKeyVaultNamespace specifies the namespace of the key vault collection. This is required.\nfunc (c *ClientEncryptionOptionsBuilder) SetKeyVaultNamespace(ns string) *ClientEncryptionOptionsBuilder {\n\tc.Opts = append(c.Opts, func(opts *ClientEncryptionOptions) error {\n\t\topts.KeyVaultNamespace = ns\n\t\treturn nil\n\t})\n\treturn c\n}\n\n// SetKmsProviders specifies options for KMS providers. This is required.\nfunc (c *ClientEncryptionOptionsBuilder) SetKmsProviders(providers map[string]map[string]any) *ClientEncryptionOptionsBuilder {\n\tc.Opts = append(c.Opts, func(opts *ClientEncryptionOptions) error {\n\t\topts.KmsProviders = providers\n\t\treturn nil\n\t})\n\treturn c\n}\n\n// SetTLSConfig specifies tls.Config instances for each KMS provider to use to configure TLS on all connections created\n// to the KMS provider.\n//\n// This should only be used to set custom TLS configurations. By default, the connection will use an empty tls.Config{} with MinVersion set to tls.VersionTLS12.\nfunc (c *ClientEncryptionOptionsBuilder) SetTLSConfig(cfg map[string]*tls.Config) *ClientEncryptionOptionsBuilder {\n\tc.Opts = append(c.Opts, func(opts *ClientEncryptionOptions) error {\n\t\topts.TLSConfig = cfg\n\n\t\treturn nil\n\t})\n\n\treturn c\n}\n\n// SetKeyExpiration specifies duration for the key expiration. 0 or negative value means \"never expire\".\n// The granularity is in milliseconds. Any sub-millisecond fraction will be rounded up.\nfunc (c *ClientEncryptionOptionsBuilder) SetKeyExpiration(expiration time.Duration) *ClientEncryptionOptionsBuilder {\n\tc.Opts = append(c.Opts, func(opts *ClientEncryptionOptions) error {\n\t\topts.KeyExpiration = &expiration\n\n\t\treturn nil\n\t})\n\n\treturn c\n}\n\n// BuildTLSConfig specifies tls.Config options for each KMS provider to use to configure TLS on all connections created\n// to the KMS provider. The input map should contain a mapping from each KMS provider to a document containing the necessary\n// options, as follows:\n//\n//\t{\n//\t\t\t\"kmip\": {\n//\t\t\t\t\"tlsCertificateKeyFile\": \"foo.pem\",\n//\t\t\t\t\"tlsCAFile\": \"fooCA.pem\"\n//\t\t\t}\n//\t}\n//\n// Currently, the following TLS options are supported:\n//\n// 1. \"tlsCertificateKeyFile\" (or \"sslClientCertificateKeyFile\"): The \"tlsCertificateKeyFile\" option specifies a path to\n// the client certificate and private key, which must be concatenated into one file.\n//\n// 2. \"tlsCertificateKeyFilePassword\" (or \"sslClientCertificateKeyPassword\"): Specify the password to decrypt the client\n// private key file (e.g. \"tlsCertificateKeyFilePassword=password\").\n//\n// 3. \"tlsCaFile\" (or \"sslCertificateAuthorityFile\"): Specify the path to a single or bundle of certificate authorities\n// to be considered trusted when making a TLS connection (e.g. \"tlsCaFile=/path/to/caFile\").\n//\n// This should only be used to set custom TLS options. By default, the connection will use an empty tls.Config{} with MinVersion set to tls.VersionTLS12.\nfunc BuildTLSConfig(tlsOpts map[string]any) (*tls.Config, error) {\n\t// use TLS min version 1.2 to enforce more secure hash algorithms and advanced cipher suites\n\tcfg := &tls.Config{MinVersion: tls.VersionTLS12}\n\n\tfor name := range tlsOpts {\n\t\tvar err error\n\t\tswitch name {\n\t\tcase \"tlsCertificateKeyFile\", \"sslClientCertificateKeyFile\":\n\t\t\tclientCertPath, ok := tlsOpts[name].(string)\n\t\t\tif !ok {\n\t\t\t\treturn nil, fmt.Errorf(\"expected %q value to be of type string, got %T\", name, tlsOpts[name])\n\t\t\t}\n\t\t\t// apply custom key file password if found, otherwise use empty string\n\t\t\tif keyPwd, found := tlsOpts[\"tlsCertificateKeyFilePassword\"].(string); found {\n\t\t\t\t_, err = addClientCertFromConcatenatedFile(cfg, clientCertPath, keyPwd)\n\t\t\t} else if keyPwd, found := tlsOpts[\"sslClientCertificateKeyPassword\"].(string); found {\n\t\t\t\t_, err = addClientCertFromConcatenatedFile(cfg, clientCertPath, keyPwd)\n\t\t\t} else {\n\t\t\t\t_, err = addClientCertFromConcatenatedFile(cfg, clientCertPath, \"\")\n\t\t\t}\n\t\tcase \"tlsCertificateKeyFilePassword\", \"sslClientCertificateKeyPassword\":\n\t\t\tcontinue\n\t\tcase \"tlsCAFile\", \"sslCertificateAuthorityFile\":\n\t\t\tcaPath, ok := tlsOpts[name].(string)\n\t\t\tif !ok {\n\t\t\t\treturn nil, fmt.Errorf(\"expected %q value to be of type string, got %T\", name, tlsOpts[name])\n\t\t\t}\n\t\t\terr = addCACertFromFile(cfg, caPath)\n\t\tdefault:\n\t\t\treturn nil, fmt.Errorf(\"unrecognized TLS option %v\", name)\n\t\t}\n\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\treturn cfg, nil\n}\n"
  },
  {
    "path": "mongo/options/clientoptions.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage options\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"crypto/tls\"\n\t\"crypto/x509\"\n\t\"encoding/pem\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io/ioutil\"\n\t\"math\"\n\t\"net\"\n\t\"net/http\"\n\t\"reflect\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/youmark/pkcs8\"\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/event\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/httputil\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/optionsutil\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/readconcern\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/readpref\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/writeconcern\"\n\t\"go.mongodb.org/mongo-driver/v2/tag\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/auth\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/connstring\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/wiremessage\"\n)\n\nconst (\n\t// ServerMonitoringModeAuto indicates that the client will behave like \"poll\"\n\t// mode when running on a FaaS (Function as a Service) platform, or like\n\t// \"stream\" mode otherwise. The client detects its execution environment by\n\t// following the rules for generating the \"client.env\" handshake metadata field\n\t// as specified in the MongoDB Handshake specification. This is the default\n\t// mode.\n\tServerMonitoringModeAuto = connstring.ServerMonitoringModeAuto\n\n\t// ServerMonitoringModePoll indicates that the client will periodically check\n\t// the server using a hello or legacy hello command and then sleep for\n\t// heartbeatFrequencyMS milliseconds before running another check.\n\tServerMonitoringModePoll = connstring.ServerMonitoringModePoll\n\n\t// ServerMonitoringModeStream indicates that the client will use a streaming\n\t// protocol when the server supports it. The streaming protocol optimally\n\t// reduces the time it takes for a client to discover server state changes.\n\tServerMonitoringModeStream = connstring.ServerMonitoringModeStream\n)\n\n// ContextDialer is an interface that can be implemented by types that can create connections. It should be used to\n// provide a custom dialer when configuring a Client.\n//\n// DialContext should return a connection to the provided address on the given network.\ntype ContextDialer interface {\n\tDialContext(ctx context.Context, network, address string) (net.Conn, error)\n}\n\n// Credential can be used to provide authentication options when configuring a Client.\n//\n// AuthMechanism: the mechanism to use for authentication. Supported values include \"SCRAM-SHA-256\", \"SCRAM-SHA-1\",\n// \"PLAIN\", \"GSSAPI\", \"MONGODB-X509\", and \"MONGODB-AWS\". This can also be set through the \"authMechanism\"\n// URI option. (e.g. \"authMechanism=PLAIN\"). For more information, see\n// https://www.mongodb.com/docs/manual/core/authentication-mechanisms/.\n//\n// AuthMechanismProperties can be used to specify additional configuration options for certain mechanisms. They can also\n// be set through the \"authMechanismProperites\" URI option\n// (e.g. \"authMechanismProperties=SERVICE_NAME:service,CANONICALIZE_HOST_NAME:true\"). Supported properties are:\n//\n// 1. SERVICE_NAME: The service name to use for GSSAPI authentication. The default is \"mongodb\".\n//\n// 2. CANONICALIZE_HOST_NAME: If \"true\", the driver will canonicalize the host name for GSSAPI authentication. The default\n// is \"false\".\n//\n// 3. SERVICE_REALM: The service realm for GSSAPI authentication.\n//\n// 4. SERVICE_HOST: The host name to use for GSSAPI authentication. This should be specified if the host name to use for\n// authentication is different than the one given for Client construction.\n//\n// 4. AWS_SESSION_TOKEN: The AWS token for MONGODB-AWS authentication. This is optional and used for authentication with\n// temporary credentials.\n//\n// The SERVICE_HOST and CANONICALIZE_HOST_NAME properties must not be used at the same time on Linux and Darwin\n// systems.\n//\n// AuthSource: the name of the database to use for authentication. This defaults to \"$external\" for MONGODB-AWS,\n// MONGODB-OIDC, MONGODB-X509, GSSAPI, and PLAIN. It defaults to  \"admin\" for all other auth mechanisms. This can\n// also be set through the \"authSource\" URI option (e.g. \"authSource=otherDb\").\n//\n// Username: the username for authentication. This can also be set through the URI as a username:password pair before\n// the first @ character. For example, a URI for user \"user\", password \"pwd\", and host \"localhost:27017\" would be\n// \"mongodb://user:pwd@localhost:27017\". This is optional for X509 authentication and will be extracted from the\n// client certificate if not specified.\n//\n// Password: the password for authentication. This must not be specified for X509 and is optional for GSSAPI\n// authentication.\n//\n// PasswordSet: For GSSAPI, this must be true if a password is specified, even if the password is the empty string, and\n// false if no password is specified, indicating that the password should be taken from the context of the running\n// process. For other mechanisms, this field is ignored.\ntype Credential struct {\n\tAuthMechanism           string\n\tAuthMechanismProperties map[string]string\n\tAuthSource              string\n\tUsername                string\n\tPassword                string\n\tPasswordSet             bool\n\tOIDCMachineCallback     OIDCCallback\n\tOIDCHumanCallback       OIDCCallback\n}\n\n// OIDCCallback is the type for both Human and Machine Callback flows.\n// RefreshToken will always be nil in the OIDCArgs for the Machine flow.\ntype OIDCCallback func(context.Context, *OIDCArgs) (*OIDCCredential, error)\n\n// OIDCArgs contains the arguments for the OIDC callback.\ntype OIDCArgs struct {\n\tVersion      int\n\tIDPInfo      *IDPInfo\n\tRefreshToken *string\n}\n\n// OIDCCredential contains the access token and refresh token.\ntype OIDCCredential struct {\n\tAccessToken  string\n\tExpiresAt    *time.Time\n\tRefreshToken *string\n}\n\n// IDPInfo contains the information needed to perform OIDC authentication with\n// an Identity Provider.\ntype IDPInfo struct {\n\tIssuer        string\n\tClientID      string\n\tRequestScopes []string\n}\n\n// BSONOptions are optional BSON marshaling and unmarshaling behaviors.\ntype BSONOptions struct {\n\t// UseJSONStructTags causes the driver to fall back to using the \"json\"\n\t// struct tag if a \"bson\" struct tag is not specified.\n\tUseJSONStructTags bool\n\n\t// ErrorOnInlineDuplicates causes the driver to return an error if there is\n\t// a duplicate field in the marshaled BSON when the \"inline\" struct tag\n\t// option is set.\n\tErrorOnInlineDuplicates bool\n\n\t// IntMinSize causes the driver to marshal Go integer values (int, int8,\n\t// int16, int32, int64, uint, uint8, uint16, uint32, or uint64) as the\n\t// minimum BSON int size (either 32 or 64 bits) that can represent the\n\t// integer value.\n\tIntMinSize bool\n\n\t// NilMapAsEmpty causes the driver to marshal nil Go maps as empty BSON\n\t// documents instead of BSON null.\n\t//\n\t// Empty BSON documents take up slightly more space than BSON null, but\n\t// preserve the ability to use document update operations like \"$set\" that\n\t// do not work on BSON null.\n\tNilMapAsEmpty bool\n\n\t// NilSliceAsEmpty causes the driver to marshal nil Go slices as empty BSON\n\t// arrays instead of BSON null.\n\t//\n\t// Empty BSON arrays take up slightly more space than BSON null, but\n\t// preserve the ability to use array update operations like \"$push\" or\n\t// \"$addToSet\" that do not work on BSON null.\n\tNilSliceAsEmpty bool\n\n\t// NilByteSliceAsEmpty causes the driver to marshal nil Go byte slices as\n\t// empty BSON binary values instead of BSON null.\n\tNilByteSliceAsEmpty bool\n\n\t// OmitZeroStruct causes the driver to consider the zero value for a struct\n\t// (e.g. MyStruct{}) as empty and omit it from the marshaled BSON when the\n\t// \"omitempty\" struct tag option or the \"OmitEmpty\" field is set.\n\tOmitZeroStruct bool\n\n\t// OmitEmpty causes the driver to omit empty values from the marshaled BSON.\n\tOmitEmpty bool\n\n\t// StringifyMapKeysWithFmt causes the driver to convert Go map keys to BSON\n\t// document field name strings using fmt.Sprint instead of the default\n\t// string conversion logic.\n\tStringifyMapKeysWithFmt bool\n\n\t// AllowTruncatingDoubles causes the driver to truncate the fractional part\n\t// of BSON \"double\" values when attempting to unmarshal them into a Go\n\t// integer (int, int8, int16, int32, or int64) struct field. The truncation\n\t// logic does not apply to BSON \"decimal128\" values.\n\tAllowTruncatingDoubles bool\n\n\t// BinaryAsSlice causes the driver to unmarshal BSON binary field values\n\t// that are the \"Generic\" or \"Old\" BSON binary subtype as a Go byte slice\n\t// instead of a bson.Binary.\n\tBinaryAsSlice bool\n\n\t// DefaultDocumentM causes the driver to always unmarshal documents into the\n\t// bson.M type. This behavior is restricted to data typed as\n\t// \"any\" or \"map[string]any\".\n\tDefaultDocumentM bool\n\n\t// DefaultDocumentMap causes the driver to always unmarshal documents into the\n\t// map[string]any type. This behavior is restricted to data typed as \"any\" or\n\t// \"map[string]any\".\n\tDefaultDocumentMap bool\n\n\t// ObjectIDAsHexString causes the Decoder to decode object IDs to their hex\n\t// representation.\n\tObjectIDAsHexString bool\n\n\t// UseLocalTimeZone causes the driver to unmarshal time.Time values in the\n\t// local timezone instead of the UTC timezone.\n\tUseLocalTimeZone bool\n\n\t// ZeroMaps causes the driver to delete any existing values from Go maps in\n\t// the destination value before unmarshaling BSON documents into them.\n\tZeroMaps bool\n\n\t// ZeroStructs causes the driver to delete any existing values from Go\n\t// structs in the destination value before unmarshaling BSON documents into\n\t// them.\n\tZeroStructs bool\n}\n\n// DriverInfo appends the client metadata generated by the driver when\n// handshaking the server. These options do not replace the values used\n// during the handshake, rather they are deliminated with a | with the\n// driver-generated data. This should be used by libraries wrapping the driver,\n// e.g. ODMs.\ntype DriverInfo struct {\n\tName     string // Name of the library wrapping the driver.\n\tVersion  string // Version of the library wrapping the driver.\n\tPlatform string // Platform information for the wrapping driver.\n}\n\n// ClientOptions contains arguments to configure a Client instance. Arguments\n// can be set through the ClientOptions setter functions. See each function for\n// documentation.\ntype ClientOptions struct {\n\tAppName                  *string\n\tAuth                     *Credential\n\tAutoEncryptionOptions    *AutoEncryptionOptions\n\tConnectTimeout           *time.Duration\n\tCompressors              []string\n\tDialer                   ContextDialer\n\tDirect                   *bool\n\tDisableOCSPEndpointCheck *bool\n\tDriverInfo               *DriverInfo\n\tHeartbeatInterval        *time.Duration\n\tHosts                    []string\n\tHTTPClient               *http.Client\n\tLoadBalanced             *bool\n\tLocalThreshold           *time.Duration\n\tLoggerOptions            *LoggerOptions\n\tMaxConnIdleTime          *time.Duration\n\tMaxPoolSize              *uint64\n\tMinPoolSize              *uint64\n\tMaxConnecting            *uint64\n\tPoolMonitor              *event.PoolMonitor\n\tMonitor                  *event.CommandMonitor\n\tServerMonitor            *event.ServerMonitor\n\tReadConcern              *readconcern.ReadConcern\n\tReadPreference           *readpref.ReadPref\n\tBSONOptions              *BSONOptions\n\tRegistry                 *bson.Registry\n\tReplicaSet               *string\n\tRetryReads               *bool\n\tRetryWrites              *bool\n\tServerAPIOptions         *ServerAPIOptions\n\tServerMonitoringMode     *string\n\tServerSelectionTimeout   *time.Duration\n\tSRVMaxHosts              *int\n\tSRVServiceName           *string\n\tTimeout                  *time.Duration\n\tTLSConfig                *tls.Config\n\tWriteConcern             *writeconcern.WriteConcern\n\tZlibLevel                *int\n\tZstdLevel                *int\n\n\t// Crypt specifies a custom driver.Crypt to be used to encrypt and decrypt documents. The default is no\n\t// encryption.\n\t//\n\t// Deprecated: This option is for internal use only and should not be set (see GODRIVER-2149). It may be\n\t// changed in any release. This option will be removed in 3.0 and replaced with the Custom options.Options\n\t// pattern: SetInternalClientOptions(clientOptions, \"crypt\", myCrypt)\n\tCrypt driver.Crypt\n\n\t// Deployment specifies a custom deployment to use for the new Client.\n\t//\n\t// Deprecated: This option is for internal use only and should not be set. It may be changed in any release.\n\t// This option will be removed in 3.0 and replaced with the Custom options.Options pattern:\n\t// SetInternalClientOptions(clientOptions, \"deployment\", myDeployment)\n\tDeployment driver.Deployment\n\n\t// Custom specifies internal options for the new Client.\n\t//\n\t// Deprecated: This option is for internal use only and should not be set. It may be changed or removed in any\n\t// release.\n\tCustom optionsutil.Options\n\n\tconnString *connstring.ConnString\n\terr        error\n}\n\n// Client creates a new ClientOptions instance.\nfunc Client() *ClientOptions {\n\topts := &ClientOptions{}\n\topts = opts.SetHTTPClient(httputil.NewHTTPClient())\n\n\treturn opts\n}\n\nfunc setURIOpts(uri string, opts *ClientOptions) error {\n\tconnString, err := connstring.ParseAndValidate(uri)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\topts.connString = connString\n\n\tif connString.AppName != \"\" {\n\t\topts.AppName = &connString.AppName\n\t}\n\n\t// Only create a Credential if there is a request for authentication via\n\t// non-empty credentials in the URI.\n\tif connString.HasAuthParameters() {\n\t\topts.Auth = &Credential{\n\t\t\tAuthMechanism:           connString.AuthMechanism,\n\t\t\tAuthMechanismProperties: connString.AuthMechanismProperties,\n\t\t\tAuthSource:              connString.AuthSource,\n\t\t\tUsername:                connString.Username,\n\t\t\tPassword:                connString.Password,\n\t\t\tPasswordSet:             connString.PasswordSet,\n\t\t}\n\t}\n\n\tif connString.ConnectSet {\n\t\tdirect := connString.Connect == connstring.SingleConnect\n\t\topts.Direct = &direct\n\t}\n\n\tif connString.DirectConnectionSet {\n\t\topts.Direct = &connString.DirectConnection\n\t}\n\n\tif connString.ConnectTimeoutSet {\n\t\topts.ConnectTimeout = &connString.ConnectTimeout\n\t}\n\n\tif len(connString.Compressors) > 0 {\n\t\topts.Compressors = connString.Compressors\n\t\tif stringSliceContains(opts.Compressors, \"zlib\") {\n\t\t\tdefaultLevel := wiremessage.DefaultZlibLevel\n\t\t\topts.ZlibLevel = &defaultLevel\n\t\t}\n\t\tif stringSliceContains(opts.Compressors, \"zstd\") {\n\t\t\tdefaultLevel := wiremessage.DefaultZstdLevel\n\t\t\topts.ZstdLevel = &defaultLevel\n\t\t}\n\t}\n\n\tif connString.HeartbeatIntervalSet {\n\t\topts.HeartbeatInterval = &connString.HeartbeatInterval\n\t}\n\n\topts.Hosts = connString.Hosts\n\n\tif connString.LoadBalancedSet {\n\t\topts.LoadBalanced = &connString.LoadBalanced\n\t}\n\n\tif connString.LocalThresholdSet {\n\t\topts.LocalThreshold = &connString.LocalThreshold\n\t}\n\n\tif connString.MaxConnIdleTimeSet {\n\t\topts.MaxConnIdleTime = &connString.MaxConnIdleTime\n\t}\n\n\tif connString.MaxPoolSizeSet {\n\t\topts.MaxPoolSize = &connString.MaxPoolSize\n\t}\n\n\tif connString.MinPoolSizeSet {\n\t\topts.MinPoolSize = &connString.MinPoolSize\n\t}\n\n\tif connString.MaxConnectingSet {\n\t\topts.MaxConnecting = &connString.MaxConnecting\n\t}\n\n\tif connString.ReadConcernLevel != \"\" {\n\t\topts.ReadConcern = &readconcern.ReadConcern{Level: connString.ReadConcernLevel}\n\t}\n\n\tif connString.ReadPreference != \"\" || len(connString.ReadPreferenceTagSets) > 0 || connString.MaxStalenessSet {\n\t\treadPrefOpts := make([]readpref.Option, 0, 1)\n\n\t\ttagSets := tag.NewTagSetsFromMaps(connString.ReadPreferenceTagSets)\n\t\tif len(tagSets) > 0 {\n\t\t\treadPrefOpts = append(readPrefOpts, readpref.WithTagSets(tagSets...))\n\t\t}\n\n\t\tif connString.MaxStaleness != 0 {\n\t\t\treadPrefOpts = append(readPrefOpts, readpref.WithMaxStaleness(connString.MaxStaleness))\n\t\t}\n\n\t\tmode, err := readpref.ModeFromString(connString.ReadPreference)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\topts.ReadPreference, err = readpref.New(mode, readPrefOpts...)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tif connString.RetryWritesSet {\n\t\topts.RetryWrites = &connString.RetryWrites\n\t}\n\n\tif connString.RetryReadsSet {\n\t\topts.RetryReads = &connString.RetryReads\n\t}\n\n\tif connString.ReplicaSet != \"\" {\n\t\topts.ReplicaSet = &connString.ReplicaSet\n\t}\n\n\tif connString.ServerSelectionTimeoutSet {\n\t\topts.ServerSelectionTimeout = &connString.ServerSelectionTimeout\n\t}\n\n\tif connString.SRVMaxHosts != 0 {\n\t\topts.SRVMaxHosts = &connString.SRVMaxHosts\n\t}\n\n\tif connString.SRVServiceName != \"\" {\n\t\topts.SRVServiceName = &connString.SRVServiceName\n\t}\n\n\tif connString.SSL {\n\t\ttlsConfig := new(tls.Config)\n\n\t\tif connString.SSLCaFileSet {\n\t\t\tif err := addCACertFromFile(tlsConfig, connString.SSLCaFile); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\n\t\tif connString.SSLInsecure {\n\t\t\ttlsConfig.InsecureSkipVerify = true\n\t\t}\n\n\t\tvar x509Subject string\n\t\tvar keyPasswd string\n\t\tif connString.SSLClientCertificateKeyPasswordSet && connString.SSLClientCertificateKeyPassword != nil {\n\t\t\tkeyPasswd = connString.SSLClientCertificateKeyPassword()\n\t\t}\n\n\t\tvar err error\n\t\tif connString.SSLClientCertificateKeyFileSet {\n\t\t\tx509Subject, err = addClientCertFromConcatenatedFile(tlsConfig, connString.SSLClientCertificateKeyFile, keyPasswd)\n\t\t} else if connString.SSLCertificateFileSet || connString.SSLPrivateKeyFileSet {\n\t\t\tx509Subject, err = addClientCertFromSeparateFiles(tlsConfig, connString.SSLCertificateFile,\n\t\t\t\tconnString.SSLPrivateKeyFile, keyPasswd)\n\t\t}\n\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\t// If a username wasn't specified fork x509, add one from the certificate.\n\t\tif opts.Auth != nil && strings.ToLower(opts.Auth.AuthMechanism) == \"mongodb-x509\" && opts.Auth.Username == \"\" {\n\t\t\t// The Go x509 package gives the subject with the pairs in reverse order that we want.\n\t\t\topts.Auth.Username = extractX509UsernameFromSubject(x509Subject)\n\t\t}\n\n\t\topts.TLSConfig = tlsConfig\n\t}\n\n\tif connString.JSet || connString.WString != \"\" || connString.WNumberSet {\n\t\topts.WriteConcern = &writeconcern.WriteConcern{}\n\n\t\tif len(connString.WString) > 0 {\n\t\t\topts.WriteConcern.W = connString.WString\n\t\t} else if connString.WNumberSet {\n\t\t\topts.WriteConcern.W = connString.WNumber\n\t\t}\n\n\t\tif connString.JSet {\n\t\t\topts.WriteConcern.Journal = &connString.J\n\t\t}\n\t}\n\n\tif connString.ZlibLevelSet {\n\t\topts.ZlibLevel = &connString.ZlibLevel\n\t}\n\tif connString.ZstdLevelSet {\n\t\topts.ZstdLevel = &connString.ZstdLevel\n\t}\n\n\tif connString.SSLDisableOCSPEndpointCheckSet {\n\t\topts.DisableOCSPEndpointCheck = &connString.SSLDisableOCSPEndpointCheck\n\t}\n\n\tif connString.TimeoutSet {\n\t\topts.Timeout = &connString.Timeout\n\t}\n\n\treturn nil\n}\n\n// GetURI returns the original URI used to configure the ClientOptions instance.\n// If ApplyURI was not called during construction, this returns \"\".\nfunc (c *ClientOptions) GetURI() string {\n\tif c != nil && c.connString != nil {\n\t\treturn c.connString.Original\n\t}\n\n\treturn \"\"\n}\n\n// Validate validates the client options. This method will return the first\n// error found.\nfunc (c *ClientOptions) Validate() error {\n\tif c.err != nil {\n\t\treturn c.err\n\t}\n\n\t// Direct connections cannot be made if multiple hosts are specified or an SRV\n\t// URI is used.\n\tif c.Direct != nil && *c.Direct {\n\t\tif len(c.Hosts) > 1 {\n\t\t\treturn errors.New(\"a direct connection cannot be made if multiple hosts are specified\")\n\t\t}\n\t\tif c.connString != nil && c.connString.Scheme == connstring.SchemeMongoDBSRV {\n\t\t\treturn errors.New(\"a direct connection cannot be made if an SRV URI is used\")\n\t\t}\n\t}\n\n\tif c.HeartbeatInterval != nil && *c.HeartbeatInterval < (500*time.Millisecond) {\n\t\treturn fmt.Errorf(\"heartbeatFrequencyMS must exceed the minimum heartbeat interval of 500ms, got heartbeatFrequencyMS=%q\",\n\t\t\t*c.HeartbeatInterval)\n\t}\n\n\tif c.MaxPoolSize != nil && c.MinPoolSize != nil && *c.MaxPoolSize != 0 &&\n\t\t*c.MinPoolSize > *c.MaxPoolSize {\n\t\treturn fmt.Errorf(\"minPoolSize must be less than or equal to maxPoolSize, got minPoolSize=%d maxPoolSize=%d\",\n\t\t\t*c.MinPoolSize, *c.MaxPoolSize)\n\t}\n\n\t// verify server API version if ServerAPIOptions are passed in.\n\tif c.ServerAPIOptions != nil {\n\t\tif err := c.ServerAPIOptions.ServerAPIVersion.Validate(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\t// Validation for load-balanced mode.\n\tif c.LoadBalanced != nil && *c.LoadBalanced {\n\t\tif len(c.Hosts) > 1 {\n\t\t\treturn connstring.ErrLoadBalancedWithMultipleHosts\n\t\t}\n\t\tif c.ReplicaSet != nil {\n\t\t\treturn connstring.ErrLoadBalancedWithReplicaSet\n\t\t}\n\t\tif c.Direct != nil && *c.Direct {\n\t\t\treturn connstring.ErrLoadBalancedWithDirectConnection\n\t\t}\n\t}\n\n\t// Validation for srvMaxHosts.\n\tif c.SRVMaxHosts != nil && *c.SRVMaxHosts > 0 {\n\t\tif c.ReplicaSet != nil {\n\t\t\treturn connstring.ErrSRVMaxHostsWithReplicaSet\n\t\t}\n\t\tif c.LoadBalanced != nil && *c.LoadBalanced {\n\t\t\treturn connstring.ErrSRVMaxHostsWithLoadBalanced\n\t\t}\n\t}\n\n\tif mode := c.ServerMonitoringMode; mode != nil && !connstring.IsValidServerMonitoringMode(*mode) {\n\t\treturn fmt.Errorf(\"invalid server monitoring mode: %q\", *mode)\n\t}\n\n\tif to := c.Timeout; to != nil && *to < 0 {\n\t\treturn fmt.Errorf(`invalid value %q for \"Timeout\": value must be positive`, *to)\n\t}\n\n\t// OIDC Validation\n\tif c.Auth != nil && c.Auth.AuthMechanism == auth.MongoDBOIDC {\n\t\tif c.Auth.Password != \"\" {\n\t\t\treturn fmt.Errorf(\"password must not be set for the %s auth mechanism\", auth.MongoDBOIDC)\n\t\t}\n\t\tif c.Auth.OIDCMachineCallback != nil && c.Auth.OIDCHumanCallback != nil {\n\t\t\treturn fmt.Errorf(\"cannot set both OIDCMachineCallback and OIDCHumanCallback, only one may be specified\")\n\t\t}\n\t\tif c.Auth.OIDCHumanCallback == nil && c.Auth.AuthMechanismProperties[auth.AllowedHostsProp] != \"\" {\n\t\t\treturn fmt.Errorf(\"cannot specify ALLOWED_HOSTS without an OIDCHumanCallback\")\n\t\t}\n\t\tif c.Auth.OIDCMachineCallback == nil && c.Auth.OIDCHumanCallback == nil && c.Auth.AuthMechanismProperties[auth.EnvironmentProp] == \"\" {\n\t\t\treturn errors.New(\"must specify at least one of OIDCMachineCallback, OIDCHumanCallback, or ENVIRONMENT authMechanismProperty\")\n\t\t}\n\n\t\t// Return an error if an unsupported authMechanismProperty is specified\n\t\t// for MONGODB-OIDC.\n\t\tfor prop := range c.Auth.AuthMechanismProperties {\n\t\t\tswitch prop {\n\t\t\tcase auth.AllowedHostsProp, auth.EnvironmentProp, auth.ResourceProp:\n\t\t\tdefault:\n\t\t\t\treturn fmt.Errorf(\"auth mechanism property %q is not valid for MONGODB-OIDC\", prop)\n\t\t\t}\n\t\t}\n\n\t\tif env, ok := c.Auth.AuthMechanismProperties[auth.EnvironmentProp]; ok {\n\t\t\tswitch env {\n\t\t\tcase auth.GCPEnvironmentValue, auth.AzureEnvironmentValue:\n\t\t\t\tif c.Auth.AuthMechanismProperties[auth.ResourceProp] == \"\" {\n\t\t\t\t\treturn fmt.Errorf(\"%q must be set for the %s %q\", auth.ResourceProp, env, auth.EnvironmentProp)\n\t\t\t\t}\n\t\t\t\tfallthrough\n\t\t\tcase auth.K8SEnvironmentValue:\n\t\t\t\tif c.Auth.OIDCMachineCallback != nil {\n\t\t\t\t\treturn fmt.Errorf(\"OIDCMachineCallback cannot be specified with the %s %q\", env, auth.EnvironmentProp)\n\t\t\t\t}\n\t\t\t\tif c.Auth.OIDCHumanCallback != nil {\n\t\t\t\t\treturn fmt.Errorf(\"OIDCHumanCallback cannot be specified with the %s %q\", env, auth.EnvironmentProp)\n\t\t\t\t}\n\t\t\tcase auth.TestEnvironmentValue:\n\t\t\t\tif c.Auth.AuthMechanismProperties[auth.ResourceProp] != \"\" {\n\t\t\t\t\treturn fmt.Errorf(\"%q must not be set for the %s %q\", auth.ResourceProp, env, auth.EnvironmentProp)\n\t\t\t\t}\n\t\t\t\tif c.Auth.Username != \"\" {\n\t\t\t\t\treturn fmt.Errorf(\"must not specify username for %s %q\", env, auth.EnvironmentProp)\n\t\t\t\t}\n\t\t\tdefault:\n\t\t\t\treturn fmt.Errorf(\"the %s %q is not supported for MONGODB-OIDC\", env, auth.EnvironmentProp)\n\t\t\t}\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// ApplyURI parses the given URI and sets options accordingly. The URI can contain host names, IPv4/IPv6 literals, or\n// an SRV record that will be resolved when the Client is created. When using an SRV record, TLS support is\n// implicitly enabled. Specify the \"tls=false\" URI option to override this.\n//\n// If the connection string contains any options that have previously been set, it will overwrite them. Options that\n// correspond to multiple URI parameters, such as WriteConcern, will be completely overwritten if any of the query\n// parameters are specified. If an option is set on ClientOptions after this method is called, that option will override\n// any option applied via the connection string.\n//\n// If the URI format is incorrect or there are conflicting options specified in the URI an error will be recorded and\n// can be retrieved by calling Validate.\n//\n// For more information about the URI format, see https://www.mongodb.com/docs/manual/reference/connection-string/. See\n// mongo.Connect documentation for examples of using URIs for different Client configurations.\nfunc (c *ClientOptions) ApplyURI(uri string) *ClientOptions {\n\tif c.err != nil {\n\t\treturn c\n\t}\n\n\tc.err = setURIOpts(uri, c)\n\n\treturn c\n}\n\n// SetAppName specifies an application name that is sent to the server when creating new connections. It is used by the\n// server to log connection and profiling information (e.g. slow query logs). This can also be set through the \"appName\"\n// URI option (e.g \"appName=example_application\"). The default is empty, meaning no app name will be sent.\nfunc (c *ClientOptions) SetAppName(s string) *ClientOptions {\n\tc.AppName = &s\n\n\treturn c\n}\n\n// SetAuth specifies a Credential containing options for configuring authentication. See the options.Credential\n// documentation for more information about Credential fields. The default is an empty Credential, meaning no\n// authentication will be configured.\nfunc (c *ClientOptions) SetAuth(auth Credential) *ClientOptions {\n\tc.Auth = &auth\n\n\treturn c\n}\n\n// SetCompressors sets the compressors that can be used when communicating with a server. Valid values are:\n//\n// 1. \"snappy\"\n//\n// 2. \"zlib\"\n//\n// 3. \"zstd\" - requires server version >= 4.2, and driver version >= 1.2.0 with cgo support enabled or driver\n// version >= 1.3.0 without cgo.\n//\n// If this option is specified, the driver will perform a negotiation with the server to determine a common list of\n// compressors and will use the first one in that list when performing operations. See\n// https://www.mongodb.com/docs/manual/reference/program/mongod/#cmdoption-mongod-networkmessagecompressors for more\n// information about configuring compression on the server and the server-side defaults.\n//\n// This can also be set through the \"compressors\" URI option (e.g. \"compressors=zstd,zlib,snappy\"). The default is\n// an empty slice, meaning no compression will be enabled.\nfunc (c *ClientOptions) SetCompressors(comps []string) *ClientOptions {\n\tc.Compressors = comps\n\n\treturn c\n}\n\n// SetConnectTimeout specifies a timeout that is used for creating connections to the server. This can be set through\n// ApplyURI with the \"connectTimeoutMS\" (e.g \"connectTimeoutMS=30\") option. If set to 0, no timeout will be used. The\n// default is 30 seconds.\nfunc (c *ClientOptions) SetConnectTimeout(d time.Duration) *ClientOptions {\n\tc.ConnectTimeout = &d\n\n\treturn c\n}\n\n// SetDialer specifies a custom ContextDialer to be used to create new connections to the server. This method overrides\n// the default net.Dialer, so dialer options such as Timeout, KeepAlive, Resolver, etc can be set.\n// See https://golang.org/pkg/net/#Dialer for more information about the net.Dialer type.\nfunc (c *ClientOptions) SetDialer(d ContextDialer) *ClientOptions {\n\tc.Dialer = d\n\n\treturn c\n}\n\n// SetDirect specifies whether or not a direct connect should be made. If set to true, the driver will only connect to\n// the host provided in the URI and will not discover other hosts in the cluster. This can also be set through the\n// \"directConnection\" URI option. This option cannot be set to true if multiple hosts are specified, either through\n// ApplyURI or SetHosts, or an SRV URI is used.\n//\n// As of driver version 1.4, the \"connect\" URI option has been deprecated and replaced with \"directConnection\". The\n// \"connect\" URI option has two values:\n//\n// 1. \"connect=direct\" for direct connections. This corresponds to \"directConnection=true\".\n//\n// 2. \"connect=automatic\" for automatic discovery. This corresponds to \"directConnection=false\"\n//\n// If the \"connect\" and \"directConnection\" URI options are both specified in the connection string, their values must\n// not conflict. Direct connections are not valid if multiple hosts are specified or an SRV URI is used. The default\n// value for this option is false.\nfunc (c *ClientOptions) SetDirect(b bool) *ClientOptions {\n\tc.Direct = &b\n\n\treturn c\n}\n\n// SetHeartbeatInterval specifies the amount of time to wait between periodic background server checks. This can also be\n// set through the \"heartbeatFrequencyMS\" URI option (e.g. \"heartbeatFrequencyMS=10000\"). The default is 10 seconds.\n// The minimum is 500ms.\nfunc (c *ClientOptions) SetHeartbeatInterval(d time.Duration) *ClientOptions {\n\tc.HeartbeatInterval = &d\n\n\treturn c\n}\n\n// SetHosts specifies a list of host names or IP addresses for servers in a cluster. Both IPv4 and IPv6 addresses are\n// supported. IPv6 literals must be enclosed in '[]' following RFC-2732 syntax.\n//\n// Hosts can also be specified as a comma-separated list in a URI. For example, to include \"localhost:27017\" and\n// \"localhost:27018\", a URI could be \"mongodb://localhost:27017,localhost:27018\". The default is [\"localhost:27017\"]\nfunc (c *ClientOptions) SetHosts(s []string) *ClientOptions {\n\tc.Hosts = s\n\n\treturn c\n}\n\n// SetLoadBalanced specifies whether or not the MongoDB deployment is hosted behind a load balancer. This can also be\n// set through the \"loadBalanced\" URI option. The driver will error during Client configuration if this option is set\n// to true and one of the following conditions are met:\n//\n// 1. Multiple hosts are specified, either via the ApplyURI or SetHosts methods. This includes the case where an SRV\n// URI is used and the SRV record resolves to multiple hostnames.\n// 2. A replica set name is specified, either via the URI or the SetReplicaSet method.\n// 3. The options specify whether or not a direct connection should be made, either via the URI or the SetDirect method.\n//\n// The default value is false.\nfunc (c *ClientOptions) SetLoadBalanced(lb bool) *ClientOptions {\n\tc.LoadBalanced = &lb\n\n\treturn c\n}\n\n// SetLocalThreshold specifies the width of the 'latency window': when choosing between multiple suitable servers for an\n// operation, this is the acceptable non-negative delta between shortest and longest average round-trip times. A server\n// within the latency window is selected randomly. This can also be set through the \"localThresholdMS\" URI option (e.g.\n// \"localThresholdMS=15000\"). The default is 15 milliseconds.\nfunc (c *ClientOptions) SetLocalThreshold(d time.Duration) *ClientOptions {\n\tc.LocalThreshold = &d\n\n\treturn c\n}\n\n// SetLoggerOptions specifies a LoggerOptions containing options for\n// configuring a logger.\nfunc (c *ClientOptions) SetLoggerOptions(lopts *LoggerOptions) *ClientOptions {\n\tc.LoggerOptions = lopts\n\n\treturn c\n}\n\n// SetMaxConnIdleTime specifies the maximum amount of time that a connection will remain idle in a connection pool\n// before it is removed from the pool and closed. This can also be set through the \"maxIdleTimeMS\" URI option (e.g.\n// \"maxIdleTimeMS=10000\"). The default is 0, meaning a connection can remain unused indefinitely.\nfunc (c *ClientOptions) SetMaxConnIdleTime(d time.Duration) *ClientOptions {\n\tc.MaxConnIdleTime = &d\n\n\treturn c\n}\n\n// SetMaxPoolSize specifies that maximum number of connections allowed in the driver's connection pool to each server.\n// Requests to a server will block if this maximum is reached. This can also be set through the \"maxPoolSize\" URI option\n// (e.g. \"maxPoolSize=100\"). If this is 0, maximum connection pool size is not limited. The default is 100.\nfunc (c *ClientOptions) SetMaxPoolSize(u uint64) *ClientOptions {\n\tc.MaxPoolSize = &u\n\n\treturn c\n}\n\n// SetMinPoolSize specifies the minimum number of connections allowed in the driver's connection pool to each server. If\n// this is non-zero, each server's pool will be maintained in the background to ensure that the size does not fall below\n// the minimum. This can also be set through the \"minPoolSize\" URI option (e.g. \"minPoolSize=100\"). The default is 0.\nfunc (c *ClientOptions) SetMinPoolSize(u uint64) *ClientOptions {\n\tc.MinPoolSize = &u\n\n\treturn c\n}\n\n// SetMaxConnecting specifies the maximum number of connections a connection pool may establish simultaneously. This can\n// also be set through the \"maxConnecting\" URI option (e.g. \"maxConnecting=2\"). If this is 0, the default is used. The\n// default is 2. Values greater than 100 are not recommended.\nfunc (c *ClientOptions) SetMaxConnecting(u uint64) *ClientOptions {\n\tc.MaxConnecting = &u\n\n\treturn c\n}\n\n// SetPoolMonitor specifies a PoolMonitor to receive connection pool events. See the event.PoolMonitor documentation\n// for more information about the structure of the monitor and events that can be received.\nfunc (c *ClientOptions) SetPoolMonitor(m *event.PoolMonitor) *ClientOptions {\n\tc.PoolMonitor = m\n\n\treturn c\n}\n\n// SetMonitor specifies a CommandMonitor to receive command events. See the event.CommandMonitor documentation for more\n// information about the structure of the monitor and events that can be received.\nfunc (c *ClientOptions) SetMonitor(m *event.CommandMonitor) *ClientOptions {\n\tc.Monitor = m\n\n\treturn c\n}\n\n// SetServerMonitor specifies an SDAM monitor used to monitor SDAM events.\nfunc (c *ClientOptions) SetServerMonitor(m *event.ServerMonitor) *ClientOptions {\n\tc.ServerMonitor = m\n\n\treturn c\n}\n\n// SetReadConcern specifies the read concern to use for read operations. A read concern level can also be set through\n// the \"readConcernLevel\" URI option (e.g. \"readConcernLevel=majority\"). The default is nil, meaning the server will use\n// its configured default.\nfunc (c *ClientOptions) SetReadConcern(rc *readconcern.ReadConcern) *ClientOptions {\n\tc.ReadConcern = rc\n\n\treturn c\n}\n\n// SetReadPreference specifies the read preference to use for read operations. This can also be set through the\n// following URI options:\n//\n// 1. \"readPreference\" - Specify the read preference mode (e.g. \"readPreference=primary\").\n//\n// 2. \"readPreferenceTags\": Specify one or more read preference tags\n// (e.g. \"readPreferenceTags=region:south,datacenter:A\").\n//\n// 3. \"maxStalenessSeconds\" (or \"maxStaleness\"): Specify a maximum replication lag for reads from secondaries in a\n// replica set (e.g. \"maxStalenessSeconds=10\").\n//\n// The default is readpref.Primary(). See https://www.mongodb.com/docs/manual/core/read-preference/#read-preference for\n// more information about read preferences.\nfunc (c *ClientOptions) SetReadPreference(rp *readpref.ReadPref) *ClientOptions {\n\tc.ReadPreference = rp\n\n\treturn c\n}\n\n// SetBSONOptions configures optional BSON marshaling and unmarshaling behavior.\nfunc (c *ClientOptions) SetBSONOptions(bopts *BSONOptions) *ClientOptions {\n\tc.BSONOptions = bopts\n\n\treturn c\n}\n\n// SetRegistry specifies the BSON registry to use for BSON marshalling/unmarshalling operations. The default is\n// bson.NewRegistry().\nfunc (c *ClientOptions) SetRegistry(registry *bson.Registry) *ClientOptions {\n\tc.Registry = registry\n\n\treturn c\n}\n\n// SetReplicaSet specifies the replica set name for the cluster. If specified, the cluster will be treated as a replica\n// set and the driver will automatically discover all servers in the set, starting with the nodes specified through\n// ApplyURI or SetHosts. All nodes in the replica set must have the same replica set name, or they will not be\n// considered as part of the set by the Client. This can also be set through the \"replicaSet\" URI option (e.g.\n// \"replicaSet=replset\"). The default is empty.\nfunc (c *ClientOptions) SetReplicaSet(s string) *ClientOptions {\n\tc.ReplicaSet = &s\n\n\treturn c\n}\n\n// SetRetryWrites specifies whether supported write operations should be retried once on certain errors, such as network\n// errors.\n//\n// Supported operations are InsertOne, UpdateOne, ReplaceOne, DeleteOne, FindOneAndDelete, FindOneAndReplace,\n// FindOneAndDelete, InsertMany, and BulkWrite. Note that BulkWrite requests must not include UpdateManyModel or\n// DeleteManyModel instances to be considered retryable. Unacknowledged writes will not be retried, even if this option\n// is set to true.\n//\n// This option only works on a replica set or sharded cluster and will be ignored for any other cluster type.\n// This can also be set through the \"retryWrites\" URI option (e.g. \"retryWrites=true\"). The default is true.\nfunc (c *ClientOptions) SetRetryWrites(b bool) *ClientOptions {\n\tc.RetryWrites = &b\n\n\treturn c\n}\n\n// SetRetryReads specifies whether supported read operations should be retried once on certain errors, such as network\n// errors.\n//\n// Supported operations are Find, FindOne, Aggregate without a $out stage, Distinct, CountDocuments,\n// EstimatedDocumentCount, Watch (for Client, Database, and Collection), ListCollections, and ListDatabases. Note that\n// operations run through RunCommand are not retried.\n//\n// The default is true.\nfunc (c *ClientOptions) SetRetryReads(b bool) *ClientOptions {\n\tc.RetryReads = &b\n\n\treturn c\n}\n\n// SetServerSelectionTimeout specifies how long the driver will wait to find an available, suitable server to execute an\n// operation. This can also be set through the \"serverSelectionTimeoutMS\" URI option (e.g.\n// \"serverSelectionTimeoutMS=30000\"). The default value is 30 seconds.\nfunc (c *ClientOptions) SetServerSelectionTimeout(d time.Duration) *ClientOptions {\n\tc.ServerSelectionTimeout = &d\n\n\treturn c\n}\n\n// SetTimeout specifies the amount of time that a single operation run on this\n// Client can execute before returning an error. The deadline of any operation\n// run through the Client will be honored above any Timeout set on the Client;\n// Timeout will only be honored if there is no deadline on the operation\n// Context. Timeout can also be set through the \"timeoutMS\" URI option\n// (e.g. \"timeoutMS=1000\"). The default value is nil, meaning operations do not\n// inherit a timeout from the Client.\n//\n// If any Timeout is set (even 0) on the Client, the values of MaxTime on\n// operation options, TransactionOptions.MaxCommitTime and\n// SessionOptions.DefaultMaxCommitTime will be ignored.\nfunc (c *ClientOptions) SetTimeout(d time.Duration) *ClientOptions {\n\tc.Timeout = &d\n\n\treturn c\n}\n\n// SetTLSConfig specifies a tls.Config instance to use use to configure TLS on all connections created to the cluster.\n// This can also be set through the following URI options:\n//\n// 1. \"tls\" (or \"ssl\"): Specify if TLS should be used (e.g. \"tls=true\").\n//\n// 2. Either \"tlsCertificateKeyFile\" (or \"sslClientCertificateKeyFile\") or a combination of \"tlsCertificateFile\" and\n// \"tlsPrivateKeyFile\". The \"tlsCertificateKeyFile\" option specifies a path to the client certificate and private key,\n// which must be concatenated into one file. The \"tlsCertificateFile\" and \"tlsPrivateKey\" combination specifies separate\n// paths to the client certificate and private key, respectively. Note that if \"tlsCertificateKeyFile\" is used, the\n// other two options must not be specified. Only the subject name of the first certificate is honored as the username\n// for X509 auth in a file with multiple certs.\n//\n// 3. \"tlsCertificateKeyFilePassword\" (or \"sslClientCertificateKeyPassword\"): Specify the password to decrypt the client\n// private key file (e.g. \"tlsCertificateKeyFilePassword=password\").\n//\n// 4. \"tlsCaFile\" (or \"sslCertificateAuthorityFile\"): Specify the path to a single or bundle of certificate authorities\n// to be considered trusted when making a TLS connection (e.g. \"tlsCaFile=/path/to/caFile\").\n//\n// 5. \"tlsInsecure\" (or \"sslInsecure\"): Specifies whether or not certificates and hostnames received from the server\n// should be validated. If true (e.g. \"tlsInsecure=true\"), the TLS library will accept any certificate presented by the\n// server and any host name in that certificate. Note that setting this to true makes TLS susceptible to\n// man-in-the-middle attacks and should only be done for testing.\n//\n// The default is nil, meaning no TLS will be enabled.\nfunc (c *ClientOptions) SetTLSConfig(cfg *tls.Config) *ClientOptions {\n\tc.TLSConfig = cfg\n\n\treturn c\n}\n\n// SetHTTPClient specifies the http.Client to be used for any HTTP requests.\n//\n// This should only be used to set custom HTTP client configurations. By default, the connection will use an httputil.DefaultHTTPClient.\nfunc (c *ClientOptions) SetHTTPClient(client *http.Client) *ClientOptions {\n\tc.HTTPClient = client\n\n\treturn c\n}\n\n// SetWriteConcern specifies the write concern to use to for write operations. This can also be set through the following\n// URI options:\n//\n// 1. \"w\": Specify the number of nodes in the cluster that must acknowledge write operations before the operation\n// returns or \"majority\" to specify that a majority of the nodes must acknowledge writes. This can either be an integer\n// (e.g. \"w=10\") or the string \"majority\" (e.g. \"w=majority\").\n//\n// 2. \"wTimeoutMS\": Specify how long write operations should wait for the correct number of nodes to acknowledge the\n// operation (e.g. \"wTimeoutMS=1000\").\n//\n// 3. \"journal\": Specifies whether or not write operations should be written to an on-disk journal on the server before\n// returning (e.g. \"journal=true\").\n//\n// The default is nil, meaning the server will use its configured default.\nfunc (c *ClientOptions) SetWriteConcern(wc *writeconcern.WriteConcern) *ClientOptions {\n\tc.WriteConcern = wc\n\n\treturn c\n}\n\n// SetZlibLevel specifies the level for the zlib compressor. This option is ignored if zlib is not specified as a\n// compressor through ApplyURI or SetCompressors. Supported values are -1 through 9, inclusive. -1 tells the zlib\n// library to use its default, 0 means no compression, 1 means best speed, and 9 means best compression.\n// This can also be set through the \"zlibCompressionLevel\" URI option (e.g. \"zlibCompressionLevel=-1\"). Defaults to -1.\nfunc (c *ClientOptions) SetZlibLevel(level int) *ClientOptions {\n\tc.ZlibLevel = &level\n\n\treturn c\n}\n\n// SetZstdLevel sets the level for the zstd compressor. This option is ignored if zstd is not specified as a compressor\n// through ApplyURI or SetCompressors. Supported values are 1 through 20, inclusive. 1 means best speed and 20 means\n// best compression. This can also be set through the \"zstdCompressionLevel\" URI option. Defaults to 6.\nfunc (c *ClientOptions) SetZstdLevel(level int) *ClientOptions {\n\tc.ZstdLevel = &level\n\n\treturn c\n}\n\n// SetAutoEncryptionOptions specifies an AutoEncryptionOptions instance to automatically encrypt and decrypt commands\n// and their results. See the options.AutoEncryptionOptions documentation for more information about the supported\n// options.\nfunc (c *ClientOptions) SetAutoEncryptionOptions(aeopts *AutoEncryptionOptions) *ClientOptions {\n\tc.AutoEncryptionOptions = aeopts\n\n\treturn c\n}\n\n// SetDisableOCSPEndpointCheck specifies whether or not the driver should reach out to OCSP responders to verify the\n// certificate status for certificates presented by the server that contain a list of OCSP responders.\n//\n// If set to true, the driver will verify the status of the certificate using a response stapled by the server, if there\n// is one, but will not send an HTTP request to any responders if there is no staple. In this case, the driver will\n// continue the connection even though the certificate status is not known.\n//\n// This can also be set through the tlsDisableOCSPEndpointCheck URI option. Both this URI option and tlsInsecure must\n// not be set at the same time and will error if they are. The default value is false.\nfunc (c *ClientOptions) SetDisableOCSPEndpointCheck(disableCheck bool) *ClientOptions {\n\tc.DisableOCSPEndpointCheck = &disableCheck\n\n\treturn c\n}\n\n// SetServerAPIOptions specifies a ServerAPIOptions instance used to configure the API version sent to the server\n// when running commands. See the options.ServerAPIOptions documentation for more information about the supported\n// options.\nfunc (c *ClientOptions) SetServerAPIOptions(sopts *ServerAPIOptions) *ClientOptions {\n\tc.ServerAPIOptions = sopts\n\n\treturn c\n}\n\n// SetServerMonitoringMode specifies the server monitoring protocol to use. See\n// the helper constants ServerMonitoringModeAuto, ServerMonitoringModePoll, and\n// ServerMonitoringModeStream for more information about valid server\n// monitoring modes.\nfunc (c *ClientOptions) SetServerMonitoringMode(mode string) *ClientOptions {\n\tc.ServerMonitoringMode = &mode\n\n\treturn c\n}\n\n// SetSRVMaxHosts specifies the maximum number of SRV results to randomly select during polling. To limit the number\n// of hosts selected in SRV discovery, this function must be called before ApplyURI. This can also be set through\n// the \"srvMaxHosts\" URI option.\nfunc (c *ClientOptions) SetSRVMaxHosts(srvMaxHosts int) *ClientOptions {\n\tc.SRVMaxHosts = &srvMaxHosts\n\n\treturn c\n}\n\n// SetSRVServiceName specifies a custom SRV service name to use in SRV polling. To use a custom SRV service name\n// in SRV discovery, this function must be called before ApplyURI. This can also be set through the \"srvServiceName\"\n// URI option.\nfunc (c *ClientOptions) SetSRVServiceName(srvName string) *ClientOptions {\n\tc.SRVServiceName = &srvName\n\n\treturn c\n}\n\n// SetDriverInfo configures optional data to include in the handshake's client\n// metadata, delimited by \"|\" with the driver-generated data. This should be\n// used by libraries wrapping the driver, e.g. ODMs.\nfunc (c *ClientOptions) SetDriverInfo(info *DriverInfo) *ClientOptions {\n\tc.DriverInfo = info\n\n\treturn c\n}\n\n// addCACertFromFile adds a root CA certificate to the configuration given a path\n// to the containing file.\nfunc addCACertFromFile(cfg *tls.Config, file string) error {\n\tdata, err := ioutil.ReadFile(file)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif cfg.RootCAs == nil {\n\t\tcfg.RootCAs = x509.NewCertPool()\n\t}\n\tif !cfg.RootCAs.AppendCertsFromPEM(data) {\n\t\treturn errors.New(\"the specified CA file does not contain any valid certificates\")\n\t}\n\n\treturn nil\n}\n\nfunc addClientCertFromSeparateFiles(cfg *tls.Config, keyFile, certFile, keyPassword string) (string, error) {\n\tkeyData, err := ioutil.ReadFile(keyFile)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tcertData, err := ioutil.ReadFile(certFile)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\tkeySize := len(keyData)\n\tif keySize > 64*1024*1024 {\n\t\treturn \"\", errors.New(\"X.509 key must be less than 64 MiB\")\n\t}\n\tcertSize := len(certData)\n\tif certSize > 64*1024*1024 {\n\t\treturn \"\", errors.New(\"X.509 certificate must be less than 64 MiB\")\n\t}\n\tdataSize := int64(keySize) + int64(certSize) + 1\n\tif dataSize > math.MaxInt {\n\t\treturn \"\", errors.New(\"size overflow\")\n\t}\n\tdata := make([]byte, 0, int(dataSize))\n\tdata = append(data, keyData...)\n\tdata = append(data, '\\n')\n\tdata = append(data, certData...)\n\treturn addClientCertFromBytes(cfg, data, keyPassword)\n}\n\nfunc addClientCertFromConcatenatedFile(cfg *tls.Config, certKeyFile, keyPassword string) (string, error) {\n\tdata, err := ioutil.ReadFile(certKeyFile)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\treturn addClientCertFromBytes(cfg, data, keyPassword)\n}\n\n// addClientCertFromBytes adds client certificates to the configuration given a path to the\n// containing file and returns the subject name in the first certificate.\nfunc addClientCertFromBytes(cfg *tls.Config, data []byte, keyPasswd string) (string, error) {\n\tvar currentBlock *pem.Block\n\tvar certDecodedBlock []byte\n\tvar certBlocks, keyBlocks [][]byte\n\n\tremaining := data\n\tstart := 0\n\tfor {\n\t\tcurrentBlock, remaining = pem.Decode(remaining)\n\t\tif currentBlock == nil {\n\t\t\tbreak\n\t\t}\n\n\t\tif currentBlock.Type == \"CERTIFICATE\" {\n\t\t\tcertBlock := data[start : len(data)-len(remaining)]\n\t\t\tcertBlocks = append(certBlocks, certBlock)\n\t\t\t// Assign the certDecodedBlock when it is never set,\n\t\t\t// so only the first certificate is honored in a file with multiple certs.\n\t\t\tif certDecodedBlock == nil {\n\t\t\t\tcertDecodedBlock = currentBlock.Bytes\n\t\t\t}\n\t\t\tstart += len(certBlock)\n\t\t} else if strings.HasSuffix(currentBlock.Type, \"PRIVATE KEY\") {\n\t\t\tisEncrypted := x509.IsEncryptedPEMBlock(currentBlock) || strings.Contains(currentBlock.Type, \"ENCRYPTED PRIVATE KEY\")\n\t\t\tif isEncrypted {\n\t\t\t\tif keyPasswd == \"\" {\n\t\t\t\t\treturn \"\", fmt.Errorf(\"no password provided to decrypt private key\")\n\t\t\t\t}\n\n\t\t\t\tvar keyBytes []byte\n\t\t\t\tvar err error\n\t\t\t\t// Process the X.509-encrypted or PKCS-encrypted PEM block.\n\t\t\t\tif x509.IsEncryptedPEMBlock(currentBlock) {\n\t\t\t\t\t// Only covers encrypted PEM data with a DEK-Info header.\n\t\t\t\t\tkeyBytes, err = x509.DecryptPEMBlock(currentBlock, []byte(keyPasswd))\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\treturn \"\", err\n\t\t\t\t\t}\n\t\t\t\t} else if strings.Contains(currentBlock.Type, \"ENCRYPTED\") {\n\t\t\t\t\t// The pkcs8 package only handles the PKCS #5 v2.0 scheme.\n\t\t\t\t\tdecrypted, err := pkcs8.ParsePKCS8PrivateKey(currentBlock.Bytes, []byte(keyPasswd))\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\treturn \"\", err\n\t\t\t\t\t}\n\t\t\t\t\tkeyBytes, err = x509.MarshalPKCS8PrivateKey(decrypted)\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\treturn \"\", err\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tvar encoded bytes.Buffer\n\t\t\t\terr = pem.Encode(&encoded, &pem.Block{Type: currentBlock.Type, Bytes: keyBytes})\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn \"\", fmt.Errorf(\"error encoding private key as PEM: %w\", err)\n\t\t\t\t}\n\t\t\t\tkeyBlock := encoded.Bytes()\n\t\t\t\tkeyBlocks = append(keyBlocks, keyBlock)\n\t\t\t\tstart = len(data) - len(remaining)\n\t\t\t} else {\n\t\t\t\tkeyBlock := data[start : len(data)-len(remaining)]\n\t\t\t\tkeyBlocks = append(keyBlocks, keyBlock)\n\t\t\t\tstart += len(keyBlock)\n\t\t\t}\n\t\t}\n\t}\n\tif len(certBlocks) == 0 {\n\t\treturn \"\", fmt.Errorf(\"failed to find CERTIFICATE\")\n\t}\n\tif len(keyBlocks) == 0 {\n\t\treturn \"\", fmt.Errorf(\"failed to find PRIVATE KEY\")\n\t}\n\n\tcert, err := tls.X509KeyPair(bytes.Join(certBlocks, []byte(\"\\n\")), bytes.Join(keyBlocks, []byte(\"\\n\")))\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\tcfg.Certificates = append(cfg.Certificates, cert)\n\n\t// The documentation for the tls.X509KeyPair indicates that the Leaf certificate is not\n\t// retained.\n\tcrt, err := x509.ParseCertificate(certDecodedBlock)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\treturn crt.Subject.String(), nil\n}\n\nfunc stringSliceContains(source []string, target string) bool {\n\tfor _, str := range source {\n\t\tif str == target {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\n// create a username for x509 authentication from an x509 certificate subject.\nfunc extractX509UsernameFromSubject(subject string) string {\n\t// the Go x509 package gives the subject with the pairs in the reverse order from what we want.\n\tpairs := strings.Split(subject, \",\")\n\tfor left, right := 0, len(pairs)-1; left < right; left, right = left+1, right-1 {\n\t\tpairs[left], pairs[right] = pairs[right], pairs[left]\n\t}\n\n\treturn strings.Join(pairs, \",\")\n}\n\n// MergeClientOptions combines the given *ClientOptions into a single\n// *ClientOptions in a last one wins fashion. The specified options are merged\n// with the existing options on the client, with the specified options taking\n// precedence.\nfunc MergeClientOptions(opts ...*ClientOptions) *ClientOptions {\n\tif len(opts) == 1 {\n\t\tif opts[0] == nil {\n\t\t\treturn Client()\n\t\t}\n\n\t\treturn opts[0]\n\t}\n\n\tc := Client()\n\tfor _, opt := range opts {\n\t\tif opt == nil {\n\t\t\tcontinue\n\t\t}\n\t\toptValue := reflect.ValueOf(opt).Elem()\n\t\tcValue := reflect.ValueOf(c).Elem()\n\t\tfor i := 0; i < optValue.NumField(); i++ {\n\t\t\tfield := optValue.Field(i)\n\t\t\tfieldType := optValue.Type().Field(i)\n\t\t\t// Check if the field is exported and can be set\n\t\t\tif field.CanSet() && fieldType.PkgPath == \"\" && !field.IsZero() {\n\t\t\t\tcValue.Field(i).Set(field)\n\t\t\t}\n\t\t}\n\n\t\t// Manually handle unexported fields\n\t\tif opt.err != nil {\n\t\t\tc.err = opt.err\n\t\t}\n\n\t\tif opt.connString != nil {\n\t\t\tc.connString = opt.connString\n\t\t}\n\t}\n\n\treturn c\n}\n"
  },
  {
    "path": "mongo/options/clientoptions_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2022-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage options\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"crypto/tls\"\n\t\"crypto/x509\"\n\t\"encoding/pem\"\n\t\"errors\"\n\t\"fmt\"\n\t\"net\"\n\t\"net/http\"\n\t\"os\"\n\t\"reflect\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/google/go-cmp/cmp\"\n\t\"github.com/google/go-cmp/cmp/cmpopts\"\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/event\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/httputil\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/optionsutil\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/ptrutil\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/readconcern\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/readpref\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/writeconcern\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/connstring\"\n)\n\nvar tClientOptions = reflect.TypeOf(&ClientOptions{})\n\nfunc TestClientOptions(t *testing.T) {\n\tt.Run(\"ApplyURI/doesn't overwrite previous errors\", func(t *testing.T) {\n\t\turi := \"not-mongo-db-uri://\"\n\t\twant := fmt.Errorf(\n\t\t\t\"error parsing uri: %w\",\n\t\t\terrors.New(`scheme must be \"mongodb\" or \"mongodb+srv\"`))\n\t\tco := Client().ApplyURI(uri).ApplyURI(\"mongodb://localhost/\")\n\t\tgot := co.Validate()\n\t\tif !cmp.Equal(got, want, cmp.Comparer(compareErrors)) {\n\t\t\tt.Errorf(\"Did not received expected error. got %v; want %v\", got, want)\n\t\t}\n\t})\n\tt.Run(\"Set\", func(t *testing.T) {\n\t\ttestCases := []struct {\n\t\t\tname        string\n\t\t\tfn          any    // method to be run\n\t\t\targ         any    // argument for method\n\t\t\tfield       string // field to be set\n\t\t\tdereference bool   // Should we compare a pointer or the field\n\t\t}{\n\t\t\t{\"AppName\", (*ClientOptions).SetAppName, \"example-application\", \"AppName\", true},\n\t\t\t{\"Auth\", (*ClientOptions).SetAuth, Credential{Username: \"foo\", Password: \"bar\"}, \"Auth\", true},\n\t\t\t{\"Compressors\", (*ClientOptions).SetCompressors, []string{\"zstd\", \"snappy\", \"zlib\"}, \"Compressors\", true},\n\t\t\t{\"ConnectTimeout\", (*ClientOptions).SetConnectTimeout, 5 * time.Second, \"ConnectTimeout\", true},\n\t\t\t{\"Dialer\", (*ClientOptions).SetDialer, testDialer{Num: 12345}, \"Dialer\", true},\n\t\t\t{\"HeartbeatInterval\", (*ClientOptions).SetHeartbeatInterval, 5 * time.Second, \"HeartbeatInterval\", true},\n\t\t\t{\"Hosts\", (*ClientOptions).SetHosts, []string{\"localhost:27017\", \"localhost:27018\", \"localhost:27019\"}, \"Hosts\", true},\n\t\t\t{\"LocalThreshold\", (*ClientOptions).SetLocalThreshold, 5 * time.Second, \"LocalThreshold\", true},\n\t\t\t{\"MaxConnIdleTime\", (*ClientOptions).SetMaxConnIdleTime, 5 * time.Second, \"MaxConnIdleTime\", true},\n\t\t\t{\"MaxPoolSize\", (*ClientOptions).SetMaxPoolSize, uint64(250), \"MaxPoolSize\", true},\n\t\t\t{\"MinPoolSize\", (*ClientOptions).SetMinPoolSize, uint64(10), \"MinPoolSize\", true},\n\t\t\t{\"MaxConnecting\", (*ClientOptions).SetMaxConnecting, uint64(10), \"MaxConnecting\", true},\n\t\t\t{\"PoolMonitor\", (*ClientOptions).SetPoolMonitor, &event.PoolMonitor{}, \"PoolMonitor\", false},\n\t\t\t{\"Monitor\", (*ClientOptions).SetMonitor, &event.CommandMonitor{}, \"Monitor\", false},\n\t\t\t{\"ReadConcern\", (*ClientOptions).SetReadConcern, readconcern.Majority(), \"ReadConcern\", false},\n\t\t\t{\"ReadPreference\", (*ClientOptions).SetReadPreference, readpref.SecondaryPreferred(), \"ReadPreference\", false},\n\t\t\t{\"Registry\", (*ClientOptions).SetRegistry, bson.NewRegistry(), \"Registry\", false},\n\t\t\t{\"ReplicaSet\", (*ClientOptions).SetReplicaSet, \"example-replicaset\", \"ReplicaSet\", true},\n\t\t\t{\"RetryWrites\", (*ClientOptions).SetRetryWrites, true, \"RetryWrites\", true},\n\t\t\t{\"ServerSelectionTimeout\", (*ClientOptions).SetServerSelectionTimeout, 5 * time.Second, \"ServerSelectionTimeout\", true},\n\t\t\t{\"Direct\", (*ClientOptions).SetDirect, true, \"Direct\", true},\n\t\t\t{\"TLSConfig\", (*ClientOptions).SetTLSConfig, &tls.Config{}, \"TLSConfig\", false},\n\t\t\t{\"WriteConcern\", (*ClientOptions).SetWriteConcern, writeconcern.Majority(), \"WriteConcern\", false},\n\t\t\t{\"ZlibLevel\", (*ClientOptions).SetZlibLevel, 6, \"ZlibLevel\", true},\n\t\t\t{\"DisableOCSPEndpointCheck\", (*ClientOptions).SetDisableOCSPEndpointCheck, true, \"DisableOCSPEndpointCheck\", true},\n\t\t\t{\"LoadBalanced\", (*ClientOptions).SetLoadBalanced, true, \"LoadBalanced\", true},\n\t\t}\n\n\t\topt1, opt2, optResult := Client(), Client(), Client()\n\t\tfor idx, tc := range testCases {\n\t\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\t\tfn := reflect.ValueOf(tc.fn)\n\t\t\t\tif fn.Kind() != reflect.Func {\n\t\t\t\t\tt.Fatal(\"fn argument must be a function\")\n\t\t\t\t}\n\t\t\t\tif fn.Type().NumIn() < 2 || fn.Type().In(0) != tClientOptions {\n\t\t\t\t\tt.Fatal(\"fn argument must have a *ClientOptions as the first argument and one other argument\")\n\t\t\t\t}\n\t\t\t\tif _, exists := tClientOptions.Elem().FieldByName(tc.field); !exists {\n\t\t\t\t\tt.Fatalf(\"field (%s) does not exist in ClientOptions\", tc.field)\n\t\t\t\t}\n\t\t\t\targs := make([]reflect.Value, 2)\n\t\t\t\tclient := reflect.New(tClientOptions.Elem())\n\t\t\t\targs[0] = client\n\t\t\t\twant := reflect.ValueOf(tc.arg)\n\t\t\t\targs[1] = want\n\n\t\t\t\tif !want.IsValid() || !want.CanInterface() {\n\t\t\t\t\tt.Fatal(\"arg property of test case must be valid\")\n\t\t\t\t}\n\n\t\t\t\t_ = fn.Call(args)\n\n\t\t\t\t// To avoid duplication we're piggybacking on the Set* tests to make the\n\t\t\t\t// MergeClientOptions test simpler and more thorough.\n\t\t\t\t// To do this we set the odd numbered test cases to the first opt, the even and\n\t\t\t\t// divisible by three test cases to the second, and the result of merging the two to\n\t\t\t\t// the result option. This gives us coverage of options set by the first option, by\n\t\t\t\t// the second, and by both.\n\t\t\t\tif idx%2 != 0 {\n\t\t\t\t\targs[0] = reflect.ValueOf(opt1)\n\t\t\t\t\t_ = fn.Call(args)\n\t\t\t\t}\n\t\t\t\tif idx%2 == 0 || idx%3 == 0 {\n\t\t\t\t\targs[0] = reflect.ValueOf(opt2)\n\t\t\t\t\t_ = fn.Call(args)\n\t\t\t\t}\n\t\t\t\targs[0] = reflect.ValueOf(optResult)\n\t\t\t\t_ = fn.Call(args)\n\n\t\t\t\tgot := client.Elem().FieldByName(tc.field)\n\t\t\t\tif !got.IsValid() || !got.CanInterface() {\n\t\t\t\t\tt.Fatal(\"cannot create concrete instance from retrieved field\")\n\t\t\t\t}\n\n\t\t\t\tif got.Kind() == reflect.Ptr && tc.dereference {\n\t\t\t\t\tgot = got.Elem()\n\t\t\t\t}\n\n\t\t\t\tif !cmp.Equal(\n\t\t\t\t\tgot.Interface(), want.Interface(),\n\t\t\t\t\tcmp.AllowUnexported(readconcern.ReadConcern{}, writeconcern.WriteConcern{}, readpref.ReadPref{}),\n\t\t\t\t\tcmp.Comparer(func(r1, r2 *bson.Registry) bool { return r1 == r2 }),\n\t\t\t\t\tcmp.Comparer(func(cfg1, cfg2 *tls.Config) bool { return cfg1 == cfg2 }),\n\t\t\t\t\tcmp.Comparer(func(fp1, fp2 *event.PoolMonitor) bool { return fp1 == fp2 }),\n\t\t\t\t) {\n\t\t\t\t\tt.Errorf(\"Field not set properly. got %v; want %v\", got.Interface(), want.Interface())\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\n\t\tt.Run(\"MergeClientOptions/all set\", func(t *testing.T) {\n\t\t\twant := optResult\n\t\t\tgot := MergeClientOptions(nil, opt1, opt2)\n\t\t\tif diff := cmp.Diff(\n\t\t\t\tgot, want,\n\t\t\t\tcmp.AllowUnexported(readconcern.ReadConcern{}, writeconcern.WriteConcern{}, readpref.ReadPref{}),\n\t\t\t\tcmp.Comparer(func(r1, r2 *bson.Registry) bool { return r1 == r2 }),\n\t\t\t\tcmp.Comparer(func(cfg1, cfg2 *tls.Config) bool { return cfg1 == cfg2 }),\n\t\t\t\tcmp.Comparer(func(fp1, fp2 *event.PoolMonitor) bool { return fp1 == fp2 }),\n\t\t\t\tcmp.Comparer(optionsutil.Equal),\n\t\t\t\tcmp.AllowUnexported(ClientOptions{}),\n\t\t\t\tcmpopts.IgnoreFields(http.Client{}, \"Transport\"),\n\t\t\t); diff != \"\" {\n\t\t\t\tt.Errorf(\"diff:\\n%s\", diff)\n\t\t\t\tt.Errorf(\"Merged client options do not match. got %v; want %v\", got, want)\n\t\t\t}\n\t\t})\n\n\t\t// go-cmp dont support error comparisons (https://github.com/google/go-cmp/issues/24)\n\t\t// Use specifique test for this\n\t\tt.Run(\"MergeClientOptions/err\", func(t *testing.T) {\n\t\t\topt1, opt2 := Client(), Client()\n\t\t\topt1.err = errors.New(\"Test error\")\n\n\t\t\tgot := MergeClientOptions(nil, opt1, opt2)\n\t\t\tif got.err.Error() != \"Test error\" {\n\t\t\t\tt.Errorf(\"Merged client options do not match. got %v; want %v\", got.err.Error(), opt1.err.Error())\n\t\t\t}\n\t\t})\n\n\t\tt.Run(\"MergeClientOptions single nil option\", func(t *testing.T) {\n\t\t\tgot := MergeClientOptions(nil)\n\t\t\tassert.Equal(t, Client(), got)\n\t\t})\n\n\t\tt.Run(\"MergeClientOptions multiple nil options\", func(t *testing.T) {\n\t\t\tgot := MergeClientOptions(nil, nil)\n\t\t\tassert.Equal(t, Client(), got)\n\t\t})\n\t})\n\tt.Run(\"direct connection validation\", func(t *testing.T) {\n\t\tt.Run(\"multiple hosts\", func(t *testing.T) {\n\t\t\texpectedErr := errors.New(\"a direct connection cannot be made if multiple hosts are specified\")\n\n\t\t\ttestCases := []struct {\n\t\t\t\tname string\n\t\t\t\topts *ClientOptions\n\t\t\t}{\n\t\t\t\t{\"hosts in URI\", Client().ApplyURI(\"mongodb://localhost,localhost2\")},\n\t\t\t\t{\"hosts in options\", Client().SetHosts([]string{\"localhost\", \"localhost2\"})},\n\t\t\t}\n\t\t\tfor _, tc := range testCases {\n\t\t\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\t\t\terr := tc.opts.SetDirect(true).Validate()\n\t\t\t\t\tassert.NotNil(t, err, \"expected error, got nil\")\n\t\t\t\t\tassert.Equal(t, expectedErr.Error(), err.Error(), \"expected error %v, got %v\", expectedErr, err)\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\t\tt.Run(\"srv\", func(t *testing.T) {\n\t\t\texpectedErr := errors.New(\"a direct connection cannot be made if an SRV URI is used\")\n\t\t\t// Use a non-SRV URI and manually set the scheme because using an SRV URI would force an SRV lookup.\n\t\t\topts := Client().ApplyURI(\"mongodb://localhost:27017\")\n\n\t\t\topts.connString.Scheme = connstring.SchemeMongoDBSRV\n\n\t\t\terr := opts.SetDirect(true).Validate()\n\t\t\tassert.NotNil(t, err, \"expected error, got nil\")\n\t\t\tassert.Equal(t, expectedErr.Error(), err.Error(), \"expected error %v, got %v\", expectedErr, err)\n\t\t})\n\t})\n\tt.Run(\"loadBalanced validation\", func(t *testing.T) {\n\t\ttestCases := []struct {\n\t\t\tname string\n\t\t\topts *ClientOptions\n\t\t\terr  error\n\t\t}{\n\t\t\t{\"multiple hosts in URI\", Client().ApplyURI(\"mongodb://foo,bar\"), connstring.ErrLoadBalancedWithMultipleHosts},\n\t\t\t{\"multiple hosts in options\", Client().SetHosts([]string{\"foo\", \"bar\"}), connstring.ErrLoadBalancedWithMultipleHosts},\n\t\t\t{\"replica set name\", Client().SetReplicaSet(\"foo\"), connstring.ErrLoadBalancedWithReplicaSet},\n\t\t\t{\"directConnection=true\", Client().SetDirect(true), connstring.ErrLoadBalancedWithDirectConnection},\n\t\t}\n\t\tfor _, tc := range testCases {\n\t\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\t\t// The loadBalanced option should not be validated if it is unset or false.\n\t\t\t\terr := tc.opts.Validate()\n\t\t\t\tassert.Nil(t, err, \"Validate error when loadBalanced is unset: %v\", err)\n\n\t\t\t\ttc.opts.SetLoadBalanced(false)\n\t\t\t\terr = tc.opts.Validate()\n\t\t\t\tassert.Nil(t, err, \"Validate error when loadBalanced=false: %v\", err)\n\n\t\t\t\ttc.opts.SetLoadBalanced(true)\n\t\t\t\terr = tc.opts.Validate()\n\t\t\t\tassert.Equal(t, tc.err, err, \"expected error %v when loadBalanced=true, got %v\", tc.err, err)\n\t\t\t})\n\t\t}\n\t})\n\tt.Run(\"heartbeatFrequencyMS validation\", func(t *testing.T) {\n\t\ttestCases := []struct {\n\t\t\tname string\n\t\t\topts *ClientOptions\n\t\t\terr  error\n\t\t}{\n\t\t\t{\n\t\t\t\tname: \"heartbeatFrequencyMS > minimum (500ms)\",\n\t\t\t\topts: Client().SetHeartbeatInterval(10000 * time.Millisecond),\n\t\t\t\terr:  nil,\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"heartbeatFrequencyMS == minimum (500ms)\",\n\t\t\t\topts: Client().SetHeartbeatInterval(500 * time.Millisecond),\n\t\t\t\terr:  nil,\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"heartbeatFrequencyMS < minimum (500ms)\",\n\t\t\t\topts: Client().SetHeartbeatInterval(10 * time.Millisecond),\n\t\t\t\terr:  errors.New(\"heartbeatFrequencyMS must exceed the minimum heartbeat interval of 500ms, got heartbeatFrequencyMS=\\\"10ms\\\"\"),\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"heartbeatFrequencyMS == 0\",\n\t\t\t\topts: Client().SetHeartbeatInterval(0 * time.Millisecond),\n\t\t\t\terr:  errors.New(\"heartbeatFrequencyMS must exceed the minimum heartbeat interval of 500ms, got heartbeatFrequencyMS=\\\"0s\\\"\"),\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"heartbeatFrequencyMS > minimum (500ms) from URI\",\n\t\t\t\topts: Client().ApplyURI(\"mongodb://localhost:27017/?heartbeatFrequencyMS=10000\"),\n\t\t\t\terr:  nil,\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"heartbeatFrequencyMS == minimum (500ms) from URI\",\n\t\t\t\topts: Client().ApplyURI(\"mongodb://localhost:27017/?heartbeatFrequencyMS=500\"),\n\t\t\t\terr:  nil,\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"heartbeatFrequencyMS < minimum (500ms) from URI\",\n\t\t\t\topts: Client().ApplyURI(\"mongodb://localhost:27017/?heartbeatFrequencyMS=10\"),\n\t\t\t\terr:  errors.New(\"heartbeatFrequencyMS must exceed the minimum heartbeat interval of 500ms, got heartbeatFrequencyMS=\\\"10ms\\\"\"),\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"heartbeatFrequencyMS == 0 from URI\",\n\t\t\t\topts: Client().ApplyURI(\"mongodb://localhost:27017/?heartbeatFrequencyMS=0\"),\n\t\t\t\terr:  errors.New(\"heartbeatFrequencyMS must exceed the minimum heartbeat interval of 500ms, got heartbeatFrequencyMS=\\\"0s\\\"\"),\n\t\t\t},\n\t\t}\n\t\tfor _, tc := range testCases {\n\t\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\t\terr := tc.opts.Validate()\n\t\t\t\tassert.Equal(t, tc.err, err)\n\t\t\t})\n\t\t}\n\t})\n\tt.Run(\"minPoolSize validation\", func(t *testing.T) {\n\t\ttestCases := []struct {\n\t\t\tname string\n\t\t\topts *ClientOptions\n\t\t\terr  error\n\t\t}{\n\t\t\t{\n\t\t\t\t\"minPoolSize < maxPoolSize\",\n\t\t\t\tClient().SetMinPoolSize(128).SetMaxPoolSize(256),\n\t\t\t\tnil,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"minPoolSize == maxPoolSize\",\n\t\t\t\tClient().SetMinPoolSize(128).SetMaxPoolSize(128),\n\t\t\t\tnil,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"minPoolSize > maxPoolSize\",\n\t\t\t\tClient().SetMinPoolSize(64).SetMaxPoolSize(32),\n\t\t\t\terrors.New(\"minPoolSize must be less than or equal to maxPoolSize, got minPoolSize=64 maxPoolSize=32\"),\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"maxPoolSize == 0\",\n\t\t\t\tClient().SetMinPoolSize(128).SetMaxPoolSize(0),\n\t\t\t\tnil,\n\t\t\t},\n\t\t}\n\t\tfor _, tc := range testCases {\n\t\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\t\terr := tc.opts.Validate()\n\t\t\t\tassert.Equal(t, tc.err, err, \"expected error %v, got %v\", tc.err, err)\n\t\t\t})\n\t\t}\n\t})\n\tt.Run(\"srvMaxHosts validation\", func(t *testing.T) {\n\t\ttestCases := []struct {\n\t\t\tname string\n\t\t\topts *ClientOptions\n\t\t\terr  error\n\t\t}{\n\t\t\t{\"replica set name\", Client().SetReplicaSet(\"foo\"), connstring.ErrSRVMaxHostsWithReplicaSet},\n\t\t\t{\"loadBalanced=true\", Client().SetLoadBalanced(true), connstring.ErrSRVMaxHostsWithLoadBalanced},\n\t\t\t{\"loadBalanced=false\", Client().SetLoadBalanced(false), nil},\n\t\t}\n\t\tfor _, tc := range testCases {\n\t\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\t\terr := tc.opts.Validate()\n\t\t\t\tassert.Nil(t, err, \"Validate error when srvMxaHosts is unset: %v\", err)\n\n\t\t\t\ttc.opts.SetSRVMaxHosts(0)\n\t\t\t\terr = tc.opts.Validate()\n\t\t\t\tassert.Nil(t, err, \"Validate error when srvMaxHosts is 0: %v\", err)\n\n\t\t\t\ttc.opts.SetSRVMaxHosts(2)\n\t\t\t\terr = tc.opts.Validate()\n\t\t\t\tassert.Equal(t, tc.err, err, \"expected error %v when srvMaxHosts > 0, got %v\", tc.err, err)\n\t\t\t})\n\t\t}\n\t})\n\tt.Run(\"srvMaxHosts validation\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\ttestCases := []struct {\n\t\t\tname string\n\t\t\topts *ClientOptions\n\t\t\terr  error\n\t\t}{\n\t\t\t{\n\t\t\t\tname: \"valid ServerAPI\",\n\t\t\t\topts: Client().SetServerAPIOptions(ServerAPI(ServerAPIVersion1)),\n\t\t\t\terr:  nil,\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"invalid ServerAPI\",\n\t\t\t\topts: Client().SetServerAPIOptions(ServerAPI(\"nope\")),\n\t\t\t\terr:  errors.New(`api version \"nope\" not supported; this driver version only supports API version \"1\"`),\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"invalid ServerAPI with other invalid options\",\n\t\t\t\topts: Client().SetServerAPIOptions(ServerAPI(\"nope\")).SetSRVMaxHosts(1).SetReplicaSet(\"foo\"),\n\t\t\t\terr:  errors.New(`api version \"nope\" not supported; this driver version only supports API version \"1\"`),\n\t\t\t},\n\t\t}\n\t\tfor _, tc := range testCases {\n\t\t\ttc := tc // Capture range variable.\n\n\t\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\t\tt.Parallel()\n\n\t\t\t\terr := tc.opts.Validate()\n\t\t\t\tassert.Equal(t, tc.err, err, \"want error %v, got error %v\", tc.err, err)\n\t\t\t})\n\t\t}\n\t})\n\tt.Run(\"server monitoring mode validation\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\ttestCases := []struct {\n\t\t\tname string\n\t\t\topts *ClientOptions\n\t\t\terr  error\n\t\t}{\n\t\t\t{\n\t\t\t\tname: \"undefined\",\n\t\t\t\topts: Client(),\n\t\t\t\terr:  nil,\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"auto\",\n\t\t\t\topts: Client().SetServerMonitoringMode(ServerMonitoringModeAuto),\n\t\t\t\terr:  nil,\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"poll\",\n\t\t\t\topts: Client().SetServerMonitoringMode(ServerMonitoringModePoll),\n\t\t\t\terr:  nil,\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"stream\",\n\t\t\t\topts: Client().SetServerMonitoringMode(ServerMonitoringModeStream),\n\t\t\t\terr:  nil,\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"invalid\",\n\t\t\t\topts: Client().SetServerMonitoringMode(\"invalid\"),\n\t\t\t\terr:  errors.New(\"invalid server monitoring mode: \\\"invalid\\\"\"),\n\t\t\t},\n\t\t}\n\n\t\tfor _, tc := range testCases {\n\t\t\ttc := tc // Capture the range variable\n\n\t\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\t\tt.Parallel()\n\n\t\t\t\terr := tc.opts.Validate()\n\t\t\t\tassert.Equal(t, tc.err, err, \"expected error %v, got %v\", tc.err, err)\n\t\t\t})\n\t\t}\n\t})\n\tt.Run(\"OIDC auth configuration validation\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\temptyCb := func(_ context.Context, _ *OIDCArgs) (*OIDCCredential, error) {\n\t\t\treturn nil, nil\n\t\t}\n\n\t\ttestCases := []struct {\n\t\t\tname string\n\t\t\topts *ClientOptions\n\t\t\terr  error\n\t\t}{\n\t\t\t{\n\t\t\t\tname: \"password must not be set\",\n\t\t\t\topts: Client().SetAuth(Credential{AuthMechanism: \"MONGODB-OIDC\", Password: \"password\"}),\n\t\t\t\terr:  fmt.Errorf(\"password must not be set for the MONGODB-OIDC auth mechanism\"),\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"cannot set both OIDCMachineCallback and OIDCHumanCallback simultaneously\",\n\t\t\t\topts: Client().SetAuth(Credential{\n\t\t\t\t\tAuthMechanism:       \"MONGODB-OIDC\",\n\t\t\t\t\tOIDCMachineCallback: emptyCb, OIDCHumanCallback: emptyCb,\n\t\t\t\t}),\n\t\t\t\terr: fmt.Errorf(\"cannot set both OIDCMachineCallback and OIDCHumanCallback, only one may be specified\"),\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"cannot set ALLOWED_HOSTS without OIDCHumanCallback\",\n\t\t\t\topts: Client().SetAuth(Credential{\n\t\t\t\t\tAuthMechanism:           \"MONGODB-OIDC\",\n\t\t\t\t\tOIDCMachineCallback:     emptyCb,\n\t\t\t\t\tAuthMechanismProperties: map[string]string{\"ALLOWED_HOSTS\": \"www.example.com\"},\n\t\t\t\t}),\n\t\t\t\terr: fmt.Errorf(\"cannot specify ALLOWED_HOSTS without an OIDCHumanCallback\"),\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"cannot set OIDCMachineCallback in GCP Environment\",\n\t\t\t\topts: Client().SetAuth(Credential{\n\t\t\t\t\tAuthMechanism:           \"MONGODB-OIDC\",\n\t\t\t\t\tOIDCMachineCallback:     emptyCb,\n\t\t\t\t\tAuthMechanismProperties: map[string]string{\"ENVIRONMENT\": \"gcp\", \"TOKEN_RESOURCE\": \"stuff\"},\n\t\t\t\t}),\n\t\t\t\terr: fmt.Errorf(`OIDCMachineCallback cannot be specified with the gcp \"ENVIRONMENT\"`),\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"cannot set OIDCMachineCallback in AZURE Environment\",\n\t\t\t\topts: Client().SetAuth(Credential{\n\t\t\t\t\tAuthMechanism:           \"MONGODB-OIDC\",\n\t\t\t\t\tOIDCMachineCallback:     emptyCb,\n\t\t\t\t\tAuthMechanismProperties: map[string]string{\"ENVIRONMENT\": \"azure\", \"TOKEN_RESOURCE\": \"stuff\"},\n\t\t\t\t}),\n\t\t\t\terr: fmt.Errorf(`OIDCMachineCallback cannot be specified with the azure \"ENVIRONMENT\"`),\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"TOKEN_RESOURCE must be set in GCP Environment\",\n\t\t\t\topts: Client().SetAuth(Credential{\n\t\t\t\t\tAuthMechanism:           \"MONGODB-OIDC\",\n\t\t\t\t\tAuthMechanismProperties: map[string]string{\"ENVIRONMENT\": \"gcp\"},\n\t\t\t\t}),\n\t\t\t\terr: fmt.Errorf(`\"TOKEN_RESOURCE\" must be set for the gcp \"ENVIRONMENT\"`),\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"TOKEN_RESOURCE must be set in AZURE Environment\",\n\t\t\t\topts: Client().SetAuth(Credential{\n\t\t\t\t\tAuthMechanism:           \"MONGODB-OIDC\",\n\t\t\t\t\tAuthMechanismProperties: map[string]string{\"ENVIRONMENT\": \"azure\"},\n\t\t\t\t}),\n\t\t\t\terr: fmt.Errorf(`\"TOKEN_RESOURCE\" must be set for the azure \"ENVIRONMENT\"`),\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"TOKEN_RESOURCE must not be set in TEST Environment\",\n\t\t\t\topts: Client().SetAuth(Credential{\n\t\t\t\t\tAuthMechanism:           \"MONGODB-OIDC\",\n\t\t\t\t\tAuthMechanismProperties: map[string]string{\"ENVIRONMENT\": \"test\", \"TOKEN_RESOURCE\": \"stuff\"},\n\t\t\t\t}),\n\t\t\t\terr: fmt.Errorf(`\"TOKEN_RESOURCE\" must not be set for the test \"ENVIRONMENT\"`),\n\t\t\t},\n\t\t}\n\t\tfor _, tc := range testCases {\n\t\t\ttc := tc // Capture range variable.\n\n\t\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\t\tt.Parallel()\n\n\t\t\t\terr := tc.opts.Validate()\n\t\t\t\tassert.Equal(t, tc.err, err, \"want error %v, got error %v\", tc.err, err)\n\t\t\t})\n\t\t}\n\t})\n}\n\ntype nonDefaultTransport struct{}\n\nfunc (*nonDefaultTransport) RoundTrip(*http.Request) (*http.Response, error) { return nil, nil }\n\nfunc TestClientHTTPTransport(t *testing.T) {\n\tt.Run(\"Default client\", func(t *testing.T) {\n\t\tgot := Client().HTTPClient\n\t\tassert.Equal(t, http.DefaultClient, got)\n\t})\n\tt.Run(\"Non-default global transport\", func(t *testing.T) {\n\t\thttp.DefaultTransport = &nonDefaultTransport{}\n\t\tgot := Client().HTTPClient.Transport\n\t\tassert.Equal(t, &nonDefaultTransport{}, got)\n\t})\n}\n\nfunc createCertPool(t *testing.T, paths ...string) *x509.CertPool {\n\tt.Helper()\n\n\tpool := x509.NewCertPool()\n\tfor _, path := range paths {\n\t\tpool.AddCert(loadCert(t, path))\n\t}\n\treturn pool\n}\n\nfunc loadCert(t *testing.T, file string) *x509.Certificate {\n\tt.Helper()\n\n\tdata := readFile(t, file)\n\tblock, _ := pem.Decode(data)\n\tcert, err := x509.ParseCertificate(block.Bytes)\n\tassert.Nil(t, err, \"ParseCertificate error for %s: %v\", file, err)\n\treturn cert\n}\n\nfunc readFile(t *testing.T, path string) []byte {\n\tdata, err := os.ReadFile(path)\n\tassert.Nil(t, err, \"ReadFile error for %s: %v\", path, err)\n\treturn data\n}\n\ntype testDialer struct {\n\tNum int\n}\n\nfunc (testDialer) DialContext(context.Context, string, string) (net.Conn, error) {\n\treturn nil, nil\n}\n\nfunc compareTLSConfig(cfg1, cfg2 *tls.Config) bool {\n\tif cfg1 == nil && cfg2 == nil {\n\t\treturn true\n\t}\n\n\tif cfg1 == nil || cfg2 == nil {\n\t\treturn true\n\t}\n\n\tif (cfg1.RootCAs == nil && cfg1.RootCAs != nil) || (cfg1.RootCAs != nil && cfg1.RootCAs == nil) {\n\t\treturn false\n\t}\n\n\tif cfg1.RootCAs != nil {\n\t\tcfg1Subjects := cfg1.RootCAs.Subjects()\n\t\tcfg2Subjects := cfg2.RootCAs.Subjects()\n\t\tif len(cfg1Subjects) != len(cfg2Subjects) {\n\t\t\treturn false\n\t\t}\n\n\t\tfor idx, firstSubject := range cfg1Subjects {\n\t\t\tif !bytes.Equal(firstSubject, cfg2Subjects[idx]) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t}\n\t}\n\n\tif len(cfg1.Certificates) != len(cfg2.Certificates) {\n\t\treturn false\n\t}\n\n\tif cfg1.InsecureSkipVerify != cfg2.InsecureSkipVerify {\n\t\treturn false\n\t}\n\n\treturn true\n}\n\nfunc compareErrors(err1, err2 error) bool {\n\tif err1 == nil && err2 == nil {\n\t\treturn true\n\t}\n\n\tif err1 == nil || err2 == nil {\n\t\treturn false\n\t}\n\n\tvar ospe1, ospe2 *os.PathError\n\tif errors.As(err1, &ospe1) && errors.As(err2, &ospe2) {\n\t\treturn ospe1.Op == ospe2.Op && ospe1.Path == ospe2.Path\n\t}\n\n\tif err1.Error() != err2.Error() {\n\t\treturn false\n\t}\n\n\treturn true\n}\n\nfunc TestApplyURI(t *testing.T) {\n\tt.Parallel()\n\n\ttestCases := []struct {\n\t\tname     string\n\t\turi      string\n\t\twantopts *ClientOptions\n\t}{\n\t\t{\n\t\t\tname: \"ParseError\",\n\t\t\turi:  \"not-mongo-db-uri://\",\n\t\t\twantopts: &ClientOptions{\n\t\t\t\terr: fmt.Errorf(\n\t\t\t\t\t\"error parsing uri: %w\",\n\t\t\t\t\terrors.New(`scheme must be \"mongodb\" or \"mongodb+srv\"`)),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"ReadPreference Invalid Mode\",\n\t\t\turi:  \"mongodb://localhost/?maxStaleness=200\",\n\t\t\twantopts: &ClientOptions{\n\t\t\t\tHosts: []string{\"localhost\"},\n\t\t\t\terr:   fmt.Errorf(\"unknown read preference %v\", \"\"),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"ReadPreference Primary With Options\",\n\t\t\turi:  \"mongodb://localhost/?readPreference=Primary&maxStaleness=200\",\n\t\t\twantopts: &ClientOptions{\n\t\t\t\tHosts: []string{\"localhost\"},\n\t\t\t\terr:   errors.New(\"can not specify tags, max staleness, or hedge with mode primary\"),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"TLS addCertFromFile error\",\n\t\t\turi:  \"mongodb://localhost/?ssl=true&sslCertificateAuthorityFile=testdata/doesntexist\",\n\t\t\twantopts: &ClientOptions{\n\t\t\t\tHosts: []string{\"localhost\"},\n\t\t\t\terr:   &os.PathError{Op: \"open\", Path: \"testdata/doesntexist\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"TLS ClientCertificateKey\",\n\t\t\turi:  \"mongodb://localhost/?ssl=true&sslClientCertificateKeyFile=testdata/doesntexist\",\n\t\t\twantopts: &ClientOptions{\n\t\t\t\tHosts: []string{\"localhost\"},\n\t\t\t\terr:   &os.PathError{Op: \"open\", Path: \"testdata/doesntexist\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"AppName\",\n\t\t\turi:  \"mongodb://localhost/?appName=awesome-example-application\",\n\t\t\twantopts: &ClientOptions{\n\t\t\t\tHosts:   []string{\"localhost\"},\n\t\t\t\tAppName: ptrutil.Ptr[string](\"awesome-example-application\"),\n\t\t\t\terr:     nil,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"AuthMechanism\",\n\t\t\turi:  \"mongodb://localhost/?authMechanism=mongodb-x509\",\n\t\t\twantopts: &ClientOptions{\n\t\t\t\tHosts: []string{\"localhost\"},\n\t\t\t\tAuth:  &Credential{AuthSource: \"$external\", AuthMechanism: \"mongodb-x509\"},\n\t\t\t\terr:   nil,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"AuthMechanismProperties\",\n\t\t\turi:  \"mongodb://foo@localhost/?authMechanism=gssapi&authMechanismProperties=SERVICE_NAME:mongodb-fake\",\n\t\t\twantopts: &ClientOptions{\n\t\t\t\tHosts: []string{\"localhost\"},\n\t\t\t\tAuth: &Credential{\n\t\t\t\t\tAuthSource:              \"$external\",\n\t\t\t\t\tAuthMechanism:           \"gssapi\",\n\t\t\t\t\tAuthMechanismProperties: map[string]string{\"SERVICE_NAME\": \"mongodb-fake\"},\n\t\t\t\t\tUsername:                \"foo\",\n\t\t\t\t},\n\t\t\t\terr: nil,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"AuthSource\",\n\t\t\turi:  \"mongodb://foo@localhost/?authSource=random-database-example\",\n\t\t\twantopts: &ClientOptions{\n\t\t\t\tHosts: []string{\"localhost\"},\n\t\t\t\tAuth:  &Credential{AuthSource: \"random-database-example\", Username: \"foo\"},\n\t\t\t\terr:   nil,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"Username\",\n\t\t\turi:  \"mongodb://foo@localhost/\",\n\t\t\twantopts: &ClientOptions{\n\t\t\t\tHosts: []string{\"localhost\"},\n\t\t\t\tAuth:  &Credential{AuthSource: \"admin\", Username: \"foo\"},\n\t\t\t\terr:   nil,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"Unescaped slash in username\",\n\t\t\turi:  \"mongodb:///:pwd@localhost\",\n\t\t\twantopts: &ClientOptions{\n\t\t\t\terr: fmt.Errorf(\"error parsing uri: %w\", errors.New(\"unescaped slash in username\")),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"Password\",\n\t\t\turi:  \"mongodb://foo:bar@localhost/\",\n\t\t\twantopts: &ClientOptions{\n\t\t\t\tHosts: []string{\"localhost\"},\n\t\t\t\tAuth: &Credential{\n\t\t\t\t\tAuthSource: \"admin\", Username: \"foo\",\n\t\t\t\t\tPassword: \"bar\", PasswordSet: true,\n\t\t\t\t},\n\t\t\t\terr: nil,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"Single character username and password\",\n\t\t\turi:  \"mongodb://f:b@localhost/\",\n\t\t\twantopts: &ClientOptions{\n\t\t\t\tHosts: []string{\"localhost\"},\n\t\t\t\tAuth: &Credential{\n\t\t\t\t\tAuthSource: \"admin\", Username: \"f\",\n\t\t\t\t\tPassword: \"b\", PasswordSet: true,\n\t\t\t\t},\n\t\t\t\terr: nil,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"Connect\",\n\t\t\turi:  \"mongodb://localhost/?connect=direct\",\n\t\t\twantopts: &ClientOptions{\n\t\t\t\tHosts:  []string{\"localhost\"},\n\t\t\t\tDirect: ptrutil.Ptr[bool](true),\n\t\t\t\terr:    nil,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"ConnectTimeout\",\n\t\t\turi:  \"mongodb://localhost/?connectTimeoutms=5000\",\n\t\t\twantopts: &ClientOptions{\n\t\t\t\tHosts:          []string{\"localhost\"},\n\t\t\t\tConnectTimeout: ptrutil.Ptr[time.Duration](5 * time.Second),\n\t\t\t\terr:            nil,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"Compressors\",\n\t\t\turi:  \"mongodb://localhost/?compressors=zlib,snappy\",\n\t\t\twantopts: &ClientOptions{\n\t\t\t\tHosts:       []string{\"localhost\"},\n\t\t\t\tCompressors: []string{\"zlib\", \"snappy\"},\n\t\t\t\tZlibLevel:   ptrutil.Ptr[int](6),\n\t\t\t\terr:         nil,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"DatabaseNoAuth\",\n\t\t\turi:  \"mongodb://localhost/example-database\",\n\t\t\twantopts: &ClientOptions{\n\t\t\t\tHosts: []string{\"localhost\"},\n\t\t\t\terr:   nil,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"DatabaseAsDefault\",\n\t\t\turi:  \"mongodb://foo@localhost/example-database\",\n\t\t\twantopts: &ClientOptions{\n\t\t\t\tHosts: []string{\"localhost\"},\n\t\t\t\tAuth:  &Credential{AuthSource: \"example-database\", Username: \"foo\"},\n\t\t\t\terr:   nil,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"HeartbeatInterval\",\n\t\t\turi:  \"mongodb://localhost/?heartbeatIntervalms=12000\",\n\t\t\twantopts: &ClientOptions{\n\t\t\t\tHosts:             []string{\"localhost\"},\n\t\t\t\tHeartbeatInterval: ptrutil.Ptr[time.Duration](12 * time.Second),\n\t\t\t\terr:               nil,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"Hosts\",\n\t\t\turi:  \"mongodb://localhost:27017,localhost:27018,localhost:27019/\",\n\t\t\twantopts: &ClientOptions{\n\t\t\t\tHosts: []string{\"localhost:27017\", \"localhost:27018\", \"localhost:27019\"},\n\t\t\t\terr:   nil,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"LocalThreshold\",\n\t\t\turi:  \"mongodb://localhost/?localThresholdMS=200\",\n\t\t\twantopts: &ClientOptions{\n\t\t\t\tHosts:          []string{\"localhost\"},\n\t\t\t\tLocalThreshold: ptrutil.Ptr[time.Duration](200 * time.Millisecond),\n\t\t\t\terr:            nil,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"MaxConnIdleTime\",\n\t\t\turi:  \"mongodb://localhost/?maxIdleTimeMS=300000\",\n\t\t\twantopts: &ClientOptions{\n\t\t\t\tHosts:           []string{\"localhost\"},\n\t\t\t\tMaxConnIdleTime: ptrutil.Ptr[time.Duration](5 * time.Minute),\n\t\t\t\terr:             nil,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"MaxPoolSize\",\n\t\t\turi:  \"mongodb://localhost/?maxPoolSize=256\",\n\t\t\twantopts: &ClientOptions{\n\t\t\t\tHosts:       []string{\"localhost\"},\n\t\t\t\tMaxPoolSize: ptrutil.Ptr[uint64](256),\n\t\t\t\terr:         nil,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"MinPoolSize\",\n\t\t\turi:  \"mongodb://localhost/?minPoolSize=256\",\n\t\t\twantopts: &ClientOptions{\n\t\t\t\tHosts:       []string{\"localhost\"},\n\t\t\t\tMinPoolSize: ptrutil.Ptr[uint64](256),\n\t\t\t\terr:         nil,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"MaxConnecting\",\n\t\t\turi:  \"mongodb://localhost/?maxConnecting=10\",\n\t\t\twantopts: &ClientOptions{\n\t\t\t\tHosts:         []string{\"localhost\"},\n\t\t\t\tMaxConnecting: ptrutil.Ptr[uint64](10),\n\t\t\t\terr:           nil,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"ReadConcern\",\n\t\t\turi:  \"mongodb://localhost/?readConcernLevel=linearizable\",\n\t\t\twantopts: &ClientOptions{\n\t\t\t\tHosts:       []string{\"localhost\"},\n\t\t\t\tReadConcern: readconcern.Linearizable(),\n\t\t\t\terr:         nil,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"ReadPreference\",\n\t\t\turi:  \"mongodb://localhost/?readPreference=secondaryPreferred\",\n\t\t\twantopts: &ClientOptions{\n\t\t\t\tHosts:          []string{\"localhost\"},\n\t\t\t\tReadPreference: readpref.SecondaryPreferred(),\n\t\t\t\terr:            nil,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"ReadPreferenceTagSets\",\n\t\t\turi:  \"mongodb://localhost/?readPreference=secondaryPreferred&readPreferenceTags=foo:bar\",\n\t\t\twantopts: &ClientOptions{\n\t\t\t\tHosts:          []string{\"localhost\"},\n\t\t\t\tReadPreference: readpref.SecondaryPreferred(readpref.WithTags(\"foo\", \"bar\")),\n\t\t\t\terr:            nil,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"MaxStaleness\",\n\t\t\turi:  \"mongodb://localhost/?readPreference=secondaryPreferred&maxStaleness=250\",\n\t\t\twantopts: &ClientOptions{\n\t\t\t\tHosts:          []string{\"localhost\"},\n\t\t\t\tReadPreference: readpref.SecondaryPreferred(readpref.WithMaxStaleness(250 * time.Second)),\n\t\t\t\terr:            nil,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"RetryWrites\",\n\t\t\turi:  \"mongodb://localhost/?retryWrites=true\",\n\t\t\twantopts: &ClientOptions{\n\t\t\t\tHosts:       []string{\"localhost\"},\n\t\t\t\tRetryWrites: ptrutil.Ptr[bool](true),\n\t\t\t\terr:         nil,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"ReplicaSet\",\n\t\t\turi:  \"mongodb://localhost/?replicaSet=rs01\",\n\t\t\twantopts: &ClientOptions{\n\t\t\t\tHosts:      []string{\"localhost\"},\n\t\t\t\tReplicaSet: ptrutil.Ptr[string](\"rs01\"),\n\t\t\t\terr:        nil,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"ServerSelectionTimeout\",\n\t\t\turi:  \"mongodb://localhost/?serverSelectionTimeoutMS=45000\",\n\t\t\twantopts: &ClientOptions{\n\t\t\t\tHosts:                  []string{\"localhost\"},\n\t\t\t\tServerSelectionTimeout: ptrutil.Ptr[time.Duration](45 * time.Second),\n\t\t\t\terr:                    nil,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"SocketTimeout\",\n\t\t\turi:  \"mongodb://localhost/?socketTimeoutMS=15000\",\n\t\t\twantopts: &ClientOptions{\n\t\t\t\tHosts: []string{\"localhost\"},\n\t\t\t\terr:   nil,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"TLS CACertificate\",\n\t\t\turi:  \"mongodb://localhost/?ssl=true&sslCertificateAuthorityFile=testdata/ca.pem\",\n\t\t\twantopts: &ClientOptions{\n\t\t\t\tHosts: []string{\"localhost\"},\n\t\t\t\tTLSConfig: &tls.Config{\n\t\t\t\t\tRootCAs: createCertPool(t, \"testdata/ca.pem\"),\n\t\t\t\t},\n\t\t\t\terr: nil,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"TLS Insecure\",\n\t\t\turi:  \"mongodb://localhost/?ssl=true&sslInsecure=true\",\n\t\t\twantopts: &ClientOptions{\n\t\t\t\tHosts: []string{\"localhost\"},\n\t\t\t\tTLSConfig: &tls.Config{\n\t\t\t\t\tInsecureSkipVerify: true,\n\t\t\t\t},\n\t\t\t\terr: nil,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"TLS ClientCertificateKey\",\n\t\t\turi:  \"mongodb://localhost/?ssl=true&sslClientCertificateKeyFile=testdata/nopass/certificate.pem\",\n\t\t\twantopts: &ClientOptions{\n\t\t\t\tHosts: []string{\"localhost\"},\n\t\t\t\tTLSConfig: &tls.Config{\n\t\t\t\t\tCertificates: make([]tls.Certificate, 1),\n\t\t\t\t},\n\t\t\t\terr: nil,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"TLS ClientCertificateKey with password\",\n\t\t\turi:  \"mongodb://localhost/?ssl=true&sslClientCertificateKeyFile=testdata/certificate.pem&sslClientCertificateKeyPassword=passphrase\",\n\t\t\twantopts: &ClientOptions{\n\t\t\t\tHosts: []string{\"localhost\"},\n\t\t\t\tTLSConfig: &tls.Config{\n\t\t\t\t\tCertificates: make([]tls.Certificate, 1),\n\t\t\t\t},\n\t\t\t\terr: nil,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"TLS Username\",\n\t\t\turi:  \"mongodb://localhost/?ssl=true&authMechanism=mongodb-x509&sslClientCertificateKeyFile=testdata/nopass/certificate.pem\",\n\t\t\twantopts: &ClientOptions{\n\t\t\t\tHosts: []string{\"localhost\"},\n\t\t\t\tAuth: &Credential{\n\t\t\t\t\tAuthMechanism: \"mongodb-x509\", AuthSource: \"$external\",\n\t\t\t\t\tUsername: `C=US,ST=New York,L=New York City, Inc,O=MongoDB\\,OU=WWW`,\n\t\t\t\t},\n\t\t\t\terr: nil,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"WriteConcern J\",\n\t\t\turi:  \"mongodb://localhost/?journal=true\",\n\t\t\twantopts: &ClientOptions{\n\t\t\t\tHosts:        []string{\"localhost\"},\n\t\t\t\tWriteConcern: writeconcern.Journaled(),\n\t\t\t\terr:          nil,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"WriteConcern WString\",\n\t\t\turi:  \"mongodb://localhost/?w=majority\",\n\t\t\twantopts: &ClientOptions{\n\t\t\t\tHosts:        []string{\"localhost\"},\n\t\t\t\tWriteConcern: writeconcern.Majority(),\n\t\t\t\terr:          nil,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"WriteConcern W\",\n\t\t\turi:  \"mongodb://localhost/?w=3\",\n\t\t\twantopts: &ClientOptions{\n\t\t\t\tHosts:        []string{\"localhost\"},\n\t\t\t\tWriteConcern: &writeconcern.WriteConcern{W: 3},\n\t\t\t\terr:          nil,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"WriteConcern WTimeout\",\n\t\t\turi:  \"mongodb://localhost/?wTimeoutMS=45000\",\n\t\t\twantopts: &ClientOptions{\n\t\t\t\tHosts: []string{\"localhost\"},\n\t\t\t\terr:   nil,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"ZLibLevel\",\n\t\t\turi:  \"mongodb://localhost/?zlibCompressionLevel=4\",\n\t\t\twantopts: &ClientOptions{\n\t\t\t\tHosts:     []string{\"localhost\"},\n\t\t\t\tZlibLevel: ptrutil.Ptr[int](4),\n\t\t\t\terr:       nil,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"TLS tlsCertificateFile and tlsPrivateKeyFile\",\n\t\t\turi:  \"mongodb://localhost/?tlsCertificateFile=testdata/nopass/cert.pem&tlsPrivateKeyFile=testdata/nopass/key.pem\",\n\t\t\twantopts: &ClientOptions{\n\t\t\t\tHosts: []string{\"localhost\"},\n\t\t\t\tTLSConfig: &tls.Config{\n\t\t\t\t\tCertificates: make([]tls.Certificate, 1),\n\t\t\t\t},\n\t\t\t\terr: nil,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"TLS only tlsCertificateFile\",\n\t\t\turi:  \"mongodb://localhost/?tlsCertificateFile=testdata/nopass/cert.pem\",\n\t\t\twantopts: &ClientOptions{\n\t\t\t\terr: fmt.Errorf(\n\t\t\t\t\t\"error validating uri: %w\",\n\t\t\t\t\terrors.New(\"the tlsPrivateKeyFile URI option must be provided if the tlsCertificateFile option is specified\")),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"TLS only tlsPrivateKeyFile\",\n\t\t\turi:  \"mongodb://localhost/?tlsPrivateKeyFile=testdata/nopass/key.pem\",\n\t\t\twantopts: &ClientOptions{\n\t\t\t\terr: fmt.Errorf(\n\t\t\t\t\t\"error validating uri: %w\",\n\t\t\t\t\terrors.New(\"the tlsCertificateFile URI option must be provided if the tlsPrivateKeyFile option is specified\")),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"TLS tlsCertificateFile and tlsPrivateKeyFile and tlsCertificateKeyFile\",\n\t\t\turi:  \"mongodb://localhost/?tlsCertificateFile=testdata/nopass/cert.pem&tlsPrivateKeyFile=testdata/nopass/key.pem&tlsCertificateKeyFile=testdata/nopass/certificate.pem\",\n\t\t\twantopts: &ClientOptions{\n\t\t\t\terr: fmt.Errorf(\n\t\t\t\t\t\"error validating uri: %w\",\n\t\t\t\t\terrors.New(\"the sslClientCertificateKeyFile/tlsCertificateKeyFile URI option cannot be provided \"+\n\t\t\t\t\t\t\"along with tlsCertificateFile or tlsPrivateKeyFile\")),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"disable OCSP endpoint check\",\n\t\t\turi:  \"mongodb://localhost/?tlsDisableOCSPEndpointCheck=true\",\n\t\t\twantopts: &ClientOptions{\n\t\t\t\tHosts:                    []string{\"localhost\"},\n\t\t\t\tDisableOCSPEndpointCheck: ptrutil.Ptr[bool](true),\n\t\t\t\terr:                      nil,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"directConnection\",\n\t\t\turi:  \"mongodb://localhost/?directConnection=true\",\n\t\t\twantopts: &ClientOptions{\n\t\t\t\tHosts:  []string{\"localhost\"},\n\t\t\t\tDirect: ptrutil.Ptr[bool](true),\n\t\t\t\terr:    nil,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"TLS CA file with multiple certificiates\",\n\t\t\turi:  \"mongodb://localhost/?tlsCAFile=testdata/ca-with-intermediates.pem\",\n\t\t\twantopts: &ClientOptions{\n\t\t\t\tHosts: []string{\"localhost\"},\n\t\t\t\tTLSConfig: &tls.Config{\n\t\t\t\t\tRootCAs: createCertPool(t, \"testdata/ca-with-intermediates-first.pem\",\n\t\t\t\t\t\t\"testdata/ca-with-intermediates-second.pem\", \"testdata/ca-with-intermediates-third.pem\"),\n\t\t\t\t},\n\t\t\t\terr: nil,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"TLS empty CA file\",\n\t\t\turi:  \"mongodb://localhost/?tlsCAFile=testdata/empty-ca.pem\",\n\t\t\twantopts: &ClientOptions{\n\t\t\t\tHosts: []string{\"localhost\"},\n\t\t\t\terr:   errors.New(\"the specified CA file does not contain any valid certificates\"),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"TLS CA file with no certificates\",\n\t\t\turi:  \"mongodb://localhost/?tlsCAFile=testdata/ca-key.pem\",\n\t\t\twantopts: &ClientOptions{\n\t\t\t\tHosts: []string{\"localhost\"},\n\t\t\t\terr:   errors.New(\"the specified CA file does not contain any valid certificates\"),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"TLS malformed CA file\",\n\t\t\turi:  \"mongodb://localhost/?tlsCAFile=testdata/malformed-ca.pem\",\n\t\t\twantopts: &ClientOptions{\n\t\t\t\tHosts: []string{\"localhost\"},\n\t\t\t\terr:   errors.New(\"the specified CA file does not contain any valid certificates\"),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"loadBalanced=true\",\n\t\t\turi:  \"mongodb://localhost/?loadBalanced=true\",\n\t\t\twantopts: &ClientOptions{\n\t\t\t\tHosts:        []string{\"localhost\"},\n\t\t\t\tLoadBalanced: ptrutil.Ptr[bool](true),\n\t\t\t\terr:          nil,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"loadBalanced=false\",\n\t\t\turi:  \"mongodb://localhost/?loadBalanced=false\",\n\t\t\twantopts: &ClientOptions{\n\t\t\t\tHosts:        []string{\"localhost\"},\n\t\t\t\tLoadBalanced: ptrutil.Ptr[bool](false),\n\t\t\t\terr:          nil,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"srvServiceName\",\n\t\t\turi:  \"mongodb+srv://test22.test.build.10gen.cc/?srvServiceName=customname\",\n\t\t\twantopts: &ClientOptions{\n\t\t\t\tHosts:          []string{\"localhost.test.build.10gen.cc:27017\", \"localhost.test.build.10gen.cc:27018\"},\n\t\t\t\tSRVServiceName: ptrutil.Ptr[string](\"customname\"),\n\t\t\t\terr:            nil,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"srvMaxHosts\",\n\t\t\turi:  \"mongodb+srv://test1.test.build.10gen.cc/?srvMaxHosts=2\",\n\t\t\twantopts: &ClientOptions{\n\t\t\t\tHosts:       []string{\"localhost.test.build.10gen.cc:27017\", \"localhost.test.build.10gen.cc:27018\"},\n\t\t\t\tSRVMaxHosts: ptrutil.Ptr[int](2),\n\t\t\t\terr:         nil,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"GODRIVER-2263 regression test\",\n\t\t\turi:  \"mongodb://localhost/?tlsCertificateKeyFile=testdata/one-pk-multiple-certs.pem\",\n\t\t\twantopts: &ClientOptions{\n\t\t\t\tHosts:     []string{\"localhost\"},\n\t\t\t\tTLSConfig: &tls.Config{Certificates: make([]tls.Certificate, 1)},\n\t\t\t\terr:       nil,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"GODRIVER-2650 X509 certificate\",\n\t\t\turi:  \"mongodb://localhost/?ssl=true&authMechanism=mongodb-x509&sslClientCertificateKeyFile=testdata/one-pk-multiple-certs.pem\",\n\t\t\twantopts: &ClientOptions{\n\t\t\t\tHosts: []string{\"localhost\"},\n\t\t\t\tAuth: &Credential{\n\t\t\t\t\tAuthMechanism: \"mongodb-x509\", AuthSource: \"$external\",\n\t\t\t\t\t// Subject name in the first certificate is used as the username for X509 auth.\n\t\t\t\t\tUsername: `C=US,ST=New York,L=New York City,O=MongoDB,OU=Drivers,CN=localhost`,\n\t\t\t\t},\n\t\t\t\tTLSConfig: &tls.Config{Certificates: make([]tls.Certificate, 1)},\n\t\t\t\terr:       nil,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"ALLOWED_HOSTS cannot be specified in URI connection\",\n\t\t\turi:  \"mongodb://localhost/?authMechanism=MONGODB-OIDC&authMechanismProperties=ALLOWED_HOSTS:example.com\",\n\t\t\twantopts: &ClientOptions{\n\t\t\t\tHTTPClient: httputil.DefaultHTTPClient,\n\t\t\t\terr:        errors.New(`error validating uri: ALLOWED_HOSTS cannot be specified in the URI connection string for the \"MONGODB-OIDC\" auth mechanism, it must be specified through the ClientOptions directly`),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"colon in TOKEN_RESOURCE works as expected\",\n\t\t\turi:  \"mongodb://example.com/?authMechanism=MONGODB-OIDC&authMechanismProperties=TOKEN_RESOURCE:mongodb://test-cluster\",\n\t\t\twantopts: &ClientOptions{\n\t\t\t\tHosts:      []string{\"example.com\"},\n\t\t\t\tAuth:       &Credential{AuthMechanism: \"MONGODB-OIDC\", AuthSource: \"$external\", AuthMechanismProperties: map[string]string{\"TOKEN_RESOURCE\": \"mongodb://test-cluster\"}},\n\t\t\t\tHTTPClient: httputil.DefaultHTTPClient,\n\t\t\t\terr:        nil,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"oidc azure\",\n\t\t\turi:  \"mongodb://example.com/?authMechanism=MONGODB-OIDC&authMechanismProperties=TOKEN_RESOURCE:mongodb://test-cluster,ENVIRONMENT:azureManagedIdentities\",\n\t\t\twantopts: &ClientOptions{\n\t\t\t\tHosts: []string{\"example.com\"},\n\t\t\t\tAuth: &Credential{AuthMechanism: \"MONGODB-OIDC\", AuthSource: \"$external\", AuthMechanismProperties: map[string]string{\n\t\t\t\t\t\"ENVIRONMENT\":    \"azureManagedIdentities\",\n\t\t\t\t\t\"TOKEN_RESOURCE\": \"mongodb://test-cluster\",\n\t\t\t\t}},\n\t\t\t\tHTTPClient: httputil.DefaultHTTPClient,\n\t\t\t\terr:        nil,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"oidc gcp\",\n\t\t\turi:  \"mongodb://test.mongodb.net/?authMechanism=MONGODB-OIDC&authMechanismProperties=ENVIRONMENT:gcp,TOKEN_RESOURCE:mongodb://test-cluster\",\n\t\t\twantopts: &ClientOptions{\n\t\t\t\tHosts: []string{\"test.mongodb.net\"},\n\t\t\t\tAuth: &Credential{AuthMechanism: \"MONGODB-OIDC\", AuthSource: \"$external\", AuthMechanismProperties: map[string]string{\n\t\t\t\t\t\"ENVIRONMENT\":    \"gcp\",\n\t\t\t\t\t\"TOKEN_RESOURCE\": \"mongodb://test-cluster\",\n\t\t\t\t}},\n\t\t\t\tHTTPClient: httputil.DefaultHTTPClient,\n\t\t\t\terr:        nil,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"comma in key:value pair causes error\",\n\t\t\turi:  \"mongodb://example.com/?authMechanismProperties=TOKEN_RESOURCE:mongodb://host1%2Chost2\",\n\t\t\twantopts: &ClientOptions{\n\t\t\t\tHTTPClient: httputil.DefaultHTTPClient,\n\t\t\t\terr:        errors.New(`error parsing uri: invalid authMechanism property`),\n\t\t\t},\n\t\t},\n\t}\n\n\tfor _, test := range testCases {\n\t\tt.Run(test.name, func(t *testing.T) {\n\t\t\tresult := Client().ApplyURI(test.uri)\n\n\t\t\t// Manually add the URI and ConnString to the test expectations to avoid adding them in each test\n\t\t\t// definition. The ConnString should only be recorded if there was no error while parsing.\n\t\t\tcs, err := connstring.ParseAndValidate(test.uri)\n\t\t\tif err == nil {\n\t\t\t\ttest.wantopts.connString = cs\n\t\t\t}\n\n\t\t\tif test.wantopts.HTTPClient == nil {\n\t\t\t\ttest.wantopts.HTTPClient = httputil.DefaultHTTPClient\n\t\t\t}\n\n\t\t\t// We have to sort string slices in comparison, as Hosts resolved from SRV URIs do not have a set order.\n\t\t\tstringLess := func(a, b string) bool { return a < b }\n\t\t\tif diff := cmp.Diff(\n\t\t\t\ttest.wantopts, result,\n\t\t\t\tcmp.AllowUnexported(ClientOptions{}, readconcern.ReadConcern{}, writeconcern.WriteConcern{}, readpref.ReadPref{}),\n\t\t\t\tcmp.Comparer(func(r1, r2 *bson.Registry) bool { return r1 == r2 }),\n\t\t\t\tcmp.Comparer(compareTLSConfig),\n\t\t\t\tcmp.Comparer(compareErrors),\n\t\t\t\tcmp.Comparer(optionsutil.Equal),\n\t\t\t\tcmpopts.SortSlices(stringLess),\n\t\t\t\tcmpopts.IgnoreFields(connstring.ConnString{}, \"SSLClientCertificateKeyPassword\"),\n\t\t\t\tcmpopts.IgnoreFields(http.Client{}, \"Transport\"),\n\t\t\t); diff != \"\" {\n\t\t\t\tt.Errorf(\"URI did not apply correctly: (-want +got)\\n%s\", diff)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "mongo/options/collectionoptions.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage options\n\nimport (\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/readconcern\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/readpref\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/writeconcern\"\n)\n\n// CollectionOptions represents arguments that can be used to configure a Collection.\n//\n// See corresponding setter methods for documentation.\ntype CollectionOptions struct {\n\tReadConcern    *readconcern.ReadConcern\n\tWriteConcern   *writeconcern.WriteConcern\n\tReadPreference *readpref.ReadPref\n\tBSONOptions    *BSONOptions\n\tRegistry       *bson.Registry\n}\n\n// CollectionOptionsBuilder contains options to configure a Collection instance.\n// Each option can be set through setter functions. See documentation for each\n// setter function for an explanation of the option.\ntype CollectionOptionsBuilder struct {\n\tOpts []func(*CollectionOptions) error\n}\n\n// Collection creates a new CollectionOptions instance.\nfunc Collection() *CollectionOptionsBuilder {\n\treturn &CollectionOptionsBuilder{}\n}\n\n// List returns a list of CollectionOptions setter functions.\nfunc (c *CollectionOptionsBuilder) List() []func(*CollectionOptions) error {\n\treturn c.Opts\n}\n\n// SetReadConcern sets the value for the ReadConcern field. ReadConcern is the read concern to use for\n// operations executed on the Collection. The default value is nil, which means that the read concern\n// of the Database used to configure the Collection will be used.\nfunc (c *CollectionOptionsBuilder) SetReadConcern(rc *readconcern.ReadConcern) *CollectionOptionsBuilder {\n\tc.Opts = append(c.Opts, func(opts *CollectionOptions) error {\n\t\topts.ReadConcern = rc\n\n\t\treturn nil\n\t})\n\n\treturn c\n}\n\n// SetWriteConcern sets the value for the WriteConcern field. WriteConcern is the write concern to\n// use for operations executed on the Collection. The default value is nil, which means that the write\n// concern of the Database used to configure the Collection will be used.\nfunc (c *CollectionOptionsBuilder) SetWriteConcern(wc *writeconcern.WriteConcern) *CollectionOptionsBuilder {\n\tc.Opts = append(c.Opts, func(opts *CollectionOptions) error {\n\t\topts.WriteConcern = wc\n\n\t\treturn nil\n\t})\n\n\treturn c\n}\n\n// SetReadPreference sets the value for the ReadPreference field. ReadPreference is the read preference\n// to use for operations executed on the Collection. The default value is nil, which means that the\n// read preference of the Database used to configure the Collection will be used.\nfunc (c *CollectionOptionsBuilder) SetReadPreference(rp *readpref.ReadPref) *CollectionOptionsBuilder {\n\tc.Opts = append(c.Opts, func(opts *CollectionOptions) error {\n\t\topts.ReadPreference = rp\n\n\t\treturn nil\n\t})\n\n\treturn c\n}\n\n// SetBSONOptions configures optional BSON marshaling and unmarshaling behavior. BSONOptions configures\n// optional BSON marshaling and unmarshaling behavior.\nfunc (c *CollectionOptionsBuilder) SetBSONOptions(bopts *BSONOptions) *CollectionOptionsBuilder {\n\tc.Opts = append(c.Opts, func(opts *CollectionOptions) error {\n\t\topts.BSONOptions = bopts\n\n\t\treturn nil\n\t})\n\n\treturn c\n}\n\n// SetRegistry sets the value for the Registry field. Registry is the BSON registry to marshal and\n// unmarshal documents for operations executed on the Collection. The default value is nil, which\n// means that the registry of the Database used to configure the Collection will be used.\nfunc (c *CollectionOptionsBuilder) SetRegistry(r *bson.Registry) *CollectionOptionsBuilder {\n\tc.Opts = append(c.Opts, func(opts *CollectionOptions) error {\n\t\topts.Registry = r\n\n\t\treturn nil\n\t})\n\treturn c\n}\n"
  },
  {
    "path": "mongo/options/countoptions.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage options\n\nimport \"go.mongodb.org/mongo-driver/v2/internal/optionsutil\"\n\n// CountOptions represents arguments that can be used to configure a\n// CountDocuments operation.\n//\n// See corresponding setter methods for documentation.\ntype CountOptions struct {\n\tCollation *Collation\n\tComment   any\n\tHint      any\n\tLimit     *int64\n\tSkip      *int64\n\n\t// Deprecated: This option is for internal use only and should not be set. It may be changed or removed in any\n\t// release.\n\tInternal optionsutil.Options\n}\n\n// CountOptionsBuilder contains options to configure count operations. Each\n// option can be set through setter functions. See documentation for each setter\n// function for an explanation of the option.\ntype CountOptionsBuilder struct {\n\tOpts []func(*CountOptions) error\n}\n\n// Count creates a new CountOptions instance.\nfunc Count() *CountOptionsBuilder {\n\treturn &CountOptionsBuilder{}\n}\n\n// List returns a list of CountOptions setter functions.\nfunc (co *CountOptionsBuilder) List() []func(*CountOptions) error {\n\treturn co.Opts\n}\n\n// SetCollation sets the value for the Collation field. Specifies a collation to\n// use for string comparisons during the operation. The default value is nil,\n// which means the default collation of the collection will be used.\nfunc (co *CountOptionsBuilder) SetCollation(c *Collation) *CountOptionsBuilder {\n\tco.Opts = append(co.Opts, func(opts *CountOptions) error {\n\t\topts.Collation = c\n\n\t\treturn nil\n\t})\n\n\treturn co\n}\n\n// SetComment sets the value for the Comment field. Specifies a string or document that will be included\n// in server logs, profiling logs, and currentOp queries to help trace the operation. The default is nil,\n// which means that no comment will be included in the logs.\nfunc (co *CountOptionsBuilder) SetComment(comment any) *CountOptionsBuilder {\n\tco.Opts = append(co.Opts, func(opts *CountOptions) error {\n\t\topts.Comment = comment\n\n\t\treturn nil\n\t})\n\n\treturn co\n}\n\n// SetHint sets the value for the Hint field. Specifies the index to use for the aggregation. This should\n// either be the index name as a string or the index specification as a document. The driver will return\n// an error if the hint parameter is a multi-key map. The default value is nil, which means that no hint\n// will be sent.\nfunc (co *CountOptionsBuilder) SetHint(h any) *CountOptionsBuilder {\n\tco.Opts = append(co.Opts, func(opts *CountOptions) error {\n\t\topts.Hint = h\n\n\t\treturn nil\n\t})\n\n\treturn co\n}\n\n// SetLimit sets the value for the Limit field. Specifies the maximum number of documents to count. The\n// default value is 0, which means that there is no limit and all documents matching the filter will be\n// counted.\nfunc (co *CountOptionsBuilder) SetLimit(i int64) *CountOptionsBuilder {\n\tco.Opts = append(co.Opts, func(opts *CountOptions) error {\n\t\topts.Limit = &i\n\n\t\treturn nil\n\t})\n\n\treturn co\n}\n\n// SetSkip sets the value for the Skip field. Specifies the number of documents to skip before counting.\n// The default value is 0.\nfunc (co *CountOptionsBuilder) SetSkip(i int64) *CountOptionsBuilder {\n\tco.Opts = append(co.Opts, func(opts *CountOptions) error {\n\t\topts.Skip = &i\n\n\t\treturn nil\n\t})\n\n\treturn co\n}\n"
  },
  {
    "path": "mongo/options/createcollectionoptions.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage options\n\nimport (\n\t\"time\"\n)\n\n// DefaultIndexOptions represents the default arguments for a collection to\n// apply on new indexes. This type can be used when creating a new collection\n// through the CreateCollectionOptions.SetDefaultIndexOptions method.\n//\n// See corresponding setter methods for documentation.\ntype DefaultIndexOptions struct {\n\tStorageEngine any\n}\n\n// DefaultIndexOptionsBuilder contains options to configure default index\n// operations. Each option can be set through setter functions. See\n// documentation for each setter function for an explanation of the option.\ntype DefaultIndexOptionsBuilder struct {\n\tOpts []func(*DefaultIndexOptions) error\n}\n\n// DefaultIndex creates a new DefaultIndexOptions instance.\nfunc DefaultIndex() *DefaultIndexOptionsBuilder {\n\treturn &DefaultIndexOptionsBuilder{}\n}\n\n// List returns a list of DefaultIndexOptions setter functions.\nfunc (d *DefaultIndexOptionsBuilder) List() []func(*DefaultIndexOptions) error {\n\treturn d.Opts\n}\n\n// SetStorageEngine sets the value for the StorageEngine field. Specifies the storage engine to use for\n// the index. The value must be a document in the form {<storage engine name>: <options>}. The default\n// value is nil, which means that the default storage engine will be used.\nfunc (d *DefaultIndexOptionsBuilder) SetStorageEngine(storageEngine any) *DefaultIndexOptionsBuilder {\n\td.Opts = append(d.Opts, func(opts *DefaultIndexOptions) error {\n\t\topts.StorageEngine = storageEngine\n\n\t\treturn nil\n\t})\n\n\treturn d\n}\n\n// TimeSeriesOptions specifies arguments on a time-series collection.\n//\n// See corresponding setter methods for documentation.\ntype TimeSeriesOptions struct {\n\tTimeField      string\n\tMetaField      *string\n\tGranularity    *string\n\tBucketMaxSpan  *time.Duration\n\tBucketRounding *time.Duration\n}\n\n// TimeSeriesOptionsBuilder contains options to configure timeseries operations.\n// Each option can be set through setter functions. See documentation for each\n// setter function for an explanation of the option.\ntype TimeSeriesOptionsBuilder struct {\n\tOpts []func(*TimeSeriesOptions) error\n}\n\n// TimeSeries creates a new TimeSeriesOptions instance.\nfunc TimeSeries() *TimeSeriesOptionsBuilder {\n\treturn &TimeSeriesOptionsBuilder{}\n}\n\n// List returns a list of TimeSeriesOptions setter functions.\nfunc (tso *TimeSeriesOptionsBuilder) List() []func(*TimeSeriesOptions) error {\n\treturn tso.Opts\n}\n\n// SetTimeField sets the value for the TimeField. TimeField is the top-level field to be used\n// for time. Inserted documents must have this field, and the field must be of the BSON UTC\n// datetime type (0x9).\nfunc (tso *TimeSeriesOptionsBuilder) SetTimeField(timeField string) *TimeSeriesOptionsBuilder {\n\ttso.Opts = append(tso.Opts, func(opts *TimeSeriesOptions) error {\n\t\topts.TimeField = timeField\n\n\t\treturn nil\n\t})\n\n\treturn tso\n}\n\n// SetMetaField sets the value for the MetaField. MetaField is the name of the top-level field\n// describing the series. This field is used to group related data and may be of any BSON type,\n// except for array. This name may not be the same as the TimeField or _id. This field is optional.\nfunc (tso *TimeSeriesOptionsBuilder) SetMetaField(metaField string) *TimeSeriesOptionsBuilder {\n\ttso.Opts = append(tso.Opts, func(opts *TimeSeriesOptions) error {\n\t\topts.MetaField = &metaField\n\n\t\treturn nil\n\t})\n\n\treturn tso\n}\n\n// SetGranularity sets the value for Granularity. Granularity is the granularity of time-series data.\n// Allowed granularity options are \"seconds\", \"minutes\" and \"hours\". This field is optional.\nfunc (tso *TimeSeriesOptionsBuilder) SetGranularity(granularity string) *TimeSeriesOptionsBuilder {\n\ttso.Opts = append(tso.Opts, func(opts *TimeSeriesOptions) error {\n\t\topts.Granularity = &granularity\n\n\t\treturn nil\n\t})\n\n\treturn tso\n}\n\n// SetBucketMaxSpan sets the value for BucketMaxSpan. BucketMaxSpan is the maximum range of time\n// values for a bucket. The time.Duration is rounded down to the nearest second and applied as\n// the command option: \"bucketRoundingSeconds\". This field is optional.\nfunc (tso *TimeSeriesOptionsBuilder) SetBucketMaxSpan(dur time.Duration) *TimeSeriesOptionsBuilder {\n\ttso.Opts = append(tso.Opts, func(opts *TimeSeriesOptions) error {\n\t\topts.BucketMaxSpan = &dur\n\n\t\treturn nil\n\t})\n\n\treturn tso\n}\n\n// SetBucketRounding sets the value for BucketRounding. BucketRounding is used to determine the\n// minimum time boundary when opening a new bucket by rounding the first timestamp down to the next\n// multiple of this value. The time.Duration is rounded down to the nearest second and applied as\n// the command option: \"bucketRoundingSeconds\". This field is optional.\nfunc (tso *TimeSeriesOptionsBuilder) SetBucketRounding(dur time.Duration) *TimeSeriesOptionsBuilder {\n\ttso.Opts = append(tso.Opts, func(opts *TimeSeriesOptions) error {\n\t\topts.BucketRounding = &dur\n\n\t\treturn nil\n\t})\n\n\treturn tso\n}\n\n// CreateCollectionOptions represents arguments that can be used to configure a\n// CreateCollection operation.\n//\n// See corresponding setter methods for documentation.\ntype CreateCollectionOptions struct {\n\tCapped                       *bool\n\tCollation                    *Collation\n\tChangeStreamPreAndPostImages any\n\tDefaultIndexOptions          *DefaultIndexOptionsBuilder\n\tMaxDocuments                 *int64\n\tSizeInBytes                  *int64\n\tStorageEngine                any\n\tValidationAction             *string\n\tValidationLevel              *string\n\tValidator                    any\n\tExpireAfterSeconds           *int64\n\tTimeSeriesOptions            *TimeSeriesOptionsBuilder\n\tEncryptedFields              any\n\tClusteredIndex               any\n}\n\n// CreateCollectionOptionsBuilder contains options to configure a new\n// collection. Each option can be set through setter functions. See\n// documentation for each setter function for an explanation of the option.\ntype CreateCollectionOptionsBuilder struct {\n\tOpts []func(*CreateCollectionOptions) error\n}\n\n// CreateCollection creates a new CreateCollectionOptions instance.\nfunc CreateCollection() *CreateCollectionOptionsBuilder {\n\treturn &CreateCollectionOptionsBuilder{}\n}\n\n// List returns a list of CreateCollectionOptions setter functions.\nfunc (c *CreateCollectionOptionsBuilder) List() []func(*CreateCollectionOptions) error {\n\treturn c.Opts\n}\n\n// SetCapped sets the value for the Capped field. Specifies if the collection is capped\n// (see https://www.mongodb.com/docs/manual/core/capped-collections/). If true, the SizeInBytes\n// option must also be specified. The default value is false.\nfunc (c *CreateCollectionOptionsBuilder) SetCapped(capped bool) *CreateCollectionOptionsBuilder {\n\tc.Opts = append(c.Opts, func(opts *CreateCollectionOptions) error {\n\t\topts.Capped = &capped\n\n\t\treturn nil\n\t})\n\n\treturn c\n}\n\n// SetCollation sets the value for the Collation field. Specifies the default\n// collation for the new collection. The default value is nil.\nfunc (c *CreateCollectionOptionsBuilder) SetCollation(collation *Collation) *CreateCollectionOptionsBuilder {\n\tc.Opts = append(c.Opts, func(opts *CreateCollectionOptions) error {\n\t\topts.Collation = collation\n\n\t\treturn nil\n\t})\n\n\treturn c\n}\n\n// SetChangeStreamPreAndPostImages sets the value for the ChangeStreamPreAndPostImages field.\n// Specifies how change streams opened against the collection can return pre- and post-images of\n// updated documents. The value must be a document in the form {<option name>: <options>}. This\n// option is only valid for MongoDB versions >= 6.0. The default value is nil, which means that\n// change streams opened against the collection will not return pre- and post-images of updated\n// documents in any way.\nfunc (c *CreateCollectionOptionsBuilder) SetChangeStreamPreAndPostImages(csppi any) *CreateCollectionOptionsBuilder {\n\tc.Opts = append(c.Opts, func(opts *CreateCollectionOptions) error {\n\t\topts.ChangeStreamPreAndPostImages = &csppi\n\n\t\treturn nil\n\t})\n\n\treturn c\n}\n\n// SetDefaultIndexOptions sets the value for the DefaultIndexOptions field.\n// Specifies a default configuration for indexes on the collection. The default\n// value is nil, meaning indexes will be configured using server defaults.\nfunc (c *CreateCollectionOptionsBuilder) SetDefaultIndexOptions(iopts *DefaultIndexOptionsBuilder) *CreateCollectionOptionsBuilder {\n\tc.Opts = append(c.Opts, func(opts *CreateCollectionOptions) error {\n\t\topts.DefaultIndexOptions = iopts\n\n\t\treturn nil\n\t})\n\n\treturn c\n}\n\n// SetMaxDocuments sets the value for the MaxDocuments field. Specifies the maximum number of documents\n// allowed in a capped collection. The limit specified by the SizeInBytes option takes precedence over\n// this option. If a capped collection reaches its size limit, old documents will be removed, regardless\n// of the number of documents in the collection. The default value is 0, meaning the maximum number of\n// documents is unbounded.\nfunc (c *CreateCollectionOptionsBuilder) SetMaxDocuments(max int64) *CreateCollectionOptionsBuilder {\n\tc.Opts = append(c.Opts, func(opts *CreateCollectionOptions) error {\n\t\topts.MaxDocuments = &max\n\n\t\treturn nil\n\t})\n\n\treturn c\n}\n\n// SetSizeInBytes sets the value for the SizeInBytes field. Specifies the maximum size in bytes for a\n// capped collection. The default value is 0.\nfunc (c *CreateCollectionOptionsBuilder) SetSizeInBytes(size int64) *CreateCollectionOptionsBuilder {\n\tc.Opts = append(c.Opts, func(opts *CreateCollectionOptions) error {\n\t\topts.SizeInBytes = &size\n\n\t\treturn nil\n\t})\n\n\treturn c\n}\n\n// SetStorageEngine sets the value for the StorageEngine field. Specifies the storage engine to use for\n// the index. The value must be a document in the form {<storage engine name>: <options>}. The default\n// value is nil, which means that the default storage engine will be used.\nfunc (c *CreateCollectionOptionsBuilder) SetStorageEngine(storageEngine any) *CreateCollectionOptionsBuilder {\n\tc.Opts = append(c.Opts, func(opts *CreateCollectionOptions) error {\n\t\topts.StorageEngine = &storageEngine\n\n\t\treturn nil\n\t})\n\n\treturn c\n}\n\n// SetValidationAction sets the value for the ValidationAction field. Specifies what should happen if a\n// document being inserted does not pass validation. Valid values are \"error\" and \"warn\". See\n// https://www.mongodb.com/docs/manual/core/schema-validation/#accept-or-reject-invalid-documents for more\n// information. The default value is \"error\".\nfunc (c *CreateCollectionOptionsBuilder) SetValidationAction(action string) *CreateCollectionOptionsBuilder {\n\tc.Opts = append(c.Opts, func(opts *CreateCollectionOptions) error {\n\t\topts.ValidationAction = &action\n\n\t\treturn nil\n\t})\n\n\treturn c\n}\n\n// SetValidationLevel sets the value for the ValidationLevel field. Specifies how strictly the server applies\n// validation rules to existing documents in the collection during update operations. Valid values are \"off\",\n// \"strict\", and \"moderate\". See https://www.mongodb.com/docs/manual/core/schema-validation/#existing-documents\n// for more information. The default value is \"strict\".\nfunc (c *CreateCollectionOptionsBuilder) SetValidationLevel(level string) *CreateCollectionOptionsBuilder {\n\tc.Opts = append(c.Opts, func(opts *CreateCollectionOptions) error {\n\t\topts.ValidationLevel = &level\n\n\t\treturn nil\n\t})\n\n\treturn c\n}\n\n// SetValidator sets the value for the Validator field. Sets a document specifying validation rules for the\n// collection. See https://www.mongodb.com/docs/manual/core/schema-validation/ for more information about\n// schema validation. The default value is nil, meaning no validator will be used for the collection.\nfunc (c *CreateCollectionOptionsBuilder) SetValidator(validator any) *CreateCollectionOptionsBuilder {\n\tc.Opts = append(c.Opts, func(opts *CreateCollectionOptions) error {\n\t\topts.Validator = validator\n\n\t\treturn nil\n\t})\n\n\treturn c\n}\n\n// SetExpireAfterSeconds sets the value for the ExpireAfterSeconds field. Specifies value\n// indicating after how many seconds old time-series data should be deleted.\n// See https://www.mongodb.com/docs/manual/reference/command/create/ for supported options,\n// and https://www.mongodb.com/docs/manual/core/timeseries-collections/ for more information\n// on time-series collections.\n//\n// This option is only valid for MongoDB versions >= 5.0\nfunc (c *CreateCollectionOptionsBuilder) SetExpireAfterSeconds(eas int64) *CreateCollectionOptionsBuilder {\n\tc.Opts = append(c.Opts, func(opts *CreateCollectionOptions) error {\n\t\topts.ExpireAfterSeconds = &eas\n\n\t\treturn nil\n\t})\n\n\treturn c\n}\n\n// SetTimeSeriesOptions sets the options for time-series collections. Specifies options for specifying\n// a time-series collection. See https://www.mongodb.com/docs/manual/reference/command/create/ for\n// supported options, and https://www.mongodb.com/docs/manual/core/timeseries-collections/ for more\n// information on time-series collections.\n//\n// This option is only valid for MongoDB versions >= 5.0\nfunc (c *CreateCollectionOptionsBuilder) SetTimeSeriesOptions(timeSeriesOpts *TimeSeriesOptionsBuilder) *CreateCollectionOptionsBuilder {\n\tc.Opts = append(c.Opts, func(opts *CreateCollectionOptions) error {\n\t\topts.TimeSeriesOptions = timeSeriesOpts\n\n\t\treturn nil\n\t})\n\n\treturn c\n}\n\n// SetEncryptedFields sets the encrypted fields for encrypted collections.\n//\n// This option is only valid for MongoDB versions >= 6.0\nfunc (c *CreateCollectionOptionsBuilder) SetEncryptedFields(encryptedFields any) *CreateCollectionOptionsBuilder {\n\tc.Opts = append(c.Opts, func(opts *CreateCollectionOptions) error {\n\t\topts.EncryptedFields = encryptedFields\n\n\t\treturn nil\n\t})\n\n\treturn c\n}\n\n// SetClusteredIndex sets the value for the ClusteredIndex field which is used\n// to create a collection with a clustered index.\n//\n// This option is only valid for MongoDB versions >= 5.3\nfunc (c *CreateCollectionOptionsBuilder) SetClusteredIndex(clusteredIndex any) *CreateCollectionOptionsBuilder {\n\tc.Opts = append(c.Opts, func(opts *CreateCollectionOptions) error {\n\t\topts.ClusteredIndex = clusteredIndex\n\n\t\treturn nil\n\t})\n\n\treturn c\n}\n\n// CreateViewOptions represents arguments that can be used to configure a\n// CreateView operation.\n//\n// See corresponding setter methods for documentation.\ntype CreateViewOptions struct {\n\tCollation *Collation\n}\n\n// CreateViewOptionsBuilder contains options to configure a new view. Each\n// option can be set through setter functions. See documentation for each setter\n// function for an explanation of the option.\ntype CreateViewOptionsBuilder struct {\n\tOpts []func(*CreateViewOptions) error\n}\n\n// CreateView creates an new CreateViewOptions instance.\nfunc CreateView() *CreateViewOptionsBuilder {\n\treturn &CreateViewOptionsBuilder{}\n}\n\n// List returns a list of TimeSeriesOptions setter functions.\nfunc (c *CreateViewOptionsBuilder) List() []func(*CreateViewOptions) error {\n\treturn c.Opts\n}\n\n// SetCollation sets the value for the Collation field. Specifies the default\n// collation for the new collection. The default value is nil.\nfunc (c *CreateViewOptionsBuilder) SetCollation(collation *Collation) *CreateViewOptionsBuilder {\n\tc.Opts = append(c.Opts, func(opts *CreateViewOptions) error {\n\t\topts.Collation = collation\n\n\t\treturn nil\n\t})\n\n\treturn c\n}\n"
  },
  {
    "path": "mongo/options/datakeyoptions.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage options\n\n// DataKeyOptions represents all possible options used to create a new data key.\n//\n// See corresponding setter methods for documentation.\ntype DataKeyOptions struct {\n\tMasterKey   any\n\tKeyAltNames []string\n\tKeyMaterial []byte\n}\n\n// DataKeyOptionsBuilder contains options to configure DataKey operations. Each\n// option can be set through setter functions. See documentation for each setter\n// function for an explanation of the option.\ntype DataKeyOptionsBuilder struct {\n\tOpts []func(*DataKeyOptions) error\n}\n\n// DataKey creates a new DataKeyOptions instance.\nfunc DataKey() *DataKeyOptionsBuilder {\n\treturn &DataKeyOptionsBuilder{}\n}\n\n// List returns a list of DataKey setter functions.\nfunc (dk *DataKeyOptionsBuilder) List() []func(*DataKeyOptions) error {\n\treturn dk.Opts\n}\n\n// SetMasterKey specifies a KMS-specific key used to encrypt the new data key.\n//\n// If being used with a local KMS provider, this option is not applicable and should not be specified.\n//\n// For the AWS, Azure, and GCP KMS providers, this option is required and must be a document. For each, the value of the\n// \"endpoint\" or \"keyVaultEndpoint\" must be a host name with an optional port number (e.g. \"foo.com\" or \"foo.com:443\").\n//\n// When using AWS, the document must have the format:\n//\n//\t{\n//\t  region: <string>,\n//\t  key: <string>,             // The Amazon Resource Name (ARN) to the AWS customer master key (CMK).\n//\t  endpoint: Optional<string> // An alternate host identifier to send KMS requests to.\n//\t}\n//\n// If unset, the \"endpoint\" defaults to \"kms.<region>.amazonaws.com\".\n//\n// When using Azure, the document must have the format:\n//\n//\t{\n//\t  keyVaultEndpoint: <string>,  // A host identifier to send KMS requests to.\n//\t  keyName: <string>,\n//\t  keyVersion: Optional<string> // A specific version of the named key.\n//\t}\n//\n// If unset, \"keyVersion\" defaults to the key's primary version.\n//\n// When using GCP, the document must have the format:\n//\n//\t{\n//\t  projectId: <string>,\n//\t  location: <string>,\n//\t  keyRing: <string>,\n//\t  keyName: <string>,\n//\t  keyVersion: Optional<string>, // A specific version of the named key.\n//\t  endpoint: Optional<string>    // An alternate host identifier to send KMS requests to.\n//\t}\n//\n// If unset, \"keyVersion\" defaults to the key's primary version and \"endpoint\" defaults to \"cloudkms.googleapis.com\".\nfunc (dk *DataKeyOptionsBuilder) SetMasterKey(masterKey any) *DataKeyOptionsBuilder {\n\tdk.Opts = append(dk.Opts, func(opts *DataKeyOptions) error {\n\t\topts.MasterKey = masterKey\n\n\t\treturn nil\n\t})\n\n\treturn dk\n}\n\n// SetKeyAltNames specifies an optional list of string alternate names used to reference a key. If a key is created'\n// with alternate names, encryption may refer to the key by a unique alternate name instead of by _id.\nfunc (dk *DataKeyOptionsBuilder) SetKeyAltNames(keyAltNames []string) *DataKeyOptionsBuilder {\n\tdk.Opts = append(dk.Opts, func(opts *DataKeyOptions) error {\n\t\topts.KeyAltNames = keyAltNames\n\n\t\treturn nil\n\t})\n\n\treturn dk\n}\n\n// SetKeyMaterial will set a custom keyMaterial to DataKeyOptions which can be used to encrypt data. If omitted,\n// keyMaterial is generated form a cryptographically secure random source. \"Key Material\" is used interchangeably\n// with \"dataKey\" and \"Data Encryption Key\" (DEK).\nfunc (dk *DataKeyOptionsBuilder) SetKeyMaterial(keyMaterial []byte) *DataKeyOptionsBuilder {\n\tdk.Opts = append(dk.Opts, func(opts *DataKeyOptions) error {\n\t\topts.KeyMaterial = keyMaterial\n\n\t\treturn nil\n\t})\n\n\treturn dk\n}\n"
  },
  {
    "path": "mongo/options/dboptions.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage options\n\nimport (\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/readconcern\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/readpref\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/writeconcern\"\n)\n\n// DatabaseOptions represents arguments that can be used to configure a\n// database.\n//\n// See corresponding setter methods for documentation.\ntype DatabaseOptions struct {\n\tReadConcern    *readconcern.ReadConcern\n\tWriteConcern   *writeconcern.WriteConcern\n\tReadPreference *readpref.ReadPref\n\tBSONOptions    *BSONOptions\n\tRegistry       *bson.Registry\n}\n\n// DatabaseOptionsBuilder contains options to configure a database object. Each\n// option can be set through setter functions. See documentation for each setter\n// function for an explanation of the option.\ntype DatabaseOptionsBuilder struct {\n\tOpts []func(*DatabaseOptions) error\n}\n\n// Database creates a new DatabaseOptions instance.\nfunc Database() *DatabaseOptionsBuilder {\n\treturn &DatabaseOptionsBuilder{}\n}\n\n// List returns a list of DatabaseOptions setter functions.\nfunc (d *DatabaseOptionsBuilder) List() []func(*DatabaseOptions) error {\n\treturn d.Opts\n}\n\n// SetReadConcern sets the value for the ReadConcern field. ReadConcern is the read concern\n// to use for operations executed on the Database. The default value is nil, which means that\n// the read concern of the Client used to configure the Database will be used.\nfunc (d *DatabaseOptionsBuilder) SetReadConcern(rc *readconcern.ReadConcern) *DatabaseOptionsBuilder {\n\td.Opts = append(d.Opts, func(opts *DatabaseOptions) error {\n\t\topts.ReadConcern = rc\n\n\t\treturn nil\n\t})\n\n\treturn d\n}\n\n// SetWriteConcern sets the value for the WriteConcern field. WriteConcern is the write concern\n// to use for operations executed on the Database. The default value is nil, which means that\n// the write concern of the Client used to configure the Database will be used.\nfunc (d *DatabaseOptionsBuilder) SetWriteConcern(wc *writeconcern.WriteConcern) *DatabaseOptionsBuilder {\n\td.Opts = append(d.Opts, func(opts *DatabaseOptions) error {\n\t\topts.WriteConcern = wc\n\n\t\treturn nil\n\t})\n\n\treturn d\n}\n\n// SetReadPreference sets the value for the ReadPreference field. ReadPreference is the read\n// preference to use for operations executed on the Database. The default value is nil, which\n// means that the read preference of the Client used to configure the Database will be used.\nfunc (d *DatabaseOptionsBuilder) SetReadPreference(rp *readpref.ReadPref) *DatabaseOptionsBuilder {\n\td.Opts = append(d.Opts, func(opts *DatabaseOptions) error {\n\t\topts.ReadPreference = rp\n\n\t\treturn nil\n\t})\n\n\treturn d\n}\n\n// SetBSONOptions configures optional BSON marshaling and unmarshaling behavior. BSONOptions\n// configures optional BSON marshaling and unmarshaling behavior.\nfunc (d *DatabaseOptionsBuilder) SetBSONOptions(bopts *BSONOptions) *DatabaseOptionsBuilder {\n\td.Opts = append(d.Opts, func(opts *DatabaseOptions) error {\n\t\topts.BSONOptions = bopts\n\n\t\treturn nil\n\t})\n\n\treturn d\n}\n\n// SetRegistry sets the value for the Registry field. Registry is the BSON registry to marshal and\n// unmarshal documents for operations executed on the Database. The default value is nil, which\n// means that the registry of the Client used to configure the Database will be used.\nfunc (d *DatabaseOptionsBuilder) SetRegistry(r *bson.Registry) *DatabaseOptionsBuilder {\n\td.Opts = append(d.Opts, func(opts *DatabaseOptions) error {\n\t\topts.Registry = r\n\n\t\treturn nil\n\t})\n\treturn d\n}\n"
  },
  {
    "path": "mongo/options/deleteoptions.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage options\n\nimport \"go.mongodb.org/mongo-driver/v2/internal/optionsutil\"\n\n// DeleteOneOptions represents arguments that can be used to configure DeleteOne\n// operations.\n//\n// See corresponding setter methods for documentation.\ntype DeleteOneOptions struct {\n\tCollation *Collation\n\tComment   any\n\tHint      any\n\tLet       any\n\n\t// Deprecated: This option is for internal use only and should not be set. It may be changed or removed in any\n\t// release.\n\tInternal optionsutil.Options\n}\n\n// DeleteOneOptionsBuilder contains options to configure DeleteOne operations. Each\n// option can be set through setter functions. See documentation for each setter\n// function for an explanation of the option.\ntype DeleteOneOptionsBuilder struct {\n\tOpts []func(*DeleteOneOptions) error\n}\n\n// DeleteOne creates a new DeleteOneOptions instance.\nfunc DeleteOne() *DeleteOneOptionsBuilder {\n\treturn &DeleteOneOptionsBuilder{}\n}\n\n// List returns a list of DeleteOneOptions setter functions.\nfunc (do *DeleteOneOptionsBuilder) List() []func(*DeleteOneOptions) error {\n\treturn do.Opts\n}\n\n// SetCollation sets the value for the Collation field. Specifies a collation to\n// use for string comparisons during the operation. The default value is nil,\n// which means the default collation of the collection will be used.\nfunc (do *DeleteOneOptionsBuilder) SetCollation(c *Collation) *DeleteOneOptionsBuilder {\n\tdo.Opts = append(do.Opts, func(opts *DeleteOneOptions) error {\n\t\topts.Collation = c\n\n\t\treturn nil\n\t})\n\n\treturn do\n}\n\n// SetComment sets the value for the Comment field. Specifies a string or document that will\n// be included in server logs, profiling logs, and currentOp queries to help trace the operation.\n// The default value is nil, which means that no comment will be included in the logs.\nfunc (do *DeleteOneOptionsBuilder) SetComment(comment any) *DeleteOneOptionsBuilder {\n\tdo.Opts = append(do.Opts, func(opts *DeleteOneOptions) error {\n\t\topts.Comment = comment\n\n\t\treturn nil\n\t})\n\n\treturn do\n}\n\n// SetHint sets the value for the Hint field. Specifies the index to use for the\n// operation. This should either be the index name as a string or the index\n// specification as a document. This option is only valid for MongoDB versions\n// >= 4.4. Server versions < 4.4 will return an error if this option is\n// specified. The driver will return an error if this option is specified during\n// an unacknowledged write operation. The driver will return an error if the\n// hint parameter is a multi-key map. The default value is nil, which means that\n// no hint will be sent.\nfunc (do *DeleteOneOptionsBuilder) SetHint(hint any) *DeleteOneOptionsBuilder {\n\tdo.Opts = append(do.Opts, func(opts *DeleteOneOptions) error {\n\t\topts.Hint = hint\n\n\t\treturn nil\n\t})\n\n\treturn do\n}\n\n// SetLet sets the value for the Let field. Specifies parameters for the delete expression. This\n// option is only valid for MongoDB versions >= 5.0. Older servers will report an error for using\n// this option. This must be a document mapping parameter names to values. Values must be constant\n// or closed expressions that do not reference document fields. Parameters can then be accessed as\n// variables in an aggregate expression context (e.g. \"$$var\").\nfunc (do *DeleteOneOptionsBuilder) SetLet(let any) *DeleteOneOptionsBuilder {\n\tdo.Opts = append(do.Opts, func(opts *DeleteOneOptions) error {\n\t\topts.Let = let\n\n\t\treturn nil\n\t})\n\n\treturn do\n}\n\n// DeleteManyOptions represents arguments that can be used to configure DeleteMany\n// operations.\n//\n// See corresponding setter methods for documentation.\ntype DeleteManyOptions struct {\n\tCollation *Collation\n\tComment   any\n\tHint      any\n\tLet       any\n\n\t// Deprecated: This option is for internal use only and should not be set. It may be changed or removed in any\n\t// release.\n\tInternal optionsutil.Options\n}\n\n// DeleteManyOptionsBuilder contains options to configure DeleteMany operations.\n// Each option can be set through setter functions. See documentation for each\n// setter function for an explanation of the option.\ntype DeleteManyOptionsBuilder struct {\n\tOpts []func(*DeleteManyOptions) error\n}\n\n// DeleteMany creates a new DeleteManyOptions instance.\nfunc DeleteMany() *DeleteManyOptionsBuilder {\n\treturn &DeleteManyOptionsBuilder{}\n}\n\n// List returns a list of DeleteOneOptions setter functions.\nfunc (do *DeleteManyOptionsBuilder) List() []func(*DeleteManyOptions) error {\n\treturn do.Opts\n}\n\n// SetCollation sets the value for the Collation field. Specifies a collation to\n// use for string comparisons during the operation. The default value is nil,\n// which means the default collation of the collection will be used.\nfunc (do *DeleteManyOptionsBuilder) SetCollation(c *Collation) *DeleteManyOptionsBuilder {\n\tdo.Opts = append(do.Opts, func(opts *DeleteManyOptions) error {\n\t\topts.Collation = c\n\n\t\treturn nil\n\t})\n\n\treturn do\n}\n\n// SetComment sets the value for the Comment field. Specifies a string or document that will be\n// included in server logs, profiling logs, and currentOp queries to help trace the operation.\n// The default value is nil, which means that no comment will be included in the logs.\nfunc (do *DeleteManyOptionsBuilder) SetComment(comment any) *DeleteManyOptionsBuilder {\n\tdo.Opts = append(do.Opts, func(opts *DeleteManyOptions) error {\n\t\topts.Comment = comment\n\n\t\treturn nil\n\t})\n\n\treturn do\n}\n\n// SetHint sets the value for the Hint field. Specifies the index to use for the\n// operation. This should either be the index name as a string or the index\n// specification as a document. This option is only valid for MongoDB versions\n// >= 4.4. Server versions < 4.4 will return an error if this option is\n// specified. The driver will return an error if this option is specified during\n// an unacknowledged write operation. The driver will return an error if the\n// hint parameter is a multi-key map. The default value is nil, which means that\n// no hint will be sent.\nfunc (do *DeleteManyOptionsBuilder) SetHint(hint any) *DeleteManyOptionsBuilder {\n\tdo.Opts = append(do.Opts, func(opts *DeleteManyOptions) error {\n\t\topts.Hint = hint\n\n\t\treturn nil\n\t})\n\n\treturn do\n}\n\n// SetLet sets the value for the Let field. Specifies parameters for the delete expression.\n// This option is only valid for MongoDB versions >= 5.0. Older servers will report an error\n// for using this option. This must be a document mapping parameter names to values. Values\n// must be constant or closed expressions that do not reference document fields. Parameters\n// can then be accessed as variables in an aggregate expression context (e.g. \"$$var\").\nfunc (do *DeleteManyOptionsBuilder) SetLet(let any) *DeleteManyOptionsBuilder {\n\tdo.Opts = append(do.Opts, func(opts *DeleteManyOptions) error {\n\t\topts.Let = let\n\n\t\treturn nil\n\t})\n\n\treturn do\n}\n"
  },
  {
    "path": "mongo/options/distinctoptions.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage options\n\nimport \"go.mongodb.org/mongo-driver/v2/internal/optionsutil\"\n\n// DistinctOptions represents arguments that can be used to configure a Distinct\n// operation.\n//\n// See corresponding setter methods for documentation.\ntype DistinctOptions struct {\n\tCollation *Collation\n\tComment   any\n\tHint      any\n\n\t// Deprecated: This option is for internal use only and should not be set. It may be changed or removed in any\n\t// release.\n\tInternal optionsutil.Options\n}\n\n// DistinctOptionsBuilder contains options to configure distinct operations. Each\n// option can be set through setter functions. See documentation for each setter\n// function for an explanation of the option.\ntype DistinctOptionsBuilder struct {\n\tOpts []func(*DistinctOptions) error\n}\n\n// Distinct creates a new DistinctOptions instance.\nfunc Distinct() *DistinctOptionsBuilder {\n\treturn &DistinctOptionsBuilder{}\n}\n\n// List returns a list of DistinctArg setter functions.\nfunc (do *DistinctOptionsBuilder) List() []func(*DistinctOptions) error {\n\treturn do.Opts\n}\n\n// SetCollation sets the value for the Collation field. Specifies a collation to\n// use for string comparisons during the operation. The default value is nil,\n// which means the default collation of the collection will be used.\nfunc (do *DistinctOptionsBuilder) SetCollation(c *Collation) *DistinctOptionsBuilder {\n\tdo.Opts = append(do.Opts, func(opts *DistinctOptions) error {\n\t\topts.Collation = c\n\n\t\treturn nil\n\t})\n\n\treturn do\n}\n\n// SetComment sets the value for the Comment field. Specifies a string or document that\n// will be included in server logs, profiling logs, and currentOp queries to help trace\n// the operation. The default value is nil, which means that no comment will be included\n// in the logs.\nfunc (do *DistinctOptionsBuilder) SetComment(comment any) *DistinctOptionsBuilder {\n\tdo.Opts = append(do.Opts, func(opts *DistinctOptions) error {\n\t\topts.Comment = comment\n\n\t\treturn nil\n\t})\n\n\treturn do\n}\n\n// SetHint specifies the index to use for the operation. This should either be\n// the index name as a string or the index specification as a document. This\n// option is only valid for MongoDB versions >= 7.1. Previous server versions\n// will return an error if an index hint is specified. Distinct returns an error\n// if the hint parameter is a multi-key map. The default value is nil, which\n// means that no index hint will be sent.\n//\n// SetHint sets the Hint field.\nfunc (do *DistinctOptionsBuilder) SetHint(hint any) *DistinctOptionsBuilder {\n\tdo.Opts = append(do.Opts, func(opts *DistinctOptions) error {\n\t\topts.Hint = hint\n\n\t\treturn nil\n\t})\n\n\treturn do\n}\n"
  },
  {
    "path": "mongo/options/doc.go",
    "content": "// Copyright (C) MongoDB, Inc. 2022-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\n// Package options defines the optional configurations for the MongoDB Go Driver.\npackage options\n"
  },
  {
    "path": "mongo/options/dropcollectionoptions.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage options\n\n// DropCollectionOptions represents arguments that can be used to configure a\n// Drop operation.\n//\n// See corresponding setter methods for documentation.\ntype DropCollectionOptions struct {\n\tEncryptedFields any\n}\n\n// DropCollectionOptionsBuilder contains options to configure collection drop\n// operations. Each option can be set through setter functions. See\n// documentation for each setter function for an explanation of the option.\ntype DropCollectionOptionsBuilder struct {\n\tOpts []func(*DropCollectionOptions) error\n}\n\n// DropCollection creates a new DropCollectionOptions instance.\nfunc DropCollection() *DropCollectionOptionsBuilder {\n\treturn &DropCollectionOptionsBuilder{}\n}\n\n// List returns a list of DropCollectionOptions setter functions.\nfunc (d *DropCollectionOptionsBuilder) List() []func(*DropCollectionOptions) error {\n\treturn d.Opts\n}\n\n// SetEncryptedFields sets the encrypted fields for encrypted collections.\n//\n// This option is only valid for MongoDB versions >= 6.0\nfunc (d *DropCollectionOptionsBuilder) SetEncryptedFields(encryptedFields any) *DropCollectionOptionsBuilder {\n\td.Opts = append(d.Opts, func(opts *DropCollectionOptions) error {\n\t\topts.EncryptedFields = encryptedFields\n\n\t\treturn nil\n\t})\n\n\treturn d\n}\n"
  },
  {
    "path": "mongo/options/encryptoptions.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage options\n\nimport (\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n)\n\n// These constants specify valid values for QueryType\n// QueryType is used for Queryable Encryption.\nconst (\n\tQueryTypeEquality string = \"equality\"\n)\n\n// RangeOptions specifies index options for a Queryable Encryption field supporting \"range\" queries.\n//\n// See corresponding setter methods for documentation.\ntype RangeOptions struct {\n\tMin        *bson.RawValue\n\tMax        *bson.RawValue\n\tSparsity   *int64\n\tTrimFactor *int32\n\tPrecision  *int32\n}\n\n// RangeOptionsBuilder contains options to configure RangeOptions for queryable\n// encryption. Each option can be set through setter functions. See\n// documentation for each setter function for an explanation of the option.\ntype RangeOptionsBuilder struct {\n\tOpts []func(*RangeOptions) error\n}\n\n// Range creates a new RangeOptions instance.\nfunc Range() *RangeOptionsBuilder {\n\treturn &RangeOptionsBuilder{}\n}\n\n// List returns a list of RangeOptions setter functions.\nfunc (ro *RangeOptionsBuilder) List() []func(*RangeOptions) error {\n\treturn ro.Opts\n}\n\n// SetMin sets the range index minimum value.\nfunc (ro *RangeOptionsBuilder) SetMin(min bson.RawValue) *RangeOptionsBuilder {\n\tro.Opts = append(ro.Opts, func(opts *RangeOptions) error {\n\t\topts.Min = &min\n\n\t\treturn nil\n\t})\n\n\treturn ro\n}\n\n// SetMax sets the range index maximum value.\nfunc (ro *RangeOptionsBuilder) SetMax(max bson.RawValue) *RangeOptionsBuilder {\n\tro.Opts = append(ro.Opts, func(opts *RangeOptions) error {\n\t\topts.Max = &max\n\n\t\treturn nil\n\t})\n\n\treturn ro\n}\n\n// SetSparsity sets the range index sparsity.\nfunc (ro *RangeOptionsBuilder) SetSparsity(sparsity int64) *RangeOptionsBuilder {\n\tro.Opts = append(ro.Opts, func(opts *RangeOptions) error {\n\t\topts.Sparsity = &sparsity\n\n\t\treturn nil\n\t})\n\n\treturn ro\n}\n\n// SetTrimFactor sets the range index trim factor.\nfunc (ro *RangeOptionsBuilder) SetTrimFactor(trimFactor int32) *RangeOptionsBuilder {\n\tro.Opts = append(ro.Opts, func(opts *RangeOptions) error {\n\t\topts.TrimFactor = &trimFactor\n\n\t\treturn nil\n\t})\n\n\treturn ro\n}\n\n// SetPrecision sets the range index precision.\nfunc (ro *RangeOptionsBuilder) SetPrecision(precision int32) *RangeOptionsBuilder {\n\tro.Opts = append(ro.Opts, func(opts *RangeOptions) error {\n\t\topts.Precision = &precision\n\n\t\treturn nil\n\t})\n\n\treturn ro\n}\n\n// TextOptions specifies index options for a Queryable Encryption field supporting \"text\" queries.\n//\n// See corresponding setter methods for documentation.\n//\n// Beta: This is a preview feature and should only be used for experimental workloads.\n// It is not intended for public use. It is subject to breaking changes.\ntype TextOptions struct {\n\tSubstring          *SubstringOptions\n\tPrefix             *PrefixOptions\n\tSuffix             *SuffixOptions\n\tCaseSensitive      bool\n\tDiacriticSensitive bool\n}\n\n// SubstringOptions specifies options to support substring queries.\n//\n// Beta: This is a preview feature and should only be used for experimental workloads.\n// It is not intended for public use. It is subject to breaking changes.\ntype SubstringOptions struct {\n\tStrMaxLength      int32\n\tStrMinQueryLength int32\n\tStrMaxQueryLength int32\n}\n\n// PrefixOptions specifies options to support prefix queries.\n//\n// Beta: This is a preview feature and should only be used for experimental workloads.\n// It is not intended for public use. It is subject to breaking changes.\ntype PrefixOptions struct {\n\tStrMinQueryLength int32\n\tStrMaxQueryLength int32\n}\n\n// SuffixOptions specifies options to support suffix queries.\n//\n// Beta: This is a preview feature and should only be used for experimental workloads.\n// It is not intended for public use. It is subject to breaking changes.\ntype SuffixOptions struct {\n\tStrMinQueryLength int32\n\tStrMaxQueryLength int32\n}\n\n// TextOptionsBuilder contains options to configure TextOptions for queryable\n// encryption. Each option can be set through setter functions. See\n// documentation for each setter function for an explanation of the option.\n//\n// Beta: This is a preview feature and should only be used for experimental workloads.\n// It is not intended for public use. It is subject to breaking changes.\ntype TextOptionsBuilder struct {\n\tOpts []func(*TextOptions) error\n}\n\n// Text creates a new TextOptions instance.\n//\n// Beta: This is a preview feature and should only be used for experimental workloads.\n// It is not intended for public use. It is subject to breaking changes.\nfunc Text() *TextOptionsBuilder {\n\treturn &TextOptionsBuilder{}\n}\n\n// List returns a list of TextOptions setter functions.\nfunc (to *TextOptionsBuilder) List() []func(*TextOptions) error {\n\treturn to.Opts\n}\n\n// SetSubstring sets the text index substring value.\n//\n// Beta: This is a preview feature and should only be used for experimental workloads.\n// It is not intended for public use. It is subject to breaking changes.\nfunc (to *TextOptionsBuilder) SetSubstring(substring SubstringOptions) *TextOptionsBuilder {\n\tto.Opts = append(to.Opts, func(opts *TextOptions) error {\n\t\topts.Substring = &substring\n\n\t\treturn nil\n\t})\n\n\treturn to\n}\n\n// SetPrefix sets the text index prefix value.\n//\n// Beta: This is a preview feature and should only be used for experimental workloads.\n// It is not intended for public use. It is subject to breaking changes.\nfunc (to *TextOptionsBuilder) SetPrefix(prefix PrefixOptions) *TextOptionsBuilder {\n\tto.Opts = append(to.Opts, func(opts *TextOptions) error {\n\t\topts.Prefix = &prefix\n\n\t\treturn nil\n\t})\n\n\treturn to\n}\n\n// SetSuffix sets the text index suffix value.\n//\n// Beta: This is a preview feature and should only be used for experimental workloads.\n// It is not intended for public use. It is subject to breaking changes.\nfunc (to *TextOptionsBuilder) SetSuffix(suffix SuffixOptions) *TextOptionsBuilder {\n\tto.Opts = append(to.Opts, func(opts *TextOptions) error {\n\t\topts.Suffix = &suffix\n\n\t\treturn nil\n\t})\n\n\treturn to\n}\n\n// SetCaseSensitive sets the text index caseSensitive value.\n//\n// Beta: This is a preview feature and should only be used for experimental workloads.\n// It is not intended for public use. It is subject to breaking changes.\nfunc (to *TextOptionsBuilder) SetCaseSensitive(caseSensitive bool) *TextOptionsBuilder {\n\tto.Opts = append(to.Opts, func(opts *TextOptions) error {\n\t\topts.CaseSensitive = caseSensitive\n\n\t\treturn nil\n\t})\n\n\treturn to\n}\n\n// SetDiacriticSensitive sets the text index diacriticSensitive value.\n//\n// Beta: This is a preview feature and should only be used for experimental workloads.\n// It is not intended for public use. It is subject to breaking changes.\nfunc (to *TextOptionsBuilder) SetDiacriticSensitive(diacriticSensitive bool) *TextOptionsBuilder {\n\tto.Opts = append(to.Opts, func(opts *TextOptions) error {\n\t\topts.DiacriticSensitive = diacriticSensitive\n\n\t\treturn nil\n\t})\n\n\treturn to\n}\n\n// EncryptOptions represents arguments to explicitly encrypt a value.\n//\n// See corresponding setter methods for documentation.\ntype EncryptOptions struct {\n\tKeyID            *bson.Binary\n\tKeyAltName       *string\n\tAlgorithm        string\n\tQueryType        string\n\tContentionFactor *int64\n\tRangeOptions     *RangeOptionsBuilder\n\tTextOptions      *TextOptionsBuilder\n}\n\n// EncryptOptionsBuilder contains options to configure Encryptopts for\n// queryeable encryption. Each option can be set through setter functions. See\n// documentation for each setter function for an explanation of the option.\ntype EncryptOptionsBuilder struct {\n\tOpts []func(*EncryptOptions) error\n}\n\n// List returns a list of EncryptOptions setter functions.\nfunc (e *EncryptOptionsBuilder) List() []func(*EncryptOptions) error {\n\treturn e.Opts\n}\n\n// Encrypt creates a new EncryptOptions instance.\nfunc Encrypt() *EncryptOptionsBuilder {\n\treturn &EncryptOptionsBuilder{}\n}\n\n// SetKeyID specifies an _id of a data key. This should be a UUID (a bson.Binary with subtype 4).\nfunc (e *EncryptOptionsBuilder) SetKeyID(keyID bson.Binary) *EncryptOptionsBuilder {\n\te.Opts = append(e.Opts, func(opts *EncryptOptions) error {\n\t\topts.KeyID = &keyID\n\n\t\treturn nil\n\t})\n\treturn e\n}\n\n// SetKeyAltName identifies a key vault document by 'keyAltName'.\nfunc (e *EncryptOptionsBuilder) SetKeyAltName(keyAltName string) *EncryptOptionsBuilder {\n\te.Opts = append(e.Opts, func(opts *EncryptOptions) error {\n\t\topts.KeyAltName = &keyAltName\n\n\t\treturn nil\n\t})\n\n\treturn e\n}\n\n// SetAlgorithm specifies an algorithm to use for encryption. This should be one of the following:\n// - AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\n// - AEAD_AES_256_CBC_HMAC_SHA_512-Random\n// - Indexed\n// - Unindexed\n// - Range\n// This is required.\n// Indexed and Unindexed are used for Queryable Encryption.\nfunc (e *EncryptOptionsBuilder) SetAlgorithm(algorithm string) *EncryptOptionsBuilder {\n\te.Opts = append(e.Opts, func(opts *EncryptOptions) error {\n\t\topts.Algorithm = algorithm\n\n\t\treturn nil\n\t})\n\n\treturn e\n}\n\n// SetQueryType specifies the intended query type. It is only valid to set if algorithm is \"Indexed\".\n// This should be one of the following:\n// - equality\n// QueryType is used for Queryable Encryption.\nfunc (e *EncryptOptionsBuilder) SetQueryType(queryType string) *EncryptOptionsBuilder {\n\te.Opts = append(e.Opts, func(opts *EncryptOptions) error {\n\t\topts.QueryType = queryType\n\n\t\treturn nil\n\t})\n\n\treturn e\n}\n\n// SetContentionFactor specifies the contention factor. It is only valid to set if algorithm is \"Indexed\".\n// ContentionFactor is used for Queryable Encryption.\nfunc (e *EncryptOptionsBuilder) SetContentionFactor(contentionFactor int64) *EncryptOptionsBuilder {\n\te.Opts = append(e.Opts, func(opts *EncryptOptions) error {\n\t\topts.ContentionFactor = &contentionFactor\n\n\t\treturn nil\n\t})\n\n\treturn e\n}\n\n// SetRangeOptions specifies the options to use for explicit encryption with range. It is only valid to set if algorithm is \"range\".\nfunc (e *EncryptOptionsBuilder) SetRangeOptions(ro *RangeOptionsBuilder) *EncryptOptionsBuilder {\n\te.Opts = append(e.Opts, func(opts *EncryptOptions) error {\n\t\topts.RangeOptions = ro\n\n\t\treturn nil\n\t})\n\n\treturn e\n}\n\n// SetTextOptions specifies the options to use for text queries.\n//\n// Beta: This is a preview feature and should only be used for experimental workloads.\n// It is not intended for public use. It is subject to breaking changes.\nfunc (e *EncryptOptionsBuilder) SetTextOptions(to *TextOptionsBuilder) *EncryptOptionsBuilder {\n\te.Opts = append(e.Opts, func(opts *EncryptOptions) error {\n\t\topts.TextOptions = to\n\n\t\treturn nil\n\t})\n\n\treturn e\n}\n"
  },
  {
    "path": "mongo/options/estimatedcountoptions.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage options\n\nimport \"go.mongodb.org/mongo-driver/v2/internal/optionsutil\"\n\n// EstimatedDocumentCountOptions represents arguments that can be used to configure\n// an EstimatedDocumentCount operation.\n//\n// See corresponding setter methods for documentation.\ntype EstimatedDocumentCountOptions struct {\n\tComment any\n\n\t// Deprecated: This option is for internal use only and should not be set. It may be changed or removed in any\n\t// release.\n\tInternal optionsutil.Options\n}\n\n// EstimatedDocumentCountOptionsBuilder contains options to estimate document\n// count. Each option can be set through setter functions. See documentation for\n// each setter function for an explanation of the option.\ntype EstimatedDocumentCountOptionsBuilder struct {\n\tOpts []func(*EstimatedDocumentCountOptions) error\n}\n\n// EstimatedDocumentCount creates a new EstimatedDocumentCountOptions instance.\nfunc EstimatedDocumentCount() *EstimatedDocumentCountOptionsBuilder {\n\treturn &EstimatedDocumentCountOptionsBuilder{}\n}\n\n// List returns a list of CountOptions setter functions.\nfunc (eco *EstimatedDocumentCountOptionsBuilder) List() []func(*EstimatedDocumentCountOptions) error {\n\treturn eco.Opts\n}\n\n// SetComment sets the value for the Comment field. Specifies a string or document\n// that will be included in server logs, profiling logs, and currentOp queries to help\n// trace the operation.  The default is nil, which means that no comment will be\n// included in the logs.\nfunc (eco *EstimatedDocumentCountOptionsBuilder) SetComment(comment any) *EstimatedDocumentCountOptionsBuilder {\n\teco.Opts = append(eco.Opts, func(opts *EstimatedDocumentCountOptions) error {\n\t\topts.Comment = comment\n\n\t\treturn nil\n\t})\n\n\treturn eco\n}\n"
  },
  {
    "path": "mongo/options/example_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2023-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage options_test\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"fmt\"\n\t\"io\"\n\t\"log\"\n\t\"sync\"\n\n\t\"go.mongodb.org/mongo-driver/v2/mongo\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n)\n\ntype CustomLogger struct {\n\tio.Writer\n\tmu sync.Mutex\n}\n\nfunc (logger *CustomLogger) Info(level int, msg string, _ ...any) {\n\tlogger.mu.Lock()\n\tdefer logger.mu.Unlock()\n\n\tfmt.Fprintf(logger, \"level=%d msg=%s\\n\", level, msg)\n}\n\nfunc (logger *CustomLogger) Error(err error, msg string, _ ...any) {\n\tlogger.mu.Lock()\n\tdefer logger.mu.Unlock()\n\n\tfmt.Fprintf(logger, \"err=%v msg=%s\\n\", err, msg)\n}\n\nfunc ExampleClientOptions_SetLoggerOptions_customLogger() {\n\tbuf := bytes.NewBuffer(nil)\n\tsink := &CustomLogger{Writer: buf}\n\n\t// Create a client with our logger options.\n\tloggerOptions := options.\n\t\tLogger().\n\t\tSetSink(sink).\n\t\tSetMaxDocumentLength(25).\n\t\tSetComponentLevel(options.LogComponentCommand, options.LogLevelDebug)\n\n\tclientOptions := options.\n\t\tClient().\n\t\tApplyURI(\"mongodb://localhost:27017\").\n\t\tSetLoggerOptions(loggerOptions)\n\n\tclient, err := mongo.Connect(clientOptions)\n\tif err != nil {\n\t\tlog.Panicf(\"error connecting to MongoDB: %v\", err)\n\t}\n\n\tdefer func() { _ = client.Disconnect(context.TODO()) }()\n\n\t// Make a database request to test our logging solution.\n\tcoll := client.Database(\"test\").Collection(\"test\")\n\n\t_, err = coll.InsertOne(context.TODO(), map[string]string{\"foo\": \"bar\"})\n\tif err != nil {\n\t\tlog.Panicf(\"InsertOne failed: %v\", err)\n\t}\n\n\t// Print the logs.\n\tfmt.Println(buf.String())\n}\n"
  },
  {
    "path": "mongo/options/findoptions.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage options\n\nimport (\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/optionsutil\"\n)\n\n// FindOptions represents arguments that can be used to configure a Find\n// operation.\n//\n// See corresponding setter methods for documentation.\ntype FindOptions struct {\n\tAllowPartialResults *bool\n\tCollation           *Collation\n\tComment             any\n\tHint                any\n\tMax                 any\n\tMaxAwaitTime        *time.Duration\n\tMin                 any\n\tOplogReplay         *bool\n\tProjection          any\n\tReturnKey           *bool\n\tShowRecordID        *bool\n\tSkip                *int64\n\tSort                any\n\t// The above are in common with FindOneopts.\n\tAllowDiskUse    *bool\n\tBatchSize       *int32\n\tCursorType      *CursorType\n\tLet             any\n\tLimit           *int64\n\tNoCursorTimeout *bool\n\n\t// Deprecated: This option is for internal use only and should not be set. It may be changed or removed in any\n\t// release.\n\tInternal optionsutil.Options\n}\n\n// FindOptionsBuilder represents functional options that configure an Findopts.\ntype FindOptionsBuilder struct {\n\tOpts []func(*FindOptions) error\n}\n\n// Find creates a new FindOptions instance.\nfunc Find() *FindOptionsBuilder {\n\treturn &FindOptionsBuilder{}\n}\n\n// List returns a list of FindOptions setter functions.\nfunc (f *FindOptionsBuilder) List() []func(*FindOptions) error {\n\treturn f.Opts\n}\n\n// SetAllowDiskUse sets the value for the AllowDiskUse field. AllowDiskUse\n// specifies whether the server can write temporary data to disk while executing\n// the Find operation. This option is only valid for MongoDB versions >= 4.4.\n// Server versions < 4.4 will return an error if this option is specified. The\n// default value is false.\nfunc (f *FindOptionsBuilder) SetAllowDiskUse(b bool) *FindOptionsBuilder {\n\tf.Opts = append(f.Opts, func(opts *FindOptions) error {\n\t\topts.AllowDiskUse = &b\n\t\treturn nil\n\t})\n\treturn f\n}\n\n// SetAllowPartialResults sets the value for the AllowPartialResults field. AllowPartial results\n// specifies whether the Find operation on a sharded cluster can return partial results if some\n// shards are down rather than returning an error. The default value is false.\nfunc (f *FindOptionsBuilder) SetAllowPartialResults(b bool) *FindOptionsBuilder {\n\tf.Opts = append(f.Opts, func(opts *FindOptions) error {\n\t\topts.AllowPartialResults = &b\n\t\treturn nil\n\t})\n\treturn f\n}\n\n// SetBatchSize sets the value for the BatchSize field. BatchSize is the maximum number of documents\n// to be included in each batch returned by the server.\nfunc (f *FindOptionsBuilder) SetBatchSize(i int32) *FindOptionsBuilder {\n\tf.Opts = append(f.Opts, func(opts *FindOptions) error {\n\t\topts.BatchSize = &i\n\t\treturn nil\n\t})\n\treturn f\n}\n\n// SetCollation sets the value for the Collation field. Collation specifies a\n// collation to use for string comparisons during the operation. The default\n// value is nil, which means the default collation of the collection will be\n// used.\nfunc (f *FindOptionsBuilder) SetCollation(collation *Collation) *FindOptionsBuilder {\n\tf.Opts = append(f.Opts, func(opts *FindOptions) error {\n\t\topts.Collation = collation\n\t\treturn nil\n\t})\n\treturn f\n}\n\n// SetComment sets the value for the Comment field. Specifies a string or document that will be\n// included in server logs, profiling logs, and currentOp queries to help trace the operation.\n// The default is nil, which means that no comment will be included in the logs.\nfunc (f *FindOptionsBuilder) SetComment(comment any) *FindOptionsBuilder {\n\tf.Opts = append(f.Opts, func(opts *FindOptions) error {\n\t\topts.Comment = &comment\n\t\treturn nil\n\t})\n\treturn f\n}\n\n// SetCursorType sets the value for the CursorType field. CursorType specifies the type of cursor\n// that should be created for the operation. The default is NonTailable, which means that the\n// cursor will be closed by the server when the last batch of documents is retrieved.\nfunc (f *FindOptionsBuilder) SetCursorType(ct CursorType) *FindOptionsBuilder {\n\tf.Opts = append(f.Opts, func(opts *FindOptions) error {\n\t\topts.CursorType = &ct\n\t\treturn nil\n\t})\n\treturn f\n}\n\n// SetHint sets the value for the Hint field. Hint is the index to use for the Find operation.\n// This should either be the index name as a string or the index specification as a document.\n// The driver will return an error if the hint parameter is a multi-key map. The default\n// value is nil, which means that no hint will be sent.\nfunc (f *FindOptionsBuilder) SetHint(hint any) *FindOptionsBuilder {\n\tf.Opts = append(f.Opts, func(opts *FindOptions) error {\n\t\topts.Hint = hint\n\t\treturn nil\n\t})\n\treturn f\n}\n\n// SetLet sets the value for the Let field. Let specifies parameters for the find expression.\n// This option is only valid for MongoDB versions >= 5.0. Older servers will report an error\n// for using this option. This must be a document mapping parameter names to values. Values\n// must be constant or closed expressions that do not reference document fields. Parameters\n// can then be accessed as variables in an aggregate expression context (e.g. \"$$var\").\nfunc (f *FindOptionsBuilder) SetLet(let any) *FindOptionsBuilder {\n\tf.Opts = append(f.Opts, func(opts *FindOptions) error {\n\t\topts.Let = let\n\t\treturn nil\n\t})\n\treturn f\n}\n\n// SetLimit sets the value for the Limit field. Limit is the maximum number of documents to return.\n// The default value is 0, which means that all documents matching the filter will be returned.\n// A negative limit specifies that the resulting documents should be returned in a single batch.\n// The default value is 0.\nfunc (f *FindOptionsBuilder) SetLimit(i int64) *FindOptionsBuilder {\n\tf.Opts = append(f.Opts, func(opts *FindOptions) error {\n\t\topts.Limit = &i\n\t\treturn nil\n\t})\n\treturn f\n}\n\n// SetMax sets the value for the Max field. Max is a document specifying the exclusive upper bound\n// for a specific index. The default value is nil, which means that there is no maximum value.\nfunc (f *FindOptionsBuilder) SetMax(max any) *FindOptionsBuilder {\n\tf.Opts = append(f.Opts, func(opts *FindOptions) error {\n\t\topts.Max = max\n\t\treturn nil\n\t})\n\treturn f\n}\n\n// SetMaxAwaitTime sets the value for the MaxAwaitTime field. MaxAwaitTime is\n// the maximum amount of time that the server should wait for new documents to\n// satisfy a tailable cursor query. This option is only valid for tailable await\n// cursors (see the CursorType option for more information). For other cursor\n// types, this option is ignored.\nfunc (f *FindOptionsBuilder) SetMaxAwaitTime(d time.Duration) *FindOptionsBuilder {\n\tf.Opts = append(f.Opts, func(opts *FindOptions) error {\n\t\topts.MaxAwaitTime = &d\n\t\treturn nil\n\t})\n\treturn f\n}\n\n// SetMin sets the value for the Min field. Min is a document specifying the inclusive lower bound\n// for a specific index. The default value is 0, which means that there is no minimum value.\nfunc (f *FindOptionsBuilder) SetMin(min any) *FindOptionsBuilder {\n\tf.Opts = append(f.Opts, func(opts *FindOptions) error {\n\t\topts.Min = min\n\t\treturn nil\n\t})\n\treturn f\n}\n\n// SetNoCursorTimeout sets the value for the NoCursorTimeout field. NoCursorTimeout specifies\n// whether the cursor created by the operation will not timeout after a period of inactivity.\n// The default value is false.\nfunc (f *FindOptionsBuilder) SetNoCursorTimeout(b bool) *FindOptionsBuilder {\n\tf.Opts = append(f.Opts, func(opts *FindOptions) error {\n\t\topts.NoCursorTimeout = &b\n\t\treturn nil\n\t})\n\treturn f\n}\n\n// SetOplogReplay sets the value for the OplogReplay field. OplogReplay is for internal\n// replication use only and should not be set.\n//\n// Deprecated: This option has been deprecated in MongoDB version 4.4 and will be ignored by\n// the server if it is set.\nfunc (f *FindOptionsBuilder) SetOplogReplay(b bool) *FindOptionsBuilder {\n\tf.Opts = append(f.Opts, func(opts *FindOptions) error {\n\t\topts.OplogReplay = &b\n\t\treturn nil\n\t})\n\treturn f\n}\n\n// SetProjection sets the value for the Projection field. Projection is a document describing\n// which fields will be included in the documents returned by the Find operation. The\n// default value is nil, which means all fields will be included.\nfunc (f *FindOptionsBuilder) SetProjection(projection any) *FindOptionsBuilder {\n\tf.Opts = append(f.Opts, func(opts *FindOptions) error {\n\t\topts.Projection = projection\n\t\treturn nil\n\t})\n\treturn f\n}\n\n// SetReturnKey sets the value for the ReturnKey field. ReturnKey specifies whether the\n// documents returned by the Find operation will only contain fields corresponding to the\n// index used. The default value is false.\nfunc (f *FindOptionsBuilder) SetReturnKey(b bool) *FindOptionsBuilder {\n\tf.Opts = append(f.Opts, func(opts *FindOptions) error {\n\t\topts.ReturnKey = &b\n\t\treturn nil\n\t})\n\treturn f\n}\n\n// SetShowRecordID sets the value for the ShowRecordID field. ShowRecordID specifies whether\n// a $recordId field with a record identifier will be included in the documents returned by\n// the Find operation. The default value is false.\nfunc (f *FindOptionsBuilder) SetShowRecordID(b bool) *FindOptionsBuilder {\n\tf.Opts = append(f.Opts, func(opts *FindOptions) error {\n\t\topts.ShowRecordID = &b\n\t\treturn nil\n\t})\n\treturn f\n}\n\n// SetSkip sets the value for the Skip field. Skip is the number of documents to skip before\n// adding documents to the result. The default value is 0.\nfunc (f *FindOptionsBuilder) SetSkip(i int64) *FindOptionsBuilder {\n\tf.Opts = append(f.Opts, func(opts *FindOptions) error {\n\t\topts.Skip = &i\n\t\treturn nil\n\t})\n\treturn f\n}\n\n// SetSort sets the value for the Sort field. Sort is a document specifying the order in which\n// documents should be returned. The sort parameter is evaluated sequentially, so the driver will\n// return an error if it is a multi-key map (which is unordeded). The default value is nil.\nfunc (f *FindOptionsBuilder) SetSort(sort any) *FindOptionsBuilder {\n\tf.Opts = append(f.Opts, func(opts *FindOptions) error {\n\t\topts.Sort = sort\n\t\treturn nil\n\t})\n\treturn f\n}\n\n// FindOneOptions represents arguments that can be used to configure a FindOne\n// operation.\n//\n// See corresponding setter methods for documentation.\ntype FindOneOptions struct {\n\tAllowPartialResults *bool\n\tCollation           *Collation\n\tComment             any\n\tHint                any\n\tMax                 any\n\tMin                 any\n\tOplogReplay         *bool\n\tProjection          any\n\tReturnKey           *bool\n\tShowRecordID        *bool\n\tSkip                *int64\n\tSort                any\n\n\t// Deprecated: This option is for internal use only and should not be set. It may be changed or removed in any\n\t// release.\n\tInternal optionsutil.Options\n}\n\n// FindOneOptionsBuilder represents functional options that configure an\n// FindOneopts.\ntype FindOneOptionsBuilder struct {\n\tOpts []func(*FindOneOptions) error\n}\n\n// FindOne creates a new FindOneOptions instance.\nfunc FindOne() *FindOneOptionsBuilder {\n\treturn &FindOneOptionsBuilder{}\n}\n\n// List returns a list of FindOneOptions setter functions.\nfunc (f *FindOneOptionsBuilder) List() []func(*FindOneOptions) error {\n\treturn f.Opts\n}\n\n// SetAllowPartialResults sets the value for the AllowPartialResults field. If true, an operation\n// on a sharded cluster can return partial results if some shards are down rather than returning\n// an error. The default value is false.\nfunc (f *FindOneOptionsBuilder) SetAllowPartialResults(b bool) *FindOneOptionsBuilder {\n\tf.Opts = append(f.Opts, func(opts *FindOneOptions) error {\n\t\topts.AllowPartialResults = &b\n\t\treturn nil\n\t})\n\treturn f\n}\n\n// SetCollation sets the value for the Collation field. Specifies a collation to\n// use for string comparisons during the operation. The default value is nil,\n// which means the default collation of the collection will be used.\nfunc (f *FindOneOptionsBuilder) SetCollation(collation *Collation) *FindOneOptionsBuilder {\n\tf.Opts = append(f.Opts, func(opts *FindOneOptions) error {\n\t\topts.Collation = collation\n\t\treturn nil\n\t})\n\treturn f\n}\n\n// SetComment sets the value for the Comment field. Specifies a string or document that will be\n// included in server logs, profiling logs, and currentOp queries to help trace the operation.\n// The default is nil, which means that no comment will be included in the logs.\nfunc (f *FindOneOptionsBuilder) SetComment(comment any) *FindOneOptionsBuilder {\n\tf.Opts = append(f.Opts, func(opts *FindOneOptions) error {\n\t\topts.Comment = &comment\n\t\treturn nil\n\t})\n\treturn f\n}\n\n// SetHint sets the value for the Hint field. Specifies the index to use for the aggregation.\n// This should either be the index name as a string or the index specification as a document.\n// The driver will return an error if the hint parameter is a multi-key map. The default value\n// is nil, which means that no hint will be sent.\nfunc (f *FindOneOptionsBuilder) SetHint(hint any) *FindOneOptionsBuilder {\n\tf.Opts = append(f.Opts, func(opts *FindOneOptions) error {\n\t\topts.Hint = hint\n\t\treturn nil\n\t})\n\treturn f\n}\n\n// SetMax sets the value for the Max field. Sets a document specifying the exclusive upper bound\n// for a specific index. The default value is nil, which means that there is no maximum value.\nfunc (f *FindOneOptionsBuilder) SetMax(max any) *FindOneOptionsBuilder {\n\tf.Opts = append(f.Opts, func(opts *FindOneOptions) error {\n\t\topts.Max = max\n\t\treturn nil\n\t})\n\treturn f\n}\n\n// SetMin sets the value for the Min field. Sets a document specifying the inclusive lower bound\n// for a specific index. The default value is 0, which means that there is no minimum value.\nfunc (f *FindOneOptionsBuilder) SetMin(min any) *FindOneOptionsBuilder {\n\tf.Opts = append(f.Opts, func(opts *FindOneOptions) error {\n\t\topts.Min = min\n\t\treturn nil\n\t})\n\treturn f\n}\n\n// SetOplogReplay sets the value for the OplogReplay field. OplogReplay is for internal\n// replication use only and should not be set.\n//\n// Deprecated: This option has been deprecated in MongoDB version 4.4 and will be ignored by\n// the server if it is set.\nfunc (f *FindOneOptionsBuilder) SetOplogReplay(b bool) *FindOneOptionsBuilder {\n\tf.Opts = append(f.Opts, func(opts *FindOneOptions) error {\n\t\topts.OplogReplay = &b\n\t\treturn nil\n\t})\n\treturn f\n}\n\n// SetProjection sets the value for the Projection field. Sets a document describing which fields\n// will be included in the document returned by the operation. The default value is nil, which\n// means all fields will be included.\nfunc (f *FindOneOptionsBuilder) SetProjection(projection any) *FindOneOptionsBuilder {\n\tf.Opts = append(f.Opts, func(opts *FindOneOptions) error {\n\t\topts.Projection = projection\n\t\treturn nil\n\t})\n\treturn f\n}\n\n// SetReturnKey sets the value for the ReturnKey field. If true, the document returned by the\n// operation will only contain fields corresponding to the index used. The default value\n// is false.\nfunc (f *FindOneOptionsBuilder) SetReturnKey(b bool) *FindOneOptionsBuilder {\n\tf.Opts = append(f.Opts, func(opts *FindOneOptions) error {\n\t\topts.ReturnKey = &b\n\t\treturn nil\n\t})\n\treturn f\n}\n\n// SetShowRecordID sets the value for the ShowRecordID field. If true, a $recordId field with\n// a record identifier will be included in the document returned by the operation. The default\n// value is false.\nfunc (f *FindOneOptionsBuilder) SetShowRecordID(b bool) *FindOneOptionsBuilder {\n\tf.Opts = append(f.Opts, func(opts *FindOneOptions) error {\n\t\topts.ShowRecordID = &b\n\t\treturn nil\n\t})\n\treturn f\n}\n\n// SetSkip sets the value for the Skip field. Specifies the number of documents to skip before\n// selecting the document to be returned. The default value is 0.\nfunc (f *FindOneOptionsBuilder) SetSkip(i int64) *FindOneOptionsBuilder {\n\tf.Opts = append(f.Opts, func(opts *FindOneOptions) error {\n\t\topts.Skip = &i\n\t\treturn nil\n\t})\n\treturn f\n}\n\n// SetSort sets the value for the Sort field. Sets a document specifying the sort order to\n// apply to the query. The first document in the sorted order will be returned. The sort\n// parameter is evaluated sequentially, so the driver will return an error if it is a multi-\n// key map (which is unordeded). The default value is nil.\nfunc (f *FindOneOptionsBuilder) SetSort(sort any) *FindOneOptionsBuilder {\n\tf.Opts = append(f.Opts, func(opts *FindOneOptions) error {\n\t\topts.Sort = sort\n\t\treturn nil\n\t})\n\treturn f\n}\n\n// FindOneAndReplaceOptions represents arguments that can be used to configure a\n// FindOneAndReplace instance.\n//\n// See corresponding setter methods for documentation.\ntype FindOneAndReplaceOptions struct {\n\tBypassDocumentValidation *bool\n\tCollation                *Collation\n\tComment                  any\n\tProjection               any\n\tReturnDocument           *ReturnDocument\n\tSort                     any\n\tUpsert                   *bool\n\tHint                     any\n\tLet                      any\n\n\t// Deprecated: This option is for internal use only and should not be set. It may be changed or removed in any\n\t// release.\n\tInternal optionsutil.Options\n}\n\n// FindOneAndReplaceOptionsBuilder contains options to perform a findAndModify\n// operation. Each option can be set through setter functions. See documentation\n// for each setter function for an explanation of the option.\ntype FindOneAndReplaceOptionsBuilder struct {\n\tOpts []func(*FindOneAndReplaceOptions) error\n}\n\n// FindOneAndReplace creates a new FindOneAndReplaceOptions instance.\nfunc FindOneAndReplace() *FindOneAndReplaceOptionsBuilder {\n\treturn &FindOneAndReplaceOptionsBuilder{}\n}\n\n// List returns a list of FindOneAndReplaceOptions setter functions.\nfunc (f *FindOneAndReplaceOptionsBuilder) List() []func(*FindOneAndReplaceOptions) error {\n\treturn f.Opts\n}\n\n// SetBypassDocumentValidation sets the value for the BypassDocumentValidation field. If true, writes\n// executed as part of the operation will opt out of document-level validation on the server. The\n// default value is false. See https://www.mongodb.com/docs/manual/core/schema-validation/ for more\n// information about document validation.\nfunc (f *FindOneAndReplaceOptionsBuilder) SetBypassDocumentValidation(b bool) *FindOneAndReplaceOptionsBuilder {\n\tf.Opts = append(f.Opts, func(opts *FindOneAndReplaceOptions) error {\n\t\topts.BypassDocumentValidation = &b\n\n\t\treturn nil\n\t})\n\n\treturn f\n}\n\n// SetCollation sets the value for the Collation field. Specifies a collation to\n// use for string comparisons during the operation. The default value is nil,\n// which means the default collation of the collection will be used.\nfunc (f *FindOneAndReplaceOptionsBuilder) SetCollation(collation *Collation) *FindOneAndReplaceOptionsBuilder {\n\tf.Opts = append(f.Opts, func(opts *FindOneAndReplaceOptions) error {\n\t\topts.Collation = collation\n\n\t\treturn nil\n\t})\n\n\treturn f\n}\n\n// SetComment sets the value for the Comment field. Specifies a string or document that will be\n// included in server logs, profiling logs, and currentOp queries to help trace the operation.\n// The default value is nil, which means that no comment will be included in the logs.\nfunc (f *FindOneAndReplaceOptionsBuilder) SetComment(comment any) *FindOneAndReplaceOptionsBuilder {\n\tf.Opts = append(f.Opts, func(opts *FindOneAndReplaceOptions) error {\n\t\topts.Comment = comment\n\n\t\treturn nil\n\t})\n\n\treturn f\n}\n\n// SetProjection sets the value for the Projection field. Sets a document describing which fields\n// will be included in the document returned by the operation. The default value is nil, which\n// means all fields will be included.\nfunc (f *FindOneAndReplaceOptionsBuilder) SetProjection(projection any) *FindOneAndReplaceOptionsBuilder {\n\tf.Opts = append(f.Opts, func(opts *FindOneAndReplaceOptions) error {\n\t\topts.Projection = projection\n\n\t\treturn nil\n\t})\n\n\treturn f\n}\n\n// SetReturnDocument sets the value for the ReturnDocument field. Specifies whether the original\n// or replaced document should be returned by the operation. The default value is Before, which\n// means the original document will be returned from before the replacement is performed.\nfunc (f *FindOneAndReplaceOptionsBuilder) SetReturnDocument(rd ReturnDocument) *FindOneAndReplaceOptionsBuilder {\n\tf.Opts = append(f.Opts, func(opts *FindOneAndReplaceOptions) error {\n\t\topts.ReturnDocument = &rd\n\n\t\treturn nil\n\t})\n\n\treturn f\n}\n\n// SetSort sets the value for the Sort field. Sets a document specifying which document should\n// be replaced if the filter used by the operation matches multiple documents in the collection.\n// If set, the first document in the sorted order will be replaced. The sort parameter is evaluated\n// sequentially, so the driver will return an error if it is a multi-key map (which is unordeded).\n// The default value is nil.\nfunc (f *FindOneAndReplaceOptionsBuilder) SetSort(sort any) *FindOneAndReplaceOptionsBuilder {\n\tf.Opts = append(f.Opts, func(opts *FindOneAndReplaceOptions) error {\n\t\topts.Sort = sort\n\n\t\treturn nil\n\t})\n\n\treturn f\n}\n\n// SetUpsert sets the value for the Upsert field. If true, a new document will be inserted if\n// the filter does not match any documents in the collection. The default value is false.\nfunc (f *FindOneAndReplaceOptionsBuilder) SetUpsert(b bool) *FindOneAndReplaceOptionsBuilder {\n\tf.Opts = append(f.Opts, func(opts *FindOneAndReplaceOptions) error {\n\t\topts.Upsert = &b\n\n\t\treturn nil\n\t})\n\n\treturn f\n}\n\n// SetHint sets the value for the Hint field. Specifies the index to use for the operation.\n// This should either be the index name as a string or the index specification as a document.\n// This option is only valid for MongoDB versions >= 4.4. MongoDB version 4.2 will report an\n// error if this option is specified. For server versions < 4.2, the driver will return an\n// error if this option is specified. The driver will return an error if this option is used\n// with during an unacknowledged write operation. The driver will return an error if the\n// hint parameter is a multi-key map. The default value is nil, which means that no hint\n// will be sent.\nfunc (f *FindOneAndReplaceOptionsBuilder) SetHint(hint any) *FindOneAndReplaceOptionsBuilder {\n\tf.Opts = append(f.Opts, func(opts *FindOneAndReplaceOptions) error {\n\t\topts.Hint = hint\n\n\t\treturn nil\n\t})\n\n\treturn f\n}\n\n// SetLet sets the value for the Let field. Specifies parameters for the find one and\n// replace expression. This option is only valid for MongoDB versions >= 5.0. Older\n// servers will report an error for using this option. This must be a document mapping\n// parameter names to values. Values must be constant or closed expressions that do not\n// reference document fields. Parameters can then be accessed as variables in an\n// aggregate expression context (e.g. \"$$var\").\nfunc (f *FindOneAndReplaceOptionsBuilder) SetLet(let any) *FindOneAndReplaceOptionsBuilder {\n\tf.Opts = append(f.Opts, func(opts *FindOneAndReplaceOptions) error {\n\t\topts.Let = let\n\n\t\treturn nil\n\t})\n\n\treturn f\n}\n\n// FindOneAndUpdateOptions represents arguments that can be used to configure a\n// FindOneAndUpdate options.\n//\n// See corresponding setter methods for documentation.\ntype FindOneAndUpdateOptions struct {\n\tArrayFilters             []any\n\tBypassDocumentValidation *bool\n\tCollation                *Collation\n\tComment                  any\n\tProjection               any\n\tReturnDocument           *ReturnDocument\n\tSort                     any\n\tUpsert                   *bool\n\tHint                     any\n\tLet                      any\n\n\t// Deprecated: This option is for internal use only and should not be set. It may be changed or removed in any\n\t// release.\n\tInternal optionsutil.Options\n}\n\n// FindOneAndUpdateOptionsBuilder contains options to configure a\n// findOneAndUpdate operation. Each option can be set through setter functions.\n// See documentation for each setter function for an explanation of the option.\ntype FindOneAndUpdateOptionsBuilder struct {\n\tOpts []func(*FindOneAndUpdateOptions) error\n}\n\n// FindOneAndUpdate creates a new FindOneAndUpdateOptions instance.\nfunc FindOneAndUpdate() *FindOneAndUpdateOptionsBuilder {\n\treturn &FindOneAndUpdateOptionsBuilder{}\n}\n\n// List returns a list of FindOneAndUpdateOptions setter functions.\nfunc (f *FindOneAndUpdateOptionsBuilder) List() []func(*FindOneAndUpdateOptions) error {\n\treturn f.Opts\n}\n\n// SetArrayFilters sets the value for the ArrayFilters field. ArrayFilters is a\n// set of filters specifying to which array elements an update should apply. The\n// default value is nil, which means the update will apply to all array\n// elements.\nfunc (f *FindOneAndUpdateOptionsBuilder) SetArrayFilters(filters []any) *FindOneAndUpdateOptionsBuilder {\n\tf.Opts = append(f.Opts, func(opts *FindOneAndUpdateOptions) error {\n\t\topts.ArrayFilters = filters\n\n\t\treturn nil\n\t})\n\n\treturn f\n}\n\n// SetBypassDocumentValidation sets the value for the BypassDocumentValidation field. If true,\n// writes executed as part of the operation will opt out of document-level validation on the server.\n// The default value is false. See https://www.mongodb.com/docs/manual/core/schema-validation/\n// for more information about document validation.\nfunc (f *FindOneAndUpdateOptionsBuilder) SetBypassDocumentValidation(b bool) *FindOneAndUpdateOptionsBuilder {\n\tf.Opts = append(f.Opts, func(opts *FindOneAndUpdateOptions) error {\n\t\topts.BypassDocumentValidation = &b\n\n\t\treturn nil\n\t})\n\n\treturn f\n}\n\n// SetCollation sets the value for the Collation field. Specifies a collation to\n// use for string comparisons during the operation. The default value is nil,\n// which means the default collation of the collection will be used.\nfunc (f *FindOneAndUpdateOptionsBuilder) SetCollation(collation *Collation) *FindOneAndUpdateOptionsBuilder {\n\tf.Opts = append(f.Opts, func(opts *FindOneAndUpdateOptions) error {\n\t\topts.Collation = collation\n\n\t\treturn nil\n\t})\n\n\treturn f\n}\n\n// SetComment sets the value for the Comment field. Specifies a string or document that will be\n// included in server logs, profiling logs, and currentOp queries to help trace the operation.\n// The default value is nil, which means that no comment will be included in the logs.\nfunc (f *FindOneAndUpdateOptionsBuilder) SetComment(comment any) *FindOneAndUpdateOptionsBuilder {\n\tf.Opts = append(f.Opts, func(opts *FindOneAndUpdateOptions) error {\n\t\topts.Comment = comment\n\n\t\treturn nil\n\t})\n\n\treturn f\n}\n\n// SetProjection sets the value for the Projection field. Sets a document describing which fields\n// will be included in the document returned by the operation. The default value is nil, which\n// means all fields will be included.\nfunc (f *FindOneAndUpdateOptionsBuilder) SetProjection(projection any) *FindOneAndUpdateOptionsBuilder {\n\tf.Opts = append(f.Opts, func(opts *FindOneAndUpdateOptions) error {\n\t\topts.Projection = projection\n\n\t\treturn nil\n\t})\n\n\treturn f\n}\n\n// SetReturnDocument sets the value for the ReturnDocument field. Specifies whether the original\n// or replaced document should be returned by the operation. The default value is Before, which\n// means the original document will be returned before the replacement is performed.\nfunc (f *FindOneAndUpdateOptionsBuilder) SetReturnDocument(rd ReturnDocument) *FindOneAndUpdateOptionsBuilder {\n\tf.Opts = append(f.Opts, func(opts *FindOneAndUpdateOptions) error {\n\t\topts.ReturnDocument = &rd\n\n\t\treturn nil\n\t})\n\n\treturn f\n}\n\n// SetSort sets the value for the Sort field. Sets a document specifying which document should\n// be updated if the filter used by the operation matches multiple documents in the collection.\n// If set, the first document in the sorted order will be updated. The sort parameter is evaluated\n// sequentially, so the driver will return an error if it is a multi-key map (which is unordeded).\n// The default value is nil.\nfunc (f *FindOneAndUpdateOptionsBuilder) SetSort(sort any) *FindOneAndUpdateOptionsBuilder {\n\tf.Opts = append(f.Opts, func(opts *FindOneAndUpdateOptions) error {\n\t\topts.Sort = sort\n\n\t\treturn nil\n\t})\n\n\treturn f\n}\n\n// SetUpsert sets the value for the Upsert field. If true, a new document will be inserted if\n// the filter does not match any documents in the collection. The default value is false.\nfunc (f *FindOneAndUpdateOptionsBuilder) SetUpsert(b bool) *FindOneAndUpdateOptionsBuilder {\n\tf.Opts = append(f.Opts, func(opts *FindOneAndUpdateOptions) error {\n\t\topts.Upsert = &b\n\n\t\treturn nil\n\t})\n\n\treturn f\n}\n\n// SetHint sets the value for the Hint field. Specifies the index to use for the operation.\n// This should either be the index name as a string or the index specification as a document.\n// This option is only valid for MongoDB versions >= 4.4. MongoDB version 4.2 will report an\n// error if this option is specified. For server versions < 4.2, the driver will return an\n// error if this option is specified. The driver will return an error if this option is used\n// with during an unacknowledged write operation. The driver will return an error if the\n// hint parameter is a multi-key map. The default value is nil, which means that no hint\n// will be sent.\nfunc (f *FindOneAndUpdateOptionsBuilder) SetHint(hint any) *FindOneAndUpdateOptionsBuilder {\n\tf.Opts = append(f.Opts, func(opts *FindOneAndUpdateOptions) error {\n\t\topts.Hint = hint\n\n\t\treturn nil\n\t})\n\n\treturn f\n}\n\n// SetLet sets the value for the Let field. Specifies parameters for the find one and update\n// expression. This option is only valid for MongoDB versions >= 5.0. Older servers will\n// report an error for using this option. This must be a document mapping parameter names\n// to values. Values must be constant or closed expressions that do not reference document\n// fields. Parameters can then be accessed as variables in an aggregate expression context\n// (e.g. \"$$var\").\nfunc (f *FindOneAndUpdateOptionsBuilder) SetLet(let any) *FindOneAndUpdateOptionsBuilder {\n\tf.Opts = append(f.Opts, func(opts *FindOneAndUpdateOptions) error {\n\t\topts.Let = let\n\n\t\treturn nil\n\t})\n\n\treturn f\n}\n\n// FindOneAndDeleteOptions represents arguments that can be used to configure a\n// FindOneAndDelete operation.\n//\n// See corresponding setter methods for documentation.\ntype FindOneAndDeleteOptions struct {\n\tCollation  *Collation\n\tComment    any\n\tProjection any\n\tSort       any\n\tHint       any\n\tLet        any\n\n\t// Deprecated: This option is for internal use only and should not be set. It may be changed or removed in any\n\t// release.\n\tInternal optionsutil.Options\n}\n\n// FindOneAndDeleteOptionsBuilder contains options to configure delete\n// operations. Each option can be set through setter functions. See\n// documentation for each setter function for an explanation of the option.\ntype FindOneAndDeleteOptionsBuilder struct {\n\tOpts []func(*FindOneAndDeleteOptions) error\n}\n\n// FindOneAndDelete creates a new FindOneAndDeleteOptions instance.\nfunc FindOneAndDelete() *FindOneAndDeleteOptionsBuilder {\n\treturn &FindOneAndDeleteOptionsBuilder{}\n}\n\n// List returns a list of FindOneAndDeleteOptions setter functions.\nfunc (f *FindOneAndDeleteOptionsBuilder) List() []func(*FindOneAndDeleteOptions) error {\n\treturn f.Opts\n}\n\n// SetCollation sets the value for the Collation field. Specifies a collation to\n// use for string comparisons during the operation. The default value is nil,\n// which means the default collation of the collection will be used.\nfunc (f *FindOneAndDeleteOptionsBuilder) SetCollation(collation *Collation) *FindOneAndDeleteOptionsBuilder {\n\tf.Opts = append(f.Opts, func(opts *FindOneAndDeleteOptions) error {\n\t\topts.Collation = collation\n\n\t\treturn nil\n\t})\n\n\treturn f\n}\n\n// SetComment sets the value for the Comment field. Specifies a string or document that will be\n// included in server logs, profiling logs, and currentOp queries to help trace the operation.\n// The default value is nil, which means that no comment will be included in the logs.\nfunc (f *FindOneAndDeleteOptionsBuilder) SetComment(comment any) *FindOneAndDeleteOptionsBuilder {\n\tf.Opts = append(f.Opts, func(opts *FindOneAndDeleteOptions) error {\n\t\topts.Comment = comment\n\n\t\treturn nil\n\t})\n\n\treturn f\n}\n\n// SetProjection sets the value for the Projection field. Sets a document describing which fields\n// will be included in the document returned by the operation. The default value is nil, which\n// means all fields will be included.\nfunc (f *FindOneAndDeleteOptionsBuilder) SetProjection(projection any) *FindOneAndDeleteOptionsBuilder {\n\tf.Opts = append(f.Opts, func(opts *FindOneAndDeleteOptions) error {\n\t\topts.Projection = projection\n\n\t\treturn nil\n\t})\n\n\treturn f\n}\n\n// SetSort sets the value for the Sort field. Sets a document specifying which document should\n// be replaced if the filter used by the operation matches multiple documents in the collection.\n// If set, the first document in the sorted order will be deleted. The sort parameter is evaluated\n// sequentially, so the driver will return an error if it is a multi-key map (which is unordeded).\n// The default value is nil.\nfunc (f *FindOneAndDeleteOptionsBuilder) SetSort(sort any) *FindOneAndDeleteOptionsBuilder {\n\tf.Opts = append(f.Opts, func(opts *FindOneAndDeleteOptions) error {\n\t\topts.Sort = sort\n\n\t\treturn nil\n\t})\n\n\treturn f\n}\n\n// SetHint sets the value for the Hint field. Specifies the index to use for the operation.\n// This should either be the index name as a string or the index specification as a document.\n// This option is only valid for MongoDB versions >= 4.4. MongoDB version 4.2 will report an\n// error if this option is specified. For server versions < 4.2, the driver will return an\n// error if this option is specified. The driver will return an error if this option is used\n// with during an unacknowledged write operation. The driver will return an error if the hint\n// parameter is a multi-key map. The default value is nil, which means that no hint will be sent.\nfunc (f *FindOneAndDeleteOptionsBuilder) SetHint(hint any) *FindOneAndDeleteOptionsBuilder {\n\tf.Opts = append(f.Opts, func(opts *FindOneAndDeleteOptions) error {\n\t\topts.Hint = hint\n\n\t\treturn nil\n\t})\n\n\treturn f\n}\n\n// SetLet sets the value for the Let field. Specifies parameters for the find one and delete\n// expression. This option is only valid for MongoDB versions >= 5.0. Older servers will\n// report an error for using this option. This must be a document mapping parameter names to\n// values. Values must be constant or closed expressions that do not reference document fields.\n// Parameters can then be accessed as variables in an aggregate expression context (e.g. \"$$var\").\nfunc (f *FindOneAndDeleteOptionsBuilder) SetLet(let any) *FindOneAndDeleteOptionsBuilder {\n\tf.Opts = append(f.Opts, func(opts *FindOneAndDeleteOptions) error {\n\t\topts.Let = let\n\n\t\treturn nil\n\t})\n\n\treturn f\n}\n"
  },
  {
    "path": "mongo/options/gridfsoptions.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage options\n\nimport (\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/readconcern\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/readpref\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/writeconcern\"\n)\n\n// DefaultName is the default name for a GridFS bucket.\nvar DefaultName = \"fs\"\n\n// DefaultChunkSize is the default size of each file chunk in bytes (255 KiB).\nvar DefaultChunkSize int32 = 255 * 1024\n\n// DefaultRevision is the default revision number for a download by name operation.\nvar DefaultRevision int32 = -1\n\n// BucketOptions represents arguments that can be used to configure GridFS\n// bucket.\n//\n// See corresponding setter methods for documentation.\ntype BucketOptions struct {\n\tName           *string\n\tChunkSizeBytes *int32\n\tWriteConcern   *writeconcern.WriteConcern\n\tReadConcern    *readconcern.ReadConcern\n\tReadPreference *readpref.ReadPref\n}\n\n// BucketOptionsBuilder contains options to configure a gridfs bucket. Each\n// option can be set through setter functions. See documentation for each setter\n// function for an explanation of the option.\ntype BucketOptionsBuilder struct {\n\tOpts []func(*BucketOptions) error\n}\n\n// GridFSBucket creates a new BucketOptions instance.\nfunc GridFSBucket() *BucketOptionsBuilder {\n\tbo := &BucketOptionsBuilder{}\n\tbo.SetName(DefaultName).SetChunkSizeBytes(DefaultChunkSize)\n\n\treturn bo\n}\n\n// List returns a list of CountOptions setter functions.\nfunc (b *BucketOptionsBuilder) List() []func(*BucketOptions) error {\n\treturn b.Opts\n}\n\n// SetName sets the value for the Name field. Specifies the name of the bucket.\n// The default value is \"fs\".\nfunc (b *BucketOptionsBuilder) SetName(name string) *BucketOptionsBuilder {\n\tb.Opts = append(b.Opts, func(opts *BucketOptions) error {\n\t\topts.Name = &name\n\n\t\treturn nil\n\t})\n\n\treturn b\n}\n\n// SetChunkSizeBytes sets the value for the ChunkSize field. Specifies the number\n// of bytes in each chunk in the bucket. The default value is 255 KiB.\nfunc (b *BucketOptionsBuilder) SetChunkSizeBytes(i int32) *BucketOptionsBuilder {\n\tb.Opts = append(b.Opts, func(opts *BucketOptions) error {\n\t\topts.ChunkSizeBytes = &i\n\n\t\treturn nil\n\t})\n\n\treturn b\n}\n\n// SetWriteConcern sets the value for the WriteConcern field. Specifies the write\n// concern for the bucket. The default value is the write concern of the database\n// from which the bucket is created.\nfunc (b *BucketOptionsBuilder) SetWriteConcern(wc *writeconcern.WriteConcern) *BucketOptionsBuilder {\n\tb.Opts = append(b.Opts, func(opts *BucketOptions) error {\n\t\topts.WriteConcern = wc\n\n\t\treturn nil\n\t})\n\n\treturn b\n}\n\n// SetReadConcern sets the value for the ReadConcern field. Specifies the read\n// concern for the bucket. The default value is the read concern of the database\n// from which the bucket is created.\nfunc (b *BucketOptionsBuilder) SetReadConcern(rc *readconcern.ReadConcern) *BucketOptionsBuilder {\n\tb.Opts = append(b.Opts, func(opts *BucketOptions) error {\n\t\topts.ReadConcern = rc\n\n\t\treturn nil\n\t})\n\n\treturn b\n}\n\n// SetReadPreference sets the value for the ReadPreference field. Specifies the\n// read preference for the bucket. The default value is the read preference of\n// the database from which the bucket is created.\nfunc (b *BucketOptionsBuilder) SetReadPreference(rp *readpref.ReadPref) *BucketOptionsBuilder {\n\tb.Opts = append(b.Opts, func(opts *BucketOptions) error {\n\t\topts.ReadPreference = rp\n\n\t\treturn nil\n\t})\n\n\treturn b\n}\n\n// GridFSUploadOptions represents arguments that can be used to configure a GridFS\n// upload operation.\n//\n// See corresponding setter methods for documentation.\ntype GridFSUploadOptions struct {\n\tChunkSizeBytes *int32\n\tMetadata       any\n\tRegistry       *bson.Registry\n}\n\n// GridFSUploadOptionsBuilder contains options to configure a GridFS Upload.\n// Each option can be set through setter functions. See documentation for each\n// setter function for an explanation of the option.\ntype GridFSUploadOptionsBuilder struct {\n\tOpts []func(*GridFSUploadOptions) error\n}\n\n// GridFSUpload creates a new GridFSUploadOptions instance.\nfunc GridFSUpload() *GridFSUploadOptionsBuilder {\n\topts := &GridFSUploadOptionsBuilder{}\n\topts.SetRegistry(defaultRegistry)\n\n\treturn opts\n}\n\n// List returns a list of GridFSUploadOptions setter functions.\nfunc (u *GridFSUploadOptionsBuilder) List() []func(*GridFSUploadOptions) error {\n\treturn u.Opts\n}\n\n// SetChunkSizeBytes sets the value for the ChunkSize field. Specifies the number of\n// bytes in each chunk in the bucket. The default value is DefaultChunkSize (255 KiB).\nfunc (u *GridFSUploadOptionsBuilder) SetChunkSizeBytes(i int32) *GridFSUploadOptionsBuilder {\n\tu.Opts = append(u.Opts, func(opts *GridFSUploadOptions) error {\n\t\topts.ChunkSizeBytes = &i\n\n\t\treturn nil\n\t})\n\n\treturn u\n}\n\n// SetMetadata sets the value for the Metadata field. Specifies additional application data\n// that will be stored in the \"metadata\" field of the document in the files collection.\n// The default value is nil, which means that the document in the files collection will\n// not contain a \"metadata\" field.\nfunc (u *GridFSUploadOptionsBuilder) SetMetadata(doc any) *GridFSUploadOptionsBuilder {\n\tu.Opts = append(u.Opts, func(opts *GridFSUploadOptions) error {\n\t\topts.Metadata = doc\n\n\t\treturn nil\n\t})\n\n\treturn u\n}\n\n// SetRegistry sets the bson codec registry for the Registry field. Specifies the BSON\n// registry to use for converting filters to BSON documents. The default value is\n// bson.NewRegistry().\nfunc (u *GridFSUploadOptionsBuilder) SetRegistry(registry *bson.Registry) *GridFSUploadOptionsBuilder {\n\tu.Opts = append(u.Opts, func(opts *GridFSUploadOptions) error {\n\t\topts.Registry = registry\n\n\t\treturn nil\n\t})\n\n\treturn u\n}\n\n// GridFSNameOptions represents arguments that can be used to configure a GridFS\n// DownloadByName operation.\n//\n// See corresponding setter methods for documentation.\ntype GridFSNameOptions struct {\n\tRevision *int32\n}\n\n// GridFSNameOptionsBuilder contains options to configure a GridFS name. Each\n// option can be set through setter functions. See documentation for each setter\n// function for an explanation of the option.\ntype GridFSNameOptionsBuilder struct {\n\tOpts []func(*GridFSNameOptions) error\n}\n\n// GridFSName creates a new GridFSNameOptions instance.\nfunc GridFSName() *GridFSNameOptionsBuilder {\n\treturn &GridFSNameOptionsBuilder{}\n}\n\n// List returns a list of GridFSNameOptions setter functions.\nfunc (n *GridFSNameOptionsBuilder) List() []func(*GridFSNameOptions) error {\n\treturn n.Opts\n}\n\n// SetRevision sets the value for the Revision field. Specifies the revision\n// of the file to retrieve. Revision numbers are defined as follows:\n//\n// * 0 = the original stored file\n// * 1 = the first revision\n// * 2 = the second revision\n// * etc..\n// * -2 = the second most recent revision\n// * -1 = the most recent revision.\n//\n// The default value is -1\nfunc (n *GridFSNameOptionsBuilder) SetRevision(r int32) *GridFSNameOptionsBuilder {\n\tn.Opts = append(n.Opts, func(opts *GridFSNameOptions) error {\n\t\topts.Revision = &r\n\n\t\treturn nil\n\t})\n\n\treturn n\n}\n\n// GridFSFindOptions represents arguments that can be used to configure a GridFS\n// Find operation.\n//\n// See corresponding setter methods for documentation.\ntype GridFSFindOptions struct {\n\tAllowDiskUse    *bool\n\tBatchSize       *int32\n\tLimit           *int32\n\tNoCursorTimeout *bool\n\tSkip            *int32\n\tSort            any\n}\n\n// GridFSFindOptionsBuilder contains options to configure find operations. Each\n// option can be set through setter functions. See documentation for each setter\n// function for an explanation of the option.\ntype GridFSFindOptionsBuilder struct {\n\tOpts []func(*GridFSFindOptions) error\n}\n\n// GridFSFind creates a new GridFSFindOptions instance.\nfunc GridFSFind() *GridFSFindOptionsBuilder {\n\treturn &GridFSFindOptionsBuilder{}\n}\n\n// List returns a list of GridFSFindOptions setter functions.\nfunc (f *GridFSFindOptionsBuilder) List() []func(*GridFSFindOptions) error {\n\treturn f.Opts\n}\n\n// SetAllowDiskUse sets the value for the AllowDiskUse field. If true, the server can\n// write temporary data to disk while executing the find operation. The default value\n// is false. This option is only valid for MongoDB versions >= 4.4. For previous server\n// versions, the server will return an error if this option is used.\nfunc (f *GridFSFindOptionsBuilder) SetAllowDiskUse(b bool) *GridFSFindOptionsBuilder {\n\tf.Opts = append(f.Opts, func(opts *GridFSFindOptions) error {\n\t\topts.AllowDiskUse = &b\n\n\t\treturn nil\n\t})\n\n\treturn f\n}\n\n// SetBatchSize sets the value for the BatchSize field. Specifies the maximum number\n// of documents to be included in each batch returned by the server.\nfunc (f *GridFSFindOptionsBuilder) SetBatchSize(i int32) *GridFSFindOptionsBuilder {\n\tf.Opts = append(f.Opts, func(opts *GridFSFindOptions) error {\n\t\topts.BatchSize = &i\n\n\t\treturn nil\n\t})\n\n\treturn f\n}\n\n// SetLimit sets the value for the Limit field. Specifies the maximum number of\n// documents to return. The default value is 0, which means that all documents\n// matching the filter will be returned. A negative limit specifies that the\n// resulting documents should be returned in a single batch. The default value is 0.\nfunc (f *GridFSFindOptionsBuilder) SetLimit(i int32) *GridFSFindOptionsBuilder {\n\tf.Opts = append(f.Opts, func(opts *GridFSFindOptions) error {\n\t\topts.Limit = &i\n\n\t\treturn nil\n\t})\n\n\treturn f\n}\n\n// SetNoCursorTimeout sets the value for the NoCursorTimeout field. If true, the\n// cursor created by the operation will not timeout after a period of inactivity.\n// The default value is false.\nfunc (f *GridFSFindOptionsBuilder) SetNoCursorTimeout(b bool) *GridFSFindOptionsBuilder {\n\tf.Opts = append(f.Opts, func(opts *GridFSFindOptions) error {\n\t\topts.NoCursorTimeout = &b\n\n\t\treturn nil\n\t})\n\n\treturn f\n}\n\n// SetSkip sets the value for the Skip field. Specifies the number of documents\n// to skip before adding documents to the result. The default value is 0.\nfunc (f *GridFSFindOptionsBuilder) SetSkip(i int32) *GridFSFindOptionsBuilder {\n\tf.Opts = append(f.Opts, func(opts *GridFSFindOptions) error {\n\t\topts.Skip = &i\n\n\t\treturn nil\n\t})\n\n\treturn f\n}\n\n// SetSort sets the value for the Sort field. Sets a document specifying the order\n// in which documents should be returned. The sort parameter is evaluated sequentially,\n// so the driver will return an error if it is a multi-key map (which is unordeded).\n// The default value is nil.\nfunc (f *GridFSFindOptionsBuilder) SetSort(sort any) *GridFSFindOptionsBuilder {\n\tf.Opts = append(f.Opts, func(opts *GridFSFindOptions) error {\n\t\topts.Sort = sort\n\n\t\treturn nil\n\t})\n\n\treturn f\n}\n"
  },
  {
    "path": "mongo/options/indexoptions.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage options\n\nimport \"go.mongodb.org/mongo-driver/v2/internal/optionsutil\"\n\n// CreateIndexesOptions represents arguments that can be used to configure\n// IndexView.CreateOne and IndexView.CreateMany operations.\n//\n// See corresponding setter methods for documentation.\ntype CreateIndexesOptions struct {\n\tCommitQuorum any\n\n\t// Deprecated: This option is for internal use only and should not be set. It may be changed or removed in any\n\t// release.\n\tInternal optionsutil.Options\n}\n\n// CreateIndexesOptionsBuilder contains options to create indexes. Each option\n// can be set through setter functions. See documentation for each setter\n// function for an explanation of the option.\n//\n// See corresponding setter methods for documentation.\ntype CreateIndexesOptionsBuilder struct {\n\tOpts []func(*CreateIndexesOptions) error\n}\n\n// CreateIndexes creates a new CreateIndexesOptions instance.\nfunc CreateIndexes() *CreateIndexesOptionsBuilder {\n\treturn &CreateIndexesOptionsBuilder{}\n}\n\n// List returns a list of CreateIndexesOptions setter functions.\nfunc (c *CreateIndexesOptionsBuilder) List() []func(*CreateIndexesOptions) error {\n\treturn c.Opts\n}\n\n// SetCommitQuorumInt sets the value for the CommitQuorum field as an int32.\n// Specifies the number of data-bearing members of a replica set, including the primary,\n// that must complete the index builds successfully before the primary marks the indexes\n// as ready.\n//\n// Semantics for int: the number of members that must complete the build.\n//\n// This option is only available on MongoDB versions >= 4.4. A client-side error will\n// be returned if the option is specified for MongoDB versions <= 4.2. The default\n// value is nil, meaning that the server-side default will be used. See\n// dochub.mongodb.org/core/index-commit-quorum for more information.\nfunc (c *CreateIndexesOptionsBuilder) SetCommitQuorumInt(quorum int32) *CreateIndexesOptionsBuilder {\n\tc.Opts = append(c.Opts, func(opts *CreateIndexesOptions) error {\n\t\topts.CommitQuorum = quorum\n\n\t\treturn nil\n\t})\n\n\treturn c\n}\n\n// SetCommitQuorumString sets the value for the CommitQuorum field as a string.\n// Specifies the number of data-bearing members of a replica set, including the primary,\n// that must complete the index builds successfully before the primary marks the indexes\n// as ready.\n//\n// Semantics for String: specifies a tag. All members with that tag must complete the build.\n//\n// This option is only available on MongoDB versions >= 4.4. A client-side error will\n// be returned if the option is specified for MongoDB versions <= 4.2. The default\n// value is nil, meaning that the server-side default will be used. See\n// dochub.mongodb.org/core/index-commit-quorum for more information.\nfunc (c *CreateIndexesOptionsBuilder) SetCommitQuorumString(quorum string) *CreateIndexesOptionsBuilder {\n\tc.Opts = append(c.Opts, func(opts *CreateIndexesOptions) error {\n\t\topts.CommitQuorum = quorum\n\n\t\treturn nil\n\t})\n\n\treturn c\n}\n\n// SetCommitQuorumMajority sets the value for the CommitQuorum to special \"majority\" value.\n// Specifies the number of data-bearing members of a replica set, including the primary,\n// that must complete the index builds successfully before the primary marks the indexes\n// as ready.\n//\n// Semantics for \"majority\": A special value to indicate that more than half the nodes\n// must complete the build.\n//\n// This option is only available on MongoDB versions >= 4.4. A client-side error will\n// be returned if the option is specified for MongoDB versions <= 4.2. The default\n// value is nil, meaning that the server-side default will be used. See\n// dochub.mongodb.org/core/index-commit-quorum for more information.\nfunc (c *CreateIndexesOptionsBuilder) SetCommitQuorumMajority() *CreateIndexesOptionsBuilder {\n\tc.Opts = append(c.Opts, func(opts *CreateIndexesOptions) error {\n\t\topts.CommitQuorum = \"majority\"\n\n\t\treturn nil\n\t})\n\n\treturn c\n}\n\n// SetCommitQuorumVotingMembers sets the value for the CommitQuorum to special \"votingMembers\" value.\n// Specifies the number of data-bearing members of a replica set, including the primary,\n// that must complete the index builds successfully before the primary marks the indexes\n// as ready.\n//\n// Semantics for \"votingMembers\": A special value to indicate that all voting data-bearing\n// nodes must complete.\n//\n// This option is only available on MongoDB versions >= 4.4. A client-side error will\n// be returned if the option is specified for MongoDB versions <= 4.2. The default\n// value is nil, meaning that the server-side default will be used. See\n// dochub.mongodb.org/core/index-commit-quorum for more information.\nfunc (c *CreateIndexesOptionsBuilder) SetCommitQuorumVotingMembers() *CreateIndexesOptionsBuilder {\n\tc.Opts = append(c.Opts, func(opts *CreateIndexesOptions) error {\n\t\topts.CommitQuorum = \"votingMembers\"\n\n\t\treturn nil\n\t})\n\n\treturn c\n}\n\n// DropIndexesOptions represents arguments that can be used to configure\n// IndexView.DropOne and IndexView.DropAll operations.\ntype DropIndexesOptions struct {\n\t// Deprecated: This option is for internal use only and should not be set. It may be changed or removed in any\n\t// release.\n\tInternal optionsutil.Options\n}\n\n// DropIndexesOptionsBuilder contains options to configure dropping indexes.\n// Each option can be set through setter functions. See documentation for each\n// setter function for an explanation of the option.\ntype DropIndexesOptionsBuilder struct {\n\tOpts []func(*DropIndexesOptions) error\n}\n\n// DropIndexes creates a new DropIndexesOptions instance.\nfunc DropIndexes() *DropIndexesOptionsBuilder {\n\treturn &DropIndexesOptionsBuilder{}\n}\n\n// List returns a list of DropIndexesOptions setter functions.\nfunc (d *DropIndexesOptionsBuilder) List() []func(*DropIndexesOptions) error {\n\treturn d.Opts\n}\n\n// ListIndexesOptions represents arguments that can be used to configure an\n// IndexView.List operation.\n//\n// See corresponding setter methods for documentation.\ntype ListIndexesOptions struct {\n\tBatchSize *int32\n\n\t// Deprecated: This option is for internal use only and should not be set. It may be changed or removed in any\n\t// release.\n\tInternal optionsutil.Options\n}\n\n// ListIndexesOptionsBuilder contains options to configure count operations. Each\n// option can be set through setter functions. See documentation for each setter\n// function for an explanation of the option.\ntype ListIndexesOptionsBuilder struct {\n\tOpts []func(*ListIndexesOptions) error\n}\n\n// ListIndexes creates a new ListIndexesOptions instance.\nfunc ListIndexes() *ListIndexesOptionsBuilder {\n\treturn &ListIndexesOptionsBuilder{}\n}\n\n// List returns a list of CountOptions setter functions.\nfunc (l *ListIndexesOptionsBuilder) List() []func(*ListIndexesOptions) error {\n\treturn l.Opts\n}\n\n// SetBatchSize sets the value for the BatchSize field. Specifies the maximum number\n// of documents to be included in each batch returned by the server.\nfunc (l *ListIndexesOptionsBuilder) SetBatchSize(i int32) *ListIndexesOptionsBuilder {\n\tl.Opts = append(l.Opts, func(opts *ListIndexesOptions) error {\n\t\topts.BatchSize = &i\n\n\t\treturn nil\n\t})\n\n\treturn l\n}\n\n// IndexOptions represents arguments that can be used to configure a new index\n// created through the IndexView.CreateOne or IndexView.CreateMany operations.\n//\n// See corresponding setter methods for documentation.\ntype IndexOptions struct {\n\tExpireAfterSeconds      *int32\n\tName                    *string\n\tSparse                  *bool\n\tStorageEngine           any\n\tUnique                  *bool\n\tVersion                 *int32\n\tDefaultLanguage         *string\n\tLanguageOverride        *string\n\tTextVersion             *int32\n\tWeights                 any\n\tSphereVersion           *int32\n\tBits                    *int32\n\tMax                     *float64\n\tMin                     *float64\n\tBucketSize              *int32\n\tPartialFilterExpression any\n\tCollation               *Collation\n\tWildcardProjection      any\n\tHidden                  *bool\n}\n\n// IndexOptionsBuilder contains options to configure index operations. Each option\n// can be set through setter functions. See documentation for each setter\n// function for an explanation of the option.\ntype IndexOptionsBuilder struct {\n\tOpts []func(*IndexOptions) error\n}\n\n// Index creates a new IndexOptions instance.\nfunc Index() *IndexOptionsBuilder {\n\treturn &IndexOptionsBuilder{}\n}\n\n// List returns a list of IndexOptions setter functions.\nfunc (i *IndexOptionsBuilder) List() []func(*IndexOptions) error {\n\treturn i.Opts\n}\n\n// SetExpireAfterSeconds sets value for the ExpireAfterSeconds field. Specifies the length\n// of time, in seconds, for documents to remain in the collection. The default value is 0,\n// which means that documents will remain in the collection until they're explicitly\n// deleted or the collection is dropped.\nfunc (i *IndexOptionsBuilder) SetExpireAfterSeconds(seconds int32) *IndexOptionsBuilder {\n\ti.Opts = append(i.Opts, func(opts *IndexOptions) error {\n\t\topts.ExpireAfterSeconds = &seconds\n\n\t\treturn nil\n\t})\n\n\treturn i\n}\n\n// SetName sets the value for the Name field. Specifies the name of the index. The default\n// value is \"[field1]_[direction1]_[field2]_[direction2]...\". For example, an index with\n// the specification {name: 1, age: -1} will be named \"name_1_age_-1\".\nfunc (i *IndexOptionsBuilder) SetName(name string) *IndexOptionsBuilder {\n\ti.Opts = append(i.Opts, func(opts *IndexOptions) error {\n\t\topts.Name = &name\n\n\t\treturn nil\n\t})\n\n\treturn i\n}\n\n// SetSparse sets the value of the Sparse field. If true, the index will only reference\n// documents that contain the fields specified in the index. The default is false.\nfunc (i *IndexOptionsBuilder) SetSparse(sparse bool) *IndexOptionsBuilder {\n\ti.Opts = append(i.Opts, func(opts *IndexOptions) error {\n\t\topts.Sparse = &sparse\n\n\t\treturn nil\n\t})\n\n\treturn i\n}\n\n// SetStorageEngine sets the value for the StorageEngine field. Specifies the\n// storage engine to use for the index. The value must be a document in the form\n// {<storage engine name>: <options>}. The default value is nil, which means that\n// the default storage engine will be used.\nfunc (i *IndexOptionsBuilder) SetStorageEngine(engine any) *IndexOptionsBuilder {\n\ti.Opts = append(i.Opts, func(opts *IndexOptions) error {\n\t\topts.StorageEngine = engine\n\n\t\treturn nil\n\t})\n\n\treturn i\n}\n\n// SetUnique sets the value for the Unique field. If true, the collection will not\n// accept insertion or update of documents where the index key value matches an\n// existing value in the index. The default is false.\nfunc (i *IndexOptionsBuilder) SetUnique(unique bool) *IndexOptionsBuilder {\n\ti.Opts = append(i.Opts, func(opts *IndexOptions) error {\n\t\topts.Unique = &unique\n\n\t\treturn nil\n\t})\n\n\treturn i\n}\n\n// SetVersion sets the value for the Version field. Specifies the index version\n// number, either 0 or 1.\nfunc (i *IndexOptionsBuilder) SetVersion(version int32) *IndexOptionsBuilder {\n\ti.Opts = append(i.Opts, func(opts *IndexOptions) error {\n\t\topts.Version = &version\n\n\t\treturn nil\n\t})\n\n\treturn i\n}\n\n// SetDefaultLanguage sets the value for the DefaultLanguage field. Specifies the\n// language that determines the list of stop words and the rules for the stemmer\n// and tokenizer. This option is only applicable for text indexes and is ignored for\n// other index types. The default value is \"english\".\nfunc (i *IndexOptionsBuilder) SetDefaultLanguage(language string) *IndexOptionsBuilder {\n\ti.Opts = append(i.Opts, func(opts *IndexOptions) error {\n\t\topts.DefaultLanguage = &language\n\n\t\treturn nil\n\t})\n\n\treturn i\n}\n\n// SetLanguageOverride sets the value of the LanguageOverride field. Specifies the name\n// of the field in the collection's documents that contains the override language for the\n// document. This option is only applicable for text indexes and is ignored for other index\n// types. The default value is the value of the DefaultLanguage option.\nfunc (i *IndexOptionsBuilder) SetLanguageOverride(override string) *IndexOptionsBuilder {\n\ti.Opts = append(i.Opts, func(opts *IndexOptions) error {\n\t\topts.LanguageOverride = &override\n\n\t\treturn nil\n\t})\n\n\treturn i\n}\n\n// SetTextVersion sets the value for the TextVersion field. Specifies the index version number\n// for a text index. See https://www.mongodb.com/docs/manual/core/index-text/#text-versions\n// for information about different version numbers.\nfunc (i *IndexOptionsBuilder) SetTextVersion(version int32) *IndexOptionsBuilder {\n\ti.Opts = append(i.Opts, func(opts *IndexOptions) error {\n\t\topts.TextVersion = &version\n\n\t\treturn nil\n\t})\n\n\treturn i\n}\n\n// SetWeights sets the value for the Weights field. Sets a document that contains field\n// and weight pairs. The weight is an integer ranging from 1 to 99,999, inclusive,\n// indicating the significance of the field relative to the other indexed fields in\n// terms of the score. This option is only applicable for text indexes and is ignored\n// for other index types. The default value is nil, which means that every field will\n// have a weight of 1.\nfunc (i *IndexOptionsBuilder) SetWeights(weights any) *IndexOptionsBuilder {\n\ti.Opts = append(i.Opts, func(opts *IndexOptions) error {\n\t\topts.Weights = weights\n\n\t\treturn nil\n\t})\n\n\treturn i\n}\n\n// SetSphereVersion sets the value for the SphereVersion field. Specifies the index version number\n// for a 2D sphere index. See https://www.mongodb.com/docs/manual/core/2dsphere/#dsphere-v2 for\n// information about different version numbers.\nfunc (i *IndexOptionsBuilder) SetSphereVersion(version int32) *IndexOptionsBuilder {\n\ti.Opts = append(i.Opts, func(opts *IndexOptions) error {\n\t\topts.SphereVersion = &version\n\n\t\treturn nil\n\t})\n\n\treturn i\n}\n\n// SetBits sets the value for the Bits field. Specifies the precision of the stored geohash\n// value of the location data. This option only applies to 2D indexes and is ignored for\n// other index types. The value must be between 1 and 32, inclusive. The default value is 26.\nfunc (i *IndexOptionsBuilder) SetBits(bits int32) *IndexOptionsBuilder {\n\ti.Opts = append(i.Opts, func(opts *IndexOptions) error {\n\t\topts.Bits = &bits\n\n\t\treturn nil\n\t})\n\n\treturn i\n}\n\n// SetMax sets the value for the Max field. Specifies the upper inclusive boundary for\n// longitude and latitude values. This option is only applicable to 2D indexes and\n// is ignored for other index types. The default value is 180.0.\nfunc (i *IndexOptionsBuilder) SetMax(max float64) *IndexOptionsBuilder {\n\ti.Opts = append(i.Opts, func(opts *IndexOptions) error {\n\t\topts.Max = &max\n\n\t\treturn nil\n\t})\n\n\treturn i\n}\n\n// SetMin sets the value for the Min field. Specifies the lower inclusive boundary for\n// longitude and latitude values. This option is only applicable to 2D indexes and\n// is ignored for other index types. The default value is -180.0.\nfunc (i *IndexOptionsBuilder) SetMin(min float64) *IndexOptionsBuilder {\n\ti.Opts = append(i.Opts, func(opts *IndexOptions) error {\n\t\topts.Min = &min\n\n\t\treturn nil\n\t})\n\n\treturn i\n}\n\n// SetBucketSize sets the value for the BucketSize field. Specifies the number of units\n// within which to group location values. Location values that are within BucketSize\n// units of each other will be grouped in the same bucket. This option is only applicable\n// to geoHaystack indexes and is ignored for other index types. The value must be greater\n// than 0.\nfunc (i *IndexOptionsBuilder) SetBucketSize(bucketSize int32) *IndexOptionsBuilder {\n\ti.Opts = append(i.Opts, func(opts *IndexOptions) error {\n\t\topts.BucketSize = &bucketSize\n\n\t\treturn nil\n\t})\n\n\treturn i\n}\n\n// SetPartialFilterExpression sets the value for the PartialFilterExpression field. Sets\n// a document that defines which collection documents the index should reference.\nfunc (i *IndexOptionsBuilder) SetPartialFilterExpression(expression any) *IndexOptionsBuilder {\n\ti.Opts = append(i.Opts, func(opts *IndexOptions) error {\n\t\topts.PartialFilterExpression = expression\n\n\t\treturn nil\n\t})\n\n\treturn i\n}\n\n// SetCollation sets the value for the Collation field. Specifies the collation to use for\n// string comparisons for the index.\nfunc (i *IndexOptionsBuilder) SetCollation(collation *Collation) *IndexOptionsBuilder {\n\ti.Opts = append(i.Opts, func(opts *IndexOptions) error {\n\t\topts.Collation = collation\n\n\t\treturn nil\n\t})\n\n\treturn i\n}\n\n// SetWildcardProjection sets the value for the WildcardProjection field. Sets a document\n// that defines the wildcard projection for the index.\nfunc (i *IndexOptionsBuilder) SetWildcardProjection(wildcardProjection any) *IndexOptionsBuilder {\n\ti.Opts = append(i.Opts, func(opts *IndexOptions) error {\n\t\topts.WildcardProjection = wildcardProjection\n\n\t\treturn nil\n\t})\n\n\treturn i\n}\n\n// SetHidden sets the value for the Hidden field. If true, the index will exist on the\n// target collection but will not be used by the query planner when executing operations.\n// This option is only valid for MongoDB versions >= 4.4. The default value is false.\nfunc (i *IndexOptionsBuilder) SetHidden(hidden bool) *IndexOptionsBuilder {\n\ti.Opts = append(i.Opts, func(opts *IndexOptions) error {\n\t\topts.Hidden = &hidden\n\n\t\treturn nil\n\t})\n\n\treturn i\n}\n"
  },
  {
    "path": "mongo/options/insertoptions.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage options\n\nimport \"go.mongodb.org/mongo-driver/v2/internal/optionsutil\"\n\n// InsertOneOptions represents arguments that can be used to configure an InsertOne\n// operation.\n//\n// See corresponding setter methods for documentation.\ntype InsertOneOptions struct {\n\tBypassDocumentValidation *bool\n\tComment                  any\n\n\t// Deprecated: This option is for internal use only and should not be set. It may be changed or removed in any\n\t// release.\n\tInternal optionsutil.Options\n}\n\n// InsertOneOptionsBuilder represents functional options that configure an\n// InsertOneopts.\ntype InsertOneOptionsBuilder struct {\n\tOpts []func(*InsertOneOptions) error\n}\n\n// InsertOne creates a new InsertOneOptions instance.\nfunc InsertOne() *InsertOneOptionsBuilder {\n\treturn &InsertOneOptionsBuilder{}\n}\n\n// List returns a list of InsertOneOptions setter functions.\nfunc (ioo *InsertOneOptionsBuilder) List() []func(*InsertOneOptions) error {\n\treturn ioo.Opts\n}\n\n// SetBypassDocumentValidation sets the value for the BypassDocumentValidation field. If true,\n// writes executed as part of the operation will opt out of document-level validation on the\n// server. The default value is false. See https://www.mongodb.com/docs/manual/core/schema-validation/\n// for more information about document validation.\nfunc (ioo *InsertOneOptionsBuilder) SetBypassDocumentValidation(b bool) *InsertOneOptionsBuilder {\n\tioo.Opts = append(ioo.Opts, func(opts *InsertOneOptions) error {\n\t\topts.BypassDocumentValidation = &b\n\t\treturn nil\n\t})\n\treturn ioo\n}\n\n// SetComment sets the value for the Comment field. Specifies a string or document that will be included in server logs, profiling logs, and currentOp queries to help trace\n// the operation.  The default value is nil, which means that no comment will be included in the logs.\nfunc (ioo *InsertOneOptionsBuilder) SetComment(comment any) *InsertOneOptionsBuilder {\n\tioo.Opts = append(ioo.Opts, func(opts *InsertOneOptions) error {\n\t\topts.Comment = &comment\n\t\treturn nil\n\t})\n\treturn ioo\n}\n\n// InsertManyOptions represents arguments that can be used to configure an\n// InsertMany operation.\n//\n// See corresponding setter methods for documentation.\ntype InsertManyOptions struct {\n\tBypassDocumentValidation *bool\n\tComment                  any\n\tOrdered                  *bool\n\n\t// Deprecated: This option is for internal use only and should not be set. It may be changed or removed in any\n\t// release.\n\tInternal optionsutil.Options\n}\n\n// InsertManyOptionsBuilder contains options to configure insert operations.\n// Each option can be set through setter functions. See documentation for each\n// setter function for an explanation of the option.\ntype InsertManyOptionsBuilder struct {\n\tOpts []func(*InsertManyOptions) error\n}\n\n// InsertMany creates a new InsertManyOptions instance.\nfunc InsertMany() *InsertManyOptionsBuilder {\n\topts := &InsertManyOptionsBuilder{}\n\topts.SetOrdered(DefaultOrdered)\n\n\treturn opts\n}\n\n// List returns a list of InsertManyOptions setter functions.\nfunc (imo *InsertManyOptionsBuilder) List() []func(*InsertManyOptions) error {\n\treturn imo.Opts\n}\n\n// SetBypassDocumentValidation sets the value for the BypassDocumentValidation field. If true,\n// writes executed as part of the operation will opt out of document-level validation on the\n// server. The default value is false. See https://www.mongodb.com/docs/manual/core/schema-validation/\n// for more information about document validation.\nfunc (imo *InsertManyOptionsBuilder) SetBypassDocumentValidation(b bool) *InsertManyOptionsBuilder {\n\timo.Opts = append(imo.Opts, func(opts *InsertManyOptions) error {\n\t\topts.BypassDocumentValidation = &b\n\n\t\treturn nil\n\t})\n\n\treturn imo\n}\n\n// SetComment sets the value for the Comment field. Specifies a string or document that will be\n// included in server logs, profiling logs, and currentOp queries to help trace the operation.\n// The default value is nil, which means that no comment will be included in the logs.\nfunc (imo *InsertManyOptionsBuilder) SetComment(comment any) *InsertManyOptionsBuilder {\n\timo.Opts = append(imo.Opts, func(opts *InsertManyOptions) error {\n\t\topts.Comment = comment\n\n\t\treturn nil\n\t})\n\n\treturn imo\n}\n\n// SetOrdered sets the value for the Ordered field. If true, no writes will be executed after\n// one fails. The default value is true.\nfunc (imo *InsertManyOptionsBuilder) SetOrdered(b bool) *InsertManyOptionsBuilder {\n\timo.Opts = append(imo.Opts, func(opts *InsertManyOptions) error {\n\t\topts.Ordered = &b\n\n\t\treturn nil\n\t})\n\n\treturn imo\n}\n"
  },
  {
    "path": "mongo/options/listcollectionsoptions.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage options\n\nimport \"go.mongodb.org/mongo-driver/v2/internal/optionsutil\"\n\n// ListCollectionsOptions represents arguments that can be used to configure a\n// ListCollections operation.\n//\n// See corresponding setter methods for documentation.\ntype ListCollectionsOptions struct {\n\tNameOnly              *bool\n\tBatchSize             *int32\n\tAuthorizedCollections *bool\n\n\t// Deprecated: This option is for internal use only and should not be set. It may be changed or removed in any\n\t// release.\n\tInternal optionsutil.Options\n}\n\n// ListCollectionsOptionsBuilder contains options to configure list collection\n// operations. Each option can be set through setter functions. See\n// documentation for each setter function for an explanation of the option.\ntype ListCollectionsOptionsBuilder struct {\n\tOpts []func(*ListCollectionsOptions) error\n}\n\n// ListCollections creates a new ListCollectionsOptions instance.\nfunc ListCollections() *ListCollectionsOptionsBuilder {\n\treturn &ListCollectionsOptionsBuilder{}\n}\n\n// List returns a list of CountOptions setter functions.\nfunc (lc *ListCollectionsOptionsBuilder) List() []func(*ListCollectionsOptions) error {\n\treturn lc.Opts\n}\n\n// SetNameOnly sets the value for the NameOnly field. If true, each collection document will only\n// contain a field for the collection name. The default value is false.\nfunc (lc *ListCollectionsOptionsBuilder) SetNameOnly(b bool) *ListCollectionsOptionsBuilder {\n\tlc.Opts = append(lc.Opts, func(opts *ListCollectionsOptions) error {\n\t\topts.NameOnly = &b\n\n\t\treturn nil\n\t})\n\n\treturn lc\n}\n\n// SetBatchSize sets the value for the BatchSize field. Specifies the maximum number of documents\n// to be included in each batch returned by the server.\nfunc (lc *ListCollectionsOptionsBuilder) SetBatchSize(size int32) *ListCollectionsOptionsBuilder {\n\tlc.Opts = append(lc.Opts, func(opts *ListCollectionsOptions) error {\n\t\topts.BatchSize = &size\n\n\t\treturn nil\n\t})\n\n\treturn lc\n}\n\n// SetAuthorizedCollections sets the value for the AuthorizedCollections field. If true, and\n// NameOnly is true, limits the documents returned to only contain collections the user is\n// authorized to use. The default value is false.\nfunc (lc *ListCollectionsOptionsBuilder) SetAuthorizedCollections(b bool) *ListCollectionsOptionsBuilder {\n\tlc.Opts = append(lc.Opts, func(opts *ListCollectionsOptions) error {\n\t\topts.AuthorizedCollections = &b\n\n\t\treturn nil\n\t})\n\n\treturn lc\n}\n"
  },
  {
    "path": "mongo/options/listdatabasesoptions.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage options\n\n// ListDatabasesOptions represents arguments that can be used to configure a\n// ListDatabases operation.\n//\n// See corresponding setter methods for documentation.\ntype ListDatabasesOptions struct {\n\tNameOnly            *bool\n\tAuthorizedDatabases *bool\n}\n\n// ListDatabasesOptionsBuilder represents functional options that configure a\n// ListDatabasesopts.\ntype ListDatabasesOptionsBuilder struct {\n\tOpts []func(*ListDatabasesOptions) error\n}\n\n// ListDatabases creates a new ListDatabasesOptions instance.\nfunc ListDatabases() *ListDatabasesOptionsBuilder {\n\treturn &ListDatabasesOptionsBuilder{}\n}\n\n// List returns a list of ListDatabasesOptions setter functions.\nfunc (ld *ListDatabasesOptionsBuilder) List() []func(*ListDatabasesOptions) error {\n\treturn ld.Opts\n}\n\n// SetNameOnly sets the value for the NameOnly field. If true, only the Name field of the returned\n// DatabaseSpecification objects will be populated. The default value is false.\nfunc (ld *ListDatabasesOptionsBuilder) SetNameOnly(b bool) *ListDatabasesOptionsBuilder {\n\tld.Opts = append(ld.Opts, func(opts *ListDatabasesOptions) error {\n\t\topts.NameOnly = &b\n\t\treturn nil\n\t})\n\treturn ld\n}\n\n// SetAuthorizedDatabases sets the value for the AuthorizedDatabases field. If true, only the\n// databases which the user is authorized to see will be returned. For more information about the\n// behavior of this option, see https://www.mongodb.com/docs/manual/reference/privilege-actions/#find.\n// The default value is true.\nfunc (ld *ListDatabasesOptionsBuilder) SetAuthorizedDatabases(b bool) *ListDatabasesOptionsBuilder {\n\tld.Opts = append(ld.Opts, func(opts *ListDatabasesOptions) error {\n\t\topts.AuthorizedDatabases = &b\n\t\treturn nil\n\t})\n\treturn ld\n}\n"
  },
  {
    "path": "mongo/options/lister.go",
    "content": "// Copyright (C) MongoDB, Inc. 2024-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage options\n\n// Lister is an interface that wraps a List method to return a\n// slice of option setters.\ntype Lister[T any] interface {\n\tList() []func(*T) error\n}\n"
  },
  {
    "path": "mongo/options/loggeroptions.go",
    "content": "// Copyright (C) MongoDB, Inc. 2023-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage options\n\nimport (\n\t\"go.mongodb.org/mongo-driver/v2/internal/logger\"\n)\n\n// LogLevel is an enumeration representing the supported log severity levels.\ntype LogLevel int\n\nconst (\n\t// LogLevelInfo enables logging of informational messages. These logs\n\t// are high-level information about normal driver behavior.\n\tLogLevelInfo LogLevel = LogLevel(logger.LevelInfo)\n\n\t// LogLevelDebug enables logging of debug messages. These logs can be\n\t// voluminous and are intended for detailed information that may be\n\t// helpful when debugging an application.\n\tLogLevelDebug LogLevel = LogLevel(logger.LevelDebug)\n)\n\n// LogComponent is an enumeration representing the \"components\" which can be\n// logged against. A LogLevel can be configured on a per-component basis.\ntype LogComponent int\n\nconst (\n\t// LogComponentAll enables logging for all components.\n\tLogComponentAll LogComponent = LogComponent(logger.ComponentAll)\n\n\t// LogComponentCommand enables command monitor logging.\n\tLogComponentCommand LogComponent = LogComponent(logger.ComponentCommand)\n\n\t// LogComponentTopology enables topology logging.\n\tLogComponentTopology LogComponent = LogComponent(logger.ComponentTopology)\n\n\t// LogComponentServerSelection enables server selection logging.\n\tLogComponentServerSelection LogComponent = LogComponent(logger.ComponentServerSelection)\n\n\t// LogComponentConnection enables connection services logging.\n\tLogComponentConnection LogComponent = LogComponent(logger.ComponentConnection)\n)\n\n// LogSink is an interface that can be implemented to provide a custom sink for\n// the driver's logs.\ntype LogSink interface {\n\t// Info logs a non-error message with the given key/value pairs. This\n\t// method will only be called if the provided level has been defined\n\t// for a component in the LoggerOptions.\n\t//\n\t// Here are the following level mappings for V = \"Verbosity\":\n\t//\n\t//  - V(0): off\n\t//  - V(1): informational\n\t//  - V(2): debugging\n\t//\n\t// This level mapping is taken from the go-logr/logr library\n\t// specifications, specifically:\n\t//\n\t// \"Level V(0) is the default, and logger.V(0).Info() has the same\n\t// meaning as logger.Info().\"\n\tInfo(level int, message string, keysAndValues ...any)\n\n\t// Error logs an error message with the given key/value pairs\n\tError(err error, message string, keysAndValues ...any)\n}\n\n// LoggerOptions represent arguments used to configure Logging in the Go Driver.\n//\n// See corresponding setter methods for documentation.\ntype LoggerOptions struct {\n\tComponentLevels   map[LogComponent]LogLevel\n\tSink              LogSink\n\tMaxDocumentLength uint\n}\n\n// Logger creates a new LoggerOptions instance.\nfunc Logger() *LoggerOptions {\n\treturn &LoggerOptions{}\n}\n\n// SetComponentLevel sets the LogLevel value for a LogComponent. ComponentLevels is a map of\n// LogComponent to LogLevel. The LogLevel for a given LogComponent will be used to determine\n// if a log message should be logged.\nfunc (opts *LoggerOptions) SetComponentLevel(component LogComponent, level LogLevel) *LoggerOptions {\n\tif opts.ComponentLevels == nil {\n\t\topts.ComponentLevels = map[LogComponent]LogLevel{}\n\t}\n\n\topts.ComponentLevels[component] = level\n\n\treturn opts\n}\n\n// SetMaxDocumentLength sets the maximum length of a document to be logged. Sink is the\n// LogSink that will be used to log messages. If this is nil, the driver will use the\n// standard logging library.\nfunc (opts *LoggerOptions) SetMaxDocumentLength(maxDocumentLength uint) *LoggerOptions {\n\topts.MaxDocumentLength = maxDocumentLength\n\n\treturn opts\n}\n\n// SetSink sets the LogSink to use for logging. MaxDocumentLength is the maximum length\n// of a document to be logged. If the underlying document is larger than this value, it\n// will be truncated and appended with an ellipses \"...\".\nfunc (opts *LoggerOptions) SetSink(sink LogSink) *LoggerOptions {\n\topts.Sink = sink\n\n\treturn opts\n}\n"
  },
  {
    "path": "mongo/options/mongooptions.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage options\n\nimport (\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n)\n\nvar defaultRegistry = bson.NewRegistry()\n\n// Collation allows users to specify language-specific rules for string comparison, such as\n// rules for lettercase and accent marks.\ntype Collation struct {\n\tLocale          string `bson:\",omitempty\"` // The locale\n\tCaseLevel       bool   `bson:\",omitempty\"` // The case level\n\tCaseFirst       string `bson:\",omitempty\"` // The case ordering\n\tStrength        int    `bson:\",omitempty\"` // The number of comparison levels to use\n\tNumericOrdering bool   `bson:\",omitempty\"` // Whether to order numbers based on numerical order and not collation order\n\tAlternate       string `bson:\",omitempty\"` // Whether spaces and punctuation are considered base characters\n\tMaxVariable     string `bson:\",omitempty\"` // Which characters are affected by alternate: \"shifted\"\n\tNormalization   bool   `bson:\",omitempty\"` // Causes text to be normalized into Unicode NFD\n\tBackwards       bool   `bson:\",omitempty\"` // Causes secondary differences to be considered in reverse order, as it is done in the French language\n}\n\n// CursorType specifies whether a cursor should close when the last data is retrieved. See\n// NonTailable, Tailable, and TailableAwait.\ntype CursorType int8\n\nconst (\n\t// NonTailable specifies that a cursor should close after retrieving the last data.\n\tNonTailable CursorType = iota\n\t// Tailable specifies that a cursor should not close when the last data is retrieved and can be resumed later.\n\tTailable\n\t// TailableAwait specifies that a cursor should not close when the last data is retrieved and\n\t// that it should block for a certain amount of time for new data before returning no data.\n\tTailableAwait\n)\n\n// ReturnDocument specifies whether a findAndUpdate operation should return the document as it was\n// before the update or as it is after the update.\ntype ReturnDocument int8\n\nconst (\n\t// Before specifies that findAndUpdate should return the document as it was before the update.\n\tBefore ReturnDocument = iota\n\t// After specifies that findAndUpdate should return the document as it is after the update.\n\tAfter\n)\n\n// FullDocument specifies how a change stream should return the modified document.\ntype FullDocument string\n\nconst (\n\t// Default does not include a document copy.\n\tDefault FullDocument = \"default\"\n\t// Off is the same as sending no value for fullDocumentBeforeChange.\n\tOff FullDocument = \"off\"\n\t// Required is the same as WhenAvailable but raises a server-side error if the post-image is not available.\n\tRequired FullDocument = \"required\"\n\t// UpdateLookup includes a delta describing the changes to the document and a copy of the entire document that\n\t// was changed.\n\tUpdateLookup FullDocument = \"updateLookup\"\n\t// WhenAvailable includes a post-image of the modified document for replace and update change events\n\t// if the post-image for this event is available.\n\tWhenAvailable FullDocument = \"whenAvailable\"\n)\n"
  },
  {
    "path": "mongo/options/replaceoptions.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage options\n\nimport \"go.mongodb.org/mongo-driver/v2/internal/optionsutil\"\n\n// ReplaceOptions represents arguments that can be used to configure a ReplaceOne\n// operation.\n//\n// See corresponding setter methods for documentation.\ntype ReplaceOptions struct {\n\tBypassDocumentValidation *bool\n\tCollation                *Collation\n\tComment                  any\n\tHint                     any\n\tUpsert                   *bool\n\tLet                      any\n\tSort                     any\n\n\t// Deprecated: This option is for internal use only and should not be set. It may be changed or removed in any\n\t// release.\n\tInternal optionsutil.Options\n}\n\n// ReplaceOptionsBuilder contains options to configure replace operations. Each\n// option can be set through setter functions. See documentation for each setter\n// function for an explanation of the option.\ntype ReplaceOptionsBuilder struct {\n\tOpts []func(*ReplaceOptions) error\n}\n\n// Replace creates a new ReplaceOptions instance.\nfunc Replace() *ReplaceOptionsBuilder {\n\treturn &ReplaceOptionsBuilder{}\n}\n\n// List returns a list of CountOptions setter functions.\nfunc (ro *ReplaceOptionsBuilder) List() []func(*ReplaceOptions) error {\n\treturn ro.Opts\n}\n\n// SetBypassDocumentValidation sets the value for the BypassDocumentValidation field. If true,\n// writes executed as part of the operation will opt out of document-level validation on the server.\n// The default value is false. See https://www.mongodb.com/docs/manual/core/schema-validation/ for\n// more information about document validation.\nfunc (ro *ReplaceOptionsBuilder) SetBypassDocumentValidation(b bool) *ReplaceOptionsBuilder {\n\tro.Opts = append(ro.Opts, func(opts *ReplaceOptions) error {\n\t\topts.BypassDocumentValidation = &b\n\n\t\treturn nil\n\t})\n\n\treturn ro\n}\n\n// SetCollation sets the value for the Collation field. Specifies a collation to\n// use for string comparisons during the operation. The default value is nil,\n// which means the default collation of the collection will be used.\nfunc (ro *ReplaceOptionsBuilder) SetCollation(c *Collation) *ReplaceOptionsBuilder {\n\tro.Opts = append(ro.Opts, func(opts *ReplaceOptions) error {\n\t\topts.Collation = c\n\n\t\treturn nil\n\t})\n\n\treturn ro\n}\n\n// SetComment sets the value for the Comment field. Specifies a string or document that will\n// be included in server logs, profiling logs, and currentOp queries to help trace the operation.\n// The default value is nil, which means that no comment will be included in the logs.\nfunc (ro *ReplaceOptionsBuilder) SetComment(comment any) *ReplaceOptionsBuilder {\n\tro.Opts = append(ro.Opts, func(opts *ReplaceOptions) error {\n\t\topts.Comment = comment\n\n\t\treturn nil\n\t})\n\n\treturn ro\n}\n\n// SetHint sets the value for the Hint field. Specifies the index to use for the\n// operation. This should either be the index name as a string or the index\n// specification as a document. This option is only valid for MongoDB versions\n// >= 4.2. Server versions < 4.2 will return an error if this option is\n// specified. The driver will return an error if this option is specified during\n// an unacknowledged write operation. The driver will return an error if the\n// hint parameter is a multi-key map. The default value is nil, which means that\n// no hint will be sent.\nfunc (ro *ReplaceOptionsBuilder) SetHint(h any) *ReplaceOptionsBuilder {\n\tro.Opts = append(ro.Opts, func(opts *ReplaceOptions) error {\n\t\topts.Hint = h\n\n\t\treturn nil\n\t})\n\n\treturn ro\n}\n\n// SetUpsert sets the value for the Upsert field. If true, a new document will be inserted\n// if the filter does not match any documents in the collection. The default value is false.\nfunc (ro *ReplaceOptionsBuilder) SetUpsert(b bool) *ReplaceOptionsBuilder {\n\tro.Opts = append(ro.Opts, func(opts *ReplaceOptions) error {\n\t\topts.Upsert = &b\n\n\t\treturn nil\n\t})\n\n\treturn ro\n}\n\n// SetLet sets the value for the Let field. Specifies parameters for the aggregate expression.\n// This option is only valid for MongoDB versions >= 5.0. Older servers will report an error\n// for using this option. This must be a document mapping parameter names to values. Values\n// must be constant or closed expressions that do not reference document fields. Parameters\n// can then be accessed as variables in an aggregate expression context (e.g. \"$$var\").\nfunc (ro *ReplaceOptionsBuilder) SetLet(l any) *ReplaceOptionsBuilder {\n\tro.Opts = append(ro.Opts, func(opts *ReplaceOptions) error {\n\t\topts.Let = l\n\n\t\treturn nil\n\t})\n\n\treturn ro\n}\n\n// SetSort sets the value for the Sort field. Specifies a document specifying which document should\n// be replaced if the filter used by the operation matches multiple documents in the collection. If\n// set, the first document in the sorted order will be replaced. This option is only valid for MongoDB\n// versions >= 8.0. The sort parameter is evaluated sequentially, so the driver will return an error\n// if it is a multi-key map (which is unordeded). The default value is nil.\nfunc (ro *ReplaceOptionsBuilder) SetSort(s any) *ReplaceOptionsBuilder {\n\tro.Opts = append(ro.Opts, func(opts *ReplaceOptions) error {\n\t\topts.Sort = s\n\n\t\treturn nil\n\t})\n\n\treturn ro\n}\n"
  },
  {
    "path": "mongo/options/rewrapdatakeyoptions.go",
    "content": "// Copyright (C) MongoDB, Inc. 2022-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage options\n\n// RewrapManyDataKeyOptions represents all possible options used to decrypt and\n// encrypt all matching data keys with a possibly new masterKey.\n//\n// See corresponding setter methods for documentation.\ntype RewrapManyDataKeyOptions struct {\n\tProvider  *string\n\tMasterKey any\n}\n\n// RewrapManyDataKeyOptionsBuilder contains options to configure rewraping a\n// data key. Each option can be set through setter functions. See documentation\n// for each setter function for an explanation of the option.\ntype RewrapManyDataKeyOptionsBuilder struct {\n\tOpts []func(*RewrapManyDataKeyOptions) error\n}\n\n// RewrapManyDataKey creates a new RewrapManyDataKeyOptions instance.\nfunc RewrapManyDataKey() *RewrapManyDataKeyOptionsBuilder {\n\treturn new(RewrapManyDataKeyOptionsBuilder)\n}\n\n// List returns a list of CountOptions setter functions.\nfunc (rmdko *RewrapManyDataKeyOptionsBuilder) List() []func(*RewrapManyDataKeyOptions) error {\n\treturn rmdko.Opts\n}\n\n// SetProvider sets the value for the Provider field. Provider identifies the new KMS provider.\n// If omitted, encrypting uses the current KMS provider.\nfunc (rmdko *RewrapManyDataKeyOptionsBuilder) SetProvider(provider string) *RewrapManyDataKeyOptionsBuilder {\n\trmdko.Opts = append(rmdko.Opts, func(opts *RewrapManyDataKeyOptions) error {\n\t\topts.Provider = &provider\n\n\t\treturn nil\n\t})\n\n\treturn rmdko\n}\n\n// SetMasterKey sets the value for the MasterKey field. MasterKey identifies the new masterKey.\n// If omitted, rewraps with the current masterKey.\nfunc (rmdko *RewrapManyDataKeyOptionsBuilder) SetMasterKey(masterKey any) *RewrapManyDataKeyOptionsBuilder {\n\trmdko.Opts = append(rmdko.Opts, func(opts *RewrapManyDataKeyOptions) error {\n\t\topts.MasterKey = masterKey\n\n\t\treturn nil\n\t})\n\n\treturn rmdko\n}\n"
  },
  {
    "path": "mongo/options/runcmdoptions.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage options\n\nimport (\n\t\"go.mongodb.org/mongo-driver/v2/mongo/readpref\"\n)\n\n// RunCmdOptions represents arguments that can be used to configure a RunCommand\n// operation.\n//\n// See corresponding setter methods for documentation.\ntype RunCmdOptions struct {\n\tReadPreference *readpref.ReadPref\n}\n\n// RunCmdOptionsBuilder contains options to configure runCommand operations.\n// Each option can be set through setter functions. See documentation for each\n// setter function for an explanation of the option.\ntype RunCmdOptionsBuilder struct {\n\tOpts []func(*RunCmdOptions) error\n}\n\n// RunCmd creates a new RunCmdOptions instance.\nfunc RunCmd() *RunCmdOptionsBuilder {\n\treturn &RunCmdOptionsBuilder{}\n}\n\n// List returns a list of CountOptions setter functions.\nfunc (rc *RunCmdOptionsBuilder) List() []func(*RunCmdOptions) error {\n\treturn rc.Opts\n}\n\n// SetReadPreference sets value for the ReadPreference field. Specifies the read preference\n// to use for the operation. The default value is nil, which means that the primary read\n// preference will be used.\nfunc (rc *RunCmdOptionsBuilder) SetReadPreference(rp *readpref.ReadPref) *RunCmdOptionsBuilder {\n\trc.Opts = append(rc.Opts, func(opts *RunCmdOptions) error {\n\t\topts.ReadPreference = rp\n\n\t\treturn nil\n\t})\n\n\treturn rc\n}\n"
  },
  {
    "path": "mongo/options/searchindexoptions.go",
    "content": "// Copyright (C) MongoDB, Inc. 2023-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage options\n\n// SearchIndexesOptions represents arguments that can be used to configure a\n// SearchIndexView.\ntype SearchIndexesOptions struct {\n\tName *string\n\tType *string\n}\n\n// SearchIndexesOptionsBuilder contains options to configure search index\n// operations. Each option can be set through setter functions. See\n// documentation for each setter function for an explanation of the option.\ntype SearchIndexesOptionsBuilder struct {\n\tOpts []func(*SearchIndexesOptions) error\n}\n\n// SearchIndexes creates a new SearchIndexesOptions instance.\nfunc SearchIndexes() *SearchIndexesOptionsBuilder {\n\treturn &SearchIndexesOptionsBuilder{}\n}\n\n// List returns a list of CountOptions setter functions.\nfunc (sio *SearchIndexesOptionsBuilder) List() []func(*SearchIndexesOptions) error {\n\treturn sio.Opts\n}\n\n// SetName sets the value for the Name field.\nfunc (sio *SearchIndexesOptionsBuilder) SetName(name string) *SearchIndexesOptionsBuilder {\n\tsio.Opts = append(sio.Opts, func(opts *SearchIndexesOptions) error {\n\t\topts.Name = &name\n\n\t\treturn nil\n\t})\n\n\treturn sio\n}\n\n// SetType sets the value for the Type field.\nfunc (sio *SearchIndexesOptionsBuilder) SetType(typ string) *SearchIndexesOptionsBuilder {\n\tsio.Opts = append(sio.Opts, func(opts *SearchIndexesOptions) error {\n\t\topts.Type = &typ\n\n\t\treturn nil\n\t})\n\n\treturn sio\n}\n\n// CreateSearchIndexesOptions represents arguments that can be used to configure\n// a SearchIndexView.CreateOne or SearchIndexView.CreateMany operation.\ntype CreateSearchIndexesOptions struct{}\n\n// CreateSearchIndexesOptionsBuilder contains options to configure creating\n// search indexes. Each option can be set through setter functions. See\n// documentation for each setter function for an explanation of the option.\ntype CreateSearchIndexesOptionsBuilder struct {\n\tOpts []func(*CreateSearchIndexesOptions) error\n}\n\n// List returns a list of CreateSearchIndexesOptions setter functions.\nfunc (csio *CreateSearchIndexesOptionsBuilder) List() []func(*CreateSearchIndexesOptions) error {\n\treturn csio.Opts\n}\n\n// ListSearchIndexesOptions represents arguments that can be used to configure a\n// SearchIndexView.List operation.\ntype ListSearchIndexesOptions struct {\n\tAggregateOptions *AggregateOptions\n}\n\n// ListSearchIndexesOptionsBuilder contains options that can be used to\n// configure a SearchIndexView.List operation.\ntype ListSearchIndexesOptionsBuilder struct {\n\tOpts []func(*ListSearchIndexesOptions) error\n}\n\n// List returns a list of ListSearchIndexesOptions setter functions.\nfunc (lsi *ListSearchIndexesOptionsBuilder) List() []func(*ListSearchIndexesOptions) error {\n\treturn lsi.Opts\n}\n\n// DropSearchIndexOptions represents arguments that can be used to configure a\n// SearchIndexView.DropOne operation.\ntype DropSearchIndexOptions struct{}\n\n// DropSearchIndexOptionsBuilder contains options to configure dropping search\n// indexes. Each option can be set through setter functions. See documentation\n// for each setter function for an explanation of the option.\ntype DropSearchIndexOptionsBuilder struct {\n\tOpts []func(*DropSearchIndexOptions) error\n}\n\n// List returns a list of DropSearchIndexOptions setter functions.\nfunc (dsio *DropSearchIndexOptionsBuilder) List() []func(*DropSearchIndexOptions) error {\n\treturn dsio.Opts\n}\n\n// UpdateSearchIndexOptions represents arguments that can be used to configure a\n// SearchIndexView.UpdateOne operation.\ntype UpdateSearchIndexOptions struct{}\n\n// UpdateSearchIndexOptionsBuilder contains options to configure updating search\n// indexes. Each option can be set through setter functions. See documentation\n// for each setter function for an explanation of the option.\ntype UpdateSearchIndexOptionsBuilder struct {\n\tOpts []func(*UpdateSearchIndexOptions) error\n}\n\n// List returns a list of UpdateSearchIndexOptions setter functions.\nfunc (usio *UpdateSearchIndexOptionsBuilder) List() []func(*UpdateSearchIndexOptions) error {\n\treturn usio.Opts\n}\n"
  },
  {
    "path": "mongo/options/serverapioptions.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage options\n\nimport (\n\t\"fmt\"\n)\n\n// ServerAPIOptions represents arguments used to configure the API version sent to\n// the server when running commands.\n//\n// Sending a specified server API version causes the server to behave in a\n// manner compatible with that API version. It also causes the driver to behave\n// in a manner compatible with the driver’s behavior as of the release when the\n// driver first started to support the specified server API version.\n//\n// The user must specify a ServerAPIVersion if including ServerAPIOptions in\n// their client. That version must also be currently supported by the driver.\n// This version of the driver supports API version \"1\".\n//\n// See corresponding setter methods for documentation.\ntype ServerAPIOptions struct {\n\tServerAPIVersion  ServerAPIVersion\n\tStrict            *bool\n\tDeprecationErrors *bool\n}\n\n// ServerAPI creates a new ServerAPIOptions configured with the provided\n// serverAPIversion.\nfunc ServerAPI(serverAPIVersion ServerAPIVersion) *ServerAPIOptions {\n\treturn &ServerAPIOptions{ServerAPIVersion: serverAPIVersion}\n}\n\n// SetStrict specifies whether the server should return errors for features that are not part of the API version.\nfunc (s *ServerAPIOptions) SetStrict(strict bool) *ServerAPIOptions {\n\ts.Strict = &strict\n\n\treturn s\n}\n\n// SetDeprecationErrors specifies whether the server should return errors for deprecated features.\nfunc (s *ServerAPIOptions) SetDeprecationErrors(deprecationErrors bool) *ServerAPIOptions {\n\ts.DeprecationErrors = &deprecationErrors\n\n\treturn s\n}\n\n// ServerAPIVersion represents an API version that can be used in ServerAPIOptions.\ntype ServerAPIVersion string\n\nconst (\n\t// ServerAPIVersion1 is the first API version.\n\tServerAPIVersion1 ServerAPIVersion = \"1\"\n)\n\n// Validate determines if the provided ServerAPIVersion is currently supported by the driver.\nfunc (sav ServerAPIVersion) Validate() error {\n\tif sav == ServerAPIVersion1 {\n\t\treturn nil\n\t}\n\treturn fmt.Errorf(\"api version %q not supported; this driver version only supports API version \\\"1\\\"\", sav)\n}\n"
  },
  {
    "path": "mongo/options/sessionoptions.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage options\n\nimport \"go.mongodb.org/mongo-driver/v2/bson\"\n\n// DefaultCausalConsistency is the default value for the CausalConsistency option.\nvar DefaultCausalConsistency = true\n\n// SessionOptions represents arguments that can be used to configure a Session.\n//\n// See corresponding setter methods for documentation.\ntype SessionOptions struct {\n\tCausalConsistency         *bool\n\tDefaultTransactionOptions *TransactionOptionsBuilder\n\tSnapshot                  *bool\n\tSnapshotTime              *bson.Timestamp\n}\n\n// SessionOptionsBuilder represents functional options that configure a Sessionopts.\ntype SessionOptionsBuilder struct {\n\tOpts []func(*SessionOptions) error\n}\n\n// Session creates a new SessionOptions instance.\nfunc Session() *SessionOptionsBuilder {\n\treturn &SessionOptionsBuilder{}\n}\n\n// List returns a list of SessionOptions setter functions.\nfunc (s *SessionOptionsBuilder) List() []func(*SessionOptions) error {\n\treturn s.Opts\n}\n\n// SetCausalConsistency sets the value for the CausalConsistency field. If true, causal\n// consistency will be enabled for the session. This option cannot be set to true if Snapshot\n// is set to true. The default value is true unless Snapshot is set to true. See\n// https://www.mongodb.com/docs/manual/core/read-isolation-consistency-recency/#sessions\n// for more information.\nfunc (s *SessionOptionsBuilder) SetCausalConsistency(b bool) *SessionOptionsBuilder {\n\ts.Opts = append(s.Opts, func(opts *SessionOptions) error {\n\t\topts.CausalConsistency = &b\n\t\treturn nil\n\t})\n\treturn s\n}\n\n// SetDefaultTransactionOptions sets the value for the DefaultTransactionOptions field.\n// Specifies the default options for transactions started in the session. If this object\n// or any value on the object is nil, the client-level read concern, write concern,\n// and/or read preference will be used to start the session.\nfunc (s *SessionOptionsBuilder) SetDefaultTransactionOptions(dt *TransactionOptionsBuilder) *SessionOptionsBuilder {\n\ts.Opts = append(s.Opts, func(opts *SessionOptions) error {\n\t\topts.DefaultTransactionOptions = dt\n\t\treturn nil\n\t})\n\treturn s\n}\n\n// SetSnapshot sets the value for the Snapshot field. If true, all read operations performed\n// with this session will be read from the same snapshot. This option cannot be set to true\n// if CausalConsistency is set to true. Transactions and write operations are not allowed on\n// snapshot sessions and will error. The default value is false.\nfunc (s *SessionOptionsBuilder) SetSnapshot(b bool) *SessionOptionsBuilder {\n\ts.Opts = append(s.Opts, func(opts *SessionOptions) error {\n\t\topts.Snapshot = &b\n\t\treturn nil\n\t})\n\treturn s\n}\n\n// SetSnapshotTime sets the value for the SnapshotTime field. Specifies the\n// timestamp to use for snapshot reads within the session. This option can only\n// be set if Snapshot is set to true. If not provided, the snapshot time will be\n// determined automatically from the atClusterTime of the first read operation\n// performed in the session. The default value is nil.\nfunc (s *SessionOptionsBuilder) SetSnapshotTime(t bson.Timestamp) *SessionOptionsBuilder {\n\ts.Opts = append(s.Opts, func(opts *SessionOptions) error {\n\t\topts.SnapshotTime = &t\n\t\treturn nil\n\t})\n\treturn s\n}\n"
  },
  {
    "path": "mongo/options/testdata/ca-key.pem",
    "content": "-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEAo+gfY2cD5r55HwqLDauU7Fj7jv7iYUzjNG3/iTPrsHkSrmmW\n/DnmQ9Px/D78sOwkIzLNqdt4j94pOxEzY4XzVpYqoiTUkJH+vj7yoEhlCulgvewY\nJp6s5dSo0k5DksVLbCjKOfXzL5hAwn//Q1P9m58sBwb1FW7ElHK6CNIhLoijeJPi\nv7SUPb6mitPzn4k+WAbPLgsOGoFJMVns0czzhcc45nlVZIsW1UsETjXAcof1MSh4\nbpGgjNo/0Tnkn2mrPeS4n1KsWTPWGjVQBCXhKBXLGDIrLksF5H4OF26SOSq0lg2O\n6zjDDfIiEiAmTDHojJHBAAVVk9h8jmOG/J1qVwIDAQABAoIBACO1gJEhcmIlATJY\nE69owEs1n6ipsxxLqfxUMqXrn9+UoRNKgdB1tMr2M/QmrNrGt6DLgq8M15lQZ18t\nAiselcM5eBRXPdA5XmyIPkfZPdpAugjmlORlEXoI+LbIwkQJo8LuqBA0m0S1QPae\n+Dfs5oWvCeKGC4Md3MoSrKEbVotSdC1P3MCM+tCzYIL+QivjOJFMGrdYhk9qQRER\nU7G03j5JZmknMo82JknxsAgunrWEZlwfA+KrvXOwAgTQJxtqh1lKv+NK6EhIjqVm\n25Zgi89SFqFNS5ShrzdNqvBZyrEktzr4oi4fWR5MoQR/5wIjF92nc9+qjhqvj5U8\n9kl9LYECgYEAxkblYtVi7YXUjT2JZ93n+3tUiXvf966pQjtux01Qg3t1X2lJ8OrL\nPI/A4anT6s5PNnHENuRJj/k9Uu9AItv4IonklmMGwOKvxUBgl9aHEYFjJ2+ej6wp\n2OXawdSIinuCBTQ/qX5IrgmQFPrj80585pVhB0zDuqtl1af3TauVWWECgYEA05+y\njPXxM2+ws73XBaPFHMUpHCUTSHtljjLM+aSbIlklHtTCvmdOFG8Qrhn9O6ivJ6ms\nC91ETB0JJ3ZSnimvYK/yV7N6lJcY1GSLOdLScf0oFGAbLQbk5h5t5iIuhOi8lJCa\natg8zIQMADudrHtbQGtQrz6903jJrYUOB6SqRrcCgYEAmb4lZeJyKB0KuE4L+ob9\nt2llHon4G3TKd7nf5xhTIvCbDVV16SIWXypUdGacFMjOcTf73lhblhC79U/g2mbY\nW+eyYM3+UZg0lO2bOIE3EGLUgKKzP1JOzIlGe9+R1Fd6dRCTgJiUQKbXmv0DvgiR\nHrabmu5yIo9+khIA17ABSWECgYBVfIt6GdfHuXSeBzAXHSMBDjwfHHL/sqlBmg/n\nQLFSXMR8AeJxW/XUl1Ebo3ACa4/l68qOOGHghVIvWMLjFV+9JRY2i1AXOjpTghIj\n16AfX+6S3Ifd1o0c0GoArwovsnyLXyahxpw42ZaneW2mQWaJTVCYmjXAeWiu6bLT\nL5eBXQKBgD/TvxZ+VEAJPMUDAoTahZ//RwokBX8zOkRAnCl8VCLU0kLv9/Phht7F\nNtg/pnJ0Ksrhr4X5WRl/ziAIXwc2j+qVGjwXnU0o9tu/KJ7OkfCAFjSM6Tk6ILE2\nMN7yw4baP/7JNQcf90VsIcTUvBOOu+agO2FuE5P86C8DaRkTE1Co\n-----END RSA PRIVATE KEY-----\n"
  },
  {
    "path": "mongo/options/testdata/ca-with-intermediates-first.pem",
    "content": "-----BEGIN CERTIFICATE-----\nMIIDdDCCAlwCBBmRIxIwDQYJKoZIhvcNAQELBQAwdDELMAkGA1UEBhMCVVMxETAP\nBgNVBAgMCE5ldyBZb3JrMRYwFAYDVQQHDA1OZXcgWW9yayBDaXR5MRAwDgYDVQQK\nDAdNb25nb0RCMQ8wDQYDVQQLDAZLZXJuZWwxFzAVBgNVBAMMDktlcm5lbCBUZXN0\nIENBMB4XDTE5MDkyNTIzMjczOVoXDTM5MDkyNzIzMjczOVowdDELMAkGA1UEBhMC\nVVMxETAPBgNVBAgMCE5ldyBZb3JrMRYwFAYDVQQHDA1OZXcgWW9yayBDaXR5MRAw\nDgYDVQQKDAdNb25nb0RCMQ8wDQYDVQQLDAZLZXJuZWwxFzAVBgNVBAMMDktlcm5l\nbCBUZXN0IENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAupVkx8+n\nAqzsANKwNPeCYlf2q0WgF4kSUMNJdpmMelrr7hh7EOnAU0hTAQx9BKTEbExeCzH6\nOArFNGjewjWVXwaOpCjK8FMvK6/lGVEpmoHNF9XuiQVmaQ4bJD6rC73YjpgNIPeL\n5PyoFLEZv+X2cRBPpTcSRcf87tk8HL7v0eyk1JBhkeKK68SYdWwZlHaa1jqwmliW\nWvVMkHVH3lx0VOgQwWtOgs0K1zpcZ0sH5MGpYRQOiidIRZj3PkKeTPQe2D6VQQtv\n2yDs9dWfCxJJP9QiWclL2rF/xqlFSNEIfNZpZhk6I1DHQpA2uyJfzRH62pFasJuB\nCVh5Tr0EDoVreQIDAQABoxMwETAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEB\nCwUAA4IBAQARdNCYYWxi2fyhJwzGHwIT261d/pTlOSYLlm84c72aEneFUnfp8/H5\nJjuFbnhiX+5+h3M7eDQhra9s+H3vKr7o38EIVf5OKXvpNLwv1UUmomBvKqccioYh\nbxrfwCzfBRuUmW05kcAVn8iKovqyxL7npEZbckwtT+BqZ4kOL4Uzre+S1HMx0zOu\nxulSYA/sBoJ2BB93ZIAqB+f/+InS9yggzyhhaQqS7QEl1L4nZE4Oy0jKcxdCzysm\nTqiyH+OI5SVRTfXh4XvHmdWBBaQyaTmQzXYUxUi7jg1jEAiebCGrEJv9plwq4KfC\ncze9NLBjaXR3GzonT8kICyVT/0UvhuJg\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "mongo/options/testdata/ca-with-intermediates-second.pem",
    "content": "-----BEGIN CERTIFICATE-----\nMIIDcjCCAloCBFNvXv0wDQYJKoZIhvcNAQELBQAwdDELMAkGA1UEBhMCVVMxETAP\nBgNVBAgMCE5ldyBZb3JrMRYwFAYDVQQHDA1OZXcgWW9yayBDaXR5MRAwDgYDVQQK\nDAdNb25nb0RCMQ8wDQYDVQQLDAZLZXJuZWwxFzAVBgNVBAMMDktlcm5lbCBUZXN0\nIENBMB4XDTE5MTAxNjE3NTc0OVoXDTM5MTAxODE3NTc0OVowdTELMAkGA1UEBhMC\nVVMxETAPBgNVBAgMCE5ldyBZb3JrMRYwFAYDVQQHDA1OZXcgWW9yayBDaXR5MRAw\nDgYDVQQKDAdNb25nb0RCMQ8wDQYDVQQLDAZLZXJuZWwxGDAWBgNVBAMMD0ludGVy\nbWVkaWF0ZSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOcrt0K/\nK8ueq9lQ9wVtA+rka82FPaHXpZkH7ii/ZJlNcDXLaJPdRb7XgAabAS3gru+Hpm/g\nkAofoL7cDmGZRAbiW0I6X5Nlmjh4/0E6RxSs0urhU34m8sM3OPmAgbq7oLlFINZ6\nGbVR2RzMWhK5HPld8WApDkIVpBFbRGV7dt/LVKY34Nab9hu15WRSH3wzSHJvt8e1\nalYxaRUoE27Fk3zdHPw9Z91/XvfIThTsNXEQVOeaieyQLwoKx8tpMa/ck/mFmSkD\nnH4h37ev0ZABB04iTBfLGCpUCKd5LerYIqEu4UPp9r9czbco7twp+k8875pJ3bpV\nwMH0/jF68GTC8HsCAwEAAaMQMA4wDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsF\nAAOCAQEAaL61HT6JD19Ok2LY6b5KkZ4oRhiV73l2/82X88GcV63Bl0rN6AF+W0ut\nGuHzSqcHV4qZkt9ezNoDYq41BsV4I8Cxvyaz8is956uyrzQAsVCzidZSGeq2GIfV\n9y+IRISBRdzXQHrB0ni93b16MJO1KoJxZbXcb8W9ll2URp+YEnHIrhWpDcMcHN3O\nmjoy1+WpHDXpNK2D3Xk5tT5pwxJZWRPB9wvPfTux5Vp07+Rkef9YjXL2ZZxt4vKu\nWU0HJWfK905HpbHjwFNRlbT39WWaF8VeJ2nT6OrXTpE8tDdPoeZt5u76eaWEmsf5\ngX2ClrsSmegOKT52V+2NAlKDjQkaDg==\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "mongo/options/testdata/ca-with-intermediates-third.pem",
    "content": "-----BEGIN CERTIFICATE-----\nMIIDaTCCAlECBCLMKCQwDQYJKoZIhvcNAQELBQAwdTELMAkGA1UEBhMCVVMxETAP\nBgNVBAgMCE5ldyBZb3JrMRYwFAYDVQQHDA1OZXcgWW9yayBDaXR5MRAwDgYDVQQK\nDAdNb25nb0RCMQ8wDQYDVQQLDAZLZXJuZWwxGDAWBgNVBAMMD0ludGVybWVkaWF0\nZSBDQTAeFw0xOTEwMTYxNzU3NDlaFw0zOTEwMTgxNzU3NDlaMH0xCzAJBgNVBAYT\nAlVTMREwDwYDVQQIDAhOZXcgWW9yazEWMBQGA1UEBwwNTmV3IFlvcmsgQ2l0eTEQ\nMA4GA1UECgwHTW9uZ29EQjEPMA0GA1UECwwGS2VybmVsMSAwHgYDVQQDDBdTZXJ2\nZXIgVmlhIEludGVybWVkaWF0ZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC\nggEBAKCj95jJi63dCJDKyTWQAJwgLtvqwPmIvBMbYMcmrolP3tYolwkkh6c0RJdP\nUTy4gJaLZ9b9B8GuiGFatG+ZzEqCUh6AlG1lGJwdyqyP2eotpzkZJhwLGFc81DUt\n7WKhZjHikgXcAd3VMCXsjv7/axvBnc459SpsCW5heOiJ3Ap8OJIrTuD4GAWW5cfW\nGYa1v07hqXZ7SEloRE5Hm5bItnZA9+A5QNCj2a2PqK3TlrtCnDP125iYHc4W/+NO\n1V9LPKpDffdJ7slhrX69k9oV8HTQBgu7lRHKQlprW64pg6EMsw/5hpGjpZkyG9J9\nL1FYrXqsagA1E5j87JQH+SJDOEUCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAPRu7\nYtgaGPx4O2IfvYoK+BsiD/55WylDp/0zxf/xipvuoPRcMgX1ORoNuQTPfeIMxlZS\nUAIRHQSjOZQkH0WL2dglqaoBhTXO6c0PUuWOUsDDwnZE8lj26Jkka6PC8d8t7gjt\nw/k2IRnhcklKOu6pNlcYi+/WGcWphmkjMRWeka0vqXab/bxF1sdeHD9bMY59QOwI\nFT0JCG8Wn0LSi6V/Ip35JV3CryBP1Jual2aCXIXEKm3m+P4NboRQTZ7zqIcJoA8S\ni2GkuHCPOhhQP2isiRRBILujcJxnRYrR7qNIqvMrmPx3va8fMrHKtWAx+bfo/EIJ\nhlU1Eyls7Xv83x3vaA==\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "mongo/options/testdata/ca-with-intermediates.pem",
    "content": "-----BEGIN CERTIFICATE-----\nMIIDdDCCAlwCBBmRIxIwDQYJKoZIhvcNAQELBQAwdDELMAkGA1UEBhMCVVMxETAP\nBgNVBAgMCE5ldyBZb3JrMRYwFAYDVQQHDA1OZXcgWW9yayBDaXR5MRAwDgYDVQQK\nDAdNb25nb0RCMQ8wDQYDVQQLDAZLZXJuZWwxFzAVBgNVBAMMDktlcm5lbCBUZXN0\nIENBMB4XDTE5MDkyNTIzMjczOVoXDTM5MDkyNzIzMjczOVowdDELMAkGA1UEBhMC\nVVMxETAPBgNVBAgMCE5ldyBZb3JrMRYwFAYDVQQHDA1OZXcgWW9yayBDaXR5MRAw\nDgYDVQQKDAdNb25nb0RCMQ8wDQYDVQQLDAZLZXJuZWwxFzAVBgNVBAMMDktlcm5l\nbCBUZXN0IENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAupVkx8+n\nAqzsANKwNPeCYlf2q0WgF4kSUMNJdpmMelrr7hh7EOnAU0hTAQx9BKTEbExeCzH6\nOArFNGjewjWVXwaOpCjK8FMvK6/lGVEpmoHNF9XuiQVmaQ4bJD6rC73YjpgNIPeL\n5PyoFLEZv+X2cRBPpTcSRcf87tk8HL7v0eyk1JBhkeKK68SYdWwZlHaa1jqwmliW\nWvVMkHVH3lx0VOgQwWtOgs0K1zpcZ0sH5MGpYRQOiidIRZj3PkKeTPQe2D6VQQtv\n2yDs9dWfCxJJP9QiWclL2rF/xqlFSNEIfNZpZhk6I1DHQpA2uyJfzRH62pFasJuB\nCVh5Tr0EDoVreQIDAQABoxMwETAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEB\nCwUAA4IBAQARdNCYYWxi2fyhJwzGHwIT261d/pTlOSYLlm84c72aEneFUnfp8/H5\nJjuFbnhiX+5+h3M7eDQhra9s+H3vKr7o38EIVf5OKXvpNLwv1UUmomBvKqccioYh\nbxrfwCzfBRuUmW05kcAVn8iKovqyxL7npEZbckwtT+BqZ4kOL4Uzre+S1HMx0zOu\nxulSYA/sBoJ2BB93ZIAqB+f/+InS9yggzyhhaQqS7QEl1L4nZE4Oy0jKcxdCzysm\nTqiyH+OI5SVRTfXh4XvHmdWBBaQyaTmQzXYUxUi7jg1jEAiebCGrEJv9plwq4KfC\ncze9NLBjaXR3GzonT8kICyVT/0UvhuJg\n-----END CERTIFICATE-----\n-----BEGIN CERTIFICATE-----\nMIIDcjCCAloCBFNvXv0wDQYJKoZIhvcNAQELBQAwdDELMAkGA1UEBhMCVVMxETAP\nBgNVBAgMCE5ldyBZb3JrMRYwFAYDVQQHDA1OZXcgWW9yayBDaXR5MRAwDgYDVQQK\nDAdNb25nb0RCMQ8wDQYDVQQLDAZLZXJuZWwxFzAVBgNVBAMMDktlcm5lbCBUZXN0\nIENBMB4XDTE5MTAxNjE3NTc0OVoXDTM5MTAxODE3NTc0OVowdTELMAkGA1UEBhMC\nVVMxETAPBgNVBAgMCE5ldyBZb3JrMRYwFAYDVQQHDA1OZXcgWW9yayBDaXR5MRAw\nDgYDVQQKDAdNb25nb0RCMQ8wDQYDVQQLDAZLZXJuZWwxGDAWBgNVBAMMD0ludGVy\nbWVkaWF0ZSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOcrt0K/\nK8ueq9lQ9wVtA+rka82FPaHXpZkH7ii/ZJlNcDXLaJPdRb7XgAabAS3gru+Hpm/g\nkAofoL7cDmGZRAbiW0I6X5Nlmjh4/0E6RxSs0urhU34m8sM3OPmAgbq7oLlFINZ6\nGbVR2RzMWhK5HPld8WApDkIVpBFbRGV7dt/LVKY34Nab9hu15WRSH3wzSHJvt8e1\nalYxaRUoE27Fk3zdHPw9Z91/XvfIThTsNXEQVOeaieyQLwoKx8tpMa/ck/mFmSkD\nnH4h37ev0ZABB04iTBfLGCpUCKd5LerYIqEu4UPp9r9czbco7twp+k8875pJ3bpV\nwMH0/jF68GTC8HsCAwEAAaMQMA4wDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsF\nAAOCAQEAaL61HT6JD19Ok2LY6b5KkZ4oRhiV73l2/82X88GcV63Bl0rN6AF+W0ut\nGuHzSqcHV4qZkt9ezNoDYq41BsV4I8Cxvyaz8is956uyrzQAsVCzidZSGeq2GIfV\n9y+IRISBRdzXQHrB0ni93b16MJO1KoJxZbXcb8W9ll2URp+YEnHIrhWpDcMcHN3O\nmjoy1+WpHDXpNK2D3Xk5tT5pwxJZWRPB9wvPfTux5Vp07+Rkef9YjXL2ZZxt4vKu\nWU0HJWfK905HpbHjwFNRlbT39WWaF8VeJ2nT6OrXTpE8tDdPoeZt5u76eaWEmsf5\ngX2ClrsSmegOKT52V+2NAlKDjQkaDg==\n-----END CERTIFICATE-----\n-----BEGIN CERTIFICATE-----\nMIIDaTCCAlECBCLMKCQwDQYJKoZIhvcNAQELBQAwdTELMAkGA1UEBhMCVVMxETAP\nBgNVBAgMCE5ldyBZb3JrMRYwFAYDVQQHDA1OZXcgWW9yayBDaXR5MRAwDgYDVQQK\nDAdNb25nb0RCMQ8wDQYDVQQLDAZLZXJuZWwxGDAWBgNVBAMMD0ludGVybWVkaWF0\nZSBDQTAeFw0xOTEwMTYxNzU3NDlaFw0zOTEwMTgxNzU3NDlaMH0xCzAJBgNVBAYT\nAlVTMREwDwYDVQQIDAhOZXcgWW9yazEWMBQGA1UEBwwNTmV3IFlvcmsgQ2l0eTEQ\nMA4GA1UECgwHTW9uZ29EQjEPMA0GA1UECwwGS2VybmVsMSAwHgYDVQQDDBdTZXJ2\nZXIgVmlhIEludGVybWVkaWF0ZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC\nggEBAKCj95jJi63dCJDKyTWQAJwgLtvqwPmIvBMbYMcmrolP3tYolwkkh6c0RJdP\nUTy4gJaLZ9b9B8GuiGFatG+ZzEqCUh6AlG1lGJwdyqyP2eotpzkZJhwLGFc81DUt\n7WKhZjHikgXcAd3VMCXsjv7/axvBnc459SpsCW5heOiJ3Ap8OJIrTuD4GAWW5cfW\nGYa1v07hqXZ7SEloRE5Hm5bItnZA9+A5QNCj2a2PqK3TlrtCnDP125iYHc4W/+NO\n1V9LPKpDffdJ7slhrX69k9oV8HTQBgu7lRHKQlprW64pg6EMsw/5hpGjpZkyG9J9\nL1FYrXqsagA1E5j87JQH+SJDOEUCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAPRu7\nYtgaGPx4O2IfvYoK+BsiD/55WylDp/0zxf/xipvuoPRcMgX1ORoNuQTPfeIMxlZS\nUAIRHQSjOZQkH0WL2dglqaoBhTXO6c0PUuWOUsDDwnZE8lj26Jkka6PC8d8t7gjt\nw/k2IRnhcklKOu6pNlcYi+/WGcWphmkjMRWeka0vqXab/bxF1sdeHD9bMY59QOwI\nFT0JCG8Wn0LSi6V/Ip35JV3CryBP1Jual2aCXIXEKm3m+P4NboRQTZ7zqIcJoA8S\ni2GkuHCPOhhQP2isiRRBILujcJxnRYrR7qNIqvMrmPx3va8fMrHKtWAx+bfo/EIJ\nhlU1Eyls7Xv83x3vaA==\n-----END CERTIFICATE-----\n-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCgo/eYyYut3QiQ\nysk1kACcIC7b6sD5iLwTG2DHJq6JT97WKJcJJIenNESXT1E8uICWi2fW/QfBrohh\nWrRvmcxKglIegJRtZRicHcqsj9nqLac5GSYcCxhXPNQ1Le1ioWYx4pIF3AHd1TAl\n7I7+/2sbwZ3OOfUqbAluYXjoidwKfDiSK07g+BgFluXH1hmGtb9O4al2e0hJaERO\nR5uWyLZ2QPfgOUDQo9mtj6it05a7Qpwz9duYmB3OFv/jTtVfSzyqQ333Se7JYa1+\nvZPaFfB00AYLu5URykJaa1uuKYOhDLMP+YaRo6WZMhvSfS9RWK16rGoANROY/OyU\nB/kiQzhFAgMBAAECggEAYNydpj4ZADwgNxZfnvF2vK1XM/noZE6DgU3n3A3B/j/v\n0NhmwfeJ6FNG7KnCBUgHGT9z3Jlz/OBdkb5cwFJPcboFBClp1lC4NyJrnAOdVWwz\nweUdKpmyEqA2IN7RncBOW9QPUoMniPhMcQGj87RVhEYJ/ljKMMs2IJ2bhzPhP1xV\nMK/NJ6cT680MwZTzWWIPYT30Pv6IDm5INjb+AG4PuzDXTx28GsCw2z9RnlH8aSdu\n9qSNEoh4oXef4ny2niKa+fwRgTrkUYyljML34301dx6MclsMz0k9SprGrB2dPAxg\nIV3pRO3rqS6oEZKjyyXHPV45Q/AAm3tsP5aZCJm9fQKBgQDO6Qj5UpJT1PZcpTLW\nUY1l9N9GWDlRCCpprQYKwOWK6H9Pb5ptBYzGrIjC9YmX9hYjL7wOiznSsCO4zv8d\n1AR56A3APzD2iQQTyRkY9lr0CbtOSeYyg6MHQOVY8T8E/gw5M6SakLeB9ZwOLjxl\nfwu7gKAYmZi+h11R7f5EP3HZdwKBgQDGwK0RAC+fxtGFOY9CGqwG5rom3VPvpKd9\ngjlekXbGguA2Z21y6IdMjpTcrbnCX3DRNedpq1LH0r4xW2zrjV4P7g3j7qeSQwqW\nexHLvcfEkUAjPpaE64sC3qYgwJSwNdVWG+ikBmvT4oobFsOZzL6563Md54Tlo+bN\nMQ4dPhCrIwKBgFR4OGyotAo7X9RUsNtj8cjU3i32qHeXUNFRjKzpMQyze/3u/ulR\nIbaeCzskSGbcVQ9KVojNd+62b+7ruqTvwGAQR0Tbx15uc1ase22AbYNBUdFVRAAN\nU4oLiVX1LxgXqt/TYHilafVJUstLPubkpeKHUVSZqAzocEWZVnuANzdjAoGAFvUW\niIou9hOvC+Z0J7yQ9lMcWXTjRyELv7GVEtlWkON5Jo+X0tgNGMi3ZS4j8NG5ZFEH\no+sIKtCq62SWFjdEEC79J8DfVkOsK2mXyqLnOktUzz1hgYT7j59MLShRhHETbVjX\n7GpZdiYKscpVWaSOu0b5CxE9BpGCDV2HNdV/c2UCgYEArnS54agkV1qQ6AbMHdkD\nqAiDqOosDDzxgnEz422r5S9CGZ4n8uu9PGhb8NtlLxxxfKnArFGOiHmV0aPULUNp\ntX+vq4JC+1gyStUdwYkrhN48eG1KUFiAqcfEFxBgvdtpKxeOd+O5SKD/ceqJO7+3\nZ826BiCbw4nJAFNHmbNr6iw=\n-----END PRIVATE KEY-----\n"
  },
  {
    "path": "mongo/options/testdata/ca.pem",
    "content": "-----BEGIN CERTIFICATE-----\nMIIDmzCCAoOgAwIBAgIUDcX5ZdXD55buEoQtl45OcMrCtSUwDQYJKoZIhvcNAQEL\nBQAwXTELMAkGA1UEBhMCVVMxETAPBgNVBAgTCE5ldyBZb3JrMRYwFAYDVQQHEw1O\nZXcgWW9yayBDaXR5MRUwEwYDVQQKEwxNb25nb0RCLCBJbmMxDDAKBgNVBAsTA1dX\nVzAeFw0xOTAyMDgxODM2MDBaFw0yNDAyMDcxODM2MDBaMF0xCzAJBgNVBAYTAlVT\nMREwDwYDVQQIEwhOZXcgWW9yazEWMBQGA1UEBxMNTmV3IFlvcmsgQ2l0eTEVMBMG\nA1UEChMMTW9uZ29EQiwgSW5jMQwwCgYDVQQLEwNXV1cwggEiMA0GCSqGSIb3DQEB\nAQUAA4IBDwAwggEKAoIBAQCj6B9jZwPmvnkfCosNq5TsWPuO/uJhTOM0bf+JM+uw\neRKuaZb8OeZD0/H8Pvyw7CQjMs2p23iP3ik7ETNjhfNWliqiJNSQkf6+PvKgSGUK\n6WC97Bgmnqzl1KjSTkOSxUtsKMo59fMvmEDCf/9DU/2bnywHBvUVbsSUcroI0iEu\niKN4k+K/tJQ9vqaK0/OfiT5YBs8uCw4agUkxWezRzPOFxzjmeVVkixbVSwRONcBy\nh/UxKHhukaCM2j/ROeSfaas95LifUqxZM9YaNVAEJeEoFcsYMisuSwXkfg4XbpI5\nKrSWDY7rOMMN8iISICZMMeiMkcEABVWT2HyOY4b8nWpXAgMBAAGjUzBRMA4GA1Ud\nDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBT6a2dHAAcv+ueV\nzUQcteAnZXAHIzAPBgNVHREECDAGhwR/AAABMA0GCSqGSIb3DQEBCwUAA4IBAQBa\nKQn/gWmyFE8LaL6lCEmSveT2jKk1OfJ6QFFAv8sJZhhx9X4HaYzFhhafvX7pp9TZ\nJLBDRznb4aro6u81jLk6lIP8eQsDnDk68VZtLOUcIgkO8bUqQP93RsNJKyv2o6h4\neEyNb3cHqrhD05PlGx5D6uKuLAgfxd2pzx8OHGSV1yfr7asgL0fW+077/OQk/9Gk\nC+oi9uLjpKKXKwZz/hhyaKBHovlaw/xmDCjnrn+pnB3K+PFBCdU1yHqiKHcqsnEE\nqGrUL+MPP7lK/g2tyybrZVyXMVsvhESgk3RFao0cR0jj6xQDaSRNTSAX9Rl3ZysJ\n9P1ekH8zhXV924mUePXW\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "mongo/options/testdata/cert.pem",
    "content": "-----BEGIN CERTIFICATE-----\nMIIEejCCA2KgAwIBAgIJAM+1x3rLrnhEMA0GCSqGSIb3DQEBBQUAMIGEMQswCQYD\nVQQGEwJVUzERMA8GA1UECBMITmV3IFlvcmsxFjAUBgNVBAcTDU5ldyBZb3JrIENp\ndHkxEDAOBgNVBAoTB01vbmdvREIxEDAOBgNVBAsTB0RyaXZlcnMxJjAkBgNVBAMT\nHUV4YW1wbGUgR28gRHJpdmVyIENlcnRpZmljYXRlMB4XDTE5MDIwODE4MzUxNloX\nDTI5MDIwNTE4MzUxNlowgYQxCzAJBgNVBAYTAlVTMREwDwYDVQQIEwhOZXcgWW9y\nazEWMBQGA1UEBxMNTmV3IFlvcmsgQ2l0eTEQMA4GA1UEChMHTW9uZ29EQjEQMA4G\nA1UECxMHRHJpdmVyczEmMCQGA1UEAxMdRXhhbXBsZSBHbyBEcml2ZXIgQ2VydGlm\naWNhdGUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvUnMj0/0tnvmi\nc5qR9HFIHyuow8DKsJggyy9DMWP7NlOV309rMj9bghithSrjP7LGc5wRMHpOd2Iu\nBtL21Mxlb09Vo8lEYnS0vZQg7fu+JPcRL3cwtRm5BOQFy8PghrAOUcq8UNxBfOb4\n0f8OjEtAE4yCVTDvzUg6YOohwJi3Qm98qVwYy2RAo5BM2EK8ZyFbF+Eouz6hHHf+\nq0anWX5qI4gpCpQgrrtD4x+IRDjWfUwX3AJ1RsMgrPkv/aaVjw2u0Qx4m5m8q64s\ndJCjDHHesCLMm7Eygt92mjYrAmJfeUeLovGntwPi1ZWz3Mbd9BRUGQb2mKSEJEao\nhTssMOAfAgMBAAGjgewwgekwHQYDVR0OBBYEFPGN3fwZlmrOpQzO6ZYydSM4xF5m\nMIG5BgNVHSMEgbEwga6AFPGN3fwZlmrOpQzO6ZYydSM4xF5moYGKpIGHMIGEMQsw\nCQYDVQQGEwJVUzERMA8GA1UECBMITmV3IFlvcmsxFjAUBgNVBAcTDU5ldyBZb3Jr\nIENpdHkxEDAOBgNVBAoTB01vbmdvREIxEDAOBgNVBAsTB0RyaXZlcnMxJjAkBgNV\nBAMTHUV4YW1wbGUgR28gRHJpdmVyIENlcnRpZmljYXRlggkAz7XHesuueEQwDAYD\nVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEARyAiYkXwGRyLOkjcZ9+5ZYD8\nfyLSYda4LpPUWL46nJGYJvDZrfEYsX9nCrOEty0WwBa6WVZupV/beLSgosLf3DmS\npApExPw7tOZAxD+MtX5IM3jA3+EDHZpSZElK4VeZ0MGE3ZLj8MzcKF1qDjOIVjxk\nbCnDBaO/SG4oEIzUwwiTUgwjMwTOta3uM9CUuoYw9KEPtYzh696jzGqPwkqDMMID\nNCl1VDgp1TzTHSDOsmjUWMghnozmv4hGPhA67kOWxpnwRd+LadcofvmXmqp13c3Z\nWBQQqkcBd92Ud00tBkaYYAZjjhnHEJyCE+TR2DKnk7u/uyBqD2DqbEKXInHcow==\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "mongo/options/testdata/certificate.pem",
    "content": "-----BEGIN CERTIFICATE-----\nMIIElTCCA32gAwIBAgIJAL4i1e9b7VeFMA0GCSqGSIb3DQEBBQUAMIGNMQswCQYD\nVQQGEwJVUzERMA8GA1UECBMITmV3IFlvcmsxFjAUBgNVBAcTDU5ldyBZb3JrIENp\ndHkxFjAUBgNVBAoTDU1vbmdvREIsIEluYy4xHTAbBgNVBAsTFERyaXZlcnMsIEdv\nOiBFeGFtcGxlMRwwGgYDVQQDExNleGFtcGxlLm1vbmdvZGIuY29tMB4XDTE5MDIw\nODE5MDgxOFoXDTI5MDIwNTE5MDgxOFowgY0xCzAJBgNVBAYTAlVTMREwDwYDVQQI\nEwhOZXcgWW9yazEWMBQGA1UEBxMNTmV3IFlvcmsgQ2l0eTEWMBQGA1UEChMNTW9u\nZ29EQiwgSW5jLjEdMBsGA1UECxMURHJpdmVycywgR286IEV4YW1wbGUxHDAaBgNV\nBAMTE2V4YW1wbGUubW9uZ29kYi5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw\nggEKAoIBAQDHslvR+trs9X/1SZ9xo+bUD+/zi6wKzAaNFwXCDITJY6EW8s3mL1mC\nRdAO4lVH9ZHIMWQmjGtTNTFcASjlRxB361y69iOiIOnTWa0WUSnFdNVVxCeSEzw0\n3K2SnybdH4hdH5PGYrUcd38Zhw9WMZmrEZ5f3YaT9ODFsy/t8vusnB2tSjwb6Wev\n9daGb4k3/gzB2xT7UEo9otzsXrN+DeSwFNpkRBYkKlZ+evcwK6km8CZBpNDAJ6mP\naJ+caKwDY0i0QordvEnzmbd/CsbX6JCSX2F7yrWT2cQZN6MfGHM9ZzaNfxjGsHFd\nRxpDIT0e5aRfpnpIrALQNCR57gWH+jwTAgMBAAGjgfUwgfIwHQYDVR0OBBYEFFqV\nmsnbtRAv9Z25KQw1TnX3WO94MIHCBgNVHSMEgbowgbeAFFqVmsnbtRAv9Z25KQw1\nTnX3WO94oYGTpIGQMIGNMQswCQYDVQQGEwJVUzERMA8GA1UECBMITmV3IFlvcmsx\nFjAUBgNVBAcTDU5ldyBZb3JrIENpdHkxFjAUBgNVBAoTDU1vbmdvREIsIEluYy4x\nHTAbBgNVBAsTFERyaXZlcnMsIEdvOiBFeGFtcGxlMRwwGgYDVQQDExNleGFtcGxl\nLm1vbmdvZGIuY29tggkAviLV71vtV4UwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0B\nAQUFAAOCAQEAhg4g7KZ6wekhgL+XQ7OM7Xh+VVX375cRRSblsYz5ws6Tju0SJYGU\nUcNBY1XpwTx36j4Fqbygboj+hLi0adL1NrXBfEYLNXiO8IQd+jskzn884+U2ii1l\nynNXzUJ3ByIw2Ufsr7hlhrUzb2R6IaRAfBnUxDtB6Q7rXj9g60n6phbWv991mhzj\nQhd6+gK+4L1AGr8GSpvuI8G6WUvkIJ7XMy0Cvc3p6OJ6kn5dHxUgQSLb/le4lGiN\nB7dB4cIhV1oN3LMk29PY2iY7RV5NZBaEEAk9T58NypRVQK8DoMQtkbpvWKTUbKqa\nI2YZ4IbcVfWVSEgspi59NMm8/jmDMot6Fw==\n-----END CERTIFICATE-----\n-----BEGIN RSA PRIVATE KEY-----\nProc-Type: 4,ENCRYPTED\nDEK-Info: DES-EDE3-CBC,29C1684ECF3DEADF\n\nWX/uHcI/ORmzz4N9Pd7FQNRYJo5fye+jsPgmaVBvaVE6y92sbEGoaT8Wzvrm1JYd\nyD8emPLHWZuchA7CBx51rc736FWE5oyqf5CLAdp/ZOr4W0VDnhjOjp5wYB/k5c4y\n1lyqFZPejEU0c9UlRiXmZQKKXm7TafM5VSedhDALPuD3aa8lUPAfzVQkkMj3kEoD\nYYcMXNA3GiPRiQipbVEcISuhzxQFbqCK1TQBP2xXx12Dl6vGrINElWXGJP4FAqzv\n9m8/Pwu/1RZtEWW7+W4powVM/vCGaRPC0p/YD55BzceC/OLzVDspHajYPvDE0s/e\nlmFFPcZaoThFFbIGhqYESFqJ2xcAaQLGsp4viet7JbTfhvZjoYq5ZNcTSlVkDsWz\nCw/kFbFiJoc9yf2AYdvbEazS4SUAzEILliE8ETXTyIqiwhqRq5ZDLbQzVptI2Qhy\nDazRdQsUon/7avSYHTcI8e2aUGeyPzujRHfzXgPVR4f6TAy/DJ6mWT+5lP93AdXM\nIcfpp2Y5K8VY6N21jPPK9iPixTO2kndRXgGxLsaG8D2bcEAdM3H5BMZCBIBNPTUl\nRE79Kdp4YpCNuIHKk80zfGZM5TPJgpTJvd2nDEMM4522nDVF116PByd62S7gj5D6\no0rjpV/Vm26ech2O4d3dwKC+4K7ZHicUIo3HJ16oyxKPniYOlbYRrYUY3kNLmfPG\nHF8ZuXauz27x0FGI3zwrEt8j9q1g6l9TIY2NrOEEWoQuK7CrtK4J95/Ypm5bKCdC\nyy/saiST9SpjCoz96JhHFY1/zDXqenV9Xz6V1if/42kMJx/nbixpJkHktNCgoZ9i\n5ouT+GZMW0VxtlpG/SgGYSICHlbdSRdrKsJQ752GiqQ7Ijm3gyzlZNyWUY8M/L6U\nvKf18YKHOBHBVhleiLi78INVNABtJWoLvJAizjBGD0Nqpo8i1GyPcQv8OTWjbEH6\n+gWf6tAaSH+cvJwP0CO+PoyoQ7pf4Gdmo+UFPfEhjXjSpSzY3n7hlmqmTvJ15XD2\nULS4K2w4WBq/wbaRkuWOrqDcI4EoJitCQGtG+XmwErMslbuHPcRPHZvq5F6D34O5\n+6z8c75/bpGHCcSOb4aBwU5LrxTPvbiBML42Es7pdy8D4M8PH+W7enjC+jeBS3ey\nvkq6Bc4FTS67SWxAx2rXPIIqjgXO/W3iGECycwnJNGtTA3Xg4CDiJGDqEsKZcCpn\nSX4CLwGytJhHW8KeSW2i3MjjR7BBzebej8HSzRrdxDO9J8J/usmWPo6T3sgozZuX\nHvMI9ApYsm0cYkouqgTmNrMwhyb2hvQbIlJY6Q0l3UekQAuV2RdVQekKgSOY0k6j\nKnfMvxVFWpgDfaF/tVQ19kbSLkIlXUc8T4O9Vu61BYRhLY3jxSq8WN5I5sBAJJJ/\nWL46ihd4ttkAKjSH6WUnDgXDMWp/mjUwYB+JZKzRr1C97kWAKdsFI+J83aBcCKtZ\nM5l8Sq9QsosNuAV7UjKJMwUz6mT4U/txTzEvShQTBbHTHAveS5cUYlW40dTtaNPJ\nXxEt/4jpjugODg94nWxDV55vfn8RkmpVxPKMOGKDDUQunuLXAdP97Gu2RoFqFVaQ\n-----END RSA PRIVATE KEY-----\n"
  },
  {
    "path": "mongo/options/testdata/csr.json",
    "content": "{\n    \"hosts\": [\n        \"example.com\",\n        \"www.example.com\",\n        \"127.0.0.1\"\n    ],\n    \"key\": {\n        \"algo\": \"rsa\",\n        \"size\": 2048\n    },\n    \"names\": [\n        {\n            \"C\":  \"US\",\n            \"L\":  \"New York City\",\n            \"O\":  \"MongoDB, Inc\",\n            \"OU\": \"WWW\",\n            \"ST\": \"New York\"\n        }\n    ]\n}\n"
  },
  {
    "path": "mongo/options/testdata/empty-ca.pem",
    "content": ""
  },
  {
    "path": "mongo/options/testdata/key.pem",
    "content": "-----BEGIN RSA PRIVATE KEY-----\nProc-Type: 4,ENCRYPTED\nDEK-Info: DES-EDE3-CBC,29C1684ECF3DEADF\n\nWX/uHcI/ORmzz4N9Pd7FQNRYJo5fye+jsPgmaVBvaVE6y92sbEGoaT8Wzvrm1JYd\nyD8emPLHWZuchA7CBx51rc736FWE5oyqf5CLAdp/ZOr4W0VDnhjOjp5wYB/k5c4y\n1lyqFZPejEU0c9UlRiXmZQKKXm7TafM5VSedhDALPuD3aa8lUPAfzVQkkMj3kEoD\nYYcMXNA3GiPRiQipbVEcISuhzxQFbqCK1TQBP2xXx12Dl6vGrINElWXGJP4FAqzv\n9m8/Pwu/1RZtEWW7+W4powVM/vCGaRPC0p/YD55BzceC/OLzVDspHajYPvDE0s/e\nlmFFPcZaoThFFbIGhqYESFqJ2xcAaQLGsp4viet7JbTfhvZjoYq5ZNcTSlVkDsWz\nCw/kFbFiJoc9yf2AYdvbEazS4SUAzEILliE8ETXTyIqiwhqRq5ZDLbQzVptI2Qhy\nDazRdQsUon/7avSYHTcI8e2aUGeyPzujRHfzXgPVR4f6TAy/DJ6mWT+5lP93AdXM\nIcfpp2Y5K8VY6N21jPPK9iPixTO2kndRXgGxLsaG8D2bcEAdM3H5BMZCBIBNPTUl\nRE79Kdp4YpCNuIHKk80zfGZM5TPJgpTJvd2nDEMM4522nDVF116PByd62S7gj5D6\no0rjpV/Vm26ech2O4d3dwKC+4K7ZHicUIo3HJ16oyxKPniYOlbYRrYUY3kNLmfPG\nHF8ZuXauz27x0FGI3zwrEt8j9q1g6l9TIY2NrOEEWoQuK7CrtK4J95/Ypm5bKCdC\nyy/saiST9SpjCoz96JhHFY1/zDXqenV9Xz6V1if/42kMJx/nbixpJkHktNCgoZ9i\n5ouT+GZMW0VxtlpG/SgGYSICHlbdSRdrKsJQ752GiqQ7Ijm3gyzlZNyWUY8M/L6U\nvKf18YKHOBHBVhleiLi78INVNABtJWoLvJAizjBGD0Nqpo8i1GyPcQv8OTWjbEH6\n+gWf6tAaSH+cvJwP0CO+PoyoQ7pf4Gdmo+UFPfEhjXjSpSzY3n7hlmqmTvJ15XD2\nULS4K2w4WBq/wbaRkuWOrqDcI4EoJitCQGtG+XmwErMslbuHPcRPHZvq5F6D34O5\n+6z8c75/bpGHCcSOb4aBwU5LrxTPvbiBML42Es7pdy8D4M8PH+W7enjC+jeBS3ey\nvkq6Bc4FTS67SWxAx2rXPIIqjgXO/W3iGECycwnJNGtTA3Xg4CDiJGDqEsKZcCpn\nSX4CLwGytJhHW8KeSW2i3MjjR7BBzebej8HSzRrdxDO9J8J/usmWPo6T3sgozZuX\nHvMI9ApYsm0cYkouqgTmNrMwhyb2hvQbIlJY6Q0l3UekQAuV2RdVQekKgSOY0k6j\nKnfMvxVFWpgDfaF/tVQ19kbSLkIlXUc8T4O9Vu61BYRhLY3jxSq8WN5I5sBAJJJ/\nWL46ihd4ttkAKjSH6WUnDgXDMWp/mjUwYB+JZKzRr1C97kWAKdsFI+J83aBcCKtZ\nM5l8Sq9QsosNuAV7UjKJMwUz6mT4U/txTzEvShQTBbHTHAveS5cUYlW40dTtaNPJ\nXxEt/4jpjugODg94nWxDV55vfn8RkmpVxPKMOGKDDUQunuLXAdP97Gu2RoFqFVaQ\n-----END RSA PRIVATE KEY-----\n"
  },
  {
    "path": "mongo/options/testdata/malformed-ca.pem",
    "content": "totally valid CA certificate\n"
  },
  {
    "path": "mongo/options/testdata/nopass/cert.pem",
    "content": "-----BEGIN CERTIFICATE-----\nMIIDxjCCAq6gAwIBAgIUdT7iyEmPsG2bPvhF080w1UpbXEowDQYJKoZIhvcNAQEL\nBQAwXTELMAkGA1UEBhMCVVMxETAPBgNVBAgTCE5ldyBZb3JrMRYwFAYDVQQHEw1O\nZXcgWW9yayBDaXR5MRUwEwYDVQQKEwxNb25nb0RCLCBJbmMxDDAKBgNVBAsTA1dX\nVzAeFw0xOTAyMDgxODUzMDBaFw0yMDAyMDgxODUzMDBaMF0xCzAJBgNVBAYTAlVT\nMREwDwYDVQQIEwhOZXcgWW9yazEWMBQGA1UEBxMNTmV3IFlvcmsgQ2l0eTEVMBMG\nA1UEChMMTW9uZ29EQiwgSW5jMQwwCgYDVQQLEwNXV1cwggEiMA0GCSqGSIb3DQEB\nAQUAA4IBDwAwggEKAoIBAQCS1V3pEo4El+dii490Nz3qi1iLWx+bzUXpM96NDcFF\nv/u9VHBtyOlCSK9/kgzbnq01PqMJr3Rq3x6K1Lwd+wlqpp+bfNCRn7unhfiRQ3aL\nBpZbNSYrtaWMWK2RqqdBv5lIM0xoGsFpZT4QhbAdc/i7HajlGgrkizPvcsvQadsA\nItj7sbPZswqEqQS+nFLpPmA0vF5klVESZsuzXiEtO1crHNYSXAbSaYZfl3A7xUhw\nidtroXm28HifF1DcPX8QiEREjgPDyZdJpbOmDt6xx52pRpX78GgWfHSusgESezwi\nlMUVJ0tyXFMvhqdKnW//RV/OUe8UFES/IVZ5l+r1t1EfAgMBAAGjfjB8MA4GA1Ud\nDwEB/wQEAwIFoDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwDAYDVR0T\nAQH/BAIwADAdBgNVHQ4EFgQUrs6cayQcO4BLP8ccND0O/hvBTK0wHgYDVR0RBBcw\nFYITZXhhbXBsZS5tb25nb2RiLmNvbTANBgkqhkiG9w0BAQsFAAOCAQEAmXvIbyK+\nGyPN9KQpN/gS4XB6ajVAmTqNqvxMs6Ql/y6crjBn2wP0FIYNuDThP0byeOLBUmOL\n/0Qz3QpeCOCIal03fZdUte0Nypj/N6LFbo6V53Ir+AFz7rzKHPMj52KoPGTLDhgN\nLqanKkO630+pOoYeNfFLKJpEX+5JqiVMqCxwIdjmz94MG2RAKS4kGjtTLL0Ac52N\ngWb/YJKhQazoRuFqLpowcQwOIgpis4goZnJ3qm4wakY50/fOLTLZutkJu4zkGenG\n+x8NZ7ht6uVHVNhC8IIC/b60VOr3rylBZm+NTcP2AaWKZ5TVEyIm8CkGnF7D/4aa\nXYPCLNQsuA93sg==\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "mongo/options/testdata/nopass/certificate.pem",
    "content": "-----BEGIN CERTIFICATE-----\nMIIDxjCCAq6gAwIBAgIUdT7iyEmPsG2bPvhF080w1UpbXEowDQYJKoZIhvcNAQEL\nBQAwXTELMAkGA1UEBhMCVVMxETAPBgNVBAgTCE5ldyBZb3JrMRYwFAYDVQQHEw1O\nZXcgWW9yayBDaXR5MRUwEwYDVQQKEwxNb25nb0RCLCBJbmMxDDAKBgNVBAsTA1dX\nVzAeFw0xOTAyMDgxODUzMDBaFw0yMDAyMDgxODUzMDBaMF0xCzAJBgNVBAYTAlVT\nMREwDwYDVQQIEwhOZXcgWW9yazEWMBQGA1UEBxMNTmV3IFlvcmsgQ2l0eTEVMBMG\nA1UEChMMTW9uZ29EQiwgSW5jMQwwCgYDVQQLEwNXV1cwggEiMA0GCSqGSIb3DQEB\nAQUAA4IBDwAwggEKAoIBAQCS1V3pEo4El+dii490Nz3qi1iLWx+bzUXpM96NDcFF\nv/u9VHBtyOlCSK9/kgzbnq01PqMJr3Rq3x6K1Lwd+wlqpp+bfNCRn7unhfiRQ3aL\nBpZbNSYrtaWMWK2RqqdBv5lIM0xoGsFpZT4QhbAdc/i7HajlGgrkizPvcsvQadsA\nItj7sbPZswqEqQS+nFLpPmA0vF5klVESZsuzXiEtO1crHNYSXAbSaYZfl3A7xUhw\nidtroXm28HifF1DcPX8QiEREjgPDyZdJpbOmDt6xx52pRpX78GgWfHSusgESezwi\nlMUVJ0tyXFMvhqdKnW//RV/OUe8UFES/IVZ5l+r1t1EfAgMBAAGjfjB8MA4GA1Ud\nDwEB/wQEAwIFoDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwDAYDVR0T\nAQH/BAIwADAdBgNVHQ4EFgQUrs6cayQcO4BLP8ccND0O/hvBTK0wHgYDVR0RBBcw\nFYITZXhhbXBsZS5tb25nb2RiLmNvbTANBgkqhkiG9w0BAQsFAAOCAQEAmXvIbyK+\nGyPN9KQpN/gS4XB6ajVAmTqNqvxMs6Ql/y6crjBn2wP0FIYNuDThP0byeOLBUmOL\n/0Qz3QpeCOCIal03fZdUte0Nypj/N6LFbo6V53Ir+AFz7rzKHPMj52KoPGTLDhgN\nLqanKkO630+pOoYeNfFLKJpEX+5JqiVMqCxwIdjmz94MG2RAKS4kGjtTLL0Ac52N\ngWb/YJKhQazoRuFqLpowcQwOIgpis4goZnJ3qm4wakY50/fOLTLZutkJu4zkGenG\n+x8NZ7ht6uVHVNhC8IIC/b60VOr3rylBZm+NTcP2AaWKZ5TVEyIm8CkGnF7D/4aa\nXYPCLNQsuA93sg==\n-----END CERTIFICATE-----\n-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEAktVd6RKOBJfnYouPdDc96otYi1sfm81F6TPejQ3BRb/7vVRw\nbcjpQkivf5IM256tNT6jCa90at8eitS8HfsJaqafm3zQkZ+7p4X4kUN2iwaWWzUm\nK7WljFitkaqnQb+ZSDNMaBrBaWU+EIWwHXP4ux2o5RoK5Isz73LL0GnbACLY+7Gz\n2bMKhKkEvpxS6T5gNLxeZJVREmbLs14hLTtXKxzWElwG0mmGX5dwO8VIcInba6F5\ntvB4nxdQ3D1/EIhERI4Dw8mXSaWzpg7escedqUaV+/BoFnx0rrIBEns8IpTFFSdL\nclxTL4anSp1v/0VfzlHvFBREvyFWeZfq9bdRHwIDAQABAoIBAGHqRPii9e+cOKGf\nfWc7W1+n0sEDYB2ukntDqpoXXRYdQ0trHponS113SQjiJQ7jTo/eGCziQtjtttlP\nqa64c/wZ5v9yCUfC5Al/rlV4TnnBR8zEsZKeTCo+DGjCC00t7pKWGtKlXOrip7Ou\n4tWRO4HT8ofsiO0E4vo/bCisHm2EzOSwjwT1RBV6wuOxVNa5ITY6nq/4zu+KbcQP\nx4T1jAk3mte9E9+C2k5UXB+uBSg+jxnw+7MgP0Lr5ZtkKHf3rdcpWcHXTkR3Jjsp\neBGx8A+aiqEdXIxFYIMomJrUsSPYM2pZ2foFr0EuJA2c2Ht6dh6PKpM3WYEmxGJg\nltuwriECgYEAw43/MNYS4hSEU9XRnsry71T16ZpIf1DEGNdm8hUpR5l5xGWxBCNN\nt/UShGklpRrXjSrZZmvCJRfJ4EfSGMUk22xy1gtkHq/3/mcfuLUTL2qi+EmGyLI/\nlLni94msCZWJKfXQphVt0BdlMK6SrN3dQTZhOYXBEe4AMaoWHFJXYBECgYEAwDge\ntKHDzUycfbLPxfRsg5TinuFe6JZ2RZwf+sY+X9+v96cIiYH7eobO4MxcX/CriDlX\nfHfqrFYhwXGkw9vwPfRx2BRMPELh2u6+ZKnt5vUahsEWE29nLjlZa+OPsa8NoxXv\nbNS03VhPu2pwf6LMWnQVXzR9zagwMetX1dFvzi8CgYEAiBR24WAGseY/Eyu2e/6U\nQPFpUo63JklovDSggZMyL43gS0fMf6mlen6CzTtyI8ti9ew8bmZMKo28QGMaE3yU\nolB8NPCmUih3tUPDsj/nQta843Qe4+gOEIuuIj1eRykU0usor3o3+VdeZhsFHGAj\niC60R1gJO1XYMrc+PaUbAlECgYBpIgjBWTzj3AQ3yAZWSjLT4Sn2JmQnqKv1tMfU\n/Hr/KzRuqsBDnYFzaapbT8syeksHjf1oyZnkOpPp3wN0UKZc7Icb3TY5O4eWnEAs\nhLjnOqYQkClH5gW0RY/AL83XLFy0F3pFty1ZZ0Tsx/HSDbPhwV4zHV21j0kU9Jit\nDQku5wKBgEjABXtkNyAH2PObn10sPphaAFFJop6QRNpwwF/dx+jrd+nPd+wZzK9U\nAbE6K+VjjZ39vQ0mfawfHwjyQ3QR3Pb/fGzmQ8pQjpGT227A8IdcGOy6Acffg+uF\nV8K9VbdLIIC7A3h3Bwf7E3aGddIB+rJeApAg/8UlFgvMiJDb8a+2\n-----END RSA PRIVATE KEY-----\n"
  },
  {
    "path": "mongo/options/testdata/nopass/key.pem",
    "content": "-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEAktVd6RKOBJfnYouPdDc96otYi1sfm81F6TPejQ3BRb/7vVRw\nbcjpQkivf5IM256tNT6jCa90at8eitS8HfsJaqafm3zQkZ+7p4X4kUN2iwaWWzUm\nK7WljFitkaqnQb+ZSDNMaBrBaWU+EIWwHXP4ux2o5RoK5Isz73LL0GnbACLY+7Gz\n2bMKhKkEvpxS6T5gNLxeZJVREmbLs14hLTtXKxzWElwG0mmGX5dwO8VIcInba6F5\ntvB4nxdQ3D1/EIhERI4Dw8mXSaWzpg7escedqUaV+/BoFnx0rrIBEns8IpTFFSdL\nclxTL4anSp1v/0VfzlHvFBREvyFWeZfq9bdRHwIDAQABAoIBAGHqRPii9e+cOKGf\nfWc7W1+n0sEDYB2ukntDqpoXXRYdQ0trHponS113SQjiJQ7jTo/eGCziQtjtttlP\nqa64c/wZ5v9yCUfC5Al/rlV4TnnBR8zEsZKeTCo+DGjCC00t7pKWGtKlXOrip7Ou\n4tWRO4HT8ofsiO0E4vo/bCisHm2EzOSwjwT1RBV6wuOxVNa5ITY6nq/4zu+KbcQP\nx4T1jAk3mte9E9+C2k5UXB+uBSg+jxnw+7MgP0Lr5ZtkKHf3rdcpWcHXTkR3Jjsp\neBGx8A+aiqEdXIxFYIMomJrUsSPYM2pZ2foFr0EuJA2c2Ht6dh6PKpM3WYEmxGJg\nltuwriECgYEAw43/MNYS4hSEU9XRnsry71T16ZpIf1DEGNdm8hUpR5l5xGWxBCNN\nt/UShGklpRrXjSrZZmvCJRfJ4EfSGMUk22xy1gtkHq/3/mcfuLUTL2qi+EmGyLI/\nlLni94msCZWJKfXQphVt0BdlMK6SrN3dQTZhOYXBEe4AMaoWHFJXYBECgYEAwDge\ntKHDzUycfbLPxfRsg5TinuFe6JZ2RZwf+sY+X9+v96cIiYH7eobO4MxcX/CriDlX\nfHfqrFYhwXGkw9vwPfRx2BRMPELh2u6+ZKnt5vUahsEWE29nLjlZa+OPsa8NoxXv\nbNS03VhPu2pwf6LMWnQVXzR9zagwMetX1dFvzi8CgYEAiBR24WAGseY/Eyu2e/6U\nQPFpUo63JklovDSggZMyL43gS0fMf6mlen6CzTtyI8ti9ew8bmZMKo28QGMaE3yU\nolB8NPCmUih3tUPDsj/nQta843Qe4+gOEIuuIj1eRykU0usor3o3+VdeZhsFHGAj\niC60R1gJO1XYMrc+PaUbAlECgYBpIgjBWTzj3AQ3yAZWSjLT4Sn2JmQnqKv1tMfU\n/Hr/KzRuqsBDnYFzaapbT8syeksHjf1oyZnkOpPp3wN0UKZc7Icb3TY5O4eWnEAs\nhLjnOqYQkClH5gW0RY/AL83XLFy0F3pFty1ZZ0Tsx/HSDbPhwV4zHV21j0kU9Jit\nDQku5wKBgEjABXtkNyAH2PObn10sPphaAFFJop6QRNpwwF/dx+jrd+nPd+wZzK9U\nAbE6K+VjjZ39vQ0mfawfHwjyQ3QR3Pb/fGzmQ8pQjpGT227A8IdcGOy6Acffg+uF\nV8K9VbdLIIC7A3h3Bwf7E3aGddIB+rJeApAg/8UlFgvMiJDb8a+2\n-----END RSA PRIVATE KEY-----\n"
  },
  {
    "path": "mongo/options/testdata/one-pk-multiple-certs.pem",
    "content": "This .pem file includes the following:\n- Private Key\n- Certificate 1 for Private Key\n- Certificate 2 for Issuer of Certificate 1.\n\nThis .pem file was created from test certificates from:\nhttps://x509gen.corp.mongodb.com/#/cert/5ce5b21a42a0ef0008b11399\n\n-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEAgqdjbfumni3Z8A7q2TpVvk1XAHAT/LGsNOEZ/Ksuypv+q3BK\nBewgsF54n0oc2UshHDw8hIO6UsmsjRplySTvEfLkEaDiCA9xQ/TYVqSbx5PtFIVg\nicPniCl01w3cjsO/GqydeyE9O7X7u58tlEyXNgW+5s9wROPSV/TM5gFNmMOHVDJz\nQEiQVyVzd/96gYQHS44C/tibXb8Au+Z3dX0B5xPncbTITiZFpxqE7LXY29ZsyhOe\nhbA9YS+YmsVnYJI52kWUMeo4HDXYNa7vifdJmbRhmpAAyvDatZdwvdWlEp0odf8k\nJV9LNGvc9GZ31UhZmUAUYt4zR1awFr0xUOalMwIDAQABAoIBADrEW77OoGUprouB\nyjSnPoX4d5ek0fipWhH25h92R/euLuCiA0miqhlqhMrxJQS//Eac+YOJVLeEKu7U\n3s+Yo0fpfphHSyPg/ktYyC5AoCkm6k5+p6uPyIbATuOkQn6coUq4NaV6j/ZZpr7/\nnBrGUIVmFDXxPEhgSO5zF+6ky2hYdF/XSRHSS3ieGxy6PD9dHpWebVnZJ1EtElEc\n8h+K0pJzXol++WWNR0S9OZHbYOWfew/2r4K2jfNAYsVjXOLGb2k1k/jC2qN8fq7R\n6BGB0D2932X0WfUXBTxj3uBeTcz25ZnQeXuWmcmGRePBuW1nZaZnKcowbdu5gp19\npeaVzWECgYEA0fo9KEb1ffUzZTvX42PX0sZyxCmqLqOOQ9DWt9HNIscykIszF4XH\nnrrTASD5p3IAweZI/g7VGxjx5C9irqH4NLIswILCcaSyLKzIy9rj4D/4cEdFT4YQ\n8zTWXJAEgyphOSBPrefiwBDtbaQyUU+5lOXLEh+ceXjd/56WS0fOBukCgYEAn0pU\nQg3XuWBi0RbaGWdFJ/A/DTmNP6tn9ULij7bjhpEgOT6E3H7gSUmC4QXKhAu+fdKH\nbmCs5GYGl9UOiERLvKl4HqU2lCniCKNP2ygRibH8BiKBNtLAp6SGRi6SJsJqfvge\nWddtG6WNNBXLpiNzeQJW6Cm0iKJF6skNzkpNMbsCgYEAwuwP9eHZZvJGocVp5hb7\ndRjv1RXwe5ctK9skWEQUsCJG2FPAPCfeZYV892FLIJv+BAHo5J+USLFha7hpXT/S\nj+iPDq7UHncLR9mmFUe/np8D3AbtWqNT/NGh5Q90gObIliVekkve/Pk5zzxL0Zu2\n5XhkiRB7S3BedTscvgmMQnECgYAF8pGI0dl/K3ElG3RTRH7Ziefgx4hVm0HLb47k\nDNEcAikw2fu9++LF+b+wRTYVjhazvfuejG+IK6MNYkAjqGEnjzBT/Jk4GQQ+jFJ7\n/VgCSyn81Kb8dAwLdmIpFq4QUmwFh6AifJ+vS5QlILc04df66bJzArOh8mUn6g/g\n5vdQ5wKBgBxewVKdmZY/GLn8ieFOHimG8y2xKa59j6nMTGaklKEoSyW60GiHk+Jl\nq5LhUwHOQx3HzD5dy0ne2+0gN88iySfIe5rGXS7S9Pvnu06WIIbAnGWfedl026KJ\nTfg4SpzwaZtnXN/UJ62aaNgW2uYrdy4wF4M8zhXsgpj9w6xF369x\n-----END RSA PRIVATE KEY-----\n-----BEGIN CERTIFICATE-----\nMIIDhTCCAm2gAwIBAgIDAxBVMA0GCSqGSIb3DQEBCwUAMHoxHDAaBgNVBAMTE2Ns\naWVudC1pbnRlcm1lZGlhdGUxEDAOBgNVBAsTB0RyaXZlcnMxEDAOBgNVBAoTB01v\nbmdvREIxFjAUBgNVBAcTDU5ldyBZb3JrIENpdHkxETAPBgNVBAgTCE5ldyBZb3Jr\nMQswCQYDVQQGEwJVUzAeFw0xOTA2MDMxNTQyMjVaFw0zOTA2MDMxNTQyMjVaMHAx\nEjAQBgNVBAMTCWxvY2FsaG9zdDEQMA4GA1UECxMHRHJpdmVyczEQMA4GA1UEChMH\nTW9uZ29EQjEWMBQGA1UEBxMNTmV3IFlvcmsgQ2l0eTERMA8GA1UECBMITmV3IFlv\ncmsxCzAJBgNVBAYTAlVTMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA\ngqdjbfumni3Z8A7q2TpVvk1XAHAT/LGsNOEZ/Ksuypv+q3BKBewgsF54n0oc2Ush\nHDw8hIO6UsmsjRplySTvEfLkEaDiCA9xQ/TYVqSbx5PtFIVgicPniCl01w3cjsO/\nGqydeyE9O7X7u58tlEyXNgW+5s9wROPSV/TM5gFNmMOHVDJzQEiQVyVzd/96gYQH\nS44C/tibXb8Au+Z3dX0B5xPncbTITiZFpxqE7LXY29ZsyhOehbA9YS+YmsVnYJI5\n2kWUMeo4HDXYNa7vifdJmbRhmpAAyvDatZdwvdWlEp0odf8kJV9LNGvc9GZ31UhZ\nmUAUYt4zR1awFr0xUOalMwIDAQABox4wHDAaBgNVHREEEzARgglsb2NhbGhvc3SH\nBH8AAAEwDQYJKoZIhvcNAQELBQADggEBABNAqbfQon7VDIZIVoxL7fNsyv4y/mGI\ngR9hTfSJHVyFpgTHjIobKYDpRx01uVTjEGMVjOAcpOYQIfTa7MFdbELrH5q6UUyl\nJ5i65MJlzIWgrchVzzgcP5UeBRnuUrFRDmt8VOGIA7CI6mWW/dDTHg7NVK5PbVi4\nad56ogiptbnj1GKkImV2TCpwYhNxkf68OcwgfbknkWDxPjGFQjvYLspQBYdK+j9s\n0tOGMqbt+8DH7whioH+yv4KhlN9g4R3oOybG82E0fJ8NbdsjEmQby28/f/U8fsjV\nEDnxjuxJs/C9AH42Jpu56Zp16hhEz72mVZqeJZe+11wcUiyFW7jgEGc=\n-----END CERTIFICATE-----\n-----BEGIN CERTIFICATE-----\nMIIDjTCCAnWgAwIBAgIDB0QCMA0GCSqGSIb3DQEBCwUAMHkxGzAZBgNVBAMTEkRy\naXZlcnMgVGVzdGluZyBDQTEQMA4GA1UECxMHRHJpdmVyczEQMA4GA1UEChMHTW9u\nZ29EQjEWMBQGA1UEBxMNTmV3IFlvcmsgQ2l0eTERMA8GA1UECBMITmV3IFlvcmsx\nCzAJBgNVBAYTAlVTMB4XDTE5MDYwMzE1NDAyM1oXDTM5MDYwMzE1NDAyM1owejEc\nMBoGA1UEAxMTY2xpZW50LWludGVybWVkaWF0ZTEQMA4GA1UECxMHRHJpdmVyczEQ\nMA4GA1UEChMHTW9uZ29EQjEWMBQGA1UEBxMNTmV3IFlvcmsgQ2l0eTERMA8GA1UE\nCBMITmV3IFlvcmsxCzAJBgNVBAYTAlVTMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A\nMIIBCgKCAQEAsIKl4kILAnpSbDPQaUNmd8P8X+h45+RXCXmlbNSnKvsDCZU2Q/QV\nBxMaOp7jNwOVBdECCQKYp1EL5B8O7rz7i/yJ80MGnh7mQW1Hnan79eIzq8ah5QSz\nzJNLdzpix6CBR2GyL4BSl3mujNESEYtotcnihP7+YV6Sis9bttzhYhjmHm7346k2\nIpCBhCTS3AEzVL7ExmEc6D7qVA7pMtRFUFoIUzpGNp53v1wUVjwPiiHmzNsuq/kE\nfqurB7WHIogux2xBVdfrU7Pfr+1MdD5rDuUB6VblWQBeeha1L6TvSSP3GHC2fG8j\nCnhVNye7kWStd3r6Zfpqp9VDTUu0uXM9qwIDAQABox0wGzAMBgNVHRMEBTADAQH/\nMAsGA1UdDwQEAwICBDANBgkqhkiG9w0BAQsFAAOCAQEAX66R/Jvk2jUoVnvuPztR\niinyq9JXCXVQuG+dJ4ATy6JB8xmuaDMYmOSwzVyYZWQGIlqkwNZR5wga5OnksicI\nNm6dvhc5JbQ7ERDNgWzNtBaNkzJsaEjc9/FWcLlJeuQN+ZFdFVP+6k4aAWiYwf4A\nKDvyUILEI59TWaScYnQmQcb/v81gLIq1IjaINAkuZcCHAgu2ZWLOSofgnEATiLYH\ncXyy9WeRpd8nB3pgy1OjCN1E7+K6yHYYJYPQol25znAB5TLPbWwA8ek8fdmC3IpG\nHOQXMsoPKCdlgNzG1RXalFSIO1AU2nYO5zeVB1l+WbzEp+o/zq0bi/HcvBhNAtRm\nLg==\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "mongo/options/transactionoptions.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage options\n\nimport (\n\t\"go.mongodb.org/mongo-driver/v2/mongo/readconcern\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/readpref\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/writeconcern\"\n)\n\n// TransactionOptions represents arguments that can be used to configure a\n// transaction.\n//\n// See corresponding setter methods for documentation.\ntype TransactionOptions struct {\n\tReadConcern    *readconcern.ReadConcern\n\tReadPreference *readpref.ReadPref\n\tWriteConcern   *writeconcern.WriteConcern\n}\n\n// TransactionOptionsBuilder contains arguments to configure count operations.\n// Each option can be set through setter functions. See documentation for each\n// setter function for an explanation of the option.\ntype TransactionOptionsBuilder struct {\n\tOpts []func(*TransactionOptions) error\n}\n\n// Transaction creates a new TransactionOptions instance.\nfunc Transaction() *TransactionOptionsBuilder {\n\treturn &TransactionOptionsBuilder{}\n}\n\n// List returns a list of TransactionOptions setter functions.\nfunc (t *TransactionOptionsBuilder) List() []func(*TransactionOptions) error {\n\treturn t.Opts\n}\n\n// SetReadConcern sets the value for the ReadConcern field. Specifies the read concern for operations\n// in the transaction. The default value is nil, which means that the default read concern of the\n// session used to start the transaction will be used.\nfunc (t *TransactionOptionsBuilder) SetReadConcern(rc *readconcern.ReadConcern) *TransactionOptionsBuilder {\n\tt.Opts = append(t.Opts, func(opts *TransactionOptions) error {\n\t\topts.ReadConcern = rc\n\n\t\treturn nil\n\t})\n\n\treturn t\n}\n\n// SetReadPreference sets the value for the ReadPreference field. Specifies the read preference for\n// operations in the transaction. The default value is nil, which means that the default read\n// preference of the session used to start the transaction will be used.\nfunc (t *TransactionOptionsBuilder) SetReadPreference(rp *readpref.ReadPref) *TransactionOptionsBuilder {\n\tt.Opts = append(t.Opts, func(opts *TransactionOptions) error {\n\t\topts.ReadPreference = rp\n\n\t\treturn nil\n\t})\n\n\treturn t\n}\n\n// SetWriteConcern sets the value for the WriteConcern field. Specifies the write concern for\n// operations in the transaction. The default value is nil, which means that the default\n// write concern of the session used to start the transaction will be used.\nfunc (t *TransactionOptionsBuilder) SetWriteConcern(wc *writeconcern.WriteConcern) *TransactionOptionsBuilder {\n\tt.Opts = append(t.Opts, func(opts *TransactionOptions) error {\n\t\topts.WriteConcern = wc\n\n\t\treturn nil\n\t})\n\n\treturn t\n}\n"
  },
  {
    "path": "mongo/options/updateoptions.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage options\n\nimport \"go.mongodb.org/mongo-driver/v2/internal/optionsutil\"\n\n// UpdateOneOptions represents arguments that can be used to configure UpdateOne\n// operations.\n//\n// See corresponding setter methods for documentation.\ntype UpdateOneOptions struct {\n\tArrayFilters             []any\n\tBypassDocumentValidation *bool\n\tCollation                *Collation\n\tComment                  any\n\tHint                     any\n\tUpsert                   *bool\n\tLet                      any\n\tSort                     any\n\n\t// Deprecated: This option is for internal use only and should not be set. It may be changed or removed in any\n\t// release.\n\tInternal optionsutil.Options\n}\n\n// UpdateOneOptionsBuilder contains options to configure UpdateOne operations.\n// Each option can be set through setter functions. See documentation for each\n// setter function for an explanation of the option.\ntype UpdateOneOptionsBuilder struct {\n\tOpts []func(*UpdateOneOptions) error\n}\n\n// UpdateOne creates a new UpdateOneOptions instance.\nfunc UpdateOne() *UpdateOneOptionsBuilder {\n\treturn &UpdateOneOptionsBuilder{}\n}\n\n// List returns a list of UpdateOneOptions setter functions.\nfunc (uo *UpdateOneOptionsBuilder) List() []func(*UpdateOneOptions) error {\n\treturn uo.Opts\n}\n\n// SetArrayFilters sets the value for the ArrayFilters field. ArrayFilters is a\n// set of filters specifying to which array elements an update should apply. The\n// default value is nil, which means the update will apply to all array\n// elements.\nfunc (uo *UpdateOneOptionsBuilder) SetArrayFilters(af []any) *UpdateOneOptionsBuilder {\n\tuo.Opts = append(uo.Opts, func(opts *UpdateOneOptions) error {\n\t\topts.ArrayFilters = af\n\n\t\treturn nil\n\t})\n\n\treturn uo\n}\n\n// SetBypassDocumentValidation sets the value for the BypassDocumentValidation field. If true,\n// writes executed as part of the operation will opt out of document-level validation on the server.\n// The default value is false. See https://www.mongodb.com/docs/manual/core/schema-validation/ for\n// more information about document validation.\nfunc (uo *UpdateOneOptionsBuilder) SetBypassDocumentValidation(b bool) *UpdateOneOptionsBuilder {\n\tuo.Opts = append(uo.Opts, func(opts *UpdateOneOptions) error {\n\t\topts.BypassDocumentValidation = &b\n\n\t\treturn nil\n\t})\n\n\treturn uo\n}\n\n// SetCollation sets the value for the Collation field. Specifies a collation to\n// use for string comparisons during the operation. The default value is nil,\n// which means the default collation of the collection will be used.\nfunc (uo *UpdateOneOptionsBuilder) SetCollation(c *Collation) *UpdateOneOptionsBuilder {\n\tuo.Opts = append(uo.Opts, func(opts *UpdateOneOptions) error {\n\t\topts.Collation = c\n\n\t\treturn nil\n\t})\n\n\treturn uo\n}\n\n// SetComment sets the value for the Comment field. Specifies a string or document that will be\n// included in server logs, profiling logs, and currentOp queries to help trace the operation.\n// The default value is nil, which means that no comment will be included in the logs.\nfunc (uo *UpdateOneOptionsBuilder) SetComment(comment any) *UpdateOneOptionsBuilder {\n\tuo.Opts = append(uo.Opts, func(opts *UpdateOneOptions) error {\n\t\topts.Comment = comment\n\n\t\treturn nil\n\t})\n\n\treturn uo\n}\n\n// SetHint sets the value for the Hint field. Specifies the index to use for the\n// operation. This should either be the index name as a string or the index\n// specification as a document. This option is only valid for MongoDB versions\n// >= 4.2. Server versions < 4.2 will return an error if this option is\n// specified. The driver will return an error if this option is specified during\n// an unacknowledged write operation. The driver will return an error if the\n// hint parameter is a multi-key map. The default value is nil, which means that\n// no hint will be sent.\nfunc (uo *UpdateOneOptionsBuilder) SetHint(h any) *UpdateOneOptionsBuilder {\n\tuo.Opts = append(uo.Opts, func(opts *UpdateOneOptions) error {\n\t\topts.Hint = h\n\n\t\treturn nil\n\t})\n\n\treturn uo\n}\n\n// SetUpsert sets the value for the Upsert field. If true, a new document will be inserted if the\n// filter does not match any documents in the collection. The default value is false.\nfunc (uo *UpdateOneOptionsBuilder) SetUpsert(b bool) *UpdateOneOptionsBuilder {\n\tuo.Opts = append(uo.Opts, func(opts *UpdateOneOptions) error {\n\t\topts.Upsert = &b\n\n\t\treturn nil\n\t})\n\n\treturn uo\n}\n\n// SetLet sets the value for the Let field. Specifies parameters for the update expression. This\n// option is only valid for MongoDB versions >= 5.0. Older servers will report an error for using\n// this option. This must be a document mapping parameter names to values. Values must be constant\n// or closed expressions that do not reference document fields. Parameters can then be accessed\n// as variables in an aggregate expression context (e.g. \"$$var\").\nfunc (uo *UpdateOneOptionsBuilder) SetLet(l any) *UpdateOneOptionsBuilder {\n\tuo.Opts = append(uo.Opts, func(opts *UpdateOneOptions) error {\n\t\topts.Let = l\n\n\t\treturn nil\n\t})\n\n\treturn uo\n}\n\n// SetSort sets the value for the Sort field. Specifies a document specifying which document should\n// be updated if the filter used by the operation matches multiple documents in the collection. If\n// set, the first document in the sorted order will be updated. This option is only valid for MongoDB\n// versions >= 8.0. The sort parameter is evaluated sequentially, so the driver will return an error\n// if it is a multi-key map (which is unordeded). The default value is nil.\nfunc (uo *UpdateOneOptionsBuilder) SetSort(s any) *UpdateOneOptionsBuilder {\n\tuo.Opts = append(uo.Opts, func(opts *UpdateOneOptions) error {\n\t\topts.Sort = s\n\n\t\treturn nil\n\t})\n\n\treturn uo\n}\n\n// UpdateManyOptions represents arguments that can be used to configure UpdateMany\n// operations.\n//\n// See corresponding setter methods for documentation.\ntype UpdateManyOptions struct {\n\tArrayFilters             []any\n\tBypassDocumentValidation *bool\n\tCollation                *Collation\n\tComment                  any\n\tHint                     any\n\tUpsert                   *bool\n\tLet                      any\n\n\t// Deprecated: This option is for internal use only and should not be set. It may be changed or removed in any\n\t// release.\n\tInternal optionsutil.Options\n}\n\n// UpdateManyOptionsBuilder contains options to configure UpdateMany operations.\n// Each option can be set through setter functions. See documentation for each\n// setter function for an explanation of the option.\ntype UpdateManyOptionsBuilder struct {\n\tOpts []func(*UpdateManyOptions) error\n}\n\n// UpdateMany creates a new UpdateManyOptions instance.\nfunc UpdateMany() *UpdateManyOptionsBuilder {\n\treturn &UpdateManyOptionsBuilder{}\n}\n\n// List returns a list of UpdateManyOptions setter functions.\nfunc (uo *UpdateManyOptionsBuilder) List() []func(*UpdateManyOptions) error {\n\treturn uo.Opts\n}\n\n// SetArrayFilters sets the value for the ArrayFilters field. ArrayFilters is a\n// set of filters specifying to which array elements an update should apply. The\n// default value is nil, which means the update will apply to all array\n// elements.\nfunc (uo *UpdateManyOptionsBuilder) SetArrayFilters(af []any) *UpdateManyOptionsBuilder {\n\tuo.Opts = append(uo.Opts, func(opts *UpdateManyOptions) error {\n\t\topts.ArrayFilters = af\n\n\t\treturn nil\n\t})\n\n\treturn uo\n}\n\n// SetBypassDocumentValidation sets the value for the BypassDocumentValidation field. If true,\n// writes executed as part of the operation will opt out of document-level validation on the server.\n// The default value is false. See https://www.mongodb.com/docs/manual/core/schema-validation/ for\n// more information about document validation.\nfunc (uo *UpdateManyOptionsBuilder) SetBypassDocumentValidation(b bool) *UpdateManyOptionsBuilder {\n\tuo.Opts = append(uo.Opts, func(opts *UpdateManyOptions) error {\n\t\topts.BypassDocumentValidation = &b\n\n\t\treturn nil\n\t})\n\n\treturn uo\n}\n\n// SetCollation sets the value for the Collation field. Specifies a collation to\n// use for string comparisons during the operation. The default value is nil,\n// which means the default collation of the collection will be used.\nfunc (uo *UpdateManyOptionsBuilder) SetCollation(c *Collation) *UpdateManyOptionsBuilder {\n\tuo.Opts = append(uo.Opts, func(opts *UpdateManyOptions) error {\n\t\topts.Collation = c\n\n\t\treturn nil\n\t})\n\n\treturn uo\n}\n\n// SetComment sets the value for the Comment field. Specifies a string or document that will be\n// included in server logs, profiling logs, and currentOp queries to help trace the operation.\n// The default value is nil, which means that no comment will be included in the logs.\nfunc (uo *UpdateManyOptionsBuilder) SetComment(comment any) *UpdateManyOptionsBuilder {\n\tuo.Opts = append(uo.Opts, func(opts *UpdateManyOptions) error {\n\t\topts.Comment = comment\n\n\t\treturn nil\n\t})\n\n\treturn uo\n}\n\n// SetHint sets the value for the Hint field. Specifies the index to use for the\n// operation. This should either be the index name as a string or the index\n// specification as a document. This option is only valid for MongoDB versions\n// >= 4.2. Server versions < 4.2 will return an error if this option is\n// specified. The driver will return an error if this option is specified during\n// an unacknowledged write operation. The driver will return an error if the\n// hint parameter is a multi-key map. The default value is nil, which means that\n// no hint will be sent.\nfunc (uo *UpdateManyOptionsBuilder) SetHint(h any) *UpdateManyOptionsBuilder {\n\tuo.Opts = append(uo.Opts, func(opts *UpdateManyOptions) error {\n\t\topts.Hint = h\n\n\t\treturn nil\n\t})\n\n\treturn uo\n}\n\n// SetUpsert sets the value for the Upsert field. If true, a new document will be inserted if the\n// filter does not match any documents in the collection. The default value is false.\nfunc (uo *UpdateManyOptionsBuilder) SetUpsert(b bool) *UpdateManyOptionsBuilder {\n\tuo.Opts = append(uo.Opts, func(opts *UpdateManyOptions) error {\n\t\topts.Upsert = &b\n\n\t\treturn nil\n\t})\n\n\treturn uo\n}\n\n// SetLet sets the value for the Let field. Specifies parameters for the update expression. This\n// option is only valid for MongoDB versions >= 5.0. Older servers will report an error for using\n// this option. This must be a document mapping parameter names to values. Values must be constant\n// or closed expressions that do not reference document fields. Parameters can then be accessed\n// as variables in an aggregate expression context (e.g. \"$$var\").\nfunc (uo *UpdateManyOptionsBuilder) SetLet(l any) *UpdateManyOptionsBuilder {\n\tuo.Opts = append(uo.Opts, func(opts *UpdateManyOptions) error {\n\t\topts.Let = l\n\n\t\treturn nil\n\t})\n\n\treturn uo\n}\n"
  },
  {
    "path": "mongo/read_write_concern_spec_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage mongo\n\nimport (\n\t\"bytes\"\n\t\"errors\"\n\t\"os\"\n\t\"path\"\n\t\"reflect\"\n\t\"testing\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/spectest\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/readconcern\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/writeconcern\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/connstring\"\n)\n\nconst (\n\tconnstringTestsDir = \"connection-string\"\n\tdocumentTestsDir   = \"document\"\n)\n\nvar (\n\tserverDefaultConcern = []byte{5, 0, 0, 0, 0} // server default read concern and write concern is empty document\n\tspecTestRegistry     = func() *bson.Registry {\n\t\treg := bson.NewRegistry()\n\t\treg.RegisterTypeMapEntry(bson.TypeEmbeddedDocument, reflect.TypeOf(bson.Raw{}))\n\t\treturn reg\n\t}()\n\treadWriteConcernTestsDir = spectest.Path(\"read-write-concern/tests\")\n)\n\ntype connectionStringTestFile struct {\n\tTests []connectionStringTest `bson:\"tests\"`\n}\n\ntype connectionStringTest struct {\n\tDescription  string   `bson:\"description\"`\n\tURI          string   `bson:\"uri\"`\n\tValid        bool     `bson:\"valid\"`\n\tReadConcern  bson.Raw `bson:\"readConcern\"`\n\tWriteConcern bson.Raw `bson:\"writeConcern\"`\n}\n\ntype documentTestFile struct {\n\tTests []documentTest `bson:\"tests\"`\n}\n\ntype documentTest struct {\n\tDescription          string    `bson:\"description\"`\n\tValid                bool      `bson:\"valid\"`\n\tReadConcern          bson.Raw  `bson:\"readConcern\"`\n\tReadConcernDocument  *bson.Raw `bson:\"readConcernDocument\"`\n\tWriteConcern         bson.Raw  `bson:\"writeConcern\"`\n\tWriteConcernDocument *bson.Raw `bson:\"writeConcernDocument\"`\n\tIsServerDefault      *bool     `bson:\"isServerDefault\"`\n\tIsAcknowledged       *bool     `bson:\"isAcknowledged\"`\n}\n\nfunc TestReadWriteConcernSpec(t *testing.T) {\n\tt.Run(\"connstring\", func(t *testing.T) {\n\t\tfor _, file := range jsonFilesInDir(t, path.Join(readWriteConcernTestsDir, connstringTestsDir)) {\n\t\t\tt.Run(file, func(t *testing.T) {\n\t\t\t\trunConnectionStringTestFile(t, path.Join(readWriteConcernTestsDir, connstringTestsDir, file))\n\t\t\t})\n\t\t}\n\t})\n\tt.Run(\"document\", func(t *testing.T) {\n\t\tfor _, file := range jsonFilesInDir(t, path.Join(readWriteConcernTestsDir, documentTestsDir)) {\n\t\t\tt.Run(file, func(t *testing.T) {\n\t\t\t\trunDocumentTestFile(t, path.Join(readWriteConcernTestsDir, documentTestsDir, file))\n\t\t\t})\n\t\t}\n\t})\n}\n\nfunc runConnectionStringTestFile(t *testing.T, filePath string) {\n\tcontent, err := os.ReadFile(filePath)\n\tassert.Nil(t, err, \"ReadFile error for %v: %v\", filePath, err)\n\n\tvar testFile connectionStringTestFile\n\tvr, err := bson.NewExtJSONValueReader(bytes.NewReader(content), false)\n\tassert.Nil(t, err, \"NewExtJSONValueReader error: %v\", err)\n\tdec := bson.NewDecoder(vr)\n\tdec.SetRegistry(specTestRegistry)\n\terr = dec.Decode(&testFile)\n\tassert.Nil(t, err, \"decode error: %v\", err)\n\n\tfor _, test := range testFile.Tests {\n\t\tt.Run(test.Description, func(t *testing.T) {\n\t\t\trunConnectionStringTest(t, test)\n\t\t})\n\t}\n}\n\nfunc runConnectionStringTest(t *testing.T, test connectionStringTest) {\n\tspectest.CheckSkip(t)\n\n\tcs, err := connstring.ParseAndValidate(test.URI)\n\tif !test.Valid {\n\t\tassert.NotNil(t, err, \"expected Parse error, got nil\")\n\t\treturn\n\t}\n\tassert.Nil(t, err, \"Parse error: %v\", err)\n\n\tif test.ReadConcern != nil {\n\t\texpected := readConcernFromRaw(t, test.ReadConcern)\n\t\tassert.Equal(t, expected.Level, cs.ReadConcernLevel,\n\t\t\t\"expected level %v, got %v\", expected.Level, cs.ReadConcernLevel)\n\t}\n\tif test.WriteConcern != nil {\n\t\texpectedWc := writeConcernFromRaw(t, test.WriteConcern)\n\t\tif expectedWc.wSet {\n\t\t\texpected := expectedWc.W\n\t\t\tif _, ok := expected.(int); ok {\n\t\t\t\tassert.True(t, cs.WNumberSet, \"expected WNumberSet, got false\")\n\t\t\t\tassert.Equal(t, expected, cs.WNumber, \"expected w value %v, got %v\", expected, cs.WNumber)\n\t\t\t} else {\n\t\t\t\tassert.False(t, cs.WNumberSet, \"expected WNumberSet to be false, got true\")\n\t\t\t\tassert.Equal(t, expected, cs.WString, \"expected w value %v, got %v\", expected, cs.WString)\n\t\t\t}\n\t\t}\n\t\tif expectedWc.jSet {\n\t\t\tassert.True(t, cs.JSet, \"expected JSet, got false\")\n\t\t\tassert.Equal(t, *expectedWc.Journal, cs.J, \"expected j value %v, got %v\", *expectedWc.Journal, cs.J)\n\t\t}\n\t}\n}\n\nfunc runDocumentTestFile(t *testing.T, filePath string) {\n\tcontent, err := os.ReadFile(filePath)\n\tassert.Nil(t, err, \"ReadFile error: %v\", err)\n\n\tvar testFile documentTestFile\n\tvr, err := bson.NewExtJSONValueReader(bytes.NewReader(content), false)\n\tassert.Nil(t, err, \"NewExtJSONValueReader error: %v\", err)\n\tdec := bson.NewDecoder(vr)\n\tdec.SetRegistry(specTestRegistry)\n\terr = dec.Decode(&testFile)\n\tassert.Nil(t, err, \"decode error: %v\", err)\n\n\tfor _, test := range testFile.Tests {\n\t\tt.Run(test.Description, func(t *testing.T) {\n\t\t\trunDocumentTest(t, test)\n\t\t})\n\t}\n}\n\nfunc runDocumentTest(t *testing.T, test documentTest) {\n\tif test.ReadConcern != nil {\n\t\t_, actual, err := driver.MarshalBSONReadConcern(readConcernFromRaw(t, test.ReadConcern))\n\t\tif !test.Valid {\n\t\t\tassert.NotNil(t, err, \"expected an invalid read concern\")\n\t\t} else {\n\t\t\tassert.Nil(t, err, \"got error: %v\", err)\n\t\t\tcompareDocuments(t, *test.ReadConcernDocument, actual)\n\t\t}\n\n\t\tif test.IsServerDefault != nil {\n\t\t\tgotServerDefault := bytes.Equal(actual, serverDefaultConcern)\n\t\t\tassert.Equal(t, *test.IsServerDefault, gotServerDefault, \"expected server default read concern, got %s\", actual)\n\t\t}\n\t}\n\tif test.WriteConcern != nil {\n\t\tactualWc := writeConcernFromRaw(t, test.WriteConcern)\n\t\t_, actual, err := driver.MarshalBSONWriteConcern(actualWc.WriteConcern, 0)\n\t\tif !test.Valid {\n\t\t\tassert.NotNil(t, err, \"expected an invalid write concern\")\n\t\t\treturn\n\t\t}\n\t\tif test.IsAcknowledged != nil {\n\t\t\tactualAck := actualWc.Acknowledged()\n\t\t\tassert.Equal(t, *test.IsAcknowledged, actualAck,\n\t\t\t\t\"expected acknowledged %v, got %v\", *test.IsAcknowledged, actualAck)\n\t\t}\n\n\t\texpected := *test.WriteConcernDocument\n\t\tif errors.Is(err, driver.ErrEmptyWriteConcern) {\n\t\t\telems, _ := expected.Elements()\n\t\t\tif len(elems) == 0 {\n\t\t\t\tassert.NotNil(t, test.IsServerDefault, \"expected write concern %s, got empty\", expected)\n\t\t\t\tassert.True(t, *test.IsServerDefault, \"expected write concern %s, got empty\", expected)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif _, jErr := expected.LookupErr(\"j\"); jErr == nil && len(elems) == 1 {\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\n\t\tassert.Nil(t, err, \"MarshalBSONValue error: %v\", err)\n\t\tif jVal, err := expected.LookupErr(\"j\"); err == nil && !jVal.Boolean() {\n\t\t\tactual = actual[:len(actual)-1]\n\t\t\tactual = bsoncore.AppendBooleanElement(actual, \"j\", false)\n\t\t\tactual, _ = bsoncore.AppendDocumentEnd(actual, 0)\n\t\t}\n\t\tcompareDocuments(t, expected, actual)\n\t}\n}\n\nfunc readConcernFromRaw(t *testing.T, rc bson.Raw) *readconcern.ReadConcern {\n\tt.Helper()\n\n\tconcern := &readconcern.ReadConcern{}\n\telems, _ := rc.Elements()\n\tfor _, elem := range elems {\n\t\tkey := elem.Key()\n\t\tval := elem.Value()\n\n\t\tswitch key {\n\t\tcase \"level\":\n\t\t\tconcern.Level = val.StringValue()\n\t\tdefault:\n\t\t\tt.Fatalf(\"unrecognized read concern field %v\", key)\n\t\t}\n\t}\n\treturn concern\n}\n\ntype writeConcern struct {\n\t*writeconcern.WriteConcern\n\tjSet bool\n\twSet bool\n}\n\nfunc writeConcernFromRaw(t *testing.T, wcRaw bson.Raw) writeConcern {\n\tvar wc writeConcern\n\twc.WriteConcern = &writeconcern.WriteConcern{}\n\n\telems, _ := wcRaw.Elements()\n\tfor _, elem := range elems {\n\t\tkey := elem.Key()\n\t\tval := elem.Value()\n\n\t\tswitch key {\n\t\tcase \"w\":\n\t\t\twc.wSet = true\n\t\t\tswitch val.Type {\n\t\t\tcase bson.TypeInt32:\n\t\t\t\tw := int(val.Int32())\n\t\t\t\twc.W = w\n\t\t\tcase bson.TypeString:\n\t\t\t\twc.W = val.StringValue()\n\t\t\tdefault:\n\t\t\t\tt.Fatalf(\"unexpected type for w: %v\", val.Type)\n\t\t\t}\n\t\tcase \"journal\":\n\t\t\twc.jSet = true\n\t\t\tj := val.Boolean()\n\t\t\twc.Journal = &j\n\t\tcase \"wtimeoutMS\": // Do nothing, this field is deprecated\n\t\t\tt.Skip(\"the wtimeoutMS write concern option is not supported\")\n\t\tdefault:\n\t\t\tt.Fatalf(\"unrecognized write concern field: %v\", key)\n\t\t}\n\t}\n\treturn wc\n}\n\n// generate a slice of all JSON file names in a directory\nfunc jsonFilesInDir(t *testing.T, dir string) []string {\n\tt.Helper()\n\n\tfiles := make([]string, 0)\n\n\tentries, err := os.ReadDir(dir)\n\tassert.Nil(t, err, \"unable to read json file: %v\", err)\n\n\tfor _, entry := range entries {\n\t\tif entry.IsDir() || path.Ext(entry.Name()) != \".json\" {\n\t\t\tcontinue\n\t\t}\n\n\t\tfiles = append(files, entry.Name())\n\t}\n\n\treturn files\n}\n"
  },
  {
    "path": "mongo/readconcern/readconcern.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\n// Package readconcern defines read concerns for MongoDB operations.\n//\n// For more information about MongoDB read concerns, see\n// https://www.mongodb.com/docs/manual/reference/read-concern/\npackage readconcern\n\n// A ReadConcern defines a MongoDB read concern, which allows you to control the consistency and\n// isolation properties of the data read from replica sets and replica set shards.\n//\n// For more information about MongoDB read concerns, see\n// https://www.mongodb.com/docs/manual/reference/read-concern/\ntype ReadConcern struct {\n\tLevel string\n}\n\n// Local returns a ReadConcern that requests data from the instance with no guarantee that the data\n// has been written to a majority of the replica set members (i.e. may be rolled back).\n//\n// For more information about read concern \"local\", see\n// https://www.mongodb.com/docs/manual/reference/read-concern-local/\nfunc Local() *ReadConcern {\n\treturn &ReadConcern{Level: \"local\"}\n}\n\n// Majority returns a ReadConcern that requests data that has been acknowledged by a majority of the\n// replica set members (i.e. the documents read are durable and guaranteed not to roll back).\n//\n// For more information about read concern \"majority\", see\n// https://www.mongodb.com/docs/manual/reference/read-concern-majority/\nfunc Majority() *ReadConcern {\n\treturn &ReadConcern{Level: \"majority\"}\n}\n\n// Linearizable returns a ReadConcern that requests data that reflects all successful\n// majority-acknowledged writes that completed prior to the start of the read operation.\n//\n// For more information about read concern \"linearizable\", see\n// https://www.mongodb.com/docs/manual/reference/read-concern-linearizable/\nfunc Linearizable() *ReadConcern {\n\treturn &ReadConcern{Level: \"linearizable\"}\n}\n\n// Available returns a ReadConcern that requests data from an instance with no guarantee that the\n// data has been written to a majority of the replica set members (i.e. may be rolled back).\n//\n// For more information about read concern \"available\", see\n// https://www.mongodb.com/docs/manual/reference/read-concern-available/\nfunc Available() *ReadConcern {\n\treturn &ReadConcern{Level: \"available\"}\n}\n\n// Snapshot returns a ReadConcern that requests majority-committed data as it appears across shards\n// from a specific single point in time in the recent past.\n//\n// For more information about read concern \"snapshot\", see\n// https://www.mongodb.com/docs/manual/reference/read-concern-snapshot/\nfunc Snapshot() *ReadConcern {\n\treturn &ReadConcern{Level: \"snapshot\"}\n}\n"
  },
  {
    "path": "mongo/readpref/mode.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage readpref\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n)\n\n// Mode indicates the user's preference on reads.\ntype Mode uint8\n\n// Mode constants\nconst (\n\t_ Mode = iota\n\t// PrimaryMode indicates that only a primary is\n\t// considered for reading. This is the default\n\t// mode.\n\tPrimaryMode\n\t// PrimaryPreferredMode indicates that if a primary\n\t// is available, use it; otherwise, eligible\n\t// secondaries will be considered.\n\tPrimaryPreferredMode\n\t// SecondaryMode indicates that only secondaries\n\t// should be considered.\n\tSecondaryMode\n\t// SecondaryPreferredMode indicates that only secondaries\n\t// should be considered when one is available. If none\n\t// are available, then a primary will be considered.\n\tSecondaryPreferredMode\n\t// NearestMode indicates that all primaries and secondaries\n\t// will be considered.\n\tNearestMode\n)\n\n// ModeFromString returns a mode corresponding to\n// mode.\nfunc ModeFromString(mode string) (Mode, error) {\n\tswitch strings.ToLower(mode) {\n\tcase \"primary\":\n\t\treturn PrimaryMode, nil\n\tcase \"primarypreferred\":\n\t\treturn PrimaryPreferredMode, nil\n\tcase \"secondary\":\n\t\treturn SecondaryMode, nil\n\tcase \"secondarypreferred\":\n\t\treturn SecondaryPreferredMode, nil\n\tcase \"nearest\":\n\t\treturn NearestMode, nil\n\t}\n\treturn Mode(0), fmt.Errorf(\"unknown read preference %v\", mode)\n}\n\n// String returns the string representation of mode.\nfunc (mode Mode) String() string {\n\tswitch mode {\n\tcase PrimaryMode:\n\t\treturn \"primary\"\n\tcase PrimaryPreferredMode:\n\t\treturn \"primaryPreferred\"\n\tcase SecondaryMode:\n\t\treturn \"secondary\"\n\tcase SecondaryPreferredMode:\n\t\treturn \"secondaryPreferred\"\n\tcase NearestMode:\n\t\treturn \"nearest\"\n\tdefault:\n\t\treturn \"unknown\"\n\t}\n}\n\n// IsValid checks whether the mode is valid.\nfunc (mode Mode) IsValid() bool {\n\tswitch mode {\n\tcase PrimaryMode,\n\t\tPrimaryPreferredMode,\n\t\tSecondaryMode,\n\t\tSecondaryPreferredMode,\n\t\tNearestMode:\n\t\treturn true\n\tdefault:\n\t\treturn false\n\t}\n}\n"
  },
  {
    "path": "mongo/readpref/mode_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2020-present.\n\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage readpref\n\nimport (\n\t\"testing\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n)\n\nfunc TestMode_String(t *testing.T) {\n\tt.Parallel()\n\n\ttestCases := []struct {\n\t\tname string\n\t\tmode Mode\n\t}{\n\t\t{\"primary\", PrimaryMode},\n\t\t{\"primaryPreferred\", PrimaryPreferredMode},\n\t\t{\"secondary\", SecondaryMode},\n\t\t{\"secondaryPreferred\", SecondaryPreferredMode},\n\t\t{\"nearest\", NearestMode},\n\t\t{\"unknown\", Mode(42)},\n\t}\n\n\tfor _, tc := range testCases {\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\tassert.Equal(t, tc.name, tc.mode.String(), \"expected %q, got %q\", tc.name, tc.mode.String())\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "mongo/readpref/options.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage readpref\n\nimport (\n\t\"errors\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/tag\"\n)\n\n// ErrInvalidTagSet indicates that an invalid set of tags was specified.\nvar ErrInvalidTagSet = errors.New(\"an even number of tags must be specified\")\n\n// Option configures a read preference\ntype Option func(*ReadPref) error\n\n// WithMaxStaleness sets the maximum staleness a\n// server is allowed.\nfunc WithMaxStaleness(ms time.Duration) Option {\n\treturn func(rp *ReadPref) error {\n\t\trp.maxStaleness = ms\n\t\trp.maxStalenessSet = true\n\t\treturn nil\n\t}\n}\n\n// WithTags specifies a single tag set used to match replica set members. If no members match the\n// tag set, read operations will return an error. To avoid errors if no members match the tag set, use\n// [WithTagSets] and include an empty tag set as the last tag set in the list.\n//\n// The last call to [WithTags] or [WithTagSets] overrides all previous calls to either method.\n//\n// For more information about read preference tags, see\n// https://www.mongodb.com/docs/manual/core/read-preference-tags/\nfunc WithTags(tags ...string) Option {\n\treturn func(rp *ReadPref) error {\n\t\tlength := len(tags)\n\t\tif length < 2 || length%2 != 0 {\n\t\t\treturn ErrInvalidTagSet\n\t\t}\n\n\t\ttagset := make(tag.Set, 0, length/2)\n\n\t\tfor i := 1; i < length; i += 2 {\n\t\t\ttagset = append(tagset, tag.Tag{Name: tags[i-1], Value: tags[i]})\n\t\t}\n\n\t\treturn WithTagSets(tagset)(rp)\n\t}\n}\n\n// WithTagSets specifies a list of tag sets used to match replica set members. If the list contains\n// multiple tag sets, members are matched against each tag set in succession until a match is found.\n// Once a match is found, the remaining tag sets are ignored. If no members match any of the tag\n// sets, the read operation returns with an error. To avoid an error if no members match any of the\n// tag sets, include an empty tag set as the last tag set in the list.\n//\n// The last call to [WithTags] or [WithTagSets] overrides all previous calls to either method.\n//\n// For more information about read preference tags, see\n// https://www.mongodb.com/docs/manual/core/read-preference-tags/\nfunc WithTagSets(tagSets ...tag.Set) Option {\n\treturn func(rp *ReadPref) error {\n\t\trp.tagSets = tagSets\n\t\treturn nil\n\t}\n}\n\n// WithHedgeEnabled specifies whether or not hedged reads should be enabled in\n// the server. This feature requires MongoDB server version 4.4 or higher. For\n// more information about hedged reads, see\n// https://www.mongodb.com/docs/manual/core/sharded-cluster-query-router/#mongos-hedged-reads.\n// If not specified, the default is to not send a value to the server, which\n// will result in the server defaults being used.\n//\n// Deprecated: Hedged reads are deprecated in MongoDB 8.0 and may be removed in\n// a future MongoDB version.\nfunc WithHedgeEnabled(hedgeEnabled bool) Option {\n\treturn func(rp *ReadPref) error {\n\t\trp.hedgeEnabled = &hedgeEnabled\n\t\treturn nil\n\t}\n}\n"
  },
  {
    "path": "mongo/readpref/options_example_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2023-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage readpref_test\n\nimport (\n\t\"go.mongodb.org/mongo-driver/v2/mongo\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/readpref\"\n\t\"go.mongodb.org/mongo-driver/v2/tag\"\n)\n\n// Configure a Client with a read preference that selects the nearest replica\n// set member that includes tags \"region: South\" and \"datacenter: A\".\nfunc ExampleWithTags() {\n\trp := readpref.Nearest(\n\t\treadpref.WithTags(\n\t\t\t\"region\", \"South\",\n\t\t\t\"datacenter\", \"A\"))\n\n\topts := options.Client().\n\t\tApplyURI(\"mongodb://localhost:27017\").\n\t\tSetReadPreference(rp)\n\n\t_, err := mongo.Connect(opts)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n}\n\n// Configure a Client with a read preference that selects the nearest replica\n// set member matching a set of tags. Try to match members in 3 stages:\n//\n//  1. Match replica set members that include tags \"region: South\" and\n//     \"datacenter: A\".\n//  2. Match replica set members that includes tag \"region: South\".\n//  3. Match any replica set member.\n//\n// Stage 3 is used to avoid errors when no members match the previous 2 stages.\nfunc ExampleWithTagSets() {\n\ttagSetList := tag.NewTagSetsFromMaps([]map[string]string{\n\t\t{\"region\": \"South\", \"datacenter\": \"A\"},\n\t\t{\"region\": \"South\"},\n\t\t{},\n\t})\n\n\trp := readpref.Nearest(readpref.WithTagSets(tagSetList...))\n\n\topts := options.Client().\n\t\tApplyURI(\"mongodb://localhost\").\n\t\tSetReadPreference(rp)\n\n\t_, err := mongo.Connect(opts)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n}\n"
  },
  {
    "path": "mongo/readpref/readpref.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\n// Package readpref defines read preferences for MongoDB queries.\npackage readpref\n\nimport (\n\t\"bytes\"\n\t\"errors\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/tag\"\n)\n\nvar errInvalidReadPreference = errors.New(\"can not specify tags, max staleness, or hedge with mode primary\")\n\n// Primary constructs a read preference with a PrimaryMode.\nfunc Primary() *ReadPref {\n\treturn &ReadPref{mode: PrimaryMode}\n}\n\n// PrimaryPreferred constructs a read preference with a PrimaryPreferredMode.\nfunc PrimaryPreferred(opts ...Option) *ReadPref {\n\t// New only returns an error with a mode of Primary\n\trp, _ := New(PrimaryPreferredMode, opts...)\n\treturn rp\n}\n\n// SecondaryPreferred constructs a read preference with a SecondaryPreferredMode.\nfunc SecondaryPreferred(opts ...Option) *ReadPref {\n\t// New only returns an error with a mode of Primary\n\trp, _ := New(SecondaryPreferredMode, opts...)\n\treturn rp\n}\n\n// Secondary constructs a read preference with a SecondaryMode.\nfunc Secondary(opts ...Option) *ReadPref {\n\t// New only returns an error with a mode of Primary\n\trp, _ := New(SecondaryMode, opts...)\n\treturn rp\n}\n\n// Nearest constructs a read preference with a NearestMode.\nfunc Nearest(opts ...Option) *ReadPref {\n\t// New only returns an error with a mode of Primary\n\trp, _ := New(NearestMode, opts...)\n\treturn rp\n}\n\n// New creates a new ReadPref.\nfunc New(mode Mode, opts ...Option) (*ReadPref, error) {\n\trp := &ReadPref{\n\t\tmode: mode,\n\t}\n\n\tif mode == PrimaryMode && len(opts) != 0 {\n\t\treturn nil, errInvalidReadPreference\n\t}\n\n\tfor _, opt := range opts {\n\t\tif opt == nil {\n\t\t\tcontinue\n\t\t}\n\t\terr := opt(rp)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\treturn rp, nil\n}\n\n// ReadPref determines which servers are considered suitable for read operations.\ntype ReadPref struct {\n\tmaxStaleness    time.Duration\n\tmaxStalenessSet bool\n\tmode            Mode\n\ttagSets         []tag.Set\n\thedgeEnabled    *bool\n}\n\n// MaxStaleness is the maximum amount of time to allow\n// a server to be considered eligible for selection. The\n// second return value indicates if this value has been set.\nfunc (r *ReadPref) MaxStaleness() (time.Duration, bool) {\n\treturn r.maxStaleness, r.maxStalenessSet\n}\n\n// Mode indicates the mode of the read preference.\nfunc (r *ReadPref) Mode() Mode {\n\treturn r.mode\n}\n\n// TagSets are multiple tag sets indicating\n// which servers should be considered.\nfunc (r *ReadPref) TagSets() []tag.Set {\n\treturn r.tagSets\n}\n\n// HedgeEnabled returns whether or not hedged reads are enabled for this read\n// preference. If this option was not specified during read preference\n// construction, nil is returned.\n//\n// Deprecated: Hedged reads are deprecated in MongoDB 8.0 and may be removed in\n// a future MongoDB version.\nfunc (r *ReadPref) HedgeEnabled() *bool {\n\treturn r.hedgeEnabled\n}\n\n// String returns a human-readable description of the read preference.\nfunc (r *ReadPref) String() string {\n\tvar b bytes.Buffer\n\tb.WriteString(r.mode.String())\n\tdelim := \"(\"\n\tif r.maxStalenessSet {\n\t\tfmt.Fprintf(&b, \"%smaxStaleness=%v\", delim, r.maxStaleness)\n\t\tdelim = \" \"\n\t}\n\tfor _, tagSet := range r.tagSets {\n\t\tfmt.Fprintf(&b, \"%stagSet=%s\", delim, tagSet.String())\n\t\tdelim = \" \"\n\t}\n\tif r.hedgeEnabled != nil {\n\t\tfmt.Fprintf(&b, \"%shedgeEnabled=%v\", delim, *r.hedgeEnabled)\n\t\tdelim = \" \"\n\t}\n\tif delim != \"(\" {\n\t\tb.WriteString(\")\")\n\t}\n\treturn b.String()\n}\n"
  },
  {
    "path": "mongo/readpref/readpref_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage readpref\n\nimport (\n\t\"testing\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/require\"\n\t\"go.mongodb.org/mongo-driver/v2/tag\"\n)\n\nfunc TestPrimary(t *testing.T) {\n\tsubject := Primary()\n\n\trequire.Equal(t, PrimaryMode, subject.Mode())\n\t_, set := subject.MaxStaleness()\n\trequire.False(t, set)\n\trequire.Len(t, subject.TagSets(), 0)\n}\n\nfunc TestPrimaryPreferred(t *testing.T) {\n\tsubject := PrimaryPreferred()\n\n\trequire.Equal(t, PrimaryPreferredMode, subject.Mode())\n\t_, set := subject.MaxStaleness()\n\trequire.False(t, set)\n\trequire.Len(t, subject.TagSets(), 0)\n}\n\nfunc TestPrimaryPreferred_with_options(t *testing.T) {\n\tsubject := PrimaryPreferred(\n\t\tWithMaxStaleness(time.Duration(10)),\n\t\tWithTags(\"a\", \"1\", \"b\", \"2\"),\n\t)\n\n\trequire.Equal(t, PrimaryPreferredMode, subject.Mode())\n\tms, set := subject.MaxStaleness()\n\trequire.True(t, set)\n\trequire.Equal(t, time.Duration(10), ms)\n\trequire.Equal(t, []tag.Set{{tag.Tag{Name: \"a\", Value: \"1\"}, tag.Tag{Name: \"b\", Value: \"2\"}}}, subject.TagSets())\n}\n\nfunc TestSecondaryPreferred(t *testing.T) {\n\tsubject := SecondaryPreferred()\n\n\trequire.Equal(t, SecondaryPreferredMode, subject.Mode())\n\t_, set := subject.MaxStaleness()\n\trequire.False(t, set)\n\trequire.Len(t, subject.TagSets(), 0)\n}\n\nfunc TestSecondaryPreferred_with_options(t *testing.T) {\n\tsubject := SecondaryPreferred(\n\t\tWithMaxStaleness(time.Duration(10)),\n\t\tWithTags(\"a\", \"1\", \"b\", \"2\"),\n\t)\n\n\trequire.Equal(t, SecondaryPreferredMode, subject.Mode())\n\tms, set := subject.MaxStaleness()\n\trequire.True(t, set)\n\trequire.Equal(t, time.Duration(10), ms)\n\trequire.Equal(t, []tag.Set{{tag.Tag{Name: \"a\", Value: \"1\"}, tag.Tag{Name: \"b\", Value: \"2\"}}}, subject.TagSets())\n}\n\nfunc TestSecondary(t *testing.T) {\n\tsubject := Secondary()\n\n\trequire.Equal(t, SecondaryMode, subject.Mode())\n\t_, set := subject.MaxStaleness()\n\trequire.False(t, set)\n\trequire.Len(t, subject.TagSets(), 0)\n}\n\nfunc TestSecondary_with_options(t *testing.T) {\n\tsubject := Secondary(\n\t\tWithMaxStaleness(time.Duration(10)),\n\t\tWithTags(\"a\", \"1\", \"b\", \"2\"),\n\t)\n\n\trequire.Equal(t, SecondaryMode, subject.Mode())\n\tms, set := subject.MaxStaleness()\n\trequire.True(t, set)\n\trequire.Equal(t, time.Duration(10), ms)\n\trequire.Equal(t, []tag.Set{{tag.Tag{Name: \"a\", Value: \"1\"}, tag.Tag{Name: \"b\", Value: \"2\"}}}, subject.TagSets())\n}\n\nfunc TestNearest(t *testing.T) {\n\tsubject := Nearest()\n\n\trequire.Equal(t, NearestMode, subject.Mode())\n\t_, set := subject.MaxStaleness()\n\trequire.False(t, set)\n\trequire.Len(t, subject.TagSets(), 0)\n}\n\nfunc TestNearest_with_options(t *testing.T) {\n\tsubject := Nearest(\n\t\tWithMaxStaleness(time.Duration(10)),\n\t\tWithTags(\"a\", \"1\", \"b\", \"2\"),\n\t)\n\n\trequire.Equal(t, NearestMode, subject.Mode())\n\tms, set := subject.MaxStaleness()\n\trequire.True(t, set)\n\trequire.Equal(t, time.Duration(10), ms)\n\trequire.Equal(t, []tag.Set{{tag.Tag{Name: \"a\", Value: \"1\"}, tag.Tag{Name: \"b\", Value: \"2\"}}}, subject.TagSets())\n}\n\nfunc TestHedge(t *testing.T) {\n\tt.Run(\"hedge specified with primary mode errors\", func(t *testing.T) {\n\t\t_, err := New(PrimaryMode, WithHedgeEnabled(true))\n\t\tassert.Equal(t, errInvalidReadPreference, err, \"expected error %v, got %v\", errInvalidReadPreference, err)\n\t})\n\tt.Run(\"valid hedge document and mode succeeds\", func(t *testing.T) {\n\t\trp, err := New(SecondaryMode, WithHedgeEnabled(true))\n\t\tassert.Nil(t, err, \"expected no error, got %v\", err)\n\t\tenabled := rp.HedgeEnabled()\n\t\tassert.NotNil(t, enabled, \"expected HedgeEnabled to return a non-nil value, got nil\")\n\t\tassert.True(t, *enabled, \"expected HedgeEnabled to return true, got false\")\n\t})\n}\n\nfunc TestReadPref_String(t *testing.T) {\n\tt.Run(\"ReadPref.String() with all options\", func(t *testing.T) {\n\t\treadPref := Nearest(\n\t\t\tWithMaxStaleness(120*time.Second),\n\t\t\tWithTagSets(tag.Set{{\"a\", \"1\"}, {\"b\", \"2\"}}, tag.Set{{\"q\", \"5\"}, {\"r\", \"6\"}}),\n\t\t\tWithHedgeEnabled(true),\n\t\t)\n\t\texpected := \"nearest(maxStaleness=2m0s tagSet=a=1,b=2 tagSet=q=5,r=6 hedgeEnabled=true)\"\n\t\tassert.Equal(t, expected, readPref.String(), \"expected %q, got %q\", expected, readPref.String())\n\t})\n\tt.Run(\"ReadPref.String() with one option\", func(t *testing.T) {\n\t\treadPref := Secondary(WithTags(\"a\", \"1\", \"b\", \"2\"))\n\t\texpected := \"secondary(tagSet=a=1,b=2)\"\n\t\tassert.Equal(t, expected, readPref.String(), \"expected %q, got %q\", expected, readPref.String())\n\t})\n\tt.Run(\"ReadPref.String() with no options\", func(t *testing.T) {\n\t\treadPref := Primary()\n\t\texpected := \"primary\"\n\t\tassert.Equal(t, expected, readPref.String(), \"expected %q, got %q\", expected, readPref.String())\n\t})\n}\n"
  },
  {
    "path": "mongo/results.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage mongo\n\nimport (\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/operation\"\n)\n\n// ClientBulkWriteResult is the result type returned by a client-level BulkWrite operation.\ntype ClientBulkWriteResult struct {\n\t// The number of documents inserted.\n\tInsertedCount int64\n\n\t// The number of documents matched by filters in update and replace operations.\n\tMatchedCount int64\n\n\t// The number of documents modified by update and replace operations.\n\tModifiedCount int64\n\n\t// The number of documents deleted.\n\tDeletedCount int64\n\n\t// The number of documents upserted by update and replace operations.\n\tUpsertedCount int64\n\n\t// A map of operation index to the _id of each inserted document.\n\tInsertResults map[int]ClientBulkWriteInsertResult\n\n\t// A map of operation index to the _id of each updated document.\n\tUpdateResults map[int]ClientBulkWriteUpdateResult\n\n\t// A map of operation index to the _id of each deleted document.\n\tDeleteResults map[int]ClientBulkWriteDeleteResult\n\n\t// Operation performed with an acknowledged write. Values for other fields may\n\t// not be deterministic if the write operation was unacknowledged.\n\tAcknowledged bool\n\n\t// HasVerboseResults indicates whether this result contains verbose results.\n\tHasVerboseResults bool\n}\n\n// ClientBulkWriteInsertResult is the result type returned by a client-level bulk write of InsertOne operation.\ntype ClientBulkWriteInsertResult struct {\n\t// The _id of the inserted document. A value generated by the driver will be of type primitive.ObjectID.\n\tInsertedID any\n}\n\n// ClientBulkWriteUpdateResult is the result type returned from a client-level bulk write of UpdateOne, UpdateMany, and ReplaceOne operation.\ntype ClientBulkWriteUpdateResult struct {\n\tMatchedCount  int64 // The number of documents matched by the filter.\n\tModifiedCount int64 // The number of documents modified by the operation.\n\tUpsertedID    any   // The _id field of the upserted document, or nil if no upsert was done.\n}\n\n// ClientBulkWriteDeleteResult is the result type returned by a client-level bulk write DeleteOne and DeleteMany operation.\ntype ClientBulkWriteDeleteResult struct {\n\tDeletedCount int64 // The number of documents deleted.\n}\n\n// BulkWriteResult is the result type returned by a BulkWrite operation.\ntype BulkWriteResult struct {\n\t// The number of documents inserted.\n\tInsertedCount int64\n\n\t// The number of documents matched by filters in update and replace operations.\n\tMatchedCount int64\n\n\t// The number of documents modified by update and replace operations.\n\tModifiedCount int64\n\n\t// The number of documents deleted.\n\tDeletedCount int64\n\n\t// The number of documents upserted by update and replace operations.\n\tUpsertedCount int64\n\n\t// A map of operation index to the _id of each upserted document.\n\tUpsertedIDs map[int64]any\n\n\t// Operation performed with an acknowledged write. Values for other fields may\n\t// not be deterministic if the write operation was unacknowledged.\n\tAcknowledged bool\n}\n\n// InsertOneResult is the result type returned by an InsertOne operation.\ntype InsertOneResult struct {\n\t// The _id of the inserted document. A value generated by the driver will be of type bson.ObjectID.\n\tInsertedID any\n\n\t// Operation performed with an acknowledged write. Values for other fields may\n\t// not be deterministic if the write operation was unacknowledged.\n\tAcknowledged bool\n}\n\n// InsertManyResult is a result type returned by an InsertMany operation.\ntype InsertManyResult struct {\n\t// The _id values of the inserted documents. Values generated by the driver will be of type bson.ObjectID.\n\tInsertedIDs []any\n\n\t// Operation performed with an acknowledged write. Values for other fields may\n\t// not be deterministic if the write operation was unacknowledged.\n\tAcknowledged bool\n}\n\n// TODO(GODRIVER-2367): Remove the BSON struct tags on DeleteResult.\n\n// DeleteResult is the result type returned by DeleteOne and DeleteMany operations.\ntype DeleteResult struct {\n\tDeletedCount int64 // The number of documents deleted.\n\n\t// Operation performed with an acknowledged write. Values for other fields may\n\t// not be deterministic if the write operation was unacknowledged.\n\tAcknowledged bool\n}\n\n// RewrapManyDataKeyResult is the result of the bulk write operation used to update the key vault collection with\n// rewrapped data keys.\ntype RewrapManyDataKeyResult struct {\n\t*BulkWriteResult\n}\n\n// ListDatabasesResult is a result of a ListDatabases operation.\ntype ListDatabasesResult struct {\n\t// A slice containing one DatabaseSpecification for each database matched by the operation's filter.\n\tDatabases []DatabaseSpecification\n\n\t// The total size of the database files of the returned databases in bytes.\n\t// This will be the sum of the SizeOnDisk field for each specification in Databases.\n\tTotalSize int64\n}\n\nfunc newListDatabasesResultFromOperation(res operation.ListDatabasesResult) ListDatabasesResult {\n\tvar ldr ListDatabasesResult\n\tldr.Databases = make([]DatabaseSpecification, 0, len(res.Databases))\n\tfor _, spec := range res.Databases {\n\t\tldr.Databases = append(\n\t\t\tldr.Databases,\n\t\t\tDatabaseSpecification{Name: spec.Name, SizeOnDisk: spec.SizeOnDisk, Empty: spec.Empty},\n\t\t)\n\t}\n\tldr.TotalSize = res.TotalSize\n\treturn ldr\n}\n\n// DatabaseSpecification contains information for a database. This type is returned as part of ListDatabasesResult.\ntype DatabaseSpecification struct {\n\tName       string // The name of the database.\n\tSizeOnDisk int64  // The total size of the database files on disk in bytes.\n\tEmpty      bool   // Specifies whether or not the database is empty.\n}\n\n// UpdateResult is the result type returned from UpdateOne, UpdateMany, and ReplaceOne operations.\ntype UpdateResult struct {\n\tMatchedCount  int64 // The number of documents matched by the filter.\n\tModifiedCount int64 // The number of documents modified by the operation.\n\tUpsertedCount int64 // The number of documents upserted by the operation.\n\tUpsertedID    any   // The _id field of the upserted document, or nil if no upsert was done.\n\n\t// Operation performed with an acknowledged write. Values for other fields may\n\t// not be deterministic if the write operation was unacknowledged.\n\tAcknowledged bool\n}\n\n// IndexSpecification represents an index in a database. This type is returned by the IndexView.ListSpecifications\n// function and is also used in the CollectionSpecification type.\ntype IndexSpecification struct {\n\t// The index name.\n\tName string\n\n\t// The namespace for the index. This is a string in the format \"databaseName.collectionName\".\n\tNamespace string\n\n\t// The keys specification document for the index.\n\tKeysDocument bson.Raw\n\n\t// The index version.\n\tVersion int32\n\n\t// The length of time, in seconds, for documents to remain in the collection. The default value is 0, which means\n\t// that documents will remain in the collection until they're explicitly deleted or the collection is dropped.\n\tExpireAfterSeconds *int32\n\n\t// If true, the index will only reference documents that contain the fields specified in the index. The default is\n\t// false.\n\tSparse *bool\n\n\t// If true, the collection will not accept insertion or update of documents where the index key value matches an\n\t// existing value in the index. The default is false.\n\tUnique *bool\n\n\t// The clustered index.\n\tClustered *bool\n}\n\ntype indexListSpecificationResponse struct {\n\tName               string   `bson:\"name\"`\n\tNamespace          string   `bson:\"ns\"`\n\tKeysDocument       bson.Raw `bson:\"key\"`\n\tVersion            int32    `bson:\"v\"`\n\tExpireAfterSeconds *int32   `bson:\"expireAfterSeconds\"`\n\tSparse             *bool    `bson:\"sparse\"`\n\tUnique             *bool    `bson:\"unique\"`\n\tClustered          *bool    `bson:\"clustered\"`\n}\n\n// CollectionSpecification represents a collection in a database. This type is returned by the\n// Database.ListCollectionSpecifications function.\ntype CollectionSpecification struct {\n\t// The collection name.\n\tName string\n\n\t// The type of the collection. This will either be \"collection\" or \"view\".\n\tType string\n\n\t// Whether or not the collection is readOnly.\n\tReadOnly bool\n\n\t// The collection UUID as a bson.Binary with subtype 4.\n\tUUID *bson.Binary\n\n\t// A document containing the options used to construct the collection.\n\tOptions bson.Raw\n\n\t// An IndexSpecification instance with details about the collection's _id index.\n\tIDIndex IndexSpecification\n}\n\n// DistinctResult represents an array of BSON data returned from an operation.\n// If the operation resulted in an error, all DistinctResult methods will return\n// that error. If the operation did not return any data, all DistinctResult\n// methods will return ErrNoDocuments.\ntype DistinctResult struct {\n\terr      error\n\tarr      bson.RawArray\n\treg      *bson.Registry\n\tbsonOpts *options.BSONOptions\n}\n\n// Decode will unmarshal the array represented by this DistinctResult into v. If\n// there was an error from the operation that created this DistinctReuslt, that\n// error will be returned. If the operation returned no array, Decode will\n// return ErrNoDocuments.\n//\n// If the operation was successful and returned an array, Decode will return any\n// errors from the unmarshalling process without any modification. If v is nil\n// or is a typed nil, an error will be returned.\nfunc (dr *DistinctResult) Decode(v any) error {\n\tdoc := bsoncore.NewDocumentBuilder().\n\t\tAppendValue(\"arr\", bsoncore.Value{\n\t\t\tType: bsoncore.TypeArray,\n\t\t\tData: dr.arr,\n\t\t}).Build()\n\n\tdec := getDecoder(doc, dr.bsonOpts, dr.reg)\n\n\treturn dec.Decode(&struct{ Arr any }{Arr: v})\n}\n\n// Err provides a way to check for query errors without calling Decode. Err\n// returns the error, if any, that was encountered while running the operation.\n// If the operation was successful but did not return any documents, Err returns\n// ErrNoDocuments. If this error is not nil, this error will also be returned\n// from Decode.\nfunc (dr *DistinctResult) Err() error {\n\treturn dr.err\n}\n\n// Raw returns the document represented by this DistinctResult as a bson.Raw. If\n// there was an error from the operation that created this DistinctResult, both\n// the result and that error will be returned. If the operation returned no\n// documents, this will return (nil, ErrNoDocuments).\nfunc (dr *DistinctResult) Raw() (bson.RawArray, error) {\n\tif dr.err != nil {\n\t\treturn nil, dr.err\n\t}\n\n\treturn dr.arr, nil\n}\n"
  },
  {
    "path": "mongo/search_index_view.go",
    "content": "// Copyright (C) MongoDB, Inc. 2023-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage mongo\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"strconv\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/mongoutil\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/operation\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/session\"\n)\n\n// SearchIndexView is a type that can be used to create, drop, list and update\n// search indexes on a collection. A SearchIndexView for a collection can be\n// created by a call to Collection.SearchIndexes().\n//\n// Search index commands are asynchronous and return from the server before\n// the index is successfully updated, created or dropped. In order to determine\n// when an index has been created / updated, users are expected to run the\n// listSearchIndexes repeatedly until index changes appear.\ntype SearchIndexView struct {\n\tcoll *Collection\n}\n\n// SearchIndexModel represents a new search index to be created.\ntype SearchIndexModel struct {\n\t// A document describing the definition for the search index. It cannot be nil.\n\t// See https://www.mongodb.com/docs/atlas/atlas-search/create-index/ for reference.\n\tDefinition any\n\n\t// The search index options.\n\tOptions *options.SearchIndexesOptionsBuilder\n}\n\n// List executes a listSearchIndexes command and returns a cursor over the search indexes in the collection.\n//\n// The name parameter specifies the index name. A nil pointer matches all indexes.\n//\n// The opts parameter can be used to specify options for this operation (see the options.ListSearchIndexesOptions\n// documentation).\nfunc (siv SearchIndexView) List(\n\tctx context.Context,\n\tsearchIdxOpts options.Lister[options.SearchIndexesOptions],\n\topts ...options.Lister[options.ListSearchIndexesOptions],\n) (*Cursor, error) {\n\tif ctx == nil {\n\t\tctx = context.Background()\n\t}\n\n\tsearchIdxArgs, err := mongoutil.NewOptions[options.SearchIndexesOptions](searchIdxOpts)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to construct options from builder: %w\", err)\n\t}\n\n\tindex := bson.D{}\n\tif searchIdxArgs != nil && searchIdxArgs.Name != nil {\n\t\tindex = bson.D{{\"name\", *searchIdxArgs.Name}}\n\t}\n\n\targs, err := mongoutil.NewOptions[options.ListSearchIndexesOptions](opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\taggregateOpts := mongoutil.NewOptionsLister(args.AggregateOptions, nil)\n\n\treturn siv.coll.Aggregate(ctx, Pipeline{{{\"$listSearchIndexes\", index}}}, aggregateOpts)\n}\n\n// CreateOne executes a createSearchIndexes command to create a search index on the collection and returns the name of the new\n// search index. See the SearchIndexView.CreateMany documentation for more information and an example.\n//\n// This is an asynchronous operation.\nfunc (siv SearchIndexView) CreateOne(\n\tctx context.Context,\n\tmodel SearchIndexModel,\n\topts ...options.Lister[options.CreateSearchIndexesOptions],\n) (string, error) {\n\tnames, err := siv.CreateMany(ctx, []SearchIndexModel{model}, opts...)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\treturn names[0], nil\n}\n\n// CreateMany executes a createSearchIndexes command to create multiple search indexes on the collection and returns\n// the names of the new search indexes.\n//\n// For each SearchIndexModel in the models parameter, the index name can be specified.\n//\n// The opts parameter can be used to specify options for this operation (see the options.CreateSearchIndexesOptions\n// documentation).\n//\n// This is an asynchronous operation.\nfunc (siv SearchIndexView) CreateMany(\n\tctx context.Context,\n\tmodels []SearchIndexModel,\n\t_ ...options.Lister[options.CreateSearchIndexesOptions],\n) ([]string, error) {\n\tvar indexes bsoncore.Document\n\taidx, indexes := bsoncore.AppendArrayStart(indexes)\n\n\tfor i, model := range models {\n\t\tif model.Definition == nil {\n\t\t\treturn nil, fmt.Errorf(\"search index model definition cannot be nil\")\n\t\t}\n\n\t\tdefinition, err := marshal(model.Definition, siv.coll.bsonOpts, siv.coll.registry)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tvar iidx int32\n\t\tiidx, indexes = bsoncore.AppendDocumentElementStart(indexes, strconv.Itoa(i))\n\t\tif model.Options != nil {\n\t\t\tsearchIndexArgs, err := mongoutil.NewOptions[options.SearchIndexesOptions](model.Options)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"failed to construct options from builder: %w\", err)\n\t\t\t}\n\n\t\t\tif searchIndexArgs.Name != nil {\n\t\t\t\tindexes = bsoncore.AppendStringElement(indexes, \"name\", *searchIndexArgs.Name)\n\t\t\t}\n\n\t\t\tif searchIndexArgs.Type != nil {\n\t\t\t\tindexes = bsoncore.AppendStringElement(indexes, \"type\", *searchIndexArgs.Type)\n\t\t\t}\n\t\t}\n\t\tindexes = bsoncore.AppendDocumentElement(indexes, \"definition\", definition)\n\n\t\tindexes, err = bsoncore.AppendDocumentEnd(indexes, iidx)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\tindexes, err := bsoncore.AppendArrayEnd(indexes, aidx)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tsess := sessionFromContext(ctx)\n\n\tif sess == nil && siv.coll.client.sessionPool != nil {\n\t\tsess = session.NewImplicitClientSession(siv.coll.client.sessionPool, siv.coll.client.id)\n\t\tdefer sess.EndSession()\n\t}\n\n\terr = siv.coll.client.validSession(sess)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tselector := makePinnedSelector(sess, siv.coll.writeSelector)\n\n\top := operation.NewCreateSearchIndexes(indexes).\n\t\tSession(sess).CommandMonitor(siv.coll.client.monitor).\n\t\tServerSelector(selector).ClusterClock(siv.coll.client.clock).\n\t\tCollection(siv.coll.name).Database(siv.coll.db.name).\n\t\tDeployment(siv.coll.client.deployment).ServerAPI(siv.coll.client.serverAPI).\n\t\tTimeout(siv.coll.client.timeout).Authenticator(siv.coll.client.authenticator)\n\n\terr = op.Execute(ctx)\n\tif err != nil {\n\t\t_, err = processWriteError(err)\n\t\treturn nil, err\n\t}\n\n\tindexesCreated := op.Result().IndexesCreated\n\tnames := make([]string, 0, len(indexesCreated))\n\tfor _, index := range indexesCreated {\n\t\tnames = append(names, index.Name)\n\t}\n\n\treturn names, nil\n}\n\n// DropOne executes a dropSearchIndexes operation to drop a search index on the collection.\n//\n// The name parameter should be the name of the search index to drop. If the name is \"*\", ErrMultipleIndexDrop will be returned\n// without running the command because doing so would drop all search indexes.\n//\n// The opts parameter can be used to specify options for this operation (see the options.DropSearchIndexOptions\n// documentation).\n//\n// This is an asynchronous operation.\nfunc (siv SearchIndexView) DropOne(\n\tctx context.Context,\n\tname string,\n\t_ ...options.Lister[options.DropSearchIndexOptions],\n) error {\n\tif name == \"*\" {\n\t\treturn ErrMultipleIndexDrop\n\t}\n\n\tif ctx == nil {\n\t\tctx = context.Background()\n\t}\n\n\tsess := sessionFromContext(ctx)\n\tif sess == nil && siv.coll.client.sessionPool != nil {\n\t\tsess = session.NewImplicitClientSession(siv.coll.client.sessionPool, siv.coll.client.id)\n\t\tdefer sess.EndSession()\n\t}\n\n\terr := siv.coll.client.validSession(sess)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tselector := makePinnedSelector(sess, siv.coll.writeSelector)\n\n\top := operation.NewDropSearchIndex(name).\n\t\tSession(sess).CommandMonitor(siv.coll.client.monitor).\n\t\tServerSelector(selector).ClusterClock(siv.coll.client.clock).\n\t\tCollection(siv.coll.name).Database(siv.coll.db.name).\n\t\tDeployment(siv.coll.client.deployment).ServerAPI(siv.coll.client.serverAPI).\n\t\tTimeout(siv.coll.client.timeout).Authenticator(siv.coll.client.authenticator)\n\n\terr = op.Execute(ctx)\n\tvar de driver.Error\n\tif errors.As(err, &de) && de.NamespaceNotFound() {\n\t\treturn nil\n\t}\n\treturn err\n}\n\n// UpdateOne executes a updateSearchIndex operation to update a search index on the collection.\n//\n// The name parameter should be the name of the search index to update.\n//\n// The definition parameter is a document describing the definition for the search index. It cannot be nil.\n//\n// The opts parameter can be used to specify options for this operation (see the options.UpdateSearchIndexOptions\n// documentation).\n//\n// This is an asynchronous operation.\nfunc (siv SearchIndexView) UpdateOne(\n\tctx context.Context,\n\tname string,\n\tdefinition any,\n\t_ ...options.Lister[options.UpdateSearchIndexOptions],\n) error {\n\tif definition == nil {\n\t\treturn fmt.Errorf(\"search index definition cannot be nil\")\n\t}\n\n\tindexDefinition, err := marshal(definition, siv.coll.bsonOpts, siv.coll.registry)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif ctx == nil {\n\t\tctx = context.Background()\n\t}\n\n\tsess := sessionFromContext(ctx)\n\tif sess == nil && siv.coll.client.sessionPool != nil {\n\t\tsess = session.NewImplicitClientSession(siv.coll.client.sessionPool, siv.coll.client.id)\n\t\tdefer sess.EndSession()\n\t}\n\n\terr = siv.coll.client.validSession(sess)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tselector := makePinnedSelector(sess, siv.coll.writeSelector)\n\n\top := operation.NewUpdateSearchIndex(name, indexDefinition).\n\t\tSession(sess).CommandMonitor(siv.coll.client.monitor).\n\t\tServerSelector(selector).ClusterClock(siv.coll.client.clock).\n\t\tCollection(siv.coll.name).Database(siv.coll.db.name).\n\t\tDeployment(siv.coll.client.deployment).ServerAPI(siv.coll.client.serverAPI).\n\t\tTimeout(siv.coll.client.timeout).Authenticator(siv.coll.client.authenticator)\n\n\treturn op.Execute(ctx)\n}\n"
  },
  {
    "path": "mongo/session.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage mongo\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/mongoutil\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/randutil\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/serverselector\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/operation\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/session\"\n)\n\n// ErrWrongClient is returned when a user attempts to pass in a session created by a different client than\n// the method call is using.\nvar ErrWrongClient = errors.New(\"session was not created by this client\")\n\nvar (\n\twithTransactionTimeout = 120 * time.Second\n\tbackoffInitial         = 5 * time.Millisecond\n\tbackoffMax             = 500 * time.Millisecond\n)\n\nvar jitter interface {\n\tInt63n(int64) int64\n} = randutil.NewLockedRand()\n\n// Session is a MongoDB logical session. Sessions can be used to enable causal\n// consistency for a group of operations or to execute operations in an ACID\n// transaction. A new Session can be created from a Client instance. A Session\n// created from a Client must only be used to execute operations using that\n// Client or a Database or Collection created from that Client. For more\n// information about sessions, and their use cases, see\n// https://www.mongodb.com/docs/manual/reference/server-sessions/,\n// https://www.mongodb.com/docs/manual/core/read-isolation-consistency-recency/#causal-consistency, and\n// https://www.mongodb.com/docs/manual/core/transactions/.\n//\n// Implementations of Session are not safe for concurrent use by multiple\n// goroutines.\ntype Session struct {\n\tclientSession       *session.Client\n\tclient              *Client\n\tdeployment          driver.Deployment\n\tdidCommitAfterStart bool // true if commit was called after start with no other operations\n}\n\ntype sessionKey struct{}\n\n// NewSessionContext returns a Context that holds the given Session. If the\n// Context already contains a Session, that Session will be replaced with the\n// one provided.\n//\n// The returned Context can be used with Collection methods like\n// [Collection.InsertOne] or [Collection.Find] to run operations in a Session.\nfunc NewSessionContext(parent context.Context, sess *Session) context.Context {\n\treturn context.WithValue(parent, sessionKey{}, sess)\n}\n\n// SessionFromContext extracts the mongo.Session object stored in a Context. This can be used on a SessionContext that\n// was created implicitly through one of the callback-based session APIs or explicitly by calling NewSessionContext. If\n// there is no Session stored in the provided Context, nil is returned.\nfunc SessionFromContext(ctx context.Context) *Session {\n\tval := ctx.Value(sessionKey{})\n\tif val == nil {\n\t\treturn nil\n\t}\n\n\tsess, ok := val.(*Session)\n\tif !ok {\n\t\treturn nil\n\t}\n\n\treturn sess\n}\n\n// ID returns the current ID document associated with the session. The ID\n// document is in the form {\"id\": <BSON binary value>}.\nfunc (s *Session) ID() bson.Raw {\n\treturn bson.Raw(s.clientSession.SessionID)\n}\n\n// EndSession aborts any existing transactions and close the session.\nfunc (s *Session) EndSession(ctx context.Context) {\n\tif s.clientSession.TransactionInProgress() {\n\t\t// ignore all errors aborting during an end session\n\t\t_ = s.AbortTransaction(ctx)\n\t}\n\ts.clientSession.EndSession()\n}\n\n// WithTransaction starts a transaction on this session and runs the fn\n// callback. Errors with the TransientTransactionError and\n// UnknownTransactionCommitResult labels are retried for up to 120 seconds.\n// Inside the callback, the SessionContext must be used as the Context parameter\n// for any operations that should be part of the transaction. If the ctx\n// parameter already has a Session attached to it, it will be replaced by this\n// session. The fn callback may be run multiple times during WithTransaction due\n// to retry attempts, so it must be idempotent.\n//\n// If a command inside the callback fn fails, it may cause the transaction on\n// the server to be aborted. This situation is normally handled transparently by\n// the driver. However, if the application does not return that error from the\n// fn, the driver will not be able to determine whether the transaction was\n// aborted or not. The driver will then retry the block indefinitely.\n//\n// To avoid this situation, the application MUST NOT silently handle errors\n// within the callback fn. If the application needs to handle errors within the\n// block, it MUST return them after doing so.\n//\n// Non-retryable operation errors or any operation errors that occur after the\n// timeout expires will be returned without retrying. If the callback fails, the\n// driver will call AbortTransaction. Because this method must succeed to ensure\n// that server-side resources are properly cleaned up, context deadlines and\n// cancellations will not be respected during this call. For a usage example,\n// see the Client.StartSession method documentation.\nfunc (s *Session) WithTransaction(\n\tctx context.Context,\n\tfn func(ctx context.Context) (any, error),\n\topts ...options.Lister[options.TransactionOptions],\n) (any, error) {\n\ttransTimeout := withTransactionTimeout\n\tif s.client.timeout != nil {\n\t\ttransTimeout = *s.client.timeout\n\t}\n\tstartTime := time.Now()\n\ttimeout := time.NewTimer(transTimeout)\n\tdefer timeout.Stop()\n\tvar expDur time.Duration\n\tvar err error\n\tfor {\n\t\tif expDur == 0 {\n\t\t\texpDur = backoffInitial\n\t\t} else {\n\t\t\tif expDur > backoffMax {\n\t\t\t\texpDur = backoffMax\n\t\t\t}\n\t\t\tbackoff := expDur * time.Duration(jitter.Int63n(512)) / 512\n\t\t\tif time.Since(startTime)+backoff > transTimeout {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tsleep := time.NewTimer(backoff)\n\t\t\tselect {\n\t\t\tcase <-timeout.C:\n\t\t\t\tsleep.Stop()\n\t\t\t\treturn nil, err\n\t\t\tcase <-sleep.C:\n\t\t\t}\n\t\t\tif expDur < backoffMax {\n\t\t\t\texpDur += expDur / 2\n\t\t\t}\n\t\t}\n\n\t\terr = s.StartTransaction(opts...)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tvar res any\n\t\tres, err = fn(NewSessionContext(ctx, s))\n\t\tif err != nil {\n\t\t\tif s.clientSession.TransactionRunning() {\n\t\t\t\t// Wrap the user-provided Context in a new one that behaves like context.Background() for deadlines and\n\t\t\t\t// cancellations, but forwards Value requests to the original one.\n\t\t\t\t_ = s.AbortTransaction(newBackgroundContext(ctx))\n\t\t\t}\n\n\t\t\tselect {\n\t\t\tcase <-timeout.C:\n\t\t\t\treturn nil, err\n\t\t\tdefault:\n\t\t\t}\n\n\t\t\tif errorHasLabel(err, driver.TransientTransactionError) {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\treturn res, err\n\t\t}\n\n\t\t// Check if callback intentionally aborted and, if so, return immediately\n\t\t// with no error.\n\t\terr = s.clientSession.CheckAbortTransaction()\n\t\tif err != nil {\n\t\t\treturn res, nil\n\t\t}\n\n\t\t// If context has errored, run AbortTransaction and return, as the CommitLoop\n\t\t// has no chance of succeeding.\n\t\t//\n\t\t// Aborting after a failed CommitTransaction is dangerous. Failed transaction\n\t\t// commits may unpin the session server-side, and subsequent transaction aborts\n\t\t// may run on a new mongos which could end up with commit and abort being executed\n\t\t// simultaneously.\n\t\tif ctx.Err() != nil {\n\t\t\t// Wrap the user-provided Context in a new one that behaves like context.Background() for deadlines and\n\t\t\t// cancellations, but forwards Value requests to the original one.\n\t\t\t_ = s.AbortTransaction(newBackgroundContext(ctx))\n\t\t\treturn nil, ctx.Err()\n\t\t}\n\n\tCommitLoop:\n\t\tfor {\n\t\t\terr = s.CommitTransaction(newBackgroundContext(ctx))\n\t\t\t// End when error is nil, as transaction has been committed.\n\t\t\tif err == nil {\n\t\t\t\treturn res, nil\n\t\t\t}\n\n\t\t\tselect {\n\t\t\tcase <-timeout.C:\n\t\t\t\treturn res, err\n\t\t\tdefault:\n\t\t\t}\n\n\t\t\tvar cerr CommandError\n\t\t\tif errors.As(err, &cerr) {\n\t\t\t\tif cerr.HasErrorLabel(driver.UnknownTransactionCommitResult) && !cerr.IsMaxTimeMSExpiredError() {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif cerr.HasErrorLabel(driver.TransientTransactionError) {\n\t\t\t\t\tbreak CommitLoop\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn res, err\n\t\t}\n\t}\n}\n\n// StartTransaction starts a new transaction. This method returns an error if\n// there is already a transaction in-progress for this session.\nfunc (s *Session) StartTransaction(opts ...options.Lister[options.TransactionOptions]) error {\n\terr := s.clientSession.CheckStartTransaction()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\ts.didCommitAfterStart = false\n\n\targs, err := mongoutil.NewOptions[options.TransactionOptions](opts...)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to construct options from builder: %w\", err)\n\t}\n\n\tcoreOpts := &session.TransactionOptions{\n\t\tReadConcern:    args.ReadConcern,\n\t\tReadPreference: args.ReadPreference,\n\t\tWriteConcern:   args.WriteConcern,\n\t}\n\n\treturn s.clientSession.StartTransaction(coreOpts)\n}\n\n// AbortTransaction aborts the active transaction for this session. This method\n// returns an error if there is no active transaction for this session or if the\n// transaction has been committed or aborted.\nfunc (s *Session) AbortTransaction(ctx context.Context) error {\n\terr := s.clientSession.CheckAbortTransaction()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// Do not run the abort command if the transaction is in starting state\n\tif s.clientSession.TransactionStarting() || s.didCommitAfterStart {\n\t\treturn s.clientSession.AbortTransaction()\n\t}\n\n\tselector := makePinnedSelector(s.clientSession, &serverselector.Write{})\n\n\ts.clientSession.Aborting = true\n\t_ = operation.NewAbortTransaction().Session(s.clientSession).ClusterClock(s.client.clock).Database(\"admin\").\n\t\tDeployment(s.deployment).WriteConcern(s.clientSession.CurrentWc).ServerSelector(selector).\n\t\tRetry(driver.RetryOncePerCommand).CommandMonitor(s.client.monitor).\n\t\tRecoveryToken(bsoncore.Document(s.clientSession.RecoveryToken)).ServerAPI(s.client.serverAPI).\n\t\tAuthenticator(s.client.authenticator).Logger(s.client.logger).Execute(ctx)\n\n\ts.clientSession.Aborting = false\n\t_ = s.clientSession.AbortTransaction()\n\n\treturn nil\n}\n\n// CommitTransaction commits the active transaction for this session. This\n// method returns an error if there is no active transaction for this session or\n// if the transaction has been aborted.\nfunc (s *Session) CommitTransaction(ctx context.Context) error {\n\terr := s.clientSession.CheckCommitTransaction()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// Do not run the commit command if the transaction is in started state\n\tif s.clientSession.TransactionStarting() || s.didCommitAfterStart {\n\t\ts.didCommitAfterStart = true\n\t\treturn s.clientSession.CommitTransaction()\n\t}\n\n\tif s.clientSession.TransactionCommitted() {\n\t\ts.clientSession.RetryingCommit = true\n\t}\n\n\tselector := makePinnedSelector(s.clientSession, &serverselector.Write{})\n\n\ts.clientSession.Committing = true\n\top := operation.NewCommitTransaction().\n\t\tSession(s.clientSession).ClusterClock(s.client.clock).Database(\"admin\").Deployment(s.deployment).\n\t\tWriteConcern(s.clientSession.CurrentWc).ServerSelector(selector).Retry(driver.RetryOncePerCommand).\n\t\tCommandMonitor(s.client.monitor).RecoveryToken(bsoncore.Document(s.clientSession.RecoveryToken)).\n\t\tServerAPI(s.client.serverAPI).Authenticator(s.client.authenticator).Logger(s.client.logger)\n\n\terr = op.Execute(ctx)\n\t// Return error without updating transaction state if it is a timeout, as the transaction has not\n\t// actually been committed.\n\tif IsTimeout(err) {\n\t\treturn wrapErrors(err)\n\t}\n\ts.clientSession.Committing = false\n\tcommitErr := s.clientSession.CommitTransaction()\n\n\t// We set the write concern to majority for subsequent calls to CommitTransaction.\n\ts.clientSession.UpdateCommitTransactionWriteConcern()\n\n\tif err != nil {\n\t\treturn wrapErrors(err)\n\t}\n\treturn commitErr\n}\n\n// ClusterTime returns the current cluster time document associated with the\n// session.\nfunc (s *Session) ClusterTime() bson.Raw {\n\treturn s.clientSession.ClusterTime\n}\n\n// SnapshotTime returns the current snapshot time associated with the session.\nfunc (s *Session) SnapshotTime() bson.Timestamp {\n\treturn s.clientSession.SnapshotTime\n}\n\n// AdvanceClusterTime advances the cluster time for a session. This method\n// returns an error if the session has ended.\nfunc (s *Session) AdvanceClusterTime(d bson.Raw) error {\n\treturn s.clientSession.AdvanceClusterTime(d)\n}\n\n// OperationTime returns the current operation time document associated with the\n// session.\nfunc (s *Session) OperationTime() *bson.Timestamp {\n\treturn s.clientSession.OperationTime\n}\n\n// AdvanceOperationTime advances the operation time for a session. This method\n// returns an error if the session has ended.\nfunc (s *Session) AdvanceOperationTime(ts *bson.Timestamp) error {\n\treturn s.clientSession.AdvanceOperationTime(ts)\n}\n\n// Client is the Client associated with the session.\nfunc (s *Session) Client() *Client {\n\treturn s.client\n}\n\n// TransactionRunning returns true if the session has started a transaction and\n// it hasn't been committed or aborted.\nfunc (s *Session) TransactionRunning() bool {\n\treturn s.clientSession != nil && s.clientSession.TransactionRunning()\n}\n\n// sessionFromContext checks for a sessionImpl in the argued context and returns the session if it\n// exists\nfunc sessionFromContext(ctx context.Context) *session.Client {\n\tif ses := SessionFromContext(ctx); ses != nil {\n\t\treturn ses.clientSession\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "mongo/single_result.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage mongo\n\nimport (\n\t\"context\"\n\t\"errors\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n)\n\n// ErrNoDocuments is returned by SingleResult methods when the operation that created the SingleResult did not return\n// any documents.\nvar ErrNoDocuments = errors.New(\"mongo: no documents in result\")\n\n// SingleResult represents a single document returned from an operation. If the operation resulted in an error, all\n// SingleResult methods will return that error. If the operation did not return any documents, all SingleResult methods\n// will return ErrNoDocuments.\ntype SingleResult struct {\n\tctx      context.Context\n\terr      error\n\tcur      *Cursor\n\trdr      bson.Raw\n\tbsonOpts *options.BSONOptions\n\treg      *bson.Registry\n\n\t// Operation performed with an acknowledged write. Values returned by\n\t// SingleResult methods may not be deterministic if the write operation was\n\t// unacknowledged and so should not be relied upon.\n\tAcknowledged bool\n}\n\n// NewSingleResultFromDocument creates a SingleResult with the provided error, registry, and an underlying Cursor pre-loaded with\n// the provided document, error and registry. If no registry is provided, bson.NewRegistry() will be used. If an error distinct\n// from the one provided occurs during creation of the SingleResult, that error will be stored on the returned SingleResult.\n//\n// The document parameter must be a non-nil document.\nfunc NewSingleResultFromDocument(\n\tdocument any,\n\terr error,\n\tregistry *bson.Registry,\n) *SingleResult {\n\tif document == nil {\n\t\treturn &SingleResult{err: ErrNilDocument}\n\t}\n\tif registry == nil {\n\t\tregistry = defaultRegistry\n\t}\n\n\tcur, createErr := NewCursorFromDocuments([]any{document}, err, registry)\n\tif createErr != nil {\n\t\treturn &SingleResult{err: createErr}\n\t}\n\n\treturn &SingleResult{\n\t\tcur: cur,\n\t\terr: err,\n\t\treg: registry,\n\t}\n}\n\n// Decode will unmarshal the document represented by this SingleResult into v. If there was an error from the operation\n// that created this SingleResult, that error will be returned. If the operation returned no documents, Decode will\n// return ErrNoDocuments.\n//\n// If the operation was successful and returned a document, Decode will return any errors from the unmarshalling process\n// without any modification. If v is nil or is a typed nil, an error will be returned.\nfunc (sr *SingleResult) Decode(v any) error {\n\tif sr.err != nil {\n\t\treturn sr.err\n\t}\n\tif sr.reg == nil {\n\t\treturn bson.ErrNilRegistry\n\t}\n\n\tif sr.err = sr.setRdrContents(); sr.err != nil {\n\t\treturn sr.err\n\t}\n\n\tdec := getDecoder(sr.rdr, sr.bsonOpts, sr.reg)\n\n\treturn dec.Decode(v)\n}\n\n// Raw returns the document represented by this SingleResult as a bson.Raw. If\n// there was an error from the operation that created this SingleResult, both\n// the result and that error will be returned. If the operation returned no\n// documents, this will return (nil, ErrNoDocuments).\nfunc (sr *SingleResult) Raw() (bson.Raw, error) {\n\tif sr.err != nil {\n\t\treturn sr.rdr, sr.err\n\t}\n\n\tif sr.err = sr.setRdrContents(); sr.err != nil {\n\t\treturn nil, sr.err\n\t}\n\n\treturn sr.rdr, nil\n}\n\n// setRdrContents will set the contents of rdr by iterating the underlying cursor if necessary.\nfunc (sr *SingleResult) setRdrContents() error {\n\tswitch {\n\tcase sr.err != nil:\n\t\treturn sr.err\n\tcase sr.rdr != nil:\n\t\treturn nil\n\tcase sr.cur != nil:\n\t\tdefer sr.cur.Close(sr.ctx)\n\n\t\tif !sr.cur.Next(sr.ctx) {\n\t\t\tif err := sr.cur.Err(); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\n\t\t\treturn ErrNoDocuments\n\t\t}\n\n\t\tsr.rdr = sr.cur.Current\n\n\t\treturn nil\n\t}\n\n\treturn ErrNoDocuments\n}\n\n// Err provides a way to check for query errors without calling Decode. Err returns the error, if\n// any, that was encountered while running the operation. If the operation was successful but did\n// not return any documents, Err returns ErrNoDocuments. If this error is not nil, this error will\n// also be returned from Decode.\nfunc (sr *SingleResult) Err() error {\n\tsr.err = sr.setRdrContents()\n\n\treturn sr.err\n}\n"
  },
  {
    "path": "mongo/single_result_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage mongo\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"testing\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n)\n\nfunc TestNewSingleResultFromDocument(t *testing.T) {\n\t// Mock a document returned by FindOne in SingleResult.\n\tt.Run(\"mock FindOne\", func(t *testing.T) {\n\t\tfindOneResult := bson.D{{\"_id\", 2}, {\"foo\", \"bar\"}}\n\t\tres := NewSingleResultFromDocument(findOneResult, nil, nil)\n\n\t\t// Assert that first, decoded document is as expected.\n\t\tfindOneResultBytes, err := bson.Marshal(findOneResult)\n\t\tassert.Nil(t, err, \"Marshal error: %v\", err)\n\t\texpectedRawBytes := bson.Raw(findOneResultBytes)\n\t\trawBytes, err := res.Raw()\n\t\tassert.Nil(t, err, \"Raw error: %v\", err)\n\t\tassert.Equal(t, expectedRawBytes, rawBytes,\n\t\t\t\"expected decoded SingleResult to be %v, got %v\", expectedRawBytes, rawBytes)\n\n\t\t// Assert that RDR contents are set correctly after Decode.\n\t\tassert.NotNil(t, res.rdr, \"expected non-nil rdr contents\")\n\t\tassert.Equal(t, expectedRawBytes, res.rdr,\n\t\t\t\"expected RDR contents to be %v, got %v\", expectedRawBytes, res.rdr)\n\n\t\t// Assert that a call to cur.Next will return false, as there was only one document in\n\t\t// the slice passed to NewSingleResultFromDocument.\n\t\tnext := res.cur.Next(context.Background())\n\t\tassert.False(t, next, \"expected call to Next to return false, got true\")\n\n\t\t// Check for error on SingleResult.\n\t\tassert.Nil(t, res.Err(), \"SingleResult error: %v\", res.Err())\n\n\t\t// Assert that a call to cur.Close will not fail.\n\t\terr = res.cur.Close(context.Background())\n\t\tassert.Nil(t, err, \"Close error: %v\", err)\n\t})\n\n\t// Mock an error in SingleResult.\n\tt.Run(\"mock FindOne with error\", func(t *testing.T) {\n\t\tmockErr := fmt.Errorf(\"mock error\")\n\t\tres := NewSingleResultFromDocument(bson.D{}, mockErr, nil)\n\n\t\t// Assert that the raw bytes returns the mocked error.\n\t\t_, err := res.Raw()\n\t\tassert.NotNil(t, err, \"expected Raw error, got nil\")\n\t\tassert.Equal(t, mockErr, err, \"expected error %v, got %v\", mockErr, err)\n\n\t\t// Check for error on SingleResult.\n\t\tassert.NotNil(t, res.Err(), \"expected SingleResult error, got nil\")\n\t\tassert.Equal(t, mockErr, res.Err(), \"expected SingleResult error %v, got %v\",\n\t\t\tmockErr, res.Err())\n\n\t\t// Assert that error is propagated to underlying cursor.\n\t\tassert.NotNil(t, res.cur.err, \"expected underlying cursor, got nil\")\n\t\tassert.Equal(t, mockErr, res.cur.err, \"expected underlying cursor %v, got %v\",\n\t\t\tmockErr, res.cur.err)\n\t})\n\n\t// Mock an error in SingleResult.\n\tt.Run(\"mock FindOne with error\", func(t *testing.T) {\n\t\tmockErr := fmt.Errorf(\"mock error\")\n\t\tres := NewSingleResultFromDocument(bson.D{}, mockErr, nil)\n\n\t\t// Assert that the raw bytes returns the mocked error.\n\t\t_, err := res.Raw()\n\t\tassert.NotNil(t, err, \"expected Raw error, got nil\")\n\t\tassert.Equal(t, mockErr, err, \"expected error %v, got %v\", mockErr, err)\n\n\t\t// Check for error on SingleResult.\n\t\tassert.NotNil(t, res.Err(), \"expected SingleResult error, got nil\")\n\t\tassert.Equal(t, mockErr, res.Err(), \"expected SingleResult error %v, got %v\",\n\t\t\tmockErr, res.Err())\n\n\t\t// Assert that error is propagated to underlying cursor.\n\t\tassert.NotNil(t, res.cur.err, \"expected underlying cursor, got nil\")\n\t\tassert.Equal(t, mockErr, res.cur.err, \"expected underlying cursor %v, got %v\",\n\t\t\tmockErr, res.cur.err)\n\t})\n}\n\nfunc TestSingleResult_Decode(t *testing.T) {\n\tt.Run(\"decode twice\", func(t *testing.T) {\n\t\tt.Run(\"bson.Raw\", func(t *testing.T) {\n\t\t\t// Test that Decode and Raw can be called more than once\n\t\t\tc, err := newCursor(newTestBatchCursor(1, 1), nil, defaultRegistry)\n\t\t\tassert.Nil(t, err, \"newCursor error: %v\", err)\n\n\t\t\tsr := &SingleResult{cur: c, reg: defaultRegistry}\n\t\t\tvar firstDecode, secondDecode bson.Raw\n\t\t\terr = sr.Decode(&firstDecode)\n\t\t\tassert.Nil(t, err, \"Decode error: %v\", err)\n\t\t\terr = sr.Decode(&secondDecode)\n\t\t\tassert.Nil(t, err, \"Decode error: %v\", err)\n\n\t\t\trawBytes, err := sr.Raw()\n\t\t\tassert.Nil(t, err, \"Raw error: %v\", err)\n\n\t\t\tassert.Equal(t, firstDecode, secondDecode, \"expected contents %v, got %v\", firstDecode, secondDecode)\n\t\t\tassert.Equal(t, firstDecode, rawBytes, \"expected contents %v, got %v\", firstDecode, rawBytes)\n\t\t})\n\t})\n\n\tt.Run(\"decode with error\", func(t *testing.T) {\n\t\tt.Run(\"bson.Raw\", func(t *testing.T) {\n\t\t\tr := []byte(\"foo\")\n\t\t\tsr := &SingleResult{\n\t\t\t\trdr: r,\n\t\t\t\terr: errors.New(\"Raw error\"),\n\t\t\t}\n\t\t\tres, err := sr.Raw()\n\t\t\tresBytes := []byte(res)\n\t\t\tassert.Equal(t, r, resBytes, \"expected contents %v, got %v\", r, resBytes)\n\t\t\tassert.Equal(t, sr.err, err, \"expected error %v, got %v\", sr.err, err)\n\t\t})\n\t})\n}\n\nfunc TestSingleResult_Err(t *testing.T) {\n\tt.Run(\"bson.Raw\", func(t *testing.T) {\n\t\tsr := &SingleResult{}\n\t\tassert.Equal(t, ErrNoDocuments, sr.Err(), \"expected error %v, got %v\", ErrNoDocuments, sr.Err())\n\t})\n}\n"
  },
  {
    "path": "mongo/with_transactions_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage mongo\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"math\"\n\t\"os\"\n\t\"strconv\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/event\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/failpoint\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/integtest\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/require\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/readpref\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/writeconcern\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/description\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/topology\"\n)\n\nvar (\n\tconnsCheckedOut  int\n\terrorInterrupted int32 = 11601\n)\n\nfunc TestConvenientTransactions(t *testing.T) {\n\tif testing.Short() {\n\t\tt.Skip(\"skipping integration test in short mode\")\n\t}\n\tif os.Getenv(\"DOCKER_RUNNING\") != \"\" {\n\t\tt.Skip(\"skipping test in docker environment\")\n\t}\n\n\tclient := setupConvenientTransactions(t)\n\tdb := client.Database(\"TestConvenientTransactions\")\n\tdbAdmin := client.Database(\"admin\")\n\n\tdefer func() {\n\t\tsessions := client.NumberSessionsInProgress()\n\t\tconns := connsCheckedOut\n\n\t\terr := dbAdmin.RunCommand(bgCtx, bson.D{\n\t\t\t{\"killAllSessions\", bson.A{}},\n\t\t}).Err()\n\t\tif err != nil {\n\t\t\tvar ce CommandError\n\t\t\tif !errors.As(err, &ce) || ce.Code != errorInterrupted {\n\t\t\t\tt.Fatalf(\"killAllSessions error: %v\", err)\n\t\t\t}\n\t\t}\n\n\t\t_ = db.Drop(bgCtx)\n\t\t_ = client.Disconnect(bgCtx)\n\n\t\tassert.Equal(t, 0, sessions, \"%v sessions checked out\", sessions)\n\t\tassert.Equal(t, 0, conns, \"%v connections checked out\", conns)\n\t}()\n\n\tt.Run(\"callback raises custom error\", func(t *testing.T) {\n\t\tcoll := db.Collection(t.Name())\n\t\t_, err := coll.InsertOne(bgCtx, bson.D{{\"x\", 1}})\n\t\tassert.Nil(t, err, \"InsertOne error: %v\", err)\n\n\t\tsess, err := client.StartSession()\n\t\tassert.Nil(t, err, \"StartSession error: %v\", err)\n\t\tdefer sess.EndSession(context.Background())\n\n\t\ttestErr := errors.New(\"test error\")\n\t\t_, err = sess.WithTransaction(context.Background(), func(context.Context) (any, error) {\n\t\t\treturn nil, testErr\n\t\t})\n\t\tassert.Equal(t, testErr, err, \"expected error %v, got %v\", testErr, err)\n\t})\n\tt.Run(\"callback returns value\", func(t *testing.T) {\n\t\tcoll := db.Collection(t.Name())\n\t\t_, err := coll.InsertOne(bgCtx, bson.D{{\"x\", 1}})\n\t\tassert.Nil(t, err, \"InsertOne error: %v\", err)\n\n\t\tsess, err := client.StartSession()\n\t\tassert.Nil(t, err, \"StartSession error: %v\", err)\n\t\tdefer sess.EndSession(context.Background())\n\n\t\tres, err := sess.WithTransaction(context.Background(), func(context.Context) (any, error) {\n\t\t\treturn false, nil\n\t\t})\n\t\tassert.Nil(t, err, \"WithTransaction error: %v\", err)\n\t\tresBool, ok := res.(bool)\n\t\tassert.True(t, ok, \"expected result type %T, got %T\", false, res)\n\t\tassert.False(t, resBool, \"expected result false, got %v\", resBool)\n\t})\n\tt.Run(\"retry timeout enforced\", func(t *testing.T) {\n\t\ttimeout := withTransactionTimeout\n\t\tdefer func() { withTransactionTimeout = timeout }()\n\t\twithTransactionTimeout = time.Second\n\n\t\tcoll := db.Collection(t.Name())\n\t\t_, err := coll.InsertOne(bgCtx, bson.D{{\"x\", 1}})\n\t\tassert.Nil(t, err, \"InsertOne error: %v\", err)\n\n\t\tt.Run(\"transient transaction error\", func(t *testing.T) {\n\t\t\tsess, err := client.StartSession()\n\t\t\tassert.Nil(t, err, \"StartSession error: %v\", err)\n\t\t\tdefer sess.EndSession(context.Background())\n\n\t\t\t_, err = sess.WithTransaction(context.Background(), func(context.Context) (any, error) {\n\t\t\t\treturn nil, CommandError{Name: \"test Error\", Labels: []string{driver.TransientTransactionError}}\n\t\t\t})\n\t\t\tassert.NotNil(t, err, \"expected WithTransaction error, got nil\")\n\t\t\tvar cmdErr CommandError\n\t\t\tassert.True(t, errors.As(err, &cmdErr), \"expected error type %T, got %T\", cmdErr, err)\n\t\t\tassert.True(t, cmdErr.HasErrorLabel(driver.TransientTransactionError),\n\t\t\t\t\"expected error with label %v, got %v\", driver.TransientTransactionError, cmdErr)\n\t\t})\n\t\tt.Run(\"unknown transaction commit result\", func(t *testing.T) {\n\t\t\t// set failpoint\n\t\t\tfailpoint := bson.D{\n\t\t\t\t{\"configureFailPoint\", \"failCommand\"},\n\t\t\t\t{\"mode\", \"alwaysOn\"},\n\t\t\t\t{\"data\", bson.D{\n\t\t\t\t\t{\"failCommands\", bson.A{\"commitTransaction\"}},\n\t\t\t\t\t{\"closeConnection\", true},\n\t\t\t\t}},\n\t\t\t}\n\t\t\terr = dbAdmin.RunCommand(bgCtx, failpoint).Err()\n\t\t\tassert.Nil(t, err, \"error setting failpoint: %v\", err)\n\t\t\tdefer func() {\n\t\t\t\terr = dbAdmin.RunCommand(bgCtx, bson.D{\n\t\t\t\t\t{\"configureFailPoint\", \"failCommand\"},\n\t\t\t\t\t{\"mode\", \"off\"},\n\t\t\t\t}).Err()\n\t\t\t\tassert.Nil(t, err, \"error turning off failpoint: %v\", err)\n\t\t\t}()\n\n\t\t\tsess, err := client.StartSession()\n\t\t\tassert.Nil(t, err, \"StartSession error: %v\", err)\n\t\t\tdefer sess.EndSession(context.Background())\n\n\t\t\t_, err = sess.WithTransaction(context.Background(), func(ctx context.Context) (any, error) {\n\t\t\t\t_, err := coll.InsertOne(ctx, bson.D{{\"x\", 1}})\n\t\t\t\treturn nil, err\n\t\t\t})\n\t\t\tassert.NotNil(t, err, \"expected WithTransaction error, got nil\")\n\t\t\tvar cmdErr CommandError\n\t\t\tassert.True(t, errors.As(err, &cmdErr), \"expected error type %T, got %T\", cmdErr, err)\n\t\t\tassert.True(t, cmdErr.HasErrorLabel(driver.UnknownTransactionCommitResult),\n\t\t\t\t\"expected error with label %v, got %v\", driver.UnknownTransactionCommitResult, cmdErr)\n\t\t})\n\t\tt.Run(\"commit transient transaction error\", func(t *testing.T) {\n\t\t\t// set failpoint\n\t\t\tfailpoint := bson.D{\n\t\t\t\t{\"configureFailPoint\", \"failCommand\"},\n\t\t\t\t{\"mode\", \"alwaysOn\"},\n\t\t\t\t{\"data\", bson.D{\n\t\t\t\t\t{\"failCommands\", bson.A{\"commitTransaction\"}},\n\t\t\t\t\t{\"errorCode\", 251},\n\t\t\t\t}},\n\t\t\t}\n\t\t\terr = dbAdmin.RunCommand(bgCtx, failpoint).Err()\n\t\t\tassert.Nil(t, err, \"error setting failpoint: %v\", err)\n\t\t\tdefer func() {\n\t\t\t\terr = dbAdmin.RunCommand(bgCtx, bson.D{\n\t\t\t\t\t{\"configureFailPoint\", \"failCommand\"},\n\t\t\t\t\t{\"mode\", \"off\"},\n\t\t\t\t}).Err()\n\t\t\t\tassert.Nil(t, err, \"error turning off failpoint: %v\", err)\n\t\t\t}()\n\n\t\t\tsess, err := client.StartSession()\n\t\t\tassert.Nil(t, err, \"StartSession error: %v\", err)\n\t\t\tdefer sess.EndSession(context.Background())\n\n\t\t\t_, err = sess.WithTransaction(context.Background(), func(ctx context.Context) (any, error) {\n\t\t\t\t_, err := coll.InsertOne(ctx, bson.D{{\"x\", 1}})\n\t\t\t\treturn nil, err\n\t\t\t})\n\t\t\tassert.NotNil(t, err, \"expected WithTransaction error, got nil\")\n\t\t\tvar cmdErr CommandError\n\t\t\tassert.True(t, errors.As(err, &cmdErr), \"expected error type %T, got %T\", cmdErr, err)\n\t\t\tassert.True(t, cmdErr.HasErrorLabel(driver.TransientTransactionError),\n\t\t\t\t\"expected error with label %v, got %v\", driver.TransientTransactionError, cmdErr)\n\t\t})\n\t})\n\tt.Run(\"retry backoff is enforced\", func(t *testing.T) {\n\t\tversion, err := getServerVersion(dbAdmin)\n\t\trequire.NoError(t, err, \"getServerVersion error: %v\", err)\n\t\tif compareVersions(version, \"4.4\") < 0 {\n\t\t\tt.Skip(\"skipping versions < 4.4\")\n\t\t}\n\n\t\tfp := failpoint.FailPoint{\n\t\t\tConfigureFailPoint: \"failCommand\",\n\t\t\tMode: failpoint.Mode{\n\t\t\t\tTimes: 13,\n\t\t\t},\n\t\t\tData: failpoint.Data{\n\t\t\t\tFailCommands: []string{\"commitTransaction\"},\n\t\t\t\tErrorCode:    251,\n\t\t\t},\n\t\t}\n\n\t\ttransWithJitter := func(t *testing.T, testJitter *testJitter) time.Duration {\n\t\t\tj := jitter\n\t\t\tdefer func() { jitter = j }()\n\n\t\t\tjitter = testJitter\n\n\t\t\terr := dbAdmin.RunCommand(bgCtx, fp).Err()\n\t\t\trequire.NoError(t, err, \"error setting failpoint: %v\", err)\n\n\t\t\tsession, err := client.StartSession()\n\t\t\tdefer session.EndSession(context.Background())\n\t\t\trequire.NoError(t, err, \"StartSession error: %v\", err)\n\n\t\t\tcoll := db.Collection(t.Name())\n\t\t\tstartTime := time.Now()\n\t\t\t_, err = session.WithTransaction(context.Background(), func(sesctx context.Context) (any, error) {\n\t\t\t\tif _, err := coll.InsertOne(sesctx, bson.D{}); err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t\treturn nil, nil\n\t\t\t})\n\t\t\trequire.NoError(t, err, \"WithTransaction error: %v\", err)\n\t\t\treturn time.Since(startTime)\n\t\t}\n\t\tnoBackoffTime := transWithJitter(t, &testJitter{ratio: 0})\n\t\twithBackoffTime := transWithJitter(t, &testJitter{ratio: 1})\n\t\tassert.InDelta(\n\t\t\tt,\n\t\t\twithBackoffTime, noBackoffTime+1_800*time.Millisecond, float64(500*time.Millisecond),\n\t\t\t\"with backoff time: %v, no backoff time: %v\", withBackoffTime, noBackoffTime,\n\t\t)\n\t})\n\tt.Run(\"abortTransaction does not time out\", func(t *testing.T) {\n\t\t// Create a special CommandMonitor that only records information about abortTransaction events and also\n\t\t// records the Context used in the CommandStartedEvent listener.\n\t\tvar abortStarted []*event.CommandStartedEvent\n\t\tvar abortSucceeded []*event.CommandSucceededEvent\n\t\tvar abortFailed []*event.CommandFailedEvent\n\t\tvar abortCtx context.Context\n\t\tmonitor := &event.CommandMonitor{\n\t\t\tStarted: func(ctx context.Context, evt *event.CommandStartedEvent) {\n\t\t\t\tif evt.CommandName == \"abortTransaction\" {\n\t\t\t\t\tabortStarted = append(abortStarted, evt)\n\t\t\t\t\tif abortCtx == nil {\n\t\t\t\t\t\tabortCtx = ctx\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t},\n\t\t\tSucceeded: func(_ context.Context, evt *event.CommandSucceededEvent) {\n\t\t\t\tif evt.CommandName == \"abortTransaction\" {\n\t\t\t\t\tabortSucceeded = append(abortSucceeded, evt)\n\t\t\t\t}\n\t\t\t},\n\t\t\tFailed: func(_ context.Context, evt *event.CommandFailedEvent) {\n\t\t\t\tif evt.CommandName == \"abortTransaction\" {\n\t\t\t\t\tabortFailed = append(abortFailed, evt)\n\t\t\t\t}\n\t\t\t},\n\t\t}\n\n\t\t// Set up a new Client using the command monitor defined above get a handle to a collection. The collection\n\t\t// needs to be explicitly created on the server because implicit collection creation is not allowed in\n\t\t// transactions for server versions <= 4.2.\n\t\tclient := setupConvenientTransactions(t, options.Client().SetMonitor(monitor))\n\t\tdb := client.Database(\"foo\")\n\t\tcoll := db.Collection(\"bar\")\n\t\terr := db.RunCommand(bgCtx, bson.D{{\"create\", coll.Name()}}).Err()\n\t\tassert.Nil(t, err, \"error creating collection on server: %v\\n\", err)\n\n\t\tsess, err := client.StartSession()\n\t\tassert.Nil(t, err, \"StartSession error: %v\", err)\n\t\tdefer func() {\n\t\t\tsess.EndSession(bgCtx)\n\t\t\t_ = coll.Drop(bgCtx)\n\t\t\t_ = client.Disconnect(bgCtx)\n\t\t}()\n\n\t\t// Create a cancellable Context with a value for ctxKey.\n\t\ttype ctxKey struct{}\n\t\tctx, cancel := context.WithCancel(context.WithValue(context.Background(), ctxKey{}, \"foobar\"))\n\t\tdefer cancel()\n\n\t\t// The WithTransaction callback does an Insert to ensure that the txn has been started server-side. After the\n\t\t// insert succeeds, it cancels the Context created above and returns a non-retryable error, which forces\n\t\t// WithTransaction to abort the txn.\n\t\tcallbackErr := errors.New(\"error\")\n\t\tcallback := func(ctx context.Context) (any, error) {\n\t\t\t_, err = coll.InsertOne(ctx, bson.D{{\"x\", 1}})\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\n\t\t\tcancel()\n\t\t\treturn nil, callbackErr\n\t\t}\n\n\t\t_, err = sess.WithTransaction(ctx, callback)\n\t\tassert.Equal(t, callbackErr, err, \"expected WithTransaction error %v, got %v\", callbackErr, err)\n\n\t\t// Assert that abortTransaction was sent once and succeede.\n\t\tassert.Equal(t, 1, len(abortStarted), \"expected 1 abortTransaction started event, got %d\", len(abortStarted))\n\t\tassert.Equal(t, 1, len(abortSucceeded), \"expected 1 abortTransaction succeeded event, got %d\",\n\t\t\tlen(abortSucceeded))\n\t\tassert.Equal(t, 0, len(abortFailed), \"expected 0 abortTransaction failed event, got %d\", len(abortFailed))\n\n\t\t// Assert that the Context propagated to the CommandStartedEvent listener for abortTransaction contained a value\n\t\t// for ctxKey.\n\t\tctxValue, ok := abortCtx.Value(ctxKey{}).(string)\n\t\tassert.True(t, ok, \"expected context for abortTransaction to contain ctxKey\")\n\t\tassert.Equal(t, \"foobar\", ctxValue, \"expected value for ctxKey to be 'world', got %s\", ctxValue)\n\t})\n\tt.Run(\"commitTransaction timeout allows abortTransaction\", func(t *testing.T) {\n\t\t// Create a special CommandMonitor that only records information about abortTransaction events.\n\t\tvar abortStarted []*event.CommandStartedEvent\n\t\tvar abortSucceeded []*event.CommandSucceededEvent\n\t\tvar abortFailed []*event.CommandFailedEvent\n\t\tmonitor := &event.CommandMonitor{\n\t\t\tStarted: func(_ context.Context, evt *event.CommandStartedEvent) {\n\t\t\t\tif evt.CommandName == \"abortTransaction\" {\n\t\t\t\t\tabortStarted = append(abortStarted, evt)\n\t\t\t\t}\n\t\t\t},\n\t\t\tSucceeded: func(_ context.Context, evt *event.CommandSucceededEvent) {\n\t\t\t\tif evt.CommandName == \"abortTransaction\" {\n\t\t\t\t\tabortSucceeded = append(abortSucceeded, evt)\n\t\t\t\t}\n\t\t\t},\n\t\t\tFailed: func(_ context.Context, evt *event.CommandFailedEvent) {\n\t\t\t\tif evt.CommandName == \"abortTransaction\" {\n\t\t\t\t\tabortFailed = append(abortFailed, evt)\n\t\t\t\t}\n\t\t\t},\n\t\t}\n\n\t\t// Set up a new Client using the command monitor defined above get a handle to a collection. The collection\n\t\t// needs to be explicitly created on the server because implicit collection creation is not allowed in\n\t\t// transactions for server versions <= 4.2.\n\t\tclient := setupConvenientTransactions(t, options.Client().SetMonitor(monitor))\n\t\tdb := client.Database(\"foo\")\n\t\tcoll := db.Collection(\"test\")\n\t\tdefer func() {\n\t\t\t_ = coll.Drop(bgCtx)\n\t\t}()\n\n\t\terr := db.RunCommand(bgCtx, bson.D{{\"create\", coll.Name()}}).Err()\n\t\tassert.Nil(t, err, \"error creating collection on server: %v\", err)\n\n\t\t// Start session.\n\t\tsession, err := client.StartSession()\n\t\tdefer session.EndSession(bgCtx)\n\t\tassert.Nil(t, err, \"StartSession error: %v\", err)\n\n\t\t_ = WithSession(bgCtx, session, func(ctx context.Context) error {\n\t\t\t// Start transaction.\n\t\t\terr = session.StartTransaction()\n\t\t\tassert.Nil(t, err, \"StartTransaction error: %v\", err)\n\n\t\t\t// Insert a document.\n\t\t\t_, err := coll.InsertOne(ctx, bson.D{{\"val\", 17}})\n\t\t\tassert.Nil(t, err, \"InsertOne error: %v\", err)\n\n\t\t\t// Set a timeout of 0 for commitTransaction.\n\t\t\tcommitTimeoutCtx, commitCancel := context.WithTimeout(ctx, 0)\n\t\t\tdefer commitCancel()\n\n\t\t\t// CommitTransaction results in context.DeadlineExceeded.\n\t\t\tcommitErr := session.CommitTransaction(commitTimeoutCtx)\n\t\t\tassert.True(t, IsTimeout(commitErr),\n\t\t\t\t\"expected timeout error error; got %v\", commitErr)\n\n\t\t\t// Assert session state is not Committed.\n\t\t\tclientSession := session.clientSession\n\t\t\tassert.False(t, clientSession.TransactionCommitted(), \"expected session state to not be Committed\")\n\n\t\t\t// AbortTransaction without error.\n\t\t\tabortErr := session.AbortTransaction(context.Background())\n\t\t\tassert.Nil(t, abortErr, \"AbortTransaction error: %v\", abortErr)\n\n\t\t\t// Assert that AbortTransaction was started once and succeeded.\n\t\t\tassert.Equal(t, 1, len(abortStarted), \"expected 1 abortTransaction started event, got %d\", len(abortStarted))\n\t\t\tassert.Equal(t, 1, len(abortSucceeded), \"expected 1 abortTransaction succeeded event, got %d\",\n\t\t\t\tlen(abortSucceeded))\n\t\t\tassert.Equal(t, 0, len(abortFailed), \"expected 0 abortTransaction failed events, got %d\", len(abortFailed))\n\n\t\t\treturn nil\n\t\t})\n\t})\n\tt.Run(\"context error before commitTransaction does not retry and aborts\", func(t *testing.T) {\n\t\ttimeout := withTransactionTimeout\n\t\tdefer func() { withTransactionTimeout = timeout }()\n\t\twithTransactionTimeout = 2 * time.Second\n\n\t\t// Create a special CommandMonitor that only records information about abortTransaction events.\n\t\tvar abortStarted []*event.CommandStartedEvent\n\t\tvar abortSucceeded []*event.CommandSucceededEvent\n\t\tvar abortFailed []*event.CommandFailedEvent\n\t\tmonitor := &event.CommandMonitor{\n\t\t\tStarted: func(_ context.Context, evt *event.CommandStartedEvent) {\n\t\t\t\tif evt.CommandName == \"abortTransaction\" {\n\t\t\t\t\tabortStarted = append(abortStarted, evt)\n\t\t\t\t}\n\t\t\t},\n\t\t\tSucceeded: func(_ context.Context, evt *event.CommandSucceededEvent) {\n\t\t\t\tif evt.CommandName == \"abortTransaction\" {\n\t\t\t\t\tabortSucceeded = append(abortSucceeded, evt)\n\t\t\t\t}\n\t\t\t},\n\t\t\tFailed: func(_ context.Context, evt *event.CommandFailedEvent) {\n\t\t\t\tif evt.CommandName == \"abortTransaction\" {\n\t\t\t\t\tabortFailed = append(abortFailed, evt)\n\t\t\t\t}\n\t\t\t},\n\t\t}\n\n\t\t// Set up a new Client using the command monitor defined above get a handle to a collection. The collection\n\t\t// needs to be explicitly created on the server because implicit collection creation is not allowed in\n\t\t// transactions for server versions <= 4.2.\n\t\tclient := setupConvenientTransactions(t, options.Client().SetMonitor(monitor))\n\t\tdb := client.Database(\"foo\")\n\t\tcoll := db.Collection(\"test\")\n\t\t// Explicitly create the collection on server because implicit collection creation is not allowed in\n\t\t// transactions for server versions <= 4.2.\n\t\terr := db.RunCommand(bgCtx, bson.D{{\"create\", coll.Name()}}).Err()\n\t\tassert.Nil(t, err, \"error creating collection on server: %v\", err)\n\t\tdefer func() {\n\t\t\t_ = coll.Drop(bgCtx)\n\t\t}()\n\n\t\t// Start session.\n\t\tsess, err := client.StartSession()\n\t\tassert.Nil(t, err, \"StartSession error: %v\", err)\n\t\tdefer sess.EndSession(context.Background())\n\n\t\t// Defer running killAllSessions to manually close open transaction.\n\t\tdefer func() {\n\t\t\terr := dbAdmin.RunCommand(bgCtx, bson.D{\n\t\t\t\t{\"killAllSessions\", bson.A{}},\n\t\t\t}).Err()\n\t\t\tif err != nil {\n\t\t\t\tvar ce CommandError\n\t\t\t\tif !errors.As(err, &ce) || ce.Code != errorInterrupted {\n\t\t\t\t\tt.Fatalf(\"killAllSessions error: %v\", err)\n\t\t\t\t}\n\t\t\t}\n\t\t}()\n\n\t\t// Insert a document within a session and manually cancel context before\n\t\t// \"commitTransaction\" can be sent.\n\t\tcallback := func() bool {\n\t\t\ttransactionCtx, cancel := context.WithCancel(context.Background())\n\t\t\t_, _ = sess.WithTransaction(transactionCtx, func(ctx context.Context) (any, error) {\n\t\t\t\t_, err := coll.InsertOne(ctx, bson.M{\"x\": 1})\n\t\t\t\tassert.NoError(t, err, \"InsertOne error: %v\", err)\n\t\t\t\tcancel()\n\t\t\t\treturn nil, nil\n\t\t\t})\n\t\t\treturn true\n\t\t}\n\n\t\t// Assert that transaction is canceled within 500ms and not 2 seconds.\n\t\tassert.Eventually(t,\n\t\t\tcallback,\n\t\t\t500*time.Millisecond,\n\t\t\ttime.Millisecond,\n\t\t\t\"expected transaction to be canceled within 500ms\")\n\n\t\t// Assert that AbortTransaction was started once and succeeded.\n\t\tassert.Equal(t, 1, len(abortStarted), \"expected 1 abortTransaction started event, got %d\", len(abortStarted))\n\t\tassert.Equal(t, 1, len(abortSucceeded), \"expected 1 abortTransaction succeeded event, got %d\",\n\t\t\tlen(abortSucceeded))\n\t\tassert.Equal(t, 0, len(abortFailed), \"expected 0 abortTransaction failed events, got %d\", len(abortFailed))\n\t})\n\tt.Run(\"wrapped transient transaction error retried\", func(t *testing.T) {\n\t\tsess, err := client.StartSession()\n\t\tassert.Nil(t, err, \"StartSession error: %v\", err)\n\t\tdefer sess.EndSession(context.Background())\n\n\t\t// returnError tracks whether or not the callback is being retried\n\t\treturnError := true\n\t\tres, err := sess.WithTransaction(context.Background(), func(context.Context) (any, error) {\n\t\t\tif returnError {\n\t\t\t\treturnError = false\n\t\t\t\treturn nil, fmt.Errorf(\"%w\",\n\t\t\t\t\tCommandError{\n\t\t\t\t\t\tName:   \"test Error\",\n\t\t\t\t\t\tLabels: []string{driver.TransientTransactionError},\n\t\t\t\t\t},\n\t\t\t\t)\n\t\t\t}\n\t\t\treturn false, nil\n\t\t})\n\t\tassert.Nil(t, err, \"WithTransaction error: %v\", err)\n\t\tresBool, ok := res.(bool)\n\t\tassert.True(t, ok, \"expected result type %T, got %T\", false, res)\n\t\tassert.False(t, resBool, \"expected result false, got %v\", resBool)\n\t})\n\tt.Run(\"expired context before callback does not retry\", func(t *testing.T) {\n\t\ttimeout := withTransactionTimeout\n\t\tdefer func() { withTransactionTimeout = timeout }()\n\t\twithTransactionTimeout = 2 * time.Second\n\n\t\tcoll := db.Collection(\"test\")\n\t\t// Explicitly create the collection on server because implicit collection creation is not allowed in\n\t\t// transactions for server versions <= 4.2.\n\t\terr := db.RunCommand(bgCtx, bson.D{{\"create\", coll.Name()}}).Err()\n\t\tassert.Nil(t, err, \"error creating collection on server: %v\", err)\n\t\tdefer func() {\n\t\t\t_ = coll.Drop(bgCtx)\n\t\t}()\n\n\t\tsess, err := client.StartSession()\n\t\tassert.Nil(t, err, \"StartSession error: %v\", err)\n\t\tdefer sess.EndSession(context.Background())\n\n\t\tcallback := func() bool {\n\t\t\t// Create transaction context with short timeout.\n\t\t\twithTransactionContext, cancel := context.WithTimeout(context.Background(), time.Nanosecond)\n\t\t\tdefer cancel()\n\n\t\t\t_, _ = sess.WithTransaction(withTransactionContext, func(ctx context.Context) (any, error) {\n\t\t\t\t_, err := coll.InsertOne(ctx, bson.D{{}})\n\t\t\t\treturn nil, err\n\t\t\t})\n\t\t\treturn true\n\t\t}\n\n\t\t// Assert that transaction fails within 500ms and not 2 seconds.\n\t\tassert.Eventually(t,\n\t\t\tcallback,\n\t\t\t500*time.Millisecond,\n\t\t\ttime.Millisecond,\n\t\t\t\"expected transaction to fail within 500ms\")\n\t})\n\tt.Run(\"canceled context before callback does not retry\", func(t *testing.T) {\n\t\ttimeout := withTransactionTimeout\n\t\tdefer func() { withTransactionTimeout = timeout }()\n\t\twithTransactionTimeout = 2 * time.Second\n\n\t\tcoll := db.Collection(\"test\")\n\t\t// Explicitly create the collection on server because implicit collection creation is not allowed in\n\t\t// transactions for server versions <= 4.2.\n\t\terr := db.RunCommand(bgCtx, bson.D{{\"create\", coll.Name()}}).Err()\n\t\tassert.Nil(t, err, \"error creating collection on server: %v\", err)\n\t\tdefer func() {\n\t\t\t_ = coll.Drop(bgCtx)\n\t\t}()\n\n\t\tsess, err := client.StartSession()\n\t\tassert.Nil(t, err, \"StartSession error: %v\", err)\n\t\tdefer sess.EndSession(context.Background())\n\n\t\tcallback := func() bool {\n\t\t\t// Create transaction context and cancel it immediately.\n\t\t\twithTransactionContext, cancel := context.WithTimeout(context.Background(), 2*time.Second)\n\t\t\tcancel()\n\n\t\t\t_, _ = sess.WithTransaction(withTransactionContext, func(ctx context.Context) (any, error) {\n\t\t\t\t_, err := coll.InsertOne(ctx, bson.D{{}})\n\t\t\t\treturn nil, err\n\t\t\t})\n\t\t\treturn true\n\t\t}\n\n\t\t// Assert that transaction fails within 500ms and not 2 seconds.\n\t\tassert.Eventually(t,\n\t\t\tcallback,\n\t\t\t500*time.Millisecond,\n\t\t\ttime.Millisecond,\n\t\t\t\"expected transaction to fail within 500ms\")\n\t})\n\tt.Run(\"slow operation in callback retries\", func(t *testing.T) {\n\t\tif os.Getenv(\"TOPOLOGY\") == \"sharded_cluster\" {\n\t\t\tt.Skip(\"skipping on sharded clusters due to SERVER-96344; see GODRIVER-3801\")\n\t\t}\n\t\ttimeout := withTransactionTimeout\n\t\tdefer func() { withTransactionTimeout = timeout }()\n\t\twithTransactionTimeout = 2 * time.Second\n\n\t\tcoll := db.Collection(\"test\")\n\t\t// Explicitly create the collection on server because implicit collection creation is not allowed in\n\t\t// transactions for server versions <= 4.2.\n\t\terr := db.RunCommand(bgCtx, bson.D{{\"create\", coll.Name()}}).Err()\n\t\tassert.Nil(t, err, \"error creating collection on server: %v\", err)\n\t\tdefer func() {\n\t\t\t_ = coll.Drop(bgCtx)\n\t\t}()\n\n\t\t// Set failpoint to block insertOne once for 500ms.\n\t\tfailpoint := bson.D{\n\t\t\t{\"configureFailPoint\", \"failCommand\"},\n\t\t\t{\"mode\", bson.D{\n\t\t\t\t{\"times\", 1},\n\t\t\t}},\n\t\t\t{\"data\", bson.D{\n\t\t\t\t{\"failCommands\", bson.A{\"insert\"}},\n\t\t\t\t{\"blockConnection\", true},\n\t\t\t\t{\"blockTimeMS\", 500},\n\t\t\t}},\n\t\t}\n\t\terr = dbAdmin.RunCommand(bgCtx, failpoint).Err()\n\t\tassert.Nil(t, err, \"error setting failpoint: %v\", err)\n\t\tdefer func() {\n\t\t\terr = dbAdmin.RunCommand(bgCtx, bson.D{\n\t\t\t\t{\"configureFailPoint\", \"failCommand\"},\n\t\t\t\t{\"mode\", \"off\"},\n\t\t\t}).Err()\n\t\t\tassert.Nil(t, err, \"error turning off failpoint: %v\", err)\n\t\t}()\n\n\t\tsess, err := client.StartSession()\n\t\tassert.Nil(t, err, \"StartSession error: %v\", err)\n\t\tdefer sess.EndSession(context.Background())\n\n\t\tcallback := func() bool {\n\t\t\t_, err = sess.WithTransaction(context.Background(), func(ctx context.Context) (any, error) {\n\t\t\t\t// Set a timeout of 300ms to cause a timeout on first insertOne\n\t\t\t\t// and force a retry.\n\t\t\t\tc, cancel := context.WithTimeout(ctx, 300*time.Millisecond)\n\t\t\t\tdefer cancel()\n\n\t\t\t\t_, err := coll.InsertOne(c, bson.D{{}})\n\t\t\t\treturn nil, err\n\t\t\t})\n\t\t\tassert.NoError(t, err, \"WithTransaction error: %v\", err)\n\t\t\treturn true\n\t\t}\n\n\t\t// Assert that transaction passes within 2 seconds.\n\t\tassert.Eventually(t,\n\t\t\tcallback,\n\t\t\twithTransactionTimeout,\n\t\t\ttime.Millisecond,\n\t\t\t\"expected transaction to be passed within 2s\")\n\t})\n\tt.Run(\"retries correctly for joined errors\", func(t *testing.T) {\n\t\ttimeout := withTransactionTimeout\n\t\tdefer func() { withTransactionTimeout = timeout }()\n\t\twithTransactionTimeout = 500 * time.Millisecond\n\n\t\tsess, err := client.StartSession()\n\t\trequire.Nil(t, err, \"StartSession error: %v\", err)\n\t\tdefer sess.EndSession(context.Background())\n\n\t\tcount := 0\n\t\t_, _ = sess.WithTransaction(context.Background(), func(context.Context) (any, error) {\n\t\t\tcount++\n\t\t\ttime.Sleep(10 * time.Millisecond)\n\n\t\t\t// Return a combined error value that is built using both\n\t\t\t// errors.Join and fmt.Errorf with multiple \"%w\" verbs, nesting a\n\t\t\t// retryable CommandError within the joined error tree.\n\t\t\treturn nil, errors.Join(\n\t\t\t\tfmt.Errorf(\"%w, %w\",\n\t\t\t\t\tCommandError{Name: \"test err 1\", Labels: []string{driver.TransientTransactionError}},\n\t\t\t\t\terrors.New(\"test err 2\"),\n\t\t\t\t),\n\t\t\t\terrors.New(\"test err 3\"),\n\t\t\t)\n\t\t})\n\t\tassert.Greater(t, count, 1, \"expected WithTransaction callback to be retried at least once\")\n\t})\n}\n\ntype testJitter struct {\n\tratio float64\n}\n\nfunc (j *testJitter) Int63n(n int64) int64 {\n\tif j.ratio <= 0 {\n\t\treturn 0\n\t}\n\tif j.ratio >= 1 {\n\t\treturn n\n\t}\n\tval := j.ratio * float64(n)\n\treturn int64(val)\n}\n\nfunc setupConvenientTransactions(t *testing.T, extraClientOpts ...*options.ClientOptions) *Client {\n\tcs := integtest.ConnString(t)\n\tpoolMonitor := &event.PoolMonitor{\n\t\tEvent: func(evt *event.PoolEvent) {\n\t\t\tswitch evt.Type {\n\t\t\tcase event.ConnectionCheckedOut:\n\t\t\t\tconnsCheckedOut++\n\t\t\tcase event.ConnectionCheckedIn:\n\t\t\t\tconnsCheckedOut--\n\t\t\t}\n\t\t},\n\t}\n\n\tbaseClientOpts := options.Client().\n\t\tApplyURI(cs.Original).\n\t\tSetReadPreference(readpref.Primary()).\n\t\tSetWriteConcern(writeconcern.Majority()).\n\t\tSetPoolMonitor(poolMonitor)\n\tintegtest.AddTestServerAPIVersion(baseClientOpts)\n\tfullClientOpts := []*options.ClientOptions{baseClientOpts}\n\tfullClientOpts = append(fullClientOpts, extraClientOpts...)\n\n\tclient, err := Connect(fullClientOpts...)\n\tassert.Nil(t, err, \"Connect error: %v\", err)\n\n\tversion, err := getServerVersion(client.Database(\"admin\"))\n\tassert.Nil(t, err, \"getServerVersion error: %v\", err)\n\ttopoKind := client.deployment.(*topology.Topology).Kind()\n\tif compareVersions(version, \"4.1\") < 0 || topoKind == description.TopologyKindSingle {\n\t\tt.Skip(\"skipping standalones and versions < 4.1\")\n\t}\n\n\tif topoKind != description.TopologyKindSharded {\n\t\treturn client\n\t}\n\n\t// For sharded clusters, disconnect the previous Client and create a new one that's pinned to a single mongos.\n\t_ = client.Disconnect(bgCtx)\n\tfullClientOpts = append(fullClientOpts, options.Client().SetHosts([]string{cs.Hosts[0]}))\n\tclient, err = Connect(fullClientOpts...)\n\tassert.Nil(t, err, \"Connect error: %v\", err)\n\treturn client\n}\n\nfunc getServerVersion(db *Database) (string, error) {\n\tserverStatus, err := db.RunCommand(\n\t\tcontext.Background(),\n\t\tbson.D{{\"serverStatus\", 1}},\n\t).Raw()\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\tversion, err := serverStatus.LookupErr(\"version\")\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\treturn version.StringValue(), nil\n}\n\n// compareVersions compares two version number strings (i.e. positive integers separated by\n// periods). Comparisons are done to the lesser precision of the two versions. For example, 3.2 is\n// considered equal to 3.2.11, whereas 3.2.0 is considered less than 3.2.11.\n//\n// Returns a positive int if version1 is greater than version2, a negative int if version1 is less\n// than version2, and 0 if version1 is equal to version2.\nfunc compareVersions(v1 string, v2 string) int {\n\tn1 := strings.Split(v1, \".\")\n\tn2 := strings.Split(v2, \".\")\n\n\tfor i := 0; i < int(math.Min(float64(len(n1)), float64(len(n2)))); i++ {\n\t\ti1, err := strconv.Atoi(n1[i])\n\t\tif err != nil {\n\t\t\treturn 1\n\t\t}\n\n\t\ti2, err := strconv.Atoi(n2[i])\n\t\tif err != nil {\n\t\t\treturn -1\n\t\t}\n\n\t\tdifference := i1 - i2\n\t\tif difference != 0 {\n\t\t\treturn difference\n\t\t}\n\t}\n\n\treturn 0\n}\n"
  },
  {
    "path": "mongo/writeconcern/writeconcern.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\n// Package writeconcern defines write concerns for MongoDB operations.\n//\n// For more information about MongoDB write concerns, see\n// https://www.mongodb.com/docs/manual/reference/write-concern/\npackage writeconcern\n\n// WCMajority can be used to create a WriteConcern with a W value of \"majority\".\nconst WCMajority = \"majority\"\n\n// A WriteConcern defines a MongoDB write concern, which describes the level of acknowledgment\n// requested from MongoDB for write operations to a standalone mongod, to replica sets, or to\n// sharded clusters.\n//\n// For more information about MongoDB write concerns, see\n// https://www.mongodb.com/docs/manual/reference/write-concern/\ntype WriteConcern struct {\n\t// W requests acknowledgment that the write operation has propagated to a\n\t// specified number of mongod instances or to mongod instances with\n\t// specified tags. It sets the \"w\" option in a MongoDB write concern.\n\t//\n\t// W values must be a string or an int.\n\t//\n\t// Common values are:\n\t//   - \"majority\": requests acknowledgment that write operations have been\n\t//     durably committed to the calculated majority of the data-bearing\n\t//     voting members.\n\t//   - 1: requests acknowledgment that write operations have been written\n\t//     to 1 node.\n\t//   - 0: requests no acknowledgment of write operations\n\t//\n\t// For more information about the \"w\" option, see\n\t// https://www.mongodb.com/docs/manual/reference/write-concern/#w-option\n\tW any\n\n\t// Journal requests acknowledgment from MongoDB that the write operation has\n\t// been written to the on-disk journal. It sets the \"j\" option in a MongoDB\n\t// write concern.\n\t//\n\t// For more information about the \"j\" option, see\n\t// https://www.mongodb.com/docs/manual/reference/write-concern/#j-option\n\tJournal *bool\n}\n\n// Unacknowledged returns a WriteConcern that requests no acknowledgment of\n// write operations.\n//\n// For more information about write concern \"w: 0\", see\n// https://www.mongodb.com/docs/manual/reference/write-concern/#mongodb-writeconcern-writeconcern.-number-\nfunc Unacknowledged() *WriteConcern {\n\treturn &WriteConcern{W: 0}\n}\n\n// W1 returns a WriteConcern that requests acknowledgment that write operations\n// have been written to memory on one node (e.g. the standalone mongod or the\n// primary in a replica set).\n//\n// For more information about write concern \"w: 1\", see\n// https://www.mongodb.com/docs/manual/reference/write-concern/#mongodb-writeconcern-writeconcern.-number-\nfunc W1() *WriteConcern {\n\treturn &WriteConcern{W: 1}\n}\n\n// Journaled returns a WriteConcern that requests acknowledgment that write\n// operations have been written to the on-disk journal on MongoDB.\n//\n// The database's default value for \"w\" determines how many nodes must write to\n// their on-disk journal before the write operation is acknowledged.\n//\n// For more information about write concern \"j: true\", see\n// https://www.mongodb.com/docs/manual/reference/write-concern/#mongodb-writeconcern-ournal\nfunc Journaled() *WriteConcern {\n\tjournal := true\n\treturn &WriteConcern{Journal: &journal}\n}\n\n// Majority returns a WriteConcern that requests acknowledgment that write\n// operations have been durably committed to the calculated majority of the\n// data-bearing voting members.\n//\n// Write concern \"w: majority\" typically requires write operations to be written\n// to the on-disk journal before they are acknowledged, unless journaling is\n// disabled on MongoDB or the \"writeConcernMajorityJournalDefault\" replica set\n// configuration is set to false.\n//\n// For more information about write concern \"w: majority\", see\n// https://www.mongodb.com/docs/manual/reference/write-concern/#mongodb-writeconcern-writeconcern.-majority-\nfunc Majority() *WriteConcern {\n\treturn &WriteConcern{W: WCMajority}\n}\n\n// Custom returns a WriteConcern that requests acknowledgment that write\n// operations have propagated to tagged members that satisfy the custom write\n// concern defined in \"settings.getLastErrorModes\".\n//\n// For more information about custom write concern names, see\n// https://www.mongodb.com/docs/manual/reference/write-concern/#mongodb-writeconcern-writeconcern.-custom-write-concern-name-\nfunc Custom(tag string) *WriteConcern {\n\treturn &WriteConcern{W: tag}\n}\n\n// Acknowledged indicates whether or not a write with the given write concern will be acknowledged.\nfunc (wc *WriteConcern) Acknowledged() bool {\n\t// Only {w: 0} or {w: 0, j: false} are an unacknowledged write concerns. All other values are\n\t// acknowledged.\n\treturn wc == nil || wc.W != 0 || (wc.Journal != nil && *wc.Journal)\n}\n\n// IsValid returns true if the WriteConcern is valid.\nfunc (wc *WriteConcern) IsValid() bool {\n\tif wc == nil {\n\t\treturn true\n\t}\n\n\tswitch w := wc.W.(type) {\n\tcase int:\n\t\t// A write concern with {w: int} must have a non-negative value and\n\t\t// cannot have the combination {w: 0, j: true}.\n\t\treturn w >= 0 && (w > 0 || wc.Journal == nil || !*wc.Journal)\n\tcase string, nil:\n\t\t// A write concern with {w: string} or no w specified is always valid.\n\t\treturn true\n\tdefault:\n\t\t// A write concern with an unsupported w type is not valid.\n\t\treturn false\n\t}\n}\n"
  },
  {
    "path": "mongo/writeconcern/writeconcern_example_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2023-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage writeconcern_test\n\nimport (\n\t\"go.mongodb.org/mongo-driver/v2/mongo\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/writeconcern\"\n)\n\n// Configure a Client with write concern \"majority\" that requests\n// acknowledgement that a majority of the nodes have committed write operations.\nfunc Example_majority() {\n\twc := writeconcern.Majority()\n\n\topts := options.Client().\n\t\tApplyURI(\"mongodb://localhost:27017\").\n\t\tSetWriteConcern(wc)\n\n\t_, err := mongo.Connect(opts)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n}\n\n// Configure a Client with a write concern that requests acknowledgement that\n// exactly 2 nodes have committed and journaled write operations.\nfunc Example_w2Journaled() {\n\twc := &writeconcern.WriteConcern{\n\t\tW:       2,\n\t\tJournal: boolPtr(true),\n\t}\n\n\topts := options.Client().\n\t\tApplyURI(\"mongodb://localhost:27017\").\n\t\tSetWriteConcern(wc)\n\n\t_, err := mongo.Connect(opts)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n}\n\n// boolPtr is a helper function to convert a bool constant into a bool pointer.\n//\n// If you're using a version of Go that supports generics, you can define a\n// generic version of this function that works with any type. For example:\n//\n//\tfunc ptr[T any](v T) *T {\n//\t\treturn &v\n//\t}\nfunc boolPtr(b bool) *bool {\n\treturn &b\n}\n"
  },
  {
    "path": "mongo/writeconcern/writeconcern_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage writeconcern_test\n\nimport (\n\t\"testing\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/writeconcern\"\n)\n\nfunc TestWriteConcern(t *testing.T) {\n\tboolPtr := func(b bool) *bool { return &b }\n\n\ttestCases := []struct {\n\t\tname             string\n\t\twc               *writeconcern.WriteConcern\n\t\twantAcknowledged bool\n\t\twantIsValid      bool\n\t}{\n\t\t{\n\t\t\tname:             \"Unacknowledged\",\n\t\t\twc:               writeconcern.Unacknowledged(),\n\t\t\twantAcknowledged: false,\n\t\t\twantIsValid:      true,\n\t\t},\n\t\t{\n\t\t\tname:             \"W1\",\n\t\t\twc:               writeconcern.W1(),\n\t\t\twantAcknowledged: true,\n\t\t\twantIsValid:      true,\n\t\t},\n\t\t{\n\t\t\tname:             \"Journaled\",\n\t\t\twc:               writeconcern.Journaled(),\n\t\t\twantAcknowledged: true,\n\t\t\twantIsValid:      true,\n\t\t},\n\t\t{\n\t\t\tname:             \"Majority\",\n\t\t\twc:               writeconcern.Majority(),\n\t\t\twantAcknowledged: true,\n\t\t\twantIsValid:      true,\n\t\t},\n\t\t{\n\t\t\tname: \"{w: 0, j: true}\",\n\t\t\twc: &writeconcern.WriteConcern{\n\t\t\t\tW:       0,\n\t\t\t\tJournal: boolPtr(true),\n\t\t\t},\n\t\t\twantAcknowledged: true,\n\t\t\twantIsValid:      false,\n\t\t},\n\t\t{\n\t\t\tname:             \"{w: custom}\",\n\t\t\twc:               &writeconcern.WriteConcern{W: \"custom\"},\n\t\t\twantAcknowledged: true,\n\t\t\twantIsValid:      true,\n\t\t},\n\t\t{\n\t\t\tname:             \"nil\",\n\t\t\twc:               nil,\n\t\t\twantAcknowledged: true,\n\t\t\twantIsValid:      true,\n\t\t},\n\t\t{\n\t\t\tname: \"invalid type\",\n\t\t\twc: &writeconcern.WriteConcern{\n\t\t\t\tW: struct{ Field string }{},\n\t\t\t},\n\t\t\twantAcknowledged: true,\n\t\t\twantIsValid:      false,\n\t\t},\n\t}\n\n\tfor _, tc := range testCases {\n\t\ttc := tc // Capture range variable.\n\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tassert.Equal(t,\n\t\t\t\ttc.wantAcknowledged,\n\t\t\t\ttc.wc.Acknowledged(),\n\t\t\t\t\"expected and actual Acknowledged value are different\")\n\t\t\tassert.Equal(t,\n\t\t\t\ttc.wantIsValid,\n\t\t\t\ttc.wc.IsValid(),\n\t\t\t\t\"expected and actual IsValid value are different\")\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "sbom.json",
    "content": "{\n    \"metadata\": {\n      \"timestamp\": \"2024-05-02T17:36:29.429171+00:00\"\n    },\n    \"components\": [],\n    \"serialNumber\": \"urn:uuid:06a59521-ad52-420b-aee6-7d9ed15e1fd9\",\n    \"version\": 1,\n    \"$schema\": \"http://cyclonedx.org/schema/bom-1.5.schema.json\",\n    \"bomFormat\": \"CycloneDX\",\n    \"specVersion\": \"1.5\"\n  }"
  },
  {
    "path": "tag/tag.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\n// Package tag provides types for filtering replica set members using tags in a read preference.\n//\n// For more information about read preference tags, see\n// https://www.mongodb.com/docs/manual/core/read-preference-tags/\npackage tag\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n)\n\n// Tag is a name/value pair.\ntype Tag struct {\n\tName  string\n\tValue string\n}\n\n// String returns a human-readable human-readable description of the tag.\nfunc (tag Tag) String() string {\n\treturn fmt.Sprintf(\"%s=%s\", tag.Name, tag.Value)\n}\n\n// NewTagSetFromMap creates a tag set from a map.\n//\n// For more information about read preference tags, see\n// https://www.mongodb.com/docs/manual/core/read-preference-tags/\nfunc NewTagSetFromMap(m map[string]string) Set {\n\tset := make(Set, 0, len(m))\n\tfor k, v := range m {\n\t\tset = append(set, Tag{Name: k, Value: v})\n\t}\n\n\treturn set\n}\n\n// NewTagSetsFromMaps creates a list of tag sets from a slice of maps.\n//\n// For more information about read preference tags, see\n// https://www.mongodb.com/docs/manual/core/read-preference-tags/\nfunc NewTagSetsFromMaps(maps []map[string]string) []Set {\n\tsets := make([]Set, 0, len(maps))\n\tfor _, m := range maps {\n\t\tsets = append(sets, NewTagSetFromMap(m))\n\t}\n\treturn sets\n}\n\n// Set is an ordered list of Tags.\ntype Set []Tag\n\n// Contains indicates whether the name/value pair exists in the tagset.\nfunc (ts Set) Contains(name, value string) bool {\n\tfor _, t := range ts {\n\t\tif t.Name == name && t.Value == value {\n\t\t\treturn true\n\t\t}\n\t}\n\n\treturn false\n}\n\n// ContainsAll indicates whether all the name/value pairs exist in the tagset.\nfunc (ts Set) ContainsAll(other []Tag) bool {\n\tfor _, ot := range other {\n\t\tif !ts.Contains(ot.Name, ot.Value) {\n\t\t\treturn false\n\t\t}\n\t}\n\n\treturn true\n}\n\n// String returns a human-readable human-readable description of the tagset.\nfunc (ts Set) String() string {\n\tvar b bytes.Buffer\n\tfor i, tag := range ts {\n\t\tif i > 0 {\n\t\t\tb.WriteString(\",\")\n\t\t}\n\t\tb.WriteString(tag.String())\n\t}\n\treturn b.String()\n}\n"
  },
  {
    "path": "tag/tag_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage tag\n\nimport (\n\t\"testing\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/require\"\n)\n\nfunc TestTag_String(t *testing.T) {\n\tt.Parallel()\n\n\ttag := Tag{Name: \"a\", Value: \"1\"}\n\tassert.Equal(t, \"a=1\", tag.String(), `expected \"a=1\", got %q`, tag.String())\n}\n\nfunc TestTagSets_NewTagSet(t *testing.T) {\n\tt.Parallel()\n\n\tts := Set{Tag{Name: \"a\", Value: \"1\"}}\n\n\trequire.True(t, ts.Contains(\"a\", \"1\"))\n\trequire.False(t, ts.Contains(\"1\", \"a\"))\n\trequire.False(t, ts.Contains(\"A\", \"1\"))\n\trequire.False(t, ts.Contains(\"a\", \"10\"))\n}\n\nfunc TestTagSets_NewTagSetFromMap(t *testing.T) {\n\tt.Parallel()\n\n\tts := NewTagSetFromMap(map[string]string{\"a\": \"1\"})\n\n\trequire.True(t, ts.Contains(\"a\", \"1\"))\n\trequire.False(t, ts.Contains(\"1\", \"a\"))\n\trequire.False(t, ts.Contains(\"A\", \"1\"))\n\trequire.False(t, ts.Contains(\"a\", \"10\"))\n}\n\nfunc TestTagSets_NewTagSetsFromMaps(t *testing.T) {\n\tt.Parallel()\n\n\ttss := NewTagSetsFromMaps([]map[string]string{{\"a\": \"1\"}, {\"b\": \"1\"}})\n\n\trequire.Len(t, tss, 2)\n\n\tts := tss[0]\n\trequire.True(t, ts.Contains(\"a\", \"1\"))\n\trequire.False(t, ts.Contains(\"1\", \"a\"))\n\trequire.False(t, ts.Contains(\"A\", \"1\"))\n\trequire.False(t, ts.Contains(\"a\", \"10\"))\n\n\tts = tss[1]\n\trequire.True(t, ts.Contains(\"b\", \"1\"))\n\trequire.False(t, ts.Contains(\"1\", \"b\"))\n\trequire.False(t, ts.Contains(\"B\", \"1\"))\n\trequire.False(t, ts.Contains(\"b\", \"10\"))\n}\n\nfunc TestTagSets_ContainsAll(t *testing.T) {\n\tt.Parallel()\n\n\tts := Set{\n\t\tTag{Name: \"a\", Value: \"1\"},\n\t\tTag{Name: \"b\", Value: \"2\"},\n\t}\n\n\ttest := Set{Tag{Name: \"a\", Value: \"1\"}}\n\trequire.True(t, ts.ContainsAll(test))\n\ttest = Set{Tag{Name: \"a\", Value: \"1\"}, Tag{Name: \"b\", Value: \"2\"}}\n\trequire.True(t, ts.ContainsAll(test))\n\ttest = Set{Tag{Name: \"a\", Value: \"1\"}, Tag{Name: \"b\", Value: \"2\"}}\n\trequire.True(t, ts.ContainsAll(test))\n\n\ttest = Set{Tag{Name: \"a\", Value: \"2\"}, Tag{Name: \"b\", Value: \"1\"}}\n\trequire.False(t, ts.ContainsAll(test))\n\ttest = Set{Tag{Name: \"a\", Value: \"1\"}, Tag{Name: \"b\", Value: \"1\"}}\n\trequire.False(t, ts.ContainsAll(test))\n\ttest = Set{Tag{Name: \"a\", Value: \"2\"}, Tag{Name: \"b\", Value: \"2\"}}\n\trequire.False(t, ts.ContainsAll(test))\n}\n\nfunc TestTagSets_String(t *testing.T) {\n\tt.Parallel()\n\n\tts := Set{\n\t\tTag{Name: \"a\", Value: \"1\"},\n\t\tTag{Name: \"b\", Value: \"2\"},\n\t}\n\tassert.Equal(t, \"a=1,b=2\", ts.String(), `expected \"a=1,b=2\", got %q`, ts.String())\n}\n"
  },
  {
    "path": "testdata/client-side-encryption-prose/change-streams-test.json",
    "content": "{\n    \"json_schema\": {\n        \"properties\": {\n            \"encrypted_string\": {\n                \"encrypt\": {\n                    \"keyId\": [\n                        {\n                            \"$binary\": {\n                                \"base64\": \"AAAAAAAAAAAAAAAAAAAAAA==\",\n                                \"subType\": \"04\"\n                            }\n                        }\n                    ],\n                    \"bsonType\": \"string\",\n                    \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\"\n                }\n            }\n        },\n        \"bsonType\": \"object\"\n    },\n    \"key_vault_data\": [\n        {\n            \"status\": 1,\n            \"_id\": {\n                \"$binary\": {\n                    \"base64\": \"AAAAAAAAAAAAAAAAAAAAAA==\",\n                    \"subType\": \"04\"\n                }\n            },\n            \"masterKey\": {\n                \"provider\": \"aws\",\n                \"key\": \"arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0\",\n                \"region\": \"us-east-1\"\n            },\n            \"updateDate\": {\n                \"$date\": {\n                    \"$numberLong\": \"1552949630483\"\n                }\n            },\n            \"keyMaterial\": {\n                \"$binary\": {\n                    \"base64\": \"AQICAHhQNmWG2CzOm1dq3kWLM+iDUZhEqnhJwH9wZVpuZ94A8gEqnsxXlR51T5EbEVezUqqKAAAAwjCBvwYJKoZIhvcNAQcGoIGxMIGuAgEAMIGoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDHa4jo6yp0Z18KgbUgIBEIB74sKxWtV8/YHje5lv5THTl0HIbhSwM6EqRlmBiFFatmEWaeMk4tO4xBX65eq670I5TWPSLMzpp8ncGHMmvHqRajNBnmFtbYxN3E3/WjxmdbOOe+OXpnGJPcGsftc7cB2shRfA4lICPnE26+oVNXT6p0Lo20nY5XC7jyCO\",\n                    \"subType\": \"00\"\n                }\n            },\n            \"creationDate\": {\n                \"$date\": {\n                    \"$numberLong\": \"1552949630483\"\n                }\n            },\n            \"keyAltNames\": [\n                \"altname\",\n                \"another_altname\"\n            ]\n        }\n    ],\n    \"encrypted_document\": {\n        \"_id\": 1,\n        \"encrypted_string\": {\n            \"$binary\": {\n                \"base64\": \"AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==\",\n                \"subType\": \"06\"\n            }\n        }\n    },\n    \"decrytped_document\": {\n        \"_id\": 1,\n        \"encrypted_string\": \"string0\"\n    }\n}"
  },
  {
    "path": "testdata/client-side-encryption-prose/corpus-encrypted.json",
    "content": "{\n  \"_id\": \"client_side_encryption_corpus\",\n  \"altname_aws\": \"aws\",\n  \"altname_local\": \"local\",\n  \"aws_double_rand_auto_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"double\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgFkgAAAAAAAAAAAAAAAAAABchrWPF5OPeuFpk4tUV325TmoNpGW+L5iPSXcLQIr319WJFIp3EDy5QiAHBfz2rThI7imU4eLXndIUrsjM0S/vg==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_double_rand_auto_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"double\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgFkgAAAAAAAAAAAAAAAAAABga5hXFiFvH/wOr0wOHSHFWRZ4pEs/UCC1XJWf46Dod3GY9Ry5j1ZyzeHueJxc4Ym5M8UHKSmJuXmNo9m9ZnkiA==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_double_rand_explicit_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"double\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgFkgAAAAAAAAAAAAAAAAAABjTYZbsro/YxLWBb88qPXEIDQdzY7UZyK4UaZZ8h62OTxp43Zp9j6WvOEzKhXt4oJPMxlAxyTdqO6MllX5bsDrw==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_double_rand_explicit_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"double\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgFkgAAAAAAAAAAAAAAAAAABqkyXdeS3aWH2tRFoKxsIIL3ZH05gkiAEbutrjrdfw0b110iPhuCCOb0gP/nX/NRNCg1kCFZ543Vu0xZ0BRXlvQ==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_double_det_explicit_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"double\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": { \"$numberDouble\": \"1.234\" }\n  },\n  \"aws_double_det_explicit_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"double\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": { \"$numberDouble\": \"1.234\" }\n  },\n  \"aws_string_rand_auto_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"string\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgFkgAAAAAAAAAAAAAAAAAACAsI5E0rVT8TpIONY3TnbRvIxUjKsiy9ynVd/fE7U1lndE7KR6dTzs8QWK13kdKxO+njKPeC2ObBX904QmJ65Sw==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_string_rand_auto_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"string\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgFkgAAAAAAAAAAAAAAAAAACgBE6J6MRxPSDe+gfJPL8nBvuEIRBYxNS/73LqBTDJYyN/lsHQ6UlFDT5B4EkIPmHPTe+UBMOhZQ1bsP+DK8Aog==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_string_rand_explicit_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"string\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgFkgAAAAAAAAAAAAAAAAAACbdTVDBWn35M5caKZgLFoiSVeFGKRj5K/QtupKNc8/dPIyCE+/a4PU51G/YIzFpYmp91nLpyq7lD/eJ/V0q66Zw==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_string_rand_explicit_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"string\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgFkgAAAAAAAAAAAAAAAAAACa4O+kE2BaqM0E+yiBrbCuE0YEGTrZ7L/+SuWm9gN3UupxwAQpRfxXAuUCTc9u1CXnvL+ga+VJMcWD2bawnn/Rg==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_string_det_auto_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"string\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AQFkgAAAAAAAAAAAAAAAAAACyvOW8NcqRkZYzujivwVmYptJkic27PWr3Nq3Yv5Njz8cJdoyesVaQan6mn+U3wdfGEH8zbUUISdCx5qgvXEpvw==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_string_det_explicit_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"string\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AQFkgAAAAAAAAAAAAAAAAAACyvOW8NcqRkZYzujivwVmYptJkic27PWr3Nq3Yv5Njz8cJdoyesVaQan6mn+U3wdfGEH8zbUUISdCx5qgvXEpvw==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_string_det_explicit_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"string\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AQFkgAAAAAAAAAAAAAAAAAACyvOW8NcqRkZYzujivwVmYptJkic27PWr3Nq3Yv5Njz8cJdoyesVaQan6mn+U3wdfGEH8zbUUISdCx5qgvXEpvw==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_object_rand_auto_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"object\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgFkgAAAAAAAAAAAAAAAAAADI+/afY6Eka8j1VNThWIeGkDZ7vo4/l66a01Z+lVUFFnVLeUV/nz9kM6uTTplNRUa+RXmNmwkoR/BHRnGc7wRNA==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_object_rand_auto_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"object\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgFkgAAAAAAAAAAAAAAAAAADzN4hVXWXKerhggRRtwWnDu2W2wQ5KIWb/X1WCZJKTjQSQ5LNHVasabBCa4U1q46PQ5pDDM1PkVjW6o+zzl/4xw==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_object_rand_explicit_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"object\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgFkgAAAAAAAAAAAAAAAAAADhSs5zKFMuuux3fqFFuPito3N+bp5TgmkUtJtFXjmA/EnLuexGARvEeGUsMJ/n0VzKbbsiE8+AsUNY3o9YXutqQ==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_object_rand_explicit_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"object\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgFkgAAAAAAAAAAAAAAAAAADpj8MSov16h26bFDrHepsNkW+tOLOjRP7oj1Tnj75qZ+uqxxVkQ5B/t/Ihk5fikHTJGAcRBR5Vv6kJ/ulMaDnvQ==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_object_det_explicit_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"object\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": { \"x\": { \"$numberInt\": \"1\" } }\n  },\n  \"aws_object_det_explicit_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"object\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": { \"x\": { \"$numberInt\": \"1\" } }\n  },\n  \"aws_array_rand_auto_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"array\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgFkgAAAAAAAAAAAAAAAAAAETWDOZ6zV39H2+W+BkwZIoxI3BNF6phKoiBZ9+i4T9uEoyU3TmoTPjuI0YNwR1v/p5/9rlVCG0KLZd16eeMb3zxZXjqh6IAJqfhsBQ7bzBYI=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_array_rand_auto_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"array\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgFkgAAAAAAAAAAAAAAAAAAE1xeHbld2JjUiPB1k+xMZuIzNSai7mv1iusCswxKEfYCZ7YtR0GDQTxN4676CwhcodSDiysjgOxSFIGlptKCvl0k46LNq0EGypP9yWBLvdjQ=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_array_rand_explicit_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"array\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgFkgAAAAAAAAAAAAAAAAAAEFVa4U2uW65MGihhdOmpZFgnwGTs3VeN5TXXbXJ5cfm0CwXF3EPlzAVjy5WO/+lbvFufpQnIiLH59/kVygmwn+2P9zPNJnSGIJW9gaV8Vye8=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_array_rand_explicit_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"array\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgFkgAAAAAAAAAAAAAAAAAAE11VXbfg7DJQ5/CB9XdBO0hCrxOkK3RrEjPGJ0FXlUo76IMna1uo+NVmDnM63CRlGE3/TEbZPpp0w0jn4vZLKvBmGr7o7WQusRY4jnRf5oH4=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_array_det_explicit_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"array\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": [\n      { \"$numberInt\": \"1\" },\n      { \"$numberInt\": \"2\" },\n      { \"$numberInt\": \"3\" }\n    ]\n  },\n  \"aws_array_det_explicit_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"array\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": [\n      { \"$numberInt\": \"1\" },\n      { \"$numberInt\": \"2\" },\n      { \"$numberInt\": \"3\" }\n    ]\n  },\n  \"aws_binData=00_rand_auto_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"binData=00\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgFkgAAAAAAAAAAAAAAAAAAFpZYSktIHzGLZ6mcBFxywICqxdurqLVJcQR34ngix5YIOOulCYEhBSDzzSEyixEPCuU6cEzeuafpZRHX4qgcr9Q==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_binData=00_rand_auto_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"binData=00\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgFkgAAAAAAAAAAAAAAAAAAFshzESR9SyR++9r2yeaEjJYScMDez414s8pZkB3C8ihDa+rsyaxNy4yrF7qNEWjFrdFaH7zD2LdlPx+TKZgROlg==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_binData=00_rand_explicit_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"binData=00\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgFkgAAAAAAAAAAAAAAAAAAFpYwZRPDom7qyAe5WW/QNSq97/OYgRT8xUEaaR5pkbQEFd/Cwtl8Aib/3Bs1CT3MVaHVWna2u5Gcc4s/v18zLhg==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_binData=00_rand_explicit_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"binData=00\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgFkgAAAAAAAAAAAAAAAAAAFBq1RIU1YGHKAS1SAtS42fKtQBHQ/BCQzRutirNdvWlrXxF81LSaS7QgQyycZ2ePiOLsSm2vZS4xaQETeCgRC4g==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_binData=00_det_auto_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"binData=00\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AQFkgAAAAAAAAAAAAAAAAAAF6SJGmfD3hLVc4tLPm4v2zFuHoRxUDLumBR8Q0AlKK2nQPyvuHEPVBD3vQdDi+Q7PwFxmovJsHccr59VnzvpJeg==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_binData=00_det_explicit_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"binData=00\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AQFkgAAAAAAAAAAAAAAAAAAF6SJGmfD3hLVc4tLPm4v2zFuHoRxUDLumBR8Q0AlKK2nQPyvuHEPVBD3vQdDi+Q7PwFxmovJsHccr59VnzvpJeg==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_binData=00_det_explicit_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"binData=00\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AQFkgAAAAAAAAAAAAAAAAAAF6SJGmfD3hLVc4tLPm4v2zFuHoRxUDLumBR8Q0AlKK2nQPyvuHEPVBD3vQdDi+Q7PwFxmovJsHccr59VnzvpJeg==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_binData=04_rand_auto_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"binData=04\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgFkgAAAAAAAAAAAAAAAAAAFM5685zqlM8pc3xubtCFuf724g/bWXsebpNzw5E5HrxUqSBBVOvjs3IJH74+Supe169qejY358nOG41mLZvO2wJByvT14qmgUGpgBaLaxPR0=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_binData=04_rand_auto_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"binData=04\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgFkgAAAAAAAAAAAAAAAAAAFfLqOzpfjz/XYHDLnliUAA5ehi6s+OIjvrLa59ubqEf8DuoCEWlO13Dl8X42IBB4hoSsO2RUeWtc9MeH4SdIUh/xJN3qS7qzjh/H+GvZRdAM=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_binData=04_rand_explicit_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"binData=04\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgFkgAAAAAAAAAAAAAAAAAAFkmKfKAbz9tqVaiM9MRhYttiY3vgDwXpdYLQ4uUgWX89KRayLADWortYL+Oq+roFhO3oiwB9vjeWGIdgbj5wSh/50JT/2Gs85TXFe1GFjfWs=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_binData=04_rand_explicit_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"binData=04\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgFkgAAAAAAAAAAAAAAAAAAFKbufv83ddN+07Q5Ocq0VxUEV+BesSrVM7Bol3cMlWjHi7P+MrdwhNEa94xlxlDwU3b+RD6kW+AuNEQ2byA3CX2JjZE1gHwN7l0ukXuqpD0A=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_binData=04_det_auto_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"binData=04\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AQFkgAAAAAAAAAAAAAAAAAAFlg7ceq9w/JMhHcNzQks6UrKYAffpUyeWuBIpcuLoB7YbFO61Dphseh77pzZbk3OvmveUq6EtCP2pmsq7hA+QV4hkv6BTn4m6wnXw6ss/qfE=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_binData=04_det_explicit_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"binData=04\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AQFkgAAAAAAAAAAAAAAAAAAFlg7ceq9w/JMhHcNzQks6UrKYAffpUyeWuBIpcuLoB7YbFO61Dphseh77pzZbk3OvmveUq6EtCP2pmsq7hA+QV4hkv6BTn4m6wnXw6ss/qfE=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_binData=04_det_explicit_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"binData=04\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AQFkgAAAAAAAAAAAAAAAAAAFlg7ceq9w/JMhHcNzQks6UrKYAffpUyeWuBIpcuLoB7YbFO61Dphseh77pzZbk3OvmveUq6EtCP2pmsq7hA+QV4hkv6BTn4m6wnXw6ss/qfE=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_undefined_rand_explicit_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"undefined\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": { \"$undefined\": true }\n  },\n  \"aws_undefined_rand_explicit_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"undefined\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": { \"$undefined\": true }\n  },\n  \"aws_undefined_det_explicit_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"undefined\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": { \"$undefined\": true }\n  },\n  \"aws_undefined_det_explicit_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"undefined\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": { \"$undefined\": true }\n  },\n  \"aws_objectId_rand_auto_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"objectId\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgFkgAAAAAAAAAAAAAAAAAAHASE+V+LlkmwgF9QNjBK8QBvC973NaTMk6wbd57VB2EpQzrgxMtR5gYzVeqq4xaaHqrncyZCOIxDJkFlaim2NqA==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_objectId_rand_auto_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"objectId\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgFkgAAAAAAAAAAAAAAAAAAHf/+9Qj/ozcDoUb8RNBnajU1d9hJ/6fE17IEZnw+ma6v5yH8LqZk9w3dtm6Sfw1unMhcMKrmIgs6kxqRWhNREJg==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_objectId_rand_explicit_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"objectId\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgFkgAAAAAAAAAAAAAAAAAAHzX8ejVLhoarQ5xgWsJitU/9eBm/Hlt2IIbZtS0SBc80qzkkWTaP9Zl9wrILH/Hwwx8RFnts855eKII3NJFa3BA==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_objectId_rand_explicit_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"objectId\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgFkgAAAAAAAAAAAAAAAAAAHG5l6nUCY8f/6xO6TsPDrZHcdPRyMe3muMlY2DxHwv9GJNDR5Ne5VEAzUjnbgoy+B29SX4oY8cXJ6XhVz8mt3Eg==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_objectId_det_auto_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"objectId\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AQFkgAAAAAAAAAAAAAAAAAAHTMY2l+gY8glm4HeSsGfCSfOsTVTzYU8qnQV8iqEFHrO5SBJac59gv3N/jukMwAnt0j6vIIQrROkVetU24YY7sQ==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_objectId_det_explicit_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"objectId\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AQFkgAAAAAAAAAAAAAAAAAAHTMY2l+gY8glm4HeSsGfCSfOsTVTzYU8qnQV8iqEFHrO5SBJac59gv3N/jukMwAnt0j6vIIQrROkVetU24YY7sQ==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_objectId_det_explicit_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"objectId\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AQFkgAAAAAAAAAAAAAAAAAAHTMY2l+gY8glm4HeSsGfCSfOsTVTzYU8qnQV8iqEFHrO5SBJac59gv3N/jukMwAnt0j6vIIQrROkVetU24YY7sQ==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_bool_rand_auto_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"bool\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgFkgAAAAAAAAAAAAAAAAAAISm4UFt1HC2j0ObpTBg7SvF2Dq31i9To2ED4F3JcTihhq0fVzaSCsUz9VTJ0ziHmeNPNdfPPZO6qA/CDEZBO4jg==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_bool_rand_auto_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"bool\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgFkgAAAAAAAAAAAAAAAAAAIj93KeAa96DmZXdB8boFvW19jhJSMmtSs5ag5FDSkH8MdKG2d2VoBOdUlBrL+LHYELqeDHCszY7qCirvb5mIgZg==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_bool_rand_explicit_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"bool\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgFkgAAAAAAAAAAAAAAAAAAIMbDFEuHIl5MNEsWnYLIand1vpK6EMv7Mso6qxrN4wHSVVwmxK+GCPgrKoUQsNuTssFWNCu0IhwrXOagDEfmlxw==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_bool_rand_explicit_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"bool\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgFkgAAAAAAAAAAAAAAAAAAIkIaWfmPdxgAV5Rtb6on6T0NGt9GPFDScQD5I/Ch0ngiTCCKceJOjU0ljd3YTgfWRA1p/MlMIV0I5YAWZXKTHlg==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_bool_det_explicit_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"bool\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": true\n  },\n  \"aws_bool_det_explicit_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"bool\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": true\n  },\n  \"aws_date_rand_auto_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"date\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgFkgAAAAAAAAAAAAAAAAAAJz1VG4+QnQXEE+TGu/pzfPugGMVTiC1xnenG1ByRdPvsERVw9WComWl1tb9tt9oblD7H/q0y1+y8HevkDqohB2Q==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_date_rand_auto_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"date\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgFkgAAAAAAAAAAAAAAAAAAJa1kI2mIIYWjf7zjf5dD9+psvAQpjZ3nnsoXA5upcIwEtZaC8bxKKHVpOLOP3rTbvT5EV6vLhXkferGoyaqd/8w==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_date_rand_explicit_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"date\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgFkgAAAAAAAAAAAAAAAAAAJ9Q5Xe4UuOLQTUwosk47A6xx40XJcNoICCNtKrHqsUYy0QLCFRc5v4nA0160BVghURizbUtX8iuIp11pnsDyRtA==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_date_rand_explicit_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"date\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgFkgAAAAAAAAAAAAAAAAAAJkHOdUc/4U82wxWJZ0SYABkJjQqNApkH2Iy/5S+PoatPgynoeSFTU9FmAbuWV/gbtIfBiaCOIjlsdonl/gf9+5w==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_date_det_auto_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"date\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AQFkgAAAAAAAAAAAAAAAAAAJEEpQNsiqMWPqD4lhMkiOJHGE8FxOeYrKPiiAp/bZTrLKyCSS0ZL1WT9H3cGzxWPm5veihCjKqWhjatC/pjtzbQ==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_date_det_explicit_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"date\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AQFkgAAAAAAAAAAAAAAAAAAJEEpQNsiqMWPqD4lhMkiOJHGE8FxOeYrKPiiAp/bZTrLKyCSS0ZL1WT9H3cGzxWPm5veihCjKqWhjatC/pjtzbQ==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_date_det_explicit_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"date\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AQFkgAAAAAAAAAAAAAAAAAAJEEpQNsiqMWPqD4lhMkiOJHGE8FxOeYrKPiiAp/bZTrLKyCSS0ZL1WT9H3cGzxWPm5veihCjKqWhjatC/pjtzbQ==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_null_rand_explicit_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"null\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": null\n  },\n  \"aws_null_rand_explicit_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"null\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": null\n  },\n  \"aws_null_det_explicit_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"null\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": null\n  },\n  \"aws_null_det_explicit_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"null\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": null\n  },\n  \"aws_regex_rand_auto_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"regex\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgFkgAAAAAAAAAAAAAAAAAALnhViSt3HqTDzyLN4mWO9srBU8TjRvPWsAJYfj/5sgI/yFuWdrggMs3Aq6G+K3tRrX3Yb+osy5CLiFCxq9WIvAA==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_regex_rand_auto_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"regex\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgFkgAAAAAAAAAAAAAAAAAALbL2RS2tGQLBZ+6LtXLKAWFKcoKui+u4+gMIlFemLgpdO2eLqrMJB53ccqZImX8ons9UgAwDkiD68hWy8e7KHfg==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_regex_rand_explicit_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"regex\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgFkgAAAAAAAAAAAAAAAAAALa0+ftF6W/0Ul4J9VT/3chXFktE1o+OK4S14h2kyOqDVNA8yMKuyCK5nWl1yZvjJ76TuhEABte23oxcBP5QwalQ==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_regex_rand_explicit_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"regex\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgFkgAAAAAAAAAAAAAAAAAALS4Yo9Fwk6OTx2CWdnObFT2L4rHngeIbdCyT4/YMJYd+jLU3mph14M1ptZZg+TBIgSPHq+BkvpRDifbMmOVr/Hg==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_regex_det_auto_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"regex\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AQFkgAAAAAAAAAAAAAAAAAALpwNlokiTCUtTa2Kx9NVGvXR/aKPGhR5iaCT7nHEk4BOiZ9Kr4cRHdPCeZ7A+gjG4cKoT62sm3Fj1FwSOl8J8aQ==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_regex_det_explicit_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"regex\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AQFkgAAAAAAAAAAAAAAAAAALpwNlokiTCUtTa2Kx9NVGvXR/aKPGhR5iaCT7nHEk4BOiZ9Kr4cRHdPCeZ7A+gjG4cKoT62sm3Fj1FwSOl8J8aQ==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_regex_det_explicit_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"regex\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AQFkgAAAAAAAAAAAAAAAAAALpwNlokiTCUtTa2Kx9NVGvXR/aKPGhR5iaCT7nHEk4BOiZ9Kr4cRHdPCeZ7A+gjG4cKoT62sm3Fj1FwSOl8J8aQ==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_dbPointer_rand_auto_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"dbPointer\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgFkgAAAAAAAAAAAAAAAAAAMfCVAnMNbRGsThnoVGb2KDsCIU2ehcPtebk/TFG4GZvEmculscLLih813lEz5NHS2sAXBn721EzUS7d0TKAPbmEYFwUBnijIQIPvUoUO8AQM=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_dbPointer_rand_auto_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"dbPointer\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgFkgAAAAAAAAAAAAAAAAAAMvYJ5BtaMLVXV+qj85q5WqKRlzlHOBIIxZfUE/BBXUwqSTpJLdQQD++DDh6F2dtorBeYa3oUv2ef3ImASk5j23joU35Pm3Zt9Ci1pMNGodWs=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_dbPointer_rand_explicit_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"dbPointer\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgFkgAAAAAAAAAAAAAAAAAAMdsmYtPDw8kKjfB2kWfx5W1oNEkWWct1lRpesN303pUWsawDJpfBx40lg18So2X/g4yGIwpY3qfEKQZA4vCJeT+MTjhRXFjXA7eS/mxv8f3E=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_dbPointer_rand_explicit_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"dbPointer\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgFkgAAAAAAAAAAAAAAAAAAM0hcvS5zmY3mlTp0SfME/rINlflF/sx2KvP0eJTdH+Uk0WHuTkFIJAza+bXvV/gB7iNC350qyzUX3M6NHx/9s/5yBpY8MawTZTZ7WCQIA+ZI=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_dbPointer_det_auto_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"dbPointer\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AQFkgAAAAAAAAAAAAAAAAAAMp4QxbaEOij66L+RtaMekrDSm6QbfJBTQ8lQFhxfq9n7SVuQ9Zwdy14Ja8tyI3cGgQzQ/73rHUJ3CKA4+OYr63skYUkkkdlHxUrIMd5j5woc=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_dbPointer_det_explicit_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"dbPointer\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AQFkgAAAAAAAAAAAAAAAAAAMp4QxbaEOij66L+RtaMekrDSm6QbfJBTQ8lQFhxfq9n7SVuQ9Zwdy14Ja8tyI3cGgQzQ/73rHUJ3CKA4+OYr63skYUkkkdlHxUrIMd5j5woc=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_dbPointer_det_explicit_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"dbPointer\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AQFkgAAAAAAAAAAAAAAAAAAMp4QxbaEOij66L+RtaMekrDSm6QbfJBTQ8lQFhxfq9n7SVuQ9Zwdy14Ja8tyI3cGgQzQ/73rHUJ3CKA4+OYr63skYUkkkdlHxUrIMd5j5woc=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_javascript_rand_auto_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"javascript\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgFkgAAAAAAAAAAAAAAAAAAN3HzAC9BTD7Jgi0PR4RS/Z6L6QtAQ7VhbKRbX+1smmnYniH6jVBM6zyxMDM8h9YjMPNs8EJrGDnisuf33w5KI/A==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_javascript_rand_auto_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"javascript\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgFkgAAAAAAAAAAAAAAAAAANJpw+znlu3ecSiNyZ0EerVsow4aDRF2auI3Wy69EVexJkQlHO753PjRn8hG/x2kY8ROy5IUU43jaugP5AN1bwNQ==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_javascript_rand_explicit_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"javascript\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgFkgAAAAAAAAAAAAAAAAAANzoDiq8uI0+l8COY8YdM9S3rpLvPOHOWmJqJNtOyS0ZXUx1SB5paRJ4W3Eg8KuXEeoFwvBDe9cW9YT66CzkjlBw==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_javascript_rand_explicit_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"javascript\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgFkgAAAAAAAAAAAAAAAAAAN/JhtRongJweLC5SdrXHhsFz3p82q3cwXf8Sru21DK6S39S997y3uhVLn0xlX5d94PxK1XVYSjz1oVuMxZouZ7Q==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_javascript_det_auto_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"javascript\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AQFkgAAAAAAAAAAAAAAAAAANE39aEGiuUZ1WyakVEBgkGzLp5whkIjJ4uiaFLXniRszJL70FRkcf+aFXlA5Y4So9/ODKF76qbSsH4Jk6L+3mog==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_javascript_det_explicit_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"javascript\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AQFkgAAAAAAAAAAAAAAAAAANE39aEGiuUZ1WyakVEBgkGzLp5whkIjJ4uiaFLXniRszJL70FRkcf+aFXlA5Y4So9/ODKF76qbSsH4Jk6L+3mog==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_javascript_det_explicit_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"javascript\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AQFkgAAAAAAAAAAAAAAAAAANE39aEGiuUZ1WyakVEBgkGzLp5whkIjJ4uiaFLXniRszJL70FRkcf+aFXlA5Y4So9/ODKF76qbSsH4Jk6L+3mog==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_symbol_rand_auto_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"symbol\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgFkgAAAAAAAAAAAAAAAAAAOBv1T9tleM0xwNe7efg/MlShyzvXe3Pmg1GzPl3gjFRHZGWXR578KqX+8oiz65eXGzNuyOFvcpnR2gYCs3NeKeQfctO5plEiIva6nzCI5SK8=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_symbol_rand_auto_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"symbol\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgFkgAAAAAAAAAAAAAAAAAAOwLgGws8CMh+GgkEJFAx8tDIflyjsgG+/1FmZZobKAg8NOKqfXjtbnNCbvR28OCk6g/8SqBm8m53G6JciwvthJ0DirdfEexiUqu7IPtaeeyw=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_symbol_rand_explicit_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"symbol\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgFkgAAAAAAAAAAAAAAAAAAORQi3dNkXzZeruWu19kEhDu6fFD/h47ILzk+OVKQMoriAQC5YFyVRp1yAkIaWsrsPcyCHlfZ99FySSQeqSYbZZNj5FqyonWvDuPTduHDy3CI=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_symbol_rand_explicit_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"symbol\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgFkgAAAAAAAAAAAAAAAAAAOj+Yl1pQPiJ6mESOISOyUYsKN/VIvC8f0derhxIPakXkwn57U0sxv+geUkrl3JZDxY3+cX5M1JZmY+PfjaYQhbTorf9RZaVC2Wwo2lMftWi0=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_symbol_det_auto_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"symbol\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AQFkgAAAAAAAAAAAAAAAAAAO5IHripygBGEsVK8RFWZ1rIIVUap8KVDuqOspZpERaj+5ZEfqIcyrP/WK9KdvwOfdOWXfP/mOwuImYgNdbaQe+ejkYe4W0Y0uneCuw88k95Q=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_symbol_det_explicit_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"symbol\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AQFkgAAAAAAAAAAAAAAAAAAO5IHripygBGEsVK8RFWZ1rIIVUap8KVDuqOspZpERaj+5ZEfqIcyrP/WK9KdvwOfdOWXfP/mOwuImYgNdbaQe+ejkYe4W0Y0uneCuw88k95Q=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_symbol_det_explicit_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"symbol\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AQFkgAAAAAAAAAAAAAAAAAAO5IHripygBGEsVK8RFWZ1rIIVUap8KVDuqOspZpERaj+5ZEfqIcyrP/WK9KdvwOfdOWXfP/mOwuImYgNdbaQe+ejkYe4W0Y0uneCuw88k95Q=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_javascriptWithScope_rand_auto_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"javascriptWithScope\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgFkgAAAAAAAAAAAAAAAAAAPT31GSNkY1RM43miv1XPYtDX1vU/xORiM3U0pumjqA+JLU/HMhH++75OcMhcAQqMjm2nZtZScxdGJsJJPEEzqjbFNMJgYc9sqR5uLnzk+2dg=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_javascriptWithScope_rand_auto_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"javascriptWithScope\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgFkgAAAAAAAAAAAAAAAAAAPUxgaKAxSQ1uzOZtzsbtrxtDT2P/zWY6lYsbChXuRUooqvyjXSkNDqKBBA7Gp5BdGiVB/JLR47Tihpbcw1s1yGhwQRvnqeDvPrf91nvElXRY=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_javascriptWithScope_rand_explicit_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"javascriptWithScope\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgFkgAAAAAAAAAAAAAAAAAAPv8W0ZtquFCLTG0TqvRjdzKa/4mvqT2FuEGQ0mXG2k2BZh2LY5APr/kgW0tP4eLjHzVld6OLiM9ZKAvENCZ6/fKOvqSwpIfkdLWUIeB4REQg=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_javascriptWithScope_rand_explicit_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"javascriptWithScope\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgFkgAAAAAAAAAAAAAAAAAAPMVhWjaxLffdAOkVgIJpjgNIldMS451NQs3C1jb+pzopHp3DlfZ+AHQpK9reMVVKjaqanhWBpL25q+feA60XVgZPCUDroiRYqMFqU//y0amw=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_javascriptWithScope_det_explicit_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"javascriptWithScope\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": { \"$code\": \"x=1\", \"$scope\": {} }\n  },\n  \"aws_javascriptWithScope_det_explicit_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"javascriptWithScope\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": { \"$code\": \"x=1\", \"$scope\": {} }\n  },\n  \"aws_int_rand_auto_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"int\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgFkgAAAAAAAAAAAAAAAAAAQFV5b3vsoZe+MT4z8soetpmrWJpm7be41FNu/rdEqHWTG32jCym6762PCNYH5+vA7ldCWQkdt+ncneHsxzPrm9w==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_int_rand_auto_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"int\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgFkgAAAAAAAAAAAAAAAAAAQY9+QenvU1Tk/dEGZP11uOZJLHAJ9hWHbEhxbtxItt1LsdU/8gOZfypilIO5BUkLT/15PUuXV28GISNh6yIuWhw==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_int_rand_explicit_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"int\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgFkgAAAAAAAAAAAAAAAAAAQruCugbneumhcinuXm89WW1PXVuSOewttp9cpsPPsCRVqe/uAkZOdJnZ2KaEZ9zki2GeqaJTs1qDmaJofc6GMEA==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_int_rand_explicit_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"int\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgFkgAAAAAAAAAAAAAAAAAAQb15qXl/tejk4pmgkc4pUxzt4eJrv/cetgzgcPVaROAQSzd8ptbgCjaV8vP46uqozRoaDFZbQ06t65c3f0x/Ucw==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_int_det_auto_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"int\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AQFkgAAAAAAAAAAAAAAAAAAQCXo6ieWvfoqkG+rP7J2BV013AVf/oNMmmGWe44VEHahF+qZHzW5I/F2qIA+xgKkk172pFq0iTSOpe+K2WHMKFw==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_int_det_explicit_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"int\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AQFkgAAAAAAAAAAAAAAAAAAQCXo6ieWvfoqkG+rP7J2BV013AVf/oNMmmGWe44VEHahF+qZHzW5I/F2qIA+xgKkk172pFq0iTSOpe+K2WHMKFw==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_int_det_explicit_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"int\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AQFkgAAAAAAAAAAAAAAAAAAQCXo6ieWvfoqkG+rP7J2BV013AVf/oNMmmGWe44VEHahF+qZHzW5I/F2qIA+xgKkk172pFq0iTSOpe+K2WHMKFw==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_timestamp_rand_auto_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"timestamp\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgFkgAAAAAAAAAAAAAAAAAAR63xXG8mrlixkQzD5VBIPE6NHicaWcS5CBhiIJDcZ0x8D9c5TgRJUfCeWhKvWFD4o0DoxcBQ2opPormFDpvmq/g==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_timestamp_rand_auto_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"timestamp\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgFkgAAAAAAAAAAAAAAAAAARAgY9LsUxP6gP4gYRvvzZ4iaHVQRNbycATiVag1YNSiDmEr4LYserYuBscdrIy4v3zgGaulFM9KV86bx0ItycZA==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_timestamp_rand_explicit_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"timestamp\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgFkgAAAAAAAAAAAAAAAAAARLneAZqPcHdzGGnXz2Ne5E7HP9cDC1+yoIwcA8OSF/IlzEjrrMAi3z6Izol6gWDlD7VOh7QYL3sASJOXyzF1hPQ==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_timestamp_rand_explicit_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"timestamp\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgFkgAAAAAAAAAAAAAAAAAARH2bU7KNo5SHxiO8JFEcT9wryuHNXyM7ADop1oPcESyay1Nc0WHPD3nr0yMAK481NxOkE3qXyaslu7bcP/744WA==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_timestamp_det_auto_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"timestamp\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AQFkgAAAAAAAAAAAAAAAAAARG7kGfx0ky+d4Hl/fRBu8oUR1Mph26Dkv3J7fxGYanpzOFMiHIfVO0uwYMvsfzG54y0DDNlS3FmmS13DzepbzGQ==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_timestamp_det_explicit_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"timestamp\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AQFkgAAAAAAAAAAAAAAAAAARG7kGfx0ky+d4Hl/fRBu8oUR1Mph26Dkv3J7fxGYanpzOFMiHIfVO0uwYMvsfzG54y0DDNlS3FmmS13DzepbzGQ==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_timestamp_det_explicit_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"timestamp\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AQFkgAAAAAAAAAAAAAAAAAARG7kGfx0ky+d4Hl/fRBu8oUR1Mph26Dkv3J7fxGYanpzOFMiHIfVO0uwYMvsfzG54y0DDNlS3FmmS13DzepbzGQ==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_long_rand_auto_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"long\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgFkgAAAAAAAAAAAAAAAAAASZbes2EdR78crt2pXVElW2YwAQh8HEBapYYeav2VQeg2syXaV/qZuD8ofnAVn4v/DydTTMVMmK+sVU/TlnAu2eA==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_long_rand_auto_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"long\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgFkgAAAAAAAAAAAAAAAAAASt+7fmMYH+fLHgybc+sng8/UmKP3YPUEPCz1SXVQljQp6orsCILSgtgGPsdeGnN5NSxh3XzerHs6zlR92fWpZCw==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_long_rand_explicit_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"long\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgFkgAAAAAAAAAAAAAAAAAAS01fF1uo6zYDToJnOT/EbDipzk7YZ6I+IspZF+avjU3XYfpRxT9NdAgKr0euWJwyAsdpWqqCwFummfrPeZOy04A==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_long_rand_explicit_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"long\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgFkgAAAAAAAAAAAAAAAAAAS6tpH796bqy58mXf38rJvVtA1uBcxBE5yIGQ4RN44oypc/pvw0ouhFI1dkoneKMtAFU/5RygZV+RvQhRtgKn76A==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_long_det_auto_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"long\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AQFkgAAAAAAAAAAAAAAAAAASC7O/8JeB4WTqQFPuMpFRsAuonPS3yu7IAPZeRPIr03CmM6HNndYIKMoFM13eELNZTdJSgg9u9ItGqRw+/XMHzQ==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_long_det_explicit_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"long\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AQFkgAAAAAAAAAAAAAAAAAASC7O/8JeB4WTqQFPuMpFRsAuonPS3yu7IAPZeRPIr03CmM6HNndYIKMoFM13eELNZTdJSgg9u9ItGqRw+/XMHzQ==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_long_det_explicit_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"long\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AQFkgAAAAAAAAAAAAAAAAAASC7O/8JeB4WTqQFPuMpFRsAuonPS3yu7IAPZeRPIr03CmM6HNndYIKMoFM13eELNZTdJSgg9u9ItGqRw+/XMHzQ==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_decimal_rand_auto_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"decimal\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgFkgAAAAAAAAAAAAAAAAAATgf5zW9EgnWHPxj4HAGt472eN9UXP41TaF8V2J7S2zqSpiBZGKDuOIjw2FBSqaNp53vvfl9HpwAuQBJZhrwkBCKRkKV/AAR3/pTpuoqhSKaM=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_decimal_rand_auto_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"decimal\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgFkgAAAAAAAAAAAAAAAAAATPRfvZWdupE2N0W1DXUx7X8Zz7g43jawJL7PbQtTYetI78xRETkMdygwSEHgs+cvnUBBtYIeKRVkOGZQkwf568OclhDiPxUeD38cR5blBq/U=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_decimal_rand_explicit_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"decimal\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgFkgAAAAAAAAAAAAAAAAAAT+ZnCg2lSMIohZ9RJ4CNs3LZ0g+nV04cYAmrxTSrTSBPGlZ7Ywh5A2rCss7AUijYZiKiYyZbuAzukbOuVRhdCtm+xo9+DyLAwTezF18okk6Y=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_decimal_rand_explicit_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"decimal\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgFkgAAAAAAAAAAAAAAAAAATlnQYASsTZRRHzFjcbCClXartcXBVRrYv7JImMkDmAj6EAjf/ZqpjeykkS/wohMhXaNwyZBdREr+n+GDV7imYoL4WRBOLnqB6hrYidlWqNzE=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"aws_decimal_det_explicit_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"decimal\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": { \"$numberDecimal\": \"1.234\" }\n  },\n  \"aws_decimal_det_explicit_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"decimal\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": { \"$numberDecimal\": \"1.234\" }\n  },\n  \"aws_minKey_rand_explicit_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"minKey\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": { \"$minKey\": 1 }\n  },\n  \"aws_minKey_rand_explicit_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"minKey\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": { \"$minKey\": 1 }\n  },\n  \"aws_minKey_det_explicit_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"minKey\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": { \"$minKey\": 1 }\n  },\n  \"aws_minKey_det_explicit_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"minKey\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": { \"$minKey\": 1 }\n  },\n  \"aws_maxKey_rand_explicit_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"maxKey\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": { \"$maxKey\": 1 }\n  },\n  \"aws_maxKey_rand_explicit_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"maxKey\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": { \"$maxKey\": 1 }\n  },\n  \"aws_maxKey_det_explicit_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"maxKey\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": { \"$maxKey\": 1 }\n  },\n  \"aws_maxKey_det_explicit_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"maxKey\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": { \"$maxKey\": 1 }\n  },\n  \"local_double_rand_auto_id\": {\n    \"kms\": \"local\",\n    \"type\": \"double\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AizggCwAAAAAAAAAAAAAAAABGF195CB8nRmK9+KxYO7T96MeXucC/ILQtEEQAS4zrwj3Qz7YEQrf/apvbKTCkn3siN2XSDLQ/7dmddZa9xa9yQ==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_double_rand_auto_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"double\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AizggCwAAAAAAAAAAAAAAAABY8g18z6ZOjGtfNxaAmU95tXMdoM6qbtDMpB72paqiHZTW1UGB22HPXiEnVz05JTBzzX4fc6tOldX6aJel812Zg==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_double_rand_explicit_id\": {\n    \"kms\": \"local\",\n    \"type\": \"double\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AizggCwAAAAAAAAAAAAAAAABDlHwN8hYyScEhhx64TdJ2Qp2rmKRg8983zdqIL1914tyPwRQq7ySCOhmFif2S7v4KT+r0uOfimYvKD1n9rKHlg==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_double_rand_explicit_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"double\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AizggCwAAAAAAAAAAAAAAAAB2VnTFlaCRzAZZTQiMWQORFNgXIuAJlHJXIHiYow2eO6JbVghWTpH+MsdafBNPVnc0zKuZBL0Qs2Nuk1xiQaqhA==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_double_det_explicit_id\": {\n    \"kms\": \"local\",\n    \"type\": \"double\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": { \"$numberDouble\": \"1.234\" }\n  },\n  \"local_double_det_explicit_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"double\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": { \"$numberDouble\": \"1.234\" }\n  },\n  \"local_string_rand_auto_id\": {\n    \"kms\": \"local\",\n    \"type\": \"string\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AizggCwAAAAAAAAAAAAAAAAC5NBAPM8q2n9fnkwQfE9so/XcO51plPBNs5VlBRbDw68k9T6/uZ2TWsAvTYtVooY59zHHr2QS3usKbGQB6J61rA==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_string_rand_auto_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"string\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AizggCwAAAAAAAAAAAAAAAACM/EjGMrkYHvSZra26m74upuvLkfKXTs+tTWquGzrgWYLnLt8I6XBIwx1VymS9EybrCU/ewmtgjLUNUFQacIeXA==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_string_rand_explicit_id\": {\n    \"kms\": \"local\",\n    \"type\": \"string\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AizggCwAAAAAAAAAAAAAAAACn4tD26UG8lO9gTZaxen6yXzHo/a2lokeY1ClxHMtJODoJr2JZzIDHP3A9aZ8L4+Vu+nyqphaWyGaGONKu8gpcQ==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_string_rand_explicit_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"string\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AizggCwAAAAAAAAAAAAAAAACZfoO2LjY+IB31FZ1Tq7pHr0DCFKGJqWcXcOrnZ7bV9Euc9f101motJc31sp8nF5CTCfd83VQE0319eQrxDDaSw==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_string_det_auto_id\": {\n    \"kms\": \"local\",\n    \"type\": \"string\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"ASzggCwAAAAAAAAAAAAAAAACW0cZMYWOY3eoqQQkSdBtS9iHC4CSQA27dy6XJGcmTV8EDuhGNnPmbx0EKFTDb0PCSyCjMyuE4nsgmNYgjTaSuw==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_string_det_explicit_id\": {\n    \"kms\": \"local\",\n    \"type\": \"string\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"ASzggCwAAAAAAAAAAAAAAAACW0cZMYWOY3eoqQQkSdBtS9iHC4CSQA27dy6XJGcmTV8EDuhGNnPmbx0EKFTDb0PCSyCjMyuE4nsgmNYgjTaSuw==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_string_det_explicit_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"string\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"ASzggCwAAAAAAAAAAAAAAAACW0cZMYWOY3eoqQQkSdBtS9iHC4CSQA27dy6XJGcmTV8EDuhGNnPmbx0EKFTDb0PCSyCjMyuE4nsgmNYgjTaSuw==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_object_rand_auto_id\": {\n    \"kms\": \"local\",\n    \"type\": \"object\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AizggCwAAAAAAAAAAAAAAAADlekcUsETAkkKTjCVx5EISJN+sftrQax/VhaWXLyRgRz97adXXmwZkMyt+035SHZsF91i2LaXziMA4RHoP+nKFw==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_object_rand_auto_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"object\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AizggCwAAAAAAAAAAAAAAAADpaQmy5r6q9gLqEm+FIi/OyQgcuUnrICCP9rC4S3wR6qUHd82IW/3dFQUzwTkaXxgStjopamQMuZ4ESRj0xx0bA==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_object_rand_explicit_id\": {\n    \"kms\": \"local\",\n    \"type\": \"object\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AizggCwAAAAAAAAAAAAAAAADCHRJCINzWY0u4gZPWEmHg/JoQ8IW4yMfUyzYJCQrEMp4rUeupIuxqSuq2QyLBYZBBv0r7t3lNH49I5qDeav2vA==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_object_rand_explicit_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"object\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AizggCwAAAAAAAAAAAAAAAADrHQQUnLF1jdNmFY/V266cS28XAB4nOKetHAcSbwkeUxNzgZT1g+XMQaYfcNMMv/ywypKU1KpgLMsEOpm4qcPkQ==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_object_det_explicit_id\": {\n    \"kms\": \"local\",\n    \"type\": \"object\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": { \"x\": { \"$numberInt\": \"1\" } }\n  },\n  \"local_object_det_explicit_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"object\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": { \"x\": { \"$numberInt\": \"1\" } }\n  },\n  \"local_array_rand_auto_id\": {\n    \"kms\": \"local\",\n    \"type\": \"array\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AizggCwAAAAAAAAAAAAAAAAEXa7bQ5vGPNsLdklM/H+sop8aCL4vlDiVUoVjTAGjTngn2WLcdKLWxaNSyMdJpsI/NsxQJ58YrcwP+yHzi9rZVtRdbg7m8p+CYcq1vUm6UoQ=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_array_rand_auto_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"array\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AizggCwAAAAAAAAAAAAAAAAEVlZlOvtRmGIhcYi/qPl3HKi/qf0yRQrkbVo9rScYkxDCBN9wA55pAWHDQ/5Sjy4d0DwL57k+M1G9e7xSIrv8xXKwoIuuabhSWaIX2eJHroY=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_array_rand_explicit_id\": {\n    \"kms\": \"local\",\n    \"type\": \"array\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AizggCwAAAAAAAAAAAAAAAAEYBLSYHHt2rohezMF4lMjNdqy9CY33EHf+pgRbJwVXZScLDgn9CcqeRsdU8bW5h2qgNpQvoSMBB7pW+Dgp1RauTHZSOd4PcZpAGjwoFDWSSM=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_array_rand_explicit_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"array\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AizggCwAAAAAAAAAAAAAAAAES1IJ8S2NxWekolQockxLJvzFSGfKQ9Xbi55vO8LyWo0sIG9ZgPQXtVQkZ301CsdFduvx9A0vDqQ0MGYc4plxNnpUTizJPRUDyez5dOgZ9tI=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_array_det_explicit_id\": {\n    \"kms\": \"local\",\n    \"type\": \"array\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": [\n      { \"$numberInt\": \"1\" },\n      { \"$numberInt\": \"2\" },\n      { \"$numberInt\": \"3\" }\n    ]\n  },\n  \"local_array_det_explicit_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"array\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": [\n      { \"$numberInt\": \"1\" },\n      { \"$numberInt\": \"2\" },\n      { \"$numberInt\": \"3\" }\n    ]\n  },\n  \"local_binData=00_rand_auto_id\": {\n    \"kms\": \"local\",\n    \"type\": \"binData=00\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AizggCwAAAAAAAAAAAAAAAAF+hgWs4ZCo9GnmhSM9SDSWzWX4E7Tlp4TwlEy3zfO/rrMREECGB4u8LD8Ju9b8YP+xcZhMI1tcz/vrQS87NffUg==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_binData=00_rand_auto_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"binData=00\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AizggCwAAAAAAAAAAAAAAAAFtEvaXWpGfXC1GlKu0AeRDaeBKHryGoS0tAUr48vfYk7umCr+fJKyXCY9vSv7wCiQxWLe8V/EZWkHsu0zqhJw9w==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_binData=00_rand_explicit_id\": {\n    \"kms\": \"local\",\n    \"type\": \"binData=00\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AizggCwAAAAAAAAAAAAAAAAF/1L5bvmMX3Bk2nAw8KvvRd/7nZ82XHVasT0jrlPhSiJU7ehJMeUCOb7HCHU6KgCzZB9C2W3NoVhLKIhE9ZnYdg==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_binData=00_rand_explicit_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"binData=00\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AizggCwAAAAAAAAAAAAAAAAFK0W5IWKzggR4UU+fhwA2p8YCHLfmx5y1OEtHc/9be9eEYTORACDmWY6207Vd4LhBJCedd+Q5qMm7NRZjjhyLEQ==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_binData=00_det_auto_id\": {\n    \"kms\": \"local\",\n    \"type\": \"binData=00\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"ASzggCwAAAAAAAAAAAAAAAAF1ofBnK9+ERP29P/i14GQ/y3muic6tNKY532zCkzQkJSktYCOeXS8DdY1DdaOP/asZWzPTdgwby6/iZcAxJU+xQ==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_binData=00_det_explicit_id\": {\n    \"kms\": \"local\",\n    \"type\": \"binData=00\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"ASzggCwAAAAAAAAAAAAAAAAF1ofBnK9+ERP29P/i14GQ/y3muic6tNKY532zCkzQkJSktYCOeXS8DdY1DdaOP/asZWzPTdgwby6/iZcAxJU+xQ==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_binData=00_det_explicit_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"binData=00\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"ASzggCwAAAAAAAAAAAAAAAAF1ofBnK9+ERP29P/i14GQ/y3muic6tNKY532zCkzQkJSktYCOeXS8DdY1DdaOP/asZWzPTdgwby6/iZcAxJU+xQ==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_binData=04_rand_auto_id\": {\n    \"kms\": \"local\",\n    \"type\": \"binData=04\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AizggCwAAAAAAAAAAAAAAAAFxq38aA4k/tYHPwJFRK0pahlo/3zjCe3VHJRqURRA+04lbJCvdkQTawxWlf8o+3Pcetl1UcPTQigdYp5KbIkstuPstLbT+TZXHVD1os9LTRw=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_binData=04_rand_auto_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"binData=04\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AizggCwAAAAAAAAAAAAAAAAFTXNWchCPmCSY0+AL22/kCBmAoDJDX5T18jpJHLdvZtHs0zwD64b9hLvfRK268BlNu4P37KDFE6LT0QzjG7brqzFJf3ZaadDCKeIw1q7DWQs=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_binData=04_rand_explicit_id\": {\n    \"kms\": \"local\",\n    \"type\": \"binData=04\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AizggCwAAAAAAAAAAAAAAAAF7XgMgKjQmWYWmobrYWKiGYCKsy5kTgVweFBuzvFISaZjFsq2hrZB2DwUaOeT6XUPH/Onrdjc3fNElf3FdQDHif4rt+1lh9jEX+nMbRw9i3s=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_binData=04_rand_explicit_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"binData=04\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AizggCwAAAAAAAAAAAAAAAAFGoA/1H0waFLor6LbkUCLC2Wm9j/ZT7yifPbf0G7WvO0+gBLlffr3aJIQ9ik5vxPbmDDMCoYlbEYgb8i9I5tKC17WPhjVH2N2+4l9y7aEmS4=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_binData=04_det_auto_id\": {\n    \"kms\": \"local\",\n    \"type\": \"binData=04\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"ASzggCwAAAAAAAAAAAAAAAAFwO3hsD8ee/uwgUiHWem8fGe54LsTJWqgbRCacIe6sxrsyLT6EsVIqg4Sn7Ou+FC3WJbFld5kx8euLe/MHa8FGYjxD97z5j+rUx5tt3T6YbA=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_binData=04_det_explicit_id\": {\n    \"kms\": \"local\",\n    \"type\": \"binData=04\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"ASzggCwAAAAAAAAAAAAAAAAFwO3hsD8ee/uwgUiHWem8fGe54LsTJWqgbRCacIe6sxrsyLT6EsVIqg4Sn7Ou+FC3WJbFld5kx8euLe/MHa8FGYjxD97z5j+rUx5tt3T6YbA=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_binData=04_det_explicit_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"binData=04\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"ASzggCwAAAAAAAAAAAAAAAAFwO3hsD8ee/uwgUiHWem8fGe54LsTJWqgbRCacIe6sxrsyLT6EsVIqg4Sn7Ou+FC3WJbFld5kx8euLe/MHa8FGYjxD97z5j+rUx5tt3T6YbA=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_undefined_rand_explicit_id\": {\n    \"kms\": \"local\",\n    \"type\": \"undefined\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": { \"$undefined\": true }\n  },\n  \"local_undefined_rand_explicit_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"undefined\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": { \"$undefined\": true }\n  },\n  \"local_undefined_det_explicit_id\": {\n    \"kms\": \"local\",\n    \"type\": \"undefined\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": { \"$undefined\": true }\n  },\n  \"local_undefined_det_explicit_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"undefined\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": { \"$undefined\": true }\n  },\n  \"local_objectId_rand_auto_id\": {\n    \"kms\": \"local\",\n    \"type\": \"objectId\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AizggCwAAAAAAAAAAAAAAAAHfvxWRZOzfao3faE3RglL0IcDpBcNwqiGL5KgSokmRxWjjWeiel88Mbo5Plo0SswwNQ2H7C5GVG21L+UbvcW63g==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_objectId_rand_auto_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"objectId\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AizggCwAAAAAAAAAAAAAAAAHhd9lSOO7bHE7PM+Uxa2v3X1FF66IwyEr0wqnyTaOM+cHQLmec/RlEaRIQ1x2AiW7LwmmVgZ0xBMK9CMh0Lhbyw==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_objectId_rand_explicit_id\": {\n    \"kms\": \"local\",\n    \"type\": \"objectId\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AizggCwAAAAAAAAAAAAAAAAHETwT9bo+JtboBVW/8GzzMQCpn22iiNJnlxYfyO45jvYJQRs29RRIouCsnFkmC7cfAO3GlVxv113euYjIO7AlAg==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_objectId_rand_explicit_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"objectId\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AizggCwAAAAAAAAAAAAAAAAHhsguAMBzQUFBAitpJDzKEaMDGUGfvCzmUUhf4rnp8xeall/p91TUudaSMcU11XEgJ0Mym4IbYRd8+TfUai0nvw==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_objectId_det_auto_id\": {\n    \"kms\": \"local\",\n    \"type\": \"objectId\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"ASzggCwAAAAAAAAAAAAAAAAH4ElF4AvQ+kkGfhadgKNy3GcYrDZPN6RpzaMYIhcCGDvC9W+cIS9dH1aJbPU7vTPmEZnnynPTDWjw3rAj2+9mOA==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_objectId_det_explicit_id\": {\n    \"kms\": \"local\",\n    \"type\": \"objectId\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"ASzggCwAAAAAAAAAAAAAAAAH4ElF4AvQ+kkGfhadgKNy3GcYrDZPN6RpzaMYIhcCGDvC9W+cIS9dH1aJbPU7vTPmEZnnynPTDWjw3rAj2+9mOA==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_objectId_det_explicit_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"objectId\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"ASzggCwAAAAAAAAAAAAAAAAH4ElF4AvQ+kkGfhadgKNy3GcYrDZPN6RpzaMYIhcCGDvC9W+cIS9dH1aJbPU7vTPmEZnnynPTDWjw3rAj2+9mOA==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_bool_rand_auto_id\": {\n    \"kms\": \"local\",\n    \"type\": \"bool\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AizggCwAAAAAAAAAAAAAAAAIxGld4J/2vSWg5tjQulpkm9C6WeUcLbv2yfKRXPAbmLpv3u4Yrmr5qisJtqmDPTcb993WosvCYAh0UGW+zpsdEg==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_bool_rand_auto_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"bool\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AizggCwAAAAAAAAAAAAAAAAIpUFPiS2uoW1Aqs0WQkBa201OBmsuJ8WUKcv5aBPASkcwfaw9qSWs3QrbEDR2GyoU4SeYOByCAQMzXCPoIYAFdQ==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_bool_rand_explicit_id\": {\n    \"kms\": \"local\",\n    \"type\": \"bool\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AizggCwAAAAAAAAAAAAAAAAIJuzu1a60meYlU3LMjw/7G4Vh/lqKopxdpGWoLXEmY/NoHgX6Fkv9iTwxv/Nv8rZwtawpFV+mQUG/6A1IHMBASQ==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_bool_rand_explicit_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"bool\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AizggCwAAAAAAAAAAAAAAAAIn9VjxL5TdGgJLckNHRrIaL32L31q5OERRZG2M5OYKk66TnrlfEs+ykcDvGwMGKpr/PYjY5kBHDc/oELGJJbWRQ==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_bool_det_explicit_id\": {\n    \"kms\": \"local\",\n    \"type\": \"bool\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": true\n  },\n  \"local_bool_det_explicit_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"bool\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": true\n  },\n  \"local_date_rand_auto_id\": {\n    \"kms\": \"local\",\n    \"type\": \"date\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AizggCwAAAAAAAAAAAAAAAAJPPv4MC5xzt2uxPGBHH9g2z03o9SQjjmuxt97Ub1UcKCCHsGED3bx6YSrocuEMiFFI4d5Fqgl8HNeS4j0PR0tYA==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_date_rand_auto_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"date\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AizggCwAAAAAAAAAAAAAAAAJ6i2A9Hi4xWlOMjFMGpwaRctR1VFnb4El166n18RvjKic46V+WoadvLHS32RhPOvkLVYwIeU4C+vrO5isBNoUdw==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_date_rand_explicit_id\": {\n    \"kms\": \"local\",\n    \"type\": \"date\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AizggCwAAAAAAAAAAAAAAAAJHcniV7Q0C8ZTWrE0hp5i5bUPlrrRdNLZckfODw8XNVtVPDjbznglccQmI7w1t8kOVp65eKzVzUOXN0YkqA+1QA==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_date_rand_explicit_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"date\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AizggCwAAAAAAAAAAAAAAAAJKCUCjC3hsmEKKYwGP3ceh3zR+ArE8LYFOQfN87aEsTr60VrzHXmsE8PvizRhhMnrp07ljzQkuat39L+0QSR2qQ==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_date_det_auto_id\": {\n    \"kms\": \"local\",\n    \"type\": \"date\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"ASzggCwAAAAAAAAAAAAAAAAJ1GMYQTruoKr6fv9XCbcVkx/3yivymPSMEkPCRDYxQv45w4TqBKMDfpRd1TOLOv1qvcb+gjH+z5IfVBMp2IpG/Q==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_date_det_explicit_id\": {\n    \"kms\": \"local\",\n    \"type\": \"date\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"ASzggCwAAAAAAAAAAAAAAAAJ1GMYQTruoKr6fv9XCbcVkx/3yivymPSMEkPCRDYxQv45w4TqBKMDfpRd1TOLOv1qvcb+gjH+z5IfVBMp2IpG/Q==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_date_det_explicit_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"date\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"ASzggCwAAAAAAAAAAAAAAAAJ1GMYQTruoKr6fv9XCbcVkx/3yivymPSMEkPCRDYxQv45w4TqBKMDfpRd1TOLOv1qvcb+gjH+z5IfVBMp2IpG/Q==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_null_rand_explicit_id\": {\n    \"kms\": \"local\",\n    \"type\": \"null\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": null\n  },\n  \"local_null_rand_explicit_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"null\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": null\n  },\n  \"local_null_det_explicit_id\": {\n    \"kms\": \"local\",\n    \"type\": \"null\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": null\n  },\n  \"local_null_det_explicit_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"null\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": null\n  },\n  \"local_regex_rand_auto_id\": {\n    \"kms\": \"local\",\n    \"type\": \"regex\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AizggCwAAAAAAAAAAAAAAAALXKw7zSgqQj1AKoWO0MoMxsBuu0cMB6KdJQCRKdupoLV/Y22owwsVpDDMv5sgUpkG5YIV+Fz7taHodXE07qHopw==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_regex_rand_auto_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"regex\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AizggCwAAAAAAAAAAAAAAAALntOLXq7VW1+jwba/dSbidMo2bewNo7AtK9A1CPwk9XrjUQaEOQxfRpho3BYQEo2U67fQdsY/tyhaj4jduHn9JQ==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_regex_rand_explicit_id\": {\n    \"kms\": \"local\",\n    \"type\": \"regex\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AizggCwAAAAAAAAAAAAAAAALlMMG2iS/gEOEsVKR7sxBJP2IUzZ+aRbozDSkqADncresBvaPBSE17lng5NG7H1JRCAcP1rH/Te+0CrMd7JpRAQ==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_regex_rand_explicit_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"regex\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AizggCwAAAAAAAAAAAAAAAAL1YNnlVu5+njDLxh1LMhIPOH19RykAXhxrUbCy6TI5MLQsAOSgAJbXOTXeKr0D8/Ff0phToWOKl193gOOIp8yZQ==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_regex_det_auto_id\": {\n    \"kms\": \"local\",\n    \"type\": \"regex\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"ASzggCwAAAAAAAAAAAAAAAALiZbL5nFIZl7cSLH5E3wK3jJeAeFc7hLHNITtLAu+o10raEs5i/UCihMHmkf8KHZxghs056pfm5BjPzlL9x7IHQ==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_regex_det_explicit_id\": {\n    \"kms\": \"local\",\n    \"type\": \"regex\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"ASzggCwAAAAAAAAAAAAAAAALiZbL5nFIZl7cSLH5E3wK3jJeAeFc7hLHNITtLAu+o10raEs5i/UCihMHmkf8KHZxghs056pfm5BjPzlL9x7IHQ==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_regex_det_explicit_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"regex\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"ASzggCwAAAAAAAAAAAAAAAALiZbL5nFIZl7cSLH5E3wK3jJeAeFc7hLHNITtLAu+o10raEs5i/UCihMHmkf8KHZxghs056pfm5BjPzlL9x7IHQ==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_dbPointer_rand_auto_id\": {\n    \"kms\": \"local\",\n    \"type\": \"dbPointer\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AizggCwAAAAAAAAAAAAAAAAMUdAA9uOSk1tXJVe/CG3Ps6avYTEF1eHj1wSlCHkFxqlMtTO+rIQpikpjH0MrcXvEEdAO8g5hFZ01I7DWyK5AAxTxDqVF+kOaQ2VfKs6hyuo=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_dbPointer_rand_auto_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"dbPointer\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AizggCwAAAAAAAAAAAAAAAAMiNqvqLwZrPnsF235z+Obl1K9iEXdJ5GucMGpJdRG4lRvRE0Oy1vh6ztNTpYPY/tXyUFTBWlzl/lITalSEm/dT1Bnlh0iPAFrAiNySf662og=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_dbPointer_rand_explicit_id\": {\n    \"kms\": \"local\",\n    \"type\": \"dbPointer\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AizggCwAAAAAAAAAAAAAAAAM+Tn31YcKiowBTJWRYCYAEO7UARDE2/jTVGEKXCpiwEqqP3JSAS0b80zYt8dxo5mVhUo2a02ClKrB8vs+B6sU1kXrahSaVSEHZlRSGN9fWgo=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_dbPointer_rand_explicit_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"dbPointer\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AizggCwAAAAAAAAAAAAAAAAMdOZZUvpJIqG9qiOLy5x4BdftyHipPDZn/eeLEc7ir3v4jJsY3dsv6fQERo5U9lMynNGA9PJePVzq5tWsIMX0EcCQcMfGmosfkYDzN1OX99A=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_dbPointer_det_auto_id\": {\n    \"kms\": \"local\",\n    \"type\": \"dbPointer\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"ASzggCwAAAAAAAAAAAAAAAAMQWace2C1w3yqtmo/rgz3YtIDnx1Ia/oDsoHnnMZlEy5RoK3uosi1hvNAZCSg3Sen0H7MH3XVhGGMCL4cS69uJ0ENSvh+K6fiZzAXCKUPfvM=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_dbPointer_det_explicit_id\": {\n    \"kms\": \"local\",\n    \"type\": \"dbPointer\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"ASzggCwAAAAAAAAAAAAAAAAMQWace2C1w3yqtmo/rgz3YtIDnx1Ia/oDsoHnnMZlEy5RoK3uosi1hvNAZCSg3Sen0H7MH3XVhGGMCL4cS69uJ0ENSvh+K6fiZzAXCKUPfvM=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_dbPointer_det_explicit_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"dbPointer\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"ASzggCwAAAAAAAAAAAAAAAAMQWace2C1w3yqtmo/rgz3YtIDnx1Ia/oDsoHnnMZlEy5RoK3uosi1hvNAZCSg3Sen0H7MH3XVhGGMCL4cS69uJ0ENSvh+K6fiZzAXCKUPfvM=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_javascript_rand_auto_id\": {\n    \"kms\": \"local\",\n    \"type\": \"javascript\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AizggCwAAAAAAAAAAAAAAAANNL2AMKwTDyMIvxLKhBxZKx50C0tBdkLwuXmuMcrUqZeH8bsvjtttoM9LWkkileMyeTWgxblJ1b+uQ+V+4VT6fA==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_javascript_rand_auto_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"javascript\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AizggCwAAAAAAAAAAAAAAAANBjBlHGw3K3TWQHpvfa1z0bKhNnVFC/lZArIexo3wjdGq3MdkGA5cuBIp87HHmOIv6o/pvQ9K74v48RQl+JH44A==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_javascript_rand_explicit_id\": {\n    \"kms\": \"local\",\n    \"type\": \"javascript\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AizggCwAAAAAAAAAAAAAAAANjvM7u3vNVyKpyI7g5kbzBpHPzXzOQToDSng5/c9yjMG+qi4TPtOyassobJOnMmDYBLyqRXCl/GsDLprbg5jxuA==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_javascript_rand_explicit_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"javascript\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AizggCwAAAAAAAAAAAAAAAANMtO7KneuVx4gSOjX4MQjKL80zJhnt+efDBylkpNsqKyxBXB60nkiredGzwaK3/4QhIfGJrC1fQpwUwu/v1L17g==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_javascript_det_auto_id\": {\n    \"kms\": \"local\",\n    \"type\": \"javascript\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"ASzggCwAAAAAAAAAAAAAAAANmQsg9E/BzGJVNVhSNyunS/TH0332oVFdPS6gjX0Cp/JC0YhB97DLz3N4e/q8ECaz7tTdQt9JacNUgxo+YCULUA==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_javascript_det_explicit_id\": {\n    \"kms\": \"local\",\n    \"type\": \"javascript\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"ASzggCwAAAAAAAAAAAAAAAANmQsg9E/BzGJVNVhSNyunS/TH0332oVFdPS6gjX0Cp/JC0YhB97DLz3N4e/q8ECaz7tTdQt9JacNUgxo+YCULUA==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_javascript_det_explicit_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"javascript\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"ASzggCwAAAAAAAAAAAAAAAANmQsg9E/BzGJVNVhSNyunS/TH0332oVFdPS6gjX0Cp/JC0YhB97DLz3N4e/q8ECaz7tTdQt9JacNUgxo+YCULUA==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_symbol_rand_auto_id\": {\n    \"kms\": \"local\",\n    \"type\": \"symbol\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AizggCwAAAAAAAAAAAAAAAAOOuO2b23mekwI8b6gWeEgRy1lLOCsNyBKvdmizK7/oOVKCvd+3kwUn9a6TxygooiVAN/Aohr1cjb8jRlMPWpkP0iO0+Tt6+vkizgFsQW4iio=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_symbol_rand_auto_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"symbol\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AizggCwAAAAAAAAAAAAAAAAOhN4QPOcmGnFKGvTfhz6TQleDA02X6oWULLHTnOUJYfE3OUSyf2ULEQh1yhdKdwXMuYVgGl28pMosiwkBShrXYe5ZlMjiZCIMZWSdUMV0tXk=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_symbol_rand_explicit_id\": {\n    \"kms\": \"local\",\n    \"type\": \"symbol\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AizggCwAAAAAAAAAAAAAAAAO9aWi9RliwQHdXHoJME9VyN6XgyGd95Eclx+ZFYfLxBGAuUnPNjSfVuNZwYdyKC8JX79+mYhk7IXmcGV4z+4486sxyLk3idi4Kmpz2ESqV5g=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_symbol_rand_explicit_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"symbol\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AizggCwAAAAAAAAAAAAAAAAO/qev3DPfpkQoSW9aHOyalwfI/VYDQVN5VMINx4kw2vEqHiI1HRdZRPOz3q74TlQEy3TMNMTYdCvh5bpN/PptRZCTQbzP6ugz9dTp79w5/Ok=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_symbol_det_auto_id\": {\n    \"kms\": \"local\",\n    \"type\": \"symbol\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"ASzggCwAAAAAAAAAAAAAAAAOsg5cs6VpZWoTOFg4ztZmpj8kSTeCArVcI1Zz2pOnmMqNv/vcKQGhKSBbfniMripr7iuiYtlgkHGsdO2FqUp6Jb8NEWm5uWqdNU21zR9SRkE=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_symbol_det_explicit_id\": {\n    \"kms\": \"local\",\n    \"type\": \"symbol\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"ASzggCwAAAAAAAAAAAAAAAAOsg5cs6VpZWoTOFg4ztZmpj8kSTeCArVcI1Zz2pOnmMqNv/vcKQGhKSBbfniMripr7iuiYtlgkHGsdO2FqUp6Jb8NEWm5uWqdNU21zR9SRkE=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_symbol_det_explicit_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"symbol\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"ASzggCwAAAAAAAAAAAAAAAAOsg5cs6VpZWoTOFg4ztZmpj8kSTeCArVcI1Zz2pOnmMqNv/vcKQGhKSBbfniMripr7iuiYtlgkHGsdO2FqUp6Jb8NEWm5uWqdNU21zR9SRkE=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_javascriptWithScope_rand_auto_id\": {\n    \"kms\": \"local\",\n    \"type\": \"javascriptWithScope\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AizggCwAAAAAAAAAAAAAAAAP5gLMvLOAc6vGAvC7bGmEC4eweptAiX3A7L0iCoHps/wm0FBLkfpF6F4pCjVYiY1lTID38wliRLPyhntCj+cfvlMfKSjouNgXMIWyQ8GKZ2c=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_javascriptWithScope_rand_auto_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"javascriptWithScope\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AizggCwAAAAAAAAAAAAAAAAPVsw9Opn/P5SAdJhX4MTxIcsmaG8isIN4NKPi9k1u/Vj7AVkcxYqwurAghaJpmfoAgMruvzi1hcKvd05yHd9Nk0vkvODwDgnjJB6QO+qUce8=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_javascriptWithScope_rand_explicit_id\": {\n    \"kms\": \"local\",\n    \"type\": \"javascriptWithScope\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AizggCwAAAAAAAAAAAAAAAAPLUa+nsrqiHkVdE5K1xl/ZsiZqQznG2yVXyA3b3loBylbcL2NEBp1JUeGnPZ0y5ZK4AmoL6NMH2Io313rW3V8FTArs/OOQWPRJSe6h0M3wXk=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_javascriptWithScope_rand_explicit_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"javascriptWithScope\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AizggCwAAAAAAAAAAAAAAAAPzUKaXCH0JImSlY73HVop9g9c0YssNEiA7Dy7Vji61avxvnuJJfghDchdwwaY7Vc8+0bymoanUWcErRctLzjm+1uKeMnFQokR8wFtnS3PgpQ=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_javascriptWithScope_det_explicit_id\": {\n    \"kms\": \"local\",\n    \"type\": \"javascriptWithScope\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": { \"$code\": \"x=1\", \"$scope\": {} }\n  },\n  \"local_javascriptWithScope_det_explicit_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"javascriptWithScope\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": { \"$code\": \"x=1\", \"$scope\": {} }\n  },\n  \"local_int_rand_auto_id\": {\n    \"kms\": \"local\",\n    \"type\": \"int\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AizggCwAAAAAAAAAAAAAAAAQHXpXb3KlHA2KFTBgl0VoLCu0CUf1ae4DckkwDorbredVSqxvA5e+NvVudY5yuea6bC9F57JlbjI8NWYAUw4q0Q==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_int_rand_auto_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"int\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AizggCwAAAAAAAAAAAAAAAAQSxXMF4+TKV+a3lcxXky8VepEqdg5wI/jg+C4CAUgNurq2XhgrxyqiMjkU8z07tfyoLYyX6P+dTrwj6nzvvchCw==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_int_rand_explicit_id\": {\n    \"kms\": \"local\",\n    \"type\": \"int\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AizggCwAAAAAAAAAAAAAAAAQmzteYnshCI8HBGd7UYUKvcg4xl6M8PRyi1xX/WHbjyQkAJXxczS8hO91wuqStE3tBNSmulUejz9S691ufTd6ZA==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_int_rand_explicit_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"int\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AizggCwAAAAAAAAAAAAAAAAQLCHLru//++QSoWVEyw2v6TUfCnlrPJXrpLLezWf16vK85jTfm8vJbb2X2UzX04wGzVL9tCFFsWX6Z5gHXhgSBg==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_int_det_auto_id\": {\n    \"kms\": \"local\",\n    \"type\": \"int\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"ASzggCwAAAAAAAAAAAAAAAAQIxWjLBromNUgiOoeoZ4RUJUYIfhfOmab0sa4qYlS9bgYI41FU6BtzaOevR16O9i+uACbiHL0X6FMXKjOmiRAug==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_int_det_explicit_id\": {\n    \"kms\": \"local\",\n    \"type\": \"int\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"ASzggCwAAAAAAAAAAAAAAAAQIxWjLBromNUgiOoeoZ4RUJUYIfhfOmab0sa4qYlS9bgYI41FU6BtzaOevR16O9i+uACbiHL0X6FMXKjOmiRAug==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_int_det_explicit_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"int\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"ASzggCwAAAAAAAAAAAAAAAAQIxWjLBromNUgiOoeoZ4RUJUYIfhfOmab0sa4qYlS9bgYI41FU6BtzaOevR16O9i+uACbiHL0X6FMXKjOmiRAug==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_timestamp_rand_auto_id\": {\n    \"kms\": \"local\",\n    \"type\": \"timestamp\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AizggCwAAAAAAAAAAAAAAAARntIycg0Xkd16GEa//VSJI4Rkl7dT6MpRa+D3MiTEeio5Yy8zGK0u2BtEP/9MCRQw2hJDYj5znVqwhdduM0OTiA==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_timestamp_rand_auto_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"timestamp\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AizggCwAAAAAAAAAAAAAAAARWA9Ox5ejDPeWxfjbRgcGCtF/G5yrPMbBJD9ESDFc0NaVe0sdNNTisEVxsSkn7M/S4FCibKh+C8femr7xhu1iTw==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_timestamp_rand_explicit_id\": {\n    \"kms\": \"local\",\n    \"type\": \"timestamp\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AizggCwAAAAAAAAAAAAAAAARrEfOL4+4Qh7IkhHnHcBEANGfMF8n2wUDnsZ0lXEb0fACKzaN5OKaxMIQBs/3pFBw721qRfCHY+ByKeaQuABbzg==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_timestamp_rand_explicit_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"timestamp\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AizggCwAAAAAAAAAAAAAAAARW8nwmnBt+LFIAcFWvOzX8llrGcveQKFhyYUIth9d7wtpTyc9myFp8GBQCnjDpKzA6lPmbqVYeLU0L9q0h6SHGQ==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_timestamp_det_auto_id\": {\n    \"kms\": \"local\",\n    \"type\": \"timestamp\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"ASzggCwAAAAAAAAAAAAAAAAR6uMylGytMq8QDr5Yz3w9HlW2MkGt6yIgUKcXYSaXru8eer+EkLv66/vy5rHqTfV0+8ryoi+d+PWO5U6b3Ng5Gg==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_timestamp_det_explicit_id\": {\n    \"kms\": \"local\",\n    \"type\": \"timestamp\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"ASzggCwAAAAAAAAAAAAAAAAR6uMylGytMq8QDr5Yz3w9HlW2MkGt6yIgUKcXYSaXru8eer+EkLv66/vy5rHqTfV0+8ryoi+d+PWO5U6b3Ng5Gg==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_timestamp_det_explicit_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"timestamp\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"ASzggCwAAAAAAAAAAAAAAAAR6uMylGytMq8QDr5Yz3w9HlW2MkGt6yIgUKcXYSaXru8eer+EkLv66/vy5rHqTfV0+8ryoi+d+PWO5U6b3Ng5Gg==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_long_rand_auto_id\": {\n    \"kms\": \"local\",\n    \"type\": \"long\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AizggCwAAAAAAAAAAAAAAAASrinKUOpHIB7MNRmCAPWcP4CjZwfr5JaRT3G/GqY9B/6csj3+N9jmo1fYvM8uHcnmf5hzDDOamaE2FF1jDKkrHw==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_long_rand_auto_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"long\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AizggCwAAAAAAAAAAAAAAAAShWMPYDkCpTC2XLYyykPJMihASLKn6HHcB2Eh7jFwQb/8D1HCQoPmOHMyXaN4AtIKm1oqEfma6FSnEPENQoledQ==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_long_rand_explicit_id\": {\n    \"kms\": \"local\",\n    \"type\": \"long\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AizggCwAAAAAAAAAAAAAAAASd2h34ZLib+GiYayrm/FIZ/weg8wF41T0PfF8NCLTJCoT7gIkdpNRz2zkkQgZMR31efNKtsM8Bs4wgZbkrXsXWg==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_long_rand_explicit_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"long\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AizggCwAAAAAAAAAAAAAAAASPAvdjz+a3FvXqDSjazaGqwZxrfXlfFB5/VjQFXQB0gpodCEaz1qaLSKfCWBg83ftrYKa/1sa44gU5NBthDfDwQ==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_long_det_auto_id\": {\n    \"kms\": \"local\",\n    \"type\": \"long\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"ASzggCwAAAAAAAAAAAAAAAASQk372m/hW3WX82/GH+ikPv3QUwK7Hh/RBpAguiNxMdNhkgA/y2gznVNm17t6djyub7+d5zN4P5PLS/EOm2kjtw==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_long_det_explicit_id\": {\n    \"kms\": \"local\",\n    \"type\": \"long\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"ASzggCwAAAAAAAAAAAAAAAASQk372m/hW3WX82/GH+ikPv3QUwK7Hh/RBpAguiNxMdNhkgA/y2gznVNm17t6djyub7+d5zN4P5PLS/EOm2kjtw==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_long_det_explicit_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"long\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"ASzggCwAAAAAAAAAAAAAAAASQk372m/hW3WX82/GH+ikPv3QUwK7Hh/RBpAguiNxMdNhkgA/y2gznVNm17t6djyub7+d5zN4P5PLS/EOm2kjtw==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_decimal_rand_auto_id\": {\n    \"kms\": \"local\",\n    \"type\": \"decimal\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AizggCwAAAAAAAAAAAAAAAATLnMMDZhnGSn5F5xHhsJXxiTGXd61Eq6fgppOlxUNVlsZNYyr5tZ3owfTTqRuD9yRg97x65WiHewBBnJJSeirCTAy9zZxWPVlJSiC0gO7rbM=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_decimal_rand_auto_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"decimal\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AizggCwAAAAAAAAAAAAAAAATenMh7NKQioGjpuEojIrYKFaJhbuGxUgu2yTTbe3TndhgHryhW9GXiUqo8WTpnXqpC5E/z03ZYLWfCbe7qGdL6T7bbrTpaTaWZnnAm3XaCqY=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_decimal_rand_explicit_id\": {\n    \"kms\": \"local\",\n    \"type\": \"decimal\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AizggCwAAAAAAAAAAAAAAAAT9vqXuKRh+2HxeCMr+pQYdhYNw7xrTdU4dySWz0X6tCK7LZO5AV72utmRJxID7Bqv1ZlXAk00V92oDLyKG9kHeG5+S34QE/aLCPsAWcppfxY=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_decimal_rand_explicit_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"decimal\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AizggCwAAAAAAAAAAAAAAAATtqOCFMbOkls3LikQNXlnlkRr5gJns1+5Kvbt7P7texMa/QlXkYSHhtwESyfOcCQ2sw1T0eZ9DDuNaznpdK2KIqZBkVEC9iMoxqIqXF7Nab0=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"local_decimal_det_explicit_id\": {\n    \"kms\": \"local\",\n    \"type\": \"decimal\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": { \"$numberDecimal\": \"1.234\" }\n  },\n  \"local_decimal_det_explicit_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"decimal\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": { \"$numberDecimal\": \"1.234\" }\n  },\n  \"local_minKey_rand_explicit_id\": {\n    \"kms\": \"local\",\n    \"type\": \"minKey\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": { \"$minKey\": 1 }\n  },\n  \"local_minKey_rand_explicit_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"minKey\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": { \"$minKey\": 1 }\n  },\n  \"local_minKey_det_explicit_id\": {\n    \"kms\": \"local\",\n    \"type\": \"minKey\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": { \"$minKey\": 1 }\n  },\n  \"local_minKey_det_explicit_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"minKey\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": { \"$minKey\": 1 }\n  },\n  \"local_maxKey_rand_explicit_id\": {\n    \"kms\": \"local\",\n    \"type\": \"maxKey\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": { \"$maxKey\": 1 }\n  },\n  \"local_maxKey_rand_explicit_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"maxKey\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": { \"$maxKey\": 1 }\n  },\n  \"local_maxKey_det_explicit_id\": {\n    \"kms\": \"local\",\n    \"type\": \"maxKey\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": { \"$maxKey\": 1 }\n  },\n  \"local_maxKey_det_explicit_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"maxKey\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": { \"$maxKey\": 1 }\n  },\n  \"payload=0,algo=rand\": {\n    \"kms\": \"local\",\n    \"type\": \"string\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AizggCwAAAAAAAAAAAAAAAACcsBdT93ivCyvtkfQz9qb1A9Ll+I6hnGE0kFy3rmVG6xAvipmRJSoVq3iv7iUEDvaqmPXfjeH8h8cPYT86v3XSg==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"payload=1,algo=rand\": {\n    \"kms\": \"local\",\n    \"type\": \"string\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AizggCwAAAAAAAAAAAAAAAACQOzpNBEGSrANr3Wl8uYpqeIc7pjc8e2LS2FaSrb8tM9F3mR1FqGgfJtn3eD+HZf3Y3WEDGK8975a/1BufkMqIQ==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"payload=2,algo=rand\": {\n    \"kms\": \"local\",\n    \"type\": \"string\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AizggCwAAAAAAAAAAAAAAAACyGJEcuN1pG5oSEyxuKFwqddGHVU5Untbib7LkmtoJe9HngTofkOpeHZH/hV6Z3CFxLu6WFliJoySsFFbnFy9ag==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"payload=3,algo=rand\": {\n    \"kms\": \"local\",\n    \"type\": \"string\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AizggCwAAAAAAAAAAAAAAAACLbp4w6mx45lR1vvgmeRja/y8U+WnR2oH4IpfrDi4lKM+JPVnJweiN3/1wAy+sXSy0S1Yh9yxmhh9ISoTkAuVxw==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"payload=4,algo=rand\": {\n    \"kms\": \"local\",\n    \"type\": \"string\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AizggCwAAAAAAAAAAAAAAAACG0qMY/GPZ/2CR61cxbuizywefyMZVdeTCn5KFjqwejgxeBwX0JmGNHKKWbQIDQykRFv0q0WHUgsRmRhaotNCyQ==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"payload=5,algo=rand\": {\n    \"kms\": \"local\",\n    \"type\": \"string\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AizggCwAAAAAAAAAAAAAAAACJI1onNpQfZhaYWrPEzHvNaJRqUDZK2xoyonB5E473BPgp3zvn0Jmz1deL8GzS+HlkjCrx39OvHyVt3+3S0kYYw==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"payload=6,algo=rand\": {\n    \"kms\": \"local\",\n    \"type\": \"string\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AizggCwAAAAAAAAAAAAAAAAClyKY9tZBjl7SewSXr3MdoWRDUNgLaXDUjENpjyYvi/54EQ9a+J/LAAh1892i+mLpYxEUAmcftPyfX3VhbCgUQw==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"payload=7,algo=rand\": {\n    \"kms\": \"local\",\n    \"type\": \"string\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AizggCwAAAAAAAAAAAAAAAACAMbEA+kNvnVV7B//ds2/QoVot061kbazoMwB/psB5eFdLVB5qApAXEWgQEMwkNnsTUYbtSduQz6uGwdagtNBRw==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"payload=8,algo=rand\": {\n    \"kms\": \"local\",\n    \"type\": \"string\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AizggCwAAAAAAAAAAAAAAAACzdSK/d7Ni6D8qUgNopnEU5ia1K5llhBGk3O1Tf71t4ThnQjYW9eI/rIohWmev5CGWLHhwuvvKUtFcTAe+NMQww==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"payload=9,algo=rand\": {\n    \"kms\": \"local\",\n    \"type\": \"string\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AizggCwAAAAAAAAAAAAAAAACzQcEa+ktF2EZf35TtyatnSGGaIVvFhZNuo5P3VwQvoONJrK2cSad7PBDAv3xDAB+VPZAigXAGQvd051sHooOHg==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"payload=10,algo=rand\": {\n    \"kms\": \"local\",\n    \"type\": \"string\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AizggCwAAAAAAAAAAAAAAAACpfoDmApsR5xOD3TDhcHeD7Jco3kPFuuWjDpHtMepMOJ3S0c+ngGGhzPGZtEz2xuD/E7AQn1ryp/WAQ+WwkaJkQ==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"payload=11,algo=rand\": {\n    \"kms\": \"local\",\n    \"type\": \"string\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AizggCwAAAAAAAAAAAAAAAACICMRXmx3oKqYv0IpmzkSMBIGT4Li3MPBF4Lw1s5F69WvZApD58glIKB6b7koIrF5qc2Wrb1/Nw+stRv0zvQ8Y9CcFV4OHm6WoEw+XDlWXJ4=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"payload=12,algo=rand\": {\n    \"kms\": \"local\",\n    \"type\": \"string\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AizggCwAAAAAAAAAAAAAAAACTArUn0WUTojQC4fSvq3TwJVTsZNhWAK2WB057u2EnkUzMC0xsbU6611W6Okx6idZ7pMudXpBC34fRDrJPXOu3BxK+ZLCOWS2FqsvWq3HeTY=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"payload=13,algo=rand\": {\n    \"kms\": \"local\",\n    \"type\": \"string\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AizggCwAAAAAAAAAAAAAAAACU1Ojn7EM2i+5KK2Beh1gPLhryK3Y7PtaZ/v4JvstxuAV4OHOR9yROP7pwenHXxczkWXvcyMY9OCdmHO8pkQkXO21798IPkDDN/ejJUFI0Uw=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"payload=14,algo=rand\": {\n    \"kms\": \"local\",\n    \"type\": \"string\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AizggCwAAAAAAAAAAAAAAAAC0ZLwSliCbcr/e1uiYWk6gRuD/5qiyulQ7IUNWjhpBR6SLUfX2+yExLzps9hoOp53j9zRSKIzyleZ8yGLTLeN+Lz9BUe2ZT+sV8NiqZz3pkA=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"payload=15,algo=rand\": {\n    \"kms\": \"local\",\n    \"type\": \"string\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AizggCwAAAAAAAAAAAAAAAACQ9pmlQeFDr+jEhFwjL/eGVxdv70JdnkLaKdJ3/jkvCX1VPU5HmQIi+JWY3Rrw844E/6sBR6zIODn5aM0WfyP8a2zKRAWaVQZ7n+QE9hDN/8=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"payload=16,algo=rand\": {\n    \"kms\": \"local\",\n    \"type\": \"string\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AizggCwAAAAAAAAAAAAAAAACiOcItInDGHqvkH0I3udp5nnX32XzDeqya/3KDjgZPT5GHek1vFTZ4924JVxFqFQz+No9rOVmyxm8O2fxjTK2vsjtADzKGnMTtFYZqghYCuc=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"payload=0,algo=det\": {\n    \"kms\": \"local\",\n    \"type\": \"string\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"ASzggCwAAAAAAAAAAAAAAAACijFptWQy7a1Y0rpXEvamXWI9v9dnx0Qj84/mKUsVpc3agkQ0B04uPYeROdt2MeEeiZoEKVWV0NjBocAQCEz7dw==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"payload=1,algo=det\": {\n    \"kms\": \"local\",\n    \"type\": \"string\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"ASzggCwAAAAAAAAAAAAAAAAChR90taVWsZk+++sgibX6CnFeQQHNoB8V+n2gmDe3CIT/t+WvhMf9D+mQipbAlrUyHgGihKMHcvAZ5RZ/spaH4Q==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"payload=2,algo=det\": {\n    \"kms\": \"local\",\n    \"type\": \"string\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"ASzggCwAAAAAAAAAAAAAAAAC67wemDv1Xdu7+EMR9LMBTOxfyAqsGaxQibwamZItzplslL/Dp3t9g9vPuNzq0dWwhnfxQ9GBe8OA3dtRaifYCA==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"payload=3,algo=det\": {\n    \"kms\": \"local\",\n    \"type\": \"string\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"ASzggCwAAAAAAAAAAAAAAAACVLxch+uC7weXrbtylCo1m4HYZmh0sd9JCrlTECO2M56JK1X9a30i2BDUdhPuoTvvODv74CGXkZKdist3o0mGAQ==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"payload=4,algo=det\": {\n    \"kms\": \"local\",\n    \"type\": \"string\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"ASzggCwAAAAAAAAAAAAAAAACexfIZGkOYaCGktOUc6cgAYg7Bd/C5ZYmdb7b8+rd5BKWbthW6N6CxhDIyh/DHvkPAeIzfTYA2/9w6tsjfD/TPQ==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"payload=5,algo=det\": {\n    \"kms\": \"local\",\n    \"type\": \"string\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"ASzggCwAAAAAAAAAAAAAAAACjUH/dPW4egOvFMJJnpWK8v27MeLkbXC4GFl1j+wPqTsIEeIWkzEmcXjHLTQGE2GplHHc/zxwRwD2dXdbzvsCDw==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"payload=6,algo=det\": {\n    \"kms\": \"local\",\n    \"type\": \"string\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"ASzggCwAAAAAAAAAAAAAAAACzvS+QkGlvb05pNn+vBMml09yKmE8yM6lwccNIST5uZSsUxXf2hrxPtO7Ylc4lmBAJt/9bcM59JIeT9fpYMc75w==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"payload=7,algo=det\": {\n    \"kms\": \"local\",\n    \"type\": \"string\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"ASzggCwAAAAAAAAAAAAAAAACSf2RxHJpRuh4j8nS1dfonUtsJEwgqfWrwOsfuT/tAGXgDN0ObUpzL2K7G2vmePjP4dwycCSIL3+2j34bqBJK1Q==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"payload=8,algo=det\": {\n    \"kms\": \"local\",\n    \"type\": \"string\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"ASzggCwAAAAAAAAAAAAAAAACu96YYeLXXoYdEZYNU9UAZjSd6G4fOE1edrA6/RjZKVGWKxftmvj5g1VAOiom0XuTZUe1ihbnwhvKexeoa3Vc8Q==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"payload=9,algo=det\": {\n    \"kms\": \"local\",\n    \"type\": \"string\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"ASzggCwAAAAAAAAAAAAAAAACX+UjBKo9+N0Z+mbyqZqkQv2ETMSn6aPTONWgJtw5nWklcxKjUSSLI+8LW/6M6Xf9a7177GsqmV2f/yCRF58Xtw==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"payload=10,algo=det\": {\n    \"kms\": \"local\",\n    \"type\": \"string\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"ASzggCwAAAAAAAAAAAAAAAACL6TVscFzIJ9+Zj6LsCZ9xhaZuTZdvz1nJe4l69nKyj9hCjnyuiV6Ve4AXwQ5W1wiPfkJ0fCZS33NwiHw7QQ/vg==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"payload=11,algo=det\": {\n    \"kms\": \"local\",\n    \"type\": \"string\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"ASzggCwAAAAAAAAAAAAAAAACPLq7IcWhTVwkKmy0flN7opoQzx7tTe1eD9JIc25FC9B6KGQkdcRDglDDR7/m6+kBtTnq88y63vBgomTxA8ZxQE+3pB7zCiBhX0QznuXvP44=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"payload=12,algo=det\": {\n    \"kms\": \"local\",\n    \"type\": \"string\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"ASzggCwAAAAAAAAAAAAAAAACxv7v4pKtom5z1g9FUuyjEWAbdzJ3ytPNZlOfVr6KZnUPhIH7PfCz3/lTdYYWBTj01+SUZiC/7ruof9QDhsSiNWP7nUyHpQ/C3joI/BBjtDA=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"payload=13,algo=det\": {\n    \"kms\": \"local\",\n    \"type\": \"string\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"ASzggCwAAAAAAAAAAAAAAAACZhiElQ/MvyVMwMkZPu8pT54Ap6TlpVSEbE4nIQzzeU3XKVuspMdI5IXvvgfULXKXc+AOu6oQXZ+wAJ1tErVOsb48HF1g0wbXbBA31C5qLEM=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"payload=14,algo=det\": {\n    \"kms\": \"local\",\n    \"type\": \"string\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"ASzggCwAAAAAAAAAAAAAAAACdp8mDOeDuDLhE0LzTOT2p0CMaUsAQrGCzmiK6Ab9xvaIcPPcejUcpdO3XXAS/pPab4+TUwO5GbI5pDJ29zwaOiOz2H3OJ2m2p5BHQp9mCys=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"payload=15,algo=det\": {\n    \"kms\": \"local\",\n    \"type\": \"string\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"ASzggCwAAAAAAAAAAAAAAAACmtLohoP/gotuon2IvnGeLEfCWHRMhG9Wp4tPu/vbJJkJkbQTP35HRG9VrMV7KKrEQbOsJ2Y6UDBra4tyjn0fIkwwc/0X9i+xaP+TrwpNabE=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"payload=16,algo=det\": {\n    \"kms\": \"local\",\n    \"type\": \"string\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"ASzggCwAAAAAAAAAAAAAAAAC6s9eUtSneKWj3/A7S+bPZLj3t1WtUh7ltW80b8jCRzA+kOI26j1MEb1tt68HgcnH1IJ3YQ/+UHlV95OgwSnIxlib/HJn3U0s8mpuCWe1Auo=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_double_rand_auto_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"double\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgGVERAAAAAAAAAAAAAAAAAB0S2kOZe54q6iZqeTLndkX+kehTKtb30jTP7FS+Zx+cxhFs626OrGY+jrH41cLfroCccacyNHUZFRinfqZPNOyw==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_double_rand_auto_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"double\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgGVERAAAAAAAAAAAAAAAAABYViH7PLjCIdmTibW9dGCJADwXx2dRSMYxEmulPu89clAoeLDa8pwJ7YxLFQCcTGmZRfmp58dDDAzV8tyyE8QMg==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_double_rand_explicit_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"double\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgGVERAAAAAAAAAAAAAAAAABeRahSj4pniBp0rLIEZE8MdeyiIKcYuTZiuGzGiXbFbntEPow88DFHIBSxbMGR7p/8jCpPL+GqBwFkPkafXbMzg==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_double_rand_explicit_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"double\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgGVERAAAAAAAAAAAAAAAAABdaa3vKtO4cAEUjYJfOPl1KbbgeWtphfUuJd6MxR9VReNSf1jc+kONwmkPVQs2WyZ1n+TSQMGRoBp1nHRttDdTg==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_double_det_explicit_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"double\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": {\n      \"$numberDouble\": \"1.2339999999999999858\"\n    }\n  },\n  \"azure_double_det_explicit_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"double\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": {\n      \"$numberDouble\": \"1.2339999999999999858\"\n    }\n  },\n  \"azure_string_rand_auto_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"string\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgGVERAAAAAAAAAAAAAAAAACeoztcDg9oZ7ixHinReWQTrAumpsfyb0E1s3BGOFHgBCi1tW79CEXfqN8riFRc1YeRTlN4k5ShgHaBWBlax+XoQ==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_string_rand_auto_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"string\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgGVERAAAAAAAAAAAAAAAAACov9cXQvDHeKOS5Gxcxa8vdAcTsTXDYgUucGzsCyh4TnTWKGQEVk3DHndUXX569TKCjq5QsC//oWEwweCn1nZ4g==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_string_rand_explicit_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"string\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgGVERAAAAAAAAAAAAAAAAACKU5qTdMdO0buQ/37ZRANUAAafcsoNMOTxJsDOfkqUb+/kRgM1ePlwVvk4EJiAGhJ/4SEmEOpwv05TT3PxGur2Q==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_string_rand_explicit_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"string\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgGVERAAAAAAAAAAAAAAAAACX/ODKGHUyAKxoJ/c/3lEDBTc+eP/VS8OHrLhYoP96McpnFSgYi5jfUwvrFYa715fkass4N0nAHE6TzoGTYyk6Q==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_string_det_auto_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"string\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AQGVERAAAAAAAAAAAAAAAAACmVI7YK4JLOzutEdQ79he817Vk5EDP/3hXwOlGmERZCtp8J8HcqClhV+pyvRLGbwmlh12fbSs9nEp7mrobQm9wA==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_string_det_explicit_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"string\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AQGVERAAAAAAAAAAAAAAAAACmVI7YK4JLOzutEdQ79he817Vk5EDP/3hXwOlGmERZCtp8J8HcqClhV+pyvRLGbwmlh12fbSs9nEp7mrobQm9wA==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_string_det_explicit_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"string\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AQGVERAAAAAAAAAAAAAAAAACmVI7YK4JLOzutEdQ79he817Vk5EDP/3hXwOlGmERZCtp8J8HcqClhV+pyvRLGbwmlh12fbSs9nEp7mrobQm9wA==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_object_rand_auto_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"object\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgGVERAAAAAAAAAAAAAAAAADWkZMsfCo4dOPMH1RXC7GkZFt1RCjJf0vaLDA09ih1Jl47SOetZELQ7B1TQjRQitktzrfD43jk8Fn4J5ZYZu1qQ==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_object_rand_auto_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"object\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgGVERAAAAAAAAAAAAAAAAADJFMymfstltZP1oAqj4bgbCk8uLGtCd12eLqvSq0ZO+JDvls7PAovwmoWwigHunP8BBXT8sLydK+jn1sHfnhrlw==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_object_rand_explicit_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"object\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgGVERAAAAAAAAAAAAAAAAADCen+XrLYKg7gIVubVfdbQwuJ0mFHxhSUUyyBWj4RCeLeLUYXckboPGixXWB9XdwcOnInfF9u6qvktY67GtYASQ==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_object_rand_explicit_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"object\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgGVERAAAAAAAAAAAAAAAAADnUyp/7eLmxxxOdsP+mNuJABK4PQoKFWDAY7lDrH6MYa03ryASOihPZWYZWXZLrbAf7cQQhElEkKqKwY8+NXgqg==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_object_det_explicit_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"object\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": {\n      \"x\": {\n        \"$numberInt\": \"1\"\n      }\n    }\n  },\n  \"azure_object_det_explicit_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"object\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": {\n      \"x\": {\n        \"$numberInt\": \"1\"\n      }\n    }\n  },\n  \"azure_array_rand_auto_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"array\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgGVERAAAAAAAAAAAAAAAAAEtk14WyoatZcNPlg3y/XJNsBt6neFJeQwR06B9rMGV58oIsmeE5zMtUOBYTgzlnwyKpqI/XVAg8s1VxvsrvGCyLVPwGVyDztwtMgVSW6QM3s=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_array_rand_auto_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"array\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgGVERAAAAAAAAAAAAAAAAAERTO63J4Nj1BpFlqVduA2IrAiGoV4jEOH3FnFgx7ZP7da/YBmLX/bc1EqdpC8v4faHxp74iU0xAB0yW4WgySDX7rriL5cw9sMpqgLRaBxGug=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_array_rand_explicit_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"array\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgGVERAAAAAAAAAAAAAAAAAEs09qQdNVwh+KFqKPREQkw0XFdRNHAvjYJzs5MDE9+QxvtKlmVKSK3wkxDdCrcH4r7ePV2nCy2h1IHYqaDnnt4s5dSawI2l88iTT+bBcCSrU=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_array_rand_explicit_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"array\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgGVERAAAAAAAAAAAAAAAAAEaQ/YL50up4YIMJuVJSiAP06IQ+YjdKLIfkN/prbOZMiXErcD1Vq1hwGhfGdpEsLVu8E7IhJb4wakVC/2dLZoRP95az6HqRRauNNZAIQMKfY=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_array_det_explicit_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"array\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": [\n      {\n        \"$numberInt\": \"1\"\n      },\n      {\n        \"$numberInt\": \"2\"\n      },\n      {\n        \"$numberInt\": \"3\"\n      }\n    ]\n  },\n  \"azure_array_det_explicit_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"array\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": [\n      {\n        \"$numberInt\": \"1\"\n      },\n      {\n        \"$numberInt\": \"2\"\n      },\n      {\n        \"$numberInt\": \"3\"\n      }\n    ]\n  },\n  \"azure_binData=00_rand_auto_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"binData=00\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgGVERAAAAAAAAAAAAAAAAAFl/leuLAHf1p6aRKHdFyN9FM6MW2XzBemql2xQgqkwJ6YOQXW6Pu/aI1scXVOrvrSu3+wBvByjHu++1AqFgzZRQ==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_binData=00_rand_auto_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"binData=00\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgGVERAAAAAAAAAAAAAAAAAF4Nq/LwyufT/mx0LtFSkupNHTuyjbr4yUy1N5/37XhkpqZ1e4sWCHGNaTDEm5+cvdnbqZ/MMkBv855dc8N7vnGA==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_binData=00_rand_explicit_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"binData=00\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgGVERAAAAAAAAAAAAAAAAAFv1Kbv54uXJ76Ih63vtmszQtzkXqDlv8LDCFO3sjzu70+tgRXOhLm3J8uZpwoiNkgM6oNLn0en7tnEekYB9++CA==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_binData=00_rand_explicit_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"binData=00\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgGVERAAAAAAAAAAAAAAAAAFgcYC1n7cGGXpv0qf1Kb8t9y/6kbhscGt2QJkQpAiqadFPPYDU/wwaKdDz94NpAHMZizUbhf9tvZ3UXl1bozhDA==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_binData=00_det_auto_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"binData=00\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AQGVERAAAAAAAAAAAAAAAAAFvswfP3+jgia6rAyrypvbso3Xm4d7MEgJRUCWFYzA+9ov++vmeirgoTp/rFavTNOPb+61fvl1WKbVwrgODusaMg==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_binData=00_det_explicit_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"binData=00\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AQGVERAAAAAAAAAAAAAAAAAFvswfP3+jgia6rAyrypvbso3Xm4d7MEgJRUCWFYzA+9ov++vmeirgoTp/rFavTNOPb+61fvl1WKbVwrgODusaMg==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_binData=00_det_explicit_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"binData=00\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AQGVERAAAAAAAAAAAAAAAAAFvswfP3+jgia6rAyrypvbso3Xm4d7MEgJRUCWFYzA+9ov++vmeirgoTp/rFavTNOPb+61fvl1WKbVwrgODusaMg==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_binData=04_rand_auto_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"binData=04\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgGVERAAAAAAAAAAAAAAAAAFMzMC3BLn/zWE9dxpcD8G0h4aifSY0zSHS9xTVJXgq21s2WU++Ov2UvHatVozmtZltsUN9JvSWqOBQRkFsrXvI7bc4lYfOoOmfpTHFcRDA/c=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_binData=04_rand_auto_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"binData=04\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgGVERAAAAAAAAAAAAAAAAAFDlBN5hUTcjamOg/sgyeG0S52kphsjUgvlpuqHYz6VVdLtZ69cGHOVqqyml3x2rVqWUZJjd4ZodOhlwWq9p+i5IYNot2QaBvi8NZSaiThTc0=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_binData=04_rand_explicit_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"binData=04\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgGVERAAAAAAAAAAAAAAAAAFjvS2ozJuAL3rCvyBpraVtgL91OMdiskmgYnyfKlzd8EhYLd1cL4yxnTUjRXx+W+p8uN0/QZo+mynhcWnwcq83raY+I1HftSTx+S6rZ0qyDM=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_binData=04_rand_explicit_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"binData=04\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgGVERAAAAAAAAAAAAAAAAAFqUMd/I0yOdy5W4THvFc6yrgSzB6arkRs/06b0M9Ii+QtAY6vbz+/aJ0Iy3Jm8TahC1wOZVmTj5luQpr+PHZMCEAFadv+0K/Nsx6xVhAh9gg=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_binData=04_det_auto_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"binData=04\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AQGVERAAAAAAAAAAAAAAAAAFmN+KMrERGmfmue8/hG4D+ZcGzxC2HntdYBLjEolzvS9FV5JH/adxyUAnMpyL8FNznARL51rbv/G1nXPn9mPabsQ4BtWEAQbHx9TiXd+xbB0=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_binData=04_det_explicit_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"binData=04\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AQGVERAAAAAAAAAAAAAAAAAFmN+KMrERGmfmue8/hG4D+ZcGzxC2HntdYBLjEolzvS9FV5JH/adxyUAnMpyL8FNznARL51rbv/G1nXPn9mPabsQ4BtWEAQbHx9TiXd+xbB0=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_binData=04_det_explicit_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"binData=04\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AQGVERAAAAAAAAAAAAAAAAAFmN+KMrERGmfmue8/hG4D+ZcGzxC2HntdYBLjEolzvS9FV5JH/adxyUAnMpyL8FNznARL51rbv/G1nXPn9mPabsQ4BtWEAQbHx9TiXd+xbB0=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_undefined_rand_explicit_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"undefined\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": {\n      \"$undefined\": true\n    }\n  },\n  \"azure_undefined_rand_explicit_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"undefined\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": {\n      \"$undefined\": true\n    }\n  },\n  \"azure_undefined_det_explicit_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"undefined\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": {\n      \"$undefined\": true\n    }\n  },\n  \"azure_undefined_det_explicit_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"undefined\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": {\n      \"$undefined\": true\n    }\n  },\n  \"azure_objectId_rand_auto_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"objectId\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgGVERAAAAAAAAAAAAAAAAAH3sYVJpCKi310YxndMwm5ltEbbiRO1RwZxxeEkzI8tptbNXC8t7RkrT8VSJZ43wbGYCiqH5RZy9v8pYwtUm4STw==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_objectId_rand_auto_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"objectId\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgGVERAAAAAAAAAAAAAAAAAHD7agzVEc0JwesHHhkpGYIDAHQ+3Hc691kqic6YmVvK2N45fD5aRKftaZNs5OxSj3tNHSo7lQ+DVtPj8uSSpsVg==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_objectId_rand_explicit_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"objectId\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgGVERAAAAAAAAAAAAAAAAAHEgKgy2mpMLpfeEWqbvQOaRZAy+cEGXGon3e53/JoH6dZneEyyt4ZrcrK6uRqyUPWX0q104JbCYxfbtHtdzWgPQ==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_objectId_rand_explicit_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"objectId\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgGVERAAAAAAAAAAAAAAAAAHqSv6Nruw3TIi7y0FPRjSfnJmWSdv5XMhAtnHNkT8MVuHeM32ayo0yc8dTA1wlkRtAI5JrGxTfERCXYuCojvvXg==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_objectId_det_auto_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"objectId\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AQGVERAAAAAAAAAAAAAAAAAHcPRjIOyLDUJCDcdWkUySKCFS2AFkIa1OQyQAfC3Zh5HwJ1O7j2o+iYKRerhbni8lBiZH7EUMm1JcxM99lLC5jQ==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_objectId_det_explicit_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"objectId\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AQGVERAAAAAAAAAAAAAAAAAHcPRjIOyLDUJCDcdWkUySKCFS2AFkIa1OQyQAfC3Zh5HwJ1O7j2o+iYKRerhbni8lBiZH7EUMm1JcxM99lLC5jQ==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_objectId_det_explicit_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"objectId\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AQGVERAAAAAAAAAAAAAAAAAHcPRjIOyLDUJCDcdWkUySKCFS2AFkIa1OQyQAfC3Zh5HwJ1O7j2o+iYKRerhbni8lBiZH7EUMm1JcxM99lLC5jQ==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_bool_rand_auto_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"bool\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgGVERAAAAAAAAAAAAAAAAAIYVWPvzSmiCs9LwRlv/AoQWhaS5mzoKX4W26M5eg/gPjOZbEVYOV80pWMxCcZWRAyV/NDWDUmKtRQDMU9b8lCJw==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_bool_rand_auto_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"bool\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgGVERAAAAAAAAAAAAAAAAAIsAB01Ugqtw4T9SkuJBQN1y/ewpRAyz0vjFPdKI+jmPMmaXpMlXDJU8ZbTKm/nh6sjJCFcY5oZJ83ylbp2gHc6w==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_bool_rand_explicit_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"bool\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgGVERAAAAAAAAAAAAAAAAAIr8/qFd564X1mqHEhB0y7bzGFdrHuw+Gk45nXla3VvGHzeIJy6j2Wdl0uziWslMmBvNp8WweW+jQ6E2Fu7SiojQ==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_bool_rand_explicit_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"bool\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgGVERAAAAAAAAAAAAAAAAAIWsca5FAnS2zhHnmKmexvvXMTgsZZ7uAFHnjQassUcay6mvIWH4hOnGiRxt5Zm0wO4S6cZq+PZrmEH5/n9rJcJQ==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_bool_det_explicit_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"bool\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": true\n  },\n  \"azure_bool_det_explicit_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"bool\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": true\n  },\n  \"azure_date_rand_auto_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"date\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgGVERAAAAAAAAAAAAAAAAAJwKo7XW5daIFlwY1mDAnJdHlcUgF+74oViL28hQGhde63pkPyyS6lPkYrc1gcCK5DL7PwsSX4Vb9SsNAG9860xw==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_date_rand_auto_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"date\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgGVERAAAAAAAAAAAAAAAAAJYZdWIqvqTztGKJkSASMEOjyrUFKnYql8fMIEzfEZWx2BYsIkxxOUUUCASg/Jsn09fTLVQ7yLD+LwycuI2uaXsw==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_date_rand_explicit_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"date\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgGVERAAAAAAAAAAAAAAAAAJuWzKqi3KV8GbGGnT7i9N4BACUuNjt5AgKsjWIfrWRXK1+jRQFq0bYlVWaliT9CNIygL2aTF0H4eHl55PAI84MQ==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_date_rand_explicit_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"date\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgGVERAAAAAAAAAAAAAAAAAJ5JTtTuP4zTnEbaVlS/W59SrZ08LOC4ZIl+h+H4RnfHUfBXDwUou+APolVaYko+VZMKecrikdPeewgzWaqazJ1g==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_date_det_auto_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"date\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AQGVERAAAAAAAAAAAAAAAAAJCREIp/SPolAZcVU1iOmaJaN2tFId5HhrjNmhp6xhA1AIPLnN+U7TAqesxFN7iebR9fXI5fZxYNgyWqQC1rqUJw==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_date_det_explicit_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"date\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AQGVERAAAAAAAAAAAAAAAAAJCREIp/SPolAZcVU1iOmaJaN2tFId5HhrjNmhp6xhA1AIPLnN+U7TAqesxFN7iebR9fXI5fZxYNgyWqQC1rqUJw==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_date_det_explicit_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"date\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AQGVERAAAAAAAAAAAAAAAAAJCREIp/SPolAZcVU1iOmaJaN2tFId5HhrjNmhp6xhA1AIPLnN+U7TAqesxFN7iebR9fXI5fZxYNgyWqQC1rqUJw==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_null_rand_explicit_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"null\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": null\n  },\n  \"azure_null_rand_explicit_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"null\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": null\n  },\n  \"azure_null_det_explicit_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"null\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": null\n  },\n  \"azure_null_det_explicit_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"null\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": null\n  },\n  \"azure_regex_rand_auto_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"regex\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgGVERAAAAAAAAAAAAAAAAALsMm3W2ogEiI6m0l8dS5Xhqnw+vMBvN1EesOTqAZOk4tQleX6fWARwUUnjFxbuejU7ISb50fc/Ul+ntL9z/2nHQ==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_regex_rand_auto_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"regex\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgGVERAAAAAAAAAAAAAAAAALITQNQI0hfCeMTxH0Hce1Cf5tinQG+Bq8EolUACvxUUQcDqIXfFXn19tV/Qyj4lIdnnwh/18hiswgEpJRK7uLGw==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_regex_rand_explicit_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"regex\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgGVERAAAAAAAAAAAAAAAAALw/1QI/bKeiGUrrtC+yXOTvxZ2mJjSelPPGOm1mge0ws8DsX0DPHmo6MjhnRO4u0c/LWiE3hwHG2rYjAFlFXZ5A==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_regex_rand_explicit_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"regex\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgGVERAAAAAAAAAAAAAAAAAL6Sl58UfFCHCZzWIB4r19/ZjeSRAoWeTFCFedKiwyR8/xnL+8jzXK/9+vTIspP6j35lFapr+f4iBNB9WjdpYNKA==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_regex_det_auto_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"regex\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AQGVERAAAAAAAAAAAAAAAAALxshM91Tsql/8kPe3dC16oP36XSUIN6godiRVIJLJ+NAwYtEkThthQsln7CrkIxIx6npN6A/hw1CBJERS/cqWhw==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_regex_det_explicit_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"regex\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AQGVERAAAAAAAAAAAAAAAAALxshM91Tsql/8kPe3dC16oP36XSUIN6godiRVIJLJ+NAwYtEkThthQsln7CrkIxIx6npN6A/hw1CBJERS/cqWhw==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_regex_det_explicit_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"regex\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AQGVERAAAAAAAAAAAAAAAAALxshM91Tsql/8kPe3dC16oP36XSUIN6godiRVIJLJ+NAwYtEkThthQsln7CrkIxIx6npN6A/hw1CBJERS/cqWhw==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_dbPointer_rand_auto_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"dbPointer\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgGVERAAAAAAAAAAAAAAAAAMaAd1v/XCYM2Kzi/f4utR6aHOFORmzZ17EepEjkn5IeKshktUpPWjI/dBwSunn5Qxx2zI3nm06c3SDvp6tw8qb7u4qXjLQYhlsQ0bHvvm+vE=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_dbPointer_rand_auto_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"dbPointer\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgGVERAAAAAAAAAAAAAAAAAM6VNjkN9bMIzfC7AX0ZhOEXPpyPE0nzYq3c5TNHrgeGWdZDR9GVdbO9t55zQrQJJ2Mmevh8c0WaAUV+YODv7ty6TDBsPbaKWWqMzu/v9RXHo=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_dbPointer_rand_explicit_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"dbPointer\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgGVERAAAAAAAAAAAAAAAAAM66tywuMhwdyUjxfl7EOdKHNCLeIPnct3PgKrAKlOQFjiNQUIA2ShVy0qYpJcvvFsuQ5e8Bjr0IqeBc8mC7n4euRSM1UXpLqI5XHgXMMaYpI=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_dbPointer_rand_explicit_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"dbPointer\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgGVERAAAAAAAAAAAAAAAAAMtPQEbZ4gWoSYjVZLd5X6j0XxutWY1Ecrys2ErKRgZaxP0uGe8uw0cnr2Z5PYylaYmsSicLwD1PwWY42PKmaGBDraHmdfqDOPvrNxhBrfU/E=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_dbPointer_det_auto_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"dbPointer\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AQGVERAAAAAAAAAAAAAAAAAMxUcVqq6RpAUCv08qGkmjuwVAIgLeYyh7xZnMeCYVGmhJKIP1Zdt1SvRGRV0jzwCQmXgxNd04adRwJnG/PRQIsL9aH3ilJgEnUbOo1nqR7yw=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_dbPointer_det_explicit_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"dbPointer\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AQGVERAAAAAAAAAAAAAAAAAMxUcVqq6RpAUCv08qGkmjuwVAIgLeYyh7xZnMeCYVGmhJKIP1Zdt1SvRGRV0jzwCQmXgxNd04adRwJnG/PRQIsL9aH3ilJgEnUbOo1nqR7yw=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_dbPointer_det_explicit_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"dbPointer\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AQGVERAAAAAAAAAAAAAAAAAMxUcVqq6RpAUCv08qGkmjuwVAIgLeYyh7xZnMeCYVGmhJKIP1Zdt1SvRGRV0jzwCQmXgxNd04adRwJnG/PRQIsL9aH3ilJgEnUbOo1nqR7yw=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_javascript_rand_auto_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"javascript\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgGVERAAAAAAAAAAAAAAAAANWXPb5z3a0S7F26vkmBF3fV+oXYUj15OEtnSlXlUrc+gbhbPDxSvCPnTBEy5sNu4ndkvEZZxYgZInkF2q4rhlfQ==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_javascript_rand_auto_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"javascript\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgGVERAAAAAAAAAAAAAAAAANN4mcwLz/J4eOUknhVsy6kdF1ThDP8cx6dNpOwJWAiyPHEsn+i6JmMTlfQMBrUp9HB/u3R+jLO5yz4XgLUKE8Tw==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_javascript_rand_explicit_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"javascript\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgGVERAAAAAAAAAAAAAAAAANJ+t5Z8hSQaoNzszzkWndAo4A0avDf9bKFa7euznz8ZYInnl9RUVqWMyxjSuIotAvTyYSJzxh+w2hKCgVf+MjEA==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_javascript_rand_explicit_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"javascript\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgGVERAAAAAAAAAAAAAAAAANRLOQFpmkEg/KdWMmaurkNtUhy45rgtoipc9kQz6olgDWiMim81XC0AW5cOvjbHXL3w7Du28Kwdsp4j0PTTXHUQ==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_javascript_det_auto_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"javascript\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AQGVERAAAAAAAAAAAAAAAAANUrNUS/7/dmKVWBd+2JKGEn1hxbFSyu3p5sDNatukG2m16t4WwxzmYAg8PuQbAxekprs7iaLA+7D2Kn3ZuMSQOw==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_javascript_det_explicit_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"javascript\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AQGVERAAAAAAAAAAAAAAAAANUrNUS/7/dmKVWBd+2JKGEn1hxbFSyu3p5sDNatukG2m16t4WwxzmYAg8PuQbAxekprs7iaLA+7D2Kn3ZuMSQOw==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_javascript_det_explicit_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"javascript\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AQGVERAAAAAAAAAAAAAAAAANUrNUS/7/dmKVWBd+2JKGEn1hxbFSyu3p5sDNatukG2m16t4WwxzmYAg8PuQbAxekprs7iaLA+7D2Kn3ZuMSQOw==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_symbol_rand_auto_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"symbol\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgGVERAAAAAAAAAAAAAAAAAORMcgtQSU+/2Qlq57neRrVuAFSeSwkqdo+z1fh6IKjyEzhCy+u5bTzSzTopyKJQTCUZA2mSpRezWkM87oiGfhMFkBRVreMcE62eH+BLlgUaM=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_symbol_rand_auto_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"symbol\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgGVERAAAAAAAAAAAAAAAAAOIKlAw/A3nwHn0tO2cYtJx0azB8MGmXtt+bRptzn8yHlUSpMpYaiU0ssBBiLkmMLAITYebLqDk3NHESyP7PvbSfX1E2XVn2Nf694ZqPWMec8=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_symbol_rand_explicit_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"symbol\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgGVERAAAAAAAAAAAAAAAAAO8SXW76AEr/6D6zyP1RYwmwdVM2AINaXZn3Ipy+fynWTUV6XIPIRR7xMTttNo2zlh7fgXDZ28PmjooGlQzn0q0JVQmXPCIPM3aqAmMcgyuqg=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_symbol_rand_explicit_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"symbol\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgGVERAAAAAAAAAAAAAAAAAOtoJWm2Ucre0foHIiOutsX1WIyub7t3Lby3/F8zRXn+l6ixlTjAPgWFwpRnYg96Lt2ACDDQ9CO51ejr9qk0b8LDBwG3qU5Cuibsp7vo1VsdI=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_symbol_det_auto_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"symbol\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AQGVERAAAAAAAAAAAAAAAAAOvp/FMMmWVMkiuN51uFMFBiRQAcc9jftlNsHsLoNtohZaGni26kgX94b+/EI8pdWF5xA/73JlGlij0Rt+vC9s/zTDItRpn0bJL54WPphDcmA=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_symbol_det_explicit_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"symbol\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AQGVERAAAAAAAAAAAAAAAAAOvp/FMMmWVMkiuN51uFMFBiRQAcc9jftlNsHsLoNtohZaGni26kgX94b+/EI8pdWF5xA/73JlGlij0Rt+vC9s/zTDItRpn0bJL54WPphDcmA=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_symbol_det_explicit_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"symbol\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AQGVERAAAAAAAAAAAAAAAAAOvp/FMMmWVMkiuN51uFMFBiRQAcc9jftlNsHsLoNtohZaGni26kgX94b+/EI8pdWF5xA/73JlGlij0Rt+vC9s/zTDItRpn0bJL54WPphDcmA=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_javascriptWithScope_rand_auto_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"javascriptWithScope\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgGVERAAAAAAAAAAAAAAAAAPCw9NnvJyuTYIgZxr1w1UiG85PGZ4rO62DWWDF98HwVM/Y6u7hNdNjkaWjYFsPMl38ioHw/pS8GFR62QmH2RAw/BV0wI7pNy2evANr3i3gKg=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_javascriptWithScope_rand_auto_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"javascriptWithScope\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgGVERAAAAAAAAAAAAAAAAAPXQzqnQ2UWkIYof8/OfadNMa7iVKAbOaiu7YGm8iVrx+W6uxKLPFugVqHtQ29hYXXf33xr8rqGNxDlAe7/x1OeYEif71f7LUkmKF9WxJV9Ko=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_javascriptWithScope_rand_explicit_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"javascriptWithScope\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgGVERAAAAAAAAAAAAAAAAAP0nxlppgPyjLx0eBempbOlL21G6KbABSrE6+YuNDcsjJjxCQuLR9+aoAwa+yCDEC7GZ1E3oP489edKUuNpE4Ts26jy4aRegu4DmyECUeBwAg=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_javascriptWithScope_rand_explicit_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"javascriptWithScope\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgGVERAAAAAAAAAAAAAAAAAPO89afu9Sb+cK9wwM1cO1DPjvu5UNyObjjTScy1hy9PzllJGfj7b84f0Ah74jPYsMPwI0Eslu/IYF3+5jmquq5Qp/VUQESlxqRqRK0xIeMfs=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_javascriptWithScope_det_explicit_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"javascriptWithScope\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": {\n      \"$code\": \"x=1\",\n      \"$scope\": {}\n    }\n  },\n  \"azure_javascriptWithScope_det_explicit_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"javascriptWithScope\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": {\n      \"$code\": \"x=1\",\n      \"$scope\": {}\n    }\n  },\n  \"azure_int_rand_auto_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"int\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgGVERAAAAAAAAAAAAAAAAAQUyy4uWmWdzypsK81q9egREg4s80X3L2hzxJzC+fL08Xzy1z9grpPPCfJrluUVKMMGmmZR8gJPJ70igN3unJbzg==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_int_rand_auto_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"int\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgGVERAAAAAAAAAAAAAAAAAQr4gyoHKpGsSJo8CMsYSJk/KilFMJhsDCmxrha7yfNW1uR5sjyZj4B4s6uTXGw76x7aR/AvecDlY3QFJb8L1mjg==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_int_rand_explicit_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"int\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgGVERAAAAAAAAAAAAAAAAAQ0zgXYPV1MuEFksmDpVDoWkoZQelm3+rYrMiT64KYywO//75799W8TbR3a7O6Q/ErjKQOin2OCp8EWwZqTDdz5w==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_int_rand_explicit_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"int\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgGVERAAAAAAAAAAAAAAAAAQG+qz00yizREbP3tla1elMiwf8TKLbUU2XWUP+E0vey/wvbjTTIzqwUlz/b9St77CHJhavypP3hMrngXR9GapbQ==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_int_det_auto_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"int\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AQGVERAAAAAAAAAAAAAAAAAQCkJH+CataLqp/xBjO77QBprC2xPV+rE+goSZ3C6aqwXIeTYHTOqEbeaFb5iZcqYH5nWvNvnfbZSIMyvSfrPjhw==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_int_det_explicit_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"int\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AQGVERAAAAAAAAAAAAAAAAAQCkJH+CataLqp/xBjO77QBprC2xPV+rE+goSZ3C6aqwXIeTYHTOqEbeaFb5iZcqYH5nWvNvnfbZSIMyvSfrPjhw==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_int_det_explicit_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"int\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AQGVERAAAAAAAAAAAAAAAAAQCkJH+CataLqp/xBjO77QBprC2xPV+rE+goSZ3C6aqwXIeTYHTOqEbeaFb5iZcqYH5nWvNvnfbZSIMyvSfrPjhw==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_timestamp_rand_auto_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"timestamp\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgGVERAAAAAAAAAAAAAAAAARwcXYtx+A7g/zGkjGdkyVxZGCO9Nzj3D70NIpl2TeH2j9qYGP4DenwL1xSgrL2Ez+X58d2BvNhKrjA9y2w1Z8kA==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_timestamp_rand_auto_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"timestamp\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgGVERAAAAAAAAAAAAAAAAARQ0Pjx3l92Aqhn2e1hot2M9rQ6aLPE2Iw8AVhm5AD8FWywWih12Fn2p9+kiE33yKPOCyrTWQHKPtB4yYhqnJgGg==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_timestamp_rand_explicit_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"timestamp\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgGVERAAAAAAAAAAAAAAAAARvFMlIzh2IjpHkTJ8buqTOqBA0+CxVDsZacUhSHVMgJLN+0DJsJy8OfkmKMu9Lk5hULY00Udoja87x+79mYfmeQ==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_timestamp_rand_explicit_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"timestamp\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgGVERAAAAAAAAAAAAAAAAAR+2SCd7V5ukAkh7CYpNPIatzTL8osNoA4Mb5jjjbos8eMamImw0fbH8YA+Rdm4CgGdQQ9VDX7MtMWlArkj0Jpew==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_timestamp_det_auto_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"timestamp\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AQGVERAAAAAAAAAAAAAAAAARe72T/oC09QGE1vuljb6ZEHa6llEwMLT+C4s9u1fREkOKndpmrOlGE8zOey4teizY1ypOMkIZ8GDQJJ4kLSpNkQ==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_timestamp_det_explicit_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"timestamp\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AQGVERAAAAAAAAAAAAAAAAARe72T/oC09QGE1vuljb6ZEHa6llEwMLT+C4s9u1fREkOKndpmrOlGE8zOey4teizY1ypOMkIZ8GDQJJ4kLSpNkQ==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_timestamp_det_explicit_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"timestamp\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AQGVERAAAAAAAAAAAAAAAAARe72T/oC09QGE1vuljb6ZEHa6llEwMLT+C4s9u1fREkOKndpmrOlGE8zOey4teizY1ypOMkIZ8GDQJJ4kLSpNkQ==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_long_rand_auto_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"long\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgGVERAAAAAAAAAAAAAAAAASSSgX7k8iw0xFe0AiIzOu0e0P7Ujyfsk/Cdl0fR5X8V3QLVER+1Qa47Qpb8iWL2VLBSh+55HvIEtvhWn8SwXaog==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_long_rand_auto_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"long\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgGVERAAAAAAAAAAAAAAAAASUhKr5K7ulGTeFbhIvJ2DDE10gRAFn5+2zqnsIFSY8lYV2PBYcENdeNBXZs6kyIAYhJdQyuOChVCerTI5jmQWDw==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_long_rand_explicit_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"long\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgGVERAAAAAAAAAAAAAAAAASHxawpjTHdXYRWQSZ7Qi7gFC+o4dW2mPH8s5nQkPFY/EubcJbdAZ5HFp66NfPaDJ/NSH6Vy+TkpX3683RC+bjSQ==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_long_rand_explicit_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"long\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgGVERAAAAAAAAAAAAAAAAASVaMAv6UjuBOUZMJ9qz+58TQWmgaMpS9xrJziJY80ml9aRlDTtRubP7U40CgbDvrtY1QgHbkF/di1XDCB6iXMMg==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_long_det_auto_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"long\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AQGVERAAAAAAAAAAAAAAAAAS06L8oEPeMvVlA32VlobdOWG24OoyMbv9PyYsHLsbT0bHFwU7lYUSQG9EkYVRNPEDzvXpciE1jT7KT8CRY8XT/g==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_long_det_explicit_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"long\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AQGVERAAAAAAAAAAAAAAAAAS06L8oEPeMvVlA32VlobdOWG24OoyMbv9PyYsHLsbT0bHFwU7lYUSQG9EkYVRNPEDzvXpciE1jT7KT8CRY8XT/g==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_long_det_explicit_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"long\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AQGVERAAAAAAAAAAAAAAAAAS06L8oEPeMvVlA32VlobdOWG24OoyMbv9PyYsHLsbT0bHFwU7lYUSQG9EkYVRNPEDzvXpciE1jT7KT8CRY8XT/g==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_decimal_rand_auto_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"decimal\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgGVERAAAAAAAAAAAAAAAAATJ6LZgPu9F+rPtYsMuvwOx62+g1dAk858BUtE9FjC/300DnbDiolhkHNcyoFs07NYUNgLthW2rISb/ejmsDCt/oqnf8zWYf9vrJEfHaS/Ocw=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_decimal_rand_auto_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"decimal\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgGVERAAAAAAAAAAAAAAAAATX8eD6qFYWKwIGvXtQG79fXKuPW9hkIV0OwrmNNIqRltw6gPHl+/1X8Q6rgmjCxqvhB05AxTj7xz64gP+ILkPQY8e8VGuCOvOdwDo2IPwy18=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_decimal_rand_explicit_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"decimal\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgGVERAAAAAAAAAAAAAAAAATBjQ9E5wDdTS/iI1XDqGmDBC5aLbPB4nSyrjRLfv1zEoPRjmcHlQmMRJA0mori2VQv6EBFNHeczFCenJaSAkuh77czeXM2vH3T6qwEIDs4dw=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_decimal_rand_explicit_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"decimal\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AgGVERAAAAAAAAAAAAAAAAATtkjbhdve7MNuLaTm6qvaewuVUxeC1DMz1fd4RC4jeiBFMd5uZUVJTiOIerwQ6P5G5lkMlezKDWgKl2FUvZH6c7V3JknhsaWcV5iLWGUL6Zc=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"azure_decimal_det_explicit_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"decimal\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": {\n      \"$numberDecimal\": \"1.234\"\n    }\n  },\n  \"azure_decimal_det_explicit_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"decimal\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": {\n      \"$numberDecimal\": \"1.234\"\n    }\n  },\n  \"azure_minKey_rand_explicit_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"minKey\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": {\n      \"$minKey\": 1\n    }\n  },\n  \"azure_minKey_rand_explicit_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"minKey\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": {\n      \"$minKey\": 1\n    }\n  },\n  \"azure_minKey_det_explicit_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"minKey\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": {\n      \"$minKey\": 1\n    }\n  },\n  \"azure_minKey_det_explicit_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"minKey\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": {\n      \"$minKey\": 1\n    }\n  },\n  \"azure_maxKey_rand_explicit_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"maxKey\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": {\n      \"$maxKey\": 1\n    }\n  },\n  \"azure_maxKey_rand_explicit_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"maxKey\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": {\n      \"$maxKey\": 1\n    }\n  },\n  \"azure_maxKey_det_explicit_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"maxKey\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": {\n      \"$maxKey\": 1\n    }\n  },\n  \"azure_maxKey_det_explicit_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"maxKey\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": {\n      \"$maxKey\": 1\n    }\n  },\n  \"gcp_double_rand_auto_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"double\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AhgjwAAAAAAAAAAAAAAAAAABFoHQxnh1XSC0k1B01uFFg7rE9sZVBn4PXo26JX8gx9tuxu+4l9Avb23H9BfOzuWiEc43iw87K/W2y0VfKp5CCg==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_double_rand_auto_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"double\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AhgjwAAAAAAAAAAAAAAAAAABRkZkEtQEFB/r268cNfYRQbN4u5Cxjl9Uh+8wq9TFWLQH2E/9wj2vTLlxQ2cQsM7Qd+XxR5idjfBf9CKAfvUa/A==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_double_rand_explicit_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"double\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AhgjwAAAAAAAAAAAAAAAAAABDSUZ+0BbDDEZxCXA+J2T6Js8Uor2dfXSf7s/hpLrg6dxcW2chpht9XLiLOXG5w83TzCAI5pF8cQgBpBpYjR8RQ==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_double_rand_explicit_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"double\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AhgjwAAAAAAAAAAAAAAAAAABCYxugs7L+4S+1rr0VILSbtBm79JPTLuzluQAv0+8hbu5Z6zReOL6Ta1vQH1oA+pSPGYA4euye3zNl1X6ZewbPw==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_double_det_explicit_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"double\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": {\n      \"$numberDouble\": \"1.2339999999999999858\"\n    }\n  },\n  \"gcp_double_det_explicit_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"double\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": {\n      \"$numberDouble\": \"1.2339999999999999858\"\n    }\n  },\n  \"gcp_string_rand_auto_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"string\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AhgjwAAAAAAAAAAAAAAAAAACx3wSslJEiD80YLTH0n4Bbs4yWVPQl15AU8pZMLLQePqEtI+BJy3t2bqNP1098jS0CGSf+LQmQvXhJn1aNFeMTw==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_string_rand_auto_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"string\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AhgjwAAAAAAAAAAAAAAAAAAC5BTe5KP5UxSIk6dJlkz8aaZ/9fg44XPWHafiiL/48lcv3AWbu2gcBo1EDuc1sJQu6XMrtDCRQ7PCHsL7sEQMGQ==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_string_rand_explicit_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"string\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AhgjwAAAAAAAAAAAAAAAAAACyJN55OcyXXJ71x8VphTaIuIg6kQtGgVKPhWx0LSdYc6JOjB6LTdA7SEWiSlSWWFZE26UmKcPbkbLDAYf4IVrzQ==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_string_rand_explicit_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"string\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AhgjwAAAAAAAAAAAAAAAAAACoa0d9gqfPP5s3+GoruwzxoQFgli8SmjpTVRLAOcFxqGdfrwSbpYffSw/OR45sZPxXCL6T2MtUvZsl7ukv0jBnw==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_string_det_auto_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"string\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"ARgjwAAAAAAAAAAAAAAAAAACTCkyETcWayIZ9YEoQEBVIF3i7iXEe6M3KjYYaSVCYdqSbSHBzlwKWYbP+Xj/MMYBYTLZ1aiRQWCMK4gWPYppZw==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_string_det_explicit_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"string\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"ARgjwAAAAAAAAAAAAAAAAAACTCkyETcWayIZ9YEoQEBVIF3i7iXEe6M3KjYYaSVCYdqSbSHBzlwKWYbP+Xj/MMYBYTLZ1aiRQWCMK4gWPYppZw==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_string_det_explicit_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"string\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"ARgjwAAAAAAAAAAAAAAAAAACTCkyETcWayIZ9YEoQEBVIF3i7iXEe6M3KjYYaSVCYdqSbSHBzlwKWYbP+Xj/MMYBYTLZ1aiRQWCMK4gWPYppZw==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_object_rand_auto_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"object\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AhgjwAAAAAAAAAAAAAAAAAADy+8fkyeNYdIK001YogXfKc25zRXS1VGIFVWR6jRfrexy9C8LBBfX3iDwGNPbP2pkC3Tq16OoziQB6iNGf7s7yg==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_object_rand_auto_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"object\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AhgjwAAAAAAAAAAAAAAAAAADixoDdvm57gH8ooOaKI57WyZD5uaPmuYgmrgAFuV8I+oaalqYctnNSYlzQKCMQX/mIcTxvW3oOWY7+IzAz7npvw==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_object_rand_explicit_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"object\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AhgjwAAAAAAAAAAAAAAAAAADvq0OAoijgHaVMhsoNMdfWFLyISDo6Y13sYM0CoBXS/oXJNIJJvhgKPbFSV/h4IgiDLy4qNYOTJQvpqt094RPgQ==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_object_rand_explicit_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"object\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AhgjwAAAAAAAAAAAAAAAAAADuTZF7/uqGjFbjzBYspPkxGWvvVAEN/ib8bfPOQrEobtTWuU+ju9H3TlT9DMuFy7RdUZnPB0D3HkM8+zky5xeBw==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_object_det_explicit_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"object\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": {\n      \"x\": {\n        \"$numberInt\": \"1\"\n      }\n    }\n  },\n  \"gcp_object_det_explicit_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"object\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": {\n      \"x\": {\n        \"$numberInt\": \"1\"\n      }\n    }\n  },\n  \"gcp_array_rand_auto_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"array\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AhgjwAAAAAAAAAAAAAAAAAAE085kJIBX6S93D94bcRjkOegEKsksi2R1cxoVDoOpSdHh3S6bZAOh50W405wvnOKf3KTP9SICDUehQKQZSC026Y5dwVQ2GiM7PtpSedthKJs=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_array_rand_auto_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"array\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AhgjwAAAAAAAAAAAAAAAAAAEk/FAXsaqyVr6I+MY5L0axeLhskcEfLZeB8whLMKbjLDLa8Iep+IdrFVSfKo03Zr/7Ah8Js01aT6+Vt4EDMJK0mGKZJOjsrAf3b6RS+Mzebg=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_array_rand_explicit_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"array\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AhgjwAAAAAAAAAAAAAAAAAAEDY7J9JGiurctYr7ytakNjcryVm42fkubcVpQpUYEkpK/G9NLGjrJuFgNW5ZVjYiPKEBbDB7vEtJqGux0BU++hrvVHNJ3wUT2mbDE18NE4KE=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_array_rand_explicit_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"array\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AhgjwAAAAAAAAAAAAAAAAAAErFFlw8W9J2y+751RnYLw0TSK9ThD6sP3i4zPbZtiuhc90RFoJhScvqM9i4sDKuYePZZRLBxdX4EZhZClOmswCGDLCIWsQlSvCwgDcIsRR/w=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_array_det_explicit_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"array\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": [\n      {\n        \"$numberInt\": \"1\"\n      },\n      {\n        \"$numberInt\": \"2\"\n      },\n      {\n        \"$numberInt\": \"3\"\n      }\n    ]\n  },\n  \"gcp_array_det_explicit_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"array\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": [\n      {\n        \"$numberInt\": \"1\"\n      },\n      {\n        \"$numberInt\": \"2\"\n      },\n      {\n        \"$numberInt\": \"3\"\n      }\n    ]\n  },\n  \"gcp_binData=00_rand_auto_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"binData=00\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AhgjwAAAAAAAAAAAAAAAAAAF0R5BNkQKfm6wx/tob8nVGDEYV/pvy9UeCqc9gFNuB5d9KxCkgyxryV65rbB90OriqvWFO2jcxzchRYgRI3fQ+A==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_binData=00_rand_auto_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"binData=00\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AhgjwAAAAAAAAAAAAAAAAAAF4wcT8XGc3xNdKYDX5/cbUwPDdnkIXlWWCCYeSXSk2oWPxMZnPsVQ44nXKJJsKitoE3r/hL1sSG5239WzCWyx9g==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_binData=00_rand_explicit_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"binData=00\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AhgjwAAAAAAAAAAAAAAAAAAF07OFs5mlx0AB6QBanaybLuhuFbG+19KxSqHlSgELcz6TQKI6equX97OZdaWSWf2SSeiYm5E6+Y3lgA5l4KxC2A==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_binData=00_rand_explicit_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"binData=00\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AhgjwAAAAAAAAAAAAAAAAAAFZ74Q7JMm7y2i3wRmjIRKefhmdnrhP1NXJgploi+44eQ2eRraZsW7peGPYyIfsXEbhgV5+aLmiYgvemBywfdogQ==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_binData=00_det_auto_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"binData=00\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"ARgjwAAAAAAAAAAAAAAAAAAFhwJkocj36WXoY3mg2GWUrJ5IQTo9MvkwEwRFKdkcxm9pX2PZPK7bN5ZWw3IFcQ/0GfaW6V4LYr8WarZdLF0p5g==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_binData=00_det_explicit_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"binData=00\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"ARgjwAAAAAAAAAAAAAAAAAAFhwJkocj36WXoY3mg2GWUrJ5IQTo9MvkwEwRFKdkcxm9pX2PZPK7bN5ZWw3IFcQ/0GfaW6V4LYr8WarZdLF0p5g==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_binData=00_det_explicit_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"binData=00\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"ARgjwAAAAAAAAAAAAAAAAAAFhwJkocj36WXoY3mg2GWUrJ5IQTo9MvkwEwRFKdkcxm9pX2PZPK7bN5ZWw3IFcQ/0GfaW6V4LYr8WarZdLF0p5g==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_binData=04_rand_auto_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"binData=04\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AhgjwAAAAAAAAAAAAAAAAAAFmDO47RTVXzm8D4hfhLICILrQJg3yOwG3HYfCdz7yaanPow2Y6bMxvXxk+kDS29aS8pJKDqJQQoMGc1ZFD3yYKsLQHRi/8rW6TNDQd4sCQ00=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_binData=04_rand_auto_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"binData=04\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AhgjwAAAAAAAAAAAAAAAAAAFpiu9Q3LTuPmgdWBqo5Kw0vGF9xU1rMyE4xwR8GccZ7ZMrUcR4AnZnAP7ah5Oz8e7qonNYX4d09obesYSLlIjyK7J7qg+GWiEURgbvmOngaA=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_binData=04_rand_explicit_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"binData=04\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AhgjwAAAAAAAAAAAAAAAAAAFHRy8dveGuMng9WMmadIp39jD7iEfl3bEjKmzyNoAc0wIcSJZo9kdGbNEwZ4p+A1gz273fmAt/AJwAxwvqdlanLWBr4wiSKz1Mu9VaBcTlyY=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_binData=04_rand_explicit_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"binData=04\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AhgjwAAAAAAAAAAAAAAAAAAFiqO+sKodqXuVox0zTbKuY4Ng0QE1If2hDLWXljAEZdYABPk20UJyL/CHR49WP2Cwvi4evJCf8sEfKpR+ugPiyxWzP3iVe6qqTzP93BBjqoc=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_binData=04_det_auto_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"binData=04\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"ARgjwAAAAAAAAAAAAAAAAAAFEp5Gut6iENHUqDMVdBm4cxQy35gnslTf7vSWW9InFh323BvaTTiubxbxTiMKIa/u47MfMprL9HNQSwgpAQc4lped+YnlRW8RYvTcG4frFtA=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_binData=04_det_explicit_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"binData=04\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"ARgjwAAAAAAAAAAAAAAAAAAFEp5Gut6iENHUqDMVdBm4cxQy35gnslTf7vSWW9InFh323BvaTTiubxbxTiMKIa/u47MfMprL9HNQSwgpAQc4lped+YnlRW8RYvTcG4frFtA=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_binData=04_det_explicit_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"binData=04\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"ARgjwAAAAAAAAAAAAAAAAAAFEp5Gut6iENHUqDMVdBm4cxQy35gnslTf7vSWW9InFh323BvaTTiubxbxTiMKIa/u47MfMprL9HNQSwgpAQc4lped+YnlRW8RYvTcG4frFtA=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_undefined_rand_explicit_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"undefined\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": {\n      \"$undefined\": true\n    }\n  },\n  \"gcp_undefined_rand_explicit_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"undefined\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": {\n      \"$undefined\": true\n    }\n  },\n  \"gcp_undefined_det_explicit_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"undefined\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": {\n      \"$undefined\": true\n    }\n  },\n  \"gcp_undefined_det_explicit_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"undefined\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": {\n      \"$undefined\": true\n    }\n  },\n  \"gcp_objectId_rand_auto_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"objectId\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AhgjwAAAAAAAAAAAAAAAAAAH8Kt6coc8bPI4QIwS1tIdk6pPA05xlZvrOyAQgvoqaozMtWzG15OunQLDdS3yJ5WRiV7kO6CIKqRrvL2RykB5sw==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_objectId_rand_auto_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"objectId\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AhgjwAAAAAAAAAAAAAAAAAAHU5Yzmz2mbgNQrGSvglgVuv14nQWzipBkZUVSO4eYZ7wLrj/9t0fnizsu7Isgg5oA9fV0Snh/A9pDnHZWoccXUw==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_objectId_rand_explicit_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"objectId\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AhgjwAAAAAAAAAAAAAAAAAAHsdq5/FLqbjMDiNzf+6k9yxUtFVjS/xSqErqaboOl21934pAzgkOzBGodpKKFuK0Ta4f3h21XS+84wlIYPMlTtw==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_objectId_rand_explicit_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"objectId\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AhgjwAAAAAAAAAAAAAAAAAAHokIdXxNQ/NBMdMAVNxyVuz/J5pMMdtfxxJxr7PbsRJ3FoD2QNjTgE1Wsz0G4o09Wv9UWD+/mIqPVlLgx1sRtPw==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_objectId_det_auto_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"objectId\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"ARgjwAAAAAAAAAAAAAAAAAAHkcbaj3Hy3b4HkjRkMgiw5h6jBW7Sc56QSJmAPmVSc2T4B8d79A49dW0RyEiInZJcnVRjrYzUTRtgRaG4/FRd8g==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_objectId_det_explicit_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"objectId\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"ARgjwAAAAAAAAAAAAAAAAAAHkcbaj3Hy3b4HkjRkMgiw5h6jBW7Sc56QSJmAPmVSc2T4B8d79A49dW0RyEiInZJcnVRjrYzUTRtgRaG4/FRd8g==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_objectId_det_explicit_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"objectId\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"ARgjwAAAAAAAAAAAAAAAAAAHkcbaj3Hy3b4HkjRkMgiw5h6jBW7Sc56QSJmAPmVSc2T4B8d79A49dW0RyEiInZJcnVRjrYzUTRtgRaG4/FRd8g==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_bool_rand_auto_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"bool\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AhgjwAAAAAAAAAAAAAAAAAAIf7vUYS5XFrEU4g03lzj9dk8a2MkaQdlH8nE/507D2Gm5XKQLi2jCENZ9UaQm3MQtVr4Uqrgz2GZiQHt9mXcG3w==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_bool_rand_auto_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"bool\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AhgjwAAAAAAAAAAAAAAAAAAIdOC4Tx/TaVLRtOL/Qh8RUFIzHFB6nSegZoITwZeDethd8V3+R+aIAgzfN3pvmZzagHyVCm2nbNYJNdjOJhuDrg==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_bool_rand_explicit_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"bool\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AhgjwAAAAAAAAAAAAAAAAAAIzB14mX2vaZdiW9kGc+wYEgTCXA0FB5AVEyuERD00+K7U5Otlc6ZUwMtb9nGUu+M7PnnfxiDFHCrUWrTkAZzSUw==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_bool_rand_explicit_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"bool\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AhgjwAAAAAAAAAAAAAAAAAAIhRLg79ACCMfeERBgG1wirirrZXZzbK11RxHkAbf14Fji2L3sdMBdLBU5I028+rmtDdC7khcNMt11V6XGKpAjnA==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_bool_det_explicit_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"bool\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": true\n  },\n  \"gcp_bool_det_explicit_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"bool\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": true\n  },\n  \"gcp_date_rand_auto_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"date\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AhgjwAAAAAAAAAAAAAAAAAAJL+mjI8xBmSahOOi3XkGRGxjhGNdJb445KZtRAaUdCV0vMKbrefuiDHJDPCYo7mLYNhRSIhQfs63IFYMrlKP26A==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_date_rand_auto_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"date\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AhgjwAAAAAAAAAAAAAAAAAAJbeyqO5FRmqvPYyOb0tdKtK6JOg8QKbCl37/iFeEm7N0T0Pjb8Io4U0ndB3O6fjokc3kDQrZcQkV+OFWIMuKFjw==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_date_rand_explicit_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"date\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AhgjwAAAAAAAAAAAAAAAAAAJVz3rSYIcoYtM0tZ8pB2Ytgh8RvYPeZvW7aUVJfZkZlIhfUHOHEf5kHqxzt8E1l2n3lmK/7ZVCFUuCCmr8cZyWw==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_date_rand_explicit_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"date\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AhgjwAAAAAAAAAAAAAAAAAAJAiQqNyUcpuDEpFt7skp2NSHFCux2XObrIIFgXReYgtWoapL/n4zksJXl89PGavzNPBZbzgEa8uwwAe+S+Y6TLg==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_date_det_auto_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"date\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"ARgjwAAAAAAAAAAAAAAAAAAJmATV2A1P5DmrS8uES6AMD9y+EU3x7u4K4J0p296iSkCEgIdZZORhPIEnuJK3FHw1II6IEShW2nd7sOJRZSGKcg==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_date_det_explicit_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"date\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"ARgjwAAAAAAAAAAAAAAAAAAJmATV2A1P5DmrS8uES6AMD9y+EU3x7u4K4J0p296iSkCEgIdZZORhPIEnuJK3FHw1II6IEShW2nd7sOJRZSGKcg==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_date_det_explicit_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"date\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"ARgjwAAAAAAAAAAAAAAAAAAJmATV2A1P5DmrS8uES6AMD9y+EU3x7u4K4J0p296iSkCEgIdZZORhPIEnuJK3FHw1II6IEShW2nd7sOJRZSGKcg==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_null_rand_explicit_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"null\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": null\n  },\n  \"gcp_null_rand_explicit_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"null\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": null\n  },\n  \"gcp_null_det_explicit_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"null\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": null\n  },\n  \"gcp_null_det_explicit_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"null\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": null\n  },\n  \"gcp_regex_rand_auto_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"regex\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AhgjwAAAAAAAAAAAAAAAAAALiebb3hWwJRqlgVEhLYKKvo6cnlU7BFnZnvlZ8GuIr11fUvcnS9Tg2m7vPmfL7WVyuNrXlR48x28Es49YuaxuIg==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_regex_rand_auto_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"regex\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AhgjwAAAAAAAAAAAAAAAAAALouDFNLVgBXqhJvBRj9DKacuD1AQ2NAVDW93P9NpZDFFwGOFxmKUcklbPj8KkHqvma8ovVUBTLLUDR+tKFRvC2Q==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_regex_rand_explicit_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"regex\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AhgjwAAAAAAAAAAAAAAAAAALtdcT9+3R1he4eniT+1opqs/YtujFlqzBXssv+hCKhJQVY/IXde32nNpQ1WTgUc7jfIJl/v9HvuA9cDHPtDWWTg==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_regex_rand_explicit_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"regex\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AhgjwAAAAAAAAAAAAAAAAAALAwlRAlj4Zpn+wu9eOcs5CsNgrkVwrgmu1tc4wyQp0Lt+3UcplYsXQMrMPcTx3yB0JcI4Kh65n/DrAaA+G/a6iw==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_regex_det_auto_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"regex\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"ARgjwAAAAAAAAAAAAAAAAAALbCutQ7D94gk0djewcQiEdMFVVa21+Dn5enQf/mqPi3o7vPy7OejDBk9fiZRffsioRMhlx2cxqa8T3+AkeN96yg==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_regex_det_explicit_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"regex\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"ARgjwAAAAAAAAAAAAAAAAAALbCutQ7D94gk0djewcQiEdMFVVa21+Dn5enQf/mqPi3o7vPy7OejDBk9fiZRffsioRMhlx2cxqa8T3+AkeN96yg==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_regex_det_explicit_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"regex\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"ARgjwAAAAAAAAAAAAAAAAAALbCutQ7D94gk0djewcQiEdMFVVa21+Dn5enQf/mqPi3o7vPy7OejDBk9fiZRffsioRMhlx2cxqa8T3+AkeN96yg==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_dbPointer_rand_auto_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"dbPointer\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AhgjwAAAAAAAAAAAAAAAAAAMG8P+Y2YNIgknxE0/yPDCHASBvCU1IJwsEyaJPuOjn03enxEN7z/wbjVMN0lGUptDP3SVL+OIZtQ35VRP84MtnbdhcfZWqMhLjzrCjmtHUEg=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_dbPointer_rand_auto_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"dbPointer\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AhgjwAAAAAAAAAAAAAAAAAAMKCLFUN6ApB5fSVEWazRddhKTEwgqI/mxfe0BBxht69pZQYhTjhOJP0YcIrtr+RCeHOa4FIJgQod1CFOellIzO5YH5CuV4wPxCAlOdbJcBK8=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_dbPointer_rand_explicit_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"dbPointer\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AhgjwAAAAAAAAAAAAAAAAAAM7ULEA6uKKv4Pu4Sa3aAt7dXtEwfQC98aJoLBapHT+xXtn5GWPynOZQNtV3lGaYExQjiGdYbzOcav3SVy/sYTe3ktgkQnuZfe0tk0zyvKIMM=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_dbPointer_rand_explicit_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"dbPointer\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AhgjwAAAAAAAAAAAAAAAAAAMoMveHO1MadAKuT498xiKWWBUKRbH7k7P2YETDg/BufVw0swos07rk6WJa1vqyF61QEmACjy4pmlK/5P0VfKJBAIvif51YqHPQkobJVS3nVA=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_dbPointer_det_auto_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"dbPointer\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"ARgjwAAAAAAAAAAAAAAAAAAMz+9m1bE+Th9YeyPmJdtJPO0F5QYsGYtU/Eom/LSoYjDmTmV2ehkKx/cevIxJfZUc+Mvv/uGoeuubGl8tiX4l+f6yLrSIS6QBtIHYKXk+JNE=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_dbPointer_det_explicit_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"dbPointer\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"ARgjwAAAAAAAAAAAAAAAAAAMz+9m1bE+Th9YeyPmJdtJPO0F5QYsGYtU/Eom/LSoYjDmTmV2ehkKx/cevIxJfZUc+Mvv/uGoeuubGl8tiX4l+f6yLrSIS6QBtIHYKXk+JNE=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_dbPointer_det_explicit_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"dbPointer\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"ARgjwAAAAAAAAAAAAAAAAAAMz+9m1bE+Th9YeyPmJdtJPO0F5QYsGYtU/Eom/LSoYjDmTmV2ehkKx/cevIxJfZUc+Mvv/uGoeuubGl8tiX4l+f6yLrSIS6QBtIHYKXk+JNE=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_javascript_rand_auto_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"javascript\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AhgjwAAAAAAAAAAAAAAAAAANqBD0ITMn4BaFnDp7BX7vXbRBkFwmjQRVUeBbwsQtv5WVlJMAd/2+w7tyH8Wc44x0/9U/DA5GVhpTrtdDyPBI3w==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_javascript_rand_auto_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"javascript\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AhgjwAAAAAAAAAAAAAAAAAANtA0q4mbkAaKX4x1xk0/094Mln0wnh2bYnI6s6dh+l2WLDH7A9JMZxCl6kc4uOsEfbOvjP/PLIYtdMGs14EjM5A==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_javascript_rand_explicit_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"javascript\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AhgjwAAAAAAAAAAAAAAAAAANfrW3pmeiFdBFt5tJS6Auq9Wo/J4r/vMRiueLWxig5S1zYuf9kFPJMK/nN9HqQPIcBIJIC2i/uEPgeepaNXACCw==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_javascript_rand_explicit_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"javascript\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AhgjwAAAAAAAAAAAAAAAAAANL7UZNzpwfwhRn/HflWIE9CSxGYNwLSo9d86HsOJ42rrZKq6HQqm/hiEAg0lyqCxVIVFxYEc2BUWSaq4/+SSyZw==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_javascript_det_auto_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"javascript\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"ARgjwAAAAAAAAAAAAAAAAAANB2d97R8nUJqnG0JPsWzyFe5pct5jvUljdkPnlZvLN1ZH+wSu4WmLfjri6IzzYP//f8tywn4Il+R4lZ0Kr/RAeA==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_javascript_det_explicit_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"javascript\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"ARgjwAAAAAAAAAAAAAAAAAANB2d97R8nUJqnG0JPsWzyFe5pct5jvUljdkPnlZvLN1ZH+wSu4WmLfjri6IzzYP//f8tywn4Il+R4lZ0Kr/RAeA==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_javascript_det_explicit_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"javascript\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"ARgjwAAAAAAAAAAAAAAAAAANB2d97R8nUJqnG0JPsWzyFe5pct5jvUljdkPnlZvLN1ZH+wSu4WmLfjri6IzzYP//f8tywn4Il+R4lZ0Kr/RAeA==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_symbol_rand_auto_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"symbol\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AhgjwAAAAAAAAAAAAAAAAAAOsGdnr6EKcBdOAvYrP0o1pWbhhJbYsqfVwwwS1zq6ZkBayOss2J3TuYwBGXhJFlq3iIiWLdxGQ883XIvuAECnqUNuvpK2rOLwtDg8xJLiH24=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_symbol_rand_auto_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"symbol\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AhgjwAAAAAAAAAAAAAAAAAAOpfa6CUSnJBvnWdd7pSZ2pXAbYm68Yka6xa/fuyhVx/Tc926/JpqmOmQtXqbOj8dZra0rQ3/yxHySwgD7s9Qr+xvyL7LvAguGkGmEV5H4Xz4=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_symbol_rand_explicit_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"symbol\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AhgjwAAAAAAAAAAAAAAAAAAO085iqYGFdtjiFWHcNqE0HuKMNHmk49DVh+pX8Pb4p3ehB57JL1nRqaXqHPqhFenxSEInT/te9HQRr+ADcHADvUGsScfm/n85v85nq6X+5y4=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_symbol_rand_explicit_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"symbol\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AhgjwAAAAAAAAAAAAAAAAAAOiidb+2TsbAb2wc7MtDzb/UYsjgVNSw410Sz9pm+Uy7aZROE5SURKXdLjrCH2ZM2a+XCAl3o9yAoNgmAjEvYVxjmyzLK00EVjT42MBOrdA+k=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_symbol_det_auto_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"symbol\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"ARgjwAAAAAAAAAAAAAAAAAAOFBGo77joqvZl7QQMB9ebMsAI3uro8ILQTJsTUgAqNzSh1mNzqihGHZYe84xtgMrVxNuwcjkidkRbNnLXWLuarOx4tgmOLx5A5G1eYEe3s7Q=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_symbol_det_explicit_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"symbol\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"ARgjwAAAAAAAAAAAAAAAAAAOFBGo77joqvZl7QQMB9ebMsAI3uro8ILQTJsTUgAqNzSh1mNzqihGHZYe84xtgMrVxNuwcjkidkRbNnLXWLuarOx4tgmOLx5A5G1eYEe3s7Q=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_symbol_det_explicit_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"symbol\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"ARgjwAAAAAAAAAAAAAAAAAAOFBGo77joqvZl7QQMB9ebMsAI3uro8ILQTJsTUgAqNzSh1mNzqihGHZYe84xtgMrVxNuwcjkidkRbNnLXWLuarOx4tgmOLx5A5G1eYEe3s7Q=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_javascriptWithScope_rand_auto_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"javascriptWithScope\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AhgjwAAAAAAAAAAAAAAAAAAPUsQHeXWhdmyfQ2Sq1ev1HMuMhBTc/FZFKO9tMMcI9qzjr+z4IdCOFCcx24/T/6NCsDpMiOGNnCdaBCCNRwNM0CTIkpHNLO+RSZORDgAsm9Q=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_javascriptWithScope_rand_auto_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"javascriptWithScope\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AhgjwAAAAAAAAAAAAAAAAAAPRZawtuu0gErebyFqiQw0LxniWhdeujGzaqfAXriGo/2fU7PalzTlWQa8wsv0y7Q/i1K4JbQwCEFpJWLppmtZshCGbVWjpPljB2BH4NNrLPE=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_javascriptWithScope_rand_explicit_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"javascriptWithScope\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AhgjwAAAAAAAAAAAAAAAAAAP0qkQjuKmKIqdrsrR9djxt+1jFlEL7K9bP1oz7QWuY38dZJOoGwa6G1bP4wDzjsucJLCEgU2IY+t7BHraBFXvR/Aar8ID5eXcvJ7iOPIyqUw=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_javascriptWithScope_rand_explicit_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"javascriptWithScope\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AhgjwAAAAAAAAAAAAAAAAAAP6L41iuBWGLg3hQZuhXp4MupTQvIT07+/+CRY292sC02mehk5BkuSOEVrehlvyvBJFKia4Bqd/UWvY8PnUPLqFKTLnokONWbAuh36y3gjStw=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_javascriptWithScope_det_explicit_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"javascriptWithScope\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": {\n      \"$code\": \"x=1\",\n      \"$scope\": {}\n    }\n  },\n  \"gcp_javascriptWithScope_det_explicit_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"javascriptWithScope\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": {\n      \"$code\": \"x=1\",\n      \"$scope\": {}\n    }\n  },\n  \"gcp_int_rand_auto_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"int\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AhgjwAAAAAAAAAAAAAAAAAAQ+6oRKWMSvC+3UGrHSyGeVlR9bFnZtFTmYlUoGn04k6ndtCl8rsmBVUV6dMMYd7znnZtTSIGPI8q6jwf/NJjdIw==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_int_rand_auto_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"int\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AhgjwAAAAAAAAAAAAAAAAAAQnz5jAbrrdutTPFA4m3MvlVJr3bpurTKY5xjwO5k8DZpeWTJzr+kVEJjG6M8/RgC/0UFNgBBrDbDhYa8PZHRijw==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_int_rand_explicit_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"int\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AhgjwAAAAAAAAAAAAAAAAAAQfRFoxUgjrv8up/eZ/fLlr/z++d/jFm30nYvKqsnQT7vkmmujJWc8yAtthR9OI6W5biBgAkounqRHhvatLZC6gA==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_int_rand_explicit_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"int\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AhgjwAAAAAAAAAAAAAAAAAAQY/ePk59RY6vLejx9a5ITwkT9000KAubVSqMoQwv7lNXO+GKZfZoLHG6k1MA/IxTvl1Zbz1Tw1bTctmj0HPEGNA==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_int_det_auto_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"int\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"ARgjwAAAAAAAAAAAAAAAAAAQE9RVV9pOuysUUEGKq0u6ztFM0gTpoOHcHsTFQstA7+L9XTvxWEgL3RgNeq5KtKdODlxl62niV8dnQwlSoDSSWw==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_int_det_explicit_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"int\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"ARgjwAAAAAAAAAAAAAAAAAAQE9RVV9pOuysUUEGKq0u6ztFM0gTpoOHcHsTFQstA7+L9XTvxWEgL3RgNeq5KtKdODlxl62niV8dnQwlSoDSSWw==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_int_det_explicit_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"int\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"ARgjwAAAAAAAAAAAAAAAAAAQE9RVV9pOuysUUEGKq0u6ztFM0gTpoOHcHsTFQstA7+L9XTvxWEgL3RgNeq5KtKdODlxl62niV8dnQwlSoDSSWw==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_timestamp_rand_auto_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"timestamp\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AhgjwAAAAAAAAAAAAAAAAAARLnk1LpJIriKr6iiY1yBDGnfkRaHNwWcQyL+mORtYC4+AQ6oMv0qpGrJxS2QCbYY1tGmAISqZHCIExCG+TIv4bw==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_timestamp_rand_auto_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"timestamp\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AhgjwAAAAAAAAAAAAAAAAAARaqYXh9AVZI6gvRZrBwbprE5P3K5Qf4PIK1ca+mLRNOof0EExyAhtku7mYXusLeq0ww/tV6Zt1cA36KsT8a0Nog==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_timestamp_rand_explicit_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"timestamp\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AhgjwAAAAAAAAAAAAAAAAAARLXzBjkCN8BpfXDIrb94kuZCD07Uo/DMBfMIWQtAb1++tTheUoY2ClQz33Luh4g8NXwuMJ7h8ufE70N2+b1yrUg==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_timestamp_rand_explicit_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"timestamp\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AhgjwAAAAAAAAAAAAAAAAAARe44QH9ZvTAuHsWhEMoue8eHod+cJpBm+Kl/Xtw7NI/6UTOOHC5Kkg20EvX3+GwXdAGk0bUSCFiTZb/yPox1OlA==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_timestamp_det_auto_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"timestamp\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"ARgjwAAAAAAAAAAAAAAAAAARzXjP6d6j/iQxiz1/TC/m+IfAGLFH9wY2ksS//i9x15QttlhcRrT3XmPvxaP5OjTHac4Gq3m2aXiJH56lETyl8A==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_timestamp_det_explicit_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"timestamp\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"ARgjwAAAAAAAAAAAAAAAAAARzXjP6d6j/iQxiz1/TC/m+IfAGLFH9wY2ksS//i9x15QttlhcRrT3XmPvxaP5OjTHac4Gq3m2aXiJH56lETyl8A==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_timestamp_det_explicit_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"timestamp\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"ARgjwAAAAAAAAAAAAAAAAAARzXjP6d6j/iQxiz1/TC/m+IfAGLFH9wY2ksS//i9x15QttlhcRrT3XmPvxaP5OjTHac4Gq3m2aXiJH56lETyl8A==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_long_rand_auto_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"long\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AhgjwAAAAAAAAAAAAAAAAAASuGZs48eEyVBJ9vvM6cvRySfuR0WM4kL7lx52rSGXBKtkZywyP5rJwNtRn9WTBMDqc1O/4jUgYXpqHx39SLhUPA==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_long_rand_auto_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"long\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AhgjwAAAAAAAAAAAAAAAAAAS/62F71oKTX1GlvOP89uNhXpIyLZ5OdnuLeM/hvL5HWyOudSb06cG3+xnPg3QgppAYFK5X2PGgrEcrA87AykLPg==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_long_rand_explicit_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"long\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AhgjwAAAAAAAAAAAAAAAAAASSgx+p4YzTvjZ+GCZCFHEKHNXJUSloPnLRHE4iJ515Epb8Tox7h8/aIAkB3ulnDS9BiT5UKdye2TWf8OBEwkXzg==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_long_rand_explicit_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"long\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AhgjwAAAAAAAAAAAAAAAAAAStqszyEfltpgd3aYeoyqaJX27OX861o06VhNX/N2fdSfKx0NQq/hWlWTkX6hK3hjCijiTtHmhFQR6QLkHD/6THw==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_long_det_auto_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"long\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"ARgjwAAAAAAAAAAAAAAAAAAS0wJHtZKnxJlWnlSu0xuq7bZR25UdwcbdCRSaXBC0EXEFuqlzrZSn1lcwKPKGZQO8EQ6SdQDqK95alMLmM8eQrQ==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_long_det_explicit_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"long\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"ARgjwAAAAAAAAAAAAAAAAAAS0wJHtZKnxJlWnlSu0xuq7bZR25UdwcbdCRSaXBC0EXEFuqlzrZSn1lcwKPKGZQO8EQ6SdQDqK95alMLmM8eQrQ==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_long_det_explicit_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"long\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"ARgjwAAAAAAAAAAAAAAAAAAS0wJHtZKnxJlWnlSu0xuq7bZR25UdwcbdCRSaXBC0EXEFuqlzrZSn1lcwKPKGZQO8EQ6SdQDqK95alMLmM8eQrQ==\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_decimal_rand_auto_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"decimal\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AhgjwAAAAAAAAAAAAAAAAAATg4U3nbHBX/Az3ie2yurEIJO6cFryQWKiCpBbx1z0NF7RXd7kFC1XzaY6zcBjfl2AfRO8FFmgjTmFXb6gTRSSF0iAZJZTslfe3n6YFtwSKDI=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_decimal_rand_auto_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"decimal\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AhgjwAAAAAAAAAAAAAAAAAATdSSyp0ewboV5zI3T3TV/FOrdx0UQbFHhqcH+yqpotoWPSw5dxE+BEoihYLeaPKuVU/rUIY4TUv05Egj7Ovg62Kpk3cPscxsGtE/T2Ppbt6o=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_decimal_rand_explicit_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"decimal\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AhgjwAAAAAAAAAAAAAAAAAATl7k20T22pf5Y9knVwIDyOIlbHyZBJqyi3Mai8APEZIYjpSKDKs8QNAH69CIjupyge8Izw4Cuch0bRrvMbp6YFfrUgk1JIQ4iLKkqqzHpBTY=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_decimal_rand_explicit_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"decimal\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AhgjwAAAAAAAAAAAAAAAAAATF7YLkhkuLhXdxrQk2fJTs128tRNYHeodkqw7ha/TxW3Czr5gE272gnkdzfNoS7uu9XwOr1yjrC6y/8gHALAWn77WvGrAlBktLQbIIinsuds=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"gcp_decimal_det_explicit_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"decimal\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": {\n      \"$numberDecimal\": \"1.234\"\n    }\n  },\n  \"gcp_decimal_det_explicit_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"decimal\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": {\n      \"$numberDecimal\": \"1.234\"\n    }\n  },\n  \"gcp_minKey_rand_explicit_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"minKey\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": {\n      \"$minKey\": 1\n    }\n  },\n  \"gcp_minKey_rand_explicit_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"minKey\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": {\n      \"$minKey\": 1\n    }\n  },\n  \"gcp_minKey_det_explicit_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"minKey\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": {\n      \"$minKey\": 1\n    }\n  },\n  \"gcp_minKey_det_explicit_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"minKey\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": {\n      \"$minKey\": 1\n    }\n  },\n  \"gcp_maxKey_rand_explicit_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"maxKey\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": {\n      \"$maxKey\": 1\n    }\n  },\n  \"gcp_maxKey_rand_explicit_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"maxKey\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": {\n      \"$maxKey\": 1\n    }\n  },\n  \"gcp_maxKey_det_explicit_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"maxKey\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": {\n      \"$maxKey\": 1\n    }\n  },\n  \"gcp_maxKey_det_explicit_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"maxKey\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": {\n      \"$maxKey\": 1\n    }\n  }\n}"
  },
  {
    "path": "testdata/client-side-encryption-prose/corpus-key-aws.json",
    "content": "{\n    \"status\": {\n        \"$numberInt\": \"1\"\n    }, \n    \"_id\": {\n        \"$binary\": {\n            \"base64\": \"AWSAAAAAAAAAAAAAAAAAAA==\", \n            \"subType\": \"04\"\n        }\n    }, \n    \"masterKey\": {\n        \"region\": \"us-east-1\", \n        \"key\": \"arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0\", \n        \"provider\": \"aws\"\n    }, \n    \"updateDate\": {\n        \"$date\": {\n            \"$numberLong\": \"1557827033449\"\n        }\n    }, \n    \"keyMaterial\": {\n        \"$binary\": {\n            \"base64\": \"AQICAHhQNmWG2CzOm1dq3kWLM+iDUZhEqnhJwH9wZVpuZ94A8gEqnsxXlR51T5EbEVezUqqKAAAAwjCBvwYJKoZIhvcNAQcGoIGxMIGuAgEAMIGoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDHa4jo6yp0Z18KgbUgIBEIB74sKxWtV8/YHje5lv5THTl0HIbhSwM6EqRlmBiFFatmEWaeMk4tO4xBX65eq670I5TWPSLMzpp8ncGHMmvHqRajNBnmFtbYxN3E3/WjxmdbOOe+OXpnGJPcGsftc7cB2shRfA4lICPnE26+oVNXT6p0Lo20nY5XC7jyCO\", \n            \"subType\": \"00\"\n        }\n    }, \n    \"creationDate\": {\n        \"$date\": {\n            \"$numberLong\": \"1557827033449\"\n        }\n    },\n    \"keyAltNames\": [\"aws\"]\n}"
  },
  {
    "path": "testdata/client-side-encryption-prose/corpus-key-azure.json",
    "content": "{\n    \"_id\": {\n        \"$binary\": {\n            \"base64\": \"AZUREAAAAAAAAAAAAAAAAA==\",\n            \"subType\": \"04\"\n        }\n    },\n    \"keyMaterial\": {\n        \"$binary\": {\n            \"base64\": \"n+HWZ0ZSVOYA3cvQgP7inN4JSXfOH85IngmeQxRpQHjCCcqT3IFqEWNlrsVHiz3AELimHhX4HKqOLWMUeSIT6emUDDoQX9BAv8DR1+E1w4nGs/NyEneac78EYFkK3JysrFDOgl2ypCCTKAypkn9CkAx1if4cfgQE93LW4kczcyHdGiH36CIxrCDGv1UzAvERN5Qa47DVwsM6a+hWsF2AAAJVnF0wYLLJU07TuRHdMrrphPWXZsFgyV+lRqJ7DDpReKNO8nMPLV/mHqHBHGPGQiRdb9NoJo8CvokGz4+KE8oLwzKf6V24dtwZmRkrsDV4iOhvROAzz+Euo1ypSkL3mw==\",\n            \"subType\": \"00\"\n        }\n    },\n    \"creationDate\": {\n        \"$date\": {\n            \"$numberLong\": \"1601573901680\"\n        }\n    },\n    \"updateDate\": {\n        \"$date\": {\n            \"$numberLong\": \"1601573901680\"\n        }\n    },\n    \"status\": {\n        \"$numberInt\": \"0\"\n    },\n    \"masterKey\": {\n        \"provider\": \"azure\",\n        \"keyVaultEndpoint\": \"key-vault-csfle.vault.azure.net\",\n        \"keyName\": \"key-name-csfle\"\n    },\n    \"keyAltNames\": [\"azure\"]\n}"
  },
  {
    "path": "testdata/client-side-encryption-prose/corpus-key-gcp.json",
    "content": "{\n    \"_id\": {\n        \"$binary\": {\n            \"base64\": \"GCPAAAAAAAAAAAAAAAAAAA==\",\n            \"subType\": \"04\"\n        }\n    },\n    \"keyMaterial\": {\n        \"$binary\": {\n            \"base64\": \"CiQAIgLj0WyktnB4dfYHo5SLZ41K4ASQrjJUaSzl5vvVH0G12G0SiQEAjlV8XPlbnHDEDFbdTO4QIe8ER2/172U1ouLazG0ysDtFFIlSvWX5ZnZUrRMmp/R2aJkzLXEt/zf8Mn4Lfm+itnjgo5R9K4pmPNvvPKNZX5C16lrPT+aA+rd+zXFSmlMg3i5jnxvTdLHhg3G7Q/Uv1ZIJskKt95bzLoe0tUVzRWMYXLIEcohnQg==\",\n            \"subType\": \"00\"\n        }\n    },\n    \"creationDate\": {\n        \"$date\": {\n            \"$numberLong\": \"1601574333107\"\n        }\n    },\n    \"updateDate\": {\n        \"$date\": {\n            \"$numberLong\": \"1601574333107\"\n        }\n    },\n    \"status\": {\n        \"$numberInt\": \"0\"\n    },\n    \"masterKey\": {\n        \"provider\": \"gcp\",\n        \"projectId\": \"devprod-drivers\",\n        \"location\": \"global\",\n        \"keyRing\": \"key-ring-csfle\",\n        \"keyName\": \"key-name-csfle\"\n    },\n    \"keyAltNames\": [\"gcp\"]\n}"
  },
  {
    "path": "testdata/client-side-encryption-prose/corpus-key-kmip.json",
    "content": "{\n    \"_id\": {\n        \"$binary\": {\n            \"base64\": \"KMIPAAAAAAAAAAAAAAAAAA==\",\n            \"subType\": \"04\"\n        }\n    },\n    \"keyMaterial\": {\n        \"$binary\": {\n            \"base64\": \"eUYDyB0HuWb+lQgUwO+6qJQyTTDTY2gp9FbemL7ZFo0pvr0x6rm6Ff9OVUTGH6HyMKipaeHdiIJU1dzsLwvqKvi7Beh+U4iaIWX/K0oEg1GOsJc0+Z/in8gNHbGUYLmycHViM3LES3kdt7FdFSUl5rEBHrM71yoNEXImz17QJWMGOuT4x6yoi2pvnaRJwfrI4DjpmnnTrDMac92jgZehbg==\",\n            \"subType\": \"00\"\n        }\n    },\n    \"creationDate\": {\n        \"$date\": {\n            \"$numberLong\": \"1634220190041\"\n        }\n    },\n    \"updateDate\": {\n        \"$date\": {\n            \"$numberLong\": \"1634220190041\"\n        }\n    },\n    \"status\": {\n        \"$numberInt\": \"0\"\n    },\n    \"masterKey\": {\n        \"provider\": \"kmip\",\n        \"keyId\": \"1\"\n    },\n    \"keyAltNames\": [\"kmip\"]\n}"
  },
  {
    "path": "testdata/client-side-encryption-prose/corpus-key-local.json",
    "content": "{\n    \"status\": {\n        \"$numberInt\": \"1\"\n    }, \n    \"_id\": {\n        \"$binary\": {\n            \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\", \n            \"subType\": \"04\"\n        }\n    }, \n    \"masterKey\": {\n        \"provider\": \"local\"\n    }, \n    \"updateDate\": {\n        \"$date\": {\n            \"$numberLong\": \"1557827033449\"\n        }\n    }, \n    \"keyMaterial\": {\n        \"$binary\": {\n            \"base64\": \"Ce9HSz/HKKGkIt4uyy+jDuKGA+rLC2cycykMo6vc8jXxqa1UVDYHWq1r+vZKbnnSRBfB981akzRKZCFpC05CTyFqDhXv6OnMjpG97OZEREGIsHEYiJkBW0jJJvfLLgeLsEpBzsro9FztGGXASxyxFRZFhXvHxyiLOKrdWfs7X1O/iK3pEoHMx6uSNSfUOgbebLfIqW7TO++iQS5g1xovXA==\", \n            \"subType\": \"00\"\n        }\n    }, \n    \"creationDate\": {\n        \"$date\": {\n            \"$numberLong\": \"1557827033449\"\n        }\n    },\n    \"keyAltNames\": [ \"local\" ]\n}"
  },
  {
    "path": "testdata/client-side-encryption-prose/corpus-schema.json",
    "content": "{\n  \"bsonType\": \"object\",\n  \"properties\": {\n    \"aws_double_rand_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"AWSAAAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"double\"\n          }\n        }\n      }\n    },\n    \"aws_double_rand_auto_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": \"/altname_aws\",\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"double\"\n          }\n        }\n      }\n    },\n    \"aws_double_rand_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"aws_double_rand_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"aws_string_rand_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"AWSAAAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"string\"\n          }\n        }\n      }\n    },\n    \"aws_string_rand_auto_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": \"/altname_aws\",\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"string\"\n          }\n        }\n      }\n    },\n    \"aws_string_rand_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"aws_string_rand_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"aws_string_det_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"AWSAAAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\",\n            \"bsonType\": \"string\"\n          }\n        }\n      }\n    },\n    \"aws_string_det_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"aws_string_det_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"aws_object_rand_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"AWSAAAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"object\"\n          }\n        }\n      }\n    },\n    \"aws_object_rand_auto_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": \"/altname_aws\",\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"object\"\n          }\n        }\n      }\n    },\n    \"aws_object_rand_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"aws_object_rand_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"aws_array_rand_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"AWSAAAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"array\"\n          }\n        }\n      }\n    },\n    \"aws_array_rand_auto_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": \"/altname_aws\",\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"array\"\n          }\n        }\n      }\n    },\n    \"aws_array_rand_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"aws_array_rand_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"aws_binData=00_rand_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"AWSAAAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"binData\"\n          }\n        }\n      }\n    },\n    \"aws_binData=00_rand_auto_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": \"/altname_aws\",\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"binData\"\n          }\n        }\n      }\n    },\n    \"aws_binData=00_rand_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"aws_binData=00_rand_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"aws_binData=00_det_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"AWSAAAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\",\n            \"bsonType\": \"binData\"\n          }\n        }\n      }\n    },\n    \"aws_binData=00_det_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"aws_binData=00_det_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"aws_binData=04_rand_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"AWSAAAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"binData\"\n          }\n        }\n      }\n    },\n    \"aws_binData=04_rand_auto_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": \"/altname_aws\",\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"binData\"\n          }\n        }\n      }\n    },\n    \"aws_binData=04_rand_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"aws_binData=04_rand_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"aws_binData=04_det_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"AWSAAAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\",\n            \"bsonType\": \"binData\"\n          }\n        }\n      }\n    },\n    \"aws_binData=04_det_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"aws_binData=04_det_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"aws_objectId_rand_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"AWSAAAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"objectId\"\n          }\n        }\n      }\n    },\n    \"aws_objectId_rand_auto_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": \"/altname_aws\",\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"objectId\"\n          }\n        }\n      }\n    },\n    \"aws_objectId_rand_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"aws_objectId_rand_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"aws_objectId_det_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"AWSAAAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\",\n            \"bsonType\": \"objectId\"\n          }\n        }\n      }\n    },\n    \"aws_objectId_det_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"aws_objectId_det_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"aws_bool_rand_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"AWSAAAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"bool\"\n          }\n        }\n      }\n    },\n    \"aws_bool_rand_auto_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": \"/altname_aws\",\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"bool\"\n          }\n        }\n      }\n    },\n    \"aws_bool_rand_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"aws_bool_rand_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"aws_date_rand_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"AWSAAAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"date\"\n          }\n        }\n      }\n    },\n    \"aws_date_rand_auto_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": \"/altname_aws\",\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"date\"\n          }\n        }\n      }\n    },\n    \"aws_date_rand_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"aws_date_rand_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"aws_date_det_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"AWSAAAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\",\n            \"bsonType\": \"date\"\n          }\n        }\n      }\n    },\n    \"aws_date_det_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"aws_date_det_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"aws_regex_rand_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"AWSAAAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"regex\"\n          }\n        }\n      }\n    },\n    \"aws_regex_rand_auto_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": \"/altname_aws\",\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"regex\"\n          }\n        }\n      }\n    },\n    \"aws_regex_rand_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"aws_regex_rand_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"aws_regex_det_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"AWSAAAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\",\n            \"bsonType\": \"regex\"\n          }\n        }\n      }\n    },\n    \"aws_regex_det_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"aws_regex_det_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"aws_dbPointer_rand_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"AWSAAAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"dbPointer\"\n          }\n        }\n      }\n    },\n    \"aws_dbPointer_rand_auto_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": \"/altname_aws\",\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"dbPointer\"\n          }\n        }\n      }\n    },\n    \"aws_dbPointer_rand_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"aws_dbPointer_rand_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"aws_dbPointer_det_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"AWSAAAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\",\n            \"bsonType\": \"dbPointer\"\n          }\n        }\n      }\n    },\n    \"aws_dbPointer_det_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"aws_dbPointer_det_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"aws_javascript_rand_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"AWSAAAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"javascript\"\n          }\n        }\n      }\n    },\n    \"aws_javascript_rand_auto_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": \"/altname_aws\",\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"javascript\"\n          }\n        }\n      }\n    },\n    \"aws_javascript_rand_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"aws_javascript_rand_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"aws_javascript_det_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"AWSAAAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\",\n            \"bsonType\": \"javascript\"\n          }\n        }\n      }\n    },\n    \"aws_javascript_det_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"aws_javascript_det_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"aws_symbol_rand_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"AWSAAAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"symbol\"\n          }\n        }\n      }\n    },\n    \"aws_symbol_rand_auto_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": \"/altname_aws\",\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"symbol\"\n          }\n        }\n      }\n    },\n    \"aws_symbol_rand_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"aws_symbol_rand_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"aws_symbol_det_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"AWSAAAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\",\n            \"bsonType\": \"symbol\"\n          }\n        }\n      }\n    },\n    \"aws_symbol_det_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"aws_symbol_det_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"aws_javascriptWithScope_rand_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"AWSAAAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"javascriptWithScope\"\n          }\n        }\n      }\n    },\n    \"aws_javascriptWithScope_rand_auto_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": \"/altname_aws\",\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"javascriptWithScope\"\n          }\n        }\n      }\n    },\n    \"aws_javascriptWithScope_rand_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"aws_javascriptWithScope_rand_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"aws_int_rand_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"AWSAAAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"int\"\n          }\n        }\n      }\n    },\n    \"aws_int_rand_auto_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": \"/altname_aws\",\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"int\"\n          }\n        }\n      }\n    },\n    \"aws_int_rand_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"aws_int_rand_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"aws_int_det_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"AWSAAAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\",\n            \"bsonType\": \"int\"\n          }\n        }\n      }\n    },\n    \"aws_int_det_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"aws_int_det_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"aws_timestamp_rand_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"AWSAAAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"timestamp\"\n          }\n        }\n      }\n    },\n    \"aws_timestamp_rand_auto_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": \"/altname_aws\",\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"timestamp\"\n          }\n        }\n      }\n    },\n    \"aws_timestamp_rand_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"aws_timestamp_rand_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"aws_timestamp_det_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"AWSAAAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\",\n            \"bsonType\": \"timestamp\"\n          }\n        }\n      }\n    },\n    \"aws_timestamp_det_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"aws_timestamp_det_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"aws_long_rand_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"AWSAAAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"long\"\n          }\n        }\n      }\n    },\n    \"aws_long_rand_auto_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": \"/altname_aws\",\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"long\"\n          }\n        }\n      }\n    },\n    \"aws_long_rand_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"aws_long_rand_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"aws_long_det_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"AWSAAAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\",\n            \"bsonType\": \"long\"\n          }\n        }\n      }\n    },\n    \"aws_long_det_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"aws_long_det_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"aws_decimal_rand_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"AWSAAAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"decimal\"\n          }\n        }\n      }\n    },\n    \"aws_decimal_rand_auto_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": \"/altname_aws\",\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"decimal\"\n          }\n        }\n      }\n    },\n    \"aws_decimal_rand_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"aws_decimal_rand_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"local_double_rand_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"double\"\n          }\n        }\n      }\n    },\n    \"local_double_rand_auto_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": \"/altname_local\",\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"double\"\n          }\n        }\n      }\n    },\n    \"local_double_rand_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"local_double_rand_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"local_string_rand_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"string\"\n          }\n        }\n      }\n    },\n    \"local_string_rand_auto_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": \"/altname_local\",\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"string\"\n          }\n        }\n      }\n    },\n    \"local_string_rand_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"local_string_rand_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"local_string_det_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\",\n            \"bsonType\": \"string\"\n          }\n        }\n      }\n    },\n    \"local_string_det_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"local_string_det_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"local_object_rand_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"object\"\n          }\n        }\n      }\n    },\n    \"local_object_rand_auto_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": \"/altname_local\",\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"object\"\n          }\n        }\n      }\n    },\n    \"local_object_rand_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"local_object_rand_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"local_array_rand_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"array\"\n          }\n        }\n      }\n    },\n    \"local_array_rand_auto_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": \"/altname_local\",\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"array\"\n          }\n        }\n      }\n    },\n    \"local_array_rand_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"local_array_rand_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"local_binData=00_rand_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"binData\"\n          }\n        }\n      }\n    },\n    \"local_binData=00_rand_auto_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": \"/altname_local\",\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"binData\"\n          }\n        }\n      }\n    },\n    \"local_binData=00_rand_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"local_binData=00_rand_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"local_binData=00_det_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\",\n            \"bsonType\": \"binData\"\n          }\n        }\n      }\n    },\n    \"local_binData=00_det_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"local_binData=00_det_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"local_binData=04_rand_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"binData\"\n          }\n        }\n      }\n    },\n    \"local_binData=04_rand_auto_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": \"/altname_local\",\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"binData\"\n          }\n        }\n      }\n    },\n    \"local_binData=04_rand_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"local_binData=04_rand_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"local_binData=04_det_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\",\n            \"bsonType\": \"binData\"\n          }\n        }\n      }\n    },\n    \"local_binData=04_det_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"local_binData=04_det_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"local_objectId_rand_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"objectId\"\n          }\n        }\n      }\n    },\n    \"local_objectId_rand_auto_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": \"/altname_local\",\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"objectId\"\n          }\n        }\n      }\n    },\n    \"local_objectId_rand_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"local_objectId_rand_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"local_objectId_det_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\",\n            \"bsonType\": \"objectId\"\n          }\n        }\n      }\n    },\n    \"local_objectId_det_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"local_objectId_det_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"local_bool_rand_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"bool\"\n          }\n        }\n      }\n    },\n    \"local_bool_rand_auto_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": \"/altname_local\",\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"bool\"\n          }\n        }\n      }\n    },\n    \"local_bool_rand_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"local_bool_rand_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"local_date_rand_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"date\"\n          }\n        }\n      }\n    },\n    \"local_date_rand_auto_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": \"/altname_local\",\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"date\"\n          }\n        }\n      }\n    },\n    \"local_date_rand_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"local_date_rand_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"local_date_det_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\",\n            \"bsonType\": \"date\"\n          }\n        }\n      }\n    },\n    \"local_date_det_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"local_date_det_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"local_regex_rand_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"regex\"\n          }\n        }\n      }\n    },\n    \"local_regex_rand_auto_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": \"/altname_local\",\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"regex\"\n          }\n        }\n      }\n    },\n    \"local_regex_rand_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"local_regex_rand_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"local_regex_det_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\",\n            \"bsonType\": \"regex\"\n          }\n        }\n      }\n    },\n    \"local_regex_det_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"local_regex_det_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"local_dbPointer_rand_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"dbPointer\"\n          }\n        }\n      }\n    },\n    \"local_dbPointer_rand_auto_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": \"/altname_local\",\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"dbPointer\"\n          }\n        }\n      }\n    },\n    \"local_dbPointer_rand_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"local_dbPointer_rand_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"local_dbPointer_det_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\",\n            \"bsonType\": \"dbPointer\"\n          }\n        }\n      }\n    },\n    \"local_dbPointer_det_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"local_dbPointer_det_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"local_javascript_rand_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"javascript\"\n          }\n        }\n      }\n    },\n    \"local_javascript_rand_auto_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": \"/altname_local\",\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"javascript\"\n          }\n        }\n      }\n    },\n    \"local_javascript_rand_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"local_javascript_rand_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"local_javascript_det_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\",\n            \"bsonType\": \"javascript\"\n          }\n        }\n      }\n    },\n    \"local_javascript_det_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"local_javascript_det_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"local_symbol_rand_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"symbol\"\n          }\n        }\n      }\n    },\n    \"local_symbol_rand_auto_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": \"/altname_local\",\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"symbol\"\n          }\n        }\n      }\n    },\n    \"local_symbol_rand_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"local_symbol_rand_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"local_symbol_det_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\",\n            \"bsonType\": \"symbol\"\n          }\n        }\n      }\n    },\n    \"local_symbol_det_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"local_symbol_det_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"local_javascriptWithScope_rand_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"javascriptWithScope\"\n          }\n        }\n      }\n    },\n    \"local_javascriptWithScope_rand_auto_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": \"/altname_local\",\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"javascriptWithScope\"\n          }\n        }\n      }\n    },\n    \"local_javascriptWithScope_rand_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"local_javascriptWithScope_rand_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"local_int_rand_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"int\"\n          }\n        }\n      }\n    },\n    \"local_int_rand_auto_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": \"/altname_local\",\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"int\"\n          }\n        }\n      }\n    },\n    \"local_int_rand_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"local_int_rand_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"local_int_det_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\",\n            \"bsonType\": \"int\"\n          }\n        }\n      }\n    },\n    \"local_int_det_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"local_int_det_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"local_timestamp_rand_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"timestamp\"\n          }\n        }\n      }\n    },\n    \"local_timestamp_rand_auto_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": \"/altname_local\",\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"timestamp\"\n          }\n        }\n      }\n    },\n    \"local_timestamp_rand_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"local_timestamp_rand_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"local_timestamp_det_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\",\n            \"bsonType\": \"timestamp\"\n          }\n        }\n      }\n    },\n    \"local_timestamp_det_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"local_timestamp_det_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"local_long_rand_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"long\"\n          }\n        }\n      }\n    },\n    \"local_long_rand_auto_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": \"/altname_local\",\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"long\"\n          }\n        }\n      }\n    },\n    \"local_long_rand_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"local_long_rand_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"local_long_det_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\",\n            \"bsonType\": \"long\"\n          }\n        }\n      }\n    },\n    \"local_long_det_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"local_long_det_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"local_decimal_rand_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"decimal\"\n          }\n        }\n      }\n    },\n    \"local_decimal_rand_auto_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": \"/altname_local\",\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"decimal\"\n          }\n        }\n      }\n    },\n    \"local_decimal_rand_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"local_decimal_rand_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"azure_double_rand_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"AZUREAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"double\"\n          }\n        }\n      }\n    },\n    \"azure_double_rand_auto_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": \"/altname_azure\",\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"double\"\n          }\n        }\n      }\n    },\n    \"azure_double_rand_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"azure_double_rand_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"azure_string_rand_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"AZUREAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"string\"\n          }\n        }\n      }\n    },\n    \"azure_string_rand_auto_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": \"/altname_azure\",\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"string\"\n          }\n        }\n      }\n    },\n    \"azure_string_rand_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"azure_string_rand_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"azure_string_det_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"AZUREAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\",\n            \"bsonType\": \"string\"\n          }\n        }\n      }\n    },\n    \"azure_string_det_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"azure_string_det_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"azure_object_rand_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"AZUREAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"object\"\n          }\n        }\n      }\n    },\n    \"azure_object_rand_auto_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": \"/altname_azure\",\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"object\"\n          }\n        }\n      }\n    },\n    \"azure_object_rand_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"azure_object_rand_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"azure_array_rand_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"AZUREAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"array\"\n          }\n        }\n      }\n    },\n    \"azure_array_rand_auto_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": \"/altname_azure\",\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"array\"\n          }\n        }\n      }\n    },\n    \"azure_array_rand_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"azure_array_rand_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"azure_binData=00_rand_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"AZUREAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"binData\"\n          }\n        }\n      }\n    },\n    \"azure_binData=00_rand_auto_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": \"/altname_azure\",\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"binData\"\n          }\n        }\n      }\n    },\n    \"azure_binData=00_rand_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"azure_binData=00_rand_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"azure_binData=00_det_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"AZUREAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\",\n            \"bsonType\": \"binData\"\n          }\n        }\n      }\n    },\n    \"azure_binData=00_det_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"azure_binData=00_det_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"azure_binData=04_rand_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"AZUREAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"binData\"\n          }\n        }\n      }\n    },\n    \"azure_binData=04_rand_auto_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": \"/altname_azure\",\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"binData\"\n          }\n        }\n      }\n    },\n    \"azure_binData=04_rand_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"azure_binData=04_rand_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"azure_binData=04_det_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"AZUREAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\",\n            \"bsonType\": \"binData\"\n          }\n        }\n      }\n    },\n    \"azure_binData=04_det_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"azure_binData=04_det_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"azure_objectId_rand_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"AZUREAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"objectId\"\n          }\n        }\n      }\n    },\n    \"azure_objectId_rand_auto_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": \"/altname_azure\",\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"objectId\"\n          }\n        }\n      }\n    },\n    \"azure_objectId_rand_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"azure_objectId_rand_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"azure_objectId_det_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"AZUREAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\",\n            \"bsonType\": \"objectId\"\n          }\n        }\n      }\n    },\n    \"azure_objectId_det_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"azure_objectId_det_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"azure_bool_rand_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"AZUREAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"bool\"\n          }\n        }\n      }\n    },\n    \"azure_bool_rand_auto_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": \"/altname_azure\",\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"bool\"\n          }\n        }\n      }\n    },\n    \"azure_bool_rand_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"azure_bool_rand_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"azure_date_rand_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"AZUREAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"date\"\n          }\n        }\n      }\n    },\n    \"azure_date_rand_auto_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": \"/altname_azure\",\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"date\"\n          }\n        }\n      }\n    },\n    \"azure_date_rand_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"azure_date_rand_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"azure_date_det_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"AZUREAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\",\n            \"bsonType\": \"date\"\n          }\n        }\n      }\n    },\n    \"azure_date_det_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"azure_date_det_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"azure_regex_rand_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"AZUREAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"regex\"\n          }\n        }\n      }\n    },\n    \"azure_regex_rand_auto_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": \"/altname_azure\",\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"regex\"\n          }\n        }\n      }\n    },\n    \"azure_regex_rand_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"azure_regex_rand_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"azure_regex_det_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"AZUREAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\",\n            \"bsonType\": \"regex\"\n          }\n        }\n      }\n    },\n    \"azure_regex_det_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"azure_regex_det_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"azure_dbPointer_rand_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"AZUREAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"dbPointer\"\n          }\n        }\n      }\n    },\n    \"azure_dbPointer_rand_auto_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": \"/altname_azure\",\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"dbPointer\"\n          }\n        }\n      }\n    },\n    \"azure_dbPointer_rand_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"azure_dbPointer_rand_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"azure_dbPointer_det_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"AZUREAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\",\n            \"bsonType\": \"dbPointer\"\n          }\n        }\n      }\n    },\n    \"azure_dbPointer_det_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"azure_dbPointer_det_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"azure_javascript_rand_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"AZUREAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"javascript\"\n          }\n        }\n      }\n    },\n    \"azure_javascript_rand_auto_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": \"/altname_azure\",\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"javascript\"\n          }\n        }\n      }\n    },\n    \"azure_javascript_rand_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"azure_javascript_rand_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"azure_javascript_det_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"AZUREAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\",\n            \"bsonType\": \"javascript\"\n          }\n        }\n      }\n    },\n    \"azure_javascript_det_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"azure_javascript_det_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"azure_symbol_rand_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"AZUREAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"symbol\"\n          }\n        }\n      }\n    },\n    \"azure_symbol_rand_auto_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": \"/altname_azure\",\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"symbol\"\n          }\n        }\n      }\n    },\n    \"azure_symbol_rand_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"azure_symbol_rand_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"azure_symbol_det_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"AZUREAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\",\n            \"bsonType\": \"symbol\"\n          }\n        }\n      }\n    },\n    \"azure_symbol_det_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"azure_symbol_det_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"azure_javascriptWithScope_rand_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"AZUREAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"javascriptWithScope\"\n          }\n        }\n      }\n    },\n    \"azure_javascriptWithScope_rand_auto_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": \"/altname_azure\",\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"javascriptWithScope\"\n          }\n        }\n      }\n    },\n    \"azure_javascriptWithScope_rand_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"azure_javascriptWithScope_rand_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"azure_int_rand_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"AZUREAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"int\"\n          }\n        }\n      }\n    },\n    \"azure_int_rand_auto_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": \"/altname_azure\",\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"int\"\n          }\n        }\n      }\n    },\n    \"azure_int_rand_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"azure_int_rand_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"azure_int_det_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"AZUREAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\",\n            \"bsonType\": \"int\"\n          }\n        }\n      }\n    },\n    \"azure_int_det_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"azure_int_det_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"azure_timestamp_rand_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"AZUREAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"timestamp\"\n          }\n        }\n      }\n    },\n    \"azure_timestamp_rand_auto_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": \"/altname_azure\",\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"timestamp\"\n          }\n        }\n      }\n    },\n    \"azure_timestamp_rand_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"azure_timestamp_rand_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"azure_timestamp_det_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"AZUREAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\",\n            \"bsonType\": \"timestamp\"\n          }\n        }\n      }\n    },\n    \"azure_timestamp_det_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"azure_timestamp_det_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"azure_long_rand_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"AZUREAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"long\"\n          }\n        }\n      }\n    },\n    \"azure_long_rand_auto_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": \"/altname_azure\",\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"long\"\n          }\n        }\n      }\n    },\n    \"azure_long_rand_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"azure_long_rand_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"azure_long_det_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"AZUREAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\",\n            \"bsonType\": \"long\"\n          }\n        }\n      }\n    },\n    \"azure_long_det_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"azure_long_det_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"azure_decimal_rand_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"AZUREAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"decimal\"\n          }\n        }\n      }\n    },\n    \"azure_decimal_rand_auto_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": \"/altname_azure\",\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"decimal\"\n          }\n        }\n      }\n    },\n    \"azure_decimal_rand_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"azure_decimal_rand_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"gcp_double_rand_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"GCPAAAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"double\"\n          }\n        }\n      }\n    },\n    \"gcp_double_rand_auto_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": \"/altname_gcp\",\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"double\"\n          }\n        }\n      }\n    },\n    \"gcp_double_rand_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"gcp_double_rand_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"gcp_string_rand_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"GCPAAAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"string\"\n          }\n        }\n      }\n    },\n    \"gcp_string_rand_auto_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": \"/altname_gcp\",\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"string\"\n          }\n        }\n      }\n    },\n    \"gcp_string_rand_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"gcp_string_rand_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"gcp_string_det_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"GCPAAAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\",\n            \"bsonType\": \"string\"\n          }\n        }\n      }\n    },\n    \"gcp_string_det_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"gcp_string_det_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"gcp_object_rand_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"GCPAAAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"object\"\n          }\n        }\n      }\n    },\n    \"gcp_object_rand_auto_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": \"/altname_gcp\",\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"object\"\n          }\n        }\n      }\n    },\n    \"gcp_object_rand_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"gcp_object_rand_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"gcp_array_rand_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"GCPAAAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"array\"\n          }\n        }\n      }\n    },\n    \"gcp_array_rand_auto_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": \"/altname_gcp\",\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"array\"\n          }\n        }\n      }\n    },\n    \"gcp_array_rand_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"gcp_array_rand_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"gcp_binData=00_rand_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"GCPAAAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"binData\"\n          }\n        }\n      }\n    },\n    \"gcp_binData=00_rand_auto_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": \"/altname_gcp\",\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"binData\"\n          }\n        }\n      }\n    },\n    \"gcp_binData=00_rand_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"gcp_binData=00_rand_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"gcp_binData=00_det_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"GCPAAAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\",\n            \"bsonType\": \"binData\"\n          }\n        }\n      }\n    },\n    \"gcp_binData=00_det_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"gcp_binData=00_det_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"gcp_binData=04_rand_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"GCPAAAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"binData\"\n          }\n        }\n      }\n    },\n    \"gcp_binData=04_rand_auto_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": \"/altname_gcp\",\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"binData\"\n          }\n        }\n      }\n    },\n    \"gcp_binData=04_rand_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"gcp_binData=04_rand_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"gcp_binData=04_det_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"GCPAAAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\",\n            \"bsonType\": \"binData\"\n          }\n        }\n      }\n    },\n    \"gcp_binData=04_det_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"gcp_binData=04_det_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"gcp_objectId_rand_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"GCPAAAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"objectId\"\n          }\n        }\n      }\n    },\n    \"gcp_objectId_rand_auto_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": \"/altname_gcp\",\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"objectId\"\n          }\n        }\n      }\n    },\n    \"gcp_objectId_rand_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"gcp_objectId_rand_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"gcp_objectId_det_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"GCPAAAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\",\n            \"bsonType\": \"objectId\"\n          }\n        }\n      }\n    },\n    \"gcp_objectId_det_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"gcp_objectId_det_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"gcp_bool_rand_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"GCPAAAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"bool\"\n          }\n        }\n      }\n    },\n    \"gcp_bool_rand_auto_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": \"/altname_gcp\",\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"bool\"\n          }\n        }\n      }\n    },\n    \"gcp_bool_rand_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"gcp_bool_rand_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"gcp_date_rand_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"GCPAAAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"date\"\n          }\n        }\n      }\n    },\n    \"gcp_date_rand_auto_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": \"/altname_gcp\",\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"date\"\n          }\n        }\n      }\n    },\n    \"gcp_date_rand_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"gcp_date_rand_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"gcp_date_det_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"GCPAAAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\",\n            \"bsonType\": \"date\"\n          }\n        }\n      }\n    },\n    \"gcp_date_det_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"gcp_date_det_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"gcp_regex_rand_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"GCPAAAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"regex\"\n          }\n        }\n      }\n    },\n    \"gcp_regex_rand_auto_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": \"/altname_gcp\",\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"regex\"\n          }\n        }\n      }\n    },\n    \"gcp_regex_rand_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"gcp_regex_rand_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"gcp_regex_det_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"GCPAAAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\",\n            \"bsonType\": \"regex\"\n          }\n        }\n      }\n    },\n    \"gcp_regex_det_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"gcp_regex_det_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"gcp_dbPointer_rand_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"GCPAAAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"dbPointer\"\n          }\n        }\n      }\n    },\n    \"gcp_dbPointer_rand_auto_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": \"/altname_gcp\",\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"dbPointer\"\n          }\n        }\n      }\n    },\n    \"gcp_dbPointer_rand_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"gcp_dbPointer_rand_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"gcp_dbPointer_det_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"GCPAAAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\",\n            \"bsonType\": \"dbPointer\"\n          }\n        }\n      }\n    },\n    \"gcp_dbPointer_det_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"gcp_dbPointer_det_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"gcp_javascript_rand_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"GCPAAAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"javascript\"\n          }\n        }\n      }\n    },\n    \"gcp_javascript_rand_auto_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": \"/altname_gcp\",\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"javascript\"\n          }\n        }\n      }\n    },\n    \"gcp_javascript_rand_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"gcp_javascript_rand_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"gcp_javascript_det_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"GCPAAAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\",\n            \"bsonType\": \"javascript\"\n          }\n        }\n      }\n    },\n    \"gcp_javascript_det_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"gcp_javascript_det_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"gcp_symbol_rand_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"GCPAAAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"symbol\"\n          }\n        }\n      }\n    },\n    \"gcp_symbol_rand_auto_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": \"/altname_gcp\",\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"symbol\"\n          }\n        }\n      }\n    },\n    \"gcp_symbol_rand_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"gcp_symbol_rand_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"gcp_symbol_det_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"GCPAAAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\",\n            \"bsonType\": \"symbol\"\n          }\n        }\n      }\n    },\n    \"gcp_symbol_det_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"gcp_symbol_det_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"gcp_javascriptWithScope_rand_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"GCPAAAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"javascriptWithScope\"\n          }\n        }\n      }\n    },\n    \"gcp_javascriptWithScope_rand_auto_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": \"/altname_gcp\",\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"javascriptWithScope\"\n          }\n        }\n      }\n    },\n    \"gcp_javascriptWithScope_rand_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"gcp_javascriptWithScope_rand_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"gcp_int_rand_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"GCPAAAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"int\"\n          }\n        }\n      }\n    },\n    \"gcp_int_rand_auto_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": \"/altname_gcp\",\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"int\"\n          }\n        }\n      }\n    },\n    \"gcp_int_rand_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"gcp_int_rand_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"gcp_int_det_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"GCPAAAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\",\n            \"bsonType\": \"int\"\n          }\n        }\n      }\n    },\n    \"gcp_int_det_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"gcp_int_det_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"gcp_timestamp_rand_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"GCPAAAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"timestamp\"\n          }\n        }\n      }\n    },\n    \"gcp_timestamp_rand_auto_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": \"/altname_gcp\",\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"timestamp\"\n          }\n        }\n      }\n    },\n    \"gcp_timestamp_rand_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"gcp_timestamp_rand_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"gcp_timestamp_det_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"GCPAAAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\",\n            \"bsonType\": \"timestamp\"\n          }\n        }\n      }\n    },\n    \"gcp_timestamp_det_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"gcp_timestamp_det_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"gcp_long_rand_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"GCPAAAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"long\"\n          }\n        }\n      }\n    },\n    \"gcp_long_rand_auto_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": \"/altname_gcp\",\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"long\"\n          }\n        }\n      }\n    },\n    \"gcp_long_rand_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"gcp_long_rand_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"gcp_long_det_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"GCPAAAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\",\n            \"bsonType\": \"long\"\n          }\n        }\n      }\n    },\n    \"gcp_long_det_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"gcp_long_det_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"gcp_decimal_rand_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"GCPAAAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"decimal\"\n          }\n        }\n      }\n    },\n    \"gcp_decimal_rand_auto_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": \"/altname_gcp\",\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"decimal\"\n          }\n        }\n      }\n    },\n    \"gcp_decimal_rand_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"gcp_decimal_rand_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"kmip_double_rand_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"KMIPAAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"double\"\n          }\n        }\n      }\n    },\n    \"kmip_double_rand_auto_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": \"/altname_kmip\",\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"double\"\n          }\n        }\n      }\n    },\n    \"kmip_double_rand_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"kmip_double_rand_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"kmip_string_rand_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"KMIPAAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"string\"\n          }\n        }\n      }\n    },\n    \"kmip_string_rand_auto_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": \"/altname_kmip\",\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"string\"\n          }\n        }\n      }\n    },\n    \"kmip_string_rand_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"kmip_string_rand_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"kmip_string_det_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"KMIPAAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\",\n            \"bsonType\": \"string\"\n          }\n        }\n      }\n    },\n    \"kmip_string_det_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"kmip_string_det_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"kmip_object_rand_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"KMIPAAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"object\"\n          }\n        }\n      }\n    },\n    \"kmip_object_rand_auto_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": \"/altname_kmip\",\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"object\"\n          }\n        }\n      }\n    },\n    \"kmip_object_rand_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"kmip_object_rand_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"kmip_array_rand_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"KMIPAAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"array\"\n          }\n        }\n      }\n    },\n    \"kmip_array_rand_auto_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": \"/altname_kmip\",\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"array\"\n          }\n        }\n      }\n    },\n    \"kmip_array_rand_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"kmip_array_rand_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"kmip_binData=00_rand_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"KMIPAAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"binData\"\n          }\n        }\n      }\n    },\n    \"kmip_binData=00_rand_auto_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": \"/altname_kmip\",\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"binData\"\n          }\n        }\n      }\n    },\n    \"kmip_binData=00_rand_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"kmip_binData=00_rand_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"kmip_binData=00_det_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"KMIPAAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\",\n            \"bsonType\": \"binData\"\n          }\n        }\n      }\n    },\n    \"kmip_binData=00_det_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"kmip_binData=00_det_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"kmip_binData=04_rand_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"KMIPAAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"binData\"\n          }\n        }\n      }\n    },\n    \"kmip_binData=04_rand_auto_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": \"/altname_kmip\",\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"binData\"\n          }\n        }\n      }\n    },\n    \"kmip_binData=04_rand_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"kmip_binData=04_rand_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"kmip_binData=04_det_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"KMIPAAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\",\n            \"bsonType\": \"binData\"\n          }\n        }\n      }\n    },\n    \"kmip_binData=04_det_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"kmip_binData=04_det_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"kmip_objectId_rand_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"KMIPAAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"objectId\"\n          }\n        }\n      }\n    },\n    \"kmip_objectId_rand_auto_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": \"/altname_kmip\",\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"objectId\"\n          }\n        }\n      }\n    },\n    \"kmip_objectId_rand_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"kmip_objectId_rand_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"kmip_objectId_det_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"KMIPAAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\",\n            \"bsonType\": \"objectId\"\n          }\n        }\n      }\n    },\n    \"kmip_objectId_det_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"kmip_objectId_det_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"kmip_bool_rand_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"KMIPAAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"bool\"\n          }\n        }\n      }\n    },\n    \"kmip_bool_rand_auto_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": \"/altname_kmip\",\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"bool\"\n          }\n        }\n      }\n    },\n    \"kmip_bool_rand_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"kmip_bool_rand_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"kmip_date_rand_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"KMIPAAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"date\"\n          }\n        }\n      }\n    },\n    \"kmip_date_rand_auto_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": \"/altname_kmip\",\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"date\"\n          }\n        }\n      }\n    },\n    \"kmip_date_rand_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"kmip_date_rand_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"kmip_date_det_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"KMIPAAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\",\n            \"bsonType\": \"date\"\n          }\n        }\n      }\n    },\n    \"kmip_date_det_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"kmip_date_det_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"kmip_regex_rand_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"KMIPAAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"regex\"\n          }\n        }\n      }\n    },\n    \"kmip_regex_rand_auto_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": \"/altname_kmip\",\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"regex\"\n          }\n        }\n      }\n    },\n    \"kmip_regex_rand_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"kmip_regex_rand_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"kmip_regex_det_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"KMIPAAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\",\n            \"bsonType\": \"regex\"\n          }\n        }\n      }\n    },\n    \"kmip_regex_det_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"kmip_regex_det_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"kmip_dbPointer_rand_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"KMIPAAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"dbPointer\"\n          }\n        }\n      }\n    },\n    \"kmip_dbPointer_rand_auto_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": \"/altname_kmip\",\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"dbPointer\"\n          }\n        }\n      }\n    },\n    \"kmip_dbPointer_rand_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"kmip_dbPointer_rand_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"kmip_dbPointer_det_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"KMIPAAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\",\n            \"bsonType\": \"dbPointer\"\n          }\n        }\n      }\n    },\n    \"kmip_dbPointer_det_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"kmip_dbPointer_det_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"kmip_javascript_rand_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"KMIPAAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"javascript\"\n          }\n        }\n      }\n    },\n    \"kmip_javascript_rand_auto_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": \"/altname_kmip\",\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"javascript\"\n          }\n        }\n      }\n    },\n    \"kmip_javascript_rand_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"kmip_javascript_rand_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"kmip_javascript_det_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"KMIPAAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\",\n            \"bsonType\": \"javascript\"\n          }\n        }\n      }\n    },\n    \"kmip_javascript_det_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"kmip_javascript_det_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"kmip_symbol_rand_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"KMIPAAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"symbol\"\n          }\n        }\n      }\n    },\n    \"kmip_symbol_rand_auto_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": \"/altname_kmip\",\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"symbol\"\n          }\n        }\n      }\n    },\n    \"kmip_symbol_rand_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"kmip_symbol_rand_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"kmip_symbol_det_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"KMIPAAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\",\n            \"bsonType\": \"symbol\"\n          }\n        }\n      }\n    },\n    \"kmip_symbol_det_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"kmip_symbol_det_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"kmip_javascriptWithScope_rand_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"KMIPAAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"javascriptWithScope\"\n          }\n        }\n      }\n    },\n    \"kmip_javascriptWithScope_rand_auto_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": \"/altname_kmip\",\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"javascriptWithScope\"\n          }\n        }\n      }\n    },\n    \"kmip_javascriptWithScope_rand_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"kmip_javascriptWithScope_rand_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"kmip_int_rand_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"KMIPAAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"int\"\n          }\n        }\n      }\n    },\n    \"kmip_int_rand_auto_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": \"/altname_kmip\",\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"int\"\n          }\n        }\n      }\n    },\n    \"kmip_int_rand_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"kmip_int_rand_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"kmip_int_det_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"KMIPAAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\",\n            \"bsonType\": \"int\"\n          }\n        }\n      }\n    },\n    \"kmip_int_det_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"kmip_int_det_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"kmip_timestamp_rand_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"KMIPAAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"timestamp\"\n          }\n        }\n      }\n    },\n    \"kmip_timestamp_rand_auto_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": \"/altname_kmip\",\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"timestamp\"\n          }\n        }\n      }\n    },\n    \"kmip_timestamp_rand_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"kmip_timestamp_rand_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"kmip_timestamp_det_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"KMIPAAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\",\n            \"bsonType\": \"timestamp\"\n          }\n        }\n      }\n    },\n    \"kmip_timestamp_det_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"kmip_timestamp_det_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"kmip_long_rand_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"KMIPAAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"long\"\n          }\n        }\n      }\n    },\n    \"kmip_long_rand_auto_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": \"/altname_kmip\",\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"long\"\n          }\n        }\n      }\n    },\n    \"kmip_long_rand_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"kmip_long_rand_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"kmip_long_det_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"KMIPAAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\",\n            \"bsonType\": \"long\"\n          }\n        }\n      }\n    },\n    \"kmip_long_det_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"kmip_long_det_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"kmip_decimal_rand_auto_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": [\n              {\n                \"$binary\": {\n                  \"base64\": \"KMIPAAAAAAAAAAAAAAAAAA==\",\n                  \"subType\": \"04\"\n                }\n              }\n            ],\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"decimal\"\n          }\n        }\n      }\n    },\n    \"kmip_decimal_rand_auto_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"encrypt\": {\n            \"keyId\": \"/altname_kmip\",\n            \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Random\",\n            \"bsonType\": \"decimal\"\n          }\n        }\n      }\n    },\n    \"kmip_decimal_rand_explicit_id\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    },\n    \"kmip_decimal_rand_explicit_altname\": {\n      \"bsonType\": \"object\",\n      \"properties\": {\n        \"value\": {\n          \"bsonType\": \"binData\"\n        }\n      }\n    }\n  }\n}"
  },
  {
    "path": "testdata/client-side-encryption-prose/corpus.json",
    "content": "{\n  \"_id\": \"client_side_encryption_corpus\",\n  \"altname_aws\": \"aws\",\n  \"altname_local\": \"local\",\n  \"altname_azure\": \"azure\",\n  \"altname_gcp\": \"gcp\",\n  \"altname_kmip\": \"kmip\",\n  \"aws_double_rand_auto_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"double\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberDouble\": \"1.234\"\n    }\n  },\n  \"aws_double_rand_auto_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"double\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberDouble\": \"1.234\"\n    }\n  },\n  \"aws_double_rand_explicit_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"double\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberDouble\": \"1.234\"\n    }\n  },\n  \"aws_double_rand_explicit_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"double\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberDouble\": \"1.234\"\n    }\n  },\n  \"aws_double_det_explicit_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"double\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": {\n      \"$numberDouble\": \"1.234\"\n    }\n  },\n  \"aws_double_det_explicit_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"double\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": {\n      \"$numberDouble\": \"1.234\"\n    }\n  },\n  \"aws_string_rand_auto_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"string\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": \"mongodb\"\n  },\n  \"aws_string_rand_auto_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"string\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": \"mongodb\"\n  },\n  \"aws_string_rand_explicit_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"string\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": \"mongodb\"\n  },\n  \"aws_string_rand_explicit_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"string\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": \"mongodb\"\n  },\n  \"aws_string_det_auto_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"string\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": \"mongodb\"\n  },\n  \"aws_string_det_explicit_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"string\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": \"mongodb\"\n  },\n  \"aws_string_det_explicit_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"string\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": \"mongodb\"\n  },\n  \"aws_object_rand_auto_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"object\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"x\": {\n        \"$numberInt\": \"1\"\n      }\n    }\n  },\n  \"aws_object_rand_auto_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"object\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"x\": {\n        \"$numberInt\": \"1\"\n      }\n    }\n  },\n  \"aws_object_rand_explicit_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"object\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"x\": {\n        \"$numberInt\": \"1\"\n      }\n    }\n  },\n  \"aws_object_rand_explicit_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"object\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"x\": {\n        \"$numberInt\": \"1\"\n      }\n    }\n  },\n  \"aws_object_det_explicit_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"object\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": {\n      \"x\": {\n        \"$numberInt\": \"1\"\n      }\n    }\n  },\n  \"aws_object_det_explicit_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"object\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": {\n      \"x\": {\n        \"$numberInt\": \"1\"\n      }\n    }\n  },\n  \"aws_array_rand_auto_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"array\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": [\n      {\n        \"$numberInt\": \"1\"\n      },\n      {\n        \"$numberInt\": \"2\"\n      },\n      {\n        \"$numberInt\": \"3\"\n      }\n    ]\n  },\n  \"aws_array_rand_auto_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"array\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": [\n      {\n        \"$numberInt\": \"1\"\n      },\n      {\n        \"$numberInt\": \"2\"\n      },\n      {\n        \"$numberInt\": \"3\"\n      }\n    ]\n  },\n  \"aws_array_rand_explicit_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"array\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": [\n      {\n        \"$numberInt\": \"1\"\n      },\n      {\n        \"$numberInt\": \"2\"\n      },\n      {\n        \"$numberInt\": \"3\"\n      }\n    ]\n  },\n  \"aws_array_rand_explicit_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"array\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": [\n      {\n        \"$numberInt\": \"1\"\n      },\n      {\n        \"$numberInt\": \"2\"\n      },\n      {\n        \"$numberInt\": \"3\"\n      }\n    ]\n  },\n  \"aws_array_det_explicit_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"array\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": [\n      {\n        \"$numberInt\": \"1\"\n      },\n      {\n        \"$numberInt\": \"2\"\n      },\n      {\n        \"$numberInt\": \"3\"\n      }\n    ]\n  },\n  \"aws_array_det_explicit_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"array\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": [\n      {\n        \"$numberInt\": \"1\"\n      },\n      {\n        \"$numberInt\": \"2\"\n      },\n      {\n        \"$numberInt\": \"3\"\n      }\n    ]\n  },\n  \"aws_binData=00_rand_auto_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"binData=00\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AQIDBA==\",\n        \"subType\": \"00\"\n      }\n    }\n  },\n  \"aws_binData=00_rand_auto_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"binData=00\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AQIDBA==\",\n        \"subType\": \"00\"\n      }\n    }\n  },\n  \"aws_binData=00_rand_explicit_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"binData=00\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AQIDBA==\",\n        \"subType\": \"00\"\n      }\n    }\n  },\n  \"aws_binData=00_rand_explicit_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"binData=00\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AQIDBA==\",\n        \"subType\": \"00\"\n      }\n    }\n  },\n  \"aws_binData=00_det_auto_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"binData=00\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AQIDBA==\",\n        \"subType\": \"00\"\n      }\n    }\n  },\n  \"aws_binData=00_det_explicit_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"binData=00\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AQIDBA==\",\n        \"subType\": \"00\"\n      }\n    }\n  },\n  \"aws_binData=00_det_explicit_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"binData=00\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AQIDBA==\",\n        \"subType\": \"00\"\n      }\n    }\n  },\n  \"aws_binData=04_rand_auto_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"binData=04\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AAECAwQFBgcICQoLDA0ODw==\",\n        \"subType\": \"04\"\n      }\n    }\n  },\n  \"aws_binData=04_rand_auto_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"binData=04\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AAECAwQFBgcICQoLDA0ODw==\",\n        \"subType\": \"04\"\n      }\n    }\n  },\n  \"aws_binData=04_rand_explicit_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"binData=04\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AAECAwQFBgcICQoLDA0ODw==\",\n        \"subType\": \"04\"\n      }\n    }\n  },\n  \"aws_binData=04_rand_explicit_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"binData=04\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AAECAwQFBgcICQoLDA0ODw==\",\n        \"subType\": \"04\"\n      }\n    }\n  },\n  \"aws_binData=04_det_auto_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"binData=04\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AAECAwQFBgcICQoLDA0ODw==\",\n        \"subType\": \"04\"\n      }\n    }\n  },\n  \"aws_binData=04_det_explicit_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"binData=04\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AAECAwQFBgcICQoLDA0ODw==\",\n        \"subType\": \"04\"\n      }\n    }\n  },\n  \"aws_binData=04_det_explicit_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"binData=04\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AAECAwQFBgcICQoLDA0ODw==\",\n        \"subType\": \"04\"\n      }\n    }\n  },\n  \"aws_undefined_rand_explicit_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"undefined\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": {\n      \"$undefined\": true\n    }\n  },\n  \"aws_undefined_rand_explicit_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"undefined\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": {\n      \"$undefined\": true\n    }\n  },\n  \"aws_undefined_det_explicit_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"undefined\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": {\n      \"$undefined\": true\n    }\n  },\n  \"aws_undefined_det_explicit_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"undefined\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": {\n      \"$undefined\": true\n    }\n  },\n  \"aws_objectId_rand_auto_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"objectId\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$oid\": \"01234567890abcdef0123456\"\n    }\n  },\n  \"aws_objectId_rand_auto_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"objectId\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$oid\": \"01234567890abcdef0123456\"\n    }\n  },\n  \"aws_objectId_rand_explicit_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"objectId\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$oid\": \"01234567890abcdef0123456\"\n    }\n  },\n  \"aws_objectId_rand_explicit_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"objectId\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$oid\": \"01234567890abcdef0123456\"\n    }\n  },\n  \"aws_objectId_det_auto_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"objectId\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$oid\": \"01234567890abcdef0123456\"\n    }\n  },\n  \"aws_objectId_det_explicit_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"objectId\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$oid\": \"01234567890abcdef0123456\"\n    }\n  },\n  \"aws_objectId_det_explicit_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"objectId\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$oid\": \"01234567890abcdef0123456\"\n    }\n  },\n  \"aws_bool_rand_auto_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"bool\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": true\n  },\n  \"aws_bool_rand_auto_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"bool\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": true\n  },\n  \"aws_bool_rand_explicit_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"bool\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": true\n  },\n  \"aws_bool_rand_explicit_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"bool\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": true\n  },\n  \"aws_bool_det_explicit_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"bool\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": true\n  },\n  \"aws_bool_det_explicit_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"bool\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": true\n  },\n  \"aws_date_rand_auto_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"date\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$date\": {\n        \"$numberLong\": \"12345\"\n      }\n    }\n  },\n  \"aws_date_rand_auto_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"date\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$date\": {\n        \"$numberLong\": \"12345\"\n      }\n    }\n  },\n  \"aws_date_rand_explicit_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"date\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$date\": {\n        \"$numberLong\": \"12345\"\n      }\n    }\n  },\n  \"aws_date_rand_explicit_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"date\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$date\": {\n        \"$numberLong\": \"12345\"\n      }\n    }\n  },\n  \"aws_date_det_auto_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"date\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$date\": {\n        \"$numberLong\": \"12345\"\n      }\n    }\n  },\n  \"aws_date_det_explicit_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"date\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$date\": {\n        \"$numberLong\": \"12345\"\n      }\n    }\n  },\n  \"aws_date_det_explicit_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"date\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$date\": {\n        \"$numberLong\": \"12345\"\n      }\n    }\n  },\n  \"aws_null_rand_explicit_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"null\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": null\n  },\n  \"aws_null_rand_explicit_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"null\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": null\n  },\n  \"aws_null_det_explicit_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"null\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": null\n  },\n  \"aws_null_det_explicit_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"null\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": null\n  },\n  \"aws_regex_rand_auto_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"regex\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$regularExpression\": {\n        \"pattern\": \".*\",\n        \"options\": \"\"\n      }\n    }\n  },\n  \"aws_regex_rand_auto_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"regex\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$regularExpression\": {\n        \"pattern\": \".*\",\n        \"options\": \"\"\n      }\n    }\n  },\n  \"aws_regex_rand_explicit_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"regex\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$regularExpression\": {\n        \"pattern\": \".*\",\n        \"options\": \"\"\n      }\n    }\n  },\n  \"aws_regex_rand_explicit_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"regex\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$regularExpression\": {\n        \"pattern\": \".*\",\n        \"options\": \"\"\n      }\n    }\n  },\n  \"aws_regex_det_auto_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"regex\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$regularExpression\": {\n        \"pattern\": \".*\",\n        \"options\": \"\"\n      }\n    }\n  },\n  \"aws_regex_det_explicit_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"regex\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$regularExpression\": {\n        \"pattern\": \".*\",\n        \"options\": \"\"\n      }\n    }\n  },\n  \"aws_regex_det_explicit_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"regex\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$regularExpression\": {\n        \"pattern\": \".*\",\n        \"options\": \"\"\n      }\n    }\n  },\n  \"aws_dbPointer_rand_auto_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"dbPointer\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$dbPointer\": {\n        \"$ref\": \"db.example\",\n        \"$id\": {\n          \"$oid\": \"01234567890abcdef0123456\"\n        }\n      }\n    }\n  },\n  \"aws_dbPointer_rand_auto_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"dbPointer\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$dbPointer\": {\n        \"$ref\": \"db.example\",\n        \"$id\": {\n          \"$oid\": \"01234567890abcdef0123456\"\n        }\n      }\n    }\n  },\n  \"aws_dbPointer_rand_explicit_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"dbPointer\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$dbPointer\": {\n        \"$ref\": \"db.example\",\n        \"$id\": {\n          \"$oid\": \"01234567890abcdef0123456\"\n        }\n      }\n    }\n  },\n  \"aws_dbPointer_rand_explicit_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"dbPointer\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$dbPointer\": {\n        \"$ref\": \"db.example\",\n        \"$id\": {\n          \"$oid\": \"01234567890abcdef0123456\"\n        }\n      }\n    }\n  },\n  \"aws_dbPointer_det_auto_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"dbPointer\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$dbPointer\": {\n        \"$ref\": \"db.example\",\n        \"$id\": {\n          \"$oid\": \"01234567890abcdef0123456\"\n        }\n      }\n    }\n  },\n  \"aws_dbPointer_det_explicit_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"dbPointer\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$dbPointer\": {\n        \"$ref\": \"db.example\",\n        \"$id\": {\n          \"$oid\": \"01234567890abcdef0123456\"\n        }\n      }\n    }\n  },\n  \"aws_dbPointer_det_explicit_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"dbPointer\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$dbPointer\": {\n        \"$ref\": \"db.example\",\n        \"$id\": {\n          \"$oid\": \"01234567890abcdef0123456\"\n        }\n      }\n    }\n  },\n  \"aws_javascript_rand_auto_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"javascript\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$code\": \"x=1\"\n    }\n  },\n  \"aws_javascript_rand_auto_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"javascript\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$code\": \"x=1\"\n    }\n  },\n  \"aws_javascript_rand_explicit_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"javascript\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$code\": \"x=1\"\n    }\n  },\n  \"aws_javascript_rand_explicit_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"javascript\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$code\": \"x=1\"\n    }\n  },\n  \"aws_javascript_det_auto_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"javascript\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$code\": \"x=1\"\n    }\n  },\n  \"aws_javascript_det_explicit_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"javascript\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$code\": \"x=1\"\n    }\n  },\n  \"aws_javascript_det_explicit_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"javascript\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$code\": \"x=1\"\n    }\n  },\n  \"aws_symbol_rand_auto_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"symbol\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$symbol\": \"mongodb-symbol\"\n    }\n  },\n  \"aws_symbol_rand_auto_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"symbol\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$symbol\": \"mongodb-symbol\"\n    }\n  },\n  \"aws_symbol_rand_explicit_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"symbol\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$symbol\": \"mongodb-symbol\"\n    }\n  },\n  \"aws_symbol_rand_explicit_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"symbol\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$symbol\": \"mongodb-symbol\"\n    }\n  },\n  \"aws_symbol_det_auto_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"symbol\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$symbol\": \"mongodb-symbol\"\n    }\n  },\n  \"aws_symbol_det_explicit_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"symbol\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$symbol\": \"mongodb-symbol\"\n    }\n  },\n  \"aws_symbol_det_explicit_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"symbol\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$symbol\": \"mongodb-symbol\"\n    }\n  },\n  \"aws_javascriptWithScope_rand_auto_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"javascriptWithScope\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$code\": \"x=1\",\n      \"$scope\": {}\n    }\n  },\n  \"aws_javascriptWithScope_rand_auto_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"javascriptWithScope\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$code\": \"x=1\",\n      \"$scope\": {}\n    }\n  },\n  \"aws_javascriptWithScope_rand_explicit_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"javascriptWithScope\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$code\": \"x=1\",\n      \"$scope\": {}\n    }\n  },\n  \"aws_javascriptWithScope_rand_explicit_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"javascriptWithScope\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$code\": \"x=1\",\n      \"$scope\": {}\n    }\n  },\n  \"aws_javascriptWithScope_det_explicit_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"javascriptWithScope\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": {\n      \"$code\": \"x=1\",\n      \"$scope\": {}\n    }\n  },\n  \"aws_javascriptWithScope_det_explicit_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"javascriptWithScope\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": {\n      \"$code\": \"x=1\",\n      \"$scope\": {}\n    }\n  },\n  \"aws_int_rand_auto_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"int\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberInt\": \"123\"\n    }\n  },\n  \"aws_int_rand_auto_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"int\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberInt\": \"123\"\n    }\n  },\n  \"aws_int_rand_explicit_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"int\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberInt\": \"123\"\n    }\n  },\n  \"aws_int_rand_explicit_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"int\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberInt\": \"123\"\n    }\n  },\n  \"aws_int_det_auto_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"int\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberInt\": \"123\"\n    }\n  },\n  \"aws_int_det_explicit_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"int\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberInt\": \"123\"\n    }\n  },\n  \"aws_int_det_explicit_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"int\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberInt\": \"123\"\n    }\n  },\n  \"aws_timestamp_rand_auto_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"timestamp\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$timestamp\": {\n        \"t\": 0,\n        \"i\": 12345\n      }\n    }\n  },\n  \"aws_timestamp_rand_auto_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"timestamp\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$timestamp\": {\n        \"t\": 0,\n        \"i\": 12345\n      }\n    }\n  },\n  \"aws_timestamp_rand_explicit_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"timestamp\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$timestamp\": {\n        \"t\": 0,\n        \"i\": 12345\n      }\n    }\n  },\n  \"aws_timestamp_rand_explicit_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"timestamp\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$timestamp\": {\n        \"t\": 0,\n        \"i\": 12345\n      }\n    }\n  },\n  \"aws_timestamp_det_auto_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"timestamp\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$timestamp\": {\n        \"t\": 0,\n        \"i\": 12345\n      }\n    }\n  },\n  \"aws_timestamp_det_explicit_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"timestamp\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$timestamp\": {\n        \"t\": 0,\n        \"i\": 12345\n      }\n    }\n  },\n  \"aws_timestamp_det_explicit_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"timestamp\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$timestamp\": {\n        \"t\": 0,\n        \"i\": 12345\n      }\n    }\n  },\n  \"aws_long_rand_auto_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"long\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberLong\": \"456\"\n    }\n  },\n  \"aws_long_rand_auto_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"long\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberLong\": \"456\"\n    }\n  },\n  \"aws_long_rand_explicit_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"long\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberLong\": \"456\"\n    }\n  },\n  \"aws_long_rand_explicit_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"long\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberLong\": \"456\"\n    }\n  },\n  \"aws_long_det_auto_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"long\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberLong\": \"456\"\n    }\n  },\n  \"aws_long_det_explicit_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"long\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberLong\": \"456\"\n    }\n  },\n  \"aws_long_det_explicit_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"long\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberLong\": \"456\"\n    }\n  },\n  \"aws_decimal_rand_auto_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"decimal\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberDecimal\": \"1.234\"\n    }\n  },\n  \"aws_decimal_rand_auto_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"decimal\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberDecimal\": \"1.234\"\n    }\n  },\n  \"aws_decimal_rand_explicit_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"decimal\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberDecimal\": \"1.234\"\n    }\n  },\n  \"aws_decimal_rand_explicit_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"decimal\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberDecimal\": \"1.234\"\n    }\n  },\n  \"aws_decimal_det_explicit_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"decimal\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": {\n      \"$numberDecimal\": \"1.234\"\n    }\n  },\n  \"aws_decimal_det_explicit_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"decimal\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": {\n      \"$numberDecimal\": \"1.234\"\n    }\n  },\n  \"aws_minKey_rand_explicit_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"minKey\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": {\n      \"$minKey\": 1\n    }\n  },\n  \"aws_minKey_rand_explicit_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"minKey\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": {\n      \"$minKey\": 1\n    }\n  },\n  \"aws_minKey_det_explicit_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"minKey\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": {\n      \"$minKey\": 1\n    }\n  },\n  \"aws_minKey_det_explicit_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"minKey\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": {\n      \"$minKey\": 1\n    }\n  },\n  \"aws_maxKey_rand_explicit_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"maxKey\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": {\n      \"$maxKey\": 1\n    }\n  },\n  \"aws_maxKey_rand_explicit_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"maxKey\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": {\n      \"$maxKey\": 1\n    }\n  },\n  \"aws_maxKey_det_explicit_id\": {\n    \"kms\": \"aws\",\n    \"type\": \"maxKey\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": {\n      \"$maxKey\": 1\n    }\n  },\n  \"aws_maxKey_det_explicit_altname\": {\n    \"kms\": \"aws\",\n    \"type\": \"maxKey\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": {\n      \"$maxKey\": 1\n    }\n  },\n  \"local_double_rand_auto_id\": {\n    \"kms\": \"local\",\n    \"type\": \"double\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberDouble\": \"1.234\"\n    }\n  },\n  \"local_double_rand_auto_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"double\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberDouble\": \"1.234\"\n    }\n  },\n  \"local_double_rand_explicit_id\": {\n    \"kms\": \"local\",\n    \"type\": \"double\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberDouble\": \"1.234\"\n    }\n  },\n  \"local_double_rand_explicit_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"double\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberDouble\": \"1.234\"\n    }\n  },\n  \"local_double_det_explicit_id\": {\n    \"kms\": \"local\",\n    \"type\": \"double\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": {\n      \"$numberDouble\": \"1.234\"\n    }\n  },\n  \"local_double_det_explicit_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"double\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": {\n      \"$numberDouble\": \"1.234\"\n    }\n  },\n  \"local_string_rand_auto_id\": {\n    \"kms\": \"local\",\n    \"type\": \"string\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": \"mongodb\"\n  },\n  \"local_string_rand_auto_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"string\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": \"mongodb\"\n  },\n  \"local_string_rand_explicit_id\": {\n    \"kms\": \"local\",\n    \"type\": \"string\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": \"mongodb\"\n  },\n  \"local_string_rand_explicit_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"string\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": \"mongodb\"\n  },\n  \"local_string_det_auto_id\": {\n    \"kms\": \"local\",\n    \"type\": \"string\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": \"mongodb\"\n  },\n  \"local_string_det_explicit_id\": {\n    \"kms\": \"local\",\n    \"type\": \"string\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": \"mongodb\"\n  },\n  \"local_string_det_explicit_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"string\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": \"mongodb\"\n  },\n  \"local_object_rand_auto_id\": {\n    \"kms\": \"local\",\n    \"type\": \"object\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"x\": {\n        \"$numberInt\": \"1\"\n      }\n    }\n  },\n  \"local_object_rand_auto_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"object\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"x\": {\n        \"$numberInt\": \"1\"\n      }\n    }\n  },\n  \"local_object_rand_explicit_id\": {\n    \"kms\": \"local\",\n    \"type\": \"object\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"x\": {\n        \"$numberInt\": \"1\"\n      }\n    }\n  },\n  \"local_object_rand_explicit_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"object\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"x\": {\n        \"$numberInt\": \"1\"\n      }\n    }\n  },\n  \"local_object_det_explicit_id\": {\n    \"kms\": \"local\",\n    \"type\": \"object\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": {\n      \"x\": {\n        \"$numberInt\": \"1\"\n      }\n    }\n  },\n  \"local_object_det_explicit_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"object\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": {\n      \"x\": {\n        \"$numberInt\": \"1\"\n      }\n    }\n  },\n  \"local_array_rand_auto_id\": {\n    \"kms\": \"local\",\n    \"type\": \"array\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": [\n      {\n        \"$numberInt\": \"1\"\n      },\n      {\n        \"$numberInt\": \"2\"\n      },\n      {\n        \"$numberInt\": \"3\"\n      }\n    ]\n  },\n  \"local_array_rand_auto_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"array\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": [\n      {\n        \"$numberInt\": \"1\"\n      },\n      {\n        \"$numberInt\": \"2\"\n      },\n      {\n        \"$numberInt\": \"3\"\n      }\n    ]\n  },\n  \"local_array_rand_explicit_id\": {\n    \"kms\": \"local\",\n    \"type\": \"array\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": [\n      {\n        \"$numberInt\": \"1\"\n      },\n      {\n        \"$numberInt\": \"2\"\n      },\n      {\n        \"$numberInt\": \"3\"\n      }\n    ]\n  },\n  \"local_array_rand_explicit_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"array\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": [\n      {\n        \"$numberInt\": \"1\"\n      },\n      {\n        \"$numberInt\": \"2\"\n      },\n      {\n        \"$numberInt\": \"3\"\n      }\n    ]\n  },\n  \"local_array_det_explicit_id\": {\n    \"kms\": \"local\",\n    \"type\": \"array\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": [\n      {\n        \"$numberInt\": \"1\"\n      },\n      {\n        \"$numberInt\": \"2\"\n      },\n      {\n        \"$numberInt\": \"3\"\n      }\n    ]\n  },\n  \"local_array_det_explicit_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"array\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": [\n      {\n        \"$numberInt\": \"1\"\n      },\n      {\n        \"$numberInt\": \"2\"\n      },\n      {\n        \"$numberInt\": \"3\"\n      }\n    ]\n  },\n  \"local_binData=00_rand_auto_id\": {\n    \"kms\": \"local\",\n    \"type\": \"binData=00\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AQIDBA==\",\n        \"subType\": \"00\"\n      }\n    }\n  },\n  \"local_binData=00_rand_auto_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"binData=00\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AQIDBA==\",\n        \"subType\": \"00\"\n      }\n    }\n  },\n  \"local_binData=00_rand_explicit_id\": {\n    \"kms\": \"local\",\n    \"type\": \"binData=00\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AQIDBA==\",\n        \"subType\": \"00\"\n      }\n    }\n  },\n  \"local_binData=00_rand_explicit_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"binData=00\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AQIDBA==\",\n        \"subType\": \"00\"\n      }\n    }\n  },\n  \"local_binData=00_det_auto_id\": {\n    \"kms\": \"local\",\n    \"type\": \"binData=00\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AQIDBA==\",\n        \"subType\": \"00\"\n      }\n    }\n  },\n  \"local_binData=00_det_explicit_id\": {\n    \"kms\": \"local\",\n    \"type\": \"binData=00\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AQIDBA==\",\n        \"subType\": \"00\"\n      }\n    }\n  },\n  \"local_binData=00_det_explicit_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"binData=00\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AQIDBA==\",\n        \"subType\": \"00\"\n      }\n    }\n  },\n  \"local_binData=04_rand_auto_id\": {\n    \"kms\": \"local\",\n    \"type\": \"binData=04\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AAECAwQFBgcICQoLDA0ODw==\",\n        \"subType\": \"04\"\n      }\n    }\n  },\n  \"local_binData=04_rand_auto_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"binData=04\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AAECAwQFBgcICQoLDA0ODw==\",\n        \"subType\": \"04\"\n      }\n    }\n  },\n  \"local_binData=04_rand_explicit_id\": {\n    \"kms\": \"local\",\n    \"type\": \"binData=04\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AAECAwQFBgcICQoLDA0ODw==\",\n        \"subType\": \"04\"\n      }\n    }\n  },\n  \"local_binData=04_rand_explicit_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"binData=04\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AAECAwQFBgcICQoLDA0ODw==\",\n        \"subType\": \"04\"\n      }\n    }\n  },\n  \"local_binData=04_det_auto_id\": {\n    \"kms\": \"local\",\n    \"type\": \"binData=04\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AAECAwQFBgcICQoLDA0ODw==\",\n        \"subType\": \"04\"\n      }\n    }\n  },\n  \"local_binData=04_det_explicit_id\": {\n    \"kms\": \"local\",\n    \"type\": \"binData=04\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AAECAwQFBgcICQoLDA0ODw==\",\n        \"subType\": \"04\"\n      }\n    }\n  },\n  \"local_binData=04_det_explicit_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"binData=04\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AAECAwQFBgcICQoLDA0ODw==\",\n        \"subType\": \"04\"\n      }\n    }\n  },\n  \"local_undefined_rand_explicit_id\": {\n    \"kms\": \"local\",\n    \"type\": \"undefined\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": {\n      \"$undefined\": true\n    }\n  },\n  \"local_undefined_rand_explicit_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"undefined\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": {\n      \"$undefined\": true\n    }\n  },\n  \"local_undefined_det_explicit_id\": {\n    \"kms\": \"local\",\n    \"type\": \"undefined\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": {\n      \"$undefined\": true\n    }\n  },\n  \"local_undefined_det_explicit_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"undefined\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": {\n      \"$undefined\": true\n    }\n  },\n  \"local_objectId_rand_auto_id\": {\n    \"kms\": \"local\",\n    \"type\": \"objectId\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$oid\": \"01234567890abcdef0123456\"\n    }\n  },\n  \"local_objectId_rand_auto_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"objectId\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$oid\": \"01234567890abcdef0123456\"\n    }\n  },\n  \"local_objectId_rand_explicit_id\": {\n    \"kms\": \"local\",\n    \"type\": \"objectId\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$oid\": \"01234567890abcdef0123456\"\n    }\n  },\n  \"local_objectId_rand_explicit_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"objectId\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$oid\": \"01234567890abcdef0123456\"\n    }\n  },\n  \"local_objectId_det_auto_id\": {\n    \"kms\": \"local\",\n    \"type\": \"objectId\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$oid\": \"01234567890abcdef0123456\"\n    }\n  },\n  \"local_objectId_det_explicit_id\": {\n    \"kms\": \"local\",\n    \"type\": \"objectId\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$oid\": \"01234567890abcdef0123456\"\n    }\n  },\n  \"local_objectId_det_explicit_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"objectId\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$oid\": \"01234567890abcdef0123456\"\n    }\n  },\n  \"local_bool_rand_auto_id\": {\n    \"kms\": \"local\",\n    \"type\": \"bool\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": true\n  },\n  \"local_bool_rand_auto_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"bool\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": true\n  },\n  \"local_bool_rand_explicit_id\": {\n    \"kms\": \"local\",\n    \"type\": \"bool\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": true\n  },\n  \"local_bool_rand_explicit_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"bool\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": true\n  },\n  \"local_bool_det_explicit_id\": {\n    \"kms\": \"local\",\n    \"type\": \"bool\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": true\n  },\n  \"local_bool_det_explicit_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"bool\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": true\n  },\n  \"local_date_rand_auto_id\": {\n    \"kms\": \"local\",\n    \"type\": \"date\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$date\": {\n        \"$numberLong\": \"12345\"\n      }\n    }\n  },\n  \"local_date_rand_auto_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"date\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$date\": {\n        \"$numberLong\": \"12345\"\n      }\n    }\n  },\n  \"local_date_rand_explicit_id\": {\n    \"kms\": \"local\",\n    \"type\": \"date\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$date\": {\n        \"$numberLong\": \"12345\"\n      }\n    }\n  },\n  \"local_date_rand_explicit_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"date\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$date\": {\n        \"$numberLong\": \"12345\"\n      }\n    }\n  },\n  \"local_date_det_auto_id\": {\n    \"kms\": \"local\",\n    \"type\": \"date\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$date\": {\n        \"$numberLong\": \"12345\"\n      }\n    }\n  },\n  \"local_date_det_explicit_id\": {\n    \"kms\": \"local\",\n    \"type\": \"date\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$date\": {\n        \"$numberLong\": \"12345\"\n      }\n    }\n  },\n  \"local_date_det_explicit_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"date\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$date\": {\n        \"$numberLong\": \"12345\"\n      }\n    }\n  },\n  \"local_null_rand_explicit_id\": {\n    \"kms\": \"local\",\n    \"type\": \"null\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": null\n  },\n  \"local_null_rand_explicit_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"null\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": null\n  },\n  \"local_null_det_explicit_id\": {\n    \"kms\": \"local\",\n    \"type\": \"null\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": null\n  },\n  \"local_null_det_explicit_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"null\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": null\n  },\n  \"local_regex_rand_auto_id\": {\n    \"kms\": \"local\",\n    \"type\": \"regex\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$regularExpression\": {\n        \"pattern\": \".*\",\n        \"options\": \"\"\n      }\n    }\n  },\n  \"local_regex_rand_auto_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"regex\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$regularExpression\": {\n        \"pattern\": \".*\",\n        \"options\": \"\"\n      }\n    }\n  },\n  \"local_regex_rand_explicit_id\": {\n    \"kms\": \"local\",\n    \"type\": \"regex\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$regularExpression\": {\n        \"pattern\": \".*\",\n        \"options\": \"\"\n      }\n    }\n  },\n  \"local_regex_rand_explicit_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"regex\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$regularExpression\": {\n        \"pattern\": \".*\",\n        \"options\": \"\"\n      }\n    }\n  },\n  \"local_regex_det_auto_id\": {\n    \"kms\": \"local\",\n    \"type\": \"regex\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$regularExpression\": {\n        \"pattern\": \".*\",\n        \"options\": \"\"\n      }\n    }\n  },\n  \"local_regex_det_explicit_id\": {\n    \"kms\": \"local\",\n    \"type\": \"regex\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$regularExpression\": {\n        \"pattern\": \".*\",\n        \"options\": \"\"\n      }\n    }\n  },\n  \"local_regex_det_explicit_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"regex\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$regularExpression\": {\n        \"pattern\": \".*\",\n        \"options\": \"\"\n      }\n    }\n  },\n  \"local_dbPointer_rand_auto_id\": {\n    \"kms\": \"local\",\n    \"type\": \"dbPointer\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$dbPointer\": {\n        \"$ref\": \"db.example\",\n        \"$id\": {\n          \"$oid\": \"01234567890abcdef0123456\"\n        }\n      }\n    }\n  },\n  \"local_dbPointer_rand_auto_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"dbPointer\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$dbPointer\": {\n        \"$ref\": \"db.example\",\n        \"$id\": {\n          \"$oid\": \"01234567890abcdef0123456\"\n        }\n      }\n    }\n  },\n  \"local_dbPointer_rand_explicit_id\": {\n    \"kms\": \"local\",\n    \"type\": \"dbPointer\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$dbPointer\": {\n        \"$ref\": \"db.example\",\n        \"$id\": {\n          \"$oid\": \"01234567890abcdef0123456\"\n        }\n      }\n    }\n  },\n  \"local_dbPointer_rand_explicit_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"dbPointer\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$dbPointer\": {\n        \"$ref\": \"db.example\",\n        \"$id\": {\n          \"$oid\": \"01234567890abcdef0123456\"\n        }\n      }\n    }\n  },\n  \"local_dbPointer_det_auto_id\": {\n    \"kms\": \"local\",\n    \"type\": \"dbPointer\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$dbPointer\": {\n        \"$ref\": \"db.example\",\n        \"$id\": {\n          \"$oid\": \"01234567890abcdef0123456\"\n        }\n      }\n    }\n  },\n  \"local_dbPointer_det_explicit_id\": {\n    \"kms\": \"local\",\n    \"type\": \"dbPointer\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$dbPointer\": {\n        \"$ref\": \"db.example\",\n        \"$id\": {\n          \"$oid\": \"01234567890abcdef0123456\"\n        }\n      }\n    }\n  },\n  \"local_dbPointer_det_explicit_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"dbPointer\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$dbPointer\": {\n        \"$ref\": \"db.example\",\n        \"$id\": {\n          \"$oid\": \"01234567890abcdef0123456\"\n        }\n      }\n    }\n  },\n  \"local_javascript_rand_auto_id\": {\n    \"kms\": \"local\",\n    \"type\": \"javascript\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$code\": \"x=1\"\n    }\n  },\n  \"local_javascript_rand_auto_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"javascript\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$code\": \"x=1\"\n    }\n  },\n  \"local_javascript_rand_explicit_id\": {\n    \"kms\": \"local\",\n    \"type\": \"javascript\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$code\": \"x=1\"\n    }\n  },\n  \"local_javascript_rand_explicit_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"javascript\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$code\": \"x=1\"\n    }\n  },\n  \"local_javascript_det_auto_id\": {\n    \"kms\": \"local\",\n    \"type\": \"javascript\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$code\": \"x=1\"\n    }\n  },\n  \"local_javascript_det_explicit_id\": {\n    \"kms\": \"local\",\n    \"type\": \"javascript\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$code\": \"x=1\"\n    }\n  },\n  \"local_javascript_det_explicit_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"javascript\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$code\": \"x=1\"\n    }\n  },\n  \"local_symbol_rand_auto_id\": {\n    \"kms\": \"local\",\n    \"type\": \"symbol\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$symbol\": \"mongodb-symbol\"\n    }\n  },\n  \"local_symbol_rand_auto_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"symbol\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$symbol\": \"mongodb-symbol\"\n    }\n  },\n  \"local_symbol_rand_explicit_id\": {\n    \"kms\": \"local\",\n    \"type\": \"symbol\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$symbol\": \"mongodb-symbol\"\n    }\n  },\n  \"local_symbol_rand_explicit_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"symbol\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$symbol\": \"mongodb-symbol\"\n    }\n  },\n  \"local_symbol_det_auto_id\": {\n    \"kms\": \"local\",\n    \"type\": \"symbol\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$symbol\": \"mongodb-symbol\"\n    }\n  },\n  \"local_symbol_det_explicit_id\": {\n    \"kms\": \"local\",\n    \"type\": \"symbol\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$symbol\": \"mongodb-symbol\"\n    }\n  },\n  \"local_symbol_det_explicit_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"symbol\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$symbol\": \"mongodb-symbol\"\n    }\n  },\n  \"local_javascriptWithScope_rand_auto_id\": {\n    \"kms\": \"local\",\n    \"type\": \"javascriptWithScope\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$code\": \"x=1\",\n      \"$scope\": {}\n    }\n  },\n  \"local_javascriptWithScope_rand_auto_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"javascriptWithScope\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$code\": \"x=1\",\n      \"$scope\": {}\n    }\n  },\n  \"local_javascriptWithScope_rand_explicit_id\": {\n    \"kms\": \"local\",\n    \"type\": \"javascriptWithScope\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$code\": \"x=1\",\n      \"$scope\": {}\n    }\n  },\n  \"local_javascriptWithScope_rand_explicit_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"javascriptWithScope\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$code\": \"x=1\",\n      \"$scope\": {}\n    }\n  },\n  \"local_javascriptWithScope_det_explicit_id\": {\n    \"kms\": \"local\",\n    \"type\": \"javascriptWithScope\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": {\n      \"$code\": \"x=1\",\n      \"$scope\": {}\n    }\n  },\n  \"local_javascriptWithScope_det_explicit_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"javascriptWithScope\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": {\n      \"$code\": \"x=1\",\n      \"$scope\": {}\n    }\n  },\n  \"local_int_rand_auto_id\": {\n    \"kms\": \"local\",\n    \"type\": \"int\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberInt\": \"123\"\n    }\n  },\n  \"local_int_rand_auto_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"int\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberInt\": \"123\"\n    }\n  },\n  \"local_int_rand_explicit_id\": {\n    \"kms\": \"local\",\n    \"type\": \"int\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberInt\": \"123\"\n    }\n  },\n  \"local_int_rand_explicit_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"int\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberInt\": \"123\"\n    }\n  },\n  \"local_int_det_auto_id\": {\n    \"kms\": \"local\",\n    \"type\": \"int\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberInt\": \"123\"\n    }\n  },\n  \"local_int_det_explicit_id\": {\n    \"kms\": \"local\",\n    \"type\": \"int\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberInt\": \"123\"\n    }\n  },\n  \"local_int_det_explicit_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"int\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberInt\": \"123\"\n    }\n  },\n  \"local_timestamp_rand_auto_id\": {\n    \"kms\": \"local\",\n    \"type\": \"timestamp\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$timestamp\": {\n        \"t\": 0,\n        \"i\": 12345\n      }\n    }\n  },\n  \"local_timestamp_rand_auto_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"timestamp\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$timestamp\": {\n        \"t\": 0,\n        \"i\": 12345\n      }\n    }\n  },\n  \"local_timestamp_rand_explicit_id\": {\n    \"kms\": \"local\",\n    \"type\": \"timestamp\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$timestamp\": {\n        \"t\": 0,\n        \"i\": 12345\n      }\n    }\n  },\n  \"local_timestamp_rand_explicit_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"timestamp\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$timestamp\": {\n        \"t\": 0,\n        \"i\": 12345\n      }\n    }\n  },\n  \"local_timestamp_det_auto_id\": {\n    \"kms\": \"local\",\n    \"type\": \"timestamp\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$timestamp\": {\n        \"t\": 0,\n        \"i\": 12345\n      }\n    }\n  },\n  \"local_timestamp_det_explicit_id\": {\n    \"kms\": \"local\",\n    \"type\": \"timestamp\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$timestamp\": {\n        \"t\": 0,\n        \"i\": 12345\n      }\n    }\n  },\n  \"local_timestamp_det_explicit_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"timestamp\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$timestamp\": {\n        \"t\": 0,\n        \"i\": 12345\n      }\n    }\n  },\n  \"local_long_rand_auto_id\": {\n    \"kms\": \"local\",\n    \"type\": \"long\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberLong\": \"456\"\n    }\n  },\n  \"local_long_rand_auto_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"long\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberLong\": \"456\"\n    }\n  },\n  \"local_long_rand_explicit_id\": {\n    \"kms\": \"local\",\n    \"type\": \"long\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberLong\": \"456\"\n    }\n  },\n  \"local_long_rand_explicit_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"long\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberLong\": \"456\"\n    }\n  },\n  \"local_long_det_auto_id\": {\n    \"kms\": \"local\",\n    \"type\": \"long\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberLong\": \"456\"\n    }\n  },\n  \"local_long_det_explicit_id\": {\n    \"kms\": \"local\",\n    \"type\": \"long\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberLong\": \"456\"\n    }\n  },\n  \"local_long_det_explicit_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"long\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberLong\": \"456\"\n    }\n  },\n  \"local_decimal_rand_auto_id\": {\n    \"kms\": \"local\",\n    \"type\": \"decimal\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberDecimal\": \"1.234\"\n    }\n  },\n  \"local_decimal_rand_auto_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"decimal\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberDecimal\": \"1.234\"\n    }\n  },\n  \"local_decimal_rand_explicit_id\": {\n    \"kms\": \"local\",\n    \"type\": \"decimal\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberDecimal\": \"1.234\"\n    }\n  },\n  \"local_decimal_rand_explicit_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"decimal\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberDecimal\": \"1.234\"\n    }\n  },\n  \"local_decimal_det_explicit_id\": {\n    \"kms\": \"local\",\n    \"type\": \"decimal\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": {\n      \"$numberDecimal\": \"1.234\"\n    }\n  },\n  \"local_decimal_det_explicit_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"decimal\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": {\n      \"$numberDecimal\": \"1.234\"\n    }\n  },\n  \"local_minKey_rand_explicit_id\": {\n    \"kms\": \"local\",\n    \"type\": \"minKey\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": {\n      \"$minKey\": 1\n    }\n  },\n  \"local_minKey_rand_explicit_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"minKey\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": {\n      \"$minKey\": 1\n    }\n  },\n  \"local_minKey_det_explicit_id\": {\n    \"kms\": \"local\",\n    \"type\": \"minKey\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": {\n      \"$minKey\": 1\n    }\n  },\n  \"local_minKey_det_explicit_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"minKey\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": {\n      \"$minKey\": 1\n    }\n  },\n  \"local_maxKey_rand_explicit_id\": {\n    \"kms\": \"local\",\n    \"type\": \"maxKey\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": {\n      \"$maxKey\": 1\n    }\n  },\n  \"local_maxKey_rand_explicit_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"maxKey\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": {\n      \"$maxKey\": 1\n    }\n  },\n  \"local_maxKey_det_explicit_id\": {\n    \"kms\": \"local\",\n    \"type\": \"maxKey\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": {\n      \"$maxKey\": 1\n    }\n  },\n  \"local_maxKey_det_explicit_altname\": {\n    \"kms\": \"local\",\n    \"type\": \"maxKey\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": {\n      \"$maxKey\": 1\n    }\n  },\n  \"azure_double_rand_auto_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"double\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberDouble\": \"1.234\"\n    }\n  },\n  \"azure_double_rand_auto_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"double\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberDouble\": \"1.234\"\n    }\n  },\n  \"azure_double_rand_explicit_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"double\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberDouble\": \"1.234\"\n    }\n  },\n  \"azure_double_rand_explicit_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"double\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberDouble\": \"1.234\"\n    }\n  },\n  \"azure_double_det_explicit_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"double\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": {\n      \"$numberDouble\": \"1.234\"\n    }\n  },\n  \"azure_double_det_explicit_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"double\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": {\n      \"$numberDouble\": \"1.234\"\n    }\n  },\n  \"azure_string_rand_auto_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"string\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": \"mongodb\"\n  },\n  \"azure_string_rand_auto_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"string\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": \"mongodb\"\n  },\n  \"azure_string_rand_explicit_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"string\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": \"mongodb\"\n  },\n  \"azure_string_rand_explicit_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"string\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": \"mongodb\"\n  },\n  \"azure_string_det_auto_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"string\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": \"mongodb\"\n  },\n  \"azure_string_det_explicit_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"string\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": \"mongodb\"\n  },\n  \"azure_string_det_explicit_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"string\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": \"mongodb\"\n  },\n  \"azure_object_rand_auto_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"object\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"x\": {\n        \"$numberInt\": \"1\"\n      }\n    }\n  },\n  \"azure_object_rand_auto_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"object\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"x\": {\n        \"$numberInt\": \"1\"\n      }\n    }\n  },\n  \"azure_object_rand_explicit_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"object\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"x\": {\n        \"$numberInt\": \"1\"\n      }\n    }\n  },\n  \"azure_object_rand_explicit_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"object\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"x\": {\n        \"$numberInt\": \"1\"\n      }\n    }\n  },\n  \"azure_object_det_explicit_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"object\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": {\n      \"x\": {\n        \"$numberInt\": \"1\"\n      }\n    }\n  },\n  \"azure_object_det_explicit_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"object\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": {\n      \"x\": {\n        \"$numberInt\": \"1\"\n      }\n    }\n  },\n  \"azure_array_rand_auto_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"array\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": [\n      {\n        \"$numberInt\": \"1\"\n      },\n      {\n        \"$numberInt\": \"2\"\n      },\n      {\n        \"$numberInt\": \"3\"\n      }\n    ]\n  },\n  \"azure_array_rand_auto_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"array\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": [\n      {\n        \"$numberInt\": \"1\"\n      },\n      {\n        \"$numberInt\": \"2\"\n      },\n      {\n        \"$numberInt\": \"3\"\n      }\n    ]\n  },\n  \"azure_array_rand_explicit_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"array\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": [\n      {\n        \"$numberInt\": \"1\"\n      },\n      {\n        \"$numberInt\": \"2\"\n      },\n      {\n        \"$numberInt\": \"3\"\n      }\n    ]\n  },\n  \"azure_array_rand_explicit_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"array\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": [\n      {\n        \"$numberInt\": \"1\"\n      },\n      {\n        \"$numberInt\": \"2\"\n      },\n      {\n        \"$numberInt\": \"3\"\n      }\n    ]\n  },\n  \"azure_array_det_explicit_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"array\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": [\n      {\n        \"$numberInt\": \"1\"\n      },\n      {\n        \"$numberInt\": \"2\"\n      },\n      {\n        \"$numberInt\": \"3\"\n      }\n    ]\n  },\n  \"azure_array_det_explicit_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"array\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": [\n      {\n        \"$numberInt\": \"1\"\n      },\n      {\n        \"$numberInt\": \"2\"\n      },\n      {\n        \"$numberInt\": \"3\"\n      }\n    ]\n  },\n  \"azure_binData=00_rand_auto_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"binData=00\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AQIDBA==\",\n        \"subType\": \"00\"\n      }\n    }\n  },\n  \"azure_binData=00_rand_auto_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"binData=00\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AQIDBA==\",\n        \"subType\": \"00\"\n      }\n    }\n  },\n  \"azure_binData=00_rand_explicit_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"binData=00\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AQIDBA==\",\n        \"subType\": \"00\"\n      }\n    }\n  },\n  \"azure_binData=00_rand_explicit_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"binData=00\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AQIDBA==\",\n        \"subType\": \"00\"\n      }\n    }\n  },\n  \"azure_binData=00_det_auto_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"binData=00\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AQIDBA==\",\n        \"subType\": \"00\"\n      }\n    }\n  },\n  \"azure_binData=00_det_explicit_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"binData=00\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AQIDBA==\",\n        \"subType\": \"00\"\n      }\n    }\n  },\n  \"azure_binData=00_det_explicit_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"binData=00\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AQIDBA==\",\n        \"subType\": \"00\"\n      }\n    }\n  },\n  \"azure_binData=04_rand_auto_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"binData=04\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AAECAwQFBgcICQoLDA0ODw==\",\n        \"subType\": \"04\"\n      }\n    }\n  },\n  \"azure_binData=04_rand_auto_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"binData=04\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AAECAwQFBgcICQoLDA0ODw==\",\n        \"subType\": \"04\"\n      }\n    }\n  },\n  \"azure_binData=04_rand_explicit_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"binData=04\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AAECAwQFBgcICQoLDA0ODw==\",\n        \"subType\": \"04\"\n      }\n    }\n  },\n  \"azure_binData=04_rand_explicit_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"binData=04\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AAECAwQFBgcICQoLDA0ODw==\",\n        \"subType\": \"04\"\n      }\n    }\n  },\n  \"azure_binData=04_det_auto_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"binData=04\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AAECAwQFBgcICQoLDA0ODw==\",\n        \"subType\": \"04\"\n      }\n    }\n  },\n  \"azure_binData=04_det_explicit_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"binData=04\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AAECAwQFBgcICQoLDA0ODw==\",\n        \"subType\": \"04\"\n      }\n    }\n  },\n  \"azure_binData=04_det_explicit_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"binData=04\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AAECAwQFBgcICQoLDA0ODw==\",\n        \"subType\": \"04\"\n      }\n    }\n  },\n  \"azure_undefined_rand_explicit_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"undefined\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": {\n      \"$undefined\": true\n    }\n  },\n  \"azure_undefined_rand_explicit_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"undefined\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": {\n      \"$undefined\": true\n    }\n  },\n  \"azure_undefined_det_explicit_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"undefined\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": {\n      \"$undefined\": true\n    }\n  },\n  \"azure_undefined_det_explicit_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"undefined\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": {\n      \"$undefined\": true\n    }\n  },\n  \"azure_objectId_rand_auto_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"objectId\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$oid\": \"01234567890abcdef0123456\"\n    }\n  },\n  \"azure_objectId_rand_auto_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"objectId\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$oid\": \"01234567890abcdef0123456\"\n    }\n  },\n  \"azure_objectId_rand_explicit_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"objectId\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$oid\": \"01234567890abcdef0123456\"\n    }\n  },\n  \"azure_objectId_rand_explicit_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"objectId\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$oid\": \"01234567890abcdef0123456\"\n    }\n  },\n  \"azure_objectId_det_auto_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"objectId\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$oid\": \"01234567890abcdef0123456\"\n    }\n  },\n  \"azure_objectId_det_explicit_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"objectId\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$oid\": \"01234567890abcdef0123456\"\n    }\n  },\n  \"azure_objectId_det_explicit_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"objectId\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$oid\": \"01234567890abcdef0123456\"\n    }\n  },\n  \"azure_bool_rand_auto_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"bool\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": true\n  },\n  \"azure_bool_rand_auto_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"bool\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": true\n  },\n  \"azure_bool_rand_explicit_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"bool\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": true\n  },\n  \"azure_bool_rand_explicit_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"bool\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": true\n  },\n  \"azure_bool_det_explicit_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"bool\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": true\n  },\n  \"azure_bool_det_explicit_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"bool\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": true\n  },\n  \"azure_date_rand_auto_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"date\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$date\": {\n        \"$numberLong\": \"12345\"\n      }\n    }\n  },\n  \"azure_date_rand_auto_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"date\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$date\": {\n        \"$numberLong\": \"12345\"\n      }\n    }\n  },\n  \"azure_date_rand_explicit_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"date\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$date\": {\n        \"$numberLong\": \"12345\"\n      }\n    }\n  },\n  \"azure_date_rand_explicit_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"date\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$date\": {\n        \"$numberLong\": \"12345\"\n      }\n    }\n  },\n  \"azure_date_det_auto_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"date\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$date\": {\n        \"$numberLong\": \"12345\"\n      }\n    }\n  },\n  \"azure_date_det_explicit_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"date\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$date\": {\n        \"$numberLong\": \"12345\"\n      }\n    }\n  },\n  \"azure_date_det_explicit_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"date\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$date\": {\n        \"$numberLong\": \"12345\"\n      }\n    }\n  },\n  \"azure_null_rand_explicit_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"null\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": null\n  },\n  \"azure_null_rand_explicit_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"null\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": null\n  },\n  \"azure_null_det_explicit_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"null\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": null\n  },\n  \"azure_null_det_explicit_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"null\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": null\n  },\n  \"azure_regex_rand_auto_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"regex\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$regularExpression\": {\n        \"pattern\": \".*\",\n        \"options\": \"\"\n      }\n    }\n  },\n  \"azure_regex_rand_auto_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"regex\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$regularExpression\": {\n        \"pattern\": \".*\",\n        \"options\": \"\"\n      }\n    }\n  },\n  \"azure_regex_rand_explicit_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"regex\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$regularExpression\": {\n        \"pattern\": \".*\",\n        \"options\": \"\"\n      }\n    }\n  },\n  \"azure_regex_rand_explicit_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"regex\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$regularExpression\": {\n        \"pattern\": \".*\",\n        \"options\": \"\"\n      }\n    }\n  },\n  \"azure_regex_det_auto_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"regex\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$regularExpression\": {\n        \"pattern\": \".*\",\n        \"options\": \"\"\n      }\n    }\n  },\n  \"azure_regex_det_explicit_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"regex\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$regularExpression\": {\n        \"pattern\": \".*\",\n        \"options\": \"\"\n      }\n    }\n  },\n  \"azure_regex_det_explicit_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"regex\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$regularExpression\": {\n        \"pattern\": \".*\",\n        \"options\": \"\"\n      }\n    }\n  },\n  \"azure_dbPointer_rand_auto_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"dbPointer\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$dbPointer\": {\n        \"$ref\": \"db.example\",\n        \"$id\": {\n          \"$oid\": \"01234567890abcdef0123456\"\n        }\n      }\n    }\n  },\n  \"azure_dbPointer_rand_auto_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"dbPointer\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$dbPointer\": {\n        \"$ref\": \"db.example\",\n        \"$id\": {\n          \"$oid\": \"01234567890abcdef0123456\"\n        }\n      }\n    }\n  },\n  \"azure_dbPointer_rand_explicit_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"dbPointer\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$dbPointer\": {\n        \"$ref\": \"db.example\",\n        \"$id\": {\n          \"$oid\": \"01234567890abcdef0123456\"\n        }\n      }\n    }\n  },\n  \"azure_dbPointer_rand_explicit_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"dbPointer\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$dbPointer\": {\n        \"$ref\": \"db.example\",\n        \"$id\": {\n          \"$oid\": \"01234567890abcdef0123456\"\n        }\n      }\n    }\n  },\n  \"azure_dbPointer_det_auto_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"dbPointer\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$dbPointer\": {\n        \"$ref\": \"db.example\",\n        \"$id\": {\n          \"$oid\": \"01234567890abcdef0123456\"\n        }\n      }\n    }\n  },\n  \"azure_dbPointer_det_explicit_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"dbPointer\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$dbPointer\": {\n        \"$ref\": \"db.example\",\n        \"$id\": {\n          \"$oid\": \"01234567890abcdef0123456\"\n        }\n      }\n    }\n  },\n  \"azure_dbPointer_det_explicit_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"dbPointer\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$dbPointer\": {\n        \"$ref\": \"db.example\",\n        \"$id\": {\n          \"$oid\": \"01234567890abcdef0123456\"\n        }\n      }\n    }\n  },\n  \"azure_javascript_rand_auto_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"javascript\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$code\": \"x=1\"\n    }\n  },\n  \"azure_javascript_rand_auto_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"javascript\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$code\": \"x=1\"\n    }\n  },\n  \"azure_javascript_rand_explicit_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"javascript\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$code\": \"x=1\"\n    }\n  },\n  \"azure_javascript_rand_explicit_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"javascript\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$code\": \"x=1\"\n    }\n  },\n  \"azure_javascript_det_auto_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"javascript\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$code\": \"x=1\"\n    }\n  },\n  \"azure_javascript_det_explicit_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"javascript\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$code\": \"x=1\"\n    }\n  },\n  \"azure_javascript_det_explicit_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"javascript\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$code\": \"x=1\"\n    }\n  },\n  \"azure_symbol_rand_auto_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"symbol\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$symbol\": \"mongodb-symbol\"\n    }\n  },\n  \"azure_symbol_rand_auto_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"symbol\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$symbol\": \"mongodb-symbol\"\n    }\n  },\n  \"azure_symbol_rand_explicit_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"symbol\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$symbol\": \"mongodb-symbol\"\n    }\n  },\n  \"azure_symbol_rand_explicit_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"symbol\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$symbol\": \"mongodb-symbol\"\n    }\n  },\n  \"azure_symbol_det_auto_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"symbol\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$symbol\": \"mongodb-symbol\"\n    }\n  },\n  \"azure_symbol_det_explicit_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"symbol\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$symbol\": \"mongodb-symbol\"\n    }\n  },\n  \"azure_symbol_det_explicit_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"symbol\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$symbol\": \"mongodb-symbol\"\n    }\n  },\n  \"azure_javascriptWithScope_rand_auto_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"javascriptWithScope\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$code\": \"x=1\",\n      \"$scope\": {}\n    }\n  },\n  \"azure_javascriptWithScope_rand_auto_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"javascriptWithScope\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$code\": \"x=1\",\n      \"$scope\": {}\n    }\n  },\n  \"azure_javascriptWithScope_rand_explicit_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"javascriptWithScope\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$code\": \"x=1\",\n      \"$scope\": {}\n    }\n  },\n  \"azure_javascriptWithScope_rand_explicit_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"javascriptWithScope\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$code\": \"x=1\",\n      \"$scope\": {}\n    }\n  },\n  \"azure_javascriptWithScope_det_explicit_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"javascriptWithScope\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": {\n      \"$code\": \"x=1\",\n      \"$scope\": {}\n    }\n  },\n  \"azure_javascriptWithScope_det_explicit_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"javascriptWithScope\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": {\n      \"$code\": \"x=1\",\n      \"$scope\": {}\n    }\n  },\n  \"azure_int_rand_auto_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"int\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberInt\": \"123\"\n    }\n  },\n  \"azure_int_rand_auto_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"int\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberInt\": \"123\"\n    }\n  },\n  \"azure_int_rand_explicit_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"int\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberInt\": \"123\"\n    }\n  },\n  \"azure_int_rand_explicit_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"int\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberInt\": \"123\"\n    }\n  },\n  \"azure_int_det_auto_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"int\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberInt\": \"123\"\n    }\n  },\n  \"azure_int_det_explicit_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"int\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberInt\": \"123\"\n    }\n  },\n  \"azure_int_det_explicit_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"int\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberInt\": \"123\"\n    }\n  },\n  \"azure_timestamp_rand_auto_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"timestamp\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$timestamp\": {\n        \"t\": 0,\n        \"i\": 12345\n      }\n    }\n  },\n  \"azure_timestamp_rand_auto_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"timestamp\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$timestamp\": {\n        \"t\": 0,\n        \"i\": 12345\n      }\n    }\n  },\n  \"azure_timestamp_rand_explicit_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"timestamp\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$timestamp\": {\n        \"t\": 0,\n        \"i\": 12345\n      }\n    }\n  },\n  \"azure_timestamp_rand_explicit_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"timestamp\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$timestamp\": {\n        \"t\": 0,\n        \"i\": 12345\n      }\n    }\n  },\n  \"azure_timestamp_det_auto_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"timestamp\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$timestamp\": {\n        \"t\": 0,\n        \"i\": 12345\n      }\n    }\n  },\n  \"azure_timestamp_det_explicit_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"timestamp\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$timestamp\": {\n        \"t\": 0,\n        \"i\": 12345\n      }\n    }\n  },\n  \"azure_timestamp_det_explicit_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"timestamp\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$timestamp\": {\n        \"t\": 0,\n        \"i\": 12345\n      }\n    }\n  },\n  \"azure_long_rand_auto_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"long\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberLong\": \"456\"\n    }\n  },\n  \"azure_long_rand_auto_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"long\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberLong\": \"456\"\n    }\n  },\n  \"azure_long_rand_explicit_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"long\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberLong\": \"456\"\n    }\n  },\n  \"azure_long_rand_explicit_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"long\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberLong\": \"456\"\n    }\n  },\n  \"azure_long_det_auto_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"long\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberLong\": \"456\"\n    }\n  },\n  \"azure_long_det_explicit_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"long\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberLong\": \"456\"\n    }\n  },\n  \"azure_long_det_explicit_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"long\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberLong\": \"456\"\n    }\n  },\n  \"azure_decimal_rand_auto_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"decimal\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberDecimal\": \"1.234\"\n    }\n  },\n  \"azure_decimal_rand_auto_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"decimal\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberDecimal\": \"1.234\"\n    }\n  },\n  \"azure_decimal_rand_explicit_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"decimal\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberDecimal\": \"1.234\"\n    }\n  },\n  \"azure_decimal_rand_explicit_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"decimal\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberDecimal\": \"1.234\"\n    }\n  },\n  \"azure_decimal_det_explicit_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"decimal\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": {\n      \"$numberDecimal\": \"1.234\"\n    }\n  },\n  \"azure_decimal_det_explicit_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"decimal\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": {\n      \"$numberDecimal\": \"1.234\"\n    }\n  },\n  \"azure_minKey_rand_explicit_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"minKey\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": {\n      \"$minKey\": 1\n    }\n  },\n  \"azure_minKey_rand_explicit_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"minKey\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": {\n      \"$minKey\": 1\n    }\n  },\n  \"azure_minKey_det_explicit_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"minKey\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": {\n      \"$minKey\": 1\n    }\n  },\n  \"azure_minKey_det_explicit_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"minKey\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": {\n      \"$minKey\": 1\n    }\n  },\n  \"azure_maxKey_rand_explicit_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"maxKey\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": {\n      \"$maxKey\": 1\n    }\n  },\n  \"azure_maxKey_rand_explicit_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"maxKey\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": {\n      \"$maxKey\": 1\n    }\n  },\n  \"azure_maxKey_det_explicit_id\": {\n    \"kms\": \"azure\",\n    \"type\": \"maxKey\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": {\n      \"$maxKey\": 1\n    }\n  },\n  \"azure_maxKey_det_explicit_altname\": {\n    \"kms\": \"azure\",\n    \"type\": \"maxKey\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": {\n      \"$maxKey\": 1\n    }\n  },\n  \"gcp_double_rand_auto_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"double\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberDouble\": \"1.234\"\n    }\n  },\n  \"gcp_double_rand_auto_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"double\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberDouble\": \"1.234\"\n    }\n  },\n  \"gcp_double_rand_explicit_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"double\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberDouble\": \"1.234\"\n    }\n  },\n  \"gcp_double_rand_explicit_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"double\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberDouble\": \"1.234\"\n    }\n  },\n  \"gcp_double_det_explicit_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"double\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": {\n      \"$numberDouble\": \"1.234\"\n    }\n  },\n  \"gcp_double_det_explicit_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"double\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": {\n      \"$numberDouble\": \"1.234\"\n    }\n  },\n  \"gcp_string_rand_auto_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"string\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": \"mongodb\"\n  },\n  \"gcp_string_rand_auto_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"string\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": \"mongodb\"\n  },\n  \"gcp_string_rand_explicit_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"string\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": \"mongodb\"\n  },\n  \"gcp_string_rand_explicit_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"string\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": \"mongodb\"\n  },\n  \"gcp_string_det_auto_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"string\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": \"mongodb\"\n  },\n  \"gcp_string_det_explicit_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"string\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": \"mongodb\"\n  },\n  \"gcp_string_det_explicit_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"string\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": \"mongodb\"\n  },\n  \"gcp_object_rand_auto_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"object\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"x\": {\n        \"$numberInt\": \"1\"\n      }\n    }\n  },\n  \"gcp_object_rand_auto_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"object\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"x\": {\n        \"$numberInt\": \"1\"\n      }\n    }\n  },\n  \"gcp_object_rand_explicit_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"object\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"x\": {\n        \"$numberInt\": \"1\"\n      }\n    }\n  },\n  \"gcp_object_rand_explicit_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"object\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"x\": {\n        \"$numberInt\": \"1\"\n      }\n    }\n  },\n  \"gcp_object_det_explicit_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"object\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": {\n      \"x\": {\n        \"$numberInt\": \"1\"\n      }\n    }\n  },\n  \"gcp_object_det_explicit_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"object\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": {\n      \"x\": {\n        \"$numberInt\": \"1\"\n      }\n    }\n  },\n  \"gcp_array_rand_auto_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"array\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": [\n      {\n        \"$numberInt\": \"1\"\n      },\n      {\n        \"$numberInt\": \"2\"\n      },\n      {\n        \"$numberInt\": \"3\"\n      }\n    ]\n  },\n  \"gcp_array_rand_auto_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"array\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": [\n      {\n        \"$numberInt\": \"1\"\n      },\n      {\n        \"$numberInt\": \"2\"\n      },\n      {\n        \"$numberInt\": \"3\"\n      }\n    ]\n  },\n  \"gcp_array_rand_explicit_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"array\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": [\n      {\n        \"$numberInt\": \"1\"\n      },\n      {\n        \"$numberInt\": \"2\"\n      },\n      {\n        \"$numberInt\": \"3\"\n      }\n    ]\n  },\n  \"gcp_array_rand_explicit_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"array\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": [\n      {\n        \"$numberInt\": \"1\"\n      },\n      {\n        \"$numberInt\": \"2\"\n      },\n      {\n        \"$numberInt\": \"3\"\n      }\n    ]\n  },\n  \"gcp_array_det_explicit_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"array\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": [\n      {\n        \"$numberInt\": \"1\"\n      },\n      {\n        \"$numberInt\": \"2\"\n      },\n      {\n        \"$numberInt\": \"3\"\n      }\n    ]\n  },\n  \"gcp_array_det_explicit_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"array\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": [\n      {\n        \"$numberInt\": \"1\"\n      },\n      {\n        \"$numberInt\": \"2\"\n      },\n      {\n        \"$numberInt\": \"3\"\n      }\n    ]\n  },\n  \"gcp_binData=00_rand_auto_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"binData=00\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AQIDBA==\",\n        \"subType\": \"00\"\n      }\n    }\n  },\n  \"gcp_binData=00_rand_auto_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"binData=00\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AQIDBA==\",\n        \"subType\": \"00\"\n      }\n    }\n  },\n  \"gcp_binData=00_rand_explicit_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"binData=00\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AQIDBA==\",\n        \"subType\": \"00\"\n      }\n    }\n  },\n  \"gcp_binData=00_rand_explicit_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"binData=00\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AQIDBA==\",\n        \"subType\": \"00\"\n      }\n    }\n  },\n  \"gcp_binData=00_det_auto_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"binData=00\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AQIDBA==\",\n        \"subType\": \"00\"\n      }\n    }\n  },\n  \"gcp_binData=00_det_explicit_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"binData=00\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AQIDBA==\",\n        \"subType\": \"00\"\n      }\n    }\n  },\n  \"gcp_binData=00_det_explicit_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"binData=00\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AQIDBA==\",\n        \"subType\": \"00\"\n      }\n    }\n  },\n  \"gcp_binData=04_rand_auto_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"binData=04\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AAECAwQFBgcICQoLDA0ODw==\",\n        \"subType\": \"04\"\n      }\n    }\n  },\n  \"gcp_binData=04_rand_auto_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"binData=04\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AAECAwQFBgcICQoLDA0ODw==\",\n        \"subType\": \"04\"\n      }\n    }\n  },\n  \"gcp_binData=04_rand_explicit_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"binData=04\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AAECAwQFBgcICQoLDA0ODw==\",\n        \"subType\": \"04\"\n      }\n    }\n  },\n  \"gcp_binData=04_rand_explicit_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"binData=04\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AAECAwQFBgcICQoLDA0ODw==\",\n        \"subType\": \"04\"\n      }\n    }\n  },\n  \"gcp_binData=04_det_auto_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"binData=04\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AAECAwQFBgcICQoLDA0ODw==\",\n        \"subType\": \"04\"\n      }\n    }\n  },\n  \"gcp_binData=04_det_explicit_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"binData=04\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AAECAwQFBgcICQoLDA0ODw==\",\n        \"subType\": \"04\"\n      }\n    }\n  },\n  \"gcp_binData=04_det_explicit_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"binData=04\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AAECAwQFBgcICQoLDA0ODw==\",\n        \"subType\": \"04\"\n      }\n    }\n  },\n  \"gcp_undefined_rand_explicit_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"undefined\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": {\n      \"$undefined\": true\n    }\n  },\n  \"gcp_undefined_rand_explicit_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"undefined\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": {\n      \"$undefined\": true\n    }\n  },\n  \"gcp_undefined_det_explicit_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"undefined\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": {\n      \"$undefined\": true\n    }\n  },\n  \"gcp_undefined_det_explicit_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"undefined\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": {\n      \"$undefined\": true\n    }\n  },\n  \"gcp_objectId_rand_auto_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"objectId\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$oid\": \"01234567890abcdef0123456\"\n    }\n  },\n  \"gcp_objectId_rand_auto_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"objectId\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$oid\": \"01234567890abcdef0123456\"\n    }\n  },\n  \"gcp_objectId_rand_explicit_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"objectId\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$oid\": \"01234567890abcdef0123456\"\n    }\n  },\n  \"gcp_objectId_rand_explicit_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"objectId\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$oid\": \"01234567890abcdef0123456\"\n    }\n  },\n  \"gcp_objectId_det_auto_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"objectId\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$oid\": \"01234567890abcdef0123456\"\n    }\n  },\n  \"gcp_objectId_det_explicit_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"objectId\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$oid\": \"01234567890abcdef0123456\"\n    }\n  },\n  \"gcp_objectId_det_explicit_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"objectId\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$oid\": \"01234567890abcdef0123456\"\n    }\n  },\n  \"gcp_bool_rand_auto_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"bool\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": true\n  },\n  \"gcp_bool_rand_auto_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"bool\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": true\n  },\n  \"gcp_bool_rand_explicit_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"bool\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": true\n  },\n  \"gcp_bool_rand_explicit_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"bool\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": true\n  },\n  \"gcp_bool_det_explicit_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"bool\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": true\n  },\n  \"gcp_bool_det_explicit_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"bool\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": true\n  },\n  \"gcp_date_rand_auto_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"date\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$date\": {\n        \"$numberLong\": \"12345\"\n      }\n    }\n  },\n  \"gcp_date_rand_auto_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"date\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$date\": {\n        \"$numberLong\": \"12345\"\n      }\n    }\n  },\n  \"gcp_date_rand_explicit_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"date\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$date\": {\n        \"$numberLong\": \"12345\"\n      }\n    }\n  },\n  \"gcp_date_rand_explicit_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"date\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$date\": {\n        \"$numberLong\": \"12345\"\n      }\n    }\n  },\n  \"gcp_date_det_auto_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"date\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$date\": {\n        \"$numberLong\": \"12345\"\n      }\n    }\n  },\n  \"gcp_date_det_explicit_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"date\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$date\": {\n        \"$numberLong\": \"12345\"\n      }\n    }\n  },\n  \"gcp_date_det_explicit_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"date\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$date\": {\n        \"$numberLong\": \"12345\"\n      }\n    }\n  },\n  \"gcp_null_rand_explicit_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"null\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": null\n  },\n  \"gcp_null_rand_explicit_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"null\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": null\n  },\n  \"gcp_null_det_explicit_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"null\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": null\n  },\n  \"gcp_null_det_explicit_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"null\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": null\n  },\n  \"gcp_regex_rand_auto_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"regex\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$regularExpression\": {\n        \"pattern\": \".*\",\n        \"options\": \"\"\n      }\n    }\n  },\n  \"gcp_regex_rand_auto_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"regex\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$regularExpression\": {\n        \"pattern\": \".*\",\n        \"options\": \"\"\n      }\n    }\n  },\n  \"gcp_regex_rand_explicit_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"regex\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$regularExpression\": {\n        \"pattern\": \".*\",\n        \"options\": \"\"\n      }\n    }\n  },\n  \"gcp_regex_rand_explicit_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"regex\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$regularExpression\": {\n        \"pattern\": \".*\",\n        \"options\": \"\"\n      }\n    }\n  },\n  \"gcp_regex_det_auto_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"regex\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$regularExpression\": {\n        \"pattern\": \".*\",\n        \"options\": \"\"\n      }\n    }\n  },\n  \"gcp_regex_det_explicit_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"regex\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$regularExpression\": {\n        \"pattern\": \".*\",\n        \"options\": \"\"\n      }\n    }\n  },\n  \"gcp_regex_det_explicit_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"regex\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$regularExpression\": {\n        \"pattern\": \".*\",\n        \"options\": \"\"\n      }\n    }\n  },\n  \"gcp_dbPointer_rand_auto_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"dbPointer\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$dbPointer\": {\n        \"$ref\": \"db.example\",\n        \"$id\": {\n          \"$oid\": \"01234567890abcdef0123456\"\n        }\n      }\n    }\n  },\n  \"gcp_dbPointer_rand_auto_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"dbPointer\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$dbPointer\": {\n        \"$ref\": \"db.example\",\n        \"$id\": {\n          \"$oid\": \"01234567890abcdef0123456\"\n        }\n      }\n    }\n  },\n  \"gcp_dbPointer_rand_explicit_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"dbPointer\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$dbPointer\": {\n        \"$ref\": \"db.example\",\n        \"$id\": {\n          \"$oid\": \"01234567890abcdef0123456\"\n        }\n      }\n    }\n  },\n  \"gcp_dbPointer_rand_explicit_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"dbPointer\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$dbPointer\": {\n        \"$ref\": \"db.example\",\n        \"$id\": {\n          \"$oid\": \"01234567890abcdef0123456\"\n        }\n      }\n    }\n  },\n  \"gcp_dbPointer_det_auto_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"dbPointer\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$dbPointer\": {\n        \"$ref\": \"db.example\",\n        \"$id\": {\n          \"$oid\": \"01234567890abcdef0123456\"\n        }\n      }\n    }\n  },\n  \"gcp_dbPointer_det_explicit_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"dbPointer\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$dbPointer\": {\n        \"$ref\": \"db.example\",\n        \"$id\": {\n          \"$oid\": \"01234567890abcdef0123456\"\n        }\n      }\n    }\n  },\n  \"gcp_dbPointer_det_explicit_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"dbPointer\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$dbPointer\": {\n        \"$ref\": \"db.example\",\n        \"$id\": {\n          \"$oid\": \"01234567890abcdef0123456\"\n        }\n      }\n    }\n  },\n  \"gcp_javascript_rand_auto_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"javascript\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$code\": \"x=1\"\n    }\n  },\n  \"gcp_javascript_rand_auto_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"javascript\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$code\": \"x=1\"\n    }\n  },\n  \"gcp_javascript_rand_explicit_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"javascript\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$code\": \"x=1\"\n    }\n  },\n  \"gcp_javascript_rand_explicit_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"javascript\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$code\": \"x=1\"\n    }\n  },\n  \"gcp_javascript_det_auto_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"javascript\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$code\": \"x=1\"\n    }\n  },\n  \"gcp_javascript_det_explicit_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"javascript\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$code\": \"x=1\"\n    }\n  },\n  \"gcp_javascript_det_explicit_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"javascript\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$code\": \"x=1\"\n    }\n  },\n  \"gcp_symbol_rand_auto_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"symbol\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$symbol\": \"mongodb-symbol\"\n    }\n  },\n  \"gcp_symbol_rand_auto_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"symbol\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$symbol\": \"mongodb-symbol\"\n    }\n  },\n  \"gcp_symbol_rand_explicit_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"symbol\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$symbol\": \"mongodb-symbol\"\n    }\n  },\n  \"gcp_symbol_rand_explicit_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"symbol\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$symbol\": \"mongodb-symbol\"\n    }\n  },\n  \"gcp_symbol_det_auto_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"symbol\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$symbol\": \"mongodb-symbol\"\n    }\n  },\n  \"gcp_symbol_det_explicit_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"symbol\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$symbol\": \"mongodb-symbol\"\n    }\n  },\n  \"gcp_symbol_det_explicit_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"symbol\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$symbol\": \"mongodb-symbol\"\n    }\n  },\n  \"gcp_javascriptWithScope_rand_auto_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"javascriptWithScope\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$code\": \"x=1\",\n      \"$scope\": {}\n    }\n  },\n  \"gcp_javascriptWithScope_rand_auto_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"javascriptWithScope\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$code\": \"x=1\",\n      \"$scope\": {}\n    }\n  },\n  \"gcp_javascriptWithScope_rand_explicit_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"javascriptWithScope\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$code\": \"x=1\",\n      \"$scope\": {}\n    }\n  },\n  \"gcp_javascriptWithScope_rand_explicit_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"javascriptWithScope\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$code\": \"x=1\",\n      \"$scope\": {}\n    }\n  },\n  \"gcp_javascriptWithScope_det_explicit_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"javascriptWithScope\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": {\n      \"$code\": \"x=1\",\n      \"$scope\": {}\n    }\n  },\n  \"gcp_javascriptWithScope_det_explicit_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"javascriptWithScope\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": {\n      \"$code\": \"x=1\",\n      \"$scope\": {}\n    }\n  },\n  \"gcp_int_rand_auto_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"int\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberInt\": \"123\"\n    }\n  },\n  \"gcp_int_rand_auto_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"int\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberInt\": \"123\"\n    }\n  },\n  \"gcp_int_rand_explicit_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"int\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberInt\": \"123\"\n    }\n  },\n  \"gcp_int_rand_explicit_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"int\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberInt\": \"123\"\n    }\n  },\n  \"gcp_int_det_auto_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"int\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberInt\": \"123\"\n    }\n  },\n  \"gcp_int_det_explicit_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"int\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberInt\": \"123\"\n    }\n  },\n  \"gcp_int_det_explicit_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"int\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberInt\": \"123\"\n    }\n  },\n  \"gcp_timestamp_rand_auto_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"timestamp\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$timestamp\": {\n        \"t\": 0,\n        \"i\": 12345\n      }\n    }\n  },\n  \"gcp_timestamp_rand_auto_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"timestamp\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$timestamp\": {\n        \"t\": 0,\n        \"i\": 12345\n      }\n    }\n  },\n  \"gcp_timestamp_rand_explicit_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"timestamp\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$timestamp\": {\n        \"t\": 0,\n        \"i\": 12345\n      }\n    }\n  },\n  \"gcp_timestamp_rand_explicit_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"timestamp\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$timestamp\": {\n        \"t\": 0,\n        \"i\": 12345\n      }\n    }\n  },\n  \"gcp_timestamp_det_auto_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"timestamp\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$timestamp\": {\n        \"t\": 0,\n        \"i\": 12345\n      }\n    }\n  },\n  \"gcp_timestamp_det_explicit_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"timestamp\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$timestamp\": {\n        \"t\": 0,\n        \"i\": 12345\n      }\n    }\n  },\n  \"gcp_timestamp_det_explicit_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"timestamp\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$timestamp\": {\n        \"t\": 0,\n        \"i\": 12345\n      }\n    }\n  },\n  \"gcp_long_rand_auto_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"long\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberLong\": \"456\"\n    }\n  },\n  \"gcp_long_rand_auto_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"long\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberLong\": \"456\"\n    }\n  },\n  \"gcp_long_rand_explicit_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"long\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberLong\": \"456\"\n    }\n  },\n  \"gcp_long_rand_explicit_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"long\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberLong\": \"456\"\n    }\n  },\n  \"gcp_long_det_auto_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"long\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberLong\": \"456\"\n    }\n  },\n  \"gcp_long_det_explicit_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"long\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberLong\": \"456\"\n    }\n  },\n  \"gcp_long_det_explicit_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"long\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberLong\": \"456\"\n    }\n  },\n  \"gcp_decimal_rand_auto_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"decimal\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberDecimal\": \"1.234\"\n    }\n  },\n  \"gcp_decimal_rand_auto_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"decimal\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberDecimal\": \"1.234\"\n    }\n  },\n  \"gcp_decimal_rand_explicit_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"decimal\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberDecimal\": \"1.234\"\n    }\n  },\n  \"gcp_decimal_rand_explicit_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"decimal\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberDecimal\": \"1.234\"\n    }\n  },\n  \"gcp_decimal_det_explicit_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"decimal\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": {\n      \"$numberDecimal\": \"1.234\"\n    }\n  },\n  \"gcp_decimal_det_explicit_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"decimal\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": {\n      \"$numberDecimal\": \"1.234\"\n    }\n  },\n  \"gcp_minKey_rand_explicit_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"minKey\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": {\n      \"$minKey\": 1\n    }\n  },\n  \"gcp_minKey_rand_explicit_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"minKey\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": {\n      \"$minKey\": 1\n    }\n  },\n  \"gcp_minKey_det_explicit_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"minKey\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": {\n      \"$minKey\": 1\n    }\n  },\n  \"gcp_minKey_det_explicit_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"minKey\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": {\n      \"$minKey\": 1\n    }\n  },\n  \"gcp_maxKey_rand_explicit_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"maxKey\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": {\n      \"$maxKey\": 1\n    }\n  },\n  \"gcp_maxKey_rand_explicit_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"maxKey\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": {\n      \"$maxKey\": 1\n    }\n  },\n  \"gcp_maxKey_det_explicit_id\": {\n    \"kms\": \"gcp\",\n    \"type\": \"maxKey\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": {\n      \"$maxKey\": 1\n    }\n  },\n  \"gcp_maxKey_det_explicit_altname\": {\n    \"kms\": \"gcp\",\n    \"type\": \"maxKey\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": {\n      \"$maxKey\": 1\n    }\n  },\n  \"kmip_double_rand_auto_id\": {\n    \"kms\": \"kmip\",\n    \"type\": \"double\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberDouble\": \"1.234\"\n    }\n  },\n  \"kmip_double_rand_auto_altname\": {\n    \"kms\": \"kmip\",\n    \"type\": \"double\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberDouble\": \"1.234\"\n    }\n  },\n  \"kmip_double_rand_explicit_id\": {\n    \"kms\": \"kmip\",\n    \"type\": \"double\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberDouble\": \"1.234\"\n    }\n  },\n  \"kmip_double_rand_explicit_altname\": {\n    \"kms\": \"kmip\",\n    \"type\": \"double\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberDouble\": \"1.234\"\n    }\n  },\n  \"kmip_double_det_explicit_id\": {\n    \"kms\": \"kmip\",\n    \"type\": \"double\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": {\n      \"$numberDouble\": \"1.234\"\n    }\n  },\n  \"kmip_double_det_explicit_altname\": {\n    \"kms\": \"kmip\",\n    \"type\": \"double\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": {\n      \"$numberDouble\": \"1.234\"\n    }\n  },\n  \"kmip_string_rand_auto_id\": {\n    \"kms\": \"kmip\",\n    \"type\": \"string\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": \"mongodb\"\n  },\n  \"kmip_string_rand_auto_altname\": {\n    \"kms\": \"kmip\",\n    \"type\": \"string\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": \"mongodb\"\n  },\n  \"kmip_string_rand_explicit_id\": {\n    \"kms\": \"kmip\",\n    \"type\": \"string\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": \"mongodb\"\n  },\n  \"kmip_string_rand_explicit_altname\": {\n    \"kms\": \"kmip\",\n    \"type\": \"string\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": \"mongodb\"\n  },\n  \"kmip_string_det_auto_id\": {\n    \"kms\": \"kmip\",\n    \"type\": \"string\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": \"mongodb\"\n  },\n  \"kmip_string_det_explicit_id\": {\n    \"kms\": \"kmip\",\n    \"type\": \"string\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": \"mongodb\"\n  },\n  \"kmip_string_det_explicit_altname\": {\n    \"kms\": \"kmip\",\n    \"type\": \"string\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": \"mongodb\"\n  },\n  \"kmip_object_rand_auto_id\": {\n    \"kms\": \"kmip\",\n    \"type\": \"object\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"x\": {\n        \"$numberInt\": \"1\"\n      }\n    }\n  },\n  \"kmip_object_rand_auto_altname\": {\n    \"kms\": \"kmip\",\n    \"type\": \"object\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"x\": {\n        \"$numberInt\": \"1\"\n      }\n    }\n  },\n  \"kmip_object_rand_explicit_id\": {\n    \"kms\": \"kmip\",\n    \"type\": \"object\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"x\": {\n        \"$numberInt\": \"1\"\n      }\n    }\n  },\n  \"kmip_object_rand_explicit_altname\": {\n    \"kms\": \"kmip\",\n    \"type\": \"object\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"x\": {\n        \"$numberInt\": \"1\"\n      }\n    }\n  },\n  \"kmip_object_det_explicit_id\": {\n    \"kms\": \"kmip\",\n    \"type\": \"object\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": {\n      \"x\": {\n        \"$numberInt\": \"1\"\n      }\n    }\n  },\n  \"kmip_object_det_explicit_altname\": {\n    \"kms\": \"kmip\",\n    \"type\": \"object\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": {\n      \"x\": {\n        \"$numberInt\": \"1\"\n      }\n    }\n  },\n  \"kmip_array_rand_auto_id\": {\n    \"kms\": \"kmip\",\n    \"type\": \"array\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": [\n      {\n        \"$numberInt\": \"1\"\n      },\n      {\n        \"$numberInt\": \"2\"\n      },\n      {\n        \"$numberInt\": \"3\"\n      }\n    ]\n  },\n  \"kmip_array_rand_auto_altname\": {\n    \"kms\": \"kmip\",\n    \"type\": \"array\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": [\n      {\n        \"$numberInt\": \"1\"\n      },\n      {\n        \"$numberInt\": \"2\"\n      },\n      {\n        \"$numberInt\": \"3\"\n      }\n    ]\n  },\n  \"kmip_array_rand_explicit_id\": {\n    \"kms\": \"kmip\",\n    \"type\": \"array\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": [\n      {\n        \"$numberInt\": \"1\"\n      },\n      {\n        \"$numberInt\": \"2\"\n      },\n      {\n        \"$numberInt\": \"3\"\n      }\n    ]\n  },\n  \"kmip_array_rand_explicit_altname\": {\n    \"kms\": \"kmip\",\n    \"type\": \"array\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": [\n      {\n        \"$numberInt\": \"1\"\n      },\n      {\n        \"$numberInt\": \"2\"\n      },\n      {\n        \"$numberInt\": \"3\"\n      }\n    ]\n  },\n  \"kmip_array_det_explicit_id\": {\n    \"kms\": \"kmip\",\n    \"type\": \"array\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": [\n      {\n        \"$numberInt\": \"1\"\n      },\n      {\n        \"$numberInt\": \"2\"\n      },\n      {\n        \"$numberInt\": \"3\"\n      }\n    ]\n  },\n  \"kmip_array_det_explicit_altname\": {\n    \"kms\": \"kmip\",\n    \"type\": \"array\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": [\n      {\n        \"$numberInt\": \"1\"\n      },\n      {\n        \"$numberInt\": \"2\"\n      },\n      {\n        \"$numberInt\": \"3\"\n      }\n    ]\n  },\n  \"kmip_binData=00_rand_auto_id\": {\n    \"kms\": \"kmip\",\n    \"type\": \"binData=00\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AQIDBA==\",\n        \"subType\": \"00\"\n      }\n    }\n  },\n  \"kmip_binData=00_rand_auto_altname\": {\n    \"kms\": \"kmip\",\n    \"type\": \"binData=00\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AQIDBA==\",\n        \"subType\": \"00\"\n      }\n    }\n  },\n  \"kmip_binData=00_rand_explicit_id\": {\n    \"kms\": \"kmip\",\n    \"type\": \"binData=00\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AQIDBA==\",\n        \"subType\": \"00\"\n      }\n    }\n  },\n  \"kmip_binData=00_rand_explicit_altname\": {\n    \"kms\": \"kmip\",\n    \"type\": \"binData=00\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AQIDBA==\",\n        \"subType\": \"00\"\n      }\n    }\n  },\n  \"kmip_binData=00_det_auto_id\": {\n    \"kms\": \"kmip\",\n    \"type\": \"binData=00\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AQIDBA==\",\n        \"subType\": \"00\"\n      }\n    }\n  },\n  \"kmip_binData=00_det_explicit_id\": {\n    \"kms\": \"kmip\",\n    \"type\": \"binData=00\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AQIDBA==\",\n        \"subType\": \"00\"\n      }\n    }\n  },\n  \"kmip_binData=00_det_explicit_altname\": {\n    \"kms\": \"kmip\",\n    \"type\": \"binData=00\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AQIDBA==\",\n        \"subType\": \"00\"\n      }\n    }\n  },\n  \"kmip_binData=04_rand_auto_id\": {\n    \"kms\": \"kmip\",\n    \"type\": \"binData=04\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AAECAwQFBgcICQoLDA0ODw==\",\n        \"subType\": \"04\"\n      }\n    }\n  },\n  \"kmip_binData=04_rand_auto_altname\": {\n    \"kms\": \"kmip\",\n    \"type\": \"binData=04\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AAECAwQFBgcICQoLDA0ODw==\",\n        \"subType\": \"04\"\n      }\n    }\n  },\n  \"kmip_binData=04_rand_explicit_id\": {\n    \"kms\": \"kmip\",\n    \"type\": \"binData=04\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AAECAwQFBgcICQoLDA0ODw==\",\n        \"subType\": \"04\"\n      }\n    }\n  },\n  \"kmip_binData=04_rand_explicit_altname\": {\n    \"kms\": \"kmip\",\n    \"type\": \"binData=04\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AAECAwQFBgcICQoLDA0ODw==\",\n        \"subType\": \"04\"\n      }\n    }\n  },\n  \"kmip_binData=04_det_auto_id\": {\n    \"kms\": \"kmip\",\n    \"type\": \"binData=04\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AAECAwQFBgcICQoLDA0ODw==\",\n        \"subType\": \"04\"\n      }\n    }\n  },\n  \"kmip_binData=04_det_explicit_id\": {\n    \"kms\": \"kmip\",\n    \"type\": \"binData=04\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AAECAwQFBgcICQoLDA0ODw==\",\n        \"subType\": \"04\"\n      }\n    }\n  },\n  \"kmip_binData=04_det_explicit_altname\": {\n    \"kms\": \"kmip\",\n    \"type\": \"binData=04\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$binary\": {\n        \"base64\": \"AAECAwQFBgcICQoLDA0ODw==\",\n        \"subType\": \"04\"\n      }\n    }\n  },\n  \"kmip_undefined_rand_explicit_id\": {\n    \"kms\": \"kmip\",\n    \"type\": \"undefined\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": {\n      \"$undefined\": true\n    }\n  },\n  \"kmip_undefined_rand_explicit_altname\": {\n    \"kms\": \"kmip\",\n    \"type\": \"undefined\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": {\n      \"$undefined\": true\n    }\n  },\n  \"kmip_undefined_det_explicit_id\": {\n    \"kms\": \"kmip\",\n    \"type\": \"undefined\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": {\n      \"$undefined\": true\n    }\n  },\n  \"kmip_undefined_det_explicit_altname\": {\n    \"kms\": \"kmip\",\n    \"type\": \"undefined\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": {\n      \"$undefined\": true\n    }\n  },\n  \"kmip_objectId_rand_auto_id\": {\n    \"kms\": \"kmip\",\n    \"type\": \"objectId\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$oid\": \"01234567890abcdef0123456\"\n    }\n  },\n  \"kmip_objectId_rand_auto_altname\": {\n    \"kms\": \"kmip\",\n    \"type\": \"objectId\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$oid\": \"01234567890abcdef0123456\"\n    }\n  },\n  \"kmip_objectId_rand_explicit_id\": {\n    \"kms\": \"kmip\",\n    \"type\": \"objectId\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$oid\": \"01234567890abcdef0123456\"\n    }\n  },\n  \"kmip_objectId_rand_explicit_altname\": {\n    \"kms\": \"kmip\",\n    \"type\": \"objectId\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$oid\": \"01234567890abcdef0123456\"\n    }\n  },\n  \"kmip_objectId_det_auto_id\": {\n    \"kms\": \"kmip\",\n    \"type\": \"objectId\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$oid\": \"01234567890abcdef0123456\"\n    }\n  },\n  \"kmip_objectId_det_explicit_id\": {\n    \"kms\": \"kmip\",\n    \"type\": \"objectId\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$oid\": \"01234567890abcdef0123456\"\n    }\n  },\n  \"kmip_objectId_det_explicit_altname\": {\n    \"kms\": \"kmip\",\n    \"type\": \"objectId\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$oid\": \"01234567890abcdef0123456\"\n    }\n  },\n  \"kmip_bool_rand_auto_id\": {\n    \"kms\": \"kmip\",\n    \"type\": \"bool\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": true\n  },\n  \"kmip_bool_rand_auto_altname\": {\n    \"kms\": \"kmip\",\n    \"type\": \"bool\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": true\n  },\n  \"kmip_bool_rand_explicit_id\": {\n    \"kms\": \"kmip\",\n    \"type\": \"bool\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": true\n  },\n  \"kmip_bool_rand_explicit_altname\": {\n    \"kms\": \"kmip\",\n    \"type\": \"bool\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": true\n  },\n  \"kmip_bool_det_explicit_id\": {\n    \"kms\": \"kmip\",\n    \"type\": \"bool\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": true\n  },\n  \"kmip_bool_det_explicit_altname\": {\n    \"kms\": \"kmip\",\n    \"type\": \"bool\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": true\n  },\n  \"kmip_date_rand_auto_id\": {\n    \"kms\": \"kmip\",\n    \"type\": \"date\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$date\": {\n        \"$numberLong\": \"12345\"\n      }\n    }\n  },\n  \"kmip_date_rand_auto_altname\": {\n    \"kms\": \"kmip\",\n    \"type\": \"date\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$date\": {\n        \"$numberLong\": \"12345\"\n      }\n    }\n  },\n  \"kmip_date_rand_explicit_id\": {\n    \"kms\": \"kmip\",\n    \"type\": \"date\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$date\": {\n        \"$numberLong\": \"12345\"\n      }\n    }\n  },\n  \"kmip_date_rand_explicit_altname\": {\n    \"kms\": \"kmip\",\n    \"type\": \"date\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$date\": {\n        \"$numberLong\": \"12345\"\n      }\n    }\n  },\n  \"kmip_date_det_auto_id\": {\n    \"kms\": \"kmip\",\n    \"type\": \"date\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$date\": {\n        \"$numberLong\": \"12345\"\n      }\n    }\n  },\n  \"kmip_date_det_explicit_id\": {\n    \"kms\": \"kmip\",\n    \"type\": \"date\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$date\": {\n        \"$numberLong\": \"12345\"\n      }\n    }\n  },\n  \"kmip_date_det_explicit_altname\": {\n    \"kms\": \"kmip\",\n    \"type\": \"date\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$date\": {\n        \"$numberLong\": \"12345\"\n      }\n    }\n  },\n  \"kmip_null_rand_explicit_id\": {\n    \"kms\": \"kmip\",\n    \"type\": \"null\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": null\n  },\n  \"kmip_null_rand_explicit_altname\": {\n    \"kms\": \"kmip\",\n    \"type\": \"null\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": null\n  },\n  \"kmip_null_det_explicit_id\": {\n    \"kms\": \"kmip\",\n    \"type\": \"null\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": null\n  },\n  \"kmip_null_det_explicit_altname\": {\n    \"kms\": \"kmip\",\n    \"type\": \"null\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": null\n  },\n  \"kmip_regex_rand_auto_id\": {\n    \"kms\": \"kmip\",\n    \"type\": \"regex\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$regularExpression\": {\n        \"pattern\": \".*\",\n        \"options\": \"\"\n      }\n    }\n  },\n  \"kmip_regex_rand_auto_altname\": {\n    \"kms\": \"kmip\",\n    \"type\": \"regex\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$regularExpression\": {\n        \"pattern\": \".*\",\n        \"options\": \"\"\n      }\n    }\n  },\n  \"kmip_regex_rand_explicit_id\": {\n    \"kms\": \"kmip\",\n    \"type\": \"regex\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$regularExpression\": {\n        \"pattern\": \".*\",\n        \"options\": \"\"\n      }\n    }\n  },\n  \"kmip_regex_rand_explicit_altname\": {\n    \"kms\": \"kmip\",\n    \"type\": \"regex\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$regularExpression\": {\n        \"pattern\": \".*\",\n        \"options\": \"\"\n      }\n    }\n  },\n  \"kmip_regex_det_auto_id\": {\n    \"kms\": \"kmip\",\n    \"type\": \"regex\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$regularExpression\": {\n        \"pattern\": \".*\",\n        \"options\": \"\"\n      }\n    }\n  },\n  \"kmip_regex_det_explicit_id\": {\n    \"kms\": \"kmip\",\n    \"type\": \"regex\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$regularExpression\": {\n        \"pattern\": \".*\",\n        \"options\": \"\"\n      }\n    }\n  },\n  \"kmip_regex_det_explicit_altname\": {\n    \"kms\": \"kmip\",\n    \"type\": \"regex\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$regularExpression\": {\n        \"pattern\": \".*\",\n        \"options\": \"\"\n      }\n    }\n  },\n  \"kmip_dbPointer_rand_auto_id\": {\n    \"kms\": \"kmip\",\n    \"type\": \"dbPointer\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$dbPointer\": {\n        \"$ref\": \"db.example\",\n        \"$id\": {\n          \"$oid\": \"01234567890abcdef0123456\"\n        }\n      }\n    }\n  },\n  \"kmip_dbPointer_rand_auto_altname\": {\n    \"kms\": \"kmip\",\n    \"type\": \"dbPointer\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$dbPointer\": {\n        \"$ref\": \"db.example\",\n        \"$id\": {\n          \"$oid\": \"01234567890abcdef0123456\"\n        }\n      }\n    }\n  },\n  \"kmip_dbPointer_rand_explicit_id\": {\n    \"kms\": \"kmip\",\n    \"type\": \"dbPointer\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$dbPointer\": {\n        \"$ref\": \"db.example\",\n        \"$id\": {\n          \"$oid\": \"01234567890abcdef0123456\"\n        }\n      }\n    }\n  },\n  \"kmip_dbPointer_rand_explicit_altname\": {\n    \"kms\": \"kmip\",\n    \"type\": \"dbPointer\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$dbPointer\": {\n        \"$ref\": \"db.example\",\n        \"$id\": {\n          \"$oid\": \"01234567890abcdef0123456\"\n        }\n      }\n    }\n  },\n  \"kmip_dbPointer_det_auto_id\": {\n    \"kms\": \"kmip\",\n    \"type\": \"dbPointer\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$dbPointer\": {\n        \"$ref\": \"db.example\",\n        \"$id\": {\n          \"$oid\": \"01234567890abcdef0123456\"\n        }\n      }\n    }\n  },\n  \"kmip_dbPointer_det_explicit_id\": {\n    \"kms\": \"kmip\",\n    \"type\": \"dbPointer\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$dbPointer\": {\n        \"$ref\": \"db.example\",\n        \"$id\": {\n          \"$oid\": \"01234567890abcdef0123456\"\n        }\n      }\n    }\n  },\n  \"kmip_dbPointer_det_explicit_altname\": {\n    \"kms\": \"kmip\",\n    \"type\": \"dbPointer\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$dbPointer\": {\n        \"$ref\": \"db.example\",\n        \"$id\": {\n          \"$oid\": \"01234567890abcdef0123456\"\n        }\n      }\n    }\n  },\n  \"kmip_javascript_rand_auto_id\": {\n    \"kms\": \"kmip\",\n    \"type\": \"javascript\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$code\": \"x=1\"\n    }\n  },\n  \"kmip_javascript_rand_auto_altname\": {\n    \"kms\": \"kmip\",\n    \"type\": \"javascript\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$code\": \"x=1\"\n    }\n  },\n  \"kmip_javascript_rand_explicit_id\": {\n    \"kms\": \"kmip\",\n    \"type\": \"javascript\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$code\": \"x=1\"\n    }\n  },\n  \"kmip_javascript_rand_explicit_altname\": {\n    \"kms\": \"kmip\",\n    \"type\": \"javascript\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$code\": \"x=1\"\n    }\n  },\n  \"kmip_javascript_det_auto_id\": {\n    \"kms\": \"kmip\",\n    \"type\": \"javascript\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$code\": \"x=1\"\n    }\n  },\n  \"kmip_javascript_det_explicit_id\": {\n    \"kms\": \"kmip\",\n    \"type\": \"javascript\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$code\": \"x=1\"\n    }\n  },\n  \"kmip_javascript_det_explicit_altname\": {\n    \"kms\": \"kmip\",\n    \"type\": \"javascript\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$code\": \"x=1\"\n    }\n  },\n  \"kmip_symbol_rand_auto_id\": {\n    \"kms\": \"kmip\",\n    \"type\": \"symbol\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$symbol\": \"mongodb-symbol\"\n    }\n  },\n  \"kmip_symbol_rand_auto_altname\": {\n    \"kms\": \"kmip\",\n    \"type\": \"symbol\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$symbol\": \"mongodb-symbol\"\n    }\n  },\n  \"kmip_symbol_rand_explicit_id\": {\n    \"kms\": \"kmip\",\n    \"type\": \"symbol\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$symbol\": \"mongodb-symbol\"\n    }\n  },\n  \"kmip_symbol_rand_explicit_altname\": {\n    \"kms\": \"kmip\",\n    \"type\": \"symbol\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$symbol\": \"mongodb-symbol\"\n    }\n  },\n  \"kmip_symbol_det_auto_id\": {\n    \"kms\": \"kmip\",\n    \"type\": \"symbol\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$symbol\": \"mongodb-symbol\"\n    }\n  },\n  \"kmip_symbol_det_explicit_id\": {\n    \"kms\": \"kmip\",\n    \"type\": \"symbol\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$symbol\": \"mongodb-symbol\"\n    }\n  },\n  \"kmip_symbol_det_explicit_altname\": {\n    \"kms\": \"kmip\",\n    \"type\": \"symbol\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$symbol\": \"mongodb-symbol\"\n    }\n  },\n  \"kmip_javascriptWithScope_rand_auto_id\": {\n    \"kms\": \"kmip\",\n    \"type\": \"javascriptWithScope\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$code\": \"x=1\",\n      \"$scope\": {}\n    }\n  },\n  \"kmip_javascriptWithScope_rand_auto_altname\": {\n    \"kms\": \"kmip\",\n    \"type\": \"javascriptWithScope\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$code\": \"x=1\",\n      \"$scope\": {}\n    }\n  },\n  \"kmip_javascriptWithScope_rand_explicit_id\": {\n    \"kms\": \"kmip\",\n    \"type\": \"javascriptWithScope\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$code\": \"x=1\",\n      \"$scope\": {}\n    }\n  },\n  \"kmip_javascriptWithScope_rand_explicit_altname\": {\n    \"kms\": \"kmip\",\n    \"type\": \"javascriptWithScope\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$code\": \"x=1\",\n      \"$scope\": {}\n    }\n  },\n  \"kmip_javascriptWithScope_det_explicit_id\": {\n    \"kms\": \"kmip\",\n    \"type\": \"javascriptWithScope\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": {\n      \"$code\": \"x=1\",\n      \"$scope\": {}\n    }\n  },\n  \"kmip_javascriptWithScope_det_explicit_altname\": {\n    \"kms\": \"kmip\",\n    \"type\": \"javascriptWithScope\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": {\n      \"$code\": \"x=1\",\n      \"$scope\": {}\n    }\n  },\n  \"kmip_int_rand_auto_id\": {\n    \"kms\": \"kmip\",\n    \"type\": \"int\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberInt\": \"123\"\n    }\n  },\n  \"kmip_int_rand_auto_altname\": {\n    \"kms\": \"kmip\",\n    \"type\": \"int\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberInt\": \"123\"\n    }\n  },\n  \"kmip_int_rand_explicit_id\": {\n    \"kms\": \"kmip\",\n    \"type\": \"int\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberInt\": \"123\"\n    }\n  },\n  \"kmip_int_rand_explicit_altname\": {\n    \"kms\": \"kmip\",\n    \"type\": \"int\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberInt\": \"123\"\n    }\n  },\n  \"kmip_int_det_auto_id\": {\n    \"kms\": \"kmip\",\n    \"type\": \"int\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberInt\": \"123\"\n    }\n  },\n  \"kmip_int_det_explicit_id\": {\n    \"kms\": \"kmip\",\n    \"type\": \"int\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberInt\": \"123\"\n    }\n  },\n  \"kmip_int_det_explicit_altname\": {\n    \"kms\": \"kmip\",\n    \"type\": \"int\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberInt\": \"123\"\n    }\n  },\n  \"kmip_timestamp_rand_auto_id\": {\n    \"kms\": \"kmip\",\n    \"type\": \"timestamp\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$timestamp\": {\n        \"t\": 0,\n        \"i\": 12345\n      }\n    }\n  },\n  \"kmip_timestamp_rand_auto_altname\": {\n    \"kms\": \"kmip\",\n    \"type\": \"timestamp\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$timestamp\": {\n        \"t\": 0,\n        \"i\": 12345\n      }\n    }\n  },\n  \"kmip_timestamp_rand_explicit_id\": {\n    \"kms\": \"kmip\",\n    \"type\": \"timestamp\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$timestamp\": {\n        \"t\": 0,\n        \"i\": 12345\n      }\n    }\n  },\n  \"kmip_timestamp_rand_explicit_altname\": {\n    \"kms\": \"kmip\",\n    \"type\": \"timestamp\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$timestamp\": {\n        \"t\": 0,\n        \"i\": 12345\n      }\n    }\n  },\n  \"kmip_timestamp_det_auto_id\": {\n    \"kms\": \"kmip\",\n    \"type\": \"timestamp\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$timestamp\": {\n        \"t\": 0,\n        \"i\": 12345\n      }\n    }\n  },\n  \"kmip_timestamp_det_explicit_id\": {\n    \"kms\": \"kmip\",\n    \"type\": \"timestamp\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$timestamp\": {\n        \"t\": 0,\n        \"i\": 12345\n      }\n    }\n  },\n  \"kmip_timestamp_det_explicit_altname\": {\n    \"kms\": \"kmip\",\n    \"type\": \"timestamp\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$timestamp\": {\n        \"t\": 0,\n        \"i\": 12345\n      }\n    }\n  },\n  \"kmip_long_rand_auto_id\": {\n    \"kms\": \"kmip\",\n    \"type\": \"long\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberLong\": \"456\"\n    }\n  },\n  \"kmip_long_rand_auto_altname\": {\n    \"kms\": \"kmip\",\n    \"type\": \"long\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberLong\": \"456\"\n    }\n  },\n  \"kmip_long_rand_explicit_id\": {\n    \"kms\": \"kmip\",\n    \"type\": \"long\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberLong\": \"456\"\n    }\n  },\n  \"kmip_long_rand_explicit_altname\": {\n    \"kms\": \"kmip\",\n    \"type\": \"long\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberLong\": \"456\"\n    }\n  },\n  \"kmip_long_det_auto_id\": {\n    \"kms\": \"kmip\",\n    \"type\": \"long\",\n    \"algo\": \"det\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberLong\": \"456\"\n    }\n  },\n  \"kmip_long_det_explicit_id\": {\n    \"kms\": \"kmip\",\n    \"type\": \"long\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberLong\": \"456\"\n    }\n  },\n  \"kmip_long_det_explicit_altname\": {\n    \"kms\": \"kmip\",\n    \"type\": \"long\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberLong\": \"456\"\n    }\n  },\n  \"kmip_decimal_rand_auto_id\": {\n    \"kms\": \"kmip\",\n    \"type\": \"decimal\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberDecimal\": \"1.234\"\n    }\n  },\n  \"kmip_decimal_rand_auto_altname\": {\n    \"kms\": \"kmip\",\n    \"type\": \"decimal\",\n    \"algo\": \"rand\",\n    \"method\": \"auto\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberDecimal\": \"1.234\"\n    }\n  },\n  \"kmip_decimal_rand_explicit_id\": {\n    \"kms\": \"kmip\",\n    \"type\": \"decimal\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberDecimal\": \"1.234\"\n    }\n  },\n  \"kmip_decimal_rand_explicit_altname\": {\n    \"kms\": \"kmip\",\n    \"type\": \"decimal\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": true,\n    \"value\": {\n      \"$numberDecimal\": \"1.234\"\n    }\n  },\n  \"kmip_decimal_det_explicit_id\": {\n    \"kms\": \"kmip\",\n    \"type\": \"decimal\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": {\n      \"$numberDecimal\": \"1.234\"\n    }\n  },\n  \"kmip_decimal_det_explicit_altname\": {\n    \"kms\": \"kmip\",\n    \"type\": \"decimal\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": {\n      \"$numberDecimal\": \"1.234\"\n    }\n  },\n  \"kmip_minKey_rand_explicit_id\": {\n    \"kms\": \"kmip\",\n    \"type\": \"minKey\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": {\n      \"$minKey\": 1\n    }\n  },\n  \"kmip_minKey_rand_explicit_altname\": {\n    \"kms\": \"kmip\",\n    \"type\": \"minKey\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": {\n      \"$minKey\": 1\n    }\n  },\n  \"kmip_minKey_det_explicit_id\": {\n    \"kms\": \"kmip\",\n    \"type\": \"minKey\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": {\n      \"$minKey\": 1\n    }\n  },\n  \"kmip_minKey_det_explicit_altname\": {\n    \"kms\": \"kmip\",\n    \"type\": \"minKey\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": {\n      \"$minKey\": 1\n    }\n  },\n  \"kmip_maxKey_rand_explicit_id\": {\n    \"kms\": \"kmip\",\n    \"type\": \"maxKey\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": {\n      \"$maxKey\": 1\n    }\n  },\n  \"kmip_maxKey_rand_explicit_altname\": {\n    \"kms\": \"kmip\",\n    \"type\": \"maxKey\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": {\n      \"$maxKey\": 1\n    }\n  },\n  \"kmip_maxKey_det_explicit_id\": {\n    \"kms\": \"kmip\",\n    \"type\": \"maxKey\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": false,\n    \"value\": {\n      \"$maxKey\": 1\n    }\n  },\n  \"kmip_maxKey_det_explicit_altname\": {\n    \"kms\": \"kmip\",\n    \"type\": \"maxKey\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"altname\",\n    \"allowed\": false,\n    \"value\": {\n      \"$maxKey\": 1\n    }\n  },\n  \"payload=0,algo=rand\": {\n    \"kms\": \"local\",\n    \"type\": \"string\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": \"\"\n  },\n  \"payload=1,algo=rand\": {\n    \"kms\": \"local\",\n    \"type\": \"string\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": \"a\"\n  },\n  \"payload=2,algo=rand\": {\n    \"kms\": \"local\",\n    \"type\": \"string\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": \"aa\"\n  },\n  \"payload=3,algo=rand\": {\n    \"kms\": \"local\",\n    \"type\": \"string\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": \"aaa\"\n  },\n  \"payload=4,algo=rand\": {\n    \"kms\": \"local\",\n    \"type\": \"string\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": \"aaaa\"\n  },\n  \"payload=5,algo=rand\": {\n    \"kms\": \"local\",\n    \"type\": \"string\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": \"aaaaa\"\n  },\n  \"payload=6,algo=rand\": {\n    \"kms\": \"local\",\n    \"type\": \"string\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": \"aaaaaa\"\n  },\n  \"payload=7,algo=rand\": {\n    \"kms\": \"local\",\n    \"type\": \"string\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": \"aaaaaaa\"\n  },\n  \"payload=8,algo=rand\": {\n    \"kms\": \"local\",\n    \"type\": \"string\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": \"aaaaaaaa\"\n  },\n  \"payload=9,algo=rand\": {\n    \"kms\": \"local\",\n    \"type\": \"string\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": \"aaaaaaaaa\"\n  },\n  \"payload=10,algo=rand\": {\n    \"kms\": \"local\",\n    \"type\": \"string\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": \"aaaaaaaaaa\"\n  },\n  \"payload=11,algo=rand\": {\n    \"kms\": \"local\",\n    \"type\": \"string\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": \"aaaaaaaaaaa\"\n  },\n  \"payload=12,algo=rand\": {\n    \"kms\": \"local\",\n    \"type\": \"string\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": \"aaaaaaaaaaaa\"\n  },\n  \"payload=13,algo=rand\": {\n    \"kms\": \"local\",\n    \"type\": \"string\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": \"aaaaaaaaaaaaa\"\n  },\n  \"payload=14,algo=rand\": {\n    \"kms\": \"local\",\n    \"type\": \"string\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": \"aaaaaaaaaaaaaa\"\n  },\n  \"payload=15,algo=rand\": {\n    \"kms\": \"local\",\n    \"type\": \"string\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": \"aaaaaaaaaaaaaaa\"\n  },\n  \"payload=16,algo=rand\": {\n    \"kms\": \"local\",\n    \"type\": \"string\",\n    \"algo\": \"rand\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": \"aaaaaaaaaaaaaaaa\"\n  },\n  \"payload=0,algo=det\": {\n    \"kms\": \"local\",\n    \"type\": \"string\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": \"\"\n  },\n  \"payload=1,algo=det\": {\n    \"kms\": \"local\",\n    \"type\": \"string\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": \"a\"\n  },\n  \"payload=2,algo=det\": {\n    \"kms\": \"local\",\n    \"type\": \"string\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": \"aa\"\n  },\n  \"payload=3,algo=det\": {\n    \"kms\": \"local\",\n    \"type\": \"string\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": \"aaa\"\n  },\n  \"payload=4,algo=det\": {\n    \"kms\": \"local\",\n    \"type\": \"string\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": \"aaaa\"\n  },\n  \"payload=5,algo=det\": {\n    \"kms\": \"local\",\n    \"type\": \"string\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": \"aaaaa\"\n  },\n  \"payload=6,algo=det\": {\n    \"kms\": \"local\",\n    \"type\": \"string\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": \"aaaaaa\"\n  },\n  \"payload=7,algo=det\": {\n    \"kms\": \"local\",\n    \"type\": \"string\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": \"aaaaaaa\"\n  },\n  \"payload=8,algo=det\": {\n    \"kms\": \"local\",\n    \"type\": \"string\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": \"aaaaaaaa\"\n  },\n  \"payload=9,algo=det\": {\n    \"kms\": \"local\",\n    \"type\": \"string\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": \"aaaaaaaaa\"\n  },\n  \"payload=10,algo=det\": {\n    \"kms\": \"local\",\n    \"type\": \"string\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": \"aaaaaaaaaa\"\n  },\n  \"payload=11,algo=det\": {\n    \"kms\": \"local\",\n    \"type\": \"string\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": \"aaaaaaaaaaa\"\n  },\n  \"payload=12,algo=det\": {\n    \"kms\": \"local\",\n    \"type\": \"string\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": \"aaaaaaaaaaaa\"\n  },\n  \"payload=13,algo=det\": {\n    \"kms\": \"local\",\n    \"type\": \"string\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": \"aaaaaaaaaaaaa\"\n  },\n  \"payload=14,algo=det\": {\n    \"kms\": \"local\",\n    \"type\": \"string\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": \"aaaaaaaaaaaaaa\"\n  },\n  \"payload=15,algo=det\": {\n    \"kms\": \"local\",\n    \"type\": \"string\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": \"aaaaaaaaaaaaaaa\"\n  },\n  \"payload=16,algo=det\": {\n    \"kms\": \"local\",\n    \"type\": \"string\",\n    \"algo\": \"det\",\n    \"method\": \"explicit\",\n    \"identifier\": \"id\",\n    \"allowed\": true,\n    \"value\": \"aaaaaaaaaaaaaaaa\"\n  }\n}"
  },
  {
    "path": "testdata/client-side-encryption-prose/encrypted-fields.json",
    "content": "{\n    \"fields\": [\n        {\n            \"keyId\": {\n                \"$binary\": {\n                    \"base64\": \"EjRWeBI0mHYSNBI0VniQEg==\",\n                    \"subType\": \"04\"\n                }\n            },\n            \"path\": \"encryptedIndexed\",\n            \"bsonType\": \"string\",\n            \"queries\": {\n                \"queryType\": \"equality\",\n                \"contention\": {\n                    \"$numberLong\": \"0\"\n                }\n            }\n        },\n        {\n            \"keyId\": {\n                \"$binary\": {\n                    \"base64\": \"q83vqxI0mHYSNBI0VniQEg==\",\n                    \"subType\": \"04\"\n                }\n            },\n            \"path\": \"encryptedUnindexed\",\n            \"bsonType\": \"string\"\n        }\n    ]\n}"
  },
  {
    "path": "testdata/client-side-encryption-prose/encryptedFields-prefix-suffix.json",
    "content": "{\n  \"fields\": [\n    {\n            \"keyId\": {\n                \"$binary\": {\n                    \"base64\": \"EjRWeBI0mHYSNBI0VniQEg==\",\n                    \"subType\": \"04\"\n                }\n            },\n            \"path\": \"encryptedText\",\n            \"bsonType\": \"string\",\n            \"queries\": [\n                {\n                    \"queryType\": \"prefixPreview\",\n                    \"strMinQueryLength\": {\n                        \"$numberInt\": \"2\"\n                    },\n                    \"strMaxQueryLength\": {\n                        \"$numberInt\": \"10\"\n                    },\n                    \"caseSensitive\": true,\n                    \"diacriticSensitive\": true\n                },\n                {\n                    \"queryType\": \"suffixPreview\",\n                    \"strMinQueryLength\": {\n                        \"$numberInt\": \"2\"\n                    },\n                    \"strMaxQueryLength\": {\n                        \"$numberInt\": \"10\"\n                    },\n                    \"caseSensitive\": true,\n                    \"diacriticSensitive\": true\n                }\n            ]\n        }\n  ]\n}\n"
  },
  {
    "path": "testdata/client-side-encryption-prose/external-key.json",
    "content": "{\n  \"status\": {\n    \"$numberInt\": \"1\"\n  },\n  \"_id\": {\n    \"$binary\": {\n      \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n      \"subType\": \"04\"\n    }\n  },\n  \"masterKey\": {\n    \"provider\": \"local\"\n  },\n  \"updateDate\": {\n    \"$date\": {\n      \"$numberLong\": \"1557827033449\"\n    }\n  },\n  \"keyMaterial\": {\n    \"$binary\": {\n      \"base64\": \"Ce9HSz/HKKGkIt4uyy+jDuKGA+rLC2cycykMo6vc8jXxqa1UVDYHWq1r+vZKbnnSRBfB981akzRKZCFpC05CTyFqDhXv6OnMjpG97OZEREGIsHEYiJkBW0jJJvfLLgeLsEpBzsro9FztGGXASxyxFRZFhXvHxyiLOKrdWfs7X1O/iK3pEoHMx6uSNSfUOgbebLfIqW7TO++iQS5g1xovXA==\",\n      \"subType\": \"00\"\n    }\n  },\n  \"creationDate\": {\n    \"$date\": {\n      \"$numberLong\": \"1557827033449\"\n    }\n  },\n  \"keyAltNames\": [ \"local\" ]\n}"
  },
  {
    "path": "testdata/client-side-encryption-prose/external-schema.json",
    "content": "{\n  \"bsonType\": \"object\",\n  \"properties\": {\n    \"encrypted\": {\n      \"encrypt\": {\n        \"keyId\": [\n          {\n            \"$binary\": {\n              \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n              \"subType\": \"04\"\n            }\n          }\n        ],\n        \"bsonType\": \"string\",\n        \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\"\n      }\n    }\n  }\n}"
  },
  {
    "path": "testdata/client-side-encryption-prose/key1-document.json",
    "content": "{\n    \"_id\": {\n        \"$binary\": {\n            \"base64\": \"EjRWeBI0mHYSNBI0VniQEg==\",\n            \"subType\": \"04\"\n        }\n    },\n    \"keyMaterial\": {\n        \"$binary\": {\n            \"base64\": \"sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==\",\n            \"subType\": \"00\"\n        }\n    },\n    \"creationDate\": {\n        \"$date\": {\n            \"$numberLong\": \"1648914851981\"\n        }\n    },\n    \"updateDate\": {\n        \"$date\": {\n            \"$numberLong\": \"1648914851981\"\n        }\n    },\n    \"status\": {\n        \"$numberInt\": \"0\"\n    },\n    \"masterKey\": {\n        \"provider\": \"local\"\n    }\n}\n"
  },
  {
    "path": "testdata/client-side-encryption-prose/limits-doc.json",
    "content": "{\n  \"00\": \"a\",\n  \"01\": \"a\",\n  \"02\": \"a\",\n  \"03\": \"a\",\n  \"04\": \"a\",\n  \"05\": \"a\",\n  \"06\": \"a\",\n  \"07\": \"a\",\n  \"08\": \"a\",\n  \"09\": \"a\",\n  \"10\": \"a\",\n  \"11\": \"a\",\n  \"12\": \"a\",\n  \"13\": \"a\",\n  \"14\": \"a\",\n  \"15\": \"a\",\n  \"16\": \"a\",\n  \"17\": \"a\",\n  \"18\": \"a\",\n  \"19\": \"a\",\n  \"20\": \"a\",\n  \"21\": \"a\",\n  \"22\": \"a\",\n  \"23\": \"a\",\n  \"24\": \"a\",\n  \"25\": \"a\",\n  \"26\": \"a\",\n  \"27\": \"a\",\n  \"28\": \"a\",\n  \"29\": \"a\",\n  \"30\": \"a\",\n  \"31\": \"a\",\n  \"32\": \"a\",\n  \"33\": \"a\",\n  \"34\": \"a\",\n  \"35\": \"a\",\n  \"36\": \"a\",\n  \"37\": \"a\",\n  \"38\": \"a\",\n  \"39\": \"a\",\n  \"40\": \"a\",\n  \"41\": \"a\",\n  \"42\": \"a\",\n  \"43\": \"a\",\n  \"44\": \"a\",\n  \"45\": \"a\",\n  \"46\": \"a\",\n  \"47\": \"a\",\n  \"48\": \"a\",\n  \"49\": \"a\",\n  \"50\": \"a\",\n  \"51\": \"a\",\n  \"52\": \"a\",\n  \"53\": \"a\",\n  \"54\": \"a\",\n  \"55\": \"a\",\n  \"56\": \"a\",\n  \"57\": \"a\",\n  \"58\": \"a\",\n  \"59\": \"a\",\n  \"60\": \"a\",\n  \"61\": \"a\",\n  \"62\": \"a\",\n  \"63\": \"a\",\n  \"64\": \"a\",\n  \"65\": \"a\",\n  \"66\": \"a\",\n  \"67\": \"a\",\n  \"68\": \"a\",\n  \"69\": \"a\",\n  \"70\": \"a\",\n  \"71\": \"a\",\n  \"72\": \"a\",\n  \"73\": \"a\",\n  \"74\": \"a\",\n  \"75\": \"a\",\n  \"76\": \"a\",\n  \"77\": \"a\",\n  \"78\": \"a\",\n  \"79\": \"a\",\n  \"80\": \"a\",\n  \"81\": \"a\",\n  \"82\": \"a\",\n  \"83\": \"a\",\n  \"84\": \"a\",\n  \"85\": \"a\",\n  \"86\": \"a\",\n  \"87\": \"a\",\n  \"88\": \"a\",\n  \"89\": \"a\",\n  \"90\": \"a\",\n  \"91\": \"a\",\n  \"92\": \"a\",\n  \"93\": \"a\",\n  \"94\": \"a\",\n  \"95\": \"a\",\n  \"96\": \"a\",\n  \"97\": \"a\",\n  \"98\": \"a\",\n  \"99\": \"a\"\n}"
  },
  {
    "path": "testdata/client-side-encryption-prose/limits-key.json",
    "content": "{\n  \"status\": {\n    \"$numberInt\": \"1\"\n  },\n  \"_id\": {\n    \"$binary\": {\n      \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n      \"subType\": \"04\"\n    }\n  },\n  \"masterKey\": {\n    \"provider\": \"local\"\n  },\n  \"updateDate\": {\n    \"$date\": {\n      \"$numberLong\": \"1557827033449\"\n    }\n  },\n  \"keyMaterial\": {\n    \"$binary\": {\n      \"base64\": \"Ce9HSz/HKKGkIt4uyy+jDuKGA+rLC2cycykMo6vc8jXxqa1UVDYHWq1r+vZKbnnSRBfB981akzRKZCFpC05CTyFqDhXv6OnMjpG97OZEREGIsHEYiJkBW0jJJvfLLgeLsEpBzsro9FztGGXASxyxFRZFhXvHxyiLOKrdWfs7X1O/iK3pEoHMx6uSNSfUOgbebLfIqW7TO++iQS5g1xovXA==\",\n      \"subType\": \"00\"\n    }\n  },\n  \"creationDate\": {\n    \"$date\": {\n      \"$numberLong\": \"1557827033449\"\n    }\n  },\n  \"keyAltNames\": [ \"local\" ]\n}"
  },
  {
    "path": "testdata/client-side-encryption-prose/limits-schema.json",
    "content": "{\n  \"properties\": {\n    \"00\": {\n      \"encrypt\": {\n        \"keyId\": [\n          {\n            \"$binary\": {\n              \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n              \"subType\": \"04\"\n            }\n          }\n        ],\n        \"bsonType\": \"string\",\n        \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\"\n      }\n    },\n    \"01\": {\n      \"encrypt\": {\n        \"keyId\": [\n          {\n            \"$binary\": {\n              \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n              \"subType\": \"04\"\n            }\n          }\n        ],\n        \"bsonType\": \"string\",\n        \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\"\n      }\n    },\n    \"02\": {\n      \"encrypt\": {\n        \"keyId\": [\n          {\n            \"$binary\": {\n              \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n              \"subType\": \"04\"\n            }\n          }\n        ],\n        \"bsonType\": \"string\",\n        \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\"\n      }\n    },\n    \"03\": {\n      \"encrypt\": {\n        \"keyId\": [\n          {\n            \"$binary\": {\n              \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n              \"subType\": \"04\"\n            }\n          }\n        ],\n        \"bsonType\": \"string\",\n        \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\"\n      }\n    },\n    \"04\": {\n      \"encrypt\": {\n        \"keyId\": [\n          {\n            \"$binary\": {\n              \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n              \"subType\": \"04\"\n            }\n          }\n        ],\n        \"bsonType\": \"string\",\n        \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\"\n      }\n    },\n    \"05\": {\n      \"encrypt\": {\n        \"keyId\": [\n          {\n            \"$binary\": {\n              \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n              \"subType\": \"04\"\n            }\n          }\n        ],\n        \"bsonType\": \"string\",\n        \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\"\n      }\n    },\n    \"06\": {\n      \"encrypt\": {\n        \"keyId\": [\n          {\n            \"$binary\": {\n              \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n              \"subType\": \"04\"\n            }\n          }\n        ],\n        \"bsonType\": \"string\",\n        \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\"\n      }\n    },\n    \"07\": {\n      \"encrypt\": {\n        \"keyId\": [\n          {\n            \"$binary\": {\n              \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n              \"subType\": \"04\"\n            }\n          }\n        ],\n        \"bsonType\": \"string\",\n        \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\"\n      }\n    },\n    \"08\": {\n      \"encrypt\": {\n        \"keyId\": [\n          {\n            \"$binary\": {\n              \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n              \"subType\": \"04\"\n            }\n          }\n        ],\n        \"bsonType\": \"string\",\n        \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\"\n      }\n    },\n    \"09\": {\n      \"encrypt\": {\n        \"keyId\": [\n          {\n            \"$binary\": {\n              \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n              \"subType\": \"04\"\n            }\n          }\n        ],\n        \"bsonType\": \"string\",\n        \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\"\n      }\n    },\n    \"10\": {\n      \"encrypt\": {\n        \"keyId\": [\n          {\n            \"$binary\": {\n              \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n              \"subType\": \"04\"\n            }\n          }\n        ],\n        \"bsonType\": \"string\",\n        \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\"\n      }\n    },\n    \"11\": {\n      \"encrypt\": {\n        \"keyId\": [\n          {\n            \"$binary\": {\n              \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n              \"subType\": \"04\"\n            }\n          }\n        ],\n        \"bsonType\": \"string\",\n        \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\"\n      }\n    },\n    \"12\": {\n      \"encrypt\": {\n        \"keyId\": [\n          {\n            \"$binary\": {\n              \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n              \"subType\": \"04\"\n            }\n          }\n        ],\n        \"bsonType\": \"string\",\n        \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\"\n      }\n    },\n    \"13\": {\n      \"encrypt\": {\n        \"keyId\": [\n          {\n            \"$binary\": {\n              \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n              \"subType\": \"04\"\n            }\n          }\n        ],\n        \"bsonType\": \"string\",\n        \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\"\n      }\n    },\n    \"14\": {\n      \"encrypt\": {\n        \"keyId\": [\n          {\n            \"$binary\": {\n              \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n              \"subType\": \"04\"\n            }\n          }\n        ],\n        \"bsonType\": \"string\",\n        \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\"\n      }\n    },\n    \"15\": {\n      \"encrypt\": {\n        \"keyId\": [\n          {\n            \"$binary\": {\n              \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n              \"subType\": \"04\"\n            }\n          }\n        ],\n        \"bsonType\": \"string\",\n        \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\"\n      }\n    },\n    \"16\": {\n      \"encrypt\": {\n        \"keyId\": [\n          {\n            \"$binary\": {\n              \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n              \"subType\": \"04\"\n            }\n          }\n        ],\n        \"bsonType\": \"string\",\n        \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\"\n      }\n    },\n    \"17\": {\n      \"encrypt\": {\n        \"keyId\": [\n          {\n            \"$binary\": {\n              \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n              \"subType\": \"04\"\n            }\n          }\n        ],\n        \"bsonType\": \"string\",\n        \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\"\n      }\n    },\n    \"18\": {\n      \"encrypt\": {\n        \"keyId\": [\n          {\n            \"$binary\": {\n              \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n              \"subType\": \"04\"\n            }\n          }\n        ],\n        \"bsonType\": \"string\",\n        \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\"\n      }\n    },\n    \"19\": {\n      \"encrypt\": {\n        \"keyId\": [\n          {\n            \"$binary\": {\n              \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n              \"subType\": \"04\"\n            }\n          }\n        ],\n        \"bsonType\": \"string\",\n        \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\"\n      }\n    },\n    \"20\": {\n      \"encrypt\": {\n        \"keyId\": [\n          {\n            \"$binary\": {\n              \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n              \"subType\": \"04\"\n            }\n          }\n        ],\n        \"bsonType\": \"string\",\n        \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\"\n      }\n    },\n    \"21\": {\n      \"encrypt\": {\n        \"keyId\": [\n          {\n            \"$binary\": {\n              \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n              \"subType\": \"04\"\n            }\n          }\n        ],\n        \"bsonType\": \"string\",\n        \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\"\n      }\n    },\n    \"22\": {\n      \"encrypt\": {\n        \"keyId\": [\n          {\n            \"$binary\": {\n              \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n              \"subType\": \"04\"\n            }\n          }\n        ],\n        \"bsonType\": \"string\",\n        \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\"\n      }\n    },\n    \"23\": {\n      \"encrypt\": {\n        \"keyId\": [\n          {\n            \"$binary\": {\n              \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n              \"subType\": \"04\"\n            }\n          }\n        ],\n        \"bsonType\": \"string\",\n        \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\"\n      }\n    },\n    \"24\": {\n      \"encrypt\": {\n        \"keyId\": [\n          {\n            \"$binary\": {\n              \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n              \"subType\": \"04\"\n            }\n          }\n        ],\n        \"bsonType\": \"string\",\n        \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\"\n      }\n    },\n    \"25\": {\n      \"encrypt\": {\n        \"keyId\": [\n          {\n            \"$binary\": {\n              \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n              \"subType\": \"04\"\n            }\n          }\n        ],\n        \"bsonType\": \"string\",\n        \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\"\n      }\n    },\n    \"26\": {\n      \"encrypt\": {\n        \"keyId\": [\n          {\n            \"$binary\": {\n              \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n              \"subType\": \"04\"\n            }\n          }\n        ],\n        \"bsonType\": \"string\",\n        \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\"\n      }\n    },\n    \"27\": {\n      \"encrypt\": {\n        \"keyId\": [\n          {\n            \"$binary\": {\n              \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n              \"subType\": \"04\"\n            }\n          }\n        ],\n        \"bsonType\": \"string\",\n        \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\"\n      }\n    },\n    \"28\": {\n      \"encrypt\": {\n        \"keyId\": [\n          {\n            \"$binary\": {\n              \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n              \"subType\": \"04\"\n            }\n          }\n        ],\n        \"bsonType\": \"string\",\n        \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\"\n      }\n    },\n    \"29\": {\n      \"encrypt\": {\n        \"keyId\": [\n          {\n            \"$binary\": {\n              \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n              \"subType\": \"04\"\n            }\n          }\n        ],\n        \"bsonType\": \"string\",\n        \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\"\n      }\n    },\n    \"30\": {\n      \"encrypt\": {\n        \"keyId\": [\n          {\n            \"$binary\": {\n              \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n              \"subType\": \"04\"\n            }\n          }\n        ],\n        \"bsonType\": \"string\",\n        \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\"\n      }\n    },\n    \"31\": {\n      \"encrypt\": {\n        \"keyId\": [\n          {\n            \"$binary\": {\n              \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n              \"subType\": \"04\"\n            }\n          }\n        ],\n        \"bsonType\": \"string\",\n        \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\"\n      }\n    },\n    \"32\": {\n      \"encrypt\": {\n        \"keyId\": [\n          {\n            \"$binary\": {\n              \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n              \"subType\": \"04\"\n            }\n          }\n        ],\n        \"bsonType\": \"string\",\n        \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\"\n      }\n    },\n    \"33\": {\n      \"encrypt\": {\n        \"keyId\": [\n          {\n            \"$binary\": {\n              \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n              \"subType\": \"04\"\n            }\n          }\n        ],\n        \"bsonType\": \"string\",\n        \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\"\n      }\n    },\n    \"34\": {\n      \"encrypt\": {\n        \"keyId\": [\n          {\n            \"$binary\": {\n              \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n              \"subType\": \"04\"\n            }\n          }\n        ],\n        \"bsonType\": \"string\",\n        \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\"\n      }\n    },\n    \"35\": {\n      \"encrypt\": {\n        \"keyId\": [\n          {\n            \"$binary\": {\n              \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n              \"subType\": \"04\"\n            }\n          }\n        ],\n        \"bsonType\": \"string\",\n        \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\"\n      }\n    },\n    \"36\": {\n      \"encrypt\": {\n        \"keyId\": [\n          {\n            \"$binary\": {\n              \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n              \"subType\": \"04\"\n            }\n          }\n        ],\n        \"bsonType\": \"string\",\n        \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\"\n      }\n    },\n    \"37\": {\n      \"encrypt\": {\n        \"keyId\": [\n          {\n            \"$binary\": {\n              \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n              \"subType\": \"04\"\n            }\n          }\n        ],\n        \"bsonType\": \"string\",\n        \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\"\n      }\n    },\n    \"38\": {\n      \"encrypt\": {\n        \"keyId\": [\n          {\n            \"$binary\": {\n              \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n              \"subType\": \"04\"\n            }\n          }\n        ],\n        \"bsonType\": \"string\",\n        \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\"\n      }\n    },\n    \"39\": {\n      \"encrypt\": {\n        \"keyId\": [\n          {\n            \"$binary\": {\n              \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n              \"subType\": \"04\"\n            }\n          }\n        ],\n        \"bsonType\": \"string\",\n        \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\"\n      }\n    },\n    \"40\": {\n      \"encrypt\": {\n        \"keyId\": [\n          {\n            \"$binary\": {\n              \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n              \"subType\": \"04\"\n            }\n          }\n        ],\n        \"bsonType\": \"string\",\n        \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\"\n      }\n    },\n    \"41\": {\n      \"encrypt\": {\n        \"keyId\": [\n          {\n            \"$binary\": {\n              \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n              \"subType\": \"04\"\n            }\n          }\n        ],\n        \"bsonType\": \"string\",\n        \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\"\n      }\n    },\n    \"42\": {\n      \"encrypt\": {\n        \"keyId\": [\n          {\n            \"$binary\": {\n              \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n              \"subType\": \"04\"\n            }\n          }\n        ],\n        \"bsonType\": \"string\",\n        \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\"\n      }\n    },\n    \"43\": {\n      \"encrypt\": {\n        \"keyId\": [\n          {\n            \"$binary\": {\n              \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n              \"subType\": \"04\"\n            }\n          }\n        ],\n        \"bsonType\": \"string\",\n        \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\"\n      }\n    },\n    \"44\": {\n      \"encrypt\": {\n        \"keyId\": [\n          {\n            \"$binary\": {\n              \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n              \"subType\": \"04\"\n            }\n          }\n        ],\n        \"bsonType\": \"string\",\n        \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\"\n      }\n    },\n    \"45\": {\n      \"encrypt\": {\n        \"keyId\": [\n          {\n            \"$binary\": {\n              \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n              \"subType\": \"04\"\n            }\n          }\n        ],\n        \"bsonType\": \"string\",\n        \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\"\n      }\n    },\n    \"46\": {\n      \"encrypt\": {\n        \"keyId\": [\n          {\n            \"$binary\": {\n              \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n              \"subType\": \"04\"\n            }\n          }\n        ],\n        \"bsonType\": \"string\",\n        \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\"\n      }\n    },\n    \"47\": {\n      \"encrypt\": {\n        \"keyId\": [\n          {\n            \"$binary\": {\n              \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n              \"subType\": \"04\"\n            }\n          }\n        ],\n        \"bsonType\": \"string\",\n        \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\"\n      }\n    },\n    \"48\": {\n      \"encrypt\": {\n        \"keyId\": [\n          {\n            \"$binary\": {\n              \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n              \"subType\": \"04\"\n            }\n          }\n        ],\n        \"bsonType\": \"string\",\n        \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\"\n      }\n    },\n    \"49\": {\n      \"encrypt\": {\n        \"keyId\": [\n          {\n            \"$binary\": {\n              \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n              \"subType\": \"04\"\n            }\n          }\n        ],\n        \"bsonType\": \"string\",\n        \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\"\n      }\n    },\n    \"50\": {\n      \"encrypt\": {\n        \"keyId\": [\n          {\n            \"$binary\": {\n              \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n              \"subType\": \"04\"\n            }\n          }\n        ],\n        \"bsonType\": \"string\",\n        \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\"\n      }\n    },\n    \"51\": {\n      \"encrypt\": {\n        \"keyId\": [\n          {\n            \"$binary\": {\n              \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n              \"subType\": \"04\"\n            }\n          }\n        ],\n        \"bsonType\": \"string\",\n        \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\"\n      }\n    },\n    \"52\": {\n      \"encrypt\": {\n        \"keyId\": [\n          {\n            \"$binary\": {\n              \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n              \"subType\": \"04\"\n            }\n          }\n        ],\n        \"bsonType\": \"string\",\n        \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\"\n      }\n    },\n    \"53\": {\n      \"encrypt\": {\n        \"keyId\": [\n          {\n            \"$binary\": {\n              \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n              \"subType\": \"04\"\n            }\n          }\n        ],\n        \"bsonType\": \"string\",\n        \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\"\n      }\n    },\n    \"54\": {\n      \"encrypt\": {\n        \"keyId\": [\n          {\n            \"$binary\": {\n              \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n              \"subType\": \"04\"\n            }\n          }\n        ],\n        \"bsonType\": \"string\",\n        \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\"\n      }\n    },\n    \"55\": {\n      \"encrypt\": {\n        \"keyId\": [\n          {\n            \"$binary\": {\n              \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n              \"subType\": \"04\"\n            }\n          }\n        ],\n        \"bsonType\": \"string\",\n        \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\"\n      }\n    },\n    \"56\": {\n      \"encrypt\": {\n        \"keyId\": [\n          {\n            \"$binary\": {\n              \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n              \"subType\": \"04\"\n            }\n          }\n        ],\n        \"bsonType\": \"string\",\n        \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\"\n      }\n    },\n    \"57\": {\n      \"encrypt\": {\n        \"keyId\": [\n          {\n            \"$binary\": {\n              \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n              \"subType\": \"04\"\n            }\n          }\n        ],\n        \"bsonType\": \"string\",\n        \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\"\n      }\n    },\n    \"58\": {\n      \"encrypt\": {\n        \"keyId\": [\n          {\n            \"$binary\": {\n              \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n              \"subType\": \"04\"\n            }\n          }\n        ],\n        \"bsonType\": \"string\",\n        \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\"\n      }\n    },\n    \"59\": {\n      \"encrypt\": {\n        \"keyId\": [\n          {\n            \"$binary\": {\n              \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n              \"subType\": \"04\"\n            }\n          }\n        ],\n        \"bsonType\": \"string\",\n        \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\"\n      }\n    },\n    \"60\": {\n      \"encrypt\": {\n        \"keyId\": [\n          {\n            \"$binary\": {\n              \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n              \"subType\": \"04\"\n            }\n          }\n        ],\n        \"bsonType\": \"string\",\n        \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\"\n      }\n    },\n    \"61\": {\n      \"encrypt\": {\n        \"keyId\": [\n          {\n            \"$binary\": {\n              \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n              \"subType\": \"04\"\n            }\n          }\n        ],\n        \"bsonType\": \"string\",\n        \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\"\n      }\n    },\n    \"62\": {\n      \"encrypt\": {\n        \"keyId\": [\n          {\n            \"$binary\": {\n              \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n              \"subType\": \"04\"\n            }\n          }\n        ],\n        \"bsonType\": \"string\",\n        \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\"\n      }\n    },\n    \"63\": {\n      \"encrypt\": {\n        \"keyId\": [\n          {\n            \"$binary\": {\n              \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n              \"subType\": \"04\"\n            }\n          }\n        ],\n        \"bsonType\": \"string\",\n        \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\"\n      }\n    },\n    \"64\": {\n      \"encrypt\": {\n        \"keyId\": [\n          {\n            \"$binary\": {\n              \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n              \"subType\": \"04\"\n            }\n          }\n        ],\n        \"bsonType\": \"string\",\n        \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\"\n      }\n    },\n    \"65\": {\n      \"encrypt\": {\n        \"keyId\": [\n          {\n            \"$binary\": {\n              \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n              \"subType\": \"04\"\n            }\n          }\n        ],\n        \"bsonType\": \"string\",\n        \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\"\n      }\n    },\n    \"66\": {\n      \"encrypt\": {\n        \"keyId\": [\n          {\n            \"$binary\": {\n              \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n              \"subType\": \"04\"\n            }\n          }\n        ],\n        \"bsonType\": \"string\",\n        \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\"\n      }\n    },\n    \"67\": {\n      \"encrypt\": {\n        \"keyId\": [\n          {\n            \"$binary\": {\n              \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n              \"subType\": \"04\"\n            }\n          }\n        ],\n        \"bsonType\": \"string\",\n        \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\"\n      }\n    },\n    \"68\": {\n      \"encrypt\": {\n        \"keyId\": [\n          {\n            \"$binary\": {\n              \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n              \"subType\": \"04\"\n            }\n          }\n        ],\n        \"bsonType\": \"string\",\n        \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\"\n      }\n    },\n    \"69\": {\n      \"encrypt\": {\n        \"keyId\": [\n          {\n            \"$binary\": {\n              \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n              \"subType\": \"04\"\n            }\n          }\n        ],\n        \"bsonType\": \"string\",\n        \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\"\n      }\n    },\n    \"70\": {\n      \"encrypt\": {\n        \"keyId\": [\n          {\n            \"$binary\": {\n              \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n              \"subType\": \"04\"\n            }\n          }\n        ],\n        \"bsonType\": \"string\",\n        \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\"\n      }\n    },\n    \"71\": {\n      \"encrypt\": {\n        \"keyId\": [\n          {\n            \"$binary\": {\n              \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n              \"subType\": \"04\"\n            }\n          }\n        ],\n        \"bsonType\": \"string\",\n        \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\"\n      }\n    },\n    \"72\": {\n      \"encrypt\": {\n        \"keyId\": [\n          {\n            \"$binary\": {\n              \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n              \"subType\": \"04\"\n            }\n          }\n        ],\n        \"bsonType\": \"string\",\n        \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\"\n      }\n    },\n    \"73\": {\n      \"encrypt\": {\n        \"keyId\": [\n          {\n            \"$binary\": {\n              \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n              \"subType\": \"04\"\n            }\n          }\n        ],\n        \"bsonType\": \"string\",\n        \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\"\n      }\n    },\n    \"74\": {\n      \"encrypt\": {\n        \"keyId\": [\n          {\n            \"$binary\": {\n              \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n              \"subType\": \"04\"\n            }\n          }\n        ],\n        \"bsonType\": \"string\",\n        \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\"\n      }\n    },\n    \"75\": {\n      \"encrypt\": {\n        \"keyId\": [\n          {\n            \"$binary\": {\n              \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n              \"subType\": \"04\"\n            }\n          }\n        ],\n        \"bsonType\": \"string\",\n        \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\"\n      }\n    },\n    \"76\": {\n      \"encrypt\": {\n        \"keyId\": [\n          {\n            \"$binary\": {\n              \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n              \"subType\": \"04\"\n            }\n          }\n        ],\n        \"bsonType\": \"string\",\n        \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\"\n      }\n    },\n    \"77\": {\n      \"encrypt\": {\n        \"keyId\": [\n          {\n            \"$binary\": {\n              \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n              \"subType\": \"04\"\n            }\n          }\n        ],\n        \"bsonType\": \"string\",\n        \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\"\n      }\n    },\n    \"78\": {\n      \"encrypt\": {\n        \"keyId\": [\n          {\n            \"$binary\": {\n              \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n              \"subType\": \"04\"\n            }\n          }\n        ],\n        \"bsonType\": \"string\",\n        \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\"\n      }\n    },\n    \"79\": {\n      \"encrypt\": {\n        \"keyId\": [\n          {\n            \"$binary\": {\n              \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n              \"subType\": \"04\"\n            }\n          }\n        ],\n        \"bsonType\": \"string\",\n        \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\"\n      }\n    },\n    \"80\": {\n      \"encrypt\": {\n        \"keyId\": [\n          {\n            \"$binary\": {\n              \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n              \"subType\": \"04\"\n            }\n          }\n        ],\n        \"bsonType\": \"string\",\n        \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\"\n      }\n    },\n    \"81\": {\n      \"encrypt\": {\n        \"keyId\": [\n          {\n            \"$binary\": {\n              \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n              \"subType\": \"04\"\n            }\n          }\n        ],\n        \"bsonType\": \"string\",\n        \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\"\n      }\n    },\n    \"82\": {\n      \"encrypt\": {\n        \"keyId\": [\n          {\n            \"$binary\": {\n              \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n              \"subType\": \"04\"\n            }\n          }\n        ],\n        \"bsonType\": \"string\",\n        \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\"\n      }\n    },\n    \"83\": {\n      \"encrypt\": {\n        \"keyId\": [\n          {\n            \"$binary\": {\n              \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n              \"subType\": \"04\"\n            }\n          }\n        ],\n        \"bsonType\": \"string\",\n        \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\"\n      }\n    },\n    \"84\": {\n      \"encrypt\": {\n        \"keyId\": [\n          {\n            \"$binary\": {\n              \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n              \"subType\": \"04\"\n            }\n          }\n        ],\n        \"bsonType\": \"string\",\n        \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\"\n      }\n    },\n    \"85\": {\n      \"encrypt\": {\n        \"keyId\": [\n          {\n            \"$binary\": {\n              \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n              \"subType\": \"04\"\n            }\n          }\n        ],\n        \"bsonType\": \"string\",\n        \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\"\n      }\n    },\n    \"86\": {\n      \"encrypt\": {\n        \"keyId\": [\n          {\n            \"$binary\": {\n              \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n              \"subType\": \"04\"\n            }\n          }\n        ],\n        \"bsonType\": \"string\",\n        \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\"\n      }\n    },\n    \"87\": {\n      \"encrypt\": {\n        \"keyId\": [\n          {\n            \"$binary\": {\n              \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n              \"subType\": \"04\"\n            }\n          }\n        ],\n        \"bsonType\": \"string\",\n        \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\"\n      }\n    },\n    \"88\": {\n      \"encrypt\": {\n        \"keyId\": [\n          {\n            \"$binary\": {\n              \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n              \"subType\": \"04\"\n            }\n          }\n        ],\n        \"bsonType\": \"string\",\n        \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\"\n      }\n    },\n    \"89\": {\n      \"encrypt\": {\n        \"keyId\": [\n          {\n            \"$binary\": {\n              \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n              \"subType\": \"04\"\n            }\n          }\n        ],\n        \"bsonType\": \"string\",\n        \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\"\n      }\n    },\n    \"90\": {\n      \"encrypt\": {\n        \"keyId\": [\n          {\n            \"$binary\": {\n              \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n              \"subType\": \"04\"\n            }\n          }\n        ],\n        \"bsonType\": \"string\",\n        \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\"\n      }\n    },\n    \"91\": {\n      \"encrypt\": {\n        \"keyId\": [\n          {\n            \"$binary\": {\n              \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n              \"subType\": \"04\"\n            }\n          }\n        ],\n        \"bsonType\": \"string\",\n        \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\"\n      }\n    },\n    \"92\": {\n      \"encrypt\": {\n        \"keyId\": [\n          {\n            \"$binary\": {\n              \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n              \"subType\": \"04\"\n            }\n          }\n        ],\n        \"bsonType\": \"string\",\n        \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\"\n      }\n    },\n    \"93\": {\n      \"encrypt\": {\n        \"keyId\": [\n          {\n            \"$binary\": {\n              \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n              \"subType\": \"04\"\n            }\n          }\n        ],\n        \"bsonType\": \"string\",\n        \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\"\n      }\n    },\n    \"94\": {\n      \"encrypt\": {\n        \"keyId\": [\n          {\n            \"$binary\": {\n              \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n              \"subType\": \"04\"\n            }\n          }\n        ],\n        \"bsonType\": \"string\",\n        \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\"\n      }\n    },\n    \"95\": {\n      \"encrypt\": {\n        \"keyId\": [\n          {\n            \"$binary\": {\n              \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n              \"subType\": \"04\"\n            }\n          }\n        ],\n        \"bsonType\": \"string\",\n        \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\"\n      }\n    },\n    \"96\": {\n      \"encrypt\": {\n        \"keyId\": [\n          {\n            \"$binary\": {\n              \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n              \"subType\": \"04\"\n            }\n          }\n        ],\n        \"bsonType\": \"string\",\n        \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\"\n      }\n    },\n    \"97\": {\n      \"encrypt\": {\n        \"keyId\": [\n          {\n            \"$binary\": {\n              \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n              \"subType\": \"04\"\n            }\n          }\n        ],\n        \"bsonType\": \"string\",\n        \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\"\n      }\n    },\n    \"98\": {\n      \"encrypt\": {\n        \"keyId\": [\n          {\n            \"$binary\": {\n              \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n              \"subType\": \"04\"\n            }\n          }\n        ],\n        \"bsonType\": \"string\",\n        \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\"\n      }\n    },\n    \"99\": {\n      \"encrypt\": {\n        \"keyId\": [\n          {\n            \"$binary\": {\n              \"base64\": \"LOCALAAAAAAAAAAAAAAAAA==\",\n              \"subType\": \"04\"\n            }\n          }\n        ],\n        \"bsonType\": \"string\",\n        \"algorithm\": \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\"\n      }\n    }\n  },\n  \"bsonType\": \"object\"\n}"
  },
  {
    "path": "testdata/client-side-encryption-prose/range-encryptedFields-Date.json",
    "content": "{\n  \"fields\": [\n    {\n      \"keyId\": {\n        \"$binary\": {\n          \"base64\": \"EjRWeBI0mHYSNBI0VniQEg==\",\n          \"subType\": \"04\"\n        }\n      },\n      \"path\": \"encryptedDate\",\n      \"bsonType\": \"date\",\n      \"queries\": {\n        \"queryType\": \"range\",\n        \"contention\": {\n          \"$numberLong\": \"0\"\n        },\n        \"trimFactor\": {\n          \"$numberInt\": \"1\"\n        },\n        \"sparsity\": {\n          \"$numberLong\": \"1\"\n        },\n        \"min\": {\n          \"$date\": {\n            \"$numberLong\": \"0\"\n          }\n        },\n        \"max\": {\n          \"$date\": {\n            \"$numberLong\": \"200\"\n          }\n        }\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "testdata/client-side-encryption-prose/range-encryptedFields-DecimalNoPrecision.json",
    "content": "{\n  \"fields\": [\n    {\n      \"keyId\": {\n        \"$binary\": {\n          \"base64\": \"EjRWeBI0mHYSNBI0VniQEg==\",\n          \"subType\": \"04\"\n        }\n      },\n      \"path\": \"encryptedDecimalNoPrecision\",\n      \"bsonType\": \"decimal\",\n      \"queries\": {\n        \"queryType\": \"range\",\n        \"contention\": {\n          \"$numberLong\": \"0\"\n        },\n        \"trimFactor\": {\n          \"$numberInt\": \"1\"\n        },\n        \"sparsity\": {\n          \"$numberLong\": \"1\"\n        }\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "testdata/client-side-encryption-prose/range-encryptedFields-DecimalPrecision.json",
    "content": "{\n  \"fields\": [\n    {\n      \"keyId\": {\n        \"$binary\": {\n          \"base64\": \"EjRWeBI0mHYSNBI0VniQEg==\",\n          \"subType\": \"04\"\n        }\n      },\n      \"path\": \"encryptedDecimalPrecision\",\n      \"bsonType\": \"decimal\",\n      \"queries\": {\n        \"queryType\": \"range\",\n        \"contention\": {\n          \"$numberLong\": \"0\"\n        },\n        \"trimFactor\": {\n          \"$numberInt\": \"1\"\n        },\n        \"sparsity\": {\n          \"$numberLong\": \"1\"\n        },\n        \"min\": {\n          \"$numberDecimal\": \"0.0\"\n        },\n        \"max\": {\n          \"$numberDecimal\": \"200.0\"\n        },\n        \"precision\": {\n          \"$numberInt\": \"2\"\n        }\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "testdata/client-side-encryption-prose/range-encryptedFields-DoubleNoPrecision.json",
    "content": "{\n  \"fields\": [\n    {\n      \"keyId\": {\n        \"$binary\": {\n          \"base64\": \"EjRWeBI0mHYSNBI0VniQEg==\",\n          \"subType\": \"04\"\n        }\n      },\n      \"path\": \"encryptedDoubleNoPrecision\",\n      \"bsonType\": \"double\",\n      \"queries\": {\n        \"queryType\": \"range\",\n        \"contention\": {\n          \"$numberLong\": \"0\"\n        },\n        \"trimFactor\": {\n          \"$numberInt\": \"1\"\n        },\n        \"sparsity\": {\n          \"$numberLong\": \"1\"\n        }\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "testdata/client-side-encryption-prose/range-encryptedFields-DoublePrecision.json",
    "content": "{\n  \"fields\": [\n    {\n      \"keyId\": {\n        \"$binary\": {\n          \"base64\": \"EjRWeBI0mHYSNBI0VniQEg==\",\n          \"subType\": \"04\"\n        }\n      },\n      \"path\": \"encryptedDoublePrecision\",\n      \"bsonType\": \"double\",\n      \"queries\": {\n        \"queryType\": \"range\",\n        \"contention\": {\n          \"$numberLong\": \"0\"\n        },\n        \"trimFactor\": {\n          \"$numberInt\": \"1\"\n        },\n        \"sparsity\": {\n          \"$numberLong\": \"1\"\n        },\n        \"min\": {\n          \"$numberDouble\": \"0.0\"\n        },\n        \"max\": {\n          \"$numberDouble\": \"200.0\"\n        },\n        \"precision\": {\n          \"$numberInt\": \"2\"\n        }\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "testdata/client-side-encryption-prose/range-encryptedFields-Int.json",
    "content": "{\n  \"fields\": [\n    {\n      \"keyId\": {\n        \"$binary\": {\n          \"base64\": \"EjRWeBI0mHYSNBI0VniQEg==\",\n          \"subType\": \"04\"\n        }\n      },\n      \"path\": \"encryptedInt\",\n      \"bsonType\": \"int\",\n      \"queries\": {\n        \"queryType\": \"range\",\n        \"contention\": {\n          \"$numberLong\": \"0\"\n        },\n        \"trimFactor\": {\n          \"$numberInt\": \"1\"\n        },\n        \"sparsity\": {\n          \"$numberLong\": \"1\"\n        },\n        \"min\": {\n          \"$numberInt\": \"0\"\n        },\n        \"max\": {\n          \"$numberInt\": \"200\"\n        }\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "testdata/client-side-encryption-prose/range-encryptedFields-Long.json",
    "content": "{\n  \"fields\": [\n    {\n      \"keyId\": {\n        \"$binary\": {\n          \"base64\": \"EjRWeBI0mHYSNBI0VniQEg==\",\n          \"subType\": \"04\"\n        }\n      },\n      \"path\": \"encryptedLong\",\n      \"bsonType\": \"long\",\n      \"queries\": {\n        \"queryType\": \"range\",\n        \"contention\": {\n          \"$numberLong\": \"0\"\n        },\n        \"trimFactor\": {\n          \"$numberInt\": \"1\"\n        },\n        \"sparsity\": {\n          \"$numberLong\": \"1\"\n        },\n        \"min\": {\n          \"$numberLong\": \"0\"\n        },\n        \"max\": {\n          \"$numberLong\": \"200\"\n        }\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "testdata/kmip-certs/ca-ec.pem",
    "content": "-----BEGIN CERTIFICATE-----\nMIIBtjCCAVsCAj+0MAoGCCqGSM49BAMCMGUxCzAJBgNVBAYTAlVTMREwDwYDVQQI\nDAhOZXcgWW9yazEWMBQGA1UEBwwNTmV3IFlvcmsgQ2l0eTEQMA4GA1UECgwHTW9u\nZ29EQjEMMAoGA1UECwwDREJYMQswCQYDVQQDDAJjYTAgFw0yMjA4MTgwMDM5NTZa\nGA8yMDYyMDgwODAwMzk1NlowZTELMAkGA1UEBhMCVVMxETAPBgNVBAgMCE5ldyBZ\nb3JrMRYwFAYDVQQHDA1OZXcgWW9yayBDaXR5MRAwDgYDVQQKDAdNb25nb0RCMQww\nCgYDVQQLDANEQlgxCzAJBgNVBAMMAmNhMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcD\nQgAE0YOWpm6I2mES1h6CKMw5j29lWDfk36/S7i2+Rw5e9JvGmDGSepDH03MJlm4l\nJ9pou6NJrtAfIhMsxvh4oECodTAKBggqhkjOPQQDAgNJADBGAiEAyr7ByfWjA1aG\nhJD1zFtU2C/+i59vGY3oYQ3gX6Y7HrICIQDkO5JF9tXeDOL5IPkpjBAp6OjACE6Y\nNs42/ywMFmyWhA==\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "testdata/kmip-certs/client-ec.pem",
    "content": "-----BEGIN CERTIFICATE-----\nMIIB/jCCAaSgAwIBAgICYz4wCgYIKoZIzj0EAwIwZTELMAkGA1UEBhMCVVMxETAP\nBgNVBAgMCE5ldyBZb3JrMRYwFAYDVQQHDA1OZXcgWW9yayBDaXR5MRAwDgYDVQQK\nDAdNb25nb0RCMQwwCgYDVQQLDANEQlgxCzAJBgNVBAMMAmNhMCAXDTIyMDgxODAw\nMzk1NloYDzIwNjIwODA4MDAzOTU2WjBpMQswCQYDVQQGEwJVUzERMA8GA1UECAwI\nTmV3IFlvcmsxFjAUBgNVBAcMDU5ldyBZb3JrIENpdHkxEDAOBgNVBAoMB01vbmdv\nREIxDDAKBgNVBAsMA0RCWDEPMA0GA1UEAwwGY2xpZW50MFkwEwYHKoZIzj0CAQYI\nKoZIzj0DAQcDQgAE3lcl3A0IqcmuNeNhk9u8KZKe/Du5/e2xd3B8MRqROb/MDTFY\nJkBatcCNhcSCIjgtFMjZ8Rv2WrrN0fvmEqYpm6M+MDwwCQYDVR0TBAIwADAaBgNV\nHREEEzARgglsb2NhbGhvc3SHBH8AAAEwEwYDVR0lBAwwCgYIKwYBBQUHAwIwCgYI\nKoZIzj0EAwIDSAAwRQIgbkV6V3MK2nZdjr7LV0PKqfxKCWRyxRACEOH61a6dctsC\nIQD6k65C8AXAPOL+cqaZjoEMBpRea4F8gL0jIwzHh+tkAA==\n-----END CERTIFICATE-----\n-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIOw4V3MEjv/5go8JQGr9Au1sa9yzLzPXVsiZ2OihwN7joAoGCCqGSM49\nAwEHoUQDQgAE3lcl3A0IqcmuNeNhk9u8KZKe/Du5/e2xd3B8MRqROb/MDTFYJkBa\ntcCNhcSCIjgtFMjZ8Rv2WrrN0fvmEqYpmw==\n-----END EC PRIVATE KEY-----\n"
  },
  {
    "path": "testdata/kmip-certs/server-ec.pem",
    "content": "-----BEGIN CERTIFICATE-----\nMIIB/TCCAaSgAwIBAgICNNIwCgYIKoZIzj0EAwIwZTELMAkGA1UEBhMCVVMxETAP\nBgNVBAgMCE5ldyBZb3JrMRYwFAYDVQQHDA1OZXcgWW9yayBDaXR5MRAwDgYDVQQK\nDAdNb25nb0RCMQwwCgYDVQQLDANEQlgxCzAJBgNVBAMMAmNhMCAXDTIyMDgxODAw\nMzk1NloYDzIwNjIwODA4MDAzOTU2WjBpMQswCQYDVQQGEwJVUzERMA8GA1UECAwI\nTmV3IFlvcmsxFjAUBgNVBAcMDU5ldyBZb3JrIENpdHkxEDAOBgNVBAoMB01vbmdv\nREIxDDAKBgNVBAsMA0RCWDEPMA0GA1UEAwwGc2VydmVyMFkwEwYHKoZIzj0CAQYI\nKoZIzj0DAQcDQgAEgWFZeI/XzPl42qAMa8UZBLoW2IdkIowhz+iu9F5LkAXI388L\nqbRE4327RvquPO7Ca5eB9GNs77DEtnfMnVuXQ6M+MDwwCQYDVR0TBAIwADAaBgNV\nHREEEzARgglsb2NhbGhvc3SHBH8AAAEwEwYDVR0lBAwwCgYIKwYBBQUHAwEwCgYI\nKoZIzj0EAwIDRwAwRAIgHz7k59ubmnFHM+4GQpz0aeQ+FQGadRYe/h31iRye2wMC\nIAvirZCoxYBLlZ0NoXH8ncmEQzgkCx9hhv7mWpjNRk/h\n-----END CERTIFICATE-----\n-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIIRjzdANl/ghc/LgEdyGRc3xo07YHu1qku3GQNGY2OnboAoGCCqGSM49\nAwEHoUQDQgAEgWFZeI/XzPl42qAMa8UZBLoW2IdkIowhz+iu9F5LkAXI388LqbRE\n4327RvquPO7Ca5eB9GNs77DEtnfMnVuXQw==\n-----END EC PRIVATE KEY-----\n"
  },
  {
    "path": "testdata/mongocrypt/collection-info.json",
    "content": "{\n    \"type\": \"collection\",\n    \"name\": \"test\",\n    \"idIndex\": {\n        \"ns\": \"test.test\",\n        \"name\": \"_id_\",\n        \"key\": {\n            \"_id\": {\n                \"$numberInt\": \"1\"\n            }\n        },\n        \"v\": {\n            \"$numberInt\": \"2\"\n        }\n    },\n    \"options\": {\n        \"validator\": {\n            \"$jsonSchema\": {\n                \"properties\": {\n                    \"ssn\": {\n                        \"encrypt\": {\n                            \"keyId\": {\n                                \"$binary\": {\n                                    \"base64\": \"YWFhYWFhYWFhYWFhYWFhYQ==\",\n                                    \"subType\": \"04\"\n                                }\n                            },\n                            \"type\": \"string\",\n                            \"algorithm\": \"AEAD_AES_CBC_HMAC_SHA512-Deterministic\"\n                        }\n                    }\n                },\n                \"bsonType\": \"object\"\n            }\n        }\n    }\n}"
  },
  {
    "path": "testdata/mongocrypt/command-reply.json",
    "content": "{\n  \"cursor\": {\n    \"firstBatch\": [\n      {\n        \"_id\": 1,\n        \"ssn\": \"457-55-5462\"\n      }\n    ],\n    \"id\": 0,\n    \"ns\": \"test.test\"\n  },\n  \"ok\": 1\n}\n"
  },
  {
    "path": "testdata/mongocrypt/command.json",
    "content": "{\n    \"find\": \"test\",\n    \"filter\": {\n        \"ssn\": \"457-55-5462\"\n    }\n}\n"
  },
  {
    "path": "testdata/mongocrypt/encrypted-command-reply.json",
    "content": "{\n  \"cursor\" : {\n    \"firstBatch\" : [\n      {\n        \"_id\": 1,\n        \"ssn\": {\n          \"$binary\": \"AWFhYWFhYWFhYWFhYWFhYWECRTOW9yZzNDn5dGwuqsrJQNLtgMEKaujhs9aRWRp+7Yo3JK8N8jC8P0Xjll6C1CwLsE/iP5wjOMhVv1KMMyOCSCrHorXRsb2IKPtzl2lKTqQ=\",\n          \"$type\": \"06\"\n        }\n      }\n    ],\n    \"id\" : 0,\n    \"ns\" : \"test.test\"\n  },\n  \"ok\" : 1\n}"
  },
  {
    "path": "testdata/mongocrypt/encrypted-command.json",
    "content": "{\n  \"filter\": {\n    \"ssn\": {\n      \"$binary\": {\n        \"base64\": \"AWFhYWFhYWFhYWFhYWFhYWECRTOW9yZzNDn5dGwuqsrJQNLtgMEKaujhs9aRWRp+7Yo3JK8N8jC8P0Xjll6C1CwLsE/iP5wjOMhVv1KMMyOCSCrHorXRsb2IKPtzl2lKTqQ=\",\n        \"subType\": \"06\"\n      }\n    }\n  },\n  \"find\": \"test\"\n}\n"
  },
  {
    "path": "testdata/mongocrypt/encrypted-value.json",
    "content": "{\n  \"v\": {\n    \"$binary\": \"AWFhYWFhYWFhYWFhYWFhYWECW+zDjR/69eS6VtuMD5+O2lZw6JyiWOw3avI7mnUkdpKzPfvy8F/nlZrgZa2cGmQsb0TmLZuk5trldosnGKD91w==\",\n    \"$type\": \"06\"\n  }\n}\n"
  },
  {
    "path": "testdata/mongocrypt/json-schema.json",
    "content": "{\n  \"properties\": {\n    \"ssn\": {\n      \"encrypt\": {\n        \"keyId\": {\n          \"$binary\": \"YWFhYWFhYWFhYWFhYWFhYQ==\",\n          \"$type\": \"04\"\n        },\n        \"type\": \"string\",\n        \"algorithm\": \"AEAD_AES_CBC_HMAC_SHA512-Deterministic\"\n      }\n    }\n  },\n  \"bsonType\": \"object\"\n}"
  },
  {
    "path": "testdata/mongocrypt/key-document.json",
    "content": "{\n    \"status\": {\n        \"$numberInt\": \"1\"\n    },\n    \"_id\": {\n        \"$binary\": {\n            \"base64\": \"YWFhYWFhYWFhYWFhYWFhYQ==\",\n            \"subType\": \"04\"\n        }\n    },\n    \"masterKey\": {\n        \"region\": \"us-east-1\",\n        \"key\": \"arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0\",\n        \"provider\": \"aws\"\n    },\n    \"updateDate\": {\n        \"$date\": {\n            \"$numberLong\": \"1557827033449\"\n        }\n    },\n    \"keyMaterial\": {\n        \"$binary\": {\n            \"base64\": \"AQICAHhQNmWG2CzOm1dq3kWLM+iDUZhEqnhJwH9wZVpuZ94A8gEqnsxXlR51T5EbEVezUqqKAAAAwjCBvwYJKoZIhvcNAQcGoIGxMIGuAgEAMIGoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDHa4jo6yp0Z18KgbUgIBEIB74sKxWtV8/YHje5lv5THTl0HIbhSwM6EqRlmBiFFatmEWaeMk4tO4xBX65eq670I5TWPSLMzpp8ncGHMmvHqRajNBnmFtbYxN3E3/WjxmdbOOe+OXpnGJPcGsftc7cB2shRfA4lICPnE26+oVNXT6p0Lo20nY5XC7jyCO\",\n            \"subType\": \"00\"\n        }\n    },\n    \"creationDate\": {\n        \"$date\": {\n            \"$numberLong\": \"1557827033449\"\n        }\n    },\n    \"keyAltNames\": [\n        \"altKeyName\",\n        \"another_altname\"\n    ]\n}\n"
  },
  {
    "path": "testdata/mongocrypt/key-filter-keyAltName.json",
    "content": "{\n  \"$or\": [\n    {\n      \"_id\": {\n        \"$in\": []\n      }\n    },\n    {\n      \"keyAltNames\": {\n        \"$in\": [\"altKeyName\"]\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "testdata/mongocrypt/key-filter.json",
    "content": "{\n  \"$or\": [\n    {\n      \"_id\": {\n        \"$in\": [\n          {\n            \"$binary\": \"YWFhYWFhYWFhYWFhYWFhYQ==\",\n            \"$type\": \"04\"\n          }\n        ]\n      }\n    },\n    {\n      \"keyAltNames\": {\n        \"$in\": []\n      }\n    }\n  ]\n}"
  },
  {
    "path": "testdata/mongocrypt/kms-reply.txt",
    "content": "HTTP/1.1 200 OK\nx-amzn-RequestId: deeb35e5-4ecb-4bf1-9af5-84a54ff0af0e\nContent-Type: application/x-amz-json-1.1\nContent-Length: 233\n\n{\"KeyId\": \"arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0\", \"Plaintext\": \"TqhXy3tKckECjy4/ZNykMWG8amBF46isVPzeOgeusKrwheBmYaU8TMG5AHR/NeUDKukqo8hBGgogiQOVpLPkqBQHD8YkLsNbDmHoGOill5QAHnniF/Lz405bGucB5TfR\"}\n"
  },
  {
    "path": "testdata/mongocrypt/list-collections-filter.json",
    "content": "{\n  \"name\": \"test\"\n}"
  },
  {
    "path": "testdata/mongocrypt/local-key-document.json",
    "content": "{\n  \"_id\": {\n    \"$binary\": {\n      \"base64\": \"YWFhYWFhYWFhYWFhYWFhYQ==\",\n      \"subType\": \"04\"\n    }\n  },\n  \"keyMaterial\": {\n    \"$binary\": {\n      \"base64\": \"ACR7Hm33dDOAAD7l2ubZhSpSUWK8BkALUY+qW3UgBAEcTV8sBwZnaAWnzDsmrX55dgmYHWfynDlJogC/e33u6pbhyXvFTs5ow9OLCuCWBJ39T/Ivm3kMaZJybkejY0V+uc4UEdHvVVz/SbitVnzs2WXdMGmo1/HmDRrxGYZjewFslquv8wtUHF5pyB+QDlQBd/al9M444/8bJZFbMSmtIg==\",\n      \"subType\": \"00\"\n    }\n  },\n  \"creationDate\": {\n    \"$date\": \"2023-08-21T14:28:20.875Z\"\n  },\n  \"updateDate\": {\n    \"$date\": \"2023-08-21T14:28:20.875Z\"\n  },\n  \"status\": 0,\n  \"masterKey\": {\n    \"provider\": \"local\"\n  }\n}\n"
  },
  {
    "path": "testdata/mongocrypt/mongocryptd-command-local.json",
    "content": "{\n  \"find\": \"test\",\n  \"filter\": {\n    \"ssn\": \"457-55-5462\"\n  },\n  \"jsonSchema\": {\n    \"properties\": {\n      \"ssn\": {\n        \"encrypt\": {\n          \"keyId\": {\n            \"$binary\": \"YWFhYWFhYWFhYWFhYWFhYQ==\",\n            \"$type\": \"04\"\n          },\n          \"type\": \"string\",\n          \"algorithm\": \"AEAD_AES_CBC_HMAC_SHA512-Deterministic\"\n        }\n      }\n    },\n    \"bsonType\": \"object\"\n  },\n  \"isRemoteSchema\": false\n}"
  },
  {
    "path": "testdata/mongocrypt/mongocryptd-command-remote.json",
    "content": "{\n  \"find\": \"test\",\n  \"filter\": {\n    \"ssn\": \"457-55-5462\"\n  },\n  \"jsonSchema\": {\n    \"properties\": {\n      \"ssn\": {\n        \"encrypt\": {\n          \"keyId\": {\n            \"$binary\": \"YWFhYWFhYWFhYWFhYWFhYQ==\",\n            \"$type\": \"04\"\n          },\n          \"type\": \"string\",\n          \"algorithm\": \"AEAD_AES_CBC_HMAC_SHA512-Deterministic\"\n        }\n      }\n    },\n    \"bsonType\": \"object\"\n  },\n  \"isRemoteSchema\": true\n}"
  },
  {
    "path": "testdata/mongocrypt/mongocryptd-reply.json",
    "content": "{\n    \"schemaRequiresEncryption\": true,\n    \"ok\": {\n        \"$numberInt\": \"1\"\n    },\n    \"result\": {\n        \"filter\": {\n            \"ssn\": {\n                \"$binary\": {\n                    \"base64\": \"ADgAAAAQYQABAAAABWtpABAAAAAEYWFhYWFhYWFhYWFhYWFhYQJ2AAwAAAA0NTctNTUtNTQ2MgAA\",\n                    \"subType\": \"06\"\n                }\n            }\n        },\n        \"find\": \"test\"\n    },\n    \"hasEncryptedPlaceholders\": true\n}"
  },
  {
    "path": "version/version.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\n// DO NOT EDIT. Code generated by Github action\n// \"mongodb-labs/drivers-github-tools/golang/pre-publish\".\n\n// Package version defines the Go Driver version.\npackage version\n\n// Driver is the current version of the driver.\nvar Driver = \"2.5.0\"\n"
  },
  {
    "path": "x/README.md",
    "content": "# MongoDB Go Driver Experimental Packages\n\n**WARNING: THESE PACKAGES ARE EXPERIMENTAL AND MAY BE MODIFIED OR REMOVED\nWITHOUT NOTICE.**\n\nThis directory contains packages intended only for internal use. They are made\navailable to facilitate use cases that require access to internal MongoDB driver\nfunctionality and state. The APIs of these packages are not stable and there is\nno backward compatibility guarantee. Use with extreme caution!\n"
  },
  {
    "path": "x/bsonx/bsoncore/array.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bsoncore\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"strconv\"\n\t\"strings\"\n)\n\n// NewArrayLengthError creates and returns an error for when the length of an array exceeds the\n// bytes available.\nfunc NewArrayLengthError(length, rem int) error {\n\treturn lengthError(\"array\", length, rem)\n}\n\n// Array is a raw bytes representation of a BSON array.\ntype Array []byte\n\n// NewArrayFromReader reads an array from r. This function will only validate the length is\n// correct and that the array ends with a null byte.\nfunc NewArrayFromReader(r io.Reader) (Array, error) {\n\treturn newBufferFromReader(r)\n}\n\n// Index searches for and retrieves the value at the given index. This method will panic if\n// the array is invalid or if the index is out of bounds.\nfunc (a Array) Index(index uint) Value {\n\tvalue, err := a.IndexErr(index)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treturn value\n}\n\n// IndexErr searches for and retrieves the value at the given index.\nfunc (a Array) IndexErr(index uint) (Value, error) {\n\telem, err := indexErr(a, index)\n\tif err != nil {\n\t\treturn Value{}, err\n\t}\n\treturn elem.Value(), err\n}\n\n// DebugString outputs a human readable version of Array. It will attempt to stringify the\n// valid components of the array even if the entire array is not valid.\nfunc (a Array) DebugString() string {\n\tif len(a) < 5 {\n\t\treturn \"<malformed>\"\n\t}\n\tvar buf strings.Builder\n\tbuf.WriteString(\"Array\")\n\tlength, rem, _ := ReadLength(a) // We know we have enough bytes to read the length\n\tbuf.WriteByte('(')\n\tbuf.WriteString(strconv.Itoa(int(length)))\n\tlength -= 4\n\tbuf.WriteString(\")[\")\n\tvar elem Element\n\tvar ok bool\n\tfor length > 1 {\n\t\telem, rem, ok = ReadElement(rem)\n\t\tlength -= int32(len(elem))\n\t\tif !ok {\n\t\t\tbuf.WriteString(fmt.Sprintf(\"<malformed (%d)>\", length))\n\t\t\tbreak\n\t\t}\n\t\tbuf.WriteString(elem.Value().DebugString())\n\t\tif length != 1 {\n\t\t\tbuf.WriteByte(',')\n\t\t}\n\t}\n\tbuf.WriteByte(']')\n\n\treturn buf.String()\n}\n\n// String outputs an ExtendedJSON version of Array. If the Array is not valid, this method\n// returns an empty string.\nfunc (a Array) String() string {\n\tstr, _ := a.StringN(-1)\n\treturn str\n}\n\n// StringN stringifies an array. If N is non-negative, it will truncate the string to N bytes.\n// Otherwise, it will return the full string representation. The second return value indicates\n// whether the string was truncated or not.\nfunc (a Array) StringN(n int) (string, bool) {\n\tlength, rem, ok := ReadLength(a)\n\tif !ok || length < 5 {\n\t\treturn \"\", false\n\t}\n\tlength -= 4 // length bytes\n\tlength--    // final null byte\n\n\tif n == 0 {\n\t\treturn \"\", true\n\t}\n\n\tvar buf strings.Builder\n\tbuf.WriteByte('[')\n\n\tvar truncated bool\n\tvar elem Element\n\tvar str string\n\tfirst := true\n\tfor length > 0 && !truncated {\n\t\tneedStrLen := -1\n\t\t// Set needStrLen if n is positive, meaning we want to limit the string length.\n\t\tif n > 0 {\n\t\t\t// Stop stringifying if we reach the limit, that also ensures needStrLen is\n\t\t\t// greater than 0 if we need to limit the length.\n\t\t\tif buf.Len() >= n {\n\t\t\t\ttruncated = true\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tneedStrLen = n - buf.Len()\n\t\t}\n\n\t\t// Append a comma if this is not the first element.\n\t\tif !first {\n\t\t\tbuf.WriteByte(',')\n\t\t\t// If we are truncating, we need to account for the comma in the length.\n\t\t\tif needStrLen > 0 {\n\t\t\t\tneedStrLen--\n\t\t\t\tif needStrLen == 0 {\n\t\t\t\t\ttruncated = true\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\telem, rem, ok = ReadElement(rem)\n\t\tlength -= int32(len(elem))\n\t\t// Exit on malformed element.\n\t\tif !ok || length < 0 {\n\t\t\treturn \"\", false\n\t\t}\n\n\t\t// Delegate to StringN() on the element.\n\t\tstr, truncated = elem.Value().StringN(needStrLen)\n\t\tbuf.WriteString(str)\n\n\t\tfirst = false\n\t}\n\n\tif n <= 0 || (buf.Len() < n && !truncated) {\n\t\tbuf.WriteByte(']')\n\t} else {\n\t\ttruncated = true\n\t}\n\n\treturn buf.String(), truncated\n}\n\n// Values returns this array as a slice of values. The returned slice will contain valid values.\n// If the array is not valid, the values up to the invalid point will be returned along with an\n// error.\nfunc (a Array) Values() ([]Value, error) {\n\treturn values(a)\n}\n\n// Validate validates the array and ensures the elements contained within are valid.\nfunc (a Array) Validate() error {\n\tlength, rem, ok := ReadLength(a)\n\tif !ok {\n\t\treturn NewInsufficientBytesError(a, rem)\n\t}\n\tif int(length) > len(a) {\n\t\treturn NewArrayLengthError(int(length), len(a))\n\t}\n\tif a[length-1] != 0x00 {\n\t\treturn ErrMissingNull\n\t}\n\n\tlength -= 4\n\tvar elem Element\n\n\tvar keyNum int64\n\tfor length > 1 {\n\t\telem, rem, ok = ReadElement(rem)\n\t\tlength -= int32(len(elem))\n\t\tif !ok {\n\t\t\treturn NewInsufficientBytesError(a, rem)\n\t\t}\n\n\t\t// validate element\n\t\terr := elem.Validate()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\t// validate keys increase numerically\n\t\tif fmt.Sprint(keyNum) != elem.Key() {\n\t\t\treturn fmt.Errorf(\"array key %q is out of order or invalid\", elem.Key())\n\t\t}\n\t\tkeyNum++\n\t}\n\n\tif len(rem) < 1 || rem[0] != 0x00 {\n\t\treturn ErrMissingNull\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "x/bsonx/bsoncore/array_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bsoncore\n\nimport (\n\t\"bytes\"\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"testing\"\n\n\t\"github.com/google/go-cmp/cmp\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n)\n\nfunc TestArray(t *testing.T) {\n\tt.Run(\"Validate\", func(t *testing.T) {\n\t\tt.Run(\"TooShort\", func(t *testing.T) {\n\t\t\twant := NewInsufficientBytesError(nil, nil)\n\t\t\tgot := Array{'\\x00', '\\x00'}.Validate()\n\t\t\tif !compareErrors(got, want) {\n\t\t\t\tt.Errorf(\"Did not get expected error. got %v; want %v\", got, want)\n\t\t\t}\n\t\t})\n\t\tt.Run(\"InvalidLength\", func(t *testing.T) {\n\t\t\twant := NewArrayLengthError(200, 5)\n\t\t\tr := make(Array, 5)\n\t\t\tbinary.LittleEndian.PutUint32(r[0:4], 200)\n\t\t\tgot := r.Validate()\n\t\t\tif !compareErrors(got, want) {\n\t\t\t\tt.Errorf(\"Did not get expected error. got %v; want %v\", got, want)\n\t\t\t}\n\t\t})\n\t\tt.Run(\"Invalid Element\", func(t *testing.T) {\n\t\t\twant := NewInsufficientBytesError(nil, nil)\n\t\t\tr := make(Array, 7)\n\t\t\tbinary.LittleEndian.PutUint32(r[0:4], 7)\n\t\t\tr[4], r[5], r[6] = 0x02, 'f', 0x00\n\t\t\tgot := r.Validate()\n\t\t\tif !compareErrors(got, want) {\n\t\t\t\tt.Errorf(\"Did not get expected error. got %v; want %v\", got, want)\n\t\t\t}\n\t\t})\n\t\tt.Run(\"Missing Null Terminator\", func(t *testing.T) {\n\t\t\twant := ErrMissingNull\n\t\t\tr := make(Array, 6)\n\t\t\tbinary.LittleEndian.PutUint32(r[0:4], 6)\n\t\t\tr[4], r[5] = 0x0A, '0'\n\t\t\tgot := r.Validate()\n\t\t\tif !compareErrors(got, want) {\n\t\t\t\tt.Errorf(\"Did not get expected error. got %v; want %v\", got, want)\n\t\t\t}\n\t\t})\n\t\ttestCases := []struct {\n\t\t\tname string\n\t\t\tr    Array\n\t\t\twant error\n\t\t}{\n\t\t\t{\"array null\", Array{'\\x08', '\\x00', '\\x00', '\\x00', '\\x0A', '0', '\\x00', '\\x00'}, nil},\n\t\t\t{\n\t\t\t\t\"array\",\n\t\t\t\tArray{\n\t\t\t\t\t'\\x1B', '\\x00', '\\x00', '\\x00',\n\t\t\t\t\t'\\x02',\n\t\t\t\t\t'0', '\\x00',\n\t\t\t\t\t'\\x04', '\\x00', '\\x00', '\\x00',\n\t\t\t\t\t'\\x62', '\\x61', '\\x72', '\\x00',\n\t\t\t\t\t'\\x02',\n\t\t\t\t\t'1', '\\x00',\n\t\t\t\t\t'\\x04', '\\x00', '\\x00', '\\x00',\n\t\t\t\t\t'\\x62', '\\x61', '\\x7a', '\\x00',\n\t\t\t\t\t'\\x00',\n\t\t\t\t},\n\t\t\t\tnil,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"subarray\",\n\t\t\t\tArray{\n\t\t\t\t\t'\\x13', '\\x00', '\\x00', '\\x00',\n\t\t\t\t\t'\\x04',\n\t\t\t\t\t'0', '\\x00',\n\t\t\t\t\t'\\x0B', '\\x00', '\\x00', '\\x00', '\\x0A', '0', '\\x00',\n\t\t\t\t\t'\\x0A', '1', '\\x00', '\\x00', '\\x00',\n\t\t\t\t},\n\t\t\t\tnil,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"invalid key order\",\n\t\t\t\tArray{\n\t\t\t\t\t'\\x0B', '\\x00', '\\x00', '\\x00', '\\x0A', '2', '\\x00',\n\t\t\t\t\t'\\x0A', '0', '\\x00', '\\x00', '\\x00',\n\t\t\t\t},\n\t\t\t\terrors.New(`array key \"2\" is out of order or invalid`),\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"invalid key type\",\n\t\t\t\tArray{\n\t\t\t\t\t'\\x0B', '\\x00', '\\x00', '\\x00', '\\x0A', 'p', '\\x00',\n\t\t\t\t\t'\\x0A', 'q', '\\x00', '\\x00', '\\x00',\n\t\t\t\t},\n\t\t\t\terrors.New(`array key \"p\" is out of order or invalid`),\n\t\t\t},\n\t\t}\n\n\t\tfor _, tc := range testCases {\n\t\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\t\tgot := tc.r.Validate()\n\t\t\t\tif !compareErrors(got, tc.want) {\n\t\t\t\t\tt.Errorf(\"Returned error does not match. got %v; want %v\", got, tc.want)\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t})\n\tt.Run(\"Index\", func(t *testing.T) {\n\t\tt.Run(\"Out of bounds\", func(t *testing.T) {\n\t\t\trdr := Array{0xe, 0x0, 0x0, 0x0, 0xa, '0', 0x0, 0xa, '1', 0x0, 0xa, 0x7a, 0x0, 0x0}\n\t\t\t_, err := rdr.IndexErr(3)\n\t\t\tif !errors.Is(err, ErrOutOfBounds) {\n\t\t\t\tt.Errorf(\"Out of bounds should be returned when accessing element beyond end of Array. got %v; want %v\", err, ErrOutOfBounds)\n\t\t\t}\n\t\t})\n\t\tt.Run(\"Validation Error\", func(t *testing.T) {\n\t\t\trdr := Array{0x07, 0x00, 0x00, 0x00, 0x00}\n\t\t\t_, got := rdr.IndexErr(1)\n\t\t\twant := NewInsufficientBytesError(nil, nil)\n\t\t\tif !compareErrors(got, want) {\n\t\t\t\tt.Errorf(\"Did not receive expected error. got %v; want %v\", got, want)\n\t\t\t}\n\t\t})\n\t\ttestArray := Array{\n\t\t\t'\\x26', '\\x00', '\\x00', '\\x00',\n\t\t\t'\\x02',\n\t\t\t'0', '\\x00',\n\t\t\t'\\x04', '\\x00', '\\x00', '\\x00',\n\t\t\t'\\x62', '\\x61', '\\x72', '\\x00',\n\t\t\t'\\x02',\n\t\t\t'1', '\\x00',\n\t\t\t'\\x04', '\\x00', '\\x00', '\\x00',\n\t\t\t'\\x62', '\\x61', '\\x7a', '\\x00',\n\t\t\t'\\x02',\n\t\t\t'2', '\\x00',\n\t\t\t'\\x04', '\\x00', '\\x00', '\\x00',\n\t\t\t'\\x71', '\\x75', '\\x78', '\\x00',\n\t\t\t'\\x00',\n\t\t}\n\t\ttestCases := []struct {\n\t\t\tname  string\n\t\t\tindex uint\n\t\t\twant  Value\n\t\t}{\n\t\t\t{\n\t\t\t\t\"first\",\n\t\t\t\t0,\n\t\t\t\tValue{\n\t\t\t\t\tType: TypeString,\n\t\t\t\t\tData: []byte{0x04, 0x00, 0x00, 0x00, 0x62, 0x61, 0x72, 0x00},\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"second\",\n\t\t\t\t1,\n\t\t\t\tValue{\n\t\t\t\t\tType: TypeString,\n\t\t\t\t\tData: []byte{0x04, 0x00, 0x00, 0x00, 0x62, 0x61, 0x7a, 0x00},\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"third\",\n\t\t\t\t2,\n\t\t\t\tValue{\n\t\t\t\t\tType: TypeString,\n\t\t\t\t\tData: []byte{0x04, 0x00, 0x00, 0x00, 0x71, 0x75, 0x78, 0x00},\n\t\t\t\t},\n\t\t\t},\n\t\t}\n\n\t\tfor _, tc := range testCases {\n\t\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\t\tt.Run(\"IndexErr\", func(t *testing.T) {\n\t\t\t\t\tgot, err := testArray.IndexErr(tc.index)\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\tt.Errorf(\"Unexpected error from IndexErr: %s\", err)\n\t\t\t\t\t}\n\t\t\t\t\tif diff := cmp.Diff(got, tc.want); diff != \"\" {\n\t\t\t\t\t\tt.Errorf(\"Arrays differ: (-got +want)\\n%s\", diff)\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t\tt.Run(\"Index\", func(t *testing.T) {\n\t\t\t\t\tdefer func() {\n\t\t\t\t\t\tif err := recover(); err != nil {\n\t\t\t\t\t\t\tt.Errorf(\"Unexpected error: %v\", err)\n\t\t\t\t\t\t}\n\t\t\t\t\t}()\n\t\t\t\t\tgot := testArray.Index(tc.index)\n\t\t\t\t\tif diff := cmp.Diff(got, tc.want); diff != \"\" {\n\t\t\t\t\t\tt.Errorf(\"Arrays differ: (-got +want)\\n%s\", diff)\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t})\n\t\t}\n\t})\n\tt.Run(\"NewArrayFromReader\", func(t *testing.T) {\n\t\ttestCases := []struct {\n\t\t\tname     string\n\t\t\tioReader io.Reader\n\t\t\tarr      Array\n\t\t\terr      error\n\t\t}{\n\t\t\t{\n\t\t\t\t\"nil reader\",\n\t\t\t\tnil,\n\t\t\t\tnil,\n\t\t\t\tErrNilReader,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"premature end of reader\",\n\t\t\t\tbytes.NewBuffer([]byte{}),\n\t\t\t\tnil,\n\t\t\t\tio.EOF,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"empty Array\",\n\t\t\t\tbytes.NewBuffer([]byte{5, 0, 0, 0, 0}),\n\t\t\t\t[]byte{5, 0, 0, 0, 0},\n\t\t\t\tnil,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"non-empty Array\",\n\t\t\t\tbytes.NewBuffer([]byte{\n\t\t\t\t\t'\\x1B', '\\x00', '\\x00', '\\x00',\n\t\t\t\t\t'\\x02',\n\t\t\t\t\t'0', '\\x00',\n\t\t\t\t\t'\\x04', '\\x00', '\\x00', '\\x00',\n\t\t\t\t\t'\\x62', '\\x61', '\\x72', '\\x00',\n\t\t\t\t\t'\\x02',\n\t\t\t\t\t'1', '\\x00',\n\t\t\t\t\t'\\x04', '\\x00', '\\x00', '\\x00',\n\t\t\t\t\t'\\x62', '\\x61', '\\x7a', '\\x00',\n\t\t\t\t\t'\\x00',\n\t\t\t\t}),\n\t\t\t\t[]byte{\n\t\t\t\t\t'\\x1B', '\\x00', '\\x00', '\\x00',\n\t\t\t\t\t'\\x02',\n\t\t\t\t\t'0', '\\x00',\n\t\t\t\t\t'\\x04', '\\x00', '\\x00', '\\x00',\n\t\t\t\t\t'\\x62', '\\x61', '\\x72', '\\x00',\n\t\t\t\t\t'\\x02',\n\t\t\t\t\t'1', '\\x00',\n\t\t\t\t\t'\\x04', '\\x00', '\\x00', '\\x00',\n\t\t\t\t\t'\\x62', '\\x61', '\\x7a', '\\x00',\n\t\t\t\t\t'\\x00',\n\t\t\t\t},\n\t\t\t\tnil,\n\t\t\t},\n\t\t}\n\n\t\tfor _, tc := range testCases {\n\t\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\t\tarr, err := NewArrayFromReader(tc.ioReader)\n\t\t\t\tif !compareErrors(err, tc.err) {\n\t\t\t\t\tt.Errorf(\"errors do not match. got %v; want %v\", err, tc.err)\n\t\t\t\t}\n\t\t\t\tif !bytes.Equal(tc.arr, arr) {\n\t\t\t\t\tt.Errorf(\"Arrays differ. got %v; want %v\", tc.arr, arr)\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t})\n\tt.Run(\"DebugString\", func(t *testing.T) {\n\t\ttestCases := []struct {\n\t\t\tname             string\n\t\t\tarr              Array\n\t\t\tarrayString      string\n\t\t\tarrayDebugString string\n\t\t}{\n\t\t\t{\n\t\t\t\t\"array\",\n\t\t\t\tArray{\n\t\t\t\t\t'\\x1B', '\\x00', '\\x00', '\\x00',\n\t\t\t\t\t'\\x02',\n\t\t\t\t\t'0', '\\x00',\n\t\t\t\t\t'\\x04', '\\x00', '\\x00', '\\x00',\n\t\t\t\t\t'\\x62', '\\x61', '\\x72', '\\x00',\n\t\t\t\t\t'\\x02',\n\t\t\t\t\t'1', '\\x00',\n\t\t\t\t\t'\\x04', '\\x00', '\\x00', '\\x00',\n\t\t\t\t\t'\\x62', '\\x61', '\\x7a', '\\x00',\n\t\t\t\t\t'\\x00',\n\t\t\t\t},\n\t\t\t\t`[\"bar\",\"baz\"]`,\n\t\t\t\t`Array(27)[\"bar\",\"baz\"]`,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"subarray\",\n\t\t\t\tArray{\n\t\t\t\t\t'\\x13', '\\x00', '\\x00', '\\x00',\n\t\t\t\t\t'\\x04',\n\t\t\t\t\t'0', '\\x00',\n\t\t\t\t\t'\\x0B', '\\x00', '\\x00', '\\x00',\n\t\t\t\t\t'\\x0A', '0', '\\x00',\n\t\t\t\t\t'\\x0A', '1', '\\x00',\n\t\t\t\t\t'\\x00', '\\x00',\n\t\t\t\t},\n\t\t\t\t`[[null,null]]`,\n\t\t\t\t`Array(19)[Array(11)[null,null]]`,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"malformed--length too small\",\n\t\t\t\tArray{\n\t\t\t\t\t'\\x04', '\\x00', '\\x00', '\\x00',\n\t\t\t\t\t'\\x00',\n\t\t\t\t},\n\t\t\t\t``,\n\t\t\t\t`Array(4)[]`,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"malformed--length too large\",\n\t\t\t\tArray{\n\t\t\t\t\t'\\x13', '\\x00', '\\x00', '\\x00',\n\t\t\t\t\t'\\x00',\n\t\t\t\t},\n\t\t\t\t``,\n\t\t\t\t`Array(19)[<malformed (15)>]`,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"malformed--missing null byte\",\n\t\t\t\tArray{\n\t\t\t\t\t'\\x06', '\\x00', '\\x00', '\\x00',\n\t\t\t\t\t'\\x02', '0',\n\t\t\t\t},\n\t\t\t\t``,\n\t\t\t\t`Array(6)[<malformed (2)>]`,\n\t\t\t},\n\t\t}\n\n\t\tfor _, tc := range testCases {\n\t\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\t\tarrayString := tc.arr.String()\n\t\t\t\tif arrayString != tc.arrayString {\n\t\t\t\t\tt.Errorf(\"array strings do not match. got %q; want %q\",\n\t\t\t\t\t\tarrayString, tc.arrayString)\n\t\t\t\t}\n\n\t\t\t\tarrayDebugString := tc.arr.DebugString()\n\t\t\t\tif arrayDebugString != tc.arrayDebugString {\n\t\t\t\t\tt.Errorf(\"array debug strings do not match. got %q; want %q\",\n\t\t\t\t\t\tarrayDebugString, tc.arrayDebugString)\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t})\n}\n\nvar arrayStringTestCases = []struct {\n\tdescription string\n\tarray       Array\n\twant        string\n}{\n\t{\n\t\tdescription: \"empty array\",\n\t\tarray:       BuildArray(nil),\n\t\twant:        `[]`,\n\t},\n\t{\n\t\tdescription: \"array with 1 element\",\n\t\tarray: BuildArray(nil, Value{\n\t\t\tType: TypeInt32,\n\t\t\tData: AppendInt32(nil, 123),\n\t\t}),\n\t\twant: `[{\"$numberInt\":\"123\"}]`,\n\t},\n\t{\n\t\tdescription: \"nested array\",\n\t\tarray: BuildArray(nil, Value{\n\t\t\tType: TypeArray,\n\t\t\tData: BuildArray(nil, Value{\n\t\t\t\tType: TypeString,\n\t\t\t\tData: AppendString(nil, \"abc\"),\n\t\t\t}),\n\t\t}),\n\t\twant: `[[\"abc\"]]`,\n\t},\n\t{\n\t\tdescription: \"array with mixed types\",\n\t\tarray: BuildArray(nil,\n\t\t\tValue{\n\t\t\t\tType: TypeString,\n\t\t\t\tData: AppendString(nil, \"abc\"),\n\t\t\t},\n\t\t\tValue{\n\t\t\t\tType: TypeInt32,\n\t\t\t\tData: AppendInt32(nil, 123),\n\t\t\t},\n\t\t\tValue{\n\t\t\t\tType: TypeBoolean,\n\t\t\t\tData: AppendBoolean(nil, true),\n\t\t\t},\n\t\t),\n\t\twant: `[\"abc\",{\"$numberInt\":\"123\"},true]`,\n\t},\n}\n\nfunc TestArray_String(t *testing.T) {\n\tfor _, tc := range arrayStringTestCases {\n\t\tt.Run(tc.description, func(t *testing.T) {\n\t\t\tgot := tc.array.String()\n\t\t\tassert.Equal(t, tc.want, got, \"expected string %s, got %s\", tc.want, got)\n\t\t})\n\t}\n}\n\nfunc TestArray_StringN(t *testing.T) {\n\tfor _, tc := range arrayStringTestCases {\n\t\tfor n := -1; n <= len(tc.want)+1; n++ {\n\t\t\tt.Run(fmt.Sprintf(\"%s n==%d\", tc.description, n), func(t *testing.T) {\n\t\t\t\tgot, truncated := tc.array.StringN(n)\n\t\t\t\tl := n\n\t\t\t\ttoBeTruncated := true\n\t\t\t\tif l >= len(tc.want) || l < 0 {\n\t\t\t\t\tl = len(tc.want)\n\t\t\t\t\ttoBeTruncated = false\n\t\t\t\t}\n\t\t\t\twant := tc.want[:l]\n\t\t\t\tassert.Equal(t, want, got, \"expected truncated string %s, got %s\", want, got)\n\t\t\t\tassert.Equal(t, toBeTruncated, truncated, \"expected truncated to be %t, got %t\", toBeTruncated, truncated)\n\t\t\t})\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "x/bsonx/bsoncore/bson_arraybuilder.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bsoncore\n\nimport (\n\t\"strconv\"\n)\n\n// ArrayBuilder builds a bson array\ntype ArrayBuilder struct {\n\tarr     []byte\n\tindexes []int32\n\tkeys    []int\n}\n\n// NewArrayBuilder creates a new ArrayBuilder\nfunc NewArrayBuilder() *ArrayBuilder {\n\treturn (&ArrayBuilder{}).startArray()\n}\n\n// startArray reserves the array's length and sets the index to where the length begins\nfunc (a *ArrayBuilder) startArray() *ArrayBuilder {\n\tvar index int32\n\tindex, a.arr = AppendArrayStart(a.arr)\n\ta.indexes = append(a.indexes, index)\n\ta.keys = append(a.keys, 0)\n\treturn a\n}\n\n// Build updates the length of the array and index to the beginning of the documents length\n// bytes, then returns the array (bson bytes)\nfunc (a *ArrayBuilder) Build() Array {\n\tlastIndex := len(a.indexes) - 1\n\tlastKey := len(a.keys) - 1\n\ta.arr, _ = AppendArrayEnd(a.arr, a.indexes[lastIndex])\n\ta.indexes = a.indexes[:lastIndex]\n\ta.keys = a.keys[:lastKey]\n\treturn a.arr\n}\n\n// incrementKey() increments the value keys and returns the key to be used to a.appendArray* functions\nfunc (a *ArrayBuilder) incrementKey() string {\n\tidx := len(a.keys) - 1\n\tkey := strconv.Itoa(a.keys[idx])\n\ta.keys[idx]++\n\treturn key\n}\n\n// AppendInt32 will append i32 to ArrayBuilder.arr\nfunc (a *ArrayBuilder) AppendInt32(i32 int32) *ArrayBuilder {\n\ta.arr = AppendInt32Element(a.arr, a.incrementKey(), i32)\n\treturn a\n}\n\n// AppendDocument will append doc to ArrayBuilder.arr\nfunc (a *ArrayBuilder) AppendDocument(doc []byte) *ArrayBuilder {\n\ta.arr = AppendDocumentElement(a.arr, a.incrementKey(), doc)\n\treturn a\n}\n\n// AppendArray will append arr to ArrayBuilder.arr\nfunc (a *ArrayBuilder) AppendArray(arr []byte) *ArrayBuilder {\n\ta.arr = AppendArrayElement(a.arr, a.incrementKey(), arr)\n\treturn a\n}\n\n// AppendDouble will append f to ArrayBuilder.doc\nfunc (a *ArrayBuilder) AppendDouble(f float64) *ArrayBuilder {\n\ta.arr = AppendDoubleElement(a.arr, a.incrementKey(), f)\n\treturn a\n}\n\n// AppendString will append str to ArrayBuilder.doc\nfunc (a *ArrayBuilder) AppendString(str string) *ArrayBuilder {\n\ta.arr = AppendStringElement(a.arr, a.incrementKey(), str)\n\treturn a\n}\n\n// AppendObjectID will append oid to ArrayBuilder.doc\nfunc (a *ArrayBuilder) AppendObjectID(oid objectID) *ArrayBuilder {\n\ta.arr = AppendObjectIDElement(a.arr, a.incrementKey(), oid)\n\treturn a\n}\n\n// AppendBinary will append a BSON binary element using subtype, and\n// b to a.arr\nfunc (a *ArrayBuilder) AppendBinary(subtype byte, b []byte) *ArrayBuilder {\n\ta.arr = AppendBinaryElement(a.arr, a.incrementKey(), subtype, b)\n\treturn a\n}\n\n// AppendUndefined will append a BSON undefined element using key to a.arr\nfunc (a *ArrayBuilder) AppendUndefined() *ArrayBuilder {\n\ta.arr = AppendUndefinedElement(a.arr, a.incrementKey())\n\treturn a\n}\n\n// AppendBoolean will append a boolean element using b to a.arr\nfunc (a *ArrayBuilder) AppendBoolean(b bool) *ArrayBuilder {\n\ta.arr = AppendBooleanElement(a.arr, a.incrementKey(), b)\n\treturn a\n}\n\n// AppendDateTime will append datetime element dt to a.arr\nfunc (a *ArrayBuilder) AppendDateTime(dt int64) *ArrayBuilder {\n\ta.arr = AppendDateTimeElement(a.arr, a.incrementKey(), dt)\n\treturn a\n}\n\n// AppendNull will append a null element to a.arr\nfunc (a *ArrayBuilder) AppendNull() *ArrayBuilder {\n\ta.arr = AppendNullElement(a.arr, a.incrementKey())\n\treturn a\n}\n\n// AppendRegex will append pattern and options to a.arr\nfunc (a *ArrayBuilder) AppendRegex(pattern, options string) *ArrayBuilder {\n\ta.arr = AppendRegexElement(a.arr, a.incrementKey(), pattern, options)\n\treturn a\n}\n\n// AppendDBPointer will append ns and oid to a.arr\nfunc (a *ArrayBuilder) AppendDBPointer(ns string, oid objectID) *ArrayBuilder {\n\ta.arr = AppendDBPointerElement(a.arr, a.incrementKey(), ns, oid)\n\treturn a\n}\n\n// AppendJavaScript will append js to a.arr\nfunc (a *ArrayBuilder) AppendJavaScript(js string) *ArrayBuilder {\n\ta.arr = AppendJavaScriptElement(a.arr, a.incrementKey(), js)\n\treturn a\n}\n\n// AppendSymbol will append symbol to a.arr\nfunc (a *ArrayBuilder) AppendSymbol(symbol string) *ArrayBuilder {\n\ta.arr = AppendSymbolElement(a.arr, a.incrementKey(), symbol)\n\treturn a\n}\n\n// AppendCodeWithScope will append code and scope to a.arr\nfunc (a *ArrayBuilder) AppendCodeWithScope(code string, scope Document) *ArrayBuilder {\n\ta.arr = AppendCodeWithScopeElement(a.arr, a.incrementKey(), code, scope)\n\treturn a\n}\n\n// AppendTimestamp will append t and i to a.arr\nfunc (a *ArrayBuilder) AppendTimestamp(t, i uint32) *ArrayBuilder {\n\ta.arr = AppendTimestampElement(a.arr, a.incrementKey(), t, i)\n\treturn a\n}\n\n// AppendInt64 will append i64 to a.arr\nfunc (a *ArrayBuilder) AppendInt64(i64 int64) *ArrayBuilder {\n\ta.arr = AppendInt64Element(a.arr, a.incrementKey(), i64)\n\treturn a\n}\n\n// AppendDecimal128 will append d128 to a.arr\nfunc (a *ArrayBuilder) AppendDecimal128(high, low uint64) *ArrayBuilder {\n\ta.arr = AppendDecimal128Element(a.arr, a.incrementKey(), high, low)\n\treturn a\n}\n\n// AppendMaxKey will append a max key element to a.arr\nfunc (a *ArrayBuilder) AppendMaxKey() *ArrayBuilder {\n\ta.arr = AppendMaxKeyElement(a.arr, a.incrementKey())\n\treturn a\n}\n\n// AppendMinKey will append a min key element to a.arr\nfunc (a *ArrayBuilder) AppendMinKey() *ArrayBuilder {\n\ta.arr = AppendMinKeyElement(a.arr, a.incrementKey())\n\treturn a\n}\n\n// AppendValue appends a BSON value to the array.\nfunc (a *ArrayBuilder) AppendValue(val Value) *ArrayBuilder {\n\ta.arr = AppendValueElement(a.arr, a.incrementKey(), val)\n\treturn a\n}\n\n// StartArray starts building an inline Array. After this document is completed,\n// the user must call a.FinishArray\nfunc (a *ArrayBuilder) StartArray() *ArrayBuilder {\n\ta.arr = AppendHeader(a.arr, TypeArray, a.incrementKey())\n\ta.startArray()\n\treturn a\n}\n\n// FinishArray builds the most recent array created\nfunc (a *ArrayBuilder) FinishArray() *ArrayBuilder {\n\ta.arr = a.Build()\n\treturn a\n}\n"
  },
  {
    "path": "x/bsonx/bsoncore/bson_arraybuilder_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bsoncore\n\nimport (\n\t\"bytes\"\n\t\"encoding/binary\"\n\t\"math\"\n\t\"reflect\"\n\t\"testing\"\n)\n\nfunc TestArrayBuilder(t *testing.T) {\n\tbits := math.Float64bits(3.14159)\n\tpi := make([]byte, 8)\n\tbinary.LittleEndian.PutUint64(pi, bits)\n\n\ttestCases := []struct {\n\t\tname     string\n\t\tfn       any\n\t\tparams   []any\n\t\texpected []byte\n\t}{\n\t\t{\n\t\t\t\"AppendInt32\",\n\t\t\tNewArrayBuilder().AppendInt32,\n\t\t\t[]any{int32(256)},\n\t\t\tBuildDocumentFromElements(nil, AppendInt32Element(nil, \"0\", int32(256))),\n\t\t},\n\t\t{\n\t\t\t\"AppendDouble\",\n\t\t\tNewArrayBuilder().AppendDouble,\n\t\t\t[]any{float64(3.14159)},\n\t\t\tBuildDocumentFromElements(nil, AppendDoubleElement(nil, \"0\", float64(3.14159))),\n\t\t},\n\t\t{\n\t\t\t\"AppendString\",\n\t\t\tNewArrayBuilder().AppendString,\n\t\t\t[]any{\"x\"},\n\t\t\tBuildDocumentFromElements(nil, AppendStringElement(nil, \"0\", \"x\")),\n\t\t},\n\t\t{\n\t\t\t\"AppendDocument\",\n\t\t\tNewArrayBuilder().AppendDocument,\n\t\t\t[]any{[]byte{0x05, 0x00, 0x00, 0x00, 0x00}},\n\t\t\tBuildDocumentFromElements(nil, AppendDocumentElement(nil, \"0\", []byte{0x05, 0x00, 0x00, 0x00, 0x00})),\n\t\t},\n\t\t{\n\t\t\t\"AppendArray\",\n\t\t\tNewArrayBuilder().AppendArray,\n\t\t\t[]any{[]byte{0x05, 0x00, 0x00, 0x00, 0x00}},\n\t\t\tBuildDocumentFromElements(nil, AppendArrayElement(nil, \"0\", []byte{0x05, 0x00, 0x00, 0x00, 0x00})),\n\t\t},\n\t\t{\n\t\t\t\"AppendBinary\",\n\t\t\tNewArrayBuilder().AppendBinary,\n\t\t\t[]any{byte(0x02), []byte{0x01, 0x02, 0x03}},\n\t\t\tBuildDocumentFromElements(nil, AppendBinaryElement(nil, \"0\", byte(0x02), []byte{0x01, 0x02, 0x03})),\n\t\t},\n\t\t{\n\t\t\t\"AppendObjectID\",\n\t\t\tNewArrayBuilder().AppendObjectID,\n\t\t\t[]any{\n\t\t\t\t[12]byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C},\n\t\t\t},\n\t\t\tBuildDocumentFromElements(nil, AppendObjectIDElement(nil, \"0\",\n\t\t\t\t[12]byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C})),\n\t\t},\n\t\t{\n\t\t\t\"AppendBoolean\",\n\t\t\tNewArrayBuilder().AppendBoolean,\n\t\t\t[]any{true},\n\t\t\tBuildDocumentFromElements(nil, AppendBooleanElement(nil, \"0\", true)),\n\t\t},\n\t\t{\n\t\t\t\"AppendDateTime\",\n\t\t\tNewArrayBuilder().AppendDateTime,\n\t\t\t[]any{int64(256)},\n\t\t\tBuildDocumentFromElements(nil, AppendDateTimeElement(nil, \"0\", int64(256))),\n\t\t},\n\t\t{\n\t\t\t\"AppendNull\",\n\t\t\tNewArrayBuilder().AppendNull,\n\t\t\t[]any{},\n\t\t\tBuildDocumentFromElements(nil, AppendNullElement(nil, \"0\")),\n\t\t},\n\t\t{\n\t\t\t\"AppendRegex\",\n\t\t\tNewArrayBuilder().AppendRegex,\n\t\t\t[]any{\"bar\", \"baz\"},\n\t\t\tBuildDocumentFromElements(nil, AppendRegexElement(nil, \"0\", \"bar\", \"baz\")),\n\t\t},\n\t\t{\n\t\t\t\"AppendJavaScript\",\n\t\t\tNewArrayBuilder().AppendJavaScript,\n\t\t\t[]any{\"barbaz\"},\n\t\t\tBuildDocumentFromElements(nil, AppendJavaScriptElement(nil, \"0\", \"barbaz\")),\n\t\t},\n\t\t{\n\t\t\t\"AppendCodeWithScope\",\n\t\t\tNewArrayBuilder().AppendCodeWithScope,\n\t\t\t[]any{\"barbaz\", Document([]byte{0x05, 0x00, 0x00, 0x00, 0x00})},\n\t\t\tBuildDocumentFromElements(nil, AppendCodeWithScopeElement(nil, \"0\", \"barbaz\", Document([]byte{0x05, 0x00, 0x00, 0x00, 0x00}))),\n\t\t},\n\t\t{\n\t\t\t\"AppendTimestamp\",\n\t\t\tNewArrayBuilder().AppendTimestamp,\n\t\t\t[]any{uint32(65536), uint32(256)},\n\t\t\tBuildDocumentFromElements(nil, AppendTimestampElement(nil, \"0\", uint32(65536), uint32(256))),\n\t\t},\n\t\t{\n\t\t\t\"AppendInt64\",\n\t\t\tNewArrayBuilder().AppendInt64,\n\t\t\t[]any{int64(4294967296)},\n\t\t\tBuildDocumentFromElements(nil, AppendInt64Element(nil, \"0\", int64(4294967296))),\n\t\t},\n\t\t{\n\t\t\t\"AppendDecimal128\",\n\t\t\tNewArrayBuilder().AppendDecimal128,\n\t\t\t[]any{uint64(4294967296), uint64(65536)},\n\t\t\tBuildDocumentFromElements(nil, AppendDecimal128Element(nil, \"0\", 4294967296, 65536)),\n\t\t},\n\t\t{\n\t\t\t\"AppendMaxKey\",\n\t\t\tNewArrayBuilder().AppendMaxKey,\n\t\t\t[]any{},\n\t\t\tBuildDocumentFromElements(nil, AppendMaxKeyElement(nil, \"0\")),\n\t\t},\n\t\t{\n\t\t\t\"AppendMinKey\",\n\t\t\tNewArrayBuilder().AppendMinKey,\n\t\t\t[]any{},\n\t\t\tBuildDocumentFromElements(nil, AppendMinKeyElement(nil, \"0\")),\n\t\t},\n\t\t{\n\t\t\t\"AppendSymbol\",\n\t\t\tNewArrayBuilder().AppendSymbol,\n\t\t\t[]any{\"barbaz\"},\n\t\t\tBuildDocumentFromElements(nil, AppendSymbolElement(nil, \"0\", \"barbaz\")),\n\t\t},\n\t\t{\n\t\t\t\"AppendDBPointer\",\n\t\t\tNewArrayBuilder().AppendDBPointer,\n\t\t\t[]any{\n\t\t\t\t\"barbaz\",\n\t\t\t\t[12]byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C},\n\t\t\t},\n\t\t\tBuildDocumentFromElements(nil, AppendDBPointerElement(nil, \"0\", \"barbaz\",\n\t\t\t\t[12]byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C})),\n\t\t},\n\t\t{\n\t\t\t\"AppendUndefined\",\n\t\t\tNewArrayBuilder().AppendUndefined,\n\t\t\t[]any{},\n\t\t\tBuildDocumentFromElements(nil, AppendUndefinedElement(nil, \"0\")),\n\t\t},\n\t}\n\tfor _, tc := range testCases {\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\tfn := reflect.ValueOf(tc.fn)\n\t\t\tif fn.Kind() != reflect.Func {\n\t\t\t\tt.Fatalf(\"fn must be of kind Func but is a %v\", fn.Kind())\n\t\t\t}\n\t\t\tif fn.Type().NumIn() != len(tc.params) {\n\t\t\t\tt.Fatalf(\"tc.params must match the number of params in tc.fn. params %d; fn %d\", fn.Type().NumIn(), len(tc.params))\n\t\t\t}\n\t\t\tif fn.Type().NumOut() != 1 || fn.Type().Out(0) != reflect.TypeOf(&ArrayBuilder{}) {\n\t\t\t\tt.Fatalf(\"fn must have one return parameter and it must be an ArrayBuilder.\")\n\t\t\t}\n\t\t\tparams := make([]reflect.Value, 0, len(tc.params))\n\t\t\tfor _, param := range tc.params {\n\t\t\t\tparams = append(params, reflect.ValueOf(param))\n\t\t\t}\n\t\t\tresults := fn.Call(params)\n\t\t\tgot := results[0].Interface().(*ArrayBuilder).Build()\n\t\t\twant := tc.expected\n\t\t\tif !bytes.Equal(got, want) {\n\t\t\t\tt.Errorf(\"Did not receive expected bytes. got %v; want %v\", got, want)\n\t\t\t}\n\t\t})\n\t}\n\tt.Run(\"TestBuildTwoElementsArray\", func(t *testing.T) {\n\t\tintArr := BuildDocumentFromElements(nil, AppendInt32Element(nil, \"0\", int32(1)))\n\t\texpected := BuildDocumentFromElements(nil, AppendArrayElement(AppendInt32Element(nil, \"0\", int32(3)), \"1\", intArr))\n\t\telem := NewArrayBuilder().AppendInt32(int32(1)).Build()\n\t\tresult := NewArrayBuilder().AppendInt32(int32(3)).AppendArray(elem).Build()\n\t\tif !bytes.Equal(result, expected) {\n\t\t\tt.Errorf(\"Arrays do not match. got %v; want %v\", result, expected)\n\t\t}\n\t})\n\tt.Run(\"TestBuildInlineArray\", func(t *testing.T) {\n\t\tdocElement := BuildDocumentFromElements(nil, AppendInt32Element(nil, \"0\", int32(256)))\n\t\texpected := Document(BuildDocumentFromElements(nil, AppendArrayElement(nil, \"0\", docElement)))\n\t\tresult := NewArrayBuilder().StartArray().AppendInt32(int32(256)).FinishArray().Build()\n\t\tif !bytes.Equal(result, expected) {\n\t\t\tt.Errorf(\"Documents do not match. got %v; want %v\", result, expected)\n\t\t}\n\t})\n\tt.Run(\"TestBuildNestedInlineArray\", func(t *testing.T) {\n\t\tdocElement := BuildDocumentFromElements(nil, AppendDoubleElement(nil, \"0\", 3.14))\n\t\tdocInline := BuildDocumentFromElements(nil, AppendArrayElement(nil, \"0\", docElement))\n\t\texpected := Document(BuildDocumentFromElements(nil, AppendArrayElement(nil, \"0\", docInline)))\n\t\tresult := NewArrayBuilder().StartArray().StartArray().AppendDouble(3.14).FinishArray().FinishArray().Build()\n\t\tif !bytes.Equal(result, expected) {\n\t\t\tt.Errorf(\"Documents do not match. got %v; want %v\", result, expected)\n\t\t}\n\t})\n}\n"
  },
  {
    "path": "x/bsonx/bsoncore/bson_documentbuilder.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bsoncore\n\n// DocumentBuilder builds a bson document\ntype DocumentBuilder struct {\n\tdoc     []byte\n\tindexes []int32\n}\n\n// startDocument reserves the document's length and set the index to where the length begins\nfunc (db *DocumentBuilder) startDocument() *DocumentBuilder {\n\tvar index int32\n\tindex, db.doc = AppendDocumentStart(db.doc)\n\tdb.indexes = append(db.indexes, index)\n\treturn db\n}\n\n// NewDocumentBuilder creates a new DocumentBuilder\nfunc NewDocumentBuilder() *DocumentBuilder {\n\treturn (&DocumentBuilder{}).startDocument()\n}\n\n// Build updates the length of the document and index to the beginning of the documents length\n// bytes, then returns the document (bson bytes)\nfunc (db *DocumentBuilder) Build() Document {\n\tlast := len(db.indexes) - 1\n\tdb.doc, _ = AppendDocumentEnd(db.doc, db.indexes[last])\n\tdb.indexes = db.indexes[:last]\n\treturn db.doc\n}\n\n// AppendInt32 will append an int32 element using key and i32 to DocumentBuilder.doc\nfunc (db *DocumentBuilder) AppendInt32(key string, i32 int32) *DocumentBuilder {\n\tdb.doc = AppendInt32Element(db.doc, key, i32)\n\treturn db\n}\n\n// AppendDocument will append a bson embedded document element using key\n// and doc to DocumentBuilder.doc\nfunc (db *DocumentBuilder) AppendDocument(key string, doc []byte) *DocumentBuilder {\n\tdb.doc = AppendDocumentElement(db.doc, key, doc)\n\treturn db\n}\n\n// AppendArray will append a bson array using key and arr to DocumentBuilder.doc\nfunc (db *DocumentBuilder) AppendArray(key string, arr []byte) *DocumentBuilder {\n\tdb.doc = AppendHeader(db.doc, TypeArray, key)\n\tdb.doc = AppendArray(db.doc, arr)\n\treturn db\n}\n\n// AppendDouble will append a double element using key and f to DocumentBuilder.doc\nfunc (db *DocumentBuilder) AppendDouble(key string, f float64) *DocumentBuilder {\n\tdb.doc = AppendDoubleElement(db.doc, key, f)\n\treturn db\n}\n\n// AppendString will append str to DocumentBuilder.doc with the given key\nfunc (db *DocumentBuilder) AppendString(key string, str string) *DocumentBuilder {\n\tdb.doc = AppendStringElement(db.doc, key, str)\n\treturn db\n}\n\n// AppendObjectID will append oid to DocumentBuilder.doc with the given key\nfunc (db *DocumentBuilder) AppendObjectID(key string, oid objectID) *DocumentBuilder {\n\tdb.doc = AppendObjectIDElement(db.doc, key, oid)\n\treturn db\n}\n\n// AppendBinary will append a BSON binary element using key, subtype, and\n// b to db.doc\nfunc (db *DocumentBuilder) AppendBinary(key string, subtype byte, b []byte) *DocumentBuilder {\n\tdb.doc = AppendBinaryElement(db.doc, key, subtype, b)\n\treturn db\n}\n\n// AppendUndefined will append a BSON undefined element using key to db.doc\nfunc (db *DocumentBuilder) AppendUndefined(key string) *DocumentBuilder {\n\tdb.doc = AppendUndefinedElement(db.doc, key)\n\treturn db\n}\n\n// AppendBoolean will append a boolean element using key and b to db.doc\nfunc (db *DocumentBuilder) AppendBoolean(key string, b bool) *DocumentBuilder {\n\tdb.doc = AppendBooleanElement(db.doc, key, b)\n\treturn db\n}\n\n// AppendDateTime will append a datetime element using key and dt to db.doc\nfunc (db *DocumentBuilder) AppendDateTime(key string, dt int64) *DocumentBuilder {\n\tdb.doc = AppendDateTimeElement(db.doc, key, dt)\n\treturn db\n}\n\n// AppendNull will append a null element using key to db.doc\nfunc (db *DocumentBuilder) AppendNull(key string) *DocumentBuilder {\n\tdb.doc = AppendNullElement(db.doc, key)\n\treturn db\n}\n\n// AppendRegex will append pattern and options using key to db.doc\nfunc (db *DocumentBuilder) AppendRegex(key, pattern, options string) *DocumentBuilder {\n\tdb.doc = AppendRegexElement(db.doc, key, pattern, options)\n\treturn db\n}\n\n// AppendDBPointer will append ns and oid to using key to db.doc\nfunc (db *DocumentBuilder) AppendDBPointer(key string, ns string, oid objectID) *DocumentBuilder {\n\tdb.doc = AppendDBPointerElement(db.doc, key, ns, oid)\n\treturn db\n}\n\n// AppendJavaScript will append js using the provided key to db.doc\nfunc (db *DocumentBuilder) AppendJavaScript(key, js string) *DocumentBuilder {\n\tdb.doc = AppendJavaScriptElement(db.doc, key, js)\n\treturn db\n}\n\n// AppendSymbol will append a BSON symbol element using key and symbol db.doc\nfunc (db *DocumentBuilder) AppendSymbol(key, symbol string) *DocumentBuilder {\n\tdb.doc = AppendSymbolElement(db.doc, key, symbol)\n\treturn db\n}\n\n// AppendCodeWithScope will append code and scope using key to db.doc\nfunc (db *DocumentBuilder) AppendCodeWithScope(key string, code string, scope Document) *DocumentBuilder {\n\tdb.doc = AppendCodeWithScopeElement(db.doc, key, code, scope)\n\treturn db\n}\n\n// AppendTimestamp will append t and i to db.doc using provided key\nfunc (db *DocumentBuilder) AppendTimestamp(key string, t, i uint32) *DocumentBuilder {\n\tdb.doc = AppendTimestampElement(db.doc, key, t, i)\n\treturn db\n}\n\n// AppendInt64 will append i64 to dst using key to db.doc\nfunc (db *DocumentBuilder) AppendInt64(key string, i64 int64) *DocumentBuilder {\n\tdb.doc = AppendInt64Element(db.doc, key, i64)\n\treturn db\n}\n\n// AppendDecimal128 will append d128 to db.doc using provided key\nfunc (db *DocumentBuilder) AppendDecimal128(key string, high, low uint64) *DocumentBuilder {\n\tdb.doc = AppendDecimal128Element(db.doc, key, high, low)\n\treturn db\n}\n\n// AppendMaxKey will append a max key element using key to db.doc\nfunc (db *DocumentBuilder) AppendMaxKey(key string) *DocumentBuilder {\n\tdb.doc = AppendMaxKeyElement(db.doc, key)\n\treturn db\n}\n\n// AppendMinKey will append a min key element using key to db.doc\nfunc (db *DocumentBuilder) AppendMinKey(key string) *DocumentBuilder {\n\tdb.doc = AppendMinKeyElement(db.doc, key)\n\treturn db\n}\n\n// AppendValue will append a BSON element with the provided key and value to the document.\nfunc (db *DocumentBuilder) AppendValue(key string, val Value) *DocumentBuilder {\n\tdb.doc = AppendValueElement(db.doc, key, val)\n\treturn db\n}\n\n// StartDocument starts building an inline document element with the provided key\n// After this document is completed, the user must call finishDocument\nfunc (db *DocumentBuilder) StartDocument(key string) *DocumentBuilder {\n\tdb.doc = AppendHeader(db.doc, TypeEmbeddedDocument, key)\n\tdb = db.startDocument()\n\treturn db\n}\n\n// FinishDocument builds the most recent document created\nfunc (db *DocumentBuilder) FinishDocument() *DocumentBuilder {\n\tdb.doc = db.Build()\n\treturn db\n}\n"
  },
  {
    "path": "x/bsonx/bsoncore/bson_documentbuilder_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bsoncore\n\nimport (\n\t\"bytes\"\n\t\"encoding/binary\"\n\t\"math\"\n\t\"reflect\"\n\t\"testing\"\n)\n\nfunc TestDocumentBuilder(t *testing.T) {\n\tbits := math.Float64bits(3.14159)\n\tpi := make([]byte, 8)\n\tbinary.LittleEndian.PutUint64(pi, bits)\n\n\ttestCases := []struct {\n\t\tname     string\n\t\tfn       any\n\t\tparams   []any\n\t\texpected []byte\n\t}{\n\t\t{\n\t\t\t\"AppendInt32\",\n\t\t\tNewDocumentBuilder().AppendInt32,\n\t\t\t[]any{\"foobar\", int32(256)},\n\t\t\tBuildDocumentFromElements(nil, AppendInt32Element(nil, \"foobar\", 256)),\n\t\t},\n\t\t{\n\t\t\t\"AppendDouble\",\n\t\t\tNewDocumentBuilder().AppendDouble,\n\t\t\t[]any{\"foobar\", float64(3.14159)},\n\t\t\tBuildDocumentFromElements(nil, AppendDoubleElement(nil, \"foobar\", float64(3.14159))),\n\t\t},\n\t\t{\n\t\t\t\"AppendString\",\n\t\t\tNewDocumentBuilder().AppendString,\n\t\t\t[]any{\"foobar\", \"x\"},\n\t\t\tBuildDocumentFromElements(nil, AppendStringElement(nil, \"foobar\", \"x\")),\n\t\t},\n\t\t{\n\t\t\t\"AppendDocument\",\n\t\t\tNewDocumentBuilder().AppendDocument,\n\t\t\t[]any{\"foobar\", []byte{0x05, 0x00, 0x00, 0x00, 0x00}},\n\t\t\tBuildDocumentFromElements(nil, AppendDocumentElement(nil, \"foobar\", []byte{0x05, 0x00, 0x00, 0x00, 0x00})),\n\t\t},\n\t\t{\n\t\t\t\"AppendArray\",\n\t\t\tNewDocumentBuilder().AppendArray,\n\t\t\t[]any{\"foobar\", []byte{0x05, 0x00, 0x00, 0x00, 0x00}},\n\t\t\tBuildDocumentFromElements(nil, AppendArrayElement(nil, \"foobar\", []byte{0x05, 0x00, 0x00, 0x00, 0x00})),\n\t\t},\n\t\t{\n\t\t\t\"AppendBinary\",\n\t\t\tNewDocumentBuilder().AppendBinary,\n\t\t\t[]any{\"foobar\", byte(0x02), []byte{0x01, 0x02, 0x03}},\n\t\t\tBuildDocumentFromElements(nil, AppendBinaryElement(nil, \"foobar\", byte(0x02), []byte{0x01, 0x02, 0x03})),\n\t\t},\n\t\t{\n\t\t\t\"AppendObjectID\",\n\t\t\tNewDocumentBuilder().AppendObjectID,\n\t\t\t[]any{\n\t\t\t\t\"foobar\",\n\t\t\t\t[12]byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C},\n\t\t\t},\n\t\t\tBuildDocumentFromElements(nil, AppendObjectIDElement(nil, \"foobar\",\n\t\t\t\t[12]byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C})),\n\t\t},\n\t\t{\n\t\t\t\"AppendBoolean\",\n\t\t\tNewDocumentBuilder().AppendBoolean,\n\t\t\t[]any{\"foobar\", true},\n\t\t\tBuildDocumentFromElements(nil, AppendBooleanElement(nil, \"foobar\", true)),\n\t\t},\n\t\t{\n\t\t\t\"AppendDateTime\",\n\t\t\tNewDocumentBuilder().AppendDateTime,\n\t\t\t[]any{\"foobar\", int64(256)},\n\t\t\tBuildDocumentFromElements(nil, AppendDateTimeElement(nil, \"foobar\", int64(256))),\n\t\t},\n\t\t{\n\t\t\t\"AppendNull\",\n\t\t\tNewDocumentBuilder().AppendNull,\n\t\t\t[]any{\"foobar\"},\n\t\t\tBuildDocumentFromElements(nil, AppendNullElement(nil, \"foobar\")),\n\t\t},\n\t\t{\n\t\t\t\"AppendRegex\",\n\t\t\tNewDocumentBuilder().AppendRegex,\n\t\t\t[]any{\"foobar\", \"bar\", \"baz\"},\n\t\t\tBuildDocumentFromElements(nil, AppendRegexElement(nil, \"foobar\", \"bar\", \"baz\")),\n\t\t},\n\t\t{\n\t\t\t\"AppendJavaScript\",\n\t\t\tNewDocumentBuilder().AppendJavaScript,\n\t\t\t[]any{\"foobar\", \"barbaz\"},\n\t\t\tBuildDocumentFromElements(nil, AppendJavaScriptElement(nil, \"foobar\", \"barbaz\")),\n\t\t},\n\t\t{\n\t\t\t\"AppendCodeWithScope\",\n\t\t\tNewDocumentBuilder().AppendCodeWithScope,\n\t\t\t[]any{\"foobar\", \"barbaz\", Document([]byte{0x05, 0x00, 0x00, 0x00, 0x00})},\n\t\t\tBuildDocumentFromElements(nil, AppendCodeWithScopeElement(nil, \"foobar\", \"barbaz\", Document([]byte{0x05, 0x00, 0x00, 0x00, 0x00}))),\n\t\t},\n\t\t{\n\t\t\t\"AppendTimestamp\",\n\t\t\tNewDocumentBuilder().AppendTimestamp,\n\t\t\t[]any{\"foobar\", uint32(65536), uint32(256)},\n\t\t\tBuildDocumentFromElements(nil, AppendTimestampElement(nil, \"foobar\", uint32(65536), uint32(256))),\n\t\t},\n\t\t{\n\t\t\t\"AppendInt64\",\n\t\t\tNewDocumentBuilder().AppendInt64,\n\t\t\t[]any{\"foobar\", int64(4294967296)},\n\t\t\tBuildDocumentFromElements(nil, AppendInt64Element(nil, \"foobar\", int64(4294967296))),\n\t\t},\n\t\t{\n\t\t\t\"AppendDecimal128\",\n\t\t\tNewDocumentBuilder().AppendDecimal128,\n\t\t\t[]any{\"foobar\", uint64(4294967296), uint64(65536)},\n\t\t\tBuildDocumentFromElements(nil, AppendDecimal128Element(nil, \"foobar\", 4294967296, 65536)),\n\t\t},\n\t\t{\n\t\t\t\"AppendMaxKey\",\n\t\t\tNewDocumentBuilder().AppendMaxKey,\n\t\t\t[]any{\"foobar\"},\n\t\t\tBuildDocumentFromElements(nil, AppendMaxKeyElement(nil, \"foobar\")),\n\t\t},\n\t\t{\n\t\t\t\"AppendMinKey\",\n\t\t\tNewDocumentBuilder().AppendMinKey,\n\t\t\t[]any{\"foobar\"},\n\t\t\tBuildDocumentFromElements(nil, AppendMinKeyElement(nil, \"foobar\")),\n\t\t},\n\t\t{\n\t\t\t\"AppendSymbol\",\n\t\t\tNewDocumentBuilder().AppendSymbol,\n\t\t\t[]any{\"foobar\", \"barbaz\"},\n\t\t\tBuildDocumentFromElements(nil, AppendSymbolElement(nil, \"foobar\", \"barbaz\")),\n\t\t},\n\t\t{\n\t\t\t\"AppendDBPointer\",\n\t\t\tNewDocumentBuilder().AppendDBPointer,\n\t\t\t[]any{\n\t\t\t\t\"foobar\", \"barbaz\",\n\t\t\t\t[12]byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C},\n\t\t\t},\n\t\t\tBuildDocumentFromElements(nil, AppendDBPointerElement(nil, \"foobar\", \"barbaz\",\n\t\t\t\t[12]byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C})),\n\t\t},\n\t\t{\n\t\t\t\"AppendUndefined\",\n\t\t\tNewDocumentBuilder().AppendUndefined,\n\t\t\t[]any{\"foobar\"},\n\t\t\tBuildDocumentFromElements(nil, AppendUndefinedElement(nil, \"foobar\")),\n\t\t},\n\t}\n\tfor _, tc := range testCases {\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\tfn := reflect.ValueOf(tc.fn)\n\t\t\tif fn.Kind() != reflect.Func {\n\t\t\t\tt.Fatalf(\"fn must be of kind Func but is a %v\", fn.Kind())\n\t\t\t}\n\t\t\tif fn.Type().NumIn() != len(tc.params) {\n\t\t\t\tt.Fatalf(\"tc.params must match the number of params in tc.fn. params %d; fn %d\", fn.Type().NumIn(), len(tc.params))\n\t\t\t}\n\t\t\tif fn.Type().NumOut() != 1 || fn.Type().Out(0) != reflect.TypeOf(&DocumentBuilder{}) {\n\t\t\t\tt.Fatalf(\"fn must have one return parameter and it must be a DocumentBuilder.\")\n\t\t\t}\n\t\t\tparams := make([]reflect.Value, 0, len(tc.params))\n\t\t\tfor _, param := range tc.params {\n\t\t\t\tparams = append(params, reflect.ValueOf(param))\n\t\t\t}\n\t\t\tresults := fn.Call(params)\n\t\t\tgot := results[0].Interface().(*DocumentBuilder)\n\t\t\tdoc := got.Build()\n\t\t\twant := tc.expected\n\t\t\tif !bytes.Equal(doc, want) {\n\t\t\t\tt.Errorf(\"Did not receive expected bytes. got %v; want %v\", got, want)\n\t\t\t}\n\t\t})\n\t}\n\tt.Run(\"TestBuildTwoElements\", func(t *testing.T) {\n\t\tintArr := BuildDocumentFromElements(nil, AppendInt32Element(nil, \"0\", int32(1)))\n\t\texpected := BuildDocumentFromElements(nil, AppendArrayElement(AppendInt32Element(nil, \"x\", int32(3)), \"y\", intArr))\n\t\telem := NewArrayBuilder().AppendInt32(int32(1)).Build()\n\t\tresult := NewDocumentBuilder().AppendInt32(\"x\", int32(3)).AppendArray(\"y\", elem).Build()\n\t\tif !bytes.Equal(result, expected) {\n\t\t\tt.Errorf(\"Documents do not match. got %v; want %v\", result, expected)\n\t\t}\n\t})\n\tt.Run(\"TestBuildInlineDocument\", func(t *testing.T) {\n\t\tdocElement := BuildDocumentFromElements(nil, AppendInt32Element(nil, \"x\", int32(256)))\n\t\texpected := Document(BuildDocumentFromElements(nil, AppendDocumentElement(nil, \"y\", docElement)))\n\t\tresult := NewDocumentBuilder().StartDocument(\"y\").AppendInt32(\"x\", int32(256)).FinishDocument().Build()\n\t\tif !bytes.Equal(result, expected) {\n\t\t\tt.Errorf(\"Documents do not match. got %v; want %v\", result, expected)\n\t\t}\n\t})\n\tt.Run(\"TestBuildNestedInlineDocument\", func(t *testing.T) {\n\t\tdocElement := BuildDocumentFromElements(nil, AppendDoubleElement(nil, \"x\", 3.14))\n\t\tdocInline := BuildDocumentFromElements(nil, AppendDocumentElement(nil, \"y\", docElement))\n\t\texpected := Document(BuildDocumentFromElements(nil, AppendDocumentElement(nil, \"z\", docInline)))\n\t\tresult := NewDocumentBuilder().StartDocument(\"z\").StartDocument(\"y\").AppendDouble(\"x\", 3.14).FinishDocument().FinishDocument().Build()\n\t\tif !bytes.Equal(result, expected) {\n\t\t\tt.Errorf(\"Documents do not match. got %v; want %v\", result, expected)\n\t\t}\n\t})\n}\n"
  },
  {
    "path": "x/bsonx/bsoncore/bsoncore.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bsoncore\n\nimport (\n\t\"bytes\"\n\t\"encoding/binary\"\n\t\"fmt\"\n\t\"math\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/binaryutil\"\n)\n\nconst (\n\t// EmptyDocumentLength is the length of a document that has been started/ended but has no elements.\n\tEmptyDocumentLength = 5\n\t// nullTerminator is a string version of the 0 byte that is appended at the end of cstrings.\n\tnullTerminator       = string(byte(0))\n\tinvalidKeyPanicMsg   = \"BSON element keys cannot contain null bytes\"\n\tinvalidRegexPanicMsg = \"BSON regex values cannot contain null bytes\"\n)\n\ntype objectID = [12]byte\n\n// AppendType will append t to dst and return the extended buffer.\nfunc AppendType(dst []byte, t Type) []byte { return append(dst, byte(t)) }\n\n// AppendKey will append key to dst and return the extended buffer.\nfunc AppendKey(dst []byte, key string) []byte { return append(dst, key+nullTerminator...) }\n\n// AppendHeader will append Type t and key to dst and return the extended\n// buffer.\nfunc AppendHeader(dst []byte, t Type, key string) []byte {\n\tif !isValidCString(key) {\n\t\tpanic(invalidKeyPanicMsg)\n\t}\n\n\tdst = AppendType(dst, t)\n\tdst = append(dst, key...)\n\treturn append(dst, 0x00)\n\t// return append(AppendType(dst, t), key+string(0x00)...)\n}\n\n// TODO(skriptble): All of the Read* functions should return src resliced to start just after what was read.\n\n// ReadType will return the first byte of the provided []byte as a type. If\n// there is no available byte, false is returned.\nfunc ReadType(src []byte) (Type, []byte, bool) {\n\tif len(src) < 1 {\n\t\treturn 0, src, false\n\t}\n\treturn Type(src[0]), src[1:], true\n}\n\n// ReadKey will read a key from src. The 0x00 byte will not be present\n// in the returned string. If there are not enough bytes available, false is\n// returned.\nfunc ReadKey(src []byte) (string, []byte, bool) { return binaryutil.ReadCString(src) }\n\n// ReadKeyBytes will read a key from src as bytes. The 0x00 byte will\n// not be present in the returned string. If there are not enough bytes\n// available, false is returned.\nfunc ReadKeyBytes(src []byte) ([]byte, []byte, bool) { return binaryutil.ReadCStringBytes(src) }\n\n// ReadHeader will read a type byte and a key from src. If both of these\n// values cannot be read, false is returned.\nfunc ReadHeader(src []byte) (t Type, key string, rem []byte, ok bool) {\n\tt, rem, ok = ReadType(src)\n\tif !ok {\n\t\treturn 0, \"\", src, false\n\t}\n\tkey, rem, ok = ReadKey(rem)\n\tif !ok {\n\t\treturn 0, \"\", src, false\n\t}\n\n\treturn t, key, rem, true\n}\n\n// ReadHeaderBytes will read a type and a key from src and the remainder of the bytes\n// are returned as rem. If either the type or key cannot be red, ok will be false.\nfunc ReadHeaderBytes(src []byte) (header []byte, rem []byte, ok bool) {\n\tif len(src) < 1 {\n\t\treturn nil, src, false\n\t}\n\tidx := bytes.IndexByte(src[1:], 0x00)\n\tif idx == -1 {\n\t\treturn nil, src, false\n\t}\n\treturn src[:idx], src[idx+1:], true\n}\n\n// ReadElement reads the next full element from src. It returns the element, the remaining bytes in\n// the slice, and a boolean indicating if the read was successful.\nfunc ReadElement(src []byte) (Element, []byte, bool) {\n\tif len(src) < 1 {\n\t\treturn nil, src, false\n\t}\n\tt := Type(src[0])\n\tidx := 1\n\tfor idx < len(src) && src[idx] != 0x00 {\n\t\tidx++\n\t}\n\tif idx >= len(src) {\n\t\treturn nil, src, false\n\t}\n\tidx++ // Move past the null byte\n\tlength, ok := valueLength(src[idx:], t)\n\tif !ok {\n\t\treturn nil, src, false\n\t}\n\telemLength := idx + int(length)\n\tif elemLength > len(src) {\n\t\treturn nil, src, false\n\t}\n\treturn src[:elemLength], src[elemLength:], true\n}\n\n// AppendValueElement appends value to dst as an element using key as the element's key.\nfunc AppendValueElement(dst []byte, key string, value Value) []byte {\n\tdst = AppendHeader(dst, value.Type, key)\n\tdst = append(dst, value.Data...)\n\treturn dst\n}\n\n// ReadValue reads the next value as the provided types and returns a Value, the remaining bytes,\n// and a boolean indicating if the read was successful.\nfunc ReadValue(src []byte, t Type) (Value, []byte, bool) {\n\tdata, rem, ok := readValue(src, t)\n\tif !ok {\n\t\treturn Value{}, src, false\n\t}\n\treturn Value{Type: t, Data: data}, rem, true\n}\n\n// AppendDouble will append f to dst and return the extended buffer.\nfunc AppendDouble(dst []byte, f float64) []byte {\n\treturn binaryutil.Append64(dst, math.Float64bits(f))\n}\n\n// AppendDoubleElement will append a BSON double element using key and f to dst\n// and return the extended buffer.\nfunc AppendDoubleElement(dst []byte, key string, f float64) []byte {\n\treturn AppendDouble(AppendHeader(dst, TypeDouble, key), f)\n}\n\n// ReadDouble will read a float64 from src. If there are not enough bytes it\n// will return false.\nfunc ReadDouble(src []byte) (float64, []byte, bool) {\n\tbits, src, ok := binaryutil.ReadU64(src)\n\tif !ok {\n\t\treturn 0, src, false\n\t}\n\treturn math.Float64frombits(bits), src, true\n}\n\n// AppendString will append s to dst and return the extended buffer.\nfunc AppendString(dst []byte, s string) []byte {\n\treturn appendstring(dst, s)\n}\n\n// AppendStringElement will append a BSON string element using key and val to dst\n// and return the extended buffer.\nfunc AppendStringElement(dst []byte, key, val string) []byte {\n\treturn AppendString(AppendHeader(dst, TypeString, key), val)\n}\n\n// ReadString will read a string from src. If there are not enough bytes it\n// will return false.\nfunc ReadString(src []byte) (string, []byte, bool) {\n\treturn readstring(src)\n}\n\n// AppendDocumentStart reserves a document's length and returns the index where the length begins.\n// This index can later be used to write the length of the document.\nfunc AppendDocumentStart(dst []byte) (index int32, b []byte) {\n\t// TODO(skriptble): We really need AppendDocumentStart and AppendDocumentEnd.  AppendDocumentStart would handle calling\n\t// TODO ReserveLength and providing the index of the start of the document. AppendDocumentEnd would handle taking that\n\t// TODO start index, adding the null byte, calculating the length, and filling in the length at the start of the\n\t// TODO document.\n\treturn ReserveLength(dst)\n}\n\n// AppendDocumentStartInline functions the same as AppendDocumentStart but takes a pointer to the\n// index int32 which allows this function to be used inline.\nfunc AppendDocumentStartInline(dst []byte, index *int32) []byte {\n\tidx, doc := AppendDocumentStart(dst)\n\t*index = idx\n\treturn doc\n}\n\n// AppendDocumentElementStart writes a document element header and then reserves the length bytes.\nfunc AppendDocumentElementStart(dst []byte, key string) (index int32, b []byte) {\n\treturn AppendDocumentStart(AppendHeader(dst, TypeEmbeddedDocument, key))\n}\n\n// AppendDocumentEnd writes the null byte for a document and updates the length of the document.\n// The index should be the beginning of the document's length bytes.\nfunc AppendDocumentEnd(dst []byte, index int32) ([]byte, error) {\n\tif int(index) > len(dst)-4 {\n\t\treturn dst, fmt.Errorf(\"not enough bytes available after index to write length\")\n\t}\n\tdst = append(dst, 0x00)\n\tdst = UpdateLength(dst, index, int32(len(dst[index:])))\n\treturn dst, nil\n}\n\n// AppendDocument will append doc to dst and return the extended buffer.\nfunc AppendDocument(dst []byte, doc []byte) []byte { return append(dst, doc...) }\n\n// AppendDocumentElement will append a BSON embedded document element using key\n// and doc to dst and return the extended buffer.\nfunc AppendDocumentElement(dst []byte, key string, doc []byte) []byte {\n\treturn AppendDocument(AppendHeader(dst, TypeEmbeddedDocument, key), doc)\n}\n\n// BuildDocument will create a document with the given slice of elements and will append\n// it to dst and return the extended buffer.\nfunc BuildDocument(dst []byte, elems ...[]byte) []byte {\n\tidx, dst := ReserveLength(dst)\n\tfor _, elem := range elems {\n\t\tdst = append(dst, elem...)\n\t}\n\tdst = append(dst, 0x00)\n\tdst = UpdateLength(dst, idx, int32(len(dst[idx:])))\n\treturn dst\n}\n\n// BuildDocumentValue creates an Embedded Document value from the given elements.\nfunc BuildDocumentValue(elems ...[]byte) Value {\n\treturn Value{Type: TypeEmbeddedDocument, Data: BuildDocument(nil, elems...)}\n}\n\n// BuildDocumentElement will append a BSON embedded document element using key and the provided\n// elements and return the extended buffer.\nfunc BuildDocumentElement(dst []byte, key string, elems ...[]byte) []byte {\n\treturn BuildDocument(AppendHeader(dst, TypeEmbeddedDocument, key), elems...)\n}\n\n// BuildDocumentFromElements is an alaias for the BuildDocument function.\nvar BuildDocumentFromElements = BuildDocument\n\n// ReadDocument will read a document from src. If there are not enough bytes it\n// will return false.\nfunc ReadDocument(src []byte) (doc Document, rem []byte, ok bool) { return readLengthBytes(src) }\n\n// AppendArrayStart appends the length bytes to an array and then returns the index of the start\n// of those length bytes.\nfunc AppendArrayStart(dst []byte) (index int32, b []byte) { return ReserveLength(dst) }\n\n// AppendArrayElementStart appends an array element header and then the length bytes for an array,\n// returning the index where the length starts.\nfunc AppendArrayElementStart(dst []byte, key string) (index int32, b []byte) {\n\treturn AppendArrayStart(AppendHeader(dst, TypeArray, key))\n}\n\n// AppendArrayEnd appends the null byte to an array and calculates the length, inserting that\n// calculated length starting at index.\nfunc AppendArrayEnd(dst []byte, index int32) ([]byte, error) { return AppendDocumentEnd(dst, index) }\n\n// AppendArray will append arr to dst and return the extended buffer.\nfunc AppendArray(dst []byte, arr []byte) []byte { return append(dst, arr...) }\n\n// AppendArrayElement will append a BSON array element using key and arr to dst\n// and return the extended buffer.\nfunc AppendArrayElement(dst []byte, key string, arr []byte) []byte {\n\treturn AppendArray(AppendHeader(dst, TypeArray, key), arr)\n}\n\n// BuildArray will append a BSON array to dst built from values.\nfunc BuildArray(dst []byte, values ...Value) []byte {\n\tidx, dst := ReserveLength(dst)\n\tfor pos, val := range values {\n\t\tdst = AppendValueElement(dst, strconv.Itoa(pos), val)\n\t}\n\tdst = append(dst, 0x00)\n\tdst = UpdateLength(dst, idx, int32(len(dst[idx:])))\n\treturn dst\n}\n\n// BuildArrayElement will create an array element using the provided values.\nfunc BuildArrayElement(dst []byte, key string, values ...Value) []byte {\n\treturn BuildArray(AppendHeader(dst, TypeArray, key), values...)\n}\n\n// ReadArray will read an array from src. If there are not enough bytes it\n// will return false.\nfunc ReadArray(src []byte) (arr Array, rem []byte, ok bool) { return readLengthBytes(src) }\n\n// AppendBinary will append subtype and b to dst and return the extended buffer.\nfunc AppendBinary(dst []byte, subtype byte, b []byte) []byte {\n\tif subtype == 0x02 {\n\t\treturn appendBinarySubtype2(dst, subtype, b)\n\t}\n\tdst = append(appendLength(dst, int32(len(b))), subtype)\n\treturn append(dst, b...)\n}\n\n// AppendBinaryElement will append a BSON binary element using key, subtype, and\n// b to dst and return the extended buffer.\nfunc AppendBinaryElement(dst []byte, key string, subtype byte, b []byte) []byte {\n\treturn AppendBinary(AppendHeader(dst, TypeBinary, key), subtype, b)\n}\n\n// ReadBinary will read a subtype and bin from src. If there are not enough bytes it\n// will return false.\nfunc ReadBinary(src []byte) (subtype byte, bin []byte, rem []byte, ok bool) {\n\tlength, rem, ok := ReadLength(src)\n\tif !ok {\n\t\treturn 0x00, nil, src, false\n\t}\n\tif len(rem) < 1 { // subtype\n\t\treturn 0x00, nil, src, false\n\t}\n\tsubtype, rem = rem[0], rem[1:]\n\n\tif len(rem) < int(length) {\n\t\treturn 0x00, nil, src, false\n\t}\n\n\tif subtype == 0x02 {\n\t\tlength, rem, ok = ReadLength(rem)\n\t\tif !ok || len(rem) < int(length) {\n\t\t\treturn 0x00, nil, src, false\n\t\t}\n\t}\n\n\treturn subtype, rem[:length], rem[length:], true\n}\n\n// AppendUndefinedElement will append a BSON undefined element using key to dst\n// and return the extended buffer.\nfunc AppendUndefinedElement(dst []byte, key string) []byte {\n\treturn AppendHeader(dst, TypeUndefined, key)\n}\n\n// AppendObjectID will append oid to dst and return the extended buffer.\nfunc AppendObjectID(dst []byte, oid objectID) []byte { return append(dst, oid[:]...) }\n\n// AppendObjectIDElement will append a BSON ObjectID element using key and oid to dst\n// and return the extended buffer.\nfunc AppendObjectIDElement(dst []byte, key string, oid objectID) []byte {\n\treturn AppendObjectID(AppendHeader(dst, TypeObjectID, key), oid)\n}\n\n// ReadObjectID will read an ObjectID from src. If there are not enough bytes it\n// will return false.\nfunc ReadObjectID(src []byte) ([12]byte, []byte, bool) {\n\tvar oid objectID\n\tidLen := cap(oid)\n\tif len(src) < idLen {\n\t\treturn oid, src, false\n\t}\n\tcopy(oid[:], src[0:idLen])\n\treturn oid, src[idLen:], true\n}\n\n// AppendBoolean will append b to dst and return the extended buffer.\nfunc AppendBoolean(dst []byte, b bool) []byte {\n\tif b {\n\t\treturn append(dst, 0x01)\n\t}\n\treturn append(dst, 0x00)\n}\n\n// AppendBooleanElement will append a BSON boolean element using key and b to dst\n// and return the extended buffer.\nfunc AppendBooleanElement(dst []byte, key string, b bool) []byte {\n\treturn AppendBoolean(AppendHeader(dst, TypeBoolean, key), b)\n}\n\n// ReadBoolean will read a bool from src. If there are not enough bytes it\n// will return false.\nfunc ReadBoolean(src []byte) (bool, []byte, bool) {\n\tif len(src) < 1 {\n\t\treturn false, src, false\n\t}\n\n\treturn src[0] == 0x01, src[1:], true\n}\n\n// AppendDateTime will append dt to dst and return the extended buffer.\nfunc AppendDateTime(dst []byte, dt int64) []byte { return binaryutil.Append64(dst, dt) }\n\n// AppendDateTimeElement will append a BSON datetime element using key and dt to dst\n// and return the extended buffer.\nfunc AppendDateTimeElement(dst []byte, key string, dt int64) []byte {\n\treturn AppendDateTime(AppendHeader(dst, TypeDateTime, key), dt)\n}\n\n// ReadDateTime will read an int64 datetime from src. If there are not enough bytes it\n// will return false.\nfunc ReadDateTime(src []byte) (int64, []byte, bool) { return binaryutil.ReadI64(src) }\n\n// AppendTime will append time as a BSON DateTime to dst and return the extended buffer.\nfunc AppendTime(dst []byte, t time.Time) []byte {\n\treturn AppendDateTime(dst, t.Unix()*1000+int64(t.Nanosecond()/1e6))\n}\n\n// AppendTimeElement will append a BSON datetime element using key and dt to dst\n// and return the extended buffer.\nfunc AppendTimeElement(dst []byte, key string, t time.Time) []byte {\n\treturn AppendTime(AppendHeader(dst, TypeDateTime, key), t)\n}\n\n// ReadTime will read an time.Time datetime from src. If there are not enough bytes it\n// will return false.\nfunc ReadTime(src []byte) (time.Time, []byte, bool) {\n\tdt, rem, ok := binaryutil.ReadI64(src)\n\treturn time.Unix(dt/1e3, dt%1e3*1e6), rem, ok\n}\n\n// AppendNullElement will append a BSON null element using key to dst\n// and return the extended buffer.\nfunc AppendNullElement(dst []byte, key string) []byte { return AppendHeader(dst, TypeNull, key) }\n\n// AppendRegex will append pattern and options to dst and return the extended buffer.\nfunc AppendRegex(dst []byte, pattern, options string) []byte {\n\tif !isValidCString(pattern) || !isValidCString(options) {\n\t\tpanic(invalidRegexPanicMsg)\n\t}\n\n\treturn append(dst, pattern+nullTerminator+options+nullTerminator...)\n}\n\n// AppendRegexElement will append a BSON regex element using key, pattern, and\n// options to dst and return the extended buffer.\nfunc AppendRegexElement(dst []byte, key, pattern, options string) []byte {\n\treturn AppendRegex(AppendHeader(dst, TypeRegex, key), pattern, options)\n}\n\n// ReadRegex will read a pattern and options from src. If there are not enough bytes it\n// will return false.\nfunc ReadRegex(src []byte) (pattern, options string, rem []byte, ok bool) {\n\tpattern, rem, ok = binaryutil.ReadCString(src)\n\tif !ok {\n\t\treturn \"\", \"\", src, false\n\t}\n\toptions, rem, ok = binaryutil.ReadCString(rem)\n\tif !ok {\n\t\treturn \"\", \"\", src, false\n\t}\n\treturn pattern, options, rem, true\n}\n\n// AppendDBPointer will append ns and oid to dst and return the extended buffer.\nfunc AppendDBPointer(dst []byte, ns string, oid objectID) []byte {\n\treturn append(appendstring(dst, ns), oid[:]...)\n}\n\n// AppendDBPointerElement will append a BSON DBPointer element using key, ns,\n// and oid to dst and return the extended buffer.\nfunc AppendDBPointerElement(dst []byte, key, ns string, oid objectID) []byte {\n\treturn AppendDBPointer(AppendHeader(dst, TypeDBPointer, key), ns, oid)\n}\n\n// ReadDBPointer will read a ns and oid from src. If there are not enough bytes it\n// will return false.\nfunc ReadDBPointer(src []byte) (ns string, oid [12]byte, rem []byte, ok bool) {\n\tns, rem, ok = readstring(src)\n\tif !ok {\n\t\treturn \"\", objectID{}, src, false\n\t}\n\toid, rem, ok = ReadObjectID(rem)\n\tif !ok {\n\t\treturn \"\", objectID{}, src, false\n\t}\n\treturn ns, oid, rem, true\n}\n\n// AppendJavaScript will append js to dst and return the extended buffer.\nfunc AppendJavaScript(dst []byte, js string) []byte { return appendstring(dst, js) }\n\n// AppendJavaScriptElement will append a BSON JavaScript element using key and\n// js to dst and return the extended buffer.\nfunc AppendJavaScriptElement(dst []byte, key, js string) []byte {\n\treturn AppendJavaScript(AppendHeader(dst, TypeJavaScript, key), js)\n}\n\n// ReadJavaScript will read a js string from src. If there are not enough bytes it\n// will return false.\nfunc ReadJavaScript(src []byte) (js string, rem []byte, ok bool) { return readstring(src) }\n\n// AppendSymbol will append symbol to dst and return the extended buffer.\nfunc AppendSymbol(dst []byte, symbol string) []byte { return appendstring(dst, symbol) }\n\n// AppendSymbolElement will append a BSON symbol element using key and symbol to dst\n// and return the extended buffer.\nfunc AppendSymbolElement(dst []byte, key, symbol string) []byte {\n\treturn AppendSymbol(AppendHeader(dst, TypeSymbol, key), symbol)\n}\n\n// ReadSymbol will read a symbol string from src. If there are not enough bytes it\n// will return false.\nfunc ReadSymbol(src []byte) (symbol string, rem []byte, ok bool) { return readstring(src) }\n\n// AppendCodeWithScope will append code and scope to dst and return the extended buffer.\nfunc AppendCodeWithScope(dst []byte, code string, scope []byte) []byte {\n\tlength := int32(4 + 4 + len(code) + 1 + len(scope)) // length of cws, length of code, code, 0x00, scope\n\tdst = appendLength(dst, length)\n\n\treturn append(appendstring(dst, code), scope...)\n}\n\n// AppendCodeWithScopeElement will append a BSON code with scope element using\n// key, code, and scope to dst\n// and return the extended buffer.\nfunc AppendCodeWithScopeElement(dst []byte, key, code string, scope []byte) []byte {\n\treturn AppendCodeWithScope(AppendHeader(dst, TypeCodeWithScope, key), code, scope)\n}\n\n// ReadCodeWithScope will read code and scope from src. If there are not enough bytes it\n// will return false.\nfunc ReadCodeWithScope(src []byte) (code string, scope []byte, rem []byte, ok bool) {\n\tlength, rem, ok := ReadLength(src)\n\tif !ok || len(src) < int(length) {\n\t\treturn \"\", nil, src, false\n\t}\n\n\tcode, rem, ok = readstring(rem)\n\tif !ok {\n\t\treturn \"\", nil, src, false\n\t}\n\n\tscope, rem, ok = ReadDocument(rem)\n\tif !ok {\n\t\treturn \"\", nil, src, false\n\t}\n\treturn code, scope, rem, true\n}\n\n// AppendInt32 will append i32 to dst and return the extended buffer.\nfunc AppendInt32(dst []byte, i32 int32) []byte { return binaryutil.Append32(dst, i32) }\n\n// AppendInt32Element will append a BSON int32 element using key and i32 to dst\n// and return the extended buffer.\nfunc AppendInt32Element(dst []byte, key string, i32 int32) []byte {\n\treturn AppendInt32(AppendHeader(dst, TypeInt32, key), i32)\n}\n\n// ReadInt32 will read an int32 from src. If there are not enough bytes it\n// will return false.\nfunc ReadInt32(src []byte) (int32, []byte, bool) { return binaryutil.ReadI32(src) }\n\n// AppendTimestamp will append t and i to dst and return the extended buffer.\nfunc AppendTimestamp(dst []byte, t, i uint32) []byte {\n\treturn binaryutil.Append32(binaryutil.Append32(dst, i), t) // i is the lower 4 bytes, t is the higher 4 bytes\n}\n\n// AppendTimestampElement will append a BSON timestamp element using key, t, and\n// i to dst and return the extended buffer.\nfunc AppendTimestampElement(dst []byte, key string, t, i uint32) []byte {\n\treturn AppendTimestamp(AppendHeader(dst, TypeTimestamp, key), t, i)\n}\n\n// ReadTimestamp will read t and i from src. If there are not enough bytes it\n// will return false.\nfunc ReadTimestamp(src []byte) (t, i uint32, rem []byte, ok bool) {\n\ti, rem, ok = binaryutil.ReadU32(src)\n\tif !ok {\n\t\treturn 0, 0, src, false\n\t}\n\tt, rem, ok = binaryutil.ReadU32(rem)\n\tif !ok {\n\t\treturn 0, 0, src, false\n\t}\n\treturn t, i, rem, true\n}\n\n// AppendInt64 will append i64 to dst and return the extended buffer.\nfunc AppendInt64(dst []byte, i64 int64) []byte { return binaryutil.Append64(dst, i64) }\n\n// AppendInt64Element will append a BSON int64 element using key and i64 to dst\n// and return the extended buffer.\nfunc AppendInt64Element(dst []byte, key string, i64 int64) []byte {\n\treturn AppendInt64(AppendHeader(dst, TypeInt64, key), i64)\n}\n\n// ReadInt64 will read an int64 from src. If there are not enough bytes it\n// will return false.\nfunc ReadInt64(src []byte) (int64, []byte, bool) { return binaryutil.ReadI64(src) }\n\n// AppendDecimal128 will append high and low parts of a d128 to dst and return the extended buffer.\nfunc AppendDecimal128(dst []byte, high, low uint64) []byte {\n\treturn binaryutil.Append64(binaryutil.Append64(dst, low), high)\n}\n\n// AppendDecimal128Element will append high and low parts of a BSON bson.Decimal128 element using key and\n// d128 to dst and return the extended buffer.\nfunc AppendDecimal128Element(dst []byte, key string, high, low uint64) []byte {\n\treturn AppendDecimal128(AppendHeader(dst, TypeDecimal128, key), high, low)\n}\n\n// ReadDecimal128 will read high and low parts of a bson.Decimal128 from src. If there are not enough bytes it\n// will return false.\nfunc ReadDecimal128(src []byte) (high uint64, low uint64, rem []byte, ok bool) {\n\tlow, rem, ok = binaryutil.ReadU64(src)\n\tif !ok {\n\t\treturn 0, 0, src, false\n\t}\n\n\thigh, rem, ok = binaryutil.ReadU64(rem)\n\tif !ok {\n\t\treturn 0, 0, src, false\n\t}\n\n\treturn high, low, rem, true\n}\n\n// AppendMaxKeyElement will append a BSON max key element using key to dst\n// and return the extended buffer.\nfunc AppendMaxKeyElement(dst []byte, key string) []byte {\n\treturn AppendHeader(dst, TypeMaxKey, key)\n}\n\n// AppendMinKeyElement will append a BSON min key element using key to dst\n// and return the extended buffer.\nfunc AppendMinKeyElement(dst []byte, key string) []byte {\n\treturn AppendHeader(dst, TypeMinKey, key)\n}\n\n// EqualValue will return true if the two values are equal.\nfunc EqualValue(t1, t2 Type, v1, v2 []byte) bool {\n\tif t1 != t2 {\n\t\treturn false\n\t}\n\tv1, _, ok := readValue(v1, t1)\n\tif !ok {\n\t\treturn false\n\t}\n\tv2, _, ok = readValue(v2, t2)\n\tif !ok {\n\t\treturn false\n\t}\n\treturn bytes.Equal(v1, v2)\n}\n\n// valueLength will determine the length of the next value contained in src as if it\n// is type t. The returned bool will be false if there are not enough bytes in src for\n// a value of type t.\nfunc valueLength(src []byte, t Type) (int32, bool) {\n\tvar length int32\n\tok := true\n\tswitch t {\n\tcase TypeArray, TypeEmbeddedDocument, TypeCodeWithScope:\n\t\tlength, _, ok = ReadLength(src)\n\tcase TypeBinary:\n\t\tlength, _, ok = ReadLength(src)\n\t\tlength += 4 + 1 // binary length + subtype byte\n\tcase TypeBoolean:\n\t\tlength = 1\n\tcase TypeDBPointer:\n\t\tlength, _, ok = ReadLength(src)\n\t\tlength += 4 + 12 // string length + ObjectID length\n\tcase TypeDateTime, TypeDouble, TypeInt64, TypeTimestamp:\n\t\tlength = 8\n\tcase TypeDecimal128:\n\t\tlength = 16\n\tcase TypeInt32:\n\t\tlength = 4\n\tcase TypeJavaScript, TypeString, TypeSymbol:\n\t\tlength, _, ok = ReadLength(src)\n\t\tlength += 4\n\tcase TypeMaxKey, TypeMinKey, TypeNull, TypeUndefined:\n\t\tlength = 0\n\tcase TypeObjectID:\n\t\tlength = 12\n\tcase TypeRegex:\n\t\tregex := bytes.IndexByte(src, 0x00)\n\t\tif regex < 0 {\n\t\t\tok = false\n\t\t\tbreak\n\t\t}\n\t\tpattern := bytes.IndexByte(src[regex+1:], 0x00)\n\t\tif pattern < 0 {\n\t\t\tok = false\n\t\t\tbreak\n\t\t}\n\t\tlength = int32(int64(regex) + 1 + int64(pattern) + 1)\n\tdefault:\n\t\tok = false\n\t}\n\n\treturn length, ok\n}\n\nfunc readValue(src []byte, t Type) ([]byte, []byte, bool) {\n\tlength, ok := valueLength(src, t)\n\tif !ok || int(length) > len(src) {\n\t\treturn nil, src, false\n\t}\n\n\treturn src[:length], src[length:], true\n}\n\n// ReserveLength reserves the space required for length and returns the index where to write the length\n// and the []byte with reserved space.\nfunc ReserveLength(dst []byte) (int32, []byte) {\n\tindex := len(dst)\n\treturn int32(index), append(dst, 0x00, 0x00, 0x00, 0x00)\n}\n\n// UpdateLength updates the length at index with length and returns the []byte.\nfunc UpdateLength(dst []byte, index, length int32) []byte {\n\tbinary.LittleEndian.PutUint32(dst[index:], uint32(length))\n\treturn dst\n}\n\nfunc appendLength(dst []byte, l int32) []byte { return binaryutil.Append32(dst, l) }\n\n// ReadLength reads an int32 length from src and returns the length and the remaining bytes. If\n// there aren't enough bytes to read a valid length, src is returned unomdified and the returned\n// bool will be false.\nfunc ReadLength(src []byte) (int32, []byte, bool) {\n\tln, src, ok := binaryutil.ReadI32(src)\n\tif ln < 0 {\n\t\treturn ln, src, false\n\t}\n\treturn ln, src, ok\n}\n\nfunc appendstring(dst []byte, s string) []byte {\n\tl := int32(len(s) + 1)\n\tdst = appendLength(dst, l)\n\tdst = append(dst, s...)\n\treturn append(dst, 0x00)\n}\n\nfunc readstring(src []byte) (string, []byte, bool) {\n\tl, rem, ok := ReadLength(src)\n\tif !ok {\n\t\treturn \"\", src, false\n\t}\n\tif len(src[4:]) < int(l) || l == 0 {\n\t\treturn \"\", src, false\n\t}\n\n\treturn string(rem[:l-1]), rem[l:], true\n}\n\n// readLengthBytes attempts to read a length and that number of bytes. This\n// function requires that the length include the four bytes for itself.\nfunc readLengthBytes(src []byte) ([]byte, []byte, bool) {\n\tl, _, ok := ReadLength(src)\n\tif !ok {\n\t\treturn nil, src, false\n\t}\n\tif l < 4 {\n\t\treturn nil, src, false\n\t}\n\tif len(src) < int(l) {\n\t\treturn nil, src, false\n\t}\n\treturn src[:l], src[l:], true\n}\n\nfunc appendBinarySubtype2(dst []byte, subtype byte, b []byte) []byte {\n\tdst = appendLength(dst, int32(len(b)+4)) // The bytes we'll encode need to be 4 larger for the length bytes\n\tdst = append(dst, subtype)\n\tdst = appendLength(dst, int32(len(b)))\n\treturn append(dst, b...)\n}\n\nfunc isValidCString(cs string) bool {\n\treturn !strings.ContainsRune(cs, '\\x00')\n}\n"
  },
  {
    "path": "x/bsonx/bsoncore/bsoncore_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bsoncore\n\nimport (\n\t\"bytes\"\n\t\"encoding/binary\"\n\t\"math\"\n\t\"reflect\"\n\t\"testing\"\n\n\t\"github.com/google/go-cmp/cmp\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n)\n\nfunc compareErrors(err1, err2 error) bool {\n\tif err1 == nil && err2 == nil {\n\t\treturn true\n\t}\n\n\tif err1 == nil || err2 == nil {\n\t\treturn false\n\t}\n\n\tif err1.Error() != err2.Error() {\n\t\treturn false\n\t}\n\n\treturn true\n}\n\nfunc TestAppend(t *testing.T) {\n\tbits := math.Float64bits(3.14159)\n\tpi := make([]byte, 8)\n\tbinary.LittleEndian.PutUint64(pi, bits)\n\n\ttestCases := []struct {\n\t\tname     string\n\t\tfn       any\n\t\tparams   []any\n\t\texpected []byte\n\t}{\n\t\t{\n\t\t\t\"AppendType\",\n\t\t\tAppendType,\n\t\t\t[]any{make([]byte, 0), TypeNull},\n\t\t\t[]byte{byte(TypeNull)},\n\t\t},\n\t\t{\n\t\t\t\"AppendKey\",\n\t\t\tAppendKey,\n\t\t\t[]any{make([]byte, 0), \"foobar\"},\n\t\t\t[]byte{'f', 'o', 'o', 'b', 'a', 'r', 0x00},\n\t\t},\n\t\t{\n\t\t\t\"AppendHeader\",\n\t\t\tAppendHeader,\n\t\t\t[]any{make([]byte, 0), TypeNull, \"foobar\"},\n\t\t\t[]byte{byte(TypeNull), 'f', 'o', 'o', 'b', 'a', 'r', 0x00},\n\t\t},\n\t\t{\n\t\t\t\"AppendValueElement\",\n\t\t\tAppendValueElement,\n\t\t\t[]any{make([]byte, 0), \"testing\", Value{Type: TypeBoolean, Data: []byte{0x01}}},\n\t\t\t[]byte{byte(TypeBoolean), 't', 'e', 's', 't', 'i', 'n', 'g', 0x00, 0x01},\n\t\t},\n\t\t{\n\t\t\t\"AppendDouble\",\n\t\t\tAppendDouble,\n\t\t\t[]any{make([]byte, 0), float64(3.14159)},\n\t\t\tpi,\n\t\t},\n\t\t{\n\t\t\t\"AppendDoubleElement\",\n\t\t\tAppendDoubleElement,\n\t\t\t[]any{make([]byte, 0), \"foobar\", float64(3.14159)},\n\t\t\tappend([]byte{byte(TypeDouble), 'f', 'o', 'o', 'b', 'a', 'r', 0x00}, pi...),\n\t\t},\n\t\t{\n\t\t\t\"AppendString\",\n\t\t\tAppendString,\n\t\t\t[]any{make([]byte, 0), \"barbaz\"},\n\t\t\t[]byte{0x07, 0x00, 0x00, 0x00, 'b', 'a', 'r', 'b', 'a', 'z', 0x00},\n\t\t},\n\t\t{\n\t\t\t\"AppendStringElement\",\n\t\t\tAppendStringElement,\n\t\t\t[]any{make([]byte, 0), \"foobar\", \"barbaz\"},\n\t\t\t[]byte{\n\t\t\t\tbyte(TypeString),\n\t\t\t\t'f', 'o', 'o', 'b', 'a', 'r', 0x00,\n\t\t\t\t0x07, 0x00, 0x00, 0x00, 'b', 'a', 'r', 'b', 'a', 'z', 0x00,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"AppendDocument\",\n\t\t\tAppendDocument,\n\t\t\t[]any{[]byte{0x05, 0x00, 0x00, 0x00, 0x00}, []byte{0x05, 0x00, 0x00, 0x00, 0x00}},\n\t\t\t[]byte{0x05, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00},\n\t\t},\n\t\t{\n\t\t\t\"AppendDocumentElement\",\n\t\t\tAppendDocumentElement,\n\t\t\t[]any{make([]byte, 0), \"foobar\", []byte{0x05, 0x00, 0x00, 0x00, 0x00}},\n\t\t\t[]byte{\n\t\t\t\tbyte(TypeEmbeddedDocument),\n\t\t\t\t'f', 'o', 'o', 'b', 'a', 'r', 0x00,\n\t\t\t\t0x05, 0x00, 0x00, 0x00, 0x00,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"AppendArray\",\n\t\t\tAppendArray,\n\t\t\t[]any{[]byte{0x05, 0x00, 0x00, 0x00, 0x00}, []byte{0x05, 0x00, 0x00, 0x00, 0x00}},\n\t\t\t[]byte{0x05, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00},\n\t\t},\n\t\t{\n\t\t\t\"AppendArrayElement\",\n\t\t\tAppendArrayElement,\n\t\t\t[]any{make([]byte, 0), \"foobar\", []byte{0x05, 0x00, 0x00, 0x00, 0x00}},\n\t\t\t[]byte{\n\t\t\t\tbyte(TypeArray),\n\t\t\t\t'f', 'o', 'o', 'b', 'a', 'r', 0x00,\n\t\t\t\t0x05, 0x00, 0x00, 0x00, 0x00,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"BuildArray\",\n\t\t\tBuildArray,\n\t\t\t[]any{make([]byte, 0), Value{Type: TypeDouble, Data: AppendDouble(nil, 3.14159)}},\n\t\t\t[]byte{\n\t\t\t\t0x10, 0x00, 0x00, 0x00,\n\t\t\t\tbyte(TypeDouble), '0', 0x00,\n\t\t\t\tpi[0], pi[1], pi[2], pi[3], pi[4], pi[5], pi[6], pi[7],\n\t\t\t\t0x00,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"BuildArrayElement\",\n\t\t\tBuildArrayElement,\n\t\t\t[]any{make([]byte, 0), \"foobar\", Value{Type: TypeDouble, Data: AppendDouble(nil, 3.14159)}},\n\t\t\t[]byte{\n\t\t\t\tbyte(TypeArray),\n\t\t\t\t'f', 'o', 'o', 'b', 'a', 'r', 0x00,\n\t\t\t\t0x10, 0x00, 0x00, 0x00,\n\t\t\t\tbyte(TypeDouble), '0', 0x00,\n\t\t\t\tpi[0], pi[1], pi[2], pi[3], pi[4], pi[5], pi[6], pi[7],\n\t\t\t\t0x00,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"AppendBinary Subtype2\",\n\t\t\tAppendBinary,\n\t\t\t[]any{make([]byte, 0), byte(0x02), []byte{0x01, 0x02, 0x03}},\n\t\t\t[]byte{0x07, 0x00, 0x00, 0x00, 0x02, 0x03, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03},\n\t\t},\n\t\t{\n\t\t\t\"AppendBinaryElement Subtype 2\",\n\t\t\tAppendBinaryElement,\n\t\t\t[]any{make([]byte, 0), \"foobar\", byte(0x02), []byte{0x01, 0x02, 0x03}},\n\t\t\t[]byte{\n\t\t\t\tbyte(TypeBinary),\n\t\t\t\t'f', 'o', 'o', 'b', 'a', 'r', 0x00,\n\t\t\t\t0x07, 0x00, 0x00, 0x00,\n\t\t\t\t0x02,\n\t\t\t\t0x03, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"AppendBinary\",\n\t\t\tAppendBinary,\n\t\t\t[]any{make([]byte, 0), byte(0xFF), []byte{0x01, 0x02, 0x03}},\n\t\t\t[]byte{0x03, 0x00, 0x00, 0x00, 0xFF, 0x01, 0x02, 0x03},\n\t\t},\n\t\t{\n\t\t\t\"AppendBinaryElement\",\n\t\t\tAppendBinaryElement,\n\t\t\t[]any{make([]byte, 0), \"foobar\", byte(0xFF), []byte{0x01, 0x02, 0x03}},\n\t\t\t[]byte{\n\t\t\t\tbyte(TypeBinary),\n\t\t\t\t'f', 'o', 'o', 'b', 'a', 'r', 0x00,\n\t\t\t\t0x03, 0x00, 0x00, 0x00,\n\t\t\t\t0xFF,\n\t\t\t\t0x01, 0x02, 0x03,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"AppendUndefinedElement\",\n\t\t\tAppendUndefinedElement,\n\t\t\t[]any{make([]byte, 0), \"foobar\"},\n\t\t\t[]byte{byte(TypeUndefined), 'f', 'o', 'o', 'b', 'a', 'r', 0x00},\n\t\t},\n\t\t{\n\t\t\t\"AppendObjectID\",\n\t\t\tAppendObjectID,\n\t\t\t[]any{\n\t\t\t\tmake([]byte, 0),\n\t\t\t\t[12]byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C},\n\t\t\t},\n\t\t\t[]byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C},\n\t\t},\n\t\t{\n\t\t\t\"AppendObjectIDElement\",\n\t\t\tAppendObjectIDElement,\n\t\t\t[]any{\n\t\t\t\tmake([]byte, 0), \"foobar\",\n\t\t\t\t[12]byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C},\n\t\t\t},\n\t\t\t[]byte{\n\t\t\t\tbyte(TypeObjectID),\n\t\t\t\t'f', 'o', 'o', 'b', 'a', 'r', 0x00,\n\t\t\t\t0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"AppendBoolean (true)\",\n\t\t\tAppendBoolean,\n\t\t\t[]any{make([]byte, 0), true},\n\t\t\t[]byte{0x01},\n\t\t},\n\t\t{\n\t\t\t\"AppendBoolean (false)\",\n\t\t\tAppendBoolean,\n\t\t\t[]any{make([]byte, 0), false},\n\t\t\t[]byte{0x00},\n\t\t},\n\t\t{\n\t\t\t\"AppendBooleanElement\",\n\t\t\tAppendBooleanElement,\n\t\t\t[]any{make([]byte, 0), \"foobar\", true},\n\t\t\t[]byte{byte(TypeBoolean), 'f', 'o', 'o', 'b', 'a', 'r', 0x00, 0x01},\n\t\t},\n\t\t{\n\t\t\t\"AppendDateTime\",\n\t\t\tAppendDateTime,\n\t\t\t[]any{make([]byte, 0), int64(256)},\n\t\t\t[]byte{0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},\n\t\t},\n\t\t{\n\t\t\t\"AppendDateTimeElement\",\n\t\t\tAppendDateTimeElement,\n\t\t\t[]any{make([]byte, 0), \"foobar\", int64(256)},\n\t\t\t[]byte{byte(TypeDateTime), 'f', 'o', 'o', 'b', 'a', 'r', 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},\n\t\t},\n\t\t{\n\t\t\t\"AppendNullElement\",\n\t\t\tAppendNullElement,\n\t\t\t[]any{make([]byte, 0), \"foobar\"},\n\t\t\t[]byte{byte(TypeNull), 'f', 'o', 'o', 'b', 'a', 'r', 0x00},\n\t\t},\n\t\t{\n\t\t\t\"AppendRegex\",\n\t\t\tAppendRegex,\n\t\t\t[]any{make([]byte, 0), \"bar\", \"baz\"},\n\t\t\t[]byte{'b', 'a', 'r', 0x00, 'b', 'a', 'z', 0x00},\n\t\t},\n\t\t{\n\t\t\t\"AppendRegexElement\",\n\t\t\tAppendRegexElement,\n\t\t\t[]any{make([]byte, 0), \"foobar\", \"bar\", \"baz\"},\n\t\t\t[]byte{\n\t\t\t\tbyte(TypeRegex),\n\t\t\t\t'f', 'o', 'o', 'b', 'a', 'r', 0x00,\n\t\t\t\t'b', 'a', 'r', 0x00, 'b', 'a', 'z', 0x00,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"AppendDBPointer\",\n\t\t\tAppendDBPointer,\n\t\t\t[]any{\n\t\t\t\tmake([]byte, 0),\n\t\t\t\t\"foobar\",\n\t\t\t\t[12]byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C},\n\t\t\t},\n\t\t\t[]byte{\n\t\t\t\t0x07, 0x00, 0x00, 0x00, 'f', 'o', 'o', 'b', 'a', 'r', 0x00,\n\t\t\t\t0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"AppendDBPointerElement\",\n\t\t\tAppendDBPointerElement,\n\t\t\t[]any{\n\t\t\t\tmake([]byte, 0), \"foobar\",\n\t\t\t\t\"barbaz\",\n\t\t\t\t[12]byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C},\n\t\t\t},\n\t\t\t[]byte{\n\t\t\t\tbyte(TypeDBPointer),\n\t\t\t\t'f', 'o', 'o', 'b', 'a', 'r', 0x00,\n\t\t\t\t0x07, 0x00, 0x00, 0x00, 'b', 'a', 'r', 'b', 'a', 'z', 0x00,\n\t\t\t\t0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"AppendJavaScript\",\n\t\t\tAppendJavaScript,\n\t\t\t[]any{make([]byte, 0), \"barbaz\"},\n\t\t\t[]byte{0x07, 0x00, 0x00, 0x00, 'b', 'a', 'r', 'b', 'a', 'z', 0x00},\n\t\t},\n\t\t{\n\t\t\t\"AppendJavaScriptElement\",\n\t\t\tAppendJavaScriptElement,\n\t\t\t[]any{make([]byte, 0), \"foobar\", \"barbaz\"},\n\t\t\t[]byte{\n\t\t\t\tbyte(TypeJavaScript),\n\t\t\t\t'f', 'o', 'o', 'b', 'a', 'r', 0x00,\n\t\t\t\t0x07, 0x00, 0x00, 0x00, 'b', 'a', 'r', 'b', 'a', 'z', 0x00,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"AppendSymbol\",\n\t\t\tAppendSymbol,\n\t\t\t[]any{make([]byte, 0), \"barbaz\"},\n\t\t\t[]byte{0x07, 0x00, 0x00, 0x00, 'b', 'a', 'r', 'b', 'a', 'z', 0x00},\n\t\t},\n\t\t{\n\t\t\t\"AppendSymbolElement\",\n\t\t\tAppendSymbolElement,\n\t\t\t[]any{make([]byte, 0), \"foobar\", \"barbaz\"},\n\t\t\t[]byte{\n\t\t\t\tbyte(TypeSymbol),\n\t\t\t\t'f', 'o', 'o', 'b', 'a', 'r', 0x00,\n\t\t\t\t0x07, 0x00, 0x00, 0x00, 'b', 'a', 'r', 'b', 'a', 'z', 0x00,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"AppendCodeWithScope\",\n\t\t\tAppendCodeWithScope,\n\t\t\t[]any{[]byte{0x05, 0x00, 0x00, 0x00, 0x00}, \"foobar\", []byte{0x05, 0x00, 0x00, 0x00, 0x00}},\n\t\t\t[]byte{\n\t\t\t\t0x05, 0x00, 0x00, 0x00, 0x00,\n\t\t\t\t0x14, 0x00, 0x00, 0x00,\n\t\t\t\t0x07, 0x00, 0x00, 0x00, 'f', 'o', 'o', 'b', 'a', 'r', 0x00,\n\t\t\t\t0x05, 0x00, 0x00, 0x00, 0x00,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"AppendCodeWithScopeElement\",\n\t\t\tAppendCodeWithScopeElement,\n\t\t\t[]any{make([]byte, 0), \"foobar\", \"barbaz\", []byte{0x05, 0x00, 0x00, 0x00, 0x00}},\n\t\t\t[]byte{\n\t\t\t\tbyte(TypeCodeWithScope),\n\t\t\t\t'f', 'o', 'o', 'b', 'a', 'r', 0x00,\n\t\t\t\t0x14, 0x00, 0x00, 0x00,\n\t\t\t\t0x07, 0x00, 0x00, 0x00, 'b', 'a', 'r', 'b', 'a', 'z', 0x00,\n\t\t\t\t0x05, 0x00, 0x00, 0x00, 0x00,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"AppendInt32\",\n\t\t\tAppendInt32,\n\t\t\t[]any{make([]byte, 0), int32(256)},\n\t\t\t[]byte{0x00, 0x01, 0x00, 0x00},\n\t\t},\n\t\t{\n\t\t\t\"AppendInt32Element\",\n\t\t\tAppendInt32Element,\n\t\t\t[]any{make([]byte, 0), \"foobar\", int32(256)},\n\t\t\t[]byte{byte(TypeInt32), 'f', 'o', 'o', 'b', 'a', 'r', 0x00, 0x00, 0x01, 0x00, 0x00},\n\t\t},\n\t\t{\n\t\t\t\"AppendTimestamp\",\n\t\t\tAppendTimestamp,\n\t\t\t[]any{make([]byte, 0), uint32(65536), uint32(256)},\n\t\t\t[]byte{0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00},\n\t\t},\n\t\t{\n\t\t\t\"AppendTimestampElement\",\n\t\t\tAppendTimestampElement,\n\t\t\t[]any{make([]byte, 0), \"foobar\", uint32(65536), uint32(256)},\n\t\t\t[]byte{byte(TypeTimestamp), 'f', 'o', 'o', 'b', 'a', 'r', 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00},\n\t\t},\n\t\t{\n\t\t\t\"AppendInt64\",\n\t\t\tAppendInt64,\n\t\t\t[]any{make([]byte, 0), int64(4294967296)},\n\t\t\t[]byte{0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00},\n\t\t},\n\t\t{\n\t\t\t\"AppendInt64Element\",\n\t\t\tAppendInt64Element,\n\t\t\t[]any{make([]byte, 0), \"foobar\", int64(4294967296)},\n\t\t\t[]byte{byte(TypeInt64), 'f', 'o', 'o', 'b', 'a', 'r', 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00},\n\t\t},\n\t\t{\n\t\t\t\"AppendDecimal128\",\n\t\t\tAppendDecimal128,\n\t\t\t[]any{make([]byte, 0), uint64(4294967296), uint64(65536)},\n\t\t\t[]byte{\n\t\t\t\t0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,\n\t\t\t\t0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"AppendDecimal128Element\",\n\t\t\tAppendDecimal128Element,\n\t\t\t[]any{make([]byte, 0), \"foobar\", uint64(4294967296), uint64(65536)},\n\t\t\t[]byte{\n\t\t\t\tbyte(TypeDecimal128), 'f', 'o', 'o', 'b', 'a', 'r', 0x00,\n\t\t\t\t0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,\n\t\t\t\t0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"AppendMaxKeyElement\",\n\t\t\tAppendMaxKeyElement,\n\t\t\t[]any{make([]byte, 0), \"foobar\"},\n\t\t\t[]byte{byte(TypeMaxKey), 'f', 'o', 'o', 'b', 'a', 'r', 0x00},\n\t\t},\n\t\t{\n\t\t\t\"AppendMinKeyElement\",\n\t\t\tAppendMinKeyElement,\n\t\t\t[]any{make([]byte, 0), \"foobar\"},\n\t\t\t[]byte{byte(TypeMinKey), 'f', 'o', 'o', 'b', 'a', 'r', 0x00},\n\t\t},\n\t}\n\n\tfor _, tc := range testCases {\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\tfn := reflect.ValueOf(tc.fn)\n\t\t\tif fn.Kind() != reflect.Func {\n\t\t\t\tt.Fatalf(\"fn must be of kind Func but is a %v\", fn.Kind())\n\t\t\t}\n\t\t\tif fn.Type().NumIn() != len(tc.params) {\n\t\t\t\tt.Fatalf(\"tc.params must match the number of params in tc.fn. params %d; fn %d\", fn.Type().NumIn(), len(tc.params))\n\t\t\t}\n\t\t\tif fn.Type().NumOut() != 1 || fn.Type().Out(0) != reflect.TypeOf([]byte{}) {\n\t\t\t\tt.Fatalf(\"fn must have one return parameter and it must be a []byte.\")\n\t\t\t}\n\t\t\tparams := make([]reflect.Value, 0, len(tc.params))\n\t\t\tfor _, param := range tc.params {\n\t\t\t\tparams = append(params, reflect.ValueOf(param))\n\t\t\t}\n\t\t\tresults := fn.Call(params)\n\t\t\tgot := results[0].Interface().([]byte)\n\t\t\twant := tc.expected\n\t\t\tif !bytes.Equal(got, want) {\n\t\t\t\tt.Errorf(\"Did not receive expected bytes. got %v; want %v\", got, want)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestRead(t *testing.T) {\n\tbits := math.Float64bits(3.14159)\n\tpi := make([]byte, 8)\n\tbinary.LittleEndian.PutUint64(pi, bits)\n\n\ttestCases := []struct {\n\t\tname     string\n\t\tfn       any\n\t\tparam    []byte\n\t\texpected []any\n\t}{\n\t\t{\n\t\t\t\"ReadType/not enough bytes\",\n\t\t\tReadType,\n\t\t\t[]byte{},\n\t\t\t[]any{Type(0), []byte{}, false},\n\t\t},\n\t\t{\n\t\t\t\"ReadType/success\",\n\t\t\tReadType,\n\t\t\t[]byte{0x0A},\n\t\t\t[]any{TypeNull, []byte{}, true},\n\t\t},\n\t\t{\n\t\t\t\"ReadKey/not enough bytes\",\n\t\t\tReadKey,\n\t\t\t[]byte{},\n\t\t\t[]any{\"\", []byte{}, false},\n\t\t},\n\t\t{\n\t\t\t\"ReadKey/success\",\n\t\t\tReadKey,\n\t\t\t[]byte{'f', 'o', 'o', 'b', 'a', 'r', 0x00},\n\t\t\t[]any{\"foobar\", []byte{}, true},\n\t\t},\n\t\t{\n\t\t\t\"ReadHeader/not enough bytes (type)\",\n\t\t\tReadHeader,\n\t\t\t[]byte{},\n\t\t\t[]any{Type(0), \"\", []byte{}, false},\n\t\t},\n\t\t{\n\t\t\t\"ReadHeader/not enough bytes (key)\",\n\t\t\tReadHeader,\n\t\t\t[]byte{0x0A, 'f', 'o', 'o'},\n\t\t\t[]any{Type(0), \"\", []byte{0x0A, 'f', 'o', 'o'}, false},\n\t\t},\n\t\t{\n\t\t\t\"ReadHeader/success\",\n\t\t\tReadHeader,\n\t\t\t[]byte{0x0A, 'f', 'o', 'o', 'b', 'a', 'r', 0x00},\n\t\t\t[]any{TypeNull, \"foobar\", []byte{}, true},\n\t\t},\n\t\t{\n\t\t\t\"ReadDouble/not enough bytes\",\n\t\t\tReadDouble,\n\t\t\t[]byte{0x01, 0x02, 0x03, 0x04},\n\t\t\t[]any{float64(0.00), []byte{0x01, 0x02, 0x03, 0x04}, false},\n\t\t},\n\t\t{\n\t\t\t\"ReadDouble/success\",\n\t\t\tReadDouble,\n\t\t\tpi,\n\t\t\t[]any{float64(3.14159), []byte{}, true},\n\t\t},\n\t\t{\n\t\t\t\"ReadString/not enough bytes (length)\",\n\t\t\tReadString,\n\t\t\t[]byte{},\n\t\t\t[]any{\"\", []byte{}, false},\n\t\t},\n\t\t{\n\t\t\t\"ReadString/not enough bytes (value)\",\n\t\t\tReadString,\n\t\t\t[]byte{0x0F, 0x00, 0x00, 0x00},\n\t\t\t[]any{\"\", []byte{0x0F, 0x00, 0x00, 0x00}, false},\n\t\t},\n\t\t{\n\t\t\t\"ReadString/success\",\n\t\t\tReadString,\n\t\t\t[]byte{0x07, 0x00, 0x00, 0x00, 'f', 'o', 'o', 'b', 'a', 'r', 0x00},\n\t\t\t[]any{\"foobar\", []byte{}, true},\n\t\t},\n\t\t{\n\t\t\t\"ReadDocument/not enough bytes (length)\",\n\t\t\tReadDocument,\n\t\t\t[]byte{},\n\t\t\t[]any{Document(nil), []byte{}, false},\n\t\t},\n\t\t{\n\t\t\t\"ReadDocument/not enough bytes (value)\",\n\t\t\tReadDocument,\n\t\t\t[]byte{0x0F, 0x00, 0x00, 0x00},\n\t\t\t[]any{Document(nil), []byte{0x0F, 0x00, 0x00, 0x00}, false},\n\t\t},\n\t\t{\n\t\t\t\"ReadDocument/success\",\n\t\t\tReadDocument,\n\t\t\t[]byte{0x0A, 0x00, 0x00, 0x00, 0x0A, 'f', 'o', 'o', 0x00, 0x00},\n\t\t\t[]any{Document{0x0A, 0x00, 0x00, 0x00, 0x0A, 'f', 'o', 'o', 0x00, 0x00}, []byte{}, true},\n\t\t},\n\t\t{\n\t\t\t\"ReadArray/not enough bytes (length)\",\n\t\t\tReadArray,\n\t\t\t[]byte{},\n\t\t\t[]any{Array(nil), []byte{}, false},\n\t\t},\n\t\t{\n\t\t\t\"ReadArray/not enough bytes (value)\",\n\t\t\tReadArray,\n\t\t\t[]byte{0x0F, 0x00, 0x00, 0x00},\n\t\t\t[]any{Array(nil), []byte{0x0F, 0x00, 0x00, 0x00}, false},\n\t\t},\n\t\t{\n\t\t\t\"ReadArray/success\",\n\t\t\tReadArray,\n\t\t\t[]byte{0x08, 0x00, 0x00, 0x00, 0x0A, '0', 0x00, 0x00},\n\t\t\t[]any{Array{0x08, 0x00, 0x00, 0x00, 0x0A, '0', 0x00, 0x00}, []byte{}, true},\n\t\t},\n\t\t{\n\t\t\t\"ReadBinary/not enough bytes (length)\",\n\t\t\tReadBinary,\n\t\t\t[]byte{},\n\t\t\t[]any{byte(0), []byte(nil), []byte{}, false},\n\t\t},\n\t\t{\n\t\t\t\"ReadBinary/not enough bytes (subtype)\",\n\t\t\tReadBinary,\n\t\t\t[]byte{0x0F, 0x00, 0x00, 0x00},\n\t\t\t[]any{byte(0), []byte(nil), []byte{0x0F, 0x00, 0x00, 0x00}, false},\n\t\t},\n\t\t{\n\t\t\t\"ReadBinary/not enough bytes (value)\",\n\t\t\tReadBinary,\n\t\t\t[]byte{0x0F, 0x00, 0x00, 0x00, 0x00},\n\t\t\t[]any{byte(0), []byte(nil), []byte{0x0F, 0x00, 0x00, 0x00, 0x00}, false},\n\t\t},\n\t\t{\n\t\t\t\"ReadBinary/not enough bytes (subtype 2 length)\",\n\t\t\tReadBinary,\n\t\t\t[]byte{0x03, 0x00, 0x00, 0x00, 0x02, 0x0F, 0x00, 0x00},\n\t\t\t[]any{byte(0), []byte(nil), []byte{0x03, 0x00, 0x00, 0x00, 0x02, 0x0F, 0x00, 0x00}, false},\n\t\t},\n\t\t{\n\t\t\t\"ReadBinary/not enough bytes (subtype 2 value)\",\n\t\t\tReadBinary,\n\t\t\t[]byte{0x0F, 0x00, 0x00, 0x00, 0x02, 0x0F, 0x00, 0x00, 0x00, 0x01, 0x02},\n\t\t\t[]any{\n\t\t\t\tbyte(0), []byte(nil),\n\t\t\t\t[]byte{0x0F, 0x00, 0x00, 0x00, 0x02, 0x0F, 0x00, 0x00, 0x00, 0x01, 0x02},\n\t\t\t\tfalse,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"ReadBinary/success (subtype 2)\",\n\t\t\tReadBinary,\n\t\t\t[]byte{0x06, 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x00, 0x01, 0x02},\n\t\t\t[]any{byte(0x02), []byte{0x01, 0x02}, []byte{}, true},\n\t\t},\n\t\t{\n\t\t\t\"ReadBinary/success\",\n\t\t\tReadBinary,\n\t\t\t[]byte{0x03, 0x00, 0x00, 0x00, 0xFF, 0x01, 0x02, 0x03},\n\t\t\t[]any{byte(0xFF), []byte{0x01, 0x02, 0x03}, []byte{}, true},\n\t\t},\n\t\t{\n\t\t\t\"ReadObjectID/not enough bytes\",\n\t\t\tReadObjectID,\n\t\t\t[]byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06},\n\t\t\t[]any{[12]byte{}, []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06}, false},\n\t\t},\n\t\t{\n\t\t\t\"ReadObjectID/success\",\n\t\t\tReadObjectID,\n\t\t\t[]byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C},\n\t\t\t[]any{\n\t\t\t\t[12]byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C},\n\t\t\t\t[]byte{},\n\t\t\t\ttrue,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"ReadBoolean/not enough bytes\",\n\t\t\tReadBoolean,\n\t\t\t[]byte{},\n\t\t\t[]any{false, []byte{}, false},\n\t\t},\n\t\t{\n\t\t\t\"ReadBoolean/success\",\n\t\t\tReadBoolean,\n\t\t\t[]byte{0x01},\n\t\t\t[]any{true, []byte{}, true},\n\t\t},\n\t\t{\n\t\t\t\"ReadDateTime/not enough bytes\",\n\t\t\tReadDateTime,\n\t\t\t[]byte{0x01, 0x02, 0x03, 0x04},\n\t\t\t[]any{int64(0), []byte{0x01, 0x02, 0x03, 0x04}, false},\n\t\t},\n\t\t{\n\t\t\t\"ReadDateTime/success\",\n\t\t\tReadDateTime,\n\t\t\t[]byte{0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00},\n\t\t\t[]any{int64(65536), []byte{}, true},\n\t\t},\n\t\t{\n\t\t\t\"ReadRegex/not enough bytes (pattern)\",\n\t\t\tReadRegex,\n\t\t\t[]byte{},\n\t\t\t[]any{\"\", \"\", []byte{}, false},\n\t\t},\n\t\t{\n\t\t\t\"ReadRegex/not enough bytes (options)\",\n\t\t\tReadRegex,\n\t\t\t[]byte{'f', 'o', 'o', 0x00},\n\t\t\t[]any{\"\", \"\", []byte{'f', 'o', 'o', 0x00}, false},\n\t\t},\n\t\t{\n\t\t\t\"ReadRegex/success\",\n\t\t\tReadRegex,\n\t\t\t[]byte{'f', 'o', 'o', 0x00, 'b', 'a', 'r', 0x00},\n\t\t\t[]any{\"foo\", \"bar\", []byte{}, true},\n\t\t},\n\t\t{\n\t\t\t\"ReadDBPointer/not enough bytes (ns)\",\n\t\t\tReadDBPointer,\n\t\t\t[]byte{},\n\t\t\t[]any{\"\", [12]byte{}, []byte{}, false},\n\t\t},\n\t\t{\n\t\t\t\"ReadDBPointer/not enough bytes (objectID)\",\n\t\t\tReadDBPointer,\n\t\t\t[]byte{0x04, 0x00, 0x00, 0x00, 'f', 'o', 'o', 0x00},\n\t\t\t[]any{\"\", [12]byte{}, []byte{0x04, 0x00, 0x00, 0x00, 'f', 'o', 'o', 0x00}, false},\n\t\t},\n\t\t{\n\t\t\t\"ReadDBPointer/success\",\n\t\t\tReadDBPointer,\n\t\t\t[]byte{\n\t\t\t\t0x04, 0x00, 0x00, 0x00, 'f', 'o', 'o', 0x00,\n\t\t\t\t0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C,\n\t\t\t},\n\t\t\t[]any{\n\t\t\t\t\"foo\",\n\t\t\t\t[12]byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C},\n\t\t\t\t[]byte{},\n\t\t\t\ttrue,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"ReadJavaScript/not enough bytes (length)\",\n\t\t\tReadJavaScript,\n\t\t\t[]byte{},\n\t\t\t[]any{\"\", []byte{}, false},\n\t\t},\n\t\t{\n\t\t\t\"ReadJavaScript/not enough bytes (value)\",\n\t\t\tReadJavaScript,\n\t\t\t[]byte{0x0F, 0x00, 0x00, 0x00},\n\t\t\t[]any{\"\", []byte{0x0F, 0x00, 0x00, 0x00}, false},\n\t\t},\n\t\t{\n\t\t\t\"ReadJavaScript/success\",\n\t\t\tReadJavaScript,\n\t\t\t[]byte{0x07, 0x00, 0x00, 0x00, 'f', 'o', 'o', 'b', 'a', 'r', 0x00},\n\t\t\t[]any{\"foobar\", []byte{}, true},\n\t\t},\n\t\t{\n\t\t\t\"ReadSymbol/not enough bytes (length)\",\n\t\t\tReadSymbol,\n\t\t\t[]byte{},\n\t\t\t[]any{\"\", []byte{}, false},\n\t\t},\n\t\t{\n\t\t\t\"ReadSymbol/not enough bytes (value)\",\n\t\t\tReadSymbol,\n\t\t\t[]byte{0x0F, 0x00, 0x00, 0x00},\n\t\t\t[]any{\"\", []byte{0x0F, 0x00, 0x00, 0x00}, false},\n\t\t},\n\t\t{\n\t\t\t\"ReadSymbol/success\",\n\t\t\tReadSymbol,\n\t\t\t[]byte{0x07, 0x00, 0x00, 0x00, 'f', 'o', 'o', 'b', 'a', 'r', 0x00},\n\t\t\t[]any{\"foobar\", []byte{}, true},\n\t\t},\n\t\t{\n\t\t\t\"ReadCodeWithScope/ not enough bytes (length)\",\n\t\t\tReadCodeWithScope,\n\t\t\t[]byte{},\n\t\t\t[]any{\"\", []byte(nil), []byte{}, false},\n\t\t},\n\t\t{\n\t\t\t\"ReadCodeWithScope/ not enough bytes (value)\",\n\t\t\tReadCodeWithScope,\n\t\t\t[]byte{0x0F, 0x00, 0x00, 0x00},\n\t\t\t[]any{\"\", []byte(nil), []byte{0x0F, 0x00, 0x00, 0x00}, false},\n\t\t},\n\t\t{\n\t\t\t\"ReadCodeWithScope/not enough bytes (code value)\",\n\t\t\tReadCodeWithScope,\n\t\t\t[]byte{\n\t\t\t\t0x0C, 0x00, 0x00, 0x00,\n\t\t\t\t0x0F, 0x00, 0x00, 0x00,\n\t\t\t\t'f', 'o', 'o', 0x00,\n\t\t\t},\n\t\t\t[]any{\n\t\t\t\t\"\", []byte(nil),\n\t\t\t\t[]byte{\n\t\t\t\t\t0x0C, 0x00, 0x00, 0x00,\n\t\t\t\t\t0x0F, 0x00, 0x00, 0x00,\n\t\t\t\t\t'f', 'o', 'o', 0x00,\n\t\t\t\t},\n\t\t\t\tfalse,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"ReadCodeWithScope/success\",\n\t\t\tReadCodeWithScope,\n\t\t\t[]byte{\n\t\t\t\t0x19, 0x00, 0x00, 0x00,\n\t\t\t\t0x07, 0x00, 0x00, 0x00, 'f', 'o', 'o', 'b', 'a', 'r', 0x00,\n\t\t\t\t0x0A, 0x00, 0x00, 0x00, 0x0A, 'f', 'o', 'o', 0x00, 0x00,\n\t\t\t},\n\t\t\t[]any{\n\t\t\t\t\"foobar\",\n\t\t\t\t[]byte{0x0A, 0x00, 0x00, 0x00, 0x0A, 'f', 'o', 'o', 0x00, 0x00},\n\t\t\t\t[]byte{},\n\t\t\t\ttrue,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"ReadInt32/not enough bytes\",\n\t\t\tReadInt32,\n\t\t\t[]byte{0x01},\n\t\t\t[]any{int32(0), []byte{0x01}, false},\n\t\t},\n\t\t{\n\t\t\t\"ReadInt32/success\",\n\t\t\tReadInt32,\n\t\t\t[]byte{0x00, 0x01, 0x00, 0x00},\n\t\t\t[]any{int32(256), []byte{}, true},\n\t\t},\n\t\t{\n\t\t\t\"ReadTimestamp/not enough bytes (increment)\",\n\t\t\tReadTimestamp,\n\t\t\t[]byte{},\n\t\t\t[]any{uint32(0), uint32(0), []byte{}, false},\n\t\t},\n\t\t{\n\t\t\t\"ReadTimestamp/not enough bytes (timestamp)\",\n\t\t\tReadTimestamp,\n\t\t\t[]byte{0x00, 0x01, 0x00, 0x00},\n\t\t\t[]any{uint32(0), uint32(0), []byte{0x00, 0x01, 0x00, 0x00}, false},\n\t\t},\n\t\t{\n\t\t\t\"ReadTimestamp/success\",\n\t\t\tReadTimestamp,\n\t\t\t[]byte{0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00},\n\t\t\t[]any{uint32(65536), uint32(256), []byte{}, true},\n\t\t},\n\t\t{\n\t\t\t\"ReadInt64/not enough bytes\",\n\t\t\tReadInt64,\n\t\t\t[]byte{0x01},\n\t\t\t[]any{int64(0), []byte{0x01}, false},\n\t\t},\n\t\t{\n\t\t\t\"ReadInt64/success\",\n\t\t\tReadInt64,\n\t\t\t[]byte{0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00},\n\t\t\t[]any{int64(4294967296), []byte{}, true},\n\t\t},\n\t\t{\n\t\t\t\"ReadDecimal128/not enough bytes (low)\",\n\t\t\tReadDecimal128,\n\t\t\t[]byte{},\n\t\t\t[]any{uint64(0), uint64(0), []byte{}, false},\n\t\t},\n\t\t{\n\t\t\t\"ReadDecimal128/not enough bytes (high)\",\n\t\t\tReadDecimal128,\n\t\t\t[]byte{0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00},\n\t\t\t[]any{uint64(0), uint64(0), []byte{0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00}, false},\n\t\t},\n\t\t{\n\t\t\t\"ReadDecimal128/success\",\n\t\t\tReadDecimal128,\n\t\t\t[]byte{\n\t\t\t\t0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,\n\t\t\t\t0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,\n\t\t\t},\n\t\t\t[]any{uint64(4294967296), uint64(16777216), []byte{}, true},\n\t\t},\n\t}\n\n\tfor _, tc := range testCases {\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\tfn := reflect.ValueOf(tc.fn)\n\t\t\tif fn.Kind() != reflect.Func {\n\t\t\t\tt.Fatalf(\"fn must be of kind Func but it is a %v\", fn.Kind())\n\t\t\t}\n\t\t\tif fn.Type().NumIn() != 1 || fn.Type().In(0) != reflect.TypeOf([]byte{}) {\n\t\t\t\tt.Fatalf(\"fn must have one parameter and it must be a []byte.\")\n\t\t\t}\n\t\t\tresults := fn.Call([]reflect.Value{reflect.ValueOf(tc.param)})\n\t\t\tif len(results) != len(tc.expected) {\n\t\t\t\tt.Fatalf(\"Length of results does not match. got %d; want %d\", len(results), len(tc.expected))\n\t\t\t}\n\t\t\tfor idx := range results {\n\t\t\t\tgot := results[idx].Interface()\n\t\t\t\twant := tc.expected[idx]\n\t\t\t\tif !cmp.Equal(got, want) {\n\t\t\t\t\tt.Errorf(\"Result %d does not match. got %v; want %v\", idx, got, want)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestBuild(t *testing.T) {\n\ttestCases := []struct {\n\t\tname  string\n\t\telems [][]byte\n\t\twant  []byte\n\t}{\n\t\t{\n\t\t\t\"one element\",\n\t\t\t[][]byte{AppendDoubleElement(nil, \"pi\", 3.14159)},\n\t\t\t[]byte{0x11, 0x00, 0x00, 0x00, 0x1, 0x70, 0x69, 0x00, 0x6e, 0x86, 0x1b, 0xf0, 0xf9, 0x21, 0x9, 0x40, 0x00},\n\t\t},\n\t\t{\n\t\t\t\"two elements\",\n\t\t\t[][]byte{AppendDoubleElement(nil, \"pi\", 3.14159), AppendStringElement(nil, \"hello\", \"world!!\")},\n\t\t\t[]byte{\n\t\t\t\t0x24, 0x00, 0x00, 0x00, 0x01, 0x70, 0x69, 0x00, 0x6e, 0x86, 0x1b, 0xf0,\n\t\t\t\t0xf9, 0x21, 0x09, 0x40, 0x02, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x00, 0x08,\n\t\t\t\t0x00, 0x00, 0x00, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x21, 0x21, 0x00, 0x00,\n\t\t\t},\n\t\t},\n\t}\n\n\tfor _, tc := range testCases {\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\tt.Run(\"BuildDocument\", func(t *testing.T) {\n\t\t\t\telems := make([]byte, 0)\n\t\t\t\tfor _, elem := range tc.elems {\n\t\t\t\t\telems = append(elems, elem...)\n\t\t\t\t}\n\t\t\t\tgot := BuildDocument(nil, elems)\n\t\t\t\tif !bytes.Equal(got, tc.want) {\n\t\t\t\t\tt.Errorf(\"Documents do not match. got %v; want %v\", got, tc.want)\n\t\t\t\t}\n\t\t\t})\n\t\t\tt.Run(\"BuildDocumentFromElements\", func(t *testing.T) {\n\t\t\t\tgot := BuildDocumentFromElements(nil, tc.elems...)\n\t\t\t\tif !bytes.Equal(got, tc.want) {\n\t\t\t\t\tt.Errorf(\"Documents do not match. got %v; want %v\", got, tc.want)\n\t\t\t\t}\n\t\t\t})\n\t\t})\n\t}\n}\n\nfunc TestNullBytes(t *testing.T) {\n\t// Helper function to execute the provided callback and assert that it panics with the expected message. The\n\t// createBSONFn callback should create a BSON document/array/value and return the stringified version.\n\tassertBSONCreationPanics := func(t *testing.T, createBSONFn func(), expected string) {\n\t\tt.Helper()\n\n\t\tdefer func() {\n\t\t\tgot := recover()\n\t\t\tassert.Equal(t, expected, got, \"expected panic with error %v, got error %v\", expected, got)\n\t\t}()\n\t\tcreateBSONFn()\n\t}\n\n\tt.Run(\"element keys\", func(t *testing.T) {\n\t\tcreateDocFn := func() {\n\t\t\tNewDocumentBuilder().AppendString(\"a\\x00\", \"foo\")\n\t\t}\n\t\tassertBSONCreationPanics(t, createDocFn, invalidKeyPanicMsg)\n\t})\n\tt.Run(\"regex values\", func(t *testing.T) {\n\t\ttestCases := []struct {\n\t\t\tname    string\n\t\t\tpattern string\n\t\t\toptions string\n\t\t}{\n\t\t\t{\"null bytes in pattern\", \"a\\x00\", \"i\"},\n\t\t\t{\"null bytes in options\", \"pattern\", \"i\\x00\"},\n\t\t}\n\t\tfor _, tc := range testCases {\n\t\t\tt.Run(tc.name+\"-AppendRegexElement\", func(t *testing.T) {\n\t\t\t\tcreateDocFn := func() {\n\t\t\t\t\tAppendRegexElement(nil, \"foo\", tc.pattern, tc.options)\n\t\t\t\t}\n\t\t\t\tassertBSONCreationPanics(t, createDocFn, invalidRegexPanicMsg)\n\t\t\t})\n\t\t\tt.Run(tc.name+\"-AppendRegex\", func(t *testing.T) {\n\t\t\t\tcreateValFn := func() {\n\t\t\t\t\tAppendRegex(nil, tc.pattern, tc.options)\n\t\t\t\t}\n\t\t\t\tassertBSONCreationPanics(t, createValFn, invalidRegexPanicMsg)\n\t\t\t})\n\t\t}\n\t})\n\tt.Run(\"sub document field name\", func(t *testing.T) {\n\t\tcreateDocFn := func() {\n\t\t\tNewDocumentBuilder().StartDocument(\"foobar\").AppendDocument(\"a\\x00\", []byte(\"foo\")).FinishDocument()\n\t\t}\n\t\tassertBSONCreationPanics(t, createDocFn, invalidKeyPanicMsg)\n\t})\n}\n\nfunc TestInvalidBytes(t *testing.T) {\n\tt.Parallel()\n\n\tt.Run(\"read length less than 4 int bytes\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\t_, src, ok := readLengthBytes([]byte{0x01, 0x00, 0x00, 0x00})\n\t\tassert.False(t, ok, \"expected not ok response for invalid length read\")\n\t\tassert.Equal(t, 4, len(src), \"expected src to contain the size parameter still\")\n\t})\n}\n"
  },
  {
    "path": "x/bsonx/bsoncore/doc.go",
    "content": "// Copyright (C) MongoDB, Inc. 2022-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\n// Package bsoncore is intended for internal use only. It is made available to\n// facilitate use cases that require access to internal MongoDB driver\n// functionality and state. The API of this package is not stable and there is\n// no backward compatibility guarantee.\n//\n// WARNING: THIS PACKAGE IS EXPERIMENTAL AND MAY BE MODIFIED OR REMOVED WITHOUT\n// NOTICE! USE WITH EXTREME CAUTION!\n//\n// Package bsoncore contains functions that can be used to encode and decode\n// BSON elements and values to or from a slice of bytes. These functions are\n// aimed at allowing low level manipulation of BSON and can be used to build a\n// higher level BSON library.\n//\n// The Read* functions within this package return the values of the element and\n// a boolean indicating if the values are valid. A boolean was used instead of\n// an error because any error that would be returned would be the same: not\n// enough bytes. This library attempts to do no validation, it will only return\n// false if there are not enough bytes for an item to be read. For example, the\n// ReadDocument function checks the length, if that length is larger than the\n// number of bytes available, it will return false, if there are enough bytes,\n// it will return those bytes and true. It is the consumers responsibility to\n// validate those bytes.\n//\n// The Append* functions within this package will append the type value to the\n// given dst slice. If the slice has enough capacity, it will not grow the\n// slice. The Append*Element functions within this package operate in the same\n// way, but additionally append the BSON type and the key before the value.\npackage bsoncore\n"
  },
  {
    "path": "x/bsonx/bsoncore/document.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bsoncore\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/binaryutil\"\n)\n\n// ValidationError is an error type returned when attempting to validate a document or array.\ntype ValidationError string\n\nfunc (ve ValidationError) Error() string { return string(ve) }\n\n// NewDocumentLengthError creates and returns an error for when the length of a document exceeds the\n// bytes available.\nfunc NewDocumentLengthError(length, rem int) error {\n\treturn lengthError(\"document\", length, rem)\n}\n\nfunc lengthError(bufferType string, length, rem int) error {\n\treturn ValidationError(fmt.Sprintf(\"%v length exceeds available bytes. length=%d remainingBytes=%d\",\n\t\tbufferType, length, rem))\n}\n\n// InsufficientBytesError indicates that there were not enough bytes to read the next component.\ntype InsufficientBytesError struct {\n\tSource    []byte\n\tRemaining []byte\n}\n\n// NewInsufficientBytesError creates a new InsufficientBytesError with the given Document and\n// remaining bytes.\nfunc NewInsufficientBytesError(src, rem []byte) InsufficientBytesError {\n\treturn InsufficientBytesError{Source: src, Remaining: rem}\n}\n\n// Error implements the error interface.\nfunc (ibe InsufficientBytesError) Error() string {\n\treturn \"too few bytes to read next component\"\n}\n\n// Equal checks that err2 also is an ErrTooSmall.\nfunc (ibe InsufficientBytesError) Equal(err2 error) bool {\n\tswitch err2.(type) {\n\tcase InsufficientBytesError:\n\t\treturn true\n\tdefault:\n\t\treturn false\n\t}\n}\n\n// InvalidDepthTraversalError is returned when attempting a recursive Lookup when one component of\n// the path is neither an embedded document nor an array.\ntype InvalidDepthTraversalError struct {\n\tKey  string\n\tType Type\n}\n\nfunc (idte InvalidDepthTraversalError) Error() string {\n\treturn fmt.Sprintf(\n\t\t\"attempt to traverse into %s, but it's type is %s, not %s nor %s\",\n\t\tidte.Key, idte.Type, TypeEmbeddedDocument, TypeArray,\n\t)\n}\n\n// ErrMissingNull is returned when a document or array's last byte is not null.\nconst ErrMissingNull ValidationError = \"document or array end is missing null byte\"\n\n// ErrInvalidLength indicates that a length in a binary representation of a BSON document or array\n// is invalid.\nconst ErrInvalidLength ValidationError = \"document or array length is invalid\"\n\n// ErrNilReader indicates that an operation was attempted on a nil io.Reader.\nvar ErrNilReader = errors.New(\"nil reader\")\n\n// ErrEmptyKey indicates that no key was provided to a Lookup method.\nvar ErrEmptyKey = errors.New(\"empty key provided\")\n\n// ErrElementNotFound indicates that an Element matching a certain condition does not exist.\nvar ErrElementNotFound = errors.New(\"element not found\")\n\n// ErrOutOfBounds indicates that an index provided to access something was invalid.\nvar ErrOutOfBounds = errors.New(\"out of bounds\")\n\n// Document is a raw bytes representation of a BSON document.\ntype Document []byte\n\n// NewDocumentFromReader reads a document from r. This function will only validate the length is\n// correct and that the document ends with a null byte.\nfunc NewDocumentFromReader(r io.Reader) (Document, error) {\n\treturn newBufferFromReader(r)\n}\n\nfunc newBufferFromReader(r io.Reader) ([]byte, error) {\n\tif r == nil {\n\t\treturn nil, ErrNilReader\n\t}\n\n\tvar lengthBytes [4]byte\n\n\t// ReadFull guarantees that we will have read at least len(lengthBytes) if err == nil\n\t_, err := io.ReadFull(r, lengthBytes[:])\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tlength, _, _ := binaryutil.ReadI32(lengthBytes[:]) // ignore ok since we always have enough bytes to read a length\n\tif length < 0 {\n\t\treturn nil, ErrInvalidLength\n\t}\n\tbuffer := make([]byte, length)\n\n\tcopy(buffer, lengthBytes[:])\n\n\t_, err = io.ReadFull(r, buffer[4:])\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif buffer[length-1] != 0x00 {\n\t\treturn nil, ErrMissingNull\n\t}\n\n\treturn buffer, nil\n}\n\n// Lookup searches the document, potentially recursively, for the given key. If there are multiple\n// keys provided, this method will recurse down, as long as the top and intermediate nodes are\n// either documents or arrays. If an error occurs or if the value doesn't exist, an empty Value is\n// returned.\nfunc (d Document) Lookup(key ...string) Value {\n\tval, _ := d.LookupErr(key...)\n\treturn val\n}\n\n// LookupErr is the same as Lookup, except it returns an error in addition to an empty Value.\nfunc (d Document) LookupErr(key ...string) (Value, error) {\n\tif len(key) < 1 {\n\t\treturn Value{}, ErrEmptyKey\n\t}\n\tlength, rem, ok := ReadLength(d)\n\tif !ok {\n\t\treturn Value{}, NewInsufficientBytesError(d, rem)\n\t}\n\n\tlength -= 4\n\n\tvar elem Element\n\tfor length > 1 {\n\t\telem, rem, ok = ReadElement(rem)\n\t\tlength -= int32(len(elem))\n\t\tif !ok {\n\t\t\treturn Value{}, NewInsufficientBytesError(d, rem)\n\t\t}\n\t\t// We use `KeyBytes` rather than `Key` to avoid a needless string alloc.\n\t\tif string(elem.KeyBytes()) != key[0] {\n\t\t\tcontinue\n\t\t}\n\t\tif len(key) > 1 {\n\t\t\ttt := Type(elem[0])\n\t\t\tswitch tt {\n\t\t\tcase TypeEmbeddedDocument:\n\t\t\t\tval, err := elem.Value().Document().LookupErr(key[1:]...)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn Value{}, err\n\t\t\t\t}\n\t\t\t\treturn val, nil\n\t\t\tcase TypeArray:\n\t\t\t\t// Convert to Document to continue Lookup recursion.\n\t\t\t\tval, err := Document(elem.Value().Array()).LookupErr(key[1:]...)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn Value{}, err\n\t\t\t\t}\n\t\t\t\treturn val, nil\n\t\t\tdefault:\n\t\t\t\treturn Value{}, InvalidDepthTraversalError{Key: elem.Key(), Type: tt}\n\t\t\t}\n\t\t}\n\t\treturn elem.ValueErr()\n\t}\n\treturn Value{}, ErrElementNotFound\n}\n\n// Index searches for and retrieves the element at the given index. This method will panic if\n// the document is invalid or if the index is out of bounds.\nfunc (d Document) Index(index uint) Element {\n\telem, err := d.IndexErr(index)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treturn elem\n}\n\n// IndexErr searches for and retrieves the element at the given index.\nfunc (d Document) IndexErr(index uint) (Element, error) {\n\treturn indexErr(d, index)\n}\n\nfunc indexErr(b []byte, index uint) (Element, error) {\n\tlength, rem, ok := ReadLength(b)\n\tif !ok {\n\t\treturn nil, NewInsufficientBytesError(b, rem)\n\t}\n\n\tlength -= 4\n\n\tvar current uint\n\tvar elem Element\n\tfor length > 1 {\n\t\telem, rem, ok = ReadElement(rem)\n\t\tlength -= int32(len(elem))\n\t\tif !ok {\n\t\t\treturn nil, NewInsufficientBytesError(b, rem)\n\t\t}\n\t\tif current != index {\n\t\t\tcurrent++\n\t\t\tcontinue\n\t\t}\n\t\treturn elem, nil\n\t}\n\treturn nil, ErrOutOfBounds\n}\n\n// DebugString outputs a human readable version of Document. It will attempt to stringify the\n// valid components of the document even if the entire document is not valid.\nfunc (d Document) DebugString() string {\n\tif len(d) < 5 {\n\t\treturn \"<malformed>\"\n\t}\n\tvar buf strings.Builder\n\tbuf.WriteString(\"Document\")\n\tlength, rem, _ := ReadLength(d) // We know we have enough bytes to read the length\n\tbuf.WriteByte('(')\n\tbuf.WriteString(strconv.Itoa(int(length)))\n\tlength -= 4\n\tbuf.WriteString(\"){\")\n\tvar elem Element\n\tvar ok bool\n\tfor length > 1 {\n\t\telem, rem, ok = ReadElement(rem)\n\t\tlength -= int32(len(elem))\n\t\tif !ok {\n\t\t\tbuf.WriteString(fmt.Sprintf(\"<malformed (%d)>\", length))\n\t\t\tbreak\n\t\t}\n\t\tbuf.WriteString(elem.DebugString())\n\t}\n\tbuf.WriteByte('}')\n\n\treturn buf.String()\n}\n\n// String outputs an ExtendedJSON version of Document. If the document is not valid, this method\n// returns an empty string.\nfunc (d Document) String() string {\n\tstr, _ := d.StringN(-1)\n\treturn str\n}\n\n// StringN stringifies a document. If N is non-negative, it will truncate the string to N bytes.\n// Otherwise, it will return the full string representation. The second return value indicates\n// whether the string was truncated or not.\nfunc (d Document) StringN(n int) (string, bool) {\n\tlength, rem, ok := ReadLength(d)\n\tif !ok || length < 5 {\n\t\treturn \"\", false\n\t}\n\tlength -= 4 // length bytes\n\tlength--    // final null byte\n\n\tif n == 0 {\n\t\treturn \"\", true\n\t}\n\n\tvar buf strings.Builder\n\tbuf.WriteByte('{')\n\n\tvar truncated bool\n\tvar elem Element\n\tvar str string\n\tfirst := true\n\tfor length > 0 && !truncated {\n\t\tneedStrLen := -1\n\t\t// Set needStrLen if n is positive, meaning we want to limit the string length.\n\t\tif n > 0 {\n\t\t\t// Stop stringifying if we reach the limit, that also ensures needStrLen is\n\t\t\t// greater than 0 if we need to limit the length.\n\t\t\tif buf.Len() >= n {\n\t\t\t\ttruncated = true\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tneedStrLen = n - buf.Len()\n\t\t}\n\n\t\t// Append a comma if this is not the first element.\n\t\tif !first {\n\t\t\tbuf.WriteByte(',')\n\t\t\t// If we are truncating, we need to account for the comma in the length.\n\t\t\tif needStrLen > 0 {\n\t\t\t\tneedStrLen--\n\t\t\t\tif needStrLen == 0 {\n\t\t\t\t\ttruncated = true\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\telem, rem, ok = ReadElement(rem)\n\t\tlength -= int32(len(elem))\n\t\t// Exit on malformed element.\n\t\tif !ok || length < 0 {\n\t\t\treturn \"\", false\n\t\t}\n\n\t\t// Delegate to StringN() on the element.\n\t\tstr, truncated = elem.StringN(needStrLen)\n\t\tbuf.WriteString(str)\n\n\t\tfirst = false\n\t}\n\n\tif n <= 0 || (buf.Len() < n && !truncated) {\n\t\tbuf.WriteByte('}')\n\t} else {\n\t\ttruncated = true\n\t}\n\n\treturn buf.String(), truncated\n}\n\n// Elements returns this document as a slice of elements. The returned slice will contain valid\n// elements. If the document is not valid, the elements up to the invalid point will be returned\n// along with an error.\nfunc (d Document) Elements() ([]Element, error) {\n\tlength, rem, ok := ReadLength(d)\n\tif !ok {\n\t\treturn nil, NewInsufficientBytesError(d, rem)\n\t}\n\n\tlength -= 4\n\n\tvar elem Element\n\tvar elems []Element\n\tfor length > 1 {\n\t\telem, rem, ok = ReadElement(rem)\n\t\tlength -= int32(len(elem))\n\t\tif !ok {\n\t\t\treturn elems, NewInsufficientBytesError(d, rem)\n\t\t}\n\t\tif err := elem.Validate(); err != nil {\n\t\t\treturn elems, err\n\t\t}\n\t\telems = append(elems, elem)\n\t}\n\treturn elems, nil\n}\n\n// Values returns this document as a slice of values. The returned slice will contain valid values.\n// If the document is not valid, the values up to the invalid point will be returned along with an\n// error.\nfunc (d Document) Values() ([]Value, error) {\n\treturn values(d)\n}\n\nfunc values(b []byte) ([]Value, error) {\n\tlength, rem, ok := ReadLength(b)\n\tif !ok {\n\t\treturn nil, NewInsufficientBytesError(b, rem)\n\t}\n\n\tlength -= 4\n\n\tvar elem Element\n\tvar vals []Value\n\tfor length > 1 {\n\t\telem, rem, ok = ReadElement(rem)\n\t\tlength -= int32(len(elem))\n\t\tif !ok {\n\t\t\treturn vals, NewInsufficientBytesError(b, rem)\n\t\t}\n\t\tif err := elem.Value().Validate(); err != nil {\n\t\t\treturn vals, err\n\t\t}\n\t\tvals = append(vals, elem.Value())\n\t}\n\treturn vals, nil\n}\n\n// Validate validates the document and ensures the elements contained within are valid.\nfunc (d Document) Validate() error {\n\tlength, rem, ok := ReadLength(d)\n\tif !ok {\n\t\treturn NewInsufficientBytesError(d, rem)\n\t}\n\tif int(length) > len(d) {\n\t\treturn NewDocumentLengthError(int(length), len(d))\n\t}\n\tif d[length-1] != 0x00 {\n\t\treturn ErrMissingNull\n\t}\n\n\tlength -= 4\n\tvar elem Element\n\n\tfor length > 1 {\n\t\telem, rem, ok = ReadElement(rem)\n\t\tlength -= int32(len(elem))\n\t\tif !ok {\n\t\t\treturn NewInsufficientBytesError(d, rem)\n\t\t}\n\t\terr := elem.Validate()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tif len(rem) < 1 || rem[0] != 0x00 {\n\t\treturn ErrMissingNull\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "x/bsonx/bsoncore/document_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bsoncore\n\nimport (\n\t\"bytes\"\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"testing\"\n\n\t\"github.com/google/go-cmp/cmp\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n)\n\nfunc ExampleDocument_Validate() {\n\tdoc := make(Document, 500)\n\tdoc[250], doc[251], doc[252], doc[253], doc[254] = 0x05, 0x00, 0x00, 0x00, 0x00\n\terr := doc[250:].Validate()\n\tfmt.Println(err)\n\n\t// Output: <nil>\n}\n\nfunc BenchmarkDocumentValidate(b *testing.B) {\n\tfor i := 0; i < b.N; i++ {\n\t\tdoc := make(Document, 500)\n\t\tdoc[250], doc[251], doc[252], doc[253], doc[254] = 0x05, 0x00, 0x00, 0x00, 0x00\n\t\t_ = doc[250:].Validate()\n\t}\n}\n\nfunc TestDocument(t *testing.T) {\n\tt.Run(\"Validate\", func(t *testing.T) {\n\t\tt.Run(\"TooShort\", func(t *testing.T) {\n\t\t\twant := NewInsufficientBytesError(nil, nil)\n\t\t\tgot := Document{'\\x00', '\\x00'}.Validate()\n\t\t\tif !compareErrors(got, want) {\n\t\t\t\tt.Errorf(\"Did not get expected error. got %v; want %v\", got, want)\n\t\t\t}\n\t\t})\n\t\tt.Run(\"InvalidLength\", func(t *testing.T) {\n\t\t\twant := NewDocumentLengthError(200, 5)\n\t\t\tr := make(Document, 5)\n\t\t\tbinary.LittleEndian.PutUint32(r[0:4], 200)\n\t\t\tgot := r.Validate()\n\t\t\tif !compareErrors(got, want) {\n\t\t\t\tt.Errorf(\"Did not get expected error. got %v; want %v\", got, want)\n\t\t\t}\n\t\t})\n\t\tt.Run(\"Invalid Element\", func(t *testing.T) {\n\t\t\twant := NewInsufficientBytesError(nil, nil)\n\t\t\tr := make(Document, 9)\n\t\t\tbinary.LittleEndian.PutUint32(r[0:4], 9)\n\t\t\tr[4], r[5], r[6], r[7], r[8] = 0x02, 'f', 'o', 'o', 0x00\n\t\t\tgot := r.Validate()\n\t\t\tif !compareErrors(got, want) {\n\t\t\t\tt.Errorf(\"Did not get expected error. got %v; want %v\", got, want)\n\t\t\t}\n\t\t})\n\t\tt.Run(\"Missing Null Terminator\", func(t *testing.T) {\n\t\t\twant := ErrMissingNull\n\t\t\tr := make(Document, 8)\n\t\t\tbinary.LittleEndian.PutUint32(r[0:4], 8)\n\t\t\tr[4], r[5], r[6], r[7] = 0x0A, 'f', 'o', 'o'\n\t\t\tgot := r.Validate()\n\t\t\tif !compareErrors(got, want) {\n\t\t\t\tt.Errorf(\"Did not get expected error. got %v; want %v\", got, want)\n\t\t\t}\n\t\t})\n\t\ttestCases := []struct {\n\t\t\tname string\n\t\t\tr    Document\n\t\t\twant error\n\t\t}{\n\t\t\t{\"null\", Document{'\\x08', '\\x00', '\\x00', '\\x00', '\\x0A', 'x', '\\x00', '\\x00'}, nil},\n\t\t\t{\n\t\t\t\t\"subdocument\",\n\t\t\t\tDocument{\n\t\t\t\t\t'\\x15', '\\x00', '\\x00', '\\x00',\n\t\t\t\t\t'\\x03',\n\t\t\t\t\t'f', 'o', 'o', '\\x00',\n\t\t\t\t\t'\\x0B', '\\x00', '\\x00', '\\x00', '\\x0A', 'a', '\\x00',\n\t\t\t\t\t'\\x0A', 'b', '\\x00', '\\x00', '\\x00',\n\t\t\t\t},\n\t\t\t\tnil,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"array\",\n\t\t\t\tDocument{\n\t\t\t\t\t'\\x15', '\\x00', '\\x00', '\\x00',\n\t\t\t\t\t'\\x04',\n\t\t\t\t\t'f', 'o', 'o', '\\x00',\n\t\t\t\t\t'\\x0B', '\\x00', '\\x00', '\\x00', '\\x0A', '1', '\\x00',\n\t\t\t\t\t'\\x0A', '2', '\\x00', '\\x00', '\\x00',\n\t\t\t\t},\n\t\t\t\tnil,\n\t\t\t},\n\t\t}\n\n\t\tfor _, tc := range testCases {\n\t\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\t\tgot := tc.r.Validate()\n\t\t\t\tif !compareErrors(got, tc.want) {\n\t\t\t\t\tt.Errorf(\"Returned error does not match. got %v; want %v\", got, tc.want)\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t})\n\tt.Run(\"Lookup\", func(t *testing.T) {\n\t\tt.Run(\"empty-key\", func(t *testing.T) {\n\t\t\trdr := Document{'\\x05', '\\x00', '\\x00', '\\x00', '\\x00'}\n\t\t\t_, err := rdr.LookupErr()\n\t\t\tif !errors.Is(err, ErrEmptyKey) {\n\t\t\t\tt.Errorf(\"Empty key lookup did not return expected result. got %v; want %v\", err, ErrEmptyKey)\n\t\t\t}\n\t\t})\n\t\tt.Run(\"corrupted-subdocument\", func(t *testing.T) {\n\t\t\trdr := Document{\n\t\t\t\t'\\x0D', '\\x00', '\\x00', '\\x00',\n\t\t\t\t'\\x03', 'x', '\\x00',\n\t\t\t\t'\\x06', '\\x00', '\\x00', '\\x00',\n\t\t\t\t'\\x01',\n\t\t\t\t'\\x00',\n\t\t\t\t'\\x00',\n\t\t\t}\n\t\t\t_, got := rdr.LookupErr(\"x\", \"y\")\n\t\t\twant := NewInsufficientBytesError(nil, nil)\n\t\t\tif !cmp.Equal(got, want) {\n\t\t\t\tt.Errorf(\"Empty key lookup did not return expected result. got %v; want %v\", got, want)\n\t\t\t}\n\t\t})\n\t\tt.Run(\"corrupted-array\", func(t *testing.T) {\n\t\t\trdr := Document{\n\t\t\t\t'\\x0D', '\\x00', '\\x00', '\\x00',\n\t\t\t\t'\\x04', 'x', '\\x00',\n\t\t\t\t'\\x06', '\\x00', '\\x00', '\\x00',\n\t\t\t\t'\\x01',\n\t\t\t\t'\\x00',\n\t\t\t\t'\\x00',\n\t\t\t}\n\t\t\t_, got := rdr.LookupErr(\"x\", \"y\")\n\t\t\twant := NewInsufficientBytesError(nil, nil)\n\t\t\tif !cmp.Equal(got, want) {\n\t\t\t\tt.Errorf(\"Empty key lookup did not return expected result. got %v; want %v\", got, want)\n\t\t\t}\n\t\t})\n\t\tt.Run(\"invalid-traversal\", func(t *testing.T) {\n\t\t\trdr := Document{'\\x08', '\\x00', '\\x00', '\\x00', '\\x0A', 'x', '\\x00', '\\x00'}\n\t\t\t_, got := rdr.LookupErr(\"x\", \"y\")\n\t\t\twant := InvalidDepthTraversalError{Key: \"x\", Type: TypeNull}\n\t\t\tif !compareErrors(got, want) {\n\t\t\t\tt.Errorf(\"Empty key lookup did not return expected result. got %v; want %v\", got, want)\n\t\t\t}\n\t\t})\n\t\ttestCases := []struct {\n\t\t\tname string\n\t\t\tr    Document\n\t\t\tkey  []string\n\t\t\twant Value\n\t\t\terr  error\n\t\t}{\n\t\t\t{\n\t\t\t\t\"first\",\n\t\t\t\tDocument{\n\t\t\t\t\t'\\x08', '\\x00', '\\x00', '\\x00', '\\x0A', 'x', '\\x00', '\\x00',\n\t\t\t\t},\n\t\t\t\t[]string{\"x\"},\n\t\t\t\tValue{Type: TypeNull, Data: []byte{}},\n\t\t\t\tnil,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"first-second\",\n\t\t\t\tDocument{\n\t\t\t\t\t'\\x15', '\\x00', '\\x00', '\\x00',\n\t\t\t\t\t'\\x03',\n\t\t\t\t\t'f', 'o', 'o', '\\x00',\n\t\t\t\t\t'\\x0B', '\\x00', '\\x00', '\\x00', '\\x0A', 'a', '\\x00',\n\t\t\t\t\t'\\x0A', 'b', '\\x00', '\\x00', '\\x00',\n\t\t\t\t},\n\t\t\t\t[]string{\"foo\", \"b\"},\n\t\t\t\tValue{Type: TypeNull, Data: []byte{}},\n\t\t\t\tnil,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"first-second-array\",\n\t\t\t\tDocument{\n\t\t\t\t\t'\\x15', '\\x00', '\\x00', '\\x00',\n\t\t\t\t\t'\\x04',\n\t\t\t\t\t'f', 'o', 'o', '\\x00',\n\t\t\t\t\t'\\x0B', '\\x00', '\\x00', '\\x00', '\\x0A', '1', '\\x00',\n\t\t\t\t\t'\\x0A', '2', '\\x00', '\\x00', '\\x00',\n\t\t\t\t},\n\t\t\t\t[]string{\"foo\", \"2\"},\n\t\t\t\tValue{Type: TypeNull, Data: []byte{}},\n\t\t\t\tnil,\n\t\t\t},\n\t\t}\n\n\t\tfor _, tc := range testCases {\n\t\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\t\tt.Run(\"Lookup\", func(t *testing.T) {\n\t\t\t\t\tgot := tc.r.Lookup(tc.key...)\n\t\t\t\t\tif !cmp.Equal(got, tc.want) {\n\t\t\t\t\t\tt.Errorf(\"Returned value does not match expected element. got %v; want %v\", got, tc.want)\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t\tt.Run(\"LookupErr\", func(t *testing.T) {\n\t\t\t\t\tgot, err := tc.r.LookupErr(tc.key...)\n\t\t\t\t\tif !errors.Is(err, tc.err) {\n\t\t\t\t\t\tt.Errorf(\"Returned error does not match. got %v; want %v\", err, tc.err)\n\t\t\t\t\t}\n\t\t\t\t\tif !cmp.Equal(got, tc.want) {\n\t\t\t\t\t\tt.Errorf(\"Returned value does not match expected element. got %v; want %v\", got, tc.want)\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t})\n\t\t}\n\t})\n\tt.Run(\"Index\", func(t *testing.T) {\n\t\tt.Run(\"Out of bounds\", func(t *testing.T) {\n\t\t\trdr := Document{0xe, 0x0, 0x0, 0x0, 0xa, 0x78, 0x0, 0xa, 0x79, 0x0, 0xa, 0x7a, 0x0, 0x0}\n\t\t\t_, err := rdr.IndexErr(3)\n\t\t\tif !errors.Is(err, ErrOutOfBounds) {\n\t\t\t\tt.Errorf(\"Out of bounds should be returned when accessing element beyond end of document. got %v; want %v\", err, ErrOutOfBounds)\n\t\t\t}\n\t\t})\n\t\tt.Run(\"Validation Error\", func(t *testing.T) {\n\t\t\trdr := Document{0x07, 0x00, 0x00, 0x00, 0x00}\n\t\t\t_, got := rdr.IndexErr(1)\n\t\t\twant := NewInsufficientBytesError(nil, nil)\n\t\t\tif !compareErrors(got, want) {\n\t\t\t\tt.Errorf(\"Did not receive expected error. got %v; want %v\", got, want)\n\t\t\t}\n\t\t})\n\t\ttestCases := []struct {\n\t\t\tname  string\n\t\t\trdr   Document\n\t\t\tindex uint\n\t\t\twant  Element\n\t\t}{\n\t\t\t{\n\t\t\t\t\"first\",\n\t\t\t\tDocument{0xe, 0x0, 0x0, 0x0, 0xa, 0x78, 0x0, 0xa, 0x79, 0x0, 0xa, 0x7a, 0x0, 0x0},\n\t\t\t\t0,\n\t\t\t\tElement{0x0a, 0x78, 0x00},\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"second\",\n\t\t\t\tDocument{0xe, 0x0, 0x0, 0x0, 0xa, 0x78, 0x0, 0xa, 0x79, 0x0, 0xa, 0x7a, 0x0, 0x0},\n\t\t\t\t1,\n\t\t\t\tElement{0x0a, 0x79, 0x00},\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"third\",\n\t\t\t\tDocument{0xe, 0x0, 0x0, 0x0, 0xa, 0x78, 0x0, 0xa, 0x79, 0x0, 0xa, 0x7a, 0x0, 0x0},\n\t\t\t\t2,\n\t\t\t\tElement{0x0a, 0x7a, 0x00},\n\t\t\t},\n\t\t}\n\n\t\tfor _, tc := range testCases {\n\t\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\t\tt.Run(\"IndexErr\", func(t *testing.T) {\n\t\t\t\t\tgot, err := tc.rdr.IndexErr(tc.index)\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\tt.Errorf(\"Unexpected error from IndexErr: %s\", err)\n\t\t\t\t\t}\n\t\t\t\t\tif diff := cmp.Diff(got, tc.want); diff != \"\" {\n\t\t\t\t\t\tt.Errorf(\"Documents differ: (-got +want)\\n%s\", diff)\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t\tt.Run(\"Index\", func(t *testing.T) {\n\t\t\t\t\tdefer func() {\n\t\t\t\t\t\tif err := recover(); err != nil {\n\t\t\t\t\t\t\tt.Errorf(\"Unexpected error: %v\", err)\n\t\t\t\t\t\t}\n\t\t\t\t\t}()\n\t\t\t\t\tgot := tc.rdr.Index(tc.index)\n\t\t\t\t\tif diff := cmp.Diff(got, tc.want); diff != \"\" {\n\t\t\t\t\t\tt.Errorf(\"Documents differ: (-got +want)\\n%s\", diff)\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t})\n\t\t}\n\t})\n\tt.Run(\"NewDocumentFromReader\", func(t *testing.T) {\n\t\ttestCases := []struct {\n\t\t\tname     string\n\t\t\tioReader io.Reader\n\t\t\tdoc      Document\n\t\t\terr      error\n\t\t}{\n\t\t\t{\n\t\t\t\t\"nil reader\",\n\t\t\t\tnil,\n\t\t\t\tnil,\n\t\t\t\tErrNilReader,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"premature end of reader\",\n\t\t\t\tbytes.NewBuffer([]byte{}),\n\t\t\t\tnil,\n\t\t\t\tio.EOF,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"empty document\",\n\t\t\t\tbytes.NewBuffer([]byte{5, 0, 0, 0, 0}),\n\t\t\t\t[]byte{5, 0, 0, 0, 0},\n\t\t\t\tnil,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"non-empty document\",\n\t\t\t\tbytes.NewBuffer([]byte{\n\t\t\t\t\t// length\n\t\t\t\t\t0x17, 0x0, 0x0, 0x0,\n\n\t\t\t\t\t// type - string\n\t\t\t\t\t0x2,\n\t\t\t\t\t// key - \"foo\"\n\t\t\t\t\t0x66, 0x6f, 0x6f, 0x0,\n\t\t\t\t\t// value - string length\n\t\t\t\t\t0x4, 0x0, 0x0, 0x0,\n\t\t\t\t\t// value - string \"bar\"\n\t\t\t\t\t0x62, 0x61, 0x72, 0x0,\n\n\t\t\t\t\t// type - null\n\t\t\t\t\t0xa,\n\t\t\t\t\t// key - \"baz\"\n\t\t\t\t\t0x62, 0x61, 0x7a, 0x0,\n\n\t\t\t\t\t// null terminator\n\t\t\t\t\t0x0,\n\t\t\t\t}),\n\t\t\t\t[]byte{\n\t\t\t\t\t// length\n\t\t\t\t\t0x17, 0x0, 0x0, 0x0,\n\n\t\t\t\t\t// type - string\n\t\t\t\t\t0x2,\n\t\t\t\t\t// key - \"foo\"\n\t\t\t\t\t0x66, 0x6f, 0x6f, 0x0,\n\t\t\t\t\t// value - string length\n\t\t\t\t\t0x4, 0x0, 0x0, 0x0,\n\t\t\t\t\t// value - string \"bar\"\n\t\t\t\t\t0x62, 0x61, 0x72, 0x0,\n\n\t\t\t\t\t// type - null\n\t\t\t\t\t0xa,\n\t\t\t\t\t// key - \"baz\"\n\t\t\t\t\t0x62, 0x61, 0x7a, 0x0,\n\n\t\t\t\t\t// null terminator\n\t\t\t\t\t0x0,\n\t\t\t\t},\n\t\t\t\tnil,\n\t\t\t},\n\t\t}\n\n\t\tfor _, tc := range testCases {\n\t\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\t\tdoc, err := NewDocumentFromReader(tc.ioReader)\n\t\t\t\tif !compareErrors(err, tc.err) {\n\t\t\t\t\tt.Errorf(\"errors do not match. got %v; want %v\", err, tc.err)\n\t\t\t\t}\n\t\t\t\tif !bytes.Equal(tc.doc, doc) {\n\t\t\t\t\tt.Errorf(\"documents differ. got %v; want %v\", tc.doc, doc)\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t})\n\tt.Run(\"Elements\", func(t *testing.T) {\n\t\tinvalidElem := BuildDocument(nil, AppendHeader(nil, TypeDouble, \"foo\"))\n\t\tinvalidTwoElem := BuildDocument(nil,\n\t\t\tAppendHeader(\n\t\t\t\tAppendDoubleElement(nil, \"pi\", 3.14159),\n\t\t\t\tTypeDouble, \"foo\",\n\t\t\t),\n\t\t)\n\t\toneElem := BuildDocument(nil, AppendDoubleElement(nil, \"pi\", 3.14159))\n\t\ttwoElems := BuildDocument(nil,\n\t\t\tAppendStringElement(\n\t\t\t\tAppendDoubleElement(nil, \"pi\", 3.14159),\n\t\t\t\t\"hello\", \"world!\",\n\t\t\t),\n\t\t)\n\t\ttestCases := []struct {\n\t\t\tname  string\n\t\t\tdoc   Document\n\t\t\telems []Element\n\t\t\terr   error\n\t\t}{\n\t\t\t{\"Insufficient Bytes Length\", Document{0x03, 0x00, 0x00}, nil, NewInsufficientBytesError(nil, nil)},\n\t\t\t{\"Insufficient Bytes First Element\", invalidElem, nil, NewInsufficientBytesError(nil, nil)},\n\t\t\t{\"Insufficient Bytes Second Element\", invalidTwoElem, []Element{AppendDoubleElement(nil, \"pi\", 3.14159)}, NewInsufficientBytesError(nil, nil)},\n\t\t\t{\"Success One Element\", oneElem, []Element{AppendDoubleElement(nil, \"pi\", 3.14159)}, nil},\n\t\t\t{\"Success Two Elements\", twoElems, []Element{AppendDoubleElement(nil, \"pi\", 3.14159), AppendStringElement(nil, \"hello\", \"world!\")}, nil},\n\t\t}\n\n\t\tfor _, tc := range testCases {\n\t\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\t\telems, err := tc.doc.Elements()\n\t\t\t\tif !compareErrors(err, tc.err) {\n\t\t\t\t\tt.Errorf(\"errors do not match. got %v; want %v\", err, tc.err)\n\t\t\t\t}\n\t\t\t\tif len(elems) != len(tc.elems) {\n\t\t\t\t\tt.Fatalf(\"number of elements returned does not match. got %d; want %d\", len(elems), len(tc.elems))\n\t\t\t\t}\n\n\t\t\t\tfor idx := range elems {\n\t\t\t\t\tgot, want := elems[idx], tc.elems[idx]\n\t\t\t\t\tif !bytes.Equal(got, want) {\n\t\t\t\t\t\tt.Errorf(\"Elements at index %d differ. got %v; want %v\", idx, got.DebugString(), want.DebugString())\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t})\n}\n\nvar documentStringTestCases = []struct {\n\tdescription string\n\tdoc         Document\n\twant        string\n}{\n\t{\n\t\tdescription: \"empty document\",\n\t\tdoc:         BuildDocument(nil),\n\t\twant:        `{}`,\n\t},\n\t{\n\t\tdescription: \"document with 1 field\",\n\t\tdoc: BuildDocument(nil,\n\t\t\tAppendInt32Element(nil, \"number\", 123),\n\t\t),\n\t\twant: `{\"number\": {\"$numberInt\":\"123\"}}`,\n\t},\n\t{\n\t\tdescription: \"nested documents\",\n\t\tdoc: BuildDocument(nil,\n\t\t\tAppendDocumentElement(nil, \"key\", BuildDocument(nil,\n\t\t\t\tAppendStringElement(nil, \"nestedKey\", \"abc\"),\n\t\t\t)),\n\t\t),\n\t\twant: `{\"key\": {\"nestedKey\": \"abc\"}}`,\n\t},\n\t{\n\t\tdescription: \"document with mixed types\",\n\t\tdoc: BuildDocument(nil,\n\t\t\tAppendStringElement(nil, \"key\", \"abc\"),\n\t\t\tAppendInt32Element(nil, \"number\", 123),\n\t\t\tAppendBooleanElement(nil, \"flag\", true),\n\t\t),\n\t\twant: `{\"key\": \"abc\",\"number\": {\"$numberInt\":\"123\"},\"flag\": true}`,\n\t},\n}\n\nfunc TestDocument_String(t *testing.T) {\n\tfor _, tc := range documentStringTestCases {\n\t\tt.Run(tc.description, func(t *testing.T) {\n\t\t\tgot := tc.doc.String()\n\t\t\tassert.Equal(t, tc.want, got, \"expected string %s, got %s\", tc.want, got)\n\t\t})\n\t}\n}\n\nfunc TestDocument_StringN(t *testing.T) {\n\tfor _, tc := range documentStringTestCases {\n\t\tfor n := -1; n <= len(tc.want)+1; n++ {\n\t\t\tt.Run(fmt.Sprintf(\"%s n==%d\", tc.description, n), func(t *testing.T) {\n\t\t\t\tgot, truncated := tc.doc.StringN(n)\n\t\t\t\tl := n\n\t\t\t\ttoBeTruncated := true\n\t\t\t\tif l >= len(tc.want) || l < 0 {\n\t\t\t\t\tl = len(tc.want)\n\t\t\t\t\ttoBeTruncated = false\n\t\t\t\t}\n\t\t\t\twant := tc.want[:l]\n\t\t\t\tassert.Equal(t, want, got, \"expected truncated string %s, got %s\", want, got)\n\t\t\t\tassert.Equal(t, toBeTruncated, truncated, \"expected truncated to be %t, got %t\", toBeTruncated, truncated)\n\t\t\t})\n\t\t}\n\t}\n}\n\nfunc TestDocument_StringN_Multibyte(t *testing.T) {\n\tmultiByteString := Document(BuildDocument(nil,\n\t\tAppendStringElement(nil, \"𨉟呐㗂越\", \"abc\"),\n\t))\n\tfor i, tc := range []struct {\n\t\tn    int\n\t\twant string\n\t}{\n\t\t{-1, `{\"𨉟呐㗂越\": \"abc\"}`},\n\t\t{0, ``},\n\t\t{1, `{`},\n\t\t{2, `{\"`},\n\t\t{3, `{\"`},\n\t\t{4, `{\"`},\n\t\t{5, `{\"`},\n\t\t{6, `{\"`},\n\t\t{7, `{\"𨉟`},\n\t\t{8, `{\"𨉟`},\n\t\t{9, `{\"𨉟`},\n\t\t{10, `{\"𨉟呐`},\n\t\t{14, `{\"𨉟呐㗂`},\n\t\t{15, `{\"𨉟呐㗂越`},\n\t\t{16, `{\"𨉟呐㗂越\"`},\n\t\t{17, `{\"𨉟呐㗂越\":`},\n\t\t{18, `{\"𨉟呐㗂越\": `},\n\t\t{19, `{\"𨉟呐㗂越\": \"`},\n\t\t{20, `{\"𨉟呐㗂越\": \"a`},\n\t} {\n\t\tt.Run(fmt.Sprintf(\"case %d\", i), func(t *testing.T) {\n\t\t\tgot, truncated := multiByteString.StringN(tc.n)\n\t\t\tassert.Equal(t, tc.want, got, \"expected truncated string %s, got %s\", tc.want, got)\n\t\t\tassert.Equal(t, tc.n != -1, truncated, \"expected truncated to be %t, got %t\", tc.n != -1, truncated)\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "x/bsonx/bsoncore/element.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bsoncore\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"strings\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/bsoncoreutil\"\n)\n\n// MalformedElementError represents a class of errors that RawElement methods return.\ntype MalformedElementError string\n\nfunc (mee MalformedElementError) Error() string { return string(mee) }\n\n// ErrElementMissingKey is returned when a RawElement is missing a key.\nconst ErrElementMissingKey MalformedElementError = \"element is missing key\"\n\n// ErrElementMissingType is returned when a RawElement is missing a type.\nconst ErrElementMissingType MalformedElementError = \"element is missing type\"\n\n// Element is a raw bytes representation of a BSON element.\ntype Element []byte\n\n// Key returns the key for this element. If the element is not valid, this method returns an empty\n// string. If knowing if the element is valid is important, use KeyErr.\nfunc (e Element) Key() string {\n\tkey, _ := e.KeyErr()\n\treturn key\n}\n\n// KeyBytes returns the key for this element as a []byte. If the element is not valid, this method\n// returns an empty string. If knowing if the element is valid is important, use KeyErr. This method\n// will not include the null byte at the end of the key in the slice of bytes.\nfunc (e Element) KeyBytes() []byte {\n\tkey, _ := e.KeyBytesErr()\n\treturn key\n}\n\n// KeyErr returns the key for this element, returning an error if the element is not valid.\nfunc (e Element) KeyErr() (string, error) {\n\tkey, err := e.KeyBytesErr()\n\treturn string(key), err\n}\n\n// KeyBytesErr returns the key for this element as a []byte, returning an error if the element is\n// not valid.\nfunc (e Element) KeyBytesErr() ([]byte, error) {\n\tif len(e) == 0 {\n\t\treturn nil, ErrElementMissingType\n\t}\n\tidx := bytes.IndexByte(e[1:], 0x00)\n\tif idx == -1 {\n\t\treturn nil, ErrElementMissingKey\n\t}\n\treturn e[1 : idx+1], nil\n}\n\n// Validate ensures the element is a valid BSON element.\nfunc (e Element) Validate() error {\n\tif len(e) < 1 {\n\t\treturn ErrElementMissingType\n\t}\n\tidx := bytes.IndexByte(e[1:], 0x00)\n\tif idx == -1 {\n\t\treturn ErrElementMissingKey\n\t}\n\treturn Value{Type: Type(e[0]), Data: e[idx+2:]}.Validate()\n}\n\n// CompareKey will compare this element's key to key. This method makes it easy to compare keys\n// without needing to allocate a string. The key may be null terminated. If a valid key cannot be\n// read this method will return false.\nfunc (e Element) CompareKey(key []byte) bool {\n\tif len(e) < 2 {\n\t\treturn false\n\t}\n\tidx := bytes.IndexByte(e[1:], 0x00)\n\tif idx == -1 {\n\t\treturn false\n\t}\n\tif index := bytes.IndexByte(key, 0x00); index > -1 {\n\t\tkey = key[:index]\n\t}\n\treturn bytes.Equal(e[1:idx+1], key)\n}\n\n// Value returns the value of this element. If the element is not valid, this method returns an\n// empty Value. If knowing if the element is valid is important, use ValueErr.\nfunc (e Element) Value() Value {\n\tval, _ := e.ValueErr()\n\treturn val\n}\n\n// ValueErr returns the value for this element, returning an error if the element is not valid.\nfunc (e Element) ValueErr() (Value, error) {\n\tif len(e) == 0 {\n\t\treturn Value{}, ErrElementMissingType\n\t}\n\tidx := bytes.IndexByte(e[1:], 0x00)\n\tif idx == -1 {\n\t\treturn Value{}, ErrElementMissingKey\n\t}\n\n\tval, rem, exists := ReadValue(e[idx+2:], Type(e[0]))\n\tif !exists {\n\t\treturn Value{}, NewInsufficientBytesError(e, rem)\n\t}\n\treturn val, nil\n}\n\n// String implements the fmt.String interface. The output will be in extended JSON format.\nfunc (e Element) String() string {\n\tstr, _ := e.StringN(-1)\n\treturn str\n}\n\n// StringN will return values in extended JSON format that will stringify an element upto N bytes.\n// If N is non-negative, it will truncate the string to N bytes. Otherwise, it will return the full\n// string representation. The second return value indicates whether the string was truncated or not.\n// If the element is not valid, this returns an empty string\nfunc (e Element) StringN(n int) (string, bool) {\n\tif len(e) == 0 {\n\t\treturn \"\", false\n\t}\n\tif n == 0 {\n\t\treturn \"\", true\n\t}\n\tif n == 1 {\n\t\treturn `\"`, true\n\t}\n\n\tt := Type(e[0])\n\tidx := bytes.IndexByte(e[1:], 0x00)\n\tif idx <= 0 {\n\t\treturn \"\", false\n\t}\n\tkey := e[1 : idx+1]\n\n\tvar buf strings.Builder\n\tbuf.WriteByte('\"')\n\tconst suffix = `\": `\n\tswitch {\n\tcase n < 0 || idx <= n-buf.Len()-len(suffix):\n\t\tbuf.Write(key)\n\t\tbuf.WriteString(suffix)\n\tcase idx < n:\n\t\tbuf.Write(key)\n\t\tbuf.WriteString(suffix[:n-idx-1])\n\t\treturn buf.String(), true\n\tdefault:\n\t\tbuf.WriteString(bsoncoreutil.Truncate(string(key), n-1))\n\t\treturn buf.String(), true\n\t}\n\n\tneedStrLen := -1\n\t// Set needStrLen if n is positive, meaning we want to limit the string length.\n\tif n > 0 {\n\t\t// Stop stringifying if we reach the limit, that also ensures needStrLen is\n\t\t// greater than 0 if we need to limit the length.\n\t\tif buf.Len() >= n {\n\t\t\treturn buf.String(), true\n\t\t}\n\t\tneedStrLen = n - buf.Len()\n\t}\n\n\tval, _, valid := ReadValue(e[idx+2:], t)\n\tif !valid {\n\t\treturn \"\", false\n\t}\n\n\tvar str string\n\tvar truncated bool\n\tif _, ok := val.StringValueOK(); ok {\n\t\tstr, truncated = val.StringN(needStrLen)\n\t} else if arr, ok := val.ArrayOK(); ok {\n\t\tstr, truncated = arr.StringN(needStrLen)\n\t} else {\n\t\tstr = val.String()\n\t\tif needStrLen > 0 && len(str) > needStrLen {\n\t\t\ttruncated = true\n\t\t\tstr = bsoncoreutil.Truncate(str, needStrLen)\n\t\t}\n\t}\n\n\tbuf.WriteString(str)\n\treturn buf.String(), truncated\n}\n\n// DebugString outputs a human readable version of RawElement. It will attempt to stringify the\n// valid components of the element even if the entire element is not valid.\nfunc (e Element) DebugString() string {\n\tif len(e) == 0 {\n\t\treturn \"<malformed>\"\n\t}\n\tt := Type(e[0])\n\tidx := bytes.IndexByte(e[1:], 0x00)\n\tif idx == -1 {\n\t\treturn fmt.Sprintf(`bson.Element{[%s]<malformed>}`, t)\n\t}\n\tkey, valBytes := []byte(e[1:idx+1]), []byte(e[idx+2:])\n\tval, _, valid := ReadValue(valBytes, t)\n\tif !valid {\n\t\treturn fmt.Sprintf(`bson.Element{[%s]\"%s\": <malformed>}`, t, key)\n\t}\n\treturn fmt.Sprintf(`bson.Element{[%s]\"%s\": %v}`, t, key, val)\n}\n"
  },
  {
    "path": "x/bsonx/bsoncore/element_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bsoncore\n\nimport (\n\t\"testing\"\n\n\t\"github.com/google/go-cmp/cmp\"\n)\n\nfunc TestElement(t *testing.T) {\n\tt.Run(\"KeyErr\", func(t *testing.T) {\n\t\ttestCases := []struct {\n\t\t\tname string\n\t\t\telem Element\n\t\t\tstr  string\n\t\t\terr  error\n\t\t}{\n\t\t\t{\"No Type\", Element{}, \"\", ErrElementMissingType},\n\t\t\t{\"No Key\", Element{0x01, 'f', 'o', 'o'}, \"\", ErrElementMissingKey},\n\t\t\t{\"Success\", AppendHeader(nil, TypeDouble, \"foo\"), \"foo\", nil},\n\t\t}\n\n\t\tfor _, tc := range testCases {\n\t\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\t\tt.Run(\"Key\", func(t *testing.T) {\n\t\t\t\t\tstr := tc.elem.Key()\n\t\t\t\t\tif str != tc.str {\n\t\t\t\t\t\tt.Errorf(\"returned strings do not match. got %s; want %s\", str, tc.str)\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t\tt.Run(\"KeyErr\", func(t *testing.T) {\n\t\t\t\t\tstr, err := tc.elem.KeyErr()\n\t\t\t\t\tif !compareErrors(err, tc.err) {\n\t\t\t\t\t\tt.Errorf(\"errors do not match. got %v; want %v\", err, tc.err)\n\t\t\t\t\t}\n\t\t\t\t\tif str != tc.str {\n\t\t\t\t\t\tt.Errorf(\"returned strings do not match. got %s; want %s\", str, tc.str)\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t})\n\t\t}\n\t})\n\tt.Run(\"Validate\", func(t *testing.T) {\n\t\ttestCases := []struct {\n\t\t\tname string\n\t\t\telem Element\n\t\t\terr  error\n\t\t}{\n\t\t\t{\"No Type\", Element{}, ErrElementMissingType},\n\t\t\t{\"No Key\", Element{0x01, 'f', 'o', 'o'}, ErrElementMissingKey},\n\t\t\t{\"Insufficient Bytes\", AppendHeader(nil, TypeDouble, \"foo\"), NewInsufficientBytesError(nil, nil)},\n\t\t\t{\"Success\", AppendDoubleElement(nil, \"foo\", 3.14159), nil},\n\t\t}\n\n\t\tfor _, tc := range testCases {\n\t\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\t\terr := tc.elem.Validate()\n\t\t\t\tif !compareErrors(err, tc.err) {\n\t\t\t\t\tt.Errorf(\"errors do not match. got %v; want %v\", err, tc.err)\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t})\n\tt.Run(\"CompareKey\", func(t *testing.T) {\n\t\ttestCases := []struct {\n\t\t\tname  string\n\t\t\telem  Element\n\t\t\tkey   []byte\n\t\t\tequal bool\n\t\t}{\n\t\t\t{\"Element Too Short\", Element{0x02}, nil, false},\n\t\t\t{\"Element Invalid Key\", Element{0x02, 'f', 'o', 'o'}, nil, false},\n\t\t\t{\"Key With Null Byte\", AppendHeader(nil, TypeDouble, \"foo\"), []byte{'f', 'o', 'o', 0x00}, true},\n\t\t\t{\"Key Without Null Byte\", AppendHeader(nil, TypeDouble, \"pi\"), []byte{'p', 'i'}, true},\n\t\t\t{\"Key With Null Byte With Extra\", AppendHeader(nil, TypeDouble, \"foo\"), []byte{'f', 'o', 'o', 0x00, 'b', 'a', 'r'}, true},\n\t\t\t{\"Prefix Key No Match\", AppendHeader(nil, TypeDouble, \"foo\"), []byte{'f', 'o', 'o', 'b', 'a', 'r'}, false},\n\t\t}\n\n\t\tfor _, tc := range testCases {\n\t\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\t\tequal := tc.elem.CompareKey(tc.key)\n\t\t\t\tif equal != tc.equal {\n\t\t\t\t\tt.Errorf(\"Did not get expected equality result. got %t; want %t\", equal, tc.equal)\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t})\n\tt.Run(\"Value & ValueErr\", func(t *testing.T) {\n\t\ttestCases := []struct {\n\t\t\tname string\n\t\t\telem Element\n\t\t\tval  Value\n\t\t\terr  error\n\t\t}{\n\t\t\t{\"No Type\", Element{}, Value{}, ErrElementMissingType},\n\t\t\t{\"No Key\", Element{0x01, 'f', 'o', 'o'}, Value{}, ErrElementMissingKey},\n\t\t\t{\"Insufficient Bytes\", AppendHeader(nil, TypeDouble, \"foo\"), Value{}, NewInsufficientBytesError(nil, nil)},\n\t\t\t{\"Success\", AppendDoubleElement(nil, \"foo\", 3.14159), Value{Type: TypeDouble, Data: AppendDouble(nil, 3.14159)}, nil},\n\t\t}\n\n\t\tfor _, tc := range testCases {\n\t\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\t\tt.Run(\"Value\", func(t *testing.T) {\n\t\t\t\t\tval := tc.elem.Value()\n\t\t\t\t\tif !cmp.Equal(val, tc.val) {\n\t\t\t\t\t\tt.Errorf(\"Values do not match. got %v; want %v\", val, tc.val)\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t\tt.Run(\"ValueErr\", func(t *testing.T) {\n\t\t\t\t\tval, err := tc.elem.ValueErr()\n\t\t\t\t\tif !compareErrors(err, tc.err) {\n\t\t\t\t\t\tt.Errorf(\"errors do not match. got %v; want %v\", err, tc.err)\n\t\t\t\t\t}\n\t\t\t\t\tif !cmp.Equal(val, tc.val) {\n\t\t\t\t\t\tt.Errorf(\"Values do not match. got %v; want %v\", val, tc.val)\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t})\n\t\t}\n\t})\n}\n"
  },
  {
    "path": "x/bsonx/bsoncore/iterator.go",
    "content": "// Copyright (C) MongoDB, Inc. 2022-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bsoncore\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n)\n\n// errCorruptedDocument is returned when a full document couldn't be read from\n// the sequence.\nvar errCorruptedDocument = errors.New(\"invalid DocumentSequence: corrupted document\")\n\n// Iterator maintains a list of BSON values and keeps track of the current\n// position in relation to its Next() method.\ntype Iterator struct {\n\tList Array // List of BSON values\n\tpos  int   // The position of the iterator in the list in reference to Next()\n}\n\n// Count returned the number of elements in the iterator's list.\nfunc (iter *Iterator) Count() int {\n\tif iter == nil {\n\t\treturn 0\n\t}\n\n\t_, rem, ok := ReadLength(iter.List)\n\tif !ok {\n\t\treturn 0\n\t}\n\n\tvar count int\n\tfor len(rem) > 1 {\n\t\t_, rem, ok = ReadElement(rem)\n\t\tif !ok {\n\t\t\treturn 0\n\t\t}\n\t\tcount++\n\t}\n\treturn count\n}\n\n// Empty returns true if the iterator's list is empty.\nfunc (iter *Iterator) Empty() bool {\n\treturn len(iter.List) <= 5\n}\n\n// Reset will reset the iteration point for the Next method to the beginning of\n// the list.\nfunc (iter *Iterator) Reset() {\n\titer.pos = 0\n}\n\n// Documents traverses the list as documents and returns them. This method\n// assumes that the underlying list is composed of documents and will return\n// an error otherwise.\nfunc (iter *Iterator) Documents() ([]Document, error) {\n\tif iter == nil || len(iter.List) == 0 {\n\t\treturn nil, nil\n\t}\n\n\tvals, err := iter.List.Values()\n\tif err != nil {\n\t\treturn nil, errCorruptedDocument\n\t}\n\n\tdocs := make([]Document, 0, len(vals))\n\tfor _, v := range vals {\n\t\tif v.Type != TypeEmbeddedDocument {\n\t\t\treturn nil, fmt.Errorf(\"invalid DocumentSequence: a non-document value was found in sequence\")\n\t\t}\n\n\t\tdocs = append(docs, v.Data)\n\t}\n\n\treturn docs, nil\n}\n\n// Next retrieves the next value from the list and returns it. This method will\n// return io.EOF when it has reached the end of the list.\nfunc (iter *Iterator) Next() (*Value, error) {\n\tif iter == nil || iter.pos >= len(iter.List) {\n\t\treturn nil, io.EOF\n\t}\n\n\tif iter.pos < 4 {\n\t\tif len(iter.List) < 4 {\n\t\t\treturn nil, errCorruptedDocument\n\t\t}\n\n\t\titer.pos = 4 // Skip the length of the document\n\t}\n\n\trem := iter.List[iter.pos:]\n\tif len(rem) == 1 && rem[0] == 0x00 {\n\t\treturn nil, io.EOF // At the end of the document\n\t}\n\n\telem, _, ok := ReadElement(rem)\n\tif !ok {\n\t\treturn nil, errCorruptedDocument\n\t}\n\n\titer.pos += len(elem)\n\tval := elem.Value()\n\n\treturn &val, nil\n}\n"
  },
  {
    "path": "x/bsonx/bsoncore/iterator_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2022-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bsoncore\n\nimport (\n\t\"io\"\n\t\"testing\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/require\"\n)\n\nfunc TestIterator_Reset(t *testing.T) {\n\tt.Parallel()\n\n\ttests := []struct {\n\t\tname   string\n\t\tvalues []Value\n\t}{\n\t\t{\n\t\t\tname: \"documents\",\n\t\t\tvalues: []Value{\n\t\t\t\t{\n\t\t\t\t\tType: TypeEmbeddedDocument,\n\t\t\t\t\tData: BuildDocument(nil, AppendDoubleElement(nil, \"pi\", 3.14159)),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tType: TypeEmbeddedDocument,\n\t\t\t\t\tData: BuildDocument(nil, AppendDoubleElement(nil, \"grav\", 9.8)),\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"strings\",\n\t\t\tvalues: []Value{\n\t\t\t\t{\n\t\t\t\t\tType: TypeString,\n\t\t\t\t\tData: AppendString(nil, \"foo\"),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tType: TypeString,\n\t\t\t\t\tData: AppendString(nil, \"bar\"),\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"type mixing\",\n\t\t\tvalues: []Value{\n\t\t\t\t{\n\t\t\t\t\tType: TypeString,\n\t\t\t\t\tData: AppendString(nil, \"foo\"),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tType: TypeBoolean,\n\t\t\t\t\tData: AppendBoolean(nil, true),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tType: TypeEmbeddedDocument,\n\t\t\t\t\tData: BuildDocument(nil, AppendDoubleElement(nil, \"pi\", 3.14159)),\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n\n\tfor _, tcase := range tests {\n\t\ttcase := tcase\n\n\t\tt.Run(tcase.name, func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\t// 1. Create the iterator\n\t\t\tarray := BuildArray(nil, tcase.values...)\n\t\t\titer := &Iterator{List: array}\n\n\t\t\t// 2. Read one of the documents using Next()\n\t\t\t_, err := iter.Next()\n\t\t\tassert.NoError(t, err)\n\n\t\t\t// 3. Reset the position\n\t\t\titer.Reset()\n\n\t\t\t// 4. Assert that we get the first value when re-running Next.\n\t\t\tgot, err := iter.Next()\n\n\t\t\tassert.NoError(t, err)\n\t\t\tassert.NotNil(t, got)\n\t\t\tassert.Equal(t, tcase.values[0], *got)\n\t\t})\n\t}\n}\n\nfunc TestIterator_Count(t *testing.T) {\n\tt.Parallel()\n\n\ttests := []struct {\n\t\tname   string\n\t\tvalues []Value\n\t\twant   int\n\t}{\n\t\t{\n\t\t\tname:   \"empty\",\n\t\t\tvalues: []Value{},\n\t\t\twant:   0,\n\t\t},\n\t\t{\n\t\t\tname:   \"nil\",\n\t\t\tvalues: nil,\n\t\t\twant:   0,\n\t\t},\n\t\t{\n\t\t\tname: \"singleton\",\n\t\t\tvalues: []Value{\n\t\t\t\t{\n\t\t\t\t\tType: TypeString,\n\t\t\t\t\tData: AppendString(nil, \"foo\"),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: 1,\n\t\t},\n\t\t{\n\t\t\tname: \"non singleton\",\n\t\t\tvalues: []Value{\n\t\t\t\t{\n\t\t\t\t\tType: TypeString,\n\t\t\t\t\tData: AppendString(nil, \"foo\"),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tType: TypeString,\n\t\t\t\t\tData: AppendString(nil, \"bar\"),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: 2,\n\t\t},\n\t\t{\n\t\t\tname: \"document bearing\",\n\t\t\tvalues: []Value{\n\t\t\t\t{\n\t\t\t\t\tType: TypeEmbeddedDocument,\n\t\t\t\t\tData: BuildDocument(nil, AppendDoubleElement(nil, \"pi\", 3.14159)),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: 1,\n\t\t},\n\t\t{\n\t\t\tname: \"type mixing\",\n\t\t\tvalues: []Value{\n\t\t\t\t{\n\t\t\t\t\tType: TypeString,\n\t\t\t\t\tData: AppendString(nil, \"foo\"),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tType: TypeBoolean,\n\t\t\t\t\tData: AppendBoolean(nil, true),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tType: TypeEmbeddedDocument,\n\t\t\t\t\tData: BuildDocument(nil, AppendDoubleElement(nil, \"pi\", 3.14159)),\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: 3,\n\t\t},\n\t}\n\n\tfor _, tcase := range tests {\n\t\ttcase := tcase\n\n\t\tt.Run(tcase.name, func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tvar array Array\n\t\t\tif tcase.values != nil {\n\t\t\t\tarray = BuildArray(nil, tcase.values...)\n\t\t\t}\n\n\t\t\tgot := (&Iterator{List: array}).Count()\n\t\t\tassert.Equal(t, tcase.want, got)\n\t\t})\n\t}\n}\n\nfunc TestIterator_Next(t *testing.T) {\n\tt.Parallel()\n\n\ttests := []struct {\n\t\tname   string\n\t\tvalues []Value\n\t\terr    error\n\t}{\n\t\t{\n\t\t\tname:   \"empty\",\n\t\t\tvalues: []Value{},\n\t\t\terr:    io.EOF,\n\t\t},\n\t\t{\n\t\t\tname:   \"nil\",\n\t\t\tvalues: nil,\n\t\t\terr:    io.EOF,\n\t\t},\n\t\t{\n\t\t\tname: \"singleton\",\n\t\t\tvalues: []Value{\n\t\t\t\t{\n\t\t\t\t\tType: TypeString,\n\t\t\t\t\tData: AppendString(nil, \"foo\"),\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"document bearing\",\n\t\t\tvalues: []Value{\n\t\t\t\t{\n\t\t\t\t\tType: TypeEmbeddedDocument,\n\t\t\t\t\tData: BuildDocument(nil, AppendDoubleElement(nil, \"pi\", 3.14159)),\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"type mixing\",\n\t\t\tvalues: []Value{\n\t\t\t\t{\n\t\t\t\t\tType: TypeString,\n\t\t\t\t\tData: AppendString(nil, \"foo\"),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tType: TypeBoolean,\n\t\t\t\t\tData: AppendBoolean(nil, true),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tType: TypeEmbeddedDocument,\n\t\t\t\t\tData: BuildDocument(nil, AppendDoubleElement(nil, \"pi\", 3.14159)),\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n\n\tfor _, tcase := range tests {\n\t\ttcase := tcase\n\n\t\tt.Run(tcase.name, func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tvar array Array\n\t\t\tif tcase.values != nil {\n\t\t\t\tarray = BuildArray(nil, tcase.values...)\n\t\t\t}\n\n\t\t\titer := &Iterator{List: array}\n\n\t\t\tfor _, want := range tcase.values {\n\t\t\t\tgot, err := iter.Next()\n\t\t\t\trequire.NoErrorf(t, err, \"failed to parse the next value\")\n\n\t\t\t\tassert.Equal(t, want.Type, got.Type)\n\t\t\t\tassert.Equal(t, want.Data, got.Data)\n\t\t\t}\n\n\t\t\t// Make sure the last call to next results in an EOF.\n\t\t\t_, err := iter.Next()\n\t\t\tassert.ErrorIs(t, err, io.EOF)\n\t\t})\n\t}\n}\n\n// BenchmarkNext measures the performance of the Next function.\nfunc BenchmarkIterator_Next(b *testing.B) {\n\tvalues := []Value{\n\t\t{\n\t\t\tType: TypeDouble,\n\t\t\tData: AppendDouble(nil, 3.14159),\n\t\t},\n\t\t{\n\t\t\tType: TypeString,\n\t\t\tData: AppendString(nil, \"foo\"),\n\t\t},\n\t\t{\n\t\t\tType: TypeEmbeddedDocument,\n\t\t\tData: BuildDocument(nil, AppendDoubleElement(nil, \"pi\", 3.14159)),\n\t\t},\n\t\t{\n\t\t\tType: TypeBoolean,\n\t\t\tData: AppendBoolean(nil, true),\n\t\t},\n\t}\n\n\titer := &Iterator{}\n\titer.List = BuildArray(nil, values...)\n\n\tb.ReportAllocs()\n\tb.ResetTimer()\n\n\tfor i := 0; i < b.N; i++ {\n\t\t_, err := iter.Next()\n\t\tif err == io.EOF {\n\t\t\t// If we reach the end of the list, reset the iterator for the next iteration.\n\t\t\titer.pos = 0\n\t\t} else if err != nil {\n\t\t\tb.Fatalf(\"Unexpected error: %v\", err)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "x/bsonx/bsoncore/tables.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n//\n// Based on github.com/golang/go by The Go Authors\n// See THIRD-PARTY-NOTICES for original license terms.\n\npackage bsoncore\n\nimport \"unicode/utf8\"\n\n// safeSet holds the value true if the ASCII character with the given array\n// position can be represented inside a JSON string without any further\n// escaping.\n//\n// All values are true except for the ASCII control characters (0-31), the\n// double quote (\"), and the backslash character (\"\\\").\nvar safeSet = [utf8.RuneSelf]bool{\n\t' ':      true,\n\t'!':      true,\n\t'\"':      false,\n\t'#':      true,\n\t'$':      true,\n\t'%':      true,\n\t'&':      true,\n\t'\\'':     true,\n\t'(':      true,\n\t')':      true,\n\t'*':      true,\n\t'+':      true,\n\t',':      true,\n\t'-':      true,\n\t'.':      true,\n\t'/':      true,\n\t'0':      true,\n\t'1':      true,\n\t'2':      true,\n\t'3':      true,\n\t'4':      true,\n\t'5':      true,\n\t'6':      true,\n\t'7':      true,\n\t'8':      true,\n\t'9':      true,\n\t':':      true,\n\t';':      true,\n\t'<':      true,\n\t'=':      true,\n\t'>':      true,\n\t'?':      true,\n\t'@':      true,\n\t'A':      true,\n\t'B':      true,\n\t'C':      true,\n\t'D':      true,\n\t'E':      true,\n\t'F':      true,\n\t'G':      true,\n\t'H':      true,\n\t'I':      true,\n\t'J':      true,\n\t'K':      true,\n\t'L':      true,\n\t'M':      true,\n\t'N':      true,\n\t'O':      true,\n\t'P':      true,\n\t'Q':      true,\n\t'R':      true,\n\t'S':      true,\n\t'T':      true,\n\t'U':      true,\n\t'V':      true,\n\t'W':      true,\n\t'X':      true,\n\t'Y':      true,\n\t'Z':      true,\n\t'[':      true,\n\t'\\\\':     false,\n\t']':      true,\n\t'^':      true,\n\t'_':      true,\n\t'`':      true,\n\t'a':      true,\n\t'b':      true,\n\t'c':      true,\n\t'd':      true,\n\t'e':      true,\n\t'f':      true,\n\t'g':      true,\n\t'h':      true,\n\t'i':      true,\n\t'j':      true,\n\t'k':      true,\n\t'l':      true,\n\t'm':      true,\n\t'n':      true,\n\t'o':      true,\n\t'p':      true,\n\t'q':      true,\n\t'r':      true,\n\t's':      true,\n\t't':      true,\n\t'u':      true,\n\t'v':      true,\n\t'w':      true,\n\t'x':      true,\n\t'y':      true,\n\t'z':      true,\n\t'{':      true,\n\t'|':      true,\n\t'}':      true,\n\t'~':      true,\n\t'\\u007f': true,\n}\n\n// htmlSafeSet holds the value true if the ASCII character with the given\n// array position can be safely represented inside a JSON string, embedded\n// inside of HTML <script> tags, without any additional escaping.\n//\n// All values are true except for the ASCII control characters (0-31), the\n// double quote (\"), the backslash character (\"\\\"), HTML opening and closing\n// tags (\"<\" and \">\"), and the ampersand (\"&\").\nvar htmlSafeSet = [utf8.RuneSelf]bool{\n\t' ':      true,\n\t'!':      true,\n\t'\"':      false,\n\t'#':      true,\n\t'$':      true,\n\t'%':      true,\n\t'&':      false,\n\t'\\'':     true,\n\t'(':      true,\n\t')':      true,\n\t'*':      true,\n\t'+':      true,\n\t',':      true,\n\t'-':      true,\n\t'.':      true,\n\t'/':      true,\n\t'0':      true,\n\t'1':      true,\n\t'2':      true,\n\t'3':      true,\n\t'4':      true,\n\t'5':      true,\n\t'6':      true,\n\t'7':      true,\n\t'8':      true,\n\t'9':      true,\n\t':':      true,\n\t';':      true,\n\t'<':      false,\n\t'=':      true,\n\t'>':      false,\n\t'?':      true,\n\t'@':      true,\n\t'A':      true,\n\t'B':      true,\n\t'C':      true,\n\t'D':      true,\n\t'E':      true,\n\t'F':      true,\n\t'G':      true,\n\t'H':      true,\n\t'I':      true,\n\t'J':      true,\n\t'K':      true,\n\t'L':      true,\n\t'M':      true,\n\t'N':      true,\n\t'O':      true,\n\t'P':      true,\n\t'Q':      true,\n\t'R':      true,\n\t'S':      true,\n\t'T':      true,\n\t'U':      true,\n\t'V':      true,\n\t'W':      true,\n\t'X':      true,\n\t'Y':      true,\n\t'Z':      true,\n\t'[':      true,\n\t'\\\\':     false,\n\t']':      true,\n\t'^':      true,\n\t'_':      true,\n\t'`':      true,\n\t'a':      true,\n\t'b':      true,\n\t'c':      true,\n\t'd':      true,\n\t'e':      true,\n\t'f':      true,\n\t'g':      true,\n\t'h':      true,\n\t'i':      true,\n\t'j':      true,\n\t'k':      true,\n\t'l':      true,\n\t'm':      true,\n\t'n':      true,\n\t'o':      true,\n\t'p':      true,\n\t'q':      true,\n\t'r':      true,\n\t's':      true,\n\t't':      true,\n\t'u':      true,\n\t'v':      true,\n\t'w':      true,\n\t'x':      true,\n\t'y':      true,\n\t'z':      true,\n\t'{':      true,\n\t'|':      true,\n\t'}':      true,\n\t'~':      true,\n\t'\\u007f': true,\n}\n"
  },
  {
    "path": "x/bsonx/bsoncore/type.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bsoncore\n\n// Type represents a BSON type.\ntype Type byte\n\n// String returns the string representation of the BSON type's name.\nfunc (bt Type) String() string {\n\tswitch bt {\n\tcase '\\x01':\n\t\treturn \"double\"\n\tcase '\\x02':\n\t\treturn \"string\"\n\tcase '\\x03':\n\t\treturn \"embedded document\"\n\tcase '\\x04':\n\t\treturn \"array\"\n\tcase '\\x05':\n\t\treturn \"binary\"\n\tcase '\\x06':\n\t\treturn \"undefined\"\n\tcase '\\x07':\n\t\treturn \"objectID\"\n\tcase '\\x08':\n\t\treturn \"boolean\"\n\tcase '\\x09':\n\t\treturn \"UTC datetime\"\n\tcase '\\x0A':\n\t\treturn \"null\"\n\tcase '\\x0B':\n\t\treturn \"regex\"\n\tcase '\\x0C':\n\t\treturn \"dbPointer\"\n\tcase '\\x0D':\n\t\treturn \"javascript\"\n\tcase '\\x0E':\n\t\treturn \"symbol\"\n\tcase '\\x0F':\n\t\treturn \"code with scope\"\n\tcase '\\x10':\n\t\treturn \"32-bit integer\"\n\tcase '\\x11':\n\t\treturn \"timestamp\"\n\tcase '\\x12':\n\t\treturn \"64-bit integer\"\n\tcase '\\x13':\n\t\treturn \"128-bit decimal\"\n\tcase '\\x7F':\n\t\treturn \"max key\"\n\tcase '\\xFF':\n\t\treturn \"min key\"\n\tdefault:\n\t\treturn \"invalid\"\n\t}\n}\n\n// BSON element types as described in https://bsonspec.org/spec.html.\nconst (\n\tTypeDouble           Type = 0x01\n\tTypeString           Type = 0x02\n\tTypeEmbeddedDocument Type = 0x03\n\tTypeArray            Type = 0x04\n\tTypeBinary           Type = 0x05\n\tTypeUndefined        Type = 0x06\n\tTypeObjectID         Type = 0x07\n\tTypeBoolean          Type = 0x08\n\tTypeDateTime         Type = 0x09\n\tTypeNull             Type = 0x0A\n\tTypeRegex            Type = 0x0B\n\tTypeDBPointer        Type = 0x0C\n\tTypeJavaScript       Type = 0x0D\n\tTypeSymbol           Type = 0x0E\n\tTypeCodeWithScope    Type = 0x0F\n\tTypeInt32            Type = 0x10\n\tTypeTimestamp        Type = 0x11\n\tTypeInt64            Type = 0x12\n\tTypeDecimal128       Type = 0x13\n\tTypeMaxKey           Type = 0x7F\n\tTypeMinKey           Type = 0xFF\n)\n"
  },
  {
    "path": "x/bsonx/bsoncore/value.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bsoncore\n\nimport (\n\t\"bytes\"\n\t\"encoding/base64\"\n\t\"encoding/hex\"\n\t\"fmt\"\n\t\"math\"\n\t\"sort\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\t\"unicode/utf8\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/bsoncoreutil\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/decimal128\"\n)\n\n// ElementTypeError specifies that a method to obtain a BSON value an incorrect type was called on a bson.Value.\ntype ElementTypeError struct {\n\tMethod string\n\tType   Type\n}\n\n// Error implements the error interface.\nfunc (ete ElementTypeError) Error() string {\n\treturn \"Call of \" + ete.Method + \" on \" + ete.Type.String() + \" type\"\n}\n\n// Value represents a BSON value with a type and raw bytes.\ntype Value struct {\n\tType Type\n\tData []byte\n}\n\n// Validate ensures the value is a valid BSON value.\nfunc (v Value) Validate() error {\n\t_, _, valid := readValue(v.Data, v.Type)\n\tif !valid {\n\t\treturn NewInsufficientBytesError(v.Data, v.Data)\n\t}\n\treturn nil\n}\n\n// IsNumber returns true if the type of v is a numeric BSON type.\nfunc (v Value) IsNumber() bool {\n\tswitch v.Type {\n\tcase TypeDouble, TypeInt32, TypeInt64, TypeDecimal128:\n\t\treturn true\n\tdefault:\n\t\treturn false\n\t}\n}\n\n// AsInt32 returns a BSON number as an int32. If the BSON type is not a numeric one, this method\n// will panic.\nfunc (v Value) AsInt32() int32 {\n\tif !v.IsNumber() {\n\t\tpanic(ElementTypeError{\"bsoncore.Value.AsInt32\", v.Type})\n\t}\n\tvar i32 int32\n\tswitch v.Type {\n\tcase TypeDouble:\n\t\tf64, _, ok := ReadDouble(v.Data)\n\t\tif !ok {\n\t\t\tpanic(NewInsufficientBytesError(v.Data, v.Data))\n\t\t}\n\t\ti32 = int32(f64)\n\tcase TypeInt32:\n\t\tvar ok bool\n\t\ti32, _, ok = ReadInt32(v.Data)\n\t\tif !ok {\n\t\t\tpanic(NewInsufficientBytesError(v.Data, v.Data))\n\t\t}\n\tcase TypeInt64:\n\t\ti64, _, ok := ReadInt64(v.Data)\n\t\tif !ok {\n\t\t\tpanic(NewInsufficientBytesError(v.Data, v.Data))\n\t\t}\n\t\ti32 = int32(i64)\n\tcase TypeDecimal128:\n\t\tpanic(ElementTypeError{\"bsoncore.Value.AsInt32\", v.Type})\n\t}\n\treturn i32\n}\n\n// AsInt32OK functions the same as AsInt32 but returns a boolean instead of panicking. False\n// indicates an error.\nfunc (v Value) AsInt32OK() (int32, bool) {\n\tif !v.IsNumber() {\n\t\treturn 0, false\n\t}\n\tvar i32 int32\n\tswitch v.Type {\n\tcase TypeDouble:\n\t\tf64, _, ok := ReadDouble(v.Data)\n\t\tif !ok {\n\t\t\treturn 0, false\n\t\t}\n\t\ti32 = int32(f64)\n\tcase TypeInt32:\n\t\tvar ok bool\n\t\ti32, _, ok = ReadInt32(v.Data)\n\t\tif !ok {\n\t\t\treturn 0, false\n\t\t}\n\tcase TypeInt64:\n\t\ti64, _, ok := ReadInt64(v.Data)\n\t\tif !ok {\n\t\t\treturn 0, false\n\t\t}\n\t\ti32 = int32(i64)\n\tcase TypeDecimal128:\n\t\treturn 0, false\n\t}\n\treturn i32, true\n}\n\n// AsInt64 returns a BSON number as an int64. If the BSON type is not a numeric one, this method\n// will panic.\nfunc (v Value) AsInt64() int64 {\n\tif !v.IsNumber() {\n\t\tpanic(ElementTypeError{\"bsoncore.Value.AsInt64\", v.Type})\n\t}\n\tvar i64 int64\n\tswitch v.Type {\n\tcase TypeDouble:\n\t\tf64, _, ok := ReadDouble(v.Data)\n\t\tif !ok {\n\t\t\tpanic(NewInsufficientBytesError(v.Data, v.Data))\n\t\t}\n\t\ti64 = int64(f64)\n\tcase TypeInt32:\n\t\ti32, _, ok := ReadInt32(v.Data)\n\t\tif !ok {\n\t\t\tpanic(NewInsufficientBytesError(v.Data, v.Data))\n\t\t}\n\t\ti64 = int64(i32)\n\tcase TypeInt64:\n\t\tvar ok bool\n\t\ti64, _, ok = ReadInt64(v.Data)\n\t\tif !ok {\n\t\t\tpanic(NewInsufficientBytesError(v.Data, v.Data))\n\t\t}\n\tcase TypeDecimal128:\n\t\tpanic(ElementTypeError{\"bsoncore.Value.AsInt64\", v.Type})\n\t}\n\treturn i64\n}\n\n// AsInt64OK functions the same as AsInt64 but returns a boolean instead of panicking. False\n// indicates an error.\nfunc (v Value) AsInt64OK() (int64, bool) {\n\tif !v.IsNumber() {\n\t\treturn 0, false\n\t}\n\tvar i64 int64\n\tswitch v.Type {\n\tcase TypeDouble:\n\t\tf64, _, ok := ReadDouble(v.Data)\n\t\tif !ok {\n\t\t\treturn 0, false\n\t\t}\n\t\ti64 = int64(f64)\n\tcase TypeInt32:\n\t\ti32, _, ok := ReadInt32(v.Data)\n\t\tif !ok {\n\t\t\treturn 0, false\n\t\t}\n\t\ti64 = int64(i32)\n\tcase TypeInt64:\n\t\tvar ok bool\n\t\ti64, _, ok = ReadInt64(v.Data)\n\t\tif !ok {\n\t\t\treturn 0, false\n\t\t}\n\tcase TypeDecimal128:\n\t\treturn 0, false\n\t}\n\treturn i64, true\n}\n\n// AsFloat64 returns a BSON number as an float64. If the BSON type is not a numeric one, this method\n// will panic.\nfunc (v Value) AsFloat64() float64 {\n\tif !v.IsNumber() {\n\t\tpanic(ElementTypeError{\"bsoncore.Value.AsFloat64\", v.Type})\n\t}\n\tvar f64 float64\n\tswitch v.Type {\n\tcase TypeDouble:\n\t\tvar ok bool\n\t\tf64, _, ok = ReadDouble(v.Data)\n\t\tif !ok {\n\t\t\tpanic(NewInsufficientBytesError(v.Data, v.Data))\n\t\t}\n\tcase TypeInt32:\n\t\ti32, _, ok := ReadInt32(v.Data)\n\t\tif !ok {\n\t\t\tpanic(NewInsufficientBytesError(v.Data, v.Data))\n\t\t}\n\t\tf64 = float64(i32)\n\tcase TypeInt64:\n\t\ti64, _, ok := ReadInt64(v.Data)\n\t\tif !ok {\n\t\t\tpanic(NewInsufficientBytesError(v.Data, v.Data))\n\t\t}\n\t\tf64 = float64(i64)\n\tcase TypeDecimal128:\n\t\tpanic(ElementTypeError{\"bsoncore.Value.AsFloat64\", v.Type})\n\t}\n\treturn f64\n}\n\n// AsFloat64OK functions the same as AsFloat64 but returns a boolean instead of panicking. False\n// indicates an error.\nfunc (v Value) AsFloat64OK() (float64, bool) {\n\tif !v.IsNumber() {\n\t\treturn 0, false\n\t}\n\tvar f64 float64\n\tswitch v.Type {\n\tcase TypeDouble:\n\t\tvar ok bool\n\t\tf64, _, ok = ReadDouble(v.Data)\n\t\tif !ok {\n\t\t\treturn 0, false\n\t\t}\n\tcase TypeInt32:\n\t\ti32, _, ok := ReadInt32(v.Data)\n\t\tif !ok {\n\t\t\treturn 0, false\n\t\t}\n\t\tf64 = float64(i32)\n\tcase TypeInt64:\n\t\ti64, _, ok := ReadInt64(v.Data)\n\t\tif !ok {\n\t\t\treturn 0, false\n\t\t}\n\t\tf64 = float64(i64)\n\tcase TypeDecimal128:\n\t\treturn 0, false\n\t}\n\treturn f64, true\n}\n\n// Equal compares v to v2 and returns true if they are equal.\nfunc (v Value) Equal(v2 Value) bool {\n\tif v.Type != v2.Type {\n\t\treturn false\n\t}\n\n\treturn bytes.Equal(v.Data, v2.Data)\n}\n\nfunc idHex(id [12]byte) string {\n\tvar buf [24]byte\n\thex.Encode(buf[:], id[:])\n\treturn string(buf[:])\n}\n\n// String implements the fmt.String interface. This method will return values in extended JSON\n// format. If the value is not valid, this returns an empty string\nfunc (v Value) String() string {\n\tstr, _ := v.StringN(-1)\n\treturn str\n}\n\n// StringN will return values in extended JSON format that will stringify a value upto N bytes.\n// If N is non-negative, it will truncate the string to N bytes. Otherwise, it will return the full\n// string representation. The second return value indicates whether the string was truncated or not.\n// If the value is not valid, this returns an empty string\nfunc (v Value) StringN(n int) (string, bool) {\n\tvar str string\n\tswitch v.Type {\n\tcase TypeString:\n\t\ts, ok := v.StringValueOK()\n\t\tif !ok {\n\t\t\treturn \"\", false\n\t\t}\n\t\tstr = escapeString(s)\n\tcase TypeEmbeddedDocument:\n\t\tdoc, ok := v.DocumentOK()\n\t\tif !ok {\n\t\t\treturn \"\", false\n\t\t}\n\t\treturn doc.StringN(n)\n\tcase TypeArray:\n\t\tarr, ok := v.ArrayOK()\n\t\tif !ok {\n\t\t\treturn \"\", false\n\t\t}\n\t\treturn arr.StringN(n)\n\tcase TypeDouble:\n\t\tf64, ok := v.DoubleOK()\n\t\tif !ok {\n\t\t\treturn \"\", false\n\t\t}\n\t\tstr = fmt.Sprintf(`{\"$numberDouble\":\"%s\"}`, formatDouble(f64))\n\tcase TypeBinary:\n\t\tsubtype, data, ok := v.BinaryOK()\n\t\tif !ok {\n\t\t\treturn \"\", false\n\t\t}\n\t\tstr = fmt.Sprintf(`{\"$binary\":{\"base64\":\"%s\",\"subType\":\"%02x\"}}`, base64.StdEncoding.EncodeToString(data), subtype)\n\tcase TypeUndefined:\n\t\tstr = `{\"$undefined\":true}`\n\tcase TypeObjectID:\n\t\toid, ok := v.ObjectIDOK()\n\t\tif !ok {\n\t\t\treturn \"\", false\n\t\t}\n\t\tstr = fmt.Sprintf(`{\"$oid\":\"%s\"}`, idHex(oid))\n\tcase TypeBoolean:\n\t\tb, ok := v.BooleanOK()\n\t\tif !ok {\n\t\t\treturn \"\", false\n\t\t}\n\t\tstr = strconv.FormatBool(b)\n\tcase TypeDateTime:\n\t\tdt, ok := v.DateTimeOK()\n\t\tif !ok {\n\t\t\treturn \"\", false\n\t\t}\n\t\tstr = fmt.Sprintf(`{\"$date\":{\"$numberLong\":\"%d\"}}`, dt)\n\tcase TypeNull:\n\t\tstr = \"null\"\n\tcase TypeRegex:\n\t\tpattern, options, ok := v.RegexOK()\n\t\tif !ok {\n\t\t\treturn \"\", false\n\t\t}\n\t\tstr = fmt.Sprintf(\n\t\t\t`{\"$regularExpression\":{\"pattern\":%s,\"options\":\"%s\"}}`,\n\t\t\tescapeString(pattern), sortStringAlphebeticAscending(options),\n\t\t)\n\tcase TypeDBPointer:\n\t\tns, pointer, ok := v.DBPointerOK()\n\t\tif !ok {\n\t\t\treturn \"\", false\n\t\t}\n\t\tstr = fmt.Sprintf(`{\"$dbPointer\":{\"$ref\":%s,\"$id\":{\"$oid\":\"%s\"}}}`, escapeString(ns), idHex(pointer))\n\tcase TypeJavaScript:\n\t\tjs, ok := v.JavaScriptOK()\n\t\tif !ok {\n\t\t\treturn \"\", false\n\t\t}\n\t\tstr = fmt.Sprintf(`{\"$code\":%s}`, escapeString(js))\n\tcase TypeSymbol:\n\t\tsymbol, ok := v.SymbolOK()\n\t\tif !ok {\n\t\t\treturn \"\", false\n\t\t}\n\t\tstr = fmt.Sprintf(`{\"$symbol\":%s}`, escapeString(symbol))\n\tcase TypeCodeWithScope:\n\t\tcode, scope, ok := v.CodeWithScopeOK()\n\t\tif !ok {\n\t\t\treturn \"\", false\n\t\t}\n\t\tstr = fmt.Sprintf(`{\"$code\":%s,\"$scope\":%s}`, code, scope)\n\tcase TypeInt32:\n\t\ti32, ok := v.Int32OK()\n\t\tif !ok {\n\t\t\treturn \"\", false\n\t\t}\n\t\tstr = fmt.Sprintf(`{\"$numberInt\":\"%d\"}`, i32)\n\tcase TypeTimestamp:\n\t\tt, i, ok := v.TimestampOK()\n\t\tif !ok {\n\t\t\treturn \"\", false\n\t\t}\n\t\tstr = fmt.Sprintf(`{\"$timestamp\":{\"t\":%v,\"i\":%v}}`, t, i)\n\tcase TypeInt64:\n\t\ti64, ok := v.Int64OK()\n\t\tif !ok {\n\t\t\treturn \"\", false\n\t\t}\n\t\tstr = fmt.Sprintf(`{\"$numberLong\":\"%d\"}`, i64)\n\tcase TypeDecimal128:\n\t\th, l, ok := v.Decimal128OK()\n\t\tif !ok {\n\t\t\treturn \"\", false\n\t\t}\n\t\tstr = fmt.Sprintf(`{\"$numberDecimal\":\"%s\"}`, decimal128.String(h, l))\n\tcase TypeMinKey:\n\t\tstr = `{\"$minKey\":1}`\n\tcase TypeMaxKey:\n\t\tstr = `{\"$maxKey\":1}`\n\tdefault:\n\t\tstr = \"\"\n\t}\n\tif n >= 0 && len(str) > n {\n\t\treturn bsoncoreutil.Truncate(str, n), true\n\t}\n\treturn str, false\n}\n\n// DebugString outputs a human readable version of Document. It will attempt to stringify the\n// valid components of the document even if the entire document is not valid.\nfunc (v Value) DebugString() string {\n\tswitch v.Type {\n\tcase TypeString:\n\t\tstr, ok := v.StringValueOK()\n\t\tif !ok {\n\t\t\treturn \"<malformed>\"\n\t\t}\n\t\treturn escapeString(str)\n\tcase TypeEmbeddedDocument:\n\t\tdoc, ok := v.DocumentOK()\n\t\tif !ok {\n\t\t\treturn \"<malformed>\"\n\t\t}\n\t\treturn doc.DebugString()\n\tcase TypeArray:\n\t\tarr, ok := v.ArrayOK()\n\t\tif !ok {\n\t\t\treturn \"<malformed>\"\n\t\t}\n\t\treturn arr.DebugString()\n\tcase TypeCodeWithScope:\n\t\tcode, scope, ok := v.CodeWithScopeOK()\n\t\tif !ok {\n\t\t\treturn \"\"\n\t\t}\n\t\treturn fmt.Sprintf(`{\"$code\":%s,\"$scope\":%s}`, code, scope.DebugString())\n\tdefault:\n\t\tstr := v.String()\n\t\tif str == \"\" {\n\t\t\treturn \"<malformed>\"\n\t\t}\n\t\treturn str\n\t}\n}\n\n// Double returns the float64 value for this element.\n// It panics if e's BSON type is not TypeDouble.\nfunc (v Value) Double() float64 {\n\tif v.Type != TypeDouble {\n\t\tpanic(ElementTypeError{\"bsoncore.Value.Double\", v.Type})\n\t}\n\tf64, _, ok := ReadDouble(v.Data)\n\tif !ok {\n\t\tpanic(NewInsufficientBytesError(v.Data, v.Data))\n\t}\n\treturn f64\n}\n\n// DoubleOK is the same as Double, but returns a boolean instead of panicking.\nfunc (v Value) DoubleOK() (float64, bool) {\n\tif v.Type != TypeDouble {\n\t\treturn 0, false\n\t}\n\tf64, _, ok := ReadDouble(v.Data)\n\tif !ok {\n\t\treturn 0, false\n\t}\n\treturn f64, true\n}\n\n// StringValue returns the string balue for this element.\n// It panics if e's BSON type is not TypeString.\n//\n// NOTE: This method is called StringValue to avoid a collision with the String method which\n// implements the fmt.Stringer interface.\nfunc (v Value) StringValue() string {\n\tif v.Type != TypeString {\n\t\tpanic(ElementTypeError{\"bsoncore.Value.StringValue\", v.Type})\n\t}\n\tstr, _, ok := ReadString(v.Data)\n\tif !ok {\n\t\tpanic(NewInsufficientBytesError(v.Data, v.Data))\n\t}\n\treturn str\n}\n\n// StringValueOK is the same as StringValue, but returns a boolean instead of\n// panicking.\nfunc (v Value) StringValueOK() (string, bool) {\n\tif v.Type != TypeString {\n\t\treturn \"\", false\n\t}\n\tstr, _, ok := ReadString(v.Data)\n\tif !ok {\n\t\treturn \"\", false\n\t}\n\treturn str, true\n}\n\n// Document returns the BSON document the Value represents as a Document. It panics if the\n// value is a BSON type other than document.\nfunc (v Value) Document() Document {\n\tif v.Type != TypeEmbeddedDocument {\n\t\tpanic(ElementTypeError{\"bsoncore.Value.Document\", v.Type})\n\t}\n\tdoc, _, ok := ReadDocument(v.Data)\n\tif !ok {\n\t\tpanic(NewInsufficientBytesError(v.Data, v.Data))\n\t}\n\treturn doc\n}\n\n// DocumentOK is the same as Document, except it returns a boolean\n// instead of panicking.\nfunc (v Value) DocumentOK() (Document, bool) {\n\tif v.Type != TypeEmbeddedDocument {\n\t\treturn nil, false\n\t}\n\tdoc, _, ok := ReadDocument(v.Data)\n\tif !ok {\n\t\treturn nil, false\n\t}\n\treturn doc, true\n}\n\n// Array returns the BSON array the Value represents as an Array. It panics if the\n// value is a BSON type other than array.\nfunc (v Value) Array() Array {\n\tif v.Type != TypeArray {\n\t\tpanic(ElementTypeError{\"bsoncore.Value.Array\", v.Type})\n\t}\n\tarr, _, ok := ReadArray(v.Data)\n\tif !ok {\n\t\tpanic(NewInsufficientBytesError(v.Data, v.Data))\n\t}\n\treturn arr\n}\n\n// ArrayOK is the same as Array, except it returns a boolean instead\n// of panicking.\nfunc (v Value) ArrayOK() (Array, bool) {\n\tif v.Type != TypeArray {\n\t\treturn nil, false\n\t}\n\tarr, _, ok := ReadArray(v.Data)\n\tif !ok {\n\t\treturn nil, false\n\t}\n\treturn arr, true\n}\n\n// Binary returns the BSON binary value the Value represents. It panics if the value is a BSON type\n// other than binary.\nfunc (v Value) Binary() (subtype byte, data []byte) {\n\tif v.Type != TypeBinary {\n\t\tpanic(ElementTypeError{\"bsoncore.Value.Binary\", v.Type})\n\t}\n\tsubtype, data, _, ok := ReadBinary(v.Data)\n\tif !ok {\n\t\tpanic(NewInsufficientBytesError(v.Data, v.Data))\n\t}\n\treturn subtype, data\n}\n\n// BinaryOK is the same as Binary, except it returns a boolean instead of\n// panicking.\nfunc (v Value) BinaryOK() (subtype byte, data []byte, ok bool) {\n\tif v.Type != TypeBinary {\n\t\treturn 0x00, nil, false\n\t}\n\tsubtype, data, _, ok = ReadBinary(v.Data)\n\tif !ok {\n\t\treturn 0x00, nil, false\n\t}\n\treturn subtype, data, true\n}\n\n// ObjectID returns the BSON objectid value the Value represents. It panics if the value is a BSON\n// type other than objectid.\nfunc (v Value) ObjectID() [12]byte {\n\tif v.Type != TypeObjectID {\n\t\tpanic(ElementTypeError{\"bsoncore.Value.ObjectID\", v.Type})\n\t}\n\toid, _, ok := ReadObjectID(v.Data)\n\tif !ok {\n\t\tpanic(NewInsufficientBytesError(v.Data, v.Data))\n\t}\n\treturn oid\n}\n\n// ObjectIDOK is the same as ObjectID, except it returns a boolean instead of\n// panicking.\nfunc (v Value) ObjectIDOK() ([12]byte, bool) {\n\tif v.Type != TypeObjectID {\n\t\treturn objectID{}, false\n\t}\n\toid, _, ok := ReadObjectID(v.Data)\n\tif !ok {\n\t\treturn objectID{}, false\n\t}\n\treturn oid, true\n}\n\n// Boolean returns the boolean value the Value represents. It panics if the\n// value is a BSON type other than boolean.\nfunc (v Value) Boolean() bool {\n\tif v.Type != TypeBoolean {\n\t\tpanic(ElementTypeError{\"bsoncore.Value.Boolean\", v.Type})\n\t}\n\tb, _, ok := ReadBoolean(v.Data)\n\tif !ok {\n\t\tpanic(NewInsufficientBytesError(v.Data, v.Data))\n\t}\n\treturn b\n}\n\n// BooleanOK is the same as Boolean, except it returns a boolean instead of\n// panicking.\nfunc (v Value) BooleanOK() (bool, bool) {\n\tif v.Type != TypeBoolean {\n\t\treturn false, false\n\t}\n\tb, _, ok := ReadBoolean(v.Data)\n\tif !ok {\n\t\treturn false, false\n\t}\n\treturn b, true\n}\n\n// DateTime returns the BSON datetime value the Value represents as a\n// unix timestamp. It panics if the value is a BSON type other than datetime.\nfunc (v Value) DateTime() int64 {\n\tif v.Type != TypeDateTime {\n\t\tpanic(ElementTypeError{\"bsoncore.Value.DateTime\", v.Type})\n\t}\n\tdt, _, ok := ReadDateTime(v.Data)\n\tif !ok {\n\t\tpanic(NewInsufficientBytesError(v.Data, v.Data))\n\t}\n\treturn dt\n}\n\n// DateTimeOK is the same as DateTime, except it returns a boolean instead of\n// panicking.\nfunc (v Value) DateTimeOK() (int64, bool) {\n\tif v.Type != TypeDateTime {\n\t\treturn 0, false\n\t}\n\tdt, _, ok := ReadDateTime(v.Data)\n\tif !ok {\n\t\treturn 0, false\n\t}\n\treturn dt, true\n}\n\n// Time returns the BSON datetime value the Value represents. It panics if the value is a BSON\n// type other than datetime.\nfunc (v Value) Time() time.Time {\n\tif v.Type != TypeDateTime {\n\t\tpanic(ElementTypeError{\"bsoncore.Value.Time\", v.Type})\n\t}\n\tdt, _, ok := ReadDateTime(v.Data)\n\tif !ok {\n\t\tpanic(NewInsufficientBytesError(v.Data, v.Data))\n\t}\n\treturn time.Unix(dt/1000, dt%1000*1000000)\n}\n\n// TimeOK is the same as Time, except it returns a boolean instead of\n// panicking.\nfunc (v Value) TimeOK() (time.Time, bool) {\n\tif v.Type != TypeDateTime {\n\t\treturn time.Time{}, false\n\t}\n\tdt, _, ok := ReadDateTime(v.Data)\n\tif !ok {\n\t\treturn time.Time{}, false\n\t}\n\treturn time.Unix(dt/1000, dt%1000*1000000), true\n}\n\n// Regex returns the BSON regex value the Value represents. It panics if the value is a BSON\n// type other than regex.\nfunc (v Value) Regex() (pattern, options string) {\n\tif v.Type != TypeRegex {\n\t\tpanic(ElementTypeError{\"bsoncore.Value.Regex\", v.Type})\n\t}\n\tpattern, options, _, ok := ReadRegex(v.Data)\n\tif !ok {\n\t\tpanic(NewInsufficientBytesError(v.Data, v.Data))\n\t}\n\treturn pattern, options\n}\n\n// RegexOK is the same as Regex, except it returns a boolean instead of\n// panicking.\nfunc (v Value) RegexOK() (pattern, options string, ok bool) {\n\tif v.Type != TypeRegex {\n\t\treturn \"\", \"\", false\n\t}\n\tpattern, options, _, ok = ReadRegex(v.Data)\n\tif !ok {\n\t\treturn \"\", \"\", false\n\t}\n\treturn pattern, options, true\n}\n\n// DBPointer returns the BSON dbpointer value the Value represents. It panics if the value is a BSON\n// type other than DBPointer.\nfunc (v Value) DBPointer() (string, [12]byte) {\n\tif v.Type != TypeDBPointer {\n\t\tpanic(ElementTypeError{\"bsoncore.Value.DBPointer\", v.Type})\n\t}\n\tns, pointer, _, ok := ReadDBPointer(v.Data)\n\tif !ok {\n\t\tpanic(NewInsufficientBytesError(v.Data, v.Data))\n\t}\n\treturn ns, pointer\n}\n\n// DBPointerOK is the same as DBPoitner, except that it returns a boolean\n// instead of panicking.\nfunc (v Value) DBPointerOK() (string, [12]byte, bool) {\n\tif v.Type != TypeDBPointer {\n\t\treturn \"\", objectID{}, false\n\t}\n\tns, pointer, _, ok := ReadDBPointer(v.Data)\n\tif !ok {\n\t\treturn \"\", objectID{}, false\n\t}\n\treturn ns, pointer, true\n}\n\n// JavaScript returns the BSON JavaScript code value the Value represents. It panics if the value is\n// a BSON type other than JavaScript code.\nfunc (v Value) JavaScript() string {\n\tif v.Type != TypeJavaScript {\n\t\tpanic(ElementTypeError{\"bsoncore.Value.JavaScript\", v.Type})\n\t}\n\tjs, _, ok := ReadJavaScript(v.Data)\n\tif !ok {\n\t\tpanic(NewInsufficientBytesError(v.Data, v.Data))\n\t}\n\treturn js\n}\n\n// JavaScriptOK is the same as Javascript, excepti that it returns a boolean\n// instead of panicking.\nfunc (v Value) JavaScriptOK() (string, bool) {\n\tif v.Type != TypeJavaScript {\n\t\treturn \"\", false\n\t}\n\tjs, _, ok := ReadJavaScript(v.Data)\n\tif !ok {\n\t\treturn \"\", false\n\t}\n\treturn js, true\n}\n\n// Symbol returns the BSON symbol value the Value represents. It panics if the value is a BSON\n// type other than symbol.\nfunc (v Value) Symbol() string {\n\tif v.Type != TypeSymbol {\n\t\tpanic(ElementTypeError{\"bsoncore.Value.Symbol\", v.Type})\n\t}\n\tsymbol, _, ok := ReadSymbol(v.Data)\n\tif !ok {\n\t\tpanic(NewInsufficientBytesError(v.Data, v.Data))\n\t}\n\treturn symbol\n}\n\n// SymbolOK is the same as Symbol, excepti that it returns a boolean\n// instead of panicking.\nfunc (v Value) SymbolOK() (string, bool) {\n\tif v.Type != TypeSymbol {\n\t\treturn \"\", false\n\t}\n\tsymbol, _, ok := ReadSymbol(v.Data)\n\tif !ok {\n\t\treturn \"\", false\n\t}\n\treturn symbol, true\n}\n\n// CodeWithScope returns the BSON JavaScript code with scope the Value represents.\n// It panics if the value is a BSON type other than JavaScript code with scope.\nfunc (v Value) CodeWithScope() (string, Document) {\n\tif v.Type != TypeCodeWithScope {\n\t\tpanic(ElementTypeError{\"bsoncore.Value.CodeWithScope\", v.Type})\n\t}\n\tcode, scope, _, ok := ReadCodeWithScope(v.Data)\n\tif !ok {\n\t\tpanic(NewInsufficientBytesError(v.Data, v.Data))\n\t}\n\treturn code, scope\n}\n\n// CodeWithScopeOK is the same as CodeWithScope, except that it returns a boolean instead of\n// panicking.\nfunc (v Value) CodeWithScopeOK() (string, Document, bool) {\n\tif v.Type != TypeCodeWithScope {\n\t\treturn \"\", nil, false\n\t}\n\tcode, scope, _, ok := ReadCodeWithScope(v.Data)\n\tif !ok {\n\t\treturn \"\", nil, false\n\t}\n\treturn code, scope, true\n}\n\n// Int32 returns the int32 the Value represents. It panics if the value is a BSON type other than\n// int32.\nfunc (v Value) Int32() int32 {\n\tif v.Type != TypeInt32 {\n\t\tpanic(ElementTypeError{\"bsoncore.Value.Int32\", v.Type})\n\t}\n\ti32, _, ok := ReadInt32(v.Data)\n\tif !ok {\n\t\tpanic(NewInsufficientBytesError(v.Data, v.Data))\n\t}\n\treturn i32\n}\n\n// Int32OK is the same as Int32, except that it returns a boolean instead of\n// panicking.\nfunc (v Value) Int32OK() (int32, bool) {\n\tif v.Type != TypeInt32 {\n\t\treturn 0, false\n\t}\n\ti32, _, ok := ReadInt32(v.Data)\n\tif !ok {\n\t\treturn 0, false\n\t}\n\treturn i32, true\n}\n\n// Timestamp returns the BSON timestamp value the Value represents. It panics if the value is a\n// BSON type other than timestamp.\nfunc (v Value) Timestamp() (t, i uint32) {\n\tif v.Type != TypeTimestamp {\n\t\tpanic(ElementTypeError{\"bsoncore.Value.Timestamp\", v.Type})\n\t}\n\tt, i, _, ok := ReadTimestamp(v.Data)\n\tif !ok {\n\t\tpanic(NewInsufficientBytesError(v.Data, v.Data))\n\t}\n\treturn t, i\n}\n\n// TimestampOK is the same as Timestamp, except that it returns a boolean\n// instead of panicking.\nfunc (v Value) TimestampOK() (t, i uint32, ok bool) {\n\tif v.Type != TypeTimestamp {\n\t\treturn 0, 0, false\n\t}\n\tt, i, _, ok = ReadTimestamp(v.Data)\n\tif !ok {\n\t\treturn 0, 0, false\n\t}\n\treturn t, i, true\n}\n\n// Int64 returns the int64 the Value represents. It panics if the value is a BSON type other than\n// int64.\nfunc (v Value) Int64() int64 {\n\tif v.Type != TypeInt64 {\n\t\tpanic(ElementTypeError{\"bsoncore.Value.Int64\", v.Type})\n\t}\n\ti64, _, ok := ReadInt64(v.Data)\n\tif !ok {\n\t\tpanic(NewInsufficientBytesError(v.Data, v.Data))\n\t}\n\treturn i64\n}\n\n// Int64OK is the same as Int64, except that it returns a boolean instead of\n// panicking.\nfunc (v Value) Int64OK() (int64, bool) {\n\tif v.Type != TypeInt64 {\n\t\treturn 0, false\n\t}\n\ti64, _, ok := ReadInt64(v.Data)\n\tif !ok {\n\t\treturn 0, false\n\t}\n\treturn i64, true\n}\n\n// Decimal128 returns the decimal the Value represents. It panics if the value is a BSON type other than\n// decimal.\nfunc (v Value) Decimal128() (uint64, uint64) {\n\tif v.Type != TypeDecimal128 {\n\t\tpanic(ElementTypeError{\"bsoncore.Value.Decimal128\", v.Type})\n\t}\n\th, l, _, ok := ReadDecimal128(v.Data)\n\tif !ok {\n\t\tpanic(NewInsufficientBytesError(v.Data, v.Data))\n\t}\n\treturn h, l\n}\n\n// Decimal128OK is the same as Decimal128, except that it returns a boolean\n// instead of panicking.\nfunc (v Value) Decimal128OK() (uint64, uint64, bool) {\n\tif v.Type != TypeDecimal128 {\n\t\treturn 0, 0, false\n\t}\n\th, l, _, ok := ReadDecimal128(v.Data)\n\tif !ok {\n\t\treturn 0, 0, false\n\t}\n\treturn h, l, true\n}\n\nvar hexChars = \"0123456789abcdef\"\n\nfunc escapeString(s string) string {\n\tescapeHTML := true\n\tvar buf bytes.Buffer\n\tbuf.WriteByte('\"')\n\tstart := 0\n\tfor i := 0; i < len(s); {\n\t\tif b := s[i]; b < utf8.RuneSelf {\n\t\t\tif htmlSafeSet[b] || (!escapeHTML && safeSet[b]) {\n\t\t\t\ti++\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif start < i {\n\t\t\t\tbuf.WriteString(s[start:i])\n\t\t\t}\n\t\t\tswitch b {\n\t\t\tcase '\\\\', '\"':\n\t\t\t\tbuf.WriteByte('\\\\')\n\t\t\t\tbuf.WriteByte(b)\n\t\t\tcase '\\n':\n\t\t\t\tbuf.WriteByte('\\\\')\n\t\t\t\tbuf.WriteByte('n')\n\t\t\tcase '\\r':\n\t\t\t\tbuf.WriteByte('\\\\')\n\t\t\t\tbuf.WriteByte('r')\n\t\t\tcase '\\t':\n\t\t\t\tbuf.WriteByte('\\\\')\n\t\t\t\tbuf.WriteByte('t')\n\t\t\tcase '\\b':\n\t\t\t\tbuf.WriteByte('\\\\')\n\t\t\t\tbuf.WriteByte('b')\n\t\t\tcase '\\f':\n\t\t\t\tbuf.WriteByte('\\\\')\n\t\t\t\tbuf.WriteByte('f')\n\t\t\tdefault:\n\t\t\t\t// This encodes bytes < 0x20 except for \\t, \\n and \\r.\n\t\t\t\t// If escapeHTML is set, it also escapes <, >, and &\n\t\t\t\t// because they can lead to security holes when\n\t\t\t\t// user-controlled strings are rendered into JSON\n\t\t\t\t// and served to some browsers.\n\t\t\t\tbuf.WriteString(`\\u00`)\n\t\t\t\tbuf.WriteByte(hexChars[b>>4])\n\t\t\t\tbuf.WriteByte(hexChars[b&0xF])\n\t\t\t}\n\t\t\ti++\n\t\t\tstart = i\n\t\t\tcontinue\n\t\t}\n\t\tc, size := utf8.DecodeRuneInString(s[i:])\n\t\tif c == utf8.RuneError && size == 1 {\n\t\t\tif start < i {\n\t\t\t\tbuf.WriteString(s[start:i])\n\t\t\t}\n\t\t\tbuf.WriteString(`\\ufffd`)\n\t\t\ti += size\n\t\t\tstart = i\n\t\t\tcontinue\n\t\t}\n\t\t// U+2028 is LINE SEPARATOR.\n\t\t// U+2029 is PARAGRAPH SEPARATOR.\n\t\t// They are both technically valid characters in JSON strings,\n\t\t// but don't work in JSONP, which has to be evaluated as JavaScript,\n\t\t// and can lead to security holes there. It is valid JSON to\n\t\t// escape them, so we do so unconditionally.\n\t\t// See http://timelessrepo.com/json-isnt-a-javascript-subset for discussion.\n\t\tif c == '\\u2028' || c == '\\u2029' {\n\t\t\tif start < i {\n\t\t\t\tbuf.WriteString(s[start:i])\n\t\t\t}\n\t\t\tbuf.WriteString(`\\u202`)\n\t\t\tbuf.WriteByte(hexChars[c&0xF])\n\t\t\ti += size\n\t\t\tstart = i\n\t\t\tcontinue\n\t\t}\n\t\ti += size\n\t}\n\tif start < len(s) {\n\t\tbuf.WriteString(s[start:])\n\t}\n\tbuf.WriteByte('\"')\n\treturn buf.String()\n}\n\nfunc formatDouble(f float64) string {\n\tvar s string\n\tswitch {\n\tcase math.IsInf(f, 1):\n\t\ts = \"Infinity\"\n\tcase math.IsInf(f, -1):\n\t\ts = \"-Infinity\"\n\tcase math.IsNaN(f):\n\t\ts = \"NaN\"\n\tdefault:\n\t\t// Print exactly one decimalType place for integers; otherwise, print as many are necessary to\n\t\t// perfectly represent it.\n\t\ts = strconv.FormatFloat(f, 'G', -1, 64)\n\t\tif !strings.ContainsRune(s, '.') {\n\t\t\ts += \".0\"\n\t\t}\n\t}\n\n\treturn s\n}\n\ntype sortableString []rune\n\nfunc (ss sortableString) Len() int {\n\treturn len(ss)\n}\n\nfunc (ss sortableString) Less(i, j int) bool {\n\treturn ss[i] < ss[j]\n}\n\nfunc (ss sortableString) Swap(i, j int) {\n\tss[i], ss[j] = ss[j], ss[i]\n}\n\nfunc sortStringAlphebeticAscending(s string) string {\n\tss := sortableString([]rune(s))\n\tsort.Sort(ss)\n\treturn string([]rune(ss))\n}\n"
  },
  {
    "path": "x/bsonx/bsoncore/value_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage bsoncore\n\nimport (\n\t\"fmt\"\n\t\"reflect\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/google/go-cmp/cmp\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n)\n\nfunc TestValue(t *testing.T) {\n\tt.Parallel()\n\n\tt.Run(\"Validate\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tt.Run(\"invalid\", func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tv := Value{Type: TypeDouble, Data: []byte{0x01, 0x02, 0x03, 0x04}}\n\t\t\twant := NewInsufficientBytesError(v.Data, v.Data)\n\t\t\tgot := v.Validate()\n\t\t\tif !compareErrors(got, want) {\n\t\t\t\tt.Errorf(\"Errors do not match. got %v; want %v\", got, want)\n\t\t\t}\n\t\t})\n\t\tt.Run(\"value\", func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tv := Value{Type: TypeDouble, Data: AppendDouble(nil, 3.14159)}\n\t\t\tvar want error\n\t\t\tgot := v.Validate()\n\t\t\tif !compareErrors(got, want) {\n\t\t\t\tt.Errorf(\"Errors do not match. got %v; want %v\", got, want)\n\t\t\t}\n\t\t})\n\t})\n\n\tt.Run(\"IsNumber\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\ttestCases := []struct {\n\t\t\tname  string\n\t\t\tval   Value\n\t\t\tisnum bool\n\t\t}{\n\t\t\t{\"double\", Value{Type: TypeDouble}, true},\n\t\t\t{\"int32\", Value{Type: TypeInt32}, true},\n\t\t\t{\"int64\", Value{Type: TypeInt64}, true},\n\t\t\t{\"decimal128\", Value{Type: TypeDecimal128}, true},\n\t\t\t{\"string\", Value{Type: TypeString}, false},\n\t\t\t{\"regex\", Value{Type: TypeRegex}, false},\n\t\t}\n\n\t\tfor _, tc := range testCases {\n\t\t\ttc := tc\n\n\t\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\t\tt.Parallel()\n\n\t\t\t\tisnum := tc.val.IsNumber()\n\t\t\t\tif isnum != tc.isnum {\n\t\t\t\t\tt.Errorf(\"IsNumber did not return the expected boolean. got %t; want %t\", isnum, tc.isnum)\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t})\n\n\tnow := time.Now().Truncate(time.Millisecond)\n\tvar oid [12]byte\n\n\ttestCases := []struct {\n\t\tname     string\n\t\tfn       any\n\t\tval      Value\n\t\tpanicErr error\n\t\tret      []any\n\t}{\n\t\t{\n\t\t\t\"Double/Not Double\", Value.Double,\n\t\t\tValue{Type: TypeString},\n\t\t\tElementTypeError{\"bsoncore.Value.Double\", TypeString},\n\t\t\tnil,\n\t\t},\n\t\t{\n\t\t\t\"Double/Insufficient Bytes\", Value.Double,\n\t\t\tValue{Type: TypeDouble, Data: []byte{0x01, 0x02, 0x03, 0x04}},\n\t\t\tNewInsufficientBytesError([]byte{0x01, 0x02, 0x03, 0x04}, []byte{0x01, 0x02, 0x03, 0x04}),\n\t\t\tnil,\n\t\t},\n\t\t{\n\t\t\t\"Double/Success\", Value.Double,\n\t\t\tValue{Type: TypeDouble, Data: AppendDouble(nil, 3.14159)},\n\t\t\tnil,\n\t\t\t[]any{float64(3.14159)},\n\t\t},\n\t\t{\n\t\t\t\"DoubleOK/Not Double\", Value.DoubleOK,\n\t\t\tValue{Type: TypeString},\n\t\t\tnil,\n\t\t\t[]any{float64(0), false},\n\t\t},\n\t\t{\n\t\t\t\"DoubleOK/Insufficient Bytes\", Value.DoubleOK,\n\t\t\tValue{Type: TypeDouble, Data: []byte{0x01, 0x02, 0x03, 0x04}},\n\t\t\tnil,\n\t\t\t[]any{float64(0), false},\n\t\t},\n\t\t{\n\t\t\t\"DoubleOK/Success\", Value.DoubleOK,\n\t\t\tValue{Type: TypeDouble, Data: AppendDouble(nil, 3.14159)},\n\t\t\tnil,\n\t\t\t[]any{float64(3.14159), true},\n\t\t},\n\t\t{\n\t\t\t\"StringValue/Not String\", Value.StringValue,\n\t\t\tValue{Type: TypeDouble},\n\t\t\tElementTypeError{\"bsoncore.Value.StringValue\", TypeDouble},\n\t\t\tnil,\n\t\t},\n\t\t{\n\t\t\t\"StringValue/Insufficient Bytes\", Value.StringValue,\n\t\t\tValue{Type: TypeString, Data: []byte{0x01, 0x02, 0x03, 0x04}},\n\t\t\tNewInsufficientBytesError([]byte{0x01, 0x02, 0x03, 0x04}, []byte{0x01, 0x02, 0x03, 0x04}),\n\t\t\tnil,\n\t\t},\n\t\t{\n\t\t\t\"StringValue/Zero Length\", Value.StringValue,\n\t\t\tValue{Type: TypeString, Data: []byte{0x00, 0x00, 0x00, 0x00}},\n\t\t\tNewInsufficientBytesError([]byte{0x00, 0x00, 0x00, 0x00}, []byte{0x00, 0x00, 0x00, 0x00}),\n\t\t\tnil,\n\t\t},\n\t\t{\n\t\t\t\"StringValue/Success\", Value.StringValue,\n\t\t\tValue{Type: TypeString, Data: AppendString(nil, \"hello, world!\")},\n\t\t\tnil,\n\t\t\t[]any{\"hello, world!\"},\n\t\t},\n\t\t{\n\t\t\t\"StringValueOK/Not String\", Value.StringValueOK,\n\t\t\tValue{Type: TypeDouble},\n\t\t\tnil,\n\t\t\t[]any{\"\", false},\n\t\t},\n\t\t{\n\t\t\t\"StringValueOK/Insufficient Bytes\", Value.StringValueOK,\n\t\t\tValue{Type: TypeString, Data: []byte{0x01, 0x02, 0x03, 0x04}},\n\t\t\tnil,\n\t\t\t[]any{\"\", false},\n\t\t},\n\t\t{\n\t\t\t\"StringValueOK/Zero Length\", Value.StringValueOK,\n\t\t\tValue{Type: TypeString, Data: []byte{0x00, 0x00, 0x00, 0x00}},\n\t\t\tnil,\n\t\t\t[]any{\"\", false},\n\t\t},\n\t\t{\n\t\t\t\"StringValueOK/Success\", Value.StringValueOK,\n\t\t\tValue{Type: TypeString, Data: AppendString(nil, \"hello, world!\")},\n\t\t\tnil,\n\t\t\t[]any{\"hello, world!\", true},\n\t\t},\n\t\t{\n\t\t\t\"Document/Not Document\", Value.Document,\n\t\t\tValue{Type: TypeString},\n\t\t\tElementTypeError{\"bsoncore.Value.Document\", TypeString},\n\t\t\tnil,\n\t\t},\n\t\t{\n\t\t\t\"Document/Insufficient Bytes\", Value.Document,\n\t\t\tValue{Type: TypeEmbeddedDocument, Data: []byte{0x01, 0x02, 0x03, 0x04}},\n\t\t\tNewInsufficientBytesError([]byte{0x01, 0x02, 0x03, 0x04}, []byte{0x01, 0x02, 0x03, 0x04}),\n\t\t\tnil,\n\t\t},\n\t\t{\n\t\t\t\"Document/Success\", Value.Document,\n\t\t\tValue{Type: TypeEmbeddedDocument, Data: []byte{0x05, 0x00, 0x00, 0x00, 0x00}},\n\t\t\tnil,\n\t\t\t[]any{Document{0x05, 0x00, 0x00, 0x00, 0x00}},\n\t\t},\n\t\t{\n\t\t\t\"DocumentOK/Not Document\", Value.DocumentOK,\n\t\t\tValue{Type: TypeString},\n\t\t\tnil,\n\t\t\t[]any{Document(nil), false},\n\t\t},\n\t\t{\n\t\t\t\"DocumentOK/Insufficient Bytes\", Value.DocumentOK,\n\t\t\tValue{Type: TypeEmbeddedDocument, Data: []byte{0x01, 0x02, 0x03, 0x04}},\n\t\t\tnil,\n\t\t\t[]any{Document(nil), false},\n\t\t},\n\t\t{\n\t\t\t\"DocumentOK/Success\", Value.DocumentOK,\n\t\t\tValue{Type: TypeEmbeddedDocument, Data: []byte{0x05, 0x00, 0x00, 0x00, 0x00}},\n\t\t\tnil,\n\t\t\t[]any{Document{0x05, 0x00, 0x00, 0x00, 0x00}, true},\n\t\t},\n\t\t{\n\t\t\t\"Array/Not Array\", Value.Array,\n\t\t\tValue{Type: TypeString},\n\t\t\tElementTypeError{\"bsoncore.Value.Array\", TypeString},\n\t\t\tnil,\n\t\t},\n\t\t{\n\t\t\t\"Array/Insufficient Bytes\", Value.Array,\n\t\t\tValue{Type: TypeArray, Data: []byte{0x01, 0x02, 0x03, 0x04}},\n\t\t\tNewInsufficientBytesError([]byte{0x01, 0x02, 0x03, 0x04}, []byte{0x01, 0x02, 0x03, 0x04}),\n\t\t\tnil,\n\t\t},\n\t\t{\n\t\t\t\"Array/Success\", Value.Array,\n\t\t\tValue{Type: TypeArray, Data: []byte{0x05, 0x00, 0x00, 0x00, 0x00}},\n\t\t\tnil,\n\t\t\t[]any{Array{0x05, 0x00, 0x00, 0x00, 0x00}},\n\t\t},\n\t\t{\n\t\t\t\"ArrayOK/Not Array\", Value.ArrayOK,\n\t\t\tValue{Type: TypeString},\n\t\t\tnil,\n\t\t\t[]any{Array(nil), false},\n\t\t},\n\t\t{\n\t\t\t\"ArrayOK/Insufficient Bytes\", Value.ArrayOK,\n\t\t\tValue{Type: TypeArray, Data: []byte{0x01, 0x02, 0x03, 0x04}},\n\t\t\tnil,\n\t\t\t[]any{Array(nil), false},\n\t\t},\n\t\t{\n\t\t\t\"ArrayOK/Success\", Value.ArrayOK,\n\t\t\tValue{Type: TypeArray, Data: []byte{0x05, 0x00, 0x00, 0x00, 0x00}},\n\t\t\tnil,\n\t\t\t[]any{Array{0x05, 0x00, 0x00, 0x00, 0x00}, true},\n\t\t},\n\t\t{\n\t\t\t\"Binary/Not Binary\", Value.Binary,\n\t\t\tValue{Type: TypeString},\n\t\t\tElementTypeError{\"bsoncore.Value.Binary\", TypeString},\n\t\t\tnil,\n\t\t},\n\t\t{\n\t\t\t\"Binary/Insufficient Bytes\", Value.Binary,\n\t\t\tValue{Type: TypeBinary, Data: []byte{0x01, 0x02, 0x03, 0x04}},\n\t\t\tNewInsufficientBytesError([]byte{0x01, 0x02, 0x03, 0x04}, []byte{0x01, 0x02, 0x03, 0x04}),\n\t\t\tnil,\n\t\t},\n\t\t{\n\t\t\t\"Binary/Success\", Value.Binary,\n\t\t\tValue{Type: TypeBinary, Data: AppendBinary(nil, 0xFF, []byte{0x01, 0x02, 0x03})},\n\t\t\tnil,\n\t\t\t[]any{byte(0xFF), []byte{0x01, 0x02, 0x03}},\n\t\t},\n\t\t{\n\t\t\t\"BinaryOK/Not Binary\", Value.BinaryOK,\n\t\t\tValue{Type: TypeString},\n\t\t\tnil,\n\t\t\t[]any{byte(0x00), []byte(nil), false},\n\t\t},\n\t\t{\n\t\t\t\"BinaryOK/Insufficient Bytes\", Value.BinaryOK,\n\t\t\tValue{Type: TypeBinary, Data: []byte{0x01, 0x02, 0x03, 0x04}},\n\t\t\tnil,\n\t\t\t[]any{byte(0x00), []byte(nil), false},\n\t\t},\n\t\t{\n\t\t\t\"BinaryOK/Success\", Value.BinaryOK,\n\t\t\tValue{Type: TypeBinary, Data: AppendBinary(nil, 0xFF, []byte{0x01, 0x02, 0x03})},\n\t\t\tnil,\n\t\t\t[]any{byte(0xFF), []byte{0x01, 0x02, 0x03}, true},\n\t\t},\n\t\t{\n\t\t\t\"ObjectID/Not ObjectID\", Value.ObjectID,\n\t\t\tValue{Type: TypeString},\n\t\t\tElementTypeError{\"bsoncore.Value.ObjectID\", TypeString},\n\t\t\tnil,\n\t\t},\n\t\t{\n\t\t\t\"ObjectID/Insufficient Bytes\", Value.ObjectID,\n\t\t\tValue{Type: TypeObjectID, Data: []byte{0x01, 0x02, 0x03, 0x04}},\n\t\t\tNewInsufficientBytesError([]byte{0x01, 0x02, 0x03, 0x04}, []byte{0x01, 0x02, 0x03, 0x04}),\n\t\t\tnil,\n\t\t},\n\t\t{\n\t\t\t\"ObjectID/Success\", Value.ObjectID,\n\t\t\tValue{Type: TypeObjectID, Data: AppendObjectID(nil, [12]byte{0x01, 0x02})},\n\t\t\tnil,\n\t\t\t[]any{[12]byte{0x01, 0x02}},\n\t\t},\n\t\t{\n\t\t\t\"ObjectIDOK/Not ObjectID\", Value.ObjectIDOK,\n\t\t\tValue{Type: TypeString},\n\t\t\tnil,\n\t\t\t[]any{[12]byte{}, false},\n\t\t},\n\t\t{\n\t\t\t\"ObjectIDOK/Insufficient Bytes\", Value.ObjectIDOK,\n\t\t\tValue{Type: TypeObjectID, Data: []byte{0x01, 0x02, 0x03, 0x04}},\n\t\t\tnil,\n\t\t\t[]any{[12]byte{}, false},\n\t\t},\n\t\t{\n\t\t\t\"ObjectIDOK/Success\", Value.ObjectIDOK,\n\t\t\tValue{Type: TypeObjectID, Data: AppendObjectID(nil, [12]byte{0x01, 0x02})},\n\t\t\tnil,\n\t\t\t[]any{[12]byte{0x01, 0x02}, true},\n\t\t},\n\t\t{\n\t\t\t\"Boolean/Not Boolean\", Value.Boolean,\n\t\t\tValue{Type: TypeString},\n\t\t\tElementTypeError{\"bsoncore.Value.Boolean\", TypeString},\n\t\t\tnil,\n\t\t},\n\t\t{\n\t\t\t\"Boolean/Insufficient Bytes\", Value.Boolean,\n\t\t\tValue{Type: TypeBoolean, Data: []byte{}},\n\t\t\tNewInsufficientBytesError([]byte{}, []byte{}),\n\t\t\tnil,\n\t\t},\n\t\t{\n\t\t\t\"Boolean/Success\", Value.Boolean,\n\t\t\tValue{Type: TypeBoolean, Data: AppendBoolean(nil, true)},\n\t\t\tnil,\n\t\t\t[]any{true},\n\t\t},\n\t\t{\n\t\t\t\"BooleanOK/Not Boolean\", Value.BooleanOK,\n\t\t\tValue{Type: TypeString},\n\t\t\tnil,\n\t\t\t[]any{false, false},\n\t\t},\n\t\t{\n\t\t\t\"BooleanOK/Insufficient Bytes\", Value.BooleanOK,\n\t\t\tValue{Type: TypeBoolean, Data: []byte{}},\n\t\t\tnil,\n\t\t\t[]any{false, false},\n\t\t},\n\t\t{\n\t\t\t\"BooleanOK/Success\", Value.BooleanOK,\n\t\t\tValue{Type: TypeBoolean, Data: AppendBoolean(nil, true)},\n\t\t\tnil,\n\t\t\t[]any{true, true},\n\t\t},\n\t\t{\n\t\t\t\"DateTime/Not DateTime\", Value.DateTime,\n\t\t\tValue{Type: TypeString},\n\t\t\tElementTypeError{\"bsoncore.Value.DateTime\", TypeString},\n\t\t\tnil,\n\t\t},\n\t\t{\n\t\t\t\"DateTime/Insufficient Bytes\", Value.DateTime,\n\t\t\tValue{Type: TypeDateTime, Data: []byte{0x01, 0x02, 0x03}},\n\t\t\tNewInsufficientBytesError([]byte{}, []byte{}),\n\t\t\tnil,\n\t\t},\n\t\t{\n\t\t\t\"DateTime/Success\", Value.DateTime,\n\t\t\tValue{Type: TypeDateTime, Data: AppendDateTime(nil, 12345)},\n\t\t\tnil,\n\t\t\t[]any{int64(12345)},\n\t\t},\n\t\t{\n\t\t\t\"DateTimeOK/Not DateTime\", Value.DateTimeOK,\n\t\t\tValue{Type: TypeString},\n\t\t\tnil,\n\t\t\t[]any{int64(0), false},\n\t\t},\n\t\t{\n\t\t\t\"DateTimeOK/Insufficient Bytes\", Value.DateTimeOK,\n\t\t\tValue{Type: TypeDateTime, Data: []byte{0x01, 0x02, 0x03}},\n\t\t\tnil,\n\t\t\t[]any{int64(0), false},\n\t\t},\n\t\t{\n\t\t\t\"DateTimeOK/Success\", Value.DateTimeOK,\n\t\t\tValue{Type: TypeDateTime, Data: AppendDateTime(nil, 12345)},\n\t\t\tnil,\n\t\t\t[]any{int64(12345), true},\n\t\t},\n\t\t{\n\t\t\t\"Time/Not DateTime\", Value.Time,\n\t\t\tValue{Type: TypeString},\n\t\t\tElementTypeError{\"bsoncore.Value.Time\", TypeString},\n\t\t\tnil,\n\t\t},\n\t\t{\n\t\t\t\"Time/Insufficient Bytes\", Value.Time,\n\t\t\tValue{Type: TypeDateTime, Data: []byte{0x01, 0x02, 0x03}},\n\t\t\tNewInsufficientBytesError([]byte{0x01, 0x02, 0x03}, []byte{0x01, 0x02, 0x03}),\n\t\t\tnil,\n\t\t},\n\t\t{\n\t\t\t\"Time/Success\", Value.Time,\n\t\t\tValue{Type: TypeDateTime, Data: AppendTime(nil, now)},\n\t\t\tnil,\n\t\t\t[]any{now},\n\t\t},\n\t\t{\n\t\t\t\"TimeOK/Not DateTime\", Value.TimeOK,\n\t\t\tValue{Type: TypeString},\n\t\t\tnil,\n\t\t\t[]any{time.Time{}, false},\n\t\t},\n\t\t{\n\t\t\t\"TimeOK/Insufficient Bytes\", Value.TimeOK,\n\t\t\tValue{Type: TypeDateTime, Data: []byte{0x01, 0x02, 0x03}},\n\t\t\tnil,\n\t\t\t[]any{time.Time{}, false},\n\t\t},\n\t\t{\n\t\t\t\"TimeOK/Success\", Value.TimeOK,\n\t\t\tValue{Type: TypeDateTime, Data: AppendTime(nil, now)},\n\t\t\tnil,\n\t\t\t[]any{now, true},\n\t\t},\n\t\t{\n\t\t\t\"Regex/Not Regex\", Value.Regex,\n\t\t\tValue{Type: TypeString},\n\t\t\tElementTypeError{\"bsoncore.Value.Regex\", TypeString},\n\t\t\tnil,\n\t\t},\n\t\t{\n\t\t\t\"Regex/Insufficient Bytes\", Value.Regex,\n\t\t\tValue{Type: TypeRegex, Data: []byte{0x01, 0x02, 0x03}},\n\t\t\tNewInsufficientBytesError([]byte{0x01, 0x02, 0x03}, []byte{0x01, 0x02, 0x03}),\n\t\t\tnil,\n\t\t},\n\t\t{\n\t\t\t\"Regex/Success\", Value.Regex,\n\t\t\tValue{Type: TypeRegex, Data: AppendRegex(nil, \"/abcdefg/\", \"hijkl\")},\n\t\t\tnil,\n\t\t\t[]any{\"/abcdefg/\", \"hijkl\"},\n\t\t},\n\t\t{\n\t\t\t\"RegexOK/Not Regex\", Value.RegexOK,\n\t\t\tValue{Type: TypeString},\n\t\t\tnil,\n\t\t\t[]any{\"\", \"\", false},\n\t\t},\n\t\t{\n\t\t\t\"RegexOK/Insufficient Bytes\", Value.RegexOK,\n\t\t\tValue{Type: TypeRegex, Data: []byte{0x01, 0x02, 0x03}},\n\t\t\tnil,\n\t\t\t[]any{\"\", \"\", false},\n\t\t},\n\t\t{\n\t\t\t\"RegexOK/Success\", Value.RegexOK,\n\t\t\tValue{Type: TypeRegex, Data: AppendRegex(nil, \"/abcdefg/\", \"hijkl\")},\n\t\t\tnil,\n\t\t\t[]any{\"/abcdefg/\", \"hijkl\", true},\n\t\t},\n\t\t{\n\t\t\t\"DBPointer/Not DBPointer\", Value.DBPointer,\n\t\t\tValue{Type: TypeString},\n\t\t\tElementTypeError{\"bsoncore.Value.DBPointer\", TypeString},\n\t\t\tnil,\n\t\t},\n\t\t{\n\t\t\t\"DBPointer/Insufficient Bytes\", Value.DBPointer,\n\t\t\tValue{Type: TypeDBPointer, Data: []byte{0x01, 0x02, 0x03}},\n\t\t\tNewInsufficientBytesError([]byte{0x01, 0x02, 0x03}, []byte{0x01, 0x02, 0x03}),\n\t\t\tnil,\n\t\t},\n\t\t{\n\t\t\t\"DBPointer/Success\", Value.DBPointer,\n\t\t\tValue{Type: TypeDBPointer, Data: AppendDBPointer(nil, \"foobar\", oid)},\n\t\t\tnil,\n\t\t\t[]any{\"foobar\", oid},\n\t\t},\n\t\t{\n\t\t\t\"DBPointerOK/Not DBPointer\", Value.DBPointerOK,\n\t\t\tValue{Type: TypeString},\n\t\t\tnil,\n\t\t\t[]any{\"\", [12]byte{}, false},\n\t\t},\n\t\t{\n\t\t\t\"DBPointerOK/Insufficient Bytes\", Value.DBPointerOK,\n\t\t\tValue{Type: TypeDBPointer, Data: []byte{0x01, 0x02, 0x03}},\n\t\t\tnil,\n\t\t\t[]any{\"\", [12]byte{}, false},\n\t\t},\n\t\t{\n\t\t\t\"DBPointerOK/Success\", Value.DBPointerOK,\n\t\t\tValue{Type: TypeDBPointer, Data: AppendDBPointer(nil, \"foobar\", oid)},\n\t\t\tnil,\n\t\t\t[]any{\"foobar\", oid, true},\n\t\t},\n\t\t{\n\t\t\t\"JavaScript/Not JavaScript\", Value.JavaScript,\n\t\t\tValue{Type: TypeString},\n\t\t\tElementTypeError{\"bsoncore.Value.JavaScript\", TypeString},\n\t\t\tnil,\n\t\t},\n\t\t{\n\t\t\t\"JavaScript/Insufficient Bytes\", Value.JavaScript,\n\t\t\tValue{Type: TypeJavaScript, Data: []byte{0x01, 0x02, 0x03}},\n\t\t\tNewInsufficientBytesError([]byte{0x01, 0x02, 0x03}, []byte{0x01, 0x02, 0x03}),\n\t\t\tnil,\n\t\t},\n\t\t{\n\t\t\t\"JavaScript/Success\", Value.JavaScript,\n\t\t\tValue{Type: TypeJavaScript, Data: AppendJavaScript(nil, \"var hello = 'world';\")},\n\t\t\tnil,\n\t\t\t[]any{\"var hello = 'world';\"},\n\t\t},\n\t\t{\n\t\t\t\"JavaScriptOK/Not JavaScript\", Value.JavaScriptOK,\n\t\t\tValue{Type: TypeString},\n\t\t\tnil,\n\t\t\t[]any{\"\", false},\n\t\t},\n\t\t{\n\t\t\t\"JavaScriptOK/Insufficient Bytes\", Value.JavaScriptOK,\n\t\t\tValue{Type: TypeJavaScript, Data: []byte{0x01, 0x02, 0x03}},\n\t\t\tnil,\n\t\t\t[]any{\"\", false},\n\t\t},\n\t\t{\n\t\t\t\"JavaScriptOK/Success\", Value.JavaScriptOK,\n\t\t\tValue{Type: TypeJavaScript, Data: AppendJavaScript(nil, \"var hello = 'world';\")},\n\t\t\tnil,\n\t\t\t[]any{\"var hello = 'world';\", true},\n\t\t},\n\t\t{\n\t\t\t\"Symbol/Not Symbol\", Value.Symbol,\n\t\t\tValue{Type: TypeString},\n\t\t\tElementTypeError{\"bsoncore.Value.Symbol\", TypeString},\n\t\t\tnil,\n\t\t},\n\t\t{\n\t\t\t\"Symbol/Insufficient Bytes\", Value.Symbol,\n\t\t\tValue{Type: TypeSymbol, Data: []byte{0x01, 0x02, 0x03}},\n\t\t\tNewInsufficientBytesError([]byte{0x01, 0x02, 0x03}, []byte{0x01, 0x02, 0x03}),\n\t\t\tnil,\n\t\t},\n\t\t{\n\t\t\t\"Symbol/Success\", Value.Symbol,\n\t\t\tValue{Type: TypeSymbol, Data: AppendSymbol(nil, \"symbol123456\")},\n\t\t\tnil,\n\t\t\t[]any{\"symbol123456\"},\n\t\t},\n\t\t{\n\t\t\t\"SymbolOK/Not Symbol\", Value.SymbolOK,\n\t\t\tValue{Type: TypeString},\n\t\t\tnil,\n\t\t\t[]any{\"\", false},\n\t\t},\n\t\t{\n\t\t\t\"SymbolOK/Insufficient Bytes\", Value.SymbolOK,\n\t\t\tValue{Type: TypeSymbol, Data: []byte{0x01, 0x02, 0x03}},\n\t\t\tnil,\n\t\t\t[]any{\"\", false},\n\t\t},\n\t\t{\n\t\t\t\"SymbolOK/Success\", Value.SymbolOK,\n\t\t\tValue{Type: TypeSymbol, Data: AppendSymbol(nil, \"symbol123456\")},\n\t\t\tnil,\n\t\t\t[]any{\"symbol123456\", true},\n\t\t},\n\t\t{\n\t\t\t\"CodeWithScope/Not CodeWithScope\", Value.CodeWithScope,\n\t\t\tValue{Type: TypeString},\n\t\t\tElementTypeError{\"bsoncore.Value.CodeWithScope\", TypeString},\n\t\t\tnil,\n\t\t},\n\t\t{\n\t\t\t\"CodeWithScope/Insufficient Bytes\", Value.CodeWithScope,\n\t\t\tValue{Type: TypeCodeWithScope, Data: []byte{0x01, 0x02, 0x03}},\n\t\t\tNewInsufficientBytesError([]byte{0x01, 0x02, 0x03}, []byte{0x01, 0x02, 0x03}),\n\t\t\tnil,\n\t\t},\n\t\t{\n\t\t\t\"CodeWithScope/Success\", Value.CodeWithScope,\n\t\t\tValue{Type: TypeCodeWithScope, Data: AppendCodeWithScope(nil, \"var hello = 'world';\", Document{0x05, 0x00, 0x00, 0x00, 0x00})},\n\t\t\tnil,\n\t\t\t[]any{\"var hello = 'world';\", Document{0x05, 0x00, 0x00, 0x00, 0x00}},\n\t\t},\n\t\t{\n\t\t\t\"CodeWithScopeOK/Not CodeWithScope\", Value.CodeWithScopeOK,\n\t\t\tValue{Type: TypeString},\n\t\t\tnil,\n\t\t\t[]any{\"\", Document(nil), false},\n\t\t},\n\t\t{\n\t\t\t\"CodeWithScopeOK/Insufficient Bytes\", Value.CodeWithScopeOK,\n\t\t\tValue{Type: TypeCodeWithScope, Data: []byte{0x01, 0x02, 0x03}},\n\t\t\tnil,\n\t\t\t[]any{\"\", Document(nil), false},\n\t\t},\n\t\t{\n\t\t\t\"CodeWithScopeOK/Success\", Value.CodeWithScopeOK,\n\t\t\tValue{Type: TypeCodeWithScope, Data: AppendCodeWithScope(nil, \"var hello = 'world';\", Document{0x05, 0x00, 0x00, 0x00, 0x00})},\n\t\t\tnil,\n\t\t\t[]any{\"var hello = 'world';\", Document{0x05, 0x00, 0x00, 0x00, 0x00}, true},\n\t\t},\n\t\t{\n\t\t\t\"Int32/Not Int32\", Value.Int32,\n\t\t\tValue{Type: TypeString},\n\t\t\tElementTypeError{\"bsoncore.Value.Int32\", TypeString},\n\t\t\tnil,\n\t\t},\n\t\t{\n\t\t\t\"Int32/Insufficient Bytes\", Value.Int32,\n\t\t\tValue{Type: TypeInt32, Data: []byte{0x01, 0x02, 0x03}},\n\t\t\tNewInsufficientBytesError([]byte{0x01, 0x02, 0x03}, []byte{0x01, 0x02, 0x03}),\n\t\t\tnil,\n\t\t},\n\t\t{\n\t\t\t\"Int32/Success\", Value.Int32,\n\t\t\tValue{Type: TypeInt32, Data: AppendInt32(nil, 1234)},\n\t\t\tnil,\n\t\t\t[]any{int32(1234)},\n\t\t},\n\t\t{\n\t\t\t\"Int32OK/Not Int32\", Value.Int32OK,\n\t\t\tValue{Type: TypeString},\n\t\t\tnil,\n\t\t\t[]any{int32(0), false},\n\t\t},\n\t\t{\n\t\t\t\"Int32OK/Insufficient Bytes\", Value.Int32OK,\n\t\t\tValue{Type: TypeInt32, Data: []byte{0x01, 0x02, 0x03}},\n\t\t\tnil,\n\t\t\t[]any{int32(0), false},\n\t\t},\n\t\t{\n\t\t\t\"Int32OK/Success\", Value.Int32OK,\n\t\t\tValue{Type: TypeInt32, Data: AppendInt32(nil, 1234)},\n\t\t\tnil,\n\t\t\t[]any{int32(1234), true},\n\t\t},\n\t\t{\n\t\t\t\"Timestamp/Not Timestamp\", Value.Timestamp,\n\t\t\tValue{Type: TypeString},\n\t\t\tElementTypeError{\"bsoncore.Value.Timestamp\", TypeString},\n\t\t\tnil,\n\t\t},\n\t\t{\n\t\t\t\"Timestamp/Insufficient Bytes\", Value.Timestamp,\n\t\t\tValue{Type: TypeTimestamp, Data: []byte{0x01, 0x02, 0x03}},\n\t\t\tNewInsufficientBytesError([]byte{0x01, 0x02, 0x03}, []byte{0x01, 0x02, 0x03}),\n\t\t\tnil,\n\t\t},\n\t\t{\n\t\t\t\"Timestamp/Success\", Value.Timestamp,\n\t\t\tValue{Type: TypeTimestamp, Data: AppendTimestamp(nil, 12345, 67890)},\n\t\t\tnil,\n\t\t\t[]any{uint32(12345), uint32(67890)},\n\t\t},\n\t\t{\n\t\t\t\"TimestampOK/Not Timestamp\", Value.TimestampOK,\n\t\t\tValue{Type: TypeString},\n\t\t\tnil,\n\t\t\t[]any{uint32(0), uint32(0), false},\n\t\t},\n\t\t{\n\t\t\t\"TimestampOK/Insufficient Bytes\", Value.TimestampOK,\n\t\t\tValue{Type: TypeTimestamp, Data: []byte{0x01, 0x02, 0x03}},\n\t\t\tnil,\n\t\t\t[]any{uint32(0), uint32(0), false},\n\t\t},\n\t\t{\n\t\t\t\"TimestampOK/Success\", Value.TimestampOK,\n\t\t\tValue{Type: TypeTimestamp, Data: AppendTimestamp(nil, 12345, 67890)},\n\t\t\tnil,\n\t\t\t[]any{uint32(12345), uint32(67890), true},\n\t\t},\n\t\t{\n\t\t\t\"Int64/Not Int64\", Value.Int64,\n\t\t\tValue{Type: TypeString},\n\t\t\tElementTypeError{\"bsoncore.Value.Int64\", TypeString},\n\t\t\tnil,\n\t\t},\n\t\t{\n\t\t\t\"Int64/Insufficient Bytes\", Value.Int64,\n\t\t\tValue{Type: TypeInt64, Data: []byte{0x01, 0x02, 0x03}},\n\t\t\tNewInsufficientBytesError([]byte{0x01, 0x02, 0x03}, []byte{0x01, 0x02, 0x03}),\n\t\t\tnil,\n\t\t},\n\t\t{\n\t\t\t\"Int64/Success\", Value.Int64,\n\t\t\tValue{Type: TypeInt64, Data: AppendInt64(nil, 1234567890)},\n\t\t\tnil,\n\t\t\t[]any{int64(1234567890)},\n\t\t},\n\t\t{\n\t\t\t\"Int64OK/Not Int64\", Value.Int64OK,\n\t\t\tValue{Type: TypeString},\n\t\t\tnil,\n\t\t\t[]any{int64(0), false},\n\t\t},\n\t\t{\n\t\t\t\"Int64OK/Insufficient Bytes\", Value.Int64OK,\n\t\t\tValue{Type: TypeInt64, Data: []byte{0x01, 0x02, 0x03}},\n\t\t\tnil,\n\t\t\t[]any{int64(0), false},\n\t\t},\n\t\t{\n\t\t\t\"Int64OK/Success\", Value.Int64OK,\n\t\t\tValue{Type: TypeInt64, Data: AppendInt64(nil, 1234567890)},\n\t\t\tnil,\n\t\t\t[]any{int64(1234567890), true},\n\t\t},\n\t\t{\n\t\t\t\"Decimal128/Not Decimal128\", Value.Decimal128,\n\t\t\tValue{Type: TypeString},\n\t\t\tElementTypeError{\"bsoncore.Value.Decimal128\", TypeString},\n\t\t\tnil,\n\t\t},\n\t\t{\n\t\t\t\"Decimal128/Insufficient Bytes\", Value.Decimal128,\n\t\t\tValue{Type: TypeDecimal128, Data: []byte{0x01, 0x02, 0x03}},\n\t\t\tNewInsufficientBytesError([]byte{0x01, 0x02, 0x03}, []byte{0x01, 0x02, 0x03}),\n\t\t\tnil,\n\t\t},\n\t\t{\n\t\t\t\"Decimal128/Success\", Value.Decimal128,\n\t\t\tValue{Type: TypeDecimal128, Data: AppendDecimal128(nil, 12345, 67890)},\n\t\t\tnil,\n\t\t\t[]any{uint64(12345), uint64(67890)},\n\t\t},\n\t\t{\n\t\t\t\"Decimal128OK/Not Decimal128\", Value.Decimal128OK,\n\t\t\tValue{Type: TypeString},\n\t\t\tnil,\n\t\t\t[]any{uint64(0), uint64(0), false},\n\t\t},\n\t\t{\n\t\t\t\"Decimal128OK/Insufficient Bytes\", Value.Decimal128OK,\n\t\t\tValue{Type: TypeDecimal128, Data: []byte{0x01, 0x02, 0x03}},\n\t\t\tnil,\n\t\t\t[]any{uint64(0), uint64(0), false},\n\t\t},\n\t\t{\n\t\t\t\"Decimal128OK/Success\", Value.Decimal128OK,\n\t\t\tValue{Type: TypeDecimal128, Data: AppendDecimal128(nil, 12345, 67890)},\n\t\t\tnil,\n\t\t\t[]any{uint64(12345), uint64(67890), true},\n\t\t},\n\t\t{\n\t\t\t\"AsInt32/Not Number\", Value.AsInt32,\n\t\t\tValue{Type: TypeString},\n\t\t\tElementTypeError{\"bsoncore.Value.AsInt32\", TypeString},\n\t\t\tnil,\n\t\t},\n\t\t{\n\t\t\t\"AsInt32/Double/Insufficient Bytes\", Value.AsInt32,\n\t\t\tValue{Type: TypeDouble, Data: []byte{0x01, 0x02, 0x03}},\n\t\t\tNewInsufficientBytesError([]byte{0x01, 0x02, 0x03}, []byte{0x01, 0x02, 0x03}),\n\t\t\tnil,\n\t\t},\n\t\t{\n\t\t\t\"AsInt32/Int32/Insufficient Bytes\", Value.AsInt32,\n\t\t\tValue{Type: TypeInt32, Data: []byte{0x01, 0x02}},\n\t\t\tNewInsufficientBytesError([]byte{0x01, 0x02}, []byte{0x01, 0x02}),\n\t\t\tnil,\n\t\t},\n\t\t{\n\t\t\t\"AsInt32/Int64/Insufficient Bytes\", Value.AsInt32,\n\t\t\tValue{Type: TypeInt64, Data: []byte{0x01, 0x02, 0x03}},\n\t\t\tNewInsufficientBytesError([]byte{0x01, 0x02, 0x03}, []byte{0x01, 0x02, 0x03}),\n\t\t\tnil,\n\t\t},\n\t\t{\n\t\t\t\"AsInt32/Decimal128\", Value.AsInt32,\n\t\t\tValue{Type: TypeDecimal128, Data: AppendDecimal128(nil, 12345, 67890)},\n\t\t\tElementTypeError{\"bsoncore.Value.AsInt32\", TypeDecimal128},\n\t\t\tnil,\n\t\t},\n\t\t{\n\t\t\t\"AsInt32/From Double\", Value.AsInt32,\n\t\t\tValue{Type: TypeDouble, Data: AppendDouble(nil, 42.7)},\n\t\t\tnil,\n\t\t\t[]any{int32(42)},\n\t\t},\n\t\t{\n\t\t\t\"AsInt32/From Int32\", Value.AsInt32,\n\t\t\tValue{Type: TypeInt32, Data: AppendInt32(nil, 12345)},\n\t\t\tnil,\n\t\t\t[]any{int32(12345)},\n\t\t},\n\t\t{\n\t\t\t\"AsInt32/From Int64\", Value.AsInt32,\n\t\t\tValue{Type: TypeInt64, Data: AppendInt64(nil, 98765)},\n\t\t\tnil,\n\t\t\t[]any{int32(98765)},\n\t\t},\n\t\t{\n\t\t\t\"AsInt32OK/Not Number\", Value.AsInt32OK,\n\t\t\tValue{Type: TypeString},\n\t\t\tnil,\n\t\t\t[]any{int32(0), false},\n\t\t},\n\t\t{\n\t\t\t\"AsInt32OK/Double/Insufficient Bytes\", Value.AsInt32OK,\n\t\t\tValue{Type: TypeDouble, Data: []byte{0x01, 0x02, 0x03}},\n\t\t\tnil,\n\t\t\t[]any{int32(0), false},\n\t\t},\n\t\t{\n\t\t\t\"AsInt32OK/Int32/Insufficient Bytes\", Value.AsInt32OK,\n\t\t\tValue{Type: TypeInt32, Data: []byte{0x01, 0x02}},\n\t\t\tnil,\n\t\t\t[]any{int32(0), false},\n\t\t},\n\t\t{\n\t\t\t\"AsInt32OK/Int64/Insufficient Bytes\", Value.AsInt32OK,\n\t\t\tValue{Type: TypeInt64, Data: []byte{0x01, 0x02, 0x03}},\n\t\t\tnil,\n\t\t\t[]any{int32(0), false},\n\t\t},\n\t\t{\n\t\t\t\"AsInt32OK/Decimal128\", Value.AsInt32OK,\n\t\t\tValue{Type: TypeDecimal128, Data: AppendDecimal128(nil, 12345, 67890)},\n\t\t\tnil,\n\t\t\t[]any{int32(0), false},\n\t\t},\n\t\t{\n\t\t\t\"AsInt32OK/From Double\", Value.AsInt32OK,\n\t\t\tValue{Type: TypeDouble, Data: AppendDouble(nil, 42.7)},\n\t\t\tnil,\n\t\t\t[]any{int32(42), true},\n\t\t},\n\t\t{\n\t\t\t\"AsInt32OK/From Int32\", Value.AsInt32OK,\n\t\t\tValue{Type: TypeInt32, Data: AppendInt32(nil, 12345)},\n\t\t\tnil,\n\t\t\t[]any{int32(12345), true},\n\t\t},\n\t\t{\n\t\t\t\"AsInt32OK/From Int64\", Value.AsInt32OK,\n\t\t\tValue{Type: TypeInt64, Data: AppendInt64(nil, 98765)},\n\t\t\tnil,\n\t\t\t[]any{int32(98765), true},\n\t\t},\n\t\t{\n\t\t\t\"AsInt64/Not Number\", Value.AsInt64,\n\t\t\tValue{Type: TypeString},\n\t\t\tElementTypeError{\"bsoncore.Value.AsInt64\", TypeString},\n\t\t\tnil,\n\t\t},\n\t\t{\n\t\t\t\"AsInt64/Double/Insufficient Bytes\", Value.AsInt64,\n\t\t\tValue{Type: TypeDouble, Data: []byte{0x01, 0x02, 0x03}},\n\t\t\tNewInsufficientBytesError([]byte{0x01, 0x02, 0x03}, []byte{0x01, 0x02, 0x03}),\n\t\t\tnil,\n\t\t},\n\t\t{\n\t\t\t\"AsInt64/Int32/Insufficient Bytes\", Value.AsInt64,\n\t\t\tValue{Type: TypeInt32, Data: []byte{0x01, 0x02}},\n\t\t\tNewInsufficientBytesError([]byte{0x01, 0x02}, []byte{0x01, 0x02}),\n\t\t\tnil,\n\t\t},\n\t\t{\n\t\t\t\"AsInt64/Int64/Insufficient Bytes\", Value.AsInt64,\n\t\t\tValue{Type: TypeInt64, Data: []byte{0x01, 0x02, 0x03}},\n\t\t\tNewInsufficientBytesError([]byte{0x01, 0x02, 0x03}, []byte{0x01, 0x02, 0x03}),\n\t\t\tnil,\n\t\t},\n\t\t{\n\t\t\t\"AsInt64/Decimal128\", Value.AsInt64,\n\t\t\tValue{Type: TypeDecimal128, Data: AppendDecimal128(nil, 12345, 67890)},\n\t\t\tElementTypeError{\"bsoncore.Value.AsInt64\", TypeDecimal128},\n\t\t\tnil,\n\t\t},\n\t\t{\n\t\t\t\"AsInt64/From Double\", Value.AsInt64,\n\t\t\tValue{Type: TypeDouble, Data: AppendDouble(nil, 123456.9)},\n\t\t\tnil,\n\t\t\t[]any{int64(123456)},\n\t\t},\n\t\t{\n\t\t\t\"AsInt64/From Int32\", Value.AsInt64,\n\t\t\tValue{Type: TypeInt32, Data: AppendInt32(nil, 12345)},\n\t\t\tnil,\n\t\t\t[]any{int64(12345)},\n\t\t},\n\t\t{\n\t\t\t\"AsInt64/From Int64\", Value.AsInt64,\n\t\t\tValue{Type: TypeInt64, Data: AppendInt64(nil, 9876543210)},\n\t\t\tnil,\n\t\t\t[]any{int64(9876543210)},\n\t\t},\n\t\t{\n\t\t\t\"AsInt64OK/Not Number\", Value.AsInt64OK,\n\t\t\tValue{Type: TypeString},\n\t\t\tnil,\n\t\t\t[]any{int64(0), false},\n\t\t},\n\t\t{\n\t\t\t\"AsInt64OK/Double/Insufficient Bytes\", Value.AsInt64OK,\n\t\t\tValue{Type: TypeDouble, Data: []byte{0x01, 0x02, 0x03}},\n\t\t\tnil,\n\t\t\t[]any{int64(0), false},\n\t\t},\n\t\t{\n\t\t\t\"AsInt64OK/Int32/Insufficient Bytes\", Value.AsInt64OK,\n\t\t\tValue{Type: TypeInt32, Data: []byte{0x01, 0x02}},\n\t\t\tnil,\n\t\t\t[]any{int64(0), false},\n\t\t},\n\t\t{\n\t\t\t\"AsInt64OK/Int64/Insufficient Bytes\", Value.AsInt64OK,\n\t\t\tValue{Type: TypeInt64, Data: []byte{0x01, 0x02, 0x03}},\n\t\t\tnil,\n\t\t\t[]any{int64(0), false},\n\t\t},\n\t\t{\n\t\t\t\"AsInt64OK/Decimal128\", Value.AsInt64OK,\n\t\t\tValue{Type: TypeDecimal128, Data: AppendDecimal128(nil, 12345, 67890)},\n\t\t\tnil,\n\t\t\t[]any{int64(0), false},\n\t\t},\n\t\t{\n\t\t\t\"AsInt64OK/From Double\", Value.AsInt64OK,\n\t\t\tValue{Type: TypeDouble, Data: AppendDouble(nil, 123456.9)},\n\t\t\tnil,\n\t\t\t[]any{int64(123456), true},\n\t\t},\n\t\t{\n\t\t\t\"AsInt64OK/From Int32\", Value.AsInt64OK,\n\t\t\tValue{Type: TypeInt32, Data: AppendInt32(nil, 12345)},\n\t\t\tnil,\n\t\t\t[]any{int64(12345), true},\n\t\t},\n\t\t{\n\t\t\t\"AsInt64OK/From Int64\", Value.AsInt64OK,\n\t\t\tValue{Type: TypeInt64, Data: AppendInt64(nil, 9876543210)},\n\t\t\tnil,\n\t\t\t[]any{int64(9876543210), true},\n\t\t},\n\t\t{\n\t\t\t\"AsFloat64/Not Number\", Value.AsFloat64,\n\t\t\tValue{Type: TypeString},\n\t\t\tElementTypeError{\"bsoncore.Value.AsFloat64\", TypeString},\n\t\t\tnil,\n\t\t},\n\t\t{\n\t\t\t\"AsFloat64/Double/Insufficient Bytes\", Value.AsFloat64,\n\t\t\tValue{Type: TypeDouble, Data: []byte{0x01, 0x02, 0x03}},\n\t\t\tNewInsufficientBytesError([]byte{0x01, 0x02, 0x03}, []byte{0x01, 0x02, 0x03}),\n\t\t\tnil,\n\t\t},\n\t\t{\n\t\t\t\"AsFloat64/Int32/Insufficient Bytes\", Value.AsFloat64,\n\t\t\tValue{Type: TypeInt32, Data: []byte{0x01, 0x02}},\n\t\t\tNewInsufficientBytesError([]byte{0x01, 0x02}, []byte{0x01, 0x02}),\n\t\t\tnil,\n\t\t},\n\t\t{\n\t\t\t\"AsFloat64/Int64/Insufficient Bytes\", Value.AsFloat64,\n\t\t\tValue{Type: TypeInt64, Data: []byte{0x01, 0x02, 0x03}},\n\t\t\tNewInsufficientBytesError([]byte{0x01, 0x02, 0x03}, []byte{0x01, 0x02, 0x03}),\n\t\t\tnil,\n\t\t},\n\t\t{\n\t\t\t\"AsFloat64/Decimal128\", Value.AsFloat64,\n\t\t\tValue{Type: TypeDecimal128, Data: AppendDecimal128(nil, 12345, 67890)},\n\t\t\tElementTypeError{\"bsoncore.Value.AsFloat64\", TypeDecimal128},\n\t\t\tnil,\n\t\t},\n\t\t{\n\t\t\t\"AsFloat64/From Double\", Value.AsFloat64,\n\t\t\tValue{Type: TypeDouble, Data: AppendDouble(nil, 3.14159)},\n\t\t\tnil,\n\t\t\t[]any{float64(3.14159)},\n\t\t},\n\t\t{\n\t\t\t\"AsFloat64/From Int32\", Value.AsFloat64,\n\t\t\tValue{Type: TypeInt32, Data: AppendInt32(nil, 42)},\n\t\t\tnil,\n\t\t\t[]any{float64(42)},\n\t\t},\n\t\t{\n\t\t\t\"AsFloat64/From Int64\", Value.AsFloat64,\n\t\t\tValue{Type: TypeInt64, Data: AppendInt64(nil, 1234567890)},\n\t\t\tnil,\n\t\t\t[]any{float64(1234567890)},\n\t\t},\n\t\t{\n\t\t\t\"AsFloat64OK/Not Number\", Value.AsFloat64OK,\n\t\t\tValue{Type: TypeString},\n\t\t\tnil,\n\t\t\t[]any{float64(0), false},\n\t\t},\n\t\t{\n\t\t\t\"AsFloat64OK/Double/Insufficient Bytes\", Value.AsFloat64OK,\n\t\t\tValue{Type: TypeDouble, Data: []byte{0x01, 0x02, 0x03}},\n\t\t\tnil,\n\t\t\t[]any{float64(0), false},\n\t\t},\n\t\t{\n\t\t\t\"AsFloat64OK/Int32/Insufficient Bytes\", Value.AsFloat64OK,\n\t\t\tValue{Type: TypeInt32, Data: []byte{0x01, 0x02}},\n\t\t\tnil,\n\t\t\t[]any{float64(0), false},\n\t\t},\n\t\t{\n\t\t\t\"AsFloat64OK/Int64/Insufficient Bytes\", Value.AsFloat64OK,\n\t\t\tValue{Type: TypeInt64, Data: []byte{0x01, 0x02, 0x03}},\n\t\t\tnil,\n\t\t\t[]any{float64(0), false},\n\t\t},\n\t\t{\n\t\t\t\"AsFloat64OK/Decimal128\", Value.AsFloat64OK,\n\t\t\tValue{Type: TypeDecimal128, Data: AppendDecimal128(nil, 12345, 67890)},\n\t\t\tnil,\n\t\t\t[]any{float64(0), false},\n\t\t},\n\t\t{\n\t\t\t\"AsFloat64OK/From Double\", Value.AsFloat64OK,\n\t\t\tValue{Type: TypeDouble, Data: AppendDouble(nil, 3.14159)},\n\t\t\tnil,\n\t\t\t[]any{float64(3.14159), true},\n\t\t},\n\t\t{\n\t\t\t\"AsFloat64OK/From Int32\", Value.AsFloat64OK,\n\t\t\tValue{Type: TypeInt32, Data: AppendInt32(nil, 42)},\n\t\t\tnil,\n\t\t\t[]any{float64(42), true},\n\t\t},\n\t\t{\n\t\t\t\"AsFloat64OK/From Int64\", Value.AsFloat64OK,\n\t\t\tValue{Type: TypeInt64, Data: AppendInt64(nil, 1234567890)},\n\t\t\tnil,\n\t\t\t[]any{float64(1234567890), true},\n\t\t},\n\t\t{\n\t\t\t\"Timestamp.String/Success\", Value.String,\n\t\t\tValue{Type: TypeTimestamp, Data: AppendTimestamp(nil, 12345, 67890)},\n\t\t\tnil,\n\t\t\t[]any{\"{\\\"$timestamp\\\":{\\\"t\\\":12345,\\\"i\\\":67890}}\"},\n\t\t},\n\t}\n\n\tfor _, tc := range testCases {\n\t\ttc := tc\n\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tdefer func() {\n\t\t\t\terr := recover()\n\t\t\t\tif !cmp.Equal(err, tc.panicErr, cmp.Comparer(compareErrors)) {\n\t\t\t\t\tt.Errorf(\"Did not receive expected panic error. got %v; want %v\", err, tc.panicErr)\n\t\t\t\t}\n\t\t\t}()\n\n\t\t\tfn := reflect.ValueOf(tc.fn)\n\t\t\tif fn.Kind() != reflect.Func || fn.Type().NumIn() != 1 || fn.Type().In(0) != reflect.TypeOf(Value{}) {\n\t\t\t\tt.Fatalf(\"test case field fn must be a function with 1 parameter that is a Value, but it is %v\", fn.Type())\n\t\t\t}\n\t\t\tgot := fn.Call([]reflect.Value{reflect.ValueOf(tc.val)})\n\t\t\twant := make([]reflect.Value, 0, len(tc.ret))\n\t\t\tfor _, ret := range tc.ret {\n\t\t\t\twant = append(want, reflect.ValueOf(ret))\n\t\t\t}\n\t\t\tif len(got) != len(want) {\n\t\t\t\tt.Fatalf(\"incorrect number of values returned. got %d; want %d\", len(got), len(want))\n\t\t\t}\n\n\t\t\tfor idx := range got {\n\t\t\t\tgotv, wantv := got[idx].Interface(), want[idx].Interface()\n\t\t\t\tif !cmp.Equal(gotv, wantv) {\n\t\t\t\t\tt.Errorf(\"return values at index %d are not equal. got %v; want %v\", idx, gotv, wantv)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n\nvar valueStringTestCases = []struct {\n\tdescription string\n\tval         Value\n\twant        string\n}{\n\t{\n\t\tdescription: \"string value\",\n\t\tval: Value{\n\t\t\tType: TypeString, Data: AppendString(nil, \"abcdefgh\"),\n\t\t},\n\t\twant: `\"abcdefgh\"`,\n\t},\n\n\t{\n\t\tdescription: \"value with special characters\",\n\t\tval: Value{\n\t\t\tType: TypeString,\n\t\t\tData: AppendString(nil, \"!@#$%^&*()\"),\n\t\t},\n\t\twant: `\"!@#$%^\\u0026*()\"`,\n\t},\n\n\t{\n\t\tdescription: \"TypeEmbeddedDocument\",\n\t\tval: Value{\n\t\t\tType: TypeEmbeddedDocument,\n\t\t\tData: BuildDocument(nil,\n\t\t\t\tAppendInt32Element(nil, \"number\", 123),\n\t\t\t),\n\t\t},\n\t\twant: `{\"number\": {\"$numberInt\":\"123\"}}`,\n\t},\n\n\t{\n\t\tdescription: \"TypeArray\",\n\t\tval: Value{\n\t\t\tType: TypeArray,\n\t\t\tData: BuildArray(nil,\n\t\t\t\tValue{\n\t\t\t\t\tType: TypeString,\n\t\t\t\t\tData: AppendString(nil, \"abc\"),\n\t\t\t\t},\n\t\t\t\tValue{\n\t\t\t\t\tType: TypeInt32,\n\t\t\t\t\tData: AppendInt32(nil, 123),\n\t\t\t\t},\n\t\t\t\tValue{\n\t\t\t\t\tType: TypeBoolean,\n\t\t\t\t\tData: AppendBoolean(nil, true),\n\t\t\t\t},\n\t\t\t),\n\t\t},\n\t\twant: `[\"abc\",{\"$numberInt\":\"123\"},true]`,\n\t},\n\n\t{\n\t\tdescription: \"TypeDouble\",\n\t\tval: Value{\n\t\t\tType: TypeDouble,\n\t\t\tData: AppendDouble(nil, 123.456),\n\t\t},\n\t\twant: `{\"$numberDouble\":\"123.456\"}`,\n\t},\n\n\t{\n\t\tdescription: \"TypeBinary\",\n\t\tval: Value{\n\t\t\tType: TypeBinary,\n\t\t\tData: AppendBinary(nil, 0x00, []byte{0x01, 0x02, 0x03}),\n\t\t},\n\t\twant: `{\"$binary\":{\"base64\":\"AQID\",\"subType\":\"00\"}}`,\n\t},\n\n\t{\n\t\tdescription: \"TypeUndefined\",\n\t\tval: Value{\n\t\t\tType: TypeUndefined,\n\t\t},\n\t\twant: `{\"$undefined\":true}`,\n\t},\n\n\t{\n\t\tdescription: \"TypeObjectID\",\n\t\tval: Value{\n\t\t\tType: TypeObjectID,\n\t\t\tData: AppendObjectID(nil, [12]byte{0x60, 0xd4, 0xc2, 0x1f, 0x4e, 0x60, 0x4a, 0x0c, 0x8b, 0x2e, 0x9c, 0x3f}),\n\t\t},\n\t\twant: `{\"$oid\":\"60d4c21f4e604a0c8b2e9c3f\"}`,\n\t},\n\n\t{\n\t\tdescription: \"TypeBoolean\",\n\t\tval: Value{\n\t\t\tType: TypeBoolean,\n\t\t\tData: AppendBoolean(nil, true),\n\t\t},\n\t\twant: `true`,\n\t},\n\n\t{\n\t\tdescription: \"TypeDateTime\",\n\t\tval: Value{\n\t\t\tType: TypeDateTime,\n\t\t\tData: AppendDateTime(nil, 1234567890),\n\t\t},\n\t\twant: `{\"$date\":{\"$numberLong\":\"1234567890\"}}`,\n\t},\n\n\t{\n\t\tdescription: \"TypeNull\",\n\t\tval: Value{\n\t\t\tType: TypeNull,\n\t\t},\n\t\twant: `null`,\n\t},\n\n\t{\n\t\tdescription: \"TypeRegex\",\n\t\tval: Value{\n\t\t\tType: TypeRegex,\n\t\t\tData: AppendRegex(nil, \"pattern\", \"i\"),\n\t\t},\n\t\twant: `{\"$regularExpression\":{\"pattern\":\"pattern\",\"options\":\"i\"}}`,\n\t},\n\n\t{\n\t\tdescription: \"TypeDBPointer\",\n\t\tval: Value{\n\t\t\tType: TypeDBPointer,\n\t\t\tData: AppendDBPointer(nil, \"namespace\", [12]byte{0x60, 0xd4, 0xc2, 0x1f, 0x4e, 0x60, 0x4a, 0x0c, 0x8b, 0x2e, 0x9c, 0x3f}),\n\t\t},\n\t\twant: `{\"$dbPointer\":{\"$ref\":\"namespace\",\"$id\":{\"$oid\":\"60d4c21f4e604a0c8b2e9c3f\"}}}`,\n\t},\n\n\t{\n\t\tdescription: \"TypeJavaScript\",\n\t\tval: Value{\n\t\t\tType: TypeJavaScript,\n\t\t\tData: AppendJavaScript(nil, \"code\"),\n\t\t},\n\t\twant: `{\"$code\":\"code\"}`,\n\t},\n\n\t{\n\t\tdescription: \"TypeSymbol\",\n\t\tval: Value{\n\t\t\tType: TypeSymbol,\n\t\t\tData: AppendSymbol(nil, \"symbol\"),\n\t\t},\n\t\twant: `{\"$symbol\":\"symbol\"}`,\n\t},\n\n\t{\n\t\tdescription: \"TypeCodeWithScope\",\n\t\tval: Value{\n\t\t\tType: TypeCodeWithScope,\n\t\t\tData: AppendCodeWithScope(nil, \"code\",\n\t\t\t\tBuildDocument(nil, AppendStringElement(nil, \"key\", \"value\")),\n\t\t\t),\n\t\t},\n\t\twant: `{\"$code\":code,\"$scope\":{\"key\": \"value\"}}`,\n\t},\n\n\t{\n\t\tdescription: \"TypeInt32\",\n\t\tval: Value{\n\t\t\tType: TypeInt32,\n\t\t\tData: AppendInt32(nil, 123),\n\t\t},\n\t\twant: `{\"$numberInt\":\"123\"}`,\n\t},\n\n\t{\n\t\tdescription: \"TypeTimestamp\",\n\t\tval: Value{\n\t\t\tType: TypeTimestamp,\n\t\t\tData: AppendTimestamp(nil, 123, 456),\n\t\t},\n\t\twant: `{\"$timestamp\":{\"t\":123,\"i\":456}}`,\n\t},\n\n\t{\n\t\tdescription: \"TypeInt64\",\n\t\tval: Value{\n\t\t\tType: TypeInt64,\n\t\t\tData: AppendInt64(nil, 1234567890),\n\t\t},\n\t\twant: `{\"$numberLong\":\"1234567890\"}`,\n\t},\n\n\t{\n\t\tdescription: \"TypeDecimal128\",\n\t\tval: Value{\n\t\t\tType: TypeDecimal128,\n\t\t\tData: AppendDecimal128(nil, 0x3040000000000000, 0x0000000000000000),\n\t\t},\n\t\twant: `{\"$numberDecimal\":\"0\"}`,\n\t},\n\n\t{\n\t\tdescription: \"TypeMinKey\",\n\t\tval: Value{\n\t\t\tType: TypeMinKey,\n\t\t},\n\t\twant: `{\"$minKey\":1}`,\n\t},\n\n\t{\n\t\tdescription: \"TypeMaxKey\",\n\t\tval: Value{\n\t\t\tType: TypeMaxKey,\n\t\t},\n\t\twant: `{\"$maxKey\":1}`,\n\t},\n}\n\nfunc TestValue_String(t *testing.T) {\n\tfor _, tc := range valueStringTestCases {\n\t\tt.Run(tc.description, func(t *testing.T) {\n\t\t\tgot := tc.val.String()\n\t\t\tassert.Equal(t, tc.want, got, \"expected string %s, got %s\", tc.want, got)\n\t\t})\n\t}\n}\n\nfunc TestValue_StringN(t *testing.T) {\n\tfor _, tc := range valueStringTestCases {\n\t\tfor n := -1; n <= len(tc.want)+1; n++ {\n\t\t\tt.Run(fmt.Sprintf(\"%s n==%d\", tc.description, n), func(t *testing.T) {\n\t\t\t\tgot, truncated := tc.val.StringN(n)\n\t\t\t\tl := n\n\t\t\t\ttoBeTruncated := true\n\t\t\t\tif l >= len(tc.want) || l < 0 {\n\t\t\t\t\tl = len(tc.want)\n\t\t\t\t\ttoBeTruncated = false\n\t\t\t\t}\n\t\t\t\twant := tc.want[:l]\n\t\t\t\tassert.Equal(t, want, got, \"expected string %s, got %s\", tc.want, got)\n\t\t\t\tassert.Equal(t, toBeTruncated, truncated, \"expected truncated to be %t, got %t\", toBeTruncated, truncated)\n\t\t\t})\n\t\t}\n\t}\n}\n\nfunc TestArray_StringN_Multibyte(t *testing.T) {\n\tmultiByteString := Value{\n\t\tType: TypeString,\n\t\tData: AppendString(nil, \"𨉟呐㗂越\"),\n\t}\n\tfor i, tc := range []struct {\n\t\tn         int\n\t\twant      string\n\t\ttruncated bool\n\t}{\n\t\t{6, `\"𨉟`, true},\n\t\t{8, `\"𨉟`, true},\n\t\t{10, `\"𨉟呐`, true},\n\t\t{15, `\"𨉟呐㗂越\"`, false},\n\t\t{21, `\"𨉟呐㗂越\"`, false},\n\t} {\n\t\tt.Run(fmt.Sprintf(\"case %d\", i), func(t *testing.T) {\n\t\t\tgot, truncated := multiByteString.StringN(tc.n)\n\t\t\tassert.Equal(t, tc.want, got, \"expected string %s, got %s\", tc.want, got)\n\t\t\tassert.Equal(t, tc.truncated, truncated, \"expected truncated to be %t, got %t\", tc.truncated, truncated)\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "x/mongo/driver/auth/auth.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage auth\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"strings\"\n\n\t\"go.mongodb.org/mongo-driver/v2/mongo/address\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/description\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/mnet\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/operation\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/session\"\n)\n\nconst sourceExternal = \"$external\"\n\n// AuthenticatorFactory constructs an authenticator.\ntype AuthenticatorFactory func(*Cred, *http.Client) (Authenticator, error)\n\nvar authFactories = make(map[string]AuthenticatorFactory)\n\nfunc init() {\n\tRegisterAuthenticatorFactory(\"\", newDefaultAuthenticator)\n\tRegisterAuthenticatorFactory(SCRAMSHA1, newScramSHA1Authenticator)\n\tRegisterAuthenticatorFactory(SCRAMSHA256, newScramSHA256Authenticator)\n\tRegisterAuthenticatorFactory(PLAIN, newPlainAuthenticator)\n\tRegisterAuthenticatorFactory(GSSAPI, newGSSAPIAuthenticator)\n\tRegisterAuthenticatorFactory(MongoDBX509, newMongoDBX509Authenticator)\n\tRegisterAuthenticatorFactory(MongoDBAWS, newMongoDBAWSAuthenticator)\n\tRegisterAuthenticatorFactory(MongoDBOIDC, newOIDCAuthenticator)\n}\n\n// CreateAuthenticator creates an authenticator.\nfunc CreateAuthenticator(name string, cred *Cred, httpClient *http.Client) (Authenticator, error) {\n\t// Return a custom error to indicate why auth mechanism \"MONGODB-CR\" is\n\t// missing, even though it was previously available.\n\tif strings.EqualFold(name, \"MONGODB-CR\") {\n\t\treturn nil, errors.New(`auth mechanism \"MONGODB-CR\" is no longer available in any supported version of MongoDB`)\n\t}\n\n\tif f, ok := authFactories[name]; ok {\n\t\treturn f(cred, httpClient)\n\t}\n\n\treturn nil, newAuthError(fmt.Sprintf(\"unknown authenticator: %s\", name), nil)\n}\n\n// RegisterAuthenticatorFactory registers the authenticator factory.\nfunc RegisterAuthenticatorFactory(name string, factory AuthenticatorFactory) {\n\tauthFactories[name] = factory\n}\n\n// HandshakeOptions packages options that can be passed to the Handshaker()\n// function.  DBUser is optional but must be of the form <dbname.username>;\n// if non-empty, then the connection will do SASL mechanism negotiation.\ntype HandshakeOptions struct {\n\tAppName               string\n\tAuthenticator         Authenticator\n\tCompressors           []string\n\tDBUser                string\n\tPerformAuthentication func(description.Server) bool\n\tClusterClock          *session.ClusterClock\n\tServerAPI             *driver.ServerAPIOptions\n\tLoadBalanced          bool\n\n\t// Fields provided by a library that wraps the Go Driver.\n\tOuterLibraryName     string\n\tOuterLibraryVersion  string\n\tOuterLibraryPlatform string\n}\n\ntype authHandshaker struct {\n\twrapped driver.Handshaker\n\toptions *HandshakeOptions\n\n\thandshakeInfo driver.HandshakeInformation\n\tconversation  SpeculativeConversation\n}\n\nvar _ driver.Handshaker = (*authHandshaker)(nil)\n\n// GetHandshakeInformation performs the initial MongoDB handshake to retrieve the required information for the provided\n// connection.\nfunc (ah *authHandshaker) GetHandshakeInformation(\n\tctx context.Context,\n\taddr address.Address,\n\tconn *mnet.Connection,\n) (driver.HandshakeInformation, error) {\n\tif ah.wrapped != nil {\n\t\treturn ah.wrapped.GetHandshakeInformation(ctx, addr, conn)\n\t}\n\n\top := operation.NewHello().\n\t\tAppName(ah.options.AppName).\n\t\tCompressors(ah.options.Compressors).\n\t\tSASLSupportedMechs(ah.options.DBUser).\n\t\tClusterClock(ah.options.ClusterClock).\n\t\tServerAPI(ah.options.ServerAPI).\n\t\tLoadBalanced(ah.options.LoadBalanced).\n\t\tOuterLibraryName(ah.options.OuterLibraryName).\n\t\tOuterLibraryVersion(ah.options.OuterLibraryVersion).\n\t\tOuterLibraryPlatform(ah.options.OuterLibraryPlatform)\n\n\tif ah.options.Authenticator != nil {\n\t\tif speculativeAuth, ok := ah.options.Authenticator.(SpeculativeAuthenticator); ok {\n\t\t\tvar err error\n\t\t\tah.conversation, err = speculativeAuth.CreateSpeculativeConversation()\n\t\t\tif err != nil {\n\t\t\t\treturn driver.HandshakeInformation{}, newAuthError(\"failed to create conversation\", err)\n\t\t\t}\n\n\t\t\t// It is possible for the speculative conversation to be nil even without error if the authenticator\n\t\t\t// cannot perform speculative authentication. An example of this is MONGODB-OIDC when there is\n\t\t\t// no AccessToken in the cache.\n\t\t\tif ah.conversation != nil {\n\t\t\t\tfirstMsg, err := ah.conversation.FirstMessage()\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn driver.HandshakeInformation{}, newAuthError(\"failed to create speculative authentication message\", err)\n\t\t\t\t}\n\n\t\t\t\top = op.SpeculativeAuthenticate(firstMsg)\n\t\t\t}\n\t\t}\n\t}\n\n\tvar err error\n\tah.handshakeInfo, err = op.GetHandshakeInformation(ctx, addr, conn)\n\tif err != nil {\n\t\treturn driver.HandshakeInformation{}, newAuthError(\"handshake failure\", err)\n\t}\n\treturn ah.handshakeInfo, nil\n}\n\n// FinishHandshake performs authentication for conn if necessary.\nfunc (ah *authHandshaker) FinishHandshake(ctx context.Context, conn *mnet.Connection) error {\n\tperformAuth := ah.options.PerformAuthentication\n\tif performAuth == nil {\n\t\tperformAuth = func(serv description.Server) bool {\n\t\t\t// Authentication is possible against all server types except arbiters\n\t\t\treturn serv.Kind != description.ServerKindRSArbiter\n\t\t}\n\t}\n\n\tif performAuth(conn.Description()) && ah.options.Authenticator != nil {\n\t\tcfg := &driver.AuthConfig{\n\t\t\tConnection:    conn,\n\t\t\tClusterClock:  ah.options.ClusterClock,\n\t\t\tHandshakeInfo: ah.handshakeInfo,\n\t\t\tServerAPI:     ah.options.ServerAPI,\n\t\t}\n\n\t\tif err := ah.authenticate(ctx, cfg); err != nil {\n\t\t\treturn newAuthError(\"auth error\", err)\n\t\t}\n\t}\n\n\tif ah.wrapped == nil {\n\t\treturn nil\n\t}\n\treturn ah.wrapped.FinishHandshake(ctx, conn)\n}\n\nfunc (ah *authHandshaker) authenticate(ctx context.Context, cfg *driver.AuthConfig) error {\n\t// If the initial hello reply included a response to the speculative authentication attempt, we only need to\n\t// conduct the remainder of the conversation.\n\tif speculativeResponse := ah.handshakeInfo.SpeculativeAuthenticate; speculativeResponse != nil {\n\t\t// Defensively ensure that the server did not include a response if speculative auth was not attempted.\n\t\tif ah.conversation == nil {\n\t\t\treturn errors.New(\"speculative auth was not attempted but the server included a response\")\n\t\t}\n\t\treturn ah.conversation.Finish(ctx, cfg, speculativeResponse)\n\t}\n\n\t// If the server does not support speculative authentication or the first attempt was not successful, we need to\n\t// perform authentication from scratch.\n\treturn ah.options.Authenticator.Auth(ctx, cfg)\n}\n\n// Handshaker creates a connection handshaker for the given authenticator.\nfunc Handshaker(h driver.Handshaker, options *HandshakeOptions) driver.Handshaker {\n\treturn &authHandshaker{\n\t\twrapped: h,\n\t\toptions: options,\n\t}\n}\n\n// Config holds the information necessary to perform an authentication attempt.\ntype Config struct {\n\tConnection    *mnet.Connection\n\tClusterClock  *session.ClusterClock\n\tHandshakeInfo driver.HandshakeInformation\n\tServerAPI     *driver.ServerAPIOptions\n\tHTTPClient    *http.Client\n}\n\n// Authenticator handles authenticating a connection.\ntype Authenticator = driver.Authenticator\n\nfunc newAuthError(msg string, inner error) error {\n\treturn &Error{\n\t\tmessage: msg,\n\t\tinner:   inner,\n\t}\n}\n\nfunc newError(err error, mech string) error {\n\treturn &Error{\n\t\tmessage: fmt.Sprintf(\"unable to authenticate using mechanism \\\"%s\\\"\", mech),\n\t\tinner:   err,\n\t}\n}\n\n// Error is an error that occurred during authentication.\ntype Error struct {\n\tmessage string\n\tinner   error\n}\n\nfunc (e *Error) Error() string {\n\tif e.inner == nil {\n\t\treturn e.message\n\t}\n\treturn fmt.Sprintf(\"%s: %s\", e.message, e.inner)\n}\n\n// Inner returns the wrapped error.\nfunc (e *Error) Inner() error {\n\treturn e.inner\n}\n\n// Unwrap returns the underlying error.\nfunc (e *Error) Unwrap() error {\n\treturn e.inner\n}\n\n// Message returns the message.\nfunc (e *Error) Message() string {\n\treturn e.message\n}\n"
  },
  {
    "path": "x/mongo/driver/auth/auth_spec_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage auth_test\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io/ioutil\"\n\t\"path\"\n\t\"testing\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/require\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/spectest\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n)\n\ntype credential struct {\n\tUsername  string\n\tPassword  *string\n\tSource    string\n\tMechanism string\n\tMechProps map[string]any `json:\"mechanism_properties\"`\n}\n\ntype testCase struct {\n\tDescription string\n\tURI         string\n\tValid       bool\n\tCredential  *credential\n}\n\ntype testContainer struct {\n\tTests []testCase\n}\n\n// Note a test supporting the deprecated gssapiServiceName property was removed from data/auth/auth_tests.json\nvar authTestsDir = spectest.Path(\"auth/tests/legacy\")\n\nfunc runTestsInFile(t *testing.T, dirname string, filename string) {\n\tfilepath := path.Join(dirname, filename)\n\tcontent, err := ioutil.ReadFile(filepath)\n\trequire.NoError(t, err)\n\n\tvar container testContainer\n\trequire.NoError(t, json.Unmarshal(content, &container))\n\n\tt.Run(filename, func(t *testing.T) {\n\t\tfor _, testCase := range container.Tests {\n\t\t\ttestCase := testCase // Capture range variable.\n\n\t\t\tt.Run(testCase.Description, func(t *testing.T) {\n\t\t\t\tspectest.CheckSkip(t)\n\n\t\t\t\trunTest(t, testCase)\n\t\t\t})\n\t\t}\n\t})\n}\n\nfunc runTest(t *testing.T, test testCase) {\n\topts := options.Client().ApplyURI(test.URI)\n\n\tif test.Valid {\n\t\trequire.NoError(t, opts.Validate())\n\t} else {\n\t\trequire.Error(t, opts.Validate())\n\n\t\treturn\n\t}\n\n\tif test.Credential == nil {\n\t\trequire.Nil(t, opts.Auth)\n\t\treturn\n\t}\n\trequire.NotNil(t, opts.Auth)\n\trequire.Equal(t, test.Credential.Username, opts.Auth.Username)\n\n\tif test.Credential.Password == nil {\n\t\trequire.False(t, opts.Auth.PasswordSet)\n\t} else {\n\t\trequire.True(t, opts.Auth.PasswordSet)\n\t\trequire.Equal(t, *test.Credential.Password, opts.Auth.Password)\n\t}\n\n\trequire.Equal(t, test.Credential.Source, opts.Auth.AuthSource)\n\n\trequire.Equal(t, test.Credential.Mechanism, opts.Auth.AuthMechanism)\n\n\tif len(test.Credential.MechProps) > 0 {\n\t\trequire.Equal(t, mapInterfaceToString(test.Credential.MechProps), opts.Auth.AuthMechanismProperties)\n\t} else {\n\t\trequire.Equal(t, 0, len(opts.Auth.AuthMechanismProperties))\n\t}\n}\n\n// Convert each any value in the map to a string.\nfunc mapInterfaceToString(m map[string]any) map[string]string {\n\tout := make(map[string]string)\n\n\tfor key, value := range m {\n\t\tout[key] = fmt.Sprint(value)\n\t}\n\n\treturn out\n}\n\n// Test case for all connection string spec tests.\nfunc TestAuthSpec(t *testing.T) {\n\tfor _, file := range spectest.FindJSONFilesInDir(t, authTestsDir) {\n\t\trunTestsInFile(t, authTestsDir, file)\n\t}\n}\n"
  },
  {
    "path": "x/mongo/driver/auth/auth_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage auth_test\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"testing\"\n\n\t\"github.com/google/go-cmp/cmp\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/require\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/auth\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/description\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/drivertest\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/mnet\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/wiremessage\"\n)\n\nfunc TestCreateAuthenticator(t *testing.T) {\n\ttests := []struct {\n\t\tname   string\n\t\tsource string\n\t\tauth   auth.Authenticator\n\t\terr    error\n\t}{\n\t\t{name: \"\", auth: &auth.DefaultAuthenticator{}},\n\t\t{name: \"SCRAM-SHA-1\", auth: &auth.ScramAuthenticator{}},\n\t\t{name: \"SCRAM-SHA-256\", auth: &auth.ScramAuthenticator{}},\n\t\t{name: \"MONGODB-CR\", err: errors.New(`auth mechanism \"MONGODB-CR\" is no longer available in any supported version of MongoDB`)},\n\t\t{name: \"PLAIN\", auth: &auth.PlainAuthenticator{}},\n\t\t{name: \"MONGODB-X509\", auth: &auth.MongoDBX509Authenticator{}},\n\t}\n\n\tfor _, test := range tests {\n\t\tt.Run(test.name, func(t *testing.T) {\n\t\t\tcred := &auth.Cred{\n\t\t\t\tUsername:    \"user\",\n\t\t\t\tPassword:    \"pencil\",\n\t\t\t\tPasswordSet: true,\n\t\t\t}\n\n\t\t\ta, err := auth.CreateAuthenticator(test.name, cred, &http.Client{})\n\t\t\tif test.err != nil {\n\t\t\t\trequire.EqualError(t, err, test.err.Error())\n\t\t\t\treturn\n\t\t\t}\n\t\t\trequire.NoError(t, err)\n\t\t\trequire.IsType(t, test.auth, a)\n\t\t})\n\t}\n}\n\nfunc compareResponses(t *testing.T, wm []byte, expectedPayload bsoncore.Document, dbName string) {\n\t_, _, _, opcode, wm, ok := wiremessage.ReadHeader(wm)\n\tif !ok {\n\t\tt.Fatalf(\"wiremessage is too short to unmarshal\")\n\t}\n\tvar actualPayload bsoncore.Document\n\tif opcode == wiremessage.OpMsg {\n\t\t// Append the $db field.\n\t\telems, err := expectedPayload.Elements()\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"expectedPayload is not valid: %v\", err)\n\t\t}\n\t\telems = append(elems, bsoncore.AppendStringElement(nil, \"$db\", dbName))\n\t\telems = append(elems, bsoncore.AppendDocumentElement(nil,\n\t\t\t\"$readPreference\",\n\t\t\tbsoncore.BuildDocumentFromElements(nil, bsoncore.AppendStringElement(nil, \"mode\", \"primaryPreferred\")),\n\t\t))\n\t\tbslc := make([][]byte, 0, len(elems)) // BuildDocumentFromElements takes a [][]byte, not a []bsoncore.Element.\n\t\tfor _, elem := range elems {\n\t\t\tbslc = append(bslc, elem)\n\t\t}\n\t\texpectedPayload = bsoncore.BuildDocumentFromElements(nil, bslc...)\n\n\t\t_, wm, ok := wiremessage.ReadMsgFlags(wm)\n\t\tif !ok {\n\t\t\tt.Fatalf(\"wiremessage is too short to unmarshal\")\n\t\t}\n\tloop:\n\t\tfor {\n\t\t\tvar stype wiremessage.SectionType\n\t\t\tstype, wm, ok = wiremessage.ReadMsgSectionType(wm)\n\t\t\tif !ok {\n\t\t\t\tt.Fatalf(\"wiremessage is too short to unmarshal\")\n\t\t\t}\n\t\t\tswitch stype {\n\t\t\tcase wiremessage.DocumentSequence:\n\t\t\t\t_, _, wm, ok = wiremessage.ReadMsgSectionDocumentSequence(wm)\n\t\t\t\tif !ok {\n\t\t\t\t\tt.Fatalf(\"wiremessage is too short to unmarshal\")\n\t\t\t\t}\n\t\t\tcase wiremessage.SingleDocument:\n\t\t\t\tactualPayload, _, ok = wiremessage.ReadMsgSectionSingleDocument(wm)\n\t\t\t\tif !ok {\n\t\t\t\t\tt.Fatalf(\"wiremessage is too short to unmarshal\")\n\t\t\t\t}\n\t\t\t\tbreak loop\n\t\t\t}\n\t\t}\n\t}\n\n\tif !cmp.Equal(actualPayload, expectedPayload) {\n\t\tt.Errorf(\"Payloads don't match. got %v; want %v\", actualPayload, expectedPayload)\n\t}\n}\n\ntype testAuthenticator struct{}\n\nfunc (a *testAuthenticator) Auth(context.Context, *driver.AuthConfig) error {\n\treturn fmt.Errorf(\"test error\")\n}\n\nfunc (a *testAuthenticator) Reauth(context.Context, *driver.AuthConfig) error {\n\treturn nil\n}\n\nfunc TestPerformAuthentication(t *testing.T) {\n\tt.Parallel()\n\n\tcases := []struct {\n\t\tname                   string\n\t\tauthenticateToAnything bool\n\t\trequire                func(*testing.T, error)\n\t}{\n\t\t{\n\t\t\tname:                   \"positive\",\n\t\t\tauthenticateToAnything: true,\n\t\t\trequire: func(t *testing.T, err error) {\n\t\t\t\trequire.EqualError(t, err, \"auth error: test error\")\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname:                   \"negative\",\n\t\t\tauthenticateToAnything: false,\n\t\t\trequire: func(t *testing.T, err error) {\n\t\t\t\trequire.NoError(t, err)\n\t\t\t},\n\t\t},\n\t}\n\tmnetconn := mnet.NewConnection(&drivertest.ChannelConn{})\n\tfor _, tc := range cases {\n\t\ttc := tc\n\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\thandshaker := auth.Handshaker(nil, &auth.HandshakeOptions{\n\t\t\t\tAuthenticator: &testAuthenticator{},\n\t\t\t\tPerformAuthentication: func(description.Server) bool {\n\t\t\t\t\treturn tc.authenticateToAnything\n\t\t\t\t},\n\t\t\t})\n\n\t\t\terr := handshaker.FinishHandshake(context.Background(), mnetconn)\n\t\t\ttc.require(t, err)\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "x/mongo/driver/auth/aws_conv.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage auth\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"crypto/rand\"\n\t\"encoding/base64\"\n\t\"errors\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"strings\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/aws/credentials\"\n\tv4signer \"go.mongodb.org/mongo-driver/v2/internal/aws/signer/v4\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n)\n\ntype clientState int\n\nconst (\n\tclientStarting clientState = iota\n\tclientFirst\n\tclientFinal\n\tclientDone\n)\n\ntype awsConversation struct {\n\tstate       clientState\n\tvalid       bool\n\tnonce       []byte\n\tcredentials *credentials.Credentials\n}\n\ntype serverMessage struct {\n\tNonce bson.Binary `bson:\"s\"`\n\tHost  string      `bson:\"h\"`\n}\n\nconst (\n\tamzDateFormat       = \"20060102T150405Z\"\n\tdefaultRegion       = \"us-east-1\"\n\tmaxHostLength       = 255\n\tresponceNonceLength = 64\n)\n\n// Step takes a string provided from a server (or just an empty string for the\n// very first conversation step) and attempts to move the authentication\n// conversation forward.  It returns a string to be sent to the server or an\n// error if the server message is invalid.  Calling Step after a conversation\n// completes is also an error.\nfunc (ac *awsConversation) Step(challenge []byte) (response []byte, err error) {\n\tswitch ac.state {\n\tcase clientStarting:\n\t\tac.state = clientFirst\n\t\tresponse = ac.firstMsg()\n\tcase clientFirst:\n\t\tac.state = clientFinal\n\t\tresponse, err = ac.finalMsg(challenge)\n\tcase clientFinal:\n\t\tac.state = clientDone\n\t\tac.valid = true\n\tdefault:\n\t\tresponse, err = nil, errors.New(\"conversation already completed\")\n\t}\n\treturn\n}\n\n// Done returns true if the conversation is completed or has errored.\nfunc (ac *awsConversation) Done() bool {\n\treturn ac.state == clientDone\n}\n\n// Valid returns true if the conversation successfully authenticated with the\n// server, including counter-validation that the server actually has the\n// user's stored credentials.\nfunc (ac *awsConversation) Valid() bool {\n\treturn ac.valid\n}\n\nfunc getRegion(host string) (string, error) {\n\tregion := defaultRegion\n\n\tif len(host) == 0 {\n\t\treturn \"\", errors.New(\"invalid STS host: empty\")\n\t}\n\tif len(host) > maxHostLength {\n\t\treturn \"\", errors.New(\"invalid STS host: too large\")\n\t}\n\t// The implicit region for sts.amazonaws.com is us-east-1\n\tif host == \"sts.amazonaws.com\" {\n\t\treturn region, nil\n\t}\n\tif strings.HasPrefix(host, \".\") || strings.HasSuffix(host, \".\") || strings.Contains(host, \"..\") {\n\t\treturn \"\", errors.New(\"invalid STS host: empty part\")\n\t}\n\n\t// If the host has multiple parts, the second part is the region\n\tparts := strings.Split(host, \".\")\n\tif len(parts) >= 2 {\n\t\tregion = parts[1]\n\t}\n\n\treturn region, nil\n}\n\nfunc (ac *awsConversation) firstMsg() []byte {\n\t// Values are cached for use in final message parameters\n\tac.nonce = make([]byte, 32)\n\t_, _ = rand.Read(ac.nonce)\n\n\tidx, msg := bsoncore.AppendDocumentStart(nil)\n\tmsg = bsoncore.AppendInt32Element(msg, \"p\", 110)\n\tmsg = bsoncore.AppendBinaryElement(msg, \"r\", 0x00, ac.nonce)\n\tmsg, _ = bsoncore.AppendDocumentEnd(msg, idx)\n\treturn msg\n}\n\nfunc (ac *awsConversation) finalMsg(s1 []byte) ([]byte, error) {\n\tvar sm serverMessage\n\terr := bson.Unmarshal(s1, &sm)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// Check nonce prefix\n\tif sm.Nonce.Subtype != 0x00 {\n\t\treturn nil, errors.New(\"server reply contained unexpected binary subtype\")\n\t}\n\tif len(sm.Nonce.Data) != responceNonceLength {\n\t\treturn nil, fmt.Errorf(\"server reply nonce was not %v bytes\", responceNonceLength)\n\t}\n\tif !bytes.HasPrefix(sm.Nonce.Data, ac.nonce) {\n\t\treturn nil, errors.New(\"server nonce did not extend client nonce\")\n\t}\n\n\tregion, err := getRegion(sm.Host)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tcreds, err := ac.credentials.GetWithContext(context.Background())\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tcurrentTime := time.Now().UTC()\n\tbody := \"Action=GetCallerIdentity&Version=2011-06-15\"\n\n\t// Create http.Request\n\treq, _ := http.NewRequest(\"POST\", \"/\", strings.NewReader(body))\n\treq.Header.Set(\"Content-Type\", \"application/x-www-form-urlencoded\")\n\treq.Header.Set(\"Content-Length\", \"43\")\n\treq.Host = sm.Host\n\treq.Header.Set(\"X-Amz-Date\", currentTime.Format(amzDateFormat))\n\tif len(creds.SessionToken) > 0 {\n\t\treq.Header.Set(\"X-Amz-Security-Token\", creds.SessionToken)\n\t}\n\treq.Header.Set(\"X-MongoDB-Server-Nonce\", base64.StdEncoding.EncodeToString(sm.Nonce.Data))\n\treq.Header.Set(\"X-MongoDB-GS2-CB-Flag\", \"n\")\n\n\t// Create signer with credentials\n\tsigner := v4signer.NewSigner(ac.credentials)\n\n\t// Get signed header\n\t_, err = signer.Sign(req, strings.NewReader(body), \"sts\", region, currentTime)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// create message\n\tidx, msg := bsoncore.AppendDocumentStart(nil)\n\tmsg = bsoncore.AppendStringElement(msg, \"a\", req.Header.Get(\"Authorization\"))\n\tmsg = bsoncore.AppendStringElement(msg, \"d\", req.Header.Get(\"X-Amz-Date\"))\n\tif len(creds.SessionToken) > 0 {\n\t\tmsg = bsoncore.AppendStringElement(msg, \"t\", creds.SessionToken)\n\t}\n\tmsg, _ = bsoncore.AppendDocumentEnd(msg, idx)\n\n\treturn msg, nil\n}\n"
  },
  {
    "path": "x/mongo/driver/auth/conversation.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage auth\n\nimport (\n\t\"context\"\n\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver\"\n)\n\n// SpeculativeConversation represents an authentication conversation that can be merged with the initial connection\n// handshake.\n//\n// FirstMessage method returns the first message to be sent to the server. This message will be included in the initial\n// hello command.\n//\n// Finish takes the server response to the initial message and conducts the remainder of the conversation to\n// authenticate the provided connection.\ntype SpeculativeConversation interface {\n\tFirstMessage() (bsoncore.Document, error)\n\tFinish(ctx context.Context, cfg *driver.AuthConfig, firstResponse bsoncore.Document) error\n}\n\n// SpeculativeAuthenticator represents an authenticator that supports speculative authentication.\ntype SpeculativeAuthenticator interface {\n\tCreateSpeculativeConversation() (SpeculativeConversation, error)\n}\n"
  },
  {
    "path": "x/mongo/driver/auth/cred.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage auth\n\nimport (\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver\"\n)\n\n// Cred is the type of user credential\ntype Cred = driver.Cred\n"
  },
  {
    "path": "x/mongo/driver/auth/creds/awscreds.go",
    "content": "// Copyright (C) MongoDB, Inc. 2022-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage creds\n\nimport (\n\t\"context\"\n\t\"net/http\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/aws/credentials\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/credproviders\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n)\n\nconst (\n\t// expiryWindow will allow the credentials to trigger refreshing prior to the credentials actually expiring.\n\t// This is beneficial so expiring credentials do not cause request to fail unexpectedly due to exceptions.\n\t//\n\t// Set an early expiration of 5 minutes before the credentials are actually expired.\n\texpiryWindow = 5 * time.Minute\n)\n\n// AWSCredentialProvider wraps AWS credentials.\ntype AWSCredentialProvider struct {\n\tCred *credentials.Credentials\n}\n\n// NewAWSCredentialProvider generates new AWSCredentialProvider\nfunc NewAWSCredentialProvider(httpClient *http.Client, providers ...credentials.Provider) AWSCredentialProvider {\n\tproviders = append(\n\t\tproviders,\n\t\tcredproviders.NewEnvProvider(),\n\t\tcredproviders.NewAssumeRoleProvider(httpClient, expiryWindow),\n\t\tcredproviders.NewECSProvider(httpClient, expiryWindow),\n\t\tcredproviders.NewEC2Provider(httpClient, expiryWindow),\n\t)\n\n\treturn AWSCredentialProvider{credentials.NewChainCredentials(providers)}\n}\n\n// GetCredentialsDoc generates AWS credentials.\nfunc (p AWSCredentialProvider) GetCredentialsDoc(ctx context.Context) (bsoncore.Document, error) {\n\tcreds, err := p.Cred.GetWithContext(ctx)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tbuilder := bsoncore.NewDocumentBuilder().\n\t\tAppendString(\"accessKeyId\", creds.AccessKeyID).\n\t\tAppendString(\"secretAccessKey\", creds.SecretAccessKey)\n\tif token := creds.SessionToken; len(token) > 0 {\n\t\tbuilder.AppendString(\"sessionToken\", token)\n\t}\n\treturn builder.Build(), nil\n}\n"
  },
  {
    "path": "x/mongo/driver/auth/creds/azurecreds.go",
    "content": "// Copyright (C) MongoDB, Inc. 2023-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage creds\n\nimport (\n\t\"context\"\n\t\"net/http\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/aws/credentials\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/credproviders\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n)\n\n// AzureCredentialProvider provides Azure credentials.\ntype AzureCredentialProvider struct {\n\tcred *credentials.Credentials\n}\n\n// NewAzureCredentialProvider generates new AzureCredentialProvider\nfunc NewAzureCredentialProvider(httpClient *http.Client) AzureCredentialProvider {\n\treturn AzureCredentialProvider{\n\t\tcredentials.NewCredentials(credproviders.NewAzureProvider(httpClient, 1*time.Minute)),\n\t}\n}\n\n// GetCredentialsDoc generates Azure credentials.\nfunc (p AzureCredentialProvider) GetCredentialsDoc(ctx context.Context) (bsoncore.Document, error) {\n\tcreds, err := p.cred.GetWithContext(ctx)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tbuilder := bsoncore.NewDocumentBuilder().\n\t\tAppendString(\"accessToken\", creds.SessionToken)\n\treturn builder.Build(), nil\n}\n"
  },
  {
    "path": "x/mongo/driver/auth/creds/credscaching_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2023-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage creds\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"net/url\"\n\t\"sync/atomic\"\n\t\"testing\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/aws/credentials\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/credproviders\"\n)\n\ntype pipeTransport struct {\n\turl    string\n\tparam  string\n\tclient *http.Client\n}\n\n// RoundTrip reassembles the original request URI into the query parameter and forwards the request.\nfunc (t pipeTransport) RoundTrip(req *http.Request) (*http.Response, error) {\n\turi, err := url.Parse(t.url)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvalues := uri.Query()\n\tvalues.Add(t.param, req.URL.String())\n\turi.RawQuery = values.Encode()\n\treq.URL = uri\n\treturn t.client.Do(req)\n}\n\nfunc TestAWSCredentialProviderCaching(t *testing.T) {\n\tconst (\n\t\turienv         = \"TEST_CONTAINER_CREDENTIALS_RELATIVE_URI\"\n\t\tkeyenv         = \"TEST_ACCESS_KEY\"\n\t\tawsRelativeURI = \"http://169.254.170.2/\"\n\t\ttestEndpoint   = \"foo\"\n\t\tparam          = \"source\"\n\t)\n\n\tt.Setenv(urienv, testEndpoint)\n\n\ttestCases := []struct {\n\t\texpiration time.Duration\n\t\treqCount   uint32\n\t}{\n\t\t{\n\t\t\texpiration: 20 * time.Minute,\n\t\t\treqCount:   1,\n\t\t},\n\t\t{\n\t\t\texpiration: 5 * time.Minute,\n\t\t\treqCount:   2,\n\t\t},\n\t\t{\n\t\t\texpiration: -1 * time.Minute,\n\t\t\treqCount:   2,\n\t\t},\n\t}\n\tfor _, tc := range testCases {\n\t\tt.Run(fmt.Sprintf(\"expires in %s\", tc.expiration.String()), func(t *testing.T) {\n\t\t\tvar cnt uint32\n\t\t\t// the test server counts the requests and replies mock responses.\n\t\t\tts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\t\t\tif r.URL.Query().Get(param) != awsRelativeURI+testEndpoint {\n\t\t\t\t\tw.WriteHeader(http.StatusNotFound)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tatomic.AddUint32(&cnt, 1)\n\t\t\t\tt := time.Now().Add(tc.expiration).Format(time.RFC3339)\n\t\t\t\t_, err := io.WriteString(w, fmt.Sprintf(`{\n\t\t\t\t\t\"AccessKeyId\": \"id\",\n\t\t\t\t\t\"SecretAccessKey\": \"key\",\n\t\t\t\t\t\"Token\": \"token\",\n\t\t\t\t\t\"Expiration\": \"%s\"\n\t\t\t\t}`, t))\n\t\t\t\tif err != nil {\n\t\t\t\t\tw.WriteHeader(http.StatusInternalServerError)\n\t\t\t\t}\n\t\t\t}))\n\t\t\tdefer ts.Close()\n\n\t\t\tclient := &http.Client{\n\t\t\t\tTransport: pipeTransport{\n\t\t\t\t\turl:    ts.URL,\n\t\t\t\t\tparam:  param,\n\t\t\t\t\tclient: ts.Client(),\n\t\t\t\t},\n\t\t\t}\n\n\t\t\tenv := credproviders.NewEnvProvider()\n\t\t\tenv.AwsAccessKeyIDEnv = credproviders.EnvVar(keyenv)\n\t\t\tecs := credproviders.NewECSProvider(client, expiryWindow)\n\t\t\tecs.AwsContainerCredentialsRelativeURIEnv = credproviders.EnvVar(urienv)\n\n\t\t\tp := AWSCredentialProvider{credentials.NewChainCredentials([]credentials.Provider{env, ecs})}\n\t\t\tvar err error\n\t\t\t_, err = p.GetCredentialsDoc(context.Background())\n\t\t\tassert.NoError(t, err, \"error in GetCredentialsDoc\")\n\t\t\t_, err = p.GetCredentialsDoc(context.Background())\n\t\t\tassert.NoError(t, err, \"error in GetCredentialsDoc\")\n\t\t\tassert.Equal(t, tc.reqCount, atomic.LoadUint32(&cnt), \"expected and actual credentials retrieval count don't match\")\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "x/mongo/driver/auth/creds/doc.go",
    "content": "// Copyright (C) MongoDB, Inc. 2024-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\n// Package creds is intended for internal use only. It is made available to\n// facilitate use cases that require access to internal MongoDB driver\n// functionality and state. The API of this package is not stable and there is\n// no backward compatibility guarantee.\n//\n// WARNING: THIS PACKAGE IS EXPERIMENTAL AND MAY BE MODIFIED OR REMOVED WITHOUT\n// NOTICE! USE WITH EXTREME CAUTION!\npackage creds\n"
  },
  {
    "path": "x/mongo/driver/auth/creds/gcpcreds.go",
    "content": "// Copyright (C) MongoDB, Inc. 2022-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage creds\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io/ioutil\"\n\t\"net/http\"\n\t\"os\"\n\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n)\n\n// GCPCredentialProvider provides GCP credentials.\ntype GCPCredentialProvider struct {\n\thttpClient *http.Client\n}\n\n// NewGCPCredentialProvider generates new GCPCredentialProvider\nfunc NewGCPCredentialProvider(httpClient *http.Client) GCPCredentialProvider {\n\treturn GCPCredentialProvider{httpClient}\n}\n\n// GetCredentialsDoc generates GCP credentials.\nfunc (p GCPCredentialProvider) GetCredentialsDoc(ctx context.Context) (bsoncore.Document, error) {\n\tmetadataHost := \"metadata.google.internal\"\n\tif envhost := os.Getenv(\"GCE_METADATA_HOST\"); envhost != \"\" {\n\t\tmetadataHost = envhost\n\t}\n\turl := fmt.Sprintf(\"http://%s/computeMetadata/v1/instance/service-accounts/default/token\", metadataHost)\n\treq, err := http.NewRequest(http.MethodGet, url, nil)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"unable to retrieve GCP credentials: %w\", err)\n\t}\n\treq.Header.Set(\"Metadata-Flavor\", \"Google\")\n\tresp, err := p.httpClient.Do(req.WithContext(ctx))\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"unable to retrieve GCP credentials: %w\", err)\n\t}\n\tdefer resp.Body.Close()\n\tbody, err := ioutil.ReadAll(resp.Body)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"unable to retrieve GCP credentials: error reading response body: %w\", err)\n\t}\n\tif resp.StatusCode != http.StatusOK {\n\t\treturn nil, fmt.Errorf(\n\t\t\t\"unable to retrieve GCP credentials: expected StatusCode 200, got StatusCode: %v. Response body: %s\",\n\t\t\tresp.StatusCode,\n\t\t\tbody)\n\t}\n\tvar tokenResponse struct {\n\t\tAccessToken string `json:\"access_token\"`\n\t}\n\t// Attempt to read body as JSON\n\terr = json.Unmarshal(body, &tokenResponse)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\n\t\t\t\"unable to retrieve GCP credentials: error reading body JSON: %w (response body: %s)\",\n\t\t\terr,\n\t\t\tbody)\n\t}\n\tif tokenResponse.AccessToken == \"\" {\n\t\treturn nil, fmt.Errorf(\"unable to retrieve GCP credentials: got unexpected empty accessToken from GCP Metadata Server. Response body: %s\", body)\n\t}\n\n\tbuilder := bsoncore.NewDocumentBuilder().AppendString(\"accessToken\", tokenResponse.AccessToken)\n\treturn builder.Build(), nil\n}\n"
  },
  {
    "path": "x/mongo/driver/auth/default.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage auth\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"net/http\"\n\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver\"\n)\n\nfunc newDefaultAuthenticator(cred *Cred, httpClient *http.Client) (Authenticator, error) {\n\tscram, err := newScramSHA256Authenticator(cred, httpClient)\n\tif err != nil {\n\t\treturn nil, newAuthError(\"failed to create internal authenticator\", err)\n\t}\n\tspeculative, ok := scram.(SpeculativeAuthenticator)\n\tif !ok {\n\t\ttypeErr := fmt.Errorf(\"expected SCRAM authenticator to be SpeculativeAuthenticator but got %T\", scram)\n\t\treturn nil, newAuthError(\"failed to create internal authenticator\", typeErr)\n\t}\n\n\treturn &DefaultAuthenticator{\n\t\tCred:                     cred,\n\t\tspeculativeAuthenticator: speculative,\n\t\thttpClient:               httpClient,\n\t}, nil\n}\n\n// DefaultAuthenticator uses SCRAM-SHA-1 or SCRAM-SHA-256, depending on the\n// server's SASL supported mechanisms.\ntype DefaultAuthenticator struct {\n\tCred *Cred\n\n\t// The authenticator to use for speculative authentication. Because the correct auth mechanism is unknown when doing\n\t// the initial hello, SCRAM-SHA-256 is used for the speculative attempt.\n\tspeculativeAuthenticator SpeculativeAuthenticator\n\n\thttpClient *http.Client\n}\n\nvar _ SpeculativeAuthenticator = (*DefaultAuthenticator)(nil)\n\n// CreateSpeculativeConversation creates a speculative conversation for SCRAM authentication.\nfunc (a *DefaultAuthenticator) CreateSpeculativeConversation() (SpeculativeConversation, error) {\n\treturn a.speculativeAuthenticator.CreateSpeculativeConversation()\n}\n\n// Auth authenticates the connection.\nfunc (a *DefaultAuthenticator) Auth(ctx context.Context, cfg *driver.AuthConfig) error {\n\tactual, err := func() (Authenticator, error) {\n\t\t// If a server provides a list of supported mechanisms, we choose\n\t\t// SCRAM-SHA-256 if it exists or else MUST use SCRAM-SHA-1.\n\t\t// Otherwise, we decide based on what is supported.\n\t\tif saslSupportedMechs := cfg.HandshakeInfo.SaslSupportedMechs; saslSupportedMechs != nil {\n\t\t\tfor _, v := range saslSupportedMechs {\n\t\t\t\tif v == SCRAMSHA256 {\n\t\t\t\t\treturn newScramSHA256Authenticator(a.Cred, a.httpClient)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn newScramSHA1Authenticator(a.Cred, a.httpClient)\n\t}()\n\tif err != nil {\n\t\treturn newAuthError(\"error creating authenticator\", err)\n\t}\n\n\treturn actual.Auth(ctx, cfg)\n}\n\n// Reauth reauthenticates the connection.\nfunc (a *DefaultAuthenticator) Reauth(_ context.Context, _ *driver.AuthConfig) error {\n\treturn newAuthError(\"DefaultAuthenticator does not support reauthentication\", nil)\n}\n"
  },
  {
    "path": "x/mongo/driver/auth/doc.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\n// Package auth is intended for internal use only. It is made available to\n// facilitate use cases that require access to internal MongoDB driver\n// functionality and state. The API of this package is not stable and there is\n// no backward compatibility guarantee.\n//\n// WARNING: THIS PACKAGE IS EXPERIMENTAL AND MAY BE MODIFIED OR REMOVED WITHOUT\n// NOTICE! USE WITH EXTREME CAUTION!\npackage auth\n"
  },
  {
    "path": "x/mongo/driver/auth/gssapi.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\n//go:build gssapi && (windows || linux || darwin)\n\npackage auth\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"net\"\n\t\"net/http\"\n\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/auth/internal/gssapi\"\n)\n\n// GSSAPI is the mechanism name for GSSAPI.\nconst GSSAPI = \"GSSAPI\"\n\nfunc newGSSAPIAuthenticator(cred *Cred, _ *http.Client) (Authenticator, error) {\n\tif cred.Source != \"\" && cred.Source != sourceExternal {\n\t\treturn nil, newAuthError(\"GSSAPI source must be empty or $external\", nil)\n\t}\n\n\treturn &GSSAPIAuthenticator{\n\t\tUsername:    cred.Username,\n\t\tPassword:    cred.Password,\n\t\tPasswordSet: cred.PasswordSet,\n\t\tProps:       cred.Props,\n\t}, nil\n}\n\n// GSSAPIAuthenticator uses the GSSAPI algorithm over SASL to authenticate a connection.\ntype GSSAPIAuthenticator struct {\n\tUsername    string\n\tPassword    string\n\tPasswordSet bool\n\tProps       map[string]string\n}\n\n// Auth authenticates the connection.\nfunc (a *GSSAPIAuthenticator) Auth(ctx context.Context, cfg *driver.AuthConfig) error {\n\ttarget := cfg.Connection.Description().Addr.String()\n\thostname, _, err := net.SplitHostPort(target)\n\tif err != nil {\n\t\treturn newAuthError(fmt.Sprintf(\"invalid endpoint (%s) specified: %s\", target, err), nil)\n\t}\n\n\tclient, err := gssapi.New(hostname, a.Username, a.Password, a.PasswordSet, a.Props)\n\tif err != nil {\n\t\treturn newAuthError(\"error creating gssapi\", err)\n\t}\n\treturn ConductSaslConversation(ctx, cfg, sourceExternal, client)\n}\n\n// Reauth reauthenticates the connection.\nfunc (a *GSSAPIAuthenticator) Reauth(_ context.Context, _ *driver.AuthConfig) error {\n\treturn newAuthError(\"GSSAPI does not support reauthentication\", nil)\n}\n"
  },
  {
    "path": "x/mongo/driver/auth/gssapi_not_enabled.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\n//go:build !gssapi\n\npackage auth\n\nimport \"net/http\"\n\n// GSSAPI is the mechanism name for GSSAPI.\nconst GSSAPI = \"GSSAPI\"\n\nfunc newGSSAPIAuthenticator(*Cred, *http.Client) (Authenticator, error) {\n\treturn nil, newAuthError(\"GSSAPI support not enabled during build (-tags gssapi)\", nil)\n}\n"
  },
  {
    "path": "x/mongo/driver/auth/gssapi_not_supported.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\n//go:build gssapi && !windows && !linux && !darwin\n\npackage auth\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\t\"runtime\"\n)\n\n// GSSAPI is the mechanism name for GSSAPI.\nconst GSSAPI = \"GSSAPI\"\n\nfunc newGSSAPIAuthenticator(*Cred, *http.Client) (Authenticator, error) {\n\treturn nil, newAuthError(fmt.Sprintf(\"GSSAPI is not supported on %s\", runtime.GOOS), nil)\n}\n"
  },
  {
    "path": "x/mongo/driver/auth/gssapi_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\n//go:build gssapi\n\npackage auth\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"go.mongodb.org/mongo-driver/v2/mongo/address\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/description\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/drivertest\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/mnet\"\n)\n\nfunc TestGSSAPIAuthenticator(t *testing.T) {\n\tt.Run(\"PropsError\", func(t *testing.T) {\n\t\t// Cannot specify both CANONICALIZE_HOST_NAME and SERVICE_HOST\n\n\t\tauthenticator := &GSSAPIAuthenticator{\n\t\t\tUsername:    \"foo\",\n\t\t\tPassword:    \"bar\",\n\t\t\tPasswordSet: true,\n\t\t\tProps: map[string]string{\n\t\t\t\t\"CANONICALIZE_HOST_NAME\": \"true\",\n\t\t\t\t\"SERVICE_HOST\":           \"localhost\",\n\t\t\t},\n\t\t}\n\t\tdesc := description.Server{\n\t\t\tWireVersion: &description.VersionRange{\n\t\t\t\tMax: 6,\n\t\t\t},\n\t\t\tAddr: address.Address(\"foo:27017\"),\n\t\t}\n\t\tchanconn := &drivertest.ChannelConn{\n\t\t\tDesc: desc,\n\t\t}\n\n\t\tmnetconn := mnet.NewConnection(chanconn)\n\n\t\terr := authenticator.Auth(context.Background(), &driver.AuthConfig{Connection: mnetconn})\n\t\tif err == nil {\n\t\t\tt.Fatalf(\"expected err, got nil\")\n\t\t}\n\t})\n}\n"
  },
  {
    "path": "x/mongo/driver/auth/internal/gssapi/gss.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\n//go:build gssapi && (linux || darwin)\n// +build gssapi\n// +build linux darwin\n\npackage gssapi\n\n/*\n#cgo linux CFLAGS: -DGOOS_linux\n#cgo linux LDFLAGS: -lgssapi_krb5 -lkrb5\n#cgo darwin CFLAGS: -DGOOS_darwin\n#cgo darwin LDFLAGS: -framework GSS\n#include \"gss_wrapper.h\"\n*/\nimport \"C\"\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"runtime\"\n\t\"strings\"\n\t\"unsafe\"\n)\n\n// New creates a new SaslClient. The target parameter should be a hostname with no port.\nfunc New(target, username, password string, passwordSet bool, props map[string]string) (*SaslClient, error) {\n\tserviceName := \"mongodb\"\n\n\tfor key, value := range props {\n\t\tswitch strings.ToUpper(key) {\n\t\tcase \"CANONICALIZE_HOST_NAME\":\n\t\t\treturn nil, fmt.Errorf(\"CANONICALIZE_HOST_NAME is not supported when using gssapi on %s\", runtime.GOOS)\n\t\tcase \"SERVICE_REALM\":\n\t\t\treturn nil, fmt.Errorf(\"SERVICE_REALM is not supported when using gssapi on %s\", runtime.GOOS)\n\t\tcase \"SERVICE_NAME\":\n\t\t\tserviceName = value\n\t\tcase \"SERVICE_HOST\":\n\t\t\ttarget = value\n\t\tdefault:\n\t\t\treturn nil, fmt.Errorf(\"unknown mechanism property %s\", key)\n\t\t}\n\t}\n\n\tservicePrincipalName := fmt.Sprintf(\"%s@%s\", serviceName, target)\n\n\treturn &SaslClient{\n\t\tservicePrincipalName: servicePrincipalName,\n\t\tusername:             username,\n\t\tpassword:             password,\n\t\tpasswordSet:          passwordSet,\n\t}, nil\n}\n\ntype SaslClient struct {\n\tservicePrincipalName string\n\tusername             string\n\tpassword             string\n\tpasswordSet          bool\n\n\t// state\n\tstate           C.gssapi_client_state\n\tcontextComplete bool\n\tdone            bool\n}\n\nfunc (sc *SaslClient) Close() {\n\tC.gssapi_client_destroy(&sc.state)\n}\n\nfunc (sc *SaslClient) Start() (string, []byte, error) {\n\tconst mechName = \"GSSAPI\"\n\n\tcservicePrincipalName := C.CString(sc.servicePrincipalName)\n\tdefer C.free(unsafe.Pointer(cservicePrincipalName))\n\tvar cusername *C.char\n\tvar cpassword *C.char\n\tif sc.username != \"\" {\n\t\tcusername = C.CString(sc.username)\n\t\tdefer C.free(unsafe.Pointer(cusername))\n\t\tif sc.passwordSet {\n\t\t\tcpassword = C.CString(sc.password)\n\t\t\tdefer C.free(unsafe.Pointer(cpassword))\n\t\t}\n\t}\n\tstatus := C.gssapi_client_init(&sc.state, cservicePrincipalName, cusername, cpassword)\n\n\tif status != C.GSSAPI_OK {\n\t\treturn mechName, nil, sc.getError(\"unable to initialize client\")\n\t}\n\n\tpayload, err := sc.Next(nil, nil)\n\n\treturn mechName, payload, err\n}\n\nfunc (sc *SaslClient) Next(_ context.Context, challenge []byte) ([]byte, error) {\n\tvar buf unsafe.Pointer\n\tvar bufLen C.size_t\n\tvar outBuf unsafe.Pointer\n\tvar outBufLen C.size_t\n\n\tif sc.contextComplete {\n\t\tif sc.username == \"\" {\n\t\t\tvar cusername *C.char\n\t\t\tstatus := C.gssapi_client_username(&sc.state, &cusername)\n\t\t\tif status != C.GSSAPI_OK {\n\t\t\t\treturn nil, sc.getError(\"unable to acquire username\")\n\t\t\t}\n\t\t\tdefer C.free(unsafe.Pointer(cusername))\n\t\t\tsc.username = C.GoString((*C.char)(unsafe.Pointer(cusername)))\n\t\t}\n\n\t\tbytes := append([]byte{1, 0, 0, 0}, []byte(sc.username)...)\n\t\tbuf = unsafe.Pointer(&bytes[0])\n\t\tbufLen = C.size_t(len(bytes))\n\t\tstatus := C.gssapi_client_wrap_msg(&sc.state, buf, bufLen, &outBuf, &outBufLen)\n\t\tif status != C.GSSAPI_OK {\n\t\t\treturn nil, sc.getError(\"unable to wrap authz\")\n\t\t}\n\n\t\tsc.done = true\n\t} else {\n\t\tif len(challenge) > 0 {\n\t\t\tbuf = unsafe.Pointer(&challenge[0])\n\t\t\tbufLen = C.size_t(len(challenge))\n\t\t}\n\n\t\tstatus := C.gssapi_client_negotiate(&sc.state, buf, bufLen, &outBuf, &outBufLen)\n\t\tswitch status {\n\t\tcase C.GSSAPI_OK:\n\t\t\tsc.contextComplete = true\n\t\tcase C.GSSAPI_CONTINUE:\n\t\tdefault:\n\t\t\treturn nil, sc.getError(\"unable to negotiate with server\")\n\t\t}\n\t}\n\n\tif outBuf != nil {\n\t\tdefer C.free(outBuf)\n\t}\n\n\treturn C.GoBytes(outBuf, C.int(outBufLen)), nil\n}\n\nfunc (sc *SaslClient) Completed() bool {\n\treturn sc.done\n}\n\nfunc (sc *SaslClient) getError(prefix string) error {\n\tvar desc *C.char\n\n\tstatus := C.gssapi_error_desc(sc.state.maj_stat, sc.state.min_stat, &desc)\n\tif status != C.GSSAPI_OK {\n\t\tif desc != nil {\n\t\t\tC.free(unsafe.Pointer(desc))\n\t\t}\n\n\t\treturn fmt.Errorf(\"%s: (%v, %v)\", prefix, sc.state.maj_stat, sc.state.min_stat)\n\t}\n\tdefer C.free(unsafe.Pointer(desc))\n\n\treturn fmt.Errorf(\"%s: %v(%v,%v)\", prefix, C.GoString(desc), int32(sc.state.maj_stat), int32(sc.state.min_stat))\n}\n"
  },
  {
    "path": "x/mongo/driver/auth/internal/gssapi/gss_wrapper.c",
    "content": "// Copyright (C) MongoDB, Inc. 2022-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\n//+build gssapi\n//+build linux darwin\n\n#include <string.h>\n#include <stdio.h>\n#include \"gss_wrapper.h\"\n\nOM_uint32 gssapi_canonicalize_name(\n    OM_uint32* minor_status,\n    char *input_name,\n    gss_OID input_name_type,\n    gss_name_t *output_name\n)\n{\n    OM_uint32 major_status;\n    gss_name_t imported_name = GSS_C_NO_NAME;\n    gss_buffer_desc buffer = GSS_C_EMPTY_BUFFER;\n\n    buffer.value = input_name;\n    buffer.length = strlen(input_name);\n    major_status = gss_import_name(minor_status, &buffer, input_name_type, &imported_name);\n    if (GSS_ERROR(major_status)) {\n        return major_status;\n    }\n\n    major_status = gss_canonicalize_name(minor_status, imported_name, (gss_OID)gss_mech_krb5, output_name);\n    if (imported_name != GSS_C_NO_NAME) {\n        OM_uint32 ignored;\n        gss_release_name(&ignored, &imported_name);\n    }\n\n    return major_status;\n}\n\nint gssapi_error_desc(\n    OM_uint32 maj_stat,\n    OM_uint32 min_stat,\n    char **desc\n)\n{\n    OM_uint32 stat = maj_stat;\n    int stat_type = GSS_C_GSS_CODE;\n    if (min_stat != 0) {\n        stat = min_stat;\n        stat_type = GSS_C_MECH_CODE;\n    }\n\n    OM_uint32 local_maj_stat, local_min_stat;\n    OM_uint32 msg_ctx = 0;\n    gss_buffer_desc desc_buffer;\n    do\n    {\n        local_maj_stat = gss_display_status(\n            &local_min_stat,\n            stat,\n            stat_type,\n            GSS_C_NO_OID,\n            &msg_ctx,\n            &desc_buffer\n        );\n        if (GSS_ERROR(local_maj_stat)) {\n            return GSSAPI_ERROR;\n        }\n\n        if (*desc) {\n            free(*desc);\n        }\n\n        *desc = calloc(1, desc_buffer.length + 1);\n        memcpy(*desc, desc_buffer.value, desc_buffer.length);\n\n        gss_release_buffer(&local_min_stat, &desc_buffer);\n    }\n    while(msg_ctx != 0);\n\n    return GSSAPI_OK;\n}\n\nint gssapi_client_init(\n    gssapi_client_state *client,\n    char* spn,\n    char* username,\n    char* password\n)\n{\n    client->cred = GSS_C_NO_CREDENTIAL;\n    client->ctx = GSS_C_NO_CONTEXT;\n\n    client->maj_stat = gssapi_canonicalize_name(&client->min_stat, spn, GSS_C_NT_HOSTBASED_SERVICE, &client->spn);\n    if (GSS_ERROR(client->maj_stat)) {\n        return GSSAPI_ERROR;\n    }\n\n    if (username) {\n        gss_name_t name;\n        client->maj_stat = gssapi_canonicalize_name(&client->min_stat, username, GSS_C_NT_USER_NAME, &name);\n        if (GSS_ERROR(client->maj_stat)) {\n            return GSSAPI_ERROR;\n        }\n\n        if (password) {\n            gss_buffer_desc password_buffer;\n            password_buffer.value = password;\n            password_buffer.length = strlen(password);\n            client->maj_stat = gss_acquire_cred_with_password(&client->min_stat, name, &password_buffer, GSS_C_INDEFINITE, GSS_C_NO_OID_SET, GSS_C_INITIATE, &client->cred, NULL, NULL);\n        } else {\n            client->maj_stat = gss_acquire_cred(&client->min_stat, name, GSS_C_INDEFINITE, GSS_C_NO_OID_SET, GSS_C_INITIATE, &client->cred, NULL, NULL);\n        }\n\n        if (GSS_ERROR(client->maj_stat)) {\n            return GSSAPI_ERROR;\n        }\n\n        OM_uint32 ignored;\n        gss_release_name(&ignored, &name);\n    }\n\n    return GSSAPI_OK;\n}\n\nint gssapi_client_username(\n    gssapi_client_state *client,\n    char** username\n)\n{\n    OM_uint32 ignored;\n    gss_name_t name = GSS_C_NO_NAME;\n\n    client->maj_stat = gss_inquire_context(&client->min_stat, client->ctx, &name, NULL, NULL, NULL, NULL, NULL, NULL);\n    if (GSS_ERROR(client->maj_stat)) {\n        return GSSAPI_ERROR;\n    }\n\n    gss_buffer_desc name_buffer;\n    client->maj_stat = gss_display_name(&client->min_stat, name, &name_buffer, NULL);\n    if (GSS_ERROR(client->maj_stat)) {\n        gss_release_name(&ignored, &name);\n        return GSSAPI_ERROR;\n    }\n\n    *username = calloc(1, name_buffer.length + 1);\n    memcpy(*username, name_buffer.value, name_buffer.length);\n\n    gss_release_buffer(&ignored, &name_buffer);\n    gss_release_name(&ignored, &name);\n    return GSSAPI_OK;\n}\n\nint gssapi_client_negotiate(\n    gssapi_client_state *client,\n    void* input,\n    size_t input_length,\n    void** output,\n    size_t* output_length\n)\n{\n    gss_buffer_desc input_buffer = GSS_C_EMPTY_BUFFER;\n    gss_buffer_desc output_buffer = GSS_C_EMPTY_BUFFER;\n\n    if (input) {\n        input_buffer.value = input;\n        input_buffer.length = input_length;\n    }\n\n    client->maj_stat = gss_init_sec_context(\n        &client->min_stat,\n        client->cred,\n        &client->ctx,\n        client->spn,\n        GSS_C_NO_OID,\n        GSS_C_MUTUAL_FLAG | GSS_C_SEQUENCE_FLAG,\n        0,\n        GSS_C_NO_CHANNEL_BINDINGS,\n        &input_buffer,\n        NULL,\n        &output_buffer,\n        NULL,\n        NULL\n    );\n\n    if (output_buffer.length) {\n        *output = malloc(output_buffer.length);\n        *output_length = output_buffer.length;\n        memcpy(*output, output_buffer.value, output_buffer.length);\n\n        OM_uint32 ignored;\n        gss_release_buffer(&ignored, &output_buffer);\n    }\n\n    if (GSS_ERROR(client->maj_stat)) {\n        return GSSAPI_ERROR;\n    } else if (client->maj_stat == GSS_S_CONTINUE_NEEDED) {\n        return GSSAPI_CONTINUE;\n    }\n\n    return GSSAPI_OK;\n}\n\nint gssapi_client_wrap_msg(\n    gssapi_client_state *client,\n    void* input,\n    size_t input_length,\n    void** output,\n    size_t* output_length\n)\n{\n    gss_buffer_desc input_buffer = GSS_C_EMPTY_BUFFER;\n    gss_buffer_desc output_buffer = GSS_C_EMPTY_BUFFER;\n\n    input_buffer.value = input;\n    input_buffer.length = input_length;\n\n    client->maj_stat = gss_wrap(&client->min_stat, client->ctx, 0, GSS_C_QOP_DEFAULT, &input_buffer, NULL, &output_buffer);\n\n    if (output_buffer.length) {\n        *output = malloc(output_buffer.length);\n        *output_length = output_buffer.length;\n        memcpy(*output, output_buffer.value, output_buffer.length);\n\n        gss_release_buffer(&client->min_stat, &output_buffer);\n    }\n\n    if (GSS_ERROR(client->maj_stat)) {\n        return GSSAPI_ERROR;\n    }\n\n    return GSSAPI_OK;\n}\n\nint gssapi_client_destroy(\n    gssapi_client_state *client\n)\n{\n    OM_uint32 ignored;\n    if (client->ctx != GSS_C_NO_CONTEXT) {\n        gss_delete_sec_context(&ignored, &client->ctx, GSS_C_NO_BUFFER);\n    }\n\n    if (client->spn != GSS_C_NO_NAME) {\n        gss_release_name(&ignored, &client->spn);\n    }\n\n    if (client->cred != GSS_C_NO_CREDENTIAL) {\n        gss_release_cred(&ignored, &client->cred);\n    }\n\n    return GSSAPI_OK;\n}\n"
  },
  {
    "path": "x/mongo/driver/auth/internal/gssapi/gss_wrapper.h",
    "content": "// Copyright (C) MongoDB, Inc. 2022-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\n//+build gssapi\n//+build linux darwin\n#ifndef GSS_WRAPPER_H\n#define GSS_WRAPPER_H\n\n#include <stdlib.h>\n#ifdef GOOS_linux\n#include <gssapi/gssapi.h>\n#include <gssapi/gssapi_krb5.h>\n#endif\n#ifdef GOOS_darwin\n#include <GSS/GSS.h>\n#endif\n\n#define GSSAPI_OK 0\n#define GSSAPI_CONTINUE 1\n#define GSSAPI_ERROR 2\n\ntypedef struct {\n    gss_name_t spn;\n    gss_cred_id_t cred;\n    gss_ctx_id_t ctx;\n\n    OM_uint32 maj_stat;\n    OM_uint32 min_stat;\n} gssapi_client_state;\n\nint gssapi_error_desc(\n    OM_uint32 maj_stat,\n    OM_uint32 min_stat,\n    char **desc\n);\n\nint gssapi_client_init(\n    gssapi_client_state *client,\n    char* spn,\n    char* username,\n    char* password\n);\n\nint gssapi_client_username(\n    gssapi_client_state *client,\n    char** username\n);\n\nint gssapi_client_negotiate(\n    gssapi_client_state *client,\n    void* input,\n    size_t input_length,\n    void** output,\n    size_t* output_length\n);\n\nint gssapi_client_wrap_msg(\n    gssapi_client_state *client,\n    void* input,\n    size_t input_length,\n    void** output,\n    size_t* output_length\n);\n\nint gssapi_client_destroy(\n    gssapi_client_state *client\n);\n\n#endif\n"
  },
  {
    "path": "x/mongo/driver/auth/internal/gssapi/sspi.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\n//go:build gssapi && windows\n// +build gssapi,windows\n\npackage gssapi\n\n// #include \"sspi_wrapper.h\"\nimport \"C\"\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"net\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\t\"unsafe\"\n)\n\n// New creates a new SaslClient. The target parameter should be a hostname with no port.\nfunc New(target, username, password string, passwordSet bool, props map[string]string) (*SaslClient, error) {\n\tinitOnce.Do(initSSPI)\n\tif initError != nil {\n\t\treturn nil, initError\n\t}\n\n\tvar err error\n\tserviceName := \"mongodb\"\n\tserviceRealm := \"\"\n\tcanonicalizeHostName := false\n\tvar serviceHostSet bool\n\n\tfor key, value := range props {\n\t\tswitch strings.ToUpper(key) {\n\t\tcase \"CANONICALIZE_HOST_NAME\":\n\t\t\tcanonicalizeHostName, err = strconv.ParseBool(value)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"%s must be a boolean (true, false, 0, 1) but got '%s'\", key, value)\n\t\t\t}\n\n\t\tcase \"SERVICE_REALM\":\n\t\t\tserviceRealm = value\n\t\tcase \"SERVICE_NAME\":\n\t\t\tserviceName = value\n\t\tcase \"SERVICE_HOST\":\n\t\t\tserviceHostSet = true\n\t\t\ttarget = value\n\t\t}\n\t}\n\n\tif canonicalizeHostName {\n\t\t// Should not canonicalize the SERVICE_HOST\n\t\tif serviceHostSet {\n\t\t\treturn nil, fmt.Errorf(\"CANONICALIZE_HOST_NAME and SERVICE_HOST canonot both be specified\")\n\t\t}\n\n\t\tnames, err := net.LookupAddr(target)\n\t\tif err != nil || len(names) == 0 {\n\t\t\treturn nil, fmt.Errorf(\"unable to canonicalize hostname: %s\", err)\n\t\t}\n\t\ttarget = names[0]\n\t\tif target[len(target)-1] == '.' {\n\t\t\ttarget = target[:len(target)-1]\n\t\t}\n\t}\n\n\tservicePrincipalName := fmt.Sprintf(\"%s/%s\", serviceName, target)\n\tif serviceRealm != \"\" {\n\t\tservicePrincipalName += \"@\" + serviceRealm\n\t}\n\n\treturn &SaslClient{\n\t\tservicePrincipalName: servicePrincipalName,\n\t\tusername:             username,\n\t\tpassword:             password,\n\t\tpasswordSet:          passwordSet,\n\t}, nil\n}\n\ntype SaslClient struct {\n\tservicePrincipalName string\n\tusername             string\n\tpassword             string\n\tpasswordSet          bool\n\n\t// state\n\tstate           C.sspi_client_state\n\tcontextComplete bool\n\tdone            bool\n}\n\nfunc (sc *SaslClient) Close() {\n\tC.sspi_client_destroy(&sc.state)\n}\n\nfunc (sc *SaslClient) Start() (string, []byte, error) {\n\tconst mechName = \"GSSAPI\"\n\n\tvar cusername *C.char\n\tvar cpassword *C.char\n\tif sc.username != \"\" {\n\t\tcusername = C.CString(sc.username)\n\t\tdefer C.free(unsafe.Pointer(cusername))\n\t\tif sc.passwordSet {\n\t\t\tcpassword = C.CString(sc.password)\n\t\t\tdefer C.free(unsafe.Pointer(cpassword))\n\t\t}\n\t}\n\tstatus := C.sspi_client_init(&sc.state, cusername, cpassword)\n\n\tif status != C.SSPI_OK {\n\t\treturn mechName, nil, sc.getError(\"unable to initialize client\")\n\t}\n\n\tpayload, err := sc.Next(nil, nil)\n\n\treturn mechName, payload, err\n}\n\nfunc (sc *SaslClient) Next(_ context.Context, challenge []byte) ([]byte, error) {\n\tvar outBuf C.PVOID\n\tvar outBufLen C.ULONG\n\n\tif sc.contextComplete {\n\t\tif sc.username == \"\" {\n\t\t\tvar cusername *C.char\n\t\t\tstatus := C.sspi_client_username(&sc.state, &cusername)\n\t\t\tif status != C.SSPI_OK {\n\t\t\t\treturn nil, sc.getError(\"unable to acquire username\")\n\t\t\t}\n\t\t\tdefer C.free(unsafe.Pointer(cusername))\n\t\t\tsc.username = C.GoString((*C.char)(unsafe.Pointer(cusername)))\n\t\t}\n\n\t\tbytes := append([]byte{1, 0, 0, 0}, []byte(sc.username)...)\n\t\tbuf := (C.PVOID)(unsafe.Pointer(&bytes[0]))\n\t\tbufLen := C.ULONG(len(bytes))\n\t\tstatus := C.sspi_client_wrap_msg(&sc.state, buf, bufLen, &outBuf, &outBufLen)\n\t\tif status != C.SSPI_OK {\n\t\t\treturn nil, sc.getError(\"unable to wrap authz\")\n\t\t}\n\n\t\tsc.done = true\n\t} else {\n\t\tvar buf C.PVOID\n\t\tvar bufLen C.ULONG\n\t\tif len(challenge) > 0 {\n\t\t\tbuf = (C.PVOID)(unsafe.Pointer(&challenge[0]))\n\t\t\tbufLen = C.ULONG(len(challenge))\n\t\t}\n\t\tcservicePrincipalName := C.CString(sc.servicePrincipalName)\n\t\tdefer C.free(unsafe.Pointer(cservicePrincipalName))\n\n\t\tstatus := C.sspi_client_negotiate(&sc.state, cservicePrincipalName, buf, bufLen, &outBuf, &outBufLen)\n\t\tswitch status {\n\t\tcase C.SSPI_OK:\n\t\t\tsc.contextComplete = true\n\t\tcase C.SSPI_CONTINUE:\n\t\tdefault:\n\t\t\treturn nil, sc.getError(\"unable to negotiate with server\")\n\t\t}\n\t}\n\n\tif outBuf != C.PVOID(nil) {\n\t\tdefer C.free(unsafe.Pointer(outBuf))\n\t}\n\n\treturn C.GoBytes(unsafe.Pointer(outBuf), C.int(outBufLen)), nil\n}\n\nfunc (sc *SaslClient) Completed() bool {\n\treturn sc.done\n}\n\nfunc (sc *SaslClient) getError(prefix string) error {\n\treturn getError(prefix, sc.state.status)\n}\n\nvar (\n\tinitOnce  sync.Once\n\tinitError error\n)\n\nfunc initSSPI() {\n\trc := C.sspi_init()\n\tif rc != 0 {\n\t\tinitError = fmt.Errorf(\"error initializing sspi: %v\", rc)\n\t}\n}\n\nfunc getError(prefix string, status C.SECURITY_STATUS) error {\n\tvar s string\n\tswitch status {\n\tcase C.SEC_E_ALGORITHM_MISMATCH:\n\t\ts = \"The client and server cannot communicate because they do not possess a common algorithm.\"\n\tcase C.SEC_E_BAD_BINDINGS:\n\t\ts = \"The SSPI channel bindings supplied by the client are incorrect.\"\n\tcase C.SEC_E_BAD_PKGID:\n\t\ts = \"The requested package identifier does not exist.\"\n\tcase C.SEC_E_BUFFER_TOO_SMALL:\n\t\ts = \"The buffers supplied to the function are not large enough to contain the information.\"\n\tcase C.SEC_E_CANNOT_INSTALL:\n\t\ts = \"The security package cannot initialize successfully and should not be installed.\"\n\tcase C.SEC_E_CANNOT_PACK:\n\t\ts = \"The package is unable to pack the context.\"\n\tcase C.SEC_E_CERT_EXPIRED:\n\t\ts = \"The received certificate has expired.\"\n\tcase C.SEC_E_CERT_UNKNOWN:\n\t\ts = \"An unknown error occurred while processing the certificate.\"\n\tcase C.SEC_E_CERT_WRONG_USAGE:\n\t\ts = \"The certificate is not valid for the requested usage.\"\n\tcase C.SEC_E_CONTEXT_EXPIRED:\n\t\ts = \"The application is referencing a context that has already been closed. A properly written application should not receive this error.\"\n\tcase C.SEC_E_CROSSREALM_DELEGATION_FAILURE:\n\t\ts = \"The server attempted to make a Kerberos-constrained delegation request for a target outside the server's realm.\"\n\tcase C.SEC_E_CRYPTO_SYSTEM_INVALID:\n\t\ts = \"The cryptographic system or checksum function is not valid because a required function is unavailable.\"\n\tcase C.SEC_E_DECRYPT_FAILURE:\n\t\ts = \"The specified data could not be decrypted.\"\n\tcase C.SEC_E_DELEGATION_REQUIRED:\n\t\ts = \"The requested operation cannot be completed. The computer must be trusted for delegation\"\n\tcase C.SEC_E_DOWNGRADE_DETECTED:\n\t\ts = \"The system detected a possible attempt to compromise security. Verify that the server that authenticated you can be contacted.\"\n\tcase C.SEC_E_ENCRYPT_FAILURE:\n\t\ts = \"The specified data could not be encrypted.\"\n\tcase C.SEC_E_ILLEGAL_MESSAGE:\n\t\ts = \"The message received was unexpected or badly formatted.\"\n\tcase C.SEC_E_INCOMPLETE_CREDENTIALS:\n\t\ts = \"The credentials supplied were not complete and could not be verified. The context could not be initialized.\"\n\tcase C.SEC_E_INCOMPLETE_MESSAGE:\n\t\ts = \"The message supplied was incomplete. The signature was not verified.\"\n\tcase C.SEC_E_INSUFFICIENT_MEMORY:\n\t\ts = \"Not enough memory is available to complete the request.\"\n\tcase C.SEC_E_INTERNAL_ERROR:\n\t\ts = \"An error occurred that did not map to an SSPI error code.\"\n\tcase C.SEC_E_INVALID_HANDLE:\n\t\ts = \"The handle passed to the function is not valid.\"\n\tcase C.SEC_E_INVALID_TOKEN:\n\t\ts = \"The token passed to the function is not valid.\"\n\tcase C.SEC_E_ISSUING_CA_UNTRUSTED:\n\t\ts = \"An untrusted certification authority (CA) was detected while processing the smart card certificate used for authentication.\"\n\tcase C.SEC_E_ISSUING_CA_UNTRUSTED_KDC:\n\t\ts = \"An untrusted CA was detected while processing the domain controller certificate used for authentication. The system event log contains additional information.\"\n\tcase C.SEC_E_KDC_CERT_EXPIRED:\n\t\ts = \"The domain controller certificate used for smart card logon has expired.\"\n\tcase C.SEC_E_KDC_CERT_REVOKED:\n\t\ts = \"The domain controller certificate used for smart card logon has been revoked.\"\n\tcase C.SEC_E_KDC_INVALID_REQUEST:\n\t\ts = \"A request that is not valid was sent to the KDC.\"\n\tcase C.SEC_E_KDC_UNABLE_TO_REFER:\n\t\ts = \"The KDC was unable to generate a referral for the service requested.\"\n\tcase C.SEC_E_KDC_UNKNOWN_ETYPE:\n\t\ts = \"The requested encryption type is not supported by the KDC.\"\n\tcase C.SEC_E_LOGON_DENIED:\n\t\ts = \"The logon has been denied\"\n\tcase C.SEC_E_MAX_REFERRALS_EXCEEDED:\n\t\ts = \"The number of maximum ticket referrals has been exceeded.\"\n\tcase C.SEC_E_MESSAGE_ALTERED:\n\t\ts = \"The message supplied for verification has been altered.\"\n\tcase C.SEC_E_MULTIPLE_ACCOUNTS:\n\t\ts = \"The received certificate was mapped to multiple accounts.\"\n\tcase C.SEC_E_MUST_BE_KDC:\n\t\ts = \"The local computer must be a Kerberos domain controller (KDC)\"\n\tcase C.SEC_E_NO_AUTHENTICATING_AUTHORITY:\n\t\ts = \"No authority could be contacted for authentication.\"\n\tcase C.SEC_E_NO_CREDENTIALS:\n\t\ts = \"No credentials are available.\"\n\tcase C.SEC_E_NO_IMPERSONATION:\n\t\ts = \"No impersonation is allowed for this context.\"\n\tcase C.SEC_E_NO_IP_ADDRESSES:\n\t\ts = \"Unable to accomplish the requested task because the local computer does not have any IP addresses.\"\n\tcase C.SEC_E_NO_KERB_KEY:\n\t\ts = \"No Kerberos key was found.\"\n\tcase C.SEC_E_NO_PA_DATA:\n\t\ts = \"Policy administrator (PA) data is needed to determine the encryption type\"\n\tcase C.SEC_E_NO_S4U_PROT_SUPPORT:\n\t\ts = \"The Kerberos subsystem encountered an error. A service for user protocol request was made against a domain controller which does not support service for a user.\"\n\tcase C.SEC_E_NO_TGT_REPLY:\n\t\ts = \"The client is trying to negotiate a context and the server requires a user-to-user connection\"\n\tcase C.SEC_E_NOT_OWNER:\n\t\ts = \"The caller of the function does not own the credentials.\"\n\tcase C.SEC_E_OK:\n\t\ts = \"The operation completed successfully.\"\n\tcase C.SEC_E_OUT_OF_SEQUENCE:\n\t\ts = \"The message supplied for verification is out of sequence.\"\n\tcase C.SEC_E_PKINIT_CLIENT_FAILURE:\n\t\ts = \"The smart card certificate used for authentication is not trusted.\"\n\tcase C.SEC_E_PKINIT_NAME_MISMATCH:\n\t\ts = \"The client certificate does not contain a valid UPN or does not match the client name in the logon request.\"\n\tcase C.SEC_E_QOP_NOT_SUPPORTED:\n\t\ts = \"The quality of protection attribute is not supported by this package.\"\n\tcase C.SEC_E_REVOCATION_OFFLINE_C:\n\t\ts = \"The revocation status of the smart card certificate used for authentication could not be determined.\"\n\tcase C.SEC_E_REVOCATION_OFFLINE_KDC:\n\t\ts = \"The revocation status of the domain controller certificate used for smart card authentication could not be determined. The system event log contains additional information.\"\n\tcase C.SEC_E_SECPKG_NOT_FOUND:\n\t\ts = \"The security package was not recognized.\"\n\tcase C.SEC_E_SECURITY_QOS_FAILED:\n\t\ts = \"The security context could not be established due to a failure in the requested quality of service (for example\"\n\tcase C.SEC_E_SHUTDOWN_IN_PROGRESS:\n\t\ts = \"A system shutdown is in progress.\"\n\tcase C.SEC_E_SMARTCARD_CERT_EXPIRED:\n\t\ts = \"The smart card certificate used for authentication has expired.\"\n\tcase C.SEC_E_SMARTCARD_CERT_REVOKED:\n\t\ts = \"The smart card certificate used for authentication has been revoked. Additional information may exist in the event log.\"\n\tcase C.SEC_E_SMARTCARD_LOGON_REQUIRED:\n\t\ts = \"Smart card logon is required and was not used.\"\n\tcase C.SEC_E_STRONG_CRYPTO_NOT_SUPPORTED:\n\t\ts = \"The other end of the security negotiation requires strong cryptography\"\n\tcase C.SEC_E_TARGET_UNKNOWN:\n\t\ts = \"The target was not recognized.\"\n\tcase C.SEC_E_TIME_SKEW:\n\t\ts = \"The clocks on the client and server computers do not match.\"\n\tcase C.SEC_E_TOO_MANY_PRINCIPALS:\n\t\ts = \"The KDC reply contained more than one principal name.\"\n\tcase C.SEC_E_UNFINISHED_CONTEXT_DELETED:\n\t\ts = \"A security context was deleted before the context was completed. This is considered a logon failure.\"\n\tcase C.SEC_E_UNKNOWN_CREDENTIALS:\n\t\ts = \"The credentials provided were not recognized.\"\n\tcase C.SEC_E_UNSUPPORTED_FUNCTION:\n\t\ts = \"The requested function is not supported.\"\n\tcase C.SEC_E_UNSUPPORTED_PREAUTH:\n\t\ts = \"An unsupported preauthentication mechanism was presented to the Kerberos package.\"\n\tcase C.SEC_E_UNTRUSTED_ROOT:\n\t\ts = \"The certificate chain was issued by an authority that is not trusted.\"\n\tcase C.SEC_E_WRONG_CREDENTIAL_HANDLE:\n\t\ts = \"The supplied credential handle does not match the credential associated with the security context.\"\n\tcase C.SEC_E_WRONG_PRINCIPAL:\n\t\ts = \"The target principal name is incorrect.\"\n\tcase C.SEC_I_COMPLETE_AND_CONTINUE:\n\t\ts = \"The function completed successfully\"\n\tcase C.SEC_I_COMPLETE_NEEDED:\n\t\ts = \"The function completed successfully\"\n\tcase C.SEC_I_CONTEXT_EXPIRED:\n\t\ts = \"The message sender has finished using the connection and has initiated a shutdown. For information about initiating or recognizing a shutdown\"\n\tcase C.SEC_I_CONTINUE_NEEDED:\n\t\ts = \"The function completed successfully\"\n\tcase C.SEC_I_INCOMPLETE_CREDENTIALS:\n\t\ts = \"The credentials supplied were not complete and could not be verified. Additional information can be returned from the context.\"\n\tcase C.SEC_I_LOCAL_LOGON:\n\t\ts = \"The logon was completed\"\n\tcase C.SEC_I_NO_LSA_CONTEXT:\n\t\ts = \"There is no LSA mode context associated with this context.\"\n\tcase C.SEC_I_RENEGOTIATE:\n\t\ts = \"The context data must be renegotiated with the peer.\"\n\tdefault:\n\t\treturn fmt.Errorf(\"%s: 0x%x\", prefix, uint32(status))\n\t}\n\n\treturn fmt.Errorf(\"%s: %s(0x%x)\", prefix, s, uint32(status))\n}\n"
  },
  {
    "path": "x/mongo/driver/auth/internal/gssapi/sspi_wrapper.c",
    "content": "// Copyright (C) MongoDB, Inc. 2022-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\n//+build gssapi,windows\n\n#include \"sspi_wrapper.h\"\n\nstatic HINSTANCE sspi_secur32_dll = NULL;\nstatic PSecurityFunctionTable sspi_functions = NULL;\nstatic const LPSTR SSPI_PACKAGE_NAME = \"kerberos\";\n\nint sspi_init(\n)\n{\n\t// Load the secur32.dll library using its exact path. Passing the exact DLL path rather than allowing LoadLibrary to\n\t// search in different locations removes the possibility of DLL preloading attacks. We use GetSystemDirectoryA and\n\t// LoadLibraryA rather than the GetSystemDirectory/LoadLibrary aliases to ensure the ANSI versions are used so we\n\t// don't have to account for variations in char sizes if UNICODE is enabled.\n\n\t// Passing a 0 size will return the required buffer length to hold the path, including the null terminator.\n\tint requiredLen = GetSystemDirectoryA(NULL, 0);\n\tif (!requiredLen) {\n\t\treturn GetLastError();\n\t}\n\n\t// Allocate a buffer to hold the system directory + \"\\secur32.dll\" (length 12, not including null terminator).\n\tint actualLen = requiredLen + 12;\n\tchar *directoryBuffer = (char *) calloc(1, actualLen);\n\tint directoryLen = GetSystemDirectoryA(directoryBuffer, actualLen);\n\tif (!directoryLen) {\n\t\tfree(directoryBuffer);\n\t\treturn GetLastError();\n\t}\n\n\t// Append the DLL name to the buffer.\n\tchar *dllName = \"\\\\secur32.dll\";\n\tstrcpy_s(&(directoryBuffer[directoryLen]), actualLen - directoryLen, dllName);\n\n\tsspi_secur32_dll = LoadLibraryA(directoryBuffer);\n\tfree(directoryBuffer);\n\tif (!sspi_secur32_dll) {\n\t\treturn GetLastError();\n\t}\n\n    INIT_SECURITY_INTERFACE init_security_interface = (INIT_SECURITY_INTERFACE)GetProcAddress(sspi_secur32_dll, SECURITY_ENTRYPOINT);\n    if (!init_security_interface) {\n        return -1;\n    }\n\n    sspi_functions = (*init_security_interface)();\n    if (!sspi_functions) {\n        return -2;\n    }\n\n\treturn SSPI_OK;\n}\n\nint sspi_client_init(\n    sspi_client_state *client,\n    char* username,\n    char* password\n)\n{\n\tTimeStamp timestamp;\n\n    if (username) {\n        if (password) {\n            SEC_WINNT_AUTH_IDENTITY auth_identity;\n\n        #ifdef _UNICODE\n            auth_identity.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;\n        #else\n            auth_identity.Flags = SEC_WINNT_AUTH_IDENTITY_ANSI;\n        #endif\n            auth_identity.User = (LPSTR) username;\n            auth_identity.UserLength = strlen(username);\n            auth_identity.Password = (LPSTR) password;\n            auth_identity.PasswordLength = strlen(password);\n            auth_identity.Domain = NULL;\n            auth_identity.DomainLength = 0;\n            client->status = sspi_functions->AcquireCredentialsHandle(NULL, SSPI_PACKAGE_NAME, SECPKG_CRED_OUTBOUND, NULL, &auth_identity, NULL, NULL, &client->cred, &timestamp);\n        } else {\n            client->status = sspi_functions->AcquireCredentialsHandle(username, SSPI_PACKAGE_NAME, SECPKG_CRED_OUTBOUND, NULL, NULL, NULL, NULL, &client->cred, &timestamp);\n        }\n    } else {\n        client->status = sspi_functions->AcquireCredentialsHandle(NULL, SSPI_PACKAGE_NAME, SECPKG_CRED_OUTBOUND, NULL, NULL, NULL, NULL, &client->cred, &timestamp);\n    }\n\n    if (client->status != SEC_E_OK) {\n        return SSPI_ERROR;\n    }\n\n    return SSPI_OK;\n}\n\nint sspi_client_username(\n    sspi_client_state *client,\n    char** username\n)\n{\n    SecPkgCredentials_Names names;\n\tclient->status = sspi_functions->QueryCredentialsAttributes(&client->cred, SECPKG_CRED_ATTR_NAMES, &names);\n\n\tif (client->status != SEC_E_OK) {\n\t\treturn SSPI_ERROR;\n\t}\n\n\tint len = strlen(names.sUserName) + 1;\n\t*username = malloc(len);\n\tmemcpy(*username, names.sUserName, len);\n\n\tsspi_functions->FreeContextBuffer(names.sUserName);\n\n    return SSPI_OK;\n}\n\nint sspi_client_negotiate(\n    sspi_client_state *client,\n    char* spn,\n    PVOID input,\n    ULONG input_length,\n    PVOID* output,\n    ULONG* output_length\n)\n{\n    SecBufferDesc inbuf;\n\tSecBuffer in_bufs[1];\n\tSecBufferDesc outbuf;\n\tSecBuffer out_bufs[1];\n\n\tif (client->has_ctx > 0) {\n\t\tinbuf.ulVersion = SECBUFFER_VERSION;\n\t\tinbuf.cBuffers = 1;\n\t\tinbuf.pBuffers = in_bufs;\n\t\tin_bufs[0].pvBuffer = input;\n\t\tin_bufs[0].cbBuffer = input_length;\n\t\tin_bufs[0].BufferType = SECBUFFER_TOKEN;\n\t}\n\n\toutbuf.ulVersion = SECBUFFER_VERSION;\n\toutbuf.cBuffers = 1;\n\toutbuf.pBuffers = out_bufs;\n\tout_bufs[0].pvBuffer = NULL;\n\tout_bufs[0].cbBuffer = 0;\n\tout_bufs[0].BufferType = SECBUFFER_TOKEN;\n\n\tULONG context_attr = 0;\n\n\tclient->status = sspi_functions->InitializeSecurityContext(\n        &client->cred,\n        client->has_ctx > 0 ? &client->ctx : NULL,\n        (LPSTR) spn,\n        ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_MUTUAL_AUTH,\n        0,\n        SECURITY_NETWORK_DREP,\n        client->has_ctx > 0 ? &inbuf : NULL,\n        0,\n        &client->ctx,\n        &outbuf,\n        &context_attr,\n        NULL);\n\n    if (client->status != SEC_E_OK && client->status != SEC_I_CONTINUE_NEEDED) {\n        return SSPI_ERROR;\n    }\n\n    client->has_ctx = 1;\n\n\t*output = malloc(out_bufs[0].cbBuffer);\n\t*output_length = out_bufs[0].cbBuffer;\n\tmemcpy(*output, out_bufs[0].pvBuffer, *output_length);\n    sspi_functions->FreeContextBuffer(out_bufs[0].pvBuffer);\n\n    if (client->status == SEC_I_CONTINUE_NEEDED) {\n        return SSPI_CONTINUE;\n    }\n\n    return SSPI_OK;\n}\n\nint sspi_client_wrap_msg(\n    sspi_client_state *client,\n    PVOID input,\n    ULONG input_length,\n    PVOID* output,\n    ULONG* output_length\n)\n{\n    SecPkgContext_Sizes sizes;\n\n\tclient->status = sspi_functions->QueryContextAttributes(&client->ctx, SECPKG_ATTR_SIZES, &sizes);\n\tif (client->status != SEC_E_OK) {\n\t\treturn SSPI_ERROR;\n\t}\n\n\tchar *msg = malloc((sizes.cbSecurityTrailer + input_length + sizes.cbBlockSize) * sizeof(char));\n\tmemcpy(&msg[sizes.cbSecurityTrailer], input, input_length);\n\n\tSecBuffer wrap_bufs[3];\n\tSecBufferDesc wrap_buf_desc;\n\twrap_buf_desc.cBuffers = 3;\n\twrap_buf_desc.pBuffers = wrap_bufs;\n\twrap_buf_desc.ulVersion = SECBUFFER_VERSION;\n\n\twrap_bufs[0].cbBuffer = sizes.cbSecurityTrailer;\n\twrap_bufs[0].BufferType = SECBUFFER_TOKEN;\n\twrap_bufs[0].pvBuffer = msg;\n\n\twrap_bufs[1].cbBuffer = input_length;\n\twrap_bufs[1].BufferType = SECBUFFER_DATA;\n\twrap_bufs[1].pvBuffer = msg + sizes.cbSecurityTrailer;\n\n\twrap_bufs[2].cbBuffer = sizes.cbBlockSize;\n\twrap_bufs[2].BufferType = SECBUFFER_PADDING;\n\twrap_bufs[2].pvBuffer = msg + sizes.cbSecurityTrailer + input_length;\n\n\tclient->status = sspi_functions->EncryptMessage(&client->ctx, SECQOP_WRAP_NO_ENCRYPT, &wrap_buf_desc, 0);\n\tif (client->status != SEC_E_OK) {\n\t\tfree(msg);\n\t\treturn SSPI_ERROR;\n\t}\n\n\t*output_length = wrap_bufs[0].cbBuffer + wrap_bufs[1].cbBuffer + wrap_bufs[2].cbBuffer;\n\t*output = malloc(*output_length);\n\n\tmemcpy(*output, wrap_bufs[0].pvBuffer, wrap_bufs[0].cbBuffer);\n\tmemcpy(*output + wrap_bufs[0].cbBuffer, wrap_bufs[1].pvBuffer, wrap_bufs[1].cbBuffer);\n\tmemcpy(*output + wrap_bufs[0].cbBuffer + wrap_bufs[1].cbBuffer, wrap_bufs[2].pvBuffer, wrap_bufs[2].cbBuffer);\n\n\tfree(msg);\n\n\treturn SSPI_OK;\n}\n\nint sspi_client_destroy(\n    sspi_client_state *client\n)\n{\n    if (client->has_ctx > 0) {\n        sspi_functions->DeleteSecurityContext(&client->ctx);\n    }\n\n    sspi_functions->FreeCredentialsHandle(&client->cred);\n\n    return SSPI_OK;\n}\n"
  },
  {
    "path": "x/mongo/driver/auth/internal/gssapi/sspi_wrapper.h",
    "content": "// Copyright (C) MongoDB, Inc. 2022-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\n//+build gssapi,windows\n\n#ifndef SSPI_WRAPPER_H\n#define SSPI_WRAPPER_H\n\n#define SECURITY_WIN32 1  /* Required for SSPI */\n\n#include <windows.h>\n#include <sspi.h>\n\n#define SSPI_OK 0\n#define SSPI_CONTINUE 1\n#define SSPI_ERROR 2\n\ntypedef struct {\n    CredHandle cred;\n    CtxtHandle ctx;\n\n    int has_ctx;\n\n    SECURITY_STATUS status;\n} sspi_client_state;\n\nint sspi_init();\n\nint sspi_client_init(\n    sspi_client_state *client,\n    char* username,\n    char* password\n);\n\nint sspi_client_username(\n    sspi_client_state *client,\n    char** username\n);\n\nint sspi_client_negotiate(\n    sspi_client_state *client,\n    char* spn,\n    PVOID input,\n    ULONG input_length,\n    PVOID* output,\n    ULONG* output_length\n);\n\nint sspi_client_wrap_msg(\n    sspi_client_state *client,\n    PVOID input,\n    ULONG input_length,\n    PVOID* output,\n    ULONG* output_length\n);\n\nint sspi_client_destroy(\n    sspi_client_state *client\n);\n\n#endif\n"
  },
  {
    "path": "x/mongo/driver/auth/mongodbaws.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage auth\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"net/http\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/aws/credentials\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/credproviders\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/auth/creds\"\n)\n\n// MongoDBAWS is the mechanism name for MongoDBAWS.\nconst MongoDBAWS = \"MONGODB-AWS\"\n\nfunc newMongoDBAWSAuthenticator(cred *Cred, httpClient *http.Client) (Authenticator, error) {\n\tif cred.Source != \"\" && cred.Source != sourceExternal {\n\t\treturn nil, newAuthError(\"MONGODB-AWS source must be empty or $external\", nil)\n\t}\n\tif httpClient == nil {\n\t\treturn nil, errors.New(\"httpClient must not be nil\")\n\t}\n\treturn &MongoDBAWSAuthenticator{\n\t\tcredentials: &credproviders.StaticProvider{\n\t\t\tValue: credentials.Value{\n\t\t\t\tAccessKeyID:     cred.Username,\n\t\t\t\tSecretAccessKey: cred.Password,\n\t\t\t\tSessionToken:    cred.Props[\"AWS_SESSION_TOKEN\"],\n\t\t\t},\n\t\t},\n\t\thttpClient: httpClient,\n\t}, nil\n}\n\n// MongoDBAWSAuthenticator uses AWS-IAM credentials over SASL to authenticate a connection.\ntype MongoDBAWSAuthenticator struct {\n\tcredentials *credproviders.StaticProvider\n\thttpClient  *http.Client\n}\n\n// Auth authenticates the connection.\nfunc (a *MongoDBAWSAuthenticator) Auth(ctx context.Context, cfg *driver.AuthConfig) error {\n\tproviders := creds.NewAWSCredentialProvider(a.httpClient, a.credentials)\n\tadapter := &awsSaslAdapter{\n\t\tconversation: &awsConversation{\n\t\t\tcredentials: providers.Cred,\n\t\t},\n\t}\n\terr := ConductSaslConversation(ctx, cfg, sourceExternal, adapter)\n\tif err != nil {\n\t\treturn newAuthError(\"sasl conversation error\", err)\n\t}\n\treturn nil\n}\n\n// Reauth reauthenticates the connection.\nfunc (a *MongoDBAWSAuthenticator) Reauth(_ context.Context, _ *driver.AuthConfig) error {\n\treturn newAuthError(\"AWS authentication does not support reauthentication\", nil)\n}\n\ntype awsSaslAdapter struct {\n\tconversation *awsConversation\n}\n\nvar _ SaslClient = (*awsSaslAdapter)(nil)\n\nfunc (a *awsSaslAdapter) Start() (string, []byte, error) {\n\tstep, err := a.conversation.Step(nil)\n\tif err != nil {\n\t\treturn MongoDBAWS, nil, err\n\t}\n\treturn MongoDBAWS, step, nil\n}\n\nfunc (a *awsSaslAdapter) Next(_ context.Context, challenge []byte) ([]byte, error) {\n\tstep, err := a.conversation.Step(challenge)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn step, nil\n}\n\nfunc (a *awsSaslAdapter) Completed() bool {\n\treturn a.conversation.Done()\n}\n"
  },
  {
    "path": "x/mongo/driver/auth/mongodbaws_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage auth\n\nimport (\n\t\"errors\"\n\t\"testing\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n)\n\nfunc TestGetRegion(t *testing.T) {\n\tlongHost := make([]rune, 256)\n\temptyErr := errors.New(\"invalid STS host: empty\")\n\ttooLongErr := errors.New(\"invalid STS host: too large\")\n\temptyPartErr := errors.New(\"invalid STS host: empty part\")\n\ttestCases := []struct {\n\t\tname   string\n\t\thost   string\n\t\terr    error\n\t\tregion string\n\t}{\n\t\t{\"success default\", \"sts.amazonaws.com\", nil, \"us-east-1\"},\n\t\t{\"success parse\", \"first.second\", nil, \"second\"},\n\t\t{\"success no region\", \"first\", nil, \"us-east-1\"},\n\t\t{\"error host too long\", string(longHost), tooLongErr, \"\"},\n\t\t{\"error host empty\", \"\", emptyErr, \"\"},\n\t\t{\"error empty middle part\", \"abc..def\", emptyPartErr, \"\"},\n\t\t{\"error empty part\", \"first.\", emptyPartErr, \"\"},\n\t}\n\tfor _, tc := range testCases {\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\treg, err := getRegion(tc.host)\n\t\t\tif tc.err == nil {\n\t\t\t\tassert.Nil(t, err, \"error getting region: %v\", err)\n\t\t\t\tassert.Equal(t, tc.region, reg, \"expected %v, got %v\", tc.region, reg)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tassert.NotNil(t, err, \"expected error, got nil\")\n\t\t\tassert.Equal(t, err, tc.err, \"expected error: %v, got: %v\", tc.err, err)\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "x/mongo/driver/auth/oidc.go",
    "content": "// Copyright (C) MongoDB, Inc. 2024-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage auth\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"os\"\n\t\"regexp\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/mnet\"\n)\n\n// MongoDBOIDC is the string constant for the MONGODB-OIDC authentication mechanism.\nconst MongoDBOIDC = \"MONGODB-OIDC\"\n\n// Valid authMechanismProperties keys for MONGODB-OIDC.\nconst (\n\t// EnvironmentProp is the property key name that specifies the environment for the OIDC authenticator.\n\tEnvironmentProp = \"ENVIRONMENT\"\n\n\t// ResourceProp is the property key name that specifies the token resource for GCP and AZURE OIDC auth.\n\tResourceProp = \"TOKEN_RESOURCE\"\n\n\t// AllowedHostsProp is the property key name that specifies the allowed hosts for the OIDC authenticator.\n\tAllowedHostsProp = \"ALLOWED_HOSTS\"\n)\n\n// Valid ENVIRONMENT authMechismProperty values for MONGODB-OIDC.\nconst (\n\t// AzureEnvironmentValue is the value for the Azure environment.\n\tAzureEnvironmentValue = \"azure\"\n\n\t// GCPEnvironmentValue is the value for the GCP environment.\n\tGCPEnvironmentValue = \"gcp\"\n\n\t// K8SEnvironmentValue is the value for Kubernetes environments.\n\tK8SEnvironmentValue = \"k8s\"\n\n\t// TestEnvironmentValue is the value for the test environment.\n\tTestEnvironmentValue = \"test\"\n)\n\nconst (\n\tapiVersion             = 1\n\tinvalidateSleepTimeout = 100 * time.Millisecond\n\n\t// The CSOT specification says to apply a 1-minute timeout if \"CSOT is not applied\". That's\n\t// ambiguous for the v1.x Go Driver because it could mean either \"no timeout provided\" or \"CSOT not\n\t// enabled\". Always use a maximum timeout duration of 1 minute, allowing us to ignore the ambiguity.\n\t// Contexts with a shorter timeout are unaffected.\n\tmachineCallbackTimeout = time.Minute\n\thumanCallbackTimeout   = 5 * time.Minute\n)\n\nvar defaultAllowedHosts = []*regexp.Regexp{\n\tregexp.MustCompile(`^.*[.]mongodb[.]net(:\\d+)?$`),\n\tregexp.MustCompile(`^.*[.]mongodb-qa[.]net(:\\d+)?$`),\n\tregexp.MustCompile(`^.*[.]mongodb-dev[.]net(:\\d+)?$`),\n\tregexp.MustCompile(`^.*[.]mongodbgov[.]net(:\\d+)?$`),\n\tregexp.MustCompile(`^localhost(:\\d+)?$`),\n\tregexp.MustCompile(`^127[.]0[.]0[.]1(:\\d+)?$`),\n\tregexp.MustCompile(`^::1(:\\d+)?$`),\n\tregexp.MustCompile(`^.*[.]mongo[.]com(:\\d+)?$`),\n}\n\n// OIDCCallback is a function that takes a context and OIDCArgs and returns an OIDCCredential.\ntype OIDCCallback = driver.OIDCCallback\n\n// OIDCArgs contains the arguments for the OIDC callback.\ntype OIDCArgs = driver.OIDCArgs\n\n// OIDCCredential contains the access token and refresh token.\ntype OIDCCredential = driver.OIDCCredential\n\n// IDPInfo contains the information needed to perform OIDC authentication with an Identity Provider.\ntype IDPInfo = driver.IDPInfo\n\nvar (\n\t_ driver.Authenticator     = (*OIDCAuthenticator)(nil)\n\t_ SpeculativeAuthenticator = (*OIDCAuthenticator)(nil)\n\t_ SaslClient               = (*oidcOneStep)(nil)\n\t_ SaslClient               = (*oidcTwoStep)(nil)\n)\n\n// OIDCAuthenticator is synchronized and handles caching of the access token, refreshToken,\n// and IDPInfo. It also provides a mechanism to refresh the access token, but this functionality\n// is only for the OIDC Human flow.\ntype OIDCAuthenticator struct {\n\tmu sync.Mutex // Guards all of the info in the OIDCAuthenticator struct.\n\n\tAuthMechanismProperties map[string]string\n\tOIDCMachineCallback     OIDCCallback\n\tOIDCHumanCallback       OIDCCallback\n\n\tallowedHosts *[]*regexp.Regexp\n\tuserName     string\n\thttpClient   *http.Client\n\taccessToken  string\n\trefreshToken *string\n\tidpInfo      *IDPInfo\n\ttokenGenID   uint64\n}\n\n// SetAccessToken allows for manually setting the access token for the OIDCAuthenticator, this is\n// only for testing purposes.\nfunc (oa *OIDCAuthenticator) SetAccessToken(accessToken string) {\n\toa.mu.Lock()\n\tdefer oa.mu.Unlock()\n\toa.accessToken = accessToken\n}\n\nfunc newOIDCAuthenticator(cred *Cred, httpClient *http.Client) (Authenticator, error) {\n\tif cred.Source != \"\" && cred.Source != sourceExternal {\n\t\treturn nil, newAuthError(\"MONGODB-OIDC source must be empty or $external\", nil)\n\t}\n\tif cred.Password != \"\" {\n\t\treturn nil, fmt.Errorf(\"password cannot be specified for %q\", MongoDBOIDC)\n\t}\n\tif cred.Props != nil {\n\t\tif env, ok := cred.Props[EnvironmentProp]; ok {\n\t\t\tswitch strings.ToLower(env) {\n\t\t\tcase AzureEnvironmentValue, GCPEnvironmentValue:\n\t\t\t\tif _, ok := cred.Props[ResourceProp]; !ok {\n\t\t\t\t\treturn nil, fmt.Errorf(\"%q must be specified for %q %q\", ResourceProp, env, EnvironmentProp)\n\t\t\t\t}\n\t\t\t\tfallthrough\n\t\t\tcase K8SEnvironmentValue, TestEnvironmentValue:\n\t\t\t\tif cred.OIDCMachineCallback != nil || cred.OIDCHumanCallback != nil {\n\t\t\t\t\treturn nil, fmt.Errorf(\"OIDC callbacks are not allowed for %q %q\", env, EnvironmentProp)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\toa := &OIDCAuthenticator{\n\t\tuserName:                cred.Username,\n\t\thttpClient:              httpClient,\n\t\tAuthMechanismProperties: cred.Props,\n\t\tOIDCMachineCallback:     cred.OIDCMachineCallback,\n\t\tOIDCHumanCallback:       cred.OIDCHumanCallback,\n\t}\n\terr := oa.setAllowedHosts()\n\treturn oa, err\n}\n\nfunc createPatternsForGlobs(hosts []string) ([]*regexp.Regexp, error) {\n\tvar err error\n\tret := make([]*regexp.Regexp, len(hosts))\n\tfor i := range hosts {\n\t\thosts[i] = strings.ReplaceAll(hosts[i], \".\", \"[.]\")\n\t\thosts[i] = strings.ReplaceAll(hosts[i], \"*\", \".*\")\n\t\thosts[i] = \"^\" + hosts[i] + \"(:\\\\d+)?$\"\n\t\tret[i], err = regexp.Compile(hosts[i])\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\treturn ret, nil\n}\n\nfunc (oa *OIDCAuthenticator) setAllowedHosts() error {\n\tif oa.AuthMechanismProperties == nil {\n\t\toa.allowedHosts = &defaultAllowedHosts\n\t\treturn nil\n\t}\n\n\tallowedHosts, ok := oa.AuthMechanismProperties[AllowedHostsProp]\n\tif !ok {\n\t\toa.allowedHosts = &defaultAllowedHosts\n\t\treturn nil\n\t}\n\tglobs := strings.Split(allowedHosts, \",\")\n\tret, err := createPatternsForGlobs(globs)\n\tif err != nil {\n\t\treturn err\n\t}\n\toa.allowedHosts = &ret\n\treturn nil\n}\n\nfunc (oa *OIDCAuthenticator) validateConnectionAddressWithAllowedHosts(conn *mnet.Connection) error {\n\tif oa.allowedHosts == nil {\n\t\t// should be unreachable, but this is a safety check.\n\t\treturn newAuthError(fmt.Sprintf(\"%q missing\", AllowedHostsProp), nil)\n\t}\n\tallowedHosts := *oa.allowedHosts\n\tif len(allowedHosts) == 0 {\n\t\treturn newAuthError(fmt.Sprintf(\"empty %q specified\", AllowedHostsProp), nil)\n\t}\n\tfor _, pattern := range allowedHosts {\n\t\tif pattern.MatchString(string(conn.Address())) {\n\t\t\treturn nil\n\t\t}\n\t}\n\treturn newAuthError(fmt.Sprintf(\"address %q not allowed by %q: %v\", conn.Address(), AllowedHostsProp, allowedHosts), nil)\n}\n\ntype oidcOneStep struct {\n\tuserName    string\n\taccessToken string\n}\n\ntype oidcTwoStep struct {\n\tconn *mnet.Connection\n\toa   *OIDCAuthenticator\n}\n\nfunc jwtStepRequest(accessToken string) []byte {\n\treturn bsoncore.NewDocumentBuilder().\n\t\tAppendString(\"jwt\", accessToken).\n\t\tBuild()\n}\n\nfunc principalStepRequest(principal string) []byte {\n\tdoc := bsoncore.NewDocumentBuilder()\n\tif principal != \"\" {\n\t\tdoc.AppendString(\"n\", principal)\n\t}\n\treturn doc.Build()\n}\n\nfunc (oos *oidcOneStep) Start() (string, []byte, error) {\n\treturn MongoDBOIDC, jwtStepRequest(oos.accessToken), nil\n}\n\nfunc (oos *oidcOneStep) Next(context.Context, []byte) ([]byte, error) {\n\treturn nil, newAuthError(\"unexpected step in OIDC authentication\", nil)\n}\n\nfunc (*oidcOneStep) Completed() bool {\n\treturn true\n}\n\nfunc (ots *oidcTwoStep) Start() (string, []byte, error) {\n\treturn MongoDBOIDC, principalStepRequest(ots.oa.userName), nil\n}\n\nfunc (ots *oidcTwoStep) Next(ctx context.Context, msg []byte) ([]byte, error) {\n\tvar idpInfo IDPInfo\n\terr := bson.Unmarshal(msg, &idpInfo)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"error unmarshaling BSON document: %w\", err)\n\t}\n\n\taccessToken, err := ots.oa.getAccessToken(ctx,\n\t\tots.conn,\n\t\t&OIDCArgs{\n\t\t\tVersion: apiVersion,\n\t\t\t// idpInfo is nil for machine callbacks in the current spec.\n\t\t\tIDPInfo: &idpInfo,\n\t\t\t// there is no way there could be a refresh token when there is no IDPInfo.\n\t\t\tRefreshToken: nil,\n\t\t},\n\t\t// two-step callbacks are always human callbacks.\n\t\tots.oa.OIDCHumanCallback)\n\n\treturn jwtStepRequest(accessToken), err\n}\n\nfunc (*oidcTwoStep) Completed() bool {\n\treturn true\n}\n\nfunc (oa *OIDCAuthenticator) providerCallback() (OIDCCallback, error) {\n\tenv, ok := oa.AuthMechanismProperties[EnvironmentProp]\n\tif !ok {\n\t\treturn nil, nil\n\t}\n\n\tswitch env {\n\tcase AzureEnvironmentValue:\n\t\tresource, ok := oa.AuthMechanismProperties[ResourceProp]\n\t\tif !ok {\n\t\t\treturn nil, newAuthError(fmt.Sprintf(\"%q must be specified for Azure OIDC\", ResourceProp), nil)\n\t\t}\n\t\treturn getAzureOIDCCallback(oa.userName, resource, oa.httpClient), nil\n\tcase GCPEnvironmentValue:\n\t\tresource, ok := oa.AuthMechanismProperties[ResourceProp]\n\t\tif !ok {\n\t\t\treturn nil, newAuthError(fmt.Sprintf(\"%q must be specified for GCP OIDC\", ResourceProp), nil)\n\t\t}\n\t\treturn getGCPOIDCCallback(resource, oa.httpClient), nil\n\tcase K8SEnvironmentValue:\n\t\treturn k8sOIDCCallback, nil\n\t}\n\n\treturn nil, fmt.Errorf(\"%q %q not supported for MONGODB-OIDC\", EnvironmentProp, env)\n}\n\n// getAzureOIDCCallback returns the callback for the Azure Identity Provider.\nfunc getAzureOIDCCallback(clientID string, resource string, httpClient *http.Client) OIDCCallback {\n\t// return the callback parameterized by the clientID and resource, also passing in the user\n\t// configured httpClient.\n\treturn func(ctx context.Context, _ *OIDCArgs) (*OIDCCredential, error) {\n\t\tresource = url.QueryEscape(resource)\n\t\tvar uri string\n\t\tif clientID != \"\" {\n\t\t\turi = fmt.Sprintf(\"http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=%s&client_id=%s\", resource, clientID)\n\t\t} else {\n\t\t\turi = fmt.Sprintf(\"http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=%s\", resource)\n\t\t}\n\t\treq, err := http.NewRequestWithContext(ctx, http.MethodGet, uri, nil)\n\t\tif err != nil {\n\t\t\treturn nil, newAuthError(\"error creating http request to Azure Identity Provider\", err)\n\t\t}\n\t\treq.Header.Add(\"Metadata\", \"true\")\n\t\treq.Header.Add(\"Accept\", \"application/json\")\n\t\tresp, err := httpClient.Do(req)\n\t\tif err != nil {\n\t\t\treturn nil, newAuthError(\"error getting access token from Azure Identity Provider\", err)\n\t\t}\n\t\tdefer resp.Body.Close()\n\t\tvar azureResp struct {\n\t\t\tAccessToken string `json:\"access_token\"`\n\t\t\tExpiresOn   int64  `json:\"expires_on,string\"`\n\t\t}\n\n\t\tif resp.StatusCode != http.StatusOK {\n\t\t\treturn nil, newAuthError(fmt.Sprintf(\"failed to get a valid response from Azure Identity Provider, http code: %d\", resp.StatusCode), nil)\n\t\t}\n\t\terr = json.NewDecoder(resp.Body).Decode(&azureResp)\n\t\tif err != nil {\n\t\t\treturn nil, newAuthError(\"failed parsing result from Azure Identity Provider\", err)\n\t\t}\n\t\texpireTime := time.Unix(azureResp.ExpiresOn, 0)\n\t\treturn &OIDCCredential{\n\t\t\tAccessToken: azureResp.AccessToken,\n\t\t\tExpiresAt:   &expireTime,\n\t\t}, nil\n\t}\n}\n\n// getGCPOIDCCallback returns the callback for the GCP Identity Provider.\nfunc getGCPOIDCCallback(resource string, httpClient *http.Client) OIDCCallback {\n\t// return the callback parameterized by the clientID and resource, also passing in the user\n\t// configured httpClient.\n\treturn func(ctx context.Context, _ *OIDCArgs) (*OIDCCredential, error) {\n\t\tresource = url.QueryEscape(resource)\n\t\turi := fmt.Sprintf(\"http://metadata/computeMetadata/v1/instance/service-accounts/default/identity?audience=%s\", resource)\n\t\treq, err := http.NewRequestWithContext(ctx, http.MethodGet, uri, nil)\n\t\tif err != nil {\n\t\t\treturn nil, newAuthError(\"error creating http request to GCP Identity Provider\", err)\n\t\t}\n\t\treq.Header.Add(\"Metadata-Flavor\", \"Google\")\n\t\tresp, err := httpClient.Do(req)\n\t\tif err != nil {\n\t\t\treturn nil, newAuthError(\"error getting access token from GCP Identity Provider\", err)\n\t\t}\n\t\tdefer resp.Body.Close()\n\t\tif resp.StatusCode != http.StatusOK {\n\t\t\treturn nil, newAuthError(fmt.Sprintf(\"failed to get a valid response from GCP Identity Provider, http code: %d\", resp.StatusCode), nil)\n\t\t}\n\t\taccessToken, err := io.ReadAll(resp.Body)\n\t\tif err != nil {\n\t\t\treturn nil, newAuthError(\"failed parsing reading response from GCP Identity Provider\", err)\n\t\t}\n\t\treturn &OIDCCredential{\n\t\t\tAccessToken: string(accessToken),\n\t\t\tExpiresAt:   nil,\n\t\t}, nil\n\t}\n}\n\n// k8sOIDCCallbackfunc is the callback for the Kubernetes token provider.\nfunc k8sOIDCCallback(context.Context, *OIDCArgs) (*OIDCCredential, error) {\n\t// Check for the presence of the Azure and AWS token file path environment\n\t// variables. If neither are set, use the GKE default token file path.\n\tvar path string\n\tif p := os.Getenv(\"AZURE_FEDERATED_TOKEN_FILE\"); p != \"\" {\n\t\tpath = p\n\t} else if p := os.Getenv(\"AWS_WEB_IDENTITY_TOKEN_FILE\"); p != \"\" {\n\t\tpath = p\n\t} else {\n\t\tpath = \"/var/run/secrets/kubernetes.io/serviceaccount/token\"\n\t}\n\n\ttoken, err := os.ReadFile(path)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"error reading OIDC token from %q: %w\", path, err)\n\t}\n\n\treturn &OIDCCredential{\n\t\tAccessToken: string(token),\n\t}, nil\n}\n\nfunc (oa *OIDCAuthenticator) getAccessToken(\n\tctx context.Context,\n\tconn *mnet.Connection,\n\targs *OIDCArgs,\n\tcallback OIDCCallback,\n) (string, error) {\n\toa.mu.Lock()\n\tdefer oa.mu.Unlock()\n\n\tif oa.accessToken != \"\" {\n\t\treturn oa.accessToken, nil\n\t}\n\n\t// Attempt to refresh the access token if a refresh token is available.\n\tif args.RefreshToken != nil {\n\t\tcred, err := callback(ctx, args)\n\t\tif err == nil && cred != nil {\n\t\t\toa.accessToken = cred.AccessToken\n\t\t\toa.tokenGenID++\n\t\t\tconn.SetOIDCTokenGenID(oa.tokenGenID)\n\t\t\toa.refreshToken = cred.RefreshToken\n\t\t\treturn cred.AccessToken, nil\n\t\t}\n\t\toa.refreshToken = nil\n\t\targs.RefreshToken = nil\n\t}\n\t// If we get here this means there either was no refresh token or the refresh token failed.\n\tcred, err := callback(ctx, args)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\t// This line should never occur, if go conventions are followed, but it is a safety check such\n\t// that we do not throw nil pointer errors to our users if they abuse the API.\n\tif cred == nil {\n\t\treturn \"\", newAuthError(\"OIDC callback returned nil credential with no specified error\", nil)\n\t}\n\n\toa.accessToken = cred.AccessToken\n\toa.tokenGenID++\n\tconn.SetOIDCTokenGenID(oa.tokenGenID)\n\toa.refreshToken = cred.RefreshToken\n\t// always set the IdPInfo, in most cases, this should just be recopying the same pointer, or nil\n\t// in the machine flow.\n\toa.idpInfo = args.IDPInfo\n\n\treturn cred.AccessToken, nil\n}\n\n// invalidateAccessToken invalidates the access token, if the force flag is set to true (which is\n// only on a Reauth call) or if the tokenGenID of the connection is greater than or equal to the\n// tokenGenID of the OIDCAuthenticator. It should never actually be greater than, but only equal,\n// but this is a safety check, since extra invalidation is only a performance impact, not a\n// correctness impact.\nfunc (oa *OIDCAuthenticator) invalidateAccessToken(conn *mnet.Connection) {\n\toa.mu.Lock()\n\tdefer oa.mu.Unlock()\n\ttokenGenID := conn.OIDCTokenGenID()\n\t// If the connection used in a Reauth is a new connection it will not have a correct tokenGenID,\n\t// it will instead be set to 0. In the absence of information, the only safe thing to do is to\n\t// invalidate the cached accessToken.\n\tif tokenGenID == 0 || tokenGenID >= oa.tokenGenID {\n\t\toa.accessToken = \"\"\n\t\tconn.SetOIDCTokenGenID(0)\n\t}\n}\n\n// Reauth reauthenticates the connection when the server returns a 391 code. Reauth is part of the\n// driver.Authenticator interface.\nfunc (oa *OIDCAuthenticator) Reauth(ctx context.Context, cfg *driver.AuthConfig) error {\n\toa.invalidateAccessToken(cfg.Connection)\n\treturn oa.Auth(ctx, cfg)\n}\n\n// Auth authenticates the connection.\nfunc (oa *OIDCAuthenticator) Auth(ctx context.Context, cfg *driver.AuthConfig) error {\n\tvar err error\n\n\tif cfg == nil {\n\t\treturn newAuthError(fmt.Sprintf(\"config must be set for %q authentication\", MongoDBOIDC), nil)\n\t}\n\tconn := cfg.Connection\n\n\toa.mu.Lock()\n\tcachedAccessToken := oa.accessToken\n\tcachedRefreshToken := oa.refreshToken\n\tcachedIDPInfo := oa.idpInfo\n\toa.mu.Unlock()\n\n\tif cachedAccessToken != \"\" {\n\t\terr = ConductSaslConversation(ctx, cfg, sourceExternal, &oidcOneStep{\n\t\t\tuserName:    oa.userName,\n\t\t\taccessToken: cachedAccessToken,\n\t\t})\n\t\tif err == nil {\n\t\t\treturn nil\n\t\t}\n\t\t// this seems like it could be incorrect since we could be inavlidating an access token that\n\t\t// has already been replaced by a different auth attempt, but the TokenGenID will prevernt\n\t\t// that from happening.\n\t\toa.invalidateAccessToken(conn)\n\t\ttime.Sleep(invalidateSleepTimeout)\n\t}\n\n\tif oa.OIDCHumanCallback != nil {\n\t\treturn oa.doAuthHuman(ctx, cfg, oa.OIDCHumanCallback, cachedIDPInfo, cachedRefreshToken)\n\t}\n\n\t// Handle user provided or automatic provider machine callback.\n\tvar machineCallback OIDCCallback\n\tif oa.OIDCMachineCallback != nil {\n\t\tmachineCallback = oa.OIDCMachineCallback\n\t} else {\n\t\tmachineCallback, err = oa.providerCallback()\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"error getting built-in OIDC provider: %w\", err)\n\t\t}\n\t}\n\n\tif machineCallback != nil {\n\t\treturn oa.doAuthMachine(ctx, cfg, machineCallback)\n\t}\n\treturn newAuthError(\"no OIDC callback provided\", nil)\n}\n\nfunc (oa *OIDCAuthenticator) doAuthHuman(ctx context.Context, cfg *driver.AuthConfig, humanCallback OIDCCallback, idpInfo *IDPInfo, refreshToken *string) error {\n\t// Ensure that the connection address is allowed by the allowed hosts.\n\terr := oa.validateConnectionAddressWithAllowedHosts(cfg.Connection)\n\tif err != nil {\n\t\treturn err\n\t}\n\tsubCtx, cancel := context.WithTimeout(ctx, humanCallbackTimeout)\n\tdefer cancel()\n\t// If the idpInfo exists, we can just do one step\n\tif idpInfo != nil {\n\t\taccessToken, err := oa.getAccessToken(subCtx,\n\t\t\tcfg.Connection,\n\t\t\t&OIDCArgs{\n\t\t\t\tVersion: apiVersion,\n\t\t\t\t// idpInfo is nil for machine callbacks in the current spec.\n\t\t\t\tIDPInfo:      idpInfo,\n\t\t\t\tRefreshToken: refreshToken,\n\t\t\t},\n\t\t\thumanCallback)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn ConductSaslConversation(\n\t\t\tsubCtx,\n\t\t\tcfg,\n\t\t\tsourceExternal,\n\t\t\t&oidcOneStep{accessToken: accessToken},\n\t\t)\n\t}\n\t// otherwise, we need the two step where we ask the server for the IdPInfo first.\n\tots := &oidcTwoStep{\n\t\tconn: cfg.Connection,\n\t\toa:   oa,\n\t}\n\treturn ConductSaslConversation(subCtx, cfg, sourceExternal, ots)\n}\n\nfunc (oa *OIDCAuthenticator) doAuthMachine(ctx context.Context, cfg *driver.AuthConfig, machineCallback OIDCCallback) error {\n\tsubCtx, cancel := context.WithTimeout(ctx, machineCallbackTimeout)\n\taccessToken, err := oa.getAccessToken(subCtx,\n\t\tcfg.Connection,\n\t\t&OIDCArgs{\n\t\t\tVersion: apiVersion,\n\t\t\t// idpInfo is nil for machine callbacks in the current spec.\n\t\t\tIDPInfo:      nil,\n\t\t\tRefreshToken: nil,\n\t\t},\n\t\tmachineCallback)\n\tcancel()\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn ConductSaslConversation(\n\t\tctx,\n\t\tcfg,\n\t\tsourceExternal,\n\t\t&oidcOneStep{accessToken: accessToken},\n\t)\n}\n\n// CreateSpeculativeConversation creates a speculative conversation for OIDC authentication.\nfunc (oa *OIDCAuthenticator) CreateSpeculativeConversation() (SpeculativeConversation, error) {\n\toa.mu.Lock()\n\tdefer oa.mu.Unlock()\n\taccessToken := oa.accessToken\n\tif accessToken == \"\" {\n\t\treturn nil, nil // Skip speculative auth.\n\t}\n\n\treturn newSaslConversation(&oidcOneStep{accessToken: accessToken}, sourceExternal, true), nil\n}\n"
  },
  {
    "path": "x/mongo/driver/auth/oidc_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage auth\n\nimport (\n\t\"regexp\"\n\t\"testing\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n)\n\nfunc TestCreatePatternsForGlobs(t *testing.T) {\n\tt.Run(\"transform allowedHosts patterns\", func(t *testing.T) {\n\t\thosts := []string{\n\t\t\t\"*.mongodb.net\",\n\t\t\t\"*.mongodb-qa.net\",\n\t\t\t\"*.mongodb-dev.net\",\n\t\t\t\"*.mongodbgov.net\",\n\t\t\t\"localhost\",\n\t\t\t\"127.0.0.1\",\n\t\t\t\"::1\",\n\t\t\t\"*.mongo.com\",\n\t\t}\n\n\t\tcheck, err := createPatternsForGlobs(hosts)\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t,\n\t\t\t[]*regexp.Regexp{\n\t\t\t\tregexp.MustCompile(`^.*[.]mongodb[.]net(:\\d+)?$`),\n\t\t\t\tregexp.MustCompile(`^.*[.]mongodb-qa[.]net(:\\d+)?$`),\n\t\t\t\tregexp.MustCompile(`^.*[.]mongodb-dev[.]net(:\\d+)?$`),\n\t\t\t\tregexp.MustCompile(`^.*[.]mongodbgov[.]net(:\\d+)?$`),\n\t\t\t\tregexp.MustCompile(`^localhost(:\\d+)?$`),\n\t\t\t\tregexp.MustCompile(`^127[.]0[.]0[.]1(:\\d+)?$`),\n\t\t\t\tregexp.MustCompile(`^::1(:\\d+)?$`),\n\t\t\t\tregexp.MustCompile(`^.*[.]mongo[.]com(:\\d+)?$`),\n\t\t\t},\n\t\t\tcheck,\n\t\t)\n\t})\n}\n"
  },
  {
    "path": "x/mongo/driver/auth/plain.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage auth\n\nimport (\n\t\"context\"\n\t\"net/http\"\n\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver\"\n)\n\n// PLAIN is the mechanism name for PLAIN.\nconst PLAIN = \"PLAIN\"\n\nfunc newPlainAuthenticator(cred *Cred, _ *http.Client) (Authenticator, error) {\n\t// TODO(GODRIVER-3317): The PLAIN specification says about auth source:\n\t//\n\t// \"MUST be specified. Defaults to the database name if supplied on the\n\t// connection string or $external.\"\n\t//\n\t// We should actually pass through the auth source, not always pass\n\t// $external. If it's empty, we should default to $external.\n\t//\n\t// For example:\n\t//\n\t//  source := cred.Source\n\t//  if source == \"\" {\n\t//      source = \"$external\"\n\t//  }\n\t//\n\treturn &PlainAuthenticator{\n\t\tUsername: cred.Username,\n\t\tPassword: cred.Password,\n\t}, nil\n}\n\n// PlainAuthenticator uses the PLAIN algorithm over SASL to authenticate a connection.\ntype PlainAuthenticator struct {\n\tUsername string\n\tPassword string\n}\n\n// Auth authenticates the connection.\nfunc (a *PlainAuthenticator) Auth(ctx context.Context, cfg *driver.AuthConfig) error {\n\treturn ConductSaslConversation(ctx, cfg, sourceExternal, &plainSaslClient{\n\t\tusername: a.Username,\n\t\tpassword: a.Password,\n\t})\n}\n\n// Reauth reauthenticates the connection.\nfunc (a *PlainAuthenticator) Reauth(_ context.Context, _ *driver.AuthConfig) error {\n\treturn newAuthError(\"Plain authentication does not support reauthentication\", nil)\n}\n\ntype plainSaslClient struct {\n\tusername string\n\tpassword string\n}\n\nvar _ SaslClient = (*plainSaslClient)(nil)\n\nfunc (c *plainSaslClient) Start() (string, []byte, error) {\n\tb := []byte(\"\\x00\" + c.username + \"\\x00\" + c.password)\n\treturn PLAIN, b, nil\n}\n\nfunc (c *plainSaslClient) Next(context.Context, []byte) ([]byte, error) {\n\treturn nil, newAuthError(\"unexpected server challenge\", nil)\n}\n\nfunc (c *plainSaslClient) Completed() bool {\n\treturn true\n}\n"
  },
  {
    "path": "x/mongo/driver/auth/plain_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage auth_test\n\nimport (\n\t\"context\"\n\t\"encoding/base64\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/require\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/auth\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/description\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/drivertest\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/mnet\"\n)\n\nfunc TestPlainAuthenticator_Fails(t *testing.T) {\n\tt.Parallel()\n\n\tauthenticator := auth.PlainAuthenticator{\n\t\tUsername: \"user\",\n\t\tPassword: \"pencil\",\n\t}\n\n\tresps := make(chan []byte, 1)\n\twriteReplies(resps, bsoncore.BuildDocumentFromElements(nil,\n\t\tbsoncore.AppendInt32Element(nil, \"ok\", 1),\n\t\tbsoncore.AppendInt32Element(nil, \"conversationId\", 1),\n\t\tbsoncore.AppendBinaryElement(nil, \"payload\", 0x00, []byte{}),\n\t\tbsoncore.AppendInt32Element(nil, \"code\", 143),\n\t\tbsoncore.AppendBooleanElement(nil, \"done\", true),\n\t))\n\n\tdesc := description.Server{\n\t\tWireVersion: &description.VersionRange{\n\t\t\tMax: 6,\n\t\t},\n\t}\n\tc := &drivertest.ChannelConn{\n\t\tWritten:  make(chan []byte, 1),\n\t\tReadResp: resps,\n\t\tDesc:     desc,\n\t}\n\n\tmnetconn := mnet.NewConnection(c)\n\n\terr := authenticator.Auth(context.Background(), &driver.AuthConfig{Connection: mnetconn})\n\tif err == nil {\n\t\tt.Fatalf(\"expected an error but got none\")\n\t}\n\n\terrPrefix := \"unable to authenticate using mechanism \\\"PLAIN\\\"\"\n\tif !strings.HasPrefix(err.Error(), errPrefix) {\n\t\tt.Fatalf(\"expected an err starting with \\\"%s\\\" but got \\\"%s\\\"\", errPrefix, err)\n\t}\n}\n\nfunc TestPlainAuthenticator_Extra_server_message(t *testing.T) {\n\tt.Parallel()\n\n\tauthenticator := auth.PlainAuthenticator{\n\t\tUsername: \"user\",\n\t\tPassword: \"pencil\",\n\t}\n\n\tresps := make(chan []byte, 2)\n\twriteReplies(resps, bsoncore.BuildDocumentFromElements(nil,\n\t\tbsoncore.AppendInt32Element(nil, \"ok\", 1),\n\t\tbsoncore.AppendInt32Element(nil, \"conversationId\", 1),\n\t\tbsoncore.AppendBinaryElement(nil, \"payload\", 0x00, []byte{}),\n\t\tbsoncore.AppendBooleanElement(nil, \"done\", false),\n\t), bsoncore.BuildDocumentFromElements(nil,\n\t\tbsoncore.AppendInt32Element(nil, \"ok\", 1),\n\t\tbsoncore.AppendInt32Element(nil, \"conversationId\", 1),\n\t\tbsoncore.AppendBinaryElement(nil, \"payload\", 0x00, []byte{}),\n\t\tbsoncore.AppendBooleanElement(nil, \"done\", true),\n\t))\n\n\tdesc := description.Server{\n\t\tWireVersion: &description.VersionRange{\n\t\t\tMax: 6,\n\t\t},\n\t}\n\tc := &drivertest.ChannelConn{\n\t\tWritten:  make(chan []byte, 1),\n\t\tReadResp: resps,\n\t\tDesc:     desc,\n\t}\n\n\tmnetconn := mnet.NewConnection(c)\n\n\terr := authenticator.Auth(context.Background(), &driver.AuthConfig{Connection: mnetconn})\n\tif err == nil {\n\t\tt.Fatalf(\"expected an error but got none\")\n\t}\n\n\terrPrefix := \"unable to authenticate using mechanism \\\"PLAIN\\\": unexpected server challenge\"\n\tif !strings.HasPrefix(err.Error(), errPrefix) {\n\t\tt.Fatalf(\"expected an err starting with \\\"%s\\\" but got \\\"%s\\\"\", errPrefix, err)\n\t}\n}\n\nfunc TestPlainAuthenticator_Succeeds(t *testing.T) {\n\tt.Parallel()\n\n\tauthenticator := auth.PlainAuthenticator{\n\t\tUsername: \"user\",\n\t\tPassword: \"pencil\",\n\t}\n\n\tresps := make(chan []byte, 1)\n\twriteReplies(resps, bsoncore.BuildDocumentFromElements(nil,\n\t\tbsoncore.AppendInt32Element(nil, \"ok\", 1),\n\t\tbsoncore.AppendInt32Element(nil, \"conversationId\", 1),\n\t\tbsoncore.AppendBinaryElement(nil, \"payload\", 0x00, []byte{}),\n\t\tbsoncore.AppendBooleanElement(nil, \"done\", true),\n\t))\n\n\tdesc := description.Server{\n\t\tWireVersion: &description.VersionRange{\n\t\t\tMax: 6,\n\t\t},\n\t}\n\tc := &drivertest.ChannelConn{\n\t\tWritten:  make(chan []byte, 1),\n\t\tReadResp: resps,\n\t\tDesc:     desc,\n\t}\n\n\tmnetconn := mnet.NewConnection(c)\n\n\terr := authenticator.Auth(context.Background(), &driver.AuthConfig{Connection: mnetconn})\n\tif err != nil {\n\t\tt.Fatalf(\"expected no error but got \\\"%s\\\"\", err)\n\t}\n\n\tif len(c.Written) != 1 {\n\t\tt.Fatalf(\"expected 1 messages to be sent but had %d\", len(c.Written))\n\t}\n\n\tpayload, _ := base64.StdEncoding.DecodeString(\"AHVzZXIAcGVuY2ls\")\n\texpectedCmd := bsoncore.BuildDocumentFromElements(nil,\n\t\tbsoncore.AppendInt32Element(nil, \"saslStart\", 1),\n\t\tbsoncore.AppendStringElement(nil, \"mechanism\", \"PLAIN\"),\n\t\tbsoncore.AppendBinaryElement(nil, \"payload\", 0x00, payload),\n\t)\n\tcompareResponses(t, <-c.Written, expectedCmd, \"$external\")\n}\n\nfunc TestPlainAuthenticator_SucceedsBoolean(t *testing.T) {\n\tt.Parallel()\n\n\tauthenticator := auth.PlainAuthenticator{\n\t\tUsername: \"user\",\n\t\tPassword: \"pencil\",\n\t}\n\n\tresps := make(chan []byte, 1)\n\twriteReplies(resps, bsoncore.BuildDocumentFromElements(nil,\n\t\tbsoncore.AppendBooleanElement(nil, \"ok\", true),\n\t\tbsoncore.AppendInt32Element(nil, \"conversationId\", 1),\n\t\tbsoncore.AppendBinaryElement(nil, \"payload\", 0x00, []byte{}),\n\t\tbsoncore.AppendBooleanElement(nil, \"done\", true),\n\t))\n\n\tdesc := description.Server{\n\t\tWireVersion: &description.VersionRange{\n\t\t\tMax: 6,\n\t\t},\n\t}\n\tc := &drivertest.ChannelConn{\n\t\tWritten:  make(chan []byte, 1),\n\t\tReadResp: resps,\n\t\tDesc:     desc,\n\t}\n\n\tmnetconn := mnet.NewConnection(c)\n\n\terr := authenticator.Auth(context.Background(), &driver.AuthConfig{Connection: mnetconn})\n\trequire.NoError(t, err, \"Auth error\")\n\trequire.Len(t, c.Written, 1, \"expected 1 messages to be sent\")\n\n\tpayload, err := base64.StdEncoding.DecodeString(\"AHVzZXIAcGVuY2ls\")\n\trequire.NoError(t, err, \"DecodeString error\")\n\n\texpectedCmd := bsoncore.BuildDocumentFromElements(nil,\n\t\tbsoncore.AppendInt32Element(nil, \"saslStart\", 1),\n\t\tbsoncore.AppendStringElement(nil, \"mechanism\", \"PLAIN\"),\n\t\tbsoncore.AppendBinaryElement(nil, \"payload\", 0x00, payload),\n\t)\n\tcompareResponses(t, <-c.Written, expectedCmd, \"$external\")\n}\n\nfunc writeReplies(c chan []byte, docs ...bsoncore.Document) {\n\tfor _, doc := range docs {\n\t\treply := drivertest.MakeReply(doc)\n\t\tc <- reply\n\t}\n}\n"
  },
  {
    "path": "x/mongo/driver/auth/sasl.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage auth\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/operation\"\n)\n\n// SaslClient is the client piece of a sasl conversation.\ntype SaslClient interface {\n\tStart() (string, []byte, error)\n\tNext(ctx context.Context, challenge []byte) ([]byte, error)\n\tCompleted() bool\n}\n\n// SaslClientCloser is a SaslClient that has resources to clean up.\ntype SaslClientCloser interface {\n\tSaslClient\n\tClose()\n}\n\n// ExtraOptionsSaslClient is a SaslClient that appends options to the saslStart command.\ntype ExtraOptionsSaslClient interface {\n\tStartCommandOptions() bsoncore.Document\n}\n\n// saslConversation represents a SASL conversation. This type implements the SpeculativeConversation interface so the\n// conversation can be executed in multi-step speculative fashion.\ntype saslConversation struct {\n\tclient      SaslClient\n\tsource      string\n\tmechanism   string\n\tspeculative bool\n}\n\nvar _ SpeculativeConversation = (*saslConversation)(nil)\n\nfunc newSaslConversation(client SaslClient, source string, speculative bool) *saslConversation {\n\tauthSource := source\n\tif authSource == \"\" {\n\t\tauthSource = defaultAuthDB\n\t}\n\treturn &saslConversation{\n\t\tclient:      client,\n\t\tsource:      authSource,\n\t\tspeculative: speculative,\n\t}\n}\n\n// FirstMessage returns the first message to be sent to the server. This message contains a \"db\" field so it can be used\n// for speculative authentication.\nfunc (sc *saslConversation) FirstMessage() (bsoncore.Document, error) {\n\tvar payload []byte\n\tvar err error\n\tsc.mechanism, payload, err = sc.client.Start()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tsaslCmdElements := [][]byte{\n\t\tbsoncore.AppendInt32Element(nil, \"saslStart\", 1),\n\t\tbsoncore.AppendStringElement(nil, \"mechanism\", sc.mechanism),\n\t\tbsoncore.AppendBinaryElement(nil, \"payload\", 0x00, payload),\n\t}\n\tif sc.speculative {\n\t\t// The \"db\" field is only appended for speculative auth because the hello command is executed against admin\n\t\t// so this is needed to tell the server the user's auth source. For a non-speculative attempt, the SASL commands\n\t\t// will be executed against the auth source.\n\t\tsaslCmdElements = append(saslCmdElements, bsoncore.AppendStringElement(nil, \"db\", sc.source))\n\t}\n\tif extraOptionsClient, ok := sc.client.(ExtraOptionsSaslClient); ok {\n\t\toptionsDoc := extraOptionsClient.StartCommandOptions()\n\t\tsaslCmdElements = append(saslCmdElements, bsoncore.AppendDocumentElement(nil, \"options\", optionsDoc))\n\t}\n\n\treturn bsoncore.BuildDocumentFromElements(nil, saslCmdElements...), nil\n}\n\ntype saslResponse struct {\n\tConversationID int    `bson:\"conversationId\"`\n\tCode           int    `bson:\"code\"`\n\tDone           bool   `bson:\"done\"`\n\tPayload        []byte `bson:\"payload\"`\n}\n\n// Finish completes the conversation based on the first server response to authenticate the given connection.\nfunc (sc *saslConversation) Finish(ctx context.Context, cfg *driver.AuthConfig, firstResponse bsoncore.Document) error {\n\tif closer, ok := sc.client.(SaslClientCloser); ok {\n\t\tdefer closer.Close()\n\t}\n\n\tvar saslResp saslResponse\n\terr := bson.Unmarshal(firstResponse, &saslResp)\n\tif err != nil {\n\t\tfullErr := fmt.Errorf(\"unmarshal error: %w\", err)\n\t\treturn newError(fullErr, sc.mechanism)\n\t}\n\n\tcid := saslResp.ConversationID\n\tvar payload []byte\n\tvar rdr bsoncore.Document\n\tfor {\n\t\tif saslResp.Code != 0 {\n\t\t\treturn newError(err, sc.mechanism)\n\t\t}\n\n\t\tif saslResp.Done && sc.client.Completed() {\n\t\t\treturn nil\n\t\t}\n\n\t\tpayload, err = sc.client.Next(ctx, saslResp.Payload)\n\t\tif err != nil {\n\t\t\treturn newError(err, sc.mechanism)\n\t\t}\n\n\t\tif saslResp.Done && sc.client.Completed() {\n\t\t\treturn nil\n\t\t}\n\n\t\tdoc := bsoncore.BuildDocumentFromElements(nil,\n\t\t\tbsoncore.AppendInt32Element(nil, \"saslContinue\", 1),\n\t\t\tbsoncore.AppendInt32Element(nil, \"conversationId\", int32(cid)),\n\t\t\tbsoncore.AppendBinaryElement(nil, \"payload\", 0x00, payload),\n\t\t)\n\t\tsaslContinueCmd := operation.NewCommand(doc).\n\t\t\tDatabase(sc.source).\n\t\t\tDeployment(driver.SingleConnectionDeployment{cfg.Connection}).\n\t\t\tClusterClock(cfg.ClusterClock).\n\t\t\tServerAPI(cfg.ServerAPI)\n\n\t\terr = saslContinueCmd.Execute(ctx)\n\t\tif err != nil {\n\t\t\treturn newError(err, sc.mechanism)\n\t\t}\n\t\trdr = saslContinueCmd.Result()\n\n\t\terr = bson.Unmarshal(rdr, &saslResp)\n\t\tif err != nil {\n\t\t\tfullErr := fmt.Errorf(\"unmarshal error: %w\", err)\n\t\t\treturn newError(fullErr, sc.mechanism)\n\t\t}\n\t}\n}\n\n// ConductSaslConversation runs a full SASL conversation to authenticate the given connection.\nfunc ConductSaslConversation(ctx context.Context, cfg *driver.AuthConfig, authSource string, client SaslClient) error {\n\t// Create a non-speculative SASL conversation.\n\tconversation := newSaslConversation(client, authSource, false)\n\tsaslStartDoc, err := conversation.FirstMessage()\n\tif err != nil {\n\t\treturn newError(err, conversation.mechanism)\n\t}\n\tsaslStartCmd := operation.NewCommand(saslStartDoc).\n\t\tDatabase(authSource).\n\t\tDeployment(driver.SingleConnectionDeployment{cfg.Connection}).\n\t\tClusterClock(cfg.ClusterClock).\n\t\tServerAPI(cfg.ServerAPI)\n\tif err := saslStartCmd.Execute(ctx); err != nil {\n\t\treturn newError(err, conversation.mechanism)\n\t}\n\n\treturn conversation.Finish(ctx, cfg, saslStartCmd.Result())\n}\n"
  },
  {
    "path": "x/mongo/driver/auth/scram.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\n// Copyright (C) MongoDB, Inc. 2018-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage auth\n\nimport (\n\t\"context\"\n\t\"net/http\"\n\n\t\"github.com/xdg-go/scram\"\n\t\"github.com/xdg-go/stringprep\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver\"\n)\n\nconst (\n\t// SCRAMSHA1 holds the mechanism name \"SCRAM-SHA-1\"\n\tSCRAMSHA1 = \"SCRAM-SHA-1\"\n\n\t// SCRAMSHA256 holds the mechanism name \"SCRAM-SHA-256\"\n\tSCRAMSHA256 = \"SCRAM-SHA-256\"\n)\n\n// Additional options for the saslStart command to enable a shorter SCRAM conversation\nvar scramStartOptions bsoncore.Document = bsoncore.BuildDocumentFromElements(nil,\n\tbsoncore.AppendBooleanElement(nil, \"skipEmptyExchange\", true),\n)\n\nfunc newScramSHA1Authenticator(cred *Cred, _ *http.Client) (Authenticator, error) {\n\tsource := cred.Source\n\tif source == \"\" {\n\t\tsource = \"admin\"\n\t}\n\tpassdigest := mongoPasswordDigest(cred.Username, cred.Password)\n\tclient, err := scram.SHA1.NewClientUnprepped(cred.Username, passdigest, \"\")\n\tif err != nil {\n\t\treturn nil, newAuthError(\"error initializing SCRAM-SHA-1 client\", err)\n\t}\n\tclient.WithMinIterations(4096)\n\treturn &ScramAuthenticator{\n\t\tmechanism: SCRAMSHA1,\n\t\tsource:    source,\n\t\tclient:    client,\n\t}, nil\n}\n\nfunc newScramSHA256Authenticator(cred *Cred, _ *http.Client) (Authenticator, error) {\n\tsource := cred.Source\n\tif source == \"\" {\n\t\tsource = \"admin\"\n\t}\n\tpassprep, err := stringprep.SASLprep.Prepare(cred.Password)\n\tif err != nil {\n\t\treturn nil, newAuthError(\"error SASLprepping password\", err)\n\t}\n\tclient, err := scram.SHA256.NewClientUnprepped(cred.Username, passprep, \"\")\n\tif err != nil {\n\t\treturn nil, newAuthError(\"error initializing SCRAM-SHA-256 client\", err)\n\t}\n\tclient.WithMinIterations(4096)\n\treturn &ScramAuthenticator{\n\t\tmechanism: SCRAMSHA256,\n\t\tsource:    source,\n\t\tclient:    client,\n\t}, nil\n}\n\n// ScramAuthenticator uses the SCRAM algorithm over SASL to authenticate a connection.\ntype ScramAuthenticator struct {\n\tmechanism string\n\tsource    string\n\tclient    *scram.Client\n}\n\nvar _ SpeculativeAuthenticator = (*ScramAuthenticator)(nil)\n\n// Auth authenticates the provided connection by conducting a full SASL conversation.\nfunc (a *ScramAuthenticator) Auth(ctx context.Context, cfg *driver.AuthConfig) error {\n\terr := ConductSaslConversation(ctx, cfg, a.source, a.createSaslClient())\n\tif err != nil {\n\t\treturn newAuthError(\"sasl conversation error\", err)\n\t}\n\treturn nil\n}\n\n// Reauth reauthenticates the connection.\nfunc (a *ScramAuthenticator) Reauth(_ context.Context, _ *driver.AuthConfig) error {\n\treturn newAuthError(\"SCRAM does not support reauthentication\", nil)\n}\n\n// CreateSpeculativeConversation creates a speculative conversation for SCRAM authentication.\nfunc (a *ScramAuthenticator) CreateSpeculativeConversation() (SpeculativeConversation, error) {\n\treturn newSaslConversation(a.createSaslClient(), a.source, true), nil\n}\n\nfunc (a *ScramAuthenticator) createSaslClient() SaslClient {\n\treturn &scramSaslAdapter{\n\t\tconversation: a.client.NewConversation(),\n\t\tmechanism:    a.mechanism,\n\t}\n}\n\ntype scramSaslAdapter struct {\n\tmechanism    string\n\tconversation *scram.ClientConversation\n}\n\nvar (\n\t_ SaslClient             = (*scramSaslAdapter)(nil)\n\t_ ExtraOptionsSaslClient = (*scramSaslAdapter)(nil)\n)\n\nfunc (a *scramSaslAdapter) Start() (string, []byte, error) {\n\tstep, err := a.conversation.Step(\"\")\n\tif err != nil {\n\t\treturn a.mechanism, nil, err\n\t}\n\treturn a.mechanism, []byte(step), nil\n}\n\nfunc (a *scramSaslAdapter) Next(_ context.Context, challenge []byte) ([]byte, error) {\n\tstep, err := a.conversation.Step(string(challenge))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn []byte(step), nil\n}\n\nfunc (a *scramSaslAdapter) Completed() bool {\n\treturn a.conversation.Done()\n}\n\nfunc (*scramSaslAdapter) StartCommandOptions() bsoncore.Document {\n\treturn scramStartOptions\n}\n"
  },
  {
    "path": "x/mongo/driver/auth/scram_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage auth\n\nimport (\n\t\"context\"\n\t\"net/http\"\n\t\"testing\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/description\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/drivertest\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/mnet\"\n)\n\nconst (\n\tscramSha1Nonce   = \"fyko+d2lbbFgONRv9qkxdawL\"\n\tscramSha256Nonce = \"rOprNGfwEbeRWgbNEkqO\"\n)\n\nvar (\n\tscramSha1ShortPayloads = [][]byte{\n\t\t[]byte(\"r=fyko+d2lbbFgONRv9qkxdawLHo+Vgk7qvUOKUwuWLIWg4l/9SraGMHEE,s=rQ9ZY3MntBeuP3E1TDVC4w==,i=10000\"),\n\t\t[]byte(\"v=UMWeI25JD1yNYZRMpZ4VHvhZ9e0=\"),\n\t}\n\tscramSha256ShortPayloads = [][]byte{\n\t\t[]byte(\"r=rOprNGfwEbeRWgbNEkqO%hvYDpWUa2RaTCAfuxFIlj)hNlF$k0,s=W22ZaJ0SNY7soEsUEjb6gQ==,i=4096\"),\n\t\t[]byte(\"v=6rriTRBi23WpRR/wtup+mMhUZUn/dB5nLTJRsjl95G4=\"),\n\t}\n\tscramSha1LongPayloads   = append(scramSha1ShortPayloads, []byte{})\n\tscramSha256LongPayloads = append(scramSha256ShortPayloads, []byte{})\n)\n\nfunc TestSCRAM(t *testing.T) {\n\tt.Run(\"conversation\", func(t *testing.T) {\n\t\ttestCases := []struct {\n\t\t\tname                  string\n\t\t\tcreateAuthenticatorFn func(*Cred, *http.Client) (Authenticator, error)\n\t\t\tpayloads              [][]byte\n\t\t\tnonce                 string\n\t\t}{\n\t\t\t{\"scram-sha-1 short conversation\", newScramSHA1Authenticator, scramSha1ShortPayloads, scramSha1Nonce},\n\t\t\t{\"scram-sha-256 short conversation\", newScramSHA256Authenticator, scramSha256ShortPayloads, scramSha256Nonce},\n\t\t\t{\"scram-sha-1 long conversation\", newScramSHA1Authenticator, scramSha1LongPayloads, scramSha1Nonce},\n\t\t\t{\"scram-sha-256 long conversation\", newScramSHA256Authenticator, scramSha256LongPayloads, scramSha256Nonce},\n\t\t}\n\t\tfor _, tc := range testCases {\n\t\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\t\tauthenticator, err := tc.createAuthenticatorFn(\n\t\t\t\t\t&Cred{\n\t\t\t\t\t\tUsername: \"user\",\n\t\t\t\t\t\tPassword: \"pencil\",\n\t\t\t\t\t\tSource:   \"admin\",\n\t\t\t\t\t},\n\t\t\t\t\t&http.Client{})\n\t\t\t\tassert.Nil(t, err, \"error creating authenticator: %v\", err)\n\t\t\t\tsa, _ := authenticator.(*ScramAuthenticator)\n\t\t\t\tsa.client = sa.client.WithNonceGenerator(func() string {\n\t\t\t\t\treturn tc.nonce\n\t\t\t\t})\n\n\t\t\t\tresponses := make(chan []byte, len(tc.payloads))\n\t\t\t\twriteReplies(responses, createSCRAMConversation(tc.payloads)...)\n\n\t\t\t\tdesc := description.Server{\n\t\t\t\t\tWireVersion: &description.VersionRange{\n\t\t\t\t\t\tMax: 21,\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t\tchanconn := &drivertest.ChannelConn{\n\t\t\t\t\tWritten:  make(chan []byte, len(tc.payloads)),\n\t\t\t\t\tReadResp: responses,\n\t\t\t\t\tDesc:     desc,\n\t\t\t\t}\n\n\t\t\t\tconn := mnet.NewConnection(chanconn)\n\n\t\t\t\terr = authenticator.Auth(context.Background(), &driver.AuthConfig{Connection: conn})\n\t\t\t\tassert.Nil(t, err, \"Auth error: %v\\n\", err)\n\n\t\t\t\t// Verify that the first command sent is saslStart.\n\t\t\t\tassert.True(t, len(chanconn.Written) > 1, \"wire messages were written to the connection\")\n\t\t\t\tstartCmd, err := drivertest.GetCommandFromMsgWireMessage(<-chanconn.Written)\n\t\t\t\tassert.Nil(t, err, \"error parsing wire message: %v\", err)\n\t\t\t\tcmdName := startCmd.Index(0).Key()\n\t\t\t\tassert.Equal(t, cmdName, \"saslStart\", \"cmd name mismatch; expected 'saslStart', got %v\", cmdName)\n\n\t\t\t\t// Verify that the saslStart command always has {options: {skipEmptyExchange: true}}\n\t\t\t\toptionsVal, err := startCmd.LookupErr(\"options\")\n\t\t\t\tassert.Nil(t, err, \"no options found in saslStart command\")\n\t\t\t\toptionsDoc := optionsVal.Document()\n\t\t\t\tassert.Equal(t, optionsDoc, scramStartOptions, \"expected options %v, got %v\", scramStartOptions, optionsDoc)\n\t\t\t})\n\t\t}\n\t})\n}\n\nfunc createSCRAMConversation(payloads [][]byte) []bsoncore.Document {\n\tresponses := make([]bsoncore.Document, len(payloads))\n\tfor idx, payload := range payloads {\n\t\tres := createSCRAMServerResponse(payload, idx == len(payloads)-1)\n\t\tresponses[idx] = res\n\t}\n\treturn responses\n}\n\nfunc createSCRAMServerResponse(payload []byte, done bool) bsoncore.Document {\n\treturn bsoncore.BuildDocumentFromElements(nil,\n\t\tbsoncore.AppendInt32Element(nil, \"conversationId\", 1),\n\t\tbsoncore.AppendBinaryElement(nil, \"payload\", 0x00, payload),\n\t\tbsoncore.AppendBooleanElement(nil, \"done\", done),\n\t\tbsoncore.AppendInt32Element(nil, \"ok\", 1),\n\t)\n}\n\nfunc writeReplies(c chan []byte, docs ...bsoncore.Document) {\n\tfor _, doc := range docs {\n\t\treply := drivertest.MakeReply(doc)\n\t\tc <- reply\n\t}\n}\n"
  },
  {
    "path": "x/mongo/driver/auth/speculative_scram_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage auth\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"net/http\"\n\t\"testing\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/handshake\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/address\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/drivertest\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/mnet\"\n)\n\nvar (\n\t// The base elements for a hello response.\n\thandshakeHelloElements = [][]byte{\n\t\tbsoncore.AppendInt32Element(nil, \"ok\", 1),\n\t\tbsoncore.AppendBooleanElement(nil, handshake.LegacyHelloLowercase, true),\n\t\tbsoncore.AppendInt32Element(nil, \"maxBsonObjectSize\", 16777216),\n\t\tbsoncore.AppendInt32Element(nil, \"maxMessageSizeBytes\", 48000000),\n\t\tbsoncore.AppendInt32Element(nil, \"minWireVersion\", 0),\n\t\tbsoncore.AppendInt32Element(nil, \"maxWireVersion\", 6),\n\t}\n\t// The first payload sent by the driver for SCRAM-SHA-1/256 authentication.\n\tfirstScramSha1ClientPayload   = []byte(\"n,,n=user,r=fyko+d2lbbFgONRv9qkxdawL\")\n\tfirstScramSha256ClientPayload = []byte(\"n,,n=user,r=rOprNGfwEbeRWgbNEkqO\")\n)\n\nfunc TestSpeculativeSCRAM(t *testing.T) {\n\tcred := &Cred{\n\t\tUsername:    \"user\",\n\t\tPassword:    \"pencil\",\n\t\tPasswordSet: true,\n\t\tSource:      \"admin\",\n\t}\n\n\tt.Run(\"speculative response included\", func(t *testing.T) {\n\t\t// Tests for SCRAM-SHA1 and SCRAM-SHA-256 when the hello response contains a reply to the speculative\n\t\t// authentication attempt. The driver should only send a saslContinue after the hello to complete\n\t\t// authentication.\n\n\t\ttestCases := []struct {\n\t\t\tname               string\n\t\t\tmechanism          string\n\t\t\tfirstClientPayload []byte\n\t\t\tpayloads           [][]byte\n\t\t\tnonce              string\n\t\t}{\n\t\t\t{\"SCRAM-SHA-1\", \"SCRAM-SHA-1\", firstScramSha1ClientPayload, scramSha1ShortPayloads, scramSha1Nonce},\n\t\t\t{\"SCRAM-SHA-256\", \"SCRAM-SHA-256\", firstScramSha256ClientPayload, scramSha256ShortPayloads, scramSha256Nonce},\n\t\t\t{\"Default\", \"\", firstScramSha256ClientPayload, scramSha256ShortPayloads, scramSha256Nonce},\n\t\t}\n\n\t\tfor _, tc := range testCases {\n\t\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\t\t// Create a SCRAM authenticator and overwrite the nonce generator to make the conversation\n\t\t\t\t// deterministic.\n\t\t\t\tauthenticator, err := CreateAuthenticator(tc.mechanism, cred, &http.Client{})\n\t\t\t\tassert.Nil(t, err, \"CreateAuthenticator error: %v\", err)\n\t\t\t\tsetNonce(t, authenticator, tc.nonce)\n\n\t\t\t\t// Create a Handshaker and fake connection to authenticate.\n\t\t\t\thandshaker := Handshaker(nil, &HandshakeOptions{\n\t\t\t\t\tAuthenticator: authenticator,\n\t\t\t\t\tDBUser:        \"admin.user\",\n\t\t\t\t})\n\t\t\t\tresponses := make(chan []byte, len(tc.payloads))\n\t\t\t\twriteReplies(responses, createSpeculativeSCRAMHandshake(tc.payloads)...)\n\n\t\t\t\tconn := &drivertest.ChannelConn{\n\t\t\t\t\tWritten:  make(chan []byte, len(tc.payloads)),\n\t\t\t\t\tReadResp: responses,\n\t\t\t\t}\n\n\t\t\t\tmnetconn := mnet.NewConnection(conn)\n\n\t\t\t\t// Do both parts of the handshake.\n\t\t\t\tinfo, err := handshaker.GetHandshakeInformation(context.Background(), address.Address(\"localhost:27017\"), mnetconn)\n\t\t\t\tassert.Nil(t, err, \"GetHandshakeInformation error: %v\", err)\n\t\t\t\tassert.NotNil(t, info.SpeculativeAuthenticate, \"desc.SpeculativeAuthenticate not set\")\n\t\t\t\tconn.Desc = info.Description // Set conn.Desc so the new description will be used for the authentication.\n\n\t\t\t\terr = handshaker.FinishHandshake(context.Background(), mnetconn)\n\t\t\t\tassert.Nil(t, err, \"FinishHandshake error: %v\", err)\n\t\t\t\tassert.Equal(t, 0, len(conn.ReadResp), \"%d messages left unread\", len(conn.ReadResp))\n\n\t\t\t\t// Assert that the driver sent hello with the speculative authentication message.\n\t\t\t\tassert.Equal(t, len(tc.payloads), len(conn.Written), \"expected %d wire messages to be sent, got %d\",\n\t\t\t\t\tlen(tc.payloads), (conn.Written))\n\t\t\t\thelloCmd, err := drivertest.GetCommandFromQueryWireMessage(<-conn.Written)\n\t\t\t\tassert.Nil(t, err, \"error parsing hello command: %v\", err)\n\t\t\t\tassertCommandName(t, helloCmd, handshake.LegacyHello)\n\n\t\t\t\t// Assert that the correct document was sent for speculative authentication.\n\t\t\t\tauthDocVal, err := helloCmd.LookupErr(\"speculativeAuthenticate\")\n\t\t\t\tassert.Nil(t, err, \"expected command %s to contain 'speculativeAuthenticate'\", bson.Raw(helloCmd))\n\t\t\t\tauthDoc := authDocVal.Document()\n\t\t\t\tsentMechanism := tc.mechanism\n\t\t\t\tif sentMechanism == \"\" {\n\t\t\t\t\tsentMechanism = \"SCRAM-SHA-256\"\n\t\t\t\t}\n\n\t\t\t\texpectedAuthDoc := bsoncore.BuildDocumentFromElements(nil,\n\t\t\t\t\tbsoncore.AppendInt32Element(nil, \"saslStart\", 1),\n\t\t\t\t\tbsoncore.AppendStringElement(nil, \"mechanism\", sentMechanism),\n\t\t\t\t\tbsoncore.AppendBinaryElement(nil, \"payload\", 0x00, tc.firstClientPayload),\n\t\t\t\t\tbsoncore.AppendStringElement(nil, \"db\", \"admin\"),\n\t\t\t\t\tbsoncore.AppendDocumentElement(nil, \"options\", bsoncore.BuildDocumentFromElements(nil,\n\t\t\t\t\t\tbsoncore.AppendBooleanElement(nil, \"skipEmptyExchange\", true),\n\t\t\t\t\t)),\n\t\t\t\t)\n\t\t\t\tassert.True(t, bytes.Equal(expectedAuthDoc, authDoc),\n\t\t\t\t\t\"expected speculative auth document %s, got %s\",\n\t\t\t\t\tbson.Raw(expectedAuthDoc),\n\t\t\t\t\tauthDoc,\n\t\t\t\t)\n\n\t\t\t\t// Assert that the last command sent in the handshake is saslContinue.\n\n\t\t\t\tsaslContinueCmd, err := drivertest.GetCommandFromMsgWireMessage(<-conn.Written)\n\t\t\t\tassert.Nil(t, err, \"error parsing saslContinue command: %v\", err)\n\t\t\t\tassertCommandName(t, saslContinueCmd, \"saslContinue\")\n\t\t\t})\n\t\t}\n\t})\n\tt.Run(\"speculative response not included\", func(t *testing.T) {\n\t\t// Tests for SCRAM-SHA-1 and SCRAM-SHA-256 when the hello response does not contain a reply to the\n\t\t// speculative authentication attempt. The driver should send both saslStart and saslContinue after the initial\n\t\t// hello.\n\n\t\t// There is no test for the default mechanism because we can't control the nonce used for the actual\n\t\t// authentication attempt after the speculative attempt fails.\n\n\t\ttestCases := []struct {\n\t\t\tmechanism string\n\t\t\tpayloads  [][]byte\n\t\t\tnonce     string\n\t\t}{\n\t\t\t{\"SCRAM-SHA-1\", scramSha1ShortPayloads, scramSha1Nonce},\n\t\t\t{\"SCRAM-SHA-256\", scramSha256ShortPayloads, scramSha256Nonce},\n\t\t}\n\n\t\tfor _, tc := range testCases {\n\t\t\tt.Run(tc.mechanism, func(t *testing.T) {\n\t\t\t\tauthenticator, err := CreateAuthenticator(tc.mechanism, cred, &http.Client{})\n\t\t\t\tassert.Nil(t, err, \"CreateAuthenticator error: %v\", err)\n\t\t\t\tsetNonce(t, authenticator, tc.nonce)\n\n\t\t\t\thandshaker := Handshaker(nil, &HandshakeOptions{\n\t\t\t\t\tAuthenticator: authenticator,\n\t\t\t\t\tDBUser:        \"admin.user\",\n\t\t\t\t})\n\t\t\t\tnumResponses := len(tc.payloads) + 1 // +1 for hello response\n\t\t\t\tresponses := make(chan []byte, numResponses)\n\t\t\t\twriteReplies(responses, createRegularSCRAMHandshake(tc.payloads)...)\n\n\t\t\t\tconn := &drivertest.ChannelConn{\n\t\t\t\t\tWritten:  make(chan []byte, numResponses),\n\t\t\t\t\tReadResp: responses,\n\t\t\t\t}\n\n\t\t\t\tmnetconn := mnet.NewConnection(conn)\n\n\t\t\t\tinfo, err := handshaker.GetHandshakeInformation(context.Background(), address.Address(\"localhost:27017\"), mnetconn)\n\t\t\t\tassert.Nil(t, err, \"GetHandshakeInformation error: %v\", err)\n\t\t\t\tassert.Nil(t, info.SpeculativeAuthenticate, \"expected desc.SpeculativeAuthenticate to be unset, got %s\",\n\t\t\t\t\tbson.Raw(info.SpeculativeAuthenticate))\n\t\t\t\tconn.Desc = info.Description\n\n\t\t\t\terr = handshaker.FinishHandshake(context.Background(), mnetconn)\n\t\t\t\tassert.Nil(t, err, \"FinishHandshake error: %v\", err)\n\t\t\t\tassert.Equal(t, 0, len(conn.ReadResp), \"%d messages left unread\", len(conn.ReadResp))\n\n\t\t\t\tassert.Equal(t, numResponses, len(conn.Written), \"expected %d wire messages to be sent, got %d\",\n\t\t\t\t\tnumResponses, len(conn.Written))\n\t\t\t\thello, err := drivertest.GetCommandFromQueryWireMessage(<-conn.Written)\n\t\t\t\tassert.Nil(t, err, \"error parsing hello command: %v\", err)\n\t\t\t\tassertCommandName(t, hello, handshake.LegacyHello)\n\t\t\t\t_, err = hello.LookupErr(\"speculativeAuthenticate\")\n\t\t\t\tassert.Nil(t, err, \"expected command %s to contain 'speculativeAuthenticate'\", bson.Raw(hello))\n\n\t\t\t\tsaslStart, err := drivertest.GetCommandFromMsgWireMessage(<-conn.Written)\n\t\t\t\tassert.Nil(t, err, \"error parsing saslStart command: %v\", err)\n\t\t\t\tassertCommandName(t, saslStart, \"saslStart\")\n\n\t\t\t\tsaslContinue, err := drivertest.GetCommandFromMsgWireMessage(<-conn.Written)\n\t\t\t\tassert.Nil(t, err, \"error parsing saslContinue command: %v\", err)\n\t\t\t\tassertCommandName(t, saslContinue, \"saslContinue\")\n\t\t\t})\n\t\t}\n\t})\n}\n\nfunc setNonce(t *testing.T, authenticator Authenticator, nonce string) {\n\tt.Helper()\n\tnonceGenerator := func() string {\n\t\treturn nonce\n\t}\n\n\tswitch converted := authenticator.(type) {\n\tcase *ScramAuthenticator:\n\t\tconverted.client = converted.client.WithNonceGenerator(nonceGenerator)\n\tcase *DefaultAuthenticator:\n\t\tsa := converted.speculativeAuthenticator.(*ScramAuthenticator)\n\t\tsa.client = sa.client.WithNonceGenerator(nonceGenerator)\n\tdefault:\n\t\tt.Fatalf(\"invalid authenticator type %T\", authenticator)\n\t}\n}\n\n// createSpeculativeSCRAMHandshake creates the server replies for a successful speculative SCRAM authentication attempt.\n// There are two replies:\n//\n// 1. hello reply containing a \"speculativeAuthenticate\" document.\n// 2. saslContinue reply with done:true\nfunc createSpeculativeSCRAMHandshake(payloads [][]byte) []bsoncore.Document {\n\tfirstAuthResponse := createSCRAMServerResponse(payloads[0], false)\n\tfirstAuthElem := bsoncore.AppendDocumentElement(nil, \"speculativeAuthenticate\", firstAuthResponse)\n\thello := bsoncore.BuildDocumentFromElements(nil, append(handshakeHelloElements, firstAuthElem)...)\n\n\tresponses := []bsoncore.Document{hello}\n\tfor idx := 1; idx < len(payloads); idx++ {\n\t\tresponses = append(responses, createSCRAMServerResponse(payloads[idx], idx == len(payloads)-1))\n\t}\n\treturn responses\n}\n\n// createRegularSCRAMHandshake creates the server replies for a handshake + SCRAM authentication attempt. There are\n// three replies:\n//\n// 1. hello reply\n// 2. saslStart reply with done:false\n// 3. saslContinue reply with done:true\nfunc createRegularSCRAMHandshake(payloads [][]byte) []bsoncore.Document {\n\thello := bsoncore.BuildDocumentFromElements(nil, handshakeHelloElements...)\n\tresponses := []bsoncore.Document{hello}\n\n\tfor idx, payload := range payloads {\n\t\tresponses = append(responses, createSCRAMServerResponse(payload, idx == len(payloads)-1))\n\t}\n\treturn responses\n}\n\nfunc assertCommandName(t *testing.T, cmd bsoncore.Document, expectedName string) {\n\tt.Helper()\n\n\tactualName := cmd.Index(0).Key()\n\tassert.Equal(t, expectedName, actualName, \"expected command name '%s', got '%s'\", expectedName, actualName)\n}\n"
  },
  {
    "path": "x/mongo/driver/auth/speculative_x509_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage auth\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"net/http\"\n\t\"testing\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/handshake\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/address\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/drivertest\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/mnet\"\n)\n\nvar x509Response bsoncore.Document = bsoncore.BuildDocumentFromElements(nil,\n\tbsoncore.AppendStringElement(nil, \"dbname\", \"$external\"),\n\tbsoncore.AppendStringElement(nil, \"user\", \"username\"),\n\tbsoncore.AppendInt32Element(nil, \"ok\", 1),\n)\n\nfunc TestSpeculativeX509(t *testing.T) {\n\tt.Run(\"speculative response included\", func(t *testing.T) {\n\t\t// Tests for X509 when the hello response contains a reply to the speculative authentication attempt. The\n\t\t// driver should not send any more commands after the hello.\n\n\t\tauthenticator, err := CreateAuthenticator(\"MONGODB-X509\", &Cred{}, &http.Client{})\n\t\tassert.Nil(t, err, \"CreateAuthenticator error: %v\", err)\n\t\thandshaker := Handshaker(nil, &HandshakeOptions{\n\t\t\tAuthenticator: authenticator,\n\t\t})\n\n\t\tnumResponses := 1\n\t\tresponses := make(chan []byte, numResponses)\n\t\twriteReplies(responses, createSpeculativeX509Handshake()...)\n\n\t\tconn := &drivertest.ChannelConn{\n\t\t\tWritten:  make(chan []byte, numResponses),\n\t\t\tReadResp: responses,\n\t\t}\n\n\t\tmnetconn := mnet.NewConnection(conn)\n\n\t\tinfo, err := handshaker.GetHandshakeInformation(context.Background(), address.Address(\"localhost:27017\"), mnetconn)\n\t\tassert.Nil(t, err, \"GetDescription error: %v\", err)\n\t\tassert.NotNil(t, info.SpeculativeAuthenticate, \"desc.SpeculativeAuthenticate not set\")\n\t\tconn.Desc = info.Description\n\n\t\terr = handshaker.FinishHandshake(context.Background(), mnetconn)\n\t\tassert.Nil(t, err, \"FinishHandshake error: %v\", err)\n\t\tassert.Equal(t, 0, len(conn.ReadResp), \"%d messages left unread\", len(conn.ReadResp))\n\n\t\tassert.Equal(t, numResponses, len(conn.Written), \"expected %d wire messages to be sent, got %d\",\n\t\t\tnumResponses, len(conn.Written))\n\t\thello, err := drivertest.GetCommandFromQueryWireMessage(<-conn.Written)\n\t\tassert.Nil(t, err, \"error parsing hello command: %v\", err)\n\t\tassertCommandName(t, hello, handshake.LegacyHello)\n\n\t\tauthDocVal, err := hello.LookupErr(\"speculativeAuthenticate\")\n\t\tassert.Nil(t, err, \"expected command %s to contain 'speculativeAuthenticate'\", bson.Raw(hello))\n\t\tauthDoc := authDocVal.Document()\n\t\texpectedAuthDoc := bsoncore.BuildDocumentFromElements(nil,\n\t\t\tbsoncore.AppendInt32Element(nil, \"authenticate\", 1),\n\t\t\tbsoncore.AppendStringElement(nil, \"mechanism\", \"MONGODB-X509\"),\n\t\t)\n\t\tassert.True(t, bytes.Equal(expectedAuthDoc, authDoc), \"expected speculative auth document %s, got %s\",\n\t\t\texpectedAuthDoc, authDoc)\n\t})\n\tt.Run(\"speculative response not included\", func(t *testing.T) {\n\t\t// Tests for X509 when the hello response does not contain a reply to the speculative authentication attempt.\n\t\t// The driver should send an authenticate command after the hello.\n\n\t\tauthenticator, err := CreateAuthenticator(\"MONGODB-X509\", &Cred{}, &http.Client{})\n\t\tassert.Nil(t, err, \"CreateAuthenticator error: %v\", err)\n\t\thandshaker := Handshaker(nil, &HandshakeOptions{\n\t\t\tAuthenticator: authenticator,\n\t\t})\n\n\t\tnumResponses := 2\n\t\tresponses := make(chan []byte, numResponses)\n\t\twriteReplies(responses, createRegularX509Handshake()...)\n\n\t\tconn := &drivertest.ChannelConn{\n\t\t\tWritten:  make(chan []byte, numResponses),\n\t\t\tReadResp: responses,\n\t\t}\n\n\t\tmnetconn := mnet.NewConnection(conn)\n\n\t\tinfo, err := handshaker.GetHandshakeInformation(context.Background(), address.Address(\"localhost:27017\"), mnetconn)\n\t\tassert.Nil(t, err, \"GetDescription error: %v\", err)\n\t\tassert.Nil(t, info.SpeculativeAuthenticate, \"expected desc.SpeculativeAuthenticate to be unset, got %s\",\n\t\t\tbson.Raw(info.SpeculativeAuthenticate))\n\t\tconn.Desc = info.Description\n\n\t\terr = handshaker.FinishHandshake(context.Background(), mnetconn)\n\t\tassert.Nil(t, err, \"FinishHandshake error: %v\", err)\n\t\tassert.Equal(t, 0, len(conn.ReadResp), \"%d messages left unread\", len(conn.ReadResp))\n\n\t\tassert.Equal(t, numResponses, len(conn.Written), \"expected %d wire messages to be sent, got %d\",\n\t\t\tnumResponses, len(conn.Written))\n\t\thello, err := drivertest.GetCommandFromQueryWireMessage(<-conn.Written)\n\t\tassert.Nil(t, err, \"error parsing hello command: %v\", err)\n\t\tassertCommandName(t, hello, handshake.LegacyHello)\n\t\t_, err = hello.LookupErr(\"speculativeAuthenticate\")\n\t\tassert.Nil(t, err, \"expected command %s to contain 'speculativeAuthenticate'\", bson.Raw(hello))\n\n\t\tauthenticate, err := drivertest.GetCommandFromMsgWireMessage(<-conn.Written)\n\t\tassert.Nil(t, err, \"error parsing authenticate command: %v\", err)\n\t\tassertCommandName(t, authenticate, \"authenticate\")\n\t})\n}\n\n// createSpeculativeX509Handshake creates the server replies for a successful speculative X509 authentication attempt.\n// There is only one reply:\n//\n// 1. hello reply containing a \"speculativeAuthenticate\" document.\nfunc createSpeculativeX509Handshake() []bsoncore.Document {\n\tfirstAuthElem := bsoncore.AppendDocumentElement(nil, \"speculativeAuthenticate\", x509Response)\n\thello := bsoncore.BuildDocumentFromElements(nil, append(handshakeHelloElements, firstAuthElem)...)\n\treturn []bsoncore.Document{hello}\n}\n\n// createRegularX509Handshake creates the server replies for a handshake + X509 authentication attempt.\n// There are two replies:\n//\n// 1. hello reply\n// 2. authenticate reply\nfunc createRegularX509Handshake() []bsoncore.Document {\n\thello := bsoncore.BuildDocumentFromElements(nil, handshakeHelloElements...)\n\treturn []bsoncore.Document{hello, x509Response}\n}\n"
  },
  {
    "path": "x/mongo/driver/auth/util.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage auth\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\n\t// Ignore gosec warning \"Blocklisted import crypto/md5: weak cryptographic primitive\". We need\n\t// to use MD5 here to implement the SCRAM specification.\n\t/* #nosec G501 */\n\t\"crypto/md5\"\n)\n\nconst defaultAuthDB = \"admin\"\n\nfunc mongoPasswordDigest(username, password string) string {\n\t// Ignore gosec warning \"Use of weak cryptographic primitive\". We need to use MD5 here to\n\t// implement the SCRAM specification.\n\t/* #nosec G401 */\n\th := md5.New()\n\t_, _ = io.WriteString(h, username)\n\t_, _ = io.WriteString(h, \":mongo:\")\n\t_, _ = io.WriteString(h, password)\n\treturn fmt.Sprintf(\"%x\", h.Sum(nil))\n}\n"
  },
  {
    "path": "x/mongo/driver/auth/x509.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage auth\n\nimport (\n\t\"context\"\n\t\"net/http\"\n\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/operation\"\n)\n\n// MongoDBX509 is the mechanism name for MongoDBX509.\nconst MongoDBX509 = \"MONGODB-X509\"\n\nfunc newMongoDBX509Authenticator(cred *Cred, _ *http.Client) (Authenticator, error) {\n\t// TODO(GODRIVER-3309): Validate that cred.Source is either empty or\n\t// \"$external\" to make validation uniform with other auth mechanisms that\n\t// require Source to be \"$external\" (e.g. MONGODB-AWS, MONGODB-OIDC, etc).\n\treturn &MongoDBX509Authenticator{User: cred.Username}, nil\n}\n\n// MongoDBX509Authenticator uses X.509 certificates over TLS to authenticate a connection.\ntype MongoDBX509Authenticator struct {\n\tUser string\n}\n\nvar _ SpeculativeAuthenticator = (*MongoDBX509Authenticator)(nil)\n\n// x509 represents a X509 authentication conversation. This type implements the SpeculativeConversation interface so the\n// conversation can be executed in multi-step speculative fashion.\ntype x509Conversation struct{}\n\nvar _ SpeculativeConversation = (*x509Conversation)(nil)\n\n// FirstMessage returns the first message to be sent to the server.\nfunc (c *x509Conversation) FirstMessage() (bsoncore.Document, error) {\n\treturn createFirstX509Message(), nil\n}\n\n// createFirstX509Message creates the first message for the X509 conversation.\nfunc createFirstX509Message() bsoncore.Document {\n\telements := [][]byte{\n\t\tbsoncore.AppendInt32Element(nil, \"authenticate\", 1),\n\t\tbsoncore.AppendStringElement(nil, \"mechanism\", MongoDBX509),\n\t}\n\n\treturn bsoncore.BuildDocument(nil, elements...)\n}\n\n// Finish implements the SpeculativeConversation interface and is a no-op because an X509 conversation only has one\n// step.\nfunc (c *x509Conversation) Finish(context.Context, *driver.AuthConfig, bsoncore.Document) error {\n\treturn nil\n}\n\n// CreateSpeculativeConversation creates a speculative conversation for X509 authentication.\nfunc (a *MongoDBX509Authenticator) CreateSpeculativeConversation() (SpeculativeConversation, error) {\n\treturn &x509Conversation{}, nil\n}\n\n// Auth authenticates the provided connection by conducting an X509 authentication conversation.\nfunc (a *MongoDBX509Authenticator) Auth(ctx context.Context, cfg *driver.AuthConfig) error {\n\trequestDoc := createFirstX509Message()\n\tauthCmd := operation.\n\t\tNewCommand(requestDoc).\n\t\tDatabase(sourceExternal).\n\t\tDeployment(driver.SingleConnectionDeployment{cfg.Connection}).\n\t\tClusterClock(cfg.ClusterClock).\n\t\tServerAPI(cfg.ServerAPI)\n\terr := authCmd.Execute(ctx)\n\tif err != nil {\n\t\treturn newAuthError(\"round trip error\", err)\n\t}\n\n\treturn nil\n}\n\n// Reauth reauthenticates the connection.\nfunc (a *MongoDBX509Authenticator) Reauth(_ context.Context, _ *driver.AuthConfig) error {\n\treturn newAuthError(\"X509 does not support reauthentication\", nil)\n}\n"
  },
  {
    "path": "x/mongo/driver/batch_cursor.go",
    "content": "// Copyright (C) MongoDB, Inc. 2022-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage driver\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"strings\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/event\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/codecutil\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/csot\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/driverutil\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/description\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/mnet\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/session\"\n)\n\n// ErrNoCursor is returned by NewCursorResponse when the database response does\n// not contain a cursor.\nvar ErrNoCursor = errors.New(\"database response does not contain a cursor\")\n\n// BatchCursor is a batch implementation of a cursor. It returns documents in entire batches instead\n// of one at a time. An individual document cursor can be built on top of this batch cursor.\ntype BatchCursor struct {\n\tclientSession        *session.Client\n\tclock                *session.ClusterClock\n\tcomment              any\n\tencoderFn            codecutil.EncoderFn\n\tdatabase             string\n\tcollection           string\n\tid                   int64\n\terr                  error\n\tserver               Server\n\tserverDescription    description.Server\n\terrorProcessor       ErrorProcessor // This will only be set when pinning to a connection.\n\tconnection           *mnet.Connection\n\tbatchSize            int32\n\tcurrentBatch         *bsoncore.Iterator\n\tfirstBatch           bool\n\tcmdMonitor           *event.CommandMonitor\n\tpostBatchResumeToken bsoncore.Document\n\tcrypt                Crypt\n\tserverAPI            *ServerAPIOptions\n\n\t// maxAwaitTime is only valid for tailable awaitData cursors. If this option\n\t// is set, it will be used as the \"maxTimeMS\" field on getMore commands.\n\tmaxAwaitTime *time.Duration\n\n\t// legacy server (< 3.2) fields\n\tlimit       int32\n\tnumReturned int32 // number of docs returned by server\n}\n\n// CursorResponse represents the response from a command the results in a cursor. A BatchCursor can\n// be constructed from a CursorResponse.\ntype CursorResponse struct {\n\tServer               Server\n\tErrorProcessor       ErrorProcessor // This will only be set when pinning to a connection.\n\tConnection           *mnet.Connection\n\tDesc                 description.Server\n\tFirstBatch           *bsoncore.Iterator\n\tDatabase             string\n\tCollection           string\n\tID                   int64\n\tpostBatchResumeToken bsoncore.Document\n}\n\n// ExtractCursorDocument retrieves cursor document from a database response. If the\n// provided response does not contain a cursor, it returns ErrNoCursor.\nfunc ExtractCursorDocument(response bsoncore.Document) (bsoncore.Document, error) {\n\tcur, err := response.LookupErr(\"cursor\")\n\tif errors.Is(err, bsoncore.ErrElementNotFound) {\n\t\treturn nil, ErrNoCursor\n\t}\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"error getting cursor from database response: %w\", err)\n\t}\n\tcurDoc, ok := cur.DocumentOK()\n\tif !ok {\n\t\treturn nil, fmt.Errorf(\"cursor should be an embedded document but is BSON type %s\", cur.Type)\n\t}\n\treturn curDoc, nil\n}\n\n// NewCursorResponse constructs a cursor response from the given cursor document\n// extracted from a database response.\n//\n// NewCursorResponse can be used within the ProcessResponse method for an operation.\nfunc NewCursorResponse(response bsoncore.Document, info ResponseInfo) (CursorResponse, error) {\n\telems, err := response.Elements()\n\tif err != nil {\n\t\treturn CursorResponse{}, fmt.Errorf(\"error getting elements from cursor: %w\", err)\n\t}\n\tcurresp := CursorResponse{Server: info.Server, Desc: info.ConnectionDescription}\n\n\tfor _, elem := range elems {\n\t\tswitch elem.Key() {\n\t\tcase \"firstBatch\":\n\t\t\tarr, ok := elem.Value().ArrayOK()\n\t\t\tif !ok {\n\t\t\t\treturn CursorResponse{}, fmt.Errorf(\"firstBatch should be an array but is a BSON %s\", elem.Value().Type)\n\t\t\t}\n\n\t\t\tcurresp.FirstBatch = &bsoncore.Iterator{List: arr}\n\t\tcase \"ns\":\n\t\t\tns, ok := elem.Value().StringValueOK()\n\t\t\tif !ok {\n\t\t\t\treturn CursorResponse{}, fmt.Errorf(\"ns should be a string but is a BSON %s\", elem.Value().Type)\n\t\t\t}\n\t\t\tdatabase, collection, ok := strings.Cut(ns, \".\")\n\t\t\tif !ok {\n\t\t\t\treturn CursorResponse{}, errors.New(\"ns field must contain a valid namespace, but is missing '.'\")\n\t\t\t}\n\t\t\tcurresp.Database = database\n\t\t\tcurresp.Collection = collection\n\t\tcase \"id\":\n\t\t\tid, ok := elem.Value().Int64OK()\n\t\t\tif !ok {\n\t\t\t\treturn CursorResponse{}, fmt.Errorf(\"id should be an int64 but it is a BSON %s\", elem.Value().Type)\n\t\t\t}\n\t\t\tcurresp.ID = id\n\t\tcase \"postBatchResumeToken\":\n\t\t\ttoken, ok := elem.Value().DocumentOK()\n\t\t\tif !ok {\n\t\t\t\treturn CursorResponse{}, fmt.Errorf(\"post batch resume token should be a document but it is a BSON %s\", elem.Value().Type)\n\t\t\t}\n\t\t\tcurresp.postBatchResumeToken = token\n\t\t}\n\t}\n\n\t// If the deployment is behind a load balancer and the cursor has a non-zero ID, pin the cursor to a connection and\n\t// use the same connection to execute getMore and killCursors commands.\n\tif driverutil.IsServerLoadBalanced(curresp.Desc) && curresp.ID != 0 {\n\t\t// Cache the server as an ErrorProcessor to use when constructing deployments for cursor commands.\n\t\tep, ok := curresp.Server.(ErrorProcessor)\n\t\tif !ok {\n\t\t\treturn CursorResponse{}, fmt.Errorf(\"expected Server used to establish a cursor to implement ErrorProcessor, but got %T\", curresp.Server)\n\t\t}\n\t\tcurresp.ErrorProcessor = ep\n\n\t\trefConn := info.Connection.Pinner\n\t\tif refConn == nil {\n\t\t\treturn CursorResponse{}, fmt.Errorf(\"expected Connection used to establish a cursor to implement PinnedConnection, but got %T\", info.Connection)\n\t\t}\n\t\tif err := refConn.PinToCursor(); err != nil {\n\t\t\treturn CursorResponse{}, fmt.Errorf(\"error incrementing connection reference count when creating a cursor: %w\", err)\n\t\t}\n\t\tcurresp.Connection = info.Connection\n\t}\n\n\treturn curresp, nil\n}\n\n// CursorOptions are extra options that are required to construct a BatchCursor.\ntype CursorOptions struct {\n\tBatchSize             int32\n\tComment               bsoncore.Value\n\tLimit                 int32\n\tCommandMonitor        *event.CommandMonitor\n\tCrypt                 Crypt\n\tServerAPI             *ServerAPIOptions\n\tMarshalValueEncoderFn func(io.Writer) *bson.Encoder\n\n\t// MaxAwaitTime is only valid for tailable awaitData cursors. If this option\n\t// is set, it will be used as the \"maxTimeMS\" field on getMore commands.\n\tMaxAwaitTime *time.Duration\n}\n\n// SetMaxAwaitTime will set the maxTimeMS value on getMore commands for\n// tailable awaitData cursors.\nfunc (cursorOptions *CursorOptions) SetMaxAwaitTime(dur time.Duration) {\n\tcursorOptions.MaxAwaitTime = &dur\n}\n\n// NewBatchCursor creates a new BatchCursor from the provided parameters.\nfunc NewBatchCursor(\n\tcr CursorResponse,\n\tclientSession *session.Client,\n\tclock *session.ClusterClock,\n\topts CursorOptions,\n) (*BatchCursor, error) {\n\tfirstBatch := cr.FirstBatch\n\n\tbc := &BatchCursor{\n\t\tclientSession:        clientSession,\n\t\tclock:                clock,\n\t\tcomment:              opts.Comment,\n\t\tdatabase:             cr.Database,\n\t\tcollection:           cr.Collection,\n\t\tid:                   cr.ID,\n\t\tserver:               cr.Server,\n\t\tconnection:           cr.Connection,\n\t\terrorProcessor:       cr.ErrorProcessor,\n\t\tbatchSize:            opts.BatchSize,\n\t\tmaxAwaitTime:         opts.MaxAwaitTime,\n\t\tcmdMonitor:           opts.CommandMonitor,\n\t\tfirstBatch:           true,\n\t\tpostBatchResumeToken: cr.postBatchResumeToken,\n\t\tcrypt:                opts.Crypt,\n\t\tserverAPI:            opts.ServerAPI,\n\t\tserverDescription:    cr.Desc,\n\t\tencoderFn:            opts.MarshalValueEncoderFn,\n\t}\n\n\tif firstBatch != nil {\n\t\tbc.numReturned = int32(firstBatch.Count())\n\t}\n\n\tbc.currentBatch = firstBatch\n\n\treturn bc, nil\n}\n\n// NewEmptyBatchCursor returns a batch cursor that is empty.\nfunc NewEmptyBatchCursor() *BatchCursor {\n\treturn &BatchCursor{currentBatch: new(bsoncore.Iterator)}\n}\n\n// NewBatchCursorFromList returns a batch cursor with current batch set to an\n// itertor that can traverse the BSON data contained within the array.\nfunc NewBatchCursorFromList(array []byte) *BatchCursor {\n\treturn &BatchCursor{\n\t\tcurrentBatch: &bsoncore.Iterator{List: array},\n\t\tid:           0,\n\t\tserver:       nil,\n\t}\n}\n\n// ID returns the cursor ID for this batch cursor.\nfunc (bc *BatchCursor) ID() int64 {\n\treturn bc.id\n}\n\n// Next indicates if there is another batch available. Returning false does not necessarily indicate\n// that the cursor is closed. This method will return false when an empty batch is returned.\n//\n// If Next returns true, there is a valid batch of documents available. If Next returns false, there\n// is not a valid batch of documents available.\nfunc (bc *BatchCursor) Next(ctx context.Context) bool {\n\tif ctx == nil {\n\t\tctx = context.Background()\n\t}\n\n\tif bc.firstBatch {\n\t\tbc.firstBatch = false\n\t\treturn !bc.currentBatch.Empty()\n\t}\n\n\tif bc.id == 0 || bc.server == nil {\n\t\treturn false\n\t}\n\n\tbc.getMore(ctx)\n\n\treturn !bc.currentBatch.Empty()\n}\n\n// Batch will return a DocumentSequence for the current batch of documents. The returned\n// DocumentSequence is only valid until the next call to Next or Close.\nfunc (bc *BatchCursor) Batch() *bsoncore.Iterator {\n\treturn bc.currentBatch\n}\n\n// Err returns the latest error encountered.\nfunc (bc *BatchCursor) Err() error {\n\treturn bc.err\n}\n\n// Close closes this batch cursor.\nfunc (bc *BatchCursor) Close(ctx context.Context) error {\n\tif ctx == nil {\n\t\tctx = context.Background()\n\t}\n\n\terr := bc.KillCursor(ctx)\n\tbc.id = 0\n\n\tbc.currentBatch.List = nil\n\tbc.currentBatch.Reset()\n\n\tconnErr := bc.unpinConnection()\n\tif err == nil {\n\t\terr = connErr\n\t}\n\treturn err\n}\n\nfunc (bc *BatchCursor) unpinConnection() error {\n\tif bc.connection == nil || bc.connection.Pinner == nil {\n\t\treturn nil\n\t}\n\n\terr := bc.connection.UnpinFromCursor()\n\tcloseErr := bc.connection.Close()\n\tif err == nil && closeErr != nil {\n\t\terr = closeErr\n\t}\n\tbc.connection = nil\n\treturn err\n}\n\n// Server returns the server for this cursor.\nfunc (bc *BatchCursor) Server() Server {\n\treturn bc.server\n}\n\nfunc (bc *BatchCursor) clearBatch() {\n\tbc.currentBatch.List = bc.currentBatch.List[:0]\n}\n\n// KillCursor kills cursor on server without closing batch cursor\nfunc (bc *BatchCursor) KillCursor(ctx context.Context) error {\n\tif bc.server == nil || bc.id == 0 {\n\t\treturn nil\n\t}\n\n\treturn Operation{\n\t\tCommandFn: func(dst []byte, _ description.SelectedServer) ([]byte, error) {\n\t\t\tdst = bsoncore.AppendStringElement(dst, \"killCursors\", bc.collection)\n\t\t\tdst = bsoncore.BuildArrayElement(dst, \"cursors\", bsoncore.Value{Type: bsoncore.TypeInt64, Data: bsoncore.AppendInt64(nil, bc.id)})\n\t\t\treturn dst, nil\n\t\t},\n\t\tDatabase:       bc.database,\n\t\tDeployment:     bc.getOperationDeployment(),\n\t\tClient:         bc.clientSession,\n\t\tClock:          bc.clock,\n\t\tLegacy:         LegacyKillCursors,\n\t\tCommandMonitor: bc.cmdMonitor,\n\t\tServerAPI:      bc.serverAPI,\n\n\t\t// No read preference is passed to the killCursor command,\n\t\t// resulting in the default read preference: \"primaryPreferred\".\n\t\t// Since this could be confusing, and there is no requirement\n\t\t// to use a read preference here, we omit it.\n\t\tomitReadPreference: true,\n\t}.Execute(ctx)\n}\n\n// calcGetMoreBatchSize calculates the number of documents to return in the\n// response of a \"getMore\" operation based on the given limit, batchSize, and\n// number of documents already returned. Returns false if a non-trivial limit is\n// lower than or equal to the number of documents already returned.\nfunc calcGetMoreBatchSize(bc BatchCursor) (int32, bool) {\n\tgmBatchSize := bc.batchSize\n\n\t// Account for legacy operations that don't support setting a limit.\n\tif bc.limit != 0 && bc.numReturned+bc.batchSize >= bc.limit {\n\t\tgmBatchSize = bc.limit - bc.numReturned\n\t\tif gmBatchSize <= 0 {\n\t\t\treturn gmBatchSize, false\n\t\t}\n\t}\n\n\treturn gmBatchSize, true\n}\n\nfunc (bc *BatchCursor) getMore(ctx context.Context) {\n\tbc.clearBatch()\n\tif bc.id == 0 {\n\t\treturn\n\t}\n\n\tnumToReturn, ok := calcGetMoreBatchSize(*bc)\n\tif !ok {\n\t\tif err := bc.Close(ctx); err != nil {\n\t\t\tbc.err = err\n\t\t}\n\n\t\treturn\n\t}\n\n\tbc.err = Operation{\n\t\tCommandFn: func(dst []byte, _ description.SelectedServer) ([]byte, error) {\n\t\t\t// If maxAwaitTime > remaining timeoutMS - minRoundTripTime, then use\n\t\t\t// send remaining TimeoutMS - minRoundTripTime allowing the server an\n\t\t\t// opportunity to respond with an empty batch.\n\t\t\tvar maxTimeMS int64\n\t\t\tif bc.maxAwaitTime != nil {\n\t\t\t\t_, ctxDeadlineSet := ctx.Deadline()\n\n\t\t\t\tif ctxDeadlineSet {\n\t\t\t\t\trttMonitor := bc.Server().RTTMonitor()\n\n\t\t\t\t\tvar ok bool\n\t\t\t\t\tmaxTimeMS, ok = driverutil.CalculateMaxTimeMS(ctx, rttMonitor.Min())\n\t\t\t\t\tif !ok && maxTimeMS <= 0 {\n\t\t\t\t\t\treturn nil, fmt.Errorf(\n\t\t\t\t\t\t\t\"calculated server-side timeout (%v ms) is less than or equal to 0 (%v): %w\",\n\t\t\t\t\t\t\tmaxTimeMS,\n\t\t\t\t\t\t\trttMonitor.Stats(),\n\t\t\t\t\t\t\tErrDeadlineWouldBeExceeded)\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif !ctxDeadlineSet || bc.maxAwaitTime.Milliseconds() < maxTimeMS {\n\t\t\t\t\tmaxTimeMS = bc.maxAwaitTime.Milliseconds()\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tdst = bsoncore.AppendInt64Element(dst, \"getMore\", bc.id)\n\t\t\tdst = bsoncore.AppendStringElement(dst, \"collection\", bc.collection)\n\t\t\tif numToReturn > 0 {\n\t\t\t\tdst = bsoncore.AppendInt32Element(dst, \"batchSize\", numToReturn)\n\t\t\t}\n\n\t\t\tif maxTimeMS > 0 {\n\t\t\t\tdst = bsoncore.AppendInt64Element(dst, \"maxTimeMS\", maxTimeMS)\n\t\t\t}\n\n\t\t\tcomment, err := codecutil.MarshalValue(bc.comment, bc.encoderFn)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"error marshaling comment as a BSON value: %w\", err)\n\t\t\t}\n\n\t\t\t// The getMore command does not support commenting pre-4.4.\n\t\t\tif comment.Type != bsoncore.Type(0) && bc.serverDescription.WireVersion.Max >= 9 {\n\t\t\t\tdst = bsoncore.AppendValueElement(dst, \"comment\", comment)\n\t\t\t}\n\n\t\t\treturn dst, nil\n\t\t},\n\t\tDatabase:   bc.database,\n\t\tDeployment: bc.getOperationDeployment(),\n\t\tProcessResponseFn: func(_ context.Context, response bsoncore.Document, _ ResponseInfo) error {\n\t\t\tid, ok := response.Lookup(\"cursor\", \"id\").Int64OK()\n\t\t\tif !ok {\n\t\t\t\treturn fmt.Errorf(\"cursor.id should be an int64 but is a BSON %s\", response.Lookup(\"cursor\", \"id\").Type)\n\t\t\t}\n\t\t\tbc.id = id\n\n\t\t\tbatch, ok := response.Lookup(\"cursor\", \"nextBatch\").ArrayOK()\n\t\t\tif !ok {\n\t\t\t\treturn fmt.Errorf(\"cursor.nextBatch should be an array but is a BSON %s\", response.Lookup(\"cursor\", \"nextBatch\").Type)\n\t\t\t}\n\n\t\t\tbc.currentBatch.List = batch\n\t\t\tbc.currentBatch.Reset()\n\n\t\t\t// Required for legacy operations which don't support limit.\n\t\t\tbc.numReturned += int32(bc.currentBatch.Count())\n\n\t\t\tpbrt, err := response.LookupErr(\"cursor\", \"postBatchResumeToken\")\n\t\t\tif err != nil {\n\t\t\t\t// I don't really understand why we don't set bc.err here\n\t\t\t\treturn nil\n\t\t\t}\n\n\t\t\tpbrtDoc, ok := pbrt.DocumentOK()\n\t\t\tif !ok {\n\t\t\t\tbc.err = fmt.Errorf(\"expected BSON type for post batch resume token to be EmbeddedDocument but got %s\", pbrt.Type)\n\t\t\t\treturn nil\n\t\t\t}\n\n\t\t\tbc.postBatchResumeToken = pbrtDoc\n\n\t\t\treturn nil\n\t\t},\n\t\tClient:         bc.clientSession,\n\t\tClock:          bc.clock,\n\t\tLegacy:         LegacyGetMore,\n\t\tCommandMonitor: bc.cmdMonitor,\n\t\tCrypt:          bc.crypt,\n\t\tServerAPI:      bc.serverAPI,\n\n\t\t// Omit the automatically-calculated maxTimeMS because setting maxTimeMS\n\t\t// on a non-awaitData cursor causes a server error. For awaitData\n\t\t// cursors, maxTimeMS is set when maxAwaitTime is specified by the above\n\t\t// CommandFn.\n\t\tOmitMaxTimeMS: true,\n\n\t\t// No read preference is passed to the getMore command,\n\t\t// resulting in the default read preference: \"primaryPreferred\".\n\t\t// Since this could be confusing, and there is no requirement\n\t\t// to use a read preference here, we omit it.\n\t\tomitReadPreference: true,\n\t}.Execute(ctx)\n\n\t// Once the cursor has been drained, we can unpin the connection if one is currently pinned.\n\tif bc.id == 0 {\n\t\terr := bc.unpinConnection()\n\t\tif err != nil && bc.err == nil {\n\t\t\tbc.err = err\n\t\t}\n\t}\n\n\t// If we're in load balanced mode and the pinned connection encounters a network error, we should not use it for\n\t// future commands. Per the spec, the connection will not be unpinned until the cursor is actually closed, but\n\t// we set the cursor ID to 0 to ensure the Close() call will not execute a killCursors command.\n\tif driverErr, ok := bc.err.(Error); ok && driverErr.NetworkError() && bc.connection != nil {\n\t\tbc.id = 0\n\t}\n\n\t// Required for legacy operations which don't support limit.\n\tif bc.limit != 0 && bc.numReturned >= bc.limit {\n\t\t// call KillCursor instead of Close because Close will clear out the data for the current batch.\n\t\terr := bc.KillCursor(ctx)\n\t\tif err != nil && bc.err == nil {\n\t\t\tbc.err = err\n\t\t}\n\t}\n}\n\n// PostBatchResumeToken returns the latest seen post batch resume token.\nfunc (bc *BatchCursor) PostBatchResumeToken() bsoncore.Document {\n\treturn bc.postBatchResumeToken\n}\n\n// SetBatchSize sets the batchSize for future getMore operations.\nfunc (bc *BatchCursor) SetBatchSize(size int32) {\n\tbc.batchSize = size\n}\n\n// SetMaxAwaitTime will set the maximum amount of time the server will allow the\n// operations to execute. The server will error if this field is set but the\n// cursor is not configured with awaitData=true.\n//\n// The time.Duration value passed by this setter will be converted and rounded\n// down to the nearest millisecond.\nfunc (bc *BatchCursor) SetMaxAwaitTime(dur time.Duration) {\n\tbc.maxAwaitTime = &dur\n}\n\n// SetComment sets the comment for future getMore operations.\nfunc (bc *BatchCursor) SetComment(comment any) {\n\tbc.comment = comment\n}\n\nfunc (bc *BatchCursor) getOperationDeployment() Deployment {\n\tif bc.connection != nil {\n\t\treturn &loadBalancedCursorDeployment{\n\t\t\terrorProcessor: bc.errorProcessor,\n\t\t\tconn:           bc.connection,\n\t\t}\n\t}\n\treturn SingleServerDeployment{bc.server}\n}\n\n// MaxAwaitTime returns the maximum amount of time the server will allow\n// the operations to execute. This is only valid for tailable awaitData cursors.\nfunc (bc *BatchCursor) MaxAwaitTime() *time.Duration {\n\treturn bc.maxAwaitTime\n}\n\n// loadBalancedCursorDeployment is used as a Deployment for getMore and killCursors commands when pinning to a\n// connection in load balanced mode. This type also functions as an ErrorProcessor to ensure that SDAM errors are\n// handled for these commands in this mode.\ntype loadBalancedCursorDeployment struct {\n\terrorProcessor ErrorProcessor\n\tconn           *mnet.Connection\n}\n\nvar (\n\t_ Deployment     = (*loadBalancedCursorDeployment)(nil)\n\t_ Server         = (*loadBalancedCursorDeployment)(nil)\n\t_ ErrorProcessor = (*loadBalancedCursorDeployment)(nil)\n)\n\nfunc (lbcd *loadBalancedCursorDeployment) SelectServer(context.Context, description.ServerSelector) (Server, error) {\n\treturn lbcd, nil\n}\n\nfunc (lbcd *loadBalancedCursorDeployment) Kind() description.TopologyKind {\n\treturn description.TopologyKindLoadBalanced\n}\n\nfunc (lbcd *loadBalancedCursorDeployment) Connection(context.Context) (*mnet.Connection, error) {\n\treturn lbcd.conn, nil\n}\n\n// RTTMonitor implements the driver.Server interface.\nfunc (lbcd *loadBalancedCursorDeployment) RTTMonitor() RTTMonitor {\n\treturn &csot.ZeroRTTMonitor{}\n}\n\nfunc (lbcd *loadBalancedCursorDeployment) ProcessError(err error, desc mnet.Describer) ProcessErrorResult {\n\treturn lbcd.errorProcessor.ProcessError(err, desc)\n}\n\n// GetServerSelectionTimeout returns zero as a server selection timeout is not\n// applicable for load-balanced cursor deployments.\nfunc (*loadBalancedCursorDeployment) GetServerSelectionTimeout() time.Duration {\n\treturn 0\n}\n"
  },
  {
    "path": "x/mongo/driver/batch_cursor_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2022-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage driver\n\nimport (\n\t\"testing\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n)\n\nfunc TestBatchCursor(t *testing.T) {\n\tt.Parallel()\n\n\tt.Run(\"setBatchSize\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tvar size int32\n\t\tbc := &BatchCursor{\n\t\t\tbatchSize: size,\n\t\t}\n\t\tassert.Equal(t, size, bc.batchSize, \"expected batchSize %v, got %v\", size, bc.batchSize)\n\n\t\tsize = int32(4)\n\t\tbc.SetBatchSize(size)\n\t\tassert.Equal(t, size, bc.batchSize, \"expected batchSize %v, got %v\", size, bc.batchSize)\n\t})\n\n\tt.Run(\"calcGetMoreBatchSize\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tfor _, tcase := range []struct {\n\t\t\tname                               string\n\t\t\tsize, limit, numReturned, expected int32\n\t\t\tok                                 bool\n\t\t}{\n\t\t\t{\n\t\t\t\tname:     \"empty\",\n\t\t\t\texpected: 0,\n\t\t\t\tok:       true,\n\t\t\t},\n\t\t\t{\n\t\t\t\tname:     \"batchSize NEQ 0\",\n\t\t\t\tsize:     4,\n\t\t\t\texpected: 4,\n\t\t\t\tok:       true,\n\t\t\t},\n\t\t\t{\n\t\t\t\tname:     \"limit NEQ 0\",\n\t\t\t\tlimit:    4,\n\t\t\t\texpected: 0,\n\t\t\t\tok:       true,\n\t\t\t},\n\t\t\t{\n\t\t\t\tname:        \"limit NEQ and batchSize + numReturned EQ limit\",\n\t\t\t\tsize:        4,\n\t\t\t\tlimit:       8,\n\t\t\t\tnumReturned: 4,\n\t\t\t\texpected:    4,\n\t\t\t\tok:          true,\n\t\t\t},\n\t\t\t{\n\t\t\t\tname:        \"limit makes batchSize negative\",\n\t\t\t\tnumReturned: 4,\n\t\t\t\tlimit:       2,\n\t\t\t\texpected:    -2,\n\t\t\t\tok:          false,\n\t\t\t},\n\t\t} {\n\t\t\ttcase := tcase\n\t\t\tt.Run(tcase.name, func(t *testing.T) {\n\t\t\t\tt.Parallel()\n\n\t\t\t\tbc := &BatchCursor{\n\t\t\t\t\tlimit:       tcase.limit,\n\t\t\t\t\tbatchSize:   tcase.size,\n\t\t\t\t\tnumReturned: tcase.numReturned,\n\t\t\t\t}\n\n\t\t\t\tbc.SetBatchSize(tcase.size)\n\n\t\t\t\tsize, ok := calcGetMoreBatchSize(*bc)\n\n\t\t\t\tassert.Equal(t, tcase.expected, size, \"expected batchSize %v, got %v\", tcase.expected, size)\n\t\t\t\tassert.Equal(t, tcase.ok, ok, \"expected ok %v, got %v\", tcase.ok, ok)\n\t\t\t})\n\t\t}\n\t})\n}\n"
  },
  {
    "path": "x/mongo/driver/batches.go",
    "content": "// Copyright (C) MongoDB, Inc. 2022-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage driver\n\nimport (\n\t\"io\"\n\t\"strconv\"\n\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/wiremessage\"\n)\n\n// Batches contains the necessary information to batch split an operation. This is only used for write\n// operations.\ntype Batches struct {\n\tIdentifier string\n\tDocuments  []bsoncore.Document\n\tOrdered    *bool\n\n\toffset int\n}\n\nvar _ OperationBatches = &Batches{}\n\n// AppendBatchSequence appends dst with document sequence of batches as long as the limits of max count, max\n// document size, or total size allows. It returns the number of batches appended, the new appended slice, and\n// any error raised. It returns the origenal input slice if nothing can be appends within the limits.\nfunc (b *Batches) AppendBatchSequence(dst []byte, maxCount, totalSize int) (int, []byte, error) {\n\tif b.Size() == 0 {\n\t\treturn 0, dst, io.EOF\n\t}\n\tl := len(dst)\n\tvar idx int32\n\tdst = wiremessage.AppendMsgSectionType(dst, wiremessage.DocumentSequence)\n\tidx, dst = bsoncore.ReserveLength(dst)\n\tdst = append(dst, b.Identifier...)\n\tdst = append(dst, 0x00)\n\tvar size int\n\tvar n int\n\tfor i := b.offset; i < len(b.Documents); i++ {\n\t\tif n == maxCount {\n\t\t\tbreak\n\t\t}\n\t\tdoc := b.Documents[i]\n\t\tsize += len(doc)\n\t\tif size > totalSize {\n\t\t\tbreak\n\t\t}\n\t\tdst = append(dst, doc...)\n\t\tn++\n\t}\n\tif n == 0 {\n\t\treturn 0, dst[:l], nil\n\t}\n\tdst = bsoncore.UpdateLength(dst, idx, int32(len(dst[idx:])))\n\treturn n, dst, nil\n}\n\n// AppendBatchArray appends dst with array of batches as long as the limits of max count, max document size, or\n// total size allows. It returns the number of batches appended, the new appended slice, and any error raised. It\n// returns the origenal input slice if nothing can be appends within the limits.\nfunc (b *Batches) AppendBatchArray(dst []byte, maxCount, totalSize int) (int, []byte, error) {\n\tif b.Size() == 0 {\n\t\treturn 0, dst, io.EOF\n\t}\n\tl := len(dst)\n\taidx, dst := bsoncore.AppendArrayElementStart(dst, b.Identifier)\n\tvar size int\n\tvar n int\n\tfor i := b.offset; i < len(b.Documents); i++ {\n\t\tif n == maxCount {\n\t\t\tbreak\n\t\t}\n\t\tdoc := b.Documents[i]\n\t\tsize += len(doc)\n\t\tif size > totalSize {\n\t\t\tbreak\n\t\t}\n\t\tdst = bsoncore.AppendDocumentElement(dst, strconv.Itoa(n), doc)\n\t\tn++\n\t}\n\tif n == 0 {\n\t\treturn 0, dst[:l], nil\n\t}\n\tvar err error\n\tdst, err = bsoncore.AppendArrayEnd(dst, aidx)\n\tif err != nil {\n\t\treturn 0, nil, err\n\t}\n\treturn n, dst, nil\n}\n\n// IsOrdered indicates if the batches are ordered.\nfunc (b *Batches) IsOrdered() *bool {\n\treturn b.Ordered\n}\n\n// AdvanceBatches advances the batches with the given input.\nfunc (b *Batches) AdvanceBatches(n int) {\n\tb.offset += n\n\tif b.offset > len(b.Documents) {\n\t\tb.offset = len(b.Documents)\n\t}\n}\n\n// Size returns the size of batches remained.\nfunc (b *Batches) Size() int {\n\tif b.offset > len(b.Documents) {\n\t\treturn 0\n\t}\n\treturn len(b.Documents) - b.offset\n}\n"
  },
  {
    "path": "x/mongo/driver/batches_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2022-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage driver\n\nimport (\n\t\"testing\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/wiremessage\"\n)\n\nfunc newTestBatches(t *testing.T) *Batches {\n\tt.Helper()\n\treturn &Batches{\n\t\tIdentifier: \"foobar\",\n\t\tDocuments: []bsoncore.Document{\n\t\t\t[]byte(\"Lorem ipsum dolor sit amet\"),\n\t\t\t[]byte(\"consectetur adipiscing elit\"),\n\t\t},\n\t}\n}\n\nfunc TestAdvancing(t *testing.T) {\n\tbatches := newTestBatches(t)\n\tbatches.AdvanceBatches(3)\n\tsize := batches.Size()\n\tassert.Equal(t, 0, size, \"expected Size(): %d, got: %d\", 1, size)\n}\n\nfunc TestAppendBatchSequence(t *testing.T) {\n\tbatches := newTestBatches(t)\n\n\tgot := []byte{42}\n\tvar n int\n\tvar err error\n\tn, got, err = batches.AppendBatchSequence(got, 2, len(batches.Documents[0]))\n\tassert.NoError(t, err)\n\tassert.Equal(t, 1, n)\n\n\tvar idx int32\n\tdst := []byte{42}\n\tdst = wiremessage.AppendMsgSectionType(dst, wiremessage.DocumentSequence)\n\tidx, dst = bsoncore.ReserveLength(dst)\n\tdst = append(dst, \"foobar\"...)\n\tdst = append(dst, 0x00)\n\tdst = append(dst, \"Lorem ipsum dolor sit amet\"...)\n\tdst = bsoncore.UpdateLength(dst, idx, int32(len(dst[idx:])))\n\tassert.Equal(t, dst, got)\n}\n\nfunc TestAppendBatchArray(t *testing.T) {\n\tbatches := newTestBatches(t)\n\n\tgot := []byte{42}\n\tvar n int\n\tvar err error\n\tn, got, err = batches.AppendBatchArray(got, 2, len(batches.Documents[0]))\n\tassert.NoError(t, err)\n\tassert.Equal(t, 1, n)\n\n\tvar idx int32\n\tdst := []byte{42}\n\tidx, dst = bsoncore.AppendArrayElementStart(dst, \"foobar\")\n\tdst = bsoncore.AppendDocumentElement(dst, \"0\", []byte(\"Lorem ipsum dolor sit amet\"))\n\tdst, err = bsoncore.AppendArrayEnd(dst, idx)\n\tassert.NoError(t, err)\n\tassert.Equal(t, dst, got)\n}\n"
  },
  {
    "path": "x/mongo/driver/command_monitoring_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage driver\n\nimport (\n\t\"testing\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/handshake\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n)\n\nfunc TestCommandMonitoring(t *testing.T) {\n\tt.Run(\"redactCommand\", func(t *testing.T) {\n\t\temptyDoc := bsoncore.BuildDocumentFromElements(nil)\n\t\tlegacyHello := bsoncore.BuildDocumentFromElements(nil,\n\t\t\tbsoncore.AppendInt32Element(nil, handshake.LegacyHello, 1),\n\t\t)\n\t\tlegacyHelloLowercase := bsoncore.BuildDocumentFromElements(nil,\n\t\t\tbsoncore.AppendInt32Element(nil, handshake.LegacyHelloLowercase, 1),\n\t\t)\n\t\tlegacyHelloSpeculative := bsoncore.BuildDocumentFromElements(nil,\n\t\t\tbsoncore.AppendInt32Element(nil, handshake.LegacyHello, 1),\n\t\t\tbsoncore.AppendDocumentElement(nil, \"speculativeAuthenticate\", emptyDoc),\n\t\t)\n\t\tlegacyHelloSpeculativeLowercase := bsoncore.BuildDocumentFromElements(nil,\n\t\t\tbsoncore.AppendInt32Element(nil, handshake.LegacyHelloLowercase, 1),\n\t\t\tbsoncore.AppendDocumentElement(nil, \"speculativeAuthenticate\", emptyDoc),\n\t\t)\n\n\t\ttestCases := []struct {\n\t\t\tname        string\n\t\t\tcommandName string\n\t\t\tcommand     bsoncore.Document\n\t\t\tredacted    bool\n\t\t}{\n\t\t\t{\"legacy hello\", handshake.LegacyHello, legacyHello, false},\n\t\t\t{\"legacy hello lowercase\", handshake.LegacyHelloLowercase, legacyHelloLowercase, false},\n\t\t\t{\"legacy hello speculative auth\", handshake.LegacyHello, legacyHelloSpeculative, true},\n\t\t\t{\"legacy hello speculative auth lowercase\", handshake.LegacyHello, legacyHelloSpeculativeLowercase, true},\n\t\t}\n\t\tfor _, tc := range testCases {\n\t\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\t\tcanMonitor := (&Operation{}).redactCommand(tc.commandName, tc.command)\n\t\t\t\tassert.Equal(t, tc.redacted, canMonitor, \"expected redacted %v, got %v\", tc.redacted, canMonitor)\n\t\t\t})\n\t\t}\n\t})\n}\n"
  },
  {
    "path": "x/mongo/driver/compression.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage driver\n\nimport (\n\t\"bytes\"\n\t\"compress/zlib\"\n\t\"fmt\"\n\t\"io\"\n\t\"sync\"\n\n\t\"github.com/klauspost/compress/snappy\"\n\t\"github.com/klauspost/compress/zstd\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/wiremessage\"\n)\n\n// CompressionOpts holds settings for how to compress a payload\ntype CompressionOpts struct {\n\tCompressor       wiremessage.CompressorID\n\tZlibLevel        int\n\tZstdLevel        int\n\tUncompressedSize int32\n}\n\n// mustZstdNewWriter creates a zstd.Encoder with the given level and a nil\n// destination writer. It panics on any errors and should only be used at\n// package initialization time.\nfunc mustZstdNewWriter(lvl zstd.EncoderLevel) *zstd.Encoder {\n\tenc, err := zstd.NewWriter(\n\t\tnil,\n\t\tzstd.WithWindowSize(8<<20), // Set window size to 8MB.\n\t\tzstd.WithEncoderLevel(lvl),\n\t)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treturn enc\n}\n\nvar zstdEncoders = [zstd.SpeedBestCompression + 1]*zstd.Encoder{\n\t0:                           nil, // zstd.speedNotSet\n\tzstd.SpeedFastest:           mustZstdNewWriter(zstd.SpeedFastest),\n\tzstd.SpeedDefault:           mustZstdNewWriter(zstd.SpeedDefault),\n\tzstd.SpeedBetterCompression: mustZstdNewWriter(zstd.SpeedBetterCompression),\n\tzstd.SpeedBestCompression:   mustZstdNewWriter(zstd.SpeedBestCompression),\n}\n\nfunc getZstdEncoder(level zstd.EncoderLevel) (*zstd.Encoder, error) {\n\tif zstd.SpeedFastest <= level && level <= zstd.SpeedBestCompression {\n\t\treturn zstdEncoders[level], nil\n\t}\n\t// The level is outside the expected range, return an error.\n\treturn nil, fmt.Errorf(\"invalid zstd compression level: %d\", level)\n}\n\n// zlibEncodersOffset is the offset into the zlibEncoders array for a given\n// compression level.\nconst zlibEncodersOffset = -zlib.HuffmanOnly // HuffmanOnly == -2\n\nvar zlibEncoders [zlib.BestCompression + zlibEncodersOffset + 1]sync.Pool\n\nfunc getZlibEncoder(level int) (*zlibEncoder, error) {\n\tif zlib.HuffmanOnly <= level && level <= zlib.BestCompression {\n\t\tif enc, _ := zlibEncoders[level+zlibEncodersOffset].Get().(*zlibEncoder); enc != nil {\n\t\t\treturn enc, nil\n\t\t}\n\t\twriter, err := zlib.NewWriterLevel(nil, level)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tenc := &zlibEncoder{writer: writer, level: level}\n\t\treturn enc, nil\n\t}\n\t// The level is outside the expected range, return an error.\n\treturn nil, fmt.Errorf(\"invalid zlib compression level: %d\", level)\n}\n\nfunc putZlibEncoder(enc *zlibEncoder) {\n\tif enc != nil {\n\t\tzlibEncoders[enc.level+zlibEncodersOffset].Put(enc)\n\t}\n}\n\ntype zlibEncoder struct {\n\twriter *zlib.Writer\n\tbuf    bytes.Buffer\n\tlevel  int\n}\n\nfunc (e *zlibEncoder) Encode(dst, src []byte) ([]byte, error) {\n\tdefer putZlibEncoder(e)\n\n\te.buf.Reset()\n\te.writer.Reset(&e.buf)\n\n\t_, err := e.writer.Write(src)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\terr = e.writer.Close()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdst = append(dst[:0], e.buf.Bytes()...)\n\treturn dst, nil\n}\n\nvar zstdBufPool = sync.Pool{\n\tNew: func() any {\n\t\ts := make([]byte, 0)\n\t\treturn &s\n\t},\n}\n\n// CompressPayload takes a byte slice and compresses it according to the options passed\nfunc CompressPayload(in []byte, opts CompressionOpts) ([]byte, error) {\n\tswitch opts.Compressor {\n\tcase wiremessage.CompressorNoOp:\n\t\treturn in, nil\n\tcase wiremessage.CompressorSnappy:\n\t\treturn snappy.Encode(nil, in), nil\n\tcase wiremessage.CompressorZLib:\n\t\tencoder, err := getZlibEncoder(opts.ZlibLevel)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\treturn encoder.Encode(nil, in)\n\tcase wiremessage.CompressorZstd:\n\t\tencoder, err := getZstdEncoder(zstd.EncoderLevelFromZstd(opts.ZstdLevel))\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tptr := zstdBufPool.Get().(*[]byte)\n\t\tb := encoder.EncodeAll(in, *ptr)\n\t\tdst := make([]byte, len(b))\n\t\tcopy(dst, b)\n\t\t*ptr = b[:0]\n\t\tzstdBufPool.Put(ptr)\n\t\treturn dst, nil\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"unknown compressor ID %v\", opts.Compressor)\n\t}\n}\n\nvar zstdReaderPool = sync.Pool{\n\tNew: func() any {\n\t\tr, _ := zstd.NewReader(nil)\n\t\treturn r\n\t},\n}\n\n// DecompressPayload takes a byte slice that has been compressed and undoes it according to the options passed\nfunc DecompressPayload(in []byte, opts CompressionOpts) ([]byte, error) {\n\tswitch opts.Compressor {\n\tcase wiremessage.CompressorNoOp:\n\t\treturn in, nil\n\tcase wiremessage.CompressorSnappy:\n\t\tl, err := snappy.DecodedLen(in)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"decoding compressed length %w\", err)\n\t\t} else if int32(l) != opts.UncompressedSize {\n\t\t\treturn nil, fmt.Errorf(\"unexpected decompression size, expected %v but got %v\", opts.UncompressedSize, l)\n\t\t}\n\t\tout := make([]byte, opts.UncompressedSize)\n\t\treturn snappy.Decode(out, in)\n\tcase wiremessage.CompressorZLib:\n\t\tr, err := zlib.NewReader(bytes.NewReader(in))\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tout := make([]byte, opts.UncompressedSize)\n\t\tif _, err := io.ReadFull(r, out); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif err := r.Close(); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\treturn out, nil\n\tcase wiremessage.CompressorZstd:\n\t\tbuf := make([]byte, 0, opts.UncompressedSize)\n\t\t// Using a pool here is about ~20% faster\n\t\t// than using a single global zstd.Reader\n\t\tr := zstdReaderPool.Get().(*zstd.Decoder)\n\t\tout, err := r.DecodeAll(in, buf)\n\t\tzstdReaderPool.Put(r)\n\t\treturn out, err\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"unknown compressor ID %v\", opts.Compressor)\n\t}\n}\n"
  },
  {
    "path": "x/mongo/driver/compression_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage driver\n\nimport (\n\t\"bytes\"\n\t\"compress/zlib\"\n\t\"os\"\n\t\"testing\"\n\n\t\"github.com/klauspost/compress/snappy\"\n\t\"github.com/klauspost/compress/zstd\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/wiremessage\"\n)\n\nfunc TestCompression(t *testing.T) {\n\tcompressors := []wiremessage.CompressorID{\n\t\twiremessage.CompressorNoOp,\n\t\twiremessage.CompressorSnappy,\n\t\twiremessage.CompressorZLib,\n\t\twiremessage.CompressorZstd,\n\t}\n\n\tfor _, compressor := range compressors {\n\t\tt.Run(compressor.String(), func(t *testing.T) {\n\t\t\tpayload := []byte(\"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt\")\n\t\t\topts := CompressionOpts{\n\t\t\t\tCompressor:       compressor,\n\t\t\t\tZlibLevel:        wiremessage.DefaultZlibLevel,\n\t\t\t\tZstdLevel:        wiremessage.DefaultZstdLevel,\n\t\t\t\tUncompressedSize: int32(len(payload)),\n\t\t\t}\n\t\t\tcompressed, err := CompressPayload(payload, opts)\n\t\t\tassert.NoError(t, err)\n\t\t\tassert.NotEqual(t, 0, len(compressed))\n\t\t\tdecompressed, err := DecompressPayload(compressed, opts)\n\t\t\tassert.NoError(t, err)\n\t\t\tassert.Equal(t, payload, decompressed)\n\t\t})\n\t}\n}\n\nfunc TestCompressionLevels(t *testing.T) {\n\tin := []byte(\"abc\")\n\twr := new(bytes.Buffer)\n\n\tt.Run(\"ZLib\", func(t *testing.T) {\n\t\topts := CompressionOpts{\n\t\t\tCompressor: wiremessage.CompressorZLib,\n\t\t}\n\t\tfor lvl := zlib.HuffmanOnly - 2; lvl < zlib.BestCompression+2; lvl++ {\n\t\t\topts.ZlibLevel = lvl\n\t\t\t_, err1 := CompressPayload(in, opts)\n\t\t\t_, err2 := zlib.NewWriterLevel(wr, lvl)\n\t\t\tif err2 != nil {\n\t\t\t\tassert.Error(t, err1, \"expected an error for ZLib level %d\", lvl)\n\t\t\t} else {\n\t\t\t\tassert.NoError(t, err1, \"unexpected error for ZLib level %d\", lvl)\n\t\t\t}\n\t\t}\n\t})\n\n\tt.Run(\"Zstd\", func(t *testing.T) {\n\t\topts := CompressionOpts{\n\t\t\tCompressor: wiremessage.CompressorZstd,\n\t\t}\n\t\tfor lvl := zstd.SpeedFastest - 2; lvl < zstd.SpeedBestCompression+2; lvl++ {\n\t\t\topts.ZstdLevel = int(lvl)\n\t\t\t_, err1 := CompressPayload(in, opts)\n\t\t\t_, err2 := zstd.NewWriter(wr, zstd.WithEncoderLevel(zstd.EncoderLevelFromZstd(opts.ZstdLevel)))\n\t\t\tif err2 != nil {\n\t\t\t\tassert.Error(t, err1, \"expected an error for Zstd level %d\", lvl)\n\t\t\t} else {\n\t\t\t\tassert.NoError(t, err1, \"unexpected error for Zstd level %d\", lvl)\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc TestDecompressFailures(t *testing.T) {\n\tt.Parallel()\n\n\tt.Run(\"snappy decompress huge size\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\topts := CompressionOpts{\n\t\t\tCompressor:       wiremessage.CompressorSnappy,\n\t\t\tUncompressedSize: 100, // reasonable size\n\t\t}\n\t\t// Compressed data is twice as large as declared above.\n\t\t// In test we use actual compression so that the decompress action would pass without fix (thus failing test).\n\t\t// When decompression starts it allocates a buffer of the defined size, regardless of a valid compressed body following.\n\t\tcompressedData, err := CompressPayload(make([]byte, opts.UncompressedSize*2), opts)\n\t\tassert.NoError(t, err, \"premature error making compressed example\")\n\n\t\t_, err = DecompressPayload(compressedData, opts)\n\t\tassert.Error(t, err)\n\t})\n}\n\nvar (\n\tcompressionPayload      []byte\n\tcompressedSnappyPayload []byte\n\tcompressedZLibPayload   []byte\n\tcompressedZstdPayload   []byte\n)\n\nfunc initCompressionPayload(b *testing.B) {\n\tif compressionPayload != nil {\n\t\treturn\n\t}\n\tdata, err := os.ReadFile(\"testdata/compression.go\")\n\tif err != nil {\n\t\tb.Fatal(err)\n\t}\n\tfor i := 1; i < 10; i++ {\n\t\tdata = append(data, data...)\n\t}\n\tcompressionPayload = data\n\n\tcompressedSnappyPayload = snappy.Encode(compressedSnappyPayload[:0], data)\n\n\t{\n\t\tvar buf bytes.Buffer\n\t\tenc, err := zstd.NewWriter(&buf, zstd.WithEncoderLevel(zstd.SpeedDefault))\n\t\tif err != nil {\n\t\t\tb.Fatal(err)\n\t\t}\n\t\tcompressedZstdPayload = enc.EncodeAll(data, nil)\n\t}\n\n\t{\n\t\tvar buf bytes.Buffer\n\t\tenc := zlib.NewWriter(&buf)\n\t\tif _, err := enc.Write(data); err != nil {\n\t\t\tb.Fatal(err)\n\t\t}\n\t\tif err := enc.Close(); err != nil {\n\t\t\tb.Fatal(err)\n\t\t}\n\t\tif err := enc.Close(); err != nil {\n\t\t\tb.Fatal(err)\n\t\t}\n\t\tcompressedZLibPayload = append(compressedZLibPayload[:0], buf.Bytes()...)\n\t}\n\n\tb.ResetTimer()\n}\n\nfunc BenchmarkCompressPayload(b *testing.B) {\n\tinitCompressionPayload(b)\n\n\tcompressors := []wiremessage.CompressorID{\n\t\twiremessage.CompressorSnappy,\n\t\twiremessage.CompressorZLib,\n\t\twiremessage.CompressorZstd,\n\t}\n\n\tfor _, compressor := range compressors {\n\t\tb.Run(compressor.String(), func(b *testing.B) {\n\t\t\topts := CompressionOpts{\n\t\t\t\tCompressor: compressor,\n\t\t\t\tZlibLevel:  wiremessage.DefaultZlibLevel,\n\t\t\t\tZstdLevel:  wiremessage.DefaultZstdLevel,\n\t\t\t}\n\t\t\tpayload := compressionPayload\n\t\t\tb.SetBytes(int64(len(payload)))\n\t\t\tb.ReportAllocs()\n\t\t\tb.RunParallel(func(pb *testing.PB) {\n\t\t\t\tfor pb.Next() {\n\t\t\t\t\t_, err := CompressPayload(payload, opts)\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\tb.Error(err)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t})\n\t\t})\n\t}\n}\n\nfunc BenchmarkDecompressPayload(b *testing.B) {\n\tinitCompressionPayload(b)\n\n\tbenchmarks := []struct {\n\t\tcompressor wiremessage.CompressorID\n\t\tpayload    []byte\n\t}{\n\t\t{wiremessage.CompressorSnappy, compressedSnappyPayload},\n\t\t{wiremessage.CompressorZLib, compressedZLibPayload},\n\t\t{wiremessage.CompressorZstd, compressedZstdPayload},\n\t}\n\n\tfor _, bench := range benchmarks {\n\t\tb.Run(bench.compressor.String(), func(b *testing.B) {\n\t\t\topts := CompressionOpts{\n\t\t\t\tCompressor:       bench.compressor,\n\t\t\t\tZlibLevel:        wiremessage.DefaultZlibLevel,\n\t\t\t\tZstdLevel:        wiremessage.DefaultZstdLevel,\n\t\t\t\tUncompressedSize: int32(len(compressionPayload)),\n\t\t\t}\n\t\t\tpayload := bench.payload\n\t\t\tb.SetBytes(int64(len(compressionPayload)))\n\t\t\tb.ReportAllocs()\n\t\t\tb.RunParallel(func(pb *testing.PB) {\n\t\t\t\tfor pb.Next() {\n\t\t\t\t\t_, err := DecompressPayload(payload, opts)\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\tb.Fatal(err)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t})\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "x/mongo/driver/connstring/connstring.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\n// Package connstring is intended for internal use only. It is made available to\n// facilitate use cases that require access to internal MongoDB driver\n// functionality and state. The API of this package is not stable and there is\n// no backward compatibility guarantee.\n//\n// WARNING: THIS PACKAGE IS EXPERIMENTAL AND MAY BE MODIFIED OR REMOVED WITHOUT\n// NOTICE! USE WITH EXTREME CAUTION!\npackage connstring\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"net\"\n\t\"net/url\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/randutil\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/auth\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/dns\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/wiremessage\"\n)\n\nconst (\n\t// ServerMonitoringModeAuto indicates that the client will behave like \"poll\"\n\t// mode when running on a FaaS (Function as a Service) platform, or like\n\t// \"stream\" mode otherwise. The client detects its execution environment by\n\t// following the rules for generating the \"client.env\" handshake metadata field\n\t// as specified in the MongoDB Handshake specification. This is the default\n\t// mode.\n\tServerMonitoringModeAuto = \"auto\"\n\n\t// ServerMonitoringModePoll indicates that the client will periodically check\n\t// the server using a hello or legacy hello command and then sleep for\n\t// heartbeatFrequencyMS milliseconds before running another check.\n\tServerMonitoringModePoll = \"poll\"\n\n\t// ServerMonitoringModeStream indicates that the client will use a streaming\n\t// protocol when the server supports it. The streaming protocol optimally\n\t// reduces the time it takes for a client to discover server state changes.\n\tServerMonitoringModeStream = \"stream\"\n)\n\nvar (\n\t// ErrLoadBalancedWithMultipleHosts is returned when loadBalanced=true is\n\t// specified in a URI with multiple hosts.\n\tErrLoadBalancedWithMultipleHosts = errors.New(\n\t\t\"loadBalanced cannot be set to true if multiple hosts are specified\")\n\n\t// ErrLoadBalancedWithReplicaSet is returned when loadBalanced=true is\n\t// specified in a URI with the replicaSet option.\n\tErrLoadBalancedWithReplicaSet = errors.New(\n\t\t\"loadBalanced cannot be set to true if a replica set name is specified\")\n\n\t// ErrLoadBalancedWithDirectConnection is returned when loadBalanced=true is\n\t// specified in a URI with the directConnection option.\n\tErrLoadBalancedWithDirectConnection = errors.New(\n\t\t\"loadBalanced cannot be set to true if the direct connection option is specified\")\n\n\t// ErrSRVMaxHostsWithReplicaSet is returned when srvMaxHosts > 0 is\n\t// specified in a URI with the replicaSet option.\n\tErrSRVMaxHostsWithReplicaSet = errors.New(\n\t\t\"srvMaxHosts cannot be a positive value if a replica set name is specified\")\n\n\t// ErrSRVMaxHostsWithLoadBalanced is returned when srvMaxHosts > 0 is\n\t// specified in a URI with loadBalanced=true.\n\tErrSRVMaxHostsWithLoadBalanced = errors.New(\n\t\t\"srvMaxHosts cannot be a positive value if loadBalanced is set to true\")\n)\n\n// random is a package-global pseudo-random number generator.\nvar random = randutil.NewLockedRand()\n\n// ParseAndValidate parses the provided URI into a ConnString object.\n// It check that all values are valid.\nfunc ParseAndValidate(s string) (*ConnString, error) {\n\tconnStr, err := Parse(s)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\terr = connStr.Validate()\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"error validating uri: %w\", err)\n\t}\n\treturn connStr, nil\n}\n\n// Parse parses the provided URI into a ConnString object\n// but does not check that all values are valid. Use `ConnString.Validate()`\n// to run the validation checks separately.\nfunc Parse(s string) (*ConnString, error) {\n\tp := parser{dnsResolver: dns.DefaultResolver}\n\tconnStr, err := p.parse(s)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"error parsing uri: %w\", err)\n\t}\n\treturn connStr, err\n}\n\n// ConnString represents a connection string to mongodb.\ntype ConnString struct {\n\tOriginal                           string\n\tAppName                            string\n\tAuthMechanism                      string\n\tAuthMechanismProperties            map[string]string\n\tAuthMechanismPropertiesSet         bool\n\tAuthSource                         string\n\tAuthSourceSet                      bool\n\tCompressors                        []string\n\tConnect                            ConnectMode\n\tConnectSet                         bool\n\tDirectConnection                   bool\n\tDirectConnectionSet                bool\n\tConnectTimeout                     time.Duration\n\tConnectTimeoutSet                  bool\n\tDatabase                           string\n\tHeartbeatInterval                  time.Duration\n\tHeartbeatIntervalSet               bool\n\tHosts                              []string\n\tJ                                  bool\n\tJSet                               bool\n\tLoadBalanced                       bool\n\tLoadBalancedSet                    bool\n\tLocalThreshold                     time.Duration\n\tLocalThresholdSet                  bool\n\tMaxConnIdleTime                    time.Duration\n\tMaxConnIdleTimeSet                 bool\n\tMaxPoolSize                        uint64\n\tMaxPoolSizeSet                     bool\n\tMinPoolSize                        uint64\n\tMinPoolSizeSet                     bool\n\tMaxConnecting                      uint64\n\tMaxConnectingSet                   bool\n\tPassword                           string\n\tPasswordSet                        bool\n\tRawHosts                           []string\n\tReadConcernLevel                   string\n\tReadPreference                     string\n\tReadPreferenceTagSets              []map[string]string\n\tRetryWrites                        bool\n\tRetryWritesSet                     bool\n\tRetryReads                         bool\n\tRetryReadsSet                      bool\n\tMaxStaleness                       time.Duration\n\tMaxStalenessSet                    bool\n\tReplicaSet                         string\n\tScheme                             string\n\tServerMonitoringMode               string\n\tServerSelectionTimeout             time.Duration\n\tServerSelectionTimeoutSet          bool\n\tSocketTimeout                      time.Duration\n\tSocketTimeoutSet                   bool\n\tSRVMaxHosts                        int\n\tSRVServiceName                     string\n\tSSL                                bool\n\tSSLSet                             bool\n\tSSLClientCertificateKeyFile        string\n\tSSLClientCertificateKeyFileSet     bool\n\tSSLClientCertificateKeyPassword    func() string\n\tSSLClientCertificateKeyPasswordSet bool\n\tSSLCertificateFile                 string\n\tSSLCertificateFileSet              bool\n\tSSLPrivateKeyFile                  string\n\tSSLPrivateKeyFileSet               bool\n\tSSLInsecure                        bool\n\tSSLInsecureSet                     bool\n\tSSLCaFile                          string\n\tSSLCaFileSet                       bool\n\tSSLDisableOCSPEndpointCheck        bool\n\tSSLDisableOCSPEndpointCheckSet     bool\n\tTimeout                            time.Duration\n\tTimeoutSet                         bool\n\tWString                            string\n\tWNumber                            int\n\tWNumberSet                         bool\n\tUsername                           string\n\tUsernameSet                        bool\n\tZlibLevel                          int\n\tZlibLevelSet                       bool\n\tZstdLevel                          int\n\tZstdLevelSet                       bool\n\n\tOptions        map[string][]string\n\tUnknownOptions map[string][]string\n}\n\nfunc (u *ConnString) String() string {\n\treturn u.Original\n}\n\n// HasAuthParameters returns true if this ConnString has any authentication parameters set and therefore represents\n// a request for authentication.\nfunc (u *ConnString) HasAuthParameters() bool {\n\t// Check all auth parameters except for AuthSource because an auth source without other credentials is semantically\n\t// valid and must not be interpreted as a request for authentication.\n\treturn u.AuthMechanism != \"\" || u.AuthMechanismProperties != nil || u.UsernameSet || u.PasswordSet\n}\n\n// Validate checks that the Auth and SSL parameters are valid values.\nfunc (u *ConnString) Validate() error {\n\tvar err error\n\n\tif err = u.validateAuth(); err != nil {\n\t\treturn err\n\t}\n\n\tif err = u.validateSSL(); err != nil {\n\t\treturn err\n\t}\n\n\t// Check for invalid write concern (i.e. w=0 and j=true)\n\tif u.WNumberSet && u.WNumber == 0 && u.JSet && u.J {\n\t\treturn errors.New(\"a write concern cannot have both w=0 and j=true\")\n\t}\n\n\t// Check for invalid use of direct connections.\n\tif (u.ConnectSet && u.Connect == SingleConnect) ||\n\t\t(u.DirectConnectionSet && u.DirectConnection) {\n\t\tif len(u.Hosts) > 1 {\n\t\t\treturn errors.New(\"a direct connection cannot be made if multiple hosts are specified\")\n\t\t}\n\t\tif u.Scheme == SchemeMongoDBSRV {\n\t\t\treturn errors.New(\"a direct connection cannot be made if an SRV URI is used\")\n\t\t}\n\t\tif u.LoadBalancedSet && u.LoadBalanced {\n\t\t\treturn ErrLoadBalancedWithDirectConnection\n\t\t}\n\t}\n\n\t// Validation for load-balanced mode.\n\tif u.LoadBalancedSet && u.LoadBalanced {\n\t\tif len(u.Hosts) > 1 {\n\t\t\treturn ErrLoadBalancedWithMultipleHosts\n\t\t}\n\t\tif u.ReplicaSet != \"\" {\n\t\t\treturn ErrLoadBalancedWithReplicaSet\n\t\t}\n\t}\n\n\t// Check for invalid use of SRVMaxHosts.\n\tif u.SRVMaxHosts > 0 {\n\t\tif u.ReplicaSet != \"\" {\n\t\t\treturn ErrSRVMaxHostsWithReplicaSet\n\t\t}\n\t\tif u.LoadBalanced {\n\t\t\treturn ErrSRVMaxHostsWithLoadBalanced\n\t\t}\n\t}\n\n\t// Check for OIDC auth mechanism properties that cannot be set in the ConnString.\n\tif u.AuthMechanism == auth.MongoDBOIDC {\n\t\tif _, ok := u.AuthMechanismProperties[auth.AllowedHostsProp]; ok {\n\t\t\treturn fmt.Errorf(\n\t\t\t\t\"ALLOWED_HOSTS cannot be specified in the URI connection string for the %q auth mechanism, it must be specified through the ClientOptions directly\",\n\t\t\t\tauth.MongoDBOIDC,\n\t\t\t)\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc (u *ConnString) setDefaultAuthParams(dbName string) error {\n\t// We do this check here rather than in validateAuth because this function is called as part of parsing and sets\n\t// the value of AuthSource if authentication is enabled.\n\tif u.AuthSourceSet && u.AuthSource == \"\" {\n\t\treturn errors.New(\"authSource must be non-empty when supplied in a URI\")\n\t}\n\n\tswitch strings.ToLower(u.AuthMechanism) {\n\tcase \"plain\":\n\t\tif u.AuthSource == \"\" {\n\t\t\tu.AuthSource = dbName\n\t\t\tif u.AuthSource == \"\" {\n\t\t\t\tu.AuthSource = \"$external\"\n\t\t\t}\n\t\t}\n\tcase \"gssapi\":\n\t\tif u.AuthMechanismProperties == nil {\n\t\t\tu.AuthMechanismProperties = map[string]string{\n\t\t\t\t\"SERVICE_NAME\": \"mongodb\",\n\t\t\t}\n\t\t} else if v, ok := u.AuthMechanismProperties[\"SERVICE_NAME\"]; !ok || v == \"\" {\n\t\t\tu.AuthMechanismProperties[\"SERVICE_NAME\"] = \"mongodb\"\n\t\t}\n\t\tfallthrough\n\tcase \"mongodb-aws\", \"mongodb-x509\", \"mongodb-oidc\":\n\t\tif u.AuthSource == \"\" {\n\t\t\tu.AuthSource = \"$external\"\n\t\t} else if u.AuthSource != \"$external\" {\n\t\t\treturn fmt.Errorf(\"auth source must be $external\")\n\t\t}\n\tcase \"scram-sha-1\":\n\t\tfallthrough\n\tcase \"scram-sha-256\":\n\t\tif u.AuthSource == \"\" {\n\t\t\tu.AuthSource = dbName\n\t\t\tif u.AuthSource == \"\" {\n\t\t\t\tu.AuthSource = \"admin\"\n\t\t\t}\n\t\t}\n\tcase \"\":\n\t\t// Only set auth source if there is a request for authentication via non-empty credentials.\n\t\tif u.AuthSource == \"\" && (u.AuthMechanismProperties != nil || u.Username != \"\" || u.PasswordSet) {\n\t\t\tu.AuthSource = dbName\n\t\t\tif u.AuthSource == \"\" {\n\t\t\t\tu.AuthSource = \"admin\"\n\t\t\t}\n\t\t}\n\tdefault:\n\t\treturn fmt.Errorf(\"invalid auth mechanism\")\n\t}\n\treturn nil\n}\n\nfunc (u *ConnString) addOptions(connectionArgPairs []string) error {\n\tvar tlsssl *bool // used to determine if tls and ssl options are both specified and set differently.\n\tfor _, pair := range connectionArgPairs {\n\t\tkv := strings.SplitN(pair, \"=\", 2)\n\t\tif len(kv) != 2 || kv[0] == \"\" {\n\t\t\treturn fmt.Errorf(\"invalid option\")\n\t\t}\n\n\t\tkey, err := url.QueryUnescape(kv[0])\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"invalid option key %q: %w\", kv[0], err)\n\t\t}\n\n\t\tvalue, err := url.QueryUnescape(kv[1])\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"invalid option value %q: %w\", kv[1], err)\n\t\t}\n\n\t\tlowerKey := strings.ToLower(key)\n\t\tswitch lowerKey {\n\t\tcase \"appname\":\n\t\t\tu.AppName = value\n\t\tcase \"authmechanism\":\n\t\t\tu.AuthMechanism = value\n\t\tcase \"authmechanismproperties\":\n\t\t\tu.AuthMechanismProperties = make(map[string]string)\n\t\t\tpairs := strings.Split(value, \",\")\n\t\t\tfor _, pair := range pairs {\n\t\t\t\tkv := strings.SplitN(pair, \":\", 2)\n\t\t\t\tif len(kv) != 2 || kv[0] == \"\" {\n\t\t\t\t\treturn fmt.Errorf(\"invalid authMechanism property\")\n\t\t\t\t}\n\t\t\t\tu.AuthMechanismProperties[kv[0]] = kv[1]\n\t\t\t}\n\t\t\tu.AuthMechanismPropertiesSet = true\n\t\tcase \"authsource\":\n\t\t\tu.AuthSource = value\n\t\t\tu.AuthSourceSet = true\n\t\tcase \"compressors\":\n\t\t\tcompressors := strings.Split(value, \",\")\n\t\t\tif len(compressors) < 1 {\n\t\t\t\treturn fmt.Errorf(\"must have at least 1 compressor\")\n\t\t\t}\n\t\t\tu.Compressors = compressors\n\t\tcase \"connect\":\n\t\t\tswitch strings.ToLower(value) {\n\t\t\tcase \"automatic\":\n\t\t\tcase \"direct\":\n\t\t\t\tu.Connect = SingleConnect\n\t\t\tdefault:\n\t\t\t\treturn fmt.Errorf(\"invalid 'connect' value: %q\", value)\n\t\t\t}\n\t\t\tif u.DirectConnectionSet {\n\t\t\t\texpectedValue := u.Connect == SingleConnect // directConnection should be true if connect=direct\n\t\t\t\tif u.DirectConnection != expectedValue {\n\t\t\t\t\treturn fmt.Errorf(\"options connect=%q and directConnection=%v conflict\", value, u.DirectConnection)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tu.ConnectSet = true\n\t\tcase \"directconnection\":\n\t\t\tswitch strings.ToLower(value) {\n\t\t\tcase \"true\":\n\t\t\t\tu.DirectConnection = true\n\t\t\tcase \"false\":\n\t\t\tdefault:\n\t\t\t\treturn fmt.Errorf(\"invalid 'directConnection' value: %q\", value)\n\t\t\t}\n\n\t\t\tif u.ConnectSet {\n\t\t\t\texpectedValue := AutoConnect\n\t\t\t\tif u.DirectConnection {\n\t\t\t\t\texpectedValue = SingleConnect\n\t\t\t\t}\n\n\t\t\t\tif u.Connect != expectedValue {\n\t\t\t\t\treturn fmt.Errorf(\"options connect=%q and directConnection=%q conflict\", u.Connect, value)\n\t\t\t\t}\n\t\t\t}\n\t\t\tu.DirectConnectionSet = true\n\t\tcase \"connecttimeoutms\":\n\t\t\tn, err := strconv.Atoi(value)\n\t\t\tif err != nil || n < 0 {\n\t\t\t\treturn fmt.Errorf(\"invalid value for %q: %q\", key, value)\n\t\t\t}\n\t\t\tu.ConnectTimeout = time.Duration(n) * time.Millisecond\n\t\t\tu.ConnectTimeoutSet = true\n\t\tcase \"heartbeatintervalms\", \"heartbeatfrequencyms\":\n\t\t\tn, err := strconv.Atoi(value)\n\t\t\tif err != nil || n < 0 {\n\t\t\t\treturn fmt.Errorf(\"invalid value for %q: %q\", key, value)\n\t\t\t}\n\t\t\tu.HeartbeatInterval = time.Duration(n) * time.Millisecond\n\t\t\tu.HeartbeatIntervalSet = true\n\t\tcase \"journal\":\n\t\t\tswitch value {\n\t\t\tcase \"true\":\n\t\t\t\tu.J = true\n\t\t\tcase \"false\":\n\t\t\t\tu.J = false\n\t\t\tdefault:\n\t\t\t\treturn fmt.Errorf(\"invalid value for %q: %q\", key, value)\n\t\t\t}\n\n\t\t\tu.JSet = true\n\t\tcase \"loadbalanced\":\n\t\t\tswitch value {\n\t\t\tcase \"true\":\n\t\t\t\tu.LoadBalanced = true\n\t\t\tcase \"false\":\n\t\t\t\tu.LoadBalanced = false\n\t\t\tdefault:\n\t\t\t\treturn fmt.Errorf(\"invalid value for %q: %q\", key, value)\n\t\t\t}\n\n\t\t\tu.LoadBalancedSet = true\n\t\tcase \"localthresholdms\":\n\t\t\tn, err := strconv.Atoi(value)\n\t\t\tif err != nil || n < 0 {\n\t\t\t\treturn fmt.Errorf(\"invalid value for %q: %q\", key, value)\n\t\t\t}\n\t\t\tu.LocalThreshold = time.Duration(n) * time.Millisecond\n\t\t\tu.LocalThresholdSet = true\n\t\tcase \"maxidletimems\":\n\t\t\tn, err := strconv.Atoi(value)\n\t\t\tif err != nil || n < 0 {\n\t\t\t\treturn fmt.Errorf(\"invalid value for %q: %q\", key, value)\n\t\t\t}\n\t\t\tu.MaxConnIdleTime = time.Duration(n) * time.Millisecond\n\t\t\tu.MaxConnIdleTimeSet = true\n\t\tcase \"maxpoolsize\":\n\t\t\tn, err := strconv.Atoi(value)\n\t\t\tif err != nil || n < 0 {\n\t\t\t\treturn fmt.Errorf(\"invalid value for %q: %q\", key, value)\n\t\t\t}\n\t\t\tu.MaxPoolSize = uint64(n)\n\t\t\tu.MaxPoolSizeSet = true\n\t\tcase \"minpoolsize\":\n\t\t\tn, err := strconv.Atoi(value)\n\t\t\tif err != nil || n < 0 {\n\t\t\t\treturn fmt.Errorf(\"invalid value for %q: %q\", key, value)\n\t\t\t}\n\t\t\tu.MinPoolSize = uint64(n)\n\t\t\tu.MinPoolSizeSet = true\n\t\tcase \"maxconnecting\":\n\t\t\tn, err := strconv.Atoi(value)\n\t\t\tif err != nil || n < 0 {\n\t\t\t\treturn fmt.Errorf(\"invalid value for %q: %q\", key, value)\n\t\t\t}\n\t\t\tu.MaxConnecting = uint64(n)\n\t\t\tu.MaxConnectingSet = true\n\t\tcase \"readconcernlevel\":\n\t\t\tu.ReadConcernLevel = value\n\t\tcase \"readpreference\":\n\t\t\tu.ReadPreference = value\n\t\tcase \"readpreferencetags\":\n\t\t\tif value == \"\" {\n\t\t\t\t// If \"readPreferenceTags=\" is supplied, append an empty map to tag sets to\n\t\t\t\t// represent a wild-card.\n\t\t\t\tu.ReadPreferenceTagSets = append(u.ReadPreferenceTagSets, map[string]string{})\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\ttags := make(map[string]string)\n\t\t\titems := strings.Split(value, \",\")\n\t\t\tfor _, item := range items {\n\t\t\t\tparts := strings.Split(item, \":\")\n\t\t\t\tif len(parts) != 2 {\n\t\t\t\t\treturn fmt.Errorf(\"invalid value for %q: %q\", key, value)\n\t\t\t\t}\n\t\t\t\ttags[parts[0]] = parts[1]\n\t\t\t}\n\t\t\tu.ReadPreferenceTagSets = append(u.ReadPreferenceTagSets, tags)\n\t\tcase \"maxstaleness\", \"maxstalenessseconds\":\n\t\t\tn, err := strconv.Atoi(value)\n\t\t\tif err != nil || n < 0 {\n\t\t\t\treturn fmt.Errorf(\"invalid value for %q: %q\", key, value)\n\t\t\t}\n\t\t\tu.MaxStaleness = time.Duration(n) * time.Second\n\t\t\tu.MaxStalenessSet = true\n\t\tcase \"replicaset\":\n\t\t\tu.ReplicaSet = value\n\t\tcase \"retrywrites\":\n\t\t\tswitch value {\n\t\t\tcase \"true\":\n\t\t\t\tu.RetryWrites = true\n\t\t\tcase \"false\":\n\t\t\t\tu.RetryWrites = false\n\t\t\tdefault:\n\t\t\t\treturn fmt.Errorf(\"invalid value for %q: %q\", key, value)\n\t\t\t}\n\n\t\t\tu.RetryWritesSet = true\n\t\tcase \"retryreads\":\n\t\t\tswitch value {\n\t\t\tcase \"true\":\n\t\t\t\tu.RetryReads = true\n\t\t\tcase \"false\":\n\t\t\t\tu.RetryReads = false\n\t\t\tdefault:\n\t\t\t\treturn fmt.Errorf(\"invalid value for %q: %q\", key, value)\n\t\t\t}\n\n\t\t\tu.RetryReadsSet = true\n\t\tcase \"servermonitoringmode\":\n\t\t\tif !IsValidServerMonitoringMode(value) {\n\t\t\t\treturn fmt.Errorf(\"invalid value for %q: %q\", key, value)\n\t\t\t}\n\n\t\t\tu.ServerMonitoringMode = value\n\t\tcase \"serverselectiontimeoutms\":\n\t\t\tn, err := strconv.Atoi(value)\n\t\t\tif err != nil || n < 0 {\n\t\t\t\treturn fmt.Errorf(\"invalid value for %q: %q\", key, value)\n\t\t\t}\n\t\t\tu.ServerSelectionTimeout = time.Duration(n) * time.Millisecond\n\t\t\tu.ServerSelectionTimeoutSet = true\n\t\tcase \"sockettimeoutms\":\n\t\t\tn, err := strconv.Atoi(value)\n\t\t\tif err != nil || n < 0 {\n\t\t\t\treturn fmt.Errorf(\"invalid value for %q: %q\", key, value)\n\t\t\t}\n\t\t\tu.SocketTimeout = time.Duration(n) * time.Millisecond\n\t\t\tu.SocketTimeoutSet = true\n\t\tcase \"srvmaxhosts\":\n\t\t\t// srvMaxHosts can only be set on URIs with the \"mongodb+srv\" scheme\n\t\t\tif u.Scheme != SchemeMongoDBSRV {\n\t\t\t\treturn fmt.Errorf(\"cannot specify srvMaxHosts on non-SRV URI\")\n\t\t\t}\n\n\t\t\tn, err := strconv.Atoi(value)\n\t\t\tif err != nil || n < 0 {\n\t\t\t\treturn fmt.Errorf(\"invalid value for %q: %q\", key, value)\n\t\t\t}\n\t\t\tu.SRVMaxHosts = n\n\t\tcase \"srvservicename\":\n\t\t\t// srvServiceName can only be set on URIs with the \"mongodb+srv\" scheme\n\t\t\tif u.Scheme != SchemeMongoDBSRV {\n\t\t\t\treturn fmt.Errorf(\"cannot specify srvServiceName on non-SRV URI\")\n\t\t\t}\n\n\t\t\t// srvServiceName must be between 1 and 62 characters according to\n\t\t\t// our specification. Empty service names are not valid, and the service\n\t\t\t// name (including prepended underscore) should not exceed the 63 character\n\t\t\t// limit for DNS query subdomains.\n\t\t\tif len(value) < 1 || len(value) > 62 {\n\t\t\t\treturn fmt.Errorf(\"srvServiceName value must be between 1 and 62 characters\")\n\t\t\t}\n\t\t\tu.SRVServiceName = value\n\t\tcase \"ssl\", \"tls\":\n\t\t\tswitch value {\n\t\t\tcase \"true\":\n\t\t\t\tu.SSL = true\n\t\t\tcase \"false\":\n\t\t\t\tu.SSL = false\n\t\t\tdefault:\n\t\t\t\treturn fmt.Errorf(\"invalid value for %q: %q\", key, value)\n\t\t\t}\n\t\t\tif tlsssl == nil {\n\t\t\t\ttlsssl = new(bool)\n\t\t\t\t*tlsssl = u.SSL\n\t\t\t} else if *tlsssl != u.SSL {\n\t\t\t\treturn errors.New(\"tls and ssl options, when both specified, must be equivalent\")\n\t\t\t}\n\n\t\t\tu.SSLSet = true\n\t\tcase \"sslclientcertificatekeyfile\", \"tlscertificatekeyfile\":\n\t\t\tu.SSL = true\n\t\t\tu.SSLSet = true\n\t\t\tu.SSLClientCertificateKeyFile = value\n\t\t\tu.SSLClientCertificateKeyFileSet = true\n\t\tcase \"sslclientcertificatekeypassword\", \"tlscertificatekeyfilepassword\":\n\t\t\tu.SSLClientCertificateKeyPassword = func() string { return value }\n\t\t\tu.SSLClientCertificateKeyPasswordSet = true\n\t\tcase \"tlscertificatefile\":\n\t\t\tu.SSL = true\n\t\t\tu.SSLSet = true\n\t\t\tu.SSLCertificateFile = value\n\t\t\tu.SSLCertificateFileSet = true\n\t\tcase \"tlsprivatekeyfile\":\n\t\t\tu.SSL = true\n\t\t\tu.SSLSet = true\n\t\t\tu.SSLPrivateKeyFile = value\n\t\t\tu.SSLPrivateKeyFileSet = true\n\t\tcase \"sslinsecure\", \"tlsinsecure\":\n\t\t\tswitch value {\n\t\t\tcase \"true\":\n\t\t\t\tu.SSLInsecure = true\n\t\t\tcase \"false\":\n\t\t\t\tu.SSLInsecure = false\n\t\t\tdefault:\n\t\t\t\treturn fmt.Errorf(\"invalid value for %q: %q\", key, value)\n\t\t\t}\n\n\t\t\tu.SSLInsecureSet = true\n\t\tcase \"sslcertificateauthorityfile\", \"tlscafile\":\n\t\t\tu.SSL = true\n\t\t\tu.SSLSet = true\n\t\t\tu.SSLCaFile = value\n\t\t\tu.SSLCaFileSet = true\n\t\tcase \"timeoutms\":\n\t\t\tn, err := strconv.Atoi(value)\n\t\t\tif err != nil || n < 0 {\n\t\t\t\treturn fmt.Errorf(\"invalid value for %q: %q\", key, value)\n\t\t\t}\n\t\t\tu.Timeout = time.Duration(n) * time.Millisecond\n\t\t\tu.TimeoutSet = true\n\t\tcase \"tlsdisableocspendpointcheck\":\n\t\t\tu.SSL = true\n\t\t\tu.SSLSet = true\n\n\t\t\tswitch value {\n\t\t\tcase \"true\":\n\t\t\t\tu.SSLDisableOCSPEndpointCheck = true\n\t\t\tcase \"false\":\n\t\t\t\tu.SSLDisableOCSPEndpointCheck = false\n\t\t\tdefault:\n\t\t\t\treturn fmt.Errorf(\"invalid value for %q: %q\", key, value)\n\t\t\t}\n\t\t\tu.SSLDisableOCSPEndpointCheckSet = true\n\t\tcase \"w\":\n\t\t\tif w, err := strconv.Atoi(value); err == nil {\n\t\t\t\tif w < 0 {\n\t\t\t\t\treturn fmt.Errorf(\"invalid value for %q: %q\", key, value)\n\t\t\t\t}\n\n\t\t\t\tu.WNumber = w\n\t\t\t\tu.WNumberSet = true\n\t\t\t\tu.WString = \"\"\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\tu.WString = value\n\t\t\tu.WNumberSet = false\n\t\tcase \"zlibcompressionlevel\":\n\t\t\tlevel, err := strconv.Atoi(value)\n\t\t\tif err != nil || (level < -1 || level > 9) {\n\t\t\t\treturn fmt.Errorf(\"invalid value for %q: %q\", key, value)\n\t\t\t}\n\n\t\t\tif level == -1 {\n\t\t\t\tlevel = wiremessage.DefaultZlibLevel\n\t\t\t}\n\t\t\tu.ZlibLevel = level\n\t\t\tu.ZlibLevelSet = true\n\t\tcase \"zstdcompressionlevel\":\n\t\t\tconst maxZstdLevel = 22 // https://github.com/facebook/zstd/blob/a880ca239b447968493dd2fed3850e766d6305cc/contrib/linux-kernel/lib/zstd/compress.c#L3291\n\t\t\tlevel, err := strconv.Atoi(value)\n\t\t\tif err != nil || (level < -1 || level > maxZstdLevel) {\n\t\t\t\treturn fmt.Errorf(\"invalid value for %q: %q\", key, value)\n\t\t\t}\n\n\t\t\tif level == -1 {\n\t\t\t\tlevel = wiremessage.DefaultZstdLevel\n\t\t\t}\n\t\t\tu.ZstdLevel = level\n\t\t\tu.ZstdLevelSet = true\n\t\tdefault:\n\t\t\tif u.UnknownOptions == nil {\n\t\t\t\tu.UnknownOptions = make(map[string][]string)\n\t\t\t}\n\t\t\tu.UnknownOptions[lowerKey] = append(u.UnknownOptions[lowerKey], value)\n\t\t}\n\n\t\tif u.Options == nil {\n\t\t\tu.Options = make(map[string][]string)\n\t\t}\n\t\tu.Options[lowerKey] = append(u.Options[lowerKey], value)\n\t}\n\treturn nil\n}\n\nfunc (u *ConnString) validateAuth() error {\n\tswitch strings.ToLower(u.AuthMechanism) {\n\tcase \"mongodb-x509\":\n\t\tif u.Password != \"\" {\n\t\t\treturn fmt.Errorf(\"password cannot be specified for MONGO-X509\")\n\t\t}\n\t\tif u.AuthMechanismProperties != nil {\n\t\t\treturn fmt.Errorf(\"MONGO-X509 cannot have mechanism properties\")\n\t\t}\n\tcase \"mongodb-aws\":\n\t\tif u.Username != \"\" && u.Password == \"\" {\n\t\t\treturn fmt.Errorf(\"username without password is invalid for MONGODB-AWS\")\n\t\t}\n\t\tif u.Username == \"\" && u.Password != \"\" {\n\t\t\treturn fmt.Errorf(\"password without username is invalid for MONGODB-AWS\")\n\t\t}\n\t\tvar token bool\n\t\tfor k := range u.AuthMechanismProperties {\n\t\t\tif k != \"AWS_SESSION_TOKEN\" {\n\t\t\t\treturn fmt.Errorf(\"invalid auth property for MONGODB-AWS\")\n\t\t\t}\n\t\t\ttoken = true\n\t\t}\n\t\tif token && u.Username == \"\" && u.Password == \"\" {\n\t\t\treturn fmt.Errorf(\"token without username and password is invalid for MONGODB-AWS\")\n\t\t}\n\tcase \"gssapi\":\n\t\tif u.Username == \"\" {\n\t\t\treturn fmt.Errorf(\"username required for GSSAPI\")\n\t\t}\n\t\tfor k := range u.AuthMechanismProperties {\n\t\t\tif k != \"SERVICE_NAME\" && k != \"CANONICALIZE_HOST_NAME\" && k != \"SERVICE_REALM\" && k != \"SERVICE_HOST\" {\n\t\t\t\treturn fmt.Errorf(\"invalid auth property for GSSAPI\")\n\t\t\t}\n\t\t}\n\tcase \"plain\":\n\t\tif u.Username == \"\" {\n\t\t\treturn fmt.Errorf(\"username required for PLAIN\")\n\t\t}\n\t\tif u.Password == \"\" {\n\t\t\treturn fmt.Errorf(\"password required for PLAIN\")\n\t\t}\n\t\tif u.AuthMechanismProperties != nil {\n\t\t\treturn fmt.Errorf(\"PLAIN cannot have mechanism properties\")\n\t\t}\n\tcase \"scram-sha-1\":\n\t\tif u.Username == \"\" {\n\t\t\treturn fmt.Errorf(\"username required for SCRAM-SHA-1\")\n\t\t}\n\t\tif u.Password == \"\" {\n\t\t\treturn fmt.Errorf(\"password required for SCRAM-SHA-1\")\n\t\t}\n\t\tif u.AuthMechanismProperties != nil {\n\t\t\treturn fmt.Errorf(\"SCRAM-SHA-1 cannot have mechanism properties\")\n\t\t}\n\tcase \"scram-sha-256\":\n\t\tif u.Username == \"\" {\n\t\t\treturn fmt.Errorf(\"username required for SCRAM-SHA-256\")\n\t\t}\n\t\tif u.Password == \"\" {\n\t\t\treturn fmt.Errorf(\"password required for SCRAM-SHA-256\")\n\t\t}\n\t\tif u.AuthMechanismProperties != nil {\n\t\t\treturn fmt.Errorf(\"SCRAM-SHA-256 cannot have mechanism properties\")\n\t\t}\n\tcase \"mongodb-oidc\":\n\t\tif u.Password != \"\" {\n\t\t\treturn fmt.Errorf(\"password cannot be specified for MONGODB-OIDC\")\n\t\t}\n\tcase \"\":\n\t\tif u.UsernameSet && u.Username == \"\" {\n\t\t\treturn fmt.Errorf(\"username required if URI contains user info\")\n\t\t}\n\tdefault:\n\t\treturn fmt.Errorf(\"invalid auth mechanism\")\n\t}\n\treturn nil\n}\n\nfunc (u *ConnString) validateSSL() error {\n\tif !u.SSL {\n\t\treturn nil\n\t}\n\n\tif u.SSLClientCertificateKeyFileSet {\n\t\tif u.SSLCertificateFileSet || u.SSLPrivateKeyFileSet {\n\t\t\treturn errors.New(\"the sslClientCertificateKeyFile/tlsCertificateKeyFile URI option cannot be provided \" +\n\t\t\t\t\"along with tlsCertificateFile or tlsPrivateKeyFile\")\n\t\t}\n\t\treturn nil\n\t}\n\tif u.SSLCertificateFileSet && !u.SSLPrivateKeyFileSet {\n\t\treturn errors.New(\"the tlsPrivateKeyFile URI option must be provided if the tlsCertificateFile option is specified\")\n\t}\n\tif u.SSLPrivateKeyFileSet && !u.SSLCertificateFileSet {\n\t\treturn errors.New(\"the tlsCertificateFile URI option must be provided if the tlsPrivateKeyFile option is specified\")\n\t}\n\n\tif u.SSLInsecureSet && u.SSLDisableOCSPEndpointCheckSet {\n\t\treturn errors.New(\"the sslInsecure/tlsInsecure URI option cannot be provided along with \" +\n\t\t\t\"tlsDisableOCSPEndpointCheck \")\n\t}\n\treturn nil\n}\n\nfunc sanitizeHost(host string) (string, error) {\n\tif host == \"\" {\n\t\treturn host, nil\n\t}\n\tunescaped, err := url.QueryUnescape(host)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"invalid host %q: %w\", host, err)\n\t}\n\n\t_, port, err := net.SplitHostPort(unescaped)\n\t// this is unfortunate that SplitHostPort actually requires\n\t// a port to exist.\n\tif err != nil {\n\t\tif addrError, ok := err.(*net.AddrError); !ok || addrError.Err != \"missing port in address\" {\n\t\t\treturn \"\", err\n\t\t}\n\t}\n\n\tif port != \"\" {\n\t\td, err := strconv.Atoi(port)\n\t\tif err != nil {\n\t\t\treturn \"\", fmt.Errorf(\"port must be an integer: %w\", err)\n\t\t}\n\t\tif d <= 0 || d >= 65536 {\n\t\t\treturn \"\", fmt.Errorf(\"port must be in the range [1, 65535]\")\n\t\t}\n\t}\n\treturn unescaped, nil\n}\n\n// ConnectMode informs the driver on how to connect\n// to the server.\ntype ConnectMode uint8\n\nvar _ fmt.Stringer = ConnectMode(0)\n\n// ConnectMode constants.\nconst (\n\tAutoConnect ConnectMode = iota\n\tSingleConnect\n)\n\n// String implements the fmt.Stringer interface.\nfunc (c ConnectMode) String() string {\n\tswitch c {\n\tcase AutoConnect:\n\t\treturn \"automatic\"\n\tcase SingleConnect:\n\t\treturn \"direct\"\n\tdefault:\n\t\treturn \"unknown\"\n\t}\n}\n\n// Scheme constants\nconst (\n\tSchemeMongoDB    = \"mongodb\"\n\tSchemeMongoDBSRV = \"mongodb+srv\"\n)\n\ntype parser struct {\n\tdnsResolver *dns.Resolver\n}\n\nfunc (p *parser) parse(original string) (*ConnString, error) {\n\tconnStr := &ConnString{}\n\tconnStr.Original = original\n\turi := original\n\n\tvar err error\n\tswitch {\n\tcase strings.HasPrefix(uri, SchemeMongoDBSRV+\"://\"):\n\t\tconnStr.Scheme = SchemeMongoDBSRV\n\t\t// remove the scheme\n\t\turi = uri[len(SchemeMongoDBSRV)+3:]\n\tcase strings.HasPrefix(uri, SchemeMongoDB+\"://\"):\n\t\tconnStr.Scheme = SchemeMongoDB\n\t\t// remove the scheme\n\t\turi = uri[len(SchemeMongoDB)+3:]\n\tdefault:\n\t\treturn nil, errors.New(`scheme must be \"mongodb\" or \"mongodb+srv\"`)\n\t}\n\n\tif idx := strings.Index(uri, \"@\"); idx != -1 {\n\t\tuserInfo := uri[:idx]\n\t\turi = uri[idx+1:]\n\n\t\tusername := userInfo\n\t\tvar password string\n\n\t\tif u, p, ok := strings.Cut(userInfo, \":\"); ok {\n\t\t\tusername = u\n\t\t\tpassword = p\n\t\t\tconnStr.PasswordSet = true\n\t\t}\n\n\t\t// Validate and process the username.\n\t\tif strings.Contains(username, \"/\") {\n\t\t\treturn nil, fmt.Errorf(\"unescaped slash in username\")\n\t\t}\n\t\tconnStr.Username, err = url.PathUnescape(username)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"invalid username: %w\", err)\n\t\t}\n\t\tconnStr.UsernameSet = true\n\n\t\t// Validate and process the password.\n\t\tif strings.Contains(password, \":\") {\n\t\t\treturn nil, fmt.Errorf(\"unescaped colon in password\")\n\t\t}\n\t\tif strings.Contains(password, \"/\") {\n\t\t\treturn nil, fmt.Errorf(\"unescaped slash in password\")\n\t\t}\n\t\tconnStr.Password, err = url.PathUnescape(password)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"invalid password: %w\", err)\n\t\t}\n\t}\n\n\t// fetch the hosts field\n\thosts := uri\n\tif idx := strings.IndexAny(uri, \"/?@\"); idx != -1 {\n\t\tif uri[idx] == '@' {\n\t\t\treturn nil, fmt.Errorf(\"unescaped @ sign in user info\")\n\t\t}\n\t\tif uri[idx] == '?' {\n\t\t\treturn nil, fmt.Errorf(\"must have a / before the query ?\")\n\t\t}\n\t\thosts = uri[:idx]\n\t}\n\n\tfor _, host := range strings.Split(hosts, \",\") {\n\t\thost, err = sanitizeHost(host)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"invalid host %q: %w\", host, err)\n\t\t}\n\t\tif host != \"\" {\n\t\t\tconnStr.RawHosts = append(connStr.RawHosts, host)\n\t\t}\n\t}\n\tconnStr.Hosts = connStr.RawHosts\n\turi = uri[len(hosts):]\n\textractedDatabase, err := extractDatabaseFromURI(uri)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\turi = extractedDatabase.uri\n\tconnStr.Database = extractedDatabase.db\n\n\t// grab connection arguments from URI\n\tconnectionArgsFromQueryString, err := extractQueryArgsFromURI(uri)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// grab connection arguments from TXT record and enable SSL if \"mongodb+srv://\"\n\tvar connectionArgsFromTXT []string\n\tif connStr.Scheme == SchemeMongoDBSRV && p.dnsResolver != nil {\n\t\tconnectionArgsFromTXT, err = p.dnsResolver.GetConnectionArgsFromTXT(hosts)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\t// SSL is enabled by default for SRV, but can be manually disabled with \"ssl=false\".\n\t\tconnStr.SSL = true\n\t\tconnStr.SSLSet = true\n\t}\n\n\t// add connection arguments from URI and TXT records to connstring\n\tconnectionArgPairs := make([]string, 0, len(connectionArgsFromTXT)+len(connectionArgsFromQueryString))\n\tconnectionArgPairs = append(connectionArgPairs, connectionArgsFromTXT...)\n\tconnectionArgPairs = append(connectionArgPairs, connectionArgsFromQueryString...)\n\n\terr = connStr.addOptions(connectionArgPairs)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// do SRV lookup if \"mongodb+srv://\"\n\tif connStr.Scheme == SchemeMongoDBSRV && p.dnsResolver != nil {\n\t\tparsedHosts, err := p.dnsResolver.ParseHosts(hosts, connStr.SRVServiceName, true)\n\t\tif err != nil {\n\t\t\treturn connStr, err\n\t\t}\n\n\t\t// If p.SRVMaxHosts is non-zero and is less than the number of hosts, randomly\n\t\t// select SRVMaxHosts hosts from parsedHosts.\n\t\tif connStr.SRVMaxHosts > 0 && connStr.SRVMaxHosts < len(parsedHosts) {\n\t\t\trandom.Shuffle(len(parsedHosts), func(i, j int) {\n\t\t\t\tparsedHosts[i], parsedHosts[j] = parsedHosts[j], parsedHosts[i]\n\t\t\t})\n\t\t\tparsedHosts = parsedHosts[:connStr.SRVMaxHosts]\n\t\t}\n\n\t\tvar hosts []string\n\t\tfor _, host := range parsedHosts {\n\t\t\thost, err = sanitizeHost(host)\n\t\t\tif err != nil {\n\t\t\t\treturn connStr, fmt.Errorf(\"invalid host %q: %w\", host, err)\n\t\t\t}\n\t\t\tif host != \"\" {\n\t\t\t\thosts = append(hosts, host)\n\t\t\t}\n\t\t}\n\t\tconnStr.Hosts = hosts\n\t}\n\tif len(connStr.Hosts) == 0 {\n\t\treturn nil, fmt.Errorf(\"must have at least 1 host\")\n\t}\n\n\terr = connStr.setDefaultAuthParams(extractedDatabase.db)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn connStr, nil\n}\n\n// IsValidServerMonitoringMode will return true if the given string matches a\n// valid server monitoring mode.\nfunc IsValidServerMonitoringMode(mode string) bool {\n\treturn mode == ServerMonitoringModeAuto ||\n\t\tmode == ServerMonitoringModeStream ||\n\t\tmode == ServerMonitoringModePoll\n}\n\nfunc extractQueryArgsFromURI(uri string) ([]string, error) {\n\tif len(uri) == 0 {\n\t\treturn nil, nil\n\t}\n\n\tif uri[0] != '?' {\n\t\treturn nil, errors.New(\"must have a ? separator between path and query\")\n\t}\n\n\turi = uri[1:]\n\tif len(uri) == 0 {\n\t\treturn nil, nil\n\t}\n\treturn strings.FieldsFunc(uri, func(r rune) bool { return r == ';' || r == '&' }), nil\n}\n\ntype extractedDatabase struct {\n\turi string\n\tdb  string\n}\n\n// extractDatabaseFromURI is a helper function to retrieve information about\n// the database from the passed in URI. It accepts as an argument the currently\n// parsed URI and returns the remainder of the uri, the database it found,\n// and any error it encounters while parsing.\nfunc extractDatabaseFromURI(uri string) (extractedDatabase, error) {\n\tif len(uri) == 0 {\n\t\treturn extractedDatabase{}, nil\n\t}\n\n\tif uri[0] != '/' {\n\t\treturn extractedDatabase{}, errors.New(\"must have a / separator between hosts and path\")\n\t}\n\n\turi = uri[1:]\n\tif len(uri) == 0 {\n\t\treturn extractedDatabase{}, nil\n\t}\n\n\tdatabase := uri\n\tif idx := strings.IndexRune(uri, '?'); idx != -1 {\n\t\tdatabase = uri[:idx]\n\t}\n\n\tescapedDatabase, err := url.QueryUnescape(database)\n\tif err != nil {\n\t\treturn extractedDatabase{}, fmt.Errorf(\"invalid database %q: %w\", database, err)\n\t}\n\n\turi = uri[len(database):]\n\n\treturn extractedDatabase{\n\t\turi: uri,\n\t\tdb:  escapedDatabase,\n\t}, nil\n}\n"
  },
  {
    "path": "x/mongo/driver/connstring/connstring_spec_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage connstring_test\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io/ioutil\"\n\t\"math\"\n\t\"path\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/require\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/spectest\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/connstring\"\n)\n\ntype host struct {\n\tType string\n\tHost string\n\tPort json.Number\n}\n\ntype auth struct {\n\tUsername string\n\tPassword *string\n\tDB       string\n}\n\ntype testCase struct {\n\tDescription string\n\tURI         string\n\tValid       bool\n\tWarning     bool\n\tHosts       []host\n\tAuth        *auth\n\tOptions     map[string]any\n}\n\ntype testContainer struct {\n\tTests []testCase\n}\n\nvar (\n\tconnstringTestsDir = spectest.Path(\"connection-string/tests\")\n\turioptionsTestDir  = spectest.Path(\"uri-options/tests\")\n)\n\nfunc (h *host) toString() string {\n\tswitch h.Type {\n\tcase \"unix\":\n\t\treturn h.Host\n\tcase \"ip_literal\":\n\t\tif len(h.Port) == 0 {\n\t\t\treturn \"[\" + h.Host + \"]\"\n\t\t}\n\t\treturn \"[\" + h.Host + \"]\" + \":\" + string(h.Port)\n\tcase \"ipv4\":\n\t\tfallthrough\n\tcase \"hostname\":\n\t\tif len(h.Port) == 0 {\n\t\t\treturn h.Host\n\t\t}\n\t\treturn h.Host + \":\" + string(h.Port)\n\t}\n\n\treturn \"\"\n}\n\nfunc hostsToStrings(hosts []host) []string {\n\tout := make([]string, len(hosts))\n\n\tfor i, host := range hosts {\n\t\tout[i] = host.toString()\n\t}\n\n\treturn out\n}\n\nfunc runTestsInFile(t *testing.T, dirname string, filename string, warningsError bool) {\n\tfilepath := path.Join(dirname, filename)\n\tcontent, err := ioutil.ReadFile(filepath)\n\trequire.NoError(t, err)\n\n\tvar container testContainer\n\trequire.NoError(t, json.Unmarshal(content, &container))\n\n\tt.Run(filename, func(t *testing.T) {\n\t\tfor _, testCase := range container.Tests {\n\t\t\ttestCase := testCase // Capture range variable.\n\n\t\t\tt.Run(testCase.Description, func(t *testing.T) {\n\t\t\t\tspectest.CheckSkip(t)\n\n\t\t\t\trunTest(t, testCase, warningsError)\n\t\t\t})\n\t\t}\n\t})\n}\n\nfunc runTest(t *testing.T, test testCase, warningsError bool) {\n\tspectest.CheckSkip(t)\n\n\tcs, err := connstring.ParseAndValidate(test.URI)\n\t// Since we don't have warnings in Go, we return warnings as errors.\n\t//\n\t// This is a bit unfortunate, but since we do raise warnings as errors with the newer\n\t// URI options, but don't with some of the older things, we do a switch on the filename\n\t// here. We are trying to not break existing user applications that have unrecognized\n\t// options.\n\tif test.Valid && (!test.Warning || !warningsError) {\n\t\trequire.NoError(t, err)\n\t} else {\n\t\trequire.Error(t, err)\n\t\treturn\n\t}\n\n\trequire.Equal(t, test.URI, cs.Original)\n\n\tif test.Hosts != nil {\n\t\trequire.Equal(t, hostsToStrings(test.Hosts), cs.Hosts)\n\t}\n\n\tif test.Auth != nil {\n\t\trequire.Equal(t, test.Auth.Username, cs.Username)\n\n\t\tif test.Auth.Password == nil {\n\t\t\trequire.False(t, cs.PasswordSet)\n\t\t} else {\n\t\t\trequire.True(t, cs.PasswordSet)\n\t\t\trequire.Equal(t, *test.Auth.Password, cs.Password)\n\t\t}\n\n\t\tif test.Auth.DB != cs.Database {\n\t\t\trequire.Equal(t, test.Auth.DB, cs.AuthSource)\n\t\t} else {\n\t\t\trequire.Equal(t, test.Auth.DB, cs.Database)\n\t\t}\n\t}\n\n\t// Check that all options are present.\n\tverifyConnStringOptions(t, cs, test.Options)\n\n\t// Check that non-present options are unset. This will be redundant with the above checks\n\t// for options that are present.\n\tvar ok bool\n\n\t_, ok = test.Options[\"maxPoolSize\"]\n\trequire.Equal(t, ok, cs.MaxPoolSizeSet)\n}\n\n// Test case for all connection string spec tests.\nfunc TestConnStringSpec(t *testing.T) {\n\tfor _, file := range spectest.FindJSONFilesInDir(t, connstringTestsDir) {\n\t\trunTestsInFile(t, connstringTestsDir, file, false)\n\t}\n}\n\nfunc TestURIOptionsSpec(t *testing.T) {\n\tfor _, file := range spectest.FindJSONFilesInDir(t, urioptionsTestDir) {\n\t\trunTestsInFile(t, urioptionsTestDir, file, true)\n\t}\n}\n\n// verifyConnStringOptions verifies the options on the connection string.\nfunc verifyConnStringOptions(t *testing.T, cs *connstring.ConnString, options map[string]any) {\n\t// Check that all options are present.\n\tfor key, value := range options {\n\n\t\tkey = strings.ToLower(key)\n\t\tswitch key {\n\t\tcase \"appname\":\n\t\t\trequire.Equal(t, value, cs.AppName)\n\t\tcase \"authsource\":\n\t\t\trequire.Equal(t, value, cs.AuthSource)\n\t\tcase \"authmechanism\":\n\t\t\trequire.Equal(t, value, cs.AuthMechanism)\n\t\tcase \"authmechanismproperties\":\n\t\t\tconvertedMap := value.(map[string]any)\n\t\t\trequire.Equal(t,\n\t\t\t\tmapInterfaceToString(convertedMap),\n\t\t\t\tcs.AuthMechanismProperties)\n\t\tcase \"compressors\":\n\t\t\trequire.Equal(t, convertToStringSlice(value), cs.Compressors)\n\t\tcase \"connecttimeoutms\":\n\t\t\trequire.Equal(t, value, float64(cs.ConnectTimeout/time.Millisecond))\n\t\tcase \"directconnection\":\n\t\t\trequire.True(t, cs.DirectConnectionSet)\n\t\t\trequire.Equal(t, value, cs.DirectConnection)\n\t\tcase \"heartbeatfrequencyms\":\n\t\t\trequire.Equal(t, value, float64(cs.HeartbeatInterval/time.Millisecond))\n\t\tcase \"journal\":\n\t\t\trequire.True(t, cs.JSet)\n\t\t\trequire.Equal(t, value, cs.J)\n\t\tcase \"loadbalanced\":\n\t\t\trequire.True(t, cs.LoadBalancedSet)\n\t\t\trequire.Equal(t, value, cs.LoadBalanced)\n\t\tcase \"localthresholdms\":\n\t\t\trequire.True(t, cs.LocalThresholdSet)\n\t\t\trequire.Equal(t, value, float64(cs.LocalThreshold/time.Millisecond))\n\t\tcase \"maxidletimems\":\n\t\t\trequire.Equal(t, value, float64(cs.MaxConnIdleTime/time.Millisecond))\n\t\tcase \"maxpoolsize\":\n\t\t\trequire.True(t, cs.MaxPoolSizeSet)\n\t\t\trequire.Equal(t, value, float64(cs.MaxPoolSize))\n\t\tcase \"maxstalenessseconds\":\n\t\t\trequire.True(t, cs.MaxStalenessSet)\n\t\t\trequire.Equal(t, value, float64(cs.MaxStaleness/time.Second))\n\t\tcase \"minpoolsize\":\n\t\t\trequire.True(t, cs.MinPoolSizeSet)\n\t\t\trequire.Equal(t, value, float64(cs.MinPoolSize))\n\t\tcase \"readpreference\":\n\t\t\trequire.Equal(t, value, cs.ReadPreference)\n\t\tcase \"readpreferencetags\":\n\t\t\tsm, ok := value.([]any)\n\t\t\trequire.True(t, ok)\n\t\t\ttags := make([]map[string]string, 0, len(sm))\n\t\t\tfor _, i := range sm {\n\t\t\t\tm, ok := i.(map[string]any)\n\t\t\t\trequire.True(t, ok)\n\t\t\t\ttags = append(tags, mapInterfaceToString(m))\n\t\t\t}\n\t\t\trequire.Equal(t, tags, cs.ReadPreferenceTagSets)\n\t\tcase \"readconcernlevel\":\n\t\t\trequire.Equal(t, value, cs.ReadConcernLevel)\n\t\tcase \"replicaset\":\n\t\t\trequire.Equal(t, value, cs.ReplicaSet)\n\t\tcase \"retrywrites\":\n\t\t\trequire.True(t, cs.RetryWritesSet)\n\t\t\trequire.Equal(t, value, cs.RetryWrites)\n\t\tcase \"serverselectiontimeoutms\":\n\t\t\trequire.Equal(t, value, float64(cs.ServerSelectionTimeout/time.Millisecond))\n\t\tcase \"srvmaxhosts\":\n\t\t\trequire.Equal(t, value, float64(cs.SRVMaxHosts))\n\t\tcase \"srvservicename\":\n\t\t\trequire.Equal(t, value, cs.SRVServiceName)\n\t\tcase \"ssl\", \"tls\":\n\t\t\trequire.Equal(t, value, cs.SSL)\n\t\tcase \"sockettimeoutms\":\n\t\t\trequire.Equal(t, value, float64(cs.SocketTimeout/time.Millisecond))\n\t\tcase \"tlsallowinvalidcertificates\", \"tlsallowinvalidhostnames\", \"tlsinsecure\":\n\t\t\trequire.True(t, cs.SSLInsecureSet)\n\t\t\trequire.Equal(t, value, cs.SSLInsecure)\n\t\tcase \"tlscafile\":\n\t\t\trequire.True(t, cs.SSLCaFileSet)\n\t\t\trequire.Equal(t, value, cs.SSLCaFile)\n\t\tcase \"tlscertificatekeyfile\":\n\t\t\trequire.True(t, cs.SSLClientCertificateKeyFileSet)\n\t\t\trequire.Equal(t, value, cs.SSLClientCertificateKeyFile)\n\t\tcase \"tlscertificatekeyfilepassword\":\n\t\t\trequire.True(t, cs.SSLClientCertificateKeyPasswordSet)\n\t\t\trequire.Equal(t, value, cs.SSLClientCertificateKeyPassword())\n\t\tcase \"w\":\n\t\t\tif cs.WNumberSet {\n\t\t\t\tvalueInt := getIntFromInterface(value)\n\t\t\t\trequire.NotNil(t, valueInt)\n\t\t\t\trequire.Equal(t, *valueInt, int64(cs.WNumber))\n\t\t\t} else {\n\t\t\t\trequire.Equal(t, value, cs.WString)\n\t\t\t}\n\t\tcase \"waitqueuetimeoutms\":\n\t\tcase \"zlibcompressionlevel\":\n\t\t\trequire.Equal(t, value, float64(cs.ZlibLevel))\n\t\tcase \"zstdcompressionlevel\":\n\t\t\trequire.Equal(t, value, float64(cs.ZstdLevel))\n\t\tcase \"tlsdisableocspendpointcheck\":\n\t\t\trequire.Equal(t, value, cs.SSLDisableOCSPEndpointCheck)\n\t\tcase \"servermonitoringmode\":\n\t\t\trequire.Equal(t, value, cs.ServerMonitoringMode)\n\t\tcase \"timeoutms\":\n\t\t\trequire.Equal(t, value, float64(cs.Timeout/time.Millisecond))\n\t\tcase \"maxconnecting\":\n\t\t\trequire.Equal(t, value, float64(cs.MaxConnecting))\n\t\tdefault:\n\t\t\topt, ok := cs.UnknownOptions[key]\n\t\t\trequire.True(t, ok)\n\t\t\trequire.Contains(t, opt, fmt.Sprint(value))\n\t\t}\n\t}\n}\n\n// Convert each any value in the map to a string.\nfunc mapInterfaceToString(m map[string]any) map[string]string {\n\tout := make(map[string]string)\n\n\tfor key, value := range m {\n\t\tout[key] = fmt.Sprint(value)\n\t}\n\n\treturn out\n}\n\n// getIntFromInterface attempts to convert an empty interface value to an integer.\n//\n// Returns nil if it is not possible.\nfunc getIntFromInterface(i any) *int64 {\n\tvar out int64\n\n\tswitch v := i.(type) {\n\tcase int:\n\t\tout = int64(v)\n\tcase int32:\n\t\tout = int64(v)\n\tcase int64:\n\t\tout = v\n\tcase float32:\n\t\tf := float64(v)\n\t\tif math.Floor(f) != f || f > float64(math.MaxInt64) {\n\t\t\tbreak\n\t\t}\n\n\t\tout = int64(f)\n\n\tcase float64:\n\t\tif math.Floor(v) != v || v > float64(math.MaxInt64) {\n\t\t\tbreak\n\t\t}\n\n\t\tout = int64(v)\n\tdefault:\n\t\treturn nil\n\t}\n\n\treturn &out\n}\n\nfunc convertToStringSlice(i any) []string {\n\ts, ok := i.([]any)\n\tif !ok {\n\t\treturn nil\n\t}\n\tret := make([]string, 0, len(s))\n\tfor _, v := range s {\n\t\tstr, ok := v.(string)\n\t\tif !ok {\n\t\t\tcontinue\n\t\t}\n\t\tret = append(ret, str)\n\t}\n\treturn ret\n}\n"
  },
  {
    "path": "x/mongo/driver/connstring/connstring_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage connstring_test\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/require\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/connstring\"\n)\n\nfunc TestAppName(t *testing.T) {\n\ttests := []struct {\n\t\ts        string\n\t\texpected string\n\t\terr      bool\n\t}{\n\t\t{s: \"appName=Funny\", expected: \"Funny\"},\n\t\t{s: \"appName=awesome\", expected: \"awesome\"},\n\t\t{s: \"appName=\", expected: \"\"},\n\t}\n\n\tfor _, test := range tests {\n\t\ts := fmt.Sprintf(\"mongodb://localhost/?%s\", test.s)\n\t\tt.Run(s, func(t *testing.T) {\n\t\t\tcs, err := connstring.ParseAndValidate(s)\n\t\t\tif test.err {\n\t\t\t\trequire.Error(t, err)\n\t\t\t} else {\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.Equal(t, test.expected, cs.AppName)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestAuthMechanism(t *testing.T) {\n\ttests := []struct {\n\t\ts        string\n\t\texpected string\n\t\terr      bool\n\t}{\n\t\t{s: \"authMechanism=scram-sha-1\", expected: \"scram-sha-1\"},\n\t\t{s: \"authMechanism=scram-sha-256\", expected: \"scram-sha-256\"},\n\t\t{s: \"authMechanism=plain\", expected: \"plain\"},\n\t}\n\n\tfor _, test := range tests {\n\t\ts := fmt.Sprintf(\"mongodb://user:pass@localhost/?%s\", test.s)\n\t\tt.Run(s, func(t *testing.T) {\n\t\t\tcs, err := connstring.ParseAndValidate(s)\n\t\t\tif test.err {\n\t\t\t\trequire.Error(t, err)\n\t\t\t} else {\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.Equal(t, test.expected, cs.AuthMechanism)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestAuthSource(t *testing.T) {\n\ttests := []struct {\n\t\ts        string\n\t\texpected string\n\t\terr      bool\n\t}{\n\t\t{s: \"foobar?authSource=bazqux\", expected: \"bazqux\"},\n\t\t{s: \"foobar\", expected: \"foobar\"},\n\t\t{s: \"\", expected: \"admin\"},\n\t}\n\n\tfor _, test := range tests {\n\t\ts := fmt.Sprintf(\"mongodb://user:pass@localhost/%s\", test.s)\n\t\tt.Run(s, func(t *testing.T) {\n\t\t\tcs, err := connstring.ParseAndValidate(s)\n\t\t\tif test.err {\n\t\t\t\trequire.Error(t, err)\n\t\t\t} else {\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.Equal(t, test.expected, cs.AuthSource)\n\t\t\t}\n\t\t})\n\t}\n\n\ttests = []struct {\n\t\ts        string\n\t\texpected string\n\t\terr      bool\n\t}{\n\t\t{s: \"authMechanismProperties=ENVIRONMENT:gcp,TOKEN_RESOURCE:mongodb://test-cluster\", expected: \"$external\"},\n\t}\n\n\tfor _, test := range tests {\n\t\ts := fmt.Sprintf(\"mongodb://test.mongodb.net/?authMechanism=MONGODB-OIDC&/%s\", test.s)\n\t\tt.Run(s, func(t *testing.T) {\n\t\t\tcs, err := connstring.ParseAndValidate(s)\n\t\t\tif test.err {\n\t\t\t\trequire.Error(t, err)\n\t\t\t} else {\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.Equal(t, test.expected, cs.AuthSource)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestConnect(t *testing.T) {\n\ttests := []struct {\n\t\ts        string\n\t\texpected connstring.ConnectMode\n\t\terr      bool\n\t}{\n\t\t{s: \"connect=automatic\", expected: connstring.AutoConnect},\n\t\t{s: \"connect=AUTOMATIC\", expected: connstring.AutoConnect},\n\t\t{s: \"connect=direct\", expected: connstring.SingleConnect},\n\t\t{s: \"connect=blah\", err: true},\n\t\t// Combinations of connect and directConnection where connect is set first - conflicting combinations must\n\t\t// error.\n\t\t{s: \"connect=automatic&directConnection=true\", err: true},\n\t\t{s: \"connect=automatic&directConnection=false\", expected: connstring.AutoConnect},\n\t\t{s: \"connect=direct&directConnection=true\", expected: connstring.SingleConnect},\n\t\t{s: \"connect=direct&directConnection=false\", err: true},\n\t\t// Combinations of connect and directConnection where directConnection is set first.\n\t\t{s: \"directConnection=true&connect=automatic\", err: true},\n\t\t{s: \"directConnection=false&connect=automatic\", expected: connstring.AutoConnect},\n\t\t{s: \"directConnection=true&connect=direct\", expected: connstring.SingleConnect},\n\t\t{s: \"directConnection=false&connect=direct\", err: true},\n\t}\n\n\tfor _, test := range tests {\n\t\ts := fmt.Sprintf(\"mongodb://localhost/?%s\", test.s)\n\t\tt.Run(s, func(t *testing.T) {\n\t\t\tcs, err := connstring.ParseAndValidate(s)\n\t\t\tif test.err {\n\t\t\t\trequire.Error(t, err)\n\t\t\t} else {\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.Equal(t, test.expected, cs.Connect)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestDirectConnection(t *testing.T) {\n\ttestCases := []struct {\n\t\ts        string\n\t\texpected bool\n\t\terr      bool\n\t}{\n\t\t{\"directConnection=true\", true, false},\n\t\t{\"directConnection=false\", false, false},\n\t\t{\"directConnection=TRUE\", true, false},\n\t\t{\"directConnection=FALSE\", false, false},\n\t\t{\"directConnection=blah\", false, true},\n\t}\n\n\tfor _, tc := range testCases {\n\t\ts := fmt.Sprintf(\"mongodb://localhost/?%s\", tc.s)\n\t\tt.Run(s, func(t *testing.T) {\n\t\t\tcs, err := connstring.ParseAndValidate(s)\n\t\t\tif tc.err {\n\t\t\t\tassert.NotNil(t, err, \"expected error, got nil\")\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tassert.Nil(t, err, \"expected no error, got %v\", err)\n\t\t\tassert.Equal(t, tc.expected, cs.DirectConnection, \"expected DirectConnection value %v, got %v\", tc.expected,\n\t\t\t\tcs.DirectConnection)\n\t\t\tassert.True(t, cs.DirectConnectionSet, \"expected DirectConnectionSet to be true, got false\")\n\t\t})\n\t}\n}\n\nfunc TestConnectTimeout(t *testing.T) {\n\ttests := []struct {\n\t\ts        string\n\t\texpected time.Duration\n\t\terr      bool\n\t}{\n\t\t{s: \"connectTimeoutMS=10\", expected: time.Duration(10) * time.Millisecond},\n\t\t{s: \"connectTimeoutMS=100\", expected: time.Duration(100) * time.Millisecond},\n\t\t{s: \"connectTimeoutMS=-2\", err: true},\n\t\t{s: \"connectTimeoutMS=gsdge\", err: true},\n\t}\n\n\tfor _, test := range tests {\n\t\ts := fmt.Sprintf(\"mongodb://localhost/?%s\", test.s)\n\t\tt.Run(s, func(t *testing.T) {\n\t\t\tcs, err := connstring.ParseAndValidate(s)\n\t\t\tif test.err {\n\t\t\t\trequire.Error(t, err)\n\t\t\t} else {\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.Equal(t, test.expected, cs.ConnectTimeout)\n\t\t\t\trequire.True(t, cs.ConnectTimeoutSet)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestHeartbeatInterval(t *testing.T) {\n\ttests := []struct {\n\t\ts        string\n\t\texpected time.Duration\n\t\terr      bool\n\t}{\n\t\t{s: \"heartbeatIntervalMS=10\", expected: time.Duration(10) * time.Millisecond},\n\t\t{s: \"heartbeatIntervalMS=100\", expected: time.Duration(100) * time.Millisecond},\n\t\t{s: \"heartbeatIntervalMS=-2\", err: true},\n\t\t{s: \"heartbeatIntervalMS=gsdge\", err: true},\n\t}\n\n\tfor _, test := range tests {\n\t\ts := fmt.Sprintf(\"mongodb://localhost/?%s\", test.s)\n\t\tt.Run(s, func(t *testing.T) {\n\t\t\tcs, err := connstring.ParseAndValidate(s)\n\t\t\tif test.err {\n\t\t\t\trequire.Error(t, err)\n\t\t\t} else {\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.Equal(t, test.expected, cs.HeartbeatInterval)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestLocalThreshold(t *testing.T) {\n\ttests := []struct {\n\t\ts        string\n\t\texpected time.Duration\n\t\terr      bool\n\t}{\n\t\t{s: \"localThresholdMS=0\", expected: time.Duration(0) * time.Millisecond},\n\t\t{s: \"localThresholdMS=10\", expected: time.Duration(10) * time.Millisecond},\n\t\t{s: \"localThresholdMS=100\", expected: time.Duration(100) * time.Millisecond},\n\t\t{s: \"localThresholdMS=-2\", err: true},\n\t\t{s: \"localThresholdMS=gsdge\", err: true},\n\t}\n\n\tfor _, test := range tests {\n\t\ts := fmt.Sprintf(\"mongodb://localhost/?%s\", test.s)\n\t\tt.Run(s, func(t *testing.T) {\n\t\t\tcs, err := connstring.ParseAndValidate(s)\n\t\t\tif test.err {\n\t\t\t\trequire.Error(t, err)\n\t\t\t} else {\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.Equal(t, test.expected, cs.LocalThreshold)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestMaxConnIdleTime(t *testing.T) {\n\ttests := []struct {\n\t\ts        string\n\t\texpected time.Duration\n\t\terr      bool\n\t}{\n\t\t{s: \"maxIdleTimeMS=10\", expected: time.Duration(10) * time.Millisecond},\n\t\t{s: \"maxIdleTimeMS=100\", expected: time.Duration(100) * time.Millisecond},\n\t\t{s: \"maxIdleTimeMS=-2\", err: true},\n\t\t{s: \"maxIdleTimeMS=gsdge\", err: true},\n\t}\n\n\tfor _, test := range tests {\n\t\ts := fmt.Sprintf(\"mongodb://localhost/?%s\", test.s)\n\t\tt.Run(s, func(t *testing.T) {\n\t\t\tcs, err := connstring.ParseAndValidate(s)\n\t\t\tif test.err {\n\t\t\t\trequire.Error(t, err)\n\t\t\t} else {\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.Equal(t, test.expected, cs.MaxConnIdleTime)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestMaxPoolSize(t *testing.T) {\n\ttests := []struct {\n\t\ts        string\n\t\texpected uint64\n\t\terr      bool\n\t}{\n\t\t{s: \"maxPoolSize=10\", expected: 10},\n\t\t{s: \"maxPoolSize=100\", expected: 100},\n\t\t{s: \"maxPoolSize=-2\", err: true},\n\t\t{s: \"maxPoolSize=gsdge\", err: true},\n\t}\n\n\tfor _, test := range tests {\n\t\ts := fmt.Sprintf(\"mongodb://localhost/?%s\", test.s)\n\t\tt.Run(s, func(t *testing.T) {\n\t\t\tcs, err := connstring.ParseAndValidate(s)\n\t\t\tif test.err {\n\t\t\t\trequire.Error(t, err)\n\t\t\t} else {\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.True(t, cs.MaxPoolSizeSet)\n\t\t\t\trequire.Equal(t, test.expected, cs.MaxPoolSize)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestMinPoolSize(t *testing.T) {\n\ttests := []struct {\n\t\ts        string\n\t\texpected uint64\n\t\terr      bool\n\t}{\n\t\t{s: \"minPoolSize=10\", expected: 10},\n\t\t{s: \"minPoolSize=100\", expected: 100},\n\t\t{s: \"minPoolSize=-2\", err: true},\n\t\t{s: \"minPoolSize=gsdge\", err: true},\n\t}\n\n\tfor _, test := range tests {\n\t\ts := fmt.Sprintf(\"mongodb://localhost/?%s\", test.s)\n\t\tt.Run(s, func(t *testing.T) {\n\t\t\tcs, err := connstring.ParseAndValidate(s)\n\t\t\tif test.err {\n\t\t\t\trequire.Error(t, err)\n\t\t\t} else {\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.True(t, cs.MinPoolSizeSet)\n\t\t\t\trequire.Equal(t, test.expected, cs.MinPoolSize)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestMaxConnecting(t *testing.T) {\n\ttests := []struct {\n\t\ts        string\n\t\texpected uint64\n\t\terr      bool\n\t}{\n\t\t{s: \"maxConnecting=10\", expected: 10},\n\t\t{s: \"maxConnecting=100\", expected: 100},\n\t\t{s: \"maxConnecting=-2\", err: true},\n\t\t{s: \"maxConnecting=gsdge\", err: true},\n\t}\n\n\tfor _, test := range tests {\n\t\ts := fmt.Sprintf(\"mongodb://localhost/?%s\", test.s)\n\t\tt.Run(s, func(t *testing.T) {\n\t\t\tcs, err := connstring.ParseAndValidate(s)\n\t\t\tif test.err {\n\t\t\t\trequire.Error(t, err)\n\t\t\t} else {\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.True(t, cs.MaxConnectingSet)\n\t\t\t\trequire.Equal(t, test.expected, cs.MaxConnecting)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestReadPreference(t *testing.T) {\n\ttests := []struct {\n\t\ts        string\n\t\texpected string\n\t\terr      bool\n\t}{\n\t\t{s: \"readPreference=primary\", expected: \"primary\"},\n\t\t{s: \"readPreference=secondaryPreferred\", expected: \"secondaryPreferred\"},\n\t\t{s: \"readPreference=something\", expected: \"something\"}, // we don't validate here\n\t}\n\n\tfor _, test := range tests {\n\t\ts := fmt.Sprintf(\"mongodb://localhost/?%s\", test.s)\n\t\tt.Run(s, func(t *testing.T) {\n\t\t\tcs, err := connstring.ParseAndValidate(s)\n\t\t\tif test.err {\n\t\t\t\trequire.Error(t, err)\n\t\t\t} else {\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.Equal(t, test.expected, cs.ReadPreference)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestReadPreferenceTags(t *testing.T) {\n\ttests := []struct {\n\t\ts        string\n\t\texpected []map[string]string\n\t\terr      bool\n\t}{\n\t\t{s: \"\", expected: nil},\n\t\t{s: \"readPreferenceTags=one:1\", expected: []map[string]string{{\"one\": \"1\"}}},\n\t\t{s: \"readPreferenceTags=one:1,two:2\", expected: []map[string]string{{\"one\": \"1\", \"two\": \"2\"}}},\n\t\t{s: \"readPreferenceTags=one:1&readPreferenceTags=two:2\", expected: []map[string]string{{\"one\": \"1\"}, {\"two\": \"2\"}}},\n\t\t{s: \"readPreferenceTags=one:1:3,two:2\", err: true},\n\t\t{s: \"readPreferenceTags=one:1&readPreferenceTags=two:2&readPreferenceTags=\", expected: []map[string]string{{\"one\": \"1\"}, {\"two\": \"2\"}, {}}},\n\t\t{s: \"readPreferenceTags=\", expected: []map[string]string{{}}},\n\t}\n\n\tfor _, test := range tests {\n\t\ts := fmt.Sprintf(\"mongodb://localhost/?%s\", test.s)\n\t\tt.Run(s, func(t *testing.T) {\n\t\t\tcs, err := connstring.ParseAndValidate(s)\n\t\t\tif test.err {\n\t\t\t\trequire.Error(t, err)\n\t\t\t} else {\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.Equal(t, test.expected, cs.ReadPreferenceTagSets)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestMaxStaleness(t *testing.T) {\n\ttests := []struct {\n\t\ts        string\n\t\texpected time.Duration\n\t\terr      bool\n\t}{\n\t\t{s: \"maxStaleness=10\", expected: time.Duration(10) * time.Second},\n\t\t{s: \"maxStaleness=100\", expected: time.Duration(100) * time.Second},\n\t\t{s: \"maxStaleness=-2\", err: true},\n\t\t{s: \"maxStaleness=gsdge\", err: true},\n\t}\n\tfor _, test := range tests {\n\t\ts := fmt.Sprintf(\"mongodb://localhost/?%s\", test.s)\n\t\tt.Run(s, func(t *testing.T) {\n\t\t\tcs, err := connstring.ParseAndValidate(s)\n\t\t\tif test.err {\n\t\t\t\trequire.Error(t, err)\n\t\t\t} else {\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.Equal(t, test.expected, cs.MaxStaleness)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestReplicaSet(t *testing.T) {\n\ttests := []struct {\n\t\ts        string\n\t\texpected string\n\t\terr      bool\n\t}{\n\t\t{s: \"replicaSet=auto\", expected: \"auto\"},\n\t\t{s: \"replicaSet=rs0\", expected: \"rs0\"},\n\t}\n\n\tfor _, test := range tests {\n\t\ts := fmt.Sprintf(\"mongodb://localhost/?%s\", test.s)\n\t\tt.Run(s, func(t *testing.T) {\n\t\t\tcs, err := connstring.ParseAndValidate(s)\n\t\t\tif test.err {\n\t\t\t\trequire.Error(t, err)\n\t\t\t} else {\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.Equal(t, test.expected, cs.ReplicaSet)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestRetryWrites(t *testing.T) {\n\ttests := []struct {\n\t\ts        string\n\t\texpected bool\n\t\terr      bool\n\t}{\n\t\t{s: \"retryWrites=true\", expected: true},\n\t\t{s: \"retryWrites=false\", expected: false},\n\t\t{s: \"retryWrites=foobar\", expected: false, err: true},\n\t}\n\n\tfor _, test := range tests {\n\t\ts := fmt.Sprintf(\"mongodb://localhost/?%s\", test.s)\n\t\tt.Run(s, func(t *testing.T) {\n\t\t\tcs, err := connstring.ParseAndValidate(s)\n\t\t\tif test.err {\n\t\t\t\trequire.Error(t, err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\trequire.NoError(t, err)\n\t\t\trequire.Equal(t, test.expected, cs.RetryWrites)\n\t\t\trequire.True(t, cs.RetryWritesSet)\n\t\t})\n\t}\n}\n\nfunc TestRetryReads(t *testing.T) {\n\ttests := []struct {\n\t\ts        string\n\t\texpected bool\n\t\terr      bool\n\t}{\n\t\t{s: \"retryReads=true\", expected: true},\n\t\t{s: \"retryReads=false\", expected: false},\n\t\t{s: \"retryReads=foobar\", expected: false, err: true},\n\t}\n\n\tfor _, test := range tests {\n\t\ts := fmt.Sprintf(\"mongodb://localhost/?%s\", test.s)\n\t\tt.Run(s, func(t *testing.T) {\n\t\t\tcs, err := connstring.ParseAndValidate(s)\n\t\t\tif test.err {\n\t\t\t\trequire.Error(t, err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\trequire.NoError(t, err)\n\t\t\trequire.Equal(t, test.expected, cs.RetryReads)\n\t\t\trequire.True(t, cs.RetryReadsSet)\n\t\t})\n\t}\n}\n\nfunc TestScheme(t *testing.T) {\n\t// Can't unit test 'mongodb+srv' because that requires networking.  Tested\n\t// in x/mongo/driver/topology/initial_dns_seedlist_discovery_test.go\n\tcs, err := connstring.ParseAndValidate(\"mongodb://localhost/\")\n\trequire.NoError(t, err)\n\trequire.Equal(t, cs.Scheme, \"mongodb\")\n\trequire.Equal(t, cs.Scheme, connstring.SchemeMongoDB)\n}\n\nfunc TestServerSelectionTimeout(t *testing.T) {\n\ttests := []struct {\n\t\ts        string\n\t\texpected time.Duration\n\t\terr      bool\n\t}{\n\t\t{s: \"serverSelectionTimeoutMS=10\", expected: time.Duration(10) * time.Millisecond},\n\t\t{s: \"serverSelectionTimeoutMS=100\", expected: time.Duration(100) * time.Millisecond},\n\t\t{s: \"serverSelectionTimeoutMS=-2\", err: true},\n\t\t{s: \"serverSelectionTimeoutMS=gsdge\", err: true},\n\t}\n\n\tfor _, test := range tests {\n\t\ts := fmt.Sprintf(\"mongodb://localhost/?%s\", test.s)\n\t\tt.Run(s, func(t *testing.T) {\n\t\t\tcs, err := connstring.ParseAndValidate(s)\n\t\t\tif test.err {\n\t\t\t\trequire.Error(t, err)\n\t\t\t} else {\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.Equal(t, test.expected, cs.ServerSelectionTimeout)\n\t\t\t\trequire.True(t, cs.ServerSelectionTimeoutSet)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestSocketTimeout(t *testing.T) {\n\ttests := []struct {\n\t\ts        string\n\t\texpected time.Duration\n\t\terr      bool\n\t}{\n\t\t{s: \"socketTimeoutMS=10\", expected: time.Duration(10) * time.Millisecond},\n\t\t{s: \"socketTimeoutMS=100\", expected: time.Duration(100) * time.Millisecond},\n\t\t{s: \"socketTimeoutMS=-2\", err: true},\n\t\t{s: \"socketTimeoutMS=gsdge\", err: true},\n\t}\n\n\tfor _, test := range tests {\n\t\ts := fmt.Sprintf(\"mongodb://localhost/?%s\", test.s)\n\t\tt.Run(s, func(t *testing.T) {\n\t\t\tcs, err := connstring.ParseAndValidate(s)\n\t\t\tif test.err {\n\t\t\t\trequire.Error(t, err)\n\t\t\t} else {\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\trequire.Equal(t, test.expected, cs.SocketTimeout)\n\t\t\t\trequire.True(t, cs.SocketTimeoutSet)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestCompressionOptions(t *testing.T) {\n\ttests := []struct {\n\t\tname        string\n\t\turiOptions  string\n\t\tcompressors []string\n\t\tzlibLevel   int\n\t\tzstdLevel   int\n\t\terr         bool\n\t}{\n\t\t{name: \"SingleCompressor\", uriOptions: \"compressors=zlib\", compressors: []string{\"zlib\"}},\n\t\t{name: \"MultiCompressors\", uriOptions: \"compressors=snappy,zlib\", compressors: []string{\"snappy\", \"zlib\"}},\n\t\t{name: \"ZlibWithLevel\", uriOptions: \"compressors=zlib&zlibCompressionLevel=7\", compressors: []string{\"zlib\"}, zlibLevel: 7},\n\t\t{name: \"DefaultZlibLevel\", uriOptions: \"compressors=zlib&zlibCompressionLevel=-1\", compressors: []string{\"zlib\"}, zlibLevel: 6},\n\t\t{name: \"InvalidZlibLevel\", uriOptions: \"compressors=zlib&zlibCompressionLevel=-2\", compressors: []string{\"zlib\"}, err: true},\n\t\t{name: \"ZstdWithLevel\", uriOptions: \"compressors=zstd&zstdCompressionLevel=20\", compressors: []string{\"zstd\"}, zstdLevel: 20},\n\t\t{name: \"DefaultZstdLevel\", uriOptions: \"compressors=zstd&zstdCompressionLevel=-1\", compressors: []string{\"zstd\"}, zstdLevel: 6},\n\t\t{name: \"InvalidZstdLevel\", uriOptions: \"compressors=zstd&zstdCompressionLevel=30\", compressors: []string{\"zstd\"}, err: true},\n\t}\n\n\tfor _, tc := range tests {\n\t\turi := fmt.Sprintf(\"mongodb://localhost/?%s\", tc.uriOptions)\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\tcs, err := connstring.ParseAndValidate(uri)\n\t\t\tif tc.err {\n\t\t\t\tassert.Error(t, err)\n\t\t\t} else {\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tassert.Equal(t, tc.compressors, cs.Compressors)\n\t\t\t\tif tc.zlibLevel != 0 {\n\t\t\t\t\tassert.Equal(t, tc.zlibLevel, cs.ZlibLevel)\n\t\t\t\t}\n\t\t\t\tif tc.zstdLevel != 0 {\n\t\t\t\t\tassert.Equal(t, tc.zstdLevel, cs.ZstdLevel)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "x/mongo/driver/connstring/initial_dns_seedlist_discovery_prose_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2024-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage connstring\n\nimport (\n\t\"fmt\"\n\t\"net\"\n\t\"testing\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/dns\"\n)\n\nfunc TestInitialDNSSeedlistDiscoveryProse(t *testing.T) {\n\tnewTestParser := func(record string) *parser {\n\t\treturn &parser{&dns.Resolver{\n\t\t\tLookupSRV: func(_, _, _ string) (string, []*net.SRV, error) {\n\t\t\t\treturn \"\", []*net.SRV{\n\t\t\t\t\t{\n\t\t\t\t\t\tTarget: record,\n\t\t\t\t\t\tPort:   27017,\n\t\t\t\t\t},\n\t\t\t\t}, nil\n\t\t\t},\n\t\t\tLookupTXT: func(string) ([]string, error) {\n\t\t\t\treturn nil, nil\n\t\t\t},\n\t\t}}\n\t}\n\n\tt.Run(\"1. Allow SRVs with fewer than 3 . separated parts\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tcases := []struct {\n\t\t\trecord string\n\t\t\turi    string\n\t\t}{\n\t\t\t{\"test_1.localhost\", \"mongodb+srv://localhost\"},\n\t\t\t{\"test_1.mongo.local\", \"mongodb+srv://mongo.local\"},\n\t\t}\n\t\tfor _, c := range cases {\n\t\t\tc := c\n\t\t\tt.Run(c.uri, func(t *testing.T) {\n\t\t\t\tt.Parallel()\n\n\t\t\t\t_, err := newTestParser(c.record).parse(c.uri)\n\t\t\t\tassert.NoError(t, err, \"expected no URI parsing error, got %v\", err)\n\t\t\t})\n\t\t}\n\t})\n\tt.Run(\"2. Throw when return address does not end with SRV domain\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tcases := []struct {\n\t\t\trecord string\n\t\t\turi    string\n\t\t}{\n\t\t\t{\"localhost.mongodb\", \"mongodb+srv://localhost\"},\n\t\t\t{\"test_1.evil.local\", \"mongodb+srv://mongo.local\"},\n\t\t\t{\"blogs.evil.com\", \"mongodb+srv://blogs.mongodb.com\"},\n\t\t}\n\t\tfor _, c := range cases {\n\t\t\tc := c\n\t\t\tt.Run(c.uri, func(t *testing.T) {\n\t\t\t\tt.Parallel()\n\n\t\t\t\t_, err := newTestParser(c.record).parse(c.uri)\n\t\t\t\tassert.ErrorContains(t, err, \"domain suffix from SRV record not matched input domain\")\n\t\t\t})\n\t\t}\n\t})\n\tt.Run(\"3. Throw when return address is identical to SRV hostname\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tcases := []struct {\n\t\t\trecord string\n\t\t\turi    string\n\t\t\tlabels int\n\t\t}{\n\t\t\t{\"localhost\", \"mongodb+srv://localhost\", 1},\n\t\t\t{\"mongo.local\", \"mongodb+srv://mongo.local\", 2},\n\t\t}\n\t\tfor _, c := range cases {\n\t\t\tc := c\n\t\t\tt.Run(c.uri, func(t *testing.T) {\n\t\t\t\tt.Parallel()\n\n\t\t\t\t_, err := newTestParser(c.record).parse(c.uri)\n\t\t\t\texpected := fmt.Sprintf(\n\t\t\t\t\t\"server record (%d levels) should have more domain levels than parent URI (%d levels)\",\n\t\t\t\t\tc.labels, c.labels,\n\t\t\t\t)\n\t\t\t\tassert.ErrorContains(t, err, expected)\n\t\t\t})\n\t\t}\n\t})\n\tt.Run(\"4. Throw when return address does not contain . separating shared part of domain\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tcases := []struct {\n\t\t\trecord string\n\t\t\turi    string\n\t\t}{\n\t\t\t{\"test_1.cluster_1localhost\", \"mongodb+srv://localhost\"},\n\t\t\t{\"test_1.my_hostmongo.local\", \"mongodb+srv://mongo.local\"},\n\t\t\t{\"cluster.testmongodb.com\", \"mongodb+srv://blogs.mongodb.com\"},\n\t\t}\n\t\tfor _, c := range cases {\n\t\t\tc := c\n\t\t\tt.Run(c.uri, func(t *testing.T) {\n\t\t\t\tt.Parallel()\n\n\t\t\t\t_, err := newTestParser(c.record).parse(c.uri)\n\t\t\t\tassert.ErrorContains(t, err, \"domain suffix from SRV record not matched input domain\")\n\t\t\t})\n\t\t}\n\t})\n}\n"
  },
  {
    "path": "x/mongo/driver/crypt.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage driver\n\nimport (\n\t\"context\"\n\t\"crypto/tls\"\n\t\"fmt\"\n\t\"strings\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/mongocrypt\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/mongocrypt/options\"\n)\n\nconst (\n\tdefaultKmsPort    = 443\n\tdefaultKmsTimeout = 10 * time.Second\n)\n\n// CollectionInfoFn is a callback used to retrieve collection information.\ntype CollectionInfoFn func(ctx context.Context, db string, filter bsoncore.Document) (bsoncore.Document, error)\n\n// KeyRetrieverFn is a callback used to retrieve keys from the key vault.\ntype KeyRetrieverFn func(ctx context.Context, filter bsoncore.Document) ([]bsoncore.Document, error)\n\n// MarkCommandFn is a callback used to add encryption markings to a command.\ntype MarkCommandFn func(ctx context.Context, db string, cmd bsoncore.Document) (bsoncore.Document, error)\n\n// CryptOptions specifies options to configure a Crypt instance.\ntype CryptOptions struct {\n\tMongoCrypt           *mongocrypt.MongoCrypt\n\tCollInfoFn           CollectionInfoFn\n\tKeyFn                KeyRetrieverFn\n\tMarkFn               MarkCommandFn\n\tTLSConfig            map[string]*tls.Config\n\tBypassAutoEncryption bool\n\tBypassQueryAnalysis  bool\n}\n\n// Crypt is an interface implemented by types that can encrypt and decrypt instances of\n// bsoncore.Document.\n//\n// Users should rely on the driver's crypt type (used by default) for encryption and decryption\n// unless they are perfectly confident in another implementation of Crypt.\ntype Crypt interface {\n\t// Encrypt encrypts the given command.\n\tEncrypt(ctx context.Context, db string, cmd bsoncore.Document) (bsoncore.Document, error)\n\t// Decrypt decrypts the given command response.\n\tDecrypt(ctx context.Context, cmdResponse bsoncore.Document) (bsoncore.Document, error)\n\t// CreateDataKey creates a data key using the given KMS provider and options.\n\tCreateDataKey(ctx context.Context, kmsProvider string, opts *options.DataKeyOptions) (bsoncore.Document, error)\n\t// EncryptExplicit encrypts the given value with the given options.\n\tEncryptExplicit(ctx context.Context, val bsoncore.Value, opts *options.ExplicitEncryptionOptions) (byte, []byte, error)\n\t// EncryptExplicitExpression encrypts the given expression with the given options.\n\tEncryptExplicitExpression(ctx context.Context, val bsoncore.Document, opts *options.ExplicitEncryptionOptions) (bsoncore.Document, error)\n\t// DecryptExplicit decrypts the given encrypted value.\n\tDecryptExplicit(ctx context.Context, subtype byte, data []byte) (bsoncore.Value, error)\n\t// Close cleans up any resources associated with the Crypt instance.\n\tClose()\n\t// BypassAutoEncryption returns true if auto-encryption should be bypassed.\n\tBypassAutoEncryption() bool\n\t// RewrapDataKey attempts to rewrap the document data keys matching the filter, preparing the re-wrapped documents\n\t// to be returned as a slice of bsoncore.Document.\n\tRewrapDataKey(ctx context.Context, filter []byte, opts *options.RewrapManyDataKeyOptions) ([]bsoncore.Document, error)\n}\n\n// crypt consumes the libmongocrypt.MongoCrypt type to iterate the mongocrypt state machine and perform encryption\n// and decryption.\ntype crypt struct {\n\tmongoCrypt *mongocrypt.MongoCrypt\n\tcollInfoFn CollectionInfoFn\n\tkeyFn      KeyRetrieverFn\n\tmarkFn     MarkCommandFn\n\ttlsConfig  map[string]*tls.Config\n\n\tbypassAutoEncryption bool\n}\n\n// NewCrypt creates a new Crypt instance configured with the given AutoEncryptionOptions.\nfunc NewCrypt(opts *CryptOptions) Crypt {\n\tc := &crypt{\n\t\tmongoCrypt:           opts.MongoCrypt,\n\t\tcollInfoFn:           opts.CollInfoFn,\n\t\tkeyFn:                opts.KeyFn,\n\t\tmarkFn:               opts.MarkFn,\n\t\ttlsConfig:            opts.TLSConfig,\n\t\tbypassAutoEncryption: opts.BypassAutoEncryption,\n\t}\n\treturn c\n}\n\n// Encrypt encrypts the given command.\nfunc (c *crypt) Encrypt(ctx context.Context, db string, cmd bsoncore.Document) (bsoncore.Document, error) {\n\tif c.bypassAutoEncryption {\n\t\treturn cmd, nil\n\t}\n\n\tcryptCtx, err := c.mongoCrypt.CreateEncryptionContext(db, cmd)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer cryptCtx.Close()\n\n\treturn c.executeStateMachine(ctx, cryptCtx, db)\n}\n\n// Decrypt decrypts the given command response.\nfunc (c *crypt) Decrypt(ctx context.Context, cmdResponse bsoncore.Document) (bsoncore.Document, error) {\n\tcryptCtx, err := c.mongoCrypt.CreateDecryptionContext(cmdResponse)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer cryptCtx.Close()\n\n\treturn c.executeStateMachine(ctx, cryptCtx, \"\")\n}\n\n// CreateDataKey creates a data key using the given KMS provider and options.\nfunc (c *crypt) CreateDataKey(ctx context.Context, kmsProvider string, opts *options.DataKeyOptions) (bsoncore.Document, error) {\n\tcryptCtx, err := c.mongoCrypt.CreateDataKeyContext(kmsProvider, opts)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer cryptCtx.Close()\n\n\treturn c.executeStateMachine(ctx, cryptCtx, \"\")\n}\n\n// RewrapDataKey attempts to rewrap the document data keys matching the filter, preparing the re-wrapped documents to\n// be returned as a slice of bsoncore.Document.\nfunc (c *crypt) RewrapDataKey(ctx context.Context, filter []byte,\n\topts *options.RewrapManyDataKeyOptions,\n) ([]bsoncore.Document, error) {\n\tcryptCtx, err := c.mongoCrypt.RewrapDataKeyContext(filter, opts)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer cryptCtx.Close()\n\n\trewrappedBSON, err := c.executeStateMachine(ctx, cryptCtx, \"\")\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif rewrappedBSON == nil {\n\t\treturn nil, nil\n\t}\n\n\t// mongocrypt_ctx_rewrap_many_datakey_init wraps the documents in a BSON of the form { \"v\": [(BSON document), ...] }\n\t// where each BSON document in the slice is a document containing a rewrapped datakey.\n\trewrappedDocumentBytes, err := rewrappedBSON.LookupErr(\"v\")\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// Parse the resulting BSON as individual documents.\n\trewrappedDocsArray, ok := rewrappedDocumentBytes.ArrayOK()\n\tif !ok {\n\t\treturn nil, fmt.Errorf(\"expected results from mongocrypt_ctx_rewrap_many_datakey_init to be an array\")\n\t}\n\n\trewrappedDocumentValues, err := rewrappedDocsArray.Values()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\trewrappedDocuments := []bsoncore.Document{}\n\tfor _, rewrappedDocumentValue := range rewrappedDocumentValues {\n\t\tif rewrappedDocumentValue.Type != bsoncore.TypeEmbeddedDocument {\n\t\t\t// If a value in the document's array returned by mongocrypt is anything other than an embedded document,\n\t\t\t// then something is wrong and we should terminate the routine.\n\t\t\treturn nil, fmt.Errorf(\"expected value of type %q, got: %q\",\n\t\t\t\tbsoncore.TypeEmbeddedDocument.String(),\n\t\t\t\trewrappedDocumentValue.Type.String())\n\t\t}\n\t\trewrappedDocuments = append(rewrappedDocuments, rewrappedDocumentValue.Document())\n\t}\n\treturn rewrappedDocuments, nil\n}\n\n// EncryptExplicit encrypts the given value with the given options.\nfunc (c *crypt) EncryptExplicit(ctx context.Context, val bsoncore.Value, opts *options.ExplicitEncryptionOptions) (byte, []byte, error) {\n\tidx, doc := bsoncore.AppendDocumentStart(nil)\n\tdoc = bsoncore.AppendValueElement(doc, \"v\", val)\n\tdoc, _ = bsoncore.AppendDocumentEnd(doc, idx)\n\n\tcryptCtx, err := c.mongoCrypt.CreateExplicitEncryptionContext(doc, opts)\n\tif err != nil {\n\t\treturn 0, nil, err\n\t}\n\tdefer cryptCtx.Close()\n\n\tres, err := c.executeStateMachine(ctx, cryptCtx, \"\")\n\tif err != nil {\n\t\treturn 0, nil, err\n\t}\n\n\tsub, data := res.Lookup(\"v\").Binary()\n\treturn sub, data, nil\n}\n\n// EncryptExplicitExpression encrypts the given expression with the given options.\nfunc (c *crypt) EncryptExplicitExpression(ctx context.Context, expr bsoncore.Document, opts *options.ExplicitEncryptionOptions) (bsoncore.Document, error) {\n\tidx, doc := bsoncore.AppendDocumentStart(nil)\n\tdoc = bsoncore.AppendDocumentElement(doc, \"v\", expr)\n\tdoc, _ = bsoncore.AppendDocumentEnd(doc, idx)\n\n\tcryptCtx, err := c.mongoCrypt.CreateExplicitEncryptionExpressionContext(doc, opts)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer cryptCtx.Close()\n\n\tres, err := c.executeStateMachine(ctx, cryptCtx, \"\")\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tencryptedExpr := res.Lookup(\"v\").Document()\n\treturn encryptedExpr, nil\n}\n\n// DecryptExplicit decrypts the given encrypted value.\nfunc (c *crypt) DecryptExplicit(ctx context.Context, subtype byte, data []byte) (bsoncore.Value, error) {\n\tidx, doc := bsoncore.AppendDocumentStart(nil)\n\tdoc = bsoncore.AppendBinaryElement(doc, \"v\", subtype, data)\n\tdoc, _ = bsoncore.AppendDocumentEnd(doc, idx)\n\n\tcryptCtx, err := c.mongoCrypt.CreateExplicitDecryptionContext(doc)\n\tif err != nil {\n\t\treturn bsoncore.Value{}, err\n\t}\n\tdefer cryptCtx.Close()\n\n\tres, err := c.executeStateMachine(ctx, cryptCtx, \"\")\n\tif err != nil {\n\t\treturn bsoncore.Value{}, err\n\t}\n\n\treturn res.Lookup(\"v\"), nil\n}\n\n// Close cleans up any resources associated with the Crypt instance.\nfunc (c *crypt) Close() {\n\tc.mongoCrypt.Close()\n}\n\nfunc (c *crypt) BypassAutoEncryption() bool {\n\treturn c.bypassAutoEncryption\n}\n\nfunc (c *crypt) executeStateMachine(ctx context.Context, cryptCtx *mongocrypt.Context, db string) (bsoncore.Document, error) {\n\tvar err error\n\tfor {\n\t\tstate := cryptCtx.State()\n\t\tswitch state {\n\t\tcase mongocrypt.NeedMongoCollInfo:\n\t\t\terr = c.collectionInfo(ctx, cryptCtx, db)\n\t\tcase mongocrypt.NeedMongoMarkings:\n\t\t\terr = c.markCommand(ctx, cryptCtx, db)\n\t\tcase mongocrypt.NeedMongoKeys:\n\t\t\terr = c.retrieveKeys(ctx, cryptCtx)\n\t\tcase mongocrypt.NeedKms:\n\t\t\terr = c.decryptKeys(cryptCtx)\n\t\tcase mongocrypt.Ready:\n\t\t\treturn cryptCtx.Finish()\n\t\tcase mongocrypt.Done:\n\t\t\treturn nil, nil\n\t\tcase mongocrypt.NeedKmsCredentials:\n\t\t\terr = c.provideKmsProviders(ctx, cryptCtx)\n\t\tdefault:\n\t\t\treturn nil, fmt.Errorf(\"invalid Crypt state: %v\", state)\n\t\t}\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n}\n\nfunc (c *crypt) collectionInfo(ctx context.Context, cryptCtx *mongocrypt.Context, db string) error {\n\top, err := cryptCtx.NextOperation()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tcollInfo, err := c.collInfoFn(ctx, db, op)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif collInfo != nil {\n\t\tif err = cryptCtx.AddOperationResult(collInfo); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\treturn cryptCtx.CompleteOperation()\n}\n\nfunc (c *crypt) markCommand(ctx context.Context, cryptCtx *mongocrypt.Context, db string) error {\n\top, err := cryptCtx.NextOperation()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tmarkedCmd, err := c.markFn(ctx, db, op)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif err = cryptCtx.AddOperationResult(markedCmd); err != nil {\n\t\treturn err\n\t}\n\n\treturn cryptCtx.CompleteOperation()\n}\n\nfunc (c *crypt) retrieveKeys(ctx context.Context, cryptCtx *mongocrypt.Context) error {\n\top, err := cryptCtx.NextOperation()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tkeys, err := c.keyFn(ctx, op)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, key := range keys {\n\t\tif err = cryptCtx.AddOperationResult(key); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\treturn cryptCtx.CompleteOperation()\n}\n\nfunc (c *crypt) decryptKeys(cryptCtx *mongocrypt.Context) error {\n\tfor {\n\t\tkmsCtx := cryptCtx.NextKmsContext()\n\t\tif kmsCtx == nil {\n\t\t\tbreak\n\t\t}\n\n\t\tif err := c.decryptKey(kmsCtx); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\treturn cryptCtx.FinishKmsContexts()\n}\n\nfunc (c *crypt) decryptKey(kmsCtx *mongocrypt.KmsContext) error {\n\thost, err := kmsCtx.HostName()\n\tif err != nil {\n\t\treturn err\n\t}\n\tmsg, err := kmsCtx.Message()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// add a port to the address if it's not already present\n\taddr := host\n\tif idx := strings.IndexByte(host, ':'); idx == -1 {\n\t\taddr = fmt.Sprintf(\"%s:%d\", host, defaultKmsPort)\n\t}\n\n\tkmsProvider := kmsCtx.KMSProvider()\n\ttlsCfg := c.tlsConfig[kmsProvider]\n\tif tlsCfg == nil {\n\t\ttlsCfg = &tls.Config{MinVersion: tls.VersionTLS12}\n\t}\n\tconn, err := tls.Dial(\"tcp\", addr, tlsCfg)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer func() {\n\t\t_ = conn.Close()\n\t}()\n\n\tif err = conn.SetWriteDeadline(time.Now().Add(defaultKmsTimeout)); err != nil {\n\t\treturn err\n\t}\n\tif _, err = conn.Write(msg); err != nil {\n\t\treturn err\n\t}\n\n\tfor {\n\t\tbytesNeeded := kmsCtx.BytesNeeded()\n\t\tif bytesNeeded == 0 {\n\t\t\treturn nil\n\t\t}\n\n\t\tres := make([]byte, bytesNeeded)\n\t\tbytesRead, err := conn.Read(res)\n\t\tif err != nil {\n\t\t\treturn kmsCtx.RequestError()\n\t\t}\n\n\t\tif err = kmsCtx.FeedResponse(res[:bytesRead]); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n}\n\nfunc (c *crypt) provideKmsProviders(ctx context.Context, cryptCtx *mongocrypt.Context) error {\n\tkmsProviders, err := c.mongoCrypt.GetKmsProviders(ctx)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn cryptCtx.ProvideKmsProviders(kmsProviders)\n}\n"
  },
  {
    "path": "x/mongo/driver/description/server.go",
    "content": "// Copyright (C) MongoDB, Inc. 2024-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage description\n\nimport (\n\t\"fmt\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/address\"\n\t\"go.mongodb.org/mongo-driver/v2/tag\"\n)\n\n// ServerKind represents the type of a single server in a topology.\ntype ServerKind uint32\n\n// These constants are the possible types of servers.\nconst (\n\tServerKindStandalone   ServerKind = 1\n\tServerKindRSMember     ServerKind = 2\n\tServerKindRSPrimary    ServerKind = 4 + ServerKindRSMember\n\tServerKindRSSecondary  ServerKind = 8 + ServerKindRSMember\n\tServerKindRSArbiter    ServerKind = 16 + ServerKindRSMember\n\tServerKindRSGhost      ServerKind = 32 + ServerKindRSMember\n\tServerKindMongos       ServerKind = 256\n\tServerKindLoadBalancer ServerKind = 512\n)\n\n// UnknownStr represents an unknown server kind.\nconst UnknownStr = \"Unknown\"\n\n// String returns a stringified version of the kind or \"Unknown\" if the kind is\n// invalid.\nfunc (kind ServerKind) String() string {\n\tswitch kind {\n\tcase ServerKindStandalone:\n\t\treturn \"Standalone\"\n\tcase ServerKindRSMember:\n\t\treturn \"RSOther\"\n\tcase ServerKindRSPrimary:\n\t\treturn \"RSPrimary\"\n\tcase ServerKindRSSecondary:\n\t\treturn \"RSSecondary\"\n\tcase ServerKindRSArbiter:\n\t\treturn \"RSArbiter\"\n\tcase ServerKindRSGhost:\n\t\treturn \"RSGhost\"\n\tcase ServerKindMongos:\n\t\treturn \"Mongos\"\n\tcase ServerKindLoadBalancer:\n\t\treturn \"LoadBalancer\"\n\t}\n\n\treturn UnknownStr\n}\n\n// Unknown is an unknown server or topology kind.\nconst Unknown = 0\n\n// TopologyVersion represents a software version.\ntype TopologyVersion struct {\n\tProcessID bson.ObjectID\n\tCounter   int64\n}\n\n// VersionRange represents a range of versions.\ntype VersionRange struct {\n\tMin int32\n\tMax int32\n}\n\n// Server contains information about a node in a cluster. This is created from\n// hello command responses. If the value of the Kind field is LoadBalancer, only\n// the Addr and Kind fields will be set. All other fields will be set to the\n// zero value of the field's type.\ntype Server struct {\n\tAddr address.Address\n\n\tArbiters              []string\n\tAverageRTT            time.Duration\n\tAverageRTTSet         bool\n\tCompression           []string // compression methods returned by server\n\tCanonicalAddr         address.Address\n\tElectionID            bson.ObjectID\n\tHeartbeatInterval     time.Duration\n\tHelloOK               bool\n\tHosts                 []string\n\tIsCryptd              bool\n\tLastError             error\n\tLastUpdateTime        time.Time\n\tLastWriteTime         time.Time\n\tMaxBatchCount         uint32\n\tMaxDocumentSize       uint32\n\tMaxMessageSize        uint32\n\tMembers               []address.Address\n\tPassives              []string\n\tPassive               bool\n\tPrimary               address.Address\n\tReadOnly              bool\n\tServiceID             *bson.ObjectID // Only set for servers that are deployed behind a load balancer.\n\tSessionTimeoutMinutes *int64\n\tSetName               string\n\tSetVersion            uint32\n\tTags                  tag.Set\n\tTopologyVersion       *TopologyVersion\n\tKind                  ServerKind\n\tWireVersion           *VersionRange\n}\n\nfunc (s Server) String() string {\n\tstr := fmt.Sprintf(\"Addr: %s, Type: %s\", s.Addr, s.Kind)\n\tif len(s.Tags) != 0 {\n\t\tstr += fmt.Sprintf(\", Tag sets: %s\", s.Tags)\n\t}\n\n\tif s.AverageRTTSet {\n\t\tstr += fmt.Sprintf(\", Average RTT: %d\", s.AverageRTT)\n\t}\n\n\tif s.LastError != nil {\n\t\tstr += fmt.Sprintf(\", Last error: %s\", s.LastError)\n\t}\n\treturn str\n}\n\n// SelectedServer augments the Server type by also including the TopologyKind of\n// the topology that includes the server. This type should be used to track the\n// state of a server that was selected to perform an operation.\ntype SelectedServer struct {\n\tServer\n\tKind TopologyKind\n}\n\n// ServerSelector is an interface implemented by types that can perform server\n// selection given a topology description and list of candidate servers. The\n// selector should filter the provided candidates list and return a subset that\n// matches some criteria.\ntype ServerSelector interface {\n\tSelectServer(Topology, []Server) ([]Server, error)\n}\n"
  },
  {
    "path": "x/mongo/driver/description/topology.go",
    "content": "// Copyright (C) MongoDB, Inc. 2024-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage description\n\nimport \"fmt\"\n\n// TopologyKind represents a specific topology configuration.\ntype TopologyKind uint32\n\n// These constants are the available topology configurations.\nconst (\n\tTopologyKindSingle                TopologyKind = 1\n\tTopologyKindReplicaSet            TopologyKind = 2\n\tTopologyKindReplicaSetNoPrimary   TopologyKind = 4 + TopologyKindReplicaSet\n\tTopologyKindReplicaSetWithPrimary TopologyKind = 8 + TopologyKindReplicaSet\n\tTopologyKindSharded               TopologyKind = 256\n\tTopologyKindLoadBalanced          TopologyKind = 512\n)\n\n// Topology contains information about a MongoDB cluster.\ntype Topology struct {\n\tServers               []Server\n\tSetName               string\n\tKind                  TopologyKind\n\tSessionTimeoutMinutes *int64\n\tCompatibilityErr      error\n}\n\n// String implements the Stringer interface.\nfunc (t Topology) String() string {\n\tvar serversStr string\n\tfor _, s := range t.Servers {\n\t\tserversStr += \"{ \" + s.String() + \" }, \"\n\t}\n\treturn fmt.Sprintf(\"Type: %s, Servers: [%s]\", t.Kind, serversStr)\n}\n\n// String implements the fmt.Stringer interface.\nfunc (kind TopologyKind) String() string {\n\tswitch kind {\n\tcase TopologyKindSingle:\n\t\treturn \"Single\"\n\tcase TopologyKindReplicaSet:\n\t\treturn \"ReplicaSet\"\n\tcase TopologyKindReplicaSetNoPrimary:\n\t\treturn \"ReplicaSetNoPrimary\"\n\tcase TopologyKindReplicaSetWithPrimary:\n\t\treturn \"ReplicaSetWithPrimary\"\n\tcase TopologyKindSharded:\n\t\treturn \"Sharded\"\n\tcase TopologyKindLoadBalanced:\n\t\treturn \"LoadBalanced\"\n\t}\n\n\treturn \"Unknown\"\n}\n"
  },
  {
    "path": "x/mongo/driver/dns/dns.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\n// Package dns is intended for internal use only. It is made available to\n// facilitate use cases that require access to internal MongoDB driver\n// functionality and state. The API of this package is not stable and there is\n// no backward compatibility guarantee.\n//\n// WARNING: THIS PACKAGE IS EXPERIMENTAL AND MAY BE MODIFIED OR REMOVED WITHOUT\n// NOTICE! USE WITH EXTREME CAUTION!\npackage dns\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"net\"\n\t\"runtime\"\n\t\"strings\"\n)\n\n// Resolver resolves DNS records.\ntype Resolver struct {\n\t// Holds the functions to use for DNS lookups\n\tLookupSRV func(string, string, string) (string, []*net.SRV, error)\n\tLookupTXT func(string) ([]string, error)\n}\n\n// DefaultResolver is a Resolver that uses the default Resolver from the net package.\nvar DefaultResolver = &Resolver{net.LookupSRV, net.LookupTXT}\n\n// ParseHosts uses the srv string and service name to get the hosts.\nfunc (r *Resolver) ParseHosts(host string, srvName string, stopOnErr bool) ([]string, error) {\n\tparsedHosts := strings.Split(host, \",\")\n\n\tif len(parsedHosts) != 1 {\n\t\treturn nil, fmt.Errorf(\"URI with SRV must include one and only one hostname\")\n\t}\n\treturn r.fetchSeedlistFromSRV(parsedHosts[0], srvName, stopOnErr)\n}\n\n// GetConnectionArgsFromTXT gets the TXT record associated with the host and returns the connection arguments.\nfunc (r *Resolver) GetConnectionArgsFromTXT(host string) ([]string, error) {\n\tvar connectionArgsFromTXT []string\n\n\t// error ignored because not finding a TXT record should not be\n\t// considered an error.\n\trecordsFromTXT, _ := r.LookupTXT(host)\n\n\t// This is a temporary fix to get around bug https://github.com/golang/go/issues/21472.\n\t// It will currently incorrectly concatenate multiple TXT records to one\n\t// on windows.\n\tif runtime.GOOS == \"windows\" {\n\t\trecordsFromTXT = []string{strings.Join(recordsFromTXT, \"\")}\n\t}\n\n\tif len(recordsFromTXT) > 1 {\n\t\treturn nil, errors.New(\"multiple records from TXT not supported\")\n\t}\n\tif len(recordsFromTXT) > 0 {\n\t\tconnectionArgsFromTXT = strings.FieldsFunc(recordsFromTXT[0], func(r rune) bool { return r == ';' || r == '&' })\n\n\t\terr := validateTXTResult(connectionArgsFromTXT)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\treturn connectionArgsFromTXT, nil\n}\n\nfunc (r *Resolver) fetchSeedlistFromSRV(host string, srvName string, stopOnErr bool) ([]string, error) {\n\tvar err error\n\n\t_, _, err = net.SplitHostPort(host)\n\n\tif err == nil {\n\t\t// we were able to successfully extract a port from the host,\n\t\t// but should not be able to when using SRV\n\t\treturn nil, fmt.Errorf(\"URI with srv must not include a port number\")\n\t}\n\n\t// default to \"mongodb\" as service name if not supplied\n\tif srvName == \"\" {\n\t\tsrvName = \"mongodb\"\n\t}\n\t_, addresses, err := r.LookupSRV(srvName, \"tcp\", host)\n\tif err != nil && strings.Contains(err.Error(), \"cannot unmarshal DNS message\") {\n\t\treturn nil, fmt.Errorf(\"see https://pkg.go.dev/go.mongodb.org/mongo-driver/mongo#hdr-Potential_DNS_Issues: %w\", err)\n\t} else if err != nil {\n\t\treturn nil, err\n\t}\n\n\ttrimmedHost := strings.TrimSuffix(host, \".\")\n\n\tparsedHosts := make([]string, 0, len(addresses))\n\tfor _, address := range addresses {\n\t\ttrimmedAddressTarget := strings.TrimSuffix(address.Target, \".\")\n\t\terr := validateSRVResult(trimmedAddressTarget, trimmedHost)\n\t\tif err != nil {\n\t\t\tif stopOnErr {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\t\tparsedHosts = append(parsedHosts, fmt.Sprintf(\"%s:%d\", trimmedAddressTarget, address.Port))\n\t}\n\treturn parsedHosts, nil\n}\n\nfunc validateSRVResult(recordFromSRV, inputHostName string) error {\n\tseparatedInputDomain := strings.Split(strings.ToLower(inputHostName), \".\")\n\tseparatedRecord := strings.Split(strings.ToLower(recordFromSRV), \".\")\n\tif l := len(separatedInputDomain); l < 3 && len(separatedRecord) <= l {\n\t\treturn fmt.Errorf(\"server record (%d levels) should have more domain levels than parent URI (%d levels)\", l, len(separatedRecord))\n\t}\n\tif len(separatedRecord) < len(separatedInputDomain) {\n\t\treturn errors.New(\"domain suffix from SRV record not matched input domain\")\n\t}\n\n\tinputDomainSuffix := separatedInputDomain\n\tif len(inputDomainSuffix) > 2 {\n\t\tinputDomainSuffix = inputDomainSuffix[1:]\n\t}\n\tdomainSuffixOffset := len(separatedRecord) - len(inputDomainSuffix)\n\n\trecordDomainSuffix := separatedRecord[domainSuffixOffset:]\n\tfor ix, label := range inputDomainSuffix {\n\t\tif label != recordDomainSuffix[ix] {\n\t\t\treturn errors.New(\"domain suffix from SRV record not matched input domain\")\n\t\t}\n\t}\n\treturn nil\n}\n\nvar allowedTXTOptions = map[string]struct{}{\n\t\"authsource\":   {},\n\t\"replicaset\":   {},\n\t\"loadbalanced\": {},\n}\n\nfunc validateTXTResult(paramsFromTXT []string) error {\n\tfor _, param := range paramsFromTXT {\n\t\tkv := strings.SplitN(param, \"=\", 2)\n\t\tif len(kv) != 2 {\n\t\t\treturn errors.New(\"invalid TXT record\")\n\t\t}\n\t\tkey := strings.ToLower(kv[0])\n\t\tif _, ok := allowedTXTOptions[key]; !ok {\n\t\t\treturn fmt.Errorf(\"cannot specify option '%s' in TXT record\", kv[0])\n\t\t}\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "x/mongo/driver/driver.go",
    "content": "// Copyright (C) MongoDB, Inc. 2022-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\n// Package driver is intended for internal use only. It is made available to\n// facilitate use cases that require access to internal MongoDB driver\n// functionality and state. The API of this package is not stable and there is\n// no backward compatibility guarantee.\n//\n// WARNING: THIS PACKAGE IS EXPERIMENTAL AND MAY BE MODIFIED OR REMOVED WITHOUT\n// NOTICE! USE WITH EXTREME CAUTION!\npackage driver\n\nimport (\n\t\"context\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/csot\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/address\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/description\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/mnet\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/session\"\n)\n\n// AuthConfig holds the information necessary to perform an authentication attempt.\n// this was moved from the auth package to avoid a circular dependency. The auth package\n// reexports this under the old name to avoid breaking the public api.\ntype AuthConfig struct {\n\tDescription   description.Server\n\tConnection    *mnet.Connection\n\tClusterClock  *session.ClusterClock\n\tHandshakeInfo HandshakeInformation\n\tServerAPI     *ServerAPIOptions\n}\n\n// OIDCCallback is the type for both Human and Machine Callback flows. RefreshToken will always be\n// nil in the OIDCArgs for the Machine flow.\ntype OIDCCallback func(context.Context, *OIDCArgs) (*OIDCCredential, error)\n\n// OIDCArgs contains the arguments for the OIDC callback.\ntype OIDCArgs struct {\n\tVersion      int\n\tIDPInfo      *IDPInfo\n\tRefreshToken *string\n}\n\n// OIDCCredential contains the access token and refresh token.\ntype OIDCCredential struct {\n\tAccessToken  string\n\tExpiresAt    *time.Time\n\tRefreshToken *string\n}\n\n// IDPInfo contains the information needed to perform OIDC authentication with an Identity Provider.\ntype IDPInfo struct {\n\tIssuer        string   `bson:\"issuer\"`\n\tClientID      string   `bson:\"clientId\"`\n\tRequestScopes []string `bson:\"requestScopes\"`\n}\n\n// Authenticator handles authenticating a connection. The implementers of this interface\n// are all in the auth package. Most authentication mechanisms do not allow for Reauth,\n// but this is included in the interface so that whenever a new mechanism is added, it\n// must be explicitly considered.\ntype Authenticator interface {\n\t// Auth authenticates the connection.\n\tAuth(context.Context, *AuthConfig) error\n\tReauth(context.Context, *AuthConfig) error\n}\n\n// Cred is a user's credential.\ntype Cred struct {\n\tSource              string\n\tUsername            string\n\tPassword            string\n\tPasswordSet         bool\n\tProps               map[string]string\n\tOIDCMachineCallback OIDCCallback\n\tOIDCHumanCallback   OIDCCallback\n}\n\n// Deployment is implemented by types that can select a server from a deployment.\ntype Deployment interface {\n\tSelectServer(context.Context, description.ServerSelector) (Server, error)\n\tKind() description.TopologyKind\n\n\t// GetServerSelectionTimeout returns a timeout that should be used to set a\n\t// deadline for server selection. This logic is not handleded internally by\n\t// the ServerSelector, as a resulting deadline may be applicable by follow-up\n\t// operations such as checking out a connection.\n\tGetServerSelectionTimeout() time.Duration\n}\n\n// Connector represents a type that can connect to a server.\ntype Connector interface {\n\tConnect() error\n}\n\n// Disconnector represents a type that can disconnect from a server.\ntype Disconnector interface {\n\tDisconnect(context.Context) error\n}\n\n// Subscription represents a subscription to topology updates. A subscriber can receive updates through the\n// Updates field.\ntype Subscription struct {\n\tUpdates <-chan description.Topology\n\tID      uint64\n}\n\n// Subscriber represents a type to which another type can subscribe. A subscription contains a channel that\n// is updated with topology descriptions.\ntype Subscriber interface {\n\tSubscribe() (*Subscription, error)\n\tUnsubscribe(*Subscription) error\n}\n\n// Server represents a MongoDB server. Implementations should pool connections and handle the\n// retrieving and returning of connections.\ntype Server interface {\n\tConnection(context.Context) (*mnet.Connection, error)\n\n\t// RTTMonitor returns the round-trip time monitor associated with this server.\n\tRTTMonitor() RTTMonitor\n}\n\n// RTTMonitor represents a round-trip-time monitor.\ntype RTTMonitor interface {\n\t// EWMA returns the exponentially weighted moving average observed round-trip time.\n\tEWMA() time.Duration\n\n\t// Min returns the minimum observed round-trip time over the window period.\n\tMin() time.Duration\n\n\t// Stats returns stringified stats of the current state of the monitor.\n\tStats() string\n}\n\nvar _ RTTMonitor = &csot.ZeroRTTMonitor{}\n\n// LocalAddresser is a type that is able to supply its local address\ntype LocalAddresser interface {\n\tLocalAddress() address.Address\n}\n\n// Expirable represents an expirable object.\ntype Expirable interface {\n\tExpire() error\n\tAlive() bool\n}\n\n// ProcessErrorResult represents the result of a ErrorProcessor.ProcessError() call. Exact values for this type can be\n// checked directly (e.g. res == ServerMarkedUnknown), but it is recommended that applications use the ServerChanged()\n// function instead.\ntype ProcessErrorResult int\n\nconst (\n\t// NoChange indicates that the error did not affect the state of the server.\n\tNoChange ProcessErrorResult = iota\n\t// ServerMarkedUnknown indicates that the error only resulted in the server being marked as Unknown.\n\tServerMarkedUnknown\n\t// ConnectionPoolCleared indicates that the error resulted in the server being marked as Unknown and its connection\n\t// pool being cleared.\n\tConnectionPoolCleared\n)\n\n// ErrorProcessor implementations can handle processing errors, which may modify their internal state.\n// If this type is implemented by a Server, then Operation.Execute will call it's ProcessError\n// method after it decodes a wire message.\ntype ErrorProcessor interface {\n\tProcessError(err error, desc mnet.Describer) ProcessErrorResult\n}\n\n// HandshakeInformation contains information extracted from a MongoDB connection handshake. This is a helper type that\n// augments description.Server by also tracking server connection ID and authentication-related fields. We use this type\n// rather than adding authentication-related fields to description.Server to avoid retaining sensitive information in a\n// user-facing type. The server connection ID is stored in this type because unlike description.Server, all handshakes are\n// correlated with a single network connection.\ntype HandshakeInformation struct {\n\tDescription             description.Server\n\tSpeculativeAuthenticate bsoncore.Document\n\tServerConnectionID      *int64\n\tSaslSupportedMechs      []string\n}\n\n// Handshaker is the interface implemented by types that can perform a MongoDB\n// handshake over a provided driver.Connection. This is used during connection\n// initialization. Implementations must be goroutine safe.\ntype Handshaker interface {\n\tGetHandshakeInformation(context.Context, address.Address, *mnet.Connection) (HandshakeInformation, error)\n\tFinishHandshake(context.Context, *mnet.Connection) error\n}\n\n// SingleServerDeployment is an implementation of Deployment that always returns a single server.\ntype SingleServerDeployment struct{ Server }\n\nvar _ Deployment = SingleServerDeployment{}\n\n// SelectServer implements the Deployment interface. This method does not use the\n// description.SelectedServer provided and instead returns the embedded Server.\nfunc (ssd SingleServerDeployment) SelectServer(context.Context, description.ServerSelector) (Server, error) {\n\treturn ssd.Server, nil\n}\n\n// Kind implements the Deployment interface. It always returns description.TopologyKindSingle.\nfunc (SingleServerDeployment) Kind() description.TopologyKind { return description.TopologyKindSingle }\n\n// GetServerSelectionTimeout returns zero as a server selection timeout is not\n// applicable for single server deployments.\nfunc (SingleServerDeployment) GetServerSelectionTimeout() time.Duration {\n\treturn 0\n}\n\n// SingleConnectionDeployment is an implementation of Deployment that always returns the same Connection. This\n// implementation should only be used for connection handshakes and server heartbeats as it does not implement\n// ErrorProcessor, which is necessary for application operations.\ntype SingleConnectionDeployment struct{ C *mnet.Connection }\n\nvar (\n\t_ Deployment = SingleConnectionDeployment{}\n\t_ Server     = SingleConnectionDeployment{}\n)\n\n// SelectServer implements the Deployment interface. This method does not use the\n// description.SelectedServer provided and instead returns itself. The Connections returned from the\n// Connection method have a no-op Close method.\nfunc (scd SingleConnectionDeployment) SelectServer(context.Context, description.ServerSelector) (Server, error) {\n\treturn scd, nil\n}\n\n// GetServerSelectionTimeout returns zero as a server selection timeout is not\n// applicable for single connection deployment.\nfunc (SingleConnectionDeployment) GetServerSelectionTimeout() time.Duration {\n\treturn 0\n}\n\n// Kind implements the Deployment interface. It always returns description.TopologyKindSingle.\nfunc (SingleConnectionDeployment) Kind() description.TopologyKind {\n\treturn description.TopologyKindSingle\n}\n\n// Connection implements the Server interface. It always returns the embedded connection.\nfunc (scd SingleConnectionDeployment) Connection(context.Context) (*mnet.Connection, error) {\n\treturn scd.C, nil\n}\n\n// RTTMonitor implements the driver.Server interface.\nfunc (scd SingleConnectionDeployment) RTTMonitor() RTTMonitor {\n\treturn &csot.ZeroRTTMonitor{}\n}\n\n// TODO(GODRIVER-617): We can likely use 1 type for both the Type and the RetryMode by using 2 bits for the mode and 1\n// TODO bit for the type. Although in the practical sense, we might not want to do that since the type of retryability\n// TODO is tied to the operation itself and isn't going change, e.g. and insert operation will always be a write,\n// TODO however some operations are both reads and  writes, for instance aggregate is a read but with a $out parameter\n// TODO it's a write.\n\n// Type specifies whether an operation is a read, write, or unknown.\ntype Type uint\n\n// THese are the availables types of Type.\nconst (\n\t_ Type = iota\n\tWrite\n\tRead\n)\n\n// RetryMode specifies the way that retries are handled for retryable operations.\ntype RetryMode uint\n\n// These are the modes available for retrying. Note that if Timeout is specified on the Client, the\n// operation will automatically retry as many times as possible within the context's deadline\n// unless RetryNone is used.\nconst (\n\t// RetryNone disables retrying.\n\tRetryNone RetryMode = iota\n\t// RetryOnce will enable retrying the entire operation once if Timeout is not specified.\n\tRetryOnce\n\t// RetryOncePerCommand will enable retrying each command associated with an operation if Timeout\n\t// is not specified. For example, if an insert is batch split into 4 commands then each of\n\t// those commands is eligible for one retry.\n\tRetryOncePerCommand\n\t// RetryContext will enable retrying until the context.Context's deadline is exceeded or it is\n\t// cancelled.\n\tRetryContext\n)\n\n// Enabled returns if this RetryMode enables retrying.\nfunc (rm RetryMode) Enabled() bool {\n\treturn rm == RetryOnce || rm == RetryOncePerCommand || rm == RetryContext\n}\n"
  },
  {
    "path": "x/mongo/driver/drivertest/channel_conn.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage drivertest\n\nimport (\n\t\"context\"\n\t\"errors\"\n\n\t\"go.mongodb.org/mongo-driver/v2/mongo/address\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/description\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/wiremessage\"\n)\n\n// ChannelConn implements the driver.Connection interface by reading and writing wire messages\n// to a channel\ntype ChannelConn struct {\n\tWriteErr error\n\tWritten  chan []byte\n\tReadResp chan []byte\n\tReadErr  chan error\n\tDesc     description.Server\n}\n\n// OIDCTokenGenID implements the driver.Connection interface by returning the OIDCToken generation\n// (which is always 0)\nfunc (c *ChannelConn) OIDCTokenGenID() uint64 {\n\treturn 0\n}\n\n// SetOIDCTokenGenID implements the driver.Connection interface by setting the OIDCToken generation\n// (which is always 0)\nfunc (c *ChannelConn) SetOIDCTokenGenID(uint64) {}\n\n// Write implements the driver.Connection interface.\nfunc (c *ChannelConn) Write(ctx context.Context, wm []byte) error {\n\t// Copy wm in case it came from a buffer pool.\n\tb := make([]byte, len(wm))\n\tcopy(b, wm)\n\tselect {\n\tcase c.Written <- b:\n\tcase <-ctx.Done():\n\t\treturn ctx.Err()\n\tdefault:\n\t\tc.WriteErr = errors.New(\"could not write wiremessage to written channel\")\n\t}\n\treturn c.WriteErr\n}\n\n// Read implements the driver.Connection interface.\nfunc (c *ChannelConn) Read(ctx context.Context) ([]byte, error) {\n\tvar wm []byte\n\tvar err error\n\tselect {\n\tcase wm = <-c.ReadResp:\n\tcase err = <-c.ReadErr:\n\tcase <-ctx.Done():\n\t\terr = ctx.Err()\n\t}\n\treturn wm, err\n}\n\n// Description implements the driver.Connection interface.\nfunc (c *ChannelConn) Description() description.Server { return c.Desc }\n\n// Close implements the driver.Connection interface.\nfunc (c *ChannelConn) Close() error {\n\treturn nil\n}\n\n// ID implements the driver.Connection interface.\nfunc (c *ChannelConn) ID() string {\n\treturn \"faked\"\n}\n\n// DriverConnectionID implements the driver.Connection interface.\nfunc (c *ChannelConn) DriverConnectionID() int64 {\n\treturn 0\n}\n\n// ServerConnectionID implements the driver.Connection interface.\nfunc (c *ChannelConn) ServerConnectionID() *int64 {\n\tserverConnectionID := int64(42)\n\treturn &serverConnectionID\n}\n\n// Address implements the driver.Connection interface.\nfunc (c *ChannelConn) Address() address.Address { return address.Address(\"0.0.0.0\") }\n\n// Stale implements the driver.Connection interface.\nfunc (c *ChannelConn) Stale() bool {\n\treturn false\n}\n\n// MakeReply creates an OP_REPLY wiremessage from a BSON document\nfunc MakeReply(doc bsoncore.Document) []byte {\n\tvar dst []byte\n\tidx, dst := wiremessage.AppendHeaderStart(dst, 10, 9, wiremessage.OpReply)\n\tdst = wiremessage.AppendReplyFlags(dst, 0)\n\tdst = wiremessage.AppendReplyCursorID(dst, 0)\n\tdst = wiremessage.AppendReplyStartingFrom(dst, 0)\n\tdst = wiremessage.AppendReplyNumberReturned(dst, 1)\n\tdst = append(dst, doc...)\n\treturn bsoncore.UpdateLength(dst, idx, int32(len(dst[idx:])))\n}\n\n// GetCommandFromQueryWireMessage returns the command sent in an OP_QUERY wire message.\nfunc GetCommandFromQueryWireMessage(wm []byte) (bsoncore.Document, error) {\n\tvar ok bool\n\t_, _, _, _, wm, ok = wiremessage.ReadHeader(wm)\n\tif !ok {\n\t\treturn nil, errors.New(\"could not read header\")\n\t}\n\t_, wm, ok = wiremessage.ReadQueryFlags(wm)\n\tif !ok {\n\t\treturn nil, errors.New(\"could not read flags\")\n\t}\n\t_, wm, ok = wiremessage.ReadQueryFullCollectionName(wm)\n\tif !ok {\n\t\treturn nil, errors.New(\"could not read fullCollectionName\")\n\t}\n\t_, wm, ok = wiremessage.ReadQueryNumberToSkip(wm)\n\tif !ok {\n\t\treturn nil, errors.New(\"could not read numberToSkip\")\n\t}\n\t_, wm, ok = wiremessage.ReadQueryNumberToReturn(wm)\n\tif !ok {\n\t\treturn nil, errors.New(\"could not read numberToReturn\")\n\t}\n\n\tvar query bsoncore.Document\n\tquery, _, ok = wiremessage.ReadQueryQuery(wm)\n\tif !ok {\n\t\treturn nil, errors.New(\"could not read query\")\n\t}\n\treturn query, nil\n}\n\n// GetCommandFromMsgWireMessage returns the command document sent in an OP_MSG wire message.\nfunc GetCommandFromMsgWireMessage(wm []byte) (bsoncore.Document, error) {\n\tvar ok bool\n\t_, _, _, _, wm, ok = wiremessage.ReadHeader(wm)\n\tif !ok {\n\t\treturn nil, errors.New(\"could not read header\")\n\t}\n\n\t_, wm, ok = wiremessage.ReadMsgFlags(wm)\n\tif !ok {\n\t\treturn nil, errors.New(\"could not read flags\")\n\t}\n\t_, wm, ok = wiremessage.ReadMsgSectionType(wm)\n\tif !ok {\n\t\treturn nil, errors.New(\"could not read section type\")\n\t}\n\n\tcmdDoc, _, ok := wiremessage.ReadMsgSectionSingleDocument(wm)\n\tif !ok {\n\t\treturn nil, errors.New(\"could not read command document\")\n\t}\n\treturn cmdDoc, nil\n}\n"
  },
  {
    "path": "x/mongo/driver/drivertest/channel_netconn.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage drivertest\n\nimport (\n\t\"errors\"\n\t\"net\"\n\t\"time\"\n)\n\n// ChannelNetConn implements the net.Conn interface by reading and writing wire messages to a channel.\ntype ChannelNetConn struct {\n\tWriteErr error\n\tWritten  chan []byte\n\tReadResp chan []byte\n\tReadErr  chan error\n}\n\n// Read reads data from the connection\nfunc (c *ChannelNetConn) Read(b []byte) (int, error) {\n\tvar wm []byte\n\tvar err error\n\tselect {\n\tcase wm = <-c.ReadResp:\n\tcase err = <-c.ReadErr:\n\t}\n\treturn copy(b, wm), err\n}\n\n// Write writes data to the connection.\nfunc (c *ChannelNetConn) Write(b []byte) (int, error) {\n\tcopyBuf := make([]byte, len(b))\n\tcopy(copyBuf, b)\n\n\tselect {\n\tcase c.Written <- copyBuf:\n\tdefault:\n\t\tc.WriteErr = errors.New(\"could not write wm to Written channel\")\n\t}\n\treturn len(b), c.WriteErr\n}\n\n// Close closes the connection.\nfunc (c *ChannelNetConn) Close() error {\n\treturn nil\n}\n\n// LocalAddr returns the local network address.\nfunc (c *ChannelNetConn) LocalAddr() net.Addr {\n\treturn nil\n}\n\n// RemoteAddr returns the remote network address.\nfunc (c *ChannelNetConn) RemoteAddr() net.Addr {\n\treturn nil\n}\n\n// SetDeadline sets the read and write deadlines associated with the connection.\nfunc (c *ChannelNetConn) SetDeadline(_ time.Time) error {\n\treturn nil\n}\n\n// SetReadDeadline sets the read and write deadlines associated with the connection.\nfunc (c *ChannelNetConn) SetReadDeadline(_ time.Time) error {\n\treturn nil\n}\n\n// SetWriteDeadline sets the read and write deadlines associated with the connection.\nfunc (c *ChannelNetConn) SetWriteDeadline(_ time.Time) error {\n\treturn nil\n}\n\n// GetWrittenMessage gets the last wire message written to the connection\nfunc (c *ChannelNetConn) GetWrittenMessage() []byte {\n\tselect {\n\tcase wm := <-c.Written:\n\t\treturn wm\n\tdefault:\n\t\treturn nil\n\t}\n}\n\n// AddResponse adds a response to the connection.\nfunc (c *ChannelNetConn) AddResponse(resp []byte) error {\n\tselect {\n\tcase c.ReadResp <- resp[:4]:\n\tdefault:\n\t\treturn errors.New(\"could not write length bytes\")\n\t}\n\n\tselect {\n\tcase c.ReadResp <- resp[4:]:\n\tdefault:\n\t\treturn errors.New(\"could not write response bytes\")\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "x/mongo/driver/drivertest/doc.go",
    "content": "// Copyright (C) MongoDB, Inc. 2024-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\n// Package drivertest is intended for internal use only. It is made available to\n// facilitate use cases that require access to internal MongoDB driver\n// functionality and state. The API of this package is not stable and there is\n// no backward compatibility guarantee.\n//\n// WARNING: THIS PACKAGE IS EXPERIMENTAL AND MAY BE MODIFIED OR REMOVED WITHOUT\n// NOTICE! USE WITH EXTREME CAUTION!\npackage drivertest\n"
  },
  {
    "path": "x/mongo/driver/drivertest/opmsg_deployment.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage drivertest\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/csot\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/driverutil\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/address\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/description\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/mnet\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/wiremessage\"\n)\n\nconst (\n\tserverAddress          = address.Address(\"127.0.0.1:27017\")\n\tmaxDocumentSize uint32 = 16777216\n\tmaxMessageSize  uint32 = 48000000\n\tmaxBatchCount   uint32 = 100000\n)\n\nvar (\n\tsessionTimeoutMinutes int64 = 30\n\n\t// MockDescription is the server description used for the mock deployment. Each mocked connection returns this\n\t// value from its Description method.\n\tMockDescription = description.Server{\n\t\tCanonicalAddr:         serverAddress,\n\t\tMaxDocumentSize:       maxDocumentSize,\n\t\tMaxMessageSize:        maxMessageSize,\n\t\tMaxBatchCount:         maxBatchCount,\n\t\tSessionTimeoutMinutes: &sessionTimeoutMinutes,\n\t\tKind:                  description.ServerKindRSPrimary,\n\t\tWireVersion: &description.VersionRange{\n\t\t\tMax: driverutil.MaxWireVersion,\n\t\t},\n\t}\n\n\t// ErrNoResponsesRemaining is the error returned when the mocked connection is asked for a response and does not have\n\t// any responses buffered.\n\tErrNoResponsesRemaining = errors.New(\"no responses remaining\")\n)\n\n// connection implements the driver.Connection interface and responds to wire messages with pre-configured responses.\ntype connection struct {\n\tresponses []bson.D // responses to send when ReadWireMessage is called\n}\n\nvar (\n\t_ mnet.ReadWriteCloser = &connection{}\n\t_ mnet.Describer       = &connection{}\n)\n\n// Write is a no-op.\nfunc (c *connection) Write(context.Context, []byte) error {\n\treturn nil\n}\n\nfunc (c *connection) OIDCTokenGenID() uint64 {\n\treturn 0\n}\n\nfunc (c *connection) SetOIDCTokenGenID(uint64) {\n}\n\n// Read returns the next response in the connection's list of responses.\nfunc (c *connection) Read(_ context.Context) ([]byte, error) {\n\tvar dst []byte\n\tif len(c.responses) == 0 {\n\t\treturn dst, ErrNoResponsesRemaining\n\t}\n\tnextRes := c.responses[0]\n\tc.responses = c.responses[1:]\n\n\tvar wmindex int32\n\twmindex, dst = wiremessage.AppendHeaderStart(dst, wiremessage.NextRequestID(), 0, wiremessage.OpMsg)\n\tdst = wiremessage.AppendMsgFlags(dst, 0)\n\tdst = wiremessage.AppendMsgSectionType(dst, wiremessage.SingleDocument)\n\tresBytes, _ := bson.Marshal(nextRes)\n\tdst = append(dst, resBytes...)\n\tdst = bsoncore.UpdateLength(dst, wmindex, int32(len(dst[wmindex:])))\n\treturn dst, nil\n}\n\n// Description returns a fixed server description for the connection.\nfunc (c *connection) Description() description.Server {\n\treturn MockDescription\n}\n\n// Close is a no-op operation.\nfunc (*connection) Close() error {\n\treturn nil\n}\n\n// ID returns a fixed identifier for the connection.\nfunc (*connection) ID() string {\n\treturn \"<mock_connection>\"\n}\n\n// DriverConnectionID returns a fixed identifier for the driver pool connection.\nfunc (*connection) DriverConnectionID() int64 {\n\treturn 0\n}\n\n// ServerConnectionID returns a fixed identifier for the server connection.\nfunc (*connection) ServerConnectionID() *int64 {\n\tserverConnectionID := int64(42)\n\treturn &serverConnectionID\n}\n\n// Address returns a fixed address for the connection.\nfunc (*connection) Address() address.Address {\n\treturn serverAddress\n}\n\n// Stale returns if the connection is stale.\nfunc (*connection) Stale() bool {\n\treturn false\n}\n\n// MockDeployment wraps a connection and implements the driver.Deployment interface.\ntype MockDeployment struct {\n\tconn    *connection\n\tupdates chan description.Topology\n}\n\nvar (\n\t_ driver.Deployment   = &MockDeployment{}\n\t_ driver.Server       = &MockDeployment{}\n\t_ driver.Connector    = &MockDeployment{}\n\t_ driver.Disconnector = &MockDeployment{}\n\t_ driver.Subscriber   = &MockDeployment{}\n)\n\n// SelectServer implements the Deployment interface. This method does not use the\n// description.SelectedServer provided and instead returns itself. The Connections returned from the\n// Connection method have a no-op Close method.\nfunc (md *MockDeployment) SelectServer(context.Context, description.ServerSelector) (driver.Server, error) {\n\treturn md, nil\n}\n\n// GetServerSelectionTimeout returns zero as a server selection timeout is not\n// applicable for mock deployments.\nfunc (*MockDeployment) GetServerSelectionTimeout() time.Duration {\n\treturn 0\n}\n\n// Kind implements the Deployment interface. It always returns description.TopologyKindSingle.\nfunc (md *MockDeployment) Kind() description.TopologyKind {\n\treturn description.TopologyKindSingle\n}\n\n// Connection implements the driver.Server interface.\nfunc (md *MockDeployment) Connection(context.Context) (*mnet.Connection, error) {\n\treturn mnet.NewConnection(md.conn), nil\n}\n\n// RTTMonitor implements the driver.Server interface.\nfunc (md *MockDeployment) RTTMonitor() driver.RTTMonitor {\n\treturn &csot.ZeroRTTMonitor{}\n}\n\n// Connect is a no-op method which implements the driver.Connector interface.\nfunc (md *MockDeployment) Connect() error {\n\treturn nil\n}\n\n// Disconnect is a no-op method which implements the driver.Disconnector interface {\nfunc (md *MockDeployment) Disconnect(context.Context) error {\n\tclose(md.updates)\n\treturn nil\n}\n\n// Subscribe returns a subscription from which new topology descriptions can be retrieved.\n// Subscribe implements the driver.Subscriber interface.\nfunc (md *MockDeployment) Subscribe() (*driver.Subscription, error) {\n\tif md.updates == nil {\n\t\tmd.updates = make(chan description.Topology, 1)\n\n\t\tmd.updates <- description.Topology{\n\t\t\tSessionTimeoutMinutes: &sessionTimeoutMinutes,\n\t\t}\n\t}\n\n\treturn &driver.Subscription{\n\t\tUpdates: md.updates,\n\t}, nil\n}\n\n// Unsubscribe is a no-op method which implements the driver.Subscriber interface.\nfunc (md *MockDeployment) Unsubscribe(*driver.Subscription) error {\n\treturn nil\n}\n\n// AddResponses adds responses to this mock deployment.\nfunc (md *MockDeployment) AddResponses(responses ...bson.D) {\n\tmd.conn.responses = append(md.conn.responses, responses...)\n}\n\n// ClearResponses clears all remaining responses in this mock deployment.\nfunc (md *MockDeployment) ClearResponses() {\n\tmd.conn.responses = md.conn.responses[:0]\n}\n\n// NewMockDeployment returns a mock driver.Deployment that responds with OP_MSG wire messages.\n// However, for most use cases, we suggest testing with an actual database.\nfunc NewMockDeployment(responses ...bson.D) *MockDeployment {\n\treturn &MockDeployment{\n\t\tconn: &connection{\n\t\t\tresponses: responses,\n\t\t},\n\t}\n}\n"
  },
  {
    "path": "x/mongo/driver/drivertest/opmsg_deployment_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage drivertest\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/require\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n)\n\nfunc TestOPMSGMockDeployment(t *testing.T) {\n\tmd := NewMockDeployment()\n\n\topts := options.Client()\n\topts.Deployment = md\n\n\tclient, err := mongo.Connect(opts)\n\n\tt.Run(\"NewMockDeployment connect to client\", func(t *testing.T) {\n\t\trequire.NoError(t, err, \"unexpected error from connect to mockdeployment\")\n\t})\n\tt.Run(\"AddResponses with one\", func(t *testing.T) {\n\t\tres := bson.D{{\"ok\", 1}}\n\t\tmd.AddResponses(res)\n\t\tassert.NotNil(t, md.conn.responses, \"expected non-nil responses\")\n\t\tassert.Len(t, md.conn.responses, 1, \"expected 1 response, got %v\", len(md.conn.responses))\n\t\terr = client.Ping(context.Background(), nil)\n\t\trequire.NoError(t, err)\n\t})\n\tt.Run(\"AddResponses with multiple\", func(t *testing.T) {\n\t\tres1 := bson.D{{\"ok\", 1}}\n\t\tres2 := bson.D{{\"ok\", 2}}\n\t\tres3 := bson.D{{\"ok\", 3}}\n\t\tmd.AddResponses(res1, res2, res3)\n\t\tassert.NotNil(t, md.conn.responses, \"expected non-nil responses\")\n\t\tassert.Len(t, md.conn.responses, 3, \"expected 3 responses, got %v\", len(md.conn.responses))\n\t\terr = client.Ping(context.Background(), nil)\n\t\trequire.NoError(t, err)\n\t})\n\tt.Run(\"ClearResponses\", func(t *testing.T) {\n\t\tmd.ClearResponses()\n\t\tassert.NotNil(t, md.conn.responses, \"expected non-nil responses\")\n\t\tassert.Len(t, md.conn.responses, 0, \"expected 0 responses, got %v\", len(md.conn.responses))\n\t\terr = client.Ping(context.Background(), nil)\n\t\trequire.Error(t, err, \"expected Ping error, got nil\")\n\t})\n}\n"
  },
  {
    "path": "x/mongo/driver/errors.go",
    "content": "// Copyright (C) MongoDB, Inc. 2022-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage driver\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"strings\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/driverutil\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/description\"\n)\n\n// LegacyNotPrimaryErrMsg is the error message that older MongoDB servers (see\n// SERVER-50412 for versions) return when a write operation is erroneously sent\n// to a non-primary node.\nconst LegacyNotPrimaryErrMsg = \"not master\"\n\nvar (\n\tretryableCodes = []int32{\n\t\t6,     // HostUnreachable\n\t\t7,     // HostNotFound\n\t\t89,    // NetworkTimeout\n\t\t91,    // ShutdownInProgress\n\t\t134,   // ReadConcernMajorityNotAvailableYet\n\t\t189,   // PrimarySteppedDown\n\t\t262,   // ExceededTimeLimit\n\t\t9001,  // SocketException\n\t\t10107, // NotWritablePrimary\n\t\t11600, // InterruptedAtShutdown\n\t\t11602, // InterruptedDueToReplStateChange\n\t\t13435, // NotPrimaryNoSecondaryOk\n\t\t13436, // NotPrimaryOrSecondary\n\t}\n\n\tnodeIsRecoveringCodes   = []int32{11600, 11602, 13436, 189, 91}\n\tnotPrimaryCodes         = []int32{10107, 13435, 10058}\n\tnodeIsShuttingDownCodes = []int32{11600, 91}\n\n\tunknownReplWriteConcernCode   = int32(79)\n\tunsatisfiableWriteConcernCode = int32(100)\n)\n\nvar (\n\t// UnknownTransactionCommitResult is an error label for unknown transaction commit results.\n\tUnknownTransactionCommitResult = \"UnknownTransactionCommitResult\"\n\t// TransientTransactionError is an error label for transient errors with transactions.\n\tTransientTransactionError = \"TransientTransactionError\"\n\t// NetworkError is an error label for network errors.\n\tNetworkError = \"NetworkError\"\n\t// RetryableWriteError is an error label for retryable write errors.\n\tRetryableWriteError = \"RetryableWriteError\"\n\t// NoWritesPerformed is an error label indicated that no writes were performed for an operation.\n\tNoWritesPerformed = \"NoWritesPerformed\"\n\t// ErrCursorNotFound is the cursor not found error for legacy find operations.\n\tErrCursorNotFound = errors.New(\"cursor not found\")\n\t// ErrUnacknowledgedWrite is returned from functions that have an unacknowledged\n\t// write concern.\n\tErrUnacknowledgedWrite = errors.New(\"unacknowledged write\")\n\t// ErrUnsupportedStorageEngine is returned when a retryable write is attempted against a server\n\t// that uses a storage engine that does not support retryable writes\n\tErrUnsupportedStorageEngine = errors.New(\"this MongoDB deployment does not support retryable writes. Please add retryWrites=false to your connection string\")\n\t// ErrDeadlineWouldBeExceeded is returned when a Timeout set on an operation\n\t// would be exceeded if the operation were sent to the server. It wraps\n\t// context.DeadlineExceeded.\n\tErrDeadlineWouldBeExceeded = fmt.Errorf(\n\t\t\"operation not sent to server, as Timeout would be exceeded: %w\",\n\t\tcontext.DeadlineExceeded)\n\t// ErrSystemOverloadedError is returned when the server reports that it is overloaded\n\tErrSystemOverloadedError = \"SystemOverloadedError\"\n\t// ErrRetryableError is returned when the server reports that the operation is retryable\n\tErrRetryableError = \"RetryableError\"\n)\n\n// QueryFailureError is an error representing a command failure as a document.\ntype QueryFailureError struct {\n\tMessage  string\n\tResponse bsoncore.Document\n\tWrapped  error\n}\n\n// Error implements the error interface.\nfunc (e QueryFailureError) Error() string {\n\treturn fmt.Sprintf(\"%s: %v\", e.Message, e.Response)\n}\n\n// Unwrap returns the underlying error.\nfunc (e QueryFailureError) Unwrap() error {\n\treturn e.Wrapped\n}\n\n// ResponseError is an error parsing the response to a command.\ntype ResponseError struct {\n\tMessage string\n\tWrapped error\n}\n\n// NewCommandResponseError creates a CommandResponseError.\nfunc NewCommandResponseError(msg string, err error) ResponseError {\n\treturn ResponseError{Message: msg, Wrapped: err}\n}\n\n// Error implements the error interface.\nfunc (e ResponseError) Error() string {\n\tif e.Wrapped != nil {\n\t\treturn fmt.Sprintf(\"%s: %s\", e.Message, e.Wrapped)\n\t}\n\treturn e.Message\n}\n\n// WriteCommandError is an error for a write command.\ntype WriteCommandError struct {\n\tWriteConcernError *WriteConcernError\n\tWriteErrors       WriteErrors\n\tLabels            []string\n\tRaw               bsoncore.Document\n}\n\n// UnsupportedStorageEngine returns whether or not the WriteCommandError comes from a retryable write being attempted\n// against a server that has a storage engine where they are not supported\nfunc (wce WriteCommandError) UnsupportedStorageEngine() bool {\n\tfor _, writeError := range wce.WriteErrors {\n\t\tif writeError.Code == 20 && strings.HasPrefix(strings.ToLower(writeError.Message), \"transaction numbers\") {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\nfunc (wce WriteCommandError) Error() string {\n\tvar buf bytes.Buffer\n\tfmt.Fprint(&buf, \"write command error: [\")\n\tfmt.Fprintf(&buf, \"{%s}, \", wce.WriteErrors)\n\tfmt.Fprintf(&buf, \"{%s}]\", wce.WriteConcernError)\n\treturn buf.String()\n}\n\n// Retryable returns true if the error is retryable\nfunc (wce WriteCommandError) Retryable(serverKind description.ServerKind, wireVersion *description.VersionRange) bool {\n\tfor _, label := range wce.Labels {\n\t\tif label == RetryableWriteError {\n\t\t\treturn true\n\t\t}\n\t}\n\tif wireVersion != nil && wireVersion.Max >= 9 {\n\t\treturn false\n\t}\n\n\tif wce.WriteConcernError == nil {\n\t\treturn false\n\t}\n\treturn wce.WriteConcernError.Retryable(serverKind, wireVersion)\n}\n\n// HasErrorLabel returns true if the error contains the specified label.\nfunc (wce WriteCommandError) HasErrorLabel(label string) bool {\n\tfor _, l := range wce.Labels {\n\t\tif l == label {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\n// WriteConcernError is a write concern failure that occurred as a result of a\n// write operation.\ntype WriteConcernError struct {\n\tName            string\n\tCode            int64\n\tMessage         string\n\tDetails         bsoncore.Document\n\tLabels          []string\n\tTopologyVersion *description.TopologyVersion\n\tRaw             bsoncore.Document\n}\n\nfunc (wce WriteConcernError) Error() string {\n\tif wce.Name != \"\" {\n\t\treturn fmt.Sprintf(\"(%v) %v\", wce.Name, wce.Message)\n\t}\n\treturn wce.Message\n}\n\n// Retryable returns true if the error is retryable\nfunc (wce WriteConcernError) Retryable(serverKind description.ServerKind, wireVersion *description.VersionRange) bool {\n\tif serverKind == description.ServerKindMongos && wireVersion.Max < 9 {\n\t\t// For a pre-4.4 mongos response, we can trust that mongos will have already\n\t\t// retried the operation if necessary. Drivers should not retry to avoid\n\t\t// \"excessive retrying\".\n\t\treturn false\n\t}\n\n\tfor _, code := range retryableCodes {\n\t\tif wce.Code == int64(code) {\n\t\t\treturn true\n\t\t}\n\t}\n\n\treturn false\n}\n\n// NodeIsRecovering returns true if this error is a node is recovering error.\nfunc (wce WriteConcernError) NodeIsRecovering() bool {\n\tfor _, code := range nodeIsRecoveringCodes {\n\t\tif wce.Code == int64(code) {\n\t\t\treturn true\n\t\t}\n\t}\n\thasNoCode := wce.Code == 0\n\treturn hasNoCode && strings.Contains(wce.Message, \"node is recovering\")\n}\n\n// NodeIsShuttingDown returns true if this error is a node is shutting down error.\nfunc (wce WriteConcernError) NodeIsShuttingDown() bool {\n\tfor _, code := range nodeIsShuttingDownCodes {\n\t\tif wce.Code == int64(code) {\n\t\t\treturn true\n\t\t}\n\t}\n\thasNoCode := wce.Code == 0\n\treturn hasNoCode && strings.Contains(wce.Message, \"node is shutting down\")\n}\n\n// NotPrimary returns true if this error is a not primary error.\nfunc (wce WriteConcernError) NotPrimary() bool {\n\tfor _, code := range notPrimaryCodes {\n\t\tif wce.Code == int64(code) {\n\t\t\treturn true\n\t\t}\n\t}\n\thasNoCode := wce.Code == 0\n\treturn hasNoCode && strings.Contains(wce.Message, LegacyNotPrimaryErrMsg)\n}\n\n// WriteError is a non-write concern failure that occurred as a result of a write\n// operation.\ntype WriteError struct {\n\tIndex   int64\n\tCode    int64\n\tMessage string\n\tDetails bsoncore.Document\n\tRaw     bsoncore.Document\n}\n\nfunc (we WriteError) Error() string { return we.Message }\n\n// WriteErrors is a group of non-write concern failures that occurred as a result\n// of a write operation.\ntype WriteErrors []WriteError\n\nfunc (we WriteErrors) Error() string {\n\tvar buf bytes.Buffer\n\tfmt.Fprint(&buf, \"write errors: [\")\n\tfor idx, err := range we {\n\t\tif idx != 0 {\n\t\t\tfmt.Fprintf(&buf, \", \")\n\t\t}\n\t\tfmt.Fprintf(&buf, \"{%s}\", err)\n\t}\n\tfmt.Fprint(&buf, \"]\")\n\treturn buf.String()\n}\n\n// Error is a command execution error from the database.\ntype Error struct {\n\tCode            int32\n\tMessage         string\n\tLabels          []string\n\tName            string\n\tWrapped         error\n\tTopologyVersion *description.TopologyVersion\n\tRaw             bsoncore.Document\n}\n\n// UnsupportedStorageEngine returns whether e came as a result of an unsupported storage engine\nfunc (e Error) UnsupportedStorageEngine() bool {\n\treturn e.Code == 20 && strings.HasPrefix(strings.ToLower(e.Message), \"transaction numbers\")\n}\n\n// Error implements the error interface.\nfunc (e Error) Error() string {\n\tvar msg string\n\tif e.Name != \"\" {\n\t\tmsg = fmt.Sprintf(\"(%v)\", e.Name)\n\t}\n\tmsg += \" \" + e.Message\n\tif e.Wrapped != nil {\n\t\tmsg += \": \" + e.Wrapped.Error()\n\t}\n\treturn msg\n}\n\n// Unwrap returns the underlying error.\nfunc (e Error) Unwrap() error {\n\treturn e.Wrapped\n}\n\n// HasErrorLabel returns true if the error contains the specified label.\nfunc (e Error) HasErrorLabel(label string) bool {\n\tfor _, l := range e.Labels {\n\t\tif l == label {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\n// RetryableRead returns true if the error is retryable for a read operation\nfunc (e Error) RetryableRead() bool {\n\tfor _, label := range e.Labels {\n\t\tif label == NetworkError {\n\t\t\treturn true\n\t\t}\n\t}\n\tfor _, code := range retryableCodes {\n\t\tif e.Code == code {\n\t\t\treturn true\n\t\t}\n\t}\n\n\treturn false\n}\n\n// RetryableWrite returns true if the error is retryable for a write operation\nfunc (e Error) RetryableWrite(wireVersion *description.VersionRange) bool {\n\tfor _, label := range e.Labels {\n\t\tif label == NetworkError || label == RetryableWriteError {\n\t\t\treturn true\n\t\t}\n\t}\n\tif wireVersion != nil && wireVersion.Max >= 9 {\n\t\treturn false\n\t}\n\tfor _, code := range retryableCodes {\n\t\tif e.Code == code {\n\t\t\treturn true\n\t\t}\n\t}\n\n\treturn false\n}\n\n// NetworkError returns true if the error is a network error.\nfunc (e Error) NetworkError() bool {\n\tfor _, label := range e.Labels {\n\t\tif label == NetworkError {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\n// NodeIsRecovering returns true if this error is a node is recovering error.\nfunc (e Error) NodeIsRecovering() bool {\n\tfor _, code := range nodeIsRecoveringCodes {\n\t\tif e.Code == code {\n\t\t\treturn true\n\t\t}\n\t}\n\thasNoCode := e.Code == 0\n\treturn hasNoCode && strings.Contains(e.Message, \"node is recovering\")\n}\n\n// NodeIsShuttingDown returns true if this error is a node is shutting down error.\nfunc (e Error) NodeIsShuttingDown() bool {\n\tfor _, code := range nodeIsShuttingDownCodes {\n\t\tif e.Code == code {\n\t\t\treturn true\n\t\t}\n\t}\n\thasNoCode := e.Code == 0\n\treturn hasNoCode && strings.Contains(e.Message, \"node is shutting down\")\n}\n\n// NotPrimary returns true if this error is a not primary error.\nfunc (e Error) NotPrimary() bool {\n\tfor _, code := range notPrimaryCodes {\n\t\tif e.Code == code {\n\t\t\treturn true\n\t\t}\n\t}\n\thasNoCode := e.Code == 0\n\treturn hasNoCode && strings.Contains(e.Message, LegacyNotPrimaryErrMsg)\n}\n\n// NamespaceNotFound returns true if this errors is a NamespaceNotFound error.\nfunc (e Error) NamespaceNotFound() bool {\n\treturn e.Code == 26 || e.Message == \"ns not found\"\n}\n\n// ExtractErrorFromServerResponse extracts an error from a server response bsoncore.Document\n// if there is one. Also used in testing for SDAM.\nfunc ExtractErrorFromServerResponse(doc bsoncore.Document) error {\n\tvar errmsg, codeName string\n\tvar code int32\n\tvar labels []string\n\tvar ok bool\n\tvar tv *description.TopologyVersion\n\tvar wcError WriteCommandError\n\telems, err := doc.Elements()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, elem := range elems {\n\t\tswitch elem.Key() {\n\t\tcase \"ok\":\n\t\t\tswitch elem.Value().Type {\n\t\t\tcase bsoncore.TypeInt32:\n\t\t\t\tif elem.Value().Int32() == 1 {\n\t\t\t\t\tok = true\n\t\t\t\t}\n\t\t\tcase bsoncore.TypeInt64:\n\t\t\t\tif elem.Value().Int64() == 1 {\n\t\t\t\t\tok = true\n\t\t\t\t}\n\t\t\tcase bsoncore.TypeDouble:\n\t\t\t\tif elem.Value().Double() == 1 {\n\t\t\t\t\tok = true\n\t\t\t\t}\n\t\t\tcase bsoncore.TypeBoolean:\n\t\t\t\tif elem.Value().Boolean() {\n\t\t\t\t\tok = true\n\t\t\t\t}\n\t\t\t}\n\t\tcase \"errmsg\":\n\t\t\tif str, okay := elem.Value().StringValueOK(); okay {\n\t\t\t\terrmsg = str\n\t\t\t}\n\t\tcase \"codeName\":\n\t\t\tif str, okay := elem.Value().StringValueOK(); okay {\n\t\t\t\tcodeName = str\n\t\t\t}\n\t\tcase \"code\":\n\t\t\tif c, okay := elem.Value().Int32OK(); okay {\n\t\t\t\tcode = c\n\t\t\t}\n\t\tcase \"errorLabels\":\n\t\t\tif arr, okay := elem.Value().ArrayOK(); okay {\n\t\t\t\tvals, err := arr.Values()\n\t\t\t\tif err != nil {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tfor _, val := range vals {\n\t\t\t\t\tif str, ok := val.StringValueOK(); ok {\n\t\t\t\t\t\tlabels = append(labels, str)\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t}\n\t\tcase \"writeErrors\":\n\t\t\tarr, exists := elem.Value().ArrayOK()\n\t\t\tif !exists {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tvals, err := arr.Values()\n\t\t\tif err != nil {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tfor _, val := range vals {\n\t\t\t\tvar we WriteError\n\t\t\t\tdoc, exists := val.DocumentOK()\n\t\t\t\tif !exists {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif index, exists := doc.Lookup(\"index\").AsInt64OK(); exists {\n\t\t\t\t\twe.Index = index\n\t\t\t\t}\n\t\t\t\tif code, exists := doc.Lookup(\"code\").AsInt64OK(); exists {\n\t\t\t\t\twe.Code = code\n\t\t\t\t}\n\t\t\t\tif msg, exists := doc.Lookup(\"errmsg\").StringValueOK(); exists {\n\t\t\t\t\twe.Message = msg\n\t\t\t\t}\n\t\t\t\tif info, exists := doc.Lookup(\"errInfo\").DocumentOK(); exists {\n\t\t\t\t\twe.Details = make([]byte, len(info))\n\t\t\t\t\tcopy(we.Details, info)\n\t\t\t\t}\n\t\t\t\twe.Raw = doc\n\t\t\t\twcError.WriteErrors = append(wcError.WriteErrors, we)\n\t\t\t}\n\t\tcase \"writeConcernError\":\n\t\t\tdoc, exists := elem.Value().DocumentOK()\n\t\t\tif !exists {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\twcError.WriteConcernError = new(WriteConcernError)\n\t\t\twcError.WriteConcernError.Raw = doc\n\t\t\tif code, exists := doc.Lookup(\"code\").AsInt64OK(); exists {\n\t\t\t\twcError.WriteConcernError.Code = code\n\t\t\t}\n\t\t\tif name, exists := doc.Lookup(\"codeName\").StringValueOK(); exists {\n\t\t\t\twcError.WriteConcernError.Name = name\n\t\t\t}\n\t\t\tif msg, exists := doc.Lookup(\"errmsg\").StringValueOK(); exists {\n\t\t\t\twcError.WriteConcernError.Message = msg\n\t\t\t}\n\t\t\tif info, exists := doc.Lookup(\"errInfo\").DocumentOK(); exists {\n\t\t\t\twcError.WriteConcernError.Details = make([]byte, len(info))\n\t\t\t\tcopy(wcError.WriteConcernError.Details, info)\n\t\t\t}\n\t\t\tif errLabels, exists := doc.Lookup(\"errorLabels\").ArrayOK(); exists {\n\t\t\t\tvals, err := errLabels.Values()\n\t\t\t\tif err != nil {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tfor _, val := range vals {\n\t\t\t\t\tif str, ok := val.StringValueOK(); ok {\n\t\t\t\t\t\tlabels = append(labels, str)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\tcase \"topologyVersion\":\n\t\t\tdoc, ok := elem.Value().DocumentOK()\n\t\t\tif !ok {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tversion, err := driverutil.NewTopologyVersion(bson.Raw(doc))\n\t\t\tif err == nil {\n\t\t\t\ttv = version\n\t\t\t}\n\t\t}\n\t}\n\n\tif !ok {\n\t\tif errmsg == \"\" {\n\t\t\terrmsg = \"command failed\"\n\t\t}\n\n\t\terr := Error{\n\t\t\tCode:            code,\n\t\t\tMessage:         errmsg,\n\t\t\tName:            codeName,\n\t\t\tLabels:          labels,\n\t\t\tTopologyVersion: tv,\n\t\t\tRaw:             doc,\n\t\t}\n\n\t\t// If we get a MaxTimeMSExpired error, assume that the error was caused\n\t\t// by setting \"maxTimeMS\" on the command based on the context deadline\n\t\t// or on \"timeoutMS\". In that case, make the error wrap\n\t\t// context.DeadlineExceeded so that users can always check\n\t\t//\n\t\t//  errors.Is(err, context.DeadlineExceeded)\n\t\t//\n\t\t// for either client-side or server-side timeouts.\n\t\tif err.Code == 50 {\n\t\t\terr.Wrapped = context.DeadlineExceeded\n\t\t}\n\n\t\treturn err\n\t}\n\n\tif len(wcError.WriteErrors) > 0 || wcError.WriteConcernError != nil {\n\t\twcError.Labels = labels\n\t\tif wcError.WriteConcernError != nil {\n\t\t\twcError.WriteConcernError.TopologyVersion = tv\n\t\t}\n\t\twcError.Raw = doc\n\t\treturn wcError\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "x/mongo/driver/integration/aggregate_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2022-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage integration\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"testing\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/integtest\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/serverselector\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/writeconcern\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/operation\"\n)\n\nfunc TestAggregate(t *testing.T) {\n\tif testing.Short() {\n\t\tt.Skip(\"skipping integration test in short mode\")\n\t}\n\n\tt.Run(\"Multiple Batches\", func(t *testing.T) {\n\t\tds := []bsoncore.Document{\n\t\t\tbsoncore.BuildDocument(nil, bsoncore.AppendInt32Element(nil, \"_id\", 1)),\n\t\t\tbsoncore.BuildDocument(nil, bsoncore.AppendInt32Element(nil, \"_id\", 2)),\n\t\t\tbsoncore.BuildDocument(nil, bsoncore.AppendInt32Element(nil, \"_id\", 3)),\n\t\t\tbsoncore.BuildDocument(nil, bsoncore.AppendInt32Element(nil, \"_id\", 4)),\n\t\t\tbsoncore.BuildDocument(nil, bsoncore.AppendInt32Element(nil, \"_id\", 5)),\n\t\t}\n\t\twc := writeconcern.Majority()\n\t\tautoInsertDocs(t, wc, ds...)\n\n\t\top := operation.NewAggregate(bsoncore.BuildArray(nil,\n\t\t\tbsoncore.BuildDocumentValue(\n\t\t\t\tbsoncore.BuildDocumentElement(nil,\n\t\t\t\t\t\"$match\", bsoncore.BuildDocumentElement(nil,\n\t\t\t\t\t\t\"_id\", bsoncore.AppendInt32Element(nil, \"$gt\", 2),\n\t\t\t\t\t),\n\t\t\t\t),\n\t\t\t),\n\t\t\tbsoncore.BuildDocumentValue(\n\t\t\t\tbsoncore.BuildDocumentElement(nil,\n\t\t\t\t\t\"$sort\", bsoncore.AppendInt32Element(nil, \"_id\", 1),\n\t\t\t\t),\n\t\t\t),\n\t\t)).Collection(integtest.ColName(t)).Database(dbName).Deployment(integtest.Topology(t)).\n\t\t\tServerSelector(&serverselector.Write{}).BatchSize(2)\n\t\terr := op.Execute(context.Background())\n\t\tnoerr(t, err)\n\t\tcursor, err := op.Result(driver.CursorOptions{BatchSize: 2})\n\t\tnoerr(t, err)\n\n\t\tvar got []bsoncore.Document\n\t\tfor i := 0; i < 2; i++ {\n\t\t\tif !cursor.Next(context.Background()) {\n\t\t\t\tt.Error(\"Cursor should have results, but does not have a next result\")\n\t\t\t}\n\t\t\tdocs, err := cursor.Batch().Documents()\n\t\t\tnoerr(t, err)\n\t\t\tgot = append(got, docs...)\n\t\t}\n\t\treaders := ds[2:]\n\t\tfor i, g := range got {\n\t\t\tif !bytes.Equal(g[:len(readers[i])], readers[i]) {\n\t\t\t\tt.Errorf(\"Did not get expected document. got %v; want %v\", bson.Raw(g[:len(readers[i])]), readers[i])\n\t\t\t}\n\t\t}\n\n\t\tif cursor.Next(context.Background()) {\n\t\t\tt.Error(\"Cursor should be exhausted but has more results\")\n\t\t}\n\t})\n\tt.Run(\"AllowDiskUse\", func(t *testing.T) {\n\t\tds := []bsoncore.Document{\n\t\t\tbsoncore.BuildDocument(nil, bsoncore.AppendInt32Element(nil, \"_id\", 1)),\n\t\t\tbsoncore.BuildDocument(nil, bsoncore.AppendInt32Element(nil, \"_id\", 2)),\n\t\t}\n\t\twc := writeconcern.Majority()\n\t\tautoInsertDocs(t, wc, ds...)\n\n\t\top := operation.NewAggregate(bsoncore.BuildArray(nil)).Collection(integtest.ColName(t)).Database(dbName).\n\t\t\tDeployment(integtest.Topology(t)).ServerSelector(&serverselector.Write{}).AllowDiskUse(true)\n\t\terr := op.Execute(context.Background())\n\t\tif err != nil {\n\t\t\tt.Errorf(\"Expected no error from allowing disk use, but got %v\", err)\n\t\t}\n\t})\n}\n"
  },
  {
    "path": "x/mongo/driver/integration/compressor_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2022-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage integration\n\nimport (\n\t\"context\"\n\t\"os\"\n\t\"testing\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/integtest\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/require\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/writeconcern\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/operation\"\n)\n\nfunc TestCompression(t *testing.T) {\n\tcomp := os.Getenv(\"MONGO_GO_DRIVER_COMPRESSOR\")\n\tif len(comp) == 0 {\n\t\tt.Skip(\"Skipping because no compressor specified\")\n\t}\n\n\twc := writeconcern.Majority()\n\tcollOne := integtest.ColName(t)\n\n\tdropCollection(t, integtest.DBName(t), collOne)\n\tinsertDocs(t, integtest.DBName(t), collOne, wc,\n\t\tbsoncore.BuildDocument(nil, bsoncore.AppendStringElement(nil, \"name\", \"compression_test\")),\n\t)\n\n\tcmd := operation.NewCommand(bsoncore.BuildDocument(nil, bsoncore.AppendInt32Element(nil, \"serverStatus\", 1))).\n\t\tDeployment(integtest.Topology(t)).\n\t\tDatabase(integtest.DBName(t))\n\n\tctx := context.Background()\n\terr := cmd.Execute(ctx)\n\tnoerr(t, err)\n\tresult := cmd.Result()\n\n\tserverVersion, err := result.LookupErr(\"version\")\n\tnoerr(t, err)\n\n\tif integtest.CompareVersions(t, serverVersion.StringValue(), \"3.4\") < 0 {\n\t\tt.Skip(\"skipping compression test for version < 3.4\")\n\t}\n\n\tnetworkVal, err := result.LookupErr(\"network\")\n\tnoerr(t, err)\n\n\trequire.Equal(t, bsoncore.TypeEmbeddedDocument, networkVal.Type)\n\n\tcompressionVal, err := networkVal.Document().LookupErr(\"compression\")\n\tnoerr(t, err)\n\n\tcompressorDoc, err := compressionVal.Document().LookupErr(comp)\n\tnoerr(t, err)\n\n\tcompressorKey := \"compressor\"\n\tcompareTo36 := integtest.CompareVersions(t, serverVersion.StringValue(), \"3.6\")\n\tif compareTo36 < 0 {\n\t\tcompressorKey = \"compressed\"\n\t}\n\tcompressor, err := compressorDoc.Document().LookupErr(compressorKey)\n\tnoerr(t, err)\n\n\tbytesIn, err := compressor.Document().LookupErr(\"bytesIn\")\n\tnoerr(t, err)\n\n\trequire.True(t, bytesIn.IsNumber())\n\trequire.True(t, bytesIn.Int64() > 0)\n}\n"
  },
  {
    "path": "x/mongo/driver/integration/doc.go",
    "content": "// Copyright (C) MongoDB, Inc. 2024-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\n// Package integration is intended for internal use only. It is made available\n// to facilitate use cases that require access to internal MongoDB driver\n// functionality and state. The API of this package is not stable and there is\n// no backward compatibility guarantee.\n//\n// WARNING: THIS PACKAGE IS EXPERIMENTAL AND MAY BE MODIFIED OR REMOVED WITHOUT\n// NOTICE! USE WITH EXTREME CAUTION!\npackage integration\n"
  },
  {
    "path": "x/mongo/driver/integration/integration.go",
    "content": "// Copyright (C) MongoDB, Inc. 2022-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage integration\n"
  },
  {
    "path": "x/mongo/driver/integration/main_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2022-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage integration\n\nimport (\n\t\"context\"\n\t\"flag\"\n\t\"fmt\"\n\t\"os\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/integtest\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/require\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/serverselector\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/writeconcern\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/auth\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/connstring\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/operation\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/topology\"\n)\n\nvar (\n\thost             *string\n\tconnectionString *connstring.ConnString\n\tdbName           string\n)\n\nfunc TestMain(m *testing.M) {\n\tflag.Parse()\n\n\tmongodbURI := os.Getenv(\"MONGODB_URI\")\n\tif mongodbURI == \"\" {\n\t\tmongodbURI = \"mongodb://localhost:27017\"\n\t}\n\n\tmongodbURI = addTLSConfigToURI(mongodbURI)\n\tmongodbURI = addCompressorToURI(mongodbURI)\n\n\tvar err error\n\tconnectionString, err = connstring.ParseAndValidate(mongodbURI)\n\tif err != nil {\n\t\tfmt.Printf(\"Could not parse connection string: %v\\n\", err)\n\t\tos.Exit(1)\n\t}\n\n\thost = &connectionString.Hosts[0]\n\n\tdbName = fmt.Sprintf(\"mongo-go-driver-%d\", os.Getpid())\n\tif connectionString.Database != \"\" {\n\t\tdbName = connectionString.Database\n\t}\n\tos.Exit(m.Run())\n}\n\nfunc noerr(t *testing.T, err error) {\n\tif err != nil {\n\t\tt.Helper()\n\t\tt.Errorf(\"Unexpected error: %v\", err)\n\t\tt.FailNow()\n\t}\n}\n\nfunc autherr(t *testing.T, err error) {\n\tt.Helper()\n\tswitch e := err.(type) {\n\tcase topology.ConnectionError:\n\t\tif _, ok := e.Wrapped.(*auth.Error); !ok {\n\t\t\tt.Fatal(\"Expected auth error and didn't get one\")\n\t\t}\n\tcase *auth.Error:\n\t\treturn\n\tdefault:\n\t\tt.Fatal(\"Expected auth error and didn't get one\")\n\t}\n}\n\n// addTLSConfigToURI checks for the environmental variable indicating that the tests are being run\n// on an SSL-enabled server, and if so, returns a new URI with the necessary configuration.\nfunc addTLSConfigToURI(uri string) string {\n\tcaFile := os.Getenv(\"MONGO_GO_DRIVER_CA_FILE\")\n\tif len(caFile) == 0 {\n\t\treturn uri\n\t}\n\n\tif !strings.ContainsRune(uri, '?') {\n\t\tif uri[len(uri)-1] != '/' {\n\t\t\turi += \"/\"\n\t\t}\n\n\t\turi += \"?\"\n\t} else {\n\t\turi += \"&\"\n\t}\n\n\treturn uri + \"ssl=true&sslCertificateAuthorityFile=\" + caFile\n}\n\nfunc addCompressorToURI(uri string) string {\n\tcomp := os.Getenv(\"MONGO_GO_DRIVER_COMPRESSOR\")\n\tif len(comp) == 0 {\n\t\treturn uri\n\t}\n\n\tif !strings.ContainsRune(uri, '?') {\n\t\tif uri[len(uri)-1] != '/' {\n\t\t\turi += \"/\"\n\t\t}\n\n\t\turi += \"?\"\n\t} else {\n\t\turi += \"&\"\n\t}\n\n\treturn uri + \"compressors=\" + comp\n}\n\n// runCommand runs an arbitrary command on a given database of the target\n// server.\nfunc runCommand(s driver.Server, db string, cmd bsoncore.Document) error {\n\top := operation.NewCommand(cmd).\n\t\tDatabase(db).\n\t\tDeployment(driver.SingleServerDeployment{Server: s})\n\treturn op.Execute(context.Background())\n}\n\n// dropCollection drops the collection in the test cluster.\nfunc dropCollection(t *testing.T, dbname, colname string) {\n\terr := operation.NewCommand(bsoncore.BuildDocument(nil, bsoncore.AppendStringElement(nil, \"drop\", colname))).\n\t\tDatabase(dbname).ServerSelector(&serverselector.Write{}).Deployment(integtest.Topology(t)).\n\t\tExecute(context.Background())\n\tif de, ok := err.(driver.Error); err != nil && (!ok || !de.NamespaceNotFound()) {\n\t\trequire.NoError(t, err)\n\t}\n}\n\n// autoInsertDocs inserts the docs into the test cluster.\nfunc autoInsertDocs(t *testing.T, writeConcern *writeconcern.WriteConcern, docs ...bsoncore.Document) {\n\tinsertDocs(t, integtest.DBName(t), integtest.ColName(t), writeConcern, docs...)\n}\n\n// insertDocs inserts the docs into the test cluster.\nfunc insertDocs(t *testing.T, dbname, colname string, writeConcern *writeconcern.WriteConcern, docs ...bsoncore.Document) {\n\tt.Helper()\n\n\t// The initial call to integtest.Topology drops the database used by the\n\t// tests, so we have to call it here first to prevent the existing test code\n\t// from dropping the database after we've inserted data.\n\tintegtest.Topology(t)\n\n\tclient, err := mongo.Connect(options.Client().ApplyURI(connectionString.Original).SetWriteConcern(writeConcern))\n\trequire.NoError(t, err)\n\tdefer func() {\n\t\t_ = client.Disconnect(context.Background())\n\t}()\n\n\tcoll := client.Database(dbname).Collection(colname)\n\t_, err = coll.InsertMany(context.Background(), docs)\n\trequire.NoError(t, err)\n}\n"
  },
  {
    "path": "x/mongo/driver/integration/scram_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2022-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage integration\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"os\"\n\t\"testing\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/integtest\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/serverselector\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/writeconcern\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver\"\n)\n\ntype scramTestCase struct {\n\tusername    string\n\tpassword    string\n\tmechanisms  []string\n\taltPassword string\n}\n\nfunc TestSCRAM(t *testing.T) {\n\tif os.Getenv(\"AUTH\") != \"auth\" {\n\t\tt.Skip(\"Skipping because authentication is required\")\n\t}\n\n\tserver, err := integtest.Topology(t).SelectServer(context.Background(), &serverselector.Write{})\n\tnoerr(t, err)\n\tserverConnection, err := server.Connection(context.Background())\n\tnoerr(t, err)\n\tdefer serverConnection.Close()\n\n\t// Unicode constants for testing\n\tromanFour := \"\\u2163\" // ROMAN NUMERAL FOUR -> SASL prepped is \"IV\"\n\tromanNine := \"\\u2168\" // ROMAN NUMERAL NINE -> SASL prepped is \"IX\"\n\n\ttestUsers := []scramTestCase{\n\t\t// SCRAM spec test steps 1-3\n\t\t{username: \"sha1\", password: \"sha1\", mechanisms: []string{\"SCRAM-SHA-1\"}},\n\t\t{username: \"sha256\", password: \"sha256\", mechanisms: []string{\"SCRAM-SHA-256\"}},\n\t\t{username: \"both\", password: \"both\", mechanisms: []string{\"SCRAM-SHA-1\", \"SCRAM-SHA-256\"}},\n\t\t// SCRAM spec test step 4\n\t\t{username: \"IX\", password: \"IX\", mechanisms: []string{\"SCRAM-SHA-256\"}, altPassword: \"I\\u00ADX\"},\n\t\t{username: romanNine, password: romanFour, mechanisms: []string{\"SCRAM-SHA-256\"}, altPassword: \"I\\u00ADV\"},\n\t}\n\n\t// Verify that test (root) user is authenticated.  If this fails, the\n\t// rest of the test can't succeed.\n\twc := writeconcern.Majority()\n\tcollOne := integtest.ColName(t)\n\tdropCollection(t, integtest.DBName(t), collOne)\n\tinsertDocs(t, integtest.DBName(t),\n\t\tcollOne, wc, bsoncore.BuildDocument(nil, bsoncore.AppendStringElement(nil, \"name\", \"scram_test\")),\n\t)\n\n\t// Test step 1: Create users for test cases\n\terr = createScramUsers(t, server, testUsers)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\t// Step 2 and 3a: For each auth mechanism, \"SCRAM-SHA-1\", \"SCRAM-SHA-256\"\n\t// and \"negotiate\" (a fake, placeholder mechanism), iterate over each user\n\t// and ensure that each mechanism that should succeed does so and each\n\t// that should fail does so.\n\tfor _, m := range []string{\"SCRAM-SHA-1\", \"SCRAM-SHA-256\", \"negotiate\"} {\n\t\tfor _, c := range testUsers {\n\t\t\tt.Run(\n\t\t\t\tfmt.Sprintf(\"%s %s\", c.username, m),\n\t\t\t\tfunc(t *testing.T) {\n\t\t\t\t\terr := testScramUserAuthWithMech(t, c, m)\n\t\t\t\t\tif m == \"negotiate\" || hasAuthMech(c.mechanisms, m) {\n\t\t\t\t\t\tnoerr(t, err)\n\t\t\t\t\t} else {\n\t\t\t\t\t\tautherr(t, err)\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t)\n\t\t}\n\t}\n\n\t// Step 3b: test non-existing user with negotiation fails with\n\t// an auth.Error type.\n\tbogus := scramTestCase{username: \"eliot\", password: \"trustno1\"}\n\terr = testScramUserAuthWithMech(t, bogus, \"negotiate\")\n\tautherr(t, err)\n\n\t// XXX Step 4: test alternate password forms\n\tfor _, c := range testUsers {\n\t\tif c.altPassword == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tc.password = c.altPassword\n\t\tt.Run(\n\t\t\tfmt.Sprintf(\"%s alternate password\", c.username),\n\t\t\tfunc(t *testing.T) {\n\t\t\t\terr := testScramUserAuthWithMech(t, c, \"SCRAM-SHA-256\")\n\t\t\t\tnoerr(t, err)\n\t\t\t},\n\t\t)\n\t}\n}\n\nfunc hasAuthMech(mechs []string, m string) bool {\n\tfor _, v := range mechs {\n\t\tif v == m {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\nfunc testScramUserAuthWithMech(t *testing.T, c scramTestCase, mech string) error {\n\tt.Helper()\n\tcredential := options.Credential{\n\t\tUsername:   c.username,\n\t\tPassword:   c.password,\n\t\tAuthSource: integtest.DBName(t),\n\t}\n\tswitch mech {\n\tcase \"negotiate\":\n\t\tcredential.AuthMechanism = \"\"\n\tdefault:\n\t\tcredential.AuthMechanism = mech\n\t}\n\treturn runScramAuthTest(t, credential)\n}\n\nfunc runScramAuthTest(t *testing.T, credential options.Credential) error {\n\tt.Helper()\n\ttopology := integtest.TopologyWithCredential(t, credential)\n\tserver, err := topology.SelectServer(context.Background(), &serverselector.Write{})\n\tnoerr(t, err)\n\n\tcmd := bsoncore.BuildDocument(nil, bsoncore.AppendInt32Element(nil, \"dbstats\", 1))\n\treturn runCommand(server, integtest.DBName(t), cmd)\n}\n\nfunc createScramUsers(t *testing.T, s driver.Server, cases []scramTestCase) error {\n\tdb := integtest.DBName(t)\n\tfor _, c := range cases {\n\t\tvar values []bsoncore.Value\n\t\tfor _, v := range c.mechanisms {\n\t\t\tvalues = append(values, bsoncore.Value{Type: bsoncore.TypeString, Data: bsoncore.AppendString(nil, v)})\n\t\t}\n\t\tnewUserCmd := bsoncore.BuildDocumentFromElements(nil,\n\t\t\tbsoncore.AppendStringElement(nil, \"createUser\", c.username),\n\t\t\tbsoncore.AppendStringElement(nil, \"pwd\", c.password),\n\t\t\tbsoncore.AppendArrayElement(nil, \"roles\", bsoncore.BuildArray(nil,\n\t\t\t\tbsoncore.Value{Type: bsoncore.TypeEmbeddedDocument, Data: bsoncore.BuildDocumentFromElements(nil,\n\t\t\t\t\tbsoncore.AppendStringElement(nil, \"role\", \"readWrite\"),\n\t\t\t\t\tbsoncore.AppendStringElement(nil, \"db\", db),\n\t\t\t\t)},\n\t\t\t)),\n\t\t\tbsoncore.AppendArrayElement(nil, \"mechanisms\", bsoncore.BuildArray(nil, values...)),\n\t\t)\n\t\terr := runCommand(s, db, newUserCmd)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"Couldn't create user '%s' on db '%s': %w\", c.username, integtest.DBName(t), err)\n\t\t}\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "x/mongo/driver/legacy.go",
    "content": "// Copyright (C) MongoDB, Inc. 2022-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage driver\n\n// LegacyOperationKind indicates if an operation is a legacy find, getMore, or killCursors. This is used\n// in Operation.Execute, which will create legacy OP_QUERY, OP_GET_MORE, or OP_KILL_CURSORS instead\n// of sending them as a command.\ntype LegacyOperationKind uint\n\n// These constants represent the three different kinds of legacy operations.\nconst (\n\tLegacyNone LegacyOperationKind = iota\n\tLegacyFind\n\tLegacyGetMore\n\tLegacyKillCursors\n\tLegacyListCollections\n\tLegacyListIndexes\n\tLegacyHandshake\n)\n"
  },
  {
    "path": "x/mongo/driver/mnet/connection.go",
    "content": "// Copyright (C) MongoDB, Inc. 2023-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage mnet\n\nimport (\n\t\"context\"\n\t\"io\"\n\n\t\"go.mongodb.org/mongo-driver/v2/mongo/address\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/description\"\n)\n\n// ReadWriteCloser represents a Connection where server operations\n// can read from, written to, and closed.\ntype ReadWriteCloser interface {\n\tRead(ctx context.Context) ([]byte, error)\n\tWrite(ctx context.Context, wm []byte) error\n\tio.Closer\n}\n\n// Describer represents a Connection that can be described.\ntype Describer interface {\n\tDescription() description.Server\n\tID() string\n\tServerConnectionID() *int64\n\tDriverConnectionID() int64\n\tAddress() address.Address\n\tStale() bool\n\tOIDCTokenGenID() uint64\n\tSetOIDCTokenGenID(uint64)\n}\n\n// Streamer represents a Connection that supports streaming wire protocol\n// messages using the moreToCome and exhaustAllowed flags.\n//\n// The SetStreaming and CurrentlyStreaming functions correspond to the\n// moreToCome flag on server responses. If a response has moreToCome set,\n// SetStreaming(true) will be called and CurrentlyStreaming() should return\n// true.\n//\n// CanStream corresponds to the exhaustAllowed flag. The operations layer will\n// set exhaustAllowed on outgoing wire messages to inform the server that the\n// driver supports streaming.\ntype Streamer interface {\n\tSetStreaming(bool)\n\tCurrentlyStreaming() bool\n\tSupportsStreaming() bool\n}\n\n// Compressor is an interface used to compress wire messages. If a Connection\n// supports compression it should implement this interface as well. The\n// CompressWireMessage method will be called during the execution of an\n// operation if the wire message is allowed to be compressed.\ntype Compressor interface {\n\tCompressWireMessage(src, dst []byte) ([]byte, error)\n}\n\n// Pinner represents a Connection that can be pinned by one or more cursors or\n// transactions. Implementations of this interface should maintain the following\n// invariants:\n//\n//  1. Each Pin* call should increment the number of references for the\n//     connection.\n//  2. Each Unpin* call should decrement the number of references for the\n//     connection.\n//  3. Calls to Close() should be ignored until all resources have unpinned the\n//     connection.\ntype Pinner interface {\n\tPinToCursor() error\n\tPinToTransaction() error\n\tUnpinFromCursor() error\n\tUnpinFromTransaction() error\n}\n\n// Connection represents a connection to a MongoDB server.\ntype Connection struct {\n\tReadWriteCloser\n\tDescriber\n\tStreamer\n\tCompressor\n\tPinner\n}\n\n// NewConnection creates a new Connection with the provided component. This\n// constructor returns a component that is already a Connection to avoid\n// mis-asserting the composite interfaces.\nfunc NewConnection(component interface {\n\tReadWriteCloser\n\tDescriber\n},\n) *Connection {\n\tif _, ok := component.(*Connection); ok {\n\t\treturn component.(*Connection)\n\t}\n\n\tconn := &Connection{\n\t\tReadWriteCloser: component,\n\t}\n\n\tif describer, ok := component.(Describer); ok {\n\t\tconn.Describer = describer\n\t}\n\n\tif streamer, ok := component.(Streamer); ok {\n\t\tconn.Streamer = streamer\n\t}\n\n\tif compressor, ok := component.(Compressor); ok {\n\t\tconn.Compressor = compressor\n\t}\n\n\tif pinner, ok := component.(Pinner); ok {\n\t\tconn.Pinner = pinner\n\t}\n\n\treturn conn\n}\n"
  },
  {
    "path": "x/mongo/driver/mongocrypt/binary.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\n//go:build cse\n\npackage mongocrypt\n\n/*\n#include <stdlib.h>\n#include <mongocrypt.h>\n*/\nimport \"C\"\n\nimport (\n\t\"unsafe\"\n)\n\n// binary is a wrapper type around a mongocrypt_binary_t*\ntype binary struct {\n\tp       *C.uint8_t\n\twrapped *C.mongocrypt_binary_t\n}\n\n// newBinary creates an empty binary instance.\nfunc newBinary() *binary {\n\treturn &binary{\n\t\twrapped: C.mongocrypt_binary_new(),\n\t}\n}\n\n// newBinaryFromBytes creates a binary instance from a byte buffer.\nfunc newBinaryFromBytes(data []byte) *binary {\n\tif len(data) == 0 {\n\t\treturn newBinary()\n\t}\n\n\t// TODO: Consider using runtime.Pinner to replace the C.CBytes after using go1.21.0.\n\taddr := (*C.uint8_t)(C.CBytes(data)) // uint8_t*\n\tdataLen := C.uint32_t(len(data))     // uint32_t\n\treturn &binary{\n\t\tp:       addr,\n\t\twrapped: C.mongocrypt_binary_new_from_data(addr, dataLen),\n\t}\n}\n\n// toBytes converts the given binary instance to []byte.\nfunc (b *binary) toBytes() []byte {\n\tdataPtr := C.mongocrypt_binary_data(b.wrapped) // C.uint8_t*\n\tdataLen := C.mongocrypt_binary_len(b.wrapped)  // C.uint32_t\n\n\treturn C.GoBytes(unsafe.Pointer(dataPtr), C.int(dataLen))\n}\n\n// close cleans up any resources associated with the given binary instance.\nfunc (b *binary) close() {\n\tif b.p != nil {\n\t\tC.free(unsafe.Pointer(b.p))\n\t}\n\tC.mongocrypt_binary_destroy(b.wrapped)\n}\n"
  },
  {
    "path": "x/mongo/driver/mongocrypt/binary_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\n//go:build cse\n\npackage mongocrypt\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"path\"\n\t\"runtime\"\n\t\"sort\"\n\t\"testing\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/require\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/mongocrypt/options\"\n)\n\n// load JSON for benchmark\nfunc resourceToDocumentForBench(b *testing.B, filename string) bsoncore.Document {\n\tb.Helper()\n\n\tcontent, err := os.ReadFile(path.Join(resourcesDir, filename))\n\trequire.NoError(b, err)\n\n\tvar doc bsoncore.Document\n\n\terr = bson.UnmarshalExtJSON(content, false, &doc)\n\trequire.NoError(b, err)\n\n\treturn doc\n}\n\n// Add encryption key to the encryption context.\nfunc addMongoKeysForBench(b *testing.B, encryptCtx *Context) {\n\tb.Helper()\n\n\tif encryptCtx.State() != NeedMongoKeys {\n\t\treturn\n\t}\n\n\t_, err := encryptCtx.NextOperation()\n\trequire.NoError(b, err)\n\n\t// feed result and finish op\n\terr = encryptCtx.AddOperationResult(resourceToDocumentForBench(b, \"local-key-document.json\"))\n\trequire.NoError(b, err)\n\n\terr = encryptCtx.CompleteOperation()\n\trequire.NoError(b, err)\n}\n\n// encrypt a document for benchmarking\nfunc createEncryptedDocForBench(b *testing.B, crypt *MongoCrypt, iter int) bsoncore.Document {\n\tb.Helper()\n\n\tconst algorithm = \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\"\n\n\t// create explicit encryption context and check initial state\n\tkeyID := bson.Binary{\n\t\tSubtype: 0x04, // 0x04 is UUID subtype\n\t\tData:    []byte(\"aaaaaaaaaaaaaaaa\"),\n\t}\n\n\tencryptOpts := &options.ExplicitEncryptionOptions{Algorithm: algorithm, KeyID: &keyID}\n\tdoc := bsoncore.NewDocumentBuilder().AppendString(\"v\", fmt.Sprintf(\"value %04v\", iter)).Build()\n\n\tencryptCtx, err := crypt.CreateExplicitEncryptionContext(doc, encryptOpts)\n\trequire.NoError(b, err)\n\n\tdefer encryptCtx.Close()\n\n\taddMongoKeysForBench(b, encryptCtx)\n\n\t// perform final encryption\n\tencryptedDoc, err := encryptCtx.Finish()\n\trequire.NoError(b, err)\n\n\treturn encryptedDoc\n}\n\n// decrypt an encrypted document for benchmarking.\nfunc decryptDocForBench(b *testing.B, crypt *MongoCrypt, encryptedDoc bsoncore.Document) bsoncore.Document {\n\tb.Helper()\n\n\t// create explicit decryption context and check initial state\n\tdecryptCtx, err := crypt.CreateDecryptionContext(encryptedDoc)\n\trequire.NoError(b, err)\n\n\tdefer decryptCtx.Close()\n\n\t// perform final decryption\n\tdecryptedDoc, err := decryptCtx.Finish()\n\trequire.NoError(b, err)\n\n\treturn decryptedDoc\n}\n\n// create a document of the form:\n// { \"key0001\": <encrypted \"value 0001\">, \"key0002\": <encrypted \"value 0002\">, ... }\nfunc createEncryptedDocForBulkDecryptionBench(b *testing.B, crypt *MongoCrypt, count int) bsoncore.Document {\n\tbldr := bsoncore.NewDocumentBuilder()\n\n\tfor i := 0; i < count; i++ {\n\t\tencDoc := createEncryptedDocForBench(b, crypt, i)\n\t\tbldr.AppendValue(fmt.Sprintf(\"key%04v\", i), encDoc.Lookup(\"v\"))\n\t}\n\n\treturn bldr.Build()\n}\n\n// Create a MongoCrypt object for benchmarking bulk decryption.\nfunc newCryptForBench(b *testing.B) *MongoCrypt {\n\tkey := []byte{\n\t\t0x9d, 0x94, 0x4b, 0x0d, 0x93, 0xd0, 0xc5, 0x44,\n\t\t0xa5, 0x72, 0xfd, 0x32, 0x1b, 0x94, 0x30, 0x90,\n\t\t0x23, 0x35, 0x73, 0x7c, 0xf0, 0xf6, 0xc2, 0xf4,\n\t\t0xda, 0x23, 0x56, 0xe7, 0x8f, 0x04, 0xcc, 0xfa,\n\t\t0xde, 0x75, 0xb4, 0x51, 0x87, 0xf3, 0x8b, 0x97,\n\t\t0xd7, 0x4b, 0x44, 0x3b, 0xac, 0x39, 0xa2, 0xc6,\n\t\t0x4d, 0x91, 0x00, 0x3e, 0xd1, 0xfa, 0x4a, 0x30,\n\t\t0xc1, 0xd2, 0xc6, 0x5e, 0xfb, 0xac, 0x41, 0xf2,\n\t\t0x48, 0x13, 0x3c, 0x9b, 0x50, 0xfc, 0xa7, 0x24,\n\t\t0x7a, 0x2e, 0x02, 0x63, 0xa3, 0xc6, 0x16, 0x25,\n\t\t0x51, 0x50, 0x78, 0x3e, 0x0f, 0xd8, 0x6e, 0x84,\n\t\t0xa6, 0xec, 0x8d, 0x2d, 0x24, 0x47, 0xe5, 0xaf,\n\t}\n\n\tlocalProvider := bsoncore.NewDocumentBuilder().\n\t\tAppendBinary(\"key\", 0, key).\n\t\tBuild()\n\n\tkmsProviders := bsoncore.NewDocumentBuilder().\n\t\tAppendDocument(\"local\", localProvider).\n\t\tBuild()\n\n\tcryptOpts := &options.MongoCryptOptions{\n\t\tKmsProviders: kmsProviders,\n\t}\n\n\tcrypt, err := NewMongoCrypt(cryptOpts)\n\n\trequire.NoError(b, err)\n\tassert.NotNil(b, crypt)\n\n\treturn crypt\n}\n\nfunc calcMedian(values []float64) float64 {\n\tsort.Float64s(values)\n\n\tn := len(values)\n\tif n%2 == 0 {\n\t\treturn (values[n/2-1] + values[n/2]) / 2.0\n\t}\n\n\treturn values[(n-1)/2]\n}\n\nfunc BenchmarkBulkDecryption(b *testing.B) {\n\tconst numKeys = 1500\n\tconst repeatCount = 10\n\n\t// Create crypt that uses a data key with the \"local\" KMS provider.\n\tcrypt := newCryptForBench(b)\n\tdefer crypt.Close()\n\n\t// Encrypt 1500 string values of the form value 0001, value 0002, value 0003,\n\t// ... with the algorithm AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic.\n\tencryptedDoc := createEncryptedDocForBulkDecryptionBench(b, crypt, numKeys)\n\n\t// decrypt the document repeatedly for one second to warm up the benchmark.\n\tfor start := time.Now(); time.Since(start) < time.Second; {\n\t\tdecryptDocForBench(b, crypt, encryptedDoc)\n\t}\n\n\tbenchmarks := []struct {\n\t\tthreads int\n\t}{\n\t\t{threads: 1},\n\t\t{threads: 2},\n\t\t{threads: 8},\n\t\t{threads: 64},\n\t}\n\n\t// Run the benchmark. Repeat benchmark for thread counts: (1, 2, 8, 64).\n\t// Repeat 10 times.\n\tfor _, bench := range benchmarks {\n\t\tvar opsPerSec []float64\n\n\t\tb.Run(fmt.Sprintf(\"threadCount=%v\", bench.threads), func(b *testing.B) {\n\t\t\truntime.GOMAXPROCS(bench.threads)\n\n\t\t\tfor i := 0; i < repeatCount; i++ {\n\t\t\t\tb.ResetTimer()\n\t\t\t\tb.ReportAllocs()\n\n\t\t\t\tstartTime := time.Now()\n\n\t\t\t\tb.RunParallel(func(pb *testing.PB) {\n\t\t\t\t\tfor pb.Next() {\n\t\t\t\t\t\tdecryptDocForBench(b, crypt, encryptedDoc)\n\t\t\t\t\t}\n\t\t\t\t})\n\n\t\t\t\topsPerSec = append(opsPerSec, float64(b.N)/time.Now().Sub(startTime).Seconds())\n\t\t\t}\n\t\t})\n\n\t\tb.Logf(\"thread count: %v, median ops/sec: %.2f\\n\", bench.threads, calcMedian(opsPerSec))\n\t}\n}\n"
  },
  {
    "path": "x/mongo/driver/mongocrypt/errors.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\n//go:build cse\n\npackage mongocrypt\n\n// #include <mongocrypt.h>\nimport \"C\"\n\nimport (\n\t\"fmt\"\n)\n\n// Error represents an error from an operation on a MongoCrypt instance.\ntype Error struct {\n\tCode    int32\n\tMessage string\n}\n\n// Error implements the error interface.\nfunc (e Error) Error() string {\n\treturn fmt.Sprintf(\"mongocrypt error %d: %v\", e.Code, e.Message)\n}\n\n// errorFromStatus builds a Error from a mongocrypt_status_t object.\nfunc errorFromStatus(status *C.mongocrypt_status_t) error {\n\tcCode := C.mongocrypt_status_code(status) // uint32_t\n\t// mongocrypt_status_message takes uint32_t* as its second param to store the length of the returned string.\n\t// pass nil because the length is handled by C.GoString\n\tcMsg := C.mongocrypt_status_message(status, nil) // const char*\n\tvar msg string\n\tif cMsg != nil {\n\t\tmsg = C.GoString(cMsg)\n\t}\n\n\treturn Error{\n\t\tCode:    int32(cCode),\n\t\tMessage: msg,\n\t}\n}\n"
  },
  {
    "path": "x/mongo/driver/mongocrypt/errors_not_enabled.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\n//go:build !cse\n\npackage mongocrypt\n\n// Error represents an error from an operation on a MongoCrypt instance.\ntype Error struct {\n\tCode    int32\n\tMessage string\n}\n\n// Error implements the error interface\nfunc (Error) Error() string {\n\tpanic(cseNotSupportedMsg)\n}\n"
  },
  {
    "path": "x/mongo/driver/mongocrypt/mongocrypt.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\n//go:build cse\n\npackage mongocrypt\n\n// #cgo linux solaris darwin pkg-config: libmongocrypt\n// #cgo windows CFLAGS: -I\"c:/libmongocrypt/include\"\n// #cgo windows LDFLAGS: -lmongocrypt -Lc:/libmongocrypt/bin\n// #include <mongocrypt.h>\n// #include <stdlib.h>\nimport \"C\"\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"time\"\n\t\"unsafe\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/httputil\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/auth/creds\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/mongocrypt/options\"\n)\n\ntype kmsProvider interface {\n\tGetCredentialsDoc(context.Context) (bsoncore.Document, error)\n}\n\ntype MongoCrypt struct {\n\twrapped      *C.mongocrypt_t\n\tkmsProviders map[string]kmsProvider\n\thttpClient   *http.Client\n}\n\n// Version returns the version string for the loaded libmongocrypt, or an empty string\n// if libmongocrypt was not loaded.\nfunc Version() string {\n\tstr := C.GoString(C.mongocrypt_version(nil))\n\treturn str\n}\n\n// NewMongoCrypt constructs a new MongoCrypt instance configured using the provided MongoCryptOptions.\nfunc NewMongoCrypt(opts *options.MongoCryptOptions) (*MongoCrypt, error) {\n\t// create mongocrypt_t handle\n\twrapped := C.mongocrypt_new()\n\tif wrapped == nil {\n\t\treturn nil, errors.New(\"could not create new mongocrypt object\")\n\t}\n\tC.mongocrypt_setopt_retry_kms(wrapped, true)\n\thttpClient := opts.HTTPClient\n\tif httpClient == nil {\n\t\thttpClient = httputil.DefaultHTTPClient\n\t}\n\tkmsProviders := make(map[string]kmsProvider)\n\tif needsKmsProvider(opts.KmsProviders, \"gcp\") {\n\t\tkmsProviders[\"gcp\"] = creds.NewGCPCredentialProvider(httpClient)\n\t}\n\tif needsKmsProvider(opts.KmsProviders, \"aws\") {\n\t\tkmsProviders[\"aws\"] = creds.NewAWSCredentialProvider(httpClient)\n\t}\n\tif needsKmsProvider(opts.KmsProviders, \"azure\") {\n\t\tkmsProviders[\"azure\"] = creds.NewAzureCredentialProvider(httpClient)\n\t}\n\tcrypt := &MongoCrypt{\n\t\twrapped:      wrapped,\n\t\tkmsProviders: kmsProviders,\n\t\thttpClient:   httpClient,\n\t}\n\n\t// set options in mongocrypt\n\tif err := crypt.setProviderOptions(opts.KmsProviders); err != nil {\n\t\treturn nil, err\n\t}\n\tif err := crypt.setLocalSchemaMap(opts.LocalSchemaMap); err != nil {\n\t\treturn nil, err\n\t}\n\tif err := crypt.setEncryptedFieldsMap(opts.EncryptedFieldsMap); err != nil {\n\t\treturn nil, err\n\t}\n\n\tif opts.BypassQueryAnalysis {\n\t\tC.mongocrypt_setopt_bypass_query_analysis(crypt.wrapped)\n\t}\n\n\tvar keyExpirationMs uint64 = 60_000 // 60,000 ms\n\tif opts.KeyExpiration != nil {\n\t\tif *opts.KeyExpiration <= 0 {\n\t\t\tkeyExpirationMs = 0\n\t\t} else {\n\t\t\t// find the ceiling integer millisecond for the expiration\n\t\t\tkeyExpirationMs = uint64((*opts.KeyExpiration + time.Millisecond - 1) / time.Millisecond)\n\t\t}\n\t}\n\tC.mongocrypt_setopt_key_expiration(crypt.wrapped, C.uint64_t(keyExpirationMs))\n\n\t// If loading the crypt_shared library isn't disabled, set the default library search path \"$SYSTEM\"\n\t// and set a library override path if one was provided.\n\tif !opts.CryptSharedLibDisabled {\n\t\tsystemStr := C.CString(\"$SYSTEM\")\n\t\tdefer C.free(unsafe.Pointer(systemStr))\n\t\tC.mongocrypt_setopt_append_crypt_shared_lib_search_path(crypt.wrapped, systemStr)\n\n\t\tif opts.CryptSharedLibOverridePath != \"\" {\n\t\t\tcryptSharedLibOverridePathStr := C.CString(opts.CryptSharedLibOverridePath)\n\t\t\tdefer C.free(unsafe.Pointer(cryptSharedLibOverridePathStr))\n\t\t\tC.mongocrypt_setopt_set_crypt_shared_lib_path_override(crypt.wrapped, cryptSharedLibOverridePathStr)\n\t\t}\n\t}\n\n\tC.mongocrypt_setopt_use_need_kms_credentials_state(crypt.wrapped)\n\n\t// initialize handle\n\tif !C.mongocrypt_init(crypt.wrapped) {\n\t\treturn nil, crypt.createErrorFromStatus()\n\t}\n\n\treturn crypt, nil\n}\n\n// CreateEncryptionContext creates a Context to use for encryption.\nfunc (m *MongoCrypt) CreateEncryptionContext(db string, cmd bsoncore.Document) (*Context, error) {\n\tctx := newContext(C.mongocrypt_ctx_new(m.wrapped))\n\tif ctx.wrapped == nil {\n\t\treturn nil, m.createErrorFromStatus()\n\t}\n\n\tcmdBinary := newBinaryFromBytes(cmd)\n\tdefer cmdBinary.close()\n\tdbStr := C.CString(db)\n\tdefer C.free(unsafe.Pointer(dbStr))\n\n\tif ok := C.mongocrypt_ctx_encrypt_init(ctx.wrapped, dbStr, C.int32_t(-1), cmdBinary.wrapped); !ok {\n\t\treturn nil, ctx.createErrorFromStatus()\n\t}\n\treturn ctx, nil\n}\n\n// CreateDecryptionContext creates a Context to use for decryption.\nfunc (m *MongoCrypt) CreateDecryptionContext(cmd bsoncore.Document) (*Context, error) {\n\tctx := newContext(C.mongocrypt_ctx_new(m.wrapped))\n\tif ctx.wrapped == nil {\n\t\treturn nil, m.createErrorFromStatus()\n\t}\n\n\tcmdBinary := newBinaryFromBytes(cmd)\n\tdefer cmdBinary.close()\n\n\tif ok := C.mongocrypt_ctx_decrypt_init(ctx.wrapped, cmdBinary.wrapped); !ok {\n\t\treturn nil, ctx.createErrorFromStatus()\n\t}\n\treturn ctx, nil\n}\n\n// lookupString returns a string for the value corresponding to the given key in the document.\n// if the key does not exist or the value is not a string, the empty string is returned.\nfunc lookupString(doc bsoncore.Document, key string) string {\n\tstrVal, _ := doc.Lookup(key).StringValueOK()\n\treturn strVal\n}\n\nfunc setAltName(ctx *Context, altName string) error {\n\t// create document {\"keyAltName\": keyAltName}\n\tidx, doc := bsoncore.AppendDocumentStart(nil)\n\tdoc = bsoncore.AppendStringElement(doc, \"keyAltName\", altName)\n\tdoc, _ = bsoncore.AppendDocumentEnd(doc, idx)\n\n\tkeyAltBinary := newBinaryFromBytes(doc)\n\tdefer keyAltBinary.close()\n\n\tif ok := C.mongocrypt_ctx_setopt_key_alt_name(ctx.wrapped, keyAltBinary.wrapped); !ok {\n\t\treturn ctx.createErrorFromStatus()\n\t}\n\treturn nil\n}\n\nfunc setKeyMaterial(ctx *Context, keyMaterial []byte) error {\n\t// Create document {\"keyMaterial\": keyMaterial} using the generic binary sybtype 0x00.\n\tidx, doc := bsoncore.AppendDocumentStart(nil)\n\tdoc = bsoncore.AppendBinaryElement(doc, \"keyMaterial\", 0x00, keyMaterial)\n\tdoc, err := bsoncore.AppendDocumentEnd(doc, idx)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tkeyMaterialBinary := newBinaryFromBytes(doc)\n\tdefer keyMaterialBinary.close()\n\n\tif ok := C.mongocrypt_ctx_setopt_key_material(ctx.wrapped, keyMaterialBinary.wrapped); !ok {\n\t\treturn ctx.createErrorFromStatus()\n\t}\n\treturn nil\n}\n\nfunc rewrapDataKey(ctx *Context, filter []byte) error {\n\tfilterBinary := newBinaryFromBytes(filter)\n\tdefer filterBinary.close()\n\n\tif ok := C.mongocrypt_ctx_rewrap_many_datakey_init(ctx.wrapped, filterBinary.wrapped); !ok {\n\t\treturn ctx.createErrorFromStatus()\n\t}\n\treturn nil\n}\n\n// CreateDataKeyContext creates a Context to use for creating a data key.\nfunc (m *MongoCrypt) CreateDataKeyContext(kmsProvider string, opts *options.DataKeyOptions) (*Context, error) {\n\tctx := newContext(C.mongocrypt_ctx_new(m.wrapped))\n\tif ctx.wrapped == nil {\n\t\treturn nil, m.createErrorFromStatus()\n\t}\n\n\t// Create a masterKey document of the form { \"provider\": <provider string>, other options... }.\n\tvar masterKey bsoncore.Document\n\tswitch {\n\tcase opts.MasterKey != nil:\n\t\t// The original key passed into the top-level API was already transformed into a raw BSON document and passed\n\t\t// down to here, so we can modify it without copying. Remove the terminating byte to add the \"provider\" field.\n\t\tmasterKey = opts.MasterKey[:len(opts.MasterKey)-1]\n\t\tmasterKey = bsoncore.AppendStringElement(masterKey, \"provider\", kmsProvider)\n\t\tmasterKey, _ = bsoncore.AppendDocumentEnd(masterKey, 0)\n\tdefault:\n\t\tmasterKey = bsoncore.NewDocumentBuilder().AppendString(\"provider\", kmsProvider).Build()\n\t}\n\n\tmasterKeyBinary := newBinaryFromBytes(masterKey)\n\tdefer masterKeyBinary.close()\n\n\tif ok := C.mongocrypt_ctx_setopt_key_encryption_key(ctx.wrapped, masterKeyBinary.wrapped); !ok {\n\t\treturn nil, ctx.createErrorFromStatus()\n\t}\n\n\tfor _, altName := range opts.KeyAltNames {\n\t\tif err := setAltName(ctx, altName); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\tif opts.KeyMaterial != nil {\n\t\tif err := setKeyMaterial(ctx, opts.KeyMaterial); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\tif ok := C.mongocrypt_ctx_datakey_init(ctx.wrapped); !ok {\n\t\treturn nil, ctx.createErrorFromStatus()\n\t}\n\treturn ctx, nil\n}\n\nconst (\n\tIndexTypeUnindexed = 1\n\tIndexTypeIndexed   = 2\n)\n\n// createExplicitEncryptionContext creates an explicit encryption context.\nfunc (m *MongoCrypt) createExplicitEncryptionContext(opts *options.ExplicitEncryptionOptions) (*Context, error) {\n\tctx := newContext(C.mongocrypt_ctx_new(m.wrapped))\n\tif ctx.wrapped == nil {\n\t\treturn nil, m.createErrorFromStatus()\n\t}\n\n\tif opts.KeyID != nil {\n\t\tkeyIDBinary := newBinaryFromBytes(opts.KeyID.Data)\n\t\tdefer keyIDBinary.close()\n\n\t\tif ok := C.mongocrypt_ctx_setopt_key_id(ctx.wrapped, keyIDBinary.wrapped); !ok {\n\t\t\treturn nil, ctx.createErrorFromStatus()\n\t\t}\n\t}\n\tif opts.KeyAltName != nil {\n\t\tif err := setAltName(ctx, *opts.KeyAltName); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\tif opts.RangeOptions != nil {\n\t\tidx, mongocryptDoc := bsoncore.AppendDocumentStart(nil)\n\t\tif opts.RangeOptions.Min != nil {\n\t\t\tmongocryptDoc = bsoncore.AppendValueElement(mongocryptDoc, \"min\", *opts.RangeOptions.Min)\n\t\t}\n\t\tif opts.RangeOptions.Max != nil {\n\t\t\tmongocryptDoc = bsoncore.AppendValueElement(mongocryptDoc, \"max\", *opts.RangeOptions.Max)\n\t\t}\n\t\tif opts.RangeOptions.Precision != nil {\n\t\t\tmongocryptDoc = bsoncore.AppendInt32Element(mongocryptDoc, \"precision\", *opts.RangeOptions.Precision)\n\t\t}\n\t\tif opts.RangeOptions.Sparsity != nil {\n\t\t\tmongocryptDoc = bsoncore.AppendInt64Element(mongocryptDoc, \"sparsity\", *opts.RangeOptions.Sparsity)\n\t\t}\n\t\tif opts.RangeOptions.TrimFactor != nil {\n\t\t\tmongocryptDoc = bsoncore.AppendInt32Element(mongocryptDoc, \"trimFactor\", *opts.RangeOptions.TrimFactor)\n\t\t}\n\n\t\tmongocryptDoc, err := bsoncore.AppendDocumentEnd(mongocryptDoc, idx)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tmongocryptBinary := newBinaryFromBytes(mongocryptDoc)\n\t\tdefer mongocryptBinary.close()\n\n\t\tif ok := C.mongocrypt_ctx_setopt_algorithm_range(ctx.wrapped, mongocryptBinary.wrapped); !ok {\n\t\t\treturn nil, ctx.createErrorFromStatus()\n\t\t}\n\t}\n\n\tif opts.TextOptions != nil {\n\t\tidx, mongocryptDoc := bsoncore.AppendDocumentStart(nil)\n\t\tif opts.TextOptions.Substring != nil {\n\t\t\tsubstringIdx, substringDoc := bsoncore.AppendDocumentStart(nil)\n\t\t\tsubstringDoc = bsoncore.AppendInt32Element(substringDoc, \"strMaxLength\", opts.TextOptions.Substring.StrMaxLength)\n\t\t\tsubstringDoc = bsoncore.AppendInt32Element(substringDoc, \"strMinQueryLength\", opts.TextOptions.Substring.StrMinQueryLength)\n\t\t\tsubstringDoc = bsoncore.AppendInt32Element(substringDoc, \"strMaxQueryLength\", opts.TextOptions.Substring.StrMaxQueryLength)\n\t\t\tsubstringDoc, err := bsoncore.AppendDocumentEnd(substringDoc, substringIdx)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"error building substring doc: %w\", err)\n\t\t\t}\n\t\t\tmongocryptDoc = bsoncore.AppendDocumentElement(mongocryptDoc, \"substring\", substringDoc)\n\t\t}\n\t\tif opts.TextOptions.Prefix != nil {\n\t\t\tprefixIdx, prefixDoc := bsoncore.AppendDocumentStart(nil)\n\t\t\tprefixDoc = bsoncore.AppendInt32Element(prefixDoc, \"strMinQueryLength\", opts.TextOptions.Prefix.StrMinQueryLength)\n\t\t\tprefixDoc = bsoncore.AppendInt32Element(prefixDoc, \"strMaxQueryLength\", opts.TextOptions.Prefix.StrMaxQueryLength)\n\t\t\tprefixDoc, err := bsoncore.AppendDocumentEnd(prefixDoc, prefixIdx)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"error building prefix doc: %w\", err)\n\t\t\t}\n\t\t\tmongocryptDoc = bsoncore.AppendDocumentElement(mongocryptDoc, \"prefix\", prefixDoc)\n\t\t}\n\t\tif opts.TextOptions.Suffix != nil {\n\t\t\tsuffixIdx, suffixDoc := bsoncore.AppendDocumentStart(nil)\n\t\t\tsuffixDoc = bsoncore.AppendInt32Element(suffixDoc, \"strMinQueryLength\", opts.TextOptions.Suffix.StrMinQueryLength)\n\t\t\tsuffixDoc = bsoncore.AppendInt32Element(suffixDoc, \"strMaxQueryLength\", opts.TextOptions.Suffix.StrMaxQueryLength)\n\t\t\tsuffixDoc, err := bsoncore.AppendDocumentEnd(suffixDoc, suffixIdx)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"error building suffix doc: %w\", err)\n\t\t\t}\n\t\t\tmongocryptDoc = bsoncore.AppendDocumentElement(mongocryptDoc, \"suffix\", suffixDoc)\n\t\t}\n\t\tmongocryptDoc = bsoncore.AppendBooleanElement(mongocryptDoc, \"caseSensitive\", opts.TextOptions.CaseSensitive)\n\t\tmongocryptDoc = bsoncore.AppendBooleanElement(mongocryptDoc, \"diacriticSensitive\", opts.TextOptions.DiacriticSensitive)\n\n\t\tmongocryptDoc, err := bsoncore.AppendDocumentEnd(mongocryptDoc, idx)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"error building text options doc: %w\", err)\n\t\t}\n\n\t\tmongocryptBinary := newBinaryFromBytes(mongocryptDoc)\n\t\tdefer mongocryptBinary.close()\n\n\t\tif ok := C.mongocrypt_ctx_setopt_algorithm_text(ctx.wrapped, mongocryptBinary.wrapped); !ok {\n\t\t\treturn nil, fmt.Errorf(\"error setting text algorithm option: %w\", ctx.createErrorFromStatus())\n\t\t}\n\t}\n\n\talgoStr := C.CString(opts.Algorithm)\n\tdefer C.free(unsafe.Pointer(algoStr))\n\n\tif ok := C.mongocrypt_ctx_setopt_algorithm(ctx.wrapped, algoStr, -1); !ok {\n\t\treturn nil, ctx.createErrorFromStatus()\n\t}\n\n\tif opts.QueryType != \"\" {\n\t\tqueryStr := C.CString(opts.QueryType)\n\t\tdefer C.free(unsafe.Pointer(queryStr))\n\t\tif ok := C.mongocrypt_ctx_setopt_query_type(ctx.wrapped, queryStr, -1); !ok {\n\t\t\treturn nil, ctx.createErrorFromStatus()\n\t\t}\n\t}\n\n\tif opts.ContentionFactor != nil {\n\t\tif ok := C.mongocrypt_ctx_setopt_contention_factor(ctx.wrapped, C.int64_t(*opts.ContentionFactor)); !ok {\n\t\t\treturn nil, ctx.createErrorFromStatus()\n\t\t}\n\t}\n\treturn ctx, nil\n}\n\n// CreateExplicitEncryptionContext creates a Context to use for explicit encryption.\nfunc (m *MongoCrypt) CreateExplicitEncryptionContext(doc bsoncore.Document, opts *options.ExplicitEncryptionOptions) (*Context, error) {\n\tctx, err := m.createExplicitEncryptionContext(opts)\n\tif err != nil {\n\t\treturn ctx, err\n\t}\n\tdocBinary := newBinaryFromBytes(doc)\n\tdefer docBinary.close()\n\tif ok := C.mongocrypt_ctx_explicit_encrypt_init(ctx.wrapped, docBinary.wrapped); !ok {\n\t\treturn nil, ctx.createErrorFromStatus()\n\t}\n\n\treturn ctx, nil\n}\n\n// CreateExplicitEncryptionExpressionContext creates a Context to use for explicit encryption of an expression.\nfunc (m *MongoCrypt) CreateExplicitEncryptionExpressionContext(doc bsoncore.Document, opts *options.ExplicitEncryptionOptions) (*Context, error) {\n\tctx, err := m.createExplicitEncryptionContext(opts)\n\tif err != nil {\n\t\treturn ctx, err\n\t}\n\tdocBinary := newBinaryFromBytes(doc)\n\tdefer docBinary.close()\n\tif ok := C.mongocrypt_ctx_explicit_encrypt_expression_init(ctx.wrapped, docBinary.wrapped); !ok {\n\t\treturn nil, ctx.createErrorFromStatus()\n\t}\n\n\treturn ctx, nil\n}\n\n// CreateExplicitDecryptionContext creates a Context to use for explicit decryption.\nfunc (m *MongoCrypt) CreateExplicitDecryptionContext(doc bsoncore.Document) (*Context, error) {\n\tctx := newContext(C.mongocrypt_ctx_new(m.wrapped))\n\tif ctx.wrapped == nil {\n\t\treturn nil, m.createErrorFromStatus()\n\t}\n\n\tdocBinary := newBinaryFromBytes(doc)\n\tdefer docBinary.close()\n\n\tif ok := C.mongocrypt_ctx_explicit_decrypt_init(ctx.wrapped, docBinary.wrapped); !ok {\n\t\treturn nil, ctx.createErrorFromStatus()\n\t}\n\treturn ctx, nil\n}\n\n// CryptSharedLibVersion returns the version number for the loaded crypt_shared library, or 0 if the\n// crypt_shared library was not loaded.\nfunc (m *MongoCrypt) CryptSharedLibVersion() uint64 {\n\treturn uint64(C.mongocrypt_crypt_shared_lib_version(m.wrapped))\n}\n\n// CryptSharedLibVersionString returns the version string for the loaded crypt_shared library, or an\n// empty string if the crypt_shared library was not loaded.\nfunc (m *MongoCrypt) CryptSharedLibVersionString() string {\n\t// Pass in a pointer for \"len\", but ignore the value because C.GoString can determine the string\n\t// length without it.\n\tlen := C.uint(0)\n\tstr := C.GoString(C.mongocrypt_crypt_shared_lib_version_string(m.wrapped, &len))\n\treturn str\n}\n\n// Close cleans up any resources associated with the given MongoCrypt instance.\nfunc (m *MongoCrypt) Close() {\n\tC.mongocrypt_destroy(m.wrapped)\n\tif m.httpClient == httputil.DefaultHTTPClient {\n\t\thttputil.CloseIdleHTTPConnections(m.httpClient)\n\t}\n}\n\n// RewrapDataKeyContext create a Context to use for rewrapping a data key.\nfunc (m *MongoCrypt) RewrapDataKeyContext(filter []byte, opts *options.RewrapManyDataKeyOptions) (*Context, error) {\n\tconst masterKey = \"masterKey\"\n\tconst providerKey = \"provider\"\n\n\tctx := newContext(C.mongocrypt_ctx_new(m.wrapped))\n\tif ctx.wrapped == nil {\n\t\treturn nil, m.createErrorFromStatus()\n\t}\n\n\tif opts.MasterKey != nil && opts.Provider == nil {\n\t\t// Provider is nil, but MasterKey is set. This is an error.\n\t\treturn nil, fmt.Errorf(\"expected 'Provider' to be set to identify type of 'MasterKey'\")\n\t}\n\n\tif opts.Provider != nil {\n\t\t// If a provider has been specified, create an encryption key document for creating a data key or for rewrapping\n\t\t// datakeys. If a new provider is not specified, then the filter portion of this logic returns the data as it\n\t\t// exists in the collection.\n\t\tidx, mongocryptDoc := bsoncore.AppendDocumentStart(nil)\n\t\tmongocryptDoc = bsoncore.AppendStringElement(mongocryptDoc, providerKey, *opts.Provider)\n\n\t\tif opts.MasterKey != nil {\n\t\t\tmongocryptDoc = opts.MasterKey[:len(opts.MasterKey)-1]\n\t\t\tmongocryptDoc = bsoncore.AppendStringElement(mongocryptDoc, providerKey, *opts.Provider)\n\t\t}\n\n\t\tmongocryptDoc, err := bsoncore.AppendDocumentEnd(mongocryptDoc, idx)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tmongocryptBinary := newBinaryFromBytes(mongocryptDoc)\n\t\tdefer mongocryptBinary.close()\n\n\t\t// Add new masterKey to the mongocrypt context.\n\t\tif ok := C.mongocrypt_ctx_setopt_key_encryption_key(ctx.wrapped, mongocryptBinary.wrapped); !ok {\n\t\t\treturn nil, ctx.createErrorFromStatus()\n\t\t}\n\t}\n\n\treturn ctx, rewrapDataKey(ctx, filter)\n}\n\nfunc (m *MongoCrypt) setProviderOptions(kmsProviders bsoncore.Document) error {\n\tprovidersBinary := newBinaryFromBytes(kmsProviders)\n\tdefer providersBinary.close()\n\n\tif ok := C.mongocrypt_setopt_kms_providers(m.wrapped, providersBinary.wrapped); !ok {\n\t\treturn m.createErrorFromStatus()\n\t}\n\treturn nil\n}\n\n// setLocalSchemaMap sets the local schema map in mongocrypt.\nfunc (m *MongoCrypt) setLocalSchemaMap(schemaMap map[string]bsoncore.Document) error {\n\tif len(schemaMap) == 0 {\n\t\treturn nil\n\t}\n\n\t// convert schema map to BSON document\n\tschemaMapBSON, err := bson.Marshal(schemaMap)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"error marshalling SchemaMap: %v\", err)\n\t}\n\n\tschemaMapBinary := newBinaryFromBytes(schemaMapBSON)\n\tdefer schemaMapBinary.close()\n\n\tif ok := C.mongocrypt_setopt_schema_map(m.wrapped, schemaMapBinary.wrapped); !ok {\n\t\treturn m.createErrorFromStatus()\n\t}\n\treturn nil\n}\n\n// setEncryptedFieldsMap sets the encryptedfields map in mongocrypt.\nfunc (m *MongoCrypt) setEncryptedFieldsMap(encryptedfieldsMap map[string]bsoncore.Document) error {\n\tif len(encryptedfieldsMap) == 0 {\n\t\treturn nil\n\t}\n\n\t// convert encryptedfields map to BSON document\n\tencryptedfieldsMapBSON, err := bson.Marshal(encryptedfieldsMap)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"error marshalling EncryptedFieldsMap: %v\", err)\n\t}\n\n\tencryptedfieldsMapBinary := newBinaryFromBytes(encryptedfieldsMapBSON)\n\tdefer encryptedfieldsMapBinary.close()\n\n\tif ok := C.mongocrypt_setopt_encrypted_field_config_map(m.wrapped, encryptedfieldsMapBinary.wrapped); !ok {\n\t\treturn m.createErrorFromStatus()\n\t}\n\treturn nil\n}\n\n// createErrorFromStatus creates a new Error based on the status of the MongoCrypt instance.\nfunc (m *MongoCrypt) createErrorFromStatus() error {\n\tstatus := C.mongocrypt_status_new()\n\tdefer C.mongocrypt_status_destroy(status)\n\tC.mongocrypt_status(m.wrapped, status)\n\treturn errorFromStatus(status)\n}\n\n// needsKmsProvider returns true if provider was initially set to an empty document.\n// An empty document signals the driver to fetch credentials.\nfunc needsKmsProvider(kmsProviders bsoncore.Document, provider string) bool {\n\tval, err := kmsProviders.LookupErr(provider)\n\tif err != nil {\n\t\t// KMS provider is not configured.\n\t\treturn false\n\t}\n\tdoc, ok := val.DocumentOK()\n\t// KMS provider is an empty document if the length is 5.\n\t// An empty document contains 4 bytes of \"\\x00\" and a null byte.\n\treturn ok && len(doc) == 5\n}\n\n// GetKmsProviders attempts to obtain credentials from environment.\n// It is expected to be called when a libmongocrypt context is in the mongocrypt.NeedKmsCredentials state.\nfunc (m *MongoCrypt) GetKmsProviders(ctx context.Context) (bsoncore.Document, error) {\n\tbuilder := bsoncore.NewDocumentBuilder()\n\tfor k, p := range m.kmsProviders {\n\t\tdoc, err := p.GetCredentialsDoc(ctx)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"unable to retrieve %s credentials: %w\", k, err)\n\t\t}\n\t\tbuilder.AppendDocument(k, doc)\n\t}\n\treturn builder.Build(), nil\n}\n"
  },
  {
    "path": "x/mongo/driver/mongocrypt/mongocrypt_context.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\n//go:build cse\n\npackage mongocrypt\n\n// #include <mongocrypt.h>\nimport \"C\"\n\nimport (\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n)\n\n// Context represents a mongocrypt_ctx_t handle\ntype Context struct {\n\twrapped *C.mongocrypt_ctx_t\n}\n\n// newContext creates a Context wrapper around the given C type.\nfunc newContext(wrapped *C.mongocrypt_ctx_t) *Context {\n\treturn &Context{\n\t\twrapped: wrapped,\n\t}\n}\n\n// State returns the current State of the Context.\nfunc (c *Context) State() State {\n\treturn State(int(C.mongocrypt_ctx_state(c.wrapped)))\n}\n\n// NextOperation gets the document for the next database operation to run.\nfunc (c *Context) NextOperation() (bsoncore.Document, error) {\n\topDocBinary := newBinary() // out param for mongocrypt_ctx_mongo_op to fill in operation\n\tdefer opDocBinary.close()\n\n\tif ok := C.mongocrypt_ctx_mongo_op(c.wrapped, opDocBinary.wrapped); !ok {\n\t\treturn nil, c.createErrorFromStatus()\n\t}\n\treturn opDocBinary.toBytes(), nil\n}\n\n// AddOperationResult feeds the result of a database operation to mongocrypt.\nfunc (c *Context) AddOperationResult(result bsoncore.Document) error {\n\tresultBinary := newBinaryFromBytes(result)\n\tdefer resultBinary.close()\n\n\tif ok := C.mongocrypt_ctx_mongo_feed(c.wrapped, resultBinary.wrapped); !ok {\n\t\treturn c.createErrorFromStatus()\n\t}\n\treturn nil\n}\n\n// CompleteOperation signals a database operation has been completed.\nfunc (c *Context) CompleteOperation() error {\n\tif ok := C.mongocrypt_ctx_mongo_done(c.wrapped); !ok {\n\t\treturn c.createErrorFromStatus()\n\t}\n\treturn nil\n}\n\n// NextKmsContext returns the next KmsContext, or nil if there are no more.\nfunc (c *Context) NextKmsContext() *KmsContext {\n\tctx := C.mongocrypt_ctx_next_kms_ctx(c.wrapped)\n\tif ctx == nil {\n\t\treturn nil\n\t}\n\treturn newKmsContext(ctx)\n}\n\n// FinishKmsContexts signals that all KMS contexts have been completed.\nfunc (c *Context) FinishKmsContexts() error {\n\tif ok := C.mongocrypt_ctx_kms_done(c.wrapped); !ok {\n\t\treturn c.createErrorFromStatus()\n\t}\n\treturn nil\n}\n\n// Finish performs the final operations for the context and returns the resulting document.\nfunc (c *Context) Finish() (bsoncore.Document, error) {\n\tdocBinary := newBinary() // out param for mongocrypt_ctx_finalize to fill in resulting document\n\tdefer docBinary.close()\n\n\tif ok := C.mongocrypt_ctx_finalize(c.wrapped, docBinary.wrapped); !ok {\n\t\treturn nil, c.createErrorFromStatus()\n\t}\n\treturn docBinary.toBytes(), nil\n}\n\n// Close cleans up any resources associated with the given Context instance.\nfunc (c *Context) Close() {\n\tC.mongocrypt_ctx_destroy(c.wrapped)\n}\n\n// createErrorFromStatus creates a new Error based on the status of the MongoCrypt instance.\nfunc (c *Context) createErrorFromStatus() error {\n\tstatus := C.mongocrypt_status_new()\n\tdefer C.mongocrypt_status_destroy(status)\n\tC.mongocrypt_ctx_status(c.wrapped, status)\n\treturn errorFromStatus(status)\n}\n\n// ProvideKmsProviders provides the KMS providers when in the NeedKmsCredentials state.\nfunc (c *Context) ProvideKmsProviders(kmsProviders bsoncore.Document) error {\n\tkmsProvidersBinary := newBinaryFromBytes(kmsProviders)\n\tdefer kmsProvidersBinary.close()\n\n\tif ok := C.mongocrypt_ctx_provide_kms_providers(c.wrapped, kmsProvidersBinary.wrapped); !ok {\n\t\treturn c.createErrorFromStatus()\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "x/mongo/driver/mongocrypt/mongocrypt_context_not_enabled.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\n//go:build !cse\n\npackage mongocrypt\n\nimport (\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n)\n\n// Context represents a mongocrypt_ctx_t handle\ntype Context struct{}\n\n// State returns the current State of the Context.\nfunc (c *Context) State() State {\n\tpanic(cseNotSupportedMsg)\n}\n\n// NextOperation gets the document for the next database operation to run.\nfunc (c *Context) NextOperation() (bsoncore.Document, error) {\n\tpanic(cseNotSupportedMsg)\n}\n\n// AddOperationResult feeds the result of a database operation to mongocrypt.\nfunc (c *Context) AddOperationResult(bsoncore.Document) error {\n\tpanic(cseNotSupportedMsg)\n}\n\n// CompleteOperation signals a database operation has been completed.\nfunc (c *Context) CompleteOperation() error {\n\tpanic(cseNotSupportedMsg)\n}\n\n// NextKmsContext returns the next KmsContext, or nil if there are no more.\nfunc (c *Context) NextKmsContext() *KmsContext {\n\tpanic(cseNotSupportedMsg)\n}\n\n// FinishKmsContexts signals that all KMS contexts have been completed.\nfunc (c *Context) FinishKmsContexts() error {\n\tpanic(cseNotSupportedMsg)\n}\n\n// Finish performs the final operations for the context and returns the resulting document.\nfunc (c *Context) Finish() (bsoncore.Document, error) {\n\tpanic(cseNotSupportedMsg)\n}\n\n// Close cleans up any resources associated with the given Context instance.\nfunc (c *Context) Close() {\n\tpanic(cseNotSupportedMsg)\n}\n\n// ProvideKmsProviders provides the KMS providers when in the NeedKmsCredentials state.\nfunc (c *Context) ProvideKmsProviders(bsoncore.Document) error {\n\tpanic(cseNotSupportedMsg)\n}\n"
  },
  {
    "path": "x/mongo/driver/mongocrypt/mongocrypt_kms_context.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\n//go:build cse\n\npackage mongocrypt\n\n// #include <mongocrypt.h>\nimport \"C\"\nimport \"time\"\n\n// KmsContext represents a mongocrypt_kms_ctx_t handle.\ntype KmsContext struct {\n\twrapped *C.mongocrypt_kms_ctx_t\n}\n\n// newKmsContext creates a KmsContext wrapper around the given C type.\nfunc newKmsContext(wrapped *C.mongocrypt_kms_ctx_t) *KmsContext {\n\treturn &KmsContext{\n\t\twrapped: wrapped,\n\t}\n}\n\n// HostName gets the host name of the KMS.\nfunc (kc *KmsContext) HostName() (string, error) {\n\tvar hostname *C.char // out param for mongocrypt function to fill in hostname\n\tif ok := C.mongocrypt_kms_ctx_endpoint(kc.wrapped, &hostname); !ok {\n\t\treturn \"\", kc.createErrorFromStatus()\n\t}\n\treturn C.GoString(hostname), nil\n}\n\n// KMSProvider gets the KMS provider of the KMS context.\nfunc (kc *KmsContext) KMSProvider() string {\n\tkmsProvider := C.mongocrypt_kms_ctx_get_kms_provider(kc.wrapped, nil)\n\treturn C.GoString(kmsProvider)\n}\n\n// Message returns the message to send to the KMS.\nfunc (kc *KmsContext) Message() ([]byte, error) {\n\ttime.Sleep(time.Duration(C.mongocrypt_kms_ctx_usleep(kc.wrapped)) * time.Microsecond)\n\n\tmsgBinary := newBinary()\n\tdefer msgBinary.close()\n\n\tif ok := C.mongocrypt_kms_ctx_message(kc.wrapped, msgBinary.wrapped); !ok {\n\t\treturn nil, kc.createErrorFromStatus()\n\t}\n\treturn msgBinary.toBytes(), nil\n}\n\n// BytesNeeded returns the number of bytes that should be received from the KMS.\n// After sending the message to the KMS, this message should be called in a loop until the number returned is 0.\nfunc (kc *KmsContext) BytesNeeded() int32 {\n\treturn int32(C.mongocrypt_kms_ctx_bytes_needed(kc.wrapped))\n}\n\n// FeedResponse feeds the bytes received from the KMS to mongocrypt.\nfunc (kc *KmsContext) FeedResponse(response []byte) error {\n\tresponseBinary := newBinaryFromBytes(response)\n\tdefer responseBinary.close()\n\n\tif ok := C.mongocrypt_kms_ctx_feed(kc.wrapped, responseBinary.wrapped); !ok {\n\t\treturn kc.createErrorFromStatus()\n\t}\n\treturn nil\n}\n\n// createErrorFromStatus creates a new Error from the status of the KmsContext instance.\nfunc (kc *KmsContext) createErrorFromStatus() error {\n\tstatus := C.mongocrypt_status_new()\n\tdefer C.mongocrypt_status_destroy(status)\n\tC.mongocrypt_kms_ctx_status(kc.wrapped, status)\n\treturn errorFromStatus(status)\n}\n\n// RequestError returns the source of the network error for KMS requests.\nfunc (kc *KmsContext) RequestError() error {\n\tif bool(C.mongocrypt_kms_ctx_fail(kc.wrapped)) {\n\t\treturn nil\n\t}\n\treturn kc.createErrorFromStatus()\n}\n"
  },
  {
    "path": "x/mongo/driver/mongocrypt/mongocrypt_kms_context_not_enabled.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\n//go:build !cse\n\npackage mongocrypt\n\n// KmsContext represents a mongocrypt_kms_ctx_t handle.\ntype KmsContext struct{}\n\n// HostName gets the host name of the KMS.\nfunc (kc *KmsContext) HostName() (string, error) {\n\tpanic(cseNotSupportedMsg)\n}\n\n// Message returns the message to send to the KMS.\nfunc (kc *KmsContext) Message() ([]byte, error) {\n\tpanic(cseNotSupportedMsg)\n}\n\n// KMSProvider gets the KMS provider of the KMS context.\nfunc (kc *KmsContext) KMSProvider() string {\n\tpanic(cseNotSupportedMsg)\n}\n\n// BytesNeeded returns the number of bytes that should be received from the KMS.\n// After sending the message to the KMS, this message should be called in a loop until the number returned is 0.\nfunc (kc *KmsContext) BytesNeeded() int32 {\n\tpanic(cseNotSupportedMsg)\n}\n\n// FeedResponse feeds the bytes received from the KMS to mongocrypt.\nfunc (kc *KmsContext) FeedResponse([]byte) error {\n\tpanic(cseNotSupportedMsg)\n}\n\n// RequestError returns the source of the network error for KMS requests.\nfunc (kc *KmsContext) RequestError() error {\n\tpanic(cseNotSupportedMsg)\n}\n"
  },
  {
    "path": "x/mongo/driver/mongocrypt/mongocrypt_not_enabled.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\n//go:build !cse\n\n// Package mongocrypt is intended for internal use only. It is made available to\n// facilitate use cases that require access to internal MongoDB driver\n// functionality and state. The API of this package is not stable and there is\n// no backward compatibility guarantee.\n//\n// WARNING: THIS PACKAGE IS EXPERIMENTAL AND MAY BE MODIFIED OR REMOVED WITHOUT\n// NOTICE! USE WITH EXTREME CAUTION!\npackage mongocrypt\n\nimport (\n\t\"context\"\n\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/mongocrypt/options\"\n)\n\nconst cseNotSupportedMsg = \"client-side encryption not enabled. add the cse build tag to support\"\n\n// MongoCrypt represents a mongocrypt_t handle.\ntype MongoCrypt struct{}\n\n// Version returns the version string for the loaded libmongocrypt, or an empty string\n// if libmongocrypt was not loaded.\nfunc Version() string {\n\treturn \"\"\n}\n\n// NewMongoCrypt constructs a new MongoCrypt instance configured using the provided MongoCryptOptions.\nfunc NewMongoCrypt(*options.MongoCryptOptions) (*MongoCrypt, error) {\n\tpanic(cseNotSupportedMsg)\n}\n\n// CreateEncryptionContext creates a Context to use for encryption.\nfunc (m *MongoCrypt) CreateEncryptionContext(string, bsoncore.Document) (*Context, error) {\n\tpanic(cseNotSupportedMsg)\n}\n\n// CreateExplicitEncryptionExpressionContext creates a Context to use for explicit encryption of an expression.\nfunc (m *MongoCrypt) CreateExplicitEncryptionExpressionContext(bsoncore.Document, *options.ExplicitEncryptionOptions) (*Context, error) {\n\tpanic(cseNotSupportedMsg)\n}\n\n// CreateDecryptionContext creates a Context to use for decryption.\nfunc (m *MongoCrypt) CreateDecryptionContext(bsoncore.Document) (*Context, error) {\n\tpanic(cseNotSupportedMsg)\n}\n\n// CreateDataKeyContext creates a Context to use for creating a data key.\nfunc (m *MongoCrypt) CreateDataKeyContext(string, *options.DataKeyOptions) (*Context, error) {\n\tpanic(cseNotSupportedMsg)\n}\n\n// CreateExplicitEncryptionContext creates a Context to use for explicit encryption.\nfunc (m *MongoCrypt) CreateExplicitEncryptionContext(bsoncore.Document, *options.ExplicitEncryptionOptions) (*Context, error) {\n\tpanic(cseNotSupportedMsg)\n}\n\n// RewrapDataKeyContext creates a Context to use for rewrapping a data key.\nfunc (m *MongoCrypt) RewrapDataKeyContext([]byte, *options.RewrapManyDataKeyOptions) (*Context, error) {\n\tpanic(cseNotSupportedMsg)\n}\n\n// CreateExplicitDecryptionContext creates a Context to use for explicit decryption.\nfunc (m *MongoCrypt) CreateExplicitDecryptionContext(bsoncore.Document) (*Context, error) {\n\tpanic(cseNotSupportedMsg)\n}\n\n// CryptSharedLibVersion returns the version number for the loaded crypt_shared library, or 0 if the\n// crypt_shared library was not loaded.\nfunc (m *MongoCrypt) CryptSharedLibVersion() uint64 {\n\tpanic(cseNotSupportedMsg)\n}\n\n// CryptSharedLibVersionString returns the version string for the loaded crypt_shared library, or an\n// empty string if the crypt_shared library was not loaded.\nfunc (m *MongoCrypt) CryptSharedLibVersionString() string {\n\tpanic(cseNotSupportedMsg)\n}\n\n// Close cleans up any resources associated with the given MongoCrypt instance.\nfunc (m *MongoCrypt) Close() {\n\tpanic(cseNotSupportedMsg)\n}\n\n// GetKmsProviders returns the originally configured KMS providers.\nfunc (m *MongoCrypt) GetKmsProviders(context.Context) (bsoncore.Document, error) {\n\tpanic(cseNotSupportedMsg)\n}\n"
  },
  {
    "path": "x/mongo/driver/mongocrypt/mongocrypt_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\n//go:build cse\n\npackage mongocrypt\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t\"io/ioutil\"\n\t\"os\"\n\t\"path\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/mongocrypt/options\"\n)\n\nconst resourcesDir = \"../../../../testdata/mongocrypt\"\n\nfunc noerr(t *testing.T, err error) {\n\tif err != nil {\n\t\tt.Helper()\n\t\tt.Errorf(\"Unexpected error: (%T)%v\", err, err)\n\t\tt.FailNow()\n\t}\n}\n\nfunc compareStates(t *testing.T, expected, actual State) {\n\tt.Helper()\n\tif expected != actual {\n\t\tt.Fatalf(\"state mismatch; expected %s, got %s\", expected, actual)\n\t}\n}\n\nfunc compareResources(t *testing.T, expected, actual bsoncore.Document) {\n\tt.Helper()\n\tif !bytes.Equal(expected, actual) {\n\t\tt.Fatalf(\"resource mismatch; expected %v, got %v\", expected, actual)\n\t}\n}\n\nfunc createMongoCrypt(t *testing.T) *MongoCrypt {\n\tt.Helper()\n\n\tawsProvider := bsoncore.NewDocumentBuilder().\n\t\tAppendString(\"accessKeyId\", \"example\").\n\t\tAppendString(\"secretAccessKey\", \"example\").\n\t\tBuild()\n\tlocalProvider := bsoncore.NewDocumentBuilder().\n\t\tAppendBinary(\"key\", 0, make([]byte, 96)).\n\t\tBuild()\n\tkmsProviders := bsoncore.NewDocumentBuilder().\n\t\tAppendDocument(\"aws\", awsProvider).\n\t\tAppendDocument(\"local\", localProvider).\n\t\tBuild()\n\n\tcryptOpts := &options.MongoCryptOptions{\n\t\tKmsProviders: kmsProviders,\n\t}\n\tcrypt, err := NewMongoCrypt(cryptOpts)\n\tnoerr(t, err)\n\tif crypt == nil {\n\t\tt.Fatalf(\"expected MongoCrypt instance but got nil\")\n\t}\n\treturn crypt\n}\n\nfunc resourceToDocument(t *testing.T, filename string) bsoncore.Document {\n\tt.Helper()\n\tfilepath := path.Join(resourcesDir, filename)\n\tcontent, err := ioutil.ReadFile(filepath)\n\tnoerr(t, err)\n\n\tvar doc bsoncore.Document\n\tnoerr(t, bson.UnmarshalExtJSON(content, false, &doc))\n\treturn doc\n}\n\nfunc httpResponseToBytes(t *testing.T, filename string) []byte {\n\tt.Helper()\n\tfile, err := os.Open(path.Join(resourcesDir, filename))\n\tnoerr(t, err)\n\tdefer func() {\n\t\t_ = file.Close()\n\t}()\n\n\tfirstLine := true\n\tvar fileStr string\n\tscanner := bufio.NewScanner(file)\n\tfor scanner.Scan() {\n\t\tif !firstLine {\n\t\t\tfileStr += \"\\r\\n\"\n\t\t}\n\t\tfirstLine = false\n\t\tfileStr += scanner.Text()\n\t}\n\tnoerr(t, scanner.Err())\n\n\treturn []byte(fileStr)\n}\n\n// iterate a context in the NeedKms state\nfunc testKmsCtx(t *testing.T, ctx *Context, keyAltName bool) {\n\t// get op to send to key vault\n\tkeyFilter, err := ctx.NextOperation()\n\tnoerr(t, err)\n\tfilterFile := \"key-filter.json\"\n\tif keyAltName {\n\t\tfilterFile = \"key-filter-keyAltName.json\"\n\t}\n\tcompareResources(t, resourceToDocument(t, filterFile), keyFilter)\n\n\t// feed result and finish op\n\tnoerr(t, ctx.AddOperationResult(resourceToDocument(t, \"key-document.json\")))\n\tnoerr(t, ctx.CompleteOperation())\n\tcompareStates(t, NeedKms, ctx.State())\n\n\t// verify KMS hostname\n\tkmsCtx := ctx.NextKmsContext()\n\thostname, err := kmsCtx.HostName()\n\tnoerr(t, err)\n\n\t// TODO GODRIVER-2217: Simply check if hostname != expectedHost once all OSes build the latest libmongocrypt\n\t// TODO versions.\n\t//\n\t// Only check for the hostname. libmongocrypt versions that do not include MONGOCRYPT-352 will not\n\t// include the default port \"443\".\n\texpectedHost := \"kms.us-east-1.amazonaws.com\"\n\tif !strings.Contains(hostname, expectedHost) {\n\t\tt.Fatalf(\"hostname mismatch; expected %s to contain %s\", hostname, expectedHost)\n\t}\n\n\t// get message to send to KMS\n\tkmsMsg, err := kmsCtx.Message()\n\tnoerr(t, err)\n\tif len(kmsMsg) != 790 {\n\t\tt.Fatalf(\"message length mismatch; expected 790, got %d\", len(kmsMsg))\n\t}\n\n\t// feed mock KMS response\n\tbytesNeeded := kmsCtx.BytesNeeded()\n\tif bytesNeeded != 1024 {\n\t\tt.Fatalf(\"number of bytes mismatch; expected 1024, got %d\", bytesNeeded)\n\t}\n\tnoerr(t, kmsCtx.FeedResponse(httpResponseToBytes(t, \"kms-reply.txt\")))\n\tbytesNeeded = kmsCtx.BytesNeeded()\n\tif bytesNeeded != 0 {\n\t\tt.Fatalf(\"number of bytes mismatch; expected 0, got %d\", bytesNeeded)\n\t}\n\n\t// verify that there are no more KMS contexts left\n\tkmsCtx = ctx.NextKmsContext()\n\tif kmsCtx != nil {\n\t\tt.Fatalf(\"expected nil but got a KmsContext\")\n\t}\n\tnoerr(t, ctx.FinishKmsContexts())\n}\n\nfunc TestMongoCrypt(t *testing.T) {\n\tt.Run(\"encrypt\", func(t *testing.T) {\n\t\tt.Run(\"remote schema\", func(t *testing.T) {\n\t\t\tcrypt := createMongoCrypt(t)\n\t\t\tdefer crypt.Close()\n\n\t\t\t// create encryption context and check initial state\n\t\t\tcmdDoc := resourceToDocument(t, \"command.json\")\n\t\t\tencryptCtx, err := crypt.CreateEncryptionContext(\"test\", cmdDoc)\n\t\t\tnoerr(t, err)\n\t\t\tdefer encryptCtx.Close()\n\t\t\tcompareStates(t, NeedMongoCollInfo, encryptCtx.State())\n\n\t\t\t// get listCollections op\n\t\t\tlistCollFilter, err := encryptCtx.NextOperation()\n\t\t\tnoerr(t, err)\n\t\t\tcompareResources(t, resourceToDocument(t, \"list-collections-filter.json\"), listCollFilter)\n\n\t\t\t// feed result and finish op\n\t\t\tnoerr(t, encryptCtx.AddOperationResult(resourceToDocument(t, \"collection-info.json\")))\n\t\t\tnoerr(t, encryptCtx.CompleteOperation())\n\t\t\tcompareStates(t, NeedMongoMarkings, encryptCtx.State())\n\n\t\t\t// get mongocryptd op\n\t\t\tmongocryptdCmd, err := encryptCtx.NextOperation()\n\t\t\tnoerr(t, err)\n\t\t\tcompareResources(t, resourceToDocument(t, \"mongocryptd-command-remote.json\"), mongocryptdCmd)\n\n\t\t\t// feed result and finish op\n\t\t\tnoerr(t, encryptCtx.AddOperationResult(resourceToDocument(t, \"mongocryptd-reply.json\")))\n\t\t\tnoerr(t, encryptCtx.CompleteOperation())\n\t\t\tcompareStates(t, NeedMongoKeys, encryptCtx.State())\n\n\t\t\t// mock KMS communication and iterate encryptCtx\n\t\t\ttestKmsCtx(t, encryptCtx, false)\n\t\t\tcompareStates(t, Ready, encryptCtx.State())\n\n\t\t\t// perform final encryption\n\t\t\tencryptedDoc, err := encryptCtx.Finish()\n\t\t\tnoerr(t, err)\n\t\t\tcompareResources(t, resourceToDocument(t, \"encrypted-command.json\"), encryptedDoc)\n\t\t})\n\t\tt.Run(\"local schema\", func(t *testing.T) {\n\t\t\t// take schema from collection info and create MongoCrypt instance\n\t\t\tcollInfo := resourceToDocument(t, \"collection-info.json\")\n\t\t\tschema := collInfo.Lookup(\"options\", \"validator\", \"$jsonSchema\").Document()\n\t\t\tschemaMap := map[string]bsoncore.Document{\n\t\t\t\t\"test.test\": schema,\n\t\t\t}\n\t\t\tkmsProviders := bsoncore.NewDocumentBuilder().\n\t\t\t\tStartDocument(\"aws\").\n\t\t\t\tAppendString(\"accessKeyId\", \"example\").\n\t\t\t\tAppendString(\"secretAccessKey\", \"example\").\n\t\t\t\tFinishDocument().\n\t\t\t\tBuild()\n\t\t\tcryptOpts := &options.MongoCryptOptions{\n\t\t\t\tKmsProviders:   kmsProviders,\n\t\t\t\tLocalSchemaMap: schemaMap,\n\t\t\t}\n\t\t\tcrypt, err := NewMongoCrypt(cryptOpts)\n\t\t\tnoerr(t, err)\n\t\t\tdefer crypt.Close()\n\n\t\t\t// create encryption context and check initial state\n\t\t\tencryptCtx, err := crypt.CreateEncryptionContext(\"test\", resourceToDocument(t, \"command.json\"))\n\t\t\tnoerr(t, err)\n\t\t\tdefer encryptCtx.Close()\n\t\t\tcompareStates(t, NeedMongoMarkings, encryptCtx.State())\n\n\t\t\t// get mongocryptd op\n\t\t\tmongocryptdCmd, err := encryptCtx.NextOperation()\n\t\t\tnoerr(t, err)\n\t\t\tcompareResources(t, resourceToDocument(t, \"mongocryptd-command-local.json\"), mongocryptdCmd)\n\n\t\t\t// feed result and finish op\n\t\t\tnoerr(t, encryptCtx.AddOperationResult(resourceToDocument(t, \"mongocryptd-reply.json\")))\n\t\t\tnoerr(t, encryptCtx.CompleteOperation())\n\t\t\tcompareStates(t, NeedMongoKeys, encryptCtx.State())\n\n\t\t\t// mock KMS communication and iterate encryptCtx\n\t\t\ttestKmsCtx(t, encryptCtx, false)\n\t\t\tcompareStates(t, Ready, encryptCtx.State())\n\n\t\t\t// perform final encryption\n\t\t\tencryptedDoc, err := encryptCtx.Finish()\n\t\t\tnoerr(t, err)\n\t\t\tcompareResources(t, resourceToDocument(t, \"encrypted-command.json\"), encryptedDoc)\n\t\t})\n\t\tt.Run(\"invalid bson\", func(t *testing.T) {\n\t\t\tcrypt := createMongoCrypt(t)\n\t\t\tdefer crypt.Close()\n\n\t\t\t_, err := crypt.CreateEncryptionContext(\"test\", []byte{0x1, 0x2, 0x3})\n\t\t\tif err == nil {\n\t\t\t\tt.Fatalf(\"expected error creating encryption context for invalid BSON but got nil\")\n\t\t\t}\n\t\t\tif _, ok := err.(Error); !ok {\n\t\t\t\tt.Fatalf(\"error type mismatch; expected Error, got %v\", err)\n\t\t\t}\n\t\t})\n\t})\n\tt.Run(\"decrypt\", func(t *testing.T) {\n\t\tcrypt := createMongoCrypt(t)\n\t\tdefer crypt.Close()\n\n\t\t// create decryption context and check initial state\n\t\tdecryptCtx, err := crypt.CreateDecryptionContext(resourceToDocument(t, \"encrypted-command-reply.json\"))\n\t\tnoerr(t, err)\n\t\tdefer decryptCtx.Close()\n\t\tcompareStates(t, NeedMongoKeys, decryptCtx.State())\n\n\t\t// mock KMS communication and iterate decryptCtx\n\t\ttestKmsCtx(t, decryptCtx, false)\n\t\tcompareStates(t, Ready, decryptCtx.State())\n\n\t\t// perform final decryption\n\t\tdecryptedDoc, err := decryptCtx.Finish()\n\t\tnoerr(t, err)\n\t\tcompareResources(t, resourceToDocument(t, \"command-reply.json\"), decryptedDoc)\n\t})\n\tt.Run(\"data key creation\", func(t *testing.T) {\n\t\tcrypt := createMongoCrypt(t)\n\t\tdefer crypt.Close()\n\n\t\t// create master key document\n\t\tvar midx int32\n\t\tvar masterKey bsoncore.Document\n\t\tmidx, masterKey = bsoncore.AppendDocumentStart(nil)\n\t\tmasterKey, _ = bsoncore.AppendDocumentEnd(masterKey, midx)\n\n\t\t// create data key context and check initial state\n\t\tdataKeyOpts := &options.DataKeyOptions{MasterKey: masterKey}\n\t\tdataKeyCtx, err := crypt.CreateDataKeyContext(\"local\", dataKeyOpts)\n\t\tnoerr(t, err)\n\t\tdefer dataKeyCtx.Close()\n\t\tcompareStates(t, Ready, dataKeyCtx.State())\n\n\t\t// create data key\n\t\tdataKeyDoc, err := dataKeyCtx.Finish()\n\t\tnoerr(t, err)\n\t\tif len(dataKeyDoc) == 0 {\n\t\t\tt.Fatalf(\"expected data key document but got empty doc\")\n\t\t}\n\t\tcompareStates(t, Done, dataKeyCtx.State())\n\t})\n\tt.Run(\"explicit roundtrip\", func(t *testing.T) {\n\t\t// algorithm to use for encryption/decryption\n\t\talgorithm := \"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic\"\n\t\t// doc to encrypt\n\t\tidx, originalDoc := bsoncore.AppendDocumentStart(nil)\n\t\toriginalDoc = bsoncore.AppendStringElement(originalDoc, \"v\", \"hello\")\n\t\toriginalDoc, _ = bsoncore.AppendDocumentEnd(originalDoc, idx)\n\n\t\tt.Run(\"no keyAltName\", func(t *testing.T) {\n\t\t\tcrypt := createMongoCrypt(t)\n\t\t\tdefer crypt.Close()\n\n\t\t\t// create explicit encryption context and check initial state\n\t\t\tkeyID := bson.Binary{\n\t\t\t\tSubtype: 0x04, // 0x04 is UUID subtype\n\t\t\t\tData:    []byte(\"aaaaaaaaaaaaaaaa\"),\n\t\t\t}\n\t\t\topts := &options.ExplicitEncryptionOptions{KeyID: &keyID, Algorithm: algorithm}\n\t\t\tencryptCtx, err := crypt.CreateExplicitEncryptionContext(originalDoc, opts)\n\t\t\tnoerr(t, err)\n\t\t\tdefer encryptCtx.Close()\n\t\t\tcompareStates(t, NeedMongoKeys, encryptCtx.State())\n\n\t\t\t// mock KMS communication and iterate encryptCtx\n\t\t\ttestKmsCtx(t, encryptCtx, false)\n\t\t\tcompareStates(t, Ready, encryptCtx.State())\n\n\t\t\t// perform final encryption\n\t\t\tencryptedDoc, err := encryptCtx.Finish()\n\t\t\tnoerr(t, err)\n\t\t\tcompareStates(t, Done, encryptCtx.State())\n\t\t\tcompareResources(t, resourceToDocument(t, \"encrypted-value.json\"), encryptedDoc)\n\n\t\t\t// create explicit decryption context and check initial state\n\t\t\tdecryptCtx, err := crypt.CreateDecryptionContext(encryptedDoc)\n\t\t\tnoerr(t, err)\n\t\t\tdefer decryptCtx.Close()\n\t\t\tcompareStates(t, Ready, decryptCtx.State())\n\n\t\t\t// perform final decryption\n\t\t\tdecryptedDoc, err := decryptCtx.Finish()\n\t\t\tnoerr(t, err)\n\t\t\tcompareStates(t, Done, decryptCtx.State())\n\t\t\tcompareResources(t, originalDoc, decryptedDoc)\n\t\t})\n\t\tt.Run(\"keyAltName\", func(t *testing.T) {\n\t\t\tcrypt := createMongoCrypt(t)\n\t\t\tdefer crypt.Close()\n\n\t\t\t// create explicit encryption context and check initial state\n\t\t\tkeyAltName := \"altKeyName\"\n\t\t\topts := &options.ExplicitEncryptionOptions{KeyAltName: &keyAltName, Algorithm: algorithm}\n\t\t\tencryptCtx, err := crypt.CreateExplicitEncryptionContext(originalDoc, opts)\n\t\t\tnoerr(t, err)\n\t\t\tdefer encryptCtx.Close()\n\t\t\tcompareStates(t, NeedMongoKeys, encryptCtx.State())\n\n\t\t\t// mock KMS communication and iterate encryptCtx\n\t\t\ttestKmsCtx(t, encryptCtx, true)\n\t\t\tcompareStates(t, Ready, encryptCtx.State())\n\n\t\t\t// perform final encryption\n\t\t\tencryptedDoc, err := encryptCtx.Finish()\n\t\t\tnoerr(t, err)\n\t\t\tcompareStates(t, Done, encryptCtx.State())\n\t\t\tcompareResources(t, resourceToDocument(t, \"encrypted-value.json\"), encryptedDoc)\n\n\t\t\t// create explicit decryption context and check initial state\n\t\t\t// the cryptCtx should automatically be in the Ready state because the key should be cached from the\n\t\t\t// encryption process.\n\t\t\tdecryptCtx, err := crypt.CreateExplicitDecryptionContext(encryptedDoc)\n\t\t\tnoerr(t, err)\n\t\t\tdefer decryptCtx.Close()\n\t\t\tcompareStates(t, Ready, decryptCtx.State())\n\n\t\t\t// perform final decryption\n\t\t\tdecryptedDoc, err := decryptCtx.Finish()\n\t\t\tnoerr(t, err)\n\t\t\tcompareStates(t, Done, decryptCtx.State())\n\t\t\tcompareResources(t, originalDoc, decryptedDoc)\n\t\t})\n\t})\n}\n"
  },
  {
    "path": "x/mongo/driver/mongocrypt/options/doc.go",
    "content": "// Copyright (C) MongoDB, Inc. 2024-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\n// Package options is intended for internal use only. It is made available to\n// facilitate use cases that require access to internal MongoDB driver\n// functionality and state. The API of this package is not stable and there is\n// no backward compatibility guarantee.\n//\n// WARNING: THIS PACKAGE IS EXPERIMENTAL AND MAY BE MODIFIED OR REMOVED WITHOUT\n// NOTICE! USE WITH EXTREME CAUTION!\npackage options\n"
  },
  {
    "path": "x/mongo/driver/mongocrypt/options/mongocrypt_context_options.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage options\n\nimport (\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n)\n\n// DataKeyOptions specifies options for creating a new data key.\ntype DataKeyOptions struct {\n\tKeyAltNames []string\n\tKeyMaterial []byte\n\tMasterKey   bsoncore.Document\n}\n\n// QueryType describes the type of query the result of Encrypt is used for.\ntype QueryType int\n\n// These constants specify valid values for QueryType\nconst (\n\tQueryTypeEquality QueryType = 1\n)\n\n// ExplicitEncryptionOptions specifies options for configuring an explicit encryption context.\ntype ExplicitEncryptionOptions struct {\n\tKeyID            *bson.Binary\n\tKeyAltName       *string\n\tAlgorithm        string\n\tQueryType        string\n\tContentionFactor *int64\n\tRangeOptions     *ExplicitRangeOptions\n\tTextOptions      *ExplicitTextOptions\n}\n\n// ExplicitRangeOptions specifies options for the range index.\ntype ExplicitRangeOptions struct {\n\tMin        *bsoncore.Value\n\tMax        *bsoncore.Value\n\tSparsity   *int64\n\tTrimFactor *int32\n\tPrecision  *int32\n}\n\n// ExplicitTextOptions specifies options for the text query.\ntype ExplicitTextOptions struct {\n\tSubstring          *SubstringOptions\n\tPrefix             *PrefixOptions\n\tSuffix             *SuffixOptions\n\tCaseSensitive      bool\n\tDiacriticSensitive bool\n}\n\n// SubstringOptions specifies options to support substring queries.\ntype SubstringOptions struct {\n\tStrMaxLength      int32\n\tStrMinQueryLength int32\n\tStrMaxQueryLength int32\n}\n\n// PrefixOptions specifies options to support prefix queries.\ntype PrefixOptions struct {\n\tStrMinQueryLength int32\n\tStrMaxQueryLength int32\n}\n\n// SuffixOptions specifies options to support suffix queries.\ntype SuffixOptions struct {\n\tStrMinQueryLength int32\n\tStrMaxQueryLength int32\n}\n\n// ExplicitEncryption creates a new ExplicitEncryptionOptions instance.\nfunc ExplicitEncryption() *ExplicitEncryptionOptions {\n\treturn &ExplicitEncryptionOptions{}\n}\n\n// SetKeyID sets the key identifier.\nfunc (eeo *ExplicitEncryptionOptions) SetKeyID(keyID bson.Binary) *ExplicitEncryptionOptions {\n\teeo.KeyID = &keyID\n\treturn eeo\n}\n\n// SetKeyAltName sets the key alternative name.\nfunc (eeo *ExplicitEncryptionOptions) SetKeyAltName(keyAltName string) *ExplicitEncryptionOptions {\n\teeo.KeyAltName = &keyAltName\n\treturn eeo\n}\n\n// SetAlgorithm specifies an encryption algorithm.\nfunc (eeo *ExplicitEncryptionOptions) SetAlgorithm(algorithm string) *ExplicitEncryptionOptions {\n\teeo.Algorithm = algorithm\n\treturn eeo\n}\n\n// SetQueryType specifies the query type.\nfunc (eeo *ExplicitEncryptionOptions) SetQueryType(queryType string) *ExplicitEncryptionOptions {\n\teeo.QueryType = queryType\n\treturn eeo\n}\n\n// SetContentionFactor specifies the contention factor.\nfunc (eeo *ExplicitEncryptionOptions) SetContentionFactor(contentionFactor int64) *ExplicitEncryptionOptions {\n\teeo.ContentionFactor = &contentionFactor\n\treturn eeo\n}\n\n// SetRangeOptions specifies the range options.\nfunc (eeo *ExplicitEncryptionOptions) SetRangeOptions(ro ExplicitRangeOptions) *ExplicitEncryptionOptions {\n\teeo.RangeOptions = &ro\n\treturn eeo\n}\n\n// SetTextOptions specifies the text options.\nfunc (eeo *ExplicitEncryptionOptions) SetTextOptions(to ExplicitTextOptions) *ExplicitEncryptionOptions {\n\teeo.TextOptions = &to\n\treturn eeo\n}\n\n// RewrapManyDataKeyOptions represents all possible options used to decrypt and encrypt all matching data keys with a\n// possibly new masterKey.\ntype RewrapManyDataKeyOptions struct {\n\t// Provider identifies the new KMS provider. If omitted, encrypting uses the current KMS provider.\n\tProvider *string\n\n\t// MasterKey identifies the new masterKey. If omitted, rewraps with the current masterKey.\n\tMasterKey bsoncore.Document\n}\n"
  },
  {
    "path": "x/mongo/driver/mongocrypt/options/mongocrypt_options.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage options\n\nimport (\n\t\"net/http\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n)\n\n// MongoCryptOptions specifies options to configure a MongoCrypt instance.\ntype MongoCryptOptions struct {\n\tKmsProviders               bsoncore.Document\n\tLocalSchemaMap             map[string]bsoncore.Document\n\tBypassQueryAnalysis        bool\n\tEncryptedFieldsMap         map[string]bsoncore.Document\n\tCryptSharedLibDisabled     bool\n\tCryptSharedLibOverridePath string\n\tHTTPClient                 *http.Client\n\tKeyExpiration              *time.Duration\n}\n"
  },
  {
    "path": "x/mongo/driver/mongocrypt/state.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage mongocrypt\n\n// State represents a state that a MongocryptContext can be in.\ntype State int\n\n// These constants are valid values for the State type.\n// The values must match the values defined in the mongocrypt_ctx_state_t enum in libmongocrypt.\nconst (\n\tStateError         State = 0\n\tNeedMongoCollInfo  State = 1\n\tNeedMongoMarkings  State = 2\n\tNeedMongoKeys      State = 3\n\tNeedKms            State = 4\n\tReady              State = 5\n\tDone               State = 6\n\tNeedKmsCredentials State = 7\n)\n\n// String implements the Stringer interface.\nfunc (s State) String() string {\n\tswitch s {\n\tcase StateError:\n\t\treturn \"Error\"\n\tcase NeedMongoCollInfo:\n\t\treturn \"NeedMongoCollInfo\"\n\tcase NeedMongoMarkings:\n\t\treturn \"NeedMongoMarkings\"\n\tcase NeedMongoKeys:\n\t\treturn \"NeedMongoKeys\"\n\tcase NeedKms:\n\t\treturn \"NeedKms\"\n\tcase Ready:\n\t\treturn \"Ready\"\n\tcase Done:\n\t\treturn \"Done\"\n\tcase NeedKmsCredentials:\n\t\treturn \"NeedKmsCredentials\"\n\tdefault:\n\t\treturn \"Unknown State\"\n\t}\n}\n"
  },
  {
    "path": "x/mongo/driver/ocsp/cache.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage ocsp\n\nimport (\n\t\"crypto\"\n\t\"sync\"\n\t\"time\"\n\n\t\"golang.org/x/crypto/ocsp\"\n)\n\ntype cacheKey struct {\n\tHashAlgorithm  crypto.Hash\n\tIssuerNameHash string\n\tIssuerKeyHash  string\n\tSerialNumber   string\n}\n\n// Cache represents an OCSP cache.\ntype Cache interface {\n\tUpdate(*ocsp.Request, *ResponseDetails) *ResponseDetails\n\tGet(request *ocsp.Request) *ResponseDetails\n}\n\n// ConcurrentCache is an implementation of ocsp.Cache that's safe for concurrent use.\ntype ConcurrentCache struct {\n\tcache map[cacheKey]*ResponseDetails\n\tsync.Mutex\n}\n\nvar _ Cache = (*ConcurrentCache)(nil)\n\n// NewCache creates an empty OCSP cache.\nfunc NewCache() *ConcurrentCache {\n\treturn &ConcurrentCache{\n\t\tcache: make(map[cacheKey]*ResponseDetails),\n\t}\n}\n\n// Update updates the cache entry for the provided request. The provided response will only be cached if it has a\n// status that is not ocsp.Unknown and has a non-zero NextUpdate time. If there is an existing cache entry for request,\n// it will be overwritten by response if response.NextUpdate is further ahead in the future than the existing entry's\n// NextUpdate.\n//\n// This function returns the most up-to-date response corresponding to the request.\nfunc (c *ConcurrentCache) Update(request *ocsp.Request, response *ResponseDetails) *ResponseDetails {\n\tunknown := response.Status == ocsp.Unknown\n\thasUpdateTime := !response.NextUpdate.IsZero()\n\tcanBeCached := !unknown && hasUpdateTime\n\tkey := createCacheKey(request)\n\n\tc.Lock()\n\tdefer c.Unlock()\n\n\tcurrent, ok := c.cache[key]\n\tif !ok {\n\t\tif canBeCached {\n\t\t\tc.cache[key] = response\n\t\t}\n\n\t\t// Return the provided response even though it might not have been cached because it's the most up-to-date\n\t\t// response available.\n\t\treturn response\n\t}\n\n\t// If the new response is Unknown, we can't cache it. Return the existing cached response.\n\tif unknown {\n\t\treturn current\n\t}\n\n\t// If a response has no nextUpdate set, the responder is telling us that newer information is always available.\n\t// In this case, remove the existing cache entry because it is stale and return the new response because it is\n\t// more up-to-date.\n\tif !hasUpdateTime {\n\t\tdelete(c.cache, key)\n\t\treturn response\n\t}\n\n\t// If we get here, the new response is conclusive and has a non-empty nextUpdate so it can be cached. Overwrite\n\t// the existing cache entry if the new one will be valid for longer.\n\tnewest := current\n\tif response.NextUpdate.After(current.NextUpdate) {\n\t\tc.cache[key] = response\n\t\tnewest = response\n\t}\n\treturn newest\n}\n\n// Get returns the cached response for the request, or nil if there is no cached response. If the cached response has\n// expired, it will be removed from the cache and nil will be returned.\nfunc (c *ConcurrentCache) Get(request *ocsp.Request) *ResponseDetails {\n\tkey := createCacheKey(request)\n\n\tc.Lock()\n\tdefer c.Unlock()\n\n\tresponse, ok := c.cache[key]\n\tif !ok {\n\t\treturn nil\n\t}\n\n\tif time.Now().UTC().Before(response.NextUpdate) {\n\t\treturn response\n\t}\n\tdelete(c.cache, key)\n\treturn nil\n}\n\nfunc createCacheKey(request *ocsp.Request) cacheKey {\n\treturn cacheKey{\n\t\tHashAlgorithm:  request.HashAlgorithm,\n\t\tIssuerNameHash: string(request.IssuerNameHash),\n\t\tIssuerKeyHash:  string(request.IssuerKeyHash),\n\t\tSerialNumber:   request.SerialNumber.String(),\n\t}\n}\n"
  },
  {
    "path": "x/mongo/driver/ocsp/cache_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage ocsp\n\nimport (\n\t\"context\"\n\t\"crypto\"\n\t\"crypto/tls\"\n\t\"errors\"\n\t\"testing\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"golang.org/x/crypto/ocsp\"\n)\n\nvar ctx = context.Background()\n\nfunc TestCache(t *testing.T) {\n\ttestRequest := &ocsp.Request{\n\t\tHashAlgorithm:  crypto.SHA1,\n\t\tIssuerNameHash: []byte(\"issuerNameHash\"),\n\t\tIssuerKeyHash:  []byte(\"issuerKeyHash\"),\n\t}\n\ttestRequestKey := createCacheKey(testRequest)\n\n\tt.Run(\"verification errors if cache is nil\", func(t *testing.T) {\n\t\terr := Verify(ctx, tls.ConnectionState{}, &VerifyOptions{})\n\t\tassert.NotNil(t, err, \"expected error, got nil\")\n\n\t\tvar ocspErr *Error\n\t\tassert.True(t, errors.As(err, &ocspErr), \"expected error of type %T, got %v of type %T\", &Error{}, err, err)\n\t\texpected := &Error{\n\t\t\twrapped: errors.New(\"no OCSP cache provided\"),\n\t\t}\n\t\tassert.Equal(t, expected.Error(), ocspErr.Error(), \"expected error %v, got %v\", expected, ocspErr)\n\t})\n\tt.Run(\"put\", func(t *testing.T) {\n\t\tt.Run(\"empty cache\", func(t *testing.T) {\n\t\t\tgood := &ResponseDetails{\n\t\t\t\tStatus:     ocsp.Good,\n\t\t\t\tNextUpdate: futureTime(10),\n\t\t\t}\n\t\t\trevoked := &ResponseDetails{\n\t\t\t\tStatus:     ocsp.Revoked,\n\t\t\t\tNextUpdate: futureTime(10),\n\t\t\t}\n\t\t\tunknown := &ResponseDetails{\n\t\t\t\tStatus:     ocsp.Unknown,\n\t\t\t\tNextUpdate: futureTime(10),\n\t\t\t}\n\t\t\tgoodNoUpdate := &ResponseDetails{Status: ocsp.Good}\n\t\t\trevokedNoUpdate := &ResponseDetails{Status: ocsp.Revoked}\n\t\t\tunknownNoUpdate := &ResponseDetails{Status: ocsp.Unknown}\n\n\t\t\ttestCases := []struct {\n\t\t\t\tname     string\n\t\t\t\tresponse *ResponseDetails\n\t\t\t\tcached   bool\n\t\t\t}{\n\t\t\t\t{\"good response is cached\", good, true},\n\t\t\t\t{\"revoked response is cached\", revoked, true},\n\t\t\t\t{\"good response without nextUpdate is not cached\", goodNoUpdate, false},\n\t\t\t\t{\"revoked response without nextUpdate is not cached\", revokedNoUpdate, false},\n\t\t\t\t{\"unknown response with nextUpdate is not cached\", unknown, false},\n\t\t\t\t{\"unknown response without nextUpdate is not cached\", unknownNoUpdate, false},\n\t\t\t}\n\t\t\tfor _, tc := range testCases {\n\t\t\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\t\t\tcache := NewCache()\n\t\t\t\t\tputRes := cache.Update(testRequest, tc.response)\n\t\t\t\t\t// Put should always return tc.response even if it wasn't added to the cache because it is the most\n\t\t\t\t\t// up-to-date response.\n\t\t\t\t\tassert.Equal(t, tc.response, putRes, \"expected Put to return %v, got %v\", tc.response, putRes)\n\n\t\t\t\t\tcurrent, ok := cache.cache[testRequestKey]\n\t\t\t\t\tif tc.cached {\n\t\t\t\t\t\tassert.True(t, ok, \"expected cache to contain entry but did not\")\n\t\t\t\t\t\tassert.Equal(t, tc.response, current, \"expected cache to contain %v, got %v\", tc.response, current)\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t\tassert.False(t, ok, \"expected cache to contain nil, got %v\", current)\n\t\t\t\t\tassert.Nil(t, current, \"expected cache to contain no entry, got %v\", current)\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\t\tt.Run(\"non-empty cache\", func(t *testing.T) {\n\t\t\tcachedUpdateMinutes := 10\n\t\t\toriginalCached := &ResponseDetails{\n\t\t\t\tStatus:     ocsp.Good,\n\t\t\t\tNextUpdate: futureTime(cachedUpdateMinutes),\n\t\t\t}\n\n\t\t\tunknownUpdate := &ResponseDetails{\n\t\t\t\tStatus:     ocsp.Unknown,\n\t\t\t\tNextUpdate: futureTime(cachedUpdateMinutes + 5),\n\t\t\t}\n\t\t\tgoodNoUpdate := &ResponseDetails{Status: ocsp.Good}\n\t\t\trevokedNoUpdate := &ResponseDetails{Status: ocsp.Revoked}\n\t\t\tgoodEarlierUpdate := &ResponseDetails{\n\t\t\t\tStatus:     ocsp.Good,\n\t\t\t\tNextUpdate: futureTime(cachedUpdateMinutes - 5),\n\t\t\t}\n\t\t\trevokedEarlierUpdate := &ResponseDetails{\n\t\t\t\tStatus:     ocsp.Revoked,\n\t\t\t\tNextUpdate: futureTime(cachedUpdateMinutes - 5),\n\t\t\t}\n\t\t\tgoodLaterUpdate := &ResponseDetails{\n\t\t\t\tStatus:     ocsp.Good,\n\t\t\t\tNextUpdate: futureTime(cachedUpdateMinutes + 5),\n\t\t\t}\n\t\t\trevokedLaterUpdate := &ResponseDetails{\n\t\t\t\tStatus:     ocsp.Revoked,\n\t\t\t\tNextUpdate: futureTime(cachedUpdateMinutes + 5),\n\t\t\t}\n\n\t\t\ttestCases := []struct {\n\t\t\t\tname      string\n\t\t\t\tresponse  *ResponseDetails\n\t\t\t\tputReturn *ResponseDetails\n\t\t\t\tcached    *ResponseDetails\n\t\t\t}{\n\t\t\t\t{\"unknown with later nextUpdate is not cached\", unknownUpdate, originalCached, originalCached},\n\t\t\t\t{\"good with no nextUpdate clears cache\", goodNoUpdate, goodNoUpdate, nil},\n\t\t\t\t{\"revoked with no nextUpdate clears cache\", revokedNoUpdate, revokedNoUpdate, nil},\n\t\t\t\t{\"good with earlier nextUpdate is not cached\", goodEarlierUpdate, originalCached, originalCached},\n\t\t\t\t{\"revoked with earlier nextUpdate is not cached\", revokedEarlierUpdate, originalCached, originalCached},\n\t\t\t\t{\"good with later nextUpdate is cached\", goodLaterUpdate, goodLaterUpdate, goodLaterUpdate},\n\t\t\t\t{\"revoked with later nextUpdate is cached\", revokedLaterUpdate, revokedLaterUpdate, revokedLaterUpdate},\n\t\t\t}\n\t\t\tfor _, tc := range testCases {\n\t\t\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\t\t\tcache := NewCache()\n\t\t\t\t\t_ = cache.Update(testRequest, originalCached)\n\n\t\t\t\t\tputReturn := cache.Update(testRequest, tc.response)\n\t\t\t\t\tassert.Equal(t, tc.putReturn, putReturn, \"expected Put to return %v, got %v\", tc.putReturn, putReturn)\n\n\t\t\t\t\tcurrent, ok := cache.cache[testRequestKey]\n\t\t\t\t\tif tc.cached == nil {\n\t\t\t\t\t\tassert.False(t, ok, \"expected cache to contain no entry, got %v\", current)\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\n\t\t\t\t\tassert.True(t, ok, \"expected cache to contain %v, got nil\", tc.cached)\n\t\t\t\t\tassert.Equal(t, tc.cached, current, \"expected cache to contain %v, got %v\", tc.cached, current)\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\t})\n\tt.Run(\"get\", func(t *testing.T) {\n\t\tt.Run(\"empty cache returns nil\", func(t *testing.T) {\n\t\t\tcache := NewCache()\n\t\t\tres := cache.Get(testRequest)\n\t\t\tassert.Nil(t, res, \"expected Get to return nil, got %v\", res)\n\t\t})\n\t\tt.Run(\"valid entry returned\", func(t *testing.T) {\n\t\t\tcache := NewCache()\n\t\t\toriginalResponse := &ResponseDetails{\n\t\t\t\tStatus:     ocsp.Good,\n\t\t\t\tNextUpdate: futureTime(10),\n\t\t\t}\n\t\t\tcache.cache[testRequestKey] = originalResponse\n\n\t\t\tres := cache.Get(testRequest)\n\t\t\tassert.Equal(t, originalResponse, res, \"expected Get to return %v, got %v\", originalResponse, res)\n\t\t})\n\t\tt.Run(\"expired entry is deleted\", func(t *testing.T) {\n\t\t\tcache := NewCache()\n\t\t\toriginalResponse := &ResponseDetails{\n\t\t\t\tStatus:     ocsp.Good,\n\t\t\t\tNextUpdate: futureTime(-10),\n\t\t\t}\n\t\t\tcache.cache[testRequestKey] = originalResponse\n\n\t\t\tres := cache.Get(testRequest)\n\t\t\tassert.Nil(t, res, \"expected Get to return nil, got %v\", res)\n\t\t\tcached, ok := cache.cache[testRequestKey]\n\t\t\tassert.False(t, ok, \"expected cache to contain no entry, got %v\", cached)\n\t\t})\n\t})\n}\n\nfunc futureTime(minutes int) time.Time {\n\treturn time.Now().Add(time.Duration(minutes) * time.Minute).UTC()\n}\n"
  },
  {
    "path": "x/mongo/driver/ocsp/config.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage ocsp\n\nimport (\n\t\"crypto/x509\"\n\t\"errors\"\n\t\"fmt\"\n\t\"net/http\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/httputil\"\n\t\"golang.org/x/crypto/ocsp\"\n)\n\ntype config struct {\n\tserverCert, issuer      *x509.Certificate\n\tcache                   Cache\n\tdisableEndpointChecking bool\n\tocspRequest             *ocsp.Request\n\tocspRequestBytes        []byte\n\thttpClient              *http.Client\n}\n\nfunc newConfig(certChain []*x509.Certificate, opts *VerifyOptions) (config, error) {\n\tcfg := config{\n\t\tcache:                   opts.Cache,\n\t\tdisableEndpointChecking: opts.DisableEndpointChecking,\n\t\thttpClient:              opts.HTTPClient,\n\t}\n\n\tif cfg.httpClient == nil {\n\t\tcfg.httpClient = httputil.DefaultHTTPClient\n\t}\n\n\tif len(certChain) == 0 {\n\t\treturn cfg, errors.New(\"verified certificate chain contained no certificates\")\n\t}\n\n\t// In the case where the leaf certificate and CA are the same, the chain may only contain one certificate.\n\tcfg.serverCert = certChain[0]\n\tcfg.issuer = certChain[0]\n\tif len(certChain) > 1 {\n\t\t// If the chain has multiple certificates, the one directly after the leaf should be the issuer. Use\n\t\t// CheckSignatureFrom to verify that it is the issuer.\n\t\tcfg.issuer = certChain[1]\n\n\t\tif err := cfg.serverCert.CheckSignatureFrom(cfg.issuer); err != nil {\n\t\t\terrString := \"error checking if server certificate is signed by the issuer in the verified chain: %v\"\n\t\t\treturn cfg, fmt.Errorf(errString, err)\n\t\t}\n\t}\n\n\tvar err error\n\tcfg.ocspRequestBytes, err = ocsp.CreateRequest(cfg.serverCert, cfg.issuer, nil)\n\tif err != nil {\n\t\treturn cfg, fmt.Errorf(\"error creating OCSP request: %w\", err)\n\t}\n\tcfg.ocspRequest, err = ocsp.ParseRequest(cfg.ocspRequestBytes)\n\tif err != nil {\n\t\treturn cfg, fmt.Errorf(\"error parsing OCSP request bytes: %w\", err)\n\t}\n\n\treturn cfg, nil\n}\n"
  },
  {
    "path": "x/mongo/driver/ocsp/ocsp.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\n// Package ocsp is intended for internal use only. It is made available to\n// facilitate use cases that require access to internal MongoDB driver\n// functionality and state. The API of this package is not stable and there is\n// no backward compatibility guarantee.\n//\n// WARNING: THIS PACKAGE IS EXPERIMENTAL AND MAY BE MODIFIED OR REMOVED WITHOUT\n// NOTICE! USE WITH EXTREME CAUTION!\npackage ocsp\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"crypto/tls\"\n\t\"crypto/x509\"\n\t\"crypto/x509/pkix\"\n\t\"encoding/asn1\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io/ioutil\"\n\t\"math/big\"\n\t\"net/http\"\n\t\"time\"\n\n\t\"golang.org/x/crypto/ocsp\"\n\t\"golang.org/x/sync/errgroup\"\n)\n\nvar (\n\ttlsFeatureExtensionOID = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 1, 24}\n\tmustStapleFeatureValue = big.NewInt(5)\n)\n\n// Error represents an OCSP verification error\ntype Error struct {\n\twrapped error\n}\n\n// Error implements the error interface\nfunc (e *Error) Error() string {\n\treturn fmt.Sprintf(\"OCSP verification failed: %v\", e.wrapped)\n}\n\n// Unwrap returns the underlying error.\nfunc (e *Error) Unwrap() error {\n\treturn e.wrapped\n}\n\nfunc newOCSPError(wrapped error) error {\n\treturn &Error{wrapped: wrapped}\n}\n\n// ResponseDetails contains a subset of the details needed from an OCSP response after the original response has been\n// validated.\ntype ResponseDetails struct {\n\tStatus     int\n\tNextUpdate time.Time\n}\n\nfunc extractResponseDetails(res *ocsp.Response) *ResponseDetails {\n\treturn &ResponseDetails{\n\t\tStatus:     res.Status,\n\t\tNextUpdate: res.NextUpdate,\n\t}\n}\n\n// Verify performs OCSP verification for the provided ConnectionState instance.\nfunc Verify(ctx context.Context, connState tls.ConnectionState, opts *VerifyOptions) error {\n\tif opts.Cache == nil {\n\t\t// There should always be an OCSP cache. Even if the user has specified the URI option to disable communication\n\t\t// with OCSP responders, the driver will cache any stapled responses. Requiring that the cache is non-nil\n\t\t// allows us to confirm that the cache is correctly being passed down from a higher level.\n\t\treturn newOCSPError(errors.New(\"no OCSP cache provided\"))\n\t}\n\tif len(connState.VerifiedChains) == 0 {\n\t\treturn newOCSPError(errors.New(\"no verified certificate chains reported after TLS handshake\"))\n\t}\n\n\tcertChain := connState.VerifiedChains[0]\n\tif numCerts := len(certChain); numCerts == 0 {\n\t\treturn newOCSPError(errors.New(\"verified chain contained no certificates\"))\n\t}\n\n\tocspCfg, err := newConfig(certChain, opts)\n\tif err != nil {\n\t\treturn newOCSPError(err)\n\t}\n\n\tres, err := getParsedResponse(ctx, ocspCfg, connState)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif res == nil {\n\t\t// If no response was parsed from the staple and responders, the status of the certificate is unknown, so don't\n\t\t// error.\n\t\treturn nil\n\t}\n\n\tif res.Status == ocsp.Revoked {\n\t\treturn newOCSPError(errors.New(\"certificate is revoked\"))\n\t}\n\treturn nil\n}\n\n// getParsedResponse attempts to parse a response from the stapled OCSP data or by contacting OCSP responders if no\n// staple is present.\nfunc getParsedResponse(ctx context.Context, cfg config, connState tls.ConnectionState) (*ResponseDetails, error) {\n\tstapledResponse, err := processStaple(cfg, connState.OCSPResponse)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif stapledResponse != nil {\n\t\t// If there is a staple, attempt to cache it. The cache.Update call will resolve conflicts with an existing\n\t\t// cache enry if necessary.\n\t\treturn cfg.cache.Update(cfg.ocspRequest, stapledResponse), nil\n\t}\n\tif cachedResponse := cfg.cache.Get(cfg.ocspRequest); cachedResponse != nil {\n\t\treturn cachedResponse, nil\n\t}\n\n\t// If there is no stapled or cached response, fall back to querying the responders if that functionality has not\n\t// been disabled.\n\tif cfg.disableEndpointChecking {\n\t\treturn nil, nil\n\t}\n\texternalResponse := contactResponders(ctx, cfg)\n\tif externalResponse == nil {\n\t\t// None of the responders were available.\n\t\treturn nil, nil\n\t}\n\n\t// Similar to the stapled response case above, unconditionally call Update and it will either cache the response\n\t// or resolve conflicts if a different connection has cached a response since the previous call to Get.\n\treturn cfg.cache.Update(cfg.ocspRequest, externalResponse), nil\n}\n\n// processStaple returns the OCSP response from the provided staple. An error will be returned if any of the following\n// are true:\n//\n// 1. cfg.serverCert has the Must-Staple extension but the staple is empty.\n// 2. The staple is malformed.\n// 3. The staple does not cover cfg.serverCert.\n// 4. The OCSP response has an error status.\nfunc processStaple(cfg config, staple []byte) (*ResponseDetails, error) {\n\tmustStaple, err := isMustStapleCertificate(cfg.serverCert)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// If the server has a Must-Staple certificate and the server does not present a stapled OCSP response, error.\n\tif mustStaple && len(staple) == 0 {\n\t\treturn nil, errors.New(\"server provided a certificate with the Must-Staple extension but did not \" +\n\t\t\t\"provide a stapled OCSP response\")\n\t}\n\n\tif len(staple) == 0 {\n\t\treturn nil, nil\n\t}\n\n\tparsedResponse, err := ocsp.ParseResponseForCert(staple, cfg.serverCert, cfg.issuer)\n\tif err != nil {\n\t\t// If the stapled response could not be parsed correctly, error. This can happen if the response is malformed,\n\t\t// the response does not cover the certificate presented by the server, or if the response contains an error\n\t\t// status.\n\t\treturn nil, fmt.Errorf(\"error parsing stapled response: %w\", err)\n\t}\n\tif err = verifyResponse(cfg, parsedResponse); err != nil {\n\t\treturn nil, fmt.Errorf(\"error validating stapled response: %w\", err)\n\t}\n\n\treturn extractResponseDetails(parsedResponse), nil\n}\n\n// isMustStapleCertificate determines whether or not an X509 certificate is a must-staple certificate.\nfunc isMustStapleCertificate(cert *x509.Certificate) (bool, error) {\n\tvar featureExtension pkix.Extension\n\tvar foundExtension bool\n\tfor _, ext := range cert.Extensions {\n\t\tif ext.Id.Equal(tlsFeatureExtensionOID) {\n\t\t\tfeatureExtension = ext\n\t\t\tfoundExtension = true\n\t\t\tbreak\n\t\t}\n\t}\n\tif !foundExtension {\n\t\treturn false, nil\n\t}\n\n\t// The value for the TLS feature extension is a sequence of integers. Per the asn1.Unmarshal documentation, an\n\t// integer can be unmarshalled into an int, int32, int64, or *big.Int and unmarshalling will error if the integer\n\t// cannot be encoded into the target type.\n\t//\n\t// Use []*big.Int to ensure that all values in the sequence can be successfully unmarshalled.\n\tvar featureValues []*big.Int\n\tif _, err := asn1.Unmarshal(featureExtension.Value, &featureValues); err != nil {\n\t\treturn false, fmt.Errorf(\"error unmarshalling TLS feature extension values: %w\", err)\n\t}\n\n\tfor _, value := range featureValues {\n\t\tif value.Cmp(mustStapleFeatureValue) == 0 {\n\t\t\treturn true, nil\n\t\t}\n\t}\n\treturn false, nil\n}\n\n// contactResponders will send a request to all OCSP responders reported by cfg.serverCert. The\n// first response that conclusively identifies cfg.serverCert as good or revoked will be returned.\n// If all responders are unavailable or no responder returns a conclusive status, it returns nil.\n// contactResponders will wait for up to 5 seconds to get a certificate status response.\nfunc contactResponders(ctx context.Context, cfg config) *ResponseDetails {\n\tif len(cfg.serverCert.OCSPServer) == 0 {\n\t\treturn nil\n\t}\n\n\t// Limit all OCSP responder calls to a maximum of 5 seconds or when the passed-in context expires,\n\t// whichever happens first.\n\tctx, cancel := context.WithTimeout(ctx, 5*time.Second)\n\tdefer cancel()\n\n\tgroup, ctx := errgroup.WithContext(ctx)\n\tocspResponses := make(chan *ocsp.Response, len(cfg.serverCert.OCSPServer))\n\tdefer close(ocspResponses)\n\n\tfor _, endpoint := range cfg.serverCert.OCSPServer {\n\t\t// Re-assign endpoint so it gets re-scoped rather than using the iteration variable in the goroutine. See\n\t\t// https://golang.org/doc/faq#closures_and_goroutines.\n\t\tendpoint := endpoint\n\n\t\t// Start a group of goroutines that each attempt to request the certificate status from one\n\t\t// of the OCSP endpoints listed in the server certificate. We want to \"soft fail\" on all\n\t\t// errors, so this function never returns actual errors. Only a \"done\" error is returned\n\t\t// when a response is received so the errgroup cancels any other in-progress requests.\n\t\tgroup.Go(func() error {\n\t\t\t// Use bytes.NewReader instead of bytes.NewBuffer because a bytes.Buffer is an owning representation and the\n\t\t\t// docs recommend not using the underlying []byte after creating the buffer, so a new copy of the request\n\t\t\t// bytes would be needed for each request.\n\t\t\trequest, err := http.NewRequest(\"POST\", endpoint, bytes.NewReader(cfg.ocspRequestBytes))\n\t\t\tif err != nil {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\trequest = request.WithContext(ctx)\n\n\t\t\thttpResponse, err := cfg.httpClient.Do(request)\n\t\t\tif err != nil {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\tdefer func() {\n\t\t\t\t_ = httpResponse.Body.Close()\n\t\t\t}()\n\n\t\t\tif httpResponse.StatusCode != 200 {\n\t\t\t\treturn nil\n\t\t\t}\n\n\t\t\thttpBytes, err := ioutil.ReadAll(httpResponse.Body)\n\t\t\tif err != nil {\n\t\t\t\treturn nil\n\t\t\t}\n\n\t\t\tocspResponse, err := ocsp.ParseResponseForCert(httpBytes, cfg.serverCert, cfg.issuer)\n\t\t\tif err != nil || verifyResponse(cfg, ocspResponse) != nil || ocspResponse.Status == ocsp.Unknown {\n\t\t\t\t// If there was an error parsing/validating the response or the response was\n\t\t\t\t// inconclusive, suppress the error because we want to ignore this responder.\n\t\t\t\treturn nil\n\t\t\t}\n\n\t\t\t// Send the conclusive response on the response channel and return a \"done\" error that\n\t\t\t// will cause the errgroup to cancel all other in-progress requests.\n\t\t\tocspResponses <- ocspResponse\n\t\t\treturn errors.New(\"done\")\n\t\t})\n\t}\n\n\t_ = group.Wait()\n\tselect {\n\tcase res := <-ocspResponses:\n\t\treturn extractResponseDetails(res)\n\tdefault:\n\t\t// If there is no OCSP response on the response channel, all OCSP calls either failed or\n\t\t// were inconclusive. Return nil.\n\t\treturn nil\n\t}\n}\n\n// verifyResponse checks that the provided OCSP response is valid.\nfunc verifyResponse(cfg config, res *ocsp.Response) error {\n\tif err := verifyExtendedKeyUsage(cfg, res); err != nil {\n\t\treturn err\n\t}\n\n\tcurrTime := time.Now().UTC()\n\tif res.ThisUpdate.After(currTime) {\n\t\treturn fmt.Errorf(\"reported thisUpdate time %s is after current time %s\", res.ThisUpdate, currTime)\n\t}\n\tif !res.NextUpdate.IsZero() && res.NextUpdate.Before(currTime) {\n\t\treturn fmt.Errorf(\"reported nextUpdate time %s is before current time %s\", res.NextUpdate, currTime)\n\t}\n\treturn nil\n}\n\nfunc verifyExtendedKeyUsage(cfg config, res *ocsp.Response) error {\n\tif res.Certificate == nil {\n\t\treturn nil\n\t}\n\n\tnamesMatch := res.RawResponderName != nil && bytes.Equal(res.RawResponderName, cfg.issuer.RawSubject)\n\tkeyHashesMatch := res.ResponderKeyHash != nil && bytes.Equal(res.ResponderKeyHash, cfg.ocspRequest.IssuerKeyHash)\n\tif namesMatch || keyHashesMatch {\n\t\t// The responder certificate is the same as the issuer certificate.\n\t\treturn nil\n\t}\n\n\t// There is a delegate.\n\tfor _, extKeyUsage := range res.Certificate.ExtKeyUsage {\n\t\tif extKeyUsage == x509.ExtKeyUsageOCSPSigning {\n\t\t\treturn nil\n\t\t}\n\t}\n\n\treturn errors.New(\"delegate responder certificate is missing the OCSP signing extended key usage\")\n}\n"
  },
  {
    "path": "x/mongo/driver/ocsp/ocsp_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\n//go:build go1.13\n\npackage ocsp\n\nimport (\n\t\"context\"\n\t\"crypto/x509\"\n\t\"net\"\n\t\"testing\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/httputil\"\n)\n\nfunc TestContactResponders(t *testing.T) {\n\tt.Run(\"context cancellation is honored\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tctx, cancel := context.WithCancel(context.Background())\n\t\tcancel()\n\n\t\tserverCert := &x509.Certificate{\n\t\t\tOCSPServer: []string{\"https://localhost:5000\"},\n\t\t}\n\t\tcfg := config{\n\t\t\tserverCert: serverCert,\n\t\t\tissuer:     &x509.Certificate{},\n\t\t\tcache:      NewCache(),\n\t\t\thttpClient: httputil.DefaultHTTPClient,\n\t\t}\n\n\t\tres := contactResponders(ctx, cfg)\n\t\tassert.Nil(t, res, \"expected nil response details, but got %v\", res)\n\t})\n\tt.Run(\"context timeout is honored\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)\n\t\tdefer cancel()\n\n\t\t// Create a TCP listener on a random port that doesn't accept any connections, causing\n\t\t// connection attempts to hang indefinitely from the client's perspective.\n\t\tl, err := net.Listen(\"tcp\", \"localhost:0\")\n\t\tassert.Nil(t, err, \"tls.Listen() error: %v\", err)\n\t\tdefer l.Close()\n\n\t\tserverCert := &x509.Certificate{\n\t\t\tOCSPServer: []string{\"https://\" + l.Addr().String()},\n\t\t}\n\t\tcfg := config{\n\t\t\tserverCert: serverCert,\n\t\t\tissuer:     &x509.Certificate{},\n\t\t\tcache:      NewCache(),\n\t\t\thttpClient: httputil.DefaultHTTPClient,\n\t\t}\n\n\t\t// Expect that contactResponders() returns a nil response but does not cause any errors when\n\t\t// the passed-in context times out.\n\t\tstart := time.Now()\n\t\tres := contactResponders(ctx, cfg)\n\t\tduration := time.Since(start)\n\t\tassert.Nil(t, res, \"expected nil response, but got: %v\", res)\n\t\tassert.True(t, duration <= 5*time.Second, \"expected duration to be <= 5s, but was %v\", duration)\n\t})\n}\n"
  },
  {
    "path": "x/mongo/driver/ocsp/options.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage ocsp\n\nimport \"net/http\"\n\n// VerifyOptions specifies options to configure OCSP verification.\ntype VerifyOptions struct {\n\tCache                   Cache\n\tDisableEndpointChecking bool\n\tHTTPClient              *http.Client\n}\n"
  },
  {
    "path": "x/mongo/driver/operation/abort_transaction.go",
    "content": "// Copyright (C) MongoDB, Inc. 2019-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage operation\n\nimport (\n\t\"context\"\n\t\"errors\"\n\n\t\"go.mongodb.org/mongo-driver/v2/event\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/driverutil\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/logger\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/writeconcern\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/description\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/session\"\n)\n\n// AbortTransaction performs an abortTransaction operation.\ntype AbortTransaction struct {\n\tauthenticator driver.Authenticator\n\trecoveryToken bsoncore.Document\n\tsession       *session.Client\n\tclock         *session.ClusterClock\n\tcollection    string\n\tmonitor       *event.CommandMonitor\n\tcrypt         driver.Crypt\n\tdatabase      string\n\tdeployment    driver.Deployment\n\tselector      description.ServerSelector\n\twriteConcern  *writeconcern.WriteConcern\n\tretry         *driver.RetryMode\n\tserverAPI     *driver.ServerAPIOptions\n\tlogger        *logger.Logger\n}\n\n// NewAbortTransaction constructs and returns a new AbortTransaction.\nfunc NewAbortTransaction() *AbortTransaction {\n\treturn &AbortTransaction{}\n}\n\nfunc (at *AbortTransaction) processResponse(context.Context, bsoncore.Document, driver.ResponseInfo) error {\n\treturn nil\n}\n\n// Execute runs this operations and returns an error if the operation did not execute successfully.\nfunc (at *AbortTransaction) Execute(ctx context.Context) error {\n\tif at.deployment == nil {\n\t\treturn errors.New(\"the AbortTransaction operation must have a Deployment set before Execute can be called\")\n\t}\n\n\treturn driver.Operation{\n\t\tCommandFn:         at.command,\n\t\tProcessResponseFn: at.processResponse,\n\t\tRetryMode:         at.retry,\n\t\tType:              driver.Write,\n\t\tClient:            at.session,\n\t\tClock:             at.clock,\n\t\tCommandMonitor:    at.monitor,\n\t\tCrypt:             at.crypt,\n\t\tDatabase:          at.database,\n\t\tDeployment:        at.deployment,\n\t\tSelector:          at.selector,\n\t\tWriteConcern:      at.writeConcern,\n\t\tServerAPI:         at.serverAPI,\n\t\tName:              driverutil.AbortTransactionOp,\n\t\tAuthenticator:     at.authenticator,\n\t\tLogger:            at.logger,\n\t}.Execute(ctx)\n}\n\nfunc (at *AbortTransaction) command(dst []byte, _ description.SelectedServer) ([]byte, error) {\n\tdst = bsoncore.AppendInt32Element(dst, \"abortTransaction\", 1)\n\tif at.recoveryToken != nil {\n\t\tdst = bsoncore.AppendDocumentElement(dst, \"recoveryToken\", at.recoveryToken)\n\t}\n\treturn dst, nil\n}\n\n// RecoveryToken sets the recovery token to use when committing or aborting a sharded transaction.\nfunc (at *AbortTransaction) RecoveryToken(recoveryToken bsoncore.Document) *AbortTransaction {\n\tif at == nil {\n\t\tat = new(AbortTransaction)\n\t}\n\n\tat.recoveryToken = recoveryToken\n\treturn at\n}\n\n// Session sets the session for this operation.\nfunc (at *AbortTransaction) Session(session *session.Client) *AbortTransaction {\n\tif at == nil {\n\t\tat = new(AbortTransaction)\n\t}\n\n\tat.session = session\n\treturn at\n}\n\n// ClusterClock sets the cluster clock for this operation.\nfunc (at *AbortTransaction) ClusterClock(clock *session.ClusterClock) *AbortTransaction {\n\tif at == nil {\n\t\tat = new(AbortTransaction)\n\t}\n\n\tat.clock = clock\n\treturn at\n}\n\n// Collection sets the collection that this command will run against.\nfunc (at *AbortTransaction) Collection(collection string) *AbortTransaction {\n\tif at == nil {\n\t\tat = new(AbortTransaction)\n\t}\n\n\tat.collection = collection\n\treturn at\n}\n\n// CommandMonitor sets the monitor to use for APM events.\nfunc (at *AbortTransaction) CommandMonitor(monitor *event.CommandMonitor) *AbortTransaction {\n\tif at == nil {\n\t\tat = new(AbortTransaction)\n\t}\n\n\tat.monitor = monitor\n\treturn at\n}\n\n// Crypt sets the Crypt object to use for automatic encryption and decryption.\nfunc (at *AbortTransaction) Crypt(crypt driver.Crypt) *AbortTransaction {\n\tif at == nil {\n\t\tat = new(AbortTransaction)\n\t}\n\n\tat.crypt = crypt\n\treturn at\n}\n\n// Database sets the database to run this operation against.\nfunc (at *AbortTransaction) Database(database string) *AbortTransaction {\n\tif at == nil {\n\t\tat = new(AbortTransaction)\n\t}\n\n\tat.database = database\n\treturn at\n}\n\n// Deployment sets the deployment to use for this operation.\nfunc (at *AbortTransaction) Deployment(deployment driver.Deployment) *AbortTransaction {\n\tif at == nil {\n\t\tat = new(AbortTransaction)\n\t}\n\n\tat.deployment = deployment\n\treturn at\n}\n\n// ServerSelector sets the selector used to retrieve a server.\nfunc (at *AbortTransaction) ServerSelector(selector description.ServerSelector) *AbortTransaction {\n\tif at == nil {\n\t\tat = new(AbortTransaction)\n\t}\n\n\tat.selector = selector\n\treturn at\n}\n\n// WriteConcern sets the write concern for this operation.\nfunc (at *AbortTransaction) WriteConcern(writeConcern *writeconcern.WriteConcern) *AbortTransaction {\n\tif at == nil {\n\t\tat = new(AbortTransaction)\n\t}\n\n\tat.writeConcern = writeConcern\n\treturn at\n}\n\n// Retry enables retryable mode for this operation. Retries are handled automatically in driver.Operation.Execute based\n// on how the operation is set.\nfunc (at *AbortTransaction) Retry(retry driver.RetryMode) *AbortTransaction {\n\tif at == nil {\n\t\tat = new(AbortTransaction)\n\t}\n\n\tat.retry = &retry\n\treturn at\n}\n\n// ServerAPI sets the server API version for this operation.\nfunc (at *AbortTransaction) ServerAPI(serverAPI *driver.ServerAPIOptions) *AbortTransaction {\n\tif at == nil {\n\t\tat = new(AbortTransaction)\n\t}\n\n\tat.serverAPI = serverAPI\n\treturn at\n}\n\n// Authenticator sets the authenticator to use for this operation.\nfunc (at *AbortTransaction) Authenticator(authenticator driver.Authenticator) *AbortTransaction {\n\tif at == nil {\n\t\tat = new(AbortTransaction)\n\t}\n\n\tat.authenticator = authenticator\n\treturn at\n}\n\n// Logger sets the logger for this operation.\nfunc (at *AbortTransaction) Logger(logger *logger.Logger) *AbortTransaction {\n\tif at == nil {\n\t\tat = new(AbortTransaction)\n\t}\n\n\tat.logger = logger\n\treturn at\n}\n"
  },
  {
    "path": "x/mongo/driver/operation/aggregate.go",
    "content": "// Copyright (C) MongoDB, Inc. 2019-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage operation\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/event\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/driverutil\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/readconcern\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/readpref\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/writeconcern\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/description\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/session\"\n)\n\n// Aggregate represents an aggregate operation.\ntype Aggregate struct {\n\tauthenticator            driver.Authenticator\n\tallowDiskUse             *bool\n\tbatchSize                *int32\n\tbypassDocumentValidation *bool\n\tcollation                bsoncore.Document\n\tcomment                  bsoncore.Value\n\thint                     bsoncore.Value\n\tpipeline                 bsoncore.Document\n\tsession                  *session.Client\n\tclock                    *session.ClusterClock\n\tcollection               string\n\tmonitor                  *event.CommandMonitor\n\tdatabase                 string\n\tdeployment               driver.Deployment\n\treadConcern              *readconcern.ReadConcern\n\treadPreference           *readpref.ReadPref\n\tretry                    *driver.RetryMode\n\tselector                 description.ServerSelector\n\twriteConcern             *writeconcern.WriteConcern\n\tcrypt                    driver.Crypt\n\tserverAPI                *driver.ServerAPIOptions\n\tlet                      bsoncore.Document\n\thasOutputStage           bool\n\tcustomOptions            map[string]bsoncore.Value\n\ttimeout                  *time.Duration\n\tomitMaxTimeMS            bool\n\trawData                  *bool\n\n\tresult driver.CursorResponse\n}\n\n// NewAggregate constructs and returns a new Aggregate.\nfunc NewAggregate(pipeline bsoncore.Document) *Aggregate {\n\treturn &Aggregate{\n\t\tpipeline: pipeline,\n\t}\n}\n\n// Result returns the result of executing this operation.\nfunc (a *Aggregate) Result(opts driver.CursorOptions) (*driver.BatchCursor, error) {\n\tclientSession := a.session\n\n\tclock := a.clock\n\topts.ServerAPI = a.serverAPI\n\treturn driver.NewBatchCursor(a.result, clientSession, clock, opts)\n}\n\n// ResultCursorResponse returns the underlying CursorResponse result of executing this\n// operation.\nfunc (a *Aggregate) ResultCursorResponse() driver.CursorResponse {\n\treturn a.result\n}\n\nfunc (a *Aggregate) processResponse(_ context.Context, resp bsoncore.Document, info driver.ResponseInfo) error {\n\tcurDoc, err := driver.ExtractCursorDocument(resp)\n\tif err != nil {\n\t\treturn err\n\t}\n\ta.result, err = driver.NewCursorResponse(curDoc, info)\n\treturn err\n}\n\n// Execute runs this operations and returns an error if the operation did not execute successfully.\nfunc (a *Aggregate) Execute(ctx context.Context) error {\n\tif a.deployment == nil {\n\t\treturn errors.New(\"the Aggregate operation must have a Deployment set before Execute can be called\")\n\t}\n\n\treturn driver.Operation{\n\t\tCommandFn:         a.command,\n\t\tProcessResponseFn: a.processResponse,\n\n\t\tClient:                         a.session,\n\t\tClock:                          a.clock,\n\t\tCommandMonitor:                 a.monitor,\n\t\tDatabase:                       a.database,\n\t\tDeployment:                     a.deployment,\n\t\tReadConcern:                    a.readConcern,\n\t\tReadPreference:                 a.readPreference,\n\t\tType:                           driver.Read,\n\t\tRetryMode:                      a.retry,\n\t\tSelector:                       a.selector,\n\t\tWriteConcern:                   a.writeConcern,\n\t\tCrypt:                          a.crypt,\n\t\tMinimumWriteConcernWireVersion: 5,\n\t\tServerAPI:                      a.serverAPI,\n\t\tIsOutputAggregate:              a.hasOutputStage,\n\t\tTimeout:                        a.timeout,\n\t\tName:                           driverutil.AggregateOp,\n\t\tAuthenticator:                  a.authenticator,\n\t\tOmitMaxTimeMS:                  a.omitMaxTimeMS,\n\t}.Execute(ctx)\n}\n\nfunc (a *Aggregate) command(dst []byte, desc description.SelectedServer) ([]byte, error) {\n\theader := bsoncore.Value{Type: bsoncore.TypeString, Data: bsoncore.AppendString(nil, a.collection)}\n\tif a.collection == \"\" {\n\t\theader = bsoncore.Value{Type: bsoncore.TypeInt32, Data: []byte{0x01, 0x00, 0x00, 0x00}}\n\t}\n\tdst = bsoncore.AppendValueElement(dst, \"aggregate\", header)\n\n\tcursorIdx, cursorDoc := bsoncore.AppendDocumentStart(nil)\n\tif a.allowDiskUse != nil {\n\t\tdst = bsoncore.AppendBooleanElement(dst, \"allowDiskUse\", *a.allowDiskUse)\n\t}\n\tif a.batchSize != nil {\n\t\tcursorDoc = bsoncore.AppendInt32Element(cursorDoc, \"batchSize\", *a.batchSize)\n\t}\n\tif a.bypassDocumentValidation != nil {\n\t\tdst = bsoncore.AppendBooleanElement(dst, \"bypassDocumentValidation\", *a.bypassDocumentValidation)\n\t}\n\tif a.collation != nil {\n\t\tif desc.WireVersion == nil || !driverutil.VersionRangeIncludes(*desc.WireVersion, 5) {\n\t\t\treturn nil, errors.New(\"the 'collation' command parameter requires a minimum server wire version of 5\")\n\t\t}\n\t\tdst = bsoncore.AppendDocumentElement(dst, \"collation\", a.collation)\n\t}\n\tif a.comment.Type != bsoncore.Type(0) {\n\t\tdst = bsoncore.AppendValueElement(dst, \"comment\", a.comment)\n\t}\n\tif a.hint.Type != bsoncore.Type(0) {\n\t\tdst = bsoncore.AppendValueElement(dst, \"hint\", a.hint)\n\t}\n\tif a.pipeline != nil {\n\t\tdst = bsoncore.AppendArrayElement(dst, \"pipeline\", a.pipeline)\n\t}\n\tif a.let != nil {\n\t\tdst = bsoncore.AppendDocumentElement(dst, \"let\", a.let)\n\t}\n\t// Set rawData for 8.2+ servers.\n\tif a.rawData != nil && desc.WireVersion != nil && driverutil.VersionRangeIncludes(*desc.WireVersion, 27) {\n\t\tdst = bsoncore.AppendBooleanElement(dst, \"rawData\", *a.rawData)\n\t}\n\tfor optionName, optionValue := range a.customOptions {\n\t\tdst = bsoncore.AppendValueElement(dst, optionName, optionValue)\n\t}\n\tcursorDoc, _ = bsoncore.AppendDocumentEnd(cursorDoc, cursorIdx)\n\tdst = bsoncore.AppendDocumentElement(dst, \"cursor\", cursorDoc)\n\n\treturn dst, nil\n}\n\n// AllowDiskUse enables writing to temporary files. When true, aggregation stages can write to the dbPath/_tmp directory.\nfunc (a *Aggregate) AllowDiskUse(allowDiskUse bool) *Aggregate {\n\tif a == nil {\n\t\ta = new(Aggregate)\n\t}\n\n\ta.allowDiskUse = &allowDiskUse\n\treturn a\n}\n\n// BatchSize specifies the number of documents to return in every batch.\nfunc (a *Aggregate) BatchSize(batchSize int32) *Aggregate {\n\tif a == nil {\n\t\ta = new(Aggregate)\n\t}\n\n\ta.batchSize = &batchSize\n\treturn a\n}\n\n// BypassDocumentValidation allows the write to opt-out of document level validation. This only applies when the $out stage is specified.\nfunc (a *Aggregate) BypassDocumentValidation(bypassDocumentValidation bool) *Aggregate {\n\tif a == nil {\n\t\ta = new(Aggregate)\n\t}\n\n\ta.bypassDocumentValidation = &bypassDocumentValidation\n\treturn a\n}\n\n// Collation specifies a collation.\nfunc (a *Aggregate) Collation(collation bsoncore.Document) *Aggregate {\n\tif a == nil {\n\t\ta = new(Aggregate)\n\t}\n\n\ta.collation = collation\n\treturn a\n}\n\n// Comment sets a value to help trace an operation.\nfunc (a *Aggregate) Comment(comment bsoncore.Value) *Aggregate {\n\tif a == nil {\n\t\ta = new(Aggregate)\n\t}\n\n\ta.comment = comment\n\treturn a\n}\n\n// Hint specifies the index to use.\nfunc (a *Aggregate) Hint(hint bsoncore.Value) *Aggregate {\n\tif a == nil {\n\t\ta = new(Aggregate)\n\t}\n\n\ta.hint = hint\n\treturn a\n}\n\n// Pipeline determines how data is transformed for an aggregation.\nfunc (a *Aggregate) Pipeline(pipeline bsoncore.Document) *Aggregate {\n\tif a == nil {\n\t\ta = new(Aggregate)\n\t}\n\n\ta.pipeline = pipeline\n\treturn a\n}\n\n// Session sets the session for this operation.\nfunc (a *Aggregate) Session(session *session.Client) *Aggregate {\n\tif a == nil {\n\t\ta = new(Aggregate)\n\t}\n\n\ta.session = session\n\treturn a\n}\n\n// ClusterClock sets the cluster clock for this operation.\nfunc (a *Aggregate) ClusterClock(clock *session.ClusterClock) *Aggregate {\n\tif a == nil {\n\t\ta = new(Aggregate)\n\t}\n\n\ta.clock = clock\n\treturn a\n}\n\n// Collection sets the collection that this command will run against.\nfunc (a *Aggregate) Collection(collection string) *Aggregate {\n\tif a == nil {\n\t\ta = new(Aggregate)\n\t}\n\n\ta.collection = collection\n\treturn a\n}\n\n// CommandMonitor sets the monitor to use for APM events.\nfunc (a *Aggregate) CommandMonitor(monitor *event.CommandMonitor) *Aggregate {\n\tif a == nil {\n\t\ta = new(Aggregate)\n\t}\n\n\ta.monitor = monitor\n\treturn a\n}\n\n// Database sets the database to run this operation against.\nfunc (a *Aggregate) Database(database string) *Aggregate {\n\tif a == nil {\n\t\ta = new(Aggregate)\n\t}\n\n\ta.database = database\n\treturn a\n}\n\n// Deployment sets the deployment to use for this operation.\nfunc (a *Aggregate) Deployment(deployment driver.Deployment) *Aggregate {\n\tif a == nil {\n\t\ta = new(Aggregate)\n\t}\n\n\ta.deployment = deployment\n\treturn a\n}\n\n// ReadConcern specifies the read concern for this operation.\nfunc (a *Aggregate) ReadConcern(readConcern *readconcern.ReadConcern) *Aggregate {\n\tif a == nil {\n\t\ta = new(Aggregate)\n\t}\n\n\ta.readConcern = readConcern\n\treturn a\n}\n\n// ReadPreference set the read preference used with this operation.\nfunc (a *Aggregate) ReadPreference(readPreference *readpref.ReadPref) *Aggregate {\n\tif a == nil {\n\t\ta = new(Aggregate)\n\t}\n\n\ta.readPreference = readPreference\n\treturn a\n}\n\n// ServerSelector sets the selector used to retrieve a server.\nfunc (a *Aggregate) ServerSelector(selector description.ServerSelector) *Aggregate {\n\tif a == nil {\n\t\ta = new(Aggregate)\n\t}\n\n\ta.selector = selector\n\treturn a\n}\n\n// WriteConcern sets the write concern for this operation.\nfunc (a *Aggregate) WriteConcern(writeConcern *writeconcern.WriteConcern) *Aggregate {\n\tif a == nil {\n\t\ta = new(Aggregate)\n\t}\n\n\ta.writeConcern = writeConcern\n\treturn a\n}\n\n// Retry enables retryable writes for this operation. Retries are not handled automatically,\n// instead a boolean is returned from Execute and SelectAndExecute that indicates if the\n// operation can be retried. Retrying is handled by calling RetryExecute.\nfunc (a *Aggregate) Retry(retry driver.RetryMode) *Aggregate {\n\tif a == nil {\n\t\ta = new(Aggregate)\n\t}\n\n\ta.retry = &retry\n\treturn a\n}\n\n// Crypt sets the Crypt object to use for automatic encryption and decryption.\nfunc (a *Aggregate) Crypt(crypt driver.Crypt) *Aggregate {\n\tif a == nil {\n\t\ta = new(Aggregate)\n\t}\n\n\ta.crypt = crypt\n\treturn a\n}\n\n// ServerAPI sets the server API version for this operation.\nfunc (a *Aggregate) ServerAPI(serverAPI *driver.ServerAPIOptions) *Aggregate {\n\tif a == nil {\n\t\ta = new(Aggregate)\n\t}\n\n\ta.serverAPI = serverAPI\n\treturn a\n}\n\n// Let specifies the let document to use. This option is only valid for server versions 5.0 and above.\nfunc (a *Aggregate) Let(let bsoncore.Document) *Aggregate {\n\tif a == nil {\n\t\ta = new(Aggregate)\n\t}\n\n\ta.let = let\n\treturn a\n}\n\n// HasOutputStage specifies whether the aggregate contains an output stage. Used in determining when to\n// append read preference at the operation level.\nfunc (a *Aggregate) HasOutputStage(hos bool) *Aggregate {\n\tif a == nil {\n\t\ta = new(Aggregate)\n\t}\n\n\ta.hasOutputStage = hos\n\treturn a\n}\n\n// CustomOptions specifies extra options to use in the aggregate command.\nfunc (a *Aggregate) CustomOptions(co map[string]bsoncore.Value) *Aggregate {\n\tif a == nil {\n\t\ta = new(Aggregate)\n\t}\n\n\ta.customOptions = co\n\treturn a\n}\n\n// Timeout sets the timeout for this operation.\nfunc (a *Aggregate) Timeout(timeout *time.Duration) *Aggregate {\n\tif a == nil {\n\t\ta = new(Aggregate)\n\t}\n\n\ta.timeout = timeout\n\treturn a\n}\n\n// Authenticator sets the authenticator to use for this operation.\nfunc (a *Aggregate) Authenticator(authenticator driver.Authenticator) *Aggregate {\n\tif a == nil {\n\t\ta = new(Aggregate)\n\t}\n\n\ta.authenticator = authenticator\n\treturn a\n}\n\n// OmitMaxTimeMS omits the automatically-calculated \"maxTimeMS\" from the\n// command.\nfunc (a *Aggregate) OmitMaxTimeMS(omit bool) *Aggregate {\n\tif a == nil {\n\t\ta = new(Aggregate)\n\t}\n\n\ta.omitMaxTimeMS = omit\n\treturn a\n}\n\n// RawData sets the rawData to access timeseries data in the compressed format.\nfunc (a *Aggregate) RawData(rawData bool) *Aggregate {\n\tif a == nil {\n\t\ta = new(Aggregate)\n\t}\n\n\ta.rawData = &rawData\n\treturn a\n}\n"
  },
  {
    "path": "x/mongo/driver/operation/command.go",
    "content": "// Copyright (C) MongoDB, Inc. 2021-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage operation\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/event\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/logger\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/readpref\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/description\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/session\"\n)\n\n// Command is used to run a generic operation.\ntype Command struct {\n\tauthenticator  driver.Authenticator\n\tcommand        bsoncore.Document\n\tdatabase       string\n\tdeployment     driver.Deployment\n\tselector       description.ServerSelector\n\treadPreference *readpref.ReadPref\n\tclock          *session.ClusterClock\n\tsession        *session.Client\n\tmonitor        *event.CommandMonitor\n\tresultResponse bsoncore.Document\n\tresultCursor   *driver.BatchCursor\n\tcrypt          driver.Crypt\n\tserverAPI      *driver.ServerAPIOptions\n\tcreateCursor   bool\n\tcursorOpts     driver.CursorOptions\n\ttimeout        *time.Duration\n\tlogger         *logger.Logger\n}\n\n// NewCommand constructs and returns a new Command. Once the operation is executed, the result may only be accessed via\n// the Result() function.\nfunc NewCommand(command bsoncore.Document) *Command {\n\treturn &Command{\n\t\tcommand: command,\n\t}\n}\n\n// NewCursorCommand constructs a new Command. Once the operation is executed, the server response will be used to\n// construct a cursor, which can be accessed via the ResultCursor() function.\nfunc NewCursorCommand(command bsoncore.Document, cursorOpts driver.CursorOptions) *Command {\n\treturn &Command{\n\t\tcommand:      command,\n\t\tcursorOpts:   cursorOpts,\n\t\tcreateCursor: true,\n\t}\n}\n\n// Result returns the result of executing this operation.\nfunc (c *Command) Result() bsoncore.Document { return c.resultResponse }\n\n// ResultCursor returns the BatchCursor that was constructed using the command response. If the operation was not\n// configured to create a cursor (i.e. it was created using NewCommand rather than NewCursorCommand), this function\n// will return nil and an error.\nfunc (c *Command) ResultCursor() (*driver.BatchCursor, error) {\n\tif !c.createCursor {\n\t\treturn nil, errors.New(\"command operation was not configured to create a cursor, but a result cursor was requested\")\n\t}\n\treturn c.resultCursor, nil\n}\n\n// Execute runs this operations and returns an error if the operation did not execute successfully.\nfunc (c *Command) Execute(ctx context.Context) error {\n\tif c.deployment == nil {\n\t\treturn errors.New(\"the Command operation must have a Deployment set before Execute can be called\")\n\t}\n\n\treturn driver.Operation{\n\t\tCommandFn: func(dst []byte, _ description.SelectedServer) ([]byte, error) {\n\t\t\treturn append(dst, c.command[4:len(c.command)-1]...), nil\n\t\t},\n\t\tProcessResponseFn: func(_ context.Context, resp bsoncore.Document, info driver.ResponseInfo) error {\n\t\t\tc.resultResponse = resp\n\n\t\t\tif c.createCursor {\n\t\t\t\tcurDoc, err := driver.ExtractCursorDocument(resp)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tcursorRes, err := driver.NewCursorResponse(curDoc, info)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\n\t\t\t\tc.resultCursor, err = driver.NewBatchCursor(cursorRes, c.session, c.clock, c.cursorOpts)\n\t\t\t\treturn err\n\t\t\t}\n\n\t\t\treturn nil\n\t\t},\n\t\tClient:         c.session,\n\t\tClock:          c.clock,\n\t\tCommandMonitor: c.monitor,\n\t\tDatabase:       c.database,\n\t\tDeployment:     c.deployment,\n\t\tReadPreference: c.readPreference,\n\t\tSelector:       c.selector,\n\t\tCrypt:          c.crypt,\n\t\tServerAPI:      c.serverAPI,\n\t\tTimeout:        c.timeout,\n\t\tLogger:         c.logger,\n\t\tAuthenticator:  c.authenticator,\n\t}.Execute(ctx)\n}\n\n// Session sets the session for this operation.\nfunc (c *Command) Session(session *session.Client) *Command {\n\tif c == nil {\n\t\tc = new(Command)\n\t}\n\n\tc.session = session\n\treturn c\n}\n\n// ClusterClock sets the cluster clock for this operation.\nfunc (c *Command) ClusterClock(clock *session.ClusterClock) *Command {\n\tif c == nil {\n\t\tc = new(Command)\n\t}\n\n\tc.clock = clock\n\treturn c\n}\n\n// CommandMonitor sets the monitor to use for APM events.\nfunc (c *Command) CommandMonitor(monitor *event.CommandMonitor) *Command {\n\tif c == nil {\n\t\tc = new(Command)\n\t}\n\n\tc.monitor = monitor\n\treturn c\n}\n\n// Database sets the database to run this operation against.\nfunc (c *Command) Database(database string) *Command {\n\tif c == nil {\n\t\tc = new(Command)\n\t}\n\n\tc.database = database\n\treturn c\n}\n\n// Deployment sets the deployment to use for this operation.\nfunc (c *Command) Deployment(deployment driver.Deployment) *Command {\n\tif c == nil {\n\t\tc = new(Command)\n\t}\n\n\tc.deployment = deployment\n\treturn c\n}\n\n// ReadPreference set the read preference used with this operation.\nfunc (c *Command) ReadPreference(readPreference *readpref.ReadPref) *Command {\n\tif c == nil {\n\t\tc = new(Command)\n\t}\n\n\tc.readPreference = readPreference\n\treturn c\n}\n\n// ServerSelector sets the selector used to retrieve a server.\nfunc (c *Command) ServerSelector(selector description.ServerSelector) *Command {\n\tif c == nil {\n\t\tc = new(Command)\n\t}\n\n\tc.selector = selector\n\treturn c\n}\n\n// Crypt sets the Crypt object to use for automatic encryption and decryption.\nfunc (c *Command) Crypt(crypt driver.Crypt) *Command {\n\tif c == nil {\n\t\tc = new(Command)\n\t}\n\n\tc.crypt = crypt\n\treturn c\n}\n\n// ServerAPI sets the server API version for this operation.\nfunc (c *Command) ServerAPI(serverAPI *driver.ServerAPIOptions) *Command {\n\tif c == nil {\n\t\tc = new(Command)\n\t}\n\n\tc.serverAPI = serverAPI\n\treturn c\n}\n\n// Timeout sets the timeout for this operation.\nfunc (c *Command) Timeout(timeout *time.Duration) *Command {\n\tif c == nil {\n\t\tc = new(Command)\n\t}\n\n\tc.timeout = timeout\n\treturn c\n}\n\n// Logger sets the logger for this operation.\nfunc (c *Command) Logger(logger *logger.Logger) *Command {\n\tif c == nil {\n\t\tc = new(Command)\n\t}\n\n\tc.logger = logger\n\treturn c\n}\n\n// Authenticator sets the authenticator to use for this operation.\nfunc (c *Command) Authenticator(authenticator driver.Authenticator) *Command {\n\tif c == nil {\n\t\tc = new(Command)\n\t}\n\n\tc.authenticator = authenticator\n\treturn c\n}\n"
  },
  {
    "path": "x/mongo/driver/operation/commit_transaction.go",
    "content": "// Copyright (C) MongoDB, Inc. 2019-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage operation\n\nimport (\n\t\"context\"\n\t\"errors\"\n\n\t\"go.mongodb.org/mongo-driver/v2/event\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/driverutil\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/logger\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/writeconcern\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/description\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/session\"\n)\n\n// CommitTransaction attempts to commit a transaction.\ntype CommitTransaction struct {\n\tauthenticator driver.Authenticator\n\trecoveryToken bsoncore.Document\n\tsession       *session.Client\n\tclock         *session.ClusterClock\n\tmonitor       *event.CommandMonitor\n\tcrypt         driver.Crypt\n\tdatabase      string\n\tdeployment    driver.Deployment\n\tselector      description.ServerSelector\n\twriteConcern  *writeconcern.WriteConcern\n\tretry         *driver.RetryMode\n\tserverAPI     *driver.ServerAPIOptions\n\tlogger        *logger.Logger\n}\n\n// NewCommitTransaction constructs and returns a new CommitTransaction.\nfunc NewCommitTransaction() *CommitTransaction {\n\treturn &CommitTransaction{}\n}\n\nfunc (ct *CommitTransaction) processResponse(context.Context, bsoncore.Document, driver.ResponseInfo) error {\n\treturn nil\n}\n\n// Execute runs this operations and returns an error if the operation did not execute successfully.\nfunc (ct *CommitTransaction) Execute(ctx context.Context) error {\n\tif ct.deployment == nil {\n\t\treturn errors.New(\"the CommitTransaction operation must have a Deployment set before Execute can be called\")\n\t}\n\n\treturn driver.Operation{\n\t\tCommandFn:         ct.command,\n\t\tProcessResponseFn: ct.processResponse,\n\t\tRetryMode:         ct.retry,\n\t\tType:              driver.Write,\n\t\tClient:            ct.session,\n\t\tClock:             ct.clock,\n\t\tCommandMonitor:    ct.monitor,\n\t\tCrypt:             ct.crypt,\n\t\tDatabase:          ct.database,\n\t\tDeployment:        ct.deployment,\n\t\tSelector:          ct.selector,\n\t\tWriteConcern:      ct.writeConcern,\n\t\tServerAPI:         ct.serverAPI,\n\t\tName:              driverutil.CommitTransactionOp,\n\t\tAuthenticator:     ct.authenticator,\n\t\tLogger:            ct.logger,\n\t}.Execute(ctx)\n}\n\nfunc (ct *CommitTransaction) command(dst []byte, _ description.SelectedServer) ([]byte, error) {\n\tdst = bsoncore.AppendInt32Element(dst, \"commitTransaction\", 1)\n\tif ct.recoveryToken != nil {\n\t\tdst = bsoncore.AppendDocumentElement(dst, \"recoveryToken\", ct.recoveryToken)\n\t}\n\treturn dst, nil\n}\n\n// RecoveryToken sets the recovery token to use when committing or aborting a sharded transaction.\nfunc (ct *CommitTransaction) RecoveryToken(recoveryToken bsoncore.Document) *CommitTransaction {\n\tif ct == nil {\n\t\tct = new(CommitTransaction)\n\t}\n\n\tct.recoveryToken = recoveryToken\n\treturn ct\n}\n\n// Session sets the session for this operation.\nfunc (ct *CommitTransaction) Session(session *session.Client) *CommitTransaction {\n\tif ct == nil {\n\t\tct = new(CommitTransaction)\n\t}\n\n\tct.session = session\n\treturn ct\n}\n\n// ClusterClock sets the cluster clock for this operation.\nfunc (ct *CommitTransaction) ClusterClock(clock *session.ClusterClock) *CommitTransaction {\n\tif ct == nil {\n\t\tct = new(CommitTransaction)\n\t}\n\n\tct.clock = clock\n\treturn ct\n}\n\n// CommandMonitor sets the monitor to use for APM events.\nfunc (ct *CommitTransaction) CommandMonitor(monitor *event.CommandMonitor) *CommitTransaction {\n\tif ct == nil {\n\t\tct = new(CommitTransaction)\n\t}\n\n\tct.monitor = monitor\n\treturn ct\n}\n\n// Crypt sets the Crypt object to use for automatic encryption and decryption.\nfunc (ct *CommitTransaction) Crypt(crypt driver.Crypt) *CommitTransaction {\n\tif ct == nil {\n\t\tct = new(CommitTransaction)\n\t}\n\n\tct.crypt = crypt\n\treturn ct\n}\n\n// Database sets the database to run this operation against.\nfunc (ct *CommitTransaction) Database(database string) *CommitTransaction {\n\tif ct == nil {\n\t\tct = new(CommitTransaction)\n\t}\n\n\tct.database = database\n\treturn ct\n}\n\n// Deployment sets the deployment to use for this operation.\nfunc (ct *CommitTransaction) Deployment(deployment driver.Deployment) *CommitTransaction {\n\tif ct == nil {\n\t\tct = new(CommitTransaction)\n\t}\n\n\tct.deployment = deployment\n\treturn ct\n}\n\n// ServerSelector sets the selector used to retrieve a server.\nfunc (ct *CommitTransaction) ServerSelector(selector description.ServerSelector) *CommitTransaction {\n\tif ct == nil {\n\t\tct = new(CommitTransaction)\n\t}\n\n\tct.selector = selector\n\treturn ct\n}\n\n// WriteConcern sets the write concern for this operation.\nfunc (ct *CommitTransaction) WriteConcern(writeConcern *writeconcern.WriteConcern) *CommitTransaction {\n\tif ct == nil {\n\t\tct = new(CommitTransaction)\n\t}\n\n\tct.writeConcern = writeConcern\n\treturn ct\n}\n\n// Retry enables retryable mode for this operation. Retries are handled automatically in driver.Operation.Execute based\n// on how the operation is set.\nfunc (ct *CommitTransaction) Retry(retry driver.RetryMode) *CommitTransaction {\n\tif ct == nil {\n\t\tct = new(CommitTransaction)\n\t}\n\n\tct.retry = &retry\n\treturn ct\n}\n\n// ServerAPI sets the server API version for this operation.\nfunc (ct *CommitTransaction) ServerAPI(serverAPI *driver.ServerAPIOptions) *CommitTransaction {\n\tif ct == nil {\n\t\tct = new(CommitTransaction)\n\t}\n\n\tct.serverAPI = serverAPI\n\treturn ct\n}\n\n// Authenticator sets the authenticator to use for this operation.\nfunc (ct *CommitTransaction) Authenticator(authenticator driver.Authenticator) *CommitTransaction {\n\tif ct == nil {\n\t\tct = new(CommitTransaction)\n\t}\n\n\tct.authenticator = authenticator\n\treturn ct\n}\n\n// Logger sets the logger for this operation.\nfunc (ct *CommitTransaction) Logger(logger *logger.Logger) *CommitTransaction {\n\tif ct == nil {\n\t\tct = new(CommitTransaction)\n\t}\n\n\tct.logger = logger\n\treturn ct\n}\n"
  },
  {
    "path": "x/mongo/driver/operation/count.go",
    "content": "// Copyright (C) MongoDB, Inc. 2019-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage operation\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/event\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/driverutil\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/readconcern\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/readpref\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/description\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/session\"\n)\n\n// Count represents a count operation.\ntype Count struct {\n\tauthenticator  driver.Authenticator\n\tquery          bsoncore.Document\n\tsession        *session.Client\n\tclock          *session.ClusterClock\n\tcollection     string\n\tcomment        bsoncore.Value\n\tmonitor        *event.CommandMonitor\n\tcrypt          driver.Crypt\n\tdatabase       string\n\tdeployment     driver.Deployment\n\treadConcern    *readconcern.ReadConcern\n\treadPreference *readpref.ReadPref\n\tselector       description.ServerSelector\n\tretry          *driver.RetryMode\n\tresult         CountResult\n\tserverAPI      *driver.ServerAPIOptions\n\ttimeout        *time.Duration\n\trawData        *bool\n}\n\n// CountResult represents a count result returned by the server.\ntype CountResult struct {\n\t// The number of documents found\n\tN int64\n}\n\nfunc buildCountResult(response bsoncore.Document) (CountResult, error) {\n\telements, err := response.Elements()\n\tif err != nil {\n\t\treturn CountResult{}, err\n\t}\n\tcr := CountResult{}\n\tfor _, element := range elements {\n\t\tswitch element.Key() {\n\t\tcase \"n\": // for count using original command\n\t\t\tvar ok bool\n\t\t\tcr.N, ok = element.Value().AsInt64OK()\n\t\t\tif !ok {\n\t\t\t\treturn cr, fmt.Errorf(\"response field 'n' is type int64, but received BSON type %s\",\n\t\t\t\t\telement.Value().Type)\n\t\t\t}\n\t\tcase \"cursor\": // for count using aggregate with $collStats\n\t\t\tfirstBatch, err := element.Value().Document().LookupErr(\"firstBatch\")\n\t\t\tif err != nil {\n\t\t\t\treturn cr, err\n\t\t\t}\n\n\t\t\t// get count value from first batch\n\t\t\tval := firstBatch.Array().Index(0)\n\t\t\tcount, err := val.Document().LookupErr(\"n\")\n\t\t\tif err != nil {\n\t\t\t\treturn cr, err\n\t\t\t}\n\n\t\t\t// use count as Int64 for result\n\t\t\tvar ok bool\n\t\t\tcr.N, ok = count.AsInt64OK()\n\t\t\tif !ok {\n\t\t\t\treturn cr, fmt.Errorf(\"response field 'n' is type int64, but received BSON type %s\",\n\t\t\t\t\telement.Value().Type)\n\t\t\t}\n\t\t}\n\t}\n\treturn cr, nil\n}\n\n// NewCount constructs and returns a new Count.\nfunc NewCount() *Count {\n\treturn &Count{}\n}\n\n// Result returns the result of executing this operation.\nfunc (c *Count) Result() CountResult { return c.result }\n\nfunc (c *Count) processResponse(_ context.Context, resp bsoncore.Document, _ driver.ResponseInfo) error {\n\tvar err error\n\tc.result, err = buildCountResult(resp)\n\treturn err\n}\n\n// Execute runs this operations and returns an error if the operation did not execute successfully.\nfunc (c *Count) Execute(ctx context.Context) error {\n\tif c.deployment == nil {\n\t\treturn errors.New(\"the Count operation must have a Deployment set before Execute can be called\")\n\t}\n\n\terr := driver.Operation{\n\t\tCommandFn:         c.command,\n\t\tProcessResponseFn: c.processResponse,\n\t\tRetryMode:         c.retry,\n\t\tType:              driver.Read,\n\t\tClient:            c.session,\n\t\tClock:             c.clock,\n\t\tCommandMonitor:    c.monitor,\n\t\tCrypt:             c.crypt,\n\t\tDatabase:          c.database,\n\t\tDeployment:        c.deployment,\n\t\tReadConcern:       c.readConcern,\n\t\tReadPreference:    c.readPreference,\n\t\tSelector:          c.selector,\n\t\tServerAPI:         c.serverAPI,\n\t\tTimeout:           c.timeout,\n\t\tName:              driverutil.CountOp,\n\t\tAuthenticator:     c.authenticator,\n\t}.Execute(ctx)\n\t// Swallow error if NamespaceNotFound(26) is returned from aggregate on non-existent namespace\n\tif err != nil {\n\t\tdErr, ok := err.(driver.Error)\n\t\tif ok && dErr.Code == 26 {\n\t\t\terr = nil\n\t\t}\n\t}\n\treturn err\n}\n\nfunc (c *Count) command(dst []byte, desc description.SelectedServer) ([]byte, error) {\n\tdst = bsoncore.AppendStringElement(dst, \"count\", c.collection)\n\tif c.query != nil {\n\t\tdst = bsoncore.AppendDocumentElement(dst, \"query\", c.query)\n\t}\n\tif c.comment.Type != bsoncore.Type(0) {\n\t\tdst = bsoncore.AppendValueElement(dst, \"comment\", c.comment)\n\t}\n\t// Set rawData for 8.2+ servers.\n\tif c.rawData != nil && desc.WireVersion != nil && driverutil.VersionRangeIncludes(*desc.WireVersion, 27) {\n\t\tdst = bsoncore.AppendBooleanElement(dst, \"rawData\", *c.rawData)\n\t}\n\treturn dst, nil\n}\n\n// Query determines what results are returned from find.\nfunc (c *Count) Query(query bsoncore.Document) *Count {\n\tif c == nil {\n\t\tc = new(Count)\n\t}\n\n\tc.query = query\n\treturn c\n}\n\n// Session sets the session for this operation.\nfunc (c *Count) Session(session *session.Client) *Count {\n\tif c == nil {\n\t\tc = new(Count)\n\t}\n\n\tc.session = session\n\treturn c\n}\n\n// ClusterClock sets the cluster clock for this operation.\nfunc (c *Count) ClusterClock(clock *session.ClusterClock) *Count {\n\tif c == nil {\n\t\tc = new(Count)\n\t}\n\n\tc.clock = clock\n\treturn c\n}\n\n// Collection sets the collection that this command will run against.\nfunc (c *Count) Collection(collection string) *Count {\n\tif c == nil {\n\t\tc = new(Count)\n\t}\n\n\tc.collection = collection\n\treturn c\n}\n\n// Comment sets a value to help trace an operation.\nfunc (c *Count) Comment(comment bsoncore.Value) *Count {\n\tif c == nil {\n\t\tc = new(Count)\n\t}\n\n\tc.comment = comment\n\treturn c\n}\n\n// CommandMonitor sets the monitor to use for APM events.\nfunc (c *Count) CommandMonitor(monitor *event.CommandMonitor) *Count {\n\tif c == nil {\n\t\tc = new(Count)\n\t}\n\n\tc.monitor = monitor\n\treturn c\n}\n\n// Crypt sets the Crypt object to use for automatic encryption and decryption.\nfunc (c *Count) Crypt(crypt driver.Crypt) *Count {\n\tif c == nil {\n\t\tc = new(Count)\n\t}\n\n\tc.crypt = crypt\n\treturn c\n}\n\n// Database sets the database to run this operation against.\nfunc (c *Count) Database(database string) *Count {\n\tif c == nil {\n\t\tc = new(Count)\n\t}\n\n\tc.database = database\n\treturn c\n}\n\n// Deployment sets the deployment to use for this operation.\nfunc (c *Count) Deployment(deployment driver.Deployment) *Count {\n\tif c == nil {\n\t\tc = new(Count)\n\t}\n\n\tc.deployment = deployment\n\treturn c\n}\n\n// ReadConcern specifies the read concern for this operation.\nfunc (c *Count) ReadConcern(readConcern *readconcern.ReadConcern) *Count {\n\tif c == nil {\n\t\tc = new(Count)\n\t}\n\n\tc.readConcern = readConcern\n\treturn c\n}\n\n// ReadPreference set the read preference used with this operation.\nfunc (c *Count) ReadPreference(readPreference *readpref.ReadPref) *Count {\n\tif c == nil {\n\t\tc = new(Count)\n\t}\n\n\tc.readPreference = readPreference\n\treturn c\n}\n\n// ServerSelector sets the selector used to retrieve a server.\nfunc (c *Count) ServerSelector(selector description.ServerSelector) *Count {\n\tif c == nil {\n\t\tc = new(Count)\n\t}\n\n\tc.selector = selector\n\treturn c\n}\n\n// Retry enables retryable mode for this operation. Retries are handled automatically in driver.Operation.Execute based\n// on how the operation is set.\nfunc (c *Count) Retry(retry driver.RetryMode) *Count {\n\tif c == nil {\n\t\tc = new(Count)\n\t}\n\n\tc.retry = &retry\n\treturn c\n}\n\n// ServerAPI sets the server API version for this operation.\nfunc (c *Count) ServerAPI(serverAPI *driver.ServerAPIOptions) *Count {\n\tif c == nil {\n\t\tc = new(Count)\n\t}\n\n\tc.serverAPI = serverAPI\n\treturn c\n}\n\n// Timeout sets the timeout for this operation.\nfunc (c *Count) Timeout(timeout *time.Duration) *Count {\n\tif c == nil {\n\t\tc = new(Count)\n\t}\n\n\tc.timeout = timeout\n\treturn c\n}\n\n// Authenticator sets the authenticator to use for this operation.\nfunc (c *Count) Authenticator(authenticator driver.Authenticator) *Count {\n\tif c == nil {\n\t\tc = new(Count)\n\t}\n\n\tc.authenticator = authenticator\n\treturn c\n}\n\n// RawData sets the rawData to access timeseries data in the compressed format.\nfunc (c *Count) RawData(rawData bool) *Count {\n\tif c == nil {\n\t\tc = new(Count)\n\t}\n\n\tc.rawData = &rawData\n\treturn c\n}\n"
  },
  {
    "path": "x/mongo/driver/operation/create.go",
    "content": "// Copyright (C) MongoDB, Inc. 2019-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage operation\n\nimport (\n\t\"context\"\n\t\"errors\"\n\n\t\"go.mongodb.org/mongo-driver/v2/event\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/driverutil\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/writeconcern\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/description\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/session\"\n)\n\n// Create represents a create operation.\ntype Create struct {\n\tauthenticator                driver.Authenticator\n\tcapped                       *bool\n\tcollation                    bsoncore.Document\n\tchangeStreamPreAndPostImages bsoncore.Document\n\tcollectionName               *string\n\tindexOptionDefaults          bsoncore.Document\n\tmax                          *int64\n\tpipeline                     bsoncore.Document\n\tsize                         *int64\n\tstorageEngine                bsoncore.Document\n\tvalidationAction             *string\n\tvalidationLevel              *string\n\tvalidator                    bsoncore.Document\n\tviewOn                       *string\n\tsession                      *session.Client\n\tclock                        *session.ClusterClock\n\tmonitor                      *event.CommandMonitor\n\tcrypt                        driver.Crypt\n\tdatabase                     string\n\tdeployment                   driver.Deployment\n\tselector                     description.ServerSelector\n\twriteConcern                 *writeconcern.WriteConcern\n\tserverAPI                    *driver.ServerAPIOptions\n\texpireAfterSeconds           *int64\n\ttimeSeries                   bsoncore.Document\n\tencryptedFields              bsoncore.Document\n\tclusteredIndex               bsoncore.Document\n}\n\n// NewCreate constructs and returns a new Create.\nfunc NewCreate(collectionName string) *Create {\n\treturn &Create{\n\t\tcollectionName: &collectionName,\n\t}\n}\n\nfunc (c *Create) processResponse(context.Context, bsoncore.Document, driver.ResponseInfo) error {\n\treturn nil\n}\n\n// Execute runs this operations and returns an error if the operation did not execute successfully.\nfunc (c *Create) Execute(ctx context.Context) error {\n\tif c.deployment == nil {\n\t\treturn errors.New(\"the Create operation must have a Deployment set before Execute can be called\")\n\t}\n\n\treturn driver.Operation{\n\t\tCommandFn:         c.command,\n\t\tProcessResponseFn: c.processResponse,\n\t\tClient:            c.session,\n\t\tClock:             c.clock,\n\t\tCommandMonitor:    c.monitor,\n\t\tCrypt:             c.crypt,\n\t\tDatabase:          c.database,\n\t\tDeployment:        c.deployment,\n\t\tSelector:          c.selector,\n\t\tWriteConcern:      c.writeConcern,\n\t\tServerAPI:         c.serverAPI,\n\t\tAuthenticator:     c.authenticator,\n\t}.Execute(ctx)\n}\n\nfunc (c *Create) command(dst []byte, desc description.SelectedServer) ([]byte, error) {\n\tif c.collectionName != nil {\n\t\tdst = bsoncore.AppendStringElement(dst, \"create\", *c.collectionName)\n\t}\n\tif c.capped != nil {\n\t\tdst = bsoncore.AppendBooleanElement(dst, \"capped\", *c.capped)\n\t}\n\tif c.changeStreamPreAndPostImages != nil {\n\t\tdst = bsoncore.AppendDocumentElement(dst, \"changeStreamPreAndPostImages\", c.changeStreamPreAndPostImages)\n\t}\n\tif c.collation != nil {\n\t\tif desc.WireVersion == nil || !driverutil.VersionRangeIncludes(*desc.WireVersion, 5) {\n\t\t\treturn nil, errors.New(\"the 'collation' command parameter requires a minimum server wire version of 5\")\n\t\t}\n\t\tdst = bsoncore.AppendDocumentElement(dst, \"collation\", c.collation)\n\t}\n\tif c.indexOptionDefaults != nil {\n\t\tdst = bsoncore.AppendDocumentElement(dst, \"indexOptionDefaults\", c.indexOptionDefaults)\n\t}\n\tif c.max != nil {\n\t\tdst = bsoncore.AppendInt64Element(dst, \"max\", *c.max)\n\t}\n\tif c.pipeline != nil {\n\t\tdst = bsoncore.AppendArrayElement(dst, \"pipeline\", c.pipeline)\n\t}\n\tif c.size != nil {\n\t\tdst = bsoncore.AppendInt64Element(dst, \"size\", *c.size)\n\t}\n\tif c.storageEngine != nil {\n\t\tdst = bsoncore.AppendDocumentElement(dst, \"storageEngine\", c.storageEngine)\n\t}\n\tif c.validationAction != nil {\n\t\tdst = bsoncore.AppendStringElement(dst, \"validationAction\", *c.validationAction)\n\t}\n\tif c.validationLevel != nil {\n\t\tdst = bsoncore.AppendStringElement(dst, \"validationLevel\", *c.validationLevel)\n\t}\n\tif c.validator != nil {\n\t\tdst = bsoncore.AppendDocumentElement(dst, \"validator\", c.validator)\n\t}\n\tif c.viewOn != nil {\n\t\tdst = bsoncore.AppendStringElement(dst, \"viewOn\", *c.viewOn)\n\t}\n\tif c.expireAfterSeconds != nil {\n\t\tdst = bsoncore.AppendInt64Element(dst, \"expireAfterSeconds\", *c.expireAfterSeconds)\n\t}\n\tif c.timeSeries != nil {\n\t\tdst = bsoncore.AppendDocumentElement(dst, \"timeseries\", c.timeSeries)\n\t}\n\tif c.encryptedFields != nil {\n\t\tdst = bsoncore.AppendDocumentElement(dst, \"encryptedFields\", c.encryptedFields)\n\t}\n\tif c.clusteredIndex != nil {\n\t\tdst = bsoncore.AppendDocumentElement(dst, \"clusteredIndex\", c.clusteredIndex)\n\t}\n\treturn dst, nil\n}\n\n// Capped specifies if the collection is capped.\nfunc (c *Create) Capped(capped bool) *Create {\n\tif c == nil {\n\t\tc = new(Create)\n\t}\n\n\tc.capped = &capped\n\treturn c\n}\n\n// Collation specifies a collation.\nfunc (c *Create) Collation(collation bsoncore.Document) *Create {\n\tif c == nil {\n\t\tc = new(Create)\n\t}\n\n\tc.collation = collation\n\treturn c\n}\n\n// ChangeStreamPreAndPostImages specifies how change streams opened against the collection can return pre-\n// and post-images of updated documents. This option is only valid for server versions 6.0 and above.\nfunc (c *Create) ChangeStreamPreAndPostImages(csppi bsoncore.Document) *Create {\n\tif c == nil {\n\t\tc = new(Create)\n\t}\n\n\tc.changeStreamPreAndPostImages = csppi\n\treturn c\n}\n\n// CollectionName specifies the name of the collection to create.\nfunc (c *Create) CollectionName(collectionName string) *Create {\n\tif c == nil {\n\t\tc = new(Create)\n\t}\n\n\tc.collectionName = &collectionName\n\treturn c\n}\n\n// IndexOptionDefaults specifies a default configuration for indexes on the collection.\nfunc (c *Create) IndexOptionDefaults(indexOptionDefaults bsoncore.Document) *Create {\n\tif c == nil {\n\t\tc = new(Create)\n\t}\n\n\tc.indexOptionDefaults = indexOptionDefaults\n\treturn c\n}\n\n// Max specifies the maximum number of documents allowed in a capped collection.\nfunc (c *Create) Max(max int64) *Create {\n\tif c == nil {\n\t\tc = new(Create)\n\t}\n\n\tc.max = &max\n\treturn c\n}\n\n// Pipeline specifies the agggregtion pipeline to be run against the source to create the view.\nfunc (c *Create) Pipeline(pipeline bsoncore.Document) *Create {\n\tif c == nil {\n\t\tc = new(Create)\n\t}\n\n\tc.pipeline = pipeline\n\treturn c\n}\n\n// Size specifies the maximum size in bytes for a capped collection.\nfunc (c *Create) Size(size int64) *Create {\n\tif c == nil {\n\t\tc = new(Create)\n\t}\n\n\tc.size = &size\n\treturn c\n}\n\n// StorageEngine specifies the storage engine to use for the index.\nfunc (c *Create) StorageEngine(storageEngine bsoncore.Document) *Create {\n\tif c == nil {\n\t\tc = new(Create)\n\t}\n\n\tc.storageEngine = storageEngine\n\treturn c\n}\n\n// ValidationAction specifies what should happen if a document being inserted does not pass validation.\nfunc (c *Create) ValidationAction(validationAction string) *Create {\n\tif c == nil {\n\t\tc = new(Create)\n\t}\n\n\tc.validationAction = &validationAction\n\treturn c\n}\n\n// ValidationLevel specifies how strictly the server applies validation rules to existing documents in the collection\n// during update operations.\nfunc (c *Create) ValidationLevel(validationLevel string) *Create {\n\tif c == nil {\n\t\tc = new(Create)\n\t}\n\n\tc.validationLevel = &validationLevel\n\treturn c\n}\n\n// Validator specifies validation rules for the collection.\nfunc (c *Create) Validator(validator bsoncore.Document) *Create {\n\tif c == nil {\n\t\tc = new(Create)\n\t}\n\n\tc.validator = validator\n\treturn c\n}\n\n// ViewOn specifies the name of the source collection or view on which the view will be created.\nfunc (c *Create) ViewOn(viewOn string) *Create {\n\tif c == nil {\n\t\tc = new(Create)\n\t}\n\n\tc.viewOn = &viewOn\n\treturn c\n}\n\n// Session sets the session for this operation.\nfunc (c *Create) Session(session *session.Client) *Create {\n\tif c == nil {\n\t\tc = new(Create)\n\t}\n\n\tc.session = session\n\treturn c\n}\n\n// ClusterClock sets the cluster clock for this operation.\nfunc (c *Create) ClusterClock(clock *session.ClusterClock) *Create {\n\tif c == nil {\n\t\tc = new(Create)\n\t}\n\n\tc.clock = clock\n\treturn c\n}\n\n// CommandMonitor sets the monitor to use for APM events.\nfunc (c *Create) CommandMonitor(monitor *event.CommandMonitor) *Create {\n\tif c == nil {\n\t\tc = new(Create)\n\t}\n\n\tc.monitor = monitor\n\treturn c\n}\n\n// Crypt sets the Crypt object to use for automatic encryption and decryption.\nfunc (c *Create) Crypt(crypt driver.Crypt) *Create {\n\tif c == nil {\n\t\tc = new(Create)\n\t}\n\n\tc.crypt = crypt\n\treturn c\n}\n\n// Database sets the database to run this operation against.\nfunc (c *Create) Database(database string) *Create {\n\tif c == nil {\n\t\tc = new(Create)\n\t}\n\n\tc.database = database\n\treturn c\n}\n\n// Deployment sets the deployment to use for this operation.\nfunc (c *Create) Deployment(deployment driver.Deployment) *Create {\n\tif c == nil {\n\t\tc = new(Create)\n\t}\n\n\tc.deployment = deployment\n\treturn c\n}\n\n// ServerSelector sets the selector used to retrieve a server.\nfunc (c *Create) ServerSelector(selector description.ServerSelector) *Create {\n\tif c == nil {\n\t\tc = new(Create)\n\t}\n\n\tc.selector = selector\n\treturn c\n}\n\n// WriteConcern sets the write concern for this operation.\nfunc (c *Create) WriteConcern(writeConcern *writeconcern.WriteConcern) *Create {\n\tif c == nil {\n\t\tc = new(Create)\n\t}\n\n\tc.writeConcern = writeConcern\n\treturn c\n}\n\n// ServerAPI sets the server API version for this operation.\nfunc (c *Create) ServerAPI(serverAPI *driver.ServerAPIOptions) *Create {\n\tif c == nil {\n\t\tc = new(Create)\n\t}\n\n\tc.serverAPI = serverAPI\n\treturn c\n}\n\n// ExpireAfterSeconds sets the seconds to wait before deleting old time-series data.\nfunc (c *Create) ExpireAfterSeconds(eas int64) *Create {\n\tif c == nil {\n\t\tc = new(Create)\n\t}\n\n\tc.expireAfterSeconds = &eas\n\treturn c\n}\n\n// TimeSeries sets the time series options for this operation.\nfunc (c *Create) TimeSeries(timeSeries bsoncore.Document) *Create {\n\tif c == nil {\n\t\tc = new(Create)\n\t}\n\n\tc.timeSeries = timeSeries\n\treturn c\n}\n\n// EncryptedFields sets the EncryptedFields for this operation.\nfunc (c *Create) EncryptedFields(ef bsoncore.Document) *Create {\n\tif c == nil {\n\t\tc = new(Create)\n\t}\n\n\tc.encryptedFields = ef\n\treturn c\n}\n\n// ClusteredIndex sets the ClusteredIndex option for this operation.\nfunc (c *Create) ClusteredIndex(ci bsoncore.Document) *Create {\n\tif c == nil {\n\t\tc = new(Create)\n\t}\n\n\tc.clusteredIndex = ci\n\treturn c\n}\n\n// Authenticator sets the authenticator to use for this operation.\nfunc (c *Create) Authenticator(authenticator driver.Authenticator) *Create {\n\tif c == nil {\n\t\tc = new(Create)\n\t}\n\n\tc.authenticator = authenticator\n\treturn c\n}\n"
  },
  {
    "path": "x/mongo/driver/operation/create_indexes.go",
    "content": "// Copyright (C) MongoDB, Inc. 2019-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage operation\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/event\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/driverutil\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/writeconcern\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/description\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/session\"\n)\n\n// CreateIndexes performs a createIndexes operation.\ntype CreateIndexes struct {\n\tauthenticator driver.Authenticator\n\tcommitQuorum  bsoncore.Value\n\tindexes       bsoncore.Document\n\tsession       *session.Client\n\tclock         *session.ClusterClock\n\tcollection    string\n\tmonitor       *event.CommandMonitor\n\tcrypt         driver.Crypt\n\tdatabase      string\n\tdeployment    driver.Deployment\n\tselector      description.ServerSelector\n\twriteConcern  *writeconcern.WriteConcern\n\tresult        CreateIndexesResult\n\tserverAPI     *driver.ServerAPIOptions\n\ttimeout       *time.Duration\n\trawData       *bool\n}\n\n// CreateIndexesResult represents a createIndexes result returned by the server.\ntype CreateIndexesResult struct {\n\t// If the collection was created automatically.\n\tCreatedCollectionAutomatically bool\n\t// The number of indexes existing after this command.\n\tIndexesAfter int32\n\t// The number of indexes existing before this command.\n\tIndexesBefore int32\n}\n\nfunc buildCreateIndexesResult(response bsoncore.Document) (CreateIndexesResult, error) {\n\telements, err := response.Elements()\n\tif err != nil {\n\t\treturn CreateIndexesResult{}, err\n\t}\n\tcir := CreateIndexesResult{}\n\tfor _, element := range elements {\n\t\tswitch element.Key() {\n\t\tcase \"createdCollectionAutomatically\":\n\t\t\tvar ok bool\n\t\t\tcir.CreatedCollectionAutomatically, ok = element.Value().BooleanOK()\n\t\t\tif !ok {\n\t\t\t\treturn cir, fmt.Errorf(\"response field 'createdCollectionAutomatically' is type bool, but received BSON type %s\", element.Value().Type)\n\t\t\t}\n\t\tcase \"indexesAfter\":\n\t\t\tvar ok bool\n\t\t\tcir.IndexesAfter, ok = element.Value().AsInt32OK()\n\t\t\tif !ok {\n\t\t\t\treturn cir, fmt.Errorf(\"response field 'indexesAfter' is type int32, but received BSON type %s\", element.Value().Type)\n\t\t\t}\n\t\tcase \"indexesBefore\":\n\t\t\tvar ok bool\n\t\t\tcir.IndexesBefore, ok = element.Value().AsInt32OK()\n\t\t\tif !ok {\n\t\t\t\treturn cir, fmt.Errorf(\"response field 'indexesBefore' is type int32, but received BSON type %s\", element.Value().Type)\n\t\t\t}\n\t\t}\n\t}\n\treturn cir, nil\n}\n\n// NewCreateIndexes constructs and returns a new CreateIndexes.\nfunc NewCreateIndexes(indexes bsoncore.Document) *CreateIndexes {\n\treturn &CreateIndexes{\n\t\tindexes: indexes,\n\t}\n}\n\n// Result returns the result of executing this operation.\nfunc (ci *CreateIndexes) Result() CreateIndexesResult { return ci.result }\n\nfunc (ci *CreateIndexes) processResponse(_ context.Context, resp bsoncore.Document, _ driver.ResponseInfo) error {\n\tvar err error\n\tci.result, err = buildCreateIndexesResult(resp)\n\treturn err\n}\n\n// Execute runs this operations and returns an error if the operation did not execute successfully.\nfunc (ci *CreateIndexes) Execute(ctx context.Context) error {\n\tif ci.deployment == nil {\n\t\treturn errors.New(\"the CreateIndexes operation must have a Deployment set before Execute can be called\")\n\t}\n\n\treturn driver.Operation{\n\t\tCommandFn:         ci.command,\n\t\tProcessResponseFn: ci.processResponse,\n\t\tClient:            ci.session,\n\t\tClock:             ci.clock,\n\t\tCommandMonitor:    ci.monitor,\n\t\tCrypt:             ci.crypt,\n\t\tDatabase:          ci.database,\n\t\tDeployment:        ci.deployment,\n\t\tSelector:          ci.selector,\n\t\tWriteConcern:      ci.writeConcern,\n\t\tServerAPI:         ci.serverAPI,\n\t\tTimeout:           ci.timeout,\n\t\tName:              driverutil.CreateIndexesOp,\n\t\tAuthenticator:     ci.authenticator,\n\t}.Execute(ctx)\n}\n\nfunc (ci *CreateIndexes) command(dst []byte, desc description.SelectedServer) ([]byte, error) {\n\tdst = bsoncore.AppendStringElement(dst, \"createIndexes\", ci.collection)\n\tif ci.commitQuorum.Type != bsoncore.Type(0) {\n\t\tif desc.WireVersion == nil || !driverutil.VersionRangeIncludes(*desc.WireVersion, 9) {\n\t\t\treturn nil, errors.New(\"the 'commitQuorum' command parameter requires a minimum server wire version of 9\")\n\t\t}\n\t\tdst = bsoncore.AppendValueElement(dst, \"commitQuorum\", ci.commitQuorum)\n\t}\n\tif ci.indexes != nil {\n\t\tdst = bsoncore.AppendArrayElement(dst, \"indexes\", ci.indexes)\n\t}\n\t// Set rawData for 8.2+ servers.\n\tif ci.rawData != nil && desc.WireVersion != nil && driverutil.VersionRangeIncludes(*desc.WireVersion, 27) {\n\t\tdst = bsoncore.AppendBooleanElement(dst, \"rawData\", *ci.rawData)\n\t}\n\treturn dst, nil\n}\n\n// CommitQuorum specifies the number of data-bearing members of a replica set, including the primary, that must\n// complete the index builds successfully before the primary marks the indexes as ready. This should either be a\n// string or int32 value.\nfunc (ci *CreateIndexes) CommitQuorum(commitQuorum bsoncore.Value) *CreateIndexes {\n\tif ci == nil {\n\t\tci = new(CreateIndexes)\n\t}\n\n\tci.commitQuorum = commitQuorum\n\treturn ci\n}\n\n// Indexes specifies an array containing index specification documents for the indexes being created.\nfunc (ci *CreateIndexes) Indexes(indexes bsoncore.Document) *CreateIndexes {\n\tif ci == nil {\n\t\tci = new(CreateIndexes)\n\t}\n\n\tci.indexes = indexes\n\treturn ci\n}\n\n// Session sets the session for this operation.\nfunc (ci *CreateIndexes) Session(session *session.Client) *CreateIndexes {\n\tif ci == nil {\n\t\tci = new(CreateIndexes)\n\t}\n\n\tci.session = session\n\treturn ci\n}\n\n// ClusterClock sets the cluster clock for this operation.\nfunc (ci *CreateIndexes) ClusterClock(clock *session.ClusterClock) *CreateIndexes {\n\tif ci == nil {\n\t\tci = new(CreateIndexes)\n\t}\n\n\tci.clock = clock\n\treturn ci\n}\n\n// Collection sets the collection that this command will run against.\nfunc (ci *CreateIndexes) Collection(collection string) *CreateIndexes {\n\tif ci == nil {\n\t\tci = new(CreateIndexes)\n\t}\n\n\tci.collection = collection\n\treturn ci\n}\n\n// CommandMonitor sets the monitor to use for APM events.\nfunc (ci *CreateIndexes) CommandMonitor(monitor *event.CommandMonitor) *CreateIndexes {\n\tif ci == nil {\n\t\tci = new(CreateIndexes)\n\t}\n\n\tci.monitor = monitor\n\treturn ci\n}\n\n// Crypt sets the Crypt object to use for automatic encryption and decryption.\nfunc (ci *CreateIndexes) Crypt(crypt driver.Crypt) *CreateIndexes {\n\tif ci == nil {\n\t\tci = new(CreateIndexes)\n\t}\n\n\tci.crypt = crypt\n\treturn ci\n}\n\n// Database sets the database to run this operation against.\nfunc (ci *CreateIndexes) Database(database string) *CreateIndexes {\n\tif ci == nil {\n\t\tci = new(CreateIndexes)\n\t}\n\n\tci.database = database\n\treturn ci\n}\n\n// Deployment sets the deployment to use for this operation.\nfunc (ci *CreateIndexes) Deployment(deployment driver.Deployment) *CreateIndexes {\n\tif ci == nil {\n\t\tci = new(CreateIndexes)\n\t}\n\n\tci.deployment = deployment\n\treturn ci\n}\n\n// ServerSelector sets the selector used to retrieve a server.\nfunc (ci *CreateIndexes) ServerSelector(selector description.ServerSelector) *CreateIndexes {\n\tif ci == nil {\n\t\tci = new(CreateIndexes)\n\t}\n\n\tci.selector = selector\n\treturn ci\n}\n\n// WriteConcern sets the write concern for this operation.\nfunc (ci *CreateIndexes) WriteConcern(writeConcern *writeconcern.WriteConcern) *CreateIndexes {\n\tif ci == nil {\n\t\tci = new(CreateIndexes)\n\t}\n\n\tci.writeConcern = writeConcern\n\treturn ci\n}\n\n// ServerAPI sets the server API version for this operation.\nfunc (ci *CreateIndexes) ServerAPI(serverAPI *driver.ServerAPIOptions) *CreateIndexes {\n\tif ci == nil {\n\t\tci = new(CreateIndexes)\n\t}\n\n\tci.serverAPI = serverAPI\n\treturn ci\n}\n\n// Timeout sets the timeout for this operation.\nfunc (ci *CreateIndexes) Timeout(timeout *time.Duration) *CreateIndexes {\n\tif ci == nil {\n\t\tci = new(CreateIndexes)\n\t}\n\n\tci.timeout = timeout\n\treturn ci\n}\n\n// Authenticator sets the authenticator to use for this operation.\nfunc (ci *CreateIndexes) Authenticator(authenticator driver.Authenticator) *CreateIndexes {\n\tif ci == nil {\n\t\tci = new(CreateIndexes)\n\t}\n\n\tci.authenticator = authenticator\n\treturn ci\n}\n\n// RawData sets the rawData to access timeseries data in the compressed format.\nfunc (ci *CreateIndexes) RawData(rawData bool) *CreateIndexes {\n\tif ci == nil {\n\t\tci = new(CreateIndexes)\n\t}\n\n\tci.rawData = &rawData\n\treturn ci\n}\n"
  },
  {
    "path": "x/mongo/driver/operation/create_search_indexes.go",
    "content": "// Copyright (C) MongoDB, Inc. 2023-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage operation\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/event\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/description\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/session\"\n)\n\n// CreateSearchIndexes performs a createSearchIndexes operation.\ntype CreateSearchIndexes struct {\n\tauthenticator driver.Authenticator\n\tindexes       bsoncore.Document\n\tsession       *session.Client\n\tclock         *session.ClusterClock\n\tcollection    string\n\tmonitor       *event.CommandMonitor\n\tcrypt         driver.Crypt\n\tdatabase      string\n\tdeployment    driver.Deployment\n\tselector      description.ServerSelector\n\tresult        CreateSearchIndexesResult\n\tserverAPI     *driver.ServerAPIOptions\n\ttimeout       *time.Duration\n}\n\n// CreateSearchIndexResult represents a single search index result in CreateSearchIndexesResult.\ntype CreateSearchIndexResult struct {\n\tName string\n}\n\n// CreateSearchIndexesResult represents a createSearchIndexes result returned by the server.\ntype CreateSearchIndexesResult struct {\n\tIndexesCreated []CreateSearchIndexResult\n}\n\nfunc buildCreateSearchIndexesResult(response bsoncore.Document) (CreateSearchIndexesResult, error) {\n\telements, err := response.Elements()\n\tif err != nil {\n\t\treturn CreateSearchIndexesResult{}, err\n\t}\n\tcsir := CreateSearchIndexesResult{}\n\tfor _, element := range elements {\n\t\tswitch element.Key() {\n\t\tcase \"indexesCreated\":\n\t\t\tarr, ok := element.Value().ArrayOK()\n\t\t\tif !ok {\n\t\t\t\treturn csir, fmt.Errorf(\"response field 'indexesCreated' is type array, but received BSON type %s\", element.Value().Type)\n\t\t\t}\n\n\t\t\tvar values []bsoncore.Value\n\t\t\tvalues, err = arr.Values()\n\t\t\tif err != nil {\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\tfor _, val := range values {\n\t\t\t\tvalDoc, ok := val.DocumentOK()\n\t\t\t\tif !ok {\n\t\t\t\t\treturn csir, fmt.Errorf(\"indexesCreated value is type document, but received BSON type %s\", val.Type)\n\t\t\t\t}\n\t\t\t\tvar indexesCreated CreateSearchIndexResult\n\t\t\t\tif err = bson.Unmarshal(valDoc, &indexesCreated); err != nil {\n\t\t\t\t\treturn csir, err\n\t\t\t\t}\n\t\t\t\tcsir.IndexesCreated = append(csir.IndexesCreated, indexesCreated)\n\t\t\t}\n\t\t}\n\t}\n\treturn csir, nil\n}\n\n// NewCreateSearchIndexes constructs and returns a new CreateSearchIndexes.\nfunc NewCreateSearchIndexes(indexes bsoncore.Document) *CreateSearchIndexes {\n\treturn &CreateSearchIndexes{\n\t\tindexes: indexes,\n\t}\n}\n\n// Result returns the result of executing this operation.\nfunc (csi *CreateSearchIndexes) Result() CreateSearchIndexesResult { return csi.result }\n\nfunc (csi *CreateSearchIndexes) processResponse(_ context.Context, resp bsoncore.Document, _ driver.ResponseInfo) error {\n\tvar err error\n\tcsi.result, err = buildCreateSearchIndexesResult(resp)\n\treturn err\n}\n\n// Execute runs this operations and returns an error if the operation did not execute successfully.\nfunc (csi *CreateSearchIndexes) Execute(ctx context.Context) error {\n\tif csi.deployment == nil {\n\t\treturn errors.New(\"the CreateSearchIndexes operation must have a Deployment set before Execute can be called\")\n\t}\n\n\treturn driver.Operation{\n\t\tCommandFn:         csi.command,\n\t\tProcessResponseFn: csi.processResponse,\n\t\tClient:            csi.session,\n\t\tClock:             csi.clock,\n\t\tCommandMonitor:    csi.monitor,\n\t\tCrypt:             csi.crypt,\n\t\tDatabase:          csi.database,\n\t\tDeployment:        csi.deployment,\n\t\tSelector:          csi.selector,\n\t\tServerAPI:         csi.serverAPI,\n\t\tTimeout:           csi.timeout,\n\t\tAuthenticator:     csi.authenticator,\n\t}.Execute(ctx)\n}\n\nfunc (csi *CreateSearchIndexes) command(dst []byte, _ description.SelectedServer) ([]byte, error) {\n\tdst = bsoncore.AppendStringElement(dst, \"createSearchIndexes\", csi.collection)\n\tif csi.indexes != nil {\n\t\tdst = bsoncore.AppendArrayElement(dst, \"indexes\", csi.indexes)\n\t}\n\treturn dst, nil\n}\n\n// Indexes specifies an array containing index specification documents for the indexes being created.\nfunc (csi *CreateSearchIndexes) Indexes(indexes bsoncore.Document) *CreateSearchIndexes {\n\tif csi == nil {\n\t\tcsi = new(CreateSearchIndexes)\n\t}\n\n\tcsi.indexes = indexes\n\treturn csi\n}\n\n// Session sets the session for this operation.\nfunc (csi *CreateSearchIndexes) Session(session *session.Client) *CreateSearchIndexes {\n\tif csi == nil {\n\t\tcsi = new(CreateSearchIndexes)\n\t}\n\n\tcsi.session = session\n\treturn csi\n}\n\n// ClusterClock sets the cluster clock for this operation.\nfunc (csi *CreateSearchIndexes) ClusterClock(clock *session.ClusterClock) *CreateSearchIndexes {\n\tif csi == nil {\n\t\tcsi = new(CreateSearchIndexes)\n\t}\n\n\tcsi.clock = clock\n\treturn csi\n}\n\n// Collection sets the collection that this command will run against.\nfunc (csi *CreateSearchIndexes) Collection(collection string) *CreateSearchIndexes {\n\tif csi == nil {\n\t\tcsi = new(CreateSearchIndexes)\n\t}\n\n\tcsi.collection = collection\n\treturn csi\n}\n\n// CommandMonitor sets the monitor to use for APM events.\nfunc (csi *CreateSearchIndexes) CommandMonitor(monitor *event.CommandMonitor) *CreateSearchIndexes {\n\tif csi == nil {\n\t\tcsi = new(CreateSearchIndexes)\n\t}\n\n\tcsi.monitor = monitor\n\treturn csi\n}\n\n// Crypt sets the Crypt object to use for automatic encryption and decryption.\nfunc (csi *CreateSearchIndexes) Crypt(crypt driver.Crypt) *CreateSearchIndexes {\n\tif csi == nil {\n\t\tcsi = new(CreateSearchIndexes)\n\t}\n\n\tcsi.crypt = crypt\n\treturn csi\n}\n\n// Database sets the database to run this operation against.\nfunc (csi *CreateSearchIndexes) Database(database string) *CreateSearchIndexes {\n\tif csi == nil {\n\t\tcsi = new(CreateSearchIndexes)\n\t}\n\n\tcsi.database = database\n\treturn csi\n}\n\n// Deployment sets the deployment to use for this operation.\nfunc (csi *CreateSearchIndexes) Deployment(deployment driver.Deployment) *CreateSearchIndexes {\n\tif csi == nil {\n\t\tcsi = new(CreateSearchIndexes)\n\t}\n\n\tcsi.deployment = deployment\n\treturn csi\n}\n\n// ServerSelector sets the selector used to retrieve a server.\nfunc (csi *CreateSearchIndexes) ServerSelector(selector description.ServerSelector) *CreateSearchIndexes {\n\tif csi == nil {\n\t\tcsi = new(CreateSearchIndexes)\n\t}\n\n\tcsi.selector = selector\n\treturn csi\n}\n\n// ServerAPI sets the server API version for this operation.\nfunc (csi *CreateSearchIndexes) ServerAPI(serverAPI *driver.ServerAPIOptions) *CreateSearchIndexes {\n\tif csi == nil {\n\t\tcsi = new(CreateSearchIndexes)\n\t}\n\n\tcsi.serverAPI = serverAPI\n\treturn csi\n}\n\n// Timeout sets the timeout for this operation.\nfunc (csi *CreateSearchIndexes) Timeout(timeout *time.Duration) *CreateSearchIndexes {\n\tif csi == nil {\n\t\tcsi = new(CreateSearchIndexes)\n\t}\n\n\tcsi.timeout = timeout\n\treturn csi\n}\n\n// Authenticator sets the authenticator to use for this operation.\nfunc (csi *CreateSearchIndexes) Authenticator(authenticator driver.Authenticator) *CreateSearchIndexes {\n\tif csi == nil {\n\t\tcsi = new(CreateSearchIndexes)\n\t}\n\n\tcsi.authenticator = authenticator\n\treturn csi\n}\n"
  },
  {
    "path": "x/mongo/driver/operation/delete.go",
    "content": "// Copyright (C) MongoDB, Inc. 2019-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage operation\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/event\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/driverutil\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/logger\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/writeconcern\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/description\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/session\"\n)\n\n// Delete performs a delete operation\ntype Delete struct {\n\tauthenticator driver.Authenticator\n\tcomment       bsoncore.Value\n\tdeletes       []bsoncore.Document\n\tordered       *bool\n\tsession       *session.Client\n\tclock         *session.ClusterClock\n\tcollection    string\n\tmonitor       *event.CommandMonitor\n\tcrypt         driver.Crypt\n\tdatabase      string\n\tdeployment    driver.Deployment\n\tselector      description.ServerSelector\n\twriteConcern  *writeconcern.WriteConcern\n\tretry         *driver.RetryMode\n\thint          *bool\n\tresult        DeleteResult\n\tserverAPI     *driver.ServerAPIOptions\n\tlet           bsoncore.Document\n\ttimeout       *time.Duration\n\trawData       *bool\n\tlogger        *logger.Logger\n}\n\n// DeleteResult represents a delete result returned by the server.\ntype DeleteResult struct {\n\t// Number of documents successfully deleted.\n\tN int64\n}\n\nfunc buildDeleteResult(response bsoncore.Document) (DeleteResult, error) {\n\telements, err := response.Elements()\n\tif err != nil {\n\t\treturn DeleteResult{}, err\n\t}\n\tdr := DeleteResult{}\n\tfor _, element := range elements {\n\t\tif element.Key() == \"n\" {\n\t\t\tvar ok bool\n\t\t\tdr.N, ok = element.Value().AsInt64OK()\n\t\t\tif !ok {\n\t\t\t\treturn dr, fmt.Errorf(\"response field 'n' is type int32 or int64, but received BSON type %s\", element.Value().Type)\n\t\t\t}\n\t\t}\n\t}\n\treturn dr, nil\n}\n\n// NewDelete constructs and returns a new Delete.\nfunc NewDelete(deletes ...bsoncore.Document) *Delete {\n\treturn &Delete{\n\t\tdeletes: deletes,\n\t}\n}\n\n// Result returns the result of executing this operation.\nfunc (d *Delete) Result() DeleteResult { return d.result }\n\nfunc (d *Delete) processResponse(_ context.Context, resp bsoncore.Document, _ driver.ResponseInfo) error {\n\tdr, err := buildDeleteResult(resp)\n\td.result.N += dr.N\n\treturn err\n}\n\n// Execute runs this operations and returns an error if the operation did not execute successfully.\nfunc (d *Delete) Execute(ctx context.Context) error {\n\tif d.deployment == nil {\n\t\treturn errors.New(\"the Delete operation must have a Deployment set before Execute can be called\")\n\t}\n\tbatches := &driver.Batches{\n\t\tIdentifier: \"deletes\",\n\t\tDocuments:  d.deletes,\n\t\tOrdered:    d.ordered,\n\t}\n\n\treturn driver.Operation{\n\t\tCommandFn:         d.command,\n\t\tProcessResponseFn: d.processResponse,\n\t\tBatches:           batches,\n\t\tRetryMode:         d.retry,\n\t\tType:              driver.Write,\n\t\tClient:            d.session,\n\t\tClock:             d.clock,\n\t\tCommandMonitor:    d.monitor,\n\t\tCrypt:             d.crypt,\n\t\tDatabase:          d.database,\n\t\tDeployment:        d.deployment,\n\t\tSelector:          d.selector,\n\t\tWriteConcern:      d.writeConcern,\n\t\tServerAPI:         d.serverAPI,\n\t\tTimeout:           d.timeout,\n\t\tLogger:            d.logger,\n\t\tName:              driverutil.DeleteOp,\n\t\tAuthenticator:     d.authenticator,\n\t}.Execute(ctx)\n}\n\nfunc (d *Delete) command(dst []byte, desc description.SelectedServer) ([]byte, error) {\n\tdst = bsoncore.AppendStringElement(dst, \"delete\", d.collection)\n\tif d.comment.Type != bsoncore.Type(0) {\n\t\tdst = bsoncore.AppendValueElement(dst, \"comment\", d.comment)\n\t}\n\tif d.ordered != nil {\n\t\tdst = bsoncore.AppendBooleanElement(dst, \"ordered\", *d.ordered)\n\t}\n\tif d.hint != nil && *d.hint {\n\t\tif desc.WireVersion == nil || !driverutil.VersionRangeIncludes(*desc.WireVersion, 5) {\n\t\t\treturn nil, errors.New(\"the 'hint' command parameter requires a minimum server wire version of 5\")\n\t\t}\n\t\tif !d.writeConcern.Acknowledged() {\n\t\t\treturn nil, errUnacknowledgedHint\n\t\t}\n\t}\n\tif d.let != nil {\n\t\tdst = bsoncore.AppendDocumentElement(dst, \"let\", d.let)\n\t}\n\t// Set rawData for 8.2+ servers.\n\tif d.rawData != nil && desc.WireVersion != nil && driverutil.VersionRangeIncludes(*desc.WireVersion, 27) {\n\t\tdst = bsoncore.AppendBooleanElement(dst, \"rawData\", *d.rawData)\n\t}\n\treturn dst, nil\n}\n\n// Deletes adds documents to this operation that will be used to determine what documents to delete when this operation\n// is executed. These documents should have the form {q: <query>, limit: <integer limit>, collation: <document>}. The\n// collation field is optional. If limit is 0, there will be no limit on the number of documents deleted.\nfunc (d *Delete) Deletes(deletes ...bsoncore.Document) *Delete {\n\tif d == nil {\n\t\td = new(Delete)\n\t}\n\n\td.deletes = deletes\n\treturn d\n}\n\n// Ordered sets ordered. If true, when a write fails, the operation will return the error, when\n// false write failures do not stop execution of the operation.\nfunc (d *Delete) Ordered(ordered bool) *Delete {\n\tif d == nil {\n\t\td = new(Delete)\n\t}\n\n\td.ordered = &ordered\n\treturn d\n}\n\n// Session sets the session for this operation.\nfunc (d *Delete) Session(session *session.Client) *Delete {\n\tif d == nil {\n\t\td = new(Delete)\n\t}\n\n\td.session = session\n\treturn d\n}\n\n// ClusterClock sets the cluster clock for this operation.\nfunc (d *Delete) ClusterClock(clock *session.ClusterClock) *Delete {\n\tif d == nil {\n\t\td = new(Delete)\n\t}\n\n\td.clock = clock\n\treturn d\n}\n\n// Collection sets the collection that this command will run against.\nfunc (d *Delete) Collection(collection string) *Delete {\n\tif d == nil {\n\t\td = new(Delete)\n\t}\n\n\td.collection = collection\n\treturn d\n}\n\n// Comment sets a value to help trace an operation.\nfunc (d *Delete) Comment(comment bsoncore.Value) *Delete {\n\tif d == nil {\n\t\td = new(Delete)\n\t}\n\n\td.comment = comment\n\treturn d\n}\n\n// CommandMonitor sets the monitor to use for APM events.\nfunc (d *Delete) CommandMonitor(monitor *event.CommandMonitor) *Delete {\n\tif d == nil {\n\t\td = new(Delete)\n\t}\n\n\td.monitor = monitor\n\treturn d\n}\n\n// Crypt sets the Crypt object to use for automatic encryption and decryption.\nfunc (d *Delete) Crypt(crypt driver.Crypt) *Delete {\n\tif d == nil {\n\t\td = new(Delete)\n\t}\n\n\td.crypt = crypt\n\treturn d\n}\n\n// Database sets the database to run this operation against.\nfunc (d *Delete) Database(database string) *Delete {\n\tif d == nil {\n\t\td = new(Delete)\n\t}\n\n\td.database = database\n\treturn d\n}\n\n// Deployment sets the deployment to use for this operation.\nfunc (d *Delete) Deployment(deployment driver.Deployment) *Delete {\n\tif d == nil {\n\t\td = new(Delete)\n\t}\n\n\td.deployment = deployment\n\treturn d\n}\n\n// ServerSelector sets the selector used to retrieve a server.\nfunc (d *Delete) ServerSelector(selector description.ServerSelector) *Delete {\n\tif d == nil {\n\t\td = new(Delete)\n\t}\n\n\td.selector = selector\n\treturn d\n}\n\n// WriteConcern sets the write concern for this operation.\nfunc (d *Delete) WriteConcern(writeConcern *writeconcern.WriteConcern) *Delete {\n\tif d == nil {\n\t\td = new(Delete)\n\t}\n\n\td.writeConcern = writeConcern\n\treturn d\n}\n\n// Retry enables retryable mode for this operation. Retries are handled automatically in driver.Operation.Execute based\n// on how the operation is set.\nfunc (d *Delete) Retry(retry driver.RetryMode) *Delete {\n\tif d == nil {\n\t\td = new(Delete)\n\t}\n\n\td.retry = &retry\n\treturn d\n}\n\n// Hint is a flag to indicate that the update document contains a hint. Hint is only supported by\n// servers >= 4.4. Older servers will report an error for using the hint option.\nfunc (d *Delete) Hint(hint bool) *Delete {\n\tif d == nil {\n\t\td = new(Delete)\n\t}\n\n\td.hint = &hint\n\treturn d\n}\n\n// ServerAPI sets the server API version for this operation.\nfunc (d *Delete) ServerAPI(serverAPI *driver.ServerAPIOptions) *Delete {\n\tif d == nil {\n\t\td = new(Delete)\n\t}\n\n\td.serverAPI = serverAPI\n\treturn d\n}\n\n// Let specifies the let document to use. This option is only valid for server versions 5.0 and above.\nfunc (d *Delete) Let(let bsoncore.Document) *Delete {\n\tif d == nil {\n\t\td = new(Delete)\n\t}\n\n\td.let = let\n\treturn d\n}\n\n// Timeout sets the timeout for this operation.\nfunc (d *Delete) Timeout(timeout *time.Duration) *Delete {\n\tif d == nil {\n\t\td = new(Delete)\n\t}\n\n\td.timeout = timeout\n\treturn d\n}\n\n// Logger sets the logger for this operation.\nfunc (d *Delete) Logger(logger *logger.Logger) *Delete {\n\tif d == nil {\n\t\td = new(Delete)\n\t}\n\n\td.logger = logger\n\n\treturn d\n}\n\n// Authenticator sets the authenticator to use for this operation.\nfunc (d *Delete) Authenticator(authenticator driver.Authenticator) *Delete {\n\tif d == nil {\n\t\td = new(Delete)\n\t}\n\n\td.authenticator = authenticator\n\treturn d\n}\n\n// RawData sets the rawData to access timeseries data in the compressed format.\nfunc (d *Delete) RawData(rawData bool) *Delete {\n\tif d == nil {\n\t\td = new(Delete)\n\t}\n\n\td.rawData = &rawData\n\treturn d\n}\n"
  },
  {
    "path": "x/mongo/driver/operation/distinct.go",
    "content": "// Copyright (C) MongoDB, Inc. 2019-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage operation\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/event\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/driverutil\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/readconcern\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/readpref\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/description\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/session\"\n)\n\n// Distinct performs a distinct operation.\ntype Distinct struct {\n\tauthenticator  driver.Authenticator\n\tcollation      bsoncore.Document\n\tkey            *string\n\tquery          bsoncore.Document\n\tsession        *session.Client\n\tclock          *session.ClusterClock\n\tcollection     string\n\tcomment        bsoncore.Value\n\thint           bsoncore.Value\n\tmonitor        *event.CommandMonitor\n\tcrypt          driver.Crypt\n\tdatabase       string\n\tdeployment     driver.Deployment\n\treadConcern    *readconcern.ReadConcern\n\treadPreference *readpref.ReadPref\n\tselector       description.ServerSelector\n\tretry          *driver.RetryMode\n\tresult         DistinctResult\n\tserverAPI      *driver.ServerAPIOptions\n\ttimeout        *time.Duration\n\trawData        *bool\n}\n\n// DistinctResult represents a distinct result returned by the server.\ntype DistinctResult struct {\n\t// The distinct values for the field.\n\tValues bsoncore.Value\n}\n\nfunc buildDistinctResult(response bsoncore.Document) (DistinctResult, error) {\n\telements, err := response.Elements()\n\tif err != nil {\n\t\treturn DistinctResult{}, err\n\t}\n\tdr := DistinctResult{}\n\tfor _, element := range elements {\n\t\tif element.Key() == \"values\" {\n\t\t\tdr.Values = element.Value()\n\t\t}\n\t}\n\treturn dr, nil\n}\n\n// NewDistinct constructs and returns a new Distinct.\nfunc NewDistinct(key string, query bsoncore.Document) *Distinct {\n\treturn &Distinct{\n\t\tkey:   &key,\n\t\tquery: query,\n\t}\n}\n\n// Result returns the result of executing this operation.\nfunc (d *Distinct) Result() DistinctResult { return d.result }\n\nfunc (d *Distinct) processResponse(_ context.Context, resp bsoncore.Document, _ driver.ResponseInfo) error {\n\tvar err error\n\td.result, err = buildDistinctResult(resp)\n\treturn err\n}\n\n// Execute runs this operations and returns an error if the operation did not execute successfully.\nfunc (d *Distinct) Execute(ctx context.Context) error {\n\tif d.deployment == nil {\n\t\treturn errors.New(\"the Distinct operation must have a Deployment set before Execute can be called\")\n\t}\n\n\treturn driver.Operation{\n\t\tCommandFn:         d.command,\n\t\tProcessResponseFn: d.processResponse,\n\t\tRetryMode:         d.retry,\n\t\tType:              driver.Read,\n\t\tClient:            d.session,\n\t\tClock:             d.clock,\n\t\tCommandMonitor:    d.monitor,\n\t\tCrypt:             d.crypt,\n\t\tDatabase:          d.database,\n\t\tDeployment:        d.deployment,\n\t\tReadConcern:       d.readConcern,\n\t\tReadPreference:    d.readPreference,\n\t\tSelector:          d.selector,\n\t\tServerAPI:         d.serverAPI,\n\t\tTimeout:           d.timeout,\n\t\tName:              driverutil.DistinctOp,\n\t\tAuthenticator:     d.authenticator,\n\t}.Execute(ctx)\n}\n\nfunc (d *Distinct) command(dst []byte, desc description.SelectedServer) ([]byte, error) {\n\tdst = bsoncore.AppendStringElement(dst, \"distinct\", d.collection)\n\tif d.collation != nil {\n\t\tif desc.WireVersion == nil || !driverutil.VersionRangeIncludes(*desc.WireVersion, 5) {\n\t\t\treturn nil, errors.New(\"the 'collation' command parameter requires a minimum server wire version of 5\")\n\t\t}\n\t\tdst = bsoncore.AppendDocumentElement(dst, \"collation\", d.collation)\n\t}\n\tif d.comment.Type != bsoncore.Type(0) {\n\t\tdst = bsoncore.AppendValueElement(dst, \"comment\", d.comment)\n\t}\n\tif d.hint.Type != bsoncore.Type(0) {\n\t\tdst = bsoncore.AppendValueElement(dst, \"hint\", d.hint)\n\t}\n\tif d.key != nil {\n\t\tdst = bsoncore.AppendStringElement(dst, \"key\", *d.key)\n\t}\n\tif d.query != nil {\n\t\tdst = bsoncore.AppendDocumentElement(dst, \"query\", d.query)\n\t}\n\t// Set rawData for 8.2+ servers.\n\tif d.rawData != nil && desc.WireVersion != nil && driverutil.VersionRangeIncludes(*desc.WireVersion, 27) {\n\t\tdst = bsoncore.AppendBooleanElement(dst, \"rawData\", *d.rawData)\n\t}\n\treturn dst, nil\n}\n\n// Collation specifies a collation to be used.\nfunc (d *Distinct) Collation(collation bsoncore.Document) *Distinct {\n\tif d == nil {\n\t\td = new(Distinct)\n\t}\n\n\td.collation = collation\n\treturn d\n}\n\n// Key specifies which field to return distinct values for.\nfunc (d *Distinct) Key(key string) *Distinct {\n\tif d == nil {\n\t\td = new(Distinct)\n\t}\n\n\td.key = &key\n\treturn d\n}\n\n// Query specifies which documents to return distinct values from.\nfunc (d *Distinct) Query(query bsoncore.Document) *Distinct {\n\tif d == nil {\n\t\td = new(Distinct)\n\t}\n\n\td.query = query\n\treturn d\n}\n\n// Session sets the session for this operation.\nfunc (d *Distinct) Session(session *session.Client) *Distinct {\n\tif d == nil {\n\t\td = new(Distinct)\n\t}\n\n\td.session = session\n\treturn d\n}\n\n// ClusterClock sets the cluster clock for this operation.\nfunc (d *Distinct) ClusterClock(clock *session.ClusterClock) *Distinct {\n\tif d == nil {\n\t\td = new(Distinct)\n\t}\n\n\td.clock = clock\n\treturn d\n}\n\n// Collection sets the collection that this command will run against.\nfunc (d *Distinct) Collection(collection string) *Distinct {\n\tif d == nil {\n\t\td = new(Distinct)\n\t}\n\n\td.collection = collection\n\treturn d\n}\n\n// Comment sets a value to help trace an operation.\nfunc (d *Distinct) Comment(comment bsoncore.Value) *Distinct {\n\tif d == nil {\n\t\td = new(Distinct)\n\t}\n\n\td.comment = comment\n\treturn d\n}\n\n// Hint sets a value to help trace an operation.\nfunc (d *Distinct) Hint(hint bsoncore.Value) *Distinct {\n\tif d == nil {\n\t\td = new(Distinct)\n\t}\n\n\td.hint = hint\n\treturn d\n}\n\n// CommandMonitor sets the monitor to use for APM events.\nfunc (d *Distinct) CommandMonitor(monitor *event.CommandMonitor) *Distinct {\n\tif d == nil {\n\t\td = new(Distinct)\n\t}\n\n\td.monitor = monitor\n\treturn d\n}\n\n// Crypt sets the Crypt object to use for automatic encryption and decryption.\nfunc (d *Distinct) Crypt(crypt driver.Crypt) *Distinct {\n\tif d == nil {\n\t\td = new(Distinct)\n\t}\n\n\td.crypt = crypt\n\treturn d\n}\n\n// Database sets the database to run this operation against.\nfunc (d *Distinct) Database(database string) *Distinct {\n\tif d == nil {\n\t\td = new(Distinct)\n\t}\n\n\td.database = database\n\treturn d\n}\n\n// Deployment sets the deployment to use for this operation.\nfunc (d *Distinct) Deployment(deployment driver.Deployment) *Distinct {\n\tif d == nil {\n\t\td = new(Distinct)\n\t}\n\n\td.deployment = deployment\n\treturn d\n}\n\n// ReadConcern specifies the read concern for this operation.\nfunc (d *Distinct) ReadConcern(readConcern *readconcern.ReadConcern) *Distinct {\n\tif d == nil {\n\t\td = new(Distinct)\n\t}\n\n\td.readConcern = readConcern\n\treturn d\n}\n\n// ReadPreference set the read preference used with this operation.\nfunc (d *Distinct) ReadPreference(readPreference *readpref.ReadPref) *Distinct {\n\tif d == nil {\n\t\td = new(Distinct)\n\t}\n\n\td.readPreference = readPreference\n\treturn d\n}\n\n// ServerSelector sets the selector used to retrieve a server.\nfunc (d *Distinct) ServerSelector(selector description.ServerSelector) *Distinct {\n\tif d == nil {\n\t\td = new(Distinct)\n\t}\n\n\td.selector = selector\n\treturn d\n}\n\n// Retry enables retryable mode for this operation. Retries are handled automatically in driver.Operation.Execute based\n// on how the operation is set.\nfunc (d *Distinct) Retry(retry driver.RetryMode) *Distinct {\n\tif d == nil {\n\t\td = new(Distinct)\n\t}\n\n\td.retry = &retry\n\treturn d\n}\n\n// ServerAPI sets the server API version for this operation.\nfunc (d *Distinct) ServerAPI(serverAPI *driver.ServerAPIOptions) *Distinct {\n\tif d == nil {\n\t\td = new(Distinct)\n\t}\n\n\td.serverAPI = serverAPI\n\treturn d\n}\n\n// Timeout sets the timeout for this operation.\nfunc (d *Distinct) Timeout(timeout *time.Duration) *Distinct {\n\tif d == nil {\n\t\td = new(Distinct)\n\t}\n\n\td.timeout = timeout\n\treturn d\n}\n\n// Authenticator sets the authenticator to use for this operation.\nfunc (d *Distinct) Authenticator(authenticator driver.Authenticator) *Distinct {\n\tif d == nil {\n\t\td = new(Distinct)\n\t}\n\n\td.authenticator = authenticator\n\treturn d\n}\n\n// RawData sets the rawData to access timeseries data in the compressed format.\nfunc (d *Distinct) RawData(rawData bool) *Distinct {\n\tif d == nil {\n\t\td = new(Distinct)\n\t}\n\n\td.rawData = &rawData\n\treturn d\n}\n"
  },
  {
    "path": "x/mongo/driver/operation/doc.go",
    "content": "// Copyright (C) MongoDB, Inc. 2024-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\n// Package operation is intended for internal use only. It is made available to\n// facilitate use cases that require access to internal MongoDB driver\n// functionality and state. The API of this package is not stable and there is\n// no backward compatibility guarantee.\n//\n// WARNING: THIS PACKAGE IS EXPERIMENTAL AND MAY BE MODIFIED OR REMOVED WITHOUT\n// NOTICE! USE WITH EXTREME CAUTION!\npackage operation\n"
  },
  {
    "path": "x/mongo/driver/operation/drop_collection.go",
    "content": "// Copyright (C) MongoDB, Inc. 2019-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage operation\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/event\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/driverutil\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/writeconcern\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/description\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/session\"\n)\n\n// DropCollection performs a drop operation.\ntype DropCollection struct {\n\tauthenticator driver.Authenticator\n\tsession       *session.Client\n\tclock         *session.ClusterClock\n\tcollection    string\n\tmonitor       *event.CommandMonitor\n\tcrypt         driver.Crypt\n\tdatabase      string\n\tdeployment    driver.Deployment\n\tselector      description.ServerSelector\n\twriteConcern  *writeconcern.WriteConcern\n\tresult        DropCollectionResult\n\tserverAPI     *driver.ServerAPIOptions\n\ttimeout       *time.Duration\n}\n\n// DropCollectionResult represents a dropCollection result returned by the server.\ntype DropCollectionResult struct {\n\t// The number of indexes in the dropped collection.\n\tNIndexesWas int32\n\t// The namespace of the dropped collection.\n\tNs string\n}\n\nfunc buildDropCollectionResult(response bsoncore.Document) (DropCollectionResult, error) {\n\telements, err := response.Elements()\n\tif err != nil {\n\t\treturn DropCollectionResult{}, err\n\t}\n\tdcr := DropCollectionResult{}\n\tfor _, element := range elements {\n\t\tswitch element.Key() {\n\t\tcase \"nIndexesWas\":\n\t\t\tvar ok bool\n\t\t\tdcr.NIndexesWas, ok = element.Value().AsInt32OK()\n\t\t\tif !ok {\n\t\t\t\treturn dcr, fmt.Errorf(\"response field 'nIndexesWas' is type int32, but received BSON type %s\", element.Value().Type)\n\t\t\t}\n\t\tcase \"ns\":\n\t\t\tvar ok bool\n\t\t\tdcr.Ns, ok = element.Value().StringValueOK()\n\t\t\tif !ok {\n\t\t\t\treturn dcr, fmt.Errorf(\"response field 'ns' is type string, but received BSON type %s\", element.Value().Type)\n\t\t\t}\n\t\t}\n\t}\n\treturn dcr, nil\n}\n\n// NewDropCollection constructs and returns a new DropCollection.\nfunc NewDropCollection() *DropCollection {\n\treturn &DropCollection{}\n}\n\n// Result returns the result of executing this operation.\nfunc (dc *DropCollection) Result() DropCollectionResult { return dc.result }\n\nfunc (dc *DropCollection) processResponse(_ context.Context, resp bsoncore.Document, _ driver.ResponseInfo) error {\n\tvar err error\n\tdc.result, err = buildDropCollectionResult(resp)\n\treturn err\n}\n\n// Execute runs this operations and returns an error if the operation did not execute successfully.\nfunc (dc *DropCollection) Execute(ctx context.Context) error {\n\tif dc.deployment == nil {\n\t\treturn errors.New(\"the DropCollection operation must have a Deployment set before Execute can be called\")\n\t}\n\n\treturn driver.Operation{\n\t\tCommandFn:         dc.command,\n\t\tProcessResponseFn: dc.processResponse,\n\t\tClient:            dc.session,\n\t\tClock:             dc.clock,\n\t\tCommandMonitor:    dc.monitor,\n\t\tCrypt:             dc.crypt,\n\t\tDatabase:          dc.database,\n\t\tDeployment:        dc.deployment,\n\t\tSelector:          dc.selector,\n\t\tWriteConcern:      dc.writeConcern,\n\t\tServerAPI:         dc.serverAPI,\n\t\tTimeout:           dc.timeout,\n\t\tName:              driverutil.DropOp,\n\t\tAuthenticator:     dc.authenticator,\n\t}.Execute(ctx)\n}\n\nfunc (dc *DropCollection) command(dst []byte, _ description.SelectedServer) ([]byte, error) {\n\tdst = bsoncore.AppendStringElement(dst, \"drop\", dc.collection)\n\treturn dst, nil\n}\n\n// Session sets the session for this operation.\nfunc (dc *DropCollection) Session(session *session.Client) *DropCollection {\n\tif dc == nil {\n\t\tdc = new(DropCollection)\n\t}\n\n\tdc.session = session\n\treturn dc\n}\n\n// ClusterClock sets the cluster clock for this operation.\nfunc (dc *DropCollection) ClusterClock(clock *session.ClusterClock) *DropCollection {\n\tif dc == nil {\n\t\tdc = new(DropCollection)\n\t}\n\n\tdc.clock = clock\n\treturn dc\n}\n\n// Collection sets the collection that this command will run against.\nfunc (dc *DropCollection) Collection(collection string) *DropCollection {\n\tif dc == nil {\n\t\tdc = new(DropCollection)\n\t}\n\n\tdc.collection = collection\n\treturn dc\n}\n\n// CommandMonitor sets the monitor to use for APM events.\nfunc (dc *DropCollection) CommandMonitor(monitor *event.CommandMonitor) *DropCollection {\n\tif dc == nil {\n\t\tdc = new(DropCollection)\n\t}\n\n\tdc.monitor = monitor\n\treturn dc\n}\n\n// Crypt sets the Crypt object to use for automatic encryption and decryption.\nfunc (dc *DropCollection) Crypt(crypt driver.Crypt) *DropCollection {\n\tif dc == nil {\n\t\tdc = new(DropCollection)\n\t}\n\n\tdc.crypt = crypt\n\treturn dc\n}\n\n// Database sets the database to run this operation against.\nfunc (dc *DropCollection) Database(database string) *DropCollection {\n\tif dc == nil {\n\t\tdc = new(DropCollection)\n\t}\n\n\tdc.database = database\n\treturn dc\n}\n\n// Deployment sets the deployment to use for this operation.\nfunc (dc *DropCollection) Deployment(deployment driver.Deployment) *DropCollection {\n\tif dc == nil {\n\t\tdc = new(DropCollection)\n\t}\n\n\tdc.deployment = deployment\n\treturn dc\n}\n\n// ServerSelector sets the selector used to retrieve a server.\nfunc (dc *DropCollection) ServerSelector(selector description.ServerSelector) *DropCollection {\n\tif dc == nil {\n\t\tdc = new(DropCollection)\n\t}\n\n\tdc.selector = selector\n\treturn dc\n}\n\n// WriteConcern sets the write concern for this operation.\nfunc (dc *DropCollection) WriteConcern(writeConcern *writeconcern.WriteConcern) *DropCollection {\n\tif dc == nil {\n\t\tdc = new(DropCollection)\n\t}\n\n\tdc.writeConcern = writeConcern\n\treturn dc\n}\n\n// ServerAPI sets the server API version for this operation.\nfunc (dc *DropCollection) ServerAPI(serverAPI *driver.ServerAPIOptions) *DropCollection {\n\tif dc == nil {\n\t\tdc = new(DropCollection)\n\t}\n\n\tdc.serverAPI = serverAPI\n\treturn dc\n}\n\n// Timeout sets the timeout for this operation.\nfunc (dc *DropCollection) Timeout(timeout *time.Duration) *DropCollection {\n\tif dc == nil {\n\t\tdc = new(DropCollection)\n\t}\n\n\tdc.timeout = timeout\n\treturn dc\n}\n\n// Authenticator sets the authenticator to use for this operation.\nfunc (dc *DropCollection) Authenticator(authenticator driver.Authenticator) *DropCollection {\n\tif dc == nil {\n\t\tdc = new(DropCollection)\n\t}\n\n\tdc.authenticator = authenticator\n\treturn dc\n}\n"
  },
  {
    "path": "x/mongo/driver/operation/drop_database.go",
    "content": "// Copyright (C) MongoDB, Inc. 2019-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage operation\n\nimport (\n\t\"context\"\n\t\"errors\"\n\n\t\"go.mongodb.org/mongo-driver/v2/event\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/driverutil\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/writeconcern\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/description\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/session\"\n)\n\n// DropDatabase performs a dropDatabase operation\ntype DropDatabase struct {\n\tauthenticator driver.Authenticator\n\tsession       *session.Client\n\tclock         *session.ClusterClock\n\tmonitor       *event.CommandMonitor\n\tcrypt         driver.Crypt\n\tdatabase      string\n\tdeployment    driver.Deployment\n\tselector      description.ServerSelector\n\twriteConcern  *writeconcern.WriteConcern\n\tserverAPI     *driver.ServerAPIOptions\n}\n\n// NewDropDatabase constructs and returns a new DropDatabase.\nfunc NewDropDatabase() *DropDatabase {\n\treturn &DropDatabase{}\n}\n\n// Execute runs this operations and returns an error if the operation did not execute successfully.\nfunc (dd *DropDatabase) Execute(ctx context.Context) error {\n\tif dd.deployment == nil {\n\t\treturn errors.New(\"the DropDatabase operation must have a Deployment set before Execute can be called\")\n\t}\n\n\treturn driver.Operation{\n\t\tCommandFn:      dd.command,\n\t\tClient:         dd.session,\n\t\tClock:          dd.clock,\n\t\tCommandMonitor: dd.monitor,\n\t\tCrypt:          dd.crypt,\n\t\tDatabase:       dd.database,\n\t\tDeployment:     dd.deployment,\n\t\tSelector:       dd.selector,\n\t\tWriteConcern:   dd.writeConcern,\n\t\tServerAPI:      dd.serverAPI,\n\t\tName:           driverutil.DropDatabaseOp,\n\t\tAuthenticator:  dd.authenticator,\n\t}.Execute(ctx)\n}\n\nfunc (dd *DropDatabase) command(dst []byte, _ description.SelectedServer) ([]byte, error) {\n\tdst = bsoncore.AppendInt32Element(dst, \"dropDatabase\", 1)\n\treturn dst, nil\n}\n\n// Session sets the session for this operation.\nfunc (dd *DropDatabase) Session(session *session.Client) *DropDatabase {\n\tif dd == nil {\n\t\tdd = new(DropDatabase)\n\t}\n\n\tdd.session = session\n\treturn dd\n}\n\n// ClusterClock sets the cluster clock for this operation.\nfunc (dd *DropDatabase) ClusterClock(clock *session.ClusterClock) *DropDatabase {\n\tif dd == nil {\n\t\tdd = new(DropDatabase)\n\t}\n\n\tdd.clock = clock\n\treturn dd\n}\n\n// CommandMonitor sets the monitor to use for APM events.\nfunc (dd *DropDatabase) CommandMonitor(monitor *event.CommandMonitor) *DropDatabase {\n\tif dd == nil {\n\t\tdd = new(DropDatabase)\n\t}\n\n\tdd.monitor = monitor\n\treturn dd\n}\n\n// Crypt sets the Crypt object to use for automatic encryption and decryption.\nfunc (dd *DropDatabase) Crypt(crypt driver.Crypt) *DropDatabase {\n\tif dd == nil {\n\t\tdd = new(DropDatabase)\n\t}\n\n\tdd.crypt = crypt\n\treturn dd\n}\n\n// Database sets the database to run this operation against.\nfunc (dd *DropDatabase) Database(database string) *DropDatabase {\n\tif dd == nil {\n\t\tdd = new(DropDatabase)\n\t}\n\n\tdd.database = database\n\treturn dd\n}\n\n// Deployment sets the deployment to use for this operation.\nfunc (dd *DropDatabase) Deployment(deployment driver.Deployment) *DropDatabase {\n\tif dd == nil {\n\t\tdd = new(DropDatabase)\n\t}\n\n\tdd.deployment = deployment\n\treturn dd\n}\n\n// ServerSelector sets the selector used to retrieve a server.\nfunc (dd *DropDatabase) ServerSelector(selector description.ServerSelector) *DropDatabase {\n\tif dd == nil {\n\t\tdd = new(DropDatabase)\n\t}\n\n\tdd.selector = selector\n\treturn dd\n}\n\n// WriteConcern sets the write concern for this operation.\nfunc (dd *DropDatabase) WriteConcern(writeConcern *writeconcern.WriteConcern) *DropDatabase {\n\tif dd == nil {\n\t\tdd = new(DropDatabase)\n\t}\n\n\tdd.writeConcern = writeConcern\n\treturn dd\n}\n\n// ServerAPI sets the server API version for this operation.\nfunc (dd *DropDatabase) ServerAPI(serverAPI *driver.ServerAPIOptions) *DropDatabase {\n\tif dd == nil {\n\t\tdd = new(DropDatabase)\n\t}\n\n\tdd.serverAPI = serverAPI\n\treturn dd\n}\n\n// Authenticator sets the authenticator to use for this operation.\nfunc (dd *DropDatabase) Authenticator(authenticator driver.Authenticator) *DropDatabase {\n\tif dd == nil {\n\t\tdd = new(DropDatabase)\n\t}\n\n\tdd.authenticator = authenticator\n\treturn dd\n}\n"
  },
  {
    "path": "x/mongo/driver/operation/drop_indexes.go",
    "content": "// Copyright (C) MongoDB, Inc. 2019-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage operation\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/event\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/driverutil\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/writeconcern\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/description\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/session\"\n)\n\n// DropIndexes performs an dropIndexes operation.\ntype DropIndexes struct {\n\tauthenticator driver.Authenticator\n\tindex         any\n\tsession       *session.Client\n\tclock         *session.ClusterClock\n\tcollection    string\n\tmonitor       *event.CommandMonitor\n\tcrypt         driver.Crypt\n\tdatabase      string\n\tdeployment    driver.Deployment\n\tselector      description.ServerSelector\n\twriteConcern  *writeconcern.WriteConcern\n\tresult        DropIndexesResult\n\tserverAPI     *driver.ServerAPIOptions\n\ttimeout       *time.Duration\n\trawData       *bool\n}\n\n// DropIndexesResult represents a dropIndexes result returned by the server.\ntype DropIndexesResult struct {\n\t// Number of indexes that existed before the drop was executed.\n\tNIndexesWas int32\n}\n\nfunc buildDropIndexesResult(response bsoncore.Document) (DropIndexesResult, error) {\n\telements, err := response.Elements()\n\tif err != nil {\n\t\treturn DropIndexesResult{}, err\n\t}\n\tdir := DropIndexesResult{}\n\tfor _, element := range elements {\n\t\tif element.Key() == \"nIndexesWas\" {\n\t\t\tvar ok bool\n\t\t\tdir.NIndexesWas, ok = element.Value().AsInt32OK()\n\t\t\tif !ok {\n\t\t\t\treturn dir, fmt.Errorf(\"response field 'nIndexesWas' is type int32, but received BSON type %s\", element.Value().Type)\n\t\t\t}\n\t\t}\n\t}\n\treturn dir, nil\n}\n\n// NewDropIndexes constructs and returns a new DropIndexes.\nfunc NewDropIndexes(index any) *DropIndexes {\n\treturn &DropIndexes{\n\t\tindex: index,\n\t}\n}\n\n// Result returns the result of executing this operation.\nfunc (di *DropIndexes) Result() DropIndexesResult { return di.result }\n\nfunc (di *DropIndexes) processResponse(_ context.Context, resp bsoncore.Document, _ driver.ResponseInfo) error {\n\tvar err error\n\tdi.result, err = buildDropIndexesResult(resp)\n\treturn err\n}\n\n// Execute runs this operations and returns an error if the operation did not execute successfully.\nfunc (di *DropIndexes) Execute(ctx context.Context) error {\n\tif di.deployment == nil {\n\t\treturn errors.New(\"the DropIndexes operation must have a Deployment set before Execute can be called\")\n\t}\n\n\treturn driver.Operation{\n\t\tCommandFn:         di.command,\n\t\tProcessResponseFn: di.processResponse,\n\t\tClient:            di.session,\n\t\tClock:             di.clock,\n\t\tCommandMonitor:    di.monitor,\n\t\tCrypt:             di.crypt,\n\t\tDatabase:          di.database,\n\t\tDeployment:        di.deployment,\n\t\tSelector:          di.selector,\n\t\tWriteConcern:      di.writeConcern,\n\t\tServerAPI:         di.serverAPI,\n\t\tTimeout:           di.timeout,\n\t\tName:              driverutil.DropIndexesOp,\n\t\tAuthenticator:     di.authenticator,\n\t}.Execute(ctx)\n}\n\nfunc (di *DropIndexes) command(dst []byte, desc description.SelectedServer) ([]byte, error) {\n\tdst = bsoncore.AppendStringElement(dst, \"dropIndexes\", di.collection)\n\n\tswitch t := di.index.(type) {\n\tcase string:\n\t\tdst = bsoncore.AppendStringElement(dst, \"index\", t)\n\tcase bsoncore.Document:\n\t\tif di.index != nil {\n\t\t\tdst = bsoncore.AppendDocumentElement(dst, \"index\", t)\n\t\t}\n\t}\n\t// Set rawData for 8.2+ servers.\n\tif di.rawData != nil && desc.WireVersion != nil && driverutil.VersionRangeIncludes(*desc.WireVersion, 27) {\n\t\tdst = bsoncore.AppendBooleanElement(dst, \"rawData\", *di.rawData)\n\t}\n\n\treturn dst, nil\n}\n\n// Index specifies the name of the index to drop. If '*' is specified, all indexes will be dropped.\nfunc (di *DropIndexes) Index(index any) *DropIndexes {\n\tif di == nil {\n\t\tdi = new(DropIndexes)\n\t}\n\n\tdi.index = index\n\treturn di\n}\n\n// Session sets the session for this operation.\nfunc (di *DropIndexes) Session(session *session.Client) *DropIndexes {\n\tif di == nil {\n\t\tdi = new(DropIndexes)\n\t}\n\n\tdi.session = session\n\treturn di\n}\n\n// ClusterClock sets the cluster clock for this operation.\nfunc (di *DropIndexes) ClusterClock(clock *session.ClusterClock) *DropIndexes {\n\tif di == nil {\n\t\tdi = new(DropIndexes)\n\t}\n\n\tdi.clock = clock\n\treturn di\n}\n\n// Collection sets the collection that this command will run against.\nfunc (di *DropIndexes) Collection(collection string) *DropIndexes {\n\tif di == nil {\n\t\tdi = new(DropIndexes)\n\t}\n\n\tdi.collection = collection\n\treturn di\n}\n\n// CommandMonitor sets the monitor to use for APM events.\nfunc (di *DropIndexes) CommandMonitor(monitor *event.CommandMonitor) *DropIndexes {\n\tif di == nil {\n\t\tdi = new(DropIndexes)\n\t}\n\n\tdi.monitor = monitor\n\treturn di\n}\n\n// Crypt sets the Crypt object to use for automatic encryption and decryption.\nfunc (di *DropIndexes) Crypt(crypt driver.Crypt) *DropIndexes {\n\tif di == nil {\n\t\tdi = new(DropIndexes)\n\t}\n\n\tdi.crypt = crypt\n\treturn di\n}\n\n// Database sets the database to run this operation against.\nfunc (di *DropIndexes) Database(database string) *DropIndexes {\n\tif di == nil {\n\t\tdi = new(DropIndexes)\n\t}\n\n\tdi.database = database\n\treturn di\n}\n\n// Deployment sets the deployment to use for this operation.\nfunc (di *DropIndexes) Deployment(deployment driver.Deployment) *DropIndexes {\n\tif di == nil {\n\t\tdi = new(DropIndexes)\n\t}\n\n\tdi.deployment = deployment\n\treturn di\n}\n\n// ServerSelector sets the selector used to retrieve a server.\nfunc (di *DropIndexes) ServerSelector(selector description.ServerSelector) *DropIndexes {\n\tif di == nil {\n\t\tdi = new(DropIndexes)\n\t}\n\n\tdi.selector = selector\n\treturn di\n}\n\n// WriteConcern sets the write concern for this operation.\nfunc (di *DropIndexes) WriteConcern(writeConcern *writeconcern.WriteConcern) *DropIndexes {\n\tif di == nil {\n\t\tdi = new(DropIndexes)\n\t}\n\n\tdi.writeConcern = writeConcern\n\treturn di\n}\n\n// ServerAPI sets the server API version for this operation.\nfunc (di *DropIndexes) ServerAPI(serverAPI *driver.ServerAPIOptions) *DropIndexes {\n\tif di == nil {\n\t\tdi = new(DropIndexes)\n\t}\n\n\tdi.serverAPI = serverAPI\n\treturn di\n}\n\n// Timeout sets the timeout for this operation.\nfunc (di *DropIndexes) Timeout(timeout *time.Duration) *DropIndexes {\n\tif di == nil {\n\t\tdi = new(DropIndexes)\n\t}\n\n\tdi.timeout = timeout\n\treturn di\n}\n\n// Authenticator sets the authenticator to use for this operation.\nfunc (di *DropIndexes) Authenticator(authenticator driver.Authenticator) *DropIndexes {\n\tif di == nil {\n\t\tdi = new(DropIndexes)\n\t}\n\n\tdi.authenticator = authenticator\n\treturn di\n}\n\n// RawData sets the rawData to access timeseries data in the compressed format.\nfunc (di *DropIndexes) RawData(rawData bool) *DropIndexes {\n\tif di == nil {\n\t\tdi = new(DropIndexes)\n\t}\n\n\tdi.rawData = &rawData\n\treturn di\n}\n"
  },
  {
    "path": "x/mongo/driver/operation/drop_search_index.go",
    "content": "// Copyright (C) MongoDB, Inc. 2023-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage operation\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/event\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/description\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/session\"\n)\n\n// DropSearchIndex performs an dropSearchIndex operation.\ntype DropSearchIndex struct {\n\tauthenticator driver.Authenticator\n\tindex         string\n\tsession       *session.Client\n\tclock         *session.ClusterClock\n\tcollection    string\n\tmonitor       *event.CommandMonitor\n\tcrypt         driver.Crypt\n\tdatabase      string\n\tdeployment    driver.Deployment\n\tselector      description.ServerSelector\n\tresult        DropSearchIndexResult\n\tserverAPI     *driver.ServerAPIOptions\n\ttimeout       *time.Duration\n}\n\n// DropSearchIndexResult represents a dropSearchIndex result returned by the server.\ntype DropSearchIndexResult struct {\n\tOk int32\n}\n\nfunc buildDropSearchIndexResult(response bsoncore.Document) (DropSearchIndexResult, error) {\n\telements, err := response.Elements()\n\tif err != nil {\n\t\treturn DropSearchIndexResult{}, err\n\t}\n\tdsir := DropSearchIndexResult{}\n\tfor _, element := range elements {\n\t\tif element.Key() == \"ok\" {\n\t\t\tvar ok bool\n\t\t\tdsir.Ok, ok = element.Value().AsInt32OK()\n\t\t\tif !ok {\n\t\t\t\treturn dsir, fmt.Errorf(\"response field 'ok' is type int32, but received BSON type %s\", element.Value().Type)\n\t\t\t}\n\t\t}\n\t}\n\treturn dsir, nil\n}\n\n// NewDropSearchIndex constructs and returns a new DropSearchIndex.\nfunc NewDropSearchIndex(index string) *DropSearchIndex {\n\treturn &DropSearchIndex{\n\t\tindex: index,\n\t}\n}\n\n// Result returns the result of executing this operation.\nfunc (dsi *DropSearchIndex) Result() DropSearchIndexResult { return dsi.result }\n\nfunc (dsi *DropSearchIndex) processResponse(_ context.Context, resp bsoncore.Document, _ driver.ResponseInfo) error {\n\tvar err error\n\tdsi.result, err = buildDropSearchIndexResult(resp)\n\treturn err\n}\n\n// Execute runs this operations and returns an error if the operation did not execute successfully.\nfunc (dsi *DropSearchIndex) Execute(ctx context.Context) error {\n\tif dsi.deployment == nil {\n\t\treturn errors.New(\"the DropSearchIndex operation must have a Deployment set before Execute can be called\")\n\t}\n\n\treturn driver.Operation{\n\t\tCommandFn:         dsi.command,\n\t\tProcessResponseFn: dsi.processResponse,\n\t\tClient:            dsi.session,\n\t\tClock:             dsi.clock,\n\t\tCommandMonitor:    dsi.monitor,\n\t\tCrypt:             dsi.crypt,\n\t\tDatabase:          dsi.database,\n\t\tDeployment:        dsi.deployment,\n\t\tSelector:          dsi.selector,\n\t\tServerAPI:         dsi.serverAPI,\n\t\tTimeout:           dsi.timeout,\n\t\tAuthenticator:     dsi.authenticator,\n\t}.Execute(ctx)\n}\n\nfunc (dsi *DropSearchIndex) command(dst []byte, _ description.SelectedServer) ([]byte, error) {\n\tdst = bsoncore.AppendStringElement(dst, \"dropSearchIndex\", dsi.collection)\n\tdst = bsoncore.AppendStringElement(dst, \"name\", dsi.index)\n\treturn dst, nil\n}\n\n// Index specifies the name of the index to drop. If '*' is specified, all indexes will be dropped.\nfunc (dsi *DropSearchIndex) Index(index string) *DropSearchIndex {\n\tif dsi == nil {\n\t\tdsi = new(DropSearchIndex)\n\t}\n\n\tdsi.index = index\n\treturn dsi\n}\n\n// Session sets the session for this operation.\nfunc (dsi *DropSearchIndex) Session(session *session.Client) *DropSearchIndex {\n\tif dsi == nil {\n\t\tdsi = new(DropSearchIndex)\n\t}\n\n\tdsi.session = session\n\treturn dsi\n}\n\n// ClusterClock sets the cluster clock for this operation.\nfunc (dsi *DropSearchIndex) ClusterClock(clock *session.ClusterClock) *DropSearchIndex {\n\tif dsi == nil {\n\t\tdsi = new(DropSearchIndex)\n\t}\n\n\tdsi.clock = clock\n\treturn dsi\n}\n\n// Collection sets the collection that this command will run against.\nfunc (dsi *DropSearchIndex) Collection(collection string) *DropSearchIndex {\n\tif dsi == nil {\n\t\tdsi = new(DropSearchIndex)\n\t}\n\n\tdsi.collection = collection\n\treturn dsi\n}\n\n// CommandMonitor sets the monitor to use for APM events.\nfunc (dsi *DropSearchIndex) CommandMonitor(monitor *event.CommandMonitor) *DropSearchIndex {\n\tif dsi == nil {\n\t\tdsi = new(DropSearchIndex)\n\t}\n\n\tdsi.monitor = monitor\n\treturn dsi\n}\n\n// Crypt sets the Crypt object to use for automatic encryption and decryption.\nfunc (dsi *DropSearchIndex) Crypt(crypt driver.Crypt) *DropSearchIndex {\n\tif dsi == nil {\n\t\tdsi = new(DropSearchIndex)\n\t}\n\n\tdsi.crypt = crypt\n\treturn dsi\n}\n\n// Database sets the database to run this operation against.\nfunc (dsi *DropSearchIndex) Database(database string) *DropSearchIndex {\n\tif dsi == nil {\n\t\tdsi = new(DropSearchIndex)\n\t}\n\n\tdsi.database = database\n\treturn dsi\n}\n\n// Deployment sets the deployment to use for this operation.\nfunc (dsi *DropSearchIndex) Deployment(deployment driver.Deployment) *DropSearchIndex {\n\tif dsi == nil {\n\t\tdsi = new(DropSearchIndex)\n\t}\n\n\tdsi.deployment = deployment\n\treturn dsi\n}\n\n// ServerSelector sets the selector used to retrieve a server.\nfunc (dsi *DropSearchIndex) ServerSelector(selector description.ServerSelector) *DropSearchIndex {\n\tif dsi == nil {\n\t\tdsi = new(DropSearchIndex)\n\t}\n\n\tdsi.selector = selector\n\treturn dsi\n}\n\n// ServerAPI sets the server API version for this operation.\nfunc (dsi *DropSearchIndex) ServerAPI(serverAPI *driver.ServerAPIOptions) *DropSearchIndex {\n\tif dsi == nil {\n\t\tdsi = new(DropSearchIndex)\n\t}\n\n\tdsi.serverAPI = serverAPI\n\treturn dsi\n}\n\n// Timeout sets the timeout for this operation.\nfunc (dsi *DropSearchIndex) Timeout(timeout *time.Duration) *DropSearchIndex {\n\tif dsi == nil {\n\t\tdsi = new(DropSearchIndex)\n\t}\n\n\tdsi.timeout = timeout\n\treturn dsi\n}\n\n// Authenticator sets the authenticator to use for this operation.\nfunc (dsi *DropSearchIndex) Authenticator(authenticator driver.Authenticator) *DropSearchIndex {\n\tif dsi == nil {\n\t\tdsi = new(DropSearchIndex)\n\t}\n\n\tdsi.authenticator = authenticator\n\treturn dsi\n}\n"
  },
  {
    "path": "x/mongo/driver/operation/end_sessions.go",
    "content": "// Copyright (C) MongoDB, Inc. 2019-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage operation\n\nimport (\n\t\"context\"\n\t\"errors\"\n\n\t\"go.mongodb.org/mongo-driver/v2/event\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/driverutil\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/description\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/session\"\n)\n\n// EndSessions performs an endSessions operation.\ntype EndSessions struct {\n\tauthenticator driver.Authenticator\n\tsessionIDs    bsoncore.Document\n\tsession       *session.Client\n\tclock         *session.ClusterClock\n\tmonitor       *event.CommandMonitor\n\tcrypt         driver.Crypt\n\tdatabase      string\n\tdeployment    driver.Deployment\n\tselector      description.ServerSelector\n\tserverAPI     *driver.ServerAPIOptions\n}\n\n// NewEndSessions constructs and returns a new EndSessions.\nfunc NewEndSessions(sessionIDs bsoncore.Document) *EndSessions {\n\treturn &EndSessions{\n\t\tsessionIDs: sessionIDs,\n\t}\n}\n\nfunc (es *EndSessions) processResponse(context.Context, bsoncore.Document, driver.ResponseInfo) error {\n\treturn nil\n}\n\n// Execute runs this operations and returns an error if the operation did not execute successfully.\nfunc (es *EndSessions) Execute(ctx context.Context) error {\n\tif es.deployment == nil {\n\t\treturn errors.New(\"the EndSessions operation must have a Deployment set before Execute can be called\")\n\t}\n\n\treturn driver.Operation{\n\t\tCommandFn:         es.command,\n\t\tProcessResponseFn: es.processResponse,\n\t\tClient:            es.session,\n\t\tClock:             es.clock,\n\t\tCommandMonitor:    es.monitor,\n\t\tCrypt:             es.crypt,\n\t\tDatabase:          es.database,\n\t\tDeployment:        es.deployment,\n\t\tSelector:          es.selector,\n\t\tServerAPI:         es.serverAPI,\n\t\tName:              driverutil.EndSessionsOp,\n\t\tAuthenticator:     es.authenticator,\n\t}.Execute(ctx)\n}\n\nfunc (es *EndSessions) command(dst []byte, _ description.SelectedServer) ([]byte, error) {\n\tif es.sessionIDs != nil {\n\t\tdst = bsoncore.AppendArrayElement(dst, \"endSessions\", es.sessionIDs)\n\t}\n\treturn dst, nil\n}\n\n// SessionIDs specifies the sessions to be expired.\nfunc (es *EndSessions) SessionIDs(sessionIDs bsoncore.Document) *EndSessions {\n\tif es == nil {\n\t\tes = new(EndSessions)\n\t}\n\n\tes.sessionIDs = sessionIDs\n\treturn es\n}\n\n// Session sets the session for this operation.\nfunc (es *EndSessions) Session(session *session.Client) *EndSessions {\n\tif es == nil {\n\t\tes = new(EndSessions)\n\t}\n\n\tes.session = session\n\treturn es\n}\n\n// ClusterClock sets the cluster clock for this operation.\nfunc (es *EndSessions) ClusterClock(clock *session.ClusterClock) *EndSessions {\n\tif es == nil {\n\t\tes = new(EndSessions)\n\t}\n\n\tes.clock = clock\n\treturn es\n}\n\n// CommandMonitor sets the monitor to use for APM events.\nfunc (es *EndSessions) CommandMonitor(monitor *event.CommandMonitor) *EndSessions {\n\tif es == nil {\n\t\tes = new(EndSessions)\n\t}\n\n\tes.monitor = monitor\n\treturn es\n}\n\n// Crypt sets the Crypt object to use for automatic encryption and decryption.\nfunc (es *EndSessions) Crypt(crypt driver.Crypt) *EndSessions {\n\tif es == nil {\n\t\tes = new(EndSessions)\n\t}\n\n\tes.crypt = crypt\n\treturn es\n}\n\n// Database sets the database to run this operation against.\nfunc (es *EndSessions) Database(database string) *EndSessions {\n\tif es == nil {\n\t\tes = new(EndSessions)\n\t}\n\n\tes.database = database\n\treturn es\n}\n\n// Deployment sets the deployment to use for this operation.\nfunc (es *EndSessions) Deployment(deployment driver.Deployment) *EndSessions {\n\tif es == nil {\n\t\tes = new(EndSessions)\n\t}\n\n\tes.deployment = deployment\n\treturn es\n}\n\n// ServerSelector sets the selector used to retrieve a server.\nfunc (es *EndSessions) ServerSelector(selector description.ServerSelector) *EndSessions {\n\tif es == nil {\n\t\tes = new(EndSessions)\n\t}\n\n\tes.selector = selector\n\treturn es\n}\n\n// ServerAPI sets the server API version for this operation.\nfunc (es *EndSessions) ServerAPI(serverAPI *driver.ServerAPIOptions) *EndSessions {\n\tif es == nil {\n\t\tes = new(EndSessions)\n\t}\n\n\tes.serverAPI = serverAPI\n\treturn es\n}\n\n// Authenticator sets the authenticator to use for this operation.\nfunc (es *EndSessions) Authenticator(authenticator driver.Authenticator) *EndSessions {\n\tif es == nil {\n\t\tes = new(EndSessions)\n\t}\n\n\tes.authenticator = authenticator\n\treturn es\n}\n"
  },
  {
    "path": "x/mongo/driver/operation/errors.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage operation\n\nimport \"errors\"\n\nvar errUnacknowledgedHint = errors.New(\"the 'hint' command parameter cannot be used with unacknowledged writes\")\n"
  },
  {
    "path": "x/mongo/driver/operation/find.go",
    "content": "// Copyright (C) MongoDB, Inc. 2019-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage operation\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/event\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/driverutil\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/logger\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/readconcern\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/readpref\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/description\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/session\"\n)\n\n// Find performs a find operation.\ntype Find struct {\n\tauthenticator       driver.Authenticator\n\tallowDiskUse        *bool\n\tallowPartialResults *bool\n\tawaitData           *bool\n\tbatchSize           *int32\n\tcollation           bsoncore.Document\n\tcomment             bsoncore.Value\n\tfilter              bsoncore.Document\n\thint                bsoncore.Value\n\tlet                 bsoncore.Document\n\tlimit               *int64\n\tmax                 bsoncore.Document\n\tmin                 bsoncore.Document\n\tnoCursorTimeout     *bool\n\toplogReplay         *bool\n\tprojection          bsoncore.Document\n\treturnKey           *bool\n\tshowRecordID        *bool\n\tsingleBatch         *bool\n\tskip                *int64\n\tsnapshot            *bool\n\tsort                bsoncore.Document\n\ttailable            *bool\n\tsession             *session.Client\n\tclock               *session.ClusterClock\n\tcollection          string\n\tmonitor             *event.CommandMonitor\n\tcrypt               driver.Crypt\n\tdatabase            string\n\tdeployment          driver.Deployment\n\treadConcern         *readconcern.ReadConcern\n\treadPreference      *readpref.ReadPref\n\tselector            description.ServerSelector\n\tretry               *driver.RetryMode\n\tresult              driver.CursorResponse\n\tserverAPI           *driver.ServerAPIOptions\n\ttimeout             *time.Duration\n\trawData             *bool\n\tlogger              *logger.Logger\n\tomitMaxTimeMS       bool\n}\n\n// NewFind constructs and returns a new Find.\nfunc NewFind(filter bsoncore.Document) *Find {\n\treturn &Find{\n\t\tfilter: filter,\n\t}\n}\n\n// Result returns the result of executing this operation.\nfunc (f *Find) Result(opts driver.CursorOptions) (*driver.BatchCursor, error) {\n\topts.ServerAPI = f.serverAPI\n\treturn driver.NewBatchCursor(f.result, f.session, f.clock, opts)\n}\n\nfunc (f *Find) processResponse(_ context.Context, resp bsoncore.Document, info driver.ResponseInfo) error {\n\tcurDoc, err := driver.ExtractCursorDocument(resp)\n\tif err != nil {\n\t\treturn err\n\t}\n\tf.result, err = driver.NewCursorResponse(curDoc, info)\n\treturn err\n}\n\n// Execute runs this operations and returns an error if the operation did not execute successfully.\nfunc (f *Find) Execute(ctx context.Context) error {\n\tif f.deployment == nil {\n\t\treturn errors.New(\"the Find operation must have a Deployment set before Execute can be called\")\n\t}\n\n\treturn driver.Operation{\n\t\tCommandFn:         f.command,\n\t\tProcessResponseFn: f.processResponse,\n\t\tRetryMode:         f.retry,\n\t\tType:              driver.Read,\n\t\tClient:            f.session,\n\t\tClock:             f.clock,\n\t\tCommandMonitor:    f.monitor,\n\t\tCrypt:             f.crypt,\n\t\tDatabase:          f.database,\n\t\tDeployment:        f.deployment,\n\t\tReadConcern:       f.readConcern,\n\t\tReadPreference:    f.readPreference,\n\t\tSelector:          f.selector,\n\t\tLegacy:            driver.LegacyFind,\n\t\tServerAPI:         f.serverAPI,\n\t\tTimeout:           f.timeout,\n\t\tLogger:            f.logger,\n\t\tName:              driverutil.FindOp,\n\t\tAuthenticator:     f.authenticator,\n\t\tOmitMaxTimeMS:     f.omitMaxTimeMS,\n\t}.Execute(ctx)\n}\n\nfunc (f *Find) command(dst []byte, desc description.SelectedServer) ([]byte, error) {\n\tdst = bsoncore.AppendStringElement(dst, \"find\", f.collection)\n\tif f.allowDiskUse != nil {\n\t\tif desc.WireVersion == nil || !driverutil.VersionRangeIncludes(*desc.WireVersion, 4) {\n\t\t\treturn nil, errors.New(\"the 'allowDiskUse' command parameter requires a minimum server wire version of 4\")\n\t\t}\n\t\tdst = bsoncore.AppendBooleanElement(dst, \"allowDiskUse\", *f.allowDiskUse)\n\t}\n\tif f.allowPartialResults != nil {\n\t\tdst = bsoncore.AppendBooleanElement(dst, \"allowPartialResults\", *f.allowPartialResults)\n\t}\n\tif f.awaitData != nil {\n\t\tdst = bsoncore.AppendBooleanElement(dst, \"awaitData\", *f.awaitData)\n\t}\n\tif f.batchSize != nil {\n\t\tdst = bsoncore.AppendInt32Element(dst, \"batchSize\", *f.batchSize)\n\t}\n\tif f.collation != nil {\n\t\tif desc.WireVersion == nil || !driverutil.VersionRangeIncludes(*desc.WireVersion, 5) {\n\t\t\treturn nil, errors.New(\"the 'collation' command parameter requires a minimum server wire version of 5\")\n\t\t}\n\t\tdst = bsoncore.AppendDocumentElement(dst, \"collation\", f.collation)\n\t}\n\tif f.comment.Type != bsoncore.Type(0) {\n\t\tdst = bsoncore.AppendValueElement(dst, \"comment\", f.comment)\n\t}\n\tif f.filter != nil {\n\t\tdst = bsoncore.AppendDocumentElement(dst, \"filter\", f.filter)\n\t}\n\tif f.hint.Type != bsoncore.Type(0) {\n\t\tdst = bsoncore.AppendValueElement(dst, \"hint\", f.hint)\n\t}\n\tif f.let != nil {\n\t\tdst = bsoncore.AppendDocumentElement(dst, \"let\", f.let)\n\t}\n\tif f.limit != nil {\n\t\tdst = bsoncore.AppendInt64Element(dst, \"limit\", *f.limit)\n\t}\n\tif f.max != nil {\n\t\tdst = bsoncore.AppendDocumentElement(dst, \"max\", f.max)\n\t}\n\tif f.min != nil {\n\t\tdst = bsoncore.AppendDocumentElement(dst, \"min\", f.min)\n\t}\n\tif f.noCursorTimeout != nil {\n\t\tdst = bsoncore.AppendBooleanElement(dst, \"noCursorTimeout\", *f.noCursorTimeout)\n\t}\n\tif f.oplogReplay != nil {\n\t\tdst = bsoncore.AppendBooleanElement(dst, \"oplogReplay\", *f.oplogReplay)\n\t}\n\tif f.projection != nil {\n\t\tdst = bsoncore.AppendDocumentElement(dst, \"projection\", f.projection)\n\t}\n\tif f.returnKey != nil {\n\t\tdst = bsoncore.AppendBooleanElement(dst, \"returnKey\", *f.returnKey)\n\t}\n\tif f.showRecordID != nil {\n\t\tdst = bsoncore.AppendBooleanElement(dst, \"showRecordId\", *f.showRecordID)\n\t}\n\tif f.singleBatch != nil {\n\t\tdst = bsoncore.AppendBooleanElement(dst, \"singleBatch\", *f.singleBatch)\n\t}\n\tif f.skip != nil {\n\t\tdst = bsoncore.AppendInt64Element(dst, \"skip\", *f.skip)\n\t}\n\tif f.snapshot != nil {\n\t\tdst = bsoncore.AppendBooleanElement(dst, \"snapshot\", *f.snapshot)\n\t}\n\tif f.sort != nil {\n\t\tdst = bsoncore.AppendDocumentElement(dst, \"sort\", f.sort)\n\t}\n\tif f.tailable != nil {\n\t\tdst = bsoncore.AppendBooleanElement(dst, \"tailable\", *f.tailable)\n\t}\n\t// Set rawData for 8.2+ servers.\n\tif f.rawData != nil && desc.WireVersion != nil && driverutil.VersionRangeIncludes(*desc.WireVersion, 27) {\n\t\tdst = bsoncore.AppendBooleanElement(dst, \"rawData\", *f.rawData)\n\t}\n\treturn dst, nil\n}\n\n// AllowDiskUse when true allows temporary data to be written to disk during the find command.\"\nfunc (f *Find) AllowDiskUse(allowDiskUse bool) *Find {\n\tif f == nil {\n\t\tf = new(Find)\n\t}\n\n\tf.allowDiskUse = &allowDiskUse\n\treturn f\n}\n\n// AllowPartialResults when true allows partial results to be returned if some shards are down.\nfunc (f *Find) AllowPartialResults(allowPartialResults bool) *Find {\n\tif f == nil {\n\t\tf = new(Find)\n\t}\n\n\tf.allowPartialResults = &allowPartialResults\n\treturn f\n}\n\n// AwaitData when true makes a cursor block before returning when no data is available.\nfunc (f *Find) AwaitData(awaitData bool) *Find {\n\tif f == nil {\n\t\tf = new(Find)\n\t}\n\n\tf.awaitData = &awaitData\n\treturn f\n}\n\n// BatchSize specifies the number of documents to return in every batch.\nfunc (f *Find) BatchSize(batchSize int32) *Find {\n\tif f == nil {\n\t\tf = new(Find)\n\t}\n\n\tf.batchSize = &batchSize\n\treturn f\n}\n\n// Collation specifies a collation to be used.\nfunc (f *Find) Collation(collation bsoncore.Document) *Find {\n\tif f == nil {\n\t\tf = new(Find)\n\t}\n\n\tf.collation = collation\n\treturn f\n}\n\n// Comment sets a value to help trace an operation.\nfunc (f *Find) Comment(comment bsoncore.Value) *Find {\n\tif f == nil {\n\t\tf = new(Find)\n\t}\n\n\tf.comment = comment\n\treturn f\n}\n\n// Filter determines what results are returned from find.\nfunc (f *Find) Filter(filter bsoncore.Document) *Find {\n\tif f == nil {\n\t\tf = new(Find)\n\t}\n\n\tf.filter = filter\n\treturn f\n}\n\n// Hint specifies the index to use.\nfunc (f *Find) Hint(hint bsoncore.Value) *Find {\n\tif f == nil {\n\t\tf = new(Find)\n\t}\n\n\tf.hint = hint\n\treturn f\n}\n\n// Let specifies the let document to use. This option is only valid for server versions 5.0 and above.\nfunc (f *Find) Let(let bsoncore.Document) *Find {\n\tif f == nil {\n\t\tf = new(Find)\n\t}\n\n\tf.let = let\n\treturn f\n}\n\n// Limit sets a limit on the number of documents to return.\nfunc (f *Find) Limit(limit int64) *Find {\n\tif f == nil {\n\t\tf = new(Find)\n\t}\n\n\tf.limit = &limit\n\treturn f\n}\n\n// Max sets an exclusive upper bound for a specific index.\nfunc (f *Find) Max(max bsoncore.Document) *Find {\n\tif f == nil {\n\t\tf = new(Find)\n\t}\n\n\tf.max = max\n\treturn f\n}\n\n// Min sets an inclusive lower bound for a specific index.\nfunc (f *Find) Min(min bsoncore.Document) *Find {\n\tif f == nil {\n\t\tf = new(Find)\n\t}\n\n\tf.min = min\n\treturn f\n}\n\n// NoCursorTimeout when true prevents cursor from timing out after an inactivity period.\nfunc (f *Find) NoCursorTimeout(noCursorTimeout bool) *Find {\n\tif f == nil {\n\t\tf = new(Find)\n\t}\n\n\tf.noCursorTimeout = &noCursorTimeout\n\treturn f\n}\n\n// OplogReplay when true replays a replica set's oplog.\nfunc (f *Find) OplogReplay(oplogReplay bool) *Find {\n\tif f == nil {\n\t\tf = new(Find)\n\t}\n\n\tf.oplogReplay = &oplogReplay\n\treturn f\n}\n\n// Projection limits the fields returned for all documents.\nfunc (f *Find) Projection(projection bsoncore.Document) *Find {\n\tif f == nil {\n\t\tf = new(Find)\n\t}\n\n\tf.projection = projection\n\treturn f\n}\n\n// ReturnKey when true returns index keys for all result documents.\nfunc (f *Find) ReturnKey(returnKey bool) *Find {\n\tif f == nil {\n\t\tf = new(Find)\n\t}\n\n\tf.returnKey = &returnKey\n\treturn f\n}\n\n// ShowRecordID when true adds a $recordId field with the record identifier to returned documents.\nfunc (f *Find) ShowRecordID(showRecordID bool) *Find {\n\tif f == nil {\n\t\tf = new(Find)\n\t}\n\n\tf.showRecordID = &showRecordID\n\treturn f\n}\n\n// SingleBatch specifies whether the results should be returned in a single batch.\nfunc (f *Find) SingleBatch(singleBatch bool) *Find {\n\tif f == nil {\n\t\tf = new(Find)\n\t}\n\n\tf.singleBatch = &singleBatch\n\treturn f\n}\n\n// Skip specifies the number of documents to skip before returning.\nfunc (f *Find) Skip(skip int64) *Find {\n\tif f == nil {\n\t\tf = new(Find)\n\t}\n\n\tf.skip = &skip\n\treturn f\n}\n\n// Snapshot prevents the cursor from returning a document more than once because of an intervening write operation.\nfunc (f *Find) Snapshot(snapshot bool) *Find {\n\tif f == nil {\n\t\tf = new(Find)\n\t}\n\n\tf.snapshot = &snapshot\n\treturn f\n}\n\n// Sort specifies the order in which to return results.\nfunc (f *Find) Sort(sort bsoncore.Document) *Find {\n\tif f == nil {\n\t\tf = new(Find)\n\t}\n\n\tf.sort = sort\n\treturn f\n}\n\n// Tailable keeps a cursor open and resumable after the last data has been retrieved.\nfunc (f *Find) Tailable(tailable bool) *Find {\n\tif f == nil {\n\t\tf = new(Find)\n\t}\n\n\tf.tailable = &tailable\n\treturn f\n}\n\n// Session sets the session for this operation.\nfunc (f *Find) Session(session *session.Client) *Find {\n\tif f == nil {\n\t\tf = new(Find)\n\t}\n\n\tf.session = session\n\treturn f\n}\n\n// ClusterClock sets the cluster clock for this operation.\nfunc (f *Find) ClusterClock(clock *session.ClusterClock) *Find {\n\tif f == nil {\n\t\tf = new(Find)\n\t}\n\n\tf.clock = clock\n\treturn f\n}\n\n// Collection sets the collection that this command will run against.\nfunc (f *Find) Collection(collection string) *Find {\n\tif f == nil {\n\t\tf = new(Find)\n\t}\n\n\tf.collection = collection\n\treturn f\n}\n\n// CommandMonitor sets the monitor to use for APM events.\nfunc (f *Find) CommandMonitor(monitor *event.CommandMonitor) *Find {\n\tif f == nil {\n\t\tf = new(Find)\n\t}\n\n\tf.monitor = monitor\n\treturn f\n}\n\n// Crypt sets the Crypt object to use for automatic encryption and decryption.\nfunc (f *Find) Crypt(crypt driver.Crypt) *Find {\n\tif f == nil {\n\t\tf = new(Find)\n\t}\n\n\tf.crypt = crypt\n\treturn f\n}\n\n// Database sets the database to run this operation against.\nfunc (f *Find) Database(database string) *Find {\n\tif f == nil {\n\t\tf = new(Find)\n\t}\n\n\tf.database = database\n\treturn f\n}\n\n// Deployment sets the deployment to use for this operation.\nfunc (f *Find) Deployment(deployment driver.Deployment) *Find {\n\tif f == nil {\n\t\tf = new(Find)\n\t}\n\n\tf.deployment = deployment\n\treturn f\n}\n\n// ReadConcern specifies the read concern for this operation.\nfunc (f *Find) ReadConcern(readConcern *readconcern.ReadConcern) *Find {\n\tif f == nil {\n\t\tf = new(Find)\n\t}\n\n\tf.readConcern = readConcern\n\treturn f\n}\n\n// ReadPreference set the read preference used with this operation.\nfunc (f *Find) ReadPreference(readPreference *readpref.ReadPref) *Find {\n\tif f == nil {\n\t\tf = new(Find)\n\t}\n\n\tf.readPreference = readPreference\n\treturn f\n}\n\n// ServerSelector sets the selector used to retrieve a server.\nfunc (f *Find) ServerSelector(selector description.ServerSelector) *Find {\n\tif f == nil {\n\t\tf = new(Find)\n\t}\n\n\tf.selector = selector\n\treturn f\n}\n\n// Retry enables retryable mode for this operation. Retries are handled automatically in driver.Operation.Execute based\n// on how the operation is set.\nfunc (f *Find) Retry(retry driver.RetryMode) *Find {\n\tif f == nil {\n\t\tf = new(Find)\n\t}\n\n\tf.retry = &retry\n\treturn f\n}\n\n// ServerAPI sets the server API version for this operation.\nfunc (f *Find) ServerAPI(serverAPI *driver.ServerAPIOptions) *Find {\n\tif f == nil {\n\t\tf = new(Find)\n\t}\n\n\tf.serverAPI = serverAPI\n\treturn f\n}\n\n// Timeout sets the timeout for this operation.\nfunc (f *Find) Timeout(timeout *time.Duration) *Find {\n\tif f == nil {\n\t\tf = new(Find)\n\t}\n\n\tf.timeout = timeout\n\treturn f\n}\n\n// Logger sets the logger for this operation.\nfunc (f *Find) Logger(logger *logger.Logger) *Find {\n\tif f == nil {\n\t\tf = new(Find)\n\t}\n\n\tf.logger = logger\n\treturn f\n}\n\n// Authenticator sets the authenticator to use for this operation.\nfunc (f *Find) Authenticator(authenticator driver.Authenticator) *Find {\n\tif f == nil {\n\t\tf = new(Find)\n\t}\n\n\tf.authenticator = authenticator\n\treturn f\n}\n\n// RawData sets the rawData to access timeseries data in the compressed format.\nfunc (f *Find) RawData(rawData bool) *Find {\n\tif f == nil {\n\t\tf = new(Find)\n\t}\n\n\tf.rawData = &rawData\n\treturn f\n}\n\n// OmitMaxTimeMS omits the automatically-calculated \"maxTimeMS\" from the\n// command.\nfunc (f *Find) OmitMaxTimeMS(omit bool) *Find {\n\tif f == nil {\n\t\tf = new(Find)\n\t}\n\n\tf.omitMaxTimeMS = omit\n\treturn f\n}\n"
  },
  {
    "path": "x/mongo/driver/operation/find_and_modify.go",
    "content": "// Copyright (C) MongoDB, Inc. 2019-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage operation\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/event\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/driverutil\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/writeconcern\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/description\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/session\"\n)\n\n// FindAndModify performs a findAndModify operation.\ntype FindAndModify struct {\n\tauthenticator            driver.Authenticator\n\tarrayFilters             bsoncore.Array\n\tbypassDocumentValidation *bool\n\tcollation                bsoncore.Document\n\tcomment                  bsoncore.Value\n\tfields                   bsoncore.Document\n\tnewDocument              *bool\n\tquery                    bsoncore.Document\n\tremove                   *bool\n\tsort                     bsoncore.Document\n\tupdate                   bsoncore.Value\n\tupsert                   *bool\n\tsession                  *session.Client\n\tclock                    *session.ClusterClock\n\tcollection               string\n\tmonitor                  *event.CommandMonitor\n\tdatabase                 string\n\tdeployment               driver.Deployment\n\tselector                 description.ServerSelector\n\twriteConcern             *writeconcern.WriteConcern\n\tretry                    *driver.RetryMode\n\tcrypt                    driver.Crypt\n\thint                     bsoncore.Value\n\tserverAPI                *driver.ServerAPIOptions\n\tlet                      bsoncore.Document\n\ttimeout                  *time.Duration\n\trawData                  *bool\n\tadditionalCmd            bson.D\n\n\tresult FindAndModifyResult\n}\n\n// LastErrorObject represents information about updates and upserts returned by the server.\ntype LastErrorObject struct {\n\t// True if an update modified an existing document\n\tUpdatedExisting bool\n\t// Object ID of the upserted document.\n\tUpserted any\n}\n\n// FindAndModifyResult represents a findAndModify result returned by the server.\ntype FindAndModifyResult struct {\n\t// Either the old or modified document, depending on the value of the new parameter.\n\tValue bsoncore.Document\n\t// Contains information about updates and upserts.\n\tLastErrorObject LastErrorObject\n}\n\nfunc buildFindAndModifyResult(response bsoncore.Document) (FindAndModifyResult, error) {\n\telements, err := response.Elements()\n\tif err != nil {\n\t\treturn FindAndModifyResult{}, err\n\t}\n\tfamr := FindAndModifyResult{}\n\tfor _, element := range elements {\n\t\tswitch element.Key() {\n\t\tcase \"value\":\n\t\t\tvar ok bool\n\t\t\tfamr.Value, ok = element.Value().DocumentOK()\n\n\t\t\t// The 'value' field returned by a FindAndModify can be null in the case that no document was found.\n\t\t\tif element.Value().Type != bsoncore.TypeNull && !ok {\n\t\t\t\treturn famr, fmt.Errorf(\"response field 'value' is type document or null, but received BSON type %s\", element.Value().Type)\n\t\t\t}\n\t\tcase \"lastErrorObject\":\n\t\t\tvalDoc, ok := element.Value().DocumentOK()\n\t\t\tif !ok {\n\t\t\t\treturn famr, fmt.Errorf(\"response field 'lastErrorObject' is type document, but received BSON type %s\", element.Value().Type)\n\t\t\t}\n\n\t\t\tvar leo LastErrorObject\n\t\t\tif err = bson.Unmarshal(valDoc, &leo); err != nil {\n\t\t\t\treturn famr, err\n\t\t\t}\n\t\t\tfamr.LastErrorObject = leo\n\t\t}\n\t}\n\treturn famr, nil\n}\n\n// NewFindAndModify constructs and returns a new FindAndModify.\nfunc NewFindAndModify(query bsoncore.Document) *FindAndModify {\n\treturn &FindAndModify{\n\t\tquery: query,\n\t}\n}\n\n// Result returns the result of executing this operation.\nfunc (fam *FindAndModify) Result() FindAndModifyResult { return fam.result }\n\nfunc (fam *FindAndModify) processResponse(_ context.Context, resp bsoncore.Document, _ driver.ResponseInfo) error {\n\tvar err error\n\n\tfam.result, err = buildFindAndModifyResult(resp)\n\treturn err\n}\n\n// Execute runs this operations and returns an error if the operation did not execute successfully.\nfunc (fam *FindAndModify) Execute(ctx context.Context) error {\n\tif fam.deployment == nil {\n\t\treturn errors.New(\"the FindAndModify operation must have a Deployment set before Execute can be called\")\n\t}\n\n\treturn driver.Operation{\n\t\tCommandFn:         fam.command,\n\t\tProcessResponseFn: fam.processResponse,\n\n\t\tRetryMode:      fam.retry,\n\t\tType:           driver.Write,\n\t\tClient:         fam.session,\n\t\tClock:          fam.clock,\n\t\tCommandMonitor: fam.monitor,\n\t\tDatabase:       fam.database,\n\t\tDeployment:     fam.deployment,\n\t\tSelector:       fam.selector,\n\t\tWriteConcern:   fam.writeConcern,\n\t\tCrypt:          fam.crypt,\n\t\tServerAPI:      fam.serverAPI,\n\t\tTimeout:        fam.timeout,\n\t\tName:           driverutil.FindAndModifyOp,\n\t\tAuthenticator:  fam.authenticator,\n\t}.Execute(ctx)\n}\n\nfunc (fam *FindAndModify) command(dst []byte, desc description.SelectedServer) ([]byte, error) {\n\tdst = bsoncore.AppendStringElement(dst, \"findAndModify\", fam.collection)\n\tif fam.arrayFilters != nil {\n\n\t\tif desc.WireVersion == nil || !driverutil.VersionRangeIncludes(*desc.WireVersion, 6) {\n\t\t\treturn nil, errors.New(\"the 'arrayFilters' command parameter requires a minimum server wire version of 6\")\n\t\t}\n\t\tdst = bsoncore.AppendArrayElement(dst, \"arrayFilters\", fam.arrayFilters)\n\t}\n\tif fam.bypassDocumentValidation != nil {\n\t\tdst = bsoncore.AppendBooleanElement(dst, \"bypassDocumentValidation\", *fam.bypassDocumentValidation)\n\t}\n\tif fam.collation != nil {\n\n\t\tif desc.WireVersion == nil || !driverutil.VersionRangeIncludes(*desc.WireVersion, 5) {\n\t\t\treturn nil, errors.New(\"the 'collation' command parameter requires a minimum server wire version of 5\")\n\t\t}\n\t\tdst = bsoncore.AppendDocumentElement(dst, \"collation\", fam.collation)\n\t}\n\tif fam.comment.Type != bsoncore.Type(0) {\n\t\tdst = bsoncore.AppendValueElement(dst, \"comment\", fam.comment)\n\t}\n\tif fam.fields != nil {\n\t\tdst = bsoncore.AppendDocumentElement(dst, \"fields\", fam.fields)\n\t}\n\tif fam.newDocument != nil {\n\t\tdst = bsoncore.AppendBooleanElement(dst, \"new\", *fam.newDocument)\n\t}\n\tif fam.query != nil {\n\t\tdst = bsoncore.AppendDocumentElement(dst, \"query\", fam.query)\n\t}\n\tif fam.remove != nil {\n\t\tdst = bsoncore.AppendBooleanElement(dst, \"remove\", *fam.remove)\n\t}\n\tif fam.sort != nil {\n\t\tdst = bsoncore.AppendDocumentElement(dst, \"sort\", fam.sort)\n\t}\n\tif fam.update.Data != nil {\n\t\tdst = bsoncore.AppendValueElement(dst, \"update\", fam.update)\n\t}\n\tif fam.upsert != nil {\n\t\tdst = bsoncore.AppendBooleanElement(dst, \"upsert\", *fam.upsert)\n\t}\n\tif fam.hint.Type != bsoncore.Type(0) {\n\n\t\tif desc.WireVersion == nil || !driverutil.VersionRangeIncludes(*desc.WireVersion, 8) {\n\t\t\treturn nil, errors.New(\"the 'hint' command parameter requires a minimum server wire version of 8\")\n\t\t}\n\t\tif !fam.writeConcern.Acknowledged() {\n\t\t\treturn nil, errUnacknowledgedHint\n\t\t}\n\t\tdst = bsoncore.AppendValueElement(dst, \"hint\", fam.hint)\n\t}\n\tif fam.let != nil {\n\t\tdst = bsoncore.AppendDocumentElement(dst, \"let\", fam.let)\n\t}\n\t// Set rawData for 8.2+ servers.\n\tif fam.rawData != nil && desc.WireVersion != nil && driverutil.VersionRangeIncludes(*desc.WireVersion, 27) {\n\t\tdst = bsoncore.AppendBooleanElement(dst, \"rawData\", *fam.rawData)\n\t}\n\tif len(fam.additionalCmd) > 0 {\n\t\tdoc, err := bson.Marshal(fam.additionalCmd)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tdst = append(dst, doc[4:len(doc)-1]...)\n\t}\n\n\treturn dst, nil\n}\n\n// ArrayFilters specifies an array of filter documents that determines which array elements to modify for an update operation on an array field.\nfunc (fam *FindAndModify) ArrayFilters(arrayFilters bsoncore.Array) *FindAndModify {\n\tif fam == nil {\n\t\tfam = new(FindAndModify)\n\t}\n\n\tfam.arrayFilters = arrayFilters\n\treturn fam\n}\n\n// BypassDocumentValidation specifies if document validation can be skipped when executing the operation.\nfunc (fam *FindAndModify) BypassDocumentValidation(bypassDocumentValidation bool) *FindAndModify {\n\tif fam == nil {\n\t\tfam = new(FindAndModify)\n\t}\n\n\tfam.bypassDocumentValidation = &bypassDocumentValidation\n\treturn fam\n}\n\n// Collation specifies a collation to be used.\nfunc (fam *FindAndModify) Collation(collation bsoncore.Document) *FindAndModify {\n\tif fam == nil {\n\t\tfam = new(FindAndModify)\n\t}\n\n\tfam.collation = collation\n\treturn fam\n}\n\n// Comment sets a value to help trace an operation.\nfunc (fam *FindAndModify) Comment(comment bsoncore.Value) *FindAndModify {\n\tif fam == nil {\n\t\tfam = new(FindAndModify)\n\t}\n\n\tfam.comment = comment\n\treturn fam\n}\n\n// Fields specifies a subset of fields to return.\nfunc (fam *FindAndModify) Fields(fields bsoncore.Document) *FindAndModify {\n\tif fam == nil {\n\t\tfam = new(FindAndModify)\n\t}\n\n\tfam.fields = fields\n\treturn fam\n}\n\n// NewDocument specifies whether to return the modified document or the original. Defaults to false (return original).\nfunc (fam *FindAndModify) NewDocument(newDocument bool) *FindAndModify {\n\tif fam == nil {\n\t\tfam = new(FindAndModify)\n\t}\n\n\tfam.newDocument = &newDocument\n\treturn fam\n}\n\n// Query specifies the selection criteria for the modification.\nfunc (fam *FindAndModify) Query(query bsoncore.Document) *FindAndModify {\n\tif fam == nil {\n\t\tfam = new(FindAndModify)\n\t}\n\n\tfam.query = query\n\treturn fam\n}\n\n// Remove specifies that the matched document should be removed. Defaults to false.\nfunc (fam *FindAndModify) Remove(remove bool) *FindAndModify {\n\tif fam == nil {\n\t\tfam = new(FindAndModify)\n\t}\n\n\tfam.remove = &remove\n\treturn fam\n}\n\n// Sort determines which document the operation modifies if the query matches multiple documents.The first document matched by the sort order will be modified.\nfunc (fam *FindAndModify) Sort(sort bsoncore.Document) *FindAndModify {\n\tif fam == nil {\n\t\tfam = new(FindAndModify)\n\t}\n\n\tfam.sort = sort\n\treturn fam\n}\n\n// Update specifies the update document to perform on the matched document.\nfunc (fam *FindAndModify) Update(update bsoncore.Value) *FindAndModify {\n\tif fam == nil {\n\t\tfam = new(FindAndModify)\n\t}\n\n\tfam.update = update\n\treturn fam\n}\n\n// Upsert specifies whether or not to create a new document if no documents match the query when doing an update. Defaults to false.\nfunc (fam *FindAndModify) Upsert(upsert bool) *FindAndModify {\n\tif fam == nil {\n\t\tfam = new(FindAndModify)\n\t}\n\n\tfam.upsert = &upsert\n\treturn fam\n}\n\n// Session sets the session for this operation.\nfunc (fam *FindAndModify) Session(session *session.Client) *FindAndModify {\n\tif fam == nil {\n\t\tfam = new(FindAndModify)\n\t}\n\n\tfam.session = session\n\treturn fam\n}\n\n// ClusterClock sets the cluster clock for this operation.\nfunc (fam *FindAndModify) ClusterClock(clock *session.ClusterClock) *FindAndModify {\n\tif fam == nil {\n\t\tfam = new(FindAndModify)\n\t}\n\n\tfam.clock = clock\n\treturn fam\n}\n\n// Collection sets the collection that this command will run against.\nfunc (fam *FindAndModify) Collection(collection string) *FindAndModify {\n\tif fam == nil {\n\t\tfam = new(FindAndModify)\n\t}\n\n\tfam.collection = collection\n\treturn fam\n}\n\n// CommandMonitor sets the monitor to use for APM events.\nfunc (fam *FindAndModify) CommandMonitor(monitor *event.CommandMonitor) *FindAndModify {\n\tif fam == nil {\n\t\tfam = new(FindAndModify)\n\t}\n\n\tfam.monitor = monitor\n\treturn fam\n}\n\n// Database sets the database to run this operation against.\nfunc (fam *FindAndModify) Database(database string) *FindAndModify {\n\tif fam == nil {\n\t\tfam = new(FindAndModify)\n\t}\n\n\tfam.database = database\n\treturn fam\n}\n\n// Deployment sets the deployment to use for this operation.\nfunc (fam *FindAndModify) Deployment(deployment driver.Deployment) *FindAndModify {\n\tif fam == nil {\n\t\tfam = new(FindAndModify)\n\t}\n\n\tfam.deployment = deployment\n\treturn fam\n}\n\n// ServerSelector sets the selector used to retrieve a server.\nfunc (fam *FindAndModify) ServerSelector(selector description.ServerSelector) *FindAndModify {\n\tif fam == nil {\n\t\tfam = new(FindAndModify)\n\t}\n\n\tfam.selector = selector\n\treturn fam\n}\n\n// WriteConcern sets the write concern for this operation.\nfunc (fam *FindAndModify) WriteConcern(writeConcern *writeconcern.WriteConcern) *FindAndModify {\n\tif fam == nil {\n\t\tfam = new(FindAndModify)\n\t}\n\n\tfam.writeConcern = writeConcern\n\treturn fam\n}\n\n// Retry enables retryable writes for this operation. Retries are not handled automatically,\n// instead a boolean is returned from Execute and SelectAndExecute that indicates if the\n// operation can be retried. Retrying is handled by calling RetryExecute.\nfunc (fam *FindAndModify) Retry(retry driver.RetryMode) *FindAndModify {\n\tif fam == nil {\n\t\tfam = new(FindAndModify)\n\t}\n\n\tfam.retry = &retry\n\treturn fam\n}\n\n// Crypt sets the Crypt object to use for automatic encryption and decryption.\nfunc (fam *FindAndModify) Crypt(crypt driver.Crypt) *FindAndModify {\n\tif fam == nil {\n\t\tfam = new(FindAndModify)\n\t}\n\n\tfam.crypt = crypt\n\treturn fam\n}\n\n// Hint specifies the index to use.\nfunc (fam *FindAndModify) Hint(hint bsoncore.Value) *FindAndModify {\n\tif fam == nil {\n\t\tfam = new(FindAndModify)\n\t}\n\n\tfam.hint = hint\n\treturn fam\n}\n\n// ServerAPI sets the server API version for this operation.\nfunc (fam *FindAndModify) ServerAPI(serverAPI *driver.ServerAPIOptions) *FindAndModify {\n\tif fam == nil {\n\t\tfam = new(FindAndModify)\n\t}\n\n\tfam.serverAPI = serverAPI\n\treturn fam\n}\n\n// Let specifies the let document to use. This option is only valid for server versions 5.0 and above.\nfunc (fam *FindAndModify) Let(let bsoncore.Document) *FindAndModify {\n\tif fam == nil {\n\t\tfam = new(FindAndModify)\n\t}\n\n\tfam.let = let\n\treturn fam\n}\n\n// Timeout sets the timeout for this operation.\nfunc (fam *FindAndModify) Timeout(timeout *time.Duration) *FindAndModify {\n\tif fam == nil {\n\t\tfam = new(FindAndModify)\n\t}\n\n\tfam.timeout = timeout\n\treturn fam\n}\n\n// Authenticator sets the authenticator to use for this operation.\nfunc (fam *FindAndModify) Authenticator(authenticator driver.Authenticator) *FindAndModify {\n\tif fam == nil {\n\t\tfam = new(FindAndModify)\n\t}\n\n\tfam.authenticator = authenticator\n\treturn fam\n}\n\n// RawData sets the rawData to access timeseries data in the compressed format.\nfunc (fam *FindAndModify) RawData(rawData bool) *FindAndModify {\n\tif fam == nil {\n\t\tfam = new(FindAndModify)\n\t}\n\n\tfam.rawData = &rawData\n\treturn fam\n}\n\n// AdditionalCmd sets additional command fields to be attached.\nfunc (fam *FindAndModify) AdditionalCmd(d bson.D) *FindAndModify {\n\tif fam == nil {\n\t\tfam = new(FindAndModify)\n\t}\n\n\tfam.additionalCmd = d\n\treturn fam\n}\n"
  },
  {
    "path": "x/mongo/driver/operation/hello.go",
    "content": "// Copyright (C) MongoDB, Inc. 2021-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage operation\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"os\"\n\t\"runtime\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/bsonutil\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/driverutil\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/handshake\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/address\"\n\t\"go.mongodb.org/mongo-driver/v2/version\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/description\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/mnet\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/session\"\n)\n\n// maxClientMetadataSize is the maximum size of the client metadata document\n// that can be sent to the server. Note that the maximum document size on\n// standalone and replica servers is 1024, but the maximum document size on\n// sharded clusters is 512.\nconst maxClientMetadataSize = 512\n\nconst driverName = \"mongo-go-driver\"\n\n// Hello is used to run the handshake operation.\ntype Hello struct {\n\tauthenticator      driver.Authenticator\n\tappname            string\n\tcompressors        []string\n\tsaslSupportedMechs string\n\td                  driver.Deployment\n\tclock              *session.ClusterClock\n\tspeculativeAuth    bsoncore.Document\n\ttopologyVersion    *description.TopologyVersion\n\tmaxAwaitTimeMS     *int64\n\tserverAPI          *driver.ServerAPIOptions\n\tloadBalanced       bool\n\tomitMaxTimeMS      bool\n\n\t// Fields provided by a library that wraps the Go Driver.\n\touterLibraryName     string\n\touterLibraryVersion  string\n\touterLibraryPlatform string\n\n\tres bsoncore.Document\n}\n\nvar _ driver.Handshaker = (*Hello)(nil)\n\n// NewHello constructs a Hello.\nfunc NewHello() *Hello { return &Hello{} }\n\n// AppName sets the application name in the client metadata sent in this operation.\nfunc (h *Hello) AppName(appname string) *Hello {\n\th.appname = appname\n\treturn h\n}\n\n// ClusterClock sets the cluster clock for this operation.\nfunc (h *Hello) ClusterClock(clock *session.ClusterClock) *Hello {\n\tif h == nil {\n\t\th = new(Hello)\n\t}\n\n\th.clock = clock\n\treturn h\n}\n\n// Compressors sets the compressors that can be used.\nfunc (h *Hello) Compressors(compressors []string) *Hello {\n\th.compressors = compressors\n\treturn h\n}\n\n// SASLSupportedMechs retrieves the supported SASL mechanism for the given user when this operation\n// is run.\nfunc (h *Hello) SASLSupportedMechs(username string) *Hello {\n\th.saslSupportedMechs = username\n\treturn h\n}\n\n// Deployment sets the Deployment for this operation.\nfunc (h *Hello) Deployment(d driver.Deployment) *Hello {\n\th.d = d\n\treturn h\n}\n\n// SpeculativeAuthenticate sets the document to be used for speculative authentication.\nfunc (h *Hello) SpeculativeAuthenticate(doc bsoncore.Document) *Hello {\n\th.speculativeAuth = doc\n\treturn h\n}\n\n// TopologyVersion sets the TopologyVersion to be used for heartbeats.\nfunc (h *Hello) TopologyVersion(tv *description.TopologyVersion) *Hello {\n\th.topologyVersion = tv\n\treturn h\n}\n\n// MaxAwaitTimeMS sets the maximum time for the server to wait for topology changes during a heartbeat.\nfunc (h *Hello) MaxAwaitTimeMS(awaitTime int64) *Hello {\n\th.maxAwaitTimeMS = &awaitTime\n\treturn h\n}\n\n// ServerAPI sets the server API version for this operation.\nfunc (h *Hello) ServerAPI(serverAPI *driver.ServerAPIOptions) *Hello {\n\th.serverAPI = serverAPI\n\treturn h\n}\n\n// LoadBalanced specifies whether or not this operation is being sent over a connection to a load balanced cluster.\nfunc (h *Hello) LoadBalanced(lb bool) *Hello {\n\th.loadBalanced = lb\n\treturn h\n}\n\n// OuterLibraryName specifies the name of the library wrapping the Go Driver.\nfunc (h *Hello) OuterLibraryName(name string) *Hello {\n\th.outerLibraryName = name\n\n\treturn h\n}\n\n// OuterLibraryVersion specifies the version of the library wrapping the Go\n// Driver.\nfunc (h *Hello) OuterLibraryVersion(version string) *Hello {\n\th.outerLibraryVersion = version\n\n\treturn h\n}\n\n// OuterLibraryPlatform specifies the platform of the library wrapping the Go\n// Driver.\nfunc (h *Hello) OuterLibraryPlatform(platform string) *Hello {\n\th.outerLibraryPlatform = platform\n\n\treturn h\n}\n\n// Result returns the result of executing this operation.\nfunc (h *Hello) Result(addr address.Address) description.Server {\n\treturn driverutil.NewServerDescription(addr, bson.Raw(h.res))\n}\n\nconst dockerEnvPath = \"/.dockerenv\"\n\nconst (\n\t// Runtime names\n\truntimeNameDocker = \"docker\"\n\n\t// Orchestrator names\n\torchestratorNameK8s = \"kubernetes\"\n)\n\n// getFaasEnvName parses the FaaS environment variable name and returns the\n// corresponding name used by the client. If none of the variables or variables\n// for multiple names are populated the FaaS values MUST be entirely omitted.\n// When variables for multiple \"client.env.name\" values are present, \"vercel\"\n// takes precedence over \"aws.lambda\"; any other combination MUST cause FaaS\n// values to be entirely omitted.\nfunc getFaasEnvName() string {\n\tenvVars := []string{\n\t\tdriverutil.EnvVarAWSExecutionEnv,\n\t\tdriverutil.EnvVarAWSLambdaRuntimeAPI,\n\t\tdriverutil.EnvVarFunctionsWorkerRuntime,\n\t\tdriverutil.EnvVarKService,\n\t\tdriverutil.EnvVarFunctionName,\n\t\tdriverutil.EnvVarVercel,\n\t}\n\n\t// If none of the variables are populated the client.env value MUST be\n\t// entirely omitted.\n\tnames := make(map[string]struct{})\n\n\tfor _, envVar := range envVars {\n\t\tval := os.Getenv(envVar)\n\t\tif val == \"\" {\n\t\t\tcontinue\n\t\t}\n\n\t\tvar name string\n\n\t\tswitch envVar {\n\t\tcase driverutil.EnvVarAWSExecutionEnv:\n\t\t\tif !strings.HasPrefix(val, driverutil.AwsLambdaPrefix) {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tname = driverutil.EnvNameAWSLambda\n\t\tcase driverutil.EnvVarAWSLambdaRuntimeAPI:\n\t\t\tname = driverutil.EnvNameAWSLambda\n\t\tcase driverutil.EnvVarFunctionsWorkerRuntime:\n\t\t\tname = driverutil.EnvNameAzureFunc\n\t\tcase driverutil.EnvVarKService, driverutil.EnvVarFunctionName:\n\t\t\tname = driverutil.EnvNameGCPFunc\n\t\tcase driverutil.EnvVarVercel:\n\t\t\t// \"vercel\" takes precedence over \"aws.lambda\".\n\t\t\tdelete(names, driverutil.EnvNameAWSLambda)\n\n\t\t\tname = driverutil.EnvNameVercel\n\t\t}\n\n\t\tnames[name] = struct{}{}\n\t\tif len(names) > 1 {\n\t\t\t// If multiple names are populated the client.env value\n\t\t\t// MUST be entirely omitted.\n\t\t\tnames = nil\n\n\t\t\tbreak\n\t\t}\n\t}\n\n\tfor name := range names {\n\t\treturn name\n\t}\n\n\treturn \"\"\n}\n\ntype containerInfo struct {\n\truntime      string\n\torchestrator string\n}\n\n// getContainerEnvInfo returns runtime and orchestrator of a container.\n// If no fields is populated, the client.env.container value MUST be entirely\n// omitted.\nfunc getContainerEnvInfo() *containerInfo {\n\tvar runtime, orchestrator string\n\tif _, err := os.Stat(dockerEnvPath); !os.IsNotExist(err) {\n\t\truntime = runtimeNameDocker\n\t}\n\tif v := os.Getenv(driverutil.EnvVarK8s); v != \"\" {\n\t\torchestrator = orchestratorNameK8s\n\t}\n\tif runtime != \"\" || orchestrator != \"\" {\n\t\treturn &containerInfo{\n\t\t\truntime:      runtime,\n\t\t\torchestrator: orchestrator,\n\t\t}\n\t}\n\treturn nil\n}\n\n// appendClientAppName appends the application metadata to the dst. It is the\n// responsibility of the caller to check that this appending does not cause dst\n// to exceed any size limitations.\nfunc appendClientAppName(dst []byte, name string) ([]byte, error) {\n\tif name == \"\" {\n\t\treturn dst, nil\n\t}\n\n\tvar idx int32\n\tidx, dst = bsoncore.AppendDocumentElementStart(dst, \"application\")\n\n\tdst = bsoncore.AppendStringElement(dst, \"name\", name)\n\n\treturn bsoncore.AppendDocumentEnd(dst, idx)\n}\n\n// appendClientDriver appends the driver metadata to dst. It is the\n// responsibility of the caller to check that this appending does not cause dst\n// to exceed any size limitations.\nfunc appendClientDriver(dst []byte, outerLibraryName, outerLibraryVersion string) ([]byte, error) {\n\tvar idx int32\n\tidx, dst = bsoncore.AppendDocumentElementStart(dst, \"driver\")\n\n\tname := driverName\n\tif outerLibraryName != \"\" {\n\t\tname = name + \"|\" + outerLibraryName\n\t}\n\n\tversion := version.Driver\n\tif outerLibraryVersion != \"\" {\n\t\tversion = version + \"|\" + outerLibraryVersion\n\t}\n\n\tdst = bsoncore.AppendStringElement(dst, \"name\", name)\n\tdst = bsoncore.AppendStringElement(dst, \"version\", version)\n\n\treturn bsoncore.AppendDocumentEnd(dst, idx)\n}\n\n// appendClientEnv appends the environment metadata to dst. It is the\n// responsibility of the caller to check that this appending does not cause dst\n// to exceed any size limitations.\nfunc appendClientEnv(dst []byte, omitNonName, omitDoc bool) ([]byte, error) {\n\tif omitDoc {\n\t\treturn dst, nil\n\t}\n\n\tname := getFaasEnvName()\n\tcontainer := getContainerEnvInfo()\n\t// Omit the entire 'env' if both name and container are empty because other\n\t// fields depend on either of them.\n\tif name == \"\" && container == nil {\n\t\treturn dst, nil\n\t}\n\n\tvar idx int32\n\n\tidx, dst = bsoncore.AppendDocumentElementStart(dst, \"env\")\n\n\tif name != \"\" {\n\t\tdst = bsoncore.AppendStringElement(dst, \"name\", name)\n\t}\n\n\taddMem := func(envVar string) []byte {\n\t\tmem := os.Getenv(envVar)\n\t\tif mem == \"\" {\n\t\t\treturn dst\n\t\t}\n\n\t\tmemInt64, err := strconv.ParseInt(mem, 10, 32)\n\t\tif err != nil {\n\t\t\treturn dst\n\t\t}\n\n\t\tmemInt32 := int32(memInt64)\n\n\t\treturn bsoncore.AppendInt32Element(dst, \"memory_mb\", memInt32)\n\t}\n\n\taddRegion := func(envVar string) []byte {\n\t\tregion := os.Getenv(envVar)\n\t\tif region == \"\" {\n\t\t\treturn dst\n\t\t}\n\n\t\treturn bsoncore.AppendStringElement(dst, \"region\", region)\n\t}\n\n\taddTimeout := func(envVar string) []byte {\n\t\ttimeout := os.Getenv(envVar)\n\t\tif timeout == \"\" {\n\t\t\treturn dst\n\t\t}\n\n\t\ttimeoutInt64, err := strconv.ParseInt(timeout, 10, 32)\n\t\tif err != nil {\n\t\t\treturn dst\n\t\t}\n\n\t\ttimeoutInt32 := int32(timeoutInt64)\n\t\treturn bsoncore.AppendInt32Element(dst, \"timeout_sec\", timeoutInt32)\n\t}\n\n\tif !omitNonName {\n\t\t// No other FaaS fields will be populated if the name is empty.\n\t\tswitch name {\n\t\tcase driverutil.EnvNameAWSLambda:\n\t\t\tdst = addMem(driverutil.EnvVarAWSLambdaFunctionMemorySize)\n\t\t\tdst = addRegion(driverutil.EnvVarAWSRegion)\n\t\tcase driverutil.EnvNameGCPFunc:\n\t\t\tdst = addMem(driverutil.EnvVarFunctionMemoryMB)\n\t\t\tdst = addRegion(driverutil.EnvVarFunctionRegion)\n\t\t\tdst = addTimeout(driverutil.EnvVarFunctionTimeoutSec)\n\t\tcase driverutil.EnvNameVercel:\n\t\t\tdst = addRegion(driverutil.EnvVarVercelRegion)\n\t\t}\n\t}\n\n\tif container != nil {\n\t\tvar idxCntnr int32\n\t\tidxCntnr, dst = bsoncore.AppendDocumentElementStart(dst, \"container\")\n\t\tif container.runtime != \"\" {\n\t\t\tdst = bsoncore.AppendStringElement(dst, \"runtime\", container.runtime)\n\t\t}\n\t\tif container.orchestrator != \"\" {\n\t\t\tdst = bsoncore.AppendStringElement(dst, \"orchestrator\", container.orchestrator)\n\t\t}\n\t\tvar err error\n\t\tdst, err = bsoncore.AppendDocumentEnd(dst, idxCntnr)\n\t\tif err != nil {\n\t\t\treturn dst, err\n\t\t}\n\t}\n\n\treturn bsoncore.AppendDocumentEnd(dst, idx)\n}\n\n// appendClientOS appends the OS metadata to dst. It is the responsibility of the\n// caller to check that this appending does not cause dst to exceed any size\n// limitations.\nfunc appendClientOS(dst []byte, omitNonType bool) ([]byte, error) {\n\tvar idx int32\n\n\tidx, dst = bsoncore.AppendDocumentElementStart(dst, \"os\")\n\n\tdst = bsoncore.AppendStringElement(dst, \"type\", runtime.GOOS)\n\tif !omitNonType {\n\t\tdst = bsoncore.AppendStringElement(dst, \"architecture\", runtime.GOARCH)\n\t}\n\n\treturn bsoncore.AppendDocumentEnd(dst, idx)\n}\n\n// appendClientPlatform appends the platform metadata to dst. It is the\n// responsibility of the caller to check that this appending does not cause dst\n// to exceed any size limitations.\nfunc appendClientPlatform(dst []byte, outerLibraryPlatform string) []byte {\n\tplatform := runtime.Version()\n\tif outerLibraryPlatform != \"\" {\n\t\tplatform = platform + \"|\" + outerLibraryPlatform\n\t}\n\n\treturn bsoncore.AppendStringElement(dst, \"platform\", platform)\n}\n\n// encodeClientMetadata encodes the client metadata into a BSON document. maxLen\n// is the maximum length the document can be. If the document exceeds maxLen,\n// then an empty byte slice is returned. If there is not enough space to encode\n// a document, the document is truncated and returned.\n//\n// This function attempts to build the following document. Fields are omitted to\n// save space following the MongoDB Handshake.\n//\n//\t{\n//\t\tapplication: {\n//\t\t\tname: \"<string>\"\n//\t\t},\n//\t\tdriver: {\n//\t\t\tname: \"<string>\",\n//\t\t\tversion: \"<string>\"\n//\t\t},\n//\t\tplatform: \"<string>\",\n//\t\tos: {\n//\t\t\ttype: \"<string>\",\n//\t\t\tname: \"<string>\",\n//\t\t\tarchitecture: \"<string>\",\n//\t\t\tversion: \"<string>\"\n//\t\t},\n//\t\tenv: {\n//\t\t\tname: \"<string>\",\n//\t\t\ttimeout_sec: 42,\n//\t\t\tmemory_mb: 1024,\n//\t\t\tregion: \"<string>\",\n//\t\t\tcontainer: {\n//\t\t\t\truntime: \"<string>\",\n//\t\t\t\torchestrator: \"<string>\"\n//\t\t\t}\n//\t\t}\n//\t}\nfunc encodeClientMetadata(h *Hello, maxLen int) ([]byte, error) {\n\tdst := make([]byte, 0, maxLen)\n\n\tomitEnvDoc := false\n\tomitEnvNonName := false\n\tomitOSNonType := false\n\tomitEnvDocument := false\n\ttruncatePlatform := false\n\nretry:\n\tvar idx int32\n\tidx, dst = bsoncore.AppendDocumentStart(dst)\n\n\tvar err error\n\tdst, err = appendClientAppName(dst, h.appname)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tdst, err = appendClientDriver(dst, h.outerLibraryName, h.outerLibraryVersion)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tdst, err = appendClientOS(dst, omitOSNonType)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif !truncatePlatform {\n\t\tdst = appendClientPlatform(dst, h.outerLibraryPlatform)\n\t}\n\n\tif !omitEnvDocument {\n\t\tdst, err = appendClientEnv(dst, omitEnvNonName, omitEnvDoc)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\tdst, err = bsoncore.AppendDocumentEnd(dst, idx)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif len(dst) > maxLen {\n\t\t// Implementers SHOULD cumulatively update fields in the\n\t\t// following order until the document is under the size limit\n\t\t//\n\t\t//    1. Omit fields from ``env`` except ``env.name``\n\t\t//    2. Omit fields from ``os`` except ``os.type``\n\t\t//    3. Omit the ``env`` document entirely\n\t\t//    4. Truncate ``platform``\n\t\tdst = dst[:0]\n\n\t\tif !omitEnvNonName {\n\t\t\tomitEnvNonName = true\n\n\t\t\tgoto retry\n\t\t}\n\n\t\tif !omitOSNonType {\n\t\t\tomitOSNonType = true\n\n\t\t\tgoto retry\n\t\t}\n\n\t\tif !omitEnvDoc {\n\t\t\tomitEnvDoc = true\n\n\t\t\tgoto retry\n\t\t}\n\n\t\tif !truncatePlatform {\n\t\t\ttruncatePlatform = true\n\n\t\t\tgoto retry\n\t\t}\n\n\t\t// There is nothing left to update. Return an empty slice to\n\t\t// tell caller not to append a `client` document.\n\t\treturn nil, nil\n\t}\n\n\treturn dst, nil\n}\n\n// handshakeCommand appends all necessary command fields as well as client metadata, SASL supported mechs, and compression.\nfunc (h *Hello) handshakeCommand(dst []byte, desc description.SelectedServer) ([]byte, error) {\n\tdst, err := h.command(dst, desc)\n\tif err != nil {\n\t\treturn dst, err\n\t}\n\n\tif h.saslSupportedMechs != \"\" {\n\t\tdst = bsoncore.AppendStringElement(dst, \"saslSupportedMechs\", h.saslSupportedMechs)\n\t}\n\tif h.speculativeAuth != nil {\n\t\tdst = bsoncore.AppendDocumentElement(dst, \"speculativeAuthenticate\", h.speculativeAuth)\n\t}\n\tvar idx int32\n\tidx, dst = bsoncore.AppendArrayElementStart(dst, \"compression\")\n\tfor i, compressor := range h.compressors {\n\t\tdst = bsoncore.AppendStringElement(dst, strconv.Itoa(i), compressor)\n\t}\n\tdst, _ = bsoncore.AppendArrayEnd(dst, idx)\n\n\tclientMetadata, _ := encodeClientMetadata(h, maxClientMetadataSize)\n\n\t// If the client metadata is empty, do not append it to the command.\n\tif len(clientMetadata) > 0 {\n\t\tdst = bsoncore.AppendDocumentElement(dst, \"client\", clientMetadata)\n\t}\n\n\treturn dst, nil\n}\n\n// command appends all necessary command fields.\nfunc (h *Hello) command(dst []byte, desc description.SelectedServer) ([]byte, error) {\n\t// Use \"hello\" if topology is LoadBalanced, API version is declared or server\n\t// has responded with \"helloOk\". Otherwise, use legacy hello.\n\tif h.loadBalanced || h.serverAPI != nil || desc.HelloOK {\n\t\tdst = bsoncore.AppendInt32Element(dst, \"hello\", 1)\n\t} else {\n\t\tdst = bsoncore.AppendInt32Element(dst, handshake.LegacyHello, 1)\n\t}\n\tdst = bsoncore.AppendBooleanElement(dst, \"helloOk\", true)\n\n\tif tv := h.topologyVersion; tv != nil {\n\t\tvar tvIdx int32\n\n\t\ttvIdx, dst = bsoncore.AppendDocumentElementStart(dst, \"topologyVersion\")\n\t\tdst = bsoncore.AppendObjectIDElement(dst, \"processId\", tv.ProcessID)\n\t\tdst = bsoncore.AppendInt64Element(dst, \"counter\", tv.Counter)\n\t\tdst, _ = bsoncore.AppendDocumentEnd(dst, tvIdx)\n\t}\n\tif h.maxAwaitTimeMS != nil {\n\t\tdst = bsoncore.AppendInt64Element(dst, \"maxAwaitTimeMS\", *h.maxAwaitTimeMS)\n\t}\n\tif h.loadBalanced {\n\t\t// The loadBalanced parameter should only be added if it's true. We should never explicitly send\n\t\t// loadBalanced=false per the load balancing spec.\n\t\tdst = bsoncore.AppendBooleanElement(dst, \"loadBalanced\", true)\n\t}\n\n\treturn dst, nil\n}\n\n// Execute runs this operation.\nfunc (h *Hello) Execute(ctx context.Context) error {\n\tif h.d == nil {\n\t\treturn errors.New(\"a Hello must have a Deployment set before Execute can be called\")\n\t}\n\n\treturn h.createOperation().Execute(ctx)\n}\n\n// StreamResponse gets the next streaming Hello response from the server.\nfunc (h *Hello) StreamResponse(ctx context.Context, conn *mnet.Connection) error {\n\treturn h.createOperation().ExecuteExhaust(ctx, conn)\n}\n\n// isLegacyHandshake returns True if server API version is not requested and\n// loadBalanced is False. If this is the case, then the drivers MUST use legacy\n// hello for the first message of the initial handshake with the OP_QUERY\n// protocol\nfunc isLegacyHandshake(srvAPI *driver.ServerAPIOptions, loadbalanced bool) bool {\n\treturn srvAPI == nil && !loadbalanced\n}\n\nfunc (h *Hello) createOperation() driver.Operation {\n\top := driver.Operation{\n\t\tClock:      h.clock,\n\t\tCommandFn:  h.command,\n\t\tDatabase:   \"admin\",\n\t\tDeployment: h.d,\n\t\tProcessResponseFn: func(_ context.Context, resp bsoncore.Document, _ driver.ResponseInfo) error {\n\t\t\th.res = resp\n\t\t\treturn nil\n\t\t},\n\t\tServerAPI:     h.serverAPI,\n\t\tOmitMaxTimeMS: h.omitMaxTimeMS,\n\t}\n\n\tif isLegacyHandshake(h.serverAPI, h.loadBalanced) {\n\t\top.Legacy = driver.LegacyHandshake\n\t}\n\n\treturn op\n}\n\n// GetHandshakeInformation performs the MongoDB handshake for the provided connection and returns the relevant\n// information about the server. This function implements the driver.Handshaker interface.\nfunc (h *Hello) GetHandshakeInformation(ctx context.Context, _ address.Address, conn *mnet.Connection) (driver.HandshakeInformation, error) {\n\tdeployment := driver.SingleConnectionDeployment{C: conn}\n\n\top := driver.Operation{\n\t\tClock:      h.clock,\n\t\tCommandFn:  h.handshakeCommand,\n\t\tDeployment: deployment,\n\t\tDatabase:   \"admin\",\n\t\tProcessResponseFn: func(_ context.Context, resp bsoncore.Document, _ driver.ResponseInfo) error {\n\t\t\th.res = resp\n\t\t\treturn nil\n\t\t},\n\t\tServerAPI: h.serverAPI,\n\t}\n\n\tif isLegacyHandshake(h.serverAPI, h.loadBalanced) {\n\t\top.Legacy = driver.LegacyHandshake\n\t}\n\n\tif err := op.Execute(ctx); err != nil {\n\t\treturn driver.HandshakeInformation{}, err\n\t}\n\n\tinfo := driver.HandshakeInformation{\n\t\tDescription: h.Result(conn.Address()),\n\t}\n\tif speculativeAuthenticate, ok := h.res.Lookup(\"speculativeAuthenticate\").DocumentOK(); ok {\n\t\tinfo.SpeculativeAuthenticate = speculativeAuthenticate\n\t}\n\tif serverConnectionID, ok := h.res.Lookup(\"connectionId\").AsInt64OK(); ok {\n\t\tinfo.ServerConnectionID = &serverConnectionID\n\t}\n\n\tvar err error\n\n\t// Cast to bson.Raw to lookup saslSupportedMechs to avoid converting from bsoncore.Value to bson.RawValue for the\n\t// StringSliceFromRawValue call.\n\tif saslSupportedMechs, lookupErr := bson.Raw(h.res).LookupErr(\"saslSupportedMechs\"); lookupErr == nil {\n\t\tinfo.SaslSupportedMechs, err = bsonutil.StringSliceFromRawValue(\"saslSupportedMechs\", saslSupportedMechs)\n\t}\n\treturn info, err\n}\n\n// FinishHandshake implements the Handshaker interface. This is a no-op function because a non-authenticated connection\n// does not do anything besides the initial Hello for a handshake.\nfunc (h *Hello) FinishHandshake(context.Context, *mnet.Connection) error {\n\treturn nil\n}\n\n// OmitMaxTimeMS will ensure maxTimMS is not included in the wire message\n// constructed to send a hello request.\nfunc (h *Hello) OmitMaxTimeMS(val bool) *Hello {\n\tif h == nil {\n\t\th = new(Hello)\n\t}\n\th.omitMaxTimeMS = val\n\treturn h\n}\n\n// Authenticator sets the authenticator to use for this operation.\nfunc (h *Hello) Authenticator(authenticator driver.Authenticator) *Hello {\n\tif h == nil {\n\t\th = new(Hello)\n\t}\n\n\th.authenticator = authenticator\n\treturn h\n}\n"
  },
  {
    "path": "x/mongo/driver/operation/hello_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2023-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage operation\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"runtime\"\n\t\"testing\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/driverutil\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/require\"\n\t\"go.mongodb.org/mongo-driver/v2/version\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n)\n\nfunc assertDocsEqual(t *testing.T, got bsoncore.Document, want []byte) {\n\tt.Helper()\n\n\tvar gotD bson.D\n\terr := bson.Unmarshal(got, &gotD)\n\trequire.NoError(t, err, \"error unmarshaling got document: %v\", err)\n\n\tvar wantD bson.D\n\terr = bson.UnmarshalExtJSON(want, true, &wantD)\n\trequire.NoError(t, err, \"error unmarshaling want byte slice: %v\", err)\n\n\tassert.Equal(t, wantD, gotD, \"got %v, want %v\", gotD, wantD)\n}\n\nfunc encodeWithCallback(t *testing.T, cb func(int, []byte) ([]byte, error)) bsoncore.Document {\n\tt.Helper()\n\n\tvar err error\n\tidx, dst := bsoncore.AppendDocumentStart(nil)\n\n\tdst, err = cb(len(dst), dst)\n\trequire.NoError(t, err, \"error appending client metadata: %v\", err)\n\n\tdst, err = bsoncore.AppendDocumentEnd(dst, idx)\n\trequire.NoError(t, err, \"error appending document end: %v\", err)\n\n\tgot, _, ok := bsoncore.ReadDocument(dst)\n\trequire.True(t, ok, \"error reading document: %v\", got)\n\n\treturn got\n}\n\n// clearTestEnv will clear the test environment created by tests. This will\n// ensure that the local environment does not effect the outcome of a unit\n// test.\nfunc clearTestEnv(t *testing.T) {\n\tt.Setenv(\"AWS_EXECUTION_ENV\", \"\")\n\tt.Setenv(\"AWS_LAMBDA_RUNTIME_API\", \"\")\n\tt.Setenv(\"FUNCTIONS_WORKER_RUNTIME\", \"\")\n\tt.Setenv(\"K_SERVICE\", \"\")\n\tt.Setenv(\"FUNCTION_NAME\", \"\")\n\tt.Setenv(\"VERCEL\", \"\")\n\tt.Setenv(\"AWS_REGION\", \"\")\n\tt.Setenv(\"AWS_LAMBDA_FUNCTION_MEMORY_SIZE\", \"\")\n\tt.Setenv(\"FUNCTION_MEMORY_MB\", \"\")\n\tt.Setenv(\"FUNCTION_TIMEOUT_SEC\", \"\")\n\tt.Setenv(\"FUNCTION_REGION\", \"\")\n\tt.Setenv(\"VERCEL_REGION\", \"\")\n}\n\nfunc TestAppendClientName(t *testing.T) {\n\tt.Parallel()\n\n\ttests := []struct {\n\t\tname    string\n\t\tappname string\n\t\twant    []byte // Extend JSON\n\t}{\n\t\t{\n\t\t\tname: \"empty\",\n\t\t\twant: []byte(`{}`),\n\t\t},\n\t\t{\n\t\t\tname:    \"non-empty\",\n\t\t\tappname: \"foo\",\n\t\t\twant:    []byte(`{\"application\":{\"name\":\"foo\"}}`),\n\t\t},\n\t}\n\n\tfor _, test := range tests {\n\t\ttest := test\n\n\t\tt.Run(test.name, func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tcb := func(_ int, dst []byte) ([]byte, error) {\n\t\t\t\tvar err error\n\t\t\t\tdst, err = appendClientAppName(dst, test.appname)\n\n\t\t\t\treturn dst, err\n\t\t\t}\n\n\t\t\tgot := encodeWithCallback(t, cb)\n\t\t\tassertDocsEqual(t, got, test.want)\n\t\t})\n\t}\n}\n\nfunc TestAppendClientDriver(t *testing.T) {\n\tt.Parallel()\n\n\ttests := []struct {\n\t\tname                string\n\t\touterLibraryName    string\n\t\touterLibraryVersion string\n\t\twant                []byte // Extend JSON\n\t}{\n\t\t{\n\t\t\tname:                \"full\",\n\t\t\touterLibraryName:    \"\",\n\t\t\touterLibraryVersion: \"\",\n\t\t\twant:                []byte(fmt.Sprintf(`{\"driver\":{\"name\": %q, \"version\": %q}}`, driverName, version.Driver)),\n\t\t},\n\t\t{\n\t\t\tname:                \"with outer library data\",\n\t\t\touterLibraryName:    \"outer-library-name\",\n\t\t\touterLibraryVersion: \"outer-library-version\",\n\t\t\twant:                []byte(fmt.Sprintf(`{\"driver\":{\"name\": \"%s|outer-library-name\", \"version\": \"%s|outer-library-version\"}}`, driverName, version.Driver)),\n\t\t},\n\t}\n\n\tfor _, test := range tests {\n\t\ttest := test\n\n\t\tt.Run(test.name, func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tcb := func(_ int, dst []byte) ([]byte, error) {\n\t\t\t\tvar err error\n\t\t\t\tdst, err = appendClientDriver(dst, test.outerLibraryName, test.outerLibraryVersion)\n\n\t\t\t\treturn dst, err\n\t\t\t}\n\n\t\t\tgot := encodeWithCallback(t, cb)\n\t\t\tassertDocsEqual(t, got, test.want)\n\t\t})\n\t}\n}\n\nfunc TestAppendClientEnv(t *testing.T) {\n\tclearTestEnv(t)\n\n\tif os.Getenv(\"DOCKER_RUNNING\") != \"\" {\n\t\tt.Skip(\"These tests gives different results when run in Docker due to extra environment data.\")\n\t}\n\n\ttests := []struct {\n\t\tname          string\n\t\tomitEnvFields bool\n\t\tenv           map[string]string\n\t\twant          []byte // Extended JSON\n\t}{\n\t\t{\n\t\t\tname: \"empty\",\n\t\t\twant: []byte(`{}`),\n\t\t},\n\t\t{\n\t\t\tname:          \"empty with omit\",\n\t\t\tomitEnvFields: true,\n\t\t\twant:          []byte(`{}`),\n\t\t},\n\t\t{\n\t\t\tname: \"aws only\",\n\t\t\tenv: map[string]string{\n\t\t\t\t\"AWS_EXECUTION_ENV\": \"AWS_Lambda_foo\",\n\t\t\t},\n\t\t\twant: []byte(`{\"env\":{\"name\":\"aws.lambda\"}}`),\n\t\t},\n\t\t{\n\t\t\tname: \"aws mem only\",\n\t\t\tenv: map[string]string{\n\t\t\t\t\"AWS_EXECUTION_ENV\":               \"AWS_Lambda_foo\",\n\t\t\t\t\"AWS_LAMBDA_FUNCTION_MEMORY_SIZE\": \"1024\",\n\t\t\t},\n\t\t\twant: []byte(`{\"env\":{\"name\":\"aws.lambda\",\"memory_mb\":1024}}`),\n\t\t},\n\t\t{\n\t\t\tname: \"aws region only\",\n\t\t\tenv: map[string]string{\n\t\t\t\t\"AWS_EXECUTION_ENV\": \"AWS_Lambda_foo\",\n\t\t\t\t\"AWS_REGION\":        \"us-east-2\",\n\t\t\t},\n\t\t\twant: []byte(`{\"env\":{\"name\":\"aws.lambda\",\"region\":\"us-east-2\"}}`),\n\t\t},\n\t\t{\n\t\t\tname: \"aws mem and region\",\n\t\t\tenv: map[string]string{\n\t\t\t\t\"AWS_EXECUTION_ENV\":               \"AWS_Lambda_foo\",\n\t\t\t\t\"AWS_LAMBDA_FUNCTION_MEMORY_SIZE\": \"1024\",\n\t\t\t\t\"AWS_REGION\":                      \"us-east-2\",\n\t\t\t},\n\t\t\twant: []byte(`{\"env\":{\"name\":\"aws.lambda\",\"memory_mb\":1024,\"region\":\"us-east-2\"}}`),\n\t\t},\n\t\t{\n\t\t\tname:          \"aws mem and region with omit fields\",\n\t\t\tomitEnvFields: true,\n\t\t\tenv: map[string]string{\n\t\t\t\t\"AWS_EXECUTION_ENV\":               \"AWS_Lambda_foo\",\n\t\t\t\t\"AWS_LAMBDA_FUNCTION_MEMORY_SIZE\": \"1024\",\n\t\t\t\t\"AWS_REGION\":                      \"us-east-2\",\n\t\t\t},\n\t\t\twant: []byte(`{\"env\":{\"name\":\"aws.lambda\"}}`),\n\t\t},\n\t\t{\n\t\t\tname: \"gcp only\",\n\t\t\tenv: map[string]string{\n\t\t\t\t\"K_SERVICE\": \"servicename\",\n\t\t\t},\n\t\t\twant: []byte(`{\"env\":{\"name\":\"gcp.func\"}}`),\n\t\t},\n\t\t{\n\t\t\tname: \"gcp mem\",\n\t\t\tenv: map[string]string{\n\t\t\t\t\"K_SERVICE\":          \"servicename\",\n\t\t\t\t\"FUNCTION_MEMORY_MB\": \"1024\",\n\t\t\t},\n\t\t\twant: []byte(`{\"env\":{\"name\":\"gcp.func\",\"memory_mb\":1024}}`),\n\t\t},\n\t\t{\n\t\t\tname: \"gcp region\",\n\t\t\tenv: map[string]string{\n\t\t\t\t\"K_SERVICE\":       \"servicename\",\n\t\t\t\t\"FUNCTION_REGION\": \"us-east-2\",\n\t\t\t},\n\t\t\twant: []byte(`{\"env\":{\"name\":\"gcp.func\",\"region\":\"us-east-2\"}}`),\n\t\t},\n\t\t{\n\t\t\tname: \"gcp timeout\",\n\t\t\tenv: map[string]string{\n\t\t\t\t\"K_SERVICE\":            \"servicename\",\n\t\t\t\t\"FUNCTION_TIMEOUT_SEC\": \"1\",\n\t\t\t},\n\t\t\twant: []byte(`{\"env\":{\"name\":\"gcp.func\",\"timeout_sec\":1}}`),\n\t\t},\n\t\t{\n\t\t\tname: \"gcp mem, region, and timeout\",\n\t\t\tenv: map[string]string{\n\t\t\t\t\"K_SERVICE\":            \"servicename\",\n\t\t\t\t\"FUNCTION_TIMEOUT_SEC\": \"1\",\n\t\t\t\t\"FUNCTION_REGION\":      \"us-east-2\",\n\t\t\t\t\"FUNCTION_MEMORY_MB\":   \"1024\",\n\t\t\t},\n\t\t\twant: []byte(`{\"env\":{\"name\":\"gcp.func\",\"memory_mb\":1024,\"region\":\"us-east-2\",\"timeout_sec\":1}}`),\n\t\t},\n\t\t{\n\t\t\tname:          \"gcp mem, region, and timeout with omit fields\",\n\t\t\tomitEnvFields: true,\n\t\t\tenv: map[string]string{\n\t\t\t\t\"K_SERVICE\":            \"servicename\",\n\t\t\t\t\"FUNCTION_TIMEOUT_SEC\": \"1\",\n\t\t\t\t\"FUNCTION_REGION\":      \"us-east-2\",\n\t\t\t\t\"FUNCTION_MEMORY_MB\":   \"1024\",\n\t\t\t},\n\t\t\twant: []byte(`{\"env\":{\"name\":\"gcp.func\"}}`),\n\t\t},\n\t\t{\n\t\t\tname: \"vercel only\",\n\t\t\tenv: map[string]string{\n\t\t\t\t\"VERCEL\": \"1\",\n\t\t\t},\n\t\t\twant: []byte(`{\"env\":{\"name\":\"vercel\"}}`),\n\t\t},\n\t\t{\n\t\t\tname: \"vercel region\",\n\t\t\tenv: map[string]string{\n\t\t\t\t\"VERCEL\":        \"1\",\n\t\t\t\t\"VERCEL_REGION\": \"us-east-2\",\n\t\t\t},\n\t\t\twant: []byte(`{\"env\":{\"name\":\"vercel\",\"region\":\"us-east-2\"}}`),\n\t\t},\n\t\t{\n\t\t\tname: \"azure only\",\n\t\t\tenv: map[string]string{\n\t\t\t\t\"FUNCTIONS_WORKER_RUNTIME\": \"go1.x\",\n\t\t\t},\n\t\t\twant: []byte(`{\"env\":{\"name\":\"azure.func\"}}`),\n\t\t},\n\t\t{\n\t\t\tname: \"k8s\",\n\t\t\tenv: map[string]string{\n\t\t\t\t\"KUBERNETES_SERVICE_HOST\": \"0.0.0.0\",\n\t\t\t},\n\t\t\twant: []byte(`{\"env\":{\"container\":{\"orchestrator\":\"kubernetes\"}}}`),\n\t\t},\n\t\t// client.env.container.runtime is untested.\n\t}\n\n\tfor _, test := range tests {\n\t\ttest := test\n\n\t\tt.Run(test.name, func(t *testing.T) {\n\t\t\tfor key, val := range test.env {\n\t\t\t\tt.Setenv(key, val)\n\t\t\t}\n\n\t\t\tcb := func(_ int, dst []byte) ([]byte, error) {\n\t\t\t\tvar err error\n\t\t\t\tdst, err = appendClientEnv(dst, test.omitEnvFields, false)\n\n\t\t\t\treturn dst, err\n\t\t\t}\n\n\t\t\tgot := encodeWithCallback(t, cb)\n\t\t\tassertDocsEqual(t, got, test.want)\n\t\t})\n\t}\n}\n\nfunc TestAppendClientOS(t *testing.T) {\n\tt.Parallel()\n\n\ttests := []struct {\n\t\tname        string\n\t\tomitNonType bool\n\t\twant        []byte // Extended JSON\n\t}{\n\t\t{\n\t\t\tname: \"full\",\n\t\t\twant: []byte(fmt.Sprintf(`{\"os\":{\"type\":%q,\"architecture\":%q}}`, runtime.GOOS, runtime.GOARCH)),\n\t\t},\n\t\t{\n\t\t\tname:        \"partial\",\n\t\t\tomitNonType: true,\n\t\t\twant:        []byte(fmt.Sprintf(`{\"os\":{\"type\":%q}}`, runtime.GOOS)),\n\t\t},\n\t}\n\n\tfor _, test := range tests {\n\t\ttest := test\n\n\t\tt.Run(test.name, func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tcb := func(_ int, dst []byte) ([]byte, error) {\n\t\t\t\tvar err error\n\t\t\t\tdst, err = appendClientOS(dst, test.omitNonType)\n\n\t\t\t\treturn dst, err\n\t\t\t}\n\n\t\t\tgot := encodeWithCallback(t, cb)\n\t\t\tassertDocsEqual(t, got, test.want)\n\t\t})\n\t}\n}\n\nfunc TestAppendClientPlatform(t *testing.T) {\n\tt.Parallel()\n\n\ttests := []struct {\n\t\tname                 string\n\t\touterLibraryPlatform string\n\t\twant                 []byte // Extended JSON\n\t}{\n\t\t{\n\t\t\tname:                 \"full\",\n\t\t\touterLibraryPlatform: \"\",\n\t\t\twant:                 []byte(fmt.Sprintf(`{\"platform\":%q}`, runtime.Version())),\n\t\t},\n\t\t{\n\t\t\tname:                 \"with outer library data\",\n\t\t\touterLibraryPlatform: \"outer-library-platform\",\n\t\t\twant:                 []byte(fmt.Sprintf(`{\"platform\":\"%s|outer-library-platform\"}`, runtime.Version())),\n\t\t},\n\t}\n\n\tfor _, test := range tests {\n\t\ttest := test\n\n\t\tt.Run(test.name, func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tcb := func(_ int, dst []byte) ([]byte, error) {\n\t\t\t\tvar err error\n\t\t\t\tdst = appendClientPlatform(dst, test.outerLibraryPlatform)\n\n\t\t\t\treturn dst, err\n\t\t\t}\n\n\t\t\tgot := encodeWithCallback(t, cb)\n\t\t\tassertDocsEqual(t, got, test.want)\n\t\t})\n\t}\n}\n\nfunc TestEncodeClientMetadata(t *testing.T) {\n\tclearTestEnv(t)\n\n\tif os.Getenv(\"DOCKER_RUNNING\") != \"\" {\n\t\tt.Skip(\"These tests gives different results when run in Docker due to extra environment data.\")\n\t}\n\n\ttype application struct {\n\t\tName string `bson:\"name\"`\n\t}\n\n\ttype driver struct {\n\t\tName    string `bson:\"name\"`\n\t\tVersion string `bson:\"version\"`\n\t}\n\n\ttype dist struct {\n\t\tType         string `bson:\"type,omitempty\"`\n\t\tArchitecture string `bson:\"architecture,omitempty\"`\n\t}\n\n\ttype container struct {\n\t\tRuntime      string `bson:\"runtime,omitempty\"`\n\t\tOrchestrator string `bson:\"orchestrator,omitempty\"`\n\t}\n\n\ttype env struct {\n\t\tName       string     `bson:\"name,omitempty\"`\n\t\tTimeoutSec int64      `bson:\"timeout_sec,omitempty\"`\n\t\tMemoryMB   int32      `bson:\"memory_mb,omitempty\"`\n\t\tRegion     string     `bson:\"region,omitempty\"`\n\t\tContainer  *container `bson:\"container,omitempty\"`\n\t}\n\n\ttype clientMetadata struct {\n\t\tApplication *application `bson:\"application\"`\n\t\tDriver      *driver      `bson:\"driver\"`\n\t\tOS          *dist        `bson:\"os\"`\n\t\tPlatform    string       `bson:\"platform,omitempty\"`\n\t\tEnv         *env         `bson:\"env,omitempty\"`\n\t}\n\n\tformatJSON := func(client *clientMetadata) []byte {\n\t\tbytes, err := bson.MarshalExtJSON(client, true, false)\n\t\trequire.NoError(t, err, \"error encoding client metadata for test: %v\", err)\n\n\t\treturn bytes\n\t}\n\n\t// Set environment variables to add `env` field to handshake.\n\tt.Setenv(\"AWS_LAMBDA_RUNTIME_API\", \"lambda\")\n\tt.Setenv(\"AWS_LAMBDA_FUNCTION_MEMORY_SIZE\", \"123\")\n\tt.Setenv(\"AWS_REGION\", \"us-east-2\")\n\tt.Setenv(\"KUBERNETES_SERVICE_HOST\", \"0.0.0.0\")\n\n\tt.Run(\"nothing is omitted\", func(t *testing.T) {\n\t\tgot, err := encodeClientMetadata(NewHello().AppName(\"foo\"), maxClientMetadataSize)\n\t\tassert.Nil(t, err, \"error in encodeClientMetadata: %v\", err)\n\n\t\twant := formatJSON(&clientMetadata{\n\t\t\tApplication: &application{Name: \"foo\"},\n\t\t\tDriver:      &driver{Name: driverName, Version: version.Driver},\n\t\t\tOS:          &dist{Type: runtime.GOOS, Architecture: runtime.GOARCH},\n\t\t\tPlatform:    runtime.Version(),\n\t\t\tEnv: &env{\n\t\t\t\tName:     \"aws.lambda\",\n\t\t\t\tMemoryMB: 123,\n\t\t\t\tRegion:   \"us-east-2\",\n\t\t\t\tContainer: &container{\n\t\t\t\t\tOrchestrator: \"kubernetes\",\n\t\t\t\t},\n\t\t\t},\n\t\t})\n\n\t\tassertDocsEqual(t, got, want)\n\t})\n\n\tt.Run(\"env is omitted sub env.name\", func(t *testing.T) {\n\t\t// Calculate the full length of a bsoncore.Document.\n\t\ttemp, err := encodeClientMetadata(NewHello().AppName(\"foo\"), maxClientMetadataSize)\n\t\trequire.NoError(t, err, \"error constructing template: %v\", err)\n\n\t\tgot, err := encodeClientMetadata(NewHello().AppName(\"foo\"), len(temp)-1)\n\t\tassert.Nil(t, err, \"error in encodeClientMetadata: %v\", err)\n\n\t\twant := formatJSON(&clientMetadata{\n\t\t\tApplication: &application{Name: \"foo\"},\n\t\t\tDriver:      &driver{Name: driverName, Version: version.Driver},\n\t\t\tOS:          &dist{Type: runtime.GOOS, Architecture: runtime.GOARCH},\n\t\t\tPlatform:    runtime.Version(),\n\t\t\tEnv: &env{\n\t\t\t\tName: \"aws.lambda\",\n\t\t\t\tContainer: &container{\n\t\t\t\t\tOrchestrator: \"kubernetes\",\n\t\t\t\t},\n\t\t\t},\n\t\t})\n\n\t\tassertDocsEqual(t, got, want)\n\t})\n\n\tt.Run(\"os is omitted sub os.type\", func(t *testing.T) {\n\t\t// Calculate the full length of a bsoncore.Document.\n\t\ttemp, err := encodeClientMetadata(NewHello().AppName(\"foo\"), maxClientMetadataSize)\n\t\trequire.NoError(t, err, \"error constructing template: %v\", err)\n\n\t\t// Calculate what the environment costs.\n\t\tedst, err := appendClientEnv(nil, false, false)\n\t\trequire.NoError(t, err, \"error constructing env template: %v\", err)\n\n\t\t// Calculate what the env.name costs.\n\t\tndst := bsoncore.AppendStringElement(nil, \"name\", \"aws.lambda\")\n\t\tidx, ndst := bsoncore.AppendDocumentElementStart(ndst, \"container\")\n\t\tndst = bsoncore.AppendStringElement(ndst, \"orchestrator\", \"kubernetes\")\n\t\tndst, err = bsoncore.AppendDocumentEnd(ndst, idx)\n\t\trequire.NoError(t, err)\n\n\t\t// Environment sub name.\n\t\tenvSubName := len(edst) - len(ndst)\n\n\t\tgot, err := encodeClientMetadata(NewHello().AppName(\"foo\"), len(temp)-envSubName-1)\n\t\tassert.Nil(t, err, \"error in encodeClientMetadata: %v\", err)\n\n\t\twant := formatJSON(&clientMetadata{\n\t\t\tApplication: &application{Name: \"foo\"},\n\t\t\tDriver:      &driver{Name: driverName, Version: version.Driver},\n\t\t\tOS:          &dist{Type: runtime.GOOS},\n\t\t\tPlatform:    runtime.Version(),\n\t\t\tEnv: &env{\n\t\t\t\tName: \"aws.lambda\",\n\t\t\t\tContainer: &container{\n\t\t\t\t\tOrchestrator: \"kubernetes\",\n\t\t\t\t},\n\t\t\t},\n\t\t})\n\n\t\tassertDocsEqual(t, got, want)\n\t})\n\n\tt.Run(\"omit the env doc entirely\", func(t *testing.T) {\n\t\t// Calculate the full length of a bsoncore.Document.\n\t\ttemp, err := encodeClientMetadata(NewHello().AppName(\"foo\"), maxClientMetadataSize)\n\t\trequire.NoError(t, err, \"error constructing template: %v\", err)\n\n\t\t// Calculate what the environment costs.\n\t\tedst, err := appendClientEnv(nil, false, false)\n\t\trequire.NoError(t, err, \"error constructing env template: %v\", err)\n\n\t\t// Calculate what the os.type costs.\n\t\todst := bsoncore.AppendStringElement(nil, \"type\", runtime.GOOS)\n\n\t\t// Calculate what the environment plus the os.type costs.\n\t\tenvAndOSType := len(edst) + len(odst)\n\n\t\tgot, err := encodeClientMetadata(NewHello().AppName(\"foo\"), len(temp)-envAndOSType-1)\n\t\tassert.Nil(t, err, \"error in encodeClientMetadata: %v\", err)\n\n\t\twant := formatJSON(&clientMetadata{\n\t\t\tApplication: &application{Name: \"foo\"},\n\t\t\tDriver:      &driver{Name: driverName, Version: version.Driver},\n\t\t\tOS:          &dist{Type: runtime.GOOS},\n\t\t\tPlatform:    runtime.Version(),\n\t\t})\n\n\t\tassertDocsEqual(t, got, want)\n\t})\n\n\tt.Run(\"omit the platform\", func(t *testing.T) {\n\t\t// Calculate the full length of a bsoncore.Document.\n\t\ttemp, err := encodeClientMetadata(NewHello().AppName(\"foo\"), maxClientMetadataSize)\n\t\trequire.NoError(t, err, \"error constructing template: %v\", err)\n\n\t\t// Calculate what the environment costs.\n\t\tedst, err := appendClientEnv(nil, false, false)\n\t\trequire.NoError(t, err, \"error constructing env template: %v\", err)\n\n\t\t// Calculate what the os.type costs.\n\t\todst := bsoncore.AppendStringElement(nil, \"type\", runtime.GOOS)\n\n\t\t// Calculate what the platform costs\n\t\tpdst := appendClientPlatform(nil, \"\")\n\n\t\t// Calculate what the environment plus the os.type costs.\n\t\tenvAndOSTypeAndPlatform := len(edst) + len(odst) + len(pdst)\n\n\t\tgot, err := encodeClientMetadata(NewHello().AppName(\"foo\"), len(temp)-envAndOSTypeAndPlatform)\n\t\tassert.Nil(t, err, \"error in encodeClientMetadata: %v\", err)\n\n\t\twant := formatJSON(&clientMetadata{\n\t\t\tApplication: &application{Name: \"foo\"},\n\t\t\tDriver:      &driver{Name: driverName, Version: version.Driver},\n\t\t\tOS:          &dist{Type: runtime.GOOS},\n\t\t})\n\n\t\tassertDocsEqual(t, got, want)\n\t})\n\n\tt.Run(\"0 max len\", func(t *testing.T) {\n\t\tgot, err := encodeClientMetadata(NewHello().AppName(\"foo\"), 0)\n\t\tassert.Nil(t, err, \"error in encodeClientMetadata: %v\", err)\n\t\tassert.Len(t, got, 0)\n\t})\n}\n\nfunc TestParseFaasEnvName(t *testing.T) {\n\tclearTestEnv(t)\n\n\ttests := []struct {\n\t\tname string\n\t\tenv  map[string]string\n\t\twant string\n\t}{\n\t\t{\n\t\t\tname: \"no env\",\n\t\t\twant: \"\",\n\t\t},\n\t\t{\n\t\t\tname: \"one aws\",\n\t\t\tenv: map[string]string{\n\t\t\t\t\"AWS_EXECUTION_ENV\": \"AWS_Lambda_foo\",\n\t\t\t},\n\t\t\twant: \"aws.lambda\",\n\t\t},\n\t\t{\n\t\t\tname: \"both aws options\",\n\t\t\tenv: map[string]string{\n\t\t\t\t\"AWS_EXECUTION_ENV\":      \"AWS_Lambda_foo\",\n\t\t\t\t\"AWS_LAMBDA_RUNTIME_API\": \"hello\",\n\t\t\t},\n\t\t\twant: \"aws.lambda\",\n\t\t},\n\t\t{\n\t\t\tname: \"multiple variables\",\n\t\t\tenv: map[string]string{\n\t\t\t\t\"AWS_EXECUTION_ENV\":        \"AWS_Lambda_foo\",\n\t\t\t\t\"FUNCTIONS_WORKER_RUNTIME\": \"hello\",\n\t\t\t},\n\t\t\twant: \"\",\n\t\t},\n\t\t{\n\t\t\tname: \"vercel and aws lambda\",\n\t\t\tenv: map[string]string{\n\t\t\t\t\"AWS_EXECUTION_ENV\": \"AWS_Lambda_foo\",\n\t\t\t\t\"VERCEL\":            \"hello\",\n\t\t\t},\n\t\t\twant: \"vercel\",\n\t\t},\n\t\t{\n\t\t\tname: \"invalid aws prefix\",\n\t\t\tenv: map[string]string{\n\t\t\t\t\"AWS_EXECUTION_ENV\": \"foo\",\n\t\t\t},\n\t\t\twant: \"\",\n\t\t},\n\t}\n\n\tfor _, test := range tests {\n\t\ttest := test\n\n\t\tt.Run(test.name, func(t *testing.T) {\n\t\t\tfor key, value := range test.env {\n\t\t\t\tt.Setenv(key, value)\n\t\t\t}\n\n\t\t\tgot := driverutil.GetFaasEnvName()\n\t\t\tif got != test.want {\n\t\t\t\tt.Errorf(\"parseFaasEnvName(%s) = %s, want %s\", test.name, got, test.want)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc BenchmarkClientMetadata(b *testing.B) {\n\tb.ReportAllocs()\n\tb.ResetTimer()\n\n\tb.RunParallel(func(pb *testing.PB) {\n\t\tfor pb.Next() {\n\t\t\t_, err := encodeClientMetadata(NewHello().AppName(\"foo\"), maxClientMetadataSize)\n\t\t\tif err != nil {\n\t\t\t\tb.Fatal(err)\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc BenchmarkClientMetadtaLargeEnv(b *testing.B) {\n\tb.ReportAllocs()\n\tb.ResetTimer()\n\n\tb.Setenv(\"aws.lambda\", \"foo\")\n\n\tstr := \"\"\n\tfor i := 0; i < 512; i++ {\n\t\tstr += \"a\"\n\t}\n\n\tb.Setenv(\"AWS_LAMBDA_RUNTIME_API\", str)\n\n\tb.RunParallel(func(pb *testing.PB) {\n\t\tfor pb.Next() {\n\t\t\t_, err := encodeClientMetadata(NewHello().AppName(\"foo\"), maxClientMetadataSize)\n\t\t\tif err != nil {\n\t\t\t\tb.Fatal(err)\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc FuzzEncodeClientMetadata(f *testing.F) {\n\tf.Fuzz(func(t *testing.T, b []byte, appname string) {\n\t\tif len(b) > maxClientMetadataSize {\n\t\t\treturn\n\t\t}\n\n\t\t_, err := encodeClientMetadata(NewHello().AppName(appname), maxClientMetadataSize)\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"error appending client: %v\", err)\n\t\t}\n\n\t\t_, err = appendClientAppName(b, appname)\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"error appending client app name: %v\", err)\n\t\t}\n\n\t\t_, err = appendClientDriver(b, \"\", \"\")\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"error appending client driver: %v\", err)\n\t\t}\n\n\t\t_, err = appendClientEnv(b, false, false)\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"error appending client env ff: %v\", err)\n\t\t}\n\n\t\t_, err = appendClientEnv(b, false, true)\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"error appending client env ft: %v\", err)\n\t\t}\n\n\t\t_, err = appendClientEnv(b, true, false)\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"error appending client env tf: %v\", err)\n\t\t}\n\n\t\t_, err = appendClientEnv(b, true, true)\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"error appending client env tt: %v\", err)\n\t\t}\n\n\t\t_, err = appendClientOS(b, false)\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"error appending client os f: %v\", err)\n\t\t}\n\n\t\t_, err = appendClientOS(b, true)\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"error appending client os t: %v\", err)\n\t\t}\n\n\t\tappendClientPlatform(b, \"\")\n\t})\n}\n"
  },
  {
    "path": "x/mongo/driver/operation/list_collections.go",
    "content": "// Copyright (C) MongoDB, Inc. 2019-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage operation\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/event\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/driverutil\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/readpref\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/description\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/session\"\n)\n\n// ListCollections performs a listCollections operation.\ntype ListCollections struct {\n\tauthenticator         driver.Authenticator\n\tfilter                bsoncore.Document\n\tnameOnly              *bool\n\tauthorizedCollections *bool\n\tsession               *session.Client\n\tclock                 *session.ClusterClock\n\tmonitor               *event.CommandMonitor\n\tcrypt                 driver.Crypt\n\tdatabase              string\n\tdeployment            driver.Deployment\n\treadPreference        *readpref.ReadPref\n\tselector              description.ServerSelector\n\tretry                 *driver.RetryMode\n\tresult                driver.CursorResponse\n\tbatchSize             *int32\n\tserverAPI             *driver.ServerAPIOptions\n\ttimeout               *time.Duration\n\trawData               *bool\n}\n\n// NewListCollections constructs and returns a new ListCollections.\nfunc NewListCollections(filter bsoncore.Document) *ListCollections {\n\treturn &ListCollections{\n\t\tfilter: filter,\n\t}\n}\n\n// Result returns the result of executing this operation.\nfunc (lc *ListCollections) Result(opts driver.CursorOptions) (*driver.BatchCursor, error) {\n\topts.ServerAPI = lc.serverAPI\n\n\treturn driver.NewBatchCursor(lc.result, lc.session, lc.clock, opts)\n}\n\nfunc (lc *ListCollections) processResponse(_ context.Context, resp bsoncore.Document, info driver.ResponseInfo) error {\n\tcurDoc, err := driver.ExtractCursorDocument(resp)\n\tif err != nil {\n\t\treturn err\n\t}\n\tlc.result, err = driver.NewCursorResponse(curDoc, info)\n\treturn err\n}\n\n// Execute runs this operations and returns an error if the operation did not execute successfully.\nfunc (lc *ListCollections) Execute(ctx context.Context) error {\n\tif lc.deployment == nil {\n\t\treturn errors.New(\"the ListCollections operation must have a Deployment set before Execute can be called\")\n\t}\n\n\treturn driver.Operation{\n\t\tCommandFn:         lc.command,\n\t\tProcessResponseFn: lc.processResponse,\n\t\tRetryMode:         lc.retry,\n\t\tType:              driver.Read,\n\t\tClient:            lc.session,\n\t\tClock:             lc.clock,\n\t\tCommandMonitor:    lc.monitor,\n\t\tCrypt:             lc.crypt,\n\t\tDatabase:          lc.database,\n\t\tDeployment:        lc.deployment,\n\t\tReadPreference:    lc.readPreference,\n\t\tSelector:          lc.selector,\n\t\tLegacy:            driver.LegacyListCollections,\n\t\tServerAPI:         lc.serverAPI,\n\t\tTimeout:           lc.timeout,\n\t\tName:              driverutil.ListCollectionsOp,\n\t\tAuthenticator:     lc.authenticator,\n\t}.Execute(ctx)\n}\n\nfunc (lc *ListCollections) command(dst []byte, desc description.SelectedServer) ([]byte, error) {\n\tdst = bsoncore.AppendInt32Element(dst, \"listCollections\", 1)\n\tif lc.filter != nil {\n\t\tdst = bsoncore.AppendDocumentElement(dst, \"filter\", lc.filter)\n\t}\n\tif lc.nameOnly != nil {\n\t\tdst = bsoncore.AppendBooleanElement(dst, \"nameOnly\", *lc.nameOnly)\n\t}\n\tif lc.authorizedCollections != nil {\n\t\tdst = bsoncore.AppendBooleanElement(dst, \"authorizedCollections\", *lc.authorizedCollections)\n\t}\n\n\tcursorDoc := bsoncore.NewDocumentBuilder()\n\tif lc.batchSize != nil {\n\t\tcursorDoc.AppendInt32(\"batchSize\", *lc.batchSize)\n\t}\n\tdst = bsoncore.AppendDocumentElement(dst, \"cursor\", cursorDoc.Build())\n\n\t// Set rawData for 8.2+ servers.\n\tif lc.rawData != nil && desc.WireVersion != nil && driverutil.VersionRangeIncludes(*desc.WireVersion, 27) {\n\t\tdst = bsoncore.AppendBooleanElement(dst, \"rawData\", *lc.rawData)\n\t}\n\n\treturn dst, nil\n}\n\n// Filter determines what results are returned from listCollections.\nfunc (lc *ListCollections) Filter(filter bsoncore.Document) *ListCollections {\n\tif lc == nil {\n\t\tlc = new(ListCollections)\n\t}\n\n\tlc.filter = filter\n\treturn lc\n}\n\n// NameOnly specifies whether to only return collection names.\nfunc (lc *ListCollections) NameOnly(nameOnly bool) *ListCollections {\n\tif lc == nil {\n\t\tlc = new(ListCollections)\n\t}\n\n\tlc.nameOnly = &nameOnly\n\treturn lc\n}\n\n// AuthorizedCollections specifies whether to only return collections the user\n// is authorized to use.\nfunc (lc *ListCollections) AuthorizedCollections(authorizedCollections bool) *ListCollections {\n\tif lc == nil {\n\t\tlc = new(ListCollections)\n\t}\n\n\tlc.authorizedCollections = &authorizedCollections\n\treturn lc\n}\n\n// Session sets the session for this operation.\nfunc (lc *ListCollections) Session(session *session.Client) *ListCollections {\n\tif lc == nil {\n\t\tlc = new(ListCollections)\n\t}\n\n\tlc.session = session\n\treturn lc\n}\n\n// ClusterClock sets the cluster clock for this operation.\nfunc (lc *ListCollections) ClusterClock(clock *session.ClusterClock) *ListCollections {\n\tif lc == nil {\n\t\tlc = new(ListCollections)\n\t}\n\n\tlc.clock = clock\n\treturn lc\n}\n\n// CommandMonitor sets the monitor to use for APM events.\nfunc (lc *ListCollections) CommandMonitor(monitor *event.CommandMonitor) *ListCollections {\n\tif lc == nil {\n\t\tlc = new(ListCollections)\n\t}\n\n\tlc.monitor = monitor\n\treturn lc\n}\n\n// Crypt sets the Crypt object to use for automatic encryption and decryption.\nfunc (lc *ListCollections) Crypt(crypt driver.Crypt) *ListCollections {\n\tif lc == nil {\n\t\tlc = new(ListCollections)\n\t}\n\n\tlc.crypt = crypt\n\treturn lc\n}\n\n// Database sets the database to run this operation against.\nfunc (lc *ListCollections) Database(database string) *ListCollections {\n\tif lc == nil {\n\t\tlc = new(ListCollections)\n\t}\n\n\tlc.database = database\n\treturn lc\n}\n\n// Deployment sets the deployment to use for this operation.\nfunc (lc *ListCollections) Deployment(deployment driver.Deployment) *ListCollections {\n\tif lc == nil {\n\t\tlc = new(ListCollections)\n\t}\n\n\tlc.deployment = deployment\n\treturn lc\n}\n\n// ReadPreference set the read preference used with this operation.\nfunc (lc *ListCollections) ReadPreference(readPreference *readpref.ReadPref) *ListCollections {\n\tif lc == nil {\n\t\tlc = new(ListCollections)\n\t}\n\n\tlc.readPreference = readPreference\n\treturn lc\n}\n\n// ServerSelector sets the selector used to retrieve a server.\nfunc (lc *ListCollections) ServerSelector(selector description.ServerSelector) *ListCollections {\n\tif lc == nil {\n\t\tlc = new(ListCollections)\n\t}\n\n\tlc.selector = selector\n\treturn lc\n}\n\n// Retry enables retryable mode for this operation. Retries are handled automatically in driver.Operation.Execute based\n// on how the operation is set.\nfunc (lc *ListCollections) Retry(retry driver.RetryMode) *ListCollections {\n\tif lc == nil {\n\t\tlc = new(ListCollections)\n\t}\n\n\tlc.retry = &retry\n\treturn lc\n}\n\n// BatchSize specifies the number of documents to return in every batch.\nfunc (lc *ListCollections) BatchSize(batchSize int32) *ListCollections {\n\tif lc == nil {\n\t\tlc = new(ListCollections)\n\t}\n\n\tlc.batchSize = &batchSize\n\treturn lc\n}\n\n// ServerAPI sets the server API version for this operation.\nfunc (lc *ListCollections) ServerAPI(serverAPI *driver.ServerAPIOptions) *ListCollections {\n\tif lc == nil {\n\t\tlc = new(ListCollections)\n\t}\n\n\tlc.serverAPI = serverAPI\n\treturn lc\n}\n\n// Timeout sets the timeout for this operation.\nfunc (lc *ListCollections) Timeout(timeout *time.Duration) *ListCollections {\n\tif lc == nil {\n\t\tlc = new(ListCollections)\n\t}\n\n\tlc.timeout = timeout\n\treturn lc\n}\n\n// Authenticator sets the authenticator to use for this operation.\nfunc (lc *ListCollections) Authenticator(authenticator driver.Authenticator) *ListCollections {\n\tif lc == nil {\n\t\tlc = new(ListCollections)\n\t}\n\n\tlc.authenticator = authenticator\n\treturn lc\n}\n\n// RawData sets the rawData to access timeseries data in the compressed format.\nfunc (lc *ListCollections) RawData(rawData bool) *ListCollections {\n\tif lc == nil {\n\t\tlc = new(ListCollections)\n\t}\n\n\tlc.rawData = &rawData\n\treturn lc\n}\n"
  },
  {
    "path": "x/mongo/driver/operation/list_databases.go",
    "content": "// Copyright (C) MongoDB, Inc. 2019-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage operation\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/event\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/driverutil\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/readpref\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/description\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/session\"\n)\n\n// ListDatabases performs a listDatabases operation.\ntype ListDatabases struct {\n\tauthenticator       driver.Authenticator\n\tfilter              bsoncore.Document\n\tauthorizedDatabases *bool\n\tnameOnly            *bool\n\tsession             *session.Client\n\tclock               *session.ClusterClock\n\tmonitor             *event.CommandMonitor\n\tdatabase            string\n\tdeployment          driver.Deployment\n\treadPreference      *readpref.ReadPref\n\tretry               *driver.RetryMode\n\tselector            description.ServerSelector\n\tcrypt               driver.Crypt\n\tserverAPI           *driver.ServerAPIOptions\n\ttimeout             *time.Duration\n\n\tresult ListDatabasesResult\n}\n\n// ListDatabasesResult represents a listDatabases result returned by the server.\ntype ListDatabasesResult struct {\n\t// An array of documents, one document for each database\n\tDatabases []databaseRecord\n\t// The sum of the size of all the database files on disk in bytes.\n\tTotalSize int64\n}\n\ntype databaseRecord struct {\n\tName       string\n\tSizeOnDisk int64 `bson:\"sizeOnDisk\"`\n\tEmpty      bool\n}\n\nfunc buildListDatabasesResult(response bsoncore.Document) (ListDatabasesResult, error) {\n\telements, err := response.Elements()\n\tif err != nil {\n\t\treturn ListDatabasesResult{}, err\n\t}\n\tir := ListDatabasesResult{}\n\tfor _, element := range elements {\n\t\tswitch element.Key() {\n\t\tcase \"totalSize\":\n\t\t\tvar ok bool\n\t\t\tir.TotalSize, ok = element.Value().AsInt64OK()\n\t\t\tif !ok {\n\t\t\t\treturn ir, fmt.Errorf(\"response field 'totalSize' is type int64, but received BSON type %s: %s\", element.Value().Type, element.Value())\n\t\t\t}\n\t\tcase \"databases\":\n\t\t\tarr, ok := element.Value().ArrayOK()\n\t\t\tif !ok {\n\t\t\t\treturn ir, fmt.Errorf(\"response field 'databases' is type array, but received BSON type %s\", element.Value().Type)\n\t\t\t}\n\n\t\t\tvar tmp bsoncore.Document\n\t\t\terr := bson.Unmarshal(arr, &tmp)\n\t\t\tif err != nil {\n\t\t\t\treturn ir, err\n\t\t\t}\n\n\t\t\trecords, err := tmp.Elements()\n\t\t\tif err != nil {\n\t\t\t\treturn ir, err\n\t\t\t}\n\n\t\t\tir.Databases = make([]databaseRecord, len(records))\n\t\t\tfor i, val := range records {\n\t\t\t\tvalueDoc, ok := val.Value().DocumentOK()\n\t\t\t\tif !ok {\n\t\t\t\t\treturn ir, fmt.Errorf(\"'databases' element is type document, but received BSON type %s\", val.Value().Type)\n\t\t\t\t}\n\n\t\t\t\telems, err := valueDoc.Elements()\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn ir, err\n\t\t\t\t}\n\n\t\t\t\tfor _, elem := range elems {\n\t\t\t\t\tswitch elem.Key() {\n\t\t\t\t\tcase \"name\":\n\t\t\t\t\t\tir.Databases[i].Name, ok = elem.Value().StringValueOK()\n\t\t\t\t\t\tif !ok {\n\t\t\t\t\t\t\treturn ir, fmt.Errorf(\"response field 'name' is type string, but received BSON type %s\", elem.Value().Type)\n\t\t\t\t\t\t}\n\t\t\t\t\tcase \"sizeOnDisk\":\n\t\t\t\t\t\tir.Databases[i].SizeOnDisk, ok = elem.Value().AsInt64OK()\n\t\t\t\t\t\tif !ok {\n\t\t\t\t\t\t\treturn ir, fmt.Errorf(\"response field 'sizeOnDisk' is type int64, but received BSON type %s\", elem.Value().Type)\n\t\t\t\t\t\t}\n\t\t\t\t\tcase \"empty\":\n\t\t\t\t\t\tir.Databases[i].Empty, ok = elem.Value().BooleanOK()\n\t\t\t\t\t\tif !ok {\n\t\t\t\t\t\t\treturn ir, fmt.Errorf(\"response field 'empty' is type bool, but received BSON type %s\", elem.Value().Type)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn ir, nil\n}\n\n// NewListDatabases constructs and returns a new ListDatabases.\nfunc NewListDatabases(filter bsoncore.Document) *ListDatabases {\n\treturn &ListDatabases{\n\t\tfilter: filter,\n\t}\n}\n\n// Result returns the result of executing this operation.\nfunc (ld *ListDatabases) Result() ListDatabasesResult { return ld.result }\n\nfunc (ld *ListDatabases) processResponse(_ context.Context, resp bsoncore.Document, _ driver.ResponseInfo) error {\n\tvar err error\n\n\tld.result, err = buildListDatabasesResult(resp)\n\treturn err\n}\n\n// Execute runs this operations and returns an error if the operation did not execute successfully.\nfunc (ld *ListDatabases) Execute(ctx context.Context) error {\n\tif ld.deployment == nil {\n\t\treturn errors.New(\"the ListDatabases operation must have a Deployment set before Execute can be called\")\n\t}\n\n\treturn driver.Operation{\n\t\tCommandFn:         ld.command,\n\t\tProcessResponseFn: ld.processResponse,\n\n\t\tClient:         ld.session,\n\t\tClock:          ld.clock,\n\t\tCommandMonitor: ld.monitor,\n\t\tDatabase:       ld.database,\n\t\tDeployment:     ld.deployment,\n\t\tReadPreference: ld.readPreference,\n\t\tRetryMode:      ld.retry,\n\t\tType:           driver.Read,\n\t\tSelector:       ld.selector,\n\t\tCrypt:          ld.crypt,\n\t\tServerAPI:      ld.serverAPI,\n\t\tTimeout:        ld.timeout,\n\t\tName:           driverutil.ListDatabasesOp,\n\t\tAuthenticator:  ld.authenticator,\n\t}.Execute(ctx)\n}\n\nfunc (ld *ListDatabases) command(dst []byte, _ description.SelectedServer) ([]byte, error) {\n\tdst = bsoncore.AppendInt32Element(dst, \"listDatabases\", 1)\n\tif ld.filter != nil {\n\t\tdst = bsoncore.AppendDocumentElement(dst, \"filter\", ld.filter)\n\t}\n\tif ld.nameOnly != nil {\n\t\tdst = bsoncore.AppendBooleanElement(dst, \"nameOnly\", *ld.nameOnly)\n\t}\n\tif ld.authorizedDatabases != nil {\n\t\tdst = bsoncore.AppendBooleanElement(dst, \"authorizedDatabases\", *ld.authorizedDatabases)\n\t}\n\n\treturn dst, nil\n}\n\n// Filter determines what results are returned from listDatabases.\nfunc (ld *ListDatabases) Filter(filter bsoncore.Document) *ListDatabases {\n\tif ld == nil {\n\t\tld = new(ListDatabases)\n\t}\n\n\tld.filter = filter\n\treturn ld\n}\n\n// NameOnly specifies whether to only return database names.\nfunc (ld *ListDatabases) NameOnly(nameOnly bool) *ListDatabases {\n\tif ld == nil {\n\t\tld = new(ListDatabases)\n\t}\n\n\tld.nameOnly = &nameOnly\n\treturn ld\n}\n\n// AuthorizedDatabases specifies whether to only return databases which the user is authorized to use.\"\nfunc (ld *ListDatabases) AuthorizedDatabases(authorizedDatabases bool) *ListDatabases {\n\tif ld == nil {\n\t\tld = new(ListDatabases)\n\t}\n\n\tld.authorizedDatabases = &authorizedDatabases\n\treturn ld\n}\n\n// Session sets the session for this operation.\nfunc (ld *ListDatabases) Session(session *session.Client) *ListDatabases {\n\tif ld == nil {\n\t\tld = new(ListDatabases)\n\t}\n\n\tld.session = session\n\treturn ld\n}\n\n// ClusterClock sets the cluster clock for this operation.\nfunc (ld *ListDatabases) ClusterClock(clock *session.ClusterClock) *ListDatabases {\n\tif ld == nil {\n\t\tld = new(ListDatabases)\n\t}\n\n\tld.clock = clock\n\treturn ld\n}\n\n// CommandMonitor sets the monitor to use for APM events.\nfunc (ld *ListDatabases) CommandMonitor(monitor *event.CommandMonitor) *ListDatabases {\n\tif ld == nil {\n\t\tld = new(ListDatabases)\n\t}\n\n\tld.monitor = monitor\n\treturn ld\n}\n\n// Database sets the database to run this operation against.\nfunc (ld *ListDatabases) Database(database string) *ListDatabases {\n\tif ld == nil {\n\t\tld = new(ListDatabases)\n\t}\n\n\tld.database = database\n\treturn ld\n}\n\n// Deployment sets the deployment to use for this operation.\nfunc (ld *ListDatabases) Deployment(deployment driver.Deployment) *ListDatabases {\n\tif ld == nil {\n\t\tld = new(ListDatabases)\n\t}\n\n\tld.deployment = deployment\n\treturn ld\n}\n\n// ReadPreference set the read preference used with this operation.\nfunc (ld *ListDatabases) ReadPreference(readPreference *readpref.ReadPref) *ListDatabases {\n\tif ld == nil {\n\t\tld = new(ListDatabases)\n\t}\n\n\tld.readPreference = readPreference\n\treturn ld\n}\n\n// ServerSelector sets the selector used to retrieve a server.\nfunc (ld *ListDatabases) ServerSelector(selector description.ServerSelector) *ListDatabases {\n\tif ld == nil {\n\t\tld = new(ListDatabases)\n\t}\n\n\tld.selector = selector\n\treturn ld\n}\n\n// Retry enables retryable mode for this operation. Retries are handled automatically in driver.Operation.Execute based\n// on how the operation is set.\nfunc (ld *ListDatabases) Retry(retry driver.RetryMode) *ListDatabases {\n\tif ld == nil {\n\t\tld = new(ListDatabases)\n\t}\n\n\tld.retry = &retry\n\treturn ld\n}\n\n// Crypt sets the Crypt object to use for automatic encryption and decryption.\nfunc (ld *ListDatabases) Crypt(crypt driver.Crypt) *ListDatabases {\n\tif ld == nil {\n\t\tld = new(ListDatabases)\n\t}\n\n\tld.crypt = crypt\n\treturn ld\n}\n\n// ServerAPI sets the server API version for this operation.\nfunc (ld *ListDatabases) ServerAPI(serverAPI *driver.ServerAPIOptions) *ListDatabases {\n\tif ld == nil {\n\t\tld = new(ListDatabases)\n\t}\n\n\tld.serverAPI = serverAPI\n\treturn ld\n}\n\n// Timeout sets the timeout for this operation.\nfunc (ld *ListDatabases) Timeout(timeout *time.Duration) *ListDatabases {\n\tif ld == nil {\n\t\tld = new(ListDatabases)\n\t}\n\n\tld.timeout = timeout\n\treturn ld\n}\n\n// Authenticator sets the authenticator to use for this operation.\nfunc (ld *ListDatabases) Authenticator(authenticator driver.Authenticator) *ListDatabases {\n\tif ld == nil {\n\t\tld = new(ListDatabases)\n\t}\n\n\tld.authenticator = authenticator\n\treturn ld\n}\n"
  },
  {
    "path": "x/mongo/driver/operation/list_indexes.go",
    "content": "// Copyright (C) MongoDB, Inc. 2019-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage operation\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/event\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/driverutil\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/description\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/session\"\n)\n\n// ListIndexes performs a listIndexes operation.\ntype ListIndexes struct {\n\tauthenticator driver.Authenticator\n\tbatchSize     *int32\n\tsession       *session.Client\n\tclock         *session.ClusterClock\n\tcollection    string\n\tmonitor       *event.CommandMonitor\n\tdatabase      string\n\tdeployment    driver.Deployment\n\tselector      description.ServerSelector\n\tretry         *driver.RetryMode\n\tcrypt         driver.Crypt\n\tserverAPI     *driver.ServerAPIOptions\n\ttimeout       *time.Duration\n\trawData       *bool\n\n\tresult driver.CursorResponse\n}\n\n// NewListIndexes constructs and returns a new ListIndexes.\nfunc NewListIndexes() *ListIndexes {\n\treturn &ListIndexes{}\n}\n\n// Result returns the result of executing this operation.\nfunc (li *ListIndexes) Result(opts driver.CursorOptions) (*driver.BatchCursor, error) {\n\tclientSession := li.session\n\n\tclock := li.clock\n\topts.ServerAPI = li.serverAPI\n\treturn driver.NewBatchCursor(li.result, clientSession, clock, opts)\n}\n\nfunc (li *ListIndexes) processResponse(_ context.Context, resp bsoncore.Document, info driver.ResponseInfo) error {\n\tcurDoc, err := driver.ExtractCursorDocument(resp)\n\tif err != nil {\n\t\treturn err\n\t}\n\tli.result, err = driver.NewCursorResponse(curDoc, info)\n\treturn err\n}\n\n// Execute runs this operations and returns an error if the operation did not execute successfully.\nfunc (li *ListIndexes) Execute(ctx context.Context) error {\n\tif li.deployment == nil {\n\t\treturn errors.New(\"the ListIndexes operation must have a Deployment set before Execute can be called\")\n\t}\n\n\treturn driver.Operation{\n\t\tCommandFn:         li.command,\n\t\tProcessResponseFn: li.processResponse,\n\n\t\tClient:         li.session,\n\t\tClock:          li.clock,\n\t\tCommandMonitor: li.monitor,\n\t\tDatabase:       li.database,\n\t\tDeployment:     li.deployment,\n\t\tSelector:       li.selector,\n\t\tCrypt:          li.crypt,\n\t\tLegacy:         driver.LegacyListIndexes,\n\t\tRetryMode:      li.retry,\n\t\tType:           driver.Read,\n\t\tServerAPI:      li.serverAPI,\n\t\tTimeout:        li.timeout,\n\t\tName:           driverutil.ListIndexesOp,\n\t\tAuthenticator:  li.authenticator,\n\t}.Execute(ctx)\n}\n\nfunc (li *ListIndexes) command(dst []byte, desc description.SelectedServer) ([]byte, error) {\n\tdst = bsoncore.AppendStringElement(dst, \"listIndexes\", li.collection)\n\tcursorIdx, cursorDoc := bsoncore.AppendDocumentStart(nil)\n\n\tif li.batchSize != nil {\n\t\tcursorDoc = bsoncore.AppendInt32Element(cursorDoc, \"batchSize\", *li.batchSize)\n\t}\n\tcursorDoc, _ = bsoncore.AppendDocumentEnd(cursorDoc, cursorIdx)\n\tdst = bsoncore.AppendDocumentElement(dst, \"cursor\", cursorDoc)\n\t// Set rawData for 8.2+ servers.\n\tif li.rawData != nil && desc.WireVersion != nil && driverutil.VersionRangeIncludes(*desc.WireVersion, 27) {\n\t\tdst = bsoncore.AppendBooleanElement(dst, \"rawData\", *li.rawData)\n\t}\n\n\treturn dst, nil\n}\n\n// BatchSize specifies the number of documents to return in every batch.\nfunc (li *ListIndexes) BatchSize(batchSize int32) *ListIndexes {\n\tif li == nil {\n\t\tli = new(ListIndexes)\n\t}\n\n\tli.batchSize = &batchSize\n\treturn li\n}\n\n// Session sets the session for this operation.\nfunc (li *ListIndexes) Session(session *session.Client) *ListIndexes {\n\tif li == nil {\n\t\tli = new(ListIndexes)\n\t}\n\n\tli.session = session\n\treturn li\n}\n\n// ClusterClock sets the cluster clock for this operation.\nfunc (li *ListIndexes) ClusterClock(clock *session.ClusterClock) *ListIndexes {\n\tif li == nil {\n\t\tli = new(ListIndexes)\n\t}\n\n\tli.clock = clock\n\treturn li\n}\n\n// Collection sets the collection that this command will run against.\nfunc (li *ListIndexes) Collection(collection string) *ListIndexes {\n\tif li == nil {\n\t\tli = new(ListIndexes)\n\t}\n\n\tli.collection = collection\n\treturn li\n}\n\n// CommandMonitor sets the monitor to use for APM events.\nfunc (li *ListIndexes) CommandMonitor(monitor *event.CommandMonitor) *ListIndexes {\n\tif li == nil {\n\t\tli = new(ListIndexes)\n\t}\n\n\tli.monitor = monitor\n\treturn li\n}\n\n// Database sets the database to run this operation against.\nfunc (li *ListIndexes) Database(database string) *ListIndexes {\n\tif li == nil {\n\t\tli = new(ListIndexes)\n\t}\n\n\tli.database = database\n\treturn li\n}\n\n// Deployment sets the deployment to use for this operation.\nfunc (li *ListIndexes) Deployment(deployment driver.Deployment) *ListIndexes {\n\tif li == nil {\n\t\tli = new(ListIndexes)\n\t}\n\n\tli.deployment = deployment\n\treturn li\n}\n\n// ServerSelector sets the selector used to retrieve a server.\nfunc (li *ListIndexes) ServerSelector(selector description.ServerSelector) *ListIndexes {\n\tif li == nil {\n\t\tli = new(ListIndexes)\n\t}\n\n\tli.selector = selector\n\treturn li\n}\n\n// Retry enables retryable mode for this operation. Retries are handled automatically in driver.Operation.Execute based\n// on how the operation is set.\nfunc (li *ListIndexes) Retry(retry driver.RetryMode) *ListIndexes {\n\tif li == nil {\n\t\tli = new(ListIndexes)\n\t}\n\n\tli.retry = &retry\n\treturn li\n}\n\n// Crypt sets the Crypt object to use for automatic encryption and decryption.\nfunc (li *ListIndexes) Crypt(crypt driver.Crypt) *ListIndexes {\n\tif li == nil {\n\t\tli = new(ListIndexes)\n\t}\n\n\tli.crypt = crypt\n\treturn li\n}\n\n// ServerAPI sets the server API version for this operation.\nfunc (li *ListIndexes) ServerAPI(serverAPI *driver.ServerAPIOptions) *ListIndexes {\n\tif li == nil {\n\t\tli = new(ListIndexes)\n\t}\n\n\tli.serverAPI = serverAPI\n\treturn li\n}\n\n// Timeout sets the timeout for this operation.\nfunc (li *ListIndexes) Timeout(timeout *time.Duration) *ListIndexes {\n\tif li == nil {\n\t\tli = new(ListIndexes)\n\t}\n\n\tli.timeout = timeout\n\treturn li\n}\n\n// Authenticator sets the authenticator to use for this operation.\nfunc (li *ListIndexes) Authenticator(authenticator driver.Authenticator) *ListIndexes {\n\tif li == nil {\n\t\tli = new(ListIndexes)\n\t}\n\n\tli.authenticator = authenticator\n\treturn li\n}\n\n// RawData sets the rawData to access timeseries data in the compressed format.\nfunc (li *ListIndexes) RawData(rawData bool) *ListIndexes {\n\tif li == nil {\n\t\tli = new(ListIndexes)\n\t}\n\n\tli.rawData = &rawData\n\treturn li\n}\n"
  },
  {
    "path": "x/mongo/driver/operation/update.go",
    "content": "// Copyright (C) MongoDB, Inc. 2019-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage operation\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/event\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/driverutil\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/logger\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/writeconcern\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/description\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/session\"\n)\n\n// Update performs an update operation.\ntype Update struct {\n\tauthenticator            driver.Authenticator\n\tbypassDocumentValidation *bool\n\tcomment                  bsoncore.Value\n\tordered                  *bool\n\tupdates                  []bsoncore.Document\n\tsession                  *session.Client\n\tclock                    *session.ClusterClock\n\tcollection               string\n\tmonitor                  *event.CommandMonitor\n\tdatabase                 string\n\tdeployment               driver.Deployment\n\thint                     *bool\n\tarrayFilters             *bool\n\tselector                 description.ServerSelector\n\twriteConcern             *writeconcern.WriteConcern\n\tretry                    *driver.RetryMode\n\tresult                   UpdateResult\n\tcrypt                    driver.Crypt\n\tserverAPI                *driver.ServerAPIOptions\n\tlet                      bsoncore.Document\n\ttimeout                  *time.Duration\n\trawData                  *bool\n\tadditionalCmd            bson.D\n\tlogger                   *logger.Logger\n}\n\n// Upsert contains the information for an upsert in an Update operation.\ntype Upsert struct {\n\tIndex int64\n\tID    any `bson:\"_id\"`\n}\n\n// UpdateResult contains information for the result of an Update operation.\ntype UpdateResult struct {\n\t// Number of documents matched.\n\tN int64\n\t// Number of documents modified.\n\tNModified int64\n\t// Information about upserted documents.\n\tUpserted []Upsert\n}\n\nfunc buildUpdateResult(response bsoncore.Document) (UpdateResult, error) {\n\telements, err := response.Elements()\n\tif err != nil {\n\t\treturn UpdateResult{}, err\n\t}\n\tur := UpdateResult{}\n\tfor _, element := range elements {\n\t\tswitch element.Key() {\n\t\tcase \"nModified\":\n\t\t\tvar ok bool\n\t\t\tur.NModified, ok = element.Value().AsInt64OK()\n\t\t\tif !ok {\n\t\t\t\treturn ur, fmt.Errorf(\"response field 'nModified' is type int32 or int64, but received BSON type %s\", element.Value().Type)\n\t\t\t}\n\t\tcase \"n\":\n\t\t\tvar ok bool\n\t\t\tur.N, ok = element.Value().AsInt64OK()\n\t\t\tif !ok {\n\t\t\t\treturn ur, fmt.Errorf(\"response field 'n' is type int32 or int64, but received BSON type %s\", element.Value().Type)\n\t\t\t}\n\t\tcase \"upserted\":\n\t\t\tarr, ok := element.Value().ArrayOK()\n\t\t\tif !ok {\n\t\t\t\treturn ur, fmt.Errorf(\"response field 'upserted' is type array, but received BSON type %s\", element.Value().Type)\n\t\t\t}\n\n\t\t\tvar values []bsoncore.Value\n\t\t\tvalues, err = arr.Values()\n\t\t\tif err != nil {\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\tfor _, val := range values {\n\t\t\t\tvalDoc, ok := val.DocumentOK()\n\t\t\t\tif !ok {\n\t\t\t\t\treturn ur, fmt.Errorf(\"upserted value is type document, but received BSON type %s\", val.Type)\n\t\t\t\t}\n\t\t\t\tvar upsert Upsert\n\t\t\t\tif err = bson.Unmarshal(valDoc, &upsert); err != nil {\n\t\t\t\t\treturn ur, err\n\t\t\t\t}\n\t\t\t\tur.Upserted = append(ur.Upserted, upsert)\n\t\t\t}\n\t\t}\n\t}\n\treturn ur, nil\n}\n\n// NewUpdate constructs and returns a new Update.\nfunc NewUpdate(updates ...bsoncore.Document) *Update {\n\treturn &Update{\n\t\tupdates: updates,\n\t}\n}\n\n// Result returns the result of executing this operation.\nfunc (u *Update) Result() UpdateResult { return u.result }\n\nfunc (u *Update) processResponse(_ context.Context, resp bsoncore.Document, info driver.ResponseInfo) error {\n\tur, err := buildUpdateResult(resp)\n\n\tu.result.N += ur.N\n\tu.result.NModified += ur.NModified\n\tif info.CurrentIndex > 0 {\n\t\tfor ind := range ur.Upserted {\n\t\t\tur.Upserted[ind].Index += int64(info.CurrentIndex)\n\t\t}\n\t}\n\tu.result.Upserted = append(u.result.Upserted, ur.Upserted...)\n\treturn err\n}\n\n// Execute runs this operations and returns an error if the operation did not execute successfully.\nfunc (u *Update) Execute(ctx context.Context) error {\n\tif u.deployment == nil {\n\t\treturn errors.New(\"the Update operation must have a Deployment set before Execute can be called\")\n\t}\n\tbatches := &driver.Batches{\n\t\tIdentifier: \"updates\",\n\t\tDocuments:  u.updates,\n\t\tOrdered:    u.ordered,\n\t}\n\n\treturn driver.Operation{\n\t\tCommandFn:         u.command,\n\t\tProcessResponseFn: u.processResponse,\n\t\tBatches:           batches,\n\t\tRetryMode:         u.retry,\n\t\tType:              driver.Write,\n\t\tClient:            u.session,\n\t\tClock:             u.clock,\n\t\tCommandMonitor:    u.monitor,\n\t\tDatabase:          u.database,\n\t\tDeployment:        u.deployment,\n\t\tSelector:          u.selector,\n\t\tWriteConcern:      u.writeConcern,\n\t\tCrypt:             u.crypt,\n\t\tServerAPI:         u.serverAPI,\n\t\tTimeout:           u.timeout,\n\t\tLogger:            u.logger,\n\t\tName:              driverutil.UpdateOp,\n\t\tAuthenticator:     u.authenticator,\n\t}.Execute(ctx)\n}\n\nfunc (u *Update) command(dst []byte, desc description.SelectedServer) ([]byte, error) {\n\tdst = bsoncore.AppendStringElement(dst, \"update\", u.collection)\n\tif u.bypassDocumentValidation != nil &&\n\t\t(desc.WireVersion != nil && driverutil.VersionRangeIncludes(*desc.WireVersion, 4)) {\n\n\t\tdst = bsoncore.AppendBooleanElement(dst, \"bypassDocumentValidation\", *u.bypassDocumentValidation)\n\t}\n\tif u.comment.Type != bsoncore.Type(0) {\n\t\tdst = bsoncore.AppendValueElement(dst, \"comment\", u.comment)\n\t}\n\tif u.ordered != nil {\n\t\tdst = bsoncore.AppendBooleanElement(dst, \"ordered\", *u.ordered)\n\t}\n\tif u.hint != nil && *u.hint {\n\n\t\tif desc.WireVersion == nil || !driverutil.VersionRangeIncludes(*desc.WireVersion, 5) {\n\t\t\treturn nil, errors.New(\"the 'hint' command parameter requires a minimum server wire version of 5\")\n\t\t}\n\t\tif !u.writeConcern.Acknowledged() {\n\t\t\treturn nil, errUnacknowledgedHint\n\t\t}\n\t}\n\tif u.arrayFilters != nil && *u.arrayFilters {\n\t\tif desc.WireVersion == nil || !driverutil.VersionRangeIncludes(*desc.WireVersion, 6) {\n\t\t\treturn nil, errors.New(\"the 'arrayFilters' command parameter requires a minimum server wire version of 6\")\n\t\t}\n\t}\n\tif u.let != nil {\n\t\tdst = bsoncore.AppendDocumentElement(dst, \"let\", u.let)\n\t}\n\t// Set rawData for 8.2+ servers.\n\tif u.rawData != nil && desc.WireVersion != nil && driverutil.VersionRangeIncludes(*desc.WireVersion, 27) {\n\t\tdst = bsoncore.AppendBooleanElement(dst, \"rawData\", *u.rawData)\n\t}\n\tif len(u.additionalCmd) > 0 {\n\t\tdoc, err := bson.Marshal(u.additionalCmd)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tdst = append(dst, doc[4:len(doc)-1]...)\n\t}\n\n\treturn dst, nil\n}\n\n// BypassDocumentValidation allows the operation to opt-out of document level validation.\nfunc (u *Update) BypassDocumentValidation(bypassDocumentValidation bool) *Update {\n\tif u == nil {\n\t\tu = new(Update)\n\t}\n\n\tu.bypassDocumentValidation = &bypassDocumentValidation\n\treturn u\n}\n\n// Hint is a flag to indicate that the update document contains a hint. Hint is only supported by\n// servers >= 4.2. Older servers will report an error for using the hint option.\nfunc (u *Update) Hint(hint bool) *Update {\n\tif u == nil {\n\t\tu = new(Update)\n\t}\n\n\tu.hint = &hint\n\treturn u\n}\n\n// ArrayFilters is a flag to indicate that the update document contains an arrayFilters field.\nfunc (u *Update) ArrayFilters(arrayFilters bool) *Update {\n\tif u == nil {\n\t\tu = new(Update)\n\t}\n\n\tu.arrayFilters = &arrayFilters\n\treturn u\n}\n\n// Ordered sets ordered. If true, when a write fails, the operation will return the error, when\n// false write failures do not stop execution of the operation.\nfunc (u *Update) Ordered(ordered bool) *Update {\n\tif u == nil {\n\t\tu = new(Update)\n\t}\n\n\tu.ordered = &ordered\n\treturn u\n}\n\n// Updates specifies an array of update statements to perform when this operation is executed.\n// Each update document must have the following structure:\n// {q: <query>, u: <update>, multi: <boolean>, collation: Optional<Document>, arrayFitlers: Optional<Array>, hint: Optional<string/Document>}.\nfunc (u *Update) Updates(updates ...bsoncore.Document) *Update {\n\tif u == nil {\n\t\tu = new(Update)\n\t}\n\n\tu.updates = updates\n\treturn u\n}\n\n// Session sets the session for this operation.\nfunc (u *Update) Session(session *session.Client) *Update {\n\tif u == nil {\n\t\tu = new(Update)\n\t}\n\n\tu.session = session\n\treturn u\n}\n\n// ClusterClock sets the cluster clock for this operation.\nfunc (u *Update) ClusterClock(clock *session.ClusterClock) *Update {\n\tif u == nil {\n\t\tu = new(Update)\n\t}\n\n\tu.clock = clock\n\treturn u\n}\n\n// Collection sets the collection that this command will run against.\nfunc (u *Update) Collection(collection string) *Update {\n\tif u == nil {\n\t\tu = new(Update)\n\t}\n\n\tu.collection = collection\n\treturn u\n}\n\n// CommandMonitor sets the monitor to use for APM events.\nfunc (u *Update) CommandMonitor(monitor *event.CommandMonitor) *Update {\n\tif u == nil {\n\t\tu = new(Update)\n\t}\n\n\tu.monitor = monitor\n\treturn u\n}\n\n// Comment sets a value to help trace an operation.\nfunc (u *Update) Comment(comment bsoncore.Value) *Update {\n\tif u == nil {\n\t\tu = new(Update)\n\t}\n\n\tu.comment = comment\n\treturn u\n}\n\n// Database sets the database to run this operation against.\nfunc (u *Update) Database(database string) *Update {\n\tif u == nil {\n\t\tu = new(Update)\n\t}\n\n\tu.database = database\n\treturn u\n}\n\n// Deployment sets the deployment to use for this operation.\nfunc (u *Update) Deployment(deployment driver.Deployment) *Update {\n\tif u == nil {\n\t\tu = new(Update)\n\t}\n\n\tu.deployment = deployment\n\treturn u\n}\n\n// ServerSelector sets the selector used to retrieve a server.\nfunc (u *Update) ServerSelector(selector description.ServerSelector) *Update {\n\tif u == nil {\n\t\tu = new(Update)\n\t}\n\n\tu.selector = selector\n\treturn u\n}\n\n// WriteConcern sets the write concern for this operation.\nfunc (u *Update) WriteConcern(writeConcern *writeconcern.WriteConcern) *Update {\n\tif u == nil {\n\t\tu = new(Update)\n\t}\n\n\tu.writeConcern = writeConcern\n\treturn u\n}\n\n// Retry enables retryable writes for this operation. Retries are not handled automatically,\n// instead a boolean is returned from Execute and SelectAndExecute that indicates if the\n// operation can be retried. Retrying is handled by calling RetryExecute.\nfunc (u *Update) Retry(retry driver.RetryMode) *Update {\n\tif u == nil {\n\t\tu = new(Update)\n\t}\n\n\tu.retry = &retry\n\treturn u\n}\n\n// Crypt sets the Crypt object to use for automatic encryption and decryption.\nfunc (u *Update) Crypt(crypt driver.Crypt) *Update {\n\tif u == nil {\n\t\tu = new(Update)\n\t}\n\n\tu.crypt = crypt\n\treturn u\n}\n\n// ServerAPI sets the server API version for this operation.\nfunc (u *Update) ServerAPI(serverAPI *driver.ServerAPIOptions) *Update {\n\tif u == nil {\n\t\tu = new(Update)\n\t}\n\n\tu.serverAPI = serverAPI\n\treturn u\n}\n\n// Let specifies the let document to use. This option is only valid for server versions 5.0 and above.\nfunc (u *Update) Let(let bsoncore.Document) *Update {\n\tif u == nil {\n\t\tu = new(Update)\n\t}\n\n\tu.let = let\n\treturn u\n}\n\n// Timeout sets the timeout for this operation.\nfunc (u *Update) Timeout(timeout *time.Duration) *Update {\n\tif u == nil {\n\t\tu = new(Update)\n\t}\n\n\tu.timeout = timeout\n\treturn u\n}\n\n// Logger sets the logger for this operation.\nfunc (u *Update) Logger(logger *logger.Logger) *Update {\n\tif u == nil {\n\t\tu = new(Update)\n\t}\n\n\tu.logger = logger\n\treturn u\n}\n\n// Authenticator sets the authenticator to use for this operation.\nfunc (u *Update) Authenticator(authenticator driver.Authenticator) *Update {\n\tif u == nil {\n\t\tu = new(Update)\n\t}\n\n\tu.authenticator = authenticator\n\treturn u\n}\n\n// RawData sets the rawData to access timeseries data in the compressed format.\nfunc (u *Update) RawData(rawData bool) *Update {\n\tif u == nil {\n\t\tu = new(Update)\n\t}\n\n\tu.rawData = &rawData\n\treturn u\n}\n\n// AdditionalCmd sets additional command fields to be attached.\nfunc (u *Update) AdditionalCmd(d bson.D) *Update {\n\tif u == nil {\n\t\tu = new(Update)\n\t}\n\n\tu.additionalCmd = d\n\treturn u\n}\n"
  },
  {
    "path": "x/mongo/driver/operation/update_search_index.go",
    "content": "// Copyright (C) MongoDB, Inc. 2023-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage operation\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/event\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/description\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/session\"\n)\n\n// UpdateSearchIndex performs a updateSearchIndex operation.\ntype UpdateSearchIndex struct {\n\tauthenticator driver.Authenticator\n\tindex         string\n\tdefinition    bsoncore.Document\n\tsession       *session.Client\n\tclock         *session.ClusterClock\n\tcollection    string\n\tmonitor       *event.CommandMonitor\n\tcrypt         driver.Crypt\n\tdatabase      string\n\tdeployment    driver.Deployment\n\tselector      description.ServerSelector\n\tresult        UpdateSearchIndexResult\n\tserverAPI     *driver.ServerAPIOptions\n\ttimeout       *time.Duration\n}\n\n// UpdateSearchIndexResult represents a single index in the updateSearchIndexResult result.\ntype UpdateSearchIndexResult struct {\n\tOk int32\n}\n\nfunc buildUpdateSearchIndexResult(response bsoncore.Document) (UpdateSearchIndexResult, error) {\n\telements, err := response.Elements()\n\tif err != nil {\n\t\treturn UpdateSearchIndexResult{}, err\n\t}\n\tusir := UpdateSearchIndexResult{}\n\tfor _, element := range elements {\n\t\tif element.Key() == \"ok\" {\n\t\t\tvar ok bool\n\t\t\tusir.Ok, ok = element.Value().AsInt32OK()\n\t\t\tif !ok {\n\t\t\t\treturn usir, fmt.Errorf(\"response field 'ok' is type int32, but received BSON type %s\", element.Value().Type)\n\t\t\t}\n\t\t}\n\t}\n\treturn usir, nil\n}\n\n// NewUpdateSearchIndex constructs and returns a new UpdateSearchIndex.\nfunc NewUpdateSearchIndex(index string, definition bsoncore.Document) *UpdateSearchIndex {\n\treturn &UpdateSearchIndex{\n\t\tindex:      index,\n\t\tdefinition: definition,\n\t}\n}\n\n// Result returns the result of executing this operation.\nfunc (usi *UpdateSearchIndex) Result() UpdateSearchIndexResult { return usi.result }\n\nfunc (usi *UpdateSearchIndex) processResponse(_ context.Context, resp bsoncore.Document, _ driver.ResponseInfo) error {\n\tvar err error\n\tusi.result, err = buildUpdateSearchIndexResult(resp)\n\treturn err\n}\n\n// Execute runs this operations and returns an error if the operation did not execute successfully.\nfunc (usi *UpdateSearchIndex) Execute(ctx context.Context) error {\n\tif usi.deployment == nil {\n\t\treturn errors.New(\"the UpdateSearchIndex operation must have a Deployment set before Execute can be called\")\n\t}\n\n\treturn driver.Operation{\n\t\tCommandFn:         usi.command,\n\t\tProcessResponseFn: usi.processResponse,\n\t\tClient:            usi.session,\n\t\tClock:             usi.clock,\n\t\tCommandMonitor:    usi.monitor,\n\t\tCrypt:             usi.crypt,\n\t\tDatabase:          usi.database,\n\t\tDeployment:        usi.deployment,\n\t\tSelector:          usi.selector,\n\t\tServerAPI:         usi.serverAPI,\n\t\tTimeout:           usi.timeout,\n\t\tAuthenticator:     usi.authenticator,\n\t}.Execute(ctx)\n}\n\nfunc (usi *UpdateSearchIndex) command(dst []byte, _ description.SelectedServer) ([]byte, error) {\n\tdst = bsoncore.AppendStringElement(dst, \"updateSearchIndex\", usi.collection)\n\tdst = bsoncore.AppendStringElement(dst, \"name\", usi.index)\n\tdst = bsoncore.AppendDocumentElement(dst, \"definition\", usi.definition)\n\treturn dst, nil\n}\n\n// Index specifies the index of the document being updated.\nfunc (usi *UpdateSearchIndex) Index(name string) *UpdateSearchIndex {\n\tif usi == nil {\n\t\tusi = new(UpdateSearchIndex)\n\t}\n\n\tusi.index = name\n\treturn usi\n}\n\n// Definition specifies the definition for the document being created.\nfunc (usi *UpdateSearchIndex) Definition(definition bsoncore.Document) *UpdateSearchIndex {\n\tif usi == nil {\n\t\tusi = new(UpdateSearchIndex)\n\t}\n\n\tusi.definition = definition\n\treturn usi\n}\n\n// Session sets the session for this operation.\nfunc (usi *UpdateSearchIndex) Session(session *session.Client) *UpdateSearchIndex {\n\tif usi == nil {\n\t\tusi = new(UpdateSearchIndex)\n\t}\n\n\tusi.session = session\n\treturn usi\n}\n\n// ClusterClock sets the cluster clock for this operation.\nfunc (usi *UpdateSearchIndex) ClusterClock(clock *session.ClusterClock) *UpdateSearchIndex {\n\tif usi == nil {\n\t\tusi = new(UpdateSearchIndex)\n\t}\n\n\tusi.clock = clock\n\treturn usi\n}\n\n// Collection sets the collection that this command will run against.\nfunc (usi *UpdateSearchIndex) Collection(collection string) *UpdateSearchIndex {\n\tif usi == nil {\n\t\tusi = new(UpdateSearchIndex)\n\t}\n\n\tusi.collection = collection\n\treturn usi\n}\n\n// CommandMonitor sets the monitor to use for APM events.\nfunc (usi *UpdateSearchIndex) CommandMonitor(monitor *event.CommandMonitor) *UpdateSearchIndex {\n\tif usi == nil {\n\t\tusi = new(UpdateSearchIndex)\n\t}\n\n\tusi.monitor = monitor\n\treturn usi\n}\n\n// Crypt sets the Crypt object to use for automatic encryption and decryption.\nfunc (usi *UpdateSearchIndex) Crypt(crypt driver.Crypt) *UpdateSearchIndex {\n\tif usi == nil {\n\t\tusi = new(UpdateSearchIndex)\n\t}\n\n\tusi.crypt = crypt\n\treturn usi\n}\n\n// Database sets the database to run this operation against.\nfunc (usi *UpdateSearchIndex) Database(database string) *UpdateSearchIndex {\n\tif usi == nil {\n\t\tusi = new(UpdateSearchIndex)\n\t}\n\n\tusi.database = database\n\treturn usi\n}\n\n// Deployment sets the deployment to use for this operation.\nfunc (usi *UpdateSearchIndex) Deployment(deployment driver.Deployment) *UpdateSearchIndex {\n\tif usi == nil {\n\t\tusi = new(UpdateSearchIndex)\n\t}\n\n\tusi.deployment = deployment\n\treturn usi\n}\n\n// ServerSelector sets the selector used to retrieve a server.\nfunc (usi *UpdateSearchIndex) ServerSelector(selector description.ServerSelector) *UpdateSearchIndex {\n\tif usi == nil {\n\t\tusi = new(UpdateSearchIndex)\n\t}\n\n\tusi.selector = selector\n\treturn usi\n}\n\n// ServerAPI sets the server API version for this operation.\nfunc (usi *UpdateSearchIndex) ServerAPI(serverAPI *driver.ServerAPIOptions) *UpdateSearchIndex {\n\tif usi == nil {\n\t\tusi = new(UpdateSearchIndex)\n\t}\n\n\tusi.serverAPI = serverAPI\n\treturn usi\n}\n\n// Timeout sets the timeout for this operation.\nfunc (usi *UpdateSearchIndex) Timeout(timeout *time.Duration) *UpdateSearchIndex {\n\tif usi == nil {\n\t\tusi = new(UpdateSearchIndex)\n\t}\n\n\tusi.timeout = timeout\n\treturn usi\n}\n\n// Authenticator sets the authenticator to use for this operation.\nfunc (usi *UpdateSearchIndex) Authenticator(authenticator driver.Authenticator) *UpdateSearchIndex {\n\tif usi == nil {\n\t\tusi = new(UpdateSearchIndex)\n\t}\n\n\tusi.authenticator = authenticator\n\treturn usi\n}\n"
  },
  {
    "path": "x/mongo/driver/operation.go",
    "content": "// Copyright (C) MongoDB, Inc. 2022-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage driver\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"math\"\n\t\"net\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/event\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/csot\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/driverutil\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/handshake\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/logger\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/serverselector\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/address\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/readconcern\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/readpref\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/writeconcern\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/description\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/mnet\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/session\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/wiremessage\"\n)\n\nconst defaultLocalThreshold = 15 * time.Millisecond\n\nvar (\n\t// ErrNoDocCommandResponse occurs when the server indicated a response existed, but none was found.\n\tErrNoDocCommandResponse = errors.New(\"command returned no documents\")\n\t// ErrMultiDocCommandResponse occurs when the server sent multiple documents in response to a command.\n\tErrMultiDocCommandResponse = errors.New(\"command returned multiple documents\")\n\t// ErrReplyDocumentMismatch occurs when the number of documents returned in an OP_QUERY does not match the numberReturned field.\n\tErrReplyDocumentMismatch = errors.New(\"number of documents returned does not match numberReturned field\")\n\t// ErrNonPrimaryReadPref is returned when a read is attempted in a transaction with a non-primary read preference.\n\tErrNonPrimaryReadPref = errors.New(\"read preference in a transaction must be primary\")\n\t// ErrEmptyReadConcern indicates that a read concern has no fields set.\n\tErrEmptyReadConcern = errors.New(\"a read concern must have at least one field set\")\n\t// ErrEmptyWriteConcern indicates that a write concern has no fields set.\n\tErrEmptyWriteConcern = errors.New(\"a write concern must have at least one field set\")\n\t// ErrDocumentTooLarge occurs when a document that is larger than the maximum size accepted by a\n\t// server is passed to an insert command.\n\tErrDocumentTooLarge = errors.New(\"an inserted document is too large\")\n\t// errDatabaseNameEmpty occurs when a database name is not provided.\n\terrDatabaseNameEmpty = errors.New(\"database name cannot be empty\")\n\t// errNegativeW indicates that a negative integer `w` field was specified.\n\terrNegativeW = errors.New(\"write concern `w` field cannot be a negative number\")\n\t// errInconsistent indicates that an inconsistent write concern was specified.\n\terrInconsistent = errors.New(\"a write concern cannot have both w=0 and j=true\")\n)\n\nconst (\n\t// maximum BSON object size when in-use encryption is enabled\n\tcryptMaxBsonObjectSize int = 2097152\n\t// minimum wire version necessary to use automatic encryption\n\tcryptMinWireVersion int32 = 8\n\t// minimum wire version necessary to use read snapshots\n\treadSnapshotMinWireVersion int32 = 13\n)\n\n// RetryablePoolError is a connection pool error that can be retried while executing an operation.\ntype RetryablePoolError interface {\n\tRetryable() bool\n}\n\n// labeledError is an error that can have error labels added to it.\ntype labeledError interface {\n\terror\n\tHasErrorLabel(string) bool\n}\n\n// InvalidOperationError is returned from Validate and indicates that a required field is missing\n// from an instance of Operation.\ntype InvalidOperationError struct{ MissingField string }\n\nfunc (err InvalidOperationError) Error() string {\n\treturn \"the \" + err.MissingField + \" field must be set on Operation\"\n}\n\n// opReply stores information returned in an OP_REPLY response from the server.\n// The err field stores any error that occurred when decoding or validating the OP_REPLY response.\ntype opReply struct {\n\tresponseFlags wiremessage.ReplyFlag\n\tcursorID      int64\n\tstartingFrom  int32\n\tnumReturned   int32\n\tdocuments     []bsoncore.Document\n\terr           error\n}\n\n// startedInformation keeps track of all of the information necessary for monitoring started events.\ntype startedInformation struct {\n\tcmd               bsoncore.Document\n\trequestID         int32\n\tcmdName           string\n\tdocumentSequences []struct {\n\t\tidentifier string\n\t\tdata       []byte\n\t}\n\tprocessedBatches   int\n\tconnID             string\n\tdriverConnectionID int64\n\tserverConnID       *int64\n\tredacted           bool\n\tserviceID          *bson.ObjectID\n\tserverAddress      address.Address\n}\n\n// finishedInformation keeps track of all of the information necessary for monitoring success and failure events.\ntype finishedInformation struct {\n\tcmdName            string\n\trequestID          int32\n\tresponse           bsoncore.Document\n\tcmdErr             error\n\tconnID             string\n\tdriverConnectionID int64\n\tserverConnID       *int64\n\tredacted           bool\n\tserviceID          *bson.ObjectID\n\tserverAddress      address.Address\n\tduration           time.Duration\n}\n\n// success returns true if there was no command error or the command error is a\n// \"WriteCommandError\". Commands that executed on the server and return a status\n// of { ok: 1.0 } are considered successful commands and MUST generate a\n// CommandSucceededEvent and \"command succeeded\" log message. Commands that have\n// write errors are included since the actual command did succeed, only writes\n// failed.\nfunc (info finishedInformation) success() bool {\n\tif _, ok := info.cmdErr.(WriteCommandError); ok {\n\t\treturn true\n\t}\n\n\treturn info.cmdErr == nil\n}\n\n// ResponseInfo contains the context required to parse a server response.\ntype ResponseInfo struct {\n\tServer                Server\n\tConnection            *mnet.Connection\n\tConnectionDescription description.Server\n\tCurrentIndex          int\n\tError                 error\n}\n\nfunc redactStartedInformationCmd(info startedInformation) bson.Raw {\n\tintLen := func(n int) int {\n\t\tif n == 0 {\n\t\t\treturn 1 // Special case: 0 has one digit\n\t\t}\n\t\tcount := 0\n\t\tfor n > 0 {\n\t\t\tn /= 10\n\t\t\tcount++\n\t\t}\n\t\treturn count\n\t}\n\n\tvar cmdCopy bson.Raw\n\n\t// Make a copy of the command. Redact if the command is security\n\t// sensitive and cannot be monitored. If there was a type 1 payload for\n\t// the current batch, convert it to a BSON array\n\tif !info.redacted {\n\t\tcmdLen := len(info.cmd)\n\t\tfor _, seq := range info.documentSequences {\n\t\t\tcmdLen += 7 // 2 (header) + 4 (array length) + 1 (array end)\n\t\t\tcmdLen += len(seq.identifier)\n\t\t\tdata := seq.data\n\t\t\ti := 0\n\t\t\tfor {\n\t\t\t\tdoc, rest, ok := bsoncore.ReadDocument(data)\n\t\t\t\tif !ok {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tdata = rest\n\t\t\t\tcmdLen += len(doc)\n\t\t\t\tcmdLen += intLen(i)\n\t\t\t\ti++\n\t\t\t}\n\t\t}\n\n\t\tcmdCopy = make([]byte, 0, cmdLen)\n\t\tcmdCopy = append(cmdCopy, info.cmd...)\n\n\t\tif len(info.documentSequences) > 0 {\n\t\t\t// remove 0 byte at end\n\t\t\tcmdCopy = cmdCopy[:len(info.cmd)-1]\n\t\t\tfor _, seq := range info.documentSequences {\n\t\t\t\tcmdCopy = appendDocumentArray(cmdCopy, seq.identifier, seq.data)\n\t\t\t}\n\t\t\t// add back 0 byte and update length\n\t\t\tcmdCopy, _ = bsoncore.AppendDocumentEnd(cmdCopy, 0)\n\t\t}\n\t}\n\n\treturn cmdCopy\n}\n\nfunc redactFinishedInformationResponse(info finishedInformation) bson.Raw {\n\tif !info.redacted {\n\t\treturn bson.Raw(info.response)\n\t}\n\n\treturn bson.Raw{}\n}\n\n// OperationBatches contains the documents that are split when executing a write command that potentially\n// has more documents than can fit in a single command.\ntype OperationBatches interface {\n\tAppendBatchSequence(dst []byte, maxCount int, totalSize int) (int, []byte, error)\n\tAppendBatchArray(dst []byte, maxCount int, totalSize int) (int, []byte, error)\n\tIsOrdered() *bool\n\tAdvanceBatches(n int)\n\tSize() int\n}\n\n// Operation is used to execute an operation. It contains all of the common code required to\n// select a server, transform an operation into a command, write the command to a connection from\n// the selected server, read a response from that connection, process the response, and potentially\n// retry.\n//\n// The required fields are Database, CommandFn, and Deployment. All other fields are optional.\n//\n// While an Operation can be constructed manually, drivergen should be used to generate an\n// implementation of an operation instead. This will ensure that there are helpers for constructing\n// the operation and that this type isn't configured incorrectly.\ntype Operation struct {\n\t// CommandFn is used to create the command that will be wrapped in a wire message and sent to\n\t// the server. This function should only add the elements of the command and not start or end\n\t// the enclosing BSON document. Per the command API, the first element must be the name of the\n\t// command to run. This field is required.\n\tCommandFn func(dst []byte, desc description.SelectedServer) ([]byte, error)\n\n\t// Database is the database that the command will be run against. This field is required.\n\tDatabase string\n\n\t// Deployment is the MongoDB Deployment to use. While most of the time this will be multiple\n\t// servers, commands that need to run against a single, preselected server can use the\n\t// SingleServerDeployment type. Commands that need to run on a preselected connection can use\n\t// the SingleConnectionDeployment type.\n\tDeployment Deployment\n\n\t// ProcessResponseFn is called after a response to the command is returned. The server is\n\t// provided for types like Cursor that are required to run subsequent commands using the same\n\t// server.\n\tProcessResponseFn func(context.Context, bsoncore.Document, ResponseInfo) error\n\n\t// Selector is the server selector that's used during both initial server selection and\n\t// subsequent selection for retries. Depending on the Deployment implementation, the\n\t// SelectServer method may not actually be called.\n\tSelector description.ServerSelector\n\n\t// ReadPreference is the read preference that will be attached to the command. If this field is\n\t// not specified a default read preference of primary will be used.\n\tReadPreference *readpref.ReadPref\n\n\t// ReadConcern is the read concern used when running read commands. This field should not be set\n\t// for write operations. If this field is set, it will be encoded onto the commands sent to the\n\t// server.\n\tReadConcern *readconcern.ReadConcern\n\n\t// MinimumReadConcernWireVersion specifies the minimum wire version to add the read concern to\n\t// the command being executed.\n\tMinimumReadConcernWireVersion int32\n\n\t// WriteConcern is the write concern used when running write commands. This field should not be\n\t// set for read operations. If this field is set, it will be encoded onto the commands sent to\n\t// the server.\n\tWriteConcern *writeconcern.WriteConcern\n\n\t// MinimumWriteConcernWireVersion specifies the minimum wire version to add the write concern to\n\t// the command being executed.\n\tMinimumWriteConcernWireVersion int32\n\n\t// Client is the session used with this operation. This can be either an implicit or explicit\n\t// session. If the server selected does not support sessions and Client is specified the\n\t// behavior depends on the session type. If the session is implicit, the session fields will not\n\t// be encoded onto the command. If the session is explicit, an error will be returned. The\n\t// caller is responsible for ensuring that this field is nil if the Deployment does not support\n\t// sessions.\n\tClient *session.Client\n\n\t// Clock is a cluster clock, different from the one contained within a session.Client. This\n\t// allows updating cluster times for a global cluster clock while allowing individual session's\n\t// cluster clocks to be only updated as far as the last command that's been run.\n\tClock *session.ClusterClock\n\n\t// RetryMode specifies how to retry. There are three modes that enable retry: RetryOnce,\n\t// RetryOncePerCommand, and RetryContext. For more information about what these modes do, please\n\t// refer to their definitions. Both RetryMode and Type must be set for retryability to be enabled.\n\t// If Timeout is set on the Client, the operation will automatically retry as many times as\n\t// possible unless RetryNone is used.\n\tRetryMode *RetryMode\n\n\t// Type specifies the kind of operation this is. There is only one mode that enables retry: Write.\n\t// For more information about what this mode does, please refer to it's definition. Both Type and\n\t// RetryMode must be set for retryability to be enabled.\n\tType Type\n\n\t// Batches contains the documents that are split when executing a write command that potentially\n\t// has more documents than can fit in a single command. This should only be specified for\n\t// commands that are batch compatible.\n\tBatches OperationBatches\n\n\t// Legacy sets the legacy type for this operation. There are only 3 types that require legacy\n\t// support: find, getMore, and killCursors. For more information about LegacyOperationKind,\n\t// please refer to it's definition.\n\tLegacy LegacyOperationKind\n\n\t// CommandMonitor specifies the monitor to use for APM events. If this field is not set,\n\t// no events will be reported.\n\tCommandMonitor *event.CommandMonitor\n\n\t// Crypt specifies a Crypt object to use for automatic in-use encryption and decryption.\n\tCrypt Crypt\n\n\t// ServerAPI specifies options used to configure the API version sent to the server.\n\tServerAPI *ServerAPIOptions\n\n\t// IsOutputAggregate specifies whether this operation is an aggregate with an output stage. If true,\n\t// read preference will not be added to the command on wire versions < 13.\n\tIsOutputAggregate bool\n\n\t// Timeout is the amount of time that this operation can execute before returning an error. The default value\n\t// nil, which means that the timeout of the operation's caller will be used.\n\tTimeout *time.Duration\n\n\tLogger *logger.Logger\n\n\t// Name is the name of the operation. This is used when serializing\n\t// OP_MSG as well as for logging server selection data.\n\tName string\n\n\t// OmitMaxTimeMS will ensure that wire messages sent to the server in service\n\t// of the operation do not contain a maxTimeMS field.\n\tOmitMaxTimeMS bool\n\n\t// Authenticator is the authenticator to use for this operation when a reauthentication is\n\t// required.\n\tAuthenticator Authenticator\n\n\t// omitReadPreference is a boolean that indicates whether to omit the\n\t// read preference from the command. This omition includes the case\n\t// where a default read preference is used when the operation\n\t// ReadPreference is not specified.\n\tomitReadPreference bool\n}\n\n// shouldEncrypt returns true if this operation should automatically be encrypted.\nfunc (op Operation) shouldEncrypt() bool {\n\treturn op.Crypt != nil && !op.Crypt.BypassAutoEncryption()\n}\n\n// selectServer handles performing server selection for an operation.\nfunc (op Operation) selectServer(\n\tctx context.Context,\n\trequestID int32,\n\tdeprioritized []description.Server,\n) (Server, error) {\n\tif err := op.Validate(); err != nil {\n\t\treturn nil, err\n\t}\n\n\tselector := op.Selector\n\tif selector == nil {\n\t\trp := op.ReadPreference\n\t\tif rp == nil {\n\t\t\trp = readpref.Primary()\n\t\t}\n\n\t\tselector = &serverselector.Composite{\n\t\t\tSelectors: []description.ServerSelector{\n\t\t\t\t&serverselector.ReadPref{ReadPref: rp},\n\t\t\t\t&serverselector.Latency{Latency: defaultLocalThreshold},\n\t\t\t},\n\t\t}\n\t}\n\n\t// Wrap the selector to filter out deprioritized servers.\n\tdeprioritizedSelector := serverselector.NewDeprioritized(selector, deprioritized)\n\n\tctx = logger.WithOperationName(ctx, op.Name)\n\tctx = logger.WithOperationID(ctx, requestID)\n\n\treturn op.Deployment.SelectServer(ctx, deprioritizedSelector)\n}\n\n// getServerAndConnection should be used to retrieve a Server and Connection to execute an operation.\nfunc (op Operation) getServerAndConnection(\n\tctx context.Context,\n\trequestID int32,\n\tdeprioritized []description.Server,\n) (Server, *mnet.Connection, error) {\n\tctx, cancel := csot.WithServerSelectionTimeout(ctx, op.Deployment.GetServerSelectionTimeout())\n\tdefer cancel()\n\n\tserver, err := op.selectServer(ctx, requestID, deprioritized)\n\tif err != nil {\n\t\tif op.Client != nil &&\n\t\t\t(!op.Client.Committing && !op.Client.Aborting) && op.Client.TransactionRunning() {\n\t\t\terr = Error{\n\t\t\t\tMessage: err.Error(),\n\t\t\t\tLabels:  []string{TransientTransactionError},\n\t\t\t\tWrapped: err,\n\t\t\t}\n\t\t}\n\t\treturn nil, nil, err\n\t}\n\n\t// If the provided client session has a pinned connection, it should be used for the operation because this\n\t// indicates that we're in a transaction and the target server is behind a load balancer.\n\tif op.Client != nil && op.Client.PinnedConnection != nil {\n\t\tconn := mnet.NewConnection(op.Client.PinnedConnection)\n\t\treturn server, conn, nil\n\t}\n\n\t// Otherwise, default to checking out a connection from the server's pool.\n\tconn, err := server.Connection(ctx)\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\n\t// If we're in load balanced mode and this is the first operation in a transaction, pin the session to a connection.\n\tif driverutil.IsServerLoadBalanced(conn.Description()) && op.Client != nil && op.Client.TransactionStarting() {\n\t\tif conn.Pinner == nil {\n\t\t\t// Close the original connection to avoid a leak.\n\t\t\t_ = conn.Close()\n\t\t\treturn nil, nil, fmt.Errorf(\"expected Connection used to start a transaction to be a PinnedConnection, but got %T\", conn)\n\t\t}\n\t\tif err := conn.PinToTransaction(); err != nil {\n\t\t\t// Close the original connection to avoid a leak.\n\t\t\t_ = conn.Close()\n\t\t\treturn nil, nil, fmt.Errorf(\"error incrementing connection reference count when starting a transaction: %w\", err)\n\t\t}\n\t\top.Client.PinnedConnection = conn\n\t}\n\n\treturn server, conn, nil\n}\n\n// Validate validates this operation, ensuring the fields are set properly.\nfunc (op Operation) Validate() error {\n\tif op.CommandFn == nil {\n\t\treturn InvalidOperationError{MissingField: \"CommandFn\"}\n\t}\n\tif op.Deployment == nil {\n\t\treturn InvalidOperationError{MissingField: \"Deployment\"}\n\t}\n\tif op.Database == \"\" {\n\t\treturn errDatabaseNameEmpty\n\t}\n\tif op.Client != nil && !op.WriteConcern.Acknowledged() {\n\t\treturn errors.New(\"session provided for an unacknowledged write\")\n\t}\n\treturn nil\n}\n\nvar memoryPool = sync.Pool{\n\tNew: func() any {\n\t\t// Start with 1kb buffers.\n\t\tb := make([]byte, 1024)\n\t\t// Return a pointer as the static analysis tool suggests.\n\t\treturn &b\n\t},\n}\n\n// Execute runs this operation.\nfunc (op Operation) Execute(ctx context.Context) error {\n\terr := op.Validate()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tctx, cancel := csot.WithTimeout(ctx, op.Timeout)\n\tdefer cancel()\n\n\tif op.Client != nil {\n\t\tif err := op.Client.StartCommand(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tvar retries int\n\tif op.RetryMode != nil {\n\t\tswitch op.Type {\n\t\tcase Write:\n\t\t\tif op.Client == nil {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tswitch *op.RetryMode {\n\t\t\tcase RetryOnce, RetryOncePerCommand:\n\t\t\t\tretries = 1\n\t\t\tcase RetryContext:\n\t\t\t\tretries = -1\n\t\t\t}\n\t\tcase Read:\n\t\t\tswitch *op.RetryMode {\n\t\t\tcase RetryOnce, RetryOncePerCommand:\n\t\t\t\tretries = 1\n\t\t\tcase RetryContext:\n\t\t\t\tretries = -1\n\t\t\t}\n\t\t}\n\n\t\t// If context is a Timeout context, automatically set retries to -1 (infinite) if retrying is\n\t\t// enabled.\n\t\tif csot.IsTimeoutContext(ctx) && op.RetryMode.Enabled() {\n\t\t\tretries = -1\n\t\t}\n\t}\n\n\tvar srvr Server\n\tvar conn *mnet.Connection\n\tvar res bsoncore.Document\n\tvar operationErr WriteCommandError\n\tvar prevErr error\n\tvar prevIndefiniteErr error\n\tretrySupported := false\n\tfirst := true\n\tcurrIndex := 0\n\n\t// deprioritizedServers are a running list of servers that should be\n\t// deprioritized during server selection. Servers are accumulated across\n\t// retry attempts to avoid repeatedly selecting servers that have failed.\n\tvar deprioritizedServers []description.Server\n\n\t// resetForRetry records the error that caused the retry, decrements retries, and resets the\n\t// retry loop variables to request a new server and a new connection for the next attempt.\n\tresetForRetry := func(err error) {\n\t\tretries--\n\t\tprevErr = err\n\n\t\tvar isOverloadedError bool\n\t\t// Set the previous indefinite error to be returned in any case where a retryable write error does not have a\n\t\t// NoWritesPerfomed label (the definite case).\n\t\tif lerr, ok := err.(labeledError); ok {\n\t\t\t// If the \"prevIndefiniteErr\" is nil, then the current error is the first error encountered\n\t\t\t// during the retry attempt cycle. We must persist the first error in the case where all\n\t\t\t// following errors are labeled \"NoWritesPerformed\", which would otherwise raise nil as the\n\t\t\t// error.\n\t\t\tif prevIndefiniteErr == nil {\n\t\t\t\tprevIndefiniteErr = lerr\n\t\t\t}\n\n\t\t\t// If the error is not labeled NoWritesPerformed and is retryable, then set the previous\n\t\t\t// indefinite error to be the current error.\n\t\t\tif !lerr.HasErrorLabel(NoWritesPerformed) && lerr.HasErrorLabel(RetryableWriteError) {\n\t\t\t\tprevIndefiniteErr = err\n\t\t\t}\n\n\t\t\tif lerr.HasErrorLabel(ErrSystemOverloadedError) {\n\t\t\t\tisOverloadedError = true\n\t\t\t}\n\t\t}\n\n\t\t// If we got a connection, close it immediately to release pool resources\n\t\t// for subsequent retries.\n\t\tif conn != nil {\n\t\t\tif op.Deployment.Kind() == description.TopologyKindSharded || isOverloadedError {\n\t\t\t\tdeprioritizedServers = append(deprioritizedServers, conn.Description())\n\t\t\t}\n\t\t\tconn.Close()\n\t\t}\n\n\t\t// Set the server and connection to nil to request a new server and connection.\n\t\tsrvr = nil\n\t\tconn = nil\n\t}\n\n\twm := memoryPool.Get().(*[]byte)\n\tdefer func() {\n\t\t// Proper usage of a sync.Pool requires each entry to have approximately the same memory\n\t\t// cost. To obtain this property when the stored type contains a variably-sized buffer,\n\t\t// we add a hard limit on the maximum buffer to place back in the pool. We limit the\n\t\t// size to 16MiB because that's the maximum wire message size supported by MongoDB.\n\t\t//\n\t\t// Comment copied from https://cs.opensource.google/go/go/+/refs/tags/go1.19:src/fmt/print.go;l=147\n\t\t//\n\t\t// Recycle byte slices that are smaller than 16MiB and at least half occupied.\n\t\tif c := cap(*wm); c < 16*1024*1024 && c/2 < len(*wm) {\n\t\t\tmemoryPool.Put(wm)\n\t\t}\n\t}()\n\tfor {\n\t\t// If we're starting a retry and the error from the previous try was\n\t\t// a context canceled or deadline exceeded error, stop retrying and\n\t\t// return that error.\n\t\tif errors.Is(prevErr, context.Canceled) || errors.Is(prevErr, context.DeadlineExceeded) {\n\t\t\treturn prevErr\n\t\t}\n\n\t\trequestID := wiremessage.NextRequestID()\n\n\t\t// If the server or connection are nil, try to select a new server and get a new connection.\n\t\tif srvr == nil || conn == nil {\n\t\t\tsrvr, conn, err = op.getServerAndConnection(ctx, requestID, deprioritizedServers)\n\t\t\tif err != nil {\n\t\t\t\t// If the returned error is retryable and there are retries remaining (negative\n\t\t\t\t// retries means retry indefinitely), then retry the operation. Set the server\n\t\t\t\t// and connection to nil to request a new server and connection.\n\t\t\t\tif rerr, ok := err.(RetryablePoolError); ok && rerr.Retryable() && retries != 0 {\n\t\t\t\t\tresetForRetry(err)\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\t// If this is a retry and there's an error from a previous attempt, return the previous\n\t\t\t\t// error instead of the current connection error.\n\t\t\t\tif prevErr != nil {\n\t\t\t\t\treturn prevErr\n\t\t\t\t}\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tdefer conn.Close()\n\n\t\t\t// Set the server if it has not already been set and the session type is implicit. This will\n\t\t\t// limit the number of implicit sessions to no greater than an application's maxPoolSize\n\t\t\t// (ignoring operations that hold on to the session like cursors).\n\t\t\tif op.Client != nil && op.Client.Server == nil && op.Client.IsImplicit {\n\t\t\t\tif op.Client.Terminated {\n\t\t\t\t\treturn fmt.Errorf(\"unexpected nil session for a terminated implicit session\")\n\t\t\t\t}\n\t\t\t\tif err := op.Client.SetServer(); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Run steps that must only be run on the first attempt, but not again for retries.\n\t\tif first {\n\t\t\t// Determine if retries are supported for the current operation on the current server\n\t\t\t// description. Per the retryable writes specification, only determine this for the\n\t\t\t// first server selected:\n\t\t\t//\n\t\t\t//   If the server selected for the first attempt of a retryable write operation does\n\t\t\t//   not support retryable writes, drivers MUST execute the write as if retryable writes\n\t\t\t//   were not enabled.\n\t\t\tretrySupported = op.retryable(conn.Description())\n\n\t\t\t// If retries are supported for the current operation on the current server description,\n\t\t\t// client retries are enabled, the operation type is write, and we haven't incremented\n\t\t\t// the txn number yet, enable retry writes on the session and increment the txn number.\n\t\t\t// Calling IncrementTxnNumber() for server descriptions or topologies that do not\n\t\t\t// support retries (e.g. standalone topologies) will cause server errors. Only do this\n\t\t\t// check for the first attempt to keep retried writes in the same transaction.\n\t\t\tretryEnabled := op.RetryMode != nil && op.RetryMode.Enabled()\n\t\t\tneedToIncrease := op.Client != nil && !op.Client.Committing && !op.Client.Aborting\n\t\t\tif retrySupported && op.Type == Write && retryEnabled && needToIncrease {\n\t\t\t\top.Client.IncrementTxnNumber()\n\t\t\t}\n\n\t\t\tfirst = false\n\t\t}\n\n\t\t// Calculate maxTimeMS value to potentially be appended to the wire message.\n\t\tmaxTimeMS, err := op.calculateMaxTimeMS(ctx, srvr.RTTMonitor().Min(), srvr.RTTMonitor().Stats())\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\t// Set maxTimeMS to 0 if connected to mongocryptd to avoid appending the field. The final\n\t\t// encrypted command may contain multiple maxTimeMS fields otherwise.\n\t\tif conn.Description().IsCryptd {\n\t\t\tmaxTimeMS = 0\n\t\t}\n\n\t\tdesc := description.SelectedServer{\n\t\t\tServer: conn.Description(),\n\t\t\tKind:   op.Deployment.Kind(),\n\t\t}\n\n\t\tvar moreToCome bool\n\t\tvar startedInfo startedInformation\n\t\t*wm, moreToCome, startedInfo, err = op.createWireMessage(ctx, maxTimeMS, (*wm)[:0], desc, conn, requestID)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tretryEnabled := op.RetryMode != nil && op.RetryMode.Enabled()\n\n\t\t// set extra data and send event if possible\n\t\tstartedInfo.connID = conn.ID()\n\t\tstartedInfo.driverConnectionID = conn.DriverConnectionID()\n\t\tstartedInfo.cmdName = op.getCommandName(startedInfo.cmd)\n\n\t\t// If the command name does not match the operation name, update\n\t\t// the operation name as a sanity check. It's more correct to\n\t\t// be aligned with the data passed to the server via the\n\t\t// wire message.\n\t\tif startedInfo.cmdName != op.Name {\n\t\t\top.Name = startedInfo.cmdName\n\t\t}\n\n\t\tstartedInfo.redacted = op.redactCommand(startedInfo.cmdName, startedInfo.cmd)\n\t\tstartedInfo.serviceID = conn.Description().ServiceID\n\t\tstartedInfo.serverConnID = conn.ServerConnectionID()\n\t\tstartedInfo.serverAddress = conn.Description().Addr\n\n\t\top.publishStartedEvent(ctx, startedInfo)\n\n\t\t// compress wiremessage if allowed\n\t\tif compressor := conn.Compressor; compressor != nil && op.canCompress(startedInfo.cmdName) {\n\t\t\tb := memoryPool.Get().(*[]byte)\n\t\t\t*b, err = compressor.CompressWireMessage(*wm, (*b)[:0])\n\t\t\tmemoryPool.Put(wm)\n\t\t\twm = b\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\n\t\tfinishedInfo := finishedInformation{\n\t\t\tcmdName:            startedInfo.cmdName,\n\t\t\tdriverConnectionID: startedInfo.driverConnectionID,\n\t\t\trequestID:          startedInfo.requestID,\n\t\t\tconnID:             startedInfo.connID,\n\t\t\tserverConnID:       startedInfo.serverConnID,\n\t\t\tredacted:           startedInfo.redacted,\n\t\t\tserviceID:          startedInfo.serviceID,\n\t\t\tserverAddress:      desc.Addr,\n\t\t}\n\n\t\tstartedTime := time.Now()\n\n\t\t// Check for possible context error. If no context error, check if there's enough time to perform a\n\t\t// round trip before the Context deadline. If ctx is a Timeout Context, use the 90th percentile RTT\n\t\t// as a threshold. Otherwise, use the minimum observed RTT.\n\t\tif ctx.Err() != nil {\n\t\t\terr = ctx.Err()\n\t\t} else if deadline, ok := ctx.Deadline(); ok {\n\t\t\tif time.Now().Add(srvr.RTTMonitor().Min()).After(deadline) {\n\t\t\t\terr = fmt.Errorf(\"%w: %v\", ErrDeadlineWouldBeExceeded, srvr.RTTMonitor().Stats())\n\t\t\t}\n\t\t}\n\n\t\tif err == nil {\n\t\t\t// roundtrip using either the full roundTripper or a special one for when the moreToCome\n\t\t\t// flag is set\n\t\t\troundTrip := op.roundTrip\n\t\t\tif moreToCome {\n\t\t\t\troundTrip = op.moreToComeRoundTrip\n\t\t\t}\n\t\t\tres, err = roundTrip(ctx, conn, *wm)\n\n\t\t\tif ep, ok := srvr.(ErrorProcessor); ok {\n\t\t\t\t_ = ep.ProcessError(err, conn)\n\t\t\t}\n\t\t}\n\n\t\tfinishedInfo.response = res\n\t\tfinishedInfo.cmdErr = err\n\t\tfinishedInfo.duration = time.Since(startedTime)\n\n\t\top.publishFinishedEvent(ctx, finishedInfo)\n\n\t\t// prevIndefiniteErrorIsSet is \"true\" if the \"err\" variable has been set to the \"prevIndefiniteErr\" in\n\t\t// a case in the switch statement below.\n\t\tvar prevIndefiniteErrIsSet bool\n\n\t\t// TODO(GODRIVER-2579): When refactoring the \"Execute\" method, consider creating a separate method for the\n\t\t// error handling logic below. This will remove the necessity of the \"checkError\" goto label.\n\tcheckError:\n\t\tswitch tt := err.(type) {\n\t\tcase WriteCommandError:\n\t\t\tif e := err.(WriteCommandError); retrySupported && op.Type == Write && e.UnsupportedStorageEngine() {\n\t\t\t\treturn ErrUnsupportedStorageEngine\n\t\t\t}\n\n\t\t\tconnDesc := conn.Description()\n\t\t\tretryableErr := tt.Retryable(connDesc.Kind, connDesc.WireVersion)\n\t\t\tpreRetryWriteLabelVersion := connDesc.WireVersion != nil && connDesc.WireVersion.Max < 9\n\t\t\tinTransaction := op.Client != nil &&\n\t\t\t\t(!op.Client.Committing && !op.Client.Aborting) && op.Client.TransactionRunning()\n\t\t\t// If retry is enabled and the operation isn't in a transaction, add a RetryableWriteError label for\n\t\t\t// retryable errors from pre-4.4 servers\n\t\t\tif retryableErr && preRetryWriteLabelVersion && retryEnabled && !inTransaction {\n\t\t\t\ttt.Labels = append(tt.Labels, RetryableWriteError)\n\t\t\t}\n\n\t\t\t// If retries are supported for the current operation on the first server description,\n\t\t\t// the error is considered retryable, and there are retries remaining (negative retries\n\t\t\t// means retry indefinitely), then retry the operation.\n\t\t\tif retrySupported && retryEnabled && retryableErr && retries != 0 {\n\t\t\t\tif op.Client != nil && op.Client.Committing {\n\t\t\t\t\t// Apply majority write concern for retries\n\t\t\t\t\top.Client.UpdateCommitTransactionWriteConcern()\n\t\t\t\t\top.WriteConcern = op.Client.CurrentWc\n\t\t\t\t}\n\t\t\t\tresetForRetry(tt)\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\t// If the error is no longer retryable and has the NoWritesPerformed label, then we should\n\t\t\t// set the error to the \"previous indefinite error\" unless the current error is already the\n\t\t\t// \"previous indefinite error\". After resetting, repeat the error check.\n\t\t\tif tt.HasErrorLabel(NoWritesPerformed) && !prevIndefiniteErrIsSet {\n\t\t\t\terr = prevIndefiniteErr\n\t\t\t\tprevIndefiniteErrIsSet = true\n\n\t\t\t\tgoto checkError\n\t\t\t}\n\n\t\t\t// If the operation isn't being retried, process the response\n\t\t\tif op.ProcessResponseFn != nil {\n\t\t\t\tinfo := ResponseInfo{\n\t\t\t\t\tServer:                srvr,\n\t\t\t\t\tConnection:            conn,\n\t\t\t\t\tConnectionDescription: desc.Server,\n\t\t\t\t\tCurrentIndex:          currIndex,\n\t\t\t\t\tError:                 tt,\n\t\t\t\t}\n\t\t\t\t_ = op.ProcessResponseFn(ctx, res, info)\n\t\t\t}\n\n\t\t\t// If batching is enabled and either ordered is the default (which is true) or\n\t\t\t// explicitly set to true and we have write errors, return the errors.\n\t\t\tif op.Batches != nil && len(tt.WriteErrors) > 0 {\n\t\t\t\tif currIndex > 0 {\n\t\t\t\t\tfor i := range tt.WriteErrors {\n\t\t\t\t\t\ttt.WriteErrors[i].Index += int64(currIndex)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif isOrdered := op.Batches.IsOrdered(); isOrdered == nil || *isOrdered {\n\t\t\t\t\treturn tt\n\t\t\t\t}\n\t\t\t}\n\t\t\tif op.Client != nil && op.Client.Committing && tt.WriteConcernError != nil {\n\t\t\t\t// When running commitTransaction we return WriteConcernErrors as an Error.\n\t\t\t\terr := Error{\n\t\t\t\t\tName:    tt.WriteConcernError.Name,\n\t\t\t\t\tCode:    int32(tt.WriteConcernError.Code),\n\t\t\t\t\tMessage: tt.WriteConcernError.Message,\n\t\t\t\t\tLabels:  tt.Labels,\n\t\t\t\t\tRaw:     tt.Raw,\n\t\t\t\t}\n\t\t\t\t// The UnknownTransactionCommitResult label is added to all writeConcernErrors besides unknownReplWriteConcernCode\n\t\t\t\t// and unsatisfiableWriteConcernCode\n\t\t\t\tif err.Code != unknownReplWriteConcernCode && err.Code != unsatisfiableWriteConcernCode {\n\t\t\t\t\terr.Labels = append(err.Labels, UnknownTransactionCommitResult)\n\t\t\t\t}\n\t\t\t\tif retryableErr && retryEnabled {\n\t\t\t\t\terr.Labels = append(err.Labels, RetryableWriteError)\n\t\t\t\t}\n\t\t\t\treturn err\n\t\t\t}\n\t\t\toperationErr.WriteConcernError = tt.WriteConcernError\n\t\t\toperationErr.WriteErrors = append(operationErr.WriteErrors, tt.WriteErrors...)\n\t\t\toperationErr.Labels = tt.Labels\n\t\t\toperationErr.Raw = tt.Raw\n\t\tcase Error:\n\t\t\t// 391 is the reauthentication required error code, so we will attempt a reauth and\n\t\t\t// retry the operation, if it is successful.\n\t\t\tif tt.Code == 391 {\n\t\t\t\tif op.Authenticator != nil {\n\t\t\t\t\tcfg := AuthConfig{\n\t\t\t\t\t\tDescription:  conn.Description(),\n\t\t\t\t\t\tConnection:   conn,\n\t\t\t\t\t\tClusterClock: op.Clock,\n\t\t\t\t\t\tServerAPI:    op.ServerAPI,\n\t\t\t\t\t}\n\t\t\t\t\tif err := op.Authenticator.Reauth(ctx, &cfg); err != nil {\n\t\t\t\t\t\treturn fmt.Errorf(\"error reauthenticating: %w\", err)\n\t\t\t\t\t}\n\t\t\t\t\tif op.Client != nil && op.Client.Committing {\n\t\t\t\t\t\t// Apply majority write concern for retries\n\t\t\t\t\t\top.Client.UpdateCommitTransactionWriteConcern()\n\t\t\t\t\t\top.WriteConcern = op.Client.CurrentWc\n\t\t\t\t\t}\n\t\t\t\t\tresetForRetry(tt)\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t}\n\t\t\tif tt.HasErrorLabel(TransientTransactionError) || tt.HasErrorLabel(UnknownTransactionCommitResult) {\n\t\t\t\tif err := op.Client.ClearPinnedResources(); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif e := err.(Error); retrySupported && op.Type == Write && e.UnsupportedStorageEngine() {\n\t\t\t\treturn ErrUnsupportedStorageEngine\n\t\t\t}\n\n\t\t\tconnDesc := conn.Description()\n\t\t\tvar retryableErr bool\n\t\t\tif op.Type == Write {\n\t\t\t\tretryableErr = tt.RetryableWrite(connDesc.WireVersion)\n\t\t\t\tpreRetryWriteLabelVersion := connDesc.WireVersion != nil && connDesc.WireVersion.Max < 9\n\t\t\t\tinTransaction := op.Client != nil &&\n\t\t\t\t\t(!op.Client.Committing && !op.Client.Aborting) && op.Client.TransactionRunning()\n\t\t\t\t// If retryWrites is enabled and the operation isn't in a transaction, add a RetryableWriteError label\n\t\t\t\t// for network errors and retryable errors from pre-4.4 servers\n\t\t\t\tif retryEnabled && !inTransaction &&\n\t\t\t\t\t(tt.HasErrorLabel(NetworkError) || (retryableErr && preRetryWriteLabelVersion)) {\n\t\t\t\t\ttt.Labels = append(tt.Labels, RetryableWriteError)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tretryableErr = tt.RetryableRead()\n\t\t\t}\n\n\t\t\t// If retries are supported for the current operation on the first server description,\n\t\t\t// the error is considered retryable, and there are retries remaining (negative retries\n\t\t\t// means retry indefinitely), then retry the operation.\n\t\t\tif retrySupported && retryEnabled && retryableErr && retries != 0 {\n\t\t\t\tif op.Client != nil && op.Client.Committing {\n\t\t\t\t\t// Apply majority write concern for retries\n\t\t\t\t\top.Client.UpdateCommitTransactionWriteConcern()\n\t\t\t\t\top.WriteConcern = op.Client.CurrentWc\n\t\t\t\t}\n\t\t\t\tresetForRetry(tt)\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\t// If the error is no longer retryable and has the NoWritesPerformed label, then we should\n\t\t\t// set the error to the \"previous indefinite error\" unless the current error is already the\n\t\t\t// \"previous indefinite error\". After resetting, repeat the error check.\n\t\t\tif tt.HasErrorLabel(NoWritesPerformed) && !prevIndefiniteErrIsSet {\n\t\t\t\terr = prevIndefiniteErr\n\t\t\t\tprevIndefiniteErrIsSet = true\n\n\t\t\t\tgoto checkError\n\t\t\t}\n\n\t\t\t// If the operation isn't being retried, process the response\n\t\t\tif op.ProcessResponseFn != nil {\n\t\t\t\tinfo := ResponseInfo{\n\t\t\t\t\tServer:                srvr,\n\t\t\t\t\tConnection:            conn,\n\t\t\t\t\tConnectionDescription: desc.Server,\n\t\t\t\t\tCurrentIndex:          currIndex,\n\t\t\t\t\tError:                 tt,\n\t\t\t\t}\n\t\t\t\t_ = op.ProcessResponseFn(ctx, res, info)\n\t\t\t}\n\n\t\t\tif op.Client != nil && op.Client.Committing && (retryableErr || tt.Code == 50) {\n\t\t\t\t// If we got a retryable error or MaxTimeMSExpired error, we add UnknownTransactionCommitResult.\n\t\t\t\ttt.Labels = append(tt.Labels, UnknownTransactionCommitResult)\n\t\t\t}\n\t\t\treturn tt\n\t\tcase nil:\n\t\t\tif moreToCome {\n\t\t\t\treturn ErrUnacknowledgedWrite\n\t\t\t}\n\t\t\tif op.ProcessResponseFn != nil {\n\t\t\t\tinfo := ResponseInfo{\n\t\t\t\t\tServer:                srvr,\n\t\t\t\t\tConnection:            conn,\n\t\t\t\t\tConnectionDescription: desc.Server,\n\t\t\t\t\tCurrentIndex:          currIndex,\n\t\t\t\t\tError:                 tt,\n\t\t\t\t}\n\t\t\t\tperr := op.ProcessResponseFn(ctx, res, info)\n\t\t\t\tif perr != nil {\n\t\t\t\t\treturn perr\n\t\t\t\t}\n\t\t\t}\n\t\tdefault:\n\t\t\tif op.ProcessResponseFn != nil {\n\t\t\t\tinfo := ResponseInfo{\n\t\t\t\t\tServer:                srvr,\n\t\t\t\t\tConnection:            conn,\n\t\t\t\t\tConnectionDescription: desc.Server,\n\t\t\t\t\tCurrentIndex:          currIndex,\n\t\t\t\t\tError:                 tt,\n\t\t\t\t}\n\t\t\t\t_ = op.ProcessResponseFn(ctx, res, info)\n\t\t\t}\n\t\t\treturn err\n\t\t}\n\n\t\t// If we're batching and there are batches remaining, advance to the next batch. This isn't\n\t\t// a retry, so increment the transaction number, reset the retries number, and don't set\n\t\t// server or connection to nil to continue using the same connection.\n\t\tif op.Batches != nil && op.Batches.Size() > startedInfo.processedBatches {\n\t\t\t// If retries are supported for the current operation on the current server description,\n\t\t\t// the session isn't nil, and client retries are enabled, increment the txn number.\n\t\t\t// Calling IncrementTxnNumber() for server descriptions or topologies that do not\n\t\t\t// support retries (e.g. standalone topologies) will cause server errors.\n\t\t\tif retrySupported && op.Client != nil && retryEnabled {\n\t\t\t\top.Client.IncrementTxnNumber()\n\n\t\t\t\t// Reset the retries number for RetryOncePerCommand unless context is a Timeout context, in\n\t\t\t\t// which case retries should remain as -1 (as many times as possible).\n\t\t\t\tif *op.RetryMode == RetryOncePerCommand && !csot.IsTimeoutContext(ctx) {\n\t\t\t\t\tretries = 1\n\t\t\t\t}\n\t\t\t}\n\t\t\tcurrIndex += startedInfo.processedBatches\n\t\t\top.Batches.AdvanceBatches(startedInfo.processedBatches)\n\t\t\tcontinue\n\t\t}\n\t\tbreak\n\t}\n\tif len(operationErr.WriteErrors) > 0 || operationErr.WriteConcernError != nil {\n\t\treturn operationErr\n\t}\n\treturn nil\n}\n\n// Retryable writes are supported if the server supports sessions, the operation is not\n// within a transaction, and the write is acknowledged\nfunc (op Operation) retryable(desc description.Server) bool {\n\tswitch op.Type {\n\tcase Write:\n\t\tif op.Client != nil && (op.Client.Committing || op.Client.Aborting) {\n\t\t\treturn true\n\t\t}\n\t\tif retryWritesSupported(desc) &&\n\t\t\top.Client != nil && (!op.Client.TransactionInProgress() && !op.Client.TransactionStarting()) &&\n\t\t\top.WriteConcern.Acknowledged() {\n\t\t\treturn true\n\t\t}\n\tcase Read:\n\t\tif op.Client != nil && (op.Client.Committing || op.Client.Aborting) {\n\t\t\treturn true\n\t\t}\n\t\tif op.Client == nil || (!op.Client.TransactionInProgress() && !op.Client.TransactionStarting()) {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\n// roundTrip writes a wiremessage to the connection and then reads a wiremessage. The wm parameter\n// is reused when reading the wiremessage.\nfunc (op Operation) roundTrip(ctx context.Context, conn *mnet.Connection, wm []byte) ([]byte, error) {\n\terr := conn.Write(ctx, wm)\n\tif err != nil {\n\t\treturn nil, op.networkError(err)\n\t}\n\treturn op.readWireMessage(ctx, conn)\n}\n\nfunc (op Operation) readWireMessage(ctx context.Context, conn *mnet.Connection) (result []byte, err error) {\n\twm, err := conn.Read(ctx)\n\tif err != nil {\n\t\treturn nil, op.networkError(err)\n\t}\n\n\t// If we're using a streamable connection, we set its streaming state based on the moreToCome flag in the server\n\t// response.\n\tif streamer := conn.Streamer; streamer != nil {\n\t\tstreamer.SetStreaming(wiremessage.IsMsgMoreToCome(wm))\n\t}\n\n\tlength, _, _, opcode, rem, ok := wiremessage.ReadHeader(wm)\n\tif !ok || len(wm) < int(length) {\n\t\treturn nil, errors.New(\"malformed wire message: insufficient bytes\")\n\t}\n\tif opcode == wiremessage.OpCompressed {\n\t\trawsize := length - 16 // remove header size\n\t\t// decompress wiremessage\n\t\topcode, rem, err = op.decompressWireMessage(rem[:rawsize])\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\t// decode\n\tres, err := op.decodeResult(opcode, rem)\n\t// When a cluster clock is given, update cluster/operation time and recovery tokens before handling the error\n\t// to ensure we're properly updating everything.\n\tif op.Clock != nil {\n\t\top.updateClusterTimes(res)\n\t}\n\top.updateOperationTime(res)\n\top.Client.UpdateRecoveryToken(bson.Raw(res))\n\n\t// Update snapshot time if operation was a \"find\", \"aggregate\" or \"distinct\".\n\tif op.Name == driverutil.FindOp || op.Name == driverutil.AggregateOp || op.Name == driverutil.DistinctOp {\n\t\top.Client.UpdateSnapshotTime(res)\n\t}\n\n\tif err != nil {\n\t\treturn res, err\n\t}\n\n\t// If there is no error, automatically attempt to decrypt all results if in-use encryption is enabled.\n\tif op.Crypt != nil {\n\t\tres, err = op.Crypt.Decrypt(ctx, res)\n\t}\n\treturn res, err\n}\n\n// networkError wraps the provided error in an Error with label \"NetworkError\" and, if a transaction\n// is running or committing, the appropriate transaction state labels. The returned error indicates\n// the operation should be retried for reads and writes. If err is nil, networkError returns nil.\nfunc (op Operation) networkError(err error) error {\n\tif err == nil {\n\t\treturn nil\n\t}\n\n\tlabels := []string{NetworkError}\n\tif op.Client != nil {\n\t\top.Client.MarkDirty()\n\t}\n\tif op.Client != nil && op.Client.TransactionRunning() && !op.Client.Committing {\n\t\tlabels = append(labels, TransientTransactionError)\n\t}\n\tif op.Client != nil && op.Client.Committing {\n\t\tlabels = append(labels, UnknownTransactionCommitResult)\n\t}\n\treturn Error{Message: err.Error(), Labels: labels, Wrapped: err}\n}\n\n// moreToComeRoundTrip writes a wiremessage to the provided connection. This is used when an OP_MSG is\n// being sent with  the moreToCome bit set.\nfunc (op *Operation) moreToComeRoundTrip(ctx context.Context, conn *mnet.Connection, wm []byte) (result []byte, err error) {\n\terr = conn.Write(ctx, wm)\n\tif err != nil {\n\t\tif op.Client != nil {\n\t\t\top.Client.MarkDirty()\n\t\t}\n\t\terr = Error{Message: err.Error(), Labels: []string{TransientTransactionError, NetworkError}, Wrapped: err}\n\t}\n\treturn bsoncore.BuildDocument(nil, bsoncore.AppendInt32Element(nil, \"ok\", 1)), err\n}\n\n// decompressWireMessage handles decompressing a wiremessage without the header.\nfunc (Operation) decompressWireMessage(wm []byte) (wiremessage.OpCode, []byte, error) {\n\t// get the original opcode and uncompressed size\n\topcode, rem, ok := wiremessage.ReadCompressedOriginalOpCode(wm)\n\tif !ok {\n\t\treturn 0, nil, errors.New(\"malformed OP_COMPRESSED: missing original opcode\")\n\t}\n\tuncompressedSize, rem, ok := wiremessage.ReadCompressedUncompressedSize(rem)\n\tif !ok {\n\t\treturn 0, nil, errors.New(\"malformed OP_COMPRESSED: missing uncompressed size\")\n\t}\n\t// get the compressor ID and decompress the message\n\tcompressorID, rem, ok := wiremessage.ReadCompressedCompressorID(rem)\n\tif !ok {\n\t\treturn 0, nil, errors.New(\"malformed OP_COMPRESSED: missing compressor ID\")\n\t}\n\n\topts := CompressionOpts{\n\t\tCompressor:       compressorID,\n\t\tUncompressedSize: uncompressedSize,\n\t}\n\tuncompressed, err := DecompressPayload(rem, opts)\n\tif err != nil {\n\t\treturn 0, nil, err\n\t}\n\n\treturn opcode, uncompressed, nil\n}\n\nfunc (op Operation) createLegacyHandshakeWireMessage(\n\tctx context.Context,\n\tmaxTimeMS int64,\n\tdst []byte,\n\tdesc description.SelectedServer,\n) (int, []byte, []byte, error) {\n\tflags := op.secondaryOK(desc)\n\tdst = wiremessage.AppendQueryFlags(dst, flags)\n\n\tdollarCmd := [...]byte{'.', '$', 'c', 'm', 'd'}\n\n\t// FullCollectionName\n\tdst = append(dst, op.Database...)\n\tdst = append(dst, dollarCmd[:]...)\n\tdst = append(dst, 0x00)\n\tdst = wiremessage.AppendQueryNumberToSkip(dst, 0)\n\tdst = wiremessage.AppendQueryNumberToReturn(dst, -1)\n\n\twrapper := int32(-1)\n\trp, err := op.createReadPref(desc, true)\n\tif err != nil {\n\t\treturn 0, dst, nil, err\n\t}\n\tif len(rp) > 0 {\n\t\twrapper, dst = bsoncore.AppendDocumentStart(dst)\n\t\tdst = bsoncore.AppendHeader(dst, bsoncore.TypeEmbeddedDocument, \"$query\")\n\t}\n\tidx, dst := bsoncore.AppendDocumentStart(dst)\n\n\tdst, err = op.CommandFn(dst, desc)\n\tif err != nil {\n\t\treturn 0, dst, nil, err\n\t}\n\tvar n int\n\tif op.Batches != nil {\n\t\tn, dst, err = op.Batches.AppendBatchArray(dst, int(desc.MaxBatchCount), int(desc.MaxMessageSize))\n\t\tif err != nil {\n\t\t\treturn 0, dst, nil, err\n\t\t}\n\t\tif n == 0 {\n\t\t\treturn 0, dst, nil, ErrDocumentTooLarge\n\t\t}\n\t}\n\n\tdst, err = op.addReadConcern(dst, desc)\n\tif err != nil {\n\t\treturn 0, dst, nil, err\n\t}\n\n\tdst, err = op.addWriteConcern(ctx, dst, desc)\n\tif err != nil {\n\t\treturn 0, dst, nil, err\n\t}\n\n\tdst, err = op.addSession(dst, desc, false)\n\tif err != nil {\n\t\treturn 0, dst, nil, err\n\t}\n\n\tdst = op.addClusterTime(dst, desc)\n\tdst = op.addServerAPI(dst)\n\t// If maxTimeMS is greater than 0 append it to wire message. A maxTimeMS value of 0 only explicitly\n\t// specifies the default behavior of no timeout server-side.\n\tif maxTimeMS > 0 {\n\t\tdst = bsoncore.AppendInt64Element(dst, \"maxTimeMS\", maxTimeMS)\n\t}\n\n\tdst, _ = bsoncore.AppendDocumentEnd(dst, idx)\n\n\tif len(rp) > 0 {\n\t\tidx = wrapper\n\t\tvar err error\n\t\tdst = bsoncore.AppendDocumentElement(dst, \"$readPreference\", rp)\n\t\tdst, err = bsoncore.AppendDocumentEnd(dst, idx)\n\t\tif err != nil {\n\t\t\treturn 0, dst, nil, err\n\t\t}\n\t}\n\n\treturn n, dst, dst[idx:], nil\n}\n\nfunc (op Operation) createMsgWireMessage(\n\tctx context.Context,\n\tmaxTimeMS int64,\n\tdst []byte,\n\tdesc description.SelectedServer,\n\tconn *mnet.Connection,\n\tcmdFn func([]byte, description.SelectedServer) ([]byte, error),\n) ([]byte, []byte, error) {\n\tvar flags wiremessage.MsgFlag\n\t// Set the ExhaustAllowed flag if the connection supports streaming. This will tell the server that it can\n\t// respond with the MoreToCome flag and then stream responses over this connection.\n\tif streamer := conn.Streamer; streamer != nil && streamer.SupportsStreaming() {\n\t\tflags = wiremessage.ExhaustAllowed\n\t}\n\tdst = wiremessage.AppendMsgFlags(dst, flags)\n\t// Body\n\tdst = wiremessage.AppendMsgSectionType(dst, wiremessage.SingleDocument)\n\n\tidx, dst := bsoncore.AppendDocumentStart(dst)\n\n\tvar err error\n\tdst, err = cmdFn(dst, desc)\n\tif err != nil {\n\t\treturn dst, nil, err\n\t}\n\tdst, err = op.addReadConcern(dst, desc)\n\tif err != nil {\n\t\treturn dst, nil, err\n\t}\n\tdst, err = op.addWriteConcern(ctx, dst, desc)\n\tif err != nil {\n\t\treturn dst, nil, err\n\t}\n\tretryWrite := op.retryable(conn.Description()) && op.RetryMode != nil && op.RetryMode.Enabled()\n\n\tdst, err = op.addSession(dst, desc, retryWrite)\n\tif err != nil {\n\t\treturn dst, nil, err\n\t}\n\n\tdst = op.addClusterTime(dst, desc)\n\tdst = op.addServerAPI(dst)\n\t// If maxTimeMS is greater than 0 append it to wire message. A maxTimeMS value of 0 only explicitly\n\t// specifies the default behavior of no timeout server-side.\n\tif maxTimeMS > 0 {\n\t\tdst = bsoncore.AppendInt64Element(dst, \"maxTimeMS\", maxTimeMS)\n\t}\n\n\tdst = bsoncore.AppendStringElement(dst, \"$db\", op.Database)\n\trp, err := op.createReadPref(desc, false)\n\tif err != nil {\n\t\treturn dst, nil, err\n\t}\n\tif len(rp) > 0 {\n\t\tdst = bsoncore.AppendDocumentElement(dst, \"$readPreference\", rp)\n\t}\n\n\tdst, _ = bsoncore.AppendDocumentEnd(dst, idx)\n\n\treturn dst, dst[idx:], nil\n}\n\n// isLegacyHandshake returns True if the operation is the first message of\n// the initial handshake and should use a legacy hello.\nfunc isLegacyHandshake(op Operation, desc description.SelectedServer) bool {\n\tisInitialHandshake := desc.WireVersion == nil || desc.WireVersion.Max == 0\n\n\treturn op.Legacy == LegacyHandshake && isInitialHandshake\n}\n\nfunc (op Operation) createWireMessage(\n\tctx context.Context,\n\tmaxTimeMS int64,\n\tdst []byte,\n\tdesc description.SelectedServer,\n\tconn *mnet.Connection,\n\trequestID int32,\n) ([]byte, bool, startedInformation, error) {\n\tvar info startedInformation\n\tvar wmindex int32\n\tvar err error\n\n\tunacknowledged := op.WriteConcern != nil && !op.WriteConcern.Acknowledged()\n\n\tfIdx := -1\n\tisLegacy := isLegacyHandshake(op, desc)\n\tswitch {\n\tcase isLegacy:\n\t\trequestID := wiremessage.NextRequestID()\n\t\twmindex, dst = wiremessage.AppendHeaderStart(dst, requestID, 0, wiremessage.OpQuery)\n\t\tinfo.processedBatches, dst, info.cmd, err = op.createLegacyHandshakeWireMessage(ctx, maxTimeMS, dst, desc)\n\tcase op.shouldEncrypt():\n\t\tif desc.WireVersion.Max < cryptMinWireVersion {\n\t\t\treturn dst, false, info, errors.New(\"auto-encryption requires a MongoDB version of 4.2\")\n\t\t}\n\t\tcmdFn := func(dst []byte, desc description.SelectedServer) ([]byte, error) {\n\t\t\tinfo.processedBatches, dst, err = op.addEncryptCommandFields(ctx, dst, desc)\n\t\t\treturn dst, err\n\t\t}\n\t\twmindex, dst = wiremessage.AppendHeaderStart(dst, requestID, 0, wiremessage.OpMsg)\n\t\tfIdx = len(dst)\n\t\tdst, info.cmd, err = op.createMsgWireMessage(ctx, maxTimeMS, dst, desc, conn, cmdFn)\n\tdefault:\n\t\twmindex, dst = wiremessage.AppendHeaderStart(dst, requestID, 0, wiremessage.OpMsg)\n\t\tfIdx = len(dst)\n\n\t\tbatchOffset := -1\n\t\tswitch op.Batches.(type) {\n\t\tcase *Batches:\n\t\t\tdst, info.cmd, err = op.createMsgWireMessage(ctx, maxTimeMS, dst, desc, conn, op.CommandFn)\n\t\t\tif err == nil && op.Batches != nil {\n\t\t\t\tbatchOffset = len(dst)\n\t\t\t\tinfo.processedBatches, dst, err = op.Batches.AppendBatchSequence(dst,\n\t\t\t\t\tint(desc.MaxBatchCount), int(desc.MaxMessageSize),\n\t\t\t\t)\n\t\t\t\tif err != nil {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tif info.processedBatches == 0 {\n\t\t\t\t\terr = ErrDocumentTooLarge\n\t\t\t\t}\n\t\t\t}\n\t\tdefault:\n\t\t\tvar batches []byte\n\t\t\tif op.Batches != nil {\n\t\t\t\tinfo.processedBatches, batches, err = op.Batches.AppendBatchSequence(batches,\n\t\t\t\t\tint(desc.MaxBatchCount), int(desc.MaxMessageSize),\n\t\t\t\t)\n\t\t\t\tif err != nil {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tif info.processedBatches == 0 {\n\t\t\t\t\terr = ErrDocumentTooLarge\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t\tdst, info.cmd, err = op.createMsgWireMessage(ctx, maxTimeMS, dst, desc, conn, op.CommandFn)\n\t\t\tif err == nil && len(batches) > 0 {\n\t\t\t\tbatchOffset = len(dst)\n\t\t\t\tdst = append(dst, batches...)\n\t\t\t}\n\t\t}\n\t\tif err == nil && batchOffset > 0 {\n\t\t\t// the remaining of dst is expected to be document sequences such as \"ops\", \"nsInfo\".\n\t\t\tfor b := dst[batchOffset:]; len(b) > 0; /* nothing */ {\n\t\t\t\tstype, data, ok := wiremessage.ReadMsgSectionType(b)\n\t\t\t\tif !ok || stype != wiremessage.DocumentSequence {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tvar identifier string\n\t\t\t\tidentifier, data, b, ok = wiremessage.ReadMsgSectionRawDocumentSequence(data)\n\t\t\t\tif !ok {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tinfo.documentSequences = append(info.documentSequences, struct {\n\t\t\t\t\tidentifier string\n\t\t\t\t\tdata       []byte\n\t\t\t\t}{identifier, data})\n\t\t\t}\n\t\t}\n\t}\n\tif err != nil {\n\t\treturn nil, false, info, err\n\t}\n\n\tvar moreToCome bool\n\t// We set the MoreToCome bit if we have a write concern, it's unacknowledged, and we either\n\t// aren't batching or we are encoding the last batch.\n\tbatching := op.Batches != nil && op.Batches.Size() > info.processedBatches\n\tif fIdx > 0 && unacknowledged && !batching {\n\t\tdst[fIdx] |= byte(wiremessage.MoreToCome)\n\t\tmoreToCome = true\n\t}\n\tinfo.requestID = requestID\n\treturn bsoncore.UpdateLength(dst, wmindex, int32(len(dst[wmindex:]))), moreToCome, info, nil\n}\n\nfunc (op Operation) addEncryptCommandFields(ctx context.Context, dst []byte, desc description.SelectedServer) (int, []byte, error) {\n\tidx, cmdDst := bsoncore.AppendDocumentStart(nil)\n\tvar err error\n\t// create temporary command document\n\tcmdDst, err = op.CommandFn(cmdDst, desc)\n\tif err != nil {\n\t\treturn 0, nil, err\n\t}\n\tvar n int\n\tif op.Batches != nil {\n\t\tif maxBatchCount := int(desc.MaxBatchCount); maxBatchCount > 1 {\n\t\t\tn, cmdDst, err = op.Batches.AppendBatchArray(cmdDst, maxBatchCount, cryptMaxBsonObjectSize)\n\t\t\tif err != nil {\n\t\t\t\treturn 0, nil, err\n\t\t\t}\n\t\t}\n\t\tif n == 0 {\n\t\t\tn, cmdDst, err = op.Batches.AppendBatchArray(cmdDst, 1, int(desc.MaxMessageSize))\n\t\t\tif err != nil {\n\t\t\t\treturn 0, nil, err\n\t\t\t}\n\t\t\tif n == 0 {\n\t\t\t\treturn 0, nil, ErrDocumentTooLarge\n\t\t\t}\n\t\t}\n\t}\n\tcmdDst, err = bsoncore.AppendDocumentEnd(cmdDst, idx)\n\tif err != nil {\n\t\treturn 0, nil, err\n\t}\n\t// encrypt the command\n\tencrypted, err := op.Crypt.Encrypt(ctx, op.Database, cmdDst)\n\tif err != nil {\n\t\treturn 0, nil, err\n\t}\n\t// append encrypted command to original destination, removing the first 4 bytes (length) and final byte (terminator)\n\tdst = append(dst, encrypted[4:len(encrypted)-1]...)\n\treturn n, dst, nil\n}\n\n// addServerAPI adds the relevant fields for server API specification to the wire message in dst.\nfunc (op Operation) addServerAPI(dst []byte) []byte {\n\tsa := op.ServerAPI\n\tif sa == nil {\n\t\treturn dst\n\t}\n\n\tdst = bsoncore.AppendStringElement(dst, \"apiVersion\", sa.ServerAPIVersion)\n\tif sa.Strict != nil {\n\t\tdst = bsoncore.AppendBooleanElement(dst, \"apiStrict\", *sa.Strict)\n\t}\n\tif sa.DeprecationErrors != nil {\n\t\tdst = bsoncore.AppendBooleanElement(dst, \"apiDeprecationErrors\", *sa.DeprecationErrors)\n\t}\n\treturn dst\n}\n\n// MarshalBSONReadConcern marshals a ReadConcern.\nfunc MarshalBSONReadConcern(rc *readconcern.ReadConcern) (bson.Type, []byte, error) {\n\tif rc == nil {\n\t\treturn 0, nil, ErrEmptyReadConcern\n\t}\n\n\tvar elems []byte\n\tif len(rc.Level) > 0 {\n\t\telems = bsoncore.AppendStringElement(elems, \"level\", rc.Level)\n\t}\n\n\treturn bson.TypeEmbeddedDocument, bsoncore.BuildDocument(nil, elems), nil\n}\n\nfunc (op Operation) addReadConcern(dst []byte, desc description.SelectedServer) ([]byte, error) {\n\tif op.MinimumReadConcernWireVersion > 0 && (desc.WireVersion == nil ||\n\t\t!driverutil.VersionRangeIncludes(*desc.WireVersion, op.MinimumReadConcernWireVersion)) {\n\n\t\treturn dst, nil\n\t}\n\trc := op.ReadConcern\n\tclient := op.Client\n\t// Starting transaction's read concern overrides all others\n\tif client != nil && client.TransactionStarting() && client.CurrentRc != nil {\n\t\trc = client.CurrentRc\n\t}\n\n\t// start transaction must append afterclustertime IF causally consistent and operation time exists\n\tif rc == nil && client != nil && client.TransactionStarting() && client.Consistent && client.OperationTime != nil {\n\t\trc = &readconcern.ReadConcern{}\n\t}\n\n\tif client != nil && client.Snapshot {\n\t\tif desc.WireVersion.Max < readSnapshotMinWireVersion {\n\t\t\treturn dst, errors.New(\"snapshot reads require MongoDB 5.0 or later\")\n\t\t}\n\t\trc = readconcern.Snapshot()\n\t}\n\n\t_, data, err := MarshalBSONReadConcern(rc) // always returns a document\n\tif errors.Is(err, ErrEmptyReadConcern) {\n\t\treturn dst, nil\n\t}\n\n\tif err != nil {\n\t\treturn dst, err\n\t}\n\n\tif sessionsSupported(desc.WireVersion) && client != nil {\n\t\tif client.Consistent && client.OperationTime != nil {\n\t\t\tdata = data[:len(data)-1] // remove the null byte\n\t\t\tdata = bsoncore.AppendTimestampElement(data, \"afterClusterTime\", client.OperationTime.T, client.OperationTime.I)\n\t\t\tdata, _ = bsoncore.AppendDocumentEnd(data, 0)\n\t\t}\n\t\tif client.Snapshot && client.SnapshotTimeSet {\n\t\t\tdata = data[:len(data)-1] // remove the null byte\n\t\t\tdata = bsoncore.AppendTimestampElement(data, \"atClusterTime\", client.SnapshotTime.T, client.SnapshotTime.I)\n\t\t\tdata, _ = bsoncore.AppendDocumentEnd(data, 0)\n\t\t}\n\t}\n\n\tif len(data) == bsoncore.EmptyDocumentLength {\n\t\treturn dst, nil\n\t}\n\treturn bsoncore.AppendDocumentElement(dst, \"readConcern\", data), nil\n}\n\n// MarshalBSONWriteConcern marshals a WriteConcern.\nfunc MarshalBSONWriteConcern(wc *writeconcern.WriteConcern, wtimeout time.Duration) (bson.Type, []byte, error) {\n\tif wc == nil {\n\t\treturn 0, nil, ErrEmptyWriteConcern\n\t}\n\n\tvar elems []byte\n\tif wc.W != nil {\n\t\t// Only support string or int values for W. That aligns with the\n\t\t// documentation and the behavior of other functions, like Acknowledged.\n\t\tswitch w := wc.W.(type) {\n\t\tcase int:\n\t\t\tif w < 0 {\n\t\t\t\treturn 0, nil, errNegativeW\n\t\t\t}\n\n\t\t\t// If Journal=true and W=0, return an error because that write\n\t\t\t// concern is ambiguous.\n\t\t\tif wc.Journal != nil && *wc.Journal && w == 0 {\n\t\t\t\treturn 0, nil, errInconsistent\n\t\t\t}\n\n\t\t\t// Check for lower and upper bounds on architecture-dependent int.\n\t\t\tif w > math.MaxInt32 {\n\t\t\t\treturn 0, nil, fmt.Errorf(\"WriteConcern.W overflows int32: %v\", wc.W)\n\t\t\t}\n\n\t\t\telems = bsoncore.AppendInt32Element(elems, \"w\", int32(w))\n\t\tcase string:\n\t\t\telems = bsoncore.AppendStringElement(elems, \"w\", w)\n\t\tdefault:\n\t\t\treturn 0,\n\t\t\t\tnil,\n\t\t\t\tfmt.Errorf(\"WriteConcern.W must be a string or int, but is a %T\", wc.W)\n\t\t}\n\t}\n\n\tif wc.Journal != nil {\n\t\telems = bsoncore.AppendBooleanElement(elems, \"j\", *wc.Journal)\n\t}\n\n\tif wtimeout != 0 {\n\t\telems = bsoncore.AppendInt64Element(elems, \"wtimeout\", int64(wtimeout/time.Millisecond))\n\t}\n\n\tif len(elems) == 0 {\n\t\treturn 0, nil, ErrEmptyWriteConcern\n\t}\n\n\treturn bson.TypeEmbeddedDocument, bsoncore.BuildDocument(nil, elems), nil\n}\n\nfunc (op Operation) addWriteConcern(ctx context.Context, dst []byte, desc description.SelectedServer) ([]byte, error) {\n\tif op.MinimumWriteConcernWireVersion > 0 && (desc.WireVersion == nil ||\n\t\t!driverutil.VersionRangeIncludes(*desc.WireVersion, op.MinimumWriteConcernWireVersion)) {\n\n\t\treturn dst, nil\n\t}\n\twc := op.WriteConcern\n\tif wc == nil {\n\t\treturn dst, nil\n\t}\n\n\t// The specifications for committing a transaction states:\n\t//\n\t// > if the modified write concern does not include a wtimeout value, drivers\n\t// > MUST also apply wtimeout: 10000 to the write concern in order to avoid\n\t// > waiting forever (or until a socket timeout) if the majority write concern\n\t// > cannot be satisfied.\n\tvar wtimeout time.Duration\n\tif _, ok := ctx.Deadline(); op.Client != nil && op.Timeout == nil && !ok {\n\t\twtimeout = op.Client.CurrentWTimeout\n\t}\n\n\ttyp, wcBSON, err := MarshalBSONWriteConcern(wc, wtimeout)\n\tif errors.Is(err, ErrEmptyWriteConcern) {\n\t\treturn dst, nil\n\t}\n\n\tif err != nil {\n\t\treturn dst, err\n\t}\n\n\treturn append(bsoncore.AppendHeader(dst, bsoncore.Type(typ), \"writeConcern\"), wcBSON...), nil\n}\n\nfunc (op Operation) addSession(dst []byte, desc description.SelectedServer, retryWrite bool) ([]byte, error) {\n\tclient := op.Client\n\n\t// If the operation is defined for an explicit session but the server\n\t// does not support sessions, then throw an error.\n\tif client != nil && !client.IsImplicit && desc.SessionTimeoutMinutes == nil {\n\t\treturn nil, fmt.Errorf(\"current topology does not support sessions\")\n\t}\n\n\tif client == nil || !sessionsSupported(desc.WireVersion) || desc.SessionTimeoutMinutes == nil {\n\t\treturn dst, nil\n\t}\n\tif err := client.UpdateUseTime(); err != nil {\n\t\treturn dst, err\n\t}\n\tdst = bsoncore.AppendDocumentElement(dst, \"lsid\", client.SessionID)\n\n\tvar addedTxnNumber bool\n\tif op.Type == Write && retryWrite {\n\t\taddedTxnNumber = true\n\t\tdst = bsoncore.AppendInt64Element(dst, \"txnNumber\", op.Client.TxnNumber)\n\t}\n\tif client.TransactionRunning() || client.RetryingCommit {\n\t\tif !addedTxnNumber {\n\t\t\tdst = bsoncore.AppendInt64Element(dst, \"txnNumber\", op.Client.TxnNumber)\n\t\t}\n\t\tif client.TransactionStarting() {\n\t\t\tdst = bsoncore.AppendBooleanElement(dst, \"startTransaction\", true)\n\t\t}\n\t\tdst = bsoncore.AppendBooleanElement(dst, \"autocommit\", false)\n\t}\n\n\treturn dst, client.ApplyCommand(desc.Server)\n}\n\nfunc (op Operation) addClusterTime(dst []byte, desc description.SelectedServer) []byte {\n\tclient, clock := op.Client, op.Clock\n\tif (clock == nil && client == nil) || !sessionsSupported(desc.WireVersion) {\n\t\treturn dst\n\t}\n\tvar clusterTime bson.Raw\n\tif clock != nil {\n\t\tclusterTime = clock.GetClusterTime()\n\t}\n\tif client != nil {\n\t\tclusterTime = session.MaxClusterTime(clusterTime, client.ClusterTime)\n\t}\n\tif clusterTime == nil {\n\t\treturn dst\n\t}\n\tval, err := clusterTime.LookupErr(\"$clusterTime\")\n\tif err != nil {\n\t\treturn dst\n\t}\n\treturn append(bsoncore.AppendHeader(dst, bsoncore.Type(val.Type), \"$clusterTime\"), val.Value...)\n}\n\n// calculateMaxTimeMS calculates the value of the 'maxTimeMS' field to potentially append\n// to the wire message based on the current context's deadline and the 90th percentile RTT\n// if the ctx is a Timeout context. If the context is not a Timeout context, it uses the\n// operation's MaxTimeMS if set. If no MaxTimeMS is set on the operation, and context is\n// not a Timeout context, calculateMaxTimeMS returns 0.\nfunc (op Operation) calculateMaxTimeMS(ctx context.Context, rttMin time.Duration, rttStats string) (int64, error) {\n\tif op.OmitMaxTimeMS {\n\t\treturn 0, nil\n\t}\n\n\t// Calculate maxTimeMS value to potentially be appended to the wire message.\n\tmaxTimeMS, ok := driverutil.CalculateMaxTimeMS(ctx, rttMin)\n\tif !ok && maxTimeMS <= 0 {\n\t\treturn 0, fmt.Errorf(\n\t\t\t\"calculated server-side timeout (%v ms) is less than or equal to 0 (%v): %w\",\n\t\t\tmaxTimeMS,\n\t\t\trttStats,\n\t\t\tErrDeadlineWouldBeExceeded)\n\t}\n\n\treturn maxTimeMS, nil\n}\n\n// updateClusterTimes updates the cluster times for the session and cluster clock attached to this\n// operation. While the session's AdvanceClusterTime may return an error, this method does not\n// because an error being returned from this method will not be returned further up.\nfunc (op Operation) updateClusterTimes(response bsoncore.Document) {\n\t// Extract cluster time.\n\tvalue, err := response.LookupErr(\"$clusterTime\")\n\tif err != nil {\n\t\t// $clusterTime not included by the server\n\t\treturn\n\t}\n\tclusterTime := bsoncore.BuildDocumentFromElements(nil, bsoncore.AppendValueElement(nil, \"$clusterTime\", value))\n\n\tsess, clock := op.Client, op.Clock\n\n\tif sess != nil {\n\t\t_ = sess.AdvanceClusterTime(bson.Raw(clusterTime))\n\t}\n\n\tif clock != nil {\n\t\tclock.AdvanceClusterTime(bson.Raw(clusterTime))\n\t}\n}\n\n// updateOperationTime updates the operation time on the session attached to this operation. While\n// the session's AdvanceOperationTime method may return an error, this method does not because an\n// error being returned from this method will not be returned further up.\nfunc (op Operation) updateOperationTime(response bsoncore.Document) {\n\tsess := op.Client\n\tif sess == nil {\n\t\treturn\n\t}\n\n\topTimeElem, err := response.LookupErr(\"operationTime\")\n\tif err != nil {\n\t\t// operationTime not included by the server\n\t\treturn\n\t}\n\n\tt, i := opTimeElem.Timestamp()\n\t_ = sess.AdvanceOperationTime(&bson.Timestamp{\n\t\tT: t,\n\t\tI: i,\n\t})\n}\n\nfunc (op Operation) getReadPrefBasedOnTransaction() (*readpref.ReadPref, error) {\n\tif op.Client != nil && op.Client.TransactionRunning() {\n\t\t// Transaction's read preference always takes priority\n\t\trp := op.Client.CurrentRp\n\t\t// Reads in a transaction must have read preference primary\n\t\t// This must not be checked in startTransaction\n\t\tif rp != nil && !op.Client.TransactionStarting() && rp.Mode() != readpref.PrimaryMode {\n\t\t\treturn nil, ErrNonPrimaryReadPref\n\t\t}\n\t\treturn rp, nil\n\t}\n\treturn op.ReadPreference, nil\n}\n\n// createReadPref will attempt to create a document with the \"readPreference\"\n// object and various related fields such as \"mode\", \"tags\", and\n// \"maxStalenessSeconds\".\nfunc (op Operation) createReadPref(desc description.SelectedServer, isOpQuery bool) (bsoncore.Document, error) {\n\tif op.omitReadPreference {\n\t\treturn nil, nil\n\t}\n\n\t// TODO(GODRIVER-2231): Instead of checking if isOutputAggregate and desc.Server.WireVersion.Max < 13, somehow check\n\t// TODO if supplied readPreference was \"overwritten\" with primary in description.selectForReplicaSet.\n\tif desc.Server.Kind == description.ServerKindStandalone || (isOpQuery &&\n\t\tdesc.Server.Kind != description.ServerKindMongos) ||\n\t\top.Type == Write || (op.IsOutputAggregate && desc.WireVersion.Max < 13) {\n\t\t// Don't send read preference for:\n\t\t// 1. all standalones\n\t\t// 2. non-mongos when using OP_QUERY\n\t\t// 3. all writes\n\t\t// 4. when operation is an aggregate with an output stage, and selected server's wire\n\t\t//    version is < 13\n\t\treturn nil, nil\n\t}\n\n\tidx, doc := bsoncore.AppendDocumentStart(nil)\n\trp, err := op.getReadPrefBasedOnTransaction()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif rp == nil {\n\t\tif desc.Kind == description.TopologyKindSingle && desc.Server.Kind != description.ServerKindMongos {\n\t\t\tdoc = bsoncore.AppendStringElement(doc, \"mode\", \"primaryPreferred\")\n\t\t\tdoc, _ = bsoncore.AppendDocumentEnd(doc, idx)\n\t\t\treturn doc, nil\n\t\t}\n\t\treturn nil, nil\n\t}\n\n\tswitch rp.Mode() {\n\tcase readpref.PrimaryMode:\n\t\tif desc.Server.Kind == description.ServerKindMongos {\n\t\t\treturn nil, nil\n\t\t}\n\t\tif desc.Kind == description.TopologyKindSingle {\n\t\t\tdoc = bsoncore.AppendStringElement(doc, \"mode\", \"primaryPreferred\")\n\t\t\tdoc, _ = bsoncore.AppendDocumentEnd(doc, idx)\n\t\t\treturn doc, nil\n\t\t}\n\n\t\t// OP_MSG requires never sending read preference \"primary\"\n\t\t// except for topology \"single\".\n\t\t//\n\t\t// It is important to note that although the Go Driver does not\n\t\t// support legacy opcodes, OP_QUERY has different rules for\n\t\t// adding read preference to commands.\n\t\treturn nil, nil\n\tcase readpref.PrimaryPreferredMode:\n\t\tdoc = bsoncore.AppendStringElement(doc, \"mode\", \"primaryPreferred\")\n\tcase readpref.SecondaryPreferredMode:\n\t\t_, ok := rp.MaxStaleness()\n\t\tif desc.Server.Kind == description.ServerKindMongos && isOpQuery && !ok && len(rp.TagSets()) == 0 &&\n\t\t\trp.HedgeEnabled() == nil {\n\n\t\t\treturn nil, nil\n\t\t}\n\t\tdoc = bsoncore.AppendStringElement(doc, \"mode\", \"secondaryPreferred\")\n\tcase readpref.SecondaryMode:\n\t\tdoc = bsoncore.AppendStringElement(doc, \"mode\", \"secondary\")\n\tcase readpref.NearestMode:\n\t\tdoc = bsoncore.AppendStringElement(doc, \"mode\", \"nearest\")\n\t}\n\n\tsets := make([]bsoncore.Document, 0, len(rp.TagSets()))\n\tfor _, ts := range rp.TagSets() {\n\t\ti, set := bsoncore.AppendDocumentStart(nil)\n\t\tfor _, t := range ts {\n\t\t\tset = bsoncore.AppendStringElement(set, t.Name, t.Value)\n\t\t}\n\t\tset, _ = bsoncore.AppendDocumentEnd(set, i)\n\t\tsets = append(sets, set)\n\t}\n\tif len(sets) > 0 {\n\t\tvar aidx int32\n\t\taidx, doc = bsoncore.AppendArrayElementStart(doc, \"tags\")\n\t\tfor i, set := range sets {\n\t\t\tdoc = bsoncore.AppendDocumentElement(doc, strconv.Itoa(i), set)\n\t\t}\n\t\tdoc, _ = bsoncore.AppendArrayEnd(doc, aidx)\n\t}\n\n\tif d, ok := rp.MaxStaleness(); ok {\n\t\tdoc = bsoncore.AppendInt32Element(doc, \"maxStalenessSeconds\", int32(d.Seconds()))\n\t}\n\n\tif hedgeEnabled := rp.HedgeEnabled(); hedgeEnabled != nil {\n\t\tvar hedgeIdx int32\n\t\thedgeIdx, doc = bsoncore.AppendDocumentElementStart(doc, \"hedge\")\n\t\tdoc = bsoncore.AppendBooleanElement(doc, \"enabled\", *hedgeEnabled)\n\t\tdoc, err = bsoncore.AppendDocumentEnd(doc, hedgeIdx)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"error creating hedge document: %w\", err)\n\t\t}\n\t}\n\n\tdoc, _ = bsoncore.AppendDocumentEnd(doc, idx)\n\treturn doc, nil\n}\n\nfunc (op Operation) secondaryOK(desc description.SelectedServer) wiremessage.QueryFlag {\n\tif desc.Kind == description.TopologyKindSingle && desc.Server.Kind != description.ServerKindMongos {\n\t\treturn wiremessage.SecondaryOK\n\t}\n\n\tif rp := op.ReadPreference; rp != nil && rp.Mode() != readpref.PrimaryMode {\n\t\treturn wiremessage.SecondaryOK\n\t}\n\n\treturn 0\n}\n\nfunc (Operation) canCompress(cmd string) bool {\n\tif cmd == handshake.LegacyHello || cmd == \"hello\" || cmd == \"saslStart\" || cmd == \"saslContinue\" || cmd == \"getnonce\" || cmd == \"authenticate\" ||\n\t\tcmd == \"createUser\" || cmd == \"updateUser\" || cmd == \"copydbSaslStart\" || cmd == \"copydbgetnonce\" || cmd == \"copydb\" {\n\t\treturn false\n\t}\n\treturn true\n}\n\n// decodeOpReply extracts the necessary information from an OP_REPLY wire message.\n// Returns the decoded OP_REPLY. If the err field of the returned opReply is non-nil, an error occurred while decoding\n// or validating the response and the other fields are undefined.\nfunc (Operation) decodeOpReply(wm []byte) opReply {\n\tvar reply opReply\n\tvar ok bool\n\n\treply.responseFlags, wm, ok = wiremessage.ReadReplyFlags(wm)\n\tif !ok {\n\t\treply.err = errors.New(\"malformed OP_REPLY: missing flags\")\n\t\treturn reply\n\t}\n\treply.cursorID, wm, ok = wiremessage.ReadReplyCursorID(wm)\n\tif !ok {\n\t\treply.err = errors.New(\"malformed OP_REPLY: missing cursorID\")\n\t\treturn reply\n\t}\n\treply.startingFrom, wm, ok = wiremessage.ReadReplyStartingFrom(wm)\n\tif !ok {\n\t\treply.err = errors.New(\"malformed OP_REPLY: missing startingFrom\")\n\t\treturn reply\n\t}\n\treply.numReturned, wm, ok = wiremessage.ReadReplyNumberReturned(wm)\n\tif !ok {\n\t\treply.err = errors.New(\"malformed OP_REPLY: missing numberReturned\")\n\t\treturn reply\n\t}\n\treply.documents, _, ok = wiremessage.ReadReplyDocuments(wm)\n\tif !ok {\n\t\treply.err = errors.New(\"malformed OP_REPLY: could not read documents from reply\")\n\t}\n\n\tif reply.responseFlags&wiremessage.QueryFailure == wiremessage.QueryFailure {\n\t\treply.err = QueryFailureError{\n\t\t\tMessage:  \"command failure\",\n\t\t\tResponse: reply.documents[0],\n\t\t}\n\t\treturn reply\n\t}\n\tif reply.responseFlags&wiremessage.CursorNotFound == wiremessage.CursorNotFound {\n\t\treply.err = ErrCursorNotFound\n\t\treturn reply\n\t}\n\tif reply.numReturned != int32(len(reply.documents)) {\n\t\treply.err = ErrReplyDocumentMismatch\n\t\treturn reply\n\t}\n\n\treturn reply\n}\n\nfunc (op Operation) decodeResult(opcode wiremessage.OpCode, wm []byte) (bsoncore.Document, error) {\n\tswitch opcode {\n\tcase wiremessage.OpReply:\n\t\treply := op.decodeOpReply(wm)\n\t\tif reply.err != nil {\n\t\t\treturn nil, reply.err\n\t\t}\n\t\tif reply.numReturned == 0 {\n\t\t\treturn nil, ErrNoDocCommandResponse\n\t\t}\n\t\tif reply.numReturned > 1 {\n\t\t\treturn nil, ErrMultiDocCommandResponse\n\t\t}\n\t\trdr := reply.documents[0]\n\t\tif err := rdr.Validate(); err != nil {\n\t\t\treturn nil, NewCommandResponseError(\"malformed OP_REPLY: invalid document\", err)\n\t\t}\n\n\t\treturn rdr, ExtractErrorFromServerResponse(rdr)\n\tcase wiremessage.OpMsg:\n\t\t_, wm, ok := wiremessage.ReadMsgFlags(wm)\n\t\tif !ok {\n\t\t\treturn nil, errors.New(\"malformed wire message: missing OP_MSG flags\")\n\t\t}\n\n\t\tvar res bsoncore.Document\n\t\tfor len(wm) > 0 {\n\t\t\tvar stype wiremessage.SectionType\n\t\t\tstype, wm, ok = wiremessage.ReadMsgSectionType(wm)\n\t\t\tif !ok {\n\t\t\t\treturn nil, errors.New(\"malformed wire message: insuffienct bytes to read section type\")\n\t\t\t}\n\n\t\t\tswitch stype {\n\t\t\tcase wiremessage.SingleDocument:\n\t\t\t\tres, wm, ok = wiremessage.ReadMsgSectionSingleDocument(wm)\n\t\t\t\tif !ok {\n\t\t\t\t\treturn nil, errors.New(\"malformed wire message: insufficient bytes to read single document\")\n\t\t\t\t}\n\t\t\tcase wiremessage.DocumentSequence:\n\t\t\t\t_, _, wm, ok = wiremessage.ReadMsgSectionDocumentSequence(wm)\n\t\t\t\tif !ok {\n\t\t\t\t\treturn nil, errors.New(\"malformed wire message: insufficient bytes to read document sequence\")\n\t\t\t\t}\n\t\t\tdefault:\n\t\t\t\treturn nil, fmt.Errorf(\"malformed wire message: unknown section type %v\", stype)\n\t\t\t}\n\t\t}\n\n\t\terr := res.Validate()\n\t\tif err != nil {\n\t\t\treturn nil, NewCommandResponseError(\"malformed OP_MSG: invalid document\", err)\n\t\t}\n\n\t\treturn res, ExtractErrorFromServerResponse(res)\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"cannot decode result from %s\", opcode)\n\t}\n}\n\n// getCommandName returns the name of the command from the given BSON document.\nfunc (op Operation) getCommandName(doc []byte) string {\n\t// skip 4 bytes for document length and 1 byte for element type\n\tidx := bytes.IndexByte(doc[5:], 0x00) // look for the 0 byte after the command name\n\treturn string(doc[5 : idx+5])\n}\n\nfunc (op *Operation) redactCommand(cmd string, doc bsoncore.Document) bool {\n\tif cmd == \"authenticate\" || cmd == \"saslStart\" || cmd == \"saslContinue\" || cmd == \"getnonce\" || cmd == \"createUser\" ||\n\t\tcmd == \"updateUser\" || cmd == \"copydbgetnonce\" || cmd == \"copydbsaslstart\" || cmd == \"copydb\" {\n\n\t\treturn true\n\t}\n\tif strings.ToLower(cmd) != handshake.LegacyHelloLowercase && cmd != \"hello\" {\n\t\treturn false\n\t}\n\n\t// A hello without speculative authentication can be monitored.\n\t_, err := doc.LookupErr(\"speculativeAuthenticate\")\n\treturn err == nil\n}\n\n// canLogCommandMessage returns true if the command can be logged.\nfunc (op Operation) canLogCommandMessage() bool {\n\treturn op.Logger != nil && op.Logger.LevelComponentEnabled(logger.LevelDebug, logger.ComponentCommand)\n}\n\nfunc (op Operation) canPublishStartedEvent() bool {\n\treturn op.CommandMonitor != nil && op.CommandMonitor.Started != nil\n}\n\n// publishStartedEvent publishes a CommandStartedEvent to the operation's command monitor if possible. If the command is\n// an unacknowledged write, a CommandSucceededEvent will be published as well. If started events are not being monitored,\n// no events are published.\nfunc (op Operation) publishStartedEvent(ctx context.Context, info startedInformation) {\n\t// If logging is enabled for the command component at the debug level, log the command response.\n\tif op.canLogCommandMessage() {\n\t\thost, port, _ := net.SplitHostPort(info.serverAddress.String())\n\n\t\tredactedCmd := redactStartedInformationCmd(info)\n\t\tformattedCmd := logger.FormatDocument(redactedCmd, op.Logger.MaxDocumentLength)\n\n\t\top.Logger.Print(logger.LevelDebug,\n\t\t\tlogger.ComponentCommand,\n\t\t\tlogger.CommandStarted,\n\t\t\tlogger.SerializeCommand(logger.Command{\n\t\t\t\tDriverConnectionID: info.driverConnectionID,\n\t\t\t\tMessage:            logger.CommandStarted,\n\t\t\t\tName:               info.cmdName,\n\t\t\t\tDatabaseName:       op.Database,\n\t\t\t\tRequestID:          int64(info.requestID),\n\t\t\t\tServerConnectionID: info.serverConnID,\n\t\t\t\tServerHost:         host,\n\t\t\t\tServerPort:         port,\n\t\t\t\tServiceID:          info.serviceID,\n\t\t\t},\n\t\t\t\tlogger.KeyCommand, formattedCmd)...)\n\n\t}\n\n\tif op.canPublishStartedEvent() {\n\t\tstarted := &event.CommandStartedEvent{\n\t\t\tCommand:            redactStartedInformationCmd(info),\n\t\t\tDatabaseName:       op.Database,\n\t\t\tCommandName:        info.cmdName,\n\t\t\tRequestID:          int64(info.requestID),\n\t\t\tConnectionID:       info.connID,\n\t\t\tServerConnectionID: info.serverConnID,\n\t\t\tServiceID:          info.serviceID,\n\t\t}\n\t\top.CommandMonitor.Started(ctx, started)\n\t}\n}\n\n// canPublishFinishedEvent returns true if a CommandSucceededEvent can be\n// published for the given command. This is true if the command is not an\n// unacknowledged write and the command monitor is monitoring succeeded events.\nfunc (op Operation) canPublishFinishedEvent(info finishedInformation) bool {\n\tsuccess := info.success()\n\n\treturn op.CommandMonitor != nil &&\n\t\t(!success || op.CommandMonitor.Succeeded != nil) &&\n\t\t(success || op.CommandMonitor.Failed != nil)\n}\n\n// publishFinishedEvent publishes either a CommandSucceededEvent or a CommandFailedEvent to the operation's command\n// monitor if possible. If success/failure events aren't being monitored, no events are published.\nfunc (op Operation) publishFinishedEvent(ctx context.Context, info finishedInformation) {\n\tif op.canLogCommandMessage() && info.success() {\n\t\thost, port, _ := net.SplitHostPort(info.serverAddress.String())\n\n\t\tredactedReply := redactFinishedInformationResponse(info)\n\n\t\tformattedReply := logger.FormatDocument(redactedReply, op.Logger.MaxDocumentLength)\n\n\t\top.Logger.Print(logger.LevelDebug,\n\t\t\tlogger.ComponentCommand,\n\t\t\tlogger.CommandSucceeded,\n\t\t\tlogger.SerializeCommand(logger.Command{\n\t\t\t\tDriverConnectionID: info.driverConnectionID,\n\t\t\t\tMessage:            logger.CommandSucceeded,\n\t\t\t\tName:               info.cmdName,\n\t\t\t\tDatabaseName:       op.Database,\n\t\t\t\tRequestID:          int64(info.requestID),\n\t\t\t\tServerConnectionID: info.serverConnID,\n\t\t\t\tServerHost:         host,\n\t\t\t\tServerPort:         port,\n\t\t\t\tServiceID:          info.serviceID,\n\t\t\t},\n\t\t\t\tlogger.KeyDurationMS, info.duration.Milliseconds(),\n\t\t\t\tlogger.KeyReply, formattedReply)...)\n\t}\n\n\tif op.canLogCommandMessage() && !info.success() {\n\t\thost, port, _ := net.SplitHostPort(info.serverAddress.String())\n\n\t\tformattedReply := logger.FormatString(info.cmdErr.Error(), op.Logger.MaxDocumentLength)\n\n\t\top.Logger.Print(logger.LevelDebug,\n\t\t\tlogger.ComponentCommand,\n\t\t\tlogger.CommandFailed,\n\t\t\tlogger.SerializeCommand(logger.Command{\n\t\t\t\tDriverConnectionID: info.driverConnectionID,\n\t\t\t\tMessage:            logger.CommandFailed,\n\t\t\t\tName:               info.cmdName,\n\t\t\t\tDatabaseName:       op.Database,\n\t\t\t\tRequestID:          int64(info.requestID),\n\t\t\t\tServerConnectionID: info.serverConnID,\n\t\t\t\tServerHost:         host,\n\t\t\t\tServerPort:         port,\n\t\t\t\tServiceID:          info.serviceID,\n\t\t\t},\n\t\t\t\tlogger.KeyDurationMS, info.duration.Milliseconds(),\n\t\t\t\tlogger.KeyFailure, formattedReply)...)\n\t}\n\n\t// If the finished event cannot be published, return early.\n\tif !op.canPublishFinishedEvent(info) {\n\t\treturn\n\t}\n\n\tfinished := event.CommandFinishedEvent{\n\t\tCommandName:        info.cmdName,\n\t\tDatabaseName:       op.Database,\n\t\tRequestID:          int64(info.requestID),\n\t\tConnectionID:       info.connID,\n\t\tDuration:           info.duration,\n\t\tServerConnectionID: info.serverConnID,\n\t\tServiceID:          info.serviceID,\n\t}\n\n\tif info.success() {\n\t\tsuccessEvent := &event.CommandSucceededEvent{\n\t\t\tReply:                redactFinishedInformationResponse(info),\n\t\t\tCommandFinishedEvent: finished,\n\t\t}\n\t\top.CommandMonitor.Succeeded(ctx, successEvent)\n\n\t\treturn\n\t}\n\n\tfailedEvent := &event.CommandFailedEvent{\n\t\tFailure:              info.cmdErr,\n\t\tCommandFinishedEvent: finished,\n\t}\n\top.CommandMonitor.Failed(ctx, failedEvent)\n}\n\n// sessionsSupported returns true of the given server version indicates that it supports sessions.\nfunc sessionsSupported(wireVersion *description.VersionRange) bool {\n\treturn wireVersion != nil\n}\n\n// retryWritesSupported returns true if this description represents a server that supports retryable writes.\nfunc retryWritesSupported(s description.Server) bool {\n\treturn s.SessionTimeoutMinutes != nil && s.Kind != description.ServerKindStandalone\n}\n\n// appendDocumentArray will append an array header and document elements in data to dst and return the extended buffer.\nfunc appendDocumentArray(dst []byte, key string, data []byte) []byte {\n\taidx, dst := bsoncore.AppendArrayElementStart(dst, key)\n\tvar doc bsoncore.Document\n\tvar ok bool\n\ti := 0\n\tfor {\n\t\tdoc, data, ok = bsoncore.ReadDocument(data)\n\t\tif !ok {\n\t\t\tbreak\n\t\t}\n\t\tdst = bsoncore.AppendDocumentElement(dst, strconv.Itoa(i), doc)\n\t\ti++\n\t}\n\n\tdst, _ = bsoncore.AppendArrayEnd(dst, aidx)\n\n\treturn dst\n}\n"
  },
  {
    "path": "x/mongo/driver/operation_exhaust.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage driver\n\nimport (\n\t\"context\"\n\t\"errors\"\n\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/mnet\"\n)\n\n// ExecuteExhaust reads a response from the provided StreamerConnection. This will error if the connection's\n// CurrentlyStreaming function returns false.\nfunc (op Operation) ExecuteExhaust(ctx context.Context, conn *mnet.Connection) error {\n\tif !conn.CurrentlyStreaming() {\n\t\treturn errors.New(\"exhaust read must be done with a connection that is currently streaming\")\n\t}\n\n\tres, err := op.readWireMessage(ctx, conn)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif op.ProcessResponseFn != nil {\n\t\t// Server, ConnectionDescription, and CurrentIndex are unused in this mode.\n\t\tinfo := ResponseInfo{\n\t\t\tConnection: conn,\n\t\t}\n\t\tif err = op.ProcessResponseFn(ctx, res, info); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "x/mongo/driver/operation_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2022-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage driver\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"errors\"\n\t\"strconv\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/google/go-cmp/cmp\"\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/csot\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/handshake\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/serverselector\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/uuid\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/address\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/readconcern\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/readpref\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/writeconcern\"\n\t\"go.mongodb.org/mongo-driver/v2/tag\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/description\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/mnet\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/session\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/wiremessage\"\n)\n\nfunc noerr(t *testing.T, err error) {\n\tt.Helper()\n\tif err != nil {\n\t\tt.Errorf(\"Unexpected error: %v\", err)\n\t\tt.FailNow()\n\t}\n}\n\nfunc compareErrors(err1, err2 error) bool {\n\tif err1 == nil && err2 == nil {\n\t\treturn true\n\t}\n\n\tif err1 == nil || err2 == nil {\n\t\treturn false\n\t}\n\n\tif err1.Error() != err2.Error() {\n\t\treturn false\n\t}\n\n\treturn true\n}\n\nfunc TestOperation(t *testing.T) {\n\tint64ToPtr := func(i64 int64) *int64 { return &i64 }\n\n\tt.Run(\"selectServer\", func(t *testing.T) {\n\t\tt.Run(\"returns validation error\", func(t *testing.T) {\n\t\t\top := &Operation{}\n\t\t\t_, err := op.selectServer(context.Background(), 1, nil)\n\t\t\tif err == nil {\n\t\t\t\tt.Error(\"Expected a validation error from selectServer, but got <nil>\")\n\t\t\t}\n\t\t})\n\t\tt.Run(\"uses specified server selector\", func(t *testing.T) {\n\t\t\twant := new(mockServerSelector)\n\t\t\td := new(mockDeployment)\n\t\t\top := &Operation{\n\t\t\t\tCommandFn:  func([]byte, description.SelectedServer) ([]byte, error) { return nil, nil },\n\t\t\t\tDeployment: d,\n\t\t\t\tDatabase:   \"testing\",\n\t\t\t\tSelector:   want,\n\t\t\t}\n\t\t\t_, err := op.selectServer(context.Background(), 1, nil)\n\t\t\tnoerr(t, err)\n\n\t\t\t// Selector is always wrapped with Deprioritized, even when deprioritized is nil.\n\t\t\tif _, ok := d.params.selector.(*serverselector.Deprioritized); !ok {\n\t\t\t\tt.Errorf(\"Expected Deprioritized wrapper around server selector, got %T\", d.params.selector)\n\t\t\t}\n\t\t})\n\t\tt.Run(\"uses a default server selector\", func(t *testing.T) {\n\t\t\td := new(mockDeployment)\n\t\t\top := &Operation{\n\t\t\t\tCommandFn:  func([]byte, description.SelectedServer) ([]byte, error) { return nil, nil },\n\t\t\t\tDeployment: d,\n\t\t\t\tDatabase:   \"testing\",\n\t\t\t}\n\t\t\t_, err := op.selectServer(context.Background(), 1, nil)\n\t\t\tnoerr(t, err)\n\t\t\tif d.params.selector == nil {\n\t\t\t\tt.Error(\"The selectServer method should use a default selector when not specified on Operation, but it passed <nil>.\")\n\t\t\t}\n\t\t})\n\t})\n\tt.Run(\"Validate\", func(t *testing.T) {\n\t\tcmdFn := func([]byte, description.SelectedServer) ([]byte, error) { return nil, nil }\n\t\td := new(mockDeployment)\n\t\ttestCases := []struct {\n\t\t\tname string\n\t\t\top   *Operation\n\t\t\terr  error\n\t\t}{\n\t\t\t{\"CommandFn\", &Operation{}, InvalidOperationError{MissingField: \"CommandFn\"}},\n\t\t\t{\"Deployment\", &Operation{CommandFn: cmdFn}, InvalidOperationError{MissingField: \"Deployment\"}},\n\t\t\t{\"Database\", &Operation{CommandFn: cmdFn, Deployment: d}, errDatabaseNameEmpty},\n\t\t\t{\"<nil>\", &Operation{CommandFn: cmdFn, Deployment: d, Database: \"test\"}, nil},\n\t\t}\n\n\t\tfor _, tc := range testCases {\n\t\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\t\tif tc.op == nil {\n\t\t\t\t\tt.Fatal(\"op cannot be <nil>\")\n\t\t\t\t}\n\t\t\t\twant := tc.err\n\t\t\t\tgot := tc.op.Validate()\n\t\t\t\tif !cmp.Equal(got, want, cmp.Comparer(compareErrors)) {\n\t\t\t\t\tt.Errorf(\"Did not validate properly. got %v; want %v\", got, want)\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t})\n\tt.Run(\"retryableWrite\", func(t *testing.T) {\n\t\tsessPool := session.NewPool(nil)\n\t\tid, err := uuid.New()\n\t\tnoerr(t, err)\n\n\t\tsess, err := session.NewClientSession(sessPool, id)\n\t\tnoerr(t, err)\n\n\t\tsessStartingTransaction, err := session.NewClientSession(sessPool, id)\n\t\tnoerr(t, err)\n\t\terr = sessStartingTransaction.StartTransaction(nil)\n\t\tnoerr(t, err)\n\n\t\tsessInProgressTransaction, err := session.NewClientSession(sessPool, id)\n\t\tnoerr(t, err)\n\t\terr = sessInProgressTransaction.StartTransaction(nil)\n\t\tnoerr(t, err)\n\t\terr = sessInProgressTransaction.ApplyCommand(description.Server{})\n\t\tnoerr(t, err)\n\n\t\twcAck := writeconcern.Majority()\n\t\twcUnack := writeconcern.Unacknowledged()\n\n\t\tdescRetryable := description.Server{\n\t\t\tWireVersion:           &description.VersionRange{Min: 6, Max: 21},\n\t\t\tSessionTimeoutMinutes: int64ToPtr(1),\n\t\t}\n\n\t\tdescNotRetryableWireVersion := description.Server{\n\t\t\tWireVersion:           &description.VersionRange{Min: 6, Max: 21},\n\t\t\tSessionTimeoutMinutes: int64ToPtr(1),\n\t\t}\n\n\t\tdescNotRetryableStandalone := description.Server{\n\t\t\tWireVersion:           &description.VersionRange{Min: 6, Max: 21},\n\t\t\tSessionTimeoutMinutes: int64ToPtr(1),\n\t\t\tKind:                  description.ServerKindStandalone,\n\t\t}\n\n\t\ttestCases := []struct {\n\t\t\tname string\n\t\t\top   Operation\n\t\t\tdesc description.Server\n\t\t\twant Type\n\t\t}{\n\t\t\t{\"deployment doesn't support\", Operation{}, description.Server{}, Type(0)},\n\t\t\t{\"wire version too low\", Operation{Client: sess, WriteConcern: wcAck}, descNotRetryableWireVersion, Type(0)},\n\t\t\t{\"standalone not supported\", Operation{Client: sess, WriteConcern: wcAck}, descNotRetryableStandalone, Type(0)},\n\t\t\t{\n\t\t\t\t\"transaction in progress\",\n\t\t\t\tOperation{Client: sessInProgressTransaction, WriteConcern: wcAck},\n\t\t\t\tdescRetryable, Type(0),\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"transaction starting\",\n\t\t\t\tOperation{Client: sessStartingTransaction, WriteConcern: wcAck},\n\t\t\t\tdescRetryable, Type(0),\n\t\t\t},\n\t\t\t{\"unacknowledged write concern\", Operation{Client: sess, WriteConcern: wcUnack}, descRetryable, Type(0)},\n\t\t\t{\n\t\t\t\t\"acknowledged write concern\",\n\t\t\t\tOperation{Client: sess, WriteConcern: wcAck, Type: Write},\n\t\t\t\tdescRetryable, Write,\n\t\t\t},\n\t\t}\n\n\t\tfor _, tc := range testCases {\n\t\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\t\tgot := tc.op.retryable(tc.desc)\n\t\t\t\tif got != (tc.want != Type(0)) {\n\t\t\t\t\tt.Errorf(\"Did not receive expected Type. got %v; want %v\", got, tc.want)\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t})\n\tt.Run(\"addReadConcern\", func(t *testing.T) {\n\t\tmajorityRc := bsoncore.AppendDocumentElement(nil, \"readConcern\", bsoncore.BuildDocument(nil,\n\t\t\tbsoncore.AppendStringElement(nil, \"level\", \"majority\"),\n\t\t))\n\n\t\ttestCases := []struct {\n\t\t\tname string\n\t\t\trc   *readconcern.ReadConcern\n\t\t\twant bsoncore.Document\n\t\t}{\n\t\t\t{\"nil\", nil, nil},\n\t\t\t{\"empty\", &readconcern.ReadConcern{}, nil},\n\t\t\t{\"non-empty\", readconcern.Majority(), majorityRc},\n\t\t}\n\n\t\tfor _, tc := range testCases {\n\t\t\tgot, err := Operation{ReadConcern: tc.rc}.addReadConcern(nil, description.SelectedServer{})\n\t\t\tnoerr(t, err)\n\t\t\tif !bytes.Equal(got, tc.want) {\n\t\t\t\tt.Errorf(\"ReadConcern elements do not match. got %v; want %v\", got, tc.want)\n\t\t\t}\n\t\t}\n\t})\n\tt.Run(\"addWriteConcern\", func(t *testing.T) {\n\t\twant := bsoncore.AppendDocumentElement(nil, \"writeConcern\", bsoncore.BuildDocumentFromElements(\n\t\t\tnil, bsoncore.AppendStringElement(nil, \"w\", \"majority\"),\n\t\t))\n\t\tgot, err := Operation{WriteConcern: writeconcern.Majority()}.\n\t\t\taddWriteConcern(context.Background(), nil, description.SelectedServer{})\n\t\tnoerr(t, err)\n\t\tif !bytes.Equal(got, want) {\n\t\t\tt.Errorf(\"WriteConcern elements do not match. got %v; want %v\", got, want)\n\t\t}\n\t})\n\tt.Run(\"addSession\", func(t *testing.T) { t.Skip(\"These tests should be covered by spec tests.\") })\n\tt.Run(\"addClusterTime\", func(t *testing.T) {\n\t\tt.Run(\"adds max cluster time\", func(t *testing.T) {\n\t\t\twant := bsoncore.AppendDocumentElement(nil, \"$clusterTime\", bsoncore.BuildDocumentFromElements(nil,\n\t\t\t\tbsoncore.AppendTimestampElement(nil, \"clusterTime\", 1234, 5678),\n\t\t\t))\n\t\t\tnewer := bsoncore.BuildDocumentFromElements(nil, want)\n\t\t\tolder := bsoncore.BuildDocumentFromElements(nil,\n\t\t\t\tbsoncore.AppendDocumentElement(nil, \"$clusterTime\", bsoncore.BuildDocumentFromElements(nil,\n\t\t\t\t\tbsoncore.AppendTimestampElement(nil, \"clusterTime\", 1234, 5670),\n\t\t\t\t)),\n\t\t\t)\n\n\t\t\tclusterClock := new(session.ClusterClock)\n\t\t\tclusterClock.AdvanceClusterTime(newer)\n\t\t\tsessPool := session.NewPool(nil)\n\t\t\tid, err := uuid.New()\n\t\t\tnoerr(t, err)\n\n\t\t\tsess, err := session.NewClientSession(sessPool, id)\n\t\t\tnoerr(t, err)\n\t\t\terr = sess.AdvanceClusterTime(older)\n\t\t\tnoerr(t, err)\n\n\t\t\tgot := Operation{Client: sess, Clock: clusterClock}.addClusterTime(nil, description.SelectedServer{\n\t\t\t\tServer: description.Server{WireVersion: &description.VersionRange{Min: 6, Max: 21}},\n\t\t\t})\n\t\t\tif !bytes.Equal(got, want) {\n\t\t\t\tt.Errorf(\"ClusterTimes do not match. got %v; want %v\", got, want)\n\t\t\t}\n\t\t})\n\t})\n\tt.Run(\"calculateMaxTimeMS\", func(t *testing.T) {\n\t\tvar (\n\t\t\ttimeout  = 5 * time.Second\n\t\t\tshortRTT = 50 * time.Millisecond\n\t\t\tlongRTT  = 10 * time.Second\n\t\t)\n\n\t\ttimeoutCtx, cancel := csot.WithTimeout(context.Background(), &timeout)\n\t\tdefer cancel()\n\n\t\ttestCases := []struct {\n\t\t\tname     string\n\t\t\top       Operation\n\t\t\tctx      context.Context\n\t\t\trtt      RTTMonitor\n\t\t\trttMin   time.Duration\n\t\t\trttStats string\n\t\t\twant     int64\n\t\t\terr      error\n\t\t}{\n\t\t\t{\n\t\t\t\tname:     \"uses context deadline and rtt90 with timeout\",\n\t\t\t\tctx:      timeoutCtx,\n\t\t\t\trttMin:   shortRTT,\n\t\t\t\trttStats: \"\",\n\t\t\t\twant:     5000,\n\t\t\t\terr:      nil,\n\t\t\t},\n\t\t\t{\n\t\t\t\tname:     \"sub millisecond rtt should round up\",\n\t\t\t\tctx:      context.Background(),\n\t\t\t\trttMin:   longRTT,\n\t\t\t\trttStats: \"\",\n\t\t\t\twant:     1,\n\t\t\t\terr:      nil,\n\t\t\t},\n\t\t}\n\t\tfor _, tc := range testCases {\n\t\t\t// Capture test-case for parallel sub-test.\n\t\t\ttc := tc\n\t\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\t\tt.Parallel()\n\n\t\t\t\tgot, err := tc.op.calculateMaxTimeMS(tc.ctx, tc.rttMin, tc.rttStats)\n\n\t\t\t\t// Assert that the calculated maxTimeMS is less than or equal to the expected value. A few\n\t\t\t\t// milliseconds will have elapsed toward the context deadline, and (remainingTimeout\n\t\t\t\t// - rtt90) will be slightly smaller than the expected value.\n\t\t\t\tif got > tc.want {\n\t\t\t\t\tt.Errorf(\"maxTimeMS value higher than expected. got %v; wanted at most %v\", got, tc.want)\n\t\t\t\t}\n\t\t\t\tif !errors.Is(err, tc.err) {\n\t\t\t\t\tt.Errorf(\"error values do not match. got %v; want %v\", err, tc.err)\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t})\n\tt.Run(\"updateClusterTimes\", func(t *testing.T) {\n\t\tclustertime := bsoncore.BuildDocumentFromElements(nil,\n\t\t\tbsoncore.AppendDocumentElement(nil, \"$clusterTime\", bsoncore.BuildDocumentFromElements(nil,\n\t\t\t\tbsoncore.AppendTimestampElement(nil, \"clusterTime\", 1234, 5678),\n\t\t\t)),\n\t\t)\n\n\t\tclusterClock := new(session.ClusterClock)\n\t\tsessPool := session.NewPool(nil)\n\t\tid, err := uuid.New()\n\t\tnoerr(t, err)\n\n\t\tsess, err := session.NewClientSession(sessPool, id)\n\t\tnoerr(t, err)\n\t\tOperation{Client: sess, Clock: clusterClock}.updateClusterTimes(clustertime)\n\n\t\tgot := sess.ClusterTime\n\t\tif !bytes.Equal(got, clustertime) {\n\t\t\tt.Errorf(\"ClusterTimes do not match. got %v; want %v\", got, clustertime)\n\t\t}\n\t\tgot = clusterClock.GetClusterTime()\n\t\tif !bytes.Equal(got, clustertime) {\n\t\t\tt.Errorf(\"ClusterTimes do not match. got %v; want %v\", got, clustertime)\n\t\t}\n\n\t\tOperation{}.updateClusterTimes(bsoncore.BuildDocumentFromElements(nil)) // should do nothing\n\t})\n\tt.Run(\"updateOperationTime\", func(t *testing.T) {\n\t\twant := bson.Timestamp{T: 1234, I: 4567}\n\n\t\tsessPool := session.NewPool(nil)\n\t\tid, err := uuid.New()\n\t\tnoerr(t, err)\n\n\t\tsess, err := session.NewClientSession(sessPool, id)\n\t\tnoerr(t, err)\n\t\tif sess.OperationTime != nil {\n\t\t\tt.Fatal(\"OperationTime should not be set on new session.\")\n\t\t}\n\t\tresponse := bsoncore.BuildDocumentFromElements(nil, bsoncore.AppendTimestampElement(nil, \"operationTime\", want.T, want.I))\n\t\tOperation{Client: sess}.updateOperationTime(response)\n\t\tgot := sess.OperationTime\n\t\tif got.T != want.T || got.I != want.I {\n\t\t\tt.Errorf(\"OperationTimes do not match. got %v; want %v\", got, want)\n\t\t}\n\n\t\tresponse = bsoncore.BuildDocumentFromElements(nil)\n\t\tOperation{Client: sess}.updateOperationTime(response)\n\t\tgot = sess.OperationTime\n\t\tif got.T != want.T || got.I != want.I {\n\t\t\tt.Errorf(\"OperationTimes do not match. got %v; want %v\", got, want)\n\t\t}\n\n\t\tOperation{}.updateOperationTime(response) // should do nothing\n\t})\n\tt.Run(\"createReadPref\", func(t *testing.T) {\n\t\trpWithTags := bsoncore.BuildDocumentFromElements(nil,\n\t\t\tbsoncore.AppendStringElement(nil, \"mode\", \"secondaryPreferred\"),\n\t\t\tbsoncore.BuildArrayElement(nil, \"tags\",\n\t\t\t\tbsoncore.Value{\n\t\t\t\t\tType: bsoncore.TypeEmbeddedDocument,\n\t\t\t\t\tData: bsoncore.BuildDocumentFromElements(nil,\n\t\t\t\t\t\tbsoncore.AppendStringElement(nil, \"disk\", \"ssd\"),\n\t\t\t\t\t\tbsoncore.AppendStringElement(nil, \"use\", \"reporting\"),\n\t\t\t\t\t),\n\t\t\t\t},\n\t\t\t),\n\t\t)\n\t\trpWithMaxStaleness := bsoncore.BuildDocumentFromElements(nil,\n\t\t\tbsoncore.AppendStringElement(nil, \"mode\", \"secondaryPreferred\"),\n\t\t\tbsoncore.AppendInt32Element(nil, \"maxStalenessSeconds\", 25),\n\t\t)\n\t\t// Hedged read preference: {mode: \"secondaryPreferred\", hedge: {enabled: true}}\n\t\trpWithHedge := bsoncore.BuildDocumentFromElements(nil,\n\t\t\tbsoncore.AppendStringElement(nil, \"mode\", \"secondaryPreferred\"),\n\t\t\tbsoncore.AppendDocumentElement(nil, \"hedge\", bsoncore.BuildDocumentFromElements(nil,\n\t\t\t\tbsoncore.AppendBooleanElement(nil, \"enabled\", true),\n\t\t\t)),\n\t\t)\n\t\trpWithAllOptions := bsoncore.BuildDocumentFromElements(nil,\n\t\t\tbsoncore.AppendStringElement(nil, \"mode\", \"secondaryPreferred\"),\n\t\t\tbsoncore.BuildArrayElement(nil, \"tags\",\n\t\t\t\tbsoncore.Value{\n\t\t\t\t\tType: bsoncore.TypeEmbeddedDocument,\n\t\t\t\t\tData: bsoncore.BuildDocumentFromElements(nil,\n\t\t\t\t\t\tbsoncore.AppendStringElement(nil, \"disk\", \"ssd\"),\n\t\t\t\t\t\tbsoncore.AppendStringElement(nil, \"use\", \"reporting\"),\n\t\t\t\t\t),\n\t\t\t\t},\n\t\t\t),\n\t\t\tbsoncore.AppendInt32Element(nil, \"maxStalenessSeconds\", 25),\n\t\t\tbsoncore.AppendDocumentElement(nil, \"hedge\", bsoncore.BuildDocumentFromElements(nil,\n\t\t\t\tbsoncore.AppendBooleanElement(nil, \"enabled\", false),\n\t\t\t)),\n\t\t)\n\n\t\trpPrimaryPreferred := bsoncore.BuildDocumentFromElements(nil, bsoncore.AppendStringElement(nil, \"mode\", \"primaryPreferred\"))\n\t\trpSecondaryPreferred := bsoncore.BuildDocumentFromElements(nil, bsoncore.AppendStringElement(nil, \"mode\", \"secondaryPreferred\"))\n\t\trpSecondary := bsoncore.BuildDocumentFromElements(nil, bsoncore.AppendStringElement(nil, \"mode\", \"secondary\"))\n\t\trpNearest := bsoncore.BuildDocumentFromElements(nil, bsoncore.AppendStringElement(nil, \"mode\", \"nearest\"))\n\n\t\ttestCases := []struct {\n\t\t\tname       string\n\t\t\trp         *readpref.ReadPref\n\t\t\tserverKind description.ServerKind\n\t\t\ttopoKind   description.TopologyKind\n\t\t\topQuery    bool\n\t\t\twant       bsoncore.Document\n\t\t}{\n\t\t\t{\"nil/single/mongos\", nil, description.ServerKindMongos, description.TopologyKindSingle, false, nil},\n\t\t\t{\"nil/single/secondary\", nil, description.ServerKindRSSecondary, description.TopologyKindSingle, false, rpPrimaryPreferred},\n\t\t\t{\"primary/mongos\", readpref.Primary(), description.ServerKindMongos, description.TopologyKindSharded, false, nil},\n\t\t\t{\"primary/single\", readpref.Primary(), description.ServerKindRSPrimary, description.TopologyKindSingle, false, rpPrimaryPreferred},\n\t\t\t{\"primary/primary\", readpref.Primary(), description.ServerKindRSPrimary, description.TopologyKindReplicaSet, false, nil},\n\t\t\t{\"primaryPreferred\", readpref.PrimaryPreferred(), description.ServerKindRSSecondary, description.TopologyKindReplicaSet, false, rpPrimaryPreferred},\n\t\t\t{\"secondaryPreferred/mongos/opquery\", readpref.SecondaryPreferred(), description.ServerKindMongos, description.TopologyKindSharded, true, nil},\n\t\t\t{\"secondaryPreferred\", readpref.SecondaryPreferred(), description.ServerKindRSSecondary, description.TopologyKindReplicaSet, false, rpSecondaryPreferred},\n\t\t\t{\"secondary\", readpref.Secondary(), description.ServerKindRSSecondary, description.TopologyKindReplicaSet, false, rpSecondary},\n\t\t\t{\"nearest\", readpref.Nearest(), description.ServerKindRSSecondary, description.TopologyKindReplicaSet, false, rpNearest},\n\t\t\t{\n\t\t\t\t\"secondaryPreferred/withTags\",\n\t\t\t\treadpref.SecondaryPreferred(readpref.WithTags(\"disk\", \"ssd\", \"use\", \"reporting\")),\n\t\t\t\tdescription.ServerKindRSSecondary, description.TopologyKindReplicaSet, false, rpWithTags,\n\t\t\t},\n\t\t\t// GODRIVER-2205: Ensure empty tag sets are written as an empty document in the read\n\t\t\t// preference document. Empty tag sets match any server and are used as a fallback when\n\t\t\t// no other tag sets match any servers.\n\t\t\t{\n\t\t\t\t\"secondaryPreferred/withTags/emptyTagSet\",\n\t\t\t\treadpref.SecondaryPreferred(readpref.WithTagSets(\n\t\t\t\t\ttag.Set{{Name: \"disk\", Value: \"ssd\"}},\n\t\t\t\t\ttag.Set{})),\n\t\t\t\tdescription.ServerKindRSSecondary,\n\t\t\t\tdescription.TopologyKindReplicaSet,\n\t\t\t\tfalse,\n\t\t\t\tbsoncore.NewDocumentBuilder().\n\t\t\t\t\tAppendString(\"mode\", \"secondaryPreferred\").\n\t\t\t\t\tAppendArray(\"tags\", bsoncore.NewArrayBuilder().\n\t\t\t\t\t\tAppendDocument(bsoncore.NewDocumentBuilder().AppendString(\"disk\", \"ssd\").Build()).\n\t\t\t\t\t\tAppendDocument(bsoncore.NewDocumentBuilder().Build()).\n\t\t\t\t\t\tBuild()).\n\t\t\t\t\tBuild(),\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"secondaryPreferred/withMaxStaleness\",\n\t\t\t\treadpref.SecondaryPreferred(readpref.WithMaxStaleness(25 * time.Second)),\n\t\t\t\tdescription.ServerKindRSSecondary, description.TopologyKindReplicaSet, false, rpWithMaxStaleness,\n\t\t\t},\n\t\t\t{\n\t\t\t\t// A read preference document is generated for SecondaryPreferred if the hedge document is non-nil.\n\t\t\t\t\"secondaryPreferred with hedge to mongos using OP_QUERY\",\n\t\t\t\treadpref.SecondaryPreferred(readpref.WithHedgeEnabled(true)),\n\t\t\t\tdescription.ServerKindMongos,\n\t\t\t\tdescription.TopologyKindSharded,\n\t\t\t\ttrue,\n\t\t\t\trpWithHedge,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"secondaryPreferred with all options\",\n\t\t\t\treadpref.SecondaryPreferred(\n\t\t\t\t\treadpref.WithTags(\"disk\", \"ssd\", \"use\", \"reporting\"),\n\t\t\t\t\treadpref.WithMaxStaleness(25*time.Second),\n\t\t\t\t\treadpref.WithHedgeEnabled(false),\n\t\t\t\t),\n\t\t\t\tdescription.ServerKindRSSecondary,\n\t\t\t\tdescription.TopologyKindReplicaSet,\n\t\t\t\tfalse,\n\t\t\t\trpWithAllOptions,\n\t\t\t},\n\t\t}\n\n\t\tfor _, tc := range testCases {\n\t\t\ttc := tc\n\t\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\t\tdesc := description.SelectedServer{Kind: tc.topoKind, Server: description.Server{Kind: tc.serverKind}}\n\t\t\t\tgot, err := Operation{ReadPreference: tc.rp}.createReadPref(desc, tc.opQuery)\n\t\t\t\tif err != nil {\n\t\t\t\t\tt.Fatalf(\"error creating read pref: %v\", err)\n\t\t\t\t}\n\t\t\t\tif !bytes.Equal(got, tc.want) {\n\t\t\t\t\tt.Errorf(\"Returned documents do not match. got %v; want %v\", got, tc.want)\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t})\n\tt.Run(\"secondaryOK\", func(t *testing.T) {\n\t\tt.Run(\"description.SelectedServer\", func(t *testing.T) {\n\t\t\twant := wiremessage.SecondaryOK\n\t\t\tdesc := description.SelectedServer{\n\t\t\t\tKind:   description.TopologyKindSingle,\n\t\t\t\tServer: description.Server{Kind: description.ServerKindRSSecondary},\n\t\t\t}\n\t\t\tgot := Operation{}.secondaryOK(desc)\n\t\t\tif got != want {\n\t\t\t\tt.Errorf(\"Did not receive expected query flags. got %v; want %v\", got, want)\n\t\t\t}\n\t\t})\n\t\tt.Run(\"readPreference\", func(t *testing.T) {\n\t\t\twant := wiremessage.SecondaryOK\n\t\t\tgot := Operation{ReadPreference: readpref.Secondary()}.secondaryOK(description.SelectedServer{})\n\t\t\tif got != want {\n\t\t\t\tt.Errorf(\"Did not receive expected query flags. got %v; want %v\", got, want)\n\t\t\t}\n\t\t})\n\t\tt.Run(\"not secondaryOK\", func(t *testing.T) {\n\t\t\tvar want wiremessage.QueryFlag\n\t\t\tgot := Operation{}.secondaryOK(description.SelectedServer{})\n\t\t\tif got != want {\n\t\t\t\tt.Errorf(\"Did not receive expected query flags. got %v; want %v\", got, want)\n\t\t\t}\n\t\t})\n\t})\n\tt.Run(\"ExecuteExhaust\", func(t *testing.T) {\n\t\tt.Run(\"errors if connection is not streaming\", func(t *testing.T) {\n\t\t\tconn := mnet.NewConnection(&mockConnection{\n\t\t\t\trStreaming: false,\n\t\t\t})\n\n\t\t\terr := Operation{}.ExecuteExhaust(context.TODO(), conn)\n\t\t\tassert.NotNil(t, err, \"expected error, got nil\")\n\t\t})\n\t})\n\tt.Run(\"exhaustAllowed and moreToCome\", func(t *testing.T) {\n\t\t// Test the interaction between exhaustAllowed and moreToCome on requests/responses when using the Execute\n\t\t// and ExecuteExhaust methods.\n\n\t\t// Create a server response wire message that has moreToCome=false.\n\t\tserverResponseDoc := bsoncore.BuildDocumentFromElements(nil,\n\t\t\tbsoncore.AppendInt32Element(nil, \"ok\", 1),\n\t\t)\n\t\tnonStreamingResponse := createExhaustServerResponse(serverResponseDoc, false)\n\n\t\t// Create a connection that reports that it cannot stream messages.\n\t\tconn := &mockConnection{\n\t\t\trDesc: description.Server{\n\t\t\t\tWireVersion: &description.VersionRange{\n\t\t\t\t\tMax: 6,\n\t\t\t\t},\n\t\t\t},\n\t\t\trReadWM:    nonStreamingResponse,\n\t\t\trCanStream: false,\n\t\t}\n\n\t\tmnetconn := mnet.NewConnection(conn)\n\n\t\top := Operation{\n\t\t\tCommandFn: func(dst []byte, _ description.SelectedServer) ([]byte, error) {\n\t\t\t\treturn bsoncore.AppendInt32Element(dst, handshake.LegacyHello, 1), nil\n\t\t\t},\n\t\t\tDatabase:   \"admin\",\n\t\t\tDeployment: SingleConnectionDeployment{C: mnetconn},\n\t\t}\n\t\terr := op.Execute(context.TODO())\n\t\tassert.Nil(t, err, \"Execute error: %v\", err)\n\n\t\t// The wire message sent to the server should not have exhaustAllowed=true. After execution, the connection\n\t\t// should not be in a streaming state.\n\t\tassertExhaustAllowedSet(t, conn.pWriteWM, false)\n\t\tassert.False(t, conn.CurrentlyStreaming(), \"expected CurrentlyStreaming to be false\")\n\n\t\t// Modify the connection to report that it can stream and create a new server response with moreToCome=true.\n\t\tstreamingResponse := createExhaustServerResponse(serverResponseDoc, true)\n\t\tconn.rReadWM = streamingResponse\n\t\tconn.rCanStream = true\n\t\terr = op.Execute(context.TODO())\n\t\tassert.Nil(t, err, \"Execute error: %v\", err)\n\t\tassertExhaustAllowedSet(t, conn.pWriteWM, true)\n\t\tassert.True(t, conn.CurrentlyStreaming(), \"expected CurrentlyStreaming to be true\")\n\n\t\t// Reset the server response and go through ExecuteExhaust to mimic streaming the next response. After\n\t\t// execution, the connection should still be in a streaming state.\n\t\tconn.rReadWM = streamingResponse\n\t\terr = op.ExecuteExhaust(context.TODO(), mnetconn)\n\t\tassert.Nil(t, err, \"ExecuteExhaust error: %v\", err)\n\t\tassert.True(t, conn.CurrentlyStreaming(), \"expected CurrentlyStreaming to be true\")\n\t})\n\tt.Run(\"context deadline exceeded not marked as TransientTransactionError\", func(t *testing.T) {\n\t\tconn := mnet.NewConnection(&mockConnection{})\n\n\t\t// Create a context that's already timed out.\n\t\tctx, cancel := context.WithDeadline(context.Background(), time.Unix(893934480, 0))\n\t\tdefer cancel()\n\n\t\top := Operation{\n\t\t\tDatabase:   \"foobar\",\n\t\t\tDeployment: SingleConnectionDeployment{C: conn},\n\t\t\tCommandFn: func(dst []byte, _ description.SelectedServer) ([]byte, error) {\n\t\t\t\tdst = bsoncore.AppendInt32Element(dst, \"ping\", 1)\n\t\t\t\treturn dst, nil\n\t\t\t},\n\t\t}\n\n\t\terr := op.Execute(ctx)\n\t\tassert.NotNil(t, err, \"expected an error from Execute(), got nil\")\n\t\t// Assert that error is just context deadline exceeded and is therefore not a driver.Error marked\n\t\t// with the TransientTransactionError label.\n\t\tassert.True(t, errors.Is(err, context.DeadlineExceeded))\n\t})\n\tt.Run(\"canceled context not marked as TransientTransactionError\", func(t *testing.T) {\n\t\tconn := mnet.NewConnection(&mockConnection{})\n\n\t\t// Create a context and cancel it immediately.\n\t\tctx, cancel := context.WithCancel(context.Background())\n\t\tcancel()\n\n\t\top := Operation{\n\t\t\tDatabase:   \"foobar\",\n\t\t\tDeployment: SingleConnectionDeployment{C: conn},\n\t\t\tCommandFn: func(dst []byte, _ description.SelectedServer) ([]byte, error) {\n\t\t\t\tdst = bsoncore.AppendInt32Element(dst, \"ping\", 1)\n\t\t\t\treturn dst, nil\n\t\t\t},\n\t\t}\n\n\t\terr := op.Execute(ctx)\n\t\tassert.NotNil(t, err, \"expected an error from Execute(), got nil\")\n\t\t// Assert that error is just context canceled and is therefore not a driver.Error marked with\n\t\t// the TransientTransactionError label.\n\t\tassert.Equal(t, err, context.Canceled, \"expected context.Canceled error, got %v\", err)\n\t})\n\tt.Run(\"ErrDeadlineWouldBeExceeded wraps context.DeadlineExceeded\", func(t *testing.T) {\n\t\t// Create a deployment that returns a server that reports a 90th\n\t\t// percentile RTT of 1 minute.\n\t\td := new(mockDeployment)\n\t\td.returns.server = mockServer{\n\t\t\tconn:       mnet.NewConnection(&mockConnection{}),\n\t\t\trttMonitor: mockRTTMonitor{min: 1 * time.Minute},\n\t\t}\n\n\t\t// Create an operation with a Timeout specified to enable CSOT behavior.\n\t\tvar dur time.Duration\n\t\top := Operation{\n\t\t\tDatabase:   \"foobar\",\n\t\t\tDeployment: d,\n\t\t\tCommandFn: func(dst []byte, _ description.SelectedServer) ([]byte, error) {\n\t\t\t\treturn dst, nil\n\t\t\t},\n\t\t\tTimeout: &dur,\n\t\t}\n\n\t\t// Call the operation with a context with a deadline less than the 90th\n\t\t// percentile RTT configured above.\n\t\tctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)\n\t\tdefer cancel()\n\t\terr := op.Execute(ctx)\n\n\t\tassert.ErrorIs(t, err, ErrDeadlineWouldBeExceeded)\n\t\tassert.ErrorIs(t, err, context.DeadlineExceeded)\n\t})\n}\n\nfunc createExhaustServerResponse(response bsoncore.Document, moreToCome bool) []byte {\n\tconst psuedoRequestID = 1\n\tidx, wm := wiremessage.AppendHeaderStart(nil, 0, psuedoRequestID, wiremessage.OpMsg)\n\tvar flags wiremessage.MsgFlag\n\tif moreToCome {\n\t\tflags = wiremessage.MoreToCome\n\t}\n\twm = wiremessage.AppendMsgFlags(wm, flags)\n\twm = wiremessage.AppendMsgSectionType(wm, wiremessage.SingleDocument)\n\twm = bsoncore.AppendDocument(wm, response)\n\treturn bsoncore.UpdateLength(wm, idx, int32(len(wm)))\n}\n\nfunc assertExhaustAllowedSet(t *testing.T, wm []byte, expected bool) {\n\tt.Helper()\n\t_, _, _, _, wm, ok := wiremessage.ReadHeader(wm)\n\tif !ok {\n\t\tt.Fatal(\"could not read wm header\")\n\t}\n\tflags, _, ok := wiremessage.ReadMsgFlags(wm)\n\tif !ok {\n\t\tt.Fatal(\"could not read wm flags\")\n\t}\n\n\tactual := flags&wiremessage.ExhaustAllowed > 0\n\tassert.Equal(t, expected, actual, \"expected exhaustAllowed set %v, got %v\", expected, actual)\n}\n\ntype mockDeployment struct {\n\tparams struct {\n\t\tselector description.ServerSelector\n\t}\n\treturns struct {\n\t\tserver                 Server\n\t\terr                    error\n\t\tretry                  bool\n\t\tkind                   description.TopologyKind\n\t\tserverSelectionTimeout time.Duration\n\t}\n}\n\nfunc (m *mockDeployment) SelectServer(_ context.Context, desc description.ServerSelector) (Server, error) {\n\tm.params.selector = desc\n\n\treturn m.returns.server, m.returns.err\n}\n\nfunc (m *mockDeployment) GetServerSelectionTimeout() time.Duration {\n\treturn m.returns.serverSelectionTimeout\n}\n\nfunc (m *mockDeployment) Kind() description.TopologyKind { return m.returns.kind }\n\ntype mockServerSelector struct{}\n\nfunc (m *mockServerSelector) SelectServer(description.Topology, []description.Server) ([]description.Server, error) {\n\tpanic(\"not implemented\")\n}\n\nfunc (m *mockServerSelector) String() string {\n\tpanic(\"not implemented\")\n}\n\ntype mockServer struct {\n\tconn       *mnet.Connection\n\terr        error\n\trttMonitor RTTMonitor\n}\n\nfunc (ms mockServer) Connection(context.Context) (*mnet.Connection, error) { return ms.conn, ms.err }\nfunc (ms mockServer) RTTMonitor() RTTMonitor                               { return ms.rttMonitor }\n\ntype mockRTTMonitor struct {\n\tewma  time.Duration\n\tmin   time.Duration\n\tstats string\n}\n\nfunc (mrm mockRTTMonitor) EWMA() time.Duration { return mrm.ewma }\nfunc (mrm mockRTTMonitor) Min() time.Duration  { return mrm.min }\nfunc (mrm mockRTTMonitor) Stats() string       { return mrm.stats }\n\ntype mockConnection struct {\n\t// parameters\n\tpWriteWM []byte\n\n\t// returns\n\trWriteErr     error\n\trReadWM       []byte\n\trReadErr      error\n\trDesc         description.Server\n\trCloseErr     error\n\trID           string\n\trServerConnID *int64\n\trAddr         address.Address\n\trCanStream    bool\n\trStreaming    bool\n}\n\nfunc (m *mockConnection) Description() description.Server { return m.rDesc }\nfunc (m *mockConnection) Close() error                    { return m.rCloseErr }\nfunc (m *mockConnection) ID() string                      { return m.rID }\nfunc (m *mockConnection) ServerConnectionID() *int64      { return m.rServerConnID }\nfunc (m *mockConnection) Address() address.Address        { return m.rAddr }\nfunc (m *mockConnection) SupportsStreaming() bool         { return m.rCanStream }\nfunc (m *mockConnection) CurrentlyStreaming() bool        { return m.rStreaming }\nfunc (m *mockConnection) SetStreaming(streaming bool)     { m.rStreaming = streaming }\nfunc (m *mockConnection) Stale() bool                     { return false }\nfunc (m *mockConnection) OIDCTokenGenID() uint64          { return 0 }\nfunc (m *mockConnection) SetOIDCTokenGenID(uint64)        {}\n\nfunc (m *mockConnection) DriverConnectionID() int64 { return 0 }\n\nfunc (m *mockConnection) Write(_ context.Context, wm []byte) error {\n\tm.pWriteWM = wm\n\treturn m.rWriteErr\n}\n\nfunc (m *mockConnection) Read(_ context.Context) ([]byte, error) {\n\treturn m.rReadWM, m.rReadErr\n}\n\ntype retryableError struct {\n\terror\n}\n\nfunc (retryableError) Retryable() bool { return true }\n\nvar _ RetryablePoolError = retryableError{}\n\n// mockRetryServer is used to test retry of connection checkout. Returns a retryable error from\n// Connection().\ntype mockRetryServer struct {\n\tnumCallsToConnection int\n}\n\n// Connection records the number of calls and returns retryable errors until the provided context\n// times out or is cancelled, then returns the context error.\nfunc (ms *mockRetryServer) Connection(ctx context.Context) (*mnet.Connection, error) {\n\tms.numCallsToConnection++\n\n\tif ctx.Err() != nil {\n\t\treturn nil, ctx.Err()\n\t}\n\n\ttime.Sleep(1 * time.Millisecond)\n\treturn nil, retryableError{error: errors.New(\"test error\")}\n}\n\nfunc (ms *mockRetryServer) RTTMonitor() RTTMonitor {\n\treturn &csot.ZeroRTTMonitor{}\n}\n\nfunc TestRetry(t *testing.T) {\n\tt.Run(\"retries multiple times with RetryContext\", func(t *testing.T) {\n\t\td := new(mockDeployment)\n\t\tms := new(mockRetryServer)\n\t\td.returns.server = ms\n\n\t\tctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)\n\t\tdefer cancel()\n\n\t\tretry := RetryContext\n\t\terr := Operation{\n\t\t\tCommandFn:  func([]byte, description.SelectedServer) ([]byte, error) { return nil, nil },\n\t\t\tDeployment: d,\n\t\t\tDatabase:   \"testing\",\n\t\t\tRetryMode:  &retry,\n\t\t\tType:       Read,\n\t\t}.Execute(ctx)\n\t\tassert.NotNil(t, err, \"expected an error from Execute()\")\n\n\t\t// Expect Connection() to be called at least 3 times. The first call is the initial attempt\n\t\t// to run the operation and the second is the retry. The third indicates that we retried\n\t\t// more than once, which is the behavior we want to assert.\n\t\tassert.True(t,\n\t\t\tms.numCallsToConnection >= 3,\n\t\t\t\"expected Connection() to be called at least 3 times\")\n\n\t\tdeadline, _ := ctx.Deadline()\n\t\tassert.True(t,\n\t\t\ttime.Now().After(deadline),\n\t\t\t\"expected operation to complete only after the context deadline is exceeded\")\n\t})\n}\n\nfunc TestDecodeOpReply(t *testing.T) {\n\tt.Parallel()\n\n\t// GODRIVER-2869: Prevent infinite loop caused by malformatted wiremessage with length of 0.\n\tt.Run(\"malformatted wiremessage with length of 0\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tvar wm []byte\n\t\twm = wiremessage.AppendReplyFlags(wm, 0)\n\t\twm = wiremessage.AppendReplyCursorID(wm, int64(0))\n\t\twm = wiremessage.AppendReplyStartingFrom(wm, 0)\n\t\twm = wiremessage.AppendReplyNumberReturned(wm, 0)\n\t\tidx, wm := bsoncore.ReserveLength(wm)\n\t\twm = bsoncore.UpdateLength(wm, idx, 0)\n\t\treply := Operation{}.decodeOpReply(wm)\n\t\tassert.Equal(t, []bsoncore.Document(nil), reply.documents)\n\t})\n}\n\nfunc TestMarshalBSONWriteConcern(t *testing.T) {\n\tt.Parallel()\n\n\ttests := []struct {\n\t\tname         string\n\t\twriteConcern writeconcern.WriteConcern\n\t\twantBSONType bson.Type\n\t\twtimeout     time.Duration\n\t\twant         bson.D\n\t\twantErr      string\n\t}{\n\t\t{\n\t\t\tname:         \"empty\",\n\t\t\twriteConcern: writeconcern.WriteConcern{},\n\t\t\twantBSONType: 0x0,\n\t\t\twant:         nil,\n\t\t\twtimeout:     0,\n\t\t\twantErr:      \"a write concern must have at least one field set\",\n\t\t},\n\t\t{\n\t\t\tname:         \"journal only\",\n\t\t\twriteConcern: *writeconcern.Journaled(),\n\t\t\twantBSONType: bson.TypeEmbeddedDocument,\n\t\t\twant:         bson.D{{\"j\", true}},\n\t\t\twtimeout:     0,\n\t\t\twantErr:      \"a write concern must have at least one field set\",\n\t\t},\n\t\t{\n\t\t\tname:         \"journal and wtimout\",\n\t\t\twriteConcern: *writeconcern.Journaled(),\n\t\t\twtimeout:     10 * time.Millisecond,\n\t\t\twantBSONType: bson.TypeEmbeddedDocument,\n\t\t\twant:         bson.D{{\"j\", true}, {\"wtimeout\", int64(10 * time.Millisecond / time.Millisecond)}},\n\t\t\twantErr:      \"a write concern must have at least one field set\",\n\t\t},\n\t}\n\n\tfor _, test := range tests {\n\t\ttest := test // Capture the range variable\n\n\t\tt.Run(test.name, func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tgotBSONType, gotBSON, gotErr := MarshalBSONWriteConcern(&test.writeConcern, test.wtimeout)\n\t\t\tassert.Equal(t, test.wantBSONType, gotBSONType)\n\n\t\t\twantBSON := []byte(nil)\n\n\t\t\tif test.want != nil {\n\t\t\t\tvar err error\n\n\t\t\t\twantBSON, err = bson.Marshal(test.want)\n\t\t\t\tassert.NoError(t, err)\n\t\t\t}\n\n\t\t\tassert.Equal(t, wantBSON, gotBSON)\n\n\t\t\tif gotErr != nil {\n\t\t\t\tassert.EqualError(t, gotErr, test.wantErr)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc BenchmarkRedactStartedInformationCmd(b *testing.B) {\n\tfor _, size := range []int{0, 1, 5, 10, 100, 1000} {\n\t\tinfo := startedInformation{\n\t\t\tcmd: make([]byte, 100),\n\t\t\tdocumentSequences: make([]struct {\n\t\t\t\tidentifier string\n\t\t\t\tdata       []byte\n\t\t\t}, size),\n\t\t}\n\t\tfor i := 0; i < size; i++ {\n\t\t\tinfo.documentSequences[i] = struct {\n\t\t\t\tidentifier string\n\t\t\t\tdata       []byte\n\t\t\t}{\n\t\t\t\tidentifier: strconv.Itoa(i),\n\t\t\t\tdata:       make([]byte, 100),\n\t\t\t}\n\t\t}\n\t\tb.Run(strconv.Itoa(size), func(b *testing.B) {\n\t\t\tb.ReportAllocs()\n\t\t\tfor i := 0; i < b.N; i++ {\n\t\t\t\tredactStartedInformationCmd(info)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "x/mongo/driver/serverapioptions.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage driver\n\n// TestServerAPIVersion is the most recent, stable variant of options.ServerAPIVersion.\n// Only to be used in testing.\nconst TestServerAPIVersion = \"1\"\n\n// ServerAPIOptions represents arguments used to configure the API version sent\n// to the server when running commands.\ntype ServerAPIOptions struct {\n\tServerAPIVersion  string\n\tStrict            *bool\n\tDeprecationErrors *bool\n}\n\n// NewServerAPIOptions creates a new ServerAPIOptions configured with the provided serverAPIVersion.\nfunc NewServerAPIOptions(serverAPIVersion string) *ServerAPIOptions {\n\treturn &ServerAPIOptions{ServerAPIVersion: serverAPIVersion}\n}\n\n// SetStrict specifies whether the server should return errors for features that are not part of the API version.\nfunc (s *ServerAPIOptions) SetStrict(strict bool) *ServerAPIOptions {\n\ts.Strict = &strict\n\treturn s\n}\n\n// SetDeprecationErrors specifies whether the server should return errors for deprecated features.\nfunc (s *ServerAPIOptions) SetDeprecationErrors(deprecationErrors bool) *ServerAPIOptions {\n\ts.DeprecationErrors = &deprecationErrors\n\treturn s\n}\n"
  },
  {
    "path": "x/mongo/driver/session/client_session.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage session\n\nimport (\n\t\"errors\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/uuid\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/address\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/readconcern\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/readpref\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/writeconcern\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/description\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/mnet\"\n)\n\n// ErrSessionEnded is returned when a client session is used after a call to endSession().\nvar ErrSessionEnded = errors.New(\"ended session was used\")\n\n// ErrNoTransactStarted is returned if a transaction operation is called when no transaction has started.\nvar ErrNoTransactStarted = errors.New(\"no transaction started\")\n\n// ErrTransactInProgress is returned if startTransaction() is called when a transaction is in progress.\nvar ErrTransactInProgress = errors.New(\"transaction already in progress\")\n\n// ErrAbortAfterCommit is returned when abort is called after a commit.\nvar ErrAbortAfterCommit = errors.New(\"cannot call abortTransaction after calling commitTransaction\")\n\n// ErrAbortTwice is returned if abort is called after transaction is already aborted.\nvar ErrAbortTwice = errors.New(\"cannot call abortTransaction twice\")\n\n// ErrCommitAfterAbort is returned if commit is called after an abort.\nvar ErrCommitAfterAbort = errors.New(\"cannot call commitTransaction after calling abortTransaction\")\n\n// ErrUnackWCUnsupported is returned if an unacknowledged write concern is supported for a transaction.\nvar ErrUnackWCUnsupported = errors.New(\"transactions do not support unacknowledged write concerns\")\n\n// ErrSnapshotTransaction is returned if an transaction is started on a snapshot session.\nvar ErrSnapshotTransaction = errors.New(\"transactions are not supported in snapshot sessions\")\n\n// TransactionState indicates the state of the transactions FSM.\ntype TransactionState uint8\n\n// Client Session states\nconst (\n\tNone TransactionState = iota\n\tStarting\n\tInProgress\n\tCommitted\n\tAborted\n)\n\nconst defaultWriteConcernTimeout = 10_000 * time.Millisecond\n\n// String implements the fmt.Stringer interface.\nfunc (s TransactionState) String() string {\n\tswitch s {\n\tcase None:\n\t\treturn \"none\"\n\tcase Starting:\n\t\treturn \"starting\"\n\tcase InProgress:\n\t\treturn \"in progress\"\n\tcase Committed:\n\t\treturn \"committed\"\n\tcase Aborted:\n\t\treturn \"aborted\"\n\tdefault:\n\t\treturn \"unknown\"\n\t}\n}\n\nvar _ mnet.Pinner = (LoadBalancedTransactionConnection)(nil)\n\n// LoadBalancedTransactionConnection represents a connection that's pinned by a ClientSession because it's being used\n// to execute a transaction when running against a load balancer. This interface is a copy of driver.PinnedConnection\n// and exists to be able to pin transactions to a connection without causing an import cycle.\ntype LoadBalancedTransactionConnection interface {\n\tmnet.ReadWriteCloser\n\tmnet.Describer\n\tmnet.Pinner\n}\n\n// Client is a session for clients to run commands.\ntype Client struct {\n\t*Server\n\tClientID       uuid.UUID\n\tClusterTime    bson.Raw\n\tConsistent     bool // causal consistency\n\tOperationTime  *bson.Timestamp\n\tIsImplicit     bool\n\tTerminated     bool\n\tRetryingCommit bool\n\tCommitting     bool\n\tAborting       bool\n\tSnapshot       bool\n\n\t// SnapshotTime is the atClusterTime value for snapshot reads. This field is\n\t// left immutable once set for the lifetime of the session. This guards\n\t// against users updating custom snapshot times during transactions which\n\t// could lead to a write conflict.\n\tSnapshotTime    bson.Timestamp\n\tSnapshotTimeSet bool\n\n\t// options for the current transaction\n\t// most recently set by transactionopt\n\tCurrentRc       *readconcern.ReadConcern\n\tCurrentRp       *readpref.ReadPref\n\tCurrentWc       *writeconcern.WriteConcern\n\tCurrentWTimeout time.Duration\n\n\t// default transaction options\n\ttransactionRc *readconcern.ReadConcern\n\ttransactionRp *readpref.ReadPref\n\ttransactionWc *writeconcern.WriteConcern\n\n\tpool             *Pool\n\tTransactionState TransactionState\n\tPinnedServerAddr *address.Address\n\tRecoveryToken    bson.Raw\n\tPinnedConnection LoadBalancedTransactionConnection\n}\n\nfunc getClusterTime(clusterTime bson.Raw) (uint32, uint32) {\n\tif clusterTime == nil {\n\t\treturn 0, 0\n\t}\n\n\tclusterTimeVal, err := clusterTime.LookupErr(\"$clusterTime\")\n\tif err != nil {\n\t\treturn 0, 0\n\t}\n\n\ttimestampVal, err := bson.Raw(clusterTimeVal.Value).LookupErr(\"clusterTime\")\n\tif err != nil {\n\t\treturn 0, 0\n\t}\n\n\treturn timestampVal.Timestamp()\n}\n\n// MaxClusterTime compares 2 clusterTime documents and returns the document representing the highest cluster time.\nfunc MaxClusterTime(ct1, ct2 bson.Raw) bson.Raw {\n\tepoch1, ord1 := getClusterTime(ct1)\n\tepoch2, ord2 := getClusterTime(ct2)\n\n\tswitch {\n\tcase epoch1 > epoch2:\n\t\treturn ct1\n\tcase epoch1 < epoch2:\n\t\treturn ct2\n\tcase ord1 > ord2:\n\t\treturn ct1\n\tcase ord1 < ord2:\n\t\treturn ct2\n\t}\n\n\treturn ct1\n}\n\n// NewImplicitClientSession creates a new implicit client-side session.\nfunc NewImplicitClientSession(pool *Pool, clientID uuid.UUID) *Client {\n\t// Server-side session checkout for implicit sessions is deferred until after checking out a\n\t// connection, so don't check out a server-side session right now. This will limit the number of\n\t// implicit sessions to no greater than an application's maxPoolSize.\n\n\treturn &Client{\n\t\tpool:       pool,\n\t\tClientID:   clientID,\n\t\tIsImplicit: true,\n\t}\n}\n\n// NewClientSession creates a new explicit client-side session.\nfunc NewClientSession(pool *Pool, clientID uuid.UUID, opts ...*ClientOptions) (*Client, error) {\n\tc := &Client{\n\t\tpool:     pool,\n\t\tClientID: clientID,\n\t}\n\n\tmergedOpts := mergeClientOptions(opts...)\n\tif mergedOpts.DefaultReadPreference != nil {\n\t\tc.transactionRp = mergedOpts.DefaultReadPreference\n\t}\n\tif mergedOpts.DefaultReadConcern != nil {\n\t\tc.transactionRc = mergedOpts.DefaultReadConcern\n\t}\n\tif mergedOpts.DefaultWriteConcern != nil {\n\t\tc.transactionWc = mergedOpts.DefaultWriteConcern\n\t}\n\tif mergedOpts.Snapshot != nil {\n\t\tc.Snapshot = *mergedOpts.Snapshot\n\t}\n\tif mergedOpts.SnapshotTime != nil {\n\t\tc.SnapshotTime = *mergedOpts.SnapshotTime\n\t\tc.SnapshotTimeSet = true\n\t}\n\n\t// For explicit sessions, the default for causalConsistency is true, unless Snapshot is\n\t// enabled, then it's false. Set the default and then allow any explicit causalConsistency\n\t// setting to override it.\n\tc.Consistent = !c.Snapshot\n\tif mergedOpts.CausalConsistency != nil {\n\t\tc.Consistent = *mergedOpts.CausalConsistency\n\t}\n\n\tif c.Consistent && c.Snapshot {\n\t\treturn nil, errors.New(\"causal consistency and snapshot cannot both be set for a session\")\n\t}\n\n\tif c.SnapshotTimeSet && !c.Snapshot {\n\t\treturn nil, errors.New(\"snapshotTime cannot be set when snapshot is false\")\n\t}\n\n\tif err := c.SetServer(); err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn c, nil\n}\n\n// SetServer will check out a session from the client session pool.\nfunc (c *Client) SetServer() error {\n\tvar err error\n\tc.Server, err = c.pool.GetSession()\n\treturn err\n}\n\n// AdvanceClusterTime updates the session's cluster time.\nfunc (c *Client) AdvanceClusterTime(clusterTime bson.Raw) error {\n\tif c.Terminated {\n\t\treturn ErrSessionEnded\n\t}\n\tc.ClusterTime = MaxClusterTime(c.ClusterTime, clusterTime)\n\treturn nil\n}\n\n// AdvanceOperationTime updates the session's operation time.\nfunc (c *Client) AdvanceOperationTime(opTime *bson.Timestamp) error {\n\tif c.Terminated {\n\t\treturn ErrSessionEnded\n\t}\n\n\tif c.OperationTime == nil {\n\t\tc.OperationTime = opTime\n\t\treturn nil\n\t}\n\n\tif opTime.T > c.OperationTime.T {\n\t\tc.OperationTime = opTime\n\t} else if (opTime.T == c.OperationTime.T) && (opTime.I > c.OperationTime.I) {\n\t\tc.OperationTime = opTime\n\t}\n\n\treturn nil\n}\n\n// UpdateUseTime sets the session's last used time to the current time. This must be called whenever the session is\n// used to send a command to the server to ensure that the session is not prematurely marked expired in the driver's\n// session pool. If the session has already been ended, this method will return ErrSessionEnded.\nfunc (c *Client) UpdateUseTime() error {\n\tif c.Terminated {\n\t\treturn ErrSessionEnded\n\t}\n\tc.updateUseTime()\n\treturn nil\n}\n\n// UpdateRecoveryToken updates the session's recovery token from the server response.\nfunc (c *Client) UpdateRecoveryToken(response bson.Raw) {\n\tif c == nil {\n\t\treturn\n\t}\n\n\ttoken, err := response.LookupErr(\"recoveryToken\")\n\tif err != nil {\n\t\treturn\n\t}\n\n\tc.RecoveryToken = token.Document()\n}\n\n// UpdateSnapshotTime updates the session's value for the atClusterTime field of\n// ReadConcern.\nfunc (c *Client) UpdateSnapshotTime(response bsoncore.Document) {\n\tif c == nil || c.SnapshotTimeSet {\n\t\t// Do nothing if session is nil or snapshot time is already set. The driver\n\t\t// sends the same atClusterTime for all operations in a snapshot session so\n\t\t// resetting is a potentially dangerous redundancy.\n\t\treturn\n\t}\n\n\tsubDoc := response\n\tif cur, ok := response.Lookup(\"cursor\").DocumentOK(); ok {\n\t\tsubDoc = cur\n\t}\n\n\tssTimeElem, err := subDoc.LookupErr(\"atClusterTime\")\n\tif err != nil {\n\t\t// atClusterTime not included by the server\n\t\treturn\n\t}\n\n\tt, i := ssTimeElem.Timestamp()\n\tc.SnapshotTime = bson.Timestamp{\n\t\tT: t,\n\t\tI: i,\n\t}\n\tc.SnapshotTimeSet = true\n}\n\n// ClearPinnedResources clears the pinned server and/or connection associated with the session.\nfunc (c *Client) ClearPinnedResources() error {\n\tif c == nil {\n\t\treturn nil\n\t}\n\n\tc.PinnedServerAddr = nil\n\tif c.PinnedConnection != nil {\n\t\tif err := c.PinnedConnection.UnpinFromTransaction(); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := c.PinnedConnection.Close(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tc.PinnedConnection = nil\n\treturn nil\n}\n\n// unpinConnection gracefully unpins the connection associated with the session\n// if there is one. This is done via the pinned connection's\n// UnpinFromTransaction function.\nfunc (c *Client) unpinConnection() error {\n\tif c == nil || c.PinnedConnection == nil {\n\t\treturn nil\n\t}\n\n\terr := c.PinnedConnection.UnpinFromTransaction()\n\tcloseErr := c.PinnedConnection.Close()\n\tif err == nil && closeErr != nil {\n\t\terr = closeErr\n\t}\n\tc.PinnedConnection = nil\n\treturn err\n}\n\n// EndSession ends the session.\nfunc (c *Client) EndSession() {\n\tif c.Terminated {\n\t\treturn\n\t}\n\tc.Terminated = true\n\n\t// Ignore the error when unpinning the connection because we can't do\n\t// anything about it if it doesn't work. Typically the only errors that can\n\t// happen here indicate that something went wrong with the connection state,\n\t// like it wasn't marked as pinned or attempted to return to the wrong pool.\n\t_ = c.unpinConnection()\n\tc.pool.ReturnSession(c.Server)\n}\n\n// TransactionInProgress returns true if the client session is in an active transaction.\nfunc (c *Client) TransactionInProgress() bool {\n\treturn c.TransactionState == InProgress\n}\n\n// TransactionStarting returns true if the client session is starting a transaction.\nfunc (c *Client) TransactionStarting() bool {\n\treturn c.TransactionState == Starting\n}\n\n// TransactionRunning returns true if the client session has started the transaction\n// and it hasn't been committed or aborted\nfunc (c *Client) TransactionRunning() bool {\n\treturn c != nil && (c.TransactionState == Starting || c.TransactionState == InProgress)\n}\n\n// TransactionCommitted returns true of the client session just committed a transaction.\nfunc (c *Client) TransactionCommitted() bool {\n\treturn c.TransactionState == Committed\n}\n\n// CheckStartTransaction checks to see if allowed to start transaction and returns\n// an error if not allowed\nfunc (c *Client) CheckStartTransaction() error {\n\tif c.TransactionState == InProgress || c.TransactionState == Starting {\n\t\treturn ErrTransactInProgress\n\t}\n\tif c.Snapshot {\n\t\treturn ErrSnapshotTransaction\n\t}\n\treturn nil\n}\n\n// StartTransaction initializes the transaction options and advances the state machine.\n// It does not contact the server to start the transaction.\nfunc (c *Client) StartTransaction(opts *TransactionOptions) error {\n\terr := c.CheckStartTransaction()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tc.IncrementTxnNumber()\n\tc.RetryingCommit = false\n\n\tif opts != nil {\n\t\tc.CurrentRc = opts.ReadConcern\n\t\tc.CurrentRp = opts.ReadPreference\n\t\tc.CurrentWc = opts.WriteConcern\n\t}\n\n\tif c.CurrentRc == nil {\n\t\tc.CurrentRc = c.transactionRc\n\t}\n\n\tif c.CurrentRp == nil {\n\t\tc.CurrentRp = c.transactionRp\n\t}\n\n\tif c.CurrentWc == nil {\n\t\tc.CurrentWc = c.transactionWc\n\t}\n\n\tif !c.CurrentWc.Acknowledged() {\n\t\t_ = c.clearTransactionOpts()\n\t\treturn ErrUnackWCUnsupported\n\t}\n\n\tc.TransactionState = Starting\n\treturn c.ClearPinnedResources()\n}\n\n// CheckCommitTransaction checks to see if allowed to commit transaction and returns\n// an error if not allowed.\nfunc (c *Client) CheckCommitTransaction() error {\n\tswitch c.TransactionState {\n\tcase None:\n\t\treturn ErrNoTransactStarted\n\tcase Aborted:\n\t\treturn ErrCommitAfterAbort\n\t}\n\treturn nil\n}\n\n// CommitTransaction updates the state for a successfully committed transaction and returns\n// an error if not permissible.  It does not actually perform the commit.\nfunc (c *Client) CommitTransaction() error {\n\terr := c.CheckCommitTransaction()\n\tif err != nil {\n\t\treturn err\n\t}\n\tc.TransactionState = Committed\n\treturn nil\n}\n\n// UpdateCommitTransactionWriteConcern will set the write concern to majority.\n// This should be called after a commit transaction operation fails with a\n// retryable error or after a successful commit transaction operation\n//\n// Per the transaction specifications, when commitTransaction is retried, if\n// the modified write concern does not include a \"wtimeout\" value, drivers\n// MUST apply \"wtimeout: 10000\" to the write concern in order to avoid waiting\n// forever (oruntil a socket timeout) if the majority write concern cannot be\n// satisfied. This field abstracts that functionality. For more information,\n// see SPEC-1185.\nfunc (c *Client) UpdateCommitTransactionWriteConcern() {\n\tc.CurrentWc = &writeconcern.WriteConcern{\n\t\tW: \"majority\",\n\t}\n\n\tc.CurrentWTimeout = defaultWriteConcernTimeout\n}\n\n// CheckAbortTransaction checks to see if allowed to abort transaction and returns\n// an error if not allowed.\nfunc (c *Client) CheckAbortTransaction() error {\n\tswitch c.TransactionState {\n\tcase None:\n\t\treturn ErrNoTransactStarted\n\tcase Committed:\n\t\treturn ErrAbortAfterCommit\n\tcase Aborted:\n\t\treturn ErrAbortTwice\n\t}\n\treturn nil\n}\n\n// AbortTransaction updates the state for a successfully aborted transaction and returns\n// an error if not permissible.  It does not actually perform the abort.\nfunc (c *Client) AbortTransaction() error {\n\terr := c.CheckAbortTransaction()\n\tif err != nil {\n\t\treturn err\n\t}\n\tc.TransactionState = Aborted\n\treturn c.clearTransactionOpts()\n}\n\n// StartCommand updates the session's internal state at the beginning of an operation. This must be called before\n// server selection is done for the operation as the session's state can impact the result of that process.\nfunc (c *Client) StartCommand() error {\n\tif c == nil {\n\t\treturn nil\n\t}\n\n\t// If we're executing the first operation using this session after a transaction, we must ensure that the session\n\t// is not pinned to any resources.\n\tif !c.TransactionRunning() && !c.Committing && !c.Aborting {\n\t\treturn c.ClearPinnedResources()\n\t}\n\treturn nil\n}\n\n// ApplyCommand advances the state machine upon command execution. This must be called after server selection is\n// complete.\nfunc (c *Client) ApplyCommand(desc description.Server) error {\n\tif c.Committing {\n\t\t// Do not change state if committing after already committed\n\t\treturn nil\n\t}\n\tswitch c.TransactionState {\n\tcase Starting:\n\t\tc.TransactionState = InProgress\n\t\t// If this is in a transaction and the server is a mongos, pin it\n\t\tif desc.Kind == description.ServerKindMongos {\n\t\t\tc.PinnedServerAddr = &desc.Addr\n\t\t}\n\tcase Committed, Aborted:\n\t\tc.TransactionState = None\n\t\treturn c.clearTransactionOpts()\n\t}\n\n\treturn nil\n}\n\nfunc (c *Client) clearTransactionOpts() error {\n\tc.RetryingCommit = false\n\tc.Aborting = false\n\tc.Committing = false\n\tc.CurrentWc = nil\n\tc.CurrentRp = nil\n\tc.CurrentRc = nil\n\tc.RecoveryToken = nil\n\n\treturn c.ClearPinnedResources()\n}\n"
  },
  {
    "path": "x/mongo/driver/session/client_session_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage session\n\nimport (\n\t\"bytes\"\n\t\"errors\"\n\t\"testing\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/require\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/uuid\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/description\"\n)\n\nvar (\n\tconsistent  = true\n\tsessionOpts = &ClientOptions{\n\t\tCausalConsistency: &consistent,\n\t}\n)\n\nfunc compareOperationTimes(t *testing.T, expected *bson.Timestamp, actual *bson.Timestamp) {\n\tif expected.T != actual.T {\n\t\tt.Fatalf(\"T value mismatch; expected %d got %d\", expected.T, actual.T)\n\t}\n\n\tif expected.I != actual.I {\n\t\tt.Fatalf(\"I value mismatch; expected %d got %d\", expected.I, actual.I)\n\t}\n}\n\nfunc TestClientSession(t *testing.T) {\n\tclusterTime1 := bsoncore.BuildDocument(nil, bsoncore.AppendDocumentElement(nil, \"$clusterTime\", bsoncore.BuildDocument(nil, bsoncore.AppendTimestampElement(nil, \"clusterTime\", 10, 5))))\n\tclusterTime2 := bsoncore.BuildDocument(nil, bsoncore.AppendDocumentElement(nil, \"$clusterTime\", bsoncore.BuildDocument(nil, bsoncore.AppendTimestampElement(nil, \"clusterTime\", 5, 5))))\n\tclusterTime3 := bsoncore.BuildDocument(nil, bsoncore.AppendDocumentElement(nil, \"$clusterTime\", bsoncore.BuildDocument(nil, bsoncore.AppendTimestampElement(nil, \"clusterTime\", 5, 0))))\n\n\tt.Run(\"TestMaxClusterTime\", func(t *testing.T) {\n\t\tmaxTime := MaxClusterTime(clusterTime1, clusterTime2)\n\t\tif !bytes.Equal(maxTime, clusterTime1) {\n\t\t\tt.Errorf(\"Wrong max time\")\n\t\t}\n\n\t\tmaxTime = MaxClusterTime(clusterTime3, clusterTime2)\n\t\tif !bytes.Equal(maxTime, clusterTime2) {\n\t\t\tt.Errorf(\"Wrong max time\")\n\t\t}\n\t})\n\n\tt.Run(\"TestAdvanceClusterTime\", func(t *testing.T) {\n\t\tid, _ := uuid.New()\n\t\tsess, err := NewClientSession(&Pool{}, id, sessionOpts)\n\t\trequire.Nil(t, err, \"Unexpected error\")\n\t\terr = sess.AdvanceClusterTime(clusterTime2)\n\t\trequire.Nil(t, err, \"Unexpected error\")\n\t\tif !bytes.Equal(sess.ClusterTime, clusterTime2) {\n\t\t\tt.Errorf(\"Session cluster time incorrect, expected %v, received %v\", clusterTime2, sess.ClusterTime)\n\t\t}\n\t\terr = sess.AdvanceClusterTime(clusterTime3)\n\t\trequire.Nil(t, err, \"Unexpected error\")\n\t\tif !bytes.Equal(sess.ClusterTime, clusterTime2) {\n\t\t\tt.Errorf(\"Session cluster time incorrect, expected %v, received %v\", clusterTime2, sess.ClusterTime)\n\t\t}\n\t\terr = sess.AdvanceClusterTime(clusterTime1)\n\t\trequire.Nil(t, err, \"Unexpected error\")\n\t\tif !bytes.Equal(sess.ClusterTime, clusterTime1) {\n\t\t\tt.Errorf(\"Session cluster time incorrect, expected %v, received %v\", clusterTime1, sess.ClusterTime)\n\t\t}\n\t\tsess.EndSession()\n\t})\n\n\tt.Run(\"TestEndSession\", func(t *testing.T) {\n\t\tid, _ := uuid.New()\n\t\tsess, err := NewClientSession(&Pool{}, id, sessionOpts)\n\t\trequire.Nil(t, err, \"Unexpected error\")\n\t\tsess.EndSession()\n\t\terr = sess.UpdateUseTime()\n\t\trequire.NotNil(t, err, \"Expected error, received nil\")\n\t})\n\n\tt.Run(\"TestAdvanceOperationTime\", func(t *testing.T) {\n\t\tid, _ := uuid.New()\n\t\tsess, err := NewClientSession(&Pool{}, id, sessionOpts)\n\t\trequire.Nil(t, err, \"Unexpected error\")\n\n\t\toptime1 := &bson.Timestamp{\n\t\t\tT: 1,\n\t\t\tI: 0,\n\t\t}\n\t\terr = sess.AdvanceOperationTime(optime1)\n\t\tassert.Nil(t, err, \"error updating first operation time: %s\", err)\n\t\tcompareOperationTimes(t, optime1, sess.OperationTime)\n\n\t\toptime2 := &bson.Timestamp{\n\t\t\tT: 2,\n\t\t\tI: 0,\n\t\t}\n\t\terr = sess.AdvanceOperationTime(optime2)\n\t\tassert.Nil(t, err, \"error updating second operation time: %s\", err)\n\t\tcompareOperationTimes(t, optime2, sess.OperationTime)\n\n\t\toptime3 := &bson.Timestamp{\n\t\t\tT: 2,\n\t\t\tI: 1,\n\t\t}\n\t\terr = sess.AdvanceOperationTime(optime3)\n\t\tassert.Nil(t, err, \"error updating third operation time: %s\", err)\n\t\tcompareOperationTimes(t, optime3, sess.OperationTime)\n\n\t\terr = sess.AdvanceOperationTime(&bson.Timestamp{\n\t\t\tT: 1,\n\t\t\tI: 10,\n\t\t})\n\t\tassert.Nil(t, err, \"error updating fourth operation time: %s\", err)\n\t\tcompareOperationTimes(t, optime3, sess.OperationTime)\n\t\tsess.EndSession()\n\t})\n\n\tt.Run(\"TestTransactionState\", func(t *testing.T) {\n\t\tid, _ := uuid.New()\n\t\tsess, err := NewClientSession(&Pool{}, id, nil)\n\t\trequire.Nil(t, err, \"Unexpected error\")\n\n\t\terr = sess.CommitTransaction()\n\t\tif !errors.Is(err, ErrNoTransactStarted) {\n\t\t\tt.Errorf(\"expected error, got %v\", err)\n\t\t}\n\n\t\terr = sess.AbortTransaction()\n\t\tif !errors.Is(err, ErrNoTransactStarted) {\n\t\t\tt.Errorf(\"expected error, got %v\", err)\n\t\t}\n\n\t\tif sess.TransactionState != None {\n\t\t\tt.Errorf(\"incorrect session state, expected None, received %v\", sess.TransactionState)\n\t\t}\n\n\t\terr = sess.StartTransaction(nil)\n\t\trequire.Nil(t, err, \"error starting transaction: %s\", err)\n\t\tif sess.TransactionState != Starting {\n\t\t\tt.Errorf(\"incorrect session state, expected Starting, received %v\", sess.TransactionState)\n\t\t}\n\n\t\terr = sess.StartTransaction(nil)\n\t\tif !errors.Is(err, ErrTransactInProgress) {\n\t\t\tt.Errorf(\"expected error, got %v\", err)\n\t\t}\n\n\t\terr = sess.ApplyCommand(description.Server{Kind: description.ServerKindStandalone})\n\t\tassert.Nil(t, err, \"ApplyCommand error: %v\", err)\n\t\tif sess.TransactionState != InProgress {\n\t\t\tt.Errorf(\"incorrect session state, expected InProgress, received %v\", sess.TransactionState)\n\t\t}\n\n\t\terr = sess.StartTransaction(nil)\n\t\tif !errors.Is(err, ErrTransactInProgress) {\n\t\t\tt.Errorf(\"expected error, got %v\", err)\n\t\t}\n\n\t\terr = sess.CommitTransaction()\n\t\trequire.Nil(t, err, \"error committing transaction: %s\", err)\n\t\tif sess.TransactionState != Committed {\n\t\t\tt.Errorf(\"incorrect session state, expected Committed, received %v\", sess.TransactionState)\n\t\t}\n\n\t\terr = sess.AbortTransaction()\n\t\tif !errors.Is(err, ErrAbortAfterCommit) {\n\t\t\tt.Errorf(\"expected error, got %v\", err)\n\t\t}\n\n\t\terr = sess.StartTransaction(nil)\n\t\trequire.Nil(t, err, \"error starting transaction: %s\", err)\n\t\tif sess.TransactionState != Starting {\n\t\t\tt.Errorf(\"incorrect session state, expected Starting, received %v\", sess.TransactionState)\n\t\t}\n\n\t\terr = sess.AbortTransaction()\n\t\trequire.Nil(t, err, \"error aborting transaction: %s\", err)\n\t\tif sess.TransactionState != Aborted {\n\t\t\tt.Errorf(\"incorrect session state, expected Aborted, received %v\", sess.TransactionState)\n\t\t}\n\n\t\terr = sess.AbortTransaction()\n\t\tif !errors.Is(err, ErrAbortTwice) {\n\t\t\tt.Errorf(\"expected error, got %v\", err)\n\t\t}\n\n\t\terr = sess.CommitTransaction()\n\t\tif !errors.Is(err, ErrCommitAfterAbort) {\n\t\t\tt.Errorf(\"expected error, got %v\", err)\n\t\t}\n\t})\n\n\tt.Run(\"causal consistency and snapshot\", func(t *testing.T) {\n\t\tfalseVal := false\n\t\ttrueVal := true\n\n\t\t// A test for Consistent and Snapshot both being true and causing an error can be found\n\t\t// in TestSessionsProse.\n\t\ttestCases := []struct {\n\t\t\tdescription        string\n\t\t\tconsistent         *bool\n\t\t\tsnapshot           *bool\n\t\t\texpectedConsistent bool\n\t\t\texpectedSnapshot   bool\n\t\t}{\n\t\t\t{\n\t\t\t\t\"both unset\",\n\t\t\t\tnil,\n\t\t\t\tnil,\n\t\t\t\ttrue,\n\t\t\t\tfalse,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"both false\",\n\t\t\t\t&falseVal,\n\t\t\t\t&falseVal,\n\t\t\t\tfalse,\n\t\t\t\tfalse,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"cc unset snapshot true\",\n\t\t\t\tnil,\n\t\t\t\t&trueVal,\n\t\t\t\tfalse,\n\t\t\t\ttrue,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"cc unset snapshot false\",\n\t\t\t\tnil,\n\t\t\t\t&falseVal,\n\t\t\t\ttrue,\n\t\t\t\tfalse,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"cc true snapshot unset\",\n\t\t\t\t&trueVal,\n\t\t\t\tnil,\n\t\t\t\ttrue,\n\t\t\t\tfalse,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"cc false snapshot unset\",\n\t\t\t\t&falseVal,\n\t\t\t\tnil,\n\t\t\t\tfalse,\n\t\t\t\tfalse,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"cc false snapshot true\",\n\t\t\t\t&falseVal,\n\t\t\t\t&trueVal,\n\t\t\t\tfalse,\n\t\t\t\ttrue,\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"cc true snapshot false\",\n\t\t\t\t&trueVal,\n\t\t\t\t&falseVal,\n\t\t\t\ttrue,\n\t\t\t\tfalse,\n\t\t\t},\n\t\t}\n\n\t\tfor _, tc := range testCases {\n\t\t\tt.Run(tc.description, func(t *testing.T) {\n\t\t\t\tsessOpts := &ClientOptions{\n\t\t\t\t\tCausalConsistency: tc.consistent,\n\t\t\t\t\tSnapshot:          tc.snapshot,\n\t\t\t\t}\n\n\t\t\t\tid, _ := uuid.New()\n\t\t\t\tsess, err := NewClientSession(&Pool{}, id, sessOpts)\n\t\t\t\trequire.Nil(t, err, \"unexpected NewClientSession error %v\", err)\n\n\t\t\t\trequire.Equal(t, tc.expectedConsistent, sess.Consistent,\n\t\t\t\t\t\"expected Consistent to be %v, got %v\", tc.expectedConsistent, sess.Consistent)\n\t\t\t\trequire.Equal(t, tc.expectedSnapshot, sess.Snapshot,\n\t\t\t\t\t\"expected Snapshot to be %v, got %v\", tc.expectedSnapshot, sess.Snapshot)\n\t\t\t})\n\t\t}\n\t})\n}\n\nfunc TestImplicitClientSession(t *testing.T) {\n\tt.Parallel()\n\n\tt.Run(\"causal consistency is false\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tid, err := uuid.New()\n\t\trequire.NoError(t, err)\n\n\t\tc := NewImplicitClientSession(&Pool{}, id)\n\t\tassert.False(t, c.Consistent, \"expected causal consistency to be false for implicit sessions\")\n\t})\n}\n"
  },
  {
    "path": "x/mongo/driver/session/cluster_clock.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage session\n\nimport (\n\t\"sync\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n)\n\n// ClusterClock represents a logical clock for keeping track of cluster time.\ntype ClusterClock struct {\n\tclusterTime bson.Raw\n\tlock        sync.Mutex\n}\n\n// GetClusterTime returns the cluster's current time.\nfunc (cc *ClusterClock) GetClusterTime() bson.Raw {\n\tvar ct bson.Raw\n\tcc.lock.Lock()\n\tct = cc.clusterTime\n\tcc.lock.Unlock()\n\n\treturn ct\n}\n\n// AdvanceClusterTime updates the cluster's current time.\nfunc (cc *ClusterClock) AdvanceClusterTime(clusterTime bson.Raw) {\n\tcc.lock.Lock()\n\tcc.clusterTime = MaxClusterTime(cc.clusterTime, clusterTime)\n\tcc.lock.Unlock()\n}\n"
  },
  {
    "path": "x/mongo/driver/session/cluster_clock_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage session\n\nimport (\n\t\"bytes\"\n\t\"testing\"\n\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n)\n\nfunc TestClusterClock(t *testing.T) {\n\tclusterTime1 := bsoncore.BuildDocument(nil, bsoncore.AppendDocumentElement(nil, \"$clusterTime\", bsoncore.BuildDocument(nil, bsoncore.AppendTimestampElement(nil, \"clusterTime\", 10, 5))))\n\tclusterTime2 := bsoncore.BuildDocument(nil, bsoncore.AppendDocumentElement(nil, \"$clusterTime\", bsoncore.BuildDocument(nil, bsoncore.AppendTimestampElement(nil, \"clusterTime\", 5, 5))))\n\tclusterTime3 := bsoncore.BuildDocument(nil, bsoncore.AppendDocumentElement(nil, \"$clusterTime\", bsoncore.BuildDocument(nil, bsoncore.AppendTimestampElement(nil, \"clusterTime\", 5, 0))))\n\n\tt.Run(\"ClusterTime\", func(t *testing.T) {\n\t\tclock := ClusterClock{}\n\t\tclock.AdvanceClusterTime(clusterTime3)\n\t\tdone := make(chan struct{})\n\t\tgo func() {\n\t\t\tclock.AdvanceClusterTime(clusterTime1)\n\t\t\tdone <- struct{}{}\n\t\t}()\n\t\tclock.AdvanceClusterTime(clusterTime2)\n\n\t\t<-done\n\t\tif !bytes.Equal(clock.GetClusterTime(), clusterTime1) {\n\t\t\tt.Errorf(\"Expected cluster time %v, received %v\", clusterTime1, clock.GetClusterTime())\n\t\t}\n\t})\n}\n"
  },
  {
    "path": "x/mongo/driver/session/doc.go",
    "content": "// Copyright (C) MongoDB, Inc. 2024-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\n// Package session is intended for internal use only. It is made available to\n// facilitate use cases that require access to internal MongoDB driver\n// functionality and state. The API of this package is not stable and there is\n// no backward compatibility guarantee.\n//\n// WARNING: THIS PACKAGE IS EXPERIMENTAL AND MAY BE MODIFIED OR REMOVED WITHOUT\n// NOTICE! USE WITH EXTREME CAUTION!\npackage session\n"
  },
  {
    "path": "x/mongo/driver/session/options.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage session\n\nimport (\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/readconcern\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/readpref\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/writeconcern\"\n)\n\n// ClientOptions represents all possible options for creating a client session.\ntype ClientOptions struct {\n\tCausalConsistency     *bool\n\tDefaultReadConcern    *readconcern.ReadConcern\n\tDefaultWriteConcern   *writeconcern.WriteConcern\n\tDefaultReadPreference *readpref.ReadPref\n\tSnapshot              *bool\n\tSnapshotTime          *bson.Timestamp\n}\n\n// TransactionOptions represents all possible options for starting a transaction in a session.\ntype TransactionOptions struct {\n\tReadConcern    *readconcern.ReadConcern\n\tWriteConcern   *writeconcern.WriteConcern\n\tReadPreference *readpref.ReadPref\n}\n\nfunc mergeClientOptions(opts ...*ClientOptions) *ClientOptions {\n\tc := &ClientOptions{}\n\tfor _, opt := range opts {\n\t\tif opt == nil {\n\t\t\tcontinue\n\t\t}\n\t\tif opt.CausalConsistency != nil {\n\t\t\tc.CausalConsistency = opt.CausalConsistency\n\t\t}\n\t\tif opt.DefaultReadConcern != nil {\n\t\t\tc.DefaultReadConcern = opt.DefaultReadConcern\n\t\t}\n\t\tif opt.DefaultReadPreference != nil {\n\t\t\tc.DefaultReadPreference = opt.DefaultReadPreference\n\t\t}\n\t\tif opt.DefaultWriteConcern != nil {\n\t\t\tc.DefaultWriteConcern = opt.DefaultWriteConcern\n\t\t}\n\t\tif opt.Snapshot != nil {\n\t\t\tc.Snapshot = opt.Snapshot\n\t\t}\n\t\tif opt.SnapshotTime != nil {\n\t\t\tc.SnapshotTime = opt.SnapshotTime\n\t\t}\n\t}\n\n\treturn c\n}\n"
  },
  {
    "path": "x/mongo/driver/session/server_session.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage session\n\nimport (\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/uuid\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/description\"\n)\n\n// Server is an open session with the server.\ntype Server struct {\n\tSessionID bsoncore.Document\n\tTxnNumber int64\n\tLastUsed  time.Time\n\tDirty     bool\n}\n\n// returns whether or not a session has expired given a timeout in minutes\n// a session is considered expired if it has less than 1 minute left before becoming stale\nfunc (ss *Server) expired(topoDesc topologyDescription) bool {\n\t// There is no server monitoring in LB mode, so we do not track session timeout minutes from server hello responses\n\t// and never consider sessions to be expired.\n\tif topoDesc.kind == description.TopologyKindLoadBalanced {\n\t\treturn false\n\t}\n\n\tif topoDesc.timeoutMinutes == nil || *topoDesc.timeoutMinutes <= 0 {\n\t\treturn true\n\t}\n\ttimeUnused := time.Since(ss.LastUsed).Minutes()\n\treturn timeUnused > float64(*topoDesc.timeoutMinutes-1)\n}\n\n// update the last used time for this session.\n// must be called whenever this server session is used to send a command to the server.\nfunc (ss *Server) updateUseTime() {\n\tss.LastUsed = time.Now()\n}\n\nfunc newServerSession() (*Server, error) {\n\tid, err := uuid.New()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tidx, idDoc := bsoncore.AppendDocumentStart(nil)\n\tidDoc = bsoncore.AppendBinaryElement(idDoc, \"id\", UUIDSubtype, id[:])\n\tidDoc, _ = bsoncore.AppendDocumentEnd(idDoc, idx)\n\n\treturn &Server{\n\t\tSessionID: idDoc,\n\t\tLastUsed:  time.Now(),\n\t}, nil\n}\n\n// IncrementTxnNumber increments the transaction number.\nfunc (ss *Server) IncrementTxnNumber() {\n\tss.TxnNumber++\n}\n\n// MarkDirty marks the session as dirty.\nfunc (ss *Server) MarkDirty() {\n\tss.Dirty = true\n}\n\n// UUIDSubtype is the BSON binary subtype that a UUID should be encoded as\nconst UUIDSubtype byte = 4\n"
  },
  {
    "path": "x/mongo/driver/session/server_session_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage session\n\nimport (\n\t\"testing\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/description\"\n)\n\nfunc TestServerSession(t *testing.T) {\n\tint64ToPtr := func(i64 int64) *int64 { return &i64 }\n\n\tt.Run(\"Expired\", func(t *testing.T) {\n\t\tt.Run(\"non-lb mode\", func(t *testing.T) {\n\t\t\tsess, err := newServerSession()\n\t\t\tassert.Nil(t, err, \"newServerSession error: %v\", err)\n\n\t\t\t// The session should be expired if timeoutMinutes is 0 or if its last used time is too old.\n\t\t\tassert.True(t, sess.expired(topologyDescription{}), \"expected session to be expired when timeoutMinutes=0\")\n\t\t\tsess.LastUsed = time.Now().Add(-30 * time.Minute)\n\t\t\ttopoDesc := topologyDescription{timeoutMinutes: int64ToPtr(30)}\n\t\t\tassert.True(t, sess.expired(topoDesc), \"expected session to be expired when timeoutMinutes=30\")\n\t\t})\n\t\tt.Run(\"lb mode\", func(t *testing.T) {\n\t\t\tsess, err := newServerSession()\n\t\t\tassert.Nil(t, err, \"newServerSession error: %v\", err)\n\n\t\t\t// The session should never be considered expired.\n\t\t\ttopoDesc := topologyDescription{kind: description.TopologyKindLoadBalanced}\n\t\t\tassert.False(t, sess.expired(topoDesc), \"session reported that it was expired in LB mode with timeoutMinutes=0\")\n\n\t\t\tsess.LastUsed = time.Now().Add(-30 * time.Minute)\n\t\t\ttopoDesc.timeoutMinutes = int64ToPtr(10)\n\t\t\tassert.False(t, sess.expired(topoDesc), \"session reported that it was expired in LB mode with timeoutMinutes=10\")\n\t\t})\n\t})\n}\n"
  },
  {
    "path": "x/mongo/driver/session/session_pool.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage session\n\nimport (\n\t\"sync\"\n\t\"sync/atomic\"\n\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/description\"\n)\n\n// Node represents a server session in a linked list\ntype Node struct {\n\t*Server\n\tnext *Node\n\tprev *Node\n}\n\n// topologyDescription is used to track a subset of the fields present in a description.Topology instance that are\n// relevant for determining session expiration.\ntype topologyDescription struct {\n\tkind           description.TopologyKind\n\ttimeoutMinutes *int64\n}\n\n// Pool is a pool of server sessions that can be reused.\ntype Pool struct {\n\t// number of sessions checked out of pool (accessed atomically)\n\tcheckedOut int64\n\n\tdescChan       <-chan description.Topology\n\thead           *Node\n\ttail           *Node\n\tlatestTopology topologyDescription\n\tmutex          sync.Mutex // mutex to protect list and sessionTimeout\n}\n\nfunc (p *Pool) createServerSession() (*Server, error) {\n\ts, err := newServerSession()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tatomic.AddInt64(&p.checkedOut, 1)\n\treturn s, nil\n}\n\n// NewPool creates a new server session pool\nfunc NewPool(descChan <-chan description.Topology) *Pool {\n\tp := &Pool{\n\t\tdescChan: descChan,\n\t}\n\n\treturn p\n}\n\n// assumes caller has mutex to protect the pool\nfunc (p *Pool) updateTimeout() {\n\tselect {\n\tcase newDesc := <-p.descChan:\n\t\tp.latestTopology = topologyDescription{\n\t\t\tkind:           newDesc.Kind,\n\t\t\ttimeoutMinutes: newDesc.SessionTimeoutMinutes,\n\t\t}\n\tdefault:\n\t\t// no new description waiting\n\t}\n}\n\n// GetSession retrieves an unexpired session from the pool.\nfunc (p *Pool) GetSession() (*Server, error) {\n\tp.mutex.Lock() // prevent changing the linked list while seeing if sessions have expired\n\tdefer p.mutex.Unlock()\n\n\t// empty pool\n\tif p.head == nil && p.tail == nil {\n\t\treturn p.createServerSession()\n\t}\n\n\tp.updateTimeout()\n\tfor p.head != nil {\n\t\t// pull session from head of queue and return if it is valid for at least 1 more minute\n\t\tif p.head.expired(p.latestTopology) {\n\t\t\tp.head = p.head.next\n\t\t\tcontinue\n\t\t}\n\n\t\t// found unexpired session\n\t\tsession := p.head.Server\n\t\tif p.head.next != nil {\n\t\t\tp.head.next.prev = nil\n\t\t}\n\t\tif p.tail == p.head {\n\t\t\tp.tail = nil\n\t\t\tp.head = nil\n\t\t} else {\n\t\t\tp.head = p.head.next\n\t\t}\n\n\t\tatomic.AddInt64(&p.checkedOut, 1)\n\t\treturn session, nil\n\t}\n\n\t// no valid session found\n\tp.tail = nil // empty list\n\treturn p.createServerSession()\n}\n\n// ReturnSession returns a session to the pool if it has not expired.\nfunc (p *Pool) ReturnSession(ss *Server) {\n\tif ss == nil {\n\t\treturn\n\t}\n\n\tp.mutex.Lock()\n\tdefer p.mutex.Unlock()\n\n\tatomic.AddInt64(&p.checkedOut, -1)\n\tp.updateTimeout()\n\t// check sessions at end of queue for expired\n\t// stop checking after hitting the first valid session\n\tfor p.tail != nil && p.tail.expired(p.latestTopology) {\n\t\tif p.tail.prev != nil {\n\t\t\tp.tail.prev.next = nil\n\t\t}\n\t\tp.tail = p.tail.prev\n\t}\n\n\t// session expired\n\tif ss.expired(p.latestTopology) {\n\t\treturn\n\t}\n\n\t// session is dirty\n\tif ss.Dirty {\n\t\treturn\n\t}\n\n\tnewNode := &Node{\n\t\tServer: ss,\n\t\tnext:   nil,\n\t\tprev:   nil,\n\t}\n\n\t// empty list\n\tif p.tail == nil {\n\t\tp.head = newNode\n\t\tp.tail = newNode\n\t\treturn\n\t}\n\n\t// at least 1 valid session in list\n\tnewNode.next = p.head\n\tp.head.prev = newNode\n\tp.head = newNode\n}\n\n// IDSlice returns a slice of session IDs for each session in the pool\nfunc (p *Pool) IDSlice() []bsoncore.Document {\n\tp.mutex.Lock()\n\tdefer p.mutex.Unlock()\n\n\tvar ids []bsoncore.Document\n\tfor node := p.head; node != nil; node = node.next {\n\t\tids = append(ids, node.SessionID)\n\t}\n\n\treturn ids\n}\n\n// String implements the Stringer interface\nfunc (p *Pool) String() string {\n\tp.mutex.Lock()\n\tdefer p.mutex.Unlock()\n\n\ts := \"\"\n\tfor head := p.head; head != nil; head = head.next {\n\t\ts += head.SessionID.String() + \"\\n\"\n\t}\n\n\treturn s\n}\n\n// CheckedOut returns number of sessions checked out from pool.\nfunc (p *Pool) CheckedOut() int64 {\n\treturn atomic.LoadInt64(&p.checkedOut)\n}\n"
  },
  {
    "path": "x/mongo/driver/session/session_pool_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage session\n\nimport (\n\t\"bytes\"\n\t\"testing\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/description\"\n)\n\nfunc TestSessionPool(t *testing.T) {\n\tint64ToPtr := func(i64 int64) *int64 { return &i64 }\n\n\tt.Run(\"TestLifo\", func(t *testing.T) {\n\t\tdescChan := make(chan description.Topology)\n\t\tp := NewPool(descChan)\n\t\tp.latestTopology = topologyDescription{\n\t\t\ttimeoutMinutes: int64ToPtr(30), // Set to some arbitrarily high number greater than 1 minute.\n\t\t}\n\n\t\tfirst, err := p.GetSession()\n\t\tassert.Nil(t, err, \"GetSession error: %v\", err)\n\t\tfirstID := first.SessionID\n\n\t\tsecond, err := p.GetSession()\n\t\tassert.Nil(t, err, \"GetSession error: %v\", err)\n\t\tsecondID := second.SessionID\n\n\t\tp.ReturnSession(first)\n\t\tp.ReturnSession(second)\n\n\t\tsess, err := p.GetSession()\n\t\tassert.Nil(t, err, \"GetSession error: %v\", err)\n\t\tnextSess, err := p.GetSession()\n\t\tassert.Nil(t, err, \"GetSession error: %v\", err)\n\n\t\tassert.True(t, bytes.Equal(sess.SessionID, secondID),\n\t\t\t\"first session ID mismatch; expected %s, got %s\", secondID, sess.SessionID)\n\t\tassert.True(t, bytes.Equal(nextSess.SessionID, firstID),\n\t\t\t\"second session ID mismatch; expected %s, got %s\", firstID, nextSess.SessionID)\n\t})\n\n\tt.Run(\"TestExpiredRemoved\", func(t *testing.T) {\n\t\tdescChan := make(chan description.Topology)\n\t\tp := NewPool(descChan)\n\t\t// Set timeout minutes to 0 so new sessions will always become stale when returned\n\t\tp.latestTopology = topologyDescription{}\n\n\t\tfirst, err := p.GetSession()\n\t\tassert.Nil(t, err, \"GetSession error: %v\", err)\n\t\tfirstID := first.SessionID\n\n\t\tsecond, err := p.GetSession()\n\t\tassert.Nil(t, err, \"GetSession error: %v\", err)\n\t\tsecondID := second.SessionID\n\n\t\tp.ReturnSession(first)\n\t\tp.ReturnSession(second)\n\n\t\tsess, err := p.GetSession()\n\t\tassert.Nil(t, err, \"GetSession error: %v\", err)\n\n\t\tassert.False(t, bytes.Equal(sess.SessionID, firstID), \"first expired session was not removed\")\n\t\tassert.False(t, bytes.Equal(sess.SessionID, secondID), \"second expired session was not removed\")\n\t})\n}\n"
  },
  {
    "path": "x/mongo/driver/testdata/compression.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage driver\n\nimport (\n\t\"bytes\"\n\t\"compress/zlib\"\n\t\"fmt\"\n\t\"io\"\n\t\"sync\"\n\n\t\"github.com/klauspost/compress/snappy\"\n\t\"github.com/klauspost/compress/zstd\"\n\t\"go.mongodb.org/mongo-driver/x/mongo/driver/wiremessage\"\n)\n\n// CompressionOpts holds settings for how to compress a payload\ntype CompressionOpts struct {\n\tCompressor       wiremessage.CompressorID\n\tZlibLevel        int\n\tZstdLevel        int\n\tUncompressedSize int32\n}\n\nvar zstdEncoders sync.Map // map[zstd.EncoderLevel]*zstd.Encoder\n\nfunc getZstdEncoder(level zstd.EncoderLevel) (*zstd.Encoder, error) {\n\tif v, ok := zstdEncoders.Load(level); ok {\n\t\treturn v.(*zstd.Encoder), nil\n\t}\n\tencoder, err := zstd.NewWriter(nil, zstd.WithEncoderLevel(level))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tzstdEncoders.Store(level, encoder)\n\treturn encoder, nil\n}\n\nvar zlibEncoders sync.Map // map[int /*level*/]*zlibEncoder\n\nfunc getZlibEncoder(level int) (*zlibEncoder, error) {\n\tif v, ok := zlibEncoders.Load(level); ok {\n\t\treturn v.(*zlibEncoder), nil\n\t}\n\twriter, err := zlib.NewWriterLevel(nil, level)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tencoder := &zlibEncoder{writer: writer, buf: new(bytes.Buffer)}\n\tzlibEncoders.Store(level, encoder)\n\n\treturn encoder, nil\n}\n\ntype zlibEncoder struct {\n\tmu     sync.Mutex\n\twriter *zlib.Writer\n\tbuf    *bytes.Buffer\n}\n\nfunc (e *zlibEncoder) Encode(dst, src []byte) ([]byte, error) {\n\te.mu.Lock()\n\tdefer e.mu.Unlock()\n\n\te.buf.Reset()\n\te.writer.Reset(e.buf)\n\n\t_, err := e.writer.Write(src)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\terr = e.writer.Close()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdst = append(dst[:0], e.buf.Bytes()...)\n\treturn dst, nil\n}\n\n// CompressPayload takes a byte slice and compresses it according to the options passed\nfunc CompressPayload(in []byte, opts CompressionOpts) ([]byte, error) {\n\tswitch opts.Compressor {\n\tcase wiremessage.CompressorNoOp:\n\t\treturn in, nil\n\tcase wiremessage.CompressorSnappy:\n\t\treturn snappy.Encode(nil, in), nil\n\tcase wiremessage.CompressorZLib:\n\t\tencoder, err := getZlibEncoder(opts.ZlibLevel)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\treturn encoder.Encode(nil, in)\n\tcase wiremessage.CompressorZstd:\n\t\tencoder, err := getZstdEncoder(zstd.EncoderLevelFromZstd(opts.ZstdLevel))\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\treturn encoder.EncodeAll(in, nil), nil\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"unknown compressor ID %v\", opts.Compressor)\n\t}\n}\n\n// DecompressPayload takes a byte slice that has been compressed and undoes it according to the options passed\nfunc DecompressPayload(in []byte, opts CompressionOpts) (uncompressed []byte, err error) {\n\tswitch opts.Compressor {\n\tcase wiremessage.CompressorNoOp:\n\t\treturn in, nil\n\tcase wiremessage.CompressorSnappy:\n\t\tl, err := snappy.DecodedLen(in)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"decoding compressed length %w\", err)\n\t\t} else if int32(l) != opts.UncompressedSize {\n\t\t\treturn nil, fmt.Errorf(\"unexpected decompression size, expected %v but got %v\", opts.UncompressedSize, l)\n\t\t}\n\t\tuncompressed = make([]byte, opts.UncompressedSize)\n\t\treturn snappy.Decode(uncompressed, in)\n\tcase wiremessage.CompressorZLib:\n\t\tr, err := zlib.NewReader(bytes.NewReader(in))\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tdefer func() {\n\t\t\terr = r.Close()\n\t\t}()\n\t\tuncompressed = make([]byte, opts.UncompressedSize)\n\t\t_, err = io.ReadFull(r, uncompressed)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\treturn uncompressed, nil\n\tcase wiremessage.CompressorZstd:\n\t\tr, err := zstd.NewReader(bytes.NewBuffer(in))\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tdefer r.Close()\n\t\tuncompressed = make([]byte, opts.UncompressedSize)\n\t\t_, err = io.ReadFull(r, uncompressed)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\treturn uncompressed, nil\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"unknown compressor ID %v\", opts.Compressor)\n\t}\n}\n"
  },
  {
    "path": "x/mongo/driver/topology/CMAP_spec_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage topology\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io/ioutil\"\n\t\"net\"\n\t\"path\"\n\t\"strings\"\n\t\"sync\"\n\t\"sync/atomic\"\n\t\"testing\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/event\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/require\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/spectest\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/mnet\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/operation\"\n)\n\n// skippedTestDescriptions is a collection of test descriptions that the test runner will skip. The\n// map format is {\"test description\": \"reason\", }.\nvar skippedTestDescriptions = map[string]string{\n\t// GODRIVER-1827: These 2 tests assert that in-use connections are not closed until checked\n\t// back into a closed pool, but the Go connection pool aggressively closes in-use connections.\n\t// That behavior is currently required by the \"Client.Disconnect\" API, so skip the tests.\n\t\"When a pool is closed, it MUST first destroy all available connections in that pool\": \"test requires that close does not aggressively close used connections\",\n\t\"must destroy checked in connection if pool has been closed\":                          \"test requires that close does not aggressively close used connections\",\n\t// GODRIVER-1826: The load-balancer SDAM error handling test \"errors during authentication are\n\t// processed\" currently asserts that handshake errors trigger events \"pool cleared\" then\n\t// \"connection closed\". However, the \"error during minPoolSize population clears pool\" test\n\t// asserts that handshake errors trigger events \"connection closed\" then \"pool cleared\". The Go\n\t// driver uses the same code path for creating all application connections, so those opposing\n\t// event orders cannot be satisfied simultaneously.\n\t// TODO(DRIVERS-1785): Re-enable this test once the spec test is updated to use the same event order as the \"errors\n\t// TODO during authentication are processed\" load-balancer SDAM spec test.\n\t\"error during minPoolSize population clears pool\": \"event ordering is incompatible with load-balancer SDAM spec test (DRIVERS-1785)\",\n\t// GODRIVER-1826: The Go connection pool does not currently always deliver connections created\n\t// by maintain() to waiting check-outs. There is a race condition between the goroutine started\n\t// by maintain() to check-in a requested connection and createConnections() picking up the next\n\t// wantConn created by the waiting check-outs. Most of the time, createConnections() wins and\n\t// starts creating new connections. That is not a problem for general use cases, but it prevents\n\t// the \"threads blocked by maxConnecting check out minPoolSize connections\" test from passing.\n\t// TODO(DRIVERS-2225): Re-enable this test once the spec test is updated to support the Go pool minPoolSize\n\t// TODO maintain() behavior.\n\t\"threads blocked by maxConnecting check out minPoolSize connections\": \"test requires that connections established by minPoolSize are immediately used to satisfy check-out requests (DRIVERS-2225)\",\n\t// GODRIVER-1826: The Go connection pool currently delivers any available connection to the\n\t// earliest waiting check-out request, independent of if that check-out request already\n\t// requested a new connection. That behavior is currently incompatible with the \"threads blocked\n\t// by maxConnecting check out returned connections\" test, which expects that check-out requests\n\t// that request a new connection cannot be satisfied by a check-in.\n\t// TODO(DRIVERS-2223): Re-enable this test once the spec test is updated to support the Go pool check-in behavior.\n\t\"threads blocked by maxConnecting check out returned connections\": \"test requires a checked-in connections cannot satisfy a check-out waiting on a new connection (DRIVERS-2223)\",\n}\n\ntype cmapEvent struct {\n\tEventType    string `json:\"type\"`\n\tAddress      any    `json:\"address\"`\n\tConnectionID int64  `json:\"connectionId\"`\n\tOptions      any    `json:\"options\"`\n\tReason       string `json:\"reason\"`\n}\n\ntype poolOptions struct {\n\tMaxPoolSize                int32 `json:\"maxPoolSize\"`\n\tMinPoolSize                int32 `json:\"minPoolSize\"`\n\tMaxConnecting              int32 `json:\"maxConnecting\"`\n\tMaxIdleTimeMS              int32 `json:\"maxIdleTimeMS\"`\n\tWaitQueueTimeoutMS         int32 `json:\"waitQueueTimeoutMS\"`\n\tBackgroundThreadIntervalMS int32 `json:\"backgroundThreadIntervalMS\"`\n}\n\ntype cmapTestFile struct {\n\tVersion     uint64           `json:\"version\"`\n\tStyle       string           `json:\"style\"`\n\tDescription string           `json:\"description\"`\n\tSkipReason  string           `json:\"skipReason\"`\n\tFailPoint   map[string]any   `json:\"failPoint\"`\n\tPoolOptions poolOptions      `json:\"poolOptions\"`\n\tOperations  []map[string]any `json:\"operations\"`\n\tError       *cmapTestError   `json:\"error\"`\n\tEvents      []cmapEvent      `json:\"events\"`\n\tIgnore      []string         `json:\"ignore\"`\n}\n\ntype cmapTestError struct {\n\tErrorType string `json:\"type\"`\n\tMessage   string `json:\"message\"`\n\tAddress   string `json:\"address\"`\n}\n\ntype simThread struct {\n\tJobQueue      chan func()\n\tJobsAssigned  int32\n\tJobsCompleted int32\n}\n\ntype testInfo struct {\n\tobjects                map[string]any\n\toriginalEventChan      chan *event.PoolEvent\n\tfinalEventChan         chan *event.PoolEvent\n\tthreads                map[string]*simThread\n\tbackgroundThreadErrors chan error\n\teventCounts            map[string]uint64\n\tsync.Mutex\n}\n\nvar cmapTestDir = spectest.Path(\"connection-monitoring-and-pooling/tests/cmap-format\")\n\nfunc TestCMAPSpec(t *testing.T) {\n\tfor _, testFileName := range spectest.FindJSONFilesInDir(t, cmapTestDir) {\n\t\tt.Run(testFileName, func(t *testing.T) {\n\t\t\trunCMAPTest(t, testFileName)\n\t\t})\n\t}\n}\n\nfunc runCMAPTest(t *testing.T, testFileName string) {\n\tcontent, err := ioutil.ReadFile(path.Join(cmapTestDir, testFileName))\n\trequire.NoErrorf(t, err, \"unable to read content of test file\")\n\n\tvar test cmapTestFile\n\terr = json.Unmarshal(content, &test)\n\trequire.NoErrorf(t, err, \"error unmarshalling testFile\")\n\n\tif test.SkipReason != \"\" {\n\t\tt.Skip(test.SkipReason)\n\t}\n\tif msg, ok := skippedTestDescriptions[test.Description]; ok {\n\t\tt.Skip(msg)\n\t}\n\n\ttestInfo := &testInfo{\n\t\tobjects:                make(map[string]any),\n\t\toriginalEventChan:      make(chan *event.PoolEvent, 200),\n\t\tfinalEventChan:         make(chan *event.PoolEvent, 200),\n\t\tthreads:                make(map[string]*simThread),\n\t\teventCounts:            make(map[string]uint64),\n\t\tbackgroundThreadErrors: make(chan error, 100),\n\t}\n\n\tsOpts := []ServerOption{\n\t\tWithMaxConnections(func(uint64) uint64 {\n\t\t\treturn uint64(test.PoolOptions.MaxPoolSize)\n\t\t}),\n\t\tWithMinConnections(func(uint64) uint64 {\n\t\t\treturn uint64(test.PoolOptions.MinPoolSize)\n\t\t}),\n\t\tWithMaxConnecting(func(uint64) uint64 {\n\t\t\treturn uint64(test.PoolOptions.MaxConnecting)\n\t\t}),\n\t\tWithConnectionPoolMaxIdleTime(func(time.Duration) time.Duration {\n\t\t\treturn time.Duration(test.PoolOptions.MaxIdleTimeMS) * time.Millisecond\n\t\t}),\n\t\tWithConnectionPoolMaintainInterval(func(time.Duration) time.Duration {\n\t\t\treturn time.Duration(test.PoolOptions.BackgroundThreadIntervalMS) * time.Millisecond\n\t\t}),\n\t\tWithConnectionPoolMonitor(func(*event.PoolMonitor) *event.PoolMonitor {\n\t\t\treturn &event.PoolMonitor{\n\t\t\t\tEvent: func(evt *event.PoolEvent) { testInfo.originalEventChan <- evt },\n\t\t\t}\n\t\t}),\n\t}\n\n\tvar delay time.Duration\n\tvar closeConnection bool\n\n\tif test.FailPoint != nil {\n\t\tdata, ok := test.FailPoint[\"data\"].(map[string]any)\n\t\tif !ok {\n\t\t\tt.Fatalf(\"expected to find \\\"data\\\" map in failPoint (%v)\", test.FailPoint)\n\t\t}\n\n\t\tblockConnection, _ := data[\"blockConnection\"].(bool)\n\t\tif blockTimeMS, ok := data[\"blockTimeMS\"].(float64); ok && blockConnection {\n\t\t\tdelay = time.Duration(blockTimeMS) * time.Millisecond\n\t\t}\n\n\t\tcloseConnection, _ = data[\"closeConnection\"].(bool)\n\t}\n\n\t// Use a dialer that returns mock connections that always respond with a\n\t// \"hello\" reply. If there's a failpoint configured in the test, use a\n\t// dialer that returns connections that mock the configured failpoint.\n\tsOpts = append(sOpts, WithConnectionOptions(func(...ConnectionOption) []ConnectionOption {\n\t\treturn []ConnectionOption{\n\t\t\tWithDialer(func(Dialer) Dialer {\n\t\t\t\treturn DialerFunc(func(_ context.Context, _, _ string) (net.Conn, error) {\n\t\t\t\t\tmsc := newMockSlowConn(makeHelloReply(), delay)\n\t\t\t\t\tif closeConnection {\n\t\t\t\t\t\tmsc.Close()\n\t\t\t\t\t}\n\t\t\t\t\treturn msc, nil\n\t\t\t\t})\n\t\t\t}),\n\t\t\tWithHandshaker(func(Handshaker) Handshaker {\n\t\t\t\treturn operation.NewHello()\n\t\t\t}),\n\t\t}\n\t}))\n\n\ts := NewServer(\"mongodb://fake\", bson.NewObjectID(), defaultConnectionTimeout, sOpts...)\n\ts.state = serverConnected\n\trequire.NoError(t, err, \"error connecting connection pool\")\n\tdefer s.pool.close(context.Background())\n\n\tfor _, op := range test.Operations {\n\t\tif tempErr := runOperation(t, op, testInfo, s, test.PoolOptions.WaitQueueTimeoutMS); tempErr != nil {\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"received multiple errors in primary thread: %v and %v\", err, tempErr)\n\t\t\t}\n\t\t\terr = tempErr\n\t\t}\n\t}\n\n\t// make sure all threads have finished\n\ttestInfo.Lock()\n\tthreadNames := make([]string, 0)\n\tfor threadName := range testInfo.threads {\n\t\tthreadNames = append(threadNames, threadName)\n\t}\n\ttestInfo.Unlock()\n\n\tfor _, threadName := range threadNames {\n\tWAIT:\n\t\tfor {\n\t\t\ttestInfo.Lock()\n\t\t\tthread, ok := testInfo.threads[threadName]\n\t\t\tif !ok {\n\t\t\t\tt.Fatalf(\"thread was unexpectedly ended: %v\", threadName)\n\t\t\t}\n\t\t\tif len(thread.JobQueue) == 0 && atomic.LoadInt32(&thread.JobsCompleted) == atomic.LoadInt32(&thread.JobsAssigned) {\n\t\t\t\tbreak WAIT\n\t\t\t}\n\t\t\ttestInfo.Unlock()\n\t\t}\n\t\tclose(testInfo.threads[threadName].JobQueue)\n\t\ttestInfo.Unlock()\n\t}\n\n\tif test.Error != nil {\n\t\tif err == nil || strings.ToLower(test.Error.Message) != err.Error() {\n\t\t\tvar erroredCorrectly bool\n\t\t\terrs := make([]error, 0, len(testInfo.backgroundThreadErrors)+1)\n\t\t\terrs = append(errs, err)\n\t\t\tfor len(testInfo.backgroundThreadErrors) > 0 {\n\t\t\t\tbgErr := <-testInfo.backgroundThreadErrors\n\t\t\t\terrs = append(errs, bgErr)\n\t\t\t\tif bgErr != nil && strings.Contains(bgErr.Error(), strings.ToLower(test.Error.Message)) {\n\t\t\t\t\terroredCorrectly = true\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t\tif !erroredCorrectly {\n\t\t\t\tt.Fatalf(\"error differed from expected error, expected: %v, actual errors received: %v\", test.Error.Message, errs)\n\t\t\t}\n\t\t}\n\t}\n\n\ttestInfo.Lock()\n\tdefer testInfo.Unlock()\n\tfor len(testInfo.originalEventChan) > 0 {\n\t\ttemp := <-testInfo.originalEventChan\n\t\ttestInfo.finalEventChan <- temp\n\t}\n\n\tcheckEvents(t, test.Events, testInfo.finalEventChan, test.Ignore)\n}\n\nfunc checkEvents(t *testing.T, expectedEvents []cmapEvent, actualEvents chan *event.PoolEvent, ignoreEvents []string) {\n\tfor _, expectedEvent := range expectedEvents {\n\t\tvalidEvent := nextValidEvent(t, actualEvents, ignoreEvents)\n\n\t\tif expectedEvent.EventType != validEvent.Type {\n\t\t\tvar reason string\n\t\t\tif validEvent.Type == \"ConnectionCheckOutFailed\" {\n\t\t\t\treason = \": \" + validEvent.Reason\n\t\t\t}\n\t\t\tt.Errorf(\"unexpected event occurred: expected: %v, actual: %v%v\", expectedEvent.EventType, validEvent.Type, reason)\n\t\t}\n\n\t\tif expectedEvent.Address != nil {\n\t\t\tif expectedEvent.Address == float64(42) { // can be any address\n\t\t\t\tif validEvent.Address == \"\" {\n\t\t\t\t\tt.Errorf(\"expected address in event, instead received none in %v\", expectedEvent.EventType)\n\t\t\t\t}\n\t\t\t} else { // must be specific address\n\t\t\t\taddr, ok := expectedEvent.Address.(string)\n\t\t\t\tif !ok {\n\t\t\t\t\tt.Errorf(\"received non string address: %v\", expectedEvent.Address)\n\t\t\t\t}\n\t\t\t\tif addr != validEvent.Address {\n\t\t\t\t\tt.Errorf(\"received unexpected address: %v, expected: %v\", validEvent.Address, expectedEvent.Address)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif expectedEvent.ConnectionID != 0 {\n\t\t\tif expectedEvent.ConnectionID == 42 {\n\t\t\t\tif validEvent.ConnectionID == 0 {\n\t\t\t\t\tt.Errorf(\"expected a connectionId but found none in %v\", validEvent.Type)\n\t\t\t\t}\n\t\t\t} else if expectedEvent.ConnectionID != validEvent.ConnectionID {\n\t\t\t\tt.Errorf(\"expected and actual connectionIds differed: expected: %v, actual: %v for event: %v\", expectedEvent.ConnectionID, validEvent.ConnectionID, expectedEvent.EventType)\n\t\t\t}\n\t\t}\n\n\t\tif expectedEvent.Reason != \"\" && expectedEvent.Reason != validEvent.Reason {\n\t\t\tt.Errorf(\"event reason differed from expected: expected: %v, actual: %v for %v\", expectedEvent.Reason, validEvent.Reason, expectedEvent.EventType)\n\t\t}\n\n\t\tif expectedEvent.Options != nil {\n\t\t\tif expectedEvent.Options == float64(42) {\n\t\t\t\tif validEvent.PoolOptions == nil {\n\t\t\t\t\tt.Errorf(\"expected poolevent options but found none\")\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\topts, ok := expectedEvent.Options.(map[string]any)\n\t\t\t\tif !ok {\n\t\t\t\t\tt.Errorf(\"event options were unexpected type: %T for %v\", expectedEvent.Options, expectedEvent.EventType)\n\t\t\t\t}\n\n\t\t\t\tif maxSize, ok := opts[\"maxPoolSize\"]; ok && validEvent.PoolOptions.MaxPoolSize != uint64(maxSize.(float64)) {\n\t\t\t\t\tt.Errorf(\"event's max pool size differed from expected: %v, actual: %v\", maxSize, validEvent.PoolOptions.MaxPoolSize)\n\t\t\t\t}\n\n\t\t\t\tif minSize, ok := opts[\"minPoolSize\"]; ok && validEvent.PoolOptions.MinPoolSize != uint64(minSize.(float64)) {\n\t\t\t\t\tt.Errorf(\"event's min pool size differed from expected: %v, actual: %v\", minSize, validEvent.PoolOptions.MinPoolSize)\n\t\t\t\t}\n\n\t\t\t\tif waitQueueTimeoutMS, ok := opts[\"waitQueueTimeoutMS\"]; ok && validEvent.PoolOptions.WaitQueueTimeoutMS != uint64(waitQueueTimeoutMS.(float64)) {\n\t\t\t\t\tt.Errorf(\"event's min pool size differed from expected: %v, actual: %v\", waitQueueTimeoutMS, validEvent.PoolOptions.WaitQueueTimeoutMS)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc nextValidEvent(t *testing.T, events chan *event.PoolEvent, ignoreEvents []string) *event.PoolEvent {\n\tt.Helper()\nNextEvent:\n\tfor {\n\t\tif len(events) == 0 {\n\t\t\tt.Fatalf(\"unable to get next event. too few events occurred\")\n\t\t}\n\n\t\tevent := <-events\n\t\tfor _, Type := range ignoreEvents {\n\t\t\tif event.Type == Type {\n\t\t\t\tcontinue NextEvent\n\t\t\t}\n\t\t}\n\t\treturn event\n\t}\n}\n\nfunc runOperation(t *testing.T, operation map[string]any, testInfo *testInfo, s *Server, checkOutTimeout int32) error {\n\tthreadName, ok := operation[\"thread\"]\n\tif ok { // to be run in background thread\n\t\ttestInfo.Lock()\n\t\tthread, ok := testInfo.threads[threadName.(string)]\n\t\tif !ok {\n\t\t\tthread = &simThread{\n\t\t\t\tJobQueue: make(chan func(), 200),\n\t\t\t}\n\t\t\ttestInfo.threads[threadName.(string)] = thread\n\n\t\t\tgo func() {\n\t\t\t\tfor {\n\t\t\t\t\tjob, more := <-thread.JobQueue\n\t\t\t\t\tif !more {\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t\tjob()\n\t\t\t\t\tatomic.AddInt32(&thread.JobsCompleted, 1)\n\t\t\t\t}\n\t\t\t}()\n\t\t}\n\t\ttestInfo.Unlock()\n\n\t\tatomic.AddInt32(&thread.JobsAssigned, 1)\n\t\tthread.JobQueue <- func() {\n\t\t\terr := runOperationInThread(t, operation, testInfo, s, checkOutTimeout)\n\t\t\ttestInfo.backgroundThreadErrors <- err\n\t\t}\n\n\t\treturn nil // since we don't care about errors occurring in non primary threads\n\t}\n\treturn runOperationInThread(t, operation, testInfo, s, checkOutTimeout)\n}\n\nfunc runOperationInThread(t *testing.T, operation map[string]any, testInfo *testInfo, s *Server, checkOutTimeout int32) error {\n\tname, ok := operation[\"name\"]\n\tif !ok {\n\t\tt.Fatalf(\"unable to find name in operation\")\n\t}\n\n\tswitch name {\n\tcase \"start\":\n\t\treturn nil // we dont need to start another thread since this has already been done in runOperation\n\tcase \"wait\":\n\t\ttimeMs, ok := operation[\"ms\"]\n\t\tif !ok {\n\t\t\tt.Fatalf(\"unable to find ms in wait operation\")\n\t\t}\n\t\tdur := time.Duration(int64(timeMs.(float64))) * time.Millisecond\n\t\ttime.Sleep(dur)\n\tcase \"waitForThread\":\n\t\tthreadName, ok := operation[\"target\"]\n\t\tif !ok {\n\t\t\tt.Fatalf(\"unable to waitForThread without specified threadName\")\n\t\t}\n\n\t\ttestInfo.Lock()\n\t\tthread, ok := testInfo.threads[threadName.(string)]\n\t\ttestInfo.Unlock()\n\t\tif !ok {\n\t\t\tt.Fatalf(\"unable to find thread to wait for: %v\", threadName)\n\t\t}\n\n\t\tfor {\n\t\t\t// nolint:staticcheck // QF1006: staticcheck solution onflicts with revive\n\t\t\tif atomic.LoadInt32(&thread.JobsCompleted) == atomic.LoadInt32(&thread.JobsAssigned) {\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\tcase \"waitForEvent\":\n\t\tvar targetCount int\n\t\t{\n\t\t\tf, ok := operation[\"count\"].(float64)\n\t\t\tif !ok {\n\t\t\t\tt.Fatalf(\"count is required to waitForEvent\")\n\t\t\t}\n\t\t\ttargetCount = int(f)\n\t\t}\n\n\t\ttargetEventName, ok := operation[\"event\"].(string)\n\t\tif !ok {\n\t\t\tt.Fatalf(\"event is require to waitForEvent\")\n\t\t}\n\n\t\t// If there is a timeout specified in the \"waitForEvent\" operation, then use that timeout.\n\t\t// Otherwise, use a default timeout of 10s when waiting for events. Using a default timeout\n\t\t// prevents the Go test runner from timing out, which just prints a stack trace and no\n\t\t// information about what event the test was waiting for.\n\t\ttimeout := 10 * time.Second\n\t\tif timeoutMS, ok := operation[\"timeout\"].(float64); ok {\n\t\t\ttimeout = time.Duration(timeoutMS) * time.Millisecond\n\t\t}\n\n\t\toriginalChan := testInfo.originalEventChan\n\t\tfinalChan := testInfo.finalEventChan\n\n\t\tfor {\n\t\t\tvar event *event.PoolEvent\n\t\t\t{\n\t\t\t\ttimer := time.NewTimer(timeout)\n\t\t\t\tselect {\n\t\t\t\tcase event = <-originalChan:\n\t\t\t\tcase <-timer.C:\n\t\t\t\t\tt.Fatalf(\"timed out waiting for %d %q events\", targetCount, targetEventName)\n\t\t\t\t}\n\t\t\t\ttimer.Stop()\n\t\t\t}\n\t\t\tfinalChan <- event\n\n\t\t\ttestInfo.Lock()\n\t\t\t_, ok = testInfo.eventCounts[event.Type]\n\t\t\tif !ok {\n\t\t\t\ttestInfo.eventCounts[event.Type] = 0\n\t\t\t}\n\t\t\ttestInfo.eventCounts[event.Type]++\n\t\t\tcount := testInfo.eventCounts[event.Type]\n\t\t\ttestInfo.Unlock()\n\n\t\t\tif event.Type == targetEventName && count == uint64(targetCount) {\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\tcase \"checkOut\":\n\t\tcheckoutContext := context.Background()\n\t\tif checkOutTimeout != 0 {\n\t\t\tvar cancel context.CancelFunc\n\t\t\tcheckoutContext, cancel = context.WithTimeout(context.Background(), time.Duration(checkOutTimeout)*time.Millisecond)\n\t\t\tdefer cancel()\n\t\t}\n\n\t\tc, err := s.Connection(checkoutContext)\n\t\tif label, ok := operation[\"label\"]; ok {\n\t\t\ttestInfo.Lock()\n\t\t\ttestInfo.objects[label.(string)] = c\n\t\t\ttestInfo.Unlock()\n\t\t}\n\n\t\treturn err\n\tcase \"checkIn\":\n\t\tcName, ok := operation[\"connection\"]\n\t\tif !ok {\n\t\t\tt.Fatalf(\"unable to find connection to checkin\")\n\t\t}\n\n\t\tvar cEmptyInterface any\n\t\ttestInfo.Lock()\n\t\tcEmptyInterface, ok = testInfo.objects[cName.(string)]\n\t\tdelete(testInfo.objects, cName.(string))\n\t\ttestInfo.Unlock()\n\t\tif !ok {\n\t\t\tt.Fatalf(\"was unable to find %v in objects when expected\", cName)\n\t\t}\n\n\t\tc, ok := cEmptyInterface.(*mnet.Connection)\n\t\tif !ok {\n\t\t\tt.Fatalf(\"object in objects was expected to be a connection, but was instead a %T\", cEmptyInterface)\n\t\t}\n\t\treturn c.Close()\n\tcase \"clear\":\n\t\tneedInterruption, ok := operation[\"interruptInUseConnections\"].(bool)\n\t\tif ok && needInterruption {\n\t\t\ts.pool.clearAll(fmt.Errorf(\"spec test clear\"), nil)\n\t\t} else {\n\t\t\ts.pool.clear(fmt.Errorf(\"spec test clear\"), nil)\n\t\t}\n\tcase \"close\":\n\t\ts.pool.close(context.Background())\n\tcase \"ready\":\n\t\treturn s.pool.ready()\n\tdefault:\n\t\tt.Fatalf(\"unknown operation: %v\", name)\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "x/mongo/driver/topology/DESIGN.md",
    "content": "# Topology Package Design\n\nThis document outlines the design for this package.\n\n## Topology\n\nThe `Topology` type handles monitoring the state of a MongoDB deployment and selecting servers.\nUpdating the description is handled by finite state machine which implements the server discovery\nand monitoring specification. A `Topology` can be connected and fully disconnected, which enables\nsaving resources. The `Topology` type also handles server selection following the server selection\nspecification.\n\n## Server\n\nThe `Server` type handles heartbeating a MongoDB server and holds a pool of connections.\n\n## Connection\n\nConnections are handled by two main types and an auxiliary type. The two main types are `connection`\nand `Connection`. The first holds most of the logic required to actually read and write wire\nmessages. Instances can be created with the `newConnection` method. Inside the `newConnection`\nmethod the auxiliary type, `initConnection` is used to perform the connection handshake. This is\nrequired because the `connection` type does not fully implement `driver.Connection` which is\nrequired during handshaking. The `Connection` type is what is actually returned to a consumer of the\n`topology` package. This type does implement the `driver.Connection` type, holds a reference to a\n`connection` instance, and exists mainly to prevent accidental continued usage of a connection after\nclosing it.\n\nThe connection implementations in this package are conduits for wire messages but they have no\nability to encode, decode, or validate wire messages. That must be handled by consumers.\n\n## Pool\n\nThe `pool` type implements a connection pool. It handles caching idle connections and dialing\nnew ones, but it does not track a maximum number of connections. That is the responsibility of a\nwrapping type, like `Server`.\n\nThe `pool` type has no concept of closing, instead it has concepts of connecting and disconnecting.\nThis allows a `Topology` to be disconnected,but keeping the memory around to be reconnected later.\nThere is a `close` method, but this is used to close a connection.\n\nThere are three methods related to getting and putting connections: `get`, `close`, and `put`. The\n`get` method will either retrieve a connection from the cache or it will dial a new `connection`.\nThe `close` method will close the underlying socket of a `connection`. The `put` method will put a\nconnection into the pool, placing it in the cache if there is space, otherwise it will close it.\n"
  },
  {
    "path": "x/mongo/driver/topology/cmap_prose_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage topology\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"net\"\n\t\"testing\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/event\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/require\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/operation\"\n)\n\nfunc TestCMAPProse(t *testing.T) {\n\tt.Run(\"created and closed events\", func(t *testing.T) {\n\t\tcreated := make(chan *event.PoolEvent, 10)\n\t\tclosed := make(chan *event.PoolEvent, 10)\n\t\tclearEvents := func() {\n\t\t\tfor len(created) > 0 {\n\t\t\t\t<-created\n\t\t\t}\n\t\t\tfor len(closed) > 0 {\n\t\t\t\t<-closed\n\t\t\t}\n\t\t}\n\t\tmonitor := &event.PoolMonitor{\n\t\t\tEvent: func(evt *event.PoolEvent) {\n\t\t\t\tswitch evt.Type {\n\t\t\t\tcase event.ConnectionCreated:\n\t\t\t\t\tcreated <- evt\n\t\t\t\tcase event.ConnectionClosed:\n\t\t\t\t\tclosed <- evt\n\t\t\t\t}\n\t\t\t},\n\t\t}\n\t\tgetConfig := func() poolConfig {\n\t\t\treturn poolConfig{\n\t\t\t\tPoolMonitor: monitor,\n\t\t\t}\n\t\t}\n\t\tassertConnectionCounts := func(t *testing.T, p *pool, numCreated, numClosed int) {\n\t\t\tt.Helper()\n\n\t\t\trequire.Eventuallyf(t,\n\t\t\t\tfunc() bool {\n\t\t\t\t\treturn numCreated == len(created) && numClosed == len(closed)\n\t\t\t\t},\n\t\t\t\t1*time.Second,\n\t\t\t\t10*time.Millisecond,\n\t\t\t\t\"expected %d creation events, got %d; expected %d closed events, got %d\",\n\t\t\t\tnumCreated,\n\t\t\t\tlen(created),\n\t\t\t\tnumClosed,\n\t\t\t\tlen(closed))\n\n\t\t\tnetCount := numCreated - numClosed\n\t\t\tassert.Equal(t, netCount, p.totalConnectionCount(), \"expected %d total connections, got %d\", netCount,\n\t\t\t\tp.totalConnectionCount())\n\t\t}\n\n\t\tt.Run(\"maintain\", func(t *testing.T) {\n\t\t\tt.Run(\"connection error publishes events\", func(t *testing.T) {\n\t\t\t\t// If a connection is created as part of minPoolSize maintenance and errors while connecting, checkOut()\n\t\t\t\t// should report that error and publish an event.\n\t\t\t\t// If maintain() creates a connection that encounters an error while connecting,\n\t\t\t\t// the pool should publish connection created and closed events.\n\t\t\t\tclearEvents()\n\n\t\t\t\tvar dialer DialerFunc = func(context.Context, string, string) (net.Conn, error) {\n\t\t\t\t\treturn &testNetConn{writeerr: errors.New(\"write error\")}, nil\n\t\t\t\t}\n\n\t\t\t\tcfg := getConfig()\n\t\t\t\tcfg.MinPoolSize = 1\n\t\t\t\tconnOpts := []ConnectionOption{\n\t\t\t\t\tWithDialer(func(Dialer) Dialer { return dialer }),\n\t\t\t\t\tWithHandshaker(func(Handshaker) Handshaker {\n\t\t\t\t\t\treturn operation.NewHello()\n\t\t\t\t\t}),\n\t\t\t\t}\n\t\t\t\tpool := createTestPool(t, cfg, connOpts...)\n\t\t\t\tdefer pool.close(context.Background())\n\n\t\t\t\t// Wait up to 3 seconds for the maintain() goroutine to run and for 1 connection\n\t\t\t\t// created and 1 connection closed events to be published.\n\t\t\t\tstart := time.Now()\n\t\t\t\tfor len(created) != 1 || len(closed) != 1 {\n\t\t\t\t\tif time.Since(start) > 3*time.Second {\n\t\t\t\t\t\tt.Errorf(\n\t\t\t\t\t\t\t\"Expected 1 connection created and 1 connection closed events within 3 seconds. \"+\n\t\t\t\t\t\t\t\t\"Actual created events: %d, actual closed events: %d\",\n\t\t\t\t\t\t\tlen(created),\n\t\t\t\t\t\t\tlen(closed))\n\t\t\t\t\t}\n\t\t\t\t\ttime.Sleep(time.Millisecond)\n\t\t\t\t}\n\t\t\t})\n\t\t})\n\t\tt.Run(\"checkOut\", func(t *testing.T) {\n\t\t\tt.Run(\"connection error publishes events\", func(t *testing.T) {\n\t\t\t\t// If checkOut() creates a connection that encounters an error while connecting,\n\t\t\t\t// the pool should publish connection created and closed events and checkOut should\n\t\t\t\t// return the error.\n\t\t\t\tclearEvents()\n\n\t\t\t\tvar dialer DialerFunc = func(context.Context, string, string) (net.Conn, error) {\n\t\t\t\t\treturn &testNetConn{writeerr: errors.New(\"write error\")}, nil\n\t\t\t\t}\n\n\t\t\t\tcfg := getConfig()\n\t\t\t\tconnOpts := []ConnectionOption{\n\t\t\t\t\tWithDialer(func(Dialer) Dialer { return dialer }),\n\t\t\t\t\tWithHandshaker(func(Handshaker) Handshaker {\n\t\t\t\t\t\treturn operation.NewHello()\n\t\t\t\t\t}),\n\t\t\t\t}\n\t\t\t\tpool := createTestPool(t, cfg, connOpts...)\n\t\t\t\tdefer pool.close(context.Background())\n\n\t\t\t\t_, err := pool.checkOut(context.Background())\n\t\t\t\tassert.NotNil(t, err, \"expected checkOut() error, got nil\")\n\n\t\t\t\tassertConnectionCounts(t, pool, 1, 1)\n\t\t\t})\n\t\t\tt.Run(\"pool is empty\", func(t *testing.T) {\n\t\t\t\t// If a checkOut() has to create a new connection and that connection encounters an\n\t\t\t\t// error while connecting, checkOut() should return that error and publish an event.\n\t\t\t\tclearEvents()\n\n\t\t\t\tvar dialer DialerFunc = func(context.Context, string, string) (net.Conn, error) {\n\t\t\t\t\treturn &testNetConn{writeerr: errors.New(\"write error\")}, nil\n\t\t\t\t}\n\n\t\t\t\tconnOpts := []ConnectionOption{\n\t\t\t\t\tWithDialer(func(Dialer) Dialer { return dialer }),\n\t\t\t\t\tWithHandshaker(func(Handshaker) Handshaker {\n\t\t\t\t\t\treturn operation.NewHello()\n\t\t\t\t\t}),\n\t\t\t\t}\n\t\t\t\tpool := createTestPool(t, getConfig(), connOpts...)\n\t\t\t\tdefer pool.close(context.Background())\n\n\t\t\t\t_, err := pool.checkOut(context.Background())\n\t\t\t\tassert.NotNil(t, err, \"expected checkOut() error, got nil\")\n\t\t\t\tassertConnectionCounts(t, pool, 1, 1)\n\t\t\t})\n\t\t})\n\t\tt.Run(\"checkIn\", func(t *testing.T) {\n\t\t\tt.Run(\"errored connection\", func(t *testing.T) {\n\t\t\t\t// If the connection being returned to the pool encountered a network error, it should be removed from\n\t\t\t\t// the pool and an event should be published.\n\t\t\t\tclearEvents()\n\n\t\t\t\tvar dialer DialerFunc = func(context.Context, string, string) (net.Conn, error) {\n\t\t\t\t\treturn &testNetConn{writeerr: errors.New(\"write error\")}, nil\n\t\t\t\t}\n\n\t\t\t\t// We don't use the WithHandshaker option so the connection won't error during handshaking.\n\t\t\t\tconnOpts := []ConnectionOption{\n\t\t\t\t\tWithDialer(func(Dialer) Dialer { return dialer }),\n\t\t\t\t}\n\t\t\t\tpool := createTestPool(t, getConfig(), connOpts...)\n\t\t\t\tdefer pool.close(context.Background())\n\n\t\t\t\tconn, err := pool.checkOut(context.Background())\n\t\t\t\tassert.Nil(t, err, \"checkOut() error: %v\", err)\n\n\t\t\t\t// Force a network error by writing to the connection.\n\t\t\t\terr = conn.writeWireMessage(context.Background(), nil)\n\t\t\t\tassert.NotNil(t, err, \"expected writeWireMessage error, got nil\")\n\n\t\t\t\terr = pool.checkIn(conn)\n\t\t\t\tassert.Nil(t, err, \"checkIn() error: %v\", err)\n\n\t\t\t\tassertConnectionCounts(t, pool, 1, 1)\n\t\t\t\tevt := <-closed\n\t\t\t\tassert.Equal(t, event.ReasonError, evt.Reason, \"expected reason %q, got %q\",\n\t\t\t\t\tevent.ReasonError, evt.Reason)\n\t\t\t})\n\t\t})\n\t\tt.Run(\"close\", func(t *testing.T) {\n\t\t\tt.Run(\"connections returned gracefully\", func(t *testing.T) {\n\t\t\t\t// If all connections are in the pool when close is called, they should be closed gracefully and\n\t\t\t\t// events should be published.\n\t\t\t\tclearEvents()\n\n\t\t\t\tnumConns := 5\n\t\t\t\tvar dialer DialerFunc = func(context.Context, string, string) (net.Conn, error) {\n\t\t\t\t\treturn &testNetConn{}, nil\n\t\t\t\t}\n\t\t\t\tpool := createTestPool(t, getConfig(), WithDialer(func(Dialer) Dialer { return dialer }))\n\t\t\t\tdefer pool.close(context.Background())\n\n\t\t\t\tconns := checkoutConnections(t, pool, numConns)\n\t\t\t\tassertConnectionCounts(t, pool, numConns, 0)\n\n\t\t\t\t// Return all connections to the pool and assert that none were closed by checkIn().\n\t\t\t\tfor i, c := range conns {\n\t\t\t\t\terr := pool.checkIn(c)\n\t\t\t\t\tassert.Nil(t, err, \"checkIn() error at index %d: %v\", i, err)\n\t\t\t\t}\n\t\t\t\tassertConnectionCounts(t, pool, numConns, 0)\n\n\t\t\t\t// Close the pool and assert that a closed event is published for each connection.\n\t\t\t\tpool.close(context.Background())\n\t\t\t\tassertConnectionCounts(t, pool, numConns, numConns)\n\n\t\t\t\tfor len(closed) > 0 {\n\t\t\t\t\tevt := <-closed\n\t\t\t\t\tassert.Equal(t, event.ReasonPoolClosed, evt.Reason, \"expected reason %q, got %q\",\n\t\t\t\t\t\tevent.ReasonPoolClosed, evt.Reason)\n\t\t\t\t}\n\t\t\t})\n\t\t\tt.Run(\"connections closed forcefully\", func(t *testing.T) {\n\t\t\t\t// If some connections are still checked out when close is called, they should be closed\n\t\t\t\t// forcefully and events should be published for them.\n\t\t\t\tclearEvents()\n\n\t\t\t\tnumConns := 5\n\t\t\t\tvar dialer DialerFunc = func(context.Context, string, string) (net.Conn, error) {\n\t\t\t\t\treturn &testNetConn{}, nil\n\t\t\t\t}\n\t\t\t\tpool := createTestPool(t, getConfig(), WithDialer(func(Dialer) Dialer { return dialer }))\n\n\t\t\t\tconns := checkoutConnections(t, pool, numConns)\n\t\t\t\tassertConnectionCounts(t, pool, numConns, 0)\n\n\t\t\t\t// Only return 2 of the connection.\n\t\t\t\tfor i := 0; i < 2; i++ {\n\t\t\t\t\terr := pool.checkIn(conns[i])\n\t\t\t\t\tassert.Nil(t, err, \"checkIn() error at index %d: %v\", i, err)\n\t\t\t\t}\n\t\t\t\tconns = conns[2:]\n\t\t\t\tassertConnectionCounts(t, pool, numConns, 0)\n\n\t\t\t\t// Close and assert that events are published for all connections.\n\t\t\t\tpool.close(context.Background())\n\t\t\t\tassertConnectionCounts(t, pool, numConns, numConns)\n\n\t\t\t\t// Return the remaining connections and assert that the closed event count does not increase because\n\t\t\t\t// these connections have already been closed.\n\t\t\t\tfor i, c := range conns {\n\t\t\t\t\terr := pool.checkIn(c)\n\t\t\t\t\tassert.Nil(t, err, \"checkIn() error at index %d: %v\", i, err)\n\t\t\t\t}\n\t\t\t\tassertConnectionCounts(t, pool, numConns, numConns)\n\n\t\t\t\t// Ensure all closed events have the correct reason.\n\t\t\t\tfor len(closed) > 0 {\n\t\t\t\t\tevt := <-closed\n\t\t\t\t\tassert.Equal(t, event.ReasonPoolClosed, evt.Reason, \"expected reason %q, got %q\",\n\t\t\t\t\t\tevent.ReasonPoolClosed, evt.Reason)\n\t\t\t\t}\n\t\t\t})\n\t\t})\n\t})\n}\n\nfunc createTestPool(t *testing.T, cfg poolConfig, opts ...ConnectionOption) *pool {\n\tt.Helper()\n\n\tpool := newPool(cfg, opts...)\n\terr := pool.ready()\n\tassert.Nil(t, err, \"connect error: %v\", err)\n\treturn pool\n}\n\nfunc checkoutConnections(t *testing.T, p *pool, numConns int) []*connection {\n\tconns := make([]*connection, 0, numConns)\n\n\tfor i := 0; i < numConns; i++ {\n\t\tconn, err := p.checkOut(context.Background())\n\t\tassert.Nil(t, err, \"checkOut() error at index %d: %v\", i, err)\n\t\tconns = append(conns, conn)\n\t}\n\n\treturn conns\n}\n"
  },
  {
    "path": "x/mongo/driver/topology/connection.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage topology\n\nimport (\n\t\"context\"\n\t\"crypto/tls\"\n\t\"crypto/x509\"\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"strings\"\n\t\"sync\"\n\t\"sync/atomic\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/driverutil\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/address\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/description\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/mnet\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/ocsp\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/wiremessage\"\n)\n\n// Connection state constants.\nconst (\n\tconnDisconnected int64 = iota\n\tconnConnected\n\tconnInitialized\n)\n\nvar globalConnectionID uint64 = 1\n\nvar (\n\tdefaultMaxMessageSize        uint32 = 48000000\n\terrResponseTooLarge                 = errors.New(\"length of read message too large\")\n\terrLoadBalancedStateMismatch        = errors.New(\"driver attempted to initialize in load balancing mode, but the server does not support this mode\")\n)\n\nfunc nextConnectionID() uint64 { return atomic.AddUint64(&globalConnectionID, 1) }\n\nfunc wrapConnectionError(connErr ConnectionError) error {\n\tvar dnsErr *net.DNSError\n\tif errors.As(connErr.Wrapped, &dnsErr) {\n\t\treturn connErr\n\t}\n\t// x509 errors are returned as values by the crypto/tls package\n\tvar hostErr x509.HostnameError\n\tif errors.As(connErr.Wrapped, &hostErr) {\n\t\treturn connErr\n\t}\n\tvar certErr x509.CertificateInvalidError\n\tif errors.As(connErr.Wrapped, &certErr) {\n\t\treturn connErr\n\t}\n\tvar unknownCAErr x509.UnknownAuthorityError\n\tif errors.As(connErr.Wrapped, &unknownCAErr) {\n\t\treturn connErr\n\t}\n\treturn driver.Error{\n\t\tLabels:  []string{driver.ErrSystemOverloadedError, driver.ErrRetryableError, driver.NetworkError},\n\t\tWrapped: connErr,\n\t}\n}\n\ntype connection struct {\n\t// state must be accessed using the atomic package and should be at the beginning of the struct.\n\t// - atomic bug: https://pkg.go.dev/sync/atomic#pkg-note-BUG\n\t// - suggested layout: https://go101.org/article/memory-layout.html\n\tstate int64\n\n\tid                   string\n\tnc                   net.Conn // When nil, the connection is closed.\n\taddr                 address.Address\n\tidleTimeout          time.Duration\n\tidleStart            atomic.Value // Stores a time.Time\n\tdesc                 description.Server\n\thelloRTT             time.Duration\n\tcompressor           wiremessage.CompressorID\n\tzliblevel            int\n\tzstdLevel            int\n\tconnectDone          chan struct{}\n\tconfig               *connectionConfig\n\tconnectContextMade   chan struct{}\n\tcanStream            bool\n\tcurrentlyStreaming   bool\n\tcancellationListener contextListener\n\tconnectListener      contextListener // Cancels blocking ops during connect\n\tserverConnectionID   *int64          // the server's ID for this client's connection\n\tprevCanceled         atomic.Value\n\n\t// pool related fields\n\tpool *pool\n\n\tdriverConnectionID int64\n\tgeneration         uint64\n\t// oidcTokenGenID is the monotonic generation ID for OIDC tokens, used to invalidate\n\t// accessTokens in the OIDC authenticator cache.\n\toidcTokenGenID uint64\n\n\t// awaitRemainingBytes indicates the size of server response that was not completely\n\t// read before returning the connection to the pool.\n\tawaitRemainingBytes *int32\n}\n\n// newConnection handles the creation of a connection. It does not connect the connection.\nfunc newConnection(addr address.Address, opts ...ConnectionOption) *connection {\n\tcfg := newConnectionConfig(opts...)\n\n\tid := fmt.Sprintf(\"%s[-%d]\", addr, nextConnectionID())\n\n\tc := &connection{\n\t\tid:                   id,\n\t\taddr:                 addr,\n\t\tidleTimeout:          cfg.idleTimeout,\n\t\tconnectDone:          make(chan struct{}),\n\t\tconfig:               cfg,\n\t\tconnectContextMade:   make(chan struct{}),\n\t\tcancellationListener: newContextDoneListener(),\n\t\tconnectListener:      newNonBlockingContextDoneListener(),\n\t}\n\t// Connections to non-load balanced deployments should eagerly set the generation numbers so errors encountered\n\t// at any point during connection establishment can be processed without the connection being considered stale.\n\tif !c.config.loadBalanced {\n\t\tc.setGenerationNumber()\n\t}\n\tatomic.StoreInt64(&c.state, connInitialized)\n\n\treturn c\n}\n\n// setGenerationNumber sets the connection's generation number if a callback has been provided to do so in connection\n// configuration.\nfunc (c *connection) setGenerationNumber() {\n\tif c.config.getGenerationFn != nil {\n\t\tc.generation = c.config.getGenerationFn(c.desc.ServiceID)\n\t}\n}\n\n// hasGenerationNumber returns true if the connection has set its generation number. If so, this indicates that the\n// generationNumberFn provided via the connection options has been called exactly once.\nfunc (c *connection) hasGenerationNumber() bool {\n\tif !c.config.loadBalanced {\n\t\t// The generation is known for all non-LB clusters once the connection object has been created.\n\t\treturn true\n\t}\n\n\t// For LB clusters, we set the generation after the initial handshake, so we know it's set if the connection\n\t// description has been updated to reflect that it's behind an LB.\n\treturn driverutil.IsServerLoadBalanced(c.desc)\n}\n\nfunc configureTLS(ctx context.Context,\n\ttlsConnSource tlsConnectionSource,\n\tnc net.Conn,\n\taddr address.Address,\n\tconfig *tls.Config,\n\tocspOpts *ocsp.VerifyOptions,\n) (net.Conn, error) {\n\t// Ensure config.ServerName is always set for SNI.\n\tif config.ServerName == \"\" {\n\t\thostname := addr.String()\n\t\tcolonPos := strings.LastIndex(hostname, \":\")\n\t\tif colonPos == -1 {\n\t\t\tcolonPos = len(hostname)\n\t\t}\n\n\t\thostname = hostname[:colonPos]\n\t\tconfig.ServerName = hostname\n\t}\n\n\tclient := tlsConnSource.Client(nc, config)\n\tif err := clientHandshake(ctx, client); err != nil {\n\t\treturn nil, err\n\t}\n\n\t// Only do OCSP verification if TLS verification is requested.\n\tif !config.InsecureSkipVerify {\n\t\tif ocspErr := ocsp.Verify(ctx, client.ConnectionState(), ocspOpts); ocspErr != nil {\n\t\t\treturn nil, ocspErr\n\t\t}\n\t}\n\treturn client, nil\n}\n\n// connect handles the I/O for a connection. It will dial, configure TLS, and perform initialization\n// handshakes. All errors returned by connect are considered \"before the handshake completes\" and\n// must be handled by calling the appropriate SDAM handshake error handler.\nfunc (c *connection) connect(ctx context.Context) (err error) {\n\tif !atomic.CompareAndSwapInt64(&c.state, connInitialized, connConnected) {\n\t\treturn nil\n\t}\n\n\tdefer c.closeConnectContext()\n\tdefer close(c.connectDone)\n\n\t// If connect returns an error, set the connection status as disconnected and close the\n\t// underlying net.Conn if it was created.\n\tdefer func() {\n\t\tif err != nil {\n\t\t\tatomic.StoreInt64(&c.state, connDisconnected)\n\n\t\t\tif c.nc != nil {\n\t\t\t\t_ = c.nc.Close()\n\t\t\t}\n\t\t}\n\t}()\n\n\t// Create separate contexts for dialing a connection and doing the MongoDB/auth handshakes.\n\t//\n\t// handshakeCtx is simply a cancellable version of ctx because there's no default timeout that needs to be applied\n\t// to the full handshake. The cancellation allows consumers to bail out early when dialing a connection if it's no\n\t// longer required. This is done in lock because it accesses the shared cancelConnectContext field.\n\t//\n\t// dialCtx is equal to handshakeCtx if connectTimeoutMS=0. Otherwise, it is derived from handshakeCtx so the\n\t// cancellation still applies but with an added timeout to ensure the connectTimeoutMS option is applied to socket\n\t// establishment and the TLS handshake as a whole. This is created outside of the connectContextMutex lock to avoid\n\t// holding the lock longer than necessary.\n\tctx, cancel := context.WithCancel(ctx)\n\tdefer cancel()\n\n\tgo func() {\n\t\tdefer cancel()\n\n\t\tc.connectListener.Listen(ctx, func() {})\n\t}()\n\n\t// Assign the result of DialContext to a temporary net.Conn to ensure that c.nc is not set in an error case.\n\ttempNc, err := c.config.dialer.DialContext(ctx, c.addr.Network(), c.addr.String())\n\tif err != nil {\n\t\tconnErr := ConnectionError{Wrapped: err, init: true, message: fmt.Sprintf(\"failed to connect to %s\", c.addr)}\n\t\treturn wrapConnectionError(connErr)\n\t}\n\tc.nc = tempNc\n\n\tif c.config.tlsConfig != nil {\n\t\ttlsConfig := c.config.tlsConfig.Clone()\n\n\t\t// store the result of configureTLS in a separate variable than c.nc to avoid overwriting c.nc with nil in\n\t\t// error cases.\n\t\tocspOpts := &ocsp.VerifyOptions{\n\t\t\tCache:                   c.config.ocspCache,\n\t\t\tDisableEndpointChecking: c.config.disableOCSPEndpointCheck,\n\t\t\tHTTPClient:              c.config.httpClient,\n\t\t}\n\t\ttlsNc, err := configureTLS(ctx, c.config.tlsConnectionSource, c.nc, c.addr, tlsConfig, ocspOpts)\n\t\tif err != nil {\n\t\t\tconnErr := ConnectionError{Wrapped: err, init: true, message: fmt.Sprintf(\"failed to configure TLS for %s\", c.addr)}\n\t\t\treturn wrapConnectionError(connErr)\n\t\t}\n\t\tc.nc = tlsNc\n\t}\n\n\t// running hello and authentication is handled by a handshaker on the configuration instance.\n\thandshaker := c.config.handshaker\n\tif handshaker == nil {\n\t\treturn nil\n\t}\n\n\tvar handshakeInfo driver.HandshakeInformation\n\thandshakeStartTime := time.Now()\n\n\ticonn := initConnection{c}\n\thandshakeConn := mnet.NewConnection(iconn)\n\n\thandshakeInfo, err = handshaker.GetHandshakeInformation(ctx, c.addr, handshakeConn)\n\tif err != nil {\n\t\tconnErr := ConnectionError{Wrapped: err, init: true}\n\t\treturn wrapConnectionError(connErr)\n\t}\n\n\t// We only need to retain the Description field as the connection's description. The authentication-related\n\t// fields in handshakeInfo are tracked by the handshaker if necessary.\n\tc.desc = handshakeInfo.Description\n\tc.serverConnectionID = handshakeInfo.ServerConnectionID\n\tc.helloRTT = time.Since(handshakeStartTime)\n\n\t// If the application has indicated that the cluster is load balanced, ensure the server has included serviceId\n\t// in its handshake response to signal that it knows it's behind an LB as well.\n\tif c.config.loadBalanced && c.desc.ServiceID == nil {\n\t\treturn ConnectionError{Wrapped: errLoadBalancedStateMismatch, init: true}\n\t}\n\n\t// For load-balanced connections, the generation number depends on the service ID, which isn't known until the\n\t// initial MongoDB handshake is done. To account for this, we don't attempt to set the connection's generation\n\t// number unless GetHandshakeInformation succeeds.\n\tif c.config.loadBalanced {\n\t\tc.setGenerationNumber()\n\t}\n\n\t// If we successfully finished the first part of the handshake and verified LB state, continue with the rest of\n\t// the handshake. Authentication errors are not connection establishment errors and do not get backpressure labels.\n\tif err = handshaker.FinishHandshake(ctx, handshakeConn); err != nil {\n\t\treturn ConnectionError{Wrapped: err, init: true}\n\t}\n\n\tif len(c.desc.Compression) > 0 {\n\tclientMethodLoop:\n\t\tfor _, method := range c.config.compressors {\n\t\t\tfor _, serverMethod := range c.desc.Compression {\n\t\t\t\tif method != serverMethod {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\tswitch strings.ToLower(method) {\n\t\t\t\tcase \"snappy\":\n\t\t\t\t\tc.compressor = wiremessage.CompressorSnappy\n\t\t\t\tcase \"zlib\":\n\t\t\t\t\tc.compressor = wiremessage.CompressorZLib\n\t\t\t\t\tc.zliblevel = wiremessage.DefaultZlibLevel\n\t\t\t\t\tif c.config.zlibLevel != nil {\n\t\t\t\t\t\tc.zliblevel = *c.config.zlibLevel\n\t\t\t\t\t}\n\t\t\t\tcase \"zstd\":\n\t\t\t\t\tc.compressor = wiremessage.CompressorZstd\n\t\t\t\t\tc.zstdLevel = wiremessage.DefaultZstdLevel\n\t\t\t\t\tif c.config.zstdLevel != nil {\n\t\t\t\t\t\tc.zstdLevel = *c.config.zstdLevel\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak clientMethodLoop\n\t\t\t}\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (c *connection) wait() {\n\tif c.connectDone != nil {\n\t\t<-c.connectDone\n\t}\n}\n\nfunc (c *connection) closeConnectContext() {\n\tif c.connectListener != nil {\n\t\tc.connectListener.StopListening()\n\t}\n}\n\nfunc (c *connection) cancellationListenerCallback() {\n\t_ = c.close()\n}\n\nfunc transformNetworkError(ctx context.Context, originalError error, contextDeadlineUsed bool) error {\n\tif originalError == nil {\n\t\treturn nil\n\t}\n\n\t// If there was an error and the context was cancelled, we assume it happened due to the cancellation.\n\tif errors.Is(ctx.Err(), context.Canceled) {\n\t\treturn ctx.Err()\n\t}\n\n\t// If there was a timeout error and the context deadline was used, we convert the error into\n\t// context.DeadlineExceeded.\n\tif !contextDeadlineUsed {\n\t\treturn originalError\n\t}\n\tif netErr, ok := originalError.(net.Error); ok && netErr.Timeout() {\n\t\treturn fmt.Errorf(\"%w: %s: %s\",\n\t\t\tcontext.DeadlineExceeded,\n\t\t\t\"client timed out waiting for server response\",\n\t\t\toriginalError.Error())\n\t}\n\n\treturn originalError\n}\n\nfunc (c *connection) writeWireMessage(ctx context.Context, wm []byte) error {\n\tvar err error\n\tif atomic.LoadInt64(&c.state) != connConnected {\n\t\treturn ConnectionError{\n\t\t\tConnectionID: c.id,\n\t\t\tmessage:      \"connection is closed\",\n\t\t}\n\t}\n\n\tdeadline, contextDeadlineUsed := ctx.Deadline()\n\tif err := c.nc.SetWriteDeadline(deadline); err != nil {\n\t\treturn ConnectionError{ConnectionID: c.id, Wrapped: err, message: \"failed to set write deadline\"}\n\t}\n\n\terr = c.write(ctx, wm)\n\tif err != nil {\n\t\t_ = c.close()\n\n\t\treturn ConnectionError{\n\t\t\tConnectionID: c.id,\n\t\t\tWrapped:      transformNetworkError(ctx, err, contextDeadlineUsed),\n\t\t\tmessage:      \"unable to write wire message to network\",\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc (c *connection) write(ctx context.Context, wm []byte) (err error) {\n\tgo c.cancellationListener.Listen(ctx, c.cancellationListenerCallback)\n\tdefer func() {\n\t\t// There is a race condition between Write and StopListening. If the context is cancelled after c.nc.Write\n\t\t// succeeds, the cancellation listener could fire and close the connection. In this case, the connection has\n\t\t// been invalidated but the error is nil. To account for this, overwrite the error to context.Cancelled if\n\t\t// the abortedForCancellation flag was set.\n\n\t\tif aborted := c.cancellationListener.StopListening(); aborted && err == nil {\n\t\t\terr = context.Canceled\n\t\t}\n\t}()\n\n\t_, err = c.nc.Write(wm)\n\treturn err\n}\n\n// readWireMessage reads a wiremessage from the connection. The dst parameter will be overwritten.\nfunc (c *connection) readWireMessage(ctx context.Context) ([]byte, error) {\n\tif atomic.LoadInt64(&c.state) != connConnected {\n\t\treturn nil, ConnectionError{\n\t\t\tConnectionID: c.id,\n\t\t\tmessage:      \"connection is closed\",\n\t\t}\n\t}\n\n\tdeadline, contextDeadlineUsed := ctx.Deadline()\n\tif err := c.nc.SetReadDeadline(deadline); err != nil {\n\t\treturn nil, ConnectionError{ConnectionID: c.id, Wrapped: err, message: \"failed to set read deadline\"}\n\t}\n\n\tdst, errMsg, err := c.read(ctx)\n\tif err != nil {\n\t\tif c.awaitRemainingBytes == nil {\n\t\t\t// If the connection was not marked as awaiting response, close the\n\t\t\t// connection because we don't know what the connection state is.\n\t\t\t_ = c.close()\n\t\t}\n\n\t\tmessage := errMsg\n\t\treturn nil, ConnectionError{\n\t\t\tConnectionID: c.id,\n\t\t\tWrapped:      transformNetworkError(ctx, err, contextDeadlineUsed),\n\t\t\tmessage:      message,\n\t\t}\n\t}\n\n\treturn dst, nil\n}\n\nfunc (c *connection) parseWmSizeBytes(wmSizeBytes [4]byte) (int32, error) {\n\t// read the length as an int32\n\tsize := int32(binary.LittleEndian.Uint32(wmSizeBytes[:]))\n\n\tif size < 4 {\n\t\treturn 0, fmt.Errorf(\"malformed message length: %d\", size)\n\t}\n\t// In the case of a hello response where MaxMessageSize has not yet been set, use the hard-coded\n\t// defaultMaxMessageSize instead.\n\tmaxMessageSize := c.desc.MaxMessageSize\n\tif maxMessageSize == 0 {\n\t\tmaxMessageSize = defaultMaxMessageSize\n\t}\n\tif uint32(size) > maxMessageSize {\n\t\treturn 0, errResponseTooLarge\n\t}\n\n\treturn size, nil\n}\n\nfunc (c *connection) read(ctx context.Context) (bytesRead []byte, errMsg string, err error) {\n\tgo c.cancellationListener.Listen(ctx, c.cancellationListenerCallback)\n\tdefer func() {\n\t\t// If the context is cancelled after we finish reading the server response, the cancellation listener could fire\n\t\t// even though the socket reads succeed. To account for this, we overwrite err to be context.Canceled if the\n\t\t// abortedForCancellation flag is set.\n\n\t\tif aborted := c.cancellationListener.StopListening(); aborted && err == nil {\n\t\t\terrMsg = \"unable to read server response\"\n\t\t\terr = context.Canceled\n\t\t}\n\t}()\n\n\tisCSOTTimeout := func(err error) bool {\n\t\t// If the error was a timeout error, instead of closing the\n\t\t// connection mark it as awaiting response so the pool can read the\n\t\t// response before making it available to other operations.\n\t\tnerr := net.Error(nil)\n\t\treturn errors.As(err, &nerr) && nerr.Timeout()\n\t}\n\n\t// We use an array here because it only costs 4 bytes on the stack and means we'll only need to\n\t// reslice dst once instead of twice.\n\tvar sizeBuf [4]byte\n\n\t// We do a ReadFull into an array here instead of doing an opportunistic ReadAtLeast into dst\n\t// because there might be more than one wire message waiting to be read, for example when\n\t// reading messages from an exhaust cursor.\n\tn, err := io.ReadFull(c.nc, sizeBuf[:])\n\tif err != nil {\n\t\tif l := int32(n); l == 0 && isCSOTTimeout(err) {\n\t\t\tc.awaitRemainingBytes = &l\n\t\t}\n\t\treturn nil, \"incomplete read of message header\", err\n\t}\n\tsize, err := c.parseWmSizeBytes(sizeBuf)\n\tif err != nil {\n\t\treturn nil, err.Error(), err\n\t}\n\n\tdst := make([]byte, size)\n\tcopy(dst, sizeBuf[:])\n\n\tn, err = io.ReadFull(c.nc, dst[4:])\n\tif err != nil {\n\t\tremainingBytes := size - 4 - int32(n)\n\t\tif remainingBytes > 0 && isCSOTTimeout(err) {\n\t\t\tc.awaitRemainingBytes = &remainingBytes\n\t\t}\n\t\treturn dst, \"incomplete read of full message\", err\n\t}\n\n\treturn dst, \"\", nil\n}\n\nfunc (c *connection) close() error {\n\t// Stop any blocking operations occurring in connect(), but await closing the\n\t// connections directly before closing the connection context. This ensures\n\t// that closing a connection will manifest as an io.EOF error, avoiding\n\t// non-deterministic connection closure errors.\n\tdefer c.closeConnectContext()\n\n\t// Overwrite the connection state as the first step so only the first close call will execute.\n\tif !atomic.CompareAndSwapInt64(&c.state, connConnected, connDisconnected) {\n\t\treturn nil\n\t}\n\n\tvar err error\n\tif c.nc != nil {\n\t\terr = c.nc.Close()\n\t}\n\n\treturn err\n}\n\n// closed returns true if the connection has been closed by the driver.\nfunc (c *connection) closed() bool {\n\treturn atomic.LoadInt64(&c.state) == connDisconnected\n}\n\nfunc (c *connection) idleTimeoutExpired() bool {\n\tif c.idleTimeout == 0 {\n\t\treturn false\n\t}\n\n\tidleStart, ok := c.idleStart.Load().(time.Time)\n\treturn ok && idleStart.Add(c.idleTimeout).Before(time.Now())\n}\n\nfunc (c *connection) bumpIdleStart() {\n\tif c.idleTimeout > 0 {\n\t\tc.idleStart.Store(time.Now())\n\t}\n}\n\nfunc (c *connection) setCanStream(canStream bool) {\n\tc.canStream = canStream\n}\n\nfunc (c *connection) setStreaming(streaming bool) {\n\tc.currentlyStreaming = streaming\n}\n\nfunc (c *connection) getCurrentlyStreaming() bool {\n\treturn c.currentlyStreaming\n}\n\nfunc (c *connection) previousCanceled() bool {\n\tif val := c.prevCanceled.Load(); val != nil {\n\t\treturn val.(bool)\n\t}\n\n\treturn false\n}\n\nfunc (c *connection) ID() string {\n\treturn c.id\n}\n\nfunc (c *connection) ServerConnectionID() *int64 {\n\treturn c.serverConnectionID\n}\n\n// DriverConnectionID returns the driver connection ID.\nfunc (c *connection) DriverConnectionID() int64 {\n\treturn c.driverConnectionID\n}\n\nfunc (c *connection) OIDCTokenGenID() uint64 {\n\treturn c.oidcTokenGenID\n}\n\nfunc (c *connection) SetOIDCTokenGenID(genID uint64) {\n\tc.oidcTokenGenID = genID\n}\n\n// initConnection is an adapter used during connection initialization. It has the minimum\n// functionality necessary to implement the driver.Connection interface, which is required to pass a\n// *connection to a Handshaker.\ntype initConnection struct{ *connection }\n\nvar (\n\t_ mnet.ReadWriteCloser = initConnection{}\n\t_ mnet.Describer       = initConnection{}\n\t_ mnet.Streamer        = initConnection{}\n)\n\nfunc (c initConnection) Description() description.Server {\n\tif c.connection == nil {\n\t\treturn description.Server{}\n\t}\n\treturn c.desc\n}\nfunc (c initConnection) Close() error             { return nil }\nfunc (c initConnection) ID() string               { return c.id }\nfunc (c initConnection) Address() address.Address { return c.addr }\nfunc (c initConnection) Stale() bool              { return false }\nfunc (c initConnection) LocalAddress() address.Address {\n\tif c.connection == nil || c.nc == nil {\n\t\treturn address.Address(\"0.0.0.0\")\n\t}\n\treturn address.Address(c.nc.LocalAddr().String())\n}\n\nfunc (c initConnection) Write(ctx context.Context, wm []byte) error {\n\treturn c.writeWireMessage(ctx, wm)\n}\n\nfunc (c initConnection) Read(ctx context.Context) ([]byte, error) {\n\treturn c.readWireMessage(ctx)\n}\n\nfunc (c initConnection) SetStreaming(streaming bool) {\n\tc.setStreaming(streaming)\n}\n\nfunc (c initConnection) CurrentlyStreaming() bool {\n\treturn c.getCurrentlyStreaming()\n}\n\nfunc (c initConnection) SupportsStreaming() bool {\n\treturn c.canStream\n}\n\n// Connection implements the driver.Connection interface to allow reading and writing wire\n// messages and the driver.Expirable interface to allow expiring. It wraps an underlying\n// topology.connection to make it more goroutine-safe and nil-safe.\ntype Connection struct {\n\tconnection    *connection\n\trefCount      int\n\tcleanupPoolFn func()\n\n\toidcTokenGenID uint64\n\n\t// cleanupServerFn resets the server state when a connection is returned to the connection pool\n\t// via Close() or expired via Expire().\n\tcleanupServerFn func()\n\n\tmu sync.RWMutex\n}\n\nvar (\n\t_ mnet.ReadWriteCloser = (*Connection)(nil)\n\t_ mnet.Describer       = (*Connection)(nil)\n\t_ mnet.Compressor      = (*Connection)(nil)\n\t_ mnet.Pinner          = (*Connection)(nil)\n\t_ driver.Expirable     = (*Connection)(nil)\n)\n\n// Write handles writing a wire message to the underlying connection.\nfunc (c *Connection) Write(ctx context.Context, wm []byte) error {\n\tc.mu.RLock()\n\tdefer c.mu.RUnlock()\n\tif c.connection == nil {\n\t\treturn ErrConnectionClosed\n\t}\n\treturn c.connection.writeWireMessage(ctx, wm)\n}\n\n// Read handles reading a wire message from the underlying connection. The dst\n// parameter will be overwritten with the new wire message.\nfunc (c *Connection) Read(ctx context.Context) ([]byte, error) {\n\tc.mu.RLock()\n\tdefer c.mu.RUnlock()\n\tif c.connection == nil {\n\t\treturn nil, ErrConnectionClosed\n\t}\n\treturn c.connection.readWireMessage(ctx)\n}\n\n// CompressWireMessage handles compressing the provided wire message using the underlying\n// connection's compressor. The dst parameter will be overwritten with the new wire message. If\n// there is no compressor set on the underlying connection, then no compression will be performed.\nfunc (c *Connection) CompressWireMessage(src, dst []byte) ([]byte, error) {\n\tc.mu.RLock()\n\tdefer c.mu.RUnlock()\n\tif c.connection == nil {\n\t\treturn dst, ErrConnectionClosed\n\t}\n\tif c.connection.compressor == wiremessage.CompressorNoOp {\n\t\treturn append(dst, src...), nil\n\t}\n\t_, reqid, respto, origcode, rem, ok := wiremessage.ReadHeader(src)\n\tif !ok {\n\t\treturn dst, errors.New(\"wiremessage is too short to compress, less than 16 bytes\")\n\t}\n\tidx, dst := wiremessage.AppendHeaderStart(dst, reqid, respto, wiremessage.OpCompressed)\n\tdst = wiremessage.AppendCompressedOriginalOpCode(dst, origcode)\n\tdst = wiremessage.AppendCompressedUncompressedSize(dst, int32(len(rem)))\n\tdst = wiremessage.AppendCompressedCompressorID(dst, c.connection.compressor)\n\topts := driver.CompressionOpts{\n\t\tCompressor: c.connection.compressor,\n\t\tZlibLevel:  c.connection.zliblevel,\n\t\tZstdLevel:  c.connection.zstdLevel,\n\t}\n\tcompressed, err := driver.CompressPayload(rem, opts)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdst = wiremessage.AppendCompressedCompressedMessage(dst, compressed)\n\treturn bsoncore.UpdateLength(dst, idx, int32(len(dst[idx:]))), nil\n}\n\n// Description returns the server description of the server this connection is connected to.\nfunc (c *Connection) Description() description.Server {\n\tc.mu.RLock()\n\tdefer c.mu.RUnlock()\n\tif c.connection == nil {\n\t\treturn description.Server{}\n\t}\n\treturn c.connection.desc\n}\n\n// Close returns this connection to the connection pool. This method may not closeConnection the underlying\n// socket.\nfunc (c *Connection) Close() error {\n\tc.mu.Lock()\n\tdefer c.mu.Unlock()\n\tif c.connection == nil || c.refCount > 0 {\n\t\treturn nil\n\t}\n\n\treturn c.cleanupReferences()\n}\n\n// Expire closes this connection and will closeConnection the underlying socket.\nfunc (c *Connection) Expire() error {\n\tc.mu.Lock()\n\tdefer c.mu.Unlock()\n\tif c.connection == nil {\n\t\treturn nil\n\t}\n\n\t_ = c.connection.close()\n\treturn c.cleanupReferences()\n}\n\nfunc (c *Connection) cleanupReferences() error {\n\terr := c.connection.pool.checkIn(c.connection)\n\tif c.cleanupPoolFn != nil {\n\t\tc.cleanupPoolFn()\n\t\tc.cleanupPoolFn = nil\n\t}\n\tif c.cleanupServerFn != nil {\n\t\tc.cleanupServerFn()\n\t\tc.cleanupServerFn = nil\n\t}\n\tc.connection = nil\n\treturn err\n}\n\n// Alive returns if the connection is still alive.\nfunc (c *Connection) Alive() bool {\n\treturn c.connection != nil\n}\n\n// ID returns the ID of this connection.\nfunc (c *Connection) ID() string {\n\tc.mu.RLock()\n\tdefer c.mu.RUnlock()\n\tif c.connection == nil {\n\t\treturn \"<closed>\"\n\t}\n\treturn c.connection.id\n}\n\n// ServerConnectionID returns the server connection ID of this connection.\nfunc (c *Connection) ServerConnectionID() *int64 {\n\tif c.connection == nil {\n\t\treturn nil\n\t}\n\treturn c.connection.serverConnectionID\n}\n\n// Stale returns if the connection is stale.\nfunc (c *Connection) Stale() bool {\n\tc.mu.RLock()\n\tdefer c.mu.RUnlock()\n\treturn c.connection.pool.stale(c.connection)\n}\n\n// Address returns the address of this connection.\nfunc (c *Connection) Address() address.Address {\n\tc.mu.RLock()\n\tdefer c.mu.RUnlock()\n\tif c.connection == nil {\n\t\treturn address.Address(\"0.0.0.0\")\n\t}\n\treturn c.connection.addr\n}\n\n// LocalAddress returns the local address of the connection\nfunc (c *Connection) LocalAddress() address.Address {\n\tc.mu.RLock()\n\tdefer c.mu.RUnlock()\n\tif c.connection == nil || c.connection.nc == nil {\n\t\treturn address.Address(\"0.0.0.0\")\n\t}\n\treturn address.Address(c.connection.nc.LocalAddr().String())\n}\n\n// PinToCursor updates this connection to reflect that it is pinned to a cursor.\nfunc (c *Connection) PinToCursor() error {\n\treturn c.pin(\"cursor\", c.connection.pool.pinConnectionToCursor, c.connection.pool.unpinConnectionFromCursor)\n}\n\n// PinToTransaction updates this connection to reflect that it is pinned to a transaction.\nfunc (c *Connection) PinToTransaction() error {\n\treturn c.pin(\"transaction\", c.connection.pool.pinConnectionToTransaction, c.connection.pool.unpinConnectionFromTransaction)\n}\n\nfunc (c *Connection) pin(reason string, updatePoolFn, cleanupPoolFn func()) error {\n\tc.mu.Lock()\n\tdefer c.mu.Unlock()\n\tif c.connection == nil {\n\t\treturn fmt.Errorf(\"attempted to pin a connection for a %s, but the connection has already been returned to the pool\", reason)\n\t}\n\n\t// Only use the provided callbacks for the first reference to avoid double-counting pinned connection statistics\n\t// in the pool.\n\tif c.refCount == 0 {\n\t\tupdatePoolFn()\n\t\tc.cleanupPoolFn = cleanupPoolFn\n\t}\n\tc.refCount++\n\treturn nil\n}\n\n// UnpinFromCursor updates this connection to reflect that it is no longer pinned to a cursor.\nfunc (c *Connection) UnpinFromCursor() error {\n\treturn c.unpin(\"cursor\")\n}\n\n// UnpinFromTransaction updates this connection to reflect that it is no longer pinned to a transaction.\nfunc (c *Connection) UnpinFromTransaction() error {\n\treturn c.unpin(\"transaction\")\n}\n\nfunc (c *Connection) unpin(reason string) error {\n\tc.mu.Lock()\n\tdefer c.mu.Unlock()\n\tif c.connection == nil {\n\t\t// We don't error here because the resource could have been forcefully closed via Expire.\n\t\treturn nil\n\t}\n\tif c.refCount == 0 {\n\t\treturn fmt.Errorf(\"attempted to unpin a connection from a %s, but the connection is not pinned by any resources\", reason)\n\t}\n\n\tc.refCount--\n\treturn nil\n}\n\n// DriverConnectionID returns the driver connection ID.\nfunc (c *Connection) DriverConnectionID() int64 {\n\treturn c.connection.DriverConnectionID()\n}\n\n// OIDCTokenGenID returns the OIDC token generation ID.\nfunc (c *Connection) OIDCTokenGenID() uint64 {\n\treturn c.oidcTokenGenID\n}\n\n// SetOIDCTokenGenID sets the OIDC token generation ID.\nfunc (c *Connection) SetOIDCTokenGenID(genID uint64) {\n\tc.oidcTokenGenID = genID\n}\n"
  },
  {
    "path": "x/mongo/driver/topology/connection_errors_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\n//go:build go1.13\n\npackage topology\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"net\"\n\t\"testing\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/require\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/address\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/auth\"\n)\n\nfunc TestConnectionErrors(t *testing.T) {\n\tt.Run(\"errors are wrapped\", func(t *testing.T) {\n\t\tt.Run(\"dial error\", func(t *testing.T) {\n\t\t\tdialError := errors.New(\"foo\")\n\n\t\t\tconn := newConnection(address.Address(\"\"), WithDialer(func(Dialer) Dialer {\n\t\t\t\treturn DialerFunc(func(context.Context, string, string) (net.Conn, error) { return nil, dialError })\n\t\t\t}))\n\n\t\t\terr := conn.connect(context.Background())\n\t\t\tassert.True(t, errors.Is(err, dialError), \"expected error %v, got %v\", dialError, err)\n\t\t})\n\t\tt.Run(\"handshake error\", func(t *testing.T) {\n\t\t\tconn := newConnection(address.Address(\"\"),\n\t\t\t\tWithHandshaker(func(Handshaker) Handshaker {\n\t\t\t\t\treturn auth.Handshaker(nil, &auth.HandshakeOptions{})\n\t\t\t\t}),\n\t\t\t\tWithDialer(func(Dialer) Dialer {\n\t\t\t\t\treturn DialerFunc(func(context.Context, string, string) (net.Conn, error) {\n\t\t\t\t\t\treturn &net.TCPConn{}, nil\n\t\t\t\t\t})\n\t\t\t\t}),\n\t\t\t)\n\t\t\tdefer func() { require.NoError(t, conn.close()) }()\n\n\t\t\tctx, cancel := context.WithCancel(context.Background())\n\t\t\tcancel()\n\t\t\terr := conn.connect(ctx)\n\t\t\tassert.True(t, errors.Is(err, context.Canceled), \"expected error %v, got %v\", context.Canceled, err)\n\t\t})\n\t})\n}\n"
  },
  {
    "path": "x/mongo/driver/topology/connection_legacy.go",
    "content": "// Copyright (C) MongoDB, Inc. 2022-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage topology\n"
  },
  {
    "path": "x/mongo/driver/topology/connection_options.go",
    "content": "// Copyright (C) MongoDB, Inc. 2022-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage topology\n\nimport (\n\t\"context\"\n\t\"crypto/tls\"\n\t\"net\"\n\t\"net/http\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/event\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/httputil\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/ocsp\"\n)\n\n// Dialer is used to make network connections.\ntype Dialer interface {\n\tDialContext(ctx context.Context, network, address string) (net.Conn, error)\n}\n\n// DialerFunc is a type implemented by functions that can be used as a Dialer.\ntype DialerFunc func(ctx context.Context, network, address string) (net.Conn, error)\n\n// DialContext implements the Dialer interface.\nfunc (df DialerFunc) DialContext(ctx context.Context, network, address string) (net.Conn, error) {\n\treturn df(ctx, network, address)\n}\n\n// DefaultDialer is the Dialer implementation that is used by this package. Changing this\n// will also change the Dialer used for this package. This should only be changed why all\n// of the connections being made need to use a different Dialer. Most of the time, using a\n// WithDialer option is more appropriate than changing this variable.\nvar DefaultDialer Dialer = &net.Dialer{}\n\n// Handshaker is the interface implemented by types that can perform a MongoDB\n// handshake over a provided driver.Connection. This is used during connection\n// initialization. Implementations must be goroutine safe.\ntype Handshaker = driver.Handshaker\n\n// generationNumberFn is a callback type used by a connection to fetch its generation number given its service ID.\ntype generationNumberFn func(serviceID *bson.ObjectID) uint64\n\ntype connectionConfig struct {\n\tdialer                   Dialer\n\thandshaker               Handshaker\n\tidleTimeout              time.Duration\n\tcmdMonitor               *event.CommandMonitor\n\ttlsConfig                *tls.Config\n\thttpClient               *http.Client\n\tcompressors              []string\n\tzlibLevel                *int\n\tzstdLevel                *int\n\tocspCache                ocsp.Cache\n\tdisableOCSPEndpointCheck bool\n\ttlsConnectionSource      tlsConnectionSource\n\tloadBalanced             bool\n\tgetGenerationFn          generationNumberFn\n}\n\nfunc newConnectionConfig(opts ...ConnectionOption) *connectionConfig {\n\tcfg := &connectionConfig{\n\t\tdialer:              nil,\n\t\ttlsConnectionSource: defaultTLSConnectionSource,\n\t\thttpClient:          httputil.DefaultHTTPClient,\n\t}\n\n\tfor _, opt := range opts {\n\t\tif opt == nil {\n\t\t\tcontinue\n\t\t}\n\t\topt(cfg)\n\t}\n\n\tif cfg.dialer == nil {\n\t\t// Use a zero value of net.Dialer when nothing is specified, so the Go driver applies default default behaviors\n\t\t// such as Timeout, KeepAlive, DNS resolving, etc. See https://golang.org/pkg/net/#Dialer for more information.\n\t\tcfg.dialer = &net.Dialer{}\n\t}\n\n\treturn cfg\n}\n\n// ConnectionOption is used to configure a connection.\ntype ConnectionOption func(*connectionConfig)\n\nfunc withTLSConnectionSource(fn func(tlsConnectionSource) tlsConnectionSource) ConnectionOption {\n\treturn func(c *connectionConfig) {\n\t\tc.tlsConnectionSource = fn(c.tlsConnectionSource)\n\t}\n}\n\n// WithCompressors sets the compressors that can be used for communication.\nfunc WithCompressors(fn func([]string) []string) ConnectionOption {\n\treturn func(c *connectionConfig) {\n\t\tc.compressors = fn(c.compressors)\n\t}\n}\n\n// WithDialer configures the Dialer to use when making a new connection to MongoDB.\nfunc WithDialer(fn func(Dialer) Dialer) ConnectionOption {\n\treturn func(c *connectionConfig) {\n\t\tc.dialer = fn(c.dialer)\n\t}\n}\n\n// WithHandshaker configures the Handshaker that wll be used to initialize newly\n// dialed connections.\nfunc WithHandshaker(fn func(Handshaker) Handshaker) ConnectionOption {\n\treturn func(c *connectionConfig) {\n\t\tc.handshaker = fn(c.handshaker)\n\t}\n}\n\n// WithIdleTimeout configures the maximum idle time to allow for a connection.\nfunc WithIdleTimeout(fn func(time.Duration) time.Duration) ConnectionOption {\n\treturn func(c *connectionConfig) {\n\t\tc.idleTimeout = fn(c.idleTimeout)\n\t}\n}\n\n// WithTLSConfig configures the TLS options for a connection.\nfunc WithTLSConfig(fn func(*tls.Config) *tls.Config) ConnectionOption {\n\treturn func(c *connectionConfig) {\n\t\tc.tlsConfig = fn(c.tlsConfig)\n\t}\n}\n\n// WithHTTPClient configures the HTTP client for a connection.\nfunc WithHTTPClient(fn func(*http.Client) *http.Client) ConnectionOption {\n\treturn func(c *connectionConfig) {\n\t\tc.httpClient = fn(c.httpClient)\n\t}\n}\n\n// WithMonitor configures a event for command monitoring.\nfunc WithMonitor(fn func(*event.CommandMonitor) *event.CommandMonitor) ConnectionOption {\n\treturn func(c *connectionConfig) {\n\t\tc.cmdMonitor = fn(c.cmdMonitor)\n\t}\n}\n\n// WithZlibLevel sets the zLib compression level.\nfunc WithZlibLevel(fn func(*int) *int) ConnectionOption {\n\treturn func(c *connectionConfig) {\n\t\tc.zlibLevel = fn(c.zlibLevel)\n\t}\n}\n\n// WithZstdLevel sets the zstd compression level.\nfunc WithZstdLevel(fn func(*int) *int) ConnectionOption {\n\treturn func(c *connectionConfig) {\n\t\tc.zstdLevel = fn(c.zstdLevel)\n\t}\n}\n\n// WithOCSPCache specifies a cache to use for OCSP verification.\nfunc WithOCSPCache(fn func(ocsp.Cache) ocsp.Cache) ConnectionOption {\n\treturn func(c *connectionConfig) {\n\t\tc.ocspCache = fn(c.ocspCache)\n\t}\n}\n\n// WithDisableOCSPEndpointCheck specifies whether or the driver should perform non-stapled OCSP verification. If set\n// to true, the driver will only check stapled responses and will continue the connection without reaching out to\n// OCSP responders.\nfunc WithDisableOCSPEndpointCheck(fn func(bool) bool) ConnectionOption {\n\treturn func(c *connectionConfig) {\n\t\tc.disableOCSPEndpointCheck = fn(c.disableOCSPEndpointCheck)\n\t}\n}\n\n// WithConnectionLoadBalanced specifies whether or not the connection is to a server behind a load balancer.\nfunc WithConnectionLoadBalanced(fn func(bool) bool) ConnectionOption {\n\treturn func(c *connectionConfig) {\n\t\tc.loadBalanced = fn(c.loadBalanced)\n\t}\n}\n\nfunc withGenerationNumberFn(fn func(generationNumberFn) generationNumberFn) ConnectionOption {\n\treturn func(c *connectionConfig) {\n\t\tc.getGenerationFn = fn(c.getGenerationFn)\n\t}\n}\n"
  },
  {
    "path": "x/mongo/driver/topology/connection_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage topology\n\nimport (\n\t\"context\"\n\t\"crypto/tls\"\n\t\"crypto/x509\"\n\t\"errors\"\n\t\"math/rand\"\n\t\"net\"\n\t\"sync\"\n\t\"sync/atomic\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/google/go-cmp/cmp\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/require\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/address\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/description\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/mnet\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/wiremessage\"\n)\n\ntype testHandshaker struct {\n\tgetHandshakeInformation func(context.Context, address.Address, *mnet.Connection) (driver.HandshakeInformation, error)\n\tfinishHandshake         func(context.Context, *mnet.Connection) error\n}\n\n// GetHandshakeInformation implements the Handshaker interface.\nfunc (th *testHandshaker) GetHandshakeInformation(ctx context.Context, addr address.Address, conn *mnet.Connection) (driver.HandshakeInformation, error) {\n\tif th.getHandshakeInformation != nil {\n\t\treturn th.getHandshakeInformation(ctx, addr, conn)\n\t}\n\treturn driver.HandshakeInformation{}, nil\n}\n\n// FinishHandshake implements the Handshaker interface.\nfunc (th *testHandshaker) FinishHandshake(ctx context.Context, conn *mnet.Connection) error {\n\tif th.finishHandshake != nil {\n\t\treturn th.finishHandshake(ctx, conn)\n\t}\n\treturn nil\n}\n\nvar _ driver.Handshaker = &testHandshaker{}\n\nfunc TestConnection(t *testing.T) {\n\tt.Run(\"connection\", func(t *testing.T) {\n\t\tt.Run(\"newConnection\", func(t *testing.T) {\n\t\t\tt.Run(\"no default idle timeout\", func(t *testing.T) {\n\t\t\t\tconn := newConnection(address.Address(\"\"))\n\t\t\t\twantTimeout := time.Duration(0)\n\t\t\t\tassert.Equal(t, wantTimeout, conn.idleTimeout, \"expected idle timeout %v, got %v\", wantTimeout,\n\t\t\t\t\tconn.idleTimeout)\n\t\t\t})\n\t\t})\n\t\tt.Run(\"connect\", func(t *testing.T) {\n\t\t\tt.Run(\"connection refused gets backpressure labels\", func(t *testing.T) {\n\t\t\t\tconn := newConnection(address.Address(\"127.0.0.1:1\"))\n\t\t\t\terr := conn.connect(context.Background())\n\t\t\t\tvar de driver.Error\n\t\t\t\trequire.True(t, errors.As(err, &de), \"expected driver.Error, got %T: %v\", err, err)\n\t\t\t\tassert.True(t, de.HasErrorLabel(driver.ErrSystemOverloadedError),\n\t\t\t\t\t\"expected SystemOverloadedError label on connection refused error\")\n\t\t\t\tassert.True(t, de.HasErrorLabel(driver.ErrRetryableError),\n\t\t\t\t\t\"expected RetryableError label on connection refused error\")\n\t\t\t})\n\t\t\tt.Run(\"DNS lookup failure does not get backpressure labels\", func(t *testing.T) {\n\t\t\t\tconn := newConnection(address.Address(\"nonexistent.invalid:27017\"))\n\t\t\t\terr := conn.connect(context.Background())\n\t\t\t\tvar de driver.Error\n\t\t\t\tassert.False(t, errors.As(err, &de),\n\t\t\t\t\t\"DNS errors should not be wrapped in driver.Error with backpressure labels, got: %v\", err)\n\t\t\t\tvar connErr ConnectionError\n\t\t\t\trequire.True(t, errors.As(err, &connErr), \"expected ConnectionError, got %T: %v\", err, err)\n\t\t\t\tvar dnsErr *net.DNSError\n\t\t\t\tassert.True(t, errors.As(connErr.Wrapped, &dnsErr),\n\t\t\t\t\t\"expected *net.DNSError inside ConnectionError, got %T: %v\", connErr.Wrapped, connErr.Wrapped)\n\t\t\t})\n\t\t\tt.Run(\"TLS unknown authority error does not get backpressure labels\", func(t *testing.T) {\n\t\t\t\tconn := newConnection(address.Address(\"google.com:443\"),\n\t\t\t\t\tWithTLSConfig(func(_ *tls.Config) *tls.Config {\n\t\t\t\t\t\treturn &tls.Config{\n\t\t\t\t\t\t\tServerName: \"google.com\",\n\t\t\t\t\t\t\tRootCAs:    x509.NewCertPool(),\n\t\t\t\t\t\t}\n\t\t\t\t\t}),\n\t\t\t\t)\n\t\t\t\terr := conn.connect(context.Background())\n\t\t\t\tvar de driver.Error\n\t\t\t\tassert.False(t, errors.As(err, &de),\n\t\t\t\t\t\"x509.UnknownAuthorityError should not get backpressure labels, got: %v\", err)\n\t\t\t\tvar connErr ConnectionError\n\t\t\t\trequire.True(t, errors.As(err, &connErr),\n\t\t\t\t\t\"expected ConnectionError, got %T: %v\", err, err)\n\t\t\t\tvar unknownCAErr x509.UnknownAuthorityError\n\t\t\t\tassert.True(t, errors.As(connErr.Wrapped, &unknownCAErr),\n\t\t\t\t\t\"expected x509.UnknownAuthorityError, got %T: %v\", connErr.Wrapped, connErr.Wrapped)\n\t\t\t})\n\t\t\tt.Run(\"TLS hostname mismatch does not get backpressure labels\", func(t *testing.T) {\n\t\t\t\tconn := newConnection(address.Address(\"google.com:443\"),\n\t\t\t\t\tWithTLSConfig(func(_ *tls.Config) *tls.Config {\n\t\t\t\t\t\treturn &tls.Config{\n\t\t\t\t\t\t\tServerName: \"wronghost.example.com\",\n\t\t\t\t\t\t}\n\t\t\t\t\t}),\n\t\t\t\t)\n\t\t\t\terr := conn.connect(context.Background())\n\t\t\t\tvar de driver.Error\n\t\t\t\tassert.False(t, errors.As(err, &de),\n\t\t\t\t\t\"x509.HostnameError should not get backpressure labels, got: %v\", err)\n\t\t\t\tvar connErr ConnectionError\n\t\t\t\trequire.True(t, errors.As(err, &connErr),\n\t\t\t\t\t\"expected ConnectionError, got %T: %v\", err, err)\n\t\t\t\tvar hostErr x509.HostnameError\n\t\t\t\tassert.True(t, errors.As(connErr.Wrapped, &hostErr),\n\t\t\t\t\t\"expected x509.HostnameError, got %T: %v\", connErr.Wrapped, connErr.Wrapped)\n\t\t\t})\n\t\t\tt.Run(\"handshaker error\", func(t *testing.T) {\n\t\t\t\terr := errors.New(\"handshaker error\")\n\t\t\t\tvar want error = ConnectionError{Wrapped: err, init: true}\n\t\t\t\tconn := newConnection(address.Address(\"\"),\n\t\t\t\t\tWithHandshaker(func(Handshaker) Handshaker {\n\t\t\t\t\t\treturn &testHandshaker{\n\t\t\t\t\t\t\tfinishHandshake: func(context.Context, *mnet.Connection) error {\n\t\t\t\t\t\t\t\treturn err\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}\n\t\t\t\t\t}),\n\t\t\t\t\tWithDialer(func(Dialer) Dialer {\n\t\t\t\t\t\treturn DialerFunc(func(context.Context, string, string) (net.Conn, error) {\n\t\t\t\t\t\t\treturn &net.TCPConn{}, nil\n\t\t\t\t\t\t})\n\t\t\t\t\t}),\n\t\t\t\t)\n\t\t\t\tgot := conn.connect(context.Background())\n\t\t\t\tif !cmp.Equal(got, want, cmp.Comparer(compareErrors)) {\n\t\t\t\t\tt.Errorf(\"errors do not match. got %v; want %v\", got, want)\n\t\t\t\t}\n\t\t\t\tconnState := atomic.LoadInt64(&conn.state)\n\t\t\t\tassert.Equal(t, connDisconnected, connState, \"expected connection state %v, got %v\", connDisconnected, connState)\n\t\t\t})\n\t\t\tt.Run(\"context is not pinned by connect\", func(t *testing.T) {\n\t\t\t\t// connect creates a cancel-able version of the context passed to it and stores the CancelFunc on the\n\t\t\t\t// connection. The CancelFunc must be set to nil once the connection has been established so the driver\n\t\t\t\t// does not pin the memory associated with the context for the connection's lifetime.\n\n\t\t\t\tt.Run(\"connect succeeds\", func(t *testing.T) {\n\t\t\t\t\t// In the case where connect finishes successfully, it unpins the CancelFunc.\n\n\t\t\t\t\tconn := newConnection(address.Address(\"\"),\n\t\t\t\t\t\tWithDialer(func(Dialer) Dialer {\n\t\t\t\t\t\t\treturn DialerFunc(func(context.Context, string, string) (net.Conn, error) {\n\t\t\t\t\t\t\t\treturn &net.TCPConn{}, nil\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t}),\n\t\t\t\t\t\tWithHandshaker(func(Handshaker) Handshaker {\n\t\t\t\t\t\t\treturn &testHandshaker{}\n\t\t\t\t\t\t}),\n\t\t\t\t\t)\n\n\t\t\t\t\terr := conn.connect(context.Background())\n\t\t\t\t\tassert.Nil(t, err, \"error establishing connection: %v\", err)\n\t\t\t\t})\n\t\t\t\tt.Run(\"connect cancelled\", func(t *testing.T) {\n\t\t\t\t\t// In the case where connection establishment is cancelled, the closeConnectContext function\n\t\t\t\t\t// unpins the CancelFunc.\n\n\t\t\t\t\t// Create a connection that will block in connect until doneChan is closed. This prevents\n\t\t\t\t\t// connect from succeeding and unpinning the CancelFunc.\n\t\t\t\t\tdoneChan := make(chan struct{})\n\t\t\t\t\tconn := newConnection(address.Address(\"\"),\n\t\t\t\t\t\tWithDialer(func(Dialer) Dialer {\n\t\t\t\t\t\t\treturn DialerFunc(func(context.Context, string, string) (net.Conn, error) {\n\t\t\t\t\t\t\t\t<-doneChan\n\t\t\t\t\t\t\t\treturn &net.TCPConn{}, nil\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t}),\n\t\t\t\t\t\tWithHandshaker(func(Handshaker) Handshaker {\n\t\t\t\t\t\t\treturn &testHandshaker{}\n\t\t\t\t\t\t}),\n\t\t\t\t\t)\n\n\t\t\t\t\t// Call connect in a goroutine because it will block.\n\t\t\t\t\tvar done atomic.Value\n\t\t\t\t\tgo func() {\n\t\t\t\t\t\tdefer done.Store(true)\n\t\t\t\t\t\t_ = conn.connect(context.Background())\n\t\t\t\t\t}()\n\n\t\t\t\t\t// Simulate cancelling connection establishment and assert that this clears the CancelFunc.\n\t\t\t\t\tconn.closeConnectContext()\n\t\t\t\t\tclose(doneChan)\n\n\t\t\t\t\tassert.Eventually(t,\n\t\t\t\t\t\tfunc() bool { return done.Load() != nil && done.Load().(bool) },\n\t\t\t\t\t\t100*time.Millisecond,\n\t\t\t\t\t\t1*time.Millisecond,\n\t\t\t\t\t\t\"TODO\")\n\t\t\t\t})\n\t\t\t})\n\t\t\tt.Run(\"tls\", func(t *testing.T) {\n\t\t\t\tt.Run(\"connection source is set to default if unspecified\", func(t *testing.T) {\n\t\t\t\t\tconn := newConnection(address.Address(\"\"))\n\t\t\t\t\tassert.NotNil(t, conn.config.tlsConnectionSource, \"expected tlsConnectionSource to be set but was not\")\n\t\t\t\t})\n\t\t\t\tt.Run(\"server name\", func(t *testing.T) {\n\t\t\t\t\ttestCases := []struct {\n\t\t\t\t\t\tname               string\n\t\t\t\t\t\taddr               address.Address\n\t\t\t\t\t\tcfg                *tls.Config\n\t\t\t\t\t\texpectedServerName string\n\t\t\t\t\t}{\n\t\t\t\t\t\t{\"set to connection address if empty\", \"localhost:27017\", &tls.Config{}, \"localhost\"},\n\t\t\t\t\t\t{\"left alone if non-empty\", \"localhost:27017\", &tls.Config{ServerName: \"other\"}, \"other\"},\n\t\t\t\t\t}\n\t\t\t\t\tfor _, tc := range testCases {\n\t\t\t\t\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\t\t\t\t\tvar sentCfg *tls.Config\n\t\t\t\t\t\t\tvar testTLSConnectionSource tlsConnectionSourceFn = func(nc net.Conn, cfg *tls.Config) tlsConn {\n\t\t\t\t\t\t\t\tsentCfg = cfg\n\t\t\t\t\t\t\t\treturn tls.Client(nc, cfg)\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tconnOpts := []ConnectionOption{\n\t\t\t\t\t\t\t\tWithDialer(func(Dialer) Dialer {\n\t\t\t\t\t\t\t\t\treturn DialerFunc(func(context.Context, string, string) (net.Conn, error) {\n\t\t\t\t\t\t\t\t\t\treturn &net.TCPConn{}, nil\n\t\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t\tWithHandshaker(func(Handshaker) Handshaker {\n\t\t\t\t\t\t\t\t\treturn &testHandshaker{}\n\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t\tWithTLSConfig(func(*tls.Config) *tls.Config {\n\t\t\t\t\t\t\t\t\treturn tc.cfg\n\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t\twithTLSConnectionSource(func(tlsConnectionSource) tlsConnectionSource {\n\t\t\t\t\t\t\t\t\treturn testTLSConnectionSource\n\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tconn := newConnection(tc.addr, connOpts...)\n\n\t\t\t\t\t\t\t_ = conn.connect(context.Background())\n\t\t\t\t\t\t\tassert.NotNil(t, sentCfg, \"expected TLS config to be set, but was not\")\n\t\t\t\t\t\t\tassert.Equal(t, tc.expectedServerName, sentCfg.ServerName, \"expected ServerName %s, got %s\",\n\t\t\t\t\t\t\t\ttc.expectedServerName, sentCfg.ServerName)\n\t\t\t\t\t\t})\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t})\n\t\t})\n\t\tt.Run(\"writeWireMessage\", func(t *testing.T) {\n\t\t\tt.Run(\"closed connection\", func(t *testing.T) {\n\t\t\t\tconn := &connection{id: \"foobar\"}\n\t\t\t\twant := ConnectionError{ConnectionID: \"foobar\", message: \"connection is closed\"}\n\t\t\t\tgot := conn.writeWireMessage(context.Background(), []byte{})\n\t\t\t\tif !cmp.Equal(got, want, cmp.Comparer(compareErrors)) {\n\t\t\t\t\tt.Errorf(\"errors do not match. got %v; want %v\", got, want)\n\t\t\t\t}\n\t\t\t})\n\t\t\tt.Run(\"deadlines\", func(t *testing.T) {\n\t\t\t\ttestCases := []struct {\n\t\t\t\t\tname        string\n\t\t\t\t\tctxDeadline time.Duration\n\t\t\t\t\tdeadline    time.Time\n\t\t\t\t}{\n\t\t\t\t\t{\"no deadline\", 0, time.Now().Add(1 * time.Second)},\n\t\t\t\t\t{\"ctx deadline\", 5 * time.Second, time.Now().Add(6 * time.Second)},\n\t\t\t\t}\n\n\t\t\t\tfor _, tc := range testCases {\n\t\t\t\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\t\t\t\tctx := context.Background()\n\t\t\t\t\t\tif tc.ctxDeadline > 0 {\n\t\t\t\t\t\t\tvar cancel context.CancelFunc\n\t\t\t\t\t\t\tctx, cancel = context.WithTimeout(ctx, tc.ctxDeadline)\n\t\t\t\t\t\t\tdefer cancel()\n\t\t\t\t\t\t}\n\t\t\t\t\t\twant := ConnectionError{\n\t\t\t\t\t\t\tConnectionID: \"foobar\",\n\t\t\t\t\t\t\tWrapped:      errors.New(\"set writeDeadline error\"),\n\t\t\t\t\t\t\tmessage:      \"failed to set write deadline\",\n\t\t\t\t\t\t}\n\t\t\t\t\t\ttnc := &testNetConn{deadlineerr: errors.New(\"set writeDeadline error\")}\n\t\t\t\t\t\tconn := &connection{id: \"foobar\", nc: tnc, state: connConnected}\n\t\t\t\t\t\tgot := conn.writeWireMessage(ctx, []byte{})\n\t\t\t\t\t\tif !cmp.Equal(got, want, cmp.Comparer(compareErrors)) {\n\t\t\t\t\t\t\tt.Errorf(\"errors do not match. got %v; want %v\", got, want)\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif !tc.deadline.After(tnc.writeDeadline) {\n\t\t\t\t\t\t\tt.Errorf(\"write deadline not properly set. got %v; want %v\", tnc.writeDeadline, tc.deadline)\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t})\n\t\t\tt.Run(\"Write\", func(t *testing.T) {\n\t\t\t\twriteErrMsg := \"unable to write wire message to network\"\n\n\t\t\t\tt.Run(\"error\", func(t *testing.T) {\n\t\t\t\t\terr := errors.New(\"Write error\")\n\t\t\t\t\ttnc := &testNetConn{writeerr: err}\n\t\t\t\t\tconn := &connection{id: \"foobar\", nc: tnc, state: connConnected}\n\t\t\t\t\tlistener := newTestCancellationListener(false)\n\t\t\t\t\tconn.cancellationListener = listener\n\n\t\t\t\t\twant := ConnectionError{ConnectionID: \"foobar\", Wrapped: err, message: writeErrMsg}\n\t\t\t\t\tgot := conn.writeWireMessage(context.Background(), []byte{})\n\t\t\t\t\tif !cmp.Equal(got, want, cmp.Comparer(compareErrors)) {\n\t\t\t\t\t\tt.Errorf(\"errors do not match. got %v; want %v\", got, want)\n\t\t\t\t\t}\n\t\t\t\t\tif !tnc.closed {\n\t\t\t\t\t\tt.Errorf(\"failed to closeConnection net.Conn after error writing bytes.\")\n\t\t\t\t\t}\n\t\t\t\t\tlistener.assertCalledOnce(t)\n\t\t\t\t})\n\t\t\t\tt.Run(\"success\", func(t *testing.T) {\n\t\t\t\t\ttnc := &testNetConn{}\n\t\t\t\t\tconn := &connection{id: \"foobar\", nc: tnc, state: connConnected}\n\t\t\t\t\tlistener := newTestCancellationListener(false)\n\t\t\t\t\tconn.cancellationListener = listener\n\n\t\t\t\t\twant := []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A}\n\t\t\t\t\terr := conn.writeWireMessage(context.Background(), want)\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\tgot := tnc.buf\n\t\t\t\t\tif !cmp.Equal(got, want) {\n\t\t\t\t\t\tt.Errorf(\"writeWireMessage did not write the proper bytes. got %v; want %v\", got, want)\n\t\t\t\t\t}\n\t\t\t\t\tlistener.assertCalledOnce(t)\n\t\t\t\t})\n\t\t\t\tt.Run(\"cancel in-progress write\", func(t *testing.T) {\n\t\t\t\t\t// Simulate context cancellation during a network write.\n\n\t\t\t\t\tnc := newCancellationWriteConn(&testNetConn{}, 0)\n\t\t\t\t\tconn := &connection{id: \"foobar\", nc: nc, state: connConnected}\n\t\t\t\t\tlistener := newTestCancellationListener(false)\n\t\t\t\t\tconn.cancellationListener = listener\n\n\t\t\t\t\tctx, cancel := context.WithCancel(context.Background())\n\t\t\t\t\tvar err error\n\n\t\t\t\t\tvar wg sync.WaitGroup\n\t\t\t\t\twg.Add(1)\n\t\t\t\t\tgo func() {\n\t\t\t\t\t\tdefer wg.Done()\n\t\t\t\t\t\terr = conn.writeWireMessage(ctx, []byte(\"foobar\"))\n\t\t\t\t\t}()\n\n\t\t\t\t\t<-nc.operationStartedChan\n\t\t\t\t\tcancel()\n\t\t\t\t\tnc.continueChan <- struct{}{}\n\n\t\t\t\t\twg.Wait()\n\t\t\t\t\twant := ConnectionError{ConnectionID: conn.id, Wrapped: context.Canceled, message: writeErrMsg}\n\t\t\t\t\tassert.Equal(t, want, err, \"expected error %v, got %v\", want, err)\n\t\t\t\t\tassert.Equal(t, connDisconnected, conn.state, \"expected connection state %v, got %v\", connDisconnected,\n\t\t\t\t\t\tconn.state)\n\t\t\t\t})\n\t\t\t\tt.Run(\"connection is closed if context is cancelled even if network write succeeds\", func(t *testing.T) {\n\t\t\t\t\t// Test the race condition between Write and the cancellation listener. The socket write will\n\t\t\t\t\t// succeed, but we set the abortedForCancellation flag to true to simulate the context being\n\t\t\t\t\t// cancelled immediately after the Write finishes.\n\n\t\t\t\t\ttnc := &testNetConn{}\n\t\t\t\t\tconn := &connection{id: \"foobar\", nc: tnc, state: connConnected}\n\t\t\t\t\tlistener := newTestCancellationListener(true)\n\t\t\t\t\tconn.cancellationListener = listener\n\n\t\t\t\t\twant := ConnectionError{ConnectionID: conn.id, Wrapped: context.Canceled, message: writeErrMsg}\n\t\t\t\t\terr := conn.writeWireMessage(context.Background(), []byte(\"foobar\"))\n\t\t\t\t\tassert.Equal(t, want, err, \"expected error %v, got %v\", want, err)\n\t\t\t\t\tassert.Equal(t, conn.state, connDisconnected, \"expected connection state %v, got %v\", connDisconnected,\n\t\t\t\t\t\tconn.state)\n\t\t\t\t})\n\t\t\t})\n\t\t})\n\t\tt.Run(\"readWireMessage\", func(t *testing.T) {\n\t\t\tt.Run(\"closed connection\", func(t *testing.T) {\n\t\t\t\tconn := &connection{id: \"foobar\"}\n\t\t\t\twant := ConnectionError{ConnectionID: \"foobar\", message: \"connection is closed\"}\n\t\t\t\t_, got := conn.readWireMessage(context.Background())\n\t\t\t\tif !cmp.Equal(got, want, cmp.Comparer(compareErrors)) {\n\t\t\t\t\tt.Errorf(\"errors do not match. got %v; want %v\", got, want)\n\t\t\t\t}\n\t\t\t})\n\t\t\tt.Run(\"deadlines\", func(t *testing.T) {\n\t\t\t\ttestCases := []struct {\n\t\t\t\t\tname        string\n\t\t\t\t\tctxDeadline time.Duration\n\t\t\t\t\tdeadline    time.Time\n\t\t\t\t}{\n\t\t\t\t\t{\"no deadline\", 0, time.Now().Add(1 * time.Second)},\n\t\t\t\t\t{\"ctx deadline\", 5 * time.Second, time.Now().Add(6 * time.Second)},\n\t\t\t\t}\n\n\t\t\t\tfor _, tc := range testCases {\n\t\t\t\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\t\t\t\tctx := context.Background()\n\t\t\t\t\t\tif tc.ctxDeadline > 0 {\n\t\t\t\t\t\t\tvar cancel context.CancelFunc\n\t\t\t\t\t\t\tctx, cancel = context.WithTimeout(ctx, tc.ctxDeadline)\n\t\t\t\t\t\t\tdefer cancel()\n\t\t\t\t\t\t}\n\t\t\t\t\t\twant := ConnectionError{\n\t\t\t\t\t\t\tConnectionID: \"foobar\",\n\t\t\t\t\t\t\tWrapped:      errors.New(\"set readDeadline error\"),\n\t\t\t\t\t\t\tmessage:      \"failed to set read deadline\",\n\t\t\t\t\t\t}\n\t\t\t\t\t\ttnc := &testNetConn{deadlineerr: errors.New(\"set readDeadline error\")}\n\t\t\t\t\t\tconn := &connection{id: \"foobar\", nc: tnc, state: connConnected}\n\t\t\t\t\t\t_, got := conn.readWireMessage(ctx)\n\t\t\t\t\t\tif !cmp.Equal(got, want, cmp.Comparer(compareErrors)) {\n\t\t\t\t\t\t\tt.Errorf(\"errors do not match. got %v; want %v\", got, want)\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif !tc.deadline.After(tnc.readDeadline) {\n\t\t\t\t\t\t\tt.Errorf(\"read deadline not properly set. got %v; want %v\", tnc.readDeadline, tc.deadline)\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t})\n\t\t\tt.Run(\"Read\", func(t *testing.T) {\n\t\t\t\tt.Run(\"size read errors\", func(t *testing.T) {\n\t\t\t\t\terr := errors.New(\"Read error\")\n\t\t\t\t\ttnc := &testNetConn{readerr: err}\n\t\t\t\t\tconn := &connection{id: \"foobar\", nc: tnc, state: connConnected}\n\t\t\t\t\tlistener := newTestCancellationListener(false)\n\t\t\t\t\tconn.cancellationListener = listener\n\n\t\t\t\t\twant := ConnectionError{ConnectionID: \"foobar\", Wrapped: err, message: \"incomplete read of message header\"}\n\t\t\t\t\t_, got := conn.readWireMessage(context.Background())\n\t\t\t\t\tif !cmp.Equal(got, want, cmp.Comparer(compareErrors)) {\n\t\t\t\t\t\tt.Errorf(\"errors do not match. got %v; want %v\", got, want)\n\t\t\t\t\t}\n\t\t\t\t\tif !tnc.closed {\n\t\t\t\t\t\tt.Errorf(\"failed to closeConnection net.Conn after error writing bytes.\")\n\t\t\t\t\t}\n\t\t\t\t\tlistener.assertCalledOnce(t)\n\t\t\t\t})\n\t\t\t\tt.Run(\"size too small errors\", func(t *testing.T) {\n\t\t\t\t\terr := errors.New(\"malformed message length: 3\")\n\t\t\t\t\ttnc := &testNetConn{readerr: err, buf: []byte{0x03, 0x00, 0x00, 0x00}}\n\t\t\t\t\tconn := &connection{id: \"foobar\", nc: tnc, state: connConnected}\n\t\t\t\t\tlistener := newTestCancellationListener(false)\n\t\t\t\t\tconn.cancellationListener = listener\n\n\t\t\t\t\twant := ConnectionError{ConnectionID: \"foobar\", Wrapped: err, message: err.Error()}\n\t\t\t\t\t_, got := conn.readWireMessage(context.Background())\n\t\t\t\t\tif !cmp.Equal(got, want, cmp.Comparer(compareErrors)) {\n\t\t\t\t\t\tt.Errorf(\"errors do not match. got %v; want %v\", got, want)\n\t\t\t\t\t}\n\t\t\t\t\tif !tnc.closed {\n\t\t\t\t\t\tt.Errorf(\"failed to closeConnection net.Conn after error writing bytes.\")\n\t\t\t\t\t}\n\t\t\t\t\tlistener.assertCalledOnce(t)\n\t\t\t\t})\n\t\t\t\tt.Run(\"full message read errors\", func(t *testing.T) {\n\t\t\t\t\terr := errors.New(\"Read error\")\n\t\t\t\t\ttnc := &testNetConn{readerr: err, buf: []byte{0x11, 0x00, 0x00, 0x00}}\n\t\t\t\t\tconn := &connection{id: \"foobar\", nc: tnc, state: connConnected}\n\t\t\t\t\tlistener := newTestCancellationListener(false)\n\t\t\t\t\tconn.cancellationListener = listener\n\n\t\t\t\t\twant := ConnectionError{ConnectionID: \"foobar\", Wrapped: err, message: \"incomplete read of full message\"}\n\t\t\t\t\t_, got := conn.readWireMessage(context.Background())\n\t\t\t\t\tif !cmp.Equal(got, want, cmp.Comparer(compareErrors)) {\n\t\t\t\t\t\tt.Errorf(\"errors do not match. got %v; want %v\", got, want)\n\t\t\t\t\t}\n\t\t\t\t\tif !tnc.closed {\n\t\t\t\t\t\tt.Errorf(\"failed to closeConnection net.Conn after error writing bytes.\")\n\t\t\t\t\t}\n\t\t\t\t\tlistener.assertCalledOnce(t)\n\t\t\t\t})\n\t\t\t\tt.Run(\"message too large errors\", func(t *testing.T) {\n\t\t\t\t\ttestCases := []struct {\n\t\t\t\t\t\tname   string\n\t\t\t\t\t\tbuffer []byte\n\t\t\t\t\t\tdesc   description.Server\n\t\t\t\t\t}{\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"message too large errors with small max message size\",\n\t\t\t\t\t\t\t[]byte{0x0A, 0x00, 0x00, 0x00}, // defines a message size of 10 in hex with the first four bytes.\n\t\t\t\t\t\t\tdescription.Server{MaxMessageSize: 9},\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"message too large errors with default max message size\",\n\t\t\t\t\t\t\t[]byte{0x01, 0x6C, 0xDC, 0x02}, // defines a message size of 48000001 in hex with the first four bytes.\n\t\t\t\t\t\t\tdescription.Server{},\n\t\t\t\t\t\t},\n\t\t\t\t\t}\n\t\t\t\t\tfor _, tc := range testCases {\n\t\t\t\t\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\t\t\t\t\terr := errors.New(\"length of read message too large\")\n\t\t\t\t\t\t\ttnc := &testNetConn{buf: make([]byte, len(tc.buffer))}\n\t\t\t\t\t\t\tcopy(tnc.buf, tc.buffer)\n\t\t\t\t\t\t\tconn := &connection{id: \"foobar\", nc: tnc, state: connConnected, desc: tc.desc}\n\t\t\t\t\t\t\tlistener := newTestCancellationListener(false)\n\t\t\t\t\t\t\tconn.cancellationListener = listener\n\n\t\t\t\t\t\t\twant := ConnectionError{ConnectionID: \"foobar\", Wrapped: err, message: err.Error()}\n\t\t\t\t\t\t\t_, got := conn.readWireMessage(context.Background())\n\t\t\t\t\t\t\tif !cmp.Equal(got, want, cmp.Comparer(compareErrors)) {\n\t\t\t\t\t\t\t\tt.Errorf(\"errors do not match. got %v; want %v\", got, want)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tlistener.assertCalledOnce(t)\n\t\t\t\t\t\t})\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t\tt.Run(\"success\", func(t *testing.T) {\n\t\t\t\t\twant := []byte{0x0A, 0x00, 0x00, 0x00, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A}\n\t\t\t\t\ttnc := &testNetConn{buf: make([]byte, len(want))}\n\t\t\t\t\tcopy(tnc.buf, want)\n\t\t\t\t\tconn := &connection{id: \"foobar\", nc: tnc, state: connConnected}\n\t\t\t\t\tlistener := newTestCancellationListener(false)\n\t\t\t\t\tconn.cancellationListener = listener\n\n\t\t\t\t\tgot, err := conn.readWireMessage(context.Background())\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t\tif !cmp.Equal(got, want) {\n\t\t\t\t\t\tt.Errorf(\"did not read full wire message. got %v; want %v\", got, want)\n\t\t\t\t\t}\n\t\t\t\t\tlistener.assertCalledOnce(t)\n\t\t\t\t})\n\t\t\t\tt.Run(\"cancel in-progress read\", func(t *testing.T) {\n\t\t\t\t\t// Simulate context cancellation during a network read. This has two sub-tests to test cancellation\n\t\t\t\t\t// when reading the msg size and when reading the rest of the msg.\n\n\t\t\t\t\ttestCases := []struct {\n\t\t\t\t\t\tname   string\n\t\t\t\t\t\tskip   int\n\t\t\t\t\t\terrmsg string\n\t\t\t\t\t}{\n\t\t\t\t\t\t{\"cancel size read\", 0, \"incomplete read of message header\"},\n\t\t\t\t\t\t{\"cancel full message read\", 1, \"incomplete read of full message\"},\n\t\t\t\t\t}\n\t\t\t\t\tfor _, tc := range testCases {\n\t\t\t\t\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\t\t\t\t\t// In the full message case, the size read needs to succeed and return a non-zero size, so\n\t\t\t\t\t\t\t// we set readBuf to indicate that the full message will have 10 bytes.\n\t\t\t\t\t\t\treadBuf := []byte{10, 0, 0, 0}\n\t\t\t\t\t\t\tnc := newCancellationReadConn(&testNetConn{}, tc.skip, readBuf)\n\n\t\t\t\t\t\t\tconn := &connection{id: \"foobar\", nc: nc, state: connConnected}\n\t\t\t\t\t\t\tlistener := newTestCancellationListener(false)\n\t\t\t\t\t\t\tconn.cancellationListener = listener\n\n\t\t\t\t\t\t\tctx, cancel := context.WithCancel(context.Background())\n\t\t\t\t\t\t\tvar err error\n\n\t\t\t\t\t\t\tvar wg sync.WaitGroup\n\t\t\t\t\t\t\twg.Add(1)\n\t\t\t\t\t\t\tgo func() {\n\t\t\t\t\t\t\t\tdefer wg.Done()\n\t\t\t\t\t\t\t\t_, err = conn.readWireMessage(ctx)\n\t\t\t\t\t\t\t}()\n\n\t\t\t\t\t\t\t<-nc.operationStartedChan\n\t\t\t\t\t\t\tcancel()\n\t\t\t\t\t\t\tnc.continueChan <- struct{}{}\n\n\t\t\t\t\t\t\twg.Wait()\n\t\t\t\t\t\t\twant := ConnectionError{ConnectionID: conn.id, Wrapped: context.Canceled, message: tc.errmsg}\n\t\t\t\t\t\t\tassert.Equal(t, want, err, \"expected error %v, got %v\", want, err)\n\t\t\t\t\t\t\tassert.Equal(t, connDisconnected, conn.state, \"expected connection state %v, got %v\", connDisconnected,\n\t\t\t\t\t\t\t\tconn.state)\n\t\t\t\t\t\t})\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t\tt.Run(\"closes connection if context is cancelled even if the socket read succeeds\", func(t *testing.T) {\n\t\t\t\t\ttnc := &testNetConn{buf: []byte{0x0A, 0x00, 0x00, 0x00, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A}}\n\t\t\t\t\tconn := &connection{id: \"foobar\", nc: tnc, state: connConnected}\n\t\t\t\t\tlistener := newTestCancellationListener(true)\n\t\t\t\t\tconn.cancellationListener = listener\n\n\t\t\t\t\twant := ConnectionError{ConnectionID: conn.id, Wrapped: context.Canceled, message: \"unable to read server response\"}\n\t\t\t\t\t_, err := conn.readWireMessage(context.Background())\n\t\t\t\t\tassert.Equal(t, want, err, \"expected error %v, got %v\", want, err)\n\t\t\t\t\tassert.Equal(t, connDisconnected, conn.state, \"expected connection state %v, got %v\", connDisconnected,\n\t\t\t\t\t\tconn.state)\n\t\t\t\t})\n\t\t\t})\n\t\t})\n\t\tt.Run(\"close\", func(t *testing.T) {\n\t\t\tt.Run(\"can close a connection that failed handshaking\", func(t *testing.T) {\n\t\t\t\tconn := newConnection(address.Address(\"\"),\n\t\t\t\t\tWithHandshaker(func(Handshaker) Handshaker {\n\t\t\t\t\t\treturn &testHandshaker{\n\t\t\t\t\t\t\tfinishHandshake: func(context.Context, *mnet.Connection) error {\n\t\t\t\t\t\t\t\treturn errors.New(\"handshake err\")\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}\n\t\t\t\t\t}),\n\t\t\t\t\tWithDialer(func(Dialer) Dialer {\n\t\t\t\t\t\treturn DialerFunc(func(context.Context, string, string) (net.Conn, error) {\n\t\t\t\t\t\t\treturn &net.TCPConn{}, nil\n\t\t\t\t\t\t})\n\t\t\t\t\t}),\n\t\t\t\t)\n\n\t\t\t\terr := conn.connect(context.Background())\n\t\t\t\tassert.NotNil(t, err, \"expected handshake error from connect, got nil\")\n\t\t\t\tconnState := atomic.LoadInt64(&conn.state)\n\t\t\t\tassert.Equal(t, connDisconnected, connState, \"expected connection state %v, got %v\", connDisconnected, connState)\n\n\t\t\t\terr = conn.close()\n\t\t\t\tassert.Nil(t, err, \"close error: %v\", err)\n\t\t\t})\n\t\t})\n\t\tt.Run(\"cancellation listener callback\", func(t *testing.T) {\n\t\t\tt.Run(\"closes connection\", func(t *testing.T) {\n\t\t\t\ttnc := &testNetConn{}\n\t\t\t\tconn := &connection{state: connConnected, nc: tnc}\n\n\t\t\t\tconn.cancellationListenerCallback()\n\t\t\t\tassert.True(t, conn.state == connDisconnected, \"expected connection state %v, got %v\", connDisconnected,\n\t\t\t\t\tconn.state)\n\t\t\t\tassert.True(t, tnc.closed, \"expected net.Conn to be closed but was not\")\n\t\t\t})\n\t\t})\n\t})\n\tt.Run(\"Connection\", func(t *testing.T) {\n\t\tt.Run(\"nil connection does not panic\", func(t *testing.T) {\n\t\t\tconn := &Connection{}\n\t\t\tdefer func() {\n\t\t\t\tif r := recover(); r != nil {\n\t\t\t\t\tt.Fatalf(\"Methods on a Connection with a nil *connection should not panic, but panicked with %v\", r)\n\t\t\t\t}\n\t\t\t}()\n\n\t\t\tvar want, got any\n\n\t\t\twant = ErrConnectionClosed\n\t\t\tgot = conn.Write(context.Background(), nil)\n\t\t\tif !cmp.Equal(got, want, cmp.Comparer(compareErrors)) {\n\t\t\t\tt.Errorf(\"errors do not match. got %v; want %v\", got, want)\n\t\t\t}\n\t\t\t_, got = conn.Read(context.Background())\n\t\t\tif !cmp.Equal(got, want, cmp.Comparer(compareErrors)) {\n\t\t\t\tt.Errorf(\"errors do not match. got %v; want %v\", got, want)\n\t\t\t}\n\n\t\t\twant = description.Server{}\n\t\t\tgot = conn.Description()\n\t\t\tif !cmp.Equal(got, want) {\n\t\t\t\tt.Errorf(\"descriptions do not match. got %v; want %v\", got, want)\n\t\t\t}\n\n\t\t\twant = nil\n\t\t\tgot = conn.Close()\n\t\t\tif !cmp.Equal(got, want, cmp.Comparer(compareErrors)) {\n\t\t\t\tt.Errorf(\"errors do not match. got %v; want %v\", got, want)\n\t\t\t}\n\n\t\t\tgot = conn.Expire()\n\t\t\tif !cmp.Equal(got, want, cmp.Comparer(compareErrors)) {\n\t\t\t\tt.Errorf(\"errors do not match. got %v; want %v\", got, want)\n\t\t\t}\n\n\t\t\twant = false\n\t\t\tgot = conn.Alive()\n\t\t\tif !cmp.Equal(got, want) {\n\t\t\t\tt.Errorf(\"Alive does not match. got %v; want %v\", got, want)\n\t\t\t}\n\n\t\t\twant = \"<closed>\"\n\t\t\tgot = conn.ID()\n\t\t\tif !cmp.Equal(got, want) {\n\t\t\t\tt.Errorf(\"IDs do not match. got %v; want %v\", got, want)\n\t\t\t}\n\n\t\t\twant = address.Address(\"0.0.0.0\")\n\t\t\tgot = conn.Address()\n\t\t\tif !cmp.Equal(got, want) {\n\t\t\t\tt.Errorf(\"Addresses do not match. got %v; want %v\", got, want)\n\t\t\t}\n\n\t\t\twant = address.Address(\"0.0.0.0\")\n\t\t\tgot = conn.LocalAddress()\n\t\t\tif !cmp.Equal(got, want) {\n\t\t\t\tt.Errorf(\"LocalAddresses do not match. got %v; want %v\", got, want)\n\t\t\t}\n\n\t\t\twant = (*int64)(nil)\n\t\t\tgot = conn.ServerConnectionID()\n\t\t\tif !cmp.Equal(got, want) {\n\t\t\t\tt.Errorf(\"ServerConnectionIDs do not match. got %v; want %v\", got, want)\n\t\t\t}\n\t\t})\n\n\t\tt.Run(\"pinning\", func(t *testing.T) {\n\t\t\tmakeMultipleConnections := func(t *testing.T, numConns int) (*pool, []*Connection, func()) {\n\t\t\t\tt.Helper()\n\n\t\t\t\taddr := bootstrapConnections(t, numConns, func(net.Conn) {})\n\t\t\t\tpool := newPool(poolConfig{\n\t\t\t\t\tAddress:        address.Address(addr.String()),\n\t\t\t\t\tConnectTimeout: defaultConnectionTimeout,\n\t\t\t\t})\n\t\t\t\terr := pool.ready()\n\t\t\t\tassert.Nil(t, err, \"pool.connect() error: %v\", err)\n\n\t\t\t\tconns := make([]*Connection, 0, numConns)\n\t\t\t\tfor i := 0; i < numConns; i++ {\n\t\t\t\t\tconn, err := pool.checkOut(context.Background())\n\t\t\t\t\tassert.Nil(t, err, \"checkOut error: %v\", err)\n\t\t\t\t\tconns = append(conns, &Connection{connection: conn})\n\t\t\t\t}\n\t\t\t\tdisconnect := func() {\n\t\t\t\t\tpool.close(context.Background())\n\t\t\t\t}\n\t\t\t\treturn pool, conns, disconnect\n\t\t\t}\n\t\t\tmakeOneConnection := func(t *testing.T) (*pool, *Connection, func()) {\n\t\t\t\tt.Helper()\n\n\t\t\t\tpool, conns, disconnect := makeMultipleConnections(t, 1)\n\t\t\t\treturn pool, conns[0], disconnect\n\t\t\t}\n\n\t\t\tassertPoolPinnedStats := func(t *testing.T, p *pool, cursorConns, txnConns uint64) {\n\t\t\t\tt.Helper()\n\n\t\t\t\tassert.Equal(t, cursorConns, p.pinnedCursorConnections, \"expected %d connections to be pinned to cursors, got %d\",\n\t\t\t\t\tcursorConns, p.pinnedCursorConnections)\n\t\t\t\tassert.Equal(t, txnConns, p.pinnedTransactionConnections, \"expected %d connections to be pinned to transactions, got %d\",\n\t\t\t\t\ttxnConns, p.pinnedTransactionConnections)\n\t\t\t}\n\n\t\t\tt.Run(\"cursors\", func(t *testing.T) {\n\t\t\t\tpool, conn, disconnect := makeOneConnection(t)\n\t\t\t\tdefer disconnect()\n\n\t\t\t\terr := conn.PinToCursor()\n\t\t\t\tassert.Nil(t, err, \"PinToCursor error: %v\", err)\n\t\t\t\tassertPoolPinnedStats(t, pool, 1, 0)\n\n\t\t\t\terr = conn.UnpinFromCursor()\n\t\t\t\tassert.Nil(t, err, \"UnpinFromCursor error: %v\", err)\n\n\t\t\t\terr = conn.Close()\n\t\t\t\tassert.Nil(t, err, \"Close error: %v\", err)\n\t\t\t\tassertPoolPinnedStats(t, pool, 0, 0)\n\t\t\t})\n\t\t\tt.Run(\"transactions\", func(t *testing.T) {\n\t\t\t\tpool, conn, disconnect := makeOneConnection(t)\n\t\t\t\tdefer disconnect()\n\n\t\t\t\terr := conn.PinToTransaction()\n\t\t\t\tassert.Nil(t, err, \"PinToTransaction error: %v\", err)\n\t\t\t\tassertPoolPinnedStats(t, pool, 0, 1)\n\n\t\t\t\terr = conn.UnpinFromTransaction()\n\t\t\t\tassert.Nil(t, err, \"UnpinFromTransaction error: %v\", err)\n\n\t\t\t\terr = conn.Close()\n\t\t\t\tassert.Nil(t, err, \"Close error: %v\", err)\n\t\t\t\tassertPoolPinnedStats(t, pool, 0, 0)\n\t\t\t})\n\t\t\tt.Run(\"pool is only updated for first reference\", func(t *testing.T) {\n\t\t\t\tpool, conn, disconnect := makeOneConnection(t)\n\t\t\t\tdefer disconnect()\n\n\t\t\t\terr := conn.PinToTransaction()\n\t\t\t\tassert.Nil(t, err, \"PinToTransaction error: %v\", err)\n\t\t\t\tassertPoolPinnedStats(t, pool, 0, 1)\n\n\t\t\t\terr = conn.PinToCursor()\n\t\t\t\tassert.Nil(t, err, \"PinToCursor error: %v\", err)\n\t\t\t\tassertPoolPinnedStats(t, pool, 0, 1)\n\n\t\t\t\terr = conn.UnpinFromCursor()\n\t\t\t\tassert.Nil(t, err, \"UnpinFromCursor error: %v\", err)\n\t\t\t\tassertPoolPinnedStats(t, pool, 0, 1)\n\n\t\t\t\terr = conn.UnpinFromTransaction()\n\t\t\t\tassert.Nil(t, err, \"UnpinFromTransaction error: %v\", err)\n\t\t\t\tassertPoolPinnedStats(t, pool, 0, 1)\n\n\t\t\t\terr = conn.Close()\n\t\t\t\tassert.Nil(t, err, \"Close error: %v\", err)\n\t\t\t\tassertPoolPinnedStats(t, pool, 0, 0)\n\t\t\t})\n\t\t\tt.Run(\"multiple connections from a pool\", func(t *testing.T) {\n\t\t\t\tpool, conns, disconnect := makeMultipleConnections(t, 2)\n\t\t\t\tdefer disconnect()\n\n\t\t\t\tfirst, second := conns[0], conns[1]\n\n\t\t\t\terr := first.PinToTransaction()\n\t\t\t\tassert.Nil(t, err, \"PinToTransaction error: %v\", err)\n\t\t\t\terr = second.PinToCursor()\n\t\t\t\tassert.Nil(t, err, \"PinToCursor error: %v\", err)\n\t\t\t\tassertPoolPinnedStats(t, pool, 1, 1)\n\n\t\t\t\terr = first.UnpinFromTransaction()\n\t\t\t\tassert.Nil(t, err, \"UnpinFromTransaction error: %v\", err)\n\t\t\t\terr = first.Close()\n\t\t\t\tassert.Nil(t, err, \"Close error: %v\", err)\n\t\t\t\tassertPoolPinnedStats(t, pool, 1, 0)\n\n\t\t\t\terr = second.UnpinFromCursor()\n\t\t\t\tassert.Nil(t, err, \"UnpinFromCursor error: %v\", err)\n\t\t\t\terr = second.Close()\n\t\t\t\tassert.Nil(t, err, \"Close error: %v\", err)\n\t\t\t\tassertPoolPinnedStats(t, pool, 0, 0)\n\t\t\t})\n\t\t\tt.Run(\"close is ignored if connection is pinned\", func(t *testing.T) {\n\t\t\t\tpool, conn, disconnect := makeOneConnection(t)\n\t\t\t\tdefer disconnect()\n\n\t\t\t\terr := conn.PinToCursor()\n\t\t\t\tassert.Nil(t, err, \"PinToCursor error: %v\", err)\n\n\t\t\t\terr = conn.Close()\n\t\t\t\tassert.Nil(t, err, \"Close error\")\n\t\t\t\tassert.NotNil(t, conn.connection, \"expected connection to be pinned but it was released to the pool\")\n\t\t\t\tassertPoolPinnedStats(t, pool, 1, 0)\n\t\t\t})\n\t\t\tt.Run(\"expire forcefully returns connection to pool\", func(t *testing.T) {\n\t\t\t\tpool, conn, disconnect := makeOneConnection(t)\n\t\t\t\tdefer disconnect()\n\n\t\t\t\terr := conn.PinToCursor()\n\t\t\t\tassert.Nil(t, err, \"PinToCursor error: %v\", err)\n\n\t\t\t\terr = conn.Expire()\n\t\t\t\tassert.Nil(t, err, \"Expire error\")\n\t\t\t\tassert.Nil(t, conn.connection, \"expected connection to be released to the pool but was not\")\n\t\t\t\tassertPoolPinnedStats(t, pool, 0, 0)\n\t\t\t})\n\t\t})\n\t})\n}\n\nfunc BenchmarkConnection(b *testing.B) {\n\tb.Run(\"CompressWireMessage CompressorNoOp\", func(b *testing.B) {\n\t\tbuf := make([]byte, 256)\n\t\t_, err := rand.Read(buf)\n\t\tif err != nil {\n\t\t\tb.Log(err)\n\t\t\tb.FailNow()\n\t\t}\n\t\tconn := Connection{connection: &connection{compressor: wiremessage.CompressorNoOp}}\n\t\tfor i := 0; i < b.N; i++ {\n\t\t\t_, err := conn.CompressWireMessage(buf, nil)\n\t\t\tif err != nil {\n\t\t\t\tb.Error(err)\n\t\t\t}\n\t\t}\n\t})\n}\n\n// cancellationTestNetConn is a net.Conn implementation that is used to test context.Cancellation during an in-progress\n// network read or write. This type has two unbuffered channels: operationStartedChan and continueChan. When Read/Write\n// starts, the type will write to operationStartedChan, which will block until the test reads from it. This signals to\n// the test that the connection has entered the net.Conn read/write. After that unblocks, the type will then read from\n// continueChan, which blocks until the test writes to it. This allows the test to perform operations with the guarantee\n// that they will complete before the read/write functions exit. Sample usage:\n//\n// nc := newCancellationWriteConn(&testNetConn{}, 0)\n// conn := &connection{nc}\n// go func() { _ = conn.writeWireMessage(ctx, []byte{\"hello world\"})}()\n// <-nc.operationStartedChan\n// log.Println(\"This print will happen inside net.Conn.Write\")\n// nc.continueChan <- struct{}{}\n//\n// By default, the read/write methods will error after they can read from continueChan to simulate a connection being\n// closed after context cancellation. This type also supports skipping to allow a number of successful read/write calls\n// before one fails.\ntype cancellationTestNetConn struct {\n\tnet.Conn\n\n\tshouldSkip           int\n\tskipCount            int\n\treadBuf              []byte\n\toperationStartedChan chan struct{}\n\tcontinueChan         chan struct{}\n}\n\n// create a cancellationTestNetConn to test cancelling net.Conn.Write().\n// skip specifies the number of writes that should succeed. Successful writes will return len(writeBuffer), nil.\nfunc newCancellationWriteConn(nc net.Conn, skip int) *cancellationTestNetConn {\n\treturn &cancellationTestNetConn{\n\t\tConn:                 nc,\n\t\tshouldSkip:           skip,\n\t\toperationStartedChan: make(chan struct{}),\n\t\tcontinueChan:         make(chan struct{}),\n\t}\n}\n\n// create a cancellationTestNetConn to test cancelling net.Conn.Read().\n// skip specifies the number of reads that should succeed. Successful reads will copy the contents of readBuf into the\n// buffer provided to Read and will return len(readBuf), nil.\nfunc newCancellationReadConn(nc net.Conn, skip int, readBuf []byte) *cancellationTestNetConn {\n\treturn &cancellationTestNetConn{\n\t\tConn:                 nc,\n\t\tshouldSkip:           skip,\n\t\treadBuf:              readBuf,\n\t\toperationStartedChan: make(chan struct{}),\n\t\tcontinueChan:         make(chan struct{}),\n\t}\n}\n\nfunc (c *cancellationTestNetConn) Read(b []byte) (int, error) {\n\tif c.skipCount < c.shouldSkip {\n\t\tc.skipCount++\n\t\tcopy(b, c.readBuf)\n\t\treturn len(c.readBuf), nil\n\t}\n\n\tc.operationStartedChan <- struct{}{}\n\t<-c.continueChan\n\treturn 0, errors.New(\"cancelled read\")\n}\n\nfunc (c *cancellationTestNetConn) Write(b []byte) (n int, err error) {\n\tif c.skipCount < c.shouldSkip {\n\t\tc.skipCount++\n\t\treturn len(b), nil\n\t}\n\n\tc.operationStartedChan <- struct{}{}\n\t<-c.continueChan\n\treturn 0, errors.New(\"cancelled write\")\n}\n\ntype testNetConn struct {\n\tnc  net.Conn\n\tbuf []byte\n\n\tdeadlineerr error\n\twriteerr    error\n\treaderr     error\n\tclosed      bool\n\n\tdeadline      time.Time\n\treadDeadline  time.Time\n\twriteDeadline time.Time\n}\n\nfunc (tnc *testNetConn) Read(b []byte) (n int, err error) {\n\tif len(tnc.buf) > 0 {\n\t\tn := copy(b, tnc.buf)\n\t\ttnc.buf = tnc.buf[n:]\n\t\treturn n, nil\n\t}\n\tif tnc.readerr != nil {\n\t\treturn 0, tnc.readerr\n\t}\n\tif tnc.nc == nil {\n\t\treturn 0, nil\n\t}\n\treturn tnc.nc.Read(b)\n}\n\nfunc (tnc *testNetConn) Write(b []byte) (n int, err error) {\n\tif tnc.writeerr != nil {\n\t\treturn 0, tnc.writeerr\n\t}\n\tif tnc.nc == nil {\n\t\tidx := len(tnc.buf)\n\t\ttnc.buf = append(tnc.buf, make([]byte, len(b))...)\n\t\tcopy(tnc.buf[idx:], b)\n\t\treturn len(b), nil\n\t}\n\treturn tnc.nc.Write(b)\n}\n\nfunc (tnc *testNetConn) Close() error {\n\ttnc.closed = true\n\tif tnc.nc == nil {\n\t\treturn nil\n\t}\n\treturn tnc.nc.Close()\n}\n\nfunc (tnc *testNetConn) LocalAddr() net.Addr {\n\tif tnc.nc == nil {\n\t\treturn nil\n\t}\n\treturn tnc.nc.LocalAddr()\n}\n\nfunc (tnc *testNetConn) RemoteAddr() net.Addr {\n\tif tnc.nc == nil {\n\t\treturn nil\n\t}\n\treturn tnc.nc.RemoteAddr()\n}\n\nfunc (tnc *testNetConn) SetDeadline(t time.Time) error {\n\ttnc.deadline = t\n\tif tnc.deadlineerr != nil {\n\t\treturn tnc.deadlineerr\n\t}\n\tif tnc.nc == nil {\n\t\treturn nil\n\t}\n\treturn tnc.nc.SetDeadline(t)\n}\n\nfunc (tnc *testNetConn) SetReadDeadline(t time.Time) error {\n\ttnc.readDeadline = t\n\tif tnc.deadlineerr != nil {\n\t\treturn tnc.deadlineerr\n\t}\n\tif tnc.nc == nil {\n\t\treturn nil\n\t}\n\treturn tnc.nc.SetReadDeadline(t)\n}\n\nfunc (tnc *testNetConn) SetWriteDeadline(t time.Time) error {\n\ttnc.writeDeadline = t\n\tif tnc.deadlineerr != nil {\n\t\treturn tnc.deadlineerr\n\t}\n\tif tnc.nc == nil {\n\t\treturn nil\n\t}\n\treturn tnc.nc.SetWriteDeadline(t)\n}\n\n// bootstrapConnection creates a listener that will listen for a single connection\n// on the return address. The user provided run function will be called with the accepted\n// connection. The user is responsible for closing the connection.\nfunc bootstrapConnections(t *testing.T, num int, run func(net.Conn)) net.Addr {\n\tl, err := net.Listen(\"tcp\", \"localhost:0\")\n\tif err != nil {\n\t\tt.Errorf(\"Could not set up a listener: %v\", err)\n\t\tt.FailNow()\n\t}\n\tgo func() {\n\t\tfor i := 0; i < num; i++ {\n\t\t\tc, err := l.Accept()\n\t\t\tif err != nil {\n\t\t\t\tt.Errorf(\"Could not accept a connection: %v\", err)\n\t\t\t}\n\t\t\tgo run(c)\n\t\t}\n\t\t_ = l.Close()\n\t}()\n\treturn l.Addr()\n}\n\ntype netconn struct {\n\tnet.Conn\n\tclosed chan struct{}\n\td      *dialer\n}\n\nfunc (nc *netconn) Close() error {\n\tnc.closed <- struct{}{}\n\tnc.d.connclosed(nc)\n\treturn nc.Conn.Close()\n}\n\ntype writeFailConn struct {\n\tnet.Conn\n}\n\nfunc (wfc *writeFailConn) Write([]byte) (int, error) {\n\treturn 0, errors.New(\"Write error\")\n}\n\nfunc (wfc *writeFailConn) SetWriteDeadline(time.Time) error {\n\treturn nil\n}\n\ntype dialer struct {\n\tDialer\n\topened        map[*netconn]struct{}\n\tclosed        map[*netconn]struct{}\n\tcloseCallBack func()\n\tsync.Mutex\n}\n\nfunc newdialer(d Dialer) *dialer {\n\treturn &dialer{Dialer: d, opened: make(map[*netconn]struct{}), closed: make(map[*netconn]struct{})}\n}\n\nfunc (d *dialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) {\n\td.Lock()\n\tdefer d.Unlock()\n\tc, err := d.Dialer.DialContext(ctx, network, address)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tnc := &netconn{Conn: c, closed: make(chan struct{}, 1), d: d}\n\td.opened[nc] = struct{}{}\n\treturn nc, nil\n}\n\nfunc (d *dialer) connclosed(nc *netconn) {\n\td.Lock()\n\tdefer d.Unlock()\n\td.closed[nc] = struct{}{}\n\tif d.closeCallBack != nil {\n\t\td.closeCallBack()\n\t}\n}\n\nfunc (d *dialer) lenopened() int {\n\td.Lock()\n\tdefer d.Unlock()\n\treturn len(d.opened)\n}\n\nfunc (d *dialer) lenclosed() int {\n\td.Lock()\n\tdefer d.Unlock()\n\treturn len(d.closed)\n}\n\ntype testCancellationListener struct {\n\tlistener         *contextDoneListener\n\tnumListen        int\n\tnumStopListening int\n\taborted          bool\n}\n\n// This function creates a new testCancellationListener. The aborted parameter specifies the value that should be\n// returned by the StopListening method.\nfunc newTestCancellationListener(aborted bool) *testCancellationListener {\n\treturn &testCancellationListener{\n\t\tlistener: newContextDoneListener(),\n\t\taborted:  aborted,\n\t}\n}\n\nfunc (tcl *testCancellationListener) Listen(ctx context.Context, abortFn func()) {\n\ttcl.numListen++\n\ttcl.listener.Listen(ctx, abortFn)\n}\n\nfunc (tcl *testCancellationListener) StopListening() bool {\n\ttcl.numStopListening++\n\ttcl.listener.StopListening()\n\treturn tcl.aborted\n}\n\nfunc (tcl *testCancellationListener) assertCalledOnce(t *testing.T) {\n\tassert.Equal(t, 1, tcl.numListen, \"expected Listen to be called once, got %d\", tcl.numListen)\n\tassert.Equal(t, 1, tcl.numStopListening, \"expected StopListening to be called once, got %d\", tcl.numListen)\n}\n\ntype testContext struct {\n\tcontext.Context\n\tdeadline time.Time\n}\n\nfunc (tc *testContext) Deadline() (time.Time, bool) {\n\treturn tc.deadline, false\n}\n\nfunc TestConnectionError(t *testing.T) {\n\tt.Parallel()\n\n\tt.Run(\"EOF\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\taddr := bootstrapConnections(t, 1, func(nc net.Conn) {\n\t\t\t_ = nc.Close()\n\t\t})\n\n\t\tp := newPool(\n\t\t\tpoolConfig{Address: address.Address(addr.String())},\n\t\t)\n\t\tdefer p.close(context.Background())\n\t\terr := p.ready()\n\t\trequire.NoError(t, err, \"unexpected close error\")\n\n\t\tconn, err := p.checkOut(context.Background())\n\t\trequire.NoError(t, err, \"unexpected checkOut error\")\n\n\t\t_, err = conn.readWireMessage(context.Background())\n\t\tassert.ErrorContains(t, err, \"connection closed unexpectedly by the other side: EOF\")\n\t})\n\tt.Run(\"timeout\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\ttimeout := 10 * time.Millisecond\n\n\t\taddr := bootstrapConnections(t, 1, func(nc net.Conn) {\n\t\t\ttime.Sleep(timeout * 2)\n\t\t\t_ = nc.Close()\n\t\t})\n\n\t\tp := newPool(\n\t\t\tpoolConfig{Address: address.Address(addr.String())},\n\t\t)\n\t\tdefer p.close(context.Background())\n\t\terr := p.ready()\n\t\trequire.NoError(t, err)\n\n\t\tconn, err := p.checkOut(context.Background())\n\t\trequire.NoError(t, err)\n\n\t\tctx := &testContext{\n\t\t\tContext:  context.Background(),\n\t\t\tdeadline: time.Now().Add(timeout),\n\t\t}\n\t\t_, err = conn.readWireMessage(ctx)\n\t\tassert.ErrorContains(t, err, \"client timed out waiting for server response\")\n\t})\n}\n"
  },
  {
    "path": "x/mongo/driver/topology/context_listener.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage topology\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"sync/atomic\"\n)\n\ntype contextListener interface {\n\tListen(context.Context, func())\n\tStopListening() bool\n}\n\n// contextDoneListener listens for context-ending eventsand notifies listeners\n// via a callback function.\ntype contextDoneListener struct {\n\taborted     atomic.Value\n\tdone        chan struct{}\n\tblockOnDone bool\n}\n\nvar _ contextListener = &contextDoneListener{}\n\n// newContextDoneListener constructs a contextDoneListener that will block\n// when a context is done until StopListening is called.\nfunc newContextDoneListener() *contextDoneListener {\n\treturn &contextDoneListener{\n\t\tdone:        make(chan struct{}),\n\t\tblockOnDone: true,\n\t}\n}\n\n// newNonBlockingContextDoneLIstener constructs a contextDoneListener that\n// will not block when a context is done. In this case there are two ways to\n// unblock the listener: a finished context or a call to StopListening.\nfunc newNonBlockingContextDoneListener() *contextDoneListener {\n\treturn &contextDoneListener{\n\t\tdone:        make(chan struct{}),\n\t\tblockOnDone: false,\n\t}\n}\n\n// Listen blocks until the provided context is cancelled or listening is aborted\n// via the StopListening function. If this detects that the context has been\n// cancelled (i.e. errors.Is(ctx.Err(), context.Canceled), the provided callback\n// is called to abort in-progress work. If blockOnDone is true, this function\n// will block until StopListening is called, even if the context expires.\nfunc (c *contextDoneListener) Listen(ctx context.Context, abortFn func()) {\n\tc.aborted.Store(false)\n\n\tselect {\n\tcase <-ctx.Done():\n\t\tif errors.Is(ctx.Err(), context.Canceled) {\n\t\t\tc.aborted.Store(true)\n\n\t\t\tabortFn()\n\t\t}\n\n\t\tif c.blockOnDone {\n\t\t\t<-c.done\n\t\t}\n\tcase <-c.done:\n\t}\n}\n\n// StopListening stops the in-progress Listen call. If blockOnDone is true, then\n// this blocks if there is no in-progress Listen call. This function will return\n// true if the provided abort callback was called when listening for\n// cancellation on the previous context.\nfunc (c *contextDoneListener) StopListening() bool {\n\tif c.blockOnDone {\n\t\tc.done <- struct{}{}\n\t} else {\n\t\tselect {\n\t\tcase c.done <- struct{}{}:\n\t\tdefault:\n\t\t}\n\t}\n\n\tif aborted := c.aborted.Load(); aborted != nil {\n\t\treturn aborted.(bool)\n\t}\n\n\treturn false\n}\n"
  },
  {
    "path": "x/mongo/driver/topology/diff.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage topology\n\nimport \"go.mongodb.org/mongo-driver/v2/x/mongo/driver/description\"\n\n// hostlistDiff is the difference between a topology and a host list.\ntype hostlistDiff struct {\n\tAdded   []string\n\tRemoved []string\n}\n\n// diffHostList compares the topology description and host list and returns the difference.\nfunc diffHostList(t description.Topology, hostlist []string) hostlistDiff {\n\tvar diff hostlistDiff\n\n\toldServers := make(map[string]bool)\n\tfor _, s := range t.Servers {\n\t\toldServers[s.Addr.String()] = true\n\t}\n\n\tfor _, addr := range hostlist {\n\t\tif oldServers[addr] {\n\t\t\tdelete(oldServers, addr)\n\t\t} else {\n\t\t\tdiff.Added = append(diff.Added, addr)\n\t\t}\n\t}\n\n\tfor addr := range oldServers {\n\t\tdiff.Removed = append(diff.Removed, addr)\n\t}\n\n\treturn diff\n}\n\n// topologyDiff is the difference between two different topology descriptions.\ntype topologyDiff struct {\n\tAdded   []description.Server\n\tRemoved []description.Server\n}\n\n// diffTopology compares the two topology descriptions and returns the difference.\nfunc diffTopology(oldtopo, newtopo description.Topology) topologyDiff {\n\tvar diff topologyDiff\n\n\toldServers := make(map[string]bool)\n\tfor _, s := range oldtopo.Servers {\n\t\toldServers[s.Addr.String()] = true\n\t}\n\n\tfor _, s := range newtopo.Servers {\n\t\taddr := s.Addr.String()\n\t\tif oldServers[addr] {\n\t\t\tdelete(oldServers, addr)\n\t\t} else {\n\t\t\tdiff.Added = append(diff.Added, s)\n\t\t}\n\t}\n\n\tfor _, s := range oldtopo.Servers {\n\t\taddr := s.Addr.String()\n\t\tif oldServers[addr] {\n\t\t\tdiff.Removed = append(diff.Removed, s)\n\t\t}\n\t}\n\n\treturn diff\n}\n"
  },
  {
    "path": "x/mongo/driver/topology/diff_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage topology\n\nimport (\n\t\"testing\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/description\"\n)\n\nfunc TestDiffHostList(t *testing.T) {\n\th1 := \"1.0.0.0:27017\"\n\th2 := \"2.0.0.0:27017\"\n\th3 := \"3.0.0.0:27017\"\n\th4 := \"4.0.0.0:27017\"\n\th5 := \"5.0.0.0:27017\"\n\th6 := \"6.0.0.0:27017\"\n\ts1 := description.Server{Addr: \"1.0.0.0:27017\"}\n\ts2 := description.Server{Addr: \"2.0.0.0:27017\"}\n\ts3 := description.Server{Addr: \"3.0.0.0:27017\"}\n\ts6 := description.Server{Addr: \"6.0.0.0:27017\"}\n\n\ttopo := description.Topology{\n\t\tServers: []description.Server{s6, s1, s3, s2},\n\t}\n\thostlist := []string{h2, h4, h3, h5}\n\n\tdiff := diffHostList(topo, hostlist)\n\n\tassert.ElementsMatch(t, []string{h4, h5}, diff.Added)\n\tassert.ElementsMatch(t, []string{h1, h6}, diff.Removed)\n\n\t// Ensure that original topology servers and hostlist were not reordered.\n\tassert.EqualValues(t, []description.Server{s6, s1, s3, s2}, topo.Servers)\n\tassert.EqualValues(t, []string{h2, h4, h3, h5}, hostlist)\n}\n\nfunc TestDiffTopology(t *testing.T) {\n\ts1 := description.Server{Addr: \"1.0.0.0:27017\"}\n\ts2 := description.Server{Addr: \"2.0.0.0:27017\"}\n\ts3 := description.Server{Addr: \"3.0.0.0:27017\"}\n\ts4 := description.Server{Addr: \"4.0.0.0:27017\"}\n\ts5 := description.Server{Addr: \"5.0.0.0:27017\"}\n\ts6 := description.Server{Addr: \"6.0.0.0:27017\"}\n\n\tt1 := description.Topology{\n\t\tServers: []description.Server{s6, s1, s3, s2},\n\t}\n\tt2 := description.Topology{\n\t\tServers: []description.Server{s2, s4, s3, s5},\n\t}\n\n\tdiff := diffTopology(t1, t2)\n\n\tassert.ElementsMatch(t, []description.Server{s4, s5}, diff.Added)\n\tassert.ElementsMatch(t, []description.Server{s1, s6}, diff.Removed)\n\n\t// Ensure that original topology servers were not reordered.\n\tassert.EqualValues(t, []description.Server{s6, s1, s3, s2}, t1.Servers)\n\tassert.EqualValues(t, []description.Server{s2, s4, s3, s5}, t2.Servers)\n}\n"
  },
  {
    "path": "x/mongo/driver/topology/errors.go",
    "content": "// Copyright (C) MongoDB, Inc. 2022-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage topology\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"os\"\n\t\"strings\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/description\"\n)\n\nvar _ error = ConnectionError{}\n\n// ConnectionError represents a connection error.\ntype ConnectionError struct {\n\tConnectionID string\n\tWrapped      error\n\n\t// init will be set to true if this error occurred during connection initialization or\n\t// during a connection handshake.\n\tinit    bool\n\tmessage string\n}\n\n// Error implements the error interface.\nfunc (e ConnectionError) Error() string {\n\tvar messages []string\n\tif e.init {\n\t\tmessages = append(messages, \"error occurred during connection handshake\")\n\t}\n\tif e.message != \"\" {\n\t\tmessages = append(messages, e.message)\n\t}\n\tif e.Wrapped != nil {\n\t\tif errors.Is(e.Wrapped, io.EOF) {\n\t\t\tmessages = append(messages, \"connection closed unexpectedly by the other side\")\n\t\t}\n\t\tif errors.Is(e.Wrapped, os.ErrDeadlineExceeded) {\n\t\t\tmessages = append(messages, \"client timed out waiting for server response\")\n\t\t} else if err, ok := e.Wrapped.(net.Error); ok && err.Timeout() {\n\t\t\tmessages = append(messages, \"client timed out waiting for server response\")\n\t\t}\n\t\tmessages = append(messages, e.Wrapped.Error())\n\t}\n\tif len(messages) > 0 {\n\t\treturn fmt.Sprintf(\"connection(%s) %s\", e.ConnectionID, strings.Join(messages, \": \"))\n\t}\n\treturn fmt.Sprintf(\"connection(%s)\", e.ConnectionID)\n}\n\n// Unwrap returns the underlying error.\nfunc (e ConnectionError) Unwrap() error {\n\treturn e.Wrapped\n}\n\n// ServerSelectionError represents a Server Selection error.\ntype ServerSelectionError struct {\n\tDesc    description.Topology\n\tWrapped error\n}\n\n// Error implements the error interface.\nfunc (e ServerSelectionError) Error() string {\n\tif e.Wrapped != nil {\n\t\treturn fmt.Sprintf(\"server selection error: %s, current topology: { %s }\", e.Wrapped.Error(), e.Desc.String())\n\t}\n\treturn fmt.Sprintf(\"server selection error: current topology: { %s }\", e.Desc.String())\n}\n\n// Unwrap returns the underlying error.\nfunc (e ServerSelectionError) Unwrap() error {\n\treturn e.Wrapped\n}\n\n// WaitQueueTimeoutError represents a timeout when requesting a connection from the pool\ntype WaitQueueTimeoutError struct {\n\tWrapped              error\n\tpinnedConnections    *pinnedConnections\n\tmaxPoolSize          uint64\n\ttotalConnections     int\n\tavailableConnections int\n\twaitDuration         time.Duration\n}\n\ntype pinnedConnections struct {\n\tcursorConnections      uint64\n\ttransactionConnections uint64\n}\n\n// Error implements the error interface.\nfunc (w WaitQueueTimeoutError) Error() string {\n\terrorMsg := \"timed out while checking out a connection from connection pool\"\n\tswitch {\n\tcase w.Wrapped == nil:\n\tcase errors.Is(w.Wrapped, context.Canceled):\n\t\terrorMsg = fmt.Sprintf(\n\t\t\t\"%s: %s\",\n\t\t\t\"canceled while checking out a connection from connection pool\",\n\t\t\tw.Wrapped.Error(),\n\t\t)\n\tdefault:\n\t\terrorMsg = fmt.Sprintf(\n\t\t\t\"%s: %s\",\n\t\t\terrorMsg,\n\t\t\tw.Wrapped.Error(),\n\t\t)\n\t}\n\n\tmsg := fmt.Sprintf(\"%s; total connections: %d, maxPoolSize: %d, \", errorMsg, w.totalConnections, w.maxPoolSize)\n\tif pinnedConnections := w.pinnedConnections; pinnedConnections != nil {\n\t\topenConnectionCount := uint64(w.totalConnections) -\n\t\t\tpinnedConnections.cursorConnections -\n\t\t\tpinnedConnections.transactionConnections\n\t\tmsg += fmt.Sprintf(\"connections in use by cursors: %d, connections in use by transactions: %d, connections in use by other operations: %d, \",\n\t\t\tpinnedConnections.cursorConnections,\n\t\t\tpinnedConnections.transactionConnections,\n\t\t\topenConnectionCount,\n\t\t)\n\t}\n\tmsg += fmt.Sprintf(\"idle connections: %d, wait duration: %s\", w.availableConnections, w.waitDuration.String())\n\treturn msg\n}\n\n// Unwrap returns the underlying error.\nfunc (w WaitQueueTimeoutError) Unwrap() error {\n\treturn w.Wrapped\n}\n"
  },
  {
    "path": "x/mongo/driver/topology/example_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage topology_test\n\nimport (\n\t\"log\"\n\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/topology\"\n)\n\nfunc Example_clusterMonitoring() {\n\ttopo, err := topology.New(nil)\n\tif err != nil {\n\t\tlog.Fatalf(\"could not create topology: %v\", err)\n\t}\n\terr = topo.Connect()\n\tif err != nil {\n\t\tlog.Fatalf(\"could not create topology: %v\", err)\n\t}\n\n\tsub, err := topo.Subscribe()\n\tif err != nil {\n\t\tlog.Fatalf(\"could not subscribe to topology: %v\", err)\n\t}\n\n\tfor desc := range sub.Updates {\n\t\tlog.Printf(\"%#v\", desc)\n\t}\n}\n"
  },
  {
    "path": "x/mongo/driver/topology/fsm.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage topology\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"sync/atomic\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/driverutil\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/ptrutil\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/address\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/description\"\n)\n\nvar (\n\t// MinSupportedMongoDBVersion is the version string for the lowest MongoDB version supported by the driver.\n\tMinSupportedMongoDBVersion = \"4.2\"\n\n\t// SupportedWireVersions is the range of wire versions supported by the driver.\n\tSupportedWireVersions = driverutil.NewVersionRange(driverutil.MinWireVersion, driverutil.MaxWireVersion)\n)\n\ntype fsm struct {\n\tdescription.Topology\n\tmaxElectionID    bson.ObjectID\n\tmaxSetVersion    uint32\n\tcompatible       atomic.Value\n\tcompatibilityErr error\n}\n\nfunc newFSM() *fsm {\n\tf := fsm{}\n\tf.compatible.Store(true)\n\treturn &f\n}\n\n// isServerDataBearing returns true if the server is a data bearing server.\nfunc isServerDataBearing(srv description.Server) bool {\n\treturn srv.Kind == description.ServerKindRSPrimary ||\n\t\tsrv.Kind == description.ServerKindRSSecondary ||\n\t\tsrv.Kind == description.ServerKindMongos ||\n\t\tsrv.Kind == description.ServerKindStandalone\n}\n\n// selectFSMSessionTimeout selects the timeout to return for the topology's\n// finite state machine. If the logicalSessionTimeoutMinutes on the FSM exists\n// and the server is data-bearing, then we determine this value by returning\n//\n//\tmin{server timeout, FSM timeout}\n//\n// where a \"nil\" value is considered less than 0.\n//\n// Otherwise, if the FSM's logicalSessionTimeoutMinutes exist, then this\n// function returns the FSM timeout.\n//\n// In the case where the FSM timeout DNE, we check all servers to see if any\n// still do not have a timeout. This function chooses the lowest of the existing\n// timeouts.\nfunc selectFSMSessionTimeout(f *fsm, s description.Server) *int64 {\n\toldMinutes := f.SessionTimeoutMinutes\n\tcomp := ptrutil.CompareInt64(oldMinutes, s.SessionTimeoutMinutes)\n\n\t// If the server is data-bearing and the current timeout exists and is\n\t// either:\n\t//\n\t// 1. larger than the server timeout, or\n\t// 2. non-nil while the server timeout is nil\n\t//\n\t// then return the server timeout.\n\tif isServerDataBearing(s) && (comp == 1 || comp == 2) {\n\t\treturn s.SessionTimeoutMinutes\n\t}\n\n\t// If the current timeout exists and the server is not data-bearing OR\n\t// min{server timeout, current timeout} = current timeout, then return\n\t// the current timeout.\n\tif oldMinutes != nil {\n\t\treturn oldMinutes\n\t}\n\n\ttimeout := s.SessionTimeoutMinutes\n\tfor _, server := range f.Servers {\n\t\t// If the server is not data-bearing, then we do not consider\n\t\t// it's timeout whether set or not.\n\t\tif !isServerDataBearing(server) {\n\t\t\tcontinue\n\t\t}\n\n\t\tsrvTimeout := server.SessionTimeoutMinutes\n\t\tcomp := ptrutil.CompareInt64(timeout, srvTimeout)\n\n\t\tif comp <= 0 { // timeout <= srvTimout\n\t\t\tcontinue\n\t\t}\n\n\t\ttimeout = server.SessionTimeoutMinutes\n\t}\n\n\treturn timeout\n}\n\n// apply takes a new server description and modifies the FSM's topology description based on it. It returns the\n// updated topology description as well as a server description. The returned server description is either the same\n// one that was passed in, or a new one in the case that it had to be changed.\n//\n// apply should operation on immutable descriptions so we don't have to lock for the entire time we're applying the\n// server description.\nfunc (f *fsm) apply(s description.Server) (description.Topology, description.Server) {\n\tnewServers := make([]description.Server, len(f.Servers))\n\tcopy(newServers, f.Servers)\n\n\t// Reset the logicalSessionTimeoutMinutes to the minimum of the FSM\n\t// and the description.server/f.servers.\n\tserverTimeoutMinutes := selectFSMSessionTimeout(f, s)\n\n\tf.Topology = description.Topology{\n\t\tKind:    f.Kind,\n\t\tServers: newServers,\n\t\tSetName: f.SetName,\n\t}\n\n\tf.SessionTimeoutMinutes = serverTimeoutMinutes\n\n\tif _, ok := f.findServer(s.Addr); !ok {\n\t\treturn f.Topology, s\n\t}\n\n\tupdatedDesc := s\n\tswitch f.Kind {\n\tcase description.Unknown:\n\t\tupdatedDesc = f.applyToUnknown(s)\n\tcase description.TopologyKindSharded:\n\t\tupdatedDesc = f.applyToSharded(s)\n\tcase description.TopologyKindReplicaSetNoPrimary:\n\t\tupdatedDesc = f.applyToReplicaSetNoPrimary(s)\n\tcase description.TopologyKindReplicaSetWithPrimary:\n\t\tupdatedDesc = f.applyToReplicaSetWithPrimary(s)\n\tcase description.TopologyKindSingle:\n\t\tupdatedDesc = f.applyToSingle(s)\n\t}\n\n\tfor _, server := range f.Servers {\n\t\tif server.WireVersion != nil {\n\t\t\tif server.WireVersion.Max < SupportedWireVersions.Min {\n\t\t\t\tf.compatible.Store(false)\n\t\t\t\tf.compatibilityErr = fmt.Errorf(\n\t\t\t\t\t\"server at %s reports wire version %d, but this version of the Go driver requires \"+\n\t\t\t\t\t\t\"at least %d (MongoDB %s)\",\n\t\t\t\t\tserver.Addr.String(),\n\t\t\t\t\tserver.WireVersion.Max,\n\t\t\t\t\tSupportedWireVersions.Min,\n\t\t\t\t\tMinSupportedMongoDBVersion,\n\t\t\t\t)\n\t\t\t\tf.CompatibilityErr = f.compatibilityErr\n\t\t\t\treturn f.Topology, s\n\t\t\t}\n\n\t\t\tif server.WireVersion.Min > SupportedWireVersions.Max {\n\t\t\t\tf.compatible.Store(false)\n\t\t\t\tf.compatibilityErr = fmt.Errorf(\n\t\t\t\t\t\"server at %s requires wire version %d, but this version of the Go driver only supports up to %d\",\n\t\t\t\t\tserver.Addr.String(),\n\t\t\t\t\tserver.WireVersion.Min,\n\t\t\t\t\tSupportedWireVersions.Max,\n\t\t\t\t)\n\t\t\t\tf.CompatibilityErr = f.compatibilityErr\n\t\t\t\treturn f.Topology, s\n\t\t\t}\n\t\t}\n\t}\n\n\tf.compatible.Store(true)\n\tf.compatibilityErr = nil\n\n\treturn f.Topology, updatedDesc\n}\n\nfunc (f *fsm) applyToReplicaSetNoPrimary(s description.Server) description.Server {\n\tswitch s.Kind {\n\tcase description.ServerKindStandalone, description.ServerKindMongos:\n\t\tf.removeServerByAddr(s.Addr)\n\tcase description.ServerKindRSPrimary:\n\t\tf.updateRSFromPrimary(s)\n\tcase description.ServerKindRSSecondary, description.ServerKindRSArbiter, description.ServerKindRSMember:\n\t\tf.updateRSWithoutPrimary(s)\n\tcase description.Unknown, description.ServerKindRSGhost:\n\t\tf.replaceServer(s)\n\t}\n\n\treturn s\n}\n\nfunc (f *fsm) applyToReplicaSetWithPrimary(s description.Server) description.Server {\n\tswitch s.Kind {\n\tcase description.ServerKindStandalone, description.ServerKindMongos:\n\t\tf.removeServerByAddr(s.Addr)\n\t\tf.checkIfHasPrimary()\n\tcase description.ServerKindRSPrimary:\n\t\tf.updateRSFromPrimary(s)\n\tcase description.ServerKindRSSecondary, description.ServerKindRSArbiter, description.ServerKindRSMember:\n\t\tf.updateRSWithPrimaryFromMember(s)\n\tcase description.Unknown, description.ServerKindRSGhost:\n\t\tf.replaceServer(s)\n\t\tf.checkIfHasPrimary()\n\t}\n\n\treturn s\n}\n\nfunc (f *fsm) applyToSharded(s description.Server) description.Server {\n\tswitch s.Kind {\n\tcase description.ServerKindMongos, description.Unknown:\n\t\tf.replaceServer(s)\n\tcase description.ServerKindStandalone, description.ServerKindRSPrimary,\n\t\tdescription.ServerKindRSSecondary, description.ServerKindRSArbiter, description.ServerKindRSMember,\n\t\tdescription.ServerKindRSGhost:\n\t\tf.removeServerByAddr(s.Addr)\n\t}\n\n\treturn s\n}\n\nfunc (f *fsm) applyToSingle(s description.Server) description.Server {\n\tswitch s.Kind {\n\tcase description.Unknown:\n\t\tf.replaceServer(s)\n\tcase description.ServerKindStandalone, description.ServerKindMongos:\n\t\tif f.SetName != \"\" {\n\t\t\tf.removeServerByAddr(s.Addr)\n\t\t\treturn s\n\t\t}\n\n\t\tf.replaceServer(s)\n\tcase description.ServerKindRSPrimary, description.ServerKindRSSecondary,\n\t\tdescription.ServerKindRSArbiter, description.ServerKindRSMember, description.ServerKindRSGhost:\n\t\t// A replica set name can be provided when creating a direct connection. In this case, if the set name returned\n\t\t// by the hello response doesn't match up with the one provided during configuration, the server description\n\t\t// is replaced with a default Unknown description.\n\t\t//\n\t\t// We create a new server description rather than doing s.Kind = description.Unknown because the other fields,\n\t\t// such as RTT, need to be cleared for Unknown descriptions as well.\n\t\tif f.SetName != \"\" && f.SetName != s.SetName {\n\t\t\ts = description.Server{\n\t\t\t\tAddr: s.Addr,\n\t\t\t\tKind: description.Unknown,\n\t\t\t}\n\t\t}\n\n\t\tf.replaceServer(s)\n\t}\n\n\treturn s\n}\n\nfunc (f *fsm) applyToUnknown(s description.Server) description.Server {\n\tswitch s.Kind {\n\tcase description.ServerKindMongos:\n\t\tf.setKind(description.TopologyKindSharded)\n\t\tf.replaceServer(s)\n\tcase description.ServerKindRSPrimary:\n\t\tf.updateRSFromPrimary(s)\n\tcase description.ServerKindRSSecondary, description.ServerKindRSArbiter, description.ServerKindRSMember:\n\t\tf.setKind(description.TopologyKindReplicaSetNoPrimary)\n\t\tf.updateRSWithoutPrimary(s)\n\tcase description.ServerKindStandalone:\n\t\tf.updateUnknownWithStandalone(s)\n\tcase description.Unknown, description.ServerKindRSGhost:\n\t\tf.replaceServer(s)\n\t}\n\n\treturn s\n}\n\nfunc (f *fsm) checkIfHasPrimary() {\n\tif _, ok := f.findPrimary(); ok {\n\t\tf.setKind(description.TopologyKindReplicaSetWithPrimary)\n\t} else {\n\t\tf.setKind(description.TopologyKindReplicaSetNoPrimary)\n\t}\n}\n\n// hasStalePrimary returns true if the topology has a primary that is \"stale\".\nfunc hasStalePrimary(fsm fsm, srv description.Server) bool {\n\t// Compare the election ID values of the server and the topology lexicographically.\n\tcompRes := bytes.Compare(srv.ElectionID[:], fsm.maxElectionID[:])\n\n\tif wireVersion := srv.WireVersion; wireVersion != nil && wireVersion.Max >= 17 {\n\t\t// In the Post-6.0 case, a primary is considered \"stale\" if the server's election ID is greater than the\n\t\t// topology's max election ID. In these versions, the primary is also considered \"stale\" if the server's\n\t\t// election ID is LTE to the topologies election ID and the server's \"setVersion\" is less than the topology's\n\t\t// max \"setVersion\".\n\t\treturn compRes == -1 || (compRes != 1 && srv.SetVersion < fsm.maxSetVersion)\n\t}\n\n\t// If the server's election ID is less than the topology's max election ID, the primary is considered\n\t// \"stale\". Similarly, if the server's \"setVersion\" is less than the topology's max \"setVersion\", the\n\t// primary is considered stale.\n\treturn compRes == -1 || fsm.maxSetVersion > srv.SetVersion\n}\n\n// transferEVTuple will transfer the (\"ElectionID\", \"SetVersion\") tuple from the description server to the topology.\n// If the primary is stale, the tuple will not be transferred, the topology will update it's \"Kind\" value, and this\n// routine will return \"false\".\nfunc transferEVTuple(srv description.Server, fsm *fsm) bool {\n\tstalePrimary := hasStalePrimary(*fsm, srv)\n\n\tif wireVersion := srv.WireVersion; wireVersion != nil && wireVersion.Max >= 17 {\n\t\tif stalePrimary {\n\t\t\tfsm.checkIfHasPrimary()\n\t\t\treturn false\n\t\t}\n\n\t\tfsm.maxElectionID = srv.ElectionID\n\t\tfsm.maxSetVersion = srv.SetVersion\n\n\t\treturn true\n\t}\n\n\tif srv.SetVersion != 0 && !srv.ElectionID.IsZero() {\n\t\tif stalePrimary {\n\t\t\tfsm.replaceServer(description.Server{\n\t\t\t\tAddr: srv.Addr,\n\t\t\t\tLastError: fmt.Errorf(\n\t\t\t\t\t\"was a primary, but its set version or election id is stale\"),\n\t\t\t})\n\n\t\t\tfsm.checkIfHasPrimary()\n\n\t\t\treturn false\n\t\t}\n\n\t\tfsm.maxElectionID = srv.ElectionID\n\t}\n\n\tif srv.SetVersion > fsm.maxSetVersion {\n\t\tfsm.maxSetVersion = srv.SetVersion\n\t}\n\n\treturn true\n}\n\nfunc (f *fsm) updateRSFromPrimary(srv description.Server) {\n\tif f.SetName == \"\" {\n\t\tf.SetName = srv.SetName\n\t} else if f.SetName != srv.SetName {\n\t\tf.removeServerByAddr(srv.Addr)\n\t\tf.checkIfHasPrimary()\n\n\t\treturn\n\t}\n\n\tif ok := transferEVTuple(srv, f); !ok {\n\t\treturn\n\t}\n\n\tif j, ok := f.findPrimary(); ok {\n\t\tf.setServer(j, description.Server{\n\t\t\tAddr:      f.Servers[j].Addr,\n\t\t\tLastError: fmt.Errorf(\"was a primary, but a new primary was discovered\"),\n\t\t})\n\t}\n\n\tf.replaceServer(srv)\n\n\tfor j := len(f.Servers) - 1; j >= 0; j-- {\n\t\tfound := false\n\t\tfor _, member := range srv.Members {\n\t\t\tif member == f.Servers[j].Addr {\n\t\t\t\tfound = true\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\n\t\tif !found {\n\t\t\tf.removeServer(j)\n\t\t}\n\t}\n\n\tfor _, member := range srv.Members {\n\t\tif _, ok := f.findServer(member); !ok {\n\t\t\tf.addServer(member)\n\t\t}\n\t}\n\n\tf.checkIfHasPrimary()\n}\n\nfunc (f *fsm) updateRSWithPrimaryFromMember(s description.Server) {\n\tif f.SetName != s.SetName {\n\t\tf.removeServerByAddr(s.Addr)\n\t\tf.checkIfHasPrimary()\n\t\treturn\n\t}\n\n\tif s.Addr != s.CanonicalAddr {\n\t\tf.removeServerByAddr(s.Addr)\n\t\tf.checkIfHasPrimary()\n\t\treturn\n\t}\n\n\tf.replaceServer(s)\n\n\tif _, ok := f.findPrimary(); !ok {\n\t\tf.setKind(description.TopologyKindReplicaSetNoPrimary)\n\t}\n}\n\nfunc (f *fsm) updateRSWithoutPrimary(s description.Server) {\n\tif f.SetName == \"\" {\n\t\tf.SetName = s.SetName\n\t} else if f.SetName != s.SetName {\n\t\tf.removeServerByAddr(s.Addr)\n\t\treturn\n\t}\n\n\tfor _, member := range s.Members {\n\t\tif _, ok := f.findServer(member); !ok {\n\t\t\tf.addServer(member)\n\t\t}\n\t}\n\n\tif s.Addr != s.CanonicalAddr {\n\t\tf.removeServerByAddr(s.Addr)\n\t\treturn\n\t}\n\n\tf.replaceServer(s)\n}\n\nfunc (f *fsm) updateUnknownWithStandalone(s description.Server) {\n\tif len(f.Servers) > 1 {\n\t\tf.removeServerByAddr(s.Addr)\n\t\treturn\n\t}\n\n\tf.setKind(description.TopologyKindSingle)\n\tf.replaceServer(s)\n}\n\nfunc (f *fsm) addServer(addr address.Address) {\n\tf.Servers = append(f.Servers, description.Server{\n\t\tAddr: addr.Canonicalize(),\n\t})\n}\n\nfunc (f *fsm) findPrimary() (int, bool) {\n\tfor i, s := range f.Servers {\n\t\tif s.Kind == description.ServerKindRSPrimary {\n\t\t\treturn i, true\n\t\t}\n\t}\n\n\treturn 0, false\n}\n\nfunc (f *fsm) findServer(addr address.Address) (int, bool) {\n\tcanon := addr.Canonicalize()\n\tfor i, s := range f.Servers {\n\t\tif canon == s.Addr {\n\t\t\treturn i, true\n\t\t}\n\t}\n\n\treturn 0, false\n}\n\nfunc (f *fsm) removeServer(i int) {\n\tf.Servers = append(f.Servers[:i], f.Servers[i+1:]...)\n}\n\nfunc (f *fsm) removeServerByAddr(addr address.Address) {\n\tif i, ok := f.findServer(addr); ok {\n\t\tf.removeServer(i)\n\t}\n}\n\nfunc (f *fsm) replaceServer(s description.Server) {\n\tif i, ok := f.findServer(s.Addr); ok {\n\t\tf.setServer(i, s)\n\t}\n}\n\nfunc (f *fsm) setServer(i int, s description.Server) {\n\tf.Servers[i] = s\n}\n\nfunc (f *fsm) setKind(k description.TopologyKind) {\n\tf.Kind = k\n}\n"
  },
  {
    "path": "x/mongo/driver/topology/fsm_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2023-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage topology\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/description\"\n)\n\nfunc TestFSMSessionTimeout(t *testing.T) {\n\tt.Parallel()\n\n\tint64ToPtr := func(i64 int64) *int64 { return &i64 }\n\n\ttests := []struct {\n\t\tname string\n\t\tf    *fsm\n\t\ts    description.Server\n\t\twant *int64\n\t}{\n\t\t{\n\t\t\tname: \"empty\",\n\t\t\tf:    &fsm{},\n\t\t\ts:    description.Server{},\n\t\t\twant: nil,\n\t\t},\n\t\t{\n\t\t\tname: \"no session support on data-bearing server with session support on fsm\",\n\t\t\tf: &fsm{\n\t\t\t\tTopology: description.Topology{\n\t\t\t\t\tSessionTimeoutMinutes: int64ToPtr(1),\n\t\t\t\t},\n\t\t\t},\n\t\t\ts: description.Server{\n\t\t\t\tKind: description.ServerKindRSPrimary,\n\t\t\t},\n\t\t\twant: nil,\n\t\t},\n\t\t{\n\t\t\tname: \"lower timeout on data-bearing server with session support on fsm\",\n\t\t\tf: &fsm{\n\t\t\t\tTopology: description.Topology{\n\t\t\t\t\tSessionTimeoutMinutes: int64ToPtr(2),\n\t\t\t\t},\n\t\t\t},\n\t\t\ts: description.Server{\n\t\t\t\tKind:                  description.ServerKindRSPrimary,\n\t\t\t\tSessionTimeoutMinutes: int64ToPtr(1),\n\t\t\t},\n\t\t\twant: int64ToPtr(1),\n\t\t},\n\t\t{\n\t\t\tname: \"session support on data-bearing server with no session support on fsm with no servers\",\n\t\t\tf:    &fsm{Topology: description.Topology{}},\n\t\t\ts: description.Server{\n\t\t\t\tKind:                  description.ServerKindRSPrimary,\n\t\t\t\tSessionTimeoutMinutes: int64ToPtr(1),\n\t\t\t},\n\t\t\twant: int64ToPtr(1),\n\t\t},\n\t\t{\n\t\t\tname: \"session support on data-bearing server with no session support on fsm and lower servers\",\n\t\t\tf: &fsm{Topology: description.Topology{\n\t\t\t\tServers: []description.Server{\n\t\t\t\t\t{\n\t\t\t\t\t\tKind:                  description.ServerKindRSPrimary,\n\t\t\t\t\t\tSessionTimeoutMinutes: int64ToPtr(1),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t}},\n\t\t\ts: description.Server{\n\t\t\t\tKind:                  description.ServerKindRSPrimary,\n\t\t\t\tSessionTimeoutMinutes: int64ToPtr(2),\n\t\t\t},\n\t\t\twant: int64ToPtr(1),\n\t\t},\n\t\t{\n\t\t\tname: \"session support on data-bearing server with no session support on fsm and higher servers\",\n\t\t\tf: &fsm{Topology: description.Topology{\n\t\t\t\tServers: []description.Server{\n\t\t\t\t\t{\n\t\t\t\t\t\tKind:                  description.ServerKindRSPrimary,\n\t\t\t\t\t\tSessionTimeoutMinutes: int64ToPtr(3),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t}},\n\t\t\ts: description.Server{\n\t\t\t\tKind:                  description.ServerKindRSPrimary,\n\t\t\t\tSessionTimeoutMinutes: int64ToPtr(2),\n\t\t\t},\n\t\t\twant: int64ToPtr(2),\n\t\t},\n\t}\n\n\tfor _, test := range tests {\n\t\ttest := test // capture the range variable\n\n\t\tt.Run(test.name, func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tgot := selectFSMSessionTimeout(test.f, test.s)\n\t\t\tgotStr := \"nil\"\n\t\t\twantStr := \"nil\"\n\t\t\tif got != nil {\n\t\t\t\tgotStr = fmt.Sprintf(\"%v\", *got)\n\t\t\t}\n\t\t\tif test.want != nil {\n\t\t\t\twantStr = fmt.Sprintf(\"%v\", *test.want)\n\t\t\t}\n\t\t\tassert.Equal(t, test.want, got, \"minFSMServersTimeout() = %v (%v), wanted %v (%v).\", got, gotStr, test.want, wantStr)\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "x/mongo/driver/topology/polling_srv_records_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage topology\n\nimport (\n\t\"context\"\n\t\"net\"\n\t\"sort\"\n\t\"strings\"\n\t\"sync/atomic\"\n\t\"testing\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/require\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/address\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/description\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/dns\"\n)\n\ntype mockResolver struct {\n\trecordsToAdd    []*net.SRV\n\trecordsToRemove []*net.SRV\n\tlookupFail      bool\n\tlookupTimeout   bool\n\tranLookup       int32\n\tfail            int32\n}\n\nfunc newMockResolver(recordsToAdd []*net.SRV, recordsToRemove []*net.SRV, lookupFail bool, lookupTimeout bool) mockResolver {\n\tres := mockResolver{\n\t\trecordsToAdd:    recordsToAdd,\n\t\trecordsToRemove: recordsToRemove,\n\t\tlookupFail:      lookupFail,\n\t\tlookupTimeout:   lookupTimeout,\n\t}\n\treturn res\n}\n\nfunc (r *mockResolver) LookupSRV(service, proto, name string) (string, []*net.SRV, error) {\n\tatomic.AddInt32(&r.ranLookup, 1)\n\tif r.lookupFail {\n\t\treturn \"\", nil, &net.DNSError{}\n\t}\n\tif r.lookupTimeout {\n\t\treturn \"\", nil, &net.DNSError{IsTimeout: true}\n\t}\n\tstr, addresses, err := net.LookupSRV(service, proto, name)\n\tif err != nil {\n\t\treturn str, addresses, err\n\t}\n\tif r.fail > 0 {\n\t\tr.fail--\n\t\treturn str, nil, nil\n\t}\n\n\t// Add/remove records to mimic changing the DNS records.\n\tif r.recordsToAdd != nil {\n\t\taddresses = append(addresses, r.recordsToAdd...)\n\t}\n\tif r.recordsToRemove != nil {\n\t\tfor _, removeAddr := range r.recordsToRemove {\n\t\t\tfor j, addr := range addresses {\n\t\t\t\tif removeAddr.Target == addr.Target && removeAddr.Port == addr.Port {\n\t\t\t\t\taddresses = append(addresses[:j], addresses[j+1:]...)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn str, addresses, err\n}\n\nfunc (r *mockResolver) LookupTXT(string) ([]string, error) { return nil, nil }\n\nvar srvPollingTests = []struct {\n\tname            string\n\trecordsToAdd    []*net.SRV\n\trecordsToRemove []*net.SRV\n\tlookupFail      bool\n\tlookupTimeout   bool\n\texpectedHosts   []string\n\theartbeatTime   bool\n}{\n\t{\"Add new record\", []*net.SRV{{\"localhost.test.build.10gen.cc.\", 27019, 0, 0}}, nil, false, false, []string{\"localhost.test.build.10gen.cc:27017\", \"localhost.test.build.10gen.cc:27018\", \"localhost.test.build.10gen.cc:27019\"}, false},\n\t{\"Remove existing record\", nil, []*net.SRV{{\"localhost.test.build.10gen.cc.\", 27018, 0, 0}}, false, false, []string{\"localhost.test.build.10gen.cc:27017\"}, false},\n\t{\"Replace existing record\", []*net.SRV{{\"localhost.test.build.10gen.cc.\", 27019, 0, 0}}, []*net.SRV{{\"localhost.test.build.10gen.cc.\", 27018, 0, 0}}, false, false, []string{\"localhost.test.build.10gen.cc:27017\", \"localhost.test.build.10gen.cc:27019\"}, false},\n\t{\"Replace both with one new\", []*net.SRV{{\"localhost.test.build.10gen.cc.\", 27019, 0, 0}}, []*net.SRV{{\"localhost.test.build.10gen.cc.\", 27017, 0, 0}, {\"localhost.test.build.10gen.cc.\", 27018, 0, 0}}, false, false, []string{\"localhost.test.build.10gen.cc:27019\"}, false},\n\t{\"Replace both with two new\", []*net.SRV{{\"localhost.test.build.10gen.cc.\", 27019, 0, 0}, {\"localhost.test.build.10gen.cc.\", 27020, 0, 0}}, []*net.SRV{{\"localhost.test.build.10gen.cc.\", 27017, 0, 0}, {\"localhost.test.build.10gen.cc.\", 27018, 0, 0}}, false, false, []string{\"localhost.test.build.10gen.cc:27019\", \"localhost.test.build.10gen.cc:27020\"}, false},\n\t{\"DNS lookup timeout\", nil, nil, false, true, []string{\"localhost.test.build.10gen.cc:27017\", \"localhost.test.build.10gen.cc:27018\"}, true},\n\t{\"DNS lookup failure\", nil, nil, true, false, []string{\"localhost.test.build.10gen.cc:27017\", \"localhost.test.build.10gen.cc:27018\"}, true},\n\t{\"Remove all\", nil, []*net.SRV{{\"localhost.test.build.10gen.cc.\", 27017, 0, 0}, {\"localhost.test.build.10gen.cc.\", 27018, 0, 0}}, false, false, []string{\"localhost.test.build.10gen.cc:27017\", \"localhost.test.build.10gen.cc:27018\"}, true},\n}\n\ntype serverSorter []description.Server\n\nfunc (ss serverSorter) Len() int      { return len(ss) }\nfunc (ss serverSorter) Swap(i, j int) { ss[i], ss[j] = ss[j], ss[i] }\nfunc (ss serverSorter) Less(i, j int) bool {\n\treturn strings.Compare(ss[i].Addr.String(), ss[j].Addr.String()) < 0\n}\n\nfunc compareHosts(t *testing.T, received []description.Server, expected []string) {\n\tt.Helper()\n\tif len(received) != len(expected) {\n\t\tt.Fatalf(\"Number of hosts in topology does not match expected value. Got %v; want %v.\", len(received), len(expected))\n\t}\n\n\t// Take a copy of servers so we don't risk a data race similar to GODRIVER-1301.\n\tservers := make([]description.Server, len(received))\n\tcopy(servers, received)\n\tactual := serverSorter(servers)\n\tsort.Sort(actual)\n\tsort.Strings(expected)\n\n\tfor i := range servers {\n\t\tif servers[i].Addr.String() != expected[i] {\n\t\t\tt.Errorf(\"Hosts in topology differ from expected values. Got %v; want %v.\",\n\t\t\t\tservers[i].Addr.String(), expected[i])\n\t\t}\n\t}\n}\n\nfunc TestPollingSRVRecordsSpec(t *testing.T) {\n\tfor _, uri := range []string{\n\t\t\"mongodb+srv://test1.test.build.10gen.cc/?heartbeatFrequencyMS=500\",\n\t\t// Test with user:pass as a regression test for GODRIVER-2620\n\t\t\"mongodb+srv://user:pass@test1.test.build.10gen.cc/?heartbeatFrequencyMS=500\",\n\t} {\n\t\tt.Run(uri, func(t *testing.T) {\n\t\t\ttestPollingSRVRecordsSpec(t, uri)\n\t\t})\n\t}\n}\n\nfunc testPollingSRVRecordsSpec(t *testing.T, uri string) {\n\tt.Helper()\n\tfor _, tt := range srvPollingTests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tcfg, err := NewConfig(options.Client().ApplyURI(uri), nil)\n\t\t\trequire.NoError(t, err, \"error constructing topology configs: %v\", err)\n\n\t\t\ttopo, err := New(cfg)\n\t\t\trequire.NoError(t, err, \"Could not create the topology: %v\", err)\n\t\t\tmockRes := newMockResolver(tt.recordsToAdd, tt.recordsToRemove, tt.lookupFail, tt.lookupTimeout)\n\t\t\ttopo.dnsResolver = &dns.Resolver{mockRes.LookupSRV, mockRes.LookupTXT}\n\t\t\ttopo.rescanSRVInterval = time.Millisecond * 5\n\t\t\terr = topo.Connect()\n\t\t\trequire.NoError(t, err, \"Could not Connect to the topology: %v\", err)\n\n\t\t\t// wait for description to update\n\t\t\tsub, err := topo.Subscribe()\n\t\t\trequire.NoError(t, err, \"Couldn't subscribe: %v\", err)\n\t\t\tvar desc description.Topology\n\t\t\tfor atomic.LoadInt32(&mockRes.ranLookup) < 2 {\n\t\t\t\tdesc = <-sub.Updates\n\t\t\t}\n\n\t\t\trequire.True(t, tt.heartbeatTime == topo.pollHeartbeatTime.Load().(bool), \"Not polling on correct intervals\")\n\t\t\tcompareHosts(t, desc.Servers, tt.expectedHosts)\n\t\t\t_ = topo.Disconnect(context.Background())\n\t\t})\n\t}\n}\n\nfunc TestPollSRVRecords(t *testing.T) {\n\tt.Run(\"Not unknown or sharded topology\", func(t *testing.T) {\n\t\turi := \"mongodb+srv://test1.test.build.10gen.cc/?heartbeatFrequencyMS=500\"\n\t\tcfg, err := NewConfig(options.Client().ApplyURI(uri), nil)\n\t\trequire.NoError(t, err, \"error constructing topology config: %v\", err)\n\n\t\ttopo, err := New(cfg)\n\t\trequire.NoError(t, err, \"Could not create the topology: %v\", err)\n\t\tmockRes := newMockResolver(nil, nil, false, false)\n\t\ttopo.dnsResolver = &dns.Resolver{mockRes.LookupSRV, mockRes.LookupTXT}\n\t\ttopo.rescanSRVInterval = time.Millisecond * 5\n\t\terr = topo.Connect()\n\t\trequire.NoError(t, err, \"Could not Connect to the topology: %v\", err)\n\t\ttopo.serversLock.Lock()\n\t\ttopo.fsm.Kind = description.TopologyKindSingle\n\t\ttopo.desc.Store(description.Topology{\n\t\t\tKind:                  topo.fsm.Kind,\n\t\t\tServers:               topo.fsm.Servers,\n\t\t\tSessionTimeoutMinutes: topo.fsm.SessionTimeoutMinutes,\n\t\t})\n\t\ttopo.serversLock.Unlock()\n\n\t\t// wait for description to update\n\t\tsub, err := topo.Subscribe()\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"Couldn't subscribe: %v\", err)\n\t\t}\n\n\t\tfor i := 0; i < 4; i++ {\n\t\t\t<-sub.Updates\n\t\t}\n\t\trequire.False(t, atomic.LoadInt32(&mockRes.ranLookup) > 0)\n\n\t\tactualHosts := topo.Description().Servers\n\t\texpectedHosts := []string{\"localhost.test.build.10gen.cc:27017\", \"localhost.test.build.10gen.cc:27018\"}\n\t\tcompareHosts(t, actualHosts, expectedHosts)\n\t\t_ = topo.Disconnect(context.Background())\n\t})\n\tt.Run(\"Failed Hostname Verification\", func(t *testing.T) {\n\t\turi := \"mongodb+srv://test1.test.build.10gen.cc/?heartbeatFrequencyMS=500\"\n\t\tcfg, err := NewConfig(options.Client().ApplyURI(uri), nil)\n\t\trequire.NoError(t, err, \"error constructing topology config: %v\", err)\n\n\t\ttopo, err := New(cfg)\n\t\trequire.NoError(t, err, \"Could not create the topology: %v\", err)\n\t\tmockRes := newMockResolver([]*net.SRV{{\"blah.bleh\", 27019, 0, 0}, {\"localhost.test.build.10gen.cc.\", 27020, 0, 0}}, nil, false, false)\n\t\ttopo.dnsResolver = &dns.Resolver{mockRes.LookupSRV, mockRes.LookupTXT}\n\t\ttopo.rescanSRVInterval = time.Millisecond * 5\n\t\terr = topo.Connect()\n\t\trequire.NoError(t, err, \"Could not Connect to the topology: %v\", err)\n\n\t\t// wait for description to update\n\t\tsub, err := topo.Subscribe()\n\t\trequire.NoError(t, err, \"Couldn't subscribe: %v\", err)\n\t\tvar desc description.Topology\n\t\tfor atomic.LoadInt32(&mockRes.ranLookup) < 2 {\n\t\t\tdesc = <-sub.Updates\n\t\t}\n\n\t\trequire.False(t, topo.pollHeartbeatTime.Load().(bool))\n\t\texpectedHosts := []string{\"localhost.test.build.10gen.cc:27017\", \"localhost.test.build.10gen.cc:27018\", \"localhost.test.build.10gen.cc:27020\"}\n\t\tcompareHosts(t, desc.Servers, expectedHosts)\n\t\t_ = topo.Disconnect(context.Background())\n\t})\n\tt.Run(\"Return to polling time\", func(t *testing.T) {\n\t\turi := \"mongodb+srv://test1.test.build.10gen.cc/?heartbeatFrequencyMS=500\"\n\t\tcfg, err := NewConfig(options.Client().ApplyURI(uri), nil)\n\t\trequire.NoError(t, err, \"error constructing topology config: %v\", err)\n\n\t\ttopo, err := New(cfg)\n\t\trequire.NoError(t, err, \"Could not create the topology: %v\", err)\n\t\tmockRes := newMockResolver(nil, nil, false, false)\n\t\tmockRes.fail = 1\n\t\ttopo.dnsResolver = &dns.Resolver{mockRes.LookupSRV, mockRes.LookupTXT}\n\t\ttopo.rescanSRVInterval = time.Millisecond * 5\n\t\terr = topo.Connect()\n\t\trequire.NoError(t, err, \"Could not Connect to the topology: %v\", err)\n\n\t\t// wait for description to update\n\t\tsub, err := topo.Subscribe()\n\t\trequire.NoError(t, err, \"Couldn't subscribe: %v\", err)\n\t\tvar desc description.Topology\n\t\tfor atomic.LoadInt32(&mockRes.ranLookup) < 3 {\n\t\t\tdesc = <-sub.Updates\n\t\t}\n\n\t\trequire.False(t, topo.pollHeartbeatTime.Load().(bool))\n\t\texpectedHosts := []string{\"localhost.test.build.10gen.cc:27017\", \"localhost.test.build.10gen.cc:27018\"}\n\t\tcompareHosts(t, desc.Servers, expectedHosts)\n\t\t_ = topo.Disconnect(context.Background())\n\t})\n}\n\nfunc TestPollingSRVRecordsLoadBalanced(t *testing.T) {\n\tcreateLBTopology := func(t *testing.T, uri string) *Topology {\n\t\tt.Helper()\n\n\t\tcfg, err := NewConfig(options.Client().ApplyURI(uri).SetLoadBalanced(true), nil)\n\t\trequire.NoError(t, err, \"error constructing topology config: %v\", err)\n\n\t\ttopo, err := New(cfg)\n\t\trequire.NoError(t, err, \"Could not create the topology: %v\", err)\n\n\t\treturn topo\n\t}\n\n\tt.Run(\"pollingRequired is set to false\", func(t *testing.T) {\n\t\ttopo := createLBTopology(t, \"mongodb+srv://test24.test.build.10gen.cc/?heartbeatFrequencyMS=500\")\n\t\tassert.False(t, topo.pollingRequired, \"expected SRV polling to not be required, but it is\")\n\t})\n\n\tt.Run(\"new records are not detected\", func(t *testing.T) {\n\t\trecordsToAdd := []*net.SRV{{\"localhost.test.build.10gen.cc.\", 27019, 0, 0}}\n\t\tmockResolver := newMockResolver(recordsToAdd, nil, false, false)\n\t\tdnsResolver := &dns.Resolver{\n\t\t\tLookupSRV: mockResolver.LookupSRV,\n\t\t\tLookupTXT: mockResolver.LookupTXT,\n\t\t}\n\n\t\ttopo := createLBTopology(t, \"mongodb+srv://test3.test.build.10gen.cc\")\n\t\ttopo.dnsResolver = dnsResolver\n\t\ttopo.rescanSRVInterval = time.Millisecond * 5\n\t\terr := topo.Connect()\n\t\tassert.Nil(t, err, \"Connect error: %v\", err)\n\t\tdefer func() {\n\t\t\t_ = topo.Disconnect(context.Background())\n\t\t}()\n\n\t\t// Wait for 2*rescanInterval and assert that polling was not done and the final host list only contains the\n\t\t// original host.\n\t\ttime.Sleep(2 * topo.rescanSRVInterval)\n\t\tlookupCalledTimes := atomic.LoadInt32(&mockResolver.ranLookup)\n\t\tassert.Equal(t, int32(0), lookupCalledTimes, \"expected SRV lookup to occur 0 times, got %d\", lookupCalledTimes)\n\t\texpectedHosts := []string{\"localhost.test.build.10gen.cc:27017\"}\n\t\tcompareHosts(t, topo.Description().Servers, expectedHosts)\n\t})\n}\n\nfunc TestPollSRVRecordsMaxHosts(t *testing.T) {\n\t// simulateSRVPoll creates a topology with srvMaxHosts, mocks the DNS changes described by\n\t// recordsToAdd and recordsToRemove, and returns the topology.\n\tsimulateSRVPoll := func(srvMaxHosts int, recordsToAdd []*net.SRV, recordsToRemove []*net.SRV) (*Topology, func(ctx context.Context) error) {\n\t\tt.Helper()\n\n\t\turi := \"mongodb+srv://test1.test.build.10gen.cc/?heartbeatFrequencyMS=500\"\n\t\tcfg, err := NewConfig(options.Client().ApplyURI(uri).SetSRVMaxHosts(srvMaxHosts), nil)\n\t\trequire.NoError(t, err, \"error constructing topology config: %v\", err)\n\n\t\ttopo, err := New(cfg)\n\t\trequire.NoError(t, err, \"Could not create the topology: %v\", err)\n\n\t\tmockRes := newMockResolver(recordsToAdd, recordsToRemove, false, false)\n\t\ttopo.dnsResolver = &dns.Resolver{mockRes.LookupSRV, mockRes.LookupTXT}\n\t\ttopo.rescanSRVInterval = time.Millisecond * 5\n\t\terr = topo.Connect()\n\t\tassert.Nil(t, err, \"Connect error: %v\", err)\n\n\t\t// Wait for description to update.\n\t\tsub, err := topo.Subscribe()\n\t\tassert.Nil(t, err, \"Subscribe error: %v\", err)\n\t\tfor atomic.LoadInt32(&mockRes.ranLookup) < 2 {\n\t\t\t<-sub.Updates\n\t\t}\n\n\t\treturn topo, topo.Disconnect\n\t}\n\n\tt.Run(\"SRVMaxHosts is 0\", func(t *testing.T) {\n\t\trecordsToAdd := []*net.SRV{{\"localhost.test.build.10gen.cc.\", 27019, 0, 0}, {\"localhost.test.build.10gen.cc.\", 27020, 0, 0}}\n\t\trecordsToRemove := []*net.SRV{{\"localhost.test.build.10gen.cc.\", 27018, 0, 0}}\n\t\ttopo, disconnect := simulateSRVPoll(0, recordsToAdd, recordsToRemove)\n\t\tdefer func() { _ = disconnect(context.Background()) }()\n\n\t\tactualHosts := topo.Description().Servers\n\t\texpectedHosts := []string{\n\t\t\t\"localhost.test.build.10gen.cc:27017\",\n\t\t\t\"localhost.test.build.10gen.cc:27019\",\n\t\t\t\"localhost.test.build.10gen.cc:27020\",\n\t\t}\n\t\tcompareHosts(t, actualHosts, expectedHosts)\n\t})\n\tt.Run(\"SRVMaxHosts is number of hosts\", func(t *testing.T) {\n\t\trecordsToAdd := []*net.SRV{{\"localhost.test.build.10gen.cc.\", 27019, 0, 0}, {\"localhost.test.build.10gen.cc.\", 27020, 0, 0}}\n\t\trecordsToRemove := []*net.SRV{{\"localhost.test.build.10gen.cc.\", 27017, 0, 0}, {\"localhost.test.build.10gen.cc.\", 27018, 0, 0}}\n\t\ttopo, disconnect := simulateSRVPoll(2, recordsToAdd, recordsToRemove)\n\t\tdefer func() { _ = disconnect(context.Background()) }()\n\n\t\tactualHosts := topo.Description().Servers\n\t\texpectedHosts := []string{\n\t\t\t\"localhost.test.build.10gen.cc:27019\",\n\t\t\t\"localhost.test.build.10gen.cc:27020\",\n\t\t}\n\t\tcompareHosts(t, actualHosts, expectedHosts)\n\t})\n\tt.Run(\"SRVMaxHosts is less than number of hosts\", func(t *testing.T) {\n\t\trecordsToAdd := []*net.SRV{{\"localhost.test.build.10gen.cc.\", 27019, 0, 0}, {\"localhost.test.build.10gen.cc.\", 27020, 0, 0}}\n\t\trecordsToRemove := []*net.SRV{{\"localhost.test.build.10gen.cc.\", 27018, 0, 0}}\n\t\ttopo, disconnect := simulateSRVPoll(2, recordsToAdd, recordsToRemove)\n\t\tdefer func() { _ = disconnect(context.Background()) }()\n\n\t\tactualHosts := topo.Description().Servers\n\t\tassert.Equal(t, 2, len(actualHosts), \"expected 2 hosts in topology server list, got %v\", len(actualHosts))\n\n\t\texpectedHost := \"localhost.test.build.10gen.cc:27017\"\n\t\taddr := address.Address(expectedHost).Canonicalize()\n\t\t_, ok := topo.servers[addr]\n\t\tassert.True(t, ok, \"topology server list did not contain expected host %v\", expectedHost)\n\t})\n}\n\nfunc TestPollSRVRecordsServiceName(t *testing.T) {\n\t// simulateSRVPoll creates a topology with srvServiceName, mocks the DNS changes described by\n\t// recordsToAdd and recordsToRemove, and returns the topology.\n\tsimulateSRVPoll := func(srvServiceName string, recordsToAdd []*net.SRV, recordsToRemove []*net.SRV) (*Topology, func(ctx context.Context) error) {\n\t\tt.Helper()\n\n\t\turi := \"mongodb+srv://test22.test.build.10gen.cc/?heartbeatFrequencyMS=500&srvServiceName=customname\"\n\t\tcfg, err := NewConfig(options.Client().ApplyURI(uri).SetSRVServiceName(srvServiceName), nil)\n\t\trequire.NoError(t, err, \"error constructing topology config: %v\", err)\n\n\t\ttopo, err := New(cfg)\n\t\trequire.NoError(t, err, \"Could not create the topology: %v\", err)\n\n\t\tmockRes := newMockResolver(recordsToAdd, recordsToRemove, false, false)\n\t\ttopo.dnsResolver = &dns.Resolver{mockRes.LookupSRV, mockRes.LookupTXT}\n\t\ttopo.rescanSRVInterval = time.Millisecond * 5\n\t\terr = topo.Connect()\n\t\tassert.Nil(t, err, \"Connect error: %v\", err)\n\n\t\t// Wait for description to update.\n\t\tsub, err := topo.Subscribe()\n\t\tassert.Nil(t, err, \"Subscribe error: %v\", err)\n\t\tfor atomic.LoadInt32(&mockRes.ranLookup) < 2 {\n\t\t\t<-sub.Updates\n\t\t}\n\n\t\treturn topo, topo.Disconnect\n\t}\n\n\tt.Run(\"SRVServiceName is customname\", func(t *testing.T) {\n\t\trecordsToAdd := []*net.SRV{{\"localhost.test.build.10gen.cc.\", 27019, 0, 0}, {\"localhost.test.build.10gen.cc.\", 27020, 0, 0}}\n\t\trecordsToRemove := []*net.SRV{{\"localhost.test.build.10gen.cc.\", 27017, 0, 0}, {\"localhost.test.build.10gen.cc.\", 27018, 0, 0}}\n\t\ttopo, disconnect := simulateSRVPoll(\"customname\", recordsToAdd, recordsToRemove)\n\t\tdefer func() { _ = disconnect(context.Background()) }()\n\n\t\tactualHosts := topo.Description().Servers\n\t\texpectedHosts := []string{\n\t\t\t\"localhost.test.build.10gen.cc:27019\",\n\t\t\t\"localhost.test.build.10gen.cc:27020\",\n\t\t}\n\t\tcompareHosts(t, actualHosts, expectedHosts)\n\t})\n}\n"
  },
  {
    "path": "x/mongo/driver/topology/pool.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage topology\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"sync\"\n\t\"sync/atomic\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/event\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/logger\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/address\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver\"\n)\n\n// Connection pool state constants.\nconst (\n\tpoolPaused int = iota\n\tpoolReady\n\tpoolClosed\n)\n\n// ErrPoolNotPaused is returned when attempting to mark a connection pool \"ready\" that is not\n// currently \"paused\".\nvar ErrPoolNotPaused = PoolError(\"only a paused pool can be marked ready\")\n\n// ErrPoolClosed is returned when attempting to check out a connection from a closed pool.\nvar ErrPoolClosed = PoolError(\"attempted to check out a connection from closed connection pool\")\n\n// ErrConnectionClosed is returned from an attempt to use an already closed connection.\nvar ErrConnectionClosed = ConnectionError{ConnectionID: \"<closed>\", message: \"connection is closed\"}\n\n// ErrWrongPool is return when a connection is returned to a pool it doesn't belong to.\nvar ErrWrongPool = PoolError(\"connection does not belong to this pool\")\n\n// PoolError is an error returned from a Pool method.\ntype PoolError string\n\nfunc (pe PoolError) Error() string { return string(pe) }\n\n// poolClearedError is an error returned when the connection pool is cleared or currently paused. It\n// is a retryable error.\ntype poolClearedError struct {\n\terr     error\n\taddress address.Address\n}\n\nfunc (pce poolClearedError) Error() string {\n\treturn fmt.Sprintf(\n\t\t\"connection pool for %v was cleared because another operation failed with: %v\",\n\t\tpce.address,\n\t\tpce.err)\n}\n\n// Retryable returns true. All poolClearedErrors are retryable.\nfunc (poolClearedError) Retryable() bool { return true }\n\n// Assert that poolClearedError is a driver.RetryablePoolError.\nvar _ driver.RetryablePoolError = poolClearedError{}\n\n// poolConfig contains all aspects of the pool that can be configured\ntype poolConfig struct {\n\tAddress          address.Address\n\tMinPoolSize      uint64\n\tMaxPoolSize      uint64\n\tMaxConnecting    uint64\n\tMaxIdleTime      time.Duration\n\tMaintainInterval time.Duration\n\tLoadBalanced     bool\n\tPoolMonitor      *event.PoolMonitor\n\tLogger           *logger.Logger\n\thandshakeErrFn   func(error, uint64, *bson.ObjectID)\n\tConnectTimeout   time.Duration\n}\n\ntype pool struct {\n\t// The following integer fields must be accessed using the atomic package\n\t// and should be at the beginning of the struct.\n\t// - atomic bug: https://pkg.go.dev/sync/atomic#pkg-note-BUG\n\t// - suggested layout: https://go101.org/article/memory-layout.html\n\n\tnextID                       int64 // nextID is the next pool ID for a new connection.\n\tpinnedCursorConnections      uint64\n\tpinnedTransactionConnections uint64\n\n\taddress       address.Address\n\tminSize       uint64\n\tmaxSize       uint64\n\tmaxConnecting uint64\n\tloadBalanced  bool\n\tmonitor       *event.PoolMonitor\n\tlogger        *logger.Logger\n\n\t// handshakeErrFn is used to handle any errors that happen during connection establishment and\n\t// handshaking.\n\thandshakeErrFn func(error, uint64, *bson.ObjectID)\n\n\tconnOpts   []ConnectionOption\n\tgeneration *poolGenerationMap\n\n\tmaintainInterval time.Duration   // maintainInterval is the maintain() loop interval.\n\tmaintainReady    chan struct{}   // maintainReady is a signal channel that starts the maintain() loop when ready() is called.\n\tbackgroundDone   *sync.WaitGroup // backgroundDone waits for all background goroutines to return.\n\n\tstateMu      sync.RWMutex // stateMu guards state, lastClearErr\n\tstate        int          // state is the current state of the connection pool.\n\tlastClearErr error        // lastClearErr is the last error that caused the pool to be cleared.\n\n\t// createConnectionsCond is the condition variable that controls when the createConnections()\n\t// loop runs or waits. Its lock guards cancelBackgroundCtx, conns, and newConnWait. Any changes\n\t// to the state of the guarded values must be made while holding the lock to prevent undefined\n\t// behavior in the createConnections() waiting logic.\n\tcreateConnectionsCond *sync.Cond\n\tcancelBackgroundCtx   context.CancelFunc    // cancelBackgroundCtx is called to signal background goroutines to stop.\n\tconns                 map[int64]*connection // conns holds all currently open connections.\n\tnewConnWait           wantConnQueue         // newConnWait holds all wantConn requests for new connections.\n\n\tidleMu         sync.Mutex    // idleMu guards idleConns, idleConnWait\n\tidleConns      []*connection // idleConns holds all idle connections.\n\tidleConnWait   wantConnQueue // idleConnWait holds all wantConn requests for idle connections.\n\tconnectTimeout time.Duration\n}\n\n// getState returns the current state of the pool. Callers must not hold the stateMu lock.\nfunc (p *pool) getState() int {\n\tp.stateMu.RLock()\n\tdefer p.stateMu.RUnlock()\n\n\treturn p.state\n}\n\nfunc mustLogPoolMessage(pool *pool) bool {\n\treturn pool.logger != nil && pool.logger.LevelComponentEnabled(\n\t\tlogger.LevelDebug, logger.ComponentConnection)\n}\n\nfunc logPoolMessage(pool *pool, msg string, keysAndValues ...any) {\n\thost, port, err := net.SplitHostPort(pool.address.String())\n\tif err != nil {\n\t\thost = pool.address.String()\n\t\tport = \"\"\n\t}\n\n\tpool.logger.Print(logger.LevelDebug,\n\t\tlogger.ComponentConnection,\n\t\tmsg,\n\t\tlogger.SerializeConnection(logger.Connection{\n\t\t\tMessage:    msg,\n\t\t\tServerHost: host,\n\t\t\tServerPort: port,\n\t\t}, keysAndValues...)...)\n}\n\ntype reason struct {\n\tloggerConn string\n\tevent      string\n}\n\n// connectionPerished checks if a given connection is perished and should be removed from the pool.\nfunc connectionPerished(conn *connection) (reason, bool) {\n\tswitch {\n\tcase conn.closed():\n\t\t// A connection would only be closed if it encountered a network error\n\t\t// during an operation and closed itself.\n\t\treturn reason{\n\t\t\tloggerConn: logger.ReasonConnClosedError,\n\t\t\tevent:      event.ReasonError,\n\t\t}, true\n\tcase conn.idleTimeoutExpired():\n\t\treturn reason{\n\t\t\tloggerConn: logger.ReasonConnClosedIdle,\n\t\t\tevent:      event.ReasonIdle,\n\t\t}, true\n\tcase conn.pool.stale(conn):\n\t\treturn reason{\n\t\t\tloggerConn: logger.ReasonConnClosedStale,\n\t\t\tevent:      event.ReasonStale,\n\t\t}, true\n\t}\n\n\treturn reason{}, false\n}\n\n// newPool creates a new pool. It will use the provided options when creating connections.\nfunc newPool(config poolConfig, connOpts ...ConnectionOption) *pool {\n\tif config.MaxIdleTime != time.Duration(0) {\n\t\tconnOpts = append(connOpts, WithIdleTimeout(func(_ time.Duration) time.Duration { return config.MaxIdleTime }))\n\t}\n\n\tvar maxConnecting uint64 = 2\n\tif config.MaxConnecting > 0 {\n\t\tmaxConnecting = config.MaxConnecting\n\t}\n\n\tmaintainInterval := 10 * time.Second\n\tif config.MaintainInterval != 0 {\n\t\tmaintainInterval = config.MaintainInterval\n\t}\n\n\tpool := &pool{\n\t\taddress:               config.Address,\n\t\tminSize:               config.MinPoolSize,\n\t\tmaxSize:               config.MaxPoolSize,\n\t\tmaxConnecting:         maxConnecting,\n\t\tloadBalanced:          config.LoadBalanced,\n\t\tmonitor:               config.PoolMonitor,\n\t\tlogger:                config.Logger,\n\t\thandshakeErrFn:        config.handshakeErrFn,\n\t\tconnOpts:              connOpts,\n\t\tgeneration:            newPoolGenerationMap(),\n\t\tstate:                 poolPaused,\n\t\tmaintainInterval:      maintainInterval,\n\t\tmaintainReady:         make(chan struct{}, 1),\n\t\tbackgroundDone:        &sync.WaitGroup{},\n\t\tcreateConnectionsCond: sync.NewCond(&sync.Mutex{}),\n\t\tconns:                 make(map[int64]*connection, config.MaxPoolSize),\n\t\tidleConns:             make([]*connection, 0, config.MaxPoolSize),\n\t\tconnectTimeout:        config.ConnectTimeout,\n\t}\n\t// minSize must not exceed maxSize if maxSize is not 0\n\tif pool.maxSize != 0 && pool.minSize > pool.maxSize {\n\t\tpool.minSize = pool.maxSize\n\t}\n\tpool.connOpts = append(pool.connOpts, withGenerationNumberFn(func(_ generationNumberFn) generationNumberFn { return pool.getGenerationForNewConnection }))\n\n\tpool.generation.connect()\n\n\t// Create a Context with cancellation that's used to signal the createConnections() and\n\t// maintain() background goroutines to stop. Also create a \"backgroundDone\" WaitGroup that is\n\t// used to wait for the background goroutines to return.\n\tvar ctx context.Context\n\tctx, pool.cancelBackgroundCtx = context.WithCancel(context.Background())\n\n\tfor i := 0; i < int(pool.maxConnecting); i++ {\n\t\tpool.backgroundDone.Add(1)\n\t\tgo pool.createConnections(ctx, pool.backgroundDone)\n\t}\n\n\t// If maintainInterval is not positive, don't start the maintain() goroutine. Expect that\n\t// negative values are only used in testing; this config value is not user-configurable.\n\tif maintainInterval > 0 {\n\t\tpool.backgroundDone.Add(1)\n\t\tgo pool.maintain(ctx, pool.backgroundDone)\n\t}\n\n\tif mustLogPoolMessage(pool) {\n\t\tkeysAndValues := logger.KeyValues{\n\t\t\tlogger.KeyMaxIdleTimeMS, config.MaxIdleTime.Milliseconds(),\n\t\t\tlogger.KeyMinPoolSize, config.MinPoolSize,\n\t\t\tlogger.KeyMaxPoolSize, config.MaxPoolSize,\n\t\t\tlogger.KeyMaxConnecting, config.MaxConnecting,\n\t\t}\n\n\t\tlogPoolMessage(pool, logger.ConnectionPoolCreated, keysAndValues...)\n\t}\n\n\tif pool.monitor != nil {\n\t\tpool.monitor.Event(&event.PoolEvent{\n\t\t\tType: event.ConnectionPoolCreated,\n\t\t\tPoolOptions: &event.MonitorPoolOptions{\n\t\t\t\tMaxPoolSize: config.MaxPoolSize,\n\t\t\t\tMinPoolSize: config.MinPoolSize,\n\t\t\t},\n\t\t\tAddress: pool.address.String(),\n\t\t})\n\t}\n\n\treturn pool\n}\n\n// stale checks if a given connection's generation is below the generation of the pool\nfunc (p *pool) stale(conn *connection) bool {\n\treturn conn == nil || p.generation.stale(conn.desc.ServiceID, conn.generation)\n}\n\n// ready puts the pool into the \"ready\" state and starts the background connection creation and\n// monitoring goroutines. ready must be called before connections can be checked out. An unused,\n// connected pool must be closed or it will leak goroutines and will not be garbage collected.\nfunc (p *pool) ready() error {\n\t// While holding the stateMu lock, set the pool to \"ready\" if it is currently \"paused\".\n\tp.stateMu.Lock()\n\tif p.state == poolReady {\n\t\tp.stateMu.Unlock()\n\t\treturn nil\n\t}\n\tif p.state != poolPaused {\n\t\tp.stateMu.Unlock()\n\t\treturn ErrPoolNotPaused\n\t}\n\tp.lastClearErr = nil\n\tp.state = poolReady\n\tp.stateMu.Unlock()\n\n\tif mustLogPoolMessage(p) {\n\t\tlogPoolMessage(p, logger.ConnectionPoolReady)\n\t}\n\n\t// Send event.PoolReady before resuming the maintain() goroutine to guarantee that the\n\t// \"pool ready\" event is always sent before maintain() starts creating connections.\n\tif p.monitor != nil {\n\t\tp.monitor.Event(&event.PoolEvent{\n\t\t\tType:    event.ConnectionPoolReady,\n\t\t\tAddress: p.address.String(),\n\t\t})\n\t}\n\n\t// Signal maintain() to wake up immediately when marking the pool \"ready\".\n\tselect {\n\tcase p.maintainReady <- struct{}{}:\n\tdefault:\n\t}\n\n\treturn nil\n}\n\n// close closes the pool, closes all connections associated with the pool, and stops all background\n// goroutines. All subsequent checkOut requests will return an error. An unused, ready pool must be\n// closed or it will leak goroutines and will not be garbage collected.\nfunc (p *pool) close(ctx context.Context) {\n\tp.stateMu.Lock()\n\tif p.state == poolClosed {\n\t\tp.stateMu.Unlock()\n\t\treturn\n\t}\n\tp.state = poolClosed\n\tp.stateMu.Unlock()\n\n\t// Call cancelBackgroundCtx() to exit the maintain() and createConnections() background\n\t// goroutines. Broadcast to the createConnectionsCond to wake up all createConnections()\n\t// goroutines. We must hold the createConnectionsCond lock here because we're changing the\n\t// condition by cancelling the \"background goroutine\" Context, even tho cancelling the Context\n\t// is also synchronized by a lock. Otherwise, we run into an intermittent bug that prevents the\n\t// createConnections() goroutines from exiting.\n\tp.createConnectionsCond.L.Lock()\n\tp.cancelBackgroundCtx()\n\tp.createConnectionsCond.Broadcast()\n\tp.createConnectionsCond.L.Unlock()\n\n\t// Wait for all background goroutines to exit.\n\tp.backgroundDone.Wait()\n\n\tp.generation.disconnect()\n\n\tif ctx == nil {\n\t\tctx = context.Background()\n\t}\n\n\t// If we have a deadline then we interpret it as a request to gracefully shutdown. We wait until\n\t// either all the connections have been checked back into the pool (i.e. total open connections\n\t// equals idle connections) or until the Context deadline is reached.\n\tif _, ok := ctx.Deadline(); ok {\n\t\tticker := time.NewTicker(100 * time.Millisecond)\n\t\tdefer ticker.Stop()\n\n\tgraceful:\n\t\tfor {\n\t\t\tif p.totalConnectionCount() == p.availableConnectionCount() {\n\t\t\t\tbreak graceful\n\t\t\t}\n\n\t\t\tselect {\n\t\t\tcase <-ticker.C:\n\t\t\tcase <-ctx.Done():\n\t\t\t\tbreak graceful\n\t\t\tdefault:\n\t\t\t}\n\t\t}\n\t}\n\n\t// Empty the idle connections stack and try to deliver ErrPoolClosed to any waiting wantConns\n\t// from idleConnWait while holding the idleMu lock.\n\tp.idleMu.Lock()\n\tfor _, conn := range p.idleConns {\n\t\t_ = p.removeConnection(conn, reason{\n\t\t\tloggerConn: logger.ReasonConnClosedPoolClosed,\n\t\t\tevent:      event.ReasonPoolClosed,\n\t\t}, nil)\n\t\t_ = p.closeConnection(conn) // We don't care about errors while closing the connection.\n\t}\n\tp.idleConns = p.idleConns[:0]\n\tfor {\n\t\tw := p.idleConnWait.popFront()\n\t\tif w == nil {\n\t\t\tbreak\n\t\t}\n\t\tw.tryDeliver(nil, ErrPoolClosed)\n\t}\n\tp.idleMu.Unlock()\n\n\t// Collect all conns from the pool and try to deliver ErrPoolClosed to any waiting wantConns\n\t// from newConnWait while holding the createConnectionsCond lock. We can't call removeConnection\n\t// on the connections while holding any locks, so do that after we release the lock.\n\tp.createConnectionsCond.L.Lock()\n\tconns := make([]*connection, 0, len(p.conns))\n\tfor _, conn := range p.conns {\n\t\tconns = append(conns, conn)\n\t}\n\tfor {\n\t\tw := p.newConnWait.popFront()\n\t\tif w == nil {\n\t\t\tbreak\n\t\t}\n\t\tw.tryDeliver(nil, ErrPoolClosed)\n\t}\n\tp.createConnectionsCond.L.Unlock()\n\n\tif mustLogPoolMessage(p) {\n\t\tlogPoolMessage(p, logger.ConnectionPoolClosed)\n\t}\n\n\tif p.monitor != nil {\n\t\tp.monitor.Event(&event.PoolEvent{\n\t\t\tType:    event.ConnectionPoolClosed,\n\t\t\tAddress: p.address.String(),\n\t\t})\n\t}\n\n\t// Now that we're not holding any locks, remove all of the connections we collected from the\n\t// pool.\n\tfor _, conn := range conns {\n\t\t_ = p.removeConnection(conn, reason{\n\t\t\tloggerConn: logger.ReasonConnClosedPoolClosed,\n\t\t\tevent:      event.ReasonPoolClosed,\n\t\t}, nil)\n\t\t_ = p.closeConnection(conn) // We don't care about errors while closing the connection.\n\t}\n}\n\nfunc (p *pool) pinConnectionToCursor() {\n\tatomic.AddUint64(&p.pinnedCursorConnections, 1)\n}\n\nfunc (p *pool) unpinConnectionFromCursor() {\n\t// See https://golang.org/pkg/sync/atomic/#AddUint64 for an explanation of the ^uint64(0) syntax.\n\tatomic.AddUint64(&p.pinnedCursorConnections, ^uint64(0))\n}\n\nfunc (p *pool) pinConnectionToTransaction() {\n\tatomic.AddUint64(&p.pinnedTransactionConnections, 1)\n}\n\nfunc (p *pool) unpinConnectionFromTransaction() {\n\t// See https://golang.org/pkg/sync/atomic/#AddUint64 for an explanation of the ^uint64(0) syntax.\n\tatomic.AddUint64(&p.pinnedTransactionConnections, ^uint64(0))\n}\n\n// checkOut checks out a connection from the pool. If an idle connection is not available, the\n// checkOut enters a queue waiting for either the next idle or new connection. If the pool is not\n// ready, checkOut returns an error.\n// Based partially on https://cs.opensource.google/go/go/+/refs/tags/go1.16.6:src/net/http/transport.go;l=1324\nfunc (p *pool) checkOut(ctx context.Context) (conn *connection, err error) {\n\tif mustLogPoolMessage(p) {\n\t\tlogPoolMessage(p, logger.ConnectionCheckoutStarted)\n\t}\n\n\t// TODO(CSOT): If a Timeout was specified at any level, respect the Timeout is server selection, connection\n\t// TODO checkout.\n\tif p.monitor != nil {\n\t\tp.monitor.Event(&event.PoolEvent{\n\t\t\tType:    event.ConnectionCheckOutStarted,\n\t\t\tAddress: p.address.String(),\n\t\t})\n\t}\n\n\tstart := time.Now()\n\t// Check the pool state while holding a stateMu read lock. If the pool state is not \"ready\",\n\t// return an error. Do all of this while holding the stateMu read lock to prevent a state change between\n\t// checking the state and entering the wait queue. Not holding the stateMu read lock here may\n\t// allow a checkOut() to enter the wait queue after clear() pauses the pool and clears the wait\n\t// queue, resulting in createConnections() doing work while the pool is \"paused\".\n\tp.stateMu.RLock()\n\tswitch p.state {\n\tcase poolClosed:\n\t\tp.stateMu.RUnlock()\n\n\t\tduration := time.Since(start)\n\t\tif mustLogPoolMessage(p) {\n\t\t\tkeysAndValues := logger.KeyValues{\n\t\t\t\tlogger.KeyDurationMS, duration.Milliseconds(),\n\t\t\t\tlogger.KeyReason, logger.ReasonConnCheckoutFailedPoolClosed,\n\t\t\t}\n\n\t\t\tlogPoolMessage(p, logger.ConnectionCheckoutFailed, keysAndValues...)\n\t\t}\n\n\t\tif p.monitor != nil {\n\t\t\tp.monitor.Event(&event.PoolEvent{\n\t\t\t\tType:     event.ConnectionCheckOutFailed,\n\t\t\t\tAddress:  p.address.String(),\n\t\t\t\tDuration: duration,\n\t\t\t\tReason:   event.ReasonPoolClosed,\n\t\t\t})\n\t\t}\n\t\treturn nil, ErrPoolClosed\n\tcase poolPaused:\n\t\t// Wrap poolCleared in a driver.Error so we can add the\n\t\t// \"TransientTransactionError\" label. This will add\n\t\t// \"TransientTransactionError\" to all poolClearedError instances, not\n\t\t// just those that happened during transactions. While that behavior is\n\t\t// different than other places we add \"TransientTransactionError\", it is\n\t\t// consistent with the Transactions specification and simplifies the\n\t\t// code.\n\t\tpcErr := poolClearedError{err: p.lastClearErr, address: p.address}\n\t\terr := driver.Error{\n\t\t\tMessage: pcErr.Error(),\n\t\t\tLabels:  []string{driver.TransientTransactionError},\n\t\t\tWrapped: pcErr,\n\t\t}\n\t\tp.stateMu.RUnlock()\n\n\t\tduration := time.Since(start)\n\t\tif mustLogPoolMessage(p) {\n\t\t\tkeysAndValues := logger.KeyValues{\n\t\t\t\tlogger.KeyDurationMS, duration.Milliseconds(),\n\t\t\t\tlogger.KeyReason, logger.ReasonConnCheckoutFailedError,\n\t\t\t}\n\n\t\t\tlogPoolMessage(p, logger.ConnectionCheckoutFailed, keysAndValues...)\n\t\t}\n\n\t\tif p.monitor != nil {\n\t\t\tp.monitor.Event(&event.PoolEvent{\n\t\t\t\tType:     event.ConnectionCheckOutFailed,\n\t\t\t\tAddress:  p.address.String(),\n\t\t\t\tReason:   event.ReasonConnectionErrored,\n\t\t\t\tDuration: duration,\n\t\t\t\tError:    err,\n\t\t\t})\n\t\t}\n\t\treturn nil, err\n\t}\n\n\tif ctx == nil {\n\t\tctx = context.Background()\n\t}\n\n\t// Create a wantConn, which we will use to request an existing idle or new connection. Always\n\t// cancel the wantConn if checkOut() returned an error to make sure any delivered connections\n\t// are returned to the pool (e.g. if a connection was delivered immediately after the Context\n\t// timed out).\n\tw := newWantConn()\n\tdefer func() {\n\t\tif err != nil {\n\t\t\tw.cancel(p, err)\n\t\t}\n\t}()\n\n\t// Get in the queue for an idle connection. If getOrQueueForIdleConn returns true, it was able to\n\t// immediately deliver an idle connection to the wantConn, so we can return the connection or\n\t// error from the wantConn without waiting for \"ready\".\n\tif delivered := p.getOrQueueForIdleConn(w); delivered {\n\t\t// If delivered = true, we didn't enter the wait queue and will return either a connection\n\t\t// or an error, so unlock the stateMu lock here.\n\t\tp.stateMu.RUnlock()\n\n\t\tduration := time.Since(start)\n\t\tif w.err != nil {\n\t\t\tif mustLogPoolMessage(p) {\n\t\t\t\tkeysAndValues := logger.KeyValues{\n\t\t\t\t\tlogger.KeyDurationMS, duration.Milliseconds(),\n\t\t\t\t\tlogger.KeyReason, logger.ReasonConnCheckoutFailedError,\n\t\t\t\t}\n\n\t\t\t\tlogPoolMessage(p, logger.ConnectionCheckoutFailed, keysAndValues...)\n\t\t\t}\n\n\t\t\tif p.monitor != nil {\n\t\t\t\tp.monitor.Event(&event.PoolEvent{\n\t\t\t\t\tType:     event.ConnectionCheckOutFailed,\n\t\t\t\t\tAddress:  p.address.String(),\n\t\t\t\t\tDuration: duration,\n\t\t\t\t\tReason:   event.ReasonConnectionErrored,\n\t\t\t\t\tError:    w.err,\n\t\t\t\t})\n\t\t\t}\n\t\t\treturn nil, w.err\n\t\t}\n\n\t\tduration = time.Since(start)\n\t\tif mustLogPoolMessage(p) {\n\t\t\tkeysAndValues := logger.KeyValues{\n\t\t\t\tlogger.KeyDriverConnectionID, w.conn.driverConnectionID,\n\t\t\t\tlogger.KeyDurationMS, duration.Milliseconds(),\n\t\t\t}\n\n\t\t\tlogPoolMessage(p, logger.ConnectionCheckedOut, keysAndValues...)\n\t\t}\n\n\t\tif p.monitor != nil {\n\t\t\tp.monitor.Event(&event.PoolEvent{\n\t\t\t\tType:         event.ConnectionCheckedOut,\n\t\t\t\tAddress:      p.address.String(),\n\t\t\t\tConnectionID: w.conn.driverConnectionID,\n\t\t\t\tDuration:     duration,\n\t\t\t})\n\t\t}\n\n\t\treturn w.conn, nil\n\t}\n\n\t// If we didn't get an immediately available idle connection, also get in the queue for a new\n\t// connection while we're waiting for an idle connection.\n\tp.queueForNewConn(w)\n\tp.stateMu.RUnlock()\n\n\t// Wait for either the wantConn to be ready or for the Context to time out.\n\twaitQueueStart := time.Now()\n\tselect {\n\tcase <-w.ready:\n\t\tif w.err != nil {\n\t\t\tduration := time.Since(start)\n\t\t\tif mustLogPoolMessage(p) {\n\t\t\t\tkeysAndValues := logger.KeyValues{\n\t\t\t\t\tlogger.KeyDurationMS, duration.Milliseconds(),\n\t\t\t\t\tlogger.KeyReason, logger.ReasonConnCheckoutFailedError,\n\t\t\t\t\tlogger.KeyError, w.err.Error(),\n\t\t\t\t}\n\n\t\t\t\tlogPoolMessage(p, logger.ConnectionCheckoutFailed, keysAndValues...)\n\t\t\t}\n\n\t\t\tif p.monitor != nil {\n\t\t\t\tp.monitor.Event(&event.PoolEvent{\n\t\t\t\t\tType:     event.ConnectionCheckOutFailed,\n\t\t\t\t\tAddress:  p.address.String(),\n\t\t\t\t\tDuration: duration,\n\t\t\t\t\tReason:   event.ReasonConnectionErrored,\n\t\t\t\t\tError:    w.err,\n\t\t\t\t})\n\t\t\t}\n\n\t\t\treturn nil, w.err\n\t\t}\n\n\t\tduration := time.Since(start)\n\t\tif mustLogPoolMessage(p) {\n\t\t\tkeysAndValues := logger.KeyValues{\n\t\t\t\tlogger.KeyDriverConnectionID, w.conn.driverConnectionID,\n\t\t\t\tlogger.KeyDurationMS, duration.Milliseconds(),\n\t\t\t}\n\n\t\t\tlogPoolMessage(p, logger.ConnectionCheckedOut, keysAndValues...)\n\t\t}\n\n\t\tif p.monitor != nil {\n\t\t\tp.monitor.Event(&event.PoolEvent{\n\t\t\t\tType:         event.ConnectionCheckedOut,\n\t\t\t\tAddress:      p.address.String(),\n\t\t\t\tConnectionID: w.conn.driverConnectionID,\n\t\t\t\tDuration:     duration,\n\t\t\t})\n\t\t}\n\t\treturn w.conn, nil\n\tcase <-ctx.Done():\n\t\twaitQueueDuration := time.Since(waitQueueStart)\n\n\t\tduration := time.Since(start)\n\t\tif mustLogPoolMessage(p) {\n\t\t\tkeysAndValues := logger.KeyValues{\n\t\t\t\tlogger.KeyDurationMS, duration.Milliseconds(),\n\t\t\t\tlogger.KeyReason, logger.ReasonConnCheckoutFailedTimout,\n\t\t\t}\n\n\t\t\tlogPoolMessage(p, logger.ConnectionCheckoutFailed, keysAndValues...)\n\t\t}\n\n\t\tif p.monitor != nil {\n\t\t\tp.monitor.Event(&event.PoolEvent{\n\t\t\t\tType:     event.ConnectionCheckOutFailed,\n\t\t\t\tAddress:  p.address.String(),\n\t\t\t\tDuration: duration,\n\t\t\t\tReason:   event.ReasonTimedOut,\n\t\t\t\tError:    ctx.Err(),\n\t\t\t})\n\t\t}\n\n\t\terr := WaitQueueTimeoutError{\n\t\t\tWrapped:              ctx.Err(),\n\t\t\tmaxPoolSize:          p.maxSize,\n\t\t\ttotalConnections:     p.totalConnectionCount(),\n\t\t\tavailableConnections: p.availableConnectionCount(),\n\t\t\twaitDuration:         waitQueueDuration,\n\t\t}\n\t\tif p.loadBalanced {\n\t\t\terr.pinnedConnections = &pinnedConnections{\n\t\t\t\tcursorConnections:      atomic.LoadUint64(&p.pinnedCursorConnections),\n\t\t\t\ttransactionConnections: atomic.LoadUint64(&p.pinnedTransactionConnections),\n\t\t\t}\n\t\t}\n\t\treturn nil, err\n\t}\n}\n\n// closeConnection closes a connection.\nfunc (p *pool) closeConnection(conn *connection) error {\n\tif conn.pool != p {\n\t\treturn ErrWrongPool\n\t}\n\n\tif atomic.LoadInt64(&conn.state) == connConnected {\n\t\tconn.closeConnectContext()\n\t\tconn.wait() // Make sure that the connection has finished connecting.\n\t}\n\n\terr := conn.close()\n\tif err != nil {\n\t\treturn ConnectionError{ConnectionID: conn.id, Wrapped: err, message: \"failed to close net.Conn\"}\n\t}\n\n\treturn nil\n}\n\nfunc (p *pool) getGenerationForNewConnection(serviceID *bson.ObjectID) uint64 {\n\treturn p.generation.addConnection(serviceID)\n}\n\n// removeConnection removes a connection from the pool and emits a \"ConnectionClosed\" event.\nfunc (p *pool) removeConnection(conn *connection, reason reason, err error) error {\n\tif conn == nil {\n\t\treturn nil\n\t}\n\n\tif conn.pool != p {\n\t\treturn ErrWrongPool\n\t}\n\n\tp.createConnectionsCond.L.Lock()\n\t_, ok := p.conns[conn.driverConnectionID]\n\tif !ok {\n\t\t// If the connection has been removed from the pool already, exit without doing any\n\t\t// additional state changes.\n\t\tp.createConnectionsCond.L.Unlock()\n\t\treturn nil\n\t}\n\tdelete(p.conns, conn.driverConnectionID)\n\t// Signal the createConnectionsCond so any goroutines waiting for a new connection slot in the\n\t// pool will proceed.\n\tp.createConnectionsCond.Signal()\n\tp.createConnectionsCond.L.Unlock()\n\n\t// Only update the generation numbers map if the connection has retrieved its generation number.\n\t// Otherwise, we'd decrement the count for the generation even though it had never been\n\t// incremented.\n\tif conn.hasGenerationNumber() {\n\t\tp.generation.removeConnection(conn.desc.ServiceID)\n\t}\n\n\tif mustLogPoolMessage(p) {\n\t\tkeysAndValues := logger.KeyValues{\n\t\t\tlogger.KeyDriverConnectionID, conn.driverConnectionID,\n\t\t\tlogger.KeyReason, reason.loggerConn,\n\t\t}\n\n\t\tif err != nil {\n\t\t\tkeysAndValues.Add(logger.KeyError, err.Error())\n\t\t}\n\n\t\tlogPoolMessage(p, logger.ConnectionClosed, keysAndValues...)\n\t}\n\n\tif p.monitor != nil {\n\t\tp.monitor.Event(&event.PoolEvent{\n\t\t\tType:         event.ConnectionClosed,\n\t\t\tAddress:      p.address.String(),\n\t\t\tConnectionID: conn.driverConnectionID,\n\t\t\tReason:       reason.event,\n\t\t\tError:        err,\n\t\t})\n\t}\n\n\treturn nil\n}\n\nvar (\n\t// BGReadTimeout is the maximum amount of the to wait when trying to read\n\t// the server reply on a connection after an operation timed out. The\n\t// default is 400ms.\n\t//\n\t// Deprecated: BGReadTimeout is intended for internal use only and may be\n\t// removed or modified at any time.\n\tBGReadTimeout = 400 * time.Millisecond\n\n\t// BGReadCallback is a callback for monitoring the behavior of the\n\t// background-read-on-timeout connection preserving mechanism.\n\t//\n\t// Deprecated: BGReadCallback is intended for internal use only and may be\n\t// removed or modified at any time.\n\tBGReadCallback func(addr string, start, read time.Time, errs []error, connClosed bool)\n)\n\n// bgRead sets a new read deadline on the provided connection and tries to read\n// any bytes returned by the server. If successful, it checks the connection\n// into the provided pool. If there are any errors, it closes the connection.\n//\n// It calls the package-global BGReadCallback function, if set, with the\n// address, timings, and any errors that occurred.\nfunc bgRead(pool *pool, conn *connection, size int32) {\n\tvar err error\n\tstart := time.Now()\n\n\tdefer func() {\n\t\tread := time.Now()\n\t\terrs := make([]error, 0)\n\t\tconnClosed := false\n\t\tif err != nil {\n\t\t\terrs = append(errs, err)\n\t\t\tconnClosed = true\n\t\t\terr = conn.close()\n\t\t\tif err != nil {\n\t\t\t\terrs = append(errs, fmt.Errorf(\"error closing conn after reading: %w\", err))\n\t\t\t}\n\t\t}\n\n\t\t// No matter what happens, always check the connection back into the\n\t\t// pool, which will either make it available for other operations or\n\t\t// remove it from the pool if it was closed.\n\t\terr = pool.checkInNoEvent(conn)\n\t\tif err != nil {\n\t\t\terrs = append(errs, fmt.Errorf(\"error checking in: %w\", err))\n\t\t}\n\n\t\tif BGReadCallback != nil {\n\t\t\tBGReadCallback(conn.addr.String(), start, read, errs, connClosed)\n\t\t}\n\t}()\n\n\terr = conn.nc.SetReadDeadline(time.Now().Add(BGReadTimeout))\n\tif err != nil {\n\t\terr = fmt.Errorf(\"error setting a read deadline: %w\", err)\n\t\treturn\n\t}\n\n\tif size == 0 {\n\t\tvar sizeBuf [4]byte\n\t\t_, err = io.ReadFull(conn.nc, sizeBuf[:])\n\t\tif err != nil {\n\t\t\terr = fmt.Errorf(\"error reading the message size: %w\", err)\n\t\t\treturn\n\t\t}\n\t\tsize, err = conn.parseWmSizeBytes(sizeBuf)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tsize -= 4\n\t}\n\t_, err = io.CopyN(io.Discard, conn.nc, int64(size))\n\tif err != nil {\n\t\terr = fmt.Errorf(\"error discarding %d byte message: %w\", size, err)\n\t}\n}\n\n// checkIn returns an idle connection to the pool. If the connection is perished or the pool is\n// closed, it is removed from the connection pool and closed.\nfunc (p *pool) checkIn(conn *connection) error {\n\tif conn == nil {\n\t\treturn nil\n\t}\n\tif conn.pool != p {\n\t\treturn ErrWrongPool\n\t}\n\n\tif mustLogPoolMessage(p) {\n\t\tkeysAndValues := logger.KeyValues{\n\t\t\tlogger.KeyDriverConnectionID, conn.driverConnectionID,\n\t\t}\n\n\t\tlogPoolMessage(p, logger.ConnectionCheckedIn, keysAndValues...)\n\t}\n\n\tif p.monitor != nil {\n\t\tp.monitor.Event(&event.PoolEvent{\n\t\t\tType:         event.ConnectionCheckedIn,\n\t\t\tConnectionID: conn.driverConnectionID,\n\t\t\tAddress:      conn.addr.String(),\n\t\t})\n\t}\n\n\treturn p.checkInNoEvent(conn)\n}\n\n// checkInNoEvent returns a connection to the pool. It behaves identically to checkIn except it does\n// not publish events. It is only intended for use by pool-internal functions.\nfunc (p *pool) checkInNoEvent(conn *connection) error {\n\tif conn == nil {\n\t\treturn nil\n\t}\n\tif conn.pool != p {\n\t\treturn ErrWrongPool\n\t}\n\n\t// If the connection has an awaiting server response, try to read the\n\t// response in another goroutine before checking it back into the pool.\n\t//\n\t// Do this here because we want to publish checkIn events when the operation\n\t// is done with the connection, not when it's ready to be used again. That\n\t// means that connections in \"awaiting response\" state are checked in but\n\t// not usable, which is not covered by the current pool events. We may need\n\t// to add pool event information in the future to communicate that.\n\tif conn.awaitRemainingBytes != nil {\n\t\tsize := *conn.awaitRemainingBytes\n\t\tconn.awaitRemainingBytes = nil\n\t\tgo bgRead(p, conn, size)\n\t\treturn nil\n\t}\n\n\t// Bump the connection idle start time here because we're about to make the\n\t// connection \"available\". The idle start time is used to determine how long\n\t// a connection has been idle and when it has reached its max idle time and\n\t// should be closed. A connection reaches its max idle time when it has been\n\t// \"available\" in the idle connections stack for more than the configured\n\t// duration (maxIdleTimeMS). Set it before we call connectionPerished(),\n\t// which checks the idle deadline, because a newly \"available\" connection\n\t// should never be perished due to max idle time.\n\tconn.bumpIdleStart()\n\n\tr, perished := connectionPerished(conn)\n\tif !perished && conn.pool.getState() == poolClosed {\n\t\tperished = true\n\t\tr = reason{\n\t\t\tloggerConn: logger.ReasonConnClosedPoolClosed,\n\t\t\tevent:      event.ReasonPoolClosed,\n\t\t}\n\t}\n\tif perished {\n\t\t_ = p.removeConnection(conn, r, nil)\n\t\tgo func() {\n\t\t\t_ = p.closeConnection(conn)\n\t\t}()\n\t\treturn nil\n\t}\n\n\tp.idleMu.Lock()\n\tdefer p.idleMu.Unlock()\n\n\tfor {\n\t\tw := p.idleConnWait.popFront()\n\t\tif w == nil {\n\t\t\tbreak\n\t\t}\n\t\tif w.tryDeliver(conn, nil) {\n\t\t\treturn nil\n\t\t}\n\t}\n\n\tfor _, idle := range p.idleConns {\n\t\tif idle == conn {\n\t\t\treturn fmt.Errorf(\"duplicate idle conn %p in idle connections stack\", conn)\n\t\t}\n\t}\n\n\tp.idleConns = append(p.idleConns, conn)\n\treturn nil\n}\n\n// clear calls clearImpl internally with a false interruptAllConnections value.\nfunc (p *pool) clear(err error, serviceID *bson.ObjectID) {\n\tp.clearImpl(err, serviceID, false)\n}\n\n// clearAll does same as the \"clear\" method but interrupts all connections.\nfunc (p *pool) clearAll(err error, serviceID *bson.ObjectID) {\n\tp.clearImpl(err, serviceID, true)\n}\n\n// interruptConnections interrupts the input connections.\nfunc (p *pool) interruptConnections(conns []*connection) {\n\tfor _, conn := range conns {\n\t\t_ = p.removeConnection(conn, reason{\n\t\t\tloggerConn: logger.ReasonConnClosedStale,\n\t\t\tevent:      event.ReasonStale,\n\t\t}, nil)\n\t\tgo func(c *connection) {\n\t\t\t_ = p.closeConnection(c)\n\t\t}(conn)\n\t}\n}\n\n// clear marks all connections as stale by incrementing the generation number, stops all background\n// goroutines, removes all requests from idleConnWait and newConnWait, and sets the pool state to\n// \"paused\". If serviceID is nil, clear marks all connections as stale. If serviceID is not nil,\n// clear marks only connections associated with the given serviceID stale (for use in load balancer\n// mode).\n// If interruptAllConnections is true, this function calls interruptConnections to interrupt all\n// non-idle connections.\nfunc (p *pool) clearImpl(err error, serviceID *bson.ObjectID, interruptAllConnections bool) {\n\tif p.getState() == poolClosed {\n\t\treturn\n\t}\n\n\tp.generation.clear(serviceID)\n\n\t// If serviceID is nil (i.e. not in load balancer mode), transition the pool to a paused state\n\t// by stopping all background goroutines, clearing the wait queues, and setting the pool state\n\t// to \"paused\".\n\tsendEvent := true\n\tif serviceID == nil {\n\t\t// While holding the stateMu lock, set the pool state to \"paused\" if it's currently \"ready\",\n\t\t// and set lastClearErr to the error that caused the pool to be cleared. If the pool is\n\t\t// already paused, don't send another \"ConnectionPoolCleared\" event.\n\t\tp.stateMu.Lock()\n\t\tif p.state == poolPaused {\n\t\t\tsendEvent = false\n\t\t}\n\t\tif p.state == poolReady {\n\t\t\tp.state = poolPaused\n\t\t}\n\t\tp.lastClearErr = err\n\t\tp.stateMu.Unlock()\n\t}\n\n\tif mustLogPoolMessage(p) {\n\t\tkeysAndValues := logger.KeyValues{\n\t\t\tlogger.KeyServiceID, serviceID,\n\t\t}\n\n\t\tlogPoolMessage(p, logger.ConnectionPoolCleared, keysAndValues...)\n\t}\n\n\tif sendEvent && p.monitor != nil {\n\t\tevent := &event.PoolEvent{\n\t\t\tType:         event.ConnectionPoolCleared,\n\t\t\tAddress:      p.address.String(),\n\t\t\tServiceID:    serviceID,\n\t\t\tInterruption: interruptAllConnections,\n\t\t\tError:        err,\n\t\t}\n\t\tp.monitor.Event(event)\n\t}\n\n\tp.removePerishedConns()\n\tif interruptAllConnections {\n\t\tp.createConnectionsCond.L.Lock()\n\t\tp.idleMu.Lock()\n\n\t\tidleConns := make(map[*connection]bool, len(p.idleConns))\n\t\tfor _, idle := range p.idleConns {\n\t\t\tidleConns[idle] = true\n\t\t}\n\n\t\tconns := make([]*connection, 0, len(p.conns))\n\t\tfor _, conn := range p.conns {\n\t\t\tif _, ok := idleConns[conn]; !ok && p.stale(conn) {\n\t\t\t\tconns = append(conns, conn)\n\t\t\t}\n\t\t}\n\n\t\tp.idleMu.Unlock()\n\t\tp.createConnectionsCond.L.Unlock()\n\n\t\tp.interruptConnections(conns)\n\t}\n\n\tif serviceID == nil {\n\t\tpcErr := poolClearedError{err: err, address: p.address}\n\n\t\t// Clear the idle connections wait queue.\n\t\tp.idleMu.Lock()\n\t\tfor {\n\t\t\tw := p.idleConnWait.popFront()\n\t\t\tif w == nil {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tw.tryDeliver(nil, pcErr)\n\t\t}\n\t\tp.idleMu.Unlock()\n\n\t\t// Clear the new connections wait queue. This effectively pauses the createConnections()\n\t\t// background goroutine because newConnWait is empty and checkOut() won't insert any more\n\t\t// wantConns into newConnWait until the pool is marked \"ready\" again.\n\t\tp.createConnectionsCond.L.Lock()\n\t\tfor {\n\t\t\tw := p.newConnWait.popFront()\n\t\t\tif w == nil {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tw.tryDeliver(nil, pcErr)\n\t\t}\n\t\tp.createConnectionsCond.L.Unlock()\n\t}\n}\n\n// getOrQueueForIdleConn attempts to deliver an idle connection to the given wantConn. If there is\n// an idle connection in the idle connections stack, it pops an idle connection, delivers it to the\n// wantConn, and returns true. If there are no idle connections in the idle connections stack, it\n// adds the wantConn to the idleConnWait queue and returns false.\nfunc (p *pool) getOrQueueForIdleConn(w *wantConn) bool {\n\tp.idleMu.Lock()\n\tdefer p.idleMu.Unlock()\n\n\t// Try to deliver an idle connection from the idleConns stack first.\n\tfor len(p.idleConns) > 0 {\n\t\tconn := p.idleConns[len(p.idleConns)-1]\n\t\tp.idleConns = p.idleConns[:len(p.idleConns)-1]\n\n\t\tif conn == nil {\n\t\t\tcontinue\n\t\t}\n\n\t\tif reason, perished := connectionPerished(conn); perished {\n\t\t\t_ = conn.pool.removeConnection(conn, reason, nil)\n\t\t\tgo func() {\n\t\t\t\t_ = conn.pool.closeConnection(conn)\n\t\t\t}()\n\t\t\tcontinue\n\t\t}\n\n\t\tif !w.tryDeliver(conn, nil) {\n\t\t\t// If we couldn't deliver the conn to w, put it back in the idleConns stack.\n\t\t\tp.idleConns = append(p.idleConns, conn)\n\t\t}\n\n\t\t// If we got here, we tried to deliver an idle conn to w. No matter if tryDeliver() returned\n\t\t// true or false, w is no longer waiting and doesn't need to be added to any wait queues, so\n\t\t// return delivered = true.\n\t\treturn true\n\t}\n\n\tp.idleConnWait.cleanFront()\n\tp.idleConnWait.pushBack(w)\n\treturn false\n}\n\nfunc (p *pool) queueForNewConn(w *wantConn) {\n\tp.createConnectionsCond.L.Lock()\n\tdefer p.createConnectionsCond.L.Unlock()\n\n\tp.newConnWait.cleanFront()\n\tp.newConnWait.pushBack(w)\n\tp.createConnectionsCond.Signal()\n}\n\nfunc (p *pool) totalConnectionCount() int {\n\tp.createConnectionsCond.L.Lock()\n\tdefer p.createConnectionsCond.L.Unlock()\n\n\treturn len(p.conns)\n}\n\nfunc (p *pool) availableConnectionCount() int {\n\tp.idleMu.Lock()\n\tdefer p.idleMu.Unlock()\n\n\treturn len(p.idleConns)\n}\n\n// createConnections creates connections for wantConn requests on the newConnWait queue.\nfunc (p *pool) createConnections(ctx context.Context, wg *sync.WaitGroup) {\n\tdefer wg.Done()\n\n\t// condition returns true if the createConnections() loop should continue and false if it should\n\t// wait. Note that the condition also listens for Context cancellation, which also causes the\n\t// loop to continue, allowing for a subsequent check to return from createConnections().\n\tcondition := func() bool {\n\t\tcheckOutWaiting := p.newConnWait.len() > 0\n\t\tpoolHasSpace := p.maxSize == 0 || uint64(len(p.conns)) < p.maxSize\n\t\tcancelled := ctx.Err() != nil\n\t\treturn (checkOutWaiting && poolHasSpace) || cancelled\n\t}\n\n\t// wait waits for there to be an available wantConn and for the pool to have space for a new\n\t// connection. When the condition becomes true, it creates a new connection and returns the\n\t// waiting wantConn and new connection. If the Context is cancelled or there are any\n\t// errors, wait returns with \"ok = false\".\n\twait := func() (*wantConn, *connection, bool) {\n\t\tp.createConnectionsCond.L.Lock()\n\t\tdefer p.createConnectionsCond.L.Unlock()\n\n\t\tfor !condition() {\n\t\t\tp.createConnectionsCond.Wait()\n\t\t}\n\n\t\tif ctx.Err() != nil {\n\t\t\treturn nil, nil, false\n\t\t}\n\n\t\tp.newConnWait.cleanFront()\n\t\tw := p.newConnWait.popFront()\n\t\tif w == nil {\n\t\t\treturn nil, nil, false\n\t\t}\n\n\t\tconn := newConnection(p.address, p.connOpts...)\n\t\tconn.pool = p\n\t\tconn.driverConnectionID = atomic.AddInt64(&p.nextID, 1)\n\t\tp.conns[conn.driverConnectionID] = conn\n\n\t\treturn w, conn, true\n\t}\n\n\tfor ctx.Err() == nil {\n\t\tw, conn, ok := wait()\n\t\tif !ok {\n\t\t\tcontinue\n\t\t}\n\n\t\tif mustLogPoolMessage(p) {\n\t\t\tkeysAndValues := logger.KeyValues{\n\t\t\t\tlogger.KeyDriverConnectionID, conn.driverConnectionID,\n\t\t\t}\n\n\t\t\tlogPoolMessage(p, logger.ConnectionCreated, keysAndValues...)\n\t\t}\n\n\t\tif p.monitor != nil {\n\t\t\tp.monitor.Event(&event.PoolEvent{\n\t\t\t\tType:         event.ConnectionCreated,\n\t\t\t\tAddress:      p.address.String(),\n\t\t\t\tConnectionID: conn.driverConnectionID,\n\t\t\t})\n\t\t}\n\n\t\tstart := time.Now()\n\t\t// Pass the createConnections context to connect to allow pool close to\n\t\t// cancel connection establishment so shutdown doesn't block indefinitely if\n\t\t// connectTimeout=0.\n\t\t//\n\t\t// Per the specifications, an explicit value of connectTimeout=0 means the\n\t\t// timeout is \"infinite\".\n\n\t\tvar cancel context.CancelFunc\n\n\t\tconnctx := context.Background()\n\t\tif p.connectTimeout != 0 {\n\t\t\tconnctx, cancel = context.WithTimeout(ctx, p.connectTimeout)\n\t\t}\n\n\t\terr := conn.connect(connctx)\n\n\t\tif cancel != nil {\n\t\t\tcancel()\n\t\t}\n\n\t\tif err != nil {\n\t\t\tw.tryDeliver(nil, err)\n\n\t\t\t// If there's an error connecting the new connection, call the handshake error handler\n\t\t\t// that implements the SDAM handshake error handling logic. This must be called after\n\t\t\t// delivering the connection error to the waiting wantConn. If it's called before, the\n\t\t\t// handshake error handler may clear the connection pool, leading to a different error\n\t\t\t// message being delivered to the same waiting wantConn in idleConnWait when the wait\n\t\t\t// queues are cleared.\n\t\t\tif p.handshakeErrFn != nil {\n\t\t\t\tp.handshakeErrFn(err, conn.generation, conn.desc.ServiceID)\n\t\t\t}\n\n\t\t\t_ = p.removeConnection(conn, reason{\n\t\t\t\tloggerConn: logger.ReasonConnClosedError,\n\t\t\t\tevent:      event.ReasonError,\n\t\t\t}, err)\n\n\t\t\t_ = p.closeConnection(conn)\n\n\t\t\tcontinue\n\t\t}\n\n\t\tduration := time.Since(start)\n\t\tif mustLogPoolMessage(p) {\n\t\t\tkeysAndValues := logger.KeyValues{\n\t\t\t\tlogger.KeyDriverConnectionID, conn.driverConnectionID,\n\t\t\t\tlogger.KeyDurationMS, duration.Milliseconds(),\n\t\t\t}\n\n\t\t\tlogPoolMessage(p, logger.ConnectionReady, keysAndValues...)\n\t\t}\n\n\t\tif p.monitor != nil {\n\t\t\tp.monitor.Event(&event.PoolEvent{\n\t\t\t\tType:         event.ConnectionReady,\n\t\t\t\tAddress:      p.address.String(),\n\t\t\t\tConnectionID: conn.driverConnectionID,\n\t\t\t\tDuration:     duration,\n\t\t\t})\n\t\t}\n\n\t\tif w.tryDeliver(conn, nil) {\n\t\t\tcontinue\n\t\t}\n\n\t\t_ = p.checkInNoEvent(conn)\n\t}\n}\n\nfunc (p *pool) maintain(ctx context.Context, wg *sync.WaitGroup) {\n\tdefer wg.Done()\n\n\tticker := time.NewTicker(p.maintainInterval)\n\tdefer ticker.Stop()\n\n\t// remove removes the *wantConn at index i from the slice and returns the new slice. The order\n\t// of the slice is not maintained.\n\tremove := func(arr []*wantConn, i int) []*wantConn {\n\t\tend := len(arr) - 1\n\t\tarr[i], arr[end] = arr[end], arr[i]\n\t\treturn arr[:end]\n\t}\n\n\t// removeNotWaiting removes any wantConns that are no longer waiting from given slice of\n\t// wantConns. That allows maintain() to use the size of its wantConns slice as an indication of\n\t// how many new connection requests are outstanding and subtract that from the number of\n\t// connections to ask for when maintaining minPoolSize.\n\tremoveNotWaiting := func(arr []*wantConn) []*wantConn {\n\t\tfor i := len(arr) - 1; i >= 0; i-- {\n\t\t\tw := arr[i]\n\t\t\tif !w.waiting() {\n\t\t\t\tarr = remove(arr, i)\n\t\t\t}\n\t\t}\n\n\t\treturn arr\n\t}\n\n\twantConns := make([]*wantConn, 0, p.minSize)\n\tdefer func() {\n\t\tfor _, w := range wantConns {\n\t\t\tw.tryDeliver(nil, ErrPoolClosed)\n\t\t}\n\t}()\n\n\tfor {\n\t\tselect {\n\t\tcase <-ticker.C:\n\t\tcase <-p.maintainReady:\n\t\tcase <-ctx.Done():\n\t\t\treturn\n\t\t}\n\n\t\t// Only maintain the pool while it's in the \"ready\" state. If the pool state is not \"ready\",\n\t\t// wait for the next tick or \"ready\" signal. Do all of this while holding the stateMu read\n\t\t// lock to prevent a state change between checking the state and entering the wait queue.\n\t\t// Not holding the stateMu read lock here may allow maintain() to request wantConns after\n\t\t// clear() pauses the pool and clears the wait queue, resulting in createConnections()\n\t\t// doing work while the pool is \"paused\".\n\t\tp.stateMu.RLock()\n\t\tif p.state != poolReady {\n\t\t\tp.stateMu.RUnlock()\n\t\t\tcontinue\n\t\t}\n\n\t\tp.removePerishedConns()\n\n\t\t// Remove any wantConns that are no longer waiting.\n\t\twantConns = removeNotWaiting(wantConns)\n\n\t\t// Figure out how many more wantConns we need to satisfy minPoolSize. Assume that the\n\t\t// outstanding wantConns (i.e. the ones that weren't removed from the slice) will all return\n\t\t// connections when they're ready, so only add wantConns to make up the difference. Limit\n\t\t// the number of connections requested to max 10 at a time to prevent overshooting\n\t\t// minPoolSize in case other checkOut() calls are requesting new connections, too.\n\t\ttotal := p.totalConnectionCount()\n\t\tn := int(p.minSize) - total - len(wantConns)\n\t\tif n > 10 {\n\t\t\tn = 10\n\t\t}\n\n\t\tfor i := 0; i < n; i++ {\n\t\t\tw := newWantConn()\n\t\t\tp.queueForNewConn(w)\n\t\t\twantConns = append(wantConns, w)\n\n\t\t\t// Start a goroutine for each new wantConn, waiting for it to be ready.\n\t\t\tgo func() {\n\t\t\t\t<-w.ready\n\t\t\t\tif w.conn != nil {\n\t\t\t\t\t_ = p.checkInNoEvent(w.conn)\n\t\t\t\t}\n\t\t\t}()\n\t\t}\n\t\tp.stateMu.RUnlock()\n\t}\n}\n\nfunc (p *pool) removePerishedConns() {\n\tp.idleMu.Lock()\n\tdefer p.idleMu.Unlock()\n\n\tfor i := range p.idleConns {\n\t\tconn := p.idleConns[i]\n\t\tif conn == nil {\n\t\t\tcontinue\n\t\t}\n\n\t\tif reason, perished := connectionPerished(conn); perished {\n\t\t\tp.idleConns[i] = nil\n\n\t\t\t_ = p.removeConnection(conn, reason, nil)\n\t\t\tgo func() {\n\t\t\t\t_ = p.closeConnection(conn)\n\t\t\t}()\n\t\t}\n\t}\n\n\tp.idleConns = compact(p.idleConns)\n}\n\n// compact removes any nil pointers from the slice and keeps the non-nil pointers, retaining the\n// order of the non-nil pointers.\nfunc compact(arr []*connection) []*connection {\n\toffset := 0\n\tfor i := range arr {\n\t\tif arr[i] == nil {\n\t\t\tcontinue\n\t\t}\n\t\tarr[offset] = arr[i]\n\t\toffset++\n\t}\n\treturn arr[:offset]\n}\n\n// A wantConn records state about a wanted connection (that is, an active call to checkOut).\n// The conn may be gotten by creating a new connection or by finding an idle connection, or a\n// cancellation may make the conn no longer wanted. These three options are racing against each\n// other and use wantConn to coordinate and agree about the winning outcome.\n// Based on https://cs.opensource.google/go/go/+/refs/tags/go1.16.6:src/net/http/transport.go;l=1174-1240\ntype wantConn struct {\n\tready chan struct{}\n\n\tmu   sync.Mutex // Guards conn, err\n\tconn *connection\n\terr  error\n}\n\nfunc newWantConn() *wantConn {\n\treturn &wantConn{\n\t\tready: make(chan struct{}, 1),\n\t}\n}\n\n// waiting reports whether w is still waiting for an answer (connection or error).\nfunc (w *wantConn) waiting() bool {\n\tselect {\n\tcase <-w.ready:\n\t\treturn false\n\tdefault:\n\t\treturn true\n\t}\n}\n\n// tryDeliver attempts to deliver conn, err to w and reports whether it succeeded.\nfunc (w *wantConn) tryDeliver(conn *connection, err error) bool {\n\tw.mu.Lock()\n\tdefer w.mu.Unlock()\n\n\tif w.conn != nil || w.err != nil {\n\t\treturn false\n\t}\n\n\tw.conn = conn\n\tw.err = err\n\tif w.conn == nil && w.err == nil {\n\t\tpanic(\"x/mongo/driver/topology: internal error: misuse of tryDeliver\")\n\t}\n\n\tclose(w.ready)\n\n\treturn true\n}\n\n// cancel marks w as no longer wanting a result (for example, due to cancellation). If a connection\n// has been delivered already, cancel returns it with p.checkInNoEvent(). Note that the caller must\n// not hold any locks on the pool while calling cancel.\nfunc (w *wantConn) cancel(p *pool, err error) {\n\tif err == nil {\n\t\tpanic(\"x/mongo/driver/topology: internal error: misuse of cancel\")\n\t}\n\n\tw.mu.Lock()\n\tif w.conn == nil && w.err == nil {\n\t\tclose(w.ready) // catch misbehavior in future delivery\n\t}\n\tconn := w.conn\n\tw.conn = nil\n\tw.err = err\n\tw.mu.Unlock()\n\n\tif conn != nil {\n\t\t_ = p.checkInNoEvent(conn)\n\t}\n}\n\n// A wantConnQueue is a queue of wantConns.\n// Based on https://cs.opensource.google/go/go/+/refs/tags/go1.16.6:src/net/http/transport.go;l=1242-1306\ntype wantConnQueue struct {\n\t// This is a queue, not a deque.\n\t// It is split into two stages - head[headPos:] and tail.\n\t// popFront is trivial (headPos++) on the first stage, and\n\t// pushBack is trivial (append) on the second stage.\n\t// If the first stage is empty, popFront can swap the\n\t// first and second stages to remedy the situation.\n\t//\n\t// This two-stage split is analogous to the use of two lists\n\t// in Okasaki's purely functional queue but without the\n\t// overhead of reversing the list when swapping stages.\n\thead    []*wantConn\n\theadPos int\n\ttail    []*wantConn\n}\n\n// len returns the number of items in the queue.\nfunc (q *wantConnQueue) len() int {\n\treturn len(q.head) - q.headPos + len(q.tail)\n}\n\n// pushBack adds w to the back of the queue.\nfunc (q *wantConnQueue) pushBack(w *wantConn) {\n\tq.tail = append(q.tail, w)\n}\n\n// popFront removes and returns the wantConn at the front of the queue.\nfunc (q *wantConnQueue) popFront() *wantConn {\n\tif q.headPos >= len(q.head) {\n\t\tif len(q.tail) == 0 {\n\t\t\treturn nil\n\t\t}\n\t\t// Pick up tail as new head, clear tail.\n\t\tq.head, q.headPos, q.tail = q.tail, 0, q.head[:0]\n\t}\n\tw := q.head[q.headPos]\n\tq.head[q.headPos] = nil\n\tq.headPos++\n\treturn w\n}\n\n// peekFront returns the wantConn at the front of the queue without removing it.\nfunc (q *wantConnQueue) peekFront() *wantConn {\n\tif q.headPos < len(q.head) {\n\t\treturn q.head[q.headPos]\n\t}\n\tif len(q.tail) > 0 {\n\t\treturn q.tail[0]\n\t}\n\treturn nil\n}\n\n// cleanFront pops any wantConns that are no longer waiting from the head of the queue.\nfunc (q *wantConnQueue) cleanFront() {\n\tfor {\n\t\tw := q.peekFront()\n\t\tif w == nil || w.waiting() {\n\t\t\treturn\n\t\t}\n\t\tq.popFront()\n\t}\n}\n"
  },
  {
    "path": "x/mongo/driver/topology/pool_generation_counter.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage topology\n\nimport (\n\t\"sync\"\n\t\"sync/atomic\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n)\n\n// Pool generation state constants.\nconst (\n\tgenerationDisconnected int64 = iota\n\tgenerationConnected\n)\n\n// generationStats represents the version of a pool. It tracks the generation number as well as the number of\n// connections that have been created in the generation.\ntype generationStats struct {\n\tgeneration uint64\n\tnumConns   uint64\n}\n\n// poolGenerationMap tracks the version for each service ID present in a pool. For deployments that are not behind a\n// load balancer, there is only one service ID: bson.NilObjectID. For load-balanced deployments, each server behind\n// the load balancer will have a unique service ID.\ntype poolGenerationMap struct {\n\t// state must be accessed using the atomic package and should be at the beginning of the struct.\n\t// - atomic bug: https://pkg.go.dev/sync/atomic#pkg-note-BUG\n\t// - suggested layout: https://go101.org/article/memory-layout.html\n\tstate         int64\n\tgenerationMap map[bson.ObjectID]*generationStats\n\n\tsync.Mutex\n}\n\nfunc newPoolGenerationMap() *poolGenerationMap {\n\tpgm := &poolGenerationMap{\n\t\tgenerationMap: make(map[bson.ObjectID]*generationStats),\n\t}\n\tpgm.generationMap[bson.NilObjectID] = &generationStats{}\n\treturn pgm\n}\n\nfunc (p *poolGenerationMap) connect() {\n\tatomic.StoreInt64(&p.state, generationConnected)\n}\n\nfunc (p *poolGenerationMap) disconnect() {\n\tatomic.StoreInt64(&p.state, generationDisconnected)\n}\n\n// addConnection increments the connection count for the generation associated with the given service ID and returns the\n// generation number for the connection.\nfunc (p *poolGenerationMap) addConnection(serviceIDPtr *bson.ObjectID) uint64 {\n\tserviceID := getServiceID(serviceIDPtr)\n\tp.Lock()\n\tdefer p.Unlock()\n\n\tstats, ok := p.generationMap[serviceID]\n\tif ok {\n\t\t// If the serviceID is already being tracked, we only need to increment the connection count.\n\t\tstats.numConns++\n\t\treturn stats.generation\n\t}\n\n\t// If the serviceID is untracked, create a new entry with a starting generation number of 0.\n\tstats = &generationStats{\n\t\tnumConns: 1,\n\t}\n\tp.generationMap[serviceID] = stats\n\treturn 0\n}\n\nfunc (p *poolGenerationMap) removeConnection(serviceIDPtr *bson.ObjectID) {\n\tserviceID := getServiceID(serviceIDPtr)\n\tp.Lock()\n\tdefer p.Unlock()\n\n\tstats, ok := p.generationMap[serviceID]\n\tif !ok {\n\t\treturn\n\t}\n\n\t// If the serviceID is being tracked, decrement the connection count and delete this serviceID to prevent the map\n\t// from growing unboundedly. This case would happen if a server behind a load-balancer was permanently removed\n\t// and its connections were pruned after a network error or idle timeout.\n\tstats.numConns--\n\tif stats.numConns == 0 {\n\t\tdelete(p.generationMap, serviceID)\n\t}\n}\n\nfunc (p *poolGenerationMap) clear(serviceIDPtr *bson.ObjectID) {\n\tserviceID := getServiceID(serviceIDPtr)\n\tp.Lock()\n\tdefer p.Unlock()\n\n\tif stats, ok := p.generationMap[serviceID]; ok {\n\t\tstats.generation++\n\t}\n}\n\nfunc (p *poolGenerationMap) stale(serviceIDPtr *bson.ObjectID, knownGeneration uint64) bool {\n\t// If the map has been disconnected, all connections should be considered stale to ensure that they're closed.\n\tif atomic.LoadInt64(&p.state) == generationDisconnected {\n\t\treturn true\n\t}\n\n\tif generation, ok := p.getGeneration(serviceIDPtr); ok {\n\t\treturn knownGeneration < generation\n\t}\n\treturn false\n}\n\nfunc (p *poolGenerationMap) getGeneration(serviceIDPtr *bson.ObjectID) (uint64, bool) {\n\tserviceID := getServiceID(serviceIDPtr)\n\tp.Lock()\n\tdefer p.Unlock()\n\n\tif stats, ok := p.generationMap[serviceID]; ok {\n\t\treturn stats.generation, true\n\t}\n\treturn 0, false\n}\n\nfunc (p *poolGenerationMap) getNumConns(serviceIDPtr *bson.ObjectID) uint64 {\n\tserviceID := getServiceID(serviceIDPtr)\n\tp.Lock()\n\tdefer p.Unlock()\n\n\tif stats, ok := p.generationMap[serviceID]; ok {\n\t\treturn stats.numConns\n\t}\n\treturn 0\n}\n\nfunc getServiceID(oid *bson.ObjectID) bson.ObjectID {\n\tif oid == nil {\n\t\treturn bson.NilObjectID\n\t}\n\treturn *oid\n}\n"
  },
  {
    "path": "x/mongo/driver/topology/pool_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2022-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage topology\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"net\"\n\t\"regexp\"\n\t\"sync\"\n\t\"testing\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/event\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/csot\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/eventtest\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/require\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/address\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/operation\"\n)\n\nfunc TestNewPool(t *testing.T) {\n\tt.Parallel()\n\n\tt.Run(\"minPoolSize should not exceed maxPoolSize\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tp := newPool(poolConfig{MinPoolSize: 100, MaxPoolSize: 10})\n\t\tassert.Equalf(t, uint64(10), p.minSize, \"expected minSize of a pool not to be greater than maxSize\")\n\n\t\tp.close(context.Background())\n\t})\n\tt.Run(\"minPoolSize may exceed maxPoolSize of 0\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tp := newPool(poolConfig{MinPoolSize: 10, MaxPoolSize: 0})\n\t\tassert.Equalf(t, uint64(10), p.minSize, \"expected minSize of a pool to be greater than maxSize of 0\")\n\n\t\tp.close(context.Background())\n\t})\n\tt.Run(\"should be paused\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tp := newPool(poolConfig{})\n\t\tassert.Equalf(t, poolPaused, p.getState(), \"expected new pool to be paused\")\n\n\t\tp.close(context.Background())\n\t})\n}\n\nfunc TestPool_closeConnection(t *testing.T) {\n\tt.Parallel()\n\n\tt.Run(\"can't close connection from different pool\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tcleanup := make(chan struct{})\n\t\tdefer close(cleanup)\n\t\taddr := bootstrapConnections(t, 1, func(nc net.Conn) {\n\t\t\t<-cleanup\n\t\t\t_ = nc.Close()\n\t\t})\n\n\t\tp1 := newPool(poolConfig{\n\t\t\tAddress:        address.Address(addr.String()),\n\t\t\tConnectTimeout: defaultConnectionTimeout,\n\t\t})\n\t\terr := p1.ready()\n\t\trequire.NoError(t, err)\n\n\t\tc, err := p1.checkOut(context.Background())\n\t\trequire.NoError(t, err)\n\n\t\tp2 := newPool(poolConfig{})\n\t\terr = p2.ready()\n\t\trequire.NoError(t, err)\n\n\t\terr = p2.closeConnection(c)\n\t\tassert.Equalf(t, ErrWrongPool, err, \"expected ErrWrongPool error\")\n\n\t\tp1.close(context.Background())\n\t\tp2.close(context.Background())\n\t})\n}\n\nfunc TestPool_close(t *testing.T) {\n\tt.Parallel()\n\n\tt.Run(\"calling close multiple times does not panic\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tp := newPool(poolConfig{\n\t\t\tConnectTimeout: defaultConnectionTimeout,\n\t\t})\n\t\terr := p.ready()\n\t\trequire.NoError(t, err)\n\n\t\tfor i := 0; i < 5; i++ {\n\t\t\tp.close(context.Background())\n\t\t}\n\t})\n\tt.Run(\"closes idle connections\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tcleanup := make(chan struct{})\n\t\tdefer close(cleanup)\n\t\taddr := bootstrapConnections(t, 3, func(nc net.Conn) {\n\t\t\t<-cleanup\n\t\t\t_ = nc.Close()\n\t\t})\n\n\t\td := newdialer(&net.Dialer{})\n\t\tp := newPool(poolConfig{\n\t\t\tAddress:        address.Address(addr.String()),\n\t\t\tConnectTimeout: defaultConnectionTimeout,\n\t\t}, WithDialer(func(Dialer) Dialer { return d }))\n\t\terr := p.ready()\n\t\trequire.NoError(t, err)\n\n\t\tconns := make([]*connection, 3)\n\t\tfor i := range conns {\n\t\t\tconns[i], err = p.checkOut(context.Background())\n\t\t\trequire.NoError(t, err)\n\t\t}\n\t\tfor i := range conns {\n\t\t\terr = p.checkIn(conns[i])\n\t\t\trequire.NoError(t, err)\n\t\t}\n\t\tassert.Equalf(t, 3, d.lenopened(), \"should have opened 3 connections\")\n\t\tassert.Equalf(t, 0, d.lenclosed(), \"should have closed 0 connections\")\n\t\tassert.Equalf(t, 3, p.availableConnectionCount(), \"should have 3 available connections\")\n\t\tassert.Equalf(t, 3, p.totalConnectionCount(), \"should have 3 total connections\")\n\n\t\tp.close(context.Background())\n\t\tassertConnectionsClosed(t, d, 3)\n\t\tassert.Equalf(t, 0, p.availableConnectionCount(), \"should have 0 available connections\")\n\t\tassert.Equalf(t, 0, p.availableConnectionCount(), \"should have 0 total connections\")\n\t})\n\tt.Run(\"closes all open connections\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tcleanup := make(chan struct{})\n\t\tdefer close(cleanup)\n\t\taddr := bootstrapConnections(t, 3, func(nc net.Conn) {\n\t\t\t<-cleanup\n\t\t\t_ = nc.Close()\n\t\t})\n\n\t\td := newdialer(&net.Dialer{})\n\t\tp := newPool(poolConfig{\n\t\t\tAddress:        address.Address(addr.String()),\n\t\t\tConnectTimeout: defaultConnectionTimeout,\n\t\t}, WithDialer(func(Dialer) Dialer { return d }))\n\t\terr := p.ready()\n\t\trequire.NoError(t, err)\n\n\t\tconns := make([]*connection, 3)\n\t\tfor i := range conns {\n\t\t\tconns[i], err = p.checkOut(context.Background())\n\t\t\trequire.NoError(t, err)\n\t\t}\n\t\tfor i := 0; i < 2; i++ {\n\t\t\terr = p.checkIn(conns[i])\n\t\t\trequire.NoError(t, err)\n\t\t}\n\t\tassert.Equalf(t, 3, d.lenopened(), \"should have opened 3 connections\")\n\t\tassert.Equalf(t, 0, d.lenclosed(), \"should have closed 0 connections\")\n\t\tassert.Equalf(t, 2, p.availableConnectionCount(), \"should have 2 available connections\")\n\t\tassert.Equalf(t, 3, p.totalConnectionCount(), \"should have 3 total connections\")\n\n\t\tp.close(context.Background())\n\t\tassertConnectionsClosed(t, d, 3)\n\t\tassert.Equalf(t, 0, p.availableConnectionCount(), \"should have 0 available connections\")\n\t\tassert.Equalf(t, 0, p.totalConnectionCount(), \"should have 0 total connections\")\n\t})\n\tt.Run(\"no race if connections are also connecting\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tcleanup := make(chan struct{})\n\t\tdefer close(cleanup)\n\t\taddr := bootstrapConnections(t, 3, func(nc net.Conn) {\n\t\t\t<-cleanup\n\t\t\t_ = nc.Close()\n\t\t})\n\n\t\tp := newPool(poolConfig{\n\t\t\tAddress:        address.Address(addr.String()),\n\t\t\tConnectTimeout: defaultConnectionTimeout,\n\t\t})\n\t\terr := p.ready()\n\t\trequire.NoError(t, err)\n\n\t\t_, err = p.checkOut(context.Background())\n\t\trequire.NoError(t, err)\n\n\t\tclosed := make(chan struct{})\n\t\tstarted := make(chan struct{})\n\t\tgo func() {\n\t\t\tclose(started)\n\n\t\t\tfor {\n\t\t\t\tselect {\n\t\t\t\tcase <-closed:\n\t\t\t\t\treturn\n\t\t\t\tdefault:\n\t\t\t\t\tc, _ := p.checkOut(context.Background())\n\t\t\t\t\t_ = p.checkIn(c)\n\t\t\t\t\ttime.Sleep(time.Millisecond)\n\t\t\t\t}\n\t\t\t}\n\t\t}()\n\n\t\t// Wait for the background goroutine to start running before trying to close the\n\t\t// connection pool.\n\t\t<-started\n\t\t_, err = p.checkOut(context.Background())\n\t\trequire.NoError(t, err)\n\n\t\tp.close(context.Background())\n\n\t\tclose(closed)\n\t})\n\tt.Run(\"shuts down gracefully if Context has a deadline\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tcleanup := make(chan struct{})\n\t\tdefer close(cleanup)\n\t\taddr := bootstrapConnections(t, 3, func(nc net.Conn) {\n\t\t\t<-cleanup\n\t\t\t_ = nc.Close()\n\t\t})\n\n\t\tp := newPool(poolConfig{\n\t\t\tAddress:        address.Address(addr.String()),\n\t\t\tConnectTimeout: defaultConnectionTimeout,\n\t\t})\n\t\terr := p.ready()\n\t\trequire.NoError(t, err)\n\n\t\t// Check out 2 connections from the pool and add them to a conns slice.\n\t\tconns := make([]*connection, 2)\n\t\tfor i := 0; i < 2; i++ {\n\t\t\tc, err := p.checkOut(context.Background())\n\t\t\trequire.NoError(t, err)\n\n\t\t\tconns[i] = c\n\t\t}\n\n\t\t// Check out a 3rd connection from the pool and immediately check it back in so there is\n\t\t// a mixture of in-use and idle connections.\n\t\tc, err := p.checkOut(context.Background())\n\t\trequire.NoError(t, err)\n\n\t\terr = p.checkIn(c)\n\t\trequire.NoError(t, err)\n\n\t\t// Start a goroutine that waits for the pool to start closing, then checks in the\n\t\t// 2 in-use connections. Assert that both connections are still connected during\n\t\t// graceful shutdown before they are checked in.\n\t\tgo func() {\n\t\t\tfor p.getState() == poolReady {\n\t\t\t\ttime.Sleep(time.Millisecond)\n\t\t\t}\n\t\t\tfor _, c := range conns {\n\t\t\t\tassert.Equalf(t, connConnected, c.state, \"expected conn to still be connected\")\n\n\t\t\t\terr := p.checkIn(c)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t}\n\t\t}()\n\n\t\t// Close the pool with a 1-hour graceful shutdown timeout. Expect that the call to\n\t\t// close() returns when all of the connections are checked in. If close() doesn't return\n\t\t// when all of the connections are checked in, the test will time out.\n\t\tctx, cancel := context.WithTimeout(context.Background(), 1*time.Hour)\n\t\tdefer cancel()\n\t\tp.close(ctx)\n\t})\n\tt.Run(\"closing a Connection does not cause an error after pool is closed\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tcleanup := make(chan struct{})\n\t\tdefer close(cleanup)\n\t\taddr := bootstrapConnections(t, 3, func(nc net.Conn) {\n\t\t\t<-cleanup\n\t\t\t_ = nc.Close()\n\t\t})\n\n\t\tp := newPool(poolConfig{\n\t\t\tAddress:        address.Address(addr.String()),\n\t\t\tConnectTimeout: defaultConnectionTimeout,\n\t\t})\n\t\terr := p.ready()\n\t\trequire.NoError(t, err)\n\n\t\tc, err := p.checkOut(context.Background())\n\t\trequire.NoError(t, err)\n\n\t\tp.close(context.Background())\n\n\t\tc1 := &Connection{connection: c}\n\t\terr = c1.Close()\n\t\trequire.NoError(t, err)\n\t})\n}\n\nfunc TestPool_ready(t *testing.T) {\n\tt.Parallel()\n\n\tt.Run(\"can ready a paused pool\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tcleanup := make(chan struct{})\n\t\tdefer close(cleanup)\n\t\taddr := bootstrapConnections(t, 6, func(nc net.Conn) {\n\t\t\t<-cleanup\n\t\t\t_ = nc.Close()\n\t\t})\n\n\t\tp := newPool(poolConfig{\n\t\t\tAddress:        address.Address(addr.String()),\n\t\t\tConnectTimeout: defaultConnectionTimeout,\n\t\t})\n\t\terr := p.ready()\n\t\trequire.NoError(t, err)\n\n\t\tconns := make([]*connection, 3)\n\t\tfor i := range conns {\n\t\t\tconn, err := p.checkOut(context.Background())\n\t\t\trequire.NoError(t, err)\n\t\t\tconns[i] = conn\n\t\t}\n\t\tassert.Equalf(t, 0, p.availableConnectionCount(), \"should have 0 available connections\")\n\t\tassert.Equalf(t, 3, p.totalConnectionCount(), \"should have 3 total connections\")\n\n\t\tp.clear(nil, nil)\n\t\tfor _, conn := range conns {\n\t\t\terr = p.checkIn(conn)\n\t\t\trequire.NoError(t, err)\n\t\t}\n\t\tassert.Equalf(t, 0, p.availableConnectionCount(), \"should have 0 available connections\")\n\t\tassert.Equalf(t, 0, p.totalConnectionCount(), \"should have 0 total connections\")\n\n\t\terr = p.ready()\n\t\trequire.NoError(t, err)\n\n\t\tfor i := 0; i < 3; i++ {\n\t\t\t_, err := p.checkOut(context.Background())\n\t\t\trequire.NoError(t, err)\n\t\t}\n\t\tassert.Equalf(t, 0, p.availableConnectionCount(), \"should have 0 available connections\")\n\t\tassert.Equalf(t, 3, p.totalConnectionCount(), \"should have 3 total connections\")\n\n\t\tp.close(context.Background())\n\t})\n\tt.Run(\"calling ready multiple times does not return an error\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tp := newPool(poolConfig{})\n\t\tfor i := 0; i < 5; i++ {\n\t\t\terr := p.ready()\n\t\t\trequire.NoError(t, err)\n\t\t}\n\n\t\tp.close(context.Background())\n\t})\n\tt.Run(\"can clear and ready multiple times\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tcleanup := make(chan struct{})\n\t\tdefer close(cleanup)\n\t\taddr := bootstrapConnections(t, 2, func(nc net.Conn) {\n\t\t\t<-cleanup\n\t\t\t_ = nc.Close()\n\t\t})\n\n\t\tp := newPool(poolConfig{\n\t\t\tAddress:        address.Address(addr.String()),\n\t\t\tConnectTimeout: defaultConnectionTimeout,\n\t\t})\n\t\terr := p.ready()\n\t\trequire.NoError(t, err)\n\n\t\tc, err := p.checkOut(context.Background())\n\t\trequire.NoError(t, err)\n\t\terr = p.checkIn(c)\n\t\trequire.NoError(t, err)\n\n\t\tfor i := 0; i < 100; i++ {\n\t\t\terr = p.ready()\n\t\t\trequire.NoError(t, err)\n\n\t\t\tp.clear(nil, nil)\n\t\t}\n\n\t\terr = p.ready()\n\t\trequire.NoError(t, err)\n\n\t\tc, err = p.checkOut(context.Background())\n\t\trequire.NoError(t, err)\n\t\terr = p.checkIn(c)\n\t\trequire.NoError(t, err)\n\n\t\tp.close(context.Background())\n\t})\n\tt.Run(\"can clear and ready multiple times concurrently\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tcleanup := make(chan struct{})\n\t\tdefer close(cleanup)\n\t\taddr := bootstrapConnections(t, 2, func(nc net.Conn) {\n\t\t\t<-cleanup\n\t\t\t_ = nc.Close()\n\t\t})\n\n\t\tp := newPool(poolConfig{\n\t\t\tAddress:        address.Address(addr.String()),\n\t\t\tConnectTimeout: defaultConnectionTimeout,\n\t\t})\n\t\terr := p.ready()\n\t\trequire.NoError(t, err)\n\n\t\tc, err := p.checkOut(context.Background())\n\t\trequire.NoError(t, err)\n\t\terr = p.checkIn(c)\n\t\trequire.NoError(t, err)\n\n\t\tvar wg sync.WaitGroup\n\t\tfor i := 0; i < 10; i++ {\n\t\t\twg.Add(1)\n\t\t\tgo func() {\n\t\t\t\tdefer wg.Done()\n\t\t\t\tfor i := 0; i < 1000; i++ {\n\t\t\t\t\terr := p.ready()\n\t\t\t\t\trequire.NoError(t, err)\n\t\t\t\t}\n\t\t\t}()\n\n\t\t\twg.Add(1)\n\t\t\tgo func() {\n\t\t\t\tdefer wg.Done()\n\t\t\t\tfor i := 0; i < 1000; i++ {\n\t\t\t\t\tp.clear(errors.New(\"test error\"), nil)\n\t\t\t\t}\n\t\t\t}()\n\t\t}\n\n\t\twg.Wait()\n\t\terr = p.ready()\n\t\trequire.NoError(t, err)\n\n\t\tc, err = p.checkOut(context.Background())\n\t\trequire.NoError(t, err)\n\t\terr = p.checkIn(c)\n\t\trequire.NoError(t, err)\n\n\t\tp.close(context.Background())\n\t})\n}\n\nfunc TestPool_checkOut(t *testing.T) {\n\tt.Parallel()\n\n\tt.Run(\"return error when attempting to create new connection\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tdialErr := errors.New(\"create new connection error\")\n\t\tp := newPool(poolConfig{\n\t\t\tAddress:        \"testaddr\",\n\t\t\tConnectTimeout: defaultConnectionTimeout,\n\t\t}, WithDialer(func(Dialer) Dialer {\n\t\t\treturn DialerFunc(func(context.Context, string, string) (net.Conn, error) {\n\t\t\t\treturn nil, dialErr\n\t\t\t})\n\t\t}))\n\t\terr := p.ready()\n\t\trequire.NoError(t, err)\n\n\t\t_, err = p.checkOut(context.Background())\n\t\twant := driver.Error{\n\t\t\tLabels: []string{driver.ErrSystemOverloadedError, driver.ErrRetryableError, driver.NetworkError},\n\t\t\tWrapped: ConnectionError{\n\t\t\t\tWrapped: dialErr,\n\t\t\t\tinit:    true,\n\t\t\t\tmessage: \"failed to connect to testaddr:27017\",\n\t\t\t},\n\t\t}\n\t\tassert.Equalf(t, want, err, \"should return error from calling checkOut()\")\n\t\t// If a connection initialization error occurs during checkOut, removing and closing the\n\t\t// failed connection both happen asynchronously with the checkOut. Wait for up to 2s for\n\t\t// the failed connection to be removed from the pool.\n\t\tassert.Eventuallyf(t,\n\t\t\tfunc() bool {\n\t\t\t\treturn p.totalConnectionCount() == 0\n\t\t\t},\n\t\t\t2*time.Second,\n\t\t\t100*time.Millisecond,\n\t\t\t\"expected pool to have 0 total connections within 10s\")\n\n\t\tp.close(context.Background())\n\t})\n\tt.Run(\"closes perished connections\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tcleanup := make(chan struct{})\n\t\tdefer close(cleanup)\n\t\taddr := bootstrapConnections(t, 2, func(nc net.Conn) {\n\t\t\t<-cleanup\n\t\t\t_ = nc.Close()\n\t\t})\n\n\t\td := newdialer(&net.Dialer{})\n\t\tp := newPool(\n\t\t\tpoolConfig{\n\t\t\t\tAddress:        address.Address(addr.String()),\n\t\t\t\tMaxIdleTime:    time.Millisecond,\n\t\t\t\tConnectTimeout: defaultConnectionTimeout,\n\t\t\t},\n\t\t\tWithDialer(func(Dialer) Dialer { return d }),\n\t\t)\n\t\terr := p.ready()\n\t\trequire.NoError(t, err)\n\n\t\t// Check out a connection and assert that the idle timeout is properly set then check it\n\t\t// back into the pool.\n\t\tc1, err := p.checkOut(context.Background())\n\t\trequire.NoError(t, err)\n\t\tassert.Equalf(t, 1, d.lenopened(), \"should have opened 1 connection\")\n\t\tassert.Equalf(t, 1, p.totalConnectionCount(), \"pool should have 1 total connection\")\n\t\tassert.Equalf(t, time.Millisecond, c1.idleTimeout, \"connection should have a 1ms idle timeout\")\n\n\t\terr = p.checkIn(c1)\n\t\trequire.NoError(t, err)\n\n\t\t// Sleep for more than the 1ms idle timeout and then try to check out a connection.\n\t\t// Expect that the previously checked-out connection is closed because it's idle and a\n\t\t// new connection is created.\n\t\ttime.Sleep(50 * time.Millisecond)\n\t\tc2, err := p.checkOut(context.Background())\n\t\trequire.NoError(t, err)\n\t\t// Assert that the connection pointers are not equal. Don't use \"assert.NotEqual\" because it asserts\n\t\t// non-equality of fields, possibly accessing some fields non-atomically and causing a race condition.\n\t\tassert.True(t, c1 != c2, \"expected a new connection on 2nd check out after idle timeout expires\")\n\t\tassert.Equalf(t, 2, d.lenopened(), \"should have opened 2 connections\")\n\t\tassert.Equalf(t, 1, p.totalConnectionCount(), \"pool should have 1 total connection\")\n\n\t\tp.close(context.Background())\n\t})\n\tt.Run(\"recycles connections\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tcleanup := make(chan struct{})\n\t\tdefer close(cleanup)\n\t\taddr := bootstrapConnections(t, 1, func(nc net.Conn) {\n\t\t\t<-cleanup\n\t\t\t_ = nc.Close()\n\t\t})\n\n\t\td := newdialer(&net.Dialer{})\n\t\tp := newPool(poolConfig{\n\t\t\tAddress:        address.Address(addr.String()),\n\t\t\tConnectTimeout: defaultConnectionTimeout,\n\t\t}, WithDialer(func(Dialer) Dialer { return d }))\n\t\terr := p.ready()\n\t\trequire.NoError(t, err)\n\n\t\tfor i := 0; i < 100; i++ {\n\t\t\tc, err := p.checkOut(context.Background())\n\t\t\trequire.NoError(t, err)\n\n\t\t\terr = p.checkIn(c)\n\t\t\trequire.NoError(t, err)\n\t\t}\n\t\tassert.Equalf(t, 1, d.lenopened(), \"should have opened 1 connection\")\n\n\t\tp.close(context.Background())\n\t})\n\tt.Run(\"cannot checkOut from closed pool\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tcleanup := make(chan struct{})\n\t\tdefer close(cleanup)\n\t\taddr := bootstrapConnections(t, 3, func(nc net.Conn) {\n\t\t\t<-cleanup\n\t\t\t_ = nc.Close()\n\t\t})\n\n\t\tp := newPool(poolConfig{\n\t\t\tAddress:        address.Address(addr.String()),\n\t\t\tConnectTimeout: defaultConnectionTimeout,\n\t\t})\n\t\terr := p.ready()\n\t\trequire.NoError(t, err)\n\n\t\tp.close(context.Background())\n\n\t\t_, err = p.checkOut(context.Background())\n\t\tassert.Equalf(\n\t\t\tt,\n\t\t\tErrPoolClosed,\n\t\t\terr,\n\t\t\t\"expected an error from checkOut() from a closed pool\")\n\t})\n\tt.Run(\"handshaker i/o fails\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tp := newPool(\n\t\t\tpoolConfig{\n\t\t\t\tConnectTimeout: defaultConnectionTimeout,\n\t\t\t},\n\t\t\tWithHandshaker(func(Handshaker) Handshaker {\n\t\t\t\treturn operation.NewHello()\n\t\t\t}),\n\t\t\tWithDialer(func(Dialer) Dialer {\n\t\t\t\treturn DialerFunc(func(context.Context, string, string) (net.Conn, error) {\n\t\t\t\t\treturn &writeFailConn{&net.TCPConn{}}, nil\n\t\t\t\t})\n\t\t\t}),\n\t\t)\n\t\terr := p.ready()\n\t\trequire.NoError(t, err)\n\n\t\t_, err = p.checkOut(context.Background())\n\t\trequire.IsTypef(t, driver.Error{}, err, \"expected a driver.Error\")\n\t\tdriverErr := err.(driver.Error)\n\t\trequire.IsTypef(t, ConnectionError{}, driverErr.Wrapped, \"expected a ConnectionError\")\n\t\tconnErr := driverErr.Wrapped.(ConnectionError)\n\t\tassert.Containsf(\n\t\t\tt,\n\t\t\tconnErr.Unwrap().Error(),\n\t\t\t\"unable to write wire message to network: Write error\",\n\t\t\t\"expected error to contain string\")\n\t\tassert.Equalf(t, 0, p.availableConnectionCount(), \"pool should have 0 available connections\")\n\t\t// On connect() failure, the connection is removed and closed after delivering the error\n\t\t// to checkOut(), so it may still count toward the total connection count briefly. Wait\n\t\t// up to 100ms for the total connection count to reach 0.\n\t\tassert.Eventually(t,\n\t\t\tfunc() bool {\n\t\t\t\treturn p.totalConnectionCount() == 0\n\t\t\t},\n\t\t\t100*time.Millisecond,\n\t\t\t1*time.Millisecond,\n\t\t\t\"expected pool to have 0 total connections within 100ms\")\n\n\t\tp.close(context.Background())\n\t})\n\t// Test that if a checkOut() times out, it returns a WaitQueueTimeout error that wraps a\n\t// context.DeadlineExceeded error.\n\tt.Run(\"wait queue timeout error\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tcleanup := make(chan struct{})\n\t\tdefer close(cleanup)\n\t\taddr := bootstrapConnections(t, 1, func(nc net.Conn) {\n\t\t\t<-cleanup\n\t\t\t_ = nc.Close()\n\t\t})\n\n\t\tp := newPool(poolConfig{\n\t\t\tAddress:        address.Address(addr.String()),\n\t\t\tMaxPoolSize:    1,\n\t\t\tConnectTimeout: defaultConnectionTimeout,\n\t\t})\n\t\terr := p.ready()\n\t\trequire.NoError(t, err)\n\n\t\t// check out first connection.\n\t\t_, err = p.checkOut(context.Background())\n\t\trequire.NoError(t, err)\n\n\t\t// Set a short timeout and check out again.\n\t\tctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)\n\t\tdefer cancel()\n\t\t_, err = p.checkOut(ctx)\n\t\tassert.NotNilf(t, err, \"expected a WaitQueueTimeout error\")\n\n\t\t// Assert that error received is WaitQueueTimeoutError with context deadline exceeded.\n\t\tassert.IsTypef(t, WaitQueueTimeoutError{}, err, \"expected a WaitQueueTimeoutError\")\n\t\tif err, ok := err.(WaitQueueTimeoutError); ok {\n\t\t\tassert.Equalf(t, context.DeadlineExceeded, err.Unwrap(), \"expected wrapped error to be a context.Timeout\")\n\t\t\tassert.Containsf(t, err.Error(), \"timed out\", `expected error message to contain \"timed out\"`)\n\t\t}\n\n\t\tp.close(context.Background())\n\t})\n\t// Test that an indefinitely blocked checkOut() doesn't cause the wait queue to overflow\n\t// if there are many other checkOut() calls that time out. This tests a scenario where a\n\t// wantConnQueue may grow unbounded while a checkOut() is blocked, even if all subsequent\n\t// checkOut() calls time out (due to the behavior of wantConnQueue.cleanFront()).\n\tt.Run(\"wait queue doesn't overflow\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tcleanup := make(chan struct{})\n\t\tdefer close(cleanup)\n\t\taddr := bootstrapConnections(t, 1, func(nc net.Conn) {\n\t\t\t<-cleanup\n\t\t\t_ = nc.Close()\n\t\t})\n\n\t\tp := newPool(poolConfig{\n\t\t\tAddress:        address.Address(addr.String()),\n\t\t\tMaxPoolSize:    1,\n\t\t\tConnectTimeout: defaultConnectionTimeout,\n\t\t})\n\t\terr := p.ready()\n\t\trequire.NoError(t, err)\n\n\t\t// Check out the 1 connection that the pool will create.\n\t\tc, err := p.checkOut(context.Background())\n\t\trequire.NoError(t, err)\n\n\t\t// Start a goroutine that tries to check out another connection with no timeout. Expect\n\t\t// this goroutine to block (wait in the wait queue) until the checked-out connection is\n\t\t// checked-in. Assert that there is no error once checkOut() finally does return.\n\t\tvar wg sync.WaitGroup\n\t\twg.Add(1)\n\t\tgo func() {\n\t\t\tdefer wg.Done()\n\n\t\t\t_, err := p.checkOut(context.Background())\n\t\t\trequire.NoError(t, err)\n\t\t}()\n\n\t\t// Run lots of check-out attempts with a low timeout and assert that each one fails with\n\t\t// a WaitQueueTimeout error. Expect no other errors or panics.\n\t\tfor i := 0; i < 50000; i++ {\n\t\t\tctx, cancel := context.WithTimeout(context.Background(), 1*time.Microsecond)\n\t\t\t_, err := p.checkOut(ctx)\n\t\t\tcancel()\n\t\t\tassert.NotNilf(t, err, \"expected a WaitQueueTimeout error\")\n\t\t\tassert.IsTypef(t, WaitQueueTimeoutError{}, err, \"expected a WaitQueueTimeoutError\")\n\t\t}\n\n\t\t// Check-in the connection we checked out earlier and wait for the checkOut() goroutine\n\t\t// to resume.\n\t\terr = p.checkIn(c)\n\t\trequire.NoError(t, err)\n\t\twg.Wait()\n\n\t\tp.close(context.Background())\n\t})\n\t// Test that checkOut() on a full connection pool creates and returns a new connection\n\t// immediately as soon as the pool is no longer full.\n\tt.Run(\"should return a new connection as soon as the pool isn't full\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tcleanup := make(chan struct{})\n\t\tdefer close(cleanup)\n\t\taddr := bootstrapConnections(t, 3, func(nc net.Conn) {\n\t\t\t<-cleanup\n\t\t\t_ = nc.Close()\n\t\t})\n\n\t\td := newdialer(&net.Dialer{})\n\t\tp := newPool(\n\t\t\tpoolConfig{\n\t\t\t\tAddress:        address.Address(addr.String()),\n\t\t\t\tMaxPoolSize:    2,\n\t\t\t\tConnectTimeout: defaultConnectionTimeout,\n\t\t\t},\n\t\t\tWithDialer(func(Dialer) Dialer { return d }),\n\t\t)\n\t\terr := p.ready()\n\t\trequire.NoError(t, err)\n\n\t\t// Check out two connections (MaxPoolSize) so that subsequent checkOut() calls should\n\t\t// block until a connection is checked back in or removed from the pool.\n\t\tc, err := p.checkOut(context.Background())\n\t\trequire.NoError(t, err)\n\t\t_, err = p.checkOut(context.Background())\n\t\trequire.NoError(t, err)\n\t\tassert.Equalf(t, 2, d.lenopened(), \"should have opened 2 connection\")\n\t\tassert.Equalf(t, 2, p.totalConnectionCount(), \"pool should have 2 total connection\")\n\t\tassert.Equalf(t, 0, p.availableConnectionCount(), \"pool should have 0 idle connection\")\n\n\t\t// Run a checkOut() with timeout and expect it to time out because the pool is at\n\t\t// MaxPoolSize and no connections are checked in or removed from the pool.\n\t\tctx, cancel := context.WithTimeout(context.Background(), 1*time.Millisecond)\n\t\tdefer cancel()\n\t\t_, err = p.checkOut(ctx)\n\t\tassert.Equalf(\n\t\t\tt,\n\t\t\tcontext.DeadlineExceeded,\n\t\t\terr.(WaitQueueTimeoutError).Wrapped,\n\t\t\t\"expected wrapped error to be a context.DeadlineExceeded\")\n\n\t\t// Start a goroutine that closes one of the checked-out connections and checks it in.\n\t\t// Expect that the checked-in connection is closed and allows blocked checkOut() to\n\t\t// complete. Assert that the time between checking in the closed connection and when the\n\t\t// checkOut() completes is within 100ms.\n\t\tvar start time.Time\n\t\tgo func() {\n\t\t\trequire.NoError(t, c.close())\n\n\t\t\tstart = time.Now()\n\t\t\terr := p.checkIn(c)\n\t\t\trequire.NoError(t, err)\n\t\t}()\n\t\t_, err = p.checkOut(context.Background())\n\t\trequire.NoError(t, err)\n\t\tassert.WithinDurationf(\n\t\t\tt,\n\t\t\ttime.Now(),\n\t\t\tstart,\n\t\t\t100*time.Millisecond,\n\t\t\t\"expected checkOut to complete within 100ms of checking in a closed connection\")\n\n\t\tassert.Equalf(t, 1, d.lenclosed(), \"should have closed 1 connection\")\n\t\tassert.Equalf(t, 3, d.lenopened(), \"should have opened 3 connection\")\n\t\tassert.Equalf(t, 2, p.totalConnectionCount(), \"pool should have 2 total connection\")\n\t\tassert.Equalf(t, 0, p.availableConnectionCount(), \"pool should have 0 idle connection\")\n\n\t\tp.close(context.Background())\n\t})\n\tt.Run(\"canceled context in wait queue\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tcleanup := make(chan struct{})\n\t\tdefer close(cleanup)\n\t\taddr := bootstrapConnections(t, 1, func(nc net.Conn) {\n\t\t\t<-cleanup\n\t\t\t_ = nc.Close()\n\t\t})\n\n\t\tp := newPool(poolConfig{\n\t\t\tAddress:        address.Address(addr.String()),\n\t\t\tMaxPoolSize:    1,\n\t\t\tConnectTimeout: defaultConnectionTimeout,\n\t\t})\n\t\terr := p.ready()\n\t\trequire.NoError(t, err)\n\n\t\t// Check out first connection.\n\t\t_, err = p.checkOut(context.Background())\n\t\trequire.NoError(t, err)\n\n\t\t// Use a canceled context to check out another connection.\n\t\tcancelCtx, cancel := context.WithCancel(context.Background())\n\t\tcancel()\n\t\t_, err = p.checkOut(cancelCtx)\n\t\tassert.NotNilf(t, err, \"expected a non-nil error\")\n\n\t\t// Assert that error received is WaitQueueTimeoutError with context canceled.\n\t\tassert.IsTypef(t, WaitQueueTimeoutError{}, err, \"expected a WaitQueueTimeoutError\")\n\t\tif err, ok := err.(WaitQueueTimeoutError); ok {\n\t\t\tassert.Equalf(t, context.Canceled, err.Unwrap(), \"expected wrapped error to be a context.Canceled\")\n\t\t\tassert.Containsf(t, err.Error(), \"canceled\", `expected error message to contain \"canceled\"`)\n\t\t}\n\n\t\tp.close(context.Background())\n\t})\n}\n\nfunc TestPool_checkIn(t *testing.T) {\n\tt.Parallel()\n\n\tt.Run(\"cannot return same connection to pool twice\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tcleanup := make(chan struct{})\n\t\tdefer close(cleanup)\n\t\taddr := bootstrapConnections(t, 1, func(nc net.Conn) {\n\t\t\t<-cleanup\n\t\t\t_ = nc.Close()\n\t\t})\n\n\t\tp := newPool(poolConfig{\n\t\t\tAddress:        address.Address(addr.String()),\n\t\t\tConnectTimeout: defaultConnectionTimeout,\n\t\t})\n\t\terr := p.ready()\n\t\trequire.NoError(t, err)\n\n\t\tc, err := p.checkOut(context.Background())\n\t\trequire.NoError(t, err)\n\t\tassert.Equalf(t, 0, p.availableConnectionCount(), \"should be no idle connections in pool\")\n\t\tassert.Equalf(t, 1, p.totalConnectionCount(), \"should be 1 total connection in pool\")\n\n\t\terr = p.checkIn(c)\n\t\trequire.NoError(t, err)\n\n\t\terr = p.checkIn(c)\n\t\tassert.NotNilf(t, err, \"expected an error trying to return the same conn to the pool twice\")\n\n\t\tassert.Equalf(t, 1, p.availableConnectionCount(), \"should have returned 1 idle connection to the pool\")\n\t\tassert.Equalf(t, 1, p.totalConnectionCount(), \"should have 1 total connection in pool\")\n\n\t\tp.close(context.Background())\n\t})\n\tt.Run(\"closes connections if the pool is closed\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tcleanup := make(chan struct{})\n\t\tdefer close(cleanup)\n\t\taddr := bootstrapConnections(t, 1, func(nc net.Conn) {\n\t\t\t<-cleanup\n\t\t\t_ = nc.Close()\n\t\t})\n\n\t\td := newdialer(&net.Dialer{})\n\t\tp := newPool(poolConfig{\n\t\t\tAddress:        address.Address(addr.String()),\n\t\t\tConnectTimeout: defaultConnectionTimeout,\n\t\t}, WithDialer(func(Dialer) Dialer { return d }))\n\t\terr := p.ready()\n\t\trequire.NoError(t, err)\n\n\t\tc, err := p.checkOut(context.Background())\n\t\trequire.NoError(t, err)\n\t\tassert.Equalf(t, 0, d.lenclosed(), \"should have closed 0 connections\")\n\t\tassert.Equalf(t, 0, p.availableConnectionCount(), \"should have 0 idle connections in pool\")\n\t\tassert.Equalf(t, 1, p.totalConnectionCount(), \"should have 1 total connection in pool\")\n\n\t\tp.close(context.Background())\n\n\t\terr = p.checkIn(c)\n\t\trequire.NoError(t, err)\n\t\tassert.Equalf(t, 1, d.lenclosed(), \"should have closed 1 connection\")\n\t\tassert.Equalf(t, 0, p.availableConnectionCount(), \"should have 0 idle connections in pool\")\n\t\tassert.Equalf(t, 0, p.totalConnectionCount(), \"should have 0 total connection in pool\")\n\t})\n\tt.Run(\"can't checkIn a connection from different pool\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tcleanup := make(chan struct{})\n\t\tdefer close(cleanup)\n\t\taddr := bootstrapConnections(t, 1, func(nc net.Conn) {\n\t\t\t<-cleanup\n\t\t\t_ = nc.Close()\n\t\t})\n\n\t\tp1 := newPool(poolConfig{\n\t\t\tAddress:        address.Address(addr.String()),\n\t\t\tConnectTimeout: defaultConnectionTimeout,\n\t\t})\n\t\terr := p1.ready()\n\t\trequire.NoError(t, err)\n\n\t\tc, err := p1.checkOut(context.Background())\n\t\trequire.NoError(t, err)\n\n\t\tp2 := newPool(poolConfig{})\n\t\terr = p2.ready()\n\t\trequire.NoError(t, err)\n\n\t\terr = p2.checkIn(c)\n\t\tassert.Equalf(t, ErrWrongPool, err, \"expected ErrWrongPool error\")\n\n\t\tp1.close(context.Background())\n\t\tp2.close(context.Background())\n\t})\n\tt.Run(\"bumps the connection idle deadline\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tcleanup := make(chan struct{})\n\t\tdefer close(cleanup)\n\t\taddr := bootstrapConnections(t, 1, func(nc net.Conn) {\n\t\t\t<-cleanup\n\t\t\t_ = nc.Close()\n\t\t})\n\n\t\td := newdialer(&net.Dialer{})\n\t\tp := newPool(poolConfig{\n\t\t\tAddress:        address.Address(addr.String()),\n\t\t\tMaxIdleTime:    100 * time.Millisecond,\n\t\t\tConnectTimeout: defaultConnectionTimeout,\n\t\t}, WithDialer(func(Dialer) Dialer { return d }))\n\t\terr := p.ready()\n\t\trequire.NoError(t, err)\n\t\tdefer p.close(context.Background())\n\n\t\tc, err := p.checkOut(context.Background())\n\t\trequire.NoError(t, err)\n\n\t\t// Sleep for 110ms, which will exceed the 100ms connection idle timeout. Then check the\n\t\t// connection back in and expect that it is not closed because checkIn() should bump the\n\t\t// connection idle deadline.\n\t\ttime.Sleep(110 * time.Millisecond)\n\t\terr = p.checkIn(c)\n\t\trequire.NoError(t, err)\n\n\t\tassert.Equalf(t, 0, d.lenclosed(), \"should have closed 0 connections\")\n\t\tassert.Equalf(t, 1, p.availableConnectionCount(), \"should have 1 idle connections in pool\")\n\t\tassert.Equalf(t, 1, p.totalConnectionCount(), \"should have 1 total connection in pool\")\n\t})\n\tt.Run(\"sets minPoolSize connection idle deadline\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tcleanup := make(chan struct{})\n\t\tdefer close(cleanup)\n\t\taddr := bootstrapConnections(t, 4, func(nc net.Conn) {\n\t\t\t<-cleanup\n\t\t\t_ = nc.Close()\n\t\t})\n\n\t\td := newdialer(&net.Dialer{})\n\t\tp := newPool(poolConfig{\n\t\t\tAddress:        address.Address(addr.String()),\n\t\t\tMinPoolSize:    3,\n\t\t\tMaxIdleTime:    10 * time.Millisecond,\n\t\t\tConnectTimeout: defaultConnectionTimeout,\n\t\t}, WithDialer(func(Dialer) Dialer { return d }))\n\t\terr := p.ready()\n\t\trequire.NoError(t, err)\n\t\tdefer p.close(context.Background())\n\n\t\t// Wait for maintain() to open 3 connections.\n\t\tassertConnectionsOpened(t, d, 3)\n\n\t\t// Sleep for 100ms, which will exceed the 10ms connection idle timeout, then try to check\n\t\t// out a connection. Expect that all minPoolSize connections checked into the pool by\n\t\t// maintain() have passed their idle deadline, so checkOut() closes all 3 connections\n\t\t// and tries to create a new connection.\n\t\ttime.Sleep(100 * time.Millisecond)\n\t\t_, err = p.checkOut(context.Background())\n\t\trequire.NoError(t, err)\n\n\t\tassertConnectionsClosed(t, d, 3)\n\t\tassert.Equalf(t, 4, d.lenopened(), \"should have opened 4 connections\")\n\t\tassert.Equalf(t, 0, p.availableConnectionCount(), \"should have 0 idle connections in pool\")\n\t\tassert.Equalf(t, 1, p.totalConnectionCount(), \"should have 1 total connection in pool\")\n\t})\n}\n\nfunc TestPool_maintain(t *testing.T) {\n\tt.Parallel()\n\n\tt.Run(\"creates MinPoolSize connections shortly after calling ready\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tcleanup := make(chan struct{})\n\t\tdefer close(cleanup)\n\t\taddr := bootstrapConnections(t, 3, func(nc net.Conn) {\n\t\t\t<-cleanup\n\t\t\t_ = nc.Close()\n\t\t})\n\n\t\td := newdialer(&net.Dialer{})\n\t\tp := newPool(poolConfig{\n\t\t\tAddress:        address.Address(addr.String()),\n\t\t\tMinPoolSize:    3,\n\t\t\tConnectTimeout: defaultConnectionTimeout,\n\t\t}, WithDialer(func(Dialer) Dialer { return d }))\n\t\terr := p.ready()\n\t\trequire.NoError(t, err)\n\n\t\tassertConnectionsOpened(t, d, 3)\n\t\tassert.Equalf(t, 3, p.availableConnectionCount(), \"should be 3 idle connections in pool\")\n\t\tassert.Equalf(t, 3, p.totalConnectionCount(), \"should be 3 total connection in pool\")\n\n\t\tp.close(context.Background())\n\t})\n\tt.Run(\"when MinPoolSize > MaxPoolSize should not exceed MaxPoolSize connections\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tcleanup := make(chan struct{})\n\t\tdefer close(cleanup)\n\t\taddr := bootstrapConnections(t, 20, func(nc net.Conn) {\n\t\t\t<-cleanup\n\t\t\t_ = nc.Close()\n\t\t})\n\n\t\td := newdialer(&net.Dialer{})\n\t\tp := newPool(poolConfig{\n\t\t\tAddress:        address.Address(addr.String()),\n\t\t\tMinPoolSize:    20,\n\t\t\tMaxPoolSize:    2,\n\t\t\tConnectTimeout: defaultConnectionTimeout,\n\t\t}, WithDialer(func(Dialer) Dialer { return d }))\n\t\terr := p.ready()\n\t\trequire.NoError(t, err)\n\n\t\tassertConnectionsOpened(t, d, 2)\n\t\tassert.Equalf(t, 2, p.availableConnectionCount(), \"should be 2 idle connections in pool\")\n\t\tassert.Equalf(t, 2, p.totalConnectionCount(), \"should be 2 total connection in pool\")\n\n\t\tp.close(context.Background())\n\t})\n\tt.Run(\"removes perished connections\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tcleanup := make(chan struct{})\n\t\tdefer close(cleanup)\n\t\taddr := bootstrapConnections(t, 5, func(nc net.Conn) {\n\t\t\t<-cleanup\n\t\t\t_ = nc.Close()\n\t\t})\n\n\t\td := newdialer(&net.Dialer{})\n\t\tp := newPool(poolConfig{\n\t\t\tAddress: address.Address(addr.String()),\n\t\t\t// Set the pool's maintain interval to 10ms so that it allows the test to run quickly.\n\t\t\tMaintainInterval: 10 * time.Millisecond,\n\t\t\tConnectTimeout:   defaultConnectionTimeout,\n\t\t}, WithDialer(func(Dialer) Dialer { return d }))\n\t\terr := p.ready()\n\t\trequire.NoError(t, err)\n\n\t\t// Check out and check in 3 connections. Assert that there are 3 total and 3 idle\n\t\t// connections in the pool.\n\t\tconns := make([]*connection, 3)\n\t\tfor i := range conns {\n\t\t\tconns[i], err = p.checkOut(context.Background())\n\t\t\trequire.NoError(t, err)\n\t\t}\n\t\tfor _, c := range conns {\n\t\t\terr = p.checkIn(c)\n\t\t\trequire.NoError(t, err)\n\t\t}\n\t\tassert.Equalf(t, 3, d.lenopened(), \"should have opened 3 connections\")\n\t\tassert.Equalf(t, 3, p.availableConnectionCount(), \"should be 3 idle connections in pool\")\n\t\tassert.Equalf(t, 3, p.totalConnectionCount(), \"should be 3 total connection in pool\")\n\n\t\t// Manually make two of the connections in the idle connections stack perished due to\n\t\t// passing the connection's idle deadline. Assert that maintain() closes the two\n\t\t// perished connections and removes them from the pool.\n\t\tp.idleMu.Lock()\n\t\tfor i := 0; i < 2; i++ {\n\t\t\tp.idleConns[i].idleTimeout = time.Millisecond\n\t\t\tp.idleConns[i].idleStart.Store(time.Now().Add(-1 * time.Hour))\n\t\t}\n\t\tp.idleMu.Unlock()\n\t\tassertConnectionsClosed(t, d, 2)\n\t\tassert.Equalf(t, 1, p.availableConnectionCount(), \"should be 1 idle connections in pool\")\n\t\tassert.Equalf(t, 1, p.totalConnectionCount(), \"should be 1 total connection in pool\")\n\n\t\tp.close(context.Background())\n\t})\n\tt.Run(\"removes perished connections and replaces them to maintain MinPoolSize\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tcleanup := make(chan struct{})\n\t\tdefer close(cleanup)\n\t\taddr := bootstrapConnections(t, 5, func(nc net.Conn) {\n\t\t\t<-cleanup\n\t\t\t_ = nc.Close()\n\t\t})\n\n\t\td := newdialer(&net.Dialer{})\n\t\tp := newPool(poolConfig{\n\t\t\tAddress:     address.Address(addr.String()),\n\t\t\tMinPoolSize: 3,\n\t\t\t// Set the pool's maintain interval to 10ms so that it allows the test to run quickly.\n\t\t\tMaintainInterval: 10 * time.Millisecond,\n\t\t\tConnectTimeout:   defaultConnectionTimeout,\n\t\t}, WithDialer(func(Dialer) Dialer { return d }))\n\t\terr := p.ready()\n\t\trequire.NoError(t, err)\n\t\tassertConnectionsOpened(t, d, 3)\n\t\tassert.Equalf(t, 3, p.availableConnectionCount(), \"should be 3 idle connections in pool\")\n\t\tassert.Equalf(t, 3, p.totalConnectionCount(), \"should be 3 total connection in pool\")\n\n\t\tp.idleMu.Lock()\n\t\tfor i := 0; i < 2; i++ {\n\t\t\tp.idleConns[i].idleTimeout = time.Millisecond\n\t\t\tp.idleConns[i].idleStart.Store(time.Now().Add(-1 * time.Hour))\n\t\t}\n\t\tp.idleMu.Unlock()\n\t\tassertConnectionsClosed(t, d, 2)\n\t\tassertConnectionsOpened(t, d, 5)\n\t\tassert.Equalf(t, 3, p.availableConnectionCount(), \"should be 3 idle connections in pool\")\n\t\tassert.Equalf(t, 3, p.totalConnectionCount(), \"should be 3 total connection in pool\")\n\n\t\tp.close(context.Background())\n\t})\n}\n\nfunc TestBackgroundRead(t *testing.T) {\n\tt.Parallel()\n\n\tnewBGReadCallback := func(errsCh chan []error) func(string, time.Time, time.Time, []error, bool) {\n\t\treturn func(_ string, _, _ time.Time, errs []error, _ bool) {\n\t\t\terrsCh <- errs\n\t\t\tclose(errsCh)\n\t\t}\n\t}\n\n\tt.Run(\"incomplete read of message header\", func(t *testing.T) {\n\t\terrsCh := make(chan []error)\n\t\tvar originalCallback func(string, time.Time, time.Time, []error, bool)\n\t\toriginalCallback, BGReadCallback = BGReadCallback, newBGReadCallback(errsCh)\n\t\tt.Cleanup(func() {\n\t\t\tBGReadCallback = originalCallback\n\t\t})\n\n\t\ttimeout := 10 * time.Millisecond\n\n\t\tcleanup := make(chan struct{})\n\t\tdefer close(cleanup)\n\t\taddr := bootstrapConnections(t, 1, func(nc net.Conn) {\n\t\t\tdefer func() {\n\t\t\t\t<-cleanup\n\t\t\t\t_ = nc.Close()\n\t\t\t}()\n\n\t\t\t_, err := nc.Write([]byte{10, 0, 0})\n\t\t\trequire.NoError(t, err)\n\t\t})\n\n\t\tp := newPool(\n\t\t\tpoolConfig{Address: address.Address(addr.String())},\n\t\t)\n\t\tdefer p.close(context.Background())\n\t\terr := p.ready()\n\t\trequire.NoError(t, err)\n\n\t\tconn, err := p.checkOut(context.Background())\n\t\trequire.NoError(t, err)\n\t\tctx, cancel := csot.WithTimeout(context.Background(), &timeout)\n\t\tdefer cancel()\n\t\t_, err = conn.readWireMessage(ctx)\n\t\tregex := regexp.MustCompile(\n\t\t\t`^connection\\(.*\\[-\\d+\\]\\) incomplete read of message header: context deadline exceeded: client timed out waiting for server response: read tcp 127.0.0.1:.*->127.0.0.1:.*: i\\/o timeout$`,\n\t\t)\n\t\tassert.True(t, regex.MatchString(err.Error()), \"error %q does not match pattern %q\", err, regex)\n\t\tassert.Nil(t, conn.awaitRemainingBytes, \"conn.awaitRemainingBytes should be nil\")\n\t\tclose(errsCh) // this line causes a double close if BGReadCallback is ever called.\n\t})\n\tt.Run(\"timeout reading message header, successful background read\", func(t *testing.T) {\n\t\terrsCh := make(chan []error)\n\t\tvar originalCallback func(string, time.Time, time.Time, []error, bool)\n\t\toriginalCallback, BGReadCallback = BGReadCallback, newBGReadCallback(errsCh)\n\t\tt.Cleanup(func() {\n\t\t\tBGReadCallback = originalCallback\n\t\t})\n\n\t\ttimeout := 10 * time.Millisecond\n\n\t\taddr := bootstrapConnections(t, 1, func(nc net.Conn) {\n\t\t\tdefer func() {\n\t\t\t\t_ = nc.Close()\n\t\t\t}()\n\n\t\t\t// Wait until the operation times out, then write an full message.\n\t\t\ttime.Sleep(timeout * 2)\n\t\t\t_, err := nc.Write([]byte{10, 0, 0, 0, 0, 0, 0, 0, 0, 0})\n\t\t\trequire.NoError(t, err)\n\t\t})\n\n\t\tp := newPool(\n\t\t\tpoolConfig{Address: address.Address(addr.String())},\n\t\t)\n\t\tdefer p.close(context.Background())\n\t\terr := p.ready()\n\t\trequire.NoError(t, err)\n\n\t\tconn, err := p.checkOut(context.Background())\n\t\trequire.NoError(t, err)\n\t\tctx, cancel := csot.WithTimeout(context.Background(), &timeout)\n\t\tdefer cancel()\n\t\t_, err = conn.readWireMessage(ctx)\n\t\tregex := regexp.MustCompile(\n\t\t\t`^connection\\(.*\\[-\\d+\\]\\) incomplete read of message header: context deadline exceeded: client timed out waiting for server response: read tcp 127.0.0.1:.*->127.0.0.1:.*: i\\/o timeout$`,\n\t\t)\n\t\tassert.True(t, regex.MatchString(err.Error()), \"error %q does not match pattern %q\", err, regex)\n\t\terr = p.checkIn(conn)\n\t\trequire.NoError(t, err)\n\t\tvar bgErrs []error\n\t\tselect {\n\t\tcase bgErrs = <-errsCh:\n\t\tcase <-time.After(3 * time.Second):\n\t\t\tassert.Fail(t, \"did not receive expected error after waiting for 3 seconds\")\n\t\t}\n\t\trequire.Len(t, bgErrs, 0, \"expected no error from bgRead()\")\n\t})\n\tt.Run(\"timeout reading message header, incomplete head during background read\", func(t *testing.T) {\n\t\terrsCh := make(chan []error)\n\t\tvar originalCallback func(string, time.Time, time.Time, []error, bool)\n\t\toriginalCallback, BGReadCallback = BGReadCallback, newBGReadCallback(errsCh)\n\t\tt.Cleanup(func() {\n\t\t\tBGReadCallback = originalCallback\n\t\t})\n\n\t\ttimeout := 10 * time.Millisecond\n\n\t\taddr := bootstrapConnections(t, 1, func(nc net.Conn) {\n\t\t\tdefer func() {\n\t\t\t\t_ = nc.Close()\n\t\t\t}()\n\n\t\t\t// Wait until the operation times out, then write an incomplete head.\n\t\t\ttime.Sleep(timeout * 2)\n\t\t\t_, err := nc.Write([]byte{10, 0, 0})\n\t\t\trequire.NoError(t, err)\n\t\t})\n\n\t\tp := newPool(\n\t\t\tpoolConfig{Address: address.Address(addr.String())},\n\t\t)\n\t\tdefer p.close(context.Background())\n\t\terr := p.ready()\n\t\trequire.NoError(t, err)\n\n\t\tconn, err := p.checkOut(context.Background())\n\t\trequire.NoError(t, err)\n\t\tctx, cancel := csot.WithTimeout(context.Background(), &timeout)\n\t\tdefer cancel()\n\t\t_, err = conn.readWireMessage(ctx)\n\t\tregex := regexp.MustCompile(\n\t\t\t`^connection\\(.*\\[-\\d+\\]\\) incomplete read of message header: context deadline exceeded: client timed out waiting for server response: read tcp 127.0.0.1:.*->127.0.0.1:.*: i\\/o timeout$`,\n\t\t)\n\t\tassert.True(t, regex.MatchString(err.Error()), \"error %q does not match pattern %q\", err, regex)\n\t\terr = p.checkIn(conn)\n\t\trequire.NoError(t, err)\n\t\tvar bgErrs []error\n\t\tselect {\n\t\tcase bgErrs = <-errsCh:\n\t\tcase <-time.After(3 * time.Second):\n\t\t\tassert.Fail(t, \"did not receive expected error after waiting for 3 seconds\")\n\t\t}\n\t\trequire.Len(t, bgErrs, 1, \"expected 1 error from bgRead()\")\n\t\tassert.EqualError(t, bgErrs[0], \"error reading the message size: unexpected EOF\")\n\t})\n\tt.Run(\"timeout reading message header, background read timeout\", func(t *testing.T) {\n\t\terrsCh := make(chan []error)\n\t\tvar originalCallback func(string, time.Time, time.Time, []error, bool)\n\t\toriginalCallback, BGReadCallback = BGReadCallback, newBGReadCallback(errsCh)\n\t\tt.Cleanup(func() {\n\t\t\tBGReadCallback = originalCallback\n\t\t})\n\n\t\ttimeout := 10 * time.Millisecond\n\n\t\tcleanup := make(chan struct{})\n\t\tdefer close(cleanup)\n\t\taddr := bootstrapConnections(t, 1, func(nc net.Conn) {\n\t\t\tdefer func() {\n\t\t\t\t<-cleanup\n\t\t\t\t_ = nc.Close()\n\t\t\t}()\n\n\t\t\t// Wait until the operation times out, then write an incomplete\n\t\t\t// message.\n\t\t\ttime.Sleep(timeout * 2)\n\t\t\t_, err := nc.Write([]byte{10, 0, 0, 0, 0, 0, 0, 0})\n\t\t\trequire.NoError(t, err)\n\t\t})\n\n\t\tp := newPool(\n\t\t\tpoolConfig{Address: address.Address(addr.String())},\n\t\t)\n\t\tdefer p.close(context.Background())\n\t\terr := p.ready()\n\t\trequire.NoError(t, err)\n\n\t\tconn, err := p.checkOut(context.Background())\n\t\trequire.NoError(t, err)\n\t\tctx, cancel := csot.WithTimeout(context.Background(), &timeout)\n\t\tdefer cancel()\n\t\t_, err = conn.readWireMessage(ctx)\n\t\tregex := regexp.MustCompile(\n\t\t\t`^connection\\(.*\\[-\\d+\\]\\) incomplete read of message header: context deadline exceeded: client timed out waiting for server response: read tcp 127.0.0.1:.*->127.0.0.1:.*: i\\/o timeout$`,\n\t\t)\n\t\tassert.True(t, regex.MatchString(err.Error()), \"error %q does not match pattern %q\", err, regex)\n\t\terr = p.checkIn(conn)\n\t\trequire.NoError(t, err)\n\t\tvar bgErrs []error\n\t\tselect {\n\t\tcase bgErrs = <-errsCh:\n\t\tcase <-time.After(3 * time.Second):\n\t\t\tassert.Fail(t, \"did not receive expected error after waiting for 3 seconds\")\n\t\t}\n\t\trequire.Len(t, bgErrs, 1, \"expected 1 error from bgRead()\")\n\t\twantErr := regexp.MustCompile(\n\t\t\t`^error discarding 6 byte message: read tcp 127.0.0.1:.*->127.0.0.1:.*: i\\/o timeout$`,\n\t\t)\n\t\tassert.True(t, wantErr.MatchString(bgErrs[0].Error()), \"error %q does not match pattern %q\", bgErrs[0], wantErr)\n\t})\n\tt.Run(\"timeout reading full message, successful background read\", func(t *testing.T) {\n\t\terrsCh := make(chan []error)\n\t\tvar originalCallback func(string, time.Time, time.Time, []error, bool)\n\t\toriginalCallback, BGReadCallback = BGReadCallback, newBGReadCallback(errsCh)\n\t\tt.Cleanup(func() {\n\t\t\tBGReadCallback = originalCallback\n\t\t})\n\n\t\ttimeout := 10 * time.Millisecond\n\n\t\taddr := bootstrapConnections(t, 1, func(nc net.Conn) {\n\t\t\tdefer func() {\n\t\t\t\t_ = nc.Close()\n\t\t\t}()\n\n\t\t\tvar err error\n\t\t\t_, err = nc.Write([]byte{12, 0, 0, 0, 0, 0, 0, 0, 1})\n\t\t\trequire.NoError(t, err)\n\t\t\ttime.Sleep(timeout * 2)\n\t\t\t// write a complete message\n\t\t\t_, err = nc.Write([]byte{2, 3, 4})\n\t\t\trequire.NoError(t, err)\n\t\t})\n\n\t\tp := newPool(\n\t\t\tpoolConfig{Address: address.Address(addr.String())},\n\t\t)\n\t\tdefer p.close(context.Background())\n\t\terr := p.ready()\n\t\trequire.NoError(t, err)\n\n\t\tconn, err := p.checkOut(context.Background())\n\t\trequire.NoError(t, err)\n\t\tctx, cancel := csot.WithTimeout(context.Background(), &timeout)\n\t\tdefer cancel()\n\t\t_, err = conn.readWireMessage(ctx)\n\t\tregex := regexp.MustCompile(\n\t\t\t`^connection\\(.*\\[-\\d+\\]\\) incomplete read of full message: context deadline exceeded: client timed out waiting for server response: read tcp 127.0.0.1:.*->127.0.0.1:.*: i\\/o timeout$`,\n\t\t)\n\t\tassert.True(t, regex.MatchString(err.Error()), \"error %q does not match pattern %q\", err, regex)\n\t\terr = p.checkIn(conn)\n\t\trequire.NoError(t, err)\n\t\tvar bgErrs []error\n\t\tselect {\n\t\tcase bgErrs = <-errsCh:\n\t\tcase <-time.After(3 * time.Second):\n\t\t\tassert.Fail(t, \"did not receive expected error after waiting for 3 seconds\")\n\t\t}\n\t\trequire.Len(t, bgErrs, 0, \"expected no error from bgRead()\")\n\t})\n\tt.Run(\"timeout reading full message, background read EOF\", func(t *testing.T) {\n\t\terrsCh := make(chan []error)\n\t\tvar originalCallback func(string, time.Time, time.Time, []error, bool)\n\t\toriginalCallback, BGReadCallback = BGReadCallback, newBGReadCallback(errsCh)\n\t\tt.Cleanup(func() {\n\t\t\tBGReadCallback = originalCallback\n\t\t})\n\n\t\ttimeout := 10 * time.Millisecond\n\n\t\taddr := bootstrapConnections(t, 1, func(nc net.Conn) {\n\t\t\tdefer func() {\n\t\t\t\t_ = nc.Close()\n\t\t\t}()\n\n\t\t\tvar err error\n\t\t\t_, err = nc.Write([]byte{12, 0, 0, 0, 0, 0, 0, 0, 1})\n\t\t\trequire.NoError(t, err)\n\t\t\ttime.Sleep(timeout * 2)\n\t\t\t// write an incomplete message\n\t\t\t_, err = nc.Write([]byte{2})\n\t\t\trequire.NoError(t, err)\n\t\t})\n\n\t\tp := newPool(\n\t\t\tpoolConfig{Address: address.Address(addr.String())},\n\t\t)\n\t\tdefer p.close(context.Background())\n\t\terr := p.ready()\n\t\trequire.NoError(t, err)\n\n\t\tconn, err := p.checkOut(context.Background())\n\t\trequire.NoError(t, err)\n\t\tctx, cancel := csot.WithTimeout(context.Background(), &timeout)\n\t\tdefer cancel()\n\t\t_, err = conn.readWireMessage(ctx)\n\t\tregex := regexp.MustCompile(\n\t\t\t`^connection\\(.*\\[-\\d+\\]\\) incomplete read of full message: context deadline exceeded: client timed out waiting for server response: read tcp 127.0.0.1:.*->127.0.0.1:.*: i\\/o timeout$`,\n\t\t)\n\t\tassert.True(t, regex.MatchString(err.Error()), \"error %q does not match pattern %q\", err, regex)\n\t\terr = p.checkIn(conn)\n\t\trequire.NoError(t, err)\n\t\tvar bgErrs []error\n\t\tselect {\n\t\tcase bgErrs = <-errsCh:\n\t\tcase <-time.After(3 * time.Second):\n\t\t\tassert.Fail(t, \"did not receive expected error after waiting for 3 seconds\")\n\t\t}\n\t\trequire.Len(t, bgErrs, 1, \"expected 1 error from bgRead()\")\n\t\tassert.EqualError(t, bgErrs[0], \"error discarding 3 byte message: EOF\")\n\t})\n}\n\nfunc assertConnectionsClosed(t *testing.T, dialer *dialer, count int) {\n\tt.Helper()\n\n\tstart := time.Now()\n\tfor {\n\t\tif dialer.lenclosed() == count {\n\t\t\treturn\n\t\t}\n\t\tif time.Since(start) > 3*time.Second {\n\t\t\tt.Errorf(\n\t\t\t\t\"Waited for 3 seconds for %d connections to be closed, but got %d\",\n\t\t\t\tcount,\n\t\t\t\tdialer.lenclosed())\n\t\t\treturn\n\t\t}\n\t\ttime.Sleep(100 * time.Millisecond)\n\t}\n}\n\nfunc assertConnectionsOpened(t *testing.T, dialer *dialer, count int) {\n\tt.Helper()\n\n\tstart := time.Now()\n\tfor {\n\t\tif dialer.lenopened() == count {\n\t\t\treturn\n\t\t}\n\t\tif time.Since(start) > 3*time.Second {\n\t\t\tt.Errorf(\n\t\t\t\t\"Waited for 3 seconds for %d connections to be opened, but got %d\",\n\t\t\t\tcount,\n\t\t\t\tdialer.lenopened())\n\t\t\treturn\n\t\t}\n\t\ttime.Sleep(100 * time.Millisecond)\n\t}\n}\n\nfunc TestPool_PoolMonitor(t *testing.T) {\n\tt.Parallel()\n\n\tt.Run(\"records durations\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tcleanup := make(chan struct{})\n\t\tdefer close(cleanup)\n\n\t\t// Create a listener that responds to exactly 1 connection. All\n\t\t// subsequent connection requests should fail.\n\t\taddr := bootstrapConnections(t, 1, func(nc net.Conn) {\n\t\t\t<-cleanup\n\t\t\t_ = nc.Close()\n\t\t})\n\n\t\ttpm := eventtest.NewTestPoolMonitor()\n\t\tdialer := &net.Dialer{}\n\t\tp := newPool(\n\t\t\tpoolConfig{\n\t\t\t\tAddress:     address.Address(addr.String()),\n\t\t\t\tPoolMonitor: tpm.PoolMonitor,\n\t\t\t},\n\t\t\t// Add a 10ms delay to dialing so the test is reliable on operating\n\t\t\t// systems that can't measure very short durations (e.g. Windows).\n\t\t\tWithDialer(func(Dialer) Dialer {\n\t\t\t\treturn DialerFunc(func(ctx context.Context, n, a string) (net.Conn, error) {\n\t\t\t\t\ttime.Sleep(10 * time.Millisecond)\n\t\t\t\t\treturn dialer.DialContext(ctx, n, a)\n\t\t\t\t})\n\t\t\t}))\n\n\t\terr := p.ready()\n\t\trequire.NoError(t, err, \"ready error\")\n\n\t\t// Check out a connection to trigger \"ConnectionReady\" and\n\t\t// \"ConnectionCheckedOut\" events.\n\t\tconn, err := p.checkOut(context.Background())\n\t\trequire.NoError(t, err, \"checkOut error\")\n\n\t\t// Close the connection so the next checkOut tries to create a new\n\t\t// connection.\n\t\terr = conn.close()\n\t\trequire.NoError(t, err, \"close error\")\n\n\t\terr = p.checkIn(conn)\n\t\trequire.NoError(t, err, \"checkIn error\")\n\n\t\t// Try to check out another connection to trigger a\n\t\t// \"ConnectionCheckOutFailed\" event.\n\t\t_, err = p.checkOut(context.Background())\n\t\trequire.Error(t, err, \"expected a checkOut error\")\n\n\t\tp.close(context.Background())\n\n\t\tevents := tpm.Events(func(evt *event.PoolEvent) bool {\n\t\t\tswitch evt.Type {\n\t\t\tcase \"ConnectionReady\", \"ConnectionCheckedOut\", \"ConnectionCheckOutFailed\":\n\t\t\t\treturn true\n\t\t\t}\n\t\t\treturn false\n\t\t})\n\n\t\trequire.Lenf(t, events, 3, \"expected there to be 3 pool events\")\n\n\t\tassert.Equal(t, events[0].Type, \"ConnectionReady\")\n\t\tassert.Positive(t,\n\t\t\tevents[0].Duration,\n\t\t\t\"expected ConnectionReady Duration to be set\")\n\n\t\tassert.Equal(t, events[1].Type, \"ConnectionCheckedOut\")\n\t\tassert.Positive(t,\n\t\t\tevents[1].Duration,\n\t\t\t\"expected ConnectionCheckedOut Duration to be set\")\n\n\t\tassert.Equal(t, events[2].Type, \"ConnectionCheckOutFailed\")\n\t\tassert.Positive(t,\n\t\t\tevents[2].Duration,\n\t\t\t\"expected ConnectionCheckOutFailed Duration to be set\")\n\t})\n}\n\nfunc TestPool_Error(t *testing.T) {\n\tt.Parallel()\n\n\tt.Run(\"should have TransientTransactionError\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tp := newPool(poolConfig{})\n\t\tassert.Equalf(t, poolPaused, p.getState(), \"expected new pool to be paused\")\n\n\t\t// Since new pool is paused, checkout should throw PoolClearedError.\n\t\t_, err := p.checkOut(context.Background())\n\t\tvar le driver.Error\n\t\tif errors.As(err, &le) {\n\t\t\tassert.ErrorIs(t, poolClearedError{}, le.Unwrap(), \"expect error to be PoolClearedError\")\n\t\t\tassert.True(t, le.HasErrorLabel(driver.TransientTransactionError), `expected error to include the \"TransientTransactionError\" label`)\n\t\t} else {\n\t\t\tt.Errorf(\"expected labeled error, got %v\", err)\n\t\t}\n\n\t\tp.close(context.Background())\n\t})\n}\n"
  },
  {
    "path": "x/mongo/driver/topology/rtt_monitor.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage topology\n\nimport (\n\t\"container/list\"\n\t\"context\"\n\t\"fmt\"\n\t\"sync\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/mnet\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/operation\"\n)\n\nconst (\n\trttAlphaValue             = 0.2\n\tminRTTSamplesForMovingMin = 2\n\tmaxRTTSamplesForMovingMin = 10\n)\n\ntype rttConfig struct {\n\t// The minimum interval between RTT measurements. The actual interval may be greater if running\n\t// the operation takes longer than the interval.\n\tinterval time.Duration\n\n\tminRTTWindow       time.Duration\n\tcreateConnectionFn func() *connection\n\tconnectTimeout     time.Duration\n\tcreateOperationFn  func(*mnet.Connection) *operation.Hello\n}\n\ntype rttMonitor struct {\n\tmu sync.RWMutex // mu guards samples, offset, minRTT, averageRTT, and averageRTTSet\n\n\t// connMu guards connecting and disconnecting. This is necessary since\n\t// disconnecting will await the cancellation of a started connection. The\n\t// use case for rttMonitor.connect needs to be goroutine safe.\n\tconnMu                 sync.Mutex\n\taverageRTT             time.Duration\n\taverageRTTSet          bool\n\tmovingMin              *list.List\n\tminRTT                 time.Duration\n\tstddevRTT              time.Duration\n\tstddevSum              float64\n\tcallsToAppendMovingMin int\n\n\tcloseWg  sync.WaitGroup\n\tcfg      *rttConfig\n\tctx      context.Context\n\tcancelFn context.CancelFunc\n}\n\nvar _ driver.RTTMonitor = &rttMonitor{}\n\nfunc newRTTMonitor(cfg *rttConfig) *rttMonitor {\n\tif cfg.interval <= 0 {\n\t\tpanic(\"RTT monitor interval must be greater than 0\")\n\t}\n\n\tctx, cancel := context.WithCancel(context.Background())\n\n\treturn &rttMonitor{\n\t\tcfg:       cfg,\n\t\tctx:       ctx,\n\t\tcancelFn:  cancel,\n\t\tmovingMin: list.New(),\n\t}\n}\n\nfunc (r *rttMonitor) connect() {\n\tr.connMu.Lock()\n\tdefer r.connMu.Unlock()\n\n\tr.closeWg.Add(1)\n\n\tgo func() {\n\t\tdefer r.closeWg.Done()\n\n\t\tr.start()\n\t}()\n}\n\nfunc (r *rttMonitor) disconnect() {\n\tr.connMu.Lock()\n\tdefer r.connMu.Unlock()\n\n\tr.cancelFn()\n\n\t// Wait for the existing connection to complete.\n\tr.closeWg.Wait()\n}\n\nfunc (r *rttMonitor) start() {\n\tvar conn *connection\n\tdefer func() {\n\t\tif conn != nil {\n\t\t\t// If the connection exists, we need to wait for it to be connected because\n\t\t\t// conn.connect() and conn.close() cannot be called concurrently. If the connection\n\t\t\t// wasn't successfully opened, its state was set back to disconnected, so calling\n\t\t\t// conn.close() will be a no-op.\n\t\t\tconn.closeConnectContext()\n\t\t\tconn.wait()\n\t\t\t_ = conn.close()\n\t\t}\n\t}()\n\n\tticker := time.NewTicker(r.cfg.interval)\n\tdefer ticker.Stop()\n\n\tfor {\n\t\tconn = r.cfg.createConnectionFn()\n\n\t\tctx, cancel := context.WithTimeout(r.ctx, r.cfg.connectTimeout)\n\n\t\terr := conn.connect(ctx)\n\t\tcancel()\n\n\t\t// Add an RTT sample from the new connection handshake and start a runHellos() loop if we\n\t\t// successfully established the new connection. Otherwise, close the connection and try to\n\t\t// create another new connection.\n\t\tif err == nil {\n\t\t\tr.runHellos(conn)\n\t\t\tr.addSample(conn.helloRTT)\n\t\t}\n\n\t\t// Close any connection here because we're either about to try to create another new\n\t\t// connection or we're about to exit the loop.\n\t\t_ = conn.close()\n\n\t\t// If a connection error happens quickly, always wait for the monitoring interval to try\n\t\t// to create a new connection to prevent creating connections too quickly.\n\t\tselect {\n\t\tcase <-ticker.C:\n\t\tcase <-r.ctx.Done():\n\t\t\treturn\n\t\t}\n\t}\n}\n\n// runHellos runs \"hello\" operations in a loop using the provided connection, measuring and\n// recording the operation durations as RTT samples. If it encounters any errors, it returns.\nfunc (r *rttMonitor) runHellos(conn *connection) {\n\tticker := time.NewTicker(r.cfg.interval)\n\tdefer ticker.Stop()\n\n\tfor {\n\t\t// Assume that the connection establishment recorded the first RTT sample, so wait for the\n\t\t// first tick before trying to record another RTT sample.\n\t\tselect {\n\t\tcase <-ticker.C:\n\t\tcase <-r.ctx.Done():\n\t\t\treturn\n\t\t}\n\n\t\t// Create a Context with the operation timeout specified in the RTT monitor config. If a\n\t\t// timeout is not set in the RTT monitor config, default to the connection's\n\t\t// \"connectTimeoutMS\". The purpose of the timeout is to allow the RTT monitor to continue\n\t\t// monitoring server RTTs after an operation gets stuck. An operation can get stuck if the\n\t\t// server or a proxy stops responding to requests on the RTT connection but does not close\n\t\t// the TCP socket, effectively creating an operation that will never complete. We expect\n\t\t// that \"connectTimeoutMS\" provides at least enough time for a single round trip.\n\t\tctx, cancel := context.WithTimeout(r.ctx, r.cfg.connectTimeout)\n\n\t\tstart := time.Now()\n\t\ticonn := mnet.NewConnection(initConnection{conn})\n\n\t\terr := r.cfg.createOperationFn(iconn).Execute(ctx)\n\t\tcancel()\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\t// Only record a sample if the \"hello\" operation was successful. If it was not successful,\n\t\t// the operation may not have actually performed a complete round trip, so the duration may\n\t\t// be artificially short.\n\t\tr.addSample(time.Since(start))\n\t}\n}\n\n// reset sets the average, min, and stddev RTT to 0. This should only be called from the server monitor\n// when an error occurs during a server check. Errors in the RTT monitor should not reset the RTTs.\nfunc (r *rttMonitor) reset() {\n\tr.mu.Lock()\n\tdefer r.mu.Unlock()\n\n\tr.movingMin = list.New()\n\tr.averageRTT = 0\n\tr.averageRTTSet = false\n\tr.stddevSum = 0\n\tr.callsToAppendMovingMin = 0\n}\n\n// appendMovingMin will append the RTT to the movingMin list which tracks a\n// minimum RTT within the last \"minRTTSamplesForMovingMin\" RTT samples.\nfunc (r *rttMonitor) appendMovingMin(rtt time.Duration) {\n\tr.callsToAppendMovingMin++\n\n\tif r.movingMin == nil || rtt < 0 {\n\t\treturn\n\t}\n\n\tif r.movingMin.Len() == maxRTTSamplesForMovingMin {\n\t\tr.movingMin.Remove(r.movingMin.Front())\n\t}\n\n\tr.movingMin.PushBack(rtt)\n\n\t// Collect a sum of stddevs over maxRTTSamplesForMovingMin calls, ignore if calls are less than max\n\tif r.callsToAppendMovingMin >= maxRTTSamplesForMovingMin {\n\t\tstddev := standardDeviationList(r.movingMin)\n\t\tr.stddevSum += stddev\n\t}\n}\n\n// min will return the minimum value in the movingMin list.\nfunc (r *rttMonitor) min() time.Duration {\n\tif r.movingMin == nil || r.movingMin.Len() < minRTTSamplesForMovingMin {\n\t\treturn 0\n\t}\n\n\tvar min time.Duration\n\tfor e := r.movingMin.Front(); e != nil; e = e.Next() {\n\t\tval := e.Value.(time.Duration)\n\n\t\tif min == 0 || val < min {\n\t\t\tmin = val\n\t\t}\n\t}\n\n\treturn min\n}\n\n// stddev will return the current moving stddev.\nfunc (r *rttMonitor) stddev() time.Duration {\n\tvar stddev time.Duration\n\n\tif r.callsToAppendMovingMin < maxRTTSamplesForMovingMin {\n\t\treturn 0\n\t}\n\n\t// Get the number of times stddev was updated and calculate the average stddev\n\tfrequency := (r.callsToAppendMovingMin + 1) - maxRTTSamplesForMovingMin\n\tstddev = time.Duration(r.stddevSum / float64(frequency))\n\n\treturn stddev\n}\n\nfunc (r *rttMonitor) addSample(rtt time.Duration) {\n\t// Lock for the duration of this method. We're doing compuationally inexpensive work very infrequently, so lock\n\t// contention isn't expected.\n\tr.mu.Lock()\n\tdefer r.mu.Unlock()\n\n\tr.appendMovingMin(rtt)\n\tr.minRTT = r.min()\n\tr.stddevRTT = r.stddev()\n\n\tif !r.averageRTTSet {\n\t\tr.averageRTT = rtt\n\t\tr.averageRTTSet = true\n\t\treturn\n\t}\n\n\tr.averageRTT = time.Duration(rttAlphaValue*float64(rtt) + (1-rttAlphaValue)*float64(r.averageRTT))\n}\n\n// EWMA returns the exponentially weighted moving average observed round-trip time.\nfunc (r *rttMonitor) EWMA() time.Duration {\n\tr.mu.RLock()\n\tdefer r.mu.RUnlock()\n\n\treturn r.averageRTT\n}\n\n// Min returns the minimum observed round-trip time over the window period.\nfunc (r *rttMonitor) Min() time.Duration {\n\tr.mu.RLock()\n\tdefer r.mu.RUnlock()\n\n\treturn r.minRTT\n}\n\n// Stats returns stringified stats of the current state of the monitor.\nfunc (r *rttMonitor) Stats() string {\n\tr.mu.RLock()\n\tdefer r.mu.RUnlock()\n\n\treturn fmt.Sprintf(\n\t\t\"network round-trip time stats: moving avg: %v, min: %v, moving stddev: %v\",\n\t\tr.averageRTT,\n\t\tr.minRTT,\n\t\tr.stddevRTT)\n}\n"
  },
  {
    "path": "x/mongo/driver/topology/rtt_monitor_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2022-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage topology\n\nimport (\n\t\"bytes\"\n\t\"container/list\"\n\t\"context\"\n\t\"io\"\n\t\"net\"\n\t\"sync\"\n\t\"sync/atomic\"\n\t\"testing\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/require\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/address\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/drivertest\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/mnet\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/operation\"\n)\n\nfunc makeHelloReply() []byte {\n\tdoc := bsoncore.NewDocumentBuilder().AppendInt32(\"ok\", 1).Build()\n\treturn drivertest.MakeReply(doc)\n}\n\nvar _ net.Conn = &mockSlowConn{}\n\ntype mockSlowConn struct {\n\treader *bytes.Reader\n\tdelay  time.Duration\n\tclosed atomic.Value\n}\n\n// newMockSlowConn returns a net.Conn that reads from the specified response after blocking for a\n// delay duration. Calls to Write() reset the read buffer, so subsequent Read() calls read from the\n// beginning of the provided response.\nfunc newMockSlowConn(response []byte, delay time.Duration) *mockSlowConn {\n\tvar closed atomic.Value\n\tclosed.Store(false)\n\n\treturn &mockSlowConn{\n\t\treader: bytes.NewReader(response),\n\t\tdelay:  delay,\n\t\tclosed: closed,\n\t}\n}\n\nfunc (msc *mockSlowConn) Read(b []byte) (int, error) {\n\ttime.Sleep(msc.delay)\n\tif msc.closed.Load().(bool) {\n\t\treturn 0, io.ErrUnexpectedEOF\n\t}\n\treturn msc.reader.Read(b)\n}\n\nfunc (msc *mockSlowConn) Write(b []byte) (int, error) {\n\tif msc.closed.Load().(bool) {\n\t\treturn 0, io.ErrUnexpectedEOF\n\t}\n\t_, err := msc.reader.Seek(0, io.SeekStart)\n\treturn len(b), err\n}\n\n// Close closes the mock connection. All subsequent calls to Read or Write return error\n// io.ErrUnexpectedEOF. It is not safe to call Close concurrently with Read or Write.\nfunc (msc *mockSlowConn) Close() error {\n\tmsc.closed.Store(true)\n\treturn nil\n}\n\nfunc (*mockSlowConn) LocalAddr() net.Addr                { return nil }\nfunc (*mockSlowConn) RemoteAddr() net.Addr               { return nil }\nfunc (*mockSlowConn) SetDeadline(_ time.Time) error      { return nil }\nfunc (*mockSlowConn) SetReadDeadline(_ time.Time) error  { return nil }\nfunc (*mockSlowConn) SetWriteDeadline(_ time.Time) error { return nil }\n\nfunc TestRTTMonitor(t *testing.T) {\n\tt.Run(\"measures the average and minimum RTT\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tdialer := DialerFunc(func(_ context.Context, _, _ string) (net.Conn, error) {\n\t\t\treturn newMockSlowConn(makeHelloReply(), 10*time.Millisecond), nil\n\t\t})\n\t\trtt := newRTTMonitor(&rttConfig{\n\t\t\tinterval:       10 * time.Millisecond,\n\t\t\tconnectTimeout: defaultConnectionTimeout,\n\t\t\tcreateConnectionFn: func() *connection {\n\t\t\t\treturn newConnection(\"\", WithDialer(func(Dialer) Dialer { return dialer }))\n\t\t\t},\n\t\t\tcreateOperationFn: func(conn *mnet.Connection) *operation.Hello {\n\t\t\t\treturn operation.NewHello().Deployment(driver.SingleConnectionDeployment{C: conn})\n\t\t\t},\n\t\t})\n\t\trtt.connect()\n\t\tdefer rtt.disconnect()\n\n\t\tassert.Eventuallyf(\n\t\t\tt,\n\t\t\tfunc() bool { return rtt.EWMA() > 0 && rtt.Min() > 0 },\n\t\t\t1*time.Second,\n\t\t\t10*time.Millisecond,\n\t\t\t\"expected EWMA() and Min() to return positive durations within 1 second\")\n\t\tassert.True(\n\t\t\tt,\n\t\t\trtt.EWMA() > 0,\n\t\t\t\"expected EWMA() to return a positive duration, got %v\",\n\t\t\trtt.EWMA())\n\t\tassert.True(\n\t\t\tt,\n\t\t\trtt.Min() > 0,\n\t\t\t\"expected Min() to return a positive duration, got %v\",\n\t\t\trtt.Min())\n\t})\n\n\tt.Run(\"can connect and disconnect repeatedly\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tdialer := DialerFunc(func(_ context.Context, _, _ string) (net.Conn, error) {\n\t\t\treturn newMockSlowConn(makeHelloReply(), 10*time.Millisecond), nil\n\t\t})\n\t\trtt := newRTTMonitor(&rttConfig{\n\t\t\tinterval: 10 * time.Second,\n\t\t\tcreateConnectionFn: func() *connection {\n\t\t\t\treturn newConnection(\"\", WithDialer(func(Dialer) Dialer {\n\t\t\t\t\treturn dialer\n\t\t\t\t}))\n\t\t\t},\n\t\t\tcreateOperationFn: func(conn *mnet.Connection) *operation.Hello {\n\t\t\t\treturn operation.NewHello().Deployment(driver.SingleConnectionDeployment{C: conn})\n\t\t\t},\n\t\t})\n\t\tfor i := 0; i < 100; i++ {\n\t\t\trtt.connect()\n\t\t\trtt.disconnect()\n\t\t}\n\t})\n\n\tt.Run(\"works after reset\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tdialer := DialerFunc(func(_ context.Context, _, _ string) (net.Conn, error) {\n\t\t\treturn newMockSlowConn(makeHelloReply(), 10*time.Millisecond), nil\n\t\t})\n\t\trtt := newRTTMonitor(&rttConfig{\n\t\t\tconnectTimeout: defaultConnectionTimeout,\n\t\t\tinterval:       10 * time.Millisecond,\n\t\t\tcreateConnectionFn: func() *connection {\n\t\t\t\treturn newConnection(\"\", WithDialer(func(Dialer) Dialer { return dialer }))\n\t\t\t},\n\t\t\tcreateOperationFn: func(conn *mnet.Connection) *operation.Hello {\n\t\t\t\treturn operation.NewHello().Deployment(driver.SingleConnectionDeployment{C: conn})\n\t\t\t},\n\t\t})\n\t\trtt.connect()\n\t\tdefer rtt.disconnect()\n\n\t\tfor i := 0; i < 3; i++ {\n\t\t\tassert.Eventuallyf(\n\t\t\t\tt,\n\t\t\t\tfunc() bool { return rtt.EWMA() > 0 },\n\t\t\t\t1*time.Second,\n\t\t\t\t10*time.Millisecond,\n\t\t\t\t\"expected EWMA() to return a positive duration within 1 second\")\n\t\t\tassert.Eventuallyf(\n\t\t\t\tt,\n\t\t\t\tfunc() bool { return rtt.Min() > 0 },\n\t\t\t\t1*time.Second,\n\t\t\t\t10*time.Millisecond,\n\t\t\t\t\"expected Min() to return a positive duration within 1 second\")\n\n\t\t\trtt.reset()\n\t\t}\n\t})\n\n\t// GODRIVER-2464\n\t// Test that the RTT monitor can continue monitoring server RTTs after an operation gets stuck.\n\t// An operation can get stuck if the server or a proxy stops responding to requests on the RTT\n\t// connection but does not close the TCP socket, effectively creating an operation that will\n\t// never complete.\n\tt.Run(\"stuck operations time out\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\t// Start a goroutine that listens for and accepts TCP connections, reads requests, and\n\t\t// responds with {\"ok\": 1}. The first 2 connections simulate \"stuck\" connections and never\n\t\t// respond or close.\n\t\tl, err := net.Listen(\"tcp\", \"localhost:0\")\n\t\trequire.NoError(t, err)\n\t\tvar wg sync.WaitGroup\n\t\twg.Add(1)\n\t\tgo func() {\n\t\t\tdefer wg.Done()\n\n\t\t\tfor i := 0; ; i++ {\n\t\t\t\tconn, err := l.Accept()\n\t\t\t\tif err != nil {\n\t\t\t\t\t// The listen loop is cancelled by closing the listener, so there will always be\n\t\t\t\t\t// an error here. Log the error to make debugging easier in case of unexpected\n\t\t\t\t\t// errors.\n\t\t\t\t\tt.Logf(\"Accept error: %v\", err)\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\t// Only close connections when the listener loop returns to prevent closing \"stuck\"\n\t\t\t\t// connections while the test is running.\n\t\t\t\tdefer conn.Close()\n\n\t\t\t\twg.Add(1)\n\t\t\t\tgo func(i int) {\n\t\t\t\t\tdefer wg.Done()\n\n\t\t\t\t\tbuf := make([]byte, 256)\n\t\t\t\t\tfor {\n\t\t\t\t\t\tif _, err := conn.Read(buf); err != nil {\n\t\t\t\t\t\t\t// The connection read/write loop is cancelled by closing the connection,\n\t\t\t\t\t\t\t// so may be an expected error here. Log the error to make debugging\n\t\t\t\t\t\t\t// easier in case of unexpected errors.\n\t\t\t\t\t\t\tt.Logf(\"Read error: %v\", err)\n\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// For the first 2 connections, read the request but never respond and don't\n\t\t\t\t\t\t// close the connection. That simulates the behavior of a \"stuck\" connection.\n\t\t\t\t\t\tif i < 2 {\n\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Delay for 10ms so that systems with limited timing granularity (e.g. some\n\t\t\t\t\t\t// older versions of Windows) can measure a non-zero latency.\n\t\t\t\t\t\ttime.Sleep(10 * time.Millisecond)\n\n\t\t\t\t\t\tif _, err := conn.Write(makeHelloReply()); err != nil {\n\t\t\t\t\t\t\t// The connection read/write loop is cancelled by closing the connection,\n\t\t\t\t\t\t\t// so may be an expected error here. Log the error to make debugging\n\t\t\t\t\t\t\t// easier in case of unexpected errors.\n\t\t\t\t\t\t\tt.Logf(\"Write error: %v\", err)\n\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}(i)\n\t\t\t}\n\t\t}()\n\n\t\trtt := newRTTMonitor(&rttConfig{\n\t\t\tinterval:       10 * time.Millisecond,\n\t\t\tconnectTimeout: 100 * time.Millisecond,\n\t\t\tcreateConnectionFn: func() *connection {\n\t\t\t\treturn newConnection(address.Address(l.Addr().String()))\n\t\t\t},\n\t\t\tcreateOperationFn: func(conn *mnet.Connection) *operation.Hello {\n\t\t\t\treturn operation.NewHello().Deployment(driver.SingleConnectionDeployment{C: conn})\n\t\t\t},\n\t\t})\n\t\trtt.connect()\n\n\t\tassert.Eventuallyf(\n\t\t\tt,\n\t\t\tfunc() bool { return rtt.EWMA() > 0 },\n\t\t\t1*time.Second,\n\t\t\t10*time.Millisecond,\n\t\t\t\"expected EWMA() to return a positive duration within 1 second\")\n\t\tassert.Eventuallyf(\n\t\t\tt,\n\t\t\tfunc() bool { return rtt.Min() > 0 },\n\t\t\t1*time.Second,\n\t\t\t10*time.Millisecond,\n\t\t\t\"expected Min() to return a positive duration within 1 second\")\n\n\t\trtt.disconnect()\n\t\tl.Close()\n\t\twg.Wait()\n\t})\n}\n\n// makeArithmeticSamples will make an arithmetic sequence of time.Duration\n// samples starting at the lower value as ms and ending at the upper value as\n// ms. For example, if lower=1 and upder=4, then the return value will be\n// [1ms, 2ms, 3ms, 4ms].\nfunc makeArithmeticSamples(lower, upper int) []time.Duration {\n\tsamples := []time.Duration{}\n\tfor i := 0; i < upper-lower+1; i++ {\n\t\tsamples = append(samples, time.Duration(lower+i)*time.Millisecond)\n\t}\n\n\treturn samples\n}\n\nfunc TestRTTMonitor_appendMovingMin(t *testing.T) {\n\tt.Parallel()\n\n\ttests := []struct {\n\t\tname    string\n\t\tsamples []time.Duration\n\t\twant    []time.Duration\n\t}{\n\t\t{\n\t\t\tname:    \"singleton\",\n\t\t\tsamples: makeArithmeticSamples(1, 1),\n\t\t\twant:    makeArithmeticSamples(1, 1),\n\t\t},\n\t\t{\n\t\t\tname:    \"multiplicity\",\n\t\t\tsamples: makeArithmeticSamples(1, 2),\n\t\t\twant:    makeArithmeticSamples(1, 2),\n\t\t},\n\t\t{\n\t\t\tname:    \"exceed maxRTTSamples\",\n\t\t\tsamples: makeArithmeticSamples(1, 11),\n\t\t\twant:    makeArithmeticSamples(2, 11),\n\t\t},\n\t\t{\n\t\t\tname:    \"exceed maxRTTSamples but only with negative values\",\n\t\t\tsamples: makeArithmeticSamples(-1, 9),\n\t\t\twant:    makeArithmeticSamples(0, 9),\n\t\t},\n\t}\n\n\tfor _, test := range tests {\n\t\ttest := test // capture the range variable\n\n\t\tt.Run(test.name, func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\trtt := &rttMonitor{\n\t\t\t\tmovingMin: list.New(),\n\t\t\t}\n\n\t\t\tfor _, sample := range test.samples {\n\t\t\t\trtt.appendMovingMin(sample)\n\t\t\t}\n\n\t\t\tpos := 0\n\t\t\tfor e := rtt.movingMin.Front(); e != nil; e = e.Next() {\n\t\t\t\tassert.Equal(t, test.want[pos], e.Value)\n\n\t\t\t\tpos++\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestRTTMonitor_min(t *testing.T) {\n\tt.Parallel()\n\n\ttests := []struct {\n\t\tname    string\n\t\tsamples []time.Duration\n\t\twant    time.Duration\n\t}{\n\t\t{\n\t\t\tname:    \"empty\",\n\t\t\tsamples: []time.Duration{},\n\t\t\twant:    0,\n\t\t},\n\t\t{\n\t\t\tname:    \"one\",\n\t\t\tsamples: makeArithmeticSamples(1, 1),\n\t\t\twant:    0,\n\t\t},\n\t\t{\n\t\t\tname:    \"two\",\n\t\t\tsamples: makeArithmeticSamples(1, 2),\n\t\t\twant:    1 * time.Millisecond,\n\t\t},\n\t\t{\n\t\t\tname:    \"non-unit lower bound\",\n\t\t\tsamples: makeArithmeticSamples(2, 9),\n\t\t\twant:    2 * time.Millisecond,\n\t\t},\n\t\t{\n\t\t\tname:    \"negative lower bound with 2 values\",\n\t\t\tsamples: []time.Duration{-1, 1},\n\t\t\twant:    0,\n\t\t},\n\t\t{\n\t\t\tname: \"negative lower bound with 3 values\",\n\t\t\tsamples: []time.Duration{\n\t\t\t\t-1 * time.Millisecond,\n\t\t\t\t1 * time.Millisecond,\n\t\t\t\t2 * time.Millisecond,\n\t\t\t},\n\t\t\twant: 1 * time.Millisecond,\n\t\t},\n\t\t{\n\t\t\tname: \"non-sequential\",\n\t\t\tsamples: []time.Duration{\n\t\t\t\t2 * time.Millisecond,\n\t\t\t\t1 * time.Millisecond,\n\t\t\t},\n\t\t\twant: 1 * time.Millisecond,\n\t\t},\n\t}\n\n\tfor _, test := range tests {\n\t\ttest := test // capture the range variable\n\n\t\tt.Run(test.name, func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\trtt := &rttMonitor{\n\t\t\t\tmovingMin: list.New(),\n\t\t\t}\n\n\t\t\tfor _, sample := range test.samples {\n\t\t\t\trtt.appendMovingMin(sample)\n\t\t\t}\n\n\t\t\tassert.Equal(t, test.want, rtt.min())\n\t\t})\n\t}\n}\n\nfunc TestRTTMonitor_stddev(t *testing.T) {\n\tt.Parallel()\n\n\ttests := []struct {\n\t\tname    string\n\t\tsamples []time.Duration\n\t\twant    float64\n\t}{\n\t\t{\n\t\t\tname:    \"empty\",\n\t\t\tsamples: []time.Duration{},\n\t\t\twant:    0,\n\t\t},\n\t\t{\n\t\t\tname:    \"one\",\n\t\t\tsamples: makeArithmeticSamples(1, 1),\n\t\t\twant:    0,\n\t\t},\n\t\t{\n\t\t\tname:    \"below maxRTTSamples\",\n\t\t\tsamples: makeArithmeticSamples(1, 5),\n\t\t\twant:    0,\n\t\t},\n\t\t{\n\t\t\tname:    \"equal maxRTTSamples\",\n\t\t\tsamples: makeArithmeticSamples(1, 10),\n\t\t\twant:    2.872281e+06,\n\t\t},\n\t\t{\n\t\t\tname:    \"exceed maxRTTSamples\",\n\t\t\tsamples: makeArithmeticSamples(1, 15),\n\t\t\twant:    2.872281e+06,\n\t\t},\n\t\t{\n\t\t\tname: \"non-sequential\",\n\t\t\tsamples: []time.Duration{\n\t\t\t\t2 * time.Millisecond,\n\t\t\t\t1 * time.Millisecond,\n\t\t\t\t4 * time.Millisecond,\n\t\t\t\t3 * time.Millisecond,\n\t\t\t\t7 * time.Millisecond,\n\t\t\t\t12 * time.Millisecond,\n\t\t\t\t6 * time.Millisecond,\n\t\t\t\t8 * time.Millisecond,\n\t\t\t\t5 * time.Millisecond,\n\t\t\t\t13 * time.Millisecond,\n\t\t\t},\n\t\t\twant: 3.806573e+06,\n\t\t},\n\t}\n\n\tfor _, test := range tests {\n\t\ttest := test // capture the range variable\n\n\t\tt.Run(test.name, func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\trtt := &rttMonitor{\n\t\t\t\tmovingMin: list.New(),\n\t\t\t}\n\t\t\tfor _, sample := range test.samples {\n\t\t\t\trtt.appendMovingMin(sample)\n\t\t\t}\n\t\t\tassert.Equal(t, test.want, float64(rtt.stddev()))\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "x/mongo/driver/topology/sdam_spec_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage topology\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"io/ioutil\"\n\t\"net\"\n\t\"path\"\n\t\"sync\"\n\t\"testing\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/event\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/driverutil\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/spectest\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/address\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/description\"\n)\n\ntype response struct {\n\tHost  string\n\tHello Hello\n}\n\ntype Hello struct {\n\tArbiters                     []string          `bson:\"arbiters,omitempty\"`\n\tArbiterOnly                  bool              `bson:\"arbiterOnly,omitempty\"`\n\tClusterTime                  bson.Raw          `bson:\"$clusterTime,omitempty\"`\n\tCompression                  []string          `bson:\"compression,omitempty\"`\n\tElectionID                   bson.ObjectID     `bson:\"electionId,omitempty\"`\n\tHidden                       bool              `bson:\"hidden,omitempty\"`\n\tHosts                        []string          `bson:\"hosts,omitempty\"`\n\tHelloOK                      bool              `bson:\"helloOk,omitempty\"`\n\tIsWritablePrimary            bool              `bson:\"isWritablePrimary,omitempty\"`\n\tIsReplicaSet                 bool              `bson:\"isreplicaset,omitempty\"`\n\tLastWrite                    *lastWriteDate    `bson:\"lastWrite,omitempty\"`\n\tLogicalSessionTimeoutMinutes uint32            `bson:\"logicalSessionTimeoutMinutes,omitempty\"`\n\tMaxBSONObjectSize            uint32            `bson:\"maxBsonObjectSize,omitempty\"`\n\tMaxMessageSizeBytes          uint32            `bson:\"maxMessageSizeBytes,omitempty\"`\n\tMaxWriteBatchSize            uint32            `bson:\"maxWriteBatchSize,omitempty\"`\n\tMe                           string            `bson:\"me,omitempty\"`\n\tMaxWireVersion               int32             `bson:\"maxWireVersion,omitempty\"`\n\tMinWireVersion               int32             `bson:\"minWireVersion,omitempty\"`\n\tMsg                          string            `bson:\"msg,omitempty\"`\n\tOK                           int32             `bson:\"ok\"`\n\tPassives                     []string          `bson:\"passives,omitempty\"`\n\tPrimary                      string            `bson:\"primary,omitempty\"`\n\tReadOnly                     bool              `bson:\"readOnly,omitempty\"`\n\tSaslSupportedMechs           []string          `bson:\"saslSupportedMechs,omitempty\"`\n\tSecondary                    bool              `bson:\"secondary,omitempty\"`\n\tSetName                      string            `bson:\"setName,omitempty\"`\n\tSetVersion                   uint32            `bson:\"setVersion,omitempty\"`\n\tTags                         map[string]string `bson:\"tags,omitempty\"`\n\tTopologyVersion              *topologyVersion  `bson:\"topologyVersion,omitempty\"`\n}\n\ntype lastWriteDate struct {\n\tLastWriteDate time.Time `bson:\"lastWriteDate\"`\n}\n\ntype server struct {\n\tType            string\n\tSetName         string\n\tSetVersion      uint32\n\tElectionID      *bson.ObjectID `bson:\"electionId\"`\n\tMinWireVersion  *int32\n\tMaxWireVersion  *int32\n\tTopologyVersion *topologyVersion\n\tPool            *testPool\n}\n\ntype topologyVersion struct {\n\tProcessID bson.ObjectID `bson:\"processId\"`\n\tCounter   int64\n}\n\ntype testPool struct {\n\tGeneration uint64\n}\n\ntype applicationError struct {\n\tAddress        string\n\tGeneration     *uint64\n\tMaxWireVersion *int32\n\tWhen           string\n\tType           string\n\tResponse       bsoncore.Document\n}\n\ntype topologyDescription struct {\n\tTopologyType string              `bson:\"topologyType\"`\n\tServers      []serverDescription `bson:\"servers\"`\n\tSetName      string              `bson:\"setName,omitempty\"`\n}\n\ntype serverDescription struct {\n\tAddress  string   `bson:\"address\"`\n\tArbiters []string `bson:\"arbiters\"`\n\tHosts    []string `bson:\"hosts\"`\n\tPassives []string `bson:\"passives\"`\n\tPrimary  string   `bson:\"primary,omitempty\"`\n\tSetName  string   `bson:\"setName,omitempty\"`\n\tType     string   `bson:\"type\"`\n}\n\ntype topologyOpeningEvent struct {\n\tTopologyID string `bson:\"topologyId\"`\n}\n\ntype serverOpeningEvent struct {\n\tAddress    string `bson:\"address\"`\n\tTopologyID string `bson:\"topologyId\"`\n}\n\ntype topologyDescriptionChangedEvent struct {\n\tTopologyID          string              `bson:\"topologyId\"`\n\tPreviousDescription topologyDescription `bson:\"previousDescription\"`\n\tNewDescription      topologyDescription `bson:\"newDescription\"`\n}\n\ntype serverDescriptionChangedEvent struct {\n\tAddress             string            `bson:\"address\"`\n\tTopologyID          string            `bson:\"topologyId\"`\n\tPreviousDescription serverDescription `bson:\"previousDescription\"`\n\tNewDescription      serverDescription `bson:\"newDescription\"`\n}\n\ntype serverClosedEvent struct {\n\tAddress    string `bson:\"address\"`\n\tTopologyID string `bson:\"topologyId\"`\n}\n\ntype monitoringEvent struct {\n\tTopologyOpeningEvent            *topologyOpeningEvent            `bson:\"topology_opening_event,omitempty\"`\n\tServerOpeningEvent              *serverOpeningEvent              `bson:\"server_opening_event,omitempty\"`\n\tTopologyDescriptionChangedEvent *topologyDescriptionChangedEvent `bson:\"topology_description_changed_event,omitempty\"`\n\tServerDescriptionChangedEvent   *serverDescriptionChangedEvent   `bson:\"server_description_changed_event,omitempty\"`\n\tServerClosedEvent               *serverClosedEvent               `bson:\"server_closed_event,omitempty\"`\n}\n\ntype outcome struct {\n\tServers                      map[string]server\n\tTopologyType                 string\n\tSetName                      string\n\tLogicalSessionTimeoutMinutes *int64\n\tMaxSetVersion                uint32\n\tMaxElectionID                bson.ObjectID `bson:\"maxElectionId\"`\n\tCompatible                   *bool\n\tEvents                       []monitoringEvent\n}\n\ntype phase struct {\n\tDescription       string\n\tResponses         []response\n\tApplicationErrors []applicationError\n\tOutcome           outcome\n}\n\ntype testCase struct {\n\tDescription string\n\tURI         string\n\tPhases      []phase\n}\n\nfunc serverDescriptionChanged(e *event.ServerDescriptionChangedEvent) {\n\tlock.Lock()\n\tpublishedEvents = append(publishedEvents, *e)\n\tlock.Unlock()\n}\n\nfunc serverOpening(e *event.ServerOpeningEvent) {\n\tlock.Lock()\n\tpublishedEvents = append(publishedEvents, *e)\n\tlock.Unlock()\n}\n\nfunc topologyDescriptionChanged(e *event.TopologyDescriptionChangedEvent) {\n\tlock.Lock()\n\tpublishedEvents = append(publishedEvents, *e)\n\tlock.Unlock()\n}\n\nfunc topologyOpening(e *event.TopologyOpeningEvent) {\n\tlock.Lock()\n\tpublishedEvents = append(publishedEvents, *e)\n\tlock.Unlock()\n}\n\nfunc serverClosed(e *event.ServerClosedEvent) {\n\tlock.Lock()\n\tpublishedEvents = append(publishedEvents, *e)\n\tlock.Unlock()\n}\n\nvar testsDir = spectest.Path(\"server-discovery-and-monitoring/tests\")\n\nvar (\n\tpublishedEvents []any\n\tlock            sync.Mutex\n)\n\nfunc (r *response) UnmarshalBSON(buf []byte) error {\n\tdoc := bson.Raw(buf)\n\tif err := doc.Index(0).Value().Unmarshal(&r.Host); err != nil {\n\t\treturn fmt.Errorf(\"error unmarshalling Host: %w\", err)\n\t}\n\n\tif err := doc.Index(1).Value().Unmarshal(&r.Hello); err != nil {\n\t\treturn fmt.Errorf(\"error unmarshalling Hello: %w\", err)\n\t}\n\n\treturn nil\n}\n\nfunc setUpTopology(t *testing.T, uri string) *Topology {\n\tsdam := &event.ServerMonitor{\n\t\tServerDescriptionChanged:   serverDescriptionChanged,\n\t\tServerOpening:              serverOpening,\n\t\tTopologyDescriptionChanged: topologyDescriptionChanged,\n\t\tTopologyOpening:            topologyOpening,\n\t\tServerClosed:               serverClosed,\n\t}\n\n\tcfg, err := NewConfig(options.Client().ApplyURI(uri).SetServerMonitor(sdam), nil)\n\tassert.Nil(t, err, \"error constructing topology config: %v\", err)\n\n\t// Disable server monitoring because the hosts in the SDAM spec tests don't actually exist, so the server monitor\n\t// can race with the test and mark the server Unknown when it fails to connect, which causes tests to fail.\n\tcfg.ServerOpts = append(cfg.ServerOpts, withMonitoringDisabled(func(bool) bool { return true }))\n\n\ttopo, err := New(cfg)\n\tassert.Nil(t, err, \"topology.New error: %v\", err)\n\n\terr = topo.Connect()\n\tassert.Nil(t, err, \"topology.Connect error: %v\", err)\n\n\treturn topo\n}\n\nfunc applyResponses(t *testing.T, topo *Topology, responses []response, sub *driver.Subscription) {\n\tselect {\n\tcase <-sub.Updates:\n\tdefault:\n\t}\n\tfor _, response := range responses {\n\t\tdoc, err := bson.Marshal(response.Hello)\n\t\tassert.Nil(t, err, \"Marshal error: %v\", err)\n\n\t\taddr := address.Address(response.Host)\n\t\tdesc := driverutil.NewServerDescription(addr, doc)\n\t\tserver, ok := topo.servers[addr]\n\t\tif ok {\n\t\t\tserver.updateDescription(desc)\n\t\t} else {\n\t\t\t// for tests that check that server descriptions that aren't in the topology aren't applied\n\t\t\ttopo.apply(context.Background(), desc)\n\t\t}\n\t\tselect {\n\t\tcase <-sub.Updates:\n\t\tdefault:\n\t\t\treturn\n\t\t}\n\t}\n}\n\ntype netErr struct {\n\ttimeout bool\n}\n\nfunc (n netErr) Error() string {\n\treturn \"error\"\n}\n\nfunc (n netErr) Timeout() bool {\n\treturn n.timeout\n}\n\nfunc (n netErr) Temporary() bool {\n\treturn false\n}\n\nvar _ net.Error = (*netErr)(nil)\n\nfunc applyErrors(t *testing.T, topo *Topology, errors []applicationError) {\n\tfor _, appErr := range errors {\n\t\tvar currError error\n\t\tswitch appErr.Type {\n\t\tcase \"command\":\n\t\t\tcurrError = driver.ExtractErrorFromServerResponse(appErr.Response)\n\t\tcase \"network\":\n\t\t\tcurrError = driver.Error{\n\t\t\t\tLabels:  []string{driver.NetworkError},\n\t\t\t\tWrapped: ConnectionError{Wrapped: netErr{false}},\n\t\t\t}\n\t\tcase \"timeout\":\n\t\t\tcurrError = driver.Error{\n\t\t\t\tLabels:  []string{driver.NetworkError},\n\t\t\t\tWrapped: ConnectionError{Wrapped: netErr{true}},\n\t\t\t}\n\t\tdefault:\n\t\t\tt.Fatalf(\"unrecognized error type: %v\", appErr.Type)\n\t\t}\n\t\tserver, ok := topo.servers[address.Address(appErr.Address)]\n\t\tassert.True(t, ok, \"server not found: %v\", appErr.Address)\n\n\t\tdesc := server.Description()\n\t\tversionRange := driverutil.NewVersionRange(0, *appErr.MaxWireVersion)\n\t\tdesc.WireVersion = &versionRange\n\n\t\tgeneration, _ := server.pool.generation.getGeneration(nil)\n\t\tif appErr.Generation != nil {\n\t\t\tgeneration = *appErr.Generation\n\t\t}\n\t\t// use generation number to check conn stale\n\t\tinnerConn := connection{\n\t\t\tdesc:       desc,\n\t\t\tgeneration: generation,\n\t\t\tpool:       server.pool,\n\t\t}\n\t\tconn := Connection{connection: &innerConn}\n\n\t\t// Backpressure labels are applied to network/timeout errors during\n\t\t// connection establishment.\n\t\tif appErr.When == \"beforeHandshakeCompletes\" && (appErr.Type == \"network\" || appErr.Type == \"timeout\") {\n\t\t\tif de, ok := currError.(driver.Error); ok {\n\t\t\t\tde.Labels = append(de.Labels, driver.ErrSystemOverloadedError, driver.ErrRetryableError)\n\t\t\t\tcurrError = de\n\t\t\t}\n\t\t}\n\n\t\tswitch appErr.When {\n\t\tcase \"beforeHandshakeCompletes\":\n\t\t\tserver.ProcessHandshakeError(currError, generation, nil)\n\t\tcase \"afterHandshakeCompletes\":\n\t\t\t_ = server.ProcessError(currError, &conn)\n\t\tdefault:\n\t\t\tt.Fatalf(\"unrecognized applicationError.When value: %v\", appErr.When)\n\t\t}\n\t}\n}\n\nfunc compareServerDescriptions(t *testing.T,\n\texpected serverDescription, actual event.ServerDescription, idx int,\n) {\n\tt.Helper()\n\n\tassert.Equal(t, expected.Address, actual.Addr.String(),\n\t\t\"%v: expected server address %s, got %s\", idx, expected.Address, actual.Addr)\n\n\tassert.Equal(t, len(expected.Hosts), len(actual.Hosts),\n\t\t\"%v: expected %d hosts, got %d\", idx, len(expected.Hosts), len(actual.Hosts))\n\tfor idx, expectedHost := range expected.Hosts {\n\t\tactualHost := actual.Hosts[idx]\n\t\tassert.Equal(t, expectedHost, actualHost, \"%v: expected host %s, got %s\", idx, expectedHost, actualHost)\n\t}\n\n\tassert.Equal(t, len(expected.Passives), len(actual.Passives),\n\t\t\"%v: expected %d hosts, got %d\", idx, len(expected.Passives), len(actual.Passives))\n\tfor idx, expectedPassive := range expected.Passives {\n\t\tactualPassive := actual.Passives[idx]\n\t\tassert.Equal(t, expectedPassive, actualPassive, \"%v: expected passive %s, got %s\", idx, expectedPassive, actualPassive)\n\t}\n\n\tassert.Equal(t, expected.Primary, string(actual.Primary),\n\t\t\"%v: expected primary %s, got %s\", idx, expected.Primary, actual.Primary)\n\tassert.Equal(t, expected.SetName, actual.SetName,\n\t\t\"%v: expected set name %s, got %s\", idx, expected.SetName, actual.SetName)\n\n\t// PossiblePrimary is only relevant to single-threaded drivers.\n\tif expected.Type == \"PossiblePrimary\" {\n\t\texpected.Type = \"Unknown\"\n\t}\n\tassert.Equal(t, expected.Type, actual.Kind,\n\t\t\"%v: expected server kind %s, got %s\", idx, expected.Type, actual.Kind)\n}\n\nfunc compareTopologyDescriptions(t *testing.T,\n\texpected topologyDescription, actual event.TopologyDescription, idx int,\n) {\n\tt.Helper()\n\n\tassert.Equal(t, expected.TopologyType, actual.Kind,\n\t\t\"%v: expected topology kind %s, got %s\", idx, expected.TopologyType, actual.Kind)\n\tassert.Equal(t, len(expected.Servers), len(actual.Servers),\n\t\t\"%v: expected %d servers, got %d\", idx, len(expected.Servers), len(actual.Servers))\n\n\tfor idx, es := range expected.Servers {\n\t\tas := actual.Servers[idx]\n\t\tcompareServerDescriptions(t, es, as, idx)\n\t}\n\n\tassert.Equal(t, expected.SetName, actual.SetName,\n\t\t\"%v: expected set name %s, got %s\", idx, expected.SetName, actual.SetName)\n}\n\nfunc compareEvents(t *testing.T, events []monitoringEvent) {\n\tt.Helper()\n\n\tlock.Lock()\n\tdefer lock.Unlock()\n\n\tassert.Equal(t, len(events), len(publishedEvents),\n\t\t\"expected %d published events, got %d\\n\",\n\t\tlen(events), len(publishedEvents))\n\n\tfor idx, me := range events {\n\t\tif me.TopologyOpeningEvent != nil {\n\t\t\tactual, ok := publishedEvents[idx].(event.TopologyOpeningEvent)\n\t\t\tassert.True(t, ok, \"%v: expected type %T, got %T\", idx, event.TopologyOpeningEvent{}, publishedEvents[idx])\n\t\t\tassert.False(t, actual.TopologyID.IsZero(), \"%v: expected topology id\", idx)\n\t\t}\n\t\tif me.ServerOpeningEvent != nil {\n\t\t\tactual, ok := publishedEvents[idx].(event.ServerOpeningEvent)\n\t\t\tassert.True(t, ok, \"%v: expected type %T, got %T\", idx, event.ServerOpeningEvent{}, publishedEvents[idx])\n\n\t\t\tevt := me.ServerOpeningEvent\n\t\t\tassert.Equal(t, evt.Address, string(actual.Address),\n\t\t\t\t\"%v: expected address %s, got %s\", idx, evt.Address, actual.Address)\n\t\t\tassert.False(t, actual.TopologyID.IsZero(), \"%v: expected topology id\", idx)\n\t\t}\n\t\tif me.TopologyDescriptionChangedEvent != nil {\n\t\t\tactual, ok := publishedEvents[idx].(event.TopologyDescriptionChangedEvent)\n\t\t\tassert.True(t, ok, \"%v: expected type %T, got %T\", idx, event.TopologyDescriptionChangedEvent{}, publishedEvents[idx])\n\n\t\t\tevt := me.TopologyDescriptionChangedEvent\n\t\t\tcompareTopologyDescriptions(t, evt.PreviousDescription, actual.PreviousDescription, idx)\n\t\t\tcompareTopologyDescriptions(t, evt.NewDescription, actual.NewDescription, idx)\n\t\t\tassert.False(t, actual.TopologyID.IsZero(), \"%v: expected topology id\", idx)\n\t\t}\n\t\tif me.ServerDescriptionChangedEvent != nil {\n\t\t\tactual, ok := publishedEvents[idx].(event.ServerDescriptionChangedEvent)\n\t\t\tassert.True(t, ok, \"%v: expected type %T, got %T\", idx, event.ServerDescriptionChangedEvent{}, publishedEvents[idx])\n\n\t\t\tevt := me.ServerDescriptionChangedEvent\n\t\t\tassert.Equal(t, evt.Address, string(actual.Address),\n\t\t\t\t\"%v: expected server address %s, got %s\", idx, evt.Address, actual.Address)\n\t\t\tcompareServerDescriptions(t, evt.PreviousDescription, actual.PreviousDescription, idx)\n\t\t\tcompareServerDescriptions(t, evt.NewDescription, actual.NewDescription, idx)\n\t\t\tassert.False(t, actual.TopologyID.IsZero(), \"%v: expected topology id\", idx)\n\t\t}\n\t\tif me.ServerClosedEvent != nil {\n\t\t\tactual, ok := publishedEvents[idx].(event.ServerClosedEvent)\n\t\t\tassert.True(t, ok, \"%v: expected type %T, got %T\", idx, event.ServerClosedEvent{}, publishedEvents[idx])\n\n\t\t\tevt := me.ServerClosedEvent\n\t\t\tassert.Equal(t, evt.Address, string(actual.Address),\n\t\t\t\t\"%v: expected server address %s, got %s\", idx, evt.Address, actual.Address)\n\t\t\tassert.False(t, actual.TopologyID.IsZero(), \"%v: expected topology id\", idx)\n\t\t}\n\t}\n}\n\nfunc findServerInTopology(topo description.Topology, addr address.Address) (description.Server, bool) {\n\tfor _, server := range topo.Servers {\n\t\tif server.Addr.String() == addr.String() {\n\t\t\treturn server, true\n\t\t}\n\t}\n\treturn description.Server{}, false\n}\n\nfunc runTest(t *testing.T, directory string, filename string) {\n\tfilepath := path.Join(testsDir, directory, filename)\n\tcontent, err := ioutil.ReadFile(filepath)\n\tassert.Nil(t, err, \"ReadFile error: %v\", err)\n\n\tt.Run(directory+\"/\"+filename, func(t *testing.T) {\n\t\tspectest.CheckSkip(t)\n\n\t\tvar test testCase\n\t\terr = bson.UnmarshalExtJSON(content, false, &test)\n\t\tassert.Nil(t, err, \"Unmarshal error: %v\", err)\n\t\ttopo := setUpTopology(t, test.URI)\n\t\tsub, err := topo.Subscribe()\n\t\tassert.Nil(t, err, \"subscribe error: %v\", err)\n\n\t\tfor _, phase := range test.Phases {\n\t\t\tapplyResponses(t, topo, phase.Responses, sub)\n\t\t\tapplyErrors(t, topo, phase.ApplicationErrors)\n\n\t\t\tif phase.Outcome.Events != nil {\n\t\t\t\tcompareEvents(t, phase.Outcome.Events)\n\t\t\t\tpublishedEvents = nil\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tpublishedEvents = nil\n\t\t\tif phase.Outcome.Compatible == nil || *phase.Outcome.Compatible {\n\t\t\t\tassert.True(t, topo.fsm.compatible.Load().(bool), \"Expected servers to be compatible\")\n\t\t\t\tassert.Nil(t, topo.fsm.compatibilityErr, \"expected fsm.compatibility to be nil, got %v\",\n\t\t\t\t\ttopo.fsm.compatibilityErr)\n\t\t\t} else {\n\t\t\t\tassert.False(t, topo.fsm.compatible.Load().(bool), \"Expected servers to not be compatible\")\n\t\t\t\tassert.NotNil(t, topo.fsm.compatibilityErr, \"expected fsm.compatibility error to be non-nil\")\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tdesc := topo.Description()\n\n\t\t\tassert.Equal(t, phase.Outcome.TopologyType, desc.Kind.String(),\n\t\t\t\t\"expected TopologyType to be %v, got %v\", phase.Outcome.TopologyType, desc.Kind.String())\n\t\t\tassert.Equal(t, phase.Outcome.SetName, topo.fsm.SetName,\n\t\t\t\t\"expected SetName to be %v, got %v\", phase.Outcome.SetName, topo.fsm.SetName)\n\t\t\tassert.Equal(t, len(phase.Outcome.Servers), len(desc.Servers),\n\t\t\t\t\"expected %v servers, got %v\", len(phase.Outcome.Servers), len(desc.Servers))\n\n\t\t\tassert.Equal(t,\n\t\t\t\tphase.Outcome.LogicalSessionTimeoutMinutes,\n\t\t\t\tdesc.SessionTimeoutMinutes,\n\t\t\t\t\"expected and actual logical session timeout minutes are different\")\n\n\t\t\tassert.Equal(t, phase.Outcome.MaxSetVersion, topo.fsm.maxSetVersion,\n\t\t\t\t\"expected maxSetVersion to be %v, got %v\", phase.Outcome.MaxSetVersion, topo.fsm.maxSetVersion)\n\t\t\tassert.Equal(t, phase.Outcome.MaxElectionID, topo.fsm.maxElectionID,\n\t\t\t\t\"expected maxElectionID to be %v, got %v\", phase.Outcome.MaxElectionID, topo.fsm.maxElectionID)\n\n\t\t\tfor addr, server := range phase.Outcome.Servers {\n\t\t\t\tfsmServer, ok := findServerInTopology(desc, address.Address(addr))\n\t\t\t\tassert.True(t, ok, \"Couldn't find server %v\", addr)\n\n\t\t\t\tassert.Equal(t, address.Address(addr), fsmServer.Addr,\n\t\t\t\t\t\"expected server address to be %v, got %v\", address.Address(addr), fsmServer.Addr)\n\t\t\t\tassert.Equal(t, server.SetName, fsmServer.SetName,\n\t\t\t\t\t\"expected server SetName to be %v, got %v\", server.SetName, fsmServer.SetName)\n\t\t\t\tassert.Equal(t, server.SetVersion, fsmServer.SetVersion,\n\t\t\t\t\t\"expected server SetVersion to be %v, got %v\", server.SetVersion, fsmServer.SetVersion)\n\t\t\t\tif server.ElectionID != nil {\n\t\t\t\t\tassert.Equal(t, *server.ElectionID, fsmServer.ElectionID,\n\t\t\t\t\t\t\"expected server ElectionID to be %v, got %v\", *server.ElectionID, fsmServer.ElectionID)\n\t\t\t\t}\n\t\t\t\tif server.TopologyVersion != nil {\n\n\t\t\t\t\tassert.NotNil(t, fsmServer.TopologyVersion, \"expected server TopologyVersion not to be nil\")\n\t\t\t\t\tassert.Equal(t, server.TopologyVersion.ProcessID, fsmServer.TopologyVersion.ProcessID,\n\t\t\t\t\t\t\"expected server TopologyVersion ProcessID to be %v, got %v\", server.TopologyVersion.ProcessID, fsmServer.TopologyVersion.ProcessID)\n\t\t\t\t\tassert.Equal(t, server.TopologyVersion.Counter, fsmServer.TopologyVersion.Counter,\n\t\t\t\t\t\t\"expected server TopologyVersion Counter to be %v, got %v\", server.TopologyVersion.Counter, fsmServer.TopologyVersion.Counter)\n\t\t\t\t} else {\n\t\t\t\t\tassert.Nil(t, fsmServer.TopologyVersion, \"expected server TopologyVersion to be nil\")\n\t\t\t\t}\n\n\t\t\t\t// PossiblePrimary is only relevant to single-threaded drivers.\n\t\t\t\tif server.Type == \"PossiblePrimary\" {\n\t\t\t\t\tserver.Type = \"Unknown\"\n\t\t\t\t}\n\n\t\t\t\tassert.Equal(t, server.Type, fsmServer.Kind.String(),\n\t\t\t\t\t\"expected server Type to be %v, got %v\", server.Type, fsmServer.Kind.String())\n\t\t\t\tif server.Pool != nil {\n\t\t\t\t\ttopo.serversLock.Lock()\n\t\t\t\t\tactualServer := topo.servers[address.Address(addr)]\n\t\t\t\t\ttopo.serversLock.Unlock()\n\t\t\t\t\tactualGeneration, _ := actualServer.pool.generation.getGeneration(nil)\n\t\t\t\t\tassert.Equal(t, server.Pool.Generation, actualGeneration,\n\t\t\t\t\t\t\"expected server pool generation to be %v, got %v\", server.Pool.Generation, actualGeneration)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t})\n}\n\n// Test case for all SDAM spec tests.\nfunc TestSDAMSpec(t *testing.T) {\n\tfor _, subdir := range []string{\"single\", \"rs\", \"sharded\", \"load-balanced\", \"errors\", \"monitoring\"} {\n\t\tfor _, file := range spectest.FindJSONFilesInDir(t, path.Join(testsDir, subdir)) {\n\t\t\trunTest(t, subdir, file)\n\t\t}\n\t}\n}\n\nfunc TestHasStalePrimary(t *testing.T) {\n\tt.Parallel()\n\n\tt.Run(\"WV17 SEI EQ MEI and SSV LT MSV\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tsrv := description.Server{\n\t\t\tWireVersion: &description.VersionRange{Min: 17, Max: 17},\n\t\t\tElectionID:  bson.NewObjectIDFromTimestamp(time.Now()),\n\t\t\tSetVersion:  1,\n\t\t}\n\n\t\tfsm := fsm{\n\t\t\tmaxElectionID: srv.ElectionID,\n\t\t\tmaxSetVersion: 2,\n\t\t}\n\n\t\tboolVal := hasStalePrimary(fsm, srv)\n\t\tassert.True(t, boolVal, \"expected true, got false\")\n\t})\n\n\tt.Run(\"WV17 SEI EQ MEI and SSV GT MSV\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tsrv := description.Server{\n\t\t\tWireVersion: &description.VersionRange{Min: 17, Max: 17},\n\t\t\tElectionID:  bson.NewObjectIDFromTimestamp(time.Now()),\n\t\t\tSetVersion:  2,\n\t\t}\n\n\t\tfsm := fsm{\n\t\t\tmaxElectionID: srv.ElectionID,\n\t\t\tmaxSetVersion: 1,\n\t\t}\n\n\t\tboolVal := hasStalePrimary(fsm, srv)\n\t\tassert.False(t, boolVal, \"expected false, got true\")\n\t})\n\n\tt.Run(\"WV17 SEI EQ MEI and SSV EQ MSV\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tsrv := description.Server{\n\t\t\tWireVersion: &description.VersionRange{Min: 17, Max: 17},\n\t\t\tElectionID:  bson.NewObjectIDFromTimestamp(time.Now()),\n\t\t\tSetVersion:  1,\n\t\t}\n\n\t\tfsm := fsm{\n\t\t\tmaxElectionID: srv.ElectionID,\n\t\t\tmaxSetVersion: 1,\n\t\t}\n\n\t\tboolVal := hasStalePrimary(fsm, srv)\n\t\tassert.False(t, boolVal, \"expected false, got true\")\n\t})\n\n\tt.Run(\"WV17 SEI GT MEI and SSV LT MSV\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tfsm := fsm{\n\t\t\tmaxElectionID: bson.NewObjectIDFromTimestamp(time.Now()),\n\t\t\tmaxSetVersion: 2,\n\t\t}\n\n\t\tsrv := description.Server{\n\t\t\tWireVersion: &description.VersionRange{Min: 17, Max: 17},\n\t\t\tElectionID:  bson.NewObjectIDFromTimestamp(time.Now().Add(time.Second)),\n\t\t\tSetVersion:  1,\n\t\t}\n\n\t\tboolVal := hasStalePrimary(fsm, srv)\n\t\tassert.False(t, boolVal, \"expected false, got true\")\n\t})\n\n\tt.Run(\"WV17 SEI GT MEI and SSV GT MSV\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tfsm := fsm{\n\t\t\tmaxElectionID: bson.NewObjectIDFromTimestamp(time.Now()),\n\t\t\tmaxSetVersion: 1,\n\t\t}\n\n\t\tsrv := description.Server{\n\t\t\tWireVersion: &description.VersionRange{Min: 17, Max: 17},\n\t\t\tElectionID:  bson.NewObjectIDFromTimestamp(time.Now().Add(time.Second)),\n\t\t\tSetVersion:  2,\n\t\t}\n\n\t\tboolVal := hasStalePrimary(fsm, srv)\n\t\tassert.False(t, boolVal, \"expected false, got true\")\n\t})\n\n\tt.Run(\"WV17 SEI GT MEI and SSV EQ MSV\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tfsm := fsm{\n\t\t\tmaxElectionID: bson.NewObjectIDFromTimestamp(time.Now()),\n\t\t\tmaxSetVersion: 1,\n\t\t}\n\n\t\tsrv := description.Server{\n\t\t\tWireVersion: &description.VersionRange{Min: 17, Max: 17},\n\t\t\tElectionID:  bson.NewObjectIDFromTimestamp(time.Now().Add(time.Second)),\n\t\t\tSetVersion:  1,\n\t\t}\n\n\t\tboolVal := hasStalePrimary(fsm, srv)\n\t\tassert.False(t, boolVal, \"expected false, got true\")\n\t})\n\n\tt.Run(\"WV17 SEI LT MEI and SSV LT MSV\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tsrv := description.Server{\n\t\t\tWireVersion: &description.VersionRange{Min: 17, Max: 17},\n\t\t\tElectionID:  bson.NewObjectIDFromTimestamp(time.Now().Add(-time.Second)),\n\t\t\tSetVersion:  1,\n\t\t}\n\n\t\tfsm := fsm{\n\t\t\tmaxElectionID: bson.NewObjectIDFromTimestamp(time.Now()),\n\t\t\tmaxSetVersion: 2,\n\t\t}\n\n\t\tboolVal := hasStalePrimary(fsm, srv)\n\t\tassert.True(t, boolVal, \"expected true, got false\")\n\t})\n\n\tt.Run(\"WV17 SEI LT MEI and SSV GT MSV\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tsrv := description.Server{\n\t\t\tWireVersion: &description.VersionRange{Min: 17, Max: 17},\n\t\t\tElectionID:  bson.NewObjectIDFromTimestamp(time.Now().Add(-time.Second)),\n\t\t\tSetVersion:  2,\n\t\t}\n\n\t\tfsm := fsm{\n\t\t\tmaxElectionID: bson.NewObjectIDFromTimestamp(time.Now()),\n\t\t\tmaxSetVersion: 1,\n\t\t}\n\n\t\tboolVal := hasStalePrimary(fsm, srv)\n\t\tassert.True(t, boolVal, \"expected true, got false\")\n\t})\n\n\tt.Run(\"WV17 SEI LT MEI and SSV EQ MSV\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tsrv := description.Server{\n\t\t\tWireVersion: &description.VersionRange{Min: 17, Max: 17},\n\t\t\tElectionID:  bson.NewObjectIDFromTimestamp(time.Now().Add(-time.Second)),\n\t\t\tSetVersion:  1,\n\t\t}\n\n\t\tfsm := fsm{\n\t\t\tmaxElectionID: bson.NewObjectIDFromTimestamp(time.Now()),\n\t\t\tmaxSetVersion: 1,\n\t\t}\n\n\t\tboolVal := hasStalePrimary(fsm, srv)\n\t\tassert.True(t, boolVal, \"expected true, got false\")\n\t})\n}\n"
  },
  {
    "path": "x/mongo/driver/topology/server.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage topology\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"net\"\n\t\"sync\"\n\t\"sync/atomic\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/event\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/driverutil\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/logger\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/address\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/connstring\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/description\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/mnet\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/operation\"\n)\n\nconst (\n\tminHeartbeatInterval = 500 * time.Millisecond\n\twireVersion42        = 8 // Wire version for MongoDB 4.2\n)\n\n// Server state constants.\nconst (\n\tserverDisconnected int64 = iota\n\tserverDisconnecting\n\tserverConnected\n)\n\nfunc serverStateString(state int64) string {\n\tswitch state {\n\tcase serverDisconnected:\n\t\treturn \"Disconnected\"\n\tcase serverDisconnecting:\n\t\treturn \"Disconnecting\"\n\tcase serverConnected:\n\t\treturn \"Connected\"\n\t}\n\n\treturn \"\"\n}\n\n// newServerDescriptionFromError creates a new unknown server description with\n// the given parameters.\nfunc newServerDescriptionFromError(\n\taddr address.Address,\n\terr error,\n\ttv *description.TopologyVersion,\n) description.Server {\n\treturn description.Server{\n\t\tAddr:            addr,\n\t\tLastError:       err,\n\t\tKind:            description.Unknown,\n\t\tTopologyVersion: tv,\n\t}\n}\n\n// newDefaultServerDescription creates a new unknown server description with the\n// given address.\nfunc newDefaultServerDescription(addr address.Address) description.Server {\n\treturn newServerDescriptionFromError(addr, nil, nil)\n}\n\nvar (\n\t// ErrServerClosed occurs when an attempt to Get a connection is made after\n\t// the server has been closed.\n\tErrServerClosed = errors.New(\"server is closed\")\n\t// ErrServerConnected occurs when at attempt to Connect is made after a server\n\t// has already been connected.\n\tErrServerConnected = errors.New(\"server is connected\")\n\n\terrCheckCancelled = errors.New(\"server check cancelled\")\n\temptyDescription  = newDefaultServerDescription(\"\")\n)\n\n// SelectedServer represents a specific server that was selected during server selection.\n// It contains the kind of the topology it was selected from.\ntype SelectedServer struct {\n\t*Server\n\n\tKind description.TopologyKind\n}\n\n// Description returns a description of the server as of the last heartbeat.\nfunc (ss *SelectedServer) Description() description.SelectedServer {\n\tsdesc := ss.Server.Description()\n\treturn description.SelectedServer{\n\t\tServer: sdesc,\n\t\tKind:   ss.Kind,\n\t}\n}\n\n// Server is a single server within a topology.\ntype Server struct {\n\t// The following integer fields must be accessed using the atomic package and should be at the\n\t// beginning of the struct.\n\t// - atomic bug: https://pkg.go.dev/sync/atomic#pkg-note-BUG\n\t// - suggested layout: https://go101.org/article/memory-layout.html\n\n\tstate          int64\n\toperationCount int64\n\n\tcfg     *serverConfig\n\taddress address.Address\n\n\t// connection related fields\n\tpool *pool\n\n\t// goroutine management fields\n\tdone          chan struct{}\n\tcheckNow      chan struct{}\n\tdisconnecting chan struct{}\n\tclosewg       sync.WaitGroup\n\n\t// description related fields\n\tdesc                   atomic.Value // holds a description.Server\n\tupdateTopologyCallback atomic.Value\n\ttopologyID             bson.ObjectID\n\n\t// subscriber related fields\n\tsubLock             sync.Mutex\n\tsubscribers         map[uint64]chan description.Server\n\tcurrentSubscriberID uint64\n\tsubscriptionsClosed bool\n\n\tconn *connection\n\n\t// Calling StopListening on the heartbeatListner will cancel the context\n\t// passed to the heartbeat check. This will result in the current connection\n\t// being closed.\n\theartbeatListener contextListener\n\n\tprocessErrorLock sync.Mutex\n\trttMonitor       *rttMonitor\n\tmonitorOnce      sync.Once\n}\n\n// updateTopologyCallback is a callback used to create a server that should be called when the parent Topology instance\n// should be updated based on a new server description. The callback must return the server description that should be\n// stored by the server.\ntype updateTopologyCallback func(description.Server) description.Server\n\n// ConnectServer creates a new Server and then initializes it using the\n// Connect method.\nfunc ConnectServer(\n\taddr address.Address,\n\tupdateCallback updateTopologyCallback,\n\ttopologyID bson.ObjectID,\n\tconnectTimeout time.Duration,\n\topts ...ServerOption,\n) (*Server, error) {\n\tsrvr := NewServer(addr, topologyID, connectTimeout, opts...)\n\terr := srvr.Connect(updateCallback)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn srvr, nil\n}\n\n// NewServer creates a new server. The mongodb server at the address will be monitored\n// on an internal monitoring goroutine.\nfunc NewServer(\n\taddr address.Address,\n\ttopologyID bson.ObjectID,\n\tconnectTimeout time.Duration,\n\topts ...ServerOption,\n) *Server {\n\tcfg := newServerConfig(connectTimeout, opts...)\n\n\ts := &Server{\n\t\tstate: serverDisconnected,\n\n\t\tcfg:     cfg,\n\t\taddress: addr,\n\n\t\tdone:          make(chan struct{}),\n\t\tcheckNow:      make(chan struct{}, 1),\n\t\tdisconnecting: make(chan struct{}),\n\n\t\ttopologyID: topologyID,\n\n\t\tsubscribers:       make(map[uint64]chan description.Server),\n\t\theartbeatListener: newNonBlockingContextDoneListener(),\n\t}\n\ts.desc.Store(newDefaultServerDescription(addr))\n\trttCfg := &rttConfig{\n\t\tinterval:           cfg.heartbeatInterval,\n\t\tminRTTWindow:       5 * time.Minute,\n\t\tcreateConnectionFn: s.createConnection,\n\t\tcreateOperationFn:  s.createBaseOperation,\n\t\tconnectTimeout:     connectTimeout,\n\t}\n\ts.rttMonitor = newRTTMonitor(rttCfg)\n\n\tpc := poolConfig{\n\t\tAddress:          addr,\n\t\tMinPoolSize:      cfg.minConns,\n\t\tMaxPoolSize:      cfg.maxConns,\n\t\tMaxConnecting:    cfg.maxConnecting,\n\t\tMaxIdleTime:      cfg.poolMaxIdleTime,\n\t\tMaintainInterval: cfg.poolMaintainInterval,\n\t\tLoadBalanced:     cfg.loadBalanced,\n\t\tPoolMonitor:      cfg.poolMonitor,\n\t\tLogger:           cfg.logger,\n\t\thandshakeErrFn:   s.ProcessHandshakeError,\n\t\tConnectTimeout:   connectTimeout,\n\t}\n\n\tconnectionOpts := copyConnectionOpts(cfg.connectionOpts)\n\ts.pool = newPool(pc, connectionOpts...)\n\ts.publishServerOpeningEvent(s.address)\n\n\treturn s\n}\n\nfunc mustLogServerMessage(srv *Server) bool {\n\treturn srv.cfg.logger != nil && srv.cfg.logger.LevelComponentEnabled(\n\t\tlogger.LevelDebug, logger.ComponentTopology)\n}\n\nfunc logServerMessage(srv *Server, msg string, keysAndValues ...any) {\n\tserverHost, serverPort, err := net.SplitHostPort(srv.address.String())\n\tif err != nil {\n\t\tserverHost = srv.address.String()\n\t\tserverPort = \"\"\n\t}\n\n\tvar driverConnectionID int64\n\tvar serverConnectionID *int64\n\n\tif srv.conn != nil {\n\t\tdriverConnectionID = srv.conn.driverConnectionID\n\t\tserverConnectionID = srv.conn.serverConnectionID\n\t}\n\n\tsrv.cfg.logger.Print(logger.LevelDebug,\n\t\tlogger.ComponentTopology,\n\t\tmsg,\n\t\tlogger.SerializeServer(logger.Server{\n\t\t\tDriverConnectionID: driverConnectionID,\n\t\t\tTopologyID:         srv.topologyID,\n\t\t\tMessage:            msg,\n\t\t\tServerConnectionID: serverConnectionID,\n\t\t\tServerHost:         serverHost,\n\t\t\tServerPort:         serverPort,\n\t\t}, keysAndValues...)...)\n}\n\n// Connect initializes the Server by starting background monitoring goroutines.\n// This method must be called before a Server can be used.\nfunc (s *Server) Connect(updateCallback updateTopologyCallback) error {\n\tif !atomic.CompareAndSwapInt64(&s.state, serverDisconnected, serverConnected) {\n\t\treturn ErrServerConnected\n\t}\n\n\tdesc := newDefaultServerDescription(s.address)\n\tif s.cfg.loadBalanced {\n\t\t// LBs automatically start off with kind LoadBalancer because there is no monitoring routine for state changes.\n\t\tdesc.Kind = description.ServerKindLoadBalancer\n\t}\n\ts.desc.Store(desc)\n\ts.updateTopologyCallback.Store(updateCallback)\n\n\tif !s.cfg.monitoringDisabled && !s.cfg.loadBalanced {\n\t\ts.closewg.Add(1)\n\t\tgo s.update()\n\t}\n\n\t// The CMAP spec describes that pools should only be marked \"ready\" when the server description\n\t// is updated to something other than \"Unknown\". However, we maintain the previous Server\n\t// behavior here and immediately mark the pool as ready during Connect() to simplify and speed\n\t// up the Client startup behavior. The risk of marking a pool as ready proactively during\n\t// Connect() is that we could attempt to create connections to a server that was configured\n\t// erroneously until the first server check or checkOut() failure occurs, when the SDAM error\n\t// handler would transition the Server back to \"Unknown\" and set the pool to \"paused\".\n\treturn s.pool.ready()\n}\n\n// Disconnect closes sockets to the server referenced by this Server.\n// Subscriptions to this Server will be closed. Disconnect will shutdown\n// any monitoring goroutines, closeConnection the idle connection pool, and will\n// wait until all the in use connections have been returned to the connection\n// pool and are closed before returning. If the context expires via\n// cancellation, deadline, or timeout before the in use connections have been\n// returned, the in use connections will be closed, resulting in the failure of\n// any in flight read or write operations. If this method returns with no\n// errors, all connections associated with this Server have been closed.\nfunc (s *Server) Disconnect(ctx context.Context) error {\n\tif !atomic.CompareAndSwapInt64(&s.state, serverConnected, serverDisconnecting) {\n\t\treturn ErrServerClosed\n\t}\n\n\ts.updateTopologyCallback.Store((updateTopologyCallback)(nil))\n\n\tclose(s.done)\n\n\ts.heartbeatListener.StopListening()\n\n\ts.pool.close(ctx)\n\n\ts.closewg.Wait()\n\ts.rttMonitor.disconnect()\n\tatomic.StoreInt64(&s.state, serverDisconnected)\n\n\treturn nil\n}\n\n// Connection gets a connection to the server.\nfunc (s *Server) Connection(ctx context.Context) (*mnet.Connection, error) {\n\tif atomic.LoadInt64(&s.state) != serverConnected {\n\t\treturn nil, ErrServerClosed\n\t}\n\n\t// Increment the operation count before calling checkOut to make sure that all connection\n\t// requests are included in the operation count, including those in the wait queue. If we got an\n\t// error instead of a connection, immediately decrement the operation count.\n\tatomic.AddInt64(&s.operationCount, 1)\n\tconn, err := s.pool.checkOut(ctx)\n\tif err != nil {\n\t\tatomic.AddInt64(&s.operationCount, -1)\n\t\treturn nil, err\n\t}\n\n\tserverConn := &Connection{\n\t\tconnection: conn,\n\t\tcleanupServerFn: func() {\n\t\t\t// Decrement the operation count whenever the caller is done with the connection. Note\n\t\t\t// that cleanupServerFn() is not called while the connection is pinned to a cursor or\n\t\t\t// transaction, so the operation count is not decremented until the cursor is closed or\n\t\t\t// the transaction is committed or aborted. Use an int64 instead of a uint64 to mitigate\n\t\t\t// the impact of any possible bugs that could cause the uint64 to underflow, which would\n\t\t\t// make the server much less selectable.\n\t\t\tatomic.AddInt64(&s.operationCount, -1)\n\t\t},\n\t}\n\n\treturn mnet.NewConnection(serverConn), nil\n}\n\n// ProcessHandshakeError implements SDAM error handling for errors that occur before a connection\n// finishes handshaking.\nfunc (s *Server) ProcessHandshakeError(err error, startingGenerationNumber uint64, serviceID *bson.ObjectID) {\n\t// Ignore the error if the server is behind a load balancer but the service ID is unknown. This indicates that the\n\t// error happened when dialing the connection or during the MongoDB handshake, so we don't know the service ID to\n\t// use for clearing the pool.\n\tif err == nil || s.cfg.loadBalanced && serviceID == nil {\n\t\treturn\n\t}\n\t// Ignore the error if the connection is stale.\n\tif generation, _ := s.pool.generation.getGeneration(serviceID); startingGenerationNumber < generation {\n\t\treturn\n\t}\n\n\t// Unwrap any connection errors. If there is no wrapped connection error, then the error should\n\t// not result in any Server state change (e.g. a command error from the database).\n\twrappedConnErr := unwrapConnectionError(err)\n\tif wrappedConnErr == nil {\n\t\treturn\n\t}\n\n\t// Do not clear the pool when backpressure error label applied.\n\tvar de driver.Error\n\tif errors.As(err, &de) && de.HasErrorLabel(driver.ErrSystemOverloadedError) {\n\t\treturn\n\t}\n\n\t// Must hold the processErrorLock while updating the server description and clearing the pool.\n\t// Not holding the lock leads to possible out-of-order processing of pool.clear() and\n\t// pool.ready() calls from concurrent server description updates.\n\ts.processErrorLock.Lock()\n\tdefer s.processErrorLock.Unlock()\n\n\t// Since the only kind of ConnectionError we receive from pool.Get will be an initialization error, we should set\n\t// the description.Server appropriately. The description should not have a TopologyVersion because the staleness\n\t// checking logic above has already determined that this description is not stale.\n\ts.updateDescription(newServerDescriptionFromError(s.address, wrappedConnErr, nil))\n\ts.pool.clear(err, serviceID)\n\ts.heartbeatListener.StopListening()\n}\n\n// Description returns a description of the server as of the last heartbeat.\nfunc (s *Server) Description() description.Server {\n\treturn s.desc.Load().(description.Server)\n}\n\n// SelectedDescription returns a description.SelectedServer with a Kind of\n// Single. This can be used when performing tasks like monitoring a batch\n// of servers and you want to run one off commands against those servers.\nfunc (s *Server) SelectedDescription() description.SelectedServer {\n\tsdesc := s.Description()\n\treturn description.SelectedServer{\n\t\tServer: sdesc,\n\t\tKind:   description.TopologyKindSingle,\n\t}\n}\n\n// Subscribe returns a ServerSubscription which has a channel on which all\n// updated server descriptions will be sent. The channel will have a buffer\n// size of one, and will be pre-populated with the current description.\nfunc (s *Server) Subscribe() (*ServerSubscription, error) {\n\tif atomic.LoadInt64(&s.state) != serverConnected {\n\t\treturn nil, ErrSubscribeAfterClosed\n\t}\n\tch := make(chan description.Server, 1)\n\tch <- s.desc.Load().(description.Server)\n\n\ts.subLock.Lock()\n\tdefer s.subLock.Unlock()\n\tif s.subscriptionsClosed {\n\t\treturn nil, ErrSubscribeAfterClosed\n\t}\n\tid := s.currentSubscriberID\n\ts.subscribers[id] = ch\n\ts.currentSubscriberID++\n\n\tss := &ServerSubscription{\n\t\tC:  ch,\n\t\ts:  s,\n\t\tid: id,\n\t}\n\n\treturn ss, nil\n}\n\n// RequestImmediateCheck will cause the server to send a heartbeat immediately\n// instead of waiting for the heartbeat timeout.\nfunc (s *Server) RequestImmediateCheck() {\n\tselect {\n\tcase s.checkNow <- struct{}{}:\n\tdefault:\n\t}\n}\n\n// getWriteConcernErrorForProcessing extracts a driver.WriteConcernError from the provided error. This function returns\n// (error, true) if the error is a WriteConcernError and the falls under the requirements for SDAM error\n// handling and (nil, false) otherwise.\nfunc getWriteConcernErrorForProcessing(err error) (*driver.WriteConcernError, bool) {\n\tvar writeCmdErr driver.WriteCommandError\n\tif !errors.As(err, &writeCmdErr) {\n\t\treturn nil, false\n\t}\n\n\twcerr := writeCmdErr.WriteConcernError\n\tif wcerr != nil && (wcerr.NodeIsRecovering() || wcerr.NotPrimary()) {\n\t\treturn wcerr, true\n\t}\n\treturn nil, false\n}\n\n// ProcessError handles SDAM error handling and implements driver.ErrorProcessor.\nfunc (s *Server) ProcessError(err error, describer mnet.Describer) driver.ProcessErrorResult {\n\t// Ignore nil errors.\n\tif err == nil {\n\t\treturn driver.NoChange\n\t}\n\n\t// Ignore errors from stale connections because the error came from a previous generation of the\n\t// connection pool. The root cause of the error has already been handled, which is what caused\n\t// the pool generation to increment. Processing errors for stale connections could result in\n\t// handling the same error root cause multiple times (e.g. a temporary network interrupt causing\n\t// all connections to the same server to return errors).\n\tif describer.Stale() {\n\t\treturn driver.NoChange\n\t}\n\n\t// Must hold the processErrorLock while updating the server description and clearing the pool.\n\t// Not holding the lock leads to possible out-of-order processing of pool.clear() and\n\t// pool.ready() calls from concurrent server description updates.\n\ts.processErrorLock.Lock()\n\tdefer s.processErrorLock.Unlock()\n\n\t// Get the wire version and service ID from the connection description because they will never\n\t// change for the lifetime of a connection and can possibly be different between connections to\n\t// the same server.\n\tconnDesc := describer.Description()\n\twireVersion := connDesc.WireVersion\n\tserviceID := connDesc.ServiceID\n\n\t// Get the topology version from the Server description because the Server description is\n\t// updated by heartbeats and errors, so typically has a more up-to-date topology version.\n\tserverDesc := s.desc.Load().(description.Server)\n\ttopologyVersion := serverDesc.TopologyVersion\n\n\t// We don't currently update the Server topology version when we create new application\n\t// connections, so it's possible for a connection's topology version to be newer than the\n\t// Server's topology version. Pick the \"newest\" of the two topology versions.\n\t// Technically a nil topology version on a new database response should be considered a new\n\t// topology version and replace the Server's topology version. However, we don't know if the\n\t// connection's topology version is based on a new or old database response, so we ignore a nil\n\t// topology version on the connection for now.\n\t//\n\t// TODO(GODRIVER-2841): Remove this logic once we set the Server description when we create\n\t// TODO application connections because then the Server's topology version will always be the\n\t// TODO latest known.\n\tif tv := connDesc.TopologyVersion; tv != nil && driverutil.CompareTopologyVersions(topologyVersion, tv) < 0 {\n\t\ttopologyVersion = tv\n\t}\n\n\t// Invalidate server description if not primary or node recovering error occurs.\n\t// These errors can be reported as a command error or a write concern error.\n\tif cerr, ok := err.(driver.Error); ok && (cerr.NodeIsRecovering() || cerr.NotPrimary()) {\n\t\t// Ignore errors that came from when the database was on a previous topology version.\n\t\tif driverutil.CompareTopologyVersions(topologyVersion, cerr.TopologyVersion) >= 0 {\n\t\t\treturn driver.NoChange\n\t\t}\n\n\t\t// updates description to unknown\n\t\ts.updateDescription(newServerDescriptionFromError(s.address, err, cerr.TopologyVersion))\n\t\ts.RequestImmediateCheck()\n\n\t\tres := driver.ServerMarkedUnknown\n\t\t// If the node is shutting down or is older than 4.2, we synchronously clear the pool\n\t\tif cerr.NodeIsShuttingDown() || wireVersion == nil || wireVersion.Max < wireVersion42 {\n\t\t\tres = driver.ConnectionPoolCleared\n\t\t\ts.pool.clear(err, serviceID)\n\t\t}\n\n\t\treturn res\n\t}\n\tif wcerr, ok := getWriteConcernErrorForProcessing(err); ok {\n\t\t// Ignore errors that came from when the database was on a previous topology version.\n\t\tif driverutil.CompareTopologyVersions(topologyVersion, wcerr.TopologyVersion) >= 0 {\n\t\t\treturn driver.NoChange\n\t\t}\n\n\t\t// updates description to unknown\n\t\ts.updateDescription(newServerDescriptionFromError(s.address, err, wcerr.TopologyVersion))\n\t\ts.RequestImmediateCheck()\n\n\t\tres := driver.ServerMarkedUnknown\n\t\t// If the node is shutting down or is older than 4.2, we synchronously clear the pool\n\t\tif wcerr.NodeIsShuttingDown() || wireVersion == nil || wireVersion.Max < wireVersion42 {\n\t\t\tres = driver.ConnectionPoolCleared\n\t\t\ts.pool.clear(err, serviceID)\n\t\t}\n\t\treturn res\n\t}\n\n\twrappedConnErr := unwrapConnectionError(err)\n\tif wrappedConnErr == nil {\n\t\treturn driver.NoChange\n\t}\n\n\t// Ignore transient timeout errors.\n\tif netErr, ok := wrappedConnErr.(net.Error); ok && netErr.Timeout() {\n\t\treturn driver.NoChange\n\t}\n\tif errors.Is(wrappedConnErr, context.Canceled) || errors.Is(wrappedConnErr, context.DeadlineExceeded) {\n\t\treturn driver.NoChange\n\t}\n\n\t// For a non-timeout network error, we clear the pool, set the description to Unknown, and cancel the in-progress\n\t// monitoring check. The check is cancelled last to avoid a post-cancellation reconnect racing with\n\t// updateDescription.\n\ts.updateDescription(newServerDescriptionFromError(s.address, err, nil))\n\ts.pool.clear(err, serviceID)\n\ts.heartbeatListener.StopListening()\n\treturn driver.ConnectionPoolCleared\n}\n\ntype serverChecker interface {\n\tcheck(ctx context.Context) (description.Server, error)\n}\n\nvar _ serverChecker = &Server{}\n\n// checkServerWithSignal will run the server heartbeat check, canceling if the\n// sig channel's buffer is emptied or is closed.\nfunc checkServerWithSignal(\n\tchecker serverChecker,\n\tconn *connection,\n\tlistener contextListener,\n) (description.Server, error) {\n\t// Create a context for the blocking operations associated with checking the\n\t// status of a server.\n\t//\n\t// The Server Monitoring spec already mandates that drivers set and\n\t// dynamically update the read/write timeout of the dedicated connections\n\t// used in monitoring threads, so we rely on that to time out commands\n\t// rather than adding complexity to the behavior of timeoutMS.\n\tctx, cancel := context.WithCancel(context.Background())\n\n\tdefer listener.StopListening()\n\tdefer cancel()\n\n\tgo func(conn *connection) {\n\t\tdefer cancel()\n\n\t\tvar aborted bool\n\t\tlistener.Listen(ctx, func() {\n\t\t\taborted = true\n\t\t})\n\n\t\t// Close the connection if the listener was stopped before\n\t\t// checkServerWithSignal terminates.\n\t\tif !aborted {\n\t\t\tif conn == nil {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\t// If the connection exists, we need to wait for it to be connected\n\t\t\t// because conn.connect() and conn.close() cannot be called concurrently.\n\t\t\t// If the connection wasn't successfully opened, its state was set back\n\t\t\t// to disconnected, so calling conn.close() will be a no-op.\n\t\t\tconn.closeConnectContext()\n\t\t\tconn.wait()\n\t\t\tconn.prevCanceled.Store(true)\n\t\t\t_ = conn.close()\n\t\t}\n\t}(conn)\n\n\treturn checker.check(ctx)\n}\n\n// update handle performing heartbeats and updating any subscribers of the\n// newest description.Server retrieved.\nfunc (s *Server) update() {\n\tdefer s.closewg.Done()\n\theartbeatTicker := time.NewTicker(s.cfg.heartbeatInterval)\n\trateLimiter := time.NewTicker(minHeartbeatInterval)\n\tdefer heartbeatTicker.Stop()\n\tdefer rateLimiter.Stop()\n\tcheckNow := s.checkNow\n\tdone := s.done\n\n\tdefer func() {\n\t\t_ = recover()\n\t}()\n\n\tcloseServer := func() {\n\t\ts.subLock.Lock()\n\t\tfor id, c := range s.subscribers {\n\t\t\tclose(c)\n\t\t\tdelete(s.subscribers, id)\n\t\t}\n\t\ts.subscriptionsClosed = true\n\t\ts.subLock.Unlock()\n\n\t\tif s.conn != nil {\n\t\t\t_ = s.conn.close()\n\t\t}\n\t}\n\n\twaitUntilNextCheck := func() {\n\t\t// Wait until heartbeatFrequency elapses, an application operation requests an immediate check, or the server\n\t\t// is disconnecting.\n\t\tselect {\n\t\tcase <-heartbeatTicker.C:\n\t\tcase <-checkNow:\n\t\tcase <-done:\n\t\t\t// Return because the next update iteration will check the done channel again and clean up.\n\t\t\treturn\n\t\t}\n\n\t\t// Ensure we only return if minHeartbeatFrequency has elapsed or the server is disconnecting.\n\t\tselect {\n\t\tcase <-rateLimiter.C:\n\t\tcase <-done:\n\t\t\treturn\n\t\t}\n\t}\n\n\ttimeoutCnt := 0\n\tfor {\n\t\t// Check if the server is disconnecting. Even if waitForNextCheck has already read from the done channel, we\n\t\t// can safely read from it again because Disconnect closes the channel.\n\t\tselect {\n\t\tcase <-done:\n\t\t\tcloseServer()\n\t\t\treturn\n\t\tdefault:\n\t\t}\n\n\t\tpreviousDescription := s.Description()\n\n\t\tdesc, err := checkServerWithSignal(s, s.conn, s.heartbeatListener)\n\n\t\t// The only error returned from checkServerWithSignal is errCheckCancelled.\n\t\tif errors.Is(err, errCheckCancelled) {\n\t\t\tif atomic.LoadInt64(&s.state) != serverConnected {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\t// If the server is not disconnecting, the check was cancelled by an application operation after an error.\n\t\t\t// Wait before running the next check.\n\t\t\twaitUntilNextCheck()\n\t\t\tcontinue\n\t\t}\n\n\t\tif isShortcut := func() bool {\n\t\t\t// Must hold the processErrorLock while updating the server description and clearing the\n\t\t\t// pool. Not holding the lock leads to possible out-of-order processing of pool.clear() and\n\t\t\t// pool.ready() calls from concurrent server description updates.\n\t\t\ts.processErrorLock.Lock()\n\t\t\tdefer s.processErrorLock.Unlock()\n\n\t\t\ts.updateDescription(desc)\n\t\t\t// Retry after the first timeout before clearing the pool in case of a FAAS pause as\n\t\t\t// described in GODRIVER-2577.\n\t\t\tif err := unwrapConnectionError(desc.LastError); err != nil && timeoutCnt < 1 {\n\t\t\t\tif errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) {\n\t\t\t\t\ttimeoutCnt++\n\t\t\t\t\t// We want to immediately retry on timeout error. Continue to next loop.\n\t\t\t\t\treturn true\n\t\t\t\t}\n\t\t\t\tif err, ok := err.(net.Error); ok && err.Timeout() {\n\t\t\t\t\ttimeoutCnt++\n\t\t\t\t\t// We want to immediately retry on timeout error. Continue to next loop.\n\t\t\t\t\treturn true\n\t\t\t\t}\n\t\t\t}\n\t\t\tif err := desc.LastError; err != nil {\n\t\t\t\t// Clear the pool once the description has been updated to Unknown. Pass in a nil service ID to clear\n\t\t\t\t// because the monitoring routine only runs for non-load balanced deployments in which servers don't return\n\t\t\t\t// IDs.\n\t\t\t\tif timeoutCnt > 0 {\n\t\t\t\t\ts.pool.clearAll(err, nil)\n\t\t\t\t} else {\n\t\t\t\t\ts.pool.clear(err, nil)\n\t\t\t\t}\n\t\t\t}\n\t\t\t// We're either not handling a timeout error, or we just handled the 2nd consecutive\n\t\t\t// timeout error. In either case, reset the timeout count to 0 and return false to\n\t\t\t// continue the normal check process.\n\t\t\ttimeoutCnt = 0\n\t\t\treturn false\n\t\t}(); isShortcut {\n\t\t\tcontinue\n\t\t}\n\n\t\t// If the server supports streaming or we're already streaming, we want to move to streaming the next response\n\t\t// without waiting. If the server has transitioned to Unknown from a network error, we want to do another\n\t\t// check without waiting in case it was a transient error and the server isn't actually down.\n\t\tconnectionIsStreaming := s.conn != nil && s.conn.getCurrentlyStreaming()\n\t\ttransitionedFromNetworkError := desc.LastError != nil && unwrapConnectionError(desc.LastError) != nil &&\n\t\t\tpreviousDescription.Kind != description.Unknown\n\n\t\tif isStreamingEnabled(s) && isStreamable(s) {\n\t\t\ts.monitorOnce.Do(s.rttMonitor.connect)\n\t\t}\n\n\t\tif isStreamingEnabled(s) && (isStreamable(s) || connectionIsStreaming) || transitionedFromNetworkError {\n\t\t\tcontinue\n\t\t}\n\n\t\t// The server either does not support the streamable protocol or is not in a healthy state, so we wait until\n\t\t// the next check.\n\t\twaitUntilNextCheck()\n\t}\n}\n\n// updateDescription handles updating the description on the Server, notifying\n// subscribers, and potentially draining the connection pool. The initial\n// parameter is used to determine if this is the first description from the\n// server.\nfunc (s *Server) updateDescription(desc description.Server) {\n\tif s.cfg.loadBalanced {\n\t\t// In load balanced mode, there are no updates from the monitoring routine. For errors encountered in pooled\n\t\t// connections, the server should not be marked Unknown to ensure that the LB remains selectable.\n\t\treturn\n\t}\n\n\tdefer func() {\n\t\t//  ¯\\_(ツ)_/¯\n\t\t_ = recover()\n\t}()\n\n\t// Anytime we update the server description to something other than \"unknown\", set the pool to\n\t// \"ready\". Do this before updating the description so that connections can be checked out as\n\t// soon as the server is selectable. If the pool is already ready, this operation is a no-op.\n\t// Note that this behavior is roughly consistent with the current Go driver behavior (connects\n\t// to all servers, even non-data-bearing nodes) but deviates slightly from CMAP spec, which\n\t// specifies a more restricted set of server descriptions and topologies that should mark the\n\t// pool ready. We don't have access to the topology here, so prefer the current Go driver\n\t// behavior for simplicity.\n\tif desc.Kind != description.Unknown {\n\t\t_ = s.pool.ready()\n\t}\n\n\t// Use the updateTopologyCallback to update the parent Topology and get the description that should be stored.\n\tcallback, ok := s.updateTopologyCallback.Load().(updateTopologyCallback)\n\tif ok && callback != nil {\n\t\tdesc = callback(desc)\n\t}\n\ts.desc.Store(desc)\n\n\ts.subLock.Lock()\n\tfor _, c := range s.subscribers {\n\t\tselect {\n\t\t// drain the channel if it isn't empty\n\t\tcase <-c:\n\t\tdefault:\n\t\t}\n\t\tc <- desc\n\t}\n\ts.subLock.Unlock()\n}\n\n// createConnection creates a new connection instance but does not call connect on it. The caller must call connect\n// before the connection can be used for network operations.\nfunc (s *Server) createConnection() *connection {\n\topts := copyConnectionOpts(s.cfg.connectionOpts)\n\topts = append(opts,\n\t\tWithHandshaker(func(Handshaker) Handshaker {\n\t\t\thandshaker := operation.NewHello().AppName(s.cfg.appname).Compressors(s.cfg.compressionOpts).\n\t\t\t\tServerAPI(s.cfg.serverAPI)\n\n\t\t\tif s.cfg.driverInfo != nil {\n\t\t\t\tdriverInfo := s.cfg.driverInfo.Load()\n\t\t\t\tif driverInfo != nil {\n\t\t\t\t\thandshaker = handshaker.OuterLibraryName(driverInfo.Name).OuterLibraryVersion(driverInfo.Version).\n\t\t\t\t\t\tOuterLibraryPlatform(driverInfo.Platform)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn handshaker\n\t\t}),\n\t\t// Override any monitors specified in options with nil to avoid monitoring heartbeats.\n\t\tWithMonitor(func(*event.CommandMonitor) *event.CommandMonitor { return nil }),\n\t)\n\n\treturn newConnection(s.address, opts...)\n}\n\nfunc copyConnectionOpts(opts []ConnectionOption) []ConnectionOption {\n\toptsCopy := make([]ConnectionOption, len(opts))\n\tcopy(optsCopy, opts)\n\treturn optsCopy\n}\n\nfunc (s *Server) setupHeartbeatConnection(ctx context.Context) error {\n\tconn := s.createConnection()\n\n\ts.conn = conn\n\n\tif s.cfg.connectTimeout != 0 {\n\t\tvar cancelFn context.CancelFunc\n\t\tctx, cancelFn = context.WithTimeout(ctx, s.cfg.connectTimeout)\n\n\t\tdefer cancelFn()\n\t}\n\n\treturn s.conn.connect(ctx)\n}\n\nfunc (s *Server) createBaseOperation(conn *mnet.Connection) *operation.Hello {\n\treturn operation.\n\t\tNewHello().\n\t\tDeployment(driver.SingleConnectionDeployment{C: conn}).\n\t\tServerAPI(s.cfg.serverAPI)\n}\n\nfunc isStreamingEnabled(srv *Server) bool {\n\tswitch srv.cfg.serverMonitoringMode {\n\tcase connstring.ServerMonitoringModeStream:\n\t\treturn true\n\tcase connstring.ServerMonitoringModePoll:\n\t\treturn false\n\tdefault:\n\t\treturn driverutil.GetFaasEnvName() == \"\"\n\t}\n}\n\nfunc isStreamable(srv *Server) bool {\n\treturn srv.Description().Kind != description.Unknown && srv.Description().TopologyVersion != nil\n}\n\nfunc (s *Server) streamable() bool {\n\treturn isStreamingEnabled(s) && isStreamable(s)\n}\n\n// getHeartbeatTimeout will return the maximum allowable duration for streaming\n// or polling a hello command during server monitoring.\nfunc getHeartbeatTimeout(srv *Server) time.Duration {\n\tif srv.conn.getCurrentlyStreaming() || srv.streamable() {\n\t\t// If connectTimeoutMS=0, the operation timeout should be infinite.\n\t\t// Otherwise, it is connectTimeoutMS + heartbeatFrequencyMS to account for\n\t\t// the fact that the query will block for heartbeatFrequencyMS\n\t\t// server-side.\n\t\tstreamingTO := srv.cfg.connectTimeout\n\t\tif streamingTO != 0 {\n\t\t\tstreamingTO += srv.cfg.heartbeatInterval\n\t\t}\n\n\t\treturn streamingTO\n\t}\n\n\t// The server doesn't support the awaitable protocol. Set the timeout to\n\t// connectTimeoutMS and execute a regular heartbeat without any additional\n\t// parameters.\n\treturn srv.cfg.connectTimeout\n}\n\n// withHeartbeatTimeout will apply the appropriate timeout to the parent context\n// for server monitoring.\nfunc withHeartbeatTimeout(parent context.Context, srv *Server) (context.Context, context.CancelFunc) {\n\tvar cancel context.CancelFunc\n\n\ttimeout := getHeartbeatTimeout(srv)\n\tif timeout == 0 {\n\t\treturn parent, cancel\n\t}\n\n\treturn context.WithTimeout(parent, timeout)\n}\n\n// doHandshake will construct the hello operation use to execute a handshake\n// between the client and a server. Depending on the configuration and version,\n// this function will either poll, stream, or resume streaming.\nfunc doHandshake(ctx context.Context, srv *Server) (description.Server, error) {\n\theartbeatConn := mnet.NewConnection(initConnection{srv.conn})\n\thandshakeOp := srv.createBaseOperation(heartbeatConn)\n\n\t// Using timeoutMS in the monitoring and RTT calculation threads would require\n\t// another special case in the code that derives maxTimeMS from timeoutMS\n\t// because the awaitable hello requests sent to 4.4+ servers already have a\n\t// maxAwaitTimeMS field. Adding maxTimeMS also does not help for non-awaitable\n\t// hello commands because we expect them to execute quickly on the server. The\n\t// Server Monitoring spec already mandates that drivers set and dynamically\n\t// update the read/write timeout of the dedicated connections used in\n\t// monitoring threads, so we rely on that to time out commands rather than\n\t// adding complexity to the behavior of timeoutMS.\n\thandshakeOp = handshakeOp.OmitMaxTimeMS(true)\n\n\t// Apply monitoring timeout.\n\tctx, cancel := withHeartbeatTimeout(ctx, srv)\n\tdefer cancel()\n\n\t// If we are currently streaming, get more data and return the result.\n\tif srv.conn.getCurrentlyStreaming() {\n\t\tif err := handshakeOp.StreamResponse(ctx, heartbeatConn); err != nil {\n\t\t\treturn description.Server{}, err\n\t\t}\n\n\t\treturn handshakeOp.Result(srv.address), nil\n\t}\n\n\t// If the server supports streaming, update it so the next handshake streams\n\t// the response.\n\tif srv.streamable() {\n\t\tsrv.conn.setCanStream(true)\n\n\t\tmaxAwaitTimeMS := int64(srv.cfg.heartbeatInterval) / 1e6\n\n\t\thandshakeOp = handshakeOp.\n\t\t\tTopologyVersion(srv.Description().TopologyVersion).\n\t\t\tMaxAwaitTimeMS(maxAwaitTimeMS)\n\t}\n\n\t// Perform the handshake.\n\tif err := handshakeOp.Execute(ctx); err != nil {\n\t\treturn description.Server{}, err\n\t}\n\n\treturn handshakeOp.Result(srv.address), nil\n}\n\nfunc (s *Server) check(ctx context.Context) (description.Server, error) {\n\tvar descPtr *description.Server\n\tvar err error\n\tvar execDuration time.Duration\n\n\tstart := time.Now()\n\n\tvar previousCanceled bool\n\tif s.conn != nil {\n\t\tpreviousCanceled = s.conn.previousCanceled()\n\t}\n\n\t// Create a new connection if this is the first check, the connection was closed after an error during the previous\n\t// check, or the previous check was cancelled.\n\tif s.conn == nil || s.conn.closed() || previousCanceled {\n\t\tconnID := \"0\"\n\t\tif s.conn != nil {\n\t\t\tconnID = s.conn.ID()\n\t\t}\n\t\ts.publishServerHeartbeatStartedEvent(connID, false)\n\t\t// Create a new connection and add it's handshake RTT as a sample.\n\t\terr = s.setupHeartbeatConnection(ctx)\n\t\texecDuration = time.Since(start)\n\t\tconnID = \"0\"\n\t\tif s.conn != nil {\n\t\t\tconnID = s.conn.ID()\n\t\t}\n\t\tif err == nil {\n\t\t\t// Use the description from the connection handshake as the value for this check.\n\t\t\ts.rttMonitor.addSample(s.conn.helloRTT)\n\t\t\tdescPtr = &s.conn.desc\n\t\t\ts.publishServerHeartbeatSucceededEvent(connID, execDuration, s.conn.desc, false)\n\t\t} else {\n\t\t\terr = unwrapConnectionError(err)\n\t\t\ts.publishServerHeartbeatFailedEvent(connID, execDuration, err, false)\n\t\t}\n\t} else {\n\t\t// An existing connection is being used. Use the server description\n\t\t// properties to execute the right heartbeat.\n\n\t\tstreamable := isStreamingEnabled(s) && isStreamable(s)\n\n\t\ts.publishServerHeartbeatStartedEvent(s.conn.ID(), s.conn.getCurrentlyStreaming() || streamable)\n\n\t\tvar tempDesc description.Server\n\t\ttempDesc, err = doHandshake(ctx, s) // Perform a handshake with the server\n\n\t\texecDuration = time.Since(start)\n\n\t\t// We need to record an RTT sample in the polling case so that if the server\n\t\t// is < 4.4, or if polling is specified by the user, then the\n\t\t// RTT-short-circuit feature of CSOT is not disabled.\n\t\tif !streamable {\n\t\t\ts.rttMonitor.addSample(execDuration)\n\t\t}\n\n\t\tif err == nil {\n\t\t\tdescPtr = &tempDesc\n\t\t\ts.publishServerHeartbeatSucceededEvent(s.conn.ID(), execDuration,\n\t\t\t\ttempDesc, s.conn.getCurrentlyStreaming() || streamable)\n\t\t} else {\n\t\t\t// Close the connection here rather than below so we ensure we're not closing a connection that wasn't\n\t\t\t// successfully created.\n\t\t\tif s.conn != nil {\n\t\t\t\t_ = s.conn.close()\n\t\t\t}\n\t\t\ts.publishServerHeartbeatFailedEvent(s.conn.ID(), execDuration, err, s.conn.getCurrentlyStreaming() || streamable)\n\t\t}\n\t}\n\n\tif descPtr != nil {\n\t\t// The check was successful. Set the average RTT and return.\n\t\tdesc := *descPtr\n\t\tdesc.AverageRTT = s.rttMonitor.EWMA()\n\t\tdesc.AverageRTTSet = true\n\t\tdesc.HeartbeatInterval = s.cfg.heartbeatInterval\n\n\t\treturn desc, nil\n\t}\n\n\tif previousCanceled {\n\t\t// If the previous check was cancelled, we don't want to clear the pool. Return a sentinel error so the caller\n\t\t// will know that an actual error didn't occur.\n\t\treturn emptyDescription, errCheckCancelled\n\t}\n\n\t// An error occurred. We reset the RTT monitor for all errors and return an Unknown description. The pool must also\n\t// be cleared, but only after the description has already been updated, so that is handled by the caller.\n\ttopologyVersion := extractTopologyVersion(err)\n\ts.rttMonitor.reset()\n\treturn newServerDescriptionFromError(s.address, err, topologyVersion), nil\n}\n\nfunc extractTopologyVersion(err error) *description.TopologyVersion {\n\tif ce, ok := err.(ConnectionError); ok {\n\t\terr = ce.Wrapped\n\t}\n\n\tswitch converted := err.(type) {\n\tcase driver.Error:\n\t\treturn converted.TopologyVersion\n\tcase driver.WriteCommandError:\n\t\tif converted.WriteConcernError != nil {\n\t\t\treturn converted.WriteConcernError.TopologyVersion\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// RTTMonitor returns this server's round-trip-time monitor.\nfunc (s *Server) RTTMonitor() driver.RTTMonitor {\n\treturn s.rttMonitor\n}\n\n// OperationCount returns the current number of in-progress operations for this server.\nfunc (s *Server) OperationCount() int64 {\n\treturn atomic.LoadInt64(&s.operationCount)\n}\n\n// String implements the Stringer interface.\nfunc (s *Server) String() string {\n\tdesc := s.Description()\n\tstate := atomic.LoadInt64(&s.state)\n\tstr := fmt.Sprintf(\"Addr: %s, Type: %s, State: %s\",\n\t\ts.address, desc.Kind, serverStateString(state))\n\tif len(desc.Tags) != 0 {\n\t\tstr += fmt.Sprintf(\", Tag sets: %s\", desc.Tags)\n\t}\n\tif state == serverConnected {\n\t\tstr += fmt.Sprintf(\", Average RTT: %s, Min RTT: %s\", desc.AverageRTT, s.RTTMonitor().Min())\n\t}\n\tif desc.LastError != nil {\n\t\tstr += fmt.Sprintf(\", Last error: %s\", desc.LastError)\n\t}\n\n\treturn str\n}\n\n// ServerSubscription represents a subscription to the description.Server updates for\n// a specific server.\ntype ServerSubscription struct {\n\tC  <-chan description.Server\n\ts  *Server\n\tid uint64\n}\n\n// Unsubscribe unsubscribes this ServerSubscription from updates and closes the\n// subscription channel.\nfunc (ss *ServerSubscription) Unsubscribe() error {\n\tss.s.subLock.Lock()\n\tdefer ss.s.subLock.Unlock()\n\tif ss.s.subscriptionsClosed {\n\t\treturn nil\n\t}\n\n\tch, ok := ss.s.subscribers[ss.id]\n\tif !ok {\n\t\treturn nil\n\t}\n\n\tclose(ch)\n\tdelete(ss.s.subscribers, ss.id)\n\n\treturn nil\n}\n\n// publishes a ServerOpeningEvent to indicate the server is being initialized\nfunc (s *Server) publishServerOpeningEvent(addr address.Address) {\n\tif s == nil {\n\t\treturn\n\t}\n\n\tserverOpening := &event.ServerOpeningEvent{\n\t\tAddress:    addr,\n\t\tTopologyID: s.topologyID,\n\t}\n\n\tif s.cfg.serverMonitor != nil && s.cfg.serverMonitor.ServerOpening != nil {\n\t\ts.cfg.serverMonitor.ServerOpening(serverOpening)\n\t}\n\n\tif mustLogServerMessage(s) {\n\t\tlogServerMessage(s, logger.TopologyServerOpening)\n\t}\n}\n\n// publishes a ServerHeartbeatStartedEvent to indicate a hello command has started\nfunc (s *Server) publishServerHeartbeatStartedEvent(connectionID string, await bool) {\n\tserverHeartbeatStarted := &event.ServerHeartbeatStartedEvent{\n\t\tConnectionID: connectionID,\n\t\tAwaited:      await,\n\t}\n\n\tif s != nil && s.cfg.serverMonitor != nil && s.cfg.serverMonitor.ServerHeartbeatStarted != nil {\n\t\ts.cfg.serverMonitor.ServerHeartbeatStarted(serverHeartbeatStarted)\n\t}\n\n\tif mustLogServerMessage(s) {\n\t\tlogServerMessage(s, logger.TopologyServerHeartbeatStarted,\n\t\t\tlogger.KeyAwaited, await)\n\t}\n}\n\n// publishes a ServerHeartbeatSucceededEvent to indicate hello has succeeded\nfunc (s *Server) publishServerHeartbeatSucceededEvent(connectionID string,\n\tduration time.Duration,\n\tdesc description.Server,\n\tawait bool,\n) {\n\tserverHeartbeatSucceeded := &event.ServerHeartbeatSucceededEvent{\n\t\tDuration:     duration,\n\t\tReply:        newEventServerDescription(desc),\n\t\tConnectionID: connectionID,\n\t\tAwaited:      await,\n\t}\n\n\tif s != nil && s.cfg.serverMonitor != nil && s.cfg.serverMonitor.ServerHeartbeatSucceeded != nil {\n\t\ts.cfg.serverMonitor.ServerHeartbeatSucceeded(serverHeartbeatSucceeded)\n\t}\n\n\tif mustLogServerMessage(s) {\n\t\tdescRaw, _ := bson.Marshal(struct {\n\t\t\tdescription.Server `bson:\",inline\"`\n\t\t\tOk                 int32\n\t\t}{\n\t\t\tServer: desc,\n\t\t\tOk: func() int32 {\n\t\t\t\tif desc.LastError != nil {\n\t\t\t\t\treturn 0\n\t\t\t\t}\n\n\t\t\t\treturn 1\n\t\t\t}(),\n\t\t})\n\n\t\tlogServerMessage(s, logger.TopologyServerHeartbeatSucceeded,\n\t\t\tlogger.KeyAwaited, await,\n\t\t\tlogger.KeyDurationMS, duration.Milliseconds(),\n\t\t\tlogger.KeyReply, bson.Raw(descRaw).String())\n\t}\n}\n\n// publishes a ServerHeartbeatFailedEvent to indicate hello has failed\nfunc (s *Server) publishServerHeartbeatFailedEvent(connectionID string,\n\tduration time.Duration,\n\terr error,\n\tawait bool,\n) {\n\tserverHeartbeatFailed := &event.ServerHeartbeatFailedEvent{\n\t\tDuration:     duration,\n\t\tFailure:      err,\n\t\tConnectionID: connectionID,\n\t\tAwaited:      await,\n\t}\n\n\tif s != nil && s.cfg.serverMonitor != nil && s.cfg.serverMonitor.ServerHeartbeatFailed != nil {\n\t\ts.cfg.serverMonitor.ServerHeartbeatFailed(serverHeartbeatFailed)\n\t}\n\n\tif mustLogServerMessage(s) {\n\t\tlogServerMessage(s, logger.TopologyServerHeartbeatFailed,\n\t\t\tlogger.KeyAwaited, await,\n\t\t\tlogger.KeyDurationMS, duration.Milliseconds(),\n\t\t\tlogger.KeyFailure, err.Error())\n\t}\n}\n\n// unwrapConnectionError returns the connection error wrapped by err, or nil if err does not wrap a connection error.\nfunc unwrapConnectionError(err error) error {\n\t// This is essentially an implementation of errors.As to unwrap this error until we get a ConnectionError and then\n\t// return ConnectionError.Wrapped.\n\n\tconnErr, ok := err.(ConnectionError)\n\tif ok {\n\t\treturn connErr.Wrapped\n\t}\n\n\tdriverErr, ok := err.(driver.Error)\n\tif !ok || !driverErr.NetworkError() {\n\t\treturn nil\n\t}\n\n\tconnErr, ok = driverErr.Wrapped.(ConnectionError)\n\tif ok {\n\t\treturn connErr.Wrapped\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "x/mongo/driver/topology/server_options.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage topology\n\nimport (\n\t\"sync/atomic\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/event\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/logger\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/connstring\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/session\"\n)\n\nvar defaultRegistry = bson.NewRegistry()\n\ntype serverConfig struct {\n\tclock                *session.ClusterClock\n\tcompressionOpts      []string\n\tconnectionOpts       []ConnectionOption\n\tappname              string\n\theartbeatInterval    time.Duration\n\tconnectTimeout       time.Duration\n\tserverMonitoringMode string\n\tserverMonitor        *event.ServerMonitor\n\tregistry             *bson.Registry\n\tmonitoringDisabled   bool\n\tserverAPI            *driver.ServerAPIOptions\n\tloadBalanced         bool\n\tdriverInfo           *atomic.Pointer[options.DriverInfo]\n\n\t// Connection pool options.\n\tmaxConns             uint64\n\tminConns             uint64\n\tmaxConnecting        uint64\n\tpoolMonitor          *event.PoolMonitor\n\tlogger               *logger.Logger\n\tpoolMaxIdleTime      time.Duration\n\tpoolMaintainInterval time.Duration\n}\n\nfunc newServerConfig(connectTimeout time.Duration, opts ...ServerOption) *serverConfig {\n\tcfg := &serverConfig{\n\t\theartbeatInterval: 10 * time.Second,\n\t\tconnectTimeout:    connectTimeout,\n\t\tregistry:          defaultRegistry,\n\t}\n\n\tfor _, opt := range opts {\n\t\tif opt == nil {\n\t\t\tcontinue\n\t\t}\n\t\topt(cfg)\n\t}\n\n\treturn cfg\n}\n\n// ServerOption configures a server.\ntype ServerOption func(*serverConfig)\n\n// ServerAPIFromServerOptions will return the server API options if they have been functionally set on the ServerOption\n// slice.\nfunc ServerAPIFromServerOptions(connectTimeout time.Duration, opts []ServerOption) *driver.ServerAPIOptions {\n\treturn newServerConfig(connectTimeout, opts...).serverAPI\n}\n\nfunc withMonitoringDisabled(fn func(bool) bool) ServerOption {\n\treturn func(cfg *serverConfig) {\n\t\tcfg.monitoringDisabled = fn(cfg.monitoringDisabled)\n\t}\n}\n\n// WithConnectionOptions configures the server's connections.\nfunc WithConnectionOptions(fn func(...ConnectionOption) []ConnectionOption) ServerOption {\n\treturn func(cfg *serverConfig) {\n\t\tcfg.connectionOpts = fn(cfg.connectionOpts...)\n\t}\n}\n\n// WithCompressionOptions configures the server's compressors.\nfunc WithCompressionOptions(fn func(...string) []string) ServerOption {\n\treturn func(cfg *serverConfig) {\n\t\tcfg.compressionOpts = fn(cfg.compressionOpts...)\n\t}\n}\n\n// WithServerAppName configures the server's application name.\nfunc WithServerAppName(fn func(string) string) ServerOption {\n\treturn func(cfg *serverConfig) {\n\t\tcfg.appname = fn(cfg.appname)\n\t}\n}\n\n// WithDriverInfo sets at atomic pointer to the server configuration, which will\n// be used to create the \"driver\" section on handshake commands. An atomic\n// pointer is used so that the driver info can be updated concurrently.\nfunc WithDriverInfo(info *atomic.Pointer[options.DriverInfo]) ServerOption {\n\treturn func(cfg *serverConfig) {\n\t\tcfg.driverInfo = info\n\t}\n}\n\n// WithHeartbeatInterval configures a server's heartbeat interval.\nfunc WithHeartbeatInterval(fn func(time.Duration) time.Duration) ServerOption {\n\treturn func(cfg *serverConfig) {\n\t\tcfg.heartbeatInterval = fn(cfg.heartbeatInterval)\n\t}\n}\n\n// WithMaxConnections configures the maximum number of connections to allow for\n// a given server. If max is 0, then maximum connection pool size is not limited.\nfunc WithMaxConnections(fn func(uint64) uint64) ServerOption {\n\treturn func(cfg *serverConfig) {\n\t\tcfg.maxConns = fn(cfg.maxConns)\n\t}\n}\n\n// WithMinConnections configures the minimum number of connections to allow for\n// a given server. If min is 0, then there is no lower limit to the number of\n// connections.\nfunc WithMinConnections(fn func(uint64) uint64) ServerOption {\n\treturn func(cfg *serverConfig) {\n\t\tcfg.minConns = fn(cfg.minConns)\n\t}\n}\n\n// WithMaxConnecting configures the maximum number of connections a connection\n// pool may establish simultaneously. If maxConnecting is 0, the default value\n// of 2 is used.\nfunc WithMaxConnecting(fn func(uint64) uint64) ServerOption {\n\treturn func(cfg *serverConfig) {\n\t\tcfg.maxConnecting = fn(cfg.maxConnecting)\n\t}\n}\n\n// WithConnectionPoolMaxIdleTime configures the maximum time that a connection can remain idle in the connection pool\n// before being removed. If connectionPoolMaxIdleTime is 0, then no idle time is set and connections will not be removed\n// because of their age\nfunc WithConnectionPoolMaxIdleTime(fn func(time.Duration) time.Duration) ServerOption {\n\treturn func(cfg *serverConfig) {\n\t\tcfg.poolMaxIdleTime = fn(cfg.poolMaxIdleTime)\n\t}\n}\n\n// WithConnectionPoolMaintainInterval configures the interval that the background connection pool\n// maintenance goroutine runs.\nfunc WithConnectionPoolMaintainInterval(fn func(time.Duration) time.Duration) ServerOption {\n\treturn func(cfg *serverConfig) {\n\t\tcfg.poolMaintainInterval = fn(cfg.poolMaintainInterval)\n\t}\n}\n\n// WithConnectionPoolMonitor configures the monitor for all connection pool actions\nfunc WithConnectionPoolMonitor(fn func(*event.PoolMonitor) *event.PoolMonitor) ServerOption {\n\treturn func(cfg *serverConfig) {\n\t\tcfg.poolMonitor = fn(cfg.poolMonitor)\n\t}\n}\n\n// WithServerMonitor configures the monitor for all SDAM events for a server\nfunc WithServerMonitor(fn func(*event.ServerMonitor) *event.ServerMonitor) ServerOption {\n\treturn func(cfg *serverConfig) {\n\t\tcfg.serverMonitor = fn(cfg.serverMonitor)\n\t}\n}\n\n// WithClock configures the ClusterClock for the server to use.\nfunc WithClock(fn func(clock *session.ClusterClock) *session.ClusterClock) ServerOption {\n\treturn func(cfg *serverConfig) {\n\t\tcfg.clock = fn(cfg.clock)\n\t}\n}\n\n// WithRegistry configures the registry for the server to use when creating\n// cursors.\nfunc WithRegistry(fn func(*bson.Registry) *bson.Registry) ServerOption {\n\treturn func(cfg *serverConfig) {\n\t\tcfg.registry = fn(cfg.registry)\n\t}\n}\n\n// WithServerAPI configures the server API options for the server to use.\nfunc WithServerAPI(fn func(serverAPI *driver.ServerAPIOptions) *driver.ServerAPIOptions) ServerOption {\n\treturn func(cfg *serverConfig) {\n\t\tcfg.serverAPI = fn(cfg.serverAPI)\n\t}\n}\n\n// WithServerLoadBalanced specifies whether or not the server is behind a load balancer.\nfunc WithServerLoadBalanced(fn func(bool) bool) ServerOption {\n\treturn func(cfg *serverConfig) {\n\t\tcfg.loadBalanced = fn(cfg.loadBalanced)\n\t}\n}\n\n// withLogger configures the logger for the server to use.\nfunc withLogger(fn func() *logger.Logger) ServerOption {\n\treturn func(cfg *serverConfig) {\n\t\tcfg.logger = fn()\n\t}\n}\n\n// withServerMonitoringMode configures the mode (stream, poll, or auto) to use\n// for monitoring.\nfunc withServerMonitoringMode(mode *string) ServerOption {\n\treturn func(cfg *serverConfig) {\n\t\tif mode != nil {\n\t\t\tcfg.serverMonitoringMode = *mode\n\n\t\t\treturn\n\t\t}\n\n\t\tcfg.serverMonitoringMode = connstring.ServerMonitoringModeAuto\n\t}\n}\n"
  },
  {
    "path": "x/mongo/driver/topology/server_rtt_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage topology\n\nimport (\n\t\"encoding/json\"\n\t\"io/ioutil\"\n\t\"path\"\n\t\"testing\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/spectest\"\n)\n\n// Test case for all server selection rtt spec tests.\nfunc TestServerSelectionRTTSpec(t *testing.T) {\n\ttype testCase struct {\n\t\t// AvgRttMs is either \"NULL\" or float\n\t\tAvgRttMs  any     `json:\"avg_rtt_ms\"`\n\t\tNewRttMs  float64 `json:\"new_rtt_ms\"`\n\t\tNewAvgRtt float64 `json:\"new_avg_rtt\"`\n\t}\n\n\ttestsDir := spectest.Path(\"server-selection/tests/rtt\")\n\n\tfor _, file := range spectest.FindJSONFilesInDir(t, testsDir) {\n\t\tfunc(t *testing.T, filename string) {\n\t\t\tfilepath := path.Join(testsDir, filename)\n\t\t\tcontent, err := ioutil.ReadFile(filepath)\n\t\t\tassert.Nil(t, err, \"ReadFile error for %s: %v\", filepath, err)\n\n\t\t\tt.Run(filename, func(t *testing.T) {\n\t\t\t\tvar test testCase\n\t\t\t\terr = json.Unmarshal(content, &test)\n\t\t\t\tassert.Nil(t, err, \"Unmarshal error: %v\", err)\n\n\t\t\t\tmonitor := newRTTMonitor(&rttConfig{\n\t\t\t\t\tinterval: 10 * time.Second,\n\t\t\t\t})\n\t\t\t\tif test.AvgRttMs != \"NULL\" {\n\t\t\t\t\t// If not \"NULL\", then must be a number, so typecast to float64\n\t\t\t\t\tmonitor.addSample(time.Duration(test.AvgRttMs.(float64) * float64(time.Millisecond)))\n\t\t\t\t}\n\n\t\t\t\tmonitor.addSample(time.Duration(test.NewRttMs * float64(time.Millisecond)))\n\t\t\t\texpectedRTT := time.Duration(test.NewAvgRtt * float64(time.Millisecond))\n\t\t\t\tactualRTT := monitor.EWMA()\n\t\t\t\tassert.Equal(t, expectedRTT, actualRTT, \"expected average RTT %s, got %s\", expectedRTT, actualRTT)\n\t\t\t})\n\t\t}(t, file)\n\t}\n}\n"
  },
  {
    "path": "x/mongo/driver/topology/server_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\n//go:build go1.13\n\npackage topology\n\nimport (\n\t\"context\"\n\t\"crypto/tls\"\n\t\"crypto/x509\"\n\t\"errors\"\n\t\"io/ioutil\"\n\t\"net\"\n\t\"os\"\n\t\"runtime\"\n\t\"sync\"\n\t\"sync/atomic\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/google/go-cmp/cmp\"\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/event\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/eventtest\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/require\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/address\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/auth\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/connstring\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/description\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/drivertest\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/mnet\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/wiremessage\"\n)\n\ntype channelNetConnDialer struct{}\n\nfunc (cncd *channelNetConnDialer) DialContext(_ context.Context, _, _ string) (net.Conn, error) {\n\tcnc := &drivertest.ChannelNetConn{\n\t\tWritten:  make(chan []byte, 1),\n\t\tReadResp: make(chan []byte, 2),\n\t\tReadErr:  make(chan error, 1),\n\t}\n\tif err := cnc.AddResponse(makeHelloReply()); err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn cnc, nil\n}\n\ntype errorQueue struct {\n\terrors []error\n\tmutex  sync.Mutex\n}\n\nfunc (eq *errorQueue) head() error {\n\teq.mutex.Lock()\n\tdefer eq.mutex.Unlock()\n\tif len(eq.errors) > 0 {\n\t\treturn eq.errors[0]\n\t}\n\treturn nil\n}\n\nfunc (eq *errorQueue) dequeue() bool {\n\teq.mutex.Lock()\n\tdefer eq.mutex.Unlock()\n\tif len(eq.errors) > 0 {\n\t\teq.errors = eq.errors[1:]\n\t\treturn true\n\t}\n\treturn false\n}\n\ntype timeoutConn struct {\n\tnet.Conn\n\terrors *errorQueue\n}\n\nfunc (c *timeoutConn) Read(b []byte) (int, error) {\n\tn, err := 0, c.errors.head()\n\tif err == nil {\n\t\tn, err = c.Conn.Read(b)\n\t}\n\treturn n, err\n}\n\nfunc (c *timeoutConn) Write(b []byte) (int, error) {\n\tn, err := 0, c.errors.head()\n\tif err == nil {\n\t\tn, err = c.Conn.Write(b)\n\t}\n\treturn n, err\n}\n\ntype timeoutDialer struct {\n\tDialer\n\terrors *errorQueue\n}\n\nfunc (d *timeoutDialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) {\n\tc, e := d.Dialer.DialContext(ctx, network, address)\n\n\tif caFile := os.Getenv(\"MONGO_GO_DRIVER_CA_FILE\"); len(caFile) > 0 {\n\t\tpem, err := ioutil.ReadFile(caFile)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tca := x509.NewCertPool()\n\t\tif !ca.AppendCertsFromPEM(pem) {\n\t\t\treturn nil, errors.New(\"unable to load CA file\")\n\t\t}\n\n\t\tconfig := &tls.Config{\n\t\t\tInsecureSkipVerify: true,\n\t\t\tRootCAs:            ca,\n\t\t}\n\t\tc = tls.Client(c, config)\n\t}\n\treturn &timeoutConn{c, d.errors}, e\n}\n\n// TestServerHeartbeatTimeout tests timeout retry and preemptive canceling.\nfunc TestServerHeartbeatTimeout(t *testing.T) {\n\tif testing.Short() {\n\t\tt.Skip(\"skipping integration test in short mode\")\n\t}\n\tif os.Getenv(\"DOCKER_RUNNING\") != \"\" {\n\t\tt.Skip(\"Skipping this test in docker.\")\n\t}\n\n\tnetworkTimeoutError := &net.DNSError{\n\t\tIsTimeout: true,\n\t}\n\n\ttestCases := []struct {\n\t\tdesc                string\n\t\tioErrors            []error\n\t\texpectInterruptions int\n\t}{\n\t\t{\n\t\t\tdesc:                \"one single timeout should not clear the pool\",\n\t\t\tioErrors:            []error{nil, networkTimeoutError, nil, networkTimeoutError, nil},\n\t\t\texpectInterruptions: 0,\n\t\t},\n\t\t{\n\t\t\tdesc:                \"continuous timeouts should clear the pool with interruption\",\n\t\t\tioErrors:            []error{nil, networkTimeoutError, networkTimeoutError, nil},\n\t\t\texpectInterruptions: 1,\n\t\t},\n\t}\n\tfor _, tc := range testCases {\n\t\ttc := tc\n\t\tt.Run(tc.desc, func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tvar wg sync.WaitGroup\n\t\t\twg.Add(1)\n\n\t\t\terrors := &errorQueue{errors: tc.ioErrors}\n\t\t\ttpm := eventtest.NewTestPoolMonitor()\n\t\t\tserver := NewServer(\n\t\t\t\taddress.Address(\"localhost:27017\"),\n\t\t\t\tbson.NewObjectID(),\n\t\t\t\tdefaultConnectionTimeout,\n\t\t\t\tWithConnectionPoolMonitor(func(*event.PoolMonitor) *event.PoolMonitor {\n\t\t\t\t\treturn tpm.PoolMonitor\n\t\t\t\t}),\n\t\t\t\tWithConnectionOptions(func(opts ...ConnectionOption) []ConnectionOption {\n\t\t\t\t\treturn append(opts,\n\t\t\t\t\t\tWithDialer(func(Dialer) Dialer {\n\t\t\t\t\t\t\tvar dialer net.Dialer\n\t\t\t\t\t\t\treturn &timeoutDialer{&dialer, errors}\n\t\t\t\t\t\t}))\n\t\t\t\t}),\n\t\t\t\tWithServerMonitor(func(*event.ServerMonitor) *event.ServerMonitor {\n\t\t\t\t\treturn &event.ServerMonitor{\n\t\t\t\t\t\tServerHeartbeatSucceeded: func(*event.ServerHeartbeatSucceededEvent) {\n\t\t\t\t\t\t\tif !errors.dequeue() {\n\t\t\t\t\t\t\t\twg.Done()\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t},\n\t\t\t\t\t\tServerHeartbeatFailed: func(*event.ServerHeartbeatFailedEvent) {\n\t\t\t\t\t\t\tif !errors.dequeue() {\n\t\t\t\t\t\t\t\twg.Done()\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t},\n\t\t\t\t\t}\n\t\t\t\t}),\n\t\t\t\tWithHeartbeatInterval(func(time.Duration) time.Duration {\n\t\t\t\t\treturn 200 * time.Millisecond\n\t\t\t\t}),\n\t\t\t)\n\t\t\trequire.NoError(t, server.Connect(nil))\n\t\t\twg.Wait()\n\t\t\tinterruptions := tpm.Interruptions()\n\t\t\tassert.Equal(t, tc.expectInterruptions, interruptions, \"expected %d interruption but got %d\", tc.expectInterruptions, interruptions)\n\t\t})\n\t}\n}\n\n// TestServerConnectionTimeout tests how different timeout errors are handled during connection\n// creation and server handshake.\nfunc TestServerConnectionTimeout(t *testing.T) {\n\ttestCases := []struct {\n\t\tdesc              string\n\t\tdialer            func(Dialer) Dialer\n\t\thandshaker        func(Handshaker) Handshaker\n\t\toperationTimeout  time.Duration\n\t\tconnectTimeout    time.Duration\n\t\texpectErr         bool\n\t\texpectPoolCleared bool\n\t}{\n\t\t{\n\t\t\tdesc:              \"successful connection should not clear the pool\",\n\t\t\texpectErr:         false,\n\t\t\texpectPoolCleared: false,\n\t\t\tconnectTimeout:    defaultConnectionTimeout,\n\t\t},\n\t\t{\n\t\t\tdesc: \"timeout error during dialing should not clear the pool due to backpressure\",\n\t\t\tdialer: func(Dialer) Dialer {\n\t\t\t\tvar d net.Dialer\n\t\t\t\treturn DialerFunc(func(ctx context.Context, network, addr string) (net.Conn, error) {\n\t\t\t\t\t// Wait for the passed in context to time out. Expect the error returned by\n\t\t\t\t\t// DialContext() to be treated as a timeout caused by reaching connectTimeoutMS.\n\t\t\t\t\t<-ctx.Done()\n\t\t\t\t\treturn d.DialContext(ctx, network, addr)\n\t\t\t\t})\n\t\t\t},\n\t\t\toperationTimeout:  1 * time.Minute,\n\t\t\tconnectTimeout:    100 * time.Millisecond,\n\t\t\texpectErr:         true,\n\t\t\texpectPoolCleared: false,\n\t\t},\n\t\t{\n\t\t\tdesc: \"timeout error during dialing with no operation timeout should not clear the pool due to the backpressure\",\n\t\t\tdialer: func(Dialer) Dialer {\n\t\t\t\tvar d net.Dialer\n\t\t\t\treturn DialerFunc(func(ctx context.Context, network, addr string) (net.Conn, error) {\n\t\t\t\t\t// Wait for the passed in context to time out. Expect the error returned by\n\t\t\t\t\t// DialContext() to be treated as a timeout caused by reaching connectTimeoutMS.\n\t\t\t\t\t<-ctx.Done()\n\t\t\t\t\treturn d.DialContext(ctx, network, addr)\n\t\t\t\t})\n\t\t\t},\n\t\t\toperationTimeout:  0, // Uses a context.Background() with no timeout.\n\t\t\tconnectTimeout:    100 * time.Millisecond,\n\t\t\texpectErr:         true,\n\t\t\texpectPoolCleared: false,\n\t\t},\n\t\t{\n\t\t\tdesc: \"dial errors unrelated to context timeouts should clear the pool\",\n\t\t\tdialer: func(Dialer) Dialer {\n\t\t\t\tvar d net.Dialer\n\t\t\t\treturn DialerFunc(func(ctx context.Context, _, _ string) (net.Conn, error) {\n\t\t\t\t\t// Try to dial an invalid TCP address and expect an error.\n\t\t\t\t\treturn d.DialContext(ctx, \"tcp\", \"300.0.0.0:nope\")\n\t\t\t\t})\n\t\t\t},\n\t\t\texpectErr:         true,\n\t\t\texpectPoolCleared: true,\n\t\t\tconnectTimeout:    defaultConnectionTimeout,\n\t\t},\n\t\t{\n\t\t\tdesc: \"operation context timeout with unrelated dial errors should clear the pool\",\n\t\t\tdialer: func(Dialer) Dialer {\n\t\t\t\tvar d net.Dialer\n\t\t\t\treturn DialerFunc(func(ctx context.Context, _, _ string) (net.Conn, error) {\n\t\t\t\t\t// Try to dial an invalid TCP address and expect an error.\n\t\t\t\t\tc, err := d.DialContext(ctx, \"tcp\", \"300.0.0.0:nope\")\n\t\t\t\t\t// Wait for the passed in context to time out. Expect that the context error is\n\t\t\t\t\t// ignored because the dial error is not a timeout.\n\t\t\t\t\t<-ctx.Done()\n\t\t\t\t\treturn c, err\n\t\t\t\t})\n\t\t\t},\n\t\t\toperationTimeout:  1 * time.Millisecond,\n\t\t\tconnectTimeout:    100 * time.Millisecond,\n\t\t\texpectErr:         true,\n\t\t\texpectPoolCleared: true,\n\t\t},\n\t}\n\n\tfor _, tc := range testCases {\n\t\ttc := tc\n\t\tt.Run(tc.desc, func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\t// Create a TCP listener on a random port. The listener will accept connections but not\n\t\t\t// read or write to them.\n\t\t\tl, err := net.Listen(\"tcp\", \"127.0.0.1:0\")\n\t\t\trequire.NoError(t, err)\n\t\t\tdefer func() {\n\t\t\t\t_ = l.Close()\n\t\t\t}()\n\n\t\t\ttpm := eventtest.NewTestPoolMonitor()\n\t\t\tserver := NewServer(\n\t\t\t\taddress.Address(l.Addr().String()),\n\t\t\t\tbson.NewObjectID(),\n\t\t\t\ttc.connectTimeout,\n\t\t\t\tWithConnectionPoolMonitor(func(*event.PoolMonitor) *event.PoolMonitor {\n\t\t\t\t\treturn tpm.PoolMonitor\n\t\t\t\t}),\n\t\t\t\t// Replace the default dialer and handshaker with the test dialer and handshaker, if\n\t\t\t\t// present.\n\t\t\t\tWithConnectionOptions(func(opts ...ConnectionOption) []ConnectionOption {\n\t\t\t\t\tif tc.dialer != nil {\n\t\t\t\t\t\topts = append(opts, WithDialer(tc.dialer))\n\t\t\t\t\t}\n\t\t\t\t\tif tc.handshaker != nil {\n\t\t\t\t\t\topts = append(opts, WithHandshaker(tc.handshaker))\n\t\t\t\t\t}\n\t\t\t\t\treturn opts\n\t\t\t\t}),\n\t\t\t\t// Disable monitoring to prevent unrelated failures from the RTT monitor and\n\t\t\t\t// heartbeats from unexpectedly clearing the connection pool.\n\t\t\t\twithMonitoringDisabled(func(bool) bool { return true }),\n\t\t\t)\n\t\t\trequire.NoError(t, server.Connect(nil))\n\n\t\t\t// Create a context with the operation timeout if one is specified in the test case.\n\t\t\tctx := context.Background()\n\t\t\tif tc.operationTimeout > 0 {\n\t\t\t\tvar cancel context.CancelFunc\n\t\t\t\tctx, cancel = context.WithTimeout(ctx, tc.operationTimeout)\n\t\t\t\tdefer cancel()\n\t\t\t}\n\t\t\t_, err = server.Connection(ctx)\n\t\t\tif tc.expectErr {\n\t\t\t\tassert.NotNil(t, err, \"expected an error but got nil\")\n\t\t\t} else {\n\t\t\t\tassert.Nil(t, err, \"expected no error but got %s\", err)\n\t\t\t}\n\n\t\t\t// If we expect the pool to be cleared, watch for all events until we get a\n\t\t\t// \"ConnectionPoolCleared\" event or until we hit a 10s time limit.\n\t\t\tif tc.expectPoolCleared {\n\t\t\t\tassert.Eventually(t,\n\t\t\t\t\ttpm.IsPoolCleared,\n\t\t\t\t\t10*time.Second,\n\t\t\t\t\t100*time.Millisecond,\n\t\t\t\t\t\"expected pool to be cleared within 10s but was not cleared\")\n\t\t\t}\n\n\t\t\t// Disconnect the server then close the events channel and expect that no more events\n\t\t\t// are sent on the channel.\n\t\t\t_ = server.Disconnect(context.Background())\n\n\t\t\t// If we don't expect the pool to be cleared, check all events after the server is\n\t\t\t// disconnected and make sure none were \"ConnectionPoolCleared\".\n\t\t\tif !tc.expectPoolCleared {\n\t\t\t\tassert.False(t, tpm.IsPoolCleared(), \"expected pool to not be cleared but was cleared\")\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestServer(t *testing.T) {\n\tserverTestTable := []struct {\n\t\tname            string\n\t\tconnectionError bool\n\t\tnetworkError    bool\n\t}{\n\t\t{\"auth_error\", true, false},\n\t\t{\"no_error\", false, false},\n\t\t{\"network_error_no_desc\", false, true},\n\t\t{\"network_error_desc\", false, true},\n\t}\n\n\tauthErr := ConnectionError{Wrapped: &auth.Error{}, init: true}\n\trawNetErr := &net.AddrError{}\n\tnetErr := driver.Error{\n\t\tLabels:  []string{driver.ErrSystemOverloadedError, driver.ErrRetryableError},\n\t\tWrapped: ConnectionError{Wrapped: rawNetErr, init: true, message: \"failed to connect to localhost:27017\"},\n\t}\n\tfor _, tt := range serverTestTable {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tvar returnConnectionError bool\n\t\t\ts := NewServer(\n\t\t\t\taddress.Address(\"localhost\"),\n\t\t\t\tbson.NewObjectID(),\n\t\t\t\tdefaultConnectionTimeout,\n\t\t\t\tWithConnectionOptions(func(connOpts ...ConnectionOption) []ConnectionOption {\n\t\t\t\t\treturn append(connOpts,\n\t\t\t\t\t\tWithHandshaker(func(Handshaker) Handshaker {\n\t\t\t\t\t\t\treturn &testHandshaker{\n\t\t\t\t\t\t\t\tfinishHandshake: func(context.Context, *mnet.Connection) error {\n\t\t\t\t\t\t\t\t\tvar err error\n\t\t\t\t\t\t\t\t\tif tt.connectionError && returnConnectionError {\n\t\t\t\t\t\t\t\t\t\terr = authErr.Wrapped\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\treturn err\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}),\n\t\t\t\t\t\tWithDialer(func(Dialer) Dialer {\n\t\t\t\t\t\t\treturn DialerFunc(func(context.Context, string, string) (net.Conn, error) {\n\t\t\t\t\t\t\t\tvar err error\n\t\t\t\t\t\t\t\tif tt.networkError && returnConnectionError {\n\t\t\t\t\t\t\t\t\terr = rawNetErr\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\treturn &net.TCPConn{}, err\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t}),\n\t\t\t\t\t)\n\t\t\t\t}),\n\t\t\t)\n\n\t\t\terr := s.pool.ready()\n\t\t\trequire.NoError(t, err, \"pool.ready() error\")\n\t\t\tdefer s.pool.close(context.Background())\n\n\t\t\ts.state = serverConnected\n\n\t\t\t// The internal connection pool resets the generation number once the number of connections in a generation\n\t\t\t// reaches zero, which will cause some of these tests to fail because they assert that the generation\n\t\t\t// number after a connection failure is 1. To workaround this, we call Connection() twice: once to\n\t\t\t// successfully establish a connection and once to actually do the behavior described in the test case.\n\t\t\t_, err = s.Connection(context.Background())\n\t\t\tassert.Nil(t, err, \"error getting initial connection: %v\", err)\n\t\t\treturnConnectionError = true\n\t\t\t_, err = s.Connection(context.Background())\n\n\t\t\tswitch {\n\t\t\tcase tt.connectionError && !cmp.Equal(err, authErr, cmp.Comparer(compareErrors)):\n\t\t\t\tt.Errorf(\"Expected connection error. got %v; want %v\", err, authErr)\n\t\t\tcase tt.networkError && !cmp.Equal(err, netErr, cmp.Comparer(compareErrors)):\n\t\t\t\tt.Errorf(\"Expected network error. got %v; want %v\", err, netErr)\n\t\t\tcase !tt.connectionError && !tt.networkError && err != nil:\n\t\t\t\tt.Errorf(\"Expected error to be nil. got %v; want %v\", err, \"<nil>\")\n\t\t\t}\n\n\t\t\tif tt.connectionError || tt.networkError {\n\t\t\t\tassert.Equal(t, s.Description().Kind, (description.ServerKind)(description.Unknown))\n\t\t\t}\n\n\t\t\tgeneration, _ := s.pool.generation.getGeneration(nil)\n\t\t\tswitch {\n\t\t\tcase tt.connectionError:\n\t\t\t\tassert.Equal(t, uint64(1), generation, \"Expected pool to be drained once on connection error. got %d; want %d\", generation, 1)\n\t\t\tcase tt.networkError:\n\t\t\t\tassert.NoError(t, s.Description().LastError, \"Expected no LastError\")\n\t\t\t\tassert.Equal(t, uint64(0), generation, \"Expected pool to not be drained on network error. got %d; want %d\", generation, 1)\n\t\t\t}\n\t\t})\n\t}\n\n\tt.Run(\"multiple connection initialization errors are processed correctly\", func(t *testing.T) {\n\t\tassertGenerationStats := func(t *testing.T, server *Server, serviceID bson.ObjectID, wantGeneration, wantNumConns uint64) {\n\t\t\tt.Helper()\n\n\t\t\tgetGeneration := func(serviceIDPtr *bson.ObjectID) uint64 {\n\t\t\t\tgeneration, _ := server.pool.generation.getGeneration(serviceIDPtr)\n\t\t\t\treturn generation\n\t\t\t}\n\n\t\t\t// On connection failure, the connection is removed and closed after delivering the\n\t\t\t// error to Connection(), so it may still count toward the generation connection count\n\t\t\t// briefly. Wait up to 100ms for the generation connection count to reach the target.\n\t\t\tassert.Eventuallyf(t,\n\t\t\t\tfunc() bool {\n\t\t\t\t\tgeneration, _ := server.pool.generation.getGeneration(&serviceID)\n\t\t\t\t\tnumConns := server.pool.generation.getNumConns(&serviceID)\n\t\t\t\t\treturn generation == wantGeneration && numConns == wantNumConns\n\t\t\t\t},\n\t\t\t\t100*time.Millisecond,\n\t\t\t\t10*time.Millisecond,\n\t\t\t\t\"expected generation number %v, got %v; expected connection count %v, got %v\",\n\t\t\t\twantGeneration,\n\t\t\t\tgetGeneration(&serviceID),\n\t\t\t\twantNumConns,\n\t\t\t\tserver.pool.generation.getNumConns(&serviceID))\n\t\t}\n\n\t\ttestCases := []struct {\n\t\t\tname               string\n\t\t\tloadBalanced       bool\n\t\t\tdialErr            error\n\t\t\tgetInfoErr         error\n\t\t\tfinishHandshakeErr error\n\t\t\tfinalGeneration    uint64\n\t\t\tnumNewConns        uint64\n\t\t}{\n\t\t\t// For LB clusters, errors for dialing and the initial handshake are ignored.\n\t\t\t{\"dial errors are ignored for load balancers\", true, rawNetErr, nil, nil, 0, 1},\n\t\t\t{\"initial handshake errors are ignored for load balancers\", true, nil, rawNetErr, nil, 0, 1},\n\n\t\t\t// For LB clusters, post-handshake errors clear the pool, but do not update the server\n\t\t\t// description or pause the pool.\n\t\t\t{\"post-handshake errors are not ignored for load balancers\", true, nil, nil, rawNetErr, 5, 1},\n\n\t\t\t// For non-LB clusters, dial and initial handshake errors do not clear the pool or update\n\t\t\t// the server description.\n\t\t\t{\"dial errors are ignored for non-lb clusters\", false, rawNetErr, nil, nil, 0, 1},\n\t\t\t{\"initial handshake errors are ignored for non-lb clusters\", false, nil, rawNetErr, nil, 0, 1},\n\t\t\t{\"post-handshake errors are not ignored for non-lb clusters\", false, nil, nil, rawNetErr, 1, 1},\n\t\t}\n\t\tfor _, tc := range testCases {\n\t\t\ttc := tc // Capture range variable.\n\n\t\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\t\tvar returnConnectionError bool\n\t\t\t\tvar serviceID bson.ObjectID\n\t\t\t\tif tc.loadBalanced {\n\t\t\t\t\tserviceID = bson.NewObjectID()\n\t\t\t\t}\n\n\t\t\t\thandshaker := &testHandshaker{\n\t\t\t\t\tgetHandshakeInformation: func(_ context.Context, addr address.Address, _ *mnet.Connection) (driver.HandshakeInformation, error) {\n\t\t\t\t\t\tif tc.getInfoErr != nil && returnConnectionError {\n\t\t\t\t\t\t\treturn driver.HandshakeInformation{}, tc.getInfoErr\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tdesc := newServerDescriptionFromError(addr, nil, nil)\n\t\t\t\t\t\tif tc.loadBalanced {\n\t\t\t\t\t\t\tdesc.ServiceID = &serviceID\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn driver.HandshakeInformation{Description: desc}, nil\n\t\t\t\t\t},\n\t\t\t\t\tfinishHandshake: func(context.Context, *mnet.Connection) error {\n\t\t\t\t\t\tif tc.finishHandshakeErr != nil && returnConnectionError {\n\t\t\t\t\t\t\treturn tc.finishHandshakeErr\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn nil\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t\tconnOpts := []ConnectionOption{\n\t\t\t\t\tWithDialer(func(Dialer) Dialer {\n\t\t\t\t\t\treturn DialerFunc(func(context.Context, string, string) (net.Conn, error) {\n\t\t\t\t\t\t\tvar err error\n\t\t\t\t\t\t\tif returnConnectionError && tc.dialErr != nil {\n\t\t\t\t\t\t\t\terr = tc.dialErr\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treturn &net.TCPConn{}, err\n\t\t\t\t\t\t})\n\t\t\t\t\t}),\n\t\t\t\t\tWithHandshaker(func(Handshaker) Handshaker {\n\t\t\t\t\t\treturn handshaker\n\t\t\t\t\t}),\n\t\t\t\t\tWithConnectionLoadBalanced(func(bool) bool {\n\t\t\t\t\t\treturn tc.loadBalanced\n\t\t\t\t\t}),\n\t\t\t\t}\n\t\t\t\tserverOpts := []ServerOption{\n\t\t\t\t\tWithServerLoadBalanced(func(bool) bool {\n\t\t\t\t\t\treturn tc.loadBalanced\n\t\t\t\t\t}),\n\t\t\t\t\tWithConnectionOptions(func(...ConnectionOption) []ConnectionOption {\n\t\t\t\t\t\treturn connOpts\n\t\t\t\t\t}),\n\t\t\t\t\t// Disable the monitoring routine because we're only testing pooled connections and we don't want\n\t\t\t\t\t// errors in monitoring to clear the pool and make this test flaky.\n\t\t\t\t\twithMonitoringDisabled(func(bool) bool {\n\t\t\t\t\t\treturn true\n\t\t\t\t\t}),\n\t\t\t\t\t// With the default maxConnecting (2), there are multiple goroutines creating\n\t\t\t\t\t// connections. Because handshake errors are processed after returning the error\n\t\t\t\t\t// to checkOut(), it's possible for extra connection requests to be processed\n\t\t\t\t\t// after a handshake error before the connection pool is cleared and paused. Set\n\t\t\t\t\t// maxConnecting=1 to minimize the number of extra connection requests processed\n\t\t\t\t\t// before the connection pool is cleared and paused.\n\t\t\t\t\tWithMaxConnecting(func(uint64) uint64 { return 1 }),\n\t\t\t\t}\n\n\t\t\t\tserver, err := ConnectServer(\n\t\t\t\t\taddress.Address(\"localhost:27017\"),\n\t\t\t\t\tnil,\n\t\t\t\t\tbson.NewObjectID(),\n\t\t\t\t\tdefaultConnectionTimeout,\n\t\t\t\t\tserverOpts...,\n\t\t\t\t)\n\t\t\t\tassert.Nil(t, err, \"ConnectServer error: %v\", err)\n\t\t\t\tdefer func() {\n\t\t\t\t\t_ = server.Disconnect(context.Background())\n\t\t\t\t}()\n\n\t\t\t\t_, err = server.Connection(context.Background())\n\t\t\t\tassert.Nil(t, err, \"Connection error: %v\", err)\n\t\t\t\tassertGenerationStats(t, server, serviceID, 0, 1)\n\n\t\t\t\treturnConnectionError = true\n\t\t\t\tfor i := 0; i < 5; i++ {\n\t\t\t\t\t_, err = server.Connection(context.Background())\n\t\t\t\t\tswitch {\n\t\t\t\t\tcase tc.dialErr != nil || tc.getInfoErr != nil || tc.finishHandshakeErr != nil:\n\t\t\t\t\t\tassert.NotNil(t, err, \"expected Connection error at iteration %d, got nil\", i)\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tassert.Nil(t, err, \"Connection error at iteration %d: %v\", i, err)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tassertGenerationStats(t, server, serviceID, tc.finalGeneration, tc.numNewConns)\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"Cannot starve connection request\", func(t *testing.T) {\n\t\tcleanup := make(chan struct{})\n\t\taddr := bootstrapConnections(t, 3, func(nc net.Conn) {\n\t\t\t<-cleanup\n\t\t\t_ = nc.Close()\n\t\t})\n\t\td := newdialer(&net.Dialer{})\n\t\ts := NewServer(address.Address(addr.String()),\n\t\t\tbson.NewObjectID(),\n\t\t\tdefaultConnectionTimeout,\n\t\t\tWithConnectionOptions(func(...ConnectionOption) []ConnectionOption {\n\t\t\t\treturn []ConnectionOption{WithDialer(func(_ Dialer) Dialer { return d })}\n\t\t\t}),\n\t\t\tWithMaxConnections(func(uint64) uint64 {\n\t\t\t\treturn 1\n\t\t\t}))\n\t\ts.state = serverConnected\n\t\terr := s.pool.ready()\n\t\trequire.NoError(t, err)\n\t\tdefer s.pool.close(context.Background())\n\n\t\tconn, err := s.Connection(context.Background())\n\t\trequire.NoError(t, err)\n\t\tif d.lenopened() != 1 {\n\t\t\tt.Errorf(\"Should have opened 1 connections, but didn't. got %d; want %d\", d.lenopened(), 1)\n\t\t}\n\n\t\tvar wg sync.WaitGroup\n\n\t\twg.Add(1)\n\t\tch := make(chan struct{})\n\t\tgo func() {\n\t\t\tctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)\n\t\t\tdefer cancel()\n\t\t\tch <- struct{}{}\n\t\t\t_, err := s.Connection(ctx)\n\t\t\tif err != nil {\n\t\t\t\tt.Errorf(\"Should not be able to starve connection request, but got error: %v\", err)\n\t\t\t}\n\t\t\twg.Done()\n\t\t}()\n\t\t<-ch\n\t\truntime.Gosched()\n\t\terr = conn.Close()\n\t\trequire.NoError(t, err)\n\t\twg.Wait()\n\t\tclose(cleanup)\n\t})\n\n\tt.Run(\"update topology\", func(t *testing.T) {\n\t\tvar updated atomic.Value // bool\n\t\tupdated.Store(false)\n\n\t\tupdateCallback := func(desc description.Server) description.Server {\n\t\t\tupdated.Store(true)\n\t\t\treturn desc\n\t\t}\n\n\t\ts, err := ConnectServer(\n\t\t\taddress.Address(\"localhost\"),\n\t\t\tupdateCallback,\n\t\t\tbson.NewObjectID(),\n\t\t\tdefaultConnectionTimeout,\n\t\t)\n\n\t\trequire.NoError(t, err)\n\t\ts.updateDescription(description.Server{Addr: s.address})\n\t\trequire.True(t, updated.Load().(bool))\n\t})\n\tt.Run(\"heartbeat\", func(t *testing.T) {\n\t\t// test that client metadata is sent on handshakes but not heartbeats\n\t\tdialer := &channelNetConnDialer{}\n\t\tdialerOpt := WithDialer(func(Dialer) Dialer {\n\t\t\treturn dialer\n\t\t})\n\t\tserverOpt := WithConnectionOptions(func(connOpts ...ConnectionOption) []ConnectionOption {\n\t\t\treturn append(connOpts, dialerOpt)\n\t\t})\n\n\t\ts := NewServer(address.Address(\"localhost:27017\"), bson.NewObjectID(), defaultConnectionTimeout, serverOpt)\n\n\t\t// do a heartbeat with a nil connection so a new one will be dialed\n\t\t_, err := s.check(context.Background())\n\t\tassert.Nil(t, err, \"check error: %v\", err)\n\t\tassert.NotNil(t, s.conn, \"no connection dialed in check\")\n\n\t\tchannelConn := s.conn.nc.(*drivertest.ChannelNetConn)\n\t\twm := channelConn.GetWrittenMessage()\n\t\tif wm == nil {\n\t\t\tt.Fatal(\"no wire message written for handshake\")\n\t\t}\n\t\tif !includesClientMetadata(t, wm) {\n\t\t\tt.Fatal(\"client metadata expected in handshake but not found\")\n\t\t}\n\n\t\t// do a heartbeat with a non-nil connection\n\t\tif err = channelConn.AddResponse(makeHelloReply()); err != nil {\n\t\t\tt.Fatalf(\"error adding response: %v\", err)\n\t\t}\n\t\t_, err = s.check(context.Background())\n\t\tassert.Nil(t, err, \"check error: %v\", err)\n\n\t\twm = channelConn.GetWrittenMessage()\n\t\tif wm == nil {\n\t\t\tt.Fatal(\"no wire message written for heartbeat\")\n\t\t}\n\t\tif includesClientMetadata(t, wm) {\n\t\t\tt.Fatal(\"client metadata not expected in heartbeat but found\")\n\t\t}\n\t})\n\tt.Run(\"heartbeat monitoring\", func(t *testing.T) {\n\t\tvar publishedEvents []any\n\n\t\tserverHeartbeatStarted := func(e *event.ServerHeartbeatStartedEvent) {\n\t\t\tpublishedEvents = append(publishedEvents, *e)\n\t\t}\n\n\t\tserverHeartbeatSucceeded := func(e *event.ServerHeartbeatSucceededEvent) {\n\t\t\tpublishedEvents = append(publishedEvents, *e)\n\t\t}\n\n\t\tserverHeartbeatFailed := func(e *event.ServerHeartbeatFailedEvent) {\n\t\t\tpublishedEvents = append(publishedEvents, *e)\n\t\t}\n\n\t\tsdam := &event.ServerMonitor{\n\t\t\tServerHeartbeatStarted:   serverHeartbeatStarted,\n\t\t\tServerHeartbeatSucceeded: serverHeartbeatSucceeded,\n\t\t\tServerHeartbeatFailed:    serverHeartbeatFailed,\n\t\t}\n\n\t\tdialer := &channelNetConnDialer{}\n\t\tdialerOpt := WithDialer(func(Dialer) Dialer {\n\t\t\treturn dialer\n\t\t})\n\t\tserverOpts := []ServerOption{\n\t\t\tWithConnectionOptions(func(connOpts ...ConnectionOption) []ConnectionOption {\n\t\t\t\treturn append(connOpts, dialerOpt)\n\t\t\t}),\n\t\t\twithMonitoringDisabled(func(bool) bool { return true }),\n\t\t\tWithServerMonitor(func(*event.ServerMonitor) *event.ServerMonitor { return sdam }),\n\t\t}\n\n\t\ts := NewServer(address.Address(\"localhost:27017\"), bson.NewObjectID(), defaultConnectionTimeout, serverOpts...)\n\n\t\t// set up heartbeat connection, which doesn't send events\n\t\t_, err := s.check(context.Background())\n\t\tassert.Nil(t, err, \"check error: %v\", err)\n\n\t\tchannelConn := s.conn.nc.(*drivertest.ChannelNetConn)\n\t\t_ = channelConn.GetWrittenMessage()\n\n\t\tt.Run(\"success\", func(t *testing.T) {\n\t\t\tpublishedEvents = nil\n\t\t\t// do a heartbeat with a non-nil connection\n\t\t\tif err = channelConn.AddResponse(makeHelloReply()); err != nil {\n\t\t\t\tt.Fatalf(\"error adding response: %v\", err)\n\t\t\t}\n\t\t\t_, err = s.check(context.Background())\n\t\t\t_ = channelConn.GetWrittenMessage()\n\t\t\tassert.Nil(t, err, \"check error: %v\", err)\n\n\t\t\tassert.Equal(t, len(publishedEvents), 2, \"expected %v events, got %v\", 2, len(publishedEvents))\n\n\t\t\tstarted, ok := publishedEvents[0].(event.ServerHeartbeatStartedEvent)\n\t\t\tassert.True(t, ok, \"expected type %T, got %T\", event.ServerHeartbeatStartedEvent{}, publishedEvents[0])\n\t\t\tassert.Equal(t, started.ConnectionID, s.conn.ID(), \"expected connectionID to match\")\n\t\t\tassert.False(t, started.Awaited, \"expected awaited to be false\")\n\n\t\t\tsucceeded, ok := publishedEvents[1].(event.ServerHeartbeatSucceededEvent)\n\t\t\tassert.True(t, ok, \"expected type %T, got %T\", event.ServerHeartbeatSucceededEvent{}, publishedEvents[1])\n\t\t\tassert.Equal(t, succeeded.ConnectionID, s.conn.ID(), \"expected connectionID to match\")\n\t\t\tassert.Equal(t, succeeded.Reply.Addr, s.address, \"expected address %v, got %v\", s.address, succeeded.Reply.Addr)\n\t\t\tassert.False(t, succeeded.Awaited, \"expected awaited to be false\")\n\t\t})\n\t\tt.Run(\"failure\", func(t *testing.T) {\n\t\t\tpublishedEvents = nil\n\t\t\t// do a heartbeat with a non-nil connection\n\t\t\treadErr := errors.New(\"error\")\n\t\t\tchannelConn.ReadErr <- readErr\n\t\t\t_, err = s.check(context.Background())\n\t\t\t_ = channelConn.GetWrittenMessage()\n\t\t\tassert.Nil(t, err, \"check error: %v\", err)\n\n\t\t\tassert.Equal(t, len(publishedEvents), 2, \"expected %v events, got %v\", 2, len(publishedEvents))\n\n\t\t\tstarted, ok := publishedEvents[0].(event.ServerHeartbeatStartedEvent)\n\t\t\tassert.True(t, ok, \"expected type %T, got %T\", event.ServerHeartbeatStartedEvent{}, publishedEvents[0])\n\t\t\tassert.Equal(t, started.ConnectionID, s.conn.ID(), \"expected connectionID to match\")\n\t\t\tassert.False(t, started.Awaited, \"expected awaited to be false\")\n\n\t\t\tfailed, ok := publishedEvents[1].(event.ServerHeartbeatFailedEvent)\n\t\t\tassert.True(t, ok, \"expected type %T, got %T\", event.ServerHeartbeatFailedEvent{}, publishedEvents[1])\n\t\t\tassert.Equal(t, failed.ConnectionID, s.conn.ID(), \"expected connectionID to match\")\n\t\t\tassert.False(t, failed.Awaited, \"expected awaited to be false\")\n\t\t\tassert.True(t, errors.Is(failed.Failure, readErr), \"expected Failure to be %v, got: %v\", readErr, failed.Failure)\n\t\t})\n\t})\n\tt.Run(\"WithServerAppName\", func(t *testing.T) {\n\t\tname := \"test\"\n\n\t\ts := NewServer(address.Address(\"localhost\"),\n\t\t\tbson.NewObjectID(),\n\t\t\tdefaultConnectionTimeout,\n\t\t\tWithServerAppName(func(string) string { return name }))\n\t\trequire.Equal(t, name, s.cfg.appname, \"expected appname to be: %v, got: %v\", name, s.cfg.appname)\n\t})\n}\n\nfunc TestServer_ProcessError(t *testing.T) {\n\tt.Parallel()\n\n\tprocessID := bson.NewObjectID()\n\tnewProcessID := bson.NewObjectID()\n\n\ttestCases := []struct {\n\t\tname string\n\n\t\tstartDescription description.Server // Initial server description at the start of the test.\n\n\t\tinputErr  error            // ProcessError error input.\n\t\tinputConn *mnet.Connection // ProcessError conn input.\n\n\t\twant            driver.ProcessErrorResult // Expected ProcessError return value.\n\t\twantGeneration  uint64                    // Expected resulting connection pool generation.\n\t\twantDescription description.Server        // Expected resulting server description.\n\t}{\n\t\t// Test that a nil error does not change the Server state.\n\t\t{\n\t\t\tname: \"nil error\",\n\t\t\tstartDescription: description.Server{\n\t\t\t\tKind: description.ServerKindRSPrimary,\n\t\t\t},\n\t\t\tinputErr:       nil,\n\t\t\twant:           driver.NoChange,\n\t\t\twantGeneration: 0,\n\t\t\twantDescription: description.Server{\n\t\t\t\tKind: description.ServerKindRSPrimary,\n\t\t\t},\n\t\t},\n\t\t// Test that errors that occur on stale connections are ignored.\n\t\t{\n\t\t\tname: \"stale connection\",\n\t\t\tstartDescription: description.Server{\n\t\t\t\tKind: description.ServerKindRSPrimary,\n\t\t\t},\n\t\t\tinputErr:       errors.New(\"foo\"),\n\t\t\tinputConn:      newProcessErrorTestConn(&description.VersionRange{Max: 17}, true),\n\t\t\twant:           driver.NoChange,\n\t\t\twantGeneration: 0,\n\t\t\twantDescription: description.Server{\n\t\t\t\tKind: description.ServerKindRSPrimary,\n\t\t\t},\n\t\t},\n\t\t// Test that errors that do not indicate a database state change or connection error are\n\t\t// ignored.\n\t\t{\n\t\t\tname: \"non state change error\",\n\t\t\tstartDescription: description.Server{\n\t\t\t\tKind: description.ServerKindRSPrimary,\n\t\t\t},\n\t\t\tinputErr: driver.Error{\n\t\t\t\tCode: 1,\n\t\t\t},\n\t\t\tinputConn:      newProcessErrorTestConn(&description.VersionRange{Max: 17}, false),\n\t\t\twant:           driver.NoChange,\n\t\t\twantGeneration: 0,\n\t\t\twantDescription: description.Server{\n\t\t\t\tKind: description.ServerKindRSPrimary,\n\t\t\t},\n\t\t},\n\t\t// Test that a \"not writable primary\" error with an old topology version is ignored.\n\t\t{\n\t\t\tname:             \"stale not writable primary error\",\n\t\t\tstartDescription: newServerDescription(description.ServerKindRSPrimary, processID, 1, nil),\n\t\t\tinputErr: driver.Error{\n\t\t\t\tCode: 10107, // NotWritablePrimary\n\t\t\t\tTopologyVersion: &description.TopologyVersion{\n\t\t\t\t\tProcessID: processID,\n\t\t\t\t\tCounter:   0,\n\t\t\t\t},\n\t\t\t},\n\t\t\tinputConn:       newProcessErrorTestConn(&description.VersionRange{Max: 17}, false),\n\t\t\twant:            driver.NoChange,\n\t\t\twantGeneration:  0,\n\t\t\twantDescription: newServerDescription(description.ServerKindRSPrimary, processID, 1, nil),\n\t\t},\n\t\t// Test that a \"not writable primary\" error with an newer topology version marks the Server\n\t\t// as \"unknown\" and updates its topology version.\n\t\t{\n\t\t\tname:             \"new not writable primary error\",\n\t\t\tstartDescription: newServerDescription(description.ServerKindRSPrimary, processID, 0, nil),\n\t\t\tinputErr: driver.Error{\n\t\t\t\tCode: 10107, // NotWritablePrimary\n\t\t\t\tTopologyVersion: &description.TopologyVersion{\n\t\t\t\t\tProcessID: processID,\n\t\t\t\t\tCounter:   1,\n\t\t\t\t},\n\t\t\t},\n\t\t\tinputConn:      newProcessErrorTestConn(&description.VersionRange{Max: 17}, false),\n\t\t\twant:           driver.ServerMarkedUnknown,\n\t\t\twantGeneration: 0,\n\t\t\twantDescription: newServerDescription(description.Unknown, processID, 1, driver.Error{\n\t\t\t\tCode: 10107, // NotWritablePrimary\n\t\t\t\tTopologyVersion: &description.TopologyVersion{\n\t\t\t\t\tProcessID: processID,\n\t\t\t\t\tCounter:   1,\n\t\t\t\t},\n\t\t\t}),\n\t\t},\n\t\t// Test that a \"not writable primary\" error with an different topology process ID marks the Server as\n\t\t// \"unknown\" and updates its topology version.\n\t\t{\n\t\t\tname:             \"new process ID not writable primary error\",\n\t\t\tstartDescription: newServerDescription(description.ServerKindRSPrimary, processID, 0, nil),\n\t\t\tinputErr: driver.Error{\n\t\t\t\tCode: 10107, // NotWritablePrimary\n\t\t\t\tTopologyVersion: &description.TopologyVersion{\n\t\t\t\t\tProcessID: newProcessID,\n\t\t\t\t\tCounter:   0,\n\t\t\t\t},\n\t\t\t},\n\t\t\tinputConn:      newProcessErrorTestConn(&description.VersionRange{Max: 17}, false),\n\t\t\twant:           driver.ServerMarkedUnknown,\n\t\t\twantGeneration: 0,\n\t\t\twantDescription: newServerDescription(description.Unknown, newProcessID, 0, driver.Error{\n\t\t\t\tCode: 10107, // NotWritablePrimary\n\t\t\t\tTopologyVersion: &description.TopologyVersion{\n\t\t\t\t\tProcessID: newProcessID,\n\t\t\t\t\tCounter:   0,\n\t\t\t\t},\n\t\t\t}),\n\t\t},\n\t\t// Test that a connection with a newer topology version overrides the server topology\n\t\t// version and causes an error with the same topology version to be ignored.\n\t\t// TODO(GODRIVER-2841): Remove this test case.\n\t\t{\n\t\t\tname:             \"newer connection topology version\",\n\t\t\tstartDescription: newServerDescription(description.ServerKindRSPrimary, processID, 0, nil),\n\t\t\tinputErr: driver.Error{\n\t\t\t\tCode: 10107, // NotWritablePrimary\n\t\t\t\tTopologyVersion: &description.TopologyVersion{\n\t\t\t\t\tProcessID: processID,\n\t\t\t\t\tCounter:   1,\n\t\t\t\t},\n\t\t\t},\n\t\t\tinputConn: mnet.NewConnection(&processErrorTestConn{\n\t\t\t\tdescription: description.Server{\n\t\t\t\t\tWireVersion: &description.VersionRange{Max: 17},\n\t\t\t\t\tTopologyVersion: &description.TopologyVersion{\n\t\t\t\t\t\tProcessID: processID,\n\t\t\t\t\t\tCounter:   1,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tstale: false,\n\t\t\t}),\n\t\t\twant:            driver.NoChange,\n\t\t\twantGeneration:  0,\n\t\t\twantDescription: newServerDescription(description.ServerKindRSPrimary, processID, 0, nil),\n\t\t},\n\t\t// Test that a \"node is shutting down\" error with a newer topology version clears the\n\t\t// connection pool, marks the Server as \"unknown\", and updates its topology version.\n\t\t{\n\t\t\tname:             \"new shutdown error\",\n\t\t\tstartDescription: newServerDescription(description.ServerKindRSPrimary, processID, 0, nil),\n\t\t\tinputErr: driver.Error{\n\t\t\t\tCode: 11600, // InterruptedAtShutdown\n\t\t\t\tTopologyVersion: &description.TopologyVersion{\n\t\t\t\t\tProcessID: processID,\n\t\t\t\t\tCounter:   1,\n\t\t\t\t},\n\t\t\t},\n\t\t\tinputConn:      newProcessErrorTestConn(&description.VersionRange{Max: 17}, false),\n\t\t\twant:           driver.ConnectionPoolCleared,\n\t\t\twantGeneration: 1,\n\t\t\twantDescription: newServerDescription(description.Unknown, processID, 1, driver.Error{\n\t\t\t\tCode: 11600, // InterruptedAtShutdown\n\t\t\t\tTopologyVersion: &description.TopologyVersion{\n\t\t\t\t\tProcessID: processID,\n\t\t\t\t\tCounter:   1,\n\t\t\t\t},\n\t\t\t}),\n\t\t},\n\t\t// Test that a \"not writable primary\" error with a stale topology version is ignored.\n\t\t{\n\t\t\tname:             \"stale not writable primary write concern error\",\n\t\t\tstartDescription: newServerDescription(description.ServerKindRSPrimary, processID, 1, nil),\n\t\t\tinputErr: driver.WriteCommandError{\n\t\t\t\tWriteConcernError: &driver.WriteConcernError{\n\t\t\t\t\tCode: 10107, // NotWritablePrimary\n\t\t\t\t\tTopologyVersion: &description.TopologyVersion{\n\t\t\t\t\t\tProcessID: processID,\n\t\t\t\t\t\tCounter:   0,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\tinputConn:       newProcessErrorTestConn(&description.VersionRange{Max: 17}, false),\n\t\t\twant:            driver.NoChange,\n\t\t\twantGeneration:  0,\n\t\t\twantDescription: newServerDescription(description.ServerKindRSPrimary, processID, 1, nil),\n\t\t},\n\t\t// Test that a \"not writable primary\" error with a newer topology version marks the Server\n\t\t// as \"unknown\" and updates its topology version.\n\t\t{\n\t\t\tname:             \"new not writable primary write concern error\",\n\t\t\tstartDescription: newServerDescription(description.ServerKindRSPrimary, processID, 0, nil),\n\t\t\tinputErr: driver.WriteCommandError{\n\t\t\t\tWriteConcernError: &driver.WriteConcernError{\n\t\t\t\t\tCode: 10107, // NotWritablePrimary\n\t\t\t\t\tTopologyVersion: &description.TopologyVersion{\n\t\t\t\t\t\tProcessID: processID,\n\t\t\t\t\t\tCounter:   1,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\tinputConn:      newProcessErrorTestConn(&description.VersionRange{Max: 17}, false),\n\t\t\twant:           driver.ServerMarkedUnknown,\n\t\t\twantGeneration: 0,\n\t\t\twantDescription: newServerDescription(description.Unknown, processID, 1, driver.WriteCommandError{\n\t\t\t\tWriteConcernError: &driver.WriteConcernError{\n\t\t\t\t\tCode: 10107, // NotWritablePrimary\n\t\t\t\t\tTopologyVersion: &description.TopologyVersion{\n\t\t\t\t\t\tProcessID: processID,\n\t\t\t\t\t\tCounter:   1,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t}),\n\t\t},\n\t\t// Test that \"node is shutting down\" errors that have a newer topology version than the\n\t\t// local Server topology version mark the Server as \"unknown\" and clear the connection pool.\n\t\t{\n\t\t\tname:             \"new shutdown write concern error\",\n\t\t\tstartDescription: newServerDescription(description.ServerKindRSPrimary, processID, 0, nil),\n\t\t\tinputErr: driver.WriteCommandError{\n\t\t\t\tWriteConcernError: &driver.WriteConcernError{\n\t\t\t\t\tCode: 11600, // InterruptedAtShutdown\n\t\t\t\t\tTopologyVersion: &description.TopologyVersion{\n\t\t\t\t\t\tProcessID: processID,\n\t\t\t\t\t\tCounter:   1,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\tinputConn:      newProcessErrorTestConn(&description.VersionRange{Max: 17}, false),\n\t\t\twant:           driver.ConnectionPoolCleared,\n\t\t\twantGeneration: 1,\n\t\t\twantDescription: newServerDescription(description.Unknown, processID, 1, driver.WriteCommandError{\n\t\t\t\tWriteConcernError: &driver.WriteConcernError{\n\t\t\t\t\tCode: 11600, // InterruptedAtShutdown\n\t\t\t\t\tTopologyVersion: &description.TopologyVersion{\n\t\t\t\t\t\tProcessID: processID,\n\t\t\t\t\t\tCounter:   1,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t}),\n\t\t},\n\t\t// Test that \"node is recovering\" or \"not writable primary\" errors that have a newer\n\t\t// topology version than the local Server topology version and appear to be from MongoDB\n\t\t// servers before 4.2 mark the Server as \"unknown\" and clear the connection pool.\n\t\t{\n\t\t\tname:             \"older than 4.2 write concern error\",\n\t\t\tstartDescription: newServerDescription(description.ServerKindRSPrimary, processID, 0, nil),\n\t\t\tinputErr: driver.WriteCommandError{\n\t\t\t\tWriteConcernError: &driver.WriteConcernError{\n\t\t\t\t\tCode: 10107, // NotWritablePrimary\n\t\t\t\t\tTopologyVersion: &description.TopologyVersion{\n\t\t\t\t\t\tProcessID: processID,\n\t\t\t\t\t\tCounter:   1,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\tinputConn:      newProcessErrorTestConn(&description.VersionRange{Max: 7}, false),\n\t\t\twant:           driver.ConnectionPoolCleared,\n\t\t\twantGeneration: 1,\n\t\t\twantDescription: newServerDescription(description.Unknown, processID, 1, driver.WriteCommandError{\n\t\t\t\tWriteConcernError: &driver.WriteConcernError{\n\t\t\t\t\tCode: 10107, // NotWritablePrimary\n\t\t\t\t\tTopologyVersion: &description.TopologyVersion{\n\t\t\t\t\t\tProcessID: processID,\n\t\t\t\t\t\tCounter:   1,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t}),\n\t\t},\n\t\t// Test that a network timeout error, such as a DNS lookup timeout error, is ignored.\n\t\t{\n\t\t\tname:             \"network timeout error\",\n\t\t\tstartDescription: newServerDescription(description.ServerKindRSPrimary, processID, 0, nil),\n\t\t\tinputErr: driver.Error{\n\t\t\t\tLabels: []string{driver.NetworkError},\n\t\t\t\tWrapped: ConnectionError{\n\t\t\t\t\t// Use a net.Error implementation that can return true from its Timeout() function.\n\t\t\t\t\tWrapped: &net.DNSError{\n\t\t\t\t\t\tIsTimeout: true,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\tinputConn:       newProcessErrorTestConn(&description.VersionRange{Max: 17}, false),\n\t\t\twant:            driver.NoChange,\n\t\t\twantGeneration:  0,\n\t\t\twantDescription: newServerDescription(description.ServerKindRSPrimary, processID, 0, nil),\n\t\t},\n\t\t// Test that a context canceled error is ignored.\n\t\t{\n\t\t\tname:             \"context canceled error\",\n\t\t\tstartDescription: newServerDescription(description.ServerKindRSPrimary, processID, 0, nil),\n\t\t\tinputErr: driver.Error{\n\t\t\t\tLabels: []string{driver.NetworkError},\n\t\t\t\tWrapped: ConnectionError{\n\t\t\t\t\tWrapped: context.Canceled,\n\t\t\t\t},\n\t\t\t},\n\t\t\tinputConn:       newProcessErrorTestConn(&description.VersionRange{Max: 17}, false),\n\t\t\twant:            driver.NoChange,\n\t\t\twantGeneration:  0,\n\t\t\twantDescription: newServerDescription(description.ServerKindRSPrimary, processID, 0, nil),\n\t\t},\n\t\t// Test that a non-timeout network error, such as an address lookup error, marks the server\n\t\t// as \"unknown\" and sets its topology version to nil.\n\t\t{\n\t\t\tname:             \"non-timeout network error\",\n\t\t\tstartDescription: newServerDescription(description.ServerKindRSPrimary, processID, 0, nil),\n\t\t\tinputErr: driver.Error{\n\t\t\t\tLabels: []string{driver.NetworkError},\n\t\t\t\tWrapped: ConnectionError{\n\t\t\t\t\t// Use a net.Error implementation that always returns false from its Timeout() function.\n\t\t\t\t\tWrapped: &net.AddrError{},\n\t\t\t\t},\n\t\t\t},\n\t\t\tinputConn:      newProcessErrorTestConn(&description.VersionRange{Max: 17}, false),\n\t\t\twant:           driver.ConnectionPoolCleared,\n\t\t\twantGeneration: 1,\n\t\t\twantDescription: description.Server{\n\t\t\t\tKind: description.Unknown,\n\t\t\t\tLastError: driver.Error{\n\t\t\t\t\tLabels: []string{driver.NetworkError},\n\t\t\t\t\tWrapped: ConnectionError{\n\t\t\t\t\t\tWrapped: &net.AddrError{},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n\n\tfor _, tc := range testCases {\n\t\ttc := tc // Capture range variable.\n\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tserver := NewServer(address.Address(\"\"), bson.NewObjectID(), defaultConnectionTimeout)\n\t\t\tserver.state = serverConnected\n\t\t\terr := server.pool.ready()\n\t\t\trequire.Nil(t, err, \"pool.ready() error: %v\", err)\n\n\t\t\tserver.desc.Store(tc.startDescription)\n\n\t\t\tgot := server.ProcessError(tc.inputErr, tc.inputConn)\n\t\t\tassert.Equal(t, tc.want, got, \"expected and actual ProcessError result are different\")\n\n\t\t\tdesc := server.Description()\n\t\t\tassert.Equal(t,\n\t\t\t\ttc.wantDescription,\n\t\t\t\tdesc,\n\t\t\t\t\"expected and actual server descriptions are different\")\n\n\t\t\tgeneration, _ := server.pool.generation.getGeneration(nil)\n\t\t\tassert.Equal(t,\n\t\t\t\ttc.wantGeneration,\n\t\t\t\tgeneration,\n\t\t\t\t\"expected and actual pool generation are different\")\n\t\t})\n\t}\n}\n\nfunc TestServer_getSocketTimeout(t *testing.T) {\n\tt.Parallel()\n\n\ttests := []struct {\n\t\tname              string\n\t\tenableStreaming   bool\n\t\tconnectTimeout    time.Duration\n\t\theartbeatInterval time.Duration\n\t\twant              time.Duration\n\t}{\n\t\t{\n\t\t\tname:              \"server is streamable with connectTimeout and no heartbeat interval\",\n\t\t\tenableStreaming:   true,\n\t\t\tconnectTimeout:    1,\n\t\t\theartbeatInterval: 0,\n\t\t\twant:              1,\n\t\t},\n\t\t{\n\t\t\tname:              \"server is streamable with connectTimeout and heartbeat interval\",\n\t\t\tenableStreaming:   true,\n\t\t\tconnectTimeout:    1,\n\t\t\theartbeatInterval: 1,\n\t\t\twant:              2,\n\t\t},\n\t\t{\n\t\t\tname:              \"server is streamable with no connectTimeout and heartbeat interval\",\n\t\t\tenableStreaming:   true,\n\t\t\tconnectTimeout:    0,\n\t\t\theartbeatInterval: 1,\n\t\t\twant:              0,\n\t\t},\n\t\t{\n\t\t\tname:              \"server is streamable with no connectTimeout and no heartbeat interval\",\n\t\t\tenableStreaming:   true,\n\t\t\tconnectTimeout:    0,\n\t\t\theartbeatInterval: 0,\n\t\t\twant:              0,\n\t\t},\n\t\t{\n\t\t\tname:              \"server is not streamable\",\n\t\t\tenableStreaming:   false,\n\t\t\tconnectTimeout:    1,\n\t\t\theartbeatInterval: 0,\n\t\t\twant:              1,\n\t\t},\n\t}\n\n\tfor _, test := range tests {\n\t\ttest := test // Capture the range variable\n\n\t\tt.Run(test.name, func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tsrv := &Server{\n\t\t\t\tcfg: &serverConfig{\n\t\t\t\t\tconnectTimeout:    test.connectTimeout,\n\t\t\t\t\theartbeatInterval: test.heartbeatInterval,\n\t\t\t\t},\n\t\t\t\tconn: &connection{},\n\t\t\t}\n\n\t\t\tsrv.desc.Store(description.Server{\n\t\t\t\tKind:            description.ServerKind(description.TopologyKindReplicaSet),\n\t\t\t\tTopologyVersion: &description.TopologyVersion{},\n\t\t\t})\n\n\t\t\tif test.enableStreaming {\n\t\t\t\tsrv.cfg.serverMonitoringMode = connstring.ServerMonitoringModeStream\n\t\t\t}\n\n\t\t\tgot := getHeartbeatTimeout(srv)\n\t\t\tassert.Equal(t, test.want, got)\n\t\t})\n\t}\n}\n\n// includesClientMetadata will return true if the wire message includes the\n// \"client\" field.\nfunc includesClientMetadata(t *testing.T, wm []byte) bool {\n\tt.Helper()\n\n\tvar ok bool\n\t_, _, _, _, wm, ok = wiremessage.ReadHeader(wm)\n\tif !ok {\n\t\tt.Fatal(\"could not read header\")\n\t}\n\t_, wm, ok = wiremessage.ReadQueryFlags(wm)\n\tif !ok {\n\t\tt.Fatal(\"could not read flags\")\n\t}\n\t_, wm, ok = wiremessage.ReadQueryFullCollectionName(wm)\n\tif !ok {\n\t\tt.Fatal(\"could not read fullCollectionName\")\n\t}\n\t_, wm, ok = wiremessage.ReadQueryNumberToSkip(wm)\n\tif !ok {\n\t\tt.Fatal(\"could not read numberToSkip\")\n\t}\n\t_, wm, ok = wiremessage.ReadQueryNumberToReturn(wm)\n\tif !ok {\n\t\tt.Fatal(\"could not read numberToReturn\")\n\t}\n\tvar query bsoncore.Document\n\tquery, _, ok = wiremessage.ReadQueryQuery(wm)\n\tif !ok {\n\t\tt.Fatal(\"could not read query\")\n\t}\n\n\tif _, err := query.LookupErr(\"client\"); err == nil {\n\t\treturn true\n\t}\n\tif _, err := query.LookupErr(\"$query\", \"client\"); err == nil {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// processErrorTestConn is a driver.Connection implementation used by tests\n// for Server.ProcessError. This type should not be used for other tests\n// because it does not implement all of the functions of the interface.\ntype processErrorTestConn struct {\n\tmnet.ReadWriteCloser\n\tmnet.Describer\n\t// Embed a driver.Connection to quickly implement the interface without\n\t// implementing all methods.\n\tdescription description.Server\n\tstale       bool\n}\n\nfunc newProcessErrorTestConn(wireVersion *description.VersionRange, stale bool) *mnet.Connection {\n\tpeconn := &processErrorTestConn{\n\t\tdescription: description.Server{\n\t\t\tWireVersion: wireVersion,\n\t\t},\n\t\tstale: stale,\n\t}\n\n\treturn mnet.NewConnection(peconn)\n}\n\nfunc (p *processErrorTestConn) Stale() bool {\n\treturn p.stale\n}\n\nfunc (p *processErrorTestConn) Description() description.Server {\n\treturn p.description\n}\n\n// newServerDescription is a convenience function for creating a server description with a specified\n// kind, topology version process ID and counter, and last error.\nfunc newServerDescription(\n\tkind description.ServerKind,\n\tprocessID bson.ObjectID,\n\tcounter int64,\n\tlastError error,\n) description.Server {\n\treturn description.Server{\n\t\tKind: kind,\n\t\tTopologyVersion: &description.TopologyVersion{\n\t\t\tProcessID: processID,\n\t\t\tCounter:   counter,\n\t\t},\n\t\tLastError: lastError,\n\t}\n}\n\ntype mockServerChecker struct {\n\tsleep time.Duration\n}\n\nvar _ serverChecker = &mockServerChecker{}\n\nfunc (checker *mockServerChecker) check(ctx context.Context) (description.Server, error) {\n\tselect {\n\tcase <-ctx.Done():\n\t\treturn description.Server{}, ctx.Err()\n\tcase <-time.After(checker.sleep):\n\t}\n\n\treturn description.Server{}, nil\n}\n\nfunc TestCheckServerWithSignal(t *testing.T) {\n\tt.Run(\"check finishes before signal\", func(t *testing.T) {\n\t\tlistener := newNonBlockingContextDoneListener()\n\t\tgo func() {\n\t\t\tdefer listener.StopListening()\n\n\t\t\ttime.Sleep(105 * time.Millisecond)\n\t\t}()\n\n\t\t_, err := checkServerWithSignal(&mockServerChecker{sleep: 100 * time.Millisecond}, &connection{}, listener)\n\t\tassert.NoError(t, err)\n\t})\n\n\tt.Run(\"check finishes after signal\", func(t *testing.T) {\n\t\tlistener := newNonBlockingContextDoneListener()\n\t\tgo func() {\n\t\t\tdefer listener.StopListening()\n\n\t\t\ttime.Sleep(100 * time.Millisecond)\n\t\t}()\n\n\t\t_, err := checkServerWithSignal(&mockServerChecker{sleep: 1 * time.Second}, &connection{}, listener)\n\t\tassert.Error(t, err)\n\t\tassert.ErrorIs(t, err, context.Canceled)\n\t})\n}\n"
  },
  {
    "path": "x/mongo/driver/topology/stats.go",
    "content": "// Copyright (C) MongoDB, Inc. 2024-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage topology\n\nimport (\n\t\"container/list\"\n\t\"math\"\n\t\"time\"\n)\n\nfunc standardDeviationList(l *list.List) float64 {\n\tif l.Len() == 0 {\n\t\treturn 0\n\t}\n\n\tvar mean, variance float64\n\tcount := 0.0\n\n\tfor el := l.Front(); el != nil; el = el.Next() {\n\t\tcount++\n\t\tsample := float64(el.Value.(time.Duration))\n\n\t\tdelta := sample - mean\n\t\tmean += delta / count\n\t\tvariance += delta * (sample - mean)\n\t}\n\n\treturn math.Sqrt(variance / count)\n}\n"
  },
  {
    "path": "x/mongo/driver/topology/stats_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2022-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage topology\n\nimport (\n\t\"container/list\"\n\t\"testing\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n)\n\nfunc TestStandardDeviationList_Duration(t *testing.T) {\n\ttests := []struct {\n\t\tname string\n\t\tdata []time.Duration\n\t\twant float64\n\t}{\n\t\t{\n\t\t\tname: \"empty\",\n\t\t\tdata: []time.Duration{},\n\t\t\twant: 0,\n\t\t},\n\t\t{\n\t\t\tname: \"multiple\",\n\t\t\tdata: []time.Duration{\n\t\t\t\ttime.Millisecond,\n\t\t\t\t2 * time.Millisecond,\n\t\t\t\ttime.Microsecond,\n\t\t\t},\n\t\t\twant: 816088.36667497,\n\t\t},\n\t}\n\n\tfor _, test := range tests {\n\t\tt.Run(test.name, func(t *testing.T) {\n\t\t\tl := list.New()\n\t\t\tfor _, d := range test.data {\n\t\t\t\tl.PushBack(d)\n\t\t\t}\n\n\t\t\tgot := standardDeviationList(l)\n\n\t\t\tassert.InDelta(t, test.want, got, 1e-6)\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "x/mongo/driver/topology/tls_connection_source_1_16.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\n//go:build !go1.17\n\npackage topology\n\nimport (\n\t\"context\"\n\t\"crypto/tls\"\n\t\"net\"\n)\n\ntype tlsConn interface {\n\tnet.Conn\n\n\t// Only require Handshake on the interface for Go 1.16 and less.\n\tHandshake() error\n\tConnectionState() tls.ConnectionState\n}\n\nvar _ tlsConn = (*tls.Conn)(nil)\n\ntype tlsConnectionSource interface {\n\tClient(net.Conn, *tls.Config) tlsConn\n}\n\ntype tlsConnectionSourceFn func(net.Conn, *tls.Config) tlsConn\n\nvar _ tlsConnectionSource = (tlsConnectionSourceFn)(nil)\n\nfunc (t tlsConnectionSourceFn) Client(nc net.Conn, cfg *tls.Config) tlsConn {\n\treturn t(nc, cfg)\n}\n\nvar defaultTLSConnectionSource tlsConnectionSourceFn = func(nc net.Conn, cfg *tls.Config) tlsConn {\n\treturn tls.Client(nc, cfg)\n}\n\n// clientHandshake will perform a handshake with a goroutine and wait for its completion on Go 1.16 and less\n// when HandshakeContext is not available.\nfunc clientHandshake(ctx context.Context, client tlsConn) error {\n\terrChan := make(chan error, 1)\n\tgo func() {\n\t\terrChan <- client.Handshake()\n\t}()\n\n\tselect {\n\tcase err := <-errChan:\n\t\treturn err\n\tcase <-ctx.Done():\n\t\treturn ctx.Err()\n\t}\n}\n"
  },
  {
    "path": "x/mongo/driver/topology/tls_connection_source_1_17.go",
    "content": "// Copyright (C) MongoDB, Inc. 2022-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\n//go:build go1.17\n\npackage topology\n\nimport (\n\t\"context\"\n\t\"crypto/tls\"\n\t\"net\"\n)\n\ntype tlsConn interface {\n\tnet.Conn\n\n\t// Require HandshakeContext on the interface for Go 1.17 and higher.\n\tHandshakeContext(ctx context.Context) error\n\tConnectionState() tls.ConnectionState\n}\n\nvar _ tlsConn = (*tls.Conn)(nil)\n\ntype tlsConnectionSource interface {\n\tClient(net.Conn, *tls.Config) tlsConn\n}\n\ntype tlsConnectionSourceFn func(net.Conn, *tls.Config) tlsConn\n\nvar _ tlsConnectionSource = (tlsConnectionSourceFn)(nil)\n\nfunc (t tlsConnectionSourceFn) Client(nc net.Conn, cfg *tls.Config) tlsConn {\n\treturn t(nc, cfg)\n}\n\nvar defaultTLSConnectionSource tlsConnectionSourceFn = func(nc net.Conn, cfg *tls.Config) tlsConn {\n\treturn tls.Client(nc, cfg)\n}\n\n// clientHandshake will perform a handshake on Go 1.17 and higher with HandshakeContext.\nfunc clientHandshake(ctx context.Context, client tlsConn) error {\n\treturn client.HandshakeContext(ctx)\n}\n"
  },
  {
    "path": "x/mongo/driver/topology/topology.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\n// Package topology is intended for internal use only. It is made available to\n// facilitate use cases that require access to internal MongoDB driver\n// functionality and state. The API of this package is not stable and there is\n// no backward compatibility guarantee.\n//\n// WARNING: THIS PACKAGE IS EXPERIMENTAL AND MAY BE MODIFIED OR REMOVED WITHOUT\n// NOTICE! USE WITH EXTREME CAUTION!\n//\n// Package topology contains types that handles the discovery, monitoring, and\n// selection of servers. This package is designed to expose enough inner\n// workings of service discovery and monitoring to allow low level applications\n// to have fine grained control, while hiding most of the detailed\n// implementation of the algorithms.\npackage topology\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"net\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\t\"sync/atomic\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/event\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/driverutil\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/logger\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/randutil\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/address\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/connstring\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/description\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/dns\"\n)\n\n// Topology state constants.\nconst (\n\ttopologyDisconnected int64 = iota\n\ttopologyDisconnecting\n\ttopologyConnected\n\ttopologyConnecting\n)\n\n// ErrSubscribeAfterClosed is returned when a user attempts to subscribe to a\n// closed Server or Topology.\nvar ErrSubscribeAfterClosed = errors.New(\"cannot subscribe after closeConnection\")\n\n// ErrTopologyClosed is returned when a user attempts to call a method on a\n// closed Topology.\nvar ErrTopologyClosed = errors.New(\"topology is closed\")\n\n// ErrTopologyConnected is returned whena  user attempts to Connect to an\n// already connected Topology.\nvar ErrTopologyConnected = errors.New(\"topology is connected or connecting\")\n\n// MonitorMode represents the way in which a server is monitored.\ntype MonitorMode uint8\n\n// random is a package-global pseudo-random number generator.\nvar random = randutil.NewLockedRand()\n\n// These constants are the available monitoring modes.\nconst (\n\tAutomaticMode MonitorMode = iota\n\tSingleMode\n)\n\n// Topology represents a MongoDB deployment.\ntype Topology struct {\n\tstate int64\n\n\tcfg *Config\n\n\tdesc atomic.Value // holds a description.Topology\n\n\tdnsResolver *dns.Resolver\n\n\tdone chan struct{}\n\n\tpollingRequired   bool\n\tpollingDone       chan struct{}\n\tpollingwg         sync.WaitGroup\n\trescanSRVInterval time.Duration\n\tpollHeartbeatTime atomic.Value // holds a bool\n\n\thosts []string\n\n\tupdateCallback updateTopologyCallback\n\tfsm            *fsm\n\n\t// This should really be encapsulated into it's own type. This will likely\n\t// require a redesign so we can share a minimum of data between the\n\t// subscribers and the topology.\n\tsubscribers         map[uint64]chan description.Topology\n\tcurrentSubscriberID uint64\n\tsubscriptionsClosed bool\n\tsubLock             sync.Mutex\n\n\t// We should redesign how we Connect and handle individual servers. This is\n\t// too difficult to maintain and it's rather easy to accidentally access\n\t// the servers without acquiring the lock or checking if the servers are\n\t// closed. This lock should also be an RWMutex.\n\tserversLock   sync.Mutex\n\tserversClosed bool\n\tservers       map[address.Address]*Server\n\n\tid bson.ObjectID\n}\n\nvar (\n\t_ driver.Deployment = &Topology{}\n\t_ driver.Subscriber = &Topology{}\n)\n\n// New creates a new topology. A \"nil\" config is interpreted as the default configuration.\nfunc New(cfg *Config) (*Topology, error) {\n\tif cfg == nil {\n\t\tvar err error\n\t\tcfg, err = NewConfig(options.Client(), nil)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\tt := &Topology{\n\t\tcfg:               cfg,\n\t\tdone:              make(chan struct{}),\n\t\tpollingDone:       make(chan struct{}),\n\t\trescanSRVInterval: 60 * time.Second,\n\t\tfsm:               newFSM(),\n\t\tsubscribers:       make(map[uint64]chan description.Topology),\n\t\tservers:           make(map[address.Address]*Server),\n\t\tdnsResolver:       dns.DefaultResolver,\n\t\tid:                bson.NewObjectID(),\n\t}\n\tt.desc.Store(description.Topology{})\n\tt.updateCallback = func(desc description.Server) description.Server {\n\t\treturn t.apply(context.Background(), desc)\n\t}\n\n\tif t.cfg.URI != \"\" {\n\t\tconnStr, err := connstring.Parse(t.cfg.URI)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tt.pollingRequired = (connStr.Scheme == connstring.SchemeMongoDBSRV) && !t.cfg.LoadBalanced\n\t\tt.hosts = connStr.RawHosts\n\t}\n\n\tt.publishTopologyOpeningEvent()\n\n\treturn t, nil\n}\n\nfunc mustLogTopologyMessage(topo *Topology, level logger.Level) bool {\n\treturn topo.cfg.logger != nil && topo.cfg.logger.LevelComponentEnabled(\n\t\tlevel, logger.ComponentTopology)\n}\n\nfunc logTopologyMessage(topo *Topology, level logger.Level, msg string, keysAndValues ...any) {\n\ttopo.cfg.logger.Print(level,\n\t\tlogger.ComponentTopology,\n\t\tmsg,\n\t\tlogger.SerializeTopology(logger.Topology{\n\t\t\tID:      topo.id,\n\t\t\tMessage: msg,\n\t\t}, keysAndValues...)...)\n}\n\nfunc logTopologyThirdPartyUsage(topo *Topology, parsedHosts []string) {\n\tthirdPartyMessages := [2]string{\n\t\t`You appear to be connected to a CosmosDB cluster. For more information regarding feature compatibility and support please visit https://www.mongodb.com/supportability/cosmosdb`,\n\t\t`You appear to be connected to a DocumentDB cluster. For more information regarding feature compatibility and support please visit https://www.mongodb.com/supportability/documentdb`,\n\t}\n\n\tthirdPartySuffixes := map[string]int{\n\t\t\".cosmos.azure.com\":            0,\n\t\t\".docdb.amazonaws.com\":         1,\n\t\t\".docdb-elastic.amazonaws.com\": 1,\n\t}\n\n\thostSet := make([]bool, len(thirdPartyMessages))\n\tfor _, host := range parsedHosts {\n\t\tif h, _, err := net.SplitHostPort(host); err == nil {\n\t\t\thost = h\n\t\t}\n\t\tfor suffix, env := range thirdPartySuffixes {\n\t\t\tif !strings.HasSuffix(host, suffix) {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif hostSet[env] {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\thostSet[env] = true\n\t\t\tlogTopologyMessage(topo, logger.LevelInfo, thirdPartyMessages[env])\n\t\t}\n\t}\n}\n\nfunc mustLogServerSelection(topo *Topology, level logger.Level) bool {\n\treturn topo.cfg.logger != nil && topo.cfg.logger.LevelComponentEnabled(\n\t\tlevel, logger.ComponentServerSelection)\n}\n\nfunc logServerSelection(\n\tctx context.Context,\n\ttopo *Topology,\n\tlevel logger.Level,\n\tmsg string,\n\tsrvSelector description.ServerSelector,\n\tkeysAndValues ...any,\n) {\n\tvar srvSelectorString string\n\n\tselectorStringer, ok := srvSelector.(fmt.Stringer)\n\tif ok {\n\t\tsrvSelectorString = selectorStringer.String()\n\t}\n\n\toperationName, _ := logger.OperationName(ctx)\n\toperationID, _ := logger.OperationID(ctx)\n\n\ttopo.cfg.logger.Print(level,\n\t\tlogger.ComponentServerSelection,\n\t\tmsg,\n\t\tlogger.SerializeServerSelection(logger.ServerSelection{\n\t\t\tSelector:            srvSelectorString,\n\t\t\tOperation:           operationName,\n\t\t\tOperationID:         &operationID,\n\t\t\tTopologyDescription: topo.String(),\n\t\t}, keysAndValues...)...)\n}\n\nfunc logServerSelectionSucceeded(\n\tctx context.Context,\n\ttopo *Topology,\n\tsrvSelector description.ServerSelector,\n\tserver *SelectedServer,\n) {\n\thost, port, err := net.SplitHostPort(server.address.String())\n\tif err != nil {\n\t\thost = server.address.String()\n\t\tport = \"\"\n\t}\n\n\tportInt64, _ := strconv.ParseInt(port, 10, 32)\n\n\tlogServerSelection(ctx, topo, logger.LevelDebug, logger.ServerSelectionSucceeded, srvSelector,\n\t\tlogger.KeyServerHost, host,\n\t\tlogger.KeyServerPort, portInt64)\n}\n\nfunc logServerSelectionFailed(\n\tctx context.Context,\n\ttopo *Topology,\n\tsrvSelector description.ServerSelector,\n\terr error,\n) {\n\tlogServerSelection(ctx, topo, logger.LevelDebug, logger.ServerSelectionFailed, srvSelector,\n\t\tlogger.KeyFailure, err.Error())\n}\n\n// Connect initializes a Topology and starts the monitoring process. This function\n// must be called to properly monitor the topology.\nfunc (t *Topology) Connect() error {\n\tif !atomic.CompareAndSwapInt64(&t.state, topologyDisconnected, topologyConnecting) {\n\t\treturn ErrTopologyConnected\n\t}\n\n\tt.desc.Store(description.Topology{})\n\tvar err error\n\tt.serversLock.Lock()\n\n\t// A replica set name sets the initial topology type to ReplicaSetNoPrimary unless a direct connection is also\n\t// specified, in which case the initial type is Single.\n\tif t.cfg.ReplicaSetName != \"\" {\n\t\tt.fsm.SetName = t.cfg.ReplicaSetName\n\t\tt.fsm.Kind = description.TopologyKindReplicaSetNoPrimary\n\t}\n\n\t// A direct connection unconditionally sets the topology type to Single.\n\tif t.cfg.Mode == SingleMode {\n\t\tt.fsm.Kind = description.TopologyKindSingle\n\t}\n\n\tfor _, a := range t.cfg.SeedList {\n\t\taddr := address.Address(a).Canonicalize()\n\t\tt.fsm.Servers = append(t.fsm.Servers, newServerDescriptionFromError(addr, nil, nil))\n\t}\n\n\tswitch {\n\tcase t.cfg.LoadBalanced:\n\t\t// In LoadBalanced mode, we mock a series of events: TopologyDescriptionChanged from Unknown to LoadBalanced,\n\t\t// ServerDescriptionChanged from Unknown to LoadBalancer, and then TopologyDescriptionChanged to reflect the\n\t\t// previous ServerDescriptionChanged event. We publish all of these events here because we don't start server\n\t\t// monitoring routines in this mode, so we have to mock state changes.\n\n\t\t// Transition from Unknown with no servers to LoadBalanced with a single Unknown server.\n\t\tt.fsm.Kind = description.TopologyKindLoadBalanced\n\t\tt.publishTopologyDescriptionChangedEvent(description.Topology{}, t.fsm.Topology)\n\n\t\taddr := address.Address(t.cfg.SeedList[0]).Canonicalize()\n\t\tif err := t.addServer(addr); err != nil {\n\t\t\tt.serversLock.Unlock()\n\t\t\treturn err\n\t\t}\n\n\t\t// Transition the server from Unknown to LoadBalancer.\n\t\tnewServerDesc := t.servers[addr].Description()\n\t\tt.publishServerDescriptionChangedEvent(t.fsm.Servers[0], newServerDesc)\n\n\t\t// Transition from LoadBalanced with an Unknown server to LoadBalanced with a LoadBalancer.\n\t\toldDesc := t.fsm.Topology\n\t\tt.fsm.Servers = []description.Server{newServerDesc}\n\t\tt.desc.Store(t.fsm.Topology)\n\t\tt.publishTopologyDescriptionChangedEvent(oldDesc, t.fsm.Topology)\n\tdefault:\n\t\t// In non-LB mode, we only publish an initial TopologyDescriptionChanged event from Unknown with no servers to\n\t\t// the current state (e.g. Unknown with one or more servers if we're discovering or Single with one server if\n\t\t// we're connecting directly). Other events are published when state changes occur due to responses in the\n\t\t// server monitoring goroutines.\n\n\t\tnewDesc := description.Topology{\n\t\t\tKind:                  t.fsm.Kind,\n\t\t\tServers:               t.fsm.Servers,\n\t\t\tSessionTimeoutMinutes: t.fsm.SessionTimeoutMinutes,\n\t\t}\n\t\tt.desc.Store(newDesc)\n\t\tt.publishTopologyDescriptionChangedEvent(description.Topology{}, t.fsm.Topology)\n\t\tfor _, a := range t.cfg.SeedList {\n\t\t\taddr := address.Address(a).Canonicalize()\n\t\t\terr = t.addServer(addr)\n\t\t\tif err != nil {\n\t\t\t\tt.serversLock.Unlock()\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t}\n\n\tt.serversLock.Unlock()\n\tif mustLogTopologyMessage(t, logger.LevelInfo) {\n\t\tlogTopologyThirdPartyUsage(t, t.hosts)\n\t}\n\tif t.pollingRequired {\n\t\t// sanity check before passing the hostname to resolver\n\t\tif len(t.hosts) != 1 {\n\t\t\treturn fmt.Errorf(\"URI with SRV must include one and only one hostname\")\n\t\t}\n\t\t_, _, err = net.SplitHostPort(t.hosts[0])\n\t\tif err == nil {\n\t\t\t// we were able to successfully extract a port from the host,\n\t\t\t// but should not be able to when using SRV\n\t\t\treturn fmt.Errorf(\"URI with srv must not include a port number\")\n\t\t}\n\t\tgo t.pollSRVRecords(t.hosts[0])\n\t\tt.pollingwg.Add(1)\n\t}\n\n\tt.subscriptionsClosed = false // explicitly set in case topology was disconnected and then reconnected\n\n\tatomic.StoreInt64(&t.state, topologyConnected)\n\treturn nil\n}\n\n// Disconnect closes the topology. It stops the monitoring thread and\n// closes all open subscriptions.\nfunc (t *Topology) Disconnect(ctx context.Context) error {\n\tif !atomic.CompareAndSwapInt64(&t.state, topologyConnected, topologyDisconnecting) {\n\t\treturn ErrTopologyClosed\n\t}\n\n\tservers := make(map[address.Address]*Server)\n\tt.serversLock.Lock()\n\tt.serversClosed = true\n\tfor addr, server := range t.servers {\n\t\tservers[addr] = server\n\t}\n\tt.serversLock.Unlock()\n\n\tfor _, server := range servers {\n\t\t_ = server.Disconnect(ctx)\n\t\tt.publishServerClosedEvent(server.address)\n\t}\n\n\tt.subLock.Lock()\n\tfor id, ch := range t.subscribers {\n\t\tclose(ch)\n\t\tdelete(t.subscribers, id)\n\t}\n\tt.subscriptionsClosed = true\n\tt.subLock.Unlock()\n\n\tif t.pollingRequired {\n\t\tt.pollingDone <- struct{}{}\n\t\tt.pollingwg.Wait()\n\t}\n\n\toldDesc := t.fsm.Topology\n\tt.fsm = newFSM()\n\tt.desc.Store(t.fsm.Topology)\n\tt.publishTopologyDescriptionChangedEvent(oldDesc, t.fsm.Topology)\n\n\tatomic.StoreInt64(&t.state, topologyDisconnected)\n\tt.publishTopologyClosedEvent()\n\treturn nil\n}\n\n// Description returns a description of the topology.\nfunc (t *Topology) Description() description.Topology {\n\ttd, ok := t.desc.Load().(description.Topology)\n\tif !ok {\n\t\ttd = description.Topology{}\n\t}\n\treturn td\n}\n\n// Kind returns the topology kind of this Topology.\nfunc (t *Topology) Kind() description.TopologyKind { return t.Description().Kind }\n\n// Subscribe returns a Subscription on which all updated description.Topologys\n// will be sent. The channel of the subscription will have a buffer size of one,\n// and will be pre-populated with the current description.Topology.\n// Subscribe implements the driver.Subscriber interface.\nfunc (t *Topology) Subscribe() (*driver.Subscription, error) {\n\tif atomic.LoadInt64(&t.state) != topologyConnected {\n\t\treturn nil, errors.New(\"cannot subscribe to Topology that is not connected\")\n\t}\n\tch := make(chan description.Topology, 1)\n\ttd, ok := t.desc.Load().(description.Topology)\n\tif !ok {\n\t\ttd = description.Topology{}\n\t}\n\tch <- td\n\n\tt.subLock.Lock()\n\tdefer t.subLock.Unlock()\n\tif t.subscriptionsClosed {\n\t\treturn nil, ErrSubscribeAfterClosed\n\t}\n\tid := t.currentSubscriberID\n\tt.subscribers[id] = ch\n\tt.currentSubscriberID++\n\n\treturn &driver.Subscription{\n\t\tUpdates: ch,\n\t\tID:      id,\n\t}, nil\n}\n\n// Unsubscribe unsubscribes the given subscription from the topology and closes the subscription channel.\n// Unsubscribe implements the driver.Subscriber interface.\nfunc (t *Topology) Unsubscribe(sub *driver.Subscription) error {\n\tt.subLock.Lock()\n\tdefer t.subLock.Unlock()\n\n\tif t.subscriptionsClosed {\n\t\treturn nil\n\t}\n\n\tch, ok := t.subscribers[sub.ID]\n\tif !ok {\n\t\treturn nil\n\t}\n\n\tclose(ch)\n\tdelete(t.subscribers, sub.ID)\n\treturn nil\n}\n\n// RequestImmediateCheck will send heartbeats to all the servers in the\n// topology right away, instead of waiting for the heartbeat timeout.\nfunc (t *Topology) RequestImmediateCheck() {\n\tif atomic.LoadInt64(&t.state) != topologyConnected {\n\t\treturn\n\t}\n\tt.serversLock.Lock()\n\tfor _, server := range t.servers {\n\t\tserver.RequestImmediateCheck()\n\t}\n\tt.serversLock.Unlock()\n}\n\n// SelectServer selects a server with given a selector, returning the remaining\n// computedServerSelectionTimeout.\nfunc (t *Topology) SelectServer(ctx context.Context, ss description.ServerSelector) (driver.Server, error) {\n\tif atomic.LoadInt64(&t.state) != topologyConnected {\n\t\tif mustLogServerSelection(t, logger.LevelDebug) {\n\t\t\tlogServerSelectionFailed(ctx, t, ss, ErrTopologyClosed)\n\t\t}\n\n\t\treturn nil, ErrTopologyClosed\n\t}\n\n\tvar doneOnce bool\n\tvar sub *driver.Subscription\n\n\t// Record the start time.\n\tstartTime := time.Now()\n\tfor {\n\t\tvar suitable []description.Server\n\t\tvar selectErr error\n\n\t\tif !doneOnce {\n\t\t\tif mustLogServerSelection(t, logger.LevelDebug) {\n\t\t\t\tlogServerSelection(ctx, t, logger.LevelDebug, logger.ServerSelectionStarted, ss)\n\t\t\t}\n\n\t\t\t// for the first pass, select a server from the current description.\n\t\t\t// this improves selection speed for up-to-date topology descriptions.\n\t\t\tsuitable, selectErr = t.selectServerFromDescription(t.Description(), ss)\n\t\t\tdoneOnce = true\n\t\t} else {\n\t\t\t// if the first pass didn't select a server, the previous description did not contain a suitable server, so\n\t\t\t// we subscribe to the topology and attempt to obtain a server from that subscription\n\t\t\tif sub == nil {\n\t\t\t\tvar err error\n\t\t\t\tsub, err = t.Subscribe()\n\t\t\t\tif err != nil {\n\t\t\t\t\tif mustLogServerSelection(t, logger.LevelDebug) {\n\t\t\t\t\t\tlogServerSelectionFailed(ctx, t, ss, err)\n\t\t\t\t\t}\n\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t\tdefer func() { _ = t.Unsubscribe(sub) }()\n\t\t\t}\n\n\t\t\tsuitable, selectErr = t.selectServerFromSubscription(ctx, sub.Updates, ss)\n\t\t}\n\t\tif selectErr != nil {\n\t\t\tif mustLogServerSelection(t, logger.LevelDebug) {\n\t\t\t\tlogServerSelectionFailed(ctx, t, ss, selectErr)\n\t\t\t}\n\n\t\t\treturn nil, selectErr\n\t\t}\n\n\t\tif len(suitable) == 0 {\n\t\t\t// try again if there are no servers available\n\t\t\tif mustLogServerSelection(t, logger.LevelInfo) {\n\t\t\t\telapsed := time.Since(startTime)\n\t\t\t\tremainingTime := t.cfg.ServerSelectionTimeout - elapsed\n\n\t\t\t\tlogServerSelection(ctx, t, logger.LevelInfo, logger.ServerSelectionWaiting, ss,\n\t\t\t\t\tlogger.KeyRemainingTimeMS, remainingTime.Milliseconds())\n\t\t\t}\n\n\t\t\tcontinue\n\t\t}\n\n\t\t// If there's only one suitable server description, try to find the associated server and\n\t\t// return it. This is an optimization primarily for standalone and load-balanced deployments.\n\t\tif len(suitable) == 1 {\n\t\t\tserver, err := t.FindServer(suitable[0])\n\t\t\tif err != nil {\n\t\t\t\tif mustLogServerSelection(t, logger.LevelDebug) {\n\t\t\t\t\tlogServerSelectionFailed(ctx, t, ss, err)\n\t\t\t\t}\n\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tif server == nil {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tif mustLogServerSelection(t, logger.LevelDebug) {\n\t\t\t\tlogServerSelectionSucceeded(ctx, t, ss, server)\n\t\t\t}\n\n\t\t\treturn server, nil\n\t\t}\n\n\t\t// Randomly select 2 suitable server descriptions and find servers for them. We select two\n\t\t// so we can pick the one with the one with fewer in-progress operations below.\n\t\tdesc1, desc2 := pick2(suitable)\n\t\tserver1, err := t.FindServer(desc1)\n\t\tif err != nil {\n\t\t\tif mustLogServerSelection(t, logger.LevelDebug) {\n\t\t\t\tlogServerSelectionFailed(ctx, t, ss, err)\n\t\t\t}\n\n\t\t\treturn nil, err\n\t\t}\n\t\tserver2, err := t.FindServer(desc2)\n\t\tif err != nil {\n\t\t\tif mustLogServerSelection(t, logger.LevelDebug) {\n\t\t\t\tlogServerSelectionFailed(ctx, t, ss, err)\n\t\t\t}\n\n\t\t\treturn nil, err\n\t\t}\n\n\t\t// If we don't have an actual server for one or both of the provided descriptions, either\n\t\t// return the one server we have, or try again if they're both nil. This could happen for a\n\t\t// number of reasons, including that the server has since stopped being a part of this\n\t\t// topology.\n\t\tif server1 == nil || server2 == nil {\n\t\t\tif server1 == nil && server2 == nil {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tif server1 != nil {\n\t\t\t\tif mustLogServerSelection(t, logger.LevelDebug) {\n\t\t\t\t\tlogServerSelectionSucceeded(ctx, t, ss, server1)\n\t\t\t\t}\n\t\t\t\treturn server1, nil\n\t\t\t}\n\n\t\t\tif mustLogServerSelection(t, logger.LevelDebug) {\n\t\t\t\tlogServerSelectionSucceeded(ctx, t, ss, server2)\n\t\t\t}\n\n\t\t\treturn server2, nil\n\t\t}\n\n\t\t// Of the two randomly selected suitable servers, pick the one with fewer in-use connections.\n\t\t// We use in-use connections as an analog for in-progress operations because they are almost\n\t\t// always the same value for a given server.\n\t\tif server1.OperationCount() < server2.OperationCount() {\n\t\t\tif mustLogServerSelection(t, logger.LevelDebug) {\n\t\t\t\tlogServerSelectionSucceeded(ctx, t, ss, server1)\n\t\t\t}\n\n\t\t\treturn server1, nil\n\t\t}\n\n\t\tif mustLogServerSelection(t, logger.LevelDebug) {\n\t\t\tlogServerSelectionSucceeded(ctx, t, ss, server2)\n\t\t}\n\t\treturn server2, nil\n\t}\n}\n\n// pick2 returns 2 random server descriptions from the input slice of server descriptions,\n// guaranteeing that the same element from the slice is not picked twice. The order of server\n// descriptions in the input slice may be modified. If fewer than 2 server descriptions are\n// provided, pick2 will panic.\nfunc pick2(ds []description.Server) (description.Server, description.Server) {\n\t// Select a random index from the input slice and keep the server description from that index.\n\tidx := random.Intn(len(ds))\n\ts1 := ds[idx]\n\n\t// Swap the selected index to the end and reslice to remove it so we don't pick the same server\n\t// description twice.\n\tds[idx], ds[len(ds)-1] = ds[len(ds)-1], ds[idx]\n\tds = ds[:len(ds)-1]\n\n\t// Select another random index from the input slice and return both selected server descriptions.\n\treturn s1, ds[random.Intn(len(ds))]\n}\n\n// FindServer will attempt to find a server that fits the given server description.\n// This method will return nil, nil if a matching server could not be found.\nfunc (t *Topology) FindServer(selected description.Server) (*SelectedServer, error) {\n\tif atomic.LoadInt64(&t.state) != topologyConnected {\n\t\treturn nil, ErrTopologyClosed\n\t}\n\tt.serversLock.Lock()\n\tdefer t.serversLock.Unlock()\n\tserver, ok := t.servers[selected.Addr]\n\tif !ok {\n\t\treturn nil, nil\n\t}\n\n\tdesc := t.Description()\n\treturn &SelectedServer{\n\t\tServer: server,\n\t\tKind:   desc.Kind,\n\t}, nil\n}\n\n// selectServerFromSubscription loops until a topology description is available for server selection. It returns\n// when the given context expires, server selection timeout is reached, or a description containing a selectable\n// server is available.\nfunc (t *Topology) selectServerFromSubscription(\n\tctx context.Context,\n\tsubscriptionCh <-chan description.Topology,\n\tsrvSelector description.ServerSelector,\n) ([]description.Server, error) {\n\tcurrent := t.Description()\n\tfor {\n\t\tselect {\n\t\tcase <-ctx.Done():\n\t\t\treturn nil, ServerSelectionError{Wrapped: ctx.Err(), Desc: current}\n\t\tcase current = <-subscriptionCh:\n\t\t}\n\n\t\tsuitable, err := t.selectServerFromDescription(current, srvSelector)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tif len(suitable) > 0 {\n\t\t\treturn suitable, nil\n\t\t}\n\t\tt.RequestImmediateCheck()\n\t}\n}\n\n// selectServerFromDescription process the given topology description and returns a slice of suitable servers.\nfunc (t *Topology) selectServerFromDescription(\n\tdesc description.Topology,\n\tsrvSelector description.ServerSelector,\n) ([]description.Server, error) {\n\t// Unlike selectServerFromSubscription, this code path does not check ctx.Done or selectionState.timeoutChan because\n\t// selecting a server from a description is not a blocking operation.\n\n\tif desc.CompatibilityErr != nil {\n\t\treturn nil, desc.CompatibilityErr\n\t}\n\n\t// If the topology kind is LoadBalanced, the LB is the only server and it is always considered selectable. The\n\t// selectors exported by the driver should already return the LB as a candidate, so this but this check ensures that\n\t// the LB is always selectable even if a user of the low-level driver provides a custom selector.\n\tif desc.Kind == description.TopologyKindLoadBalanced {\n\t\treturn desc.Servers, nil\n\t}\n\n\tallowedIndexes := make([]int, 0, len(desc.Servers))\n\tfor i, s := range desc.Servers {\n\t\tif s.Kind != description.Unknown {\n\t\t\tallowedIndexes = append(allowedIndexes, i)\n\t\t}\n\t}\n\n\tallowed := make([]description.Server, len(allowedIndexes))\n\tfor i, idx := range allowedIndexes {\n\t\tallowed[i] = desc.Servers[idx]\n\t}\n\n\tsuitable, err := srvSelector.SelectServer(desc, allowed)\n\tif err != nil {\n\t\treturn nil, ServerSelectionError{Wrapped: err, Desc: desc}\n\t}\n\treturn suitable, nil\n}\n\nfunc (t *Topology) pollSRVRecords(hosts string) {\n\tdefer t.pollingwg.Done()\n\n\tserverConfig := newServerConfig(t.cfg.ConnectTimeout, t.cfg.ServerOpts...)\n\theartbeatInterval := serverConfig.heartbeatInterval\n\n\tpollTicker := time.NewTicker(t.rescanSRVInterval)\n\tdefer pollTicker.Stop()\n\tt.pollHeartbeatTime.Store(false)\n\tvar doneOnce bool\n\tdefer func() {\n\t\t//  ¯\\_(ツ)_/¯\n\t\tif r := recover(); r != nil && !doneOnce {\n\t\t\t<-t.pollingDone\n\t\t}\n\t}()\n\n\tfor {\n\t\tselect {\n\t\tcase <-pollTicker.C:\n\t\tcase <-t.pollingDone:\n\t\t\tdoneOnce = true\n\t\t\treturn\n\t\t}\n\t\ttopoKind := t.Description().Kind\n\t\tif topoKind != description.Unknown && topoKind != description.TopologyKindSharded {\n\t\t\tbreak\n\t\t}\n\n\t\tparsedHosts, err := t.dnsResolver.ParseHosts(hosts, t.cfg.SRVServiceName, false)\n\t\t// DNS problem or no verified hosts returned\n\t\tif err != nil || len(parsedHosts) == 0 {\n\t\t\tif !t.pollHeartbeatTime.Load().(bool) {\n\t\t\t\tpollTicker.Stop()\n\t\t\t\tpollTicker = time.NewTicker(heartbeatInterval)\n\t\t\t\tt.pollHeartbeatTime.Store(true)\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\t\tif t.pollHeartbeatTime.Load().(bool) {\n\t\t\tpollTicker.Stop()\n\t\t\tpollTicker = time.NewTicker(t.rescanSRVInterval)\n\t\t\tt.pollHeartbeatTime.Store(false)\n\t\t}\n\n\t\tcont := t.processSRVResults(parsedHosts)\n\t\tif !cont {\n\t\t\tbreak\n\t\t}\n\t}\n\t<-t.pollingDone\n\tdoneOnce = true\n}\n\n// equalTopologies compares two topology descriptions and returns true if they\n// are equal.\nfunc equalTopologies(topo1, topo2 description.Topology) bool {\n\tif topo1.Kind != topo2.Kind {\n\t\treturn false\n\t}\n\n\ttopoServers := make(map[string]description.Server, len(topo1.Servers))\n\tfor _, s := range topo1.Servers {\n\t\ttopoServers[s.Addr.String()] = s\n\t}\n\n\totherServers := make(map[string]description.Server, len(topo2.Servers))\n\tfor _, s := range topo2.Servers {\n\t\totherServers[s.Addr.String()] = s\n\t}\n\n\tif len(topoServers) != len(otherServers) {\n\t\treturn false\n\t}\n\n\tfor _, server := range topoServers {\n\t\totherServer := otherServers[server.Addr.String()]\n\n\t\tif !driverutil.EqualServers(server, otherServer) {\n\t\t\treturn false\n\t\t}\n\t}\n\n\treturn true\n}\n\nfunc (t *Topology) processSRVResults(parsedHosts []string) bool {\n\tt.serversLock.Lock()\n\tdefer t.serversLock.Unlock()\n\n\tif t.serversClosed {\n\t\treturn false\n\t}\n\tprev := t.fsm.Topology\n\tdiff := diffHostList(t.fsm.Topology, parsedHosts)\n\n\tif len(diff.Added) == 0 && len(diff.Removed) == 0 {\n\t\treturn true\n\t}\n\n\tfor _, r := range diff.Removed {\n\t\taddr := address.Address(r).Canonicalize()\n\t\ts, ok := t.servers[addr]\n\t\tif !ok {\n\t\t\tcontinue\n\t\t}\n\t\tgo func() {\n\t\t\tcancelCtx, cancel := context.WithCancel(context.Background())\n\t\t\tcancel()\n\t\t\t_ = s.Disconnect(cancelCtx)\n\t\t}()\n\t\tdelete(t.servers, addr)\n\t\tt.fsm.removeServerByAddr(addr)\n\t\tt.publishServerClosedEvent(s.address)\n\t}\n\n\t// Now that we've removed all the hosts that disappeared from the SRV record, we need to add any\n\t// new hosts added to the SRV record. If adding all of the new hosts would increase the number\n\t// of servers past srvMaxHosts, shuffle the list of added hosts.\n\tif t.cfg.SRVMaxHosts > 0 && len(t.servers)+len(diff.Added) > t.cfg.SRVMaxHosts {\n\t\trandom.Shuffle(len(diff.Added), func(i, j int) {\n\t\t\tdiff.Added[i], diff.Added[j] = diff.Added[j], diff.Added[i]\n\t\t})\n\t}\n\t// Add all added hosts until the number of servers reaches srvMaxHosts.\n\tfor _, a := range diff.Added {\n\t\tif t.cfg.SRVMaxHosts > 0 && len(t.servers) >= t.cfg.SRVMaxHosts {\n\t\t\tbreak\n\t\t}\n\t\taddr := address.Address(a).Canonicalize()\n\t\t_ = t.addServer(addr)\n\t\tt.fsm.addServer(addr)\n\t}\n\n\t// store new description\n\tnewDesc := description.Topology{\n\t\tKind:                  t.fsm.Kind,\n\t\tServers:               t.fsm.Servers,\n\t\tSessionTimeoutMinutes: t.fsm.SessionTimeoutMinutes,\n\t}\n\tt.desc.Store(newDesc)\n\n\tif !equalTopologies(prev, newDesc) {\n\t\tt.publishTopologyDescriptionChangedEvent(prev, newDesc)\n\t}\n\n\tt.subLock.Lock()\n\tfor _, ch := range t.subscribers {\n\t\t// We drain the description if there's one in the channel\n\t\tselect {\n\t\tcase <-ch:\n\t\tdefault:\n\t\t}\n\t\tch <- newDesc\n\t}\n\tt.subLock.Unlock()\n\n\treturn true\n}\n\n// apply updates the Topology and its underlying FSM based on the provided server description and returns the server\n// description that should be stored.\nfunc (t *Topology) apply(ctx context.Context, desc description.Server) description.Server {\n\tt.serversLock.Lock()\n\tdefer t.serversLock.Unlock()\n\n\tind, ok := t.fsm.findServer(desc.Addr)\n\tif t.serversClosed || !ok {\n\t\treturn desc\n\t}\n\n\tprev := t.fsm.Topology\n\toldDesc := t.fsm.Servers[ind]\n\tif driverutil.CompareTopologyVersions(oldDesc.TopologyVersion, desc.TopologyVersion) > 0 {\n\t\treturn oldDesc\n\t}\n\n\tvar current description.Topology\n\tcurrent, desc = t.fsm.apply(desc)\n\n\tif !driverutil.EqualServers(oldDesc, desc) {\n\t\tt.publishServerDescriptionChangedEvent(oldDesc, desc)\n\t}\n\n\tdiff := diffTopology(prev, current)\n\n\tfor _, removed := range diff.Removed {\n\t\tif s, ok := t.servers[removed.Addr]; ok {\n\t\t\tgo func() {\n\t\t\t\tcancelCtx, cancel := context.WithCancel(ctx)\n\t\t\t\tcancel()\n\t\t\t\t_ = s.Disconnect(cancelCtx)\n\t\t\t}()\n\t\t\tdelete(t.servers, removed.Addr)\n\t\t\tt.publishServerClosedEvent(s.address)\n\t\t}\n\t}\n\n\tfor _, added := range diff.Added {\n\t\t_ = t.addServer(added.Addr)\n\t}\n\n\tt.desc.Store(current)\n\tif !equalTopologies(prev, current) {\n\t\tt.publishTopologyDescriptionChangedEvent(prev, current)\n\t}\n\n\tt.subLock.Lock()\n\tfor _, ch := range t.subscribers {\n\t\t// We drain the description if there's one in the channel\n\t\tselect {\n\t\tcase <-ch:\n\t\tdefault:\n\t\t}\n\t\tch <- current\n\t}\n\tt.subLock.Unlock()\n\n\treturn desc\n}\n\nfunc (t *Topology) addServer(addr address.Address) error {\n\tif _, ok := t.servers[addr]; ok {\n\t\treturn nil\n\t}\n\n\tsvr, err := ConnectServer(addr, t.updateCallback, t.id, t.cfg.ConnectTimeout, t.cfg.ServerOpts...)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tt.servers[addr] = svr\n\n\treturn nil\n}\n\n// String implements the Stringer interface\nfunc (t *Topology) String() string {\n\tdesc := t.Description()\n\n\tserversStr := \"\"\n\tt.serversLock.Lock()\n\tdefer t.serversLock.Unlock()\n\tfor _, s := range t.servers {\n\t\tserversStr += \"{ \" + s.String() + \" }, \"\n\t}\n\treturn fmt.Sprintf(\"Type: %s, Servers: [%s]\", desc.Kind, serversStr)\n}\n\n// publishes a ServerDescriptionChangedEvent to indicate the server description has changed\nfunc (t *Topology) publishServerDescriptionChangedEvent(prev description.Server, current description.Server) {\n\tserverDescriptionChanged := &event.ServerDescriptionChangedEvent{\n\t\tAddress:             current.Addr,\n\t\tTopologyID:          t.id,\n\t\tPreviousDescription: newEventServerDescription(prev),\n\t\tNewDescription:      newEventServerDescription(current),\n\t}\n\n\tif t.cfg.ServerMonitor != nil && t.cfg.ServerMonitor.ServerDescriptionChanged != nil {\n\t\tt.cfg.ServerMonitor.ServerDescriptionChanged(serverDescriptionChanged)\n\t}\n}\n\n// publishes a ServerClosedEvent to indicate the server has closed\nfunc (t *Topology) publishServerClosedEvent(addr address.Address) {\n\tserverClosed := &event.ServerClosedEvent{\n\t\tAddress:    addr,\n\t\tTopologyID: t.id,\n\t}\n\n\tif t.cfg.ServerMonitor != nil && t.cfg.ServerMonitor.ServerClosed != nil {\n\t\tt.cfg.ServerMonitor.ServerClosed(serverClosed)\n\t}\n\n\tif mustLogTopologyMessage(t, logger.LevelDebug) {\n\t\tserverHost, serverPort, err := net.SplitHostPort(addr.String())\n\t\tif err != nil {\n\t\t\tserverHost = addr.String()\n\t\t\tserverPort = \"\"\n\t\t}\n\n\t\tportInt64, _ := strconv.ParseInt(serverPort, 10, 32)\n\n\t\tlogTopologyMessage(t, logger.LevelDebug, logger.TopologyServerClosed,\n\t\t\tlogger.KeyServerHost, serverHost,\n\t\t\tlogger.KeyServerPort, portInt64)\n\t}\n}\n\n// publishes a TopologyDescriptionChangedEvent to indicate the topology description has changed\nfunc (t *Topology) publishTopologyDescriptionChangedEvent(prev description.Topology, current description.Topology) {\n\ttopologyDescriptionChanged := &event.TopologyDescriptionChangedEvent{\n\t\tTopologyID:          t.id,\n\t\tPreviousDescription: newEventServerTopology(prev),\n\t\tNewDescription:      newEventServerTopology(current),\n\t}\n\n\tif t.cfg.ServerMonitor != nil && t.cfg.ServerMonitor.TopologyDescriptionChanged != nil {\n\t\tt.cfg.ServerMonitor.TopologyDescriptionChanged(topologyDescriptionChanged)\n\t}\n\n\tif mustLogTopologyMessage(t, logger.LevelDebug) {\n\t\tlogTopologyMessage(t, logger.LevelDebug, logger.TopologyDescriptionChanged,\n\t\t\tlogger.KeyPreviousDescription, prev.String(),\n\t\t\tlogger.KeyNewDescription, current.String())\n\t}\n}\n\n// publishes a TopologyOpeningEvent to indicate the topology is being initialized\nfunc (t *Topology) publishTopologyOpeningEvent() {\n\ttopologyOpening := &event.TopologyOpeningEvent{\n\t\tTopologyID: t.id,\n\t}\n\n\tif t.cfg.ServerMonitor != nil && t.cfg.ServerMonitor.TopologyOpening != nil {\n\t\tt.cfg.ServerMonitor.TopologyOpening(topologyOpening)\n\t}\n\n\tif mustLogTopologyMessage(t, logger.LevelDebug) {\n\t\tlogTopologyMessage(t, logger.LevelDebug, logger.TopologyOpening)\n\t}\n}\n\n// publishes a TopologyClosedEvent to indicate the topology has been closed\nfunc (t *Topology) publishTopologyClosedEvent() {\n\ttopologyClosed := &event.TopologyClosedEvent{\n\t\tTopologyID: t.id,\n\t}\n\n\tif t.cfg.ServerMonitor != nil && t.cfg.ServerMonitor.TopologyClosed != nil {\n\t\tt.cfg.ServerMonitor.TopologyClosed(topologyClosed)\n\t}\n\n\tif mustLogTopologyMessage(t, logger.LevelDebug) {\n\t\tlogTopologyMessage(t, logger.LevelDebug, logger.TopologyClosed)\n\t}\n}\n\n// GetServerSelectionTimeout returns the server selection timeout defined on\n// the client options.\nfunc (t *Topology) GetServerSelectionTimeout() time.Duration {\n\tif t.cfg == nil {\n\t\treturn 0\n\t}\n\n\treturn t.cfg.ServerSelectionTimeout\n}\n\nfunc newEventServerDescription(srv description.Server) event.ServerDescription {\n\tevtSrv := event.ServerDescription{\n\t\tAddr:                  srv.Addr,\n\t\tArbiters:              srv.Arbiters,\n\t\tCompression:           srv.Compression,\n\t\tCanonicalAddr:         srv.CanonicalAddr,\n\t\tElectionID:            srv.ElectionID,\n\t\tIsCryptd:              srv.IsCryptd,\n\t\tHelloOK:               srv.HelloOK,\n\t\tHosts:                 srv.Hosts,\n\t\tKind:                  srv.Kind.String(),\n\t\tLastWriteTime:         srv.LastWriteTime,\n\t\tMaxBatchCount:         srv.MaxBatchCount,\n\t\tMaxDocumentSize:       srv.MaxDocumentSize,\n\t\tMaxMessageSize:        srv.MaxMessageSize,\n\t\tMembers:               srv.Members,\n\t\tPassive:               srv.Passive,\n\t\tPassives:              srv.Passives,\n\t\tPrimary:               srv.Primary,\n\t\tReadOnly:              srv.ReadOnly,\n\t\tServiceID:             srv.ServiceID,\n\t\tSessionTimeoutMinutes: srv.SessionTimeoutMinutes,\n\t\tSetName:               srv.SetName,\n\t\tSetVersion:            srv.SetVersion,\n\t\tTags:                  srv.Tags,\n\t}\n\n\tif srv.WireVersion != nil {\n\t\tevtSrv.MaxWireVersion = srv.WireVersion.Max\n\t\tevtSrv.MinWireVersion = srv.WireVersion.Min\n\t}\n\n\tif srv.TopologyVersion != nil {\n\t\tevtSrv.TopologyVersionProcessID = srv.TopologyVersion.ProcessID\n\t\tevtSrv.TopologyVersionCounter = srv.TopologyVersion.Counter\n\t}\n\n\treturn evtSrv\n}\n\nfunc newEventServerTopology(topo description.Topology) event.TopologyDescription {\n\tevtSrvs := make([]event.ServerDescription, len(topo.Servers))\n\tfor idx, srv := range topo.Servers {\n\t\tevtSrvs[idx] = newEventServerDescription(srv)\n\t}\n\n\tevtTopo := event.TopologyDescription{\n\t\tServers:               evtSrvs,\n\t\tSetName:               topo.SetName,\n\t\tKind:                  topo.Kind.String(),\n\t\tSessionTimeoutMinutes: topo.SessionTimeoutMinutes,\n\t\tCompatibilityErr:      topo.CompatibilityErr,\n\t}\n\n\treturn evtTopo\n}\n"
  },
  {
    "path": "x/mongo/driver/topology/topology_errors_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\n//go:build go1.13\n\npackage topology\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"sync/atomic\"\n\t\"testing\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/require\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/serverselector\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/description\"\n)\n\nvar selectNone serverselector.Func = func(description.Topology, []description.Server) ([]description.Server, error) {\n\treturn []description.Server{}, nil\n}\n\nfunc TestTopologyErrors(t *testing.T) {\n\tt.Run(\"errors are wrapped\", func(t *testing.T) {\n\t\tt.Run(\"server selection error\", func(t *testing.T) {\n\t\t\ttopo, err := New(nil)\n\t\t\trequire.NoError(t, err)\n\n\t\t\tatomic.StoreInt64(&topo.state, topologyConnected)\n\t\t\tdesc := description.Topology{\n\t\t\t\tServers: []description.Server{},\n\t\t\t}\n\t\t\ttopo.desc.Store(desc)\n\n\t\t\tctx, cancel := context.WithCancel(context.Background())\n\t\t\tcancel()\n\t\t\t_, err = topo.SelectServer(ctx, &serverselector.Write{})\n\t\t\tassert.True(t, errors.Is(err, context.Canceled), \"expected error %v, got %v\", context.Canceled, err)\n\t\t})\n\t\tt.Run(\"context deadline error\", func(t *testing.T) {\n\t\t\ttopo, err := New(nil)\n\t\t\tassert.Nil(t, err, \"error creating topology: %v\", err)\n\n\t\t\tvar serverSelectionErr error\n\t\t\tcallback := func() bool {\n\t\t\t\tselectServerCtx, cancel := context.WithTimeout(context.Background(), 10*time.Millisecond)\n\t\t\t\tdefer cancel()\n\n\t\t\t\tsubCh := make(<-chan description.Topology)\n\t\t\t\t_, serverSelectionErr = topo.selectServerFromSubscription(selectServerCtx, subCh, selectNone)\n\t\t\t\treturn true\n\t\t\t}\n\t\t\tassert.Eventually(t,\n\t\t\t\tcallback,\n\t\t\t\t150*time.Millisecond,\n\t\t\t\ttime.Millisecond,\n\t\t\t\t\"expected context deadline to fail within 150ms\")\n\n\t\t\tassert.True(t, errors.Is(serverSelectionErr, context.DeadlineExceeded), \"expected %v, received %v\",\n\t\t\t\tcontext.DeadlineExceeded, serverSelectionErr)\n\t\t})\n\t})\n}\n"
  },
  {
    "path": "x/mongo/driver/topology/topology_options.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage topology\n\nimport (\n\t\"context\"\n\t\"crypto/tls\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"sync/atomic\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/event\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/logger\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/optionsutil\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/auth\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/description\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/ocsp\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/operation\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/session\"\n)\n\nconst (\n\tdefaultServerSelectionTimeout = 30 * time.Second\n\tdefaultConnectionTimeout      = 30 * time.Second\n)\n\n// Config is used to construct a topology.\ntype Config struct {\n\tMode                   MonitorMode\n\tReplicaSetName         string\n\tSeedList               []string\n\tServerOpts             []ServerOption\n\tURI                    string\n\tConnectTimeout         time.Duration\n\tTimeout                *time.Duration\n\tServerSelectionTimeout time.Duration\n\tServerMonitor          *event.ServerMonitor\n\tSRVMaxHosts            int\n\tSRVServiceName         string\n\tLoadBalanced           bool\n\tlogger                 *logger.Logger\n}\n\n// ConvertToDriverAPIOptions converts a given ServerAPIOptions object from the\n// options package to a ServerAPIOptions object from the driver package.\nfunc ConvertToDriverAPIOptions(opts *options.ServerAPIOptions) *driver.ServerAPIOptions {\n\tdriverOpts := driver.NewServerAPIOptions(string(opts.ServerAPIVersion))\n\tif opts.Strict != nil {\n\t\tdriverOpts.SetStrict(*opts.Strict)\n\t}\n\tif opts.DeprecationErrors != nil {\n\t\tdriverOpts.SetDeprecationErrors(*opts.DeprecationErrors)\n\t}\n\treturn driverOpts\n}\n\nfunc newLogger(opts *options.LoggerOptions) (*logger.Logger, error) {\n\tif opts == nil {\n\t\topts = options.Logger()\n\t}\n\n\tcomponentLevels := make(map[logger.Component]logger.Level)\n\tfor component, level := range opts.ComponentLevels {\n\t\tcomponentLevels[logger.Component(component)] = logger.Level(level)\n\t}\n\n\tlog, err := logger.New(opts.Sink, opts.MaxDocumentLength, componentLevels)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"error creating logger: %w\", err)\n\t}\n\n\treturn log, nil\n}\n\n// convertOIDCArgs converts the internal *driver.OIDCArgs into the equivalent\n// public type *options.OIDCArgs.\nfunc convertOIDCArgs(args *driver.OIDCArgs) *options.OIDCArgs {\n\tif args == nil {\n\t\treturn nil\n\t}\n\treturn &options.OIDCArgs{\n\t\tVersion:      args.Version,\n\t\tIDPInfo:      (*options.IDPInfo)(args.IDPInfo),\n\t\tRefreshToken: args.RefreshToken,\n\t}\n}\n\n// ConvertCreds takes an [options.Credential] and returns the equivalent\n// [driver.Cred].\nfunc ConvertCreds(cred *options.Credential) *driver.Cred {\n\tif cred == nil {\n\t\treturn nil\n\t}\n\n\tvar oidcMachineCallback auth.OIDCCallback\n\tif cred.OIDCMachineCallback != nil {\n\t\toidcMachineCallback = func(ctx context.Context, args *driver.OIDCArgs) (*driver.OIDCCredential, error) {\n\t\t\tcred, err := cred.OIDCMachineCallback(ctx, convertOIDCArgs(args))\n\t\t\treturn (*driver.OIDCCredential)(cred), err\n\t\t}\n\t}\n\n\tvar oidcHumanCallback auth.OIDCCallback\n\tif cred.OIDCHumanCallback != nil {\n\t\toidcHumanCallback = func(ctx context.Context, args *driver.OIDCArgs) (*driver.OIDCCredential, error) {\n\t\t\tcred, err := cred.OIDCHumanCallback(ctx, convertOIDCArgs(args))\n\t\t\treturn (*driver.OIDCCredential)(cred), err\n\t\t}\n\t}\n\n\treturn &auth.Cred{\n\t\tSource:              cred.AuthSource,\n\t\tUsername:            cred.Username,\n\t\tPassword:            cred.Password,\n\t\tPasswordSet:         cred.PasswordSet,\n\t\tProps:               cred.AuthMechanismProperties,\n\t\tOIDCMachineCallback: oidcMachineCallback,\n\t\tOIDCHumanCallback:   oidcHumanCallback,\n\t}\n}\n\n// NewConfig will translate data from client options into a topology config for\n// building non-default deployments. Server and topology options are not honored\n// if a custom deployment is used.\nfunc NewConfig(opts *options.ClientOptions, clock *session.ClusterClock) (*Config, error) {\n\tvar authenticator driver.Authenticator\n\tvar err error\n\tif opts.Auth != nil {\n\t\tauthenticator, err = auth.CreateAuthenticator(\n\t\t\topts.Auth.AuthMechanism,\n\t\t\tConvertCreds(opts.Auth),\n\t\t\topts.HTTPClient,\n\t\t)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"error creating authenticator: %w\", err)\n\t\t}\n\t}\n\treturn NewAuthenticatorConfig(authenticator,\n\t\tWithAuthConfigClock(clock),\n\t\tWithAuthConfigClientOptions(opts),\n\t)\n}\n\ntype authConfigOptions struct {\n\tclock      *session.ClusterClock\n\topts       *options.ClientOptions\n\tdriverInfo *atomic.Pointer[options.DriverInfo]\n}\n\n// AuthConfigOption is a function that configures authConfigOptions.\ntype AuthConfigOption func(*authConfigOptions)\n\n// WithAuthConfigClock sets the cluster clock in authConfigOptions.\nfunc WithAuthConfigClock(clock *session.ClusterClock) AuthConfigOption {\n\treturn func(co *authConfigOptions) {\n\t\tco.clock = clock\n\t}\n}\n\n// WithAuthConfigClientOptions sets the client options in authConfigOptions.\nfunc WithAuthConfigClientOptions(opts *options.ClientOptions) AuthConfigOption {\n\treturn func(co *authConfigOptions) {\n\t\tco.opts = opts\n\t}\n}\n\n// WithAuthConfigDriverInfo sets the driver info in authConfigOptions.\nfunc WithAuthConfigDriverInfo(driverInfo *atomic.Pointer[options.DriverInfo]) AuthConfigOption {\n\treturn func(co *authConfigOptions) {\n\t\tco.driverInfo = driverInfo\n\t}\n}\n\n// NewAuthenticatorConfig will translate data from client options into a\n// topology config for building non-default deployments. Server and topology\n// options are not honored if a custom deployment is used. It uses a passed in\n// authenticator to authenticate the connection.\nfunc NewAuthenticatorConfig(authenticator driver.Authenticator, clientOpts ...AuthConfigOption) (*Config, error) {\n\tsettings := authConfigOptions{}\n\tfor _, apply := range clientOpts {\n\t\tapply(&settings)\n\t}\n\n\topts := settings.opts\n\tclock := settings.clock\n\tdriverInfo := settings.driverInfo\n\n\tvar serverAPI *driver.ServerAPIOptions\n\n\tif err := opts.Validate(); err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar connOpts []ConnectionOption\n\tvar serverOpts []ServerOption\n\n\tcfgp := &Config{\n\t\tTimeout: opts.Timeout,\n\t}\n\n\t// Set the default \"ServerSelectionTimeout\" to 30 seconds.\n\tcfgp.ServerSelectionTimeout = defaultServerSelectionTimeout\n\n\t// Set the default \"ConnectionTimeout\" to 30 seconds.\n\tcfgp.ConnectTimeout = defaultConnectionTimeout\n\n\t// Set the default \"SeedList\" to localhost.\n\tcfgp.SeedList = []string{\"localhost:27017\"}\n\n\t// TODO(GODRIVER-814): Add tests for topology, server, and connection related options.\n\n\t// ServerAPIOptions need to be handled early as other client and server options below reference\n\t// c.serverAPI and serverOpts.serverAPI.\n\tif opts.ServerAPIOptions != nil {\n\t\tserverAPI = ConvertToDriverAPIOptions(opts.ServerAPIOptions)\n\t\tserverOpts = append(serverOpts, WithServerAPI(func(*driver.ServerAPIOptions) *driver.ServerAPIOptions {\n\t\t\treturn serverAPI\n\t\t}))\n\t}\n\n\tcfgp.URI = opts.GetURI()\n\n\tif opts.SRVServiceName != nil {\n\t\tcfgp.SRVServiceName = *opts.SRVServiceName\n\t}\n\n\tif opts.SRVMaxHosts != nil {\n\t\tcfgp.SRVMaxHosts = *opts.SRVMaxHosts\n\t}\n\n\t// AppName\n\tvar appName string\n\tif opts.AppName != nil {\n\t\tappName = *opts.AppName\n\n\t\tserverOpts = append(serverOpts, WithServerAppName(func(string) string {\n\t\t\treturn appName\n\t\t}))\n\t}\n\n\tif driverInfo != nil {\n\t\tserverOpts = append(serverOpts, WithDriverInfo(driverInfo))\n\t}\n\n\t// Compressors & ZlibLevel\n\tvar comps []string\n\tif len(opts.Compressors) > 0 {\n\t\tcomps = opts.Compressors\n\n\t\tconnOpts = append(connOpts, WithCompressors(\n\t\t\tfunc(compressors []string) []string {\n\t\t\t\treturn append(compressors, comps...)\n\t\t\t},\n\t\t))\n\n\t\tfor _, comp := range comps {\n\t\t\tswitch comp {\n\t\t\tcase \"zlib\":\n\t\t\t\tconnOpts = append(connOpts, WithZlibLevel(func(*int) *int {\n\t\t\t\t\treturn opts.ZlibLevel\n\t\t\t\t}))\n\t\t\tcase \"zstd\":\n\t\t\t\tconnOpts = append(connOpts, WithZstdLevel(func(*int) *int {\n\t\t\t\t\treturn opts.ZstdLevel\n\t\t\t\t}))\n\t\t\t}\n\t\t}\n\n\t\tserverOpts = append(serverOpts, WithCompressionOptions(\n\t\t\tfunc(opts ...string) []string { return append(opts, comps...) },\n\t\t))\n\t}\n\n\tvar loadBalanced bool\n\tif opts.LoadBalanced != nil {\n\t\tloadBalanced = *opts.LoadBalanced\n\t}\n\n\t// Handshaker\n\tvar handshaker func(driver.Handshaker) driver.Handshaker\n\tif authenticator != nil {\n\t\thandshaker = func(driver.Handshaker) driver.Handshaker {\n\t\t\thandshakeOpts := &auth.HandshakeOptions{\n\t\t\t\tAppName:       appName,\n\t\t\t\tAuthenticator: authenticator,\n\t\t\t\tCompressors:   comps,\n\t\t\t\tServerAPI:     serverAPI,\n\t\t\t\tLoadBalanced:  loadBalanced,\n\t\t\t\tClusterClock:  clock,\n\t\t\t}\n\n\t\t\tif opts.Auth.AuthMechanism == \"\" {\n\t\t\t\t// Required for SASL mechanism negotiation during handshake\n\t\t\t\thandshakeOpts.DBUser = opts.Auth.AuthSource + \".\" + opts.Auth.Username\n\t\t\t}\n\n\t\t\tif auth, ok := optionsutil.Value(opts.Custom, \"authenticateToAnything\").(bool); ok && auth {\n\t\t\t\t// Authenticate arbiters\n\t\t\t\thandshakeOpts.PerformAuthentication = func(_ description.Server) bool {\n\t\t\t\t\treturn true\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif driverInfo != nil {\n\t\t\t\tif di := driverInfo.Load(); di != nil {\n\t\t\t\t\thandshakeOpts.OuterLibraryName = di.Name\n\t\t\t\t\thandshakeOpts.OuterLibraryVersion = di.Version\n\t\t\t\t\thandshakeOpts.OuterLibraryPlatform = di.Platform\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn auth.Handshaker(nil, handshakeOpts)\n\t\t}\n\t} else {\n\t\thandshaker = func(driver.Handshaker) driver.Handshaker {\n\t\t\top := operation.NewHello().\n\t\t\t\tAppName(appName).\n\t\t\t\tCompressors(comps).\n\t\t\t\tClusterClock(clock).\n\t\t\t\tServerAPI(serverAPI).\n\t\t\t\tLoadBalanced(loadBalanced)\n\n\t\t\tif driverInfo != nil {\n\t\t\t\tif di := driverInfo.Load(); di != nil {\n\t\t\t\t\top = op.OuterLibraryName(di.Name).\n\t\t\t\t\t\tOuterLibraryVersion(di.Version).\n\t\t\t\t\t\tOuterLibraryPlatform(di.Platform)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn op\n\t\t}\n\t}\n\n\tconnOpts = append(connOpts, WithHandshaker(handshaker))\n\n\t// Dialer\n\tif opts.Dialer != nil {\n\t\tconnOpts = append(connOpts, WithDialer(\n\t\t\tfunc(Dialer) Dialer { return opts.Dialer },\n\t\t))\n\t}\n\t// Direct\n\tif opts.Direct != nil && *opts.Direct {\n\t\tcfgp.Mode = SingleMode\n\t}\n\n\t// HeartbeatInterval\n\tif opts.HeartbeatInterval != nil {\n\t\tserverOpts = append(serverOpts, WithHeartbeatInterval(\n\t\t\tfunc(time.Duration) time.Duration { return *opts.HeartbeatInterval },\n\t\t))\n\t}\n\t// Hosts\n\tcfgp.SeedList = []string{\"localhost:27017\"} // default host\n\tif len(opts.Hosts) > 0 {\n\t\tcfgp.SeedList = opts.Hosts\n\t}\n\n\t// MaxConIdleTime\n\tif opts.MaxConnIdleTime != nil {\n\t\tserverOpts = append(serverOpts, WithConnectionPoolMaxIdleTime(\n\t\t\tfunc(time.Duration) time.Duration { return *opts.MaxConnIdleTime },\n\t\t))\n\t}\n\t// MaxPoolSize\n\tif opts.MaxPoolSize != nil {\n\t\tserverOpts = append(\n\t\t\tserverOpts,\n\t\t\tWithMaxConnections(func(uint64) uint64 { return *opts.MaxPoolSize }),\n\t\t)\n\t}\n\t// MinPoolSize\n\tif opts.MinPoolSize != nil {\n\t\tserverOpts = append(\n\t\t\tserverOpts,\n\t\t\tWithMinConnections(func(uint64) uint64 { return *opts.MinPoolSize }),\n\t\t)\n\t}\n\t// MaxConnecting\n\tif opts.MaxConnecting != nil {\n\t\tserverOpts = append(\n\t\t\tserverOpts,\n\t\t\tWithMaxConnecting(func(uint64) uint64 { return *opts.MaxConnecting }),\n\t\t)\n\t}\n\t// PoolMonitor\n\tif opts.PoolMonitor != nil {\n\t\tserverOpts = append(\n\t\t\tserverOpts,\n\t\t\tWithConnectionPoolMonitor(func(*event.PoolMonitor) *event.PoolMonitor { return opts.PoolMonitor }),\n\t\t)\n\t}\n\t// Monitor\n\tif opts.Monitor != nil {\n\t\tconnOpts = append(connOpts, WithMonitor(\n\t\t\tfunc(*event.CommandMonitor) *event.CommandMonitor { return opts.Monitor },\n\t\t))\n\t}\n\t// ServerMonitor\n\tif opts.ServerMonitor != nil {\n\t\tserverOpts = append(\n\t\t\tserverOpts,\n\t\t\tWithServerMonitor(func(*event.ServerMonitor) *event.ServerMonitor { return opts.ServerMonitor }),\n\t\t)\n\t\tcfgp.ServerMonitor = opts.ServerMonitor\n\t}\n\t// ReplicaSet\n\tif opts.ReplicaSet != nil {\n\t\tcfgp.ReplicaSetName = *opts.ReplicaSet\n\t}\n\t// ServerSelectionTimeout\n\tif opts.ServerSelectionTimeout != nil {\n\t\tcfgp.ServerSelectionTimeout = *opts.ServerSelectionTimeout\n\t}\n\t// ConnectionTimeout\n\tif opts.ConnectTimeout != nil {\n\t\tcfgp.ConnectTimeout = *opts.ConnectTimeout\n\t}\n\t// TLSConfig\n\tif opts.TLSConfig != nil {\n\t\tconnOpts = append(connOpts, WithTLSConfig(\n\t\t\tfunc(*tls.Config) *tls.Config {\n\t\t\t\treturn opts.TLSConfig\n\t\t\t},\n\t\t))\n\t}\n\n\t// HTTP Client\n\tif opts.HTTPClient != nil {\n\t\tconnOpts = append(connOpts, WithHTTPClient(\n\t\t\tfunc(*http.Client) *http.Client {\n\t\t\t\treturn opts.HTTPClient\n\t\t\t},\n\t\t))\n\t}\n\n\t// OCSP cache\n\tocspCache := ocsp.NewCache()\n\tconnOpts = append(\n\t\tconnOpts,\n\t\tWithOCSPCache(func(ocsp.Cache) ocsp.Cache { return ocspCache }),\n\t)\n\n\t// Disable communication with external OCSP responders.\n\tif opts.DisableOCSPEndpointCheck != nil {\n\t\tconnOpts = append(\n\t\t\tconnOpts,\n\t\t\tWithDisableOCSPEndpointCheck(func(bool) bool { return *opts.DisableOCSPEndpointCheck }),\n\t\t)\n\t}\n\n\t// LoadBalanced\n\tif opts.LoadBalanced != nil {\n\t\tcfgp.LoadBalanced = *opts.LoadBalanced\n\n\t\tserverOpts = append(\n\t\t\tserverOpts,\n\t\t\tWithServerLoadBalanced(func(bool) bool { return *opts.LoadBalanced }),\n\t\t)\n\t\tconnOpts = append(\n\t\t\tconnOpts,\n\t\t\tWithConnectionLoadBalanced(func(bool) bool { return *opts.LoadBalanced }),\n\t\t)\n\t}\n\n\tlgr, err := newLogger(opts.LoggerOptions)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tserverOpts = append(\n\t\tserverOpts,\n\t\twithLogger(func() *logger.Logger { return lgr }),\n\t\twithServerMonitoringMode(opts.ServerMonitoringMode),\n\t)\n\n\tcfgp.logger = lgr\n\n\tserverOpts = append(\n\t\tserverOpts,\n\t\tWithClock(func(*session.ClusterClock) *session.ClusterClock { return clock }),\n\t\tWithConnectionOptions(func(...ConnectionOption) []ConnectionOption { return connOpts }))\n\n\tcfgp.ServerOpts = serverOpts\n\n\treturn cfgp, nil\n}\n"
  },
  {
    "path": "x/mongo/driver/topology/topology_options_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage topology\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"net/url\"\n\t\"reflect\"\n\t\"testing\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/require\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/description\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/drivertest\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/mnet\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/xoptions\"\n)\n\nfunc TestDirectConnectionFromConnString(t *testing.T) {\n\ttype testCaseQuery [][2]string\n\n\ttestCases := []struct {\n\t\tname  string\n\t\tmode  MonitorMode\n\t\tquery *testCaseQuery\n\t}{\n\t\t{\"connect=direct\", SingleMode, &testCaseQuery{{\"connect\", \"direct\"}}},\n\t\t{\"connect=automatic\", AutomaticMode, &testCaseQuery{{\"connect\", \"automatic\"}}},\n\t\t{\"directConnection=true\", SingleMode, &testCaseQuery{{\"directconnection\", \"true\"}}},\n\t\t{\"directconnection=false\", AutomaticMode, &testCaseQuery{{\"directconnection\", \"false\"}}},\n\t\t{\"default\", AutomaticMode, nil},\n\t}\n\tfor _, tc := range testCases {\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\tdns, err := url.Parse(\"mongodb://localhost/\")\n\t\t\tassert.Nil(t, err, \"error parsing uri: %v\", err)\n\n\t\t\tif tc.query != nil {\n\t\t\t\tquery := dns.Query()\n\t\t\t\tfor _, q := range *tc.query {\n\t\t\t\t\tquery.Set(q[0], q[1])\n\t\t\t\t}\n\t\t\t\tdns.RawQuery = query.Encode()\n\t\t\t}\n\n\t\t\tcfg, err := NewConfig(options.Client().ApplyURI(dns.String()), nil)\n\t\t\tassert.Nil(t, err, \"error constructing topology config: %v\", err)\n\n\t\t\ttopo, err := New(cfg)\n\t\t\tassert.Nil(t, err, \"error constructing topology: %v\", err)\n\t\t\tassert.Equal(t, tc.mode, topo.cfg.Mode, \"expected mode %v, got %v\", tc.mode, topo.cfg.Mode)\n\t\t})\n\t}\n}\n\nfunc TestLoadBalancedFromConnString(t *testing.T) {\n\ttestCases := []struct {\n\t\tname         string\n\t\turiOptions   string\n\t\tloadBalanced bool\n\t}{\n\t\t{\"loadBalanced=true\", \"loadBalanced=true\", true},\n\t\t{\"loadBalanced=false\", \"loadBalanced=false\", false},\n\t\t{\"loadBalanced unset\", \"\", false},\n\t}\n\tfor _, tc := range testCases {\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\turi := fmt.Sprintf(\"mongodb://localhost/?%s\", tc.uriOptions)\n\t\t\tcfg, err := NewConfig(options.Client().ApplyURI(uri), nil)\n\t\t\tassert.Nil(t, err, \"error constructing topology config: %v\", err)\n\n\t\t\ttopo, err := New(cfg)\n\t\t\tassert.Nil(t, err, \"topology.New error: %v\", err)\n\t\t\tassert.Equal(t, tc.loadBalanced, topo.cfg.LoadBalanced, \"expected loadBalanced %v, got %v\", tc.loadBalanced, topo.cfg.LoadBalanced)\n\n\t\t\tsrvr := NewServer(\"\", topo.id, defaultConnectionTimeout, topo.cfg.ServerOpts...)\n\t\t\tassert.Equal(t, tc.loadBalanced, srvr.cfg.loadBalanced, \"expected loadBalanced %v, got %v\", tc.loadBalanced, srvr.cfg.loadBalanced)\n\n\t\t\tconn := newConnection(\"\", srvr.cfg.connectionOpts...)\n\t\t\tassert.Equal(t, tc.loadBalanced, conn.config.loadBalanced, \"expected loadBalanced %v, got %v\", tc.loadBalanced, conn.config.loadBalanced)\n\t\t})\n\t}\n}\n\ntype testAuthenticator struct{}\n\nvar _ driver.Authenticator = &testAuthenticator{}\n\nfunc (a *testAuthenticator) Auth(context.Context, *driver.AuthConfig) error {\n\treturn fmt.Errorf(\"test error\")\n}\n\nfunc (a *testAuthenticator) Reauth(context.Context, *driver.AuthConfig) error {\n\treturn nil\n}\n\nfunc TestAuthenticateToAnything(t *testing.T) {\n\tt.Parallel()\n\n\tcases := []struct {\n\t\tname    string\n\t\tset     func(*options.ClientOptions) error\n\t\trequire func(*testing.T, error)\n\t}{\n\t\t{\n\t\t\tname: \"default\",\n\t\t\tset:  func(*options.ClientOptions) error { return nil },\n\t\t\trequire: func(t *testing.T, err error) {\n\t\t\t\trequire.NoError(t, err)\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"positive\",\n\t\t\tset: func(opt *options.ClientOptions) error {\n\t\t\t\treturn xoptions.SetInternalClientOptions(opt, \"authenticateToAnything\", true)\n\t\t\t},\n\t\t\trequire: func(t *testing.T, err error) {\n\t\t\t\trequire.EqualError(t, err, \"auth error: test error\")\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"negative\",\n\t\t\tset: func(opt *options.ClientOptions) error {\n\t\t\t\treturn xoptions.SetInternalClientOptions(opt, \"authenticateToAnything\", false)\n\t\t\t},\n\t\t\trequire: func(t *testing.T, err error) {\n\t\t\t\trequire.NoError(t, err)\n\t\t\t},\n\t\t},\n\t}\n\n\tdescriber := &drivertest.ChannelConn{\n\t\tDesc: description.Server{Kind: description.ServerKindRSArbiter},\n\t}\n\tfor _, tc := range cases {\n\t\ttc := tc\n\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\topt := options.Client().SetAuth(options.Credential{Username: \"foo\", Password: \"bar\"})\n\t\t\terr := tc.set(opt)\n\t\t\trequire.NoError(t, err, \"error setting authenticateToAnything: %v\", err)\n\t\t\tcfg, err := NewAuthenticatorConfig(&testAuthenticator{}, WithAuthConfigClientOptions(opt))\n\t\t\trequire.NoError(t, err, \"error constructing topology config: %v\", err)\n\n\t\t\tsrvrCfg := newServerConfig(defaultConnectionTimeout, cfg.ServerOpts...)\n\t\t\tconnCfg := newConnectionConfig(srvrCfg.connectionOpts...)\n\t\t\terr = connCfg.handshaker.FinishHandshake(context.TODO(), &mnet.Connection{Describer: describer})\n\t\t\ttc.require(t, err)\n\t\t})\n\t}\n}\n\nfunc TestTopologyNewConfig(t *testing.T) {\n\tt.Run(\"default ServerSelectionTimeout\", func(t *testing.T) {\n\t\tcfg, err := NewConfig(options.Client(), nil)\n\t\tassert.Nil(t, err, \"error constructing topology config: %v\", err)\n\t\tassert.Equal(t, defaultServerSelectionTimeout, cfg.ServerSelectionTimeout)\n\t})\n\tt.Run(\"non-default ServerSelectionTimeout\", func(t *testing.T) {\n\t\tcfg, err := NewConfig(options.Client().SetServerSelectionTimeout(1), nil)\n\t\tassert.Nil(t, err, \"error constructing topology config: %v\", err)\n\t\tassert.Equal(t, time.Duration(1), cfg.ServerSelectionTimeout)\n\t})\n\tt.Run(\"default SeedList\", func(t *testing.T) {\n\t\tcfg, err := NewConfig(options.Client(), nil)\n\t\tassert.Nil(t, err, \"error constructing topology config: %v\", err)\n\t\tassert.Equal(t, []string{\"localhost:27017\"}, cfg.SeedList)\n\t})\n\tt.Run(\"non-default SeedList\", func(t *testing.T) {\n\t\tcfg, err := NewConfig(options.Client().ApplyURI(\"mongodb://localhost:27018\"), nil)\n\t\tassert.Nil(t, err, \"error constructing topology config: %v\", err)\n\t\tassert.Equal(t, []string{\"localhost:27018\"}, cfg.SeedList)\n\t})\n}\n\n// Test that convertOIDCArgs exhaustively copies all fields of a driver.OIDCArgs\n// into an options.OIDCArgs.\nfunc TestConvertOIDCArgs(t *testing.T) {\n\tt.Parallel()\n\trefreshToken := \"test refresh token\"\n\n\ttestCases := []struct {\n\t\tdesc string\n\t\targs *driver.OIDCArgs\n\t}{\n\t\t{\n\t\t\tdesc: \"populated args\",\n\t\t\targs: &driver.OIDCArgs{\n\t\t\t\tVersion: 9,\n\t\t\t\tIDPInfo: &driver.IDPInfo{\n\t\t\t\t\tIssuer:        \"test issuer\",\n\t\t\t\t\tClientID:      \"test client ID\",\n\t\t\t\t\tRequestScopes: []string{\"test scope 1\", \"test scope 2\"},\n\t\t\t\t},\n\t\t\t\tRefreshToken: &refreshToken,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tdesc: \"nil\",\n\t\t\targs: nil,\n\t\t},\n\t\t{\n\t\t\tdesc: \"nil IDPInfo and RefreshToken\",\n\t\t\targs: &driver.OIDCArgs{\n\t\t\t\tVersion:      9,\n\t\t\t\tIDPInfo:      nil,\n\t\t\t\tRefreshToken: nil,\n\t\t\t},\n\t\t},\n\t}\n\n\tfor _, tc := range testCases {\n\t\ttc := tc // Capture range variable.\n\n\t\tt.Run(tc.desc, func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tgot := convertOIDCArgs(tc.args)\n\n\t\t\tif tc.args == nil {\n\t\t\t\tassert.Nil(t, got, \"expected nil when input is nil\")\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\trequire.Equal(t,\n\t\t\t\t3,\n\t\t\t\treflect.ValueOf(*tc.args).NumField(),\n\t\t\t\t\"expected the driver.OIDCArgs struct to have exactly 3 fields\")\n\t\t\trequire.Equal(t,\n\t\t\t\t3,\n\t\t\t\treflect.ValueOf(*got).NumField(),\n\t\t\t\t\"expected the options.OIDCArgs struct to have exactly 3 fields\")\n\n\t\t\tassert.Equal(t,\n\t\t\t\ttc.args.Version,\n\t\t\t\tgot.Version,\n\t\t\t\t\"expected Version field to be equal\")\n\t\t\tassert.EqualValues(t,\n\t\t\t\ttc.args.IDPInfo,\n\t\t\t\tgot.IDPInfo,\n\t\t\t\t\"expected IDPInfo field to be convertible to equal values\")\n\t\t\tassert.Equal(t,\n\t\t\t\ttc.args.RefreshToken,\n\t\t\t\tgot.RefreshToken,\n\t\t\t\t\"expected RefreshToken field to be equal\")\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "x/mongo/driver/topology/topology_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2017-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage topology\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io/ioutil\"\n\t\"path\"\n\t\"sync/atomic\"\n\t\"testing\"\n\t\"time\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/require\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/serverselector\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/spectest\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/address\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/readpref\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/description\"\n)\n\nconst testTimeout = 2 * time.Second\n\nfunc compareErrors(err1, err2 error) bool {\n\tif err1 == nil && err2 == nil {\n\t\treturn true\n\t}\n\n\tif err1 == nil || err2 == nil {\n\t\treturn false\n\t}\n\n\tif err1.Error() != err2.Error() {\n\t\treturn false\n\t}\n\n\treturn true\n}\n\nvar _ description.ServerSelector = &mockServerSelector{}\n\ntype mockServerSelector struct {\n\tselectServerCalls atomic.Int64\n}\n\nfunc (m *mockServerSelector) SelectServer(description.Topology, []description.Server) ([]description.Server, error) {\n\tm.selectServerCalls.Add(1)\n\n\treturn nil, nil\n}\n\nfunc TestServerSelection(t *testing.T) {\n\tvar selectFirst serverselector.Func = func(_ description.Topology, candidates []description.Server) ([]description.Server, error) {\n\t\tif len(candidates) == 0 {\n\t\t\treturn []description.Server{}, nil\n\t\t}\n\t\treturn candidates[0:1], nil\n\t}\n\tvar selectNone serverselector.Func = func(description.Topology, []description.Server) ([]description.Server, error) {\n\t\treturn []description.Server{}, nil\n\t}\n\n\tt.Run(\"Success\", func(t *testing.T) {\n\t\ttopo, err := New(nil)\n\t\trequire.NoError(t, err)\n\t\tdesc := description.Topology{\n\t\t\tServers: []description.Server{\n\t\t\t\t{Addr: address.Address(\"one\"), Kind: description.ServerKindStandalone},\n\t\t\t\t{Addr: address.Address(\"two\"), Kind: description.ServerKindStandalone},\n\t\t\t\t{Addr: address.Address(\"three\"), Kind: description.ServerKindStandalone},\n\t\t\t},\n\t\t}\n\t\tsubCh := make(chan description.Topology, 1)\n\t\tsubCh <- desc\n\n\t\tsrvs, err := topo.selectServerFromSubscription(context.Background(), subCh, selectFirst)\n\t\trequire.NoError(t, err)\n\t\tif len(srvs) != 1 {\n\t\t\tt.Errorf(\"Incorrect number of descriptions returned. got %d; want %d\", len(srvs), 1)\n\t\t}\n\t\tif srvs[0].Addr != desc.Servers[0].Addr {\n\t\t\tt.Errorf(\"Incorrect sever selected. got %s; want %s\", srvs[0].Addr, desc.Servers[0].Addr)\n\t\t}\n\t})\n\tt.Run(\"Compatibility Error Min Version Too High\", func(t *testing.T) {\n\t\ttopo, err := New(nil)\n\t\trequire.NoError(t, err)\n\t\tdesc := description.Topology{\n\t\t\tKind: description.TopologyKindSingle,\n\t\t\tServers: []description.Server{\n\t\t\t\t{Addr: address.Address(\"one:27017\"), Kind: description.ServerKindStandalone, WireVersion: &description.VersionRange{Max: 11, Min: 11}},\n\t\t\t\t{Addr: address.Address(\"two:27017\"), Kind: description.ServerKindStandalone, WireVersion: &description.VersionRange{Max: 9, Min: 6}},\n\t\t\t\t{Addr: address.Address(\"three:27017\"), Kind: description.ServerKindStandalone, WireVersion: &description.VersionRange{Max: 9, Min: 6}},\n\t\t\t},\n\t\t}\n\t\twant := fmt.Errorf(\n\t\t\t\"server at %s requires wire version %d, but this version of the Go driver only supports up to %d\",\n\t\t\tdesc.Servers[0].Addr.String(),\n\t\t\tdesc.Servers[0].WireVersion.Min,\n\t\t\tSupportedWireVersions.Max,\n\t\t)\n\t\tdesc.CompatibilityErr = want\n\t\tatomic.StoreInt64(&topo.state, topologyConnected)\n\t\ttopo.desc.Store(desc)\n\t\t_, err = topo.SelectServer(context.Background(), selectFirst)\n\t\tassert.Equal(t, err, want, \"expected %v, got %v\", want, err)\n\t})\n\tt.Run(\"Compatibility Error Max Version Too Low\", func(t *testing.T) {\n\t\ttopo, err := New(nil)\n\t\trequire.NoError(t, err)\n\t\tdesc := description.Topology{\n\t\t\tKind: description.TopologyKindSingle,\n\t\t\tServers: []description.Server{\n\t\t\t\t{Addr: address.Address(\"one:27017\"), Kind: description.ServerKindStandalone, WireVersion: &description.VersionRange{Max: 21, Min: 6}},\n\t\t\t\t{Addr: address.Address(\"two:27017\"), Kind: description.ServerKindStandalone, WireVersion: &description.VersionRange{Max: 9, Min: 2}},\n\t\t\t\t{Addr: address.Address(\"three:27017\"), Kind: description.ServerKindStandalone, WireVersion: &description.VersionRange{Max: 9, Min: 2}},\n\t\t\t},\n\t\t}\n\t\twant := fmt.Errorf(\n\t\t\t\"server at %s reports wire version %d, but this version of the Go driver requires \"+\n\t\t\t\t\"at least 8 (MongoDB 4.2)\",\n\t\t\tdesc.Servers[0].Addr.String(),\n\t\t\tdesc.Servers[0].WireVersion.Max,\n\t\t)\n\t\tdesc.CompatibilityErr = want\n\t\tatomic.StoreInt64(&topo.state, topologyConnected)\n\t\ttopo.desc.Store(desc)\n\t\t_, err = topo.SelectServer(context.Background(), selectFirst)\n\t\tassert.Equal(t, err, want, \"expected %v, got %v\", want, err)\n\t})\n\tt.Run(\"Updated\", func(t *testing.T) {\n\t\ttopo, err := New(nil)\n\t\trequire.NoError(t, err)\n\t\tdesc := description.Topology{Servers: []description.Server{}}\n\t\tsubCh := make(chan description.Topology, 1)\n\t\tsubCh <- desc\n\n\t\tresp := make(chan []description.Server)\n\t\tgo func() {\n\t\t\tsrvs, err := topo.selectServerFromSubscription(context.Background(), subCh, selectFirst)\n\t\t\trequire.NoError(t, err)\n\t\t\tresp <- srvs\n\t\t}()\n\n\t\tdesc = description.Topology{\n\t\t\tServers: []description.Server{\n\t\t\t\t{Addr: address.Address(\"one\"), Kind: description.ServerKindStandalone},\n\t\t\t\t{Addr: address.Address(\"two\"), Kind: description.ServerKindStandalone},\n\t\t\t\t{Addr: address.Address(\"three\"), Kind: description.ServerKindStandalone},\n\t\t\t},\n\t\t}\n\t\tselect {\n\t\tcase subCh <- desc:\n\t\tcase <-time.After(100 * time.Millisecond):\n\t\t\tt.Error(\"Timed out while trying to send topology description\")\n\t\t}\n\n\t\tvar srvs []description.Server\n\t\tselect {\n\t\tcase srvs = <-resp:\n\t\tcase <-time.After(100 * time.Millisecond):\n\t\t\tt.Errorf(\"Timed out while trying to retrieve selected servers\")\n\t\t}\n\n\t\tif len(srvs) != 1 {\n\t\t\tt.Errorf(\"Incorrect number of descriptions returned. got %d; want %d\", len(srvs), 1)\n\t\t}\n\t\tif srvs[0].Addr != desc.Servers[0].Addr {\n\t\t\tt.Errorf(\"Incorrect sever selected. got %s; want %s\", srvs[0].Addr, desc.Servers[0].Addr)\n\t\t}\n\t})\n\tt.Run(\"Cancel\", func(t *testing.T) {\n\t\tdesc := description.Topology{\n\t\t\tServers: []description.Server{\n\t\t\t\t{Addr: address.Address(\"one\"), Kind: description.ServerKindStandalone},\n\t\t\t\t{Addr: address.Address(\"two\"), Kind: description.ServerKindStandalone},\n\t\t\t\t{Addr: address.Address(\"three\"), Kind: description.ServerKindStandalone},\n\t\t\t},\n\t\t}\n\t\ttopo, err := New(nil)\n\t\trequire.NoError(t, err)\n\t\tsubCh := make(chan description.Topology, 1)\n\t\tsubCh <- desc\n\t\tresp := make(chan error)\n\t\tctx, cancel := context.WithCancel(context.Background())\n\t\tgo func() {\n\t\t\t_, err := topo.selectServerFromSubscription(ctx, subCh, selectNone)\n\t\t\tresp <- err\n\t\t}()\n\n\t\tselect {\n\t\tcase err := <-resp:\n\t\t\tt.Errorf(\"Received error from server selection too soon: %v\", err)\n\t\tcase <-time.After(100 * time.Millisecond):\n\t\t}\n\n\t\tcancel()\n\n\t\tselect {\n\t\tcase err = <-resp:\n\t\tcase <-time.After(100 * time.Millisecond):\n\t\t\tt.Errorf(\"Timed out while trying to retrieve selected servers\")\n\t\t}\n\n\t\twant := ServerSelectionError{Wrapped: context.Canceled, Desc: desc}\n\t\tassert.Equal(t, err, want, \"Incorrect error received. got %v; want %v\", err, want)\n\t})\n\tt.Run(\"findServer returns topology kind\", func(t *testing.T) {\n\t\ttopo, err := New(nil)\n\t\trequire.NoError(t, err)\n\t\tatomic.StoreInt64(&topo.state, topologyConnected)\n\t\tsrvr, err := ConnectServer(address.Address(\"one\"), topo.updateCallback, topo.id, defaultConnectionTimeout)\n\t\trequire.NoError(t, err)\n\t\ttopo.servers[address.Address(\"one\")] = srvr\n\t\tdesc := topo.desc.Load().(description.Topology)\n\t\tdesc.Kind = description.TopologyKindSingle\n\t\ttopo.desc.Store(desc)\n\n\t\tselected := description.Server{Addr: address.Address(\"one\")}\n\n\t\tss, err := topo.FindServer(selected)\n\t\trequire.NoError(t, err)\n\t\tif ss.Kind != description.TopologyKindSingle {\n\t\t\tt.Errorf(\"findServer does not properly set the topology description kind. got %v; want %v\", ss.Kind, description.TopologyKindSingle)\n\t\t}\n\t})\n\tt.Run(\"fast path does not subscribe or check timeouts\", func(t *testing.T) {\n\t\t// Assert that the server selection fast path does not create a Subscription or check for timeout errors.\n\t\ttopo, err := New(nil)\n\t\trequire.NoError(t, err)\n\t\tatomic.StoreInt64(&topo.state, topologyConnected)\n\n\t\tprimaryAddr := address.Address(\"one\")\n\t\tdesc := description.Topology{\n\t\t\tServers: []description.Server{\n\t\t\t\t{Addr: primaryAddr, Kind: description.ServerKindRSPrimary},\n\t\t\t},\n\t\t}\n\t\ttopo.desc.Store(desc)\n\t\tfor _, srv := range desc.Servers {\n\t\t\ts, err := ConnectServer(srv.Addr, topo.updateCallback, topo.id, defaultConnectionTimeout)\n\t\t\trequire.NoError(t, err)\n\t\t\ttopo.servers[srv.Addr] = s\n\t\t}\n\n\t\t// Manually close subscriptions so calls to Subscribe will error and pass in a cancelled context to ensure the\n\t\t// fast path ignores timeout errors.\n\t\ttopo.subscriptionsClosed = true\n\t\tctx, cancel := context.WithCancel(context.Background())\n\t\tcancel()\n\t\tselectedServer, err := topo.SelectServer(ctx, &serverselector.Write{})\n\t\trequire.NoError(t, err)\n\t\tselectedAddr := selectedServer.(*SelectedServer).address\n\t\tassert.Equal(t, primaryAddr, selectedAddr, \"expected address %v, got %v\", primaryAddr, selectedAddr)\n\t})\n\tt.Run(\"default to selecting from subscription if fast path fails\", func(t *testing.T) {\n\t\ttopo, err := New(nil)\n\t\trequire.NoError(t, err)\n\n\t\tatomic.StoreInt64(&topo.state, topologyConnected)\n\t\tdesc := description.Topology{\n\t\t\tServers: []description.Server{},\n\t\t}\n\t\ttopo.desc.Store(desc)\n\n\t\ttopo.subscriptionsClosed = true\n\t\t_, err = topo.SelectServer(context.Background(), &serverselector.Write{})\n\t\tassert.Equal(t, ErrSubscribeAfterClosed, err, \"expected error %v, got %v\", ErrSubscribeAfterClosed, err)\n\t})\n\tt.Run(\"if no servers are suitable, block on topology updates\", func(t *testing.T) {\n\t\t// Create a connected Topology with no selectable servers.\n\t\ttopo, err := New(nil)\n\t\trequire.NoError(t, err)\n\t\tatomic.StoreInt64(&topo.state, topologyConnected)\n\n\t\tmss := &mockServerSelector{}\n\n\t\tctx, cancel := context.WithTimeout(context.Background(), 50*time.Millisecond)\n\t\tdefer cancel()\n\n\t\t_, err = topo.SelectServer(ctx, mss)\n\t\tassert.ErrorIs(t, err, context.DeadlineExceeded, \"expected context deadline exceeded error\")\n\n\t\t// Expect SelectServer to be called twice: once for the fast path and\n\t\t// once to select from the topology updates subscription.\n\t\t//\n\t\t// Note: The second call is due to Topology.Subscript pre-populating the\n\t\t// channel with the current topology. It's not clear what the purpose of\n\t\t// that behavior is. The main goal of this assertion is to make sure the\n\t\t// subscription path blocks on updates and doesn't turn into a busy\n\t\t// wait.\n\t\tassert.Equal(t, int64(2), mss.selectServerCalls.Load(), \"expected SelectServer to be called once\")\n\t})\n}\n\nfunc TestSessionTimeout(t *testing.T) {\n\tint64ToPtr := func(i64 int64) *int64 { return &i64 }\n\n\tt.Run(\"UpdateSessionTimeout\", func(t *testing.T) {\n\t\ttopo, err := New(nil)\n\t\trequire.NoError(t, err)\n\t\ttopo.servers[\"foo\"] = nil\n\t\ttopo.fsm.Servers = []description.Server{\n\t\t\t{\n\t\t\t\tAddr:                  address.Address(\"foo\").Canonicalize(),\n\t\t\t\tKind:                  description.ServerKindRSPrimary,\n\t\t\t\tSessionTimeoutMinutes: int64ToPtr(60),\n\t\t\t},\n\t\t}\n\n\t\tctx, cancel := context.WithTimeout(context.Background(), testTimeout)\n\t\tdefer cancel()\n\n\t\tdesc := description.Server{\n\t\t\tAddr:                  \"foo\",\n\t\t\tKind:                  description.ServerKindRSPrimary,\n\t\t\tSessionTimeoutMinutes: int64ToPtr(30),\n\t\t}\n\t\ttopo.apply(ctx, desc)\n\n\t\tcurrDesc := topo.desc.Load().(description.Topology)\n\t\twant := int64(30)\n\t\trequire.Equal(t, &want, currDesc.SessionTimeoutMinutes,\n\t\t\t\"session timeout minutes mismatch\")\n\t})\n\tt.Run(\"MultipleUpdates\", func(t *testing.T) {\n\t\ttopo, err := New(nil)\n\t\trequire.NoError(t, err)\n\t\ttopo.fsm.Kind = description.TopologyKindReplicaSetWithPrimary\n\t\ttopo.servers[\"foo\"] = nil\n\t\ttopo.servers[\"bar\"] = nil\n\t\ttopo.fsm.Servers = []description.Server{\n\t\t\t{\n\t\t\t\tAddr:                  address.Address(\"foo\").Canonicalize(),\n\t\t\t\tKind:                  description.ServerKindRSPrimary,\n\t\t\t\tSessionTimeoutMinutes: int64ToPtr(60),\n\t\t\t},\n\t\t\t{\n\t\t\t\tAddr:                  address.Address(\"bar\").Canonicalize(),\n\t\t\t\tKind:                  description.ServerKindRSSecondary,\n\t\t\t\tSessionTimeoutMinutes: int64ToPtr(60),\n\t\t\t},\n\t\t}\n\n\t\tctx, cancel := context.WithTimeout(context.Background(), testTimeout)\n\t\tdefer cancel()\n\n\t\tdesc1 := description.Server{\n\t\t\tAddr:                  \"foo\",\n\t\t\tKind:                  description.ServerKindRSPrimary,\n\t\t\tSessionTimeoutMinutes: int64ToPtr(30),\n\t\t\tMembers:               []address.Address{address.Address(\"foo\").Canonicalize(), address.Address(\"bar\").Canonicalize()},\n\t\t}\n\t\t// should update because new timeout is lower\n\t\tdesc2 := description.Server{\n\t\t\tAddr:                  \"bar\",\n\t\t\tKind:                  description.ServerKindRSPrimary,\n\t\t\tSessionTimeoutMinutes: int64ToPtr(20),\n\t\t\tMembers:               []address.Address{address.Address(\"foo\").Canonicalize(), address.Address(\"bar\").Canonicalize()},\n\t\t}\n\t\ttopo.apply(ctx, desc1)\n\t\ttopo.apply(ctx, desc2)\n\n\t\tcurrDesc := topo.Description()\n\t\twant := int64(20)\n\t\trequire.Equal(t, &want, currDesc.SessionTimeoutMinutes,\n\t\t\t\"session timeout minutes mismatch\")\n\t})\n\tt.Run(\"NoUpdate\", func(t *testing.T) {\n\t\ttopo, err := New(nil)\n\t\trequire.NoError(t, err)\n\t\ttopo.servers[\"foo\"] = nil\n\t\ttopo.servers[\"bar\"] = nil\n\t\ttopo.fsm.Servers = []description.Server{\n\t\t\t{\n\t\t\t\tAddr:                  address.Address(\"foo\").Canonicalize(),\n\t\t\t\tKind:                  description.ServerKindRSPrimary,\n\t\t\t\tSessionTimeoutMinutes: int64ToPtr(60),\n\t\t\t},\n\t\t\t{\n\t\t\t\tAddr:                  address.Address(\"bar\").Canonicalize(),\n\t\t\t\tKind:                  description.ServerKindRSSecondary,\n\t\t\t\tSessionTimeoutMinutes: int64ToPtr(60),\n\t\t\t},\n\t\t}\n\n\t\tctx, cancel := context.WithTimeout(context.Background(), testTimeout)\n\t\tdefer cancel()\n\n\t\tdesc1 := description.Server{\n\t\t\tAddr:                  \"foo\",\n\t\t\tKind:                  description.ServerKindRSPrimary,\n\t\t\tSessionTimeoutMinutes: int64ToPtr(20),\n\t\t\tMembers:               []address.Address{address.Address(\"foo\").Canonicalize(), address.Address(\"bar\").Canonicalize()},\n\t\t}\n\t\t// should not update because new timeout is higher\n\t\tdesc2 := description.Server{\n\t\t\tAddr:                  \"bar\",\n\t\t\tKind:                  description.ServerKindRSPrimary,\n\t\t\tSessionTimeoutMinutes: int64ToPtr(30),\n\t\t\tMembers:               []address.Address{address.Address(\"foo\").Canonicalize(), address.Address(\"bar\").Canonicalize()},\n\t\t}\n\t\ttopo.apply(ctx, desc1)\n\t\ttopo.apply(ctx, desc2)\n\n\t\tcurrDesc := topo.desc.Load().(description.Topology)\n\t\twant := int64(20)\n\t\trequire.Equal(t, &want, currDesc.SessionTimeoutMinutes,\n\t\t\t\"session timeout minutes mismatch\")\n\t})\n\tt.Run(\"TimeoutDataBearing\", func(t *testing.T) {\n\t\ttopo, err := New(nil)\n\t\trequire.NoError(t, err)\n\t\ttopo.servers[\"foo\"] = nil\n\t\ttopo.servers[\"bar\"] = nil\n\t\ttopo.fsm.Servers = []description.Server{\n\t\t\t{\n\t\t\t\tAddr:                  address.Address(\"foo\").Canonicalize(),\n\t\t\t\tKind:                  description.ServerKindRSPrimary,\n\t\t\t\tSessionTimeoutMinutes: int64ToPtr(60),\n\t\t\t},\n\t\t\t{\n\t\t\t\tAddr:                  address.Address(\"bar\").Canonicalize(),\n\t\t\t\tKind:                  description.ServerKindRSSecondary,\n\t\t\t\tSessionTimeoutMinutes: int64ToPtr(60),\n\t\t\t},\n\t\t}\n\n\t\tctx, cancel := context.WithTimeout(context.Background(), testTimeout)\n\t\tdefer cancel()\n\n\t\tdesc1 := description.Server{\n\t\t\tAddr:                  \"foo\",\n\t\t\tKind:                  description.ServerKindRSPrimary,\n\t\t\tSessionTimeoutMinutes: int64ToPtr(20),\n\t\t\tMembers:               []address.Address{address.Address(\"foo\").Canonicalize(), address.Address(\"bar\").Canonicalize()},\n\t\t}\n\t\t// should not update because not a data bearing server\n\t\tdesc2 := description.Server{\n\t\t\tAddr:                  \"bar\",\n\t\t\tKind:                  description.Unknown,\n\t\t\tSessionTimeoutMinutes: int64ToPtr(10),\n\t\t\tMembers:               []address.Address{address.Address(\"foo\").Canonicalize(), address.Address(\"bar\").Canonicalize()},\n\t\t}\n\t\ttopo.apply(ctx, desc1)\n\t\ttopo.apply(ctx, desc2)\n\n\t\tcurrDesc := topo.desc.Load().(description.Topology)\n\t\twant := int64(20)\n\t\tassert.Equal(t, &want, currDesc.SessionTimeoutMinutes,\n\t\t\t\"session timeout minutes mismatch\")\n\t})\n\tt.Run(\"MixedSessionSupport\", func(t *testing.T) {\n\t\ttopo, err := New(nil)\n\t\trequire.NoError(t, err)\n\t\ttopo.fsm.Kind = description.TopologyKindReplicaSetWithPrimary\n\t\ttopo.servers[\"one\"] = nil\n\t\ttopo.servers[\"two\"] = nil\n\t\ttopo.servers[\"three\"] = nil\n\t\ttopo.fsm.Servers = []description.Server{\n\t\t\t{\n\t\t\t\tAddr:                  address.Address(\"one\").Canonicalize(),\n\t\t\t\tKind:                  description.ServerKindRSPrimary,\n\t\t\t\tSessionTimeoutMinutes: int64ToPtr(20),\n\t\t\t},\n\t\t\t{\n\t\t\t\t// does not support sessions\n\t\t\t\tAddr: address.Address(\"two\").Canonicalize(),\n\t\t\t\tKind: description.ServerKindRSSecondary,\n\t\t\t},\n\t\t\t{\n\t\t\t\tAddr:                  address.Address(\"three\").Canonicalize(),\n\t\t\t\tKind:                  description.ServerKindRSPrimary,\n\t\t\t\tSessionTimeoutMinutes: int64ToPtr(60),\n\t\t\t},\n\t\t}\n\n\t\tctx, cancel := context.WithTimeout(context.Background(), testTimeout)\n\t\tdefer cancel()\n\n\t\tdesc := description.Server{\n\t\t\tAddr:                  address.Address(\"three\"),\n\t\t\tKind:                  description.ServerKindRSSecondary,\n\t\t\tSessionTimeoutMinutes: int64ToPtr(30),\n\t\t}\n\n\t\ttopo.apply(ctx, desc)\n\n\t\tcurrDesc := topo.desc.Load().(description.Topology)\n\t\trequire.Nil(t, currDesc.SessionTimeoutMinutes,\n\t\t\t\"session timeout minutes mismatch. expected: nil\")\n\t})\n}\n\nfunc TestMinPoolSize(t *testing.T) {\n\tcfg, err := NewConfig(options.Client().SetHosts([]string{\"localhost:27017\"}).SetMinPoolSize(10), nil)\n\tif err != nil {\n\t\tt.Errorf(\"error constructing topology config: %v\", err)\n\t}\n\n\ttopo, err := New(cfg)\n\tif err != nil {\n\t\tt.Errorf(\"topology.New shouldn't error. got: %v\", err)\n\t}\n\terr = topo.Connect()\n\tif err != nil {\n\t\tt.Errorf(\"topology.Connect shouldn't error. got: %v\", err)\n\t}\n}\n\nfunc TestTopology_String_Race(_ *testing.T) {\n\tch := make(chan bool)\n\ttopo := &Topology{\n\t\tservers: make(map[address.Address]*Server),\n\t}\n\n\tgo func() {\n\t\ttopo.serversLock.Lock()\n\t\tsrv := &Server{}\n\t\tsrv.desc.Store(description.Server{})\n\t\ttopo.servers[address.Address(\"127.0.0.1:27017\")] = srv\n\t\ttopo.serversLock.Unlock()\n\t\tch <- true\n\t}()\n\n\tgo func() {\n\t\t_ = topo.String()\n\t\tch <- true\n\t}()\n\n\t<-ch\n\t<-ch\n}\n\nfunc TestTopologyConstruction(t *testing.T) {\n\tt.Run(\"construct with URI\", func(t *testing.T) {\n\t\ttestCases := []struct {\n\t\t\tname            string\n\t\t\turi             string\n\t\t\tpollingRequired bool\n\t\t}{\n\t\t\t{\n\t\t\t\tname:            \"normal\",\n\t\t\t\turi:             \"mongodb://localhost:27017\",\n\t\t\t\tpollingRequired: false,\n\t\t\t},\n\t\t}\n\t\tfor _, tc := range testCases {\n\t\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\t\tcfg, err := NewConfig(options.Client().ApplyURI(tc.uri), nil)\n\t\t\t\tassert.Nil(t, err, \"error constructing topology config: %v\", err)\n\n\t\t\t\ttopo, err := New(cfg)\n\t\t\t\tassert.Nil(t, err, \"topology.New error: %v\", err)\n\n\t\t\t\tassert.Equal(t, tc.uri, topo.cfg.URI, \"expected topology URI to be %v, got %v\", tc.uri, topo.cfg.URI)\n\t\t\t\tassert.Equal(t, tc.pollingRequired, topo.pollingRequired,\n\t\t\t\t\t\"expected topo.pollingRequired to be %v, got %v\", tc.pollingRequired, topo.pollingRequired)\n\t\t\t})\n\t\t}\n\t})\n}\n\ntype mockLogSink struct {\n\tmsgs []string\n}\n\nfunc (s *mockLogSink) Info(_ int, msg string, _ ...any) {\n\ts.msgs = append(s.msgs, msg)\n}\n\nfunc (*mockLogSink) Error(error, string, ...any) {\n\t// Do nothing.\n}\n\n// Note: SRV connection strings are intentionally untested, since initial\n// lookup responses cannot be easily mocked.\nfunc TestTopologyConstructionLogging(t *testing.T) {\n\tconst (\n\t\tcosmosDBMsg   = `You appear to be connected to a CosmosDB cluster. For more information regarding feature compatibility and support please visit https://www.mongodb.com/supportability/cosmosdb`\n\t\tdocumentDBMsg = `You appear to be connected to a DocumentDB cluster. For more information regarding feature compatibility and support please visit https://www.mongodb.com/supportability/documentdb`\n\t)\n\n\tnewLoggerOptionsBldr := func(sink options.LogSink) *options.LoggerOptions {\n\t\treturn options.\n\t\t\tLogger().\n\t\t\tSetSink(sink).\n\t\t\tSetComponentLevel(options.LogComponentTopology, options.LogLevelInfo)\n\t}\n\n\tt.Run(\"CosmosDB URIs\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\ttestCases := []struct {\n\t\t\tname string\n\t\t\turi  string\n\t\t\tmsgs []string\n\t\t}{\n\t\t\t{\n\t\t\t\tname: \"normal\",\n\t\t\t\turi:  \"mongodb://a.mongo.cosmos.azure.com:19555/\",\n\t\t\t\tmsgs: []string{cosmosDBMsg},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"multiple hosts\",\n\t\t\t\turi:  \"mongodb://a.mongo.cosmos.azure.com:1955,b.mongo.cosmos.azure.com:19555/\",\n\t\t\t\tmsgs: []string{cosmosDBMsg},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"case-insensitive matching\",\n\t\t\t\turi:  \"mongodb://a.MONGO.COSMOS.AZURE.COM:19555/\",\n\t\t\t\tmsgs: []string{},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"Mixing genuine and nongenuine hosts (unlikely in practice)\",\n\t\t\t\turi:  \"mongodb://a.example.com:27017,b.mongo.cosmos.azure.com:19555/\",\n\t\t\t\tmsgs: []string{cosmosDBMsg},\n\t\t\t},\n\t\t}\n\t\tfor _, tc := range testCases {\n\t\t\ttc := tc\n\n\t\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\t\tt.Parallel()\n\n\t\t\t\tsink := &mockLogSink{}\n\t\t\t\tcfg, err := NewConfig(options.Client().ApplyURI(tc.uri).SetLoggerOptions(newLoggerOptionsBldr(sink)), nil)\n\t\t\t\trequire.Nil(t, err, \"error constructing topology config: %v\", err)\n\n\t\t\t\ttopo, err := New(cfg)\n\t\t\t\trequire.Nil(t, err, \"topology.New error: %v\", err)\n\n\t\t\t\terr = topo.Connect()\n\t\t\t\tassert.Nil(t, err, \"Connect error: %v\", err)\n\n\t\t\t\tassert.ElementsMatch(t, tc.msgs, sink.msgs, \"expected messages to be %v, got %v\", tc.msgs, sink.msgs)\n\t\t\t})\n\t\t}\n\t})\n\tt.Run(\"DocumentDB URIs\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\ttestCases := []struct {\n\t\t\tname string\n\t\t\turi  string\n\t\t\tmsgs []string\n\t\t}{\n\t\t\t{\n\t\t\t\tname: \"normal\",\n\t\t\t\turi:  \"mongodb://a.docdb.amazonaws.com:27017/\",\n\t\t\t\tmsgs: []string{documentDBMsg},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"normal\",\n\t\t\t\turi:  \"mongodb://a.docdb-elastic.amazonaws.com:27017/\",\n\t\t\t\tmsgs: []string{documentDBMsg},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"multiple hosts\",\n\t\t\t\turi:  \"mongodb://a.docdb.amazonaws.com:27017,a.docdb-elastic.amazonaws.com:27017/\",\n\t\t\t\tmsgs: []string{documentDBMsg},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"case-insensitive matching\",\n\t\t\t\turi:  \"mongodb://a.DOCDB.AMAZONAWS.COM:27017/\",\n\t\t\t\tmsgs: []string{},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"case-insensitive matching\",\n\t\t\t\turi:  \"mongodb://a.DOCDB-ELASTIC.AMAZONAWS.COM:27017/\",\n\t\t\t\tmsgs: []string{},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"Mixing genuine and nongenuine hosts (unlikely in practice)\",\n\t\t\t\turi:  \"mongodb://a.example.com:27017,b.docdb.amazonaws.com:27017/\",\n\t\t\t\tmsgs: []string{documentDBMsg},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"Mixing genuine and nongenuine hosts (unlikely in practice)\",\n\t\t\t\turi:  \"mongodb://a.example.com:27017,b.docdb-elastic.amazonaws.com:27017/\",\n\t\t\t\tmsgs: []string{documentDBMsg},\n\t\t\t},\n\t\t}\n\t\tfor _, tc := range testCases {\n\t\t\ttc := tc\n\n\t\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\t\tt.Parallel()\n\n\t\t\t\tsink := &mockLogSink{}\n\t\t\t\tcfg, err := NewConfig(options.Client().ApplyURI(tc.uri).SetLoggerOptions(newLoggerOptionsBldr(sink)), nil)\n\t\t\t\trequire.Nil(t, err, \"error constructing topology config: %v\", err)\n\n\t\t\t\ttopo, err := New(cfg)\n\t\t\t\trequire.Nil(t, err, \"topology.New error: %v\", err)\n\n\t\t\t\terr = topo.Connect()\n\t\t\t\tassert.Nil(t, err, \"Connect error: %v\", err)\n\n\t\t\t\tassert.ElementsMatch(t, tc.msgs, sink.msgs, \"expected messages to be %v, got %v\", tc.msgs, sink.msgs)\n\t\t\t})\n\t\t}\n\t})\n\tt.Run(\"Mixing CosmosDB and DocumentDB URIs\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\ttestCases := []struct {\n\t\t\tname string\n\t\t\turi  string\n\t\t\tmsgs []string\n\t\t}{\n\t\t\t{\n\t\t\t\tname: \"Mixing hosts\",\n\t\t\t\turi:  \"mongodb://a.mongo.cosmos.azure.com:19555,a.docdb.amazonaws.com:27017/\",\n\t\t\t\tmsgs: []string{cosmosDBMsg, documentDBMsg},\n\t\t\t},\n\t\t}\n\t\tfor _, tc := range testCases {\n\t\t\ttc := tc\n\n\t\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\t\tt.Parallel()\n\n\t\t\t\tsink := &mockLogSink{}\n\t\t\t\tcfg, err := NewConfig(options.Client().ApplyURI(tc.uri).SetLoggerOptions(newLoggerOptionsBldr(sink)), nil)\n\t\t\t\trequire.Nil(t, err, \"error constructing topology config: %v\", err)\n\n\t\t\t\ttopo, err := New(cfg)\n\t\t\t\trequire.Nil(t, err, \"topology.New error: %v\", err)\n\n\t\t\t\terr = topo.Connect()\n\t\t\t\tassert.Nil(t, err, \"Connect error: %v\", err)\n\n\t\t\t\tassert.ElementsMatch(t, tc.msgs, sink.msgs, \"expected messages to be %v, got %v\", tc.msgs, sink.msgs)\n\t\t\t})\n\t\t}\n\t})\n\tt.Run(\"genuine URIs\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\ttestCases := []struct {\n\t\t\tname string\n\t\t\turi  string\n\t\t\tmsgs []string\n\t\t}{\n\t\t\t{\n\t\t\t\tname: \"normal\",\n\t\t\t\turi:  \"mongodb://a.example.com:27017/\",\n\t\t\t\tmsgs: []string{},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"socket\",\n\t\t\t\turi:  \"mongodb://%2Ftmp%2Fmongodb-27017.sock/\",\n\t\t\t\tmsgs: []string{},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"srv\",\n\t\t\t\turi:  \"mongodb+srv://test22.test.build.10gen.cc/?srvServiceName=customname\",\n\t\t\t\tmsgs: []string{},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"multiple hosts\",\n\t\t\t\turi:  \"mongodb://a.example.com:27017,b.example.com:27017/\",\n\t\t\t\tmsgs: []string{},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"unexpected suffix\",\n\t\t\t\turi:  \"mongodb://a.mongo.cosmos.azure.com.tld:19555/\",\n\t\t\t\tmsgs: []string{},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"unexpected suffix\",\n\t\t\t\turi:  \"mongodb://a.docdb.amazonaws.com.tld:27017/\",\n\t\t\t\tmsgs: []string{},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"unexpected suffix\",\n\t\t\t\turi:  \"mongodb://a.docdb-elastic.amazonaws.com.tld:27017/\",\n\t\t\t\tmsgs: []string{},\n\t\t\t},\n\t\t}\n\t\tfor _, tc := range testCases {\n\t\t\ttc := tc\n\n\t\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\t\tt.Parallel()\n\n\t\t\t\tsink := &mockLogSink{}\n\t\t\t\tcfg, err := NewConfig(options.Client().ApplyURI(tc.uri).SetLoggerOptions(newLoggerOptionsBldr(sink)), nil)\n\t\t\t\trequire.Nil(t, err, \"error constructing topology config: %v\", err)\n\n\t\t\t\ttopo, err := New(cfg)\n\t\t\t\trequire.Nil(t, err, \"topology.New error: %v\", err)\n\n\t\t\t\terr = topo.Connect()\n\t\t\t\tassert.Nil(t, err, \"Connect error: %v\", err)\n\n\t\t\t\tassert.ElementsMatch(t, tc.msgs, sink.msgs, \"expected messages to be %v, got %v\", tc.msgs, sink.msgs)\n\t\t\t})\n\t\t}\n\t})\n}\n\ntype inWindowServer struct {\n\tAddress  string `json:\"address\"`\n\tType     string `json:\"type\"`\n\tAvgRTTMS int64  `json:\"avg_rtt_ms\"`\n}\n\ntype inWindowTopology struct {\n\tType    string           `json:\"type\"`\n\tServers []inWindowServer `json:\"servers\"`\n}\n\ntype inWindowOutcome struct {\n\tTolerance           float64            `json:\"tolerance\"`\n\tExpectedFrequencies map[string]float64 `json:\"expected_frequencies\"`\n}\n\ntype inWindowTopologyState struct {\n\tAddress        string `json:\"address\"`\n\tOperationCount int64  `json:\"operation_count\"`\n}\n\ntype inWindowTestCase struct {\n\tTopologyDescription inWindowTopology        `json:\"topology_description\"`\n\tMockedTopologyState []inWindowTopologyState `json:\"mocked_topology_state\"`\n\tIterations          int                     `json:\"iterations\"`\n\tOutcome             inWindowOutcome         `json:\"outcome\"`\n}\n\n// TestServerSelectionSpecInWindow runs the \"in_window\" server selection spec tests. This test is\n// in the \"topology\" package instead of the \"description\" package (where the rest of the server\n// selection spec tests are) because it primarily tests load-based server selection. Load-based\n// server selection is implemented in Topology.SelectServer() because it requires knowledge of the\n// current \"operation count\" (the number of currently running operations) for each server, so it\n// can't be effectively accomplished just with server descriptions like most other server selection\n// algorithms.\nfunc TestServerSelectionSpecInWindow(t *testing.T) {\n\ttestsDir := spectest.Path(\"server-selection/tests/in_window\")\n\n\tfiles := spectest.FindJSONFilesInDir(t, testsDir)\n\n\tfor _, file := range files {\n\t\tt.Run(file, func(t *testing.T) {\n\t\t\trunInWindowTest(t, testsDir, file)\n\t\t})\n\t}\n}\n\nfunc runInWindowTest(t *testing.T, directory string, filename string) {\n\tfilepath := path.Join(directory, filename)\n\tcontent, err := ioutil.ReadFile(filepath)\n\trequire.NoError(t, err)\n\n\tvar test inWindowTestCase\n\trequire.NoError(t, json.Unmarshal(content, &test))\n\n\t// For each server described in the test's \"topology_description\", create both a *Server and\n\t// description.Server, which are both required to run Topology.SelectServer().\n\tservers := make(map[string]*Server, len(test.TopologyDescription.Servers))\n\tdescriptions := make([]description.Server, 0, len(test.TopologyDescription.Servers))\n\tfor _, testDesc := range test.TopologyDescription.Servers {\n\t\tserver := NewServer(\n\t\t\taddress.Address(testDesc.Address),\n\t\t\tbson.NilObjectID,\n\t\t\tdefaultConnectionTimeout,\n\t\t\twithMonitoringDisabled(func(bool) bool { return true }))\n\t\tservers[testDesc.Address] = server\n\n\t\tdesc := description.Server{\n\t\t\tKind:          serverKindFromString(t, testDesc.Type),\n\t\t\tAddr:          address.Address(testDesc.Address),\n\t\t\tAverageRTT:    time.Duration(testDesc.AvgRTTMS) * time.Millisecond,\n\t\t\tAverageRTTSet: true,\n\t\t}\n\n\t\tif testDesc.AvgRTTMS > 0 {\n\t\t\tdesc.AverageRTT = time.Duration(testDesc.AvgRTTMS) * time.Millisecond\n\t\t\tdesc.AverageRTTSet = true\n\t\t}\n\n\t\tdescriptions = append(descriptions, desc)\n\t}\n\n\t// For each server state in the test's \"mocked_topology_state\", set the connection pool's\n\t// in-use connections count to the test operation count value.\n\tfor _, state := range test.MockedTopologyState {\n\t\tservers[state.Address].operationCount = state.OperationCount\n\t}\n\n\t// Create a new Topology, set the state to \"connected\", store a topology description\n\t// containing all server descriptions created from the test server descriptions, and copy\n\t// all *Server instances to the Topology's servers list.\n\ttopology, err := New(nil)\n\trequire.NoError(t, err, \"error creating new Topology\")\n\ttopology.state = topologyConnected\n\ttopology.desc.Store(description.Topology{\n\t\tKind:    topologyKindFromString(t, test.TopologyDescription.Type),\n\t\tServers: descriptions,\n\t})\n\tfor addr, server := range servers {\n\t\ttopology.servers[address.Address(addr)] = server\n\t}\n\n\t// Run server selection the required number of times and record how many times each server\n\t// address was selected.\n\tcounts := make(map[string]int, len(test.TopologyDescription.Servers))\n\tfor i := 0; i < test.Iterations; i++ {\n\t\tselected, err := topology.SelectServer(\n\t\t\tcontext.Background(),\n\t\t\t&serverselector.ReadPref{ReadPref: readpref.Nearest()})\n\t\trequire.NoError(t, err, \"error selecting server\")\n\t\tcounts[string(selected.(*SelectedServer).address)]++\n\t}\n\n\t// Convert the server selection counts to selection frequencies by dividing the counts by\n\t// the total number of server selection attempts.\n\tfrequencies := make(map[string]float64, len(counts))\n\tfor addr, count := range counts {\n\t\tfrequencies[addr] = float64(count) / float64(test.Iterations)\n\t}\n\n\t// Assert that the observed server selection frequency for each server address matches the\n\t// expected server selection frequency.\n\tfor addr, expected := range test.Outcome.ExpectedFrequencies {\n\t\tactual := frequencies[addr]\n\n\t\t// If the expected frequency for a given server is 1 or 0, then the observed frequency\n\t\t// MUST be exactly equal to the expected one.\n\t\tif expected == 1 || expected == 0 {\n\t\t\tassert.Equal(\n\t\t\t\tt,\n\t\t\t\texpected,\n\t\t\t\tactual,\n\t\t\t\t\"expected frequency of %q to be equal to %f, but is %f\",\n\t\t\t\taddr, expected, actual)\n\t\t\tcontinue\n\t\t}\n\n\t\t// Otherwise, check if the expected frequency is within the given tolerance range.\n\t\t// TODO(GODRIVER-2179): Use assert.Deltaf() when we migrate all test code to the \"testify/assert\" or an\n\t\t// TODO API-compatible library for assertions.\n\t\tlow := expected - test.Outcome.Tolerance\n\t\thigh := expected + test.Outcome.Tolerance\n\t\tassert.True(\n\t\t\tt,\n\t\t\tactual >= low && actual <= high,\n\t\t\t\"expected frequency of %q to be in range [%f, %f], but is %f\",\n\t\t\taddr, low, high, actual)\n\t}\n}\n\nfunc topologyKindFromString(t *testing.T, s string) description.TopologyKind {\n\tt.Helper()\n\n\tswitch s {\n\tcase \"Single\":\n\t\treturn description.TopologyKindSingle\n\tcase \"ReplicaSet\":\n\t\treturn description.TopologyKindReplicaSet\n\tcase \"ReplicaSetNoPrimary\":\n\t\treturn description.TopologyKindReplicaSetNoPrimary\n\tcase \"ReplicaSetWithPrimary\":\n\t\treturn description.TopologyKindReplicaSetWithPrimary\n\tcase \"Sharded\":\n\t\treturn description.TopologyKindSharded\n\tcase \"LoadBalanced\":\n\t\treturn description.TopologyKindLoadBalanced\n\tcase \"Unknown\":\n\t\treturn description.Unknown\n\tdefault:\n\t\tt.Fatalf(\"unrecognized topology kind: %q\", s)\n\t}\n\n\treturn description.Unknown\n}\n\nfunc serverKindFromString(t *testing.T, s string) description.ServerKind {\n\tt.Helper()\n\n\tswitch s {\n\tcase \"Standalone\":\n\t\treturn description.ServerKindStandalone\n\tcase \"RSOther\":\n\t\treturn description.ServerKindRSMember\n\tcase \"RSPrimary\":\n\t\treturn description.ServerKindRSPrimary\n\tcase \"RSSecondary\":\n\t\treturn description.ServerKindRSSecondary\n\tcase \"RSArbiter\":\n\t\treturn description.ServerKindRSArbiter\n\tcase \"RSGhost\":\n\t\treturn description.ServerKindRSGhost\n\tcase \"Mongos\":\n\t\treturn description.ServerKindMongos\n\tcase \"LoadBalancer\":\n\t\treturn description.ServerKindLoadBalancer\n\tcase \"PossiblePrimary\", \"Unknown\":\n\t\t// Go does not have a PossiblePrimary server type and per the SDAM spec, this type is synonymous with Unknown.\n\t\treturn description.Unknown\n\tdefault:\n\t\tt.Fatalf(\"unrecognized server kind: %q\", s)\n\t}\n\n\treturn description.Unknown\n}\n\nfunc BenchmarkSelectServerFromDescription(b *testing.B) {\n\tfor _, bcase := range []struct {\n\t\tname        string\n\t\tserversHook func(servers []description.Server)\n\t}{\n\t\t{\n\t\t\tname:        \"AllFit\",\n\t\t\tserversHook: func([]description.Server) {},\n\t\t},\n\t\t{\n\t\t\tname: \"AllButOneFit\",\n\t\t\tserversHook: func(servers []description.Server) {\n\t\t\t\tservers[0].Kind = description.Unknown\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"HalfFit\",\n\t\t\tserversHook: func(servers []description.Server) {\n\t\t\t\tfor i := 0; i < len(servers); i += 2 {\n\t\t\t\t\tservers[i].Kind = description.Unknown\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"OneFit\",\n\t\t\tserversHook: func(servers []description.Server) {\n\t\t\t\tfor i := 1; i < len(servers); i++ {\n\t\t\t\t\tservers[i].Kind = description.Unknown\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t} {\n\t\tbcase := bcase\n\n\t\tb.Run(bcase.name, func(b *testing.B) {\n\t\t\ts := description.Server{\n\t\t\t\tAddr:              address.Address(\"localhost:27017\"),\n\t\t\t\tHeartbeatInterval: time.Duration(10) * time.Second,\n\t\t\t\tLastWriteTime:     time.Date(2017, 2, 11, 14, 0, 0, 0, time.UTC),\n\t\t\t\tLastUpdateTime:    time.Date(2017, 2, 11, 14, 0, 2, 0, time.UTC),\n\t\t\t\tKind:              description.ServerKindMongos,\n\t\t\t\tWireVersion:       &description.VersionRange{Min: 6, Max: 21},\n\t\t\t}\n\t\t\tservers := make([]description.Server, 100)\n\t\t\tfor i := 0; i < len(servers); i++ {\n\t\t\t\tservers[i] = s\n\t\t\t}\n\t\t\tbcase.serversHook(servers)\n\t\t\tdesc := description.Topology{\n\t\t\t\tServers: servers,\n\t\t\t}\n\n\t\t\tb.ResetTimer()\n\t\t\tb.RunParallel(func(p *testing.PB) {\n\t\t\t\tb.ReportAllocs()\n\t\t\t\tfor p.Next() {\n\t\t\t\t\tvar c Topology\n\t\t\t\t\t_, _ = c.selectServerFromDescription(desc, selectNone)\n\t\t\t\t}\n\t\t\t})\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "x/mongo/driver/wiremessage/wiremessage.go",
    "content": "// Copyright (C) MongoDB, Inc. 2022-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\n// Package wiremessage is intended for internal use only. It is made available\n// to facilitate use cases that require access to internal MongoDB driver\n// functionality and state. The API of this package is not stable and there is\n// no backward compatibility guarantee.\n//\n// WARNING: THIS PACKAGE IS EXPERIMENTAL AND MAY BE MODIFIED OR REMOVED WITHOUT\n// NOTICE! USE WITH EXTREME CAUTION!\npackage wiremessage\n\nimport (\n\t\"strings\"\n\t\"sync/atomic\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/binaryutil\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n)\n\n// WireMessage represents a MongoDB wire message in binary form.\ntype WireMessage []byte\n\nvar globalRequestID int32\n\n// NextRequestID returns the next request ID.\nfunc NextRequestID() int32 { return atomic.AddInt32(&globalRequestID, 1) }\n\n// OpCode represents a MongoDB wire protocol opcode.\ntype OpCode int32\n\n// These constants are the valid opcodes for the version of the wireprotocol\n// supported by this library. The skipped OpCodes are historical OpCodes that\n// are no longer used.\nconst (\n\tOpReply  OpCode = 1\n\t_        OpCode = 1001\n\tOpUpdate OpCode = 2001\n\tOpInsert OpCode = 2002\n\t_        OpCode = 2003\n\t// Deprecated: Use OpMsg instead.\n\tOpQuery        OpCode = 2004\n\tOpGetMore      OpCode = 2005\n\tOpDelete       OpCode = 2006\n\tOpKillCursors  OpCode = 2007\n\tOpCommand      OpCode = 2010\n\tOpCommandReply OpCode = 2011\n\tOpCompressed   OpCode = 2012\n\tOpMsg          OpCode = 2013\n)\n\n// String implements the fmt.Stringer interface.\nfunc (oc OpCode) String() string {\n\tswitch oc {\n\tcase OpReply:\n\t\treturn \"OP_REPLY\"\n\tcase OpUpdate:\n\t\treturn \"OP_UPDATE\"\n\tcase OpInsert:\n\t\treturn \"OP_INSERT\"\n\tcase OpQuery:\n\t\treturn \"OP_QUERY\"\n\tcase OpGetMore:\n\t\treturn \"OP_GET_MORE\"\n\tcase OpDelete:\n\t\treturn \"OP_DELETE\"\n\tcase OpKillCursors:\n\t\treturn \"OP_KILL_CURSORS\"\n\tcase OpCommand:\n\t\treturn \"OP_COMMAND\"\n\tcase OpCommandReply:\n\t\treturn \"OP_COMMANDREPLY\"\n\tcase OpCompressed:\n\t\treturn \"OP_COMPRESSED\"\n\tcase OpMsg:\n\t\treturn \"OP_MSG\"\n\tdefault:\n\t\treturn \"<invalid opcode>\"\n\t}\n}\n\n// QueryFlag represents the flags on an OP_QUERY message.\ntype QueryFlag int32\n\n// These constants represent the individual flags on an OP_QUERY message.\nconst (\n\t_ QueryFlag = 1 << iota\n\tTailableCursor\n\tSecondaryOK\n\tOplogReplay\n\tNoCursorTimeout\n\tAwaitData\n\tExhaust\n\tPartial\n)\n\n// String implements the fmt.Stringer interface.\nfunc (qf QueryFlag) String() string {\n\tstrs := make([]string, 0)\n\tif qf&TailableCursor == TailableCursor {\n\t\tstrs = append(strs, \"TailableCursor\")\n\t}\n\tif qf&SecondaryOK == SecondaryOK {\n\t\tstrs = append(strs, \"SecondaryOK\")\n\t}\n\tif qf&OplogReplay == OplogReplay {\n\t\tstrs = append(strs, \"OplogReplay\")\n\t}\n\tif qf&NoCursorTimeout == NoCursorTimeout {\n\t\tstrs = append(strs, \"NoCursorTimeout\")\n\t}\n\tif qf&AwaitData == AwaitData {\n\t\tstrs = append(strs, \"AwaitData\")\n\t}\n\tif qf&Exhaust == Exhaust {\n\t\tstrs = append(strs, \"Exhaust\")\n\t}\n\tif qf&Partial == Partial {\n\t\tstrs = append(strs, \"Partial\")\n\t}\n\tstr := \"[\"\n\tstr += strings.Join(strs, \", \")\n\tstr += \"]\"\n\treturn str\n}\n\n// MsgFlag represents the flags on an OP_MSG message.\ntype MsgFlag uint32\n\n// These constants represent the individual flags on an OP_MSG message.\nconst (\n\tChecksumPresent MsgFlag = 1 << iota\n\tMoreToCome\n\n\tExhaustAllowed MsgFlag = 1 << 16\n)\n\n// ReplyFlag represents the flags of an OP_REPLY message.\ntype ReplyFlag int32\n\n// These constants represent the individual flags of an OP_REPLY message.\nconst (\n\tCursorNotFound ReplyFlag = 1 << iota\n\tQueryFailure\n\tShardConfigStale\n\tAwaitCapable\n)\n\n// String implements the fmt.Stringer interface.\nfunc (rf ReplyFlag) String() string {\n\tstrs := make([]string, 0)\n\tif rf&CursorNotFound == CursorNotFound {\n\t\tstrs = append(strs, \"CursorNotFound\")\n\t}\n\tif rf&QueryFailure == QueryFailure {\n\t\tstrs = append(strs, \"QueryFailure\")\n\t}\n\tif rf&ShardConfigStale == ShardConfigStale {\n\t\tstrs = append(strs, \"ShardConfigStale\")\n\t}\n\tif rf&AwaitCapable == AwaitCapable {\n\t\tstrs = append(strs, \"AwaitCapable\")\n\t}\n\tstr := \"[\"\n\tstr += strings.Join(strs, \", \")\n\tstr += \"]\"\n\treturn str\n}\n\n// SectionType represents the type for 1 section in an OP_MSG\ntype SectionType uint8\n\n// These constants represent the individual section types for a section in an OP_MSG\nconst (\n\tSingleDocument SectionType = iota\n\tDocumentSequence\n)\n\n// CompressorID is the ID for each type of Compressor.\ntype CompressorID uint8\n\n// These constants represent the individual compressor IDs for an OP_COMPRESSED.\nconst (\n\tCompressorNoOp CompressorID = iota\n\tCompressorSnappy\n\tCompressorZLib\n\tCompressorZstd\n)\n\n// String implements the fmt.Stringer interface.\nfunc (id CompressorID) String() string {\n\tswitch id {\n\tcase CompressorNoOp:\n\t\treturn \"CompressorNoOp\"\n\tcase CompressorSnappy:\n\t\treturn \"CompressorSnappy\"\n\tcase CompressorZLib:\n\t\treturn \"CompressorZLib\"\n\tcase CompressorZstd:\n\t\treturn \"CompressorZstd\"\n\tdefault:\n\t\treturn \"CompressorInvalid\"\n\t}\n}\n\nconst (\n\t// DefaultZlibLevel is the default level for zlib compression\n\tDefaultZlibLevel = 6\n\t// DefaultZstdLevel is the default level for zstd compression.\n\t// Matches https://github.com/wiredtiger/wiredtiger/blob/f08bc4b18612ef95a39b12166abcccf207f91596/ext/compressors/zstd/zstd_compress.c#L299\n\tDefaultZstdLevel = 6\n)\n\n// AppendHeaderStart appends a header to the dst slice and returns an index where the wire message\n// starts in dst and the updated slice.\nfunc AppendHeaderStart(dst []byte, reqid, respto int32, opcode OpCode) (index int32, b []byte) {\n\tindex, dst = bsoncore.ReserveLength(dst)\n\tdst = binaryutil.Append32(dst, reqid)\n\tdst = binaryutil.Append32(dst, respto)\n\tdst = binaryutil.Append32(dst, opcode)\n\treturn index, dst\n}\n\n// AppendHeader appends a header to dst.\nfunc AppendHeader(dst []byte, length, reqid, respto int32, opcode OpCode) []byte {\n\tdst = binaryutil.Append32(dst, length)\n\tdst = binaryutil.Append32(dst, reqid)\n\tdst = binaryutil.Append32(dst, respto)\n\tdst = binaryutil.Append32(dst, opcode)\n\treturn dst\n}\n\n// ReadHeader reads a wire message header from src.\nfunc ReadHeader(src []byte) (length, requestID, responseTo int32, opcode OpCode, rem []byte, ok bool) {\n\tif len(src) < 16 {\n\t\treturn 0, 0, 0, 0, src, false\n\t}\n\n\tlength, _, _ = binaryutil.ReadI32(src)\n\trequestID, _, _ = binaryutil.ReadI32(src[4:])\n\tresponseTo, _, _ = binaryutil.ReadI32(src[8:])\n\topcodeVal, _, _ := binaryutil.ReadI32(src[12:])\n\topcode = OpCode(opcodeVal)\n\treturn length, requestID, responseTo, opcode, src[16:], true\n}\n\n// AppendQueryFlags appends the flags for an OP_QUERY wire message.\nfunc AppendQueryFlags(dst []byte, flags QueryFlag) []byte {\n\treturn binaryutil.Append32(dst, flags)\n}\n\n// AppendMsgFlags appends the flags for an OP_MSG wire message.\nfunc AppendMsgFlags(dst []byte, flags MsgFlag) []byte {\n\treturn binaryutil.Append32(dst, flags)\n}\n\n// AppendReplyFlags appends the flags for an OP_REPLY wire message.\nfunc AppendReplyFlags(dst []byte, flags ReplyFlag) []byte {\n\treturn binaryutil.Append32(dst, flags)\n}\n\n// AppendMsgSectionType appends the section type to dst.\nfunc AppendMsgSectionType(dst []byte, stype SectionType) []byte {\n\treturn append(dst, byte(stype))\n}\n\n// AppendQueryFullCollectionName appends the full collection name to dst.\nfunc AppendQueryFullCollectionName(dst []byte, ns string) []byte {\n\treturn appendCString(dst, ns)\n}\n\n// AppendQueryNumberToSkip appends the number to skip to dst.\nfunc AppendQueryNumberToSkip(dst []byte, skip int32) []byte {\n\treturn binaryutil.Append32(dst, skip)\n}\n\n// AppendQueryNumberToReturn appends the number to return to dst.\nfunc AppendQueryNumberToReturn(dst []byte, nor int32) []byte {\n\treturn binaryutil.Append32(dst, nor)\n}\n\n// AppendReplyCursorID appends the cursor ID to dst.\nfunc AppendReplyCursorID(dst []byte, id int64) []byte {\n\treturn binaryutil.Append64(dst, id)\n}\n\n// AppendReplyStartingFrom appends the starting from field to dst.\nfunc AppendReplyStartingFrom(dst []byte, sf int32) []byte {\n\treturn binaryutil.Append32(dst, sf)\n}\n\n// AppendReplyNumberReturned appends the number returned to dst.\nfunc AppendReplyNumberReturned(dst []byte, nr int32) []byte {\n\treturn binaryutil.Append32(dst, nr)\n}\n\n// AppendCompressedOriginalOpCode appends the original opcode to dst.\nfunc AppendCompressedOriginalOpCode(dst []byte, opcode OpCode) []byte {\n\treturn binaryutil.Append32(dst, opcode)\n}\n\n// AppendCompressedUncompressedSize appends the uncompressed size of a\n// compressed wiremessage to dst.\nfunc AppendCompressedUncompressedSize(dst []byte, size int32) []byte {\n\treturn binaryutil.Append32(dst, size)\n}\n\n// AppendCompressedCompressorID appends the ID of the compressor to dst.\nfunc AppendCompressedCompressorID(dst []byte, id CompressorID) []byte {\n\treturn append(dst, byte(id))\n}\n\n// AppendCompressedCompressedMessage appends the compressed wiremessage to dst.\nfunc AppendCompressedCompressedMessage(dst []byte, msg []byte) []byte { return append(dst, msg...) }\n\n// AppendGetMoreZero appends the zero field to dst.\nfunc AppendGetMoreZero(dst []byte) []byte {\n\treturn binaryutil.Append32(dst, int32(0))\n}\n\n// AppendGetMoreFullCollectionName appends the fullCollectionName field to dst.\nfunc AppendGetMoreFullCollectionName(dst []byte, ns string) []byte {\n\treturn appendCString(dst, ns)\n}\n\n// AppendGetMoreNumberToReturn appends the numberToReturn field to dst.\nfunc AppendGetMoreNumberToReturn(dst []byte, numToReturn int32) []byte {\n\treturn binaryutil.Append32(dst, numToReturn)\n}\n\n// AppendGetMoreCursorID appends the cursorID field to dst.\nfunc AppendGetMoreCursorID(dst []byte, cursorID int64) []byte {\n\treturn binaryutil.Append64(dst, cursorID)\n}\n\n// AppendKillCursorsZero appends the zero field to dst.\nfunc AppendKillCursorsZero(dst []byte) []byte {\n\treturn binaryutil.Append32(dst, int32(0))\n}\n\n// AppendKillCursorsNumberIDs appends the numberOfCursorIDs field to dst.\nfunc AppendKillCursorsNumberIDs(dst []byte, numIDs int32) []byte {\n\treturn binaryutil.Append32(dst, numIDs)\n}\n\n// AppendKillCursorsCursorIDs appends each the cursorIDs field to dst.\nfunc AppendKillCursorsCursorIDs(dst []byte, cursors []int64) []byte {\n\tfor _, cursor := range cursors {\n\t\tdst = binaryutil.Append64(dst, cursor)\n\t}\n\treturn dst\n}\n\n// ReadMsgFlags reads the OP_MSG flags from src.\nfunc ReadMsgFlags(src []byte) (flags MsgFlag, rem []byte, ok bool) {\n\ti32, rem, ok := binaryutil.ReadI32(src)\n\treturn MsgFlag(i32), rem, ok\n}\n\n// IsMsgMoreToCome returns if the provided wire message is an OP_MSG with the more to come flag set.\nfunc IsMsgMoreToCome(wm []byte) bool {\n\tif len(wm) < 20 {\n\t\treturn false\n\t}\n\topcode, _, _ := binaryutil.ReadI32(wm[12:16])\n\tflag, _, _ := binaryutil.ReadI32(wm[16:20])\n\treturn OpCode(opcode) == OpMsg && MsgFlag(flag)&MoreToCome == MoreToCome\n}\n\n// ReadMsgSectionType reads the section type from src.\nfunc ReadMsgSectionType(src []byte) (stype SectionType, rem []byte, ok bool) {\n\tif len(src) < 1 {\n\t\treturn 0, src, false\n\t}\n\treturn SectionType(src[0]), src[1:], true\n}\n\n// ReadMsgSectionSingleDocument reads a single document from src.\nfunc ReadMsgSectionSingleDocument(src []byte) (doc bsoncore.Document, rem []byte, ok bool) {\n\treturn bsoncore.ReadDocument(src)\n}\n\n// ReadMsgSectionDocumentSequence reads an identifier and document sequence from src and returns the document sequence\n// data parsed into a slice of BSON documents.\nfunc ReadMsgSectionDocumentSequence(src []byte) (identifier string, docs []bsoncore.Document, rem []byte, ok bool) {\n\tidentifier, rem, ret, ok := ReadMsgSectionRawDocumentSequence(src)\n\tif !ok {\n\t\treturn \"\", nil, src, false\n\t}\n\n\tdocs = make([]bsoncore.Document, 0)\n\tvar doc bsoncore.Document\n\tfor {\n\t\tdoc, rem, ok = bsoncore.ReadDocument(rem)\n\t\tif !ok {\n\t\t\tbreak\n\t\t}\n\t\tdocs = append(docs, doc)\n\t}\n\tif len(rem) > 0 {\n\t\treturn \"\", nil, src, false\n\t}\n\n\treturn identifier, docs, ret, true\n}\n\n// ReadMsgSectionRawDocumentSequence reads an identifier and document sequence from src and returns the raw document\n// sequence data.\nfunc ReadMsgSectionRawDocumentSequence(src []byte) (identifier string, data []byte, rem []byte, ok bool) {\n\tlength, rem, ok := binaryutil.ReadI32(src)\n\tif !ok || int(length) > len(src) || length < 4 {\n\t\treturn \"\", nil, src, false\n\t}\n\n\t// After these assignments, rem will be the data containing the identifier string + the document sequence bytes and\n\t// rest will be the rest of the wire message after this document sequence.\n\trem, rest := rem[:length-4], rem[length-4:]\n\n\tidentifier, rem, ok = binaryutil.ReadCString(rem)\n\tif !ok {\n\t\treturn \"\", nil, src, false\n\t}\n\n\treturn identifier, rem, rest, true\n}\n\n// ReadMsgChecksum reads a checksum from src.\nfunc ReadMsgChecksum(src []byte) (checksum uint32, rem []byte, ok bool) {\n\ti32, rem, ok := binaryutil.ReadI32(src)\n\treturn uint32(i32), rem, ok\n}\n\n// ReadQueryFlags reads OP_QUERY flags from src.\n//\n// Deprecated: Construct wiremessages with OpMsg and use the ReadMsg* functions\n// instead.\nfunc ReadQueryFlags(src []byte) (flags QueryFlag, rem []byte, ok bool) {\n\ti32, rem, ok := binaryutil.ReadI32(src)\n\treturn QueryFlag(i32), rem, ok\n}\n\n// ReadQueryFullCollectionName reads the full collection name from src.\n//\n// Deprecated: Construct wiremessages with OpMsg and use the ReadMsg* functions\n// instead.\nfunc ReadQueryFullCollectionName(src []byte) (collname string, rem []byte, ok bool) {\n\treturn binaryutil.ReadCString(src)\n}\n\n// ReadQueryNumberToSkip reads the number to skip from src.\n//\n// Deprecated: Construct wiremessages with OpMsg and use the ReadMsg* functions\n// instead.\nfunc ReadQueryNumberToSkip(src []byte) (nts int32, rem []byte, ok bool) {\n\treturn binaryutil.ReadI32(src)\n}\n\n// ReadQueryNumberToReturn reads the number to return from src.\n//\n// Deprecated: Construct wiremessages with OpMsg and use the ReadMsg* functions\n// instead.\nfunc ReadQueryNumberToReturn(src []byte) (ntr int32, rem []byte, ok bool) {\n\treturn binaryutil.ReadI32(src)\n}\n\n// ReadQueryQuery reads the query from src.\n//\n// Deprecated: Construct wiremessages with OpMsg and use the ReadMsg* functions\n// instead.\nfunc ReadQueryQuery(src []byte) (query bsoncore.Document, rem []byte, ok bool) {\n\treturn bsoncore.ReadDocument(src)\n}\n\n// ReadQueryReturnFieldsSelector reads a return fields selector document from src.\n//\n// Deprecated: Construct wiremessages with OpMsg and use the ReadMsg* functions\n// instead.\nfunc ReadQueryReturnFieldsSelector(src []byte) (rfs bsoncore.Document, rem []byte, ok bool) {\n\treturn bsoncore.ReadDocument(src)\n}\n\n// ReadReplyFlags reads OP_REPLY flags from src.\nfunc ReadReplyFlags(src []byte) (flags ReplyFlag, rem []byte, ok bool) {\n\ti32, rem, ok := binaryutil.ReadI32(src)\n\treturn ReplyFlag(i32), rem, ok\n}\n\n// ReadReplyCursorID reads a cursor ID from src.\nfunc ReadReplyCursorID(src []byte) (cursorID int64, rem []byte, ok bool) {\n\treturn binaryutil.ReadI64(src)\n}\n\n// ReadReplyStartingFrom reads the starting from src.\nfunc ReadReplyStartingFrom(src []byte) (startingFrom int32, rem []byte, ok bool) {\n\treturn binaryutil.ReadI32(src)\n}\n\n// ReadReplyNumberReturned reads the numbered returned from src.\nfunc ReadReplyNumberReturned(src []byte) (numberReturned int32, rem []byte, ok bool) {\n\treturn binaryutil.ReadI32(src)\n}\n\n// ReadReplyDocuments reads as many documents as possible from src\nfunc ReadReplyDocuments(src []byte) (docs []bsoncore.Document, rem []byte, ok bool) {\n\trem = src\n\tfor {\n\t\tvar doc bsoncore.Document\n\t\tdoc, rem, ok = bsoncore.ReadDocument(rem)\n\t\tif !ok {\n\t\t\tbreak\n\t\t}\n\n\t\tdocs = append(docs, doc)\n\t}\n\n\treturn docs, rem, true\n}\n\n// ReadReplyDocument reads a reply document from src.\nfunc ReadReplyDocument(src []byte) (doc bsoncore.Document, rem []byte, ok bool) {\n\treturn bsoncore.ReadDocument(src)\n}\n\n// ReadCompressedOriginalOpCode reads the original opcode from src.\nfunc ReadCompressedOriginalOpCode(src []byte) (opcode OpCode, rem []byte, ok bool) {\n\ti32, rem, ok := binaryutil.ReadI32(src)\n\treturn OpCode(i32), rem, ok\n}\n\n// ReadCompressedUncompressedSize reads the uncompressed size of a\n// compressed wiremessage to dst.\nfunc ReadCompressedUncompressedSize(src []byte) (size int32, rem []byte, ok bool) {\n\treturn binaryutil.ReadI32(src)\n}\n\n// ReadCompressedCompressorID reads the ID of the compressor to dst.\nfunc ReadCompressedCompressorID(src []byte) (id CompressorID, rem []byte, ok bool) {\n\tif len(src) < 1 {\n\t\treturn 0, src, false\n\t}\n\treturn CompressorID(src[0]), src[1:], true\n}\n\n// ReadKillCursorsZero reads the zero field from src.\nfunc ReadKillCursorsZero(src []byte) (zero int32, rem []byte, ok bool) {\n\treturn binaryutil.ReadI32(src)\n}\n\n// ReadKillCursorsNumberIDs reads the numberOfCursorIDs field from src.\nfunc ReadKillCursorsNumberIDs(src []byte) (numIDs int32, rem []byte, ok bool) {\n\treturn binaryutil.ReadI32(src)\n}\n\n// ReadKillCursorsCursorIDs reads numIDs cursor IDs from src.\nfunc ReadKillCursorsCursorIDs(src []byte, numIDs int32) (cursorIDs []int64, rem []byte, ok bool) {\n\tvar i int32\n\tvar id int64\n\tfor i = 0; i < numIDs; i++ {\n\t\tid, src, ok = binaryutil.ReadI64(src)\n\t\tif !ok {\n\t\t\treturn cursorIDs, src, false\n\t\t}\n\n\t\tcursorIDs = append(cursorIDs, id)\n\t}\n\treturn cursorIDs, src, true\n}\n\nfunc appendCString(b []byte, str string) []byte {\n\tb = append(b, str...)\n\treturn append(b, 0x00)\n}\n"
  },
  {
    "path": "x/mongo/driver/wiremessage/wiremessage_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2024-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage wiremessage\n\nimport (\n\t\"math\"\n\t\"testing\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/assert\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/binaryutil\"\n\t\"go.mongodb.org/mongo-driver/v2/x/bsonx/bsoncore\"\n)\n\nfunc TestAppendHeaderStart(t *testing.T) {\n\ttestCases := []struct {\n\t\tdesc      string\n\t\tdst       []byte\n\t\treqid     int32\n\t\trespto    int32\n\t\topcode    OpCode\n\t\twantIdx   int32\n\t\twantBytes []byte\n\t}{\n\t\t{\n\t\t\tdesc:      \"OP_MSG\",\n\t\t\treqid:     2,\n\t\t\trespto:    1,\n\t\t\topcode:    OpMsg,\n\t\t\twantIdx:   0,\n\t\t\twantBytes: []byte{0, 0, 0, 0, 2, 0, 0, 0, 1, 0, 0, 0, 221, 7, 0, 0},\n\t\t},\n\t\t{\n\t\t\tdesc:      \"OP_QUERY\",\n\t\t\treqid:     2,\n\t\t\trespto:    1,\n\t\t\topcode:    OpQuery,\n\t\t\twantIdx:   0,\n\t\t\twantBytes: []byte{0, 0, 0, 0, 2, 0, 0, 0, 1, 0, 0, 0, 212, 7, 0, 0},\n\t\t},\n\t\t{\n\t\t\tdesc:      \"non-empty buffer\",\n\t\t\tdst:       []byte{0, 99},\n\t\t\treqid:     2,\n\t\t\trespto:    1,\n\t\t\topcode:    OpMsg,\n\t\t\twantIdx:   2,\n\t\t\twantBytes: []byte{0, 99, 0, 0, 0, 0, 2, 0, 0, 0, 1, 0, 0, 0, 221, 7, 0, 0},\n\t\t},\n\t}\n\n\tfor _, tc := range testCases {\n\t\ttc := tc // Capture range variable.\n\n\t\tt.Run(tc.desc, func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tidx, b := AppendHeaderStart(tc.dst, tc.reqid, tc.respto, tc.opcode)\n\t\t\tassert.Equal(t, tc.wantIdx, idx, \"appended slice index does not match\")\n\t\t\tassert.Equal(t, tc.wantBytes, b, \"appended bytes do not match\")\n\t\t})\n\t}\n}\n\nfunc TestReadHeader(t *testing.T) {\n\ttestCases := []struct {\n\t\tdesc           string\n\t\tsrc            []byte\n\t\twantLength     int32\n\t\twantRequestID  int32\n\t\twantResponseTo int32\n\t\twantOpcode     OpCode\n\t\twantRem        []byte\n\t\twantOK         bool\n\t}{\n\t\t{\n\t\t\tdesc:           \"OP_MSG\",\n\t\t\tsrc:            []byte{0, 0, 0, 0, 2, 0, 0, 0, 1, 0, 0, 0, 221, 7, 0, 0},\n\t\t\twantLength:     0,\n\t\t\twantRequestID:  2,\n\t\t\twantResponseTo: 1,\n\t\t\twantOpcode:     OpMsg,\n\t\t\twantRem:        []byte{},\n\t\t\twantOK:         true,\n\t\t},\n\t\t{\n\t\t\tdesc:           \"OP_QUERY\",\n\t\t\tsrc:            []byte{0, 0, 0, 0, 2, 0, 0, 0, 1, 0, 0, 0, 212, 7, 0, 0},\n\t\t\twantLength:     0,\n\t\t\twantRequestID:  2,\n\t\t\twantResponseTo: 1,\n\t\t\twantOpcode:     OpQuery,\n\t\t\twantRem:        []byte{},\n\t\t\twantOK:         true,\n\t\t},\n\t\t{\n\t\t\tdesc:           \"not enough bytes\",\n\t\t\tsrc:            []byte{0, 99},\n\t\t\twantLength:     0,\n\t\t\twantRequestID:  0,\n\t\t\twantResponseTo: 0,\n\t\t\twantOpcode:     0,\n\t\t\twantRem:        []byte{0, 99},\n\t\t\twantOK:         false,\n\t\t},\n\t\t{\n\t\t\tdesc:           \"nil\",\n\t\t\tsrc:            nil,\n\t\t\twantLength:     0,\n\t\t\twantRequestID:  0,\n\t\t\twantResponseTo: 0,\n\t\t\twantOpcode:     0,\n\t\t\twantRem:        nil,\n\t\t\twantOK:         false,\n\t\t},\n\t}\n\n\tfor _, tc := range testCases {\n\t\ttc := tc // Capture range variable.\n\n\t\tt.Run(tc.desc, func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tlength, requestID, responseTo, opcode, rem, ok := ReadHeader(tc.src)\n\t\t\tassert.Equal(t, tc.wantLength, length, \"length does not match\")\n\t\t\tassert.Equal(t, tc.wantRequestID, requestID, \"requestID does not match\")\n\t\t\tassert.Equal(t, tc.wantResponseTo, responseTo, \"responseTo does not match\")\n\t\t\tassert.Equal(t, tc.wantOpcode, opcode, \"OpCode does not match\")\n\t\t\tassert.Equal(t, tc.wantRem, rem, \"remaining bytes do not match\")\n\t\t\tassert.Equal(t, tc.wantOK, ok, \"OK does not match\")\n\t\t})\n\t}\n}\n\nfunc TestReadMsgSectionDocumentSequence(t *testing.T) {\n\ttestCases := []struct {\n\t\tdesc           string\n\t\tsrc            []byte\n\t\twantIdentifier string\n\t\twantDocs       []bsoncore.Document\n\t\twantRem        []byte\n\t\twantOK         bool\n\t}{\n\t\t{\n\t\t\tdesc: \"valid document sequence\",\n\t\t\t// Data:              |  len=17   |    \"id\"    |  empty doc   |  empty doc   |\n\t\t\tsrc:            []byte{17, 0, 0, 0, 105, 100, 0, 5, 0, 0, 0, 0, 5, 0, 0, 0, 0},\n\t\t\twantIdentifier: \"id\",\n\t\t\twantDocs: []bsoncore.Document{\n\t\t\t\t{0x5, 0x0, 0x0, 0x0, 0x0},\n\t\t\t\t{0x5, 0x0, 0x0, 0x0, 0x0},\n\t\t\t},\n\t\t\twantRem: []byte{},\n\t\t\twantOK:  true,\n\t\t},\n\t\t{\n\t\t\tdesc: \"valid document sequence with remaining bytes\",\n\t\t\t// Data:              |  len=17   |    \"id\"    |  empty doc   |  empty doc   |  rem  |\n\t\t\tsrc:            []byte{17, 0, 0, 0, 105, 100, 0, 5, 0, 0, 0, 0, 5, 0, 0, 0, 0, 99, 99},\n\t\t\twantIdentifier: \"id\",\n\t\t\twantDocs: []bsoncore.Document{\n\t\t\t\t{0x5, 0x0, 0x0, 0x0, 0x0},\n\t\t\t\t{0x5, 0x0, 0x0, 0x0, 0x0},\n\t\t\t},\n\t\t\twantRem: []byte{99, 99},\n\t\t\twantOK:  true,\n\t\t},\n\t\t{\n\t\t\tdesc:           \"not enough bytes\",\n\t\t\tsrc:            []byte{0, 1},\n\t\t\twantIdentifier: \"\",\n\t\t\twantDocs:       nil,\n\t\t\twantRem:        []byte{0, 1},\n\t\t\twantOK:         false,\n\t\t},\n\t\t{\n\t\t\tdesc:           \"incorrect size\",\n\t\t\tsrc:            []byte{3, 0, 0},\n\t\t\twantIdentifier: \"\",\n\t\t\twantDocs:       nil,\n\t\t\twantRem:        []byte{3, 0, 0},\n\t\t\twantOK:         false,\n\t\t},\n\t\t{\n\t\t\tdesc:           \"insufficient size\",\n\t\t\tsrc:            []byte{4, 0, 0},\n\t\t\twantIdentifier: \"\",\n\t\t\twantDocs:       nil,\n\t\t\twantRem:        []byte{4, 0, 0},\n\t\t\twantOK:         false,\n\t\t},\n\t\t{\n\t\t\tdesc:           \"nil\",\n\t\t\tsrc:            nil,\n\t\t\twantIdentifier: \"\",\n\t\t\twantDocs:       nil,\n\t\t\twantRem:        nil,\n\t\t\twantOK:         false,\n\t\t},\n\t}\n\n\tfor _, tc := range testCases {\n\t\ttc := tc // Capture range variable.\n\n\t\tt.Run(tc.desc, func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tidentifier, docs, rem, ok := ReadMsgSectionDocumentSequence(tc.src)\n\t\t\tassert.Equal(t, tc.wantIdentifier, identifier, \"identifier does not match\")\n\t\t\tassert.Equal(t, tc.wantDocs, docs, \"docs do not match\")\n\t\t\tassert.Equal(t, tc.wantRem, rem, \"responseTo does not match\")\n\t\t\tassert.Equal(t, tc.wantOK, ok, \"OK does not match\")\n\t\t})\n\t}\n}\n\nfunc TestAppendi32(t *testing.T) {\n\ttestCases := []struct {\n\t\tdesc string\n\t\tdst  []byte\n\t\tx    int32\n\t\twant []byte\n\t}{\n\t\t{\n\t\t\tdesc: \"0\",\n\t\t\tx:    0,\n\t\t\twant: []byte{0, 0, 0, 0},\n\t\t},\n\t\t{\n\t\t\tdesc: \"1\",\n\t\t\tx:    1,\n\t\t\twant: []byte{1, 0, 0, 0},\n\t\t},\n\t\t{\n\t\t\tdesc: \"-1\",\n\t\t\tx:    -1,\n\t\t\twant: []byte{255, 255, 255, 255},\n\t\t},\n\t\t{\n\t\t\tdesc: \"max\",\n\t\t\tx:    math.MaxInt32,\n\t\t\twant: []byte{255, 255, 255, 127},\n\t\t},\n\t\t{\n\t\t\tdesc: \"min\",\n\t\t\tx:    math.MinInt32,\n\t\t\twant: []byte{0, 0, 0, 128},\n\t\t},\n\t\t{\n\t\t\tdesc: \"non-empty dst\",\n\t\t\tdst:  []byte{0, 1, 2, 3},\n\t\t\tx:    1,\n\t\t\twant: []byte{0, 1, 2, 3, 1, 0, 0, 0},\n\t\t},\n\t}\n\n\tfor _, tc := range testCases {\n\t\ttc := tc // Capture range variable.\n\n\t\tt.Run(tc.desc, func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tb := binaryutil.Append32(tc.dst, tc.x)\n\t\t\tassert.Equal(t, tc.want, b, \"bytes do not match\")\n\t\t})\n\t}\n}\n\nfunc TestAppendi64(t *testing.T) {\n\ttestCases := []struct {\n\t\tdesc string\n\t\tdst  []byte\n\t\tx    int64\n\t\twant []byte\n\t}{\n\t\t{\n\t\t\tdesc: \"0\",\n\t\t\tx:    0,\n\t\t\twant: []byte{0, 0, 0, 0, 0, 0, 0, 0},\n\t\t},\n\t\t{\n\t\t\tdesc: \"1\",\n\t\t\tx:    1,\n\t\t\twant: []byte{1, 0, 0, 0, 0, 0, 0, 0},\n\t\t},\n\t\t{\n\t\t\tdesc: \"-1\",\n\t\t\tx:    -1,\n\t\t\twant: []byte{255, 255, 255, 255, 255, 255, 255, 255},\n\t\t},\n\t\t{\n\t\t\tdesc: \"max\",\n\t\t\tx:    math.MaxInt64,\n\t\t\twant: []byte{255, 255, 255, 255, 255, 255, 255, 127},\n\t\t},\n\t\t{\n\t\t\tdesc: \"min\",\n\t\t\tx:    math.MinInt64,\n\t\t\twant: []byte{0, 0, 0, 0, 0, 0, 0, 128},\n\t\t},\n\t\t{\n\t\t\tdesc: \"non-empty dst\",\n\t\t\tdst:  []byte{0, 1, 2, 3},\n\t\t\tx:    1,\n\t\t\twant: []byte{0, 1, 2, 3, 1, 0, 0, 0, 0, 0, 0, 0},\n\t\t},\n\t}\n\n\tfor _, tc := range testCases {\n\t\ttc := tc // Capture range variable.\n\n\t\tt.Run(tc.desc, func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tb := binaryutil.Append64(tc.dst, tc.x)\n\t\t\tassert.Equal(t, tc.want, b, \"bytes do not match\")\n\t\t})\n\t}\n}\n\nfunc TestReadi32(t *testing.T) {\n\ttestCases := []struct {\n\t\tdesc    string\n\t\tsrc     []byte\n\t\twant    int32\n\t\twantRem []byte\n\t\twantOK  bool\n\t}{\n\t\t{\n\t\t\tdesc:    \"0\",\n\t\t\tsrc:     []byte{0, 0, 0, 0},\n\t\t\twant:    0,\n\t\t\twantRem: []byte{},\n\t\t\twantOK:  true,\n\t\t},\n\t\t{\n\t\t\tdesc:    \"1\",\n\t\t\tsrc:     []byte{1, 0, 0, 0},\n\t\t\twant:    1,\n\t\t\twantRem: []byte{},\n\t\t\twantOK:  true,\n\t\t},\n\t\t{\n\t\t\tdesc:    \"-1\",\n\t\t\tsrc:     []byte{255, 255, 255, 255},\n\t\t\twant:    -1,\n\t\t\twantRem: []byte{},\n\t\t\twantOK:  true,\n\t\t},\n\t\t{\n\t\t\tdesc:    \"max\",\n\t\t\tsrc:     []byte{255, 255, 255, 127},\n\t\t\twant:    math.MaxInt32,\n\t\t\twantRem: []byte{},\n\t\t\twantOK:  true,\n\t\t},\n\t\t{\n\t\t\tdesc:    \"min\",\n\t\t\tsrc:     []byte{0, 0, 0, 128},\n\t\t\twant:    math.MinInt32,\n\t\t\twantRem: []byte{},\n\t\t\twantOK:  true,\n\t\t},\n\t\t{\n\t\t\tdesc:    \"non-empty remaining\",\n\t\t\tsrc:     []byte{1, 0, 0, 0, 0, 1, 2, 3},\n\t\t\twant:    1,\n\t\t\twantRem: []byte{0, 1, 2, 3},\n\t\t\twantOK:  true,\n\t\t},\n\t\t{\n\t\t\tdesc:    \"not enough bytes\",\n\t\t\tsrc:     []byte{0, 1, 2},\n\t\t\twant:    0,\n\t\t\twantRem: []byte{0, 1, 2},\n\t\t\twantOK:  false,\n\t\t},\n\t\t{\n\t\t\tdesc:    \"nil\",\n\t\t\tsrc:     nil,\n\t\t\twant:    0,\n\t\t\twantRem: nil,\n\t\t\twantOK:  false,\n\t\t},\n\t}\n\n\tfor _, tc := range testCases {\n\t\ttc := tc // Capture range variable.\n\n\t\tt.Run(tc.desc, func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tx, rem, ok := binaryutil.ReadI32(tc.src)\n\t\t\tassert.Equal(t, tc.want, x, \"int32 result does not match\")\n\t\t\tassert.Equal(t, tc.wantRem, rem, \"remaining bytes do not match\")\n\t\t\tassert.Equal(t, tc.wantOK, ok, \"OK does not match\")\n\t\t})\n\t}\n}\n\nfunc TestReadi64(t *testing.T) {\n\ttestCases := []struct {\n\t\tdesc    string\n\t\tsrc     []byte\n\t\twant    int64\n\t\twantRem []byte\n\t\twantOK  bool\n\t}{\n\t\t{\n\t\t\tdesc:    \"0\",\n\t\t\tsrc:     []byte{0, 0, 0, 0, 0, 0, 0, 0},\n\t\t\twant:    0,\n\t\t\twantRem: []byte{},\n\t\t\twantOK:  true,\n\t\t},\n\t\t{\n\t\t\tdesc:    \"1\",\n\t\t\tsrc:     []byte{1, 0, 0, 0, 0, 0, 0, 0},\n\t\t\twant:    1,\n\t\t\twantRem: []byte{},\n\t\t\twantOK:  true,\n\t\t},\n\t\t{\n\t\t\tdesc:    \"-1\",\n\t\t\tsrc:     []byte{255, 255, 255, 255, 255, 255, 255, 255},\n\t\t\twant:    -1,\n\t\t\twantRem: []byte{},\n\t\t\twantOK:  true,\n\t\t},\n\t\t{\n\t\t\tdesc:    \"max\",\n\t\t\tsrc:     []byte{255, 255, 255, 255, 255, 255, 255, 127},\n\t\t\twant:    math.MaxInt64,\n\t\t\twantRem: []byte{},\n\t\t\twantOK:  true,\n\t\t},\n\t\t{\n\t\t\tdesc:    \"min\",\n\t\t\tsrc:     []byte{0, 0, 0, 0, 0, 0, 0, 128},\n\t\t\twant:    math.MinInt64,\n\t\t\twantRem: []byte{},\n\t\t\twantOK:  true,\n\t\t},\n\t\t{\n\t\t\tdesc:    \"non-empty remaining\",\n\t\t\tsrc:     []byte{1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3},\n\t\t\twant:    1,\n\t\t\twantRem: []byte{0, 1, 2, 3},\n\t\t\twantOK:  true,\n\t\t},\n\t\t{\n\t\t\tdesc:    \"not enough bytes\",\n\t\t\tsrc:     []byte{0, 1, 2, 3, 4, 5, 6},\n\t\t\twant:    0,\n\t\t\twantRem: []byte{0, 1, 2, 3, 4, 5, 6},\n\t\t\twantOK:  false,\n\t\t},\n\t\t{\n\t\t\tdesc:    \"not enough bytes\",\n\t\t\tsrc:     []byte{0, 1, 2, 3, 4, 5, 6},\n\t\t\twant:    0,\n\t\t\twantRem: []byte{0, 1, 2, 3, 4, 5, 6},\n\t\t\twantOK:  false,\n\t\t},\n\t\t{\n\t\t\tdesc:    \"nil\",\n\t\t\tsrc:     nil,\n\t\t\twant:    0,\n\t\t\twantRem: nil,\n\t\t\twantOK:  false,\n\t\t},\n\t}\n\n\tfor _, tc := range testCases {\n\t\ttc := tc // Capture range variable.\n\n\t\tt.Run(tc.desc, func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\tx, rem, ok := binaryutil.ReadI64(tc.src)\n\t\t\tassert.Equal(t, tc.want, x, \"int64 result does not match\")\n\t\t\tassert.Equal(t, tc.wantRem, rem, \"remaining bytes do not match\")\n\t\t\tassert.Equal(t, tc.wantOK, ok, \"OK does not match\")\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "x/mongo/driver/xoptions/options.go",
    "content": "// Copyright (C) MongoDB, Inc. 2025-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage xoptions\n\nimport (\n\t\"fmt\"\n\n\t\"go.mongodb.org/mongo-driver/v2/bson\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/optionsutil\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver\"\n)\n\n// SetInternalClientOptions sets internal options for ClientOptions.\nfunc SetInternalClientOptions(opts *options.ClientOptions, key string, option any) error {\n\ttypeErrFunc := func(t string) error {\n\t\treturn fmt.Errorf(\"unexpected type for %q: %T is not %s\", key, option, t)\n\t}\n\tswitch key {\n\tcase \"crypt\":\n\t\tc, ok := option.(driver.Crypt)\n\t\tif !ok {\n\t\t\treturn typeErrFunc(\"driver.Crypt\")\n\t\t}\n\t\topts.Crypt = c\n\tcase \"deployment\":\n\t\td, ok := option.(driver.Deployment)\n\t\tif !ok {\n\t\t\treturn typeErrFunc(\"driver.Deployment\")\n\t\t}\n\t\topts.Deployment = d\n\tcase \"authenticateToAnything\":\n\t\tb, ok := option.(bool)\n\t\tif !ok {\n\t\t\treturn typeErrFunc(\"bool\")\n\t\t}\n\t\topts.Custom = optionsutil.WithValue(opts.Custom, key, b)\n\tdefault:\n\t\treturn fmt.Errorf(\"unsupported option: %q\", key)\n\t}\n\treturn nil\n}\n\n// SetInternalAggregateOptions sets internal options for AggregateOptions.\nfunc SetInternalAggregateOptions(a *options.AggregateOptionsBuilder, key string, option any) error {\n\ttypeErrFunc := func(t string) error {\n\t\treturn fmt.Errorf(\"unexpected type for %q: %T is not %s\", key, option, t)\n\t}\n\tswitch key {\n\tcase \"rawData\":\n\t\tb, ok := option.(bool)\n\t\tif !ok {\n\t\t\treturn typeErrFunc(\"bool\")\n\t\t}\n\t\ta.Opts = append(a.Opts, func(opts *options.AggregateOptions) error {\n\t\t\topts.Internal = optionsutil.WithValue(opts.Internal, key, b)\n\t\t\treturn nil\n\t\t})\n\tdefault:\n\t\treturn fmt.Errorf(\"unsupported option: %q\", key)\n\t}\n\treturn nil\n}\n\n// SetInternalBulkWriteOptions sets internal options for BulkWriteOptions.\nfunc SetInternalBulkWriteOptions(a *options.BulkWriteOptionsBuilder, key string, option any) error {\n\ttypeErrFunc := func(t string) error {\n\t\treturn fmt.Errorf(\"unexpected type for %q: %T is not %s\", key, option, t)\n\t}\n\tswitch key {\n\tcase \"rawData\":\n\t\tb, ok := option.(bool)\n\t\tif !ok {\n\t\t\treturn typeErrFunc(\"bool\")\n\t\t}\n\t\ta.Opts = append(a.Opts, func(opts *options.BulkWriteOptions) error {\n\t\t\topts.Internal = optionsutil.WithValue(opts.Internal, key, b)\n\t\t\treturn nil\n\t\t})\n\tcase \"addCommandFields\":\n\t\td, ok := option.(bson.D)\n\t\tif !ok {\n\t\t\treturn typeErrFunc(\"bson.D\")\n\t\t}\n\t\ta.Opts = append(a.Opts, func(opts *options.BulkWriteOptions) error {\n\t\t\topts.Internal = optionsutil.WithValue(opts.Internal, key, d)\n\t\t\treturn nil\n\t\t})\n\tdefault:\n\t\treturn fmt.Errorf(\"unsupported option: %q\", key)\n\t}\n\treturn nil\n}\n\n// SetInternalClientBulkWriteOptions sets internal options for ClientBulkWriteOptions.\nfunc SetInternalClientBulkWriteOptions(a *options.ClientBulkWriteOptionsBuilder, key string, option any) error {\n\ttypeErrFunc := func(t string) error {\n\t\treturn fmt.Errorf(\"unexpected type for %q: %T is not %s\", key, option, t)\n\t}\n\tswitch key {\n\tcase \"rawData\":\n\t\tb, ok := option.(bool)\n\t\tif !ok {\n\t\t\treturn typeErrFunc(\"bool\")\n\t\t}\n\t\ta.Opts = append(a.Opts, func(opts *options.ClientBulkWriteOptions) error {\n\t\t\topts.Internal = optionsutil.WithValue(opts.Internal, key, b)\n\t\t\treturn nil\n\t\t})\n\tcase \"addCommandFields\":\n\t\td, ok := option.(bson.D)\n\t\tif !ok {\n\t\t\treturn typeErrFunc(\"bson.D\")\n\t\t}\n\t\ta.Opts = append(a.Opts, func(opts *options.ClientBulkWriteOptions) error {\n\t\t\topts.Internal = optionsutil.WithValue(opts.Internal, key, d)\n\t\t\treturn nil\n\t\t})\n\tdefault:\n\t\treturn fmt.Errorf(\"unsupported option: %q\", key)\n\t}\n\treturn nil\n}\n\n// SetInternalCountOptions sets internal options for CountOptions.\nfunc SetInternalCountOptions(a *options.CountOptionsBuilder, key string, option any) error {\n\ttypeErrFunc := func(t string) error {\n\t\treturn fmt.Errorf(\"unexpected type for %q: %T is not %s\", key, option, t)\n\t}\n\tswitch key {\n\tcase \"rawData\":\n\t\tb, ok := option.(bool)\n\t\tif !ok {\n\t\t\treturn typeErrFunc(\"bool\")\n\t\t}\n\t\ta.Opts = append(a.Opts, func(opts *options.CountOptions) error {\n\t\t\topts.Internal = optionsutil.WithValue(opts.Internal, key, b)\n\t\t\treturn nil\n\t\t})\n\tdefault:\n\t\treturn fmt.Errorf(\"unsupported option: %q\", key)\n\t}\n\treturn nil\n}\n\n// SetInternalCreateIndexesOptions sets internal options for CreateIndexesOptions.\nfunc SetInternalCreateIndexesOptions(a *options.CreateIndexesOptionsBuilder, key string, option any) error {\n\ttypeErrFunc := func(t string) error {\n\t\treturn fmt.Errorf(\"unexpected type for %q: %T is not %s\", key, option, t)\n\t}\n\tswitch key {\n\tcase \"rawData\":\n\t\tb, ok := option.(bool)\n\t\tif !ok {\n\t\t\treturn typeErrFunc(\"bool\")\n\t\t}\n\t\ta.Opts = append(a.Opts, func(opts *options.CreateIndexesOptions) error {\n\t\t\topts.Internal = optionsutil.WithValue(opts.Internal, key, b)\n\t\t\treturn nil\n\t\t})\n\tdefault:\n\t\treturn fmt.Errorf(\"unsupported option: %q\", key)\n\t}\n\treturn nil\n}\n\n// SetInternalDeleteOneOptions sets internal options for DeleteOneOptions.\nfunc SetInternalDeleteOneOptions(a *options.DeleteOneOptionsBuilder, key string, option any) error {\n\ttypeErrFunc := func(t string) error {\n\t\treturn fmt.Errorf(\"unexpected type for %q: %T is not %s\", key, option, t)\n\t}\n\tswitch key {\n\tcase \"rawData\":\n\t\tb, ok := option.(bool)\n\t\tif !ok {\n\t\t\treturn typeErrFunc(\"bool\")\n\t\t}\n\t\ta.Opts = append(a.Opts, func(opts *options.DeleteOneOptions) error {\n\t\t\topts.Internal = optionsutil.WithValue(opts.Internal, key, b)\n\t\t\treturn nil\n\t\t})\n\tdefault:\n\t\treturn fmt.Errorf(\"unsupported option: %q\", key)\n\t}\n\treturn nil\n}\n\n// SetInternalDeleteManyOptions sets internal options for DeleteManyOptions.\nfunc SetInternalDeleteManyOptions(a *options.DeleteManyOptionsBuilder, key string, option any) error {\n\ttypeErrFunc := func(t string) error {\n\t\treturn fmt.Errorf(\"unexpected type for %q: %T is not %s\", key, option, t)\n\t}\n\tswitch key {\n\tcase \"rawData\":\n\t\tb, ok := option.(bool)\n\t\tif !ok {\n\t\t\treturn typeErrFunc(\"bool\")\n\t\t}\n\t\ta.Opts = append(a.Opts, func(opts *options.DeleteManyOptions) error {\n\t\t\topts.Internal = optionsutil.WithValue(opts.Internal, key, b)\n\t\t\treturn nil\n\t\t})\n\tdefault:\n\t\treturn fmt.Errorf(\"unsupported option: %q\", key)\n\t}\n\treturn nil\n}\n\n// SetInternalDistinctOptions sets internal options for DistinctOptions.\nfunc SetInternalDistinctOptions(a *options.DistinctOptionsBuilder, key string, option any) error {\n\ttypeErrFunc := func(t string) error {\n\t\treturn fmt.Errorf(\"unexpected type for %q: %T is not %s\", key, option, t)\n\t}\n\tswitch key {\n\tcase \"rawData\":\n\t\tb, ok := option.(bool)\n\t\tif !ok {\n\t\t\treturn typeErrFunc(\"bool\")\n\t\t}\n\t\ta.Opts = append(a.Opts, func(opts *options.DistinctOptions) error {\n\t\t\topts.Internal = optionsutil.WithValue(opts.Internal, key, b)\n\t\t\treturn nil\n\t\t})\n\tdefault:\n\t\treturn fmt.Errorf(\"unsupported option: %q\", key)\n\t}\n\treturn nil\n}\n\n// SetInternalDropIndexesOptions sets internal options for DropIndexesOptions.\nfunc SetInternalDropIndexesOptions(a *options.DropIndexesOptionsBuilder, key string, option any) error {\n\ttypeErrFunc := func(t string) error {\n\t\treturn fmt.Errorf(\"unexpected type for %q: %T is not %s\", key, option, t)\n\t}\n\tswitch key {\n\tcase \"rawData\":\n\t\tb, ok := option.(bool)\n\t\tif !ok {\n\t\t\treturn typeErrFunc(\"bool\")\n\t\t}\n\t\ta.Opts = append(a.Opts, func(opts *options.DropIndexesOptions) error {\n\t\t\topts.Internal = optionsutil.WithValue(opts.Internal, key, b)\n\t\t\treturn nil\n\t\t})\n\tdefault:\n\t\treturn fmt.Errorf(\"unsupported option: %q\", key)\n\t}\n\treturn nil\n}\n\n// SetInternalEstimatedDocumentCountOptions sets internal options for EstimatedDocumentCountOptions.\nfunc SetInternalEstimatedDocumentCountOptions(a *options.EstimatedDocumentCountOptionsBuilder, key string, option any) error {\n\ttypeErrFunc := func(t string) error {\n\t\treturn fmt.Errorf(\"unexpected type for %q: %T is not %s\", key, option, t)\n\t}\n\tswitch key {\n\tcase \"rawData\":\n\t\tb, ok := option.(bool)\n\t\tif !ok {\n\t\t\treturn typeErrFunc(\"bool\")\n\t\t}\n\t\ta.Opts = append(a.Opts, func(opts *options.EstimatedDocumentCountOptions) error {\n\t\t\topts.Internal = optionsutil.WithValue(opts.Internal, key, b)\n\t\t\treturn nil\n\t\t})\n\tdefault:\n\t\treturn fmt.Errorf(\"unsupported option: %q\", key)\n\t}\n\treturn nil\n}\n\n// SetInternalFindOptions sets internal options for FindOptions.\nfunc SetInternalFindOptions(a *options.FindOptionsBuilder, key string, option any) error {\n\ttypeErrFunc := func(t string) error {\n\t\treturn fmt.Errorf(\"unexpected type for %q: %T is not %s\", key, option, t)\n\t}\n\tswitch key {\n\tcase \"rawData\":\n\t\tb, ok := option.(bool)\n\t\tif !ok {\n\t\t\treturn typeErrFunc(\"bool\")\n\t\t}\n\t\ta.Opts = append(a.Opts, func(opts *options.FindOptions) error {\n\t\t\topts.Internal = optionsutil.WithValue(opts.Internal, key, b)\n\t\t\treturn nil\n\t\t})\n\tdefault:\n\t\treturn fmt.Errorf(\"unsupported option: %q\", key)\n\t}\n\treturn nil\n}\n\n// SetInternalFindOneOptions sets internal options for FindOneOptions.\nfunc SetInternalFindOneOptions(a *options.FindOneOptionsBuilder, key string, option any) error {\n\ttypeErrFunc := func(t string) error {\n\t\treturn fmt.Errorf(\"unexpected type for %q: %T is not %s\", key, option, t)\n\t}\n\tswitch key {\n\tcase \"rawData\":\n\t\tb, ok := option.(bool)\n\t\tif !ok {\n\t\t\treturn typeErrFunc(\"bool\")\n\t\t}\n\t\ta.Opts = append(a.Opts, func(opts *options.FindOneOptions) error {\n\t\t\topts.Internal = optionsutil.WithValue(opts.Internal, key, b)\n\t\t\treturn nil\n\t\t})\n\tdefault:\n\t\treturn fmt.Errorf(\"unsupported option: %q\", key)\n\t}\n\treturn nil\n}\n\n// SetInternalFindOneAndDeleteOptions sets internal options for FindOneAndDeleteOptions.\nfunc SetInternalFindOneAndDeleteOptions(a *options.FindOneAndDeleteOptionsBuilder, key string, option any) error {\n\ttypeErrFunc := func(t string) error {\n\t\treturn fmt.Errorf(\"unexpected type for %q: %T is not %s\", key, option, t)\n\t}\n\tswitch key {\n\tcase \"rawData\":\n\t\tb, ok := option.(bool)\n\t\tif !ok {\n\t\t\treturn typeErrFunc(\"bool\")\n\t\t}\n\t\ta.Opts = append(a.Opts, func(opts *options.FindOneAndDeleteOptions) error {\n\t\t\topts.Internal = optionsutil.WithValue(opts.Internal, key, b)\n\t\t\treturn nil\n\t\t})\n\tdefault:\n\t\treturn fmt.Errorf(\"unsupported option: %q\", key)\n\t}\n\treturn nil\n}\n\n// SetInternalFindOneAndReplaceOptions sets internal options for FindOneAndReplaceOptions.\nfunc SetInternalFindOneAndReplaceOptions(a *options.FindOneAndReplaceOptionsBuilder, key string, option any) error {\n\ttypeErrFunc := func(t string) error {\n\t\treturn fmt.Errorf(\"unexpected type for %q: %T is not %s\", key, option, t)\n\t}\n\tswitch key {\n\tcase \"rawData\":\n\t\tb, ok := option.(bool)\n\t\tif !ok {\n\t\t\treturn typeErrFunc(\"bool\")\n\t\t}\n\t\ta.Opts = append(a.Opts, func(opts *options.FindOneAndReplaceOptions) error {\n\t\t\topts.Internal = optionsutil.WithValue(opts.Internal, key, b)\n\t\t\treturn nil\n\t\t})\n\tcase \"addCommandFields\":\n\t\td, ok := option.(bson.D)\n\t\tif !ok {\n\t\t\treturn typeErrFunc(\"bson.D\")\n\t\t}\n\t\ta.Opts = append(a.Opts, func(opts *options.FindOneAndReplaceOptions) error {\n\t\t\topts.Internal = optionsutil.WithValue(opts.Internal, key, d)\n\t\t\treturn nil\n\t\t})\n\tdefault:\n\t\treturn fmt.Errorf(\"unsupported option: %q\", key)\n\t}\n\treturn nil\n}\n\n// SetInternalFindOneAndUpdateOptions sets internal options for FindOneAndUpdateOptions.\nfunc SetInternalFindOneAndUpdateOptions(a *options.FindOneAndUpdateOptionsBuilder, key string, option any) error {\n\ttypeErrFunc := func(t string) error {\n\t\treturn fmt.Errorf(\"unexpected type for %q: %T is not %s\", key, option, t)\n\t}\n\tswitch key {\n\tcase \"rawData\":\n\t\tb, ok := option.(bool)\n\t\tif !ok {\n\t\t\treturn typeErrFunc(\"bool\")\n\t\t}\n\t\ta.Opts = append(a.Opts, func(opts *options.FindOneAndUpdateOptions) error {\n\t\t\topts.Internal = optionsutil.WithValue(opts.Internal, key, b)\n\t\t\treturn nil\n\t\t})\n\tcase \"addCommandFields\":\n\t\td, ok := option.(bson.D)\n\t\tif !ok {\n\t\t\treturn typeErrFunc(\"bson.D\")\n\t\t}\n\t\ta.Opts = append(a.Opts, func(opts *options.FindOneAndUpdateOptions) error {\n\t\t\topts.Internal = optionsutil.WithValue(opts.Internal, key, d)\n\t\t\treturn nil\n\t\t})\n\tdefault:\n\t\treturn fmt.Errorf(\"unsupported option: %q\", key)\n\t}\n\treturn nil\n}\n\n// SetInternalInsertManyOptions sets internal options for InsertManyOptions.\nfunc SetInternalInsertManyOptions(a *options.InsertManyOptionsBuilder, key string, option any) error {\n\ttypeErrFunc := func(t string) error {\n\t\treturn fmt.Errorf(\"unexpected type for %q: %T is not %s\", key, option, t)\n\t}\n\tswitch key {\n\tcase \"rawData\":\n\t\tb, ok := option.(bool)\n\t\tif !ok {\n\t\t\treturn typeErrFunc(\"bool\")\n\t\t}\n\t\ta.Opts = append(a.Opts, func(opts *options.InsertManyOptions) error {\n\t\t\topts.Internal = optionsutil.WithValue(opts.Internal, key, b)\n\t\t\treturn nil\n\t\t})\n\tcase \"addCommandFields\":\n\t\td, ok := option.(bson.D)\n\t\tif !ok {\n\t\t\treturn typeErrFunc(\"bson.D\")\n\t\t}\n\t\ta.Opts = append(a.Opts, func(opts *options.InsertManyOptions) error {\n\t\t\topts.Internal = optionsutil.WithValue(opts.Internal, key, d)\n\t\t\treturn nil\n\t\t})\n\tdefault:\n\t\treturn fmt.Errorf(\"unsupported option: %q\", key)\n\t}\n\treturn nil\n}\n\n// SetInternalInsertOneOptions sets internal options for InsertOneOptions.\nfunc SetInternalInsertOneOptions(a *options.InsertOneOptionsBuilder, key string, option any) error {\n\ttypeErrFunc := func(t string) error {\n\t\treturn fmt.Errorf(\"unexpected type for %q: %T is not %s\", key, option, t)\n\t}\n\tswitch key {\n\tcase \"rawData\":\n\t\tb, ok := option.(bool)\n\t\tif !ok {\n\t\t\treturn typeErrFunc(\"bool\")\n\t\t}\n\t\ta.Opts = append(a.Opts, func(opts *options.InsertOneOptions) error {\n\t\t\topts.Internal = optionsutil.WithValue(opts.Internal, key, b)\n\t\t\treturn nil\n\t\t})\n\tcase \"addCommandFields\":\n\t\td, ok := option.(bson.D)\n\t\tif !ok {\n\t\t\treturn typeErrFunc(\"bson.D\")\n\t\t}\n\t\ta.Opts = append(a.Opts, func(opts *options.InsertOneOptions) error {\n\t\t\topts.Internal = optionsutil.WithValue(opts.Internal, key, d)\n\t\t\treturn nil\n\t\t})\n\tdefault:\n\t\treturn fmt.Errorf(\"unsupported option: %q\", key)\n\t}\n\treturn nil\n}\n\n// SetInternalListCollectionsOptions sets internal options for ListCollectionsOptions.\nfunc SetInternalListCollectionsOptions(a *options.ListCollectionsOptionsBuilder, key string, option any) error {\n\ttypeErrFunc := func(t string) error {\n\t\treturn fmt.Errorf(\"unexpected type for %q: %T is not %s\", key, option, t)\n\t}\n\tswitch key {\n\tcase \"rawData\":\n\t\tb, ok := option.(bool)\n\t\tif !ok {\n\t\t\treturn typeErrFunc(\"bool\")\n\t\t}\n\t\ta.Opts = append(a.Opts, func(opts *options.ListCollectionsOptions) error {\n\t\t\topts.Internal = optionsutil.WithValue(opts.Internal, key, b)\n\t\t\treturn nil\n\t\t})\n\tdefault:\n\t\treturn fmt.Errorf(\"unsupported option: %q\", key)\n\t}\n\treturn nil\n}\n\n// SetInternalListIndexesOptions sets internal options for ListIndexesOptions.\nfunc SetInternalListIndexesOptions(a *options.ListIndexesOptionsBuilder, key string, option any) error {\n\ttypeErrFunc := func(t string) error {\n\t\treturn fmt.Errorf(\"unexpected type for %q: %T is not %s\", key, option, t)\n\t}\n\tswitch key {\n\tcase \"rawData\":\n\t\tb, ok := option.(bool)\n\t\tif !ok {\n\t\t\treturn typeErrFunc(\"bool\")\n\t\t}\n\t\ta.Opts = append(a.Opts, func(opts *options.ListIndexesOptions) error {\n\t\t\topts.Internal = optionsutil.WithValue(opts.Internal, key, b)\n\t\t\treturn nil\n\t\t})\n\tdefault:\n\t\treturn fmt.Errorf(\"unsupported option: %q\", key)\n\t}\n\treturn nil\n}\n\n// SetInternalReplaceOptions sets internal options for ReplaceOptions.\nfunc SetInternalReplaceOptions(a *options.ReplaceOptionsBuilder, key string, option any) error {\n\ttypeErrFunc := func(t string) error {\n\t\treturn fmt.Errorf(\"unexpected type for %q: %T is not %s\", key, option, t)\n\t}\n\tswitch key {\n\tcase \"rawData\":\n\t\tb, ok := option.(bool)\n\t\tif !ok {\n\t\t\treturn typeErrFunc(\"bool\")\n\t\t}\n\t\ta.Opts = append(a.Opts, func(opts *options.ReplaceOptions) error {\n\t\t\topts.Internal = optionsutil.WithValue(opts.Internal, key, b)\n\t\t\treturn nil\n\t\t})\n\tcase \"addCommandFields\":\n\t\td, ok := option.(bson.D)\n\t\tif !ok {\n\t\t\treturn typeErrFunc(\"bson.D\")\n\t\t}\n\t\ta.Opts = append(a.Opts, func(opts *options.ReplaceOptions) error {\n\t\t\topts.Internal = optionsutil.WithValue(opts.Internal, key, d)\n\t\t\treturn nil\n\t\t})\n\tdefault:\n\t\treturn fmt.Errorf(\"unsupported option: %q\", key)\n\t}\n\treturn nil\n}\n\n// SetInternalUpdateManyOptions sets internal options for UpdateManyOptions.\nfunc SetInternalUpdateManyOptions(a *options.UpdateManyOptionsBuilder, key string, option any) error {\n\ttypeErrFunc := func(t string) error {\n\t\treturn fmt.Errorf(\"unexpected type for %q: %T is not %s\", key, option, t)\n\t}\n\tswitch key {\n\tcase \"rawData\":\n\t\tb, ok := option.(bool)\n\t\tif !ok {\n\t\t\treturn typeErrFunc(\"bool\")\n\t\t}\n\t\ta.Opts = append(a.Opts, func(opts *options.UpdateManyOptions) error {\n\t\t\topts.Internal = optionsutil.WithValue(opts.Internal, key, b)\n\t\t\treturn nil\n\t\t})\n\tcase \"addCommandFields\":\n\t\td, ok := option.(bson.D)\n\t\tif !ok {\n\t\t\treturn typeErrFunc(\"bson.D\")\n\t\t}\n\t\ta.Opts = append(a.Opts, func(opts *options.UpdateManyOptions) error {\n\t\t\topts.Internal = optionsutil.WithValue(opts.Internal, key, d)\n\t\t\treturn nil\n\t\t})\n\tdefault:\n\t\treturn fmt.Errorf(\"unsupported option: %q\", key)\n\t}\n\treturn nil\n}\n\n// SetInternalUpdateOneOptions sets internal options for UpdateOneOptions.\nfunc SetInternalUpdateOneOptions(a *options.UpdateOneOptionsBuilder, key string, option any) error {\n\ttypeErrFunc := func(t string) error {\n\t\treturn fmt.Errorf(\"unexpected type for %q: %T is not %s\", key, option, t)\n\t}\n\tswitch key {\n\tcase \"rawData\":\n\t\tb, ok := option.(bool)\n\t\tif !ok {\n\t\t\treturn typeErrFunc(\"bool\")\n\t\t}\n\t\ta.Opts = append(a.Opts, func(opts *options.UpdateOneOptions) error {\n\t\t\topts.Internal = optionsutil.WithValue(opts.Internal, key, b)\n\t\t\treturn nil\n\t\t})\n\tcase \"addCommandFields\":\n\t\td, ok := option.(bson.D)\n\t\tif !ok {\n\t\t\treturn typeErrFunc(\"bson.D\")\n\t\t}\n\t\ta.Opts = append(a.Opts, func(opts *options.UpdateOneOptions) error {\n\t\t\topts.Internal = optionsutil.WithValue(opts.Internal, key, d)\n\t\t\treturn nil\n\t\t})\n\tdefault:\n\t\treturn fmt.Errorf(\"unsupported option: %q\", key)\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "x/mongo/driver/xoptions/options_test.go",
    "content": "// Copyright (C) MongoDB, Inc. 2025-present.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n// not use this file except in compliance with the License. You may obtain\n// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n\npackage xoptions\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\n\t\"go.mongodb.org/mongo-driver/v2/internal/optionsutil\"\n\t\"go.mongodb.org/mongo-driver/v2/internal/require\"\n\t\"go.mongodb.org/mongo-driver/v2/mongo/options\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver\"\n\t\"go.mongodb.org/mongo-driver/v2/x/mongo/driver/drivertest\"\n)\n\nfunc TestSetInternalClientOptions(t *testing.T) {\n\tt.Parallel()\n\n\tcases := []struct {\n\t\tkey   string\n\t\tvalue any\n\t}{\n\t\t{\n\t\t\tkey:   \"authenticateToAnything\",\n\t\t\tvalue: true,\n\t\t},\n\t}\n\tfor _, tc := range cases {\n\t\ttc := tc\n\n\t\tt.Run(fmt.Sprintf(\"set %s\", tc.key), func(t *testing.T) {\n\t\t\tt.Parallel()\n\n\t\t\topts := options.Client()\n\t\t\terr := SetInternalClientOptions(opts, tc.key, tc.value)\n\t\t\trequire.NoError(t, err, \"error setting %s: %v\", tc.key, err)\n\t\t\tv := optionsutil.Value(opts.Custom, tc.key)\n\t\t\trequire.Equal(t, tc.value, v, \"expected %v, got %v\", tc.value, v)\n\t\t})\n\t}\n\n\tt.Run(\"set crypt\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\tc := driver.NewCrypt(&driver.CryptOptions{})\n\t\topts := options.Client()\n\t\terr := SetInternalClientOptions(opts, \"crypt\", c)\n\t\trequire.NoError(t, err, \"error setting crypt: %v\", err)\n\t\trequire.Equal(t, c, opts.Crypt, \"expected %v, got %v\", c, opts.Crypt)\n\t})\n\n\tt.Run(\"set crypt - wrong type\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\topts := options.Client()\n\t\terr := SetInternalClientOptions(opts, \"crypt\", &drivertest.MockDeployment{})\n\t\trequire.EqualError(t, err, \"unexpected type for \\\"crypt\\\": *drivertest.MockDeployment is not driver.Crypt\")\n\t})\n\n\tt.Run(\"set deployment\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\td := &drivertest.MockDeployment{}\n\t\topts := options.Client()\n\t\terr := SetInternalClientOptions(opts, \"deployment\", d)\n\t\trequire.NoError(t, err, \"error setting deployment: %v\", err)\n\t\trequire.Equal(t, d, opts.Deployment, \"expected %v, got %v\", d, opts.Deployment)\n\t})\n\n\tt.Run(\"set deployment - wrong type\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\topts := options.Client()\n\t\terr := SetInternalClientOptions(opts, \"deployment\", driver.NewCrypt(&driver.CryptOptions{}))\n\t\trequire.EqualError(t, err, \"unexpected type for \\\"deployment\\\": *driver.crypt is not driver.Deployment\")\n\t})\n\n\tt.Run(\"set unsupported option\", func(t *testing.T) {\n\t\tt.Parallel()\n\n\t\topts := options.Client()\n\t\terr := SetInternalClientOptions(opts, \"unsupported\", \"unsupported\")\n\t\trequire.EqualError(t, err, \"unsupported option: \\\"unsupported\\\"\")\n\t})\n}\n"
  }
]