11import {
22 patchExpressModule ,
3- unpatchExpressModule ,
43 expressErrorHandler ,
54 setupExpressErrorHandler ,
65} from '../../../../src/integrations/express/index' ;
76
8- import { describe , it , expect , vi } from 'vitest' ;
7+ import { describe , it , expect , vi , beforeEach } from 'vitest' ;
8+ import type { Mock } from 'vitest' ;
99import type {
1010 ExpressIntegrationOptions ,
1111 ExpressExportv5 ,
@@ -22,7 +22,6 @@ import type {
2222 ExpressHandlerOptions ,
2323} from '../../../../src/integrations/express/types' ;
2424import type { WrappedFunction } from '../../../../src/types-hoist/wrappedfunction' ;
25- import type { IncomingMessage , ServerResponse } from 'http' ;
2625
2726const sdkProcessingMetadata : unknown [ ] = [ ] ;
2827const isolationScope = {
@@ -57,6 +56,7 @@ vi.mock('../../../../src/utils/debug-logger', () => ({
5756 } ,
5857} ) ) ;
5958
59+ beforeEach ( ( ) => ( patchLayerCalls . length = 0 ) ) ;
6060const patchLayerCalls : [ options : ExpressIntegrationOptions , layer : ExpressLayer , layerPath ?: string ] [ ] = [ ] ;
6161
6262vi . mock ( '../../../../src/integrations/express/patch-layer' , ( ) => ( {
@@ -67,53 +67,78 @@ vi.mock('../../../../src/integrations/express/patch-layer', () => ({
6767 } ,
6868} ) ) ;
6969
70- describe ( '(un)patchExpressModule' , ( ) => {
71- it ( 'throws trying to patch/unpatch the wrong thing' , ( ) => {
72- expect ( ( ) => {
73- patchExpressModule ( {
74- express : { } as unknown as ExpressModuleExport ,
75- } as unknown as ExpressIntegrationOptions ) ;
76- } ) . toThrowError ( 'no valid Express route function to instrument' ) ;
77- expect ( ( ) => {
78- unpatchExpressModule ( {
79- express : { } ,
80- } as unknown as ExpressIntegrationOptions ) ;
81- } ) . toThrowError ( 'no valid Express route function to deinstrument' ) ;
82- } ) ;
70+ type ExpressSpies = Record < 'routerUse' | 'routerRoute' | 'appUse' , Mock < ( ) => void > > ;
8371
84- // these are called in the unit tests below, so set spies
85- const routerv4route = vi . fn ( ) ;
86- const routerv4use = vi . fn ( ) ;
87- const appv4use = vi . fn ( ) ;
88- const expressv4 = Object . assign ( function express ( ) { } , {
89- application : { use : appv4use } ,
72+ // get a fresh copy of a mock Express version 4 export
73+ function getExpress4 ( ) : ExpressExportv4 & { spies : ExpressSpies } {
74+ const routerRoute = vi . fn ( ) ;
75+ const routerUse = vi . fn ( ) ;
76+ const appUse = vi . fn ( ) ;
77+ const spies = {
78+ routerRoute,
79+ routerUse,
80+ appUse,
81+ } as const ;
82+ const express = Object . assign ( function express ( ) { } , {
83+ spies,
84+ application : { use : appUse } ,
9085 Router : Object . assign ( function Router ( ) { } , {
91- route : routerv4route ,
92- use : routerv4use ,
86+ route : routerRoute ,
87+ use : routerUse ,
9388 stack : [ { name : 'layer0' } , { name : 'layer1' } , { name : 'layerFinal' } ] ,
9489 } ) ,
95- } ) as unknown as ExpressExportv4 ;
96- Object . assign ( expressv4 . application , { _router : expressv4 . Router } ) ;
90+ } ) as unknown as ExpressExportv4 & { spies : ExpressSpies } ;
91+ Object . assign ( express . application , { _router : express . Router } ) ;
92+
93+ return express ;
94+ }
9795
98- const appv5use = vi . fn ( ) ;
96+ // get a fresh copy of a mock Express version 5 export
97+ function getExpress5 ( ) : ExpressExportv5 & { spies : ExpressSpies } {
98+ const routerRoute = vi . fn ( ) ;
99+ const routerUse = vi . fn ( ) ;
100+ const appUse = vi . fn ( ) ;
101+ const spies = {
102+ routerRoute,
103+ routerUse,
104+ appUse,
105+ } as const ;
99106 const expressv5 = Object . assign ( function express ( ) { } , {
100- application : { use : appv5use } ,
107+ spies,
108+ application : { use : appUse } ,
101109 Router : class Router {
102110 stack : ExpressLayer [ ] = [ ] ;
103- route ( ) { }
104- use ( ) { }
111+ route ( ...args : unknown [ ] ) {
112+ return routerRoute ( ...args ) ;
113+ }
114+ use ( ...args : unknown [ ] ) {
115+ return routerUse ( ...args ) ;
116+ }
105117 } ,
106- } ) as unknown as ExpressExportv5 ;
118+ } ) as unknown as ExpressExportv5 & { spies : ExpressSpies } ;
119+ const stack = [ { name : 'layer0' } , { name : 'layer1' } , { name : 'layerFinal' } ] ;
107120 Object . assign ( expressv5 . application , {
108- router : {
109- stack : [ { name : 'layer0' } , { name : 'layer1' } , { name : 'layerFinal' } ] ,
110- } ,
121+ router : { stack } ,
122+ } ) ;
123+
124+ return expressv5 ;
125+ }
126+
127+ describe ( 'patchExpressModule' , ( ) => {
128+ it ( 'throws trying to patch/unpatch the wrong thing' , ( ) => {
129+ expect ( ( ) => {
130+ patchExpressModule ( {
131+ express : { } as unknown as ExpressModuleExport ,
132+ } as unknown as ExpressIntegrationOptions ) ;
133+ } ) . toThrowError ( 'no valid Express route function to instrument' ) ;
111134 } ) ;
112135
113136 it ( 'can patch and restore expressv4 style module' , ( ) => {
114- const r = expressv4 . Router as ExpressRouterv4 ;
115- const a = expressv4 . application ;
116- for ( const module of [ expressv4 , { default : expressv4 } ] ) {
137+ for ( const useDefault of [ false , true ] ) {
138+ const express = getExpress4 ( ) ;
139+ const module = useDefault ? { default : express } : express ;
140+ const r = express . Router as ExpressRouterv4 ;
141+ const a = express . application ;
117142 const options = { express : module } as unknown as ExpressIntegrationOptions ;
118143 expect ( ( r . use as WrappedFunction ) . __sentry_original__ ) . toBe ( undefined ) ;
119144 expect ( ( r . route as WrappedFunction ) . __sentry_original__ ) . toBe ( undefined ) ;
@@ -124,19 +149,15 @@ describe('(un)patchExpressModule', () => {
124149 expect ( typeof ( r . use as WrappedFunction ) . __sentry_original__ ) . toBe ( 'function' ) ;
125150 expect ( typeof ( r . route as WrappedFunction ) . __sentry_original__ ) . toBe ( 'function' ) ;
126151 expect ( typeof ( a . use as WrappedFunction ) . __sentry_original__ ) . toBe ( 'function' ) ;
127-
128- unpatchExpressModule ( options ) ;
129-
130- expect ( ( r . use as WrappedFunction ) . __sentry_original__ ) . toBe ( undefined ) ;
131- expect ( ( r . route as WrappedFunction ) . __sentry_original__ ) . toBe ( undefined ) ;
132- expect ( ( a . use as WrappedFunction ) . __sentry_original__ ) . toBe ( undefined ) ;
133152 }
134153 } ) ;
135154
136155 it ( 'can patch and restore expressv5 style module' , ( ) => {
137- const r = expressv5 . Router as ExpressRouterv5 ;
138- const a = expressv5 . application ;
139- for ( const module of [ expressv5 , { default : expressv5 } ] ) {
156+ for ( const useDefault of [ false , true ] ) {
157+ const express = getExpress5 ( ) ;
158+ const r = express . Router as ExpressRouterv5 ;
159+ const a = express . application ;
160+ const module = useDefault ? { default : express } : express ;
140161 const options = { express : module } as unknown as ExpressIntegrationOptions ;
141162 expect ( ( r . prototype . use as WrappedFunction ) . __sentry_original__ ) . toBe ( undefined ) ;
142163 expect ( ( r . prototype . route as WrappedFunction ) . __sentry_original__ ) . toBe ( undefined ) ;
@@ -147,74 +168,64 @@ describe('(un)patchExpressModule', () => {
147168 expect ( typeof ( r . prototype . use as WrappedFunction ) . __sentry_original__ ) . toBe ( 'function' ) ;
148169 expect ( typeof ( r . prototype . route as WrappedFunction ) . __sentry_original__ ) . toBe ( 'function' ) ;
149170 expect ( typeof ( a . use as WrappedFunction ) . __sentry_original__ ) . toBe ( 'function' ) ;
150-
151- unpatchExpressModule ( options ) ;
152-
153- expect ( ( r . prototype . use as WrappedFunction ) . __sentry_original__ ) . toBe ( undefined ) ;
154- expect ( ( r . prototype . route as WrappedFunction ) . __sentry_original__ ) . toBe ( undefined ) ;
155- expect ( ( a . use as WrappedFunction ) . __sentry_original__ ) . toBe ( undefined ) ;
156171 }
157172 } ) ;
158173
159174 it ( 'calls patched and original Router.route' , ( ) => {
175+ const expressv4 = getExpress4 ( ) ;
176+ const { spies } = expressv4 ;
160177 const options = { express : expressv4 } ;
161178 patchExpressModule ( options ) ;
162179 expressv4 . Router . route ( 'a' ) ;
163- unpatchExpressModule ( options ) ;
164- expect ( patchLayerCalls ) . toStrictEqual ( [ [ options , { name : 'layerFinal' } , 'a' ] ] ) ;
165- patchLayerCalls . length = 0 ;
166- expect ( routerv4route ) . toHaveBeenCalledExactlyOnceWith ( 'a' ) ;
167- routerv4route . mockReset ( ) ;
180+ expect ( spies . routerRoute ) . toHaveBeenCalledExactlyOnceWith ( 'a' ) ;
168181 } ) ;
169182
170183 it ( 'calls patched and original Router.use' , ( ) => {
184+ const expressv4 = getExpress4 ( ) ;
185+ const { spies } = expressv4 ;
171186 const options = { express : expressv4 } ;
172187 patchExpressModule ( options ) ;
173188 expressv4 . Router . use ( 'a' ) ;
174- unpatchExpressModule ( options ) ;
175189 expect ( patchLayerCalls ) . toStrictEqual ( [ [ options , { name : 'layerFinal' } , 'a' ] ] ) ;
176- patchLayerCalls . length = 0 ;
177- expect ( routerv4use ) . toHaveBeenCalledExactlyOnceWith ( 'a' ) ;
178- routerv4use . mockReset ( ) ;
190+ expect ( spies . routerUse ) . toHaveBeenCalledExactlyOnceWith ( 'a' ) ;
179191 } ) ;
180192
181193 it ( 'skips patchLayer call in Router.use if no layer in the stack' , ( ) => {
194+ const expressv4 = getExpress4 ( ) ;
195+ const { spies } = expressv4 ;
182196 const options = { express : expressv4 } ;
183197 patchExpressModule ( options ) ;
184198 const { stack } = expressv4 . Router ;
185199 expressv4 . Router . stack = [ ] ;
186200 expressv4 . Router . use ( 'a' ) ;
187201 expressv4 . Router . stack = stack ;
188- unpatchExpressModule ( options ) ;
189202 expect ( patchLayerCalls ) . toStrictEqual ( [ ] ) ;
190- patchLayerCalls . length = 0 ;
191- expect ( routerv4use ) . toHaveBeenCalledExactlyOnceWith ( 'a' ) ;
192- routerv4use . mockReset ( ) ;
203+ expect ( spies . routerUse ) . toHaveBeenCalledExactlyOnceWith ( 'a' ) ;
193204 } ) ;
194205
195206 it ( 'calls patched and original application.use' , ( ) => {
207+ const expressv4 = getExpress4 ( ) ;
208+ const { spies } = expressv4 ;
196209 const options = { express : expressv4 } ;
197210 patchExpressModule ( options ) ;
198211 expressv4 . application . use ( 'a' ) ;
199- unpatchExpressModule ( options ) ;
200212 expect ( patchLayerCalls ) . toStrictEqual ( [ [ options , { name : 'layerFinal' } , 'a' ] ] ) ;
201- patchLayerCalls . length = 0 ;
202- expect ( appv4use ) . toHaveBeenCalledExactlyOnceWith ( 'a' ) ;
203- appv4use . mockReset ( ) ;
213+ expect ( spies . appUse ) . toHaveBeenCalledExactlyOnceWith ( 'a' ) ;
204214 } ) ;
205215
206216 it ( 'calls patched and original application.use on express v5' , ( ) => {
217+ const expressv5 = getExpress5 ( ) ;
218+ const { spies } = expressv5 ;
207219 const options = { express : expressv5 } ;
208220 patchExpressModule ( options ) ;
209221 expressv5 . application . use ( 'a' ) ;
210- unpatchExpressModule ( options ) ;
211222 expect ( patchLayerCalls ) . toStrictEqual ( [ [ options , { name : 'layerFinal' } , 'a' ] ] ) ;
212- patchLayerCalls . length = 0 ;
213- expect ( appv5use ) . toHaveBeenCalledExactlyOnceWith ( 'a' ) ;
214- appv5use . mockReset ( ) ;
223+ expect ( spies . appUse ) . toHaveBeenCalledExactlyOnceWith ( 'a' ) ;
215224 } ) ;
216225
217226 it ( 'skips patchLayer on application.use if no router found' , ( ) => {
227+ const expressv4 = getExpress4 ( ) ;
228+ const { spies } = expressv4 ;
218229 const options = { express : expressv4 } ;
219230 patchExpressModule ( options ) ;
220231 const app = expressv4 . application as {
@@ -223,29 +234,14 @@ describe('(un)patchExpressModule', () => {
223234 const { _router } = app ;
224235 delete app . _router ;
225236 expressv4 . application . use ( 'a' ) ;
226- unpatchExpressModule ( options ) ;
227237 app . _router = _router ;
228238 // no router, so no layers to patch!
229239 expect ( patchLayerCalls ) . toStrictEqual ( [ ] ) ;
230- patchLayerCalls . length = 0 ;
231- expect ( appv4use ) . toHaveBeenCalledExactlyOnceWith ( 'a' ) ;
232- appv4use . mockReset ( ) ;
233- } ) ;
234-
235- it ( 'debug error when unpatching fails' , ( ) => {
236- unpatchExpressModule ( { express : expressv5 } ) ;
237- expect ( debugErrors ) . toStrictEqual ( [
238- [ 'Failed to unpatch express route method:' , new Error ( 'Method route is not wrapped, and cannot be unwrapped' ) ] ,
239- [ 'Failed to unpatch express use method:' , new Error ( 'Method use is not wrapped, and cannot be unwrapped' ) ] ,
240- [
241- 'Failed to unpatch express application.use method:' ,
242- new Error ( 'Method use is not wrapped, and cannot be unwrapped' ) ,
243- ] ,
244- ] ) ;
245- debugErrors . length = 0 ;
240+ expect ( spies . appUse ) . toHaveBeenCalledExactlyOnceWith ( 'a' ) ;
246241 } ) ;
247242
248243 it ( 'debug error when patching fails' , ( ) => {
244+ const expressv5 = getExpress5 ( ) ;
249245 patchExpressModule ( { express : expressv5 } ) ;
250246 patchExpressModule ( { express : expressv5 } ) ;
251247 expect ( debugErrors ) . toStrictEqual ( [
@@ -333,7 +329,7 @@ describe('setupExpressErrorHandler', () => {
333329 const reqHandler = appUseCalls [ 0 ] ;
334330 expect ( typeof reqHandler ) . toBe ( 'function' ) ;
335331 const next = vi . fn ( ) ;
336- ( reqHandler as ( request : IncomingMessage , _res : ServerResponse , next : ( ) => void ) => void ) (
332+ ( reqHandler as ( request : ExpressRequest , _res : ExpressResponse , next : ( ) => void ) => void ) (
337333 {
338334 method : 'GET' ,
339335 headers : { request : 'headers' } ,
0 commit comments