"""mention.py DTO 单元测试。 覆盖 ``MentionType`` / ``ImplicitMentionKind`` / ``MentionTargetKind`` 枚举 与 ``MentionTarget`` / ``MentionContext`` / ``InboundMentionPolicy`` / ``InboundMentionFact`` / ``MentionDecision`` 的字段赋值、默认值、不可变 语义与 ``__post_init__`` 校验逻辑。 """ from __future__ import annotations import dataclasses import pytest from yuxi.channels.contract.dtos.mention import ( ImplicitMentionKind, InboundMentionFact, InboundMentionPolicy, MentionContext, MentionDecision, MentionTarget, MentionTargetKind, MentionType, ) from yuxi.channels.contract.errors import ValidationError pytestmark = pytest.mark.unit @pytest.mark.unit class TestMentionType: """提及类型枚举测试。""" def test_enum_values_are_correct(self): assert MentionType.EXPLICIT == "explicit" assert MentionType.IMPLICIT == "implicit" assert MentionType.NONE == "none" def test_enum_inherits_from_str(self): assert issubclass(MentionType, str) def test_enum_has_three_members(self): assert len(list(MentionType)) == 3 @pytest.mark.unit class TestImplicitMentionKind: """隐式提及类型枚举测试。""" def test_enum_values_are_correct(self): assert ImplicitMentionKind.REPLY_TO_BOT == "reply_to_bot" assert ImplicitMentionKind.QUOTED_BOT == "quoted_bot" assert ImplicitMentionKind.BOT_THREAD_PARTICIPANT == "bot_thread_participant" assert ImplicitMentionKind.NATIVE == "native" def test_enum_has_four_members(self): assert len(list(ImplicitMentionKind)) == 4 @pytest.mark.unit class TestMentionTargetKind: """提及目标类型枚举测试。""" def test_enum_values_are_correct(self): assert MentionTargetKind.USER == "user" assert MentionTargetKind.AGENT == "agent" assert MentionTargetKind.BOT == "bot" def test_enum_has_three_members(self): assert len(list(MentionTargetKind)) == 3 @pytest.mark.unit class TestMentionTarget: """提及目标 DTO 测试。""" def test_fields_are_assigned(self): # Arrange # Act target = MentionTarget(id="user-1", kind=MentionTargetKind.USER) # Assert assert target.id == "user-1" assert target.kind is MentionTargetKind.USER assert target.display_name is None def test_with_display_name(self): # Arrange # Act target = MentionTarget(id="agent-1", kind=MentionTargetKind.AGENT, display_name="助手") # Assert assert target.display_name == "助手" def test_is_frozen(self): # Arrange target = MentionTarget(id="user-1", kind=MentionTargetKind.USER) # Act / Assert with pytest.raises(dataclasses.FrozenInstanceError): target.id = "other" # type: ignore[misc] def test_empty_id_raises_validation_error(self): # Arrange # Act / Assert with pytest.raises(ValidationError) as exc_info: MentionTarget(id="", kind=MentionTargetKind.USER) assert exc_info.value.field == "id" @pytest.mark.unit class TestMentionContext: """提及上下文 DTO 测试。""" def test_required_fields_are_assigned(self): # Arrange # Act ctx = MentionContext(type=MentionType.EXPLICIT) # Assert assert ctx.type is MentionType.EXPLICIT assert ctx.mentioned_users == () assert ctx.implicit_kind is None assert ctx.mentioned_targets == () assert ctx.target_agent_ids == () def test_with_all_fields(self): # Arrange target = MentionTarget(id="agent-1", kind=MentionTargetKind.AGENT) # Act ctx = MentionContext( type=MentionType.IMPLICIT, mentioned_users=("user-1",), implicit_kind=ImplicitMentionKind.REPLY_TO_BOT, mentioned_targets=(target,), target_agent_ids=("agent-1",), ) # Assert assert ctx.mentioned_users == ("user-1",) assert ctx.implicit_kind is ImplicitMentionKind.REPLY_TO_BOT assert ctx.mentioned_targets == (target,) def test_is_frozen(self): # Arrange ctx = MentionContext(type=MentionType.NONE) # Act / Assert with pytest.raises(dataclasses.FrozenInstanceError): ctx.type = MentionType.EXPLICIT # type: ignore[misc] @pytest.mark.unit class TestInboundMentionPolicy: """入站提及策略 DTO 测试。""" def test_defaults_are_correct(self): # Arrange # Act policy = InboundMentionPolicy() # Assert assert policy.allowed_implicit_kinds == () def test_with_kinds(self): # Arrange # Act policy = InboundMentionPolicy( allowed_implicit_kinds=(ImplicitMentionKind.REPLY_TO_BOT,) ) # Assert assert policy.allowed_implicit_kinds == (ImplicitMentionKind.REPLY_TO_BOT,) def test_is_frozen(self): # Arrange policy = InboundMentionPolicy() # Act / Assert with pytest.raises(dataclasses.FrozenInstanceError): policy.allowed_implicit_kinds = () # type: ignore[misc] @pytest.mark.unit class TestInboundMentionFact: """入站提及事实 DTO 测试。""" def test_required_fields_are_assigned(self): # Arrange # Act fact = InboundMentionFact( can_detect=True, is_mentioned=True, has_any_mention=True ) # Assert assert fact.can_detect is True assert fact.is_mentioned is True assert fact.has_any_mention is True assert fact.implicit_kinds == () def test_with_implicit_kinds(self): # Arrange # Act fact = InboundMentionFact( can_detect=True, is_mentioned=True, has_any_mention=True, implicit_kinds=(ImplicitMentionKind.NATIVE,), ) # Assert assert fact.implicit_kinds == (ImplicitMentionKind.NATIVE,) def test_is_frozen(self): # Arrange fact = InboundMentionFact(can_detect=False, is_mentioned=False, has_any_mention=False) # Act / Assert with pytest.raises(dataclasses.FrozenInstanceError): fact.can_detect = True # type: ignore[misc] @pytest.mark.unit class TestMentionDecision: """提及决策 DTO 测试。""" def test_defaults_are_correct(self): # Arrange # Act decision = MentionDecision() # Assert assert decision.bypass_mention_requirement is False assert decision.skip_message is False assert decision.is_mentioned is False def test_custom_values(self): # Arrange # Act decision = MentionDecision( bypass_mention_requirement=True, skip_message=False, is_mentioned=True, ) # Assert assert decision.bypass_mention_requirement is True assert decision.is_mentioned is True def test_is_frozen(self): # Arrange decision = MentionDecision() # Act / Assert with pytest.raises(dataclasses.FrozenInstanceError): decision.skip_message = True # type: ignore[misc]