import { beforeEach, describe, expect, it, vi } from 'vitest' vi.mock('../../lib/prisma', () => ({ prisma: { reservation: { count: vi.fn(), aggregate: vi.fn(), findMany: vi.fn(), groupBy: vi.fn(), }, vehicle: { count: vi.fn(), }, customer: { count: vi.fn(), }, subscription: { findUnique: vi.fn(), }, accountingSettings: { findUnique: vi.fn(), }, }, })) vi.mock('../../services/financialReportService', () => ({ generateFinancialReport: vi.fn(), toCsv: vi.fn(), })) import { prisma } from '../../lib/prisma' import { generateFinancialReport, toCsv } from '../../services/financialReportService' import { getDashboard, getReport, getSources, getSummary } from './analytics.service' describe('analytics.service', () => { beforeEach(() => { vi.clearAllMocks() vi.setSystemTime(new Date('2026-06-08T12:00:00.000Z')) }) it('summarizes reservations, available vehicles, revenue, and customers for a day window', async () => { vi.mocked(prisma.reservation.count).mockResolvedValue(7 as never) vi.mocked(prisma.vehicle.count).mockResolvedValue(12 as never) vi.mocked(prisma.reservation.aggregate).mockResolvedValue({ _sum: { totalAmount: 4800 } } as never) vi.mocked(prisma.customer.count).mockResolvedValue(42 as never) const result = await getSummary('company_1', '14d') expect(prisma.reservation.count).toHaveBeenCalledWith({ where: { companyId: 'company_1', createdAt: { gte: new Date('2026-05-25T12:00:00.000Z') } }, }) expect(prisma.vehicle.count).toHaveBeenCalledWith({ where: { companyId: 'company_1', status: 'AVAILABLE' } }) expect(result).toEqual({ totalReservations: 7, activeVehicles: 12, totalRevenue: 4800, totalCustomers: 42, period: '14d', }) }) it('builds dashboard KPIs, percentage deltas, recent reservation cards, source breakdown, and subscription summary', async () => { vi.mocked(prisma.reservation.count) .mockResolvedValueOnce(10 as never) .mockResolvedValueOnce(5 as never) vi.mocked(prisma.vehicle.count) .mockResolvedValueOnce(4 as never) .mockResolvedValueOnce(0 as never) vi.mocked(prisma.customer.count) .mockResolvedValueOnce(20 as never) .mockResolvedValueOnce(25 as never) vi.mocked(prisma.reservation.aggregate) .mockResolvedValueOnce({ _sum: { totalAmount: 9000 } } as never) .mockResolvedValueOnce({ _sum: { totalAmount: 3000 } } as never) vi.mocked(prisma.reservation.findMany).mockResolvedValue([ { id: 'reservation_abc12345', contractNumber: null, customer: { firstName: 'Nora', lastName: 'Driver' }, vehicle: { make: 'Dacia', model: 'Duster' }, startDate: new Date('2026-06-10T00:00:00.000Z'), endDate: new Date('2026-06-12T00:00:00.000Z'), status: 'CONFIRMED', totalAmount: 1200, }, ] as never) vi.mocked(prisma.reservation.groupBy).mockResolvedValue([ { source: 'CARPLACE', _count: { id: 3 }, _sum: { totalAmount: 3600 } }, { source: 'DIRECT', _count: { id: 2 }, _sum: { totalAmount: null } }, ] as never) vi.mocked(prisma.subscription.findUnique).mockResolvedValue({ status: 'ACTIVE', plan: 'PRO', trialEndAt: new Date('2026-06-30T00:00:00.000Z'), } as never) const result = await getDashboard('company_1') expect(result.kpis).toEqual({ totalBookings: 10, activeVehicles: 4, monthlyRevenue: 9000, totalCustomers: 20, bookingsChange: 100, vehiclesChange: 100, revenueChange: 200, customersChange: -20, }) expect(result.recentReservations).toEqual([expect.objectContaining({ id: 'reservation_abc12345', bookingRef: 'ABC12345', customerName: 'Nora Driver', vehicleName: 'Dacia Duster', status: 'CONFIRMED', totalAmount: 1200, })]) expect(result.sourceBreakdown).toEqual([ { source: 'CARPLACE', count: 3, revenue: 3600 }, { source: 'DIRECT', count: 2, revenue: 0 }, ]) expect(result.subscription).toEqual({ status: 'ACTIVE', planName: 'PRO', trialEndsAt: new Date('2026-06-30T00:00:00.000Z'), }) }) it('returns source groups straight from reservation grouping', async () => { vi.mocked(prisma.reservation.groupBy).mockResolvedValue([{ source: 'DIRECT', _count: { id: 2 } }] as never) await expect(getSources('company_1')).resolves.toEqual([{ source: 'DIRECT', _count: { id: 2 } }]) expect(prisma.reservation.groupBy).toHaveBeenCalledWith({ by: ['source'], where: { companyId: 'company_1' }, _count: { id: true } }) }) it('uses explicit report dates and emits CSV only when requested', async () => { vi.mocked(prisma.accountingSettings.findUnique).mockResolvedValue({ reportingPeriod: 'ANNUAL' } as never) vi.mocked(generateFinancialReport).mockResolvedValue({ rows: [{ label: 'Revenue', amount: 1000 }] } as never) vi.mocked(toCsv).mockReturnValue('label,amount\nRevenue,1000') const result = await getReport('company_1', { from: '2026-05-01', to: '2026-05-31', format: 'CSV', }) expect(generateFinancialReport).toHaveBeenCalledWith('company_1', new Date('2026-05-01'), new Date('2026-05-31')) expect(toCsv).toHaveBeenCalledWith([{ label: 'Revenue', amount: 1000 }]) expect(result.csv).toBe('label,amount\nRevenue,1000') }) })