mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-10-29 11:27:43 +08:00
* fix(plugin): bound market metadata inputs * test(architecture): sync plugin metadata baseline * fix(plugin): narrow validated metadata identifiers * test(architecture): sync coverage baseline
96 lines
3.4 KiB
Python
96 lines
3.4 KiB
Python
"""插件安装事实与运行状态投影测试。"""
|
||
|
||
from types import SimpleNamespace
|
||
|
||
from app.runtime.extensions.plugin.catalog import PluginCatalogFacade
|
||
from app.schemas.plugin import PluginRuntimeStatus
|
||
from app.schemas.types import SystemConfigKey
|
||
|
||
|
||
def test_installed_catalog_keeps_plugins_that_are_not_loaded():
|
||
"""已安装清单中的插件即使缺依赖或源码也必须保留可观察卡片。"""
|
||
class ActivePlugin:
|
||
plugin_name = "已运行插件"
|
||
plugin_version = "1.0.0"
|
||
plugin_order = 0
|
||
|
||
active_instance = SimpleNamespace(get_state=lambda: True)
|
||
statuses = {
|
||
"ActivePlugin": PluginRuntimeStatus.ACTIVE,
|
||
"DependencyPending": PluginRuntimeStatus.DEPENDENCY_PENDING,
|
||
"SourceMissing": PluginRuntimeStatus.SOURCE_MISSING,
|
||
}
|
||
facade = PluginCatalogFacade(
|
||
classes=lambda: {"ActivePlugin": ActivePlugin},
|
||
running=lambda: {"ActivePlugin": active_instance},
|
||
storage=lambda: SimpleNamespace(
|
||
read=lambda key: [
|
||
"ActivePlugin",
|
||
"DependencyPending",
|
||
"SourceMissing",
|
||
] if key is SystemConfigKey.UserInstalledPlugins else None,
|
||
),
|
||
system=lambda: SimpleNamespace(),
|
||
market_catalog=lambda: None,
|
||
market_loader=lambda *_args, **_kwargs: [],
|
||
async_market_loader=lambda *_args, **_kwargs: [],
|
||
map_plugin=lambda **_kwargs: None,
|
||
auth_checker=lambda **_kwargs: True,
|
||
plugin_attr=lambda _plugin_id, _attr: None,
|
||
plugin_instance=lambda _plugin_id: None,
|
||
plugin_instances=lambda: {},
|
||
runtime_status=statuses.get,
|
||
log=SimpleNamespace(error=lambda *_args: None, info=lambda *_args: None),
|
||
)
|
||
|
||
plugins = facade.installed()
|
||
|
||
assert [plugin.id for plugin in plugins] == [
|
||
"ActivePlugin",
|
||
"DependencyPending",
|
||
"SourceMissing",
|
||
]
|
||
assert [plugin.runtime_status for plugin in plugins] == [
|
||
PluginRuntimeStatus.ACTIVE,
|
||
PluginRuntimeStatus.DEPENDENCY_PENDING,
|
||
PluginRuntimeStatus.SOURCE_MISSING,
|
||
]
|
||
assert plugins[1].plugin_name == "DependencyPending"
|
||
assert plugins[2].installed is True
|
||
|
||
|
||
def test_local_repository_failure_does_not_break_catalog_projection():
|
||
"""本地仓索引读取失败只影响本地候选展示,不能拖垮整个插件目录。"""
|
||
warnings: list[str] = []
|
||
|
||
def local_candidates():
|
||
raise RuntimeError("invalid local package")
|
||
|
||
facade = PluginCatalogFacade(
|
||
classes=lambda: {},
|
||
running=lambda: {},
|
||
storage=lambda: SimpleNamespace(read=lambda _key: []),
|
||
system=lambda: SimpleNamespace(
|
||
local_candidates=local_candidates,
|
||
),
|
||
market_catalog=lambda: None,
|
||
market_loader=lambda *_args, **_kwargs: [],
|
||
async_market_loader=lambda *_args, **_kwargs: [],
|
||
map_plugin=lambda **_kwargs: None,
|
||
auth_checker=lambda **_kwargs: True,
|
||
plugin_attr=lambda _plugin_id, _attr: None,
|
||
plugin_instance=lambda _plugin_id: None,
|
||
plugin_instances=lambda: {},
|
||
runtime_status=lambda _plugin_id: None,
|
||
log=SimpleNamespace(
|
||
error=lambda *_args: None,
|
||
info=lambda *_args: None,
|
||
warning=warnings.append,
|
||
),
|
||
)
|
||
|
||
assert facade.local_repository() == []
|
||
assert warnings == [
|
||
"读取本地插件仓候选失败,已跳过本地目录展示:invalid local package"
|
||
]
|