174 lines
6.4 KiB
TypeScript
174 lines
6.4 KiB
TypeScript
import { IdentityDomainInvariantError, IdentityDomainValidationError } from '../errors/IdentityDomainError';
|
|
import { RatingDelta } from '../value-objects/RatingDelta';
|
|
import { RatingDimensionKey } from '../value-objects/RatingDimensionKey';
|
|
import { RatingEventId } from '../value-objects/RatingEventId';
|
|
import { RatingEvent } from './RatingEvent';
|
|
|
|
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' });
|
|
});
|
|
});
|
|
}); |