import test from 'node:test'; import assert from 'node:assert/strict'; import { readFile } from 'node:fs/promises'; import { DEFAULT_ALERT_RULES } from '../src/release-observability.js'; const runbookPath = new URL('../docs/RELEASE-OBSERVABILITY.md', import.meta.url); const packagePath = new URL('../package.json', import.meta.url); test('every default alert rule is inventoried in the runbook with owner, threshold, and severity', async () => { const runbook = await readFile(runbookPath, 'utf8'); const headingSlugs = [...runbook.matchAll(/^#{2,4} (.+)$/gm)] .map(match => match[1].toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '')); for (const rule of DEFAULT_ALERT_RULES) { const pattern = new RegExp( `\\| \`${rule.id}\`\\s*\\| ${rule.severity}\\s*\\| ${rule.owner}\\s*\\|`, ); assert.match(runbook, pattern, `runbook table lacks a conforming row for ${rule.id}`); const anchor = rule.runbook.split('#')[1]; assert.ok(anchor && headingSlugs.includes(anchor), `runbook link for ${rule.id} does not resolve to a heading (${anchor})`); } }); test('runbook table states each rule\'s actual numeric threshold, matching DEFAULT_ALERT_RULES', async () => { const runbook = await readFile(runbookPath, 'utf8'); const rows = Object.fromEntries( runbook.split('\n') .filter(line => line.startsWith('| `')) .map(line => [line.slice(3, line.indexOf('`', 3)), line]), ); for (const rule of DEFAULT_ALERT_RULES) { // The threshold must appear as a real number in the rule's own table row, // not merely somewhere in prose, so the documented contract cannot drift // from what evaluateAlerts() enforces. const row = rows[rule.id]; assert.ok(row, `runbook table has no row for ${rule.id}`); assert.match( row, new RegExp(`\\| ${rule.threshold}\\b`), `runbook row for ${rule.id} must state its real numeric threshold ${rule.threshold}`, ); } const backlogRule = DEFAULT_ALERT_RULES.find(rule => rule.id === 'queue.backlog'); assert.equal(backlogRule.threshold, 10, 'queue.backlog must fire at exactly depth 10'); assert.match(runbook, /depth >= 10/, 'runbook must state the queue.backlog boundary as depth >= 10'); }); test('runbook documents the outage drill anchor, manual fallback, and privacy boundary', async () => { const runbook = await readFile(runbookPath, 'utf8'); assert.match(runbook, /^## Simulate a worker outage$/m); assert.match(runbook, /^## Alert inventory$/m); assert.match(runbook, /manual fallback/i); for (const forbidden of ['photos or medical imagery', 'stool records', 'session identifiers', 'environment dumps']) { assert.match(runbook, new RegExp(forbidden.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&'), 'i')); } const externalUrls = [...runbook.matchAll(/https?:\/\/(?!127\.0\.0\.1)[^\s)`\]]+/g)].map(match => match[0]); assert.deepEqual(externalUrls, [], 'runbook must not reference non-loopback hosts'); }); test('package scripts wire observability tests into the suite and syntax gate', async () => { const packageJson = JSON.parse(await readFile(packagePath, 'utf8')); assert.match(packageJson.scripts.test, /tests\/release-observability\.test\.js/); assert.match(packageJson.scripts.test, /tests\/release-dashboard\.test\.js/); assert.match(packageJson.scripts.test, /tests\/release-dashboard-cli\.test\.js/); assert.match(packageJson.scripts['check:syntax'], /node --check scripts\/release_dashboard\.mjs/); });