7a0da7932b
OSV-Scanner (Scheduled) / scan-scheduled (push) Failing after 0s
Create Release / test-gate (push) Has been cancelled
Create Release / release-gate (push) Has been cancelled
Create Release / ci-gate (push) Has been cancelled
Create Release / version-check (push) Has been cancelled
Create Release / e2e-test-gate (push) Has been cancelled
Create Release / responsive-test-gate (push) Has been cancelled
Create Release / compat-test-gate (push) Has been cancelled
Create Release / compose-integration-gate (push) Has been cancelled
Create Release / vulture-gate (push) Has been cancelled
Create Release / build (push) Has been cancelled
Create Release / provenance (push) Has been cancelled
Create Release / prerelease-docker (push) Has been cancelled
Create Release / publish-docker (push) Has been cancelled
Create Release / create-release (push) Has been cancelled
Create Release / cleanup-changelog (push) Has been cancelled
Create Release / trigger-pypi (push) Has been cancelled
Create Release / monitor-pypi (push) Has been cancelled
Create Release / Clean up orphan prerelease tags and signatures (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-form] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-metrics] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-workflow] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [settings-core] (push) Has been cancelled
CodeQL Advanced / Analyze (javascript-typescript) (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [history-news] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [library] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [link-analytics] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [chat-core] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [chat-lifecycle] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [error-benchmark] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [settings-pages] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) (push) Has been cancelled
Docker Tests (Consolidated) / Accessibility Tests (push) Has been cancelled
Docker Tests (Consolidated) / LLM Unit Tests (push) Has been cancelled
Docker Tests (Consolidated) / LLM Example Tests (push) Has been cancelled
Docker Tests (Consolidated) / Production Image Smoke Test (push) Has been cancelled
Docker Tests (Consolidated) / Infrastructure Tests (push) Has been cancelled
OSSF Scorecard / OSSF Security Scorecard Analysis (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [mobile] (push) Has been cancelled
Backwards Compatibility / Verify Encryption Constants (push) Has been cancelled
Backwards Compatibility / PyPI Version Compatibility (push) Has been cancelled
Backwards Compatibility / Database Migration Tests (push) Has been cancelled
CodeQL Advanced / Analyze (python) (push) Has been cancelled
Docker Tests (Consolidated) / detect-changes (push) Has been cancelled
Docker Tests (Consolidated) / Build Test Image (push) Has been cancelled
Docker Tests (Consolidated) / All Pytest Tests + Coverage (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [accessibility] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [api-crud] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-login] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-pages] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-register] (push) Has been cancelled
607 lines
20 KiB
Python
607 lines
20 KiB
Python
"""
|
|
Tests for base card classes in news/core/base_card.py.
|
|
|
|
Tests cover:
|
|
- CardSource dataclass
|
|
- CardVersion dataclass
|
|
- BaseCard abstract class methods
|
|
- NewsCard concrete implementation
|
|
- ResearchCard concrete implementation
|
|
- UpdateCard concrete implementation
|
|
- OverviewCard concrete implementation
|
|
"""
|
|
|
|
import pytest
|
|
from unittest.mock import patch, MagicMock
|
|
from datetime import datetime
|
|
|
|
from local_deep_research.news.core.base_card import (
|
|
CardSource,
|
|
CardVersion,
|
|
NewsCard,
|
|
ResearchCard,
|
|
UpdateCard,
|
|
OverviewCard,
|
|
)
|
|
|
|
|
|
@pytest.fixture
|
|
def fixed_time():
|
|
"""Return a fixed datetime for testing."""
|
|
return datetime(2024, 1, 15, 12, 0, 0)
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_utc_now(fixed_time):
|
|
"""Mock utc_now to return consistent timestamps."""
|
|
with patch("local_deep_research.news.core.base_card.utc_now") as mock:
|
|
mock.return_value = fixed_time
|
|
yield fixed_time
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_generate_card_id():
|
|
"""Mock card ID generation for consistent IDs."""
|
|
with patch(
|
|
"local_deep_research.news.core.base_card.generate_card_id"
|
|
) as mock:
|
|
mock.return_value = "test_card_id_123"
|
|
yield mock
|
|
|
|
|
|
class TestCardSource:
|
|
"""Tests for CardSource dataclass."""
|
|
|
|
def test_basic_creation(self):
|
|
"""Test basic CardSource creation."""
|
|
source = CardSource(type="news_item")
|
|
|
|
assert source.type == "news_item"
|
|
assert source.source_id is None
|
|
assert source.created_from == ""
|
|
assert source.metadata == {}
|
|
|
|
def test_full_creation(self):
|
|
"""Test CardSource with all fields."""
|
|
source = CardSource(
|
|
type="user_search",
|
|
source_id="search_123",
|
|
created_from="User search: AI",
|
|
metadata={"search_strategy": "quick"},
|
|
)
|
|
|
|
assert source.type == "user_search"
|
|
assert source.source_id == "search_123"
|
|
assert source.created_from == "User search: AI"
|
|
assert source.metadata == {"search_strategy": "quick"}
|
|
|
|
def test_metadata_default_is_independent(self):
|
|
"""Test metadata default doesn't share between instances."""
|
|
source1 = CardSource(type="test")
|
|
source2 = CardSource(type="test")
|
|
|
|
source1.metadata["key"] = "value"
|
|
|
|
assert "key" not in source2.metadata
|
|
|
|
|
|
class TestCardVersion:
|
|
"""Tests for CardVersion dataclass."""
|
|
|
|
def test_basic_creation(self, fixed_time):
|
|
"""Test basic CardVersion creation."""
|
|
version = CardVersion(
|
|
version_id="v1",
|
|
created_at=fixed_time,
|
|
content={"data": "test"},
|
|
query_used="test query",
|
|
)
|
|
|
|
assert version.version_id == "v1"
|
|
assert version.created_at == fixed_time
|
|
assert version.content == {"data": "test"}
|
|
assert version.query_used == "test query"
|
|
assert version.search_strategy is None
|
|
|
|
def test_with_search_strategy(self, fixed_time):
|
|
"""Test CardVersion with search strategy."""
|
|
version = CardVersion(
|
|
version_id="v1",
|
|
created_at=fixed_time,
|
|
content={},
|
|
query_used="query",
|
|
search_strategy="detailed",
|
|
)
|
|
|
|
assert version.search_strategy == "detailed"
|
|
|
|
def test_auto_generates_id_when_empty(
|
|
self, fixed_time, mock_generate_card_id
|
|
):
|
|
"""Test version_id is auto-generated when empty."""
|
|
version = CardVersion(
|
|
version_id="",
|
|
created_at=fixed_time,
|
|
content={},
|
|
query_used="query",
|
|
)
|
|
|
|
assert version.version_id == "test_card_id_123"
|
|
|
|
|
|
class TestNewsCard:
|
|
"""Tests for NewsCard concrete implementation."""
|
|
|
|
def test_basic_creation(self, mock_utc_now, mock_generate_card_id):
|
|
"""Test basic NewsCard creation."""
|
|
source = CardSource(type="news_item")
|
|
card = NewsCard(
|
|
topic="AI Development",
|
|
source=source,
|
|
user_id="user_123",
|
|
)
|
|
|
|
assert card.topic == "AI Development"
|
|
assert card.user_id == "user_123"
|
|
assert card.id == "test_card_id_123"
|
|
assert card.headline == "AI Development" # Defaults to topic
|
|
|
|
def test_with_custom_headline(self, mock_utc_now, mock_generate_card_id):
|
|
"""Test NewsCard with custom headline."""
|
|
source = CardSource(type="news_item")
|
|
card = NewsCard(
|
|
topic="AI",
|
|
source=source,
|
|
user_id="user_123",
|
|
headline="Breaking: AI Achieves New Milestone",
|
|
)
|
|
|
|
assert card.headline == "Breaking: AI Achieves New Milestone"
|
|
|
|
def test_get_card_type(self, mock_utc_now, mock_generate_card_id):
|
|
"""Test get_card_type returns 'news'."""
|
|
source = CardSource(type="news_item")
|
|
card = NewsCard(topic="test", source=source, user_id="user_123")
|
|
|
|
assert card.get_card_type() == "news"
|
|
|
|
def test_to_dict_includes_news_fields(
|
|
self, mock_utc_now, mock_generate_card_id
|
|
):
|
|
"""Test to_dict includes news-specific fields."""
|
|
source = CardSource(type="news_item")
|
|
card = NewsCard(
|
|
topic="AI",
|
|
source=source,
|
|
user_id="user_123",
|
|
headline="Test Headline",
|
|
summary="Test summary",
|
|
category="Technology",
|
|
impact_score=8,
|
|
)
|
|
|
|
result = card.to_dict()
|
|
|
|
assert result["headline"] == "Test Headline"
|
|
assert result["summary"] == "Test summary"
|
|
assert result["category"] == "Technology"
|
|
assert result["impact_score"] == 8
|
|
assert result["card_type"] == "news"
|
|
|
|
def test_default_values(self, mock_utc_now, mock_generate_card_id):
|
|
"""Test NewsCard default values."""
|
|
source = CardSource(type="news_item")
|
|
card = NewsCard(topic="test", source=source, user_id="user_123")
|
|
|
|
assert card.category == "General"
|
|
assert card.impact_score == 5
|
|
assert card.is_developing is False
|
|
assert card.time_ago == "recent"
|
|
assert card.entities == {
|
|
"people": [],
|
|
"places": [],
|
|
"organizations": [],
|
|
}
|
|
|
|
def test_interaction_initialized(self, mock_utc_now, mock_generate_card_id):
|
|
"""Test interaction tracking is initialized."""
|
|
source = CardSource(type="news_item")
|
|
card = NewsCard(topic="test", source=source, user_id="user_123")
|
|
|
|
assert card.interaction == {
|
|
"votes_up": 0,
|
|
"votes_down": 0,
|
|
"views": 0,
|
|
"shares": 0,
|
|
}
|
|
|
|
|
|
class TestResearchCard:
|
|
"""Tests for ResearchCard concrete implementation."""
|
|
|
|
def test_basic_creation(self, mock_utc_now, mock_generate_card_id):
|
|
"""Test basic ResearchCard creation."""
|
|
source = CardSource(type="user_search")
|
|
card = ResearchCard(
|
|
topic="Machine Learning",
|
|
source=source,
|
|
user_id="user_123",
|
|
)
|
|
|
|
assert card.topic == "Machine Learning"
|
|
assert card.research_depth == "quick"
|
|
assert card.key_findings == []
|
|
assert card.sources_count == 0
|
|
|
|
def test_get_card_type(self, mock_utc_now, mock_generate_card_id):
|
|
"""Test get_card_type returns 'research'."""
|
|
source = CardSource(type="user_search")
|
|
card = ResearchCard(topic="test", source=source, user_id="user_123")
|
|
|
|
assert card.get_card_type() == "research"
|
|
|
|
def test_to_dict_includes_research_fields(
|
|
self, mock_utc_now, mock_generate_card_id
|
|
):
|
|
"""Test to_dict includes research-specific fields."""
|
|
source = CardSource(type="user_search")
|
|
card = ResearchCard(
|
|
topic="test",
|
|
source=source,
|
|
user_id="user_123",
|
|
research_depth="detailed",
|
|
key_findings=["finding1", "finding2"],
|
|
sources_count=10,
|
|
)
|
|
|
|
result = card.to_dict()
|
|
|
|
assert result["research_depth"] == "detailed"
|
|
assert result["key_findings"] == ["finding1", "finding2"]
|
|
assert result["sources_count"] == 10
|
|
assert result["card_type"] == "research"
|
|
|
|
|
|
class TestUpdateCard:
|
|
"""Tests for UpdateCard concrete implementation."""
|
|
|
|
def test_basic_creation(self, mock_utc_now, mock_generate_card_id):
|
|
"""Test basic UpdateCard creation."""
|
|
source = CardSource(type="subscription")
|
|
card = UpdateCard(
|
|
topic="Updates",
|
|
source=source,
|
|
user_id="user_123",
|
|
)
|
|
|
|
assert card.topic == "Updates"
|
|
assert card.update_type == "new_stories"
|
|
assert card.count == 0
|
|
assert card.preview_items == []
|
|
|
|
def test_since_timestamp_set(self, mock_utc_now, mock_generate_card_id):
|
|
"""Test since timestamp is set on creation."""
|
|
source = CardSource(type="subscription")
|
|
card = UpdateCard(topic="test", source=source, user_id="user_123")
|
|
|
|
assert card.since == mock_utc_now
|
|
|
|
def test_get_card_type(self, mock_utc_now, mock_generate_card_id):
|
|
"""Test get_card_type returns 'update'."""
|
|
source = CardSource(type="subscription")
|
|
card = UpdateCard(topic="test", source=source, user_id="user_123")
|
|
|
|
assert card.get_card_type() == "update"
|
|
|
|
def test_to_dict_includes_update_fields(
|
|
self, mock_utc_now, mock_generate_card_id
|
|
):
|
|
"""Test to_dict includes update-specific fields."""
|
|
source = CardSource(type="subscription")
|
|
card = UpdateCard(
|
|
topic="test",
|
|
source=source,
|
|
user_id="user_123",
|
|
update_type="breaking",
|
|
count=5,
|
|
preview_items=[{"title": "Item 1"}],
|
|
)
|
|
|
|
result = card.to_dict()
|
|
|
|
assert result["update_type"] == "breaking"
|
|
assert result["count"] == 5
|
|
assert result["preview_items"] == [{"title": "Item 1"}]
|
|
assert "since" in result
|
|
assert result["card_type"] == "update"
|
|
|
|
|
|
class TestOverviewCard:
|
|
"""Tests for OverviewCard concrete implementation."""
|
|
|
|
def test_basic_creation(self, mock_utc_now, mock_generate_card_id):
|
|
"""Test basic OverviewCard creation."""
|
|
source = CardSource(type="system")
|
|
card = OverviewCard(
|
|
source=source,
|
|
user_id="user_123",
|
|
)
|
|
|
|
# Topic is set automatically for overview cards
|
|
assert card.topic == "News Overview"
|
|
|
|
def test_get_card_type(self, mock_utc_now, mock_generate_card_id):
|
|
"""Test get_card_type returns 'overview'."""
|
|
source = CardSource(type="system")
|
|
card = OverviewCard(source=source, user_id="user_123")
|
|
|
|
assert card.get_card_type() == "overview"
|
|
|
|
def test_default_stats(self, mock_utc_now, mock_generate_card_id):
|
|
"""Test default stats structure."""
|
|
source = CardSource(type="system")
|
|
card = OverviewCard(source=source, user_id="user_123")
|
|
|
|
assert card.stats == {
|
|
"total_new": 0,
|
|
"breaking": 0,
|
|
"relevant": 0,
|
|
"categories": {},
|
|
}
|
|
|
|
def test_to_dict_includes_overview_fields(
|
|
self, mock_utc_now, mock_generate_card_id
|
|
):
|
|
"""Test to_dict includes overview-specific fields."""
|
|
source = CardSource(type="system")
|
|
card = OverviewCard(
|
|
source=source,
|
|
user_id="user_123",
|
|
summary="Daily news summary",
|
|
top_stories=[{"title": "Top Story"}],
|
|
trend_analysis="Trending: AI",
|
|
)
|
|
|
|
result = card.to_dict()
|
|
|
|
assert result["summary"] == "Daily news summary"
|
|
assert result["top_stories"] == [{"title": "Top Story"}]
|
|
assert result["trend_analysis"] == "Trending: AI"
|
|
assert result["card_type"] == "overview"
|
|
|
|
|
|
class TestBaseCardMethods:
|
|
"""Tests for BaseCard concrete methods (using NewsCard as implementation)."""
|
|
|
|
def test_set_progress_callback(self, mock_utc_now, mock_generate_card_id):
|
|
"""Test setting progress callback."""
|
|
source = CardSource(type="news_item")
|
|
card = NewsCard(topic="test", source=source, user_id="user_123")
|
|
|
|
callback = MagicMock()
|
|
card.set_progress_callback(callback)
|
|
|
|
assert card.progress_callback is callback
|
|
|
|
def test_update_progress_calls_callback(
|
|
self, mock_utc_now, mock_generate_card_id
|
|
):
|
|
"""Test _update_progress calls the callback."""
|
|
source = CardSource(type="news_item")
|
|
card = NewsCard(topic="test", source=source, user_id="user_123")
|
|
|
|
callback = MagicMock()
|
|
card.set_progress_callback(callback)
|
|
card._update_progress("Processing...", 50, {"step": "analysis"})
|
|
|
|
callback.assert_called_once_with(
|
|
"Processing...", 50, {"step": "analysis"}
|
|
)
|
|
|
|
def test_update_progress_without_callback(
|
|
self, mock_utc_now, mock_generate_card_id
|
|
):
|
|
"""Test _update_progress does nothing without callback."""
|
|
source = CardSource(type="news_item")
|
|
card = NewsCard(topic="test", source=source, user_id="user_123")
|
|
|
|
# Should not raise
|
|
card._update_progress("Message", 50)
|
|
|
|
def test_to_base_dict(self, mock_utc_now, mock_generate_card_id):
|
|
"""Test to_base_dict includes common fields."""
|
|
source = CardSource(
|
|
type="news_item",
|
|
source_id="src_123",
|
|
created_from="Test source",
|
|
)
|
|
card = NewsCard(
|
|
topic="Test Topic",
|
|
source=source,
|
|
user_id="user_123",
|
|
parent_card_id="parent_456",
|
|
)
|
|
|
|
result = card.to_base_dict()
|
|
|
|
assert result["id"] == "test_card_id_123"
|
|
assert result["topic"] == "Test Topic"
|
|
assert result["user_id"] == "user_123"
|
|
assert result["parent_card_id"] == "parent_456"
|
|
assert result["versions_count"] == 0
|
|
assert "source" in result
|
|
assert result["source"]["type"] == "news_item"
|
|
|
|
def test_get_latest_version_empty(
|
|
self, mock_utc_now, mock_generate_card_id
|
|
):
|
|
"""Test get_latest_version returns None when no versions."""
|
|
source = CardSource(type="news_item")
|
|
card = NewsCard(topic="test", source=source, user_id="user_123")
|
|
|
|
assert card.get_latest_version() is None
|
|
|
|
def test_get_latest_version_returns_most_recent(
|
|
self, mock_utc_now, mock_generate_card_id
|
|
):
|
|
"""Test get_latest_version returns most recent version."""
|
|
source = CardSource(type="news_item")
|
|
card = NewsCard(topic="test", source=source, user_id="user_123")
|
|
|
|
# Add versions manually
|
|
v1 = CardVersion(
|
|
version_id="v1",
|
|
created_at=datetime(2024, 1, 1),
|
|
content={},
|
|
query_used="q1",
|
|
)
|
|
v2 = CardVersion(
|
|
version_id="v2",
|
|
created_at=datetime(2024, 1, 15),
|
|
content={},
|
|
query_used="q2",
|
|
)
|
|
card.versions = [v1, v2]
|
|
|
|
latest = card.get_latest_version()
|
|
|
|
assert latest.version_id == "v2"
|
|
|
|
|
|
class TestBaseCardHelperMethods:
|
|
"""Tests for BaseCard helper methods."""
|
|
|
|
def test_extract_headline_from_headline(
|
|
self, mock_utc_now, mock_generate_card_id
|
|
):
|
|
"""Test _extract_headline extracts from headline field."""
|
|
source = CardSource(type="news_item")
|
|
card = NewsCard(topic="test", source=source, user_id="user_123")
|
|
|
|
result = {"headline": "Test Headline"}
|
|
assert card._extract_headline(result) == "Test Headline"
|
|
|
|
def test_extract_headline_from_title(
|
|
self, mock_utc_now, mock_generate_card_id
|
|
):
|
|
"""Test _extract_headline falls back to title."""
|
|
source = CardSource(type="news_item")
|
|
card = NewsCard(topic="test", source=source, user_id="user_123")
|
|
|
|
result = {"title": "Test Title"}
|
|
assert card._extract_headline(result) == "Test Title"
|
|
|
|
def test_extract_headline_from_query(
|
|
self, mock_utc_now, mock_generate_card_id
|
|
):
|
|
"""Test _extract_headline falls back to query (truncated)."""
|
|
source = CardSource(type="news_item")
|
|
card = NewsCard(topic="test", source=source, user_id="user_123")
|
|
|
|
result = {"query": "A" * 150}
|
|
headline = card._extract_headline(result)
|
|
assert len(headline) == 100
|
|
|
|
def test_extract_summary(self, mock_utc_now, mock_generate_card_id):
|
|
"""Test _extract_summary extracts from multiple fields."""
|
|
source = CardSource(type="news_item")
|
|
card = NewsCard(topic="test", source=source, user_id="user_123")
|
|
|
|
# Try summary field
|
|
assert card._extract_summary({"summary": "Test"}) == "Test"
|
|
# Try current_knowledge field
|
|
assert (
|
|
card._extract_summary({"current_knowledge": "Knowledge"})
|
|
== "Knowledge"
|
|
)
|
|
# Try formatted_findings (truncated)
|
|
long_findings = "A" * 600
|
|
assert (
|
|
len(card._extract_summary({"formatted_findings": long_findings}))
|
|
== 500
|
|
)
|
|
|
|
def test_calculate_impact(self, mock_utc_now, mock_generate_card_id):
|
|
"""Test _calculate_impact calculates score correctly."""
|
|
source = CardSource(type="news_item")
|
|
card = NewsCard(topic="test", source=source, user_id="user_123")
|
|
|
|
# Empty result
|
|
assert card._calculate_impact({}) == 5
|
|
|
|
# With findings and sources
|
|
result = {
|
|
"findings": ["f1", "f2", "f3", "f4", "f5", "f6"],
|
|
"sources": ["s1", "s2", "s3", "s4"],
|
|
}
|
|
score = card._calculate_impact(result)
|
|
assert 1 <= score <= 10
|
|
|
|
def test_extract_topics_from_topics_field(
|
|
self, mock_utc_now, mock_generate_card_id
|
|
):
|
|
"""Test _extract_topics uses topics field."""
|
|
source = CardSource(type="news_item")
|
|
card = NewsCard(topic="test", source=source, user_id="user_123")
|
|
|
|
result = {"topics": ["AI", "ML", "Tech"]}
|
|
assert card._extract_topics(result) == ["AI", "ML", "Tech"]
|
|
|
|
def test_extract_topics_from_query(
|
|
self, mock_utc_now, mock_generate_card_id
|
|
):
|
|
"""Test _extract_topics extracts from query when no topics."""
|
|
source = CardSource(type="news_item")
|
|
card = NewsCard(topic="test", source=source, user_id="user_123")
|
|
|
|
result = {"query": "artificial intelligence machine learning"}
|
|
topics = card._extract_topics(result)
|
|
# Should extract words with length > 4
|
|
assert "artificial" in topics
|
|
assert "intelligence" in topics
|
|
|
|
def test_extract_entities(self, mock_utc_now, mock_generate_card_id):
|
|
"""Test _extract_entities returns entities or default structure."""
|
|
source = CardSource(type="news_item")
|
|
card = NewsCard(topic="test", source=source, user_id="user_123")
|
|
|
|
# With entities
|
|
result = {"entities": {"people": ["John"], "places": ["NYC"]}}
|
|
assert card._extract_entities(result) == {
|
|
"people": ["John"],
|
|
"places": ["NYC"],
|
|
}
|
|
|
|
# Without entities
|
|
entities = card._extract_entities({})
|
|
assert entities == {"people": [], "places": [], "organizations": []}
|
|
|
|
|
|
class TestBaseCardWithCustomId:
|
|
"""Tests for BaseCard with custom ID."""
|
|
|
|
def test_uses_provided_card_id(self, mock_utc_now):
|
|
"""Test card uses provided card_id instead of generating."""
|
|
source = CardSource(type="news_item")
|
|
card = NewsCard(
|
|
topic="test",
|
|
source=source,
|
|
user_id="user_123",
|
|
card_id="custom_id_456",
|
|
)
|
|
|
|
assert card.id == "custom_id_456"
|
|
|
|
def test_generates_id_when_not_provided(
|
|
self, mock_utc_now, mock_generate_card_id
|
|
):
|
|
"""Test card generates ID when not provided."""
|
|
source = CardSource(type="news_item")
|
|
card = NewsCard(
|
|
topic="test",
|
|
source=source,
|
|
user_id="user_123",
|
|
)
|
|
|
|
assert card.id == "test_card_id_123"
|