Files
OpenJarvis/tests/mcp/test_client.py
T
Jon Saad-FalconandClaude Opus 4.6 990d7d8a79 Expand test suite to 1031 tests: new agents, tools, MCP layer, model catalog
Add ReAct and OpenHands agents, WebSearch and CodeInterpreter tools,
full MCP protocol layer (server/client/transport), Gemini cloud engine
support, 12 new model specs (4 local MoE + 8 cloud), trace system,
and comprehensive test coverage across all dimensions (hardware, engine,
memory, agents, tools, MCP, integration).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 04:59:11 +00:00

103 lines
3.4 KiB
Python

"""Tests for the MCP client."""
from __future__ import annotations
import pytest
from openjarvis.mcp.client import MCPClient
from openjarvis.mcp.protocol import MCPError
from openjarvis.mcp.server import MCPServer
from openjarvis.mcp.transport import InProcessTransport
from openjarvis.tools._stubs import ToolSpec
from openjarvis.tools.calculator import CalculatorTool
from openjarvis.tools.think import ThinkTool
@pytest.fixture
def client():
"""MCP client connected via in-process transport."""
server = MCPServer([CalculatorTool(), ThinkTool()])
transport = InProcessTransport(server)
return MCPClient(transport)
class TestMCPClient:
def test_initialize_handshake(self, client):
result = client.initialize()
assert "protocolVersion" in result
assert "serverInfo" in result
assert result["serverInfo"]["name"] == "openjarvis"
assert client._initialized is True
def test_initialize_sets_capabilities(self, client):
client.initialize()
assert "tools" in client._capabilities
def test_list_tools(self, client):
tools = client.list_tools()
assert len(tools) == 2
assert all(isinstance(t, ToolSpec) for t in tools)
names = {t.name for t in tools}
assert "calculator" in names
assert "think" in names
def test_list_tools_have_descriptions(self, client):
tools = client.list_tools()
for t in tools:
assert t.description # non-empty
def test_list_tools_have_parameters(self, client):
tools = client.list_tools()
for t in tools:
assert "properties" in t.parameters
def test_call_tool_calculator(self, client):
result = client.call_tool("calculator", {"expression": "10 + 5"})
assert result["isError"] is False
assert "15" in result["content"][0]["text"]
def test_call_tool_think(self, client):
result = client.call_tool("think", {"thought": "Reasoning step."})
assert result["isError"] is False
assert "Reasoning step." in result["content"][0]["text"]
def test_call_tool_error(self, client):
result = client.call_tool("calculator", {"expression": "1/0"})
assert result["isError"] is True
assert "division by zero" in result["content"][0]["text"]
def test_call_unknown_tool_raises(self, client):
with pytest.raises(MCPError) as exc_info:
client.call_tool("nonexistent", {})
assert "Unknown tool" in str(exc_info.value)
def test_client_server_roundtrip(self, client):
"""Full lifecycle: initialize -> list -> call -> close."""
info = client.initialize()
assert "serverInfo" in info
tools = client.list_tools()
assert len(tools) >= 1
result = client.call_tool("calculator", {"expression": "7 * 8"})
assert "56" in result["content"][0]["text"]
client.close()
def test_close(self, client):
client.close()
# Close should not raise even if called multiple times
client.close()
def test_incremental_ids(self, client):
"""Each request should get a unique ID."""
id1 = client._next_id()
id2 = client._next_id()
assert id2 > id1
def test_call_tool_with_no_arguments(self, client):
"""Calling a tool with no arguments passes empty dict."""
result = client.call_tool("think")
# Think tool echoes empty thought
assert result["isError"] is False