aboutsummaryrefslogtreecommitdiff
path: root/app/tests/test_audiobook_cli.py
diff options
context:
space:
mode:
authorhistoria <historiavg@proton.me>2026-08-30 18:49:32 -0400
committerhistoria <historiavg@proton.me>2026-08-30 18:49:32 -0400
commit93f106aac2d6411c80a911adac62cd12f80e58be (patch)
tree5db8a2ff27f0d06ef313b3cf9ab84bf02f74479f /app/tests/test_audiobook_cli.py
parentd31e9149c7f141b2d3c14a79e9f067d524c8c420 (diff)
downloadtts-audiobook-generator-93f106aac2d6411c80a911adac62cd12f80e58be.tar.gz
feat: output log path when audiobook generation fails
Diffstat (limited to 'app/tests/test_audiobook_cli.py')
-rw-r--r--app/tests/test_audiobook_cli.py34
1 files changed, 32 insertions, 2 deletions
diff --git a/app/tests/test_audiobook_cli.py b/app/tests/test_audiobook_cli.py
index f13cf9e..a3f0b90 100644
--- a/app/tests/test_audiobook_cli.py
+++ b/app/tests/test_audiobook_cli.py
@@ -26,6 +26,7 @@ if str(REPO_ROOT) not in sys.path:
sys.path.insert(0, str(REPO_ROOT))
import audiobook # noqa: E402
+import logging_kit # noqa: E402
from converter import config # noqa: E402
from converter import converter as converter_mod # noqa: E402
from converter.converter import AudiobookConverter # noqa: E402
@@ -565,6 +566,10 @@ class FatalErrorReportingTests(unittest.TestCase):
(log_path,) = self.log_dir.glob("audiobook_*.log")
return log_path.read_text(encoding="utf-8")
+ def _log_path(self) -> str:
+ """The dated log file's full path, as the console reports it."""
+ return str(logging_kit.stream_path("audiobook", self.log_dir))
+
def test_expected_failure_shows_one_friendly_line(self):
message = ("The audio.cpp model 'Qwen3-TTS-12Hz-1.7B-Base-GGUF' "
"(family 'qwen3_tts') has no built-in speakers "
@@ -573,6 +578,8 @@ class FatalErrorReportingTests(unittest.TestCase):
self.assertEqual(code, 1)
self.assertEqual(out.count("[FATAL]"), 1)
self.assertIn(f"[FATAL] Fatal error: {message}", out)
+ self.assertIn(f"[INFO] Full details in the log file: "
+ f"{self._log_path()}", out)
self.assertNotIn("Traceback (most recent call last)", out)
self.assertNotIn("Traceback (most recent call last)", err)
log_text = self._log_text()
@@ -581,11 +588,14 @@ class FatalErrorReportingTests(unittest.TestCase):
def test_expected_failure_reports_error_event(self):
events = []
- code, _, _ = self._convert(ValueError("bad input"),
- progress=events.append)
+ code, out, _ = self._convert(ValueError("bad input"),
+ progress=events.append)
self.assertEqual(code, 1)
self.assertEqual(events,
[{"kind": "error", "message": "bad input"}])
+ # The TUI run view owns the console and points failures at the log
+ # itself; no console pointer on this path.
+ self.assertNotIn("Full details in the log file", out)
def test_unexpected_crash_also_shows_traceback(self):
code, out, err = self._convert(TypeError("boom"))
@@ -594,6 +604,26 @@ class FatalErrorReportingTests(unittest.TestCase):
self.assertIn("Traceback (most recent call last)", err)
self.assertIn("TypeError: boom", err)
self.assertIn("Traceback (most recent call last)", self._log_text())
+ self.assertIn(f"[INFO] Full details in the log file: "
+ f"{self._log_path()}", out)
+
+ def test_failed_run_prints_the_log_path(self):
+ # A run that fails without raising (a book aborted the rest) ends
+ # with the log file path too.
+ preflight = MagicMock(
+ return_value=([self.book], [(self.book, "dune")]))
+ fake_class = MagicMock()
+ fake_class.preflight_overwrites = preflight
+ fake_class.return_value.run.return_value = False
+ out, err = io.StringIO(), io.StringIO()
+ with patch.object(audiobook, "setup_directories"), \
+ contextlib.redirect_stdout(out), \
+ contextlib.redirect_stderr(err), \
+ patch.object(audiobook, "AudiobookConverter", fake_class):
+ code = audiobook.convert(backend="audiocpp")
+ self.assertEqual(code, 1)
+ self.assertIn(f"[INFO] Full details in the log file: "
+ f"{self._log_path()}", out.getvalue())
if __name__ == "__main__":