Files
gridpilot.gg/core/racing/application/use-cases/GetLeagueWalletUseCase.test.ts
2026-01-16 19:38:55 +01:00

213 lines
6.7 KiB
TypeScript

import { describe, it, expect, beforeEach, vi, type Mock } from 'vitest';
import {
GetLeagueWalletUseCase,
type GetLeagueWalletInput,
type GetLeagueWalletErrorCode,
} from './GetLeagueWalletUseCase';
import type { LeagueRepository } from '../../domain/repositories/LeagueRepository';
import type { LeagueWalletRepository } from '../../domain/repositories/LeagueWalletRepository';
import type { TransactionRepository } from '../../domain/repositories/TransactionRepository';
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';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
describe('GetLeagueWalletUseCase', () => {
let leagueRepository: {
exists: Mock;
};
let leagueWalletRepository: {
findByLeagueId: Mock;
};
let transactionRepository: {
findByWalletId: Mock;
};
let useCase: GetLeagueWalletUseCase;
beforeEach(() => {
leagueRepository = {
exists: vi.fn(),
};
leagueWalletRepository = {
findByLeagueId: vi.fn(),
};
transactionRepository = {
findByWalletId: vi.fn(),
};
useCase = new GetLeagueWalletUseCase(leagueRepository as unknown as LeagueRepository,
leagueWalletRepository as unknown as LeagueWalletRepository,
transactionRepository as unknown as TransactionRepository);
});
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,
});
leagueRepository.exists.mockResolvedValue(true);
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: {},
completedAt: undefined,
}).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: {},
completedAt: undefined,
}).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: {},
completedAt: undefined,
}).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: {},
completedAt: undefined,
});
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: {},
completedAt: undefined,
});
const transactions = [
sponsorshipTx,
membershipTx,
withdrawalTx,
pendingPrizeTx,
refundTx,
];
transactionRepository.findByWalletId.mockResolvedValue(transactions);
const input: GetLeagueWalletInput = { leagueId };
const result = await useCase.execute(input);
expect(result.isOk()).toBe(true);
const presented = result.unwrap();
expect(presented.wallet).toBe(wallet);
expect(presented.transactions).toHaveLength(transactions.length);
expect(presented.transactions[0]!.id).toEqual(
transactions.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime())[0]!
.id,
);
const { aggregates } = presented;
const expectedTotalRevenue =
sponsorshipTx.amount.add(membershipTx.amount).add(pendingPrizeTx.amount);
const expectedTotalFees =
sponsorshipTx.platformFee
.add(membershipTx.platformFee)
.add(pendingPrizeTx.platformFee);
const expectedTotalWithdrawals = withdrawalTx.netAmount;
const expectedPendingPayouts = pendingPrizeTx.netAmount;
expect(aggregates.balance).toBe(balance);
expect(aggregates.totalRevenue.amount).toBe(expectedTotalRevenue.amount);
expect(aggregates.totalFees.amount).toBe(expectedTotalFees.amount);
expect(aggregates.totalWithdrawals.amount).toBe(
expectedTotalWithdrawals.amount,
);
expect(aggregates.pendingPayouts.amount).toBe(expectedPendingPayouts.amount);
});
it('returns error result when wallet is missing', async () => {
const leagueId = 'league-missing';
leagueRepository.exists.mockResolvedValue(true);
leagueWalletRepository.findByLeagueId.mockResolvedValue(null);
const input: GetLeagueWalletInput = { leagueId };
const result = await useCase.execute(input);
expect(result.isErr()).toBe(true);
const err = result.unwrapErr() as ApplicationErrorCode<
GetLeagueWalletErrorCode,
{ message: string }
>;
expect(err.code).toBe('WALLET_NOT_FOUND');
expect(err.details.message).toBe('League wallet not found');
});
it('returns league not found when league does not exist', async () => {
const leagueId = 'league-missing';
leagueRepository.exists.mockResolvedValue(false);
const input: GetLeagueWalletInput = { leagueId };
const result = await useCase.execute(input);
expect(result.isErr()).toBe(true);
const err = result.unwrapErr() as ApplicationErrorCode<
GetLeagueWalletErrorCode,
{ message: string }
>;
expect(err.code).toBe('LEAGUE_NOT_FOUND');
expect(err.details.message).toBe('League not found');
});
it('returns repository error when repository throws', async () => {
const leagueId = 'league-1';
leagueRepository.exists.mockRejectedValue(new Error('DB error'));
const input: GetLeagueWalletInput = { leagueId };
const result = await useCase.execute(input);
expect(result.isErr()).toBe(true);
const err = result.unwrapErr() as ApplicationErrorCode<
GetLeagueWalletErrorCode,
{ message: string }
>;
expect(err.code).toBe('REPOSITORY_ERROR');
expect(err.details.message).toBe('DB error');
});
});