Skip to content

Commit 5d0d145

Browse files
authored
test(node): Refactor integration tests for honoIntegration (#20397)
Each `test` run in the node-integration-tests runs a new node process so it should be avoided to have multiple `test` calls within one scenario. The tests for the `honoIntegration` in Node were especially long-running because there were 480 test cases running a `test`. This was now changed to just two `test` scenarios while running the test matrix within this one test. All 12 tests now pass in about 8 seconds. **Before:** 480 test cases (2 routes x 5 methods x 3 paths x 8 tests x 2 modes), each spawning a separate Node process. **After:** 12 test cases (6 per mode x 2 modes): 1. **Transaction tests** (1 test per mode) — loops through all 90 route/method/path/type combinations inside a single test, making 90 sequential requests against one running server process. 2. **500 error tests** (1 test per mode) — loops through all 30 combinations, ignoring transactions and asserting error events. 3. **4xx ignored error tests** (4 tests per mode) — tests each error sub-path (`/401`, `/402`, `/403`, `/does-not-exist`) with a single representative combination (`GET /sync`) instead of all 120 combinations. The 4xx error-filtering behavior (`statusCode >= 500` check in `defaultShouldHandleError`) is method/route/path-agnostic, so full matrix coverage isn't needed here.
1 parent 6284aff commit 5d0d145

File tree

1 file changed

+122
-236
lines changed
  • dev-packages/node-integration-tests/suites/tracing/hono

1 file changed

+122
-236
lines changed
Lines changed: 122 additions & 236 deletions
Original file line numberDiff line numberDiff line change
@@ -1,251 +1,137 @@
1-
import { afterAll, describe, expect } from 'vitest';
1+
import { afterAll, expect } from 'vitest';
22
import { cleanupChildProcesses, createEsmAndCjsTests } from '../../../utils/runner';
33

4-
describe('hono tracing', () => {
5-
afterAll(() => {
6-
cleanupChildProcesses();
4+
const ROUTES = ['/sync', '/async'] as const;
5+
const METHODS = ['get', 'post', 'put', 'delete', 'patch'] as const;
6+
const PATHS = ['/', '/all', '/on'] as const;
7+
8+
type Method = (typeof METHODS)[number];
9+
10+
function verifyHonoSpan(name: string, type: 'middleware' | 'request_handler') {
11+
return expect.objectContaining({
12+
data: expect.objectContaining({
13+
'hono.name': name,
14+
'hono.type': type,
15+
}),
16+
description: name,
17+
op: type === 'request_handler' ? 'request_handler.hono' : 'middleware.hono',
18+
origin: 'auto.http.otel.hono',
719
});
20+
}
821

9-
createEsmAndCjsTests(__dirname, 'scenario.mjs', 'instrument.mjs', (createRunner, test) => {
10-
describe.each(['/sync', '/async'] as const)('when using %s route', route => {
11-
describe.each(['get', 'post', 'put', 'delete', 'patch'] as const)('when using %s method', method => {
12-
describe.each(['/', '/all', '/on'])('when using %s path', path => {
13-
test('should handle transaction', async () => {
14-
const runner = createRunner()
15-
.expect({
16-
transaction: {
17-
transaction: `${method.toUpperCase()} ${route}${path === '/' ? '' : path}`,
18-
spans: expect.arrayContaining([
19-
expect.objectContaining({
20-
data: expect.objectContaining({
21-
'hono.name': 'sentryRequestMiddleware',
22-
'hono.type': 'middleware',
23-
}),
24-
description: 'sentryRequestMiddleware',
25-
op: 'middleware.hono',
26-
origin: 'auto.http.otel.hono',
27-
}),
28-
expect.objectContaining({
29-
data: expect.objectContaining({
30-
'hono.name': 'sentryErrorMiddleware',
31-
'hono.type': 'middleware',
32-
}),
33-
description: 'sentryErrorMiddleware',
34-
op: 'middleware.hono',
35-
origin: 'auto.http.otel.hono',
36-
}),
37-
expect.objectContaining({
38-
data: expect.objectContaining({
39-
'hono.name': 'global',
40-
'hono.type': 'middleware',
41-
}),
42-
description: 'global',
43-
op: 'middleware.hono',
44-
origin: 'auto.http.otel.hono',
45-
}),
46-
expect.objectContaining({
47-
data: expect.objectContaining({
48-
'hono.name': 'base',
49-
'hono.type': 'middleware',
50-
}),
51-
description: 'base',
52-
op: 'middleware.hono',
53-
origin: 'auto.http.otel.hono',
54-
}),
55-
expect.objectContaining({
56-
data: expect.objectContaining({
57-
'hono.name': `${route}${path === '/' ? '' : path}`,
58-
'hono.type': 'request_handler',
59-
}),
60-
description: `${route}${path === '/' ? '' : path}`,
61-
op: 'request_handler.hono',
62-
origin: 'auto.http.otel.hono',
63-
}),
64-
]),
65-
},
66-
})
67-
.start();
68-
runner.makeRequest(method, `${route}${path === '/' ? '' : path}`);
69-
await runner.completed();
70-
});
22+
function baseSpans() {
23+
return [
24+
verifyHonoSpan('sentryRequestMiddleware', 'middleware'),
25+
verifyHonoSpan('sentryErrorMiddleware', 'middleware'),
26+
verifyHonoSpan('global', 'middleware'),
27+
verifyHonoSpan('base', 'middleware'),
28+
];
29+
}
30+
31+
afterAll(() => {
32+
cleanupChildProcesses();
33+
});
7134

72-
test('should handle transaction with anonymous middleware', async () => {
73-
const runner = createRunner()
74-
.expect({
75-
transaction: {
76-
transaction: `${method.toUpperCase()} ${route}${path === '/' ? '' : path}/middleware`,
77-
spans: expect.arrayContaining([
78-
expect.objectContaining({
79-
data: expect.objectContaining({
80-
'hono.name': 'sentryRequestMiddleware',
81-
'hono.type': 'middleware',
82-
}),
83-
description: 'sentryRequestMiddleware',
84-
op: 'middleware.hono',
85-
origin: 'auto.http.otel.hono',
86-
}),
87-
expect.objectContaining({
88-
data: expect.objectContaining({
89-
'hono.name': 'sentryErrorMiddleware',
90-
'hono.type': 'middleware',
91-
}),
92-
description: 'sentryErrorMiddleware',
93-
op: 'middleware.hono',
94-
origin: 'auto.http.otel.hono',
95-
}),
96-
expect.objectContaining({
97-
data: expect.objectContaining({
98-
'hono.name': 'global',
99-
'hono.type': 'middleware',
100-
}),
101-
description: 'global',
102-
op: 'middleware.hono',
103-
origin: 'auto.http.otel.hono',
104-
}),
105-
expect.objectContaining({
106-
data: expect.objectContaining({
107-
'hono.name': 'base',
108-
'hono.type': 'middleware',
109-
}),
110-
description: 'base',
111-
op: 'middleware.hono',
112-
origin: 'auto.http.otel.hono',
113-
}),
114-
expect.objectContaining({
115-
data: expect.objectContaining({
116-
'hono.name': 'anonymous',
117-
'hono.type': 'middleware',
118-
}),
119-
description: 'anonymous',
120-
op: 'middleware.hono',
121-
origin: 'auto.http.otel.hono',
122-
}),
123-
expect.objectContaining({
124-
data: expect.objectContaining({
125-
'hono.name': `${route}${path === '/' ? '' : path}/middleware`,
126-
'hono.type': 'request_handler',
127-
}),
128-
description: `${route}${path === '/' ? '' : path}/middleware`,
129-
op: 'request_handler.hono',
130-
origin: 'auto.http.otel.hono',
131-
}),
132-
]),
133-
},
134-
})
135-
.start();
136-
runner.makeRequest(method, `${route}${path === '/' ? '' : path}/middleware`);
137-
await runner.completed();
35+
createEsmAndCjsTests(__dirname, 'scenario.mjs', 'instrument.mjs', (createRunner, test) => {
36+
test('should handle transactions for all route/method/path combinations', async () => {
37+
const runner = createRunner();
38+
const requests: Array<{ method: Method; url: string }> = [];
39+
40+
for (const route of ROUTES) {
41+
for (const method of METHODS) {
42+
for (const path of PATHS) {
43+
const pathSuffix = path === '/' ? '' : path;
44+
const fullPath = `${route}${pathSuffix}`;
45+
46+
runner.expect({
47+
transaction: {
48+
transaction: `${method.toUpperCase()} ${fullPath}`,
49+
spans: expect.arrayContaining([...baseSpans(), verifyHonoSpan(fullPath, 'request_handler')]),
50+
},
13851
});
52+
requests.push({ method, url: fullPath });
13953

140-
test('should handle transaction with separate middleware', async () => {
141-
const runner = createRunner()
142-
.expect({
143-
transaction: {
144-
transaction: `${method.toUpperCase()} ${route}${path === '/' ? '' : path}/middleware/separately`,
145-
spans: expect.arrayContaining([
146-
expect.objectContaining({
147-
data: expect.objectContaining({
148-
'hono.name': 'sentryRequestMiddleware',
149-
'hono.type': 'middleware',
150-
}),
151-
description: 'sentryRequestMiddleware',
152-
op: 'middleware.hono',
153-
origin: 'auto.http.otel.hono',
154-
}),
155-
expect.objectContaining({
156-
data: expect.objectContaining({
157-
'hono.name': 'sentryErrorMiddleware',
158-
'hono.type': 'middleware',
159-
}),
160-
description: 'sentryErrorMiddleware',
161-
op: 'middleware.hono',
162-
origin: 'auto.http.otel.hono',
163-
}),
164-
expect.objectContaining({
165-
data: expect.objectContaining({
166-
'hono.name': 'global',
167-
'hono.type': 'middleware',
168-
}),
169-
description: 'global',
170-
op: 'middleware.hono',
171-
origin: 'auto.http.otel.hono',
172-
}),
173-
expect.objectContaining({
174-
data: expect.objectContaining({
175-
'hono.name': 'base',
176-
'hono.type': 'middleware',
177-
}),
178-
description: 'base',
179-
op: 'middleware.hono',
180-
origin: 'auto.http.otel.hono',
181-
}),
182-
expect.objectContaining({
183-
data: expect.objectContaining({
184-
'hono.name': 'anonymous',
185-
'hono.type': 'middleware',
186-
}),
187-
description: 'anonymous',
188-
op: 'middleware.hono',
189-
origin: 'auto.http.otel.hono',
190-
}),
191-
expect.objectContaining({
192-
data: expect.objectContaining({
193-
'hono.name': `${route}${path === '/' ? '' : path}/middleware/separately`,
194-
'hono.type': 'request_handler',
195-
}),
196-
description: `${route}${path === '/' ? '' : path}/middleware/separately`,
197-
op: 'request_handler.hono',
198-
origin: 'auto.http.otel.hono',
199-
}),
200-
]),
201-
},
202-
})
203-
.start();
204-
runner.makeRequest(method, `${route}${path === '/' ? '' : path}/middleware/separately`);
205-
await runner.completed();
54+
runner.expect({
55+
transaction: {
56+
transaction: `${method.toUpperCase()} ${fullPath}/middleware`,
57+
spans: expect.arrayContaining([
58+
...baseSpans(),
59+
verifyHonoSpan('anonymous', 'middleware'),
60+
verifyHonoSpan(`${fullPath}/middleware`, 'request_handler'),
61+
]),
62+
},
20663
});
64+
requests.push({ method, url: `${fullPath}/middleware` });
20765

208-
test('should handle returned errors for %s path', async () => {
209-
const runner = createRunner()
210-
.ignore('transaction')
211-
.expect({
212-
event: {
213-
exception: {
214-
values: [
215-
{
216-
mechanism: {
217-
type: 'auto.middleware.hono',
218-
handled: false,
219-
},
220-
type: 'Error',
221-
value: 'response 500',
222-
},
223-
],
224-
},
225-
},
226-
})
227-
.start();
228-
runner.makeRequest(method, `${route}${path === '/' ? '' : path}/500`, { expectError: true });
229-
await runner.completed();
66+
runner.expect({
67+
transaction: {
68+
transaction: `${method.toUpperCase()} ${fullPath}/middleware/separately`,
69+
spans: expect.arrayContaining([
70+
...baseSpans(),
71+
verifyHonoSpan('anonymous', 'middleware'),
72+
verifyHonoSpan(`${fullPath}/middleware/separately`, 'request_handler'),
73+
]),
74+
},
23075
});
76+
requests.push({ method, url: `${fullPath}/middleware/separately` });
77+
}
78+
}
79+
}
80+
81+
const started = runner.start();
82+
for (const req of requests) {
83+
await started.makeRequest(req.method, req.url);
84+
}
85+
await started.completed();
86+
}, 60_000);
87+
88+
test('should capture 500 errors for all route/method/path combinations', async () => {
89+
const runner = createRunner().ignore('transaction');
90+
const requests: Array<{ method: Method; url: string }> = [];
23191

232-
test.each(['/401', '/402', '/403', '/does-not-exist'])(
233-
'should ignores error %s path by default',
234-
async (subPath: string) => {
235-
const runner = createRunner()
236-
.expect({
237-
transaction: {
238-
transaction: `${method.toUpperCase()} ${route}`,
92+
for (const route of ROUTES) {
93+
for (const method of METHODS) {
94+
for (const path of PATHS) {
95+
const pathSuffix = path === '/' ? '' : path;
96+
97+
runner.expect({
98+
event: {
99+
exception: {
100+
values: [
101+
{
102+
mechanism: {
103+
type: 'auto.middleware.hono',
104+
handled: false,
105+
},
106+
type: 'Error',
107+
value: 'response 500',
239108
},
240-
})
241-
.start();
242-
runner.makeRequest(method, `${route}${path === '/' ? '' : path}${subPath}`, { expectError: true });
243-
runner.makeRequest(method, route);
244-
await runner.completed();
109+
],
110+
},
245111
},
246-
);
247-
});
248-
});
249-
});
112+
});
113+
requests.push({ method, url: `${route}${pathSuffix}/500` });
114+
}
115+
}
116+
}
117+
118+
const started = runner.start();
119+
for (const req of requests) {
120+
await started.makeRequest(req.method, req.url, { expectError: true });
121+
}
122+
await started.completed();
123+
}, 60_000);
124+
125+
test.each(['/401', '/402', '/403', '/does-not-exist'])('should not capture %s errors', async (subPath: string) => {
126+
const runner = createRunner()
127+
.expect({
128+
transaction: {
129+
transaction: 'GET /sync',
130+
},
131+
})
132+
.start();
133+
runner.makeRequest('get', `/sync${subPath}`, { expectError: true });
134+
runner.makeRequest('get', '/sync');
135+
await runner.completed();
250136
});
251137
});

0 commit comments

Comments
 (0)