1
1
// Note: These tests run the handler in Node.js, which has some differences to the cloudflare workers runtime.
2
2
// Although this is not ideal, this is the best we can do until we have a better way to test cloudflare workers.
3
3
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' ;
5
11
import type { Event } from '@sentry/core' ;
6
12
import * as SentryCore from '@sentry/core' ;
7
- import { beforeEach , describe , expect , test , vi } from 'vitest' ;
13
+ import { beforeEach , describe , expect , onTestFinished , test , vi } from 'vitest' ;
8
14
import { CloudflareClient } from '../src/client' ;
9
15
import { withSentry } from '../src/handler' ;
10
16
import { markAsInstrumented } from '../src/instrument' ;
@@ -24,6 +30,10 @@ const MOCK_ENV = {
24
30
SENTRY_RELEASE : '1.1.1' ,
25
31
} ;
26
32
33
+ function addDelayedWaitUntil ( context : ExecutionContext ) {
34
+ context . waitUntil ( new Promise < void > ( resolve => setTimeout ( ( ) => resolve ( ) ) ) ) ;
35
+ }
36
+
27
37
describe ( 'withSentry' , ( ) => {
28
38
beforeEach ( ( ) => {
29
39
vi . clearAllMocks ( ) ;
@@ -122,6 +132,32 @@ describe('withSentry', () => {
122
132
123
133
expect ( sentryEvent . release ) . toEqual ( '2.0.0' ) ;
124
134
} ) ;
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
+ } ) ;
125
161
} ) ;
126
162
127
163
describe ( 'scheduled handler' , ( ) => {
@@ -198,13 +234,12 @@ describe('withSentry', () => {
198
234
} satisfies ExportedHandler < typeof MOCK_ENV > ;
199
235
200
236
const context = createMockExecutionContext ( ) ;
237
+ const waitUntilSpy = vi . spyOn ( context , 'waitUntil' ) ;
201
238
const wrappedHandler = withSentry ( env => ( { dsn : env . SENTRY_DSN } ) , handler ) ;
202
239
await wrappedHandler . scheduled ?.( createMockScheduledController ( ) , MOCK_ENV , context ) ;
203
240
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 ) ) ;
208
243
} ) ;
209
244
210
245
test ( 'creates a cloudflare client and sets it on the handler' , async ( ) => {
@@ -337,6 +372,32 @@ describe('withSentry', () => {
337
372
} ) ;
338
373
} ) ;
339
374
} ) ;
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
+ } ) ;
340
401
} ) ;
341
402
342
403
describe ( 'email handler' , ( ) => {
@@ -413,13 +474,12 @@ describe('withSentry', () => {
413
474
} satisfies ExportedHandler < typeof MOCK_ENV > ;
414
475
415
476
const context = createMockExecutionContext ( ) ;
477
+ const waitUntilSpy = vi . spyOn ( context , 'waitUntil' ) ;
416
478
const wrappedHandler = withSentry ( env => ( { dsn : env . SENTRY_DSN } ) , handler ) ;
417
479
await wrappedHandler . email ?.( createMockEmailMessage ( ) , MOCK_ENV , context ) ;
418
480
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 ) ) ;
423
483
} ) ;
424
484
425
485
test ( 'creates a cloudflare client and sets it on the handler' , async ( ) => {
@@ -551,6 +611,32 @@ describe('withSentry', () => {
551
611
} ) ;
552
612
} ) ;
553
613
} ) ;
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
+ } ) ;
554
640
} ) ;
555
641
556
642
describe ( 'queue handler' , ( ) => {
@@ -627,13 +713,12 @@ describe('withSentry', () => {
627
713
} satisfies ExportedHandler < typeof MOCK_ENV > ;
628
714
629
715
const context = createMockExecutionContext ( ) ;
716
+ const waitUntilSpy = vi . spyOn ( context , 'waitUntil' ) ;
630
717
const wrappedHandler = withSentry ( env => ( { dsn : env . SENTRY_DSN } ) , handler ) ;
631
718
await wrappedHandler . queue ?.( createMockQueueBatch ( ) , MOCK_ENV , context ) ;
632
719
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 ) ) ;
637
722
} ) ;
638
723
639
724
test ( 'creates a cloudflare client and sets it on the handler' , async ( ) => {
@@ -769,6 +854,32 @@ describe('withSentry', () => {
769
854
} ) ;
770
855
} ) ;
771
856
} ) ;
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
+ } ) ;
772
883
} ) ;
773
884
774
885
describe ( 'tail handler' , ( ) => {
@@ -845,13 +956,12 @@ describe('withSentry', () => {
845
956
} satisfies ExportedHandler < typeof MOCK_ENV > ;
846
957
847
958
const context = createMockExecutionContext ( ) ;
959
+ const waitUntilSpy = vi . spyOn ( context , 'waitUntil' ) ;
848
960
const wrappedHandler = withSentry ( env => ( { dsn : env . SENTRY_DSN } ) , handler ) ;
849
961
await wrappedHandler . tail ?.( createMockTailEvent ( ) , MOCK_ENV , context ) ;
850
962
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 ) ) ;
855
965
} ) ;
856
966
857
967
test ( 'creates a cloudflare client and sets it on the handler' , async ( ) => {
@@ -941,6 +1051,32 @@ describe('withSentry', () => {
941
1051
expect ( thrownError ) . toBe ( error ) ;
942
1052
} ) ;
943
1053
} ) ;
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
+ } ) ;
944
1080
} ) ;
945
1081
946
1082
describe ( 'hono errorHandler' , ( ) => {
0 commit comments