import { describe, it, expect, vi, beforeEach } from 'vitest' import Member from '../../../server/models/member.js' import { createHelcimCustomer } from '../../../server/utils/helcim.js' import customerHandler from '../../../server/api/helcim/customer.post.js' import subscriptionHandler from '../../../server/api/helcim/subscription.post.js' import { resetRateLimit } from '../../../server/utils/rateLimit.js' import { sendWelcomeEmail } from '../../../server/utils/resend.js' import { createMockEvent } from '../helpers/createMockEvent.js' // Deliberately does NOT mock server/utils/auth.js — the bridge-cookie // hand-off between the two handlers is the contract under test. vi.mock('../../../server/models/member.js', () => ({ default: { findOne: vi.fn(), create: vi.fn(), findByIdAndUpdate: vi.fn(), findById: vi.fn(), findOneAndUpdate: vi.fn() } })) vi.mock('../../../server/utils/mongoose.js', () => ({ connectDB: vi.fn() })) vi.mock('../../../server/utils/helcim.js', () => ({ createHelcimCustomer: vi.fn(), createHelcimSubscription: vi.fn(), generateIdempotencyKey: vi.fn().mockReturnValue('idem-1'), listHelcimCustomerTransactions: vi.fn().mockResolvedValue([]) })) vi.mock('../../../server/utils/magicLink.js', () => ({ sendMagicLink: vi.fn().mockResolvedValue(undefined) })) vi.mock('../../../server/utils/resend.js', () => ({ sendWelcomeEmail: vi.fn().mockResolvedValue({ success: true }) })) vi.mock('../../../server/utils/slack.ts', () => ({ getSlackService: vi.fn().mockReturnValue(null) })) vi.mock('../../../server/utils/payments.js', () => ({ upsertPaymentFromHelcim: vi.fn().mockResolvedValue({ created: true }) })) vi.stubGlobal('helcimCustomerSchema', {}) vi.stubGlobal('helcimSubscriptionSchema', {}) const ALLOWED_ORIGIN = 'https://ghostguild.test' const MEMBER_ID = '69f231152939bf109ac79d83' const SUBSCRIPTION_BODY = { customerId: 999, customerCode: 'CST999', contributionAmount: 0, cadence: 'monthly', cardToken: null } function extractBridgeCookie(event) { const setCookie = event.node.res.getHeader('set-cookie') const cookies = Array.isArray(setCookie) ? setCookie : [setCookie].filter(Boolean) const match = cookies.find(c => typeof c === 'string' && c.startsWith('payment-bridge=')) if (!match) return null return match.match(/payment-bridge=([^;]+)/)[1] } describe('signup → subscription bridge-cookie hand-off', () => { beforeEach(() => { vi.clearAllMocks() resetRateLimit() process.env.BASE_URL = ALLOWED_ORIGIN createHelcimCustomer.mockResolvedValue({ id: 999, customerCode: 'CST999' }) }) it('$0 signup: customer endpoint issues a bridge cookie that subscription endpoint accepts', async () => { Member.findOne.mockResolvedValue(null) Member.create.mockResolvedValue({ _id: MEMBER_ID, email: 'free@example.com', name: 'Free User', circle: 'community', contributionAmount: 0, status: 'pending_payment' }) const customerEvent = createMockEvent({ method: 'POST', path: '/api/helcim/customer', headers: { origin: ALLOWED_ORIGIN }, body: { name: 'Free User', email: 'free@example.com', circle: 'community', contributionAmount: 0, agreedToGuidelines: true, billingAddress: { country: 'CA' } } }) const result1 = await customerHandler(customerEvent) expect(result1.success).toBe(true) expect(result1.member.status).toBe('pending_payment') const bridgeToken = extractBridgeCookie(customerEvent) expect(bridgeToken, 'payment-bridge cookie missing on $0 signup').toBeTruthy() Member.findOneAndUpdate.mockResolvedValue({ _id: MEMBER_ID, status: 'pending_payment' }) Member.findById.mockResolvedValue({ _id: MEMBER_ID, email: 'free@example.com', name: 'Free User', circle: 'community', contributionAmount: 0, status: 'active' }) const subscriptionEvent = createMockEvent({ method: 'POST', path: '/api/helcim/subscription', headers: { origin: ALLOWED_ORIGIN }, cookies: { 'payment-bridge': bridgeToken }, body: SUBSCRIPTION_BODY }) const result2 = await subscriptionHandler(subscriptionEvent) expect(result2.success).toBe(true) expect(result2.member.status).toBe('active') expect(sendWelcomeEmail).toHaveBeenCalledTimes(1) }) it('$0 signup with no bridge cookie carried forward → subscription returns 401', async () => { const subscriptionEvent = createMockEvent({ method: 'POST', path: '/api/helcim/subscription', headers: { origin: ALLOWED_ORIGIN }, body: SUBSCRIPTION_BODY }) await expect(subscriptionHandler(subscriptionEvent)).rejects.toMatchObject({ statusCode: 401 }) }) })