import { describe, it, expect, vi, type Mock } from 'vitest'; import { GetEntityAnalyticsQuery, type GetEntityAnalyticsInput } from './GetEntityAnalyticsQuery'; import type { IPageViewRepository } from '../repositories/IPageViewRepository'; import type { IEngagementRepository } from '@core/analytics/domain/repositories/IEngagementRepository'; import type { Logger } from '@core/shared/application'; import type { EntityType } from '../../domain/types/PageView'; describe('GetEntityAnalyticsQuery', () => { let pageViewRepository: { countByEntityId: Mock; countUniqueVisitors: Mock; }; let engagementRepository: { getSponsorClicksForEntity: Mock; }; let logger: Logger; let useCase: GetEntityAnalyticsQuery; beforeEach(() => { pageViewRepository = { countByEntityId: vi.fn(), countUniqueVisitors: vi.fn(), } as unknown as IPageViewRepository as any; engagementRepository = { getSponsorClicksForEntity: vi.fn(), } as unknown as IEngagementRepository as any; logger = { debug: vi.fn(), info: vi.fn(), warn: vi.fn(), error: vi.fn(), } as unknown as Logger; useCase = new GetEntityAnalyticsQuery( pageViewRepository as unknown as IPageViewRepository, engagementRepository as unknown as IEngagementRepository, logger, ); }); it('aggregates entity analytics and returns summary and trends', async () => { const input: GetEntityAnalyticsInput = { entityType: 'league' as EntityType, entityId: 'league-1', period: 'weekly', }; pageViewRepository.countByEntityId .mockResolvedValueOnce(100) // current period total page views .mockResolvedValueOnce(150); // previous period full page views pageViewRepository.countUniqueVisitors .mockResolvedValueOnce(40) // current period uniques .mockResolvedValueOnce(60); // previous period full uniques engagementRepository.getSponsorClicksForEntity .mockResolvedValueOnce(10) // current clicks .mockResolvedValueOnce(5); // for engagement score const result = await useCase.execute(input); expect(result.isOk()).toBe(true); const data = result.unwrap(); expect(data.entityId).toBe(input.entityId); expect(data.entityType).toBe(input.entityType); expect(data.summary.totalPageViews).toBe(100); expect(data.summary.uniqueVisitors).toBe(40); expect(data.summary.sponsorClicks).toBe(10); expect(typeof data.summary.engagementScore).toBe('number'); expect(data.summary.exposureValue).toBeGreaterThan(0); expect(data.trends.pageViewsChange).toBeDefined(); expect(data.trends.uniqueVisitorsChange).toBeDefined(); expect(data.period.start).toBeInstanceOf(Date); expect(data.period.end).toBeInstanceOf(Date); }); it('propagates repository errors', async () => { const input: GetEntityAnalyticsInput = { entityType: 'league' as EntityType, entityId: 'league-1', }; pageViewRepository.countByEntityId.mockRejectedValue(new Error('DB error')); const result = await useCase.execute(input); expect(result.isErr()).toBe(true); expect((logger.error as unknown as Mock)).toHaveBeenCalled(); }); });