From a665ba33d2e15adeba09a7c2f5f3493b26107792 Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Wed, 2 Jul 2025 17:08:22 +0200 Subject: [PATCH 01/13] feat(NODE-7009): add client metadata on demand --- src/mongo_client.ts | 11 +- .../mongodb-handshake.prose.test.ts | 8 ++ .../mongodb-handshake.spec.test.ts | 8 ++ .../metadata-not-propagated.json | 100 ++++++++++++++++++ .../metadata-not-propagated.yml | 58 ++++++++++ test/tools/unified-spec-runner/operations.ts | 5 + 6 files changed, 189 insertions(+), 1 deletion(-) create mode 100644 test/integration/mongodb-handshake/mongodb-handshake.spec.test.ts create mode 100644 test/spec/mongodb-handshake/metadata-not-propagated.json create mode 100644 test/spec/mongodb-handshake/metadata-not-propagated.yml diff --git a/src/mongo_client.ts b/src/mongo_client.ts index 77b6559049..aad17b7bb6 100644 --- a/src/mongo_client.ts +++ b/src/mongo_client.ts @@ -14,7 +14,7 @@ import { type TokenCache } from './cmap/auth/mongodb_oidc/token_cache'; import { AuthMechanism } from './cmap/auth/providers'; import type { LEGAL_TCP_SOCKET_OPTIONS, LEGAL_TLS_SOCKET_OPTIONS } from './cmap/connect'; import type { Connection } from './cmap/connection'; -import type { ClientMetadata } from './cmap/handshake/client_metadata'; +import { type ClientMetadata, makeClientMetadata } from './cmap/handshake/client_metadata'; import type { CompressorName } from './cmap/wire_protocol/compression'; import { parseOptions, resolveSRVRecord } from './connection_string'; import { MONGO_CLIENT_EVENTS } from './constants'; @@ -459,6 +459,15 @@ export class MongoClient extends TypedEventEmitter implements await this.close(); } + /** + * Append metadata to the client metadata after instantiation. + * @param driverInfo - Information abou the application or libraary. + */ + appendMetadata(driverInfo: DriverInfo) { + this.s.options.driverInfo = driverInfo; + this.s.options.metadata = makeClientMetadata(this.s.options); + } + /** @internal */ private checkForNonGenuineHosts() { const documentDBHostnames = this.options.hosts.filter((hostAddress: HostAddress) => diff --git a/test/integration/mongodb-handshake/mongodb-handshake.prose.test.ts b/test/integration/mongodb-handshake/mongodb-handshake.prose.test.ts index d2180bd638..08d371cca8 100644 --- a/test/integration/mongodb-handshake/mongodb-handshake.prose.test.ts +++ b/test/integration/mongodb-handshake/mongodb-handshake.prose.test.ts @@ -194,3 +194,11 @@ describe('Handshake Prose Tests', function () { }); }); }); + +describe('Client Metadata Update Prose Tests', function () { + let client: MongoClient; + + afterEach(async function () { + await client?.close(); + }); +}); diff --git a/test/integration/mongodb-handshake/mongodb-handshake.spec.test.ts b/test/integration/mongodb-handshake/mongodb-handshake.spec.test.ts new file mode 100644 index 0000000000..7db7ed99ea --- /dev/null +++ b/test/integration/mongodb-handshake/mongodb-handshake.spec.test.ts @@ -0,0 +1,8 @@ +import { join } from 'path'; + +import { loadSpecTests } from '../../spec'; +import { runUnifiedSuite } from '../../tools/unified-spec-runner/runner'; + +describe('MongoDB Handshake Tests (Unified)', function () { + runUnifiedSuite(loadSpecTests(join('mongodb-handshake'))); +}); diff --git a/test/spec/mongodb-handshake/metadata-not-propagated.json b/test/spec/mongodb-handshake/metadata-not-propagated.json new file mode 100644 index 0000000000..500b579b89 --- /dev/null +++ b/test/spec/mongodb-handshake/metadata-not-propagated.json @@ -0,0 +1,100 @@ +{ + "description": "client metadata is not propagated to the server", + "schemaVersion": "1.9", + "runOnRequirements": [ + { + "minServerVersion": "6.0" + } + ], + "createEntities": [ + { + "client": { + "id": "client", + "observeEvents": [ + "commandSucceededEvent", + "commandFailedEvent", + "connectionClosedEvent", + "connectionCreatedEvent" + ] + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "test" + } + } + ], + "tests": [ + { + "description": "metadata append does not create new connections or close existing ones and no hello command is sent", + "operations": [ + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "ping", + "command": { + "ping": 1 + } + }, + "expectResult": { + "ok": 1 + } + }, + { + "name": "appendMetadata", + "object": "client", + "arguments": { + "driverInfoOptions": { + "name": "framework", + "version": "2.0", + "platform": "Framework Platform" + } + } + }, + { + "name": "runCommand", + "object": "database", + "arguments": { + "commandName": "ping", + "command": { + "ping": 1 + } + }, + "expectResult": { + "ok": 1 + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCreatedEvent": {} + } + ] + }, + { + "client": "client", + "eventType": "command", + "events": [ + { + "commandSucceededEvent": { + "commandName": "ping" + } + }, + { + "commandSucceededEvent": { + "commandName": "ping" + } + } + ] + } + ] + } + ] +} diff --git a/test/spec/mongodb-handshake/metadata-not-propagated.yml b/test/spec/mongodb-handshake/metadata-not-propagated.yml new file mode 100644 index 0000000000..4ff911a739 --- /dev/null +++ b/test/spec/mongodb-handshake/metadata-not-propagated.yml @@ -0,0 +1,58 @@ +description: client metadata is not propagated to the server +schemaVersion: '1.9' +runOnRequirements: + - minServerVersion: '6.0' +createEntities: + - client: + id: &client client + observeEvents: + - commandSucceededEvent + - commandFailedEvent + - connectionClosedEvent + - connectionCreatedEvent + - database: + id: &database database + client: *client + databaseName: test +tests: + # Test that appending metadata after `MongoClient` initialization does not close existing + # connections, create new ones, and that no new `hello` command is sent. + - description: metadata append does not create new connections or close existing ones and no hello command is sent + operations: + - name: runCommand + object: *database + arguments: + commandName: ping + command: + ping: 1 + expectResult: + ok: 1 + - name: appendMetadata + object: *client + arguments: + driverInfoOptions: + name: framework + version: '2.0' + platform: Framework Platform + - name: runCommand + object: *database + arguments: + commandName: ping + command: + ping: 1 + expectResult: + ok: 1 + expectEvents: + - client: *client + eventType: cmap + events: + # Expect only one connection to be created for the first 'ping' command. + - connectionCreatedEvent: { } + - client: *client + eventType: command + events: + - commandSucceededEvent: + commandName: ping + - commandSucceededEvent: + commandName: ping + diff --git a/test/tools/unified-spec-runner/operations.ts b/test/tools/unified-spec-runner/operations.ts index 19c31c743d..d208d49fc8 100644 --- a/test/tools/unified-spec-runner/operations.ts +++ b/test/tools/unified-spec-runner/operations.ts @@ -665,6 +665,11 @@ operations.set('recordTopologyDescription', async ({ entities, operation }) => { entities.set(id, description!); }); +operations.set('appendMetadata', async ({ entities, operation }) => { + const client = entities.getEntity('client', operation.object); + client.appendMetadata(operation.arguments.driverInfoOptions); +}); + operations.set('assertTopologyType', async ({ entities, operation }) => { const { topologyDescription, From 454fcbf6b7015362eabe95f5ee3849d25f823bf4 Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Wed, 2 Jul 2025 21:08:23 +0200 Subject: [PATCH 02/13] test: first section prose tests --- src/cmap/connection_pool.ts | 6 +- src/cmap/handshake/client_metadata.ts | 22 ++++- src/mongo_client.ts | 39 +++++++-- .../mongodb-handshake.prose.test.ts | 84 +++++++++++++++++++ 4 files changed, 144 insertions(+), 7 deletions(-) diff --git a/src/cmap/connection_pool.ts b/src/cmap/connection_pool.ts index 976e280aae..946b568cd0 100644 --- a/src/cmap/connection_pool.ts +++ b/src/cmap/connection_pool.ts @@ -610,13 +610,17 @@ export class ConnectionPool extends TypedEventEmitter { } private createConnection(callback: Callback) { + // Note that metadata and extendedMetadata may have changed on the client but have + // been frozen here, so we pull the extendedMetadata promise always from the client + // no mattter what options were set at the construction of the pool. const connectOptions: ConnectionOptions = { ...this.options, id: this.connectionCounter.next().value, generation: this.generation, cancellationToken: this.cancellationToken, mongoLogger: this.mongoLogger, - authProviders: this.server.topology.client.s.authProviders + authProviders: this.server.topology.client.s.authProviders, + extendedMetadata: this.server.topology.client.options.extendedMetadata }; this.pending++; diff --git a/src/cmap/handshake/client_metadata.ts b/src/cmap/handshake/client_metadata.ts index d8dbe7c7ad..b820dbb4f6 100644 --- a/src/cmap/handshake/client_metadata.ts +++ b/src/cmap/handshake/client_metadata.ts @@ -90,7 +90,10 @@ export class LimitedSizeDocument { } } -type MakeClientMetadataOptions = Pick; +type MakeClientMetadataOptions = Pick< + MongoOptions, + 'appName' | 'driverInfo' | 'additionalDriverInfo' +>; /** * From the specs: * Implementors SHOULD cumulatively update fields in the following order until the document is under the size limit: @@ -119,6 +122,17 @@ export function makeClientMetadata(options: MakeClientMetadataOptions): ClientMe version: version.length > 0 ? `${NODE_DRIVER_VERSION}|${version}` : NODE_DRIVER_VERSION }; + // This is where we handle additional driver info added after client construction. + if (options.additionalDriverInfo) { + const { name: n = '', version: v = '' } = options.additionalDriverInfo; + if (n.length > 0) { + driverInfo.name = `${driverInfo.name}|${n}`; + } + if (v.length > 0) { + driverInfo.version = `${driverInfo.version}|${v}`; + } + } + if (!metadataDocument.ifItFitsItSits('driver', driverInfo)) { throw new MongoInvalidArgumentError( 'Unable to include driverInfo name and version, metadata cannot exceed 512 bytes' @@ -129,6 +143,12 @@ export function makeClientMetadata(options: MakeClientMetadataOptions): ClientMe if (platform.length > 0) { runtimeInfo = `${runtimeInfo}|${platform}`; } + if (options.additionalDriverInfo) { + const { platform: p = '' } = options.additionalDriverInfo; + if (p.length > 0) { + runtimeInfo = `${runtimeInfo}|${p}`; + } + } if (!metadataDocument.ifItFitsItSits('platform', runtimeInfo)) { throw new MongoInvalidArgumentError( diff --git a/src/mongo_client.ts b/src/mongo_client.ts index aad17b7bb6..72cefa56be 100644 --- a/src/mongo_client.ts +++ b/src/mongo_client.ts @@ -14,7 +14,11 @@ import { type TokenCache } from './cmap/auth/mongodb_oidc/token_cache'; import { AuthMechanism } from './cmap/auth/providers'; import type { LEGAL_TCP_SOCKET_OPTIONS, LEGAL_TLS_SOCKET_OPTIONS } from './cmap/connect'; import type { Connection } from './cmap/connection'; -import { type ClientMetadata, makeClientMetadata } from './cmap/handshake/client_metadata'; +import { + addContainerMetadata, + type ClientMetadata, + makeClientMetadata +} from './cmap/handshake/client_metadata'; import type { CompressorName } from './cmap/wire_protocol/compression'; import { parseOptions, resolveSRVRecord } from './connection_string'; import { MONGO_CLIENT_EVENTS } from './constants'; @@ -398,9 +402,29 @@ export class MongoClient extends TypedEventEmitter implements * The consolidate, parsed, transformed and merged options. */ public readonly options: Readonly< - Omit + Omit< + MongoOptions, + | 'monitorCommands' + | 'ca' + | 'crl' + | 'key' + | 'cert' + | 'additionalDriverInfo' + | 'metadata' + | 'extendedMetadata' + > > & - Pick; + Pick< + MongoOptions, + | 'monitorCommands' + | 'ca' + | 'crl' + | 'key' + | 'cert' + | 'additionalDriverInfo' + | 'metadata' + | 'extendedMetadata' + >; constructor(url: string, options?: MongoClientOptions) { super(); @@ -464,8 +488,11 @@ export class MongoClient extends TypedEventEmitter implements * @param driverInfo - Information abou the application or libraary. */ appendMetadata(driverInfo: DriverInfo) { - this.s.options.driverInfo = driverInfo; - this.s.options.metadata = makeClientMetadata(this.s.options); + this.options.additionalDriverInfo = driverInfo; + this.options.metadata = makeClientMetadata(this.options); + this.options.extendedMetadata = addContainerMetadata(this.options.metadata) + .then(undefined, squashError) + .then(result => result ?? {}); // ensure Promise } /** @internal */ @@ -1053,6 +1080,8 @@ export interface MongoOptions /** @internal */ extendedMetadata: Promise; /** @internal */ + additionalDriverInfo: DriverInfo; + /** @internal */ autoEncrypter?: AutoEncrypter; /** @internal */ tokenCache?: TokenCache; diff --git a/test/integration/mongodb-handshake/mongodb-handshake.prose.test.ts b/test/integration/mongodb-handshake/mongodb-handshake.prose.test.ts index 08d371cca8..c91d57deb7 100644 --- a/test/integration/mongodb-handshake/mongodb-handshake.prose.test.ts +++ b/test/integration/mongodb-handshake/mongodb-handshake.prose.test.ts @@ -8,6 +8,7 @@ import { LEGACY_HELLO_COMMAND, type MongoClient } from '../../mongodb'; +import { sleep } from '../../tools/utils'; type EnvironmentVariables = Array<[string, string]>; @@ -200,5 +201,88 @@ describe('Client Metadata Update Prose Tests', function () { afterEach(async function () { await client?.close(); + sinon.restore(); + }); + + describe('Test 1: Test that the driver updates metadata', function () { + let initialClientMetadata; + let updatedClientMetadata; + + const tests = [ + { testCase: 1, name: 'framework', version: '2.0', platform: 'Framework Platform' }, + { testCase: 2, name: 'framework', version: '2.0' }, + { testCase: 3, name: 'framework', platform: 'Framework Platform' }, + { testCase: 4, name: 'framework' } + ]; + + for (const { testCase, name, version, platform } of tests) { + context(`Case: ${testCase}`, function () { + // 1. Create a `MongoClient` instance with the following: + // - `maxIdleTimeMS` set to `1ms` + // - Wrapping library metadata: + // | Field | Value | + // | -------- | ---------------- | + // | name | library | + // | version | 1.2 | + // | platform | Library Platform | + // 2. Send a `ping` command to the server and verify that the command succeeds. + // 3. Save intercepted `client` document as `initialClientMetadata`. + // 4. Wait 5ms for the connection to become idle. + beforeEach(async function () { + client = this.configuration.newClient( + {}, + { + maxIdleTimeMS: 1, + monitorCommands: true, + driverInfo: { name: 'library', version: '1.2', platform: 'Library Platform' } + } + ); + + sinon.stub(Connection.prototype, 'command').callsFake(async function (ns, cmd, options) { + // @ts-expect-error: sinon will place wrappedMethod on the command method. + const command = Connection.prototype.command.wrappedMethod.bind(this); + + if (cmd.hello || cmd[LEGACY_HELLO_COMMAND]) { + if (!initialClientMetadata) { + initialClientMetadata = cmd.client; + } else { + updatedClientMetadata = cmd.client; + } + } + return command(ns, cmd, options); + }); + + await client.db('test').command({ ping: 1 }); + await sleep(5); + }); + + it('appends the metadata', async function () { + // 1. Append the `DriverInfoOptions` from the selected test case to the `MongoClient` metadata. + // 2. Send a `ping` command to the server and verify: + // - The command succeeds. + // - The framework metadata is appended to the existing `DriverInfoOptions` in the `client.driver` fields of the `hello` + // command, with values separated by a pipe `|`. + client.appendMetadata({ name, version, platform }); + await client.db('test').command({ ping: 1 }); + + expect(updatedClientMetadata.driver.name).to.equal( + name + ? `${initialClientMetadata.driver.name}|${name}` + : initialClientMetadata.driver.name + ); + expect(updatedClientMetadata.driver.version).to.equal( + version + ? `${initialClientMetadata.driver.version}|${version}` + : initialClientMetadata.driver.version + ); + expect(updatedClientMetadata.platform).to.equal( + platform + ? `${initialClientMetadata.platform}|${platform}` + : initialClientMetadata.platform + ); + expect(updatedClientMetadata.os).to.deep.equal(initialClientMetadata.os); + }); + }); + } }); }); From 01921258aff76e248ad707db8a62619a122df828 Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Thu, 3 Jul 2025 00:36:04 +0200 Subject: [PATCH 03/13] test: second round of prose tests --- src/cmap/handshake/client_metadata.ts | 25 +++--- src/connection_string.ts | 3 + src/mongo_client.ts | 8 +- .../mongodb-handshake.prose.test.ts | 79 ++++++++++++++++++- 4 files changed, 98 insertions(+), 17 deletions(-) diff --git a/src/cmap/handshake/client_metadata.ts b/src/cmap/handshake/client_metadata.ts index b820dbb4f6..822a1a8c5a 100644 --- a/src/cmap/handshake/client_metadata.ts +++ b/src/cmap/handshake/client_metadata.ts @@ -123,13 +123,14 @@ export function makeClientMetadata(options: MakeClientMetadataOptions): ClientMe }; // This is where we handle additional driver info added after client construction. - if (options.additionalDriverInfo) { - const { name: n = '', version: v = '' } = options.additionalDriverInfo; - if (n.length > 0) { - driverInfo.name = `${driverInfo.name}|${n}`; - } - if (v.length > 0) { - driverInfo.version = `${driverInfo.version}|${v}`; + if (options.additionalDriverInfo.length > 0) { + for (const { name: n = '', version: v = '' } of options.additionalDriverInfo) { + if (n.length > 0) { + driverInfo.name = `${driverInfo.name}|${n}`; + } + if (v.length > 0) { + driverInfo.version = `${driverInfo.version}|${v}`; + } } } @@ -143,10 +144,12 @@ export function makeClientMetadata(options: MakeClientMetadataOptions): ClientMe if (platform.length > 0) { runtimeInfo = `${runtimeInfo}|${platform}`; } - if (options.additionalDriverInfo) { - const { platform: p = '' } = options.additionalDriverInfo; - if (p.length > 0) { - runtimeInfo = `${runtimeInfo}|${p}`; + + if (options.additionalDriverInfo.length > 0) { + for (const { platform: p = '' } of options.additionalDriverInfo) { + if (p.length > 0) { + runtimeInfo = `${runtimeInfo}|${p}`; + } } } diff --git a/src/connection_string.ts b/src/connection_string.ts index 89bcaf8bc9..edb1a6870a 100644 --- a/src/connection_string.ts +++ b/src/connection_string.ts @@ -535,6 +535,9 @@ export function parseOptions( } ); + // Set the default for the additional driver info. + mongoOptions.additionalDriverInfo = []; + mongoOptions.metadata = makeClientMetadata(mongoOptions); mongoOptions.extendedMetadata = addContainerMetadata(mongoOptions.metadata).then( diff --git a/src/mongo_client.ts b/src/mongo_client.ts index 72cefa56be..e8fa6e50e7 100644 --- a/src/mongo_client.ts +++ b/src/mongo_client.ts @@ -409,6 +409,7 @@ export class MongoClient extends TypedEventEmitter implements | 'crl' | 'key' | 'cert' + | 'driverInfo' | 'additionalDriverInfo' | 'metadata' | 'extendedMetadata' @@ -421,6 +422,7 @@ export class MongoClient extends TypedEventEmitter implements | 'crl' | 'key' | 'cert' + | 'driverInfo' | 'additionalDriverInfo' | 'metadata' | 'extendedMetadata' @@ -488,7 +490,7 @@ export class MongoClient extends TypedEventEmitter implements * @param driverInfo - Information abou the application or libraary. */ appendMetadata(driverInfo: DriverInfo) { - this.options.additionalDriverInfo = driverInfo; + this.options.additionalDriverInfo.push(driverInfo); this.options.metadata = makeClientMetadata(this.options); this.options.extendedMetadata = addContainerMetadata(this.options.metadata) .then(undefined, squashError) @@ -1077,10 +1079,8 @@ export interface MongoOptions dbName: string; /** @deprecated - Will be made internal in a future major release. */ metadata: ClientMetadata; - /** @internal */ extendedMetadata: Promise; - /** @internal */ - additionalDriverInfo: DriverInfo; + additionalDriverInfo: DriverInfo[]; /** @internal */ autoEncrypter?: AutoEncrypter; /** @internal */ diff --git a/test/integration/mongodb-handshake/mongodb-handshake.prose.test.ts b/test/integration/mongodb-handshake/mongodb-handshake.prose.test.ts index c91d57deb7..7a0ecb0a86 100644 --- a/test/integration/mongodb-handshake/mongodb-handshake.prose.test.ts +++ b/test/integration/mongodb-handshake/mongodb-handshake.prose.test.ts @@ -204,7 +204,7 @@ describe('Client Metadata Update Prose Tests', function () { sinon.restore(); }); - describe('Test 1: Test that the driver updates metadata', function () { + describe('Test 2: Multiple Successive Metadata Updates', function () { let initialClientMetadata; let updatedClientMetadata; @@ -233,7 +233,6 @@ describe('Client Metadata Update Prose Tests', function () { {}, { maxIdleTimeMS: 1, - monitorCommands: true, driverInfo: { name: 'library', version: '1.2', platform: 'Library Platform' } } ); @@ -285,4 +284,80 @@ describe('Client Metadata Update Prose Tests', function () { }); } }); + + describe('Test 1: Test that the driver updates metadata', function () { + let initialClientMetadata; + let updatedClientMetadata; + + const tests = [ + { testCase: 1, name: 'framework', version: '2.0', platform: 'Framework Platform' }, + { testCase: 2, name: 'framework', version: '2.0' }, + { testCase: 3, name: 'framework', platform: 'Framework Platform' }, + { testCase: 4, name: 'framework' } + ]; + + for (const { testCase, name, version, platform } of tests) { + context(`Case: ${testCase}`, function () { + // 1. Create a `MongoClient` instance with the following: + // - `maxIdleTimeMS` set to `1ms` + // 2. Append the following `DriverInfoOptions` to the `MongoClient` metadata: + // | Field | Value | + // | -------- | ---------------- | + // | name | library | + // | version | 1.2 | + // | platform | Library Platform | + // 3. Send a `ping` command to the server and verify that the command succeeds. + // 4. Save intercepted `client` document as `updatedClientMetadata`. + // 5. Wait 5ms for the connection to become idle. + beforeEach(async function () { + client = this.configuration.newClient({}, { maxIdleTimeMS: 1 }); + client.appendMetadata({ name: 'library', version: '1.2', platform: 'Library Platform' }); + + sinon.stub(Connection.prototype, 'command').callsFake(async function (ns, cmd, options) { + // @ts-expect-error: sinon will place wrappedMethod on the command method. + const command = Connection.prototype.command.wrappedMethod.bind(this); + + if (cmd.hello || cmd[LEGACY_HELLO_COMMAND]) { + if (!initialClientMetadata) { + initialClientMetadata = cmd.client; + } else { + updatedClientMetadata = cmd.client; + } + } + return command(ns, cmd, options); + }); + + await client.db('test').command({ ping: 1 }); + await sleep(5); + }); + + it('appends the metadata', async function () { + // 1. Append the `DriverInfoOptions` from the selected test case to the `MongoClient` metadata. + // 2. Send a `ping` command to the server and verify: + // - The command succeeds. + // - The framework metadata is appended to the existing `DriverInfoOptions` in the `client.driver` fields of the `hello` + // command, with values separated by a pipe `|`. + client.appendMetadata({ name, version, platform }); + await client.db('test').command({ ping: 1 }); + + expect(updatedClientMetadata.driver.name).to.equal( + name + ? `${initialClientMetadata.driver.name}|${name}` + : initialClientMetadata.driver.name + ); + expect(updatedClientMetadata.driver.version).to.equal( + version + ? `${initialClientMetadata.driver.version}|${version}` + : initialClientMetadata.driver.version + ); + expect(updatedClientMetadata.platform).to.equal( + platform + ? `${initialClientMetadata.platform}|${platform}` + : initialClientMetadata.platform + ); + expect(updatedClientMetadata.os).to.deep.equal(initialClientMetadata.os); + }); + }); + } + }); }); From 6556d6d5c34fec31a075ef1ec286fde313ca5a81 Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Thu, 3 Jul 2025 01:07:53 +0200 Subject: [PATCH 04/13] test: fix unit test --- src/cmap/handshake/client_metadata.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cmap/handshake/client_metadata.ts b/src/cmap/handshake/client_metadata.ts index 822a1a8c5a..626905ebc3 100644 --- a/src/cmap/handshake/client_metadata.ts +++ b/src/cmap/handshake/client_metadata.ts @@ -123,7 +123,7 @@ export function makeClientMetadata(options: MakeClientMetadataOptions): ClientMe }; // This is where we handle additional driver info added after client construction. - if (options.additionalDriverInfo.length > 0) { + if (options.additionalDriverInfo?.length > 0) { for (const { name: n = '', version: v = '' } of options.additionalDriverInfo) { if (n.length > 0) { driverInfo.name = `${driverInfo.name}|${n}`; @@ -145,7 +145,7 @@ export function makeClientMetadata(options: MakeClientMetadataOptions): ClientMe runtimeInfo = `${runtimeInfo}|${platform}`; } - if (options.additionalDriverInfo.length > 0) { + if (options.additionalDriverInfo?.length > 0) { for (const { platform: p = '' } of options.additionalDriverInfo) { if (p.length > 0) { runtimeInfo = `${runtimeInfo}|${p}`; From f336bf674f3fe4238ccf12f65b7757c4a93c6330 Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Thu, 3 Jul 2025 23:48:48 +0200 Subject: [PATCH 05/13] Update src/mongo_client.ts Co-authored-by: Daria Pardue --- src/mongo_client.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mongo_client.ts b/src/mongo_client.ts index e8fa6e50e7..74163da461 100644 --- a/src/mongo_client.ts +++ b/src/mongo_client.ts @@ -487,7 +487,7 @@ export class MongoClient extends TypedEventEmitter implements /** * Append metadata to the client metadata after instantiation. - * @param driverInfo - Information abou the application or libraary. + * @param driverInfo - Information about the application or library. */ appendMetadata(driverInfo: DriverInfo) { this.options.additionalDriverInfo.push(driverInfo); From 3c51156ddcecbc7a2041782eba180e927b0fa1db Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Sat, 5 Jul 2025 16:48:23 -0400 Subject: [PATCH 06/13] test: fix cmap runner --- src/cmap/connection_pool.ts | 2 +- .../pool-create-min-size-error.json | 2 +- test/tools/cmap_spec_runner.ts | 19 +++++++++++-------- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/cmap/connection_pool.ts b/src/cmap/connection_pool.ts index 946b568cd0..8d747a5174 100644 --- a/src/cmap/connection_pool.ts +++ b/src/cmap/connection_pool.ts @@ -620,7 +620,7 @@ export class ConnectionPool extends TypedEventEmitter { cancellationToken: this.cancellationToken, mongoLogger: this.mongoLogger, authProviders: this.server.topology.client.s.authProviders, - extendedMetadata: this.server.topology.client.options.extendedMetadata + extendedMetadata: this.server.topology.client.options?.extendedMetadata }; this.pending++; diff --git a/test/spec/connection-monitoring-and-pooling/cmap-format/pool-create-min-size-error.json b/test/spec/connection-monitoring-and-pooling/cmap-format/pool-create-min-size-error.json index 509b2a2356..ef513b450b 100644 --- a/test/spec/connection-monitoring-and-pooling/cmap-format/pool-create-min-size-error.json +++ b/test/spec/connection-monitoring-and-pooling/cmap-format/pool-create-min-size-error.json @@ -14,7 +14,7 @@ }, "data": { "failCommands": [ - "isMaster", + "ismaster", "hello" ], "closeConnection": true, diff --git a/test/tools/cmap_spec_runner.ts b/test/tools/cmap_spec_runner.ts index ee00f1a0c5..fffde6dbf6 100644 --- a/test/tools/cmap_spec_runner.ts +++ b/test/tools/cmap_spec_runner.ts @@ -4,13 +4,11 @@ import { clearTimeout, setTimeout } from 'timers'; import { inspect } from 'util'; import { - addContainerMetadata, CMAP_EVENTS, type Connection, ConnectionPool, type ConnectionPoolOptions, type HostAddress, - makeClientMetadata, type MongoClient, type Server, shuffle, @@ -324,7 +322,9 @@ export class ThreadContext { hostAddress: this.#hostAddress, serverApi: process.env.MONGODB_API_VERSION ? { version: process.env.MONGODB_API_VERSION } - : undefined + : undefined, + + extendedMetadata: this.#server.topology.client.options?.extendedMetadata }); this.#originalServerPool = this.#server.pool; this.#server.pool = this.pool; @@ -376,8 +376,8 @@ async function runCmapTest(test: CmapTest, threadContext: ThreadContext) { delete poolOptions.backgroundThreadIntervalMS; } - const metadata = makeClientMetadata({ appName: poolOptions.appName, driverInfo: {} }); - const extendedMetadata = addContainerMetadata(metadata); + //const metadata = makeClientMetadata({ appName: poolOptions.appName }); + //const extendedMetadata = addContainerMetadata(metadata); delete poolOptions.appName; const operations = test.operations; @@ -391,8 +391,6 @@ async function runCmapTest(test: CmapTest, threadContext: ThreadContext) { threadContext.createPool({ ...poolOptions, - metadata, - extendedMetadata, minPoolSizeCheckFrequencyMS }); // yield control back to the event loop so that the ConnectionPoolCreatedEvent @@ -513,10 +511,15 @@ export function runCmapTestSuite( const selectedHostUri = hosts[0]; hostAddress = serverDescriptionMap.get(selectedHostUri).hostAddress; + const clientOptions: { appName?: string } = {}; + if (test.poolOptions?.appName) { + clientOptions.appName = test.poolOptions.appName; + } client = this.configuration.newClient( `mongodb://${hostAddress}/${ this.configuration.isLoadBalanced ? '?loadBalanced=true' : '?directConnection=true' - }` + }`, + clientOptions ); await client.connect(); if (test.failPoint) { From d84af5c14846f7c9d40b4f7cf284a411728e3bfa Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Tue, 8 Jul 2025 10:16:42 -0400 Subject: [PATCH 07/13] chore: address comments --- src/cmap/connection_pool.ts | 2 +- src/cmap/handshake/client_metadata.ts | 30 ++++++++++--------- .../mongodb-handshake.prose.test.ts | 8 +++-- .../pool-create-min-size-error.json | 2 +- test/tools/cmap_spec_runner.ts | 2 -- 5 files changed, 24 insertions(+), 20 deletions(-) diff --git a/src/cmap/connection_pool.ts b/src/cmap/connection_pool.ts index 8d747a5174..946b568cd0 100644 --- a/src/cmap/connection_pool.ts +++ b/src/cmap/connection_pool.ts @@ -620,7 +620,7 @@ export class ConnectionPool extends TypedEventEmitter { cancellationToken: this.cancellationToken, mongoLogger: this.mongoLogger, authProviders: this.server.topology.client.s.authProviders, - extendedMetadata: this.server.topology.client.options?.extendedMetadata + extendedMetadata: this.server.topology.client.options.extendedMetadata }; this.pending++; diff --git a/src/cmap/handshake/client_metadata.ts b/src/cmap/handshake/client_metadata.ts index 626905ebc3..23dcaa4b0c 100644 --- a/src/cmap/handshake/client_metadata.ts +++ b/src/cmap/handshake/client_metadata.ts @@ -2,7 +2,7 @@ import * as os from 'os'; import * as process from 'process'; import { BSON, type Document, Int32 } from '../../bson'; -import { MongoInvalidArgumentError } from '../../error'; +import { MongoInvalidArgumentError, MongoRuntimeError } from '../../error'; import type { MongoOptions } from '../../mongo_client'; import { fileIsAccessible } from '../../utils'; @@ -122,15 +122,19 @@ export function makeClientMetadata(options: MakeClientMetadataOptions): ClientMe version: version.length > 0 ? `${NODE_DRIVER_VERSION}|${version}` : NODE_DRIVER_VERSION }; + if (options.additionalDriverInfo == null) { + throw new MongoRuntimeError( + 'Client options `additionalDriverInfo` must always default to an empty array' + ); + } + // This is where we handle additional driver info added after client construction. - if (options.additionalDriverInfo?.length > 0) { - for (const { name: n = '', version: v = '' } of options.additionalDriverInfo) { - if (n.length > 0) { - driverInfo.name = `${driverInfo.name}|${n}`; - } - if (v.length > 0) { - driverInfo.version = `${driverInfo.version}|${v}`; - } + for (const { name: n = '', version: v = '' } of options.additionalDriverInfo) { + if (n.length > 0) { + driverInfo.name = `${driverInfo.name}|${n}`; + } + if (v.length > 0) { + driverInfo.version = `${driverInfo.version}|${v}`; } } @@ -145,11 +149,9 @@ export function makeClientMetadata(options: MakeClientMetadataOptions): ClientMe runtimeInfo = `${runtimeInfo}|${platform}`; } - if (options.additionalDriverInfo?.length > 0) { - for (const { platform: p = '' } of options.additionalDriverInfo) { - if (p.length > 0) { - runtimeInfo = `${runtimeInfo}|${p}`; - } + for (const { platform: p = '' } of options.additionalDriverInfo) { + if (p.length > 0) { + runtimeInfo = `${runtimeInfo}|${p}`; } } diff --git a/test/integration/mongodb-handshake/mongodb-handshake.prose.test.ts b/test/integration/mongodb-handshake/mongodb-handshake.prose.test.ts index 7a0ecb0a86..b8f9072b9a 100644 --- a/test/integration/mongodb-handshake/mongodb-handshake.prose.test.ts +++ b/test/integration/mongodb-handshake/mongodb-handshake.prose.test.ts @@ -204,7 +204,7 @@ describe('Client Metadata Update Prose Tests', function () { sinon.restore(); }); - describe('Test 2: Multiple Successive Metadata Updates', function () { + describe('Test 1: Test that the driver updates metadata', function () { let initialClientMetadata; let updatedClientMetadata; @@ -279,13 +279,15 @@ describe('Client Metadata Update Prose Tests', function () { ? `${initialClientMetadata.platform}|${platform}` : initialClientMetadata.platform ); + // - All other subfields in the client document remain unchanged from initialClientMetadata. + // (Note os is the only one getting set in these tests) expect(updatedClientMetadata.os).to.deep.equal(initialClientMetadata.os); }); }); } }); - describe('Test 1: Test that the driver updates metadata', function () { + describe('Test 2: Multiple Successive Metadata Updates', function () { let initialClientMetadata; let updatedClientMetadata; @@ -355,6 +357,8 @@ describe('Client Metadata Update Prose Tests', function () { ? `${initialClientMetadata.platform}|${platform}` : initialClientMetadata.platform ); + // - All other subfields in the client document remain unchanged from initialClientMetadata. + // (Note os is the only one getting set in these tests) expect(updatedClientMetadata.os).to.deep.equal(initialClientMetadata.os); }); }); diff --git a/test/spec/connection-monitoring-and-pooling/cmap-format/pool-create-min-size-error.json b/test/spec/connection-monitoring-and-pooling/cmap-format/pool-create-min-size-error.json index ef513b450b..509b2a2356 100644 --- a/test/spec/connection-monitoring-and-pooling/cmap-format/pool-create-min-size-error.json +++ b/test/spec/connection-monitoring-and-pooling/cmap-format/pool-create-min-size-error.json @@ -14,7 +14,7 @@ }, "data": { "failCommands": [ - "ismaster", + "isMaster", "hello" ], "closeConnection": true, diff --git a/test/tools/cmap_spec_runner.ts b/test/tools/cmap_spec_runner.ts index fffde6dbf6..598958a214 100644 --- a/test/tools/cmap_spec_runner.ts +++ b/test/tools/cmap_spec_runner.ts @@ -376,8 +376,6 @@ async function runCmapTest(test: CmapTest, threadContext: ThreadContext) { delete poolOptions.backgroundThreadIntervalMS; } - //const metadata = makeClientMetadata({ appName: poolOptions.appName }); - //const extendedMetadata = addContainerMetadata(metadata); delete poolOptions.appName; const operations = test.operations; From ecb63b5bbe4c03cb49ba11c2670d4aa7b96ef387 Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Tue, 8 Jul 2025 10:31:51 -0400 Subject: [PATCH 08/13] test: fix unit tests --- .../cmap/handshake/client_metadata.test.ts | 97 ++++++++++++------- 1 file changed, 60 insertions(+), 37 deletions(-) diff --git a/test/unit/cmap/handshake/client_metadata.test.ts b/test/unit/cmap/handshake/client_metadata.test.ts index 9ef228c1b9..8829c9ef98 100644 --- a/test/unit/cmap/handshake/client_metadata.test.ts +++ b/test/unit/cmap/handshake/client_metadata.test.ts @@ -142,7 +142,7 @@ describe('client metadata module', () => { describe('makeClientMetadata()', () => { context('when no FAAS environment is detected', () => { it('does not append FAAS metadata', () => { - const metadata = makeClientMetadata({ driverInfo: {} }); + const metadata = makeClientMetadata({ driverInfo: {}, additionalDriverInfo: [] }); expect(metadata).not.to.have.property( 'env', 'faas metadata applied in a non-faas environment' @@ -165,15 +165,18 @@ describe('client metadata module', () => { context('when driverInfo.platform is provided', () => { it('throws an error if driverInfo.platform is too large', () => { - expect(() => makeClientMetadata({ driverInfo: { platform: 'a'.repeat(512) } })).to.throw( - MongoInvalidArgumentError, - /platform/ - ); + expect(() => + makeClientMetadata({ + driverInfo: { platform: 'a'.repeat(512) }, + additionalDriverInfo: [] + }) + ).to.throw(MongoInvalidArgumentError, /platform/); }); it('appends driverInfo.platform to the platform field', () => { const options = { - driverInfo: { platform: 'myPlatform' } + driverInfo: { platform: 'myPlatform' }, + additionalDriverInfo: [] }; const metadata = makeClientMetadata(options); expect(metadata).to.deep.equal({ @@ -194,15 +197,15 @@ describe('client metadata module', () => { context('when driverInfo.name is provided', () => { it('throws an error if driverInfo.name is too large', () => { - expect(() => makeClientMetadata({ driverInfo: { name: 'a'.repeat(512) } })).to.throw( - MongoInvalidArgumentError, - /name/ - ); + expect(() => + makeClientMetadata({ driverInfo: { name: 'a'.repeat(512) }, additionalDriverInfo: [] }) + ).to.throw(MongoInvalidArgumentError, /name/); }); it('appends driverInfo.name to the driver.name field', () => { const options = { - driverInfo: { name: 'myName' } + driverInfo: { name: 'myName' }, + additionalDriverInfo: [] }; const metadata = makeClientMetadata(options); expect(metadata).to.deep.equal({ @@ -223,15 +226,15 @@ describe('client metadata module', () => { context('when driverInfo.version is provided', () => { it('throws an error if driverInfo.version is too large', () => { - expect(() => makeClientMetadata({ driverInfo: { version: 'a'.repeat(512) } })).to.throw( - MongoInvalidArgumentError, - /version/ - ); + expect(() => + makeClientMetadata({ driverInfo: { version: 'a'.repeat(512) }, additionalDriverInfo: [] }) + ).to.throw(MongoInvalidArgumentError, /version/); }); it('appends driverInfo.version to the version field', () => { const options = { - driverInfo: { version: 'myVersion' } + driverInfo: { version: 'myVersion' }, + additionalDriverInfo: [] }; const metadata = makeClientMetadata(options); expect(metadata).to.deep.equal({ @@ -251,7 +254,7 @@ describe('client metadata module', () => { }); context('when no custom driverInto is provided', () => { - const metadata = makeClientMetadata({ driverInfo: {} }); + const metadata = makeClientMetadata({ driverInfo: {}, additionalDriverInfo: [] }); it('does not append the driver info to the metadata', () => { expect(metadata).to.deep.equal({ @@ -279,7 +282,8 @@ describe('client metadata module', () => { const longString = 'a'.repeat(300); const options = { appName: longString, - driverInfo: {} + driverInfo: {}, + additionalDriverInfo: [] }; const metadata = makeClientMetadata(options); @@ -298,7 +302,8 @@ describe('client metadata module', () => { const longString = '€'.repeat(300); const options = { appName: longString, - driverInfo: {} + driverInfo: {}, + additionalDriverInfo: [] }; const metadata = makeClientMetadata(options); @@ -315,7 +320,8 @@ describe('client metadata module', () => { context('when the app name is under 128 bytes', () => { const options = { appName: 'myApplication', - driverInfo: {} + driverInfo: {}, + additionalDriverInfo: [] }; const metadata = makeClientMetadata(options); @@ -333,37 +339,40 @@ describe('client metadata module', () => { it('sets platform to Deno', () => { globalThis.Deno = { version: { deno: '1.2.3' } }; - const metadata = makeClientMetadata({ driverInfo: {} }); + const metadata = makeClientMetadata({ driverInfo: {}, additionalDriverInfo: [] }); expect(metadata.platform).to.equal('Deno v1.2.3, LE'); }); it('sets platform to Deno with driverInfo.platform', () => { globalThis.Deno = { version: { deno: '1.2.3' } }; - const metadata = makeClientMetadata({ driverInfo: { platform: 'myPlatform' } }); + const metadata = makeClientMetadata({ + driverInfo: { platform: 'myPlatform' }, + additionalDriverInfo: [] + }); expect(metadata.platform).to.equal('Deno v1.2.3, LE|myPlatform'); }); it('ignores version if Deno.version.deno is not a string', () => { globalThis.Deno = { version: { deno: 1 } }; - const metadata = makeClientMetadata({ driverInfo: {} }); + const metadata = makeClientMetadata({ driverInfo: {}, additionalDriverInfo: [] }); expect(metadata.platform).to.equal('Deno v0.0.0-unknown, LE'); }); it('ignores version if Deno.version does not have a deno property', () => { globalThis.Deno = { version: { somethingElse: '1.2.3' } }; - const metadata = makeClientMetadata({ driverInfo: {} }); + const metadata = makeClientMetadata({ driverInfo: {}, additionalDriverInfo: [] }); expect(metadata.platform).to.equal('Deno v0.0.0-unknown, LE'); }); it('ignores version if Deno.version is null', () => { globalThis.Deno = { version: null }; - const metadata = makeClientMetadata({ driverInfo: {} }); + const metadata = makeClientMetadata({ driverInfo: {}, additionalDriverInfo: [] }); expect(metadata.platform).to.equal('Deno v0.0.0-unknown, LE'); }); it('ignores version if Deno is nullish', () => { globalThis.Deno = null; - const metadata = makeClientMetadata({ driverInfo: {} }); + const metadata = makeClientMetadata({ driverInfo: {}, additionalDriverInfo: [] }); expect(metadata.platform).to.equal('Deno v0.0.0-unknown, LE'); }); }); @@ -377,7 +386,7 @@ describe('client metadata module', () => { globalThis.Bun = class { static version = '1.2.3'; }; - const metadata = makeClientMetadata({ driverInfo: {} }); + const metadata = makeClientMetadata({ driverInfo: {}, additionalDriverInfo: [] }); expect(metadata.platform).to.equal('Bun v1.2.3, LE'); }); @@ -385,7 +394,10 @@ describe('client metadata module', () => { globalThis.Bun = class { static version = '1.2.3'; }; - const metadata = makeClientMetadata({ driverInfo: { platform: 'myPlatform' } }); + const metadata = makeClientMetadata({ + driverInfo: { platform: 'myPlatform' }, + additionalDriverInfo: [] + }); expect(metadata.platform).to.equal('Bun v1.2.3, LE|myPlatform'); }); @@ -393,7 +405,7 @@ describe('client metadata module', () => { globalThis.Bun = class { static version = 1; }; - const metadata = makeClientMetadata({ driverInfo: {} }); + const metadata = makeClientMetadata({ driverInfo: {}, additionalDriverInfo: [] }); expect(metadata.platform).to.equal('Bun v0.0.0-unknown, LE'); }); @@ -401,13 +413,19 @@ describe('client metadata module', () => { globalThis.Bun = class { static version = 1; }; - const metadata = makeClientMetadata({ driverInfo: { platform: 'myPlatform' } }); + const metadata = makeClientMetadata({ + driverInfo: { platform: 'myPlatform' }, + additionalDriverInfo: [] + }); expect(metadata.platform).to.equal('Bun v0.0.0-unknown, LE|myPlatform'); }); it('ignores version if Bun is nullish', () => { globalThis.Bun = null; - const metadata = makeClientMetadata({ driverInfo: { platform: 'myPlatform' } }); + const metadata = makeClientMetadata({ + driverInfo: { platform: 'myPlatform' }, + additionalDriverInfo: [] + }); expect(metadata.platform).to.equal('Bun v0.0.0-unknown, LE|myPlatform'); }); }); @@ -528,7 +546,7 @@ describe('client metadata module', () => { }); it(`returns ${inspect(outcome)} under env property`, () => { - const { env } = makeClientMetadata({ driverInfo: {} }); + const { env } = makeClientMetadata({ driverInfo: {}, additionalDriverInfo: [] }); expect(env).to.deep.equal(outcome); }); @@ -552,7 +570,9 @@ describe('client metadata module', () => { }); it('does not attach it to the metadata', () => { - expect(makeClientMetadata({ driverInfo: {} })).not.to.have.nested.property('aws.memory_mb'); + expect( + makeClientMetadata({ driverInfo: {}, additionalDriverInfo: [] }) + ).not.to.have.nested.property('aws.memory_mb'); }); }); }); @@ -567,7 +587,7 @@ describe('client metadata module', () => { }); it('only includes env.name', () => { - const metadata = makeClientMetadata({ driverInfo: {} }); + const metadata = makeClientMetadata({ driverInfo: {}, additionalDriverInfo: [] }); expect(metadata).to.not.have.nested.property('env.region'); expect(metadata).to.have.nested.property('env.name', 'aws.lambda'); expect(metadata.env).to.have.all.keys('name'); @@ -585,7 +605,7 @@ describe('client metadata module', () => { }); it('only includes env.name', () => { - const metadata = makeClientMetadata({ driverInfo: {} }); + const metadata = makeClientMetadata({ driverInfo: {}, additionalDriverInfo: [] }); expect(metadata).to.have.property('env'); expect(metadata).to.have.nested.property('env.region', 'abc'); expect(metadata.os).to.have.all.keys('type'); @@ -602,7 +622,7 @@ describe('client metadata module', () => { }); it('omits os information', () => { - const metadata = makeClientMetadata({ driverInfo: {} }); + const metadata = makeClientMetadata({ driverInfo: {}, additionalDriverInfo: [] }); expect(metadata).to.not.have.property('os'); }); }); @@ -618,7 +638,10 @@ describe('client metadata module', () => { }); it('omits the faas env', () => { - const metadata = makeClientMetadata({ driverInfo: { name: 'a'.repeat(350) } }); + const metadata = makeClientMetadata({ + driverInfo: { name: 'a'.repeat(350) }, + additionalDriverInfo: [] + }); expect(metadata).to.not.have.property('env'); }); }); From 3c516ff582312710c7538519e1ca5e7d9f9babd0 Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Tue, 8 Jul 2025 10:57:55 -0400 Subject: [PATCH 09/13] test: fix topology test --- test/unit/sdam/topology.test.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/unit/sdam/topology.test.ts b/test/unit/sdam/topology.test.ts index 8b7be4b4f0..1c099ce1ba 100644 --- a/test/unit/sdam/topology.test.ts +++ b/test/unit/sdam/topology.test.ts @@ -53,7 +53,8 @@ describe('Topology (unit)', function () { const server: Topology = topologyWithPlaceholderClient([`localhost:27017`], { metadata: makeClientMetadata({ appName: 'My application name', - driverInfo: {} + driverInfo: {}, + additionalDriverInfo: [] }) }); From df244d344cbf842f5e605f8f6bd8c4bf0ab070b4 Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Tue, 8 Jul 2025 11:27:06 -0400 Subject: [PATCH 10/13] test: fix integration tests --- .../connection.test.ts | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/test/integration/connection-monitoring-and-pooling/connection.test.ts b/test/integration/connection-monitoring-and-pooling/connection.test.ts index cd1922dd1f..7dfbe2be53 100644 --- a/test/integration/connection-monitoring-and-pooling/connection.test.ts +++ b/test/integration/connection-monitoring-and-pooling/connection.test.ts @@ -49,8 +49,10 @@ describe('Connection', function () { ...commonConnectOptions, connectionType: Connection, ...this.configuration.options, - metadata: makeClientMetadata({ driverInfo: {} }), - extendedMetadata: addContainerMetadata(makeClientMetadata({ driverInfo: {} })) + metadata: makeClientMetadata({ driverInfo: {}, additionalDriverInfo: [] }), + extendedMetadata: addContainerMetadata( + makeClientMetadata({ driverInfo: {}, additionalDriverInfo: [] }) + ) }; let conn; @@ -72,8 +74,10 @@ describe('Connection', function () { connectionType: Connection, ...this.configuration.options, monitorCommands: true, - metadata: makeClientMetadata({ driverInfo: {} }), - extendedMetadata: addContainerMetadata(makeClientMetadata({ driverInfo: {} })) + metadata: makeClientMetadata({ driverInfo: {}, additionalDriverInfo: [] }), + extendedMetadata: addContainerMetadata( + makeClientMetadata({ driverInfo: {}, additionalDriverInfo: [] }) + ) }; let conn; @@ -104,8 +108,10 @@ describe('Connection', function () { connectionType: Connection, ...this.configuration.options, monitorCommands: true, - metadata: makeClientMetadata({ driverInfo: {} }), - extendedMetadata: addContainerMetadata(makeClientMetadata({ driverInfo: {} })) + metadata: makeClientMetadata({ driverInfo: {}, additionalDriverInfo: [] }), + extendedMetadata: addContainerMetadata( + makeClientMetadata({ driverInfo: {}, additionalDriverInfo: [] }) + ) }; let conn; From 34332d8ebd18ea89ab80200a85ba444a2ef001c4 Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Tue, 8 Jul 2025 20:00:38 -0400 Subject: [PATCH 11/13] chore: comments --- .../mongodb-handshake.prose.test.ts | 40 +++++-------------- test/tools/cmap_spec_runner.ts | 3 +- 2 files changed, 11 insertions(+), 32 deletions(-) diff --git a/test/integration/mongodb-handshake/mongodb-handshake.prose.test.ts b/test/integration/mongodb-handshake/mongodb-handshake.prose.test.ts index b8f9072b9a..e64a5e8012 100644 --- a/test/integration/mongodb-handshake/mongodb-handshake.prose.test.ts +++ b/test/integration/mongodb-handshake/mongodb-handshake.prose.test.ts @@ -264,21 +264,11 @@ describe('Client Metadata Update Prose Tests', function () { client.appendMetadata({ name, version, platform }); await client.db('test').command({ ping: 1 }); - expect(updatedClientMetadata.driver.name).to.equal( - name - ? `${initialClientMetadata.driver.name}|${name}` - : initialClientMetadata.driver.name - ); - expect(updatedClientMetadata.driver.version).to.equal( - version - ? `${initialClientMetadata.driver.version}|${version}` - : initialClientMetadata.driver.version - ); - expect(updatedClientMetadata.platform).to.equal( - platform - ? `${initialClientMetadata.platform}|${platform}` - : initialClientMetadata.platform - ); + // Since we have our own driver metadata getting added, we really want to just + // assert that the last driver info values are appended at the end. + expect(updatedClientMetadata.driver.name.endsWith('library')).to.be.true; + expect(updatedClientMetadata.driver.version.endsWith('1.2')).to.be.true; + expect(updatedClientMetadata.platform.endsWith('Library Platform')).to.be.true; // - All other subfields in the client document remain unchanged from initialClientMetadata. // (Note os is the only one getting set in these tests) expect(updatedClientMetadata.os).to.deep.equal(initialClientMetadata.os); @@ -342,21 +332,11 @@ describe('Client Metadata Update Prose Tests', function () { client.appendMetadata({ name, version, platform }); await client.db('test').command({ ping: 1 }); - expect(updatedClientMetadata.driver.name).to.equal( - name - ? `${initialClientMetadata.driver.name}|${name}` - : initialClientMetadata.driver.name - ); - expect(updatedClientMetadata.driver.version).to.equal( - version - ? `${initialClientMetadata.driver.version}|${version}` - : initialClientMetadata.driver.version - ); - expect(updatedClientMetadata.platform).to.equal( - platform - ? `${initialClientMetadata.platform}|${platform}` - : initialClientMetadata.platform - ); + // Since we have our own driver metadata getting added, we really want to just + // assert that the last driver info values are appended at the end. + expect(updatedClientMetadata.driver.name.endsWith('library')).to.be.true; + expect(updatedClientMetadata.driver.version.endsWith('1.2')).to.be.true; + expect(updatedClientMetadata.platform.endsWith('Library Platform')).to.be.true; // - All other subfields in the client document remain unchanged from initialClientMetadata. // (Note os is the only one getting set in these tests) expect(updatedClientMetadata.os).to.deep.equal(initialClientMetadata.os); diff --git a/test/tools/cmap_spec_runner.ts b/test/tools/cmap_spec_runner.ts index 598958a214..97d9255e8b 100644 --- a/test/tools/cmap_spec_runner.ts +++ b/test/tools/cmap_spec_runner.ts @@ -323,8 +323,7 @@ export class ThreadContext { serverApi: process.env.MONGODB_API_VERSION ? { version: process.env.MONGODB_API_VERSION } : undefined, - - extendedMetadata: this.#server.topology.client.options?.extendedMetadata + extendedMetadata: this.#server.topology.client.options.extendedMetadata }); this.#originalServerPool = this.#server.pool; this.#server.pool = this.pool; From 694d64b3cb69da320c66c995f482d31462e94354 Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Wed, 9 Jul 2025 10:14:42 -0400 Subject: [PATCH 12/13] test: fix tests --- .../mongodb-handshake.prose.test.ts | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/test/integration/mongodb-handshake/mongodb-handshake.prose.test.ts b/test/integration/mongodb-handshake/mongodb-handshake.prose.test.ts index e64a5e8012..dda3599934 100644 --- a/test/integration/mongodb-handshake/mongodb-handshake.prose.test.ts +++ b/test/integration/mongodb-handshake/mongodb-handshake.prose.test.ts @@ -266,9 +266,11 @@ describe('Client Metadata Update Prose Tests', function () { // Since we have our own driver metadata getting added, we really want to just // assert that the last driver info values are appended at the end. - expect(updatedClientMetadata.driver.name.endsWith('library')).to.be.true; - expect(updatedClientMetadata.driver.version.endsWith('1.2')).to.be.true; - expect(updatedClientMetadata.platform.endsWith('Library Platform')).to.be.true; + expect(updatedClientMetadata.driver.name.endsWith('framework')).to.be.true; + expect(updatedClientMetadata.driver.version.endsWith(version ? version : '1.2')).to.be + .true; + expect(updatedClientMetadata.platform.endsWith(platform ? platform : 'Library Platform')) + .to.be.true; // - All other subfields in the client document remain unchanged from initialClientMetadata. // (Note os is the only one getting set in these tests) expect(updatedClientMetadata.os).to.deep.equal(initialClientMetadata.os); @@ -334,9 +336,11 @@ describe('Client Metadata Update Prose Tests', function () { // Since we have our own driver metadata getting added, we really want to just // assert that the last driver info values are appended at the end. - expect(updatedClientMetadata.driver.name.endsWith('library')).to.be.true; - expect(updatedClientMetadata.driver.version.endsWith('1.2')).to.be.true; - expect(updatedClientMetadata.platform.endsWith('Library Platform')).to.be.true; + expect(updatedClientMetadata.driver.name.endsWith('framework')).to.be.true; + expect(updatedClientMetadata.driver.version.endsWith(version ? version : '1.2')).to.be + .true; + expect(updatedClientMetadata.platform.endsWith(platform ? platform : 'Library Platform')) + .to.be.true; // - All other subfields in the client document remain unchanged from initialClientMetadata. // (Note os is the only one getting set in these tests) expect(updatedClientMetadata.os).to.deep.equal(initialClientMetadata.os); From 7cf1864d16e27fb3a7ca16c11054700268187849 Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Thu, 10 Jul 2025 10:41:04 -0400 Subject: [PATCH 13/13] test: update assertions --- .../mongodb-handshake.prose.test.ts | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/test/integration/mongodb-handshake/mongodb-handshake.prose.test.ts b/test/integration/mongodb-handshake/mongodb-handshake.prose.test.ts index dda3599934..1cdd9e6d8f 100644 --- a/test/integration/mongodb-handshake/mongodb-handshake.prose.test.ts +++ b/test/integration/mongodb-handshake/mongodb-handshake.prose.test.ts @@ -266,11 +266,13 @@ describe('Client Metadata Update Prose Tests', function () { // Since we have our own driver metadata getting added, we really want to just // assert that the last driver info values are appended at the end. - expect(updatedClientMetadata.driver.name.endsWith('framework')).to.be.true; - expect(updatedClientMetadata.driver.version.endsWith(version ? version : '1.2')).to.be - .true; - expect(updatedClientMetadata.platform.endsWith(platform ? platform : 'Library Platform')) - .to.be.true; + expect(updatedClientMetadata.driver.name).to.match(/^.*\|framework$/); + expect(updatedClientMetadata.driver.version).to.match( + new RegExp(`^.*\\|${version ? version : '1.2'}$`) + ); + expect(updatedClientMetadata.platform).to.match( + new RegExp(`^.*\\|${platform ? platform : 'Library Platform'}$`) + ); // - All other subfields in the client document remain unchanged from initialClientMetadata. // (Note os is the only one getting set in these tests) expect(updatedClientMetadata.os).to.deep.equal(initialClientMetadata.os); @@ -336,11 +338,13 @@ describe('Client Metadata Update Prose Tests', function () { // Since we have our own driver metadata getting added, we really want to just // assert that the last driver info values are appended at the end. - expect(updatedClientMetadata.driver.name.endsWith('framework')).to.be.true; - expect(updatedClientMetadata.driver.version.endsWith(version ? version : '1.2')).to.be - .true; - expect(updatedClientMetadata.platform.endsWith(platform ? platform : 'Library Platform')) - .to.be.true; + expect(updatedClientMetadata.driver.name).to.match(/^.*\|framework$/); + expect(updatedClientMetadata.driver.version).to.match( + new RegExp(`^.*\\|${version ? version : '1.2'}$`) + ); + expect(updatedClientMetadata.platform).to.match( + new RegExp(`^.*\\|${platform ? platform : 'Library Platform'}$`) + ); // - All other subfields in the client document remain unchanged from initialClientMetadata. // (Note os is the only one getting set in these tests) expect(updatedClientMetadata.os).to.deep.equal(initialClientMetadata.os);