100 lines
4.2 KiB
JavaScript
100 lines
4.2 KiB
JavaScript
import { test } from 'node:test'
|
|
import assert from 'node:assert/strict'
|
|
import fs from 'node:fs'
|
|
import os from 'node:os'
|
|
import path from 'node:path'
|
|
import { execFileSync } from 'node:child_process'
|
|
|
|
import { createCheckpoint, restoreCheckpoint, listCheckpoints, isGitRepo, dirtySubmodules } from '../src/checkpoint.mjs'
|
|
|
|
function git(project, args) {
|
|
return execFileSync('git', ['-c', 'user.email=t@t', '-c', 'user.name=t', ...args], { cwd: project, encoding: 'utf8' })
|
|
}
|
|
|
|
function tempRepo() {
|
|
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'kilo-cp-'))
|
|
git(dir, ['init', '-q'])
|
|
fs.writeFileSync(path.join(dir, 'tracked.txt'), 'one\n')
|
|
git(dir, ['add', '.'])
|
|
git(dir, ['commit', '-qm', 'init'])
|
|
return dir
|
|
}
|
|
|
|
test('createCheckpoint captures a patch and untracked list', () => {
|
|
const project = tempRepo()
|
|
const runDir = path.join(project, 'run')
|
|
fs.appendFileSync(path.join(project, 'tracked.txt'), 'two\n')
|
|
fs.writeFileSync(path.join(project, 'new.txt'), 'untracked\n')
|
|
const cp = createCheckpoint(project, runDir, 'before iteration 1', { id: 'iter-01' })
|
|
assert.equal(cp.id, 'iter-01')
|
|
assert.ok(cp.patchBytes > 0)
|
|
assert.ok(cp.untracked.includes('new.txt'))
|
|
assert.ok(fs.existsSync(cp.patchFile))
|
|
assert.equal(listCheckpoints(runDir).length, 1)
|
|
})
|
|
|
|
test('restoreCheckpoint reverts tracked edits and removes new untracked files', () => {
|
|
const project = tempRepo()
|
|
const runDir = path.join(project, 'run')
|
|
const cp = createCheckpoint(project, runDir, 'before iteration 1', { id: 'iter-01' })
|
|
fs.writeFileSync(path.join(project, 'tracked.txt'), 'changed\n')
|
|
fs.writeFileSync(path.join(project, 'created-later.txt'), 'junk\n')
|
|
const res = restoreCheckpoint(project, cp)
|
|
assert.equal(res.ok, true)
|
|
assert.equal(fs.readFileSync(path.join(project, 'tracked.txt'), 'utf8'), 'one\n')
|
|
assert.equal(fs.existsSync(path.join(project, 'created-later.txt')), false)
|
|
})
|
|
|
|
test('restoreCheckpoint keeps untracked files that existed at checkpoint time', () => {
|
|
const project = tempRepo()
|
|
const runDir = path.join(project, 'run')
|
|
fs.writeFileSync(path.join(project, 'preexisting.txt'), 'keep me\n')
|
|
const cp = createCheckpoint(project, runDir, 'before iteration 1', { id: 'iter-01' })
|
|
fs.writeFileSync(path.join(project, 'later.txt'), 'junk\n')
|
|
restoreCheckpoint(project, cp)
|
|
assert.equal(fs.existsSync(path.join(project, 'preexisting.txt')), true)
|
|
assert.equal(fs.existsSync(path.join(project, 'later.txt')), false)
|
|
})
|
|
|
|
test('restoreCheckpoint reproduces pre-existing changes captured at checkpoint time', () => {
|
|
const project = tempRepo()
|
|
const runDir = path.join(project, 'run')
|
|
fs.writeFileSync(path.join(project, 'tracked.txt'), 'one\npreexisting\n')
|
|
const cp = createCheckpoint(project, runDir, 'before iteration 1', { id: 'iter-01' })
|
|
fs.writeFileSync(path.join(project, 'tracked.txt'), 'totally different\n')
|
|
const res = restoreCheckpoint(project, cp)
|
|
assert.equal(res.ok, true)
|
|
assert.equal(fs.readFileSync(path.join(project, 'tracked.txt'), 'utf8'), 'one\npreexisting\n')
|
|
})
|
|
|
|
test('checkpoint helpers are no-ops outside a git repository', () => {
|
|
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'kilo-nogit-'))
|
|
assert.equal(isGitRepo(dir), false)
|
|
assert.equal(createCheckpoint(dir, path.join(dir, 'run'), 'x'), null)
|
|
})
|
|
|
|
test('restoreCheckpoint refuses to run when a submodule is dirty', () => {
|
|
const project = tempRepo()
|
|
const sub = path.join(project, 'sub')
|
|
fs.mkdirSync(sub)
|
|
git(sub, ['init', '-q'])
|
|
fs.writeFileSync(path.join(sub, 'x.txt'), 'a\n')
|
|
git(sub, ['add', '.'])
|
|
git(sub, ['commit', '-qm', 'sub init'])
|
|
fs.writeFileSync(
|
|
path.join(project, '.gitmodules'),
|
|
`[submodule "sub"]\n\tpath = sub\n\turl = ./sub\n`,
|
|
)
|
|
git(project, ['add', '.gitmodules'])
|
|
git(project, ['commit', '-qm', 'add submodule'])
|
|
// Dirty the submodule pointer by committing inside it and staging the pointer.
|
|
fs.appendFileSync(path.join(sub, 'x.txt'), 'b\n')
|
|
git(sub, ['commit', '-qam', 'sub change'])
|
|
git(project, ['add', 'sub'])
|
|
assert.deepEqual(dirtySubmodules(project), ['sub'])
|
|
const cp = createCheckpoint(project, path.join(project, 'run'), 'before iteration 1', { id: 'iter-01' })
|
|
const res = restoreCheckpoint(project, cp)
|
|
assert.equal(res.ok, false)
|
|
assert.match(res.error, /submodule/i)
|
|
})
|