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' ;
23import { getDynamicSamplingContextFromSpan , setCurrentClient , spanIsSampled , spanToJSON } from '@sentry/core' ;
34
45import { BunClient } from '../../src/client' ;
56import { instrumentBunServe } from '../../src/integrations/bunserver' ;
67import { getDefaultBunClientOptions } from '../helpers' ;
78
8- // Fun fact: Bun = 2 21 14 :)
9- const DEFAULT_PORT = 22114 ;
10-
119describe ( 'Bun Serve Integration' , ( ) => {
1210 let client : BunClient ;
11+ // Fun fact: Bun = 2 21 14 :)
12+ let port : number = 22114 ;
1313
1414 beforeAll ( ( ) => {
1515 instrumentBunServe ( ) ;
1616 } ) ;
1717
1818 beforeEach ( ( ) => {
19- const options = getDefaultBunClientOptions ( { tracesSampleRate : 1 , debug : true } ) ;
19+ const options = getDefaultBunClientOptions ( { tracesSampleRate : 1 } ) ;
2020 client = new BunClient ( options ) ;
2121 setCurrentClient ( client ) ;
2222 client . init ( ) ;
2323 } ) ;
2424
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+
2531 test ( 'generates a transaction around a request' , async ( ) => {
32+ let generatedSpan : Span | undefined ;
33+
2634 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 ;
3136 } ) ;
3237
3338 const server = Bun . serve ( {
3439 async fetch ( _req ) {
3540 return new Response ( 'Bun!' ) ;
3641 } ,
37- port : DEFAULT_PORT ,
42+ port,
3843 } ) ;
44+ await fetch ( `http://localhost:${ port } /` ) ;
45+ server . stop ( ) ;
3946
40- await fetch ( 'http://localhost:22114/' ) ;
47+ if ( ! generatedSpan ) {
48+ throw 'No span was generated in the test' ;
49+ }
4150
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 /' ) ;
4355 } ) ;
4456
4557 test ( 'generates a post transaction' , async ( ) => {
58+ let generatedSpan : Span | undefined ;
59+
4660 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 ;
5162 } ) ;
5263
5364 const server = Bun . serve ( {
5465 async fetch ( _req ) {
5566 return new Response ( 'Bun!' ) ;
5667 } ,
57- port : DEFAULT_PORT ,
68+ port,
5869 } ) ;
5970
60- await fetch ( ' http://localhost:22114/' , {
71+ await fetch ( ` http://localhost:${ port } /` , {
6172 method : 'POST' ,
6273 } ) ;
6374
6475 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 /' ) ;
6585 } ) ;
6686
6787 test ( 'continues a trace' , async ( ) => {
@@ -70,55 +90,93 @@ describe('Bun Serve Integration', () => {
7090 const PARENT_SAMPLED = '1' ;
7191
7292 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' ;
7494
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 ;
8096
81- expect ( getDynamicSamplingContextFromSpan ( span ) ) . toStrictEqual ( {
82- version : '1.0' ,
83- environment : 'production' ,
84- } ) ;
97+ client . on ( 'spanEnd' , span => {
98+ generatedSpan = span ;
8599 } ) ;
86100
87101 const server = Bun . serve ( {
88102 async fetch ( _req ) {
89103 return new Response ( 'Bun!' ) ;
90104 } ,
91- port : DEFAULT_PORT ,
105+ port,
92106 } ) ;
93107
94- await fetch ( ' http://localhost:22114/' , {
108+ await fetch ( ` http://localhost:${ port } /` , {
95109 headers : { 'sentry-trace' : SENTRY_TRACE_HEADER , baggage : SENTRY_BAGGAGE_HEADER } ,
96110 } ) ;
97111
98112 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+ } ) ;
99128 } ) ;
100129
101130 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 ;
105135 } ) ;
106136
107137 const server = Bun . serve ( {
108138 async fetch ( _req ) {
109139 return new Response ( 'Bun!' ) ;
110140 } ,
111- port : DEFAULT_PORT ,
141+ port,
112142 } ) ;
113143
114- await fetch ( ' http://localhost:22114/' , {
144+ await fetch ( ` http://localhost:${ port } /` , {
115145 method : 'OPTIONS' ,
116146 } ) ;
117147
118- await fetch ( ' http://localhost:22114/' , {
148+ await fetch ( ` http://localhost:${ port } /` , {
119149 method : 'HEAD' ,
120150 } ) ;
121151
122152 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 ( ) ;
123181 } ) ;
124182} ) ;
0 commit comments