|
| 1 | +"""Tests for local-agent registry drift checker.""" |
| 2 | + |
| 3 | +import json |
| 4 | +import pathlib |
| 5 | +import sys |
| 6 | +import tempfile |
| 7 | +import unittest |
| 8 | + |
| 9 | +_REPO_ROOT = pathlib.Path(__file__).parent.parent |
| 10 | +sys.path.insert(0, str(_REPO_ROOT)) |
| 11 | + |
| 12 | +from scripts import check_local_agent_registry_drift |
| 13 | + |
| 14 | + |
| 15 | +def write_minimal_repo(root: pathlib.Path) -> None: |
| 16 | + registry = root / "sourceosctl" / "local_agents" |
| 17 | + registry.mkdir(parents=True) |
| 18 | + (registry / "node-commander.json").write_text(json.dumps({ |
| 19 | + "name": "node-commander", |
| 20 | + "label": "org.socioprophet.node-commander", |
| 21 | + "container": { |
| 22 | + "runtimeImage": "localhost/socioprophet/node-commander:dev-boot", |
| 23 | + "sourceImage": "us-central1-docker.pkg.dev/socioprophet-web/node-commander/node-commander:dev-boot", |
| 24 | + }, |
| 25 | + "macos": {"launchd": {"plist": "~/Library/LaunchAgents/org.socioprophet.node-commander.plist"}}, |
| 26 | + })) |
| 27 | + commands = root / "sourceosctl" / "commands" |
| 28 | + commands.mkdir(parents=True) |
| 29 | + (commands / "local_agent.py").write_text("localhost/socioprophet/node-commander:dev-boot us-central1-docker.pkg.dev/socioprophet-web/node-commander/node-commander:dev-boot org.socioprophet.node-commander") |
| 30 | + (commands / "local_agent_registry_cli.py").write_text("local_agent_registry_cli") |
| 31 | + scripts = root / "scripts" |
| 32 | + scripts.mkdir() |
| 33 | + (scripts / "scan_local_persistence.py").write_text("RULES = []") |
| 34 | + bin_dir = root / "bin" |
| 35 | + bin_dir.mkdir() |
| 36 | + (bin_dir / "sourceos-agent").write_text("local_agent_registry_cli") |
| 37 | + (bin_dir / "sourceosctl").write_text("local_agent_registry_cli") |
| 38 | + |
| 39 | + |
| 40 | +class TestLocalAgentRegistryDrift(unittest.TestCase): |
| 41 | + def test_repo_drift_check_passes(self): |
| 42 | + findings = check_local_agent_registry_drift.check_registry(_REPO_ROOT) |
| 43 | + highs = [f for f in findings if f.severity == "high"] |
| 44 | + self.assertEqual(highs, []) |
| 45 | + |
| 46 | + def test_minimal_repo_passes(self): |
| 47 | + with tempfile.TemporaryDirectory() as tmp: |
| 48 | + root = pathlib.Path(tmp) |
| 49 | + write_minimal_repo(root) |
| 50 | + findings = check_local_agent_registry_drift.check_registry(root) |
| 51 | + self.assertEqual(findings, []) |
| 52 | + |
| 53 | + def test_rejects_entrypoint_not_using_adapter(self): |
| 54 | + with tempfile.TemporaryDirectory() as tmp: |
| 55 | + root = pathlib.Path(tmp) |
| 56 | + write_minimal_repo(root) |
| 57 | + (root / "bin" / "sourceos-agent").write_text("from sourceosctl.commands.local_agent import main") |
| 58 | + findings = check_local_agent_registry_drift.check_registry(root) |
| 59 | + self.assertTrue(any(f.severity == "high" and f.subject == "bin/sourceos-agent" for f in findings)) |
| 60 | + |
| 61 | + def test_rejects_system_launchagent_registry_path(self): |
| 62 | + with tempfile.TemporaryDirectory() as tmp: |
| 63 | + root = pathlib.Path(tmp) |
| 64 | + write_minimal_repo(root) |
| 65 | + reg = root / "sourceosctl" / "local_agents" / "node-commander.json" |
| 66 | + payload = json.loads(reg.read_text()) |
| 67 | + payload["macos"]["launchd"]["plist"] = "/Library/LaunchAgents/org.socioprophet.node-commander.plist" |
| 68 | + reg.write_text(json.dumps(payload)) |
| 69 | + findings = check_local_agent_registry_drift.check_registry(root) |
| 70 | + self.assertTrue(any(f.severity == "high" and "system-wide" in f.message for f in findings)) |
| 71 | + |
| 72 | + def test_main_fail_on_high(self): |
| 73 | + with tempfile.TemporaryDirectory() as tmp: |
| 74 | + root = pathlib.Path(tmp) |
| 75 | + write_minimal_repo(root) |
| 76 | + (root / "bin" / "sourceosctl").write_text("legacy route") |
| 77 | + rc = check_local_agent_registry_drift.main([str(root), "--fail-on", "high"]) |
| 78 | + self.assertEqual(rc, 1) |
| 79 | + |
| 80 | + |
| 81 | +if __name__ == "__main__": |
| 82 | + unittest.main() |
0 commit comments