1
- import { beforeAll , beforeEach , describe , expect , test } from 'bun:test' ;
1
+ import { afterEach , beforeAll , beforeEach , describe , expect , test } from 'bun:test' ;
2
+ import type { Span } from '@sentry/core' ;
2
3
import { getDynamicSamplingContextFromSpan , setCurrentClient , spanIsSampled , spanToJSON } from '@sentry/core' ;
3
4
4
5
import { BunClient } from '../../src/client' ;
5
6
import { instrumentBunServe } from '../../src/integrations/bunserver' ;
6
7
import { getDefaultBunClientOptions } from '../helpers' ;
7
8
8
- // Fun fact: Bun = 2 21 14 :)
9
- const DEFAULT_PORT = 22114 ;
10
-
11
9
describe ( 'Bun Serve Integration' , ( ) => {
12
10
let client : BunClient ;
11
+ // Fun fact: Bun = 2 21 14 :)
12
+ let port : number = 22114 ;
13
13
14
14
beforeAll ( ( ) => {
15
15
instrumentBunServe ( ) ;
16
16
} ) ;
17
17
18
18
beforeEach ( ( ) => {
19
- const options = getDefaultBunClientOptions ( { tracesSampleRate : 1 , debug : true } ) ;
19
+ const options = getDefaultBunClientOptions ( { tracesSampleRate : 1 } ) ;
20
20
client = new BunClient ( options ) ;
21
21
setCurrentClient ( client ) ;
22
22
client . init ( ) ;
23
23
} ) ;
24
24
25
+ afterEach ( ( ) => {
26
+ // Don't reuse the port; Bun server stops lazily so tests may accidentally hit a server still closing from a
27
+ // previous test
28
+ port += 1 ;
29
+ } ) ;
30
+
25
31
test ( 'generates a transaction around a request' , async ( ) => {
32
+ let generatedSpan : Span | undefined ;
33
+
26
34
client . on ( 'spanEnd' , span => {
27
- expect ( spanToJSON ( span ) . status ) . toBe ( 'ok' ) ;
28
- expect ( spanToJSON ( span ) . data ?. [ 'http.response.status_code' ] ) . toEqual ( 200 ) ;
29
- expect ( spanToJSON ( span ) . op ) . toEqual ( 'http.server' ) ;
30
- expect ( spanToJSON ( span ) . description ) . toEqual ( 'GET /' ) ;
35
+ generatedSpan = span ;
31
36
} ) ;
32
37
33
38
const server = Bun . serve ( {
34
39
async fetch ( _req ) {
35
40
return new Response ( 'Bun!' ) ;
36
41
} ,
37
- port : DEFAULT_PORT ,
42
+ port,
38
43
} ) ;
44
+ await fetch ( `http://localhost:${ port } /` ) ;
45
+ server . stop ( ) ;
39
46
40
- await fetch ( 'http://localhost:22114/' ) ;
47
+ if ( ! generatedSpan ) {
48
+ throw 'No span was generated in the test' ;
49
+ }
41
50
42
- server . stop ( ) ;
51
+ expect ( spanToJSON ( generatedSpan ) . status ) . toBe ( 'ok' ) ;
52
+ expect ( spanToJSON ( generatedSpan ) . data ?. [ 'http.response.status_code' ] ) . toEqual ( 200 ) ;
53
+ expect ( spanToJSON ( generatedSpan ) . op ) . toEqual ( 'http.server' ) ;
54
+ expect ( spanToJSON ( generatedSpan ) . description ) . toEqual ( 'GET /' ) ;
43
55
} ) ;
44
56
45
57
test ( 'generates a post transaction' , async ( ) => {
58
+ let generatedSpan : Span | undefined ;
59
+
46
60
client . on ( 'spanEnd' , span => {
47
- expect ( spanToJSON ( span ) . status ) . toBe ( 'ok' ) ;
48
- expect ( spanToJSON ( span ) . data ?. [ 'http.response.status_code' ] ) . toEqual ( 200 ) ;
49
- expect ( spanToJSON ( span ) . op ) . toEqual ( 'http.server' ) ;
50
- expect ( spanToJSON ( span ) . description ) . toEqual ( 'POST /' ) ;
61
+ generatedSpan = span ;
51
62
} ) ;
52
63
53
64
const server = Bun . serve ( {
54
65
async fetch ( _req ) {
55
66
return new Response ( 'Bun!' ) ;
56
67
} ,
57
- port : DEFAULT_PORT ,
68
+ port,
58
69
} ) ;
59
70
60
- await fetch ( ' http://localhost:22114/' , {
71
+ await fetch ( ` http://localhost:${ port } /` , {
61
72
method : 'POST' ,
62
73
} ) ;
63
74
64
75
server . stop ( ) ;
76
+
77
+ if ( ! generatedSpan ) {
78
+ throw 'No span was generated in the test' ;
79
+ }
80
+
81
+ expect ( spanToJSON ( generatedSpan ) . status ) . toBe ( 'ok' ) ;
82
+ expect ( spanToJSON ( generatedSpan ) . data ?. [ 'http.response.status_code' ] ) . toEqual ( 200 ) ;
83
+ expect ( spanToJSON ( generatedSpan ) . op ) . toEqual ( 'http.server' ) ;
84
+ expect ( spanToJSON ( generatedSpan ) . description ) . toEqual ( 'POST /' ) ;
65
85
} ) ;
66
86
67
87
test ( 'continues a trace' , async ( ) => {
@@ -70,55 +90,93 @@ describe('Bun Serve Integration', () => {
70
90
const PARENT_SAMPLED = '1' ;
71
91
72
92
const SENTRY_TRACE_HEADER = `${ TRACE_ID } -${ PARENT_SPAN_ID } -${ PARENT_SAMPLED } ` ;
73
- const SENTRY_BAGGAGE_HEADER = 'sentry-version=1.0,sentry-environment=production' ;
93
+ const SENTRY_BAGGAGE_HEADER = 'sentry-version=1.0,sentry-sample_rand=0.42,sentry- environment=production' ;
74
94
75
- client . on ( 'spanEnd' , span => {
76
- expect ( span . spanContext ( ) . traceId ) . toBe ( TRACE_ID ) ;
77
- expect ( spanToJSON ( span ) . parent_span_id ) . toBe ( PARENT_SPAN_ID ) ;
78
- expect ( spanIsSampled ( span ) ) . toBe ( true ) ;
79
- expect ( span . isRecording ( ) ) . toBe ( false ) ;
95
+ let generatedSpan : Span | undefined ;
80
96
81
- expect ( getDynamicSamplingContextFromSpan ( span ) ) . toStrictEqual ( {
82
- version : '1.0' ,
83
- environment : 'production' ,
84
- } ) ;
97
+ client . on ( 'spanEnd' , span => {
98
+ generatedSpan = span ;
85
99
} ) ;
86
100
87
101
const server = Bun . serve ( {
88
102
async fetch ( _req ) {
89
103
return new Response ( 'Bun!' ) ;
90
104
} ,
91
- port : DEFAULT_PORT ,
105
+ port,
92
106
} ) ;
93
107
94
- await fetch ( ' http://localhost:22114/' , {
108
+ await fetch ( ` http://localhost:${ port } /` , {
95
109
headers : { 'sentry-trace' : SENTRY_TRACE_HEADER , baggage : SENTRY_BAGGAGE_HEADER } ,
96
110
} ) ;
97
111
98
112
server . stop ( ) ;
113
+
114
+ if ( ! generatedSpan ) {
115
+ throw 'No span was generated in the test' ;
116
+ }
117
+
118
+ expect ( generatedSpan . spanContext ( ) . traceId ) . toBe ( TRACE_ID ) ;
119
+ expect ( spanToJSON ( generatedSpan ) . parent_span_id ) . toBe ( PARENT_SPAN_ID ) ;
120
+ expect ( spanIsSampled ( generatedSpan ) ) . toBe ( true ) ;
121
+ expect ( generatedSpan . isRecording ( ) ) . toBe ( false ) ;
122
+
123
+ expect ( getDynamicSamplingContextFromSpan ( generatedSpan ) ) . toStrictEqual ( {
124
+ version : '1.0' ,
125
+ sample_rand : '0.42' ,
126
+ environment : 'production' ,
127
+ } ) ;
99
128
} ) ;
100
129
101
130
test ( 'does not create transactions for OPTIONS or HEAD requests' , async ( ) => {
102
- client . on ( 'spanEnd' , ( ) => {
103
- // This will never run, but we want to make sure it doesn't run.
104
- expect ( false ) . toEqual ( true ) ;
131
+ let generatedSpan : Span | undefined ;
132
+
133
+ client . on ( 'spanEnd' , span => {
134
+ generatedSpan = span ;
105
135
} ) ;
106
136
107
137
const server = Bun . serve ( {
108
138
async fetch ( _req ) {
109
139
return new Response ( 'Bun!' ) ;
110
140
} ,
111
- port : DEFAULT_PORT ,
141
+ port,
112
142
} ) ;
113
143
114
- await fetch ( ' http://localhost:22114/' , {
144
+ await fetch ( ` http://localhost:${ port } /` , {
115
145
method : 'OPTIONS' ,
116
146
} ) ;
117
147
118
- await fetch ( ' http://localhost:22114/' , {
148
+ await fetch ( ` http://localhost:${ port } /` , {
119
149
method : 'HEAD' ,
120
150
} ) ;
121
151
122
152
server . stop ( ) ;
153
+
154
+ expect ( generatedSpan ) . toBeUndefined ( ) ;
155
+ } ) ;
156
+
157
+ test ( 'intruments the server again if it is reloaded' , async ( ) => {
158
+ let serverWasInstrumented = false ;
159
+ client . on ( 'spanEnd' , ( ) => {
160
+ serverWasInstrumented = true ;
161
+ } ) ;
162
+
163
+ const server = Bun . serve ( {
164
+ async fetch ( _req ) {
165
+ return new Response ( 'Bun!' ) ;
166
+ } ,
167
+ port,
168
+ } ) ;
169
+
170
+ server . reload ( {
171
+ async fetch ( _req ) {
172
+ return new Response ( 'Reloaded Bun!' ) ;
173
+ } ,
174
+ } ) ;
175
+
176
+ await fetch ( `http://localhost:${ port } /` ) ;
177
+
178
+ server . stop ( ) ;
179
+
180
+ expect ( serverWasInstrumented ) . toBeTrue ( ) ;
123
181
} ) ;
124
182
} ) ;
0 commit comments