From 93e907d03286078a27e3daf6a7ace7315df2f936 Mon Sep 17 00:00:00 2001 From: jxxghp Date: Sun, 5 Jul 2026 17:24:44 +0800 Subject: [PATCH] fix: remove openlist directory via fs remove --- app/modules/filemanager/storages/alist.py | 51 ++++------------------- tests/test_alist_delete.py | 41 ++++++++++++++++++ 2 files changed, 50 insertions(+), 42 deletions(-) create mode 100644 tests/test_alist_delete.py diff --git a/app/modules/filemanager/storages/alist.py b/app/modules/filemanager/storages/alist.py index 504f68b7..f04158a7 100644 --- a/app/modules/filemanager/storages/alist.py +++ b/app/modules/filemanager/storages/alist.py @@ -479,57 +479,24 @@ class Alist(StorageBase, metaclass=WeakSingleton): """ return self.get_folder(Path(fileitem.path).parent) - def __is_empty_dir(self, fileitem: schemas.FileItem) -> bool: - """ - 判断目录是否为空 - - :param fileitem: 文件项 - :return: 是否为空目录 - """ - if fileitem.type != "dir": - return False - # 获取目录内容 - items = self.list(fileitem) - return len(items) == 0 - def delete(self, fileitem: schemas.FileItem) -> bool: """ - 删除文件或目录,空目录用专用API + 删除文件或目录 :param fileitem: 文件项 :return: 是否删除成功 """ - # 如果是空目录,优先用 remove_empty_directory - if fileitem.type == "dir" and self.__is_empty_dir(fileitem): - resp = RequestUtils(headers=self.__get_header_with_token()).post_res( - self.__get_api_url("/api/fs/remove_empty_directory"), - json={ - "src_dir": fileitem.path, - }, - ) - if resp is None: - logger.warn( - f"【OpenList】请求删除空目录 {fileitem.path} 失败,无法连接alist服务" - ) - return False - if resp.status_code != 200: - logger.warn( - f"【OpenList】请求删除空目录 {fileitem.path} 失败,状态码:{resp.status_code}" - ) - return False - result = resp.json() - if result["code"] != 200: - logger.warn( - f"【OpenList】删除空目录 {fileitem.path} 失败,错误信息:{result['message']}" - ) - return False - return True - # 其它情况(文件或非空目录) + path = Path(fileitem.path) + name = fileitem.name or path.name + if not name: + logger.warn(f"【OpenList】删除路径 {fileitem.path} 无效") + return False + resp = RequestUtils(headers=self.__get_header_with_token()).post_res( self.__get_api_url("/api/fs/remove"), json={ - "dir": Path(fileitem.path).parent.as_posix(), - "names": [fileitem.name], + "dir": path.parent.as_posix(), + "names": [name], }, ) if resp is None: diff --git a/tests/test_alist_delete.py b/tests/test_alist_delete.py new file mode 100644 index 00000000..738607d0 --- /dev/null +++ b/tests/test_alist_delete.py @@ -0,0 +1,41 @@ +from unittest.mock import MagicMock, patch + +from app.modules.filemanager.storages import alist as alist_module +from app.modules.filemanager.storages.alist import Alist +from app.schemas import FileItem + + +def test_delete_directory_uses_remove_api_without_empty_directory_probe(): + """ + 删除 OpenList 目录时应直接使用通用删除接口,避免专用空目录接口返回成功但未实际删除。 + """ + storage = Alist() + response = MagicMock() + response.status_code = 200 + response.json.return_value = {"code": 200, "message": "success", "data": None} + request_utils = MagicMock() + request_utils.post_res.return_value = response + fileitem = FileItem(storage="alist", type="dir", path="/library/empty/") + + with patch.object( + Alist, + "get_conf", + return_value={"url": "http://openlist.test", "token": "token"}, + ): + with patch.object(storage, "_Alist__get_header_with_token", return_value={}): + with patch.object(alist_module, "RequestUtils", return_value=request_utils): + with patch.object( + storage, + "list", + side_effect=AssertionError("不应探测空目录"), + ): + assert storage.delete(fileitem) is True + + request_utils.post_res.assert_called_once() + called_url = request_utils.post_res.call_args.args[0] + assert called_url == "http://openlist.test/api/fs/remove" + assert "remove_empty_directory" not in called_url + assert request_utils.post_res.call_args.kwargs["json"] == { + "dir": "/library", + "names": ["empty"], + }