Files
openclaw/test/scripts/release-notes-ledger.test.ts
Peter Steinberger c2b4ac02da fix(release): align correction sections, retries, and merged ledger semantics
Codex review + test findings on the pipeline convergence:
- candidate changelog provenance now validates the same section the
  renderer publishes (correction tags may carry their own heading) via a
  shared correctionVersionForTag helper
- guard_existing_public_release accepts a canonical proofless body with
  intact dependency evidence, matching the renderer's documented
  proof-omitted-at-limit state; retries re-append the proof
- ledgerFor regains main's noteReferences threading so prose-cited PRs
  keep their contribution-record rows, and the ledger/verify tests pin
  the merged entry shape (externalReferences) and highlight gate
2026-07-10 02:06:52 -07:00

222 lines
5.8 KiB
TypeScript

import { describe, expect, it } from "vitest";
import {
contributionRecordFor,
ledgerChecks,
ledgerFor,
renderContributionRecordEntry,
} from "../../.agents/skills/openclaw-changelog-update/scripts/verify-release-notes.mjs";
describe("renderContributionRecordEntry", () => {
it("keeps source and linked issue references without repeating PR titles", () => {
expect(
renderContributionRecordEntry({
number: 123,
title: "Fix local openclaw/openclaw#45 and openclaw/imsg#141",
linkedIssues: [{ number: 45 }, { number: 67 }],
thanks: ["alice", "bob"],
}),
).toBe("- **PR #123** Related #45, openclaw/imsg#141, #67. Thanks @alice and @bob.");
});
it("deduplicates title references and retains seeded cross-repository references", () => {
expect(
renderContributionRecordEntry({
number: 124,
title: "Fix #45, #45, and OpenClaw/imsg#141",
externalReferences: ["openclaw/imsg#141"],
priorReferences: [67],
linkedIssues: [{ number: 45 }],
thanks: [],
}),
).toBe("- **PR #124** Related #45, OpenClaw/imsg#141, #67.");
});
it("renders every source PR even without issue references or credits", () => {
expect(
renderContributionRecordEntry({
number: 456,
title: "Internal cleanup",
linkedIssues: [],
thanks: [],
}),
).toBe("- **PR #456**");
});
it("retains references and credits when a compact record is seeded again", () => {
const line = "- **PR #125** Related #45, openclaw/imsg#141. Thanks @alice and @bob.";
const record = contributionRecordFor({
source: [
"## 2026.7.1",
"",
"### Complete contribution record",
"",
"#### Pull requests",
"",
line,
].join("\n"),
});
const seeded = record.pullRequests.get(125);
expect(seeded).toEqual({
externalReferences: ["openclaw/imsg#141"],
references: [45],
thanks: ["alice", "bob"],
});
expect(
renderContributionRecordEntry({
number: 125,
title: "Title changed after release",
priorReferences: seeded?.references,
externalReferences: seeded?.externalReferences,
linkedIssues: [],
thanks: seeded?.thanks ?? [],
}),
).toBe(line);
});
it("retains seeded credits when the production ledger is rebuilt", () => {
const priorRecord = contributionRecordFor({
source: [
"## 2026.7.1",
"",
"### Complete contribution record",
"",
"#### Pull requests",
"",
"- **PR #125** Thanks @alice and @bob.",
].join("\n"),
});
const nodes = new Map([
[
125,
{
__typename: "PullRequest",
author: { __typename: "User", login: "carol" },
closingIssuesReferences: { nodes: [] },
mergedAt: "2026-07-08T00:00:00Z",
title: "fix: keep release credits",
},
],
]);
const result = ledgerFor(
"v2026.6.11",
"HEAD",
[125],
nodes,
new Map(),
new Map(),
{ issuesByPullRequest: new Map() },
priorRecord,
new Set([125]),
new Set(),
new Set(),
new Set(),
new Set(),
[],
Date.parse("2026-07-09T00:00:00Z"),
);
expect(result.ledger).toContain("- **PR #125** Thanks @carol and @alice and @bob.");
});
it("retains references from a verbose record when the source title changes", () => {
const record = contributionRecordFor({
source: [
"## 2026.7.1",
"",
"### Complete contribution record",
"",
"#### Pull requests",
"",
"- **PR #126** Fix #46 and openclaw/imsg#142. Related #68. Thanks @alice.",
].join("\n"),
});
const seeded = record.pullRequests.get(126);
expect(seeded).toEqual({
externalReferences: ["openclaw/imsg#142"],
references: [46, 68],
thanks: ["alice"],
});
});
it("requires complete reference tokens rather than matching substrings", () => {
const source = [
"## 2026.7.1",
"",
"### Highlights",
"",
"- Highlight one.",
"- Highlight two.",
"- Highlight three.",
"- Highlight four.",
"- Highlight five.",
"",
"### Changes",
"",
"### Fixes",
"",
"### Complete contribution record",
"",
"#### Pull requests",
"",
"- **PR #456** Related openclaw/imsg#141.",
].join("\n");
const entry = {
number: 456,
title: "Internal cleanup",
editorialEligible: false,
priorReferences: [45, 141],
externalReferences: [],
linkedIssues: [],
thanks: [],
};
expect(
ledgerChecks({ source }, [entry], new Map([[456, { __typename: "PullRequest" }]]), []),
).toEqual([
"missing #45 on contribution record for PR #456",
"missing #141 on contribution record for PR #456",
]);
});
it("accepts case-only differences in cross-repository references", () => {
const line = "- **PR #127** Related OpenClaw/imsg#143.";
const source = [
"## 2026.7.1",
"",
"### Highlights",
"",
"- Highlight one.",
"- Highlight two.",
"- Highlight three.",
"- Highlight four.",
"- Highlight five.",
"",
"### Changes",
"",
"### Fixes",
"",
"### Complete contribution record",
"",
"#### Pull requests",
"",
line,
].join("\n");
const entry = {
number: 127,
title: "Internal cleanup",
editorialEligible: false,
priorReferences: [],
externalReferences: ["openclaw/imsg#143"],
linkedIssues: [],
thanks: [],
};
expect(
ledgerChecks({ source }, [entry], new Map([[127, { __typename: "PullRequest" }]]), []),
).toEqual([]);
});
});