import { afterEach, expect, test } from "bun:test"; import { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { detectLegacyApiBodies } from "../src/migrations/legacy-api-body.ts"; import { updateApp } from "../src/update.ts"; const SOURCE = `page Hello { ssr { api ssrUsers GET /api/users/ssr { return userNames(users) } } view {
x
} } `; test("a legacy bare body is detected with its free identifiers", () => { const found = detectLegacyApiBodies(SOURCE); expect(found).toHaveLength(1); expect(found[0]!.name).toBe("ssrUsers"); expect(found[0]!.freeIdentifiers).toContain("users"); expect(found[0]!.freeIdentifiers).toContain("userNames"); }); test("a sectioned block is not reported", () => { const sectioned = `page P { apis { x GET /api/x { response { return data.users } } } view {
x
} } `; expect(detectLegacyApiBodies(sectioned)).toEqual([]); }); test("an identifier that only appears inside a string literal is not collected", () => { const source = `page Hello { ssr { api ssrUsers GET /api/users/ssr { return "users are great, ask userNames" } } view {
x
} } `; const found = detectLegacyApiBodies(source); expect(found).toHaveLength(1); expect(found[0]!.freeIdentifiers).not.toContain("users"); expect(found[0]!.freeIdentifiers).not.toContain("userNames"); }); test("a property-access key is not collected, only its object", () => { const source = `page Hello { ssr { api ssrUsers GET /api/users/ssr { return u.name } } view {
x
} } `; const found = detectLegacyApiBodies(source); expect(found).toHaveLength(1); expect(found[0]!.freeIdentifiers).toContain("u"); expect(found[0]!.freeIdentifiers).not.toContain("name"); }); const roots: string[] = []; afterEach(() => { for (const root of roots.splice(0)) rmSync(root, { recursive: true, force: true }); }); function project(): string { const root = mkdtempSync(join(tmpdir(), "wrnexus-migrate-legacy-api-body-")); roots.push(root); mkdirSync(join(root, "app"), { recursive: true }); writeFileSync( join(root, "package.json"), JSON.stringify({ name: "legacy-api-body-migrate-app", dependencies: { "@wrnexus/core": "^0.8.0" }, wrnexus: { version: "0.8.0" }, }), ); return root; } test("a full update run leaves a legacy bare body file byte-identical and reports it", () => { const root = project(); writeFileSync(join(root, "app", "Hello.wrn"), SOURCE); const report = { changedAutomatically: [] as string[], needsReview: [] as string[], unresolvedImports: [] as string[], ambiguousFunctions: [] as string[], legacyOutputPayloads: [] as string[], parseFailures: [] as string[], }; updateApp(root, "0.9.0", false, { report }); const after = readFileSync(join(root, "app", "Hello.wrn"), "utf8"); expect(after).toBe(SOURCE); expect(report.needsReview.some((entry) => entry.includes("ssrUsers"))).toBe(true); });