Files
payload-billing/src/__tests__/test-provider.test.ts

283 lines
8.0 KiB
TypeScript

import type { TestProviderConfig} from '../types';
import { TestPaymentProvider } from '../providers/test/provider'
import { PaymentStatus } from '../types'
describe('TestPaymentProvider', () => {
let provider: TestPaymentProvider
let config: TestProviderConfig
beforeEach(() => {
config = {
autoComplete: true,
defaultDelay: 0,
enabled: true,
}
provider = new TestPaymentProvider(config)
})
afterEach(() => {
provider.clearStoredData()
})
describe('createPayment', () => {
it('should create a payment with succeeded status when autoComplete is true', async () => {
const payment = await provider.createPayment({
amount: 2000,
currency: 'USD',
description: 'Test payment',
})
expect(payment).toMatchObject({
amount: 2000,
currency: 'USD',
description: 'Test payment',
provider: 'test',
status: 'succeeded',
})
expect(payment.id).toBeDefined()
expect(payment.createdAt).toBeDefined()
expect(payment.updatedAt).toBeDefined()
expect(payment.providerData?.testMode).toBe(true)
})
it('should create a payment with pending status when autoComplete is false', async () => {
config.autoComplete = false
provider = new TestPaymentProvider(config)
const payment = await provider.createPayment({
amount: 1500,
currency: 'EUR',
})
expect(payment).toMatchObject({
amount: 1500,
currency: 'EUR',
status: 'pending',
})
})
it('should create a failed payment when simulateFailure is true', async () => {
const payment = await provider.createPayment({
amount: 1000,
currency: 'USD',
metadata: {
test: { simulateFailure: true },
},
})
expect(payment.status).toBe('failed')
expect(payment.providerData?.simulatedFailure).toBe(true)
})
it('should apply delay when specified', async () => {
const startTime = Date.now()
await provider.createPayment({
amount: 1000,
currency: 'USD',
metadata: {
test: { delayMs: 100 },
},
})
const endTime = Date.now()
expect(endTime - startTime).toBeGreaterThanOrEqual(100)
})
it('should store payment data', async () => {
const payment = await provider.createPayment({
amount: 2000,
currency: 'USD',
})
const stored = provider.getStoredPayment(payment.id)
expect(stored).toEqual(payment)
})
})
describe('retrievePayment', () => {
it('should retrieve an existing payment', async () => {
const payment = await provider.createPayment({
amount: 2000,
currency: 'USD',
})
const retrieved = await provider.retrievePayment(payment.id)
expect(retrieved).toEqual(payment)
})
it('should throw error for non-existent payment', async () => {
await expect(provider.retrievePayment('non-existent')).rejects.toThrow(
'Payment non-existent not found'
)
})
})
describe('cancelPayment', () => {
it('should cancel a pending payment', async () => {
config.autoComplete = false
provider = new TestPaymentProvider(config)
const payment = await provider.createPayment({
amount: 2000,
currency: 'USD',
})
const canceled = await provider.cancelPayment(payment.id)
expect(canceled.status).toBe('canceled')
expect(canceled.updatedAt).not.toBe(payment.updatedAt)
})
it('should not cancel a succeeded payment', async () => {
const payment = await provider.createPayment({
amount: 2000,
currency: 'USD',
})
await expect(provider.cancelPayment(payment.id)).rejects.toThrow(
'Cannot cancel a succeeded payment'
)
})
it('should throw error for non-existent payment', async () => {
await expect(provider.cancelPayment('non-existent')).rejects.toThrow(
'Payment non-existent not found'
)
})
})
describe('refundPayment', () => {
it('should create a full refund for succeeded payment', async () => {
const payment = await provider.createPayment({
amount: 2000,
currency: 'USD',
})
const refund = await provider.refundPayment(payment.id)
expect(refund).toMatchObject({
amount: 2000,
currency: 'USD',
paymentId: payment.id,
status: 'succeeded',
})
expect(refund.id).toBeDefined()
expect(refund.createdAt).toBeDefined()
// Check payment status is updated
const updatedPayment = await provider.retrievePayment(payment.id)
expect(updatedPayment.status).toBe('refunded')
})
it('should create a partial refund', async () => {
const payment = await provider.createPayment({
amount: 2000,
currency: 'USD',
})
const refund = await provider.refundPayment(payment.id, 1000)
expect(refund.amount).toBe(1000)
// Check payment status is updated to partially_refunded
const updatedPayment = await provider.retrievePayment(payment.id)
expect(updatedPayment.status).toBe('partially_refunded')
})
it('should not refund a non-succeeded payment', async () => {
config.autoComplete = false
provider = new TestPaymentProvider(config)
const payment = await provider.createPayment({
amount: 2000,
currency: 'USD',
})
await expect(provider.refundPayment(payment.id)).rejects.toThrow(
'Can only refund succeeded payments'
)
})
it('should not refund more than payment amount', async () => {
const payment = await provider.createPayment({
amount: 2000,
currency: 'USD',
})
await expect(provider.refundPayment(payment.id, 3000)).rejects.toThrow(
'Refund amount cannot exceed payment amount'
)
})
})
describe('handleWebhook', () => {
it('should handle webhook event', async () => {
const mockRequest = {
text: () => Promise.resolve(JSON.stringify({
type: 'payment.succeeded',
data: { paymentId: 'test_pay_123' }
}))
} as Request
const event = await provider.handleWebhook(mockRequest)
expect(event).toMatchObject({
type: 'payment.succeeded',
data: { paymentId: 'test_pay_123' },
provider: 'test',
verified: true,
})
expect(event.id).toBeDefined()
})
it('should throw error for invalid JSON', async () => {
const mockRequest = {
text: () => Promise.resolve('invalid json')
} as Request
await expect(provider.handleWebhook(mockRequest)).rejects.toThrow(
'Invalid JSON in webhook body'
)
})
it('should throw error when provider is disabled', async () => {
config.enabled = false
provider = new TestPaymentProvider(config)
const mockRequest = {
text: () => Promise.resolve('{}')
} as Request
await expect(provider.handleWebhook(mockRequest)).rejects.toThrow(
'Test provider is not enabled'
)
})
})
describe('data management', () => {
it('should clear all stored data', async () => {
await provider.createPayment({ amount: 1000, currency: 'USD' })
expect(provider.getAllPayments()).toHaveLength(1)
provider.clearStoredData()
expect(provider.getAllPayments()).toHaveLength(0)
expect(provider.getAllRefunds()).toHaveLength(0)
})
it('should return all payments and refunds', async () => {
const payment1 = await provider.createPayment({ amount: 1000, currency: 'USD' })
const payment2 = await provider.createPayment({ amount: 2000, currency: 'EUR' })
const refund = await provider.refundPayment(payment1.id)
const payments = provider.getAllPayments()
const refunds = provider.getAllRefunds()
expect(payments).toHaveLength(2)
expect(refunds).toHaveLength(1)
expect(refunds[0]).toEqual(refund)
})
})
})