diff --git a/src/cmap/connection_pool.ts b/src/cmap/connection_pool.ts index 976e280aae3..946b568cd0a 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 d8dbe7c7ad9..23dcaa4b0c3 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'; @@ -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,22 @@ 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. + 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}`; + } + } + if (!metadataDocument.ifItFitsItSits('driver', driverInfo)) { throw new MongoInvalidArgumentError( 'Unable to include driverInfo name and version, metadata cannot exceed 512 bytes' @@ -130,6 +149,12 @@ export function makeClientMetadata(options: MakeClientMetadataOptions): ClientMe runtimeInfo = `${runtimeInfo}|${platform}`; } + for (const { platform: p = '' } of options.additionalDriverInfo) { + if (p.length > 0) { + runtimeInfo = `${runtimeInfo}|${p}`; + } + } + if (!metadataDocument.ifItFitsItSits('platform', runtimeInfo)) { throw new MongoInvalidArgumentError( 'Unable to include driverInfo platform, metadata cannot exceed 512 bytes' diff --git a/src/connection_string.ts b/src/connection_string.ts index 89bcaf8bc94..edb1a6870a1 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 77b6559049b..74163da4611 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 } 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,31 @@ 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' + | 'driverInfo' + | 'additionalDriverInfo' + | 'metadata' + | 'extendedMetadata' + > > & - Pick; + Pick< + MongoOptions, + | 'monitorCommands' + | 'ca' + | 'crl' + | 'key' + | 'cert' + | 'driverInfo' + | 'additionalDriverInfo' + | 'metadata' + | 'extendedMetadata' + >; constructor(url: string, options?: MongoClientOptions) { super(); @@ -459,6 +485,18 @@ export class MongoClient extends TypedEventEmitter implements await this.close(); } + /** + * Append metadata to the client metadata after instantiation. + * @param driverInfo - Information about the application or library. + */ + appendMetadata(driverInfo: DriverInfo) { + this.options.additionalDriverInfo.push(driverInfo); + this.options.metadata = makeClientMetadata(this.options); + this.options.extendedMetadata = addContainerMetadata(this.options.metadata) + .then(undefined, squashError) + .then(result => result ?? {}); // ensure Promise + } + /** @internal */ private checkForNonGenuineHosts() { const documentDBHostnames = this.options.hosts.filter((hostAddress: HostAddress) => @@ -1041,8 +1079,8 @@ export interface MongoOptions dbName: string; /** @deprecated - Will be made internal in a future major release. */ metadata: ClientMetadata; - /** @internal */ extendedMetadata: Promise; + additionalDriverInfo: DriverInfo[]; /** @internal */ autoEncrypter?: AutoEncrypter; /** @internal */ diff --git a/test/integration/connection-monitoring-and-pooling/connection.test.ts b/test/integration/connection-monitoring-and-pooling/connection.test.ts index cd1922dd1fa..7dfbe2be535 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; diff --git a/test/integration/mongodb-handshake/mongodb-handshake.prose.test.ts b/test/integration/mongodb-handshake/mongodb-handshake.prose.test.ts index d2180bd6380..1cdd9e6d8fb 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]>; @@ -194,3 +195,161 @@ describe('Handshake Prose Tests', function () { }); }); }); + +describe('Client Metadata Update Prose Tests', function () { + let client: MongoClient; + + 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, + 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 }); + + // 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).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); + }); + }); + } + }); + + describe('Test 2: Multiple Successive Metadata Updates', 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 }); + + // 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).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); + }); + }); + } + }); +}); 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 00000000000..7db7ed99eac --- /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 00000000000..500b579b89a --- /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 00000000000..4ff911a7394 --- /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/cmap_spec_runner.ts b/test/tools/cmap_spec_runner.ts index ee00f1a0c56..97d9255e8b2 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,8 @@ 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 +375,6 @@ async function runCmapTest(test: CmapTest, threadContext: ThreadContext) { delete poolOptions.backgroundThreadIntervalMS; } - const metadata = makeClientMetadata({ appName: poolOptions.appName, driverInfo: {} }); - const extendedMetadata = addContainerMetadata(metadata); delete poolOptions.appName; const operations = test.operations; @@ -391,8 +388,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 +508,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) { diff --git a/test/tools/unified-spec-runner/operations.ts b/test/tools/unified-spec-runner/operations.ts index 19c31c743d3..d208d49fc89 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, diff --git a/test/unit/cmap/handshake/client_metadata.test.ts b/test/unit/cmap/handshake/client_metadata.test.ts index 9ef228c1b97..8829c9ef981 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'); }); }); diff --git a/test/unit/sdam/topology.test.ts b/test/unit/sdam/topology.test.ts index 8b7be4b4f04..1c099ce1baf 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: [] }) });