diff --git a/apps/cli/src/legacy/commands/postgres-config/delete/delete.live.test.ts b/apps/cli/src/legacy/commands/postgres-config/delete/delete.live.test.ts new file mode 100644 index 0000000000..ce29079a86 --- /dev/null +++ b/apps/cli/src/legacy/commands/postgres-config/delete/delete.live.test.ts @@ -0,0 +1,68 @@ +import { expect } from "vitest"; + +import { + postgresConfigLiveFlags, + removePostgresConfigLiveOverride, + requireLiveSuccess, + test, + throwWithCleanup, +} from "../../../../../tests/helpers/live.ts"; + +// Seeds its own override and proves it landed before deleting, so the absence +// assertion cannot be satisfied by the pre-seed state. Teardown removes the +// seeded key only when the test did not already prove it gone. +test("removes the test-seeded override and get proves it is gone", async ({ cli, project }) => { + const flags = postgresConfigLiveFlags(project); + let targetError: unknown; + const cleanupErrors: Array = []; + let cleanupNeeded = true; + try { + const seeded = await cli([ + "postgres-config", + "update", + "--config", + "maintenance_work_mem=16MB", + ...flags, + "--no-restart", + ]); + requireLiveSuccess(seeded, "postgres-config update setup for postgres-config delete"); + const before = await cli(["postgres-config", "get", ...flags, "-o", "json"]); + requireLiveSuccess(before, "postgres-config get seed proof for postgres-config delete"); + expect(before.stdout, before.stderr).not.toBe(""); + const seededConfig = JSON.parse(before.stdout) as Record; + expect(seededConfig["maintenance_work_mem"], before.stdout).toBe("16MB"); + + const removed = await cli([ + "postgres-config", + "delete", + "--config", + "maintenance_work_mem", + ...flags, + "--no-restart", + "-o", + "json", + ]); + expect(removed.exitCode, removed.stderr).toBe(0); + expect(removed.stdout, removed.stderr).not.toBe(""); + const remaining = JSON.parse(removed.stdout) as Record; + expect(remaining["maintenance_work_mem"], removed.stdout).toBeUndefined(); + + const proof = await cli(["postgres-config", "get", ...flags, "-o", "json"]); + requireLiveSuccess(proof, "postgres-config get proof for postgres-config delete"); + expect(proof.stdout, proof.stderr).not.toBe(""); + const config = JSON.parse(proof.stdout) as Record; + expect(config["maintenance_work_mem"], proof.stdout).toBeUndefined(); + cleanupNeeded = false; + } catch (error) { + targetError = error; + } finally { + if (cleanupNeeded) { + try { + await removePostgresConfigLiveOverride(cli, project, "maintenance_work_mem"); + } catch (error) { + cleanupErrors.push(error); + } + } + } + throwWithCleanup(targetError, cleanupErrors); +}); diff --git a/apps/cli/src/legacy/commands/postgres-config/get/get.live.test.ts b/apps/cli/src/legacy/commands/postgres-config/get/get.live.test.ts new file mode 100644 index 0000000000..9e54ee571c --- /dev/null +++ b/apps/cli/src/legacy/commands/postgres-config/get/get.live.test.ts @@ -0,0 +1,22 @@ +import { expect } from "vitest"; + +import { postgresConfigLiveFlags, test } from "../../../../../tests/helpers/live.ts"; + +// A freshly provisioned project can have zero overrides, so the golden path +// pins the payload shape rather than any key: exit 0 and a JSON object on +// payload-only stdout. +test("reads the current config of the target project", async ({ cli, project }) => { + const result = await cli([ + "postgres-config", + "get", + ...postgresConfigLiveFlags(project), + "-o", + "json", + ]); + expect(result.exitCode, result.stderr).toBe(0); + expect(result.stdout, result.stderr).not.toBe(""); + const config: unknown = JSON.parse(result.stdout); + expect(config, result.stdout).toBeTypeOf("object"); + expect(config, result.stdout).not.toBeNull(); + expect(Array.isArray(config), result.stdout).toBe(false); +}); diff --git a/apps/cli/src/legacy/commands/postgres-config/update/update.live.test.ts b/apps/cli/src/legacy/commands/postgres-config/update/update.live.test.ts new file mode 100644 index 0000000000..bf4593fdd8 --- /dev/null +++ b/apps/cli/src/legacy/commands/postgres-config/update/update.live.test.ts @@ -0,0 +1,48 @@ +import { expect } from "vitest"; + +import { + postgresConfigLiveFlags, + removePostgresConfigLiveOverride, + requireLiveSuccess, + test, + throwWithCleanup, +} from "../../../../../tests/helpers/live.ts"; + +// --no-restart skips the database restart; work_mem is a dynamic parameter, so +// the override still takes effect. +test("applies an override with --no-restart and get proves it", async ({ cli, project }) => { + const flags = postgresConfigLiveFlags(project); + let targetError: unknown; + const cleanupErrors: Array = []; + try { + const updated = await cli([ + "postgres-config", + "update", + "--config", + "work_mem=7MB", + ...flags, + "--no-restart", + "-o", + "json", + ]); + expect(updated.exitCode, updated.stderr).toBe(0); + expect(updated.stdout, updated.stderr).not.toBe(""); + const applied = JSON.parse(updated.stdout) as Record; + expect(applied["work_mem"], updated.stdout).toBe("7MB"); + + const proof = await cli(["postgres-config", "get", ...flags, "-o", "json"]); + requireLiveSuccess(proof, "postgres-config get proof for postgres-config update"); + expect(proof.stdout, proof.stderr).not.toBe(""); + const config = JSON.parse(proof.stdout) as Record; + expect(config["work_mem"], proof.stdout).toBe("7MB"); + } catch (error) { + targetError = error; + } finally { + try { + await removePostgresConfigLiveOverride(cli, project, "work_mem"); + } catch (error) { + cleanupErrors.push(error); + } + } + throwWithCleanup(targetError, cleanupErrors); +}); diff --git a/apps/cli/tests/helpers/live.ts b/apps/cli/tests/helpers/live.ts index a1a7e99b94..50761a6552 100644 --- a/apps/cli/tests/helpers/live.ts +++ b/apps/cli/tests/helpers/live.ts @@ -145,6 +145,33 @@ export async function removeStorageLiveObject( } } +/** Flags every postgres-config live test passes: the family is + * experimental-gated and addresses the shared project by ref. */ +export function postgresConfigLiveFlags(project: LiveProject): ReadonlyArray { + return ["--project-ref", project.ref, "--experimental"]; +} + +/** + * Exact-key cleanup for postgres-config live tests: removes one owned override + * without a database restart. Deleting an absent key is a no-op PUT, so the + * teardown stays idempotent. + */ +export async function removePostgresConfigLiveOverride( + cli: (args: string[]) => Promise<{ exitCode: number; stdout: string; stderr: string }>, + project: LiveProject, + key: string, +): Promise { + const removed = await cli([ + "postgres-config", + "delete", + "--config", + key, + ...postgresConfigLiveFlags(project), + "--no-restart", + ]); + requireLiveSuccess(removed, `postgres-config delete cleanup for ${key}`); +} + /** * Unique migration version for a live test: a sortable `YYYYMMDDHHMMSS` UTC * stamp plus four random digits, so it always orders after any conventional