rating
This commit is contained in:
489
core/identity/domain/services/RatingEventFactory.test.ts
Normal file
489
core/identity/domain/services/RatingEventFactory.test.ts
Normal file
@@ -0,0 +1,489 @@
|
||||
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);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user