Skip to content

Commit ba92646

Browse files
committed
refactor(storage): better fs check
1 parent 185365a commit ba92646

File tree

2 files changed

+70
-4
lines changed

2 files changed

+70
-4
lines changed
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import * as vscode from "vscode"
2+
3+
vi.mock("fs/promises", async () => {
4+
const mod = await import("../../__mocks__/fs/promises")
5+
return (mod as any).default ?? mod
6+
})
7+
8+
describe("getStorageBasePath - customStoragePath", () => {
9+
const defaultPath = "/test/global-storage"
10+
11+
beforeEach(() => {
12+
vi.clearAllMocks()
13+
})
14+
15+
afterEach(() => {
16+
vi.restoreAllMocks()
17+
})
18+
19+
it("returns the configured custom path when it is writable", async () => {
20+
const customPath = "/test/storage/path"
21+
vi.spyOn(vscode.workspace, "getConfiguration").mockReturnValue({
22+
get: vi.fn().mockReturnValue(customPath),
23+
} as any)
24+
25+
const fsPromises = await import("fs/promises")
26+
const { getStorageBasePath } = await import("../storage")
27+
28+
const result = await getStorageBasePath(defaultPath)
29+
30+
expect(result).toBe(customPath)
31+
expect((fsPromises as any).mkdir).toHaveBeenCalledWith(customPath, { recursive: true })
32+
expect((fsPromises as any).access).toHaveBeenCalledWith(customPath, expect.any(Number))
33+
})
34+
35+
it("falls back to default and shows an error when custom path is not writable", async () => {
36+
const customPath = "/test/storage/unwritable"
37+
38+
vi.spyOn(vscode.workspace, "getConfiguration").mockReturnValue({
39+
get: vi.fn().mockReturnValue(customPath),
40+
} as any)
41+
42+
const showErrorSpy = vi.spyOn(vscode.window, "showErrorMessage").mockResolvedValue(undefined as any)
43+
44+
const fsPromises = await import("fs/promises")
45+
const { getStorageBasePath } = await import("../storage")
46+
47+
await (fsPromises as any).mkdir(customPath, { recursive: true })
48+
49+
const accessMock = (fsPromises as any).access as ReturnType<typeof vi.fn>
50+
accessMock.mockImplementationOnce(async (p: string) => {
51+
if (p === customPath) {
52+
const err: any = new Error("EACCES: permission denied")
53+
err.code = "EACCES"
54+
throw err
55+
}
56+
return Promise.resolve()
57+
})
58+
59+
const result = await getStorageBasePath(defaultPath)
60+
61+
expect(result).toBe(defaultPath)
62+
expect(showErrorSpy).toHaveBeenCalledTimes(1)
63+
const firstArg = showErrorSpy.mock.calls[0][0]
64+
expect(typeof firstArg).toBe("string")
65+
})
66+
})

src/utils/storage.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import * as vscode from "vscode"
22
import * as path from "path"
33
import * as fs from "fs/promises"
4+
import { constants as fsConstants } from "fs"
45

56
import { Package } from "../shared/package"
67
import { t } from "../i18n"
@@ -32,10 +33,8 @@ export async function getStorageBasePath(defaultPath: string): Promise<string> {
3233
// Ensure custom path exists
3334
await fs.mkdir(customStoragePath, { recursive: true })
3435

35-
// Test if path is writable
36-
const testFile = path.join(customStoragePath, ".write_test")
37-
await fs.writeFile(testFile, "test")
38-
await fs.rm(testFile)
36+
// Check directory write permission without creating temp files
37+
await fs.access(customStoragePath, fsConstants.R_OK | fsConstants.W_OK | fsConstants.X_OK)
3938

4039
return customStoragePath
4140
} catch (error) {
@@ -132,6 +131,7 @@ export async function promptForCustomStoragePath(): Promise<void> {
132131
try {
133132
// Test if path is accessible
134133
await fs.mkdir(result, { recursive: true })
134+
await fs.access(result, fsConstants.W_OK | fsConstants.X_OK)
135135
vscode.window.showInformationMessage(t("common:info.custom_storage_path_set", { path: result }))
136136
} catch (error) {
137137
vscode.window.showErrorMessage(

0 commit comments

Comments
 (0)