mitai-jinkendo/tests/backend/test_phase1_question_augmenter.py
Lars ca562b7130
All checks were successful
Deploy Development / deploy (push) Successful in 49s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 14s
feat: Phase 1 - Fragenergänzung + Strukturierter Container
Backend:
- question_augmenter.py (290 Zeilen): Hybrid-Modell für Fragenergänzungen
  * merge_question_augmentations(): Knotengebundene Fragen überschreiben Prompt-Defaults
  * augment_prompt_with_questions(): Markdown-formatierte Fragenergänzung
  * parse_question_augmentations_from_jsonb(): JSONB → QuestionAugmentation[]
- result_container_parser.py (250 Zeilen): Markdown-Sektionen-Parsing
  * parse_result_container(): Extrahiert Analysekern, Entscheidungsanteil, Begründungsanker
  * validate_decision_signal(): Normalisierung gegen answer_spectrum
  * Fallback-Parsing bei unstrukturierten Antworten
- routers/workflow_questions.py (236 Zeilen): CRUD für workflow_question_catalog
  * GET /api/workflow/questions (mit active_only Filter)
  * POST/PUT/DELETE (Admin only, Soft Delete)
- prompt_executor.py: Integration in execute_base_prompt()
  * Fragenergänzung vor LLM-Call (wenn node_questions oder catalog vorhanden)
  * Result-Container-Parsing nach LLM-Response
- main.py: Router-Registrierung (workflow_questions)

Tests:
- test_phase1_question_augmenter.py (8 Tests): Hybrid-Modell, Formatierung, JSONB-Parsing
- test_phase1_result_container_parser.py (17 Tests): Sektion-Extraktion, Decision-Parsing, Validierung

Alle 25 Unit-Tests bestanden.

version: 0.9j (backend)
module:  workflow 0.2.0

Konzept: .claude/task/Workflow_engine_prompting_engine/konzept_workflow_engine_konsolidated.md (Phase 1)
2026-04-03 18:02:25 +02:00

136 lines
4.0 KiB
Python

"""
Unit Tests für question_augmenter.py (Phase 1)
Run with: PYTHONPATH=./backend pytest tests/backend/test_phase1_question_augmenter.py -v
"""
import pytest
from workflow_models import QuestionAugmentation
from question_augmenter import (
augment_prompt_with_questions,
merge_question_augmentations,
format_question_list,
parse_question_augmentations_from_jsonb
)
def test_format_question_list():
"""Test: Formatierung der Fragenliste"""
questions = [
QuestionAugmentation(
id="q1",
type="relevanz",
question="Ist relevant?",
answer_spectrum=["ja", "nein", "unklar"]
),
QuestionAugmentation(
id="q2",
type="prioritaet",
question="Wie hoch?",
answer_spectrum=["hoch", "mittel", "niedrig", "unklar"]
)
]
result = format_question_list(questions)
assert "Relevanz" in result
assert "[ja/nein/unklar]" in result
assert "Prioritaet" in result # Lowercase wird capitalized
assert "[hoch/mittel/niedrig/unklar]" in result
def test_augment_prompt_with_questions():
"""Test: Prompt-Erweiterung mit Fragenergänzungen"""
base_prompt = "Analysiere die Körperdaten."
questions = [
QuestionAugmentation(
id="q1",
type="relevanz",
question="Ist relevant?",
answer_spectrum=["ja", "nein", "unklar"]
)
]
augmented = augment_prompt_with_questions(base_prompt, questions)
assert "Analysiere die Körperdaten." in augmented
assert "## Analyse" in augmented
assert "## Entscheidungsfragen" in augmented
assert "Relevanz" in augmented
assert "[ja/nein/unklar]" in augmented
def test_merge_question_augmentations_node_priority():
"""Test: Knotengebundene Fragen haben Vorrang (Hybridmodell)"""
node_questions = [
QuestionAugmentation(id="q1", type="relevanz", question="Q1", answer_spectrum=["ja", "nein"])
]
prompt_questions = [
QuestionAugmentation(id="q2", type="prioritaet", question="Q2", answer_spectrum=["hoch", "niedrig"])
]
result = merge_question_augmentations(node_questions, prompt_questions)
# Knotengebundene haben Vorrang
assert len(result) == 1
assert result[0].type == "relevanz"
def test_merge_question_augmentations_prompt_fallback():
"""Test: Prompt-Defaults werden verwendet wenn Knoten leer"""
node_questions = None
prompt_questions = [
QuestionAugmentation(id="q2", type="prioritaet", question="Q2", answer_spectrum=["hoch", "niedrig"])
]
result = merge_question_augmentations(node_questions, prompt_questions)
# Prompt-Defaults werden verwendet
assert len(result) == 1
assert result[0].type == "prioritaet"
def test_merge_question_augmentations_empty():
"""Test: Leere Liste wenn weder Knoten noch Prompt Fragen haben"""
result = merge_question_augmentations(None, None)
assert result == []
def test_parse_question_augmentations_from_jsonb():
"""Test: Parsing aus JSONB-Format"""
jsonb_data = [
{
"id": "q1",
"type": "relevanz",
"question": "Ist relevant?",
"answer_spectrum": ["ja", "nein", "unklar"]
},
{
"id": "q2",
"type": "prioritaet",
"question": "Wie hoch?",
"answer_spectrum": ["hoch", "mittel", "niedrig"]
}
]
result = parse_question_augmentations_from_jsonb(jsonb_data)
assert len(result) == 2
assert result[0].type == "relevanz"
assert result[1].type == "prioritaet"
def test_parse_question_augmentations_empty_jsonb():
"""Test: Leere Liste bei None JSONB"""
result = parse_question_augmentations_from_jsonb(None)
assert result == []
def test_parse_question_augmentations_invalid_jsonb():
"""Test: ValueError bei ungültigem JSONB"""
with pytest.raises(ValueError, match="muss ein Array sein"):
parse_question_augmentations_from_jsonb({"invalid": "format"})
if __name__ == "__main__":
pytest.main([__file__, "-v"])