import { expect, it } from "bun:test"; import { createHash } from "node:crypto"; import { copyFile, mkdir, mkdtemp, readFile, readdir, rm, symlink, writeFile } from "node:fs/promises"; import { join } from "node:path"; import { tmpdir } from "node:os"; import { astToString, COMMENT_HEADER, default as openapiTS } from "openapi-typescript"; it.each(["valid", "invalid-schema", "invalid-digest"])("synchronizes the complete artifact group or preserves it (%s)", async (scenario) => { const root = await mkdtemp(join(tmpdir(), "server-contract-sync-")); try { for (const directory of ["scripts", "openapi", "src/generated", "upstream"]) await mkdir(join(root, directory), { recursive: true }); await copyFile(new URL("../../scripts/server-contract.ts", import.meta.url), join(root, "scripts/server-contract.ts")); await symlink(new URL("../../node_modules", import.meta.url).pathname, join(root, "node_modules"), "dir"); const targets = ["openapi/openapi.json", "openapi/server-manifest.json", "src/generated/server-api.d.ts"]; for (const target of targets) await writeFile(join(root, target), `original:${target}`); const schema = { openapi: scenario === "invalid-schema" ? "9.9.9" : "3.1.0", info: { title: "Fixture", version: "1" }, paths: {} }; const raw = JSON.stringify(schema); const manifest = { contracts: { server: { sha256: scenario === "invalid-digest" ? "incorrect" : createHash("sha256").update(raw).digest("hex") } } }; await writeFile(join(root, "upstream/schema.json"), raw); await writeFile(join(root, "upstream/manifest.json"), JSON.stringify(manifest)); const child = Bun.spawn([process.execPath, "scripts/server-contract.ts", "--sync", "--upstream", "upstream/schema.json"], { cwd: root, stdout: "pipe", stderr: "pipe", }); const [code, stderr] = await Promise.all([child.exited, new Response(child.stderr).text()]); if (scenario === "valid") { expect(code).toBe(0); expect(await readFile(join(root, targets[0]), "utf8")).toBe(raw); expect(JSON.parse(await readFile(join(root, targets[1]), "utf8"))).toEqual(manifest); expect(await readFile(join(root, targets[2]), "utf8")).toBe(COMMENT_HEADER + astToString(await openapiTS(schema))); } else { expect(code).not.toBe(0); expect(stderr).toContain(scenario === "invalid-schema" ? "Unsupported OpenAPI version" : "contract digest differs"); for (const target of targets) expect(await readFile(join(root, target), "utf8")).toBe(`original:${target}`); } expect((await readdir(join(root, "openapi"))).sort()).toEqual(["openapi.json", "server-manifest.json"]); } finally { await rm(root, { recursive: true, force: true }); } });