1- /* eslint-disable @typescript-eslint/no-unused-vars */
21/* eslint-disable @typescript-eslint/no-non-null-assertion */
32
43import { Readable } from 'node:stream' ;
@@ -7,16 +6,13 @@ import { pipeline } from 'node:stream/promises';
76import { AssertionError , expect } from 'chai' ;
87
98import {
10- AbstractCursor ,
119 type ChangeStream ,
1210 Collection ,
1311 CommandStartedEvent ,
1412 Db ,
1513 type Document ,
16- type GridFSFile ,
1714 type MongoClient ,
1815 MongoError ,
19- type ObjectId ,
2016 ReadConcern ,
2117 ReadPreference ,
2218 SERVER_DESCRIPTION_CHANGED ,
@@ -25,7 +21,7 @@ import {
2521 type TopologyType ,
2622 WriteConcern
2723} from '../../mongodb' ;
28- import { getSymbolFrom , sleep } from '../../tools/utils' ;
24+ import { sleep } from '../../tools/utils' ;
2925import { type TestConfiguration } from '../runner/config' ;
3026import { EntitiesMap } from './entities' ;
3127import { expectErrorCheck , resultCheck } from './match' ;
@@ -44,11 +40,9 @@ type RunOperationFn = (
4440) => Promise < Document | boolean | number | null | void | string > ;
4541export const operations = new Map < string , RunOperationFn > ( ) ;
4642
47- export class MalformedOperationError extends AssertionError { }
48-
4943operations . set ( 'createEntities' , async ( { entities, operation, testConfig } ) => {
5044 if ( ! operation . arguments ?. entities ) {
51- throw new AssertionError ( 'encountered createEntities operation without entities argument' ) ;
45+ expect . fail ( 'encountered createEntities operation without entities argument' ) ;
5246 }
5347 await EntitiesMap . createEntities ( testConfig , null , operation . arguments . entities ! , entities ) ;
5448} ) ;
@@ -61,7 +55,7 @@ operations.set('abortTransaction', async ({ entities, operation }) => {
6155operations . set ( 'aggregate' , async ( { entities, operation } ) => {
6256 const dbOrCollection = entities . get ( operation . object ) as Db | Collection ;
6357 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` ) ;
6559 }
6660 const { pipeline, ...opts } = operation . arguments ! ;
6761 const cursor = dbOrCollection . aggregate ( pipeline , opts ) ;
@@ -153,27 +147,27 @@ operations.set('assertSameLsidOnLastTwoCommands', async ({ entities, operation }
153147 expect ( last . command . lsid . id . buffer . equals ( secondLast . command . lsid . id . buffer ) ) . to . be . true ;
154148} ) ;
155149
156- operations . set ( 'assertSessionDirty' , async ( { entities , operation } ) => {
150+ operations . set ( 'assertSessionDirty' , async ( { operation } ) => {
157151 const session = operation . arguments ! . session ;
158152 expect ( session . serverSession . isDirty ) . to . be . true ;
159153} ) ;
160154
161- operations . set ( 'assertSessionNotDirty' , async ( { entities , operation } ) => {
155+ operations . set ( 'assertSessionNotDirty' , async ( { operation } ) => {
162156 const session = operation . arguments ! . session ;
163157 expect ( session . serverSession . isDirty ) . to . be . false ;
164158} ) ;
165159
166- operations . set ( 'assertSessionPinned' , async ( { entities , operation } ) => {
160+ operations . set ( 'assertSessionPinned' , async ( { operation } ) => {
167161 const session = operation . arguments ! . session ;
168162 expect ( session . isPinned , 'session should be pinned' ) . to . be . true ;
169163} ) ;
170164
171- operations . set ( 'assertSessionUnpinned' , async ( { entities , operation } ) => {
165+ operations . set ( 'assertSessionUnpinned' , async ( { operation } ) => {
172166 const session = operation . arguments ! . session ;
173167 expect ( session . isPinned , 'session should be unpinned' ) . to . be . false ;
174168} ) ;
175169
176- operations . set ( 'assertSessionTransactionState' , async ( { entities , operation } ) => {
170+ operations . set ( 'assertSessionTransactionState' , async ( { operation } ) => {
177171 const session = operation . arguments ! . session ;
178172
179173 const transactionStateTranslation = {
@@ -230,7 +224,7 @@ operations.set('close', async ({ entities, operation }) => {
230224 } catch { }
231225 /* eslint-enable no-empty */
232226
233- throw new AssertionError ( `No closable entity with key ${ operation . object } ` ) ;
227+ expect . fail ( `No closable entity with key ${ operation . object } ` ) ;
234228} ) ;
235229
236230operations . set ( 'commitTransaction' , async ( { entities, operation } ) => {
@@ -240,8 +234,8 @@ operations.set('commitTransaction', async ({ entities, operation }) => {
240234
241235operations . set ( 'createChangeStream' , async ( { entities, operation } ) => {
242236 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` ) ;
245239 }
246240
247241 const { pipeline, ...args } = operation . arguments ! ;
@@ -292,7 +286,7 @@ operations.set('dropCollection', async ({ entities, operation }) => {
292286
293287 // TODO(NODE-4243): dropCollection should suppress namespace not found errors
294288 try {
295- return await db . dropCollection ( collection , opts ) ;
289+ await db . dropCollection ( collection , opts ) ;
296290 } catch ( err ) {
297291 if ( ! / n s n o t f o u n d / . test ( err . message ) ) {
298292 throw err ;
@@ -359,7 +353,7 @@ operations.set('insertOne', async ({ entities, operation }) => {
359353 const collection = entities . getEntity ( 'collection' , operation . object ) ;
360354 const { document, ...opts } = operation . arguments ! ;
361355 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' ) ;
363357 }
364358 // Looping exposes the fact that we can generate _ids for inserted
365359 // documents and we don't want the original operation to get modified
@@ -544,10 +538,10 @@ operations.set('upload', async ({ entities, operation }) => {
544538 const bucket = entities . getEntity ( 'bucket' , operation . object ) ;
545539 const { filename, source, ...options } = operation . arguments ?? { } ;
546540
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' ) ) ;
549543
550- await pipeline ( filestream , stream ) ;
544+ await pipeline ( fileStream , stream ) ;
551545 return stream . gridFSFile ?. _id ;
552546} ) ;
553547
@@ -586,13 +580,11 @@ operations.set('waitForEvent', async ({ entities, operation }) => {
586580 await Promise . race ( [
587581 eventPromise ,
588582 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 ( ', ' ) } ]`
596588 )
597589 )
598590 ] ) ;
@@ -682,7 +674,7 @@ operations.set('waitForPrimaryChange', async ({ entities, operation }) => {
682674 await Promise . race ( [
683675 newPrimaryPromise ,
684676 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 } ` )
686678 )
687679 ] ) ;
688680} ) ;
@@ -702,9 +694,7 @@ operations.set('waitForThread', async ({ entities, operation }) => {
702694 const thread = entities . getEntity ( 'thread' , threadId , true ) ;
703695 await Promise . race ( [
704696 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 } ` ) )
708698 ] ) ;
709699} ) ;
710700
@@ -720,7 +710,7 @@ operations.set('withTransaction', async ({ entities, operation, client, testConf
720710
721711 await session . withTransaction ( async ( ) => {
722712 for ( const callbackOperation of operation . arguments ! . callback ) {
723- await executeOperationAndCheck ( callbackOperation , entities , client , testConfig ) ;
713+ await executeOperationAndCheck ( callbackOperation , entities , client , testConfig , true ) ;
724714 }
725715 } , options ) ;
726716} ) ;
@@ -757,11 +747,10 @@ operations.set('estimatedDocumentCount', async ({ entities, operation }) => {
757747operations . set ( 'runCommand' , async ( { entities, operation } : OperationFunctionParams ) => {
758748 const db = entities . getEntity ( 'db' , operation . object ) ;
759749
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' ) ;
762751 const { command } = operation . arguments ;
763752
764- if ( operation . arguments . timeoutMS != null ) throw new AssertionError ( 'timeoutMS not supported' ) ;
753+ if ( operation . arguments . timeoutMS != null ) expect . fail ( 'timeoutMS not supported' ) ;
765754
766755 const options = {
767756 readPreference : operation . arguments . readPreference ,
@@ -931,14 +920,14 @@ operations.set('modifyCollection', async ({ entities, operation }) => {
931920 return db . command ( command , { } ) ;
932921} ) ;
933922
934- export async function executeOperationAndCheck (
923+ export async function executeTestOperation (
935924 operation : OperationDescription ,
936925 entities : EntitiesMap ,
937926 client : MongoClient ,
938927 testConfig : TestConfiguration
939- ) : Promise < void > {
928+ ) : Promise < { result : unknown ; success : true } | { result : Error ; success : false } > {
940929 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 } ` ) ;
942931
943932 if ( operation . arguments && operation . arguments . session ) {
944933 // 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(
949938 }
950939 }
951940
952- let result ;
953-
954941 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 } ;
963946 }
947+ }
964948
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 ;
969961
970962 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 ;
972967 }
973968
974969 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 ) ;
980972 }
981973}
0 commit comments