2025-12-12 08:47:12 +01:00
# tests/test_smart_chunking_integration.py
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 ---
# Fügt das Root-Verzeichnis zum Python-Pfad hinzu (wie zuvor besprochen)
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 08:59:08 +01:00
# WICHTIG: Wir importieren extract_frontmatter_from_text NICHT mehr aus
# note_payload.py, sondern entfernen den Import, da er für den Test nicht direkt nötig ist.
# ANNAHME: Der Test kann die Logik des Parsings und der Edge-Derivation nutzen,
# ohne note_payload direkt zu importieren.
2025-12-12 08:47:12 +01:00
# 1. Definieren der Test-Datei (Muss im Vault existieren, wenn es ein echter Integrationstest ist)
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.
"""
class TestSemanticChunking ( unittest . TestCase ) :
def setUp ( self ) :
# Setzt die Konfiguration auf den Typ 'journal'
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 ) :
"""
Prüft den gesamten End - to - End - Flow :
1. LLM - Chunking
2. Kanten - Injektion ( als [ [ rel : . . . ] ] )
3. Kanten - Erkennung durch derive_edges . py
"""
# --- 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 --- " )
self . assertTrue ( len ( chunks ) > 1 ,
" Erwartung: Das LLM sollte den Text in mehrere semantische Chunks zerlegen. " )
# --- 2. Injektion prüfen (Der Chunk-Text muss die Links enthalten) ---
chunk_1_text = chunks [ 0 ] . text
print ( f " Chunk 1 Text (Anfang): { chunk_1_text [ : 100 ] } ... " )
self . assertIn ( " [[rel: " , chunk_1_text ,
" Fehler: Der Chunk-Text muss die injizierte [[rel: Kante enthalten.] " )
# --- 3. Kanten-Derivation (Synchron) ---
edges = derive_edges . build_edges_for_note (
note_id = TEST_NOTE_ID ,
chunks = [ c . __dict__ for c in chunks ] # Chunker-Objekte in Dicts konvertieren
)
print ( f " --- Edge Derivation Output: { len ( edges ) } Kanten --- " )
# 4. Assertions: Prüfen auf Existenz spezifischer, vom LLM generierter Kanten
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 ' )
]
print ( f " Gefundene LLM-Kanten (inline:rel): { len ( llm_generated_edges ) } " )
self . assertTrue ( len ( llm_generated_edges ) > = 3 ,
" Erwartung: Mindestens 3 LLM-generierte Kanten (eine pro semantischem Abschnitt). " )
# Check für die spezifische Kante 'uses' (oder 'based_on'/'derived_from' von der Matrix)
has_ritual_kante = any (
e [ ' target_id ' ] == ' leitbild-rituale-system '
2025-12-12 08:59:08 +01:00
and e [ ' source_id ' ] . startswith ( TEST_NOTE_ID + ' #sem00 ' )
2025-12-12 08:47:12 +01:00
for e in llm_generated_edges
)
self . assertTrue ( has_ritual_kante ,
" Fehler: Der LLM-Chunker hat die Kante zu ' leitbild-rituale-system ' nicht korrekt an Chunk 1 gebunden. " )
# Check für die Matrix-Logik (z.B. 'derived_from' zu 'leitbild-werte')
has_matrix_kante = any (
2025-12-12 08:59:08 +01:00
e [ ' target_id ' ] . startswith ( ' leitbild-werte ' ) and e [ ' kind ' ] in [ ' based_on ' , ' derived_from ' , ' references ' ]
2025-12-12 08:47:12 +01:00
for e in llm_generated_edges
)
self . assertTrue ( has_matrix_kante ,
" Fehler: Die Matrix-Logik wurde nicht aktiv oder das LLM hat die Werte-Kante nicht erkannt. " )
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
# Wenn LLM genutzt wird, ist die ID 'sem'. Wenn by_heading genutzt wird,
# ist die ID standardmäßig 'c' und die Logik ist anders.
self . assertFalse ( chunks [ 0 ] . id . startswith ( TEST_NOTE_ID + ' #sem ' ) ,
2025-12-12 08:59:08 +01:00
" Fehler: LLM-Chunking wurde für den Status ' draft ' nicht verhindert (ID startet mit #sem). " )
2025-12-12 08:47:12 +01:00
2025-12-12 08:59:08 +01:00
# Da 'by_heading' der Fallback ist, sollte die ID mit '#c' starten
self . assertTrue ( chunks [ 0 ] . id . startswith ( TEST_NOTE_ID + ' #c ' ) ,
" 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 } ). " )
# --- Ende des Test-Skripts ---