|  | 
| 1 | 1 | import { expect } from 'chai'; | 
| 2 | 2 | import * as sinon from 'sinon'; | 
| 3 | 3 | 
 | 
| 4 |  | -import { type ClientMetadata } from '../../../mongodb'; | 
|  | 4 | +import { type ClientMetadata, type DriverInfo } from '../../../mongodb'; | 
| 5 | 5 | import { MongoClient as RawMongoClient } from '../../../src'; | 
| 6 | 6 | import { | 
| 7 | 7 |   Connection, | 
| @@ -394,8 +394,7 @@ describe('Client Metadata Update Prose Tests', function () { | 
| 394 | 394 |       { testCase: 4, name: 'library', version: '1.2', platform: 'Framework Platform' }, | 
| 395 | 395 |       { testCase: 5, name: 'framework', version: '2.0', platform: 'Library Platform' }, | 
| 396 | 396 |       { 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' } | 
| 399 | 398 |     ]; | 
| 400 | 399 | 
 | 
| 401 | 400 |     for (const { testCase, ...driverInfo } of tests) { | 
| @@ -752,4 +751,211 @@ describe('Client Metadata Update Prose Tests', function () { | 
| 752 | 751 |       expect(updatedClientMetadata).to.deep.equal(initialClientMetadata); | 
| 753 | 752 |     }); | 
| 754 | 753 |   }); | 
|  | 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 | +  }); | 
| 755 | 961 | }); | 
0 commit comments