from app.rag.chunking import TARGET_CHUNK_CHARS, chunk_markdown def test_heading_paths_follow_hierarchy() -> None: md = ( "Intro before any heading.\n\n" "## Wartung\n\nWöchentlich schmieren.\n\n" "### Schmierstoffe\n\nNur GX-220 verwenden.\n\n" "## Sicherheit\n\nLichtvorhang nie überbrücken.\n" ) chunks = chunk_markdown(md, "Maschinenhandbuch") paths = [chunk.heading_path for chunk in chunks] assert paths == [ "Maschinenhandbuch", "Maschinenhandbuch › Wartung", "Maschinenhandbuch › Wartung › Schmierstoffe", "Maschinenhandbuch › Sicherheit", ] assert "GX-220" in chunks[2].content def test_leading_h1_equal_to_title_is_not_duplicated() -> None: md = "# Handbuch\n\nText direkt unter dem Titel.\n\n## Details\n\nMehr.\n" chunks = chunk_markdown(md, "Handbuch") assert chunks[0].heading_path == "Handbuch" assert chunks[1].heading_path == "Handbuch › Details" def test_oversized_section_is_split_at_paragraphs() -> None: paragraph = "Absatz mit ausreichend vielen Wörtern für den Test. " * 20 md = "## Lang\n\n" + "\n\n".join([paragraph] * 5) chunks = chunk_markdown(md, "Doc") assert len(chunks) > 1 assert all(len(chunk.content) <= TARGET_CHUNK_CHARS + 100 for chunk in chunks) assert all(chunk.heading_path == "Doc › Lang" for chunk in chunks) def test_code_fences_are_never_split() -> None: fence = "```\n" + "\n\n".join(["zeile eins", "zeile zwei", "zeile drei"]) + "\n```" filler = "Wort " * 500 md = f"## Code\n\n{filler}\n\n{fence}\n\n{filler}" chunks = chunk_markdown(md, "Doc") fenced = [chunk for chunk in chunks if "```" in chunk.content] for chunk in fenced: assert chunk.content.count("```") % 2 == 0, "chunk split inside a fence" def test_heading_inside_fence_is_not_a_section() -> None: md = "## Skript\n\n```\n# kein heading, nur ein Kommentar\necho hi\n```\n" chunks = chunk_markdown(md, "Doc") assert len(chunks) == 1 assert chunks[0].heading_path == "Doc › Skript" def test_empty_document_yields_no_chunks() -> None: assert chunk_markdown("", "Leer") == [] assert chunk_markdown("\n\n \n", "Leer") == []