"""Tests for the post-rebuild-check script. A rebuilt machine looks finished and isn't: on velox 2026-08-13 five gaps surfaced within two days, three of which LOOKED fine (a stowed unit file, an enabled timer, a present git clone). The script runs the five checks from the post-rebuild task and turns each silent no-op into a visible line: 1. failed systemd units (user and system scope) 2. user unit files present but not enabled (linked-and-inert timers) 3. tracked *.example files whose real sibling is missing 4. gitignore-mode projects missing tooling paths their own .gitignore names 5. signal-cli holds no registered account Exit 0 with every check clean, 1 when any check found something. Test seams (env vars the production script honors; for each, SET-BUT-EMPTY means "the real probe ran and found nothing", UNSET means "run the real probe"): PRC_FAILED_UNITS newline list of "scope:unit" (scope user|system) PRC_UNIT_STATES newline list of "unit-file state" for the user unit dir PRC_LOCAL_SCAN_ROOTS newline-separated roots to scan for *.example orphans PRC_PROJECT_ROOTS newline-separated project dirs for the tooling check PRC_SIGNAL_ACCOUNTS signal-cli listAccounts output ("" = no accounts); the special value MISSING means the binary is absent Run from repo root: python3 -m unittest tests.post-rebuild-check.test_post_rebuild_check """ import os import subprocess import tempfile import time import unittest REPO_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..")) CHECK = os.path.join(REPO_ROOT, "scripts", "post-rebuild-check") def run_check(failed_units="", unit_states="", local_roots="", project_roots="", signal_accounts="+15045551234"): """Run the script with every probe stubbed; defaults are all-clean. Roots are newline-separated. Empty means "the seam is set and names no roots" -- the script tests with ${VAR+set}, so an empty value is still set and never falls through to the real probe. """ env = dict(os.environ) env["PRC_FAILED_UNITS"] = failed_units env["PRC_UNIT_STATES"] = unit_states env["PRC_LOCAL_SCAN_ROOTS"] = local_roots env["PRC_PROJECT_ROOTS"] = project_roots env["PRC_SIGNAL_ACCOUNTS"] = signal_accounts return subprocess.run( ["sh", CHECK], capture_output=True, text=True, timeout=30, env=env, ) class AllClean(unittest.TestCase): # --- Normal cases --------------------------------------------------- def test_all_clean_exits_zero(self): r = run_check() self.assertEqual(r.returncode, 0, r.stdout + r.stderr) def test_all_clean_prints_one_line_per_check(self): # The visible line per check is the point of the script: a silent # no-op is exactly what let the velox gaps sit unseen for two days. r = run_check() for label in ("failed units", "unit files", "local files", "project tooling", "signal"): self.assertIn(label, r.stdout.lower()) def test_all_clean_summary_says_clean(self): r = run_check() self.assertIn("all checks clean", r.stdout.lower()) def test_all_clean_no_deviation_lines(self): r = run_check() self.assertNotIn("DEVIATION", r.stdout) class FailedUnits(unittest.TestCase): # --- Normal cases --------------------------------------------------- def test_failed_user_unit_flags(self): r = run_check(failed_units="user:calendar-sync.service") self.assertEqual(r.returncode, 1) self.assertIn("calendar-sync.service", r.stdout) self.assertIn("DEVIATION", r.stdout) def test_failed_system_unit_flags(self): r = run_check(failed_units="system:tlp.service") self.assertEqual(r.returncode, 1) self.assertIn("tlp.service", r.stdout) def test_multiple_failed_units_each_reported(self): r = run_check( failed_units="user:calendar-sync.service\nsystem:tlp.service") self.assertIn("calendar-sync.service", r.stdout) self.assertIn("tlp.service", r.stdout) # --- Boundary cases ------------------------------------------------- def test_blank_lines_in_seam_ignored(self): r = run_check(failed_units="\n\nuser:a.service\n\n") self.assertEqual(r.returncode, 1) self.assertIn("a.service", r.stdout) class UnitFilesNotEnabled(unittest.TestCase): # --- Normal cases --------------------------------------------------- def test_disabled_timer_flags(self): r = run_check(unit_states="roam-sync.timer disabled") self.assertEqual(r.returncode, 1) self.assertIn("roam-sync.timer", r.stdout) def test_linked_timer_flags(self): # The exact velox case: a unit symlinked into the user dir by hand, # never enabled — present, inert, and it LOOKS installed. r = run_check(unit_states="signal-receive.timer linked") self.assertEqual(r.returncode, 1) self.assertIn("signal-receive.timer", r.stdout) def test_enabled_timer_passes(self): r = run_check(unit_states="roam-sync.timer enabled") self.assertEqual(r.returncode, 0, r.stdout) def test_static_service_passes(self): # A service with no [Install] section is pulled in by its timer; # "static" is its healthy state, not a gap. r = run_check(unit_states="roam-sync.service static") self.assertEqual(r.returncode, 0, r.stdout) def test_disabled_service_flags(self): r = run_check(unit_states="obsbot-wb-guard.service disabled") self.assertEqual(r.returncode, 1) self.assertIn("obsbot-wb-guard.service", r.stdout) # --- Boundary cases ------------------------------------------------- def test_mixed_states_only_inert_reported(self): r = run_check(unit_states="a.timer enabled\nb.timer disabled\n" "c.service static\nd.service linked") self.assertEqual(r.returncode, 1) self.assertNotIn("a.timer", r.stdout) self.assertIn("b.timer", r.stdout) self.assertNotIn("c.service", r.stdout) self.assertIn("d.service", r.stdout) def test_masked_unit_passes(self): # Masking is a deliberate act (ppd on laptops), not rebuild rot. r = run_check(unit_states="power-profiles-daemon.service masked") self.assertEqual(r.returncode, 0, r.stdout) def test_service_whose_timer_is_enabled_passes(self): # A timer-activated service is SUPPOSED to sit linked-not-enabled: # the timer owns activation, and enabling the service too would run # it at boot as well. Six of velox's units are this shape, and # flagging them is the noise that gets a check ignored. r = run_check(unit_states="roam-sync.service linked\n" "roam-sync.timer enabled") self.assertEqual(r.returncode, 0, r.stdout) def test_service_whose_timer_is_inert_flags_the_timer_only(self): # When the timer itself never got enabled, the timer is the finding. # Naming the service too would double-count one gap. r = run_check(unit_states="obs-record-watchdog.service linked\n" "obs-record-watchdog.timer linked") self.assertEqual(r.returncode, 1) self.assertIn("obs-record-watchdog.timer", r.stdout) self.assertNotIn("obs-record-watchdog.service", r.stdout) def test_service_without_a_timer_still_flags(self): # Nothing else can start it, so linked-not-enabled means dead. r = run_check(unit_states="emacs.service linked") self.assertEqual(r.returncode, 1) self.assertIn("emacs.service", r.stdout) def test_a_runtime_enabled_timer_suppresses_its_service(self): # enabled-runtime is a live activation path (enabled until reboot) # and generated means something produced and installed it, so the # service beneath either is being started and is not a finding. for state in ("enabled-runtime", "generated"): with self.subTest(timer=state): r = run_check(unit_states=f"foo.service linked\n" f"foo.timer {state}") self.assertEqual(r.returncode, 0, f"a {state} timer failed to suppress") def test_an_indirect_timer_does_not_suppress_its_service(self): # "indirect" means the unit file itself is NOT enabled -- only that # some Also= relative might be. Under this script's own fail-closed # rule the uncertain case flags, so suppressing here would be the # masked blind spot again in a narrower form. r = run_check(unit_states="foo.service linked\nfoo.timer indirect") self.assertEqual(r.returncode, 1, "an indirect timer suppressed a service nothing starts") self.assertIn("foo.service", r.stdout) def test_a_service_whose_timer_cannot_start_it_still_flags(self): # Suppression is earned by a timer that can actually run the service. # A masked, static, or absent timer starts nothing, so the service is # as dead as one with no timer at all -- and suppressing on the mere # presence of a timer line hides exactly that. for state in ("masked", "static", "not-found"): with self.subTest(timer=state): r = run_check(unit_states=f"foo.service linked\n" f"foo.timer {state}") self.assertEqual(r.returncode, 1, f"a {state} timer suppressed a dead service") self.assertIn("foo.service", r.stdout) class LocalExampleOrphans(unittest.TestCase): # --- Normal cases --------------------------------------------------- def test_example_without_sibling_flags(self): with tempfile.TemporaryDirectory() as root: open(os.path.join(root, "auth.local.el.example"), "w").close() r = run_check(local_roots=root) self.assertEqual(r.returncode, 1) self.assertIn("auth.local.el.example", r.stdout) def test_a_real_file_that_is_a_dangling_symlink_still_flags(self): # A sibling that exists only as a broken link is not a config the # machine can read, so it is the same gap as an absent one. with tempfile.TemporaryDirectory() as root: open(os.path.join(root, "auth.local.el.example"), "w").close() os.symlink("/nonexistent/stow/target", os.path.join(root, "auth.local.el")) r = run_check(local_roots=root) self.assertEqual(r.returncode, 1, "a dangling sibling counted as present") self.assertIn("auth.local.el.example", r.stdout) def test_example_with_sibling_passes(self): with tempfile.TemporaryDirectory() as root: open(os.path.join(root, "auth.local.el.example"), "w").close() open(os.path.join(root, "auth.local.el"), "w").close() r = run_check(local_roots=root) self.assertEqual(r.returncode, 0, r.stdout) def test_nested_example_found(self): with tempfile.TemporaryDirectory() as root: sub = os.path.join(root, "modules") os.makedirs(sub) open(os.path.join(sub, "mail.local.el.example"), "w").close() r = run_check(local_roots=root) self.assertEqual(r.returncode, 1) self.assertIn("mail.local.el.example", r.stdout) # --- Boundary cases ------------------------------------------------- def test_two_roots_both_scanned(self): with tempfile.TemporaryDirectory() as a, \ tempfile.TemporaryDirectory() as b: open(os.path.join(a, "one.example"), "w").close() open(os.path.join(b, "two.example"), "w").close() r = run_check(local_roots=a + "\n" + b) self.assertIn("one.example", r.stdout) self.assertIn("two.example", r.stdout) def test_vendored_package_dirs_not_scanned(self): # elpa/ and friends hold third-party packages that ship their own # .example docs. Those are the package's business, not this machine's, # and one of them (dirvish's) was the only finding check 3 produced on # velox — a standing false positive in front of any real one. with tempfile.TemporaryDirectory() as root: for vendor in ("elpa", "node_modules", ".venv", "straight"): d = os.path.join(root, vendor, "pkg-1.0", "docs") os.makedirs(d) open(os.path.join(d, "config.example"), "w").close() r = run_check(local_roots=root) self.assertEqual(r.returncode, 0, r.stdout) def test_an_unreadable_vendored_dir_is_not_a_finding(self): # The vendored trees are excluded by design, so failing to descend # into one is not a gap in what this check covers. Filtering find's # output without pruning its descent turns a package directory # nobody wanted read into a standing "could not fully scan". with tempfile.TemporaryDirectory() as root: locked = os.path.join(root, "elpa", "pkg-1.0") os.makedirs(locked) os.chmod(locked, 0o000) try: r = run_check(local_roots=root) finally: os.chmod(locked, 0o755) self.assertEqual(r.returncode, 0, r.stdout) def test_git_dir_not_scanned(self): # .git holds hooks' sample files; those are git's, not the tree's. with tempfile.TemporaryDirectory() as root: g = os.path.join(root, ".git", "hooks") os.makedirs(g) open(os.path.join(g, "pre-commit.example"), "w").close() r = run_check(local_roots=root) self.assertEqual(r.returncode, 0, r.stdout) # --- Error cases ---------------------------------------------------- def test_an_unreadable_subdirectory_is_a_finding_not_a_pass(self): # find exits non-zero when it cannot descend somewhere, and prints # what it did reach. Discarding that status hides every orphan under # the unreadable directory behind a clean "ok" -- the same defect # class as a probe that cannot run reading as a pass. with tempfile.TemporaryDirectory() as root: locked = os.path.join(root, "locked") os.makedirs(locked) open(os.path.join(locked, "auth.local.el.example"), "w").close() os.chmod(locked, 0o000) try: r = run_check(local_roots=root) finally: os.chmod(locked, 0o755) self.assertEqual(r.returncode, 1, "an unreadable directory read as nothing to check") self.assertIn("could not", r.stdout.lower()) def test_missing_root_is_its_own_finding(self): # A scan root that's gone is a rebuild gap too, not a pass. r = run_check(local_roots="/nonexistent/scan-root") self.assertEqual(r.returncode, 1) self.assertIn("/nonexistent/scan-root", r.stdout) def test_a_path_with_spaces_is_one_root_not_three(self): # Roots arrive newline-separated for this reason: splitting on spaces # turns one real directory into several imaginary missing ones. with tempfile.TemporaryDirectory() as base: root = os.path.join(base, "a dir with spaces") os.makedirs(root) open(os.path.join(root, "orphan.example"), "w").close() r = run_check(local_roots=root) self.assertEqual(r.stdout.count("DEVIATION"), 1, r.stdout) self.assertIn("orphan.example", r.stdout) class ProjectTooling(unittest.TestCase): def project(self, root, gitignore_lines, present=()): os.makedirs(os.path.join(root, ".git")) with open(os.path.join(root, ".gitignore"), "w") as f: f.write("\n".join(gitignore_lines) + "\n") for p in present: path = os.path.join(root, p) if p.endswith("/"): os.makedirs(path, exist_ok=True) else: open(path, "w").close() # --- Normal cases --------------------------------------------------- def test_ignored_but_absent_tooling_flags(self): with tempfile.TemporaryDirectory() as root: self.project(root, [".ai/", ".claude/", "todo.org"]) r = run_check(project_roots=root) self.assertEqual(r.returncode, 1) for missing in (".ai", ".claude", "todo.org"): self.assertIn(missing, r.stdout) def test_ignored_and_present_tooling_passes(self): with tempfile.TemporaryDirectory() as root: self.project(root, [".ai/", "CLAUDE.md"], present=(".ai/", "CLAUDE.md")) r = run_check(project_roots=root) self.assertEqual(r.returncode, 0, r.stdout) def test_claude_md_absence_never_flags(self): # CLAUDE.md is seed-only: install-lang writes it once and the project # owns it afterward, so most projects legitimately never have one. # Ratio shows the identical absences in the identical projects, which # is what proves it is the steady state and not reinstall drift. with tempfile.TemporaryDirectory() as root: self.project(root, [".ai/", "CLAUDE.md"], present=(".ai/",)) r = run_check(project_roots=root) self.assertEqual(r.returncode, 0, r.stdout) def test_missing_ai_dir_still_flags(self): # The one that carries real working state — 374 files in .emacs.d's # case — and that nothing restores: not git, not stow, not bootstrap. with tempfile.TemporaryDirectory() as root: self.project(root, [".ai/", "CLAUDE.md"]) r = run_check(project_roots=root) self.assertEqual(r.returncode, 1) self.assertIn(".ai", r.stdout) self.assertNotIn("CLAUDE.md", r.stdout) def test_unignored_tooling_never_expected(self): # A project that never gitignored todo.org never had one to lose; # the project's own .gitignore is the record of what it should hold. with tempfile.TemporaryDirectory() as root: self.project(root, [".ai/"], present=(".ai/",)) r = run_check(project_roots=root) self.assertEqual(r.returncode, 0, r.stdout) # --- Boundary cases ------------------------------------------------- def test_anchored_ignore_style_recognized(self): # Both /.ai/ (anchored) and .ai/ (unanchored) styles exist across # the fleet; the sweep-gitignore audit hit exactly this split. with tempfile.TemporaryDirectory() as root: self.project(root, ["/.ai/", "/CLAUDE.md"]) r = run_check(project_roots=root) self.assertEqual(r.returncode, 1) self.assertIn(".ai", r.stdout) def test_a_worktree_or_submodule_is_still_a_project(self): # In a worktree or submodule, .git is a file pointing at the real # gitdir rather than a directory, so a -d test skips the project # silently. with tempfile.TemporaryDirectory() as root: with open(os.path.join(root, ".git"), "w") as f: f.write("gitdir: /somewhere/else\n") with open(os.path.join(root, ".gitignore"), "w") as f: f.write(".ai/\n") r = run_check(project_roots=root) self.assertEqual(r.returncode, 1) self.assertIn(".ai", r.stdout) def test_an_unreadable_gitignore_is_a_finding_not_a_pass(self): # grep exits 2 on error and 1 on no-match, so treating both as # "nothing named" lets an unreadable ignore file pass the project # silently. with tempfile.TemporaryDirectory() as root: self.project(root, [".ai/"]) os.chmod(os.path.join(root, ".gitignore"), 0o000) try: r = run_check(project_roots=root) finally: os.chmod(os.path.join(root, ".gitignore"), 0o644) self.assertEqual(r.returncode, 1, "an unreadable .gitignore read as nothing to check") self.assertIn("could not", r.stdout.lower()) def test_non_git_dir_skipped(self): with tempfile.TemporaryDirectory() as root: with open(os.path.join(root, ".gitignore"), "w") as f: f.write(".ai/\n") r = run_check(project_roots=root) self.assertEqual(r.returncode, 0, r.stdout) def test_project_without_gitignore_skipped(self): with tempfile.TemporaryDirectory() as root: os.makedirs(os.path.join(root, ".git")) r = run_check(project_roots=root) self.assertEqual(r.returncode, 0, r.stdout) def test_unrelated_ignore_lines_no_flags(self): with tempfile.TemporaryDirectory() as root: self.project(root, ["*.pyc", "node_modules/", "dist/"]) r = run_check(project_roots=root) self.assertEqual(r.returncode, 0, r.stdout) class SignalAccount(unittest.TestCase): # --- Normal cases --------------------------------------------------- def test_registered_account_passes(self): r = run_check(signal_accounts="Number: +15045173983 ...") self.assertEqual(r.returncode, 0, r.stdout) def test_no_account_flags(self): # The velox case: a wiped registration silently breaks paging for # the whole fleet, because agent-text relays into this machine. r = run_check(signal_accounts="") self.assertEqual(r.returncode, 1) self.assertIn("signal", r.stdout.lower()) # --- Error cases ---------------------------------------------------- def test_missing_binary_flags(self): r = run_check(signal_accounts="MISSING") self.assertEqual(r.returncode, 1) self.assertIn("signal-cli", r.stdout) def test_a_stray_signal_missing_in_the_environment_is_ignored(self): # The script's own internal flag must not be settable from outside, # or a caller's unrelated variable turns a registered account into a # "signal-cli is not installed" finding. env = dict(os.environ) env.update({"PRC_FAILED_UNITS": "", "PRC_UNIT_STATES": "", "PRC_LOCAL_SCAN_ROOTS": "", "PRC_PROJECT_ROOTS": "", "PRC_SIGNAL_ACCOUNTS": "+15045551234", "signal_missing": "1"}) r = subprocess.run(["sh", CHECK], capture_output=True, text=True, timeout=30, env=env) self.assertEqual(r.returncode, 0, r.stdout) class ProbeFailure(unittest.TestCase): """A probe that could not run must never read as a clean check. This is the defect the whole script exists to catch, so it would be the worst possible place to have it. `systemctl --user` exits 1 with empty output when there is no user bus -- over ssh, from cron, under sudo, or on a TTY before the graphical session starts. Reading that as "no failed units" reports a machine as healthy precisely when nothing can be checked. """ def unset(self, *names): """Run with the named seams unset, so the real probes execute.""" env = dict(os.environ) env.update({"PRC_FAILED_UNITS": "", "PRC_UNIT_STATES": "", "PRC_LOCAL_SCAN_ROOTS": "", "PRC_PROJECT_ROOTS": "", "PRC_SIGNAL_ACCOUNTS": "+15045551234"}) for n in names: env.pop(n, None) env["XDG_RUNTIME_DIR"] = "/nonexistent-runtime-dir" return subprocess.run(["sh", CHECK], capture_output=True, text=True, timeout=30, env=env) # --- Error cases ---------------------------------------------------- def test_unreachable_user_bus_is_a_finding_not_a_pass(self): r = self.unset("PRC_FAILED_UNITS") self.assertEqual(r.returncode, 1, "a failed probe reported the machine as clean") self.assertIn("could not", r.stdout.lower()) def test_unreachable_user_bus_fails_the_unit_state_check_too(self): r = self.unset("PRC_UNIT_STATES") self.assertEqual(r.returncode, 1, "a failed probe reported the machine as clean") def test_an_unusable_tmpdir_is_a_finding_not_a_pass(self): # Every check stages its input through a temp file. If that write # fails, each loop reads nothing and every check comes back clean -- # with real findings passed in. env = dict(os.environ) env.update({"PRC_FAILED_UNITS": "user:calendar-sync.service", "PRC_UNIT_STATES": "", "PRC_LOCAL_SCAN_ROOTS": "", "PRC_PROJECT_ROOTS": "", "PRC_SIGNAL_ACCOUNTS": "+15045551234", "TMPDIR": "/nonexistent-tmp-dir"}) r = subprocess.run(["sh", CHECK], capture_output=True, text=True, timeout=30, env=env) self.assertEqual(r.returncode, 1, "an unwritable TMPDIR swallowed a real finding") class RealUnitDirEnumeration(unittest.TestCase): """Check 2's unseamed path, where the unit files are read off disk. The PRC_UNIT_STATES seam skips this enumeration entirely, so a defect in it survives every seamed test. That is where the dangling-stow-link case lives, and a dangling stow link is precisely the requirement's headline example of a unit file that LOOKED fine. """ def run_real(self, config_home): env = dict(os.environ) env.update({"PRC_FAILED_UNITS": "", "PRC_LOCAL_SCAN_ROOTS": "", "PRC_PROJECT_ROOTS": "", "PRC_SIGNAL_ACCOUNTS": "+15045551234", "XDG_CONFIG_HOME": config_home}) env.pop("PRC_UNIT_STATES", None) return subprocess.run(["sh", CHECK], capture_output=True, text=True, timeout=30, env=env) # --- Boundary cases ------------------------------------------------- def test_a_dangling_stow_link_is_enumerated_not_skipped(self): with tempfile.TemporaryDirectory() as home: unit_dir = os.path.join(home, "systemd", "user") os.makedirs(unit_dir) os.symlink("/nonexistent/stow/roam-sync.timer", os.path.join(unit_dir, "roam-sync.timer")) r = self.run_real(home) self.assertEqual(r.returncode, 1, "a dangling stow link read as nothing to check") self.assertIn("roam-sync.timer", r.stdout) self.assertIn("missing target", r.stdout) # --- Error cases ---------------------------------------------------- def test_a_missing_unit_directory_is_a_finding(self): with tempfile.TemporaryDirectory() as home: r = self.run_real(home) self.assertEqual(r.returncode, 1) self.assertIn("no user unit directory", r.stdout) class WedgedSystemctl(unittest.TestCase): """A systemd manager that never answers must not hang the check. Seen live on velox 2026-08-17: the user manager spun at 96% CPU with `is-enabled`, `cat`, and `list-unit-files` all hanging while `list-units` still returned. Unbounded, the check stops at the first unit and never runs checks 3 through 5, so the machine most in need of checking is the one it reports nothing about. """ def run_with_fake(self, script_body, timeout_s="1"): """Run against a fake systemctl, with check 2's unit dir empty. Pointing XDG_CONFIG_HOME at an empty directory keeps check 2 from making one call per real unit, so the test measures the bound rather than the size of this machine's unit directory. """ with tempfile.TemporaryDirectory() as d: fake = os.path.join(d, "systemctl") with open(fake, "w") as f: f.write(script_body) os.chmod(fake, 0o755) env = dict(os.environ) env.update({"PRC_LOCAL_SCAN_ROOTS": "", "PRC_PROJECT_ROOTS": "", "PRC_SIGNAL_ACCOUNTS": "+15045551234", "PRC_SYSTEMCTL": fake, "PRC_SYSTEMCTL_TIMEOUT": timeout_s, "XDG_CONFIG_HOME": d}) env.pop("PRC_FAILED_UNITS", None) env.pop("PRC_UNIT_STATES", None) start = time.monotonic() r = subprocess.run(["sh", CHECK], capture_output=True, text=True, timeout=60, env=env) return r, time.monotonic() - start # --- Error cases ---------------------------------------------------- def test_a_hanging_systemctl_is_bounded_and_reported(self): r, _ = self.run_with_fake("#!/bin/sh\nsleep 300\n") self.assertEqual(r.returncode, 1) self.assertIn("could not query user units", r.stdout) # The run must reach the end rather than stopping at the first call. self.assertIn("check 5/5", r.stdout) def test_a_hanging_systemctl_does_not_stall_the_whole_run(self): # The fake sleeps 8s against a 1s bound, so a bounded run lands near # 2s (two calls) and an unbounded one near 16s. Deliberately short # enough that losing the bound fails this assertion in seconds rather # than hitting the subprocess ceiling a minute later -- a regression # nobody waits out is a regression nobody catches. _, elapsed = self.run_with_fake("#!/bin/sh\nsleep 8\n") self.assertLess(elapsed, 6, "the run was not bounded by PRC_SYSTEMCTL_TIMEOUT") class Reporting(unittest.TestCase): # --- Normal cases --------------------------------------------------- def test_findings_counted_in_summary(self): # Assert the count in the summary line specifically. A bare # assertIn("2") passes on the always-present "check 2/5" text, so it # stays green even when the counter is arithmetically wrong. r = run_check(failed_units="user:a.service\nuser:b.service", unit_states="c.timer disabled") self.assertEqual(r.returncode, 1) summary = r.stdout.strip().splitlines()[-1] self.assertEqual(summary, "3 finding(s) across 5 checks") def test_the_summary_count_tracks_every_check(self): # One finding from each of the five, so a counter that drops or # double-counts any single check shows up here. with tempfile.TemporaryDirectory() as scan, \ tempfile.TemporaryDirectory() as proj: open(os.path.join(scan, "orphan.example"), "w").close() os.makedirs(os.path.join(proj, ".git")) with open(os.path.join(proj, ".gitignore"), "w") as f: f.write(".ai/\n") r = run_check(failed_units="user:a.service", unit_states="b.timer disabled", local_roots=scan, project_roots=proj, signal_accounts="") summary = r.stdout.strip().splitlines()[-1] self.assertEqual(summary, "5 finding(s) across 5 checks") def test_help_exits_zero(self): r = subprocess.run(["sh", CHECK, "--help"], capture_output=True, text=True, timeout=10) self.assertEqual(r.returncode, 0) self.assertIn("post-rebuild-check", r.stdout) # --- Error cases ---------------------------------------------------- def test_unknown_flag_errors(self): r = subprocess.run(["sh", CHECK, "--bogus"], capture_output=True, text=True, timeout=10) self.assertNotEqual(r.returncode, 0) if __name__ == "__main__": unittest.main()