import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest' vi.mock('../../../server/utils/auth.js', () => ({ requireAuth: vi.fn() })) vi.mock('../../../server/utils/validateBody.js', () => ({ validateBody: vi.fn() })) vi.mock('../../../server/utils/schemas.js', () => ({ paymentVerifySchema: {} })) import { requireAuth } from '../../../server/utils/auth.js' import { validateBody as importedValidateBody } from '../../../server/utils/validateBody.js' import initPaymentHandler from '../../../server/api/helcim/initialize-payment.post.js' import verifyPaymentHandler from '../../../server/api/helcim/verify-payment.post.js' import { createMockEvent } from '../helpers/createMockEvent.js' // helcimInitializePaymentSchema is a Nitro auto-import used by validateBody vi.stubGlobal('helcimInitializePaymentSchema', {}) const mockFetch = vi.fn() vi.stubGlobal('fetch', mockFetch) describe('initialize-payment endpoint', () => { beforeEach(() => { vi.clearAllMocks() }) afterEach(() => { mockFetch.mockReset() }) it('skips auth for event_ticket type', async () => { const body = { amount: 25, metadata: { type: 'event_ticket', eventTitle: 'Test Event', eventId: 'evt-1' } } globalThis.validateBody.mockResolvedValue(body) mockFetch.mockResolvedValue({ ok: true, json: async () => ({ checkoutToken: 'ct-123', secretToken: 'st-456' }) }) const event = createMockEvent({ method: 'POST', path: '/api/helcim/initialize-payment', body }) await initPaymentHandler(event) expect(requireAuth).not.toHaveBeenCalled() }) it('requires auth for non-event_ticket types', async () => { const body = { amount: 0, customerCode: 'code-1' } globalThis.validateBody.mockResolvedValue(body) requireAuth.mockResolvedValue(undefined) mockFetch.mockResolvedValue({ ok: true, json: async () => ({ checkoutToken: 'ct-123', secretToken: 'st-456' }) }) const event = createMockEvent({ method: 'POST', path: '/api/helcim/initialize-payment', body }) await initPaymentHandler(event) expect(requireAuth).toHaveBeenCalledWith(event) }) it('returns checkoutToken and secretToken on success', async () => { const body = { amount: 10, metadata: { type: 'event_ticket', eventTitle: 'Workshop', eventId: 'evt-2' } } globalThis.validateBody.mockResolvedValue(body) mockFetch.mockResolvedValue({ ok: true, json: async () => ({ checkoutToken: 'ct-abc', secretToken: 'st-xyz' }) }) const event = createMockEvent({ method: 'POST', path: '/api/helcim/initialize-payment', body }) const result = await initPaymentHandler(event) expect(result).toEqual({ success: true, checkoutToken: 'ct-abc', secretToken: 'st-xyz' }) }) }) describe('verify-payment endpoint', () => { beforeEach(() => { vi.clearAllMocks() }) afterEach(() => { mockFetch.mockReset() }) it('requires auth', async () => { requireAuth.mockRejectedValue( createError({ statusCode: 401, statusMessage: 'Unauthorized' }) ) const event = createMockEvent({ method: 'POST', path: '/api/helcim/verify-payment', body: { customerId: 'cust-1', cardToken: 'tok-1' } }) await expect(verifyPaymentHandler(event)).rejects.toMatchObject({ statusCode: 401, statusMessage: 'Unauthorized' }) expect(requireAuth).toHaveBeenCalledWith(event) }) it('validates with paymentVerifySchema', async () => { const body = { customerId: 'cust-1', cardToken: 'tok-1' } requireAuth.mockResolvedValue(undefined) importedValidateBody.mockResolvedValue(body) mockFetch.mockResolvedValue({ ok: true, json: async () => [{ cardToken: 'tok-1' }] }) const event = createMockEvent({ method: 'POST', path: '/api/helcim/verify-payment', body }) await verifyPaymentHandler(event) expect(importedValidateBody).toHaveBeenCalledWith(event, expect.any(Object)) }) it('returns success when card token found', async () => { const body = { customerId: 'cust-1', cardToken: 'tok-match' } requireAuth.mockResolvedValue(undefined) importedValidateBody.mockResolvedValue(body) mockFetch.mockResolvedValue({ ok: true, json: async () => [ { cardToken: 'tok-other' }, { cardToken: 'tok-match' } ] }) const event = createMockEvent({ method: 'POST', path: '/api/helcim/verify-payment', body }) const result = await verifyPaymentHandler(event) expect(result).toEqual({ success: true, cardToken: 'tok-match', message: 'Payment verified with Helcim' }) }) it('returns 400 when card token not found', async () => { const body = { customerId: 'cust-1', cardToken: 'tok-missing' } requireAuth.mockResolvedValue(undefined) importedValidateBody.mockResolvedValue(body) mockFetch.mockResolvedValue({ ok: true, json: async () => [ { cardToken: 'tok-aaa' }, { cardToken: 'tok-bbb' } ] }) const event = createMockEvent({ method: 'POST', path: '/api/helcim/verify-payment', body }) await expect(verifyPaymentHandler(event)).rejects.toMatchObject({ statusCode: 400, statusMessage: 'Payment method not found or does not belong to this customer' }) }) })