1. 删除了 test/unit/external_systems/framework/ 下的废弃空测试目录 2. 修复多处测试断言逻辑、参数传递与测试数据构造 3. 新增日志级别、缓存令牌、流事件等DTO单元测试 4. 补充路由绑定、会话仓储、outbox仓储的测试覆盖 5. 更新测试用例中的异常类型、参数校验与业务逻辑断言
227 lines
6.4 KiB
Python
227 lines
6.4 KiB
Python
"""plugin.py DTO 单元测试。
|
|
|
|
覆盖 ``Permission`` / ``EnvVar`` / ``DomainEvent`` / ``PluginSummary`` /
|
|
``InstallPluginCmd`` / ``InstallPluginResult`` / ``UninstallPluginResult`` /
|
|
``PluginConfigResult`` / ``UpdatePluginConfigCmd`` /
|
|
``BatchPluginLifecycleCmd`` / ``BatchPluginLifecycleResult`` 的字段赋值、
|
|
默认值、不可变语义与 ``__post_init__`` 校验逻辑。
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import dataclasses
|
|
from datetime import datetime
|
|
|
|
import pytest
|
|
from yuxi.channels.contract.dtos.common import Operator, OperatorRole
|
|
from yuxi.channels.contract.dtos.plugin import (
|
|
BatchPluginLifecycleCmd,
|
|
BatchPluginLifecycleResult,
|
|
BatchPluginLifecycleSuccessItem,
|
|
DomainEvent,
|
|
EnvVar,
|
|
InstallPluginCmd,
|
|
InstallPluginResult,
|
|
Permission,
|
|
PluginConfigResult,
|
|
PluginSummary,
|
|
UninstallPluginResult,
|
|
UpdatePluginConfigCmd,
|
|
)
|
|
from yuxi.channels.contract.errors import ValidationError
|
|
|
|
pytestmark = pytest.mark.unit
|
|
|
|
|
|
def _make_operator() -> Operator:
|
|
return Operator(user_id="admin-1", role=OperatorRole.SUPERADMIN_USER)
|
|
|
|
|
|
@pytest.mark.unit
|
|
class TestPermission:
|
|
"""权限等级枚举测试。"""
|
|
|
|
def test_str_values(self):
|
|
assert Permission.REQUIRED_USER == "required_user"
|
|
assert Permission.ADMIN_USER == "admin_user"
|
|
assert Permission.SUPERADMIN_USER == "superadmin_user"
|
|
|
|
|
|
@pytest.mark.unit
|
|
class TestEnvVar:
|
|
"""环境变量声明 DTO 测试。"""
|
|
|
|
def test_defaults(self):
|
|
# Act
|
|
var = EnvVar(name="API_KEY", description="API key")
|
|
# Assert
|
|
assert var.required is True
|
|
assert var.default is None
|
|
assert var.sensitive is False
|
|
|
|
|
|
@pytest.mark.unit
|
|
class TestDomainEvent:
|
|
"""领域事件 DTO 测试。"""
|
|
|
|
def test_fields_are_assigned_with_defaults(self):
|
|
# Act
|
|
event = DomainEvent(
|
|
event_id="evt-1",
|
|
event_type="PluginLoaded",
|
|
payload={"key": "value"},
|
|
timestamp=datetime(2024, 1, 1),
|
|
)
|
|
# Assert
|
|
assert event.trace_id is None
|
|
|
|
|
|
@pytest.mark.unit
|
|
class TestPluginSummary:
|
|
"""插件摘要 DTO 测试。"""
|
|
|
|
def test_fields_are_assigned(self):
|
|
# Act
|
|
summary = PluginSummary(
|
|
plugin_id="wechat-plugin",
|
|
name="WeChat Plugin",
|
|
version="1.0.0",
|
|
channel_type="wechat",
|
|
state="started",
|
|
)
|
|
# Assert
|
|
assert summary.plugin_id == "wechat-plugin"
|
|
|
|
|
|
@pytest.mark.unit
|
|
class TestInstallPluginCmd:
|
|
"""安装插件命令 DTO 测试。"""
|
|
|
|
def test_valid_construction(self):
|
|
# Act
|
|
cmd = InstallPluginCmd(
|
|
operator=_make_operator(),
|
|
source_type="path",
|
|
source="/plugins/wechat",
|
|
)
|
|
# Assert
|
|
assert cmd.version is None
|
|
assert cmd.force is False
|
|
|
|
def test_empty_source_raises(self):
|
|
# Act / Assert
|
|
with pytest.raises(ValidationError) as exc_info:
|
|
InstallPluginCmd(
|
|
operator=_make_operator(),
|
|
source_type="path",
|
|
source="",
|
|
)
|
|
assert exc_info.value.field == "source"
|
|
|
|
def test_invalid_source_type_raises(self):
|
|
# Act / Assert
|
|
with pytest.raises(ValidationError) as exc_info:
|
|
InstallPluginCmd(
|
|
operator=_make_operator(),
|
|
source_type="invalid", # type: ignore[arg-type]
|
|
source="/path",
|
|
)
|
|
assert exc_info.value.field == "source_type"
|
|
|
|
|
|
@pytest.mark.unit
|
|
class TestUpdatePluginConfigCmd:
|
|
"""更新插件配置命令 DTO 测试。"""
|
|
|
|
def test_valid_construction(self):
|
|
# Act
|
|
cmd = UpdatePluginConfigCmd(
|
|
plugin_id="wechat-plugin",
|
|
operator=_make_operator(),
|
|
config={"key": "value"},
|
|
apply_mode="hot",
|
|
)
|
|
# Assert
|
|
assert cmd.account_id is None
|
|
|
|
def test_empty_plugin_id_raises(self):
|
|
# Act / Assert
|
|
with pytest.raises(ValidationError) as exc_info:
|
|
UpdatePluginConfigCmd(
|
|
plugin_id="",
|
|
operator=_make_operator(),
|
|
config={"k": "v"},
|
|
apply_mode="hot",
|
|
)
|
|
assert exc_info.value.field == "plugin_id"
|
|
|
|
def test_empty_config_raises(self):
|
|
# Act / Assert
|
|
with pytest.raises(ValidationError) as exc_info:
|
|
UpdatePluginConfigCmd(
|
|
plugin_id="p1",
|
|
operator=_make_operator(),
|
|
config={},
|
|
apply_mode="hot",
|
|
)
|
|
assert exc_info.value.field == "config"
|
|
|
|
def test_invalid_apply_mode_raises(self):
|
|
# Act / Assert
|
|
with pytest.raises(ValidationError) as exc_info:
|
|
UpdatePluginConfigCmd(
|
|
plugin_id="p1",
|
|
operator=_make_operator(),
|
|
config={"k": "v"},
|
|
apply_mode="invalid", # type: ignore[arg-type]
|
|
)
|
|
assert exc_info.value.field == "apply_mode"
|
|
|
|
|
|
@pytest.mark.unit
|
|
class TestBatchPluginLifecycleCmd:
|
|
"""批量插件生命周期命令 DTO 测试。"""
|
|
|
|
def test_valid_with_plugin_ids(self):
|
|
# Act
|
|
cmd = BatchPluginLifecycleCmd(
|
|
operator=_make_operator(),
|
|
plugin_ids=("p1", "p2"),
|
|
)
|
|
# Assert
|
|
assert cmd.filter is None
|
|
|
|
def test_valid_with_filter(self):
|
|
# Act
|
|
cmd = BatchPluginLifecycleCmd(
|
|
operator=_make_operator(),
|
|
filter={"channel_type": "wechat"},
|
|
)
|
|
# Assert
|
|
assert cmd.plugin_ids == ()
|
|
|
|
def test_both_empty_raises(self):
|
|
# Act / Assert
|
|
with pytest.raises(ValidationError) as exc_info:
|
|
BatchPluginLifecycleCmd(operator=_make_operator())
|
|
assert exc_info.value.field == "plugin_ids"
|
|
|
|
def test_both_provided_raises(self):
|
|
# Act / Assert
|
|
with pytest.raises(ValidationError) as exc_info:
|
|
BatchPluginLifecycleCmd(
|
|
operator=_make_operator(),
|
|
plugin_ids=("p1",),
|
|
filter={"state": "stopped"},
|
|
)
|
|
assert exc_info.value.field == "plugin_ids"
|
|
|
|
def test_empty_string_in_plugin_ids_raises(self):
|
|
# Act / Assert
|
|
with pytest.raises(ValidationError) as exc_info:
|
|
BatchPluginLifecycleCmd(
|
|
operator=_make_operator(),
|
|
plugin_ids=("p1", ""),
|
|
)
|
|
assert exc_info.value.field == "plugin_ids"
|