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

This commit is contained in:
2026-01-22 17:29:06 +01:00
parent f61ebda9b7
commit 597bb48248
68 changed files with 11832 additions and 3498 deletions

View File

@@ -0,0 +1,33 @@
export interface LeagueCreateCommand {
name: string;
description?: string;
visibility: 'public' | 'private';
ownerId: string;
// Structure
maxDrivers?: number;
approvalRequired: boolean;
lateJoinAllowed: boolean;
// Schedule
raceFrequency?: string;
raceDay?: string;
raceTime?: string;
tracks?: string[];
// Scoring
scoringSystem?: any;
bonusPointsEnabled: boolean;
penaltiesEnabled: boolean;
// Stewarding
protestsEnabled: boolean;
appealsEnabled: boolean;
stewardTeam?: string[];
// Tags
gameType?: string;
skillLevel?: string;
category?: string;
tags?: string[];
}

View File

@@ -0,0 +1,40 @@
export interface LeagueCreatedEvent {
type: 'LeagueCreatedEvent';
leagueId: string;
ownerId: string;
timestamp: Date;
}
export interface LeagueUpdatedEvent {
type: 'LeagueUpdatedEvent';
leagueId: string;
updates: Partial<any>;
timestamp: Date;
}
export interface LeagueDeletedEvent {
type: 'LeagueDeletedEvent';
leagueId: string;
timestamp: Date;
}
export interface LeagueAccessedEvent {
type: 'LeagueAccessedEvent';
leagueId: string;
driverId: string;
timestamp: Date;
}
export interface LeagueEventPublisher {
emitLeagueCreated(event: LeagueCreatedEvent): Promise<void>;
emitLeagueUpdated(event: LeagueUpdatedEvent): Promise<void>;
emitLeagueDeleted(event: LeagueDeletedEvent): Promise<void>;
emitLeagueAccessed(event: LeagueAccessedEvent): Promise<void>;
getLeagueCreatedEventCount(): number;
getLeagueUpdatedEventCount(): number;
getLeagueDeletedEventCount(): number;
getLeagueAccessedEventCount(): number;
clear(): void;
}

View File

@@ -0,0 +1,169 @@
export interface LeagueData {
id: string;
name: string;
description: string | null;
visibility: 'public' | 'private';
ownerId: string;
status: 'active' | 'pending' | 'archived';
createdAt: Date;
updatedAt: Date;
// Structure
maxDrivers: number | null;
approvalRequired: boolean;
lateJoinAllowed: boolean;
// Schedule
raceFrequency: string | null;
raceDay: string | null;
raceTime: string | null;
tracks: string[] | null;
// Scoring
scoringSystem: any | null;
bonusPointsEnabled: boolean;
penaltiesEnabled: boolean;
// Stewarding
protestsEnabled: boolean;
appealsEnabled: boolean;
stewardTeam: string[] | null;
// Tags
gameType: string | null;
skillLevel: string | null;
category: string | null;
tags: string[] | null;
}
export interface LeagueStats {
leagueId: string;
memberCount: number;
raceCount: number;
sponsorCount: number;
prizePool: number;
rating: number;
reviewCount: number;
}
export interface LeagueFinancials {
leagueId: string;
walletBalance: number;
totalRevenue: number;
totalFees: number;
pendingPayouts: number;
netBalance: number;
}
export interface LeagueStewardingMetrics {
leagueId: string;
averageResolutionTime: number;
averageProtestResolutionTime: number;
averagePenaltyAppealSuccessRate: number;
averageProtestSuccessRate: number;
averageStewardingActionSuccessRate: number;
}
export interface LeaguePerformanceMetrics {
leagueId: string;
averageLapTime: number;
averageFieldSize: number;
averageIncidentCount: number;
averagePenaltyCount: number;
averageProtestCount: number;
averageStewardingActionCount: number;
}
export interface LeagueRatingMetrics {
leagueId: string;
overallRating: number;
ratingTrend: number;
rankTrend: number;
pointsTrend: number;
winRateTrend: number;
podiumRateTrend: number;
dnfRateTrend: number;
}
export interface LeagueTrendMetrics {
leagueId: string;
incidentRateTrend: number;
penaltyRateTrend: number;
protestRateTrend: number;
stewardingActionRateTrend: number;
stewardingTimeTrend: number;
protestResolutionTimeTrend: number;
}
export interface LeagueSuccessRateMetrics {
leagueId: string;
penaltyAppealSuccessRate: number;
protestSuccessRate: number;
stewardingActionSuccessRate: number;
stewardingActionAppealSuccessRate: number;
stewardingActionPenaltySuccessRate: number;
stewardingActionProtestSuccessRate: number;
}
export interface LeagueResolutionTimeMetrics {
leagueId: string;
averageStewardingTime: number;
averageProtestResolutionTime: number;
averageStewardingActionAppealPenaltyProtestResolutionTime: number;
}
export interface LeagueComplexSuccessRateMetrics {
leagueId: string;
stewardingActionAppealPenaltyProtestSuccessRate: number;
stewardingActionAppealProtestSuccessRate: number;
stewardingActionPenaltyProtestSuccessRate: number;
stewardingActionAppealPenaltyProtestSuccessRate2: number;
}
export interface LeagueComplexResolutionTimeMetrics {
leagueId: string;
stewardingActionAppealPenaltyProtestResolutionTime: number;
stewardingActionAppealProtestResolutionTime: number;
stewardingActionPenaltyProtestResolutionTime: number;
stewardingActionAppealPenaltyProtestResolutionTime2: number;
}
export interface LeagueRepository {
create(league: LeagueData): Promise<LeagueData>;
findById(id: string): Promise<LeagueData | null>;
findByName(name: string): Promise<LeagueData | null>;
findByOwner(ownerId: string): Promise<LeagueData[]>;
search(query: string): Promise<LeagueData[]>;
update(id: string, updates: Partial<LeagueData>): Promise<LeagueData>;
delete(id: string): Promise<void>;
getStats(leagueId: string): Promise<LeagueStats>;
updateStats(leagueId: string, stats: LeagueStats): Promise<LeagueStats>;
getFinancials(leagueId: string): Promise<LeagueFinancials>;
updateFinancials(leagueId: string, financials: LeagueFinancials): Promise<LeagueFinancials>;
getStewardingMetrics(leagueId: string): Promise<LeagueStewardingMetrics>;
updateStewardingMetrics(leagueId: string, metrics: LeagueStewardingMetrics): Promise<LeagueStewardingMetrics>;
getPerformanceMetrics(leagueId: string): Promise<LeaguePerformanceMetrics>;
updatePerformanceMetrics(leagueId: string, metrics: LeaguePerformanceMetrics): Promise<LeaguePerformanceMetrics>;
getRatingMetrics(leagueId: string): Promise<LeagueRatingMetrics>;
updateRatingMetrics(leagueId: string, metrics: LeagueRatingMetrics): Promise<LeagueRatingMetrics>;
getTrendMetrics(leagueId: string): Promise<LeagueTrendMetrics>;
updateTrendMetrics(leagueId: string, metrics: LeagueTrendMetrics): Promise<LeagueTrendMetrics>;
getSuccessRateMetrics(leagueId: string): Promise<LeagueSuccessRateMetrics>;
updateSuccessRateMetrics(leagueId: string, metrics: LeagueSuccessRateMetrics): Promise<LeagueSuccessRateMetrics>;
getResolutionTimeMetrics(leagueId: string): Promise<LeagueResolutionTimeMetrics>;
updateResolutionTimeMetrics(leagueId: string, metrics: LeagueResolutionTimeMetrics): Promise<LeagueResolutionTimeMetrics>;
getComplexSuccessRateMetrics(leagueId: string): Promise<LeagueComplexSuccessRateMetrics>;
updateComplexSuccessRateMetrics(leagueId: string, metrics: LeagueComplexSuccessRateMetrics): Promise<LeagueComplexSuccessRateMetrics>;
getComplexResolutionTimeMetrics(leagueId: string): Promise<LeagueComplexResolutionTimeMetrics>;
updateComplexResolutionTimeMetrics(leagueId: string, metrics: LeagueComplexResolutionTimeMetrics): Promise<LeagueComplexResolutionTimeMetrics>;
}

View File

@@ -0,0 +1,183 @@
import { LeagueRepository, LeagueData } from '../ports/LeagueRepository';
import { LeagueEventPublisher, LeagueCreatedEvent } from '../ports/LeagueEventPublisher';
import { LeagueCreateCommand } from '../ports/LeagueCreateCommand';
export class CreateLeagueUseCase {
constructor(
private readonly leagueRepository: LeagueRepository,
private readonly eventPublisher: LeagueEventPublisher,
) {}
async execute(command: LeagueCreateCommand): Promise<LeagueData> {
// Validate command
if (!command.name || command.name.trim() === '') {
throw new Error('League name is required');
}
if (!command.ownerId || command.ownerId.trim() === '') {
throw new Error('Owner ID is required');
}
if (command.maxDrivers !== undefined && command.maxDrivers < 1) {
throw new Error('Max drivers must be at least 1');
}
// Create league data
const leagueId = `league-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
const now = new Date();
const leagueData: LeagueData = {
id: leagueId,
name: command.name,
description: command.description || null,
visibility: command.visibility,
ownerId: command.ownerId,
status: 'active',
createdAt: now,
updatedAt: now,
maxDrivers: command.maxDrivers || null,
approvalRequired: command.approvalRequired,
lateJoinAllowed: command.lateJoinAllowed,
raceFrequency: command.raceFrequency || null,
raceDay: command.raceDay || null,
raceTime: command.raceTime || null,
tracks: command.tracks || null,
scoringSystem: command.scoringSystem || null,
bonusPointsEnabled: command.bonusPointsEnabled,
penaltiesEnabled: command.penaltiesEnabled,
protestsEnabled: command.protestsEnabled,
appealsEnabled: command.appealsEnabled,
stewardTeam: command.stewardTeam || null,
gameType: command.gameType || null,
skillLevel: command.skillLevel || null,
category: command.category || null,
tags: command.tags || null,
};
// Save league to repository
const savedLeague = await this.leagueRepository.create(leagueData);
// Initialize league stats
const defaultStats = {
leagueId,
memberCount: 1,
raceCount: 0,
sponsorCount: 0,
prizePool: 0,
rating: 0,
reviewCount: 0,
};
await this.leagueRepository.updateStats(leagueId, defaultStats);
// Initialize league financials
const defaultFinancials = {
leagueId,
walletBalance: 0,
totalRevenue: 0,
totalFees: 0,
pendingPayouts: 0,
netBalance: 0,
};
await this.leagueRepository.updateFinancials(leagueId, defaultFinancials);
// Initialize stewarding metrics
const defaultStewardingMetrics = {
leagueId,
averageResolutionTime: 0,
averageProtestResolutionTime: 0,
averagePenaltyAppealSuccessRate: 0,
averageProtestSuccessRate: 0,
averageStewardingActionSuccessRate: 0,
};
await this.leagueRepository.updateStewardingMetrics(leagueId, defaultStewardingMetrics);
// Initialize performance metrics
const defaultPerformanceMetrics = {
leagueId,
averageLapTime: 0,
averageFieldSize: 0,
averageIncidentCount: 0,
averagePenaltyCount: 0,
averageProtestCount: 0,
averageStewardingActionCount: 0,
};
await this.leagueRepository.updatePerformanceMetrics(leagueId, defaultPerformanceMetrics);
// Initialize rating metrics
const defaultRatingMetrics = {
leagueId,
overallRating: 0,
ratingTrend: 0,
rankTrend: 0,
pointsTrend: 0,
winRateTrend: 0,
podiumRateTrend: 0,
dnfRateTrend: 0,
};
await this.leagueRepository.updateRatingMetrics(leagueId, defaultRatingMetrics);
// Initialize trend metrics
const defaultTrendMetrics = {
leagueId,
incidentRateTrend: 0,
penaltyRateTrend: 0,
protestRateTrend: 0,
stewardingActionRateTrend: 0,
stewardingTimeTrend: 0,
protestResolutionTimeTrend: 0,
};
await this.leagueRepository.updateTrendMetrics(leagueId, defaultTrendMetrics);
// Initialize success rate metrics
const defaultSuccessRateMetrics = {
leagueId,
penaltyAppealSuccessRate: 0,
protestSuccessRate: 0,
stewardingActionSuccessRate: 0,
stewardingActionAppealSuccessRate: 0,
stewardingActionPenaltySuccessRate: 0,
stewardingActionProtestSuccessRate: 0,
};
await this.leagueRepository.updateSuccessRateMetrics(leagueId, defaultSuccessRateMetrics);
// Initialize resolution time metrics
const defaultResolutionTimeMetrics = {
leagueId,
averageStewardingTime: 0,
averageProtestResolutionTime: 0,
averageStewardingActionAppealPenaltyProtestResolutionTime: 0,
};
await this.leagueRepository.updateResolutionTimeMetrics(leagueId, defaultResolutionTimeMetrics);
// Initialize complex success rate metrics
const defaultComplexSuccessRateMetrics = {
leagueId,
stewardingActionAppealPenaltyProtestSuccessRate: 0,
stewardingActionAppealProtestSuccessRate: 0,
stewardingActionPenaltyProtestSuccessRate: 0,
stewardingActionAppealPenaltyProtestSuccessRate2: 0,
};
await this.leagueRepository.updateComplexSuccessRateMetrics(leagueId, defaultComplexSuccessRateMetrics);
// Initialize complex resolution time metrics
const defaultComplexResolutionTimeMetrics = {
leagueId,
stewardingActionAppealPenaltyProtestResolutionTime: 0,
stewardingActionAppealProtestResolutionTime: 0,
stewardingActionPenaltyProtestResolutionTime: 0,
stewardingActionAppealPenaltyProtestResolutionTime2: 0,
};
await this.leagueRepository.updateComplexResolutionTimeMetrics(leagueId, defaultComplexResolutionTimeMetrics);
// Emit event
const event: LeagueCreatedEvent = {
type: 'LeagueCreatedEvent',
leagueId,
ownerId: command.ownerId,
timestamp: now,
};
await this.eventPublisher.emitLeagueCreated(event);
return savedLeague;
}
}

View File

@@ -0,0 +1,40 @@
import { LeagueRepository, LeagueData } from '../ports/LeagueRepository';
import { LeagueEventPublisher, LeagueAccessedEvent } from '../ports/LeagueEventPublisher';
export interface GetLeagueQuery {
leagueId: string;
driverId?: string;
}
export class GetLeagueUseCase {
constructor(
private readonly leagueRepository: LeagueRepository,
private readonly eventPublisher: LeagueEventPublisher,
) {}
async execute(query: GetLeagueQuery): Promise<LeagueData> {
// Validate query
if (!query.leagueId || query.leagueId.trim() === '') {
throw new Error('League ID is required');
}
// Find league
const league = await this.leagueRepository.findById(query.leagueId);
if (!league) {
throw new Error(`League with id ${query.leagueId} not found`);
}
// Emit event if driver ID is provided
if (query.driverId) {
const event: LeagueAccessedEvent = {
type: 'LeagueAccessedEvent',
leagueId: query.leagueId,
driverId: query.driverId,
timestamp: new Date(),
};
await this.eventPublisher.emitLeagueAccessed(event);
}
return league;
}
}

View File

@@ -0,0 +1,27 @@
import { LeagueRepository, LeagueData } from '../ports/LeagueRepository';
export interface SearchLeaguesQuery {
query: string;
limit?: number;
offset?: number;
}
export class SearchLeaguesUseCase {
constructor(private readonly leagueRepository: LeagueRepository) {}
async execute(query: SearchLeaguesQuery): Promise<LeagueData[]> {
// Validate query
if (!query.query || query.query.trim() === '') {
throw new Error('Search query is required');
}
// Search leagues
const results = await this.leagueRepository.search(query.query);
// Apply limit and offset
const limit = query.limit || 10;
const offset = query.offset || 0;
return results.slice(offset, offset + limit);
}
}