diff options
| author | historia <historiavg@proton.me> | 2026-09-10 00:16:00 -0400 |
|---|---|---|
| committer | historia <historiavg@proton.me> | 2026-09-10 00:16:00 -0400 |
| commit | e2da233cd1baa859a5721542fc8b80d9f3f880e7 (patch) | |
| tree | 4e86797cbd7dce0434feef65bc0ad2e97bfbe4c0 /app/tests/test_extractors.py | |
| parent | 31459b281b6a5368c692b3c42c91e522995ebd57 (diff) | |
| download | tts-audiobook-generator-e2da233cd1baa859a5721542fc8b80d9f3f880e7.tar.gz | |
Diffstat (limited to 'app/tests/test_extractors.py')
| -rw-r--r-- | app/tests/test_extractors.py | 60 |
1 files changed, 55 insertions, 5 deletions
diff --git a/app/tests/test_extractors.py b/app/tests/test_extractors.py index 619fe5a..b7f14d9 100644 --- a/app/tests/test_extractors.py +++ b/app/tests/test_extractors.py @@ -133,6 +133,51 @@ class EpubZipfileFallbackTests(unittest.TestCase): self.assertEqual([title for title, _ in items], ["a", "b"]) + def test_spine_hrefs_are_normalized_before_matching(self): + # Hrefs are URL-encoded, entity-escaped and relative (possibly + # with ./ or ../ segments): every equivalent spelling must + # resolve to its archived chapter, or the chapter silently + # disappears from the book. + import zipfile + + from converter.extractors import _read_epub_zipfile + + container = ("<?xml version=\"1.0\"?>" + "<container><rootfiles>" + "<rootfile full-path=\"OEBPS/content.opf\"/>" + "</rootfiles></container>") + opf = ("<?xml version=\"1.0\"?>" + "<package xmlns=\"http://www.idpf.org/2007/opf\">" + "<manifest>" + "<item id=\"nav\" href=\"nav.xhtml\" properties=\"nav\"/>" + "<item id=\"c1\" href=\"./text/chapterA.xhtml\"/>" + "<item id=\"c2\" href=\"../OEBPS/text/chapterB.xhtml\"/>" + "<item id=\"c3\" href=\"text/chapter%20C.xhtml\"/>" + "</manifest>" + "<spine><itemref idref=\"nav\"/>" + "<itemref idref=\"c1\"/><itemref idref=\"c2\"/>" + "<itemref idref=\"c3\"/></spine>" + "</package>") + with tempfile.TemporaryDirectory() as tmp: + path = Path(tmp) / "book.epub" + with zipfile.ZipFile(path, "w") as zf: + zf.writestr("mimetype", "application/epub+zip") + zf.writestr("META-INF/container.xml", container) + zf.writestr("OEBPS/content.opf", opf) + zf.writestr("OEBPS/nav.xhtml", + "<html><body><p>Contents</p></body></html>") + zf.writestr("OEBPS/text/chapterA.xhtml", + "<html><body><p>Alpha text.</p></body></html>") + zf.writestr("OEBPS/text/chapterB.xhtml", + "<html><body><p>Beta text.</p></body></html>") + zf.writestr("OEBPS/text/chapter%20C.xhtml", + "<html><body><p>Gamma text.</p></body></html>") + items = _read_epub_zipfile(path) + + titles = [title for title, _ in items] + self.assertNotIn("nav", titles) + self.assertEqual(titles, ["chapterA", "chapterB", "chapter%20C"]) + def _build_test_epub(path: Path, chapters=(("One", "First chapter text."), ("Two", "Second chapter text."))) -> None: @@ -183,15 +228,20 @@ class EpubExtractionTests(unittest.TestCase): @requires_epub def test_epub_extraction_follows_spine_order(self): + # extract_text() only handles TXT and PDF; EPUB books (with their + # per-chapter structure) come through extract_sections(). + from converter.extractors import extract_sections + with tempfile.TemporaryDirectory() as tmp: path = Path(tmp) / "book.epub" _build_test_epub(path) - text = extract_text(path) + sections = extract_sections(path) - self.assertIn("First chapter text.", text) - self.assertIn("Second chapter text.", text) - self.assertLess(text.index("First chapter text."), - text.index("Second chapter text.")) + texts = [section.text for section in sections] + self.assertIn("First chapter text.", " ".join(texts)) + self.assertIn("Second chapter text.", " ".join(texts)) + self.assertLess(" ".join(texts).index("First chapter text."), + " ".join(texts).index("Second chapter text.")) class ExtractSectionsTests(unittest.TestCase): |
