Skip to content

Commit 9fc4c4e

Browse files
committed
finish up int tests
1 parent 173796b commit 9fc4c4e

File tree

1 file changed

+125
-19
lines changed

1 file changed

+125
-19
lines changed

test/integration/client-side-operations-timeout/node_csot.test.ts

Lines changed: 125 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
/* Anything javascript specific relating to timeouts */
2+
import { setTimeout } from 'node:timers/promises';
3+
24
import { expect } from 'chai';
35
import * as semver from 'semver';
46
import * as sinon from 'sinon';
57

8+
import { type CommandSucceededEvent } from '../../../lib/cmap/command_monitoring_events';
69
import {
710
BSON,
811
type ClientSession,
@@ -326,13 +329,14 @@ describe('CSOT driver tests', { requires: { mongodb: '>=4.4' } }, () => {
326329
let client: MongoClient;
327330
let internalClient: MongoClient;
328331
let commandStarted: CommandStartedEvent[];
332+
let commandSucceeded: CommandSucceededEvent[];
329333
const failpoint: FailPoint = {
330334
configureFailPoint: 'failCommand',
331335
mode: 'alwaysOn',
332336
data: {
333-
failCommands: ['find'],
337+
failCommands: ['find', 'getMore'],
334338
blockConnection: true,
335-
blockTimeMS: 30
339+
blockTimeMS: 25
336340
}
337341
};
338342

@@ -350,9 +354,11 @@ describe('CSOT driver tests', { requires: { mongodb: '>=4.4' } }, () => {
350354

351355
await internalClient.db().admin().command(failpoint);
352356

353-
client = this.configuration.newClient(undefined, { timeoutMS: 20, monitorCommands: true });
357+
client = this.configuration.newClient(undefined, { monitorCommands: true });
354358
commandStarted = [];
359+
commandSucceeded = [];
355360
client.on('commandStarted', ev => commandStarted.push(ev));
361+
client.on('commandSucceeded', ev => commandSucceeded.push(ev));
356362
});
357363

358364
afterEach(async function () {
@@ -365,12 +371,12 @@ describe('CSOT driver tests', { requires: { mongodb: '>=4.4' } }, () => {
365371
});
366372

367373
context('ITERATION mode', () => {
368-
context('when executing a valid operation', () => {
374+
context('when executing an operation', () => {
369375
it('must apply the configured timeoutMS to the initial operation execution', async function () {
370376
const cursor = client
371377
.db('db')
372378
.collection('coll')
373-
.find({}, { batchSize: 3, timeoutMode: 'iteration' })
379+
.find({}, { batchSize: 3, timeoutMode: 'iteration', timeoutMS: 10 })
374380
.limit(3);
375381

376382
const maybeError = await cursor.next().then(
@@ -385,25 +391,26 @@ describe('CSOT driver tests', { requires: { mongodb: '>=4.4' } }, () => {
385391
const cursor = client
386392
.db('db')
387393
.collection('coll')
388-
.find({}, { batchSize: 1, timeoutMode: 'iteration' })
389-
.project({ _id: 0 })
390-
.limit(2);
394+
.find({}, { batchSize: 1, timeoutMode: 'iteration', timeoutMS: 50 })
395+
.project({ _id: 0 });
391396

392-
const firstDoc = await cursor.next();
393-
expect(firstDoc).to.deep.equal({ x: 1 });
397+
// Iterating over 3 documents in the collection, each artificially taking ~25 ms due to failpoint. If timeoutMS is not refreshed, then we'd expect to error
398+
for await (const doc of cursor) {
399+
expect(doc).to.deep.equal({ x: 1 });
400+
}
394401

395-
const maybeError = await cursor.next().then(
396-
() => null,
397-
e => e
398-
);
402+
const finds = commandSucceeded.filter(ev => ev.commandName === 'find');
403+
const getMores = commandSucceeded.filter(ev => ev.commandName === 'getMore');
399404

400-
expect(maybeError).to.be.instanceOf(MongoOperationTimeoutError);
405+
expect(finds).to.have.length(1); // Expecting 1 find
406+
expect(getMores).to.have.length(3); // Expecting 3 getMores (including final empty getMore)
401407
});
408+
402409
it('does not append a maxTimeMS to the original command or getMores', async function () {
403410
const cursor = client
404411
.db('db')
405412
.collection('coll')
406-
.find({}, { batchSize: 1, timeoutMode: 'iteration' })
413+
.find({}, { batchSize: 1, timeoutMode: 'iteration', timeoutMS: 100 })
407414
.project({ _id: 0 });
408415
await cursor.toArray();
409416

@@ -422,17 +429,116 @@ describe('CSOT driver tests', { requires: { mongodb: '>=4.4' } }, () => {
422429
});
423430

424431
context('LIFETIME mode', () => {
432+
let client: MongoClient;
433+
let internalClient: MongoClient;
434+
let commandStarted: CommandStartedEvent[];
435+
let commandSucceeded: CommandSucceededEvent[];
436+
const failpoint: FailPoint = {
437+
configureFailPoint: 'failCommand',
438+
mode: 'alwaysOn',
439+
data: {
440+
failCommands: ['find', 'getMore'],
441+
blockConnection: true,
442+
blockTimeMS: 25
443+
}
444+
};
445+
446+
beforeEach(async function () {
447+
internalClient = this.configuration.newClient(undefined);
448+
await internalClient.db('db').dropCollection('coll');
449+
await internalClient
450+
.db('db')
451+
.collection('coll')
452+
.insertMany(
453+
Array.from({ length: 3 }, () => {
454+
return { x: 1 };
455+
})
456+
);
457+
458+
await internalClient.db().admin().command(failpoint);
459+
460+
client = this.configuration.newClient(undefined, { monitorCommands: true });
461+
commandStarted = [];
462+
commandSucceeded = [];
463+
client.on('commandStarted', ev => commandStarted.push(ev));
464+
client.on('commandSucceeded', ev => commandSucceeded.push(ev));
465+
});
466+
467+
afterEach(async function () {
468+
await internalClient
469+
.db()
470+
.admin()
471+
.command({ ...failpoint, mode: 'off' });
472+
await internalClient.close();
473+
await client.close();
474+
});
425475
context('when executing a next call', () => {
426476
context(
427477
'when there are documents available from previously retrieved batch and timeout has expired',
428478
() => {
429-
it('returns documents without error');
479+
it('returns documents without error', async function () {
480+
const cursor = client
481+
.db('db')
482+
.collection('coll')
483+
.find({}, { timeoutMode: 'cursorLifetime', timeoutMS: 50 })
484+
.project({ _id: 0 });
485+
const doc = await cursor.next();
486+
expect(doc).to.deep.equal({ x: 1 });
487+
expect(cursor.documents.length).to.be.gt(0);
488+
489+
await setTimeout(50);
490+
491+
const docOrErr = await cursor.next().then(
492+
d => d,
493+
e => e
494+
);
495+
496+
expect(docOrErr).to.not.be.instanceOf(MongoOperationTimeoutError);
497+
expect(docOrErr).to.be.deep.equal({ x: 1 });
498+
});
430499
}
431500
);
432501
context('when a getMore is required and the timeout has expired', () => {
433-
it('throws a MongoOperationTimeoutError');
502+
it('throws a MongoOperationTimeoutError', async function () {
503+
const cursor = client
504+
.db('db')
505+
.collection('coll')
506+
.find({}, { batchSize: 1, timeoutMode: 'cursorLifetime', timeoutMS: 50 })
507+
.project({ _id: 0 });
508+
const doc = await cursor.next();
509+
expect(doc).to.deep.equal({ x: 1 });
510+
expect(cursor.documents.length).to.equal(0);
511+
512+
await setTimeout(50);
513+
514+
const docOrErr = await cursor.next().then(
515+
d => d,
516+
e => e
517+
);
518+
519+
expect(docOrErr).to.be.instanceOf(MongoOperationTimeoutError);
520+
});
521+
});
522+
523+
it('does not apply maxTimeMS to a getMore', async function () {
524+
const cursor = client
525+
.db('db')
526+
.collection('coll')
527+
.find({}, { batchSize: 1, timeoutMode: 'cursorLifetime', timeoutMS: 1000 })
528+
.project({ _id: 0 });
529+
for await (const _doc of cursor) {
530+
// Ignore _doc
531+
}
532+
533+
const getMores = commandStarted
534+
.filter(ev => ev.command.getMore != null)
535+
.map(ev => ev.command);
536+
expect(getMores.length).to.be.gt(0);
537+
538+
for (const getMore of getMores) {
539+
expect(getMore.maxTimeMS).to.not.exist;
540+
}
434541
});
435-
it('does not apply maxTimeMS to a getMore');
436542
});
437543
});
438544
});

0 commit comments

Comments
 (0)