import { describe, it, expect, beforeAll, beforeEach, afterAll } from 'vitest' import { createPaymentSession, confirmPaymentSession, getPaymentSession, } from '../../src/services/payment.service.js' import { PaymentSessionStatus } from '../../src/constants.js' import { resetDb, resetAppConfig } from '../helpers/db.js' import { createCustomer } from '../helpers/fixtures.js' describe('payment.service', () => { let customer let otherCustomer beforeAll(async () => { await resetAppConfig() }) beforeEach(async () => { await resetDb() customer = await createCustomer({ callName: 'Alice' }) otherCustomer = await createCustomer({ callName: 'Bob' }) }) afterAll(async () => { // Leave the seeded users alone for the next test file's speed. }) it('createPaymentSession writes a row with status pending and expires_at in the future', async () => { const before = Date.now() const session = await createPaymentSession({ customerId: customer.id, durationMinutes: 15, amount: 30000, }) expect(session.status).toBe(PaymentSessionStatus.PENDING) expect(session.customer_id).toBe(customer.id) expect(session.duration_minutes).toBe(15) expect(session.amount).toBe(30000) expect(session.is_free_trial).toBe(false) expect(session.is_extension).toBe(false) expect(new Date(session.expires_at).getTime()).toBeGreaterThan(before) // Verify it's actually persisted (not just returned from the INSERT) const reloaded = await getPaymentSession(session.id) expect(reloaded.id).toBe(session.id) expect(reloaded.status).toBe(PaymentSessionStatus.PENDING) }) it('confirmPaymentSession transitions pending → confirmed', async () => { const session = await createPaymentSession({ customerId: customer.id, durationMinutes: 30, amount: 60000, }) expect(session.status).toBe(PaymentSessionStatus.PENDING) const confirmed = await confirmPaymentSession(session.id, customer.id) expect(confirmed.status).toBe(PaymentSessionStatus.CONFIRMED) expect(confirmed.confirmed_at).toBeTruthy() expect(new Date(confirmed.confirmed_at).getTime()).toBeGreaterThan(0) }) it('confirmPaymentSession throws when the session belongs to a different customer', async () => { const session = await createPaymentSession({ customerId: customer.id, durationMinutes: 15, amount: 30000, }) await expect( confirmPaymentSession(session.id, otherCustomer.id), ).rejects.toMatchObject({ code: 'FORBIDDEN', statusCode: 403, }) // Row should still be pending — the failed confirm must not have side effects. const reloaded = await getPaymentSession(session.id) expect(reloaded.status).toBe(PaymentSessionStatus.PENDING) expect(reloaded.confirmed_at).toBeNull() }) })