340 lines
13 KiB
TypeScript
340 lines
13 KiB
TypeScript
/**
|
|
* Integration Test: Sponsor League Detail Use Case Orchestration
|
|
*
|
|
* Tests the orchestration logic of sponsor league detail-related Use Cases:
|
|
* - GetEntitySponsorshipPricingUseCase: Retrieves sponsorship pricing for leagues
|
|
* - 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 { InMemorySponsorshipPricingRepository } from '../../../adapters/racing/persistence/inmemory/InMemorySponsorshipPricingRepository';
|
|
import { GetEntitySponsorshipPricingUseCase } from '../../../core/racing/application/use-cases/GetEntitySponsorshipPricingUseCase';
|
|
import { Logger } from '../../../core/shared/domain/Logger';
|
|
|
|
describe('Sponsor League Detail Use Case Orchestration', () => {
|
|
let sponsorshipPricingRepository: InMemorySponsorshipPricingRepository;
|
|
let getEntitySponsorshipPricingUseCase: GetEntitySponsorshipPricingUseCase;
|
|
let mockLogger: Logger;
|
|
|
|
beforeAll(() => {
|
|
mockLogger = {
|
|
info: () => {},
|
|
debug: () => {},
|
|
warn: () => {},
|
|
error: () => {},
|
|
} as unknown as Logger;
|
|
|
|
sponsorshipPricingRepository = new InMemorySponsorshipPricingRepository(mockLogger);
|
|
getEntitySponsorshipPricingUseCase = new GetEntitySponsorshipPricingUseCase(
|
|
sponsorshipPricingRepository,
|
|
mockLogger,
|
|
);
|
|
});
|
|
|
|
beforeEach(() => {
|
|
sponsorshipPricingRepository.clear();
|
|
});
|
|
|
|
describe('GetEntitySponsorshipPricingUseCase - Success Path', () => {
|
|
it('should retrieve sponsorship pricing for a league', async () => {
|
|
// Given: A league exists with ID "league-123"
|
|
const leagueId = 'league-123';
|
|
|
|
// And: The league has sponsorship pricing configured
|
|
const pricing = {
|
|
entityType: 'league' as const,
|
|
entityId: leagueId,
|
|
acceptingApplications: true,
|
|
mainSlot: {
|
|
price: { amount: 10000, currency: 'USD' },
|
|
benefits: ['Primary logo placement', 'League page header banner'],
|
|
},
|
|
secondarySlots: {
|
|
price: { amount: 2000, currency: 'USD' },
|
|
benefits: ['Secondary logo on liveries', 'League page sidebar placement'],
|
|
},
|
|
};
|
|
await sponsorshipPricingRepository.create(pricing);
|
|
|
|
// When: GetEntitySponsorshipPricingUseCase.execute() is called
|
|
const result = await getEntitySponsorshipPricingUseCase.execute({
|
|
entityType: 'league',
|
|
entityId: leagueId,
|
|
});
|
|
|
|
// Then: The result should contain sponsorship pricing
|
|
expect(result.isOk()).toBe(true);
|
|
const pricingResult = result.unwrap();
|
|
|
|
// And: The entity type should be correct
|
|
expect(pricingResult.entityType).toBe('league');
|
|
|
|
// And: The entity ID should be correct
|
|
expect(pricingResult.entityId).toBe(leagueId);
|
|
|
|
// And: The league should be accepting applications
|
|
expect(pricingResult.acceptingApplications).toBe(true);
|
|
|
|
// And: The tiers should contain main slot
|
|
expect(pricingResult.tiers).toHaveLength(2);
|
|
expect(pricingResult.tiers[0].name).toBe('main');
|
|
expect(pricingResult.tiers[0].price.amount).toBe(10000);
|
|
expect(pricingResult.tiers[0].price.currency).toBe('USD');
|
|
expect(pricingResult.tiers[0].benefits).toContain('Primary logo placement');
|
|
|
|
// And: The tiers should contain secondary slot
|
|
expect(pricingResult.tiers[1].name).toBe('secondary');
|
|
expect(pricingResult.tiers[1].price.amount).toBe(2000);
|
|
expect(pricingResult.tiers[1].price.currency).toBe('USD');
|
|
expect(pricingResult.tiers[1].benefits).toContain('Secondary logo on liveries');
|
|
});
|
|
|
|
it('should retrieve sponsorship pricing with only main slot', async () => {
|
|
// Given: A league exists with ID "league-123"
|
|
const leagueId = 'league-123';
|
|
|
|
// And: The league has sponsorship pricing configured with only main slot
|
|
const pricing = {
|
|
entityType: 'league' as const,
|
|
entityId: leagueId,
|
|
acceptingApplications: true,
|
|
mainSlot: {
|
|
price: { amount: 10000, currency: 'USD' },
|
|
benefits: ['Primary logo placement', 'League page header banner'],
|
|
},
|
|
};
|
|
await sponsorshipPricingRepository.create(pricing);
|
|
|
|
// When: GetEntitySponsorshipPricingUseCase.execute() is called
|
|
const result = await getEntitySponsorshipPricingUseCase.execute({
|
|
entityType: 'league',
|
|
entityId: leagueId,
|
|
});
|
|
|
|
// Then: The result should contain sponsorship pricing
|
|
expect(result.isOk()).toBe(true);
|
|
const pricingResult = result.unwrap();
|
|
|
|
// And: The tiers should contain only main slot
|
|
expect(pricingResult.tiers).toHaveLength(1);
|
|
expect(pricingResult.tiers[0].name).toBe('main');
|
|
expect(pricingResult.tiers[0].price.amount).toBe(10000);
|
|
});
|
|
|
|
it('should retrieve sponsorship pricing with custom requirements', async () => {
|
|
// Given: A league exists with ID "league-123"
|
|
const leagueId = 'league-123';
|
|
|
|
// And: The league has sponsorship pricing configured with custom requirements
|
|
const pricing = {
|
|
entityType: 'league' as const,
|
|
entityId: leagueId,
|
|
acceptingApplications: true,
|
|
customRequirements: 'Must have racing experience',
|
|
mainSlot: {
|
|
price: { amount: 10000, currency: 'USD' },
|
|
benefits: ['Primary logo placement'],
|
|
},
|
|
};
|
|
await sponsorshipPricingRepository.create(pricing);
|
|
|
|
// When: GetEntitySponsorshipPricingUseCase.execute() is called
|
|
const result = await getEntitySponsorshipPricingUseCase.execute({
|
|
entityType: 'league',
|
|
entityId: leagueId,
|
|
});
|
|
|
|
// Then: The result should contain sponsorship pricing
|
|
expect(result.isOk()).toBe(true);
|
|
const pricingResult = result.unwrap();
|
|
|
|
// And: The custom requirements should be included
|
|
expect(pricingResult.customRequirements).toBe('Must have racing experience');
|
|
});
|
|
|
|
it('should retrieve sponsorship pricing with not accepting applications', async () => {
|
|
// Given: A league exists with ID "league-123"
|
|
const leagueId = 'league-123';
|
|
|
|
// And: The league has sponsorship pricing configured but not accepting applications
|
|
const pricing = {
|
|
entityType: 'league' as const,
|
|
entityId: leagueId,
|
|
acceptingApplications: false,
|
|
mainSlot: {
|
|
price: { amount: 10000, currency: 'USD' },
|
|
benefits: ['Primary logo placement'],
|
|
},
|
|
};
|
|
await sponsorshipPricingRepository.create(pricing);
|
|
|
|
// When: GetEntitySponsorshipPricingUseCase.execute() is called
|
|
const result = await getEntitySponsorshipPricingUseCase.execute({
|
|
entityType: 'league',
|
|
entityId: leagueId,
|
|
});
|
|
|
|
// Then: The result should contain sponsorship pricing
|
|
expect(result.isOk()).toBe(true);
|
|
const pricingResult = result.unwrap();
|
|
|
|
// And: The league should not be accepting applications
|
|
expect(pricingResult.acceptingApplications).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe('GetEntitySponsorshipPricingUseCase - Error Handling', () => {
|
|
it('should return error when pricing is not configured', async () => {
|
|
// Given: A league exists with ID "league-123"
|
|
const leagueId = 'league-123';
|
|
|
|
// And: The league has no sponsorship pricing configured
|
|
// When: GetEntitySponsorshipPricingUseCase.execute() is called
|
|
const result = await getEntitySponsorshipPricingUseCase.execute({
|
|
entityType: 'league',
|
|
entityId: leagueId,
|
|
});
|
|
|
|
// Then: Should return an error
|
|
expect(result.isErr()).toBe(true);
|
|
const error = result.unwrapErr();
|
|
expect(error.code).toBe('PRICING_NOT_CONFIGURED');
|
|
});
|
|
});
|
|
|
|
describe('Sponsor League Detail Data Orchestration', () => {
|
|
it('should correctly retrieve sponsorship pricing with all tiers', async () => {
|
|
// Given: A league exists with ID "league-123"
|
|
const leagueId = 'league-123';
|
|
|
|
// And: The league has sponsorship pricing configured with both main and secondary slots
|
|
const pricing = {
|
|
entityType: 'league' as const,
|
|
entityId: leagueId,
|
|
acceptingApplications: true,
|
|
customRequirements: 'Must have racing experience',
|
|
mainSlot: {
|
|
price: { amount: 10000, currency: 'USD' },
|
|
benefits: [
|
|
'Primary logo placement on all liveries',
|
|
'League page header banner',
|
|
'Race results page branding',
|
|
'Social media feature posts',
|
|
'Newsletter sponsor spot',
|
|
],
|
|
},
|
|
secondarySlots: {
|
|
price: { amount: 2000, currency: 'USD' },
|
|
benefits: [
|
|
'Secondary logo on liveries',
|
|
'League page sidebar placement',
|
|
'Race results mention',
|
|
'Social media mentions',
|
|
],
|
|
},
|
|
};
|
|
await sponsorshipPricingRepository.create(pricing);
|
|
|
|
// When: GetEntitySponsorshipPricingUseCase.execute() is called
|
|
const result = await getEntitySponsorshipPricingUseCase.execute({
|
|
entityType: 'league',
|
|
entityId: leagueId,
|
|
});
|
|
|
|
// Then: The sponsorship pricing should be correctly retrieved
|
|
expect(result.isOk()).toBe(true);
|
|
const pricingResult = result.unwrap();
|
|
|
|
// And: The entity type should be correct
|
|
expect(pricingResult.entityType).toBe('league');
|
|
|
|
// And: The entity ID should be correct
|
|
expect(pricingResult.entityId).toBe(leagueId);
|
|
|
|
// And: The league should be accepting applications
|
|
expect(pricingResult.acceptingApplications).toBe(true);
|
|
|
|
// And: The custom requirements should be included
|
|
expect(pricingResult.customRequirements).toBe('Must have racing experience');
|
|
|
|
// And: The tiers should contain both main and secondary slots
|
|
expect(pricingResult.tiers).toHaveLength(2);
|
|
|
|
// And: The main slot should have correct price and benefits
|
|
expect(pricingResult.tiers[0].name).toBe('main');
|
|
expect(pricingResult.tiers[0].price.amount).toBe(10000);
|
|
expect(pricingResult.tiers[0].price.currency).toBe('USD');
|
|
expect(pricingResult.tiers[0].benefits).toHaveLength(5);
|
|
expect(pricingResult.tiers[0].benefits).toContain('Primary logo placement on all liveries');
|
|
|
|
// And: The secondary slot should have correct price and benefits
|
|
expect(pricingResult.tiers[1].name).toBe('secondary');
|
|
expect(pricingResult.tiers[1].price.amount).toBe(2000);
|
|
expect(pricingResult.tiers[1].price.currency).toBe('USD');
|
|
expect(pricingResult.tiers[1].benefits).toHaveLength(4);
|
|
expect(pricingResult.tiers[1].benefits).toContain('Secondary logo on liveries');
|
|
});
|
|
|
|
it('should correctly retrieve sponsorship pricing for different entity types', async () => {
|
|
// Given: A league exists with ID "league-123"
|
|
const leagueId = 'league-123';
|
|
|
|
// And: The league has sponsorship pricing configured
|
|
const leaguePricing = {
|
|
entityType: 'league' as const,
|
|
entityId: leagueId,
|
|
acceptingApplications: true,
|
|
mainSlot: {
|
|
price: { amount: 10000, currency: 'USD' },
|
|
benefits: ['Primary logo placement'],
|
|
},
|
|
};
|
|
await sponsorshipPricingRepository.create(leaguePricing);
|
|
|
|
// And: A team exists with ID "team-456"
|
|
const teamId = 'team-456';
|
|
|
|
// And: The team has sponsorship pricing configured
|
|
const teamPricing = {
|
|
entityType: 'team' as const,
|
|
entityId: teamId,
|
|
acceptingApplications: true,
|
|
mainSlot: {
|
|
price: { amount: 5000, currency: 'USD' },
|
|
benefits: ['Team logo placement'],
|
|
},
|
|
};
|
|
await sponsorshipPricingRepository.create(teamPricing);
|
|
|
|
// When: GetEntitySponsorshipPricingUseCase.execute() is called for league
|
|
const leagueResult = await getEntitySponsorshipPricingUseCase.execute({
|
|
entityType: 'league',
|
|
entityId: leagueId,
|
|
});
|
|
|
|
// Then: The league pricing should be retrieved
|
|
expect(leagueResult.isOk()).toBe(true);
|
|
const leaguePricingResult = leagueResult.unwrap();
|
|
expect(leaguePricingResult.entityType).toBe('league');
|
|
expect(leaguePricingResult.entityId).toBe(leagueId);
|
|
expect(leaguePricingResult.tiers[0].price.amount).toBe(10000);
|
|
|
|
// When: GetEntitySponsorshipPricingUseCase.execute() is called for team
|
|
const teamResult = await getEntitySponsorshipPricingUseCase.execute({
|
|
entityType: 'team',
|
|
entityId: teamId,
|
|
});
|
|
|
|
// Then: The team pricing should be retrieved
|
|
expect(teamResult.isOk()).toBe(true);
|
|
const teamPricingResult = teamResult.unwrap();
|
|
expect(teamPricingResult.entityType).toBe('team');
|
|
expect(teamPricingResult.entityId).toBe(teamId);
|
|
expect(teamPricingResult.tiers[0].price.amount).toBe(5000);
|
|
});
|
|
});
|
|
});
|