Download src/server/__tests__/computer-use-api.test.ts from chenbhao/codev: direct link, hf CLI and curl.
- Browser
- Download file 5.99 kB
-
https://huggingface.co/chenbhao/codev/resolve/main/src/server/__tests__/computer-use-api.test.ts
- Command line
-
hf download hf://chenbhao/codev/src/server/__tests__/computer-use-api.test.ts
-
curl -L -o computer-use-api.test.ts https://huggingface.co/chenbhao/codev/resolve/main/src/server/__tests__/computer-use-api.test.ts
5.99 kB
| import { afterAll, beforeAll, beforeEach, describe, expect, it } from 'bun:test' | |
| import { mkdtemp, readFile, rm } from 'node:fs/promises' | |
| import { tmpdir } from 'node:os' | |
| import { join } from 'node:path' | |
| const originalClaudeConfigDir = process.env.CLAUDE_CONFIG_DIR | |
| let configDir: string | null = null | |
| let computerUseApi: typeof import('../api/computer-use.js') | null = null | |
| async function importComputerUseApi() { | |
| if (!computerUseApi) throw new Error('Computer Use API module was not initialized') | |
| return computerUseApi | |
| } | |
| function makeRequest(method: string, body?: unknown): Request { | |
| return new Request('http://localhost/api/computer-use/authorized-apps', { | |
| method, | |
| body: body === undefined ? undefined : JSON.stringify(body), | |
| }) | |
| } | |
| async function callAuthorizedApps(method: string, body?: unknown): Promise<Response> { | |
| const { handleComputerUseApi } = await importComputerUseApi() | |
| return handleComputerUseApi( | |
| makeRequest(method, body), | |
| new URL('http://localhost/api/computer-use/authorized-apps'), | |
| ['api', 'computer-use', 'authorized-apps'], | |
| ) | |
| } | |
| beforeAll(async () => { | |
| configDir = await mkdtemp(join(tmpdir(), 'cc-haha-computer-use-api-')) | |
| process.env.CLAUDE_CONFIG_DIR = configDir | |
| computerUseApi = await import('../api/computer-use.js') | |
| }) | |
| beforeEach(async () => { | |
| if (!configDir) throw new Error('configDir was not initialized') | |
| process.env.CLAUDE_CONFIG_DIR = configDir | |
| await rm(join(configDir, 'cc-haha'), { recursive: true, force: true }) | |
| await rm(join(configDir, '.runtime'), { recursive: true, force: true }) | |
| }) | |
| afterAll(async () => { | |
| if (originalClaudeConfigDir === undefined) { | |
| delete process.env.CLAUDE_CONFIG_DIR | |
| } else { | |
| process.env.CLAUDE_CONFIG_DIR = originalClaudeConfigDir | |
| } | |
| if (configDir) { | |
| await rm(configDir, { recursive: true, force: true }) | |
| configDir = null | |
| } | |
| }) | |
| describe('Computer Use API authorized app config', () => { | |
| it('defaults Computer Use enabled for existing users without config', async () => { | |
| const res = await callAuthorizedApps('GET') | |
| expect(res.status).toBe(200) | |
| expect(await res.json()).toMatchObject({ | |
| enabled: true, | |
| authorizedApps: [], | |
| }) | |
| }) | |
| it('persists the Computer Use enabled flag independently', async () => { | |
| const putRes = await callAuthorizedApps('PUT', { enabled: false }) | |
| expect(putRes.status).toBe(200) | |
| const getRes = await callAuthorizedApps('GET') | |
| expect(await getRes.json()).toMatchObject({ enabled: false }) | |
| const raw = await readFile( | |
| join(configDir!, 'cc-haha', 'computer-use-config.json'), | |
| 'utf8', | |
| ) | |
| expect(JSON.parse(raw)).toMatchObject({ enabled: false }) | |
| }) | |
| it('persists and normalizes a custom Python interpreter path', async () => { | |
| const pythonPath = ' C:\\Users\\me\\miniconda3\\envs\\cu\\python.exe ' | |
| const putRes = await callAuthorizedApps('PUT', { pythonPath }) | |
| expect(putRes.status).toBe(200) | |
| const getRes = await callAuthorizedApps('GET') | |
| expect(await getRes.json()).toMatchObject({ | |
| pythonPath: 'C:\\Users\\me\\miniconda3\\envs\\cu\\python.exe', | |
| }) | |
| const resetRes = await callAuthorizedApps('PUT', { pythonPath: '' }) | |
| expect(resetRes.status).toBe(200) | |
| const resetGetRes = await callAuthorizedApps('GET') | |
| expect(await resetGetRes.json()).toMatchObject({ pythonPath: null }) | |
| }) | |
| }) | |
| describe('runPipInstallWithFallback', () => { | |
| it('builds a clear unsupported Python version step for setup', async () => { | |
| const { getUnsupportedPythonVersionStep } = await importComputerUseApi() | |
| expect(getUnsupportedPythonVersionStep('3.8.18')).toEqual({ | |
| name: 'python_version', | |
| ok: false, | |
| message: 'Computer Use 需要 Python >= 3.9,当前版本为 3.8.18', | |
| }) | |
| expect(getUnsupportedPythonVersionStep('3.9.19')).toBeNull() | |
| }) | |
| it('installs setup dependencies by upgrading pip before requirements', async () => { | |
| const { installSetupDependencies } = await importComputerUseApi() | |
| const calls: string[] = [] | |
| const result = await installSetupDependencies( | |
| 'python', | |
| '/tmp/requirements.txt', | |
| async (cmd, args) => { | |
| calls.push(`${cmd} ${args.join(' ')}`) | |
| return { ok: true, stdout: args.includes('-r') ? 'deps' : 'pip', stderr: '', code: 0 } | |
| }, | |
| ) | |
| expect(result.stdout).toBe('deps') | |
| expect(calls).toEqual([ | |
| 'python -m pip install --upgrade pip', | |
| 'python -m pip install -r /tmp/requirements.txt', | |
| ]) | |
| }) | |
| it('tries the mirror first and falls back to the default PyPI index', async () => { | |
| const { runPipInstallWithFallback } = await importComputerUseApi() | |
| const calls: string[] = [] | |
| const result = await runPipInstallWithFallback( | |
| 'python', | |
| ['-m', 'pip', 'install', '-r', 'requirements.txt'], | |
| async (cmd, args) => { | |
| calls.push(`${cmd} ${args.join(' ')}`) | |
| if (args.includes('-i')) { | |
| return { ok: false, stdout: '', stderr: 'mirror unavailable', code: 1 } | |
| } | |
| return { ok: true, stdout: 'installed', stderr: '', code: 0 } | |
| }, | |
| ) | |
| expect(result.ok).toBe(true) | |
| expect(result.stdout).toBe('installed') | |
| expect(calls).toEqual([ | |
| 'python -m pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple/ --trusted-host pypi.tuna.tsinghua.edu.cn', | |
| 'python -m pip install -r requirements.txt', | |
| ]) | |
| }) | |
| it('returns the first failure when every pip index attempt fails', async () => { | |
| const { runPipInstallWithFallback } = await importComputerUseApi() | |
| const result = await runPipInstallWithFallback( | |
| 'python', | |
| ['-m', 'pip', 'install', '-r', 'requirements.txt'], | |
| async (_cmd, args) => ({ | |
| ok: false, | |
| stdout: '', | |
| stderr: args.includes('-i') ? 'mirror failed' : 'default failed', | |
| code: args.includes('-i') ? 1 : 2, | |
| }), | |
| ) | |
| expect(result).toEqual({ ok: false, stdout: '', stderr: 'mirror failed', code: 1 }) | |
| }) | |
| }) | |