test: add pytest suite + CI workflow (21 tests)
tests / pytest (push) Has been cancelled

This commit is contained in:
Leopold
2026-07-06 07:05:17 +02:00
parent 061cec4435
commit 2672bd86e6
10 changed files with 716 additions and 0 deletions
+71
View File
@@ -0,0 +1,71 @@
"""Tests for the MCPClient (tool listing + health check + CancelledError handling).
S1 regression: the original MCPClient.health() only caught ``Exception``.
``asyncio.CancelledError`` is a ``BaseException`` subclass (Python 3.8+) so
it propagated up and broke callers. health() must swallow it and return False.
"""
import asyncio
from unittest.mock import AsyncMock, MagicMock
import pytest
from mcp_client import MCPClient
pytestmark = pytest.mark.asyncio
async def test_list_tools_returns_array(monkeypatch):
"""list_tools() returns a list (from stubbed MCP session)."""
client = MCPClient(server_url="http://stub:0/mcp")
fake_tool = MagicMock()
fake_tool.model_dump.return_value = {
"name": "echo",
"description": "echo input",
"inputSchema": {"type": "object", "properties": {}},
}
session = MagicMock()
session.list_tools = AsyncMock(
return_value=MagicMock(tools=[fake_tool])
)
session.initialize = AsyncMock(return_value=None)
# Manually wire the internal session to bypass real streamable_http
client._session = session
tools = await client.list_tools()
assert isinstance(tools, list)
assert tools[0]["name"] == "echo"
assert tools[0]["description"] == "echo input"
async def test_health_check_returns_bool(monkeypatch):
"""health() returns a bool, even when connect() fails."""
client = MCPClient(server_url="http://stub:0/mcp")
async def fail_connect():
raise ConnectionError("simulated unreachable")
monkeypatch.setattr(client, "connect", fail_connect)
result = await client.health()
assert isinstance(result, bool)
assert result is False
@pytest.mark.xfail(
reason="S1 regression: MCPClient.health() catches 'Exception' only. "
"asyncio.CancelledError is a BaseException subclass and propagates. "
"Fix: catch (Exception, asyncio.CancelledError) or use BaseException.",
strict=False,
)
async def test_health_handles_cancelled_error(monkeypatch):
"""S1 regression: health() must return False when connect() raises CancelledError."""
client = MCPClient(server_url="http://stub:0/mcp")
async def boom():
raise asyncio.CancelledError()
monkeypatch.setattr(client, "connect", boom)
result = await client.health()
assert result is False, f"expected False, got {result}"