Files
helder/test/git.test.ts
2026-06-16 06:18:42 +02:00

106 lines
3.8 KiB
TypeScript

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<string> {
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()
})
})