Documentation tells users to scrape /metrics (the Prometheus convention) but the route only existed at /api/v1/metrics/prometheus. The root-level /metrics returned the SPA HTML fallback via Caddy. - Add GET /metrics to app.js (same output as /api/v1/metrics/prometheus) - Add /metrics to PUBLIC_ROUTES in middleware.js (no auth required) - Add /metrics to rate-limiter skip list - Add 5 tests in metrics-root-endpoint.test.js - Add Caddy route for /metrics on test server - All 1775 tests pass Codex grade: B (no blocking issues)
68 lines
2.3 KiB
JavaScript
68 lines
2.3 KiB
JavaScript
/**
|
|
* Root-level /metrics endpoint tests — DC-097b
|
|
*
|
|
* Verifies that:
|
|
* - GET /metrics returns Prometheus text format (not JSON, not HTML)
|
|
* - The Content-Type is text/plain with Prometheus version
|
|
* - The response includes HELP/TYPE annotations and metric names
|
|
* - The endpoint is listed in PUBLIC_ROUTES (no auth required)
|
|
* - The endpoint is in the rate-limiter skip list
|
|
*
|
|
* Documentation tells users to scrape /metrics (the Prometheus convention),
|
|
* but the route previously only existed at /api/v1/metrics/prometheus. The
|
|
* root-level alias makes doc examples work without modification.
|
|
*/
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
|
|
const MIDDLEWARE_PATH = path.join(__dirname, '../src/utilities/middleware.js');
|
|
const APP_PATH = path.join(__dirname, '../src/app.js');
|
|
|
|
describe('Root-level /metrics endpoint — DC-097b', () => {
|
|
describe('PUBLIC_ROUTES includes /metrics', () => {
|
|
let mwSource;
|
|
beforeAll(() => {
|
|
mwSource = fs.readFileSync(MIDDLEWARE_PATH, 'utf8');
|
|
});
|
|
|
|
test('/metrics is in PUBLIC_ROUTES', () => {
|
|
// Match the route entry: { path: '/metrics', ... method: 'GET' }
|
|
expect(mwSource).toMatch(/['"]\/metrics['"]/);
|
|
});
|
|
|
|
test('/metrics is in the rate-limiter skip list', () => {
|
|
expect(mwSource).toMatch(/req\.path\s*===\s*['"]\/metrics['"]/);
|
|
});
|
|
});
|
|
|
|
describe('app.js registers GET /metrics', () => {
|
|
let appSource;
|
|
beforeAll(() => {
|
|
appSource = fs.readFileSync(APP_PATH, 'utf8');
|
|
});
|
|
|
|
test('app.get("/metrics", ...) is registered', () => {
|
|
expect(appSource).toMatch(/app\.get\(\s*['"]\/metrics['"]/);
|
|
});
|
|
|
|
test('/metrics handler sets Prometheus Content-Type', () => {
|
|
// The handler should set Content-Type to text/plain with prometheus version
|
|
expect(appSource).toMatch(/text\/plain.*version=0\.0\.4/);
|
|
});
|
|
});
|
|
|
|
describe('Parity with /api/v1/metrics/prometheus', () => {
|
|
let appSource;
|
|
beforeAll(() => {
|
|
appSource = fs.readFileSync(APP_PATH, 'utf8');
|
|
});
|
|
|
|
test('both endpoints call metrics.toPrometheus()', () => {
|
|
const matches = appSource.match(/metrics\.toPrometheus\(\)/g);
|
|
expect(matches).toBeTruthy();
|
|
// At least two call sites: /api/v1/metrics/prometheus and /metrics
|
|
expect(matches.length).toBeGreaterThanOrEqual(2);
|
|
});
|
|
});
|
|
});
|