@@ -37,7 +37,10 @@ function getExportNameAndPath(sessionId: string, timestamp: number) {
3737 } ;
3838}
3939
40- function createDummyFindCursor ( dataArray : unknown [ ] , chunkPushTimeoutMs ?: number ) : FindCursor {
40+ function createDummyFindCursor (
41+ dataArray : unknown [ ] ,
42+ chunkPushTimeoutMs ?: number
43+ ) : { cursor : FindCursor ; cursorCloseNotification : Promise < void > } {
4144 let index = 0 ;
4245 const readable = new Readable ( {
4346 objectMode : true ,
@@ -56,14 +59,26 @@ function createDummyFindCursor(dataArray: unknown[], chunkPushTimeoutMs?: number
5659 } ,
5760 } ) ;
5861
62+ let notifyClose : ( ) => Promise < void > ;
63+ const cursorCloseNotification = new Promise < void > ( ( resolve ) => {
64+ notifyClose = async ( ) => {
65+ await timeout ( 10 ) ;
66+ resolve ( ) ;
67+ } ;
68+ } ) ;
69+ readable . once ( "close" , ( ) => void notifyClose ?.( ) ) ;
70+
5971 return {
60- stream ( ) {
61- return readable ;
62- } ,
63- close ( ) {
64- return Promise . resolve ( readable . destroy ( ) ) ;
65- } ,
66- } as unknown as FindCursor ;
72+ cursor : {
73+ stream ( ) {
74+ return readable ;
75+ } ,
76+ close ( ) {
77+ return Promise . resolve ( readable . destroy ( ) ) ;
78+ } ,
79+ } as unknown as FindCursor ,
80+ cursorCloseNotification,
81+ } ;
6782}
6883
6984async function fileExists ( filePath : string ) {
@@ -92,21 +107,22 @@ describe("SessionExportsManager unit test", () => {
92107 // This export will finish in at-least 1 second
93108 const { exportName : exportName1 } = getExportNameAndPath ( session . sessionId , Date . now ( ) ) ;
94109 manager . createJSONExport ( {
95- input : createDummyFindCursor ( [ { name : "Test1" } ] , 1000 ) ,
110+ input : createDummyFindCursor ( [ { name : "Test1" } ] , 1000 ) . cursor ,
96111 exportName : exportName1 ,
97112 jsonExportFormat : "relaxed" ,
98113 } ) ;
99114
100115 // This export will finish way sooner than the first one
101116 const { exportName : exportName2 } = getExportNameAndPath ( session . sessionId , Date . now ( ) ) ;
117+ const { cursor, cursorCloseNotification } = createDummyFindCursor ( [ { name : "Test1" } ] ) ;
102118 manager . createJSONExport ( {
103- input : createDummyFindCursor ( [ { name : "Test1" } ] ) ,
119+ input : cursor ,
104120 exportName : exportName2 ,
105121 jsonExportFormat : "relaxed" ,
106122 } ) ;
107123
108124 // Small timeout to let the second export finish
109- await timeout ( 10 ) ;
125+ await cursorCloseNotification ;
110126 expect ( manager . availableExports ) . toHaveLength ( 1 ) ;
111127 expect ( manager . availableExports [ 0 ] ?. exportName ) . toEqual ( exportName2 ) ;
112128 } ) ;
@@ -119,42 +135,40 @@ describe("SessionExportsManager unit test", () => {
119135
120136 it ( "should throw if the resource is still being generated" , async ( ) => {
121137 const { exportName } = getExportNameAndPath ( session . sessionId , Date . now ( ) ) ;
122- const inputCursor = createDummyFindCursor ( [ { name : "Test1" } ] , 100 ) ;
138+ const { cursor } = createDummyFindCursor ( [ { name : "Test1" } ] , 100 ) ;
123139 manager . createJSONExport ( {
124- input : inputCursor ,
140+ input : cursor ,
125141 exportName,
126142 jsonExportFormat : "relaxed" ,
127143 } ) ;
128- // Small timeout but it won't be sufficient and the export
129- // generation will still be in progress
130- await timeout ( 10 ) ;
144+ // note that we do not wait for cursor close
131145 await expect ( ( ) => manager . readExport ( exportName ) ) . rejects . toThrow (
132146 "Requested export is still being generated!"
133147 ) ;
134148 } ) ;
135149
136150 it ( "should return the resource content if the resource is ready to be consumed" , async ( ) => {
137151 const { exportName } = getExportNameAndPath ( session . sessionId , Date . now ( ) ) ;
138- const inputCursor = createDummyFindCursor ( [ ] ) ;
152+ const { cursor , cursorCloseNotification } = createDummyFindCursor ( [ ] ) ;
139153 manager . createJSONExport ( {
140- input : inputCursor ,
154+ input : cursor ,
141155 exportName,
142156 jsonExportFormat : "relaxed" ,
143157 } ) ;
144- // Small timeout to account for async operation
145- await timeout ( 10 ) ;
158+ await cursorCloseNotification ;
146159 expect ( await manager . readExport ( exportName ) ) . toEqual ( "[]" ) ;
147160 } ) ;
148161 } ) ;
149162
150163 describe ( "#createJSONExport" , ( ) => {
151- let inputCursor : FindCursor ;
164+ let cursor : FindCursor ;
165+ let cursorCloseNotification : Promise < void > ;
152166 let exportName : string ;
153167 let exportPath : string ;
154168 let exportURI : string ;
155169 beforeEach ( ( ) => {
156- void inputCursor ?. close ( ) ;
157- inputCursor = createDummyFindCursor ( [
170+ void cursor ?. close ( ) ;
171+ ( { cursor , cursorCloseNotification } = createDummyFindCursor ( [
158172 {
159173 name : "foo" ,
160174 longNumber : Long . fromNumber ( 12 ) ,
@@ -163,22 +177,21 @@ describe("SessionExportsManager unit test", () => {
163177 name : "bar" ,
164178 longNumber : Long . fromNumber ( 123456 ) ,
165179 } ,
166- ] ) ;
180+ ] ) ) ;
167181 ( { exportName, exportPath, exportURI } = getExportNameAndPath ( session . sessionId , Date . now ( ) ) ) ;
168182 } ) ;
169183
170184 describe ( "when cursor is empty" , ( ) => {
171185 it ( "should create an empty export" , async ( ) => {
172- inputCursor = createDummyFindCursor ( [ ] ) ;
186+ const { cursor , cursorCloseNotification } = createDummyFindCursor ( [ ] ) ;
173187
174188 const emitSpy = vi . spyOn ( manager , "emit" ) ;
175189 manager . createJSONExport ( {
176- input : inputCursor ,
190+ input : cursor ,
177191 exportName,
178192 jsonExportFormat : "relaxed" ,
179193 } ) ;
180- // Small timeout to account for async operation
181- await timeout ( 10 ) ;
194+ await cursorCloseNotification ;
182195
183196 // Updates available export
184197 const availableExports = manager . availableExports ;
@@ -206,12 +219,11 @@ describe("SessionExportsManager unit test", () => {
206219 it ( "should export relaxed json, update available exports and emit export-available event" , async ( ) => {
207220 const emitSpy = vi . spyOn ( manager , "emit" ) ;
208221 manager . createJSONExport ( {
209- input : inputCursor ,
222+ input : cursor ,
210223 exportName,
211224 jsonExportFormat : "relaxed" ,
212225 } ) ;
213- // Small timeout to account for async operation
214- await timeout ( 10 ) ;
226+ await cursorCloseNotification ;
215227
216228 const expectedExportName = exportName . endsWith ( ".json" ) ? exportName : `${ exportName } .json` ;
217229 // Updates available export
@@ -241,12 +253,11 @@ describe("SessionExportsManager unit test", () => {
241253 it ( "should export canonical json, update available exports and emit export-available event" , async ( ) => {
242254 const emitSpy = vi . spyOn ( manager , "emit" ) ;
243255 manager . createJSONExport ( {
244- input : inputCursor ,
256+ input : cursor ,
245257 exportName,
246258 jsonExportFormat : "canonical" ,
247259 } ) ;
248- // Small timeout to account for async operation
249- await timeout ( 50 ) ;
260+ await cursorCloseNotification ;
250261
251262 const expectedExportName = exportName . endsWith ( ".json" ) ? exportName : `${ exportName } .json` ;
252263 // Updates available export
@@ -302,12 +313,11 @@ describe("SessionExportsManager unit test", () => {
302313 } ) ;
303314 } ;
304315 manager . createJSONExport ( {
305- input : inputCursor ,
316+ input : cursor ,
306317 exportName,
307318 jsonExportFormat : "relaxed" ,
308319 } ) ;
309- // Small timeout to account for async operation
310- await timeout ( 50 ) ;
320+ await cursorCloseNotification ;
311321
312322 // Because the export was never populated in the available exports.
313323 await expect ( ( ) => manager . readExport ( exportName ) ) . rejects . toThrow (
@@ -321,10 +331,11 @@ describe("SessionExportsManager unit test", () => {
321331 } ) ;
322332
323333 describe ( "#cleanupExpiredExports" , ( ) => {
324- let input : FindCursor ;
334+ let cursor : FindCursor ;
335+ let cursorCloseNotification : Promise < void > ;
325336 beforeEach ( ( ) => {
326- void input ?. close ( ) ;
327- input = createDummyFindCursor ( [
337+ void cursor ?. close ( ) ;
338+ ( { cursor , cursorCloseNotification } = createDummyFindCursor ( [
328339 {
329340 name : "foo" ,
330341 longNumber : Long . fromNumber ( 12 ) ,
@@ -333,7 +344,7 @@ describe("SessionExportsManager unit test", () => {
333344 name : "bar" ,
334345 longNumber : Long . fromNumber ( 123456 ) ,
335346 } ,
336- ] ) ;
347+ ] ) ) ;
337348 } ) ;
338349
339350 it ( "should not clean up in-progress exports" , async ( ) => {
@@ -347,8 +358,9 @@ describe("SessionExportsManager unit test", () => {
347358 } ,
348359 new CompositeLogger ( )
349360 ) ;
361+ const { cursor } = createDummyFindCursor ( [ { name : "Test" } ] , 2000 ) ;
350362 manager . createJSONExport ( {
351- input : createDummyFindCursor ( [ { name : "Test" } ] , 2000 ) ,
363+ input : cursor ,
352364 exportName,
353365 jsonExportFormat : "relaxed" ,
354366 } ) ;
@@ -374,13 +386,11 @@ describe("SessionExportsManager unit test", () => {
374386 new CompositeLogger ( )
375387 ) ;
376388 manager . createJSONExport ( {
377- input,
389+ input : cursor ,
378390 exportName,
379391 jsonExportFormat : "relaxed" ,
380392 } ) ;
381- // Small timeout to account for async operation and let export
382- // finish
383- await timeout ( 10 ) ;
393+ await cursorCloseNotification ;
384394
385395 expect ( manager . availableExports ) . toContainEqual (
386396 expect . objectContaining ( {
0 commit comments