Skip to content

Commit c5b4468

Browse files
authored
test(nitro-3): Update e2e tests for h3 route handler tracing (#21152)
Nitro `3.0.260522-beta` ([nitrojs/nitro#4240](nitrojs/nitro#4240)) now wraps file-based route handlers with `wrapHandlerWithTracing` from `h3/tracing` at build time. This means h3 emits route handler spans via `diagnostics_channel`, fixing the missing route spans we had workarounds for. so this PR bumps nitro test app from `^3.0.260429-beta` to `^3.0.260522-beta`, and asserts that `startSpan` assertions and db spans are now children of the h3 route handler span, not the srvx span directly. Also errors thrown in route handlers are now caught by the h3 tracing channel (`auto.http.nitro.onTraceError`) instead of going through Nitro's error hook (`captureErrorHook`), and are no longer wrapped in an `HTTPError` since those happen on the handler level now.
1 parent 5cebcd3 commit c5b4468

3 files changed

Lines changed: 32 additions & 66 deletions

File tree

dev-packages/e2e-tests/test-applications/nitro-3/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
"@playwright/test": "~1.56.0",
2020
"@sentry-internal/test-utils": "link:../../../test-utils",
2121
"@sentry/core": "latest || *",
22-
"nitro": "^3.0.260429-beta",
22+
"nitro": "^3.0.260522-beta",
2323
"rolldown": "latest",
2424
"vite": "latest"
2525
},

dev-packages/e2e-tests/test-applications/nitro-3/tests/errors.test.ts

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,22 +10,16 @@ test('Sends an error event to Sentry', async ({ request }) => {
1010

1111
const errorEvent = await errorEventPromise;
1212

13-
// Nitro wraps thrown errors in an HTTPError with .cause, producing a chained exception
14-
expect(errorEvent.exception?.values).toHaveLength(2);
13+
expect(errorEvent.exception?.values).toHaveLength(1);
1514

16-
// The innermost exception (values[0]) is the original thrown error
1715
expect(errorEvent.exception?.values?.[0]?.type).toBe('Error');
1816
expect(errorEvent.exception?.values?.[0]?.value).toBe('This is a test error');
1917
expect(errorEvent.exception?.values?.[0]?.mechanism).toEqual(
2018
expect.objectContaining({
2119
handled: false,
22-
type: 'auto.function.nitro.captureErrorHook',
20+
type: 'auto.http.nitro.onTraceError',
2321
}),
2422
);
25-
26-
// The outermost exception (values[1]) is the HTTPError wrapper
27-
expect(errorEvent.exception?.values?.[1]?.type).toBe('HTTPError');
28-
expect(errorEvent.exception?.values?.[1]?.value).toBe('This is a test error');
2923
});
3024

3125
test('Does not send 404 errors to Sentry', async ({ request }) => {

dev-packages/e2e-tests/test-applications/nitro-3/tests/span-nesting.test.ts

Lines changed: 29 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -33,17 +33,17 @@ test('Span nesting: h3 middleware spans are children of the srvx request span',
3333
expect(srvxSpan).toBeDefined();
3434

3535
// All h3 middleware spans should be children of the srvx span
36-
const h3Spans = event.spans?.filter(span => span.origin === 'auto.http.nitro.h3');
37-
expect(h3Spans?.length).toBeGreaterThanOrEqual(1);
36+
const h3MiddlewareSpans = event.spans?.filter(
37+
span => span.origin === 'auto.http.nitro.h3' && span.op === 'middleware.nitro',
38+
);
39+
expect(h3MiddlewareSpans?.length).toBeGreaterThanOrEqual(1);
3840

39-
for (const span of h3Spans ?? []) {
41+
for (const span of h3MiddlewareSpans ?? []) {
4042
expect(span.parent_span_id).toBe(srvxSpan!.span_id);
4143
}
4244
});
4345

44-
test('Span nesting: manual startSpan calls inside route handler are children of the srvx request span', async ({
45-
request,
46-
}) => {
46+
test('Span nesting: h3 route handler span is a child of the srvx request span', async ({ request }) => {
4747
const transactionEventPromise = waitForTransaction('nitro-3', event => {
4848
return event?.transaction === 'GET /api/test-nesting';
4949
});
@@ -52,23 +52,38 @@ test('Span nesting: manual startSpan calls inside route handler are children of
5252

5353
const event = await transactionEventPromise;
5454

55-
// Find the srvx request span — this is the parent of all h3 and manual spans
5655
const srvxSpan = event.spans?.find(span => span.origin === 'auto.http.nitro.srvx' && span.op === 'http.server');
5756
expect(srvxSpan).toBeDefined();
58-
const srvxSpanId = srvxSpan!.span_id;
57+
58+
const h3HandlerSpan = event.spans?.find(span => span.origin === 'auto.http.nitro.h3' && span.op === 'http.server');
59+
expect(h3HandlerSpan).toBeDefined();
60+
expect(h3HandlerSpan!.parent_span_id).toBe(srvxSpan!.span_id);
61+
});
62+
63+
test('Span nesting: manual startSpan calls inside route handler are children of the h3 route handler span', async ({
64+
request,
65+
}) => {
66+
const transactionEventPromise = waitForTransaction('nitro-3', event => {
67+
return event?.transaction === 'GET /api/test-nesting';
68+
});
69+
70+
await request.get('/api/test-nesting');
71+
72+
const event = await transactionEventPromise;
73+
74+
// Find the h3 route handler span
75+
const h3HandlerSpan = event.spans?.find(span => span.origin === 'auto.http.nitro.h3' && span.op === 'http.server');
76+
expect(h3HandlerSpan).toBeDefined();
5977

6078
// Find the manually created db spans
6179
const dbSelectSpan = event.spans?.find(span => span.op === 'db' && span.description === 'db.select');
6280
const dbInsertSpan = event.spans?.find(span => span.op === 'db' && span.description === 'db.insert');
6381
expect(dbSelectSpan).toBeDefined();
6482
expect(dbInsertSpan).toBeDefined();
6583

66-
// FIXME: Once nitro's h3 tracing plugin emits a separate span for route handlers (type: "route"),
67-
// the db spans should be children of the h3 route handler span, not the srvx span directly.
68-
// Currently nitro bypasses h3's ~routes for file-based routing, so h3 only emits middleware spans.
69-
// Both db spans should be children of the srvx request span
70-
expect(dbSelectSpan!.parent_span_id).toBe(srvxSpanId);
71-
expect(dbInsertSpan!.parent_span_id).toBe(srvxSpanId);
84+
// Both db spans should be children of the h3 route handler span
85+
expect(dbSelectSpan!.parent_span_id).toBe(h3HandlerSpan!.span_id);
86+
expect(dbInsertSpan!.parent_span_id).toBe(h3HandlerSpan!.span_id);
7287

7388
// Both db spans should be siblings (same parent)
7489
expect(dbSelectSpan!.parent_span_id).toBe(dbInsertSpan!.parent_span_id);
@@ -79,49 +94,6 @@ test('Span nesting: manual startSpan calls inside route handler are children of
7994
expect(serializeSpan!.parent_span_id).toBe(dbInsertSpan!.span_id);
8095
});
8196

82-
// FIXME: Nitro's file-based routing bypasses h3's ~routes, so h3's tracing plugin never wraps
83-
// route handlers with type: "route". Once this is fixed upstream or we add our own wrapping,
84-
// uncomment these tests to verify the h3 route handler span exists and is the parent of manual spans.
85-
//
86-
// test('Span nesting: h3 route handler span is a child of the srvx request span', async ({ request }) => {
87-
// const transactionEventPromise = waitForTransaction('nitro-3', event => {
88-
// return event?.transaction === 'GET /api/test-nesting';
89-
// });
90-
//
91-
// await request.get('/api/test-nesting');
92-
//
93-
// const event = await transactionEventPromise;
94-
//
95-
// const srvxSpan = event.spans?.find(span => span.origin === 'auto.http.nitro.srvx' && span.op === 'http.server');
96-
// expect(srvxSpan).toBeDefined();
97-
//
98-
// const h3HandlerSpan = event.spans?.find(
99-
// span => span.origin === 'auto.http.nitro.h3' && span.op === 'http.server',
100-
// );
101-
// expect(h3HandlerSpan).toBeDefined();
102-
// expect(h3HandlerSpan!.parent_span_id).toBe(srvxSpan!.span_id);
103-
// });
104-
//
105-
// test('Span nesting: manual startSpan calls are children of the h3 route handler span', async ({ request }) => {
106-
// const transactionEventPromise = waitForTransaction('nitro-3', event => {
107-
// return event?.transaction === 'GET /api/test-nesting';
108-
// });
109-
//
110-
// await request.get('/api/test-nesting');
111-
//
112-
// const event = await transactionEventPromise;
113-
//
114-
// const h3HandlerSpan = event.spans?.find(
115-
// span => span.origin === 'auto.http.nitro.h3' && span.op === 'http.server',
116-
// );
117-
// expect(h3HandlerSpan).toBeDefined();
118-
//
119-
// const dbSelectSpan = event.spans?.find(span => span.op === 'db' && span.description === 'db.select');
120-
// const dbInsertSpan = event.spans?.find(span => span.op === 'db' && span.description === 'db.insert');
121-
// expect(dbSelectSpan!.parent_span_id).toBe(h3HandlerSpan!.span_id);
122-
// expect(dbInsertSpan!.parent_span_id).toBe(h3HandlerSpan!.span_id);
123-
// });
124-
12597
test('Span nesting: middleware spans start before manual spans in the span tree', async ({ request }) => {
12698
const transactionEventPromise = waitForTransaction('nitro-3', event => {
12799
return event?.transaction === 'GET /api/test-nesting';

0 commit comments

Comments
 (0)