fire-planner/tests/test_examples_cli.py

101 lines
3 KiB
Python
Raw Normal View History

"""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")