359cb24d9a
- client.py: async httpx wrapper, FableAPIError, stream_get() SSE generator, singleton init_client()/get_client() and _reset_client() for tests - tools/: notes, tasks, projects, milestones, search, chat — thin async functions that accept FableClient and call Fable REST endpoints - server.py: FastMCP entry point with 20 tools registered via @mcp.tool(), each opening a fresh FableClient context per call; validates env vars at startup - tests: 34 tests covering client HTTP behaviour, error handling, singleton, SSE streaming, and all tool modules Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
36 lines
1.3 KiB
Python
36 lines
1.3 KiB
Python
"""Tests for milestones MCP tools."""
|
|
import pytest
|
|
from unittest.mock import AsyncMock
|
|
|
|
|
|
@pytest.fixture
|
|
def client(mock_client):
|
|
return mock_client
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_list_milestones_calls_correct_endpoint(client):
|
|
from fable_mcp.tools.milestones import list_milestones
|
|
client.get = AsyncMock(return_value={"milestones": []})
|
|
await list_milestones(client, project_id=3)
|
|
client.get.assert_called_once_with("/api/projects/3/milestones")
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_create_milestone_posts_to_project_endpoint(client):
|
|
from fable_mcp.tools.milestones import create_milestone
|
|
client.post = AsyncMock(return_value={"id": 10, "title": "M1"})
|
|
await create_milestone(client, project_id=3, title="M1")
|
|
path = client.post.call_args[0][0]
|
|
assert path == "/api/projects/3/milestones"
|
|
assert client.post.call_args[1]["json"]["title"] == "M1"
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_update_milestone_patches(client):
|
|
from fable_mcp.tools.milestones import update_milestone
|
|
client.patch = AsyncMock(return_value={"id": 10, "status": "completed"})
|
|
await update_milestone(client, project_id=3, milestone_id=10, status="completed")
|
|
path = client.patch.call_args[0][0]
|
|
assert "/api/projects/3/milestones/10" in path
|