import { mkdtemp, rm, writeFile } from 'node:fs/promises' import { tmpdir } from 'node:os' import { join } from 'node:path' import { afterEach, describe, expect, it } from 'vitest' import { simpleGit } from 'simple-git' import { readFile } from 'node:fs/promises' import { existsSync } from 'node:fs' import { classify, discard, load, stage } from '../src/main/git-service' describe('classify', () => { it('reads the index code as staged, working code as unstaged', () => { expect(classify('M', ' ')).toEqual({ letter: 'M', staged: true }) expect(classify(' ', 'M')).toEqual({ letter: 'M', staged: false }) expect(classify('A', ' ')).toEqual({ letter: 'A', staged: true }) expect(classify('D', ' ')).toEqual({ letter: 'D', staged: true }) expect(classify('R', ' ')).toEqual({ letter: 'R', staged: true }) }) it('treats untracked as a new (A) unstaged file', () => { expect(classify('?', '?')).toEqual({ letter: 'A', staged: false }) }) it('maps unmerged (U) to modified', () => { expect(classify('U', 'U').letter).toBe('M') }) }) describe('load (integration against a temp repo)', () => { let dir = '' afterEach(async () => { if (dir) await rm(dir, { recursive: true, force: true }) }) async function repo(): Promise { const d = await mkdtemp(join(tmpdir(), 'helder-git-')) const g = simpleGit(d) await g.init() await g.addConfig('user.email', 't@example.com') await g.addConfig('user.name', 'Test') await g.addConfig('commit.gpgsign', 'false') await writeFile(join(d, 'a.txt'), '1\n2\n3\n') await g.add('.') await g.commit('init') return d } it('returns null for a non-repo directory', async () => { dir = await mkdtemp(join(tmpdir(), 'helder-nogit-')) expect(await load(dir)).toBeNull() }) it('reports a modified file with HEAD-vs-worktree text', async () => { dir = await repo() await writeFile(join(dir, 'a.txt'), '1\nX\n3\n') const res = await load(dir) expect(res).not.toBeNull() expect(res!.branch).toBeTruthy() const a = res!.changes.find((c) => c.path === 'a.txt') expect(a?.status).toBe('M') expect(a?.staged).toBe(false) expect(a?.original).toBe('1\n2\n3\n') expect(a?.updated).toBe('1\nX\n3\n') }) it('reports an untracked file as new (A), original empty', async () => { dir = await repo() await writeFile(join(dir, 'new.txt'), 'fresh\n') const res = await load(dir) const n = res!.changes.find((c) => c.path === 'new.txt') expect(n?.status).toBe('A') expect(n?.staged).toBe(false) expect(n?.original).toBe('') expect(n?.updated).toBe('fresh\n') }) it('reflects staging', async () => { dir = await repo() await writeFile(join(dir, 'a.txt'), '1\n2\n3\n4\n') await stage(dir, ['a.txt']) const res = await load(dir) expect(res!.changes.find((c) => c.path === 'a.txt')?.staged).toBe(true) }) it('discard reverts a modified tracked file to HEAD', async () => { dir = await repo() await writeFile(join(dir, 'a.txt'), '1\nCHANGED\n3\n') await discard(dir, ['a.txt']) expect(await readFile(join(dir, 'a.txt'), 'utf8')).toBe('1\n2\n3\n') const res = await load(dir) expect(res!.changes.find((c) => c.path === 'a.txt')).toBeUndefined() }) it('discard removes a new (untracked) file from disk', async () => { dir = await repo() await writeFile(join(dir, 'new.txt'), 'fresh\n') await discard(dir, ['new.txt']) expect(existsSync(join(dir, 'new.txt'))).toBe(false) }) it('discard removes a staged-new file', async () => { dir = await repo() await writeFile(join(dir, 'staged-new.txt'), 'x\n') await stage(dir, ['staged-new.txt']) await discard(dir, ['staged-new.txt']) expect(existsSync(join(dir, 'staged-new.txt'))).toBe(false) const res = await load(dir) expect(res!.changes.find((c) => c.path === 'staged-new.txt')).toBeUndefined() }) })