Skip to content

Commit 2523f18

Browse files
committed
test: update e2e tests with middleware hook instrumentation
1 parent ac897fa commit 2523f18

File tree

6 files changed

+604
-16
lines changed

6 files changed

+604
-16
lines changed
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { defineEventHandler, setHeader, getQuery } from '#imports';
2+
3+
export default defineEventHandler({
4+
onRequest: async event => {
5+
// Set a header to indicate the onRequest hook ran
6+
setHeader(event, 'x-hooks-onrequest', 'executed');
7+
8+
// Check if we should throw an error in onRequest
9+
const query = getQuery(event);
10+
if (query.throwOnRequestError === 'true') {
11+
throw new Error('OnRequest hook error');
12+
}
13+
},
14+
15+
handler: async event => {
16+
// Set a header to indicate the main handler ran
17+
setHeader(event, 'x-hooks-handler', 'executed');
18+
19+
// Check if we should throw an error in handler
20+
const query = getQuery(event);
21+
if (query.throwHandlerError === 'true') {
22+
throw new Error('Handler error');
23+
}
24+
},
25+
26+
onBeforeResponse: async (event, response) => {
27+
// Set a header to indicate the onBeforeResponse hook ran
28+
setHeader(event, 'x-hooks-onbeforeresponse', 'executed');
29+
30+
// Check if we should throw an error in onBeforeResponse
31+
const query = getQuery(event);
32+
if (query.throwOnBeforeResponseError === 'true') {
33+
throw new Error('OnBeforeResponse hook error');
34+
}
35+
},
36+
});
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { defineEventHandler, setHeader, getQuery } from '#imports';
2+
3+
export default defineEventHandler({
4+
// Array of onRequest handlers
5+
onRequest: [
6+
async event => {
7+
setHeader(event, 'x-array-onrequest-0', 'executed');
8+
9+
const query = getQuery(event);
10+
if (query.throwOnRequest0Error === 'true') {
11+
throw new Error('OnRequest[0] hook error');
12+
}
13+
},
14+
async event => {
15+
setHeader(event, 'x-array-onrequest-1', 'executed');
16+
17+
const query = getQuery(event);
18+
if (query.throwOnRequest1Error === 'true') {
19+
throw new Error('OnRequest[1] hook error');
20+
}
21+
},
22+
],
23+
24+
handler: async event => {
25+
setHeader(event, 'x-array-handler', 'executed');
26+
},
27+
28+
// Array of onBeforeResponse handlers
29+
onBeforeResponse: [
30+
async (event, response) => {
31+
setHeader(event, 'x-array-onbeforeresponse-0', 'executed');
32+
33+
const query = getQuery(event);
34+
if (query.throwOnBeforeResponse0Error === 'true') {
35+
throw new Error('OnBeforeResponse[0] hook error');
36+
}
37+
},
38+
async (event, response) => {
39+
setHeader(event, 'x-array-onbeforeresponse-1', 'executed');
40+
41+
const query = getQuery(event);
42+
if (query.throwOnBeforeResponse1Error === 'true') {
43+
throw new Error('OnBeforeResponse[1] hook error');
44+
}
45+
},
46+
],
47+
});

dev-packages/e2e-tests/test-applications/nuxt-3/tests/middleware.test.ts

Lines changed: 219 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,16 +19,23 @@ test.describe('Server Middleware Instrumentation', () => {
1919
// Verify that we have spans for each middleware
2020
const middlewareSpans = serverTxnEvent.spans?.filter(span => span.op === 'middleware.nuxt') || [];
2121

22-
expect(middlewareSpans).toHaveLength(3);
22+
// 3 simple + 3 hooks (onRequest+handler+onBeforeResponse) + 5 array hooks (2 onRequest + 1 handler + 2 onBeforeResponse)
23+
expect(middlewareSpans).toHaveLength(11);
2324

2425
// Check for specific middleware spans
25-
const firstMiddlewareSpan = middlewareSpans.find(span => span.data?.['nuxt.middleware.name'] === '01.first.ts');
26-
const secondMiddlewareSpan = middlewareSpans.find(span => span.data?.['nuxt.middleware.name'] === '02.second.ts');
27-
const authMiddlewareSpan = middlewareSpans.find(span => span.data?.['nuxt.middleware.name'] === '03.auth.ts');
26+
const firstMiddlewareSpan = middlewareSpans.find(span => span.data?.['nuxt.middleware.name'] === '01.first');
27+
const secondMiddlewareSpan = middlewareSpans.find(span => span.data?.['nuxt.middleware.name'] === '02.second');
28+
const authMiddlewareSpan = middlewareSpans.find(span => span.data?.['nuxt.middleware.name'] === '03.auth');
29+
const hooksOnRequestSpan = middlewareSpans.find(span => span.data?.['nuxt.middleware.name'] === '04.hooks');
30+
const arrayHooksHandlerSpan = middlewareSpans.find(
31+
span => span.data?.['nuxt.middleware.name'] === '05.array-hooks',
32+
);
2833

2934
expect(firstMiddlewareSpan).toBeDefined();
3035
expect(secondMiddlewareSpan).toBeDefined();
3136
expect(authMiddlewareSpan).toBeDefined();
37+
expect(hooksOnRequestSpan).toBeDefined();
38+
expect(arrayHooksHandlerSpan).toBeDefined();
3239

3340
// Verify each span has the correct attributes
3441
[firstMiddlewareSpan, secondMiddlewareSpan, authMiddlewareSpan].forEach(span => {
@@ -37,7 +44,7 @@ test.describe('Server Middleware Instrumentation', () => {
3744
op: 'middleware.nuxt',
3845
data: expect.objectContaining({
3946
'sentry.op': 'middleware.nuxt',
40-
'sentry.origin': 'auto.http.nuxt',
47+
'sentry.origin': 'auto.middleware.nuxt',
4148
'sentry.source': 'custom',
4249
'http.request.method': 'GET',
4350
'http.route': '/api/middleware-test',
@@ -52,7 +59,8 @@ test.describe('Server Middleware Instrumentation', () => {
5259
// Verify spans have different span IDs (each middleware gets its own span)
5360
const spanIds = middlewareSpans.map(span => span.span_id);
5461
const uniqueSpanIds = new Set(spanIds);
55-
expect(uniqueSpanIds.size).toBe(3);
62+
// 3 simple + 3 hooks (onRequest+handler+onBeforeResponse) + 5 array hooks (2 onRequest + 1 handler + 2 onBeforeResponse)
63+
expect(uniqueSpanIds.size).toBe(11);
5664

5765
// Verify spans share the same trace ID
5866
const traceIds = middlewareSpans.map(span => span.trace_id);
@@ -95,7 +103,7 @@ test.describe('Server Middleware Instrumentation', () => {
95103

96104
// Find the auth middleware span
97105
const authMiddlewareSpan = serverTxnEvent.spans?.find(
98-
span => span.op === 'middleware.nuxt' && span.data?.['nuxt.middleware.name'] === '03.auth.ts',
106+
span => span.op === 'middleware.nuxt' && span.data?.['nuxt.middleware.name'] === '03.auth',
99107
);
100108

101109
expect(authMiddlewareSpan).toBeDefined();
@@ -113,9 +121,212 @@ test.describe('Server Middleware Instrumentation', () => {
113121
type: 'Error',
114122
mechanism: expect.objectContaining({
115123
handled: false,
116-
type: 'auto.http.nuxt',
124+
type: 'auto.middleware.nuxt',
117125
}),
118126
}),
119127
);
120128
});
129+
130+
test('should create spans for onRequest and onBeforeResponse hooks', async ({ request }) => {
131+
const serverTxnEventPromise = waitForTransaction('nuxt-3', txnEvent => {
132+
return txnEvent.transaction?.includes('GET /api/middleware-test') ?? false;
133+
});
134+
135+
// Make request to trigger middleware with hooks
136+
const response = await request.get('/api/middleware-test');
137+
expect(response.status()).toBe(200);
138+
139+
const serverTxnEvent = await serverTxnEventPromise;
140+
const middlewareSpans = serverTxnEvent.spans?.filter(span => span.op === 'middleware.nuxt') || [];
141+
142+
// Find spans for the hooks middleware
143+
const hooksSpans = middlewareSpans.filter(span => span.data?.['nuxt.middleware.name'] === '04.hooks');
144+
145+
// Should have spans for onRequest, handler, and onBeforeResponse
146+
expect(hooksSpans).toHaveLength(3);
147+
148+
// Find specific hook spans
149+
const onRequestSpan = hooksSpans.find(span => span.data?.['nuxt.middleware.hook.name'] === 'onRequest');
150+
const handlerSpan = hooksSpans.find(span => span.data?.['nuxt.middleware.hook.name'] === 'handler');
151+
const onBeforeResponseSpan = hooksSpans.find(
152+
span => span.data?.['nuxt.middleware.hook.name'] === 'onBeforeResponse',
153+
);
154+
155+
expect(onRequestSpan).toBeDefined();
156+
expect(handlerSpan).toBeDefined();
157+
expect(onBeforeResponseSpan).toBeDefined();
158+
159+
// Verify span names include hook types
160+
expect(onRequestSpan?.description).toBe('04.hooks.onRequest');
161+
expect(handlerSpan?.description).toBe('04.hooks');
162+
expect(onBeforeResponseSpan?.description).toBe('04.hooks.onBeforeResponse');
163+
164+
// Verify all spans have correct middleware name (without hook suffix)
165+
[onRequestSpan, handlerSpan, onBeforeResponseSpan].forEach(span => {
166+
expect(span?.data?.['nuxt.middleware.name']).toBe('04.hooks');
167+
});
168+
169+
// Verify hook-specific attributes
170+
expect(onRequestSpan?.data?.['nuxt.middleware.hook.name']).toBe('onRequest');
171+
expect(handlerSpan?.data?.['nuxt.middleware.hook.name']).toBe('handler');
172+
expect(onBeforeResponseSpan?.data?.['nuxt.middleware.hook.name']).toBe('onBeforeResponse');
173+
174+
// Verify no index attributes for single hooks
175+
expect(onRequestSpan?.data).not.toHaveProperty('nuxt.middleware.hook.index');
176+
expect(handlerSpan?.data).not.toHaveProperty('nuxt.middleware.hook.index');
177+
expect(onBeforeResponseSpan?.data).not.toHaveProperty('nuxt.middleware.hook.index');
178+
});
179+
180+
test('should create spans with index attributes for array hooks', async ({ request }) => {
181+
const serverTxnEventPromise = waitForTransaction('nuxt-3', txnEvent => {
182+
return txnEvent.transaction?.includes('GET /api/middleware-test') ?? false;
183+
});
184+
185+
// Make request to trigger middleware with array hooks
186+
const response = await request.get('/api/middleware-test');
187+
expect(response.status()).toBe(200);
188+
189+
const serverTxnEvent = await serverTxnEventPromise;
190+
const middlewareSpans = serverTxnEvent.spans?.filter(span => span.op === 'middleware.nuxt') || [];
191+
192+
// Find spans for the array hooks middleware
193+
const arrayHooksSpans = middlewareSpans.filter(span => span.data?.['nuxt.middleware.name'] === '05.array-hooks');
194+
195+
// Should have spans for 2 onRequest + 1 handler + 2 onBeforeResponse = 5 spans
196+
expect(arrayHooksSpans).toHaveLength(5);
197+
198+
// Find onRequest array spans
199+
const onRequestSpans = arrayHooksSpans.filter(span => span.data?.['nuxt.middleware.hook.name'] === 'onRequest');
200+
expect(onRequestSpans).toHaveLength(2);
201+
202+
// Find onBeforeResponse array spans
203+
const onBeforeResponseSpans = arrayHooksSpans.filter(
204+
span => span.data?.['nuxt.middleware.hook.name'] === 'onBeforeResponse',
205+
);
206+
expect(onBeforeResponseSpans).toHaveLength(2);
207+
208+
// Find handler span
209+
const handlerSpan = arrayHooksSpans.find(span => span.data?.['nuxt.middleware.hook.name'] === 'handler');
210+
expect(handlerSpan).toBeDefined();
211+
212+
// Verify index attributes for onRequest array
213+
const onRequest0Span = onRequestSpans.find(span => span.data?.['nuxt.middleware.hook.index'] === 0);
214+
const onRequest1Span = onRequestSpans.find(span => span.data?.['nuxt.middleware.hook.index'] === 1);
215+
216+
expect(onRequest0Span).toBeDefined();
217+
expect(onRequest1Span).toBeDefined();
218+
219+
// Verify index attributes for onBeforeResponse array
220+
const onBeforeResponse0Span = onBeforeResponseSpans.find(span => span.data?.['nuxt.middleware.hook.index'] === 0);
221+
const onBeforeResponse1Span = onBeforeResponseSpans.find(span => span.data?.['nuxt.middleware.hook.index'] === 1);
222+
223+
expect(onBeforeResponse0Span).toBeDefined();
224+
expect(onBeforeResponse1Span).toBeDefined();
225+
226+
// Verify span names for array handlers
227+
expect(onRequest0Span?.description).toBe('05.array-hooks.onRequest');
228+
expect(onRequest1Span?.description).toBe('05.array-hooks.onRequest');
229+
expect(onBeforeResponse0Span?.description).toBe('05.array-hooks.onBeforeResponse');
230+
expect(onBeforeResponse1Span?.description).toBe('05.array-hooks.onBeforeResponse');
231+
232+
// Verify handler has no index
233+
expect(handlerSpan?.data).not.toHaveProperty('nuxt.middleware.hook.index');
234+
});
235+
236+
test('should handle errors in onRequest hooks', async ({ request }) => {
237+
const serverTxnEventPromise = waitForTransaction('nuxt-3', txnEvent => {
238+
return txnEvent.transaction?.includes('GET /api/middleware-test') ?? false;
239+
});
240+
241+
const errorEventPromise = waitForError('nuxt-3', errorEvent => {
242+
return errorEvent?.exception?.values?.[0]?.value === 'OnRequest hook error';
243+
});
244+
245+
// Make request with query param to trigger error in onRequest
246+
const response = await request.get('/api/middleware-test?throwOnRequestError=true');
247+
expect(response.status()).toBe(500);
248+
249+
const [serverTxnEvent, errorEvent] = await Promise.all([serverTxnEventPromise, errorEventPromise]);
250+
251+
// Find the onRequest span that should have error status
252+
const onRequestSpan = serverTxnEvent.spans?.find(
253+
span =>
254+
span.op === 'middleware.nuxt' &&
255+
span.data?.['nuxt.middleware.name'] === '04.hooks' &&
256+
span.data?.['nuxt.middleware.hook.name'] === 'onRequest',
257+
);
258+
259+
expect(onRequestSpan).toBeDefined();
260+
expect(onRequestSpan?.status).toBe('internal_error');
261+
expect(errorEvent.exception?.values?.[0]?.value).toBe('OnRequest hook error');
262+
});
263+
264+
test('should handle errors in onBeforeResponse hooks', async ({ request }) => {
265+
const serverTxnEventPromise = waitForTransaction('nuxt-3', txnEvent => {
266+
return txnEvent.transaction?.includes('GET /api/middleware-test') ?? false;
267+
});
268+
269+
const errorEventPromise = waitForError('nuxt-3', errorEvent => {
270+
return errorEvent?.exception?.values?.[0]?.value === 'OnBeforeResponse hook error';
271+
});
272+
273+
// Make request with query param to trigger error in onBeforeResponse
274+
const response = await request.get('/api/middleware-test?throwOnBeforeResponseError=true');
275+
expect(response.status()).toBe(500);
276+
277+
const [serverTxnEvent, errorEvent] = await Promise.all([serverTxnEventPromise, errorEventPromise]);
278+
279+
// Find the onBeforeResponse span that should have error status
280+
const onBeforeResponseSpan = serverTxnEvent.spans?.find(
281+
span =>
282+
span.op === 'middleware.nuxt' &&
283+
span.data?.['nuxt.middleware.name'] === '04.hooks' &&
284+
span.data?.['nuxt.middleware.hook.name'] === 'onBeforeResponse',
285+
);
286+
287+
expect(onBeforeResponseSpan).toBeDefined();
288+
expect(onBeforeResponseSpan?.status).toBe('internal_error');
289+
expect(errorEvent.exception?.values?.[0]?.value).toBe('OnBeforeResponse hook error');
290+
});
291+
292+
test('should handle errors in array hooks with proper index attribution', async ({ request }) => {
293+
const serverTxnEventPromise = waitForTransaction('nuxt-3', txnEvent => {
294+
return txnEvent.transaction?.includes('GET /api/middleware-test') ?? false;
295+
});
296+
297+
const errorEventPromise = waitForError('nuxt-3', errorEvent => {
298+
return errorEvent?.exception?.values?.[0]?.value === 'OnRequest[1] hook error';
299+
});
300+
301+
// Make request with query param to trigger error in second onRequest handler
302+
const response = await request.get('/api/middleware-test?throwOnRequest1Error=true');
303+
expect(response.status()).toBe(500);
304+
305+
const [serverTxnEvent, errorEvent] = await Promise.all([serverTxnEventPromise, errorEventPromise]);
306+
307+
// Find the second onRequest span that should have error status
308+
const onRequest1Span = serverTxnEvent.spans?.find(
309+
span =>
310+
span.op === 'middleware.nuxt' &&
311+
span.data?.['nuxt.middleware.name'] === '05.array-hooks' &&
312+
span.data?.['nuxt.middleware.hook.name'] === 'onRequest' &&
313+
span.data?.['nuxt.middleware.hook.index'] === 1,
314+
);
315+
316+
expect(onRequest1Span).toBeDefined();
317+
expect(onRequest1Span?.status).toBe('internal_error');
318+
expect(errorEvent.exception?.values?.[0]?.value).toBe('OnRequest[1] hook error');
319+
320+
// Verify the first onRequest handler still executed successfully
321+
const onRequest0Span = serverTxnEvent.spans?.find(
322+
span =>
323+
span.op === 'middleware.nuxt' &&
324+
span.data?.['nuxt.middleware.name'] === '05.array-hooks' &&
325+
span.data?.['nuxt.middleware.hook.name'] === 'onRequest' &&
326+
span.data?.['nuxt.middleware.hook.index'] === 0,
327+
);
328+
329+
expect(onRequest0Span).toBeDefined();
330+
expect(onRequest0Span?.status).not.toBe('internal_error');
331+
});
121332
});
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { defineEventHandler, setHeader, getQuery } from '#imports';
2+
3+
export default defineEventHandler({
4+
onRequest: async event => {
5+
// Set a header to indicate the onRequest hook ran
6+
setHeader(event, 'x-hooks-onrequest', 'executed');
7+
8+
// Check if we should throw an error in onRequest
9+
const query = getQuery(event);
10+
if (query.throwOnRequestError === 'true') {
11+
throw new Error('OnRequest hook error');
12+
}
13+
},
14+
15+
handler: async event => {
16+
// Set a header to indicate the main handler ran
17+
setHeader(event, 'x-hooks-handler', 'executed');
18+
19+
// Check if we should throw an error in handler
20+
const query = getQuery(event);
21+
if (query.throwHandlerError === 'true') {
22+
throw new Error('Handler error');
23+
}
24+
},
25+
26+
onBeforeResponse: async (event, response) => {
27+
// Set a header to indicate the onBeforeResponse hook ran
28+
setHeader(event, 'x-hooks-onbeforeresponse', 'executed');
29+
30+
// Check if we should throw an error in onBeforeResponse
31+
const query = getQuery(event);
32+
if (query.throwOnBeforeResponseError === 'true') {
33+
throw new Error('OnBeforeResponse hook error');
34+
}
35+
},
36+
});

0 commit comments

Comments
 (0)