2025-12-12 09:18:36 +01:00
# tests/test_smart_chunking_integration.py (Letzte Korrektur zur Umgehung des AsyncIO-Fehlers)
2025-12-12 08:47:12 +01:00
import asyncio
import unittest
import os
import sys
from pathlib import Path
from typing import List , Dict
2025-12-12 08:59:08 +01:00
# --- PFAD-KORREKTUR ---
ROOT_DIR = Path ( __file__ ) . resolve ( ) . parent . parent
sys . path . insert ( 0 , str ( ROOT_DIR ) )
# ----------------------
2025-12-12 08:47:12 +01:00
# Import der Kernkomponenten
from app . core import chunker
from app . core import derive_edges
2025-12-12 09:11:01 +01:00
from app . services . semantic_analyzer import SemanticAnalyzer
2025-12-12 09:14:18 +01:00
2025-12-12 08:59:08 +01:00
2025-12-12 09:07:33 +01:00
# 1. Definieren der Test-Note (Simuliert eine journal.md Datei)
2025-12-12 08:47:12 +01:00
TEST_NOTE_ID = " 20251211-journal-sem-test "
TEST_NOTE_TYPE = " journal "
TEST_MARKDOWN = """
- - -
id : 20251211 - journal - sem - test
title : Tägliches Log - Semantischer Test
type : journal
status : active
created : 2025 - 12 - 11
tags : [ test , daily - log ]
- - -
# Log-Eintrag 2025-12-11
Heute war ein guter Tag . Zuerst habe ich mit der R1 Meditation begonnen , um meinen Nordstern Fokus zu klären . Das Ritual [ [ leitbild - rituale - system ] ] hat mir geholfen , ruhig in den Tag zu starten . Ich habe gespürt , wie wichtig meine [ [ leitbild - werte #Integrität]] für meine Entscheidungen ist. Das ist das Fundament.
Am Nachmittag gab es einen Konflikt bei der Karate - Trainer - Ausbildung . Ein Schüler war uneinsichtig . Ich habe die Situation nach [ [ leitbild - prinzipien #P4 Gerechtigkeit & Fairness]] behandelt und beide Seiten gehört (Steelman). Das war anstrengend, aber ich habe meine [[leitbild-rollen#Karate-Trainer]] Mission erfüllt. Die Konsequenz war klar und ruhig.
Abends habe ich den wöchentlichen Load - Check mit meinem Partner gemacht . Das Paar - Ritual [ [ leitbild - rituale - system #R5]] hilft, das Ziel [[leitbild-ziele-portfolio#Nordstern Partner]] aktiv zu verfolgen. Es ist der operative Rhythmus für uns beide.
"""
2025-12-12 09:18:36 +01:00
# --- ENTFERNEN DER KOMPLEXEN TEARDOWN-HILFEN ---
# Wir entfernen die fehleranfällige asynchrone Schließungslogik.
2025-12-12 09:14:18 +01:00
2025-12-12 09:11:01 +01:00
2025-12-12 08:47:12 +01:00
class TestSemanticChunking ( unittest . TestCase ) :
2025-12-12 09:07:33 +01:00
_analyzer_instance = None
@classmethod
def setUpClass ( cls ) :
2025-12-12 09:14:18 +01:00
""" Initialisiert den SemanticAnalyzer einmalig. """
2025-12-12 09:07:33 +01:00
cls . _analyzer_instance = SemanticAnalyzer ( )
2025-12-12 09:14:18 +01:00
# Stellt sicher, dass der Chunker diese Singleton-Instanz verwendet
2025-12-12 09:07:33 +01:00
chunker . _semantic_analyzer_instance = cls . _analyzer_instance
@classmethod
def tearDownClass ( cls ) :
2025-12-12 09:18:36 +01:00
"""
PRAGMATISCHE LÖSUNG : Überspringe das explizite Aclose ( ) im Teardown ,
um den Event Loop Konflikt zu vermeiden . Die GC wird die Verbindung schließen .
"""
pass
2025-12-12 09:07:33 +01:00
2025-12-12 08:47:12 +01:00
def setUp ( self ) :
self . config = chunker . get_chunk_config ( TEST_NOTE_TYPE )
def test_a_strategy_selection ( self ) :
""" Prüft, ob die Strategie ' semantic_llm ' für den Typ ' journal ' gewählt wird. """
self . assertEqual ( self . config [ ' strategy ' ] , ' semantic_llm ' ,
" Fehler: ' journal ' sollte die Strategie ' semantic_llm ' nutzen. " )
def test_b_llm_chunking_and_injection ( self ) :
"""
2025-12-12 09:11:01 +01:00
Prüft den gesamten End - to - End - Flow : 1. LLM - Chunking , 2. Kanten - Injektion , 3. Kanten - Erkennung .
2025-12-12 08:47:12 +01:00
"""
2025-12-12 09:07:33 +01:00
2025-12-12 08:47:12 +01:00
# --- 1. Chunking (Asynchron) ---
chunks = asyncio . run ( chunker . assemble_chunks (
note_id = TEST_NOTE_ID ,
md_text = TEST_MARKDOWN ,
note_type = TEST_NOTE_TYPE
) )
print ( f " \n --- LLM Chunker Output: { len ( chunks ) } Chunks --- " )
2025-12-12 09:07:33 +01:00
2025-12-12 09:18:36 +01:00
# Assertion B1: Zerlegung (Das LLM muss mehr als 1 Chunk liefern)
2025-12-12 08:47:12 +01:00
self . assertTrue ( len ( chunks ) > 1 ,
2025-12-12 09:11:01 +01:00
" Assertion B1 Fehler: LLM hat nicht zerlegt (Fallback aktiv). Prüfe LLM-Stabilität. " )
2025-12-12 08:47:12 +01:00
2025-12-12 09:11:01 +01:00
# --- 2. Injektion prüfen ---
2025-12-12 08:47:12 +01:00
chunk_1_text = chunks [ 0 ] . text
self . assertIn ( " [[rel: " , chunk_1_text ,
2025-12-12 09:07:33 +01:00
" Assertion B2 Fehler: Der Chunk-Text muss die injizierte [[rel: Kante enthalten. " )
2025-12-12 08:47:12 +01:00
# --- 3. Kanten-Derivation (Synchron) ---
edges = derive_edges . build_edges_for_note (
note_id = TEST_NOTE_ID ,
2025-12-12 09:07:33 +01:00
chunks = [ c . __dict__ for c in chunks ]
2025-12-12 08:47:12 +01:00
)
print ( f " --- Edge Derivation Output: { len ( edges ) } Kanten --- " )
2025-12-12 09:11:01 +01:00
# Assertion B3: Mindestens 3 LLM-Kanten (inline:rel)
2025-12-12 08:47:12 +01:00
llm_generated_edges = [
e for e in edges
if e . get ( ' rule_id ' ) == ' inline:rel ' and e . get ( ' source_id ' ) . startswith ( TEST_NOTE_ID + ' #sem ' )
]
self . assertTrue ( len ( llm_generated_edges ) > = 3 ,
2025-12-12 09:11:01 +01:00
" Assertion B3 Fehler: Es wurden weniger als 3 semantische Kanten gefunden. " )
2025-12-12 08:47:12 +01:00
2025-12-12 09:11:01 +01:00
# Assertion B4: Check für die Matrix-Logik / Werte-Kante
2025-12-12 08:47:12 +01:00
has_matrix_kante = any (
2025-12-12 09:07:33 +01:00
e [ ' target_id ' ] . startswith ( ' leitbild-werte ' ) and e [ ' kind ' ] in [ ' based_on ' , ' derived_from ' ]
2025-12-12 08:47:12 +01:00
for e in llm_generated_edges
)
self . assertTrue ( has_matrix_kante ,
2025-12-12 09:07:33 +01:00
" Assertion B4 Fehler: Die Matrix-Logik / Werte-Kante wurde nicht erkannt. " )
2025-12-12 08:47:12 +01:00
print ( " \n ✅ Integrationstest für Semantic Chunking erfolgreich. " )
def test_c_draft_status_prevention ( self ) :
""" Prüft, ob ' draft ' Status semantic_llm auf by_heading überschreibt. """
DRAFT_MARKDOWN = TEST_MARKDOWN . replace ( " status: active " , " status: draft " )
# 1. Chunking mit Draft Status
chunks = asyncio . run ( chunker . assemble_chunks (
note_id = TEST_NOTE_ID ,
md_text = DRAFT_MARKDOWN ,
note_type = TEST_NOTE_TYPE
) )
# 2. Prüfen der Chunker-IDs
self . assertFalse ( chunks [ 0 ] . id . startswith ( TEST_NOTE_ID + ' #sem ' ) ,
2025-12-12 09:07:33 +01:00
" Assertion C1 Fehler: LLM-Chunking wurde für den Status ' draft ' nicht verhindert. " )
2025-12-12 08:47:12 +01:00
2025-12-12 08:59:08 +01:00
self . assertTrue ( chunks [ 0 ] . id . startswith ( TEST_NOTE_ID + ' #c ' ) ,
2025-12-12 09:07:33 +01:00
" Assertion C2 Fehler: Fallback-Strategie ' by_heading ' wurde nicht korrekt ausgeführt. " )
2025-12-12 08:47:12 +01:00
2025-12-12 08:59:08 +01:00
print ( f " \n ✅ Prevention Test: Draft-Status hat LLM-Chunking verhindert (Fallback ID: { chunks [ 0 ] . id } ). " )
2025-12-12 09:07:33 +01:00
if __name__ == ' __main__ ' :
2025-12-12 09:11:01 +01:00
print ( " Starte den Semantic Chunking Integrationstest. " )
2025-12-12 09:07:33 +01:00
unittest . main ( )