/** * Tests for GetTeamRatingsSummaryQuery */ import { beforeEach, describe, expect, it, vi, type Mock } from 'vitest'; import { TeamRatingEvent } from '../../domain/entities/TeamRatingEvent'; import type { TeamRatingEventRepository } from '../../domain/repositories/TeamRatingEventRepository'; import type { TeamRatingRepository } from '../../domain/repositories/TeamRatingRepository'; import { TeamRatingSnapshot } from '../../domain/services/TeamRatingSnapshotCalculator'; import { TeamRatingDelta } from '../../domain/value-objects/TeamRatingDelta'; import { TeamRatingDimensionKey } from '../../domain/value-objects/TeamRatingDimensionKey'; import { TeamRatingEventId } from '../../domain/value-objects/TeamRatingEventId'; import { TeamRatingValue } from '../../domain/value-objects/TeamRatingValue'; import { GetTeamRatingsSummaryQuery, GetTeamRatingsSummaryQueryHandler } from './GetTeamRatingsSummaryQuery'; describe('GetTeamRatingsSummaryQuery', () => { let mockTeamRatingRepo: { findByTeamId: Mock }; let mockRatingEventRepo: { getAllByTeamId: Mock }; let handler: GetTeamRatingsSummaryQueryHandler; beforeEach(() => { mockTeamRatingRepo = { findByTeamId: vi.fn(), }; mockRatingEventRepo = { getAllByTeamId: vi.fn(), }; handler = new GetTeamRatingsSummaryQueryHandler( mockTeamRatingRepo as unknown as TeamRatingRepository, mockRatingEventRepo as unknown as TeamRatingEventRepository ); }); describe('execute', () => { it('should return summary with platform ratings', async () => { const teamId = 'team-123'; // Mock team rating snapshot const snapshot: TeamRatingSnapshot = { teamId, driving: TeamRatingValue.create(75), adminTrust: TeamRatingValue.create(60), overall: 70.5, lastUpdated: new Date('2024-01-01T10:00:00Z'), eventCount: 5, }; mockTeamRatingRepo.findByTeamId.mockResolvedValue(snapshot); // Mock rating events const event = TeamRatingEvent.create({ id: TeamRatingEventId.generate(), teamId, dimension: TeamRatingDimensionKey.create('driving'), delta: TeamRatingDelta.create(10), occurredAt: new Date('2024-01-01T10:00:00Z'), createdAt: new Date('2024-01-01T10:00:00Z'), source: { type: 'race', id: 'race-123' }, reason: { code: 'RACE_FINISH' }, visibility: { public: true }, version: 1, }); mockRatingEventRepo.getAllByTeamId.mockResolvedValue([event]); const query: GetTeamRatingsSummaryQuery = { teamId }; const result = await handler.execute(query); expect(result.teamId).toBe(teamId); expect(result.platform.driving.value).toBe(75); expect(result.platform.adminTrust.value).toBe(60); expect(result.platform.overall).toBe(70.5); expect(result.lastRatingEventAt).toBe('2024-01-01T10:00:00.000Z'); }); it('should handle missing team rating gracefully', async () => { const teamId = 'team-123'; mockTeamRatingRepo.findByTeamId.mockResolvedValue(null); mockRatingEventRepo.getAllByTeamId.mockResolvedValue([]); const query: GetTeamRatingsSummaryQuery = { teamId }; const result = await handler.execute(query); expect(result.teamId).toBe(teamId); expect(result.platform.driving.value).toBe(0); expect(result.platform.adminTrust.value).toBe(0); expect(result.platform.overall).toBe(0); expect(result.lastRatingEventAt).toBeUndefined(); }); it('should handle multiple events and find latest', async () => { const teamId = 'team-123'; const snapshot: TeamRatingSnapshot = { teamId, driving: TeamRatingValue.create(80), adminTrust: TeamRatingValue.create(70), overall: 77, lastUpdated: new Date('2024-01-02T10:00:00Z'), eventCount: 10, }; mockTeamRatingRepo.findByTeamId.mockResolvedValue(snapshot); // Multiple events with different timestamps const events = [ TeamRatingEvent.create({ id: TeamRatingEventId.generate(), teamId, dimension: TeamRatingDimensionKey.create('driving'), delta: TeamRatingDelta.create(5), occurredAt: new Date('2024-01-01T08:00:00Z'), createdAt: new Date('2024-01-01T08:00:00Z'), source: { type: 'race', id: 'race-1' }, reason: { code: 'RACE_FINISH' }, visibility: { public: true }, version: 1, }), TeamRatingEvent.create({ id: TeamRatingEventId.generate(), teamId, dimension: TeamRatingDimensionKey.create('driving'), delta: TeamRatingDelta.create(10), occurredAt: new Date('2024-01-02T10:00:00Z'), createdAt: new Date('2024-01-02T10:00:00Z'), source: { type: 'race', id: 'race-2' }, reason: { code: 'RACE_FINISH' }, visibility: { public: true }, version: 1, }), ]; mockRatingEventRepo.getAllByTeamId.mockResolvedValue(events); const query: GetTeamRatingsSummaryQuery = { teamId }; const result = await handler.execute(query); expect(result.lastRatingEventAt).toBe('2024-01-02T10:00:00.000Z'); }); it('should calculate confidence and sampleSize from event count', async () => { const teamId = 'team-123'; const snapshot: TeamRatingSnapshot = { teamId, driving: TeamRatingValue.create(65), adminTrust: TeamRatingValue.create(55), overall: 62, lastUpdated: new Date('2024-01-01T10:00:00Z'), eventCount: 8, }; mockTeamRatingRepo.findByTeamId.mockResolvedValue(snapshot); mockRatingEventRepo.getAllByTeamId.mockResolvedValue([]); const query: GetTeamRatingsSummaryQuery = { teamId }; const result = await handler.execute(query); // Confidence should be min(1, eventCount/10) = 0.8 expect(result.platform.driving.confidence).toBe(0.8); expect(result.platform.driving.sampleSize).toBe(8); expect(result.platform.adminTrust.confidence).toBe(0.8); expect(result.platform.adminTrust.sampleSize).toBe(8); }); it('should handle empty events array', async () => { const teamId = 'team-123'; const snapshot: TeamRatingSnapshot = { teamId, driving: TeamRatingValue.create(50), adminTrust: TeamRatingValue.create(50), overall: 50, lastUpdated: new Date('2024-01-01T10:00:00Z'), eventCount: 0, }; mockTeamRatingRepo.findByTeamId.mockResolvedValue(snapshot); mockRatingEventRepo.getAllByTeamId.mockResolvedValue([]); const query: GetTeamRatingsSummaryQuery = { teamId }; const result = await handler.execute(query); expect(result.platform.driving.confidence).toBe(0); expect(result.platform.driving.sampleSize).toBe(0); expect(result.platform.driving.trend).toBe('stable'); expect(result.lastRatingEventAt).toBeUndefined(); }); }); });