1. 删除了 test/unit/external_systems/framework/ 下的废弃空测试目录 2. 修复多处测试断言逻辑、参数传递与测试数据构造 3. 新增日志级别、缓存令牌、流事件等DTO单元测试 4. 补充路由绑定、会话仓储、outbox仓储的测试覆盖 5. 更新测试用例中的异常类型、参数校验与业务逻辑断言
125 lines
3.7 KiB
Python
125 lines
3.7 KiB
Python
"""degradation.py DTO 单元测试。
|
|
|
|
覆盖 ``DegradationLevel`` 枚举与 ``RecoveryStatus`` 的字段赋值、默认值、
|
|
不可变语义与 ``__post_init__`` 校验逻辑。
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import dataclasses
|
|
from datetime import datetime
|
|
|
|
import pytest
|
|
from yuxi.channels.contract.dtos.degradation import (
|
|
DegradationLevel,
|
|
RecoveryStatus,
|
|
)
|
|
from yuxi.channels.contract.errors import ValidationError
|
|
|
|
pytestmark = pytest.mark.unit
|
|
|
|
|
|
@pytest.mark.unit
|
|
class TestDegradationLevel:
|
|
"""降级等级枚举测试。"""
|
|
|
|
def test_enum_values_are_correct(self):
|
|
# Arrange
|
|
# Act
|
|
# Assert
|
|
assert DegradationLevel.HEALTHY == "healthy"
|
|
assert DegradationLevel.DEGRADED == "degraded"
|
|
assert DegradationLevel.UNHEALTHY == "unhealthy"
|
|
|
|
def test_enum_inherits_from_str(self):
|
|
# Arrange
|
|
# Act
|
|
# Assert
|
|
assert issubclass(DegradationLevel, str)
|
|
assert isinstance(DegradationLevel.HEALTHY, str)
|
|
|
|
def test_enum_has_three_members(self):
|
|
# Arrange
|
|
# Act
|
|
members = list(DegradationLevel)
|
|
# Assert
|
|
assert len(members) == 3
|
|
|
|
|
|
@pytest.mark.unit
|
|
class TestRecoveryStatus:
|
|
"""恢复状态 DTO 测试。"""
|
|
|
|
def test_required_fields_are_assigned(self):
|
|
# Arrange
|
|
# Act
|
|
status = RecoveryStatus(plugin_id="plugin-1")
|
|
# Assert
|
|
assert status.plugin_id == "plugin-1"
|
|
|
|
def test_defaults_are_correct(self):
|
|
# Arrange
|
|
# Act
|
|
status = RecoveryStatus(plugin_id="plugin-1")
|
|
# Assert
|
|
assert status.retry_count == 0
|
|
assert status.last_retry_at is None
|
|
assert status.next_retry_at is None
|
|
assert status.recovered is False
|
|
assert status.failure_reason is None
|
|
|
|
def test_custom_values_are_assigned(self):
|
|
# Arrange
|
|
now = datetime(2026, 1, 1, 12, 0, 0)
|
|
later = datetime(2026, 1, 1, 12, 5, 0)
|
|
# Act
|
|
status = RecoveryStatus(
|
|
plugin_id="plugin-1",
|
|
retry_count=2,
|
|
last_retry_at=now,
|
|
next_retry_at=later,
|
|
recovered=False,
|
|
failure_reason="connection refused",
|
|
)
|
|
# Assert
|
|
assert status.retry_count == 2
|
|
assert status.last_retry_at is now
|
|
assert status.next_retry_at is later
|
|
assert status.recovered is False
|
|
assert status.failure_reason == "connection refused"
|
|
|
|
def test_max_retry_count_is_valid(self):
|
|
# Arrange
|
|
# Act
|
|
status = RecoveryStatus(plugin_id="plugin-1", retry_count=3)
|
|
# Assert - retry_count 上限 3
|
|
assert status.retry_count == 3
|
|
|
|
def test_is_frozen(self):
|
|
# Arrange
|
|
status = RecoveryStatus(plugin_id="plugin-1")
|
|
# Act / Assert
|
|
with pytest.raises(dataclasses.FrozenInstanceError):
|
|
status.plugin_id = "other" # type: ignore[misc]
|
|
|
|
def test_empty_plugin_id_raises_validation_error(self):
|
|
# Arrange
|
|
# Act / Assert
|
|
with pytest.raises(ValidationError) as exc_info:
|
|
RecoveryStatus(plugin_id="")
|
|
assert exc_info.value.field == "plugin_id"
|
|
|
|
def test_negative_retry_count_raises_validation_error(self):
|
|
# Arrange
|
|
# Act / Assert
|
|
with pytest.raises(ValidationError) as exc_info:
|
|
RecoveryStatus(plugin_id="plugin-1", retry_count=-1)
|
|
assert exc_info.value.field == "retry_count"
|
|
|
|
def test_exceeding_retry_count_raises_validation_error(self):
|
|
# Arrange
|
|
# Act / Assert - retry_count 超过 3 应抛出
|
|
with pytest.raises(ValidationError) as exc_info:
|
|
RecoveryStatus(plugin_id="plugin-1", retry_count=4)
|
|
assert exc_info.value.field == "retry_count"
|