Files
edgeever/scripts/release-issues.test.mjs
T
edgeever-updater[bot] 507cb892ad chore: update EdgeEver from v2.19.0
EdgeEver-Upstream-Commit: a80d9bfda1aad846e20882813758a59fe27bf8ca
2026-11-04 09:13:22 +00:00

85 lines
4.7 KiB
JavaScript

import { describe, expect, test } from "bun:test";
import { parseReleaseArgs } from "./release.mjs";
import { parseChangeIssues, finishResolvedIssues, validateResolvedIssues } from "./release-issues.mjs";
const release = { tagName: "v2.19.0", url: "https://github.com/tianma-if/edgeever/releases/tag/v2.19.0", targetCommitish: "abc123", isDraft: false, isPrerelease: false };
const issues = [{ number: 767, language: "en" }, { number: 751, language: "zh" }];
const fixture = () => {
const records = new Map(issues.map(({ number }) => [number, { number, state: "open", comments: [] }]));
const calls = [];
const client = {
getIssue: (number) => records.get(number),
listComments: (number) => records.get(number).comments,
comment: (number, body) => { calls.push(["comment", number]); records.get(number).comments.push({ body }); },
close: (number) => { calls.push(["close", number]); records.get(number).state = "closed"; },
};
return { records, calls, client };
};
const finish = (f, overrides = {}) => finishResolvedIssues({ release, auditedSha: "abc123", issues, client: f.client, ...overrides });
describe("release Issue follow-up", () => {
test("requires an explicit per-change declaration and rejects PRs and conflicting languages", () => {
expect(() => parseChangeIssues([], 1)).toThrow("Each change requires");
expect(parseChangeIssues(["767:en,751:zh", "767:en", "none"], 3)).toEqual([issues[1], issues[0]]);
for (const value of ["0:en", "-1:en", "767", "767:fr", "other/repo#767:en", "none,767:en", "9007199254740992:en"]) {
expect(() => parseChangeIssues([value], 1)).toThrow("Invalid");
}
expect(() => parseChangeIssues(["767:en,767:zh"], 1)).toThrow("Conflicting");
expect(() => validateResolvedIssues(issues, (number) => ({ number, pull_request: {} }))).toThrow("not a PR");
});
test("CLI cannot omit Issue decisions, and supports a separate published-release retry", () => {
const args = ["--bump", "patch", "--issue-title", "Fix", "--label", "bug", "--change-en", "Fix PDF", "--change-zh", "修复 PDF", "--change-locale", "ja:PDF を修正", "--change-commit", "abc1234"];
expect(() => parseReleaseArgs(args)).toThrow("--change-issue");
expect(parseReleaseArgs([...args, "--change-issue", "767:en"]).resolvedIssues).toEqual([issues[0]]);
expect(parseReleaseArgs(["--finish-issues", "v2.19.0"])).toMatchObject({ finishIssues: "v2.19.0" });
expect(() => parseReleaseArgs(["--finish-issues", "v2.19.0", "--bump", "patch"])).toThrow("only accepts");
expect(() => parseReleaseArgs(["--finish-issues", "latest"])).toThrow("stable");
});
test("never mutates Issues for Drafts, prereleases, failed or stale audit receipts", () => {
for (const overrides of [{ release: { ...release, isDraft: true } }, { release: { ...release, isPrerelease: true } }, { auditedSha: undefined }, { auditedSha: "old" }]) {
const f = fixture();
expect(() => finish(f, overrides)).toThrow("successful audits");
expect(f.calls).toEqual([]);
}
});
test("backlinks in the selected language before closing, without touching unrelated Issues", () => {
const f = fixture();
finish(f);
expect(f.calls).toEqual([["comment", 767], ["close", 767], ["comment", 751], ["close", 751]]);
expect(f.records.get(767).comments[0].body).toContain(`Released in [v2.19.0](${release.url})`);
expect(f.records.get(751).comments[0].body).toContain(`本问题已在 [v2.19.0](${release.url}) 发布`);
finish(f);
expect(f.calls).toHaveLength(4);
});
test("a comment failure keeps its Issue open, finishes other Issues, and can be retried", () => {
const f = fixture();
const comment = f.client.comment;
f.client.comment = (number, body) => { if (number === 767) throw new Error("network"); comment(number, body); };
expect(() => finish(f)).toThrow("--finish-issues v2.19.0");
expect(f.records.get(767).state).toBe("open");
expect(f.records.get(751).state).toBe("closed");
f.client.comment = comment;
finish(f);
expect(f.records.get(767).state).toBe("closed");
expect(f.records.get(751).comments).toHaveLength(1);
});
test("retry after a close failure reuses the backlink and preserves already-closed reasons", () => {
const f = fixture();
const close = f.client.close;
f.client.close = () => { throw new Error("network"); };
expect(() => finish(f)).toThrow("--finish-issues");
f.client.close = close;
f.records.get(751).state = "closed";
f.records.get(751).state_reason = "not_planned";
finish(f);
expect(f.records.get(767).comments).toHaveLength(1);
expect(f.records.get(751).state_reason).toBe("not_planned");
expect(f.calls.filter(([action]) => action === "close")).toEqual([["close", 767]]);
});
});