aboutsummaryrefslogtreecommitdiff
path: root/app/tests
diff options
context:
space:
mode:
authorhistoria <historiavg@proton.me>2026-08-25 19:15:43 -0400
committerhistoria <historiavg@proton.me>2026-08-25 19:15:43 -0400
commit757588321d4c27889be6e8e3c12b75873ad1218d (patch)
treef5550ef214a1939ba5844b7d91abdabb4171a48f /app/tests
parent8c9a782dfe94525dc5f0893c98fd19543648264b (diff)
downloadtts-audiobook-generator-757588321d4c27889be6e8e3c12b75873ad1218d.tar.gz
fix: book generation logging
Diffstat (limited to 'app/tests')
-rw-r--r--app/tests/test_hub.py27
-rw-r--r--app/tests/test_runview.py49
2 files changed, 76 insertions, 0 deletions
diff --git a/app/tests/test_hub.py b/app/tests/test_hub.py
index 3feaa05..3547ba8 100644
--- a/app/tests/test_hub.py
+++ b/app/tests/test_hub.py
@@ -1246,6 +1246,33 @@ class PrepareRunConfigTests(unittest.TestCase):
self.assertEqual(cfg.server_url, spec.url)
self.assertIsNone(cfg.autostart_spec)
+ def test_book_files_travel_on_the_config_not_the_kwargs(self):
+ # _preflight stashes the plan in the form kwargs; keeping it there
+ # collided with convert()'s named book_files/planned parameters.
+ spec = self._spec()
+ status = BackendStatus("qwen", "qwen-tts", installed=True,
+ configured=True, servers=[spec])
+ kwargs = {"book_files": ["b.txt"], "planned": ["b.txt"]}
+ with tempfile.TemporaryDirectory() as tmp, \
+ patch.object(hub, "LOGS_FOLDER", Path(tmp)), \
+ patch.object(hub, "detect_all", return_value=[status]), \
+ patch("backends.common.server_running", return_value=False):
+ cfg = hub._prepare_run_config("qwen", kwargs)
+ self.assertNotIn("book_files", kwargs)
+ self.assertNotIn("planned", kwargs)
+ self.assertEqual(cfg.book_files, ["b.txt"])
+ self.assertEqual(cfg.planned, ["b.txt"])
+
+ def test_the_dated_log_file_exists_once_a_run_is_prepared(self):
+ # The run view advertises this file on failures; create it up front
+ # so a crash before setup_logging still points somewhere real.
+ with tempfile.TemporaryDirectory() as tmp:
+ with patch.object(hub, "LOGS_FOLDER", Path(tmp)), \
+ patch.object(hub, "detect_all", return_value=[]):
+ cfg = hub._prepare_run_config(
+ "audiocpp", {"api_url": "http://10.0.0.5:8080"})
+ self.assertTrue(Path(cfg.log_path).exists())
+
class PreflightTests(unittest.TestCase):
"""_preflight: overwrite prompts run in the TUI, plan stashed in kwargs."""
diff --git a/app/tests/test_runview.py b/app/tests/test_runview.py
index 7006427..556cb52 100644
--- a/app/tests/test_runview.py
+++ b/app/tests/test_runview.py
@@ -7,8 +7,11 @@ own queue to exercise state transitions, rendering, and the Esc/q
cancel → stop-server flow.
"""
+import os
import sys
+import tempfile
import unittest
+from queue import Empty
from unittest.mock import patch
from tests.test_tui import FakeCurses, FakeScreen
@@ -234,5 +237,51 @@ class RunLoopTests(_FakeTui, unittest.TestCase):
mk_stop.assert_not_called()
+class WorkerTests(_FakeTui, unittest.TestCase):
+ """The worker thread's handoff into audiobook.convert."""
+
+ def make_view(self, **cfg):
+ screen = FakeScreen()
+ return runview.RunView(screen, _config(**cfg), clock=lambda: 1000.0)
+
+ def _drain(self, view):
+ events = []
+ while True:
+ try:
+ events.append(view._queue.get_nowait())
+ except Empty:
+ return events
+
+ def test_book_files_reach_convert_once(self):
+ # Regression: _preflight stashes book_files/planned in the form
+ # kwargs and RunConfig carries them as fields too; passing both to
+ # convert() raised "got multiple values for keyword argument".
+ view = self.make_view(
+ kwargs={"voice": "alloy", "book_files": ["b.txt"],
+ "planned": ["b.txt"]},
+ book_files=["b.txt"], planned=["b.txt"])
+ with patch("audiobook.convert") as mk_convert:
+ view._worker_main()
+ _, kw = mk_convert.call_args
+ self.assertEqual(kw["book_files"], ["b.txt"])
+ self.assertEqual(kw["planned"], ["b.txt"])
+ self.assertEqual(kw["voice"], "alloy")
+ self.assertNotIn("error", [e["kind"] for e in self._drain(view)])
+
+ def test_worker_error_is_appended_to_the_dated_log(self):
+ # A crash before convert() configures logging must still leave its
+ # trace in the file the failure screen points at.
+ with tempfile.TemporaryDirectory() as tmp:
+ log_path = os.path.join(tmp, "audiobook_20260825.log")
+ view = self.make_view(log_path=log_path)
+ with patch("audiobook.convert",
+ side_effect=TypeError("boom")):
+ view._worker_main()
+ with open(log_path, encoding="utf-8") as handle:
+ text = handle.read()
+ self.assertIn("ERROR - boom", text)
+ self.assertIn("error", [e["kind"] for e in self._drain(view)])
+
+
if __name__ == "__main__":
unittest.main()