import { describe, expect, it, vi } from "vitest"; import { buildPublishCommand, githubApi, parseArgs, parseRunIdFromDispatchOutput, resolveArtifactName, } from "../../scripts/release-candidate-checklist.mjs"; describe("release candidate checklist", () => { it("requires run ids when dispatch is disabled", () => { expect(() => parseArgs(["--tag", "v2026.5.14-beta.3", "--skip-dispatch"])).toThrow( "--skip-dispatch requires --full-release-run and --npm-preflight-run", ); }); it("stops parsing options after the argument terminator", () => { const options = parseArgs([ "--tag", "v2026.5.14-beta.3", "--full-release-run", "111", "--npm-preflight-run", "222", "--skip-dispatch", "--", "--plugin-publish-scope", "selected", ]); expect(options.pluginPublishScope).toBe("all-publishable"); }); it("accepts package-manager argument separators before script options", () => { const options = parseArgs([ "--", "--tag", "v2026.5.14-beta.3", "--full-release-run", "111", "--npm-preflight-run", "222", "--skip-dispatch", "--skip-parallels", ]); expect(options.tag).toBe("v2026.5.14-beta.3"); expect(options.skipParallels).toBe(true); }); it("builds the gated release publish command from green evidence inputs", () => { const options = { ...parseArgs([ "--tag", "v2026.5.14-beta.3", "--workflow-ref", "release/2026.5.14", "--full-release-run", "111", "--npm-preflight-run", "222", "--skip-dispatch", ]), workflowRef: "release/2026.5.14", }; expect(buildPublishCommand(options)).toContain("'full_release_validation_run_id=111'"); expect(buildPublishCommand(options)).toContain("'preflight_run_id=222'"); expect(buildPublishCommand(options)).toContain("'tag=v2026.5.14-beta.3'"); expect(buildPublishCommand(options)).toContain("'plugin_publish_scope=all-publishable'"); }); it("carries the Telegram proof run into the publish command when available", () => { const options = { ...parseArgs([ "--tag", "v2026.5.14-beta.3", "--workflow-ref", "release/2026.5.14", "--full-release-run", "111", "--npm-preflight-run", "222", "--skip-dispatch", ]), workflowRef: "release/2026.5.14", npmTelegramRunId: "333", }; expect(buildPublishCommand(options)).toContain("'npm_telegram_run_id=333'"); }); it("requires explicit plugin names for selected plugin publish scope", () => { expect(() => parseArgs(["--tag", "v2026.5.14-beta.3", "--plugin-publish-scope", "selected"]), ).toThrow("--plugin-publish-scope selected requires --plugins"); }); it("rejects selected plugin publish scope for release candidates", () => { expect(() => parseArgs([ "--tag", "v2026.5.14-beta.3", "--plugin-publish-scope", "selected", "--plugins", "@openclaw/diffs", ]), ).toThrow("release candidates publish OpenClaw with --plugin-publish-scope all-publishable"); }); it("extracts a workflow run id from gh dispatch output", () => { expect( parseRunIdFromDispatchOutput( "https://github.com/openclaw/openclaw/actions/runs/25922042055\n", ), ).toBe("25922042055"); }); it("falls back to a single compatible artifact from the same run", () => { expect( resolveArtifactName( [{ name: "openclaw-npm-preflight-dba00", expired: false }], "openclaw-npm-preflight-v2026.5.16-beta.2", "openclaw-npm-preflight-", ), ).toBe("openclaw-npm-preflight-dba00"); }); it("bounds GitHub API requests with a timeout signal", async () => { const fetchImpl = vi.fn(async (_url: string, init?: RequestInit) => { expect(init?.signal).toBeInstanceOf(AbortSignal); expect(init?.headers).toMatchObject({ Accept: "application/vnd.github+json", Authorization: "Bearer test-token", "X-GitHub-Api-Version": "2022-11-28", }); return { ok: true, json: async () => ({ workflow_runs: [] }), }; }); await expect( githubApi("repos/openclaw/openclaw/actions/runs", { fetchImpl, timeoutMs: 1234, token: "test-token", }), ).resolves.toEqual({ workflow_runs: [] }); expect(fetchImpl).toHaveBeenCalledWith( "https://api.github.com/repos/openclaw/openclaw/actions/runs", expect.objectContaining({ signal: expect.any(AbortSignal), }), ); }); it("includes the GitHub API path when a request times out", async () => { const fetchImpl = vi.fn(async () => { throw new DOMException("request timed out", "TimeoutError"); }); await expect( githubApi("repos/openclaw/openclaw/actions/runs/123/jobs", { fetchImpl, timeoutMs: 5, token: "test-token", }), ).rejects.toThrow( "GitHub API repos/openclaw/openclaw/actions/runs/123/jobs timed out after 5ms", ); }); });