1. 清理测试文件中未使用的导入与冗余代码 2. 修复审计日志与批量操作的空值约束,统一填充"global"作为默认渠道 3. 调整批量消息撤回的响应语义,对齐其他端点的部分成功契约 4. 修复访问规则批量克隆的唯一约束问题,新增后缀自动处理逻辑 5. 替换anyio为asyncio并行调用,修正时间UTC导入路径 6. 优化前端外部系统概览页的刷新状态提示与缓存逻辑 7. 修复测试用例中的断言与请求方式问题,适配httpx删除请求特性 8. 重构后端路由的依赖注入,移除冗余的数据库会话依赖 9. 调整测试用例的权限校验逻辑,修正强制登出的权限判断 10. 修复语义分块测试的numpy依赖问题,清理冗余导入
218 lines
6.1 KiB
Python
218 lines
6.1 KiB
Python
"""outbound.py DTO 单元测试。
|
|
|
|
覆盖 ``RichMessageButton`` / ``RichMessageOption`` / ``RichMessageSelect`` /
|
|
``RichMessageDatePicker`` / ``RichMessageCheckbox`` / ``RichMessageInput`` /
|
|
``RichMessage`` / ``RichMessageFields`` / ``OutboundPayload`` /
|
|
``FormattedMessage`` / ``TrustedMessage`` / ``FinalMessage`` /
|
|
``BatchSendItem`` / ``BatchSendResult`` 的字段赋值、默认值、不可变语义与
|
|
``__post_init__`` 校验逻辑。
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import dataclasses
|
|
|
|
import pytest
|
|
from yuxi.channels.contract.dtos.common import MessageFormat
|
|
from yuxi.channels.contract.dtos.outbound import (
|
|
BatchSendItem,
|
|
BatchSendResult,
|
|
FinalMessage,
|
|
FormattedMessage,
|
|
OutboundPayload,
|
|
RichMessage,
|
|
RichMessageButton,
|
|
RichMessageFields,
|
|
RichMessageOption,
|
|
RichMessageSelect,
|
|
TrustedMessage,
|
|
)
|
|
from yuxi.channels.contract.errors import ValidationError
|
|
|
|
pytestmark = pytest.mark.unit
|
|
|
|
|
|
@pytest.mark.unit
|
|
class TestRichMessageButton:
|
|
"""富消息按钮 DTO 测试。"""
|
|
|
|
def test_fields_are_assigned_with_defaults(self):
|
|
# Act
|
|
btn = RichMessageButton(label="OK", action="confirm")
|
|
# Assert
|
|
assert btn.value is None
|
|
|
|
|
|
@pytest.mark.unit
|
|
class TestRichMessageOption:
|
|
"""富消息选项 DTO 测试。"""
|
|
|
|
def test_fields_are_assigned(self):
|
|
# Act
|
|
opt = RichMessageOption(label="Option A", value="a")
|
|
# Assert
|
|
assert opt.value == "a"
|
|
|
|
|
|
@pytest.mark.unit
|
|
class TestRichMessageSelect:
|
|
"""富消息下拉选择器组件 DTO 测试。"""
|
|
|
|
def test_fields_are_assigned_with_defaults(self):
|
|
# Act
|
|
sel = RichMessageSelect(
|
|
name="sel1", label="Select", options=(RichMessageOption("A", "a"),)
|
|
)
|
|
# Assert
|
|
assert sel.placeholder is None
|
|
assert sel.default_value is None
|
|
|
|
|
|
@pytest.mark.unit
|
|
class TestRichMessage:
|
|
"""富消息 DTO 测试。"""
|
|
|
|
def test_fields_are_assigned_with_defaults(self):
|
|
# Act
|
|
msg = RichMessage(text="hello")
|
|
# Assert
|
|
assert msg.title is None
|
|
assert msg.buttons == ()
|
|
assert msg.options == ()
|
|
assert msg.attachments == ()
|
|
assert msg.selects == ()
|
|
|
|
def test_is_frozen(self):
|
|
# Arrange
|
|
msg = RichMessage(text="hello")
|
|
# Act / Assert
|
|
with pytest.raises(dataclasses.FrozenInstanceError):
|
|
msg.text = "other" # type: ignore[misc]
|
|
|
|
|
|
@pytest.mark.unit
|
|
class TestRichMessageFields:
|
|
"""富消息字段 DTO 测试。"""
|
|
|
|
def test_default_fallback_format(self):
|
|
# Act
|
|
fields = RichMessageFields(rich_message=RichMessage(text="hi"))
|
|
# Assert
|
|
assert fields.fallback_format == MessageFormat.MARKDOWN
|
|
|
|
|
|
@pytest.mark.unit
|
|
class TestOutboundPayload:
|
|
"""出站负载 DTO 测试。"""
|
|
|
|
def test_defaults(self):
|
|
# Act
|
|
payload = OutboundPayload()
|
|
# Assert
|
|
assert payload.agent_run_id is None
|
|
assert payload.stream_chunks == ()
|
|
assert payload.rich_message_fields is None
|
|
assert payload.attachments == ()
|
|
|
|
|
|
@pytest.mark.unit
|
|
class TestFormattedMessage:
|
|
"""格式化消息 DTO 测试。"""
|
|
|
|
def test_defaults(self):
|
|
# Act
|
|
msg = FormattedMessage(content="hello")
|
|
# Assert
|
|
assert msg.format == MessageFormat.TEXT
|
|
assert msg.rich_message is None
|
|
assert msg.attachments == ()
|
|
|
|
|
|
@pytest.mark.unit
|
|
class TestTrustedMessage:
|
|
"""可信消息 DTO 测试。"""
|
|
|
|
def test_fields_are_assigned_with_defaults(self):
|
|
# Act
|
|
msg = TrustedMessage(content="hello", sender_id="sender-1")
|
|
# Assert
|
|
assert msg.is_owner is False
|
|
assert msg.rich_message is None
|
|
|
|
def test_is_frozen(self):
|
|
# Arrange
|
|
msg = TrustedMessage(content="hello", sender_id="s1")
|
|
# Act / Assert
|
|
with pytest.raises(dataclasses.FrozenInstanceError):
|
|
msg.sender_id = "other" # type: ignore[misc]
|
|
|
|
def test_empty_sender_id_raises(self):
|
|
# Act / Assert
|
|
with pytest.raises(ValidationError) as exc_info:
|
|
TrustedMessage(content="hello", sender_id="")
|
|
assert exc_info.value.field == "sender_id"
|
|
|
|
def test_empty_content_is_allowed(self):
|
|
# Act - 流式模式下 trusted-inject 阶段 content 可为空
|
|
msg = TrustedMessage(content="", sender_id="s1")
|
|
# Assert
|
|
assert msg.content == ""
|
|
|
|
|
|
@pytest.mark.unit
|
|
class TestFinalMessage:
|
|
"""最终消息 DTO 测试。"""
|
|
|
|
def test_fields_are_assigned_with_defaults(self):
|
|
# Act
|
|
msg = FinalMessage(content="hello", sender_id="s1")
|
|
# Assert
|
|
assert msg.prefix is None
|
|
assert msg.rich_message is None
|
|
|
|
def test_empty_content_raises(self):
|
|
# Act / Assert
|
|
with pytest.raises(ValidationError) as exc_info:
|
|
FinalMessage(content="", sender_id="s1")
|
|
assert exc_info.value.field == "content"
|
|
|
|
def test_empty_sender_id_raises(self):
|
|
# Act / Assert
|
|
with pytest.raises(ValidationError) as exc_info:
|
|
FinalMessage(content="hello", sender_id="")
|
|
assert exc_info.value.field == "sender_id"
|
|
|
|
|
|
@pytest.mark.unit
|
|
class TestBatchSendItem:
|
|
"""批量发送单条结果 DTO 测试。"""
|
|
|
|
def test_fields_are_assigned_with_defaults(self):
|
|
# Act
|
|
item = BatchSendItem(peer_id="p1", channel_msg_id="msg-1", success=True)
|
|
# Assert
|
|
assert item.error is None
|
|
|
|
def test_is_frozen(self):
|
|
# Arrange
|
|
item = BatchSendItem(peer_id="p1", channel_msg_id="m1", success=True)
|
|
# Act / Assert
|
|
with pytest.raises(dataclasses.FrozenInstanceError):
|
|
item.success = False # type: ignore[misc]
|
|
|
|
|
|
@pytest.mark.unit
|
|
class TestBatchSendResult:
|
|
"""批量发送结果 DTO 测试。"""
|
|
|
|
def test_fields_are_assigned(self):
|
|
# Arrange
|
|
items = (BatchSendItem(peer_id="p1", channel_msg_id="m1", success=True),)
|
|
# Act
|
|
result = BatchSendResult(
|
|
total=1, succeeded=("p1",), failed=(), items=items
|
|
)
|
|
# Assert
|
|
assert result.total == 1
|
|
assert result.succeeded == ("p1",)
|