Files
felhom.eu/scripts/test_reuse_refs_check.py
T
admin 0314b8e7cf
gates / gates (push) Successful in 6m43s
reuse_refs_check: sibling repos beside the MAIN clone for a worktree (R-1114)
FELHOM_WORKSPACE (set by felhom-controller's runner) names where the sibling repos are; the worktree's own
main clone is skipped. Unset: unchanged. Test red-proved both ways.

Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
2026-10-17 09:39:01 +02:00

241 lines
12 KiB
Python

# -*- coding: utf-8 -*-
"""Fixture tests for scripts/reuse_refs_check.py — one per row of its resolution table, plus the
kill condition.
Run: python3 scripts/test_reuse_refs_check.py
THE ONE THAT MATTERS is test_absent_path_fails (Scenario E). The 2026-08-02 change taught the
checker to resolve suffixes and sibling repos, which turned 13 findings green in one step. A
checker made green by being made BLIND is a failure this project has shipped before, so the
ability to still fail is pinned here, and the assertion is on the EXIT CODE — the effect — not on
the summary text, which the checker can print without having decided anything.
"""
import importlib.util
import io
import os
import shutil
import sys
import tempfile
import unittest
from unittest import mock
SCRIPT = os.path.join(os.path.dirname(os.path.abspath(__file__)), "reuse_refs_check.py")
def load_checker():
"""Fresh module per test — the checker keeps a global failure count and an index cache."""
spec = importlib.util.spec_from_file_location("reuse_refs_check_under_test", SCRIPT)
mod = importlib.util.module_from_spec(spec)
spec.loader.exec_module(mod)
return mod
def write(path, text=""):
d = os.path.dirname(path)
if d and not os.path.isdir(d):
os.makedirs(d)
with io.open(path, "w", encoding="utf-8") as f:
f.write(text)
class ReuseRefsCheckTest(unittest.TestCase):
def setUp(self):
self.tmp = tempfile.mkdtemp(prefix="reuse-refs-")
self.root = os.path.join(self.tmp, "myrepo")
write(os.path.join(self.root, ".git"), "gitdir: elsewhere\n")
def tearDown(self):
shutil.rmtree(self.tmp, ignore_errors=True)
# ── harness ──────────────────────────────────────────────────────────────────
def run_check(self, *roots):
"""Returns (exit_code, stdout). Exit code is the assertion that counts."""
mod = load_checker()
buf = io.StringIO()
real = sys.stdout
sys.stdout = buf
try:
rc = mod.main(list(roots) or [self.root])
finally:
sys.stdout = real
return rc, buf.getvalue()
def reuse(self, body):
write(os.path.join(self.root, "REUSE.md"), body)
def sibling(self, name):
p = os.path.join(self.tmp, name)
write(os.path.join(p, ".git"), "gitdir: elsewhere\n")
return p
# ── row 1: exact ─────────────────────────────────────────────────────────────
def test_exact_match_is_silent_and_passes(self):
write(os.path.join(self.root, "a", "b.go"), "package a\n")
self.reuse("see `a/b.go` for the thing\n")
rc, out = self.run_check()
self.assertEqual(rc, 0, out)
self.assertIn("exact 1", out)
self.assertNotIn("note [", out) # an exact hit prints no note
# ── row 2: suffix ────────────────────────────────────────────────────────────
def test_package_shorthand_resolves_by_suffix(self):
write(os.path.join(self.root, "controller", "internal", "pkg", "x.go"), "package pkg\n")
self.reuse("see `pkg/x.go`\n")
rc, out = self.run_check()
self.assertEqual(rc, 0, out)
self.assertIn("resolved by suffix", out)
self.assertIn("controller/internal/pkg/x.go", out)
self.assertIn("suffix 1", out)
# ── row 3: ambiguous — real citation, imprecise shorthand; NOT a failure ─────
def test_two_suffix_matches_are_ambiguous_not_fatal(self):
write(os.path.join(self.root, "one", "pkg", "x.go"), "package pkg\n")
write(os.path.join(self.root, "two", "pkg", "x.go"), "package pkg\n")
self.reuse("see `pkg/x.go`\n")
rc, out = self.run_check()
self.assertEqual(rc, 0, out)
self.assertIn("AMBIGUOUS", out)
self.assertIn("one/pkg/x.go", out)
self.assertIn("two/pkg/x.go", out)
self.assertIn("ambiguous 1", out)
# ── row 4: cross-repo, by suffix in a sibling ────────────────────────────────
def test_sibling_repo_resolution(self):
sib = self.sibling("otherrepo")
write(os.path.join(sib, "hub", "internal", "wgsync", "reconciler.go"), "package wgsync\n")
self.reuse("see `wgsync/reconciler.go`\n")
rc, out = self.run_check()
self.assertEqual(rc, 0, out)
self.assertIn("cross-repo", out)
self.assertIn("otherrepo", out)
self.assertIn("cross-repo 1", out)
# ── row 4b: cross-repo where the token CARRIES the sibling's repo name ───────
def test_sibling_repo_name_prefixed_token(self):
sib = self.sibling("felhom.eu")
write(os.path.join(sib, "scripts", "site_gates.py"), "# gate\n")
self.reuse("run `felhom.eu/scripts/site_gates.py`\n")
rc, out = self.run_check()
self.assertEqual(rc, 0, out)
self.assertIn("cross-repo", out)
# ── R-1114: a worktree outside git/ searches the workspace, never its own main clone ──
def test_worktree_searches_the_workspace_not_its_own_main_clone(self):
ws = os.path.join(self.tmp, "git")
main = os.path.join(ws, "mainrepo")
os.makedirs(os.path.join(main, ".git", "worktrees", "b3"))
write(os.path.join(main, "only", "in", "main.go"), "package in\n")
shared = os.path.join(ws, "felhom.eu")
os.makedirs(os.path.join(shared, ".git"))
write(os.path.join(shared, "scripts", "x.py"), "# gate\n")
wt = os.path.join(self.tmp, "wt", "b3")
write(os.path.join(wt, ".git"), "gitdir: %s\n" % os.path.join(main, ".git", "worktrees", "b3"))
write(os.path.join(wt, "REUSE.md"), "run `felhom.eu/scripts/x.py`\n")
rc, out = self.run_check(wt)
self.assertNotEqual(rc, 0, "without FELHOM_WORKSPACE the worktree's own parent holds no felhom.eu:\n" + out)
with mock.patch.dict(os.environ, {"FELHOM_WORKSPACE": ws}):
rc, out = self.run_check(wt)
self.assertEqual(rc, 0, out)
self.assertNotIn("mainrepo", out.split("siblings searched:")[-1]) # its own main clone is skipped
# a path that exists only in the main clone (deleted in the worktree) must NOT resolve there
write(os.path.join(wt, "REUSE.md"), "see `only/in/main.go`\n")
rc, out = self.run_check(wt)
self.assertNotEqual(rc, 0, out)
def test_non_git_sibling_is_not_searched(self):
plain = os.path.join(self.tmp, "notarepo") # no .git — not a repo, must not resolve
write(os.path.join(plain, "pkg", "x.go"), "package pkg\n")
self.reuse("see `pkg/x.go`\n")
rc, out = self.run_check()
self.assertNotEqual(rc, 0, out)
# ── row 5: THE KILL CONDITION (Scenario E) ──────────────────────────────────
def test_absent_path_fails(self):
write(os.path.join(self.root, "internal", "present.go"), "package internal\n")
self.reuse("line one\nsee `internal/definitely_absent_xyz.go`\n")
rc, out = self.run_check()
self.assertNotEqual(rc, 0, "a citation that exists NOWHERE must fail:\n" + out)
self.assertIn("definitely_absent_xyz.go", out)
self.assertIn("line 2", out) # names the line
self.assertIn("FAILED 1", out)
def test_failure_lists_every_resolution_attempted(self):
"""CLAUDE.md standing rule: a 'not found' claim must name what was tried."""
self.sibling("otherrepo")
self.reuse("see `internal/definitely_absent_xyz.go`\n")
rc, out = self.run_check()
self.assertNotEqual(rc, 0, out)
self.assertIn("tried: repo-relative myrepo/internal/definitely_absent_xyz.go", out)
self.assertIn("tried: suffix search over", out)
self.assertIn("tried: sibling otherrepo", out)
# ── evidence trees are not the file ─────────────────────────────────────────
def test_evidence_copy_does_not_satisfy_a_citation(self):
write(os.path.join(self.root, "documentation", "audits", "pkg", "x.go"), "package pkg\n")
write(os.path.join(self.root, "documentation", "tests", "pkg", "y.go"), "package pkg\n")
self.reuse("see `pkg/x.go` and `pkg/y.go`\n")
rc, out = self.run_check()
self.assertNotEqual(rc, 0, "an audits/ or documentation/tests/ copy must NOT resolve:\n" + out)
self.assertIn("FAILED 2", out)
# ── R-422: document citations are checked ───────────────────────────────────
def test_absent_md_citation_fails(self):
"""The measured hole: a rotted .md citation passed because .md was not a checked extension."""
self.reuse("see `documentation/architecture/99-does-not-exist.md`\n")
rc, out = self.run_check()
self.assertNotEqual(rc, 0, "a .md citation that exists nowhere must fail:\n" + out)
self.assertIn("99-does-not-exist.md", out)
def test_md_citation_resolves_including_under_audits(self):
"""An audit DOCUMENT is the cited thing, not an evidence copy — it must resolve (the one false
positive the 2026-10-05 walk found, in app-catalog's REUSE.md)."""
write(os.path.join(self.root, "documentation", "architecture", "05-hub.md"), "# x\n")
write(os.path.join(self.root, "documentation", "audits", "SPIKE-x.md"), "# y\n")
self.reuse("see `documentation/architecture/05-hub.md` and `documentation/audits/SPIKE-x.md`\n")
rc, out = self.run_check()
self.assertEqual(rc, 0, out)
def test_audits_still_hide_code_copies_when_docs_are_indexed(self):
"""Indexing .md under audits/ must not let a .go evidence copy there resolve."""
write(os.path.join(self.root, "documentation", "audits", "pkg", "x.go"), "package pkg\n")
write(os.path.join(self.root, "documentation", "audits", "pkg", "notes.md"), "# n\n")
self.reuse("see `pkg/x.go`\n")
rc, out = self.run_check()
self.assertNotEqual(rc, 0, out)
# ── a clone in isolation must still check itself ────────────────────────────
def test_no_siblings_is_not_a_failure(self):
write(os.path.join(self.root, "a", "b.go"), "package a\n")
self.reuse("see `a/b.go`\n")
rc, out = self.run_check()
self.assertEqual(rc, 0, out)
self.assertIn("siblings searched: none", out)
def test_missing_reuse_md_fails(self):
rc, out = self.run_check()
self.assertNotEqual(rc, 0, out)
self.assertIn("no REUSE.md", out)
# ── globs stay conventions, not refs ────────────────────────────────────────
def test_glob_is_not_a_citation(self):
self.reuse("the gates are `scripts/*.py`\n")
rc, out = self.run_check()
self.assertEqual(rc, 0, out)
self.assertIn("0 cited paths", out)
# ── no args → usage, exit 2 ─────────────────────────────────────────────────
def test_no_args_is_usage_exit_2(self):
mod = load_checker()
buf, real = io.StringIO(), sys.stdout
sys.stdout = buf
try:
rc = mod.main([])
finally:
sys.stdout = real
self.assertEqual(rc, 2)
if __name__ == "__main__":
unittest.main(verbosity=2)