"""admin.py DTO 单元测试。 覆盖 ``AdminSendCmd`` 与 ``AdminSendResult`` 的字段赋值、默认值、不可变 语义与 ``__post_init__`` 校验逻辑。 """ from __future__ import annotations import dataclasses import pytest from yuxi.channels.contract.dtos.admin import ( AdminSendCmd, AdminSendResult, ) from yuxi.channels.contract.dtos.common import ( FailureDetail, MessageContent, Operator, OperatorRole, SkipDetail, ) 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) def _make_content() -> MessageContent: return MessageContent(text="hello") @pytest.mark.unit class TestAdminSendCmd: """管理员发送命令 DTO 测试。""" def test_fields_are_assigned(self): # Arrange operator = _make_operator() content = _make_content() # Act cmd = AdminSendCmd( target="sess-1", content=content, conversation_policy="reuse", idempotency_key="idem-1", operator=operator, ) # Assert assert cmd.target == "sess-1" assert cmd.content is content assert cmd.conversation_policy == "reuse" assert cmd.idempotency_key == "idem-1" assert cmd.operator is operator assert cmd.target_type == "session_id" def test_target_type_peer_id(self): # Arrange operator = _make_operator() content = _make_content() # Act cmd = AdminSendCmd( target="peer-1", content=content, conversation_policy="new", idempotency_key="idem-1", operator=operator, target_type="peer_id", ) # Assert assert cmd.target_type == "peer_id" def test_is_frozen(self): # Arrange operator = _make_operator() content = _make_content() cmd = AdminSendCmd( target="sess-1", content=content, conversation_policy="reuse", idempotency_key="idem-1", operator=operator, ) # Act / Assert with pytest.raises(dataclasses.FrozenInstanceError): cmd.target = "other" # type: ignore[misc] def test_empty_target_raises_validation_error(self): # Arrange operator = _make_operator() content = _make_content() # Act / Assert with pytest.raises(ValidationError) as exc_info: AdminSendCmd( target="", content=content, conversation_policy="reuse", idempotency_key="idem-1", operator=operator, ) assert exc_info.value.field == "target" def test_empty_idempotency_key_raises_validation_error(self): # Arrange operator = _make_operator() content = _make_content() # Act / Assert with pytest.raises(ValidationError) as exc_info: AdminSendCmd( target="sess-1", content=content, conversation_policy="reuse", idempotency_key="", operator=operator, ) assert exc_info.value.field == "idempotency_key" @pytest.mark.unit class TestAdminSendResult: """管理员发送结果 DTO 测试。""" def test_required_fields_are_assigned(self): # Arrange # Act result = AdminSendResult(message_ids=("msg-1", "msg-2")) # Assert assert result.message_ids == ("msg-1", "msg-2") assert result.failures == () assert result.skipped == () def test_with_failures_and_skipped(self): # Arrange failures = (FailureDetail(target="peer-1", error_code="ERR", message="fail"),) skipped = (SkipDetail(target="peer-2", reason="in_denylist", policy="deny"),) # Act result = AdminSendResult( message_ids=("msg-1",), failures=failures, skipped=skipped, ) # Assert assert result.failures == failures assert result.skipped == skipped def test_is_frozen(self): # Arrange result = AdminSendResult(message_ids=("msg-1",)) # Act / Assert with pytest.raises(dataclasses.FrozenInstanceError): result.message_ids = () # type: ignore[misc]