@@ -99,7 +99,7 @@ describe('Integration | Transactions', () => {
9999 environment : 'production' ,
100100 event_id : expect . any ( String ) ,
101101 platform : 'node' ,
102- sdkProcessingMetadata : {
102+ sdkProcessingMetadata : expect . objectContaining ( {
103103 dynamicSamplingContext : expect . objectContaining ( {
104104 environment : 'production' ,
105105 public_key : expect . any ( String ) ,
@@ -112,7 +112,7 @@ describe('Integration | Transactions', () => {
112112 source : 'task' ,
113113 spanMetadata : expect . any ( Object ) ,
114114 requestPath : 'test-path' ,
115- } ,
115+ } ) ,
116116 server_name : expect . any ( String ) ,
117117 // spans are circular (they have a reference to the transaction), which leads to jest choking on this
118118 // instead we compare them in detail below
@@ -329,6 +329,159 @@ describe('Integration | Transactions', () => {
329329 ) ;
330330 } ) ;
331331
332+ it ( 'correctly creates concurrent transaction & spans when using native OTEL tracer' , async ( ) => {
333+ const beforeSendTransaction = jest . fn ( ( ) => null ) ;
334+
335+ mockSdkInit ( { enableTracing : true , beforeSendTransaction } ) ;
336+
337+ const client = Sentry . getClient < NodeExperimentalClient > ( ) ;
338+
339+ Sentry . addBreadcrumb ( { message : 'test breadcrumb 1' , timestamp : 123456 } ) ;
340+
341+ Sentry . withIsolationScope ( ( ) => {
342+ client . tracer . startActiveSpan ( 'test name' , span => {
343+ Sentry . addBreadcrumb ( { message : 'test breadcrumb 2' , timestamp : 123456 } ) ;
344+
345+ span . setAttributes ( {
346+ 'test.outer' : 'test value' ,
347+ } ) ;
348+
349+ const subSpan = Sentry . startInactiveSpan ( { name : 'inner span 1' } ) ;
350+ subSpan . end ( ) ;
351+
352+ Sentry . setTag ( 'test.tag' , 'test value' ) ;
353+
354+ client . tracer . startActiveSpan ( 'inner span 2' , innerSpan => {
355+ Sentry . addBreadcrumb ( { message : 'test breadcrumb 3' , timestamp : 123456 } ) ;
356+
357+ innerSpan . setAttributes ( {
358+ 'test.inner' : 'test value' ,
359+ } ) ;
360+
361+ innerSpan . end ( ) ;
362+ } ) ;
363+
364+ span . end ( ) ;
365+ } ) ;
366+ } ) ;
367+
368+ Sentry . withIsolationScope ( ( ) => {
369+ client . tracer . startActiveSpan ( 'test name b' , span => {
370+ Sentry . addBreadcrumb ( { message : 'test breadcrumb 2b' , timestamp : 123456 } ) ;
371+
372+ span . setAttributes ( {
373+ 'test.outer' : 'test value b' ,
374+ } ) ;
375+
376+ const subSpan = Sentry . startInactiveSpan ( { name : 'inner span 1b' } ) ;
377+ subSpan . end ( ) ;
378+
379+ Sentry . setTag ( 'test.tag' , 'test value b' ) ;
380+
381+ client . tracer . startActiveSpan ( 'inner span 2b' , innerSpan => {
382+ Sentry . addBreadcrumb ( { message : 'test breadcrumb 3b' , timestamp : 123456 } ) ;
383+
384+ innerSpan . setAttributes ( {
385+ 'test.inner' : 'test value b' ,
386+ } ) ;
387+
388+ innerSpan . end ( ) ;
389+ } ) ;
390+
391+ span . end ( ) ;
392+ } ) ;
393+ } ) ;
394+
395+ await client . flush ( ) ;
396+
397+ expect ( beforeSendTransaction ) . toHaveBeenCalledTimes ( 2 ) ;
398+ expect ( beforeSendTransaction ) . toHaveBeenCalledWith (
399+ expect . objectContaining ( {
400+ breadcrumbs : [
401+ { message : 'test breadcrumb 1' , timestamp : 123456 } ,
402+ { message : 'test breadcrumb 2' , timestamp : 123456 } ,
403+ { message : 'test breadcrumb 3' , timestamp : 123456 } ,
404+ ] ,
405+ contexts : expect . objectContaining ( {
406+ otel : expect . objectContaining ( {
407+ attributes : {
408+ 'test.outer' : 'test value' ,
409+ } ,
410+ } ) ,
411+ trace : {
412+ data : {
413+ 'otel.kind' : 'INTERNAL' ,
414+ 'sentry.origin' : 'manual' ,
415+ } ,
416+ span_id : expect . any ( String ) ,
417+ status : 'ok' ,
418+ trace_id : expect . any ( String ) ,
419+ origin : 'manual' ,
420+ } ,
421+ } ) ,
422+ spans : [
423+ expect . objectContaining ( {
424+ description : 'inner span 1' ,
425+ } ) ,
426+ expect . objectContaining ( {
427+ description : 'inner span 2' ,
428+ } ) ,
429+ ] ,
430+ start_timestamp : expect . any ( Number ) ,
431+ tags : { 'test.tag' : 'test value' } ,
432+ timestamp : expect . any ( Number ) ,
433+ transaction : 'test name' ,
434+ type : 'transaction' ,
435+ } ) ,
436+ {
437+ event_id : expect . any ( String ) ,
438+ } ,
439+ ) ;
440+
441+ expect ( beforeSendTransaction ) . toHaveBeenCalledWith (
442+ expect . objectContaining ( {
443+ breadcrumbs : [
444+ { message : 'test breadcrumb 1' , timestamp : 123456 } ,
445+ { message : 'test breadcrumb 2b' , timestamp : 123456 } ,
446+ { message : 'test breadcrumb 3b' , timestamp : 123456 } ,
447+ ] ,
448+ contexts : expect . objectContaining ( {
449+ otel : expect . objectContaining ( {
450+ attributes : {
451+ 'test.outer' : 'test value b' ,
452+ } ,
453+ } ) ,
454+ trace : {
455+ data : {
456+ 'otel.kind' : 'INTERNAL' ,
457+ 'sentry.origin' : 'manual' ,
458+ } ,
459+ span_id : expect . any ( String ) ,
460+ status : 'ok' ,
461+ trace_id : expect . any ( String ) ,
462+ origin : 'manual' ,
463+ } ,
464+ } ) ,
465+ spans : [
466+ expect . objectContaining ( {
467+ description : 'inner span 1b' ,
468+ } ) ,
469+ expect . objectContaining ( {
470+ description : 'inner span 2b' ,
471+ } ) ,
472+ ] ,
473+ start_timestamp : expect . any ( Number ) ,
474+ tags : { 'test.tag' : 'test value b' } ,
475+ timestamp : expect . any ( Number ) ,
476+ transaction : 'test name b' ,
477+ type : 'transaction' ,
478+ } ) ,
479+ {
480+ event_id : expect . any ( String ) ,
481+ } ,
482+ ) ;
483+ } ) ;
484+
332485 it ( 'correctly creates transaction & spans with a trace header data' , async ( ) => {
333486 const beforeSendTransaction = jest . fn ( ( ) => null ) ;
334487
0 commit comments