""" Tests for news/core/base_card.py Tests cover: - CardSource dataclass - CardVersion dataclass - BaseCard abstract class - Card initialization and field generation """ import pytest from unittest.mock import Mock from dataclasses import is_dataclass from abc import ABC class TestCardSourceDataclass: """Tests for CardSource dataclass.""" def test_card_source_is_dataclass(self): """CardSource is a dataclass.""" from local_deep_research.news.core.base_card import CardSource assert is_dataclass(CardSource) def test_card_source_required_type_field(self): """CardSource requires type field.""" from local_deep_research.news.core.base_card import CardSource source = CardSource(type="news_item") assert source.type == "news_item" def test_card_source_optional_fields_defaults(self): """CardSource has optional fields with defaults.""" from local_deep_research.news.core.base_card import CardSource source = CardSource(type="test") assert source.source_id is None assert source.created_from == "" assert source.metadata == {} def test_card_source_custom_metadata(self): """CardSource accepts custom metadata.""" from local_deep_research.news.core.base_card import CardSource source = CardSource( type="test", metadata={"query": "test query", "timestamp": "2024-01-01"}, ) assert source.metadata["query"] == "test query" assert source.metadata["timestamp"] == "2024-01-01" class TestCardVersionDataclass: """Tests for CardVersion dataclass.""" def test_card_version_is_dataclass(self): """CardVersion is a dataclass.""" from local_deep_research.news.core.base_card import CardVersion assert is_dataclass(CardVersion) def test_card_version_initialization(self): """CardVersion initializes with required fields.""" from local_deep_research.news.core.base_card import CardVersion from datetime import datetime version = CardVersion( version_id="v1", created_at=datetime.now(), content={"result": "test"}, query_used="test query", ) assert version.version_id == "v1" assert version.query_used == "test query" assert version.content == {"result": "test"} def test_card_version_optional_search_strategy(self): """CardVersion has optional search_strategy.""" from local_deep_research.news.core.base_card import CardVersion from datetime import datetime version = CardVersion( version_id="v1", created_at=datetime.now(), content={}, query_used="test", ) assert version.search_strategy is None def test_card_version_generates_id_if_empty(self): """CardVersion generates ID if provided ID is empty.""" from local_deep_research.news.core.base_card import CardVersion from datetime import datetime version = CardVersion( version_id="", created_at=datetime.now(), content={}, query_used="test", ) assert version.version_id != "" assert len(version.version_id) > 0 class TestBaseCardClass: """Tests for BaseCard abstract class.""" def test_base_card_is_abstract(self): """BaseCard is an abstract class.""" from local_deep_research.news.core.base_card import BaseCard assert issubclass(BaseCard, ABC) def test_base_card_is_dataclass(self): """BaseCard is a dataclass.""" from local_deep_research.news.core.base_card import BaseCard assert is_dataclass(BaseCard) class TestConcreteCard: """Tests using a concrete implementation of BaseCard.""" @pytest.fixture def card_source(self): """Create a card source.""" from local_deep_research.news.core.base_card import CardSource return CardSource(type="test") @pytest.fixture def concrete_card_class(self): """Create a concrete card class.""" from local_deep_research.news.core.base_card import BaseCard class TestCard(BaseCard): def get_card_type(self): return "test" def to_dict(self): return {"id": self.id, "topic": self.topic} return TestCard def test_card_initialization(self, concrete_card_class, card_source): """Card initializes with required fields.""" card = concrete_card_class( topic="Test Topic", source=card_source, user_id="user123", ) assert card.topic == "Test Topic" assert card.source is card_source assert card.user_id == "user123" def test_card_generates_id(self, concrete_card_class, card_source): """Card generates ID when not provided.""" card = concrete_card_class( topic="Test Topic", source=card_source, user_id="user123", ) assert card.id is not None assert len(card.id) > 0 def test_card_uses_provided_id(self, concrete_card_class, card_source): """Card uses provided card_id.""" card = concrete_card_class( topic="Test Topic", source=card_source, user_id="user123", card_id="custom-id-123", ) assert card.id == "custom-id-123" def test_card_timestamps_generated(self, concrete_card_class, card_source): """Card generates timestamps on creation.""" card = concrete_card_class( topic="Test Topic", source=card_source, user_id="user123", ) assert card.created_at is not None assert card.updated_at is not None def test_card_interaction_defaults(self, concrete_card_class, card_source): """Card has default interaction values.""" card = concrete_card_class( topic="Test Topic", source=card_source, user_id="user123", ) assert card.interaction["votes_up"] == 0 assert card.interaction["votes_down"] == 0 assert card.interaction["views"] == 0 assert card.interaction["shares"] == 0 def test_card_empty_versions_list(self, concrete_card_class, card_source): """Card has empty versions list initially.""" card = concrete_card_class( topic="Test Topic", source=card_source, user_id="user123", ) assert card.versions == [] def test_card_optional_parent_id(self, concrete_card_class, card_source): """Card has optional parent_card_id.""" card = concrete_card_class( topic="Test Topic", source=card_source, user_id="user123", parent_card_id="parent-123", ) assert card.parent_card_id == "parent-123" def test_card_default_metadata(self, concrete_card_class, card_source): """Card has empty default metadata.""" card = concrete_card_class( topic="Test Topic", source=card_source, user_id="user123", ) assert card.metadata == {} def test_card_custom_metadata(self, concrete_card_class, card_source): """Card accepts custom metadata.""" card = concrete_card_class( topic="Test Topic", source=card_source, user_id="user123", metadata={"key": "value"}, ) assert card.metadata == {"key": "value"} class TestCardProgressCallback: """Tests for card progress callback.""" @pytest.fixture def card_source(self): """Create a card source.""" from local_deep_research.news.core.base_card import CardSource return CardSource(type="test") @pytest.fixture def concrete_card_class(self): """Create a concrete card class.""" from local_deep_research.news.core.base_card import BaseCard class TestCard(BaseCard): def get_card_type(self): return "test" def to_dict(self): return {"id": self.id, "topic": self.topic} return TestCard def test_card_progress_callback_initially_none( self, concrete_card_class, card_source ): """Card progress_callback is None initially.""" card = concrete_card_class( topic="Test", source=card_source, user_id="user123", ) assert card.progress_callback is None def test_card_set_progress_callback(self, concrete_card_class, card_source): """Card can set progress callback.""" card = concrete_card_class( topic="Test", source=card_source, user_id="user123", ) callback = Mock() card.set_progress_callback(callback) assert card.progress_callback is callback