import { Test, TestingModule } from '@nestjs/testing'; import { vi } from 'vitest'; import { AnalyticsController } from './AnalyticsController'; import { AnalyticsService } from './AnalyticsService'; import type { Response } from 'express'; import { EntityType, VisitorType } from '@core/analytics/domain/types/PageView'; import { EngagementAction, EngagementEntityType } from '@core/analytics/domain/types/EngagementEvent'; import type { RecordEngagementOutputDTO } from './dtos/RecordEngagementOutputDTO'; import type { RecordPageViewOutputDTO } from './dtos/RecordPageViewOutputDTO'; describe('AnalyticsController', () => { let controller: AnalyticsController; let service: ReturnType>; beforeEach(() => { service = { recordPageView: vi.fn(), recordEngagement: vi.fn(), getDashboardData: vi.fn(), getAnalyticsMetrics: vi.fn(), } as any; controller = new AnalyticsController(service); }); describe('recordPageView', () => { it('should record a page view and return 201', async () => { const input = { entityType: EntityType.RACE, entityId: 'race-123', visitorType: VisitorType.ANONYMOUS, sessionId: 'session-456', visitorId: 'visitor-789', referrer: 'https://example.com', userAgent: 'Mozilla/5.0', country: 'US', }; const dto: RecordPageViewOutputDTO = { pageViewId: 'pv-123' }; service.recordPageView.mockResolvedValue(dto); const mockRes: ReturnType> = { status: vi.fn().mockReturnThis(), json: vi.fn(), } as unknown as ReturnType>; await controller.recordPageView(input, mockRes); expect(service.recordPageView).toHaveBeenCalledWith(input); expect(mockRes.status).toHaveBeenCalledWith(201); expect(mockRes.json).toHaveBeenCalledWith(dto); }); }); describe('recordEngagement', () => { it('should record an engagement and return 201', async () => { const input = { action: EngagementAction.CLICK_SPONSOR_LOGO, entityType: EngagementEntityType.RACE, entityId: 'race-123', actorType: 'driver' as const, sessionId: 'session-456', actorId: 'actor-789', metadata: { key: 'value' }, }; const dto: RecordEngagementOutputDTO = { eventId: 'event-123', engagementWeight: 10 }; service.recordEngagement.mockResolvedValue(dto); const mockRes: ReturnType> = { status: vi.fn().mockReturnThis(), json: vi.fn(), } as unknown as ReturnType>; await controller.recordEngagement(input, mockRes); expect(service.recordEngagement).toHaveBeenCalledWith(input); expect(mockRes.status).toHaveBeenCalledWith(201); expect(mockRes.json).toHaveBeenCalledWith(dto); }); }); describe('getDashboardData', () => { it('should return dashboard data', async () => { const dto = { totalUsers: 100, activeUsers: 50, totalRaces: 20, totalLeagues: 5, }; service.getDashboardData.mockResolvedValue(dto); const result = await controller.getDashboardData(); expect(service.getDashboardData).toHaveBeenCalled(); expect(result).toEqual(dto); }); }); describe('getAnalyticsMetrics', () => { it('should return analytics metrics', async () => { const dto = { pageViews: 1000, uniqueVisitors: 500, averageSessionDuration: 300, bounceRate: 0.4, }; service.getAnalyticsMetrics.mockResolvedValue(dto); const result = await controller.getAnalyticsMetrics(); expect(service.getAnalyticsMetrics).toHaveBeenCalled(); expect(result).toEqual(dto); }); }); });