Files
chenmingxuan fac4ccb760 feat: prepare 2.2.0 with UI and execution fixes
Add dark theme, richer Markdown and readable plugin results.
Improve Android input evidence, response integrity and diagnostics.
Unify lifecycle, guidance receipts and evidence sources across clients.
Keep direct action dispatch without an added AI quota approval layer.
Align version metadata at 2.2.0 and bundle Helper 1.2.2.

Validation: 3063 backend tests before the version bump; 44 after.
Frontend: 317 tests and production build passed.
2026-10-26 00:44:13 +08:00

59 lines
2.2 KiB
Python

"""Protocol regressions without starting ARTEMIS or invoking a device."""
import json
from typing import Any
from mcp.server.fastmcp.exceptions import ToolError
from mcp.shared.memory import create_connected_server_and_client_session
import pytest
from apps.admin_console.schemas.task_schema import RunRequest
from artemis.mcp.tool_server import ArtemisFastMCP
@pytest.mark.asyncio
async def test_unknown_arguments_are_rejected_before_dispatch():
mcp = ArtemisFastMCP("strict-input-test", strict_inputs=True)
dispatched = []
@mcp.tool()
def submit(task_desc: str) -> dict:
dispatched.append(task_desc)
return {"ok": True}
with pytest.raises(ToolError, match="未知参数.*goal"):
await mcp.call_tool("submit", {"task_desc": "original", "goal": "silently replace?"})
assert dispatched == []
assert (await mcp.list_tools())[0].inputSchema["additionalProperties"] is False
await mcp.call_tool("submit", {"task_desc": "accepted"})
assert dispatched == ["accepted"]
@pytest.mark.asyncio
async def test_legacy_json_has_structured_content_without_changing_text():
mcp = ArtemisFastMCP("structured-test", strict_inputs=True)
@mcp.tool()
def legacy() -> Any:
return {"items": [{"step_id": "actual-step"}], "offset": 0, "total": 1, "next_offset": None}
content, structured = await mcp.call_tool("legacy", {})
assert json.loads(content[0].text) == structured
assert structured["items"][0]["step_id"] == "actual-step"
async with create_connected_server_and_client_session(mcp) as session:
wire = await session.call_tool("legacy", {})
assert wire.structuredContent == structured
assert json.loads(wire.content[0].text) == structured
rejected = await session.call_tool("legacy", {"goal": "unknown field"})
assert rejected.isError is True
def test_http_submission_rejects_unknown_and_misspelled_fields():
from pydantic import ValidationError
with pytest.raises(ValidationError, match="extra_forbidden"):
RunRequest.model_validate({"goal": "original", "task_desc": "wrong contract"})
assert (
RunRequest.model_validate({"goal": "accepted", "runtime_overrides": {}}).goal == "accepted"
)