platform: modularize api/gui, add docs-tests-web foundation, and refresh root config

This commit is contained in:
beckline
2026-03-26 22:40:54 +03:00
parent 0e2d7f61ea
commit 6a56d734c2
562 changed files with 70151 additions and 16423 deletions

105
tests/events_stream.py Executable file
View File

@@ -0,0 +1,105 @@
#!/usr/bin/env python3
"""SSE smoke test with active event trigger via /api/v1/trace/append."""
import http.client
import json
import os
import sys
import threading
import time
from urllib.parse import urlparse
API_URL = os.environ.get("API_URL", "http://127.0.0.1:8080")
TIMEOUT = float(os.environ.get("EVENTS_TIMEOUT_SEC", "12"))
EVENT_REQUIRED = "trace_append"
def parse_base(api_url: str):
parsed = urlparse(api_url)
if parsed.scheme != "http":
raise ValueError("only http API_URL is supported for this smoke test")
host = parsed.hostname or "127.0.0.1"
port = parsed.port or 80
base_path = parsed.path.rstrip("/")
return host, port, base_path
def post_trace_append(host: str, port: int, base_path: str):
# Small delay to ensure SSE subscription is active before trigger.
time.sleep(1.0)
conn = http.client.HTTPConnection(host, port, timeout=8)
body = json.dumps({"kind": "gui", "line": f"sse-probe-{int(time.time())}"})
path = f"{base_path}/api/v1/trace/append"
conn.request("POST", path, body=body, headers={"Content-Type": "application/json"})
resp = conn.getresponse()
payload = resp.read().decode("utf-8", errors="ignore")
if resp.status != 200:
raise RuntimeError(f"trace/append failed: HTTP {resp.status}, body={payload}")
conn.close()
def main():
host, port, base_path = parse_base(API_URL)
stream_path = f"{base_path}/api/v1/events/stream"
conn = http.client.HTTPConnection(host, port, timeout=TIMEOUT)
conn.putrequest("GET", stream_path)
conn.putheader("Accept", "text/event-stream")
conn.putheader("Cache-Control", "no-cache")
conn.putheader("Connection", "keep-alive")
conn.endheaders()
resp = conn.getresponse()
if resp.status != 200:
print(f"[events] unexpected HTTP {resp.status}", file=sys.stderr)
sys.exit(1)
content_type = resp.getheader("Content-Type", "")
if "text/event-stream" not in content_type:
print(f"[events] bad Content-Type: {content_type}", file=sys.stderr)
sys.exit(1)
trigger_err = []
def trigger():
try:
post_trace_append(host, port, base_path)
except Exception as exc:
trigger_err.append(str(exc))
t = threading.Thread(target=trigger, daemon=True)
t.start()
got_id = False
got_required = False
deadline = time.time() + TIMEOUT
while time.time() < deadline:
raw = resp.readline()
if not raw:
break
line = raw.decode("utf-8", errors="ignore").strip()
if line.startswith("id:"):
got_id = True
if line.startswith("event:"):
event = line.split(":", 1)[1].strip()
if event == EVENT_REQUIRED:
got_required = True
print(f"[events] got required event: {event}")
break
resp.close()
conn.close()
t.join(timeout=0.5)
if trigger_err:
print(f"[events] trigger failed: {trigger_err[0]}", file=sys.stderr)
sys.exit(1)
if not got_id:
print("[events] no SSE event id observed", file=sys.stderr)
sys.exit(1)
if not got_required:
print(f"[events] missing required event: {EVENT_REQUIRED}", file=sys.stderr)
sys.exit(1)
print("[events] stream smoke passed")
if __name__ == "__main__":
main()