@@ -574,6 +574,100 @@ describe('instrumentWorkerEntrypoint', () => {
574574
575575 expect ( initAndBind ) . toHaveBeenCalledTimes ( 5 ) ;
576576 } ) ;
577+
578+ describe ( 'with a Cloudflare trace' , ( ) => {
579+ const CLOUDFLARE_TRACE_ID = 'a1b2c3d4e5f60718293a4b5c6d7e8f90' ;
580+ const CLOUDFLARE_SPAN_ID = 'b1c2d3e4f5a60718' ;
581+ const INCOMING_TRACE_ID = '12312012123120121231201212312012' ;
582+ const INCOMING_SPAN_ID = '1121201211212012' ;
583+ const CLOUDFLARE_TRACING = {
584+ getActiveSpan : ( ) => ( {
585+ isTraced : true ,
586+ spanContext : ( ) => ( { traceId : CLOUDFLARE_TRACE_ID , spanId : CLOUDFLARE_SPAN_ID , traceFlags : 1 } ) ,
587+ } ) ,
588+ } ;
589+
590+ it ( 'attaches errors of RPC calls without RPC metadata to the Cloudflare trace' , async ( ) => {
591+ const events : Event [ ] = [ ] ;
592+ const waits : Promise < unknown > [ ] = [ ] ;
593+ const context = { ...createMockExecutionContext ( ) , tracing : CLOUDFLARE_TRACING } as unknown as ExecutionContext ;
594+ context . waitUntil = vi . fn ( promise => {
595+ waits . push ( promise ) ;
596+ } ) ;
597+ const TestClass = class extends WorkerEntrypoint {
598+ async fail ( ) : Promise < never > {
599+ throw new Error ( 'rpc failure' ) ;
600+ }
601+ } ;
602+ const obj = Reflect . construct (
603+ instrumentWorkerEntrypoint (
604+ ( ) => ( {
605+ dsn : 'https://public@dsn.ingest.sentry.io/1337' ,
606+ beforeSend ( event ) {
607+ events . push ( event ) ;
608+ return null ;
609+ } ,
610+ } ) ,
611+ TestClass as unknown as WorkerEntrypointConstructor ,
612+ ) ,
613+ [ context , { } ] ,
614+ ) ;
615+
616+ await expect ( obj . fail ( ) ) . rejects . toThrow ( 'rpc failure' ) ;
617+ await Promise . all ( waits ) ;
618+
619+ expect ( events ) . toHaveLength ( 1 ) ;
620+ expect ( events [ 0 ] ?. contexts ?. trace ?. trace_id ) . toBe ( CLOUDFLARE_TRACE_ID ) ;
621+ expect ( events [ 0 ] ?. contexts ?. trace ?. parent_span_id ) . toBe ( CLOUDFLARE_SPAN_ID ) ;
622+ } ) ;
623+
624+ it ( 'makes the RPC span a child of the Cloudflare span when the SDK does not continue the RPC trace' , ( ) => {
625+ let rpcSpan : ReturnType < typeof SentryCore . spanToJSON > | undefined ;
626+ const TestClass = class extends WorkerEntrypoint {
627+ read ( ) {
628+ rpcSpan = SentryCore . spanToJSON ( SentryCore . getActiveSpan ( ) ! ) ;
629+ }
630+ } ;
631+ const obj = Reflect . construct (
632+ instrumentWorkerEntrypoint (
633+ ( ) => ( { dsn : 'https://public@o1.ingest.sentry.io/1337' , tracesSampleRate : 1 } ) ,
634+ TestClass as unknown as WorkerEntrypointConstructor ,
635+ ) ,
636+ [ { ...createMockExecutionContext ( ) , tracing : CLOUDFLARE_TRACING } , { } ] ,
637+ ) ;
638+
639+ obj . read ( {
640+ __sentry_rpc_meta__ : {
641+ 'sentry-trace' : `${ INCOMING_TRACE_ID } -${ INCOMING_SPAN_ID } -1` ,
642+ baggage : `sentry-trace_id=${ INCOMING_TRACE_ID } ,sentry-org_id=2` ,
643+ } ,
644+ } ) ;
645+
646+ expect ( rpcSpan ?. trace_id ) . toBe ( CLOUDFLARE_TRACE_ID ) ;
647+ expect ( rpcSpan ?. parent_span_id ) . toBe ( CLOUDFLARE_SPAN_ID ) ;
648+ } ) ;
649+
650+ it ( 'prefers the Sentry trace of the RPC metadata' , ( ) => {
651+ let rpcSpan : ReturnType < typeof SentryCore . spanToJSON > | undefined ;
652+ const TestClass = class extends WorkerEntrypoint {
653+ read ( ) {
654+ rpcSpan = SentryCore . spanToJSON ( SentryCore . getActiveSpan ( ) ! ) ;
655+ }
656+ } ;
657+ const obj = Reflect . construct (
658+ instrumentWorkerEntrypoint (
659+ ( ) => ( { dsn : 'https://public@o1.ingest.sentry.io/1337' , tracesSampleRate : 1 } ) ,
660+ TestClass as unknown as WorkerEntrypointConstructor ,
661+ ) ,
662+ [ { ...createMockExecutionContext ( ) , tracing : CLOUDFLARE_TRACING } , { } ] ,
663+ ) ;
664+
665+ obj . read ( { __sentry_rpc_meta__ : { 'sentry-trace' : `${ INCOMING_TRACE_ID } -${ INCOMING_SPAN_ID } -1` } } ) ;
666+
667+ expect ( rpcSpan ?. trace_id ) . toBe ( INCOMING_TRACE_ID ) ;
668+ expect ( rpcSpan ?. parent_span_id ) . toBe ( INCOMING_SPAN_ID ) ;
669+ } ) ;
670+ } ) ;
577671 } ) ;
578672
579673 describe ( 'env instrumentation' , ( ) => {
0 commit comments