Merge pull request #4112 from deepseek-harness/worktree/file-search-root-fixture-20260913
test(file-reference): publish restored workspace fixtures atomically
This commit is contained in:
commit
351645a6c9
1 file changed
+15
-9
@@ -1,4 +1,4 @@
|
||||
import { chmod, mkdir, mkdtemp, rm, symlink, writeFile } from 'node:fs/promises'
|
||||
import { chmod, mkdir, mkdtemp, readdir, rename, rm, symlink, writeFile } from 'node:fs/promises'
|
||||
import { tmpdir } from 'node:os'
|
||||
import { join } from 'node:path'
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||
@@ -18,12 +18,12 @@ vi.mock('node:fs/promises', async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import('node:fs/promises')>()
|
||||
return {
|
||||
...actual,
|
||||
readdir: (async (path: unknown, ...rest: never[]) => {
|
||||
readdir: vi.fn((async (path: unknown, ...rest: never[]) => {
|
||||
if (fsControl.denyReaddir !== undefined && String(path) === fsControl.denyReaddir) {
|
||||
throw Object.assign(new Error('EACCES: injected unreadable directory'), { code: 'EACCES' })
|
||||
}
|
||||
return (actual.readdir as (path: unknown, ...args: never[]) => Promise<unknown>)(path, ...rest)
|
||||
}) as typeof actual.readdir,
|
||||
}) as typeof actual.readdir),
|
||||
}
|
||||
})
|
||||
|
||||
@@ -68,6 +68,7 @@ afterEach(async () => {
|
||||
for (const locked of locks.splice(0)) await chmod(locked, 0o700).catch(() => undefined)
|
||||
for (const instance of searches.splice(0)) instance.dispose()
|
||||
await Promise.all(roots.splice(0).map(root => rm(root, { recursive: true, force: true })))
|
||||
vi.mocked(readdir).mockClear()
|
||||
})
|
||||
|
||||
describe('file-reference grammar', () => {
|
||||
@@ -198,6 +199,8 @@ describe('WorkspaceFileSearch', () => {
|
||||
const files = search(root)
|
||||
const signal = new AbortController().signal
|
||||
expect(await files.list('README', signal)).toEqual([{ path: 'README.md', kind: 'file' }])
|
||||
const rootReads = () => vi.mocked(readdir).mock.calls.filter(([path]) => String(path) === root).length
|
||||
const initialReads = rootReads()
|
||||
|
||||
// A root that vanishes under a live index: an unreadable branch costs its
|
||||
// own candidates, but an unreadable root must not be published as an
|
||||
@@ -205,13 +208,16 @@ describe('WorkspaceFileSearch', () => {
|
||||
await rm(root, { recursive: true, force: true })
|
||||
files.invalidate()
|
||||
expect(await files.list('README', signal)).toEqual([{ path: 'README.md', kind: 'file' }])
|
||||
await new Promise((resolve) => { setTimeout(resolve, 50) })
|
||||
expect(await files.list('README', signal)).toEqual([{ path: 'README.md', kind: 'file' }])
|
||||
await vi.waitFor(async () => {
|
||||
expect(await files.list('README', signal)).toEqual([{ path: 'README.md', kind: 'file' }])
|
||||
expect(rootReads()).toBeGreaterThan(initialReads + 1)
|
||||
})
|
||||
|
||||
// The failed attempt left the index stale, so its return is picked up
|
||||
// without waiting for another invalidation.
|
||||
await mkdir(root, { recursive: true })
|
||||
await writeFile(join(root, 'restored.ts'), 'restored')
|
||||
// A retry can read the root as soon as it exists; publish its files together.
|
||||
const replacement = await mkdtemp(`${root}-replacement-`)
|
||||
roots.push(replacement)
|
||||
await writeFile(join(replacement, 'restored.ts'), 'restored')
|
||||
await rename(replacement, root)
|
||||
await vi.waitFor(async () => {
|
||||
expect(await files.list('restored', signal)).toEqual([{ path: 'restored.ts', kind: 'file' }])
|
||||
})
|
||||
|
||||
Reference in new issue
Block a user