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

390 lines
15 KiB
Python

"""
Tests for MCP server validation functions.
These tests verify input validation for all MCP tool parameters.
"""
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 TestValidationFunctions:
"""Tests for parameter validation helper functions."""
def test_validate_query_valid(self):
"""Test valid query passes validation."""
from local_deep_research.mcp.server import _validate_query
result = _validate_query("What is quantum computing?")
assert result == "What is quantum computing?"
def test_validate_query_strips_whitespace(self):
"""Test query is stripped of whitespace."""
from local_deep_research.mcp.server import _validate_query
result = _validate_query(" test query ")
assert result == "test query"
def test_validate_query_empty_raises(self):
"""Test empty query raises ValidationError."""
from local_deep_research.mcp.server import (
_validate_query,
ValidationError,
)
with pytest.raises(ValidationError, match="cannot be empty"):
_validate_query("")
def test_validate_query_whitespace_only_raises(self):
"""Test whitespace-only query raises ValidationError."""
from local_deep_research.mcp.server import (
_validate_query,
ValidationError,
)
with pytest.raises(ValidationError, match="cannot be empty"):
_validate_query(" ")
def test_validate_query_too_long_raises(self):
"""Test query exceeding max length raises ValidationError."""
from local_deep_research.mcp.server import (
_validate_query,
ValidationError,
)
long_query = "x" * 10001
with pytest.raises(ValidationError, match="exceeds maximum length"):
_validate_query(long_query)
def test_validate_query_at_max_length(self):
"""Test query at exactly max length is valid."""
from local_deep_research.mcp.server import _validate_query
max_query = "x" * 10000
result = _validate_query(max_query)
assert len(result) == 10000
def test_validate_iterations_valid(self):
"""Test valid iterations pass validation."""
from local_deep_research.mcp.server import _validate_iterations
assert _validate_iterations(5) == 5
assert _validate_iterations(1) == 1
assert _validate_iterations(20) == 20
def test_validate_iterations_none_allowed(self):
"""Test None iterations returns None."""
from local_deep_research.mcp.server import _validate_iterations
assert _validate_iterations(None) is None
def test_validate_iterations_zero_raises(self):
"""Test zero iterations raises ValidationError."""
from local_deep_research.mcp.server import (
_validate_iterations,
ValidationError,
)
with pytest.raises(ValidationError, match="positive integer"):
_validate_iterations(0)
def test_validate_iterations_negative_raises(self):
"""Test negative iterations raises ValidationError."""
from local_deep_research.mcp.server import (
_validate_iterations,
ValidationError,
)
with pytest.raises(ValidationError, match="positive integer"):
_validate_iterations(-5)
def test_validate_iterations_exceeds_max_raises(self):
"""Test iterations exceeding max raises ValidationError."""
from local_deep_research.mcp.server import (
_validate_iterations,
ValidationError,
)
with pytest.raises(ValidationError, match="cannot exceed"):
_validate_iterations(21, max_val=20)
def test_validate_iterations_custom_max(self):
"""Test iterations with custom max value."""
from local_deep_research.mcp.server import (
_validate_iterations,
ValidationError,
)
assert _validate_iterations(5, max_val=10) == 5
with pytest.raises(ValidationError, match="cannot exceed 10"):
_validate_iterations(11, max_val=10)
def test_validate_questions_per_iteration_valid(self):
"""Test valid questions_per_iteration passes validation."""
from local_deep_research.mcp.server import (
_validate_questions_per_iteration,
)
assert _validate_questions_per_iteration(3) == 3
assert _validate_questions_per_iteration(1) == 1
assert _validate_questions_per_iteration(10) == 10
def test_validate_questions_per_iteration_none_allowed(self):
"""Test None questions_per_iteration returns None."""
from local_deep_research.mcp.server import (
_validate_questions_per_iteration,
)
assert _validate_questions_per_iteration(None) is None
def test_validate_questions_per_iteration_zero_raises(self):
"""Test zero questions_per_iteration raises ValidationError."""
from local_deep_research.mcp.server import (
_validate_questions_per_iteration,
ValidationError,
)
with pytest.raises(ValidationError, match="positive integer"):
_validate_questions_per_iteration(0)
def test_validate_questions_per_iteration_exceeds_max_raises(self):
"""Test questions_per_iteration exceeding max raises ValidationError."""
from local_deep_research.mcp.server import (
_validate_questions_per_iteration,
ValidationError,
)
with pytest.raises(ValidationError, match="cannot exceed 10"):
_validate_questions_per_iteration(11)
def test_validate_max_results_valid(self):
"""Test valid max_results passes validation."""
from local_deep_research.mcp.server import _validate_max_results
assert _validate_max_results(10) == 10
assert _validate_max_results(1) == 1
assert _validate_max_results(100) == 100
def test_validate_max_results_zero_raises(self):
"""Test zero max_results raises ValidationError."""
from local_deep_research.mcp.server import (
_validate_max_results,
ValidationError,
)
with pytest.raises(ValidationError, match="positive integer"):
_validate_max_results(0)
def test_validate_max_results_exceeds_max_raises(self):
"""Test max_results exceeding max raises ValidationError."""
from local_deep_research.mcp.server import (
_validate_max_results,
ValidationError,
)
with pytest.raises(ValidationError, match="cannot exceed 100"):
_validate_max_results(101)
class TestValidationInTools:
"""Tests for validation being applied in tool functions."""
def test_quick_research_empty_query_returns_error(self):
"""Test quick_research with empty query returns validation error."""
from local_deep_research.mcp.server import quick_research
result = quick_research(query="")
assert result["status"] == "error"
assert result["error_type"] == "validation_error"
assert "empty" in result["error"].lower()
def test_quick_research_invalid_iterations_returns_error(self):
"""Test quick_research with invalid iterations returns validation error."""
from local_deep_research.mcp.server import quick_research
result = quick_research(query="Test query", iterations=0)
assert result["status"] == "error"
assert result["error_type"] == "validation_error"
def test_quick_research_iterations_exceeds_max_returns_error(self):
"""Test quick_research with iterations exceeding max returns validation error."""
from local_deep_research.mcp.server import quick_research
result = quick_research(query="Test query", iterations=100)
assert result["status"] == "error"
assert result["error_type"] == "validation_error"
def test_detailed_research_empty_query_returns_error(self):
"""Test detailed_research with empty query returns validation error."""
from local_deep_research.mcp.server import detailed_research
result = detailed_research(query=" ")
assert result["status"] == "error"
assert result["error_type"] == "validation_error"
def test_generate_report_empty_query_returns_error(self):
"""Test generate_report with empty query returns validation error."""
from local_deep_research.mcp.server import generate_report
result = generate_report(query="")
assert result["status"] == "error"
assert result["error_type"] == "validation_error"
def test_generate_report_invalid_searches_per_section(self):
"""Test generate_report with invalid searches_per_section returns error."""
from local_deep_research.mcp.server import generate_report
result = generate_report(query="Test query", searches_per_section=0)
assert result["status"] == "error"
assert result["error_type"] == "validation_error"
def test_generate_report_searches_per_section_exceeds_max(self):
"""Test generate_report with searches_per_section exceeding max returns error."""
from local_deep_research.mcp.server import generate_report
result = generate_report(query="Test query", searches_per_section=11)
assert result["status"] == "error"
assert result["error_type"] == "validation_error"
def test_analyze_documents_empty_query_returns_error(self):
"""Test analyze_documents with empty query returns validation error."""
from local_deep_research.mcp.server import analyze_documents
result = analyze_documents(query="", collection_name="test")
assert result["status"] == "error"
assert result["error_type"] == "validation_error"
def test_analyze_documents_empty_collection_returns_error(self):
"""Test analyze_documents with empty collection returns validation error."""
from local_deep_research.mcp.server import analyze_documents
result = analyze_documents(query="Test", collection_name="")
assert result["status"] == "error"
assert result["error_type"] == "validation_error"
def test_analyze_documents_invalid_max_results_returns_error(self):
"""Test analyze_documents with invalid max_results returns error."""
from local_deep_research.mcp.server import analyze_documents
result = analyze_documents(
query="Test", collection_name="test", max_results=0
)
assert result["status"] == "error"
assert result["error_type"] == "validation_error"
class TestErrorClassification:
"""Tests for the _classify_error function."""
def test_classify_service_unavailable(self):
"""Test 503 error is classified as service_unavailable."""
from local_deep_research.mcp.server import _classify_error
assert (
_classify_error("Error 503: Service unavailable")
== "service_unavailable"
)
assert (
_classify_error("The server is unavailable")
== "service_unavailable"
)
def test_classify_model_not_found(self):
"""Test 404 error is classified as model_not_found."""
from local_deep_research.mcp.server import _classify_error
assert (
_classify_error("Error 404: Model not found") == "model_not_found"
)
assert _classify_error("Resource not found") == "model_not_found"
def test_classify_auth_error(self):
"""Test auth errors are classified correctly."""
from local_deep_research.mcp.server import _classify_error
assert _classify_error("Invalid API key") == "auth_error"
assert _classify_error("Authentication failed") == "auth_error"
assert _classify_error("Unauthorized access") == "auth_error"
assert _classify_error("Error 401: Unauthorized") == "auth_error"
def test_classify_timeout(self):
"""Test timeout errors are classified correctly."""
from local_deep_research.mcp.server import _classify_error
assert _classify_error("Request timeout") == "timeout"
# "Connection timed out" matches "timed out" check which comes before
# the "connection" check, so it returns "timeout"
assert _classify_error("Connection timed out") == "timeout"
# "Operation timed out" also matches "timed out"
assert _classify_error("Operation timed out") == "timeout"
assert _classify_error("A timeout occurred") == "timeout"
def test_classify_rate_limit(self):
"""Test rate limit errors are classified correctly."""
from local_deep_research.mcp.server import _classify_error
assert _classify_error("Rate limit exceeded") == "rate_limit"
assert _classify_error("Error 429: Too many requests") == "rate_limit"
def test_classify_connection_error(self):
"""Test connection errors are classified correctly."""
from local_deep_research.mcp.server import _classify_error
assert _classify_error("Connection refused") == "connection_error"
assert (
_classify_error("Failed to establish connection")
== "connection_error"
)
def test_classify_validation_error(self):
"""Test validation errors are classified correctly."""
from local_deep_research.mcp.server import _classify_error
assert _classify_error("Validation failed") == "validation_error"
assert _classify_error("Invalid parameter") == "validation_error"
def test_classify_unknown_error(self):
"""Test unknown errors are classified as unknown."""
from local_deep_research.mcp.server import _classify_error
assert _classify_error("Some random error") == "unknown"
assert _classify_error("Unexpected exception") == "unknown"
class TestGetAvailableStrategies:
"""Tests for the get_available_strategies function.
Note: These tests don't require the MCP package as they test the factory module.
"""
@pytest.mark.skipif(
True, reason="Tested in test_search_system_factory.py instead"
)
def test_get_available_strategies_returns_list(self):
"""Test get_available_strategies returns a list."""
pass
def test_list_strategies_uses_get_available_strategies(self):
"""Test MCP list_strategies tool uses the factory function."""
from local_deep_research.mcp.server import list_strategies
from local_deep_research.search_system_factory import (
get_available_strategies,
)
result = list_strategies()
expected = get_available_strategies()
assert result["status"] == "success"
assert result["strategies"] == expected