Skip to content

Commit 645784d

Browse files
committed
test(NODE-6626): test resources are cleaned up
1 parent 3cc3a6b commit 645784d

File tree

1 file changed

+209
-26
lines changed

1 file changed

+209
-26
lines changed

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

Lines changed: 209 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
/* eslint-disable @typescript-eslint/no-empty-function */
2+
import { expect } from 'chai';
3+
24
import { getCSFLEKMSProviders } from '../../csfle-kms-providers';
5+
import { type Collection, type FindCursor, type MongoClient } from '../../mongodb';
36
import { type TestConfiguration } from '../../tools/runner/config';
47
import { runScriptAndGetProcessInfo } from './resource_tracking_script_builder';
58

6-
describe.skip('MongoClient.close() Integration', () => {
9+
describe('MongoClient.close() Integration', () => {
710
// note: these tests are set-up in accordance of the resource ownership tree
811

912
let config: TestConfiguration;
@@ -14,7 +17,7 @@ describe.skip('MongoClient.close() Integration', () => {
1417

1518
describe('Node.js resource: TLS File read', () => {
1619
describe('when client is connecting and reads an infinite TLS file', () => {
17-
it('the file read is interrupted by client.close()', async function () {
20+
it.skip('the file read is interrupted by client.close()', async function () {
1821
await runScriptAndGetProcessInfo(
1922
'tls-file-read',
2023
config,
@@ -50,7 +53,7 @@ describe.skip('MongoClient.close() Integration', () => {
5053
});
5154

5255
describe('when MongoClientAuthProviders is instantiated and token file read hangs', () => {
53-
it('the file read is interrupted by client.close()', async () => {
56+
it.skip('the file read is interrupted by client.close()', async () => {
5457
await runScriptAndGetProcessInfo(
5558
'token-file-read',
5659
config,
@@ -77,8 +80,7 @@ describe.skip('MongoClient.close() Integration', () => {
7780
describe('Node.js resource: Server Selection Timer', () => {
7881
describe('after a Topology is created through client.connect()', () => {
7982
const metadata: MongoDBMetadataUI = { requires: { topology: 'replicaset' } };
80-
81-
it('server selection timers are cleaned up by client.close()', metadata, async () => {
83+
it.skip('server selection timers are cleaned up by client.close()', metadata, async () => {
8284
const run = async function ({ MongoClient, uri, expect, sleep, mongodb, getTimerCount }) {
8385
const serverSelectionTimeoutMS = 2222;
8486
const client = new MongoClient(uri, {
@@ -117,7 +119,7 @@ describe.skip('MongoClient.close() Integration', () => {
117119
describe('MonitorInterval', () => {
118120
describe('Node.js resource: Timer', () => {
119121
describe('after a new monitor is made', () => {
120-
it(
122+
it.skip(
121123
'monitor interval timer is cleaned up by client.close()',
122124
metadata,
123125
async function () {
@@ -150,7 +152,7 @@ describe.skip('MongoClient.close() Integration', () => {
150152
});
151153

152154
describe('after a heartbeat fails', () => {
153-
it(
155+
it.skip(
154156
'the new monitor interval timer is cleaned up by client.close()',
155157
metadata,
156158
async () => {
@@ -160,7 +162,6 @@ describe.skip('MongoClient.close() Integration', () => {
160162
const willBeHeartbeatFailed = once(client, 'serverHeartbeatFailed');
161163
client.connect();
162164
await willBeHeartbeatFailed;
163-
164165
function getMonitorTimer(servers) {
165166
for (const [, server] of servers) {
166167
return server?.monitor.monitorId.timerId;
@@ -183,7 +184,7 @@ describe.skip('MongoClient.close() Integration', () => {
183184

184185
describe('Monitoring Connection', () => {
185186
describe('Node.js resource: Socket', () => {
186-
it('no sockets remain after client.close()', metadata, async function () {
187+
it.skip('no sockets remain after client.close()', metadata, async function () {
187188
const run = async function ({ MongoClient, uri, expect, getSocketEndpoints }) {
188189
const client = new MongoClient(uri);
189190
await client.connect();
@@ -211,7 +212,7 @@ describe.skip('MongoClient.close() Integration', () => {
211212
describe('RTT Pinger', () => {
212213
describe('Node.js resource: Timer', () => {
213214
describe('after entering monitor streaming mode ', () => {
214-
it(
215+
it.skip(
215216
'the rtt pinger timer is cleaned up by client.close()',
216217
metadata,
217218
async function () {
@@ -247,8 +248,8 @@ describe.skip('MongoClient.close() Integration', () => {
247248
describe('Connection', () => {
248249
describe('Node.js resource: Socket', () => {
249250
describe('when rtt monitoring is turned on', () => {
250-
it('no sockets remain after client.close()', metadata, async () => {
251-
const run = async ({ MongoClient, uri, expect, getSockets, once, log }) => {
251+
it.skip('no sockets remain after client.close()', metadata, async () => {
252+
const run = async ({ MongoClient, uri, expect, getSockets, once }) => {
252253
const heartbeatFrequencyMS = 500;
253254
const client = new MongoClient(uri, {
254255
serverMonitoringMode: 'stream',
@@ -265,7 +266,6 @@ describe.skip('MongoClient.close() Integration', () => {
265266

266267
while (heartbeatOccurredSet.size < servers.size) {
267268
const ev = await once(client, 'serverHeartbeatSucceeded');
268-
log({ ev: ev[0] });
269269
heartbeatOccurredSet.add(ev[0].connectionId);
270270
}
271271

@@ -281,8 +281,6 @@ describe.skip('MongoClient.close() Integration', () => {
281281

282282
// close the client
283283
await client.close();
284-
285-
log({ socketsAfterClose: getSockets() });
286284
// upon close, assert rttPinger sockets are cleaned up
287285
const activeSocketsAfterClose = activeSocketsAfterHeartbeat();
288286
expect(activeSocketsAfterClose).to.have.lengthOf(0);
@@ -299,7 +297,7 @@ describe.skip('MongoClient.close() Integration', () => {
299297
describe('ConnectionPool', () => {
300298
describe('Node.js resource: minPoolSize timer', () => {
301299
describe('after new connection pool is created', () => {
302-
it('the minPoolSize timer is cleaned up by client.close()', async function () {
300+
it.skip('the minPoolSize timer is cleaned up by client.close()', async function () {
303301
const run = async function ({ MongoClient, uri, expect, getTimerCount }) {
304302
const client = new MongoClient(uri, { minPoolSize: 1 });
305303
let minPoolSizeTimerCreated = false;
@@ -357,7 +355,7 @@ describe.skip('MongoClient.close() Integration', () => {
357355
await utilClient.close();
358356
});
359357

360-
it('the wait queue timer is cleaned up by client.close()', async function () {
358+
it.skip('the wait queue timer is cleaned up by client.close()', async function () {
361359
const run = async function ({ MongoClient, uri, expect, getTimerCount, once }) {
362360
const waitQueueTimeoutMS = 1515;
363361

@@ -399,7 +397,7 @@ describe.skip('MongoClient.close() Integration', () => {
399397
describe('Connection', () => {
400398
describe('Node.js resource: Socket', () => {
401399
describe('after a minPoolSize has been set on the ConnectionPool', () => {
402-
it('no sockets remain after client.close()', async function () {
400+
it.skip('no sockets remain after client.close()', async function () {
403401
const run = async function ({ MongoClient, uri, expect, getSockets }) {
404402
// assert no sockets to start with
405403
expect(getSockets()).to.have.lengthOf(0);
@@ -431,13 +429,17 @@ describe.skip('MongoClient.close() Integration', () => {
431429
const metadata: MongoDBMetadataUI = { requires: { topology: 'sharded' } };
432430

433431
describe('after SRVPoller is created', () => {
434-
it('timers are cleaned up by client.close()', metadata, async () => {
432+
it.skip('timers are cleaned up by client.close()', metadata, async () => {
435433
const run = async function ({ MongoClient, expect, getTimerCount }) {
436434
const SRV_CONNECTION_STRING = `mongodb+srv://test1.test.build.10gen.cc`;
435+
437436
// 27018 localhost.test.build.10gen.cc.
438437
// 27017 localhost.test.build.10gen.cc.
439438

440-
const client = new MongoClient(SRV_CONNECTION_STRING);
439+
const client = new MongoClient(SRV_CONNECTION_STRING, {
440+
serverSelectionTimeoutMS: 2000,
441+
tls: false
442+
});
441443
await client.connect();
442444
// the current expected behavior is that _timeout is set to undefined until SRV polling starts
443445
// then _timeout is set to undefined again when SRV polling stops
@@ -453,29 +455,143 @@ describe.skip('MongoClient.close() Integration', () => {
453455
});
454456

455457
describe('ClientSession (Implicit)', () => {
458+
let idleSessionsBeforeClose;
459+
let idleSessionsAfterClose;
460+
let client;
461+
let utilClient;
462+
let session;
463+
464+
const metadata: MongoDBMetadataUI = {
465+
requires: {
466+
topology: ['replicaset', 'sharded'],
467+
mongodb: '>=4.2'
468+
}
469+
};
470+
471+
beforeEach(async function () {
472+
client = this.configuration.newClient();
473+
utilClient = this.configuration.newClient();
474+
await client.connect();
475+
await client
476+
.db('db')
477+
.collection('collection')
478+
?.drop()
479+
.catch(() => null);
480+
const collection = await client.db('db').createCollection('collection');
481+
session = client.startSession({ explicit: false });
482+
session.startTransaction();
483+
await collection.insertOne({ x: 1 }, { session });
484+
485+
const opBefore = await utilClient.db().admin().command({ currentOp: 1 });
486+
idleSessionsBeforeClose = opBefore.inprog.filter(s => s.type === 'idleSession');
487+
488+
await client.close();
489+
490+
const opAfter = await utilClient.db().admin().command({ currentOp: 1 });
491+
idleSessionsAfterClose = opAfter.inprog.filter(s => s.type === 'idleSession');
492+
493+
await utilClient.close();
494+
});
495+
496+
afterEach(async function () {
497+
await utilClient?.close();
498+
await session?.endSession();
499+
await client?.close();
500+
});
501+
456502
describe('Server resource: LSID/ServerSession', () => {
457503
describe('after a clientSession is implicitly created and used', () => {
458-
it.skip('the server-side ServerSession is cleaned up by client.close()', async function () {});
504+
it(
505+
'the server-side ServerSession is cleaned up by client.close()',
506+
metadata,
507+
async function () {
508+
expect(idleSessionsBeforeClose).to.not.be.empty;
509+
expect(idleSessionsAfterClose).to.be.empty;
510+
}
511+
);
459512
});
460513
});
461514

462515
describe('Server resource: Transactions', () => {
463516
describe('after a clientSession is implicitly created and used', () => {
464-
it.skip('the server-side transaction is cleaned up by client.close()', async function () {});
517+
it(
518+
'the server-side transaction is cleaned up by client.close()',
519+
metadata,
520+
async function () {
521+
expect(idleSessionsBeforeClose[0].transaction.txnNumber).to.not.null;
522+
expect(idleSessionsAfterClose).to.be.empty;
523+
}
524+
);
465525
});
466526
});
467527
});
468528

469529
describe('ClientSession (Explicit)', () => {
530+
let idleSessionsBeforeClose;
531+
let idleSessionsAfterClose;
532+
let client;
533+
let utilClient;
534+
let session;
535+
536+
const metadata: MongoDBMetadataUI = {
537+
requires: {
538+
topology: ['replicaset', 'sharded'],
539+
mongodb: '>=4.2'
540+
}
541+
};
542+
543+
beforeEach(async function () {
544+
client = this.configuration.newClient();
545+
utilClient = this.configuration.newClient();
546+
await client.connect();
547+
await client
548+
.db('db')
549+
.collection('collection')
550+
?.drop()
551+
.catch(() => null);
552+
const collection = await client.db('db').createCollection('collection');
553+
session = client.startSession();
554+
session.startTransaction();
555+
await collection.insertOne({ x: 1 }, { session });
556+
557+
const opBefore = await utilClient.db().admin().command({ currentOp: 1 });
558+
idleSessionsBeforeClose = opBefore.inprog.filter(s => s.type === 'idleSession');
559+
560+
await client.close();
561+
562+
const opAfter = await utilClient.db().admin().command({ currentOp: 1 });
563+
idleSessionsAfterClose = opAfter.inprog.filter(s => s.type === 'idleSession');
564+
});
565+
566+
afterEach(async function () {
567+
await utilClient?.close();
568+
await session?.endSession();
569+
await client?.close();
570+
});
571+
470572
describe('Server resource: LSID/ServerSession', () => {
471573
describe('after a clientSession is created and used', () => {
472-
it.skip('the server-side ServerSession is cleaned up by client.close()', async function () {});
574+
it(
575+
'the server-side ServerSession is cleaned up by client.close()',
576+
metadata,
577+
async function () {
578+
expect(idleSessionsBeforeClose).to.not.be.empty;
579+
expect(idleSessionsAfterClose).to.be.empty;
580+
}
581+
);
473582
});
474583
});
475584

476585
describe('Server resource: Transactions', () => {
477586
describe('after a clientSession is created and used', () => {
478-
it.skip('the server-side transaction is cleaned up by client.close()', async function () {});
587+
it(
588+
'the server-side transaction is cleaned up by client.close()',
589+
metadata,
590+
async function () {
591+
expect(idleSessionsBeforeClose[0].transaction.txnNumber).to.not.null;
592+
expect(idleSessionsAfterClose).to.be.empty;
593+
}
594+
);
479595
});
480596
});
481597
});
@@ -491,7 +607,7 @@ describe.skip('MongoClient.close() Integration', () => {
491607
describe('KMS Request', () => {
492608
describe('Node.js resource: TLS file read', () => {
493609
describe('when KMSRequest reads an infinite TLS file', () => {
494-
it('the file read is interrupted by client.close()', metadata, async () => {
610+
it.skip('the file read is interrupted by client.close()', metadata, async () => {
495611
await runScriptAndGetProcessInfo(
496612
'tls-file-read-auto-encryption',
497613
config,
@@ -584,8 +700,75 @@ describe.skip('MongoClient.close() Integration', () => {
584700
});
585701

586702
describe('Server resource: Cursor', () => {
703+
const metadata: MongoDBMetadataUI = {
704+
requires: {
705+
mongodb: '>=4.2.0'
706+
}
707+
};
708+
587709
describe('after cursors are created', () => {
588-
it.skip('all active server-side cursors are closed by client.close()', async function () {});
710+
let client: MongoClient;
711+
let coll: Collection;
712+
let cursor: FindCursor;
713+
let utilClient: MongoClient;
714+
715+
beforeEach(async function () {
716+
client = this.configuration.newClient();
717+
utilClient = this.configuration.newClient();
718+
await client.connect();
719+
await client
720+
.db('db')
721+
.collection('coll')
722+
?.drop()
723+
.catch(() => null);
724+
coll = await client
725+
.db('db')
726+
.createCollection('coll', { capped: true, size: 1_000, max: 4 });
727+
await coll.insertMany([{ a: 1 }, { b: 2 }, { c: 3 }]);
728+
});
729+
730+
afterEach(async function () {
731+
await utilClient?.close();
732+
await client?.close();
733+
await cursor?.close();
734+
});
735+
736+
it(
737+
'all active server-side cursors are closed by client.close()',
738+
metadata,
739+
async function () {
740+
const getCursors = async () => {
741+
const res = await utilClient
742+
.db()
743+
.admin()
744+
.command({
745+
aggregate: 1,
746+
cursor: {},
747+
pipeline: [{ $currentOp: { idleCursors: true } }]
748+
});
749+
return res.cursor.firstBatch.filter(
750+
r => r.type === 'idleCursor' || (r.type === 'op' && r.desc === 'getMore')
751+
);
752+
};
753+
754+
cursor = coll.find(
755+
{},
756+
{
757+
tailable: true,
758+
awaitData: true
759+
}
760+
);
761+
await cursor.next();
762+
763+
// assert creation
764+
expect(await getCursors()).to.not.be.empty;
765+
766+
await client.close();
767+
768+
// assert clean-up
769+
expect(await getCursors()).to.be.empty;
770+
}
771+
);
589772
});
590773
});
591774
});

0 commit comments

Comments
 (0)