@@ -100,22 +100,28 @@ describe('Retry Timeout is Enforced', function () {
100100 // Drivers should test that withTransaction enforces a non-configurable timeout before retrying
101101 // both commits and entire transactions.
102102 //
103- // Note: We use CSOT's timeoutMS to enforce a short timeout instead of blocking for the full
104- // 120-second retry timeout, as recommended by the spec: "This might be done by internally
105- // modifying the timeout value used by withTransaction with some private API or using a mock timer."
103+ // We stub performance.now() to simulate elapsed time exceeding the 120-second retry limit,
104+ // as recommended by the spec: "This might be done by internally modifying the timeout value
105+ // used by withTransaction with some private API or using a mock timer."
106106 //
107107 // The error SHOULD be propagated as a timeout error if the language allows to expose the
108108 // underlying error as a cause of a timeout error.
109109
110110 let client : MongoClient ;
111111 let collection : Collection ;
112+ let timeOffset : number ;
112113
113114 beforeEach ( async function ( ) {
114- client = this . configuration . newClient ( { timeoutMS : 500 } ) ;
115+ client = this . configuration . newClient ( ) ;
115116 collection = client . db ( 'foo' ) . collection ( 'bar' ) ;
117+
118+ timeOffset = 0 ;
119+ const originalNow = performance . now . bind ( performance ) ;
120+ sinon . stub ( performance , 'now' ) . callsFake ( ( ) => originalNow ( ) + timeOffset ) ;
116121 } ) ;
117122
118123 afterEach ( async function ( ) {
124+ sinon . restore ( ) ;
119125 await clearFailPoint ( this . configuration ) ;
120126 await client ?. close ( ) ;
121127 } ) ;
@@ -131,23 +137,23 @@ describe('Retry Timeout is Enforced', function () {
131137 }
132138 } ,
133139 async function ( ) {
134- // 1. Configure a failpoint that always fails insert with TransientTransactionError.
140+ // 1. Configure a failpoint that fails insert with TransientTransactionError.
135141 await configureFailPoint ( this . configuration , {
136142 configureFailPoint : 'failCommand' ,
137- mode : 'alwaysOn' ,
143+ mode : { times : 1 } ,
138144 data : {
139145 failCommands : [ 'insert' ] ,
140146 errorCode : 24 ,
141147 errorLabels : [ 'TransientTransactionError' ]
142148 }
143149 } ) ;
144150
145- // 2. Run withTransaction with a callback that performs an insert.
146- // The insert will always fail with TransientTransactionError, triggering retries
147- // until the timeout (timeoutMS: 100) is exceeded at the backoff check.
151+ // 2. Run withTransaction. The callback advances the clock past the 120-second retry
152+ // limit before the insert fails, so the timeout is detected immediately.
148153 const { result } = await measureDuration ( ( ) => {
149154 return client . withSession ( async s => {
150155 await s . withTransaction ( async session => {
156+ timeOffset = 120_000 ;
151157 await collection . insertOne ( { } , { session } ) ;
152158 } ) ;
153159 } ) ;
@@ -171,31 +177,31 @@ describe('Retry Timeout is Enforced', function () {
171177 }
172178 } ,
173179 async function ( ) {
174- // 1. Configure a failpoint that always fails commitTransaction with
175- // UnknownTransactionCommitResult.
180+ // 1. Configure a failpoint that fails commitTransaction with UnknownTransactionCommitResult.
176181 await configureFailPoint ( this . configuration , {
177182 configureFailPoint : 'failCommand' ,
178- mode : 'alwaysOn' ,
183+ mode : { times : 1 } ,
179184 data : {
180185 failCommands : [ 'commitTransaction' ] ,
181186 errorCode : 64 ,
182187 errorLabels : [ 'UnknownTransactionCommitResult' ]
183188 }
184189 } ) ;
185190
186- // 2. Run withTransaction with a callback that performs an insert (succeeds).
187- // The commit will always fail with UnknownTransactionCommitResult, triggering commit
188- // retries until the timeout (timeoutMS: 100) is exceeded.
191+ // 2. Run withTransaction. The callback advances the clock past the 120-second retry
192+ // limit. The insert succeeds, but the commit fails and the timeout is detected.
189193 const { result } = await measureDuration ( ( ) => {
190194 return client . withSession ( async s => {
191195 await s . withTransaction ( async session => {
196+ timeOffset = 120_000 ;
192197 await collection . insertOne ( { } , { session } ) ;
193198 } ) ;
194199 } ) ;
195200 } ) ;
196201
197- // 3. Assert that the error is a timeout error.
202+ // 3. Assert that the error is a timeout error wrapping the commit error .
198203 expect ( result ) . to . be . instanceOf ( MongoOperationTimeoutError ) ;
204+ expect ( ( result as MongoOperationTimeoutError ) . cause ) . to . be . an ( 'error' ) ;
199205 }
200206 ) ;
201207
@@ -212,24 +218,24 @@ describe('Retry Timeout is Enforced', function () {
212218 }
213219 } ,
214220 async function ( ) {
215- // 1. Configure a failpoint that always fails commitTransaction with
216- // TransientTransactionError (errorCode 251 = NoSuchTransaction).
221+ // 1. Configure a failpoint that fails commitTransaction with TransientTransactionError
222+ // (errorCode 251 = NoSuchTransaction).
217223 await configureFailPoint ( this . configuration , {
218224 configureFailPoint : 'failCommand' ,
219- mode : 'alwaysOn' ,
225+ mode : { times : 1 } ,
220226 data : {
221227 failCommands : [ 'commitTransaction' ] ,
222228 errorCode : 251 ,
223229 errorLabels : [ 'TransientTransactionError' ]
224230 }
225231 } ) ;
226232
227- // 2. Run withTransaction with a callback that performs an insert (succeeds).
228- // The commit will always fail with TransientTransactionError, triggering full
229- // transaction retries until the timeout (timeoutMS: 100) is exceeded.
233+ // 2. Run withTransaction. The callback advances the clock past the 120-second retry
234+ // limit. The insert succeeds, but the commit fails and the timeout is detected.
230235 const { result } = await measureDuration ( ( ) => {
231236 return client . withSession ( async s => {
232237 await s . withTransaction ( async session => {
238+ timeOffset = 120_000 ;
233239 await collection . insertOne ( { } , { session } ) ;
234240 } ) ;
235241 } ) ;
0 commit comments