fix(plugin): preserve market package generation

This commit is contained in:
jxxghp
2026-08-27 22:46:03 +08:00
parent cb139ee20d
commit 988b425510
3 changed files with 48 additions and 6 deletions
@@ -86,6 +86,7 @@ class PluginMetadataMapper:
plugin.history = info.get("history") or {} plugin.history = info.get("history") or {}
plugin.release = bool(info.get("release")) plugin.release = bool(info.get("release"))
plugin.repo_url = market plugin.repo_url = market
plugin.package_version = package_version
plugin.is_local = False plugin.is_local = False
plugin.add_time = add_time plugin.add_time = add_time
return plugin return plugin
+14 -2
View File
@@ -1,7 +1,7 @@
from enum import Enum as _Enum from enum import Enum as _Enum
from typing import Literal, Optional, List, Dict, Union from typing import Dict, List, Literal, Optional, Union
from pydantic import BaseModel, Field, RootModel, field_validator from pydantic import BaseModel, Field, PrivateAttr, RootModel, field_validator
from app.schemas.common import JsonData from app.schemas.common import JsonData
@@ -62,6 +62,8 @@ class Plugin(BaseModel):
""" """
插件信息 插件信息
""" """
_package_version: Optional[str] = PrivateAttr(default=None)
id: str = None id: str = None
# 插件名称 # 插件名称
plugin_name: Optional[str] = None plugin_name: Optional[str] = None
@@ -124,6 +126,16 @@ class Plugin(BaseModel):
# 实例实现模式;存量物理分身为空 # 实例实现模式;存量物理分身为空
instance_mode: Optional[str] = None instance_mode: Optional[str] = None
@property
def package_version(self) -> Optional[str]:
"""读取仅供宿主内部候选选择使用的插件包代际。"""
return self._package_version
@package_version.setter
def package_version(self, value: Optional[str]) -> None:
"""保存插件包代际,但不把它暴露到 API 响应模型。"""
self._package_version = value
class PluginRuntimeSummary(BaseModel): class PluginRuntimeSummary(BaseModel):
"""插件后台收敛状态和前端刷新代次。""" """插件后台收敛状态和前端刷新代次。"""
+33 -4
View File
@@ -1,11 +1,12 @@
import asyncio import asyncio
from types import SimpleNamespace
from unittest.mock import Mock from unittest.mock import Mock
import pytest import pytest
from packaging.version import Version from packaging.version import Version
from app.application.plugin.catalog import PluginCatalogService from app.application.plugin.catalog import PluginCatalogService
from app.runtime.extensions.plugin.metadata import PluginMetadataMapper
from app.schemas.plugin import Plugin
def _plugin( def _plugin(
@@ -13,14 +14,15 @@ def _plugin(
version: str, version: str,
repo_url: str, repo_url: str,
package_version: str | None = "v3", package_version: str | None = "v3",
): ) -> Plugin:
"""构造目录合并测试使用的最小插件 DTO。""" """构造目录合并测试使用的最小插件 DTO。"""
return SimpleNamespace( plugin = Plugin(
id=plugin_id, id=plugin_id,
plugin_version=version, plugin_version=version,
repo_url=repo_url, repo_url=repo_url,
package_version=package_version,
) )
plugin.package_version = package_version
return plugin
def _service(**overrides) -> PluginCatalogService: def _service(**overrides) -> PluginCatalogService:
@@ -132,6 +134,33 @@ def test_load_maps_market_entries_with_installed_snapshot():
assert mapper.call_args_list[1].args[3:] == (["Installed"], 1, "v3") assert mapper.call_args_list[1].args[3:] == (["Installed"], 1, "v3")
def test_metadata_mapper_preserves_package_generation_without_serializing_it():
"""市场映射必须保留候选代际,同时不能扩张插件 API 响应。"""
mapper = PluginMetadataMapper(
plugin_instance=lambda _plugin_id: None,
plugin_class=lambda _plugin_id: None,
annotate_system_version=lambda info: info,
is_package_compatible=lambda _info, _version: True,
auth_checker=lambda _plugin, _source: True,
version_compare=lambda _left, _operator, _right: False,
log=Mock(),
)
plugin = mapper.map(
plugin_id="Demo",
plugin_info={"name": "Demo", "version": "3.0.0"},
market="https://market-a",
installed_plugins=[],
add_time=1,
package_version="v3",
)
assert plugin is not None
assert plugin.package_version == "v3"
assert "package_version" not in plugin.model_dump()
assert "package_version" not in Plugin.model_json_schema()["properties"]
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_async_collect_isolates_failure_and_completes_progress(): async def test_async_collect_isolates_failure_and_completes_progress():
"""异步市场单任务失败时保留成功结果,并把进度推进到完成态。""" """异步市场单任务失败时保留成功结果,并把进度推进到完成态。"""