diff options
Diffstat (limited to 'app/tests/test_extractors.py')
| -rw-r--r-- | app/tests/test_extractors.py | 213 |
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() |
