#!/usr/bin/env python3 """API-Test fuer Einstellungen + Auto-Reset (offener Modus). Lauf: python3 tests/test_api_settings.py """ import json import os import shutil import subprocess import sys import time import urllib.request import urllib.error BASE = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) STATE = "/tmp/tp_state2" PORT = 8255 BASE_URL = f"http://127.0.0.1:{PORT}" PASS = 0 FAIL = 0 def check(name, cond, detail=""): global PASS, FAIL if cond: PASS += 1 print(f" ok {name}") else: FAIL += 1 print(f"FAIL {name} {detail}") def req(method, path, body=None): url = BASE_URL + path data = None headers = {} if body is not None: data = json.dumps(body).encode("utf-8") headers["Content-Type"] = "application/json" r = urllib.request.Request(url, data=data, headers=headers, method=method) try: with urllib.request.urlopen(r, timeout=5) as resp: return resp.status, json.loads(resp.read().decode("utf-8")) except urllib.error.HTTPError as e: try: return e.code, json.loads(e.read().decode("utf-8")) except Exception: return e.code, {} def main(): if os.path.exists(STATE): shutil.rmtree(STATE) os.makedirs(STATE, exist_ok=True) env = dict(os.environ, TRAINING_STATE_DIR=STATE, PORT=str(PORT)) proc = subprocess.Popen( [sys.executable, os.path.join(BASE, "server.py")], env=env, cwd=BASE, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) try: for _ in range(40): time.sleep(0.25) try: urllib.request.urlopen(BASE_URL + "/api/meta", timeout=1) break except Exception: pass else: print("Server nicht erreichbar") sys.exit(1) st, s = req("GET", "/api/settings") check("Settings Defaults", st == 200 and s["theme"] == "dark" and s["accent"] == "white" and s["autoReset"] is False and s["showTips"] is True, s) st, s = req("PUT", "/api/settings", {"theme": "light", "accent": "green", "autoReset": True, "showTips": False}) check("Settings speichern", st == 200 and s["theme"] == "light" and s["accent"] == "green" and s["autoReset"] is True and s["showTips"] is False, s) st, s = req("GET", "/api/settings") check("Settings persistiert", s["theme"] == "light" and s["autoReset"] is True and s["showTips"] is False, s) st, s = req("PUT", "/api/settings", {"autoReset": False}) check("Teil-Update", s["autoReset"] is False and s["theme"] == "light", s) req("PUT", "/api/settings", {"autoReset": True, "showTips": True}) ex = "Push/Bankdrücken KH (je Hand)" st, done = req("POST", "/api/sessions", {"activity": "gym", "day": "Push"}) did = done["id"] sets = [] for i, w in enumerate(["18", "18,5", "20", ""]): sets.append({"exercise": ex, "sort": i, "weight": w, "reps": "10", "done": 1 if i < 3 else 0}) req("PATCH", f"/api/sessions/{did}", {"sets": sets}) req("PATCH", f"/api/sessions/{did}", {"meta": {"status": "done"}}) req("PUT", "/api/settings", {"autoReset": False}) st, s1 = req("POST", "/api/sessions", {"activity": "gym", "day": "Push"}) check("AutoReset OFF: keine vorbefuellten Sets", len(s1["entries"]) == 0, s1["entries"]) req("DELETE", f"/api/sessions/{s1['id']}") req("PUT", "/api/settings", {"autoReset": True}) st, s2 = req("POST", "/api/sessions", {"activity": "gym", "day": "Push"}) entries = s2["entries"] check("AutoReset ON: nur 3 gefuellte Sets geladen", len(entries) == 3, entries) check("AutoReset ON: Gewichte uebernommen", [e["weight"] for e in entries] == ["18", "18,5", "20"], [e["weight"] for e in entries]) check("AutoReset ON: Abhak-Status zurueckgesetzt", all(e["done"] == 0 for e in entries), entries) check("AutoReset ON: Reps uebernommen", all(e["reps"] == "10" for e in entries), entries) new_sets = [{"exercise": ex, "sort": 0, "weight": "999", "reps": "1", "done": 0}] req("PATCH", f"/api/sessions/{s2['id']}", {"sets": new_sets}) st, filled = req("POST", f"/api/sessions/{s2['id']}/autofill") check("Autofill: Saetze ersetzt", st == 200 and len(filled["entries"]) == 3 and filled["entries"][0]["weight"] == "18", filled["entries"]) exB = "Push/Schrägbank KH (je Hand)" req("PATCH", f"/api/sessions/{s2['id']}", {"sets": [{"exercise": exB, "sort": 0, "weight": "14", "reps": "12", "done": 0}]}) st, filled = req("POST", f"/api/sessions/{s2['id']}/autofill") b = [e for e in filled["entries"] if e["exercise"] == exB] check("Autofill: Uebung ohne Verlauf unberuehrt", b and b[0]["weight"] == "14", filled["entries"]) req("PUT", "/api/settings", {"autoReset": "ja", "showTips": 1, "bogus": 42, "theme": "oled"}) st, s = req("GET", "/api/settings") check("Typen + unbekannte Keys", s["autoReset"] is True and s["showTips"] is True and "bogus" not in s and s["theme"] == "oled", s) # --- Plan-Gewichte vorbefuellen ------------------------------------ req("PUT", "/api/settings", {"autoReset": False, "planPrefill": True}) st, s3 = req("POST", "/api/sessions", {"activity": "gym", "day": "Push"}) bd = [e for e in s3["entries"] if e["exercise"] == "Push/Bankdrücken KH (je Hand)"] check("PlanPrefill: alle Plan-Saetze mit startKg", len(bd) == 4 and all(e["weight"] == "18" for e in bd), bd) check("PlanPrefill: alle Uebungen des Tags befuellt", len({e["exercise"] for e in s3["entries"]}) == 8, len(s3["entries"])) check("PlanPrefill: Reps leer, done=0", all(e["reps"] == "" and e["done"] == 0 for e in s3["entries"]), s3["entries"]) req("DELETE", f"/api/sessions/{s3['id']}") # Auto-Reset hat Vorrang vor Plan-Gewichten (je Uebung) req("PUT", "/api/settings", {"autoReset": True, "planPrefill": True}) st, s4 = req("POST", "/api/sessions", {"activity": "gym", "day": "Push"}) bd4 = [e for e in s4["entries"] if e["exercise"] == "Push/Bankdrücken KH (je Hand)"] sb4 = [e for e in s4["entries"] if e["exercise"] == "Push/Schrägbank KH (je Hand)"] check("PlanPrefill+AutoReset: letztes Workout gewinnt", len(bd4) == 3 and [e["weight"] for e in bd4] == ["18", "18,5", "20"], bd4) check("PlanPrefill+AutoReset: ohne Verlauf Plan-Gewicht", len(sb4) == 3 and all(e["weight"] == "14" for e in sb4), sb4) req("DELETE", f"/api/sessions/{s4['id']}") req("PUT", "/api/settings", {"theme": "dark", "oled": False, "accent": "white", "autoReset": False, "planPrefill": False, "showTips": True, "language": "de", "setupDone": False, "onboarded": False, "weightUnit": "kg", "startTab": "today"}) st, s = req("GET", "/api/settings") check("Zurueck auf Standard", s == { "theme": "dark", "oled": False, "accent": "white", "autoReset": False, "planPrefill": False, "showTips": True, "language": "de", "setupDone": False, "onboarded": False, "weightUnit": "kg", "startTab": "today"}, s) finally: proc.terminate() proc.wait(timeout=5) print(f"\n{PASS} ok, {FAIL} fail") sys.exit(1 if FAIL else 0) if __name__ == "__main__": main()