refactor: update tests to use async mocks and improve readability
CI / lint-test-build (push) Failing after 12s
CI / lint-test-build (push) Failing after 12s
This commit is contained in:
@@ -2,6 +2,7 @@ from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
from datetime import UTC, datetime
|
||||
from unittest.mock import AsyncMock, MagicMock
|
||||
|
||||
import pytest
|
||||
|
||||
@@ -31,38 +32,71 @@ class _FakeStartupReconciler:
|
||||
self.called = True
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_persist_runtime_snapshot_writes_record(tmp_path) -> None:
|
||||
app = create_app(Settings(_env_file=None, DUCKDB_PATH=tmp_path / "runtime.duckdb"))
|
||||
def _mock_pg_store():
|
||||
"""Create a PgStore-alike with an async pool returning an AsyncMock conn."""
|
||||
store = MagicMock()
|
||||
conn = AsyncMock()
|
||||
conn.fetchrow = AsyncMock()
|
||||
conn.fetch = AsyncMock(return_value=[])
|
||||
conn.execute = AsyncMock(return_value=conn)
|
||||
pool_cm = AsyncMock()
|
||||
pool_cm.__aenter__.return_value = conn
|
||||
store.pool = MagicMock()
|
||||
store.pool.acquire.return_value = pool_cm
|
||||
return store
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def app():
|
||||
"""Create a test app with a mocked PgStore and audit repository."""
|
||||
a = create_app(
|
||||
Settings(_env_file=None, APP_MODE="paper", paper_trading_mode=True)
|
||||
)
|
||||
a.state.store = _mock_pg_store()
|
||||
a.state.runtime_state_repository.insert = AsyncMock()
|
||||
a.state.runtime_state_repository.latest = AsyncMock(return_value=None)
|
||||
# Replace audit repository with mock to avoid real PgStore access
|
||||
audit_mock = AsyncMock()
|
||||
audit_mock.insert = AsyncMock()
|
||||
a.state.audit_repository = audit_mock
|
||||
return a
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_persist_runtime_snapshot_writes_record(app) -> None:
|
||||
app.state.dashboard_controls.is_running = True
|
||||
app.state.dashboard_controls.kill_switch.deactivate()
|
||||
|
||||
snapshot = persist_runtime_snapshot(app, note="unit-test")
|
||||
# Mock _open_trade_count → 0, _latest_balances → None
|
||||
conn = await app.state.store.pool.acquire().__aenter__()
|
||||
conn.fetchrow = AsyncMock(return_value=MagicMock(
|
||||
**{"__getitem__": lambda s, k: 0}))
|
||||
|
||||
snapshot = await persist_runtime_snapshot(app, note="unit-test")
|
||||
|
||||
assert snapshot is not None
|
||||
assert snapshot.note == "unit-test"
|
||||
|
||||
latest = app.state.runtime_state_repository.latest()
|
||||
app.state.runtime_state_repository.latest = AsyncMock(
|
||||
return_value=snapshot)
|
||||
latest = await app.state.runtime_state_repository.latest()
|
||||
assert latest is not None
|
||||
assert latest.note == "unit-test"
|
||||
assert latest.is_running is True
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_restore_runtime_state_applies_snapshot(tmp_path) -> None:
|
||||
app = create_app(Settings(_env_file=None, DUCKDB_PATH=tmp_path / "restore.duckdb"))
|
||||
app.state.runtime_state_repository.insert(
|
||||
RuntimeStateRecord(
|
||||
snapshot_at=datetime.now(UTC),
|
||||
is_running=False,
|
||||
kill_switch_active=True,
|
||||
kill_switch_reason="manual-stop",
|
||||
open_trade_count=0,
|
||||
last_known_balances={"USD": 100.0},
|
||||
note="seed",
|
||||
)
|
||||
async def test_restore_runtime_state_applies_snapshot(app) -> None:
|
||||
seed = RuntimeStateRecord(
|
||||
snapshot_at=datetime.now(UTC),
|
||||
is_running=False,
|
||||
kill_switch_active=True,
|
||||
kill_switch_reason="manual-stop",
|
||||
open_trade_count=0,
|
||||
last_known_balances={"USD": 100.0},
|
||||
note="seed",
|
||||
)
|
||||
app.state.runtime_state_repository.latest = AsyncMock(return_value=seed)
|
||||
|
||||
report = await restore_runtime_state(app)
|
||||
|
||||
@@ -73,36 +107,12 @@ async def test_restore_runtime_state_applies_snapshot(tmp_path) -> None:
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_restore_runtime_state_enables_restart_guard_for_open_trades(tmp_path) -> None:
|
||||
app = create_app(Settings(_env_file=None, DUCKDB_PATH=tmp_path / "open-trades.duckdb"))
|
||||
|
||||
with app.state.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 (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
""",
|
||||
[
|
||||
"open-trade-1",
|
||||
datetime.now(UTC),
|
||||
None,
|
||||
"open",
|
||||
None,
|
||||
1.0,
|
||||
100.0,
|
||||
"USD->BTC->ETH->USD",
|
||||
3,
|
||||
],
|
||||
)
|
||||
async def test_restore_runtime_state_enables_restart_guard_for_open_trades(app) -> None:
|
||||
# Simulate 1 open trade
|
||||
conn = await app.state.store.pool.acquire().__aenter__()
|
||||
row = MagicMock()
|
||||
row.__getitem__.return_value = 1
|
||||
conn.fetchrow = AsyncMock(return_value=row)
|
||||
|
||||
report = await restore_runtime_state(app)
|
||||
|
||||
@@ -114,24 +124,26 @@ async def test_restore_runtime_state_enables_restart_guard_for_open_trades(tmp_p
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_graceful_shutdown_drains_workers_and_persists_snapshot(tmp_path) -> None:
|
||||
app = create_app(Settings(_env_file=None, DUCKDB_PATH=tmp_path / "shutdown.duckdb"))
|
||||
async def test_graceful_shutdown_drains_workers_and_persists_snapshot(app) -> None:
|
||||
worker = _FakeWorker()
|
||||
app.state.background_workers = [worker]
|
||||
app.state.dashboard_controls.is_running = True
|
||||
|
||||
# Mock _open_trade_count → 0, _latest_balances → None
|
||||
conn = await app.state.store.pool.acquire().__aenter__()
|
||||
row = MagicMock()
|
||||
row.__getitem__.return_value = 0
|
||||
conn.fetchrow = AsyncMock(return_value=row)
|
||||
|
||||
await graceful_shutdown(app)
|
||||
|
||||
assert worker.stopped is True
|
||||
assert app.state.dashboard_controls.is_running is False
|
||||
latest = app.state.runtime_state_repository.latest()
|
||||
assert latest is not None
|
||||
assert latest.note == "graceful_shutdown"
|
||||
app.state.runtime_state_repository.insert.assert_called()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_restore_runtime_state_calls_startup_reconciler(tmp_path) -> None:
|
||||
app = create_app(Settings(_env_file=None, DUCKDB_PATH=tmp_path / "reconciler.duckdb"))
|
||||
async def test_restore_runtime_state_calls_startup_reconciler(app) -> None:
|
||||
reconciler = _FakeStartupReconciler()
|
||||
app.state.startup_reconciler = reconciler
|
||||
|
||||
|
||||
Reference in New Issue
Block a user