"""ack.py DTO 单元测试。 覆盖 ``AckPolicy`` / ``AckStage`` 枚举与 ``AckDecision`` 的字段赋值、 不可变语义与 ``__post_init__`` 校验逻辑。 """ from __future__ import annotations import dataclasses import pytest from yuxi.channels.contract.dtos.ack import ( AckDecision, AckPolicy, AckStage, ) from yuxi.channels.contract.errors import ValidationError pytestmark = pytest.mark.unit @pytest.mark.unit class TestAckPolicy: """ACK 策略枚举测试。""" def test_enum_values_are_correct(self): # Arrange # Act # Assert assert AckPolicy.AFTER_RECORD == "after_record" assert AckPolicy.AFTER_AGENT_DISPATCH == "after_agent_dispatch" assert AckPolicy.AFTER_PERSIST == "after_persist" assert AckPolicy.MANUAL == "manual" def test_enum_inherits_from_str(self): # Arrange # Act # Assert assert issubclass(AckPolicy, str) assert isinstance(AckPolicy.MANUAL, str) def test_enum_has_four_members(self): # Arrange # Act members = list(AckPolicy) # Assert assert len(members) == 4 @pytest.mark.unit class TestAckStage: """ACK 阶段枚举测试。""" def test_enum_values_are_correct(self): # Arrange # Act # Assert assert AckStage.RECORD == "record" assert AckStage.DISPATCH == "dispatch" assert AckStage.PERSIST == "persist" assert AckStage.MANUAL == "manual" def test_enum_inherits_from_str(self): # Arrange # Act # Assert assert issubclass(AckStage, str) assert isinstance(AckStage.RECORD, str) def test_enum_has_four_members(self): # Arrange # Act members = list(AckStage) # Assert assert len(members) == 4 @pytest.mark.unit class TestAckDecision: """ACK 决策 DTO 测试。""" def test_fields_are_assigned(self): # Arrange # Act decision = AckDecision(decision="ack", stage=AckStage.RECORD) # Assert assert decision.decision == "ack" assert decision.stage is AckStage.RECORD assert decision.idempotency_key is None def test_with_idempotency_key(self): # Arrange # Act decision = AckDecision( decision="nack", stage=AckStage.DISPATCH, idempotency_key="idem-1", ) # Assert assert decision.idempotency_key == "idem-1" @pytest.mark.parametrize("action", ["ack", "nack", "retry", "pending"]) def test_valid_decisions(self, action): # Arrange # Act decision = AckDecision(decision=action, stage=AckStage.RECORD) # Assert assert decision.decision == action def test_invalid_decision_raises_validation_error(self): # Arrange # Act / Assert - decision 不在合法取值集合中 with pytest.raises(ValidationError) as exc_info: AckDecision(decision="invalid", stage=AckStage.RECORD) # type: ignore[arg-type] assert exc_info.value.field == "decision" def test_is_frozen(self): # Arrange decision = AckDecision(decision="ack", stage=AckStage.RECORD) # Act / Assert with pytest.raises(dataclasses.FrozenInstanceError): decision.decision = "nack" # type: ignore[misc]