diff --git a/apps/cli/src/legacy/commands/config/diff/diff.live.test.ts b/apps/cli/src/legacy/commands/config/diff/diff.live.test.ts index 94737817ef..3599778558 100644 --- a/apps/cli/src/legacy/commands/config/diff/diff.live.test.ts +++ b/apps/cli/src/legacy/commands/config/diff/diff.live.test.ts @@ -1,3 +1,4 @@ +import { type ConfigChangeClass, projectConfigMappingRows } from "@supabase/config/internal"; import { expect } from "vitest"; import { requireLiveSuccess, test } from "../../../../../tests/helpers/live.ts"; @@ -8,18 +9,36 @@ import { requireLiveSuccess, test } from "../../../../../tests/helpers/live.ts"; // in diff.integration.test.ts. The `workspace` fixture behind `cli` is a // fresh `supabase init` project directory. test("diffs a freshly-initialized config against the project", async ({ cli, project }) => { - const result = await cli(["config", "diff", "--project-ref", project.ref]); - expect(`${result.stdout}${result.stderr}`).not.toContain("Unauthorized"); - expect(result.stderr).toContain(`Comparing against project ${project.ref} using base config`); - expect(result.stderr).toContain("Comparison scope:"); - // The GoTrue-keyed auth record — the one surface mocks cannot prove — must - // classify CLEANLY against a fresh config: the platform's reports of - // unconfigured state (session zeros canonicalized to "0s" via - // unconfiguredValue, platform-rendered mailer subjects via platformRendered, - // disabled notification toggles) are suppressed by the registry's declared - // baselines, not flagged as drift. Asserting only exit 0 here would let that noise through silently. - const authChangeLines = result.stdout.split("\n").filter((line) => line.startsWith("auth.")); - expect(authChangeLines, result.stdout).toEqual([]); + const result = await cli([ + "config", + "diff", + "--project-ref", + project.ref, + "--output-format", + "json", + ]); // Read-only success regardless of drift (no --exit-code passed). requireLiveSuccess(result, "config diff"); + expect(result.stderr).toContain(`Comparing against project ${project.ref} using base config`); + const payload = JSON.parse(result.stdout) as { + scope: { present: string[] }; + changes: Array<{ path: string[]; class: ConfigChangeClass }>; + }; + expect(payload.scope.present).toContain("auth"); + const changes = payload.changes.map(({ path, class: kind }) => ({ path: path.join("."), kind })); + const classified = changes.map((change) => `${change.path} [${change.kind}]`).join("\n"); + // A fresh project legitimately drifts from the init template (confirmations, + // TOTP, site URL), so remote auth values must reach classification, while + // the registry's declared baselines suppress the platform's own defaults. + expect( + changes.some((change) => change.path.startsWith("auth.") && change.kind !== "local_only"), + classified, + ).toBe(true); + const baselines = projectConfigMappingRows + .filter((row) => row.unconfiguredValue !== undefined || row.platformRendered === true) + .map((row) => row.configPath.join(".")); + expect( + changes.filter((change) => baselines.includes(change.path)), + classified, + ).toEqual([]); });