mindnet/tests/test_final_wp15_validation.py

154 lines
6.2 KiB
Python

# tests/test_final_wp15_validation.py
import asyncio
import unittest
import os
import sys
from pathlib import Path
from typing import List, Dict, Any
# --- PFAD-KORREKTUR ---
ROOT_DIR = Path(__file__).resolve().parent.parent
sys.path.insert(0, str(ROOT_DIR))
# ----------------------
# Import der Kernkomponenten
from app.core import chunker
from app.core import derive_edges
from app.services.semantic_analyzer import SemanticAnalyzer
# 1. Hilfsfunktion zur Manipulation der Konfiguration im Test
def get_config_for_test(strategy: str, enable_smart_edge: bool) -> Dict[str, Any]:
"""Erzeugt eine ad-hoc Konfiguration, um eine Strategie zu erzwingen."""
cfg = chunker.get_chunk_config("concept") # Nutze eine Basis
cfg['strategy'] = strategy
cfg['enable_smart_edge_allocation'] = enable_smart_edge
return cfg
# 2. Test-Daten (Muss die Entitäten aus den Vault-Dateien verwenden)
TEST_NOTE_ID = "20251212-test-integration"
TEST_NOTE_TYPE = "concept" # Kann eine beliebige Basis sein
# Text, der die Matrix-Logik und Header triggert
TEST_MARKDOWN_SMART = """
---
id: 20251212-test-integration
title: Integrationstest - Smart Edges
type: concept
status: active
---
# Teil 1: Intro
Dies ist die Einleitung. Wir definieren unsere Mission: Präsent sein und vorleben.
Dies entspricht unseren Werten [[leitbild-werte#Integrität]] und [[leitbild-werte#Respekt]].
## Teil 2: Rollenkonflikt
Der Konflikt zwischen [[leitbild-rollen#Vater]] und [[leitbild-rollen#Berufsrolle (Umbrella)]] muss gelöst werden.
Die Lösung muss [[rel:depends_on leitbild-review#Weekly Review]].
"""
# Text, der nur für Sliding Window geeignet ist
TEST_MARKDOWN_SLIDING = """
---
id: 20251212-test-sliding
title: Fließtext Protokoll
type: journal
status: active
---
Dies ist ein langer Fließtextabschnitt, der ohne Header auskommt.
Er spricht über die neue [[leitbild-prinzipien#P1 Integrität]] Regel und den Ablauf des Tages.
Das sollte in zwei Chunks zerlegt werden.
"""
# 3. Testklasse
class TestFinalWP15Integration(unittest.TestCase):
# Initiale Ressourcen-Verwaltung (um den AsyncClient zu schließen)
_analyzer_instance = None
@classmethod
def setUpClass(cls):
cls._analyzer_instance = SemanticAnalyzer()
chunker._semantic_analyzer_instance = cls._analyzer_instance
@classmethod
def tearDownClass(cls):
if cls._analyzer_instance:
# Nutzt die temporäre Loop-Lösung
loop = asyncio.get_event_loop()
loop.run_until_complete(cls._analyzer_instance.close())
# --- A. Smart Edge Allocation Test ---
def test_a_smart_edge_allocation(self):
"""Prüft die neue LLM-Orchestrierung (5 Schritte) und die Kanten-Bindung."""
config = get_config_for_test('by_heading', enable_smart_edge=True)
# 1. Chunking (Asynchroner Aufruf der neuen Orchestrierung)
chunks = asyncio.run(chunker.assemble_chunks(
note_id=TEST_NOTE_ID,
md_text=TEST_MARKDOWN_SMART,
note_type=TEST_NOTE_TYPE,
config=config # Übergibt die ad-hoc Konfiguration (Annahme: assemble_chunks akzeptiert kwargs)
))
# NOTE: Da assemble_chunks die config intern lädt, müssten wir hier idealerweise
# die types.yaml zur Laufzeit manipulieren oder die config in kwargs übergeben (letzteres ist hier angenommen).
# 2. Grundlegende Checks
self.assertTrue(len(chunks) >= 2, "A1 Fehler: Primärzerlegung (by_heading) muss mindestens 2 Chunks liefern.")
# 3. Kanten-Checks (durch derive_edges.py im Chunker ausgelöst)
# Wir suchen nach der LLM-generierten, spezifischen Kante
# Erwartet: Chunk 1/2 enthält die Kante 'derived_from' oder 'based_on' zu 'leitbild-werte'.
all_edges = []
for c in chunks:
# Um die Kanten zu erhalten, muss derive_edges manuell aufgerufen werden,
# da der Chunker nur den Text injiziert.
# Im echten Importer würde build_edges_for_note auf den injizierten Text angewendet.
# Hier simulieren wir den Endeffekt, indem wir die injizierten Kanten prüfen:
if "suggested_edges" in c.__dict__:
all_edges.extend(c.suggested_edges)
has_matrix_kante = any("based_on:leitbild-werte" in e or "derived_from:leitbild-werte" in e for e in all_edges)
self.assertTrue(has_matrix_kante,
"A2 Fehler: LLM-Kantenfilter hat die Matrix-Logik (value -> based_on/derived_from) nicht angewendet oder erkannt.")
print("\n✅ Test A: Smart Edge Allocation erfolgreich.")
# --- B. Abwärtskompatibilität (Legacy Tests) ---
def test_b_backward_compatibility(self):
"""Prüft, ob die alte, reine Sliding Window Strategie (ohne LLM-Filter) noch funktioniert."""
# Erzwinge das alte, reine Sliding Window Profil
config = get_config_for_test('sliding_window', enable_smart_edge=False)
# 1. Chunking (Sollte *mehrere* Chunks liefern, ohne LLM-Aufruf)
# Die Orchestrierung sollte nur den reinen Sliding Window Call nutzen.
chunks = asyncio.run(chunker.assemble_chunks(
note_id=TEST_NOTE_ID,
md_text=TEST_MARKDOWN_SLIDING,
note_type='journal',
config=config
))
self.assertTrue(len(chunks) >= 2, "B1 Fehler: Reine Sliding Window Strategie ist fehlerhaft oder zerlegt nicht.")
# 2. Prüfen auf Kanten-Injection (Dürfen NUR aus Wikilinks und Defaults kommen)
# Die manuelle Wikilink [[leitbild-prinzipien#P1 Integrität]] sollte in JEDEM Chunk sein
# wenn Defaults für journal aktiv sind, was falsch ist.
# Im reinen Sliding Window Modus (ohne LLM) werden Kanten nur durch derive_edges.py erkannt.
# Wir prüfen nur, dass die Chunks existieren.
self.assertNotIn('suggested_edges', chunks[0].__dict__, "B2 Fehler: LLM-Kantenfilter wurde fälschlicherweise für enable_smart_edge=False ausgeführt.")
print("\n✅ Test B: Abwärtskompatibilität (reines Sliding Window) erfolgreich.")
if __name__ == '__main__':
print("Startet den finalen WP-15 Validierungstest.")
unittest.main()