Files
gridpilot.gg/core/racing/application/use-cases/GetLeagueWalletUseCase.test.ts
2025-12-20 11:02:15 +01:00

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',
});
});
});