import { test, expect, registerUser } from '../fixtures'; test.describe('Payments API', () => { let accessToken: string; test.beforeAll(async ({ request }) => { const { accessToken: token } = await registerUser(request); accessToken = token; }); test.describe('POST /payments — Create payment', () => { test('creates a VNPay payment and returns payment URL', async ({ request }) => { const res = await request.post('/payments', { data: { provider: 'VNPAY', type: 'LISTING_FEE', amountVND: '500000', description: 'E2E test listing fee payment', returnUrl: 'https://example.com/payments/callback', }, headers: { Authorization: `Bearer ${accessToken}` }, }); // Payment creation may fail if VNPay not configured — accept 201 or 502/503 if (res.status() >= 500) { test.skip(true, 'Payment gateway not configured in test env'); return; } expect(res.status()).toBe(201); const body = await res.json(); expect(body).toHaveProperty('paymentId'); expect(body).toHaveProperty('paymentUrl'); }); test('rejects payment with missing required fields', async ({ request }) => { const res = await request.post('/payments', { data: { provider: 'VNPAY' }, headers: { Authorization: `Bearer ${accessToken}` }, }); expect(res.ok()).toBeFalsy(); expect(res.status()).toBe(400); }); test('rejects payment with invalid provider', async ({ request }) => { const res = await request.post('/payments', { data: { provider: 'INVALID_PROVIDER', type: 'LISTING_FEE', amountVND: '500000', description: 'Invalid provider test', returnUrl: 'https://example.com/callback', }, headers: { Authorization: `Bearer ${accessToken}` }, }); expect(res.ok()).toBeFalsy(); expect(res.status()).toBe(400); }); test('rejects payment with invalid type', async ({ request }) => { const res = await request.post('/payments', { data: { provider: 'VNPAY', type: 'INVALID_TYPE', amountVND: '500000', description: 'Invalid type test', returnUrl: 'https://example.com/callback', }, headers: { Authorization: `Bearer ${accessToken}` }, }); expect(res.ok()).toBeFalsy(); expect(res.status()).toBe(400); }); test('rejects unauthenticated payment creation', async ({ request }) => { const res = await request.post('/payments', { data: { provider: 'VNPAY', type: 'LISTING_FEE', amountVND: '500000', description: 'Unauth test', returnUrl: 'https://example.com/callback', }, }); expect(res.status()).toBe(401); }); }); test.describe('GET /payments — List transactions', () => { test('returns paginated transaction list for authenticated user', async ({ request }) => { const res = await request.get('/payments', { headers: { Authorization: `Bearer ${accessToken}` }, }); expect(res.status()).toBe(200); const body = await res.json(); expect(body).toHaveProperty('items'); expect(Array.isArray(body.items)).toBeTruthy(); }); test('supports pagination params', async ({ request }) => { const res = await request.get('/payments', { params: { limit: 5, offset: 0 }, headers: { Authorization: `Bearer ${accessToken}` }, }); expect(res.status()).toBe(200); const body = await res.json(); expect(body.items.length).toBeLessThanOrEqual(5); }); test('rejects unauthenticated transaction list', async ({ request }) => { const res = await request.get('/payments'); expect(res.status()).toBe(401); }); }); test.describe('GET /payments/:id — Get payment status', () => { test('returns 401 for unauthenticated request', async ({ request }) => { const res = await request.get('/payments/some-payment-id'); expect(res.status()).toBe(401); }); test('returns 404 for non-existent payment', async ({ request }) => { const res = await request.get('/payments/non-existent-payment-id', { headers: { Authorization: `Bearer ${accessToken}` }, }); expect(res.ok()).toBeFalsy(); expect([404, 400]).toContain(res.status()); }); }); test.describe('POST /payments/:id/refund — Refund (admin only)', () => { test('rejects refund from non-admin user', async ({ request }) => { const res = await request.post('/payments/some-id/refund', { data: { reason: 'Test refund from non-admin' }, headers: { Authorization: `Bearer ${accessToken}` }, }); expect(res.ok()).toBeFalsy(); expect([401, 403]).toContain(res.status()); }); }); });