Files
orca/src/main/ipc/filesystem-list-files-install-rg.test.ts
T
mirror 34fa2cd5ca
Skill update round trip / roundtrip (false, macos-latest, copy, 1.5.17) (push) Canceled after 0s
Skill update round trip / roundtrip (false, macos-latest, symlink, 1.5.17) (push) Canceled after 0s
Skill update round trip / roundtrip (false, ubuntu-latest, copy, 1.5.17) (push) Canceled after 0s
Skill update round trip / roundtrip (false, ubuntu-latest, symlink, 1.5.17) (push) Canceled after 0s
Skill update round trip / roundtrip (false, ubuntu-latest, symlink, latest) (push) Canceled after 0s
Skill update round trip / roundtrip (false, windows-latest, copy, 1.5.17) (push) Canceled after 0s
Skill update round trip / roundtrip (false, windows-latest, symlink, 1.5.17) (push) Canceled after 0s
Skill update round trip / roundtrip (true, macos-latest, copy, 1.5.17) (push) Canceled after 0s
Skill update round trip / roundtrip (true, macos-latest, symlink, 1.5.17) (push) Canceled after 0s
Skill update round trip / roundtrip (true, ubuntu-latest, copy, 1.5.17) (push) Canceled after 0s
Skill update round trip / roundtrip (true, ubuntu-latest, symlink, 1.5.17) (push) Canceled after 0s
Skill update round trip / roundtrip (true, windows-latest, copy, 1.5.17) (push) Canceled after 0s
Skill update round trip / roundtrip (true, windows-latest, symlink, 1.5.17) (push) Canceled after 0s
source snapshot 2026-09-02
2026-09-02 02:03:16 +08:00

127 lines
4.8 KiB
TypeScript

import { spawn, type ChildProcess, type SpawnOptions } from 'node:child_process'
import { EventEmitter } from 'node:events'
import { beforeEach, describe, expect, it, vi } from 'vitest'
import type { Store } from '../persistence'
import type * as GitRunner from '../git/runner'
const {
listFilesWithGitMock,
resolveAuthorizedPathMock,
checkRgAvailableMock,
getLocalGitOptionsForRegisteredWorktreeMock,
wslAwareSpawnMock
} = vi.hoisted(() => ({
listFilesWithGitMock: vi.fn(),
resolveAuthorizedPathMock: vi.fn(),
checkRgAvailableMock: vi.fn(),
getLocalGitOptionsForRegisteredWorktreeMock: vi.fn(),
wslAwareSpawnMock: vi.fn()
}))
vi.mock('./filesystem-list-files-git-fallback', () => ({
listFilesWithGit: listFilesWithGitMock
}))
vi.mock('./filesystem-auth', () => ({
resolveAuthorizedPath: resolveAuthorizedPathMock
}))
vi.mock('./rg-availability', () => ({
checkRgAvailable: checkRgAvailableMock
}))
vi.mock('./local-worktree-runtime-options', () => ({
getLocalGitOptionsForRegisteredWorktree: getLocalGitOptionsForRegisteredWorktreeMock
}))
vi.mock('../git/runner', async (importOriginal) => ({
...(await importOriginal<typeof GitRunner>()),
wslAwareSpawn: wslAwareSpawnMock
}))
import { listQuickOpenFiles } from './filesystem-list-files'
function createStartedRipgrepProcess(): ChildProcess {
const child = new EventEmitter() as ChildProcess
;(child as unknown as Record<string, unknown>).stdout = new EventEmitter()
;(
(child as unknown as Record<string, unknown>).stdout as EventEmitter & {
setEncoding: () => void
}
).setEncoding = vi.fn()
;(child as unknown as Record<string, unknown>).stderr = new EventEmitter()
;(child as unknown as Record<string, unknown>).kill = vi.fn()
;(child as unknown as Record<string, unknown>).exitCode = null
;(child as unknown as Record<string, unknown>).signalCode = null
Object.defineProperty(child, 'pid', { value: 1 })
return child
}
describe('filesystem-list-files ripgrep guidance', () => {
beforeEach(() => {
vi.clearAllMocks()
resolveAuthorizedPathMock.mockImplementation(async (path) => path)
checkRgAvailableMock.mockResolvedValue(true)
getLocalGitOptionsForRegisteredWorktreeMock.mockReturnValue({})
wslAwareSpawnMock.mockImplementation(
(_command: string, _args: string[], options: SpawnOptions & { cwd?: string }) =>
spawn('orca-definitely-missing-rg', [], { stdio: options.stdio })
)
})
it('turns only a readdir budget failure into install guidance', async () => {
listFilesWithGitMock.mockRejectedValue(new Error('File listing exceeded 10000 files'))
const rejection = listQuickOpenFiles('/workspace', {} as Store)
await expect(rejection).rejects.toThrow(
'Quick Open scan too large (File listing exceeded 10000 files).'
)
await rejection.catch((error: Error) =>
expect(error.message).toContain('Install ripgrep on the host running the Quick Open scan')
)
})
it('keeps the WSL preflight and falls back before starting real rg', async () => {
getLocalGitOptionsForRegisteredWorktreeMock.mockReturnValue({ wslDistro: 'Ubuntu' })
checkRgAvailableMock.mockResolvedValue(false)
listFilesWithGitMock.mockResolvedValue(['src/index.ts'])
await expect(listQuickOpenFiles('C:\\repo', {} as Store)).resolves.toEqual(['src/index.ts'])
expect(checkRgAvailableMock).toHaveBeenCalledWith('C:\\repo', 'Ubuntu')
expect(wslAwareSpawnMock).not.toHaveBeenCalled()
})
it("falls back when a native launcher exits outside ripgrep's contract", async () => {
const primary = createStartedRipgrepProcess()
const ignored = createStartedRipgrepProcess()
let callIndex = 0
wslAwareSpawnMock.mockImplementation(() => (++callIndex === 1 ? primary : ignored))
listFilesWithGitMock.mockResolvedValue(['src/index.ts'])
const listing = listQuickOpenFiles('/workspace', {} as Store)
await Promise.resolve()
primary.emit('close', 127, null)
await expect(listing).resolves.toEqual(['src/index.ts'])
expect(listFilesWithGitMock).toHaveBeenCalledTimes(1)
})
it('keeps cancellation and Git errors unchanged', async () => {
const cancellation = new Error('File listing cancelled')
listFilesWithGitMock.mockRejectedValueOnce(cancellation)
await expect(listQuickOpenFiles('/workspace', {} as Store)).rejects.toBe(cancellation)
const gitFailure = new Error('git ls-files exited with code 128')
listFilesWithGitMock.mockRejectedValueOnce(gitFailure)
await expect(listQuickOpenFiles('/workspace', {} as Store)).rejects.toBe(gitFailure)
})
it.skipIf(process.platform !== 'darwin')('shows the macOS install command', async () => {
listFilesWithGitMock.mockRejectedValue(new Error('File listing timed out'))
await expect(listQuickOpenFiles('/workspace', {} as Store)).rejects.toThrow(
'brew install ripgrep'
)
})
})