Files
gridpilot.gg/core/identity/domain/entities/ExternalGameRatingProfile.test.ts
2026-01-16 19:46:49 +01:00

410 lines
11 KiB
TypeScript

import { IdentityDomainValidationError } from '../errors/IdentityDomainError';
import { ExternalRating } from '../value-objects/ExternalRating';
import { ExternalRatingProvenance } from '../value-objects/ExternalRatingProvenance';
import { GameKey } from '../value-objects/GameKey';
import { UserId } from '../value-objects/UserId';
import { ExternalGameRatingProfile } from './ExternalGameRatingProfile';
describe('ExternalGameRatingProfile', () => {
let userId: UserId;
let gameKey: GameKey;
let ratings: Map<string, ExternalRating>;
let provenance: ExternalRatingProvenance;
beforeEach(() => {
userId = UserId.fromString('user-123');
gameKey = GameKey.create('iracing');
ratings = new Map([
['safety', ExternalRating.create(gameKey, 'safety', 85.5)],
['skill', ExternalRating.create(gameKey, 'skill', 92.0)],
]);
provenance = ExternalRatingProvenance.create({
source: 'iracing',
lastSyncedAt: new Date('2024-01-01'),
verified: true,
});
});
describe('create', () => {
it('should create a valid profile', () => {
const profile = ExternalGameRatingProfile.create({
userId,
gameKey,
ratings,
provenance,
});
expect(profile.userId).toBe(userId);
expect(profile.gameKey).toBe(gameKey);
expect(profile.ratings.size).toBe(2);
expect(profile.provenance).toBe(provenance);
});
it('should allow empty ratings map', () => {
const profile = ExternalGameRatingProfile.create({
userId,
gameKey,
ratings: new Map(),
provenance,
});
expect(profile.hasRatings()).toBe(false);
expect(profile.ratings.size).toBe(0);
});
it('should throw error if rating gameKey does not match profile gameKey', () => {
const wrongGameKey = GameKey.create('assetto');
const wrongRatings = new Map([
['safety', ExternalRating.create(wrongGameKey, 'safety', 85.5)],
]);
expect(() =>
ExternalGameRatingProfile.create({
userId,
gameKey,
ratings: wrongRatings,
provenance,
})
).toThrow(IdentityDomainValidationError);
});
});
describe('restore', () => {
it('should restore profile from stored data', () => {
const profile = ExternalGameRatingProfile.restore({
userId: 'user-123',
gameKey: 'iracing',
ratings: [
{ type: 'safety', gameKey: 'iracing', value: 85.5 },
{ type: 'skill', gameKey: 'iracing', value: 92.0 },
],
provenance: {
source: 'iracing',
lastSyncedAt: new Date('2024-01-01'),
verified: true,
},
});
expect(profile.userId.toString()).toBe('user-123');
expect(profile.gameKey.toString()).toBe('iracing');
expect(profile.ratings.size).toBe(2);
expect(profile.provenance.source).toBe('iracing');
expect(profile.provenance.verified).toBe(true);
});
it('should handle missing verified flag in provenance', () => {
const profile = ExternalGameRatingProfile.restore({
userId: 'user-123',
gameKey: 'iracing',
ratings: [
{ type: 'safety', gameKey: 'iracing', value: 85.5 },
],
provenance: {
source: 'iracing',
lastSyncedAt: new Date('2024-01-01'),
},
});
expect(profile.provenance.verified).toBe(false);
});
});
describe('getRatingByType', () => {
it('should return rating for existing type', () => {
const profile = ExternalGameRatingProfile.create({
userId,
gameKey,
ratings,
provenance,
});
const rating = profile.getRatingByType('safety');
expect(rating).toBeDefined();
expect(rating?.type).toBe('safety');
expect(rating?.value).toBe(85.5);
});
it('should return undefined for non-existing type', () => {
const profile = ExternalGameRatingProfile.create({
userId,
gameKey,
ratings,
provenance,
});
const rating = profile.getRatingByType('nonexistent');
expect(rating).toBeUndefined();
});
});
describe('getRatingTypes', () => {
it('should return all rating types', () => {
const profile = ExternalGameRatingProfile.create({
userId,
gameKey,
ratings,
provenance,
});
const types = profile.getRatingTypes();
expect(types).toHaveLength(2);
expect(types).toContain('safety');
expect(types).toContain('skill');
});
});
describe('hasRatings', () => {
it('should return true when has ratings', () => {
const profile = ExternalGameRatingProfile.create({
userId,
gameKey,
ratings,
provenance,
});
expect(profile.hasRatings()).toBe(true);
});
it('should return false when no ratings', () => {
const emptyRatings = new Map<string, ExternalRating>();
const profile = ExternalGameRatingProfile.create({
userId,
gameKey,
ratings: emptyRatings,
provenance,
});
expect(profile.hasRatings()).toBe(false);
});
});
describe('updateRatings', () => {
it('should update ratings and provenance', () => {
const profile = ExternalGameRatingProfile.create({
userId,
gameKey,
ratings,
provenance,
});
const newRatings = new Map([
['safety', ExternalRating.create(gameKey, 'safety', 90.0)],
['newType', ExternalRating.create(gameKey, 'newType', 88.0)],
]);
const newProvenance = ExternalRatingProvenance.create({
source: 'iracing',
lastSyncedAt: new Date('2024-01-02'),
verified: false,
});
profile.updateRatings(newRatings, newProvenance);
expect(profile.ratings.size).toBe(2);
expect(profile.getRatingByType('safety')?.value).toBe(90.0);
expect(profile.getRatingByType('newType')?.value).toBe(88.0);
expect(profile.provenance.lastSyncedAt).toEqual(new Date('2024-01-02'));
});
it('should throw error when updating with mismatched gameKey', () => {
const profile = ExternalGameRatingProfile.create({
userId,
gameKey,
ratings,
provenance,
});
const wrongGameKey = GameKey.create('assetto');
const wrongRatings = new Map([
['safety', ExternalRating.create(wrongGameKey, 'safety', 90.0)],
]);
expect(() =>
profile.updateRatings(
wrongRatings,
ExternalRatingProvenance.create({
source: 'iracing',
lastSyncedAt: new Date(),
})
)
).toThrow(IdentityDomainValidationError);
});
});
describe('markVerified', () => {
it('should mark provenance as verified', () => {
const profile = ExternalGameRatingProfile.create({
userId,
gameKey,
ratings,
provenance: ExternalRatingProvenance.create({
source: 'iracing',
lastSyncedAt: new Date(),
verified: false,
}),
});
profile.markVerified();
expect(profile.provenance.verified).toBe(true);
});
});
describe('updateLastSyncedAt', () => {
it('should update last synced timestamp', () => {
const profile = ExternalGameRatingProfile.create({
userId,
gameKey,
ratings,
provenance,
});
const newDate = new Date('2024-01-03');
profile.updateLastSyncedAt(newDate);
expect(profile.provenance.lastSyncedAt).toEqual(newDate);
});
});
describe('toSummary', () => {
it('should return summary object', () => {
const profile = ExternalGameRatingProfile.create({
userId,
gameKey,
ratings,
provenance,
});
const summary = profile.toSummary();
expect(summary.userId).toBe('user-123');
expect(summary.gameKey).toBe('iracing');
expect(summary.ratingCount).toBe(2);
expect(summary.ratingTypes).toEqual(['safety', 'skill']);
expect(summary.source).toBe('iracing');
expect(summary.verified).toBe(true);
});
});
describe('toJSON', () => {
it('should serialize to JSON format', () => {
const profile = ExternalGameRatingProfile.create({
userId,
gameKey,
ratings,
provenance,
});
const json = profile.toJSON();
expect(json.userId).toBe('user-123');
expect(json.gameKey).toBe('iracing');
expect(json.ratings).toHaveLength(2);
expect(json.provenance.source).toBe('iracing');
expect(json.provenance.verified).toBe(true);
});
});
describe('equals', () => {
it('should return true for identical profiles', () => {
const profile1 = ExternalGameRatingProfile.create({
userId,
gameKey,
ratings,
provenance,
});
const profile2 = ExternalGameRatingProfile.create({
userId,
gameKey,
ratings,
provenance,
});
expect(profile1.equals(profile2)).toBe(true);
});
it('should return false for different userId', () => {
const profile1 = ExternalGameRatingProfile.create({
userId,
gameKey,
ratings,
provenance,
});
const profile2 = ExternalGameRatingProfile.create({
userId: UserId.fromString('user-456'),
gameKey,
ratings,
provenance,
});
expect(profile1.equals(profile2)).toBe(false);
});
it('should return false for different gameKey', () => {
const profile1 = ExternalGameRatingProfile.create({
userId,
gameKey,
ratings,
provenance,
});
const differentGameKey = GameKey.create('assetto');
const differentRatings = new Map([
['safety', ExternalRating.create(differentGameKey, 'safety', 85.5)],
]);
const profile2 = ExternalGameRatingProfile.create({
userId,
gameKey: differentGameKey,
ratings: differentRatings,
provenance,
});
expect(profile1.equals(profile2)).toBe(false);
});
it('should return false for different ratings', () => {
const profile1 = ExternalGameRatingProfile.create({
userId,
gameKey,
ratings,
provenance,
});
const differentRatings = new Map([
['safety', ExternalRating.create(gameKey, 'safety', 99.0)],
]);
const profile2 = ExternalGameRatingProfile.create({
userId,
gameKey,
ratings: differentRatings,
provenance,
});
expect(profile1.equals(profile2)).toBe(false);
});
it('should return false for different provenance', () => {
const profile1 = ExternalGameRatingProfile.create({
userId,
gameKey,
ratings,
provenance,
});
const differentProvenance = ExternalRatingProvenance.create({
source: 'different',
lastSyncedAt: new Date(),
});
const profile2 = ExternalGameRatingProfile.create({
userId,
gameKey,
ratings,
provenance: differentProvenance,
});
expect(profile1.equals(profile2)).toBe(false);
});
});
});