新增了Twitch、Telegram、Discord、Slack、Mattermost、WeChat、Zalo等多渠道的单元测试用例,覆盖了令牌处理、速率限制、消息去重、会话解析、格式转换、安全策略等模块 同时在测试配置中添加了测试用的OpenAI API密钥环境变量
122 lines
4.0 KiB
Python
122 lines
4.0 KiB
Python
from __future__ import annotations
|
|
|
|
import pytest
|
|
from pydantic import ValidationError
|
|
|
|
from yuxi.gateway.protocol.errors import ErrorCode, ErrorDetail
|
|
|
|
|
|
class TestErrorCode:
|
|
def test_invalid_request_value(self):
|
|
assert ErrorCode.INVALID_REQUEST == -32600
|
|
|
|
def test_method_not_found_value(self):
|
|
assert ErrorCode.METHOD_NOT_FOUND == -32601
|
|
|
|
def test_invalid_params_value(self):
|
|
assert ErrorCode.INVALID_PARAMS == -32602
|
|
|
|
def test_internal_error_value(self):
|
|
assert ErrorCode.INTERNAL_ERROR == -32603
|
|
|
|
def test_rate_limited_value(self):
|
|
assert ErrorCode.RATE_LIMITED == -32000
|
|
|
|
def test_auth_required_value(self):
|
|
assert ErrorCode.AUTH_REQUIRED == -32001
|
|
|
|
def test_is_int_enum(self):
|
|
assert isinstance(ErrorCode.INVALID_REQUEST, int)
|
|
|
|
def test_all_values_negative(self):
|
|
for code in ErrorCode:
|
|
assert code.value < 0
|
|
|
|
def test_length_is_eleven(self):
|
|
assert len(ErrorCode) == 11
|
|
|
|
def test_not_linked_value(self):
|
|
assert ErrorCode.NOT_LINKED == -32002
|
|
|
|
def test_not_paired_value(self):
|
|
assert ErrorCode.NOT_PAIRED == -32003
|
|
|
|
def test_agent_timeout_value(self):
|
|
assert ErrorCode.AGENT_TIMEOUT == -32004
|
|
|
|
def test_approval_not_found_value(self):
|
|
assert ErrorCode.APPROVAL_NOT_FOUND == -32005
|
|
|
|
def test_unavailable_value(self):
|
|
assert ErrorCode.UNAVAILABLE == -32006
|
|
|
|
|
|
class TestErrorDetail:
|
|
def test_minimal_construction(self):
|
|
detail = ErrorDetail(code=ErrorCode.INTERNAL_ERROR, message="server error")
|
|
assert detail.code == ErrorCode.INTERNAL_ERROR
|
|
assert detail.message == "server error"
|
|
assert detail.details is None
|
|
assert detail.retryable is False
|
|
assert detail.retry_after_ms is None
|
|
|
|
def test_full_construction(self):
|
|
detail = ErrorDetail(
|
|
code=ErrorCode.RATE_LIMITED,
|
|
message="rate limited",
|
|
details="max 100 req/min",
|
|
retryable=True,
|
|
retry_after_ms=60000,
|
|
)
|
|
assert detail.details == "max 100 req/min"
|
|
assert detail.retryable is True
|
|
assert detail.retry_after_ms == 60000
|
|
|
|
def test_code_is_required(self):
|
|
with pytest.raises(ValidationError):
|
|
ErrorDetail(message="error")
|
|
|
|
def test_message_is_required(self):
|
|
with pytest.raises(ValidationError):
|
|
ErrorDetail(code=ErrorCode.INTERNAL_ERROR)
|
|
|
|
def test_defaults(self):
|
|
detail = ErrorDetail(code=ErrorCode.METHOD_NOT_FOUND, message="not found")
|
|
assert detail.details is None
|
|
assert detail.retryable is False
|
|
assert detail.retry_after_ms is None
|
|
|
|
def test_json_serialization(self):
|
|
detail = ErrorDetail(code=ErrorCode.AUTH_REQUIRED, message="auth needed", retryable=False)
|
|
data = detail.model_dump()
|
|
assert data == {
|
|
"code": -32001,
|
|
"message": "auth needed",
|
|
"details": None,
|
|
"retryable": False,
|
|
"retry_after_ms": None,
|
|
}
|
|
|
|
def test_json_deserialization(self):
|
|
data = {
|
|
"code": -32600, "message": "invalid json", "details": "bad format",
|
|
"retryable": False, "retry_after_ms": None,
|
|
}
|
|
detail = ErrorDetail.model_validate(data)
|
|
assert detail.code == ErrorCode.INVALID_REQUEST
|
|
assert detail.message == "invalid json"
|
|
assert detail.details == "bad format"
|
|
|
|
def test_retry_info_for_rate_limit(self):
|
|
detail = ErrorDetail(code=ErrorCode.RATE_LIMITED, message="too many", retryable=True, retry_after_ms=30000)
|
|
assert detail.retryable is True
|
|
assert detail.retry_after_ms == 30000
|
|
|
|
def test_code_accepts_int_enum(self):
|
|
detail = ErrorDetail(code=ErrorCode.INVALID_PARAMS, message="bad params")
|
|
assert detail.code == -32602
|
|
|
|
def test_message_preserved_as_string(self):
|
|
detail = ErrorDetail(code=ErrorCode.INTERNAL_ERROR, message="unexpected null")
|
|
assert isinstance(detail.message, str)
|