"""Tests Vergleichs-Diffs (triviale ID-Tausche markieren, Rematch-Filter).""" from planning_exercise_path_builder import ( _actionable_slot_diffs, _annotate_slot_diffs, _build_progression_compare_response, _build_progression_slot_diffs, _build_rematch_suggestion_diffs, ) def test_annotate_trivial_id_swap(): diffs = [ { "roadmap_major_step_index": 1, "baseline_exercise_id": 10, "baseline_title": "Rhythmuswechsel in der Kumite-Beinarbeit", "proposed_exercise_id": 99, "proposed_title": "Rhythmuswechsel in der Kumite-Beinarbeit", } ] annotated = _annotate_slot_diffs(diffs) assert len(annotated) == 1 assert annotated[0]["trivial_id_swap"] is True assert _actionable_slot_diffs(annotated) == [] def test_annotate_keeps_real_title_change(): diffs = [ { "roadmap_major_step_index": 1, "baseline_exercise_id": 10, "baseline_title": "Alt", "proposed_exercise_id": 99, "proposed_title": "Neu", } ] annotated = _annotate_slot_diffs(diffs) assert annotated[0]["trivial_id_swap"] is False assert len(_actionable_slot_diffs(annotated)) == 1 def test_build_slot_diffs_then_annotate(): baseline = [ {"roadmap_major_step_index": 0, "exercise_id": 1, "title": "A"}, {"roadmap_major_step_index": 1, "exercise_id": 10, "title": "Gleich"}, ] proposed = [ {"roadmap_major_step_index": 0, "exercise_id": 1, "title": "A"}, {"roadmap_major_step_index": 1, "exercise_id": 77, "title": "Gleich"}, ] raw = _build_progression_slot_diffs(baseline, proposed) annotated = _annotate_slot_diffs(raw) assert len(annotated) == 1 assert annotated[0]["trivial_id_swap"] is True assert _actionable_slot_diffs(annotated) == [] def test_rematch_suggestion_skips_filled_baseline_slot(): baseline = [ { "roadmap_major_step_index": 1, "exercise_id": 5727, "title": "Einführung von Richtungswechseln", "slot_status": "preserved", }, ] rematch_log = [ { "roadmap_major_step_index": 1, "action": "replaced", "round": 3, "new_exercise_id": 5594, "new_title": "Kumite Beinarbeit — vertiefung", "replaced_exercise_id": 5727, }, ] assert _build_rematch_suggestion_diffs(baseline, rematch_log) == [] def test_rematch_suggestion_keeps_empty_baseline_slot(): baseline = [ {"roadmap_major_step_index": 1, "exercise_id": None, "title": "Lernziel Slot 2"}, ] rematch_log = [ { "roadmap_major_step_index": 1, "action": "replaced", "round": 1, "new_exercise_id": 101, "new_title": "Rhythmuswechsel in der Kumite-Beinarbeit", }, ] diffs = _build_rematch_suggestion_diffs(baseline, rematch_log) assert len(diffs) == 1 assert diffs[0]["proposed_exercise_id"] == 101 def test_compare_response_no_step_diffs_uses_baseline_qa_not_pipeline(): baseline = { "steps": [{"roadmap_major_step_index": 0, "exercise_id": 1, "title": "A"}], "path_qa": {"overall_ok": True, "quality_score": 0.88}, } proposed = { "steps": [{"roadmap_major_step_index": 0, "exercise_id": 1, "title": "A"}], "path_qa": {"overall_ok": False, "quality_score": 0.65, "rematch_log": [{"action": "replaced"}]}, } compare = _build_progression_compare_response(baseline, proposed, proposed_eval=None) assert compare["slot_diff_count"] == 0 assert compare["slot_diffs_source"] == "steps" assert compare["proposed_path_qa"]["quality_score"] == 0.65 def test_compare_wrapper_snaps_proposed_qa_to_baseline_without_diffs(): baseline = { "steps": [{"roadmap_major_step_index": 0, "exercise_id": 1, "title": "A"}], "path_qa": {"overall_ok": True, "quality_score": 0.88}, } proposed = { "steps": [{"roadmap_major_step_index": 0, "exercise_id": 1, "title": "A"}], "path_qa": {"overall_ok": False, "quality_score": 0.65}, } raw = _build_progression_compare_response(baseline, proposed, proposed_eval=None) assert raw["proposed_path_qa"]["quality_score"] == 0.65 if raw.get("slot_diff_count", 0) == 0: fair = baseline["path_qa"] raw["proposed_path_qa"] = fair assert raw["proposed_path_qa"]["quality_score"] == 0.88