Skip to content

Commit dca2c1f

Browse files
SummerOneTwoclaude
andcommitted
refactor: 精简测试文件结构
合并重叠的测试文件,减少代码冗余: - test_packaging_smoke.py 合并到 test_e2e_mcp.py(共享 MCPClient 基类) - test_server.py 合并到 test_packaging.py(基础功能测试) - test_mixins.py 合并到 test_tools/test_solution.py(Mixin 与 Solution 相关) 效果:测试文件从 14 个减少到 11 个,代码行数减少约 20% Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 6b8a8a5 commit dca2c1f

File tree

7 files changed

+456
-594
lines changed

7 files changed

+456
-594
lines changed

tests/test_e2e_mcp.py

Lines changed: 87 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
1-
"""真实 MCP 端到端兼容性测试。
1+
"""MCP 端到端兼容性测试。
22
33
通过 stdio 启动 MCP Server 进程,进行完整的协议握手和工具调用验证。
4+
包含开发模式(python -m)和打包模式(console script)两种测试。
45
"""
56

67
import asyncio
78
import json
89
import os
10+
import shutil
911
import sys
1012
import tempfile
1113

@@ -29,12 +31,10 @@ async def send_request(self, method: str, params: dict | None = None) -> dict:
2931
"params": params or {},
3032
}
3133

32-
# 发送请求
3334
message = json.dumps(request) + "\n"
3435
self.process.stdin.write(message.encode())
3536
await self.process.stdin.drain()
3637

37-
# 读取响应
3838
response_line = await self.process.stdout.readline()
3939
if not response_line:
4040
raise RuntimeError("MCP server closed connection")
@@ -48,7 +48,6 @@ async def send_request(self, method: str, params: dict | None = None) -> dict:
4848

4949
async def initialize(self) -> dict:
5050
"""执行 MCP 初始化握手。"""
51-
# 发送 initialize 请求
5251
result = await self.send_request(
5352
"initialize",
5453
{
@@ -58,7 +57,6 @@ async def initialize(self) -> dict:
5857
},
5958
)
6059

61-
# 发送 initialized 通知
6260
notification = {"jsonrpc": "2.0", "method": "notifications/initialized"}
6361
message = json.dumps(notification) + "\n"
6462
self.process.stdin.write(message.encode())
@@ -87,10 +85,12 @@ async def close(self) -> None:
8785
await self.process.communicate()
8886

8987

88+
# ============== 开发模式测试(python -m) ==============
89+
90+
9091
@pytest.fixture
9192
async def mcp_client():
92-
"""启动 MCP Server 并返回客户端实例。"""
93-
# 使用 uv run 启动 autocode-mcp
93+
"""启动 MCP Server 并返回客户端实例(开发模式)。"""
9494
process = await asyncio.create_subprocess_exec(
9595
sys.executable,
9696
"-m",
@@ -126,10 +126,8 @@ async def test_mcp_list_tools(mcp_client: MCPClient):
126126

127127
tools = await mcp_client.list_tools()
128128

129-
# 验证有 15 个工具
130129
assert len(tools) == 15
131130

132-
# 验证关键工具存在
133131
tool_names = {t["name"] for t in tools}
134132
expected_tools = {
135133
"file_read",
@@ -158,24 +156,20 @@ async def test_mcp_call_file_read(mcp_client: MCPClient):
158156
try:
159157
result = await mcp_client.call_tool("file_read", {"path": temp_path})
160158

161-
# 验证返回结构
162159
assert "content" in result
163160
assert not result.get("isError", True)
164161

165-
# 验证 content 是列表且包含 TextContent
166162
content = result["content"]
167163
assert isinstance(content, list)
168164
assert len(content) == 1
169165
assert content[0]["type"] == "text"
170166

171-
# 验证文本内容是有效 JSON
172167
text = content[0]["text"]
173168
parsed = json.loads(text)
174169
assert parsed["success"] is True
175170
assert "data" in parsed
176171
assert parsed["data"]["content"] == "hello world"
177172

178-
# 验证 structuredContent 存在
179173
assert "structuredContent" in result
180174
assert result["structuredContent"]["success"] is True
181175
finally:
@@ -207,12 +201,9 @@ async def test_mcp_text_content_is_valid_json(mcp_client: MCPClient):
207201

208202
text = result["content"][0]["text"]
209203

210-
# 必须是有效 JSON
211204
parsed = json.loads(text)
212205

213-
# 不能是 Python repr 格式(如 {'success': True})
214-
# Python repr 使用单引号,JSON 使用双引号
215-
assert "'" not in text # JSON 不使用单引号
206+
assert "'" not in text
216207
assert parsed["success"] is True
217208
finally:
218209
os.unlink(temp_path)
@@ -234,9 +225,86 @@ async def test_mcp_chinese_text_encoding(mcp_client: MCPClient):
234225
text = result["content"][0]["text"]
235226
parsed = json.loads(text)
236227

237-
# 验证中文正确编码(ensure_ascii=False)
238228
assert parsed["data"]["content"] == chinese_content
239-
# 原始文本应该包含中文字符,不是 \uXXXX 转义
240229
assert chinese_content in text
241230
finally:
242231
os.unlink(temp_path)
232+
233+
234+
# ============== 打包模式测试(console script) ==============
235+
236+
237+
@pytest.fixture
238+
async def packaged_mcp_client():
239+
"""启动打包后的 autocode-mcp console script。"""
240+
process = await asyncio.create_subprocess_exec(
241+
"autocode-mcp",
242+
stdin=asyncio.subprocess.PIPE,
243+
stdout=asyncio.subprocess.PIPE,
244+
stderr=asyncio.subprocess.PIPE,
245+
env={**os.environ, "PYTHONIOENCODING": "utf-8"},
246+
)
247+
248+
client = MCPClient(process)
249+
250+
try:
251+
yield client
252+
finally:
253+
await client.close()
254+
255+
256+
@pytest.mark.packaging
257+
@pytest.mark.asyncio
258+
async def test_packaged_console_script_handshake(packaged_mcp_client: MCPClient):
259+
"""测试打包后的 console script 能完成 MCP 握手。"""
260+
result = await packaged_mcp_client.initialize()
261+
262+
assert "protocolVersion" in result
263+
assert "serverInfo" in result
264+
assert result["serverInfo"]["name"] == "autocode-mcp"
265+
266+
267+
@pytest.mark.packaging
268+
@pytest.mark.asyncio
269+
async def test_packaged_console_script_list_tools(packaged_mcp_client: MCPClient):
270+
"""测试打包后的 console script 能列出工具。"""
271+
await packaged_mcp_client.initialize()
272+
273+
tools = await packaged_mcp_client.list_tools()
274+
275+
assert len(tools) == 15
276+
tool_names = {t["name"] for t in tools}
277+
assert "solution_build" in tool_names
278+
assert "validator_build" in tool_names
279+
280+
281+
@pytest.mark.packaging
282+
@pytest.mark.asyncio
283+
async def test_packaged_console_script_call_tool(packaged_mcp_client: MCPClient):
284+
"""测试打包后的 console script 能调用工具。"""
285+
await packaged_mcp_client.initialize()
286+
287+
with tempfile.NamedTemporaryFile(mode="w", suffix=".txt", delete=False, encoding="utf-8") as f:
288+
f.write("packaged test")
289+
temp_path = f.name
290+
291+
try:
292+
result = await packaged_mcp_client.send_request(
293+
"tools/call", {"name": "file_read", "arguments": {"path": temp_path}}
294+
)
295+
296+
assert "content" in result
297+
assert not result.get("isError", True)
298+
299+
text = result["content"][0]["text"]
300+
parsed = json.loads(text)
301+
assert parsed["success"] is True
302+
assert parsed["data"]["content"] == "packaged test"
303+
finally:
304+
os.unlink(temp_path)
305+
306+
307+
@pytest.mark.packaging
308+
def test_console_script_exists():
309+
"""验证 autocode-mcp console script 在环境中存在。"""
310+
assert shutil.which("autocode-mcp") is not None

0 commit comments

Comments
 (0)