/** * Unit tests for src/routes/metrics.ts * * Verifies that GET /metrics returns 200 with Prometheus exposition format * and does NOT require authentication. */ import express, { Application } from 'express'; import request from 'supertest'; import { createMetricsRouter } from '../../../src/routes/metrics'; import { metricsRegistry } from '../../../src/metrics/registry'; // ──────────────────────────────────────────────────────────────────────────── // Helpers // ──────────────────────────────────────────────────────────────────────────── /** Build a minimal Express app that mounts only the metrics router. */ function buildTestApp(): Application { const app = express(); app.use('/metrics', createMetricsRouter()); return app; } // ──────────────────────────────────────────────────────────────────────────── // Tests // ──────────────────────────────────────────────────────────────────────────── describe('GET /metrics', () => { let app: Application; beforeEach(() => { metricsRegistry.resetMetrics(); app = buildTestApp(); }); it('returns HTTP 200', async () => { const res = await request(app).get('/metrics'); expect(res.status).toBe(200); }); it('returns Content-Type containing text/plain', async () => { const res = await request(app).get('/metrics'); expect(res.headers['content-type']).toMatch(/text\/plain/); }); it('does NOT require an Authorization header', async () => { // Call without any auth header — must still succeed const res = await request(app).get('/metrics'); expect(res.status).toBe(200); expect(res.status).not.toBe(401); expect(res.status).not.toBe(403); }); it('response body contains agentidp_tokens_issued_total', async () => { const res = await request(app).get('/metrics'); expect(res.text).toContain('agentidp_tokens_issued_total'); }); it('response body contains agentidp_agents_registered_total', async () => { const res = await request(app).get('/metrics'); expect(res.text).toContain('agentidp_agents_registered_total'); }); it('response body contains agentidp_http_requests_total', async () => { const res = await request(app).get('/metrics'); expect(res.text).toContain('agentidp_http_requests_total'); }); it('response body contains agentidp_http_request_duration_seconds', async () => { const res = await request(app).get('/metrics'); expect(res.text).toContain('agentidp_http_request_duration_seconds'); }); it('response body contains agentidp_db_query_duration_seconds', async () => { const res = await request(app).get('/metrics'); expect(res.text).toContain('agentidp_db_query_duration_seconds'); }); it('response body contains agentidp_redis_command_duration_seconds', async () => { const res = await request(app).get('/metrics'); expect(res.text).toContain('agentidp_redis_command_duration_seconds'); }); it('response body is valid Prometheus text exposition format (starts with # HELP or TYPE)', async () => { const res = await request(app).get('/metrics'); // Prometheus text format always begins with comment lines starting with '# ' expect(res.text).toMatch(/^# (HELP|TYPE)/m); }); });