diff --git a/scripts/smoke_public_auth_flow.py b/scripts/smoke_public_auth_flow.py new file mode 100644 index 000000000..0e6996c04 --- /dev/null +++ b/scripts/smoke_public_auth_flow.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python3 +"""Public auth/workspace smoke test for deployed Xiaoxia SaaS. + +Creates a throwaway user through the public Web-domain API path and verifies +login, /auth/me, and /workspaces. Uses only the Python standard library. +""" + +from __future__ import annotations + +import argparse +import json +import time +import urllib.error +import urllib.request + + +def request_json(method: str, url: str, payload: dict | None = None, token: str | None = None) -> tuple[int, dict]: + body = json.dumps(payload).encode("utf-8") if payload is not None else None + headers = {"Content-Type": "application/json"} + if token: + headers["Authorization"] = f"Bearer {token}" + request = urllib.request.Request(url, data=body, headers=headers, method=method) + try: + with urllib.request.urlopen(request, timeout=15) as response: + data = response.read().decode("utf-8") + return response.status, json.loads(data) if data else {} + except urllib.error.HTTPError as error: + data = error.read().decode("utf-8") + try: + parsed = json.loads(data) if data else {} + except json.JSONDecodeError: + parsed = {"raw": data} + return error.code, parsed + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--base-url", default="https://saas.xiaoxiajianji.com/api/v1") + args = parser.parse_args() + + timestamp = int(time.time()) + email = f"smoke-{timestamp}@example.com" + username = f"smoke{timestamp}" + password = "AuditPass123!" + + register_status, register_body = request_json( + "POST", + f"{args.base_url}/auth/register", + { + "email": email, + "username": username, + "password": password, + "display_name": username, + }, + ) + print(f"register={register_status}") + if register_status != 201: + print(json.dumps(register_body, ensure_ascii=False)) + return 1 + + login_status, login_body = request_json( + "POST", + f"{args.base_url}/auth/login", + {"email": email, "password": password}, + ) + print(f"login={login_status}") + token = login_body.get("access_token") + if login_status != 200 or not token: + print(json.dumps(login_body, ensure_ascii=False)) + return 1 + + me_status, me_body = request_json("GET", f"{args.base_url}/auth/me", token=token) + print(f"me={me_status}") + if me_status != 200 or me_body.get("email") != email: + print(json.dumps(me_body, ensure_ascii=False)) + return 1 + + workspaces_status, workspaces_body = request_json("GET", f"{args.base_url}/workspaces", token=token) + print(f"workspaces={workspaces_status}") + if workspaces_status != 200 or "workspaces" not in workspaces_body: + print(json.dumps(workspaces_body, ensure_ascii=False)) + return 1 + + print("public_auth_flow=ok") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main())