Skip to content

Commit 6f3f6eb

Browse files
committed
fix: eliminate getStorageBasePath race condition and vscode popup
1 parent a8aea14 commit 6f3f6eb

File tree

2 files changed

+75
-2
lines changed

2 files changed

+75
-2
lines changed
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import { describe, it, expect, beforeEach, afterEach, vi } from "vitest"
2+
import * as fs from "fs/promises"
3+
import * as path from "path"
4+
import * as os from "os"
5+
import { getStorageBasePath } from "../storage"
6+
7+
// Mock VSCode
8+
vi.mock("vscode", () => ({
9+
workspace: {
10+
getConfiguration: vi.fn(),
11+
},
12+
window: {
13+
showErrorMessage: vi.fn(),
14+
},
15+
}))
16+
17+
// Mock i18n
18+
vi.mock("../../i18n", () => ({
19+
t: vi.fn((key: string) => key),
20+
}))
21+
22+
describe("getStorageBasePath", () => {
23+
let tempDir: string
24+
25+
beforeEach(async () => {
26+
tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "storage-test-"))
27+
vi.clearAllMocks()
28+
})
29+
30+
afterEach(async () => {
31+
await fs.rm(tempDir, { recursive: true, force: true })
32+
vi.restoreAllMocks()
33+
})
34+
35+
it("should handle concurrent storage validations without race conditions", async () => {
36+
const customPath = path.join(tempDir, "custom-storage")
37+
const defaultPath = path.join(tempDir, "default-storage")
38+
39+
// Mock VSCode configuration to return custom path
40+
const mockConfig = {
41+
get: vi.fn().mockReturnValue(customPath),
42+
has: vi.fn().mockReturnValue(true),
43+
inspect: vi.fn(),
44+
update: vi.fn(),
45+
} as any
46+
47+
const vscode = await import("vscode")
48+
vi.mocked(vscode.workspace.getConfiguration).mockReturnValue(mockConfig)
49+
50+
// Create the custom storage directory
51+
await fs.mkdir(customPath, { recursive: true })
52+
53+
// Run multiple concurrent storage validations
54+
const concurrentCalls = Array(10)
55+
.fill(null)
56+
.map(() => getStorageBasePath(defaultPath))
57+
58+
// All should succeed and return the custom path
59+
const results = await Promise.all(concurrentCalls)
60+
61+
// Verify all calls succeeded
62+
expect(results).toHaveLength(10)
63+
results.forEach((result) => {
64+
expect(result).toBe(customPath)
65+
})
66+
67+
// Verify no leftover test files (all should be cleaned up with unique names)
68+
const dirContents = await fs.readdir(customPath)
69+
const testFiles = dirContents.filter((file) => file.startsWith(".write_test"))
70+
expect(testFiles).toHaveLength(0)
71+
})
72+
})

src/utils/storage.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,9 @@ export async function getStorageBasePath(defaultPath: string): Promise<string> {
3232
// Ensure custom path exists
3333
await fs.mkdir(customStoragePath, { recursive: true })
3434

35-
// Test if path is writable
36-
const testFile = path.join(customStoragePath, ".write_test")
35+
// Test if path is writable (use unique filename to avoid race conditions)
36+
const uniqueSuffix = `${Date.now()}-${Math.random().toString(36).slice(2, 11)}`
37+
const testFile = path.join(customStoragePath, `.write_test_${uniqueSuffix}`)
3738
await fs.writeFile(testFile, "test")
3839
await fs.rm(testFile)
3940

0 commit comments

Comments
 (0)