""" Integration tests for MCP server. Tests for complete research workflows with realistic mocked data. """ 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 TestSettingsFlow: """Tests for settings snapshot creation and override flow.""" def test_settings_snapshot_created_for_quick_research(self): """Verify settings snapshot is created when calling quick_research.""" from local_deep_research.mcp.server import quick_research with patch( "local_deep_research.mcp.server.create_settings_snapshot" ) as mock_snapshot: mock_snapshot.return_value = {"llm.provider": "openai"} with patch( "local_deep_research.mcp.server.ldr_quick_summary", return_value={ "summary": "Test", "findings": [], "sources": [], "iterations": 1, }, ): result = quick_research(query="test") mock_snapshot.assert_called_once() assert result["status"] == "success" def test_settings_overrides_applied_to_snapshot(self): """Verify overrides are passed to create_settings_snapshot.""" from local_deep_research.mcp.server import quick_research with patch( "local_deep_research.mcp.server.create_settings_snapshot" ) as mock_snapshot: mock_snapshot.return_value = {} with ( patch( "local_deep_research.mcp.server.ldr_quick_summary", return_value={ "summary": "Test", "findings": [], "sources": [], "iterations": 1, }, ), patch( "local_deep_research.mcp.server._validate_search_engine", side_effect=lambda e: e, ), patch( "local_deep_research.mcp.server._validate_strategy", side_effect=lambda s: s, ), ): quick_research( query="test", search_engine="wikipedia", strategy="source-based", iterations=3, ) # Check that overrides were passed (last call is from quick_research) call_kwargs = mock_snapshot.call_args[1] assert "overrides" in call_kwargs overrides = call_kwargs["overrides"] assert overrides["search.tool"] == "wikipedia" assert overrides["search.search_strategy"] == "source-based" assert overrides["search.iterations"] == 3 def test_settings_snapshot_passed_to_api(self): """Verify settings snapshot is passed to the API function.""" from local_deep_research.mcp.server import quick_research mock_settings = {"llm.provider": "openai", "search.tool": "wikipedia"} with patch( "local_deep_research.mcp.server.create_settings_snapshot", return_value=mock_settings, ): with patch( "local_deep_research.mcp.server.ldr_quick_summary" ) as mock_api: mock_api.return_value = { "summary": "Test", "findings": [], "sources": [], "iterations": 1, } quick_research(query="test query") # Verify settings_snapshot was passed to API call_kwargs = mock_api.call_args[1] assert "settings_snapshot" in call_kwargs assert call_kwargs["settings_snapshot"] == mock_settings def test_no_overrides_when_no_params(self): """Verify no overrides dict when no optional params provided.""" from local_deep_research.mcp.server import quick_research with patch( "local_deep_research.mcp.server.create_settings_snapshot" ) as mock_snapshot: mock_snapshot.return_value = {} with patch( "local_deep_research.mcp.server.ldr_quick_summary", return_value={ "summary": "Test", "findings": [], "sources": [], "iterations": 1, }, ): quick_research(query="test") # No optional params # Should still be called (with empty overrides or no overrides kwarg) mock_snapshot.assert_called_once() class TestResearchFlowIntegration: """Tests for complete research workflows.""" def test_quick_research_complete_flow(self): """Test complete quick_research flow from input to output.""" from local_deep_research.mcp.server import quick_research mock_result = { "summary": "Quantum computing uses qubits for computation.", "findings": [ {"phase": "Iteration 1", "content": "Found info about qubits"}, { "phase": "Iteration 2", "content": "Found info about superposition", }, ], "sources": [ {"title": "Wikipedia", "link": "https://wikipedia.org/quantum"}, ], "iterations": 2, "formatted_findings": "## Findings\n\nContent here", } with patch( "local_deep_research.mcp.server.ldr_quick_summary", return_value=mock_result, ): result = quick_research( query="What is quantum computing?", search_engine="wikipedia", iterations=2, ) assert result["status"] == "success" assert result["summary"] == mock_result["summary"] assert len(result["findings"]) == 2 assert len(result["sources"]) == 1 assert result["iterations"] == 2 def test_detailed_research_complete_flow(self): """Test complete detailed_research flow.""" from local_deep_research.mcp.server import detailed_research mock_result = { "query": "machine learning applications", "research_id": "research-abc-123", "summary": "Machine learning has many applications...", "findings": [{"content": "Finding 1"}, {"content": "Finding 2"}], "sources": [{"title": "Source 1", "link": "https://example.com"}], "iterations": 3, "formatted_findings": "Formatted content", "metadata": { "timestamp": "2024-01-15T10:00:00Z", "search_tool": "arxiv", "strategy": "source-based", }, } with patch( "local_deep_research.mcp.server.ldr_detailed_research", return_value=mock_result, ): result = detailed_research( query="machine learning applications", search_engine="arxiv", strategy="source-based", ) assert result["status"] == "success" assert result["query"] == "machine learning applications" assert result["research_id"] == "research-abc-123" assert "metadata" in result assert result["metadata"]["search_tool"] == "arxiv" def test_generate_report_complete_flow(self): """Test complete generate_report flow.""" from local_deep_research.mcp.server import generate_report mock_result = { "content": "# Research Report\n\n## Introduction\n\nThis report...\n\n## Findings\n\n...", "metadata": { "generated_at": "2024-01-15T10:00:00Z", "query": "climate change", }, } with patch( "local_deep_research.mcp.server.ldr_generate_report", return_value=mock_result, ): result = generate_report( query="climate change impacts", searches_per_section=3, ) assert result["status"] == "success" assert result["content"].startswith("# Research Report") assert "metadata" in result class TestDiscoveryToolsIntegration: """Integration tests for discovery tools.""" def test_list_strategies_returns_all_expected_strategies(self): """Verify list_strategies returns the complete list.""" from local_deep_research.mcp.server import list_strategies result = list_strategies() assert result["status"] == "success" strategy_names = [s["name"] for s in result["strategies"]] # Check for key strategies assert "source-based" in strategy_names assert "focused-iteration" in strategy_names assert "topic-organization" in strategy_names assert "langgraph-agent" in strategy_names def test_get_configuration_structure(self): """Verify get_configuration returns expected structure.""" from local_deep_research.mcp.server import get_configuration result = get_configuration() assert result["status"] == "success" assert "config" in result config = result["config"] assert "llm" in config assert "search" in config # Check LLM config structure assert "provider" in config["llm"] assert "model" in config["llm"] assert "temperature" in config["llm"] # Check search config structure assert "default_engine" in config["search"] assert "default_strategy" in config["search"] assert "iterations" in config["search"] class TestErrorFlowIntegration: """Integration tests for error handling flows.""" def test_quick_research_error_flow(self): """Test error handling flow in quick_research.""" from local_deep_research.mcp.server import quick_research with patch( "local_deep_research.mcp.server.ldr_quick_summary", side_effect=Exception("LLM service unavailable: 503"), ): result = quick_research(query="test") assert result["status"] == "error" assert result["error_type"] == "service_unavailable" def test_detailed_research_error_flow(self): """Test error handling flow in detailed_research.""" from local_deep_research.mcp.server import detailed_research with patch( "local_deep_research.mcp.server.ldr_detailed_research", side_effect=Exception("Invalid API key"), ): result = detailed_research(query="test") assert result["status"] == "error" assert result["error_type"] == "auth_error" def test_generate_report_error_flow(self): """Test error handling flow in generate_report.""" from local_deep_research.mcp.server import generate_report with patch( "local_deep_research.mcp.server.ldr_generate_report", side_effect=Exception("Request timeout after 30s"), ): result = generate_report(query="test") assert result["status"] == "error" assert "timeout" in result["error"].lower() assert result["error_type"] == "timeout" def test_analyze_documents_error_flow(self): """Test error handling flow in analyze_documents.""" from local_deep_research.mcp.server import analyze_documents with patch( "local_deep_research.mcp.server.ldr_analyze_documents", side_effect=Exception("Collection 'nonexistent' not found"), ): result = analyze_documents( query="test", collection_name="nonexistent" ) assert result["status"] == "error" assert result["error_type"] == "model_not_found" class TestMultipleCallsIsolation: """Tests to verify calls don't interfere with each other.""" def test_settings_isolation_between_calls(self): """Verify settings from one call don't leak to another.""" from local_deep_research.mcp.server import quick_research calls = [] def capture_call(*args, **kwargs): calls.append(kwargs.get("settings_snapshot", {})) return { "summary": "Test", "findings": [], "sources": [], "iterations": 1, } with patch( "local_deep_research.mcp.server.ldr_quick_summary", side_effect=capture_call, ): # First call with wikipedia quick_research(query="test1", search_engine="wikipedia") # Second call with arxiv quick_research(query="test2", search_engine="arxiv") # Each call should have its own settings assert len(calls) == 2 # Settings should be different (or at least both captured) def test_error_in_one_call_doesnt_affect_next(self): """Verify an error in one call doesn't affect subsequent calls.""" from local_deep_research.mcp.server import quick_research call_count = [0] def mock_api(*args, **kwargs): call_count[0] += 1 if call_count[0] == 1: raise Exception("First call fails") return { "summary": "Success", "findings": [], "sources": [], "iterations": 1, } with patch( "local_deep_research.mcp.server.ldr_quick_summary", side_effect=mock_api, ): result1 = quick_research(query="test1") result2 = quick_research(query="test2") assert result1["status"] == "error" assert result2["status"] == "success" assert result2["summary"] == "Success"