|
1 | 1 | import { expect } from 'chai';
|
2 | 2 | import * as sinon from 'sinon';
|
3 | 3 |
|
| 4 | +import { type ClientMetadata } from '../../../mongodb'; |
| 5 | +import { MongoClient as RawMongoClient } from '../../../src'; |
4 | 6 | import {
|
5 | 7 | Connection,
|
6 | 8 | getFAASEnv,
|
7 | 9 | Int32,
|
| 10 | + isDriverInfoEqual, |
8 | 11 | LEGACY_HELLO_COMMAND,
|
9 | 12 | type MongoClient
|
10 | 13 | } from '../../mongodb';
|
@@ -365,4 +368,191 @@ describe('Client Metadata Update Prose Tests', function () {
|
365 | 368 | });
|
366 | 369 | }
|
367 | 370 | });
|
| 371 | + |
| 372 | + describe('Test 3: Multiple Successive Metadata Updates with Identical/Partially Identical `DriverInfo`', function () { |
| 373 | + const originalDriverInfo = { name: 'library', version: '1.2', platform: 'Library Platform' }; |
| 374 | + let initialClientMetadata: ClientMetadata; |
| 375 | + let updatedClientMetadata: ClientMetadata; |
| 376 | + let client: RawMongoClient; |
| 377 | + |
| 378 | + // | Case | Name | Version | Platform | |
| 379 | + // | ---- | --------- | ------- | ------------------ | |
| 380 | + // | 1 | library | 1.2 | Library Platform | |
| 381 | + // | 2 | framework | 1.2 | Library Platform | |
| 382 | + // | 3 | library | 2.0 | Library Platform | |
| 383 | + // | 4 | library | 1.2 | Framework Platform | |
| 384 | + // | 5 | framework | 2.0 | Library Platform | |
| 385 | + // | 6 | framework | 1.2 | Framework Platform | |
| 386 | + // | 7 | library | 2.0 | Framework Platform | |
| 387 | + // | 8 | framework | 2.0 | Framework Platform | |
| 388 | + const tests = [ |
| 389 | + { testCase: 1, name: 'library', version: '1.2', platform: 'Library Platform' }, |
| 390 | + { testCase: 2, name: 'framework', version: '1.2', platform: 'Library Platform' }, |
| 391 | + { testCase: 3, name: 'library', version: '2.0', platform: 'Library Platform' }, |
| 392 | + { testCase: 4, name: 'library', version: '1.2', platform: 'Framework Platform' }, |
| 393 | + { testCase: 5, name: 'framework', version: '2.0', platform: 'Library Platform' }, |
| 394 | + { testCase: 6, name: 'framework', version: '1.2', platform: 'Framework Platform' }, |
| 395 | + { testCase: 7, name: 'library', version: '2.0', platform: 'Framework Platform' }, |
| 396 | + { testCase: 8, name: 'framework', version: '2.0', platform: 'Framework Platform' } |
| 397 | + ]; |
| 398 | + |
| 399 | + for (const { testCase, ...driverInfo } of tests) { |
| 400 | + context(`Case ${testCase}: ${JSON.stringify(driverInfo)}`, function () { |
| 401 | + // 1. Create a `MongoClient` instance with: |
| 402 | + // - `maxIdleTimeMS` set to `1ms` |
| 403 | + // 2. Append the following `DriverInfoOptions` to the `MongoClient` metadata: |
| 404 | + // | Field | Value | |
| 405 | + // | -------- | ---------------- | |
| 406 | + // | name | library | |
| 407 | + // | version | 1.2 | |
| 408 | + // | platform | Library Platform | |
| 409 | + // 3. Send a `ping` command to the server and verify that the command succeeds. |
| 410 | + // 4. Save intercepted `client` document as `updatedClientMetadata`. |
| 411 | + // 5. Wait 5ms for the connection to become idle. |
| 412 | + beforeEach(async function () { |
| 413 | + client = new RawMongoClient(this.configuration.url(), { maxIdleTimeMS: 1 }); |
| 414 | + client.appendMetadata(originalDriverInfo); |
| 415 | + |
| 416 | + sinon |
| 417 | + .stub(Connection.prototype, 'command') |
| 418 | + .callsFake(async function (ns, cmd, options, responseType) { |
| 419 | + // @ts-expect-error: sinon will place wrappedMethod on the command method. |
| 420 | + const command = Connection.prototype.command.wrappedMethod.bind(this); |
| 421 | + |
| 422 | + if (cmd.hello || cmd[LEGACY_HELLO_COMMAND]) { |
| 423 | + if (!initialClientMetadata) { |
| 424 | + initialClientMetadata = cmd.client; |
| 425 | + } else { |
| 426 | + updatedClientMetadata = cmd.client; |
| 427 | + } |
| 428 | + } |
| 429 | + return command(ns, cmd, options, responseType); |
| 430 | + }); |
| 431 | + |
| 432 | + await client.db('test').command({ ping: 1 }); |
| 433 | + await sleep(5); |
| 434 | + }); |
| 435 | + |
| 436 | + afterEach(async function () { |
| 437 | + await client.close(); |
| 438 | + }); |
| 439 | + |
| 440 | + it('metadata is updated correctly, if necessary', async function () { |
| 441 | + // 1. Append the `DriverInfoOptions` from the selected test case to the `MongoClient` metadata. |
| 442 | + client.appendMetadata(driverInfo); |
| 443 | + |
| 444 | + // 2. Send a `ping` command to the server and verify: |
| 445 | + // - The command succeeds. |
| 446 | + await client.db('test').command({ ping: 1 }); |
| 447 | + |
| 448 | + // - The framework metadata is appended to the existing `DriverInfoOptions` in the `client.driver` fields of the `hello` |
| 449 | + // command, with values separated by a pipe `|`. To simplify assertions in these tests, strip out the default driver info |
| 450 | + // that is automatically added by the driver (ex: `metadata.name.split('|').slice(1).join('|')`). |
| 451 | + |
| 452 | + // - If the test case's DriverInfo is identical to the driver info from setup step 2 (test case 1): |
| 453 | + // - Assert metadata.name is equal to `library` |
| 454 | + // - Assert metadata.version is equal to `1.2` |
| 455 | + // - Assert metadata.platform is equal to `LibraryPlatform` |
| 456 | + // - Otherwise: |
| 457 | + // - Assert metadata.name is equal to `library|<name>` |
| 458 | + // - Assert metadata.version is equal to `1.2|<version>` |
| 459 | + // - Assert metadata.platform is equal to `LibraryPlatform|<platform>` |
| 460 | + const { driver, platform, ...updatedRest } = updatedClientMetadata; |
| 461 | + const { driver: _driver, platform: _platform, ...originalRest } = initialClientMetadata; |
| 462 | + |
| 463 | + const extractParts = (s: string) => s.split('|').slice(1).join('|'); |
| 464 | + |
| 465 | + const actual = { |
| 466 | + name: extractParts(driver.name), |
| 467 | + version: extractParts(driver.version), |
| 468 | + platform: extractParts(platform) |
| 469 | + }; |
| 470 | + |
| 471 | + const expected = isDriverInfoEqual(driverInfo, originalDriverInfo) |
| 472 | + ? originalDriverInfo |
| 473 | + : { |
| 474 | + name: `library|${driverInfo.name}`, |
| 475 | + platform: `Library Platform|${driverInfo.platform}`, |
| 476 | + version: `1.2|${driverInfo.version}` |
| 477 | + }; |
| 478 | + |
| 479 | + expect(actual).to.deep.equal(expected); |
| 480 | + |
| 481 | + // All other subfields in the `client` document remain unchanged from `updatedClientMetadata`. |
| 482 | + expect(updatedRest).to.deep.equal(originalRest); |
| 483 | + }); |
| 484 | + }); |
| 485 | + } |
| 486 | + }); |
| 487 | + |
| 488 | + describe('Test 4: Metadata is not appended if identical to initial metadata', function () { |
| 489 | + let initialClientMetadata; |
| 490 | + let updatedClientMetadata; |
| 491 | + |
| 492 | + // 1. Create a `MongoClient` instance with the following: |
| 493 | + // - `maxIdleTimeMS` set to `1ms` |
| 494 | + // - Wrapping library metadata: |
| 495 | + // | Field | Value | |
| 496 | + // | -------- | ---------------- | |
| 497 | + // | name | library | |
| 498 | + // | version | 1.2 | |
| 499 | + // | platform | Library Platform | |
| 500 | + // 2. Send a `ping` command to the server and verify that the command succeeds. |
| 501 | + // 3. Save intercepted `client` document as `initialClientMetadata`. |
| 502 | + // 4. Wait 5ms for the connection to become idle. |
| 503 | + beforeEach(async function () { |
| 504 | + // 1. Create a `MongoClient` instance with: |
| 505 | + // - `maxIdleTimeMS` set to `1ms` |
| 506 | + // - `driverInfo` set to the following: |
| 507 | + // | Field | Value | |
| 508 | + // | -------- | ---------------- | |
| 509 | + // | name | library | |
| 510 | + // | version | 1.2 | |
| 511 | + // | platform | Library Platform | |
| 512 | + client = new RawMongoClient(this.configuration.url(), { |
| 513 | + maxIdleTimeMS: 1, |
| 514 | + driverInfo: { name: 'library', version: '1.2', platform: 'Library Platform' } |
| 515 | + }); |
| 516 | + |
| 517 | + // 2. Send a `ping` command to the server and verify that the command succeeds. |
| 518 | + // 3. Save intercepted `client` document as `clientMetadata`. |
| 519 | + sinon |
| 520 | + .stub(Connection.prototype, 'command') |
| 521 | + .callsFake(async function (ns, cmd, options, responseType) { |
| 522 | + // @ts-expect-error: sinon will place wrappedMethod on the command method. |
| 523 | + const command = Connection.prototype.command.wrappedMethod.bind(this); |
| 524 | + |
| 525 | + if (cmd.hello || cmd[LEGACY_HELLO_COMMAND]) { |
| 526 | + if (!initialClientMetadata) { |
| 527 | + initialClientMetadata = cmd.client; |
| 528 | + } else { |
| 529 | + updatedClientMetadata = cmd.client; |
| 530 | + } |
| 531 | + } |
| 532 | + return command(ns, cmd, options, responseType); |
| 533 | + }); |
| 534 | + |
| 535 | + await client.db('test').command({ ping: 1 }); |
| 536 | + |
| 537 | + // 4. Wait 5ms for the connection to become idle. |
| 538 | + await sleep(5); |
| 539 | + }); |
| 540 | + |
| 541 | + it('appends the metadata', async function () { |
| 542 | + // 5. Append the following `DriverInfoOptions` to the `MongoClient` metadata: |
| 543 | + // | Field | Value | |
| 544 | + // | -------- | ---------------- | |
| 545 | + // | name | library | |
| 546 | + // | version | 1.2 | |
| 547 | + // | platform | Library Platform | |
| 548 | + client.appendMetadata({ name: 'library', version: '1.2', platform: 'Library Platform' }); |
| 549 | + |
| 550 | + // 6. Send a `ping` command to the server and verify that the command succeeds. |
| 551 | + // 7. Save intercepted `client` document as `updatedClientMetadata`. |
| 552 | + await client.db('test').command({ ping: 1 }); |
| 553 | + |
| 554 | + // 8. Assert that `clientMetadata` is identical to `updatedClientMetadata`. |
| 555 | + expect(initialClientMetadata).to.deep.equal(updatedClientMetadata); |
| 556 | + }); |
| 557 | + }); |
368 | 558 | });
|
0 commit comments