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.py213
1 files changed, 213 insertions, 0 deletions
diff --git a/app/tests/test_extractors.py b/app/tests/test_extractors.py
new file mode 100644
index 0000000..ae1794c
--- /dev/null
+++ b/app/tests/test_extractors.py
@@ -0,0 +1,213 @@
+"""Tests for file text extraction."""
+
+import tempfile
+import unittest
+from pathlib import Path
+
+from converter.extractors import extract_text
+
+
+class TxtExtractionTests(unittest.TestCase):
+ def _extract(self, data: bytes) -> str:
+ with tempfile.TemporaryDirectory() as tmp:
+ path = Path(tmp) / "book.txt"
+ path.write_bytes(data)
+ return extract_text(path)
+
+ def test_utf8(self):
+ self.assertEqual(self._extract("héllo wörld".encode("utf-8")), "héllo wörld")
+
+ def test_utf16_with_bom(self):
+ self.assertEqual(self._extract("héllo".encode("utf-16")), "héllo")
+
+ def test_cp1252(self):
+ self.assertEqual(self._extract("“quotes”".encode("cp1252")), "“quotes”")
+
+ def test_latin1_fallback(self):
+ # 0x81 is undefined in cp1252, forcing the latin-1 catch-all
+ self.assertEqual(self._extract(b"caf\x81"), "caf\x81")
+
+ def test_unsupported_format(self):
+ with tempfile.TemporaryDirectory() as tmp:
+ path = Path(tmp) / "book.xyz"
+ path.write_bytes(b"data")
+ with self.assertRaises(ValueError):
+ extract_text(path)
+
+
+def _build_test_epub(path: Path, chapters=(("One", "First chapter text."),
+ ("Two", "Second chapter text."))) -> None:
+ from ebooklib import epub
+
+ book = epub.EpubBook()
+ book.set_identifier("test-id")
+ book.set_title("Test Book")
+ book.set_language("en")
+ book.add_author("Test Author")
+
+ items = []
+ for index, (title, text) in enumerate(chapters, 1):
+ chapter = epub.EpubHtml(title=title, file_name=f"chap{index}.xhtml", lang="en")
+ chapter.content = f"<html><body><p>{text}</p></body></html>"
+ book.add_item(chapter)
+ items.append(chapter)
+
+ book.toc = tuple(items)
+ book.spine = ["nav", *items]
+ book.add_item(epub.EpubNcx())
+ book.add_item(epub.EpubNav())
+
+ epub.write_epub(str(path), book)
+
+
+class EpubExtractionTests(unittest.TestCase):
+ def setUp(self):
+ try:
+ import ebooklib # noqa: F401
+ except ImportError:
+ self.skipTest("ebooklib not installed")
+
+ def test_ebooklib_extraction(self):
+ # Regression test: the ebooklib path used to silently return "" due to
+ # isinstance(item, ebooklib.ITEM_DOCUMENT) (an int, not a class).
+ from converter.extractors import _read_epub_ebooklib
+
+ with tempfile.TemporaryDirectory() as tmp:
+ path = Path(tmp) / "book.epub"
+ _build_test_epub(path)
+ items = _read_epub_ebooklib(path)
+
+ html = "\n".join(content for _, content in items)
+ self.assertIn("First chapter text.", html)
+ self.assertIn("Second chapter text.", html)
+
+ def test_epub_extraction_follows_spine_order(self):
+ with tempfile.TemporaryDirectory() as tmp:
+ path = Path(tmp) / "book.epub"
+ _build_test_epub(path)
+ text = extract_text(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."))
+
+
+class ExtractSectionsTests(unittest.TestCase):
+ def setUp(self):
+ try:
+ import ebooklib # noqa: F401
+ except ImportError:
+ self.skipTest("ebooklib not installed")
+
+ def test_epub_sections_split_on_chapters(self):
+ from converter.extractors import extract_sections
+
+ with tempfile.TemporaryDirectory() as tmp:
+ path = Path(tmp) / "book.epub"
+ _build_test_epub(path)
+ sections = extract_sections(path)
+
+ self.assertEqual(len(sections), 2)
+ self.assertEqual(sections[0].title, "One")
+ self.assertEqual(sections[1].title, "Two")
+ self.assertIn("First chapter text.", sections[0].text)
+ self.assertIn("Second chapter text.", sections[1].text)
+
+ def test_txt_is_single_section(self):
+ from converter.extractors import extract_sections
+
+ with tempfile.TemporaryDirectory() as tmp:
+ path = Path(tmp) / "book.txt"
+ path.write_text("Hello world.", encoding="utf-8")
+ sections = extract_sections(path)
+
+ self.assertEqual(len(sections), 1)
+ self.assertEqual(sections[0].title, "book")
+ self.assertEqual(sections[0].text, "Hello world.")
+
+ def test_single_chapter_epub_keeps_chapter_title(self):
+ from converter.extractors import extract_sections
+
+ with tempfile.TemporaryDirectory() as tmp:
+ path = Path(tmp) / "book.epub"
+ _build_test_epub(path, chapters=(("Only", "Just one chapter."),))
+ sections = extract_sections(path)
+
+ self.assertEqual(len(sections), 1)
+ self.assertEqual(sections[0].title, "Only")
+ self.assertIn("Just one chapter.", sections[0].text)
+
+
+class ExtractBookTests(unittest.TestCase):
+ def test_txt_falls_back_to_stem_and_blank_author(self):
+ from converter.extractors import extract_book
+
+ with tempfile.TemporaryDirectory() as tmp:
+ path = Path(tmp) / "mybook.txt"
+ path.write_text("Hello world.", encoding="utf-8")
+ book = extract_book(path)
+
+ self.assertEqual(book.title, "mybook")
+ self.assertEqual(book.author, "")
+ self.assertEqual(len(book.sections), 1)
+
+ def test_epub_metadata_harvested(self):
+ try:
+ import ebooklib # noqa: F401
+ except ImportError:
+ self.skipTest("ebooklib not installed")
+ from converter.extractors import extract_book
+
+ with tempfile.TemporaryDirectory() as tmp:
+ path = Path(tmp) / "book.epub"
+ _build_test_epub(path)
+ book = extract_book(path)
+
+ self.assertEqual(book.title, "Test Book")
+ self.assertEqual(book.author, "Test Author")
+ self.assertEqual([s.title for s in book.sections], ["One", "Two"])
+
+ def test_pdf_metadata_harvested(self):
+ from converter.extractors import extract_book
+
+ try:
+ from pypdf import PdfWriter
+ except ImportError:
+ self.skipTest("pypdf not installed")
+
+ with tempfile.TemporaryDirectory() as tmp:
+ path = Path(tmp) / "book.pdf"
+ writer = PdfWriter()
+ writer.add_metadata({"/Title": "PDF Title", "/Author": "PDF Author"})
+ writer.add_blank_page(width=612, height=792)
+ with open(path, "wb") as handle:
+ writer.write(handle)
+ book = extract_book(path)
+
+ self.assertEqual(book.title, "PDF Title")
+ self.assertEqual(book.author, "PDF Author")
+ self.assertEqual(len(book.sections), 1)
+
+ def test_pdf_without_metadata_falls_back(self):
+ from converter.extractors import extract_book
+
+ try:
+ from pypdf import PdfWriter
+ except ImportError:
+ self.skipTest("pypdf not installed")
+
+ with tempfile.TemporaryDirectory() as tmp:
+ path = Path(tmp) / "plain.pdf"
+ writer = PdfWriter()
+ writer.add_blank_page(width=612, height=792)
+ with open(path, "wb") as handle:
+ writer.write(handle)
+ book = extract_book(path)
+
+ self.assertEqual(book.title, "plain")
+ self.assertEqual(book.author, "")
+
+
+if __name__ == "__main__":
+ unittest.main()