Files
Onyscribe-Admin/tests/fakelogin.py
RubenRWU 7d8c226da2 Onyscribe Admin: Initialer Stand
Admin-Plattform für die Tenants von Onyscribe: Editor mit Live-Vorschau (echter Core in Sandbox), Versionen, KI-Agent (Cortecs),
OIDC-Abgleich über die Login-Developer-API, Deploy-Engine (Mittwald, gesperrt ohne DEPLOY_LIVE, Freigabe pro Tenant),
Core-Vertrag (Schema/Manifest), Core-Releases mit Kompatibilitätsprüfung, Canary-Rollout und Rollback, Zustandsabgleich.
Runbook: docs/RUNBOOK_PARALLELTEST.md

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-15 15:30:26 +02:00

60 lines
2.8 KiB
Python

"""In-Memory-Nachbau von login.onyva.dev /api/v1 (Verhalten laut LoginSeite/docs/developer-api.md) für Tests und lokale Entwicklung."""
class FakeLogin:
"""In-Memory-Nachbau von login.onyva.dev /api/v1 (Verhalten laut LoginSeite/docs/developer-api.md)."""
def __init__(self, users=("anna@x.de", "bob@x.de")):
self.apps, self.members, self.users, self.calls, self.n, self.rate_limit_once = {}, {}, set(users), [], 0, False
def __call__(self, method, url, headers, body):
path = url.split("/api/v1")[1]
self.calls.append((method, path, body))
if headers.get("Authorization") != "Bearer onyva_testkey":
return 401, {"detail": "invalid"}, {}
if self.rate_limit_once:
self.rate_limit_once = False
return 429, {"detail": "slow down"}, {"Retry-After": "2"}
parts = path.strip("/").split("/")
if parts == ["apps"]:
if method == "GET":
return 200, {"apps": list(self.apps.values())}, {}
self.n += 1
cid = f"app_{self.n:04d}"
app = {"client_id": cid, "name": body["name"], "slug": body["name"].lower(), "redirect_uris": body["redirect_uris"],
"auth_mode": body.get("auth_mode", "required"), "totp_mode": body.get("totp_mode", "disabled"),
"allow_registration": body.get("allow_registration", False), "backchannel_logout_uri": body.get("backchannel_logout_uri")}
self.apps[cid], self.members[cid] = app, {}
return 201, {**app, "client_secret": f"secret-{self.n}"}, {}
cid = parts[1]
if cid not in self.apps:
return 404, {"detail": "not found"}, {}
if len(parts) == 2:
if method == "GET":
return 200, self.apps[cid], {}
if method == "PATCH":
self.apps[cid].update(body)
return 200, self.apps[cid], {}
if method == "DELETE":
del self.apps[cid]
return 204, None, {}
if parts[2] == "rotate-secret":
self.n += 1
return 200, {"client_id": cid, "client_secret": f"rotated-{self.n}"}, {}
if parts[2] == "memberships":
if method == "GET":
return 200, {"memberships": [{"email": e, "display_name": e, "role": r} for e, r in self.members[cid].items()]}, {}
if method == "PUT":
if body["user_email"] not in self.users:
return 404, {"detail": "user"}, {}
self.members[cid][body["user_email"]] = body.get("role", "viewer")
return 200, body, {}
if method == "DELETE":
self.members[cid].pop(parts[3], None)
return 204, None, {}
return 404, {}, {}
@property
def writes(self):
return [c for c in self.calls if c[0] != "GET"]