Files
openclaw/test/scripts/release-beta-smoke.test.ts
2026-05-28 23:35:06 +02:00

165 lines
4.6 KiB
TypeScript

import { describe, expect, it } from "vitest";
import {
mergeTelegramProofIntoReleaseBody,
parseArgs,
parseWorkflowRunIdFromOutput,
pollRun,
run,
selectNewestDispatchedRunId,
} from "../../scripts/release-beta-smoke.ts";
describe("release-beta-smoke", () => {
it("rejects runs with both validation lanes skipped", () => {
expect(() => parseArgs(["--skip-parallels", "--skip-telegram"])).toThrow(
"--skip-parallels and --skip-telegram cannot be used together",
);
});
it("parses workflow run urls when gh includes them in dispatch output", () => {
expect(
parseWorkflowRunIdFromOutput(
"Dispatched: https://github.com/openclaw/openclaw/actions/runs/1234567890",
),
).toBe("1234567890");
});
it("selects the newest workflow_dispatch run not present before dispatch", () => {
const beforeIds = new Set(["100", "101"]);
expect(
selectNewestDispatchedRunId({
beforeIds,
runs: [
{ databaseId: 100, createdAt: "2026-05-04T10:00:00Z" },
{ databaseId: 102, createdAt: "2026-05-04T10:01:00Z" },
{ databaseId: 103, createdAt: "2026-05-04T10:02:00Z" },
],
}),
).toBe("103");
});
it("selects runs returned by the actions workflow runs API", () => {
const beforeIds = new Set(["200"]);
expect(
selectNewestDispatchedRunId({
beforeIds,
runs: [
{ id: 200, created_at: "2026-05-04T10:00:00Z" },
{ id: 201, created_at: "2026-05-04T10:02:00Z" },
{ id: 202, created_at: "2026-05-04T10:01:00Z" },
],
}),
).toBe("201");
});
it("replaces stale Telegram proof placeholders", () => {
const body = [
"## Changes",
"",
"### Release verification",
"",
"- npm package: https://www.npmjs.com/package/openclaw/v/2026.5.20-beta.1",
"- npm Telegram beta E2E: not supplied",
"",
"### Assets",
"",
"- artifact",
"",
].join("\n");
const merged = mergeTelegramProofIntoReleaseBody(
body,
"- npm Telegram beta E2E: https://github.com/openclaw/openclaw/actions/runs/123",
);
expect(merged).toContain("actions/runs/123");
expect(merged).not.toContain("not supplied");
expect(merged).toContain("### Assets");
});
it("inserts Telegram proof before the next release notes subsection", () => {
const body = [
"## Changes",
"",
"### Release verification",
"",
"- npm package: https://www.npmjs.com/package/openclaw/v/2026.5.20-beta.1",
"",
"### Assets",
"",
"- artifact",
"",
].join("\n");
const merged = mergeTelegramProofIntoReleaseBody(
body,
"- npm Telegram beta E2E: https://github.com/openclaw/openclaw/actions/runs/123",
);
expect(merged.indexOf("actions/runs/123")).toBeLessThan(merged.indexOf("### Assets"));
});
it("bounds child command hangs", () => {
expect(() =>
run(process.execPath, ["-e", "setInterval(() => {}, 1000)"], {
capture: true,
timeoutMs: 50,
}),
).toThrow(/timed out after 50ms/u);
});
it("uses a non-ignorable timeout signal for trapped children", () => {
expect(() =>
run(
process.execPath,
["-e", "process.on('SIGTERM', () => {}); setInterval(() => {}, 1000)"],
{
capture: true,
timeoutMs: 50,
},
),
).toThrow(/timed out after 50ms/u);
});
it("stops polling Telegram workflow runs after the timeout budget", async () => {
let now = 0;
const sleeps: number[] = [];
await expect(
pollRun("openclaw/openclaw", "123", {
now: () => now,
pollIntervalMs: 400,
readRun: () => ({
conclusion: null,
html_url: "https://github.com/openclaw/openclaw/actions/runs/123",
status: "queued",
updated_at: "2026-05-28T12:00:00Z",
}),
sleep: async (ms) => {
sleeps.push(ms);
now += ms;
},
timeoutMs: 1000,
}),
).rejects.toThrow("Telegram workflow 123 did not complete within 1000ms");
expect(sleeps).toEqual([400, 400, 200]);
});
it("returns when the Telegram workflow succeeds", async () => {
await expect(
pollRun("openclaw/openclaw", "123", {
readRun: () => ({
conclusion: "success",
html_url: "https://github.com/openclaw/openclaw/actions/runs/123",
status: "completed",
updated_at: "2026-05-28T12:00:00Z",
}),
sleep: async () => {
throw new Error("sleep should not run after completion");
},
}),
).resolves.toBeUndefined();
});
});