1- import { describe , it , expect , vi } from 'vitest' ;
1+ import { describe , it , expect , beforeEach , vi } from 'vitest' ;
22import type { PreviousTraceInfo } from '../../src/tracing/previousTrace' ;
3- import { addPreviousTraceSpanLink , PREVIOUS_TRACE_MAX_DURATION } from '../../src/tracing/previousTrace' ;
4- import type { StartSpanOptions } from '@sentry/core' ;
3+ import {
4+ addPreviousTraceSpanLink ,
5+ getPreviousTraceFromSessionStorage ,
6+ PREVIOUS_TRACE_KEY ,
7+ PREVIOUS_TRACE_MAX_DURATION ,
8+ } from '../../src/tracing/previousTrace' ;
9+ import { SentrySpan , spanToJSON , timestampInSeconds , type StartSpanOptions } from '@sentry/core' ;
10+ import { storePreviousTraceInSessionStorage } from '../../src/tracing/previousTrace' ;
11+ import { get } from 'http' ;
512
613describe ( 'addPreviousTraceSpanLink' , ( ) => {
7- it ( `adds a previous_trace span link to startSpanOptions if the previous trace was created within ${ PREVIOUS_TRACE_MAX_DURATION } M` , ( ) => {
14+ it ( `adds a previous_trace span link to startSpanOptions if the previous trace was created within ${ PREVIOUS_TRACE_MAX_DURATION } s` , ( ) => {
15+ const currentSpanStart = timestampInSeconds ( ) ;
16+
817 const previousTraceInfo : PreviousTraceInfo = {
918 spanContext : {
1019 traceId : '123' ,
1120 spanId : '456' ,
1221 traceFlags : 1 ,
1322 } ,
14- // max time reached exactly
15- startTimestamp : Date . now ( ) / 1000 - PREVIOUS_TRACE_MAX_DURATION ,
23+ // max time reached almost exactly
24+ startTimestamp : currentSpanStart - PREVIOUS_TRACE_MAX_DURATION + 1 ,
1625 } ;
1726
18- const startSpanOptions : StartSpanOptions = {
19- name : '/dashboard' ,
20- } ;
27+ const currentSpan = new SentrySpan ( {
28+ name : 'test' ,
29+ startTimestamp : currentSpanStart ,
30+ parentSpanId : '789' ,
31+ spanId : 'abc' ,
32+ traceId : 'def' ,
33+ sampled : true ,
34+ } ) ;
2135
22- const updatedPreviousTraceInfo = addPreviousTraceSpanLink ( previousTraceInfo , startSpanOptions ) ;
36+ const updatedPreviousTraceInfo = addPreviousTraceSpanLink ( previousTraceInfo , currentSpan ) ;
2337
24- expect ( updatedPreviousTraceInfo ) . toBe ( previousTraceInfo ) ;
25- expect ( startSpanOptions . links ) . toEqual ( [
38+ expect ( spanToJSON ( currentSpan ) . links ) . toEqual ( [
2639 {
2740 attributes : {
2841 'sentry.link.type' : 'previous_trace' ,
2942 } ,
30- context : {
31- spanId : '456' ,
32- traceFlags : 1 ,
33- traceId : '123' ,
34- } ,
43+ trace_id : '123' ,
44+ span_id : '456' ,
45+ sampled : true ,
3546 } ,
3647 ] ) ;
48+
49+ expect ( updatedPreviousTraceInfo ) . toEqual ( {
50+ spanContext : currentSpan . spanContext ( ) ,
51+ startTimestamp : currentSpanStart ,
52+ } ) ;
53+ } ) ;
54+
55+ it ( `doesn't add a previous_trace span link if the previous trace was created more than ${ PREVIOUS_TRACE_MAX_DURATION } s ago` , ( ) => {
56+ const currentSpanStart = timestampInSeconds ( ) ;
57+
58+ const previousTraceInfo : PreviousTraceInfo = {
59+ spanContext : {
60+ traceId : '123' ,
61+ spanId : '456' ,
62+ traceFlags : 0 ,
63+ } ,
64+ startTimestamp : Date . now ( ) / 1000 - PREVIOUS_TRACE_MAX_DURATION - 1 ,
65+ } ;
66+
67+ const currentSpan = new SentrySpan ( {
68+ name : '/dashboard' ,
69+ startTimestamp : currentSpanStart ,
70+ } ) ;
71+
72+ const updatedPreviousTraceInfo = addPreviousTraceSpanLink ( previousTraceInfo , currentSpan ) ;
73+
74+ expect ( spanToJSON ( currentSpan ) . links ) . toBeUndefined ( ) ;
75+
76+ // but still updates the previousTraceInfo to the current span
77+ expect ( updatedPreviousTraceInfo ) . toEqual ( {
78+ spanContext : currentSpan . spanContext ( ) ,
79+ startTimestamp : currentSpanStart ,
80+ } ) ;
3781 } ) ;
3882
3983 it ( "doesn't overwrite previously existing span links" , ( ) => {
@@ -46,7 +90,9 @@ describe('addPreviousTraceSpanLink', () => {
4690 startTimestamp : Date . now ( ) / 1000 ,
4791 } ;
4892
49- const startSpanOptions : StartSpanOptions = {
93+ const currentSpanStart = timestampInSeconds ( ) ;
94+
95+ const currentSpan = new SentrySpan ( {
5096 name : '/dashboard' ,
5197 links : [
5298 {
@@ -60,18 +106,16 @@ describe('addPreviousTraceSpanLink', () => {
60106 } ,
61107 } ,
62108 ] ,
63- } ;
109+ startTimestamp : currentSpanStart ,
110+ } ) ;
64111
65- const updatedPreviousTraceInfo = addPreviousTraceSpanLink ( previousTraceInfo , startSpanOptions ) ;
112+ const updatedPreviousTraceInfo = addPreviousTraceSpanLink ( previousTraceInfo , currentSpan ) ;
66113
67- expect ( updatedPreviousTraceInfo ) . toBe ( previousTraceInfo ) ;
68- expect ( startSpanOptions . links ) . toEqual ( [
114+ expect ( spanToJSON ( currentSpan ) . links ) . toEqual ( [
69115 {
70- context : {
71- traceId : '789' ,
72- spanId : '101' ,
73- traceFlags : 1 ,
74- } ,
116+ trace_id : '789' ,
117+ span_id : '101' ,
118+ sampled : true ,
75119 attributes : {
76120 someKey : 'someValue' ,
77121 } ,
@@ -80,34 +124,103 @@ describe('addPreviousTraceSpanLink', () => {
80124 attributes : {
81125 'sentry.link.type' : 'previous_trace' ,
82126 } ,
83- context : {
84- spanId : '456' ,
85- traceFlags : 1 ,
86- traceId : '123' ,
87- } ,
127+ trace_id : '123' ,
128+ span_id : '456' ,
129+ sampled : true ,
88130 } ,
89131 ] ) ;
132+
133+ expect ( updatedPreviousTraceInfo ) . toEqual ( {
134+ spanContext : currentSpan . spanContext ( ) ,
135+ startTimestamp : currentSpanStart ,
136+ } ) ;
137+ } ) ;
138+
139+ it ( "doesn't add a link and returns the current span's data as previous trace info, if previous trace info was undefined" , ( ) => {
140+ const currentSpanStart = timestampInSeconds ( ) ;
141+ const currentSpan = new SentrySpan ( { name : 'test' , startTimestamp : currentSpanStart } ) ;
142+
143+ const updatedPreviousTraceInfo = addPreviousTraceSpanLink ( undefined , currentSpan ) ;
144+
145+ expect ( spanToJSON ( currentSpan ) . links ) . toBeUndefined ( ) ;
146+
147+ expect ( updatedPreviousTraceInfo ) . toEqual ( {
148+ spanContext : currentSpan . spanContext ( ) ,
149+ startTimestamp : currentSpanStart ,
150+ } ) ;
90151 } ) ;
91152
92- it ( `doesn't add a previous_trace span link if the previous trace was created more than ${ PREVIOUS_TRACE_MAX_DURATION } M ago` , ( ) => {
153+ it ( "doesn't add a link and returns the unchanged previous trace info if the current span is part of the same trace" , ( ) => {
154+ const currentSpanStart = timestampInSeconds ( ) ;
155+ const currentSpan = new SentrySpan ( {
156+ name : 'test' ,
157+ startTimestamp : currentSpanStart ,
158+ traceId : '123' ,
159+ spanId : '456' ,
160+ } ) ;
161+
93162 const previousTraceInfo : PreviousTraceInfo = {
94163 spanContext : {
95164 traceId : '123' ,
96165 spanId : '456' ,
97- traceFlags : 0 ,
166+ traceFlags : 1 ,
98167 } ,
99- startTimestamp : Date . now ( ) / 1000 - PREVIOUS_TRACE_MAX_DURATION - 1 ,
168+ startTimestamp : currentSpanStart - 1 ,
100169 } ;
101170
102- const startSpanOptions : StartSpanOptions = {
103- name : '/dashboard' ,
104- } ;
171+ const updatedPreviousTraceInfo = addPreviousTraceSpanLink ( previousTraceInfo , currentSpan ) ;
105172
106- const updatedPreviousTraceInfo = addPreviousTraceSpanLink ( previousTraceInfo , startSpanOptions ) ;
173+ expect ( spanToJSON ( currentSpan ) . links ) . toBeUndefined ( ) ;
107174
108- expect ( updatedPreviousTraceInfo ) . toBeUndefined ( ) ;
109- expect ( startSpanOptions . links ) . toBeUndefined ( ) ;
175+ expect ( updatedPreviousTraceInfo ) . toBe ( previousTraceInfo ) ;
110176 } ) ;
111177} ) ;
112178
113- // TODO: Add tests for sessionstorage helpers
179+ describe ( 'store and retrieve previous trace data via sessionStorage ' , ( ) => {
180+ const storage : Record < string , unknown > = { } ;
181+ const sessionStorageMock = {
182+ setItem : vi . fn ( ( key , value ) => {
183+ storage [ key ] = value ;
184+ } ) ,
185+ getItem : vi . fn ( key => storage [ key ] ) ,
186+ } ;
187+
188+ beforeEach ( ( ) => {
189+ vi . clearAllMocks ( ) ;
190+ // @ts -expect-error - mock contains only necessary API
191+ globalThis . sessionStorage = sessionStorageMock ;
192+ } ) ;
193+
194+ it ( 'stores the previous trace info in sessionStorage' , ( ) => {
195+ const previousTraceInfo : PreviousTraceInfo = {
196+ spanContext : {
197+ traceId : '123' ,
198+ spanId : '456' ,
199+ traceFlags : 1 ,
200+ } ,
201+ startTimestamp : Date . now ( ) / 1000 ,
202+ } ;
203+
204+ storePreviousTraceInSessionStorage ( previousTraceInfo ) ;
205+ expect ( sessionStorageMock . setItem ) . toHaveBeenCalledWith ( PREVIOUS_TRACE_KEY , JSON . stringify ( previousTraceInfo ) ) ;
206+ expect ( getPreviousTraceFromSessionStorage ( ) ) . toEqual ( previousTraceInfo ) ;
207+ } ) ;
208+
209+ it ( "doesn't throw if accessing sessionStorage fails and returns undefined" , ( ) => {
210+ // @ts -expect-error - this is fine
211+ globalThis . sessionStorage = undefined ;
212+
213+ const previousTraceInfo : PreviousTraceInfo = {
214+ spanContext : {
215+ traceId : '123' ,
216+ spanId : '456' ,
217+ traceFlags : 1 ,
218+ } ,
219+ startTimestamp : Date . now ( ) / 1000 ,
220+ } ;
221+
222+ expect ( ( ) => storePreviousTraceInSessionStorage ( previousTraceInfo ) ) . not . toThrow ( ) ;
223+ expect ( getPreviousTraceFromSessionStorage ) . not . toThrow ( ) ;
224+ expect ( getPreviousTraceFromSessionStorage ( ) ) . toBeUndefined ( ) ;
225+ } ) ;
226+ } ) ;
0 commit comments