1
1
/* 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 ;
2
6
import { type TestConfiguration } from '../../tools/runner/config' ;
3
7
import { runScriptAndGetProcessInfo } from './resource_tracking_script_builder' ;
8
+ import { sleep } from '../../tools/utils' ;
4
9
5
10
describe . only ( 'MongoClient.close() Integration' , ( ) => {
6
11
// note: these tests are set-up in accordance of the resource ownership tree
@@ -91,7 +96,7 @@ describe.only('MongoClient.close() Integration', () => {
91
96
describe ( 'MonitorInterval' , ( ) => {
92
97
describe ( 'Node.js resource: Timer' , ( ) => {
93
98
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 ( ) {
95
100
const run = async function ( { MongoClient, uri, expect, sleep, getTimerCount } ) {
96
101
const heartbeatFrequencyMS = 2000 ;
97
102
const client = new MongoClient ( uri , { heartbeatFrequencyMS } ) ;
@@ -119,7 +124,7 @@ describe.only('MongoClient.close() Integration', () => {
119
124
} ) ;
120
125
121
126
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 ( ) => { } ) ;
123
128
} ) ;
124
129
} ) ;
125
130
} ) ;
@@ -161,7 +166,7 @@ describe.only('MongoClient.close() Integration', () => {
161
166
describe ( 'RTT Pinger' , ( ) => {
162
167
describe ( 'Node.js resource: Timer' , ( ) => {
163
168
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 ( ) {
165
170
const run = async function ( { MongoClient, uri, expect, sleep, getTimerCount } ) {
166
171
const heartbeatFrequencyMS = 2000 ;
167
172
const client = new MongoClient ( uri , {
@@ -198,7 +203,7 @@ describe.only('MongoClient.close() Integration', () => {
198
203
describe ( 'Connection' , ( ) => {
199
204
describe ( 'Node.js resource: Socket' , ( ) => {
200
205
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 ( ) => {
202
207
const run = async ( { MongoClient, uri, expect, sleep } ) => {
203
208
const heartbeatFrequencyMS = 100 ;
204
209
const client = new MongoClient ( uri , {
@@ -266,14 +271,17 @@ describe.only('MongoClient.close() Integration', () => {
266
271
267
272
const servers = client . topology ?. s . servers ;
268
273
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
+ }
274
279
}
280
+ // note: minPoolSizeCheckFrequencyMS = 100 ms by client, so this test has a chance of being flaky
281
+ expect ( getMinPoolSizeTimer ( servers ) ) . to . exist ;
275
282
276
283
await client . close ( ) ;
284
+ expect ( getMinPoolSizeTimer ( servers ) ) . to . not . exist ;
277
285
expect ( getTimerCount ( ) ) . to . equal ( 0 ) ;
278
286
} ;
279
287
await runScriptAndGetProcessInfo ( 'timer-min-pool-size' , config , run ) ;
@@ -285,29 +293,30 @@ describe.only('MongoClient.close() Integration', () => {
285
293
// waitQueueTimeoutMS
286
294
describe ( 'after new connection pool is created' , ( ) => {
287
295
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 ;
290
299
const client = new MongoClient ( uri , { minPoolSize : 1 , waitQueueTimeoutMS } ) ;
300
+ const timeoutStartedSpy = sinon . spy ( mongodb . Timeout , 'expires' ) ;
301
+ let checkoutTimeoutStarted = false ;
291
302
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
+ } ) ;
295
307
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 ) ;
301
309
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 ;
304
313
305
314
await client . close ( ) ;
306
315
expect ( getTimerCount ( ) ) . to . equal ( 0 ) ;
307
- sinon . restore ( ) ;
308
316
} ;
309
317
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} ) ;
311
320
} ) ;
312
321
} ) ;
313
322
} ) ;
@@ -348,8 +357,32 @@ describe.only('MongoClient.close() Integration', () => {
348
357
349
358
describe ( 'SrvPoller' , ( ) => {
350
359
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
+ } ;
351
366
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
+ } ) ;
353
386
} ) ;
354
387
} ) ;
355
388
} ) ;
0 commit comments