mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-09-17 23:51:08 +00:00
* refactor(storage): better fs check * fix(storage): fs check inconsistencies * test(storage): more thorough testing
155 lines
5.2 KiB
TypeScript
155 lines
5.2 KiB
TypeScript
import * as vscode from "vscode"
|
|
|
|
vi.mock("fs/promises", async () => {
|
|
const mod = await import("../../__mocks__/fs/promises")
|
|
return (mod as any).default ?? mod
|
|
})
|
|
|
|
describe("getStorageBasePath - customStoragePath", () => {
|
|
const defaultPath = "/test/global-storage"
|
|
|
|
beforeEach(() => {
|
|
vi.clearAllMocks()
|
|
})
|
|
|
|
afterEach(() => {
|
|
vi.restoreAllMocks()
|
|
})
|
|
|
|
it("returns the configured custom path when it is writable", async () => {
|
|
const customPath = "/test/storage/path"
|
|
vi.spyOn(vscode.workspace, "getConfiguration").mockReturnValue({
|
|
get: vi.fn().mockReturnValue(customPath),
|
|
} as any)
|
|
|
|
const fsPromises = await import("fs/promises")
|
|
const { getStorageBasePath } = await import("../storage")
|
|
|
|
const result = await getStorageBasePath(defaultPath)
|
|
|
|
expect(result).toBe(customPath)
|
|
expect((fsPromises as any).mkdir).toHaveBeenCalledWith(customPath, { recursive: true })
|
|
expect((fsPromises as any).access).toHaveBeenCalledWith(customPath, 7) // 7 = R_OK(4) | W_OK(2) | X_OK(1)
|
|
})
|
|
|
|
it("falls back to default and shows an error when custom path is not writable", async () => {
|
|
const customPath = "/test/storage/unwritable"
|
|
|
|
vi.spyOn(vscode.workspace, "getConfiguration").mockReturnValue({
|
|
get: vi.fn().mockReturnValue(customPath),
|
|
} as any)
|
|
|
|
const showErrorSpy = vi.spyOn(vscode.window, "showErrorMessage").mockResolvedValue(undefined as any)
|
|
|
|
const fsPromises = await import("fs/promises")
|
|
const { getStorageBasePath } = await import("../storage")
|
|
|
|
await (fsPromises as any).mkdir(customPath, { recursive: true })
|
|
|
|
const accessMock = (fsPromises as any).access as ReturnType<typeof vi.fn>
|
|
accessMock.mockImplementationOnce(async (p: string) => {
|
|
if (p === customPath) {
|
|
const err: any = new Error("EACCES: permission denied")
|
|
err.code = "EACCES"
|
|
throw err
|
|
}
|
|
return Promise.resolve()
|
|
})
|
|
|
|
const result = await getStorageBasePath(defaultPath)
|
|
|
|
expect(result).toBe(defaultPath)
|
|
expect(showErrorSpy).toHaveBeenCalledTimes(1)
|
|
const firstArg = showErrorSpy.mock.calls[0][0]
|
|
expect(typeof firstArg).toBe("string")
|
|
})
|
|
it("returns the default path when customStoragePath is an empty string and does not touch fs", async () => {
|
|
vi.spyOn(vscode.workspace, "getConfiguration").mockReturnValue({
|
|
get: vi.fn().mockReturnValue(""),
|
|
} as any)
|
|
|
|
const fsPromises = await import("fs/promises")
|
|
const { getStorageBasePath } = await import("../storage")
|
|
|
|
const result = await getStorageBasePath(defaultPath)
|
|
|
|
expect(result).toBe(defaultPath)
|
|
expect((fsPromises as any).mkdir).not.toHaveBeenCalled()
|
|
expect((fsPromises as any).access).not.toHaveBeenCalled()
|
|
})
|
|
|
|
it("falls back to default when mkdir fails and does not attempt access", async () => {
|
|
const customPath = "/test/storage/failmkdir"
|
|
|
|
vi.spyOn(vscode.workspace, "getConfiguration").mockReturnValue({
|
|
get: vi.fn().mockReturnValue(customPath),
|
|
} as any)
|
|
|
|
const showErrorSpy = vi.spyOn(vscode.window, "showErrorMessage").mockResolvedValue(undefined as any)
|
|
|
|
const fsPromises = await import("fs/promises")
|
|
const { getStorageBasePath } = await import("../storage")
|
|
|
|
const mkdirMock = (fsPromises as any).mkdir as ReturnType<typeof vi.fn>
|
|
mkdirMock.mockImplementationOnce(async (p: string) => {
|
|
if (p === customPath) {
|
|
const err: any = new Error("EACCES: permission denied")
|
|
err.code = "EACCES"
|
|
throw err
|
|
}
|
|
return Promise.resolve()
|
|
})
|
|
|
|
const result = await getStorageBasePath(defaultPath)
|
|
|
|
expect(result).toBe(defaultPath)
|
|
expect((fsPromises as any).access).not.toHaveBeenCalled()
|
|
expect(showErrorSpy).toHaveBeenCalledTimes(1)
|
|
})
|
|
|
|
it("passes the correct permission flags (R_OK | W_OK | X_OK) to fs.access", async () => {
|
|
const customPath = "/test/storage/path"
|
|
vi.spyOn(vscode.workspace, "getConfiguration").mockReturnValue({
|
|
get: vi.fn().mockReturnValue(customPath),
|
|
} as any)
|
|
|
|
const fsPromises = await import("fs/promises")
|
|
const { getStorageBasePath } = await import("../storage")
|
|
|
|
await getStorageBasePath(defaultPath)
|
|
|
|
const constants = (fsPromises as any).constants
|
|
const expectedFlags = constants.R_OK | constants.W_OK | constants.X_OK
|
|
|
|
expect((fsPromises as any).access).toHaveBeenCalledWith(customPath, expectedFlags)
|
|
})
|
|
|
|
it("falls back when directory is readable but not writable (partial permissions)", async () => {
|
|
const customPath = "/test/storage/readonly"
|
|
vi.spyOn(vscode.workspace, "getConfiguration").mockReturnValue({
|
|
get: vi.fn().mockReturnValue(customPath),
|
|
} as any)
|
|
|
|
const showErrorSpy = vi.spyOn(vscode.window, "showErrorMessage").mockResolvedValue(undefined as any)
|
|
|
|
const fsPromises = await import("fs/promises")
|
|
const { getStorageBasePath } = await import("../storage")
|
|
|
|
const accessMock = (fsPromises as any).access as ReturnType<typeof vi.fn>
|
|
const constants = (fsPromises as any).constants
|
|
accessMock.mockImplementationOnce(async (p: string, mode?: number) => {
|
|
// Simulate readable (R_OK) but not writable/executable (W_OK | X_OK)
|
|
if (p === customPath && mode && mode & (constants.W_OK | constants.X_OK)) {
|
|
const err: any = new Error("EACCES: permission denied")
|
|
err.code = "EACCES"
|
|
throw err
|
|
}
|
|
return Promise.resolve()
|
|
})
|
|
|
|
const result = await getStorageBasePath(defaultPath)
|
|
|
|
expect(result).toBe(defaultPath)
|
|
expect(showErrorSpy).toHaveBeenCalledTimes(1)
|
|
})
|
|
})
|