import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs' import * as os from 'node:os' import * as path from 'node:path' import { afterEach, describe, expect, it } from 'vitest' import { resolveClaudeBin } from './claudeBin' const tempDirs: string[] = [] afterEach(() => { while (tempDirs.length > 0) { const dir = tempDirs.pop() if (dir) rmSync(dir, { recursive: true, force: true }) } }) function tempDir(prefix = 'claude-bin-'): string { const dir = mkdtempSync(path.join(os.tmpdir(), prefix)) tempDirs.push(dir) return dir } function touchClaude(dir: string): string { mkdirSync(dir, { recursive: true }) const bin = path.join(dir, 'claude') writeFileSync(bin, '') return bin } describe('resolveClaudeBin', () => { it('优先命中 PATH 中的 claude', () => { const dir = tempDir() const bin = touchClaude(dir) expect(resolveClaudeBin({ PATH: dir }, '/no-such-home')).toBe(bin) }) it('pATH 未命中时回退到 $HOME/.local/bin/claude', () => { const home = tempDir() const bin = touchClaude(path.join(home, '.local', 'bin')) expect(resolveClaudeBin({ PATH: '' }, home)).toBe(bin) }) it('pATH 未命中时回退到 $HOME/.claude/local/claude', () => { const home = tempDir() const bin = touchClaude(path.join(home, '.claude', 'local')) expect(resolveClaudeBin({ PATH: '' }, home)).toBe(bin) }) it('pATH 与候选皆空 → null', () => { const home = tempDir() expect(resolveClaudeBin({ PATH: '' }, home)).toBeNull() }) it('pATH 中目录不存在时跳过并继续', () => { const home = tempDir() const bin = touchClaude(path.join(home, '.local', 'bin')) expect(resolveClaudeBin({ PATH: '/definitely/missing/bin' }, home)).toBe(bin) }) })