import { describe, it, expect, beforeEach, vi, Mock } from 'vitest'; import { CreateLeagueWithSeasonAndScoringUseCase, type CreateLeagueWithSeasonAndScoringCommand, type CreateLeagueWithSeasonAndScoringResult, } from './CreateLeagueWithSeasonAndScoringUseCase'; import type { ILeagueRepository } from '../../domain/repositories/ILeagueRepository'; import type { ISeasonRepository } from '../../domain/repositories/ISeasonRepository'; import type { ILeagueScoringConfigRepository } from '../../domain/repositories/ILeagueScoringConfigRepository'; import type { Logger, UseCaseOutputPort } from '@core/shared/application'; 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; }; let output: { present: Mock } & UseCaseOutputPort; 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(), }; output = { present: vi.fn() } as unknown as typeof output; useCase = new CreateLeagueWithSeasonAndScoringUseCase( leagueRepository as unknown as ILeagueRepository, seasonRepository as unknown as ISeasonRepository, leagueScoringConfigRepository as unknown as ILeagueScoringConfigRepository, getLeagueScoringPresetById, logger as unknown as Logger, output, ); }); 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); expect(result.unwrap()).toBeUndefined(); expect(output.present).toHaveBeenCalledTimes(1); const presented = (output.present as Mock).mock.calls[0]?.[0] as unknown as CreateLeagueWithSeasonAndScoringResult; expect(presented?.league.id.toString()).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'); } expect(output.present).not.toHaveBeenCalled(); }); 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'); } expect(output.present).not.toHaveBeenCalled(); }); 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); expect(result.unwrapErr().code).toBe('VALIDATION_ERROR'); expect((result.unwrapErr() as any).details.message).toBe('gameId is required'); expect(output.present).not.toHaveBeenCalled(); }); it('should return error when visibility is missing', async () => { const command: Partial = { 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'); } expect(output.present).not.toHaveBeenCalled(); }); 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'); } expect(output.present).not.toHaveBeenCalled(); }); 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'); } expect(output.present).not.toHaveBeenCalled(); }); 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'); } expect(output.present).not.toHaveBeenCalled(); }); 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'); } expect(output.present).not.toHaveBeenCalled(); }); });