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 ;
26import { type TestConfiguration } from '../../tools/runner/config' ;
37import { runScriptAndGetProcessInfo } from './resource_tracking_script_builder' ;
8+ import { sleep } from '../../tools/utils' ;
49
510describe . 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 } ) ;
0 commit comments