From 7460f064f1c948c00402b81016c10d9e0679d0ec Mon Sep 17 00:00:00 2001 From: 7ttp <117663341+7ttp@users.noreply.github.com> Date: Thu, 27 Aug 2026 18:12:28 +0530 Subject: [PATCH 1/6] test: add storage mv live coverage --- .../commands/storage/mv/mv.live.test.ts | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 apps/cli/src/legacy/commands/storage/mv/mv.live.test.ts diff --git a/apps/cli/src/legacy/commands/storage/mv/mv.live.test.ts b/apps/cli/src/legacy/commands/storage/mv/mv.live.test.ts new file mode 100644 index 0000000000..d7a8f1f8f4 --- /dev/null +++ b/apps/cli/src/legacy/commands/storage/mv/mv.live.test.ts @@ -0,0 +1,65 @@ +import { randomUUID } from "node:crypto"; +import { writeFile } from "node:fs/promises"; +import { join } from "node:path"; +import { expect } from "vitest"; + +import { requireLiveSuccess, test, throwWithCleanup } from "../../../../../tests/helpers/live.ts"; + +const STORAGE_FLAGS = ["--linked", "--experimental"]; + +async function removeObject( + cli: (args: string[]) => Promise<{ exitCode: number; stdout: string; stderr: string }>, + remote: string, +): Promise { + const removed = await cli(["storage", "rm", remote, "--yes", ...STORAGE_FLAGS]); + if ( + removed.exitCode !== 0 && + !/not found|does not exist/i.test(`${removed.stdout}\n${removed.stderr}`) + ) { + throw new Error(`storage rm cleanup failed:\n${removed.stdout}\n${removed.stderr}`); + } +} + +test("moves an uploaded object to a new path", async ({ cli, project, workspace }) => { + const suffix = randomUUID().slice(0, 8); + const local = join(workspace.path, `mv-src-${suffix}.txt`); + const source = `ss:///${project.storageBucket}/mv-src-${suffix}.txt`; + const destination = `ss:///${project.storageBucket}/mv-dst-${suffix}.txt`; + await writeFile(local, "live-e2e storage payload\n"); + + let targetError: unknown; + const cleanupErrors: Array = []; + try { + const linked = await cli(["link", "--project-ref", project.ref], { + env: { SUPABASE_DB_PASSWORD: project.dbPassword }, + }); + requireLiveSuccess(linked, "link setup for storage mv"); + const uploaded = await cli(["storage", "cp", local, source, ...STORAGE_FLAGS]); + requireLiveSuccess(uploaded, "storage cp setup for storage mv"); + + const moved = await cli(["storage", "mv", source, destination, ...STORAGE_FLAGS]); + expect(moved.exitCode, moved.stderr).toBe(0); + expect(moved.stderr, moved.stderr).toContain("Moving object:"); + + const listed = await cli([ + "storage", + "ls", + `ss:///${project.storageBucket}/`, + ...STORAGE_FLAGS, + ]); + requireLiveSuccess(listed, "storage ls proof for storage mv"); + expect(listed.stdout).toContain(`mv-dst-${suffix}.txt`); + expect(listed.stdout).not.toContain(`mv-src-${suffix}.txt`); + } catch (error) { + targetError = error; + } finally { + for (const remote of [destination, source]) { + try { + await removeObject(cli, remote); + } catch (error) { + cleanupErrors.push(error); + } + } + } + throwWithCleanup(targetError, cleanupErrors); +}); From 6f080dc0af43109bbccaf4480dd0c42ea3c9fb18 Mon Sep 17 00:00:00 2001 From: 7ttp <117663341+7ttp@users.noreply.github.com> Date: Thu, 27 Aug 2026 18:12:13 +0530 Subject: [PATCH 2/6] test: add services live coverage --- .../commands/services/services.live.test.ts | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 apps/cli/src/legacy/commands/services/services.live.test.ts diff --git a/apps/cli/src/legacy/commands/services/services.live.test.ts b/apps/cli/src/legacy/commands/services/services.live.test.ts new file mode 100644 index 0000000000..1bf1334fc4 --- /dev/null +++ b/apps/cli/src/legacy/commands/services/services.live.test.ts @@ -0,0 +1,31 @@ +import { expect } from "vitest"; + +import { requireLiveSuccess, test } from "../../../../tests/helpers/live.ts"; + +test("merges remote versions from the linked live project into table and json output", async ({ + cli, + project, +}) => { + const linked = await cli(["link", "--project-ref", project.ref, "--skip-pooler"]); + requireLiveSuccess(linked, "link setup for services"); + + const json = await cli(["services", "-o", "json"]); + expect(json.exitCode, json.stderr).toBe(0); + const rows = JSON.parse(json.stdout) as Array<{ name: string; local: string; remote: string }>; + expect(rows, json.stdout).toHaveLength(10); + const postgres = rows.find((row) => row.name === "supabase/postgres"); + if (postgres === undefined) { + throw new Error(`supabase/postgres row missing from services json:\n${json.stdout}`); + } + expect(postgres.remote.length, json.stdout).toBeGreaterThan(0); + + const table = await cli(["services"]); + expect(table.exitCode, table.stderr).toBe(0); + const postgresRow = table.stdout + .split("\n") + .find((line) => line.split("|")[0]?.trim() === "supabase/postgres"); + if (postgresRow === undefined) { + throw new Error(`supabase/postgres row missing from services table:\n${table.stdout}`); + } + expect(postgresRow.split("|")[2]?.trim(), table.stdout).toBe(postgres.remote); +}); From 77e38fdf834d7c2380cb0852d2b4fe2418215a2d Mon Sep 17 00:00:00 2001 From: 7ttp <117663341+7ttp@users.noreply.github.com> Date: Thu, 27 Aug 2026 19:56:59 +0530 Subject: [PATCH 3/6] test: deflake db pull live coverage --- .../legacy/commands/db/pull/pull.live.test.ts | 91 ++++++++++--------- 1 file changed, 46 insertions(+), 45 deletions(-) diff --git a/apps/cli/src/legacy/commands/db/pull/pull.live.test.ts b/apps/cli/src/legacy/commands/db/pull/pull.live.test.ts index bdb4119e38..d24b2011b4 100644 --- a/apps/cli/src/legacy/commands/db/pull/pull.live.test.ts +++ b/apps/cli/src/legacy/commands/db/pull/pull.live.test.ts @@ -1,68 +1,69 @@ -import { mkdir, readdir, unlink, writeFile } from "node:fs/promises"; +import { randomUUID } from "node:crypto"; +import { mkdir, readdir, readFile, unlink } from "node:fs/promises"; import { join } from "node:path"; import { expect } from "vitest"; import { requireLiveSuccess, test, throwWithCleanup } from "../../../../../tests/helpers/live.ts"; -test("pulls the remote schema after a local migration is applied", async ({ - cli, - project, - workspace, -}) => { - const version = `${Date.now()}${Math.floor(Math.random() * 10_000) - .toString() - .padStart(4, "0")}`; +// `db pull` exits non-zero when the diff comes back empty (Go-identical, see +// IN_SYNC_SUGGESTION in pull.handler.ts), so the journey seeds a remote-only +// marker table through `db query` — no local migration and no history row. +// The marker cannot exist in the freshly provisioned shadow, so the diff is +// never empty regardless of engine and the pull deterministically writes it. +test("pulls the remote schema into an initial migration", async ({ cli, project, workspace }) => { + const marker = `e2e_pull_${randomUUID().slice(0, 8)}`; const migrations = join(workspace.path, "supabase", "migrations"); await mkdir(migrations, { recursive: true }); const existingMigrations = new Set(await readdir(migrations)); - const migrationFile = join(migrations, `${version}_e2e_pull.sql`); - await writeFile(migrationFile, `create table if not exists e2e_pull_${version} (id int);\n`); let targetError: unknown; + const cleanupErrors: Array = []; try { - const pushed = await cli(["db", "push", "--db-url", project.dbUrl, "--yes"]); - requireLiveSuccess(pushed, "db push setup"); + const seeded = await cli([ + "db", + "query", + `create table if not exists ${marker} (id int)`, + "--db-url", + project.dbUrl, + ]); + requireLiveSuccess(seeded, "db query setup for db pull"); const result = await cli(["db", "pull", "--db-url", project.dbUrl, "--yes"]); expect(result.exitCode, result.stderr).toBe(0); - expect(`${result.stdout}${result.stderr}`).not.toMatch( - /dial|no route|connection refused|could not connect|server closed the connection|i\/o timeout/i, + + expect(result.stderr, result.stderr).toContain("Schema written to"); + const generated = (await readdir(migrations)).filter((file) => !existingMigrations.has(file)); + expect(generated.length, result.stderr).toBeGreaterThan(0); + const pulled = await Promise.all( + generated.map((file) => readFile(join(migrations, file), "utf8")), ); + expect(pulled.join("\n"), result.stderr).toContain(marker); } catch (error) { targetError = error; - } - - const cleanupErrors: Array = []; - // Remove all migrations created by this test before resetting. This - // includes both the seed migration and the migration generated by - // `db pull`; resetting with only the generated grant statements left - // behind can reference a table that no longer exists. - let currentMigrations: ReadonlyArray = []; - try { - currentMigrations = await readdir(migrations); - } catch (error) { - cleanupErrors.push(error); - } - for (const file of currentMigrations.filter((candidate) => !existingMigrations.has(candidate))) { + } finally { + // Remove the generated migration before resetting so the reset replays an + // empty local set and restores the baseline schema, dropping the marker. + let currentMigrations: ReadonlyArray = []; try { - await unlink(join(migrations, file)); + currentMigrations = await readdir(migrations); } catch (error) { - cleanupErrors.push( - new Error( - `db pull cleanup could not remove test migration ${join(migrations, file)}: ${ - error instanceof Error ? error.message : String(error) - }`, - ), - ); + cleanupErrors.push(error); + } + for (const file of currentMigrations.filter( + (candidate) => !existingMigrations.has(candidate), + )) { + try { + await unlink(join(migrations, file)); + } catch (error) { + cleanupErrors.push(error); + } + } + try { + const reset = await cli(["db", "reset", "--db-url", project.dbUrl, "--yes"]); + requireLiveSuccess(reset, "db reset cleanup after db pull"); + } catch (error) { + cleanupErrors.push(error); } } - - try { - const reset = await cli(["db", "reset", "--db-url", project.dbUrl, "--yes"]); - requireLiveSuccess(reset, "db reset cleanup after db pull"); - } catch (error) { - cleanupErrors.push(error); - } - throwWithCleanup(targetError, cleanupErrors); }); From 9dbce90ca0269ebceeb8bf8aa342a93089527f71 Mon Sep 17 00:00:00 2001 From: 7ttp <117663341+7ttp@users.noreply.github.com> Date: Thu, 27 Aug 2026 20:34:26 +0530 Subject: [PATCH 4/6] docs: describe the in-sync exit without go framing --- apps/cli/src/legacy/commands/db/pull/pull.live.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/cli/src/legacy/commands/db/pull/pull.live.test.ts b/apps/cli/src/legacy/commands/db/pull/pull.live.test.ts index d24b2011b4..6597aaa639 100644 --- a/apps/cli/src/legacy/commands/db/pull/pull.live.test.ts +++ b/apps/cli/src/legacy/commands/db/pull/pull.live.test.ts @@ -5,8 +5,8 @@ import { expect } from "vitest"; import { requireLiveSuccess, test, throwWithCleanup } from "../../../../../tests/helpers/live.ts"; -// `db pull` exits non-zero when the diff comes back empty (Go-identical, see -// IN_SYNC_SUGGESTION in pull.handler.ts), so the journey seeds a remote-only +// `db pull` exits non-zero when the diff comes back empty (the in-sync +// finding, see IN_SYNC_SUGGESTION in pull.handler.ts), so the journey seeds a remote-only // marker table through `db query` — no local migration and no history row. // The marker cannot exist in the freshly provisioned shadow, so the diff is // never empty regardless of engine and the pull deterministically writes it. From 5e5ee11f8b0b0f7cba56d120cea69f02609e4292 Mon Sep 17 00:00:00 2001 From: 7ttp <117663341+7ttp@users.noreply.github.com> Date: Fri, 28 Aug 2026 22:55:43 +0530 Subject: [PATCH 5/6] Nits --- .../commands/services/services.live.test.ts | 14 +++-------- .../commands/storage/cp/cp.live.test.ts | 20 +++------------- .../commands/storage/ls/ls.live.test.ts | 22 ++++------------- .../commands/storage/mv/mv.live.test.ts | 24 ++++--------------- .../commands/storage/rm/rm.live.test.ts | 22 ++++------------- .../commands/storage/storage.live-helpers.ts | 21 ++++++++++++++++ 6 files changed, 40 insertions(+), 83 deletions(-) create mode 100644 apps/cli/src/legacy/commands/storage/storage.live-helpers.ts diff --git a/apps/cli/src/legacy/commands/services/services.live.test.ts b/apps/cli/src/legacy/commands/services/services.live.test.ts index 1bf1334fc4..eaf190f516 100644 --- a/apps/cli/src/legacy/commands/services/services.live.test.ts +++ b/apps/cli/src/legacy/commands/services/services.live.test.ts @@ -2,13 +2,15 @@ import { expect } from "vitest"; import { requireLiveSuccess, test } from "../../../../tests/helpers/live.ts"; -test("merges remote versions from the linked live project into table and json output", async ({ +test("merges remote versions from the linked live project into services output", async ({ cli, project, }) => { const linked = await cli(["link", "--project-ref", project.ref, "--skip-pooler"]); requireLiveSuccess(linked, "link setup for services"); + // One remote-backed invocation is the live golden path; cross-format + // rendering is integration-tested with fixed remote data. const json = await cli(["services", "-o", "json"]); expect(json.exitCode, json.stderr).toBe(0); const rows = JSON.parse(json.stdout) as Array<{ name: string; local: string; remote: string }>; @@ -18,14 +20,4 @@ test("merges remote versions from the linked live project into table and json ou throw new Error(`supabase/postgres row missing from services json:\n${json.stdout}`); } expect(postgres.remote.length, json.stdout).toBeGreaterThan(0); - - const table = await cli(["services"]); - expect(table.exitCode, table.stderr).toBe(0); - const postgresRow = table.stdout - .split("\n") - .find((line) => line.split("|")[0]?.trim() === "supabase/postgres"); - if (postgresRow === undefined) { - throw new Error(`supabase/postgres row missing from services table:\n${table.stdout}`); - } - expect(postgresRow.split("|")[2]?.trim(), table.stdout).toBe(postgres.remote); }); diff --git a/apps/cli/src/legacy/commands/storage/cp/cp.live.test.ts b/apps/cli/src/legacy/commands/storage/cp/cp.live.test.ts index c66f82c981..aa33d4072f 100644 --- a/apps/cli/src/legacy/commands/storage/cp/cp.live.test.ts +++ b/apps/cli/src/legacy/commands/storage/cp/cp.live.test.ts @@ -4,21 +4,7 @@ import { join } from "node:path"; import { expect } from "vitest"; import { requireLiveSuccess, test, throwWithCleanup } from "../../../../../tests/helpers/live.ts"; - -const STORAGE_FLAGS = ["--linked", "--experimental"]; - -async function removeObject( - cli: (args: string[]) => Promise<{ exitCode: number; stdout: string; stderr: string }>, - remote: string, -): Promise { - const removed = await cli(["storage", "rm", remote, "--yes", ...STORAGE_FLAGS]); - if ( - removed.exitCode !== 0 && - !/not found|does not exist/i.test(`${removed.stdout}\n${removed.stderr}`) - ) { - throw new Error(`storage rm cleanup failed:\n${removed.stdout}\n${removed.stderr}`); - } -} +import { legacyRemoveStorageLiveObject, legacyStorageLiveFlags } from "../storage.live-helpers.ts"; test("copies a local file to the remote bucket", async ({ cli, project, workspace }) => { const suffix = randomUUID().slice(0, 8); @@ -34,13 +20,13 @@ test("copies a local file to the remote bucket", async ({ cli, project, workspac }); requireLiveSuccess(linked, "link setup for storage cp"); - const result = await cli(["storage", "cp", local, remote, ...STORAGE_FLAGS]); + const result = await cli(["storage", "cp", local, remote, ...legacyStorageLiveFlags]); expect(result.exitCode, result.stderr).toBe(0); } catch (error) { targetError = error; } finally { try { - await removeObject(cli, remote); + await legacyRemoveStorageLiveObject(cli, remote); } catch (error) { cleanupError = error; } diff --git a/apps/cli/src/legacy/commands/storage/ls/ls.live.test.ts b/apps/cli/src/legacy/commands/storage/ls/ls.live.test.ts index b678ceb7df..57489d519b 100644 --- a/apps/cli/src/legacy/commands/storage/ls/ls.live.test.ts +++ b/apps/cli/src/legacy/commands/storage/ls/ls.live.test.ts @@ -4,21 +4,7 @@ import { join } from "node:path"; import { expect } from "vitest"; import { requireLiveSuccess, test, throwWithCleanup } from "../../../../../tests/helpers/live.ts"; - -const STORAGE_FLAGS = ["--linked", "--experimental"]; - -async function removeObject( - cli: (args: string[]) => Promise<{ exitCode: number; stdout: string; stderr: string }>, - remote: string, -): Promise { - const removed = await cli(["storage", "rm", remote, "--yes", ...STORAGE_FLAGS]); - if ( - removed.exitCode !== 0 && - !/not found|does not exist/i.test(`${removed.stdout}\n${removed.stderr}`) - ) { - throw new Error(`storage rm cleanup failed:\n${removed.stdout}\n${removed.stderr}`); - } -} +import { legacyRemoveStorageLiveObject, legacyStorageLiveFlags } from "../storage.live-helpers.ts"; test("lists an uploaded object", async ({ cli, project, workspace }) => { const suffix = randomUUID().slice(0, 8); @@ -33,14 +19,14 @@ test("lists an uploaded object", async ({ cli, project, workspace }) => { env: { SUPABASE_DB_PASSWORD: project.dbPassword }, }); requireLiveSuccess(linked, "link setup for storage ls"); - const uploaded = await cli(["storage", "cp", local, remote, ...STORAGE_FLAGS]); + const uploaded = await cli(["storage", "cp", local, remote, ...legacyStorageLiveFlags]); requireLiveSuccess(uploaded, "storage cp setup for storage ls"); const result = await cli([ "storage", "ls", `ss:///${project.storageBucket}/`, - ...STORAGE_FLAGS, + ...legacyStorageLiveFlags, ]); expect(result.exitCode, result.stderr).toBe(0); expect(result.stdout).toContain(`upload-${suffix}.txt`); @@ -48,7 +34,7 @@ test("lists an uploaded object", async ({ cli, project, workspace }) => { targetError = error; } finally { try { - await removeObject(cli, remote); + await legacyRemoveStorageLiveObject(cli, remote); } catch (error) { cleanupError = error; } diff --git a/apps/cli/src/legacy/commands/storage/mv/mv.live.test.ts b/apps/cli/src/legacy/commands/storage/mv/mv.live.test.ts index d7a8f1f8f4..2ec1d2e18b 100644 --- a/apps/cli/src/legacy/commands/storage/mv/mv.live.test.ts +++ b/apps/cli/src/legacy/commands/storage/mv/mv.live.test.ts @@ -4,21 +4,7 @@ import { join } from "node:path"; import { expect } from "vitest"; import { requireLiveSuccess, test, throwWithCleanup } from "../../../../../tests/helpers/live.ts"; - -const STORAGE_FLAGS = ["--linked", "--experimental"]; - -async function removeObject( - cli: (args: string[]) => Promise<{ exitCode: number; stdout: string; stderr: string }>, - remote: string, -): Promise { - const removed = await cli(["storage", "rm", remote, "--yes", ...STORAGE_FLAGS]); - if ( - removed.exitCode !== 0 && - !/not found|does not exist/i.test(`${removed.stdout}\n${removed.stderr}`) - ) { - throw new Error(`storage rm cleanup failed:\n${removed.stdout}\n${removed.stderr}`); - } -} +import { legacyRemoveStorageLiveObject, legacyStorageLiveFlags } from "../storage.live-helpers.ts"; test("moves an uploaded object to a new path", async ({ cli, project, workspace }) => { const suffix = randomUUID().slice(0, 8); @@ -34,10 +20,10 @@ test("moves an uploaded object to a new path", async ({ cli, project, workspace env: { SUPABASE_DB_PASSWORD: project.dbPassword }, }); requireLiveSuccess(linked, "link setup for storage mv"); - const uploaded = await cli(["storage", "cp", local, source, ...STORAGE_FLAGS]); + const uploaded = await cli(["storage", "cp", local, source, ...legacyStorageLiveFlags]); requireLiveSuccess(uploaded, "storage cp setup for storage mv"); - const moved = await cli(["storage", "mv", source, destination, ...STORAGE_FLAGS]); + const moved = await cli(["storage", "mv", source, destination, ...legacyStorageLiveFlags]); expect(moved.exitCode, moved.stderr).toBe(0); expect(moved.stderr, moved.stderr).toContain("Moving object:"); @@ -45,7 +31,7 @@ test("moves an uploaded object to a new path", async ({ cli, project, workspace "storage", "ls", `ss:///${project.storageBucket}/`, - ...STORAGE_FLAGS, + ...legacyStorageLiveFlags, ]); requireLiveSuccess(listed, "storage ls proof for storage mv"); expect(listed.stdout).toContain(`mv-dst-${suffix}.txt`); @@ -55,7 +41,7 @@ test("moves an uploaded object to a new path", async ({ cli, project, workspace } finally { for (const remote of [destination, source]) { try { - await removeObject(cli, remote); + await legacyRemoveStorageLiveObject(cli, remote); } catch (error) { cleanupErrors.push(error); } diff --git a/apps/cli/src/legacy/commands/storage/rm/rm.live.test.ts b/apps/cli/src/legacy/commands/storage/rm/rm.live.test.ts index 674268a2cc..e22e0d25b7 100644 --- a/apps/cli/src/legacy/commands/storage/rm/rm.live.test.ts +++ b/apps/cli/src/legacy/commands/storage/rm/rm.live.test.ts @@ -4,21 +4,7 @@ import { join } from "node:path"; import { expect } from "vitest"; import { requireLiveSuccess, test, throwWithCleanup } from "../../../../../tests/helpers/live.ts"; - -const STORAGE_FLAGS = ["--linked", "--experimental"]; - -async function removeObject( - cli: (args: string[]) => Promise<{ exitCode: number; stdout: string; stderr: string }>, - remote: string, -): Promise { - const removed = await cli(["storage", "rm", remote, "--yes", ...STORAGE_FLAGS]); - if ( - removed.exitCode !== 0 && - !/not found|does not exist/i.test(`${removed.stdout}\n${removed.stderr}`) - ) { - throw new Error(`storage rm cleanup failed:\n${removed.stdout}\n${removed.stderr}`); - } -} +import { legacyRemoveStorageLiveObject, legacyStorageLiveFlags } from "../storage.live-helpers.ts"; test("removes an uploaded object", async ({ cli, project, workspace }) => { const suffix = randomUUID().slice(0, 8); @@ -33,16 +19,16 @@ test("removes an uploaded object", async ({ cli, project, workspace }) => { env: { SUPABASE_DB_PASSWORD: project.dbPassword }, }); requireLiveSuccess(linked, "link setup for storage rm"); - const uploaded = await cli(["storage", "cp", local, remote, ...STORAGE_FLAGS]); + const uploaded = await cli(["storage", "cp", local, remote, ...legacyStorageLiveFlags]); requireLiveSuccess(uploaded, "storage cp setup for storage rm"); - const result = await cli(["storage", "rm", remote, "--yes", ...STORAGE_FLAGS]); + const result = await cli(["storage", "rm", remote, "--yes", ...legacyStorageLiveFlags]); expect(result.exitCode, result.stderr).toBe(0); } catch (error) { targetError = error; } finally { try { - await removeObject(cli, remote); + await legacyRemoveStorageLiveObject(cli, remote); } catch (error) { cleanupError = error; } diff --git a/apps/cli/src/legacy/commands/storage/storage.live-helpers.ts b/apps/cli/src/legacy/commands/storage/storage.live-helpers.ts new file mode 100644 index 0000000000..478a8cfd0e --- /dev/null +++ b/apps/cli/src/legacy/commands/storage/storage.live-helpers.ts @@ -0,0 +1,21 @@ +/** Flags every storage live test passes: the suite links the shared project + * and the storage command family is experimental-gated. */ +export const legacyStorageLiveFlags: ReadonlyArray = ["--linked", "--experimental"]; + +/** + * Best-effort exact-object cleanup for storage live tests: removes one owned + * remote object, tolerating an already-removed target so teardown stays + * idempotent across the moved/renamed paths a test may leave behind. + */ +export async function legacyRemoveStorageLiveObject( + cli: (args: string[]) => Promise<{ exitCode: number; stdout: string; stderr: string }>, + remote: string, +): Promise { + const removed = await cli(["storage", "rm", remote, "--yes", ...legacyStorageLiveFlags]); + if ( + removed.exitCode !== 0 && + !/not found|does not exist/i.test(`${removed.stdout}\n${removed.stderr}`) + ) { + throw new Error(`storage rm cleanup failed:\n${removed.stdout}\n${removed.stderr}`); + } +} From 875d0c8361af99a7796257e9350510ea216b1271 Mon Sep 17 00:00:00 2001 From: 7ttp <117663341+7ttp@users.noreply.github.com> Date: Fri, 28 Aug 2026 22:58:49 +0530 Subject: [PATCH 6/6] test: keep storage live helpers in the shared live module --- .../commands/storage/cp/cp.live.test.ts | 13 +++++++---- .../commands/storage/ls/ls.live.test.ts | 15 ++++++++----- .../commands/storage/mv/mv.live.test.ts | 17 +++++++++----- .../commands/storage/rm/rm.live.test.ts | 15 ++++++++----- .../commands/storage/storage.live-helpers.ts | 21 ------------------ apps/cli/tests/helpers/live.ts | 22 +++++++++++++++++++ 6 files changed, 62 insertions(+), 41 deletions(-) delete mode 100644 apps/cli/src/legacy/commands/storage/storage.live-helpers.ts diff --git a/apps/cli/src/legacy/commands/storage/cp/cp.live.test.ts b/apps/cli/src/legacy/commands/storage/cp/cp.live.test.ts index aa33d4072f..7b0b247f2c 100644 --- a/apps/cli/src/legacy/commands/storage/cp/cp.live.test.ts +++ b/apps/cli/src/legacy/commands/storage/cp/cp.live.test.ts @@ -3,8 +3,13 @@ import { writeFile } from "node:fs/promises"; import { join } from "node:path"; import { expect } from "vitest"; -import { requireLiveSuccess, test, throwWithCleanup } from "../../../../../tests/helpers/live.ts"; -import { legacyRemoveStorageLiveObject, legacyStorageLiveFlags } from "../storage.live-helpers.ts"; +import { + removeStorageLiveObject, + requireLiveSuccess, + storageLiveFlags, + test, + throwWithCleanup, +} from "../../../../../tests/helpers/live.ts"; test("copies a local file to the remote bucket", async ({ cli, project, workspace }) => { const suffix = randomUUID().slice(0, 8); @@ -20,13 +25,13 @@ test("copies a local file to the remote bucket", async ({ cli, project, workspac }); requireLiveSuccess(linked, "link setup for storage cp"); - const result = await cli(["storage", "cp", local, remote, ...legacyStorageLiveFlags]); + const result = await cli(["storage", "cp", local, remote, ...storageLiveFlags]); expect(result.exitCode, result.stderr).toBe(0); } catch (error) { targetError = error; } finally { try { - await legacyRemoveStorageLiveObject(cli, remote); + await removeStorageLiveObject(cli, remote); } catch (error) { cleanupError = error; } diff --git a/apps/cli/src/legacy/commands/storage/ls/ls.live.test.ts b/apps/cli/src/legacy/commands/storage/ls/ls.live.test.ts index 57489d519b..ea67ee203e 100644 --- a/apps/cli/src/legacy/commands/storage/ls/ls.live.test.ts +++ b/apps/cli/src/legacy/commands/storage/ls/ls.live.test.ts @@ -3,8 +3,13 @@ import { writeFile } from "node:fs/promises"; import { join } from "node:path"; import { expect } from "vitest"; -import { requireLiveSuccess, test, throwWithCleanup } from "../../../../../tests/helpers/live.ts"; -import { legacyRemoveStorageLiveObject, legacyStorageLiveFlags } from "../storage.live-helpers.ts"; +import { + removeStorageLiveObject, + requireLiveSuccess, + storageLiveFlags, + test, + throwWithCleanup, +} from "../../../../../tests/helpers/live.ts"; test("lists an uploaded object", async ({ cli, project, workspace }) => { const suffix = randomUUID().slice(0, 8); @@ -19,14 +24,14 @@ test("lists an uploaded object", async ({ cli, project, workspace }) => { env: { SUPABASE_DB_PASSWORD: project.dbPassword }, }); requireLiveSuccess(linked, "link setup for storage ls"); - const uploaded = await cli(["storage", "cp", local, remote, ...legacyStorageLiveFlags]); + const uploaded = await cli(["storage", "cp", local, remote, ...storageLiveFlags]); requireLiveSuccess(uploaded, "storage cp setup for storage ls"); const result = await cli([ "storage", "ls", `ss:///${project.storageBucket}/`, - ...legacyStorageLiveFlags, + ...storageLiveFlags, ]); expect(result.exitCode, result.stderr).toBe(0); expect(result.stdout).toContain(`upload-${suffix}.txt`); @@ -34,7 +39,7 @@ test("lists an uploaded object", async ({ cli, project, workspace }) => { targetError = error; } finally { try { - await legacyRemoveStorageLiveObject(cli, remote); + await removeStorageLiveObject(cli, remote); } catch (error) { cleanupError = error; } diff --git a/apps/cli/src/legacy/commands/storage/mv/mv.live.test.ts b/apps/cli/src/legacy/commands/storage/mv/mv.live.test.ts index 2ec1d2e18b..96b21fa1a6 100644 --- a/apps/cli/src/legacy/commands/storage/mv/mv.live.test.ts +++ b/apps/cli/src/legacy/commands/storage/mv/mv.live.test.ts @@ -3,8 +3,13 @@ import { writeFile } from "node:fs/promises"; import { join } from "node:path"; import { expect } from "vitest"; -import { requireLiveSuccess, test, throwWithCleanup } from "../../../../../tests/helpers/live.ts"; -import { legacyRemoveStorageLiveObject, legacyStorageLiveFlags } from "../storage.live-helpers.ts"; +import { + removeStorageLiveObject, + requireLiveSuccess, + storageLiveFlags, + test, + throwWithCleanup, +} from "../../../../../tests/helpers/live.ts"; test("moves an uploaded object to a new path", async ({ cli, project, workspace }) => { const suffix = randomUUID().slice(0, 8); @@ -20,10 +25,10 @@ test("moves an uploaded object to a new path", async ({ cli, project, workspace env: { SUPABASE_DB_PASSWORD: project.dbPassword }, }); requireLiveSuccess(linked, "link setup for storage mv"); - const uploaded = await cli(["storage", "cp", local, source, ...legacyStorageLiveFlags]); + const uploaded = await cli(["storage", "cp", local, source, ...storageLiveFlags]); requireLiveSuccess(uploaded, "storage cp setup for storage mv"); - const moved = await cli(["storage", "mv", source, destination, ...legacyStorageLiveFlags]); + const moved = await cli(["storage", "mv", source, destination, ...storageLiveFlags]); expect(moved.exitCode, moved.stderr).toBe(0); expect(moved.stderr, moved.stderr).toContain("Moving object:"); @@ -31,7 +36,7 @@ test("moves an uploaded object to a new path", async ({ cli, project, workspace "storage", "ls", `ss:///${project.storageBucket}/`, - ...legacyStorageLiveFlags, + ...storageLiveFlags, ]); requireLiveSuccess(listed, "storage ls proof for storage mv"); expect(listed.stdout).toContain(`mv-dst-${suffix}.txt`); @@ -41,7 +46,7 @@ test("moves an uploaded object to a new path", async ({ cli, project, workspace } finally { for (const remote of [destination, source]) { try { - await legacyRemoveStorageLiveObject(cli, remote); + await removeStorageLiveObject(cli, remote); } catch (error) { cleanupErrors.push(error); } diff --git a/apps/cli/src/legacy/commands/storage/rm/rm.live.test.ts b/apps/cli/src/legacy/commands/storage/rm/rm.live.test.ts index e22e0d25b7..e2ec238ada 100644 --- a/apps/cli/src/legacy/commands/storage/rm/rm.live.test.ts +++ b/apps/cli/src/legacy/commands/storage/rm/rm.live.test.ts @@ -3,8 +3,13 @@ import { writeFile } from "node:fs/promises"; import { join } from "node:path"; import { expect } from "vitest"; -import { requireLiveSuccess, test, throwWithCleanup } from "../../../../../tests/helpers/live.ts"; -import { legacyRemoveStorageLiveObject, legacyStorageLiveFlags } from "../storage.live-helpers.ts"; +import { + removeStorageLiveObject, + requireLiveSuccess, + storageLiveFlags, + test, + throwWithCleanup, +} from "../../../../../tests/helpers/live.ts"; test("removes an uploaded object", async ({ cli, project, workspace }) => { const suffix = randomUUID().slice(0, 8); @@ -19,16 +24,16 @@ test("removes an uploaded object", async ({ cli, project, workspace }) => { env: { SUPABASE_DB_PASSWORD: project.dbPassword }, }); requireLiveSuccess(linked, "link setup for storage rm"); - const uploaded = await cli(["storage", "cp", local, remote, ...legacyStorageLiveFlags]); + const uploaded = await cli(["storage", "cp", local, remote, ...storageLiveFlags]); requireLiveSuccess(uploaded, "storage cp setup for storage rm"); - const result = await cli(["storage", "rm", remote, "--yes", ...legacyStorageLiveFlags]); + const result = await cli(["storage", "rm", remote, "--yes", ...storageLiveFlags]); expect(result.exitCode, result.stderr).toBe(0); } catch (error) { targetError = error; } finally { try { - await legacyRemoveStorageLiveObject(cli, remote); + await removeStorageLiveObject(cli, remote); } catch (error) { cleanupError = error; } diff --git a/apps/cli/src/legacy/commands/storage/storage.live-helpers.ts b/apps/cli/src/legacy/commands/storage/storage.live-helpers.ts deleted file mode 100644 index 478a8cfd0e..0000000000 --- a/apps/cli/src/legacy/commands/storage/storage.live-helpers.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** Flags every storage live test passes: the suite links the shared project - * and the storage command family is experimental-gated. */ -export const legacyStorageLiveFlags: ReadonlyArray = ["--linked", "--experimental"]; - -/** - * Best-effort exact-object cleanup for storage live tests: removes one owned - * remote object, tolerating an already-removed target so teardown stays - * idempotent across the moved/renamed paths a test may leave behind. - */ -export async function legacyRemoveStorageLiveObject( - cli: (args: string[]) => Promise<{ exitCode: number; stdout: string; stderr: string }>, - remote: string, -): Promise { - const removed = await cli(["storage", "rm", remote, "--yes", ...legacyStorageLiveFlags]); - if ( - removed.exitCode !== 0 && - !/not found|does not exist/i.test(`${removed.stdout}\n${removed.stderr}`) - ) { - throw new Error(`storage rm cleanup failed:\n${removed.stdout}\n${removed.stderr}`); - } -} diff --git a/apps/cli/tests/helpers/live.ts b/apps/cli/tests/helpers/live.ts index 9b87dc3465..d17ea1bbb1 100644 --- a/apps/cli/tests/helpers/live.ts +++ b/apps/cli/tests/helpers/live.ts @@ -122,6 +122,28 @@ export function requireLiveSuccess( } } +/** Flags every storage live test passes: the suite links the shared project + * and the storage command family is experimental-gated. */ +export const storageLiveFlags: ReadonlyArray = ["--linked", "--experimental"]; + +/** + * Best-effort exact-object cleanup for storage live tests: removes one owned + * remote object, tolerating an already-removed target so teardown stays + * idempotent across the moved/renamed paths a test may leave behind. + */ +export async function removeStorageLiveObject( + cli: (args: string[]) => Promise<{ exitCode: number; stdout: string; stderr: string }>, + remote: string, +): Promise { + const removed = await cli(["storage", "rm", remote, "--yes", ...storageLiveFlags]); + if ( + removed.exitCode !== 0 && + !/not found|does not exist/i.test(`${removed.stdout}\n${removed.stderr}`) + ) { + throw new Error(`storage rm cleanup failed:\n${removed.stdout}\n${removed.stderr}`); + } +} + /** Rethrow a target failure without discarding failures from exact cleanup. */ export function throwWithCleanup(primary: unknown, cleanup: ReadonlyArray): void { if (primary !== undefined) {