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:
Tianyi Cui authored and GitHub committed 2026-09-13 11:34:01 +08:00
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' }])
})