- 新增多个业务域的__init__.py模块文件,规范包导出结构 - 调整多个DTO文件的导入路径,统一模块组织方式 - 移除测试文件中多余的空行与导入语句 - 优化部分业务模块的包层级划分
138 lines
3.8 KiB
Python
138 lines
3.8 KiB
Python
"""lifecycle.py DTO 单元测试。
|
|
|
|
覆盖 ``LifecycleCmd`` 与 ``LifecycleResult`` 的字段赋值、默认值、不可变
|
|
语义与 ``__post_init__`` 校验逻辑。
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import dataclasses
|
|
from typing import Any
|
|
|
|
import pytest
|
|
from yuxi.channels.contract.dtos.plugin.lifecycle import (
|
|
LifecycleCmd,
|
|
LifecycleResult,
|
|
)
|
|
from yuxi.channels.contract.errors import ValidationError
|
|
|
|
pytestmark = pytest.mark.unit
|
|
|
|
|
|
@pytest.mark.unit
|
|
class TestLifecycleCmd:
|
|
"""生命周期命令 DTO 测试。"""
|
|
|
|
def test_fields_are_assigned(self):
|
|
# Arrange
|
|
# Act
|
|
cmd = LifecycleCmd(event="load", plugin_id="plugin-1")
|
|
# Assert
|
|
assert cmd.event == "load"
|
|
assert cmd.plugin_id == "plugin-1"
|
|
assert cmd.params is None
|
|
|
|
def test_with_params(self):
|
|
# Arrange
|
|
params: dict[str, Any] = {"force": True}
|
|
# Act
|
|
cmd = LifecycleCmd(event="start", plugin_id="plugin-1", params=params)
|
|
# Assert
|
|
assert cmd.params is params
|
|
|
|
@pytest.mark.parametrize(
|
|
"event",
|
|
["load", "start", "pause", "resume", "stop", "unload", "reload"],
|
|
)
|
|
def test_valid_events(self, event):
|
|
# Arrange
|
|
# Act
|
|
cmd = LifecycleCmd(event=event, plugin_id="plugin-1") # type: ignore[arg-type]
|
|
# Assert
|
|
assert cmd.event == event
|
|
|
|
def test_is_frozen(self):
|
|
# Arrange
|
|
cmd = LifecycleCmd(event="load", plugin_id="plugin-1")
|
|
# Act / Assert
|
|
with pytest.raises(dataclasses.FrozenInstanceError):
|
|
cmd.event = "stop" # type: ignore[misc]
|
|
|
|
def test_empty_plugin_id_raises_validation_error(self):
|
|
# Arrange
|
|
# Act / Assert
|
|
with pytest.raises(ValidationError) as exc_info:
|
|
LifecycleCmd(event="load", plugin_id="")
|
|
assert exc_info.value.field == "plugin_id"
|
|
|
|
def test_invalid_event_raises_validation_error(self):
|
|
# Arrange
|
|
# Act / Assert
|
|
with pytest.raises(ValidationError) as exc_info:
|
|
LifecycleCmd(event="invalid", plugin_id="plugin-1") # type: ignore[arg-type]
|
|
assert exc_info.value.field == "event"
|
|
|
|
|
|
@pytest.mark.unit
|
|
class TestLifecycleResult:
|
|
"""生命周期结果 DTO 测试。"""
|
|
|
|
def test_required_fields_are_assigned(self):
|
|
# Arrange
|
|
# Act
|
|
result = LifecycleResult(state="started")
|
|
# Assert
|
|
assert result.state == "started"
|
|
|
|
def test_defaults_are_correct(self):
|
|
# Arrange
|
|
# Act
|
|
result = LifecycleResult(state="started")
|
|
# Assert
|
|
assert result.error is None
|
|
assert result.error_code == ""
|
|
assert result.trace_id == ""
|
|
|
|
def test_failed_result_with_error(self):
|
|
# Arrange
|
|
# Act
|
|
result = LifecycleResult(
|
|
state="failed",
|
|
error="plugin crashed",
|
|
error_code="PLUGIN_FAILED",
|
|
trace_id="trace-1",
|
|
)
|
|
# Assert
|
|
assert result.state == "failed"
|
|
assert result.error == "plugin crashed"
|
|
assert result.error_code == "PLUGIN_FAILED"
|
|
assert result.trace_id == "trace-1"
|
|
|
|
@pytest.mark.parametrize(
|
|
"state",
|
|
[
|
|
"discovered",
|
|
"resolved",
|
|
"loaded",
|
|
"initialized",
|
|
"started",
|
|
"paused",
|
|
"stopped",
|
|
"unloaded",
|
|
"failed",
|
|
],
|
|
)
|
|
def test_valid_states(self, state):
|
|
# Arrange
|
|
# Act
|
|
result = LifecycleResult(state=state) # type: ignore[arg-type]
|
|
# Assert
|
|
assert result.state == state
|
|
|
|
def test_is_frozen(self):
|
|
# Arrange
|
|
result = LifecycleResult(state="started")
|
|
# Act / Assert
|
|
with pytest.raises(dataclasses.FrozenInstanceError):
|
|
result.state = "stopped" # type: ignore[misc]
|