from __future__ import annotations from datetime import UTC, datetime, timedelta import httpx from arbitrade.api.app import create_app from arbitrade.config.settings import Settings def _seed_metrics_data(app) -> None: store = app.state.store started = datetime.now(UTC) finished = started + timedelta(seconds=20) with store.connect() as conn: conn.execute( """ INSERT INTO trades ( trade_ref, started_at, finished_at, status, realized_pnl, estimated_pnl, capital_used, cycle, leg_count ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) """, [ "trade-1", started, finished, "filled", 15.0, 10.0, 100.0, "USD->BTC->ETH->USD", 3, ], ) conn.execute( """ INSERT INTO opportunities ( detected_at, cycle, gross_pct, net_pct, est_profit, executed ) VALUES (?, ?, ?, ?, ?, ?) """, [started, "USD->BTC->ETH->USD", 4.0, 3.0, 0.03, True], ) conn.execute( """ INSERT INTO orders ( trade_ref, order_ref, leg_index, pair, side, volume, user_ref, status, filled_volume, avg_price, raw_response, recorded_at ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) """, [ "trade-1", "order-1", 0, "BTC/USD", "buy", 2.0, 100, "closed", 2.0, 100.0, "{}", started, ], ) async def test_dashboard_page_and_fragment_and_sse(tmp_path) -> None: app = create_app( Settings(_env_file=None, DUCKDB_PATH=tmp_path / "dash.duckdb")) _seed_metrics_data(app) transport = httpx.ASGITransport(app=app) async with httpx.AsyncClient(transport=transport, base_url="http://test") as client: page = await client.get("/") fragment = await client.get("/dashboard/fragment/metrics") stream = await client.get("/dashboard/stream/metrics") assert page.status_code == 200 assert "EventSource" in page.text assert 'hx-get="/dashboard/fragment/metrics"' in page.text assert fragment.status_code == 200 assert "Realized P&L" in fragment.text assert "15.00 USD" in fragment.text assert "100.0%" in fragment.text assert stream.status_code == 200 assert stream.headers["content-type"].startswith("text/event-stream") assert "event: metrics" in stream.text assert "Realized P&L" in stream.text