from __future__ import annotations import sys from pathlib import Path sys.path.insert(0, str(Path(__file__).parents[1])) from schema_contract import migrate_training_config, validate_training_config def legacy_config(): return { "meta": { "title": "Alter Plan", "subtitle": "wird reduziert", "output_name": "plan.pdf", }, "colors": {"ACCENT": "#ff00ff"}, "training_format": {"mode": "tabata", "rounds": 8}, "phases": {"items": [{"name": "Phase 1"}]}, "stages": {"squat": {"name": "Squat", "steps": ["leicht", "schwer"]}}, "prepost": { "1": { "warmup": "Handgelenke ยท Kniebeugen", "cooldown": "Atmen
Locker gehen", "stretch": "alte Dehnung", } }, "days": [ { "num": 1, "focus": "Beine", "color": "#123456", "rotations": [ { "label": "Block A", "exercises": [ { "id": "old-placement", "exercise_id": "exercise-squat", "name": "Squat", "key": "squat", "cue": "tief", "result_schema": {"mode": "reps"}, } ], } ], } ], } def test_migration_flattens_legacy_plan_to_days_and_exercises(): migrated = migrate_training_config(legacy_config(), plan_id="test") assert migrated["schema_version"] == 4 assert migrated["contract_version"] == 3 assert migrated["plan_id"] == "test" assert "colors" not in migrated assert "training_format" not in migrated assert "phases" not in migrated assert "stages" not in migrated assert "exercise_catalog" not in migrated assert migrated["meta"] == {"title": "Alter Plan", "subtitle": "wird reduziert"} assert migrated["exercises"] == [{"id": "exercise-squat", "name": "Squat", "cue": "tief"}] day = migrated["days"][0] assert day["name"] == "Beine" assert day["warmup"]["items"] == ["Handgelenke", "Kniebeugen"] assert day["cooldown"]["items"] == ["Atmen", "Locker gehen"] assert "stretch" not in day assert "rotations" not in day item = day["exercises"][0] assert item == { "id": "old-placement", "exercise_id": "exercise-squat", "notes": "Block A", } def test_migration_is_stable_across_repeated_runs(): first = migrate_training_config(legacy_config(), plan_id="test") second = migrate_training_config(first, plan_id="test") assert first == second def test_validation_accepts_complete_minimal_plan(): cfg = { "meta": {"title": "Minimal"}, "exercises": [{"id": "exercise-pushup", "name": "Push-up"}], "days": [ { "id": "day-1", "num": 1, "name": "Tag 1", "warmup": {"items": ["Mobilisieren"]}, "cooldown": {"items": ["Atmen"]}, "exercises": [{"id": "item-1", "exercise_id": "exercise-pushup", "sets": 3, "reps": "8-12"}], } ], } assert validate_training_config(cfg)["errors"] == [] def test_validation_rejects_unknown_day_exercise_reference(): cfg = { "meta": {"title": "Kaputt"}, "exercises": [{"id": "exercise-pushup", "name": "Push-up"}], "days": [ { "id": "day-1", "num": 1, "name": "Tag 1", "exercises": [{"id": "item-1", "exercise_id": "missing"}], } ], } errors = validate_training_config(cfg)["errors"] assert [error["code"] for error in errors] == ["unknown_exercise"]