1. 删除了 test/unit/external_systems/framework/ 下的废弃空测试目录 2. 修复多处测试断言逻辑、参数传递与测试数据构造 3. 新增日志级别、缓存令牌、流事件等DTO单元测试 4. 补充路由绑定、会话仓储、outbox仓储的测试覆盖 5. 更新测试用例中的异常类型、参数校验与业务逻辑断言
221 lines
6.2 KiB
Python
221 lines
6.2 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 Attachment, MessageFormat
|
|
from yuxi.channels.contract.dtos.outbound import (
|
|
BatchSendItem,
|
|
BatchSendResult,
|
|
FinalMessage,
|
|
FormattedMessage,
|
|
OutboundPayload,
|
|
RichMessage,
|
|
RichMessageButton,
|
|
RichMessageCheckbox,
|
|
RichMessageDatePicker,
|
|
RichMessageFields,
|
|
RichMessageInput,
|
|
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",)
|