Skip to content

Commit bda3cd6

Browse files
logaretmclaude
andcommitted
test(nitro-3): Update e2e tests for h3 route handler tracing
Nitro 3.0.260522-beta (nitrojs/nitro#4240) wraps file-based route handlers with h3 tracing at build time, so h3 now emits route handler spans via diagnostics_channel. This updates the nitro-3 e2e tests to verify the new span nesting and error capture behavior. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent d75440c commit bda3cd6

3 files changed

Lines changed: 36 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: 33 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,42 @@ 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(
59+
span => span.origin === 'auto.http.nitro.h3' && span.op === 'http.server',
60+
);
61+
expect(h3HandlerSpan).toBeDefined();
62+
expect(h3HandlerSpan!.parent_span_id).toBe(srvxSpan!.span_id);
63+
});
64+
65+
test('Span nesting: manual startSpan calls inside route handler are children of the h3 route handler span', async ({
66+
request,
67+
}) => {
68+
const transactionEventPromise = waitForTransaction('nitro-3', event => {
69+
return event?.transaction === 'GET /api/test-nesting';
70+
});
71+
72+
await request.get('/api/test-nesting');
73+
74+
const event = await transactionEventPromise;
75+
76+
// Find the h3 route handler span
77+
const h3HandlerSpan = event.spans?.find(
78+
span => span.origin === 'auto.http.nitro.h3' && span.op === 'http.server',
79+
);
80+
expect(h3HandlerSpan).toBeDefined();
5981

6082
// Find the manually created db spans
6183
const dbSelectSpan = event.spans?.find(span => span.op === 'db' && span.description === 'db.select');
6284
const dbInsertSpan = event.spans?.find(span => span.op === 'db' && span.description === 'db.insert');
6385
expect(dbSelectSpan).toBeDefined();
6486
expect(dbInsertSpan).toBeDefined();
6587

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);
88+
// Both db spans should be children of the h3 route handler span
89+
expect(dbSelectSpan!.parent_span_id).toBe(h3HandlerSpan!.span_id);
90+
expect(dbInsertSpan!.parent_span_id).toBe(h3HandlerSpan!.span_id);
7291

7392
// Both db spans should be siblings (same parent)
7493
expect(dbSelectSpan!.parent_span_id).toBe(dbInsertSpan!.parent_span_id);
@@ -79,49 +98,6 @@ test('Span nesting: manual startSpan calls inside route handler are children of
7998
expect(serializeSpan!.parent_span_id).toBe(dbInsertSpan!.span_id);
8099
});
81100

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-
125101
test('Span nesting: middleware spans start before manual spans in the span tree', async ({ request }) => {
126102
const transactionEventPromise = waitForTransaction('nitro-3', event => {
127103
return event?.transaction === 'GET /api/test-nesting';

0 commit comments

Comments
 (0)