import assert from 'node:assert/strict' import { execFileSync } from 'node:child_process' import fs from 'node:fs' import os from 'node:os' import path from 'node:path' import { afterEach, test } from 'vitest' import { gitFor, repoStatus, resolveRenamePath } from './git-review-ops' const tempDirs: string[] = [] afterEach(() => { for (const dir of tempDirs.splice(0)) { fs.rmSync(dir, { force: true, recursive: true }) } }) function makeRepo() { const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'hermes-desktop-git-status-')) tempDirs.push(dir) execFileSync('git', ['init', '-q'], { cwd: dir }) execFileSync('git', ['config', 'user.email', 'hermes-test@example.com'], { cwd: dir }) execFileSync('git', ['config', 'user.name', 'Hermes Test'], { cwd: dir }) fs.writeFileSync(path.join(dir, 'tracked.txt'), 'tracked\n') execFileSync('git', ['add', 'tracked.txt'], { cwd: dir }) execFileSync('git', ['commit', '-qm', 'initial'], { cwd: dir }) return dir } test('resolveRenamePath: plain path is unchanged', () => { assert.equal(resolveRenamePath('src/a.ts'), 'src/a.ts') }) test('gitFor accepts an internally resolved git binary path containing spaces', () => { assert.doesNotThrow(() => gitFor(process.cwd(), 'C:\\Program Files\\Git\\cmd\\git.exe')) }) test('gitFor runs git through a spaced binary path', async () => { if (process.platform !== 'win32') { return } const gitBin = path.join(process.env.ProgramFiles || String.raw`C:\Program Files`, 'Git', 'cmd', 'git.exe') if (!fs.existsSync(gitBin)) { return } const repo = makeRepo() fs.writeFileSync(path.join(repo, 'changed.txt'), 'review me\n') const status = await gitFor(repo, gitBin).status() assert.equal(status.not_added.includes('changed.txt'), true) }) test('resolveRenamePath: simple rename resolves to the new path', () => { assert.equal(resolveRenamePath('old.ts => new.ts'), 'new.ts') }) test('resolveRenamePath: brace rename resolves to the new path', () => { assert.equal(resolveRenamePath('src/{old => new}/file.ts'), 'src/new/file.ts') }) test('resolveRenamePath: brace rename collapsing a segment', () => { assert.equal(resolveRenamePath('src/{lib => }/file.ts'), 'src/file.ts') }) test('repoStatus reports an untracked directory without recursively listing its contents', async () => { const dir = makeRepo() const nested = path.join(dir, 'generated', 'deep') fs.mkdirSync(nested, { recursive: true }) fs.writeFileSync(path.join(nested, 'large-output.txt'), 'generated\n') const status = await repoStatus(dir, 'git') assert.ok(status) assert.equal(status.untracked, 1) assert.equal(status.changed, 1) assert.deepEqual( status.files.map(file => file.path), ['generated/'] ) })