mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-08-19 14:04:17 +08:00
fix(storage): 修复通用管理契约下存储页报未知错误
- usage 动作返回的 StorageUsage pydantic 模型无法透过 Response[Dict[str, Any]] 开放映射校验导致 500,统一转为 dict - support_transtype 返回值恢复 transtype 包装结构,与前端期望一致 - 空结果(无用量/无整理方式)恢复成功空结构语义, 不再因 bool(结果) 被前端客户端当作业务失败弹窗 - dashboard 用量聚合改为 dict 取值;新增响应校验与空结果回归测试
This commit is contained in:
@@ -69,8 +69,8 @@ def _build_storage() -> schemas.Storage:
|
||||
_result = StorageChain().manage_storage(storage=_storage, action=StorageAction.USAGE.value)
|
||||
_usage = _result.get("data") if _result.get("success") else None
|
||||
if _usage:
|
||||
total += _usage.total
|
||||
available += _usage.available
|
||||
total += _usage.get("total") or 0
|
||||
available += _usage.get("available") or 0
|
||||
return schemas.Storage(total_storage=total, used_storage=total - available)
|
||||
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ from app.runtime.log import logger
|
||||
from app.modules import _ModuleBase
|
||||
from app.modules.filemanager.storages import StorageBase
|
||||
from app.modules.filemanager.transhandler import TransHandler
|
||||
from app.schemas import TransferInfo, ExistMediaInfo, TmdbEpisode, TransferDirectoryConf, FileItem
|
||||
from app.schemas import TransferInfo, ExistMediaInfo, TmdbEpisode, TransferDirectoryConf, FileItem, StorageUsage
|
||||
from app.schemas.types import MUSIC_ENTITY_ALBUM, MediaType, ModuleType, OtherModulesType, StorageAction
|
||||
from app.adapters.system.host import SystemUtils
|
||||
from app.foundation import text as text_tools
|
||||
@@ -152,14 +152,14 @@ class FileManagerModule(_ModuleBase):
|
||||
storage_oper = self.__get_storage_oper(storage)
|
||||
if not storage_oper:
|
||||
return {"success": False, "message": f"不支持 {storage} 的整理方式获取"}
|
||||
transtype = storage_oper.support_transtype()
|
||||
return {"success": bool(transtype), "data": transtype}
|
||||
# 与旧契约一致:返回值包装为 transtype,空结果同样返回成功空结构
|
||||
return {"success": True, "data": {"transtype": storage_oper.support_transtype() or {}}}
|
||||
if action == StorageAction.USAGE:
|
||||
storage_oper = self.__get_storage_oper(storage)
|
||||
if not storage_oper:
|
||||
return {"success": False, "message": f"不支持 {storage} 的存储使用情况"}
|
||||
usage = storage_oper.usage()
|
||||
return {"success": bool(usage), "data": usage}
|
||||
# 实现返回 pydantic 模型,转为 dict 后才能透过通用响应的开放映射校验
|
||||
return {"success": True, "data": (storage_oper.usage() or StorageUsage()).model_dump()}
|
||||
|
||||
# 登录类动作:存储实现不支持时返回失败信息
|
||||
oper_method = action.value
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
3. 端点层的 ManageRequest 通用请求结构:target + action + params
|
||||
"""
|
||||
from types import SimpleNamespace
|
||||
from typing import Any, Dict
|
||||
|
||||
import pytest
|
||||
|
||||
@@ -105,18 +106,46 @@ def test_storage_manage_save_config_passes_conf_through(module):
|
||||
|
||||
|
||||
def test_storage_manage_usage_returns_oper_data(module):
|
||||
"""usage 动作返回存储实现的用量数据。"""
|
||||
"""usage 动作返回纯 dict 用量数据,空结果同样成功。"""
|
||||
result = module.storage_manage(storage="fakestore", action="usage")
|
||||
assert result["success"] is True
|
||||
assert result["data"].total == 100
|
||||
assert result["data"].available == 40
|
||||
assert result["data"] == {"total": 100.0, "available": 40.0}
|
||||
|
||||
|
||||
def test_storage_manage_usage_data_passes_open_mapping_response(module):
|
||||
"""用量数据必须能透过通用响应 Response[Dict[str, Any]] 的开放映射校验。
|
||||
|
||||
回归守护:存储实现返回 pydantic 模型时若未转 dict,
|
||||
端点响应校验会直接 500,前端存储页整体报未知错误。
|
||||
"""
|
||||
result = module.storage_manage(storage="fakestore", action="usage")
|
||||
response = schemas.Response[Dict[str, Any]](
|
||||
success=result["success"], message=result.get("message"), data=result["data"]
|
||||
)
|
||||
assert response.data == {"total": 100.0, "available": 40.0}
|
||||
|
||||
|
||||
def test_storage_manage_usage_defaults_when_oper_returns_none(module, monkeypatch):
|
||||
"""存储实现查不到用量时返回成功的默认空结构而非业务失败。"""
|
||||
monkeypatch.setattr(_FakeStorageOper, "usage", lambda self: None)
|
||||
result = module.storage_manage(storage="fakestore", action="usage")
|
||||
assert result["success"] is True
|
||||
assert result["data"] == {"total": 0.0, "available": 0.0}
|
||||
|
||||
|
||||
def test_storage_manage_support_transtype(module):
|
||||
"""support_transtype 动作返回存储支持的整理方式。"""
|
||||
"""support_transtype 动作返回值包装为 transtype,与旧契约结构一致。"""
|
||||
result = module.storage_manage(storage="fakestore", action="support_transtype")
|
||||
assert result["success"] is True
|
||||
assert result["data"] == {"move": True}
|
||||
assert result["data"] == {"transtype": {"move": True}}
|
||||
|
||||
|
||||
def test_storage_manage_support_transtype_empty_still_succeeds(module, monkeypatch):
|
||||
"""存储不支持任何整理方式时返回成功空结构,避免前端把空结果当业务失败。"""
|
||||
monkeypatch.setattr(_FakeStorageOper, "support_transtype", lambda self: {})
|
||||
result = module.storage_manage(storage="fakestore", action="support_transtype")
|
||||
assert result["success"] is True
|
||||
assert result["data"] == {"transtype": {}}
|
||||
|
||||
|
||||
def test_storage_manage_login_action_forwards_params(module):
|
||||
|
||||
Reference in New Issue
Block a user