Files
gridpilot.gg/core/payments/application/use-cases/UpdatePaymentStatusUseCase.test.ts
2025-12-23 18:30:18 +01:00

154 lines
4.7 KiB
TypeScript

import { beforeEach, describe, expect, it, vi, type Mock } from 'vitest';
import { UpdatePaymentStatusUseCase, type UpdatePaymentStatusInput } from './UpdatePaymentStatusUseCase';
import type { IPaymentRepository } from '../../domain/repositories/IPaymentRepository';
import { PaymentStatus, PaymentType, PayerType, type Payment } from '../../domain/entities/Payment';
import type { UseCaseOutputPort } from '@core/shared/application/UseCaseOutputPort';
describe('UpdatePaymentStatusUseCase', () => {
let paymentRepository: {
findById: Mock;
update: Mock;
};
let output: {
present: Mock;
};
let useCase: UpdatePaymentStatusUseCase;
beforeEach(() => {
paymentRepository = {
findById: vi.fn(),
update: vi.fn(),
};
output = {
present: vi.fn(),
};
useCase = new UpdatePaymentStatusUseCase(
paymentRepository as unknown as IPaymentRepository,
output as unknown as UseCaseOutputPort<unknown>,
);
});
it('returns PAYMENT_NOT_FOUND when payment does not exist', async () => {
const input: UpdatePaymentStatusInput = {
paymentId: 'payment-1',
status: PaymentStatus.COMPLETED,
};
paymentRepository.findById.mockResolvedValue(null);
const result = await useCase.execute(input);
expect(result.isErr()).toBe(true);
expect(result.unwrapErr().code).toBe('PAYMENT_NOT_FOUND');
expect(paymentRepository.update).not.toHaveBeenCalled();
expect(output.present).not.toHaveBeenCalled();
});
it('sets completedAt when status becomes COMPLETED', async () => {
vi.useFakeTimers();
vi.setSystemTime(new Date('2025-01-01T00:00:00.000Z'));
try {
const input: UpdatePaymentStatusInput = {
paymentId: 'payment-1',
status: PaymentStatus.COMPLETED,
};
const existingPayment: Payment = {
id: 'payment-1',
type: PaymentType.SPONSORSHIP,
amount: 100,
platformFee: 5,
netAmount: 95,
payerId: 'payer-1',
payerType: PayerType.SPONSOR,
leagueId: 'league-1',
status: PaymentStatus.PENDING,
createdAt: new Date('2024-12-31T00:00:00.000Z'),
};
paymentRepository.findById.mockResolvedValue(existingPayment);
paymentRepository.update.mockImplementation(async (p: Payment) => ({ ...p }));
const result = await useCase.execute(input);
expect(result.isOk()).toBe(true);
expect(paymentRepository.update).toHaveBeenCalledWith(
expect.objectContaining({
id: 'payment-1',
status: PaymentStatus.COMPLETED,
completedAt: new Date('2025-01-01T00:00:00.000Z'),
}),
);
const savedPayment = paymentRepository.update.mock.results[0]?.value;
await expect(savedPayment).resolves.toEqual(
expect.objectContaining({
id: 'payment-1',
status: PaymentStatus.COMPLETED,
completedAt: new Date('2025-01-01T00:00:00.000Z'),
}),
);
const presentedPayment = (output.present.mock.calls[0]?.[0] as { payment: Payment }).payment;
expect(presentedPayment.status).toBe(PaymentStatus.COMPLETED);
expect(presentedPayment.completedAt).toEqual(new Date('2025-01-01T00:00:00.000Z'));
} finally {
vi.useRealTimers();
}
});
it('preserves completedAt when status is not COMPLETED', async () => {
vi.useFakeTimers();
vi.setSystemTime(new Date('2026-01-01T00:00:00.000Z'));
try {
const input: UpdatePaymentStatusInput = {
paymentId: 'payment-1',
status: PaymentStatus.FAILED,
};
const existingCompletedAt = new Date('2025-01-01T00:00:00.000Z');
const existingPayment: Payment = {
id: 'payment-1',
type: PaymentType.SPONSORSHIP,
amount: 100,
platformFee: 5,
netAmount: 95,
payerId: 'payer-1',
payerType: PayerType.SPONSOR,
leagueId: 'league-1',
status: PaymentStatus.COMPLETED,
createdAt: new Date('2024-12-31T00:00:00.000Z'),
completedAt: existingCompletedAt,
};
paymentRepository.findById.mockResolvedValue(existingPayment);
paymentRepository.update.mockImplementation(async (p: Payment) => ({ ...p }));
const result = await useCase.execute(input);
expect(result.isOk()).toBe(true);
expect(paymentRepository.update).toHaveBeenCalledWith(
expect.objectContaining({
id: 'payment-1',
status: PaymentStatus.FAILED,
completedAt: existingCompletedAt,
}),
);
const presentedPayment = (output.present.mock.calls[0]?.[0] as { payment: Payment }).payment;
expect(presentedPayment.status).toBe(PaymentStatus.FAILED);
expect(presentedPayment.completedAt).toEqual(existingCompletedAt);
} finally {
vi.useRealTimers();
}
});
});