From 6d0ae445608bbfc463690a6f4bf4e09d535fc632 Mon Sep 17 00:00:00 2001 From: 7ttp <117663341+7ttp@users.noreply.github.com> Date: Tue, 1 Sep 2026 19:50:11 +0530 Subject: [PATCH 1/4] test: add postgres-config get live coverage --- .../postgres-config/get/get.live.test.ts | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 apps/cli/src/legacy/commands/postgres-config/get/get.live.test.ts 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..8edc995d5a --- /dev/null +++ b/apps/cli/src/legacy/commands/postgres-config/get/get.live.test.ts @@ -0,0 +1,21 @@ +import { expect } from "vitest"; + +import { 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", + "--project-ref", + project.ref, + "--experimental", + "-o", + "json", + ]); + expect(result.exitCode, result.stderr).toBe(0); + expect(result.stdout, result.stderr).not.toBe(""); + expect(JSON.parse(result.stdout), result.stdout).toBeTypeOf("object"); +}); From 8cf96d6521a052f96625eb9c80b939d895e54c48 Mon Sep 17 00:00:00 2001 From: 7ttp <117663341+7ttp@users.noreply.github.com> Date: Tue, 1 Sep 2026 19:50:19 +0530 Subject: [PATCH 2/4] test: add postgres-config update live coverage --- .../update/update.live.test.ts | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 apps/cli/src/legacy/commands/postgres-config/update/update.live.test.ts 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..7d4716f11b --- /dev/null +++ b/apps/cli/src/legacy/commands/postgres-config/update/update.live.test.ts @@ -0,0 +1,45 @@ +import { expect } from "vitest"; + +import { 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 target = ["--project-ref", project.ref, "--experimental"]; + const noRestart = [...target, "--no-restart"]; + let targetError: unknown; + const cleanupErrors: Array = []; + try { + const updated = await cli([ + "postgres-config", + "update", + "--config", + "work_mem=7MB", + ...noRestart, + ]); + expect(updated.exitCode, updated.stderr).toBe(0); + expect(updated.stdout, updated.stderr).toMatch(/\bwork_mem +\| 7MB\b/u); + + const proof = await cli(["postgres-config", "get", ...target, "-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 { + const removed = await cli([ + "postgres-config", + "delete", + "--config", + "work_mem", + ...noRestart, + ]); + requireLiveSuccess(removed, "postgres-config delete cleanup after postgres-config update"); + } catch (error) { + cleanupErrors.push(error); + } + } + throwWithCleanup(targetError, cleanupErrors); +}); From eefdcd7fdf16e1f56cc44fa5f1ee4dec3ed39e8a Mon Sep 17 00:00:00 2001 From: 7ttp <117663341+7ttp@users.noreply.github.com> Date: Tue, 1 Sep 2026 19:50:19 +0530 Subject: [PATCH 3/4] test: add postgres-config delete live coverage --- .../delete/delete.live.test.ts | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 apps/cli/src/legacy/commands/postgres-config/delete/delete.live.test.ts 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..7d9c574f16 --- /dev/null +++ b/apps/cli/src/legacy/commands/postgres-config/delete/delete.live.test.ts @@ -0,0 +1,61 @@ +import { expect } from "vitest"; + +import { 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; the test leaves the +// shared project unchanged. +test("removes the test-seeded override and get proves it is gone", async ({ cli, project }) => { + const target = ["--project-ref", project.ref, "--experimental"]; + const noRestart = [...target, "--no-restart"]; + let targetError: unknown; + const cleanupErrors: Array = []; + try { + const seeded = await cli([ + "postgres-config", + "update", + "--config", + "maintenance_work_mem=16MB", + ...noRestart, + ]); + requireLiveSuccess(seeded, "postgres-config update setup for postgres-config delete"); + const before = await cli(["postgres-config", "get", ...target, "-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", + ...noRestart, + ]); + expect(removed.exitCode, removed.stderr).toBe(0); + expect(removed.stdout, removed.stderr).toContain("Parameter"); + expect(removed.stdout, removed.stderr).not.toContain("maintenance_work_mem"); + + const proof = await cli(["postgres-config", "get", ...target, "-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(); + } catch (error) { + targetError = error; + } finally { + try { + const restored = await cli([ + "postgres-config", + "delete", + "--config", + "maintenance_work_mem", + ...noRestart, + ]); + requireLiveSuccess(restored, "postgres-config delete cleanup after postgres-config delete"); + } catch (error) { + cleanupErrors.push(error); + } + } + throwWithCleanup(targetError, cleanupErrors); +}); From 0ab753637c0efbab1e10c023d47957327ea08d31 Mon Sep 17 00:00:00 2001 From: 7ttp <117663341+7ttp@users.noreply.github.com> Date: Wed, 2 Sep 2026 01:15:45 +0530 Subject: [PATCH 4/4] nit --- .../delete/delete.live.test.ts | 51 +++++++++++-------- .../postgres-config/get/get.live.test.ts | 11 ++-- .../update/update.live.test.ts | 31 ++++++----- apps/cli/tests/helpers/live.ts | 27 ++++++++++ 4 files changed, 79 insertions(+), 41 deletions(-) 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 index 7d9c574f16..ce29079a86 100644 --- 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 @@ -1,25 +1,32 @@ import { expect } from "vitest"; -import { requireLiveSuccess, test, throwWithCleanup } from "../../../../../tests/helpers/live.ts"; +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; the test leaves the -// shared project unchanged. +// 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 target = ["--project-ref", project.ref, "--experimental"]; - const noRestart = [...target, "--no-restart"]; + 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", - ...noRestart, + ...flags, + "--no-restart", ]); requireLiveSuccess(seeded, "postgres-config update setup for postgres-config delete"); - const before = await cli(["postgres-config", "get", ...target, "-o", "json"]); + 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; @@ -30,31 +37,31 @@ test("removes the test-seeded override and get proves it is gone", async ({ cli, "delete", "--config", "maintenance_work_mem", - ...noRestart, + ...flags, + "--no-restart", + "-o", + "json", ]); expect(removed.exitCode, removed.stderr).toBe(0); - expect(removed.stdout, removed.stderr).toContain("Parameter"); - expect(removed.stdout, removed.stderr).not.toContain("maintenance_work_mem"); + 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", ...target, "-o", "json"]); + 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 { - try { - const restored = await cli([ - "postgres-config", - "delete", - "--config", - "maintenance_work_mem", - ...noRestart, - ]); - requireLiveSuccess(restored, "postgres-config delete cleanup after postgres-config delete"); - } catch (error) { - cleanupErrors.push(error); + 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 index 8edc995d5a..9e54ee571c 100644 --- 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 @@ -1,6 +1,6 @@ import { expect } from "vitest"; -import { test } from "../../../../../tests/helpers/live.ts"; +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 @@ -9,13 +9,14 @@ test("reads the current config of the target project", async ({ cli, project }) const result = await cli([ "postgres-config", "get", - "--project-ref", - project.ref, - "--experimental", + ...postgresConfigLiveFlags(project), "-o", "json", ]); expect(result.exitCode, result.stderr).toBe(0); expect(result.stdout, result.stderr).not.toBe(""); - expect(JSON.parse(result.stdout), result.stdout).toBeTypeOf("object"); + 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 index 7d4716f11b..bf4593fdd8 100644 --- 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 @@ -1,12 +1,17 @@ import { expect } from "vitest"; -import { requireLiveSuccess, test, throwWithCleanup } from "../../../../../tests/helpers/live.ts"; +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 target = ["--project-ref", project.ref, "--experimental"]; - const noRestart = [...target, "--no-restart"]; + const flags = postgresConfigLiveFlags(project); let targetError: unknown; const cleanupErrors: Array = []; try { @@ -15,12 +20,17 @@ test("applies an override with --no-restart and get proves it", async ({ cli, pr "update", "--config", "work_mem=7MB", - ...noRestart, + ...flags, + "--no-restart", + "-o", + "json", ]); expect(updated.exitCode, updated.stderr).toBe(0); - expect(updated.stdout, updated.stderr).toMatch(/\bwork_mem +\| 7MB\b/u); + 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", ...target, "-o", "json"]); + 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; @@ -29,14 +39,7 @@ test("applies an override with --no-restart and get proves it", async ({ cli, pr targetError = error; } finally { try { - const removed = await cli([ - "postgres-config", - "delete", - "--config", - "work_mem", - ...noRestart, - ]); - requireLiveSuccess(removed, "postgres-config delete cleanup after postgres-config update"); + await removePostgresConfigLiveOverride(cli, project, "work_mem"); } catch (error) { cleanupErrors.push(error); } 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