riprap-nyc / scripts /dry_run.py
seriffic's picture
deploy: sync all changes from main at 6904684
b9a10ad
"""Quick end-to-end sanity check.
Exercises every public route once and prints a summary. Catches:
- 404/500s on routes
- missing static assets
- broken /api/stream or /api/compare SSE
- missing register data
- hallucination drops > N
Run while the server is up:
python scripts/dry_run.py
"""
from __future__ import annotations
import json
import sys
import time
import httpx
BASE = "http://127.0.0.1:8765"
def check(label: str, fn):
t0 = time.time()
try:
ok, detail = fn()
elapsed = time.time() - t0
marker = "✓" if ok else "✗"
print(f" {marker} {label:42s} ({elapsed:5.2f}s) {detail}")
return ok
except Exception as e:
elapsed = time.time() - t0
print(f" ✗ {label:42s} ({elapsed:5.2f}s) EXCEPTION: {type(e).__name__}: {e}")
return False
def get_status(path: str) -> tuple[bool, str]:
r = httpx.get(BASE + path, timeout=10)
return r.status_code == 200, f"HTTP {r.status_code} ({len(r.content)} bytes)"
def stream_one(query: str) -> tuple[bool, str]:
with httpx.stream("GET", BASE + f"/api/stream?q={query}", timeout=120) as r:
if r.status_code != 200:
return False, f"HTTP {r.status_code}"
steps = 0; final = None
for line in r.iter_lines():
if line.startswith("data: "):
d = json.loads(line[6:])
if d.get("kind") == "step": steps += 1
elif d.get("kind") == "final": final = d
if not final:
return False, f"no final event (steps={steps})"
dropped = len((final.get("audit") or {}).get("dropped") or [])
en = final.get("energy") or {}
return True, (f"steps={steps}, dropped={dropped}, "
f"energy={en.get('local_mwh','?')} mWh local")
def compare_one(a: str, b: str) -> tuple[bool, str]:
with httpx.stream("GET", BASE + f"/api/compare?a={a}&b={b}", timeout=120) as r:
if r.status_code != 200:
return False, f"HTTP {r.status_code}"
finals = {}
steps = 0
for line in r.iter_lines():
if line.startswith("data: "):
d = json.loads(line[6:])
if d.get("kind") == "step": steps += 1
elif d.get("kind") == "final": finals[d.get("side")] = d
if "a" not in finals or "b" not in finals:
return False, f"missing final (got {list(finals)})"
return True, f"both sides done; steps={steps}"
def register_check(asset_class: str) -> tuple[bool, str]:
r = httpx.get(BASE + f"/api/register/{asset_class}", timeout=10)
if r.status_code == 503:
return False, "register not built"
if r.status_code != 200:
return False, f"HTTP {r.status_code}"
data = r.json()
rows = data.get("rows", [])
tiers = {1: 0, 2: 0, 3: 0}
for r_ in rows:
tiers[r_.get("tier", 0)] = tiers.get(r_.get("tier", 0), 0) + 1
return True, f"{len(rows)} rows · tier1={tiers.get(1,0)} t2={tiers.get(2,0)} t3={tiers.get(3,0)}"
def main():
print(f"=== Riprap dry-run vs {BASE} ===\n")
print("[Pages]")
check("/", lambda: get_status("/"))
check("/compare", lambda: get_status("/compare"))
check("/register/schools", lambda: get_status("/register/schools"))
check("/register/nycha", lambda: get_status("/register/nycha"))
check("/static/style.css", lambda: get_status("/static/style.css"))
check("/static/app.js", lambda: get_status("/static/app.js"))
check("/static/compare.js", lambda: get_status("/static/compare.js"))
check("/static/register.js",lambda: get_status("/static/register.js"))
fontf = "/static/vendor/nyco/fonts/IBM-Plex-Sans/IBMPlexSans-Regular.woff2"
check(fontf, lambda: get_status(fontf))
print("\n[API: layer endpoints]")
check("/api/layers/sandy", lambda: get_status("/api/layers/sandy?lat=40.59&lon=-73.77&r=1500"))
check("/api/layers/dep_extreme_2080",
lambda: get_status("/api/layers/dep_extreme_2080?lat=40.59&lon=-73.77&r=1500"))
check("/api/floodnet_near", lambda: get_status("/api/floodnet_near?lat=40.59&lon=-73.77&r=1000"))
print("\n[API: register endpoints]")
check("/api/register/schools", lambda: register_check("schools"))
check("/api/register/nycha", lambda: register_check("nycha"))
print("\n[Streams]")
check("stream · 180 Beach 35 St",
lambda: stream_one("180 Beach 35 St, Queens"))
check("stream · Empire State (cleaner case)",
lambda: stream_one("350 5 Avenue, Manhattan"))
check("compare · Hollis vs Empire State",
lambda: compare_one("153-09 90 Avenue Jamaica Queens",
"350 5 Avenue Manhattan"))
if __name__ == "__main__":
sys.exit(main())