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

222 lines
6.6 KiB
Python

"""dashboard.py DTO 单元测试。
覆盖 ``AccountStats`` / ``MessageStats`` / ``SessionStats`` /
``OutboxStatsSummary`` / ``DashboardOverview`` / ``DashboardOverviewQuery`` /
``DashboardDeliveryQuery`` / ``ChannelDeliveryStat`` /
``DashboardDeliveryResult`` / ``DashboardTodoResult`` / ``RealtimeQuery`` /
``ChannelRealtimeStat`` / ``RealtimeMetricsResult`` 的字段赋值、默认值、
不可变语义与 ``__post_init__`` 校验逻辑。
"""
from __future__ import annotations
import dataclasses
from datetime import UTC, datetime
import pytest
from yuxi.channels.contract.dtos.shared.dashboard import (
AccountStats,
ChannelDeliveryStat,
ChannelRealtimeStat,
DashboardDeliveryQuery,
DashboardDeliveryResult,
DashboardOverviewQuery,
DashboardTodoResult,
RealtimeMetricsResult,
RealtimeQuery,
)
from yuxi.channels.contract.errors import ValidationError
pytestmark = pytest.mark.unit
def _aware_dt() -> datetime:
return datetime(2024, 1, 1, 12, 0, 0, tzinfo=UTC)
def _naive_dt() -> datetime:
return datetime(2024, 1, 1, 12, 0, 0)
@pytest.mark.unit
class TestAccountStats:
"""账户统计快照 DTO 测试。"""
def test_fields_are_assigned_with_defaults(self):
# Act
stats = AccountStats(total=10, by_channel={"wechat": 10}, by_status={"active": 8, "disabled": 2})
# Assert
assert stats.by_onboarding_status == {}
@pytest.mark.unit
class TestDashboardOverviewQuery:
"""全局总览查询 DTO 测试。"""
def test_defaults(self):
# Act
query = DashboardOverviewQuery()
# Assert
assert query.channel_type is None
assert query.start_time is None
def test_naive_start_time_raises(self):
# Act / Assert
with pytest.raises(ValidationError) as exc_info:
DashboardOverviewQuery(start_time=_naive_dt())
assert exc_info.value.field == "start_time"
def test_naive_end_time_raises(self):
# Act / Assert
with pytest.raises(ValidationError) as exc_info:
DashboardOverviewQuery(end_time=_naive_dt())
assert exc_info.value.field == "end_time"
def test_reversed_time_range_raises(self):
# Act / Assert
with pytest.raises(ValidationError) as exc_info:
DashboardOverviewQuery(
start_time=datetime(2024, 1, 2, tzinfo=UTC),
end_time=datetime(2024, 1, 1, tzinfo=UTC),
)
assert exc_info.value.field == "time_range"
@pytest.mark.unit
class TestDashboardDeliveryQuery:
"""投递总览查询 DTO 测试。"""
def test_defaults(self):
# Act
query = DashboardDeliveryQuery()
# Assert
assert query.channel_type is None
def test_naive_start_time_raises(self):
# Act / Assert
with pytest.raises(ValidationError) as exc_info:
DashboardDeliveryQuery(start_time=_naive_dt())
assert exc_info.value.field == "start_time"
def test_reversed_time_range_raises(self):
# Act / Assert
with pytest.raises(ValidationError) as exc_info:
DashboardDeliveryQuery(
start_time=datetime(2024, 1, 2, tzinfo=UTC),
end_time=datetime(2024, 1, 1, tzinfo=UTC),
)
assert exc_info.value.field == "time_range"
@pytest.mark.unit
class TestRealtimeQuery:
"""实时监控查询 DTO 测试。"""
def test_defaults(self):
# Act
query = RealtimeQuery()
# Assert
assert query.window_seconds == 60
def test_is_frozen(self):
# Arrange
query = RealtimeQuery()
# Act / Assert
with pytest.raises(dataclasses.FrozenInstanceError):
query.window_seconds = 10 # type: ignore[misc]
def test_window_below_min_raises(self):
# Act / Assert
with pytest.raises(ValidationError) as exc_info:
RealtimeQuery(window_seconds=9)
assert exc_info.value.field == "window_seconds"
def test_window_above_max_raises(self):
# Act / Assert
with pytest.raises(ValidationError) as exc_info:
RealtimeQuery(window_seconds=301)
assert exc_info.value.field == "window_seconds"
def test_window_at_min_boundary_is_valid(self):
# Act
query = RealtimeQuery(window_seconds=10)
# Assert
assert query.window_seconds == 10
def test_window_at_max_boundary_is_valid(self):
# Act
query = RealtimeQuery(window_seconds=300)
# Assert
assert query.window_seconds == 300
@pytest.mark.unit
class TestDashboardDeliveryResult:
"""投递总览结果 DTO 测试。"""
def test_fields_are_assigned(self):
# Act
result = DashboardDeliveryResult(
total_sent=100,
success_count=90,
failed_count=10,
success_rate=90.0,
avg_latency_ms=50.0,
p95_latency_ms=100.0,
p99_latency_ms=200.0,
current_queue_depth=5,
dead_letter_count=1,
by_channel=(ChannelDeliveryStat(channel_type="wechat", sent=50, success_rate=95.0),),
)
# Assert
assert result.success_rate == 90.0
assert result.by_channel[0].channel_type == "wechat"
def test_success_rate_can_be_none(self):
# Act
result = DashboardDeliveryResult(
total_sent=0,
success_count=0,
failed_count=0,
success_rate=None,
avg_latency_ms=None,
p95_latency_ms=None,
p99_latency_ms=None,
current_queue_depth=0,
dead_letter_count=0,
by_channel=(),
)
# Assert
assert result.success_rate is None
@pytest.mark.unit
class TestDashboardTodoResult:
"""工作台待办计数聚合结果 DTO 测试。"""
def test_fields_are_assigned(self):
# Act
result = DashboardTodoResult(pending_pairings=3, pending_reviews=5, dead_letter_count=2)
# Assert
assert result.pending_pairings == 3
@pytest.mark.unit
class TestRealtimeMetricsResult:
"""实时监控结果 DTO 测试。"""
def test_fields_are_assigned(self):
# Act
result = RealtimeMetricsResult(
window_seconds=60,
messages_per_second=10.5,
active_sessions=100,
active_conversations=95,
queue_depth=5,
worker_utilization=None,
by_channel=(ChannelRealtimeStat(channel_type="wechat", mps=5.0, active_sessions=50),),
)
# Assert
assert result.worker_utilization is None
assert result.by_channel[0].mps == 5.0