/** * Tests for GetUserRatingsSummaryQuery */ import { beforeEach, describe, expect, it, vi, type Mock } from 'vitest'; import { ExternalGameRatingProfile } from '../../domain/entities/ExternalGameRatingProfile'; import { RatingEvent } from '../../domain/entities/RatingEvent'; import { ExternalGameRatingRepository } from '../../domain/repositories/ExternalGameRatingRepository'; import { RatingEventRepository } from '../../domain/repositories/RatingEventRepository'; import { UserRatingRepository } from '../../domain/repositories/UserRatingRepository'; import { ExternalRating } from '../../domain/value-objects/ExternalRating'; import { ExternalRatingProvenance } from '../../domain/value-objects/ExternalRatingProvenance'; import { GameKey } from '../../domain/value-objects/GameKey'; import { RatingDelta } from '../../domain/value-objects/RatingDelta'; import { RatingDimensionKey } from '../../domain/value-objects/RatingDimensionKey'; import { RatingEventId } from '../../domain/value-objects/RatingEventId'; import { UserRating } from '../../domain/value-objects/UserRating'; import { GetUserRatingsSummaryQuery, GetUserRatingsSummaryQueryHandler } from './GetUserRatingsSummaryQuery'; import { UserId } from '../../domain/value-objects/UserId'; describe('GetUserRatingsSummaryQuery', () => { let mockUserRatingRepo: { findByUserId: Mock }; let mockExternalRatingRepo: { findByUserId: Mock }; let mockRatingEventRepo: { getAllByUserId: Mock }; let handler: GetUserRatingsSummaryQueryHandler; beforeEach(() => { mockUserRatingRepo = { findByUserId: vi.fn(), }; mockExternalRatingRepo = { findByUserId: vi.fn(), }; mockRatingEventRepo = { getAllByUserId: vi.fn(), }; handler = new GetUserRatingsSummaryQueryHandler( mockUserRatingRepo as unknown as UserRatingRepository, mockExternalRatingRepo as unknown as ExternalGameRatingRepository, mockRatingEventRepo as unknown as RatingEventRepository ); }); describe('execute', () => { it('should return summary with platform and external ratings', async () => { const userId = 'user-123'; // Mock user rating const userRating = UserRating.create(userId); mockUserRatingRepo.findByUserId.mockResolvedValue(userRating); // Mock external ratings const gameKey = GameKey.create('iracing'); const profile = ExternalGameRatingProfile.create({ userId: UserId.fromString(userId), gameKey, ratings: new Map([ ['iRating', ExternalRating.create(gameKey, 'iRating', 2200)], ['safetyRating', ExternalRating.create(gameKey, 'safetyRating', 4.5)], ]), provenance: ExternalRatingProvenance.create({ source: 'iRacing API', lastSyncedAt: new Date() }), }); mockExternalRatingRepo.findByUserId.mockResolvedValue([profile]); // Mock rating events const event = RatingEvent.create({ id: RatingEventId.generate(), userId, dimension: RatingDimensionKey.create('driving'), delta: RatingDelta.create(5), occurredAt: new Date('2024-01-01'), createdAt: new Date('2024-01-01'), source: { type: 'race', id: 'race-123' }, reason: { code: 'RACE_FINISH', summary: 'Good race', details: {} }, visibility: { public: true, redactedFields: [] }, version: 1, }); mockRatingEventRepo.getAllByUserId.mockResolvedValue([event]); const query: GetUserRatingsSummaryQuery = { userId }; const result = await handler.execute(query); expect(result.userId).toBe(userId); expect(result.platform.driving.value).toBe(50); // Default expect(result.platform.overallReputation).toBe(50); expect(result.external.iracing?.iRating).toBe(2200); expect(result.external.iracing?.safetyRating).toBe(4.5); expect(result.lastRatingEventAt).toBe('2024-01-01T00:00:00.000Z'); }); it('should handle missing user rating gracefully', async () => { const userId = 'user-123'; mockUserRatingRepo.findByUserId.mockResolvedValue(null); mockExternalRatingRepo.findByUserId.mockResolvedValue([]); mockRatingEventRepo.getAllByUserId.mockResolvedValue([]); const query: GetUserRatingsSummaryQuery = { userId }; const result = await handler.execute(query); expect(result.userId).toBe(userId); expect(result.platform.driving.value).toBe(0); expect(result.platform.overallReputation).toBe(0); expect(result.external).toEqual({}); expect(result.lastRatingEventAt).toBeUndefined(); }); it('should handle multiple external game profiles', async () => { const userId = 'user-123'; mockUserRatingRepo.findByUserId.mockResolvedValue(UserRating.create(userId)); // Multiple game profiles const iracingProfile = ExternalGameRatingProfile.create({ userId: UserId.fromString(userId), gameKey: GameKey.create('iracing'), ratings: new Map([ ['iRating', ExternalRating.create(GameKey.create('iracing'), 'iRating', 2200)], ]), provenance: ExternalRatingProvenance.create({ source: 'iRacing API', lastSyncedAt: new Date() }), }); const assettoProfile = ExternalGameRatingProfile.create({ userId: UserId.fromString(userId), gameKey: GameKey.create('assetto'), ratings: new Map([ ['rating', ExternalRating.create(GameKey.create('assetto'), 'rating', 85)], ]), provenance: ExternalRatingProvenance.create({ source: 'Assetto API', lastSyncedAt: new Date() }), }); mockExternalRatingRepo.findByUserId.mockResolvedValue([iracingProfile, assettoProfile]); mockRatingEventRepo.getAllByUserId.mockResolvedValue([]); const query: GetUserRatingsSummaryQuery = { userId }; const result = await handler.execute(query); expect(result.external.iracing.iRating).toBe(2200); expect(result.external.assetto.rating).toBe(85); }); it('should handle empty external ratings', async () => { const userId = 'user-123'; mockUserRatingRepo.findByUserId.mockResolvedValue(UserRating.create(userId)); mockExternalRatingRepo.findByUserId.mockResolvedValue([]); mockRatingEventRepo.getAllByUserId.mockResolvedValue([]); const query: GetUserRatingsSummaryQuery = { userId }; const result = await handler.execute(query); expect(result.external).toEqual({}); }); it('should use current date for timestamps when no user rating exists', async () => { const userId = 'user-123'; mockUserRatingRepo.findByUserId.mockResolvedValue(null); mockExternalRatingRepo.findByUserId.mockResolvedValue([]); mockRatingEventRepo.getAllByUserId.mockResolvedValue([]); const beforeQuery = new Date(); const query: GetUserRatingsSummaryQuery = { userId }; const result = await handler.execute(query); const afterQuery = new Date(); // Should have valid ISO date strings expect(new Date(result.createdAt).getTime()).toBeGreaterThanOrEqual(beforeQuery.getTime()); expect(new Date(result.createdAt).getTime()).toBeLessThanOrEqual(afterQuery.getTime()); expect(new Date(result.updatedAt).getTime()).toBeGreaterThanOrEqual(beforeQuery.getTime()); expect(new Date(result.updatedAt).getTime()).toBeLessThanOrEqual(afterQuery.getTime()); }); }); });