team rating
This commit is contained in:
@@ -0,0 +1,322 @@
|
||||
import { TeamRatingIntegrationAdapter } from './TeamRatingIntegrationAdapter';
|
||||
import { ITeamRaceResultsProvider } from '../ports/ITeamRaceResultsProvider';
|
||||
import { ITeamRatingEventRepository } from '@core/racing/domain/repositories/ITeamRatingEventRepository';
|
||||
import { ITeamRatingRepository } from '@core/racing/domain/repositories/ITeamRatingRepository';
|
||||
import { TeamDrivingRaceFactsDto } from '@core/racing/domain/services/TeamDrivingRatingEventFactory';
|
||||
import { TeamRatingEvent } from '@core/racing/domain/entities/TeamRatingEvent';
|
||||
import { TeamRatingEventId } from '@core/racing/domain/value-objects/TeamRatingEventId';
|
||||
import { TeamRatingDimensionKey } from '@core/racing/domain/value-objects/TeamRatingDimensionKey';
|
||||
import { TeamRatingDelta } from '@core/racing/domain/value-objects/TeamRatingDelta';
|
||||
|
||||
// Mock repositories
|
||||
class MockTeamRaceResultsProvider implements ITeamRaceResultsProvider {
|
||||
private results: TeamDrivingRaceFactsDto | null = null;
|
||||
|
||||
async getTeamRaceResults(raceId: string): Promise<TeamDrivingRaceFactsDto | null> {
|
||||
return this.results;
|
||||
}
|
||||
|
||||
setResults(results: TeamDrivingRaceFactsDto | null) {
|
||||
this.results = results;
|
||||
}
|
||||
}
|
||||
|
||||
class MockTeamRatingEventRepository implements ITeamRatingEventRepository {
|
||||
private events: TeamRatingEvent[] = [];
|
||||
|
||||
async save(event: TeamRatingEvent): Promise<TeamRatingEvent> {
|
||||
this.events.push(event);
|
||||
return event;
|
||||
}
|
||||
|
||||
async findByTeamId(teamId: string): Promise<TeamRatingEvent[]> {
|
||||
return this.events.filter(e => e.teamId === teamId);
|
||||
}
|
||||
|
||||
async findByIds(ids: TeamRatingEventId[]): Promise<TeamRatingEvent[]> {
|
||||
return this.events.filter(e => ids.some(id => id.equals(e.id)));
|
||||
}
|
||||
|
||||
async getAllByTeamId(teamId: string): Promise<TeamRatingEvent[]> {
|
||||
return this.events.filter(e => e.teamId === teamId);
|
||||
}
|
||||
|
||||
async findEventsPaginated(teamId: string): Promise<any> {
|
||||
const events = await this.getAllByTeamId(teamId);
|
||||
return {
|
||||
items: events,
|
||||
total: events.length,
|
||||
limit: 10,
|
||||
offset: 0,
|
||||
hasMore: false,
|
||||
};
|
||||
}
|
||||
|
||||
clear() {
|
||||
this.events = [];
|
||||
}
|
||||
}
|
||||
|
||||
class MockTeamRatingRepository implements ITeamRatingRepository {
|
||||
private snapshots: Map<string, any> = new Map();
|
||||
|
||||
async findByTeamId(teamId: string): Promise<any | null> {
|
||||
return this.snapshots.get(teamId) || null;
|
||||
}
|
||||
|
||||
async save(snapshot: any): Promise<any> {
|
||||
this.snapshots.set(snapshot.teamId, snapshot);
|
||||
return snapshot;
|
||||
}
|
||||
|
||||
clear() {
|
||||
this.snapshots.clear();
|
||||
}
|
||||
}
|
||||
|
||||
describe('TeamRatingIntegrationAdapter', () => {
|
||||
let adapter: TeamRatingIntegrationAdapter;
|
||||
let mockResultsProvider: MockTeamRaceResultsProvider;
|
||||
let mockEventRepo: MockTeamRatingEventRepository;
|
||||
let mockRatingRepo: MockTeamRatingRepository;
|
||||
|
||||
beforeEach(() => {
|
||||
mockResultsProvider = new MockTeamRaceResultsProvider();
|
||||
mockEventRepo = new MockTeamRatingEventRepository();
|
||||
mockRatingRepo = new MockTeamRatingRepository();
|
||||
adapter = new TeamRatingIntegrationAdapter(
|
||||
mockResultsProvider,
|
||||
mockEventRepo,
|
||||
mockRatingRepo
|
||||
);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
mockEventRepo.clear();
|
||||
mockRatingRepo.clear();
|
||||
});
|
||||
|
||||
describe('recordTeamRatings', () => {
|
||||
it('should return true when no results found', async () => {
|
||||
mockResultsProvider.setResults(null);
|
||||
|
||||
const result = await adapter.recordTeamRatings('race-123');
|
||||
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
|
||||
it('should return true when results are empty', async () => {
|
||||
mockResultsProvider.setResults({
|
||||
raceId: 'race-123',
|
||||
teamId: 'team-123',
|
||||
results: [],
|
||||
});
|
||||
|
||||
const result = await adapter.recordTeamRatings('race-123');
|
||||
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
|
||||
it('should return true when no events generated', async () => {
|
||||
mockResultsProvider.setResults({
|
||||
raceId: 'race-123',
|
||||
teamId: 'team-123',
|
||||
results: [
|
||||
{
|
||||
teamId: 'team-123',
|
||||
position: 1,
|
||||
incidents: 0,
|
||||
status: 'dns',
|
||||
fieldSize: 1,
|
||||
strengthOfField: 50,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const result = await adapter.recordTeamRatings('race-123');
|
||||
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
|
||||
it('should record team ratings successfully', async () => {
|
||||
const raceResults: TeamDrivingRaceFactsDto = {
|
||||
raceId: 'race-123',
|
||||
teamId: 'team-123',
|
||||
results: [
|
||||
{
|
||||
teamId: 'team-123',
|
||||
position: 1,
|
||||
incidents: 0,
|
||||
status: 'finished',
|
||||
fieldSize: 3,
|
||||
strengthOfField: 55,
|
||||
},
|
||||
{
|
||||
teamId: 'team-456',
|
||||
position: 2,
|
||||
incidents: 1,
|
||||
status: 'finished',
|
||||
fieldSize: 3,
|
||||
strengthOfField: 55,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
mockResultsProvider.setResults(raceResults);
|
||||
|
||||
const result = await adapter.recordTeamRatings('race-123');
|
||||
|
||||
expect(result).toBe(true);
|
||||
|
||||
// Verify events were saved
|
||||
const events1 = await mockEventRepo.getAllByTeamId('team-123');
|
||||
const events2 = await mockEventRepo.getAllByTeamId('team-456');
|
||||
|
||||
expect(events1.length).toBeGreaterThan(0);
|
||||
expect(events2.length).toBeGreaterThan(0);
|
||||
|
||||
// Verify snapshots were updated
|
||||
const snapshot1 = await mockRatingRepo.findByTeamId('team-123');
|
||||
const snapshot2 = await mockRatingRepo.findByTeamId('team-456');
|
||||
|
||||
expect(snapshot1).toBeDefined();
|
||||
expect(snapshot2).toBeDefined();
|
||||
});
|
||||
|
||||
it('should return false on error', async () => {
|
||||
const raceResults: TeamDrivingRaceFactsDto = {
|
||||
raceId: 'race-123',
|
||||
teamId: 'team-123',
|
||||
results: [
|
||||
{
|
||||
teamId: 'team-123',
|
||||
position: 1,
|
||||
incidents: 0,
|
||||
status: 'finished',
|
||||
fieldSize: 1,
|
||||
strengthOfField: 55,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
mockResultsProvider.setResults(raceResults);
|
||||
|
||||
// Mock repository to throw error
|
||||
const originalSave = mockEventRepo.save.bind(mockEventRepo);
|
||||
mockEventRepo.save = async () => {
|
||||
throw new Error('Repository error');
|
||||
};
|
||||
|
||||
const result = await adapter.recordTeamRatings('race-123');
|
||||
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('recordTeamRatingsWithDetails', () => {
|
||||
it('should return details for successful recording', async () => {
|
||||
const raceResults: TeamDrivingRaceFactsDto = {
|
||||
raceId: 'race-123',
|
||||
teamId: 'team-123',
|
||||
results: [
|
||||
{
|
||||
teamId: 'team-123',
|
||||
position: 1,
|
||||
incidents: 0,
|
||||
status: 'finished',
|
||||
fieldSize: 3,
|
||||
strengthOfField: 55,
|
||||
},
|
||||
{
|
||||
teamId: 'team-456',
|
||||
position: 2,
|
||||
incidents: 1,
|
||||
status: 'finished',
|
||||
fieldSize: 3,
|
||||
strengthOfField: 55,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
mockResultsProvider.setResults(raceResults);
|
||||
|
||||
const result = await adapter.recordTeamRatingsWithDetails('race-123');
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.eventsCreated).toBeGreaterThan(0);
|
||||
expect(result.teamsUpdated).toContain('team-123');
|
||||
expect(result.teamsUpdated).toContain('team-456');
|
||||
expect(result.errors).toEqual([]);
|
||||
});
|
||||
|
||||
it('should handle partial failures', async () => {
|
||||
const raceResults: TeamDrivingRaceFactsDto = {
|
||||
raceId: 'race-123',
|
||||
teamId: 'team-123',
|
||||
results: [
|
||||
{
|
||||
teamId: 'team-123',
|
||||
position: 1,
|
||||
incidents: 0,
|
||||
status: 'finished',
|
||||
fieldSize: 2,
|
||||
strengthOfField: 55,
|
||||
},
|
||||
{
|
||||
teamId: 'team-456',
|
||||
position: 2,
|
||||
incidents: 0,
|
||||
status: 'finished',
|
||||
fieldSize: 2,
|
||||
strengthOfField: 55,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
mockResultsProvider.setResults(raceResults);
|
||||
|
||||
// Mock repository to fail for team-456
|
||||
const originalSave = mockEventRepo.save.bind(mockEventRepo);
|
||||
mockEventRepo.save = async (event) => {
|
||||
if (event.teamId === 'team-456') {
|
||||
throw new Error('Simulated failure');
|
||||
}
|
||||
return originalSave(event);
|
||||
};
|
||||
|
||||
const result = await adapter.recordTeamRatingsWithDetails('race-123');
|
||||
|
||||
expect(result.success).toBe(false);
|
||||
expect(result.teamsUpdated).toContain('team-123');
|
||||
expect(result.teamsUpdated).not.toContain('team-456');
|
||||
expect(result.errors.length).toBeGreaterThan(0);
|
||||
expect(result.errors[0]).toContain('team-456');
|
||||
});
|
||||
|
||||
it('should handle empty results', async () => {
|
||||
mockResultsProvider.setResults({
|
||||
raceId: 'race-123',
|
||||
teamId: 'team-123',
|
||||
results: [],
|
||||
});
|
||||
|
||||
const result = await adapter.recordTeamRatingsWithDetails('race-123');
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.eventsCreated).toBe(0);
|
||||
expect(result.teamsUpdated).toEqual([]);
|
||||
expect(result.errors).toEqual([]);
|
||||
});
|
||||
|
||||
it('should handle null results', async () => {
|
||||
mockResultsProvider.setResults(null);
|
||||
|
||||
const result = await adapter.recordTeamRatingsWithDetails('race-123');
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.eventsCreated).toBe(0);
|
||||
expect(result.teamsUpdated).toEqual([]);
|
||||
expect(result.errors).toEqual([]);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user