/*
RFC-0644: Unit tests for commitWorkpieceIfDirty helper — auto-commit dirty workpiece before mission.reconcile.
- Not a contract source of truth — the module under test owns the authoritative behavior; fixtures are minimal scaffolding only.
- RFC-0644: initial unit tests for commitWorkpieceIfDirty — dirty, clean, and idempotent scenarios.
- RFC-1149: migrate ambient filesystem and process IO to the WorkspaceIO port across engine and shared
*/
import { test, expect, beforeEach, afterEach } from "vitest";
import { execSync } from "node:child_process";
import fs from "node:fs/promises";
import path from "node:path";
import os from "node:os";
import { commitWorkpieceIfDirty } from "./mission-git-commit.ts";
function git(cwd: string, args: string): string {
return execSync(`git ${args}`, {
cwd,
encoding: "utf-8",
stdio: ["pipe", "pipe", "pipe"],
}).trim();
}
let tmpDir: string;
let workpieceDir: string;
beforeEach(async () => {
tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "rfc-0644-test-"));
workpieceDir = path.join(tmpDir, "workpiece");
await fs.mkdir(workpieceDir, { recursive: true });
git(workpieceDir, "init");
git(workpieceDir, 'config user.email "test@example.com"');
git(workpieceDir, 'config user.name "Test"');
await fs.writeFile(path.join(workpieceDir, "system.md"), "# System\n");
git(workpieceDir, "add -A");
git(workpieceDir, 'commit -m "initial"');
});
afterEach(async () => {
await fs.rm(tmpDir, { recursive: true, force: true });
});
test("dirty workpiece → auto-commit created", async () => {
await fs.mkdir(path.join(workpieceDir, "public", "icons"), { recursive: true });
await fs.writeFile(path.join(workpieceDir, "public/icons/generated.svg"), "");
await fs.mkdir(path.join(workpieceDir, "src", "content"), { recursive: true });
await fs.writeFile(path.join(workpieceDir, "src/content/system.md"), "# Updated System\n");
const result = await commitWorkpieceIfDirty(workpieceDir, "test-mission");
expect(result.committed).toBe(true);
expect(result.commitSha).not.toBeNull();
expect(result.commitSha).toHaveLength(40);
const log = git(workpieceDir, "log --oneline -1");
expect(log).toContain("workpiece: auto-commit before reconcile test-mission");
const status = git(workpieceDir, "status --porcelain");
expect(status).toBe("");
});
test("clean workpiece → no auto-commit", async () => {
const initialSha = git(workpieceDir, "rev-parse HEAD");
const result = await commitWorkpieceIfDirty(workpieceDir, "test-mission");
expect(result.committed).toBe(false);
expect(result.commitSha).toBeNull();
const currentSha = git(workpieceDir, "rev-parse HEAD");
expect(currentSha).toBe(initialSha);
});
test("idempotent re-run on clean workpiece after first auto-commit", async () => {
await fs.mkdir(path.join(workpieceDir, "public", "icons"), { recursive: true });
await fs.writeFile(path.join(workpieceDir, "public/icons/generated.svg"), "");
const first = await commitWorkpieceIfDirty(workpieceDir, "test-mission");
expect(first.committed).toBe(true);
expect(first.commitSha).not.toBeNull();
const second = await commitWorkpieceIfDirty(workpieceDir, "test-mission");
expect(second.committed).toBe(false);
expect(second.commitSha).toBeNull();
const log = git(workpieceDir, "log --oneline -1");
expect(log).toContain("workpiece: auto-commit before reconcile test-mission");
});