import { Repository } from 'typeorm'; import { StravaActivityEntity } from '../database/entities'; import { AnalyticsService } from './analytics.service'; describe('AnalyticsService', () => { const createService = (activities: StravaActivityEntity[]) => { const repository = { find: jest.fn().mockResolvedValue(activities), } as unknown as Repository; return { service: new AnalyticsService(repository), repository, }; }; const activity = ( input: Partial, ): StravaActivityEntity => ({ id: input.id ?? `activity-${input.stravaActivityId ?? '1'}`, stravaActivityId: input.stravaActivityId ?? '1', name: input.name ?? 'Morning Ride', sportType: input.sportType ?? 'Ride', startDate: input.startDate ?? new Date(), distance: input.distance ?? 10000, movingTime: input.movingTime ?? 1800, totalElevationGain: input.totalElevationGain ?? 120, calories: input.calories ?? 400, averageSpeed: input.averageSpeed ?? 5.5, averageHeartrate: input.averageHeartrate ?? null, averageWatts: input.averageWatts ?? null, averageCadence: input.averageCadence ?? null, }) as StravaActivityEntity; it('returns empty dashboard buckets when there are no activities', async () => { const { service } = createService([]); const dashboard = await service.getDashboard(12); expect(dashboard.totals.activityCount).toBe(0); expect(dashboard.availableSports).toEqual([]); expect(dashboard.weekly).toHaveLength(12); expect(dashboard.sports).toEqual([]); expect(dashboard.recentActivities).toEqual([]); }); it('aggregates totals, averages, and sport summaries', async () => { const { service } = createService([ activity({ stravaActivityId: '1', sportType: 'Ride', distance: 20000, movingTime: 3600, totalElevationGain: 200, calories: 700, averageHeartrate: 140, averageWatts: 210, }), activity({ stravaActivityId: '2', sportType: 'Run', distance: 5000, movingTime: 1500, totalElevationGain: 40, calories: 350, averageHeartrate: 150, }), ]); const dashboard = await service.getDashboard(12); expect(dashboard.totals).toEqual({ activityCount: 2, distanceMeters: 25000, movingTimeSeconds: 5100, elevationGainMeters: 240, calories: 1050, }); expect(dashboard.averages.heartRate).toBe(145); expect(dashboard.averages.watts).toBe(210); expect(dashboard.sports.map((sport) => sport.sportType)).toEqual([ 'Ride', 'Run', ]); }); it('clamps the requested number of weeks', async () => { const { service } = createService([]); await expect(service.getDashboard(999)).resolves.toMatchObject({ weeks: 104, }); await expect(service.getDashboard(0)).resolves.toMatchObject({ weeks: 1, }); }); it('filters dashboard values by sport type while keeping available sports', async () => { const { service } = createService([ activity({ stravaActivityId: '1', sportType: 'Ride', distance: 20000, movingTime: 3600, }), activity({ stravaActivityId: '2', sportType: 'Run', distance: 5000, movingTime: 1500, }), ]); const dashboard = await service.getDashboard(12, 'Run'); expect(dashboard.selectedSportType).toBe('Run'); expect(dashboard.availableSports).toEqual(['Ride', 'Run']); expect(dashboard.totals.activityCount).toBe(1); expect(dashboard.totals.distanceMeters).toBe(5000); expect(dashboard.sports.map((sport) => sport.sportType)).toEqual(['Run']); }); });