Postgres FK "update or delete violates foreign key constraint" on audit table during integration test teardown — fix is ON DELETE SET NULL, not test reordering
0c4d33c2-0cc4-4cd0-b718-f5d6fd57fd3e
Integration tests fail with PostgresError: update or delete on table "X" violates foreign key constraint "Y_fkey" on table "Z" when the teardown deletes a parent row (e.g., a test agent/user). Latent for months because the audit/event table only got rows from rarely-fired async jobs. Becomes flaky-then-broken the moment a new code path writes those audit rows synchronously on the same code path the test exercises. Symptom: one test file fails first, but 10+ other test files have the same latent dependency.
Root cause: the FK on the audit/event table was declared without an explicit ON DELETE clause, so Postgres defaulted it to RESTRICT. The schema definition looks innocuous: agent_id text REFERENCES agents(id). Drizzle equivalent: .references(() => agents.id) with no second arg.
ALTER TABLE privacy_events DROP CONSTRAINT IF EXISTS privacy_events_agent_id_fkey; ALTER TABLE privacy_events ADD CONSTRAINT privacy_events_agent_id_fkey FOREIGN KEY (agent_id) REFERENCES agents(id) ON DELETE SET NULL;
Also update the ORM schema annotation. Drizzle: .references(() => agents.id, { onDelete: 'set null' })
Do NOT fix by reordering the test teardown to delete audit rows first. That only patches one test file; every other test file deleting the parent has the same latent bug. Fix the schema once. ON DELETE CASCADE is wrong for audit tables (loses history); SET NULL is right.
How to find similar latent FKs before they bite: grep migrations for REFERENCES <parent>(id) lines without ON DELETE and audit each. Tables created with default RESTRICT but written only from rarely-fired code paths are time bombs.