aboutsummaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/test_converter.py90
1 files changed, 89 insertions, 1 deletions
diff --git a/tests/test_converter.py b/tests/test_converter.py
index 707688e..a79ae58 100644
--- a/tests/test_converter.py
+++ b/tests/test_converter.py
@@ -1,12 +1,18 @@
"""Tests for the audiobook converter orchestration helpers."""
+import logging
import tempfile
import unittest
from pathlib import Path
from unittest.mock import MagicMock, patch
from converter import config
-from converter.converter import AudiobookConverter, find_existing_outputs, prompt_overwrite
+from converter.converter import (
+ AudiobookConverter,
+ find_existing_outputs,
+ prompt_overwrite,
+ setup_logging,
+)
class SanitizeFilenameTests(unittest.TestCase):
@@ -248,6 +254,88 @@ class DebugDumpTests(unittest.TestCase):
self.assertTrue(AudiobookConverter(debug=True).debug)
+class SetupLoggingTests(unittest.TestCase):
+ """Console handler stays quiet; the log file keeps the full record."""
+
+ def setUp(self):
+ self._tmp = tempfile.TemporaryDirectory()
+ self._logs_folder = patch.object(config, "LOGS_FOLDER", Path(self._tmp.name))
+ self._logs_folder.start()
+ self._root = logging.getLogger()
+ self._saved_handlers = self._root.handlers[:]
+ self._saved_level = self._root.level
+ self._saved_converter_level = logging.getLogger("converter").level
+ self._root.handlers.clear()
+
+ def tearDown(self):
+ for handler in self._root.handlers:
+ if handler not in self._saved_handlers:
+ handler.close()
+ self._root.handlers[:] = self._saved_handlers
+ self._root.setLevel(self._saved_level)
+ logging.getLogger("converter").setLevel(self._saved_converter_level)
+ self._logs_folder.stop()
+ self._tmp.cleanup()
+
+ def _console_handler(self):
+ matches = [h for h in logging.getLogger().handlers
+ if isinstance(h, logging.StreamHandler)
+ and not isinstance(h, logging.FileHandler)]
+ self.assertEqual(len(matches), 1)
+ return matches[0]
+
+ def _file_handler(self):
+ matches = [h for h in logging.getLogger().handlers
+ if isinstance(h, logging.FileHandler)]
+ self.assertEqual(len(matches), 1)
+ return matches[0]
+
+ def test_console_quiet_and_file_verbose_by_default(self):
+ setup_logging()
+ self.assertEqual(self._console_handler().level, logging.WARNING)
+ self.assertEqual(self._file_handler().level, logging.INFO)
+
+ def test_debug_flag_lowers_both_handlers(self):
+ setup_logging(debug=True)
+ self.assertEqual(self._console_handler().level, logging.DEBUG)
+ self.assertEqual(self._file_handler().level, logging.DEBUG)
+
+ def test_http_logs_filtered_from_console_only(self):
+ setup_logging(debug=True)
+ console = self._console_handler()
+ http_record = logging.LogRecord("httpx", logging.INFO, "httpx", 1,
+ "HTTP Request: GET ...", None, None)
+ self.assertFalse(console.filter(http_record))
+ chunk_record = logging.LogRecord("converter.converter", logging.DEBUG,
+ "converter", 1,
+ "Chunk 1/1 request text", None, None)
+ self.assertTrue(console.filter(chunk_record))
+
+
+class SynthesizeChunkLoggingTests(unittest.TestCase):
+ """Chunk failures surface as a single ERROR record (no print echo)."""
+
+ def setUp(self):
+ self.converter = AudiobookConverter.__new__(AudiobookConverter)
+ self.converter.tts = MagicMock()
+
+ def test_failed_chunk_logs_single_error(self):
+ self.converter.tts.process_chunk_with_retry.return_value = None
+ with self.assertLogs("converter.converter", level="ERROR") as logs:
+ results = self.converter._synthesize_chunks(["Hello."])
+ self.assertEqual(results, {1: None})
+ self.assertEqual(len(logs.output), 1)
+ self.assertIn("Chunk 1/1 failed", logs.output[0])
+
+ def test_raising_chunk_logs_single_error(self):
+ self.converter.tts.process_chunk_with_retry.side_effect = RuntimeError("boom")
+ with self.assertLogs("converter.converter", level="ERROR") as logs:
+ results = self.converter._synthesize_chunks(["Hello."])
+ self.assertEqual(results, {1: None})
+ self.assertEqual(len(logs.output), 1)
+ self.assertIn("Chunk 1/1 error: boom", logs.output[0])
+
+
class PromptOverwriteTests(unittest.TestCase):
def test_single_file_yes(self):
with patch("builtins.input", return_value="y"):