fix(startup): order plugin service composition

This commit is contained in:
jxxghp
2026-08-29 15:26:31 +08:00
parent dba4ddc168
commit d17d62e958
9 changed files with 226 additions and 237 deletions

View File

@@ -3,7 +3,7 @@ import uuid
from collections.abc import Callable
from datetime import datetime, timezone
from pathlib import Path
from typing import Any
from typing import Any, cast
from app.adapters.external.market import (
LOCAL_REPO_PREFIX,
@@ -66,6 +66,9 @@ from app.application.plugin.release import (
reset_plugin_release_service,
)
from app.application.plugin.routes import register_plugin_api
from app.application.plugin.runtime import (
PluginRuntime as PluginRuntimePort,
)
from app.application.plugin.runtime import (
configure_plugin_runtime,
get_plugin_manager,
@@ -168,17 +171,21 @@ def build_plugin_runtime_graph(host: PluginRuntimeHost) -> PluginRuntime:
)
def configure_plugin_runtime_services() -> None:
"""在模块对象图构造前发布插件 Runtime 工厂和应用层提供器。"""
configure_plugin_catalog_factory(_build_plugin_catalog)
configure_plugin_runtime_factory(build_plugin_runtime_graph)
configure_plugin_runtime(lambda: cast(PluginRuntimePort, PluginManager()))
def configure_plugin_services() -> None:
"""把兼容诊断、远程上报和站点认证等级装配到插件管理器。"""
"""在模块持久化端口就绪后装配完整插件应用服务。"""
market_transport = PluginMarketTransport()
market_client = PluginMarketClient(market_transport)
package_manager = PluginPackageManager(
source=PluginPackageSourceClient(market_transport),
plugin_root=Path(get_runtime_setting('ROOT_PATH')) / "app" / "plugins",
)
configure_plugin_catalog_factory(_build_plugin_catalog)
configure_plugin_runtime_factory(build_plugin_runtime_graph)
configure_plugin_runtime(lambda: PluginManager())
plugin_manager = get_plugin_manager()
inventory_reader = PluginCandidateInventoryReader(
market_loader=market_client.get_plugin_index_result,
@@ -519,7 +526,6 @@ async def sync_plugins(
plugin_manager = None
try:
loop = main_loop_registry.require()
configure_plugin_services()
plugin_manager = PluginManager()
with plugin_manager.mutation("启动后同步插件"):
await get_plugin_identity_migration().migrate()
@@ -714,7 +720,6 @@ def init_plugins():
"""
初始化插件
"""
configure_plugin_services()
plugin_manager = PluginManager()
if not plugin_manager.reopen_plugins():
raise RuntimeError("上一应用生命周期的插件后台服务仍未收敛")

View File

@@ -63,6 +63,7 @@ from app.startup.initializers.network import ( # noqa: E402
reset_chain_network_ports,
)
from app.startup.initializers.plugins import (
configure_plugin_runtime_services,
configure_plugin_services,
execute_task,
finalize_plugins,
@@ -469,9 +470,9 @@ def build_lifecycle_components(app: FastAPI) -> tuple[LifecycleComponent, ...]:
start_timeout_seconds=30,
),
LifecycleComponent(
name="插件服务装配",
name="插件运行时装配",
dependencies=("路由",),
start=configure_plugin_services,
start=configure_plugin_runtime_services,
start_order=65,
start_timeout_seconds=30,
),
@@ -479,7 +480,7 @@ def build_lifecycle_components(app: FastAPI) -> tuple[LifecycleComponent, ...]:
name="模块服务",
dependencies=(
"路由",
"插件服务装配",
"插件运行时装配",
"站点访问端口",
"Chain 外部端口",
"Chain 网络端口",
@@ -491,9 +492,16 @@ def build_lifecycle_components(app: FastAPI) -> tuple[LifecycleComponent, ...]:
start_timeout_seconds=300,
stop_timeout_seconds=300,
),
LifecycleComponent(
name="插件服务装配",
dependencies=("模块服务",),
start=configure_plugin_services,
start_order=72,
start_timeout_seconds=30,
),
LifecycleComponent(
name="插件备份恢复",
dependencies=("模块服务", "消息队列"),
dependencies=("插件服务装配", "消息队列"),
mode=LifecycleMode.NORMAL_ONLY,
start=prepare_plugin_restore,
start_order=80,

View File

@@ -78,7 +78,7 @@ MoviePilot V3 已经形成较清晰的模块化单体:`foundation`、`domain`
| Event Contract | 53 | 均已有 payload model,但当前全部是 diagnostic enforcement |
| Python 源码量 | 约 298,700 行 | 60 个文件超过 1,000 行,10 个超过 2,000 行 |
| 长方法 | 299 个超过 80 行 | 70 个超过 150 行,23 个超过 250 行;大量是私有方法 |
| 全量 mypy 历史债务 | 10,415 / 593 文件 | strict frontier 当前覆盖 41 个文件,Plugin、LLM Provider 与 Agent Manager owner 拆分及受益调用方低水位已固化 |
| 全量 mypy 历史债务 | 10,413 / 593 文件 | strict frontier 当前覆盖 41 个文件,Plugin、LLM Provider 与 Agent Manager owner 拆分及受益调用方低水位已固化 |
| Ruff 历史诊断 | 651 | 低水位门禁通过,但规则集只覆盖 `E4/E7/E9/F/I` |
| 覆盖率低水位 | Application 80.92%,Domain 79.89% | Chain、Runtime、Agent、Adapter、Startup 未进入包级覆盖率门禁 |

View File

@@ -3285,14 +3285,14 @@
"no-untyped-def": 2
},
"app/startup/initializers/modules.py": {
"arg-type": 7,
"arg-type": 6,
"assignment": 1,
"attr-defined": 2,
"misc": 1,
"no-any-return": 2,
"no-untyped-call": 28,
"no-untyped-def": 6,
"return-value": 3
"return-value": 2
},
"app/startup/initializers/plugins.py": {
"arg-type": 5,

View File

@@ -1,41 +1,41 @@
{
"schema_version": 2,
"generated_at": "2026-08-29T05:54:31.505070+00:00",
"generated_at": "2026-08-29T07:27:24.582316+00:00",
"platform": "macOS-26.5.2-arm64-arm-64bit-Mach-O",
"python": "3.14.3",
"repeat": 3,
"targets": {
"app.startup.lifecycle": {
"loaded_app_module_count": 491,
"max_ms": 1180.874,
"median_ms": 1117.895,
"min_ms": 1107.448,
"max_ms": 1159.462,
"median_ms": 1074.592,
"min_ms": 1072.211,
"samples_ms": [
1107.448,
1117.895,
1180.874
1159.462,
1074.592,
1072.211
]
},
"app.factory": {
"loaded_app_module_count": 503,
"max_ms": 1160.129,
"median_ms": 1133.423,
"min_ms": 1109.307,
"max_ms": 1094.111,
"median_ms": 1092.394,
"min_ms": 1090.171,
"samples_ms": [
1160.129,
1133.423,
1109.307
1090.171,
1092.394,
1094.111
]
},
"app.main": {
"loaded_app_module_count": 505,
"max_ms": 1194.892,
"median_ms": 1191.744,
"min_ms": 1190.037,
"max_ms": 1165.991,
"median_ms": 1159.713,
"min_ms": 1143.531,
"samples_ms": [
1194.892,
1191.744,
1190.037
1143.531,
1159.713,
1165.991
]
}
},
@@ -46,69 +46,33 @@
"samples": [
{
"mode": "normal",
"enabled_component_count": 31,
"startup_ms": 0.667,
"full_lifespan_ms": 1.794,
"stage_ms": {
"文件日志": 0.069,
"后台任务登记器": 0.036,
"数据库准备": 0.027,
"HTTP 基础能力": 0.025,
"站点访问端口": 0.02,
"Chain 外部端口": 0.019,
"Chain 网络端口": 0.018,
"领域依赖装配": 0.018,
"数据库引擎预热": 0.018,
"数据库连接预算": 0.018,
"路由": 0.021,
"插件服务装配": 0.018,
"模块服务": 0.018,
"消息队列": 0.018,
"插件备份恢复": 0.018,
"插件": 0.017,
"定时器": 0.017,
"监控器": 0.017,
"待处理整理回放": 0.018,
"命令服务": 0.017,
"工作流": 0.017,
"插件同步与启动收尾": 0.017
},
"threads_before": 1,
"threads_started": 1,
"threads_after": 1,
"tasks_before": 1,
"tasks_started": 1,
"tasks_after": 1,
"database_connections_started": 0
},
{
"mode": "normal",
"enabled_component_count": 31,
"startup_ms": 0.656,
"full_lifespan_ms": 1.933,
"enabled_component_count": 32,
"startup_ms": 0.756,
"full_lifespan_ms": 1.874,
"stage_ms": {
"文件日志": 0.07,
"后台任务登记器": 0.036,
"数据库准备": 0.027,
"HTTP 基础能力": 0.022,
"站点访问端口": 0.022,
"Chain 外部端口": 0.019,
"Chain 网络端口": 0.018,
"领域依赖装配": 0.018,
"数据库引擎预热": 0.018,
"数据库连接预算": 0.017,
"路由": 0.019,
"插件服务装配": 0.018,
"模块服务": 0.017,
"消息队列": 0.018,
"插件备份恢复": 0.017,
"插件": 0.017,
"定时器": 0.018,
"后台任务登记器": 0.043,
"数据库准备": 0.032,
"HTTP 基础能力": 0.028,
"站点访问端口": 0.024,
"Chain 外部端口": 0.023,
"Chain 网络端口": 0.02,
"领域依赖装配": 0.022,
"数据库引擎预热": 0.02,
"数据库连接预算": 0.022,
"路由": 0.023,
"插件运行时装配": 0.022,
"模块服务": 0.02,
"插件服务装配": 0.022,
"消息队列": 0.02,
"插件备份恢复": 0.021,
"插件": 0.021,
"定时器": 0.021,
"监控器": 0.017,
"待处理整理回放": 0.017,
"待处理整理回放": 0.019,
"命令服务": 0.017,
"工作流": 0.017,
"插件同步与启动收尾": 0.017
"工作流": 0.02,
"插件同步与启动收尾": 0.021
},
"threads_before": 1,
"threads_started": 1,
@@ -120,32 +84,71 @@
},
{
"mode": "normal",
"enabled_component_count": 31,
"startup_ms": 0.737,
"full_lifespan_ms": 1.891,
"enabled_component_count": 32,
"startup_ms": 0.709,
"full_lifespan_ms": 1.824,
"stage_ms": {
"文件日志": 0.074,
"文件日志": 0.064,
"后台任务登记器": 0.034,
"数据库准备": 0.026,
"HTTP 基础能力": 0.025,
"站点访问端口": 0.021,
"Chain 外部端口": 0.021,
"Chain 网络端口": 0.02,
"领域依赖装配": 0.02,
"数据库引擎预热": 0.017,
"数据库连接预算": 0.019,
"路由": 0.02,
"插件运行时装配": 0.019,
"模块服务": 0.02,
"插件服务装配": 0.021,
"消息队列": 0.021,
"插件备份恢复": 0.021,
"插件": 0.02,
"定时器": 0.02,
"监控器": 0.021,
"待处理整理回放": 0.021,
"命令服务": 0.021,
"工作流": 0.02,
"插件同步与启动收尾": 0.021
},
"threads_before": 1,
"threads_started": 1,
"threads_after": 1,
"tasks_before": 1,
"tasks_started": 1,
"tasks_after": 1,
"database_connections_started": 0
},
{
"mode": "normal",
"enabled_component_count": 32,
"startup_ms": 0.751,
"full_lifespan_ms": 1.889,
"stage_ms": {
"文件日志": 0.069,
"后台任务登记器": 0.037,
"数据库准备": 0.029,
"HTTP 基础能力": 0.03,
"数据库准备": 0.028,
"HTTP 基础能力": 0.029,
"站点访问端口": 0.024,
"Chain 外部端口": 0.023,
"Chain 网络端口": 0.022,
"领域依赖装配": 0.021,
"领域依赖装配": 0.022,
"数据库引擎预热": 0.021,
"数据库连接预算": 0.021,
"路由": 0.022,
"插件服务装配": 0.021,
"模块服务": 0.02,
"消息队列": 0.02,
"插件备份恢复": 0.02,
"插件": 0.019,
"定时器": 0.02,
"监控器": 0.02,
"待处理整理回放": 0.02,
"命令服务": 0.02,
"工作流": 0.02,
"插件同步与启动收尾": 0.017
"插件运行时装配": 0.021,
"模块服务": 0.021,
"插件服务装配": 0.022,
"消息队列": 0.021,
"插件备份恢复": 0.021,
"插件": 0.021,
"定时器": 0.021,
"监控器": 0.021,
"待处理整理回放": 0.021,
"命令服务": 0.021,
"工作流": 0.021,
"插件同步与启动收尾": 0.021
},
"threads_before": 1,
"threads_started": 1,
@@ -156,9 +159,9 @@
"database_connections_started": 0
}
],
"median_startup_ms": 0.667,
"median_full_lifespan_ms": 1.891,
"enabled_component_count": 31,
"median_startup_ms": 0.751,
"median_full_lifespan_ms": 1.874,
"enabled_component_count": 32,
"enabled_components": [
"文件日志",
"后台任务登记器",
@@ -171,8 +174,9 @@
"数据库引擎预热",
"数据库连接预算",
"路由",
"插件服务装配",
"插件运行时装配",
"模块服务",
"插件服务装配",
"插件备份恢复",
"消息队列",
"插件",
@@ -197,24 +201,56 @@
"samples": [
{
"mode": "safe",
"enabled_component_count": 19,
"startup_ms": 0.556,
"full_lifespan_ms": 1.16,
"enabled_component_count": 20,
"startup_ms": 0.614,
"full_lifespan_ms": 1.263,
"stage_ms": {
"文件日志": 0.072,
"后台任务登记器": 0.04,
"数据库准备": 0.026,
"HTTP 基础能力": 0.023,
"站点访问端口": 0.02,
"Chain 外部端口": 0.019,
"Chain 网络端口": 0.018,
"领域依赖装配": 0.018,
"数据库引擎预热": 0.018,
"数据库连接预算": 0.017,
"路由": 0.018,
"插件服务装配": 0.018,
"模块服务": 0.017,
"消息队列": 0.017,
"文件日志": 0.069,
"后台任务登记器": 0.039,
"数据库准备": 0.03,
"HTTP 基础能力": 0.027,
"站点访问端口": 0.024,
"Chain 外部端口": 0.024,
"Chain 网络端口": 0.022,
"领域依赖装配": 0.02,
"数据库引擎预热": 0.022,
"数据库连接预算": 0.026,
"路由": 0.023,
"插件运行时装配": 0.021,
"模块服务": 0.022,
"插件服务装配": 0.021,
"消息队列": 0.021,
"插件同步与启动收尾": 0.02
},
"threads_before": 1,
"threads_started": 1,
"threads_after": 1,
"tasks_before": 1,
"tasks_started": 1,
"tasks_after": 1,
"database_connections_started": 0
},
{
"mode": "safe",
"enabled_component_count": 20,
"startup_ms": 0.609,
"full_lifespan_ms": 1.252,
"stage_ms": {
"文件日志": 0.071,
"后台任务登记器": 0.036,
"数据库准备": 0.028,
"HTTP 基础能力": 0.026,
"站点访问端口": 0.021,
"Chain 外部端口": 0.021,
"Chain 网络端口": 0.021,
"领域依赖装配": 0.019,
"数据库引擎预热": 0.027,
"数据库连接预算": 0.022,
"路由": 0.026,
"插件运行时装配": 0.022,
"模块服务": 0.021,
"插件服务装配": 0.021,
"消息队列": 0.02,
"插件同步与启动收尾": 0.017
},
"threads_before": 1,
@@ -227,55 +263,26 @@
},
{
"mode": "safe",
"enabled_component_count": 19,
"startup_ms": 0.637,
"full_lifespan_ms": 1.26,
"enabled_component_count": 20,
"startup_ms": 0.582,
"full_lifespan_ms": 1.229,
"stage_ms": {
"文件日志": 0.081,
"后台任务登记器": 0.038,
"数据库准备": 0.03,
"HTTP 基础能力": 0.027,
"站点访问端口": 0.024,
"Chain 外部端口": 0.022,
"Chain 网络端口": 0.021,
"领域依赖装配": 0.022,
"数据库引擎预热": 0.022,
"数据库连接预算": 0.022,
"路由": 0.021,
"插件服务装配": 0.022,
"模块服务": 0.021,
"消息队列": 0.021,
"插件同步与启动收尾": 0.021
},
"threads_before": 1,
"threads_started": 1,
"threads_after": 1,
"tasks_before": 1,
"tasks_started": 1,
"tasks_after": 1,
"database_connections_started": 0
},
{
"mode": "safe",
"enabled_component_count": 19,
"startup_ms": 0.578,
"full_lifespan_ms": 1.189,
"stage_ms": {
"文件日志": 0.071,
"后台任务登记器": 0.036,
"文件日志": 0.072,
"后台任务登记器": 0.042,
"数据库准备": 0.028,
"HTTP 基础能力": 0.027,
"HTTP 基础能力": 0.025,
"站点访问端口": 0.023,
"Chain 外部端口": 0.022,
"Chain 外部端口": 0.021,
"Chain 网络端口": 0.022,
"领域依赖装配": 0.02,
"数据库引擎预热": 0.021,
"数据库连接预算": 0.021,
"路由": 0.022,
"插件服务装配": 0.021,
"领域依赖装配": 0.017,
"数据库引擎预热": 0.019,
"数据库连接预算": 0.019,
"路由": 0.019,
"插件运行时装配": 0.02,
"模块服务": 0.02,
"插件服务装配": 0.019,
"消息队列": 0.021,
"插件同步与启动收尾": 0.02
"插件同步与启动收尾": 0.019
},
"threads_before": 1,
"threads_started": 1,
@@ -286,9 +293,9 @@
"database_connections_started": 0
}
],
"median_startup_ms": 0.578,
"median_full_lifespan_ms": 1.189,
"enabled_component_count": 19,
"median_startup_ms": 0.609,
"median_full_lifespan_ms": 1.252,
"enabled_component_count": 20,
"enabled_components": [
"文件日志",
"后台任务登记器",
@@ -301,8 +308,9 @@
"数据库引擎预热",
"数据库连接预算",
"路由",
"插件服务装配",
"插件运行时装配",
"模块服务",
"插件服务装配",
"消息队列",
"整理后台服务",
"AI智能体会话",

View File

@@ -45,6 +45,7 @@ def _patch_lifespan(monkeypatch, *, failing_step: str | None = None) -> dict:
"init_workflow",
):
monkeypatch.setattr(lifecycle, name, MagicMock())
monkeypatch.setattr(lifecycle, "configure_plugin_runtime_services", MagicMock())
monkeypatch.setattr(lifecycle, "configure_plugin_services", MagicMock())
plugin_recovery = MagicMock()
plugin_recovery.replay = AsyncMock()
@@ -547,12 +548,13 @@ def test_lifespan_waits_for_uncancellable_plugin_settlement_before_shutdown(
assert order[:2] == ["settled", "backup"]
def test_lifespan_configures_plugin_services_before_modules_and_restore(monkeypatch):
"""插件 Runtime 必须先于模块 Chain 上下文和恢复阶段完成装配。"""
def test_lifespan_configures_plugin_runtime_and_services_in_dependency_order(monkeypatch):
"""插件 Runtime、模块持久化和应用服务必须按依赖顺序装配。"""
shutdown_steps = _patch_lifespan(monkeypatch)
order = []
lifecycle.configure_plugin_services.side_effect = lambda: order.append("configure")
lifecycle.configure_plugin_runtime_services.side_effect = lambda: order.append("runtime")
lifecycle.init_modules.side_effect = lambda: order.append("modules")
lifecycle.configure_plugin_services.side_effect = lambda: order.append("services")
lifecycle.get_plugin_installation_recovery.return_value.replay.side_effect = (
lambda: order.append("replay")
)
@@ -566,7 +568,7 @@ def test_lifespan_configures_plugin_services_before_modules_and_restore(monkeypa
asyncio.run(run_lifespan())
assert order == ["configure", "modules", "replay", "restore"]
assert order == ["runtime", "modules", "services", "replay", "restore"]
_assert_completed_once(shutdown_steps["close_http"])
@@ -671,8 +673,9 @@ def test_lifecycle_manifest_declares_normal_and_safe_mode_order() -> None:
"数据库引擎预热",
"数据库连接预算",
"路由",
"插件服务装配",
"插件运行时装配",
"模块服务",
"插件服务装配",
"消息队列",
"插件备份恢复",
"插件",
@@ -719,8 +722,9 @@ def test_lifecycle_manifest_declares_normal_and_safe_mode_order() -> None:
"数据库引擎预热",
"数据库连接预算",
"路由",
"插件服务装配",
"插件运行时装配",
"模块服务",
"插件服务装配",
"消息队列",
"AI智能体会话",
"整理后台服务",

View File

@@ -505,11 +505,6 @@ async def test_sync_runs_identity_migration_before_automatic_install(
"require",
lambda: asyncio.get_running_loop(),
)
monkeypatch.setattr(
plugins_initializer,
"configure_plugin_services",
lambda: order.append("configure"),
)
monkeypatch.setattr(plugins_initializer, "PluginManager", lambda: manager)
monkeypatch.setattr(
plugins_initializer,
@@ -529,7 +524,7 @@ async def test_sync_runs_identity_migration_before_automatic_install(
monkeypatch.setattr(plugins_initializer, "execute_task", execute)
assert await plugins_initializer.sync_plugins() is False
assert order == ["configure", "migrate", "identity", "sync"]
assert order == ["migrate", "identity", "sync"]
@pytest.mark.asyncio

View File

@@ -2,9 +2,6 @@
import ast
from pathlib import Path
from unittest.mock import Mock
import pytest
from app.startup.initializers import plugins as plugins_initializer
@@ -99,9 +96,9 @@ def test_startup_injects_runtime_factory_before_manager_materialization() -> Non
for node in _parse(startup_path).body
if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef))
}
service_calls = sorted(
runtime_calls = sorted(
(node.lineno, ast.unparse(node.func))
for node in ast.walk(functions["configure_plugin_services"])
for node in ast.walk(functions["configure_plugin_runtime_services"])
if isinstance(node, ast.Call)
)
sync_calls = sorted(
@@ -110,36 +107,22 @@ def test_startup_injects_runtime_factory_before_manager_materialization() -> Non
if isinstance(node, ast.Call)
)
assert service_calls.index(
next(item for item in service_calls if item[1] == "configure_plugin_runtime_factory")
) < service_calls.index(
next(item for item in service_calls if item[1] == "configure_plugin_runtime")
) < service_calls.index(
next(item for item in service_calls if item[1] == "get_plugin_manager")
)
assert sync_calls.index(
next(item for item in sync_calls if item[1] == "configure_plugin_services")
) < sync_calls.index(
next(item for item in sync_calls if item[1] == "PluginManager")
assert runtime_calls.index(
next(item for item in runtime_calls if item[1] == "configure_plugin_runtime_factory")
) < runtime_calls.index(
next(item for item in runtime_calls if item[1] == "configure_plugin_runtime")
)
assert not any(item[1] == "configure_plugin_services" for item in sync_calls)
def test_plugin_services_publish_application_runtime_before_lookup(monkeypatch) -> None:
"""插件服务装配必须先发布 Application provider,再读取 Manager。"""
def test_plugin_runtime_services_publish_application_runtime(monkeypatch) -> None:
"""模块对象图构造前必须先发布 Runtime 工厂和 Application provider。"""
order: list[str] = []
class _LookupReached(RuntimeError):
"""表示测试已执行到首次插件 Manager 读取。"""
def factory(*_args, **_kwargs):
"""返回不触发真实构造逻辑的测试替身。"""
return Mock()
monkeypatch.setattr(plugins_initializer, "PluginMarketTransport", factory)
monkeypatch.setattr(plugins_initializer, "PluginMarketClient", factory)
monkeypatch.setattr(plugins_initializer, "PluginPackageSourceClient", factory)
monkeypatch.setattr(plugins_initializer, "PluginPackageManager", factory)
monkeypatch.setattr(plugins_initializer, "configure_plugin_catalog_factory", Mock())
monkeypatch.setattr(
plugins_initializer,
"configure_plugin_catalog_factory",
lambda _factory: order.append("catalog"),
)
monkeypatch.setattr(
plugins_initializer,
"configure_plugin_runtime_factory",
@@ -151,17 +134,9 @@ def test_plugin_services_publish_application_runtime_before_lookup(monkeypatch)
lambda _provider: order.append("provider"),
)
def lookup_manager():
"""记录首次读取并终止无关的后续服务图构造。"""
order.append("lookup")
raise _LookupReached
plugins_initializer.configure_plugin_runtime_services()
monkeypatch.setattr(plugins_initializer, "get_plugin_manager", lookup_manager)
with pytest.raises(_LookupReached):
plugins_initializer.configure_plugin_services()
assert order == ["factory", "provider", "lookup"]
assert order == ["catalog", "factory", "provider"]
def test_plugin_manager_does_not_reach_raw_system_adapters() -> None:

View File

@@ -89,11 +89,6 @@ def test_init_plugins_starts_monitor_after_runtime_and_routes(monkeypatch) -> No
manager.start_monitor.side_effect = lambda **_kwargs: order.append("monitor")
manager.get_local_repo_plugins.return_value = []
manager.get_plugin_source_id.side_effect = lambda plugin_id: plugin_id
monkeypatch.setattr(
plugins_initializer,
"configure_plugin_services",
lambda: order.append("services"),
)
monkeypatch.setattr(plugins_initializer, "PluginManager", lambda: manager)
monkeypatch.setattr(
plugins_initializer,
@@ -104,7 +99,6 @@ def test_init_plugins_starts_monitor_after_runtime_and_routes(monkeypatch) -> No
plugins_initializer.init_plugins()
assert order == [
"services",
"reopen",
"plugin:ReadyPlugin",
"routes",