Skip to main content
Glama

Codebase MCP Server

by Ravenight13
test_workflow_timeout.py13.6 kB
"""Integration test for workflow-mcp timeout fallback (T021). Tests graceful degradation to default workspace when workflow-mcp server times out or is unavailable. Test Scenario: Quickstart Scenario 5 - Workflow-MCP Timeout Validates: Edge Case (Workflow Integration Timeout), Acceptance Scenario 5 Traces to: FR-013 (graceful degradation), FR-014 (failure categorization) Constitutional Compliance: - Principle VII: TDD (validates error handling correctness) - Principle II: Local-first (tests graceful degradation when external service unavailable) - Principle V: Production quality (comprehensive error recovery testing) """ from __future__ import annotations from pathlib import Path from unittest.mock import AsyncMock, patch import httpx import pytest import pytest_asyncio from src.mcp.tools.indexing import index_repository from src.mcp.tools.search import search_code # ============================================================================== # Test Fixtures # ============================================================================== @pytest.fixture def repo_timeout(tmp_path: Path) -> str: """Fixture: Test repository for timeout scenarios. Creates a minimal Python repository for testing workflow-mcp timeout fallback behavior. Args: tmp_path: Pytest fixture providing temporary directory Returns: Absolute path to repository directory """ repo_dir = tmp_path / "repo-timeout-test" repo_dir.mkdir() # Create Python file for testing (repo_dir / "service.py").write_text( """ def process_request(data: dict) -> dict: '''Process incoming request''' return validate_and_process(data) def validate_and_process(data: dict) -> dict: '''Validate and process data''' return {'status': 'success', 'data': data} """ ) return str(repo_dir) # ============================================================================== # Integration Tests - Workflow-MCP Timeout Fallback # ============================================================================== @pytest.mark.integration @pytest.mark.asyncio async def test_workflow_mcp_timeout_fallback_search(repo_timeout: str) -> None: """Test fallback to default workspace on workflow-mcp timeout. Validates that when workflow-mcp times out, the system falls back to the default workspace and continues operation gracefully. Test Steps: 1. Setup: Index repository in default workspace 2. Mock workflow-mcp timeout (httpx.TimeoutException) 3. Search without specifying project_id (triggers timeout) 4. Verify fallback to default workspace (project_id=None) 5. Verify results accessible from default workspace Expected Result: PASS after T007 (WorkflowIntegrationClient) error handling complete. Traces to: - FR-013: Graceful degradation when workflow-mcp unavailable - FR-014: Failure categorization (timeout vs unavailable) - Acceptance Scenario 5: Workflow-MCP Timeout """ # Setup: Index repository in default workspace try: await index_repository.fn( repo_path=repo_timeout, project_id=None, # Default workspace for setup ) except Exception as e: pytest.skip(f"Test blocked by infrastructure issue during setup: {e}") # Mock workflow-mcp timeout exception with patch("src.services.workflow_client.httpx.AsyncClient") as mock_client_class: mock_client = AsyncMock() mock_client.get = AsyncMock( side_effect=httpx.TimeoutException("Request timeout") ) mock_client.aclose = AsyncMock() mock_client_class.return_value = mock_client # Search without explicit project_id (triggers timeout) try: results = await search_code.fn( query="process request", project_id=None, # Should timeout and fallback ) # Verify workflow-mcp was attempted mock_client.get.assert_called_once() # Verify fallback to default workspace assert results["project_id"] is None assert results["schema_name"] == "project_default" # Verify data still accessible (graceful degradation) assert "results" in results assert "total_count" in results assert results["total_count"] >= 0 except Exception as e: error_msg = str(e).lower() if any( keyword in error_msg for keyword in ["project_id", "column", "embedding", "changeevent"] ): pytest.skip(f"Test blocked by infrastructure issue: {e}") else: raise @pytest.mark.integration @pytest.mark.asyncio async def test_workflow_mcp_connection_error_fallback(repo_timeout: str) -> None: """Test fallback to default workspace on workflow-mcp connection error. Validates that when workflow-mcp server is unavailable (connection refused), the system falls back to the default workspace gracefully. Test Steps: 1. Mock workflow-mcp connection error (httpx.ConnectError) 2. Search without specifying project_id (triggers connection error) 3. Verify fallback to default workspace 4. Verify operation succeeds despite workflow-mcp unavailable Expected Result: PASS - graceful degradation when workflow-mcp server not running Traces to: - FR-013: Graceful degradation when workflow-mcp unavailable - FR-014: Failure categorization (connection error) """ # Mock workflow-mcp connection error with patch("src.services.workflow_client.httpx.AsyncClient") as mock_client_class: mock_client = AsyncMock() mock_client.get = AsyncMock( side_effect=httpx.ConnectError("Connection refused") ) mock_client.aclose = AsyncMock() mock_client_class.return_value = mock_client # Search without explicit project_id try: results = await search_code.fn( query="process request", project_id=None, ) # Verify workflow-mcp was attempted mock_client.get.assert_called_once() # Verify fallback to default workspace assert results["project_id"] is None assert results["schema_name"] == "project_default" except Exception as e: error_msg = str(e).lower() if any( keyword in error_msg for keyword in ["project_id", "column", "embedding", "changeevent"] ): pytest.skip(f"Test blocked by infrastructure issue: {e}") else: raise @pytest.mark.integration @pytest.mark.asyncio async def test_workflow_mcp_invalid_response_fallback(repo_timeout: str) -> None: """Test fallback to default workspace on workflow-mcp invalid response. Validates that when workflow-mcp returns malformed JSON or unexpected errors, the system falls back to the default workspace gracefully. Test Steps: 1. Mock workflow-mcp invalid JSON response 2. Search without specifying project_id (triggers parse error) 3. Verify fallback to default workspace 4. Verify operation succeeds despite invalid response Expected Result: PASS - graceful degradation when workflow-mcp returns bad data Traces to: - FR-013: Graceful degradation on invalid responses - FR-014: Failure categorization (invalid_response) """ # Mock workflow-mcp invalid JSON response with patch("src.services.workflow_client.httpx.AsyncClient") as mock_client_class: mock_client = AsyncMock() mock_response = AsyncMock() mock_response.json = AsyncMock(side_effect=ValueError("Invalid JSON")) mock_response.raise_for_status = AsyncMock() mock_client.get = AsyncMock(return_value=mock_response) mock_client.aclose = AsyncMock() mock_client_class.return_value = mock_client # Search without explicit project_id try: results = await search_code.fn( query="process request", project_id=None, ) # Verify workflow-mcp was attempted mock_client.get.assert_called_once() # Verify fallback to default workspace assert results["project_id"] is None assert results["schema_name"] == "project_default" except Exception as e: error_msg = str(e).lower() if any( keyword in error_msg for keyword in ["project_id", "column", "embedding", "changeevent"] ): pytest.skip(f"Test blocked by infrastructure issue: {e}") else: raise @pytest.mark.integration @pytest.mark.asyncio async def test_workflow_mcp_timeout_during_index(repo_timeout: str) -> None: """Test fallback to default workspace on workflow-mcp timeout during indexing. Validates that when workflow-mcp times out during repository indexing, the system falls back to the default workspace and completes indexing. Test Steps: 1. Mock workflow-mcp timeout exception 2. Index repository without specifying project_id (triggers timeout) 3. Verify fallback to default workspace 4. Verify indexing completes successfully despite timeout Expected Result: PASS - indexing succeeds with default workspace fallback Traces to: - FR-013: Graceful degradation during all operations """ # Mock workflow-mcp timeout exception with patch("src.services.workflow_client.httpx.AsyncClient") as mock_client_class: mock_client = AsyncMock() mock_client.get = AsyncMock( side_effect=httpx.TimeoutException("Request timeout") ) mock_client.aclose = AsyncMock() mock_client_class.return_value = mock_client # Index repository without explicit project_id (triggers timeout) try: result = await index_repository.fn( repo_path=repo_timeout, project_id=None, # Should timeout and fallback ) # Verify workflow-mcp was attempted mock_client.get.assert_called_once() # Verify fallback to default workspace assert result["status"] == "success" assert result["project_id"] is None assert result["schema_name"] == "project_default" # Verify indexing completed assert result["files_indexed"] >= 1 except Exception as e: error_msg = str(e).lower() if any( keyword in error_msg for keyword in ["project_id", "column", "embedding", "changeevent"] ): pytest.skip(f"Test blocked by infrastructure issue: {e}") else: raise @pytest.mark.integration @pytest.mark.asyncio async def test_workflow_mcp_http_error_fallback(repo_timeout: str) -> None: """Test fallback to default workspace on workflow-mcp HTTP error. Validates that when workflow-mcp returns HTTP error (500, 503, etc.), the system falls back to the default workspace gracefully. Test Steps: 1. Mock workflow-mcp HTTP 500 error 2. Search without specifying project_id (triggers HTTP error) 3. Verify fallback to default workspace 4. Verify operation succeeds despite HTTP error Expected Result: PASS - graceful degradation on HTTP errors Traces to: - FR-013: Graceful degradation on all workflow-mcp errors """ # Mock workflow-mcp HTTP error with patch("src.services.workflow_client.httpx.AsyncClient") as mock_client_class: mock_client = AsyncMock() mock_response = AsyncMock() mock_response.raise_for_status = AsyncMock( side_effect=httpx.HTTPStatusError( "Internal Server Error", request=AsyncMock(), response=AsyncMock(status_code=500), ) ) mock_client.get = AsyncMock(return_value=mock_response) mock_client.aclose = AsyncMock() mock_client_class.return_value = mock_client # Search without explicit project_id try: results = await search_code.fn( query="process request", project_id=None, ) # Verify workflow-mcp was attempted mock_client.get.assert_called_once() # Verify fallback to default workspace assert results["project_id"] is None assert results["schema_name"] == "project_default" except Exception as e: error_msg = str(e).lower() if any( keyword in error_msg for keyword in ["project_id", "column", "embedding", "changeevent"] ): pytest.skip(f"Test blocked by infrastructure issue: {e}") else: raise # ============================================================================== # Module Exports # ============================================================================== __all__ = [ "test_workflow_mcp_timeout_fallback_search", "test_workflow_mcp_connection_error_fallback", "test_workflow_mcp_invalid_response_fallback", "test_workflow_mcp_timeout_during_index", "test_workflow_mcp_http_error_fallback", ]

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Ravenight13/codebase-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server