Skip to content

Commit 24ebcde

Browse files
eod commit
1 parent 1f9f0ba commit 24ebcde

File tree

2 files changed

+58
-25
lines changed

2 files changed

+58
-25
lines changed

test/integration/node-specific/client_close.test.ts

Lines changed: 57 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
/* eslint-disable @typescript-eslint/no-empty-function */
2+
const { expect } = require('chai');
3+
import * as sinon from 'sinon';
4+
const mongodb = require('../../mongodb');
5+
const { MongoClient } = mongodb;
26
import { type TestConfiguration } from '../../tools/runner/config';
37
import { runScriptAndGetProcessInfo } from './resource_tracking_script_builder';
8+
import { sleep } from '../../tools/utils';
49

510
describe.only('MongoClient.close() Integration', () => {
611
// note: these tests are set-up in accordance of the resource ownership tree
@@ -91,7 +96,7 @@ describe.only('MongoClient.close() Integration', () => {
9196
describe('MonitorInterval', () => {
9297
describe('Node.js resource: Timer', () => {
9398
describe('after a new monitor is made', () => {
94-
it('monitor interval timer is cleaned up by client.close()', async function () {
99+
it('monitor interval timer is cleaned up by client.close()', metadata, async function () {
95100
const run = async function ({ MongoClient, uri, expect, sleep, getTimerCount }) {
96101
const heartbeatFrequencyMS = 2000;
97102
const client = new MongoClient(uri, { heartbeatFrequencyMS });
@@ -119,7 +124,7 @@ describe.only('MongoClient.close() Integration', () => {
119124
});
120125

121126
describe('after a heartbeat fails', () => {
122-
it.skip('the new monitor interval timer is cleaned up by client.close()', async () => {});
127+
it.skip('the new monitor interval timer is cleaned up by client.close()', metadata, async () => {});
123128
});
124129
});
125130
});
@@ -161,7 +166,7 @@ describe.only('MongoClient.close() Integration', () => {
161166
describe('RTT Pinger', () => {
162167
describe('Node.js resource: Timer', () => {
163168
describe('after entering monitor streaming mode ', () => {
164-
it('the rtt pinger timer is cleaned up by client.close()', async function () {
169+
it('the rtt pinger timer is cleaned up by client.close()', metadata, async function () {
165170
const run = async function ({ MongoClient, uri, expect, sleep, getTimerCount }) {
166171
const heartbeatFrequencyMS = 2000;
167172
const client = new MongoClient(uri, {
@@ -198,7 +203,7 @@ describe.only('MongoClient.close() Integration', () => {
198203
describe('Connection', () => {
199204
describe('Node.js resource: Socket', () => {
200205
describe('when rtt monitoring is turned on', () => {
201-
it('no sockets remain after client.close()', async () => {
206+
it('no sockets remain after client.close()', metadata, async () => {
202207
const run = async ({ MongoClient, uri, expect, sleep }) => {
203208
const heartbeatFrequencyMS = 100;
204209
const client = new MongoClient(uri, {
@@ -266,14 +271,17 @@ describe.only('MongoClient.close() Integration', () => {
266271

267272
const servers = client.topology?.s.servers;
268273

269-
// note: minPoolSizeCheckFrequencyMS = 100 ms by client, so this test has a chance of being flaky
270-
for (const server of servers) {
271-
const minPoolSizeTimer = server[1].pool.minPoolSizeTimer;
272-
expect(minPoolSizeTimer).to.exist;
273-
break;
274+
275+
function getMinPoolSizeTimer(servers) {
276+
for (const server of servers) {
277+
return server[1].pool.minPoolSizeTimer;
278+
}
274279
}
280+
// note: minPoolSizeCheckFrequencyMS = 100 ms by client, so this test has a chance of being flaky
281+
expect(getMinPoolSizeTimer(servers)).to.exist;
275282

276283
await client.close();
284+
expect(getMinPoolSizeTimer(servers)).to.not.exist;
277285
expect(getTimerCount()).to.equal(0);
278286
};
279287
await runScriptAndGetProcessInfo('timer-min-pool-size', config, run);
@@ -285,29 +293,30 @@ describe.only('MongoClient.close() Integration', () => {
285293
// waitQueueTimeoutMS
286294
describe('after new connection pool is created', () => {
287295
it('the wait queue timer is cleaned up by client.close()', async function () {
288-
const run = async function ({ MongoClient, uri, expect, sinon, mongodb, getTimerCount }) {
289-
const waitQueueTimeoutMS = 999999;
296+
// note: this test is not called in a separate process since it requires stubbing internal function
297+
const run = async function ({ MongoClient, uri, expect, sinon, sleep, mongodb, getTimerCount }) {
298+
const waitQueueTimeoutMS = 999;
290299
const client = new MongoClient(uri, { minPoolSize: 1, waitQueueTimeoutMS });
300+
const timeoutStartedSpy = sinon.spy(mongodb.Timeout, 'expires');
301+
let checkoutTimeoutStarted = false;
291302

292-
sinon.spy(mongodb.Timeout, 'expires');
293-
const timeoutContextSpy = sinon.spy(mongodb.TimeoutContext, 'create');
294-
// TODO delay promise.race non-timeout promise
303+
// make waitQueue hang so check out timer isn't cleared and check that the timeout has started
304+
sinon.stub(mongodb.ConnectionPool.prototype, 'processWaitQueue').callsFake(async () => {
305+
checkoutTimeoutStarted = timeoutStartedSpy.getCalls().map(r => r.args).filter(r => r.includes(999)) ? true : false;
306+
});
295307

296-
await client
297-
.db('db')
298-
.collection('collection')
299-
.insertOne({ x: 1 })
300-
.catch(e => e);
308+
client.db('db').collection('collection').insertOne({ x: 1 }).catch(e => e);
301309

302-
expect(timeoutContextSpy.getCalls()).to.have.length.greaterThanOrEqual(1);
303-
expect(mongodb.Timeout.expires).to.have.been.calledWith(999999);
310+
// don't allow entire checkout timer to elapse to ensure close is called mid-timeout
311+
await sleep(waitQueueTimeoutMS / 2);
312+
expect(checkoutTimeoutStarted).to.be.true;
304313

305314
await client.close();
306315
expect(getTimerCount()).to.equal(0);
307-
sinon.restore();
308316
};
309317

310-
await runScriptAndGetProcessInfo('timer-check-out', config, run);
318+
const getTimerCount = () => process.getActiveResourcesInfo().filter(r => r === 'Timeout').length;
319+
await run({ MongoClient, uri: config.uri, sleep, sinon, expect, mongodb, getTimerCount});
311320
});
312321
});
313322
});
@@ -348,8 +357,32 @@ describe.only('MongoClient.close() Integration', () => {
348357

349358
describe('SrvPoller', () => {
350359
describe('Node.js resource: Timer', () => {
360+
// srv polling is not available for load-balanced mode
361+
const metadata: MongoDBMetadataUI = {
362+
requires: {
363+
topology: ['single', 'replicaset', 'sharded']
364+
}
365+
};
351366
describe('after SRVPoller is created', () => {
352-
it.skip('timers are cleaned up by client.close()', async () => {});
367+
it.only('timers are cleaned up by client.close()', metadata, async () => {
368+
const run = async function ({ MongoClient, uri, expect, sinon, getTimerCount }) {
369+
const dns = require('dns');
370+
371+
sinon.stub(dns.promises, 'resolveTxt').callsFake(async () => uri);
372+
sinon.stub(dns.promises, 'resolveSrv').callsFake(async () => uri);
373+
374+
const srvUri = uri.replace('mongodb://', 'mongodb+srv://');
375+
const client = new MongoClient(srvUri);
376+
await client.connect();
377+
378+
379+
await client.close();
380+
expect(getTimerCount()).to.equal(0);
381+
};
382+
383+
const getTimerCount = () => process.getActiveResourcesInfo().filter(r => r === 'Timeout').length;
384+
await run({ MongoClient, uri: config.uri, sleep, sinon, expect, mongodb, getTimerCount});
385+
});
353386
});
354387
});
355388
});

test/integration/node-specific/resource_tracking_script_builder.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,7 @@ export async function runScriptAndGetProcessInfo(
188188
.reduce((acc, curr) => ({ ...acc, ...curr }), {});
189189

190190
// delete temporary files
191-
await unlink(scriptName);
191+
// await unlink(scriptName);
192192
// await unlink(logFile);
193193

194194
// assertions about exit status

0 commit comments

Comments
 (0)