feat(supervision): add choice watcher daemon, HTTPS spec docs, and test suites

- bin/muse_choice_watcher.py + systemd/muse-choices-reconcile.*: automatic choice answering and timer reconciliation
- bin/digest.py: fleet log and health summarization
- docs/BOX-*-HTTPS.md: comprehensive HTTPS execution contracts and API documentation
- docs/MUSE-CHOICES-POLICY.md & docs/SUPERVISION-SPEC.md: autonomous execution specs
- tests/test_*.py: unit test suites for HTTPS API, choice watcher, fleet heal, and swarm pruning
This commit is contained in:
operator
2026-10-07 00:25:46 +00:00
parent 04339bad14
commit 0065d11e97
29 changed files with 6419 additions and 7 deletions
+108 -2
View File
@@ -12,8 +12,10 @@ import json
import os
import subprocess
import sys
import tempfile
import unittest
from pathlib import Path
from unittest import mock
REPO_ROOT = Path("/home/super/Projects/NetVM")
BIN_DIR = REPO_ROOT / "bin"
@@ -138,8 +140,12 @@ class TestMuseChatApiConnection(unittest.TestCase):
def test_muse_chat_api_approvals_command(self):
cmd = [sys.executable, str(BIN_DIR / "muse-chat-api.py"), "--account", "pip", "approvals"]
r = subprocess.run(cmd, capture_output=True, text=True)
self.assertEqual(r.returncode, 0)
self.assertIn("No pending approvals", r.stdout)
self.assertIn(r.returncode, (0, 2))
if r.returncode == 0:
self.assertIn("No pending approvals", r.stdout)
else:
self.assertIn("APPROVAL_NEEDED", r.stdout)
class TestApprovalsReplySafety(unittest.TestCase):
@@ -242,6 +248,106 @@ class TestKeyApprovalsAndPasskey(unittest.TestCase):
self.assertEqual(deny_data.get("decision"), "deny")
class _FakeWS:
"""Scripted stand-in for a CDP websocket (no network)."""
def __init__(self, recvs):
self._recvs = list(recvs)
self.sent_ids = []
def send(self, msg):
self.sent_ids.append(json.loads(msg)["id"])
def recv(self):
if not self._recvs:
raise Exception("recv queue exhausted")
item = self._recvs.pop(0)
if callable(item):
return item(self)
return item
def close(self):
pass
def _echo_last_value(value):
def _recv(ws):
return json.dumps({"id": ws.sent_ids[-1],
"result": {"result": {"value": value}}})
return _recv
class TestInspectRobustness(unittest.TestCase):
"""Regression tests for intermittent approval failures."""
def test_cdp_request_ids_unique(self):
# Millisecond-clock ids collide for rapid successive evaluates;
# a stale buffered response can then be misattributed to the
# wrong call (e.g. verify-after-click reads the click result).
# Frozen clock makes the old collision deterministic.
with mock.patch("approvals.time.time", return_value=1728000000.123):
ws = _FakeWS([_echo_last_value("a"), _echo_last_value("b")])
self.assertEqual(approvals.cdp_evaluate(ws, "1+1"), "a")
self.assertEqual(approvals.cdp_evaluate(ws, "2+2"), "b")
self.assertNotEqual(ws.sent_ids[0], ws.sent_ids[1])
def test_cdp_skips_stale_ids(self):
stale = json.dumps({"id": 999999999,
"result": {"result": {"value": "stale"}}})
ws = _FakeWS([stale, _echo_last_value("fresh")])
self.assertEqual(approvals.cdp_evaluate(ws, "1+1"), "fresh")
def test_unreachable_returns_full_shape(self):
with mock.patch.object(approvals, "get_node_pages",
side_effect=ConnectionError("nope")), \
mock.patch.object(approvals, "check_node_key_request",
return_value=None):
res = approvals.inspect_node_approvals("pip")
self.assertEqual(res["status"], "UNREACHABLE")
self.assertFalse(res["has_pending"])
for key in ("node", "title", "buttons", "is_trusted",
"input_waits", "error"):
self.assertIn(key, res)
def test_all_pages_failed_reports_error(self):
pages = [{"title": "t", "url": "u", "type": "page",
"webSocketDebuggerUrl": "ws://127.0.0.1:9/none"}]
class _DeadWSModule:
@staticmethod
def create_connection(*a, **k):
raise ConnectionError("refused")
with mock.patch.object(approvals, "get_node_pages",
return_value=pages), \
mock.patch.object(approvals, "websocket", _DeadWSModule()), \
mock.patch.object(approvals, "check_node_key_request",
return_value=None):
res = approvals.inspect_node_approvals("pip")
# Per-page CDP failures must surface as ERROR, never as a
# false CLEAR that hides pending approvals.
self.assertEqual(res["status"], "ERROR")
self.assertFalse(res["has_pending"])
self.assertIn("error", res)
def test_state_saves_roundtrip_without_leftovers(self):
# Guards the atomic-save refactor (tmp + replace): correct
# content and no stray temp files left behind.
with tempfile.TemporaryDirectory() as td:
rp = Path(td) / "resp.json"
with mock.patch.object(approvals, "RESPONDED_WAITS_FILE", rp):
approvals.save_responded_waits({"pip": {"t": "x"}})
self.assertEqual(json.loads(rp.read_text()),
{"pip": {"t": "x"}})
fp = Path(td) / "seen.json"
with mock.patch.object(approvals, "FIRST_SEEN_WAITS_FILE", fp):
approvals.save_first_seen_waits({"pip": {"t": "x"}})
self.assertEqual(json.loads(fp.read_text()),
{"pip": {"t": "x"}})
self.assertEqual(sorted(p.name for p in Path(td).iterdir()),
["resp.json", "seen.json"])
if __name__ == "__main__":
unittest.main()
+79
View File
@@ -0,0 +1,79 @@
"""The bl-side #lobby relay must stay opt-in.
fleet-alert-check.sh invokes bin/fleet-alert-relay.sh only when
FLEET_BL_RELAY=1: the container-side hook is the live pager, and running
both double-posts every alert (2026-10-06). These tests run a copy of the
checker with stubbed-out fleet commands and assert the relay stub is (not)
invoked.
"""
import os
import shutil
import subprocess
import tempfile
import unittest
from pathlib import Path
REPO_ROOT = Path(__file__).resolve().parent.parent
CHECKER = REPO_ROOT / "bin" / "fleet-alert-check.sh"
class BlRelayGate(unittest.TestCase):
def setUp(self):
self.tmp = tempfile.TemporaryDirectory()
self.addCleanup(self.tmp.cleanup)
root = Path(self.tmp.name)
self.bindir = root / "bin"
self.bindir.mkdir()
# Copy the real checker so BIN-relative lookups hit our stubs.
shutil.copy(CHECKER, self.bindir / "fleet-alert-check.sh")
(self.bindir / "fleet-alert-relay.sh").write_text(
"#!/bin/bash\necho RELAY-RAN >> \"$CALLS\"\n")
(self.bindir / "fleet-alert-relay.sh").chmod(0o755)
(self.bindir / "netvm-registry.py").write_text(
"#!/usr/bin/env python3\n") # no nodes: all fleet loops skip
(self.bindir / "netvm-registry.py").chmod(0o755)
(self.bindir / "box-ctl.py").write_text("#!/usr/bin/env python3\n")
(self.bindir / "box-ctl.py").chmod(0o755)
fakebin = root / "fakebin"
fakebin.mkdir()
(fakebin / "sudo").write_text("#!/bin/bash\necho sudo-stub >&2\nexit 1\n")
(fakebin / "sudo").chmod(0o755)
self.state = root / "state"
self.state.mkdir()
self.calls = root / "calls.log"
self.env = dict(os.environ)
self.env["PATH"] = str(fakebin) + ":/usr/bin:/bin"
self.env["FLEET_ALERT_STATE_DIR"] = str(self.state)
self.env["CALLS"] = str(self.calls)
self.env.pop("FLEET_BL_RELAY", None)
def run_checker(self, **extra):
env = dict(self.env)
env.update(extra)
# NOTE: appends 1-2 lines to the shared /tmp/fleet-alert-check.log
# (LOG path is hardcoded); same as any production timer run.
return subprocess.run(
["bash", str(self.bindir / "fleet-alert-check.sh")],
capture_output=True, text=True, env=env, timeout=120)
def relay_ran(self):
return self.calls.exists() and "RELAY-RAN" in self.calls.read_text()
def test_relay_not_invoked_by_default(self):
r = self.run_checker()
self.assertEqual(r.returncode, 0, r.stderr[-2000:])
self.assertFalse(self.relay_ran())
def test_relay_invoked_when_opted_in(self):
r = self.run_checker(FLEET_BL_RELAY="1")
self.assertEqual(r.returncode, 0, r.stderr[-2000:])
self.assertTrue(self.relay_ran())
def test_dry_run_never_invokes_relay(self):
r = self.run_checker(FLEET_BL_RELAY="1", FLEET_ALERT_DRY_RUN="1")
self.assertEqual(r.returncode, 0, r.stderr[-2000:])
self.assertFalse(self.relay_ran())
if __name__ == "__main__":
unittest.main()
+218
View File
@@ -0,0 +1,218 @@
"""Tests for approvals over HTTPS (no SSH).
Covers the approvals expansion:
box-relay.sh (agent client) -> exec-constrained.py named ops
-> box-ctl.py backend verbs -> approvals.py (fleet CDP).
Live execution is limited to validation-failure paths (BAD_NODE/BAD_ARGS,
which fail before any CDP probe) plus quality-validate dry-runs. No live
browser traffic and no live-socket round-trips here; instead we assert the
exact argv each op builds. In particular the allow build must never carry
--always/--force: remote allow is one-shot only.
"""
import importlib.util
import json
import subprocess
import sys
import unittest
from pathlib import Path
REPO_ROOT = Path(__file__).resolve().parent.parent
BOX_CTL = REPO_ROOT / "bin" / "box-ctl.py"
RELAY = REPO_ROOT / "bin" / "box-relay.sh"
def _load(name, relpath):
spec = importlib.util.spec_from_file_location(name, REPO_ROOT / relpath)
mod = importlib.util.module_from_spec(spec)
spec.loader.exec_module(mod)
return mod
exec_constrained = _load("exec_constrained_approvals",
"bin/exec-constrained.py")
def _box_ctl(*args):
return subprocess.run(
[sys.executable, str(BOX_CTL), *args],
capture_output=True, text=True, timeout=180)
class ExecApprovalOpsTests(unittest.TestCase):
def test_ops_registered_and_side_effecting(self):
spec = exec_constrained.OPS
self.assertIn("approval.check", spec)
self.assertFalse(spec["approval.check"]["side_effecting"])
for op in ("approval.deny", "approval.auto", "approval.allow"):
self.assertIn(op, spec)
self.assertTrue(spec[op]["side_effecting"], op)
def test_known_identities_only(self):
p = exec_constrained.permitted
self.assertTrue(p("some-unknown-identity", "approval.check"))
for op in ("approval.deny", "approval.auto", "approval.allow"):
self.assertFalse(p("some-unknown-identity", op), op)
self.assertTrue(p("operator-646", op), op)
self.assertFalse(p("exec-canary", "approval.check"))
def test_check_validate(self):
v = exec_constrained.OPS["approval.check"]["validate"]
self.assertEqual(v({}), {"node": None})
self.assertEqual(v({"node": None}), {"node": None})
self.assertEqual(v({"node": "646"}), {"node": "646"})
for bad in ({"node": "nope"}, {"node": "../x"},
{"node": "646", "bogus": 1}):
with self.assertRaises(exec_constrained.OpError, msg=bad):
v(bad)
def test_deny_validate(self):
v = exec_constrained.OPS["approval.deny"]["validate"]
good = v({"node": "646", "message": "not trusted",
"allow_main_chat": True})
self.assertEqual(good, {"node": "646", "message": "not trusted",
"allow_main_chat": True})
self.assertFalse(v({"node": "646",
"message": "m"})["allow_main_chat"])
# Multiline explanations are fine; other controls are not.
v({"node": "646", "message": "line1\nline2"})
over = "x" * (exec_constrained.MAX_MESSAGE + 1)
for bad in ({"node": "646"},
{"node": "646", "message": " "},
{"node": "646", "message": over},
{"node": "646", "message": "a\x07b"},
{"node": "nope", "message": "m"},
{"node": "646", "message": "m",
"allow_main_chat": "yes"},
{"node": "646", "message": "m", "force": True}):
with self.assertRaises(exec_constrained.OpError, msg=bad):
v(bad)
def test_auto_validate(self):
v = exec_constrained.OPS["approval.auto"]["validate"]
self.assertEqual(v({}), {"node": None})
self.assertEqual(v({"node": "opm"}), {"node": "opm"})
with self.assertRaises(exec_constrained.OpError):
v({"node": "nope"})
with self.assertRaises(exec_constrained.OpError):
v({"node": "646", "always": True})
def test_allow_validate(self):
v = exec_constrained.OPS["approval.allow"]["validate"]
good = v({"node": "646", "message": "trusted deploy script"})
self.assertEqual(good["message"], "trusted deploy script")
self.assertFalse(good["allow_main_chat"])
# Attribution is mandatory: the flow notifies the waiting agent,
# so a remote allow must carry its reason.
with self.assertRaises(exec_constrained.OpError):
v({"node": "646"})
with self.assertRaises(exec_constrained.OpError):
v({"node": "646", "message": " "})
with self.assertRaises(exec_constrained.OpError):
v({"node": "nope", "message": "m"})
# No persistence/force remotely, not even as rejected keys.
with self.assertRaises(exec_constrained.OpError):
v({"node": "646", "message": "m", "always": True})
with self.assertRaises(exec_constrained.OpError):
v({"node": "646", "message": "m", "force": True})
def test_build_argv_shapes(self):
ops = exec_constrained.OPS
ck = ops["approval.check"]
self.assertEqual(ck["build"]({"node": None})[-1],
"approval-check")
self.assertEqual(ck["build"]({"node": "646"})[-2:],
["approval-check", "646"])
de = ops["approval.deny"]
argv = de["build"]({"node": "646", "message": "m",
"allow_main_chat": False})
self.assertEqual(argv[-4:],
["approval-deny", "646", "--message", "m"])
argv = de["build"]({"node": "646", "message": "m",
"allow_main_chat": True})
self.assertEqual(argv[-1], "--allow-main-chat")
au = ops["approval.auto"]
self.assertEqual(au["build"]({"node": None})[-1], "approval-auto")
self.assertEqual(au["build"]({"node": "opm"})[-2:],
["approval-auto", "opm"])
al = ops["approval.allow"]
argv = al["build"]({"node": "646", "message": "m",
"allow_main_chat": False})
self.assertEqual(argv[-4:],
["approval-allow", "646", "--message", "m"])
self.assertNotIn("--always", argv)
self.assertNotIn("--force", argv)
argv = al["build"]({"node": "646", "message": "m",
"allow_main_chat": True})
self.assertEqual(argv[-1], "--allow-main-chat")
self.assertIsInstance(argv, list)
class BoxCtlApprovalTests(unittest.TestCase):
def test_rejects_unknown_node_before_cdp(self):
for args in (["approval-check", "badnode"],
["approval-list", "badnode"],
["approval-allow", "badnode", "--message", "m"],
["approval-deny", "badnode", "--message", "m"],
["approval-auto", "badnode"]):
r = _box_ctl(*args)
self.assertNotEqual(r.returncode, 0, args)
self.assertEqual(json.loads(r.stdout)["code"], "BAD_NODE",
args)
def test_rejects_missing_node(self):
for args in (["approval-allow"], ["approval-deny"]):
r = _box_ctl(*args)
self.assertNotEqual(r.returncode, 0, args)
self.assertEqual(json.loads(r.stdout)["code"], "BAD_ARGS",
args)
def test_quality_validate_approval_verbs(self):
# Note: box-ctl leaves --message optional (SSH callers may rely on
# the flow default); the exec layer is the narrower gate and
# requires it. quality-validate mirrors box-ctl.
cases = [
(["approval-check"], True),
(["approval-check", "646"], True),
(["approval-check", "nope"], False),
(["approval-check", "646", "opm"], False),
(["approval-list", "646"], True),
(["approval-allow", "646", "--message", "hi"], True),
(["approval-allow", "646"], True),
(["approval-allow"], False),
(["approval-allow", "nope", "--message", "x"], False),
(["approval-allow", "646", "--always", "--force",
"--message", "x"], True),
(["approval-approve", "646", "--message", "x"], True),
(["approval-deny", "646", "--message", "x"], True),
(["approval-deny", "646", "--message", "x",
"--allow-main-chat"], True),
(["approval-deny"], False),
(["approval-auto"], True),
(["approval-auto", "646"], True),
(["approval-auto", "nope"], False),
(["approval-auto", "a", "b"], False),
]
for args, valid in cases:
r = _box_ctl("quality-validate", *args)
self.assertEqual(json.loads(r.stdout)["valid"], valid, args)
class BoxRelayApprovalTests(unittest.TestCase):
def test_relay_help_lists_approval_commands(self):
r = subprocess.run(["bash", str(RELAY), "help"],
capture_output=True, text=True, timeout=30)
self.assertEqual(r.returncode, 0, r.stderr)
for line in ("box approvals check", "box approvals allow",
"box approvals deny", "box approvals auto"):
self.assertIn(line, r.stdout)
def test_relay_maps_approval_commands_to_ops(self):
text = RELAY.read_text()
for op in ('"approval.check"', '"approval.deny"',
'"approval.auto"', '"approval.allow"'):
self.assertIn(op, text)
if __name__ == "__main__":
unittest.main()
+345
View File
@@ -0,0 +1,345 @@
"""Tests for no-SSH agent development and communication streams.
Covers the second HTTPS expansion:
box-relay.sh (agent client) -> exec-constrained.py named ops
-> box-ctl.py backend verbs -> git / unittest / dm.py.
Live-socket round-trips are intentionally NOT covered here (loopback TCP is
unavailable in some sandboxes); instead we assert the exact argv each op
builds and execute the fast, side-effect-free argv directly. Live sends
(notify/ack) are NEVER executed here: only their validation-failure paths.
"""
import importlib.util
import json
import os
import subprocess
import sys
import unittest
from pathlib import Path
REPO_ROOT = Path(__file__).resolve().parent.parent
BOX_CTL = REPO_ROOT / "bin" / "box-ctl.py"
RELAY = REPO_ROOT / "bin" / "box-relay.sh"
def _load(name, relpath):
spec = importlib.util.spec_from_file_location(name, REPO_ROOT / relpath)
mod = importlib.util.module_from_spec(spec)
spec.loader.exec_module(mod)
return mod
exec_constrained = _load("exec_constrained_dev", "bin/exec-constrained.py")
def _box_ctl(*args):
return subprocess.run(
[sys.executable, str(BOX_CTL), *args],
capture_output=True, text=True, timeout=120)
class ExecGitOpsTests(unittest.TestCase):
def test_ops_registered_and_read_only(self):
for op in ("git.status", "git.diff", "git.log"):
self.assertIn(op, exec_constrained.OPS)
self.assertFalse(exec_constrained.OPS[op]["side_effecting"])
def test_default_perms_include_git_ops(self):
self.assertTrue(exec_constrained.permitted("some-unknown-identity", "git.status"))
self.assertTrue(exec_constrained.permitted("some-unknown-identity", "git.diff"))
self.assertTrue(exec_constrained.permitted("some-unknown-identity", "git.log"))
self.assertFalse(exec_constrained.permitted("exec-canary", "git.status"))
def test_git_status_validate(self):
v = exec_constrained.OPS["git.status"]["validate"]
self.assertEqual(v({}), {})
with self.assertRaises(exec_constrained.OpError):
v({"bogus": 1})
def test_git_diff_validate(self):
v = exec_constrained.OPS["git.diff"]["validate"]
self.assertEqual(v({}), {"path": None, "stat": False})
self.assertEqual(v({"path": "bin/dm.py", "stat": True}),
{"path": "bin/dm.py", "stat": True})
for bad in ("../x", "/abs/path", "a\x00b", ""):
with self.assertRaises(exec_constrained.OpError, msg=bad):
v({"path": bad})
def test_git_log_validate(self):
v = exec_constrained.OPS["git.log"]["validate"]
self.assertEqual(v({}), {"limit": 10, "path": None})
self.assertEqual(v({"limit": 3})["limit"], 3)
with self.assertRaises(exec_constrained.OpError):
v({"limit": 0})
with self.assertRaises(exec_constrained.OpError):
v({"limit": 51})
with self.assertRaises(exec_constrained.OpError):
v({"path": "../x"})
def test_build_argv_shapes(self):
status = exec_constrained.OPS["git.status"]
self.assertEqual(status["build"]({})[-1], "git-status")
diff = exec_constrained.OPS["git.diff"]
self.assertEqual(diff["build"]({"path": None, "stat": False})[-1], "git-diff")
argv = diff["build"]({"path": "bin/dm.py", "stat": True})
self.assertEqual(argv[-4:], ["git-diff", "--stat", "--path", "bin/dm.py"])
log = exec_constrained.OPS["git.log"]
argv = log["build"]({"limit": 3, "path": None})
self.assertEqual(argv[-3:], ["git-log", "--limit", "3"])
self.assertIsInstance(argv, list)
def test_git_status_built_argv_executes(self):
spec = exec_constrained.OPS["git.status"]
argv = spec["build"](spec["validate"]({}))
argv[0] = sys.executable # hermetic interpreter, same script + args
r = subprocess.run(argv, capture_output=True, text=True, timeout=60)
self.assertEqual(r.returncode, 0, r.stderr)
data = json.loads(r.stdout)
self.assertTrue(data["ok"])
self.assertIn("branch", data)
self.assertIsInstance(data["changes"], list)
class ExecTestsRunTests(unittest.TestCase):
def test_registered_and_side_effecting(self):
self.assertIn("tests.run", exec_constrained.OPS)
self.assertTrue(exec_constrained.OPS["tests.run"]["side_effecting"])
def test_known_identities_only(self):
# Executes repo code: excluded from the read-only default subset.
self.assertFalse(exec_constrained.permitted("some-unknown-identity", "tests.run"))
self.assertTrue(exec_constrained.permitted("operator-646", "tests.run"))
def test_validate(self):
v = exec_constrained.OPS["tests.run"]["validate"]
self.assertEqual(v({}), {"test": None, "filter": None})
self.assertEqual(v({"test": "tests.test_box_read_https"}),
{"test": "tests.test_box_read_https",
"filter": None})
self.assertEqual(v({"filter": "safepath"})["filter"], "safepath")
for bad in ("os", "tests..x", "tests/x", "tests.test-x", ""):
with self.assertRaises(exec_constrained.OpError, msg=bad):
v({"test": bad})
for bad in ("", "x" * 201, "a\nb"):
with self.assertRaises(exec_constrained.OpError, msg=repr(bad)):
v({"filter": bad})
def test_build_argv_shape(self):
b = exec_constrained.OPS["tests.run"]["build"]
self.assertEqual(b({"test": None, "filter": None})[-1], "tests-run")
argv = b({"test": "tests.test_box_read_https", "filter": None})
self.assertEqual(argv[-2:], ["tests-run", "tests.test_box_read_https"])
argv = b({"test": None, "filter": "safepath"})
self.assertEqual(argv[-3:], ["tests-run", "--filter", "safepath"])
class ExecCommsOpsTests(unittest.TestCase):
def test_registered_and_side_effecting(self):
for op in ("notify.send", "dm.ack"):
self.assertIn(op, exec_constrained.OPS)
self.assertTrue(exec_constrained.OPS[op]["side_effecting"])
def test_known_identities_only(self):
self.assertFalse(exec_constrained.permitted("some-unknown-identity", "notify.send"))
self.assertFalse(exec_constrained.permitted("some-unknown-identity", "dm.ack"))
self.assertTrue(exec_constrained.permitted("operator-646", "notify.send"))
self.assertTrue(exec_constrained.permitted("muse", "dm.ack"))
def test_notify_send_validate(self):
v = exec_constrained.OPS["notify.send"]["validate"]
self.assertEqual(v({"agent": "pip", "message": "hi"}),
{"agent": "pip", "message": "hi",
"sidechat": None, "sender": None})
with self.assertRaises(exec_constrained.OpError):
v({"agent": "nope", "message": "hi"})
with self.assertRaises(exec_constrained.OpError):
v({"agent": "pip", "message": "x" * 1001})
with self.assertRaises(exec_constrained.OpError):
v({"agent": "pip", "message": " "})
with self.assertRaises(exec_constrained.OpError):
v({"agent": "pip", "message": "hi", "sidechat": "a" * 65})
def test_dm_ack_validate(self):
v = exec_constrained.OPS["dm.ack"]["validate"]
good = v({"id": "bdf7beb6", "to": "pip", "sender": "opm"})
self.assertEqual(good["id"], "bdf7beb6")
self.assertFalse(good["allow_main_chat"])
with self.assertRaises(exec_constrained.OpError):
v({"id": "xyz!", "to": "pip", "sender": "opm"})
with self.assertRaises(exec_constrained.OpError):
v({"id": "bdf7beb6", "to": "nope", "sender": "opm"})
with self.assertRaises(exec_constrained.OpError):
v({"id": "bdf7beb6", "to": "pip"}) # sender required
def test_build_argv_shapes(self):
n = exec_constrained.OPS["notify.send"]
argv = n["build"]({"agent": "pip", "message": "hi",
"sidechat": None, "sender": None})
self.assertEqual(argv[-3:], ["notify", "pip", "hi"])
argv = n["build"]({"agent": "pip", "message": "hi",
"sidechat": "pip tasks", "sender": "opm"})
self.assertIn("--sidechat", argv)
self.assertIn("--sender", argv)
a = exec_constrained.OPS["dm.ack"]
argv = a["build"]({"id": "bdf7beb6", "to": "pip", "sender": "opm",
"sidechat": None, "allow_main_chat": False})
self.assertEqual(argv[-6:],
["ack", "bdf7beb6", "--to", "pip", "--sender", "opm"])
class BoxCtlGitTests(unittest.TestCase):
def test_git_status_live(self):
r = _box_ctl("git-status")
self.assertEqual(r.returncode, 0, r.stderr)
data = json.loads(r.stdout)
self.assertTrue(data["ok"])
self.assertTrue(data["branch"])
self.assertIsInstance(data["changes"], list)
def test_git_log_live(self):
r = _box_ctl("git-log", "--limit", "2")
self.assertEqual(r.returncode, 0, r.stderr)
data = json.loads(r.stdout)
self.assertTrue(data["ok"])
self.assertEqual(len(data["commits"]), 2)
self.assertIn("sha", data["commits"][0])
self.assertIn("subject", data["commits"][0])
def test_git_diff_stat_live(self):
r = _box_ctl("git-diff", "--stat")
self.assertEqual(r.returncode, 0, r.stderr)
data = json.loads(r.stdout)
self.assertTrue(data["ok"])
self.assertIn("diff", data)
def test_rejects_bad_path_and_limit(self):
for bad in ("../x", "/abs/path"):
r = _box_ctl("git-diff", "--path", bad)
self.assertNotEqual(r.returncode, 0)
self.assertEqual(json.loads(r.stdout)["code"], "BAD_NAME")
r = _box_ctl("git-log", "--limit", "0")
self.assertNotEqual(r.returncode, 0)
r = _box_ctl("git-log", "--limit", "51")
self.assertNotEqual(r.returncode, 0)
def test_quality_validate_git_verbs(self):
for args in (["git-status"], ["git-diff", "--stat"],
["git-diff", "--path", "bin/dm.py"],
["git-log", "--limit", "5"]):
r = _box_ctl("quality-validate", *args)
self.assertTrue(json.loads(r.stdout)["valid"], args)
r = _box_ctl("quality-validate", "git-diff", "--path", "../x")
self.assertFalse(json.loads(r.stdout)["valid"])
class BoxCtlTestsRunTests(unittest.TestCase):
def test_tests_run_single_module_live(self):
r = _box_ctl("tests-run", "tests.test_box_read_https")
self.assertEqual(r.returncode, 0, r.stderr)
data = json.loads(r.stdout)
self.assertTrue(data["ok"], data.get("output", "")[-2000:])
self.assertEqual(data["returncode"], 0)
def test_tests_run_discovery_importable(self):
# Full discover must import every test module. An impossible -k
# filter runs zero tests in seconds while still importing all of
# them, deterministically guarding the discover argv (a `-t .`
# regresses to ImportError here). Never asserts suite success:
# outage-sensitive tests may be red independently.
r = _box_ctl("tests-run", "--filter", "zzz_no_match_zzz")
self.assertEqual(r.returncode, 0, r.stderr)
data = json.loads(r.stdout)
self.assertIn("Ran 0 tests", data.get("output", ""))
self.assertNotIn("Traceback", data.get("output", ""))
def test_tests_run_survives_safepath_invoker(self):
# `python -m` drops CWD from sys.path under PYTHONSAFEPATH/-P;
# tests-run pins PYTHONPATH so it still resolves the tests package.
env = dict(os.environ)
env["PYTHONSAFEPATH"] = "1"
r = subprocess.run(
[sys.executable, str(BOX_CTL), "tests-run",
"tests.test_box_read_https"],
capture_output=True, text=True, timeout=120, env=env)
self.assertEqual(r.returncode, 0, r.stderr)
data = json.loads(r.stdout)
self.assertTrue(data["ok"], data.get("output", "")[-2000:])
def test_rejects_bad_module(self):
r = _box_ctl("tests-run", "os")
self.assertNotEqual(r.returncode, 0)
self.assertEqual(json.loads(r.stdout)["code"], "BAD_NAME")
r = _box_ctl("tests-run", "tests.nonexistent_xyz")
self.assertNotEqual(r.returncode, 0)
self.assertEqual(json.loads(r.stdout)["code"], "NOT_FOUND")
def test_quality_validate_tests_run(self):
r = _box_ctl("quality-validate", "tests-run")
self.assertTrue(json.loads(r.stdout)["valid"], r.stdout)
r = _box_ctl("quality-validate", "tests-run", "tests.test_box_read_https")
self.assertTrue(json.loads(r.stdout)["valid"], r.stdout)
r = _box_ctl("quality-validate", "tests-run", "--filter", "safepath")
self.assertTrue(json.loads(r.stdout)["valid"], r.stdout)
r = _box_ctl("quality-validate", "tests-run", "os")
self.assertFalse(json.loads(r.stdout)["valid"], r.stdout)
r = _box_ctl("quality-validate", "tests-run", "--filter")
self.assertFalse(json.loads(r.stdout)["valid"], r.stdout)
class BoxCtlAckTests(unittest.TestCase):
# Validation-failure paths only: a live ack would send a real DM.
def test_rejects_bad_id_and_agents(self):
r = _box_ctl("ack", "xyz!", "--to", "pip", "--sender", "opm")
self.assertNotEqual(r.returncode, 0)
self.assertEqual(json.loads(r.stdout)["code"], "BAD_NAME")
r = _box_ctl("ack", "bdf7beb6", "--to", "nope", "--sender", "opm")
self.assertNotEqual(r.returncode, 0)
self.assertEqual(json.loads(r.stdout)["code"], "BAD_NAME")
def test_requires_sender(self):
r = _box_ctl("ack", "bdf7beb6", "--to", "pip")
self.assertNotEqual(r.returncode, 0)
self.assertEqual(json.loads(r.stdout)["code"], "BAD_ARGS")
def test_quality_validate_ack(self):
r = _box_ctl("quality-validate", "ack", "bdf7beb6",
"--to", "pip", "--sender", "opm")
self.assertTrue(json.loads(r.stdout)["valid"], r.stdout)
r = _box_ctl("quality-validate", "ack", "xyz!",
"--to", "pip", "--sender", "opm")
self.assertFalse(json.loads(r.stdout)["valid"], r.stdout)
class BoxCtlNotifyValidationTests(unittest.TestCase):
# Failure paths only: act_notify validates before sending.
def test_rejects_unknown_agent(self):
r = _box_ctl("notify", "nope", "hi")
self.assertNotEqual(r.returncode, 0)
self.assertEqual(json.loads(r.stdout)["code"], "BAD_NAME")
def test_rejects_oversize_message(self):
r = _box_ctl("notify", "pip", "x" * 1001)
self.assertNotEqual(r.returncode, 0)
self.assertEqual(json.loads(r.stdout)["code"], "INVALID_JOB")
class BoxRelayDevTests(unittest.TestCase):
def test_relay_help_lists_dev_commands(self):
r = subprocess.run(["bash", str(RELAY), "help"],
capture_output=True, text=True, timeout=30)
self.assertEqual(r.returncode, 0, r.stderr)
for line in ("box git status", "box git diff", "box git log",
"box tests run", "box notify", "box dm ack"):
self.assertIn(line, r.stdout)
def test_relay_maps_dev_commands_to_ops(self):
text = RELAY.read_text()
for op in ("git.status", "git.diff", "git.log",
'"tests.run"', '"notify.send"', '"dm.ack"'):
self.assertIn(op, text)
if __name__ == "__main__":
unittest.main()
+246
View File
@@ -0,0 +1,246 @@
"""Tests for job lifecycle over HTTPS (no SSH).
Covers the job-lifecycle expansion:
box-relay.sh (agent client) -> exec-constrained.py named ops
-> box-ctl.py backend verbs -> jobs/*.json / systemd / job-dispatch.
Safe mutations only: put/trigger/chain/stop/disable. Deletes are
deliberately NOT exposed. Live writes, triggers, and timer control are
NEVER executed here: only validation-failure paths (which fail before any
side effect) plus the read-only job-next dry-run. Live-socket round-trips
are intentionally NOT covered here; instead we assert the exact argv each
op builds and execute the fast, side-effect-free argv directly.
"""
import importlib.util
import json
import subprocess
import sys
import unittest
from pathlib import Path
REPO_ROOT = Path(__file__).resolve().parent.parent
BOX_CTL = REPO_ROOT / "bin" / "box-ctl.py"
RELAY = REPO_ROOT / "bin" / "box-relay.sh"
# An existing job used for existence-gated validation (read-only).
EXISTING_JOB = "heartbeat"
# Well-formed names that must not exist (validation-failure paths only).
MISSING_JOB = "definitely-no-such-job-xyz"
MISSING_ID = "definitely-no-such-job-xyz-20200101-000000-deadbeef"
def _load(name, relpath):
spec = importlib.util.spec_from_file_location(name, REPO_ROOT / relpath)
mod = importlib.util.module_from_spec(spec)
spec.loader.exec_module(mod)
return mod
exec_constrained = _load("exec_constrained_jobs", "bin/exec-constrained.py")
def _box_ctl(*args, stdin=None):
return subprocess.run(
[sys.executable, str(BOX_CTL), *args],
input=stdin, capture_output=True, text=True, timeout=120)
def _job_def(name, **over):
d = {"name": name, "description": "unit test job",
"schedule": "manual", "agent": "opm",
"prompt_template": "test prompt {job_id}", "timeout": 300}
d.update(over)
return d
class ExecJobOpsTests(unittest.TestCase):
def test_ops_registered_with_side_effect_flags(self):
spec = exec_constrained.OPS
for op in ("job.put", "job.trigger", "job.chain",
"cron.timer_stop", "cron.timer_disable"):
self.assertIn(op, spec)
self.assertTrue(spec[op]["side_effecting"])
self.assertIn("job.next", spec)
self.assertFalse(spec["job.next"]["side_effecting"])
def test_no_delete_ops_exposed(self):
names = set(exec_constrained.OPS)
self.assertNotIn("job.delete", names)
self.assertNotIn("cron.timer_delete", names)
def test_permissions(self):
p = exec_constrained.permitted
self.assertTrue(p("some-unknown-identity", "job.next"))
for op in ("job.put", "job.trigger", "job.chain",
"cron.timer_stop", "cron.timer_disable"):
self.assertFalse(p("some-unknown-identity", op))
self.assertTrue(p("operator-646", op))
self.assertFalse(p("exec-canary", "job.next"))
def test_job_put_validate(self):
v = exec_constrained.OPS["job.put"]["validate"]
good = v({"name": "my-job", "definition": _job_def("my-job")})
self.assertEqual(good["name"], "my-job")
with self.assertRaises(exec_constrained.OpError):
v({"name": "Bad_Name!", "definition": _job_def("x")})
with self.assertRaises(exec_constrained.OpError):
v({"name": "my-job", "definition": ["not", "a", "dict"]})
with self.assertRaises(exec_constrained.OpError):
v({"name": "my-job", "definition": _job_def("other")})
with self.assertRaises(exec_constrained.OpError):
v({"name": "my-job"})
def test_job_trigger_validate(self):
v = exec_constrained.OPS["job.trigger"]["validate"]
self.assertEqual(v({"name": EXISTING_JOB})["name"], EXISTING_JOB)
with self.assertRaises(exec_constrained.OpError):
v({"name": MISSING_JOB})
with self.assertRaises(exec_constrained.OpError):
v({"name": "Bad_Name!"})
def test_job_chain_validate(self):
v = exec_constrained.OPS["job.chain"]["validate"]
good = v({"from": EXISTING_JOB, "to": EXISTING_JOB})
self.assertFalse(good["on_failure"])
self.assertTrue(v({"from": EXISTING_JOB, "to": EXISTING_JOB,
"on_failure": True})["on_failure"])
with self.assertRaises(exec_constrained.OpError):
v({"from": MISSING_JOB, "to": EXISTING_JOB})
with self.assertRaises(exec_constrained.OpError):
v({"from": EXISTING_JOB})
def test_job_next_validate(self):
v = exec_constrained.OPS["job.next"]["validate"]
good = v({"job_id": MISSING_ID})
self.assertEqual(good["job_id"], MISSING_ID)
self.assertIsNone(good["success"])
self.assertTrue(v({"job_id": MISSING_ID, "success": True})["success"])
for bad in ("plainname", "a-20200101-000000-xyz!",
"UPPER-20200101-000000-deadbeef", ""):
with self.assertRaises(exec_constrained.OpError, msg=bad):
v({"job_id": bad})
def test_timer_validate(self):
for op in ("cron.timer_stop", "cron.timer_disable"):
v = exec_constrained.OPS[op]["validate"]
self.assertEqual(v({"name": EXISTING_JOB})["name"], EXISTING_JOB)
with self.assertRaises(exec_constrained.OpError):
v({"name": MISSING_JOB})
def test_build_argv_shapes(self):
put = exec_constrained.OPS["job.put"]
argv = put["build"]({"name": "my-job",
"definition": _job_def("my-job")})
self.assertEqual(argv[-2:], ["job-put", "my-job"])
trig = exec_constrained.OPS["job.trigger"]
self.assertEqual(trig["build"]({"name": EXISTING_JOB})[-2:],
["job-trigger", EXISTING_JOB])
chain = exec_constrained.OPS["job.chain"]
argv = chain["build"]({"from": "a", "to": "b", "on_failure": False})
self.assertEqual(argv[-3:], ["job-chain", "a", "b"])
argv = chain["build"]({"from": "a", "to": "b", "on_failure": True})
self.assertEqual(argv[-4:], ["job-chain", "a", "b", "--on-failure"])
nxt = exec_constrained.OPS["job.next"]
self.assertEqual(nxt["build"]({"job_id": "i", "success": None})[-2:],
["job-next", "i"])
argv = nxt["build"]({"job_id": "i", "success": False})
self.assertEqual(argv[-3:], ["job-next", "i", "--fail"])
stop = exec_constrained.OPS["cron.timer_stop"]
self.assertEqual(stop["build"]({"name": EXISTING_JOB})[-2:],
["timer-stop", EXISTING_JOB])
dis = exec_constrained.OPS["cron.timer_disable"]
self.assertEqual(dis["build"]({"name": EXISTING_JOB})[-2:],
["timer-disable", EXISTING_JOB])
self.assertIsInstance(argv, list)
def test_stdin_body_routing(self):
body = exec_constrained._stdin_body(
"job.put", {"name": "my-job", "definition": _job_def("my-job")})
# box-ctl job-put reads the raw definition (not the envelope).
self.assertEqual(json.loads(body)["name"], "my-job")
self.assertNotIn("definition", json.loads(body))
self.assertIsNone(exec_constrained._stdin_body("job.trigger", {}))
env = exec_constrained._stdin_body("files.read", {"path": "x"})
self.assertEqual(json.loads(env), {"path": "x"})
class BoxCtlJobsTests(unittest.TestCase):
def test_job_next_dry_run_live(self):
r = _box_ctl("job-next", MISSING_ID)
self.assertEqual(r.returncode, 0, r.stderr)
data = json.loads(r.stdout)
self.assertTrue(data["ok"])
self.assertEqual(data["job_id"], MISSING_ID)
self.assertFalse(data["would_dispatch"])
def test_job_put_rejects_before_write(self):
r = _box_ctl("job-put", "Bad_Name!", stdin="{}")
self.assertNotEqual(r.returncode, 0)
self.assertEqual(json.loads(r.stdout)["code"], "BAD_NAME")
r = _box_ctl("job-put", "my-job", stdin="not json")
self.assertEqual(json.loads(r.stdout)["code"], "INVALID_JOB")
r = _box_ctl("job-put", "my-job",
stdin=json.dumps(_job_def("other")))
self.assertEqual(json.loads(r.stdout)["code"], "NAME_MISMATCH")
bad = _job_def("my-job")
del bad["agent"]
r = _box_ctl("job-put", "my-job", stdin=json.dumps(bad))
self.assertEqual(json.loads(r.stdout)["code"], "INVALID_JOB")
def test_job_trigger_rejects_missing(self):
r = _box_ctl("job-trigger", "Bad_Name!")
self.assertNotEqual(r.returncode, 0)
self.assertEqual(json.loads(r.stdout)["code"], "BAD_NAME")
r = _box_ctl("job-trigger", MISSING_JOB)
self.assertEqual(json.loads(r.stdout)["code"], "NOT_FOUND")
def test_job_chain_rejects_before_write(self):
r = _box_ctl("job-chain", "Bad_Name!", EXISTING_JOB)
self.assertNotEqual(r.returncode, 0)
self.assertEqual(json.loads(r.stdout)["code"], "BAD_NAME")
r = _box_ctl("job-chain", EXISTING_JOB, EXISTING_JOB)
self.assertEqual(json.loads(r.stdout)["code"], "INVALID_JOB")
r = _box_ctl("job-chain", MISSING_JOB, EXISTING_JOB)
self.assertEqual(json.loads(r.stdout)["code"], "NOT_FOUND")
def test_timer_control_rejects_before_action(self):
for verb in ("timer-stop", "timer-disable"):
r = _box_ctl(verb, "Bad_Name!")
self.assertNotEqual(r.returncode, 0)
self.assertEqual(json.loads(r.stdout)["code"], "BAD_NAME")
r = _box_ctl(verb, MISSING_JOB)
self.assertEqual(json.loads(r.stdout)["code"], "NOT_FOUND")
def test_quality_validate_job_verbs(self):
r = _box_ctl("quality-validate", "job-put", "my-job")
self.assertTrue(json.loads(r.stdout)["valid"], r.stdout)
r = _box_ctl("quality-validate", "job-trigger", EXISTING_JOB)
self.assertTrue(json.loads(r.stdout)["valid"], r.stdout)
r = _box_ctl("quality-validate", "job-chain", "a", "b")
self.assertTrue(json.loads(r.stdout)["valid"], r.stdout)
r = _box_ctl("quality-validate", "job-next", MISSING_ID)
self.assertTrue(json.loads(r.stdout)["valid"], r.stdout)
r = _box_ctl("quality-validate", "timer-stop", EXISTING_JOB)
self.assertTrue(json.loads(r.stdout)["valid"], r.stdout)
r = _box_ctl("quality-validate", "job-put", "Bad_Name!")
self.assertFalse(json.loads(r.stdout)["valid"], r.stdout)
class BoxRelayJobsTests(unittest.TestCase):
def test_relay_help_lists_job_commands(self):
r = subprocess.run(["bash", str(RELAY), "help"],
capture_output=True, text=True, timeout=30)
self.assertEqual(r.returncode, 0, r.stderr)
for line in ("box cron put", "box cron trigger", "box cron chain",
"box cron next", "box timer stop"):
self.assertIn(line, r.stdout)
def test_relay_maps_job_commands_to_ops(self):
text = RELAY.read_text()
for op in ('"job.put"', '"job.trigger"', '"job.chain"', '"job.next"',
'"cron.timer_stop"', '"cron.timer_disable"'):
self.assertIn(op, text)
if __name__ == "__main__":
unittest.main()
+233
View File
@@ -0,0 +1,233 @@
"""Tests for loop + strategy writes over HTTPS (no SSH).
Covers the loop/strategy expansion:
box-relay.sh (agent client) -> exec-constrained.py named ops
-> box-ctl.py backend verbs -> followups/variables/strategy state.
Safe mutations only. Live execution is limited to side-effect-free paths:
loop-remediate --dry-run (all writes guarded), strat-reset on a probe key
that can never exist (returns False, no write), and validation-failure
paths (which fail before any side effect). loop-resolve always appends to
job-log, and vars-reset/rollback/strat-set mutate live fleet state, so
those success paths are covered by quality-validate (dry-run) plus unit
tests — never executed here. Live-socket round-trips are intentionally
NOT covered here; instead we assert the exact argv each op builds.
"""
import importlib.util
import json
import subprocess
import sys
import unittest
from pathlib import Path
REPO_ROOT = Path(__file__).resolve().parent.parent
BOX_CTL = REPO_ROOT / "bin" / "box-ctl.py"
RELAY = REPO_ROOT / "bin" / "box-relay.sh"
MISSING_VAR = "definitely-no-such-var-xyz"
# A strategy key no agent will ever set: reset returns False, no write.
PROBE_TYPE = "heartbeat"
PROBE_SUBTYPE = "ZZZ_PROBE_NO_SUCH_SUBTYPE"
def _load(name, relpath):
spec = importlib.util.spec_from_file_location(name, REPO_ROOT / relpath)
mod = importlib.util.module_from_spec(spec)
spec.loader.exec_module(mod)
return mod
exec_constrained = _load("exec_constrained_loop", "bin/exec-constrained.py")
def _box_ctl(*args):
return subprocess.run(
[sys.executable, str(BOX_CTL), *args],
capture_output=True, text=True, timeout=180)
class ExecLoopOpsTests(unittest.TestCase):
def test_ops_registered_and_side_effecting(self):
spec = exec_constrained.OPS
for op in ("loop.remediate", "loop.resolve", "strat.set",
"strat.reset", "vars.reset", "vars.rollback"):
self.assertIn(op, spec)
self.assertTrue(spec[op]["side_effecting"])
def test_known_identities_only(self):
p = exec_constrained.permitted
for op in ("loop.remediate", "loop.resolve", "strat.set",
"strat.reset", "vars.reset", "vars.rollback"):
self.assertFalse(p("some-unknown-identity", op))
self.assertTrue(p("operator-646", op))
self.assertFalse(p("exec-canary", "loop.remediate"))
def test_remediate_validate(self):
v = exec_constrained.OPS["loop.remediate"]["validate"]
self.assertEqual(v({}), {"dry_run": False})
self.assertTrue(v({"dry_run": True})["dry_run"])
with self.assertRaises(exec_constrained.OpError):
v({"bogus": 1})
def test_resolve_validate(self):
v = exec_constrained.OPS["loop.resolve"]["validate"]
good = v({"dm_id": "bdf7beb6", "note": "looks good"})
self.assertEqual(good["dm_id"], "bdf7beb6")
self.assertEqual(good["note"], "looks good")
self.assertIsNone(v({"dm_id": "bdf7beb6"})["note"])
with self.assertRaises(exec_constrained.OpError):
v({"dm_id": "xyz!"})
with self.assertRaises(exec_constrained.OpError):
v({"dm_id": "bdf7beb6", "note": " "})
def test_strat_set_validate(self):
v = exec_constrained.OPS["strat.set"]["validate"]
good = v({"type": "job", "priority": "important", "nudges": 3})
self.assertEqual(good["type"], "job")
self.assertEqual(good["priority"], "important")
# Typos must fail: the backend silently maps unknown types to MANUAL.
with self.assertRaises(exec_constrained.OpError, msg="typo type"):
v({"type": "wkae"})
with self.assertRaises(exec_constrained.OpError):
v({"type": "job", "priority": "urgent"})
with self.assertRaises(exec_constrained.OpError):
v({"type": "job", "timeout_s": "soon"})
with self.assertRaises(exec_constrained.OpError):
v({"type": "job", "agent": "nope"})
with self.assertRaises(exec_constrained.OpError):
v({"priority": "normal"})
def test_strat_reset_validate(self):
v = exec_constrained.OPS["strat.reset"]["validate"]
good = v({"type": "heartbeat", "subtype": "DM", "agent": "opm"})
self.assertEqual(good, {"type": "heartbeat", "subtype": "DM",
"agent": "opm"})
with self.assertRaises(exec_constrained.OpError):
v({"type": "wkae"})
with self.assertRaises(exec_constrained.OpError):
v({"type": "job", "subtype": "has space"})
def test_vars_validate(self):
vr = exec_constrained.OPS["vars.reset"]["validate"]
self.assertEqual(vr({"name": "max_nudge_count"})["name"],
"max_nudge_count")
with self.assertRaises(exec_constrained.OpError):
vr({"name": "has space"})
vb = exec_constrained.OPS["vars.rollback"]["validate"]
self.assertIsNone(vb({"name": "max_nudge_count"})["revision"])
self.assertEqual(vb({"name": "x", "revision": 2})["revision"], 2)
with self.assertRaises(exec_constrained.OpError):
vb({"name": "x", "revision": 0})
with self.assertRaises(exec_constrained.OpError):
vb({"name": "x", "revision": "a\nb"})
def test_build_argv_shapes(self):
rem = exec_constrained.OPS["loop.remediate"]
self.assertEqual(rem["build"]({"dry_run": False})[-1],
"loop-remediate")
argv = rem["build"]({"dry_run": True})
self.assertEqual(argv[-2:], ["loop-remediate", "--dry-run"])
res = exec_constrained.OPS["loop.resolve"]
argv = res["build"]({"dm_id": "abc123", "note": None})
self.assertEqual(argv[-2:], ["loop-resolve", "abc123"])
argv = res["build"]({"dm_id": "abc123", "note": "n"})
self.assertEqual(argv[-3:], ["loop-resolve", "abc123", "n"])
st = exec_constrained.OPS["strat.set"]
argv = st["build"]({"type": "job", "subtype": None, "agent": None,
"track": None, "priority": "normal",
"timeout_s": None, "nudges": 2, "escalate": None})
self.assertEqual(argv[-3], "strat-set")
payload = json.loads(argv[-1])
self.assertEqual(payload["priority"], "normal")
self.assertEqual(payload["nudges"], 2)
sr = exec_constrained.OPS["strat.reset"]
argv = sr["build"]({"type": "job", "subtype": "DM",
"agent": "opm"})
self.assertEqual(argv[-4:],
["strat-reset", "job", "DM", "--agent", "opm"][-4:])
vrt = exec_constrained.OPS["vars.reset"]
self.assertEqual(vrt["build"]({"name": "x"})[-2:],
["vars-reset", "x"])
vrb = exec_constrained.OPS["vars.rollback"]
argv = vrb["build"]({"name": "x", "revision": 2})
self.assertEqual(argv[-3:], ["vars-rollback", "x", "2"])
self.assertIsInstance(argv, list)
class BoxCtlLoopTests(unittest.TestCase):
def test_remediate_dry_run_live(self):
r = _box_ctl("loop-remediate", "--dry-run")
self.assertEqual(r.returncode, 0, r.stderr)
data = json.loads(r.stdout)
self.assertTrue(data["ok"])
self.assertTrue(data["dry_run"])
self.assertIn("remediated", data)
self.assertIn("escalated", data)
def test_strat_reset_probe_key_live(self):
r = _box_ctl("strat-reset", PROBE_TYPE, PROBE_SUBTYPE)
self.assertEqual(r.returncode, 0, r.stderr)
data = json.loads(r.stdout)
self.assertTrue(data["ok"])
self.assertFalse(data["reset"])
def test_strat_set_rejects_before_write(self):
r = _box_ctl("strat-set")
self.assertNotEqual(r.returncode, 0)
self.assertEqual(json.loads(r.stdout)["code"], "BAD_NAME")
r = _box_ctl("strat-set", "job", "not json")
self.assertEqual(json.loads(r.stdout)["code"], "STRAT_ERROR")
bad = json.dumps({"priority": "urgent"})
r = _box_ctl("strat-set", "job", bad)
self.assertEqual(json.loads(r.stdout)["code"], "STRAT_ERROR")
def test_vars_rejects_unknown_before_write(self):
r = _box_ctl("vars-reset", MISSING_VAR)
self.assertNotEqual(r.returncode, 0)
self.assertEqual(json.loads(r.stdout)["code"], "VARS_ERROR")
r = _box_ctl("vars-rollback", MISSING_VAR)
self.assertNotEqual(r.returncode, 0)
self.assertEqual(json.loads(r.stdout)["code"], "VARS_ERROR")
def test_quality_validate_loop_verbs(self):
cases = [
(["loop-remediate"], True),
(["loop-remediate", "--dry-run"], True),
(["loop-resolve", "bdf7beb6"], True),
(["loop-resolve", "bdf7beb6", "note"], True),
(["loop-resolve", "xyz!"], False),
(["strat-set", "job"], True),
(["strat-set"], False),
(["strat-reset", "job", "DM", "--agent", "opm"], True),
(["strat-reset", "--agent", "nope"], False),
(["vars-reset", "max_nudge_count"], True),
(["vars-rollback", "max_nudge_count", "2"], True),
(["vars-get", "loop_health_threshold"], True),
(["vars-set", "max_nudge_count", "5"], True),
(["vars-reset"], False),
(["vars-get", "has space"], False),
]
for args, valid in cases:
r = _box_ctl("quality-validate", *args)
self.assertEqual(json.loads(r.stdout)["valid"], valid, args)
class BoxRelayLoopTests(unittest.TestCase):
def test_relay_help_lists_loop_commands(self):
r = subprocess.run(["bash", str(RELAY), "help"],
capture_output=True, text=True, timeout=30)
self.assertEqual(r.returncode, 0, r.stderr)
for line in ("box loop remediate", "box loop resolve",
"box strat set", "box strat reset",
"box vars reset", "box vars rollback"):
self.assertIn(line, r.stdout)
def test_relay_maps_loop_commands_to_ops(self):
text = RELAY.read_text()
for op in ('"loop.remediate"', '"loop.resolve"', '"strat.set"',
'"strat.reset"', '"vars.reset"', '"vars.rollback"'):
self.assertIn(op, text)
if __name__ == "__main__":
unittest.main()
+412
View File
@@ -0,0 +1,412 @@
"""Tests for md-file reads + governed writes over HTTPS (no SSH).
Covers the md expansion:
box-relay.sh (agent client) -> exec-constrained.py named ops
-> box-ctl.py backend verbs -> agent_md.py (Hatch gateway / shared templates).
Raw container writes (md-write) are deliberately NOT exposed over HTTPS;
the governed flows are amend/append (validated shared templates with git
commit) and pull/inject-drive/sync-all (push canonical templates).
Live execution is limited to validation-failure paths (which fail before
any gateway call or write), one stdin-plumbing path that the backend
safety gate rejects before writing, quality-validate dry-runs, and
agent_md validator unit tests. No live gateway traffic, no template
writes, and no live-socket round-trips here; instead we assert the exact
argv each op builds.
"""
import hashlib
import importlib.util
import json
import subprocess
import sys
import unittest
from pathlib import Path
REPO_ROOT = Path(__file__).resolve().parent.parent
BOX_CTL = REPO_ROOT / "bin" / "box-ctl.py"
RELAY = REPO_ROOT / "bin" / "box-relay.sh"
HEARTBEAT = REPO_ROOT / "shared" / "operators" / "HEARTBEAT.md"
MD_READ_OPS = ("md.audit", "md.list", "md.read", "md.diff")
MD_WRITE_OPS = ("md.pull", "md.inject_drive", "md.sync_all",
"md.amend", "md.append")
def _load(name, relpath):
spec = importlib.util.spec_from_file_location(name, REPO_ROOT / relpath)
mod = importlib.util.module_from_spec(spec)
spec.loader.exec_module(mod)
return mod
exec_constrained = _load("exec_constrained_md", "bin/exec-constrained.py")
agent_md = _load("agent_md_mdtest", "bin/agent_md.py")
def _box_ctl(*args, stdin=None):
return subprocess.run(
[sys.executable, str(BOX_CTL), *args],
input=stdin, capture_output=True, text=True, timeout=180)
class ExecMdOpsTests(unittest.TestCase):
def test_ops_registered_and_side_effecting(self):
spec = exec_constrained.OPS
for op in MD_READ_OPS:
self.assertIn(op, spec)
self.assertFalse(spec[op]["side_effecting"], op)
for op in MD_WRITE_OPS:
self.assertIn(op, spec)
self.assertTrue(spec[op]["side_effecting"], op)
def test_raw_write_not_exposed(self):
self.assertNotIn("md.write", exec_constrained.OPS)
def test_known_identities_only(self):
p = exec_constrained.permitted
for op in MD_READ_OPS:
self.assertTrue(p("some-unknown-identity", op), op)
self.assertTrue(p("operator-646", op), op)
for op in MD_WRITE_OPS:
self.assertFalse(p("some-unknown-identity", op), op)
self.assertTrue(p("operator-646", op), op)
self.assertFalse(p("exec-canary", "md.read"))
self.assertFalse(p("exec-canary", "md.amend"))
def test_audit_validate(self):
v = exec_constrained.OPS["md.audit"]["validate"]
self.assertEqual(v({}), {"accounts": None})
# Explicit null means "all accounts", same as omitted (optional-arg
# convention shared with strat.set / loop.resolve).
self.assertEqual(v({"accounts": None}), {"accounts": None})
self.assertEqual(v({"accounts": ["646", "muse-main"]})["accounts"],
["646", "muse-main"])
for bad in ({"accounts": "646"}, {"accounts": []},
{"accounts": ["../x"]}, {"accounts": ["a b"]},
{"accounts": ["ok", ""]}, {"bogus": 1}):
with self.assertRaises(exec_constrained.OpError, msg=bad):
v(bad)
def test_list_validate(self):
v = exec_constrained.OPS["md.list"]["validate"]
self.assertEqual(v({"account": "646"}),
{"account": "646", "path": ""})
self.assertEqual(v({"account": "646", "path": "sub/dir"})["path"],
"sub/dir")
for bad in ({"account": "../x"}, {"account": "a b"},
{"account": "646", "path": ".."},
{"account": "646", "path": "/abs"},
{"account": "646", "path": "a/../../x"},
{"account": "646", "bogus": 1},
{"path": "sub"}):
with self.assertRaises(exec_constrained.OpError, msg=bad):
v(bad)
def test_read_validate(self):
v = exec_constrained.OPS["md.read"]["validate"]
good = v({"account": "646", "filename": "SOUL.md"})
self.assertEqual(good, {"account": "646", "filename": "SOUL.md"})
# Reads accept any container basename, not just shared templates.
self.assertEqual(
v({"account": "646", "filename": "NOTES.md"})["filename"],
"NOTES.md")
for bad in ({"account": "646", "filename": "../x"},
{"account": "646", "filename": "/abs"},
{"account": "646", "filename": ".."},
{"account": "646", "filename": "a/b"},
{"account": "646", "filename": ""},
{"account": "a b", "filename": "SOUL.md"},
{"account": "646", "filename": "SOUL.md", "x": 1}):
with self.assertRaises(exec_constrained.OpError, msg=bad):
v(bad)
def test_diff_validate(self):
v = exec_constrained.OPS["md.diff"]["validate"]
good = v({"account": "646", "filename": "SOUL.md"})
self.assertEqual(good, {"account": "646", "filename": "SOUL.md"})
# Template flows reject non-templates: the backend indexes
# shared/operators/ by filename.
for bad in ({"account": "646", "filename": "NOPE.md"},
{"account": "646", "filename": "../x"},
{"account": "646", "filename": "NOTES.md"}):
with self.assertRaises(exec_constrained.OpError, msg=bad):
v(bad)
def test_pull_validate(self):
v = exec_constrained.OPS["md.pull"]["validate"]
self.assertEqual(v({"account": "opm", "filename": "TOOLS.md"}),
{"account": "opm", "filename": "TOOLS.md"})
with self.assertRaises(exec_constrained.OpError):
v({"account": "opm", "filename": "NOPE.md"})
with self.assertRaises(exec_constrained.OpError):
v({"account": "../x", "filename": "TOOLS.md"})
def test_inject_drive_validate(self):
v = exec_constrained.OPS["md.inject_drive"]["validate"]
self.assertEqual(v({"account": "646"}), {"account": "646"})
with self.assertRaises(exec_constrained.OpError):
v({"account": "../x"})
with self.assertRaises(exec_constrained.OpError):
v({"account": "646", "force": True})
def test_sync_all_validate(self):
v = exec_constrained.OPS["md.sync_all"]["validate"]
self.assertEqual(v({}), {})
with self.assertRaises(exec_constrained.OpError):
v({"accounts": ["646"]})
def test_amend_validate(self):
v = exec_constrained.OPS["md.amend"]["validate"]
good = v({"filename": "SOUL.md", "content": "body",
"author": "646", "reason": "tune"})
self.assertEqual(good, {"filename": "SOUL.md", "content": "body",
"author": "646", "reason": "tune"})
defaults = v({"filename": "SOUL.md", "content": "body"})
self.assertEqual(defaults["author"], "operator")
self.assertEqual(defaults["reason"], "")
over = "x" * (exec_constrained.MD_MAX_AMEND + 1)
for bad in ({"filename": "NOPE.md", "content": "body"},
{"filename": "SOUL.md", "content": " "},
{"filename": "SOUL.md", "content": over},
{"filename": "SOUL.md", "content": "x", "author": ""},
{"filename": "SOUL.md", "content": "x",
"author": "a\nb"},
{"filename": "SOUL.md", "content": "x",
"author": "a" * 65},
{"filename": "SOUL.md", "content": "x",
"reason": "r" * 257},
{"filename": "SOUL.md", "content": "x",
"reason": "a\nb"},
{"filename": "SOUL.md", "content": "x", "bogus": 1},
{"filename": "SOUL.md"}):
with self.assertRaises(exec_constrained.OpError, msg=bad):
v(bad)
def test_append_validate(self):
v = exec_constrained.OPS["md.append"]["validate"]
good = v({"filename": "AGENTS.md", "text": "lesson",
"author": "opm", "section": "Wins"})
self.assertEqual(good["section"], "Wins")
self.assertIsNone(v({"filename": "AGENTS.md",
"text": "lesson"})["section"])
over = "x" * (exec_constrained.MD_MAX_APPEND + 1)
for bad in ({"filename": "NOPE.md", "text": "lesson"},
{"filename": "AGENTS.md", "text": " "},
{"filename": "AGENTS.md", "text": over},
{"filename": "AGENTS.md", "text": "t",
"section": " "},
{"filename": "AGENTS.md", "text": "t",
"section": "s" * 129}):
with self.assertRaises(exec_constrained.OpError, msg=bad):
v(bad)
def test_build_argv_shapes(self):
ops = exec_constrained.OPS
au = ops["md.audit"]
self.assertEqual(au["build"]({"accounts": None})[-1], "md-audit")
self.assertEqual(
au["build"]({"accounts": ["646", "opm"]})[-3:],
["md-audit", "646", "opm"])
li = ops["md.list"]
self.assertEqual(li["build"]({"account": "646", "path": ""})[-2:],
["md-list", "646"])
self.assertEqual(
li["build"]({"account": "646", "path": "sub"})[-3:],
["md-list", "646", "sub"])
rd = ops["md.read"]
self.assertEqual(
rd["build"]({"account": "646", "filename": "SOUL.md"})[-3:],
["md-read", "646", "SOUL.md"])
df = ops["md.diff"]
self.assertEqual(
df["build"]({"account": "646", "filename": "SOUL.md"})[-3:],
["md-diff", "646", "SOUL.md"])
pu = ops["md.pull"]
self.assertEqual(
pu["build"]({"account": "646", "filename": "SOUL.md"})[-3:],
["md-pull", "646", "SOUL.md"])
inj = ops["md.inject_drive"]
self.assertEqual(inj["build"]({"account": "646"})[-2:],
["md-inject-drive", "646"])
self.assertEqual(ops["md.sync_all"]["build"]({})[-1], "md-sync-all")
am = ops["md.amend"]
argv = am["build"]({"filename": "SOUL.md", "content": "x",
"author": "646", "reason": "why"})
self.assertEqual(argv[-7:],
["md-amend", "SOUL.md", "--stdin",
"--author", "646", "--reason", "why"])
argv = am["build"]({"filename": "SOUL.md", "content": "x",
"author": "646", "reason": ""})
self.assertEqual(argv[-5:],
["md-amend", "SOUL.md", "--stdin",
"--author", "646"])
ap = ops["md.append"]
argv = ap["build"]({"filename": "AGENTS.md", "text": "t",
"author": "opm", "section": "Wins"})
self.assertEqual(argv[-7:],
["md-append", "AGENTS.md", "--stdin",
"--author", "opm", "--section", "Wins"])
argv = ap["build"]({"filename": "AGENTS.md", "text": "t",
"author": "opm", "section": None})
self.assertEqual(argv[-5:],
["md-append", "AGENTS.md", "--stdin",
"--author", "opm"])
self.assertIsInstance(argv, list)
def test_stdin_body(self):
sb = exec_constrained._stdin_body
self.assertEqual(sb("md.amend", {"content": "C"}), "C")
self.assertEqual(sb("md.append", {"text": "T"}), "T")
self.assertIsNone(sb("md.read", {"account": "646"}))
class AgentMdValidationTests(unittest.TestCase):
def test_account(self):
for good in ("646", "muse", "muse-main", "opm", "dev", "def"):
self.assertEqual(agent_md.validate_account(good), good)
for bad in ("../x", "a b", "", "a/b", "x" * 33, "-lead"):
with self.assertRaises(agent_md.MDValidationError, msg=bad):
agent_md.validate_account(bad)
def test_filename(self):
for good in ("SOUL.md", "NOTES.md", "a"):
self.assertEqual(agent_md.validate_filename(good), good)
self.assertEqual(
agent_md.validate_filename("SOUL.md", template_only=True),
"SOUL.md")
for bad in ("../x", "/abs", "..", ".", "a/b", ""):
with self.assertRaises(agent_md.MDValidationError, msg=bad):
agent_md.validate_filename(bad)
for bad in ("NOPE.md", "../SOUL.md", "NOTES.md"):
with self.assertRaises(agent_md.MDValidationError, msg=bad):
agent_md.validate_filename(bad, template_only=True)
def test_subpath(self):
self.assertEqual(agent_md.validate_subpath(""), "")
self.assertEqual(agent_md.validate_subpath("a/b"), "a/b")
for bad in ("..", "/abs", "a/../../x", "a b"):
with self.assertRaises(agent_md.MDValidationError, msg=bad):
agent_md.validate_subpath(bad)
def test_rejects_before_gateway(self):
# Validation failures raise MDValidationError; a call that
# reached the gateway would raise RuntimeError (no gateway
# module here) or FileNotFoundError (no cookies) instead.
with self.assertRaises(agent_md.MDValidationError):
agent_md.read_md("646", "../x")
with self.assertRaises(agent_md.MDValidationError):
agent_md.list_files("../x", "")
with self.assertRaises(agent_md.MDValidationError):
agent_md.audit_agents(["ok", "../x"])
with self.assertRaises(agent_md.MDValidationError):
agent_md.diff_md("646", "NOPE.md")
def test_amend_rejects_before_write(self):
with self.assertRaises(agent_md.MDValidationError):
agent_md.amend_md("../x", "body")
with self.assertRaises(agent_md.MDValidationError):
agent_md.append_md("NOPE.md", "note")
class BoxCtlMdTests(unittest.TestCase):
def test_rejects_traversal_before_gateway(self):
cases = [
["md-read", "646", "../x"],
["md-read", "646", ".."],
["md-list", "bad!", "x"],
["md-list", "646", "../.."],
["md-write", "646", "/abs", "hi"],
["md-audit", "../x"],
["md", "read", "646", "../x"],
]
for args in cases:
r = _box_ctl(*args)
self.assertNotEqual(r.returncode, 0, args)
self.assertEqual(json.loads(r.stdout)["code"], "BAD_NAME", args)
def test_rejects_non_template_before_write(self):
cases = [
["md-diff", "646", "NOPE.md"],
["md-pull", "646", "NOPE.md"],
["md", "diff", "646", "NOPE.md"],
["md", "amend", "NOPE.md", "content here"],
["md", "append", "NOPE.md", "note here"],
]
for args in cases:
r = _box_ctl(*args)
self.assertNotEqual(r.returncode, 0, args)
self.assertEqual(json.loads(r.stdout)["code"], "BAD_NAME", args)
r = _box_ctl("md-amend", "../x", "--stdin", stdin="hi")
self.assertEqual(json.loads(r.stdout)["code"], "BAD_NAME")
r = _box_ctl("md-append", "../x", "--stdin", stdin="hi")
self.assertEqual(json.loads(r.stdout)["code"], "BAD_NAME")
def test_amend_stdin_safety_rejection_writes_nothing(self):
before = hashlib.sha256(HEARTBEAT.read_bytes()).hexdigest()
# Gutted HEARTBEAT content via --stdin: proves stdin plumbing
# reaches the backend, and the safety gate rejects it before
# any write or git commit.
r = _box_ctl("md-amend", "HEARTBEAT.md", "--stdin", stdin="gutted")
self.assertNotEqual(r.returncode, 0)
self.assertEqual(json.loads(r.stdout)["code"], "AMEND_FAILED")
after = hashlib.sha256(HEARTBEAT.read_bytes()).hexdigest()
self.assertEqual(before, after)
def test_quality_validate_md_verbs(self):
cases = [
(["md-audit"], True),
(["md-audit", "646", "opm"], True),
(["md-audit", "../x"], False),
(["md-list", "646"], True),
(["md-list", "646", "sub/dir"], True),
(["md-list", "646", ".."], False),
(["md-list"], False),
(["md-read", "646", "SOUL.md"], True),
(["md-read", "646", "../x"], False),
(["md-read", "646"], False),
(["md-diff", "646", "SOUL.md"], True),
(["md-diff", "646", "NOPE.md"], False),
(["md-pull", "646", "SOUL.md"], True),
(["md-pull", "646"], False),
(["md-inject-drive", "646"], True),
(["md-inject-drive", "646", "--force"], True),
(["md-inject-drive"], False),
(["md-sync-all"], True),
(["md-sync-all", "--force"], True),
(["md-sync-all", "646"], False),
(["md-amend", "SOUL.md", "--stdin"], True),
(["md-amend", "SOUL.md", "--stdin", "--author", "646"], True),
(["md-amend", "NOPE.md", "--stdin"], False),
(["md-amend"], False),
(["md-append", "SOUL.md", "--stdin"], True),
(["md-append", "SOUL.md", "note", "--section", "s"], True),
(["md-append", "x", "y", "z"], False),
(["md-write", "646", "SOUL.md", "x"], True),
(["md-write", "646", "SOUL.md"], False),
]
for args, valid in cases:
r = _box_ctl("quality-validate", *args)
self.assertEqual(json.loads(r.stdout)["valid"], valid, args)
class BoxRelayMdTests(unittest.TestCase):
def test_relay_help_lists_md_commands(self):
r = subprocess.run(["bash", str(RELAY), "help"],
capture_output=True, text=True, timeout=30)
self.assertEqual(r.returncode, 0, r.stderr)
for line in ("box md audit", "box md list", "box md read",
"box md diff", "box md pull", "box md inject-drive",
"box md sync-all", "box md amend", "box md append"):
self.assertIn(line, r.stdout)
def test_relay_maps_md_commands_to_ops(self):
text = RELAY.read_text()
for op in ('"md.audit"', '"md.list"', '"md.read"', '"md.diff"',
'"md.pull"', '"md.inject_drive"', '"md.sync_all"',
'"md.amend"', '"md.append"'):
self.assertIn(op, text)
if __name__ == "__main__":
unittest.main()
+202
View File
@@ -0,0 +1,202 @@
"""Tests for read-only box lookups over HTTPS (no SSH).
Covers the agent-facing read path:
box-relay.sh (agent client) -> exec-constrained.py named ops
-> box-ctl.py backend verbs -> super-cli.py lookups.
Live-socket round-trips are intentionally NOT covered here (loopback TCP is
unavailable in some sandboxes); instead we assert the exact argv each op
builds and execute the fast argv directly.
"""
import argparse
import importlib.util
import io
import json
import subprocess
import sys
import unittest
from contextlib import redirect_stdout
from pathlib import Path
from unittest import mock
REPO_ROOT = Path(__file__).resolve().parent.parent
BOX_CTL = REPO_ROOT / "bin" / "box-ctl.py"
RELAY = REPO_ROOT / "bin" / "box-relay.sh"
def _load(name, relpath):
spec = importlib.util.spec_from_file_location(name, REPO_ROOT / relpath)
mod = importlib.util.module_from_spec(spec)
spec.loader.exec_module(mod)
return mod
exec_constrained = _load("exec_constrained_read", "bin/exec-constrained.py")
super_cli = _load("super_cli_read", "bin/super-cli.py")
def _box_ctl(*args):
return subprocess.run(
[sys.executable, str(BOX_CTL), *args],
capture_output=True, text=True, timeout=60)
class ExecReadOpsTests(unittest.TestCase):
def test_ops_registered_and_read_only(self):
self.assertIn("fleet.unread", exec_constrained.OPS)
self.assertIn("dm.log", exec_constrained.OPS)
self.assertFalse(exec_constrained.OPS["fleet.unread"]["side_effecting"])
self.assertFalse(exec_constrained.OPS["dm.log"]["side_effecting"])
def test_default_perms_include_read_ops(self):
# Any valid fleet signer can read; canary stays ping-only.
self.assertTrue(exec_constrained.permitted("some-unknown-identity", "fleet.unread"))
self.assertTrue(exec_constrained.permitted("some-unknown-identity", "dm.log"))
self.assertFalse(exec_constrained.permitted("exec-canary", "fleet.unread"))
self.assertFalse(exec_constrained.permitted("exec-canary", "dm.log"))
def test_fleet_unread_validate(self):
v = exec_constrained.OPS["fleet.unread"]["validate"]
self.assertEqual(v({}), {"agent": None})
self.assertEqual(v({"agent": "pip"}), {"agent": "pip"})
with self.assertRaises(exec_constrained.OpError):
v({"agent": "nope"})
with self.assertRaises(exec_constrained.OpError):
v({"bogus": 1})
def test_dm_log_validate(self):
v = exec_constrained.OPS["dm.log"]["validate"]
self.assertEqual(v({}), {"limit": 20, "agent": None})
self.assertEqual(v({"limit": 5, "agent": "opm"}), {"limit": 5, "agent": "opm"})
with self.assertRaises(exec_constrained.OpError):
v({"limit": 0})
with self.assertRaises(exec_constrained.OpError):
v({"limit": 101})
with self.assertRaises(exec_constrained.OpError):
v({"agent": "nope"})
with self.assertRaises(exec_constrained.OpError):
v({"bogus": 1})
def test_build_argv_shapes(self):
unread = exec_constrained.OPS["fleet.unread"]
argv = unread["build"]({"agent": None})
self.assertEqual(argv[-1], "unread")
self.assertNotIn("--agent", argv)
argv = unread["build"]({"agent": "muse"})
self.assertEqual(argv[-3:], ["unread", "--agent", "muse"])
dmlog = exec_constrained.OPS["dm.log"]
argv = dmlog["build"]({"limit": 5, "agent": "opm"})
self.assertEqual(argv[-4:], ["dm-log", "5", "--agent", "opm"])
argv = dmlog["build"]({"limit": 20, "agent": None})
self.assertEqual(argv[-2:], ["dm-log", "20"])
# argv only, never a shell string.
self.assertIsInstance(argv, list)
def test_dm_log_built_argv_executes(self):
spec = exec_constrained.OPS["dm.log"]
clean = spec["validate"]({"limit": 2})
argv = spec["build"](clean)
argv[0] = sys.executable # hermetic interpreter, same script + args
r = subprocess.run(argv, capture_output=True, text=True, timeout=60)
self.assertEqual(r.returncode, 0, r.stderr)
data = json.loads(r.stdout)
self.assertTrue(data["ok"])
self.assertEqual(len(data["entries"]), 2)
class BoxCtlReadVerbsTests(unittest.TestCase):
def test_unread_rejects_unknown_agent(self):
r = _box_ctl("unread", "--agent", "nope")
self.assertNotEqual(r.returncode, 0)
self.assertEqual(json.loads(r.stdout)["code"], "BAD_NODE")
def test_unread_rejects_positional_and_missing_value(self):
r = _box_ctl("unread", "pip")
self.assertNotEqual(r.returncode, 0)
self.assertEqual(json.loads(r.stdout)["code"], "BAD_ARGS")
r = _box_ctl("unread", "--agent")
self.assertNotEqual(r.returncode, 0)
self.assertEqual(json.loads(r.stdout)["code"], "BAD_ARGS")
def test_dm_log_rejects_bad_limit_and_agent(self):
r = _box_ctl("dm-log", "abc")
self.assertNotEqual(r.returncode, 0)
self.assertEqual(json.loads(r.stdout)["code"], "BAD_LIMIT")
r = _box_ctl("dm-log", "5", "--agent", "nope")
self.assertNotEqual(r.returncode, 0)
self.assertEqual(json.loads(r.stdout)["code"], "BAD_NODE")
r = _box_ctl("dm-log", "1", "2")
self.assertNotEqual(r.returncode, 0)
self.assertEqual(json.loads(r.stdout)["code"], "BAD_ARGS")
def test_dm_log_back_compat_limit_only(self):
r = _box_ctl("dm-log", "2")
self.assertEqual(r.returncode, 0, r.stderr)
data = json.loads(r.stdout)
self.assertTrue(data["ok"])
self.assertEqual(len(data["entries"]), 2)
def test_quality_validate_new_verbs(self):
r = _box_ctl("quality-validate", "unread", "--agent", "pip")
data = json.loads(r.stdout)
self.assertTrue(data["valid"], r.stdout)
r = _box_ctl("quality-validate", "dm-log", "5", "--agent", "opm")
self.assertTrue(json.loads(r.stdout)["valid"], r.stdout)
r = _box_ctl("quality-validate", "unread", "--agent", "nope")
self.assertFalse(json.loads(r.stdout)["valid"], r.stdout)
r = _box_ctl("quality-validate", "unread", "extra-positional")
self.assertFalse(json.loads(r.stdout)["valid"], r.stdout)
class SuperCliUnreadTests(unittest.TestCase):
def test_lookup_dispatches_unread(self):
args = argparse.Namespace(target="unread", lookup_args=[], json=False)
with mock.patch.object(super_cli, "_lookup_unreads") as m:
super_cli.cmd_lookup(args)
m.assert_called_once_with(args)
def test_lookup_unreads_json_shape(self):
fleet = [
{"node": "muse", "title": "muse (2)", "url": "https://muse.ai/thread/abc123",
"approval_pending": False},
{"node": "pip", "title": "muse", "url": "https://muse.ai/",
"approval_pending": True},
]
args = argparse.Namespace(json=True)
buf = io.StringIO()
with mock.patch.object(super_cli, "collect_fleet_data", return_value=fleet):
with redirect_stdout(buf):
super_cli._lookup_unreads(args)
data = json.loads(buf.getvalue())
self.assertTrue(data["ok"])
by_node = {n["node"]: n for n in data["nodes"]}
self.assertEqual(by_node["muse"]["unread"], 2)
self.assertEqual(by_node["muse"]["thread"], "abc123")
self.assertFalse(by_node["muse"]["approval_pending"])
self.assertEqual(by_node["pip"]["unread"], 0)
self.assertTrue(by_node["pip"]["approval_pending"])
self.assertEqual(by_node["pip"]["thread"], "home")
class BoxRelayClientTests(unittest.TestCase):
def test_relay_syntax_valid(self):
r = subprocess.run(["bash", "-n", str(RELAY)],
capture_output=True, text=True, timeout=30)
self.assertEqual(r.returncode, 0, r.stderr)
def test_relay_help_lists_read_commands(self):
r = subprocess.run(["bash", str(RELAY), "help"],
capture_output=True, text=True, timeout=30)
self.assertEqual(r.returncode, 0, r.stderr)
self.assertIn("box unread", r.stdout)
self.assertIn("box dm log", r.stdout)
def test_relay_maps_read_commands_to_ops(self):
text = RELAY.read_text()
self.assertIn("fleet.unread", text)
self.assertIn('"dm.log"', text)
if __name__ == "__main__":
unittest.main()
+192
View File
@@ -0,0 +1,192 @@
"""Tests for `box run` and `box watch` (headless muse-code tmux runs) in super-cli.py."""
import argparse
import importlib.util
import io
import unittest
from contextlib import redirect_stdout
from pathlib import Path
from unittest import mock
REPO_ROOT = Path(__file__).resolve().parent.parent
SPEC = importlib.util.spec_from_file_location(
"super_cli_box_run", REPO_ROOT / "bin" / "super-cli.py")
super_cli = importlib.util.module_from_spec(SPEC)
SPEC.loader.exec_module(super_cli)
def _ns(**over):
kw = dict(prompt="hello from unit test", prompt_file=None, session="ut-boxrun",
log=None, model=None, effort=None, provider="echo",
permission_profile=None, approval_mode="never",
trust_workspace=False, auto_approve=False)
kw.update(over)
return argparse.Namespace(**kw)
class FakeCompleted:
def __init__(self, returncode=0, stderr=""):
self.returncode = returncode
self.stderr = stderr
class BoxRunTests(unittest.TestCase):
def setUp(self):
for f in ("/tmp/ut-boxrun.prompt", "/tmp/ut-boxrun.sh", "/tmp/ut-boxrun.exit",
"/tmp/ut-boxrun-watch.sh", "/tmp/utw-watch.sh"):
try:
Path(f).unlink()
except FileNotFoundError:
pass
self.addCleanup(self._cleanup)
def _cleanup(self):
for f in ("/tmp/ut-boxrun.prompt", "/tmp/ut-boxrun.sh", "/tmp/ut-boxrun.exit",
"/tmp/ut-boxrun-watch.sh", "/tmp/utw-watch.sh"):
try:
Path(f).unlink()
except FileNotFoundError:
pass
for log in ("ut-boxrun.log", "ut-boxrun-watch.log", "utw-watch.log"):
try:
Path(super_cli.MUSE_TMUX_LOG_DIR / log).unlink()
except FileNotFoundError:
pass
def _which(self, name):
if name == "tmux":
return "/usr/bin/tmux"
if "muse-code" in name:
return "/home/super/.local/bin/muse-code"
return None
def test_spawn_writes_prompt_wrapper_and_tmux_argv(self):
calls = []
def fake_run(argv, **kw):
calls.append(argv)
return FakeCompleted(0)
with mock.patch.object(super_cli.shutil, "which", side_effect=self._which), \
mock.patch.object(super_cli.subprocess, "run", side_effect=fake_run):
buf = io.StringIO()
with redirect_stdout(buf):
super_cli.cmd_run(_ns())
out = buf.getvalue()
self.assertIn("session: ut-boxrun", out)
self.assertIn("attach: tmux -S /tmp/tmux-muse.sock attach -t ut-boxrun", out)
self.assertEqual(Path("/tmp/ut-boxrun.prompt").read_text(encoding="utf-8"),
"hello from unit test\n")
wrapper = Path("/tmp/ut-boxrun.sh").read_text(encoding="utf-8")
self.assertIn("muse-code", wrapper)
self.assertIn("--provider echo", wrapper)
self.assertIn("--prompt-file", wrapper)
self.assertIn("cd /home/super/Projects/NetVM", wrapper)
new_session = [c for c in calls if "new-session" in c]
self.assertEqual(len(new_session), 1)
self.assertIn("/tmp/tmux-muse.sock", new_session[0])
self.assertIn("ut-boxrun", new_session[0])
def test_optional_flags_passed_through(self):
calls = []
def fake_run(argv, **kw):
calls.append(argv)
return FakeCompleted(0)
ns = _ns(model="m1", effort="low", permission_profile="prof",
trust_workspace=True, approval_mode="on-request")
with mock.patch.object(super_cli.shutil, "which", side_effect=self._which), \
mock.patch.object(super_cli.subprocess, "run", side_effect=fake_run):
with redirect_stdout(io.StringIO()):
super_cli.cmd_run(ns)
wrapper = Path("/tmp/ut-boxrun.sh").read_text(encoding="utf-8")
for flag in ("--model m1", "--reasoning-effort low", "--permission-profile prof",
"--trust-workspace", "--approval-mode on-request"):
self.assertIn(flag, wrapper)
def test_no_prompt_exits_2(self):
with mock.patch.object(super_cli.shutil, "which", side_effect=self._which), \
mock.patch.object(super_cli.sys.stdin, "isatty", return_value=True):
with self.assertRaises(SystemExit) as cm:
super_cli.cmd_run(_ns(prompt=None))
self.assertEqual(cm.exception.code, 2)
def test_missing_muse_code_exits_2(self):
with mock.patch.object(super_cli.shutil, "which", return_value=None):
with self.assertRaises(SystemExit) as cm:
super_cli.cmd_run(_ns())
self.assertEqual(cm.exception.code, 2)
def test_sanitize_session_name(self):
self.assertEqual(super_cli._sanitize_tmux_name("run:2026/10/06 05.00"), "run-2026-10-06-05-00")
self.assertEqual(super_cli._sanitize_tmux_name("!!!"), "run")
def test_run_auto_approve_spawns_watcher(self):
calls = []
def fake_run(argv, **kw):
calls.append(argv)
return FakeCompleted(0)
with mock.patch.object(super_cli.shutil, "which", side_effect=self._which), \
mock.patch.object(super_cli.subprocess, "run", side_effect=fake_run):
buf = io.StringIO()
with redirect_stdout(buf):
super_cli.cmd_run(_ns(auto_approve=True))
out = buf.getvalue()
self.assertIn("watcher: ut-boxrun-watch", out)
self.assertIn("watcher-log:", out)
new_session = [c for c in calls if "new-session" in c]
self.assertEqual(len(new_session), 2)
watch_argv = [c for c in new_session if "ut-boxrun-watch" in c]
self.assertEqual(len(watch_argv), 1)
self.assertTrue(Path("/tmp/ut-boxrun-watch.sh").exists())
def test_write_watch_script_content(self):
watch_session, script_file = super_cli._write_watch_script("utw")
self.assertEqual(watch_session, "utw-watch")
text = Path(script_file).read_text(encoding="utf-8")
self.assertIn('target="utw"', text)
self.assertIn('exit_file="/tmp/utw.exit"', text)
self.assertIn("capture-pane", text)
self.assertIn('send-keys -t "$target" "1" Enter', text)
self.assertIn("max=200", text)
self.assertIn("has-session", text)
def test_watch_missing_session_exits_2(self):
def fake_run(argv, **kw):
if "has-session" in argv:
return FakeCompleted(1)
return FakeCompleted(0)
ns = argparse.Namespace(session="nope-missing")
with mock.patch.object(super_cli.shutil, "which", side_effect=self._which), \
mock.patch.object(super_cli.subprocess, "run", side_effect=fake_run):
with self.assertRaises(SystemExit) as cm:
super_cli.cmd_watch(ns)
self.assertEqual(cm.exception.code, 2)
def test_watch_spawns_watcher_session(self):
calls = []
def fake_run(argv, **kw):
calls.append(argv)
return FakeCompleted(0)
ns = argparse.Namespace(session="utw")
with mock.patch.object(super_cli.shutil, "which", side_effect=self._which), \
mock.patch.object(super_cli.subprocess, "run", side_effect=fake_run):
buf = io.StringIO()
with redirect_stdout(buf):
super_cli.cmd_watch(ns)
out = buf.getvalue()
self.assertIn("watching: utw", out)
self.assertIn("watcher: utw-watch", out)
new_session = [c for c in calls if "new-session" in c]
self.assertEqual(len(new_session), 1)
self.assertIn("utw-watch", new_session[0])
if __name__ == "__main__":
unittest.main()
+342
View File
@@ -0,0 +1,342 @@
#!/usr/bin/env python3
"""test_box_runtime.py — Runtime state sensing + `box runtime` management.
Covers: runtime_state classification (approval-pending/working/open-prompt),
muse argv approval-posture parsing, runtime_rows assembly (mocked tmux),
and the `box runtime` CLI surface.
"""
import json
import sys
import unittest
from pathlib import Path
from unittest import mock
REPO_ROOT = Path("/home/super/Projects/NetVM")
BIN_DIR = REPO_ROOT / "bin"
sys.path.insert(0, str(BIN_DIR))
import muse_choice_watcher as w
PROMPT = "❯" # Muse TUI input glyph (U+276F)
STATE_OPEN = (
"Some completed agent output here.\n"
"\n"
"───────────────────────────────────\n"
+ PROMPT + "\n"
"───────────────────────────────────\n"
" muse-spark-1.3-con… · Auto-review\n"
)
STATE_WORKING = (
"Partial agent output...\n"
"\n"
"… — running (10s · esc to interrupt)\n"
"\n"
"───────────────────────────────────\n"
+ PROMPT + "\n"
"───────────────────────────────────\n"
" muse-spark-1.3-con… · Auto-review\n"
)
STATE_WORKING_CUT = (
"Partial agent output...\n"
"◆ Calling tools (5m 53s · esc to in\n"
"\n"
"───────────────────────────────────\n"
+ PROMPT + "\n"
)
STATE_APPROVAL = (
"───────────────────────────────────\n"
"Would you like to run the following\n"
"\n"
" $ tmux capture-pane -p\n"
"\n"
"› 1. Yes, proceed (y)\n"
" 2. No, and tell Muse Code what to do instead\n"
)
STATE_SHELL = "[super@bl NetVM]$ printf 'hi'\nhi\n[super@bl NetVM]$ "
class TestRuntimeState(unittest.TestCase):
def test_open_prompt(self):
st = w.runtime_state(STATE_OPEN)
self.assertEqual(st["state"], "open-prompt")
self.assertIsNone(st["match"])
def test_working(self):
st = w.runtime_state(STATE_WORKING)
self.assertEqual(st["state"], "working")
def test_working_edge_cut_indicator(self):
st = w.runtime_state(STATE_WORKING_CUT)
self.assertEqual(st["state"], "working")
def test_approval_pending(self):
st = w.runtime_state(STATE_APPROVAL)
self.assertEqual(st["state"], "approval-pending")
self.assertEqual(st["match"]["kind"], "muse-approval")
self.assertEqual(st["match"]["key"], "1")
def test_approval_beats_working(self):
st = w.runtime_state(STATE_WORKING + STATE_APPROVAL)
self.assertEqual(st["state"], "approval-pending")
def test_working_beats_open_prompt(self):
# A working pane still renders its prompt footer.
st = w.runtime_state(STATE_WORKING)
self.assertEqual(st["state"], "working")
def test_shell_is_unknown(self):
self.assertEqual(w.runtime_state(STATE_SHELL)["state"], "unknown")
def test_empty_is_unknown(self):
self.assertEqual(w.runtime_state("")["state"], "unknown")
self.assertEqual(w.runtime_state(None)["state"], "unknown")
class TestApprovalFlags(unittest.TestCase):
def test_bare_is_not_auto(self):
p = w.muse_approval_flags(["/home/super/.local/bin/muse-bin-1.4.3"])
self.assertFalse(p["auto_approve"])
self.assertEqual(p["flags"], [])
def test_yolo(self):
p = w.muse_approval_flags(["muse", "--yolo"])
self.assertTrue(p["auto_approve"])
self.assertIn("yolo", p["flags"])
def test_disable_approval(self):
p = w.muse_approval_flags(["muse", "--disable-approval"])
self.assertTrue(p["auto_approve"])
def test_approval_mode_never(self):
p = w.muse_approval_flags(["muse", "--approval-mode", "never"])
self.assertTrue(p["auto_approve"])
self.assertIn("approval-mode=never", p["flags"])
def test_approval_mode_equals(self):
p = w.muse_approval_flags(["muse", "--approval-mode=never"])
self.assertTrue(p["auto_approve"])
def test_approval_mode_on_request_is_not_auto(self):
p = w.muse_approval_flags(["muse", "--approval-mode", "on-request"])
self.assertFalse(p["auto_approve"])
self.assertIn("approval-mode=on-request", p["flags"])
def test_empty_argv(self):
p = w.muse_approval_flags([])
self.assertFalse(p["auto_approve"])
def _tmux_result(returncode=0, stdout="", stderr=""):
r = mock.Mock()
r.returncode = returncode
r.stdout = stdout
r.stderr = stderr
return r
class TestRuntimeRows(unittest.TestCase):
LISTING = ("muse\t1\t%37\tmuse-bin-1.4.3-R5018.1\t2880158\t71\t27\n"
"muse\t1\t%38\tbash\t2880200\t100\t30\n")
def _patched(self, tmux_stdout=LISTING, tmux_rc=0, captures=None,
children=None, cmdlines=None, watcher=None):
captures = captures or {}
cmdlines = cmdlines or {}
children = children or {}
return (mock.patch.object(w, "_tmux", return_value=_tmux_result(
tmux_rc, tmux_stdout)),
mock.patch.object(w, "capture_pane",
side_effect=lambda s, p: captures.get(p)),
mock.patch.object(w, "_child_pids",
side_effect=lambda p: children.get(p, [])),
mock.patch.object(w, "_cmdline",
side_effect=lambda p: cmdlines.get(p, [])),
mock.patch.object(w, "is_running", return_value=watcher))
def test_rows_shape(self):
patches = self._patched(
captures={"%37": STATE_OPEN, "%38": STATE_SHELL},
children={2880158: [2881158]},
cmdlines={2881158: ["/home/super/.local/bin/muse-bin-1.4.3",
"--disable-approval"]},
watcher=1234)
with patches[0], patches[1], patches[2], patches[3], patches[4]:
rows = w.runtime_rows("/tmp/sock")
self.assertEqual(len(rows), 2)
muse = rows[0]
self.assertEqual(muse["pane"], "%37")
self.assertTrue(muse["is_muse"])
self.assertTrue(muse["auto_approve"])
self.assertEqual(muse["approval_flags"], ["disable-approval"])
self.assertEqual(muse["state"], "open-prompt")
self.assertTrue(muse["watcher_alive"])
self.assertEqual(muse["watcher_pid"], 1234)
self.assertEqual(muse["width"], 71)
self.assertEqual(muse["height"], 27)
self.assertFalse(muse["squeezed"])
shell = rows[1]
self.assertFalse(shell["is_muse"])
self.assertIsNone(shell["auto_approve"])
self.assertEqual(shell["state"], "unknown")
def test_bare_muse_reports_not_auto(self):
patches = self._patched(
captures={"%37": STATE_WORKING, "%38": STATE_SHELL},
children={2880158: [2881158]},
cmdlines={2881158: ["/home/super/.local/bin/muse-bin-1.4.3"]})
with patches[0], patches[1], patches[2], patches[3], patches[4]:
rows = w.runtime_rows("/tmp/sock")
self.assertFalse(rows[0]["auto_approve"])
self.assertEqual(rows[0]["state"], "working")
self.assertFalse(rows[0]["watcher_alive"])
def test_approval_pending_row_carries_kind(self):
patches = self._patched(captures={"%37": STATE_APPROVAL,
"%38": STATE_SHELL})
with patches[0], patches[1], patches[2], patches[3], patches[4]:
rows = w.runtime_rows("/tmp/sock")
self.assertEqual(rows[0]["state"], "approval-pending")
self.assertEqual(rows[0]["prompt_kind"], "muse-approval")
self.assertEqual(rows[0]["prompt_key"], "1")
def test_tmux_failure_returns_empty(self):
patches = self._patched(tmux_rc=1, tmux_stdout="")
with patches[0], patches[1], patches[2], patches[3], patches[4]:
self.assertEqual(w.runtime_rows("/tmp/sock"), [])
def test_vanished_pane_skipped(self):
patches = self._patched(captures={"%37": None, "%38": STATE_SHELL})
with patches[0], patches[1], patches[2], patches[3], patches[4]:
rows = w.runtime_rows("/tmp/sock")
self.assertEqual([r["pane"] for r in rows], ["%38"])
def test_pane_state_found_and_missing(self):
patches = self._patched(captures={"%37": STATE_OPEN,
"%38": STATE_SHELL})
with patches[0], patches[1], patches[2], patches[3], patches[4]:
hit = w.pane_state("/tmp/sock", "%37")
miss = w.pane_state("/tmp/sock", "%99")
self.assertEqual(hit["pane"], "%37")
self.assertEqual(miss["error"], "no_such_pane")
def test_rows_flag_squeezed(self):
listing = ("muse\t1\t%37\tmuse-bin-1.4\t2880158\t35\t7\n"
"muse\t1\t%38\tbash\t2880200\t35\t7\n")
patches = self._patched(
tmux_stdout=listing,
captures={"%37": STATE_OPEN, "%38": STATE_SHELL})
with patches[0], patches[1], patches[2], patches[3], patches[4]:
rows = w.runtime_rows("/tmp/sock")
self.assertTrue(rows[0]["squeezed"])
self.assertEqual(rows[0]["width"], 35)
self.assertEqual(rows[0]["height"], 7)
self.assertTrue(rows[1]["squeezed"])
class TestSpreadTargets(unittest.TestCase):
def _row(self, pane, is_muse, squeezed):
return {"socket": "/tmp/s", "session": "muse", "window": "1",
"pane": pane, "is_muse": is_muse, "squeezed": squeezed}
def test_selects_squeezed_muse_only(self):
rows = [self._row("%22", True, False),
self._row("%23", True, True),
self._row("%38", False, True)]
targets = w.spread_targets(rows)
self.assertEqual([t["pane"] for t in targets], ["%23"])
def test_empty_when_nothing_squeezed(self):
rows = [self._row("%22", True, False)]
self.assertEqual(w.spread_targets(rows), [])
def test_tolerates_missing_keys(self):
self.assertEqual(w.spread_targets([{"pane": "%1"}]), [])
class TestBoxRuntimeCLI(unittest.TestCase):
def _box(self, *argv, timeout=60):
import subprocess
cmd = [sys.executable, str(BIN_DIR / "super-cli.py"),
"runtime"] + list(argv)
return subprocess.run(cmd, capture_output=True, text=True,
timeout=timeout)
def test_list_empty_socket_json(self):
r = self._box("list", "--socket", "/nonexistent.sock", "--json")
self.assertEqual(r.returncode, 0, r.stderr[:500])
data = json.loads(r.stdout)
self.assertTrue(data["ok"])
self.assertEqual(data["runtimes"], [])
def test_list_muse_only_flag_accepted(self):
r = self._box("list", "--socket", "/nonexistent.sock", "--json",
"--muse-only")
self.assertEqual(r.returncode, 0, r.stderr[:500])
self.assertTrue(json.loads(r.stdout)["ok"])
def test_subcommand_help(self):
for sub in ("list", "send", "launch", "layout", "spread"):
r = self._box(sub, "--help")
self.assertEqual(r.returncode, 0, sub)
def test_launch_dry_run_injects_approve(self):
r = self._box("launch", "--session", "probe-x",
"--dry-run", "--json")
self.assertEqual(r.returncode, 0, r.stderr[:500])
data = json.loads(r.stdout)
self.assertTrue(data["ok"])
self.assertTrue(data["dry_run"])
self.assertEqual(data["injected"], ["--disable-approval"])
self.assertIn("--disable-approval", data["cmdline"])
self.assertIn("muse-code", data["cmdline"])
def test_launch_dry_run_respects_caller_flags(self):
r = self._box("launch", "--session", "probe-x",
"--dry-run", "--json", "--", "--yolo")
self.assertEqual(r.returncode, 0, r.stderr[:500])
data = json.loads(r.stdout)
self.assertEqual(data["injected"], [])
self.assertIn("--yolo", data["cmdline"])
self.assertNotIn("--disable-approval", data["cmdline"])
def test_send_missing_pane_json(self):
r = self._box("send", "--socket", "/nonexistent.sock",
"%99", "hi", "--json")
self.assertEqual(r.returncode, 0, r.stderr[:500])
data = json.loads(r.stdout)
self.assertFalse(data["ok"])
self.assertEqual(data["error"], "no_such_pane")
def test_layout_empty_socket_json(self):
r = self._box("layout", "--socket", "/nonexistent.sock", "--json")
self.assertEqual(r.returncode, 0, r.stderr[:500])
data = json.loads(r.stdout)
self.assertTrue(data["ok"])
self.assertEqual(data["runtimes"], [])
self.assertIn("width", data["minimum"])
self.assertIn("height", data["minimum"])
def test_spread_empty_socket_json(self):
r = self._box("spread", "--socket", "/nonexistent.sock", "--json")
self.assertEqual(r.returncode, 0, r.stderr[:500])
data = json.loads(r.stdout)
self.assertTrue(data["ok"])
self.assertEqual(data["spread"], [])
def test_spread_dry_run_empty_socket_json(self):
r = self._box("spread", "--socket", "/nonexistent.sock",
"--dry-run", "--json")
self.assertEqual(r.returncode, 0, r.stderr[:500])
data = json.loads(r.stdout)
self.assertTrue(data["dry_run"])
self.assertEqual(data["targets"], [])
if __name__ == "__main__":
unittest.main()
+309
View File
@@ -0,0 +1,309 @@
"""Tests for `box fleet heal` and `box watchdog` in super-cli.py.
Heal drives lock cleanup, watchdog-timer install, tunnel restart, and
egress/relay verification; watchdog status/run expose the systemd
watchdog layer. Shell-outs are faked; no sudo/systemctl/netns touch
the host.
"""
import argparse
import importlib.util
import io
import json
import sys
import unittest
from contextlib import redirect_stdout
from pathlib import Path
from unittest import mock
REPO_ROOT = Path(__file__).resolve().parent.parent
SPEC = importlib.util.spec_from_file_location(
"super_cli_heal", REPO_ROOT / "bin" / "super-cli.py")
super_cli = importlib.util.module_from_spec(SPEC)
SPEC.loader.exec_module(super_cli)
def _ns(**over):
kw = dict(node=None, target=None, json=True)
kw.update(over)
return argparse.Namespace(**kw)
class FakeSh:
"""Programmable stand-in for super_cli._sh; records calls."""
def __init__(self):
self.calls = []
self.handlers = []
def on(self, *needles, rc=0, out=""):
self.handlers.append((needles, rc, out))
return self
def __call__(self, cmd, timeout=120, input_text=None):
self.calls.append((list(cmd), timeout, input_text))
blob = " ".join(cmd)
for needles, rc, out in self.handlers:
if all(n in blob for n in needles):
return rc, out() if callable(out) else out
raise AssertionError("unexpected command: %r" % (cmd,))
class HealBase(unittest.TestCase):
def setUp(self):
import tempfile
self.tmp = tempfile.TemporaryDirectory()
self.addCleanup(self.tmp.cleanup)
self.root = Path(self.tmp.name)
self.systemd = self.root / "systemd"
self.systemd.mkdir()
self.locks = self.root / "locks"
self.locks.mkdir()
self.patchers = [
mock.patch.object(super_cli, "SYSTEMD_SYSTEM_DIR", self.systemd),
mock.patch.object(super_cli, "WATCHDOG_LOCK_TMPL",
str(self.locks / "chromebox-watchdog-{node}.lock")),
]
for p in self.patchers:
p.start()
self.addCleanup(self._unpatch)
self.sh = FakeSh()
self.sh_mock = mock.patch.object(super_cli, "_sh", self.sh)
self.sh_mock.start()
self.addCleanup(self.sh_mock.stop)
def _unpatch(self):
for p in self.patchers:
p.stop()
def run_heal(self, node="dev", as_json=True):
buf = io.StringIO()
with redirect_stdout(buf):
with self.assertRaises(SystemExit) as cm:
super_cli.cmd_fleet_heal(_ns(node=node, json=as_json))
return cm.exception.code, buf.getvalue()
class FleetHealTests(HealBase):
def _healthy_sh(self):
(self.sh
.on("systemctl", "enable", "--now", rc=0, out="")
.on("netvm-node-up.sh", "dev", rc=0,
out="tunnel already up (egress=1.2.3.4), skipping handshake wait\n"
"node=dev netns=warp-dev egress=1.2.3.4")
.on("curl", rc=0, out="ip=1.2.3.4\nfoo=bar"))
def test_heal_recovered(self):
(self.systemd / "chromebox-watchdog-dev.timer").write_text("x")
self._healthy_sh()
with mock.patch.object(super_cli, "probe_cdp_status",
return_value={"ok": True, "latency_ms": 12}):
code, out = self.run_heal()
self.assertEqual(code, 0)
data = json.loads(out)
self.assertEqual(data["verdict"], "RECOVERED")
self.assertTrue(data["ok"])
self.assertEqual([s["step"] for s in data["steps"]],
["lock", "timer", "tunnel", "egress", "relay"])
self.assertTrue(all(s["ok"] for s in data["steps"]))
def test_heal_down_when_egress_fails(self):
(self.systemd / "chromebox-watchdog-dev.timer").write_text("x")
(self.sh
.on("systemctl", "enable", "--now", rc=0, out="")
.on("netvm-node-up.sh", "dev", rc=0,
out="no handshake yet (endpoint=162.159.192.1)\n"
"node=dev netns=warp-dev egress=unknown")
.on("curl", rc=7, out="curl: (7) couldn't connect"))
with mock.patch.object(super_cli, "probe_cdp_status",
return_value={"ok": False, "error": "refused",
"latency_ms": None}):
code, out = self.run_heal()
self.assertEqual(code, 1)
data = json.loads(out)
self.assertEqual(data["verdict"], "DOWN")
self.assertFalse(data["ok"])
by_step = {s["step"]: s for s in data["steps"]}
self.assertFalse(by_step["tunnel"]["ok"])
self.assertFalse(by_step["egress"]["ok"])
def test_heal_down_prints_identity_guidance(self):
(self.systemd / "chromebox-watchdog-dev.timer").write_text("x")
(self.sh
.on("systemctl", rc=0, out="")
.on("netvm-node-up.sh", rc=0, out="node=dev egress=unknown")
.on("curl", rc=7, out="fail"))
with mock.patch.object(super_cli, "probe_cdp_status",
return_value={"ok": False, "error": "x",
"latency_ms": None}):
code, out = self.run_heal(as_json=False)
self.assertEqual(code, 1)
self.assertIn("netvm-new-identity.sh dev", out)
def test_heal_installs_missing_timer(self):
seen = {}
def fake(cmd, timeout=120, input_text=None):
blob = " ".join(cmd)
self.sh.calls.append((list(cmd), timeout, input_text))
if "tee" in blob:
seen["tee_target"] = cmd[-1]
seen["tee_input"] = input_text
return 0, ""
if "daemon-reload" in blob:
seen["reload"] = True
return 0, ""
if "enable" in blob:
return 0, ""
if "netvm-node-up.sh" in blob:
return 0, "node=dev egress=1.2.3.4"
if "curl" in blob:
return 0, "ip=1.2.3.4"
raise AssertionError("unexpected: %r" % (cmd,))
with mock.patch.object(super_cli, "_sh", fake):
with mock.patch.object(
super_cli, "probe_cdp_status",
return_value={"ok": True, "latency_ms": 3}):
code, _ = self.run_heal()
self.assertEqual(code, 0)
self.assertEqual(seen["tee_target"],
str(self.systemd / "chromebox-watchdog-dev.timer"))
self.assertIn("Unit=chromebox-watchdog@dev.service", seen["tee_input"])
self.assertTrue(seen["reload"])
def test_heal_clears_unwritable_lock(self):
(self.systemd / "chromebox-watchdog-dev.timer").write_text("x")
lock = self.locks / "chromebox-watchdog-dev.lock"
lock.write_text("")
lock.chmod(0o444)
self._healthy_sh()
self.sh.on("rm", str(lock), rc=0, out="")
with mock.patch.object(super_cli, "probe_cdp_status",
return_value={"ok": True, "latency_ms": 3}):
code, out = self.run_heal()
self.assertEqual(code, 0)
data = json.loads(out)
by_step = {s["step"]: s for s in data["steps"]}
self.assertTrue(by_step["lock"]["ok"])
self.assertIn("removed stale lock", by_step["lock"]["detail"])
self.assertTrue(any("rm" in " ".join(c[0]) for c in self.sh.calls))
def test_heal_rejects_unknown_node(self):
buf = io.StringIO()
with redirect_stdout(buf):
with self.assertRaises(SystemExit) as cm:
super_cli.cmd_fleet_heal(_ns(node="ghost"))
self.assertEqual(cm.exception.code, 1)
class ShHelperTests(unittest.TestCase):
def test_timeout_and_oserror(self):
import subprocess as real_subprocess
with mock.patch.object(super_cli.subprocess, "run",
side_effect=real_subprocess.TimeoutExpired("x", 1)):
self.assertEqual(super_cli._sh(["x"], timeout=1)[0], 124)
with mock.patch.object(super_cli.subprocess, "run",
side_effect=OSError("nope")):
self.assertEqual(super_cli._sh(["x"])[0], 127)
class WatchdogTests(unittest.TestCase):
def setUp(self):
self.sh = FakeSh()
self.sh_mock = mock.patch.object(super_cli, "_sh", self.sh)
self.sh_mock.start()
self.addCleanup(self.sh_mock.stop)
def _unit_states(self, missing=()):
def fake(cmd, timeout=120, input_text=None):
self.sh.calls.append((list(cmd), timeout, input_text))
unit = cmd[-1]
if "is-enabled" in cmd or "is-active" in cmd:
return (1, "") if unit in missing else (0, "")
if cmd[:3] == ["sudo", "systemctl", "start"]:
return 0, ""
raise AssertionError("unexpected: %r" % (cmd,))
return fake
def test_status_json(self):
stub = mock.Mock()
stub.collect.return_value = {
n: {"browser": "healthy", "browser_detail": "silent",
"cdp": "healthy", "cdp_detail": "silent"}
for n in super_cli.VALID_NODES}
with mock.patch.object(super_cli, "_sh", self._unit_states(
missing=("chromebox-watchdog-def.timer",))):
with mock.patch.dict(sys.modules, {"host_evidence": stub}):
buf = io.StringIO()
with redirect_stdout(buf):
super_cli.cmd_watchdog_status(_ns(json=True))
data = json.loads(buf.getvalue())
self.assertTrue(data["ok"])
self.assertEqual(len(data["nodes"]), 6)
by_node = {n["node"]: n for n in data["nodes"]}
self.assertFalse(by_node["def"]["enabled"])
self.assertFalse(by_node["def"]["active"])
self.assertTrue(by_node["dev"]["active"])
self.assertEqual(by_node["dev"]["browser"], "healthy")
self.assertIn("timer", data["relay"])
def test_status_survives_missing_evidence(self):
with mock.patch.object(super_cli, "_sh", self._unit_states()):
with mock.patch.dict(sys.modules, {"host_evidence": None}):
buf = io.StringIO()
with redirect_stdout(buf):
super_cli.cmd_watchdog_status(_ns(json=True))
data = json.loads(buf.getvalue())
self.assertTrue(data["ok"])
self.assertEqual(data["nodes"][0]["browser"], "unknown")
def test_run_node_and_relay(self):
for target, unit in (("dev", "chromebox-watchdog@dev.service"),
("relay", "cdp-relay-watchdog.service")):
with self.subTest(target=target):
with mock.patch.object(super_cli, "_sh",
self._unit_states()) as _:
buf = io.StringIO()
with redirect_stdout(buf):
with self.assertRaises(SystemExit) as cm:
super_cli.cmd_watchdog_run(
_ns(target=target, json=True))
self.assertEqual(cm.exception.code, 0)
data = json.loads(buf.getvalue())
self.assertTrue(data["ok"])
self.assertEqual(data["unit"], unit)
def test_run_rejects_bad_target(self):
with self.assertRaises(SystemExit) as cm:
super_cli.cmd_watchdog_run(_ns(target="ghost"))
self.assertEqual(cm.exception.code, 1)
def test_run_reports_start_failure(self):
def fake(cmd, timeout=120, input_text=None):
return 1, "Failed to start"
with mock.patch.object(super_cli, "_sh", fake):
buf = io.StringIO()
with redirect_stdout(buf):
with self.assertRaises(SystemExit) as cm:
super_cli.cmd_watchdog_run(_ns(target="dev", json=True))
self.assertEqual(cm.exception.code, 1)
self.assertFalse(json.loads(buf.getvalue())["ok"])
class ParserTests(unittest.TestCase):
def test_fleet_heal_parses(self):
args = super_cli.build_parser().parse_args(["fleet", "heal", "dev"])
self.assertEqual((args.domain, args.action, args.node),
("fleet", "heal", "dev"))
def test_watchdog_parses(self):
args = super_cli.build_parser().parse_args(["watchdog", "run", "relay"])
self.assertEqual((args.domain, args.action, args.target),
("watchdog", "run", "relay"))
args = super_cli.build_parser().parse_args(["watchdog"])
self.assertEqual((args.domain, args.action), ("watchdog", "status"))
if __name__ == "__main__":
unittest.main()
+134 -3
View File
@@ -129,6 +129,17 @@ class DialogTests(unittest.TestCase):
side_effect=RuntimeError("boom")):
self.assertIsNone(dialog.dialog_text(mock.Mock()))
def test_dialog_text_rejects_non_string(self):
with mock.patch.object(dialog, "cdp_evaluate", return_value=123):
self.assertIsNone(dialog.dialog_text(mock.Mock()))
def test_describe_rows_rejects_non_list(self):
with mock.patch.object(dialog, "goto_tab", return_value=True), \
mock.patch.object(dialog, "cdp_evaluate",
return_value="error"):
self.assertEqual(dialog.describe_rows(mock.Mock(),
"Permissions"), [])
def test_click_row_success_and_no_match(self):
with mock.patch.object(dialog, "cdp_evaluate",
side_effect=["CLICKED", "CLICKED",
@@ -147,6 +158,11 @@ class DialogTests(unittest.TestCase):
"Permissions"))
self.assertEqual(cdp.call_count, 1)
def test_row_js_excludes_tab_rail(self):
blob = json.dumps(dialog.TAB_NAMES)
self.assertIn(blob, dialog.JS_CLICK_ROW_TMPL)
self.assertIn(blob, dialog.JS_DESCRIBE_ROWS)
def test_describe_rows_maps(self):
rows = [{"name": "Websites", "help": "1 site", "extra": ""}]
with mock.patch.object(dialog, "goto_tab", return_value=True), \
@@ -183,8 +199,8 @@ class ControlsTests(unittest.TestCase):
on = [{"heading": "Connector defaults", "value": "auto_allow",
"checked": True}]
with mock.patch.object(controls, "cdp_evaluate",
side_effect=["CLICKED", off,
{"x": 5, "y": 6}, on]), \
side_effect=["CLICKED"] + [off] * 10
+ [{"x": 5, "y": 6}, on]), \
mock.patch.object(controls, "real_click") as click:
self.assertTrue(controls.set_radio_by_heading(
mock.Mock(), "Connector defaults", "auto_allow"))
@@ -194,7 +210,8 @@ class ControlsTests(unittest.TestCase):
off = [{"heading": "Connector defaults", "value": "auto_allow",
"checked": False}]
with mock.patch.object(controls, "cdp_evaluate",
side_effect=["CLICKED", off, None]), \
side_effect=["CLICKED"] + [off] * 10
+ [None]), \
mock.patch.object(controls, "real_click"):
self.assertFalse(controls.set_radio_by_heading(
mock.Mock(), "Connector defaults", "auto_allow"))
@@ -234,6 +251,22 @@ class ControlsTests(unittest.TestCase):
self.assertTrue(controls.set_switch(mock.Mock(),
"Transparent proxy", True))
def test_set_switch_polls_then_lands(self):
off = [{"label": "Transparent proxy row", "aria": "",
"checked": False}]
on = [{"label": "Transparent proxy row", "aria": "",
"checked": True}]
with mock.patch.object(controls, "cdp_evaluate",
side_effect=[off, "CLICKED", off, off, on]):
self.assertTrue(controls.set_switch(mock.Mock(),
"Transparent proxy", True))
def test_listers_reject_wrong_types(self):
with mock.patch.object(controls, "cdp_evaluate",
return_value="error"):
self.assertEqual(controls.list_radios(mock.Mock()), [])
self.assertEqual(controls.list_switches(mock.Mock()), [])
class TogglesTests(unittest.TestCase):
def test_resolve_static_website_protocol(self):
@@ -248,6 +281,14 @@ class TogglesTests(unittest.TestCase):
"permissions.protocols:Agent Skills endpoints")
self.assertEqual(spec["label"], "Agent Skills endpoints")
def test_resolve_protocol_network_and_substring(self):
spec = toggles.resolve_toggle("permissions.protocols:outbound-ssh")
self.assertEqual(spec["label"], "Outbound SSH")
spec = toggles.resolve_toggle("permissions.protocols:ssh")
self.assertEqual(spec["label"], "Outbound SSH")
with self.assertRaises(toggles.MenuError):
toggles.resolve_toggle("permissions.protocols:mail")
def test_resolve_unknowns_raise_before_cdp(self):
for bad in ("nope", "", "permissions.websites:",
"permissions.protocols:nope"):
@@ -315,6 +356,23 @@ class TogglesTests(unittest.TestCase):
res = toggles.set_toggle("pip", "permissions.web_access",
"always_ask")
self.assertEqual((res["ok"], res["value"]), (True, "always_ask"))
self.assertNotIn("readback_only", res)
def test_set_toggle_readback_only_success(self):
with mock.patch.object(toggles, "get_cdp_ws",
return_value=(mock.Mock(), {})), \
mock.patch.object(dialog, "open_settings",
return_value=True), \
mock.patch.object(dialog, "goto_tab", return_value=True), \
mock.patch.object(controls, "set_radio_by_heading",
return_value=False), \
mock.patch.object(toggles, "get_toggle",
return_value={"ok": True,
"value": "always_ask"}):
res = toggles.set_toggle("pip", "permissions.web_access",
"always_ask")
self.assertTrue(res["ok"])
self.assertTrue(res["readback_only"])
def test_set_toggle_readback_mismatch_fails(self):
with mock.patch.object(toggles, "get_cdp_ws",
@@ -405,6 +463,15 @@ class TabContractTests(unittest.TestCase):
"Model Context Protocol servers (SSE)")
self.assertEqual(permissions.resolve_protocol(
"Agent Skills endpoints"), "Agent Skills endpoints")
self.assertEqual(permissions.resolve_protocol("outbound-ssh"),
"Outbound SSH")
self.assertEqual(permissions.resolve_protocol("Outbound SSH"),
"Outbound SSH")
self.assertEqual(permissions.resolve_protocol("ssh"),
"Outbound SSH")
self.assertEqual(permissions.resolve_protocol("tcp"),
"Other TCP connections")
self.assertIsNone(permissions.resolve_protocol("mail"))
self.assertIsNone(permissions.resolve_protocol("nope"))
self.assertIsNone(permissions.resolve_protocol(""))
self.assertIsNone(permissions.resolve_protocol(None))
@@ -415,12 +482,76 @@ class TabContractTests(unittest.TestCase):
mock.Mock(), "x.com", "Sometimes"))
ev.assert_not_called()
def test_set_website_noop_without_click(self):
rows = [{"host": "x.com", "mode": "Allow", "x": 1, "y": 2}]
with mock.patch.object(permissions, "_websites_raw",
return_value=rows), \
mock.patch.object(permissions, "_back_to_root",
return_value=True), \
mock.patch.object(permissions, "real_click") as click, \
mock.patch("time.sleep"):
self.assertTrue(permissions.set_website_mode(
mock.Mock(), "x.com", "Allow"))
click.assert_not_called()
def test_set_website_absent_host_fails(self):
with mock.patch.object(permissions, "_websites_raw",
return_value=[]), \
mock.patch.object(permissions, "_back_to_root",
return_value=True), \
mock.patch("time.sleep"):
self.assertFalse(permissions.set_website_mode(
mock.Mock(), "y.com", "Ask"))
def test_set_website_ask_verifies_by_absence(self):
rows = [{"host": "x.com", "mode": "Allow", "x": 1, "y": 2}]
items = [{"text": "Allow"}, {"text": "Ask"}, {"text": "Deny"}]
with mock.patch.object(permissions, "_websites_raw",
return_value=rows), \
mock.patch.object(permissions, "_back_to_root",
return_value=True), \
mock.patch.object(permissions, "real_click"), \
mock.patch.object(permissions, "_eval",
side_effect=[items, "CLICKED", []]), \
mock.patch("time.sleep"):
self.assertTrue(permissions.set_website_mode(
mock.Mock(), "x.com", "Ask"))
def test_stable_rows_waits_for_agreement(self):
rows = [{"host": "x.com"}]
with mock.patch.object(permissions, "_eval",
side_effect=[None, rows, rows]), \
mock.patch("time.sleep"):
self.assertEqual(
permissions._stable_rows(mock.Mock(), "js"), rows)
def test_stable_rows_gives_up(self):
with mock.patch.object(permissions, "_eval", return_value=None), \
mock.patch("time.sleep"):
self.assertIsNone(
permissions._stable_rows(mock.Mock(), "js"))
def test_back_to_root_prefers_back_affordance(self):
with mock.patch.object(dialog, "go_back", return_value=True), \
mock.patch.object(dialog, "dialog_text",
return_value="Manage permissions\nrows"):
self.assertTrue(permissions._back_to_root(mock.Mock()))
def test_theme_values_match_live(self):
self.assertIn("avatar", general.THEME_VALUES)
self.assertNotIn("match", general.THEME_VALUES)
def test_data_controls_pinned_label(self):
self.assertEqual(data_controls.SWITCH_LABEL,
"Help improve our AI models")
sws = [{"label": "Something else", "aria": "", "checked": False},
{"label": "Help improve our AI models", "aria": "",
"checked": True}]
with mock.patch.object(dialog, "goto_tab", return_value=True), \
mock.patch.object(controls, "list_switches",
return_value=sws):
self.assertTrue(data_controls.ai_improvement(mock.Mock()))
def test_data_controls_single_switch(self):
one = [{"label": "Help improve the model", "aria": "",
"checked": True}]
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -272,8 +272,8 @@ class TestPromptTUIIntegration(unittest.TestCase):
target_text = sends[0]["text"]
h, w = self.tui.stdscr.getmaxyx()
modal_w = min(74, w - 6)
modal_h = min(20, h - 4)
modal_w = min(84 if self.tui.modal in ("prompts", "history_search") else 74, w - 6)
modal_h = min(22 if self.tui.modal in ("prompts", "history_search") else 20, h - 4)
top_y = (h - modal_h) // 2
left_x = (w - modal_w) // 2
+11
View File
@@ -198,6 +198,17 @@ class TestRateLimitUserInteraction(unittest.TestCase):
self.assertEqual(updated[0]["text"], "Earlier reply")
self.assertEqual(updated[1]["text"], "New uncommitted instruction")
def test_history_payload_mentioning_rate_limits_not_falsely_flagged(self):
"""Chat history discussing rate limits or HTTP 429 does NOT place node into cooldown when rc == 0."""
msgs_about_rate_limits = [
{"role": "user", "text": "Are we hitting any rate limits or 429 errors?"},
{"role": "assistant", "text": "No rate limit reached, all queues are unthrottled and healthy."}
]
with patch.object(muse_tui_rl, "run_command_isolated", return_value=(0, json.dumps(msgs_about_rate_limits), "")):
self.tui.data._fetch_history("pip", "thread-chat")
self.assertFalse(self.tui.data.is_node_rate_limited("pip"))
self.assertEqual(len(self.tui.data.history_cache.get(("pip", "thread-chat"), [])), 2)
if __name__ == "__main__":
import json
+50
View File
@@ -0,0 +1,50 @@
"""Regression tests for the hyphenated swarm-prune branch.
`swarm-prune` was dispatched but had no handler branch, so it exited 0
with no output. It now mirrors `swarm prune` (preview counts without
--confirm, archive only with --confirm). These tests never pass
--confirm, so they never write.
"""
import json
import subprocess
import sys
import unittest
from pathlib import Path
REPO_ROOT = Path(__file__).resolve().parent.parent
BOX_CTL = REPO_ROOT / "bin" / "box-ctl.py"
def _box_ctl(*args):
return subprocess.run(
[sys.executable, str(BOX_CTL), *args],
capture_output=True, text=True, timeout=120)
class SwarmPruneTests(unittest.TestCase):
def test_hyphenated_prune_previews_without_confirm(self):
r = _box_ctl("swarm-prune")
self.assertNotEqual(r.returncode, 0)
self.assertTrue(r.stdout.strip(), "hyphenated prune must emit JSON")
payload = json.loads(r.stdout)
self.assertEqual(payload["code"], "CONFIRM_REQUIRED")
self.assertIn("matching_count", payload["detail"])
self.assertEqual(payload["detail"]["stale_hours"], 6)
def test_hyphenated_matches_space_form(self):
hyphen = json.loads(_box_ctl("swarm-prune").stdout)
space = json.loads(_box_ctl("swarm", "prune").stdout)
self.assertEqual(hyphen["code"], "CONFIRM_REQUIRED")
self.assertEqual(space["code"], "CONFIRM_REQUIRED")
self.assertEqual(sorted(hyphen["detail"]),
sorted(space["detail"]))
def test_bad_stale_hours_falls_back_to_default(self):
r = _box_ctl("swarm-prune", "--stale-hours", "bogus")
payload = json.loads(r.stdout)
self.assertEqual(payload["code"], "CONFIRM_REQUIRED")
self.assertEqual(payload["detail"]["stale_hours"], 6)
if __name__ == "__main__":
unittest.main()
+100
View File
@@ -0,0 +1,100 @@
"""Watchdog coverage: every registry node is supervised.
Regression test for the dev/def outage (2026-10-06): both watchdogs
hardcoded the original four nodes (muse/pip/646/opm), so dev/def had no
browser supervision, no Warp-tunnel auto-recovery, and no relay
supervision. A dead tunnel paged CRITICAL partition alerts forever with
nothing acting on it.
Both scripts now resolve nodes/ports from the fleet registry
(bin/netvm-registry.py). These tests drive the real shell code sourced
with a LIB_ONLY guard (same pattern as test_agent_health.py).
"""
import importlib.util
import subprocess
import unittest
from pathlib import Path
REPO_ROOT = Path(__file__).resolve().parent.parent
CHROMEBOX_WD = REPO_ROOT / "bin" / "chromebox-watchdog.sh"
RELAY_WD = REPO_ROOT / "bin" / "cdp-relay-watchdog.sh"
# The fleet's pinned contract (also pinned in bin/netvm-names.sh).
EXPECTED_PORTS = {
"muse": 9410, "pip": 9420, "646": 9430,
"opm": 9440, "def": 9450, "dev": 9455,
}
EXPECTED_PEERS = {
"muse": "10.201.35.2", "pip": "10.201.87.2", "646": "10.201.202.2",
"opm": "10.201.157.2", "def": "10.201.66.2", "dev": "10.201.36.2",
}
def _load(mod_name, rel_path):
spec = importlib.util.spec_from_file_location(mod_name, REPO_ROOT / rel_path)
mod = importlib.util.module_from_spec(spec)
spec.loader.exec_module(mod)
return mod
def _chromebox(profile, snippet):
prog = "set -- '%s'\nsource '%s'\n%s\n" % (profile, CHROMEBOX_WD, snippet)
env = {"PATH": "/usr/bin:/bin", "CHROMEBOX_WATCHDOG_LIB_ONLY": "1"}
return subprocess.run(["bash", "-c", prog], capture_output=True,
text=True, env=env, timeout=30)
def _relay(snippet):
prog = "source '%s'\n%s\n" % (RELAY_WD, snippet)
env = {"PATH": "/usr/bin:/bin", "CDP_RELAY_WATCHDOG_LIB_ONLY": "1"}
return subprocess.run(["bash", "-c", prog], capture_output=True,
text=True, env=env, timeout=30)
class RegistryContract(unittest.TestCase):
def test_registry_matches_pinned_ports(self):
reg = _load("netvm_registry_cov", "bin/netvm-registry.py")
self.assertEqual(reg.active_nodes(), EXPECTED_PORTS)
for node, peer in EXPECTED_PEERS.items():
self.assertEqual(reg.peer_ip_for(node), peer)
class ChromeboxWatchdogPorts(unittest.TestCase):
def test_all_registry_nodes_resolve_to_pinned_ports(self):
for node, port in sorted(EXPECTED_PORTS.items()):
with self.subTest(node=node):
r = _chromebox(node, "echo \"PORT=$CDP_PORT\"")
self.assertEqual(r.returncode, 0, r.stderr)
line = next((ln for ln in r.stdout.splitlines()
if ln.startswith("PORT=")), None)
self.assertIsNotNone(
line, "no PORT line: stdout=%r stderr=%r"
% (r.stdout, r.stderr))
self.assertEqual(int(line.split("=", 1)[1]), port)
def test_unknown_profile_rejected(self):
r = _chromebox("ghost", "echo UNREACHABLE")
self.assertNotEqual(r.returncode, 0)
self.assertIn("unknown profile", r.stderr)
self.assertNotIn("UNREACHABLE", r.stdout)
class RelayWatchdogCoverage(unittest.TestCase):
def test_watched_nodes_covers_registry(self):
r = _relay("watched_nodes")
self.assertEqual(r.returncode, 0, r.stderr)
nodes = set(r.stdout.split())
for node in EXPECTED_PORTS:
self.assertIn(node, nodes)
def test_relay_targets_use_pinned_ports(self):
for node, port in sorted(EXPECTED_PORTS.items()):
with self.subTest(node=node):
r = _relay("relay_target %s" % node)
self.assertEqual(r.returncode, 0, r.stderr)
self.assertEqual(r.stdout.strip(),
"%s:%d" % (EXPECTED_PEERS[node], port))
if __name__ == "__main__":
unittest.main()