import { sql } from 'kysely' import { nanoid } from 'nanoid' import * as TestApp from '../../../test/App.js' import * as Runtime from '../../../test/runtime.js' import * as Db from '../Db.js' import * as StripeCustomers from './stripeCustomers.js' const create = () => Db.postgres({ connectionString: Runtime.postgresUrl, schema: `t_${nanoid()}` }) /** Redacts nondeterministic row fields (timestamps). */ function redact(record: StripeCustomers.Record) { return { ...record, createdAt: '', updatedAt: '' } } describe('create', () => { test('behavior: inserts with status none', async () => { const db = create() await db.migrate() const record = await StripeCustomers.create(db, { orgId: 'org_1', stripeCustomerId: 'cus_a', }) expect(redact(record)).toMatchInlineSnapshot(` { "createdAt": "", "environment": "production", "orgId": "org_1", "status": "none", "stripeCustomerId": "cus_a", "updatedAt": "", } `) await db.close() }) test('behavior: a concurrent duplicate resolves to the winning row', async () => { const db = create() await db.migrate() const winner = await StripeCustomers.create(db, { orgId: 'org_1', stripeCustomerId: 'cus_a' }) // Loser of the first-checkout race: same org, its own Stripe customer. const resolved = await StripeCustomers.create(db, { orgId: 'org_1', stripeCustomerId: 'cus_b' }) // prettier-ignore expect(resolved).toStrictEqual(winner) expect(await StripeCustomers.getByCustomer(db, 'cus_b')).toBeUndefined() await db.close() }) }) describe('get', () => { test('behavior: reads by org; undefined when absent', async () => { const db = create() await db.migrate() const record = await StripeCustomers.create(db, { orgId: 'org_1', stripeCustomerId: 'cus_a' }) expect(await StripeCustomers.get(db, 'org_1')).toStrictEqual(record) expect(await StripeCustomers.get(db, 'org_missing')).toBeUndefined() await db.close() }) test('behavior: production and sandbox rows coexist per org and read disjointly', async () => { const db = create() await db.migrate() const production = await StripeCustomers.create(db, { orgId: 'org_1', stripeCustomerId: 'cus_live' }) // prettier-ignore const sandbox = await StripeCustomers.create(db, { environment: 'sandbox', orgId: 'org_1', stripeCustomerId: 'cus_test' }) // prettier-ignore expect(sandbox.environment).toBe('sandbox') expect(await StripeCustomers.get(db, 'org_1')).toStrictEqual(production) expect(await StripeCustomers.get(db, 'org_1', 'sandbox')).toStrictEqual(sandbox) await db.close() }) }) describe('getByCustomer', () => { test('behavior: reads by Stripe customer id; undefined for unknown customers', async () => { const db = create() await db.migrate() const record = await StripeCustomers.create(db, { orgId: 'org_1', stripeCustomerId: 'cus_a' }) expect(await StripeCustomers.getByCustomer(db, 'cus_a')).toStrictEqual(record) expect(await StripeCustomers.getByCustomer(db, 'cus_unknown')).toBeUndefined() await db.close() }) }) describe('setStatus', () => { test('behavior: persists the status and bumps updatedAt', async () => { const db = create() await db.migrate() const record = await StripeCustomers.create(db, { orgId: 'org_1', stripeCustomerId: 'cus_a' }) const updated = await StripeCustomers.setStatus(db, 'org_1', 'active') expect(updated?.status).toMatchInlineSnapshot(`"active"`) expect(updated?.createdAt).toBe(record.createdAt) await db.close() }) test('behavior: undefined when the org has no billing account', async () => { const db = create() await db.migrate() expect(await StripeCustomers.setStatus(db, 'org_missing', 'active')).toBeUndefined() await db.close() }) }) describe('deleteByOrg', () => { test('behavior: deletes the row; false when absent', async () => { const db = create() await db.migrate() await StripeCustomers.create(db, { orgId: 'org_1', stripeCustomerId: 'cus_a' }) expect(await StripeCustomers.deleteByOrg(db, 'org_1')).toBe(true) expect(await StripeCustomers.get(db, 'org_1')).toBeUndefined() expect(await StripeCustomers.deleteByOrg(db, 'org_1')).toBe(false) await db.close() }) }) describe('withLock', () => { test('serializes refreshes on one handle without blocking another environment', async () => { const db = TestApp.databaseFactory()() await StripeCustomers.create(db, { orgId: 'org_1', stripeCustomerId: 'cus_prod' }) await StripeCustomers.create(db, { environment: 'sandbox', orgId: 'org_1', stripeCustomerId: 'cus_sandbox' }) // prettier-ignore const entered = Promise.withResolvers() const release = Promise.withResolvers() const active = StripeCustomers.withLock(db, { async fn(tx) { entered.resolve() await release.promise return StripeCustomers.setStatus(tx, 'org_1', 'active') }, stripeCustomerId: 'cus_prod', }) await entered.promise const detached = StripeCustomers.withLock(db, { async fn(tx, record) { expect(record?.status).toBe('active') return StripeCustomers.setStatus(tx, 'org_1', 'none') }, stripeCustomerId: 'cus_prod', }) const sandbox = StripeCustomers.withLock(db, { fn: (tx) => StripeCustomers.setStatus(tx, 'org_1', 'active', 'sandbox'), stripeCustomerId: 'cus_sandbox', }) try { await expect .poll(async () => { const { rows } = await sql<{ blocked: boolean }>` SELECT EXISTS ( SELECT 1 FROM pg_locks JOIN pg_stat_activity USING (pid) WHERE relation = 'stripe_customers'::regclass AND wait_event_type = 'Lock' ) AS blocked `.execute(db.kysely) return rows[0]!.blocked }) .toBe(true) expect((await sandbox)?.status).toBe('active') } finally { release.resolve() await Promise.allSettled([active, detached, sandbox]) } expect((await active)?.status).toBe('active') expect((await detached)?.status).toBe('none') expect((await StripeCustomers.get(db, 'org_1'))?.status).toBe('none') expect((await StripeCustomers.get(db, 'org_1', 'sandbox'))?.status).toBe('active') await db.close() }) test('rolls back failed refreshes and releases the lock', async () => { const db = TestApp.database() await StripeCustomers.create(db, { orgId: 'org_1', stripeCustomerId: 'cus_a' }) await expect( StripeCustomers.withLock(db, { async fn(tx) { await StripeCustomers.setStatus(tx, 'org_1', 'active') throw new Error('refresh failed') }, stripeCustomerId: 'cus_a', }), ).rejects.toThrowErrorMatchingInlineSnapshot(`[Error: refresh failed]`) expect((await StripeCustomers.get(db, 'org_1'))?.status).toBe('none') const updated = await StripeCustomers.withLock(db, { fn: (tx) => StripeCustomers.setStatus(tx, 'org_1', 'past_due'), stripeCustomerId: 'cus_a', }) expect(updated?.status).toBe('past_due') await db.close() }) test('passes an absent record for unknown customers', async () => { const db = TestApp.database() const record = await StripeCustomers.withLock(db, { fn: async (_tx, record) => record, stripeCustomerId: 'cus_unknown', }) expect(record).toBeUndefined() await db.close() }) })