Skip to content

Commit 376bd67

Browse files
refactor(fs): use mkdirSafe recursive and align test mocks
Replace try/access/mkdir pattern with single mkdirSafe call in ssh-key-manager; drop unused mkdir mocks from stt/tts/repo tests and mock fs-safe's mkdirSafe instead; add zod peer dep extension to pnpm workspace; note EACCES-tolerant startup in troubleshooting doc.
1 parent f20d8ff commit 376bd67

7 files changed

Lines changed: 49 additions & 22 deletions

File tree

backend/src/utils/ssh-key-manager.ts

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,7 @@ import { mkdirSafe } from './fs-safe'
99
const SSH_KEYS_DIR = join(getWorkspacePath(), '.ssh-keys')
1010

1111
async function ensureSSHKeysDir(): Promise<void> {
12-
try {
13-
await fs.access(SSH_KEYS_DIR)
14-
} catch {
15-
await mkdirSafe(SSH_KEYS_DIR, { mode: 0o700 })
16-
}
12+
await mkdirSafe(SSH_KEYS_DIR, { mode: 0o700 })
1713
}
1814

1915
async function validateSSHKey(keyPath: string): Promise<boolean> {
@@ -167,11 +163,7 @@ export function generateSSHConfig(entries: SSHConfigEntry[]): string {
167163

168164
export async function writeSSHConfig(configPath: string, configContent: string): Promise<void> {
169165
const dir = join(getWorkspacePath(), 'config')
170-
try {
171-
await fs.access(dir)
172-
} catch {
173-
await mkdirSafe(dir, { mode: 0o700 })
174-
}
166+
await mkdirSafe(dir, { mode: 0o700 })
175167

176168
await fs.writeFile(configPath, configContent, { mode: 0o600 })
177169
}

backend/test/routes/stt.test.ts

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import { describe, it, expect, vi, beforeEach } from 'vitest'
22
import * as fs from 'fs/promises'
33

44
vi.mock('fs/promises', () => ({
5-
mkdir: vi.fn(),
65
readFile: vi.fn(),
76
writeFile: vi.fn(),
87
stat: vi.fn(),
@@ -45,7 +44,6 @@ vi.mock('@opencode-manager/shared/config/env', () => ({
4544
},
4645
}))
4746

48-
const mockMkdir = fs.mkdir as any
4947
const mockReadFile = fs.readFile as any
5048
const mockWriteFile = fs.writeFile as any
5149
const mockStat = fs.stat as any
@@ -432,7 +430,6 @@ describe('STT Routes', () => {
432430
mtimeMs: Date.now() - 2 * 60 * 60 * 1000,
433431
})
434432
mockUnlink.mockResolvedValue(undefined)
435-
mockMkdir.mockResolvedValue(undefined)
436433
mockWriteFile.mockResolvedValue(undefined)
437434

438435
const mockFetch = vi.fn().mockResolvedValue({
@@ -461,7 +458,6 @@ describe('STT Routes', () => {
461458
it('should return default model when API fetch fails', async () => {
462459
const originalFetch = globalThis.fetch
463460
mockStat.mockRejectedValue(new Error('File not found'))
464-
mockMkdir.mockResolvedValue(undefined)
465461
mockWriteFile.mockResolvedValue(undefined)
466462

467463
const mockFetch = vi.fn().mockRejectedValue(new Error('Network error'))
@@ -479,7 +475,6 @@ describe('STT Routes', () => {
479475

480476
it('should force refresh when refresh=true', async () => {
481477
const originalFetch = globalThis.fetch
482-
mockMkdir.mockResolvedValue(undefined)
483478
mockWriteFile.mockResolvedValue(undefined)
484479

485480
const mockFetch = vi.fn().mockResolvedValue({

backend/test/routes/tts.test.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
22
import * as fs from 'fs/promises'
33

44
vi.mock('fs/promises', () => ({
5-
mkdir: vi.fn(),
65
readFile: vi.fn(),
76
writeFile: vi.fn(),
87
readdir: vi.fn(),

backend/test/services/repo.test.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,21 +16,25 @@ const deleteRepo = vi.fn()
1616
const lstat = vi.fn()
1717
const stat = vi.fn()
1818
const readdir = vi.fn()
19-
const mkdir = vi.fn()
2019
const symlink = vi.fn()
2120
const readlink = vi.fn()
21+
const mkdirSafe = vi.fn()
2222

2323
vi.mock('fs/promises', () => ({
2424
default: {
2525
lstat,
2626
stat,
2727
readdir,
28-
mkdir,
2928
symlink,
3029
readlink,
3130
},
3231
}))
3332

33+
vi.mock('../../src/utils/fs-safe', () => ({
34+
mkdirSafe,
35+
mkdirSyncSafe: vi.fn(),
36+
}))
37+
3438
vi.mock('../../src/utils/process', () => ({
3539
executeCommand,
3640
}))
@@ -84,7 +88,7 @@ describe('repo service', () => {
8488
beforeEach(() => {
8589
vi.clearAllMocks()
8690
ensureDirectoryExists.mockResolvedValue(undefined)
87-
mkdir.mockResolvedValue(undefined)
91+
mkdirSafe.mockResolvedValue(undefined)
8892
symlink.mockResolvedValue(undefined)
8993
readlink.mockResolvedValue('')
9094
readdir.mockResolvedValue([])

docs/troubleshooting.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ ports:
4848
4949
**Symptoms:** Container can't write to volumes
5050
51+
Startup tolerates workspace directories that already exist with wrong ownership; permission errors then appear on the first write into the directory rather than at startup.
52+
5153
**Solutions:**
5254
5355
1. Fix ownership:

pnpm-lock.yaml

Lines changed: 30 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pnpm-workspace.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,11 @@ packages:
44
- 'frontend'
55
- 'ocm-cli'
66
- '!workspace/**'
7+
8+
packageExtensions:
9+
'@hookform/resolvers':
10+
peerDependencies:
11+
zod: '*'
12+
peerDependenciesMeta:
13+
zod:
14+
optional: true

0 commit comments

Comments
 (0)