import { readFile } from 'node:fs/promises'; import { join } from 'node:path'; describe('subscription foundation migration', () => { let migrationSql: string; beforeAll(async () => { migrationSql = await readFile( join( process.cwd(), 'prisma/migrations/20260628160000_milestone121_subscription_foundation/migration.sql', ), 'utf8', ); }); it('creates subscription and entitlement tables', () => { expect(migrationSql).toContain(`CREATE TABLE "user_subscriptions"`); expect(migrationSql).toContain(`CREATE TABLE "user_entitlements"`); }); it('creates the subscription status, plan, and billing provider enums', () => { expect(migrationSql).toContain( `CREATE TYPE "SubscriptionStatus" AS ENUM ('TRIAL', 'ACTIVE', 'PAST_DUE', 'CANCELLED', 'EXPIRED')`, ); expect(migrationSql).toContain( `CREATE TYPE "SubscriptionPlan" AS ENUM ('FREE', 'PRO')`, ); expect(migrationSql).toContain( `CREATE TYPE "BillingProvider" AS ENUM ('PADDLE')`, ); }); it('enforces one subscription per user and unique provider subscription identifiers', () => { expect(migrationSql).toContain( `CREATE UNIQUE INDEX "user_subscriptions_user_id_key"`, ); expect(migrationSql).toContain( `CREATE UNIQUE INDEX "user_subscriptions_provider_subscription_id_key"`, ); }); it('keeps the provider subscription identifier nullable', () => { expect(migrationSql).toContain(`"provider_subscription_id" TEXT,`); expect(migrationSql).not.toContain( `"provider_subscription_id" TEXT NOT NULL`, ); }); it('enforces entitlement uniqueness per user and key', () => { expect(migrationSql).toContain( `CREATE UNIQUE INDEX "user_entitlements_user_id_entitlement_key_key"`, ); expect(migrationSql).toContain( `ON "user_entitlements"("user_id", "entitlement_key")`, ); }); it('relates subscriptions and entitlements to users with cascading deletes', () => { expect(migrationSql).toContain( `ADD CONSTRAINT "user_subscriptions_user_id_fkey"`, ); expect(migrationSql).toContain( `ADD CONSTRAINT "user_entitlements_user_id_fkey"`, ); expect(migrationSql.match(/REFERENCES "users"\("id"\)/g)).toHaveLength(2); expect(migrationSql.match(/ON DELETE CASCADE/g)).toHaveLength(2); }); });