// Existing status snapshots only: no API calls, bounded histories and honest no-data. import assert from 'node:assert/strict'; import fs from 'node:fs'; import vm from 'node:vm'; const source = fs.readFileSync('src/admin/web/app/overview-extra-metrics.js', 'utf8'); const registry = JSON.parse(fs.readFileSync('src/admin/web/dashboard-widgets.json', 'utf8')); const ids = registry.filter(widget => widget.category === 'performance').map(widget => widget.id); assert.equal(ids.length, 18); const widgets = new Map(ids.map(id => [id, { hidden: false }])); const fields = new Map(); const field = id => { if (!fields.has(id)) fields.set(id, { textContent: '-', points: '', setAttribute(name, value) { this[name] = value; } }); return fields.get(id); }; let now = 1000; const context = { window: {}, currentPanel: 'overview', performance: { now: () => now }, t: key => key, fmt: (v, places = 0) => Number(v).toFixed(places), fmtBytes: v => `${v} B`, $: field, document: { querySelector: selector => widgets.get(selector.match(/data-dashboard-widget="([^"]+)"/)?.[1]) || null }, }; vm.createContext(context); vm.runInContext(fs.readFileSync('src/admin/web/app/rate-sampling.js', 'utf8'), context); vm.runInContext(source, context); const render = context.window.rustProxyExtraOverview.render; const text = id => field(`overview-extra-${id}`).textContent; const points = id => field(`overview-extra-${id}-line`).points; const start = { metrics: { requests: 100, http1_requests: 70, http2_requests: 30, http3_requests: 0, tls_handshake_errors: 3, errors: 10, active_connections: 5, active: 2, websocket_active: 1, mean_duration_ms: 25 }, system: { process_cpu_percent: 12, process_rss_bytes: 1000, open_fds: 10, max_fds: 500 }, cache: { hits: 10, misses: 10, resident_bytes: 10, hot_limit_bytes: 100 }, backend_pools: [{ members: [{status: 'up'}, {status: 'down'}] }], }; render(start); assert.equal(text('extra_process_cpu'), '12.0%'); assert.equal(text('extra_process_rss'), '1000 B'); assert.equal(text('extra_backend_unhealthy'), '1'); for (const id of ['extra_tls_error_rate','extra_request_error_rate','extra_cache_hit_rate','extra_h2_share']) assert.equal(text(id), '-', `initial ${id} delta must not be invented`); now = 3000; const next = structuredClone(start); next.metrics = { ...start.metrics, requests: 130, http1_requests: 84, http2_requests: 45, http3_requests: 1, tls_handshake_errors: 5, errors: 16, active_connections: 6, active: 4, websocket_active: 3, mean_duration_ms: 40 }; next.system = { ...start.system, process_cpu_percent: 22, process_rss_bytes: 2000, open_fds: 20 }; next.cache = { ...start.cache, hits: 20, misses: 15, resident_bytes: 25 }; next.backend_pools[0].members[0].status = 'down'; render(next); assert.equal(text('extra_tls_error_rate'), '1.0/s'); assert.equal(text('extra_request_error_rate'), '3.0/s'); assert.equal(text('extra_cache_hit_rate'), '66.7%'); assert.equal(text('extra_h2_share'), '50.0%'); assert.equal(text('extra_cache_memory'), '25.0%'); assert.equal(text('extra_response_latency'), '40.0 ms'); assert.equal(text('extra_backend_unhealthy'), '2'); assert.equal(text('extra_total_rps'), '15.0/s'); assert.equal(text('extra_active_exchanges'), '4'); assert.equal(text('extra_h1_share'), '46.7%'); assert.equal(text('extra_h3_share'), '3.3%'); assert.equal(text('extra_cache_miss_rate'), '2.5/s'); assert.equal(text('extra_backend_active'), '0'); assert.equal(points('extra_process_cpu').trim().split(/\s+/).length, 2); // Hiding a chart stops sampling; stale cumulative counters are never used as an instant rate. widgets.get('extra_tls_error_rate').hidden = true; now = 5000; render({ ...next, metrics: { ...next.metrics, tls_handshake_errors: 50 } }); assert.equal(text('extra_tls_error_rate'), '1.0/s'); widgets.get('extra_tls_error_rate').hidden = false; now = 6000; render({ ...next, metrics: { ...next.metrics, tls_handshake_errors: 60 } }); assert.equal(text('extra_tls_error_rate'), '-', 'first fresh visible sample establishes a new baseline'); // After a reset, don't return a negative rate or denominator percentage. now = 7000; render({ ...next, metrics: { ...next.metrics, requests: 1, http2_requests: 0, tls_handshake_errors: 0, errors: 0 }, cache: { ...next.cache, hits: 0, misses: 0 } }); assert.equal(text('extra_h2_share'), '-'); assert.equal(text('extra_request_error_rate'), '-'); assert.equal(text('extra_tls_error_rate'), '-'); // Explicit unavailability is not represented as a measured zero. now = 8000; render({ metrics: {}, system: {}, cache: {} }); assert.equal(text('extra_process_cpu'), '-'); assert.equal(text('extra_websocket_active'), '-'); assert.equal(text('extra_backend_unhealthy'), '-'); context.window.rustProxyExtraOverview.reset(); now = 9000; for (let index = 0; index < 50; index++) { now += 1000; render({ ...next, system: { ...next.system, process_cpu_percent: index } }); } assert.equal(points('extra_process_cpu').trim().split(/\s+/).length, 30, 'bounded history'); context.currentPanel = 'vhosts'; const pointBefore = points('extra_process_cpu'); now += 1000; render(next); assert.equal(points('extra_process_cpu'), pointBefore, 'no extra sampling outside Overview'); assert.match(source, /if \(!tile \|\| tile.hidden\)/); console.log('PASS: 18 extra metric series, time-based deltas, no-data, counter resets, hidden opt-ins and 30-sample bounds');