From 4c3ba1b68107208fe32c223588576a7bdca19a12 Mon Sep 17 00:00:00 2001 From: historia Date: Thu, 27 Aug 2026 19:44:30 -0400 Subject: feat: nest start/stop server menu in configure backend menu --- app/tests/test_hub.py | 70 ++++++++++++++++++++++++++++----------------------- 1 file changed, 39 insertions(+), 31 deletions(-) (limited to 'app/tests') diff --git a/app/tests/test_hub.py b/app/tests/test_hub.py index 3550f5b..daa4abd 100644 --- a/app/tests/test_hub.py +++ b/app/tests/test_hub.py @@ -195,7 +195,7 @@ class HubMenuTests(unittest.TestCase): self.assertEqual(labels, ["Configure Backends", "Settings", "Help", "Quit"]) - def test_menu_has_all_five_when_one_installed(self): + def test_menu_lists_convert_when_one_installed(self): captured = {} def fake_menu(stdscr, title, options, **kwargs): @@ -212,8 +212,8 @@ class HubMenuTests(unittest.TestCase): labels = [label for label, _ in captured["options"]] self.assertEqual( labels, - ["Generate Audiobooks", "Configure Backends", - "Start/Stop Backend Servers", "Settings", "Help", "Quit"]) + ["Generate Audiobooks", "Configure Backends", "Settings", + "Help", "Quit"]) # The status table is passed through, one row per backend. self.assertEqual(captured["rows"], [("qwen-tts", "installed", "ok", "body")]) @@ -241,9 +241,10 @@ class HubMenuTests(unittest.TestCase): [("audio.cpp", "unavailable", "err", "dim"), ("qwen-tts", "running [remote]", "ok", "body")]) - def test_menu_hides_server_when_only_running(self): + def test_server_menu_hidden_from_main_when_only_running(self): # Running but not installed (an external server) still unlocks - # Convert — but Start/Stop needs the backend on this machine. + # Convert — but Start/Stop needs the backend on this machine, and + # lives in Configure Backends. captured = {} def fake_menu(stdscr, title, options, **kwargs): @@ -297,7 +298,7 @@ class HubMenuTests(unittest.TestCase): # Installed-but-not-ready backends → Convert is offered, but the # convert flow has nothing to list: it flashes a hint (no "Configure # Backends" detour anymore) and returns to the main menu. Then - # quit: 6 main-menu options, Quit is the 6th (Down x5). + # quit: 5 main-menu options, Quit is the 5th (Down x4). from backends import BackendStatus statuses = [BackendStatus("audiocpp", "audio.cpp", installed=True, configured=False), @@ -312,11 +313,10 @@ class HubMenuTests(unittest.TestCase): with patch.object(hub, "detect_all", return_value=statuses), \ patch.object(hub.tui, "flash", fake_flash): - # Convert(Enter) → flash → main menu; Down x5 -> Quit, Enter. + # Convert(Enter) → flash → main menu; Down x4 -> Quit, Enter. screen = FakeScreen(keys=[10, FakeCurses.KEY_DOWN, FakeCurses.KEY_DOWN, FakeCurses.KEY_DOWN, FakeCurses.KEY_DOWN, - FakeCurses.KEY_DOWN, 10]) result = hub._Hub(screen).run() self.assertIsNone(result) @@ -430,13 +430,14 @@ class SubmenuStatusTableTests(unittest.TestCase): return_value="/usr/bin/ffmpeg"): result = hub._Hub(None).screen_configure() self.assertIs(result, tui.Wizard.BACK) - # Install (faster uninstalled), then Uninstall (qwen installed); - # no audio.cpp means no model actions. qwen has no Configure entry - # (its wizard asks nothing to configure). + # Start/Stop (qwen installed), then Install (faster uninstalled), + # then Uninstall; no audio.cpp means no model actions. qwen has no + # Configure entry (its wizard asks nothing to configure). self.assertEqual([label for label, _ in captured["options"]], - ["Install Backend", "Uninstall Backend"]) - # ...the shared status table carries the states instead. - self.assertEqual(captured["table_title"], "Backend Status") + ["Start/Stop Backend Servers", "Install Backend", + "Uninstall Backend"]) + # ...the shared status table carries the states instead (no title). + self.assertIsNone(captured.get("table_title")) self.assertEqual( captured["table_rows"], [("qwen-tts", "installed", "ok", "body"), @@ -480,10 +481,11 @@ class SubmenuStatusTableTests(unittest.TestCase): self.assertIs(result, tui.Wizard.BACK) # Update sits between Install and Uninstall; it is offered once for # the whole set of installed backends (faster has nothing on disk - # and so contributes nothing). + # and so contributes nothing). Start/Stop heads the tail actions + # (qwen is installed). self.assertEqual([label for label, _ in captured["options"]], - ["Install Backend", "Update Backends", - "Uninstall Backend"]) + ["Start/Stop Backend Servers", "Install Backend", + "Update Backends", "Uninstall Backend"]) def test_configure_backends_menu_audiocpp_model_actions(self): captured = {} @@ -513,13 +515,13 @@ class SubmenuStatusTableTests(unittest.TestCase): labels = self._labels(captured["options"]) # The missing-model download heads the menu as the recommended next # step (yellow suffix), separated from the rest by a blank line; - # Configure + Uninstall follow. The backend is built, so no "Build" - # action is offered. Deleting unused models now lives inside the - # "Configure audio.cpp" wizard, not here. + # Start/Stop + Configure + Uninstall follow. The backend is built, + # so no "Build" action is offered. Deleting unused models now lives + # inside the "Configure audio.cpp" wizard, not here. self.assertEqual( labels, ["Download Missing Models (audio.cpp)", "Configure audio.cpp", - "Uninstall Backend"]) + "Start/Stop Backend Servers", "Uninstall Backend"]) self.assertEqual(captured["options"][0], ("Download Missing Models (audio.cpp)", "download_models", ("[recommended]", "warn"))) @@ -587,7 +589,8 @@ class SubmenuStatusTableTests(unittest.TestCase): captured = {} infos = [BackendInfo("audiocpp", "audio.cpp", lambda: None, lambda: 0)] # Built but no server.json: only Configure (the next step) plus - # Uninstall — no Build, no Download, no Install entry. + # Start/Stop and Uninstall — no Build, no Download, no Install + # entry. statuses = [BackendStatus("audiocpp", "audio.cpp", installed=True, configured=False)] with tempfile.TemporaryDirectory() as td: @@ -605,7 +608,8 @@ class SubmenuStatusTableTests(unittest.TestCase): result = hub._Hub(None).screen_configure() self.assertIs(result, tui.Wizard.BACK) self.assertEqual(self._labels(captured["options"]), - ["Configure audio.cpp", "Uninstall Backend"]) + ["Configure audio.cpp", "Start/Stop Backend Servers", + "Uninstall Backend"]) def test_configure_menu_offers_qwens_per_model_manager(self): # qwen ships a dedicated configure screen (per-model weight @@ -626,7 +630,8 @@ class SubmenuStatusTableTests(unittest.TestCase): result = hub._Hub(None).screen_configure() self.assertIs(result, tui.Wizard.BACK) self.assertEqual(self._labels(captured["options"]), - ["Configure qwen-tts", "Uninstall Backend"]) + ["Configure qwen-tts", "Start/Stop Backend Servers", + "Uninstall Backend"]) self.assertEqual(captured["table_rows"], [("qwen-tts", "installed", "ok", "body")]) @@ -682,7 +687,7 @@ class SubmenuStatusTableTests(unittest.TestCase): result = hub._Hub(None).screen_configure() self.assertIs(result, tui.Wizard.BACK) self.assertEqual(self._labels(captured["options"]), - ["Uninstall Backend"]) + ["Start/Stop Backend Servers", "Uninstall Backend"]) def test_convert_menu_builds_one_form_with_backend_field(self): captured = {} @@ -738,7 +743,7 @@ class SubmenuStatusTableTests(unittest.TestCase): patch.object(hub.shutil, "which", return_value="/x"): result = hub._Hub(None).screen_configure() self.assertIs(result, tui.Wizard.BACK) - self.assertEqual(captured["table_title"], "Backend Status") + self.assertIsNone(captured.get("table_title")) self.assertEqual( captured["table_rows"], [("qwen-tts", "installed", "ok", "body")]) @@ -767,7 +772,7 @@ class SubmenuStatusTableTests(unittest.TestCase): ["audio.cpp"]) # The running/stopped state lives in the status table above the # menu (not on the entries, whose colors the selection bar covers). - self.assertEqual(captured["table_title"], "Server Status") + self.assertIsNone(captured.get("table_title")) self.assertEqual(captured["table_rows"], [("audio.cpp", "stopped", "err", "body")]) @@ -3243,7 +3248,8 @@ class HubNavigationTests(unittest.TestCase): configured=True, servers=specs) registry = [self._info("qwen", "qwen-tts")] titles = [] - script = ["server", specs[0], tui.Wizard.BACK, tui.Wizard.BACK] + script = ["configure_backends", "server", specs[0], + tui.Wizard.BACK, tui.Wizard.BACK, tui.Wizard.BACK] def menu(stdscr, title, options, **kwargs): titles.append(title) @@ -3260,11 +3266,13 @@ class HubNavigationTests(unittest.TestCase): return_value=None): hub._Hub(None).run() # Selecting a server toggles it directly (no action sub-menu), then - # Esc steps back one screen at a time to the server list and main. + # Esc steps back one screen at a time: server list → Configure + # Backends → main menu. self.assertEqual( titles, - ["tts-audiobook-generator", "Start / Stop A Server", - "Start / Stop A Server", "tts-audiobook-generator"]) + ["tts-audiobook-generator", "Configure Backends", + "Start / Stop A Server", "Start / Stop A Server", + "Configure Backends", "tts-audiobook-generator"]) def test_esc_on_main_menu_quits(self): titles = self._drive([tui.Wizard.BACK], [], []) -- cgit v1.2.3