|
1 | 1 | import { expect } from 'chai';
|
2 | 2 | import { once } from 'events';
|
| 3 | +import * as sinon from 'sinon'; |
3 | 4 |
|
4 | 5 | import {
|
5 | 6 | BSONType,
|
6 | 7 | type ChangeStream,
|
7 | 8 | ClientSession,
|
8 | 9 | type Collection,
|
9 |
| - type MongoClient, |
| 10 | + MongoClient, |
10 | 11 | MongoNotConnectedError,
|
11 | 12 | ProfilingLevel,
|
12 | 13 | Topology,
|
13 | 14 | TopologyType
|
14 | 15 | } from '../../mongodb';
|
| 16 | +import { type FailPoint, sleep } from '../../tools/utils'; |
15 | 17 |
|
16 | 18 | describe('When executing an operation for the first time', () => {
|
17 | 19 | let client: MongoClient;
|
@@ -821,4 +823,104 @@ describe('When executing an operation for the first time', () => {
|
821 | 823 | });
|
822 | 824 | });
|
823 | 825 | });
|
| 826 | + |
| 827 | + describe('when CSOT is enabled', function () { |
| 828 | + let client: MongoClient; |
| 829 | + |
| 830 | + beforeEach(async function () { |
| 831 | + client = this.configuration.newClient({ timeoutMS: 500 }); |
| 832 | + }); |
| 833 | + |
| 834 | + afterEach(async function () { |
| 835 | + await client.close(); |
| 836 | + }); |
| 837 | + |
| 838 | + describe('when nothing is wrong', function () { |
| 839 | + it('connects the client', async function () { |
| 840 | + await client.connect(); |
| 841 | + expect(client).to.have.property('topology').that.is.instanceOf(Topology); |
| 842 | + }); |
| 843 | + }); |
| 844 | + |
| 845 | + describe( |
| 846 | + 'when the server requires auth and ping is delayed', |
| 847 | + { requires: { auth: 'enabled', mongodb: '>=4.4' } }, |
| 848 | + function () { |
| 849 | + beforeEach(async function () { |
| 850 | + // set failpoint to delay ping |
| 851 | + // create new util client to avoid affecting the test client |
| 852 | + const utilClient = this.configuration.newClient(); |
| 853 | + await utilClient.db('admin').command({ |
| 854 | + configureFailPoint: 'failCommand', |
| 855 | + mode: { times: 1 }, |
| 856 | + data: { failCommands: ['ping'], blockConnection: true, blockTimeMS: 1000 } |
| 857 | + } as FailPoint); |
| 858 | + await utilClient.close(); |
| 859 | + }); |
| 860 | + |
| 861 | + it('timeoutMS from the client is not used for the internal `ping`', async function () { |
| 862 | + const start = performance.now(); |
| 863 | + const returnedClient = await client.connect(); |
| 864 | + const end = performance.now(); |
| 865 | + expect(returnedClient).to.equal(client); |
| 866 | + expect(end - start).to.be.within(1000, 1500); // timeoutMS is 1000, did not apply. |
| 867 | + }); |
| 868 | + } |
| 869 | + ); |
| 870 | + |
| 871 | + describe( |
| 872 | + 'when server selection takes longer than the timeout', |
| 873 | + { requires: { auth: 'enabled', mongodb: '>=4.4' } }, |
| 874 | + function () { |
| 875 | + beforeEach(async function () { |
| 876 | + const selectServerStub = sinon |
| 877 | + .stub(Topology.prototype, 'selectServer') |
| 878 | + .callsFake(async function (selector, options) { |
| 879 | + await sleep(1000); |
| 880 | + const result = selectServerStub.wrappedMethod.call(this, selector, options); |
| 881 | + sinon.restore(); // restore after connect selection |
| 882 | + return result; |
| 883 | + }); |
| 884 | + }); |
| 885 | + |
| 886 | + // restore sinon stub after test |
| 887 | + afterEach(() => { |
| 888 | + sinon.restore(); |
| 889 | + }); |
| 890 | + |
| 891 | + it('client.connect() takes as long as selectServer is delayed for and does not throw a timeout error', async function () { |
| 892 | + const start = performance.now(); |
| 893 | + expect(client.topology).to.not.exist; // make sure not connected. |
| 894 | + const res = await client.db().collection('test').insertOne({ a: 1 }, { timeoutMS: 500 }); // auto-connect |
| 895 | + const end = performance.now(); |
| 896 | + expect(res).to.have.property('acknowledged', true); |
| 897 | + expect(end - start).to.be.within(1000, 1500); // timeoutMS is 1000, did not apply. |
| 898 | + }); |
| 899 | + } |
| 900 | + ); |
| 901 | + |
| 902 | + describe('when auto connect is used and connect() takes longer than timeoutMS', function () { |
| 903 | + // This test stubs the connect method to check that connect() does not get timed out |
| 904 | + // vs. the test above makes sure that the `ping` does not inherit the client's timeoutMS setting |
| 905 | + beforeEach(async function () { |
| 906 | + const connectStub = sinon |
| 907 | + .stub(MongoClient.prototype, 'connect') |
| 908 | + .callsFake(async function () { |
| 909 | + await sleep(1000); |
| 910 | + const result = connectStub.wrappedMethod.call(this); |
| 911 | + sinon.restore(); // restore after connect selection |
| 912 | + return result; |
| 913 | + }); |
| 914 | + }); |
| 915 | + |
| 916 | + it('the operation succeeds', async function () { |
| 917 | + const start = performance.now(); |
| 918 | + expect(client.topology).to.not.exist; // make sure not connected. |
| 919 | + const res = await client.db().collection('test').insertOne({ a: 1 }); // auto-connect |
| 920 | + const end = performance.now(); |
| 921 | + expect(res).to.have.property('acknowledged', true); |
| 922 | + expect(end - start).to.be.within(1000, 1500); // timeoutMS is 1000, did not apply. |
| 923 | + }); |
| 924 | + }); |
| 925 | + }); |
824 | 926 | });
|
0 commit comments