import { describe, it, expect, beforeEach, vi, Mock } from 'vitest'; import { ApplyPenaltyUseCase } from './ApplyPenaltyUseCase'; import type { IPenaltyRepository } from '../../domain/repositories/IPenaltyRepository'; import type { IProtestRepository } from '../../domain/repositories/IProtestRepository'; import type { IRaceRepository } from '../../domain/repositories/IRaceRepository'; import type { ILeagueMembershipRepository } from '../../domain/repositories/ILeagueMembershipRepository'; import type { Logger } from '@core/shared/application'; describe('ApplyPenaltyUseCase', () => { let mockPenaltyRepo: { create: Mock; }; let mockProtestRepo: { findById: Mock; }; let mockRaceRepo: { findById: Mock; }; let mockLeagueMembershipRepo: { getLeagueMembers: Mock; }; let mockLogger: { debug: Mock; warn: Mock; info: Mock; error: Mock; }; beforeEach(() => { mockPenaltyRepo = { create: vi.fn(), }; mockProtestRepo = { findById: vi.fn(), }; mockRaceRepo = { findById: vi.fn(), }; mockLeagueMembershipRepo = { getLeagueMembers: vi.fn(), }; mockLogger = { debug: vi.fn(), warn: vi.fn(), info: vi.fn(), error: vi.fn(), }; }); it('should return error when race does not exist', async () => { const output = { present: vi.fn(), }; const useCase = new ApplyPenaltyUseCase( mockPenaltyRepo as unknown as IPenaltyRepository, mockProtestRepo as unknown as IProtestRepository, mockRaceRepo as unknown as IRaceRepository, mockLeagueMembershipRepo as unknown as ILeagueMembershipRepository, mockLogger as unknown as Logger, output as any, ); mockRaceRepo.findById.mockResolvedValue(null); const result = await useCase.execute({ raceId: 'nonexistent', driverId: 'driver1', stewardId: 'steward1', type: 'time_penalty', value: 5, reason: 'Test penalty', }); expect(result.isOk()).toBe(false); expect(result.error!.code).toBe('RACE_NOT_FOUND'); }); it('should return error when steward does not have authority', async () => { const output = { present: vi.fn(), }; const useCase = new ApplyPenaltyUseCase( mockPenaltyRepo as unknown as IPenaltyRepository, mockProtestRepo as unknown as IProtestRepository, mockRaceRepo as unknown as IRaceRepository, mockLeagueMembershipRepo as unknown as ILeagueMembershipRepository, mockLogger as unknown as Logger, output as any, ); mockRaceRepo.findById.mockResolvedValue({ id: 'race1', leagueId: 'league1' }); mockLeagueMembershipRepo.getLeagueMembers.mockResolvedValue([ { driverId: 'steward1', role: 'member', status: 'active' }, ]); const result = await useCase.execute({ raceId: 'race1', driverId: 'driver1', stewardId: 'steward1', type: 'time_penalty', value: 5, reason: 'Test penalty', }); expect(result.isOk()).toBe(false); expect(result.error!.code).toBe('INSUFFICIENT_AUTHORITY'); }); it('should return error when protest does not exist', async () => { const output = { present: vi.fn(), }; const useCase = new ApplyPenaltyUseCase( mockPenaltyRepo as unknown as IPenaltyRepository, mockProtestRepo as unknown as IProtestRepository, mockRaceRepo as unknown as IRaceRepository, mockLeagueMembershipRepo as unknown as ILeagueMembershipRepository, mockLogger as unknown as Logger, output as any, ); mockRaceRepo.findById.mockResolvedValue({ id: 'race1', leagueId: 'league1' }); mockLeagueMembershipRepo.getLeagueMembers.mockResolvedValue([ { driverId: 'steward1', role: 'owner', status: 'active' }, ]); mockProtestRepo.findById.mockResolvedValue(null); const result = await useCase.execute({ raceId: 'race1', driverId: 'driver1', stewardId: 'steward1', type: 'time_penalty', value: 5, reason: 'Test penalty', protestId: 'protest1', }); expect(result.isOk()).toBe(false); expect(result.error!.code).toBe('PROTEST_NOT_FOUND'); }); it('should return error when protest is not upheld', async () => { const output = { present: vi.fn(), }; const useCase = new ApplyPenaltyUseCase( mockPenaltyRepo as unknown as IPenaltyRepository, mockProtestRepo as unknown as IProtestRepository, mockRaceRepo as unknown as IRaceRepository, mockLeagueMembershipRepo as unknown as ILeagueMembershipRepository, mockLogger as unknown as Logger, output as any, ); mockRaceRepo.findById.mockResolvedValue({ id: 'race1', leagueId: 'league1' }); mockLeagueMembershipRepo.getLeagueMembers.mockResolvedValue([ { driverId: 'steward1', role: 'owner', status: 'active' }, ]); mockProtestRepo.findById.mockResolvedValue({ id: 'protest1', status: 'pending', raceId: 'race1' }); const result = await useCase.execute({ raceId: 'race1', driverId: 'driver1', stewardId: 'steward1', type: 'time_penalty', value: 5, reason: 'Test penalty', protestId: 'protest1', }); expect(result.isOk()).toBe(false); expect(result.error!.code).toBe('PROTEST_NOT_UPHELD'); }); it('should return error when protest is not for this race', async () => { const output = { present: vi.fn(), }; const useCase = new ApplyPenaltyUseCase( mockPenaltyRepo as unknown as IPenaltyRepository, mockProtestRepo as unknown as IProtestRepository, mockRaceRepo as unknown as IRaceRepository, mockLeagueMembershipRepo as unknown as ILeagueMembershipRepository, mockLogger as unknown as Logger, output as any, ); mockRaceRepo.findById.mockResolvedValue({ id: 'race1', leagueId: 'league1' }); mockLeagueMembershipRepo.getLeagueMembers.mockResolvedValue([ { driverId: 'steward1', role: 'owner', status: 'active' }, ]); mockProtestRepo.findById.mockResolvedValue({ id: 'protest1', status: 'upheld', raceId: 'race2' }); const result = await useCase.execute({ raceId: 'race1', driverId: 'driver1', stewardId: 'steward1', type: 'time_penalty', value: 5, reason: 'Test penalty', protestId: 'protest1', }); expect(result.isOk()).toBe(false); expect(result.error!.code).toBe('PROTEST_NOT_FOR_RACE'); }); it('should create penalty and return result on success', async () => { const output = { present: vi.fn(), }; const useCase = new ApplyPenaltyUseCase( mockPenaltyRepo as unknown as IPenaltyRepository, mockProtestRepo as unknown as IProtestRepository, mockRaceRepo as unknown as IRaceRepository, mockLeagueMembershipRepo as unknown as ILeagueMembershipRepository, mockLogger as unknown as Logger, output as any, ); mockRaceRepo.findById.mockResolvedValue({ id: 'race1', leagueId: 'league1' }); mockLeagueMembershipRepo.getLeagueMembers.mockResolvedValue([ { driverId: 'steward1', role: 'admin', status: 'active' }, ]); mockPenaltyRepo.create.mockResolvedValue(undefined); const result = await useCase.execute({ raceId: 'race1', driverId: 'driver1', stewardId: 'steward1', type: 'time_penalty', value: 5, reason: 'Test penalty', notes: 'Test notes', }); expect(result.isOk()).toBe(true); expect(result.value).toEqual({ penaltyId: expect.any(String), }); expect(mockPenaltyRepo.create).toHaveBeenCalledWith( expect.objectContaining({ leagueId: 'league1', raceId: 'race1', driverId: 'driver1', type: 'time_penalty', value: 5, reason: 'Test penalty', issuedBy: 'steward1', status: 'pending', notes: 'Test notes', }) ); }); });