@@ -3,10 +3,11 @@ import { Duplex } from 'node:stream';
33import { expect } from 'chai' ;
44import * as sinon from 'sinon' ;
55
6- import { type MongoClient , MongoNetworkError } from '../../../src' ;
6+ import { type Collection , type Document , type MongoClient , MongoNetworkError } from '../../../src' ;
77import { Connection } from '../../../src/cmap/connection' ;
88import { ns } from '../../../src/utils' ;
99import { clearFailPoint , configureFailPoint } from '../../tools/utils' ;
10+ import { filterForCommands } from '../shared' ;
1011
1112describe ( 'Socket Errors' , ( ) => {
1213 describe ( 'when a socket emits an error' , ( ) => {
@@ -41,7 +42,7 @@ describe('Socket Errors', () => {
4142
4243 describe ( 'when destroyed by failpoint' , ( ) => {
4344 let client : MongoClient ;
44- let collection ;
45+ let collection : Collection < Document > ;
4546
4647 const metadata : MongoDBMetadataUI = { requires : { mongodb : '>=4.4' } } ;
4748
@@ -77,4 +78,53 @@ describe('Socket Errors', () => {
7778 expect ( error , error . stack ) . to . be . instanceOf ( MongoNetworkError ) ;
7879 } ) ;
7980 } ) ;
81+
82+ describe ( 'when an error is thrown writing data to a socket' , ( ) => {
83+ let client : MongoClient ;
84+ let collection : Collection < Document > ;
85+
86+ beforeEach ( async function ( ) {
87+ client = this . configuration . newClient ( { monitorCommands : true } ) ;
88+ await client . connect ( ) ;
89+ const db = client . db ( 'closeConn' ) ;
90+ collection = db . collection ( 'closeConn' ) ;
91+ await collection . deleteMany ( { } ) ;
92+
93+ for ( const [ , server ] of client . topology . s . servers ) {
94+ //@ts -expect-error: private property
95+ for ( const connection of server . pool . connections ) {
96+ //@ts -expect-error: private property
97+ const socket = connection . socket ;
98+ const stub = sinon . stub ( socket , 'write' ) . callsFake ( function ( ) {
99+ stub . restore ( ) ;
100+ throw new Error ( 'This socket has been ended by the other party' ) ;
101+ } ) ;
102+ }
103+ }
104+ } ) ;
105+
106+ afterEach ( async function ( ) {
107+ sinon . restore ( ) ;
108+ await client . close ( ) ;
109+ } ) ;
110+
111+ it ( 'retries and succeeds' , async ( ) => {
112+ const commandSucceededEvents : string [ ] = [ ] ;
113+ const commandFailedEvents : string [ ] = [ ] ;
114+ const commandStartedEvents : string [ ] = [ ] ;
115+
116+ client . on ( 'commandStarted' , filterForCommands ( 'find' , commandStartedEvents ) ) ;
117+ client . on ( 'commandSucceeded' , filterForCommands ( 'find' , commandSucceededEvents ) ) ;
118+ client . on ( 'commandFailed' , filterForCommands ( 'find' , commandFailedEvents ) ) ;
119+
120+ // call find, fail once, succeed on retry
121+ const item = await collection . findOne ( { } ) ;
122+ // check that we didn't find anything, as expected
123+ expect ( item ) . to . be . null ;
124+ // check that we have the expected command monitoring events
125+ expect ( commandStartedEvents ) . to . have . length ( 2 ) ;
126+ expect ( commandFailedEvents ) . to . have . length ( 1 ) ;
127+ expect ( commandSucceededEvents ) . to . have . length ( 1 ) ;
128+ } ) ;
129+ } ) ;
80130} ) ;
0 commit comments