view models
This commit is contained in:
@@ -1,506 +0,0 @@
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { PaymentService } from './PaymentService';
|
||||
import type { PaymentsApiClient } from '../../api/payments/PaymentsApiClient';
|
||||
import type { PaymentListPresenter } from '../../presenters/PaymentListPresenter';
|
||||
import type {
|
||||
GetPaymentsOutputDto,
|
||||
CreatePaymentInputDto,
|
||||
CreatePaymentOutputDto,
|
||||
GetMembershipFeesOutputDto,
|
||||
GetPrizesOutputDto,
|
||||
GetWalletOutputDto,
|
||||
PaymentDto,
|
||||
MembershipFeeDto,
|
||||
PrizeDto,
|
||||
WalletDto,
|
||||
} from '../../dtos';
|
||||
import type {
|
||||
PaymentViewModel,
|
||||
MembershipFeeViewModel,
|
||||
PrizeViewModel,
|
||||
WalletViewModel,
|
||||
} from '../../view-models';
|
||||
|
||||
describe('PaymentService', () => {
|
||||
let service: PaymentService;
|
||||
let mockApiClient: PaymentsApiClient;
|
||||
let mockPaymentListPresenter: PaymentListPresenter;
|
||||
let mockPresentPayment: (dto: any) => PaymentViewModel;
|
||||
let mockPresentMembershipFee: (dto: any) => MembershipFeeViewModel;
|
||||
let mockPresentPrize: (dto: any) => PrizeViewModel;
|
||||
let mockPresentWallet: (dto: any) => WalletViewModel;
|
||||
|
||||
beforeEach(() => {
|
||||
mockApiClient = {
|
||||
getPayments: vi.fn(),
|
||||
createPayment: vi.fn(),
|
||||
getMembershipFees: vi.fn(),
|
||||
getPrizes: vi.fn(),
|
||||
getWallet: vi.fn(),
|
||||
} as unknown as PaymentsApiClient;
|
||||
|
||||
mockPaymentListPresenter = {
|
||||
present: vi.fn(),
|
||||
} as unknown as PaymentListPresenter;
|
||||
|
||||
mockPresentPayment = vi.fn();
|
||||
mockPresentMembershipFee = vi.fn();
|
||||
mockPresentPrize = vi.fn();
|
||||
mockPresentWallet = vi.fn();
|
||||
|
||||
service = new PaymentService(
|
||||
mockApiClient,
|
||||
mockPaymentListPresenter,
|
||||
mockPresentPayment,
|
||||
mockPresentMembershipFee,
|
||||
mockPresentPrize,
|
||||
mockPresentWallet
|
||||
);
|
||||
});
|
||||
|
||||
describe('constructor', () => {
|
||||
it('should create instance with injected dependencies', () => {
|
||||
expect(service).toBeInstanceOf(PaymentService);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getPayments', () => {
|
||||
it('should fetch all payments and transform via presenter', async () => {
|
||||
// Arrange
|
||||
const mockDto: GetPaymentsOutputDto = {
|
||||
payments: [
|
||||
{
|
||||
id: 'payment-1',
|
||||
amount: 100,
|
||||
currency: 'USD',
|
||||
status: 'completed',
|
||||
createdAt: '2024-01-01',
|
||||
},
|
||||
{
|
||||
id: 'payment-2',
|
||||
amount: 200,
|
||||
currency: 'EUR',
|
||||
status: 'pending',
|
||||
createdAt: '2024-01-02',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const mockViewModels: PaymentViewModel[] = [
|
||||
{
|
||||
id: 'payment-1',
|
||||
amount: 100,
|
||||
currency: 'USD',
|
||||
status: 'completed',
|
||||
createdAt: '2024-01-01',
|
||||
} as PaymentViewModel,
|
||||
{
|
||||
id: 'payment-2',
|
||||
amount: 200,
|
||||
currency: 'EUR',
|
||||
status: 'pending',
|
||||
createdAt: '2024-01-02',
|
||||
} as PaymentViewModel,
|
||||
];
|
||||
|
||||
vi.mocked(mockApiClient.getPayments).mockResolvedValue(mockDto);
|
||||
vi.mocked(mockPaymentListPresenter.present).mockReturnValue(mockViewModels);
|
||||
|
||||
// Act
|
||||
const result = await service.getPayments();
|
||||
|
||||
// Assert
|
||||
expect(mockApiClient.getPayments).toHaveBeenCalledWith(undefined, undefined);
|
||||
expect(mockPaymentListPresenter.present).toHaveBeenCalledWith(mockDto);
|
||||
expect(result).toEqual(mockViewModels);
|
||||
});
|
||||
|
||||
it('should filter payments by leagueId', async () => {
|
||||
// Arrange
|
||||
const leagueId = 'league-123';
|
||||
const mockDto: GetPaymentsOutputDto = { payments: [] };
|
||||
const mockViewModels: PaymentViewModel[] = [];
|
||||
|
||||
vi.mocked(mockApiClient.getPayments).mockResolvedValue(mockDto);
|
||||
vi.mocked(mockPaymentListPresenter.present).mockReturnValue(mockViewModels);
|
||||
|
||||
// Act
|
||||
await service.getPayments(leagueId);
|
||||
|
||||
// Assert
|
||||
expect(mockApiClient.getPayments).toHaveBeenCalledWith(leagueId, undefined);
|
||||
});
|
||||
|
||||
it('should filter payments by driverId', async () => {
|
||||
// Arrange
|
||||
const driverId = 'driver-456';
|
||||
const mockDto: GetPaymentsOutputDto = { payments: [] };
|
||||
const mockViewModels: PaymentViewModel[] = [];
|
||||
|
||||
vi.mocked(mockApiClient.getPayments).mockResolvedValue(mockDto);
|
||||
vi.mocked(mockPaymentListPresenter.present).mockReturnValue(mockViewModels);
|
||||
|
||||
// Act
|
||||
await service.getPayments(undefined, driverId);
|
||||
|
||||
// Assert
|
||||
expect(mockApiClient.getPayments).toHaveBeenCalledWith(undefined, driverId);
|
||||
});
|
||||
|
||||
it('should propagate errors from API client', async () => {
|
||||
// Arrange
|
||||
const error = new Error('Failed to fetch payments');
|
||||
vi.mocked(mockApiClient.getPayments).mockRejectedValue(error);
|
||||
|
||||
// Act & Assert
|
||||
await expect(service.getPayments()).rejects.toThrow('Failed to fetch payments');
|
||||
expect(mockApiClient.getPayments).toHaveBeenCalled();
|
||||
expect(mockPaymentListPresenter.present).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('getPayment', () => {
|
||||
it('should fetch single payment by ID', async () => {
|
||||
// Arrange
|
||||
const paymentId = 'payment-123';
|
||||
const mockDto: GetPaymentsOutputDto = {
|
||||
payments: [
|
||||
{
|
||||
id: paymentId,
|
||||
amount: 100,
|
||||
currency: 'USD',
|
||||
status: 'completed',
|
||||
createdAt: '2024-01-01',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const mockViewModel: PaymentViewModel = {
|
||||
id: paymentId,
|
||||
amount: 100,
|
||||
currency: 'USD',
|
||||
status: 'completed',
|
||||
createdAt: '2024-01-01',
|
||||
} as PaymentViewModel;
|
||||
|
||||
vi.mocked(mockApiClient.getPayments).mockResolvedValue(mockDto);
|
||||
vi.mocked(mockPresentPayment).mockReturnValue(mockViewModel);
|
||||
|
||||
// Act
|
||||
const result = await service.getPayment(paymentId);
|
||||
|
||||
// Assert
|
||||
expect(mockApiClient.getPayments).toHaveBeenCalled();
|
||||
expect(mockPresentPayment).toHaveBeenCalledWith(mockDto.payments[0]);
|
||||
expect(result).toEqual(mockViewModel);
|
||||
});
|
||||
|
||||
it('should throw error when payment not found', async () => {
|
||||
// Arrange
|
||||
const paymentId = 'non-existent';
|
||||
const mockDto: GetPaymentsOutputDto = { payments: [] };
|
||||
|
||||
vi.mocked(mockApiClient.getPayments).mockResolvedValue(mockDto);
|
||||
|
||||
// Act & Assert
|
||||
await expect(service.getPayment(paymentId)).rejects.toThrow(
|
||||
`Payment with ID ${paymentId} not found`
|
||||
);
|
||||
});
|
||||
|
||||
it('should propagate errors from API client', async () => {
|
||||
// Arrange
|
||||
const error = new Error('API error');
|
||||
vi.mocked(mockApiClient.getPayments).mockRejectedValue(error);
|
||||
|
||||
// Act & Assert
|
||||
await expect(service.getPayment('payment-123')).rejects.toThrow('API error');
|
||||
});
|
||||
});
|
||||
|
||||
describe('createPayment', () => {
|
||||
it('should create a new payment', async () => {
|
||||
// Arrange
|
||||
const input: CreatePaymentInputDto = {
|
||||
amount: 150,
|
||||
currency: 'USD',
|
||||
leagueId: 'league-123',
|
||||
driverId: 'driver-456',
|
||||
};
|
||||
|
||||
const mockOutput: CreatePaymentOutputDto = {
|
||||
paymentId: 'payment-new',
|
||||
success: true,
|
||||
};
|
||||
|
||||
vi.mocked(mockApiClient.createPayment).mockResolvedValue(mockOutput);
|
||||
|
||||
// Act
|
||||
const result = await service.createPayment(input);
|
||||
|
||||
// Assert
|
||||
expect(mockApiClient.createPayment).toHaveBeenCalledWith(input);
|
||||
expect(result).toEqual(mockOutput);
|
||||
});
|
||||
|
||||
it('should propagate errors from API client', async () => {
|
||||
// Arrange
|
||||
const input: CreatePaymentInputDto = {
|
||||
amount: 150,
|
||||
currency: 'USD',
|
||||
};
|
||||
const error = new Error('Payment creation failed');
|
||||
vi.mocked(mockApiClient.createPayment).mockRejectedValue(error);
|
||||
|
||||
// Act & Assert
|
||||
await expect(service.createPayment(input)).rejects.toThrow('Payment creation failed');
|
||||
});
|
||||
});
|
||||
|
||||
describe('getMembershipFees', () => {
|
||||
it('should fetch membership fees for a league', async () => {
|
||||
// Arrange
|
||||
const leagueId = 'league-123';
|
||||
const mockDto: GetMembershipFeesOutputDto = {
|
||||
fees: [
|
||||
{
|
||||
leagueId,
|
||||
amount: 50,
|
||||
currency: 'USD',
|
||||
period: 'monthly',
|
||||
},
|
||||
],
|
||||
memberPayments: [],
|
||||
};
|
||||
|
||||
const mockViewModel: MembershipFeeViewModel = {
|
||||
leagueId,
|
||||
amount: 50,
|
||||
currency: 'USD',
|
||||
period: 'monthly',
|
||||
} as MembershipFeeViewModel;
|
||||
|
||||
vi.mocked(mockApiClient.getMembershipFees).mockResolvedValue(mockDto);
|
||||
vi.mocked(mockPresentMembershipFee).mockReturnValue(mockViewModel);
|
||||
|
||||
// Act
|
||||
const result = await service.getMembershipFees(leagueId);
|
||||
|
||||
// Assert
|
||||
expect(mockApiClient.getMembershipFees).toHaveBeenCalledWith(leagueId);
|
||||
expect(mockPresentMembershipFee).toHaveBeenCalledWith(mockDto.fees[0]);
|
||||
expect(result).toEqual([mockViewModel]);
|
||||
});
|
||||
|
||||
it('should handle empty fees list', async () => {
|
||||
// Arrange
|
||||
const leagueId = 'league-123';
|
||||
const mockDto: GetMembershipFeesOutputDto = {
|
||||
fees: [],
|
||||
memberPayments: [],
|
||||
};
|
||||
|
||||
vi.mocked(mockApiClient.getMembershipFees).mockResolvedValue(mockDto);
|
||||
|
||||
// Act
|
||||
const result = await service.getMembershipFees(leagueId);
|
||||
|
||||
// Assert
|
||||
expect(result).toEqual([]);
|
||||
expect(mockPresentMembershipFee).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should propagate errors from API client', async () => {
|
||||
// Arrange
|
||||
const error = new Error('Failed to fetch fees');
|
||||
vi.mocked(mockApiClient.getMembershipFees).mockRejectedValue(error);
|
||||
|
||||
// Act & Assert
|
||||
await expect(service.getMembershipFees('league-123')).rejects.toThrow('Failed to fetch fees');
|
||||
});
|
||||
});
|
||||
|
||||
describe('getPrizes', () => {
|
||||
it('should fetch all prizes', async () => {
|
||||
// Arrange
|
||||
const mockDto: GetPrizesOutputDto = {
|
||||
prizes: [
|
||||
{
|
||||
id: 'prize-1',
|
||||
name: 'First Place',
|
||||
amount: 1000,
|
||||
currency: 'USD',
|
||||
position: 1,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const mockViewModel: PrizeViewModel = {
|
||||
id: 'prize-1',
|
||||
name: 'First Place',
|
||||
amount: 1000,
|
||||
currency: 'USD',
|
||||
position: 1,
|
||||
} as PrizeViewModel;
|
||||
|
||||
vi.mocked(mockApiClient.getPrizes).mockResolvedValue(mockDto);
|
||||
vi.mocked(mockPresentPrize).mockReturnValue(mockViewModel);
|
||||
|
||||
// Act
|
||||
const result = await service.getPrizes();
|
||||
|
||||
// Assert
|
||||
expect(mockApiClient.getPrizes).toHaveBeenCalledWith(undefined, undefined);
|
||||
expect(mockPresentPrize).toHaveBeenCalledWith(mockDto.prizes[0]);
|
||||
expect(result).toEqual([mockViewModel]);
|
||||
});
|
||||
|
||||
it('should filter prizes by leagueId and seasonId', async () => {
|
||||
// Arrange
|
||||
const leagueId = 'league-123';
|
||||
const seasonId = 'season-456';
|
||||
const mockDto: GetPrizesOutputDto = { prizes: [] };
|
||||
|
||||
vi.mocked(mockApiClient.getPrizes).mockResolvedValue(mockDto);
|
||||
|
||||
// Act
|
||||
await service.getPrizes(leagueId, seasonId);
|
||||
|
||||
// Assert
|
||||
expect(mockApiClient.getPrizes).toHaveBeenCalledWith(leagueId, seasonId);
|
||||
});
|
||||
|
||||
it('should propagate errors from API client', async () => {
|
||||
// Arrange
|
||||
const error = new Error('Failed to fetch prizes');
|
||||
vi.mocked(mockApiClient.getPrizes).mockRejectedValue(error);
|
||||
|
||||
// Act & Assert
|
||||
await expect(service.getPrizes()).rejects.toThrow('Failed to fetch prizes');
|
||||
});
|
||||
});
|
||||
|
||||
describe('getWallet', () => {
|
||||
it('should fetch wallet for a driver', async () => {
|
||||
// Arrange
|
||||
const driverId = 'driver-123';
|
||||
const mockDto: GetWalletOutputDto = {
|
||||
driverId,
|
||||
balance: 500,
|
||||
currency: 'USD',
|
||||
transactions: [],
|
||||
};
|
||||
|
||||
const mockViewModel: WalletViewModel = {
|
||||
driverId,
|
||||
balance: 500,
|
||||
currency: 'USD',
|
||||
transactions: [],
|
||||
} as WalletViewModel;
|
||||
|
||||
vi.mocked(mockApiClient.getWallet).mockResolvedValue(mockDto);
|
||||
vi.mocked(mockPresentWallet).mockReturnValue(mockViewModel);
|
||||
|
||||
// Act
|
||||
const result = await service.getWallet(driverId);
|
||||
|
||||
// Assert
|
||||
expect(mockApiClient.getWallet).toHaveBeenCalledWith(driverId);
|
||||
expect(mockPresentWallet).toHaveBeenCalledWith(mockDto);
|
||||
expect(result).toEqual(mockViewModel);
|
||||
});
|
||||
|
||||
it('should propagate errors from API client', async () => {
|
||||
// Arrange
|
||||
const error = new Error('Failed to fetch wallet');
|
||||
vi.mocked(mockApiClient.getWallet).mockRejectedValue(error);
|
||||
|
||||
// Act & Assert
|
||||
await expect(service.getWallet('driver-123')).rejects.toThrow('Failed to fetch wallet');
|
||||
});
|
||||
});
|
||||
|
||||
describe('processPayment', () => {
|
||||
it('should process payment using createPayment', async () => {
|
||||
// Arrange
|
||||
const input: CreatePaymentInputDto = {
|
||||
amount: 100,
|
||||
currency: 'USD',
|
||||
};
|
||||
|
||||
const mockOutput: CreatePaymentOutputDto = {
|
||||
paymentId: 'payment-123',
|
||||
success: true,
|
||||
};
|
||||
|
||||
vi.mocked(mockApiClient.createPayment).mockResolvedValue(mockOutput);
|
||||
|
||||
// Act
|
||||
const result = await service.processPayment(input);
|
||||
|
||||
// Assert
|
||||
expect(mockApiClient.createPayment).toHaveBeenCalledWith(input);
|
||||
expect(result).toEqual(mockOutput);
|
||||
});
|
||||
|
||||
it('should propagate errors', async () => {
|
||||
// Arrange
|
||||
const input: CreatePaymentInputDto = {
|
||||
amount: 100,
|
||||
currency: 'USD',
|
||||
};
|
||||
const error = new Error('Processing failed');
|
||||
vi.mocked(mockApiClient.createPayment).mockRejectedValue(error);
|
||||
|
||||
// Act & Assert
|
||||
await expect(service.processPayment(input)).rejects.toThrow('Processing failed');
|
||||
});
|
||||
});
|
||||
|
||||
describe('getPaymentHistory', () => {
|
||||
it('should fetch payment history for a driver', async () => {
|
||||
// Arrange
|
||||
const driverId = 'driver-123';
|
||||
const mockDto: GetPaymentsOutputDto = {
|
||||
payments: [
|
||||
{
|
||||
id: 'payment-1',
|
||||
amount: 100,
|
||||
currency: 'USD',
|
||||
status: 'completed',
|
||||
createdAt: '2024-01-01',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const mockViewModels: PaymentViewModel[] = [
|
||||
{
|
||||
id: 'payment-1',
|
||||
amount: 100,
|
||||
currency: 'USD',
|
||||
status: 'completed',
|
||||
createdAt: '2024-01-01',
|
||||
} as PaymentViewModel,
|
||||
];
|
||||
|
||||
vi.mocked(mockApiClient.getPayments).mockResolvedValue(mockDto);
|
||||
vi.mocked(mockPaymentListPresenter.present).mockReturnValue(mockViewModels);
|
||||
|
||||
// Act
|
||||
const result = await service.getPaymentHistory(driverId);
|
||||
|
||||
// Assert
|
||||
expect(mockApiClient.getPayments).toHaveBeenCalledWith(undefined, driverId);
|
||||
expect(result).toEqual(mockViewModels);
|
||||
});
|
||||
|
||||
it('should propagate errors', async () => {
|
||||
// Arrange
|
||||
const error = new Error('History fetch failed');
|
||||
vi.mocked(mockApiClient.getPayments).mockRejectedValue(error);
|
||||
|
||||
// Act & Assert
|
||||
await expect(service.getPaymentHistory('driver-123')).rejects.toThrow('History fetch failed');
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user