diff --git a/test/adopt.test.js b/test/adopt.test.js index 1545d5b..f155ef9 100644 --- a/test/adopt.test.js +++ b/test/adopt.test.js @@ -3,24 +3,72 @@ import { describe, test, expect, beforeEach, afterEach } from 'bun:test'; import { run } from './helpers.js'; -import { mkdirSync, rmSync } from 'node:fs'; +import { chmodSync, existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs'; import { tmpdir } from 'node:os'; import { join } from 'node:path'; -const NON_AGENT_ENV = { CLAUDECODE: '', GEMINI_CLI: '', CODEX_CI: '' }; - describe('vit adopt', () => { let tmpDir; + let shimDir; + let homeDir; + let logPath; beforeEach(() => { tmpDir = join(tmpdir(), '.test-adopt-' + Math.random().toString(36).slice(2)); mkdirSync(tmpDir, { recursive: true }); + shimDir = join(tmpDir, 'shim'); + homeDir = join(tmpDir, 'home'); + logPath = join(tmpDir, 'commands.log'); + mkdirSync(shimDir); + mkdirSync(join(homeDir, 'config'), { recursive: true }); + mkdirSync(join(homeDir, 'gh'), { recursive: true }); + + for (const tool of ['gh', 'git']) { + const script = [ + '#!/bin/sh', + `log_file=${JSON.stringify(logPath)}`, + `printf '${tool}' >> "$log_file"`, + 'for a in "$@"; do', + ' printf " %s" "$a" >> "$log_file"', + ' last="$a"', + 'done', + 'printf "\\n" >> "$log_file"', + 'mkdir -p "$last"', + 'exit 0', + '', + ].join('\n'); + const shimPath = join(shimDir, tool); + writeFileSync(shimPath, script); + chmodSync(shimPath, 0o755); + } }); afterEach(() => { rmSync(tmpDir, { recursive: true, force: true }); }); + function adoptEnv() { + return { + PATH: shimDir + ':' + process.env.PATH, + HOME: homeDir, + XDG_CONFIG_HOME: join(homeDir, 'config'), + GH_CONFIG_DIR: join(homeDir, 'gh'), + GH_TOKEN: '', + GITHUB_TOKEN: '', + HTTPS_PROXY: 'http://127.0.0.1:1', + HTTP_PROXY: 'http://127.0.0.1:1', + https_proxy: 'http://127.0.0.1:1', + http_proxy: 'http://127.0.0.1:1', + NO_PROXY: '', + no_proxy: '', + }; + } + + function expectLog(...lines) { + expect(existsSync(logPath)).toBe(true); + expect(readFileSync(logPath, 'utf-8')).toBe(lines.join('\n') + '\n'); + } + test('shows help with argument', () => { const result = run('adopt --help', tmpDir); expect(result.stdout).toContain(''); @@ -33,55 +81,68 @@ describe('vit adopt', () => { }); test('fails with invalid beacon', () => { - const result = run('adopt notaurl', tmpDir, NON_AGENT_ENV); + const result = run('adopt notaurl', tmpDir, adoptEnv()); expect(result.exitCode).not.toBe(0); expect(result.stderr).toContain('Invalid beacon from vit adopt '); }); test('fails if directory already exists', () => { mkdirSync(join(tmpDir, 'hello-world')); - const result = run('adopt https://github.com/octocat/Hello-World', tmpDir, NON_AGENT_ENV); + const result = run('adopt https://github.com/octocat/Hello-World', tmpDir, adoptEnv()); expect(result.exitCode).not.toBe(0); expect(result.stderr).toContain('already exists'); + expect(existsSync(logPath)).toBe(false); }); test('rejects when run inside a coding agent', () => { - const result = run('adopt https://github.com/octocat/Hello-World', tmpDir, { CLAUDECODE: '1' }); + const result = run('adopt https://github.com/octocat/Hello-World', tmpDir, { ...adoptEnv(), CLAUDECODE: '1' }); expect(result.exitCode).toBe(1); expect(result.stderr).toContain('must be run by a human'); }); test('clones repo and shows guidance', () => { - const result = run('adopt https://github.com/octocat/Hello-World', tmpDir, NON_AGENT_ENV); + const result = run('adopt https://github.com/octocat/Hello-World', tmpDir, adoptEnv()); expect(result.exitCode).toBe(0); expect(result.stdout).toContain('vit:github.com/octocat/hello-world'); expect(result.stdout).toContain('hello-world'); expect(result.stdout).toContain('start your agent'); - }, 30000); + expectLog('gh repo fork https://github.com/octocat/hello-world --clone -- hello-world'); + }); test('clones into custom directory name', () => { - const result = run('adopt https://github.com/octocat/Hello-World my-copy', tmpDir, NON_AGENT_ENV); + const result = run('adopt https://github.com/octocat/Hello-World my-copy', tmpDir, adoptEnv()); expect(result.exitCode).toBe(0); expect(result.stdout).toContain('my-copy'); - }, 30000); + expectLog('gh repo fork https://github.com/octocat/hello-world --clone -- my-copy'); + }); test('handles vit: prefixed beacon', () => { - const result = run('adopt vit:github.com/octocat/Hello-World', tmpDir, NON_AGENT_ENV); + const result = run('adopt vit:github.com/octocat/Hello-World', tmpDir, adoptEnv()); expect(result.exitCode).toBe(0); expect(result.stdout).toContain('beacon: vit:github.com/octocat/hello-world'); - }, 30000); + expectLog('gh repo fork https://github.com/octocat/hello-world --clone -- hello-world'); + }); test('verbose flag shows step details', () => { - const result = run('adopt -v https://github.com/octocat/Hello-World', tmpDir, NON_AGENT_ENV); + const result = run('adopt -v https://github.com/octocat/Hello-World', tmpDir, adoptEnv()); expect(result.exitCode).toBe(0); expect(result.stdout).toContain('[verbose]'); expect(result.stdout).toContain('resolving beacon'); - }, 30000); + expectLog('gh repo fork https://github.com/octocat/hello-world --clone -- hello-world'); + }); test('second adopt to same dir fails', () => { - run('adopt https://github.com/octocat/Hello-World', tmpDir, NON_AGENT_ENV); - const result = run('adopt https://github.com/octocat/Hello-World', tmpDir, NON_AGENT_ENV); + const first = run('adopt https://github.com/octocat/Hello-World', tmpDir, adoptEnv()); + const result = run('adopt https://github.com/octocat/Hello-World', tmpDir, adoptEnv()); + expect(first.exitCode).toBe(0); expect(result.exitCode).not.toBe(0); expect(result.stderr).toContain('already exists'); - }, 30000); + expectLog('gh repo fork https://github.com/octocat/hello-world --clone -- hello-world'); + }); + + test('clones non-GitHub repo', () => { + const result = run('adopt https://git.example.com/octocat/hello-world', tmpDir, adoptEnv()); + expect(result.exitCode).toBe(0); + expectLog('git clone https://git.example.com/octocat/hello-world hello-world'); + }); });