/** * ICD Test Runner — Observability CRUD Tests (v0.33.0) * Tests /metrics, /api/docs, /admin/dashboard, X-Request-Id, structured logging. */ (function () { 'use strict'; var T = window.ICD; if (!T) return; T.crud.observability = async function () { // -- GET /metrics (Prometheus endpoint, no auth required) -- await T.test('crud', 'observability', 'GET /metrics returns Prometheus text', async function () { var resp = await fetch(T.base + '/metrics'); T.assert(resp.ok, 'expected 200 from /metrics, got ' + resp.status); var text = await resp.text(); T.assert(text.indexOf('armature_http_requests_total') !== -1, 'expected armature_http_requests_total in /metrics'); T.assert(text.indexOf('armature_http_request_duration_seconds') !== -1, 'expected armature_http_request_duration_seconds in /metrics'); T.assert(text.indexOf('armature_websocket_connections') !== -1, 'expected armature_websocket_connections in /metrics'); }); // -- GET /api/docs (Swagger UI) -- await T.test('crud', 'observability', 'GET /api/docs returns Swagger UI HTML', async function () { var resp = await fetch(T.base + '/api/docs'); T.assert(resp.ok, 'expected 200 from /api/docs, got ' + resp.status); var text = await resp.text(); T.assert(text.indexOf('swagger-ui') !== -1, 'expected swagger-ui in /api/docs HTML'); }); // -- GET /api/docs/openapi.yaml -- await T.test('crud', 'observability', 'GET /api/docs/openapi.yaml returns valid YAML', async function () { var resp = await fetch(T.base + '/api/docs/openapi.yaml'); T.assert(resp.ok, 'expected 200 from /api/docs/openapi.yaml, got ' + resp.status); var text = await resp.text(); T.assert(text.indexOf('openapi:') !== -1, 'expected openapi: key in YAML'); T.assert(text.indexOf('paths:') !== -1, 'expected paths: key in YAML'); }); // -- X-Request-Id header propagation -- await T.test('crud', 'observability', 'X-Request-Id header on API responses', async function () { var resp = await fetch(T.base + '/health'); T.assert(resp.ok, 'expected 200'); var rid = resp.headers.get('X-Request-Id'); T.assert(rid, 'expected X-Request-Id header in response'); T.assert(rid.length === 36, 'X-Request-Id should be UUID (36 chars), got ' + rid.length); }); // -- X-Request-Id passthrough (client sends, server echoes) -- await T.test('crud', 'observability', 'X-Request-Id passthrough', async function () { var customId = 'test-' + Date.now(); var resp = await fetch(T.base + '/health', { headers: { 'X-Request-Id': customId } }); T.assert(resp.ok, 'expected 200'); var echoed = resp.headers.get('X-Request-Id'); T.assert(echoed === customId, 'expected echoed X-Request-Id "' + customId + '", got "' + echoed + '"'); }); // -- GET /admin/dashboard (admin auth required) -- await T.test('crud', 'observability', 'GET /admin/dashboard returns dashboard data', async function () { var d = await T.apiGet('/admin/dashboard'); T.assertHasKey(d, 'uptime', '/admin/dashboard'); T.assertHasKey(d, 'ws_connections', '/admin/dashboard'); T.assertHasKey(d, 'provider_health', '/admin/dashboard'); T.assert(typeof d.ws_connections === 'number', 'ws_connections should be number'); T.assert(typeof d.uptime === 'string', 'uptime should be string'); // provider_health can be null or array if (d.provider_health) { T.assert(Array.isArray(d.provider_health), 'provider_health should be array'); } }); }; })();