| """Unit tests for task management tools.""" |
|
|
| import json |
| from unittest.mock import AsyncMock, MagicMock, patch |
|
|
| import pytest |
| from mcp.server.fastmcp import Context |
|
|
| from src.mcp_server.features.tasks.task_tools import register_task_tools |
|
|
|
|
| @pytest.fixture |
| def mock_mcp(): |
| """Create a mock MCP server for testing.""" |
| mock = MagicMock() |
| |
| mock._tools = {} |
|
|
| def tool_decorator(): |
| def decorator(func): |
| mock._tools[func.__name__] = func |
| return func |
|
|
| return decorator |
|
|
| mock.tool = tool_decorator |
| return mock |
|
|
|
|
| @pytest.fixture |
| def mock_context(): |
| """Create a mock context for testing.""" |
| return MagicMock(spec=Context) |
|
|
|
|
| @pytest.mark.asyncio |
| async def test_create_task_with_sources(mock_mcp, mock_context): |
| """Test creating a task using manage_task.""" |
| register_task_tools(mock_mcp) |
|
|
| |
| manage_task = mock_mcp._tools.get("manage_task") |
|
|
| assert manage_task is not None, "manage_task tool not registered" |
|
|
| |
| mock_res = { |
| "success": True, |
| "task": {"id": "task-123", "title": "Test Task"}, |
| "message": "Task created successfully", |
| } |
|
|
| with patch("src.mcp_server.features.tasks.task_tools.call_api", new_callable=AsyncMock) as mock_call: |
| mock_call.return_value = mock_res |
|
|
| result = await manage_task( |
| mock_context, |
| action="create", |
| project_id="project-123", |
| title="Implement OAuth2", |
| description="Add OAuth2 authentication", |
| assignee="AI IDE Agent", |
| ) |
|
|
| result_data = json.loads(result) |
| assert result_data["success"] is True |
| assert result_data["task_id"] == "task-123" |
|
|
| |
| mock_call.assert_called_once() |
| args, kwargs = mock_call.call_args |
| assert args[0] == "POST" |
| assert args[1] == "/api/tasks" |
| assert kwargs["json"]["title"] == "Implement OAuth2" |
|
|
|
|
| @pytest.mark.asyncio |
| async def test_find_tasks_with_project_filter(mock_mcp, mock_context): |
| """Test listing tasks with project-specific endpoint.""" |
| register_task_tools(mock_mcp) |
|
|
| |
| find_tasks = mock_mcp._tools.get("find_tasks") |
|
|
| assert find_tasks is not None, "find_tasks tool not registered" |
|
|
| |
| mock_res = { |
| "success": True, |
| "tasks": [ |
| {"id": "task-1", "title": "Task 1", "status": "todo"}, |
| {"id": "task-2", "title": "Task 2", "status": "doing"}, |
| ], |
| } |
|
|
| with patch("src.mcp_server.features.tasks.task_tools.call_api", new_callable=AsyncMock) as mock_call: |
| mock_call.return_value = mock_res |
|
|
| result = await find_tasks(mock_context, filter_by="project", filter_value="project-123") |
|
|
| result_data = json.loads(result) |
| assert result_data["success"] is True |
| assert len(result_data["tasks"]) == 2 |
|
|
| |
| args, kwargs = mock_call.call_args |
| assert args[1] == "/api/projects/project-123/tasks" |
|
|
|
|
| @pytest.mark.asyncio |
| async def test_find_tasks_with_status_filter(mock_mcp, mock_context): |
| """Test listing tasks with status filter.""" |
| register_task_tools(mock_mcp) |
|
|
| |
| find_tasks = mock_mcp._tools.get("find_tasks") |
|
|
| |
| mock_res = { |
| "success": True, |
| "tasks": [ |
| {"id": "task-1", "title": "Task 1", "status": "todo"}, |
| ], |
| } |
|
|
| with patch("src.mcp_server.features.tasks.task_tools.call_api", new_callable=AsyncMock) as mock_call: |
| mock_call.return_value = mock_res |
|
|
| result = await find_tasks(mock_context, filter_by="status", filter_value="todo") |
|
|
| result_data = json.loads(result) |
| assert result_data["success"] is True |
| assert result_data["tasks"][0]["status"] == "todo" |
|
|
| |
| args, kwargs = mock_call.call_args |
| assert kwargs["params"]["status"] == "todo" |
|
|
|
|
| @pytest.mark.asyncio |
| async def test_update_task_status(mock_mcp, mock_context): |
| """Test updating task status.""" |
| register_task_tools(mock_mcp) |
| manage_task = mock_mcp._tools.get("manage_task") |
|
|
| |
| mock_res = { |
| "success": True, |
| "task": {"id": "task-123", "status": "doing"}, |
| "message": "Task updated successfully", |
| } |
|
|
| with patch("src.mcp_server.features.tasks.task_tools.call_api", new_callable=AsyncMock) as mock_call: |
| mock_call.return_value = mock_res |
|
|
| result = await manage_task(mock_context, action="update", task_id="task-123", status="doing") |
|
|
| result_data = json.loads(result) |
| assert result_data["success"] is True |
| assert result_data["task"]["status"] == "doing" |
|
|
| |
| args, kwargs = mock_call.call_args |
| assert args[0] == "PUT" |
| assert args[1] == "/api/tasks/task-123" |
| assert kwargs["json"]["status"] == "doing" |
|
|
|
|
| @pytest.mark.asyncio |
| async def test_delete_task_already_archived(mock_mcp, mock_context): |
| """Test deleting an already archived/deleted task.""" |
| register_task_tools(mock_mcp) |
| manage_task = mock_mcp._tools.get("manage_task") |
|
|
| |
| mock_res = {"success": False, "error": "HTTP 404: Not Found"} |
|
|
| with patch("src.mcp_server.features.tasks.task_tools.call_api", new_callable=AsyncMock) as mock_call: |
| mock_call.return_value = mock_res |
|
|
| result = await manage_task(mock_context, action="delete", task_id="task-none") |
|
|
| result_data = json.loads(result) |
| assert result_data["success"] is False |
| assert "HTTP 404" in result_data["error"] |
|
|