1
- /* eslint-disable @typescript-eslint/no-unused-vars */
2
1
/* eslint-disable @typescript-eslint/no-non-null-assertion */
3
2
4
3
import { Readable } from 'node:stream' ;
@@ -7,16 +6,13 @@ import { pipeline } from 'node:stream/promises';
7
6
import { AssertionError , expect } from 'chai' ;
8
7
9
8
import {
10
- AbstractCursor ,
11
9
type ChangeStream ,
12
10
Collection ,
13
11
CommandStartedEvent ,
14
12
Db ,
15
13
type Document ,
16
- type GridFSFile ,
17
14
type MongoClient ,
18
15
MongoError ,
19
- type ObjectId ,
20
16
ReadConcern ,
21
17
ReadPreference ,
22
18
SERVER_DESCRIPTION_CHANGED ,
@@ -25,7 +21,7 @@ import {
25
21
type TopologyType ,
26
22
WriteConcern
27
23
} from '../../mongodb' ;
28
- import { getSymbolFrom , sleep } from '../../tools/utils' ;
24
+ import { sleep } from '../../tools/utils' ;
29
25
import { type TestConfiguration } from '../runner/config' ;
30
26
import { EntitiesMap } from './entities' ;
31
27
import { expectErrorCheck , resultCheck } from './match' ;
@@ -44,11 +40,9 @@ type RunOperationFn = (
44
40
) => Promise < Document | boolean | number | null | void | string > ;
45
41
export const operations = new Map < string , RunOperationFn > ( ) ;
46
42
47
- export class MalformedOperationError extends AssertionError { }
48
-
49
43
operations . set ( 'createEntities' , async ( { entities, operation, testConfig } ) => {
50
44
if ( ! operation . arguments ?. entities ) {
51
- throw new AssertionError ( 'encountered createEntities operation without entities argument' ) ;
45
+ expect . fail ( 'encountered createEntities operation without entities argument' ) ;
52
46
}
53
47
await EntitiesMap . createEntities ( testConfig , null , operation . arguments . entities ! , entities ) ;
54
48
} ) ;
@@ -61,7 +55,7 @@ operations.set('abortTransaction', async ({ entities, operation }) => {
61
55
operations . set ( 'aggregate' , async ( { entities, operation } ) => {
62
56
const dbOrCollection = entities . get ( operation . object ) as Db | Collection ;
63
57
if ( ! ( dbOrCollection instanceof Db || dbOrCollection instanceof Collection ) ) {
64
- throw new AssertionError ( `Operation object '${ operation . object } ' must be a db or collection` ) ;
58
+ expect . fail ( `Operation object '${ operation . object } ' must be a db or collection` ) ;
65
59
}
66
60
const { pipeline, ...opts } = operation . arguments ! ;
67
61
const cursor = dbOrCollection . aggregate ( pipeline , opts ) ;
@@ -153,27 +147,27 @@ operations.set('assertSameLsidOnLastTwoCommands', async ({ entities, operation }
153
147
expect ( last . command . lsid . id . buffer . equals ( secondLast . command . lsid . id . buffer ) ) . to . be . true ;
154
148
} ) ;
155
149
156
- operations . set ( 'assertSessionDirty' , async ( { entities , operation } ) => {
150
+ operations . set ( 'assertSessionDirty' , async ( { operation } ) => {
157
151
const session = operation . arguments ! . session ;
158
152
expect ( session . serverSession . isDirty ) . to . be . true ;
159
153
} ) ;
160
154
161
- operations . set ( 'assertSessionNotDirty' , async ( { entities , operation } ) => {
155
+ operations . set ( 'assertSessionNotDirty' , async ( { operation } ) => {
162
156
const session = operation . arguments ! . session ;
163
157
expect ( session . serverSession . isDirty ) . to . be . false ;
164
158
} ) ;
165
159
166
- operations . set ( 'assertSessionPinned' , async ( { entities , operation } ) => {
160
+ operations . set ( 'assertSessionPinned' , async ( { operation } ) => {
167
161
const session = operation . arguments ! . session ;
168
162
expect ( session . isPinned , 'session should be pinned' ) . to . be . true ;
169
163
} ) ;
170
164
171
- operations . set ( 'assertSessionUnpinned' , async ( { entities , operation } ) => {
165
+ operations . set ( 'assertSessionUnpinned' , async ( { operation } ) => {
172
166
const session = operation . arguments ! . session ;
173
167
expect ( session . isPinned , 'session should be unpinned' ) . to . be . false ;
174
168
} ) ;
175
169
176
- operations . set ( 'assertSessionTransactionState' , async ( { entities , operation } ) => {
170
+ operations . set ( 'assertSessionTransactionState' , async ( { operation } ) => {
177
171
const session = operation . arguments ! . session ;
178
172
179
173
const transactionStateTranslation = {
@@ -230,7 +224,7 @@ operations.set('close', async ({ entities, operation }) => {
230
224
} catch { }
231
225
/* eslint-enable no-empty */
232
226
233
- throw new AssertionError ( `No closable entity with key ${ operation . object } ` ) ;
227
+ expect . fail ( `No closable entity with key ${ operation . object } ` ) ;
234
228
} ) ;
235
229
236
230
operations . set ( 'commitTransaction' , async ( { entities, operation } ) => {
@@ -240,8 +234,8 @@ operations.set('commitTransaction', async ({ entities, operation }) => {
240
234
241
235
operations . set ( 'createChangeStream' , async ( { entities, operation } ) => {
242
236
const watchable = entities . get ( operation . object ) ;
243
- if ( watchable == null || ! ( 'watch' in watchable ) ) {
244
- throw new AssertionError ( `Entity ${ operation . object } must be watchable` ) ;
237
+ if ( watchable == null || typeof watchable !== 'object' || ! ( 'watch' in watchable ) ) {
238
+ expect . fail ( `Entity ${ operation . object } must be watchable` ) ;
245
239
}
246
240
247
241
const { pipeline, ...args } = operation . arguments ! ;
@@ -292,7 +286,7 @@ operations.set('dropCollection', async ({ entities, operation }) => {
292
286
293
287
// TODO(NODE-4243): dropCollection should suppress namespace not found errors
294
288
try {
295
- return await db . dropCollection ( collection , opts ) ;
289
+ await db . dropCollection ( collection , opts ) ;
296
290
} catch ( err ) {
297
291
if ( ! / n s n o t f o u n d / . test ( err . message ) ) {
298
292
throw err ;
@@ -359,7 +353,7 @@ operations.set('insertOne', async ({ entities, operation }) => {
359
353
const collection = entities . getEntity ( 'collection' , operation . object ) ;
360
354
const { document, ...opts } = operation . arguments ! ;
361
355
if ( ! document ) {
362
- throw new MalformedOperationError ( 'No document defined in the arguments for insertOne' ) ;
356
+ expect . fail ( 'No document defined in the arguments for insertOne' ) ;
363
357
}
364
358
// Looping exposes the fact that we can generate _ids for inserted
365
359
// documents and we don't want the original operation to get modified
@@ -544,10 +538,10 @@ operations.set('upload', async ({ entities, operation }) => {
544
538
const bucket = entities . getEntity ( 'bucket' , operation . object ) ;
545
539
const { filename, source, ...options } = operation . arguments ?? { } ;
546
540
547
- const stream = bucket . openUploadStream ( operation . arguments ! . filename , options ) ;
548
- const filestream = Readable . from ( Buffer . from ( operation . arguments ! . source . $$hexBytes , 'hex' ) ) ;
541
+ const stream = bucket . openUploadStream ( filename , options ) ;
542
+ const fileStream = Readable . from ( Buffer . from ( source . $$hexBytes , 'hex' ) ) ;
549
543
550
- await pipeline ( filestream , stream ) ;
544
+ await pipeline ( fileStream , stream ) ;
551
545
return stream . gridFSFile ?. _id ;
552
546
} ) ;
553
547
@@ -586,13 +580,11 @@ operations.set('waitForEvent', async ({ entities, operation }) => {
586
580
await Promise . race ( [
587
581
eventPromise ,
588
582
sleep ( 10000 ) . then ( ( ) =>
589
- Promise . reject (
590
- new AssertionError (
591
- `Timed out waiting for ${ eventName } ; captured [${ mongoClient
592
- . getCapturedEvents ( 'all' )
593
- . map ( e => e . constructor . name )
594
- . join ( ', ' ) } ]`
595
- )
583
+ expect . fail (
584
+ `Timed out waiting for ${ eventName } ; captured [${ mongoClient
585
+ . getCapturedEvents ( 'all' )
586
+ . map ( e => e . constructor . name )
587
+ . join ( ', ' ) } ]`
596
588
)
597
589
)
598
590
] ) ;
@@ -682,7 +674,7 @@ operations.set('waitForPrimaryChange', async ({ entities, operation }) => {
682
674
await Promise . race ( [
683
675
newPrimaryPromise ,
684
676
sleep ( timeoutMS ?? 10000 ) . then ( ( ) =>
685
- Promise . reject ( new AssertionError ( `Timed out waiting for primary change on ${ client } ` ) )
677
+ expect . fail ( `Timed out waiting for primary change on ${ client } ` )
686
678
)
687
679
] ) ;
688
680
} ) ;
@@ -702,9 +694,7 @@ operations.set('waitForThread', async ({ entities, operation }) => {
702
694
const thread = entities . getEntity ( 'thread' , threadId , true ) ;
703
695
await Promise . race ( [
704
696
thread . finish ( ) ,
705
- sleep ( 10000 ) . then ( ( ) =>
706
- Promise . reject ( new AssertionError ( `Timed out waiting for thread: ${ threadId } ` ) )
707
- )
697
+ sleep ( 10000 ) . then ( ( ) => expect . fail ( `Timed out waiting for thread: ${ threadId } ` ) )
708
698
] ) ;
709
699
} ) ;
710
700
@@ -720,7 +710,7 @@ operations.set('withTransaction', async ({ entities, operation, client, testConf
720
710
721
711
await session . withTransaction ( async ( ) => {
722
712
for ( const callbackOperation of operation . arguments ! . callback ) {
723
- await executeOperationAndCheck ( callbackOperation , entities , client , testConfig ) ;
713
+ await executeOperationAndCheck ( callbackOperation , entities , client , testConfig , true ) ;
724
714
}
725
715
} , options ) ;
726
716
} ) ;
@@ -757,11 +747,10 @@ operations.set('estimatedDocumentCount', async ({ entities, operation }) => {
757
747
operations . set ( 'runCommand' , async ( { entities, operation } : OperationFunctionParams ) => {
758
748
const db = entities . getEntity ( 'db' , operation . object ) ;
759
749
760
- if ( operation . arguments ?. command == null )
761
- throw new AssertionError ( 'runCommand requires a command' ) ;
750
+ if ( operation . arguments ?. command == null ) expect . fail ( 'runCommand requires a command' ) ;
762
751
const { command } = operation . arguments ;
763
752
764
- if ( operation . arguments . timeoutMS != null ) throw new AssertionError ( 'timeoutMS not supported' ) ;
753
+ if ( operation . arguments . timeoutMS != null ) expect . fail ( 'timeoutMS not supported' ) ;
765
754
766
755
const options = {
767
756
readPreference : operation . arguments . readPreference ,
@@ -931,14 +920,14 @@ operations.set('modifyCollection', async ({ entities, operation }) => {
931
920
return db . command ( command , { } ) ;
932
921
} ) ;
933
922
934
- export async function executeOperationAndCheck (
923
+ export async function executeTestOperation (
935
924
operation : OperationDescription ,
936
925
entities : EntitiesMap ,
937
926
client : MongoClient ,
938
927
testConfig : TestConfiguration
939
- ) : Promise < void > {
928
+ ) : Promise < { result : unknown ; success : true } | { result : Error ; success : false } > {
940
929
const opFunc = operations . get ( operation . name ) ;
941
- expect ( opFunc , `Unknown operation: ${ operation . name } ` ) . to . exist ;
930
+ if ( opFunc == null ) expect . fail ( `Unknown operation: ${ operation . name } ` ) ;
942
931
943
932
if ( operation . arguments && operation . arguments . session ) {
944
933
// The session could need to be either pulled from the entity map or in the case where
@@ -949,33 +938,36 @@ export async function executeOperationAndCheck(
949
938
}
950
939
}
951
940
952
- let result ;
953
-
954
941
try {
955
- result = await opFunc ! ( { entities, operation, client, testConfig } ) ;
956
- } catch ( error ) {
957
- if ( operation . expectError ) {
958
- expectErrorCheck ( error , operation . expectError , entities ) ;
959
- return ;
960
- } else if ( ! operation . ignoreResultAndError || error instanceof MalformedOperationError ) {
961
- throw error ;
962
- }
942
+ const result = await opFunc ( { entities, operation, client, testConfig } ) ;
943
+ return { result, success : true } ;
944
+ } catch ( result ) {
945
+ return { result, success : false } ;
963
946
}
947
+ }
964
948
965
- // We check the positive outcome here so the try-catch above doesn't catch our chai assertions
966
- if ( operation . ignoreResultAndError ) {
967
- return ;
968
- }
949
+ export async function executeOperationAndCheck (
950
+ operation : OperationDescription ,
951
+ entities : EntitiesMap ,
952
+ client : MongoClient ,
953
+ testConfig : TestConfiguration ,
954
+ rethrow = false
955
+ ) : Promise < void > {
956
+ const outcome = await executeTestOperation ( operation , entities , client , testConfig ) ;
957
+
958
+ if ( operation . saveResultAsEntity ) entities . set ( operation . saveResultAsEntity , outcome . result ) ;
959
+
960
+ if ( operation . ignoreResultAndError ) return ;
969
961
970
962
if ( operation . expectError ) {
971
- expect . fail ( `Operation ${ operation . name } succeeded but was not supposed to` ) ;
963
+ if ( outcome . success === true ) expect . fail ( `${ operation . name } unexpectedly succeeded` ) ;
964
+ expectErrorCheck ( outcome . result , operation . expectError , entities ) ;
965
+ if ( rethrow ) throw outcome . result ;
966
+ return ;
972
967
}
973
968
974
969
if ( operation . expectResult ) {
975
- resultCheck ( result , operation . expectResult as any , entities ) ;
976
- }
977
-
978
- if ( operation . saveResultAsEntity ) {
979
- entities . set ( operation . saveResultAsEntity , result ) ;
970
+ if ( outcome . success === false ) expect . fail ( `${ operation . name } unexpectedly failed` ) ;
971
+ return resultCheck ( outcome . result , operation . expectResult as any , entities ) ;
980
972
}
981
973
}
0 commit comments