chore: import upstream snapshot with attribution
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
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
This commit is contained in:
@@ -0,0 +1,541 @@
|
||||
"""
|
||||
Additional tests for MCP server to increase coverage.
|
||||
|
||||
Tests for:
|
||||
- Falsy value handling in _build_settings_overrides
|
||||
- Discovery tools error paths
|
||||
- Entry point functions
|
||||
- Temperature parameter in research tools
|
||||
- Import smoke tests
|
||||
- formatted_findings field
|
||||
"""
|
||||
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
|
||||
# Skip all tests if MCP is not available
|
||||
try:
|
||||
import mcp # noqa: F401
|
||||
|
||||
MCP_AVAILABLE = True
|
||||
except ImportError:
|
||||
MCP_AVAILABLE = False
|
||||
|
||||
pytestmark = pytest.mark.skipif(
|
||||
not MCP_AVAILABLE, reason="MCP package not installed"
|
||||
)
|
||||
|
||||
|
||||
class TestBuildSettingsOverridesFalsyValues:
|
||||
"""Tests for falsy value handling in _build_settings_overrides.
|
||||
|
||||
Note: The implementation uses 'is not None' checks, so falsy values
|
||||
like 0, 0.0, and "" ARE included in the result (they are valid values).
|
||||
"""
|
||||
|
||||
def test_iterations_zero_is_added(self):
|
||||
"""Test that iterations=0 IS added (is not None check)."""
|
||||
from local_deep_research.mcp.server import _build_settings_overrides
|
||||
|
||||
# Implementation uses 'is not None', so 0 is added
|
||||
result = _build_settings_overrides(iterations=0)
|
||||
assert result.get("search.iterations") == 0
|
||||
|
||||
def test_questions_per_iteration_zero_is_added(self):
|
||||
"""Test that questions_per_iteration=0 IS added."""
|
||||
from local_deep_research.mcp.server import _build_settings_overrides
|
||||
|
||||
result = _build_settings_overrides(questions_per_iteration=0)
|
||||
assert result.get("search.questions_per_iteration") == 0
|
||||
|
||||
def test_temperature_zero_is_added(self):
|
||||
"""Test that temperature=0.0 IS added (is not None check)."""
|
||||
from local_deep_research.mcp.server import _build_settings_overrides
|
||||
|
||||
result = _build_settings_overrides(temperature=0.0)
|
||||
assert result.get("llm.temperature") == 0.0
|
||||
|
||||
def test_empty_string_search_engine_is_stripped(self):
|
||||
"""Test that empty string search_engine is stripped and not added."""
|
||||
from local_deep_research.mcp.server import _build_settings_overrides
|
||||
|
||||
result = _build_settings_overrides(search_engine="")
|
||||
assert result == {}
|
||||
|
||||
def test_empty_string_strategy_is_stripped(self):
|
||||
"""Test that empty string strategy is stripped and not added."""
|
||||
from local_deep_research.mcp.server import _build_settings_overrides
|
||||
|
||||
result = _build_settings_overrides(strategy="")
|
||||
assert result == {}
|
||||
|
||||
def test_positive_temperature_added(self):
|
||||
"""Test that positive temperature is correctly added."""
|
||||
from local_deep_research.mcp.server import _build_settings_overrides
|
||||
|
||||
result = _build_settings_overrides(temperature=0.5)
|
||||
assert result["llm.temperature"] == 0.5
|
||||
|
||||
def test_temperature_one_added(self):
|
||||
"""Test that temperature=1.0 is correctly added."""
|
||||
from local_deep_research.mcp.server import _build_settings_overrides
|
||||
|
||||
result = _build_settings_overrides(temperature=1.0)
|
||||
assert result["llm.temperature"] == 1.0
|
||||
|
||||
|
||||
class TestDiscoveryToolsErrorPaths:
|
||||
"""Tests for error handling in discovery tools."""
|
||||
|
||||
def test_list_search_engines_handles_import_error(self):
|
||||
"""Test list_search_engines handles import errors gracefully."""
|
||||
from local_deep_research.mcp.server import list_search_engines
|
||||
|
||||
# The function imports create_settings_snapshot inside, so we need to patch it there
|
||||
with patch(
|
||||
"local_deep_research.api.settings_utils.create_settings_snapshot",
|
||||
side_effect=ImportError("Module not found"),
|
||||
):
|
||||
result = list_search_engines()
|
||||
|
||||
assert result["status"] == "error"
|
||||
assert result["error_type"] == "model_not_found"
|
||||
|
||||
def test_list_search_engines_handles_config_error(self):
|
||||
"""Test list_search_engines handles config errors gracefully."""
|
||||
from local_deep_research.mcp.server import list_search_engines
|
||||
|
||||
with patch(
|
||||
"local_deep_research.web_search_engines.search_engines_config.search_config",
|
||||
side_effect=Exception("Config load failed"),
|
||||
):
|
||||
result = list_search_engines()
|
||||
|
||||
assert result["status"] == "error"
|
||||
assert result["error_type"] == "unknown"
|
||||
|
||||
def test_get_configuration_handles_settings_error(self):
|
||||
"""Test get_configuration handles settings errors gracefully."""
|
||||
from local_deep_research.mcp.server import get_configuration
|
||||
|
||||
# Patch at the location where it's imported inside the function
|
||||
with patch(
|
||||
"local_deep_research.api.settings_utils.create_settings_snapshot",
|
||||
side_effect=Exception("Settings unavailable"),
|
||||
):
|
||||
result = get_configuration()
|
||||
|
||||
assert result["status"] == "error"
|
||||
assert result["error_type"] == "service_unavailable"
|
||||
|
||||
def test_get_configuration_handles_extract_error(self):
|
||||
"""Test get_configuration handles extraction errors gracefully."""
|
||||
from local_deep_research.mcp.server import get_configuration
|
||||
|
||||
# Patch at the location where it's imported inside the function
|
||||
with patch(
|
||||
"local_deep_research.api.settings_utils.extract_setting_value",
|
||||
side_effect=Exception("Extract failed"),
|
||||
):
|
||||
result = get_configuration()
|
||||
|
||||
assert result["status"] == "error"
|
||||
assert result["error_type"] == "unknown"
|
||||
|
||||
|
||||
class TestEntryPoints:
|
||||
"""Tests for entry point functions."""
|
||||
|
||||
def test_run_server_function_is_callable(self):
|
||||
"""Test that run_server is a callable function."""
|
||||
from local_deep_research.mcp.server import run_server
|
||||
|
||||
assert callable(run_server)
|
||||
|
||||
def test_run_server_exported_from_package(self):
|
||||
"""Test that run_server is exported from the mcp package."""
|
||||
from local_deep_research.mcp import run_server
|
||||
|
||||
assert callable(run_server)
|
||||
|
||||
def test_mcp_instance_exported_from_package(self):
|
||||
"""Test that mcp instance is exported from the mcp package."""
|
||||
from local_deep_research.mcp import mcp
|
||||
|
||||
assert mcp is not None
|
||||
assert mcp.name == "local-deep-research"
|
||||
|
||||
def test_main_module_imports_run_server(self):
|
||||
"""Test that __main__.py can import run_server."""
|
||||
# This tests that the import in __main__.py works
|
||||
from local_deep_research.mcp.__main__ import run_server
|
||||
|
||||
assert callable(run_server)
|
||||
|
||||
def test_run_server_disables_loguru_diagnose(self):
|
||||
"""The MCP stderr sink must pin diagnose=False (issue #4185).
|
||||
|
||||
loguru defaults diagnose=True, which dumps repr() of every traceback
|
||||
frame's locals on exception. The MCP server calls logger.exception()
|
||||
in many request handlers whose frame locals hold credentials
|
||||
(api_key, Authorization headers, search-engine secrets); leaving the
|
||||
default on would write them to the MCP client's stderr log.
|
||||
"""
|
||||
import local_deep_research.mcp.server as server_module
|
||||
|
||||
with patch.object(server_module, "logger") as mock_logger:
|
||||
with patch.object(server_module.mcp, "run"):
|
||||
server_module.run_server()
|
||||
|
||||
assert mock_logger.add.call_args_list, "stderr sink was not added"
|
||||
for add_call in mock_logger.add.call_args_list:
|
||||
assert add_call.kwargs.get("diagnose") is False, (
|
||||
f"MCP logger.add must pass diagnose=False; got "
|
||||
f"{add_call.kwargs.get('diagnose')!r}"
|
||||
)
|
||||
|
||||
|
||||
class TestTemperatureParameter:
|
||||
"""Tests for temperature parameter in research tools."""
|
||||
|
||||
def test_quick_research_with_temperature(self):
|
||||
"""Test quick_research passes temperature override."""
|
||||
from local_deep_research.mcp.server import quick_research
|
||||
|
||||
with patch(
|
||||
"local_deep_research.mcp.server.ldr_quick_summary",
|
||||
return_value={
|
||||
"summary": "Test",
|
||||
"findings": [],
|
||||
"sources": [],
|
||||
"iterations": 1,
|
||||
},
|
||||
):
|
||||
with patch(
|
||||
"local_deep_research.mcp.server.create_settings_snapshot"
|
||||
) as mock_settings:
|
||||
mock_settings.return_value = {}
|
||||
# Note: quick_research doesn't have temperature param currently
|
||||
# This test documents the current behavior
|
||||
result = quick_research(query="test", iterations=2)
|
||||
|
||||
assert result["status"] == "success"
|
||||
|
||||
def test_detailed_research_with_temperature(self):
|
||||
"""Test detailed_research passes temperature override."""
|
||||
from local_deep_research.mcp.server import detailed_research
|
||||
|
||||
with patch(
|
||||
"local_deep_research.mcp.server.ldr_detailed_research",
|
||||
return_value={
|
||||
"query": "test",
|
||||
"research_id": "123",
|
||||
"summary": "Test",
|
||||
"findings": [],
|
||||
"sources": [],
|
||||
"iterations": 1,
|
||||
},
|
||||
):
|
||||
with patch(
|
||||
"local_deep_research.mcp.server.create_settings_snapshot"
|
||||
) as mock_settings:
|
||||
mock_settings.return_value = {}
|
||||
result = detailed_research(query="test", iterations=2)
|
||||
|
||||
assert result["status"] == "success"
|
||||
|
||||
|
||||
class TestFormattedFindings:
|
||||
"""Tests for formatted_findings field in research results."""
|
||||
|
||||
def test_quick_research_returns_formatted_findings(self):
|
||||
"""Test quick_research returns formatted_findings field."""
|
||||
from local_deep_research.mcp.server import quick_research
|
||||
|
||||
with patch(
|
||||
"local_deep_research.mcp.server.ldr_quick_summary",
|
||||
return_value={
|
||||
"summary": "Test summary",
|
||||
"findings": [],
|
||||
"sources": [],
|
||||
"iterations": 1,
|
||||
"formatted_findings": "## Formatted Content\n\nTest formatted findings",
|
||||
},
|
||||
):
|
||||
result = quick_research(query="test")
|
||||
|
||||
assert result["status"] == "success"
|
||||
assert "formatted_findings" in result
|
||||
assert (
|
||||
result["formatted_findings"]
|
||||
== "## Formatted Content\n\nTest formatted findings"
|
||||
)
|
||||
|
||||
def test_quick_research_formatted_findings_defaults_empty(self):
|
||||
"""Test formatted_findings defaults to empty string when missing."""
|
||||
from local_deep_research.mcp.server import quick_research
|
||||
|
||||
with patch(
|
||||
"local_deep_research.mcp.server.ldr_quick_summary",
|
||||
return_value={
|
||||
"summary": "Test",
|
||||
"findings": [],
|
||||
"sources": [],
|
||||
"iterations": 1,
|
||||
# No formatted_findings
|
||||
},
|
||||
):
|
||||
result = quick_research(query="test")
|
||||
|
||||
assert result["status"] == "success"
|
||||
assert result["formatted_findings"] == ""
|
||||
|
||||
def test_detailed_research_returns_formatted_findings(self):
|
||||
"""Test detailed_research returns formatted_findings field."""
|
||||
from local_deep_research.mcp.server import detailed_research
|
||||
|
||||
with patch(
|
||||
"local_deep_research.mcp.server.ldr_detailed_research",
|
||||
return_value={
|
||||
"query": "test",
|
||||
"research_id": "123",
|
||||
"summary": "Test",
|
||||
"findings": [],
|
||||
"sources": [],
|
||||
"iterations": 1,
|
||||
"formatted_findings": "## Detailed Formatted\n\nContent here",
|
||||
},
|
||||
):
|
||||
result = detailed_research(query="test")
|
||||
|
||||
assert result["status"] == "success"
|
||||
assert "formatted_findings" in result
|
||||
assert "Detailed Formatted" in result["formatted_findings"]
|
||||
|
||||
|
||||
class TestImportSmokeTests:
|
||||
"""Smoke tests to verify module imports work correctly."""
|
||||
|
||||
def test_server_module_imports(self):
|
||||
"""Test that server module imports without errors."""
|
||||
from local_deep_research.mcp import server
|
||||
|
||||
assert hasattr(server, "mcp")
|
||||
assert hasattr(server, "run_server")
|
||||
assert hasattr(server, "quick_research")
|
||||
assert hasattr(server, "detailed_research")
|
||||
assert hasattr(server, "generate_report")
|
||||
assert hasattr(server, "analyze_documents")
|
||||
assert hasattr(server, "list_search_engines")
|
||||
assert hasattr(server, "list_strategies")
|
||||
assert hasattr(server, "get_configuration")
|
||||
|
||||
def test_all_tools_have_type_hints(self):
|
||||
"""Test that all tool functions have return type hints."""
|
||||
from local_deep_research.mcp.server import (
|
||||
quick_research,
|
||||
detailed_research,
|
||||
generate_report,
|
||||
analyze_documents,
|
||||
list_search_engines,
|
||||
list_strategies,
|
||||
get_configuration,
|
||||
)
|
||||
import typing
|
||||
|
||||
for func in [
|
||||
quick_research,
|
||||
detailed_research,
|
||||
generate_report,
|
||||
analyze_documents,
|
||||
list_search_engines,
|
||||
list_strategies,
|
||||
get_configuration,
|
||||
]:
|
||||
hints = typing.get_type_hints(func)
|
||||
assert "return" in hints, (
|
||||
f"{func.__name__} missing return type hint"
|
||||
)
|
||||
|
||||
def test_helper_functions_exist(self):
|
||||
"""Test that helper functions exist and are importable."""
|
||||
from local_deep_research.mcp.server import (
|
||||
_classify_error,
|
||||
_build_settings_overrides,
|
||||
)
|
||||
from local_deep_research.search_system_factory import (
|
||||
get_available_strategies,
|
||||
)
|
||||
|
||||
assert callable(_classify_error)
|
||||
assert callable(_build_settings_overrides)
|
||||
strategies = get_available_strategies()
|
||||
assert isinstance(strategies, list)
|
||||
|
||||
|
||||
class TestAvailableStrategiesContent:
|
||||
"""Tests for get_available_strategies content validation."""
|
||||
|
||||
def test_all_strategies_have_unique_names(self):
|
||||
"""Test that all strategy names are unique."""
|
||||
from local_deep_research.search_system_factory import (
|
||||
get_available_strategies,
|
||||
)
|
||||
|
||||
strategies = get_available_strategies()
|
||||
names = [s["name"] for s in strategies]
|
||||
assert len(names) == len(set(names)), "Duplicate strategy names found"
|
||||
|
||||
def test_all_strategies_have_non_empty_descriptions(self):
|
||||
"""Test that all strategies have meaningful descriptions."""
|
||||
from local_deep_research.search_system_factory import (
|
||||
get_available_strategies,
|
||||
)
|
||||
|
||||
strategies = get_available_strategies()
|
||||
for strategy in strategies:
|
||||
assert len(strategy["description"]) > 10, (
|
||||
f"Strategy {strategy['name']} has too short description"
|
||||
)
|
||||
|
||||
def test_key_strategies_included(self):
|
||||
"""Test that key strategies are included in the full list."""
|
||||
from local_deep_research.search_system_factory import (
|
||||
get_available_strategies,
|
||||
)
|
||||
|
||||
strategies = get_available_strategies()
|
||||
names = [s["name"] for s in strategies]
|
||||
|
||||
# These are the actual strategies defined in the system
|
||||
expected_strategies = [
|
||||
"source-based",
|
||||
"focused-iteration",
|
||||
"focused-iteration-standard",
|
||||
"topic-organization",
|
||||
"langgraph-agent",
|
||||
]
|
||||
|
||||
for expected in expected_strategies:
|
||||
assert expected in names, (
|
||||
f"Expected strategy '{expected}' not found"
|
||||
)
|
||||
|
||||
|
||||
class TestErrorClassificationAdditional:
|
||||
"""Additional error classification tests."""
|
||||
|
||||
def test_classify_error_case_insensitive(self):
|
||||
"""Test that error classification is case insensitive."""
|
||||
from local_deep_research.mcp.server import _classify_error
|
||||
|
||||
assert _classify_error("SERVICE UNAVAILABLE") == "service_unavailable"
|
||||
assert _classify_error("TIMEOUT error") == "timeout"
|
||||
assert _classify_error("API KEY invalid") == "auth_error"
|
||||
|
||||
def test_classify_error_partial_match(self):
|
||||
"""Test that error classification works with partial matches."""
|
||||
from local_deep_research.mcp.server import _classify_error
|
||||
|
||||
assert (
|
||||
_classify_error("The authentication system rejected the request")
|
||||
== "auth_error"
|
||||
)
|
||||
# The word "timeout" must be present for timeout classification
|
||||
assert _classify_error("Request timeout after 30 seconds") == "timeout"
|
||||
assert (
|
||||
_classify_error("Connection refused by server")
|
||||
== "connection_error"
|
||||
)
|
||||
|
||||
def test_classify_error_empty_string(self):
|
||||
"""Test classification of empty error string."""
|
||||
from local_deep_research.mcp.server import _classify_error
|
||||
|
||||
assert _classify_error("") == "unknown"
|
||||
|
||||
def test_classify_error_none_like(self):
|
||||
"""Test classification handles various error strings."""
|
||||
from local_deep_research.mcp.server import _classify_error
|
||||
|
||||
# All unknown errors
|
||||
assert _classify_error("Something went wrong") == "unknown"
|
||||
assert _classify_error("Error occurred") == "unknown"
|
||||
assert _classify_error("null pointer exception") == "unknown"
|
||||
|
||||
|
||||
class TestListSearchEnginesOutput:
|
||||
"""Tests for list_search_engines output structure."""
|
||||
|
||||
def test_engines_sorted_by_name(self):
|
||||
"""Test that engines are sorted alphabetically by name."""
|
||||
from local_deep_research.mcp.server import list_search_engines
|
||||
|
||||
mock_engines = {
|
||||
"z_engine": {"description": "Z engine"},
|
||||
"a_engine": {"description": "A engine"},
|
||||
"m_engine": {"description": "M engine"},
|
||||
}
|
||||
|
||||
with patch(
|
||||
"local_deep_research.web_search_engines.search_engines_config.search_config",
|
||||
return_value=mock_engines,
|
||||
):
|
||||
result = list_search_engines()
|
||||
|
||||
assert result["status"] == "success"
|
||||
names = [e["name"] for e in result["engines"]]
|
||||
assert names == sorted(names), "Engines should be sorted alphabetically"
|
||||
|
||||
def test_engine_info_structure(self):
|
||||
"""Test that each engine has the expected info structure."""
|
||||
from local_deep_research.mcp.server import list_search_engines
|
||||
|
||||
mock_engines = {
|
||||
"test_engine": {
|
||||
"description": "Test engine",
|
||||
"strengths": ["Fast", "Free"],
|
||||
"weaknesses": ["Limited"],
|
||||
"requires_api_key": True,
|
||||
"is_local": False,
|
||||
}
|
||||
}
|
||||
|
||||
with patch(
|
||||
"local_deep_research.web_search_engines.search_engines_config.search_config",
|
||||
return_value=mock_engines,
|
||||
):
|
||||
result = list_search_engines()
|
||||
|
||||
assert result["status"] == "success"
|
||||
engine = result["engines"][0]
|
||||
assert engine["name"] == "test_engine"
|
||||
assert engine["description"] == "Test engine"
|
||||
assert engine["strengths"] == ["Fast", "Free"]
|
||||
assert engine["weaknesses"] == ["Limited"]
|
||||
assert engine["requires_api_key"] is True
|
||||
assert engine["is_local"] is False
|
||||
|
||||
def test_engine_info_defaults(self):
|
||||
"""Test that engine info defaults work for missing fields."""
|
||||
from local_deep_research.mcp.server import list_search_engines
|
||||
|
||||
mock_engines = {
|
||||
"minimal_engine": {} # No fields
|
||||
}
|
||||
|
||||
with patch(
|
||||
"local_deep_research.web_search_engines.search_engines_config.search_config",
|
||||
return_value=mock_engines,
|
||||
):
|
||||
result = list_search_engines()
|
||||
|
||||
assert result["status"] == "success"
|
||||
engine = result["engines"][0]
|
||||
assert engine["name"] == "minimal_engine"
|
||||
assert engine["description"] == ""
|
||||
assert engine["strengths"] == []
|
||||
assert engine["weaknesses"] == []
|
||||
assert engine["requires_api_key"] is False
|
||||
assert engine["is_local"] is False
|
||||
Reference in New Issue
Block a user