Skip to main content
Glama
test_github_await_workflow.py11.9 kB
""" Unit tests for github_await_workflow_completion function. Tests the workflow monitoring functionality including: - Polling workflow run status until completion - Timeout handling - Latest run detection when no run_id provided - Failed job information gathering - Error handling """ import asyncio import json from unittest.mock import AsyncMock, MagicMock, patch import pytest from src.mcp_server_git.github.api import github_await_workflow_completion class TestGitHubAwaitWorkflowCompletion: """Test github_await_workflow_completion function.""" @pytest.mark.asyncio async def test_successful_workflow_run(self): """Test monitoring a successful workflow run.""" mock_client = MagicMock() # Mock workflow run response - completed successfully mock_run_response = AsyncMock() mock_run_response.status = 200 mock_run_response.json = AsyncMock(return_value={ "id": 12345, "status": "completed", "conclusion": "success", "name": "CI Tests", "head_branch": "main", "head_sha": "abc123def456", "html_url": "https://github.com/owner/repo/actions/runs/12345", "created_at": "2023-01-01T00:00:00Z", "updated_at": "2023-01-01T00:05:00Z", }) mock_client.get = AsyncMock(return_value=mock_run_response) with patch('src.mcp_server_git.github.api.github_client_context') as mock_context: mock_context.return_value.__aenter__.return_value = mock_client result_str = await github_await_workflow_completion( repo_owner="owner", repo_name="repo", run_id=12345, timeout_minutes=1, poll_interval_seconds=1 ) result = json.loads(result_str) assert result["status"] == "success" assert result["conclusion"] == "success" assert result["run_id"] == 12345 assert result["workflow_name"] == "CI Tests" assert "duration_seconds" in result @pytest.mark.asyncio async def test_failed_workflow_run(self): """Test monitoring a failed workflow run.""" mock_client = MagicMock() # Mock workflow run response - completed with failure mock_run_response = AsyncMock() mock_run_response.status = 200 mock_run_response.json = AsyncMock(return_value={ "id": 12345, "status": "completed", "conclusion": "failure", "name": "CI Tests", "head_branch": "main", "head_sha": "abc123def456", "html_url": "https://github.com/owner/repo/actions/runs/12345", "created_at": "2023-01-01T00:00:00Z", "updated_at": "2023-01-01T00:05:00Z", }) # Mock jobs response - one failed job mock_jobs_response = AsyncMock() mock_jobs_response.status = 200 mock_jobs_response.json = AsyncMock(return_value={ "jobs": [ { "id": 1, "name": "Test Suite", "status": "completed", "conclusion": "failure", "html_url": "https://github.com/owner/repo/actions/runs/12345/job/1", "steps": [ {"name": "Setup", "conclusion": "success"}, {"name": "Run Tests", "conclusion": "failure"}, ] } ] }) # Setup mock to return different responses for different endpoints async def mock_get(endpoint, **kwargs): if "jobs" in endpoint: return mock_jobs_response return mock_run_response mock_client.get = AsyncMock(side_effect=mock_get) with patch('src.mcp_server_git.github.api.github_client_context') as mock_context: mock_context.return_value.__aenter__.return_value = mock_client result_str = await github_await_workflow_completion( repo_owner="owner", repo_name="repo", run_id=12345, timeout_minutes=1, poll_interval_seconds=1 ) result = json.loads(result_str) assert result["status"] == "failure" assert result["conclusion"] == "failure" assert len(result["failed_jobs"]) == 1 assert result["failed_jobs"][0]["name"] == "Test Suite" assert result["failed_jobs"][0]["failed_steps"] == ["Run Tests"] @pytest.mark.asyncio async def test_timeout_while_waiting(self): """Test that timeout is handled gracefully.""" mock_client = MagicMock() # Mock workflow run response - still in progress mock_run_response = AsyncMock() mock_run_response.status = 200 mock_run_response.json = AsyncMock(return_value={ "id": 12345, "status": "in_progress", "conclusion": None, "name": "CI Tests", "head_branch": "main", "head_sha": "abc123def456", "html_url": "https://github.com/owner/repo/actions/runs/12345", }) mock_client.get = AsyncMock(return_value=mock_run_response) with patch('src.mcp_server_git.github.api.github_client_context') as mock_context: mock_context.return_value.__aenter__.return_value = mock_client # Use very short timeout to trigger timeout quickly result_str = await github_await_workflow_completion( repo_owner="owner", repo_name="repo", run_id=12345, timeout_minutes=0.01, # ~0.6 seconds poll_interval_seconds=1 ) result = json.loads(result_str) assert result["status"] == "timeout" assert result["run_id"] == 12345 assert "elapsed_seconds" in result @pytest.mark.asyncio async def test_latest_run_detection(self): """Test that latest run is detected when no run_id provided.""" mock_client = MagicMock() # Mock list runs response - get latest mock_list_response = AsyncMock() mock_list_response.status = 200 mock_list_response.json = AsyncMock(return_value={ "workflow_runs": [ {"id": 99999, "status": "in_progress"}, ] }) # Mock workflow run response - completed mock_run_response = AsyncMock() mock_run_response.status = 200 mock_run_response.json = AsyncMock(return_value={ "id": 99999, "status": "completed", "conclusion": "success", "name": "CI Tests", "head_branch": "main", "head_sha": "abc123def456", "html_url": "https://github.com/owner/repo/actions/runs/99999", "created_at": "2023-01-01T00:00:00Z", "updated_at": "2023-01-01T00:05:00Z", }) # Setup mock to return different responses call_count = [0] async def mock_get(endpoint, **kwargs): call_count[0] += 1 if call_count[0] == 1: # First call for listing runs return mock_list_response return mock_run_response # Subsequent calls for run status mock_client.get = AsyncMock(side_effect=mock_get) with patch('src.mcp_server_git.github.api.github_client_context') as mock_context: mock_context.return_value.__aenter__.return_value = mock_client result_str = await github_await_workflow_completion( repo_owner="owner", repo_name="repo", run_id=None, # No run_id - should get latest timeout_minutes=1, poll_interval_seconds=1 ) result = json.loads(result_str) assert result["run_id"] == 99999 assert result["status"] == "success" @pytest.mark.asyncio async def test_polling_until_complete(self): """Test that polling continues until workflow completes.""" mock_client = MagicMock() # Track call count to simulate workflow progression call_count = [0] async def mock_get(endpoint, **kwargs): call_count[0] += 1 mock_response = AsyncMock() mock_response.status = 200 # First 2 polls: in_progress, then completed if call_count[0] <= 2: mock_response.json = AsyncMock(return_value={ "id": 12345, "status": "in_progress", "conclusion": None, "name": "CI Tests", "head_branch": "main", "head_sha": "abc123", }) else: mock_response.json = AsyncMock(return_value={ "id": 12345, "status": "completed", "conclusion": "success", "name": "CI Tests", "head_branch": "main", "head_sha": "abc123", "html_url": "https://github.com/owner/repo/actions/runs/12345", "created_at": "2023-01-01T00:00:00Z", "updated_at": "2023-01-01T00:00:30Z", }) return mock_response mock_client.get = AsyncMock(side_effect=mock_get) with patch('src.mcp_server_git.github.api.github_client_context') as mock_context: mock_context.return_value.__aenter__.return_value = mock_client result_str = await github_await_workflow_completion( repo_owner="owner", repo_name="repo", run_id=12345, timeout_minutes=1, poll_interval_seconds=1 ) result = json.loads(result_str) assert result["status"] == "success" assert call_count[0] >= 3 # Should have polled multiple times @pytest.mark.asyncio async def test_authentication_error(self): """Test that authentication errors are handled properly.""" with patch('src.mcp_server_git.github.api.github_client_context') as mock_context: mock_context.return_value.__aenter__.side_effect = ValueError("GitHub token not configured") result_str = await github_await_workflow_completion( repo_owner="owner", repo_name="repo", run_id=12345 ) assert "Authentication error:" in result_str assert "GitHub token not configured" in result_str @pytest.mark.asyncio async def test_api_error(self): """Test that API errors are handled properly.""" mock_client = MagicMock() mock_response = AsyncMock() mock_response.status = 404 mock_response.text = AsyncMock(return_value="Not Found") mock_client.get = AsyncMock(return_value=mock_response) with patch('src.mcp_server_git.github.api.github_client_context') as mock_context: mock_context.return_value.__aenter__.return_value = mock_client result_str = await github_await_workflow_completion( repo_owner="owner", repo_name="repo", run_id=12345 ) assert "Failed to get workflow run" in result_str assert "404" in result_str

Latest Blog Posts

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/MementoRC/mcp-git'

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