mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-08-29 03:56:43 +08:00
96 lines
3.4 KiB
Python
96 lines
3.4 KiB
Python
from contextlib import nullcontext
|
|
|
|
from app.application.plugin.config import PluginConfigCommand
|
|
from app.runtime.extensions.plugin.admission import PluginMutationAdmission
|
|
|
|
|
|
def _command(
|
|
calls: list[tuple],
|
|
*,
|
|
save_result: bool = True,
|
|
mutation=None,
|
|
) -> PluginConfigCommand:
|
|
"""构造记录端口调用顺序的插件配置用例。"""
|
|
return PluginConfigCommand(
|
|
save_config=lambda plugin_id, config, force: (
|
|
calls.append(("save", plugin_id, config, force)) or save_result
|
|
),
|
|
initialize=lambda plugin_id, config: calls.append(
|
|
("initialize", plugin_id, config)
|
|
),
|
|
stop=lambda plugin_id: calls.append(("stop", plugin_id)),
|
|
delete_config=lambda plugin_id, force: (
|
|
calls.append(("delete_config", plugin_id, force)) or True
|
|
),
|
|
delete_data=lambda plugin_id, force: (
|
|
calls.append(("delete_data", plugin_id, force)) or True
|
|
),
|
|
reload_runtime=lambda plugin_id: calls.append(("reload", plugin_id)),
|
|
publish_reset=lambda plugin_id: calls.append(("publish", plugin_id)),
|
|
refresh_registrations=lambda plugin_id: calls.append(
|
|
("registrations", plugin_id)
|
|
),
|
|
mutation=mutation or (lambda _operation: nullcontext()),
|
|
)
|
|
|
|
|
|
def test_update_stops_before_runtime_side_effects_when_save_fails() -> None:
|
|
"""配置持久化失败时不得初始化插件或刷新宿主注册。"""
|
|
calls: list[tuple] = []
|
|
|
|
result = _command(calls, save_result=False).update("DemoPlugin", {"enabled": True})
|
|
|
|
assert result.success is False
|
|
assert result.message == "插件配置保存失败"
|
|
assert calls == [("save", "DemoPlugin", {"enabled": True}, False)]
|
|
|
|
|
|
def test_update_refreshes_runtime_only_after_config_is_saved() -> None:
|
|
"""配置保存成功后按初始化、注册刷新顺序生效。"""
|
|
calls: list[tuple] = []
|
|
|
|
result = _command(calls).update("DemoPlugin", {"enabled": True})
|
|
|
|
assert result.success is True
|
|
assert calls == [
|
|
("save", "DemoPlugin", {"enabled": True}, False),
|
|
("initialize", "DemoPlugin", {"enabled": True}),
|
|
("registrations", "DemoPlugin"),
|
|
]
|
|
|
|
|
|
def test_reset_preserves_compensation_cleanup_and_reload_order() -> None:
|
|
"""重置必须先让插件补偿,再停止、清理并重建运行态和注册。"""
|
|
calls: list[tuple] = []
|
|
|
|
result = _command(calls).reset("DemoPlugin")
|
|
|
|
assert result.success is True
|
|
assert calls == [
|
|
("publish", "DemoPlugin"),
|
|
("stop", "DemoPlugin"),
|
|
("delete_config", "DemoPlugin", True),
|
|
("delete_data", "DemoPlugin", True),
|
|
("reload", "DemoPlugin"),
|
|
("registrations", "DemoPlugin"),
|
|
]
|
|
|
|
|
|
def test_sealed_config_command_rejects_before_first_side_effect() -> None:
|
|
"""配置事务在 admission 封口后返回失败,且不得先发布 reset 事件。"""
|
|
calls: list[tuple] = []
|
|
admission = PluginMutationAdmission()
|
|
admission.seal()
|
|
|
|
update_result = _command(calls, mutation=admission.hold).update(
|
|
"DemoPlugin",
|
|
{"enabled": True},
|
|
)
|
|
reset_result = _command(calls, mutation=admission.hold).reset("DemoPlugin")
|
|
|
|
assert update_result.success is False
|
|
assert reset_result.success is False
|
|
assert "停机阶段" in update_result.message
|
|
assert "停机阶段" in reset_result.message
|
|
assert calls == []
|