mindnet/tests/test_final_wp15_validation.py
2025-12-12 11:45:43 +01:00

109 lines
3.8 KiB
Python

# 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()