#!/usr/bin/env python3 """Decoys for host_swap_rule_gate (R-421). Run: python3 scripts/test_host_swap_rule_gate.py Each case builds a fake workspace (felhom.eu + a felhom-controller clone) and runs the real gate against it: 1. one row's verdict flipped on the controller side only — drift (the files share every NAME; the fact is a value); 2. identical tables, but no controller test reads the table any more — the label without the fact; 3. no controller clone — INCONCLUSIVE (2), never a pass; 4. the genuine article — must pass. """ from __future__ import annotations import os import shutil import subprocess import sys import tempfile HERE = os.path.dirname(os.path.abspath(__file__)) REPO = os.path.dirname(HERE) GATE = os.path.join(HERE, "host_swap_rule_gate.py") REAL = os.path.join(REPO, "hub", "internal", "api", "testdata", "host_swap_rule_cases.json") NAME = "host_swap_rule_cases.json" def build(tmp, ctrl_table=None, ctrl_test=True, with_ctrl=True): repo = os.path.join(tmp, "felhom.eu") hub = os.path.join(repo, "hub", "internal", "api") os.makedirs(os.path.join(hub, "testdata")) shutil.copy(REAL, os.path.join(hub, "testdata", NAME)) open(os.path.join(hub, "x_test.go"), "w").write('package api\n// os.ReadFile("testdata/%s")\n' % NAME) if with_ctrl: web = os.path.join(tmp, "felhom-controller", "controller", "internal", "web") os.makedirs(os.path.join(web, "testdata")) data = open(REAL, encoding="utf-8").read() if ctrl_table is None else ctrl_table open(os.path.join(web, "testdata", NAME), "w", encoding="utf-8").write(data) body = 'package web\n// os.ReadFile("testdata/%s")\n' % NAME if ctrl_test else "package web\n" open(os.path.join(web, "y_test.go"), "w").write(body) return repo def run(repo): env = dict(os.environ, HOST_SWAP_RULE_REPO=repo) return subprocess.run([sys.executable, GATE], env=env, capture_output=True, text=True).returncode def main(): real = open(REAL, encoding="utf-8").read() flipped = real.replace('"guest_swap_mb": 12288, "short": false', '"guest_swap_mb": 12288, "short": true', 1) assert flipped != real, "the decoy's row is gone from the real table" cases = [ ("one verdict flipped on the controller side", dict(ctrl_table=flipped), 1), ("no controller test reads the table", dict(ctrl_test=False), 1), ("no controller clone", dict(with_ctrl=False), 2), ("the genuine article", dict(), 0), ] fails = 0 for label, kw, want in cases: tmp = tempfile.mkdtemp() try: got = run(build(tmp, **kw)) finally: shutil.rmtree(tmp) ok = got == want fails += 0 if ok else 1 print(" %s %-45s rc=%d want %d" % ("ok" if ok else "FAIL", label, got, want)) print("host-swap-rule decoys: %d/%d" % (len(cases) - fails, len(cases))) return 1 if fails else 0 if __name__ == "__main__": sys.exit(main())