Skip to content

Commit bd042de

Browse files
comments
1 parent d3e4740 commit bd042de

File tree

2 files changed

+226
-9
lines changed

2 files changed

+226
-9
lines changed

src/cmap/handshake/client_metadata.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,18 @@ const NODE_DRIVER_VERSION = require('../../../package.json').version;
1111

1212
/** @internal */
1313
export function isDriverInfoEqual(info1: DriverInfo, info2: DriverInfo): boolean {
14+
/** for equality comparison, we consider "" as unset */
15+
const nonEmptyCmp = (s1: string | undefined, s2: string | undefined): boolean => {
16+
s1 ||= undefined;
17+
s2 ||= undefined;
18+
19+
return s1 === s2;
20+
};
21+
1422
return (
15-
info1.name === info2.name &&
16-
info1.platform === info2.platform &&
17-
info1.version === info2.version
23+
nonEmptyCmp(info1.name, info2.name) &&
24+
nonEmptyCmp(info1.platform, info2.platform) &&
25+
nonEmptyCmp(info1.version, info2.version)
1826
);
1927
}
2028

test/integration/mongodb-handshake/mongodb-handshake.prose.test.ts

Lines changed: 215 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { expect } from 'chai';
22
import * as sinon from 'sinon';
33

4-
import { type ClientMetadata } from '../../../mongodb';
4+
import { type ClientMetadata, type DriverInfo } from '../../../mongodb';
55
import { MongoClient as RawMongoClient } from '../../../src';
66
import {
77
Connection,
@@ -394,8 +394,7 @@ describe('Client Metadata Update Prose Tests', function () {
394394
{ testCase: 4, name: 'library', version: '1.2', platform: 'Framework Platform' },
395395
{ testCase: 5, name: 'framework', version: '2.0', platform: 'Library Platform' },
396396
{ testCase: 6, name: 'framework', version: '1.2', platform: 'Framework Platform' },
397-
{ testCase: 7, name: 'library', version: '2.0', platform: 'Framework Platform' },
398-
{ testCase: 8, name: 'framework', version: '2.0', platform: 'Framework Platform' }
397+
{ testCase: 7, name: 'library', version: '2.0', platform: 'Framework Platform' }
399398
];
400399

401400
for (const { testCase, ...driverInfo } of tests) {
@@ -504,8 +503,10 @@ describe('Client Metadata Update Prose Tests', function () {
504503
// 1. Create a `MongoClient` instance with:
505504
// - `maxIdleTimeMS` set to `1ms`
506505

507-
client = new RawMongoClient(this.configuration.url(), { maxIdleTimeMS: 1 });
508-
506+
client = new RawMongoClient(this.configuration.url(), {
507+
maxIdleTimeMS: 1,
508+
serverApi: this.configuration.serverApi
509+
});
509510
// 2. Append the following `DriverInfoOptions` to the `MongoClient` metadata:
510511
// | Field | Value |
511512
// | -------- | ---------------- |
@@ -685,7 +686,8 @@ describe('Client Metadata Update Prose Tests', function () {
685686
name: 'library',
686687
version: '1.2',
687688
platform: 'Library Platform'
688-
}
689+
},
690+
serverApi: this.configuration.serverApi
689691
});
690692

691693
// 2. Send a `ping` command to the server and verify that the command succeeds.
@@ -749,4 +751,211 @@ describe('Client Metadata Update Prose Tests', function () {
749751
expect(updatedClientMetadata).to.deep.equal(initialClientMetadata);
750752
});
751753
});
754+
755+
describe('Test 7: Empty strings are considered unset when appending duplicate metadata', function () {
756+
let initialClientMetadata: ClientMetadata;
757+
let updatedClientMetadata: ClientMetadata;
758+
// TODO(NODE-6599): mongodb-legacy adds additional client metadata, breaking these prose tests
759+
let client: RawMongoClient;
760+
761+
afterEach(async function () {
762+
await client.close();
763+
initialClientMetadata = undefined;
764+
updatedClientMetadata = undefined;
765+
});
766+
767+
const driverInfos: Array<{
768+
initial: DriverInfo;
769+
appended: DriverInfo;
770+
}> = [
771+
{
772+
initial: {
773+
name: undefined,
774+
version: '1.2',
775+
platform: 'Library Platform'
776+
},
777+
appended: {
778+
name: '',
779+
version: '1.2',
780+
platform: 'Library Platform'
781+
}
782+
},
783+
{
784+
initial: {
785+
name: 'library',
786+
version: undefined,
787+
platform: 'Library Platform'
788+
},
789+
appended: {
790+
name: 'library',
791+
version: '',
792+
platform: 'Library Platform'
793+
}
794+
},
795+
{
796+
initial: {
797+
name: 'library',
798+
version: '1.2',
799+
platform: undefined
800+
},
801+
appended: {
802+
name: 'library',
803+
version: '1.2',
804+
platform: ''
805+
}
806+
}
807+
];
808+
809+
for (const [metadata, index] of driverInfos.map((infos, i) => [infos, i] as const)) {
810+
describe(`Test ${index + 1}`, function () {
811+
it('does not appended duplicate metadata', async function () {
812+
// 1. Create a `MongoClient` instance with:
813+
// - `maxIdleTimeMS` set to `1ms`
814+
client = new RawMongoClient(this.configuration.url(), {
815+
maxIdleTimeMS: 1,
816+
serverApi: this.configuration.serverApi
817+
});
818+
819+
// 2. Append the `DriverInfoOptions` from the selected test case from the initial metadata section.
820+
client.appendMetadata(metadata.initial);
821+
822+
// 3. Send a `ping` command to the server and verify that the command succeeds.
823+
// 4. Save intercepted `client` document as `initialClientMetadata`.
824+
// 8. Store the response as `updatedClientMetadata`.
825+
sinon
826+
.stub(Connection.prototype, 'command')
827+
.callsFake(async function (ns, cmd, options, responseType) {
828+
// @ts-expect-error: sinon will place wrappedMethod on the command method.
829+
const command = Connection.prototype.command.wrappedMethod.bind(this);
830+
831+
if (cmd.hello || cmd[LEGACY_HELLO_COMMAND]) {
832+
if (!initialClientMetadata) {
833+
initialClientMetadata = cmd.client;
834+
} else {
835+
updatedClientMetadata = cmd.client;
836+
}
837+
}
838+
return command(ns, cmd, options, responseType);
839+
});
840+
841+
await client.db('test').command({ ping: 1 });
842+
843+
// 5. Wait 5ms for the connection to become idle.
844+
await sleep(5);
845+
846+
// 6. Append the `DriverInfoOptions` from the selected test case from the appended metadata section.
847+
client.appendMetadata(metadata.appended);
848+
849+
// 7. Send a `ping` command to the server and verify the command succeeds.
850+
await client.db('test').command({ ping: 1 });
851+
852+
// 9. Assert that `initialClientMetadata` is identical to `updatedClientMetadata`.
853+
expect(updatedClientMetadata).to.deep.equal(initialClientMetadata);
854+
});
855+
});
856+
}
857+
});
858+
859+
describe('Test 8: Empty strings are considered unset when appending metadata identical to initial metadata', function () {
860+
let initialClientMetadata: ClientMetadata;
861+
let updatedClientMetadata: ClientMetadata;
862+
// TODO(NODE-6599): mongodb-legacy adds additional client metadata, breaking these prose tests
863+
let client: RawMongoClient;
864+
865+
afterEach(async function () {
866+
await client.close();
867+
initialClientMetadata = undefined;
868+
updatedClientMetadata = undefined;
869+
});
870+
871+
const driverInfos: Array<{
872+
initial: DriverInfo;
873+
appended: DriverInfo;
874+
}> = [
875+
{
876+
initial: {
877+
name: undefined,
878+
version: '1.2',
879+
platform: 'Library Platform'
880+
},
881+
appended: {
882+
name: '',
883+
version: '1.2',
884+
platform: 'Library Platform'
885+
}
886+
},
887+
{
888+
initial: {
889+
name: 'library',
890+
version: undefined,
891+
platform: 'Library Platform'
892+
},
893+
appended: {
894+
name: 'library',
895+
version: '',
896+
platform: 'Library Platform'
897+
}
898+
},
899+
{
900+
initial: {
901+
name: 'library',
902+
version: '1.2',
903+
platform: undefined
904+
},
905+
appended: {
906+
name: 'library',
907+
version: '1.2',
908+
platform: ''
909+
}
910+
}
911+
];
912+
913+
for (const [metadata, index] of driverInfos.map((infos, i) => [infos, i] as const)) {
914+
describe(`Test ${index + 1}`, function () {
915+
it('does not appended duplicate metadata', async function () {
916+
// 1. Create a `MongoClient` instance with:
917+
// - `maxIdleTimeMS` set to `1ms`
918+
// - `driverInfo` set to the `DriverInfoOptions` from the selected test case from the initial metadata section.
919+
client = new RawMongoClient(this.configuration.url(), {
920+
maxIdleTimeMS: 1,
921+
serverApi: this.configuration.serverApi,
922+
driverInfo: metadata.initial
923+
});
924+
925+
// 2. Send a `ping` command to the server and verify that the command succeeds.
926+
// 3. Save intercepted `client` document as `initialClientMetadata`.
927+
// 7. Store the response as `updatedClientMetadata`.
928+
sinon
929+
.stub(Connection.prototype, 'command')
930+
.callsFake(async function (ns, cmd, options, responseType) {
931+
// @ts-expect-error: sinon will place wrappedMethod on the command method.
932+
const command = Connection.prototype.command.wrappedMethod.bind(this);
933+
934+
if (cmd.hello || cmd[LEGACY_HELLO_COMMAND]) {
935+
if (!initialClientMetadata) {
936+
initialClientMetadata = cmd.client;
937+
} else {
938+
updatedClientMetadata = cmd.client;
939+
}
940+
}
941+
return command(ns, cmd, options, responseType);
942+
});
943+
944+
await client.db('test').command({ ping: 1 });
945+
946+
// 4. Wait 5ms for the connection to become idle.
947+
await sleep(5);
948+
949+
// 5. Append the `DriverInfoOptions` from the selected test case from the appended metadata section.
950+
client.appendMetadata(metadata.appended);
951+
952+
// 6. Send a `ping` command to the server and verify the command succeeds.
953+
await client.db('test').command({ ping: 1 });
954+
955+
// 8. Assert that `initialClientMetadata` is identical to `updatedClientMetadata`.
956+
expect(updatedClientMetadata).to.deep.equal(initialClientMetadata);
957+
});
958+
});
959+
}
960+
});
752961
});

0 commit comments

Comments
 (0)