11import { expect , test } from '@playwright/test' ;
2- import { waitForError , waitForTransaction } from '@sentry-internal/test-utils' ;
2+ import { getSpanOp , waitForError , waitForStreamedSpan } from '@sentry-internal/test-utils' ;
3+
4+ const APP_NAME = 'nestjs-bullmq' ;
5+
6+ function waitForProcessSpan ( ) : Promise < unknown > {
7+ return waitForStreamedSpan ( APP_NAME , span => span . is_segment && getSpanOp ( span ) === 'queue.process' ) ;
8+ }
9+
10+ /**
11+ * The `/check-isolation` route reports the leaked breadcrumbs it can see as a span attribute,
12+ * because streamed spans carry no breadcrumbs of their own.
13+ */
14+ async function getLeakedBreadcrumbs ( baseURL : string ) : Promise < unknown > {
15+ const segmentSpanPromise = waitForStreamedSpan ( APP_NAME , span => {
16+ return span . is_segment && span . name === 'GET /check-isolation' ;
17+ } ) ;
18+
19+ await fetch ( `${ baseURL } /check-isolation` ) ;
20+
21+ return ( await segmentSpanPromise ) . attributes [ 'isolation_scope.leaked_breadcrumbs' ] ?. value ;
22+ }
323
424test ( 'Sends exception to Sentry on error in @Processor process method' , async ( { baseURL } ) => {
5- const errorEventPromise = waitForError ( 'nestjs-bullmq' , event => {
25+ const errorEventPromise = waitForError ( APP_NAME , event => {
626 return (
727 ! event . type &&
828 event . exception ?. values ?. [ 0 ] ?. value === 'Test error from BullMQ processor' &&
@@ -22,43 +42,31 @@ test('Sends exception to Sentry on error in @Processor process method', async ({
2242 } ) ;
2343} ) ;
2444
25- test ( 'Creates a transaction for successful job processing' , async ( { baseURL } ) => {
26- const transactionPromise = waitForTransaction ( 'nestjs-bullmq' , transactionEvent => {
27- return transactionEvent . contexts ?. trace ?. op === 'queue.process' ;
45+ test ( 'Creates a segment span for successful job processing' , async ( { baseURL } ) => {
46+ const spanPromise = waitForStreamedSpan ( APP_NAME , span => {
47+ return span . is_segment && getSpanOp ( span ) === 'queue.process' ;
2848 } ) ;
2949
3050 // Enqueue a job that will succeed
3151 await fetch ( `${ baseURL } /enqueue/success` ) ;
3252
33- const transaction = await transactionPromise ;
53+ const span = await spanPromise ;
3454
35- expect ( transaction . transaction ) . toBe ( 'test-queue process' ) ;
36- expect ( transaction . contexts ?. trace ?. op ) . toBe ( 'queue.process' ) ;
37- expect ( transaction . contexts ?. trace ?. origin ) . toBe ( 'auto.queue.nestjs.bullmq' ) ;
55+ // Streamed messaging spans are named `<operation> <destination>`, the other way around from the
56+ // transaction name.
57+ expect ( span . name ) . toBe ( 'process test-queue' ) ;
58+ expect ( span . attributes [ 'sentry.origin' ] ) . toEqual ( { value : 'auto.queue.nestjs.bullmq' , type : 'string' } ) ;
3859} ) ;
3960
4061test ( 'BullMQ processor breadcrumbs do not leak into subsequent HTTP requests' , async ( { baseURL } ) => {
41- const processTransactionPromise = waitForTransaction ( 'nestjs-bullmq' , transactionEvent => {
42- return transactionEvent . contexts ?. trace ?. op === 'queue.process' ;
43- } ) ;
62+ const processSpanPromise = waitForProcessSpan ( ) ;
4463
4564 // Enqueue a job that adds a breadcrumb during processing
4665 await fetch ( `${ baseURL } /enqueue/breadcrumb-test` ) ;
4766
48- await processTransactionPromise ;
49-
50- const transactionPromise = waitForTransaction ( 'nestjs-bullmq' , transactionEvent => {
51- return transactionEvent . transaction === 'GET /check-isolation' ;
52- } ) ;
67+ await processSpanPromise ;
5368
54- await fetch ( `${ baseURL } /check-isolation` ) ;
55-
56- const transaction = await transactionPromise ;
57-
58- const leakedBreadcrumb = ( transaction . breadcrumbs || [ ] ) . find (
59- ( b : any ) => b . message === 'leaked-breadcrumb-from-bullmq-processor' ,
60- ) ;
61- expect ( leakedBreadcrumb ) . toBeUndefined ( ) ;
69+ expect ( await getLeakedBreadcrumbs ( baseURL ! ) ) . not . toContain ( 'leaked-breadcrumb-from-bullmq-processor' ) ;
6270} ) ;
6371
6472// TODO: @OnWorkerEvent ('completed') handlers run outside the isolation scope created by process().
@@ -67,84 +75,45 @@ test('BullMQ processor breadcrumbs do not leak into subsequent HTTP requests', a
6775test ( 'BullMQ @OnWorkerEvent completed lifecycle breadcrumbs currently leak into subsequent HTTP requests' , async ( {
6876 baseURL,
6977} ) => {
70- const processTransactionPromise = waitForTransaction ( 'nestjs-bullmq' , transactionEvent => {
71- return transactionEvent . contexts ?. trace ?. op === 'queue.process' ;
72- } ) ;
78+ const processSpanPromise = waitForProcessSpan ( ) ;
7379
7480 // Enqueue a job (the completed event fires right after the job is processed)
7581 await fetch ( `${ baseURL } /enqueue/lifecycle-breadcrumb-test` ) ;
7682
77- await processTransactionPromise ;
78-
79- const transactionPromise = waitForTransaction ( 'nestjs-bullmq' , transactionEvent => {
80- return transactionEvent . transaction === 'GET /check-isolation' ;
81- } ) ;
82-
83- await fetch ( `${ baseURL } /check-isolation` ) ;
84-
85- const transaction = await transactionPromise ;
83+ await processSpanPromise ;
8684
87- const leakedBreadcrumb = ( transaction . breadcrumbs || [ ] ) . find (
88- ( b : any ) => b . message === 'leaked-breadcrumb-from-lifecycle-event' ,
89- ) ;
90- // This SHOULD be toBeUndefined() once lifecycle event isolation is implemented.
91- expect ( leakedBreadcrumb ) . toBeDefined ( ) ;
85+ // This SHOULD be not.toContain() once lifecycle event isolation is implemented.
86+ expect ( await getLeakedBreadcrumbs ( baseURL ! ) ) . toContain ( 'leaked-breadcrumb-from-lifecycle-event' ) ;
9287} ) ;
9388
9489// TODO: @OnWorkerEvent ('active') handlers run outside the isolation scope created by process().
9590// Breadcrumbs set there leak into the default isolation scope and appear on subsequent HTTP requests.
9691test ( 'BullMQ @OnWorkerEvent active lifecycle breadcrumbs currently leak into subsequent HTTP requests' , async ( {
9792 baseURL,
9893} ) => {
99- const processTransactionPromise = waitForTransaction ( 'nestjs-bullmq' , transactionEvent => {
100- return transactionEvent . contexts ?. trace ?. op === 'queue.process' ;
101- } ) ;
94+ const processSpanPromise = waitForProcessSpan ( ) ;
10295
10396 await fetch ( `${ baseURL } /enqueue/lifecycle-active-breadcrumb-test` ) ;
10497
105- await processTransactionPromise ;
106-
107- const transactionPromise = waitForTransaction ( 'nestjs-bullmq' , transactionEvent => {
108- return transactionEvent . transaction === 'GET /check-isolation' ;
109- } ) ;
110-
111- await fetch ( `${ baseURL } /check-isolation` ) ;
98+ await processSpanPromise ;
11299
113- const transaction = await transactionPromise ;
114-
115- const leakedBreadcrumb = ( transaction . breadcrumbs || [ ] ) . find (
116- ( b : any ) => b . message === 'leaked-breadcrumb-from-active-event' ,
117- ) ;
118- // This SHOULD be toBeUndefined() once lifecycle event isolation is implemented.
119- expect ( leakedBreadcrumb ) . toBeDefined ( ) ;
100+ // This SHOULD be not.toContain() once lifecycle event isolation is implemented.
101+ expect ( await getLeakedBreadcrumbs ( baseURL ! ) ) . toContain ( 'leaked-breadcrumb-from-active-event' ) ;
120102} ) ;
121103
122104// TODO: @OnWorkerEvent ('failed') handlers run outside the isolation scope created by process().
123105// Breadcrumbs set there leak into the default isolation scope and appear on subsequent HTTP requests.
124106test ( 'BullMQ @OnWorkerEvent failed lifecycle breadcrumbs currently leak into subsequent HTTP requests' , async ( {
125107 baseURL,
126108} ) => {
127- const processTransactionPromise = waitForTransaction ( 'nestjs-bullmq' , transactionEvent => {
128- return transactionEvent . contexts ?. trace ?. op === 'queue.process' ;
129- } ) ;
109+ const processSpanPromise = waitForProcessSpan ( ) ;
130110
131111 await fetch ( `${ baseURL } /enqueue/lifecycle-failed-breadcrumb-test` ) ;
132112
133- await processTransactionPromise ;
134-
135- const transactionPromise = waitForTransaction ( 'nestjs-bullmq' , transactionEvent => {
136- return transactionEvent . transaction === 'GET /check-isolation' ;
137- } ) ;
138-
139- await fetch ( `${ baseURL } /check-isolation` ) ;
140-
141- const transaction = await transactionPromise ;
113+ await processSpanPromise ;
142114
143- const leakedBreadcrumb = ( transaction . breadcrumbs || [ ] ) . find (
144- ( b : any ) => b . message === 'leaked-breadcrumb-from-failed-event' ,
145- ) ;
146- // This SHOULD be toBeUndefined() once lifecycle event isolation is implemented.
147- expect ( leakedBreadcrumb ) . toBeDefined ( ) ;
115+ // This SHOULD be not.toContain() once lifecycle event isolation is implemented.
116+ expect ( await getLeakedBreadcrumbs ( baseURL ! ) ) . toContain ( 'leaked-breadcrumb-from-failed-event' ) ;
148117} ) ;
149118
150119// The 'progress' event does NOT leak breadcrumbs — unlike 'active', 'completed', and 'failed',
@@ -153,24 +122,11 @@ test('BullMQ @OnWorkerEvent failed lifecycle breadcrumbs currently leak into sub
153122test ( 'BullMQ @OnWorkerEvent progress lifecycle breadcrumbs do not leak into subsequent HTTP requests' , async ( {
154123 baseURL,
155124} ) => {
156- const processTransactionPromise = waitForTransaction ( 'nestjs-bullmq' , transactionEvent => {
157- return transactionEvent . contexts ?. trace ?. op === 'queue.process' ;
158- } ) ;
125+ const processSpanPromise = waitForProcessSpan ( ) ;
159126
160127 await fetch ( `${ baseURL } /enqueue/lifecycle-progress-breadcrumb-test` ) ;
161128
162- await processTransactionPromise ;
163-
164- const transactionPromise = waitForTransaction ( 'nestjs-bullmq' , transactionEvent => {
165- return transactionEvent . transaction === 'GET /check-isolation' ;
166- } ) ;
167-
168- await fetch ( `${ baseURL } /check-isolation` ) ;
169-
170- const transaction = await transactionPromise ;
129+ await processSpanPromise ;
171130
172- const leakedBreadcrumb = ( transaction . breadcrumbs || [ ] ) . find (
173- ( b : any ) => b . message === 'leaked-breadcrumb-from-progress-event' ,
174- ) ;
175- expect ( leakedBreadcrumb ) . toBeUndefined ( ) ;
131+ expect ( await getLeakedBreadcrumbs ( baseURL ! ) ) . not . toContain ( 'leaked-breadcrumb-from-progress-event' ) ;
176132} ) ;
0 commit comments