mindnet/tests/test_final_wp15_validation.py
2025-12-12 11:56:44 +01:00

110 lines
3.7 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
# WICHTIG: Setze sehr kleine Werte, um Split bei kurzem Text zu erzwingen
cfg['target'] = 50
cfg['max'] = 100
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]].
"""
# Verlängerter Text, um Split > 1 zu erzwingen (bei Target 50)
TEST_MARKDOWN_SLIDING = """
---
id: 20251212-test-legacy
title: Fließtext Protokoll
type: journal
status: active
---
Dies ist der erste Absatz. Er muss lang genug sein, damit der Chunker ihn schneidet.
Wir schreiben hier über Rituale wie [[leitbild-rituale-system]] und viele andere Dinge.
Das Wetter ist schön und die Programmierung läuft gut. Dies sind Füllsätze für Länge.
Dies ist der zweite Absatz. Er ist durch eine Leerzeile getrennt und sollte einen neuen Kontext bilden.
Auch hier schreiben wir viel Text, damit die Token-Anzahl die Grenze von 50 Tokens überschreitet.
Das System muss hier splitten.
"""
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)}")
print(f" -> Chunks generiert (Smart): {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 diesem langen Text > 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!")
print(f" -> Chunks generiert (Legacy): {len(chunks)}")
if __name__ == '__main__':
unittest.main()