154 lines
4.7 KiB
TypeScript
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();
|
|
}
|
|
});
|
|
}); |