diff --git a/.evergreen/config.yml b/.evergreen/config.yml index f74052f2a7b..415847cf6a0 100644 --- a/.evergreen/config.yml +++ b/.evergreen/config.yml @@ -2752,7 +2752,6 @@ tasks: - {key: VERSION, value: latest} - {key: TOPOLOGY, value: sharded_cluster} - {key: AUTH, value: auth} - - {key: TEST_NPM_SCRIPT, value: check:csfle} - func: install dependencies - func: bootstrap mongo-orchestration - func: run tests @@ -2768,7 +2767,6 @@ tasks: - {key: VERSION, value: rapid} - {key: TOPOLOGY, value: sharded_cluster} - {key: AUTH, value: auth} - - {key: TEST_NPM_SCRIPT, value: check:csfle} - func: install dependencies - func: bootstrap mongo-orchestration - func: run tests @@ -2784,7 +2782,6 @@ tasks: - {key: VERSION, value: '8.0'} - {key: TOPOLOGY, value: sharded_cluster} - {key: AUTH, value: auth} - - {key: TEST_NPM_SCRIPT, value: check:csfle} - func: install dependencies - func: bootstrap mongo-orchestration - func: run tests @@ -2800,7 +2797,6 @@ tasks: - {key: VERSION, value: '7.0'} - {key: TOPOLOGY, value: sharded_cluster} - {key: AUTH, value: auth} - - {key: TEST_NPM_SCRIPT, value: check:csfle} - func: install dependencies - func: bootstrap mongo-orchestration - func: run tests @@ -2816,7 +2812,6 @@ tasks: - {key: VERSION, value: '6.0'} - {key: TOPOLOGY, value: sharded_cluster} - {key: AUTH, value: auth} - - {key: TEST_NPM_SCRIPT, value: check:csfle} - func: install dependencies - func: bootstrap mongo-orchestration - func: run tests @@ -2832,7 +2827,6 @@ tasks: - {key: VERSION, value: '5.0'} - {key: TOPOLOGY, value: sharded_cluster} - {key: AUTH, value: auth} - - {key: TEST_NPM_SCRIPT, value: check:csfle} - func: install dependencies - func: bootstrap mongo-orchestration - func: run tests @@ -2848,7 +2842,6 @@ tasks: - {key: VERSION, value: '4.4'} - {key: TOPOLOGY, value: sharded_cluster} - {key: AUTH, value: auth} - - {key: TEST_NPM_SCRIPT, value: check:csfle} - func: install dependencies - func: bootstrap mongo-orchestration - func: run tests @@ -2864,7 +2857,6 @@ tasks: - {key: VERSION, value: '4.2'} - {key: TOPOLOGY, value: sharded_cluster} - {key: AUTH, value: auth} - - {key: TEST_NPM_SCRIPT, value: check:csfle} - func: install dependencies - func: bootstrap mongo-orchestration - func: run tests diff --git a/.evergreen/generate_evergreen_tasks.js b/.evergreen/generate_evergreen_tasks.js index 87ac59b9086..1c97f3985a5 100644 --- a/.evergreen/generate_evergreen_tasks.js +++ b/.evergreen/generate_evergreen_tasks.js @@ -464,8 +464,7 @@ const MONGOCRYPTD_CSFLE_TASKS = MONGODB_VERSIONS.filter( updateExpansions({ VERSION: mongoVersion, TOPOLOGY: 'sharded_cluster', - AUTH: 'auth', - TEST_NPM_SCRIPT: 'check:csfle' + AUTH: 'auth' }), { func: 'install dependencies' }, { func: 'bootstrap mongo-orchestration' }, diff --git a/.evergreen/run-tests.sh b/.evergreen/run-tests.sh index 68da6c2dd19..87ad6f4da7d 100755 --- a/.evergreen/run-tests.sh +++ b/.evergreen/run-tests.sh @@ -1,19 +1,17 @@ #!/bin/bash # set -o xtrace # Write all commands first to stderr -set -o errexit # Exit the script with error if any of the commands fail +set -o errexit # Exit the script with error if any of the commands fail # Supported/used environment variables: # AUTH Set to enable authentication. Defaults to "noauth" # SSL Set to enable SSL. Defaults to "nossl" # MONGODB_URI Set the suggested connection MONGODB_URI (including credentials and topology info) # MARCH Machine Architecture. Defaults to lowercase uname -m -# TEST_NPM_SCRIPT Script to npm run. Defaults to "integration-coverage" # SKIP_DEPS Skip installing dependencies # TEST_CSFLE Set to enforce running csfle tests AUTH=${AUTH:-noauth} MONGODB_URI=${MONGODB_URI:-} -TEST_NPM_SCRIPT=${TEST_NPM_SCRIPT:-check:integration-coverage} COMPRESSOR=${COMPRESSOR:-} SKIP_DEPS=${SKIP_DEPS:-true} @@ -28,8 +26,8 @@ fi # ssl setup SSL=${SSL:-nossl} if [ "$SSL" != "nossl" ]; then - export SSL_KEY_FILE="$DRIVERS_TOOLS/.evergreen/x509gen/client.pem" - export SSL_CA_FILE="$DRIVERS_TOOLS/.evergreen/x509gen/ca.pem" + export SSL_KEY_FILE="$DRIVERS_TOOLS/.evergreen/x509gen/client.pem" + export SSL_CA_FILE="$DRIVERS_TOOLS/.evergreen/x509gen/ca.pem" fi # run tests @@ -60,4 +58,4 @@ export MONGODB_URI=${MONGODB_URI} export LOAD_BALANCER=${LOAD_BALANCER} export TEST_CSFLE=${TEST_CSFLE} export COMPRESSOR=${COMPRESSOR} -npm run "${TEST_NPM_SCRIPT}" +npm run check:integration-coverage diff --git a/src/client-side-encryption/auto_encrypter.ts b/src/client-side-encryption/auto_encrypter.ts index b8f2e42b13b..18e2b62cc4a 100644 --- a/src/client-side-encryption/auto_encrypter.ts +++ b/src/client-side-encryption/auto_encrypter.ts @@ -365,14 +365,10 @@ export class AutoEncrypter { const client = await this._mongocryptdClient.connect(); return client; } catch (error) { - const { message } = error; - if (message && (message.match(/timed out after/) || message.match(/ENOTFOUND/))) { - throw new MongoRuntimeError( - 'Unable to connect to `mongocryptd`, please make sure it is running or in your PATH for auto-spawn', - { cause: error } - ); - } - throw error; + throw new MongoRuntimeError( + 'Unable to connect to `mongocryptd`, please make sure it is running or in your PATH for auto-spawn', + { cause: error } + ); } } diff --git a/test/integration/client-side-encryption/client_side_encryption.prose.test.js b/test/integration/client-side-encryption/client_side_encryption.prose.test.js index 9009bc94aff..4d8707ee571 100644 --- a/test/integration/client-side-encryption/client_side_encryption.prose.test.js +++ b/test/integration/client-side-encryption/client_side_encryption.prose.test.js @@ -7,7 +7,7 @@ const path = require('path'); const { dropCollection, APMEventCollector } = require('../shared'); const { EJSON } = BSON; -const { LEGACY_HELLO_COMMAND, MongoCryptError } = require('../../mongodb'); +const { LEGACY_HELLO_COMMAND, MongoCryptError, MongoRuntimeError } = require('../../mongodb'); const { MongoServerError, MongoServerSelectionError, MongoClient } = require('../../mongodb'); const { getEncryptExtraOptions } = require('../../tools/utils'); @@ -1177,9 +1177,16 @@ describe('Client Side Encryption Prose Tests', metadata, function () { .insertOne({ encrypted: 'test' }) .catch(e => e); - expect(insertError).to.be.instanceOf(MongoServerSelectionError); + expect(insertError) + .to.be.instanceOf(MongoRuntimeError) + .to.match( + /Unable to connect to `mongocryptd`, please make sure it is running or in your PATH for auto-spawn/ + ); + + const { cause } = insertError; - expect(insertError, 'Error must contain ECONNREFUSED').to.satisfy( + expect(cause).to.be.instanceOf(MongoServerSelectionError); + expect(cause, 'Error must contain ECONNREFUSED').to.satisfy( error => /ECONNREFUSED/.test(error.message) || !!error.cause?.cause?.errors?.every(e => e.code === 'ECONNREFUSED') diff --git a/test/integration/node-specific/auto_encrypter.test.ts b/test/integration/node-specific/auto_encrypter.test.ts index 57cc08ebc34..babd192f0cd 100644 --- a/test/integration/node-specific/auto_encrypter.test.ts +++ b/test/integration/node-specific/auto_encrypter.test.ts @@ -1,407 +1,233 @@ import { expect } from 'chai'; -import { spawnSync } from 'child_process'; -import * as fs from 'fs'; -import { dirname, resolve } from 'path'; import * as sinon from 'sinon'; -/* eslint-disable @typescript-eslint/no-restricted-imports */ -import { AutoEncrypter } from '../../../src/client-side-encryption/auto_encrypter'; -/* eslint-disable @typescript-eslint/no-restricted-imports */ -import { MongocryptdManager } from '../../../src/client-side-encryption/mongocryptd_manager'; -/* eslint-disable @typescript-eslint/no-restricted-imports */ -import { StateMachine } from '../../../src/client-side-encryption/state_machine'; import { - BSON, - deserialize, + ClientEncryption, + type KMSProviders, type MongoClient, - MongoError, MongoNetworkTimeoutError, - serialize + MongoRuntimeError, + StateMachine, + type UUID } from '../../mongodb'; -import { getEncryptExtraOptions } from '../../tools/utils'; +import { ClientSideEncryptionFilter } from '../../tools/runner/filters/client_encryption_filter'; -const { EJSON } = BSON; -const cryptShared = (status: 'enabled' | 'disabled') => () => { - const isPathPresent = (getEncryptExtraOptions().cryptSharedLibPath ?? '').length > 0; +export const cryptShared = (status: 'enabled' | 'disabled') => () => { + const isCryptSharedLoaded = ClientSideEncryptionFilter.cryptShared != null; if (status === 'enabled') { - return isPathPresent ? true : 'Test requires the shared library.'; + return isCryptSharedLoaded ? true : 'Test requires the shared library.'; } - return isPathPresent ? 'Test requires that the crypt shared library NOT be present' : true; + return isCryptSharedLoaded ? 'Test requires that the crypt shared library NOT be present' : true; }; -const dataPath = (fileName: string) => - resolve(__dirname, '../../unit/client-side-encryption/data', fileName); - -function readExtendedJsonToBuffer(path) { - const ejson = EJSON.parse(fs.readFileSync(path, 'utf8')); - return serialize(ejson); -} - -function readHttpResponse(path) { - let data = fs.readFileSync(path, 'utf8'); - data = data.split('\n').join('\r\n'); - return Buffer.from(data, 'utf8'); -} - -const TEST_COMMAND = JSON.parse(fs.readFileSync(dataPath(`cmd.json`), { encoding: 'utf-8' })); -const MOCK_COLLINFO_RESPONSE = readExtendedJsonToBuffer(dataPath(`collection-info.json`)); -const MOCK_MONGOCRYPTD_RESPONSE = readExtendedJsonToBuffer(dataPath(`mongocryptd-reply.json`)); -const MOCK_KEYDOCUMENT_RESPONSE = readExtendedJsonToBuffer(dataPath(`key-document.json`)); -const MOCK_KMS_DECRYPT_REPLY = readHttpResponse(dataPath(`kms-decrypt-reply.txt`)); - -describe('crypt_shared library', function () { +describe('mongocryptd auto spawn', function () { let client: MongoClient; - let autoEncrypter: AutoEncrypter | undefined; + const kmsProviders: KMSProviders = { + local: { key: Buffer.alloc(96) } + }; + let dataKey: UUID; + + const keyVaultNamespace = 'data.keys'; + + beforeEach('create data key', async function () { + const utilClient = this.configuration.newClient(); + const clientEncryption = new ClientEncryption(utilClient, { + kmsProviders, + keyVaultNamespace + }); + dataKey = await clientEncryption.createDataKey('local'); + await utilClient.close(); + }); beforeEach(async function () { - client = this.configuration.newClient(); - await client.connect(); + client = this.configuration.newClient( + {}, + { + retryReads: false, + autoEncryption: { + keyVaultNamespace, + kmsProviders, + schemaMap: { + 'namespace.collection': { + bsonType: 'object', + properties: { + ssn: { + encrypt: { + keyId: dataKey, + bsonType: 'string', + algorithm: 'AEAD_AES_256_CBC_HMAC_SHA_512-Random' + } + } + } + } + } + } + } + ); }); afterEach(async () => { - await autoEncrypter?.teardown(true); await client?.close(); - }); - const sandbox = sinon.createSandbox(); - - beforeEach(() => { - sandbox.restore(); - sandbox.stub(StateMachine.prototype, 'kmsRequest').callsFake(request => { - request.addResponse(MOCK_KMS_DECRYPT_REPLY); - return Promise.resolve(); - }); - - sandbox - .stub(StateMachine.prototype, 'fetchCollectionInfo') - .callsFake((client, ns, filter, callback) => { - callback(null, MOCK_COLLINFO_RESPONSE); - }); - - sandbox - .stub(StateMachine.prototype, 'markCommand') - .callsFake((client, ns, command, callback) => { - if (ENABLE_LOG_TEST) { - const response = bson.deserialize(MOCK_MONGOCRYPTD_RESPONSE); - response.schemaRequiresEncryption = false; - - ENABLE_LOG_TEST = false; // disable test after run - callback(null, bson.serialize(response)); - return; - } - - callback(null, MOCK_MONGOCRYPTD_RESPONSE); - }); - - sandbox.stub(StateMachine.prototype, 'fetchKeys').callsFake((client, ns, filter, callback) => { - // mock data is already serialized, our action deals with the result of a cursor - const deserializedKey = deserialize(MOCK_KEYDOCUMENT_RESPONSE); - callback(null, [deserializedKey]); - }); + sinon.restore(); }); - afterEach(() => { - sandbox.restore(); - }); - - describe('autoSpawn', function () { - it( - 'should autoSpawn a mongocryptd on init by default', - { requires: { clientSideEncryption: true, predicate: cryptShared('disabled') } }, - - async function () { - autoEncrypter = new AutoEncrypter(client, { - keyVaultNamespace: 'admin.datakeys', - kmsProviders: { - aws: { accessKeyId: 'example', secretAccessKey: 'example' }, - local: { key: Buffer.alloc(96) } - } - }); - - expect(autoEncrypter).to.have.property('cryptSharedLibVersionInfo', null); - - const localMcdm = autoEncrypter._mongocryptdManager; - sandbox.spy(localMcdm, 'spawn'); - - await autoEncrypter.init(); - expect(localMcdm.spawn).to.have.been.calledOnce; - } - ); - - it( - 'should not attempt to kick off mongocryptd on a normal error', - { requires: { clientSideEncryption: true, predicate: cryptShared('disabled') } }, - async function () { - let called = false; - StateMachine.prototype.markCommand.callsFake((client, ns, filter, callback) => { + it( + 'should autoSpawn a mongocryptd on init by default', + { requires: { clientSideEncryption: true, predicate: cryptShared('disabled') } }, + async function () { + const autoEncrypter = client.autoEncrypter; + const mongocryptdManager = autoEncrypter._mongocryptdManager; + + const spy = sinon.spy(mongocryptdManager, 'spawn'); + + await client.connect(); + + expect(spy).to.have.been.calledOnce; + } + ); + + it( + 'should not attempt to kick off mongocryptd on a non-network error from mongocrpytd', + { requires: { clientSideEncryption: true, predicate: cryptShared('disabled') } }, + async function () { + let called = false; + sinon + .stub(StateMachine.prototype, 'markCommand') + .callsFake(async function (client, ns, filter) { if (!called) { called = true; - callback(new Error('msg')); - return; + throw new Error('non-network error'); } - callback(null, MOCK_MONGOCRYPTD_RESPONSE); + return this.wrappedMethod.apply(client, ns, filter); }); - autoEncrypter = new AutoEncrypter(client, { - keyVaultNamespace: 'admin.datakeys', - kmsProviders: { - aws: { accessKeyId: 'example', secretAccessKey: 'example' }, - local: { key: Buffer.alloc(96) } - } - }); - expect(autoEncrypter).to.have.property('cryptSharedLibVersionInfo', null); - - const localMcdm = autoEncrypter._mongocryptdManager; - await autoEncrypter.init(); - - sandbox.spy(localMcdm, 'spawn'); - - const err = await autoEncrypter.encrypt('test.test', TEST_COMMAND).catch(e => e); - expect(localMcdm.spawn).to.not.have.been.called; - expect(err).to.be.an.instanceOf(Error); - } - ); - - it( - 'should restore the mongocryptd and retry once if a MongoNetworkTimeoutError is experienced', - { requires: { clientSideEncryption: true, predicate: cryptShared('disabled') } }, - async function () { - let called = false; - StateMachine.prototype.markCommand.callsFake((client, ns, filter, callback) => { + const autoEncrypter = client.autoEncrypter; + await client.connect(); + + const mongocryptdManager = autoEncrypter._mongocryptdManager; + const spy = sinon.spy(mongocryptdManager, 'spawn'); + + const error = await client + .db('namespace') + .collection('collection') + .find() + .toArray() + .catch(e => e); + expect(spy).to.not.have.been.called; + expect(error).to.be.an.instanceOf(Error); + expect(error).to.match(/non-network error/); + } + ); + + it( + 'should respawn the mongocryptd after a MongoNetworkTimeoutError is returned when communicating with mongocryptd', + { requires: { clientSideEncryption: true, predicate: cryptShared('disabled') } }, + async function () { + let called = false; + sinon + .stub(StateMachine.prototype, 'markCommand') + .callsFake(async function (client, ns, filter) { if (!called) { called = true; - callback(new MongoNetworkTimeoutError('msg')); - return; + throw new MongoNetworkTimeoutError('non-network error'); } - callback(null, MOCK_MONGOCRYPTD_RESPONSE); + return this.wrappedMethod.apply(client, ns, filter); }); - autoEncrypter = new AutoEncrypter(client, { - keyVaultNamespace: 'admin.datakeys', - kmsProviders: { - aws: { accessKeyId: 'example', secretAccessKey: 'example' }, - local: { key: Buffer.alloc(96) } - } - }); - expect(autoEncrypter).to.have.property('cryptSharedLibVersionInfo', null); + const autoEncrypter = client.autoEncrypter; + const mongocryptdManager = autoEncrypter._mongocryptdManager; - const localMcdm = autoEncrypter._mongocryptdManager; - await autoEncrypter.init(); + await client.connect(); - sandbox.spy(localMcdm, 'spawn'); + const spy = sinon.spy(mongocryptdManager, 'spawn'); - await autoEncrypter.encrypt('test.test', TEST_COMMAND); - expect(localMcdm.spawn).to.have.been.calledOnce; - } - ); + await client + .db('namespace') + .collection('collection') + .find() + .toArray() + .catch(e => e); - it( - 'should propagate error if MongoNetworkTimeoutError is experienced twice in a row', - { requires: { clientSideEncryption: true, predicate: cryptShared('disabled') } }, - async function () { - let counter = 2; - StateMachine.prototype.markCommand.callsFake((client, ns, filter, callback) => { - if (counter) { - counter -= 1; - callback(new MongoNetworkTimeoutError('msg')); - return; - } + expect(spy).to.have.been.calledOnce; + } + ); - callback(null, MOCK_MONGOCRYPTD_RESPONSE); + it( + 'should propagate error if MongoNetworkTimeoutError is experienced twice in a row', + { requires: { clientSideEncryption: true, predicate: cryptShared('disabled') } }, + async function () { + const stub = sinon + .stub(StateMachine.prototype, 'markCommand') + .callsFake(async (_client, _ns, _filter) => { + throw new MongoNetworkTimeoutError('msg'); }); - autoEncrypter = new AutoEncrypter(client, { - keyVaultNamespace: 'admin.datakeys', - kmsProviders: { - aws: { accessKeyId: 'example', secretAccessKey: 'example' }, - local: { key: Buffer.alloc(96) } - } - }); - expect(autoEncrypter).to.have.property('cryptSharedLibVersionInfo', null); + const autoEncrypter = client.autoEncrypter; + const mongocryptdManager = autoEncrypter._mongocryptdManager; + await client.connect(); - const localMcdm = autoEncrypter._mongocryptdManager; - await autoEncrypter.init(); + const spy = sinon.spy(mongocryptdManager, 'spawn'); - sandbox.spy(localMcdm, 'spawn'); + const error = await client + .db('namespace') + .collection('collection') + .find() + .toArray() + .then(() => null) + .catch(e => e); - const err = await autoEncrypter.encrypt('test.test', TEST_COMMAND).catch(e => e); - expect(localMcdm.spawn).to.have.been.calledOnce; - expect(err).to.be.an.instanceof(MongoNetworkTimeoutError); - } - ); + expect(spy).to.have.been.calledOnce; + expect(stub).to.have.been.calledTwice; + expect(error).to.be.an.instanceof(MongoNetworkTimeoutError); + expect(error).to.match(/msg/); + } + ); + + describe('when the client fails to connect to mongocryptd', function () { + let client: MongoClient; + this.afterEach(() => client?.close()); it( 'should return a useful message if mongocryptd fails to autospawn', { requires: { clientSideEncryption: true, predicate: cryptShared('disabled') } }, async function () { - autoEncrypter = new AutoEncrypter(client, { - keyVaultNamespace: 'admin.datakeys', - kmsProviders: { - aws: { accessKeyId: 'example', secretAccessKey: 'example' }, - local: { key: Buffer.alloc(96) } - }, - extraOptions: { - mongocryptdURI: 'mongodb://something.invalid:27020/' - } - }); - expect(autoEncrypter).to.have.property('cryptSharedLibVersionInfo', null); - - sandbox.stub(MongocryptdManager.prototype, 'spawn').resolves(); - - const err = await autoEncrypter.init().catch(e => e); - expect(err).to.exist; - expect(err).to.be.instanceOf(MongoError); - } - ); - }); - - describe('noAutoSpawn', function () { - ['mongocryptdBypassSpawn', 'bypassAutoEncryption', 'bypassQueryAnalysis'].forEach(opt => { - const encryptionOptions = { - keyVaultNamespace: 'admin.datakeys', - kmsProviders: { - aws: { accessKeyId: 'example', secretAccessKey: 'example' }, - local: { key: Buffer.alloc(96) } - }, - extraOptions: { - mongocryptdBypassSpawn: opt === 'mongocryptdBypassSpawn' - }, - bypassAutoEncryption: opt === 'bypassAutoEncryption', - bypassQueryAnalysis: opt === 'bypassQueryAnalysis' - }; - - it( - `should not spawn mongocryptd on startup if ${opt} is true`, - { requires: { clientSideEncryption: true, predicate: cryptShared('disabled') } }, - async function () { - autoEncrypter = new AutoEncrypter(client, encryptionOptions); - - const localMcdm = autoEncrypter._mongocryptdManager || { - spawn: () => { - // intentional empty function + client = this.configuration.newClient( + {}, + { + autoEncryption: { + keyVaultNamespace, + kmsProviders, + extraOptions: { + // wrong URI + mongocryptdURI: 'mongodb://localhost:27019' + }, + schemaMap: { + 'namespace.collection': { + bsonType: 'object', + properties: { + ssn: { + encrypt: { + keyId: dataKey, + bsonType: 'string', + algorithm: 'AEAD_AES_256_CBC_HMAC_SHA_512-Random' + } + } + } + } + } } - }; - sandbox.spy(localMcdm, 'spawn'); - - await autoEncrypter.init(); - expect(localMcdm.spawn).to.have.a.callCount(0); - } - ); - }); - - it( - 'should not spawn a mongocryptd or retry on a server selection error if mongocryptdBypassSpawn: true', - { requires: { clientSideEncryption: true, predicate: cryptShared('disabled') } }, - async function () { - let called = false; - const timeoutError = new MongoNetworkTimeoutError('msg'); - StateMachine.prototype.markCommand.callsFake((client, ns, filter, callback) => { - if (!called) { - called = true; - callback(timeoutError); - return; } + ); - callback(null, MOCK_MONGOCRYPTD_RESPONSE); - }); - - autoEncrypter = new AutoEncrypter(client, { - keyVaultNamespace: 'admin.datakeys', - kmsProviders: { - aws: { accessKeyId: 'example', secretAccessKey: 'example' }, - local: { key: Buffer.alloc(96) } - }, - extraOptions: { - mongocryptdBypassSpawn: true - } - }); - - const localMcdm = autoEncrypter._mongocryptdManager; - sandbox.spy(localMcdm, 'spawn'); - - await autoEncrypter.init(); - expect(localMcdm.spawn).to.not.have.been.called; - - const err = await autoEncrypter.encrypt('test.test', TEST_COMMAND).catch(e => e); - expect(localMcdm.spawn).to.not.have.been.called; - expect(err).to.equal(timeoutError); - } - ); - }); - - describe('crypt shared library', () => { - it('should fail if no library can be found in the search path and cryptSharedLibRequired is set', async function () { - const env = { - MONGODB_URI: this.configuration.url(), - EXTRA_OPTIONS: JSON.stringify({ - cryptSharedLibSearchPaths: ['/nonexistent'], - cryptSharedLibRequired: true - }) - }; - const file = `${__dirname}/../../tools/fixtures/shared_library_test.js`; - const { stderr } = spawnSync(process.execPath, [file], { - env, - encoding: 'utf-8' - }); - - expect(stderr).to.include('`cryptSharedLibRequired` set but no crypt_shared library loaded'); - }); - - it( - 'should load a shared library by specifying its path', - { - requires: { - predicate: cryptShared('enabled') - } - }, - async function () { - const env = { - MONGODB_URI: this.configuration.url(), - EXTRA_OPTIONS: JSON.stringify({ - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - cryptSharedLibPath: getEncryptExtraOptions().cryptSharedLibPath! - }) - }; - const file = `${__dirname}/../../tools/fixtures/shared_library_test.js`; - const { stdout } = spawnSync(process.execPath, [file], { env, encoding: 'utf-8' }); - - const response = EJSON.parse(stdout, { useBigInt64: true }); - - expect(response).not.to.be.null; - - expect(response).to.have.property('version').that.is.a('bigint'); - expect(response).to.have.property('versionStr').that.is.a('string'); - } - ); - - it( - 'should load a shared library by specifying a search path', - { - requires: { - predicate: cryptShared('enabled') - } - }, - async function () { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const cryptDir = dirname(getEncryptExtraOptions().cryptSharedLibPath!); - const env = { - MONGODB_URI: this.configuration.url(), - EXTRA_OPTIONS: JSON.stringify({ - cryptSharedLibSearchPaths: [cryptDir] - }) - }; - const file = `${__dirname}/../../tools/fixtures/shared_library_test.js`; - const { stdout } = spawnSync(process.execPath, [file], { env, encoding: 'utf-8' }); - - const response = EJSON.parse(stdout, { useBigInt64: true }); - - expect(response).not.to.be.null; - - expect(response).to.have.property('version').that.is.a('bigint'); - expect(response).to.have.property('versionStr').that.is.a('string'); + const err = await client.connect().catch(e => e); + expect(err).to.be.instanceOf(MongoRuntimeError); + expect(err).to.match( + /Unable to connect to `mongocryptd`, please make sure it is running or in your PATH for auto-spawn/ + ); } ); }); diff --git a/test/integration/node-specific/crypt_shared_lib.test.ts b/test/integration/node-specific/crypt_shared_lib.test.ts new file mode 100644 index 00000000000..2bf526f91b6 --- /dev/null +++ b/test/integration/node-specific/crypt_shared_lib.test.ts @@ -0,0 +1,83 @@ +import { expect } from 'chai'; +import { spawnSync } from 'child_process'; +import { dirname } from 'path'; + +import { BSON } from '../../mongodb'; +import { getEncryptExtraOptions } from '../../tools/utils'; +import { cryptShared } from './auto_encrypter.test'; + +const { EJSON } = BSON; + +describe('crypt shared library', () => { + it('should fail if no library can be found in the search path and cryptSharedLibRequired is set', async function () { + const env = { + MONGODB_URI: this.configuration.url(), + EXTRA_OPTIONS: JSON.stringify({ + cryptSharedLibSearchPaths: ['/nonexistent'], + cryptSharedLibRequired: true + }) + }; + const file = `${__dirname}/../../tools/fixtures/shared_library_test.js`; + const { stderr } = spawnSync(process.execPath, [file], { + env, + encoding: 'utf-8' + }); + + expect(stderr).to.include('`cryptSharedLibRequired` set but no crypt_shared library loaded'); + }); + + it( + 'should load a shared library by specifying its path', + { + requires: { + predicate: cryptShared('enabled') + } + }, + async function () { + const env = { + MONGODB_URI: this.configuration.url(), + EXTRA_OPTIONS: JSON.stringify({ + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + cryptSharedLibPath: getEncryptExtraOptions().cryptSharedLibPath! + }) + }; + const file = `${__dirname}/../../tools/fixtures/shared_library_test.js`; + const { stdout } = spawnSync(process.execPath, [file], { env, encoding: 'utf-8' }); + + const response = EJSON.parse(stdout, { useBigInt64: true }); + + expect(response).not.to.be.null; + + expect(response).to.have.property('version').that.is.a('bigint'); + expect(response).to.have.property('versionStr').that.is.a('string'); + } + ); + + it( + 'should load a shared library by specifying a search path', + { + requires: { + predicate: cryptShared('enabled') + } + }, + async function () { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const cryptDir = dirname(getEncryptExtraOptions().cryptSharedLibPath!); + const env = { + MONGODB_URI: this.configuration.url(), + EXTRA_OPTIONS: JSON.stringify({ + cryptSharedLibSearchPaths: [cryptDir] + }) + }; + const file = `${__dirname}/../../tools/fixtures/shared_library_test.js`; + const { stdout } = spawnSync(process.execPath, [file], { env, encoding: 'utf-8' }); + + const response = EJSON.parse(stdout, { useBigInt64: true }); + + expect(response).not.to.be.null; + + expect(response).to.have.property('version').that.is.a('bigint'); + expect(response).to.have.property('versionStr').that.is.a('string'); + } + ); +});