From a670c28b67a1b88abb87fd7891787731b58affcc Mon Sep 17 00:00:00 2001 From: Bailey Pearson Date: Thu, 20 Jun 2024 09:19:41 -0600 Subject: [PATCH] implementation --- package-lock.json | 122 +++++++----------- package.json | 2 +- .../client_encryption.ts | 45 +------ ...nt_side_encryption.prose.06.corpus.test.ts | 2 +- ...prose.22.range_explicit_encryption.test.ts | 51 +++++--- .../client_side_encryption.spec.test.ts | 16 +-- .../node-specific/client_encryption.test.ts | 18 ++- .../read_write_concern.spec.test.js | 3 +- .../retryable_reads.spec.test.ts | 11 +- .../filters/client_encryption_filter.ts | 28 ++-- test/tools/spec-runner/index.js | 11 +- 11 files changed, 135 insertions(+), 174 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8b2b5cc005..4de881a180 100644 --- a/package-lock.json +++ b/package-lock.json @@ -50,7 +50,7 @@ "js-yaml": "^4.1.0", "mocha": "^10.4.0", "mocha-sinon": "^2.1.2", - "mongodb-client-encryption": "^6.0.1", + "mongodb-client-encryption": "^6.1.0-alpha", "mongodb-legacy": "^6.0.1", "nyc": "^15.1.0", "prettier": "^2.8.8", @@ -7729,15 +7729,51 @@ "node": ">=10" } }, - "node_modules/mongodb": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.0.0.tgz", - "integrity": "sha512-wUIYesF4DTyDccm0noE5TwGi9ISdXUAi9T2cQ4xPc+EUBZG44bfMVt2ecOG5Ypca7eCz3oRpJm6YI6c7jAnuNw==", + "node_modules/mongodb-client-encryption": { + "version": "6.1.0-alpha", + "resolved": "https://registry.npmjs.org/mongodb-client-encryption/-/mongodb-client-encryption-6.1.0-alpha.tgz", + "integrity": "sha512-JMXPabcr5P8WybMBDjjSENkc0t3SxyqtumMxjj2BgcwpcvpmP4mQGA22offJCBMBiNXdc6Zdcmh63mH+OCwgZA==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "bindings": "^1.5.0", + "node-addon-api": "^4.3.0", + "prebuild-install": "^7.1.2" + }, + "engines": { + "node": ">=16.20.1" + } + }, + "node_modules/mongodb-connection-string-url": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.0.tgz", + "integrity": "sha512-t1Vf+m1I5hC2M5RJx/7AtxgABy1cZmIPQRMXw+gEIPn/cZNF3Oiy+l0UIypUwVB5trcWHq3crg2g3uAR9aAwsQ==", + "dependencies": { + "@types/whatwg-url": "^11.0.2", + "whatwg-url": "^13.0.0" + } + }, + "node_modules/mongodb-legacy": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/mongodb-legacy/-/mongodb-legacy-6.0.1.tgz", + "integrity": "sha512-cbm3UOqAYo6yElNk9CHNp5tQwRl1PCpBpcSkVvlvyg7S+gG6vSt1zrFiEniNaWOwwWnIr/IsAeSo48Nm701B/A==", + "dev": true, + "dependencies": { + "mongodb": "^6.0.0" + }, + "engines": { + "node": ">=16.20.1" + } + }, + "node_modules/mongodb-legacy/node_modules/mongodb": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.7.0.tgz", + "integrity": "sha512-TMKyHdtMcO0fYBNORiYdmM25ijsHs+Njs963r4Tro4OQZzqYigAzYQouwWRg4OIaiLRUEGUh/1UAcH5lxdSLIA==", "dev": true, "dependencies": { - "@mongodb-js/saslprep": "^1.1.0", - "bson": "^6.0.0", - "mongodb-connection-string-url": "^2.6.0" + "@mongodb-js/saslprep": "^1.1.5", + "bson": "^6.7.0", + "mongodb-connection-string-url": "^3.0.0" }, "engines": { "node": ">=16.20.1" @@ -7775,12 +7811,14 @@ } } }, - "node_modules/mongodb-client-encryption": { + "node_modules/mongodb-legacy/node_modules/mongodb-client-encryption": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/mongodb-client-encryption/-/mongodb-client-encryption-6.0.1.tgz", "integrity": "sha512-u6pKu9plR7hQH6VtsfYonC9dwWAM3HFEpi+Xy3EJIdUyoH6dlFgaxX8TnKx/Ycfi2I1cxTXq2IbhSpg157vVgg==", "dev": true, "hasInstallScript": true, + "optional": true, + "peer": true, "dependencies": { "bindings": "^1.5.0", "node-addon-api": "^4.3.0", @@ -7790,72 +7828,6 @@ "node": ">=16.20.1" } }, - "node_modules/mongodb-connection-string-url": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.0.tgz", - "integrity": "sha512-t1Vf+m1I5hC2M5RJx/7AtxgABy1cZmIPQRMXw+gEIPn/cZNF3Oiy+l0UIypUwVB5trcWHq3crg2g3uAR9aAwsQ==", - "dependencies": { - "@types/whatwg-url": "^11.0.2", - "whatwg-url": "^13.0.0" - } - }, - "node_modules/mongodb-legacy": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/mongodb-legacy/-/mongodb-legacy-6.0.1.tgz", - "integrity": "sha512-cbm3UOqAYo6yElNk9CHNp5tQwRl1PCpBpcSkVvlvyg7S+gG6vSt1zrFiEniNaWOwwWnIr/IsAeSo48Nm701B/A==", - "dev": true, - "dependencies": { - "mongodb": "^6.0.0" - }, - "engines": { - "node": ">=16.20.1" - } - }, - "node_modules/mongodb/node_modules/@types/whatwg-url": { - "version": "8.2.2", - "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-8.2.2.tgz", - "integrity": "sha512-FtQu10RWgn3D9U4aazdwIE2yzphmTJREDqNdODHrbrZmmMqI0vMheC/6NE/J1Yveaj8H+ela+YwWTjq5PGmuhA==", - "dev": true, - "dependencies": { - "@types/node": "*", - "@types/webidl-conversions": "*" - } - }, - "node_modules/mongodb/node_modules/mongodb-connection-string-url": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-2.6.0.tgz", - "integrity": "sha512-WvTZlI9ab0QYtTYnuMLgobULWhokRjtC7db9LtcVfJ+Hsnyr5eo6ZtNAt3Ly24XZScGMelOcGtm7lSn0332tPQ==", - "dev": true, - "dependencies": { - "@types/whatwg-url": "^8.2.1", - "whatwg-url": "^11.0.0" - } - }, - "node_modules/mongodb/node_modules/tr46": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz", - "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==", - "dev": true, - "dependencies": { - "punycode": "^2.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/mongodb/node_modules/whatwg-url": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz", - "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==", - "dev": true, - "dependencies": { - "tr46": "^3.0.0", - "webidl-conversions": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", diff --git a/package.json b/package.json index eeb8a9fd37..e7bf168f9c 100644 --- a/package.json +++ b/package.json @@ -98,7 +98,7 @@ "js-yaml": "^4.1.0", "mocha": "^10.4.0", "mocha-sinon": "^2.1.2", - "mongodb-client-encryption": "^6.0.1", + "mongodb-client-encryption": "^6.1.0-alpha", "mongodb-legacy": "^6.0.1", "nyc": "^15.1.0", "prettier": "^2.8.8", diff --git a/src/client-side-encryption/client_encryption.ts b/src/client-side-encryption/client_encryption.ts index 49747b6f6e..b78b354996 100644 --- a/src/client-side-encryption/client_encryption.ts +++ b/src/client-side-encryption/client_encryption.ts @@ -595,7 +595,7 @@ export class ClientEncryption { /** * Encrypts a Match Expression or Aggregate Expression to query a range index. * - * Only supported when queryType is "rangePreview" and algorithm is "RangePreview". + * Only supported when queryType is "range" and algorithm is "Range". * * @experimental The Range algorithm is experimental only. It is not intended for production use. It is subject to breaking changes. * @@ -737,7 +737,7 @@ export interface ClientEncryptionEncryptOptions { | 'AEAD_AES_256_CBC_HMAC_SHA_512-Random' | 'Indexed' | 'Unindexed' - | 'RangePreview'; + | 'Range'; /** * The id of the Binary dataKey to use for encryption @@ -757,7 +757,7 @@ export interface ClientEncryptionEncryptOptions { * * @experimental Public Technical Preview: The queryType `rangePreview` is experimental. */ - queryType?: 'equality' | 'rangePreview'; + queryType?: 'equality' | 'range'; /** @experimental Public Technical Preview: The index options for a Queryable Encryption field supporting "rangePreview" queries.*/ rangeOptions?: RangeOptions; @@ -957,42 +957,3 @@ export interface RangeOptions { sparsity: Long; precision?: number; } - -/** - * @public - * Options to provide when encrypting data. - */ -export interface ClientEncryptionEncryptOptions { - /** - * The algorithm to use for encryption. - */ - algorithm: - | 'AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic' - | 'AEAD_AES_256_CBC_HMAC_SHA_512-Random' - | 'Indexed' - | 'Unindexed' - | 'RangePreview'; - - /** - * The id of the Binary dataKey to use for encryption - */ - keyId?: Binary; - - /** - * A unique string name corresponding to an already existing dataKey. - */ - keyAltName?: string; - - /** The contention factor. */ - contentionFactor?: bigint | number; - - /** - * The query type supported. Only the queryType `equality` is stable. - * - * @experimental Public Technical Preview: The queryType `rangePreview` is experimental. - */ - queryType?: 'equality' | 'rangePreview'; - - /** @experimental Public Technical Preview: The index options for a Queryable Encryption field supporting "rangePreview" queries.*/ - rangeOptions?: RangeOptions; -} diff --git a/test/integration/client-side-encryption/client_side_encryption.prose.06.corpus.test.ts b/test/integration/client-side-encryption/client_side_encryption.prose.06.corpus.test.ts index b6615267bf..edbf8efaf5 100644 --- a/test/integration/client-side-encryption/client_side_encryption.prose.06.corpus.test.ts +++ b/test/integration/client-side-encryption/client_side_encryption.prose.06.corpus.test.ts @@ -15,7 +15,7 @@ describe('Client Side Encryption Prose Corpus Test', function () { const metadata = { requires: { mongodb: '>=4.2.0', - clientSideEncryption: true + clientSideEncryption: true as const } }; diff --git a/test/integration/client-side-encryption/client_side_encryption.prose.22.range_explicit_encryption.test.ts b/test/integration/client-side-encryption/client_side_encryption.prose.22.range_explicit_encryption.test.ts index 26f588bf61..e77531123d 100644 --- a/test/integration/client-side-encryption/client_side_encryption.prose.22.range_explicit_encryption.test.ts +++ b/test/integration/client-side-encryption/client_side_encryption.prose.22.range_explicit_encryption.test.ts @@ -20,13 +20,13 @@ const getKmsProviders = () => { const metaData: MongoDBMetadataUI = { requires: { - clientSideEncryption: true, + clientSideEncryption: '>=6.0.1', // The Range Explicit Encryption tests require MongoDB server 7.0+ for QE v2. // The tests must not run against a standalone. // - // `rangePreview` is not supported on 8.0+ servers. - mongodb: '>=7.0.0 <8.0.0', + // `range` is not supported on 8.0+ servers. + mongodb: '>=8.0.0', topology: '!single' } }; @@ -60,7 +60,8 @@ const dataTypes: ReadonlyArray<{ { type: 'DecimalNoPrecision', rangeOptions: prepareOptions({ - sparsity: { $numberLong: '1' } + sparsity: { $numberLong: '1' }, + trimFactor: { $numberInt: '1' } }), factory: value => new Decimal128(value.toString()) }, @@ -69,6 +70,7 @@ const dataTypes: ReadonlyArray<{ rangeOptions: prepareOptions({ min: { $numberDecimal: '0' }, max: { $numberDecimal: '200' }, + trimFactor: { $numberInt: '1' }, sparsity: { $numberLong: '1' }, precision: 2 }), @@ -76,7 +78,10 @@ const dataTypes: ReadonlyArray<{ }, { type: 'DoubleNoPrecision', - rangeOptions: prepareOptions({ sparsity: { $numberLong: '1' } }), + rangeOptions: prepareOptions({ + trimFactor: { $numberInt: '1' }, + sparsity: { $numberLong: '1' } + }), factory: value => new Double(value) }, { @@ -84,6 +89,7 @@ const dataTypes: ReadonlyArray<{ rangeOptions: prepareOptions({ min: { $numberDouble: '0' }, max: { $numberDouble: '200' }, + trimFactor: { $numberInt: '1' }, sparsity: { $numberLong: '1' }, precision: 2 }), @@ -94,6 +100,7 @@ const dataTypes: ReadonlyArray<{ rangeOptions: prepareOptions({ min: { $date: { $numberLong: '0' } }, max: { $date: { $numberLong: '200' } }, + trimFactor: { $numberInt: '1' }, sparsity: { $numberLong: '1' } }), factory: value => new Date(value) @@ -103,6 +110,7 @@ const dataTypes: ReadonlyArray<{ rangeOptions: prepareOptions({ min: { $numberInt: '0' }, max: { $numberInt: '200' }, + trimFactor: { $numberInt: '1' }, sparsity: { $numberLong: '1' } }), factory: value => value @@ -112,6 +120,7 @@ const dataTypes: ReadonlyArray<{ rangeOptions: prepareOptions({ min: { $numberLong: '0' }, max: { $numberLong: '200' }, + trimFactor: { $numberInt: '1' }, sparsity: { $numberLong: '1' } }), factory: value => Long.fromNumber(value) @@ -217,9 +226,9 @@ describe('Range Explicit Encryption', function () { const opts = { keyId, - algorithm: 'RangePreview', + algorithm: 'Range', contentionFactor: 0, - rangeOptions // TODO: is this the correct place to encrypt with rangeOpts? + rangeOptions }; encryptedZero = await clientEncryption.encrypt(factory(0), opts); @@ -257,14 +266,14 @@ describe('Range Explicit Encryption', function () { }); afterEach(async function () { - await keyVaultClient.close(); - await encryptedClient.close(); + await keyVaultClient?.close(); + await encryptedClient?.close(); }); it('Case 1: can decrypt a payload', metaData, async function () { const insertedPayload = await clientEncryption.encrypt(factory(6), { keyId, - algorithm: 'RangePreview', + algorithm: 'Range', contentionFactor: 0, rangeOptions }); @@ -283,8 +292,8 @@ describe('Range Explicit Encryption', function () { const findPayload = await clientEncryption.encryptExpression(query, { keyId, - algorithm: 'RangePreview', - queryType: 'rangePreview', + algorithm: 'Range', + queryType: 'range', contentionFactor: 0, rangeOptions }); @@ -335,8 +344,8 @@ describe('Range Explicit Encryption', function () { const findPayload = await clientEncryption.encryptExpression(query, { keyId, - algorithm: 'RangePreview', - queryType: 'rangePreview', + algorithm: 'Range', + queryType: 'range', contentionFactor: 0, rangeOptions }); @@ -380,8 +389,8 @@ describe('Range Explicit Encryption', function () { const findPayload = await clientEncryption.encryptExpression(query, { keyId, - algorithm: 'RangePreview', - queryType: 'rangePreview', + algorithm: 'Range', + queryType: 'range', contentionFactor: 0, rangeOptions }); @@ -408,8 +417,8 @@ describe('Range Explicit Encryption', function () { const findPayload = await clientEncryption.encryptExpression(query, { keyId, - algorithm: 'RangePreview', - queryType: 'rangePreview', + algorithm: 'Range', + queryType: 'range', contentionFactor: 0, rangeOptions }); @@ -458,7 +467,7 @@ describe('Range Explicit Encryption', function () { const resultOrError = await clientEncryption .encrypt(factory(201), { keyId, - algorithm: 'RangePreview', + algorithm: 'Range', contentionFactor: 0, rangeOptions }) @@ -485,7 +494,7 @@ describe('Range Explicit Encryption', function () { const resultOrError = await clientEncryption .encrypt(payload, { keyId, - algorithm: 'RangePreview', + algorithm: 'Range', contentionFactor: 0, rangeOptions }) @@ -528,7 +537,7 @@ describe('Range Explicit Encryption', function () { const resultOrError = await clientEncryption .encrypt(factory(6), { keyId, - algorithm: 'RangePreview', + algorithm: 'Range', contentionFactor: 0, rangeOptions: options }) diff --git a/test/integration/client-side-encryption/client_side_encryption.spec.test.ts b/test/integration/client-side-encryption/client_side_encryption.spec.test.ts index 77bcdf69c3..2f59171670 100644 --- a/test/integration/client-side-encryption/client_side_encryption.spec.test.ts +++ b/test/integration/client-side-encryption/client_side_encryption.spec.test.ts @@ -84,7 +84,7 @@ describe('Client Side Encryption (Legacy)', function () { generateTopologyTests(testSuites, testContext, ({ description }) => { if (SKIPPED_TESTS.has(description)) { - return false; + return 'Skipped by generic test name skip filter.'; } if (isServerless) { // TODO(NODE-4730): Fix failing csfle tests against serverless @@ -93,7 +93,7 @@ describe('Client Side Encryption (Legacy)', function () { 'encryptedFieldsMap is preferred over remote encryptedFields' ].includes(description); - return !isSkippedTest; + return isSkippedTest ? 'TODO(NODE-4730): Fix failing csfle tests against serverless' : true; } if ( @@ -102,13 +102,11 @@ describe('Client Side Encryption (Legacy)', function () { 'Automatically encrypt and decrypt with a named KMS provider' ].includes(description) ) { - if ( - typeof filter.filter({ - metadata: { requires: { clientSideEncryption: '>=6.0.1' } } - }) === 'string' - ) { - return false; - } + const result = filter.filter({ + metadata: { requires: { clientSideEncryption: '>=6.0.1' } } + }); + + if (typeof result === 'string') return result; } return true; diff --git a/test/integration/node-specific/client_encryption.test.ts b/test/integration/node-specific/client_encryption.test.ts index 7b22333fa4..b0fde3256f 100644 --- a/test/integration/node-specific/client_encryption.test.ts +++ b/test/integration/node-specific/client_encryption.test.ts @@ -321,7 +321,7 @@ describe('ClientEncryption integration tests', function () { }); completeOptions = { - algorithm: 'RangePreview', + algorithm: 'Range', contentionFactor: 0, rangeOptions: { min: new Long(0), @@ -353,6 +353,11 @@ describe('ClientEncryption integration tests', function () { const expression = { $and: [{ someField: { $gt: 1 } }] }; + const metadata: MongoDBMetadataUI = { + requires: { + clientSideEncryption: '>=6.0.1' + } + }; beforeEach(async function () { clientEncryption = new ClientEncryption(client, { @@ -366,12 +371,13 @@ describe('ClientEncryption integration tests', function () { }); completeOptions = { - algorithm: 'RangePreview', - queryType: 'rangePreview', + algorithm: 'Range', + queryType: 'range', contentionFactor: 0, rangeOptions: { min: new Int32(0), max: new Int32(10), + trimFactor: new Int32(1), sparsity: new Long(1) }, keyId: dataKey @@ -396,7 +402,7 @@ describe('ClientEncryption integration tests', function () { expect(errorOrResult).to.be.instanceof(TypeError); }); - it(`throws if algorithm does not equal 'rangePreview'`, metadata, async function () { + it(`throws if algorithm does not equal 'range'`, metadata, async function () { completeOptions['algorithm'] = 'equality'; const errorOrResult = await clientEncryption .encryptExpression(expression, completeOptions) @@ -406,10 +412,10 @@ describe('ClientEncryption integration tests', function () { }); it( - `does not throw if algorithm has different casing than 'rangePreview'`, + `does not throw if algorithm has different casing than 'range'`, metadata, async function () { - completeOptions['algorithm'] = 'rAnGePrEvIeW'; + completeOptions['algorithm'] = 'rAnGe'; const errorOrResult = await clientEncryption .encryptExpression(expression, completeOptions) .catch(e => e); diff --git a/test/integration/read-write-concern/read_write_concern.spec.test.js b/test/integration/read-write-concern/read_write_concern.spec.test.js index 2104cb7b91..1374a8e8b4 100644 --- a/test/integration/read-write-concern/read_write_concern.spec.test.js +++ b/test/integration/read-write-concern/read_write_concern.spec.test.js @@ -16,8 +16,7 @@ describe('Read Write Concern spec tests', function () { generateTopologyTests(testSuites, testContext, ({ description }) => { if (description === 'MapReduce omits default write concern') { - // The node driver does not have a mapReduce collection helper - return false; + return 'The node driver does not have a mapReduce collection helper'; } return true; }); diff --git a/test/integration/retryable-reads/retryable_reads.spec.test.ts b/test/integration/retryable-reads/retryable_reads.spec.test.ts index 594ec2b6fd..15eb7b97b8 100644 --- a/test/integration/retryable-reads/retryable_reads.spec.test.ts +++ b/test/integration/retryable-reads/retryable_reads.spec.test.ts @@ -15,7 +15,7 @@ describe('Retryable Reads (legacy)', function () { }); generateTopologyTests(testSuites, testContext, spec => { - return ( + const shouldRun = spec.description.match(/distinct/i) || spec.description.match(/aggregate/i) || spec.description.match(/countDocuments/i) || @@ -26,8 +26,13 @@ describe('Retryable Reads (legacy)', function () { spec.description.match(/listCollectionNames/i) || spec.description.match(/estimatedDocumentCount/i) || spec.description.match(/count/i) || - spec.description.match(/find/i) - ); + spec.description.match(/find/i); + + if (!shouldRun) { + return 'Test skipped by generic filter.'; + } + + return true; }); }); diff --git a/test/tools/runner/filters/client_encryption_filter.ts b/test/tools/runner/filters/client_encryption_filter.ts index f9f2ec9093..6437828c18 100644 --- a/test/tools/runner/filters/client_encryption_filter.ts +++ b/test/tools/runner/filters/client_encryption_filter.ts @@ -23,8 +23,10 @@ export class ClientSideEncryptionFilter extends Filter { enabled: boolean; static version = null; + csfleKMSProviders = null; + override async initializeFilter(client: MongoClient, context: Record) { - const CSFLE_KMS_PROVIDERS = process.env.CSFLE_KMS_PROVIDERS; + this.csfleKMSProviders = process.env.CSFLE_KMS_PROVIDERS; let mongodbClientEncryption; try { mongodbClientEncryption = require('mongodb-client-encryption'); @@ -41,13 +43,13 @@ export class ClientSideEncryptionFilter extends Filter { ) ).version; - this.enabled = !!(CSFLE_KMS_PROVIDERS && mongodbClientEncryption); + this.enabled = !!(this.csfleKMSProviders && mongodbClientEncryption); // Adds these fields onto the context so that they can be reused by tests context.clientSideEncryption = { enabled: this.enabled, mongodbClientEncryption, - CSFLE_KMS_PROVIDERS, + CSFLE_KMS_PROVIDERS: this.csfleKMSProviders, version: ClientSideEncryptionFilter.version }; } @@ -67,15 +69,21 @@ export class ClientSideEncryptionFilter extends Filter { } // TODO(NODE-3401): unskip csfle tests on windows - if (process.env.TEST_CSFLE && !this.enabled && process.platform !== 'win32') { - throw new Error('Expected CSFLE to be enabled in the CI'); + if (process.env.TEST_CSFLE && process.platform !== 'win32') { + if (!this.csfleKMSProviders) { + throw new Error('FLE tests must run, but no KMS providers were set in the environment.'); + } + if (ClientSideEncryptionFilter.version == null) { + throw new Error('FLE tests must run, but mongodb client encryption was not installed.'); + } } - const validRange = typeof clientSideEncryption === 'string' ? clientSideEncryption : '>=0.0.0'; - if (ClientSideEncryptionFilter.version && !this.enabled) - return 'Test requires FLE environment variables..'; - if (!this.enabled) return 'Test requires CSFLE to be enabled.'; - return satisfies(ClientSideEncryptionFilter.version, validRange) + if (this.csfleKMSProviders == null) return 'Test requires FLE environment variables.'; + if (ClientSideEncryptionFilter.version == null) + return 'Test requires mongodb-client-encryption to be installed.'; + + const validRange = typeof clientSideEncryption === 'string' ? clientSideEncryption : '>=0.0.0'; + return satisfies(ClientSideEncryptionFilter.version, validRange, { includePrerelease: true }) ? true : `requires mongodb-client-encryption ${validRange}, received ${ClientSideEncryptionFilter.version}`; } diff --git a/test/tools/spec-runner/index.js b/test/tools/spec-runner/index.js index 3fb15d783f..f774465cd5 100644 --- a/test/tools/spec-runner/index.js +++ b/test/tools/spec-runner/index.js @@ -152,7 +152,7 @@ function legacyRunOnToRunOnRequirement(runOn) { } /** - * @param {((test: { description: string }) => boolean)?} filter a function that returns true for any tests that should run, false otherwise. + * @param {((test: { description: string }) => true | string)?} filter a function that returns true for any tests that should run, false otherwise. */ function generateTopologyTests(testSuites, testContext, filter) { for (const testSuite of testSuites) { @@ -187,9 +187,12 @@ function generateTopologyTests(testSuites, testContext, filter) { shouldRun = false; } - if (shouldRun && typeof filter === 'function' && !filter(spec, this.configuration)) { - this.currentTest.skipReason = `filtered by custom filter passed to generateTopologyTests`; - shouldRun = false; + if (shouldRun && typeof filter === 'function') { + const result = filter(spec, this.configuration); + if (typeof result === 'string') { + this.currentTest.skipReason = result; + shouldRun = false; + } } let csfleFilterError = null;