/** * Integration Test: Sponsor Dashboard Use Case Orchestration * * Tests the orchestration logic of sponsor dashboard-related Use Cases: * - GetSponsorDashboardUseCase: Retrieves sponsor dashboard metrics * - 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 { GetSponsorDashboardUseCase } from '../../../core/racing/application/use-cases/GetSponsorDashboardUseCase'; 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 Dashboard Use Case Orchestration', () => { let sponsorRepository: InMemorySponsorRepository; let seasonSponsorshipRepository: InMemorySeasonSponsorshipRepository; let seasonRepository: InMemorySeasonRepository; let leagueRepository: InMemoryLeagueRepository; let leagueMembershipRepository: InMemoryLeagueMembershipRepository; let raceRepository: InMemoryRaceRepository; let getSponsorDashboardUseCase: GetSponsorDashboardUseCase; 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); getSponsorDashboardUseCase = new GetSponsorDashboardUseCase( sponsorRepository, seasonSponsorshipRepository, seasonRepository, leagueRepository, leagueMembershipRepository, raceRepository, ); }); beforeEach(() => { sponsorRepository.clear(); seasonSponsorshipRepository.clear(); seasonRepository.clear(); leagueRepository.clear(); leagueMembershipRepository.clear(); raceRepository.clear(); }); describe('GetSponsorDashboardUseCase - Success Path', () => { it('should retrieve dashboard metrics for a sponsor with active 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 2 active sponsorships 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 season1 = Season.create({ id: 'season-1', leagueId: 'league-1', gameId: 'game-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', gameId: 'game-1', name: 'Season 2', startDate: new Date('2025-01-01'), endDate: new Date('2025-12-31'), }); await seasonRepository.create(season2); 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: 'active', }); await seasonSponsorshipRepository.create(sponsorship2); // And: The sponsor has 5 drivers in league 1 and 3 drivers in league 2 for (let i = 1; i <= 5; 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 <= 3; i++) { const membership = LeagueMembership.create({ id: `membership-2-${i}`, leagueId: 'league-2', driverId: `driver-2-${i}`, role: 'member', status: 'active', }); await leagueMembershipRepository.saveMembership(membership); } // And: The sponsor has 3 completed races in league 1 and 2 completed races in league 2 for (let i = 1; i <= 3; i++) { const race = Race.create({ id: `race-1-${i}`, leagueId: 'league-1', track: 'Track 1', car: 'GT3', scheduledAt: new Date(`2025-0${i}-01`), status: 'completed', }); await raceRepository.create(race); } for (let i = 1; i <= 2; i++) { const race = Race.create({ id: `race-2-${i}`, leagueId: 'league-2', track: 'Track 2', car: 'GT3', scheduledAt: new Date(`2025-0${i}-01`), status: 'completed', }); await raceRepository.create(race); } // When: GetSponsorDashboardUseCase.execute() is called with sponsor ID const result = await getSponsorDashboardUseCase.execute({ sponsorId: 'sponsor-123' }); // Then: The result should contain dashboard metrics expect(result.isOk()).toBe(true); const dashboard = result.unwrap(); // And: The sponsor name should be correct expect(dashboard.sponsorName).toBe('Test Company'); // And: The metrics should show correct values expect(dashboard.metrics.impressions).toBeGreaterThan(0); expect(dashboard.metrics.races).toBe(5); // 3 + 2 expect(dashboard.metrics.drivers).toBe(8); // 5 + 3 expect(dashboard.metrics.exposure).toBeGreaterThan(0); // And: The sponsored leagues should contain both leagues expect(dashboard.sponsoredLeagues).toHaveLength(2); expect(dashboard.sponsoredLeagues[0].leagueName).toBe('League 1'); expect(dashboard.sponsoredLeagues[1].leagueName).toBe('League 2'); // And: The investment summary should show correct values expect(dashboard.investment.activeSponsorships).toBe(2); expect(dashboard.investment.totalInvestment.amount).toBe(1500); // 1000 + 500 expect(dashboard.investment.costPerThousandViews).toBeGreaterThan(0); }); it('should retrieve dashboard with zero values when sponsor has no 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 no sponsorships // When: GetSponsorDashboardUseCase.execute() is called with sponsor ID const result = await getSponsorDashboardUseCase.execute({ sponsorId: 'sponsor-123' }); // Then: The result should contain dashboard metrics with zero values expect(result.isOk()).toBe(true); const dashboard = result.unwrap(); // And: The sponsor name should be correct expect(dashboard.sponsorName).toBe('Test Company'); // And: The metrics should show zero values expect(dashboard.metrics.impressions).toBe(0); expect(dashboard.metrics.races).toBe(0); expect(dashboard.metrics.drivers).toBe(0); expect(dashboard.metrics.exposure).toBe(0); // And: The sponsored leagues should be empty expect(dashboard.sponsoredLeagues).toHaveLength(0); // And: The investment summary should show zero values expect(dashboard.investment.activeSponsorships).toBe(0); expect(dashboard.investment.totalInvestment.amount).toBe(0); expect(dashboard.investment.costPerThousandViews).toBe(0); }); it('should retrieve dashboard with mixed sponsorship statuses', 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 active, 1 pending, and 1 completed sponsorship const league1 = League.create({ id: 'league-1', name: 'League 1', description: 'Description 1', ownerId: 'owner-1', }); await leagueRepository.create(league1); const season1 = Season.create({ id: 'season-1', leagueId: 'league-1', gameId: 'game-1', name: 'Season 1', startDate: new Date('2025-01-01'), endDate: new Date('2025-12-31'), }); await seasonRepository.create(season1); 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-1', tier: 'secondary', pricing: Money.create(500, 'USD'), status: 'active', }); await seasonSponsorshipRepository.create(sponsorship2); const sponsorship3 = SeasonSponsorship.create({ id: 'sponsorship-3', sponsorId: 'sponsor-123', seasonId: 'season-1', tier: 'secondary', pricing: Money.create(300, 'USD'), status: 'active', }); await seasonSponsorshipRepository.create(sponsorship3); // When: GetSponsorDashboardUseCase.execute() is called with sponsor ID const result = await getSponsorDashboardUseCase.execute({ sponsorId: 'sponsor-123' }); // Then: The result should contain dashboard metrics expect(result.isOk()).toBe(true); const dashboard = result.unwrap(); // And: The investment summary should show only active sponsorships expect(dashboard.investment.activeSponsorships).toBe(3); expect(dashboard.investment.totalInvestment.amount).toBe(1800); // 1000 + 500 + 300 }); }); describe('GetSponsorDashboardUseCase - Error Handling', () => { it('should return error when sponsor does not exist', async () => { // Given: No sponsor exists with the given ID // When: GetSponsorDashboardUseCase.execute() is called with non-existent sponsor ID const result = await getSponsorDashboardUseCase.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 Dashboard Data Orchestration', () => { it('should correctly aggregate dashboard 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', gameId: 'game-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', gameId: 'game-1', 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', gameId: 'game-1', 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', car: 'GT3', 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', car: 'GT3', 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', car: 'GT3', scheduledAt: new Date(`2025-0${i}-01`), status: 'completed', }); await raceRepository.create(race); } // When: GetSponsorDashboardUseCase.execute() is called const result = await getSponsorDashboardUseCase.execute({ sponsorId: 'sponsor-123' }); // Then: The metrics should be correctly aggregated expect(result.isOk()).toBe(true); const dashboard = result.unwrap(); // Total drivers: 10 + 5 + 8 = 23 expect(dashboard.metrics.drivers).toBe(23); // Total races: 5 + 3 + 4 = 12 expect(dashboard.metrics.races).toBe(12); // Total investment: 1000 + 2000 + 3000 = 6000 expect(dashboard.investment.totalInvestment.amount).toBe(6000); // Total sponsorships: 3 expect(dashboard.investment.activeSponsorships).toBe(3); // Cost per thousand views should be calculated correctly expect(dashboard.investment.costPerThousandViews).toBeGreaterThan(0); }); 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', gameId: 'game-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', car: 'GT3', scheduledAt: new Date(`2025-0${i}-01`), status: 'completed', }); await raceRepository.create(race); } // When: GetSponsorDashboardUseCase.execute() is called const result = await getSponsorDashboardUseCase.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 dashboard = result.unwrap(); expect(dashboard.metrics.impressions).toBe(5000); }); it('should correctly determine sponsorship status based on season dates', 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 sponsorships with different season dates 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); // Active season (current date is between start and end) const season1 = Season.create({ id: 'season-1', leagueId: 'league-1', gameId: 'game-1', name: 'Season 1', startDate: new Date(Date.now() - 86400000), endDate: new Date(Date.now() + 86400000), }); await seasonRepository.create(season1); // Upcoming season (start date is in the future) const season2 = Season.create({ id: 'season-2', leagueId: 'league-2', gameId: 'game-1', name: 'Season 2', startDate: new Date(Date.now() + 86400000), endDate: new Date(Date.now() + 172800000), }); await seasonRepository.create(season2); // Completed season (end date is in the past) const season3 = Season.create({ id: 'season-3', leagueId: 'league-3', gameId: 'game-1', name: 'Season 3', startDate: new Date(Date.now() - 172800000), endDate: new Date(Date.now() - 86400000), }); 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: 'active', }); 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: 'active', }); await seasonSponsorshipRepository.create(sponsorship3); // When: GetSponsorDashboardUseCase.execute() is called const result = await getSponsorDashboardUseCase.execute({ sponsorId: 'sponsor-123' }); // Then: The sponsored leagues should have correct status expect(result.isOk()).toBe(true); const dashboard = result.unwrap(); expect(dashboard.sponsoredLeagues).toHaveLength(3); // League 1 should be active (current date is between start and end) expect(dashboard.sponsoredLeagues[0].status).toBe('active'); // League 2 should be upcoming (start date is in the future) expect(dashboard.sponsoredLeagues[1].status).toBe('upcoming'); // League 3 should be completed (end date is in the past) expect(dashboard.sponsoredLeagues[2].status).toBe('completed'); }); }); });