From 09c8ef194654e3add0779304d2c7afa41902d343 Mon Sep 17 00:00:00 2001 From: Konsti Wohlwend Date: Tue, 30 Jun 2026 10:43:10 -0700 Subject: [PATCH] Keep local emulator removal in a new migration, not edits to applied ones (#1694) --- .../tests/cascades-on-project-delete.ts | 30 +++++++++++++ .../tests/persists-and-enforces-uniqueness.ts | 44 +++++++++++++++++++ .../migration.sql | 7 +++ .../tests/default-and-updates.ts | 18 +++++++- 4 files changed, 98 insertions(+), 1 deletion(-) create mode 100644 apps/backend/prisma/migrations/20260226100000_add_local_emulator_project_mapping/tests/cascades-on-project-delete.ts create mode 100644 apps/backend/prisma/migrations/20260226100000_add_local_emulator_project_mapping/tests/persists-and-enforces-uniqueness.ts diff --git a/apps/backend/prisma/migrations/20260226100000_add_local_emulator_project_mapping/tests/cascades-on-project-delete.ts b/apps/backend/prisma/migrations/20260226100000_add_local_emulator_project_mapping/tests/cascades-on-project-delete.ts new file mode 100644 index 000000000..76a0434a7 --- /dev/null +++ b/apps/backend/prisma/migrations/20260226100000_add_local_emulator_project_mapping/tests/cascades-on-project-delete.ts @@ -0,0 +1,30 @@ +import { randomUUID } from "crypto"; +import type { Sql } from "postgres"; +import { expect } from "vitest"; + +export const preMigration = async (sql: Sql) => { + const projectId = `test-${randomUUID()}`; + await sql` + INSERT INTO "Project" ("id", "createdAt", "updatedAt", "displayName", "description", "isProductionMode") + VALUES (${projectId}, NOW(), NOW(), 'Cascade Test Project', '', false) + `; + return { projectId }; +}; + +export const postMigration = async (sql: Sql, ctx: Awaited>) => { + const absoluteFilePath = `/tmp/${randomUUID()}/stack.config.ts`; + + await sql` + INSERT INTO "LocalEmulatorProject" ("absoluteFilePath", "projectId", "createdAt", "updatedAt") + VALUES (${absoluteFilePath}, ${ctx.projectId}, NOW(), NOW()) + `; + + await sql`DELETE FROM "Project" WHERE "id" = ${ctx.projectId}`; + + const rows = await sql` + SELECT "absoluteFilePath" + FROM "LocalEmulatorProject" + WHERE "absoluteFilePath" = ${absoluteFilePath} + `; + expect(rows).toHaveLength(0); +}; diff --git a/apps/backend/prisma/migrations/20260226100000_add_local_emulator_project_mapping/tests/persists-and-enforces-uniqueness.ts b/apps/backend/prisma/migrations/20260226100000_add_local_emulator_project_mapping/tests/persists-and-enforces-uniqueness.ts new file mode 100644 index 000000000..6d27834d7 --- /dev/null +++ b/apps/backend/prisma/migrations/20260226100000_add_local_emulator_project_mapping/tests/persists-and-enforces-uniqueness.ts @@ -0,0 +1,44 @@ +import { randomUUID } from "crypto"; +import type { Sql } from "postgres"; +import { expect } from "vitest"; + +export const preMigration = async (sql: Sql) => { + const projectId1 = `test-${randomUUID()}`; + const projectId2 = `test-${randomUUID()}`; + + await sql` + INSERT INTO "Project" ("id", "createdAt", "updatedAt", "displayName", "description", "isProductionMode") + VALUES (${projectId1}, NOW(), NOW(), 'Local Emulator Project 1', '', false) + `; + await sql` + INSERT INTO "Project" ("id", "createdAt", "updatedAt", "displayName", "description", "isProductionMode") + VALUES (${projectId2}, NOW(), NOW(), 'Local Emulator Project 2', '', false) + `; + + return { projectId1, projectId2 }; +}; + +export const postMigration = async (sql: Sql, ctx: Awaited>) => { + const path1 = `/tmp/${randomUUID()}/stack.config.ts`; + const path2 = `/tmp/${randomUUID()}/stack.config.ts`; + + await sql` + INSERT INTO "LocalEmulatorProject" ("absoluteFilePath", "projectId", "createdAt", "updatedAt") + VALUES (${path1}, ${ctx.projectId1}, NOW(), NOW()) + `; + + await expect(sql` + INSERT INTO "LocalEmulatorProject" ("absoluteFilePath", "projectId", "createdAt", "updatedAt") + VALUES (${path1}, ${ctx.projectId2}, NOW(), NOW()) + `).rejects.toThrow(/LocalEmulatorProject_pkey/); + + await expect(sql` + INSERT INTO "LocalEmulatorProject" ("absoluteFilePath", "projectId", "createdAt", "updatedAt") + VALUES (${path2}, ${ctx.projectId1}, NOW(), NOW()) + `).rejects.toThrow(/LocalEmulatorProject_projectId_key/); + + await sql` + INSERT INTO "LocalEmulatorProject" ("absoluteFilePath", "projectId", "createdAt", "updatedAt") + VALUES (${path2}, ${ctx.projectId2}, NOW(), NOW()) + `; +}; diff --git a/apps/backend/prisma/migrations/20260513000000_add_project_development_environment/migration.sql b/apps/backend/prisma/migrations/20260513000000_add_project_development_environment/migration.sql index 2540282eb..d2ba1c650 100644 --- a/apps/backend/prisma/migrations/20260513000000_add_project_development_environment/migration.sql +++ b/apps/backend/prisma/migrations/20260513000000_add_project_development_environment/migration.sql @@ -1,2 +1,9 @@ ALTER TABLE "Project" ADD COLUMN "isDevelopmentEnvironment" BOOLEAN NOT NULL DEFAULT false; + +UPDATE "Project" +SET "isDevelopmentEnvironment" = true +WHERE "id" IN ( + SELECT "projectId" + FROM "LocalEmulatorProject" +); diff --git a/apps/backend/prisma/migrations/20260513000000_add_project_development_environment/tests/default-and-updates.ts b/apps/backend/prisma/migrations/20260513000000_add_project_development_environment/tests/default-and-updates.ts index 2c299c18c..b8f8d641b 100644 --- a/apps/backend/prisma/migrations/20260513000000_add_project_development_environment/tests/default-and-updates.ts +++ b/apps/backend/prisma/migrations/20260513000000_add_project_development_environment/tests/default-and-updates.ts @@ -4,11 +4,20 @@ import { expect } from "vitest"; export const preMigration = async (sql: Sql) => { const projectId = `test-${randomUUID()}`; + const localEmulatorProjectId = `test-${randomUUID()}`; await sql` INSERT INTO "Project" ("id", "createdAt", "updatedAt", "displayName", "description", "isProductionMode") VALUES (${projectId}, NOW(), NOW(), 'Development Environment Flag Project', '', false) `; - return { projectId }; + await sql` + INSERT INTO "Project" ("id", "createdAt", "updatedAt", "displayName", "description", "isProductionMode") + VALUES (${localEmulatorProjectId}, NOW(), NOW(), 'Existing Local Emulator Project', '', false) + `; + await sql` + INSERT INTO "LocalEmulatorProject" ("absoluteFilePath", "projectId", "createdAt", "updatedAt") + VALUES (${`/tmp/${randomUUID()}/stack.config.ts`}, ${localEmulatorProjectId}, NOW(), NOW()) + `; + return { projectId, localEmulatorProjectId }; }; export const postMigration = async (sql: Sql, ctx: Awaited>) => { @@ -20,4 +29,11 @@ export const postMigration = async (sql: Sql, ctx: Awaited