55 lines
2.9 KiB
Python
55 lines
2.9 KiB
Python
|
|
"""Exercise the installed HTTP entrypoint with metadata-only output. Port 8080 must be free."""
|
||
|
|
|
||
|
|
import argparse, json, os, socket, subprocess, sys, time
|
||
|
|
from urllib.request import build_opener, HTTPRedirectHandler, ProxyHandler, Request
|
||
|
|
from urllib.error import HTTPError, URLError
|
||
|
|
from urllib.parse import urlsplit, parse_qs
|
||
|
|
from pathlib import Path
|
||
|
|
parser = argparse.ArgumentParser(description="Local auth-shell smoke; never follows the issuer redirect.")
|
||
|
|
parser.add_argument("--receipt", type=Path)
|
||
|
|
args = parser.parse_args()
|
||
|
|
|
||
|
|
class NoRedirect(HTTPRedirectHandler):
|
||
|
|
def redirect_request(self, *args): return None
|
||
|
|
opener = build_opener(ProxyHandler({}), NoRedirect())
|
||
|
|
with socket.socket() as sock:
|
||
|
|
sock.bind(("127.0.0.1", 8080))
|
||
|
|
env = {**os.environ, "INFD_KEYCAPE_ISSUER": "https://kc.coulomb.social"}
|
||
|
|
proc = subprocess.Popen([str(Path(sys.executable).with_name("informed-decision-web"))], env=env, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||
|
|
checks = {}
|
||
|
|
try:
|
||
|
|
for attempt in range(50):
|
||
|
|
try:
|
||
|
|
with opener.open("http://127.0.0.1:8080/healthz", timeout=1) as r:
|
||
|
|
checks["installed_entrypoint_health"] = r.status == 200 and json.load(r) == {"status": "ok"}
|
||
|
|
break
|
||
|
|
except URLError:
|
||
|
|
if proc.poll() is not None: raise RuntimeError("service failed to start")
|
||
|
|
time.sleep(0.1)
|
||
|
|
assert checks.get("installed_entrypoint_health")
|
||
|
|
try: opener.open("http://127.0.0.1:8080/auth/start", timeout=2)
|
||
|
|
except HTTPError as r:
|
||
|
|
parsed=urlsplit(r.headers["Location"]); q=parse_qs(parsed.query)
|
||
|
|
checks["fixed_issuer_authorization_redirect"] = r.code==303 and parsed.scheme=="https" and parsed.netloc=="kc.coulomb.social" and parsed.path=="/authorize"
|
||
|
|
checks["exact_registered_callback_and_scopes"] = q["redirect_uri"]==["https://decisions.coulomb.social/auth/callback"] and q["scope"]==["openid approval:read approval:approve"]
|
||
|
|
checks["pkce_s256_and_browser_cookie"] = q["code_challenge_method"]==["S256"] and "Secure; HttpOnly; SameSite=Lax" in r.headers["Set-Cookie"]
|
||
|
|
r.close()
|
||
|
|
try: opener.open("http://127.0.0.1:8080/readyz", timeout=2)
|
||
|
|
except HTTPError as r:
|
||
|
|
checks["incomplete_approval_path_not_ready"] = r.code==503 and json.load(r)["reason"]=="approval_path_not_connected"
|
||
|
|
r.close()
|
||
|
|
try: opener.open(Request("http://127.0.0.1:8080/approvals/fixture/accept", data=b"{}"), timeout=2)
|
||
|
|
except HTTPError as r:
|
||
|
|
checks["binding_route_unavailable"] = r.code==404
|
||
|
|
r.close()
|
||
|
|
assert len(checks)==6 and all(checks.values())
|
||
|
|
receipt={"status":"passed", "checks":checks,"issuer_contacted":False,"native_human_login":False,"model_calls":0}
|
||
|
|
if args.receipt:
|
||
|
|
args.receipt.write_text(json.dumps(receipt,indent=2)+"\n")
|
||
|
|
print(json.dumps(receipt,indent=2))
|
||
|
|
finally:
|
||
|
|
proc.terminate()
|
||
|
|
try: proc.communicate(timeout=5)
|
||
|
|
except subprocess.TimeoutExpired:
|
||
|
|
proc.kill(); proc.communicate()
|