import { Penalty } from './Penalty'; import { RacingDomainValidationError, RacingDomainInvariantError } from '../errors/RacingDomainError'; describe('Penalty', () => { describe('create', () => { it('should create a penalty with required fields', () => { const penalty = Penalty.create({ id: 'penalty-1', leagueId: 'league-1', raceId: 'race-1', driverId: 'driver-1', type: 'time_penalty', value: 5, reason: 'Speeding', issuedBy: 'steward-1', }); expect(penalty.id).toBe('penalty-1'); expect(penalty.leagueId).toBe('league-1'); expect(penalty.raceId).toBe('race-1'); expect(penalty.driverId).toBe('driver-1'); expect(penalty.type).toBe('time_penalty'); expect(penalty.value).toBe(5); expect(penalty.reason).toBe('Speeding'); expect(penalty.issuedBy).toBe('steward-1'); expect(penalty.status).toBe('pending'); expect(penalty.appliedAt).toBeUndefined(); expect(penalty.notes).toBeUndefined(); }); it('should create a penalty with all fields', () => { const issuedAt = new Date('2023-01-01'); const appliedAt = new Date('2023-01-02'); const penalty = Penalty.create({ id: 'penalty-1', leagueId: 'league-1', raceId: 'race-1', driverId: 'driver-1', type: 'disqualification', reason: 'Unsafe driving', protestId: 'protest-1', issuedBy: 'steward-1', status: 'applied', issuedAt, appliedAt, notes: 'Applied after review', }); expect(penalty.id).toBe('penalty-1'); expect(penalty.type).toBe('disqualification'); expect(penalty.value).toBeUndefined(); expect(penalty.protestId).toBe('protest-1'); expect(penalty.status).toBe('applied'); expect(penalty.issuedAt).toEqual(issuedAt); expect(penalty.appliedAt).toEqual(appliedAt); expect(penalty.notes).toBe('Applied after review'); }); it('should throw error for invalid id', () => { expect(() => Penalty.create({ id: '', leagueId: 'league-1', raceId: 'race-1', driverId: 'driver-1', type: 'time_penalty', value: 5, reason: 'Speeding', issuedBy: 'steward-1', })).toThrow(RacingDomainValidationError); }); it('should throw error for invalid type', () => { expect(() => Penalty.create({ id: 'penalty-1', leagueId: 'league-1', raceId: 'race-1', driverId: 'driver-1', type: 'invalid_type', value: 5, reason: 'Speeding', issuedBy: 'steward-1', })).toThrow(RacingDomainValidationError); }); it('should throw error for missing value on time_penalty', () => { expect(() => Penalty.create({ id: 'penalty-1', leagueId: 'league-1', raceId: 'race-1', driverId: 'driver-1', type: 'time_penalty', reason: 'Speeding', issuedBy: 'steward-1', })).toThrow(RacingDomainValidationError); }); it('should throw error for zero value', () => { expect(() => Penalty.create({ id: 'penalty-1', leagueId: 'league-1', raceId: 'race-1', driverId: 'driver-1', type: 'time_penalty', value: 0, reason: 'Speeding', issuedBy: 'steward-1', })).toThrow(RacingDomainValidationError); }); }); describe('isPending', () => { it('should return true for pending status', () => { const penalty = Penalty.create({ id: 'penalty-1', leagueId: 'league-1', raceId: 'race-1', driverId: 'driver-1', type: 'warning', reason: 'Warning', issuedBy: 'steward-1', }); expect(penalty.isPending()).toBe(true); }); it('should return false for applied status', () => { const penalty = Penalty.create({ id: 'penalty-1', leagueId: 'league-1', raceId: 'race-1', driverId: 'driver-1', type: 'warning', reason: 'Warning', issuedBy: 'steward-1', status: 'applied', }); expect(penalty.isPending()).toBe(false); }); }); describe('markAsApplied', () => { it('should mark penalty as applied', () => { const penalty = Penalty.create({ id: 'penalty-1', leagueId: 'league-1', raceId: 'race-1', driverId: 'driver-1', type: 'warning', reason: 'Warning', issuedBy: 'steward-1', }); const applied = penalty.markAsApplied('Applied successfully'); expect(applied.status).toBe('applied'); expect(applied.appliedAt).toBeInstanceOf(Date); expect(applied.notes).toBe('Applied successfully'); }); it('should throw error if already applied', () => { const penalty = Penalty.create({ id: 'penalty-1', leagueId: 'league-1', raceId: 'race-1', driverId: 'driver-1', type: 'warning', reason: 'Warning', issuedBy: 'steward-1', status: 'applied', }); expect(() => penalty.markAsApplied()).toThrow(RacingDomainInvariantError); }); }); describe('overturn', () => { it('should overturn the penalty', () => { const penalty = Penalty.create({ id: 'penalty-1', leagueId: 'league-1', raceId: 'race-1', driverId: 'driver-1', type: 'warning', reason: 'Warning', issuedBy: 'steward-1', }); const overturned = penalty.overturn('Appealed successfully'); expect(overturned.status).toBe('overturned'); expect(overturned.notes).toBe('Appealed successfully'); }); it('should throw error if already overturned', () => { const penalty = Penalty.create({ id: 'penalty-1', leagueId: 'league-1', raceId: 'race-1', driverId: 'driver-1', type: 'warning', reason: 'Warning', issuedBy: 'steward-1', status: 'overturned', }); expect(() => penalty.overturn('Reason')).toThrow(RacingDomainInvariantError); }); }); describe('getDescription', () => { it('should return description for time_penalty', () => { const penalty = Penalty.create({ id: 'penalty-1', leagueId: 'league-1', raceId: 'race-1', driverId: 'driver-1', type: 'time_penalty', value: 5, reason: 'Speeding', issuedBy: 'steward-1', }); expect(penalty.getDescription()).toBe('+5s time penalty'); }); it('should return description for disqualification', () => { const penalty = Penalty.create({ id: 'penalty-1', leagueId: 'league-1', raceId: 'race-1', driverId: 'driver-1', type: 'disqualification', reason: 'Unsafe', issuedBy: 'steward-1', }); expect(penalty.getDescription()).toBe('Disqualified from race'); }); }); });