1. 清理测试文件中未使用的导入与冗余代码 2. 修复审计日志与批量操作的空值约束,统一填充"global"作为默认渠道 3. 调整批量消息撤回的响应语义,对齐其他端点的部分成功契约 4. 修复访问规则批量克隆的唯一约束问题,新增后缀自动处理逻辑 5. 替换anyio为asyncio并行调用,修正时间UTC导入路径 6. 优化前端外部系统概览页的刷新状态提示与缓存逻辑 7. 修复测试用例中的断言与请求方式问题,适配httpx删除请求特性 8. 重构后端路由的依赖注入,移除冗余的数据库会话依赖 9. 调整测试用例的权限校验逻辑,修正强制登出的权限判断 10. 修复语义分块测试的numpy依赖问题,清理冗余导入
204 lines
5.5 KiB
Python
204 lines
5.5 KiB
Python
"""session.py DTO 单元测试。
|
|
|
|
覆盖 ``ChannelSessionId`` / ``PeerId`` / ``ChatType`` / ``SessionOwner`` /
|
|
``OwnerTransferCmd`` / ``TemporarySessionPattern`` / ``CloseSessionCmd`` /
|
|
``SessionMessageItem`` / ``SessionStatsResult`` / ``BatchCloseSessionsCmd`` /
|
|
``BatchCloseResult`` 的字段赋值、默认值、不可变语义与 ``__post_init__``
|
|
校验逻辑。
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from datetime import datetime
|
|
|
|
import pytest
|
|
from yuxi.channels.contract.dtos.common import Operator, OperatorRole
|
|
from yuxi.channels.contract.dtos.session import (
|
|
BatchCloseSessionsCmd,
|
|
ChannelSessionId,
|
|
ChatType,
|
|
CloseSessionCmd,
|
|
OwnerTransferCmd,
|
|
PeerId,
|
|
SessionOwner,
|
|
SessionStatsResult,
|
|
)
|
|
from yuxi.channels.contract.errors import ValidationError
|
|
|
|
pytestmark = pytest.mark.unit
|
|
|
|
|
|
def _make_operator() -> Operator:
|
|
return Operator(user_id="admin-1", role=OperatorRole.ADMIN_USER)
|
|
|
|
|
|
@pytest.mark.unit
|
|
class TestChannelSessionId:
|
|
"""渠道会话 ID DTO 测试。"""
|
|
|
|
def test_value_is_assigned(self):
|
|
# Act
|
|
sid = ChannelSessionId(value="session-001")
|
|
# Assert
|
|
assert sid.value == "session-001"
|
|
|
|
|
|
@pytest.mark.unit
|
|
class TestPeerId:
|
|
"""对端 ID DTO 测试。"""
|
|
|
|
def test_value_is_assigned(self):
|
|
# Act
|
|
pid = PeerId(value="peer-001")
|
|
# Assert
|
|
assert pid.value == "peer-001"
|
|
|
|
|
|
@pytest.mark.unit
|
|
class TestChatType:
|
|
"""会话类型枚举测试。"""
|
|
|
|
def test_str_values(self):
|
|
assert ChatType.P2P == "p2p"
|
|
assert ChatType.GROUP == "group"
|
|
assert ChatType.KF == "kf"
|
|
|
|
|
|
@pytest.mark.unit
|
|
class TestSessionOwner:
|
|
"""会话所有者 DTO 测试。"""
|
|
|
|
def test_fields_are_assigned(self):
|
|
# Act
|
|
owner = SessionOwner(
|
|
conversation_id="conv-1",
|
|
owner_peer_id="peer-1",
|
|
created_at=datetime(2024, 1, 1),
|
|
)
|
|
# Assert
|
|
assert owner.owner_peer_id == "peer-1"
|
|
|
|
|
|
@pytest.mark.unit
|
|
class TestOwnerTransferCmd:
|
|
"""所有者转移命令 DTO 测试。"""
|
|
|
|
def test_valid_construction(self):
|
|
# Act
|
|
cmd = OwnerTransferCmd(
|
|
session_id="sess-1",
|
|
new_owner_id="peer-2",
|
|
operator=_make_operator(),
|
|
)
|
|
# Assert
|
|
assert cmd.new_owner_id == "peer-2"
|
|
|
|
def test_empty_session_id_raises(self):
|
|
# Act / Assert
|
|
with pytest.raises(ValidationError) as exc_info:
|
|
OwnerTransferCmd(
|
|
session_id="",
|
|
new_owner_id="peer-2",
|
|
operator=_make_operator(),
|
|
)
|
|
assert exc_info.value.field == "session_id"
|
|
|
|
def test_empty_new_owner_id_raises(self):
|
|
# Act / Assert
|
|
with pytest.raises(ValidationError) as exc_info:
|
|
OwnerTransferCmd(
|
|
session_id="sess-1",
|
|
new_owner_id="",
|
|
operator=_make_operator(),
|
|
)
|
|
assert exc_info.value.field == "new_owner_id"
|
|
|
|
|
|
@pytest.mark.unit
|
|
class TestCloseSessionCmd:
|
|
"""关闭会话命令 DTO 测试。"""
|
|
|
|
def test_valid_construction(self):
|
|
# Act
|
|
cmd = CloseSessionCmd(
|
|
session_id="sess-1",
|
|
operator=_make_operator(),
|
|
)
|
|
# Assert
|
|
assert cmd.reason is None
|
|
|
|
def test_empty_session_id_raises(self):
|
|
# Act / Assert
|
|
with pytest.raises(ValidationError) as exc_info:
|
|
CloseSessionCmd(session_id="", operator=_make_operator())
|
|
assert exc_info.value.field == "session_id"
|
|
|
|
|
|
@pytest.mark.unit
|
|
class TestBatchCloseSessionsCmd:
|
|
"""批量关闭会话命令 DTO 测试。"""
|
|
|
|
def test_valid_with_session_ids(self):
|
|
# Act
|
|
cmd = BatchCloseSessionsCmd(
|
|
operator=_make_operator(),
|
|
session_ids=("s1", "s2"),
|
|
)
|
|
# Assert
|
|
assert cmd.max_count == 100
|
|
assert cmd.filter is None
|
|
|
|
def test_valid_with_filter(self):
|
|
# Act
|
|
cmd = BatchCloseSessionsCmd(
|
|
operator=_make_operator(),
|
|
filter={"channel_type": "wechat"},
|
|
)
|
|
# Assert
|
|
assert cmd.session_ids == ()
|
|
|
|
def test_both_empty_raises(self):
|
|
# Act / Assert
|
|
with pytest.raises(ValidationError) as exc_info:
|
|
BatchCloseSessionsCmd(operator=_make_operator())
|
|
assert exc_info.value.field == "session_ids"
|
|
|
|
def test_max_count_below_min_raises(self):
|
|
# Act / Assert
|
|
with pytest.raises(ValidationError) as exc_info:
|
|
BatchCloseSessionsCmd(
|
|
operator=_make_operator(),
|
|
session_ids=("s1",),
|
|
max_count=0,
|
|
)
|
|
assert exc_info.value.field == "max_count"
|
|
|
|
def test_max_count_above_max_raises(self):
|
|
# Act / Assert
|
|
with pytest.raises(ValidationError) as exc_info:
|
|
BatchCloseSessionsCmd(
|
|
operator=_make_operator(),
|
|
session_ids=("s1",),
|
|
max_count=1001,
|
|
)
|
|
assert exc_info.value.field == "max_count"
|
|
|
|
|
|
@pytest.mark.unit
|
|
class TestSessionStatsResult:
|
|
"""会话统计结果 DTO 测试。"""
|
|
|
|
def test_fields_are_assigned_with_defaults(self):
|
|
# Act
|
|
result = SessionStatsResult(
|
|
message_count=10,
|
|
user_message_count=5,
|
|
assistant_message_count=5,
|
|
started_at=datetime(2024, 1, 1),
|
|
last_activity_at=datetime(2024, 1, 2),
|
|
duration_seconds=86400,
|
|
)
|
|
# Assert
|
|
assert result.first_response_seconds is None
|
|
assert result.avg_response_seconds is None
|