import { SeasonSponsorship } from '@core/racing/domain/entities/season/SeasonSponsorship'; import type { SeasonSponsorshipRepository } from '@core/racing/domain/repositories/SeasonSponsorshipRepository'; import { Money } from '@core/racing/domain/value-objects/Money'; import { describe, expect, it, vi, type Mock } from 'vitest'; import type { Payment } from '../../domain/entities/Payment'; import { PayerType, PaymentStatus, PaymentType } from '../../domain/entities/Payment'; import type { PaymentRepository } from '../../domain/repositories/PaymentRepository'; import type { SponsorRepository } from '@core/racing/domain/repositories/SponsorRepository'; import { GetSponsorBillingUseCase, type GetSponsorBillingInput } from './GetSponsorBillingUseCase'; describe('GetSponsorBillingUseCase', () => { let paymentRepository: { findByFilters: Mock }; let seasonSponsorshipRepository: { findBySponsorId: Mock }; let sponsorRepository: { findById: Mock }; let useCase: GetSponsorBillingUseCase; beforeEach(() => { paymentRepository = { findByFilters: vi.fn(), }; seasonSponsorshipRepository = { findBySponsorId: vi.fn(), }; sponsorRepository = { findById: vi.fn(), }; useCase = new GetSponsorBillingUseCase( paymentRepository as unknown as PaymentRepository, seasonSponsorshipRepository as unknown as SeasonSponsorshipRepository, sponsorRepository as unknown as SponsorRepository, ); }); it('derives invoices and stats from payments and sponsorships', async () => { const sponsorId = 'sponsor-1'; // Mock sponsor exists sponsorRepository.findById.mockResolvedValue({ id: sponsorId, name: 'Test Sponsor', }); const payments: Payment[] = [ { id: 'pay-1', type: PaymentType.SPONSORSHIP, amount: 100, platformFee: 10, netAmount: 90, payerId: sponsorId, payerType: PayerType.SPONSOR, leagueId: 'league-1', seasonId: 'season-1', status: PaymentStatus.COMPLETED, createdAt: new Date('2024-01-01T00:00:00.000Z'), completedAt: new Date('2024-01-01T00:00:00.000Z'), }, { id: 'pay-2', type: PaymentType.SPONSORSHIP, amount: 50, platformFee: 5, netAmount: 45, payerId: sponsorId, payerType: PayerType.SPONSOR, leagueId: 'league-1', seasonId: 'season-1', status: PaymentStatus.PENDING, createdAt: new Date('2024-02-01T00:00:00.000Z'), }, ]; paymentRepository.findByFilters.mockResolvedValue(payments); const sponsorships = [ SeasonSponsorship.create({ id: 'ss-1', seasonId: 'season-1', leagueId: 'league-1', sponsorId, tier: 'main', pricing: Money.create(100, 'USD'), status: 'active', }), SeasonSponsorship.create({ id: 'ss-2', seasonId: 'season-2', leagueId: 'league-1', sponsorId, tier: 'secondary', pricing: Money.create(50, 'USD'), status: 'pending', }), ]; seasonSponsorshipRepository.findBySponsorId.mockResolvedValue(sponsorships); const input: GetSponsorBillingInput = { sponsorId }; const result = await useCase.execute(input); expect(result.isOk()).toBe(true); const value = result.unwrap(); expect(paymentRepository.findByFilters).toHaveBeenCalledWith({ payerId: sponsorId, type: PaymentType.SPONSORSHIP, }); expect(seasonSponsorshipRepository.findBySponsorId).toHaveBeenCalledWith(sponsorId); expect(value.paymentMethods).toEqual([]); expect(value.invoices).toHaveLength(2); // totals: each invoice adds 19% VAT // pay-1 total: 100 + 19 = 119 (paid) // pay-2 total: 50 + 9.5 = 59.5 (pending) expect(value.stats.totalSpent).toBeCloseTo(119, 5); expect(value.stats.pendingAmount).toBeCloseTo(59.5, 5); expect(value.stats.activeSponsorships).toBe(1); expect(value.stats.nextPaymentDate).not.toBeNull(); expect(value.stats.nextPaymentAmount).not.toBeNull(); }); });