#!/usr/bin/env python3 # -*- coding: utf-8 -*- """Unit-, Integrations- und Mini-E2E-Tests für WP-15.""" import os import requests import json from datetime import datetime, timezone BASE = os.getenv("BASE_URL", "http://127.0.0.1:8000").rstrip("/") # ---------- Unit ---------- def _fingerprint_local(plan_payload: dict) -> str: import hashlib core = { "title": plan_payload["title"], "total_minutes": int(plan_payload["total_minutes"]), "items": [ {"exercise_external_id": it["exercise_external_id"], "duration": int(it["duration"])} for sec in plan_payload["sections"] for it in sec.get("items", []) ], } raw = json.dumps(core, sort_keys=True, ensure_ascii=False) return hashlib.sha256(raw.encode("utf-8")).hexdigest() def test_fingerprint_unit(): p = { "title": "Montag - Reaktion", "total_minutes": 90, "sections": [ {"name": "Warmup", "minutes": 15, "items": [ {"exercise_external_id": "ex:001", "duration": 10, "why": "Aufwärmen"} ]} ], } fp1 = _fingerprint_local(p) p2 = json.loads(json.dumps(p, ensure_ascii=False)) fp2 = _fingerprint_local(p2) assert fp1 == fp2 # ---------- Integration / E2E ---------- def test_template_plan_e2e(): # 1) Template anlegen tpl = { "name": "Standard 90min", "discipline": "Karate", "age_group": "Teenager", "target_group": "Breitensport", "total_minutes": 90, "sections": [ {"name": "Warmup", "target_minutes": 15, "must_keywords": ["Reaktion"], "forbid_keywords": [], "capability_targets": {"Reaktionsfähigkeit": 2}} ], "goals": ["Technik", "Kondition"], "equipment_allowed": ["Bälle"], "created_by": "tester", "version": "1.0" } r1 = requests.post(f"{BASE}/plan_templates", json=tpl) assert r1.status_code == 200, r1.text tpl_id = r1.json()["id"] # 2) Plan anlegen (aus Template, minimal) plan = { "template_id": tpl_id, "title": "KW32 – Montag", "discipline": "Karate", "age_group": "Teenager", "target_group": "Breitensport", "total_minutes": 90, "sections": [ {"name": "Warmup", "minutes": 15, "items": [ {"exercise_external_id": "ex:001", "duration": 10, "why": "Aufwärmen"} ]} ], "goals": ["Technik", "Kondition"], "capability_summary": {"Reaktionsfähigkeit": 2}, "created_by": "tester", "created_at": datetime.now(timezone.utc).isoformat(), "source": "test" } r2 = requests.post(f"{BASE}/plan", json=plan) assert r2.status_code == 200, r2.text plan_id = r2.json()["id"] # 3) Idempotenz – gleicher Plan → gleiche ID r3 = requests.post(f"{BASE}/plan", json=plan) assert r3.status_code == 200 assert r3.json()["id"] == plan_id # 4) GET /plan/{id} r4 = requests.get(f"{BASE}/plan/{plan_id}") assert r4.status_code == 200 js = r4.json() assert js["title"] == "KW32 – Montag" # EN-Dash (–)