ForcePilot/backend/test/unit/channels/contract/dtos/test_pairing.py
Kris 3ae9c0bd21 test: 批量修复与新增单元测试用例,清理废弃测试目录
1.  删除了 test/unit/external_systems/framework/ 下的废弃空测试目录
2.  修复多处测试断言逻辑、参数传递与测试数据构造
3.  新增日志级别、缓存令牌、流事件等DTO单元测试
4.  补充路由绑定、会话仓储、outbox仓储的测试覆盖
5.  更新测试用例中的异常类型、参数校验与业务逻辑断言
2026-07-11 21:43:16 +08:00

219 lines
6.2 KiB
Python

"""pairing.py DTO 单元测试。
覆盖 ``PairingId`` / ``PairingStatus`` / ``DmDecision`` / ``DmPolicy`` /
``PairingRecord`` / ``PairingQuery`` / ``BotLoopBudget`` /
``CreatePairingResult`` / ``BatchPairingCmd`` / ``CleanExpiredPairingsCmd`` /
``PairingStatsQuery`` / ``PairingTrendPoint`` / ``PairingStatsResult`` 的
字段赋值、默认值、不可变语义与 ``__post_init__`` 校验逻辑。
"""
from __future__ import annotations
import dataclasses
from datetime import datetime
import pytest
from yuxi.channels.contract.dtos.channel import ChannelType
from yuxi.channels.contract.dtos.common import Operator, OperatorRole
from yuxi.channels.contract.dtos.pairing import (
BatchPairingCmd,
BotLoopBudget,
CleanExpiredPairingsCmd,
CreatePairingResult,
DmDecision,
DmPolicy,
PairingId,
PairingQuery,
PairingRecord,
PairingStatsQuery,
PairingStatsResult,
PairingStatus,
PairingTrendPoint,
)
from yuxi.channels.contract.errors import ValidationError
pytestmark = pytest.mark.unit
def _make_operator() -> Operator:
return Operator(user_id="admin-1", role=OperatorRole.ADMIN_USER)
@pytest.mark.unit
class TestPairingId:
"""配对 ID DTO 测试。"""
def test_value_is_assigned(self):
# Act
pid = PairingId(value="pair-001")
# Assert
assert pid.value == "pair-001"
@pytest.mark.unit
class TestPairingStatus:
"""配对状态枚举测试。"""
def test_str_values(self):
assert PairingStatus.PENDING == "pending"
assert PairingStatus.APPROVED == "approved"
assert PairingStatus.REJECTED == "rejected"
assert PairingStatus.EXPIRED == "expired"
assert PairingStatus.REVOKED == "revoked"
@pytest.mark.unit
class TestDmDecision:
"""DM 决策枚举测试。"""
def test_str_values(self):
assert DmDecision.ALLOW == "allow"
assert DmDecision.DENY == "deny"
assert DmDecision.PENDING_PAIRING == "pending_pairing"
assert DmDecision.WHITELIST == "whitelist"
@pytest.mark.unit
class TestDmPolicy:
"""DM 策略枚举测试。"""
def test_str_values(self):
assert DmPolicy.ALLOW == "allow"
assert DmPolicy.DENY == "deny"
assert DmPolicy.PAIRING_REQUIRED == "pairing_required"
assert DmPolicy.WHITELIST == "whitelist"
@pytest.mark.unit
class TestPairingRecord:
"""配对记录 DTO 测试。"""
def test_fields_are_assigned_with_defaults(self):
# Act
record = PairingRecord(
pairing_id="pair-1",
channel_account_id="acc-1",
peer_id="peer-1",
status=PairingStatus.PENDING,
)
# Assert
assert record.channel_type is None
assert record.approver_id is None
assert record.version == 1
def test_is_frozen(self):
# Arrange
record = PairingRecord(
pairing_id="p1",
channel_account_id="a1",
peer_id="peer",
status=PairingStatus.PENDING,
)
# Act / Assert
with pytest.raises(dataclasses.FrozenInstanceError):
record.status = PairingStatus.APPROVED # type: ignore[misc]
@pytest.mark.unit
class TestPairingQuery:
"""配对查询 DTO 测试。"""
def test_defaults(self):
# Act
query = PairingQuery()
# Assert
assert query.limit == 100
assert query.offset == 0
assert query.status is None
@pytest.mark.unit
class TestBotLoopBudget:
"""机器人循环预算 DTO 测试。"""
def test_defaults(self):
# Act
budget = BotLoopBudget(max_replies_per_hour=100, cooldown_seconds=60)
# Assert
assert budget.current_count == 0
assert budget.last_reply_at is None
@pytest.mark.unit
class TestBatchPairingCmd:
"""批量配对命令 DTO 测试。"""
def test_valid_construction(self):
# Act
cmd = BatchPairingCmd(
pairing_ids=("pair-1", "pair-2"),
operator=_make_operator(),
)
# Assert
assert cmd.reason is None
def test_empty_pairing_ids_raises(self):
# Act / Assert
with pytest.raises(ValidationError) as exc_info:
BatchPairingCmd(pairing_ids=(), operator=_make_operator())
assert exc_info.value.field == "pairing_ids"
def test_too_many_pairing_ids_raises(self):
# Arrange
ids = tuple(f"pair-{i}" for i in range(501))
# Act / Assert
with pytest.raises(ValidationError) as exc_info:
BatchPairingCmd(pairing_ids=ids, operator=_make_operator())
assert exc_info.value.field == "pairing_ids"
def test_reason_too_long_raises(self):
# Act / Assert
with pytest.raises(ValidationError) as exc_info:
BatchPairingCmd(
pairing_ids=("pair-1",),
operator=_make_operator(),
reason="x" * 513,
)
assert exc_info.value.field == "reason"
@pytest.mark.unit
class TestCleanExpiredPairingsCmd:
"""清理过期配对命令 DTO 测试。"""
def test_defaults(self):
# Act
cmd = CleanExpiredPairingsCmd(operator=_make_operator())
# Assert
assert cmd.max_count == 500
assert cmd.channel_type is None
def test_max_count_below_min_raises(self):
# Act / Assert
with pytest.raises(ValidationError) as exc_info:
CleanExpiredPairingsCmd(operator=_make_operator(), max_count=0)
assert exc_info.value.field == "max_count"
def test_max_count_above_max_raises(self):
# Act / Assert
with pytest.raises(ValidationError) as exc_info:
CleanExpiredPairingsCmd(operator=_make_operator(), max_count=1001)
assert exc_info.value.field == "max_count"
@pytest.mark.unit
class TestPairingStatsQuery:
"""配对统计查询 DTO 测试。"""
def test_defaults(self):
# Act
query = PairingStatsQuery()
# Assert
assert query.granularity == "day"
def test_invalid_granularity_raises(self):
# Act / Assert
with pytest.raises(ValidationError) as exc_info:
PairingStatsQuery(granularity="minute")
assert exc_info.value.field == "granularity"