Files
box/tests/test_box_runtime.py
T
operator 0065d11e97 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
2026-10-07 00:25:46 +00:00

343 lines
14 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/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()