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>
59 lines
1.7 KiB
Python
59 lines
1.7 KiB
Python
"""Tests for chat MCP tools."""
|
|
import json
|
|
import pytest
|
|
from unittest.mock import AsyncMock, MagicMock, patch
|
|
|
|
|
|
@pytest.fixture
|
|
def client(mock_client):
|
|
return mock_client
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_send_message_creates_conversation_and_streams(client):
|
|
from fable_mcp.tools.chat import send_message
|
|
|
|
client.post = AsyncMock(return_value={"id": "conv-1"})
|
|
|
|
async def fake_stream(path, **kwargs):
|
|
for line in [
|
|
'data: {"type": "token", "content": "Hello"}',
|
|
'data: {"type": "token", "content": " world"}',
|
|
'data: {"type": "done"}',
|
|
]:
|
|
yield line
|
|
|
|
client.stream_get = fake_stream
|
|
|
|
result = await send_message(client, message="Hi", conversation_id=None)
|
|
assert result["conversation_id"] == "conv-1"
|
|
assert "Hello world" in result["response"]
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_send_message_reuses_existing_conversation(client):
|
|
from fable_mcp.tools.chat import send_message
|
|
|
|
async def fake_stream(path, **kwargs):
|
|
for line in [
|
|
'data: {"type": "token", "content": "Reply"}',
|
|
'data: {"type": "done"}',
|
|
]:
|
|
yield line
|
|
|
|
client.stream_get = fake_stream
|
|
|
|
result = await send_message(client, message="Hi", conversation_id="existing-conv")
|
|
# Should not call post to create a conversation
|
|
client.post.assert_not_called()
|
|
assert result["conversation_id"] == "existing-conv"
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_list_conversations_calls_get(client):
|
|
from fable_mcp.tools.chat import list_conversations
|
|
client.get = AsyncMock(return_value={"conversations": []})
|
|
await list_conversations(client)
|
|
client.get.assert_called_once()
|
|
assert "/api/chat/conversations" in client.get.call_args[0][0]
|