ticker-validate: cover failure + side-effect paths
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
3bb62763ea
commit
ca953e5ea2
1 changed files with 75 additions and 0 deletions
|
|
@ -60,3 +60,78 @@ async def test_validate_happy_path(tmp_path, monkeypatch):
|
||||||
assert result["price"] == 172.40
|
assert result["price"] == 172.40
|
||||||
assert result["currency"] == "USD"
|
assert result["currency"] == "USD"
|
||||||
assert result["as_of"] == "2026-05-27"
|
assert result["as_of"] == "2026-05-27"
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_validate_unknown_symbol(tmp_path, monkeypatch):
|
||||||
|
from app.routers.ticker_validate import validate_ticker
|
||||||
|
from app.services.market import Quote
|
||||||
|
import app.routers.ticker_validate as mod
|
||||||
|
|
||||||
|
_, factory, setup = _build_session_factory(tmp_path)
|
||||||
|
await setup()
|
||||||
|
|
||||||
|
# Mock fetch_yahoo to return a Quote with error and no price.
|
||||||
|
async def _fake_yahoo(client, symbol, label, note, anchor=None):
|
||||||
|
return Quote(symbol=symbol, source="yahoo", label=label, note=note,
|
||||||
|
price=None, currency=None, as_of=None,
|
||||||
|
error="empty result")
|
||||||
|
monkeypatch.setattr(mod, "fetch_yahoo", _fake_yahoo)
|
||||||
|
|
||||||
|
async with factory() as session:
|
||||||
|
result = await validate_ticker(symbol="XYZNOTREAL", session=session)
|
||||||
|
|
||||||
|
assert result["ok"] is False
|
||||||
|
assert "not recognised" in result["error"].lower()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_validate_empty_symbol_rejects():
|
||||||
|
from app.routers.ticker_validate import validate_ticker
|
||||||
|
|
||||||
|
# Direct call — no session needed because we short-circuit before any DB use.
|
||||||
|
result = await validate_ticker(symbol=" ", session=None)
|
||||||
|
assert result["ok"] is False
|
||||||
|
assert "required" in result["error"].lower()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_validate_seeds_universe_and_quote(tmp_path, monkeypatch):
|
||||||
|
"""Side-effect check: on success, the symbol is upserted into the
|
||||||
|
universe and a Quote row is written."""
|
||||||
|
from sqlalchemy import select
|
||||||
|
|
||||||
|
from app.models import Quote as QuoteModel
|
||||||
|
from app.routers.ticker_validate import validate_ticker
|
||||||
|
from app.services.market import Quote
|
||||||
|
import app.routers.ticker_validate as mod
|
||||||
|
|
||||||
|
_, factory, setup = _build_session_factory(tmp_path)
|
||||||
|
await setup()
|
||||||
|
|
||||||
|
upsert_calls: list[list[str]] = []
|
||||||
|
|
||||||
|
async def _fake_yahoo(client, symbol, label, note, anchor=None):
|
||||||
|
return Quote(symbol=symbol, source="yahoo", label=label, note=note,
|
||||||
|
price=100.0, currency="USD", as_of="2026-05-27", changes={})
|
||||||
|
monkeypatch.setattr(mod, "fetch_yahoo", _fake_yahoo)
|
||||||
|
|
||||||
|
async def _fake_upsert(session, tickers):
|
||||||
|
upsert_calls.append(list(tickers))
|
||||||
|
return len(list(tickers))
|
||||||
|
monkeypatch.setattr(mod, "upsert_tickers", _fake_upsert)
|
||||||
|
|
||||||
|
async with factory() as session:
|
||||||
|
result = await validate_ticker(symbol="MSFT", session=session)
|
||||||
|
|
||||||
|
assert result["ok"] is True
|
||||||
|
assert upsert_calls == [["MSFT"]]
|
||||||
|
|
||||||
|
# Quote row was written.
|
||||||
|
async with factory() as session:
|
||||||
|
rows = (await session.execute(
|
||||||
|
select(QuoteModel).where(QuoteModel.symbol == "MSFT")
|
||||||
|
)).scalars().all()
|
||||||
|
assert len(rows) == 1
|
||||||
|
assert rows[0].price == 100.0
|
||||||
|
assert rows[0].currency == "USD"
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue