import { RatingEventFactory, RaceFactsDto } from './RatingEventFactory'; describe('RatingEventFactory', () => { describe('createFromRaceFinish', () => { it('should create events from race finish data', () => { const events = RatingEventFactory.createFromRaceFinish({ userId: 'user-123', raceId: 'race-456', position: 3, totalDrivers: 10, startPosition: 5, incidents: 1, fieldStrength: 2500, status: 'finished', }); expect(events.length).toBeGreaterThan(0); const event = events[0]; expect(event).toBeDefined(); if (event) { expect(event.userId).toBe('user-123'); expect(event.source.type).toBe('race'); expect(event.source.id).toBe('race-456'); } }); it('should create events for DNS status', () => { const events = RatingEventFactory.createFromRaceFinish({ userId: 'user-123', raceId: 'race-456', position: 10, totalDrivers: 10, startPosition: 5, incidents: 0, fieldStrength: 2500, status: 'dns', }); expect(events.length).toBeGreaterThan(0); const dnsEvent = events.find(e => e.reason.code.includes('DNS')); expect(dnsEvent).toBeDefined(); }); it('should create events for DNF status', () => { const events = RatingEventFactory.createFromRaceFinish({ userId: 'user-123', raceId: 'race-456', position: 10, totalDrivers: 10, startPosition: 5, incidents: 2, fieldStrength: 2500, status: 'dnf', }); expect(events.length).toBeGreaterThan(0); const dnfEvent = events.find(e => e.reason.code.includes('DNF')); expect(dnfEvent).toBeDefined(); }); it('should create events for DSQ status', () => { const events = RatingEventFactory.createFromRaceFinish({ userId: 'user-123', raceId: 'race-456', position: 10, totalDrivers: 10, startPosition: 5, incidents: 5, fieldStrength: 2500, status: 'dsq', }); expect(events.length).toBeGreaterThan(0); const dsqEvent = events.find(e => e.reason.code.includes('DSQ')); expect(dsqEvent).toBeDefined(); }); it('should create events for AFK status', () => { const events = RatingEventFactory.createFromRaceFinish({ userId: 'user-123', raceId: 'race-456', position: 10, totalDrivers: 10, startPosition: 5, incidents: 0, fieldStrength: 2500, status: 'afk', }); expect(events.length).toBeGreaterThan(0); const afkEvent = events.find(e => e.reason.code.includes('AFK')); expect(afkEvent).toBeDefined(); }); }); describe('createDrivingEventsFromRace', () => { it('should create events for single driver with good performance', () => { const raceFacts: RaceFactsDto = { raceId: 'race-123', results: [ { userId: 'user-123', startPos: 5, finishPos: 2, incidents: 0, status: 'finished', sof: 2500, }, ], }; const eventsByUser = RatingEventFactory.createDrivingEventsFromRace(raceFacts); expect(eventsByUser.has('user-123')).toBe(true); const events = eventsByUser.get('user-123')!; expect(events.length).toBeGreaterThan(0); const performanceEvent = events.find(e => e.reason.code === 'DRIVING_FINISH_STRENGTH_GAIN'); expect(performanceEvent).toBeDefined(); expect(performanceEvent?.delta.value).toBeGreaterThan(0); }); it('should create events for multiple drivers', () => { const raceFacts: RaceFactsDto = { raceId: 'race-123', results: [ { userId: 'user-123', startPos: 5, finishPos: 2, incidents: 0, status: 'finished', sof: 2500, }, { userId: 'user-456', startPos: 3, finishPos: 8, incidents: 2, status: 'finished', sof: 2500, }, { userId: 'user-789', startPos: 5, finishPos: 5, incidents: 0, status: 'dns', sof: 2500, }, ], }; const eventsByUser = RatingEventFactory.createDrivingEventsFromRace(raceFacts); expect(eventsByUser.has('user-123')).toBe(true); expect(eventsByUser.has('user-456')).toBe(true); expect(eventsByUser.has('user-789')).toBe(true); // user-123 should have performance events const user123Events = eventsByUser.get('user-123')!; expect(user123Events.some(e => e.reason.code === 'DRIVING_FINISH_STRENGTH_GAIN')).toBe(true); // user-456 should have incident penalty const user456Events = eventsByUser.get('user-456')!; expect(user456Events.some(e => e.reason.code === 'DRIVING_INCIDENTS_PENALTY')).toBe(true); // user-789 should have DNS penalty const user789Events = eventsByUser.get('user-789')!; expect(user789Events.some(e => e.reason.code === 'DRIVING_DNS_PENALTY')).toBe(true); }); it('should create positions gained bonus', () => { const raceFacts: RaceFactsDto = { raceId: 'race-123', results: [ { userId: 'user-123', startPos: 10, finishPos: 3, incidents: 0, status: 'finished', sof: 2500, }, ], }; const eventsByUser = RatingEventFactory.createDrivingEventsFromRace(raceFacts); const events = eventsByUser.get('user-123')!; const gainEvent = events.find(e => e.reason.code === 'DRIVING_POSITIONS_GAINED_BONUS'); expect(gainEvent).toBeDefined(); expect(gainEvent?.delta.value).toBeGreaterThan(0); }); it('should handle DNF status', () => { const raceFacts: RaceFactsDto = { raceId: 'race-123', results: [ { userId: 'user-123', startPos: 5, finishPos: 10, incidents: 0, status: 'dnf', sof: 2500, }, ], }; const eventsByUser = RatingEventFactory.createDrivingEventsFromRace(raceFacts); const events = eventsByUser.get('user-123')!; const dnfEvent = events.find(e => e.reason.code === 'DRIVING_DNF_PENALTY'); expect(dnfEvent).toBeDefined(); expect(dnfEvent?.delta.value).toBe(-10); }); it('should handle DSQ status', () => { const raceFacts: RaceFactsDto = { raceId: 'race-123', results: [ { userId: 'user-123', startPos: 5, finishPos: 10, incidents: 0, status: 'dsq', sof: 2500, }, ], }; const eventsByUser = RatingEventFactory.createDrivingEventsFromRace(raceFacts); const events = eventsByUser.get('user-123')!; const dsqEvent = events.find(e => e.reason.code === 'DRIVING_DSQ_PENALTY'); expect(dsqEvent).toBeDefined(); expect(dsqEvent?.delta.value).toBe(-25); }); it('should handle AFK status', () => { const raceFacts: RaceFactsDto = { raceId: 'race-123', results: [ { userId: 'user-123', startPos: 5, finishPos: 10, incidents: 0, status: 'afk', sof: 2500, }, ], }; const eventsByUser = RatingEventFactory.createDrivingEventsFromRace(raceFacts); const events = eventsByUser.get('user-123')!; const afkEvent = events.find(e => e.reason.code === 'DRIVING_AFK_PENALTY'); expect(afkEvent).toBeDefined(); expect(afkEvent?.delta.value).toBe(-20); }); it('should handle incident penalties', () => { const raceFacts: RaceFactsDto = { raceId: 'race-123', results: [ { userId: 'user-123', startPos: 5, finishPos: 5, incidents: 3, status: 'finished', sof: 2500, }, ], }; const eventsByUser = RatingEventFactory.createDrivingEventsFromRace(raceFacts); const events = eventsByUser.get('user-123')!; const incidentEvent = events.find(e => e.reason.code === 'DRIVING_INCIDENTS_PENALTY'); expect(incidentEvent).toBeDefined(); expect(incidentEvent?.delta.value).toBeLessThan(0); }); it('should calculate SoF if not provided', () => { const raceFacts: RaceFactsDto = { raceId: 'race-123', results: [ { userId: 'user-123', startPos: 5, finishPos: 2, incidents: 0, status: 'finished', // No sof }, { userId: 'user-456', startPos: 3, finishPos: 8, incidents: 0, status: 'finished', // No sof }, ], }; const eventsByUser = RatingEventFactory.createDrivingEventsFromRace(raceFacts); // Should still work without errors expect(eventsByUser.size).toBe(2); expect(eventsByUser.get('user-123')!.length).toBeGreaterThan(0); }); it('should handle empty results', () => { const raceFacts: RaceFactsDto = { raceId: 'race-123', results: [], }; const eventsByUser = RatingEventFactory.createDrivingEventsFromRace(raceFacts); expect(eventsByUser.size).toBe(0); }); it('should handle mixed statuses', () => { const raceFacts: RaceFactsDto = { raceId: 'race-123', results: [ { userId: 'user-123', startPos: 5, finishPos: 2, incidents: 1, status: 'finished', sof: 2500, }, { userId: 'user-456', startPos: 3, finishPos: 10, incidents: 0, status: 'dnf', sof: 2500, }, { userId: 'user-789', startPos: 5, finishPos: 5, incidents: 0, status: 'dns', sof: 2500, }, ], }; const eventsByUser = RatingEventFactory.createDrivingEventsFromRace(raceFacts); expect(eventsByUser.size).toBe(3); // user-123: performance + incidents const user123Events = eventsByUser.get('user-123')!; expect(user123Events.length).toBeGreaterThanOrEqual(2); // user-456: DNF const user456Events = eventsByUser.get('user-456')!; expect(user456Events.some(e => e.reason.code === 'DRIVING_DNF_PENALTY')).toBe(true); // user-789: DNS const user789Events = eventsByUser.get('user-789')!; expect(user789Events.some(e => e.reason.code === 'DRIVING_DNS_PENALTY')).toBe(true); }); }); describe('createFromPenalty', () => { it('should create driving penalty event', () => { const events = RatingEventFactory.createFromPenalty({ userId: 'user-123', penaltyId: 'penalty-789', penaltyType: 'incident', severity: 'major', reason: 'Caused collision', }); expect(events.length).toBeGreaterThan(0); const event = events[0]; expect(event).toBeDefined(); if (event) { expect(event.dimension.value).toBe('driving'); expect(event.source.type).toBe('penalty'); expect(event.source.id).toBe('penalty-789'); } }); it('should create admin trust penalty event', () => { const events = RatingEventFactory.createFromPenalty({ userId: 'user-123', penaltyId: 'penalty-789', penaltyType: 'admin_violation', severity: 'major', reason: 'Abuse of power', }); expect(events.length).toBeGreaterThan(0); const event = events[0]; expect(event).toBeDefined(); if (event) { expect(event.dimension.value).toBe('adminTrust'); } }); }); describe('createFromVote', () => { it('should create positive vote event', () => { const events = RatingEventFactory.createFromVote({ userId: 'user-123', voteSessionId: 'vote-101', outcome: 'positive', voteCount: 8, eligibleVoterCount: 10, percentPositive: 80, }); expect(events.length).toBeGreaterThan(0); const event = events[0]; expect(event).toBeDefined(); if (event) { expect(event.dimension.value).toBe('adminTrust'); expect(event.delta.value).toBeGreaterThan(0); } }); it('should create negative vote event', () => { const events = RatingEventFactory.createFromVote({ userId: 'user-123', voteSessionId: 'vote-101', outcome: 'negative', voteCount: 2, eligibleVoterCount: 10, percentPositive: 20, }); expect(events.length).toBeGreaterThan(0); const event = events[0]; expect(event).toBeDefined(); if (event) { expect(event.delta.value).toBeLessThan(0); } }); }); describe('createFromAdminAction', () => { it('should create admin action bonus event', () => { const events = RatingEventFactory.createFromAdminAction({ userId: 'user-123', adminActionId: 'admin-202', actionType: 'sla_response', details: { responseTime: 30 }, }); expect(events.length).toBeGreaterThan(0); const event = events[0]; expect(event).toBeDefined(); if (event) { expect(event.dimension.value).toBe('adminTrust'); expect(event.delta.value).toBeGreaterThan(0); } }); it('should create admin action penalty event', () => { const events = RatingEventFactory.createFromAdminAction({ userId: 'user-123', adminActionId: 'admin-202', actionType: 'abuse_report', details: { validated: true }, }); expect(events.length).toBeGreaterThan(0); const event = events[0]; expect(event).toBeDefined(); if (event) { expect(event.dimension.value).toBe('adminTrust'); expect(event.delta.value).toBeLessThan(0); } }); }); });