From c3b2ee33101311b08cd4677481298e879ed9c398 Mon Sep 17 00:00:00 2001 From: Lars Date: Tue, 12 Aug 2025 10:27:51 +0200 Subject: [PATCH] =?UTF-8?q?scripts/test=5Fplans=5Fwp15.py=20hinzugef=C3=BC?= =?UTF-8?q?gt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- scripts/test_plans_wp15.py | 92 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 scripts/test_plans_wp15.py diff --git a/scripts/test_plans_wp15.py b/scripts/test_plans_wp15.py new file mode 100644 index 0000000..8ad1426 --- /dev/null +++ b/scripts/test_plans_wp15.py @@ -0,0 +1,92 @@ +#!/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" \ No newline at end of file