138 lines
3.8 KiB
JavaScript
138 lines
3.8 KiB
JavaScript
import assert from "node:assert/strict";
|
|
import { test } from "node:test";
|
|
import {
|
|
buildRollbackBody,
|
|
buildRollbackPlan,
|
|
resolveRollback,
|
|
runRollback,
|
|
selectRollbackTarget,
|
|
} from "./rollback.mjs";
|
|
|
|
const release = (id, tag, options = {}) => ({
|
|
id,
|
|
tag_name: tag,
|
|
draft: false,
|
|
prerelease: false,
|
|
body: `Notes for ${tag}`,
|
|
assets: [{ name: "latest-mac.yml" }],
|
|
...options,
|
|
});
|
|
|
|
const releases = [
|
|
release(1, "v1.2.5"),
|
|
release(2, "v1.2.2"),
|
|
release(3, "v1.2.1"),
|
|
release(4, "v1.2.4", { draft: true }),
|
|
release(5, "v1.2.3", { prerelease: true }),
|
|
release(6, "alpha-macos-latest", { prerelease: true }),
|
|
];
|
|
|
|
function fakeGh(releaseList, latestTag) {
|
|
const calls = [];
|
|
const gh = (args) => {
|
|
calls.push(args);
|
|
if (args.includes("--paginate")) return JSON.stringify([releaseList]);
|
|
if (args.join(" ") === "api repos/different-ai/openwork/releases/latest") {
|
|
return JSON.stringify(releaseList.find((item) => item.tag_name === latestTag));
|
|
}
|
|
return "";
|
|
};
|
|
return { calls, gh };
|
|
}
|
|
|
|
test("selects the highest published stable semver below the bad release", () => {
|
|
assert.equal(selectRollbackTarget(releases, "v1.2.5").tag_name, "v1.2.2");
|
|
});
|
|
|
|
test("validates an explicit target", () => {
|
|
assert.equal(resolveRollback(releases, "v1.2.5", "v1.2.1").target.tag_name, "v1.2.1");
|
|
});
|
|
|
|
test("refuses targets that are not lower, stable, published releases", () => {
|
|
assert.throws(
|
|
() => resolveRollback(releases, "v1.2.5", "v1.2.5"),
|
|
/must differ/,
|
|
);
|
|
assert.throws(
|
|
() => resolveRollback(releases, "v1.2.5", "v1.2.4"),
|
|
/draft/,
|
|
);
|
|
assert.throws(
|
|
() => resolveRollback(releases, "v1.2.5", "v1.2.3"),
|
|
/prerelease/,
|
|
);
|
|
assert.throws(
|
|
() => resolveRollback(releases, "v1.2.5", "v9.9.9"),
|
|
/Target release not found/,
|
|
);
|
|
assert.throws(
|
|
() => resolveRollback(releases, "v1.2.2", "v1.2.5"),
|
|
/must be lower/,
|
|
);
|
|
});
|
|
|
|
test("dry-run builds the full plan without mutation", () => {
|
|
const fake = fakeGh(releases, "v1.2.5");
|
|
const originalLog = console.log;
|
|
console.log = () => {};
|
|
try {
|
|
const result = runRollback([], {
|
|
gh: fake.gh,
|
|
now: new Date("2026-08-10T12:00:00Z"),
|
|
});
|
|
assert.equal(result.execute, false);
|
|
assert.equal(result.plan.length, 3);
|
|
assert.equal(fake.calls.length, 2);
|
|
} finally {
|
|
console.log = originalLog;
|
|
}
|
|
});
|
|
|
|
test("execute runs the three mutating plan steps in order", () => {
|
|
const fake = fakeGh(releases, "v1.2.5");
|
|
const originalLog = console.log;
|
|
console.log = () => {};
|
|
try {
|
|
runRollback(["--execute"], {
|
|
gh: fake.gh,
|
|
now: new Date("2026-08-10T12:00:00Z"),
|
|
});
|
|
} finally {
|
|
console.log = originalLog;
|
|
}
|
|
|
|
assert.deepEqual(fake.calls.slice(2).map((args) => args.slice(0, 3)), [
|
|
["release", "edit", "v1.2.2"],
|
|
["release", "edit", "v1.2.5"],
|
|
["api", "--method", "PATCH"],
|
|
]);
|
|
});
|
|
|
|
test("idempotent plan skips completed steps", () => {
|
|
const body = "> ⚠️ Rolled back on 2026-08-10. Do not install; use v1.2.4 instead.\n\nOld notes";
|
|
const plan = buildRollbackPlan({
|
|
bad: release(1, "v1.2.5", { prerelease: true, body }),
|
|
target: release(2, "v1.2.4"),
|
|
latestTag: "v1.2.4",
|
|
now: new Date("2026-08-11T12:00:00Z"),
|
|
});
|
|
assert.deepEqual(plan.map((step) => step.skip), [true, true, true]);
|
|
});
|
|
|
|
test("keep-bad-listed skips only the demotion", () => {
|
|
const plan = buildRollbackPlan({
|
|
bad: release(1, "v1.2.5"),
|
|
target: release(2, "v1.2.4"),
|
|
latestTag: "v1.2.5",
|
|
keepBadListed: true,
|
|
});
|
|
assert.deepEqual(plan.map((step) => step.skip), [false, true, false]);
|
|
});
|
|
|
|
test("prepends a dated banner while preserving the existing body", () => {
|
|
assert.equal(
|
|
buildRollbackBody("Existing notes", "v1.2.4", new Date("2026-08-10T12:00:00Z")),
|
|
"> ⚠️ Rolled back on 2026-08-10. Do not install; use v1.2.4 instead.\n\nExisting notes",
|
|
);
|
|
});
|