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:
+134
-3
@@ -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}]
|
||||
|
||||
Reference in New Issue
Block a user