"""End-to-end pipeline test — mocked PRAW + respx-mocked LLM + in-memory DB.""" from __future__ import annotations import json from collections.abc import AsyncIterator from dataclasses import dataclass from datetime import datetime from decimal import Decimal from unittest.mock import AsyncMock, MagicMock import httpx import pytest import respx from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession from fire_planner.db import FireExample from fire_planner.examples.cli import ingest_subreddit LLAMA_URL = "http://llama-cpp.llama-cpp.svc.cluster.local:8000/v1/chat/completions" CLAUDE_URL = "http://claude-agent-service.claude-agent.svc.cluster.local:8080/v1/chat/completions" @dataclass class _FakeSub: id: str title: str selftext: str permalink: str created_utc: float def _async_iter(items: list[_FakeSub]) -> AsyncIterator[_FakeSub]: async def _gen() -> AsyncIterator[_FakeSub]: for it in items: yield it return _gen() @respx.mock @pytest.mark.asyncio async def test_ingest_subreddit_end_to_end(session: AsyncSession) -> None: fakes = [ _FakeSub( id="ok1", title="FIRE at 38 in Manila", selftext="Net worth £1m, family of 3, retired last year", permalink="/r/ExpatFIRE/comments/ok1/", created_utc=datetime(2026, 1, 1).timestamp(), ), _FakeSub( # filter should drop this — no money signal id="drop1", title="Thinking about moving to Lisbon", selftext="No specifics yet", permalink="/r/ExpatFIRE/comments/drop1/", created_utc=datetime(2026, 1, 2).timestamp(), ), ] mock_subreddit = MagicMock() mock_subreddit.top = MagicMock(return_value=_async_iter(fakes)) mock_reddit = MagicMock() mock_reddit.subreddit = AsyncMock(return_value=mock_subreddit) payload = { "country": "Philippines", "city": "Manila", "portfolio_native": 1000000, "raw_currency": "GBP", "age": 38, "family_size": 3, "fi_status": "FIRE", "is_retired": True, "confidence": 0.8, } respx.post(LLAMA_URL).respond( 200, json={"choices": [{"message": {"content": json.dumps(payload)}}]}, ) fx_rates = {"GBP": Decimal("1"), "USD": Decimal("0.80")} async with httpx.AsyncClient() as client: n_inserted, n_skipped = await ingest_subreddit( session, mock_reddit, sub="ExpatFIRE", when="all", limit=10, llama_url=LLAMA_URL, claude_url=CLAUDE_URL, claude_bearer="t", client=client, fx_rates=fx_rates, ) assert n_inserted == 1 assert n_skipped == 1 rows = (await session.execute(select(FireExample))).scalars().all() assert len(rows) == 1 assert rows[0].country == "Philippines" assert rows[0].portfolio_gbp == Decimal("1000000.00")