| """End-to-end HTTP smoke test against a running server (mock LLM mode). |
| Usage: python tests/smoke_http.py [base_url] |
| """ |
| import sys |
|
|
| import requests |
|
|
| BASE = sys.argv[1] if len(sys.argv) > 1 else "http://localhost:7860" |
|
|
|
|
| def post(path, body=None): |
| r = requests.post(f"{BASE}{path}", json=body or {}, timeout=30) |
| assert r.status_code == 200, f"{path} -> {r.status_code}: {r.text[:200]}" |
| return r.json() |
|
|
|
|
| def main(): |
| |
| for path in ("/healthz", "/game", "/static/js/main.js", |
| "/static/css/game.css", "/static/js/chibi.js"): |
| r = requests.get(f"{BASE}{path}", timeout=10) |
| assert r.status_code == 200, f"GET {path} -> {r.status_code}" |
| print("OK shell + static assets") |
|
|
| data = post("/api/new_game") |
| sid = data["session_id"] |
| state = data["state"] |
| assert state["revenue"] == 150_000 and state["pocket_money"] == 3000 |
| assert "morale" not in state, "snapshot leaked morale!" |
| print(f"OK new_game (session {sid[:8]})") |
|
|
| presentations, specials, gifts_done = 0, 0, False |
| while True: |
| data = post("/api/next_event", {"session_id": sid}) |
| event, state = data["event"], data["state"] |
| if event["kind"] == "review": |
| break |
| n = event["crisis_number"] |
| if event["kind"] == "presentation": |
| presentations += 1 |
| rd = post("/api/presentation_round", |
| {"session_id": sid, "response_type": "", "text": ""})["round_data"] |
| rounds = 1 |
| while rd["kind"] == "round": |
| body = {"session_id": sid, "response_type": "custom", |
| "text": "We owned the situation, called the client, " |
| "and turned it into a pilot program."} |
| rd = post("/api/presentation_round", body)["round_data"] |
| if rd["kind"] == "round": |
| rounds += 1 |
| print(f"OK event {n}: presentation, {rounds} rounds, " |
| f"score {rd['score']}, swing {rd['revenue_delta']:+,}") |
| if rd["final"]: |
| break |
| else: |
| if event.get("special"): |
| specials += 1 |
| out = post("/api/respond", { |
| "session_id": sid, "response_type": "custom", |
| "text": "Here is what we do: full honesty with the client, " |
| "and Brad presents the apology as a roadmap.", |
| }) |
| o, state = out["outcome"], out["state"] |
| assert o["animation"], "outcome missing animation" |
| print(f"OK event {n}: {event.get('special') or 'crisis'} " |
| f"[{event['arrival']}] -> {o['revenue_delta']:+,}") |
| |
| if n == 1 and state["phase"] == "free_roam": |
| r = post("/api/idle", {"session_id": sid}) |
| print(f"OK idle roll: {r['idle']['kind']}") |
| if r["idle"]["kind"] == "email_waiting": |
| e = post("/api/read_email", {"session_id": sid})["email"] |
| print(f"OK email read: {e['subject'][:50]}") |
| c = post("/api/chat", {"session_id": sid, "npc_id": "stacey"}) |
| assert c["chat"]["npc_line"] |
| c = post("/api/chat", {"session_id": sid, "npc_id": "stacey", |
| "text": "Hang in there, you do good work."}) |
| assert "relationship_delta" in c["chat"] |
| print("OK chat open+reply with stacey") |
| c2 = post("/api/chat", {"session_id": sid, "npc_id": "derek"}) |
| assert c2["chat"]["npc_line"] |
| r3 = requests.post(f"{BASE}/api/chat", |
| json={"session_id": sid, "npc_id": "janet"}, |
| timeout=30) |
| assert r3.status_code == 409, "third chat must hit the gap cap" |
| print("OK chat caps enforced (3rd chat -> 409)") |
| |
| if not gifts_done and state["phase"] == "free_roam" \ |
| and state["gift_available"]: |
| g = post("/api/gift", {"session_id": sid, "npc_id": "stacey", |
| "tier": "small"}) |
| assert g["result"]["relationship_delta"] > 0 |
| print(f"OK gift -> stacey +{g['result']['relationship_delta']}") |
| gifts_done = True |
|
|
| review = post("/api/review", {"session_id": sid})["review"] |
| print(f"OK review: tier={review['tier']}, revenue " |
| f"${review['final_revenue']:,}, verdict: {review['verdict'][:70]}...") |
| assert presentations >= 1 and review["crises_survived"] == 15 |
| print(f"\nPASS — full quarter: {presentations} presentations, " |
| f"{specials} specials, paper trail {len(review['highlights'])} highlights") |
|
|
|
|
| if __name__ == "__main__": |
| main() |
|
|