diff options
| author | historia <historiavg@proton.me> | 2026-09-02 17:16:05 -0400 |
|---|---|---|
| committer | historia <historiavg@proton.me> | 2026-09-02 17:16:05 -0400 |
| commit | 717a3dc112519ae7cf39a212a4e3ec9e1ba17f28 (patch) | |
| tree | 119ad113ddce14414ae3293316cf7ed8c57cd5d3 /app/tests/test_hub.py | |
| parent | 8579517a35ef1865fc9b428899d73d52dcb27a14 (diff) | |
| download | tts-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.py | 159 |
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 = {} |
