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.
59 lines
2.2 KiB
Python
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"
|
|
)
|