1
1
import { expect } from 'chai' ;
2
+ import { once } from 'events' ;
2
3
import * as sinon from 'sinon' ;
3
4
import { setTimeout } from 'timers' ;
5
+ import { promisify } from 'util' ;
4
6
5
7
import {
8
+ AbstractCursor ,
6
9
type ChangeStream ,
7
10
type CommandFailedEvent ,
8
11
type CommandStartedEvent ,
@@ -16,6 +19,7 @@ import {
16
19
Timestamp
17
20
} from '../../mongodb' ;
18
21
import * as mock from '../../tools/mongodb-mock/index' ;
22
+ import { getSymbolFrom } from '../../tools/utils' ;
19
23
import { setupDatabase } from '../shared' ;
20
24
21
25
/**
@@ -68,6 +72,14 @@ function triggerResumableError(
68
72
triggerError ( ) ;
69
73
}
70
74
75
+ const initIteratorMode = async ( cs : ChangeStream ) => {
76
+ const init = getSymbolFrom ( AbstractCursor . prototype , 'kInit' ) ;
77
+ const initEvent = once ( cs . cursor , 'init' ) ;
78
+ await promisify ( cs . cursor [ init ] . bind ( cs . cursor ) ) ( ) ;
79
+ await initEvent ;
80
+ return ;
81
+ } ;
82
+
71
83
/** Waits for a change stream to start */
72
84
function waitForStarted ( changeStream , callback ) {
73
85
changeStream . cursor . once ( 'init' , ( ) => {
@@ -938,4 +950,52 @@ describe('Change Stream prose tests', function () {
938
950
}
939
951
} ) ;
940
952
} ) ;
953
+
954
+ describe ( '19. Validate that large ChangeStream events are split when using $changeStreamSplitLargeEvent' , function ( ) {
955
+ let client ;
956
+ let db ;
957
+ let collection ;
958
+ let changeStream ;
959
+
960
+ beforeEach ( async function ( ) {
961
+ const configuration = this . configuration ;
962
+ client = configuration . newClient ( ) ;
963
+ db = client . db ( 'test' ) ;
964
+ // Create a new collection _C_ with changeStreamPreAndPostImages enabled.
965
+ await db . createCollection ( 'changeStreamSplitTests' , {
966
+ changeStreamPreAndPostImages : { enabled : true }
967
+ } ) ;
968
+ collection = db . collection ( 'changeStreamSplitTests' ) ;
969
+ } ) ;
970
+
971
+ afterEach ( async function ( ) {
972
+ await changeStream . close ( ) ;
973
+ await collection . drop ( ) ;
974
+ await client . close ( ) ;
975
+ } ) ;
976
+
977
+ it ( 'splits the event into multiple fragments' , {
978
+ metadata : { requires : { topology : '!single' , mongodb : '>=7.0.0' } } ,
979
+ test : async function ( ) {
980
+ // Insert into _C_ a document at least 10mb in size, e.g. { "value": "q"*10*1024*1024 }
981
+ await collection . insertOne ( { value : 'q' . repeat ( 10 * 1024 * 1024 ) } ) ;
982
+ // Create a change stream _S_ by calling watch on _C_ with pipeline
983
+ // [{ "$changeStreamSplitLargeEvent": {} }] and fullDocumentBeforeChange=required.
984
+ changeStream = collection . watch ( [ { $changeStreamSplitLargeEvent : { } } ] , {
985
+ fullDocumentBeforeChange : 'required'
986
+ } ) ;
987
+ await initIteratorMode ( changeStream ) ;
988
+ // Call updateOne on _C_ with an empty query and an update setting the field to a new
989
+ // large value, e.g. { "$set": { "value": "z"*10*1024*1024 } }.
990
+ await collection . updateOne ( { } , { $set : { value : 'z' . repeat ( 10 * 1024 * 1024 ) } } ) ;
991
+ // Collect two events from _S_.
992
+ const eventOne = await changeStream . next ( ) ;
993
+ const eventTwo = await changeStream . next ( ) ;
994
+ // Assert that the events collected have splitEvent fields { "fragment": 1, "of": 2 }
995
+ // and { "fragment": 2, "of": 2 }, in that order.
996
+ expect ( eventOne . splitEvent ) . to . deep . equal ( { fragment : 1 , of : 2 } ) ;
997
+ expect ( eventTwo . splitEvent ) . to . deep . equal ( { fragment : 2 , of : 2 } ) ;
998
+ }
999
+ } ) ;
1000
+ } ) ;
941
1001
} ) ;
0 commit comments