# tests/test_final_wp15_validation.py import asyncio import unittest from typing import List, Dict, Any import re from pathlib import Path import sys # --- PFAD-KORREKTUR --- ROOT_DIR = Path(__file__).resolve().parent.parent sys.path.insert(0, str(ROOT_DIR)) from app.core import chunker from app.services.semantic_analyzer import SemanticAnalyzer def get_config_for_test(strategy: str, enable_smart_edge: bool) -> Dict[str, Any]: cfg = chunker.get_chunk_config("concept") cfg['strategy'] = strategy cfg['enable_smart_edge_allocation'] = enable_smart_edge cfg['target'] = 150 # Kleineres Target für sicherere Splits im Test cfg['max'] = 300 return cfg TEST_NOTE_ID_SMART = "20251212-test-smart" TEST_NOTE_ID_LEGACY = "20251212-test-legacy" TEST_MARKDOWN_SMART = """ --- id: 20251212-test-smart title: Integrationstest - Smart Edges type: concept status: active --- # Teil 1: Wichtige Definition Die Mission ist: präsent sein. Dies entspricht unseren Werten [[leitbild-werte#Integrität]]. ## Teil 2: Konflikt Der Konflikt zwischen [[leitbild-rollen#Vater]] und [[leitbild-rollen#Beruf]]. Lösung: [[rel:depends_on leitbild-review#Weekly Review]]. """ # Text mit klaren Absätzen für Sliding Window Test TEST_MARKDOWN_SLIDING = """ --- id: 20251212-test-legacy title: Fließtext Protokoll type: journal status: active --- Dies ist der erste lange Absatz. Er enthält viel Text über allgemeine Dinge und Rituale wie [[leitbild-rituale-system]]. Wir schreiben hier viel, damit der Token-Zähler anschlägt. Das ist wichtig für den Test. Dies ist der zweite Absatz, der durch eine Leerzeile getrennt ist. Er sollte idealerweise in einem neuen Chunk landen oder zumindest den Split erzwingen, wenn das Target klein genug ist (150 Tokens). Hier steht noch mehr Text. """ class TestFinalWP15Integration(unittest.TestCase): _analyzer_instance = None @classmethod def setUpClass(cls): cls._analyzer_instance = SemanticAnalyzer() chunker._semantic_analyzer_instance = cls._analyzer_instance @classmethod def tearDownClass(cls): # FIX: Kein explizites Loop-Closing hier, um RuntimeError zu vermeiden pass def test_a_smart_edge_allocation(self): """A: Prüft Smart Edge Allocation (LLM-Filter).""" config = get_config_for_test('by_heading', enable_smart_edge=True) chunks = asyncio.run(chunker.assemble_chunks( note_id=TEST_NOTE_ID_SMART, md_text=TEST_MARKDOWN_SMART, note_type='concept', config=config )) self.assertTrue(len(chunks) >= 2, f"A1 Fehler: Erwartete >= 2 Chunks, bekam {len(chunks)}") # Prüfen auf Injektion (Text muss [[rel:...]] enthalten) # Hinweis: Da wir keine echte LLM-Antwort garantieren können (Mock fehlt hier), # prüfen wir zumindest, ob der Code durchlief. # Wenn LLM fehlschlägt/leer ist, läuft der Code durch (Robustheit). print(f" -> Chunks generiert: {len(chunks)}") def test_b_backward_compatibility(self): """B: Prüft Sliding Window (Legacy).""" config = get_config_for_test('sliding_window', enable_smart_edge=False) chunks = asyncio.run(chunker.assemble_chunks( note_id=TEST_NOTE_ID_LEGACY, md_text=TEST_MARKDOWN_SLIDING, note_type='journal', config=config )) # Sliding Window muss bei 2 Absätzen und kleinem Target > 1 Chunk liefern self.assertTrue(len(chunks) >= 2, f"B1 Fehler: Sliding Window lieferte nur {len(chunks)} Chunk(s). Split defekt.") # Check: Keine LLM Kanten (da deaktiviert) injected = re.search(r'\[\[rel:', chunks[0].text) self.assertIsNone(injected, "B2 Fehler: LLM-Kanten trotz Deaktivierung gefunden!") if __name__ == '__main__': unittest.main()