#!/usr/bin/env python3 """ Extended live smoke test against the production Manager. Exercises: A. Routing surface - /api/status → 200 healthy - /api/devices/ → 401 (auth-protected, route mounted) - /api/devices/pair → 401 without auth - /api/devices/12345 PATCH/DELETE → 401 without auth - Web SPA index → 200 (assets loadable) - /v1/models with no auth → 401 (legacy v1 path still gated) B. V2 diagnostic header contract Exercises FIVE distinct V2 failure modes and asserts that each returns: - HTTP 401 - X-Heicode-Server-Time present, parseable, within 60s of local - X-Heicode-Auth-Error = expected machine-readable code This pins the whole error-code mapping we shipped in fix(devices). C. Legacy compat - V1 sk- request to /v1/models without bearer → 401 - V1 sk- request with bogus bearer "sk-xxx" → 401 Confirms the 30-day legacy compat path still runs (V2 dispatcher isn't accidentally swallowing non-V2 traffic). Exit code: 0 == all green, 1 == any failure. """ import sys import time import urllib.request import urllib.error BASE = "https://code.xinghanlab.com" UA = "heicode-smoke-extended/1.0" FAILS = [] # Honour HTTPS_PROXY env (clash/wireguard/etc) — urllib doesn't pick # it up automatically on Windows. Needed when the local machine has a # DNS hijack returning sinkhole IPs for code.xinghanlab.com. import os _proxy = os.environ.get("HTTPS_PROXY") or os.environ.get("https_proxy") if _proxy: proxy_handler = urllib.request.ProxyHandler({"https": _proxy, "http": _proxy}) _opener = urllib.request.build_opener(proxy_handler) else: _opener = urllib.request.build_opener() def http(method, path, headers=None, body=None): req = urllib.request.Request(BASE + path, method=method, data=body) req.add_header("User-Agent", UA) for k, v in (headers or {}).items(): req.add_header(k, v) try: r = _opener.open(req, timeout=15) return r.status, {k.lower(): v for k, v in r.headers.items()}, r.read().decode("utf-8", "replace") except urllib.error.HTTPError as e: return e.code, {k.lower(): v for k, v in e.headers.items()}, e.read().decode("utf-8", "replace") def check(label, ok, detail=""): print(f"[{'OK ' if ok else 'FAIL'}] {label}" + (f" -- {detail}" if detail else "")) if not ok: FAILS.append(label) import base64 # Exactly 32 bytes (0x41 * 32) — valid X25519 pubkey LENGTH (every # 32-byte sequence is a valid X25519 point per spec). Using "wrong # byte count" pubkey strings here would short-circuit the dispatcher # at eph_pubkey_malformed before we can test the downstream code # paths, so always start from a 32-byte default. VALID_LEN_EPH_PUBKEY = base64.standard_b64encode(b"\x41" * 32).decode() def v2_headers(eph_pubkey_b64=VALID_LEN_EPH_PUBKEY, timestamp_ms=None, nonce="dead" * 8): return { "Content-Encoding": "heicode-aead-v1", "X-Heicode-Device-Id": "smoke-bogus", "X-Heicode-Timestamp": str(timestamp_ms or int(time.time() * 1000)), "X-Heicode-Nonce": nonce, "X-Heicode-Fingerprint": "0" * 64, "X-Heicode-Eph-Pubkey": eph_pubkey_b64, "X-Heicode-Signature": "BBBBBBBB", "Content-Type": "application/json", } def assert_v2_failure(label, headers, body, expected_code): code, hdrs, _ = http("POST", "/v1/messages", headers=headers, body=body) diag = hdrs.get("x-heicode-auth-error", "") ts = hdrs.get("x-heicode-server-time", "") ts_ok = False try: ts_ok = ts and abs(int(ts) - int(time.time() * 1000)) < 60_000 except ValueError: pass ok = code == 401 and diag == expected_code and ts_ok check(label, ok, f"HTTP {code} code={diag!r} time={ts!r}") # ---------------------------------------------------------------- A. Routing print(f"\n=== A. Routing surface ({BASE}) ===\n") code, _, body = http("GET", "/api/status") check("/api/status returns 200 + success:true", code == 200 and '"success":true' in body, f"HTTP {code}") code, _, _ = http("GET", "/api/devices/") check("/api/devices/ unauthenticated → 401", code == 401, f"HTTP {code}") code, _, _ = http("POST", "/api/devices/pair", headers={"Content-Type": "application/json"}, body=b'{"device_id":"x","public_key":"","fingerprint":""}') check("/api/devices/pair unauthenticated → 401", code == 401, f"HTTP {code}") code, _, _ = http("PATCH", "/api/devices/99999", headers={"Content-Type": "application/json"}, body=b'{"device_name":"hax"}') check("/api/devices/:id PATCH unauthenticated → 401", code == 401, f"HTTP {code}") code, _, _ = http("DELETE", "/api/devices/99999") check("/api/devices/:id DELETE unauthenticated → 401", code == 401, f"HTTP {code}") code, _, body = http("GET", "/") check("Web SPA index → 200", code == 200 and "