All checks were successful
Quality gates / quality (pull_request) Successful in 2m9s
Strict vertical RED-GREEN TDD across eight review blockers: - drill baseline: requires every required boundary check explicitly passing plus zero alerts of any severity before anything is announced or flipped; unknown, degraded, malformed, missing-boundary, and backlog-warning baselines exit 2 with switch count zero, and stdout stays empty whenever no drill actually ran - manual fallback: deterministic truthful states — app-down, local-journal, none-required, and new app-state-unknown (availability null) so unknown telemetry can never render as proven availability or 'no degradation'; runbook gains a state table with exact wording - exact schema types: schemaVersion must be JSON number 1; counters and latency must be finite bounded integers (0..1e6, 0..3600000); generatedAtUtc must be canonical real UTC surviving round-trip; releaseTag/commit keep exact safe types; coercions reject - control-safety: controlSafe strips LF/CR too, so hostile paths can no longer forge terminal lines (regression test ships a path embedding LF + forged PAGE text); read errors use fixed wording instead of echoing attacker-derived message bytes - malformed checks reject the whole evidence object instead of silently disappearing beside healthy checks; forbidden top-level keys now fail closed rather than being ignored - status/failureClass consistency: pass/unknown checks carrying any failureClass reject the file, so all-pass evidence can never page worker.outage (fail/degraded may stay classless, preserving the closed vocabulary and nullable schema) - sanitization idempotence: omitted latency stays omitted on a second pass; explicit null latency rejects since the schema forbids it - removed the always-false queue-depth tautology in the shared fixture and made the runbook contract test verify each rule's real numeric threshold against DEFAULT_ALERT_RULES Preserved: closed failure-class vocabulary, queue depth 9/10/11 edges, raw loopback-origin validation, app-down fallback, missing-boundary rejection, transition semantics, and every independent drill case.
67 lines
3.4 KiB
JavaScript
67 lines
3.4 KiB
JavaScript
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/);
|
|
});
|