ForcePilot/backend/test/unit/channels/contract/dtos/test_degradation.py
Kris 08617091dc refactor: 整理项目包结构与导入路径
- 新增多个业务域的__init__.py模块文件,规范包导出结构
- 调整多个DTO文件的导入路径,统一模块组织方式
- 移除测试文件中多余的空行与导入语句
- 优化部分业务模块的包层级划分
2026-07-18 02:04:03 +08:00

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.plugin.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"