1+ import type { MockedFunction } from 'vitest' ;
12import { describe , beforeEach , vi , expect , it } from 'vitest' ;
23import type { UndiciRequest } from '../../src/integrations/node-fetch/types' ;
34import { addTracePropagationHeadersToFetchRequest } from '../../src/utils/outgoingFetchRequest' ;
45import { LRUMap } from '@sentry/core' ;
56import * as SentryCore from '@sentry/core' ;
67
7- const mockedGetTraceData = vi . hoisted ( ( ) =>
8+ const mockedGetTraceData : MockedFunction < ( ) => ReturnType < typeof SentryCore . getTraceData > > = vi . hoisted ( ( ) =>
89 vi . fn ( ( ) => ( {
910 'sentry-trace' : 'trace_id_1-span_id_1-1' ,
1011 baggage : 'sentry-trace_id=trace_id_1,sentry-sampled=true,sentry-environment=staging' ,
1112 } ) ) ,
1213) ;
1314
15+ const mockedClientGetOptions : MockedFunction < ( ) => Partial < SentryCore . ClientOptions > > = vi . hoisted ( ( ) =>
16+ vi . fn ( ( ) => ( {
17+ tracePropagationTargets : [ 'https://example.com' ] ,
18+ propagateTraceparent : true ,
19+ } ) ) ,
20+ ) ;
21+
1422vi . mock ( '@sentry/core' , async ( ) => {
1523 const actual = await vi . importActual ( '@sentry/core' ) ;
1624 return {
1725 ...actual ,
1826 getClient : vi . fn ( ( ) => ( {
19- getOptions : vi . fn ( ( ) => ( {
20- tracePropagationTargets : [ 'https://example.com' ] ,
21- } ) ) ,
27+ getOptions : mockedClientGetOptions ,
2228 } ) ) ,
2329 shouldPropagateTraceForUrl : ( ) => true ,
2430 getTraceData : mockedGetTraceData ,
@@ -62,6 +68,31 @@ describe('addTracePropagationHeadersToFetchRequest', () => {
6268 ] ) ;
6369 } ) ;
6470
71+ it ( 'adds sentry-trace, baggage and traceparent headers to request' , ( ) => {
72+ mockedGetTraceData . mockReturnValueOnce ( {
73+ 'sentry-trace' : 'trace_id_1-span_id_1-1' ,
74+ baggage : 'sentry-trace_id=trace_id_1,sentry-sampled=true,sentry-environment=staging' ,
75+ traceparent : '00-trace_id_1-span_id_1-01' ,
76+ } ) ;
77+
78+ const request = {
79+ headers : [ ] as string [ ] ,
80+ origin : 'https://some-service.com' ,
81+ path : '/api/test' ,
82+ } as UndiciRequest ;
83+
84+ addTracePropagationHeadersToFetchRequest ( request , new LRUMap < string , boolean > ( 100 ) ) ;
85+
86+ expect ( request . headers ) . toEqual ( [
87+ 'sentry-trace' ,
88+ 'trace_id_1-span_id_1-1' ,
89+ 'traceparent' ,
90+ '00-trace_id_1-span_id_1-01' ,
91+ 'baggage' ,
92+ 'sentry-trace_id=trace_id_1,sentry-sampled=true,sentry-environment=staging' ,
93+ ] ) ;
94+ } ) ;
95+
6596 it ( 'preserves non-sentry entries in existing baggage header' , ( ) => {
6697 const request = {
6798 headers : [ 'baggage' , 'other=entry,not=sentry' ] ,
@@ -79,6 +110,31 @@ describe('addTracePropagationHeadersToFetchRequest', () => {
79110 ] ) ;
80111 } ) ;
81112
113+ it ( 'preserves pre-existing traceparent header' , ( ) => {
114+ mockedGetTraceData . mockReturnValueOnce ( {
115+ 'sentry-trace' : 'trace_id_1-span_id_1-1' ,
116+ baggage : 'sentry-trace_id=trace_id_1,sentry-sampled=true,sentry-environment=staging' ,
117+ traceparent : '00-trace_id_1-span_id_1-01' ,
118+ } ) ;
119+
120+ const request = {
121+ headers : [ 'traceparent' , '00-some-other-trace_id-span_id_x-01' ] ,
122+ origin : 'https://some-service.com' ,
123+ path : '/api/test' ,
124+ } as UndiciRequest ;
125+
126+ addTracePropagationHeadersToFetchRequest ( request , new LRUMap < string , boolean > ( 100 ) ) ;
127+
128+ expect ( request . headers ) . toEqual ( [
129+ 'traceparent' ,
130+ '00-some-other-trace_id-span_id_x-01' ,
131+ 'sentry-trace' ,
132+ 'trace_id_1-span_id_1-1' ,
133+ 'baggage' ,
134+ 'sentry-trace_id=trace_id_1,sentry-sampled=true,sentry-environment=staging' ,
135+ ] ) ;
136+ } ) ;
137+
82138 describe ( 'when sentry-trace is already set' , ( ) => {
83139 it ( "preserves original sentry-trace header doesn't add baggage" , ( ) => {
84140 const request = {
@@ -113,6 +169,24 @@ describe('addTracePropagationHeadersToFetchRequest', () => {
113169 'sentry-trace_id=trace_id_2,sentry-sampled=true,sentry-environment=staging' ,
114170 ] ) ;
115171 } ) ;
172+
173+ it ( "doesn't add traceparent header even if propagateTraceparent is true" , ( ) => {
174+ mockedGetTraceData . mockReturnValueOnce ( {
175+ 'sentry-trace' : 'trace_id_2-span_id_2-1' ,
176+ baggage : 'sentry-trace_id=trace_id_2,sentry-sampled=true,sentry-environment=staging' ,
177+ traceparent : '00-trace_id_2-span_id_2-01' ,
178+ } ) ;
179+
180+ const request = {
181+ headers : [ 'sentry-trace' , 'trace_id_2-span_id_2-1' ] ,
182+ origin : 'https://some-service.com' ,
183+ path : '/api/test' ,
184+ } as UndiciRequest ;
185+
186+ addTracePropagationHeadersToFetchRequest ( request , new LRUMap < string , boolean > ( 100 ) ) ;
187+
188+ expect ( request . headers ) . toEqual ( [ 'sentry-trace' , 'trace_id_2-span_id_2-1' ] ) ;
189+ } ) ;
116190 } ) ;
117191
118192 describe ( 'pre-existing header deduplication' , ( ) => {
@@ -142,6 +216,43 @@ describe('addTracePropagationHeadersToFetchRequest', () => {
142216 ] ) ;
143217 } ) ;
144218
219+ it ( 'deduplicates traceparent headers if propagateTraceparent is true' , ( ) => {
220+ mockedClientGetOptions . mockReturnValueOnce ( {
221+ tracePropagationTargets : [ 'https://example.com' ] ,
222+ propagateTraceparent : true ,
223+ } ) ;
224+
225+ const request = {
226+ headers : [
227+ 'sentry-trace' ,
228+ 'user-trace_id-xyz-1' ,
229+ 'baggage' ,
230+ 'sentry-trace_id=user-trace_id-xyz-1,sentry-sampled=true,sentry-environment=user' ,
231+ 'traceparent' ,
232+ '00-user-trace_id-xyz-1-01' ,
233+ 'sentry-trace' ,
234+ 'undici-trace_id-abc-1' ,
235+ 'baggage' ,
236+ 'sentry-trace_id=undici-trace_id-abc-1,sentry-sampled=true,sentry-environment=undici' ,
237+ 'traceparent' ,
238+ '00-undici-trace_id-abc-1-01' ,
239+ ] ,
240+ origin : 'https://some-service.com' ,
241+ path : '/api/test' ,
242+ } as UndiciRequest ;
243+
244+ addTracePropagationHeadersToFetchRequest ( request , new LRUMap < string , boolean > ( 100 ) ) ;
245+
246+ expect ( request . headers ) . toEqual ( [
247+ 'sentry-trace' ,
248+ 'user-trace_id-xyz-1' ,
249+ 'baggage' ,
250+ 'sentry-trace_id=user-trace_id-xyz-1,sentry-sampled=true,sentry-environment=user' ,
251+ 'traceparent' ,
252+ '00-user-trace_id-xyz-1-01' ,
253+ ] ) ;
254+ } ) ;
255+
145256 // admittedly an unrealistic edge case but doesn't hurt to test it
146257 it ( "doesn't crash with incomplete headers array" , ( ) => {
147258 const request = {
@@ -215,6 +326,44 @@ describe('addTracePropagationHeadersToFetchRequest', () => {
215326 ] ) ;
216327 } ) ;
217328 } ) ;
329+
330+ it ( 'doesn\'t mistake a header value with "sentry-trace" for a sentry-trace header' , ( ) => {
331+ const request = {
332+ headers : [ 'x-allow-header' , 'sentry-trace' ] ,
333+ origin : 'https://some-service.com' ,
334+ path : '/api/test' ,
335+ } as UndiciRequest ;
336+
337+ addTracePropagationHeadersToFetchRequest ( request , new LRUMap < string , boolean > ( 100 ) ) ;
338+
339+ expect ( request . headers ) . toEqual ( [
340+ 'x-allow-header' ,
341+ 'sentry-trace' ,
342+ 'sentry-trace' ,
343+ 'trace_id_1-span_id_1-1' ,
344+ 'baggage' ,
345+ 'sentry-trace_id=trace_id_1,sentry-sampled=true,sentry-environment=staging' ,
346+ ] ) ;
347+ } ) ;
348+
349+ it ( 'doesn\'t mistake a header value with "baggage" for a sentry-trace header' , ( ) => {
350+ const request = {
351+ headers : [ 'x-allow-header' , 'baggage' ] ,
352+ origin : 'https://some-service.com' ,
353+ path : '/api/test' ,
354+ } as UndiciRequest ;
355+
356+ addTracePropagationHeadersToFetchRequest ( request , new LRUMap < string , boolean > ( 100 ) ) ;
357+
358+ expect ( request . headers ) . toEqual ( [
359+ 'x-allow-header' ,
360+ 'baggage' ,
361+ 'sentry-trace' ,
362+ 'trace_id_1-span_id_1-1' ,
363+ 'baggage' ,
364+ 'sentry-trace_id=trace_id_1,sentry-sampled=true,sentry-environment=staging' ,
365+ ] ) ;
366+ } ) ;
218367 } ) ;
219368
220369 describe ( 'when headers are a string' , ( ) => {
0 commit comments