aboutsummaryrefslogtreecommitdiff
path: root/app/tests/test_extractors.py
diff options
context:
space:
mode:
Diffstat (limited to 'app/tests/test_extractors.py')
-rw-r--r--app/tests/test_extractors.py80
1 files changed, 80 insertions, 0 deletions
diff --git a/app/tests/test_extractors.py b/app/tests/test_extractors.py
index 64666ba..619fe5a 100644
--- a/app/tests/test_extractors.py
+++ b/app/tests/test_extractors.py
@@ -53,6 +53,86 @@ class TxtExtractionTests(unittest.TestCase):
with self.assertRaises(ValueError):
extract_text(path)
+ def test_utf16_without_bom_detected(self):
+ self.assertEqual(self._extract("chapter one".encode("utf-16-le")),
+ "chapter one")
+
+ def test_lone_nul_does_not_flip_to_utf16(self):
+ # A single stray NUL byte in an otherwise-ASCII UTF-8 file must not
+ # switch the whole book to a UTF-16 decode (mojibake): the text
+ # comes back readable instead.
+ self.assertEqual(self._extract(b"hello world\x00rest"),
+ "hello world\x00rest")
+
+ def test_standalone_page_numbers_removed_but_years_kept(self):
+ from converter.extractors import clean_text
+
+ cleaned = clean_text("Chapter 1\n\n42\n\nIt was 1984.")
+ self.assertNotIn("42", cleaned)
+ self.assertIn("1984", cleaned)
+ kept = clean_text("It was the year\n\n1984\n\nwhen it began.")
+ self.assertIn("1984", kept)
+
+
+class EpubZipfileFallbackTests(unittest.TestCase):
+ """The no-ebooklib EPUB fallback: spine order, no TOC narration."""
+
+ @staticmethod
+ def _write_epub(path: Path):
+ import 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=\"c2\" href=\"text/chapterB.xhtml\"/>"
+ "<item id=\"c1\" href=\"text/chapterA.xhtml\"/>"
+ "</manifest>"
+ "<spine><itemref idref=\"nav\"/>"
+ "<itemref idref=\"c2\"/><itemref idref=\"c1\"/></spine>"
+ "</package>")
+ 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>")
+
+ def test_spine_order_and_no_nav(self):
+ from converter.extractors import _read_epub_zipfile
+
+ with tempfile.TemporaryDirectory() as tmp:
+ path = Path(tmp) / "book.epub"
+ self._write_epub(path)
+ items = _read_epub_zipfile(path)
+
+ titles = [title for title, _ in items]
+ self.assertNotIn("nav", titles)
+ # Spine order (B before A) beats filename sort (A before B).
+ self.assertEqual(titles, ["chapterB", "chapterA"])
+
+ def test_unparsable_opf_falls_back_to_filename_order(self):
+ import zipfile
+
+ from converter.extractors import _read_epub_zipfile
+
+ with tempfile.TemporaryDirectory() as tmp:
+ path = Path(tmp) / "book.epub"
+ with zipfile.ZipFile(path, "w") as zf:
+ zf.writestr("a.xhtml", "<html><body><p>A</p></body></html>")
+ zf.writestr("b.xhtml", "<html><body><p>B</p></body></html>")
+ items = _read_epub_zipfile(path)
+
+ self.assertEqual([title for title, _ in items], ["a", "b"])
+
def _build_test_epub(path: Path, chapters=(("One", "First chapter text."),
("Two", "Second chapter text."))) -> None: