1
- import { describe , it , expect , vi } from 'vitest' ;
1
+ import { describe , it , expect , beforeEach , vi } from 'vitest' ;
2
2
import 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' ;
5
12
6
13
describe ( '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
+
8
17
const previousTraceInfo : PreviousTraceInfo = {
9
18
spanContext : {
10
19
traceId : '123' ,
11
20
spanId : '456' ,
12
21
traceFlags : 1 ,
13
22
} ,
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 ,
16
25
} ;
17
26
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
+ } ) ;
21
35
22
- const updatedPreviousTraceInfo = addPreviousTraceSpanLink ( previousTraceInfo , startSpanOptions ) ;
36
+ const updatedPreviousTraceInfo = addPreviousTraceSpanLink ( previousTraceInfo , currentSpan ) ;
23
37
24
- expect ( updatedPreviousTraceInfo ) . toBe ( previousTraceInfo ) ;
25
- expect ( startSpanOptions . links ) . toEqual ( [
38
+ expect ( spanToJSON ( currentSpan ) . links ) . toEqual ( [
26
39
{
27
40
attributes : {
28
41
'sentry.link.type' : 'previous_trace' ,
29
42
} ,
30
- context : {
31
- spanId : '456' ,
32
- traceFlags : 1 ,
33
- traceId : '123' ,
34
- } ,
43
+ trace_id : '123' ,
44
+ span_id : '456' ,
45
+ sampled : true ,
35
46
} ,
36
47
] ) ;
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
+ } ) ;
37
81
} ) ;
38
82
39
83
it ( "doesn't overwrite previously existing span links" , ( ) => {
@@ -46,7 +90,9 @@ describe('addPreviousTraceSpanLink', () => {
46
90
startTimestamp : Date . now ( ) / 1000 ,
47
91
} ;
48
92
49
- const startSpanOptions : StartSpanOptions = {
93
+ const currentSpanStart = timestampInSeconds ( ) ;
94
+
95
+ const currentSpan = new SentrySpan ( {
50
96
name : '/dashboard' ,
51
97
links : [
52
98
{
@@ -60,18 +106,16 @@ describe('addPreviousTraceSpanLink', () => {
60
106
} ,
61
107
} ,
62
108
] ,
63
- } ;
109
+ startTimestamp : currentSpanStart ,
110
+ } ) ;
64
111
65
- const updatedPreviousTraceInfo = addPreviousTraceSpanLink ( previousTraceInfo , startSpanOptions ) ;
112
+ const updatedPreviousTraceInfo = addPreviousTraceSpanLink ( previousTraceInfo , currentSpan ) ;
66
113
67
- expect ( updatedPreviousTraceInfo ) . toBe ( previousTraceInfo ) ;
68
- expect ( startSpanOptions . links ) . toEqual ( [
114
+ expect ( spanToJSON ( currentSpan ) . links ) . toEqual ( [
69
115
{
70
- context : {
71
- traceId : '789' ,
72
- spanId : '101' ,
73
- traceFlags : 1 ,
74
- } ,
116
+ trace_id : '789' ,
117
+ span_id : '101' ,
118
+ sampled : true ,
75
119
attributes : {
76
120
someKey : 'someValue' ,
77
121
} ,
@@ -80,34 +124,103 @@ describe('addPreviousTraceSpanLink', () => {
80
124
attributes : {
81
125
'sentry.link.type' : 'previous_trace' ,
82
126
} ,
83
- context : {
84
- spanId : '456' ,
85
- traceFlags : 1 ,
86
- traceId : '123' ,
87
- } ,
127
+ trace_id : '123' ,
128
+ span_id : '456' ,
129
+ sampled : true ,
88
130
} ,
89
131
] ) ;
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
+ } ) ;
90
151
} ) ;
91
152
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
+
93
162
const previousTraceInfo : PreviousTraceInfo = {
94
163
spanContext : {
95
164
traceId : '123' ,
96
165
spanId : '456' ,
97
- traceFlags : 0 ,
166
+ traceFlags : 1 ,
98
167
} ,
99
- startTimestamp : Date . now ( ) / 1000 - PREVIOUS_TRACE_MAX_DURATION - 1 ,
168
+ startTimestamp : currentSpanStart - 1 ,
100
169
} ;
101
170
102
- const startSpanOptions : StartSpanOptions = {
103
- name : '/dashboard' ,
104
- } ;
171
+ const updatedPreviousTraceInfo = addPreviousTraceSpanLink ( previousTraceInfo , currentSpan ) ;
105
172
106
- const updatedPreviousTraceInfo = addPreviousTraceSpanLink ( previousTraceInfo , startSpanOptions ) ;
173
+ expect ( spanToJSON ( currentSpan ) . links ) . toBeUndefined ( ) ;
107
174
108
- expect ( updatedPreviousTraceInfo ) . toBeUndefined ( ) ;
109
- expect ( startSpanOptions . links ) . toBeUndefined ( ) ;
175
+ expect ( updatedPreviousTraceInfo ) . toBe ( previousTraceInfo ) ;
110
176
} ) ;
111
177
} ) ;
112
178
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