"""Shared test fixtures. Provides a configurable in-memory repository (daily history + a single rolling row) so the funnel service and API can be tested without Postgres. """ from __future__ import annotations import pytest from app.services.funnel import FunnelSnapshot class FakeRepository: """In-memory repository implementing the FunnelRepository protocol. * ``set_daily(dt, values)`` installs a daily row keyed by ``dt`` (yyyyMMdd). * ``set_latest_daily(dt)`` marks which daily ``dt`` is returned when ``fetch_daily(None)`` is called (defaults to the most recently added dt). * ``set_rolling(dt, values)`` installs the single rolling row. With nothing installed both fetches return ``None``. """ def __init__(self) -> None: self._daily: dict[str, dict[str, int | None]] = {} self._latest_daily_dt: str | None = None self._rolling: FunnelSnapshot | None = None def set_daily(self, dt: str, values: dict[str, int | None]) -> None: self._daily[dt] = dict(values) self._latest_daily_dt = dt def set_latest_daily(self, dt: str) -> None: self._latest_daily_dt = dt def set_rolling(self, dt: str, values: dict[str, int | None]) -> None: self._rolling = FunnelSnapshot(dt=dt, values=dict(values)) def clear(self) -> None: self._daily.clear() self._latest_daily_dt = None self._rolling = None async def fetch_daily(self, dt: str | None) -> FunnelSnapshot | None: if dt is None: dt = self._latest_daily_dt if dt is None: return None values = self._daily.get(dt) if values is None: return None return FunnelSnapshot(dt=dt, values=dict(values)) async def fetch_rolling(self) -> FunnelSnapshot | None: return self._rolling @pytest.fixture def repo() -> FakeRepository: return FakeRepository()