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
449 lines
14 KiB
Python
449 lines
14 KiB
Python
"""
|
|
Comprehensive tests for TopicBasedRecommender class.
|
|
Tests recommendation generation, topic filtering, and user preferences.
|
|
"""
|
|
|
|
from unittest.mock import Mock, patch
|
|
|
|
|
|
class TestTopicBasedRecommenderInit:
|
|
"""Tests for TopicBasedRecommender initialization."""
|
|
|
|
def test_inherits_from_base(self):
|
|
"""Test inherits from BaseRecommender."""
|
|
from local_deep_research.news.recommender.topic_based import (
|
|
TopicBasedRecommender,
|
|
)
|
|
from local_deep_research.news.recommender.base_recommender import (
|
|
BaseRecommender,
|
|
)
|
|
|
|
assert issubclass(TopicBasedRecommender, BaseRecommender)
|
|
|
|
def test_default_max_recommendations(self):
|
|
"""Test default max_recommendations is 5."""
|
|
from local_deep_research.news.recommender.topic_based import (
|
|
TopicBasedRecommender,
|
|
)
|
|
|
|
recommender = TopicBasedRecommender()
|
|
assert recommender.max_recommendations == 5
|
|
|
|
|
|
class TestGenerateRecommendations:
|
|
"""Tests for generate_recommendations method."""
|
|
|
|
@patch.object(
|
|
__import__(
|
|
"local_deep_research.news.recommender.topic_based",
|
|
fromlist=["TopicBasedRecommender"],
|
|
).TopicBasedRecommender,
|
|
"_get_trending_topics",
|
|
)
|
|
@patch.object(
|
|
__import__(
|
|
"local_deep_research.news.recommender.topic_based",
|
|
fromlist=["TopicBasedRecommender"],
|
|
).TopicBasedRecommender,
|
|
"_get_user_preferences",
|
|
)
|
|
@patch.object(
|
|
__import__(
|
|
"local_deep_research.news.recommender.topic_based",
|
|
fromlist=["TopicBasedRecommender"],
|
|
).TopicBasedRecommender,
|
|
"_filter_topics_by_preferences",
|
|
)
|
|
@patch.object(
|
|
__import__(
|
|
"local_deep_research.news.recommender.topic_based",
|
|
fromlist=["TopicBasedRecommender"],
|
|
).TopicBasedRecommender,
|
|
"_sort_by_relevance",
|
|
)
|
|
def test_returns_list(
|
|
self, mock_sort, mock_filter, mock_prefs, mock_topics
|
|
):
|
|
"""Test returns a list."""
|
|
from local_deep_research.news.recommender.topic_based import (
|
|
TopicBasedRecommender,
|
|
)
|
|
|
|
mock_topics.return_value = []
|
|
mock_prefs.return_value = {}
|
|
mock_filter.return_value = []
|
|
mock_sort.return_value = []
|
|
|
|
recommender = TopicBasedRecommender()
|
|
result = recommender.generate_recommendations("user123")
|
|
|
|
assert isinstance(result, list)
|
|
|
|
@patch.object(
|
|
__import__(
|
|
"local_deep_research.news.recommender.topic_based",
|
|
fromlist=["TopicBasedRecommender"],
|
|
).TopicBasedRecommender,
|
|
"_get_trending_topics",
|
|
)
|
|
def test_updates_progress(self, mock_topics):
|
|
"""Test updates progress during generation."""
|
|
from local_deep_research.news.recommender.topic_based import (
|
|
TopicBasedRecommender,
|
|
)
|
|
|
|
mock_topics.return_value = []
|
|
|
|
recommender = TopicBasedRecommender()
|
|
recommender._update_progress = Mock()
|
|
|
|
with patch.object(
|
|
recommender, "_get_user_preferences", return_value={}
|
|
):
|
|
with patch.object(
|
|
recommender, "_filter_topics_by_preferences", return_value=[]
|
|
):
|
|
with patch.object(
|
|
recommender, "_sort_by_relevance", return_value=[]
|
|
):
|
|
recommender.generate_recommendations("user123")
|
|
|
|
recommender._update_progress.assert_called()
|
|
|
|
@patch.object(
|
|
__import__(
|
|
"local_deep_research.news.recommender.topic_based",
|
|
fromlist=["TopicBasedRecommender"],
|
|
).TopicBasedRecommender,
|
|
"_get_trending_topics",
|
|
)
|
|
def test_handles_exception(self, mock_topics):
|
|
"""Test handles exception gracefully."""
|
|
from local_deep_research.news.recommender.topic_based import (
|
|
TopicBasedRecommender,
|
|
)
|
|
|
|
mock_topics.side_effect = Exception("Error getting topics")
|
|
|
|
recommender = TopicBasedRecommender()
|
|
result = recommender.generate_recommendations("user123")
|
|
|
|
assert result == []
|
|
|
|
|
|
class TestGetTrendingTopics:
|
|
"""Tests for _get_trending_topics method."""
|
|
|
|
def test_returns_list(self):
|
|
"""Test returns a list."""
|
|
from local_deep_research.news.recommender.topic_based import (
|
|
TopicBasedRecommender,
|
|
)
|
|
|
|
recommender = TopicBasedRecommender()
|
|
result = recommender._get_trending_topics(None)
|
|
|
|
assert isinstance(result, list)
|
|
|
|
def test_returns_fallback_topics_when_empty(self):
|
|
"""Test returns fallback topics when no topics found."""
|
|
from local_deep_research.news.recommender.topic_based import (
|
|
TopicBasedRecommender,
|
|
)
|
|
|
|
recommender = TopicBasedRecommender()
|
|
recommender.topic_registry = None # No registry
|
|
|
|
result = recommender._get_trending_topics(None)
|
|
|
|
assert len(result) > 0 # Should have fallback topics
|
|
|
|
def test_uses_topic_registry_when_available(self):
|
|
"""Test uses topic registry when available."""
|
|
from local_deep_research.news.recommender.topic_based import (
|
|
TopicBasedRecommender,
|
|
)
|
|
|
|
mock_registry = Mock()
|
|
mock_registry.get_trending_topics.return_value = ["AI", "Climate"]
|
|
|
|
recommender = TopicBasedRecommender()
|
|
recommender.topic_registry = mock_registry
|
|
|
|
result = recommender._get_trending_topics(None)
|
|
|
|
assert "AI" in result
|
|
assert "Climate" in result
|
|
|
|
def test_adds_context_topics(self):
|
|
"""Test adds topics from context."""
|
|
from local_deep_research.news.recommender.topic_based import (
|
|
TopicBasedRecommender,
|
|
)
|
|
|
|
recommender = TopicBasedRecommender()
|
|
|
|
context = {"current_news_topics": ["Blockchain"]}
|
|
result = recommender._get_trending_topics(context)
|
|
|
|
assert "Blockchain" in result
|
|
|
|
|
|
class TestFilterTopicsByPreferences:
|
|
"""Tests for _filter_topics_by_preferences method."""
|
|
|
|
def test_returns_list(self):
|
|
"""Test returns a list."""
|
|
from local_deep_research.news.recommender.topic_based import (
|
|
TopicBasedRecommender,
|
|
)
|
|
|
|
recommender = TopicBasedRecommender()
|
|
result = recommender._filter_topics_by_preferences(["AI", "Sports"], {})
|
|
|
|
assert isinstance(result, list)
|
|
|
|
def test_filters_disliked_topics(self):
|
|
"""Test filters out disliked topics."""
|
|
from local_deep_research.news.recommender.topic_based import (
|
|
TopicBasedRecommender,
|
|
)
|
|
|
|
recommender = TopicBasedRecommender()
|
|
preferences = {"disliked_topics": ["Sports"]}
|
|
|
|
result = recommender._filter_topics_by_preferences(
|
|
["AI", "Sports", "Tech"], preferences
|
|
)
|
|
|
|
assert "Sports" not in result
|
|
|
|
def test_preserves_non_disliked_topics(self):
|
|
"""Test preserves non-disliked topics."""
|
|
from local_deep_research.news.recommender.topic_based import (
|
|
TopicBasedRecommender,
|
|
)
|
|
|
|
recommender = TopicBasedRecommender()
|
|
preferences = {"disliked_topics": ["Sports"]}
|
|
|
|
result = recommender._filter_topics_by_preferences(
|
|
["AI", "Sports", "Tech"], preferences
|
|
)
|
|
|
|
assert "AI" in result
|
|
assert "Tech" in result
|
|
|
|
|
|
class TestGenerateTopicQuery:
|
|
"""Tests for _generate_topic_query method."""
|
|
|
|
def test_returns_string(self):
|
|
"""Test returns a string."""
|
|
from local_deep_research.news.recommender.topic_based import (
|
|
TopicBasedRecommender,
|
|
)
|
|
|
|
recommender = TopicBasedRecommender()
|
|
result = recommender._generate_topic_query("artificial intelligence")
|
|
|
|
assert isinstance(result, str)
|
|
|
|
def test_includes_topic(self):
|
|
"""Test query includes the topic."""
|
|
from local_deep_research.news.recommender.topic_based import (
|
|
TopicBasedRecommender,
|
|
)
|
|
|
|
recommender = TopicBasedRecommender()
|
|
result = recommender._generate_topic_query("blockchain")
|
|
|
|
assert "blockchain" in result.lower()
|
|
|
|
|
|
class TestCreateRecommendationCard:
|
|
"""Tests for _create_recommendation_card method."""
|
|
|
|
@patch("local_deep_research.news.recommender.topic_based.CardFactory")
|
|
def test_returns_card_or_none(self, mock_factory):
|
|
"""Test returns card or None."""
|
|
from local_deep_research.news.recommender.topic_based import (
|
|
TopicBasedRecommender,
|
|
)
|
|
|
|
recommender = TopicBasedRecommender()
|
|
|
|
result = recommender._create_recommendation_card(
|
|
"AI", "AI news today", "user123"
|
|
)
|
|
|
|
# May return None or card depending on implementation
|
|
assert result is None or hasattr(result, "topic")
|
|
|
|
|
|
class TestSortByRelevance:
|
|
"""Tests for _sort_by_relevance method."""
|
|
|
|
def test_returns_list(self):
|
|
"""Test returns a list."""
|
|
from local_deep_research.news.recommender.topic_based import (
|
|
TopicBasedRecommender,
|
|
)
|
|
|
|
recommender = TopicBasedRecommender()
|
|
result = recommender._sort_by_relevance([], "user123")
|
|
|
|
assert isinstance(result, list)
|
|
|
|
def test_preserves_cards(self):
|
|
"""Test preserves all cards in output."""
|
|
from local_deep_research.news.recommender.topic_based import (
|
|
TopicBasedRecommender,
|
|
)
|
|
|
|
# Mock cards with required attributes for sorting
|
|
mock_cards = [
|
|
Mock(impact_score=5.0, category="tech", metadata={}),
|
|
Mock(impact_score=8.0, category="news", metadata={}),
|
|
Mock(impact_score=3.0, category="science", metadata={}),
|
|
]
|
|
|
|
recommender = TopicBasedRecommender()
|
|
result = recommender._sort_by_relevance(mock_cards, "user123")
|
|
|
|
assert len(result) == len(mock_cards)
|
|
|
|
|
|
class TestTopicBasedRecommenderWithContext:
|
|
"""Tests for recommendations with various contexts."""
|
|
|
|
def test_handles_empty_context(self):
|
|
"""Test handles empty context dict."""
|
|
from local_deep_research.news.recommender.topic_based import (
|
|
TopicBasedRecommender,
|
|
)
|
|
|
|
recommender = TopicBasedRecommender()
|
|
|
|
with patch.object(
|
|
recommender, "_get_trending_topics", return_value=["AI"]
|
|
) as mock_topics:
|
|
with patch.object(
|
|
recommender, "_get_user_preferences", return_value={}
|
|
):
|
|
with patch.object(
|
|
recommender,
|
|
"_filter_topics_by_preferences",
|
|
return_value=[],
|
|
):
|
|
with patch.object(
|
|
recommender, "_sort_by_relevance", return_value=[]
|
|
):
|
|
recommender.generate_recommendations(
|
|
"user123", context={}
|
|
)
|
|
|
|
mock_topics.assert_called_once_with({})
|
|
|
|
def test_handles_none_context(self):
|
|
"""Test handles None context."""
|
|
from local_deep_research.news.recommender.topic_based import (
|
|
TopicBasedRecommender,
|
|
)
|
|
|
|
recommender = TopicBasedRecommender()
|
|
|
|
with patch.object(
|
|
recommender, "_get_trending_topics", return_value=["AI"]
|
|
) as mock_topics:
|
|
with patch.object(
|
|
recommender, "_get_user_preferences", return_value={}
|
|
):
|
|
with patch.object(
|
|
recommender,
|
|
"_filter_topics_by_preferences",
|
|
return_value=[],
|
|
):
|
|
with patch.object(
|
|
recommender, "_sort_by_relevance", return_value=[]
|
|
):
|
|
recommender.generate_recommendations(
|
|
"user123", context=None
|
|
)
|
|
|
|
mock_topics.assert_called_once_with(None)
|
|
|
|
|
|
class TestTopicBasedRecommenderEdgeCases:
|
|
"""Edge case tests for TopicBasedRecommender."""
|
|
|
|
def test_handles_no_topics(self):
|
|
"""Test handles case with no topics."""
|
|
from local_deep_research.news.recommender.topic_based import (
|
|
TopicBasedRecommender,
|
|
)
|
|
|
|
recommender = TopicBasedRecommender()
|
|
|
|
with patch.object(recommender, "_get_trending_topics", return_value=[]):
|
|
with patch.object(
|
|
recommender, "_get_user_preferences", return_value={}
|
|
):
|
|
with patch.object(
|
|
recommender,
|
|
"_filter_topics_by_preferences",
|
|
return_value=[],
|
|
):
|
|
with patch.object(
|
|
recommender, "_sort_by_relevance", return_value=[]
|
|
):
|
|
result = recommender.generate_recommendations("user123")
|
|
|
|
assert result == []
|
|
|
|
def test_limits_recommendations(self):
|
|
"""Test limits recommendations to max_recommendations."""
|
|
from local_deep_research.news.recommender.topic_based import (
|
|
TopicBasedRecommender,
|
|
)
|
|
|
|
recommender = TopicBasedRecommender()
|
|
recommender.max_recommendations = 3
|
|
|
|
with patch.object(
|
|
recommender,
|
|
"_get_trending_topics",
|
|
return_value=["A", "B", "C", "D", "E"],
|
|
):
|
|
with patch.object(
|
|
recommender, "_get_user_preferences", return_value={}
|
|
):
|
|
with patch.object(
|
|
recommender,
|
|
"_filter_topics_by_preferences",
|
|
return_value=["A", "B", "C", "D", "E"],
|
|
):
|
|
with patch.object(
|
|
recommender,
|
|
"_create_recommendation_card",
|
|
return_value=None,
|
|
):
|
|
with patch.object(
|
|
recommender, "_sort_by_relevance", return_value=[]
|
|
):
|
|
recommender.generate_recommendations("user123")
|
|
|
|
# Implementation should only process max_recommendations topics
|
|
|
|
|
|
class TestTopicBasedRecommenderImports:
|
|
"""Tests for module imports."""
|
|
|
|
def test_is_class(self):
|
|
"""Test TopicBasedRecommender is a class."""
|
|
from local_deep_research.news.recommender.topic_based import (
|
|
TopicBasedRecommender,
|
|
)
|
|
|
|
assert isinstance(TopicBasedRecommender, type)
|