Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
358 changes: 122 additions & 236 deletions dev-packages/node-integration-tests/suites/tracing/hono/test.ts
Original file line number Diff line number Diff line change
@@ -1,251 +1,137 @@
import { afterAll, describe, expect } from 'vitest';
import { afterAll, expect } from 'vitest';
import { cleanupChildProcesses, createEsmAndCjsTests } from '../../../utils/runner';

describe('hono tracing', () => {
afterAll(() => {
cleanupChildProcesses();
const ROUTES = ['/sync', '/async'] as const;
const METHODS = ['get', 'post', 'put', 'delete', 'patch'] as const;
const PATHS = ['/', '/all', '/on'] as const;

type Method = (typeof METHODS)[number];

function verifyHonoSpan(name: string, type: 'middleware' | 'request_handler') {
return expect.objectContaining({
data: expect.objectContaining({
'hono.name': name,
'hono.type': type,
}),
description: name,
op: type === 'request_handler' ? 'request_handler.hono' : 'middleware.hono',
origin: 'auto.http.otel.hono',
});
}

createEsmAndCjsTests(__dirname, 'scenario.mjs', 'instrument.mjs', (createRunner, test) => {
describe.each(['/sync', '/async'] as const)('when using %s route', route => {
describe.each(['get', 'post', 'put', 'delete', 'patch'] as const)('when using %s method', method => {
describe.each(['/', '/all', '/on'])('when using %s path', path => {
test('should handle transaction', async () => {
const runner = createRunner()
.expect({
transaction: {
transaction: `${method.toUpperCase()} ${route}${path === '/' ? '' : path}`,
spans: expect.arrayContaining([
expect.objectContaining({
data: expect.objectContaining({
'hono.name': 'sentryRequestMiddleware',
'hono.type': 'middleware',
}),
description: 'sentryRequestMiddleware',
op: 'middleware.hono',
origin: 'auto.http.otel.hono',
}),
expect.objectContaining({
data: expect.objectContaining({
'hono.name': 'sentryErrorMiddleware',
'hono.type': 'middleware',
}),
description: 'sentryErrorMiddleware',
op: 'middleware.hono',
origin: 'auto.http.otel.hono',
}),
expect.objectContaining({
data: expect.objectContaining({
'hono.name': 'global',
'hono.type': 'middleware',
}),
description: 'global',
op: 'middleware.hono',
origin: 'auto.http.otel.hono',
}),
expect.objectContaining({
data: expect.objectContaining({
'hono.name': 'base',
'hono.type': 'middleware',
}),
description: 'base',
op: 'middleware.hono',
origin: 'auto.http.otel.hono',
}),
expect.objectContaining({
data: expect.objectContaining({
'hono.name': `${route}${path === '/' ? '' : path}`,
'hono.type': 'request_handler',
}),
description: `${route}${path === '/' ? '' : path}`,
op: 'request_handler.hono',
origin: 'auto.http.otel.hono',
}),
]),
},
})
.start();
runner.makeRequest(method, `${route}${path === '/' ? '' : path}`);
await runner.completed();
});
function baseSpans() {
return [
verifyHonoSpan('sentryRequestMiddleware', 'middleware'),
verifyHonoSpan('sentryErrorMiddleware', 'middleware'),
verifyHonoSpan('global', 'middleware'),
verifyHonoSpan('base', 'middleware'),
];
}

afterAll(() => {
cleanupChildProcesses();
});

test('should handle transaction with anonymous middleware', async () => {
const runner = createRunner()
.expect({
transaction: {
transaction: `${method.toUpperCase()} ${route}${path === '/' ? '' : path}/middleware`,
spans: expect.arrayContaining([
expect.objectContaining({
data: expect.objectContaining({
'hono.name': 'sentryRequestMiddleware',
'hono.type': 'middleware',
}),
description: 'sentryRequestMiddleware',
op: 'middleware.hono',
origin: 'auto.http.otel.hono',
}),
expect.objectContaining({
data: expect.objectContaining({
'hono.name': 'sentryErrorMiddleware',
'hono.type': 'middleware',
}),
description: 'sentryErrorMiddleware',
op: 'middleware.hono',
origin: 'auto.http.otel.hono',
}),
expect.objectContaining({
data: expect.objectContaining({
'hono.name': 'global',
'hono.type': 'middleware',
}),
description: 'global',
op: 'middleware.hono',
origin: 'auto.http.otel.hono',
}),
expect.objectContaining({
data: expect.objectContaining({
'hono.name': 'base',
'hono.type': 'middleware',
}),
description: 'base',
op: 'middleware.hono',
origin: 'auto.http.otel.hono',
}),
expect.objectContaining({
data: expect.objectContaining({
'hono.name': 'anonymous',
'hono.type': 'middleware',
}),
description: 'anonymous',
op: 'middleware.hono',
origin: 'auto.http.otel.hono',
}),
expect.objectContaining({
data: expect.objectContaining({
'hono.name': `${route}${path === '/' ? '' : path}/middleware`,
'hono.type': 'request_handler',
}),
description: `${route}${path === '/' ? '' : path}/middleware`,
op: 'request_handler.hono',
origin: 'auto.http.otel.hono',
}),
]),
},
})
.start();
runner.makeRequest(method, `${route}${path === '/' ? '' : path}/middleware`);
await runner.completed();
createEsmAndCjsTests(__dirname, 'scenario.mjs', 'instrument.mjs', (createRunner, test) => {
test('should handle transactions for all route/method/path combinations', async () => {
const runner = createRunner();
const requests: Array<{ method: Method; url: string }> = [];

for (const route of ROUTES) {
for (const method of METHODS) {
for (const path of PATHS) {
const pathSuffix = path === '/' ? '' : path;
const fullPath = `${route}${pathSuffix}`;

runner.expect({
transaction: {
transaction: `${method.toUpperCase()} ${fullPath}`,
spans: expect.arrayContaining([...baseSpans(), verifyHonoSpan(fullPath, 'request_handler')]),
},
});
requests.push({ method, url: fullPath });

test('should handle transaction with separate middleware', async () => {
const runner = createRunner()
.expect({
transaction: {
transaction: `${method.toUpperCase()} ${route}${path === '/' ? '' : path}/middleware/separately`,
spans: expect.arrayContaining([
expect.objectContaining({
data: expect.objectContaining({
'hono.name': 'sentryRequestMiddleware',
'hono.type': 'middleware',
}),
description: 'sentryRequestMiddleware',
op: 'middleware.hono',
origin: 'auto.http.otel.hono',
}),
expect.objectContaining({
data: expect.objectContaining({
'hono.name': 'sentryErrorMiddleware',
'hono.type': 'middleware',
}),
description: 'sentryErrorMiddleware',
op: 'middleware.hono',
origin: 'auto.http.otel.hono',
}),
expect.objectContaining({
data: expect.objectContaining({
'hono.name': 'global',
'hono.type': 'middleware',
}),
description: 'global',
op: 'middleware.hono',
origin: 'auto.http.otel.hono',
}),
expect.objectContaining({
data: expect.objectContaining({
'hono.name': 'base',
'hono.type': 'middleware',
}),
description: 'base',
op: 'middleware.hono',
origin: 'auto.http.otel.hono',
}),
expect.objectContaining({
data: expect.objectContaining({
'hono.name': 'anonymous',
'hono.type': 'middleware',
}),
description: 'anonymous',
op: 'middleware.hono',
origin: 'auto.http.otel.hono',
}),
expect.objectContaining({
data: expect.objectContaining({
'hono.name': `${route}${path === '/' ? '' : path}/middleware/separately`,
'hono.type': 'request_handler',
}),
description: `${route}${path === '/' ? '' : path}/middleware/separately`,
op: 'request_handler.hono',
origin: 'auto.http.otel.hono',
}),
]),
},
})
.start();
runner.makeRequest(method, `${route}${path === '/' ? '' : path}/middleware/separately`);
await runner.completed();
runner.expect({
transaction: {
transaction: `${method.toUpperCase()} ${fullPath}/middleware`,
spans: expect.arrayContaining([
...baseSpans(),
verifyHonoSpan('anonymous', 'middleware'),
verifyHonoSpan(`${fullPath}/middleware`, 'request_handler'),
]),
},
});
requests.push({ method, url: `${fullPath}/middleware` });

test('should handle returned errors for %s path', async () => {
const runner = createRunner()
.ignore('transaction')
.expect({
event: {
exception: {
values: [
{
mechanism: {
type: 'auto.middleware.hono',
handled: false,
},
type: 'Error',
value: 'response 500',
},
],
},
},
})
.start();
runner.makeRequest(method, `${route}${path === '/' ? '' : path}/500`, { expectError: true });
await runner.completed();
runner.expect({
transaction: {
transaction: `${method.toUpperCase()} ${fullPath}/middleware/separately`,
spans: expect.arrayContaining([
...baseSpans(),
verifyHonoSpan('anonymous', 'middleware'),
verifyHonoSpan(`${fullPath}/middleware/separately`, 'request_handler'),
]),
},
});
requests.push({ method, url: `${fullPath}/middleware/separately` });
}
}
}

const started = runner.start();
for (const req of requests) {
await started.makeRequest(req.method, req.url);
}
await started.completed();
}, 60_000);

test('should capture 500 errors for all route/method/path combinations', async () => {
const runner = createRunner().ignore('transaction');
const requests: Array<{ method: Method; url: string }> = [];

test.each(['/401', '/402', '/403', '/does-not-exist'])(
'should ignores error %s path by default',
async (subPath: string) => {
const runner = createRunner()
.expect({
transaction: {
transaction: `${method.toUpperCase()} ${route}`,
for (const route of ROUTES) {
for (const method of METHODS) {
for (const path of PATHS) {
const pathSuffix = path === '/' ? '' : path;

runner.expect({
event: {
exception: {
values: [
{
mechanism: {
type: 'auto.middleware.hono',
handled: false,
},
type: 'Error',
value: 'response 500',
},
})
.start();
runner.makeRequest(method, `${route}${path === '/' ? '' : path}${subPath}`, { expectError: true });
runner.makeRequest(method, route);
await runner.completed();
],
},
},
);
});
});
});
});
requests.push({ method, url: `${route}${pathSuffix}/500` });
}
}
}

const started = runner.start();
for (const req of requests) {
await started.makeRequest(req.method, req.url, { expectError: true });
}
await started.completed();
}, 60_000);

test.each(['/401', '/402', '/403', '/does-not-exist'])('should not capture %s errors', async (subPath: string) => {
const runner = createRunner()
.expect({
transaction: {
transaction: 'GET /sync',
},
})
.start();
runner.makeRequest('get', `/sync${subPath}`, { expectError: true });
runner.makeRequest('get', '/sync');
await runner.completed();
});
});
Loading