@@ -894,4 +894,153 @@ describe('CSOT driver tests', metadata, () => {
894
894
} ) ;
895
895
} ) ;
896
896
} ) ;
897
+
898
+ describe ( 'when using an explicit session' , ( ) => {
899
+ const metadata : MongoDBMetadataUI = {
900
+ requires : { topology : [ 'replicaset' ] , mongodb : '>=4.4' }
901
+ } ;
902
+
903
+ describe ( 'created for a withTransaction callback' , ( ) => {
904
+ describe ( 'passing a timeoutMS and a session with a timeoutContext' , ( ) => {
905
+ let client : MongoClient ;
906
+
907
+ beforeEach ( async function ( ) {
908
+ client = this . configuration . newClient ( { timeoutMS : 123 } ) ;
909
+ } ) ;
910
+
911
+ afterEach ( async function ( ) {
912
+ await client . close ( ) ;
913
+ } ) ;
914
+
915
+ it ( 'throws a validation error from the operation' , metadata , async ( ) => {
916
+ // Drivers MUST raise a validation error if an explicit session with a timeout is used and
917
+ // the timeoutMS option is set at the operation level for operations executed as part of a withTransaction callback.
918
+
919
+ const coll = client . db ( 'db' ) . collection ( 'coll' ) ;
920
+
921
+ const session = client . startSession ( ) ;
922
+
923
+ let insertError : Error | null = null ;
924
+ const withTransactionError = await session
925
+ . withTransaction ( async session => {
926
+ insertError = await coll
927
+ . insertOne ( { x : 1 } , { session, timeoutMS : 1234 } )
928
+ . catch ( error => error ) ;
929
+ throw insertError ;
930
+ } )
931
+ . catch ( error => error ) ;
932
+
933
+ expect ( insertError ) . to . be . instanceOf ( MongoInvalidArgumentError ) ;
934
+ expect ( withTransactionError ) . to . be . instanceOf ( MongoInvalidArgumentError ) ;
935
+ } ) ;
936
+ } ) ;
937
+ } ) ;
938
+
939
+ describe ( 'created manually' , ( ) => {
940
+ describe ( 'passing a timeoutMS and a session with an inherited timeoutMS' , ( ) => {
941
+ let client : MongoClient ;
942
+
943
+ beforeEach ( async function ( ) {
944
+ client = this . configuration . newClient ( { timeoutMS : 123 } ) ;
945
+ } ) ;
946
+
947
+ afterEach ( async function ( ) {
948
+ await client . close ( ) ;
949
+ } ) ;
950
+
951
+ it ( 'does not throw a validation error' , metadata , async ( ) => {
952
+ const coll = client . db ( 'db' ) . collection ( 'coll' ) ;
953
+ const session = client . startSession ( ) ;
954
+ session . startTransaction ( ) ;
955
+ await coll . insertOne ( { x : 1 } , { session, timeoutMS : 1234 } ) ;
956
+ await session . abortTransaction ( ) ; // this uses the inherited timeoutMS, not the insert
957
+ } ) ;
958
+ } ) ;
959
+ } ) ;
960
+ } ) ;
961
+
962
+ describe ( 'Convenient Transactions' , ( ) => {
963
+ /** Tests in this section MUST only run against replica sets and sharded clusters with server versions 4.4 or higher. */
964
+ const metadata : MongoDBMetadataUI = {
965
+ requires : { topology : [ 'replicaset' , 'sharded' ] , mongodb : '>=5.0' }
966
+ } ;
967
+
968
+ describe ( 'when an operation fails inside withTransaction callback' , ( ) => {
969
+ const failpoint : FailPoint = {
970
+ configureFailPoint : 'failCommand' ,
971
+ mode : { times : 2 } ,
972
+ data : {
973
+ failCommands : [ 'insert' , 'abortTransaction' ] ,
974
+ blockConnection : true ,
975
+ blockTimeMS : 600
976
+ }
977
+ } ;
978
+
979
+ beforeEach ( async function ( ) {
980
+ if ( ! semver . satisfies ( this . configuration . version , '>=4.4' ) ) {
981
+ this . skipReason = 'Requires server version 4.4+' ;
982
+ this . skip ( ) ;
983
+ }
984
+ const internalClient = this . configuration . newClient ( ) ;
985
+ await internalClient
986
+ . db ( 'db' )
987
+ . collection ( 'coll' )
988
+ . drop ( )
989
+ . catch ( ( ) => null ) ;
990
+ await internalClient . db ( 'admin' ) . command ( failpoint ) ;
991
+ await internalClient . close ( ) ;
992
+ } ) ;
993
+
994
+ let client : MongoClient ;
995
+
996
+ afterEach ( async function ( ) {
997
+ if ( semver . satisfies ( this . configuration . version , '>=4.4' ) ) {
998
+ const internalClient = this . configuration . newClient ( ) ;
999
+ await internalClient
1000
+ . db ( 'admin' )
1001
+ . command ( { configureFailPoint : 'failCommand' , mode : 'off' } ) ;
1002
+ await internalClient . close ( ) ;
1003
+ }
1004
+ await client ?. close ( ) ;
1005
+ } ) ;
1006
+
1007
+ it (
1008
+ 'timeoutMS is refreshed for abortTransaction and the timeout error is thrown from the operation' ,
1009
+ metadata ,
1010
+ async function ( ) {
1011
+ const commandsFailed = [ ] ;
1012
+ const commandsStarted = [ ] ;
1013
+
1014
+ client = this . configuration
1015
+ . newClient ( { timeoutMS : 500 , monitorCommands : true } )
1016
+ . on ( 'commandStarted' , e => commandsStarted . push ( e . commandName ) )
1017
+ . on ( 'commandFailed' , e => commandsFailed . push ( e . commandName ) ) ;
1018
+
1019
+ const coll = client . db ( 'db' ) . collection ( 'coll' ) ;
1020
+
1021
+ const session = client . startSession ( ) ;
1022
+
1023
+ let insertError : Error | null = null ;
1024
+ const withTransactionError = await session
1025
+ . withTransaction ( async session => {
1026
+ insertError = await coll . insertOne ( { x : 1 } , { session } ) . catch ( error => error ) ;
1027
+ throw insertError ;
1028
+ } )
1029
+ . catch ( error => error ) ;
1030
+
1031
+ try {
1032
+ expect ( insertError ) . to . be . instanceOf ( MongoOperationTimeoutError ) ;
1033
+ expect ( withTransactionError ) . to . be . instanceOf ( MongoOperationTimeoutError ) ;
1034
+ expect ( commandsStarted , 'commands started' ) . to . deep . equal ( [
1035
+ 'insert' ,
1036
+ 'abortTransaction'
1037
+ ] ) ;
1038
+ expect ( commandsFailed , 'commands failed' ) . to . deep . equal ( [ 'insert' , 'abortTransaction' ] ) ;
1039
+ } finally {
1040
+ await session . endSession ( ) ;
1041
+ }
1042
+ }
1043
+ ) ;
1044
+ } ) ;
1045
+ } ) ;
897
1046
} ) ;
0 commit comments