290 lines
9.3 KiB
TypeScript
290 lines
9.3 KiB
TypeScript
import type { Logger } from '@core/shared/domain/Logger';
|
|
import { beforeEach, describe, expect, it, Mock, vi } from 'vitest';
|
|
import type { LeagueRepository } from '../../domain/repositories/LeagueRepository';
|
|
import type { LeagueScoringConfigRepository } from '../../domain/repositories/LeagueScoringConfigRepository';
|
|
import type { SeasonRepository } from '../../domain/repositories/SeasonRepository';
|
|
import {
|
|
CreateLeagueWithSeasonAndScoringUseCase,
|
|
type CreateLeagueWithSeasonAndScoringCommand
|
|
} from './CreateLeagueWithSeasonAndScoringUseCase';
|
|
|
|
describe('CreateLeagueWithSeasonAndScoringUseCase', () => {
|
|
let useCase: CreateLeagueWithSeasonAndScoringUseCase;
|
|
let leagueRepository: {
|
|
create: Mock;
|
|
};
|
|
let seasonRepository: {
|
|
create: Mock;
|
|
};
|
|
let leagueScoringConfigRepository: {
|
|
save: Mock;
|
|
};
|
|
let getLeagueScoringPresetById: Mock;
|
|
let logger: {
|
|
debug: Mock;
|
|
info: Mock;
|
|
warn: Mock;
|
|
error: Mock;
|
|
};
|
|
|
|
beforeEach(() => {
|
|
leagueRepository = {
|
|
create: vi.fn(),
|
|
};
|
|
seasonRepository = {
|
|
create: vi.fn(),
|
|
};
|
|
leagueScoringConfigRepository = {
|
|
save: vi.fn(),
|
|
};
|
|
getLeagueScoringPresetById = vi.fn();
|
|
logger = {
|
|
debug: vi.fn(),
|
|
info: vi.fn(),
|
|
warn: vi.fn(),
|
|
error: vi.fn(),
|
|
};
|
|
useCase = new CreateLeagueWithSeasonAndScoringUseCase(
|
|
leagueRepository as unknown as LeagueRepository,
|
|
seasonRepository as unknown as SeasonRepository,
|
|
leagueScoringConfigRepository as unknown as LeagueScoringConfigRepository,
|
|
getLeagueScoringPresetById,
|
|
logger as unknown as Logger
|
|
);
|
|
});
|
|
|
|
it('should create league, season, and scoring successfully', async () => {
|
|
const command = {
|
|
name: 'Test League',
|
|
description: 'A test league',
|
|
visibility: 'unranked' as const,
|
|
ownerId: 'owner-1',
|
|
gameId: 'game-1',
|
|
maxDrivers: 20,
|
|
maxTeams: 5,
|
|
enableDriverChampionship: true,
|
|
enableTeamChampionship: true,
|
|
enableNationsChampionship: false,
|
|
enableTrophyChampionship: false,
|
|
scoringPresetId: 'club-default',
|
|
};
|
|
|
|
const mockPreset = {
|
|
id: 'club-default',
|
|
name: 'Club Default',
|
|
};
|
|
|
|
getLeagueScoringPresetById.mockResolvedValue(mockPreset);
|
|
leagueRepository.create.mockResolvedValue(undefined);
|
|
seasonRepository.create.mockResolvedValue(undefined);
|
|
leagueScoringConfigRepository.save.mockResolvedValue(undefined);
|
|
|
|
const result = await useCase.execute(command);
|
|
|
|
expect(result.isOk()).toBe(true);
|
|
const presented = result.unwrap();
|
|
|
|
expect(presented.league.id).toBeDefined();
|
|
expect(presented.season.id).toBeDefined();
|
|
expect(presented.scoringConfig.seasonId.toString()).toBe(presented.season.id);
|
|
expect(leagueRepository.create).toHaveBeenCalledTimes(1);
|
|
expect(seasonRepository.create).toHaveBeenCalledTimes(1);
|
|
expect(leagueScoringConfigRepository.save).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
it('should return error when league name is empty', async () => {
|
|
const command = {
|
|
name: '',
|
|
description: 'Test description',
|
|
visibility: 'unranked' as const,
|
|
ownerId: 'owner-1',
|
|
gameId: 'game-1',
|
|
enableDriverChampionship: true,
|
|
enableTeamChampionship: true,
|
|
enableNationsChampionship: false,
|
|
enableTrophyChampionship: false,
|
|
};
|
|
|
|
const result = await useCase.execute(command);
|
|
|
|
expect(result.isErr()).toBe(true);
|
|
const err = result.unwrapErr();
|
|
expect(err.code).toBe('VALIDATION_ERROR');
|
|
if ('details' in err && err.details && typeof err.details === 'object' && 'message' in err.details) {
|
|
expect(err.details.message).toBe('League name is required');
|
|
}
|
|
});
|
|
|
|
it('should return error when ownerId is empty', async () => {
|
|
const command = {
|
|
name: 'Test League',
|
|
description: 'Test description',
|
|
visibility: 'unranked' as const,
|
|
ownerId: '',
|
|
gameId: 'game-1',
|
|
enableDriverChampionship: true,
|
|
enableTeamChampionship: true,
|
|
enableNationsChampionship: false,
|
|
enableTrophyChampionship: false,
|
|
};
|
|
|
|
const result = await useCase.execute(command as CreateLeagueWithSeasonAndScoringCommand);
|
|
|
|
expect(result.isErr()).toBe(true);
|
|
const err = result.unwrapErr();
|
|
expect(err.code).toBe('VALIDATION_ERROR');
|
|
if ('details' in err && err.details && typeof err.details === 'object' && 'message' in err.details) {
|
|
expect(err.details.message).toBe('League ownerId is required');
|
|
}
|
|
});
|
|
|
|
it('should return error when gameId is empty', async () => {
|
|
const command = {
|
|
name: 'Test League',
|
|
description: 'Test description',
|
|
visibility: 'unranked' as const,
|
|
ownerId: 'owner-1',
|
|
gameId: '',
|
|
enableDriverChampionship: true,
|
|
enableTeamChampionship: true,
|
|
enableNationsChampionship: false,
|
|
enableTrophyChampionship: false,
|
|
};
|
|
|
|
const result = await useCase.execute(command);
|
|
|
|
expect(result.isErr()).toBe(true);
|
|
const err = result.unwrapErr();
|
|
expect(err.code).toBe('VALIDATION_ERROR');
|
|
if ('details' in err && err.details && typeof err.details === 'object' && 'message' in err.details) {
|
|
expect(err.details.message).toBe('gameId is required');
|
|
}
|
|
});
|
|
|
|
it('should return error when visibility is missing', async () => {
|
|
const command: Partial<CreateLeagueWithSeasonAndScoringCommand> = {
|
|
name: 'Test League',
|
|
ownerId: 'owner-1',
|
|
gameId: 'game-1',
|
|
enableDriverChampionship: true,
|
|
enableTeamChampionship: true,
|
|
enableNationsChampionship: false,
|
|
enableTrophyChampionship: false,
|
|
};
|
|
|
|
const result = await useCase.execute(command as CreateLeagueWithSeasonAndScoringCommand);
|
|
|
|
expect(result.isErr()).toBe(true);
|
|
const err = result.unwrapErr();
|
|
expect(err.code).toBe('VALIDATION_ERROR');
|
|
if ('details' in err && err.details && typeof err.details === 'object' && 'message' in err.details) {
|
|
expect(err.details.message).toBe('visibility is required');
|
|
}
|
|
});
|
|
|
|
it('should return error when maxDrivers is invalid', async () => {
|
|
const command = {
|
|
name: 'Test League',
|
|
description: 'Test description',
|
|
visibility: 'unranked' as const,
|
|
ownerId: 'owner-1',
|
|
gameId: 'game-1',
|
|
maxDrivers: 0,
|
|
enableDriverChampionship: true,
|
|
enableTeamChampionship: true,
|
|
enableNationsChampionship: false,
|
|
enableTrophyChampionship: false,
|
|
};
|
|
|
|
const result = await useCase.execute(command);
|
|
|
|
expect(result.isErr()).toBe(true);
|
|
const err = result.unwrapErr();
|
|
expect(err.code).toBe('VALIDATION_ERROR');
|
|
if ('details' in err && err.details && typeof err.details === 'object' && 'message' in err.details) {
|
|
expect(err.details.message).toBe('maxDrivers must be greater than 0 when provided');
|
|
}
|
|
});
|
|
|
|
it('should return error when ranked league has insufficient drivers', async () => {
|
|
const command = {
|
|
name: 'Test League',
|
|
description: 'Test description',
|
|
visibility: 'ranked' as const,
|
|
ownerId: 'owner-1',
|
|
gameId: 'game-1',
|
|
maxDrivers: 5,
|
|
enableDriverChampionship: true,
|
|
enableTeamChampionship: true,
|
|
enableNationsChampionship: false,
|
|
enableTrophyChampionship: false,
|
|
};
|
|
|
|
const result = await useCase.execute(command);
|
|
|
|
expect(result.isErr()).toBe(true);
|
|
const err = result.unwrapErr();
|
|
expect(err.code).toBe('VALIDATION_ERROR');
|
|
if ('details' in err && err.details && typeof err.details === 'object' && 'message' in err.details) {
|
|
expect(err.details.message).toContain('Ranked leagues require at least 10 drivers');
|
|
}
|
|
});
|
|
|
|
it('should return error when scoring preset is unknown', async () => {
|
|
const command = {
|
|
name: 'Test League',
|
|
description: 'Test description',
|
|
visibility: 'unranked' as const,
|
|
ownerId: 'owner-1',
|
|
gameId: 'game-1',
|
|
enableDriverChampionship: true,
|
|
enableTeamChampionship: true,
|
|
enableNationsChampionship: false,
|
|
enableTrophyChampionship: false,
|
|
scoringPresetId: 'unknown-preset',
|
|
};
|
|
|
|
getLeagueScoringPresetById.mockResolvedValue(undefined);
|
|
|
|
const result = await useCase.execute(command);
|
|
|
|
expect(result.isErr()).toBe(true);
|
|
const err = result.unwrapErr();
|
|
expect(err.code).toBe('UNKNOWN_PRESET');
|
|
if ('details' in err && err.details && typeof err.details === 'object' && 'message' in err.details) {
|
|
expect(err.details.message).toBe('Unknown scoring preset: unknown-preset');
|
|
}
|
|
});
|
|
|
|
it('should return error when repository throws', async () => {
|
|
const command = {
|
|
name: 'Test League',
|
|
description: 'Test description',
|
|
visibility: 'unranked' as const,
|
|
ownerId: 'owner-1',
|
|
gameId: 'game-1',
|
|
enableDriverChampionship: true,
|
|
enableTeamChampionship: true,
|
|
enableNationsChampionship: false,
|
|
enableTrophyChampionship: false,
|
|
};
|
|
|
|
const mockPreset = {
|
|
id: 'club-default',
|
|
name: 'Club Default',
|
|
};
|
|
|
|
getLeagueScoringPresetById.mockResolvedValue(mockPreset);
|
|
leagueRepository.create.mockRejectedValue(new Error('DB error'));
|
|
|
|
const result = await useCase.execute(command);
|
|
|
|
expect(result.isErr()).toBe(true);
|
|
const err = result.unwrapErr();
|
|
expect(err.code).toBe('REPOSITORY_ERROR');
|
|
if ('details' in err && err.details && typeof err.details === 'object' && 'message' in err.details) {
|
|
expect(err.details.message).toBe('DB error');
|
|
}
|
|
});
|
|
});
|