Files
wehub-resource-sync 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
chore: import upstream snapshot with attribution
2026-07-13 13:08:55 +08:00

416 lines
15 KiB
Python

"""Extended tests for search_system.py - covering settings processing, programmatic mode,
strategy selection, and result processing."""
from unittest.mock import Mock, patch
def _make_strategy_mock():
"""Create a mock strategy with required attributes."""
mock_strategy = Mock()
mock_strategy.questions_by_iteration = []
mock_strategy.all_links_of_system = []
return mock_strategy
def _patch_system():
"""Return nested context managers for search system patching.
Usage:
with _create_patched():
...
Returns a helper that patches both create_strategy and StandardCitationHandler.
"""
class PatchContext:
def __init__(self):
self.strategy = _make_strategy_mock()
self._p1 = patch(
"local_deep_research.search_system_factory.create_strategy",
return_value=self.strategy,
)
self._p2 = patch(
"local_deep_research.citation_handlers.standard_citation_handler.StandardCitationHandler",
return_value=Mock(
_create_documents=Mock(), _format_sources=Mock()
),
)
def __enter__(self):
self.mock_create = self._p1.__enter__()
self.mock_citation = self._p2.__enter__()
return self
def __exit__(self, *args):
self._p2.__exit__(*args)
self._p1.__exit__(*args)
return PatchContext()
class TestAdvancedSearchSystemSettingsProcessing:
"""Tests for settings snapshot processing in AdvancedSearchSystem.__init__."""
def test_max_iterations_from_dict_settings(self):
"""max_iterations should be extracted from dict-wrapped settings."""
settings = {
"search.iterations": {"value": 5, "ui_element": "number"},
"search.questions_per_iteration": {
"value": 4,
"ui_element": "number",
},
}
with _patch_system():
from local_deep_research.search_system import AdvancedSearchSystem
system = AdvancedSearchSystem(
llm=Mock(), search=Mock(), settings_snapshot=settings
)
assert system.max_iterations == 5
assert system.questions_per_iteration == 4
def test_max_iterations_from_direct_value_settings(self):
"""max_iterations should work with direct (non-dict) settings values."""
settings = {
"search.iterations": 3,
"search.questions_per_iteration": 2,
}
with _patch_system():
from local_deep_research.search_system import AdvancedSearchSystem
system = AdvancedSearchSystem(
llm=Mock(), search=Mock(), settings_snapshot=settings
)
assert system.max_iterations == 3
assert system.questions_per_iteration == 2
def test_max_iterations_defaults_when_missing(self):
"""max_iterations should default to 1 when not in settings."""
with _patch_system():
from local_deep_research.search_system import AdvancedSearchSystem
system = AdvancedSearchSystem(
llm=Mock(), search=Mock(), settings_snapshot={}
)
assert system.max_iterations == 1
assert system.questions_per_iteration == 3
def test_explicit_max_iterations_overrides_settings(self):
"""Explicit max_iterations parameter should override settings snapshot."""
settings = {"search.iterations": {"value": 10}}
with _patch_system():
from local_deep_research.search_system import AdvancedSearchSystem
system = AdvancedSearchSystem(
llm=Mock(),
search=Mock(),
max_iterations=7,
settings_snapshot=settings,
)
assert system.max_iterations == 7
class TestAdvancedSearchSystemProgrammaticMode:
"""Tests for programmatic mode behavior."""
def test_programmatic_mode_sets_flag(self):
"""programmatic_mode flag should be stored."""
with _patch_system():
from local_deep_research.search_system import AdvancedSearchSystem
system = AdvancedSearchSystem(
llm=Mock(), search=Mock(), programmatic_mode=True
)
assert system.programmatic_mode is True
def test_non_programmatic_mode_default(self):
"""programmatic_mode should default to False."""
with _patch_system():
from local_deep_research.search_system import AdvancedSearchSystem
system = AdvancedSearchSystem(llm=Mock(), search=Mock())
assert system.programmatic_mode is False
class TestAdvancedSearchSystemResearchContext:
"""Tests for research context handling."""
def test_stores_research_context(self):
"""research_context should be stored on the system."""
context = {"delegate_strategy": "rapid", "some_key": "some_value"}
with _patch_system():
from local_deep_research.search_system import AdvancedSearchSystem
system = AdvancedSearchSystem(
llm=Mock(), search=Mock(), research_context=context
)
assert system.research_context == context
def test_none_research_context(self):
"""None research_context should not cause errors."""
with _patch_system():
from local_deep_research.search_system import AdvancedSearchSystem
system = AdvancedSearchSystem(
llm=Mock(), search=Mock(), research_context=None
)
assert system.research_context is None
def test_research_id_stored(self):
"""research_id should be stored on the system."""
with _patch_system():
from local_deep_research.search_system import AdvancedSearchSystem
system = AdvancedSearchSystem(
llm=Mock(), search=Mock(), research_id="test-123"
)
assert system.research_id == "test-123"
class TestAdvancedSearchSystemContextualFollowUp:
"""Tests for contextual follow-up strategy initialization."""
def test_contextual_followup_uses_delegate(self):
"""Contextual follow-up strategy should create a delegate strategy."""
context = {"delegate_strategy": "rapid"}
with _patch_system() as ctx:
with patch(
"local_deep_research.search_system.EnhancedContextualFollowUpStrategy"
) as mock_followup_cls:
mock_followup_cls.return_value = Mock()
from local_deep_research.search_system import (
AdvancedSearchSystem,
)
AdvancedSearchSystem(
llm=Mock(),
search=Mock(),
strategy_name="enhanced-contextual-followup",
research_context=context,
)
# Verify delegate strategy was created with rapid
ctx.mock_create.assert_called()
call_kwargs = ctx.mock_create.call_args
assert call_kwargs.kwargs.get("strategy_name") == "rapid" or (
call_kwargs[1].get("strategy_name") == "rapid"
)
def test_contextual_followup_defaults_delegate_to_source_based(self):
"""Contextual follow-up without delegate_strategy should default to source-based."""
with _patch_system() as ctx:
with patch(
"local_deep_research.search_system.EnhancedContextualFollowUpStrategy"
) as mock_followup_cls:
mock_followup_cls.return_value = Mock()
from local_deep_research.search_system import (
AdvancedSearchSystem,
)
AdvancedSearchSystem(
llm=Mock(),
search=Mock(),
strategy_name="contextual-followup",
research_context={},
)
call_kwargs = ctx.mock_create.call_args
strategy_name = call_kwargs.kwargs.get(
"strategy_name"
) or call_kwargs[1].get("strategy_name")
assert strategy_name == "source-based"
def test_contextual_followup_name_variants(self):
"""All contextual follow-up name variants should be recognized."""
variants = [
"enhanced-contextual-followup",
"enhanced_contextual_followup",
"contextual-followup",
"contextual_followup",
]
for variant in variants:
with _patch_system():
with patch(
"local_deep_research.search_system.EnhancedContextualFollowUpStrategy"
) as mock_followup_cls:
mock_followup_cls.return_value = Mock()
from local_deep_research.search_system import (
AdvancedSearchSystem,
)
AdvancedSearchSystem(
llm=Mock(),
search=Mock(),
strategy_name=variant,
)
# Should use EnhancedContextualFollowUpStrategy
mock_followup_cls.assert_called_once()
class TestAdvancedSearchSystemSearchOriginalQuery:
"""Tests for search_original_query parameter."""
def test_search_original_query_default_true(self):
"""search_original_query should default to True."""
with _patch_system():
from local_deep_research.search_system import AdvancedSearchSystem
system = AdvancedSearchSystem(llm=Mock(), search=Mock())
assert system.search_original_query is True
def test_search_original_query_can_be_disabled(self):
"""search_original_query can be set to False for news searches."""
with _patch_system():
from local_deep_research.search_system import AdvancedSearchSystem
system = AdvancedSearchSystem(
llm=Mock(), search=Mock(), search_original_query=False
)
assert system.search_original_query is False
class TestAdvancedSearchSystemPerformSearch:
"""Tests for _perform_search result processing."""
def test_result_includes_query(self):
"""Result should include the query if not returned by strategy."""
with _patch_system() as ctx:
ctx.strategy.analyze_topic.return_value = {
"current_knowledge": "some results"
}
ctx.strategy.questions_by_iteration = {1: ["q1"]}
from local_deep_research.search_system import AdvancedSearchSystem
system = AdvancedSearchSystem(llm=Mock(), search=Mock())
with patch(
"local_deep_research.news.core.search_integration.NewsSearchCallback"
):
result = system.analyze_topic("test query")
assert result["query"] == "test query"
def test_result_includes_search_system_reference(self):
"""Result should include reference to the search system."""
with _patch_system() as ctx:
ctx.strategy.analyze_topic.return_value = {
"current_knowledge": "results"
}
ctx.strategy.questions_by_iteration = {}
from local_deep_research.search_system import AdvancedSearchSystem
system = AdvancedSearchSystem(llm=Mock(), search=Mock())
with patch(
"local_deep_research.news.core.search_integration.NewsSearchCallback"
):
result = system.analyze_topic("test query")
assert result["search_system"] is system
def test_settings_snapshot_extraction_in_perform_search(self):
"""_perform_search should extract LLM/search info from settings snapshot."""
settings = {
"llm.provider": {"value": "openai"},
"llm.model": {"value": "gpt-4"},
"search.tool": {"value": "searxng"},
}
progress_calls = []
def capture_progress(msg, progress, metadata):
progress_calls.append((msg, progress, metadata))
with _patch_system() as ctx:
ctx.strategy.analyze_topic.return_value = {"current_knowledge": "r"}
ctx.strategy.questions_by_iteration = {}
from local_deep_research.search_system import AdvancedSearchSystem
system = AdvancedSearchSystem(
llm=Mock(), search=Mock(), settings_snapshot=settings
)
system.set_progress_callback(capture_progress)
with patch(
"local_deep_research.news.core.search_integration.NewsSearchCallback"
):
system.analyze_topic("test")
# Should have progress callbacks with LLM and search info
assert len(progress_calls) >= 2
assert "openai" in progress_calls[0][0]
assert "gpt-4" in progress_calls[0][0]
def test_links_deduplication(self):
"""all_links_of_system should not be duplicated when same object."""
shared_links = [{"title": "link1", "url": "http://example.com"}]
with _patch_system() as ctx:
ctx.strategy.analyze_topic.return_value = {"current_knowledge": "r"}
ctx.strategy.questions_by_iteration = {}
ctx.strategy.all_links_of_system = shared_links
from local_deep_research.search_system import AdvancedSearchSystem
system = AdvancedSearchSystem(llm=Mock(), search=Mock())
system.all_links_of_system = shared_links
with patch(
"local_deep_research.news.core.search_integration.NewsSearchCallback"
):
system.analyze_topic("test")
# Should NOT have duplicated the links
assert len(system.all_links_of_system) == 1
class TestAdvancedSearchSystemProgressCallback:
"""Tests for progress callback mechanism."""
def test_set_progress_callback_propagates_to_strategy(self):
"""set_progress_callback should propagate to the strategy."""
with _patch_system() as ctx:
from local_deep_research.search_system import AdvancedSearchSystem
system = AdvancedSearchSystem(llm=Mock(), search=Mock())
def noop_callback(msg, pct, meta):
return None
system.set_progress_callback(noop_callback)
ctx.strategy.set_progress_callback.assert_called_with(noop_callback)
def test_progress_callback_before_strategy_exists(self):
"""Setting callback before strategy is created should not crash."""
with _patch_system():
from local_deep_research.search_system import AdvancedSearchSystem
system = AdvancedSearchSystem(llm=Mock(), search=Mock())
# Default callback should be a no-op lambda
system.progress_callback("test", 50, {})