import { RatingEvent } from './RatingEvent'; import { RatingEventId } from '../value-objects/RatingEventId'; import { RatingDimensionKey } from '../value-objects/RatingDimensionKey'; import { RatingDelta } from '../value-objects/RatingDelta'; import { IdentityDomainValidationError, IdentityDomainInvariantError } from '../errors/IdentityDomainError'; describe('RatingEvent', () => { const validProps = { id: RatingEventId.create('123e4567-e89b-12d3-a456-426614174000'), userId: 'user-123', dimension: RatingDimensionKey.create('driving'), delta: RatingDelta.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: 'DRIVING_FINISH_STRENGTH_GAIN', summary: 'Finished 3rd in strong field', details: { position: 3, fieldStrength: 2500 }, }, visibility: { public: true, redactedFields: [] as string[], }, version: 1, }; describe('create', () => { it('should create a valid rating event', () => { const event = RatingEvent.create(validProps); expect(event.id.value).toBe(validProps.id.value); expect(event.userId).toBe(validProps.userId); expect(event.dimension.value).toBe('driving'); expect(event.delta.value).toBe(10); expect(event.occurredAt).toEqual(validProps.occurredAt); expect(event.source.type).toBe('race'); expect(event.reason.code).toBe('DRIVING_FINISH_STRENGTH_GAIN'); expect(event.visibility.public).toBe(true); expect(event.version).toBe(1); }); it('should create event with optional weight', () => { const props = { ...validProps, weight: 2 }; const event = RatingEvent.create(props); expect(event.weight).toBe(2); }); it('should create event with non-public visibility', () => { const props = { ...validProps, visibility: { public: false, redactedFields: ['reason.summary'] }, }; const event = RatingEvent.create(props); expect(event.visibility.public).toBe(false); expect(event.visibility.redactedFields).toEqual(['reason.summary']); }); it('should throw for missing userId', () => { const props = { ...validProps, userId: '' }; expect(() => RatingEvent.create(props)).toThrow(IdentityDomainValidationError); }); it('should throw for missing dimension', () => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const { dimension: _dimension, ...rest } = validProps; expect(() => RatingEvent.create(rest as typeof validProps)).toThrow(IdentityDomainValidationError); }); it('should throw for missing delta', () => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const { delta: _delta, ...rest } = validProps; expect(() => RatingEvent.create(rest as typeof validProps)).toThrow(IdentityDomainValidationError); }); it('should throw for missing source', () => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const { source: _source, ...rest } = validProps; expect(() => RatingEvent.create(rest as typeof validProps)).toThrow(IdentityDomainValidationError); }); it('should throw for missing reason', () => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const { reason: _reason, ...rest } = validProps; expect(() => RatingEvent.create(rest as typeof validProps)).toThrow(IdentityDomainValidationError); }); it('should throw for missing visibility', () => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const { visibility: _visibility, ...rest } = validProps; expect(() => RatingEvent.create(rest as typeof validProps)).toThrow(IdentityDomainValidationError); }); it('should throw for invalid version', () => { const props = { ...validProps, version: 0 }; expect(() => RatingEvent.create(props)).toThrow(IdentityDomainValidationError); }); it('should throw for future occurredAt', () => { const futureDate = new Date(Date.now() + 86400000); const props = { ...validProps, occurredAt: futureDate }; expect(() => RatingEvent.create(props)).toThrow(IdentityDomainValidationError); }); it('should throw for future createdAt', () => { const futureDate = new Date(Date.now() + 86400000); const props = { ...validProps, createdAt: futureDate }; expect(() => RatingEvent.create(props)).toThrow(IdentityDomainValidationError); }); it('should throw for occurredAt after createdAt', () => { const props = { ...validProps, occurredAt: new Date('2024-01-02T00:00:00Z'), createdAt: new Date('2024-01-01T00:00:00Z'), }; expect(() => RatingEvent.create(props)).toThrow(IdentityDomainInvariantError); }); }); describe('rehydrate', () => { it('should rehydrate event from stored data', () => { const event = RatingEvent.rehydrate(validProps); expect(event.id.value).toBe(validProps.id.value); expect(event.userId).toBe(validProps.userId); expect(event.dimension.value).toBe('driving'); }); it('should rehydrate with optional weight', () => { const props = { ...validProps, weight: 2 }; const event = RatingEvent.rehydrate(props); expect(event.weight).toBe(2); }); }); describe('equals', () => { it('should return true for same ID', () => { const event1 = RatingEvent.create(validProps); const event2 = RatingEvent.rehydrate(validProps); expect(event1.equals(event2)).toBe(true); }); it('should return false for different IDs', () => { const event1 = RatingEvent.create(validProps); const event2 = RatingEvent.create({ ...validProps, id: RatingEventId.create('123e4567-e89b-12d3-a456-426614174001'), }); expect(event1.equals(event2)).toBe(false); }); }); describe('toJSON', () => { it('should return plain object representation', () => { const event = RatingEvent.create(validProps); const json = event.toJSON(); expect(json.id).toBe(validProps.id.value); expect(json.userId).toBe(validProps.userId); expect(json.dimension).toBe('driving'); expect(json.delta).toBe(10); expect(json.source).toEqual({ type: 'race', id: 'race-456' }); }); }); });