import { describe, expect, it, vi, beforeEach } from 'vitest'; import { RecomputeUserRatingSnapshotUseCase } from './RecomputeUserRatingSnapshotUseCase'; import { IRatingEventRepository } from '../../domain/repositories/IRatingEventRepository'; import { IUserRatingRepository } from '../../domain/repositories/IUserRatingRepository'; import { UserRating } from '../../domain/value-objects/UserRating'; import { RatingEvent } from '../../domain/entities/RatingEvent'; import { RatingEventId } from '../../domain/value-objects/RatingEventId'; import { RatingDimensionKey } from '../../domain/value-objects/RatingDimensionKey'; import { RatingDelta } from '../../domain/value-objects/RatingDelta'; describe('RecomputeUserRatingSnapshotUseCase', () => { let mockEventRepo: Partial; let mockRatingRepo: Partial; beforeEach(() => { mockEventRepo = { getAllByUserId: vi.fn().mockResolvedValue([]), }; mockRatingRepo = { save: vi.fn(), }; }); it('should be constructed with repositories', () => { const useCase = new RecomputeUserRatingSnapshotUseCase( mockEventRepo as IRatingEventRepository, mockRatingRepo as IUserRatingRepository, ); expect(useCase).toBeInstanceOf(RecomputeUserRatingSnapshotUseCase); }); it('should compute snapshot from empty event list', async () => { const useCase = new RecomputeUserRatingSnapshotUseCase( mockEventRepo as IRatingEventRepository, mockRatingRepo as IUserRatingRepository, ); const result = await useCase.execute({ userId: 'user-1' }); expect(result.snapshot.userId).toBe('user-1'); expect(result.snapshot.driver.value).toBe(50); // Default expect(mockEventRepo.getAllByUserId).toHaveBeenCalledWith('user-1'); expect(mockRatingRepo.save).toHaveBeenCalled(); }); it('should compute snapshot from events', async () => { const events = [ RatingEvent.create({ id: RatingEventId.generate(), userId: 'user-1', dimension: RatingDimensionKey.create('driving'), delta: RatingDelta.create(5), occurredAt: new Date('2024-01-01T10:00:00Z'), createdAt: new Date('2024-01-01T10:00:00Z'), source: { type: 'race', id: 'race-1' }, reason: { code: 'TEST', summary: 'Test', details: {} }, visibility: { public: true, redactedFields: [] }, version: 1, }), ]; mockEventRepo.getAllByUserId = vi.fn().mockResolvedValue(events); const useCase = new RecomputeUserRatingSnapshotUseCase( mockEventRepo as IRatingEventRepository, mockRatingRepo as IUserRatingRepository, ); const result = await useCase.execute({ userId: 'user-1' }); expect(result.snapshot.userId).toBe('user-1'); expect(result.snapshot.driver.value).toBeGreaterThan(50); // Should have increased expect(mockEventRepo.getAllByUserId).toHaveBeenCalledWith('user-1'); expect(mockRatingRepo.save).toHaveBeenCalled(); }); it('should return proper DTO format', async () => { const useCase = new RecomputeUserRatingSnapshotUseCase( mockEventRepo as IRatingEventRepository, mockRatingRepo as IUserRatingRepository, ); const result = await useCase.execute({ userId: 'user-1' }); // Check DTO structure expect(result.snapshot).toHaveProperty('userId'); expect(result.snapshot).toHaveProperty('driver'); expect(result.snapshot).toHaveProperty('admin'); expect(result.snapshot).toHaveProperty('steward'); expect(result.snapshot).toHaveProperty('trust'); expect(result.snapshot).toHaveProperty('fairness'); expect(result.snapshot).toHaveProperty('overallReputation'); expect(result.snapshot).toHaveProperty('createdAt'); expect(result.snapshot).toHaveProperty('updatedAt'); // Check dimension structure expect(result.snapshot.driver).toEqual({ value: expect.any(Number), confidence: expect.any(Number), sampleSize: expect.any(Number), trend: expect.any(String), lastUpdated: expect.any(String), }); // Check dates are ISO strings expect(result.snapshot.createdAt).toMatch(/^\d{4}-\d{2}-\d{2}T/); expect(result.snapshot.updatedAt).toMatch(/^\d{4}-\d{2}-\d{2}T/); }); it('should handle calculatorVersion in DTO', async () => { // Create a rating with calculatorVersion const rating = UserRating.create('user-1'); const updated = rating.updateDriverRating(75); mockRatingRepo.save = vi.fn().mockResolvedValue(updated); const useCase = new RecomputeUserRatingSnapshotUseCase( mockEventRepo as IRatingEventRepository, mockRatingRepo as IUserRatingRepository, ); const result = await useCase.execute({ userId: 'user-1' }); // Should have calculatorVersion expect(result.snapshot.calculatorVersion).toBeDefined(); }); });