154 lines
6.2 KiB
Python
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() |