import { describe, it, expect, vi } from 'vitest' const { mockFindMany } = vi.hoisted(() => ({ mockFindMany: vi.fn() })) vi.mock('@/lib/prisma', () => ({ prisma: { sprint: { findMany: mockFindMany }, }, })) vi.mock('@/lib/product-access', () => ({ productAccessFilter: () => ({ some: 'filter' }), })) import { getVelocity } from '@/lib/insights/velocity' const completedAt = (iso: string) => new Date(iso) function makeSprint(id: string, goal: string, productId: string, productName: string, doneCounts: number, completedIso: string) { const tasks = Array.from({ length: doneCounts }, () => ({ status: 'DONE' })) return { id, sprint_goal: goal, completed_at: completedAt(completedIso), product: { id: productId, name: productName }, tasks, } } describe('getVelocity', () => { it('returns 3 sprints in chronological order with correct done counts', async () => { // DB returns newest-first (orderBy: completed_at desc), getVelocity reverses to oldest-first mockFindMany.mockResolvedValue([ makeSprint('s3', 'Sprint C', 'p1', 'Prod A', 3, '2024-03-01T00:00:00.000Z'), makeSprint('s2', 'Sprint B', 'p1', 'Prod A', 5, '2024-02-01T00:00:00.000Z'), makeSprint('s1', 'Sprint A', 'p1', 'Prod A', 2, '2024-01-01T00:00:00.000Z'), ]) const result = await getVelocity('user-1') expect(result.sprints).toHaveLength(3) expect(result.sprints.map(s => s.doneCount)).toEqual([2, 5, 3]) expect(result.sprints.map(s => s.sprintId)).toEqual(['s1', 's2', 's3']) }) it('deduplicates productNames from sprints', async () => { mockFindMany.mockResolvedValue([ makeSprint('s2', 'Sprint B', 'p2', 'Prod B', 1, '2024-02-01T00:00:00.000Z'), makeSprint('s1', 'Sprint A', 'p1', 'Prod A', 2, '2024-01-01T00:00:00.000Z'), ]) const result = await getVelocity('user-1') const ids = result.productNames.map(p => p.id) expect(new Set(ids).size).toBe(ids.length) expect(result.productNames).toHaveLength(2) }) it('returns empty sprints and productNames when no completed sprints exist', async () => { mockFindMany.mockResolvedValue([]) const result = await getVelocity('user-1') expect(result.sprints).toEqual([]) expect(result.productNames).toEqual([]) }) it('passes sprintsBack as take parameter', async () => { mockFindMany.mockResolvedValue([]) await getVelocity('user-1', 3) expect(mockFindMany).toHaveBeenCalledWith( expect.objectContaining({ take: 3 }), ) }) })