/** * Integration Test: Sponsor Leagues Use Case Orchestration * * Tests the orchestration logic of sponsor leagues-related Use Cases: * - GetSponsorSponsorshipsUseCase: Retrieves sponsor's sponsorships/campaigns * - Validates that Use Cases correctly interact with their Ports (Repositories) * - Uses In-Memory adapters for fast, deterministic testing * * Focus: Business logic orchestration, NOT UI rendering */ import { describe, it, expect, beforeAll, beforeEach } from 'vitest'; import { InMemorySponsorRepository } from '../../../adapters/racing/persistence/inmemory/InMemorySponsorRepository'; import { InMemorySeasonSponsorshipRepository } from '../../../adapters/racing/persistence/inmemory/InMemorySeasonSponsorshipRepository'; import { InMemorySeasonRepository } from '../../../adapters/racing/persistence/inmemory/InMemorySeasonRepository'; import { InMemoryLeagueRepository } from '../../../adapters/racing/persistence/inmemory/InMemoryLeagueRepository'; import { InMemoryLeagueMembershipRepository } from '../../../adapters/racing/persistence/inmemory/InMemoryLeagueMembershipRepository'; import { InMemoryRaceRepository } from '../../../adapters/racing/persistence/inmemory/InMemoryRaceRepository'; import { GetSponsorSponsorshipsUseCase } from '../../../core/racing/application/use-cases/GetSponsorSponsorshipsUseCase'; import { Sponsor } from '../../../core/racing/domain/entities/sponsor/Sponsor'; import { SeasonSponsorship } from '../../../core/racing/domain/entities/season/SeasonSponsorship'; import { Season } from '../../../core/racing/domain/entities/season/Season'; import { League } from '../../../core/racing/domain/entities/League'; import { LeagueMembership } from '../../../core/racing/domain/entities/LeagueMembership'; import { Race } from '../../../core/racing/domain/entities/Race'; import { Money } from '../../../core/racing/domain/value-objects/Money'; import { Logger } from '../../../core/shared/domain/Logger'; describe('Sponsor Leagues Use Case Orchestration', () => { let sponsorRepository: InMemorySponsorRepository; let seasonSponsorshipRepository: InMemorySeasonSponsorshipRepository; let seasonRepository: InMemorySeasonRepository; let leagueRepository: InMemoryLeagueRepository; let leagueMembershipRepository: InMemoryLeagueMembershipRepository; let raceRepository: InMemoryRaceRepository; let getSponsorSponsorshipsUseCase: GetSponsorSponsorshipsUseCase; let mockLogger: Logger; beforeAll(() => { mockLogger = { info: () => {}, debug: () => {}, warn: () => {}, error: () => {}, } as unknown as Logger; sponsorRepository = new InMemorySponsorRepository(mockLogger); seasonSponsorshipRepository = new InMemorySeasonSponsorshipRepository(mockLogger); seasonRepository = new InMemorySeasonRepository(mockLogger); leagueRepository = new InMemoryLeagueRepository(mockLogger); leagueMembershipRepository = new InMemoryLeagueMembershipRepository(mockLogger); raceRepository = new InMemoryRaceRepository(mockLogger); getSponsorSponsorshipsUseCase = new GetSponsorSponsorshipsUseCase( sponsorRepository, seasonSponsorshipRepository, seasonRepository, leagueRepository, leagueMembershipRepository, raceRepository, ); }); beforeEach(() => { sponsorRepository.clear(); seasonSponsorshipRepository.clear(); seasonRepository.clear(); leagueRepository.clear(); leagueMembershipRepository.clear(); raceRepository.clear(); }); describe('GetSponsorSponsorshipsUseCase - Success Path', () => { it('should retrieve all sponsorships for a sponsor', async () => { // Given: A sponsor exists with ID "sponsor-123" const sponsor = Sponsor.create({ id: 'sponsor-123', name: 'Test Company', contactEmail: 'test@example.com', }); await sponsorRepository.create(sponsor); // And: The sponsor has 3 sponsorships with different statuses const league1 = League.create({ id: 'league-1', name: 'League 1', description: 'Description 1', ownerId: 'owner-1', }); await leagueRepository.create(league1); const league2 = League.create({ id: 'league-2', name: 'League 2', description: 'Description 2', ownerId: 'owner-2', }); await leagueRepository.create(league2); const league3 = League.create({ id: 'league-3', name: 'League 3', description: 'Description 3', ownerId: 'owner-3', }); await leagueRepository.create(league3); const season1 = Season.create({ id: 'season-1', leagueId: 'league-1', name: 'Season 1', startDate: new Date('2025-01-01'), endDate: new Date('2025-12-31'), }); await seasonRepository.create(season1); const season2 = Season.create({ id: 'season-2', leagueId: 'league-2', name: 'Season 2', startDate: new Date('2025-01-01'), endDate: new Date('2025-12-31'), }); await seasonRepository.create(season2); const season3 = Season.create({ id: 'season-3', leagueId: 'league-3', name: 'Season 3', startDate: new Date('2025-01-01'), endDate: new Date('2025-12-31'), }); await seasonRepository.create(season3); const sponsorship1 = SeasonSponsorship.create({ id: 'sponsorship-1', sponsorId: 'sponsor-123', seasonId: 'season-1', tier: 'main', pricing: Money.create(1000, 'USD'), status: 'active', }); await seasonSponsorshipRepository.create(sponsorship1); const sponsorship2 = SeasonSponsorship.create({ id: 'sponsorship-2', sponsorId: 'sponsor-123', seasonId: 'season-2', tier: 'secondary', pricing: Money.create(500, 'USD'), status: 'pending', }); await seasonSponsorshipRepository.create(sponsorship2); const sponsorship3 = SeasonSponsorship.create({ id: 'sponsorship-3', sponsorId: 'sponsor-123', seasonId: 'season-3', tier: 'secondary', pricing: Money.create(300, 'USD'), status: 'completed', }); await seasonSponsorshipRepository.create(sponsorship3); // And: The sponsor has different numbers of drivers and races in each league for (let i = 1; i <= 10; i++) { const membership = LeagueMembership.create({ id: `membership-1-${i}`, leagueId: 'league-1', driverId: `driver-1-${i}`, role: 'member', status: 'active', }); await leagueMembershipRepository.saveMembership(membership); } for (let i = 1; i <= 5; i++) { const membership = LeagueMembership.create({ id: `membership-2-${i}`, leagueId: 'league-2', driverId: `driver-2-${i}`, role: 'member', status: 'active', }); await leagueMembershipRepository.saveMembership(membership); } for (let i = 1; i <= 8; i++) { const membership = LeagueMembership.create({ id: `membership-3-${i}`, leagueId: 'league-3', driverId: `driver-3-${i}`, role: 'member', status: 'active', }); await leagueMembershipRepository.saveMembership(membership); } for (let i = 1; i <= 5; i++) { const race = Race.create({ id: `race-1-${i}`, leagueId: 'league-1', track: 'Track 1', scheduledAt: new Date(`2025-0${i}-01`), status: 'completed', }); await raceRepository.create(race); } for (let i = 1; i <= 3; i++) { const race = Race.create({ id: `race-2-${i}`, leagueId: 'league-2', track: 'Track 2', scheduledAt: new Date(`2025-0${i}-01`), status: 'completed', }); await raceRepository.create(race); } for (let i = 1; i <= 4; i++) { const race = Race.create({ id: `race-3-${i}`, leagueId: 'league-3', track: 'Track 3', scheduledAt: new Date(`2025-0${i}-01`), status: 'completed', }); await raceRepository.create(race); } // When: GetSponsorSponsorshipsUseCase.execute() is called with sponsor ID const result = await getSponsorSponsorshipsUseCase.execute({ sponsorId: 'sponsor-123' }); // Then: The result should contain sponsor sponsorships expect(result.isOk()).toBe(true); const sponsorships = result.unwrap(); // And: The sponsor name should be correct expect(sponsorships.sponsor.name.toString()).toBe('Test Company'); // And: The sponsorships should contain all 3 sponsorships expect(sponsorships.sponsorships).toHaveLength(3); // And: The summary should show correct values expect(sponsorships.summary.totalSponsorships).toBe(3); expect(sponsorships.summary.activeSponsorships).toBe(1); expect(sponsorships.summary.totalInvestment.amount).toBe(1800); // 1000 + 500 + 300 expect(sponsorships.summary.totalPlatformFees.amount).toBe(180); // 100 + 50 + 30 // And: Each sponsorship should have correct metrics const sponsorship1Summary = sponsorships.sponsorships.find(s => s.sponsorship.id === 'sponsorship-1'); expect(sponsorship1Summary).toBeDefined(); expect(sponsorship1Summary?.metrics.drivers).toBe(10); expect(sponsorship1Summary?.metrics.races).toBe(5); expect(sponsorship1Summary?.metrics.completedRaces).toBe(5); expect(sponsorship1Summary?.metrics.impressions).toBe(5000); // 5 * 10 * 100 }); it('should retrieve sponsorships with minimal data', async () => { // Given: A sponsor exists with ID "sponsor-123" const sponsor = Sponsor.create({ id: 'sponsor-123', name: 'Test Company', contactEmail: 'test@example.com', }); await sponsorRepository.create(sponsor); // And: The sponsor has 1 sponsorship const league = League.create({ id: 'league-1', name: 'League 1', description: 'Description 1', ownerId: 'owner-1', }); await leagueRepository.create(league); const season = Season.create({ id: 'season-1', leagueId: 'league-1', name: 'Season 1', startDate: new Date('2025-01-01'), endDate: new Date('2025-12-31'), }); await seasonRepository.create(season); const sponsorship = SeasonSponsorship.create({ id: 'sponsorship-1', sponsorId: 'sponsor-123', seasonId: 'season-1', tier: 'main', pricing: Money.create(1000, 'USD'), status: 'active', }); await seasonSponsorshipRepository.create(sponsorship); // When: GetSponsorSponsorshipsUseCase.execute() is called with sponsor ID const result = await getSponsorSponsorshipsUseCase.execute({ sponsorId: 'sponsor-123' }); // Then: The result should contain sponsor sponsorships expect(result.isOk()).toBe(true); const sponsorships = result.unwrap(); // And: The sponsorships should contain 1 sponsorship expect(sponsorships.sponsorships).toHaveLength(1); // And: The summary should show correct values expect(sponsorships.summary.totalSponsorships).toBe(1); expect(sponsorships.summary.activeSponsorships).toBe(1); expect(sponsorships.summary.totalInvestment.amount).toBe(1000); expect(sponsorships.summary.totalPlatformFees.amount).toBe(100); }); it('should retrieve sponsorships with empty result when no sponsorships exist', async () => { // Given: A sponsor exists with ID "sponsor-123" const sponsor = Sponsor.create({ id: 'sponsor-123', name: 'Test Company', contactEmail: 'test@example.com', }); await sponsorRepository.create(sponsor); // And: The sponsor has no sponsorships // When: GetSponsorSponsorshipsUseCase.execute() is called with sponsor ID const result = await getSponsorSponsorshipsUseCase.execute({ sponsorId: 'sponsor-123' }); // Then: The result should contain sponsor sponsorships expect(result.isOk()).toBe(true); const sponsorships = result.unwrap(); // And: The sponsorships should be empty expect(sponsorships.sponsorships).toHaveLength(0); // And: The summary should show zero values expect(sponsorships.summary.totalSponsorships).toBe(0); expect(sponsorships.summary.activeSponsorships).toBe(0); expect(sponsorships.summary.totalInvestment.amount).toBe(0); expect(sponsorships.summary.totalPlatformFees.amount).toBe(0); }); }); describe('GetSponsorSponsorshipsUseCase - Error Handling', () => { it('should return error when sponsor does not exist', async () => { // Given: No sponsor exists with the given ID // When: GetSponsorSponsorshipsUseCase.execute() is called with non-existent sponsor ID const result = await getSponsorSponsorshipsUseCase.execute({ sponsorId: 'non-existent-sponsor' }); // Then: Should return an error expect(result.isErr()).toBe(true); const error = result.unwrapErr(); expect(error.code).toBe('SPONSOR_NOT_FOUND'); }); }); describe('Sponsor Leagues Data Orchestration', () => { it('should correctly aggregate sponsorship metrics across multiple sponsorships', async () => { // Given: A sponsor exists with ID "sponsor-123" const sponsor = Sponsor.create({ id: 'sponsor-123', name: 'Test Company', contactEmail: 'test@example.com', }); await sponsorRepository.create(sponsor); // And: The sponsor has 3 sponsorships with different investments const league1 = League.create({ id: 'league-1', name: 'League 1', description: 'Description 1', ownerId: 'owner-1', }); await leagueRepository.create(league1); const league2 = League.create({ id: 'league-2', name: 'League 2', description: 'Description 2', ownerId: 'owner-2', }); await leagueRepository.create(league2); const league3 = League.create({ id: 'league-3', name: 'League 3', description: 'Description 3', ownerId: 'owner-3', }); await leagueRepository.create(league3); const season1 = Season.create({ id: 'season-1', leagueId: 'league-1', name: 'Season 1', startDate: new Date('2025-01-01'), endDate: new Date('2025-12-31'), }); await seasonRepository.create(season1); const season2 = Season.create({ id: 'season-2', leagueId: 'league-2', name: 'Season 2', startDate: new Date('2025-01-01'), endDate: new Date('2025-12-31'), }); await seasonRepository.create(season2); const season3 = Season.create({ id: 'season-3', leagueId: 'league-3', name: 'Season 3', startDate: new Date('2025-01-01'), endDate: new Date('2025-12-31'), }); await seasonRepository.create(season3); const sponsorship1 = SeasonSponsorship.create({ id: 'sponsorship-1', sponsorId: 'sponsor-123', seasonId: 'season-1', tier: 'main', pricing: Money.create(1000, 'USD'), status: 'active', }); await seasonSponsorshipRepository.create(sponsorship1); const sponsorship2 = SeasonSponsorship.create({ id: 'sponsorship-2', sponsorId: 'sponsor-123', seasonId: 'season-2', tier: 'secondary', pricing: Money.create(2000, 'USD'), status: 'active', }); await seasonSponsorshipRepository.create(sponsorship2); const sponsorship3 = SeasonSponsorship.create({ id: 'sponsorship-3', sponsorId: 'sponsor-123', seasonId: 'season-3', tier: 'secondary', pricing: Money.create(3000, 'USD'), status: 'active', }); await seasonSponsorshipRepository.create(sponsorship3); // And: The sponsor has different numbers of drivers and races in each league for (let i = 1; i <= 10; i++) { const membership = LeagueMembership.create({ id: `membership-1-${i}`, leagueId: 'league-1', driverId: `driver-1-${i}`, role: 'member', status: 'active', }); await leagueMembershipRepository.saveMembership(membership); } for (let i = 1; i <= 5; i++) { const membership = LeagueMembership.create({ id: `membership-2-${i}`, leagueId: 'league-2', driverId: `driver-2-${i}`, role: 'member', status: 'active', }); await leagueMembershipRepository.saveMembership(membership); } for (let i = 1; i <= 8; i++) { const membership = LeagueMembership.create({ id: `membership-3-${i}`, leagueId: 'league-3', driverId: `driver-3-${i}`, role: 'member', status: 'active', }); await leagueMembershipRepository.saveMembership(membership); } for (let i = 1; i <= 5; i++) { const race = Race.create({ id: `race-1-${i}`, leagueId: 'league-1', track: 'Track 1', scheduledAt: new Date(`2025-0${i}-01`), status: 'completed', }); await raceRepository.create(race); } for (let i = 1; i <= 3; i++) { const race = Race.create({ id: `race-2-${i}`, leagueId: 'league-2', track: 'Track 2', scheduledAt: new Date(`2025-0${i}-01`), status: 'completed', }); await raceRepository.create(race); } for (let i = 1; i <= 4; i++) { const race = Race.create({ id: `race-3-${i}`, leagueId: 'league-3', track: 'Track 3', scheduledAt: new Date(`2025-0${i}-01`), status: 'completed', }); await raceRepository.create(race); } // When: GetSponsorSponsorshipsUseCase.execute() is called const result = await getSponsorSponsorshipsUseCase.execute({ sponsorId: 'sponsor-123' }); // Then: The metrics should be correctly aggregated expect(result.isOk()).toBe(true); const sponsorships = result.unwrap(); // Total drivers: 10 + 5 + 8 = 23 expect(sponsorships.sponsorships[0].metrics.drivers).toBe(10); expect(sponsorships.sponsorships[1].metrics.drivers).toBe(5); expect(sponsorships.sponsorships[2].metrics.drivers).toBe(8); // Total races: 5 + 3 + 4 = 12 expect(sponsorships.sponsorships[0].metrics.races).toBe(5); expect(sponsorships.sponsorships[1].metrics.races).toBe(3); expect(sponsorships.sponsorships[2].metrics.races).toBe(4); // Total investment: 1000 + 2000 + 3000 = 6000 expect(sponsorships.summary.totalInvestment.amount).toBe(6000); // Total platform fees: 100 + 200 + 300 = 600 expect(sponsorships.summary.totalPlatformFees.amount).toBe(600); }); it('should correctly calculate impressions based on completed races and drivers', async () => { // Given: A sponsor exists with ID "sponsor-123" const sponsor = Sponsor.create({ id: 'sponsor-123', name: 'Test Company', contactEmail: 'test@example.com', }); await sponsorRepository.create(sponsor); // And: The sponsor has 1 league with 10 drivers and 5 completed races const league = League.create({ id: 'league-1', name: 'League 1', description: 'Description 1', ownerId: 'owner-1', }); await leagueRepository.create(league); const season = Season.create({ id: 'season-1', leagueId: 'league-1', name: 'Season 1', startDate: new Date('2025-01-01'), endDate: new Date('2025-12-31'), }); await seasonRepository.create(season); const sponsorship = SeasonSponsorship.create({ id: 'sponsorship-1', sponsorId: 'sponsor-123', seasonId: 'season-1', tier: 'main', pricing: Money.create(1000, 'USD'), status: 'active', }); await seasonSponsorshipRepository.create(sponsorship); for (let i = 1; i <= 10; i++) { const membership = LeagueMembership.create({ id: `membership-${i}`, leagueId: 'league-1', driverId: `driver-${i}`, role: 'member', status: 'active', }); await leagueMembershipRepository.saveMembership(membership); } for (let i = 1; i <= 5; i++) { const race = Race.create({ id: `race-${i}`, leagueId: 'league-1', track: 'Track 1', scheduledAt: new Date(`2025-0${i}-01`), status: 'completed', }); await raceRepository.create(race); } // When: GetSponsorSponsorshipsUseCase.execute() is called const result = await getSponsorSponsorshipsUseCase.execute({ sponsorId: 'sponsor-123' }); // Then: Impressions should be calculated correctly // Impressions = completed races * drivers * 100 = 5 * 10 * 100 = 5000 expect(result.isOk()).toBe(true); const sponsorships = result.unwrap(); expect(sponsorships.sponsorships[0].metrics.impressions).toBe(5000); }); it('should correctly calculate platform fees and net amounts', async () => { // Given: A sponsor exists with ID "sponsor-123" const sponsor = Sponsor.create({ id: 'sponsor-123', name: 'Test Company', contactEmail: 'test@example.com', }); await sponsorRepository.create(sponsor); // And: The sponsor has 1 sponsorship const league = League.create({ id: 'league-1', name: 'League 1', description: 'Description 1', ownerId: 'owner-1', }); await leagueRepository.create(league); const season = Season.create({ id: 'season-1', leagueId: 'league-1', name: 'Season 1', startDate: new Date('2025-01-01'), endDate: new Date('2025-12-31'), }); await seasonRepository.create(season); const sponsorship = SeasonSponsorship.create({ id: 'sponsorship-1', sponsorId: 'sponsor-123', seasonId: 'season-1', tier: 'main', pricing: Money.create(1000, 'USD'), status: 'active', }); await seasonSponsorshipRepository.create(sponsorship); // When: GetSponsorSponsorshipsUseCase.execute() is called const result = await getSponsorSponsorshipsUseCase.execute({ sponsorId: 'sponsor-123' }); // Then: Platform fees and net amounts should be calculated correctly expect(result.isOk()).toBe(true); const sponsorships = result.unwrap(); // Platform fee = 10% of pricing = 100 expect(sponsorships.sponsorships[0].financials.platformFee.amount).toBe(100); // Net amount = pricing - platform fee = 1000 - 100 = 900 expect(sponsorships.sponsorships[0].financials.netAmount.amount).toBe(900); }); }); });