"""Tests for :class:`app.services.posts.PostService`. Uses the session-scoped ``db_engine`` fixture (temp file, migrated, seeded) so we exercise the real SQL path — not a mock. """ from __future__ import annotations from sqlalchemy import Engine from app.models.posts import PostSummary from app.services.posts import PostService def test_list_published_returns_seeded_welcome_post(db_engine: Engine) -> None: """The seeded welcome post is visible to the published-list query.""" service = PostService(db_engine) posts = service.list_published() assert len(posts) >= 1 slugs = [p.slug for p in posts] assert "welcome-to-the-farm" in slugs welcome = next(p for p in posts if p.slug == "welcome-to-the-farm") assert isinstance(welcome, PostSummary) assert welcome.title == "Welcome to the Farm" assert welcome.published_at is not None # Excerpt must be populated and short enough for a card layout. assert welcome.excerpt assert len(welcome.excerpt) <= 281 # 280 + optional ellipsis def test_list_published_is_cached(db_engine: Engine) -> None: """Subsequent calls with the same limit return the same object. The cache is keyed by limit. Two consecutive calls within the TTL window should hand back the identical list object, which proves the cache hit path works. """ service = PostService(db_engine) first = service.list_published() second = service.list_published() # Same list object = cache hit. Replaces an explicit "spy on SQL" # test — simpler and more robust to refactors. assert first is second def test_invalidate_all_forces_reload(db_engine: Engine) -> None: """After :meth:`invalidate_all` the next call re-hits the DB.""" service = PostService(db_engine) first = service.list_published() service.invalidate_all() second = service.list_published() assert first is not second # Content should still match — same DB, same rows. assert [p.slug for p in first] == [p.slug for p in second] def test_list_published_respects_limit(db_engine: Engine) -> None: """``limit`` is forwarded to the SQL query.""" service = PostService(db_engine) posts = service.list_published(limit=1) assert len(posts) <= 1