diff --git a/packages/drizzle/__tests__/drizzle.test.ts b/packages/drizzle/__tests__/drizzle.test.ts index 4ee44a95..6a228f26 100644 --- a/packages/drizzle/__tests__/drizzle.test.ts +++ b/packages/drizzle/__tests__/drizzle.test.ts @@ -117,6 +117,24 @@ beforeAll(async () => { postgresClient = postgres(process.env.DATABASE_URL as string) db = drizzle({ client: postgresClient }) + // Idempotent fixture setup. The `protect-ci` table is shared across the + // drizzle + protect/supabase + stack/supabase integration suites; each + // suite's beforeAll runs the same CREATE TABLE so a fresh database is + // ready without manual DBA work. The schema is the union of every + // column those suites read or write. + await postgresClient` + CREATE TABLE IF NOT EXISTS "protect-ci" ( + id INTEGER PRIMARY KEY GENERATED ALWAYS AS IDENTITY, + email eql_v2_encrypted, + age eql_v2_encrypted, + score eql_v2_encrypted, + profile eql_v2_encrypted, + encrypted eql_v2_encrypted, + created_at TIMESTAMPTZ DEFAULT NOW(), + test_run_id TEXT + ) + ` + const encryptedUsers = unwrapResult( await protectClient.bulkEncryptModels(userSeedData, users), 'bulkEncryptModels', diff --git a/packages/protect/__tests__/supabase.test.ts b/packages/protect/__tests__/supabase.test.ts index ea16deb2..1e4cf674 100644 --- a/packages/protect/__tests__/supabase.test.ts +++ b/packages/protect/__tests__/supabase.test.ts @@ -1,5 +1,6 @@ import 'dotenv/config' import { csColumn, csTable } from '@cipherstash/schema' +import postgres from 'postgres' import { afterAll, beforeAll, describe, expect, it } from 'vitest' import { type Encrypted, @@ -18,6 +19,9 @@ if (!process.env.SUPABASE_URL) { if (!process.env.SUPABASE_ANON_KEY) { throw new Error('Missing env.SUPABASE_ANON_KEY') } +if (!process.env.DATABASE_URL) { + throw new Error('Missing env.DATABASE_URL — needed for fixture setup DDL') +} const supabase = createClient( process.env.SUPABASE_URL, @@ -41,6 +45,33 @@ const TEST_RUN_ID = `test-run-${Date.now()}-${Math.random().toString(36).slice(2 const insertedIds: number[] = [] beforeAll(async () => { + // Idempotent fixture setup. The `protect-ci` table is shared across the + // drizzle + protect/supabase + stack/supabase integration suites; each + // suite's beforeAll runs the same CREATE TABLE so a fresh database is + // ready without manual DBA work. The schema is the union of every + // column those suites read or write. + const sql = postgres(process.env.DATABASE_URL as string, { prepare: false }) + try { + await sql` + CREATE TABLE IF NOT EXISTS "protect-ci" ( + id INTEGER PRIMARY KEY GENERATED ALWAYS AS IDENTITY, + email eql_v2_encrypted, + age eql_v2_encrypted, + score eql_v2_encrypted, + profile eql_v2_encrypted, + encrypted eql_v2_encrypted, + created_at TIMESTAMPTZ DEFAULT NOW(), + test_run_id TEXT + ) + ` + // Tell PostgREST to refresh its schema cache so the supabase-js client + // can see a freshly created table without waiting for the polling + // interval. No-op on plain Postgres (no listener bound). + await sql`NOTIFY pgrst, 'reload schema'` + } finally { + await sql.end() + } + // Clean up any data from this specific test run (safe for concurrent runs) const { error } = await supabase .from('protect-ci') @@ -50,7 +81,7 @@ beforeAll(async () => { if (error) { console.warn(`[protect]: Failed to clean up test data: ${error.message}`) } -}) +}, 30000) afterAll(async () => { // Clean up all data from this test run diff --git a/packages/stack/__tests__/supabase.test.ts b/packages/stack/__tests__/supabase.test.ts index 716dda9d..67fae663 100644 --- a/packages/stack/__tests__/supabase.test.ts +++ b/packages/stack/__tests__/supabase.test.ts @@ -2,6 +2,7 @@ import 'dotenv/config' import { Encryption } from '@/index' import { encryptedColumn, encryptedTable } from '@/schema' import { encryptedSupabase } from '@/supabase' +import postgres from 'postgres' import { afterAll, beforeAll, describe, expect, it } from 'vitest' import { createClient } from '@supabase/supabase-js' @@ -12,6 +13,9 @@ if (!process.env.SUPABASE_URL) { if (!process.env.SUPABASE_ANON_KEY) { throw new Error('Missing env.SUPABASE_ANON_KEY') } +if (!process.env.DATABASE_URL) { + throw new Error('Missing env.DATABASE_URL — needed for fixture setup DDL') +} const supabase = createClient( process.env.SUPABASE_URL, @@ -42,6 +46,33 @@ const TEST_RUN_ID = `test-run-${Date.now()}-${Math.random().toString(36).slice(2 const insertedIds: number[] = [] beforeAll(async () => { + // Idempotent fixture setup. The `protect-ci` table is shared across the + // drizzle + protect/supabase + stack/supabase integration suites; each + // suite's beforeAll runs the same CREATE TABLE so a fresh database is + // ready without manual DBA work. The schema is the union of every + // column those suites read or write. + const sql = postgres(process.env.DATABASE_URL as string, { prepare: false }) + try { + await sql` + CREATE TABLE IF NOT EXISTS "protect-ci" ( + id INTEGER PRIMARY KEY GENERATED ALWAYS AS IDENTITY, + email eql_v2_encrypted, + age eql_v2_encrypted, + score eql_v2_encrypted, + profile eql_v2_encrypted, + encrypted eql_v2_encrypted, + created_at TIMESTAMPTZ DEFAULT NOW(), + test_run_id TEXT + ) + ` + // Tell PostgREST to refresh its schema cache so the supabase-js client + // can see a freshly created table without waiting for the polling + // interval. No-op on plain Postgres (no listener bound). + await sql`NOTIFY pgrst, 'reload schema'` + } finally { + await sql.end() + } + // Clean up any data from this specific test run (safe for concurrent runs) const { error } = await supabase .from('protect-ci')