@@ -191,7 +191,7 @@ function assertMonitoringOutcome(outcome: any): asserts outcome is MonitoringOut
191191 expect ( outcome ) . to . have . property ( 'events' ) . that . is . an ( 'array' ) ;
192192}
193193
194- describe . only ( 'Server Discovery and Monitoring (spec)' , function ( ) {
194+ describe ( 'Server Discovery and Monitoring (spec)' , function ( ) {
195195 let serverConnect : sinon . SinonStub ;
196196
197197 before ( ( ) => {
@@ -314,87 +314,100 @@ const SDAM_EVENTS = [
314314 ...HEARTBEAT_EVENTS
315315] ;
316316
317- function checkoutStubImpl ( appError : ApplicationError ) {
318- return async function ( ) {
319- const connectionPoolGeneration = this . generation ;
320- const fakeConnection = {
321- generation :
322- typeof appError . generation === 'number' ? appError . generation : connectionPoolGeneration ,
323- async command ( _ , __ , ___ ) {
324- switch ( appError . type ) {
325- case 'network' :
326- throw new MongoNetworkError ( 'test generated' ) ;
327- case 'timeout' :
328- throw new MongoNetworkTimeoutError ( 'xxx timed out' ) ;
329- case 'command' :
330- throw new MongoServerError ( appError . response ) ;
331- default :
332- throw new Error (
333- // @ts -expect-error `.type` is never, but we want to access it in this unreachable code to
334- // throw an error message.
335- `SDAM unit test runner error: unexpected appError.type field: ${ appError . type } `
336- ) ;
337- }
338- }
339- } ;
340- return fakeConnection as any as Connection ;
341- } ;
342- }
343-
344- function stubConnectionEstablishment ( appError : ApplicationError ) {
345- const stubs = [ ] ;
346- if ( appError . when === 'afterHandshakeCompletes' ) {
347- const checkOutStub = sinon
348- . stub ( ConnectionPool . prototype , 'checkOut' )
349- . callsFake ( checkoutStubImpl ( appError ) ) ;
350- stubs . push ( checkOutStub ) ;
351- return stubs ;
317+ function makeConnectionsError ( appError : ApplicationError ) : sinon . SinonStub [ ] {
318+ switch ( appError . when ) {
319+ case 'beforeHandshakeCompletes' :
320+ return stubBeforeHandshake ( appError ) ;
321+ case 'afterHandshakeCompletes' :
322+ return [
323+ sinon . stub ( ConnectionPool . prototype , 'checkOut' ) . callsFake ( checkoutStubImpl ( appError ) )
324+ ] ;
325+ default :
326+ throw new Error ( 'unexpected value for `.when`: ' + appError . when ) ;
352327 }
353328
354- // eslint-disable-next-line @typescript-eslint/no-require-imports
355- const net : typeof import ( 'net' ) = require ( 'net' ) ;
356-
357- const netStub = sinon . stub ( net , 'createConnection' ) ;
329+ // if `appError.when === 'beforeHandshake`:
330+ // "Simulate this mock error as if it occurred during a new connection's handshake for an application operation."
331+ // This function mocks the basic `createConnection` API to return an unconnected socket, and uses a special connection
332+ // subclass that always throws when `command()` is executed.
333+ function stubBeforeHandshake ( appError : ApplicationError ) {
334+ const stubs = [ ] ;
335+ // eslint-disable-next-line @typescript-eslint/no-require-imports
336+ const net : typeof import ( 'net' ) = require ( 'net' ) ;
337+
338+ const netStub = sinon . stub ( net , 'createConnection' ) ;
339+
340+ netStub . callsFake ( function createConnectionStub ( ) {
341+ const socket = new net . Socket ( ) ;
342+ process . nextTick ( ( ) => socket . emit ( 'connect' ) ) ;
343+ return socket ;
344+ } ) ;
358345
359- netStub . callsFake ( function createConnectionStub ( ) {
360- const socket = new net . Socket ( ) ;
361- process . nextTick ( ( ) => socket . emit ( 'connect' ) ) ;
362- return socket ;
363- } ) ;
346+ stubs . push ( netStub ) ;
347+
348+ class StubbedConnection extends Connection {
349+ override command (
350+ _ns : unknown ,
351+ command : Document ,
352+ _options ?: unknown ,
353+ _responseType ?: unknown
354+ ) : Promise < any > {
355+ if ( command . hello || command [ LEGACY_HELLO_COMMAND ] ) {
356+ throw new MongoNetworkError ( `error executing command` , { beforeHandshake : true } ) ;
357+ }
364358
365- stubs . push ( netStub ) ;
366-
367- class StubbedConnection extends Connection {
368- override command (
369- _ns : unknown ,
370- command : Document ,
371- _options ?: unknown ,
372- _responseType ?: unknown
373- ) : Promise < any > {
374- if ( command . hello || command [ LEGACY_HELLO_COMMAND ] ) {
375- throw new MongoNetworkError ( `error executing command` , { beforeHandshake : true } ) ;
359+ throw new Error ( 'unexpected command: ' , command ) ;
376360 }
377-
378- throw new Error ( 'unexpected command: ' , command ) ;
379361 }
380- }
381362
382- // eslint-disable-next-line @typescript-eslint/no-require-imports
383- const connectionUtils : typeof import ( '../../../src/cmap/connect' ) = require ( '../../../src/cmap/connect' ) ;
363+ // eslint-disable-next-line @typescript-eslint/no-require-imports
364+ const connectionUtils : typeof import ( '../../../src/cmap/connect' ) = require ( '../../../src/cmap/connect' ) ;
365+
366+ const wrapped = sinon
367+ . stub ( connectionUtils , 'connect' )
368+ . callsFake ( async function connect ( options ) {
369+ const generation =
370+ typeof appError . generation === 'number' ? appError . generation : options . generation ;
371+ return wrapped . wrappedMethod ( {
372+ ...options ,
373+ generation,
374+ connectionType : StubbedConnection
375+ } ) ;
376+ } ) ;
384377
385- const wrapped = sinon . stub ( connectionUtils , 'connect' ) . callsFake ( async function connect ( options ) {
386- const generation =
387- typeof appError . generation === 'number' ? appError . generation : options . generation ;
388- return wrapped . wrappedMethod ( {
389- ...options ,
390- generation,
391- connectionType : StubbedConnection
392- } ) ;
393- } ) ;
378+ stubs . push ( wrapped ) ;
394379
395- stubs . push ( wrapped ) ;
380+ return stubs ;
381+ }
396382
397- return stubs ;
383+ // "Simulate this mock error as if it occurred on an established connection for an application operation (i.e. after the connection pool check out succeeds)."
384+ // This one is simple - return a stubbed connection that always throws. No need to worry about the internals of `connect()`.
385+ function checkoutStubImpl ( appError : ApplicationError ) {
386+ return async function ( ) {
387+ const connectionPoolGeneration = this . generation ;
388+ const fakeConnection = {
389+ generation :
390+ typeof appError . generation === 'number' ? appError . generation : connectionPoolGeneration ,
391+ async command ( _ , __ , ___ ) {
392+ switch ( appError . type ) {
393+ case 'network' :
394+ throw new MongoNetworkError ( 'test generated' ) ;
395+ case 'timeout' :
396+ throw new MongoNetworkTimeoutError ( 'xxx timed out' ) ;
397+ case 'command' :
398+ throw new MongoServerError ( appError . response ) ;
399+ default :
400+ throw new Error (
401+ // @ts -expect-error `.type` is never, but we want to access it in this unreachable code to
402+ // throw a helpful error message.
403+ `SDAM unit test runner error: unexpected appError.type field: ${ appError . type } `
404+ ) ;
405+ }
406+ }
407+ } ;
408+ return fakeConnection as any as Connection ;
409+ } ;
410+ }
398411}
399412
400413async function executeSDAMTest ( testData : SDAMTest ) {
@@ -424,7 +437,7 @@ async function executeSDAMTest(testData: SDAMTest) {
424437 for ( const appError of phase . applicationErrors ) {
425438 // Stub will return appError to SDAM machinery
426439
427- const stubs = stubConnectionEstablishment ( appError ) ;
440+ const stubs = makeConnectionsError ( appError ) ;
428441
429442 const server = client . topology . s . servers . get ( appError . address ) ;
430443
0 commit comments