import { beforeEach, describe, expect, it, vi, type Mock } from 'vitest'; import { GetWalletUseCase, type GetWalletInput } from './GetWalletUseCase'; import type { ITransactionRepository, IWalletRepository } from '../../domain/repositories/IWalletRepository'; import type { Transaction, Wallet } from '../../domain/entities/Wallet'; import { TransactionType } from '../../domain/entities/Wallet'; import type { UseCaseOutputPort } from '@core/shared/application/UseCaseOutputPort'; describe('GetWalletUseCase', () => { let walletRepository: { findByLeagueId: Mock; create: Mock; }; let transactionRepository: { findByWalletId: Mock; }; let output: { present: Mock; }; let useCase: GetWalletUseCase; beforeEach(() => { walletRepository = { findByLeagueId: vi.fn(), create: vi.fn(), }; transactionRepository = { findByWalletId: vi.fn(), }; output = { present: vi.fn(), }; useCase = new GetWalletUseCase( walletRepository as unknown as IWalletRepository, transactionRepository as unknown as ITransactionRepository, output as unknown as UseCaseOutputPort, ); }); it('returns INVALID_INPUT when leagueId is missing', async () => { const input = { leagueId: '' } as unknown as GetWalletInput; const result = await useCase.execute(input); expect(result.isErr()).toBe(true); expect(result.unwrapErr().code).toBe('INVALID_INPUT'); expect(output.present).not.toHaveBeenCalled(); }); it('presents existing wallet and transactions sorted desc by createdAt', async () => { const input: GetWalletInput = { leagueId: 'league-1' }; const wallet: Wallet = { id: 'wallet-1', leagueId: 'league-1', balance: 50, totalRevenue: 100, totalPlatformFees: 5, totalWithdrawn: 10, currency: 'USD', createdAt: new Date('2025-01-01T00:00:00.000Z'), }; const older: Transaction = { id: 'txn-older', walletId: 'wallet-1', type: TransactionType.DEPOSIT, amount: 25, description: 'Older', createdAt: new Date('2025-01-01T00:00:00.000Z'), }; const newer: Transaction = { id: 'txn-newer', walletId: 'wallet-1', type: TransactionType.WITHDRAWAL, amount: 10, description: 'Newer', createdAt: new Date('2025-01-02T00:00:00.000Z'), }; walletRepository.findByLeagueId.mockResolvedValue(wallet); transactionRepository.findByWalletId.mockResolvedValue([older, newer]); const result = await useCase.execute(input); expect(result.isOk()).toBe(true); expect(transactionRepository.findByWalletId).toHaveBeenCalledWith('wallet-1'); expect(output.present).toHaveBeenCalledWith({ wallet, transactions: [newer, older], }); }); it('creates wallet when missing, then presents wallet and transactions', async () => { const input: GetWalletInput = { leagueId: 'league-1' }; vi.useFakeTimers(); vi.setSystemTime(new Date('2025-01-01T00:00:00.000Z')); vi.spyOn(Math, 'random').mockReturnValue(0.123456789); try { walletRepository.findByLeagueId.mockResolvedValue(null); walletRepository.create.mockImplementation(async (w: Wallet) => w); transactionRepository.findByWalletId.mockResolvedValue([]); const result = await useCase.execute(input); expect(result.isOk()).toBe(true); expect(walletRepository.create).toHaveBeenCalledWith( expect.objectContaining({ id: expect.stringMatching(/^wallet-1735689600000-[a-z0-9]{9}$/), leagueId: 'league-1', balance: 0, totalRevenue: 0, totalPlatformFees: 0, totalWithdrawn: 0, currency: 'USD', createdAt: new Date('2025-01-01T00:00:00.000Z'), }), ); const createdWalletArg = walletRepository.create.mock.calls[0]?.[0] as Wallet; expect(transactionRepository.findByWalletId).toHaveBeenCalledWith(createdWalletArg.id); expect(output.present).toHaveBeenCalledWith({ wallet: createdWalletArg, transactions: [], }); } finally { vi.useRealTimers(); } }); });