User Stories 1-2: every completed request (including 404s and early replies from other hooks) now emits exactly one structured access-log line, and every log line produced during that request's handling shares its requestId/correlationId via a new AsyncLocalStorage-backed Pino mixin — with zero changes to any existing log call site. The previously-dead supporthub_http_request_duration_seconds histogram now actually receives observations, so error rate and latency per route are computable from /metrics alone. Also bumps @opentelemetry/sdk-trace-base 1.x -> 2.x to align with the two new tracing dependencies added in this same branch (exporter-trace-otlp-http, resources) onto one consistent major version — npm had otherwise installed two incompatible OTel core/resources majors side by side, which also happened to resolve a moderate DoS advisory in @opentelemetry/core <2.8.0. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
41 lines
1.5 KiB
TypeScript
41 lines
1.5 KiB
TypeScript
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
|
|
import { buildApp } from '@/app';
|
|
import { FastifyInstance } from 'fastify';
|
|
|
|
/** Covers specs/014-full-observability/quickstart.md Scenario 2 against a real running app. */
|
|
describe('Live request-health metrics (User Story 2)', () => {
|
|
let app: FastifyInstance;
|
|
|
|
beforeAll(async () => {
|
|
app = await buildApp();
|
|
});
|
|
|
|
afterAll(async () => {
|
|
await app.close();
|
|
});
|
|
|
|
it('records request-duration observations labeled by method/route/status for both success and failure', async () => {
|
|
const email = `metrics-test-${Date.now()}@supporthub.test`;
|
|
await app.inject({ method: 'POST', url: '/auth/login', payload: { email, password: 'wrong' } });
|
|
await app.inject({
|
|
method: 'POST',
|
|
url: '/auth/login',
|
|
payload: { email, password: 'wrong2' },
|
|
});
|
|
|
|
const metricsRes = await app.inject({ method: 'GET', url: '/metrics' });
|
|
expect(metricsRes.statusCode).toBe(200);
|
|
|
|
expect(metricsRes.body).toMatch(
|
|
/supporthub_http_request_duration_seconds_count\{method="POST",route="\/auth\/login",status_code="401"\}\s+\d+/,
|
|
);
|
|
|
|
// A second scrape's body reflects the first scrape's own request too — proves 2xx routes
|
|
// are observed just as 4xx ones are, not only error paths.
|
|
const secondScrape = await app.inject({ method: 'GET', url: '/metrics' });
|
|
expect(secondScrape.body).toMatch(
|
|
/supporthub_http_request_duration_seconds_count\{method="GET",route="\/metrics",status_code="200"\}\s+\d+/,
|
|
);
|
|
});
|
|
});
|