100 lines
2.8 KiB
Python
100 lines
2.8 KiB
Python
"""Unit tests for fundamental_service — unavailable_fields persistence."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
|
|
import pytest
|
|
from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_async_engine
|
|
|
|
from app.database import Base
|
|
from app.models.ticker import Ticker
|
|
from app.services import fundamental_service
|
|
|
|
# Use a dedicated engine so commit/refresh work without conflicting
|
|
# with the conftest transactional session.
|
|
_engine = create_async_engine("sqlite+aiosqlite://", echo=False)
|
|
_session_factory = async_sessionmaker(_engine, class_=AsyncSession, expire_on_commit=False)
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
async def _setup_tables():
|
|
async with _engine.begin() as conn:
|
|
await conn.run_sync(Base.metadata.create_all)
|
|
yield
|
|
async with _engine.begin() as conn:
|
|
await conn.run_sync(Base.metadata.drop_all)
|
|
|
|
|
|
@pytest.fixture
|
|
async def session() -> AsyncSession:
|
|
async with _session_factory() as s:
|
|
yield s
|
|
|
|
|
|
@pytest.fixture
|
|
async def ticker(session: AsyncSession) -> Ticker:
|
|
"""Create a test ticker."""
|
|
t = Ticker(symbol="AAPL")
|
|
session.add(t)
|
|
await session.commit()
|
|
await session.refresh(t)
|
|
return t
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_store_fundamental_persists_unavailable_fields(
|
|
session: AsyncSession, ticker: Ticker
|
|
):
|
|
"""unavailable_fields dict is serialized to JSON and stored."""
|
|
fields = {"pe_ratio": "requires paid plan", "revenue_growth": "requires paid plan"}
|
|
|
|
record = await fundamental_service.store_fundamental(
|
|
session,
|
|
symbol="AAPL",
|
|
pe_ratio=None,
|
|
revenue_growth=None,
|
|
market_cap=1_000_000.0,
|
|
unavailable_fields=fields,
|
|
)
|
|
|
|
assert json.loads(record.unavailable_fields_json) == fields
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_store_fundamental_defaults_to_empty_dict(
|
|
session: AsyncSession, ticker: Ticker
|
|
):
|
|
"""When unavailable_fields is not provided, column defaults to '{}'."""
|
|
record = await fundamental_service.store_fundamental(
|
|
session,
|
|
symbol="AAPL",
|
|
pe_ratio=25.0,
|
|
)
|
|
|
|
assert json.loads(record.unavailable_fields_json) == {}
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_store_fundamental_updates_unavailable_fields(
|
|
session: AsyncSession, ticker: Ticker
|
|
):
|
|
"""Updating an existing record also updates unavailable_fields_json."""
|
|
# First store
|
|
await fundamental_service.store_fundamental(
|
|
session,
|
|
symbol="AAPL",
|
|
pe_ratio=None,
|
|
unavailable_fields={"pe_ratio": "requires paid plan"},
|
|
)
|
|
|
|
# Second store — fields now available
|
|
record = await fundamental_service.store_fundamental(
|
|
session,
|
|
symbol="AAPL",
|
|
pe_ratio=25.0,
|
|
unavailable_fields={},
|
|
)
|
|
|
|
assert json.loads(record.unavailable_fields_json) == {}
|