Files
timmy-talking-turd/tests/production-runbook.test.py
T
Timmy 48f0f56835
Quality gates / quality (pull_request) Canceled after 0s
feat(ops): operator backup, restore, and production runbook (#39)
The app keeps the journal in the person's browser, so the server has no diary to lose. What an
operator can lose is the corpus: the append-only manifest.jsonl with its provenance records and its
withdrawal tombstones. scripts/operator_backup.py archives only records and names every path it
refuses (model weights, transient inference images, secrets, logs, symlinks, oversize) with the
reason, writes one SHA-256 per stored member, and produces a reproducible archive.

restore re-verifies every digest, member name, and record count before it writes a byte, and refuses
absolute, traversal, link, unlisted, and non-empty-target cases, so a tampered archive or a lost
tombstone fails loudly. docs/PRODUCTION-RUNBOOK.md covers topology, network boundaries (including
the app's real 0.0.0.0 default and the staging HOST=127.0.0.1 template), the four secrets, retention,
and both rollback paths; its documented commands are parsed against the real CLIs by its test.

The release demo runs the real backup, verify, and restore against a synthetic corpus built by the
real ingest and revoke commands and shows what was stored, what was refused, and what came back.
2026-10-29 11:02:59 +00:00

162 lines
7.8 KiB
Python

#!/usr/bin/env python3
"""Tests for the production topology, secrets, backup, and rollback runbook.
A runbook is only worth reading if its commands are the ones the tools actually accept, so this
suite parses every documented ``python3 scripts/...`` command out of the markdown and checks each
flag against the real CLI help. It also fails if the document starts carrying a real secret, or if
it keeps claiming the server holds no journal after ``server.mjs`` starts writing one.
"""
from __future__ import annotations
import re
import shlex
import subprocess
import sys
import unittest
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
RUNBOOK = ROOT / "docs" / "PRODUCTION-RUNBOOK.md"
SERVER = ROOT / "server.mjs"
BACKUP = ROOT / "scripts" / "operator_backup.py"
FENCE = re.compile(r"```bash\n(.*?)```", re.S)
SECRET_ASSIGNMENT = re.compile(
r"(?i)(?:token|secret|password|passwd|api[_-]?key|salt)\s*=\s*(?!<|\$|\"|\'|\.\.\.)[^\s`\"']{6,}"
)
def text() -> str:
return RUNBOOK.read_text(encoding="utf-8")
def sections() -> dict[str, str]:
body = text()
headings = list(re.finditer(r"^## (.+)$", body, re.M))
out: dict[str, str] = {}
for index, match in enumerate(headings):
end = headings[index + 1].start() if index + 1 < len(headings) else len(body)
out[match.group(1).strip().lower()] = body[match.end():end]
return out
def documented_commands() -> list[str]:
commands: list[str] = []
for block in FENCE.findall(text()):
for line in block.splitlines():
line = line.strip()
if line.startswith("python3 scripts/"):
commands.append(line)
return commands
def help_text(script: str, subcommand: str | None) -> str:
argv = [sys.executable, script] + ([subcommand] if subcommand else []) + ["--help"]
result = subprocess.run(argv, cwd=ROOT, capture_output=True, text=True)
assert result.returncode == 0, f"{' '.join(argv)} failed: {result.stderr}"
return result.stdout
class ProductionRunbookTests(unittest.TestCase):
def test_runbook_covers_topology_network_boundaries_secrets_backup_restore_and_rollback(self) -> None:
self.assertTrue(RUNBOOK.is_file(), "docs/PRODUCTION-RUNBOOK.md must exist")
titles = sections().keys()
for required in ("topology", "network boundaries", "secrets", "backup", "restore", "rollback"):
self.assertTrue(
any(title.startswith(required) for title in titles),
f"runbook is missing a '{required}' section; found {sorted(titles)}",
)
def test_backup_section_names_what_is_never_archived_and_why(self) -> None:
backup = next(body for title, body in sections().items() if title.startswith("backup"))
self.assertIn("scripts/operator_backup.py", backup)
for required in ("model weights", "transient inference", "secret", "tombstone"):
self.assertIn(required, backup.lower(), f"backup section must name {required}")
self.assertRegex(backup, r"manifest\.jsonl")
def test_every_documented_script_command_is_accepted_by_the_real_cli(self) -> None:
commands = documented_commands()
self.assertGreaterEqual(len(commands), 4, f"expected documented commands, found {commands}")
for command in commands:
with self.subTest(command=command):
tokens = shlex.split(command)
script = tokens[1]
rest = tokens[2:]
subcommand = rest[0] if rest and not rest[0].startswith("-") else None
combined = help_text(script, subcommand) + help_text(script, None)
for token in rest:
if token.startswith("--"):
self.assertIn(token, combined, f"{script} does not accept {token}")
for token in rest[1:]:
if not token.startswith("--"):
continue
def test_documented_backup_commands_are_the_ones_the_tool_runs(self) -> None:
commands = " ".join(documented_commands())
for flag in ("--source", "--archive", "--manifest", "--target"):
self.assertIn(flag, commands, f"the runbook must show the real {flag} flag")
self.assertIn("verify", commands)
self.assertIn("restore", commands)
def test_rollback_is_documented_as_a_tested_fixture_path_and_a_human_gated_live_drill(self) -> None:
rollback = next(body for title, body in sections().items() if title.startswith("rollback"))
self.assertIn("scripts/deploy_staging.py rollback", rollback)
self.assertRegex(rollback.lower(), r"human[- ]gate")
self.assertIn("#53", rollback)
self.assertIn("tests/staging-deploy.test.py", rollback)
# The live drill must be explicit about not being run by automation (wrapped lines are fine).
self.assertRegex(
rollback.lower(),
r"(?s)not .{0,80}automatically|does not run .{0,80}automatically",
"the runbook must say the live drill is not run automatically",
)
def test_network_boundaries_match_how_the_server_and_worker_actually_bind(self) -> None:
server = SERVER.read_text(encoding="utf-8")
# The app's own default is not loopback, so the runbook must say the deployment sets it.
self.assertRegex(server, r"HOST\|\|'0\.0\.0\.0'")
staging_env = (ROOT / "deploy" / "timmy-staging.env.example").read_text(encoding="utf-8")
self.assertIn("HOST=127.0.0.1", staging_env)
worker = (ROOT / "deploy" / "worker" / "run_worker_container.sh").read_text(encoding="utf-8")
self.assertIn("Refusing to publish the worker outside loopback", worker)
boundaries = next(body for title, body in sections().items() if title.startswith("network boundaries"))
self.assertIn("0.0.0.0", boundaries, "the runbook must name the app's real default bind address")
self.assertIn("HOST=127.0.0.1", boundaries)
self.assertIn("deploy/timmy-staging.env.example", boundaries)
self.assertIn("loopback", boundaries.lower())
self.assertIn("8080", boundaries)
def test_runbook_carries_no_literal_secret_values(self) -> None:
findings = [match.group(0) for match in SECRET_ASSIGNMENT.finditer(text())]
self.assertEqual(findings, [], f"runbook looks like it contains a real secret: {findings}")
self.assertIn("mode-600", text())
self.assertIn("never commit", text().lower())
def test_runbook_claims_match_a_stateless_server(self) -> None:
server = SERVER.read_text(encoding="utf-8")
for call in ("writeFile", "appendFile", "createWriteStream", "mkdirSync"):
self.assertNotIn(call, server, "server.mjs started persisting data; the runbook's data-lifetime claim must be updated")
body = text().lower()
self.assertIn("no server-side copy of the journal", body)
self.assertIn("/api/healthz", body)
# The health probe is allowed to report readiness, never configuration or secrets.
health = re.search(r"'/api/healthz'[^\n]*", server)
self.assertIsNotNone(health, "server.mjs must serve /api/healthz")
health_text = health.group(0) if health else ""
for forbidden in ("token", "secret", "env:", "password"):
self.assertNotIn(forbidden, health_text.lower())
# The response may report readiness only, so a new field is a deliberate decision.
for field in ("ok", "release", "commit", "visionEnabled", "agentEnabled"):
self.assertIn(field, health_text)
def test_backup_tool_is_gated_by_its_own_suite(self) -> None:
package = (ROOT / "package.json").read_text(encoding="utf-8")
self.assertIn("tests/backup-restore.test.py", package)
self.assertIn("tests/production-runbook.test.py", package)
self.assertTrue(BACKUP.is_file())
if __name__ == "__main__":
unittest.main(verbosity=2)