"""Tests for setup-fed watchdog supervision. Covers bin/ensure-node-supervision.sh (registry row + timer unit, idempotent), its hook in netvm-node-up.sh, and the registry-driven node lists in relay-health-check.sh / cdp-latency-check.sh. Shell is driven for real (LIB_ONLY sourcing where the script runs on import); UNIT_DIR/NODES_MD overrides keep everything in scratch dirs. """ import subprocess import tempfile import unittest from pathlib import Path REPO_ROOT = Path(__file__).resolve().parent.parent BIN = REPO_ROOT / "bin" def _bash(prog, extra_env=None): env = {"PATH": "/usr/bin:/bin"} env.update(extra_env or {}) return subprocess.run(["bash", "-c", prog], capture_output=True, text=True, env=env, timeout=30) class EnsureSupervision(unittest.TestCase): def setUp(self): self.tmp = tempfile.TemporaryDirectory() self.unit_dir = str(Path(self.tmp.name) / "units") Path(self.unit_dir).mkdir() self.nodes_md = str(Path(self.tmp.name) / "NODES.md") Path(self.nodes_md).write_text( "# NetVM Nodes (bl)\n\n" "| node | netns | egress_ip | cdp_port | status | agent |\n" "|------|-------|-----------|----------|--------|-------|\n" "| muse | warp-muse | 1.2.3.4 | 9410 | active | muse |\n") self.env = {"NODES_MD": self.nodes_md, "UNIT_DIR": self.unit_dir} def tearDown(self): self.tmp.cleanup() def ensure(self, *args): return _bash("'%s' %s" % ( BIN / "ensure-node-supervision.sh", " ".join("'%s'" % a for a in args)), self.env) def test_adds_registry_row_and_timer(self): r = self.ensure("pip") self.assertEqual(r.returncode, 0, r.stderr) body = Path(self.nodes_md).read_text() self.assertRegex(body, r"\| pip \| warp-pip \| \S+ \| 9420 \| active \|") unit = Path(self.unit_dir) / "chromebox-watchdog-pip.timer" self.assertTrue(unit.exists()) text = unit.read_text() self.assertIn("Unit=chromebox-watchdog@pip.service", text) self.assertIn("OnUnitActiveSec=2min", text) def test_idempotent(self): self.assertEqual(self.ensure("pip").returncode, 0) before = Path(self.nodes_md).read_text() r = self.ensure("pip") self.assertEqual(r.returncode, 0, r.stderr) self.assertEqual(Path(self.nodes_md).read_text(), before) self.assertIn("already", r.stdout) def test_honors_port_override(self): env = dict(self.env, CDP_PORT_OVERRIDE="9470") r = _bash("'%s' newnode" % (BIN / "ensure-node-supervision.sh"), env) self.assertEqual(r.returncode, 0, r.stderr) self.assertRegex(Path(self.nodes_md).read_text(), r"\| newnode \| warp-newnode \| \S+ \| 9470 \| active \|") def test_node_up_hooks_ensure(self): text = (BIN / "netvm-node-up.sh").read_text() self.assertIn("ensure-node-supervision.sh", text) class CheckScriptCoverage(unittest.TestCase): def watched(self, script, guard): prog = "source '%s'\nwatched_nodes\n" % (BIN / script) return _bash(prog, {guard: "1"}) def test_relay_health_covers_registry(self): r = self.watched("relay-health-check.sh", "RELAY_HEALTH_CHECK_LIB_ONLY") self.assertEqual(r.returncode, 0, r.stderr) nodes = set(r.stdout.split()) for n in ("muse", "pip", "646", "opm", "def", "dev"): self.assertIn(n, nodes) def test_cdp_latency_covers_registry(self): r = self.watched("cdp-latency-check.sh", "CDP_LATENCY_CHECK_LIB_ONLY") self.assertEqual(r.returncode, 0, r.stderr) nodes = set(r.stdout.split()) for n in ("muse", "pip", "646", "opm", "def", "dev"): self.assertIn(n, nodes) if __name__ == "__main__": unittest.main()