Spaces:
Running
Running
File size: 4,828 Bytes
f316f5a 23040f5 1c2dd4b 23040f5 f316f5a 23040f5 f316f5a 23040f5 f316f5a 1c2dd4b f316f5a 1c2dd4b f316f5a 23040f5 f316f5a 23040f5 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 | """End-to-end test for Retro Alpha API (LLM endpoints only — game runs in browser)."""
import os
import sys
# Force UTF-8 stdout for ₹ symbol on Windows
try:
sys.stdout.reconfigure(encoding="utf-8")
except Exception:
pass
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from fastapi.testclient import TestClient
import app as app_module
client = TestClient(app_module.app)
PASSED = 0
FAILED = 0
def check(name, condition, detail=""):
global PASSED, FAILED
if condition:
PASSED += 1
print(f" PASS: {name}")
else:
FAILED += 1
print(f" FAIL: {name} {detail}")
print("=== Static assets ===")
r = client.get("/")
check("homepage 200", r.status_code == 200)
check("homepage has RETRO ALPHA", "RETRO ALPHA" in r.text)
check("homepage loads engine.js", "/static/engine.js" in r.text)
check("homepage loads events.js", "/static/events.js" in r.text)
check("homepage loads app.js", "/static/app.js" in r.text)
check("homepage loads style.css", "/static/style.css" in r.text)
check("homepage has chat panel", "chat-panel" in r.text)
check("homepage has positions table", "positions-table" in r.text)
check("homepage has market watch", "market-watch" in r.text)
r = client.get("/static/style.css")
check("style.css 200", r.status_code == 200)
check("style.css has scanlines", "scanlines" in r.text)
check("style.css has kite-grid", "kite-grid" in r.text)
r = client.get("/static/engine.js")
check("engine.js 200", r.status_code == 200)
check("engine.js has newGame", "newGame" in r.text)
check("engine.js has advanceMonth", "advanceMonth" in r.text)
check("engine.js has localAgentDecide", "localAgentDecide" in r.text)
r = client.get("/static/events.js")
check("events.js 200", r.status_code == 200)
check("events.js has eventForMonth", "eventForMonth" in r.text)
check("events.js has dot-com", "Dot-com" in r.text)
check("events.js has 9/11", "9/11" in r.text)
r = client.get("/static/app.js")
check("app.js 200", r.status_code == 200)
check("app.js uses RetroEngine", "RetroEngine" in r.text)
print("\n=== Health ===")
r = client.get("/api/health")
check("health 200", r.status_code == 200)
h = r.json()
check("health status ok", h.get("status") == "ok")
check("health reports llm", "llm" in h)
print("\n=== Game endpoints removed (state is local now) ===")
for ep in ["/api/state", "/api/trade", "/api/advance", "/api/reset"]:
r = client.get(ep) if ep == "/api/state" else client.post(ep)
check(f"{ep} returns 404 (removed)", r.status_code == 404, f"got {r.status_code}")
print("\n=== /api/chat ===")
r = client.post("/api/chat", json={"message": "should I buy Nifty?", "snapshot": {
"cash": 500000, "total_value": 1000000, "unrealized_pnl": 0,
"positions": [{"asset": "Gold", "qty": 1.0, "price": 3000, "value": 3000}],
}})
check("chat 200", r.status_code == 200)
data = r.json()
check("chat has reply", "reply" in data and len(data["reply"]) > 0)
# Regression: no raw "error: format only" leaks from the mock
check("chat never returns 'error: format only'",
"error: format only" not in data["reply"], f"reply='{data['reply']}'")
check("chat reply looks like real commentary (has ₹ or words)",
("₹" in data["reply"] or len(data["reply"].split()) >= 4),
f"reply='{data['reply']}'")
r = client.post("/api/chat", json={"message": "", "snapshot": {}})
check("empty message rejected", r.status_code == 400)
print("\n=== /api/insight ===")
r = client.post("/api/insight", json={
"event": {"headline": "Test crash", "regime": "market_crash"},
"snapshot": {"unrealized_pnl": -100000, "cash": 500000, "total_value": 900000},
})
check("insight 200", r.status_code == 200)
data = r.json()
check("insight has text", "insight" in data and len(data["insight"]) > 0)
check("insight never returns 'error: format only'",
"error: format only" not in data["insight"], f"insight='{data['insight']}'")
print("\n=== /api/mentor ===")
r = client.post("/api/mentor", json={"summary": {
"year": 1995, "month": 4, "starting_value": 1000000, "ending_value": 1500000,
"invested_value": 800000, "cash": 700000, "unrealized_pnl": 200000,
"max_drawdown": -0.15, "sharpe_ratio": 1.2,
"allocations": {"fd": 0.2, "nifty_50": 0.4, "gold": 0.1, "crypto": 0.1},
}})
check("mentor 200", r.status_code == 200)
data = r.json()
rev = data.get("review", {})
check("mentor has roast", "roast" in rev and rev["roast"] != "Could not parse review.")
check("mentor has lesson", "lesson" in rev)
check("mentor has suggestion", "suggestion" in rev)
check("no Parse error leak", "Parse error" not in rev.get("lesson", ""))
print(f"\n{'='*40}")
print(f"PASSED: {PASSED} FAILED: {FAILED}")
print(f"{'='*40}")
if FAILED:
print("SOME TESTS FAILED")
sys.exit(1)
else:
print("ALL E2E TESTS PASSED")
|