shinkan-jinkendo/backend/tests/test_planning_path_rematch.py
Lars 1d94c2ebf1
All checks were successful
Deploy Development / deploy (push) Successful in 44s
Test Suite / pytest-backend (push) Successful in 45s
Test Suite / lint-backend (push) Successful in 0s
Test Suite / build-frontend (push) Successful in 13s
Test Suite / k6 /health Baseline (push) Successful in 36s
Test Suite / playwright-tests (push) Successful in 1m23s
Enhance Roadmap Slot Matching and Off-Topic Detection
- Introduced `auto_rematch_after_qa` parameter in `ProgressionPathSuggestRequest` to enable automatic rematching after quality assurance checks.
- Refactored roadmap slot matching logic to improve clarity and functionality, renaming `_build_steps_roadmap_first` to `_match_roadmap_slot`.
- Added `_with_roadmap_major_index` utility to streamline off-topic step detection by incorporating roadmap major step indices.
- Enhanced off-topic detection logic to utilize the new utility for improved clarity in identifying mismatches and exclusions.
- Incremented application version to reflect these updates.
2026-06-11 10:30:48 +02:00

134 lines
3.7 KiB
Python

"""Tests Auto-Rematch nach Pfad-QS (Phase A)."""
from planning_path_rematch import collect_rematch_slot_indices, rematch_roadmap_slots
from planning_progression_roadmap import ProgressionRoadmapContext, StageSpecArtifact
def _stage_specs():
return [
StageSpecArtifact(major_step_index=0, learning_goal="Grundlage"),
StageSpecArtifact(major_step_index=1, learning_goal="Vertiefung"),
StageSpecArtifact(major_step_index=2, learning_goal="Anwendung"),
]
def test_collect_rematch_slot_indices_from_stripped_with_major_index():
specs = _stage_specs()
stripped = [
{
"step_index": 1,
"roadmap_major_step_index": 1,
"issue": "technique_scope",
"reasons": ["Passt nicht zur Haupttechnik"],
}
]
indices, reasons = collect_rematch_slot_indices(
stripped_off_topic=stripped,
off_topic_steps=[],
optimization_hints=[],
stage_specs=specs,
)
assert indices == {1}
assert "Haupttechnik" in reasons[1]
def test_collect_rematch_slot_indices_resolves_step_index_to_major():
specs = _stage_specs()
off_topic = [
{
"step_index": 2,
"issue": "stage_mismatch",
"reasons": ["Ziel passt nicht"],
}
]
indices, reasons = collect_rematch_slot_indices(
stripped_off_topic=[],
off_topic_steps=off_topic,
optimization_hints=[],
stage_specs=specs,
)
assert indices == {2}
assert reasons[2] == "Ziel passt nicht"
def test_collect_rematch_slot_indices_from_optimization_hints():
specs = _stage_specs()
hints = [
{
"action": "rematch_slot",
"roadmap_major_step_index": 0,
"reason": "QS-Tier-1",
}
]
indices, _ = collect_rematch_slot_indices(
stripped_off_topic=[],
off_topic_steps=[],
optimization_hints=hints,
stage_specs=specs,
)
assert indices == {0}
def test_rematch_roadmap_slots_replaces_only_target_slot():
specs = _stage_specs()
ctx = ProgressionRoadmapContext(
goal_query="Mawashi Geri",
max_steps=3,
stage_specs=specs,
)
steps = [
{
"exercise_id": 10,
"title": "Slot 0 OK",
"roadmap_major_step_index": 0,
},
{
"exercise_id": 20,
"title": "Mae Geri falsch",
"roadmap_major_step_index": 1,
},
{
"exercise_id": 30,
"title": "Slot 2 OK",
"roadmap_major_step_index": 2,
},
]
def _fake_match(cur, *, stage_spec, used, **kwargs):
assert stage_spec.major_step_index == 1
assert 20 not in used
assert 10 in used
return (
{
"exercise_id": 21,
"title": "Sprungkraft Mawashi",
"roadmap_major_step_index": 1,
},
None,
)
ordered, log, unfilled = rematch_roadmap_slots(
None,
tenant=None,
body=None,
goal_query="Mawashi Geri",
max_steps=3,
semantic_brief=None,
path_target_profile=None,
path_intent="",
roadmap_ctx=ctx,
steps=steps,
slot_indices={1},
rematch_reasons={1: "technique_scope"},
match_slot_fn=_fake_match,
)
assert len(ordered) == 3
assert ordered[0]["exercise_id"] == 10
assert ordered[1]["exercise_id"] == 21
assert ordered[2]["exercise_id"] == 30
assert len(log) == 1
assert log[0]["action"] == "replaced"
assert log[0]["replaced_exercise_id"] == 20
assert log[0]["new_exercise_id"] == 21
assert not unfilled