"""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) == {}