File size: 6,280 Bytes
310260a | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 | """
Unit Tests for mark_complete MCP Tool
Tests the mark_complete tool functionality including:
- Toggles task to completed
- Idempotent behavior on already completed tasks
- Error handling for non-existent task_id
- Task ownership validation
- Updates updated_at timestamp
"""
import pytest
from datetime import datetime, timedelta
from src.tools.mark_complete import mark_complete_internal
from tests.utils.task_helpers import create_test_task, get_task_by_id
@pytest.mark.unit
@pytest.mark.asyncio
async def test_mark_complete_toggles_task_to_completed(mock_mcp_context, test_session):
"""
Test: mark_complete toggles task to completed
Verifies that mark_complete successfully marks a pending task as completed.
"""
# Setup: Create a pending task
task = create_test_task(test_session, mock_mcp_context.user_id, title="Test Task", completed=False)
# Execute
result = await mark_complete_internal(
ctx=mock_mcp_context,
task_id=task.id
)
# Assert
assert result["status"] == "success"
assert result["task"]["id"] == task.id
assert result["task"]["title"] == "Test Task"
assert result["task"]["completed"] is True
# Verify task is marked complete in database
updated_task = get_task_by_id(test_session, task.id)
assert updated_task.completed is True
@pytest.mark.unit
@pytest.mark.asyncio
async def test_mark_complete_on_already_completed_task_is_idempotent(mock_mcp_context, test_session):
"""
Test: mark_complete on already completed task is idempotent
Verifies that marking an already completed task as complete succeeds without error.
"""
# Setup: Create a completed task
task = create_test_task(test_session, mock_mcp_context.user_id, title="Completed Task", completed=True)
# Execute
result = await mark_complete_internal(
ctx=mock_mcp_context,
task_id=task.id
)
# Assert - should succeed (idempotent)
assert result["status"] == "success"
assert result["task"]["completed"] is True
# Verify task remains completed
updated_task = get_task_by_id(test_session, task.id)
assert updated_task.completed is True
@pytest.mark.unit
@pytest.mark.asyncio
async def test_mark_complete_with_non_existent_task_id_returns_error(mock_mcp_context):
"""
Test: mark_complete with non-existent task_id returns error
Verifies that mark_complete returns error for non-existent task.
"""
# Execute with non-existent task_id
result = await mark_complete_internal(
ctx=mock_mcp_context,
task_id=99999
)
# Assert
assert result["status"] == "error"
assert "error" in result
assert "not found" in result["error"].lower()
@pytest.mark.unit
@pytest.mark.asyncio
async def test_mark_complete_validates_task_ownership(mock_mcp_context, mock_mcp_context_user2, test_session):
"""
Test: mark_complete validates task ownership
Verifies that mark_complete returns error when trying to complete another user's task.
"""
# Setup: Create task for user 2
task = create_test_task(test_session, mock_mcp_context_user2.user_id, title="User 2 Task")
# Execute with user 1 context (trying to complete user 2's task)
result = await mark_complete_internal(
ctx=mock_mcp_context,
task_id=task.id
)
# Assert - should fail (unauthorized)
assert result["status"] == "error"
assert "error" in result
assert "not found" in result["error"].lower() # Returns "not found" to prevent information disclosure
# Verify task remains unchanged
unchanged_task = get_task_by_id(test_session, task.id)
assert unchanged_task.completed is False
@pytest.mark.unit
@pytest.mark.asyncio
async def test_mark_complete_updates_updated_at_timestamp(mock_mcp_context, test_session):
"""
Test: mark_complete updates updated_at timestamp
Verifies that mark_complete updates the updated_at timestamp.
"""
# Setup: Create task with old timestamp
task = create_test_task(test_session, mock_mcp_context.user_id, title="Test Task")
original_updated_at = task.updated_at
# Wait a moment to ensure timestamp difference
import time
time.sleep(0.1)
# Execute
result = await mark_complete_internal(
ctx=mock_mcp_context,
task_id=task.id
)
# Assert
assert result["status"] == "success"
# Verify updated_at was updated
updated_task = get_task_by_id(test_session, task.id)
assert updated_task.updated_at > original_updated_at
@pytest.mark.unit
@pytest.mark.asyncio
async def test_mark_complete_toggles_completed_to_incomplete(mock_mcp_context, test_session):
"""
Test: mark_complete toggles completed task back to incomplete
Verifies that mark_complete can toggle a completed task back to incomplete.
"""
# Setup: Create a completed task
task = create_test_task(test_session, mock_mcp_context.user_id, title="Completed Task", completed=True)
# Execute mark_complete (should toggle to incomplete)
result = await mark_complete_internal(
ctx=mock_mcp_context,
task_id=task.id
)
# Assert - task should be toggled to incomplete
assert result["status"] == "success"
assert result["task"]["completed"] is False
# Verify in database
updated_task = get_task_by_id(test_session, task.id)
assert updated_task.completed is False
@pytest.mark.unit
@pytest.mark.asyncio
async def test_mark_complete_returns_task_details(mock_mcp_context, test_session):
"""
Test: mark_complete returns task details in response
Verifies that mark_complete returns complete task information.
"""
# Setup
task = create_test_task(test_session, mock_mcp_context.user_id, title="My Task")
# Execute
result = await mark_complete_internal(
ctx=mock_mcp_context,
task_id=task.id
)
# Assert response contains required fields
assert result["status"] == "success"
assert "task" in result
task_data = result["task"]
assert "id" in task_data
assert "title" in task_data
assert "completed" in task_data
assert "updated_at" in task_data
assert task_data["id"] == task.id
assert task_data["title"] == "My Task"
|