✨ feat(vscode): 代码阅读加「问 AI」,块下面问 Claude Code,问答存进 .spx 挂在块下
This commit is contained in:
1 parent
badc722640
commit
fb53bb727c
34 files changed
+2726
-43
No files matched your search
@@ -0,0 +1,56 @@
|
||||
import { promises as fsp } from 'node:fs'
|
||||
import * as os from 'node:os'
|
||||
import * as path from 'node:path'
|
||||
import { afterEach, beforeEach, describe, expect, it } from 'vitest'
|
||||
import { ClaudeCancelledError, ClaudeError, ClaudeTimeoutError, runClaudeLines } from './spawnClaude'
|
||||
|
||||
let dir: string
|
||||
|
||||
beforeEach(async () => {
|
||||
dir = await fsp.mkdtemp(path.join(os.tmpdir(), 'fake-claude-'))
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
await fsp.rm(dir, { recursive: true, force: true })
|
||||
})
|
||||
|
||||
/** 假的 claude:一段 sh 脚本 */
|
||||
async function fakeBin(script: string): Promise<string> {
|
||||
const bin = path.join(dir, 'claude')
|
||||
await fsp.writeFile(bin, `#!/bin/sh\n${script}\n`, { mode: 0o755 })
|
||||
return bin
|
||||
}
|
||||
|
||||
describe('跑 claude、逐行交出 stdout', () => {
|
||||
it('每出来完整的一行交一行,最后没带换行的那段结束时交出;参数原样传、在 cwd 里跑', async () => {
|
||||
const bin = await fakeBin('printf \'{"a":1}\\n{"b":\'; sleep 0.1; printf \'2}\\n\'; pwd; printf "%s|" "$@"')
|
||||
const lines: string[] = []
|
||||
await runClaudeLines({ bin, args: ['-p', '带 空格 的问题'], cwd: dir, onLine: item => lines.push(item) })
|
||||
expect(lines).toEqual(['{"a":1}', '{"b":2}', await fsp.realpath(dir), '-p|带 空格 的问题|'])
|
||||
})
|
||||
|
||||
it('退出码非零:带上 stderr 的原话', async () => {
|
||||
const bin = await fakeBin('echo "No conversation found with session ID: x" >&2; exit 1')
|
||||
const error = await runClaudeLines({ bin, args: [], cwd: dir, onLine: () => {} }).catch((err: unknown) => err)
|
||||
expect(error).toBeInstanceOf(ClaudeError)
|
||||
expect((error as ClaudeError).message).toBe('Claude 退出码非零 (1): No conversation found with session ID: x')
|
||||
})
|
||||
|
||||
it('叫停:马上结束进程,报 ClaudeCancelledError;已经叫停的不再启动', async () => {
|
||||
const marker = path.join(dir, 'still-running')
|
||||
const bin = await fakeBin(`echo started; sleep 2; touch "${marker}"`)
|
||||
const controller = new AbortController()
|
||||
const started = Date.now()
|
||||
const run = runClaudeLines({ bin, args: [], cwd: dir, signal: controller.signal, onLine: () => controller.abort() })
|
||||
await expect(run).rejects.toBeInstanceOf(ClaudeCancelledError)
|
||||
expect(Date.now() - started).toBeLessThan(1500)
|
||||
await new Promise(resolve => setTimeout(resolve, 2500))
|
||||
await expect(fsp.access(marker)).rejects.toThrow()
|
||||
await expect(runClaudeLines({ bin, args: [], cwd: dir, signal: controller.signal, onLine: () => {} })).rejects.toBeInstanceOf(ClaudeCancelledError)
|
||||
}, 10_000)
|
||||
|
||||
it('超时:结束进程,报 ClaudeTimeoutError', async () => {
|
||||
const bin = await fakeBin('sleep 5')
|
||||
await expect(runClaudeLines({ bin, args: [], cwd: dir, timeoutMs: 200, onLine: () => {} })).rejects.toBeInstanceOf(ClaudeTimeoutError)
|
||||
})
|
||||
})
|
||||
Reference in new issue
Block a user