11// Note: These tests run the handler in Node.js, which has some differences to the cloudflare workers runtime. 
22// Although this is not ideal, this is the best we can do until we have a better way to test cloudflare workers. 
33
4- import  type  {  ForwardableEmailMessage ,  MessageBatch ,  ScheduledController ,  TraceItem  }  from  '@cloudflare/workers-types' ; 
4+ import  type  { 
5+   ExecutionContext , 
6+   ForwardableEmailMessage , 
7+   MessageBatch , 
8+   ScheduledController , 
9+   TraceItem , 
10+ }  from  '@cloudflare/workers-types' ; 
511import  type  {  Event  }  from  '@sentry/core' ; 
612import  *  as  SentryCore  from  '@sentry/core' ; 
7- import  {  beforeEach ,  describe ,  expect ,  test ,  vi  }  from  'vitest' ; 
13+ import  {  beforeEach ,  describe ,  expect ,  onTestFinished ,   test ,  vi  }  from  'vitest' ; 
814import  {  CloudflareClient  }  from  '../src/client' ; 
915import  {  withSentry  }  from  '../src/handler' ; 
1016import  {  markAsInstrumented  }  from  '../src/instrument' ; 
@@ -24,6 +30,10 @@ const MOCK_ENV = {
2430  SENTRY_RELEASE : '1.1.1' , 
2531} ; 
2632
33+ function  addDelayedWaitUntil ( context : ExecutionContext )  { 
34+   context . waitUntil ( new  Promise < void > ( resolve  =>  setTimeout ( ( )  =>  resolve ( ) ) ) ) ; 
35+ } 
36+ 
2737describe ( 'withSentry' ,  ( )  =>  { 
2838  beforeEach ( ( )  =>  { 
2939    vi . clearAllMocks ( ) ; 
@@ -122,6 +132,32 @@ describe('withSentry', () => {
122132
123133      expect ( sentryEvent . release ) . toEqual ( '2.0.0' ) ; 
124134    } ) ; 
135+ 
136+     test ( 'flush must be called when all waitUntil are done' ,  async  ( )  =>  { 
137+       const  flush  =  vi . spyOn ( SentryCore ,  'flush' ) ; 
138+       vi . useFakeTimers ( ) ; 
139+       onTestFinished ( ( )  =>  { 
140+         vi . useRealTimers ( ) ; 
141+       } ) ; 
142+       const  handler  =  { 
143+         fetch ( _request ,  _env ,  _context )  { 
144+           addDelayedWaitUntil ( _context ) ; 
145+           return  new  Response ( 'test' ) ; 
146+         } , 
147+       }  satisfies  ExportedHandler < typeof  MOCK_ENV > ; 
148+ 
149+       const  wrappedHandler  =  withSentry ( vi . fn ( ) ,  handler ) ; 
150+       const  waits : Promise < unknown > [ ]  =  [ ] ; 
151+       const  waitUntil  =  vi . fn ( promise  =>  waits . push ( promise ) ) ; 
152+       await  wrappedHandler . fetch ?.( new  Request ( 'https://example.com' ) ,  MOCK_ENV ,  { 
153+         waitUntil, 
154+       }  as  unknown  as  ExecutionContext ) ; 
155+       expect ( flush ) . not . toBeCalled ( ) ; 
156+       expect ( waitUntil ) . toBeCalled ( ) ; 
157+       vi . advanceTimersToNextTimer ( ) ; 
158+       await  Promise . all ( waits ) ; 
159+       expect ( flush ) . toHaveBeenCalledOnce ( ) ; 
160+     } ) ; 
125161  } ) ; 
126162
127163  describe ( 'scheduled handler' ,  ( )  =>  { 
@@ -198,13 +234,12 @@ describe('withSentry', () => {
198234      }  satisfies  ExportedHandler < typeof  MOCK_ENV > ; 
199235
200236      const  context  =  createMockExecutionContext ( ) ; 
237+       const  waitUntilSpy  =  vi . spyOn ( context ,  'waitUntil' ) ; 
201238      const  wrappedHandler  =  withSentry ( env  =>  ( {  dsn : env . SENTRY_DSN  } ) ,  handler ) ; 
202239      await  wrappedHandler . scheduled ?.( createMockScheduledController ( ) ,  MOCK_ENV ,  context ) ; 
203240
204-       // eslint-disable-next-line @typescript-eslint/unbound-method 
205-       expect ( context . waitUntil ) . toHaveBeenCalledTimes ( 1 ) ; 
206-       // eslint-disable-next-line @typescript-eslint/unbound-method 
207-       expect ( context . waitUntil ) . toHaveBeenLastCalledWith ( expect . any ( Promise ) ) ; 
241+       expect ( waitUntilSpy ) . toHaveBeenCalledTimes ( 1 ) ; 
242+       expect ( waitUntilSpy ) . toHaveBeenLastCalledWith ( expect . any ( Promise ) ) ; 
208243    } ) ; 
209244
210245    test ( 'creates a cloudflare client and sets it on the handler' ,  async  ( )  =>  { 
@@ -337,6 +372,32 @@ describe('withSentry', () => {
337372        } ) ; 
338373      } ) ; 
339374    } ) ; 
375+ 
376+     test ( 'flush must be called when all waitUntil are done' ,  async  ( )  =>  { 
377+       const  flush  =  vi . spyOn ( SentryCore ,  'flush' ) ; 
378+       vi . useFakeTimers ( ) ; 
379+       onTestFinished ( ( )  =>  { 
380+         vi . useRealTimers ( ) ; 
381+       } ) ; 
382+       const  handler  =  { 
383+         scheduled ( _controller ,  _env ,  _context )  { 
384+           addDelayedWaitUntil ( _context ) ; 
385+           return ; 
386+         } , 
387+       }  satisfies  ExportedHandler < typeof  MOCK_ENV > ; 
388+ 
389+       const  wrappedHandler  =  withSentry ( vi . fn ( ) ,  handler ) ; 
390+       const  waits : Promise < unknown > [ ]  =  [ ] ; 
391+       const  waitUntil  =  vi . fn ( promise  =>  waits . push ( promise ) ) ; 
392+       await  wrappedHandler . scheduled ?.( createMockScheduledController ( ) ,  MOCK_ENV ,  { 
393+         waitUntil, 
394+       }  as  unknown  as  ExecutionContext ) ; 
395+       expect ( flush ) . not . toBeCalled ( ) ; 
396+       expect ( waitUntil ) . toBeCalled ( ) ; 
397+       vi . advanceTimersToNextTimer ( ) ; 
398+       await  Promise . all ( waits ) ; 
399+       expect ( flush ) . toHaveBeenCalledOnce ( ) ; 
400+     } ) ; 
340401  } ) ; 
341402
342403  describe ( 'email handler' ,  ( )  =>  { 
@@ -413,13 +474,12 @@ describe('withSentry', () => {
413474      }  satisfies  ExportedHandler < typeof  MOCK_ENV > ; 
414475
415476      const  context  =  createMockExecutionContext ( ) ; 
477+       const  waitUntilSpy  =  vi . spyOn ( context ,  'waitUntil' ) ; 
416478      const  wrappedHandler  =  withSentry ( env  =>  ( {  dsn : env . SENTRY_DSN  } ) ,  handler ) ; 
417479      await  wrappedHandler . email ?.( createMockEmailMessage ( ) ,  MOCK_ENV ,  context ) ; 
418480
419-       // eslint-disable-next-line @typescript-eslint/unbound-method 
420-       expect ( context . waitUntil ) . toHaveBeenCalledTimes ( 1 ) ; 
421-       // eslint-disable-next-line @typescript-eslint/unbound-method 
422-       expect ( context . waitUntil ) . toHaveBeenLastCalledWith ( expect . any ( Promise ) ) ; 
481+       expect ( waitUntilSpy ) . toHaveBeenCalledTimes ( 1 ) ; 
482+       expect ( waitUntilSpy ) . toHaveBeenLastCalledWith ( expect . any ( Promise ) ) ; 
423483    } ) ; 
424484
425485    test ( 'creates a cloudflare client and sets it on the handler' ,  async  ( )  =>  { 
@@ -551,6 +611,32 @@ describe('withSentry', () => {
551611        } ) ; 
552612      } ) ; 
553613    } ) ; 
614+ 
615+     test ( 'flush must be called when all waitUntil are done' ,  async  ( )  =>  { 
616+       const  flush  =  vi . spyOn ( SentryCore ,  'flush' ) ; 
617+       vi . useFakeTimers ( ) ; 
618+       onTestFinished ( ( )  =>  { 
619+         vi . useRealTimers ( ) ; 
620+       } ) ; 
621+       const  handler  =  { 
622+         email ( _controller ,  _env ,  _context )  { 
623+           addDelayedWaitUntil ( _context ) ; 
624+           return ; 
625+         } , 
626+       }  satisfies  ExportedHandler < typeof  MOCK_ENV > ; 
627+ 
628+       const  wrappedHandler  =  withSentry ( vi . fn ( ) ,  handler ) ; 
629+       const  waits : Promise < unknown > [ ]  =  [ ] ; 
630+       const  waitUntil  =  vi . fn ( promise  =>  waits . push ( promise ) ) ; 
631+       await  wrappedHandler . email ?.( createMockEmailMessage ( ) ,  MOCK_ENV ,  { 
632+         waitUntil, 
633+       }  as  unknown  as  ExecutionContext ) ; 
634+       expect ( flush ) . not . toBeCalled ( ) ; 
635+       expect ( waitUntil ) . toBeCalled ( ) ; 
636+       vi . advanceTimersToNextTimer ( ) ; 
637+       await  Promise . all ( waits ) ; 
638+       expect ( flush ) . toHaveBeenCalledOnce ( ) ; 
639+     } ) ; 
554640  } ) ; 
555641
556642  describe ( 'queue handler' ,  ( )  =>  { 
@@ -627,13 +713,12 @@ describe('withSentry', () => {
627713      }  satisfies  ExportedHandler < typeof  MOCK_ENV > ; 
628714
629715      const  context  =  createMockExecutionContext ( ) ; 
716+       const  waitUntilSpy  =  vi . spyOn ( context ,  'waitUntil' ) ; 
630717      const  wrappedHandler  =  withSentry ( env  =>  ( {  dsn : env . SENTRY_DSN  } ) ,  handler ) ; 
631718      await  wrappedHandler . queue ?.( createMockQueueBatch ( ) ,  MOCK_ENV ,  context ) ; 
632719
633-       // eslint-disable-next-line @typescript-eslint/unbound-method 
634-       expect ( context . waitUntil ) . toHaveBeenCalledTimes ( 1 ) ; 
635-       // eslint-disable-next-line @typescript-eslint/unbound-method 
636-       expect ( context . waitUntil ) . toHaveBeenLastCalledWith ( expect . any ( Promise ) ) ; 
720+       expect ( waitUntilSpy ) . toHaveBeenCalledTimes ( 1 ) ; 
721+       expect ( waitUntilSpy ) . toHaveBeenLastCalledWith ( expect . any ( Promise ) ) ; 
637722    } ) ; 
638723
639724    test ( 'creates a cloudflare client and sets it on the handler' ,  async  ( )  =>  { 
@@ -769,6 +854,32 @@ describe('withSentry', () => {
769854        } ) ; 
770855      } ) ; 
771856    } ) ; 
857+ 
858+     test ( 'flush must be called when all waitUntil are done' ,  async  ( )  =>  { 
859+       const  flush  =  vi . spyOn ( SentryCore ,  'flush' ) ; 
860+       vi . useFakeTimers ( ) ; 
861+       onTestFinished ( ( )  =>  { 
862+         vi . useRealTimers ( ) ; 
863+       } ) ; 
864+       const  handler  =  { 
865+         queue ( _controller ,  _env ,  _context )  { 
866+           addDelayedWaitUntil ( _context ) ; 
867+           return ; 
868+         } , 
869+       }  satisfies  ExportedHandler < typeof  MOCK_ENV > ; 
870+ 
871+       const  wrappedHandler  =  withSentry ( vi . fn ( ) ,  handler ) ; 
872+       const  waits : Promise < unknown > [ ]  =  [ ] ; 
873+       const  waitUntil  =  vi . fn ( promise  =>  waits . push ( promise ) ) ; 
874+       await  wrappedHandler . queue ?.( createMockQueueBatch ( ) ,  MOCK_ENV ,  { 
875+         waitUntil, 
876+       }  as  unknown  as  ExecutionContext ) ; 
877+       expect ( flush ) . not . toBeCalled ( ) ; 
878+       expect ( waitUntil ) . toBeCalled ( ) ; 
879+       vi . advanceTimersToNextTimer ( ) ; 
880+       await  Promise . all ( waits ) ; 
881+       expect ( flush ) . toHaveBeenCalledOnce ( ) ; 
882+     } ) ; 
772883  } ) ; 
773884
774885  describe ( 'tail handler' ,  ( )  =>  { 
@@ -845,13 +956,12 @@ describe('withSentry', () => {
845956      }  satisfies  ExportedHandler < typeof  MOCK_ENV > ; 
846957
847958      const  context  =  createMockExecutionContext ( ) ; 
959+       const  waitUntilSpy  =  vi . spyOn ( context ,  'waitUntil' ) ; 
848960      const  wrappedHandler  =  withSentry ( env  =>  ( {  dsn : env . SENTRY_DSN  } ) ,  handler ) ; 
849961      await  wrappedHandler . tail ?.( createMockTailEvent ( ) ,  MOCK_ENV ,  context ) ; 
850962
851-       // eslint-disable-next-line @typescript-eslint/unbound-method 
852-       expect ( context . waitUntil ) . toHaveBeenCalledTimes ( 1 ) ; 
853-       // eslint-disable-next-line @typescript-eslint/unbound-method 
854-       expect ( context . waitUntil ) . toHaveBeenLastCalledWith ( expect . any ( Promise ) ) ; 
963+       expect ( waitUntilSpy ) . toHaveBeenCalledTimes ( 1 ) ; 
964+       expect ( waitUntilSpy ) . toHaveBeenLastCalledWith ( expect . any ( Promise ) ) ; 
855965    } ) ; 
856966
857967    test ( 'creates a cloudflare client and sets it on the handler' ,  async  ( )  =>  { 
@@ -941,6 +1051,32 @@ describe('withSentry', () => {
9411051        expect ( thrownError ) . toBe ( error ) ; 
9421052      } ) ; 
9431053    } ) ; 
1054+ 
1055+     test ( 'flush must be called when all waitUntil are done' ,  async  ( )  =>  { 
1056+       const  flush  =  vi . spyOn ( SentryCore ,  'flush' ) ; 
1057+       vi . useFakeTimers ( ) ; 
1058+       onTestFinished ( ( )  =>  { 
1059+         vi . useRealTimers ( ) ; 
1060+       } ) ; 
1061+       const  handler  =  { 
1062+         tail ( _controller ,  _env ,  _context )  { 
1063+           addDelayedWaitUntil ( _context ) ; 
1064+           return ; 
1065+         } , 
1066+       }  satisfies  ExportedHandler < typeof  MOCK_ENV > ; 
1067+ 
1068+       const  wrappedHandler  =  withSentry ( vi . fn ( ) ,  handler ) ; 
1069+       const  waits : Promise < unknown > [ ]  =  [ ] ; 
1070+       const  waitUntil  =  vi . fn ( promise  =>  waits . push ( promise ) ) ; 
1071+       await  wrappedHandler . tail ?.( createMockTailEvent ( ) ,  MOCK_ENV ,  { 
1072+         waitUntil, 
1073+       }  as  unknown  as  ExecutionContext ) ; 
1074+       expect ( flush ) . not . toBeCalled ( ) ; 
1075+       expect ( waitUntil ) . toBeCalled ( ) ; 
1076+       vi . advanceTimersToNextTimer ( ) ; 
1077+       await  Promise . all ( waits ) ; 
1078+       expect ( flush ) . toHaveBeenCalledOnce ( ) ; 
1079+     } ) ; 
9441080  } ) ; 
9451081
9461082  describe ( 'hono errorHandler' ,  ( )  =>  { 
0 commit comments