Spaces:
Running
Running
| """Test script for CryptoRiskEnv β validates all OpenEnv endpoints.""" | |
| import requests | |
| import json | |
| import sys | |
| base = "http://localhost:7860" | |
| errors = [] | |
| def test(name, fn): | |
| try: | |
| fn() | |
| print(f" β {name}") | |
| except Exception as e: | |
| print(f" β {name}: {e}") | |
| errors.append(name) | |
| print("=" * 60) | |
| print(" CryptoRiskEnv β Automated Endpoint Tests") | |
| print("=" * 60) | |
| # 1. Health check | |
| def t1(): | |
| r = requests.get(f"{base}/health") | |
| assert r.status_code == 200, f"Status {r.status_code}" | |
| assert r.json()["status"] == "ok" | |
| test("GET /health", t1) | |
| # 2. List tasks | |
| def t2(): | |
| r = requests.get(f"{base}/tasks") | |
| assert r.status_code == 200 | |
| tasks = r.json()["tasks"] | |
| assert len(tasks) == 3, f"Expected 3 tasks, got {len(tasks)}" | |
| ids = [t["task_id"] for t in tasks] | |
| assert "easy" in ids and "medium" in ids and "hard" in ids | |
| for t in tasks: | |
| print(f" {t['task_id']}: {t['name']} ({t['max_steps']} steps)") | |
| test("GET /tasks (3 tasks)", t2) | |
| # 3. Reset easy | |
| def t3(): | |
| r = requests.post(f"{base}/reset", json={"task_id": "easy"}) | |
| assert r.status_code == 200 | |
| data = r.json() | |
| obs = data["observation"] | |
| assert "current_price" in obs | |
| assert "rsi" in obs | |
| assert "max_trade_size" in obs | |
| assert "portfolio_value" in obs | |
| print(f" Price: ${obs['current_price']:,.2f}, RSI: {obs['rsi']}") | |
| test("POST /reset (easy)", t3) | |
| # 4. Step Hold | |
| def t4(): | |
| requests.post(f"{base}/reset", json={"task_id": "easy"}) | |
| r = requests.post(f"{base}/step", json={"action": {"action": "Hold", "amount": None}}) | |
| assert r.status_code == 200 | |
| data = r.json() | |
| assert "observation" in data | |
| assert "reward" in data | |
| assert "done" in data | |
| reward = data["reward"] | |
| assert "step_reward" in reward | |
| assert "cumulative_reward" in reward | |
| assert "risk_penalty" in reward | |
| print(f" Reward: {reward['step_reward']:.4f}, Done: {data['done']}") | |
| test("POST /step (Hold)", t4) | |
| # 5. State | |
| def t5(): | |
| requests.post(f"{base}/reset", json={"task_id": "easy"}) | |
| requests.post(f"{base}/step", json={"action": {"action": "Hold", "amount": None}}) | |
| r = requests.get(f"{base}/state") | |
| assert r.status_code == 200 | |
| state = r.json() | |
| assert "observation" in state | |
| assert "portfolio" in state | |
| assert "step_count" in state | |
| assert "done" in state | |
| assert "task_id" in state | |
| assert state["step_count"] == 1 | |
| print(f" Portfolio: ${state['portfolio']['total_value']:,.2f}") | |
| test("GET /state", t5) | |
| # 6. Easy full run + grade | |
| def t6(): | |
| requests.post(f"{base}/reset", json={"task_id": "easy"}) | |
| for i in range(5): | |
| r = requests.post(f"{base}/step", json={"action": {"action": "Hold", "amount": None}}) | |
| assert r.status_code == 200 | |
| r = requests.post(f"{base}/grade") | |
| assert r.status_code == 200 | |
| grade = r.json() | |
| assert grade["score"] == 1.0, f"Expected 1.0, got {grade['score']}" | |
| assert 0.0 <= grade["score"] <= 1.0 | |
| print(f" Score: {grade['score']} β {grade['reason'][:60]}") | |
| test("Easy task: all Hold β score 1.0", t6) | |
| # 7. Easy with wrong action β score 0.0 | |
| def t7(): | |
| requests.post(f"{base}/reset", json={"task_id": "easy"}) | |
| for i in range(5): | |
| if i == 2: | |
| action = {"action": "Buy", "amount": 100} | |
| else: | |
| action = {"action": "Hold", "amount": None} | |
| r = requests.post(f"{base}/step", json={"action": action}) | |
| r = requests.post(f"{base}/grade") | |
| grade = r.json() | |
| assert grade["score"] == 0.0, f"Expected 0.0, got {grade['score']}" | |
| print(f" Score: {grade['score']} (correct: non-hold penalized)") | |
| test("Easy task: one Buy β score 0.0", t7) | |
| # 8. Medium with violations | |
| def t8(): | |
| requests.post(f"{base}/reset", json={"task_id": "medium"}) | |
| violations = 0 | |
| for i in range(20): | |
| if i % 4 == 0: | |
| action = {"action": "Buy", "amount": 5000.0} # over limit | |
| elif i % 4 == 2: | |
| action = {"action": "Sell", "amount": 500.0} | |
| else: | |
| action = {"action": "Hold", "amount": None} | |
| r = requests.post(f"{base}/step", json={"action": action}) | |
| info = r.json().get("info", {}) | |
| if info.get("risk_violation"): | |
| violations += 1 | |
| r = requests.post(f"{base}/grade") | |
| grade = r.json() | |
| assert 0.0 <= grade["score"] <= 1.0 | |
| print(f" Score: {grade['score']:.4f}, Violations: {violations}") | |
| print(f" Breakdown: {json.dumps(grade.get('breakdown', {}))}") | |
| test("Medium task: mixed + violations", t8) | |
| # 9. Medium with perfect compliance | |
| def t9(): | |
| requests.post(f"{base}/reset", json={"task_id": "medium"}) | |
| for i in range(20): | |
| if i % 3 == 0: | |
| action = {"action": "Buy", "amount": 800.0} # under limit | |
| elif i % 3 == 1: | |
| action = {"action": "Hold", "amount": None} | |
| else: | |
| action = {"action": "Sell", "amount": 500.0} | |
| r = requests.post(f"{base}/step", json={"action": action}) | |
| r = requests.post(f"{base}/grade") | |
| grade = r.json() | |
| assert 0.0 <= grade["score"] <= 1.0 | |
| print(f" Score: {grade['score']:.4f} (compliant trading)") | |
| test("Medium task: compliant trading", t9) | |
| # 10. Hard task run | |
| def t10(): | |
| requests.post(f"{base}/reset", json={"task_id": "hard"}) | |
| for i in range(30): | |
| if i % 5 == 0: | |
| action = {"action": "Buy", "amount": 900.0} | |
| elif i % 5 == 3: | |
| action = {"action": "Sell", "amount": 700.0} | |
| else: | |
| action = {"action": "Hold", "amount": None} | |
| r = requests.post(f"{base}/step", json={"action": action}) | |
| r = requests.post(f"{base}/grade") | |
| grade = r.json() | |
| assert 0.0 <= grade["score"] <= 1.0 | |
| print(f" Score: {grade['score']:.4f}") | |
| print(f" Breakdown: {json.dumps(grade.get('breakdown', {}))}") | |
| test("Hard task: mixed trading", t10) | |
| # 11. Empty reset (validator ping) | |
| def t11(): | |
| r = requests.post(f"{base}/reset") | |
| assert r.status_code == 200 | |
| assert "observation" in r.json() | |
| test("POST /reset (empty body β validator)", t11) | |
| # 12. Grader scores are different (not constant) | |
| def t12(): | |
| scores = set() | |
| # Run easy with hold | |
| requests.post(f"{base}/reset", json={"task_id": "easy"}) | |
| for _ in range(5): | |
| requests.post(f"{base}/step", json={"action": {"action": "Hold", "amount": None}}) | |
| s1 = requests.post(f"{base}/grade").json()["score"] | |
| scores.add(s1) | |
| # Run easy with buy | |
| requests.post(f"{base}/reset", json={"task_id": "easy"}) | |
| for _ in range(5): | |
| requests.post(f"{base}/step", json={"action": {"action": "Buy", "amount": 100}}) | |
| s2 = requests.post(f"{base}/grade").json()["score"] | |
| scores.add(s2) | |
| assert len(scores) > 1, f"Grader always returns same score: {scores}" | |
| print(f" Scores vary: {scores}") | |
| test("Graders produce different scores", t12) | |
| # Summary | |
| print() | |
| print("=" * 60) | |
| if errors: | |
| print(f" FAILED: {len(errors)} test(s)") | |
| for e in errors: | |
| print(f" β {e}") | |
| sys.exit(1) | |
| else: | |
| print(" ALL 12 TESTS PASSED β") | |
| print("=" * 60) | |