test(testcontainers): strip run foreign keys after schema push

Production drops the foreign keys on and referencing the run tables, but the
test harness builds via prisma db push, which recreates them from the schema
relations. Drop them after the push so test databases match production and a
run can live in either run table.
This commit is contained in:
Dan Sutton
2026-06-19 12:36:00 +01:00
parent 1e60662690
commit 0a591fb5ce
@@ -2,6 +2,7 @@ import { createClient } from "@clickhouse/client";
import { PostgreSqlContainer, StartedPostgreSqlContainer } from "@testcontainers/postgresql";
import { RedisContainer, StartedRedisContainer } from "@testcontainers/redis";
import { tryCatch } from "@trigger.dev/core";
import { PrismaClient } from "@trigger.dev/database";
import Redis from "ioredis";
import path from "path";
import { isDebug } from "std-env";
@@ -48,9 +49,50 @@ export async function pushDatabaseSchema(databaseUrl: string) {
}
);
await dropRunForeignKeys(databaseUrl);
return result;
}
/**
* Production drops every foreign key that sits on, or points at, the run tables (`TaskRun` and
* `task_run_v2`) — a run's id is just a scalar that may live in either physical table, so the FKs
* can't be enforced. `prisma db push` doesn't know that: it recreates a constraint for every
* relation still declared in schema.prisma, so the template DB ends up with run FKs production
* doesn't have. That makes tests diverge — e.g. inserting a child row (a `TaskRunExecutionSnapshot`
* whose `runId` is a `task_run_v2` id) trips a `..._runId_fkey -> TaskRun` constraint that doesn't
* exist in prod. So after the push we strip those FKs to match production exactly.
*
* This is done dynamically (rather than naming each constraint) so any relation added to the schema
* later has its test-only run FK stripped automatically. It only removes FK constraints, so it
* cannot corrupt valid data — it makes the template DB strictly more faithful to production.
*/
async function dropRunForeignKeys(databaseUrl: string) {
const prisma = new PrismaClient({
datasources: { db: { url: databaseUrl } },
});
try {
await prisma.$executeRawUnsafe(`
DO $$
DECLARE r record;
BEGIN
FOR r IN
SELECT conrelid::regclass::text AS tbl, conname
FROM pg_constraint
WHERE contype = 'f'
AND (confrelid IN ('"TaskRun"'::regclass, 'task_run_v2'::regclass)
OR conrelid IN ('"TaskRun"'::regclass, 'task_run_v2'::regclass))
LOOP
EXECUTE format('ALTER TABLE %s DROP CONSTRAINT %I', r.tbl, r.conname);
END LOOP;
END $$;
`);
} finally {
await prisma.$disconnect();
}
}
/**
* Caps each container's CPU/memory to approximate the 2-core CI runner locally (for timing + flake
* reproduction). Set TESTCONTAINERS_CPU (cores per container, e.g. "2") and/or