410 lines
11 KiB
TypeScript
410 lines
11 KiB
TypeScript
import { ExternalGameRatingProfile } from './ExternalGameRatingProfile';
|
|
import { UserId } from '../value-objects/UserId';
|
|
import { GameKey } from '../value-objects/GameKey';
|
|
import { ExternalRating } from '../value-objects/ExternalRating';
|
|
import { ExternalRatingProvenance } from '../value-objects/ExternalRatingProvenance';
|
|
import { IdentityDomainValidationError } from '../errors/IdentityDomainError';
|
|
|
|
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);
|
|
});
|
|
});
|
|
}); |