164 lines
5.5 KiB
TypeScript
164 lines
5.5 KiB
TypeScript
import { describe, it, expect, beforeEach, vi, Mock } from 'vitest';
|
|
import { GetLeagueWalletUseCase } from './GetLeagueWalletUseCase';
|
|
import type { ILeagueWalletRepository } from '../../domain/repositories/ILeagueWalletRepository';
|
|
import type { ITransactionRepository } from '../../domain/repositories/ITransactionRepository';
|
|
import { LeagueWallet } from '../../domain/entities/league-wallet/LeagueWallet';
|
|
import { Money } from '../../domain/value-objects/Money';
|
|
import { Transaction } from '../../domain/entities/league-wallet/Transaction';
|
|
import { TransactionId } from '../../domain/entities/league-wallet/TransactionId';
|
|
import { LeagueWalletId } from '../../domain/entities/league-wallet/LeagueWalletId';
|
|
|
|
describe('GetLeagueWalletUseCase', () => {
|
|
let leagueWalletRepository: {
|
|
findByLeagueId: Mock;
|
|
};
|
|
let transactionRepository: {
|
|
findByWalletId: Mock;
|
|
};
|
|
let useCase: GetLeagueWalletUseCase;
|
|
|
|
beforeEach(() => {
|
|
leagueWalletRepository = {
|
|
findByLeagueId: vi.fn(),
|
|
};
|
|
|
|
transactionRepository = {
|
|
findByWalletId: vi.fn(),
|
|
};
|
|
|
|
useCase = new GetLeagueWalletUseCase(
|
|
leagueWalletRepository as unknown as ILeagueWalletRepository,
|
|
transactionRepository as unknown as ITransactionRepository,
|
|
);
|
|
});
|
|
|
|
it('returns mapped wallet data when wallet exists', async () => {
|
|
const leagueId = 'league-1';
|
|
|
|
const balance = Money.create(2450, 'USD');
|
|
const wallet = LeagueWallet.create({
|
|
id: 'wallet-1',
|
|
leagueId,
|
|
balance,
|
|
});
|
|
|
|
leagueWalletRepository.findByLeagueId.mockResolvedValue(wallet);
|
|
|
|
const sponsorshipTx = Transaction.create({
|
|
id: TransactionId.create('txn-1'),
|
|
walletId: LeagueWalletId.create(wallet.id.toString()),
|
|
type: 'sponsorship_payment',
|
|
amount: Money.create(1200, 'USD'),
|
|
description: 'Main Sponsor - TechCorp',
|
|
metadata: {},
|
|
}).complete();
|
|
|
|
const membershipTx = Transaction.create({
|
|
id: TransactionId.create('txn-2'),
|
|
walletId: LeagueWalletId.create(wallet.id.toString()),
|
|
type: 'membership_payment',
|
|
amount: Money.create(1600, 'USD'),
|
|
description: 'Season Fee - 32 drivers',
|
|
metadata: {},
|
|
}).complete();
|
|
|
|
const withdrawalTx = Transaction.create({
|
|
id: TransactionId.create('txn-3'),
|
|
walletId: LeagueWalletId.create(wallet.id.toString()),
|
|
type: 'withdrawal',
|
|
amount: Money.create(430, 'USD'),
|
|
description: 'Bank Transfer - Season 1 Payout',
|
|
metadata: {},
|
|
}).complete();
|
|
|
|
const pendingPrizeTx = Transaction.create({
|
|
id: TransactionId.create('txn-4'),
|
|
walletId: LeagueWalletId.create(wallet.id.toString()),
|
|
type: 'prize_payout',
|
|
amount: Money.create(150, 'USD'),
|
|
description: 'Championship Prize Pool (reserved)',
|
|
metadata: {},
|
|
});
|
|
|
|
const refundTx = Transaction.create({
|
|
id: TransactionId.create('txn-5'),
|
|
walletId: LeagueWalletId.create(wallet.id.toString()),
|
|
type: 'refund',
|
|
amount: Money.create(100, 'USD'),
|
|
description: 'Refund for cancelled sponsorship',
|
|
metadata: {},
|
|
});
|
|
|
|
const transactions = [
|
|
sponsorshipTx,
|
|
membershipTx,
|
|
withdrawalTx,
|
|
pendingPrizeTx,
|
|
refundTx,
|
|
];
|
|
|
|
transactionRepository.findByWalletId.mockResolvedValue(transactions);
|
|
|
|
const result = await useCase.execute({ leagueId });
|
|
|
|
expect(result.isOk()).toBe(true);
|
|
const viewModel = result.unwrap();
|
|
|
|
expect(viewModel.balance).toBe(balance.amount);
|
|
expect(viewModel.currency).toBe(balance.currency);
|
|
|
|
const expectedTotalRevenue =
|
|
sponsorshipTx.amount.amount +
|
|
membershipTx.amount.amount +
|
|
pendingPrizeTx.amount.amount;
|
|
|
|
const expectedTotalFees =
|
|
sponsorshipTx.platformFee.amount +
|
|
membershipTx.platformFee.amount +
|
|
pendingPrizeTx.platformFee.amount;
|
|
|
|
const expectedTotalWithdrawals = withdrawalTx.netAmount.amount;
|
|
const expectedPendingPayouts = pendingPrizeTx.netAmount.amount;
|
|
|
|
expect(viewModel.totalRevenue).toBe(expectedTotalRevenue);
|
|
expect(viewModel.totalFees).toBe(expectedTotalFees);
|
|
expect(viewModel.totalWithdrawals).toBe(expectedTotalWithdrawals);
|
|
expect(viewModel.pendingPayouts).toBe(expectedPendingPayouts);
|
|
|
|
expect(viewModel.transactions).toHaveLength(transactions.length);
|
|
expect(viewModel.transactions[0]!.id).toBe(transactions.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime())[0]!.id.toString());
|
|
expect(viewModel.transactions.find(t => t.type === 'sponsorship')).toBeTruthy();
|
|
expect(viewModel.transactions.find(t => t.type === 'membership')).toBeTruthy();
|
|
expect(viewModel.transactions.find(t => t.type === 'withdrawal')).toBeTruthy();
|
|
expect(viewModel.transactions.find(t => t.type === 'prize')).toBeTruthy();
|
|
});
|
|
|
|
it('returns error result when wallet is missing', async () => {
|
|
const leagueId = 'league-missing';
|
|
|
|
leagueWalletRepository.findByLeagueId.mockResolvedValue(null);
|
|
|
|
const result = await useCase.execute({ leagueId });
|
|
|
|
expect(result.isErr()).toBe(true);
|
|
expect(result.unwrapErr()).toEqual({
|
|
code: 'REPOSITORY_ERROR',
|
|
message: 'Wallet not found',
|
|
});
|
|
});
|
|
|
|
it('returns repository error when repository throws', async () => {
|
|
const leagueId = 'league-1';
|
|
|
|
leagueWalletRepository.findByLeagueId.mockRejectedValue(new Error('DB error'));
|
|
|
|
const result = await useCase.execute({ leagueId });
|
|
|
|
expect(result.isErr()).toBe(true);
|
|
expect(result.unwrapErr()).toEqual({
|
|
code: 'REPOSITORY_ERROR',
|
|
message: 'Failed to fetch league wallet',
|
|
});
|
|
});
|
|
});
|