ForcePilot/backend/test/unit/channels/contract/dtos/test_dashboard.py
Kris 002e6a356b chore: 批量整理代码变更,修复多类细节问题
1.  清理测试文件中未使用的导入与冗余代码
2.  修复审计日志与批量操作的空值约束,统一填充"global"作为默认渠道
3.  调整批量消息撤回的响应语义,对齐其他端点的部分成功契约
4.  修复访问规则批量克隆的唯一约束问题,新增后缀自动处理逻辑
5.  替换anyio为asyncio并行调用,修正时间UTC导入路径
6.  优化前端外部系统概览页的刷新状态提示与缓存逻辑
7.  修复测试用例中的断言与请求方式问题,适配httpx删除请求特性
8.  重构后端路由的依赖注入,移除冗余的数据库会话依赖
9.  调整测试用例的权限校验逻辑,修正强制登出的权限判断
10. 修复语义分块测试的numpy依赖问题,清理冗余导入
2026-07-13 20:48:29 +08:00

230 lines
6.7 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 datetime, UTC
import pytest
from yuxi.channels.contract.dtos.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