332 lines
15 KiB
TypeScript
332 lines
15 KiB
TypeScript
/**
|
|
* Integration Test: Sponsor Leagues Use Case Orchestration
|
|
*
|
|
* Tests the orchestration logic of sponsor leagues-related Use Cases:
|
|
* - GetAvailableLeaguesUseCase: Retrieves available leagues for sponsorship
|
|
* - GetLeagueStatisticsUseCase: Retrieves league statistics
|
|
* - FilterLeaguesUseCase: Filters leagues by availability
|
|
* - SearchLeaguesUseCase: Searches leagues by query
|
|
* - Validates that Use Cases correctly interact with their Ports (Repositories, Event Publishers)
|
|
* - 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 { InMemoryLeagueRepository } from '../../../adapters/leagues/persistence/inmemory/InMemoryLeagueRepository';
|
|
import { InMemoryEventPublisher } from '../../../adapters/events/InMemoryEventPublisher';
|
|
import { GetAvailableLeaguesUseCase } from '../../../core/sponsors/use-cases/GetAvailableLeaguesUseCase';
|
|
import { GetLeagueStatisticsUseCase } from '../../../core/sponsors/use-cases/GetLeagueStatisticsUseCase';
|
|
import { FilterLeaguesUseCase } from '../../../core/sponsors/use-cases/FilterLeaguesUseCase';
|
|
import { SearchLeaguesUseCase } from '../../../core/sponsors/use-cases/SearchLeaguesUseCase';
|
|
import { GetAvailableLeaguesQuery } from '../../../core/sponsors/ports/GetAvailableLeaguesQuery';
|
|
import { GetLeagueStatisticsQuery } from '../../../core/sponsors/ports/GetLeagueStatisticsQuery';
|
|
import { FilterLeaguesCommand } from '../../../core/sponsors/ports/FilterLeaguesCommand';
|
|
import { SearchLeaguesCommand } from '../../../core/sponsors/ports/SearchLeaguesCommand';
|
|
|
|
describe('Sponsor Leagues Use Case Orchestration', () => {
|
|
let sponsorRepository: InMemorySponsorRepository;
|
|
let leagueRepository: InMemoryLeagueRepository;
|
|
let eventPublisher: InMemoryEventPublisher;
|
|
let getAvailableLeaguesUseCase: GetAvailableLeaguesUseCase;
|
|
let getLeagueStatisticsUseCase: GetLeagueStatisticsUseCase;
|
|
let filterLeaguesUseCase: FilterLeaguesUseCase;
|
|
let searchLeaguesUseCase: SearchLeaguesUseCase;
|
|
|
|
beforeAll(() => {
|
|
// TODO: Initialize In-Memory repositories and event publisher
|
|
// sponsorRepository = new InMemorySponsorRepository();
|
|
// leagueRepository = new InMemoryLeagueRepository();
|
|
// eventPublisher = new InMemoryEventPublisher();
|
|
// getAvailableLeaguesUseCase = new GetAvailableLeaguesUseCase({
|
|
// sponsorRepository,
|
|
// leagueRepository,
|
|
// eventPublisher,
|
|
// });
|
|
// getLeagueStatisticsUseCase = new GetLeagueStatisticsUseCase({
|
|
// sponsorRepository,
|
|
// leagueRepository,
|
|
// eventPublisher,
|
|
// });
|
|
// filterLeaguesUseCase = new FilterLeaguesUseCase({
|
|
// sponsorRepository,
|
|
// leagueRepository,
|
|
// eventPublisher,
|
|
// });
|
|
// searchLeaguesUseCase = new SearchLeaguesUseCase({
|
|
// sponsorRepository,
|
|
// leagueRepository,
|
|
// eventPublisher,
|
|
// });
|
|
});
|
|
|
|
beforeEach(() => {
|
|
// TODO: Clear all In-Memory repositories before each test
|
|
// sponsorRepository.clear();
|
|
// leagueRepository.clear();
|
|
// eventPublisher.clear();
|
|
});
|
|
|
|
describe('GetAvailableLeaguesUseCase - Success Path', () => {
|
|
it('should retrieve available leagues for sponsorship', async () => {
|
|
// TODO: Implement test
|
|
// Scenario: Sponsor with available leagues
|
|
// Given: A sponsor exists with ID "sponsor-123"
|
|
// And: There are 5 leagues available for sponsorship
|
|
// When: GetAvailableLeaguesUseCase.execute() is called with sponsor ID
|
|
// Then: The result should contain all 5 leagues
|
|
// And: Each league should display its details
|
|
// And: EventPublisher should emit AvailableLeaguesAccessedEvent
|
|
});
|
|
|
|
it('should retrieve leagues with minimal data', async () => {
|
|
// TODO: Implement test
|
|
// Scenario: Sponsor with minimal leagues
|
|
// Given: A sponsor exists with ID "sponsor-123"
|
|
// And: There is 1 league available for sponsorship
|
|
// When: GetAvailableLeaguesUseCase.execute() is called with sponsor ID
|
|
// Then: The result should contain the single league
|
|
// And: EventPublisher should emit AvailableLeaguesAccessedEvent
|
|
});
|
|
|
|
it('should retrieve leagues with empty result', async () => {
|
|
// TODO: Implement test
|
|
// Scenario: Sponsor with no available leagues
|
|
// Given: A sponsor exists with ID "sponsor-123"
|
|
// And: There are no leagues available for sponsorship
|
|
// When: GetAvailableLeaguesUseCase.execute() is called with sponsor ID
|
|
// Then: The result should be empty
|
|
// And: EventPublisher should emit AvailableLeaguesAccessedEvent
|
|
});
|
|
});
|
|
|
|
describe('GetAvailableLeaguesUseCase - 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: GetAvailableLeaguesUseCase.execute() is called with non-existent sponsor ID
|
|
// Then: Should throw SponsorNotFoundError
|
|
// And: EventPublisher should NOT emit any events
|
|
});
|
|
|
|
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: GetAvailableLeaguesUseCase.execute() is called with invalid sponsor ID
|
|
// Then: Should throw ValidationError
|
|
// And: EventPublisher should NOT emit any events
|
|
});
|
|
});
|
|
|
|
describe('GetLeagueStatisticsUseCase - Success Path', () => {
|
|
it('should retrieve league statistics', async () => {
|
|
// TODO: Implement test
|
|
// Scenario: Sponsor with league statistics
|
|
// Given: A sponsor exists with ID "sponsor-123"
|
|
// And: There are 10 leagues available
|
|
// And: There are 3 main sponsor slots available
|
|
// And: There are 15 secondary sponsor slots available
|
|
// And: There are 500 total drivers
|
|
// And: Average CPM is $50
|
|
// When: GetLeagueStatisticsUseCase.execute() is called with sponsor ID
|
|
// Then: The result should show total leagues count: 10
|
|
// And: The result should show main sponsor slots available: 3
|
|
// And: The result should show secondary sponsor slots available: 15
|
|
// And: The result should show total drivers count: 500
|
|
// And: The result should show average CPM: $50
|
|
// And: EventPublisher should emit LeagueStatisticsAccessedEvent
|
|
});
|
|
|
|
it('should retrieve statistics with zero values', async () => {
|
|
// TODO: Implement test
|
|
// Scenario: Sponsor with no leagues
|
|
// Given: A sponsor exists with ID "sponsor-123"
|
|
// And: There are no leagues available
|
|
// When: GetLeagueStatisticsUseCase.execute() is called with sponsor ID
|
|
// Then: The result should show all counts as 0
|
|
// And: The result should show average CPM as 0
|
|
// And: EventPublisher should emit LeagueStatisticsAccessedEvent
|
|
});
|
|
});
|
|
|
|
describe('GetLeagueStatisticsUseCase - 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: GetLeagueStatisticsUseCase.execute() is called with non-existent sponsor ID
|
|
// Then: Should throw SponsorNotFoundError
|
|
// And: EventPublisher should NOT emit any events
|
|
});
|
|
});
|
|
|
|
describe('FilterLeaguesUseCase - Success Path', () => {
|
|
it('should filter leagues by "All" availability', async () => {
|
|
// TODO: Implement test
|
|
// Scenario: Filter by All
|
|
// Given: A sponsor exists with ID "sponsor-123"
|
|
// And: There are 5 leagues (3 with main slot available, 2 with secondary slots available)
|
|
// When: FilterLeaguesUseCase.execute() is called with availability "All"
|
|
// Then: The result should contain all 5 leagues
|
|
// And: EventPublisher should emit LeaguesFilteredEvent
|
|
});
|
|
|
|
it('should filter leagues by "Main Slot Available" availability', async () => {
|
|
// TODO: Implement test
|
|
// Scenario: Filter by Main Slot Available
|
|
// Given: A sponsor exists with ID "sponsor-123"
|
|
// And: There are 5 leagues (3 with main slot available, 2 with secondary slots available)
|
|
// When: FilterLeaguesUseCase.execute() is called with availability "Main Slot Available"
|
|
// Then: The result should contain only 3 leagues with main slot available
|
|
// And: EventPublisher should emit LeaguesFilteredEvent
|
|
});
|
|
|
|
it('should filter leagues by "Secondary Slot Available" availability', async () => {
|
|
// TODO: Implement test
|
|
// Scenario: Filter by Secondary Slot Available
|
|
// Given: A sponsor exists with ID "sponsor-123"
|
|
// And: There are 5 leagues (3 with main slot available, 2 with secondary slots available)
|
|
// When: FilterLeaguesUseCase.execute() is called with availability "Secondary Slot Available"
|
|
// Then: The result should contain only 2 leagues with secondary slots available
|
|
// And: EventPublisher should emit LeaguesFilteredEvent
|
|
});
|
|
|
|
it('should return empty result when no leagues match filter', async () => {
|
|
// TODO: Implement test
|
|
// Scenario: Filter with no matches
|
|
// Given: A sponsor exists with ID "sponsor-123"
|
|
// And: There are 2 leagues with main slot available
|
|
// When: FilterLeaguesUseCase.execute() is called with availability "Secondary Slot Available"
|
|
// Then: The result should be empty
|
|
// And: EventPublisher should emit LeaguesFilteredEvent
|
|
});
|
|
});
|
|
|
|
describe('FilterLeaguesUseCase - 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: FilterLeaguesUseCase.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 availability', async () => {
|
|
// TODO: Implement test
|
|
// Scenario: Invalid availability
|
|
// Given: A sponsor exists with ID "sponsor-123"
|
|
// When: FilterLeaguesUseCase.execute() is called with invalid availability
|
|
// Then: Should throw ValidationError
|
|
// And: EventPublisher should NOT emit any events
|
|
});
|
|
});
|
|
|
|
describe('SearchLeaguesUseCase - Success Path', () => {
|
|
it('should search leagues by league name', async () => {
|
|
// TODO: Implement test
|
|
// Scenario: Search by league name
|
|
// Given: A sponsor exists with ID "sponsor-123"
|
|
// And: There are leagues named: "Premier League", "League A", "League B"
|
|
// When: SearchLeaguesUseCase.execute() is called with query "Premier League"
|
|
// Then: The result should contain only "Premier League"
|
|
// And: EventPublisher should emit LeaguesSearchedEvent
|
|
});
|
|
|
|
it('should search leagues by partial match', async () => {
|
|
// TODO: Implement test
|
|
// Scenario: Search by partial match
|
|
// Given: A sponsor exists with ID "sponsor-123"
|
|
// And: There are leagues named: "Premier League", "League A", "League B"
|
|
// When: SearchLeaguesUseCase.execute() is called with query "League"
|
|
// Then: The result should contain all three leagues
|
|
// And: EventPublisher should emit LeaguesSearchedEvent
|
|
});
|
|
|
|
it('should return empty result when no leagues match search', async () => {
|
|
// TODO: Implement test
|
|
// Scenario: Search with no matches
|
|
// Given: A sponsor exists with ID "sponsor-123"
|
|
// And: There are leagues named: "League A", "League B"
|
|
// When: SearchLeaguesUseCase.execute() is called with query "NonExistent"
|
|
// Then: The result should be empty
|
|
// And: EventPublisher should emit LeaguesSearchedEvent
|
|
});
|
|
|
|
it('should return all leagues when search query is empty', async () => {
|
|
// TODO: Implement test
|
|
// Scenario: Search with empty query
|
|
// Given: A sponsor exists with ID "sponsor-123"
|
|
// And: There are 3 leagues available
|
|
// When: SearchLeaguesUseCase.execute() is called with empty query
|
|
// Then: The result should contain all 3 leagues
|
|
// And: EventPublisher should emit LeaguesSearchedEvent
|
|
});
|
|
});
|
|
|
|
describe('SearchLeaguesUseCase - 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: SearchLeaguesUseCase.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: SearchLeaguesUseCase.execute() is called with invalid query (e.g., null, undefined)
|
|
// Then: Should throw ValidationError
|
|
// And: EventPublisher should NOT emit any events
|
|
});
|
|
});
|
|
|
|
describe('League Data Orchestration', () => {
|
|
it('should correctly aggregate league statistics', async () => {
|
|
// TODO: Implement test
|
|
// Scenario: League statistics aggregation
|
|
// Given: A sponsor exists with ID "sponsor-123"
|
|
// And: There are 5 leagues with different slot availability
|
|
// And: There are 3 main sponsor slots available
|
|
// And: There are 15 secondary sponsor slots available
|
|
// And: There are 500 total drivers
|
|
// And: Average CPM is $50
|
|
// When: GetLeagueStatisticsUseCase.execute() is called
|
|
// Then: Total leagues should be 5
|
|
// And: Main sponsor slots available should be 3
|
|
// And: Secondary sponsor slots available should be 15
|
|
// And: Total drivers count should be 500
|
|
// And: Average CPM should be $50
|
|
// And: EventPublisher should emit LeagueStatisticsAccessedEvent
|
|
});
|
|
|
|
it('should correctly filter leagues by availability', async () => {
|
|
// TODO: Implement test
|
|
// Scenario: League availability filtering
|
|
// Given: A sponsor exists with ID "sponsor-123"
|
|
// And: There are leagues with different slot availability
|
|
// When: FilterLeaguesUseCase.execute() is called with "Main Slot Available"
|
|
// Then: Only leagues with main slot available should be returned
|
|
// And: Each league should have correct availability
|
|
// And: EventPublisher should emit LeaguesFilteredEvent
|
|
});
|
|
|
|
it('should correctly search leagues by name', async () => {
|
|
// TODO: Implement test
|
|
// Scenario: League name search
|
|
// Given: A sponsor exists with ID "sponsor-123"
|
|
// And: There are leagues with different names
|
|
// When: SearchLeaguesUseCase.execute() is called with league name
|
|
// Then: Only leagues with matching names should be returned
|
|
// And: Each league should have correct name
|
|
// And: EventPublisher should emit LeaguesSearchedEvent
|
|
});
|
|
});
|
|
});
|