Files
status-indicators/tests/test_builders.py
T
christianandClaude Fable 5.1 14fce2cf9a Audio failover: press FOH Backup by itself when Studio B dies; Companion button presses as fixes
A new fix kind "companion_press" presses any Companion button over its HTTP API (page, row, col).
A fix marked "failover" runs by itself within seconds of its check failing: inside the "failover"
windows (Sunday 06:30-13:30), even while ALARMS is muted, once per fault, never back the other way.
The hidden STB AUDIO key (inside AUDIO) watches Studio B's ping every second, Pro Tools over SSH
every 2 s and its Dante device; each check's fix is the FOH Backup press. The keys say ON FOH BACKUP
until STREAM STB on the Restart page switches back. Settings page: the press builder, the failover
tick and undo, the Failover calendar, help entry "A fallback button".

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-15 12:40:24 -04:00

158 lines
9.9 KiB
Python

"""Fix and check builders: every one of today's fixes regenerates from its fields exactly, values are
quoted or refused, a start builder never closes a running program, and this Mac becomes a local fix."""
import json
import os
import sys
import unittest
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.insert(0, ROOT)
import schema # noqa: E402
from settings import builders as b # noqa: E402
CFG = json.load(open(os.path.join(ROOT, "config.json")))
class TestTodaysFixes(unittest.TestCase):
def test_every_fix_has_a_builder_and_regenerates_exactly(self):
known = {t.get("kind") or "ssh" for t in b.TEMPLATES.values()} | {"local"}
n = 0
for fid, fix in CFG["fixes"].items():
if (fix.get("kind") or "ssh") not in known: # a kind the builders do not make yet (another feature's)
continue
self.assertTrue(fix.get("template"), fid)
built = b.build(CFG, fix)
for k in ("run", "kind", "host"):
self.assertEqual(built.get(k), fix.get(k), "%s %s" % (fid, k))
n += 1
self.assertEqual(b.mismatches(CFG), [])
self.assertGreaterEqual(n, 27)
def test_todays_remote_checks_regenerate(self):
n = 0
for t in CFG["tiles"]:
for c in t.get("checks", []):
if c.get("template"):
self.assertEqual(b.build_check(CFG, c)["cmd"], c["cmd"])
n += 1
self.assertEqual(n, 6)
def test_a_changed_command_is_refused_by_validate(self):
cfg = json.loads(json.dumps(CFG))
cfg["fixes"]["vista"]["run"] = "shutdown /s"
errors, _ = schema.validate(cfg)
self.assertTrue(any("vista" in e and "no longer matches" in e for e in errors), errors)
cfg["fixes"]["vista"]["template"] = "custom" # a hand-typed command is allowed when it says so
self.assertEqual([e for e in schema.validate(cfg)[0] if "vista" in e], [])
def test_exact_strings_of_the_shapes_that_matter(self):
vista = b.build(CFG, {"template": "win_start", "params": {"host": "lighting", "task": "Start Vista", "exe": "Vista.exe"}})
self.assertEqual(vista["run"], 'tasklist /NH /FI "IMAGENAME eq Vista.exe" | find /I "Vista.exe" >nul || (schtasks /Run /TN "\\StatusPanel\\Start Vista" >nul '
'&& ping -n 9 127.0.0.1 >nul && tasklist /NH /FI "IMAGENAME eq Vista.exe" | find /I "Vista.exe" >nul)')
self.assertEqual((vista["kind"], vista["host"]), ("ssh", "lighting"))
self.assertNotIn("%ERRORLEVEL%", vista["run"])
caps = b.build(CFG, {"template": "helpers", "params": {"labels": ["a.b", "c.d"]}})
self.assertEqual(caps["run"], "s=0; { launchctl kickstart -k gui/$(id -u)/a.b || launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/a.b.plist; } || s=1; "
"{ launchctl kickstart -k gui/$(id -u)/c.d || launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/c.d.plist; } || s=1; exit $s")
self.assertEqual(caps["kind"], "local")
class TestQuotingAndGuards(unittest.TestCase):
def test_mac_names_are_quoted(self):
f = b.build(CFG, {"template": "mac_start", "params": {"host": "tracks", "process": "Ableton Live 11 Suite", "app": "Rob's App"}})
self.assertEqual(f["run"], "pgrep -xq 'Ableton Live 11 Suite' || open -a 'Rob'\"'\"'s App'")
f = b.build(CFG, {"template": "mac_restart", "params": {"host": "tracks", "process": "x; rm -rf /", "app": "X"}})
self.assertIn("pkill -TERM -x 'x; rm -rf /'", f["run"])
def test_windows_values_are_checked(self):
for bad in ("Vista & del", "Start!Vista", 'a"b', "x%y", "a^b"):
with self.assertRaises(ValueError):
b.build(CFG, {"template": "win_start", "params": {"host": "lighting", "task": bad, "exe": "Vista.exe"}})
ok = b.build(CFG, {"template": "win_restart", "params": {"host": "bibletv", "task": "Start BibleApp", "exe": "Bible App.exe"}})
self.assertEqual(ok["run"], CFG["fixes"]["bibleapp"]["run"])
def test_helper_labels_and_folders_are_checked(self):
with self.assertRaises(ValueError):
b.build(CFG, {"template": "helpers", "params": {"labels": ["org.x; rm"]}})
cfg = json.loads(json.dumps(CFG))
cfg["site"]["solo_dir"] = '~/Library/"x"'
with self.assertRaises(ValueError):
b.build(cfg, {"template": "solo_afl", "params": {"host": "studiob"}})
cfg["site"]["solo_dir"] = "~/Library/Application Support/status-panel/"
self.assertEqual(b.build(cfg, {"template": "solo_afl", "params": {"host": "studiob"}})["run"], CFG["fixes"]["solo_afl"]["run"])
def test_start_builders_never_close_a_running_program(self):
for name, t in b.TEMPLATES.items():
if t.get("start_only") and t.get("render"):
params = {p[0]: (p[4] if p[4] is not None else {"host": "lighting" if t.get("os") == "windows" else "tracks"}.get(p[0], "Thing.exe" if t.get("os") == "windows" else "Thing"))
for p in t["params"]}
run = b.build(CFG, {"template": name, "params": params})["run"]
self.assertFalse(any(w in run for w in ("pkill", "taskkill", "killall", "kill ")), (name, run))
self.assertTrue("||" in run, (name, run)) # only if not already running
def test_this_mac_becomes_a_local_fix_and_the_wrong_os_is_refused(self):
f = b.build(CFG, {"template": "mac_restart", "params": {"host": "ingest", "process": "ProdDash", "app": "/Applications/ProdDash.app"}})
self.assertEqual((f["kind"], f.get("host")), ("local", None))
with self.assertRaises(ValueError):
b.build(CFG, {"template": "win_start", "params": {"host": "tracks", "task": "Start X", "exe": "X.exe"}})
with self.assertRaises(ValueError):
b.build(CFG, {"template": "mac_start", "params": {"host": "lighting", "process": "x", "app": "x"}})
with self.assertRaises(ValueError):
b.build(CFG, {"template": "mac_start", "params": {"host": "nowhere", "process": "x", "app": "x"}})
def test_missing_fields_and_suggested_words(self):
with self.assertRaises(ValueError) as cm:
b.build(CFG, {"template": "mac_start", "params": {"host": "tracks"}})
self.assertIn("process name is needed", str(cm.exception))
self.assertEqual(b.suggest_label(CFG, {"template": "spotify"}), "play the Christian EDM 2026 playlist in Spotify on NA Ingest (opens Spotify if it is closed)")
self.assertEqual(b.suggest_label(CFG, {"template": "win_start", "params": {"task": "Start OBS", "exe": "obs64.exe"}}), "start obs64 if it is not running")
names = {t["name"] for t in b.template_list(CFG)}
self.assertEqual(names, set(b.TEMPLATES))
def test_check_builders(self):
c = b.build_check(CFG, {"type": "remote", "template": "mac_program", "params": {"host": "prop", "process": "Pro Tools"}, "msg": "PT NOT RUNNING", "fix": "x"})
self.assertEqual((c["cmd"], c["host"], c["fix"], c["msg"]), ("pgrep -xq 'Pro Tools'", "prop", "x", "PT NOT RUNNING"))
with self.assertRaises(ValueError):
b.build_check(CFG, {"template": "mac_program", "params": {"host": "ingest", "process": "x"}})
cfg = json.loads(json.dumps(CFG))
for t in cfg["tiles"]:
for ch in t.get("checks", []):
if ch.get("template") == "mac_program":
ch["cmd"] = "true"
self.assertTrue(any("no longer matches" in e for e in schema.validate(cfg)[0]))
if __name__ == "__main__":
unittest.main()
class TestCompanionPress(unittest.TestCase):
def test_a_press_builds_from_its_fields(self):
f = b.build(CFG, {"template": "companion_press", "params": {"page": 4, "row": 0, "col": "5", "button": "FOH Backup"}, "short": "FOH BACKUP"})
self.assertEqual((f["kind"], f["page"], f["row"], f["col"], f["short"]), ("companion_press", 4, 0, 5, "FOH BACKUP"))
self.assertEqual(f["params"]["col"], 5) # copied as a number
self.assertNotIn("run", f); self.assertNotIn("host", f)
self.assertEqual(b.suggest_label(CFG, f), "press the FOH Backup button in Companion (page 4, row 0, column 5)")
self.assertEqual(b.suggest_label(CFG, {"template": "companion_press", "params": {"page": 4, "row": 0, "col": 5}}),
"press the button in Companion (page 4, row 0, column 5)")
for bad in ({"page": 4, "row": 0}, {"page": "four", "row": 0, "col": 5}, {"page": 4, "row": 1.5, "col": 5},
{"page": True, "row": 0, "col": 5}):
with self.assertRaises(ValueError):
b.build(CFG, {"template": "companion_press", "params": bad})
def test_a_changed_button_is_caught(self):
cfg = json.loads(json.dumps(CFG))
cfg["fixes"]["stream_foh_backup"]["col"] = 7 # the field moved, the params did not
self.assertTrue(any("stream_foh_backup" in e and "its col no longer matches" in e for e in b.mismatches(cfg)), b.mismatches(cfg))
self.assertEqual([e for e in b.mismatches(CFG) if "stream_" in e], [])
class TestOutlet(unittest.TestCase):
def test_outlet_cycle_renders_and_is_local(self):
f = b.build(CFG, {"template": "outlet_cycle", "params": {"outlet": "tascam", "address": "10.1.11.74", "wait_seconds": 8}})
self.assertEqual(f["run"], '/usr/bin/python3 "$HOME/Library/Application Support/status-panel/settings/outlet.py" cycle tascam --ping 10.1.11.74 --wait 8')
self.assertEqual(f["kind"], "local")
self.assertIn("tascam", b.suggest_label(CFG, {"template": "outlet_cycle", "params": {"outlet": "tascam"}}))
f = b.build(CFG, {"template": "outlet_cycle", "params": {"outlet": "x; rm", "address": "a b"}})
self.assertIn("cycle 'x; rm' --ping 'a b'", f["run"]) # quoted, never run as shell