Skip to content

Commit 8ff54e2

Browse files
tests + utils
1 parent 7ce6a8c commit 8ff54e2

File tree

2 files changed

+80
-38
lines changed

2 files changed

+80
-38
lines changed

test/integration/crud/explain.test.ts

Lines changed: 61 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,10 @@ import {
88
type Document,
99
type MongoClient,
1010
MongoOperationTimeoutError,
11-
MongoServerError
11+
MongoServerError,
12+
now
1213
} from '../../mongodb';
13-
import { clearFailPoint, configureFailPoint } from '../../tools/utils';
14+
import { clearFailPoint, configureFailPoint, measureDuration } from '../../tools/utils';
1415
import { filterForCommands } from '../shared';
1516

1617
const explain = [true, false, 'queryPlanner', 'allPlansExecution', 'executionStats', 'invalid'];
@@ -334,10 +335,14 @@ describe('CRUD API explain option', function () {
334335

335336
describe('when a cursor api is being explained', function () {
336337
describe('when timeoutMS is provided', function () {
337-
test('the explain command respects timeoutMS', async function () {
338+
test('the explain command times out after timeoutMS', async function () {
338339
const cursor = client.db('foo').collection('bar').find({}, { timeoutMS: 1000 });
339-
const timeout = await cursor.explain({ verbosity: 'queryPlanner' }).catch(e => e);
340-
expect(timeout).to.be.instanceOf(MongoOperationTimeoutError);
340+
const { duration, result } = await measureDuration(() =>
341+
cursor.explain({ verbosity: 'queryPlanner' }).catch(e => e)
342+
);
343+
344+
expect(result).to.be.instanceOf(MongoOperationTimeoutError);
345+
expect(duration).to.be.within(1000, 1000 + 100);
341346
});
342347

343348
test('the explain command has the calculated maxTimeMS value attached', async function () {
@@ -384,19 +389,23 @@ describe('CRUD API explain option', function () {
384389

385390
describe('when a non-cursor api is being explained', function () {
386391
describe('when timeoutMS is provided', function () {
387-
test('the explain command respects timeoutMS', async function () {
388-
const timeout = await client
389-
.db('foo')
390-
.collection('bar')
391-
.deleteMany(
392-
{},
393-
{
394-
timeoutMS: 1000,
395-
explain: { verbosity: 'queryPlanner' }
396-
}
397-
)
398-
.catch(e => e);
399-
expect(timeout).to.be.instanceOf(MongoOperationTimeoutError);
392+
test('the explain command times out after timeoutMS', async function () {
393+
const { duration, result } = await measureDuration(() =>
394+
client
395+
.db('foo')
396+
.collection('bar')
397+
.deleteMany(
398+
{},
399+
{
400+
timeoutMS: 1000,
401+
explain: { verbosity: 'queryPlanner' }
402+
}
403+
)
404+
.catch(e => e)
405+
);
406+
407+
expect(result).to.be.instanceOf(MongoOperationTimeoutError);
408+
expect(duration).to.be.within(1000, 1000 + 100);
400409
});
401410

402411
test('the explain command has the calculated maxTimeMS value attached', async function () {
@@ -512,56 +521,70 @@ describe('CRUD API explain option', function () {
512521
describe('find({}, { timeoutMS }).explain()', function () {
513522
test('respects the timeoutMS from the find options', async function () {
514523
const cursor = client.db('foo').collection('bar').find({}, { timeoutMS: 1000 });
515-
const timeout = await cursor.explain({ verbosity: 'queryPlanner' }).catch(e => e);
516-
expect(timeout).to.be.instanceOf(MongoOperationTimeoutError);
524+
525+
const { result, duration } = await measureDuration(() =>
526+
cursor.explain({ verbosity: 'queryPlanner' }).catch(e => e)
527+
);
528+
expect(result).to.be.instanceOf(MongoOperationTimeoutError);
529+
expect(duration).to.be.within(1000, 1000 + 100);
517530
});
518531
});
519532

520533
describe('find().explain({}, { timeoutMS })', function () {
521534
test('respects the timeoutMS from the explain helper', async function () {
522535
const cursor = client.db('foo').collection('bar').find();
523-
const timeout = await cursor
524-
.explain({ verbosity: 'queryPlanner' }, { timeoutMS: 1000 })
525-
.catch(e => e);
526-
expect(timeout).to.be.instanceOf(MongoOperationTimeoutError);
536+
537+
const { result, duration } = await measureDuration(() =>
538+
cursor.explain({ verbosity: 'queryPlanner' }, { timeoutMS: 1000 }).catch(e => e)
539+
);
540+
expect(result).to.be.instanceOf(MongoOperationTimeoutError);
541+
expect(duration).to.be.within(1000, 1000 + 100);
527542
});
528543
});
529544

530545
describe('find({}, { timeoutMS} ).explain({}, { timeoutMS })', function () {
531546
test('the timeoutMS from the explain helper has precedence', async function () {
532547
const cursor = client.db('foo').collection('bar').find({}, { timeoutMS: 2000 });
533-
const timeout = await cursor
534-
.explain({ verbosity: 'queryPlanner' }, { timeoutMS: 1000 })
535-
.catch(e => e);
536-
expect(timeout).to.be.instanceOf(MongoOperationTimeoutError);
548+
const { result, duration } = await measureDuration(() =>
549+
cursor.explain({ verbosity: 'queryPlanner' }, { timeoutMS: 1000 }).catch(e => e)
550+
);
551+
expect(result).to.be.instanceOf(MongoOperationTimeoutError);
552+
expect(duration).to.be.within(1000, 1000 + 100);
537553
});
538554
});
539555

540556
describe('aggregate([], { timeoutMS }).explain()', function () {
541557
test('respects the timeoutMS from the find options', async function () {
542558
const cursor = client.db('foo').collection('bar').aggregate([], { timeoutMS: 1000 });
543-
const timeout = await cursor.explain({ verbosity: 'queryPlanner' }).catch(e => e);
544-
expect(timeout).to.be.instanceOf(MongoOperationTimeoutError);
559+
const { result, duration } = await measureDuration(() =>
560+
cursor.explain({ verbosity: 'queryPlanner' }).catch(e => e)
561+
);
562+
563+
expect(result).to.be.instanceOf(MongoOperationTimeoutError);
564+
expect(duration).to.be.within(1000, 1000 + 100);
545565
});
546566
});
547567

548568
describe('aggregate([], { timeoutMS })', function () {
549569
test('respects the timeoutMS from the explain helper', async function () {
550570
const cursor = client.db('foo').collection('bar').aggregate();
551-
const timeout = await cursor
552-
.explain({ verbosity: 'queryPlanner' }, { timeoutMS: 1000 })
553-
.catch(e => e);
554-
expect(timeout).to.be.instanceOf(MongoOperationTimeoutError);
571+
const { result, duration } = await measureDuration(() =>
572+
cursor.explain({ verbosity: 'queryPlanner' }, { timeoutMS: 1000 }).catch(e => e)
573+
);
574+
575+
expect(result).to.be.instanceOf(MongoOperationTimeoutError);
576+
expect(duration).to.be.within(1000, 1000 + 100);
555577
});
556578
});
557579

558580
describe('aggregate([], { timeoutMS} ).explain({}, { timeoutMS })', function () {
559581
test('the timeoutMS from the explain helper has precedence', async function () {
560582
const cursor = client.db('foo').collection('bar').aggregate([], { timeoutMS: 2000 });
561-
const timeout = await cursor
562-
.explain({ verbosity: 'queryPlanner' }, { timeoutMS: 1000 })
563-
.catch(e => e);
564-
expect(timeout).to.be.instanceOf(MongoOperationTimeoutError);
583+
const { duration, result } = await measureDuration(() =>
584+
cursor.explain({ verbosity: 'queryPlanner' }, { timeoutMS: 1000 }).catch(e => e)
585+
);
586+
expect(duration).to.be.within(1000, 1000 + 100);
587+
expect(result).to.be.instanceOf(MongoOperationTimeoutError);
565588
});
566589
});
567590
});

test/tools/utils.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {
1414
type Document,
1515
type HostAddress,
1616
MongoClient,
17+
now,
1718
OP_MSG,
1819
Topology,
1920
type TopologyOptions
@@ -624,3 +625,21 @@ export async function clearFailPoint(configuration: TestConfiguration) {
624625
await utilClient.close();
625626
}
626627
}
628+
629+
/**
630+
* A utility to measure the duration of an async function. This is intended to be used for CSOT
631+
* testing, where we expect to timeout within a certain threshold and want to measure the duration
632+
* of that operation.
633+
*/
634+
export async function measureDuration<T>(f: () => Promise<T>): Promise<{
635+
duration: number;
636+
result: T | Error;
637+
}> {
638+
const start = now();
639+
const result = await f().catch(e => e);
640+
const end = now();
641+
return {
642+
duration: end - start,
643+
result
644+
};
645+
}

0 commit comments

Comments
 (0)