import unittest import os import shutil import sys from ebooklib import epub # Ensure import path sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) from abogen.book_parser import get_book_parser class TestEpubContentSlicing(unittest.TestCase): """ Tests for the complex content slicing logic in _execute_nav_parsing_logic. This covers scenarios where multiple chapters/sections are contained within a single physical HTML file, separated by anchors (fragments). """ def setUp(self): self.test_dir = "tests/test_data_slicing" if os.path.exists(self.test_dir): shutil.rmtree(self.test_dir) os.makedirs(self.test_dir) self.epub_path = os.path.join(self.test_dir, "slicing_test.epub") def tearDown(self): if os.path.exists(self.test_dir): shutil.rmtree(self.test_dir) def test_single_file_multiple_chapters(self): """ Test splitting one XHTML file into two chapters using an anchor. """ book = epub.EpubBook() book.set_identifier("slice123") book.set_title("Slicing Test Book") # Create a single content file with two sections content_html = """

Chapter 1

Text for chapter 1.


Chapter 2

Text for chapter 2.

""" c1 = epub.EpubHtml(title="Full Content", file_name="content.xhtml", lang="en") c1.content = content_html book.add_item(c1) # Create Nav that points to anchors in the SAME file # We use EpubHtml for Nav to control content exactly without ebooklib interference nav_html = """ """ nav = epub.EpubHtml(title="Nav", file_name="nav.xhtml") nav.content = nav_html book.add_item(nav) book.spine = [nav, c1] epub.write_epub(self.epub_path, book) # OPF Patching to valid crash import zipfile patched = False with zipfile.ZipFile(self.epub_path, 'r') as zin: opf_content = zin.read('EPUB/content.opf').decode('utf-8') if 'toc="ncx"' in opf_content: opf_content = opf_content.replace('toc="ncx"', '') patched = True if patched: TEMP_EPUB = self.epub_path + ".temp" with zipfile.ZipFile(TEMP_EPUB, 'w') as zout: for item in zin.infolist(): if item.filename == 'EPUB/content.opf': zout.writestr(item, opf_content) else: zout.writestr(item, zin.read(item.filename)) if patched: shutil.move(TEMP_EPUB, self.epub_path) # Parse parser = get_book_parser(self.epub_path) parser.process_content() chapters = parser.get_chapters() # Filter Nav/Intro chapters = [c for c in chapters if "Chapter" in c[1]] self.assertEqual(len(chapters), 2) self.assertEqual(chapters[0][1], "Chapter 1") self.assertEqual(chapters[1][1], "Chapter 2") # Check content of Chapter 1 # It should contain "Text for chapter 1" but NOT "Text for chapter 2" # The parser logic slices from start_pos to next_pos text1 = parser.content_texts[chapters[0][0]] self.assertIn("Text for chapter 1", text1) self.assertNotIn("Text for chapter 2", text1) # Check content of Chapter 2 text2 = parser.content_texts[chapters[1][0]] self.assertIn("Text for chapter 2", text2) def test_list_renumbering(self): """ Test that ordered lists are re-numbered when slicing. The parser has logic to reset
    or insert numbers. """ book = epub.EpubBook() book.set_identifier("list123") book.set_title("List Test Book") content_html = """

    Part 1

    1. Item A
    2. Item B

    Part 2

    1. Item C
    2. Item D
    """ c1 = epub.EpubHtml(title="Content", file_name="content.xhtml", lang="en") c1.content = content_html book.add_item(c1) nav_html = """ """ nav = epub.EpubHtml(title="Nav", file_name="nav.xhtml") nav.content = nav_html book.add_item(nav) book.spine = [nav, c1] epub.write_epub(self.epub_path, book) # Patch import zipfile patched = False with zipfile.ZipFile(self.epub_path, 'r') as zin: opf_content = zin.read('EPUB/content.opf').decode('utf-8') if 'toc="ncx"' in opf_content: opf_content = opf_content.replace('toc="ncx"', '') patched = True if patched: TEMP_EPUB = self.epub_path + ".temp" with zipfile.ZipFile(TEMP_EPUB, 'w') as zout: for item in zin.infolist(): if item.filename == 'EPUB/content.opf': zout.writestr(item, opf_content) else: zout.writestr(item, zin.read(item.filename)) if patched: shutil.move(TEMP_EPUB, self.epub_path) parser = get_book_parser(self.epub_path) parser.process_content() chapters = parser.get_chapters() chapters = [c for c in chapters if "Part" in c[1]] self.assertEqual(len(chapters), 2) # Check Part 1 text text1 = parser.content_texts[chapters[0][0]] # The parser explicitly replaces li with "1) Item A" style text self.assertIn("1) Item A", text1) self.assertIn("2) Item B", text1) # Check Part 2 text text2 = parser.content_texts[chapters[1][0]] # Should convert start="3" to "3) Item C" self.assertIn("3) Item C", text2) self.assertIn("4) Item D", text2) if __name__ == "__main__": unittest.main()