#!/usr/bin/env python3 # -*- coding: utf-8 -*- """Tests for minagent_header_gate.py (R-470/R-472) — each case writes a CHANGELOG and runs the REAL gate. Run from controller/: python3 scripts/test_minagent_header_gate.py """ import os import subprocess import sys import tempfile import unittest SCRIPTS = os.path.dirname(os.path.abspath(__file__)) GATE = os.path.join(SCRIPTS, "minagent_header_gate.py") def run(body): with tempfile.NamedTemporaryFile("w", suffix=".md", delete=False, encoding="utf-8") as f: f.write(body) path = f.name try: env = dict(os.environ, MINAGENT_GATE_CHANGELOG=path) p = subprocess.run([sys.executable, GATE], capture_output=True, text=True, env=env) return p.returncode, p.stdout + p.stderr finally: os.unlink(path) class MinAgentHeaderGateTest(unittest.TestCase): def test_header_line_passes(self): rc, out = run("## v0.239.0 — x (2026-09-13)\n\n**MinAgent: 0.129.0** (unchanged)\n\nbody\n\n## v0.238.1 — y\n") self.assertEqual(rc, 0, out) self.assertIn("v0.239.0 declares MinAgent 0.129.0", out) def test_missing_line_fails_naming_the_header(self): rc, out = run("## v0.239.0 — x (2026-09-13)\n\nbody\n\n## v0.238.1 — y\n\n**MinAgent: 0.129.0** (unchanged)\n") self.assertEqual(rc, 1, out) self.assertIn("v0.239.0", out) def test_decoy_body_mention_is_not_the_line(self): """R-421: the word in prose, a code span, or a non-bold form is the LABEL without the FACT. COMPANION RED-PROOF F (REPORT.md): relax MINAGENT_LINE_RE to a plain search for 'MinAgent'. This test then passes the decoy and fails.""" decoy = ("## v0.239.0 — x (2026-09-13)\n\n" "MinAgent is unchanged from the previous release, see `**MinAgent: 0.129.0**` there.\n" "The agent requirement (MinAgent: 0.129.0) did not move.\n\n## v0.238.1 — y\n") rc, out = run(decoy) self.assertEqual(rc, 1, out) def test_no_header_is_inconclusive(self): rc, out = run("# CHANGELOG\n\nnothing released\n") self.assertEqual(rc, 2, out) if __name__ == "__main__": unittest.main()