Files
stack/packages/queue/tests/commit.test.mjs
T
jason.woltjeandClaude Opus 5.5 6fb50cc0a8 fix(queue): genesis owner-not-reviewer check, assign wording, queue-commit HEAD-moved message, calendar dates (row 33, #1508)
Built by filbert, approved by darkwing in round 1 (#1508 comment 26651).
Manifest fe7da3ef, 10 paths. Suites green on an index export.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-04 01:25:06 -05:00

603 lines
26 KiB
JavaScript

// scripts/queue-commit.sh and the queue guard (8.12): F1, the bootstrap (F3)
// and the general cases. Every run is in a scratch repository with the
// scripts committed; nothing touches this checkout's .git.
//
// Schedules use PATH shims for git and node. Each shim calls
// <ctl>/on-git or <ctl>/on-node, when present, before and after the real
// command, and the call blocks, so an action runs at an exact point in the
// procedure. Actions run with the original PATH, so they reach real git.
import { strict as assert } from "node:assert";
import { spawn } from "node:child_process";
import { createHash } from "node:crypto";
import { chmodSync, existsSync, mkdirSync, readFileSync, rmSync, symlinkSync, unlinkSync, writeFileSync } from "node:fs";
import { join } from "node:path";
import { test } from "node:test";
import { sleepMs } from "../src/io.mjs";
import { cli, scratchRepo, sh } from "./helpers.mjs";
const QJSON = "docs/plans/queue.json";
const QMD = "docs/plans/QUEUE.md";
const FIX = `git reset -q -- ${QJSON} ${QMD}`;
const GUARD = "mosaic queue guard: refused";
const REAL_GIT = sh("sh", ["-c", "command -v git"]).stdout.trim();
function q(s) {
return `'${String(s).replaceAll("'", "'\\''")}'`;
}
function sha256(buf) {
return createHash("sha256").update(buf).digest("hex");
}
// A repository with the scripts, the guard installed by sage and, unless
// `bootstrap` is set, genesis committed through queue-commit.sh.
function ready(t, { bootstrap = false } = {}) {
const repo = scratchRepo(t, { scripts: true });
const ctl = join(repo.base, "ctl");
const shimDir = join(repo.base, "shims");
mkdirSync(ctl);
mkdirSync(shimDir);
const origPath = repo.env.PATH;
for (const [name, real] of [["git", REAL_GIT], ["node", process.execPath]]) {
const on = join(ctl, `on-${name}`);
writeFileSync(join(shimDir, name), [
"#!/bin/sh",
`if [ -x ${q(on)} ]; then PATH=${q(origPath)} ${q(on)} before "$@" </dev/null >>${q(join(ctl, "log"))} 2>&1; fi`,
`${q(real)} "$@"; s=$?`,
`if [ -x ${q(on)} ]; then PATH=${q(origPath)} ${q(on)} after "$s" "$@" </dev/null >>${q(join(ctl, "log"))} 2>&1; fi`,
"exit $s",
"",
].join("\n"), { mode: 0o755 });
}
const r = {
...repo,
ctl,
hook: join(repo.gitDir, "hooks/pre-commit"),
// Runs queue-commit.sh through the shims.
qc(args, env = {}) {
const res = sh("bash", [join(repo.root, "scripts/queue-commit.sh"), ...args], {
cwd: repo.root, env: { ...repo.env, PATH: `${shimDir}:${origPath}`, ...env }, allowFail: true,
});
return { code: res.status, out: res.stdout, err: res.stderr };
},
// Installs an action. `body` is bash; $phase is before or after, $status
// the exit status after, $sub the git subcommand, $@ the arguments.
on(name, body) {
writeFileSync(join(ctl, `on-${name}`), [
"#!/bin/bash",
"phase=$1; shift; status=",
'if [ "$phase" = after ]; then status=$1; shift; fi',
'a=("$@"); i=0; while [ "${a[i]:-}" = -C ]; do i=$((i+2)); done; sub=${a[i]:-}',
`CTL=${q(ctl)}; R=${q(repo.root)}`,
"unset GIT_INDEX_FILE",
'once() { [ -e "$CTL/once-$1" ] && return 1; : > "$CTL/once-$1"; }',
'echo "$phase $sub" >> "$CTL/calls-$(basename "$0")"',
body,
"",
].join("\n"), { mode: 0o755 });
},
off(name) {
rmSync(join(ctl, `on-${name}`), { force: true });
},
calls(name) {
const p = join(ctl, `calls-on-${name}`);
return existsSync(p) ? readFileSync(p, "utf8").split("\n").filter(Boolean) : [];
},
ctlFile(name) {
return readFileSync(join(ctl, name), "utf8").trim();
},
head() {
return repo.g("rev-parse", "HEAD").trim();
},
blob(rev, path) {
return sh("git", ["-C", repo.root, "cat-file", "blob", `${rev}:${path}`], { env: repo.env }).stdout;
},
revAt(rev) {
return JSON.parse(r.blob(rev, QJSON)).revision;
},
};
const inst = r.qc(["--install-hook", "--by", "sage"]);
assert.equal(inst.code, 0, inst.err);
if (!bootstrap) {
genesis(r);
const c = r.qc(["--genesis", "-m", "queue genesis"]);
assert.equal(c.code, 0, c.err);
}
return r;
}
function genesis(r) {
const g = cli(r, ["genesis", "--op", "genesis-2026-09-26", "--root", r.root, "--branch", "refactor", "--map", "agents/sage/work/queue-migration-map.md"], { by: "sage" });
assert.equal(g.code, 0, g.err);
}
let noteN = 0;
function note(r, id = 6) {
const res = cli(r, ["note", String(id), `note ${++noteN}`, "--op", `note-op-${noteN}`], { by: "darkwing" });
assert.equal(res.code, 0, res.err);
return res;
}
function stageFile(r, name, text = "source\n") {
writeFileSync(join(r.root, name), text);
r.g("add", "--", name);
}
function assertUntouched(r, head, res, why) {
assert.equal(res.code, 2, `${why}: ${res.err}`);
assert.equal(r.head(), head, why);
assert.ok(!r.calls("git").includes("before update-ref"), `${why}: update-ref ran`);
}
// --- F1 ---
test("F1: an ordinary commit after update-ref is refused until step 8; then it commits and the queue stays at C", (t) => {
const r = ready(t);
note(r);
r.on("git", `if [ "$phase $sub $status" = "after update-ref 0" ]; then
echo src > "$R/src.txt"; git -C "$R" add src.txt
git -C "$R" commit -q -m ordinary 2> "$CTL/commit-err"; echo $? > "$CTL/commit-rc"
fi`);
const res = r.qc(["-m", "queue rev 1"]);
assert.equal(res.code, 0, res.err);
assert.notEqual(r.ctlFile("commit-rc"), "0");
assert.match(r.ctlFile("commit-err"), new RegExp(GUARD));
assert.match(r.ctlFile("commit-err"), new RegExp(FIX.replaceAll(".", "\\.")));
const c = r.head();
assert.equal(r.revAt(c), 1);
r.off("git");
r.g("commit", "-q", "-m", "ordinary");
assert.equal(r.g("rev-parse", "HEAD^").trim(), c);
assert.equal(r.revAt("HEAD"), 1);
assert.equal(r.blob("HEAD", "src.txt"), "src\n");
});
// A commit paused in its editor after its guard passed against H. Whether
// git holds index.lock during the editor depends on the form: git 2.55
// doesn't for plain `commit -e` and does for `commit -e -- path`. Step 8
// reconciles when the lock is free and exits 3 when it isn't; either way
// the paused commit loses at its own HEAD update.
async function pausedCommit(t, form) {
const r = ready(t);
note(r);
stageFile(r, "src.txt");
const h = r.head();
const started = join(r.ctl, "editor-started");
const go = join(r.ctl, "editor-go");
const editor = join(r.ctl, "editor.sh");
writeFileSync(editor, `#!/bin/sh\n: > ${q(started)}\nwhile [ ! -e ${q(go)} ]; do sleep 0.05; done\necho "ordinary" > "$1"\n`, { mode: 0o755 });
const child = spawn("git", ["-C", r.root, "commit", "-e", "-q", ...form], { env: { ...r.env, GIT_EDITOR: editor }, stdio: ["ignore", "pipe", "pipe"] });
let childErr = "";
child.stderr.on("data", (d) => { childErr += d; });
const exited = new Promise((resolve) => child.on("exit", resolve));
for (let i = 0; i < 200 && !existsSync(started); i++) sleepMs(50);
assert.ok(existsSync(started), "the editor never started");
const locked = existsSync(join(r.gitDir, "index.lock"));
t.diagnostic(`git commit -e${form.map((a) => ` ${a}`).join("")}: index.lock ${locked ? "held" : "free"} during the editor`);
const res = r.qc(["-m", "queue rev 1"]);
writeFileSync(go, "");
const code = await exited;
assert.equal(res.code, locked ? 3 : 0, `index.lock ${locked ? "held" : "free"} during the editor: ${res.err}`);
if (locked) assert.match(res.err, /another git process holds \.git\/index\.lock/);
const c = r.head();
assert.equal(r.g("rev-parse", "HEAD^").trim(), h);
assert.equal(r.revAt(c), 1);
assert.notEqual(code, 0);
assert.match(childErr, new RegExp(`cannot lock ref 'HEAD': is at ${c} but expected ${h}`));
assert.equal(r.head(), c, "the old queue landed on top of C");
if (locked) r.g("reset", "-q", "--", "docs/plans/queue.json", "docs/plans/QUEUE.md");
assert.equal(r.g("diff", "--cached", "--name-only").trim(), "src.txt");
r.g("commit", "-q", "-m", "ordinary");
assert.equal(r.revAt("HEAD"), 1);
return locked;
}
test("F1: a plain `commit -e` whose guard ran before update-ref fails at its own HEAD update", async (t) => {
await pausedCommit(t, []);
});
test("F1: a `commit -e -- path` whose guard ran before update-ref fails at its own HEAD update", async (t) => {
await pausedCommit(t, ["--", "src.txt"]);
});
test("F1: step 8 with index.lock held exits 3, and ordinary commits stay refused until the printed command runs", (t) => {
const r = ready(t);
note(r);
r.on("git", `if [ "$phase $sub" = "before ls-files" ] && once lock; then : > "$R/.git/index.lock"; fi`);
const res = r.qc(["-m", "queue rev 1"]);
assert.equal(res.code, 3, res.err);
assert.match(res.err, /holds \.git\/index\.lock/);
assert.ok(res.err.includes(`run: ${FIX}`) || res.err.includes(`until this runs: ${FIX}`));
const c = r.head();
assert.equal(r.revAt(c), 1);
r.off("git");
unlinkSync(join(r.gitDir, "index.lock"));
stageFile(r, "src.txt");
const refused = sh("git", ["-C", r.root, "commit", "-q", "-m", "ordinary"], { env: r.env, allowFail: true });
assert.notEqual(refused.status, 0);
assert.match(refused.stderr, new RegExp(GUARD));
assert.equal(r.head(), c);
sh("sh", ["-c", FIX], { cwd: r.root, env: r.env });
r.g("commit", "-q", "-m", "ordinary");
assert.equal(r.revAt("HEAD"), 1);
assert.equal(r.g("rev-parse", "HEAD^").trim(), c);
});
test("F1: HEAD moving after the step-7 guard check and before update-ref: refused, nothing published", (t) => {
const r = ready(t);
note(r);
const h = r.head();
r.on("git", `if [ "$phase $sub" = "before update-ref" ] && once move; then
echo other > "$R/other.txt"; git -C "$R" add other.txt; git -C "$R" commit -q -m other
fi`);
const res = r.qc(["-m", "queue rev 1"]);
assert.equal(res.code, 1, res.err);
assert.match(res.err, new RegExp(`refs/heads/refactor moved since ${h}; nothing published; start again`));
assert.equal(r.g("rev-parse", "HEAD^").trim(), h);
assert.equal(r.revAt("HEAD"), 0);
assert.equal(r.g("log", "-1", "--format=%s").trim(), "other");
});
test("F1: H is recorded before the canary, so HEAD moving during the step-1 canary is refused at step 7", (t) => {
const r = ready(t);
note(r);
const h = r.head();
r.on("git", `if [ "$phase $sub" = "before hook" ] && once move; then
echo other > "$R/other.txt"; git -C "$R" add other.txt; git -C "$R" commit -q -m other
fi`);
const res = r.qc(["-m", "queue rev 1"]);
assert.equal(res.code, 2, res.err);
assert.match(res.err, new RegExp(`refused \\(step7\\): HEAD moved since ${h} was recorded \\(another commit landed\\); run queue-commit.sh again`));
assert.equal(r.g("log", "-1", "--format=%s").trim(), "other");
assert.equal(r.g("rev-parse", "HEAD^").trim(), h);
assert.equal(r.revAt("HEAD"), 0);
// A commit outside the queue files passes the step-1 canary. The step-7
// check refuses before its canary, so update-ref never runs.
assert.equal(r.calls("git").filter((c) => c === "before hook").length, 2);
assert.ok(!r.calls("git").includes("before update-ref"));
});
// Another queue commit landing after H is recorded. The canary's index
// holds H's tree and the hook diffs it with the live HEAD, so before
// 2026-10-04 the clean run failed and the script blamed the guard.
const NESTED = 'bash "$R/scripts/queue-commit.sh" -m nested >> "$CTL/nested.log" 2>&1; echo $? > "$CTL/nested-exit"';
test("F1: a queue commit landing after H is recorded: step 1 says HEAD moved, not the guard", (t) => {
const r = ready(t);
note(r);
const h = r.head();
r.on("git", `if [ "$phase $sub" = "after symbolic-ref" ] && once move; then ${NESTED}; fi`);
const res = r.qc(["-m", "queue rev 1"]);
assert.equal(r.ctlFile("nested-exit"), "0", r.ctlFile("nested.log"));
assert.equal(res.code, 2, res.err);
assert.match(res.err, new RegExp(`refused \\(step1\\): HEAD moved since ${h} was recorded \\(another commit landed\\); run queue-commit.sh again`));
assert.doesNotMatch(res.err, /not running the queue guard/);
assert.equal(r.calls("git").filter((c) => c === "before hook").length, 0);
assert.equal(r.g("log", "-1", "--format=%s").trim(), "nested");
assert.equal(r.revAt("HEAD"), 1);
// Running it again, as the message says, finds the queue already committed.
r.off("git");
note(r);
const again = r.qc(["-m", "queue rev 2"]);
assert.equal(again.code, 0, again.err);
assert.equal(r.revAt("HEAD"), 2);
});
test("F1: a queue commit landing between the HEAD check and the canary: the failed clean run is reported as HEAD moved", (t) => {
const r = ready(t);
note(r);
const h = r.head();
r.on("git", `if [ "$phase $sub" = "before hook" ] && once move; then ${NESTED}; fi`);
const res = r.qc(["-m", "queue rev 1"]);
assert.equal(r.ctlFile("nested-exit"), "0", r.ctlFile("nested.log"));
assert.equal(res.code, 2, res.err);
assert.match(res.err, new RegExp(`refused \\(step1\\): HEAD moved since ${h} was recorded \\(another commit landed\\); run queue-commit.sh again`));
assert.doesNotMatch(res.err, /not running the queue guard/);
// The clean run did run, and failed on the moved HEAD.
assert.equal(r.calls("git").filter((c) => c === "before hook").length, 1);
assert.ok(r.calls("git").includes("after hook"));
assert.equal(r.g("log", "-1", "--format=%s").trim(), "nested");
assert.equal(r.revAt("HEAD"), 1);
});
test("F1: a shared-index change during the procedure is not committed", (t) => {
const r = ready(t);
note(r);
r.on("git", `if [ "$phase $sub" = "before commit-tree" ]; then echo late > "$R/late.txt"; git -C "$R" add late.txt; fi`);
const res = r.qc(["-m", "queue rev 1"]);
assert.equal(res.code, 0, res.err);
assert.deepEqual(r.g("diff-tree", "-r", "--name-only", "HEAD^", "HEAD").trim().split("\n"), [QMD, QJSON]);
assert.equal(r.g("diff", "--cached", "--name-only").trim(), "late.txt");
});
test("F1: a queue path staged after update-ref: step 8 stops and touches nothing", (t) => {
const r = ready(t);
note(r);
r.on("git", `if [ "$phase $sub $status" = "after update-ref 0" ]; then
git -C "$R" add docs/plans/queue.json; sha256sum "$R/.git/index" | cut -d' ' -f1 > "$CTL/index-sha"
fi`);
const res = r.qc(["-m", "queue rev 1"]);
assert.equal(res.code, 3, res.err);
assert.match(res.err, /someone staged a queue path; the index was not touched/);
assert.equal(sha256(readFileSync(join(r.gitDir, "index"))), r.ctlFile("index-sha"));
assert.equal(r.revAt("HEAD"), 1);
});
test("F1: a missing or a different hook refuses", (t) => {
const r = ready(t);
note(r);
const h = r.head();
const bytes = readFileSync(r.hook);
r.on("git", "");
unlinkSync(r.hook);
assertUntouched(r, h, r.qc(["-m", "x"]), "missing");
assert.match(r.qc(["-m", "x"]).err, /queue guard is not installed/);
writeFileSync(r.hook, `${bytes}\n# edited\n`, { mode: 0o755 });
const diff = r.qc(["-m", "x"]);
assertUntouched(r, h, diff, "different");
assert.match(diff.err, /differs from scripts\/git-hooks\/pre-commit/);
const inst = r.qc(["--install-hook", "--by", "sage"]);
assert.equal(inst.code, 2);
assert.match(inst.err, /a different pre-commit hook exists/);
});
test("F1: same bytes without the exec bit, a symlinked hook, and core.hooksPath in the local or global scope each refuse before update-ref", (t) => {
const r = ready(t);
note(r);
const h = r.head();
const bytes = readFileSync(r.hook);
r.on("git", "");
chmodSync(r.hook, 0o644);
let res = r.qc(["-m", "x"]);
assertUntouched(r, h, res, "no exec bit");
assert.match(res.err, /not executable/);
chmodSync(r.hook, 0o755);
const target = join(r.base, "hook-copy");
writeFileSync(target, bytes, { mode: 0o755 });
unlinkSync(r.hook);
symlinkSync(target, r.hook);
res = r.qc(["-m", "x"]);
assertUntouched(r, h, res, "symlink");
assert.match(res.err, /is a symlink/);
unlinkSync(r.hook);
writeFileSync(r.hook, bytes, { mode: 0o755 });
r.g("config", "core.hooksPath", ".githooks");
res = r.qc(["-m", "x"]);
assertUntouched(r, h, res, "local hooksPath");
assert.match(res.err, /core\.hooksPath is set \(local/);
r.g("config", "--unset", "core.hooksPath");
r.g("config", "--global", "core.hooksPath", "/nonexistent-hooks");
res = r.qc(["-m", "x"]);
assertUntouched(r, h, res, "global hooksPath");
assert.match(res.err, /core\.hooksPath is set \(global/);
r.g("config", "--global", "--unset", "core.hooksPath");
res = r.qc(["-m", "queue rev 1"]);
assert.equal(res.code, 0, res.err);
});
test("F1: the canary refuses a hook that git would not run", (t) => {
const r = ready(t);
note(r);
const h = r.head();
r.on("git", `if [ "$phase $sub" = "before hook" ] && once chmod; then chmod 0644 "$R/.git/hooks/pre-commit"; fi`);
const res = r.qc(["-m", "x"]);
assertUntouched(r, h, res, "canary");
assert.match(res.err, /refused \(step1\): the canary's clean run failed/);
});
test("F1: the guard deactivated after step 1 is refused at the step-7 recheck", (t) => {
const r = ready(t);
note(r);
const h = r.head();
r.on("git", `if [ "$phase $sub" = "after commit-tree" ]; then chmod 0644 "$R/.git/hooks/pre-commit"; fi`);
const res = r.qc(["-m", "x"]);
assertUntouched(r, h, res, "step 7");
assert.match(res.err, /refused \(step7\): \.git\/hooks\/pre-commit is not executable/);
assert.ok(r.calls("git").includes("after commit-tree"));
});
// --- bootstrap (F3) ---
test("bootstrap: implementation-only HEAD, the guard, genesis, the --genesis commit, then an extending commit", (t) => {
const r = ready(t, { bootstrap: true });
const impl = r.head();
assert.equal(sh("git", ["-C", r.root, "cat-file", "-e", `HEAD:${QJSON}`], { env: r.env, allowFail: true }).status, 128);
assert.equal(readFileSync(r.hook, "utf8"), r.blob("HEAD", "scripts/git-hooks/pre-commit"));
genesis(r);
const g = r.qc(["--genesis", "-m", "queue genesis"]);
assert.equal(g.code, 0, g.err);
assert.match(g.err, /ok verify --snapshot rev 0: pair valid, view current, genesis alone/);
assert.equal(r.g("rev-parse", "HEAD^").trim(), impl);
assert.equal(r.revAt("HEAD"), 0);
assert.equal(r.g("status", "--porcelain").trim(), "");
note(r);
const c = r.qc(["-m", "queue rev 1"]);
assert.equal(c.code, 0, c.err);
assert.match(c.err, /extends the base/);
assert.equal(r.revAt("HEAD"), 1);
assert.equal(r.g("status", "--porcelain").trim(), "");
});
test("bootstrap: --genesis with a base present, no base without --genesis, an op before the first commit, a changed map, another branch", (t) => {
const r = ready(t, { bootstrap: true });
let res = r.qc(["-m", "x"]);
assert.equal(res.code, 2);
assert.match(res.err, /HEAD has no docs\/plans\/queue\.json; the first queue commit needs --genesis/);
genesis(r);
res = r.qc(["-m", "x"]);
assert.equal(res.code, 2);
assert.match(res.err, /needs --genesis/);
const op = cli(r, ["note", "6", "early", "--op", "note-early-1"], { by: "darkwing" });
assert.equal(op.code, 2);
assert.match(op.err, /genesis not committed/);
// The map changed in HEAD after genesis read it.
const mapPath = join(r.root, "agents/sage/work/queue-migration-map.md");
const mapText = readFileSync(mapPath, "utf8");
writeFileSync(mapPath, `${mapText}\nEdited.\n`);
r.g("add", "agents/sage/work/queue-migration-map.md");
r.g("commit", "-q", "-m", "map edit");
res = r.qc(["--genesis", "-m", "x"]);
assert.equal(res.code, 2);
assert.match(res.err, /genesis read map blob [0-9a-f]{40}, but HEAD's agents\/sage\/work\/queue-migration-map\.md is/);
writeFileSync(mapPath, mapText);
r.g("add", "agents/sage/work/queue-migration-map.md");
r.g("commit", "-q", "-m", "map restored");
r.g("checkout", "-q", "-b", "other");
res = r.qc(["--genesis", "-m", "x"]);
assert.equal(res.code, 2);
r.g("checkout", "-q", "refactor");
res = r.qc(["--genesis", "-m", "queue genesis"]);
assert.equal(res.code, 0, res.err);
res = r.qc(["--genesis", "-m", "x"]);
assert.equal(res.code, 2);
assert.match(res.err, /--genesis, but HEAD already has docs\/plans\/queue\.json/);
});
test("bootstrap: the archived tests and validator run outside any repository", (t) => {
const r = ready(t);
note(r);
r.on("node", `if [ "$phase" = before ] && { [ "$1" = --test ] || [ "$3" = --snapshot ]; }; then
{ echo "$1|$(pwd -P)|\${GIT_CEILING_DIRECTORIES:-}|$(git rev-parse --show-toplevel >/dev/null 2>&1; echo $?)"; } >> "$CTL/node-where"
fi`);
const res = r.qc(["-m", "queue rev 1"]);
assert.equal(res.code, 0, res.err);
const lines = r.ctlFile("node-where").split("\n");
assert.equal(lines.length, 2);
for (const line of lines) {
const [first, cwd, ceiling, rc] = line.split("|");
assert.ok(first === "--test" || first === "packages/queue/src/cli.mjs", first);
assert.ok(!cwd.startsWith(`${r.root}/`) && cwd !== r.root, cwd);
assert.ok(cwd.startsWith(`${ceiling}/`), `${cwd} under ${ceiling}`);
assert.notEqual(rc, "0", "a repository was found around the archive");
}
});
// --- general ---
test("general: an unrelated staged file stays staged, and the committed blobs are the snapshot bytes", (t) => {
const r = ready(t);
note(r);
stageFile(r, "src.txt");
const res = r.qc(["-m", "queue rev 1"]);
assert.equal(res.code, 0, res.err);
const m = /snapshot rev 1: queue\.json ([0-9a-f]{64}), QUEUE\.md ([0-9a-f]{64})/.exec(res.out);
assert.ok(m, res.out);
assert.equal(sha256(r.blob("HEAD", QJSON)), m[1]);
assert.equal(sha256(r.blob("HEAD", QMD)), m[2]);
assert.equal(r.blob("HEAD", QJSON), readFileSync(join(r.root, QJSON), "utf8"));
assert.equal(r.g("diff", "--cached", "--name-only").trim(), "src.txt");
assert.equal(sh("git", ["-C", r.root, "cat-file", "-e", "HEAD:src.txt"], { env: r.env, allowFail: true }).status, 128);
});
test("general: a queue write after the snapshot is not committed", (t) => {
const r = ready(t);
note(r);
const cliPath = join(r.root, "packages/queue/src/cli.mjs");
r.on("node", `if [ "$phase $status $2" = "after 0 snapshot" ] && once write; then
MOSAIC_AGENT_NAME=darkwing node ${q(cliPath)} note 6 late --op note-late-1 > "$CTL/late-out" 2>&1
fi`);
const res = r.qc(["-m", "queue rev 1"]);
assert.equal(res.code, 0, res.err);
assert.match(r.ctlFile("late-out"), /ok note-late-1 rev 2/);
assert.equal(r.revAt("HEAD"), 1);
assert.equal(JSON.parse(readFileSync(join(r.root, QJSON), "utf8")).revision, 2);
r.off("node");
const again = r.qc(["-m", "queue rev 2"]);
assert.equal(again.code, 0, again.err);
assert.equal(r.revAt("HEAD"), 2);
});
test("general: a snapshot whose log does not extend the base refuses", (t) => {
const r = ready(t);
note(r);
// A bypass commit of rev 1, then the working files rolled back to rev 0
// and the loss accepted: the working log forks from HEAD's.
r.g("add", QJSON, QMD);
r.g("commit", "-q", "--no-verify", "-m", "bypass");
writeFileSync(join(r.root, QJSON), r.blob("HEAD^", QJSON));
writeFileSync(join(r.root, QMD), r.blob("HEAD^", QMD));
const acc = cli(r, ["accept-history", "--op", "accept-1", "--reason", "test rollback", "--yes"], { by: "sage" });
assert.equal(acc.code, 0, acc.err);
const h = r.head();
r.on("git", "");
const res = r.qc(["-m", "x"]);
assertUntouched(r, h, res, "fork");
assert.match(res.err, /the snapshot's log does not extend the base's \(base rev 1, snapshot rev 1\)/);
});
test("general: install-hook privilege, repair of a same-bytes hook, and its refusals", (t) => {
const r = ready(t, { bootstrap: true });
let res = r.qc(["--install-hook"]);
assert.equal(res.code, 2);
assert.match(res.err, /no actor/);
res = r.qc(["--install-hook"], { MOSAIC_AGENT_NAME: "darkwing" });
assert.equal(res.code, 2);
assert.match(res.err, /privileged \(jason or sage\), not darkwing/);
chmodSync(r.hook, 0o644);
res = r.qc(["--install-hook"], { MOSAIC_AGENT_NAME: "jason" });
assert.equal(res.code, 0, res.err);
assert.match(res.out, /the same bytes were already there/);
assert.equal(sh("stat", ["-c", "%a", r.hook]).stdout.trim(), "755");
const bytes = readFileSync(r.hook);
unlinkSync(r.hook);
const target = join(r.base, "hook-copy");
writeFileSync(target, bytes, { mode: 0o755 });
symlinkSync(target, r.hook);
res = r.qc(["--install-hook", "--by", "sage"]);
assert.equal(res.code, 2);
assert.match(res.err, /is a symlink/);
unlinkSync(r.hook);
r.g("config", "core.hooksPath", ".githooks");
res = r.qc(["--install-hook", "--by", "sage"]);
assert.equal(res.code, 2);
assert.match(res.err, /core\.hooksPath is set/);
assert.ok(!existsSync(r.hook));
});
test("general: environment overrides, a linked worktree and usage", (t) => {
const r = ready(t);
note(r);
const h = r.head();
for (const k of ["GIT_DIR", "GIT_WORK_TREE", "GIT_INDEX_FILE", "GIT_COMMON_DIR"]) {
const res = r.qc(["-m", "x"], { [k]: join(r.root, ".git") });
assert.equal(res.code, 2, k);
assert.match(res.err, new RegExp(`${k} is set`));
}
const wt = join(r.base, "wt");
r.g("worktree", "add", "-q", "-b", "wt", wt);
const w = sh("bash", [join(wt, "scripts/queue-commit.sh"), "-m", "x"], { cwd: wt, env: r.env, allowFail: true });
assert.equal(w.status, 2);
assert.match(w.stderr, /linked worktree/);
for (const args of [[], ["-m", ""], ["--by", "sage", "-m", "x"], ["--install-hook", "-m", "x"], ["--bogus"], ["-m"]]) {
assert.equal(r.qc(args).code, 4, JSON.stringify(args));
}
assert.equal(r.head(), h);
});
test("general: a queue path staged before the run refuses at step 1", (t) => {
const r = ready(t);
note(r);
const h = r.head();
r.g("add", QMD);
r.on("git", "");
const res = r.qc(["-m", "x"]);
assertUntouched(r, h, res, "staged");
assert.match(res.err, /the shared index has staged changes to docs\/plans\/queue\.json or docs\/plans\/QUEUE\.md/);
assert.ok(res.err.includes(FIX));
assert.ok(!r.calls("git").includes("before commit-tree"));
});
test("general: HEAD's queue tests failing in the archive refuse", (t) => {
const r = ready(t);
writeFileSync(join(r.root, "packages/queue/tests/fail.test.mjs"), 'import { test } from "node:test";\ntest("fails", () => { throw new Error("archived failure"); });\n');
r.g("add", "packages/queue/tests/fail.test.mjs");
r.g("commit", "-q", "-m", "failing test");
note(r);
const h = r.head();
r.on("git", "");
const res = r.qc(["-m", "x"]);
assertUntouched(r, h, res, "archive tests");
assert.match(res.err, /archived failure/);
assert.match(res.err, /refused: HEAD's queue tests failed in the archive/);
});