1. 删除了 test/unit/external_systems/framework/ 下的废弃空测试目录 2. 修复多处测试断言逻辑、参数传递与测试数据构造 3. 新增日志级别、缓存令牌、流事件等DTO单元测试 4. 补充路由绑定、会话仓储、outbox仓储的测试覆盖 5. 更新测试用例中的异常类型、参数校验与业务逻辑断言
235 lines
6.8 KiB
Python
235 lines
6.8 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, timezone
|
|
|
|
import pytest
|
|
from yuxi.channels.contract.dtos.channel import ChannelType
|
|
from yuxi.channels.contract.dtos.dashboard import (
|
|
AccountStats,
|
|
ChannelDeliveryStat,
|
|
ChannelRealtimeStat,
|
|
DashboardDeliveryQuery,
|
|
DashboardDeliveryResult,
|
|
DashboardOverview,
|
|
DashboardOverviewQuery,
|
|
DashboardTodoResult,
|
|
MessageStats,
|
|
OutboxStatsSummary,
|
|
RealtimeMetricsResult,
|
|
RealtimeQuery,
|
|
SessionStats,
|
|
)
|
|
from yuxi.channels.contract.errors import ValidationError
|
|
|
|
pytestmark = pytest.mark.unit
|
|
|
|
|
|
def _aware_dt() -> datetime:
|
|
return datetime(2024, 1, 1, 12, 0, 0, tzinfo=timezone.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=timezone.utc),
|
|
end_time=datetime(2024, 1, 1, tzinfo=timezone.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=timezone.utc),
|
|
end_time=datetime(2024, 1, 1, tzinfo=timezone.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
|