@@ -14,6 +14,7 @@ describe("TraceContextService", () => {
1414 mockXRayShouldThrow = false ;
1515 const traceWrapper = {
1616 traceContext : ( ) => spanContextWrapper ,
17+ closeScope : jest . fn ( ) ,
1718 } ;
1819 traceContextService = new TraceContextService ( traceWrapper as any , { } as any ) ;
1920 } ) ;
@@ -126,4 +127,54 @@ describe("TraceContextService", () => {
126127 expect ( result ?. toTraceId ( ) ) . toBe ( "newTraceId" ) ;
127128 expect ( traceContextService . traceSource ) . toBe ( "event" ) ;
128129 } ) ;
130+
131+ it ( "should not leak dd-trace context from previous invocation when extract is called" , async ( ) => {
132+ // Simulate dd-trace having a stale active span from a previous invocation (warm start scenario)
133+ const staleDdTraceContext = {
134+ toTraceId : ( ) => "staleTraceId_999" ,
135+ toSpanId : ( ) => "staleSpanId_888" ,
136+ sampleMode : ( ) => 1 ,
137+ source : TraceSource . DdTrace ,
138+ spanContext : { } ,
139+ } ;
140+
141+ // Mock tracerWrapper that returns stale context initially, then null after closeScope is called
142+ let traceContextValue : any = staleDdTraceContext ;
143+ const mockCloseScopeFn = jest . fn ( ( ) => {
144+ // After closeScope is called, traceContext should return null
145+ traceContextValue = null ;
146+ } ) ;
147+
148+ const mockTracerWrapper = {
149+ traceContext : jest . fn ( ( ) => traceContextValue ) ,
150+ closeScope : mockCloseScopeFn ,
151+ } ;
152+
153+ const service = new TraceContextService ( mockTracerWrapper as any , { } as any ) ;
154+
155+ // Mock the extractor to return a NEW context for the current invocation
156+ const newEventContext = {
157+ toTraceId : ( ) => "newTraceId_123" ,
158+ toSpanId : ( ) => "newSpanId_456" ,
159+ sampleMode : ( ) => 2 ,
160+ source : TraceSource . Event ,
161+ spanContext : { } ,
162+ } ;
163+ const mockExtract = jest . fn ( ) . mockResolvedValue ( newEventContext ) ;
164+ service [ "traceExtractor" ] = { extract : mockExtract } as any ;
165+
166+ // Call extract for the new invocation
167+ await service . extract ( { } , { } as any ) ;
168+
169+ // Verify that closeScope was called to clear the stale context
170+ expect ( mockCloseScopeFn ) . toHaveBeenCalled ( ) ;
171+
172+ // After the fix: currentTraceHeaders should return the NEW context from the event
173+ // not the stale dd-trace context from the previous invocation
174+ const headers = service . currentTraceHeaders ;
175+
176+ expect ( headers [ "x-datadog-trace-id" ] ) . toBe ( "newTraceId_123" ) ;
177+ expect ( headers [ "x-datadog-parent-id" ] ) . toBe ( "newSpanId_456" ) ;
178+ expect ( headers [ "x-datadog-sampling-priority" ] ) . toBe ( "2" ) ;
179+ } ) ;
129180} ) ;
0 commit comments