diff --git a/packages/filesystem/webdav/webdav.test.ts b/packages/filesystem/webdav/webdav.test.ts index a0098baa9..90d3f9d81 100644 --- a/packages/filesystem/webdav/webdav.test.ts +++ b/packages/filesystem/webdav/webdav.test.ts @@ -70,24 +70,19 @@ describe("WebDAVFileSystem", () => { }); describe("verify", () => { - it("应当通过列目录、写入探针文件和清理探针完成验证", async () => { + it("应当通过 getQuota 与列目录完成只读校验", async () => { const fs = createTestFS(mockClient); await expect(fs.verify()).resolves.toBeUndefined(); expect(mockClient.getQuota).toHaveBeenCalled(); expect(mockClient.getDirectoryContents).toHaveBeenCalledWith("/"); - expect(mockClient.createDirectory).toHaveBeenCalledWith(expect.stringMatching(/^\/\.scriptcat-verify-/)); - expect(mockClient.putFileContents).toHaveBeenCalledWith( - expect.stringMatching(/^\/\.scriptcat-verify-.+\/probe\.txt$/), - "" - ); - expect(mockClient.deleteFile).toHaveBeenCalledWith( - expect.stringMatching(/^\/\.scriptcat-verify-.+\/probe\.txt$/) - ); - expect(mockClient.deleteFile).toHaveBeenCalledWith(expect.stringMatching(/^\/\.scriptcat-verify-/)); + // 不应在 verify 阶段尝试写探针(坚果云等根目录不可写的服务会被误杀) + expect(mockClient.createDirectory).not.toHaveBeenCalled(); + expect(mockClient.putFileContents).not.toHaveBeenCalled(); + expect(mockClient.deleteFile).not.toHaveBeenCalled(); }); - it("应当在 401 时抛出 WarpTokenError 1", async () => { + it("应当在 getQuota 401 时抛出 WarpTokenError", async () => { (mockClient.getQuota as ReturnType).mockRejectedValue({ response: { status: 401 }, message: "Unauthorized", @@ -97,7 +92,7 @@ describe("WebDAVFileSystem", () => { await expect(fs.verify()).rejects.toBeInstanceOf(WarpTokenError); }); - it("应当在 401 时抛出 WarpTokenError 2", async () => { + it("应当在 getDirectoryContents 401 时抛出 WarpTokenError", async () => { (mockClient.getDirectoryContents as ReturnType).mockRejectedValue({ response: { status: 401 }, message: "Unauthorized", @@ -107,7 +102,7 @@ describe("WebDAVFileSystem", () => { await expect(fs.verify()).rejects.toBeInstanceOf(WarpTokenError); }); - it("应当在其他错误时抛出包含原始信息的 Error 1", async () => { + it("应当在 getQuota 其他错误时抛出包含原始信息的 Error", async () => { (mockClient.getQuota as ReturnType).mockRejectedValue({ message: "Network error", }); @@ -116,7 +111,7 @@ describe("WebDAVFileSystem", () => { await expect(fs.verify()).rejects.toThrow("WebDAV verify failed: Network error"); }); - it("应当在其他错误时抛出包含原始信息的 Error 2", async () => { + it("应当在 getDirectoryContents 其他错误时抛出包含原始信息的 Error", async () => { (mockClient.getDirectoryContents as ReturnType).mockRejectedValue({ message: "Network error", }); @@ -124,21 +119,6 @@ describe("WebDAVFileSystem", () => { await expect(fs.verify()).rejects.toThrow("WebDAV verify failed: Network error"); }); - - it("应当在无法写入探针文件时验证失败并清理探针目录", async () => { - (mockClient.putFileContents as ReturnType).mockResolvedValue(false); - const fs = createTestFS(mockClient); - - await expect(fs.verify()).rejects.toThrow("WebDAV verify failed: probe file write returned false"); - expect(mockClient.deleteFile).toHaveBeenCalledWith(expect.stringMatching(/^\/\.scriptcat-verify-/)); - }); - - it("应当在删除探针文件失败时验证失败", async () => { - (mockClient.deleteFile as ReturnType).mockRejectedValueOnce(new Error("Delete denied")); - const fs = createTestFS(mockClient); - - await expect(fs.verify()).rejects.toThrow("WebDAV verify failed: Delete denied"); - }); }); describe("openDir", () => { diff --git a/packages/filesystem/webdav/webdav.ts b/packages/filesystem/webdav/webdav.ts index 60b1be77a..871097537 100644 --- a/packages/filesystem/webdav/webdav.ts +++ b/packages/filesystem/webdav/webdav.ts @@ -53,27 +53,13 @@ export default class WebDAVFileSystem implements FileSystem { } async verify(): Promise { - const verifyDir = joinPath(this.basePath, `.scriptcat-verify-${Date.now()}-${Math.random().toString(36).slice(2)}`); - const verifyFile = joinPath(verifyDir, "probe.txt"); - let dirCreated = false; - let fileCreated = false; - + // 只做只读校验:凭据 + URL 可达性。 + // 写权限不在此处探测——不同 basePath 写策略不同(坚果云等根目录不可写的服务会被误杀,见 #1444), + // 真正的写操作会在 backupToCloud / buildFileSystem 中由 createDir 立即触发并报错。 try { await this.client.getQuota(); await this.client.getDirectoryContents(this.basePath); - await this.client.createDirectory(verifyDir); - dirCreated = true; - const written = await this.client.putFileContents(verifyFile, ""); - if (!written) { - throw new Error("probe file write returned false"); - } - fileCreated = true; - await this.client.deleteFile(verifyFile); - fileCreated = false; - await this.client.deleteFile(verifyDir); - dirCreated = false; } catch (e: any) { - await this.cleanupVerifyProbe(verifyFile, verifyDir, fileCreated, dirCreated); if (e.response?.status === 401) { throw new WarpTokenError(e); } @@ -81,15 +67,6 @@ export default class WebDAVFileSystem implements FileSystem { } } - private async cleanupVerifyProbe(verifyFile: string, verifyDir: string, fileCreated: boolean, dirCreated: boolean) { - if (fileCreated) { - await this.client.deleteFile(verifyFile).catch(() => undefined); - } - if (dirCreated) { - await this.client.deleteFile(verifyDir).catch(() => undefined); - } - } - async open(file: FileInfo): Promise { return new WebDAVFileReader(this.client, joinPath(file.path, file.name)); }