import { TeamRatingEvent } from './TeamRatingEvent'; import { TeamRatingEventId } from '../value-objects/TeamRatingEventId'; import { TeamRatingDimensionKey } from '../value-objects/TeamRatingDimensionKey'; import { TeamRatingDelta } from '../value-objects/TeamRatingDelta'; import { RacingDomainValidationError, RacingDomainInvariantError } from '../errors/RacingDomainError'; describe('TeamRatingEvent', () => { const validProps = { id: TeamRatingEventId.create('123e4567-e89b-12d3-a456-426614174000'), teamId: 'team-123', dimension: TeamRatingDimensionKey.create('driving'), delta: TeamRatingDelta.create(10), occurredAt: new Date('2024-01-01T00:00:00Z'), createdAt: new Date('2024-01-01T00:00:00Z'), source: { type: 'race' as const, id: 'race-456' }, reason: { code: 'RACE_FINISH', description: 'Finished 1st in race' }, visibility: { public: true }, version: 1, }; describe('create', () => { it('should create a valid rating event', () => { const event = TeamRatingEvent.create(validProps); expect(event.id.value).toBe(validProps.id.value); expect(event.teamId).toBe(validProps.teamId); expect(event.dimension.value).toBe('driving'); expect(event.delta.value).toBe(10); expect(event.occurredAt).toEqual(validProps.occurredAt); expect(event.createdAt).toEqual(validProps.createdAt); expect(event.source).toEqual(validProps.source); expect(event.reason).toEqual(validProps.reason); expect(event.visibility).toEqual(validProps.visibility); expect(event.version).toBe(1); }); it('should create event with optional weight', () => { const props = { ...validProps, weight: 2 }; const event = TeamRatingEvent.create(props); expect(event.weight).toBe(2); }); it('should throw for empty teamId', () => { const props = { ...validProps, teamId: '' }; expect(() => TeamRatingEvent.create(props)).toThrow(RacingDomainValidationError); }); it('should throw for missing dimension', () => { const { dimension: _dimension, ...rest } = validProps; expect(() => TeamRatingEvent.create(rest as typeof validProps)).toThrow(RacingDomainValidationError); }); it('should throw for missing delta', () => { const { delta: _delta, ...rest } = validProps; expect(() => TeamRatingEvent.create(rest as typeof validProps)).toThrow(RacingDomainValidationError); }); it('should throw for missing source', () => { const { source: _source, ...rest } = validProps; expect(() => TeamRatingEvent.create(rest as typeof validProps)).toThrow(RacingDomainValidationError); }); it('should throw for missing reason', () => { const { reason: _reason, ...rest } = validProps; expect(() => TeamRatingEvent.create(rest as typeof validProps)).toThrow(RacingDomainValidationError); }); it('should throw for missing visibility', () => { const { visibility: _visibility, ...rest } = validProps; expect(() => TeamRatingEvent.create(rest as typeof validProps)).toThrow(RacingDomainValidationError); }); it('should throw for invalid weight', () => { const props = { ...validProps, weight: 0 }; expect(() => TeamRatingEvent.create(props)).toThrow(RacingDomainValidationError); }); it('should throw for future occurredAt', () => { const futureDate = new Date(Date.now() + 86400000); // Tomorrow const props = { ...validProps, occurredAt: futureDate }; expect(() => TeamRatingEvent.create(props)).toThrow(RacingDomainValidationError); }); it('should throw for future createdAt', () => { const futureDate = new Date(Date.now() + 86400000); // Tomorrow const props = { ...validProps, createdAt: futureDate }; expect(() => TeamRatingEvent.create(props)).toThrow(RacingDomainValidationError); }); it('should throw for version < 1', () => { const props = { ...validProps, version: 0 }; expect(() => TeamRatingEvent.create(props)).toThrow(RacingDomainValidationError); }); it('should throw for adminTrust dimension with race source', () => { const props = { ...validProps, dimension: TeamRatingDimensionKey.create('adminTrust'), source: { type: 'race' as const, id: 'race-456' }, }; expect(() => TeamRatingEvent.create(props)).toThrow(RacingDomainInvariantError); }); it('should throw for driving dimension with vote source', () => { const props = { ...validProps, dimension: TeamRatingDimensionKey.create('driving'), source: { type: 'vote' as const, id: 'vote-456' }, }; expect(() => TeamRatingEvent.create(props)).toThrow(RacingDomainInvariantError); }); it('should allow adminTrust with adminAction source', () => { const props = { ...validProps, dimension: TeamRatingDimensionKey.create('adminTrust'), source: { type: 'adminAction' as const, id: 'action-456' }, }; const event = TeamRatingEvent.create(props); expect(event.dimension.value).toBe('adminTrust'); }); it('should allow driving with race source', () => { const props = { ...validProps, dimension: TeamRatingDimensionKey.create('driving'), source: { type: 'race' as const, id: 'race-456' }, }; const event = TeamRatingEvent.create(props); expect(event.dimension.value).toBe('driving'); }); }); describe('rehydrate', () => { it('should rehydrate event from stored data', () => { const event = TeamRatingEvent.rehydrate(validProps); expect(event.id.value).toBe(validProps.id.value); expect(event.teamId).toBe(validProps.teamId); expect(event.dimension.value).toBe('driving'); expect(event.delta.value).toBe(10); }); it('should rehydrate event with optional weight', () => { const props = { ...validProps, weight: 2 }; const event = TeamRatingEvent.rehydrate(props); expect(event.weight).toBe(2); }); it('should return true for same ID', () => { const event1 = TeamRatingEvent.create(validProps); const event2 = TeamRatingEvent.rehydrate(validProps); expect(event1.equals(event2)).toBe(true); }); it('should return false for different IDs', () => { const event1 = TeamRatingEvent.create(validProps); const event2 = TeamRatingEvent.create({ ...validProps, id: TeamRatingEventId.create('123e4567-e89b-12d3-a456-426614174001'), }); expect(event1.equals(event2)).toBe(false); }); }); describe('toJSON', () => { it('should return plain object representation', () => { const event = TeamRatingEvent.create(validProps); const json = event.toJSON(); expect(json).toEqual({ id: validProps.id.value, teamId: validProps.teamId, dimension: 'driving', delta: 10, weight: undefined, occurredAt: validProps.occurredAt.toISOString(), createdAt: validProps.createdAt.toISOString(), source: validProps.source, reason: validProps.reason, visibility: validProps.visibility, version: 1, }); }); it('should include weight when present', () => { const props = { ...validProps, weight: 2 }; const event = TeamRatingEvent.create(props); const json = event.toJSON(); expect(json).toHaveProperty('weight', 2); }); }); });