408 lines
12 KiB
TypeScript
408 lines
12 KiB
TypeScript
import { describe, it, expect } from 'vitest';
|
|
import { RulebookViewDataBuilder } from './RulebookViewDataBuilder';
|
|
import type { RulebookApiDto } from '@/lib/types/tbd/RulebookApiDto';
|
|
|
|
describe('RulebookViewDataBuilder', () => {
|
|
describe('happy paths', () => {
|
|
it('should transform RulebookApiDto to RulebookViewData correctly', () => {
|
|
const rulebookApiDto: RulebookApiDto = {
|
|
leagueId: 'league-123',
|
|
scoringConfig: {
|
|
gameName: 'iRacing',
|
|
scoringPresetName: 'Standard',
|
|
championships: [
|
|
{
|
|
type: 'driver',
|
|
sessionTypes: ['race'],
|
|
pointsPreview: [
|
|
{ sessionType: 'race', position: 1, points: 25 },
|
|
{ sessionType: 'race', position: 2, points: 18 },
|
|
{ sessionType: 'race', position: 3, points: 15 },
|
|
],
|
|
bonusSummary: [
|
|
{ type: 'fastest_lap', points: 5, description: 'Fastest lap' },
|
|
],
|
|
},
|
|
],
|
|
dropPolicySummary: 'Drop 2 worst results',
|
|
},
|
|
};
|
|
|
|
const result = RulebookViewDataBuilder.build(rulebookApiDto);
|
|
|
|
expect(result).toEqual({
|
|
leagueId: 'league-123',
|
|
gameName: 'iRacing',
|
|
scoringPresetName: 'Standard',
|
|
championshipsCount: 1,
|
|
sessionTypes: 'race',
|
|
dropPolicySummary: 'Drop 2 worst results',
|
|
hasActiveDropPolicy: true,
|
|
positionPoints: [
|
|
{ position: 1, points: 25 },
|
|
{ position: 2, points: 18 },
|
|
{ position: 3, points: 15 },
|
|
],
|
|
bonusPoints: [
|
|
{ type: 'fastest_lap', points: 5, description: 'Fastest lap' },
|
|
],
|
|
hasBonusPoints: true,
|
|
});
|
|
});
|
|
|
|
it('should handle championship without driver type', () => {
|
|
const rulebookApiDto: RulebookApiDto = {
|
|
leagueId: 'league-456',
|
|
scoringConfig: {
|
|
gameName: 'iRacing',
|
|
scoringPresetName: 'Standard',
|
|
championships: [
|
|
{
|
|
type: 'team',
|
|
sessionTypes: ['race'],
|
|
pointsPreview: [
|
|
{ sessionType: 'race', position: 1, points: 25 },
|
|
],
|
|
bonusSummary: [],
|
|
},
|
|
],
|
|
dropPolicySummary: 'No drops',
|
|
},
|
|
};
|
|
|
|
const result = RulebookViewDataBuilder.build(rulebookApiDto);
|
|
|
|
expect(result.positionPoints).toEqual([{ position: 1, points: 25 }]);
|
|
});
|
|
|
|
it('should handle multiple championships', () => {
|
|
const rulebookApiDto: RulebookApiDto = {
|
|
leagueId: 'league-789',
|
|
scoringConfig: {
|
|
gameName: 'iRacing',
|
|
scoringPresetName: 'Standard',
|
|
championships: [
|
|
{
|
|
type: 'driver',
|
|
sessionTypes: ['race'],
|
|
pointsPreview: [
|
|
{ sessionType: 'race', position: 1, points: 25 },
|
|
],
|
|
bonusSummary: [],
|
|
},
|
|
{
|
|
type: 'team',
|
|
sessionTypes: ['race'],
|
|
pointsPreview: [
|
|
{ sessionType: 'race', position: 1, points: 25 },
|
|
],
|
|
bonusSummary: [],
|
|
},
|
|
],
|
|
dropPolicySummary: 'No drops',
|
|
},
|
|
};
|
|
|
|
const result = RulebookViewDataBuilder.build(rulebookApiDto);
|
|
|
|
expect(result.championshipsCount).toBe(2);
|
|
});
|
|
|
|
it('should handle empty bonus points', () => {
|
|
const rulebookApiDto: RulebookApiDto = {
|
|
leagueId: 'league-101',
|
|
scoringConfig: {
|
|
gameName: 'iRacing',
|
|
scoringPresetName: 'Standard',
|
|
championships: [
|
|
{
|
|
type: 'driver',
|
|
sessionTypes: ['race'],
|
|
pointsPreview: [
|
|
{ sessionType: 'race', position: 1, points: 25 },
|
|
],
|
|
bonusSummary: [],
|
|
},
|
|
],
|
|
dropPolicySummary: 'No drops',
|
|
},
|
|
};
|
|
|
|
const result = RulebookViewDataBuilder.build(rulebookApiDto);
|
|
|
|
expect(result.bonusPoints).toEqual([]);
|
|
expect(result.hasBonusPoints).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe('data transformation', () => {
|
|
it('should preserve all DTO fields in the output', () => {
|
|
const rulebookApiDto: RulebookApiDto = {
|
|
leagueId: 'league-102',
|
|
scoringConfig: {
|
|
gameName: 'iRacing',
|
|
scoringPresetName: 'Standard',
|
|
championships: [
|
|
{
|
|
type: 'driver',
|
|
sessionTypes: ['race'],
|
|
pointsPreview: [
|
|
{ sessionType: 'race', position: 1, points: 25 },
|
|
],
|
|
bonusSummary: [
|
|
{ type: 'fastest_lap', points: 5, description: 'Fastest lap' },
|
|
],
|
|
},
|
|
],
|
|
dropPolicySummary: 'Drop 2 worst results',
|
|
},
|
|
};
|
|
|
|
const result = RulebookViewDataBuilder.build(rulebookApiDto);
|
|
|
|
expect(result.leagueId).toBe(rulebookApiDto.leagueId);
|
|
expect(result.gameName).toBe(rulebookApiDto.scoringConfig.gameName);
|
|
expect(result.scoringPresetName).toBe(rulebookApiDto.scoringConfig.scoringPresetName);
|
|
expect(result.dropPolicySummary).toBe(rulebookApiDto.scoringConfig.dropPolicySummary);
|
|
});
|
|
|
|
it('should not modify the input DTO', () => {
|
|
const rulebookApiDto: RulebookApiDto = {
|
|
leagueId: 'league-103',
|
|
scoringConfig: {
|
|
gameName: 'iRacing',
|
|
scoringPresetName: 'Standard',
|
|
championships: [
|
|
{
|
|
type: 'driver',
|
|
sessionTypes: ['race'],
|
|
pointsPreview: [
|
|
{ sessionType: 'race', position: 1, points: 25 },
|
|
],
|
|
bonusSummary: [],
|
|
},
|
|
],
|
|
dropPolicySummary: 'No drops',
|
|
},
|
|
};
|
|
|
|
const originalDto = { ...rulebookApiDto };
|
|
RulebookViewDataBuilder.build(rulebookApiDto);
|
|
|
|
expect(rulebookApiDto).toEqual(originalDto);
|
|
});
|
|
});
|
|
|
|
describe('edge cases', () => {
|
|
it('should handle empty drop policy', () => {
|
|
const rulebookApiDto: RulebookApiDto = {
|
|
leagueId: 'league-104',
|
|
scoringConfig: {
|
|
gameName: 'iRacing',
|
|
scoringPresetName: 'Standard',
|
|
championships: [
|
|
{
|
|
type: 'driver',
|
|
sessionTypes: ['race'],
|
|
pointsPreview: [
|
|
{ sessionType: 'race', position: 1, points: 25 },
|
|
],
|
|
bonusSummary: [],
|
|
},
|
|
],
|
|
dropPolicySummary: '',
|
|
},
|
|
};
|
|
|
|
const result = RulebookViewDataBuilder.build(rulebookApiDto);
|
|
|
|
expect(result.hasActiveDropPolicy).toBe(false);
|
|
});
|
|
|
|
it('should handle drop policy with "All" keyword', () => {
|
|
const rulebookApiDto: RulebookApiDto = {
|
|
leagueId: 'league-105',
|
|
scoringConfig: {
|
|
gameName: 'iRacing',
|
|
scoringPresetName: 'Standard',
|
|
championships: [
|
|
{
|
|
type: 'driver',
|
|
sessionTypes: ['race'],
|
|
pointsPreview: [
|
|
{ sessionType: 'race', position: 1, points: 25 },
|
|
],
|
|
bonusSummary: [],
|
|
},
|
|
],
|
|
dropPolicySummary: 'Drop all results',
|
|
},
|
|
};
|
|
|
|
const result = RulebookViewDataBuilder.build(rulebookApiDto);
|
|
|
|
expect(result.hasActiveDropPolicy).toBe(false);
|
|
});
|
|
|
|
it('should handle multiple session types', () => {
|
|
const rulebookApiDto: RulebookApiDto = {
|
|
leagueId: 'league-106',
|
|
scoringConfig: {
|
|
gameName: 'iRacing',
|
|
scoringPresetName: 'Standard',
|
|
championships: [
|
|
{
|
|
type: 'driver',
|
|
sessionTypes: ['race', 'qualifying', 'practice'],
|
|
pointsPreview: [
|
|
{ sessionType: 'race', position: 1, points: 25 },
|
|
],
|
|
bonusSummary: [],
|
|
},
|
|
],
|
|
dropPolicySummary: 'No drops',
|
|
},
|
|
};
|
|
|
|
const result = RulebookViewDataBuilder.build(rulebookApiDto);
|
|
|
|
expect(result.sessionTypes).toBe('race, qualifying, practice');
|
|
});
|
|
|
|
it('should handle single session type', () => {
|
|
const rulebookApiDto: RulebookApiDto = {
|
|
leagueId: 'league-107',
|
|
scoringConfig: {
|
|
gameName: 'iRacing',
|
|
scoringPresetName: 'Standard',
|
|
championships: [
|
|
{
|
|
type: 'driver',
|
|
sessionTypes: ['race'],
|
|
pointsPreview: [
|
|
{ sessionType: 'race', position: 1, points: 25 },
|
|
],
|
|
bonusSummary: [],
|
|
},
|
|
],
|
|
dropPolicySummary: 'No drops',
|
|
},
|
|
};
|
|
|
|
const result = RulebookViewDataBuilder.build(rulebookApiDto);
|
|
|
|
expect(result.sessionTypes).toBe('race');
|
|
});
|
|
|
|
it('should handle empty points preview', () => {
|
|
const rulebookApiDto: RulebookApiDto = {
|
|
leagueId: 'league-108',
|
|
scoringConfig: {
|
|
gameName: 'iRacing',
|
|
scoringPresetName: 'Standard',
|
|
championships: [
|
|
{
|
|
type: 'driver',
|
|
sessionTypes: ['race'],
|
|
pointsPreview: [],
|
|
bonusSummary: [],
|
|
},
|
|
],
|
|
dropPolicySummary: 'No drops',
|
|
},
|
|
};
|
|
|
|
const result = RulebookViewDataBuilder.build(rulebookApiDto);
|
|
|
|
expect(result.positionPoints).toEqual([]);
|
|
});
|
|
|
|
it('should handle points preview with different session types', () => {
|
|
const rulebookApiDto: RulebookApiDto = {
|
|
leagueId: 'league-109',
|
|
scoringConfig: {
|
|
gameName: 'iRacing',
|
|
scoringPresetName: 'Standard',
|
|
championships: [
|
|
{
|
|
type: 'driver',
|
|
sessionTypes: ['race'],
|
|
pointsPreview: [
|
|
{ sessionType: 'race', position: 1, points: 25 },
|
|
{ sessionType: 'qualifying', position: 1, points: 10 },
|
|
],
|
|
bonusSummary: [],
|
|
},
|
|
],
|
|
dropPolicySummary: 'No drops',
|
|
},
|
|
};
|
|
|
|
const result = RulebookViewDataBuilder.build(rulebookApiDto);
|
|
|
|
expect(result.positionPoints).toEqual([{ position: 1, points: 25 }]);
|
|
});
|
|
|
|
it('should handle points preview with non-sequential positions', () => {
|
|
const rulebookApiDto: RulebookApiDto = {
|
|
leagueId: 'league-110',
|
|
scoringConfig: {
|
|
gameName: 'iRacing',
|
|
scoringPresetName: 'Standard',
|
|
championships: [
|
|
{
|
|
type: 'driver',
|
|
sessionTypes: ['race'],
|
|
pointsPreview: [
|
|
{ sessionType: 'race', position: 1, points: 25 },
|
|
{ sessionType: 'race', position: 3, points: 15 },
|
|
{ sessionType: 'race', position: 2, points: 18 },
|
|
],
|
|
bonusSummary: [],
|
|
},
|
|
],
|
|
dropPolicySummary: 'No drops',
|
|
},
|
|
};
|
|
|
|
const result = RulebookViewDataBuilder.build(rulebookApiDto);
|
|
|
|
expect(result.positionPoints).toEqual([
|
|
{ position: 1, points: 25 },
|
|
{ position: 2, points: 18 },
|
|
{ position: 3, points: 15 },
|
|
]);
|
|
});
|
|
|
|
it('should handle multiple bonus points', () => {
|
|
const rulebookApiDto: RulebookApiDto = {
|
|
leagueId: 'league-111',
|
|
scoringConfig: {
|
|
gameName: 'iRacing',
|
|
scoringPresetName: 'Standard',
|
|
championships: [
|
|
{
|
|
type: 'driver',
|
|
sessionTypes: ['race'],
|
|
pointsPreview: [
|
|
{ sessionType: 'race', position: 1, points: 25 },
|
|
],
|
|
bonusSummary: [
|
|
{ type: 'fastest_lap', points: 5, description: 'Fastest lap' },
|
|
{ type: 'pole_position', points: 3, description: 'Pole position' },
|
|
{ type: 'clean_race', points: 2, description: 'Clean race' },
|
|
],
|
|
},
|
|
],
|
|
dropPolicySummary: 'No drops',
|
|
},
|
|
};
|
|
|
|
const result = RulebookViewDataBuilder.build(rulebookApiDto);
|
|
|
|
expect(result.bonusPoints).toHaveLength(3);
|
|
expect(result.hasBonusPoints).toBe(true);
|
|
});
|
|
});
|
|
});
|