Scan survives score-refresh failures; fix expired-ORM crash after rollback
A scoring error no longer skips setup detection for the ticker: the rollback already restores a clean transaction, and qualification re-gates on live scores at alert time, so a stale score is recoverable but a skipped scan is not. Iterating symbol strings instead of Ticker instances fixes a latent crash the new regression test caught: rollback() expires ORM objects regardless of expire_on_commit, so touching ticker.symbol in the except handler triggered sync lazy-loading, which raises on an AsyncSession and killed the whole scan on the first per-ticker error. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,61 @@
|
||||
"""Tests for scan_all_tickers orchestration: error isolation between phases."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import pytest
|
||||
|
||||
from app.models.ticker import Ticker
|
||||
from app.services import rr_scanner_service, scoring_service
|
||||
from tests.conftest import _test_session_factory # type: ignore
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def session():
|
||||
async with _test_session_factory() as s:
|
||||
yield s
|
||||
|
||||
|
||||
async def test_scan_proceeds_when_score_refresh_fails(session, monkeypatch):
|
||||
"""A scoring failure must not skip setup detection for the ticker.
|
||||
|
||||
Qualification re-gates on live scores at alert time, so a stale score is
|
||||
recoverable — a skipped scan is not.
|
||||
"""
|
||||
session.add(Ticker(symbol="AAA"))
|
||||
await session.commit()
|
||||
|
||||
async def _boom(db, symbol):
|
||||
raise RuntimeError("scoring unavailable")
|
||||
|
||||
scanned: list[str] = []
|
||||
|
||||
async def _fake_scan_ticker(db, symbol, *args, **kwargs):
|
||||
scanned.append(symbol)
|
||||
return []
|
||||
|
||||
monkeypatch.setattr(scoring_service, "compute_all_dimensions", _boom)
|
||||
monkeypatch.setattr(rr_scanner_service, "scan_ticker", _fake_scan_ticker)
|
||||
|
||||
setups = await rr_scanner_service.scan_all_tickers(session)
|
||||
|
||||
assert scanned == ["AAA"]
|
||||
assert setups == []
|
||||
|
||||
|
||||
async def test_scan_error_does_not_stop_later_tickers(session, monkeypatch):
|
||||
session.add_all([Ticker(symbol="AAA"), Ticker(symbol="BBB")])
|
||||
await session.commit()
|
||||
|
||||
scanned: list[str] = []
|
||||
|
||||
async def _fake_scan_ticker(db, symbol, *args, **kwargs):
|
||||
scanned.append(symbol)
|
||||
if symbol == "AAA":
|
||||
raise RuntimeError("scan blew up")
|
||||
return []
|
||||
|
||||
monkeypatch.setattr(rr_scanner_service, "scan_ticker", _fake_scan_ticker)
|
||||
|
||||
await rr_scanner_service.scan_all_tickers(session)
|
||||
|
||||
assert scanned == ["AAA", "BBB"]
|
||||
Reference in New Issue
Block a user