mirror of
https://github.com/denizsafak/abogen.git
synced 2026-07-18 21:50:28 +02:00
- Extract slugify, sanitize_output_stem, output_timestamp_token, build_output_path - Extract apply_newline_policy, resolve_output_directory, resolve_project_layout - _prepare_output_dir and _prepare_project_layout become thin wrappers with mkdir - Add tests/test_output_paths.py (21 tests) - conversion_runner.py: 1443 → 1410 lines - All tests pass
258 lines
8.2 KiB
Python
258 lines
8.2 KiB
Python
"""Tests for output path utilities.
|
|
|
|
Tests import from domain/output_paths.py (new module).
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import re
|
|
from datetime import datetime
|
|
from pathlib import Path
|
|
from types import SimpleNamespace
|
|
|
|
import pytest
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# slugify
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
class TestSlugify:
|
|
"""slugify converts title to filesystem-safe slug."""
|
|
|
|
def test_basic_slug(self):
|
|
from abogen.domain.output_paths import slugify
|
|
|
|
assert slugify("Hello World", 0) == "hello_world"
|
|
|
|
def test_strips_special_chars(self):
|
|
from abogen.domain.output_paths import slugify
|
|
|
|
result = slugify("Chapter 1: The Beginning!", 0)
|
|
assert result == "chapter_1_the_beginning"
|
|
|
|
def test_empty_title_uses_index(self):
|
|
from abogen.domain.output_paths import slugify
|
|
|
|
assert slugify("", 5) == "chapter_05"
|
|
|
|
def test_truncated_at_80(self):
|
|
from abogen.domain.output_paths import slugify
|
|
|
|
long_title = "a" * 100
|
|
assert len(slugify(long_title, 0)) == 80
|
|
|
|
def test_preserves_hyphens(self):
|
|
from abogen.domain.output_paths import slugify
|
|
|
|
assert slugify("mid-night", 0) == "mid-night"
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# sanitize_output_stem
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
class TestSanitizeOutputStem:
|
|
"""sanitize_output_stem cleans filename stem."""
|
|
|
|
def test_basic_sanitize(self):
|
|
from abogen.domain.output_paths import sanitize_output_stem
|
|
|
|
assert sanitize_output_stem("my file.mp3") == "my_file"
|
|
|
|
def test_empty_returns_default(self):
|
|
from abogen.domain.output_paths import sanitize_output_stem
|
|
|
|
assert sanitize_output_stem("") == "output"
|
|
|
|
def test_strips_underscores(self):
|
|
from abogen.domain.output_paths import sanitize_output_stem
|
|
|
|
assert sanitize_output_stem("__test__") == "test"
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# output_timestamp_token
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
class TestOutputTimestampToken:
|
|
"""output_timestamp_token returns timestamp string."""
|
|
|
|
def test_format(self):
|
|
from abogen.domain.output_paths import output_timestamp_token
|
|
|
|
token = output_timestamp_token()
|
|
assert re.match(r"\d{8}-\d{6}", token)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# build_output_path
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
class TestBuildOutputPath:
|
|
"""build_output_path builds the output file path."""
|
|
|
|
def test_basic_path(self, tmp_path):
|
|
from abogen.domain.output_paths import build_output_path
|
|
|
|
result = build_output_path(tmp_path, "test.mp3", "mp3")
|
|
assert result.suffix == ".mp3"
|
|
assert result.parent == tmp_path
|
|
|
|
def test_stem_sanitized(self, tmp_path):
|
|
from abogen.domain.output_paths import build_output_path
|
|
|
|
result = build_output_path(tmp_path, "my file.txt", "wav")
|
|
assert "my_file" in result.name
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# apply_newline_policy
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
class TestApplyNewlinePolicy:
|
|
"""apply_newline_policy replaces single newlines in chapter text."""
|
|
|
|
def test_noop_when_disabled(self):
|
|
from abogen.domain.output_paths import apply_newline_policy
|
|
|
|
from abogen.text_extractor import ExtractedChapter
|
|
chapters = [ExtractedChapter(title="t", text="a\nb")]
|
|
apply_newline_policy(chapters, False)
|
|
assert chapters[0].text == "a\nb"
|
|
|
|
def test_replaces_single_newlines(self):
|
|
from abogen.domain.output_paths import apply_newline_policy
|
|
|
|
from abogen.text_extractor import ExtractedChapter
|
|
chapters = [ExtractedChapter(title="t", text="a\nb\nc")]
|
|
apply_newline_policy(chapters, True)
|
|
assert chapters[0].text == "a b c"
|
|
|
|
def test_preserves_double_newlines(self):
|
|
from abogen.domain.output_paths import apply_newline_policy
|
|
|
|
from abogen.text_extractor import ExtractedChapter
|
|
chapters = [ExtractedChapter(title="t", text="a\n\nb")]
|
|
apply_newline_policy(chapters, True)
|
|
assert chapters[0].text == "a\n\nb"
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# resolve_output_directory
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
class TestResolveOutputDirectory:
|
|
"""resolve_output_directory determines output dir from save_mode."""
|
|
|
|
def test_save_to_desktop(self, tmp_path):
|
|
from abogen.domain.output_paths import resolve_output_directory
|
|
|
|
result = resolve_output_directory(
|
|
save_mode="Save to Desktop",
|
|
stored_path=Path("/input/book.epub"),
|
|
output_folder=None,
|
|
desktop_dir=tmp_path,
|
|
user_output_path=None,
|
|
user_cache_outputs=None,
|
|
)
|
|
assert result == tmp_path
|
|
|
|
def test_save_next_to_input(self, tmp_path):
|
|
from abogen.domain.output_paths import resolve_output_directory
|
|
|
|
stored = tmp_path / "book.epub"
|
|
result = resolve_output_directory(
|
|
save_mode="Save next to input file",
|
|
stored_path=stored,
|
|
output_folder=None,
|
|
desktop_dir=None,
|
|
user_output_path=None,
|
|
user_cache_outputs=None,
|
|
)
|
|
assert result == tmp_path
|
|
|
|
def test_choose_output_folder(self, tmp_path):
|
|
from abogen.domain.output_paths import resolve_output_directory
|
|
|
|
custom = tmp_path / "custom"
|
|
result = resolve_output_directory(
|
|
save_mode="Choose output folder",
|
|
stored_path=Path("/x/y.epub"),
|
|
output_folder=str(custom),
|
|
desktop_dir=None,
|
|
user_output_path=None,
|
|
user_cache_outputs=None,
|
|
)
|
|
assert result == custom
|
|
|
|
def test_use_default_save_location(self, tmp_path):
|
|
from abogen.domain.output_paths import resolve_output_directory
|
|
|
|
result = resolve_output_directory(
|
|
save_mode="Use default save location",
|
|
stored_path=Path("/x/y.epub"),
|
|
output_folder=None,
|
|
desktop_dir=None,
|
|
user_output_path=tmp_path / "default",
|
|
user_cache_outputs=None,
|
|
)
|
|
assert result == tmp_path / "default"
|
|
|
|
def test_fallback_to_cache(self, tmp_path):
|
|
from abogen.domain.output_paths import resolve_output_directory
|
|
|
|
result = resolve_output_directory(
|
|
save_mode="unknown",
|
|
stored_path=Path("/x/y.epub"),
|
|
output_folder=None,
|
|
desktop_dir=None,
|
|
user_output_path=None,
|
|
user_cache_outputs=tmp_path / "cache",
|
|
)
|
|
assert result == tmp_path / "cache"
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# resolve_project_layout
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
class TestResolveProjectLayout:
|
|
"""resolve_project_layout computes project folder structure."""
|
|
|
|
def test_flat_layout(self, tmp_path):
|
|
from abogen.domain.output_paths import resolve_project_layout
|
|
|
|
root, audio, subs, meta = resolve_project_layout(
|
|
original_filename="book.epub",
|
|
save_as_project=False,
|
|
base_dir=tmp_path,
|
|
timestamp_fn=lambda: "20260101-000000",
|
|
sanitize_fn=lambda n, i: "book",
|
|
)
|
|
assert audio == root
|
|
assert subs == root
|
|
assert meta is None
|
|
|
|
def test_project_layout(self, tmp_path):
|
|
from abogen.domain.output_paths import resolve_project_layout
|
|
|
|
root, audio, subs, meta = resolve_project_layout(
|
|
original_filename="book.epub",
|
|
save_as_project=True,
|
|
base_dir=tmp_path,
|
|
timestamp_fn=lambda: "20260101-000000",
|
|
sanitize_fn=lambda n, i: "book",
|
|
)
|
|
assert root.name == "20260101-000000_book"
|
|
assert audio.name == "audio"
|
|
assert subs.name == "subtitles"
|
|
assert meta.name == "metadata"
|