aboutsummaryrefslogtreecommitdiff
path: root/app/tests/test_hub.py
diff options
context:
space:
mode:
authorhistoria <historiavg@proton.me>2026-09-02 17:16:05 -0400
committerhistoria <historiavg@proton.me>2026-09-02 17:16:05 -0400
commit717a3dc112519ae7cf39a212a4e3ec9e1ba17f28 (patch)
tree119ad113ddce14414ae3293316cf7ed8c57cd5d3 /app/tests/test_hub.py
parent8579517a35ef1865fc9b428899d73d52dcb27a14 (diff)
downloadtts-audiobook-generator-717a3dc112519ae7cf39a212a4e3ec9e1ba17f28.tar.gz
feat: auto-update via settings
Diffstat (limited to 'app/tests/test_hub.py')
-rw-r--r--app/tests/test_hub.py159
1 files changed, 159 insertions, 0 deletions
diff --git a/app/tests/test_hub.py b/app/tests/test_hub.py
index 485a37f..228a103 100644
--- a/app/tests/test_hub.py
+++ b/app/tests/test_hub.py
@@ -394,6 +394,14 @@ class SubmenuStatusTableTests(unittest.TestCase):
servers, showing running/stopped inline instead of the table.
"""
+ def setUp(self):
+ # These tests concern the backend actions; the host's git-checkout
+ # state (which gates the "Update Generator" entry) must not leak in.
+ patcher = patch.object(hub.selfupdate, "is_git_checkout",
+ return_value=False)
+ patcher.start()
+ self.addCleanup(patcher.stop)
+
def _capture_menu(self, captured):
def fake_menu(stdscr, title, options, **kwargs):
captured["title"] = title
@@ -4102,6 +4110,157 @@ class ConfigureBackendsDispatchTests(unittest.TestCase):
# freshly detected status table.
self.assertEqual(titles, ["Configure Backends", "Configure Backends"])
+ def test_configure_menu_offers_update_generator_for_git_checkouts(self):
+ def options_for(git_checkout):
+ captured = {}
+
+ def fake_menu(stdscr, title, options, **kwargs):
+ captured["options"] = options
+ return tui.Wizard.BACK
+
+ info = BackendInfo("qwen", "qwen-tts", lambda: None, lambda: 0)
+ statuses = [BackendStatus("qwen", "qwen-tts", installed=True,
+ configured=True)]
+ with patch.object(hub.tui, "menu", fake_menu), \
+ patch.object(hub, "detect_all", return_value=statuses), \
+ patch.object(hub, "REGISTRY", [info]), \
+ patch.object(hub.selfupdate, "is_git_checkout",
+ return_value=git_checkout):
+ hub._Hub(None).screen_configure()
+ return [opt[0] for opt in captured["options"]]
+
+ self.assertIn("Update Generator", options_for(True))
+ self.assertNotIn("Update Generator", options_for(False))
+
+ def test_selecting_update_generator_runs_the_action_and_reshows(self):
+ titles = []
+ ran = []
+
+ def fake_menu(stdscr, title, options, **kwargs):
+ titles.append(title)
+ return "update_self" if len(titles) == 1 else tui.Wizard.BACK
+
+ info = BackendInfo("qwen", "qwen-tts", lambda: None, lambda: 0)
+ statuses = [BackendStatus("qwen", "qwen-tts", installed=True,
+ configured=True)]
+ with patch.object(hub.tui, "menu", fake_menu), \
+ patch.object(hub, "detect_all", return_value=statuses), \
+ patch.object(hub, "REGISTRY", [info]), \
+ patch.object(hub, "_update_self_action",
+ side_effect=lambda scr: ran.append(True)):
+ result = hub._Hub(None).screen_configure()
+ self.assertIs(result, tui.Wizard.BACK)
+ self.assertEqual(ran, [True])
+ # An inline action: the same menu re-shows after the update.
+ self.assertEqual(titles, ["Configure Backends", "Configure Backends"])
+
+ def test_update_self_action_flashes_err_without_git_checkout(self):
+ patch_flash, flashes = self._capture_flashes()
+ with patch.object(hub.selfupdate, "is_git_checkout",
+ return_value=False), \
+ patch.object(hub.taskview, "run_steps") as mk_run, \
+ patch_flash:
+ hub._update_self_action(None)
+ mk_run.assert_not_called()
+ self.assertEqual(len(flashes), 1)
+ self.assertEqual(flashes[0][1], "err")
+ self.assertIn("not a git checkout", flashes[0][0])
+
+ def test_update_self_action_declined_confirm_aborts_before_running(self):
+ patch_flash, flashes = self._capture_flashes()
+ with patch.object(hub.selfupdate, "is_git_checkout",
+ return_value=True), \
+ patch.object(hub.selfupdate, "current_commit",
+ return_value="aaa"), \
+ patch.object(hub.selfupdate, "modified_tracked_files",
+ return_value=["app/converter/config.py"]), \
+ patch.object(hub.tui, "confirm",
+ return_value=False) as mk_confirm, \
+ patch.object(hub.taskview, "run_steps") as mk_run, \
+ patch_flash:
+ hub._update_self_action(None)
+ mk_run.assert_not_called()
+ self.assertEqual(flashes, [])
+ # The confirm names the files that will be preserved.
+ body = mk_confirm.call_args[1]["body"]
+ self.assertIn("app/converter/config.py", body)
+
+ def test_update_self_action_runs_step_and_flashes_update(self):
+ patch_flash, flashes = self._capture_flashes()
+ with patch.object(hub.selfupdate, "is_git_checkout",
+ return_value=True), \
+ patch.object(hub.selfupdate, "current_commit",
+ side_effect=["aaa", "bbb"]), \
+ patch.object(hub.selfupdate, "modified_tracked_files",
+ return_value=[]), \
+ patch.object(hub.taskview, "run_steps",
+ return_value=0) as mk_run, \
+ patch.object(hub.selfupdate, "update_generator",
+ return_value=0) as mk_update, \
+ patch_flash:
+ hub._update_self_action(None)
+ mk_run.assert_called_once()
+ self.assertEqual(mk_run.call_args[0][0], None)
+ self.assertEqual(mk_run.call_args[0][1], "Update Generator")
+ steps = mk_run.call_args[0][2]
+ self.assertEqual([step.title for step in steps],
+ ["Fetch and reset the checkout"])
+
+ def emit(line):
+ pass
+
+ steps[0].work(emit, "CANCEL")
+ mk_update.assert_called_once_with(emit=emit, cancel="CANCEL")
+ self.assertEqual(len(flashes), 1)
+ text, kind = flashes[0]
+ self.assertEqual(kind, "ok")
+ self.assertIn("aaa", text)
+ self.assertIn("bbb", text)
+ self.assertIn("Restart to apply", text)
+
+ def test_update_self_action_flashes_already_up_to_date(self):
+ patch_flash, flashes = self._capture_flashes()
+ with patch.object(hub.selfupdate, "is_git_checkout",
+ return_value=True), \
+ patch.object(hub.selfupdate, "current_commit",
+ side_effect=["aaa", "aaa"]), \
+ patch.object(hub.selfupdate, "modified_tracked_files",
+ return_value=[]), \
+ patch.object(hub.taskview, "run_steps", return_value=0), \
+ patch_flash:
+ hub._update_self_action(None)
+ self.assertEqual(flashes,
+ [("The generator is already up to date (aaa).",
+ "ok")])
+
+ def test_update_self_action_flashes_warn_when_cancelled(self):
+ patch_flash, flashes = self._capture_flashes()
+ with patch.object(hub.selfupdate, "is_git_checkout",
+ return_value=True), \
+ patch.object(hub.selfupdate, "current_commit",
+ side_effect=["aaa", "aaa"]), \
+ patch.object(hub.selfupdate, "modified_tracked_files",
+ return_value=[]), \
+ patch.object(hub.taskview, "run_steps", return_value=130), \
+ patch_flash:
+ hub._update_self_action(None)
+ self.assertEqual(flashes[-1][1], "warn")
+ self.assertIn("cancelled", flashes[-1][0])
+
+ def test_update_self_action_flashes_err_when_failed(self):
+ patch_flash, flashes = self._capture_flashes()
+ with patch.object(hub.selfupdate, "is_git_checkout",
+ return_value=True), \
+ patch.object(hub.selfupdate, "current_commit",
+ side_effect=["aaa", "aaa"]), \
+ patch.object(hub.selfupdate, "modified_tracked_files",
+ return_value=[]), \
+ patch.object(hub.taskview, "run_steps", return_value=1), \
+ patch_flash:
+ hub._update_self_action(None)
+ self.assertEqual(flashes[-1][1], "err")
+ self.assertIn("did not complete", flashes[-1][0])
+
def test_pick_backend_install_lists_uninstalled_only(self):
captured = {}