"""Tests for backtest UI support endpoints (S1.4). Uses a FakeOrch returning a BacktestResult with equity_curve/trades so we can assert the strategy/equity-curve/daily-pnl/trades endpoints without a real orchestrator or DB. """ import pytest import pandas as pd from fastapi.testclient import TestClient from sanguo_api.app import create_app from sanguo_api.routes import set_orchestrator from sanguo_api.auth import hash_password from sanguo_backtest.result_store import BacktestResult class FakeOrch: def __init__(self, result): self._r = result def get_result(self, task_id): return self._r def _result() -> BacktestResult: return BacktestResult( task_id="cta_t", type="cta", status="done", strategy="DoubleMaStrategy", symbol="600000", params={"fast_window": 10}, start="2024-01-01", end="2024-06-30", statistics={"total_return": 0.1, "sharpe_ratio": 1.2}, equity_curve=pd.DataFrame([ {"date": "2024-01-01", "balance": 1_000_000.0}, {"date": "2024-01-02", "balance": 1_010_000.0}, {"date": "2024-01-03", "balance": 1_005_000.0}, ]), trades=pd.DataFrame([ {"datetime": "2024-01-02", "direction": "多", "offset": "开", "price": 10.5, "volume": 100, "vt_symbol": "600000.SSE"}, ]), ) @pytest.fixture(scope="module") def client() -> TestClient: app = create_app( db_path="/tmp/test_bt_routes.db", auth_config={ "username": "admin", "password_hash": hash_password("admin"), "jwt_secret": "test-secret", "expire_minutes": 60, }, max_workers=1, ) set_orchestrator(FakeOrch(_result())) return TestClient(app) @pytest.fixture(scope="module") def token(client) -> str: r = client.post("/api/v1/auth/login", json={"username": "admin", "password": "admin"}) assert r.status_code == 200 return r.json()["token"] def test_endpoints_require_auth(client): assert client.get("/api/v1/task/t/equity-curve").status_code == 401 assert client.get("/api/v1/strategy/list").status_code == 401 def test_strategy_list_and_params(client, token): h = {"Authorization": f"Bearer {token}"} r = client.get("/api/v1/strategy/list", headers=h) assert r.status_code == 200 assert "strategies" in r.json() r2 = client.get("/api/v1/strategy/DoubleMaStrategy/params", headers=h) assert r2.status_code == 200 assert "parameters" in r2.json() def test_equity_curve(client, token): h = {"Authorization": f"Bearer {token}"} eq = client.get("/api/v1/task/t/equity-curve", headers=h).json() assert len(eq["equity_curve"]) == 3 assert eq["equity_curve"][1]["balance"] == 1_010_000.0 def test_daily_pnl(client, token): h = {"Authorization": f"Bearer {token}"} pnl = client.get("/api/v1/task/t/daily-pnl", headers=h).json() assert len(pnl["daily_pnl"]) == 3 # day 0: no prior → 0.0; day 1: +10000; day 2: -5000 assert pnl["daily_pnl"][0]["pnl"] == 0.0 assert pnl["daily_pnl"][1]["pnl"] == 10_000.0 assert pnl["daily_pnl"][2]["pnl"] == -5_000.0 def test_trades(client, token): h = {"Authorization": f"Bearer {token}"} tr = client.get("/api/v1/task/t/trades", headers=h).json() assert len(tr["trades"]) == 1 assert tr["trades"][0]["price"] == 10.5