rating
This commit is contained in:
410
core/identity/domain/entities/ExternalGameRatingProfile.test.ts
Normal file
410
core/identity/domain/entities/ExternalGameRatingProfile.test.ts
Normal file
@@ -0,0 +1,410 @@
|
||||
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);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user