mirror of
https://github.com/openclaw/openclaw.git
synced 2026-06-04 17:24:06 +00:00
196 lines
5.8 KiB
TypeScript
196 lines
5.8 KiB
TypeScript
import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
import os from "node:os";
|
|
import path from "node:path";
|
|
import { describe, expect, it } from "vitest";
|
|
import {
|
|
extractCurrentPackageChangelog,
|
|
preparePackageChangelog,
|
|
resolvePackageChangelogVersions,
|
|
restorePackageChangelog,
|
|
} from "../../scripts/package-changelog.mjs";
|
|
|
|
function changelog(strings: TemplateStringsArray, ...values: string[]) {
|
|
return `${String.raw({ raw: strings }, ...values)
|
|
.replace(/^\n/u, "")
|
|
.trimEnd()}\n`;
|
|
}
|
|
|
|
const cumulativeChangelog = changelog`
|
|
# Changelog
|
|
Docs: https://docs.openclaw.ai
|
|
## Unreleased
|
|
### Fixes
|
|
- Pending note.
|
|
## 2026.5.28
|
|
### Highlights
|
|
- Current highlight.
|
|
### Changes
|
|
- Current change.
|
|
### Fixes
|
|
- Current fix.
|
|
## 2026.5.27
|
|
### Highlights
|
|
- Older highlight.
|
|
`;
|
|
|
|
describe("package-changelog", () => {
|
|
it("maps release-channel package versions to package changelog candidate headings", () => {
|
|
expect(resolvePackageChangelogVersions("2026.5.28")).toEqual(["2026.5.28"]);
|
|
expect(resolvePackageChangelogVersions("2026.5.28-1")).toEqual(["2026.5.28-1"]);
|
|
expect(resolvePackageChangelogVersions("2026.5.28-beta.1")).toEqual([
|
|
"2026.5.28-beta.1",
|
|
"2026.5.28",
|
|
"Unreleased",
|
|
]);
|
|
expect(resolvePackageChangelogVersions("2026.5.28-alpha.2")).toEqual([
|
|
"2026.5.28-alpha.2",
|
|
"2026.5.28",
|
|
"Unreleased",
|
|
]);
|
|
});
|
|
|
|
it("extracts only the package version stable release section", () => {
|
|
expect(extractCurrentPackageChangelog(cumulativeChangelog, "2026.5.28-beta.1")).toBe(
|
|
changelog`
|
|
# Changelog
|
|
Docs: https://docs.openclaw.ai
|
|
|
|
## 2026.5.28
|
|
### Highlights
|
|
- Current highlight.
|
|
### Changes
|
|
- Current change.
|
|
### Fixes
|
|
- Current fix.
|
|
`,
|
|
);
|
|
});
|
|
|
|
it("prefers an exact prerelease section when it exists", () => {
|
|
const source = changelog`
|
|
# Changelog
|
|
## 2026.5.28-beta.2
|
|
- Beta 2 package notes with enough release detail.
|
|
## 2026.5.28
|
|
- Stable.
|
|
`;
|
|
|
|
expect(extractCurrentPackageChangelog(source, "2026.5.28-beta.2")).toBe(changelog`
|
|
# Changelog
|
|
|
|
## 2026.5.28-beta.2
|
|
- Beta 2 package notes with enough release detail.
|
|
`);
|
|
});
|
|
|
|
it("uses Unreleased only as a prerelease fallback when no release heading exists", () => {
|
|
const source = changelog`
|
|
# Changelog
|
|
## Unreleased
|
|
- Pending beta package notes with enough release detail.
|
|
## 2026.5.27
|
|
- Older stable.
|
|
`;
|
|
|
|
expect(extractCurrentPackageChangelog(source, "2026.5.28-beta.1")).toBe(changelog`
|
|
# Changelog
|
|
|
|
## Unreleased
|
|
- Pending beta package notes with enough release detail.
|
|
`);
|
|
});
|
|
|
|
it("extracts exact correction release sections", () => {
|
|
const source = changelog`
|
|
# Changelog
|
|
## 2026.5.28-1
|
|
- Correction release notes with enough detail.
|
|
## 2026.5.28
|
|
- Stable.
|
|
`;
|
|
|
|
expect(extractCurrentPackageChangelog(source, "2026.5.28-1")).toBe(changelog`
|
|
# Changelog
|
|
|
|
## 2026.5.28-1
|
|
- Correction release notes with enough detail.
|
|
`);
|
|
});
|
|
|
|
it("fails closed when package version has no matching release section", () => {
|
|
expect(() => extractCurrentPackageChangelog(cumulativeChangelog, "2026.5.29")).toThrow(
|
|
"CHANGELOG.md does not contain a release section for 2026.5.29.",
|
|
);
|
|
});
|
|
|
|
it("fails closed when the packaged changelog is unexpectedly large", () => {
|
|
const source = changelog`
|
|
# Changelog
|
|
## 2026.5.28
|
|
${"é".repeat(260_000)}
|
|
`;
|
|
|
|
expect(() => extractCurrentPackageChangelog(source, "2026.5.28")).toThrow(
|
|
"exceeds the 512000 byte safety limit",
|
|
);
|
|
});
|
|
|
|
it("fails closed when the extracted release section is effectively empty", () => {
|
|
const source = changelog`
|
|
# Changelog
|
|
Docs: https://docs.openclaw.ai
|
|
## 2026.5.28
|
|
### Fixes
|
|
## 2026.5.27
|
|
- Older stable release notes with enough detail.
|
|
`;
|
|
|
|
expect(() => extractCurrentPackageChangelog(source, "2026.5.28")).toThrow(
|
|
"below the 32 byte safety minimum",
|
|
);
|
|
});
|
|
|
|
it("prepares and restores the packaged changelog without changing the source permanently", async () => {
|
|
const root = mkdtempSync(path.join(os.tmpdir(), "openclaw-package-changelog-"));
|
|
try {
|
|
writeFileSync(path.join(root, "package.json"), '{"version":"2026.5.28-beta.1"}\n', "utf8");
|
|
writeFileSync(path.join(root, "CHANGELOG.md"), cumulativeChangelog, "utf8");
|
|
|
|
await expect(preparePackageChangelog(root)).resolves.toBe(true);
|
|
expect(readFileSync(path.join(root, "CHANGELOG.md"), "utf8")).not.toContain("## Unreleased");
|
|
expect(readFileSync(path.join(root, "CHANGELOG.md"), "utf8")).not.toContain("## 2026.5.27");
|
|
expect(readFileSync(path.join(root, "CHANGELOG.md"), "utf8")).toContain("## 2026.5.28");
|
|
|
|
await expect(restorePackageChangelog(root)).resolves.toBe(true);
|
|
expect(readFileSync(path.join(root, "CHANGELOG.md"), "utf8")).toBe(cumulativeChangelog);
|
|
} finally {
|
|
rmSync(root, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
it("refuses to restore stale backups over current changelog edits", async () => {
|
|
const root = mkdtempSync(path.join(os.tmpdir(), "openclaw-package-changelog-"));
|
|
const backupPath = path.join(
|
|
root,
|
|
".artifacts",
|
|
"package-changelog",
|
|
"CHANGELOG.md.prepack-backup",
|
|
);
|
|
const editedChangelog = cumulativeChangelog.replace("- Current fix.", "- Current fix edited.");
|
|
try {
|
|
writeFileSync(path.join(root, "package.json"), '{"version":"2026.5.28-beta.1"}\n', "utf8");
|
|
writeFileSync(path.join(root, "CHANGELOG.md"), editedChangelog, "utf8");
|
|
mkdirSync(path.dirname(backupPath), { recursive: true });
|
|
writeFileSync(backupPath, cumulativeChangelog, "utf8");
|
|
|
|
await expect(restorePackageChangelog(root)).rejects.toThrow(
|
|
"Refusing to restore packaged changelog backup",
|
|
);
|
|
expect(readFileSync(path.join(root, "CHANGELOG.md"), "utf8")).toBe(editedChangelog);
|
|
expect(existsSync(backupPath)).toBe(true);
|
|
} finally {
|
|
rmSync(root, { recursive: true, force: true });
|
|
}
|
|
});
|
|
});
|