refactor
This commit is contained in:
@@ -0,0 +1,105 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { ChampionshipStanding } from './ChampionshipStanding';
|
||||
import type { ParticipantRef } from '../../types/ParticipantRef';
|
||||
|
||||
describe('ChampionshipStanding', () => {
|
||||
const participant: ParticipantRef = { type: 'driver', id: 'driver1' };
|
||||
|
||||
it('should create a championship standing', () => {
|
||||
const standing = ChampionshipStanding.create({
|
||||
seasonId: 'season1',
|
||||
championshipId: 'champ1',
|
||||
participant,
|
||||
totalPoints: 100,
|
||||
resultsCounted: 5,
|
||||
resultsDropped: 1,
|
||||
position: 1,
|
||||
});
|
||||
|
||||
expect(standing.id).toBe('season1-champ1-driver1');
|
||||
expect(standing.seasonId).toBe('season1');
|
||||
expect(standing.championshipId).toBe('champ1');
|
||||
expect(standing.participant).toBe(participant);
|
||||
expect(standing.totalPoints.toNumber()).toBe(100);
|
||||
expect(standing.resultsCounted.toNumber()).toBe(5);
|
||||
expect(standing.resultsDropped.toNumber()).toBe(1);
|
||||
expect(standing.position.toNumber()).toBe(1);
|
||||
});
|
||||
|
||||
it('should update position', () => {
|
||||
const standing = ChampionshipStanding.create({
|
||||
seasonId: 'season1',
|
||||
championshipId: 'champ1',
|
||||
participant,
|
||||
totalPoints: 100,
|
||||
resultsCounted: 5,
|
||||
resultsDropped: 1,
|
||||
position: 1,
|
||||
});
|
||||
|
||||
const updated = standing.withPosition(2);
|
||||
expect(updated.position.toNumber()).toBe(2);
|
||||
expect(updated.id).toBe('season1-champ1-driver1');
|
||||
expect(updated.totalPoints.toNumber()).toBe(100);
|
||||
});
|
||||
|
||||
it('should throw on invalid seasonId', () => {
|
||||
expect(() => ChampionshipStanding.create({
|
||||
seasonId: '',
|
||||
championshipId: 'champ1',
|
||||
participant,
|
||||
totalPoints: 100,
|
||||
resultsCounted: 5,
|
||||
resultsDropped: 1,
|
||||
position: 1,
|
||||
})).toThrow('Season ID is required');
|
||||
});
|
||||
|
||||
it('should throw on invalid championshipId', () => {
|
||||
expect(() => ChampionshipStanding.create({
|
||||
seasonId: 'season1',
|
||||
championshipId: '',
|
||||
participant,
|
||||
totalPoints: 100,
|
||||
resultsCounted: 5,
|
||||
resultsDropped: 1,
|
||||
position: 1,
|
||||
})).toThrow('Championship ID is required');
|
||||
});
|
||||
|
||||
it('should throw on invalid participant', () => {
|
||||
expect(() => ChampionshipStanding.create({
|
||||
seasonId: 'season1',
|
||||
championshipId: 'champ1',
|
||||
participant: { type: 'driver', id: '' },
|
||||
totalPoints: 100,
|
||||
resultsCounted: 5,
|
||||
resultsDropped: 1,
|
||||
position: 1,
|
||||
})).toThrow('Participant is required');
|
||||
});
|
||||
|
||||
it('should throw on negative points', () => {
|
||||
expect(() => ChampionshipStanding.create({
|
||||
seasonId: 'season1',
|
||||
championshipId: 'champ1',
|
||||
participant,
|
||||
totalPoints: -1,
|
||||
resultsCounted: 5,
|
||||
resultsDropped: 1,
|
||||
position: 1,
|
||||
})).toThrow('Points cannot be negative');
|
||||
});
|
||||
|
||||
it('should throw on invalid position', () => {
|
||||
expect(() => ChampionshipStanding.create({
|
||||
seasonId: 'season1',
|
||||
championshipId: 'champ1',
|
||||
participant,
|
||||
totalPoints: 100,
|
||||
resultsCounted: 5,
|
||||
resultsDropped: 1,
|
||||
position: 0,
|
||||
})).toThrow('Position must be a positive integer');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,69 @@
|
||||
import type { IEntity } from '@core/shared/domain';
|
||||
import { RacingDomainValidationError } from '../../errors/RacingDomainError';
|
||||
import type { ParticipantRef } from '../../types/ParticipantRef';
|
||||
import { Points } from '../../value-objects/Points';
|
||||
import { Position } from './Position';
|
||||
import { ResultsCount } from './ResultsCount';
|
||||
|
||||
export class ChampionshipStanding implements IEntity<string> {
|
||||
readonly id: string;
|
||||
readonly seasonId: string;
|
||||
readonly championshipId: string;
|
||||
readonly participant: ParticipantRef;
|
||||
readonly totalPoints: Points;
|
||||
readonly resultsCounted: ResultsCount;
|
||||
readonly resultsDropped: ResultsCount;
|
||||
readonly position: Position;
|
||||
|
||||
private constructor(props: {
|
||||
seasonId: string;
|
||||
championshipId: string;
|
||||
participant: ParticipantRef;
|
||||
totalPoints: number;
|
||||
resultsCounted: number;
|
||||
resultsDropped: number;
|
||||
position: number;
|
||||
}) {
|
||||
this.seasonId = props.seasonId;
|
||||
this.championshipId = props.championshipId;
|
||||
this.participant = props.participant;
|
||||
this.totalPoints = Points.create(props.totalPoints);
|
||||
this.resultsCounted = ResultsCount.create(props.resultsCounted);
|
||||
this.resultsDropped = ResultsCount.create(props.resultsDropped);
|
||||
this.position = Position.create(props.position);
|
||||
this.id = `${this.seasonId}-${this.championshipId}-${this.participant.id}`;
|
||||
}
|
||||
|
||||
static create(props: {
|
||||
seasonId: string;
|
||||
championshipId: string;
|
||||
participant: ParticipantRef;
|
||||
totalPoints: number;
|
||||
resultsCounted: number;
|
||||
resultsDropped: number;
|
||||
position: number;
|
||||
}): ChampionshipStanding {
|
||||
if (!props.seasonId || props.seasonId.trim().length === 0) {
|
||||
throw new RacingDomainValidationError('Season ID is required');
|
||||
}
|
||||
if (!props.championshipId || props.championshipId.trim().length === 0) {
|
||||
throw new RacingDomainValidationError('Championship ID is required');
|
||||
}
|
||||
if (!props.participant || !props.participant.id || props.participant.id.trim().length === 0) {
|
||||
throw new RacingDomainValidationError('Participant is required');
|
||||
}
|
||||
return new ChampionshipStanding(props);
|
||||
}
|
||||
|
||||
withPosition(position: number): ChampionshipStanding {
|
||||
return ChampionshipStanding.create({
|
||||
seasonId: this.seasonId,
|
||||
championshipId: this.championshipId,
|
||||
participant: this.participant,
|
||||
totalPoints: this.totalPoints.toNumber(),
|
||||
resultsCounted: this.resultsCounted.toNumber(),
|
||||
resultsDropped: this.resultsDropped.toNumber(),
|
||||
position,
|
||||
});
|
||||
}
|
||||
}
|
||||
33
core/racing/domain/entities/championship/Position.test.ts
Normal file
33
core/racing/domain/entities/championship/Position.test.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { Position } from './Position';
|
||||
|
||||
describe('Position', () => {
|
||||
it('should create position', () => {
|
||||
const position = Position.create(1);
|
||||
expect(position.toNumber()).toBe(1);
|
||||
});
|
||||
|
||||
it('should not create zero position', () => {
|
||||
expect(() => Position.create(0)).toThrow('Position must be a positive integer');
|
||||
});
|
||||
|
||||
it('should not create negative position', () => {
|
||||
expect(() => Position.create(-1)).toThrow('Position must be a positive integer');
|
||||
});
|
||||
|
||||
it('should not create non-integer position', () => {
|
||||
expect(() => Position.create(1.5)).toThrow('Position must be a positive integer');
|
||||
});
|
||||
|
||||
it('should equal same position', () => {
|
||||
const p1 = Position.create(2);
|
||||
const p2 = Position.create(2);
|
||||
expect(p1.equals(p2)).toBe(true);
|
||||
});
|
||||
|
||||
it('should not equal different position', () => {
|
||||
const p1 = Position.create(2);
|
||||
const p2 = Position.create(3);
|
||||
expect(p1.equals(p2)).toBe(false);
|
||||
});
|
||||
});
|
||||
20
core/racing/domain/entities/championship/Position.ts
Normal file
20
core/racing/domain/entities/championship/Position.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import { RacingDomainValidationError } from '../../errors/RacingDomainError';
|
||||
|
||||
export class Position {
|
||||
private constructor(private readonly value: number) {}
|
||||
|
||||
static create(value: number): Position {
|
||||
if (!Number.isInteger(value) || value <= 0) {
|
||||
throw new RacingDomainValidationError('Position must be a positive integer');
|
||||
}
|
||||
return new Position(value);
|
||||
}
|
||||
|
||||
toNumber(): number {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
equals(other: Position): boolean {
|
||||
return this.value === other.value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { ResultsCount } from './ResultsCount';
|
||||
|
||||
describe('ResultsCount', () => {
|
||||
it('should create results count', () => {
|
||||
const count = ResultsCount.create(5);
|
||||
expect(count.toNumber()).toBe(5);
|
||||
});
|
||||
|
||||
it('should create zero results count', () => {
|
||||
const count = ResultsCount.create(0);
|
||||
expect(count.toNumber()).toBe(0);
|
||||
});
|
||||
|
||||
it('should not create negative results count', () => {
|
||||
expect(() => ResultsCount.create(-1)).toThrow('Results count must be a non-negative integer');
|
||||
});
|
||||
|
||||
it('should not create non-integer results count', () => {
|
||||
expect(() => ResultsCount.create(1.5)).toThrow('Results count must be a non-negative integer');
|
||||
});
|
||||
|
||||
it('should equal same count', () => {
|
||||
const c1 = ResultsCount.create(3);
|
||||
const c2 = ResultsCount.create(3);
|
||||
expect(c1.equals(c2)).toBe(true);
|
||||
});
|
||||
|
||||
it('should not equal different count', () => {
|
||||
const c1 = ResultsCount.create(3);
|
||||
const c2 = ResultsCount.create(4);
|
||||
expect(c1.equals(c2)).toBe(false);
|
||||
});
|
||||
});
|
||||
20
core/racing/domain/entities/championship/ResultsCount.ts
Normal file
20
core/racing/domain/entities/championship/ResultsCount.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import { RacingDomainValidationError } from '../../errors/RacingDomainError';
|
||||
|
||||
export class ResultsCount {
|
||||
private constructor(private readonly value: number) {}
|
||||
|
||||
static create(value: number): ResultsCount {
|
||||
if (!Number.isInteger(value) || value < 0) {
|
||||
throw new RacingDomainValidationError('Results count must be a non-negative integer');
|
||||
}
|
||||
return new ResultsCount(value);
|
||||
}
|
||||
|
||||
toNumber(): number {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
equals(other: ResultsCount): boolean {
|
||||
return this.value === other.value;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user