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
342 lines
12 KiB
Python
342 lines
12 KiB
Python
"""
|
|
Comprehensive tests for topic_generator module.
|
|
Tests topic extraction with LLM integration, validation, and edge cases.
|
|
"""
|
|
|
|
from unittest.mock import patch
|
|
|
|
|
|
class TestGenerateTopics:
|
|
"""Tests for generate_topics function."""
|
|
|
|
@patch("local_deep_research.news.utils.topic_generator._generate_with_llm")
|
|
@patch("local_deep_research.news.utils.topic_generator._validate_topics")
|
|
def test_returns_validated_topics(self, mock_validate, mock_llm_gen):
|
|
"""Test returns validated topics from LLM."""
|
|
from local_deep_research.news.utils.topic_generator import (
|
|
generate_topics,
|
|
)
|
|
|
|
mock_llm_gen.return_value = ["AI", "Technology", "Research"]
|
|
mock_validate.return_value = ["ai", "technology", "research"]
|
|
|
|
result = generate_topics("AI news", findings="Content")
|
|
|
|
assert result == ["ai", "technology", "research"]
|
|
|
|
@patch("local_deep_research.news.utils.topic_generator._generate_with_llm")
|
|
@patch("local_deep_research.news.utils.topic_generator._validate_topics")
|
|
def test_returns_failure_marker_when_llm_fails(
|
|
self, mock_validate, mock_llm_gen
|
|
):
|
|
"""Test returns failure marker when LLM returns empty."""
|
|
from local_deep_research.news.utils.topic_generator import (
|
|
generate_topics,
|
|
)
|
|
|
|
mock_llm_gen.return_value = []
|
|
mock_validate.return_value = ["[topic generation failed]"]
|
|
|
|
_ = generate_topics("query", findings="data")
|
|
|
|
mock_validate.assert_called_once()
|
|
call_args = mock_validate.call_args[0]
|
|
assert call_args[0] == ["[Topic generation failed]"]
|
|
|
|
@patch("local_deep_research.news.utils.topic_generator._generate_with_llm")
|
|
@patch("local_deep_research.news.utils.topic_generator._validate_topics")
|
|
def test_passes_max_topics_to_validator(self, mock_validate, mock_llm_gen):
|
|
"""Test passes max_topics to validator."""
|
|
from local_deep_research.news.utils.topic_generator import (
|
|
generate_topics,
|
|
)
|
|
|
|
mock_llm_gen.return_value = ["A", "B"]
|
|
mock_validate.return_value = ["a", "b"]
|
|
|
|
generate_topics("query", max_topics=3)
|
|
|
|
mock_validate.assert_called_once()
|
|
assert mock_validate.call_args[0][1] == 3
|
|
|
|
@patch("local_deep_research.news.utils.topic_generator._generate_with_llm")
|
|
@patch("local_deep_research.news.utils.topic_generator._validate_topics")
|
|
def test_default_max_topics_is_5(self, mock_validate, mock_llm_gen):
|
|
"""Test default max_topics is 5."""
|
|
from local_deep_research.news.utils.topic_generator import (
|
|
generate_topics,
|
|
)
|
|
|
|
mock_llm_gen.return_value = []
|
|
mock_validate.return_value = []
|
|
|
|
generate_topics("query")
|
|
|
|
mock_llm_gen.assert_called_once()
|
|
# Fourth argument is max_topics
|
|
assert mock_llm_gen.call_args[0][3] == 5
|
|
|
|
@patch("local_deep_research.news.utils.topic_generator._generate_with_llm")
|
|
@patch("local_deep_research.news.utils.topic_generator._validate_topics")
|
|
def test_passes_category_to_llm(self, mock_validate, mock_llm_gen):
|
|
"""Test passes category to LLM generator."""
|
|
from local_deep_research.news.utils.topic_generator import (
|
|
generate_topics,
|
|
)
|
|
|
|
mock_llm_gen.return_value = []
|
|
mock_validate.return_value = []
|
|
|
|
generate_topics("query", category="Technology")
|
|
|
|
assert mock_llm_gen.call_args[0][2] == "Technology"
|
|
|
|
|
|
class TestGenerateWithLLMTopicsBehavior:
|
|
"""Tests for _generate_with_llm behavior."""
|
|
|
|
def test_function_exists_and_is_callable(self):
|
|
"""Test _generate_with_llm function exists and is callable."""
|
|
from local_deep_research.news.utils.topic_generator import (
|
|
_generate_with_llm,
|
|
)
|
|
|
|
assert callable(_generate_with_llm)
|
|
|
|
def test_accepts_four_parameters(self):
|
|
"""Test accepts query, findings, category, and max_topics parameters."""
|
|
from local_deep_research.news.utils.topic_generator import (
|
|
_generate_with_llm,
|
|
)
|
|
import inspect
|
|
|
|
sig = inspect.signature(_generate_with_llm)
|
|
params = list(sig.parameters.keys())
|
|
|
|
# Now accepts settings_snapshot for egress-policy threading.
|
|
assert len(params) == 5
|
|
assert "settings_snapshot" in params
|
|
assert "query" in params
|
|
assert "findings" in params
|
|
assert "category" in params
|
|
assert "max_topics" in params
|
|
|
|
def test_returns_list(self):
|
|
"""Test returns a list (empty or with topics)."""
|
|
from local_deep_research.news.utils.topic_generator import (
|
|
_generate_with_llm,
|
|
)
|
|
|
|
# Without proper LLM setup, may return empty list
|
|
result = _generate_with_llm("query", "", "", 5)
|
|
|
|
assert isinstance(result, list)
|
|
|
|
|
|
class TestValidateTopics:
|
|
"""Tests for _validate_topics function."""
|
|
|
|
def test_returns_list(self):
|
|
"""Test returns a list."""
|
|
from local_deep_research.news.utils.topic_generator import (
|
|
_validate_topics,
|
|
)
|
|
|
|
result = _validate_topics(["AI", "Tech"], 5)
|
|
|
|
assert isinstance(result, list)
|
|
|
|
def test_removes_empty_topics(self):
|
|
"""Test removes empty string topics."""
|
|
from local_deep_research.news.utils.topic_generator import (
|
|
_validate_topics,
|
|
)
|
|
|
|
result = _validate_topics(["AI", "", "Tech", ""], 5)
|
|
|
|
assert "" not in result
|
|
|
|
def test_removes_whitespace_only_topics(self):
|
|
"""Test removes whitespace-only topics."""
|
|
from local_deep_research.news.utils.topic_generator import (
|
|
_validate_topics,
|
|
)
|
|
|
|
result = _validate_topics(["AI", " ", "Tech"], 5)
|
|
|
|
assert len([t for t in result if t.strip() == ""]) == 0
|
|
|
|
def test_removes_too_short_topics(self):
|
|
"""Test removes topics shorter than 2 characters."""
|
|
from local_deep_research.news.utils.topic_generator import (
|
|
_validate_topics,
|
|
)
|
|
|
|
result = _validate_topics(["A", "AI", "Tech"], 5)
|
|
|
|
assert "A" not in result or len("A") >= 2 # Single char removed
|
|
|
|
def test_removes_too_long_topics(self):
|
|
"""Test removes topics longer than 30 characters."""
|
|
from local_deep_research.news.utils.topic_generator import (
|
|
_validate_topics,
|
|
)
|
|
|
|
long_topic = "A" * 35
|
|
result = _validate_topics(["AI", long_topic, "Tech"], 5)
|
|
|
|
assert long_topic not in result
|
|
|
|
def test_removes_duplicates_case_insensitive(self):
|
|
"""Test removes duplicates case-insensitively."""
|
|
from local_deep_research.news.utils.topic_generator import (
|
|
_validate_topics,
|
|
)
|
|
|
|
result = _validate_topics(["AI", "ai", "Ai"], 5)
|
|
|
|
assert len(result) == 1
|
|
|
|
def test_limits_to_max_topics(self):
|
|
"""Test limits output to max_topics."""
|
|
from local_deep_research.news.utils.topic_generator import (
|
|
_validate_topics,
|
|
)
|
|
|
|
topics = ["AI", "Tech", "Science", "News", "Research", "Data"]
|
|
result = _validate_topics(topics, 3)
|
|
|
|
assert len(result) <= 3
|
|
|
|
def test_converts_to_lowercase(self):
|
|
"""Test converts topics to lowercase."""
|
|
from local_deep_research.news.utils.topic_generator import (
|
|
_validate_topics,
|
|
)
|
|
|
|
result = _validate_topics(["UPPERCASE", "MixedCase"], 5)
|
|
|
|
assert all(t == t.lower() for t in result if t != "[No valid topics]")
|
|
|
|
def test_returns_no_valid_marker_when_empty(self):
|
|
"""Test returns marker when no valid topics."""
|
|
from local_deep_research.news.utils.topic_generator import (
|
|
_validate_topics,
|
|
)
|
|
|
|
result = _validate_topics([], 5)
|
|
|
|
assert result == ["[No valid topics]"]
|
|
|
|
def test_returns_no_valid_marker_when_all_invalid(self):
|
|
"""Test returns marker when all topics are invalid."""
|
|
from local_deep_research.news.utils.topic_generator import (
|
|
_validate_topics,
|
|
)
|
|
|
|
result = _validate_topics(["", "A", "B" * 50], 5)
|
|
|
|
assert result == ["[No valid topics]"]
|
|
|
|
|
|
class TestTopicGeneratorEdgeCases:
|
|
"""Edge case tests for topic generator."""
|
|
|
|
@patch("local_deep_research.news.utils.topic_generator._generate_with_llm")
|
|
@patch("local_deep_research.news.utils.topic_generator._validate_topics")
|
|
def test_handles_unicode_topics(self, mock_validate, mock_llm_gen):
|
|
"""Test handles unicode characters in topics."""
|
|
from local_deep_research.news.utils.topic_generator import (
|
|
generate_topics,
|
|
)
|
|
|
|
mock_llm_gen.return_value = ["日本", "テクノロジー"]
|
|
mock_validate.return_value = ["日本", "テクノロジー"]
|
|
|
|
result = generate_topics("Japanese tech news")
|
|
|
|
assert result == ["日本", "テクノロジー"]
|
|
|
|
def test_validate_topics_handles_mixed_types(self):
|
|
"""Test _validate_topics handles mixed types."""
|
|
from local_deep_research.news.utils.topic_generator import (
|
|
_validate_topics,
|
|
)
|
|
|
|
# Validate handles valid strings
|
|
result = _validate_topics(["AI", "Tech", "Science"], 5)
|
|
|
|
assert isinstance(result, list)
|
|
assert all(isinstance(t, str) for t in result)
|
|
|
|
def test_generate_topics_returns_list_for_empty_input(self):
|
|
"""Test generate_topics returns list for empty input."""
|
|
from local_deep_research.news.utils.topic_generator import (
|
|
generate_topics,
|
|
)
|
|
|
|
# With empty inputs, should return failure marker
|
|
result = generate_topics("")
|
|
|
|
assert isinstance(result, list)
|
|
|
|
def test_validate_topics_handles_none_input(self):
|
|
"""Test _validate_topics handles None input gracefully."""
|
|
from local_deep_research.news.utils.topic_generator import (
|
|
_validate_topics,
|
|
)
|
|
|
|
# If topics is None, function should handle it
|
|
try:
|
|
_ = _validate_topics(None, 5)
|
|
# Should return failure marker or handle gracefully
|
|
except (TypeError, AttributeError):
|
|
# This is acceptable - None input may raise
|
|
pass
|
|
|
|
@patch("local_deep_research.news.utils.topic_generator._generate_with_llm")
|
|
@patch("local_deep_research.news.utils.topic_generator._validate_topics")
|
|
def test_zero_max_topics(self, mock_validate, mock_llm_gen):
|
|
"""Test handles zero max_topics."""
|
|
from local_deep_research.news.utils.topic_generator import (
|
|
generate_topics,
|
|
)
|
|
|
|
mock_llm_gen.return_value = []
|
|
mock_validate.return_value = []
|
|
|
|
result = generate_topics("query", max_topics=0)
|
|
|
|
assert isinstance(result, list)
|
|
|
|
|
|
class TestTopicGeneratorImports:
|
|
"""Tests for module imports."""
|
|
|
|
def test_imports_generate_topics(self):
|
|
"""Test generate_topics can be imported."""
|
|
from local_deep_research.news.utils.topic_generator import (
|
|
generate_topics,
|
|
)
|
|
|
|
assert generate_topics is not None
|
|
assert callable(generate_topics)
|
|
|
|
def test_imports_validate_topics(self):
|
|
"""Test _validate_topics can be imported."""
|
|
from local_deep_research.news.utils.topic_generator import (
|
|
_validate_topics,
|
|
)
|
|
|
|
assert _validate_topics is not None
|
|
assert callable(_validate_topics)
|
|
|
|
def test_imports_generate_with_llm(self):
|
|
"""Test _generate_with_llm can be imported."""
|
|
from local_deep_research.news.utils.topic_generator import (
|
|
_generate_with_llm,
|
|
)
|
|
|
|
assert _generate_with_llm is not None
|
|
assert callable(_generate_with_llm)
|