integration tests
Some checks failed
Contract Testing / contract-tests (pull_request) Failing after 4m46s
Contract Testing / contract-snapshot (pull_request) Has been skipped

This commit is contained in:
2026-01-22 19:16:43 +01:00
parent 597bb48248
commit 2fba80da57
25 changed files with 5143 additions and 7496 deletions

View File

@@ -1,346 +1,658 @@
/**
* Integration Test: Sponsor Campaigns Use Case Orchestration
*
*
* Tests the orchestration logic of sponsor campaigns-related Use Cases:
* - GetSponsorCampaignsUseCase: Retrieves sponsor's campaigns
* - GetCampaignStatisticsUseCase: Retrieves campaign statistics
* - FilterCampaignsUseCase: Filters campaigns by status
* - SearchCampaignsUseCase: Searches campaigns by query
* - Validates that Use Cases correctly interact with their Ports (Repositories, Event Publishers)
* - 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, afterAll, beforeEach } from 'vitest';
import { InMemorySponsorRepository } from '../../../adapters/sponsors/persistence/inmemory/InMemorySponsorRepository';
import { InMemoryCampaignRepository } from '../../../adapters/sponsors/persistence/inmemory/InMemoryCampaignRepository';
import { InMemoryEventPublisher } from '../../../adapters/events/InMemoryEventPublisher';
import { GetSponsorCampaignsUseCase } from '../../../core/sponsors/use-cases/GetSponsorCampaignsUseCase';
import { GetCampaignStatisticsUseCase } from '../../../core/sponsors/use-cases/GetCampaignStatisticsUseCase';
import { FilterCampaignsUseCase } from '../../../core/sponsors/use-cases/FilterCampaignsUseCase';
import { SearchCampaignsUseCase } from '../../../core/sponsors/use-cases/SearchCampaignsUseCase';
import { GetSponsorCampaignsQuery } from '../../../core/sponsors/ports/GetSponsorCampaignsQuery';
import { GetCampaignStatisticsQuery } from '../../../core/sponsors/ports/GetCampaignStatisticsQuery';
import { FilterCampaignsCommand } from '../../../core/sponsors/ports/FilterCampaignsCommand';
import { SearchCampaignsCommand } from '../../../core/sponsors/ports/SearchCampaignsCommand';
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 Campaigns Use Case Orchestration', () => {
let sponsorRepository: InMemorySponsorRepository;
let campaignRepository: InMemoryCampaignRepository;
let eventPublisher: InMemoryEventPublisher;
let getSponsorCampaignsUseCase: GetSponsorCampaignsUseCase;
let getCampaignStatisticsUseCase: GetCampaignStatisticsUseCase;
let filterCampaignsUseCase: FilterCampaignsUseCase;
let searchCampaignsUseCase: SearchCampaignsUseCase;
let seasonSponsorshipRepository: InMemorySeasonSponsorshipRepository;
let seasonRepository: InMemorySeasonRepository;
let leagueRepository: InMemoryLeagueRepository;
let leagueMembershipRepository: InMemoryLeagueMembershipRepository;
let raceRepository: InMemoryRaceRepository;
let getSponsorSponsorshipsUseCase: GetSponsorSponsorshipsUseCase;
let mockLogger: Logger;
beforeAll(() => {
// TODO: Initialize In-Memory repositories and event publisher
// sponsorRepository = new InMemorySponsorRepository();
// campaignRepository = new InMemoryCampaignRepository();
// eventPublisher = new InMemoryEventPublisher();
// getSponsorCampaignsUseCase = new GetSponsorCampaignsUseCase({
// sponsorRepository,
// campaignRepository,
// eventPublisher,
// });
// getCampaignStatisticsUseCase = new GetCampaignStatisticsUseCase({
// sponsorRepository,
// campaignRepository,
// eventPublisher,
// });
// filterCampaignsUseCase = new FilterCampaignsUseCase({
// sponsorRepository,
// campaignRepository,
// eventPublisher,
// });
// searchCampaignsUseCase = new SearchCampaignsUseCase({
// sponsorRepository,
// campaignRepository,
// eventPublisher,
// });
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(() => {
// TODO: Clear all In-Memory repositories before each test
// sponsorRepository.clear();
// campaignRepository.clear();
// eventPublisher.clear();
sponsorRepository.clear();
seasonSponsorshipRepository.clear();
seasonRepository.clear();
leagueRepository.clear();
leagueMembershipRepository.clear();
raceRepository.clear();
});
describe('GetSponsorCampaignsUseCase - Success Path', () => {
it('should retrieve all campaigns for a sponsor', async () => {
// TODO: Implement test
// Scenario: Sponsor with multiple campaigns
describe('GetSponsorSponsorshipsUseCase - Success Path', () => {
it('should retrieve all sponsorships for a sponsor', async () => {
// Given: A sponsor exists with ID "sponsor-123"
// And: The sponsor has 5 campaigns (2 active, 2 pending, 1 rejected)
// When: GetSponsorCampaignsUseCase.execute() is called with sponsor ID
// Then: The result should contain all 5 campaigns
// And: Each campaign should display its details
// And: EventPublisher should emit SponsorCampaignsAccessedEvent
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 campaigns with minimal data', async () => {
// TODO: Implement test
// Scenario: Sponsor with minimal campaigns
it('should retrieve sponsorships with minimal data', async () => {
// Given: A sponsor exists with ID "sponsor-123"
// And: The sponsor has 1 campaign
// When: GetSponsorCampaignsUseCase.execute() is called with sponsor ID
// Then: The result should contain the single campaign
// And: EventPublisher should emit SponsorCampaignsAccessedEvent
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 campaigns with empty result', async () => {
// TODO: Implement test
// Scenario: Sponsor with no campaigns
it('should retrieve sponsorships with empty result when no sponsorships exist', async () => {
// Given: A sponsor exists with ID "sponsor-123"
// And: The sponsor has no campaigns
// When: GetSponsorCampaignsUseCase.execute() is called with sponsor ID
// Then: The result should be empty
// And: EventPublisher should emit SponsorCampaignsAccessedEvent
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('GetSponsorCampaignsUseCase - Error Handling', () => {
it('should throw error when sponsor does not exist', async () => {
// TODO: Implement test
// Scenario: Non-existent sponsor
describe('GetSponsorSponsorshipsUseCase - Error Handling', () => {
it('should return error when sponsor does not exist', async () => {
// Given: No sponsor exists with the given ID
// When: GetSponsorCampaignsUseCase.execute() is called with non-existent sponsor ID
// Then: Should throw SponsorNotFoundError
// And: EventPublisher should NOT emit any events
});
// When: GetSponsorSponsorshipsUseCase.execute() is called with non-existent sponsor ID
const result = await getSponsorSponsorshipsUseCase.execute({ sponsorId: 'non-existent-sponsor' });
it('should throw error when sponsor ID is invalid', async () => {
// TODO: Implement test
// Scenario: Invalid sponsor ID
// Given: An invalid sponsor ID (e.g., empty string, null, undefined)
// When: GetSponsorCampaignsUseCase.execute() is called with invalid sponsor ID
// Then: Should throw ValidationError
// And: EventPublisher should NOT emit any events
// Then: Should return an error
expect(result.isErr()).toBe(true);
const error = result.unwrapErr();
expect(error.code).toBe('SPONSOR_NOT_FOUND');
});
});
describe('GetCampaignStatisticsUseCase - Success Path', () => {
it('should retrieve campaign statistics for a sponsor', async () => {
// TODO: Implement test
// Scenario: Sponsor with multiple campaigns
describe('Sponsor Campaigns Data Orchestration', () => {
it('should correctly aggregate sponsorship metrics across multiple sponsorships', async () => {
// Given: A sponsor exists with ID "sponsor-123"
// And: The sponsor has 5 campaigns (2 active, 2 pending, 1 rejected)
// And: The sponsor has total investment of $5000
// And: The sponsor has total impressions of 100000
// When: GetCampaignStatisticsUseCase.execute() is called with sponsor ID
// Then: The result should show total sponsorships count: 5
// And: The result should show active sponsorships count: 2
// And: The result should show pending sponsorships count: 2
// And: The result should show approved sponsorships count: 2
// And: The result should show rejected sponsorships count: 1
// And: The result should show total investment: $5000
// And: The result should show total impressions: 100000
// And: EventPublisher should emit CampaignStatisticsAccessedEvent
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 retrieve statistics with zero values', async () => {
// TODO: Implement test
// Scenario: Sponsor with no campaigns
it('should correctly calculate impressions based on completed races and drivers', async () => {
// Given: A sponsor exists with ID "sponsor-123"
// And: The sponsor has no campaigns
// When: GetCampaignStatisticsUseCase.execute() is called with sponsor ID
// Then: The result should show all counts as 0
// And: The result should show total investment as 0
// And: The result should show total impressions as 0
// And: EventPublisher should emit CampaignStatisticsAccessedEvent
});
});
const sponsor = Sponsor.create({
id: 'sponsor-123',
name: 'Test Company',
contactEmail: 'test@example.com',
});
await sponsorRepository.create(sponsor);
describe('GetCampaignStatisticsUseCase - Error Handling', () => {
it('should throw error when sponsor does not exist', async () => {
// TODO: Implement test
// Scenario: Non-existent sponsor
// Given: No sponsor exists with the given ID
// When: GetCampaignStatisticsUseCase.execute() is called with non-existent sponsor ID
// Then: Should throw SponsorNotFoundError
// And: EventPublisher should NOT emit any events
});
});
// 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);
describe('FilterCampaignsUseCase - Success Path', () => {
it('should filter campaigns by "All" status', async () => {
// TODO: Implement test
// Scenario: Filter by All
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"
// And: The sponsor has 5 campaigns (2 active, 2 pending, 1 rejected)
// When: FilterCampaignsUseCase.execute() is called with status "All"
// Then: The result should contain all 5 campaigns
// And: EventPublisher should emit CampaignsFilteredEvent
});
const sponsor = Sponsor.create({
id: 'sponsor-123',
name: 'Test Company',
contactEmail: 'test@example.com',
});
await sponsorRepository.create(sponsor);
it('should filter campaigns by "Active" status', async () => {
// TODO: Implement test
// Scenario: Filter by Active
// Given: A sponsor exists with ID "sponsor-123"
// And: The sponsor has 5 campaigns (2 active, 2 pending, 1 rejected)
// When: FilterCampaignsUseCase.execute() is called with status "Active"
// Then: The result should contain only 2 active campaigns
// And: EventPublisher should emit CampaignsFilteredEvent
});
// 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);
it('should filter campaigns by "Pending" status', async () => {
// TODO: Implement test
// Scenario: Filter by Pending
// Given: A sponsor exists with ID "sponsor-123"
// And: The sponsor has 5 campaigns (2 active, 2 pending, 1 rejected)
// When: FilterCampaignsUseCase.execute() is called with status "Pending"
// Then: The result should contain only 2 pending campaigns
// And: EventPublisher should emit CampaignsFilteredEvent
});
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);
it('should filter campaigns by "Approved" status', async () => {
// TODO: Implement test
// Scenario: Filter by Approved
// Given: A sponsor exists with ID "sponsor-123"
// And: The sponsor has 5 campaigns (2 active, 2 pending, 1 rejected)
// When: FilterCampaignsUseCase.execute() is called with status "Approved"
// Then: The result should contain only 2 approved campaigns
// And: EventPublisher should emit CampaignsFilteredEvent
});
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);
it('should filter campaigns by "Rejected" status', async () => {
// TODO: Implement test
// Scenario: Filter by Rejected
// Given: A sponsor exists with ID "sponsor-123"
// And: The sponsor has 5 campaigns (2 active, 2 pending, 1 rejected)
// When: FilterCampaignsUseCase.execute() is called with status "Rejected"
// Then: The result should contain only 1 rejected campaign
// And: EventPublisher should emit CampaignsFilteredEvent
});
// When: GetSponsorSponsorshipsUseCase.execute() is called
const result = await getSponsorSponsorshipsUseCase.execute({ sponsorId: 'sponsor-123' });
it('should return empty result when no campaigns match filter', async () => {
// TODO: Implement test
// Scenario: Filter with no matches
// Given: A sponsor exists with ID "sponsor-123"
// And: The sponsor has 2 active campaigns
// When: FilterCampaignsUseCase.execute() is called with status "Pending"
// Then: The result should be empty
// And: EventPublisher should emit CampaignsFilteredEvent
});
});
// Then: Platform fees and net amounts should be calculated correctly
expect(result.isOk()).toBe(true);
const sponsorships = result.unwrap();
describe('FilterCampaignsUseCase - Error Handling', () => {
it('should throw error when sponsor does not exist', async () => {
// TODO: Implement test
// Scenario: Non-existent sponsor
// Given: No sponsor exists with the given ID
// When: FilterCampaignsUseCase.execute() is called with non-existent sponsor ID
// Then: Should throw SponsorNotFoundError
// And: EventPublisher should NOT emit any events
});
// Platform fee = 10% of pricing = 100
expect(sponsorships.sponsorships[0].financials.platformFee.amount).toBe(100);
it('should throw error with invalid status', async () => {
// TODO: Implement test
// Scenario: Invalid status
// Given: A sponsor exists with ID "sponsor-123"
// When: FilterCampaignsUseCase.execute() is called with invalid status
// Then: Should throw ValidationError
// And: EventPublisher should NOT emit any events
});
});
describe('SearchCampaignsUseCase - Success Path', () => {
it('should search campaigns by league name', async () => {
// TODO: Implement test
// Scenario: Search by league name
// Given: A sponsor exists with ID "sponsor-123"
// And: The sponsor has campaigns for leagues: "League A", "League B", "League C"
// When: SearchCampaignsUseCase.execute() is called with query "League A"
// Then: The result should contain only campaigns for "League A"
// And: EventPublisher should emit CampaignsSearchedEvent
});
it('should search campaigns by partial match', async () => {
// TODO: Implement test
// Scenario: Search by partial match
// Given: A sponsor exists with ID "sponsor-123"
// And: The sponsor has campaigns for leagues: "Premier League", "League A", "League B"
// When: SearchCampaignsUseCase.execute() is called with query "League"
// Then: The result should contain campaigns for "Premier League", "League A", "League B"
// And: EventPublisher should emit CampaignsSearchedEvent
});
it('should return empty result when no campaigns match search', async () => {
// TODO: Implement test
// Scenario: Search with no matches
// Given: A sponsor exists with ID "sponsor-123"
// And: The sponsor has campaigns for leagues: "League A", "League B"
// When: SearchCampaignsUseCase.execute() is called with query "NonExistent"
// Then: The result should be empty
// And: EventPublisher should emit CampaignsSearchedEvent
});
it('should return all campaigns when search query is empty', async () => {
// TODO: Implement test
// Scenario: Search with empty query
// Given: A sponsor exists with ID "sponsor-123"
// And: The sponsor has 3 campaigns
// When: SearchCampaignsUseCase.execute() is called with empty query
// Then: The result should contain all 3 campaigns
// And: EventPublisher should emit CampaignsSearchedEvent
});
});
describe('SearchCampaignsUseCase - Error Handling', () => {
it('should throw error when sponsor does not exist', async () => {
// TODO: Implement test
// Scenario: Non-existent sponsor
// Given: No sponsor exists with the given ID
// When: SearchCampaignsUseCase.execute() is called with non-existent sponsor ID
// Then: Should throw SponsorNotFoundError
// And: EventPublisher should NOT emit any events
});
it('should throw error with invalid query', async () => {
// TODO: Implement test
// Scenario: Invalid query
// Given: A sponsor exists with ID "sponsor-123"
// When: SearchCampaignsUseCase.execute() is called with invalid query (e.g., null, undefined)
// Then: Should throw ValidationError
// And: EventPublisher should NOT emit any events
});
});
describe('Campaign Data Orchestration', () => {
it('should correctly aggregate campaign statistics', async () => {
// TODO: Implement test
// Scenario: Campaign statistics aggregation
// Given: A sponsor exists with ID "sponsor-123"
// And: The sponsor has 3 campaigns with investments: $1000, $2000, $3000
// And: The sponsor has 3 campaigns with impressions: 50000, 30000, 20000
// When: GetCampaignStatisticsUseCase.execute() is called
// Then: Total investment should be $6000
// And: Total impressions should be 100000
// And: EventPublisher should emit CampaignStatisticsAccessedEvent
});
it('should correctly filter campaigns by status', async () => {
// TODO: Implement test
// Scenario: Campaign status filtering
// Given: A sponsor exists with ID "sponsor-123"
// And: The sponsor has campaigns with different statuses
// When: FilterCampaignsUseCase.execute() is called with "Active"
// Then: Only active campaigns should be returned
// And: Each campaign should have correct status
// And: EventPublisher should emit CampaignsFilteredEvent
});
it('should correctly search campaigns by league name', async () => {
// TODO: Implement test
// Scenario: Campaign league name search
// Given: A sponsor exists with ID "sponsor-123"
// And: The sponsor has campaigns for different leagues
// When: SearchCampaignsUseCase.execute() is called with league name
// Then: Only campaigns for matching leagues should be returned
// And: Each campaign should have correct league name
// And: EventPublisher should emit CampaignsSearchedEvent
// Net amount = pricing - platform fee = 1000 - 100 = 900
expect(sponsorships.sponsorships[0].financials.netAmount.amount).toBe(900);
});
});
});