aboutsummaryrefslogtreecommitdiff
path: root/app/tests/test_extractors.py
diff options
context:
space:
mode:
authorhistoria <historiavg@proton.me>2026-09-10 00:16:00 -0400
committerhistoria <historiavg@proton.me>2026-09-10 00:16:00 -0400
commite2da233cd1baa859a5721542fc8b80d9f3f880e7 (patch)
tree4e86797cbd7dce0434feef65bc0ad2e97bfbe4c0 /app/tests/test_extractors.py
parent31459b281b6a5368c692b3c42c91e522995ebd57 (diff)
downloadtts-audiobook-generator-e2da233cd1baa859a5721542fc8b80d9f3f880e7.tar.gz
fix: smart chunking, extraction, and process-safety issuesHEADmain
Diffstat (limited to 'app/tests/test_extractors.py')
-rw-r--r--app/tests/test_extractors.py60
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&#37;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):