Files
trainingsplan/tests/test_api_settings.py
AI-Agent 8fc77212cb Startpunkt 02.10.2026: aktueller Stand des Trainingsplans
Historie bewusst neu aufgesetzt, ein einziger Commit als neuer Ausgangspunkt.
2026-10-02 01:23:25 +02:00

193 lines
7.8 KiB
Python

#!/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()