返回 CodeWhale
sync-latest-release.test.ts
根目录 / web / lib / sync-latest-release.test.ts
1 import { spawnSync } from "node:child_process";
2 import { copyFileSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, rmSync, writeFileSync } from "node:fs";
3 import { tmpdir } from "node:os";
4 import { join } from "node:path";
5 import { fileURLToPath } from "node:url";
6 import { afterEach, describe, expect, it } from "vitest";
7
8 const script = fileURLToPath(new URL("../scripts/sync-latest-release.mjs", import.meta.url));
9 const roots: string[] = [];
10 afterEach(() => { for (const root of roots.splice(0)) rmSync(root, { recursive: true, force: true }); });
11
12 const OLD = { tag: "v0.10.0", version: "0.10.0", publishedAt: "2026-09-01T00:00:00Z", url: "https://github.com/codewhale-hq/CodeWhale/releases/tag/v0.10.0" };
13
14 /** The script in a throwaway web/ + docs/ tree, with GitHub stubbed. */
15 function run(args: string[], { status = 200, cloud = { release: { latest: "0.10.0" } } }: { status?: number; cloud?: unknown } = {}) {
16 const root = mkdtempSync(join(tmpdir(), "cw-sync-release-"));
17 roots.push(root);
18 mkdirSync(join(root, "web", "scripts"), { recursive: true });
19 mkdirSync(join(root, "web", "data"), { recursive: true });
20 mkdirSync(join(root, "docs", "cloud-facts"), { recursive: true });
21 copyFileSync(script, join(root, "web", "scripts", "sync-latest-release.mjs"));
22 writeFileSync(join(root, "web", "data", "latest-published-release.json"), `${JSON.stringify(OLD, null, 2)}\n`);
23 writeFileSync(join(root, "docs", "public-surface-facts.json"), `${JSON.stringify({ latestPublishedRelease: { ...OLD, sources: ["x"] } }, null, 2)}\n`);
24 writeFileSync(join(root, "docs", "cloud-facts", "stable.json"), `${JSON.stringify(cloud, null, 2)}\n`);
25 const release = JSON.stringify({ tag_name: "v0.10.1", published_at: "2026-09-02T00:00:00Z" });
26 const stub = `globalThis.fetch = async () => new Response(${JSON.stringify(release)}, { status: ${status} });`;
27 const result = spawnSync(process.execPath, ["--import", `data:text/javascript,${encodeURIComponent(stub)}`, join(root, "web", "scripts", "sync-latest-release.mjs"), ...args], { encoding: "utf8", env: { ...process.env, GITHUB_TOKEN: "" } });
28 const read = (...parts: string[]) => JSON.parse(readFileSync(join(root, ...parts), "utf8"));
29 return { result, root, target: () => read("web", "data", "latest-published-release.json"), mirror: () => read("docs", "public-surface-facts.json") };
30 }
31
32 describe("sync-latest-release", () => {
33 it("fails --check when GitHub cannot be read instead of reporting current", () => {
34 const { result } = run(["--check"], { status: 503 });
35 expect(result.status).toBe(1);
36 expect(result.stderr).toContain("could not read the latest release");
37 });
38
39 it("writes nothing when any mirror is unusable, then moves all three together", () => {
40 const broken = run([], { cloud: { notRelease: true } });
41 expect(broken.result.status).toBe(1);
42 expect(broken.target()).toEqual(OLD);
43 expect(broken.mirror().latestPublishedRelease.tag).toBe("v0.10.0");
44 expect(readdirSync(join(broken.root, "web", "data"))).toEqual(["latest-published-release.json"]);
45
46 const ok = run([]);
47 expect(ok.result.status, ok.result.stderr).toBe(0);
48 expect(ok.target().tag).toBe("v0.10.1");
49 expect(ok.mirror().latestPublishedRelease).toMatchObject({ tag: "v0.10.1", sources: ["x"] });
50 expect(JSON.parse(readFileSync(join(ok.root, "docs", "cloud-facts", "stable.json"), "utf8")).release).toMatchObject({ latest: "0.10.1" });
51 });
52 });
53
53 lines TYPESCRIPT