"""Unit-Tests für gewichtetes Fähigkeiten-Scoring (Phase 3).""" from skill_scoring import ( ExerciseOccurrence, compute_skill_profile, match_score_for_skill_ids, _club_universal_percent, _level_range_multiplier, _skill_link_multiplier, ) def test_skill_link_multiplier_intensity_and_levels(): assert _skill_link_multiplier(intensity="hoch") == 1.2 assert _skill_link_multiplier(intensity="niedrig") == 0.85 wide = _skill_link_multiplier( intensity="mittel", required_level="basis", target_level="optimierung", ) narrow = _skill_link_multiplier( intensity="mittel", required_level="grundlagen", target_level="grundlagen", ) assert wide > narrow def test_level_range_multiplier_span(): assert _level_range_multiplier(None, None) == 1.0 assert _level_range_multiplier("aufbau", "fortgeschritten") > _level_range_multiplier("basis", "basis") def test_compute_skill_profile_aggregates_weights(): occurrences = [ ExerciseOccurrence(exercise_id=1, planned_duration_min=60), ExerciseOccurrence(exercise_id=1, planned_duration_min=30), ] skills_map = { 1: [ { "skill_id": 10, "skill_name": "Distanz", "category": "kihon", "category_id": 1, "category_name": "kihon", "main_category_id": 100, "main_category_name": "Technik", "intensity": "hoch", "required_level": "grundlagen", "target_level": "aufbau", "exercise_title": "Übung A", }, { "skill_id": 11, "skill_name": "Balance", "category": "kihon", "category_id": 1, "category_name": "kihon", "main_category_id": 100, "main_category_name": "Technik", "intensity": "niedrig", "required_level": "basis", "target_level": "basis", "exercise_title": "Übung A", }, ], } profile = compute_skill_profile(occurrences, skills_map) assert profile["scoring_version"] == "1.2" assert profile["exercise_occurrence_count"] == 2 assert profile["distinct_exercise_count"] == 1 assert len(profile["skills"]) == 2 assert profile["skills"][0]["skill_id"] == 10 assert profile["total_weight"] > profile["skills"][1]["weight"] assert abs(sum(s["share_percent"] for s in profile["skills"]) - 100.0) < 0.1 assert len(profile["by_main_category"]) == 1 assert profile["by_main_category"][0]["categories"][0]["top_skill"]["skill_id"] == 10 def test_universal_percent_against_corpus_max(): occurrences = [ExerciseOccurrence(exercise_id=1, planned_duration_min=50)] skills_map = { 1: [ { "skill_id": 10, "skill_name": "Koordination", "category_name": "Koordination", "category_id": 2, "main_category_id": 200, "main_category_name": "Körper", }, ], } profile = compute_skill_profile( occurrences, skills_map, reference_max_by_skill={10: 100.0}, ) assert profile["has_reference_scale"] is True assert profile["skills"][0]["universal_percent"] == 50.0 assert profile["skills"][0]["is_club_best_for_skill"] is False def test_club_universal_percent_capped_at_100(): pct, is_best = _club_universal_percent(150.0, 100.0) assert pct == 100.0 assert is_best is True pct2, _ = _club_universal_percent(72.6, 100.0) assert pct2 == 72.6 def test_match_score_for_skill_ids(): profile = { "total_weight": 100.0, "skills": [ {"skill_id": 1, "skill_name": "A", "weight": 40.0}, {"skill_id": 2, "skill_name": "B", "weight": 60.0}, ], } m = match_score_for_skill_ids(profile, [1]) assert m["match_weight"] == 40.0 assert m["match_score"] == 40.0 assert m["match_percent"] == 40.0 assert m["artifact_focus_percent"] == 40.0 assert m["matched_skill_ids"] == [1]