"""Tests Planungs-KI Phase E/F — Pfad-QA.""" from planning_exercise_path_builder import _pick_best_path_hit from planning_exercise_semantics import build_semantic_brief from planning_exercise_path_qa import ( apply_llm_path_reorder, detect_path_gaps, is_roadmap_planned_neighbor_pair, ) def test_pick_best_path_hit_prefers_semantic_score(): brief = build_semantic_brief("Mae Geri Perfektion") hits = [ {"id": 1, "title": "Mawashi", "score": 0.9, "semantic_score": 0.1}, {"id": 2, "title": "Mae Geri", "score": 0.75, "semantic_score": 0.85}, ] chosen = _pick_best_path_hit(hits, set(), semantic_brief=brief) assert chosen["id"] == 2 def test_phrase_compact_match_maegeri(): from planning_exercise_semantics import _phrase_in_blob assert _phrase_in_blob("mae geri", "Erlernen des Mae-Geri aus Einzelbewegungen") assert _phrase_in_blob("mae geri", "Maegeri Kihon") def test_pick_best_path_hit_fallback_title_only_in_summary(): from planning_exercise_semantics import pick_best_path_hit brief = build_semantic_brief("Mae Geri Perfektion") hits = [ { "id": 1, "title": "Kumite Stellungen", "summary": "", "score": 0.9, "semantic_score": 0.02, }, { "id": 2, "title": "Einzelbewegungen", "summary": "Schrittweise Erlernen des Mae Geri", "score": 0.5, "semantic_score": 0.08, }, ] chosen = pick_best_path_hit(hits, set(), semantic_brief=brief) assert chosen is not None assert int(chosen["id"]) == 2 def test_pick_best_path_hit_skips_used(): hits = [{"id": 1, "title": "A", "score": 0.5, "semantic_score": 0.5}] assert _pick_best_path_hit(hits, {1}) is None def test_apply_llm_path_reorder_permutation(): steps = [{"exercise_id": 1}, {"exercise_id": 2}, {"exercise_id": 3}] reordered, applied, notes = apply_llm_path_reorder( steps, {"ordered_step_indices": [0, 2, 1], "sequence_notes": ["Vertiefung vor Anwendung"]}, ) assert applied is True assert [s["exercise_id"] for s in reordered] == [1, 3, 2] assert notes def test_is_roadmap_planned_neighbor_pair(): a = {"roadmap_match_source": "stage_spec", "roadmap_major_step_index": 1} b = {"roadmap_match_source": "stage_spec", "roadmap_major_step_index": 2} c = {"roadmap_match_source": "stage_spec", "roadmap_major_step_index": 4} assert is_roadmap_planned_neighbor_pair(a, b) is True assert is_roadmap_planned_neighbor_pair(a, c) is False assert is_roadmap_planned_neighbor_pair({"exercise_id": 1}, b) is False def test_detect_path_gaps_skips_roadmap_neighbors(): brief = build_semantic_brief("Mae Geri") steps = [ { "exercise_id": 1, "title": "A", "roadmap_match_source": "stage_spec", "roadmap_major_step_index": 0, }, { "exercise_id": 2, "title": "B", "roadmap_match_source": "stage_spec", "roadmap_major_step_index": 1, }, ] class _FakeCur: def execute(self, *args, **kwargs): return None def fetchall(self): return [] def fetchone(self): return {"title": "X", "summary": "", "goal": ""} gaps = detect_path_gaps(_FakeCur(), steps, brief=brief, roadmap_first=True) assert gaps == [] def test_apply_llm_path_reorder_invalid_ignored(): steps = [{"exercise_id": 1}, {"exercise_id": 2}] reordered, applied, _ = apply_llm_path_reorder(steps, {"ordered_step_indices": [0, 0]}) assert applied is False assert reordered == steps