Some checks failed
Deploy Development / deploy (push) Successful in 43s
Test Suite / pytest-backend (push) Failing after 2s
Test Suite / lint-backend (push) Successful in 0s
Test Suite / build-frontend (push) Successful in 13s
Test Suite / k6 /health Baseline (push) Successful in 34s
Test Suite / playwright-tests (push) Successful in 1m20s
- Introduced new catalog context handling in planning prompt functions, allowing for improved integration of planning variables. - Added optional catalog context parameters in various functions to streamline the merging of planning prompt variables. - Updated frontend components to include CatalogPromptSlotsEditor for managing prompt slots across different catalog types. - Enhanced API utilities to support fetching and updating catalog prompt slots, improving backend functionality for catalog management. - Incremented version numbers and updated changelog to reflect the new features and improvements.
167 lines
6.0 KiB
Python
167 lines
6.0 KiB
Python
"""Tests Katalog-Prompt-Slots (H2)."""
|
|
from unittest.mock import MagicMock
|
|
|
|
from catalog_prompt_slots import (
|
|
build_catalog_guidance_for_prompt,
|
|
pick_active_catalog_item,
|
|
placeholder_key,
|
|
resolve_catalog_prompt_variables,
|
|
)
|
|
from planning_catalog_context import PlanningCatalogContextItem, ProgressionPlanningCatalogContext
|
|
from planning_prompt_variables import merge_planning_prompt_variables
|
|
|
|
|
|
def _mock_cur(
|
|
rows_by_table=None,
|
|
slots_by_kind_id=None,
|
|
slot_types_ready=True,
|
|
):
|
|
rows_by_table = rows_by_table or {}
|
|
slots_by_kind_id = slots_by_kind_id or {}
|
|
|
|
cur = MagicMock()
|
|
|
|
def execute(sql, params=None):
|
|
sql_l = (sql or "").lower()
|
|
if "to_regclass" in sql_l:
|
|
cur.fetchone.return_value = {"t": "catalog_prompt_slot_types" if slot_types_ready else None}
|
|
return
|
|
if "from catalog_prompt_slot_types" in sql_l:
|
|
cur.fetchall.return_value = []
|
|
return
|
|
if "from catalog_prompt_slots" in sql_l:
|
|
kind, cid = params[0], int(params[1])
|
|
slot_map = slots_by_kind_id.get((kind, cid), {})
|
|
cur.fetchall.return_value = [
|
|
{"slot_key": k, "content": v} for k, v in slot_map.items()
|
|
]
|
|
return
|
|
for table, rows in rows_by_table.items():
|
|
if f"from {table}" in sql_l:
|
|
item_id = int(params[0])
|
|
raw = rows.get(item_id)
|
|
if raw is None:
|
|
cur.fetchone.return_value = None
|
|
elif isinstance(raw, dict):
|
|
cur.fetchone.return_value = {
|
|
"id": item_id,
|
|
"name": raw.get("name", ""),
|
|
"description": raw.get("description", ""),
|
|
}
|
|
else:
|
|
cur.fetchone.return_value = {
|
|
"id": item_id,
|
|
"name": str(raw),
|
|
"description": "",
|
|
}
|
|
return
|
|
cur.fetchone.return_value = None
|
|
cur.fetchall.return_value = []
|
|
|
|
cur.execute.side_effect = execute
|
|
return cur
|
|
|
|
|
|
def test_pick_active_catalog_item_primary_wins():
|
|
items = [
|
|
PlanningCatalogContextItem(id=1, is_primary=False, weight=0.9),
|
|
PlanningCatalogContextItem(id=2, is_primary=True, weight=0.5),
|
|
]
|
|
assert pick_active_catalog_item(items).id == 2
|
|
|
|
|
|
def test_granular_placeholder_focus_area_hints_on_path_qa():
|
|
cur = _mock_cur(
|
|
rows_by_table={"focus_areas": {4: {"name": "Gewaltschutz"}}},
|
|
slots_by_kind_id={
|
|
("focus_area", 4): {
|
|
"description": "Planung zielt auf Prävention und Deeskalation.",
|
|
"hints_on_path_qa": "Lücken sind fehlende Deeskalations-Stufen.",
|
|
"anti_patterns": "Nicht nach Kumite-Tiefe bewerten.",
|
|
}
|
|
},
|
|
)
|
|
catalog = ProgressionPlanningCatalogContext(
|
|
focus_areas=[PlanningCatalogContextItem(id=4, is_primary=True)],
|
|
)
|
|
resolved = resolve_catalog_prompt_variables(cur, catalog, slug="planning_exercise_path_qa")
|
|
assert "Deeskalation" in resolved[placeholder_key("focus_area", "hints_on_path_qa")]
|
|
assert "Deeskalation" in resolved["catalog_guidance_block"]
|
|
assert resolved["has_catalog_guidance"] == "true"
|
|
|
|
|
|
def test_description_fallback_from_stammdaten():
|
|
cur = _mock_cur(
|
|
rows_by_table={
|
|
"focus_areas": {
|
|
4: {
|
|
"name": "Gewaltschutz",
|
|
"description": "Gewaltprävention und Deeskalation",
|
|
}
|
|
}
|
|
},
|
|
slots_by_kind_id={("focus_area", 4): {}},
|
|
)
|
|
catalog = ProgressionPlanningCatalogContext(
|
|
focus_areas=[PlanningCatalogContextItem(id=4, is_primary=True)],
|
|
)
|
|
resolved = resolve_catalog_prompt_variables(cur, catalog)
|
|
assert resolved[placeholder_key("focus_area", "description")] == "Gewaltprävention und Deeskalation"
|
|
|
|
|
|
def test_empty_without_catalog():
|
|
cur = MagicMock()
|
|
out = build_catalog_guidance_for_prompt(cur, None)
|
|
assert out["has_catalog_guidance"] is False
|
|
assert out["catalog_guidance_block"] == ""
|
|
|
|
|
|
def test_unknown_entry_no_guidance_block():
|
|
cur = _mock_cur(rows_by_table={"focus_areas": {99: {"name": "Unbekannter Fokus XYZ"}}})
|
|
catalog = ProgressionPlanningCatalogContext(
|
|
focus_areas=[PlanningCatalogContextItem(id=99, is_primary=True)],
|
|
)
|
|
out = build_catalog_guidance_for_prompt(cur, catalog)
|
|
assert out["has_catalog_guidance"] is False
|
|
assert out["catalog_guidance_block"] == ""
|
|
assert "Unbekannter Fokus XYZ" in out["catalog_context_json"]
|
|
|
|
|
|
def test_merge_planning_prompt_variables_granular_keys():
|
|
cur = _mock_cur(
|
|
rows_by_table={"focus_areas": {4: {"name": "Gewaltschutz"}}},
|
|
slots_by_kind_id={
|
|
("focus_area", 4): {"hints_on_path_qa": "Deeskalation und Grenzen."}
|
|
},
|
|
)
|
|
catalog = ProgressionPlanningCatalogContext(
|
|
focus_areas=[PlanningCatalogContextItem(id=4, is_primary=True)],
|
|
)
|
|
merged = merge_planning_prompt_variables(
|
|
cur,
|
|
{"goal_query": "Deeskalation Kinder"},
|
|
catalog=catalog,
|
|
slug="planning_exercise_path_qa",
|
|
)
|
|
assert merged[placeholder_key("focus_area", "hints_on_path_qa")].startswith("Deeskalation")
|
|
assert merged["has_catalog_guidance"] == "true"
|
|
|
|
|
|
def test_priority_order_in_guidance_block():
|
|
cur = _mock_cur(
|
|
rows_by_table={
|
|
"focus_areas": {1: {"name": "Gewaltschutz"}},
|
|
"training_types": {2: {"name": "Breitensport"}},
|
|
},
|
|
slots_by_kind_id={
|
|
("focus_area", 1): {"description": "Fokus-Text"},
|
|
("training_type", 2): {"description": "Stil-Text"},
|
|
},
|
|
)
|
|
catalog = ProgressionPlanningCatalogContext(
|
|
focus_areas=[PlanningCatalogContextItem(id=1, is_primary=True)],
|
|
training_types=[PlanningCatalogContextItem(id=2, is_primary=True)],
|
|
)
|
|
block = build_catalog_guidance_for_prompt(cur, catalog)["catalog_guidance_block"]
|
|
assert block.index("Primärfokus") < block.index("Trainingsstil")
|