refactor: 重构 schemas 结构,统一枚举定义
This commit is contained in:
@@ -1,250 +0,0 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import pytest
|
||||
|
||||
from core.agentscope.schemas.agui_input import (
|
||||
MAX_MESSAGES,
|
||||
MAX_RUN_ID_LENGTH,
|
||||
MAX_RUN_INPUT_BYTES,
|
||||
MAX_TEXT_CHARS,
|
||||
parse_run_input,
|
||||
validate_run_request_messages_contract,
|
||||
)
|
||||
|
||||
|
||||
def _base_payload() -> dict[str, object]:
|
||||
return {
|
||||
"threadId": "00000000-0000-0000-0000-000000000001",
|
||||
"runId": "run-1",
|
||||
"state": {},
|
||||
"messages": [{"id": "u1", "role": "user", "content": "hello"}],
|
||||
"tools": [],
|
||||
"context": [],
|
||||
"forwardedProps": {"agent_type": "worker"},
|
||||
}
|
||||
|
||||
|
||||
def test_parse_run_input_rejects_invalid_uuid() -> None:
|
||||
payload = _base_payload()
|
||||
payload["threadId"] = "bad-uuid"
|
||||
|
||||
with pytest.raises(ValueError, match="threadId must be a valid UUID"):
|
||||
parse_run_input(payload)
|
||||
|
||||
|
||||
def test_parse_run_input_rejects_message_count_over_limit() -> None:
|
||||
payload = _base_payload()
|
||||
payload["messages"] = [
|
||||
{"id": f"u{i}", "role": "user", "content": "x"} for i in range(MAX_MESSAGES + 1)
|
||||
]
|
||||
|
||||
with pytest.raises(ValueError, match="RunAgentInput.messages exceeds limit"):
|
||||
parse_run_input(payload)
|
||||
|
||||
|
||||
def test_parse_run_input_rejects_user_text_over_limit() -> None:
|
||||
payload = _base_payload()
|
||||
payload["messages"] = [
|
||||
{"id": "u1", "role": "user", "content": "x" * (MAX_TEXT_CHARS + 1)}
|
||||
]
|
||||
|
||||
with pytest.raises(
|
||||
ValueError, match="RunAgentInput user message text exceeds limit"
|
||||
):
|
||||
parse_run_input(payload)
|
||||
|
||||
|
||||
def test_parse_run_input_rejects_payload_over_limit() -> None:
|
||||
payload = _base_payload()
|
||||
payload["forwardedProps"] = {"blob": "x" * MAX_RUN_INPUT_BYTES}
|
||||
|
||||
with pytest.raises(ValueError, match="RunAgentInput payload exceeds size limit"):
|
||||
parse_run_input(payload)
|
||||
|
||||
|
||||
def test_parse_run_input_rejects_run_id_over_limit() -> None:
|
||||
payload = _base_payload()
|
||||
payload["runId"] = "r" * (MAX_RUN_ID_LENGTH + 1)
|
||||
|
||||
with pytest.raises(ValueError, match="runId exceeds length limit"):
|
||||
parse_run_input(payload)
|
||||
|
||||
|
||||
def test_validate_run_request_messages_contract_requires_single_user_message() -> None:
|
||||
payload = _base_payload()
|
||||
payload["messages"] = [
|
||||
{"id": "u1", "role": "user", "content": "hello"},
|
||||
{"id": "u2", "role": "user", "content": "again"},
|
||||
]
|
||||
run_input = parse_run_input(payload)
|
||||
|
||||
with pytest.raises(
|
||||
ValueError,
|
||||
match="RunAgentInput.messages must contain exactly one user message",
|
||||
):
|
||||
validate_run_request_messages_contract(run_input)
|
||||
|
||||
|
||||
def test_validate_run_request_messages_contract_accepts_binary_url_blocks() -> None:
|
||||
payload = _base_payload()
|
||||
payload["messages"] = [
|
||||
{
|
||||
"id": "u1",
|
||||
"role": "user",
|
||||
"content": [
|
||||
{"type": "text", "text": "请分析"},
|
||||
{
|
||||
"type": "binary",
|
||||
"mimeType": "image/png",
|
||||
"url": "https://signed.example/a.png",
|
||||
},
|
||||
],
|
||||
}
|
||||
]
|
||||
run_input = parse_run_input(payload)
|
||||
|
||||
validate_run_request_messages_contract(run_input)
|
||||
|
||||
|
||||
def test_validate_run_request_messages_contract_rejects_binary_data_block() -> None:
|
||||
payload = _base_payload()
|
||||
payload["messages"] = [
|
||||
{
|
||||
"id": "u1",
|
||||
"role": "user",
|
||||
"content": [
|
||||
{"type": "text", "text": "请分析"},
|
||||
{
|
||||
"type": "binary",
|
||||
"mimeType": "image/png",
|
||||
"data": "aGVsbG8=",
|
||||
},
|
||||
],
|
||||
}
|
||||
]
|
||||
run_input = parse_run_input(payload)
|
||||
|
||||
with pytest.raises(ValueError, match="binary content requires url"):
|
||||
validate_run_request_messages_contract(run_input)
|
||||
|
||||
|
||||
def test_parse_run_input_accepts_snake_case_aliases() -> None:
|
||||
payload = {
|
||||
"thread_id": "00000000-0000-0000-0000-000000000001",
|
||||
"run_id": "run-1",
|
||||
"state": {},
|
||||
"messages": [
|
||||
{
|
||||
"id": "u1",
|
||||
"role": "user",
|
||||
"content": [
|
||||
{"type": "text", "text": "hello"},
|
||||
{
|
||||
"type": "binary",
|
||||
"mime_type": "image/png",
|
||||
"url": "https://signed.example/a.png",
|
||||
},
|
||||
],
|
||||
}
|
||||
],
|
||||
"tools": [],
|
||||
"context": [],
|
||||
"forwarded_props": {"agent_type": "worker"},
|
||||
}
|
||||
|
||||
run_input = parse_run_input(payload)
|
||||
|
||||
assert run_input.thread_id == "00000000-0000-0000-0000-000000000001"
|
||||
assert run_input.run_id == "run-1"
|
||||
validate_run_request_messages_contract(run_input)
|
||||
|
||||
|
||||
def test_parse_run_input_accepts_client_time_forwarded_props() -> None:
|
||||
payload = _base_payload()
|
||||
payload["forwardedProps"] = {
|
||||
"agent_type": "worker",
|
||||
"client_time": {
|
||||
"device_timezone": "America/Los_Angeles",
|
||||
"client_now_iso": "2026-03-16T09:12:33-07:00",
|
||||
"client_epoch_ms": 1773658353000,
|
||||
},
|
||||
}
|
||||
|
||||
run_input = parse_run_input(payload)
|
||||
|
||||
assert run_input.forwarded_props is not None
|
||||
|
||||
|
||||
def test_parse_run_input_rejects_invalid_client_time_timezone() -> None:
|
||||
payload = _base_payload()
|
||||
payload["forwardedProps"] = {
|
||||
"agent_type": "worker",
|
||||
"client_time": {
|
||||
"device_timezone": "Mars/OlympusMons",
|
||||
"client_now_iso": "2026-03-16T09:12:33-07:00",
|
||||
"client_epoch_ms": 1773658353000,
|
||||
},
|
||||
}
|
||||
|
||||
with pytest.raises(ValueError, match="invalid RunAgentInput.forwardedProps"):
|
||||
parse_run_input(payload)
|
||||
|
||||
|
||||
def test_parse_run_input_rejects_invalid_client_time_now_iso() -> None:
|
||||
payload = _base_payload()
|
||||
payload["forwardedProps"] = {
|
||||
"agent_type": "worker",
|
||||
"client_time": {
|
||||
"device_timezone": "America/Los_Angeles",
|
||||
"client_now_iso": "2026-03-16 09:12:33",
|
||||
"client_epoch_ms": 1773658353000,
|
||||
},
|
||||
}
|
||||
|
||||
with pytest.raises(ValueError, match="invalid RunAgentInput.forwardedProps"):
|
||||
parse_run_input(payload)
|
||||
|
||||
|
||||
def test_parse_run_input_rejects_invalid_client_time_epoch_type() -> None:
|
||||
payload = _base_payload()
|
||||
payload["forwardedProps"] = {
|
||||
"agent_type": "worker",
|
||||
"client_time": {
|
||||
"device_timezone": "America/Los_Angeles",
|
||||
"client_now_iso": "2026-03-16T09:12:33-07:00",
|
||||
"client_epoch_ms": "1773658353000",
|
||||
},
|
||||
}
|
||||
|
||||
with pytest.raises(ValueError, match="invalid RunAgentInput.forwardedProps"):
|
||||
parse_run_input(payload)
|
||||
|
||||
|
||||
def test_parse_run_input_rejects_unknown_forwarded_props_key() -> None:
|
||||
payload = _base_payload()
|
||||
payload["forwardedProps"] = {
|
||||
"agent_type": "worker",
|
||||
"client_time": {
|
||||
"device_timezone": "America/Los_Angeles",
|
||||
"client_now_iso": "2026-03-16T09:12:33-07:00",
|
||||
"client_epoch_ms": 1773658353000,
|
||||
},
|
||||
"unexpected": {"foo": "bar"},
|
||||
}
|
||||
|
||||
with pytest.raises(ValueError, match="invalid RunAgentInput.forwardedProps"):
|
||||
parse_run_input(payload)
|
||||
|
||||
|
||||
def test_parse_run_input_rejects_missing_forwarded_props_agent_type() -> None:
|
||||
payload = _base_payload()
|
||||
payload["forwardedProps"] = {
|
||||
"client_time": {
|
||||
"device_timezone": "America/Los_Angeles",
|
||||
"client_now_iso": "2026-03-16T09:12:33-07:00",
|
||||
"client_epoch_ms": 1773658353000,
|
||||
}
|
||||
}
|
||||
|
||||
with pytest.raises(ValueError, match="invalid RunAgentInput.forwardedProps"):
|
||||
parse_run_input(payload)
|
||||
Reference in New Issue
Block a user