Files
FabledScribe/tests/test_news_api.py
T
bvandeusen 260103d533 feat: inject briefing article content for deep article Q&A
Add _build_briefing_article_context() helper to llm.py that reads
rss_item_ids from briefing message metadata and injects article content
into the system prompt. Pass conv_id through build_context() and
generation_task.py.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 00:31:43 -04:00

109 lines
3.4 KiB
Python

"""Tests for news API — retention constant and endpoint formatting."""
import pytest
from unittest.mock import AsyncMock, MagicMock
from datetime import datetime, timezone
def test_rss_item_max_age_is_90_days():
"""Retention window should be 90 days."""
from fabledassistant.services.rss import ITEM_MAX_AGE_DAYS
assert ITEM_MAX_AGE_DAYS == 90
def _make_mock_session(rows):
"""Return a mock async context manager whose session.execute returns rows."""
mock_result = MagicMock()
mock_result.mappings.return_value.all.return_value = rows
mock_session = AsyncMock()
mock_session.execute = AsyncMock(return_value=mock_result)
mock_cm = MagicMock()
mock_cm.__aenter__ = AsyncMock(return_value=mock_session)
mock_cm.__aexit__ = AsyncMock(return_value=False)
return mock_cm
def test_list_news_item_serialisation():
"""News item serialisation should truncate content to 300 chars and include reaction."""
pub = datetime(2026, 3, 28, 8, 0, tzinfo=timezone.utc)
item = {
"id": 1,
"title": "EU AI Act deadline",
"url": "https://example.com/ai",
"content": "x" * 500,
"published_at": pub,
"topics": ["tech"],
"feed_title": "TechCrunch",
"reaction": "up",
}
result = {
"id": item["id"],
"title": item["title"],
"url": item["url"],
"snippet": (item["content"] or "")[:300],
"published_at": item["published_at"].isoformat() if item["published_at"] else None,
"topics": item["topics"] or [],
"source": item["feed_title"],
"reaction": item["reaction"],
}
assert result["snippet"] == "x" * 300
assert result["source"] == "TechCrunch"
assert result["reaction"] == "up"
assert result["published_at"] == pub.isoformat()
def test_build_briefing_article_context_metadata_extraction():
"""Verify the rss_item_ids extraction logic from message metadata."""
import json
# Simulates the logic in _build_briefing_article_context
def extract_ids(msg_metadata):
meta = msg_metadata or {}
if isinstance(meta, str):
try:
meta = json.loads(meta)
except Exception:
return []
return meta.get("rss_item_ids") or []
assert extract_ids({}) == []
assert extract_ids(None) == []
assert extract_ids({"rss_item_ids": [1, 2, 3]}) == [1, 2, 3]
assert extract_ids(json.dumps({"rss_item_ids": [4, 5]})) == [4, 5]
assert extract_ids("not json") == []
def test_list_news_null_content_serialisation():
"""Null content should produce empty snippet without error."""
item = {
"id": 2,
"title": "No content article",
"url": "https://example.com/b",
"content": None,
"published_at": None,
"topics": None,
"feed_title": "Hacker News",
"reaction": None,
}
result = {
"id": item["id"],
"title": item["title"],
"url": item["url"],
"snippet": (item["content"] or "")[:300],
"published_at": item["published_at"].isoformat() if item["published_at"] else None,
"topics": item["topics"] or [],
"source": item["feed_title"],
"reaction": item["reaction"],
}
assert result["snippet"] == ""
assert result["topics"] == []
assert result["published_at"] is None
assert result["reaction"] is None