"""lifecycle.py DTO 单元测试。 覆盖 ``LifecycleCmd`` 与 ``LifecycleResult`` 的字段赋值、默认值、不可变 语义与 ``__post_init__`` 校验逻辑。 """ from __future__ import annotations import dataclasses from typing import Any import pytest from yuxi.channels.contract.dtos.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]