diff --git a/packages/filesystem/googledrive/googledrive.test.ts b/packages/filesystem/googledrive/googledrive.test.ts index a6e099bc6..ccd276a92 100644 --- a/packages/filesystem/googledrive/googledrive.test.ts +++ b/packages/filesystem/googledrive/googledrive.test.ts @@ -25,4 +25,38 @@ describe("GoogleDriveFileSystem", () => { await expect(fs.delete("missing.txt")).resolves.toBeUndefined(); }); + + it("ensureDirExists should create missing nested directories and return final id", async () => { + const fs = new GoogleDriveFileSystem("/", "token"); + const findSpy = vi.spyOn(fs, "findFolderByName").mockResolvedValue(null); + const createSpy = vi + .spyOn(fs, "createFolder") + .mockResolvedValueOnce({ id: "id-A", name: "A" }) + .mockResolvedValueOnce({ id: "id-B", name: "B" }); + + await expect(fs.ensureDirExists("/A/B")).resolves.toBe("id-B"); + + expect(findSpy.mock.calls).toEqual([ + ["A", "appDataFolder"], + ["B", "id-A"], + ]); + expect(createSpy.mock.calls).toEqual([ + ["A", "appDataFolder"], + ["B", "id-A"], + ]); + }); + + it("writer should ensure directory from root when filesystem is opened in subdir", async () => { + const fs = new GoogleDriveFileSystem("/Base", "token"); + const writer = await fs.create("file.txt"); + const ensureSpy = vi.spyOn(fs, "ensureDirExists").mockResolvedValue("base-id"); + const findSpy = vi.spyOn(fs, "findFileInDirectory").mockResolvedValue(null); + const requestSpy = vi.spyOn(fs, "request").mockResolvedValue({}); + + await expect(writer.write("content")).resolves.toBeUndefined(); + + expect(ensureSpy).toHaveBeenCalledWith("/Base"); + expect(findSpy).toHaveBeenCalledWith("file.txt", "base-id"); + expect(requestSpy).toHaveBeenCalledTimes(1); + }); }); diff --git a/packages/filesystem/googledrive/googledrive.ts b/packages/filesystem/googledrive/googledrive.ts index fb54786e2..fc3821787 100644 --- a/packages/filesystem/googledrive/googledrive.ts +++ b/packages/filesystem/googledrive/googledrive.ts @@ -40,6 +40,14 @@ export default class GoogleDriveFileSystem implements FileSystem { } const fullPath = joinPath(this.path, dir); + await this.ensureDirPath(fullPath); + } + + private async ensureDirPath(fullPath: string): Promise { + if (fullPath === "/" || fullPath === "") { + return "appDataFolder"; + } + const dirs = fullPath.split("/").filter(Boolean); // 从根目录开始逐级创建目录 @@ -69,7 +77,7 @@ export default class GoogleDriveFileSystem implements FileSystem { parentId = folderId; } - return Promise.resolve(); + return parentId; } async findFolderByName(name: string, parentId: string): Promise<{ id: string; name: string } | null> { const query = `name='${name}' and mimeType='application/vnd.google-apps.folder' and '${parentId}' in parents and trashed=false`; @@ -315,24 +323,6 @@ export default class GoogleDriveFileSystem implements FileSystem { // 确保目录存在并返回目录ID,优化Writer避免重复获取 async ensureDirExists(dirPath: string): Promise { - if (dirPath === "/" || dirPath === "") { - return "appDataFolder"; - } - - // 先检查缓存 - const cachedId = this.pathToIdCache.get(dirPath); - if (cachedId) { - return cachedId; - } - - // 如果没有缓存,使用getFileId方法 - const foundId = await this.getFileId(dirPath); - if (!foundId) { - throw new Error(`Failed to create or find directory: ${dirPath}`); - } - - // 缓存结果 - this.pathToIdCache.set(dirPath, foundId); - return foundId; + return this.ensureDirPath(dirPath); } }