import { beforeEach, describe, expect, it, mock, vi } from 'bun:test'; import { Hono } from 'hono'; const db = { group: { findMany: mock(), }, attendee: { findMany: mock(), }, classSession: { findMany: mock(), }, attendance: { findMany: mock(), }, }; mock.module('@/lib/prisma', () => ({ default: db, getPrismaClient: mock(), UnitOfWork: class { executeResult = mock(async (cb: (tx: unknown) => Promise) => cb(db)); execute = mock(async (cb: (tx: unknown) => Promise) => cb(db)); }, })); import { groupsRiskOverviewRoute } from '@/modules/analytics'; const userId = 'user-1'; const DAY_MS = 24 * 60 * 60 * 1000; const now = Date.now(); const at = (offsetDays: number) => new Date(now - offsetDays * DAY_MS); function makeApp(userValue: unknown) { const app = new Hono(); app.use('*', async (c, next) => { c.set('user', userValue as never); await next(); }); app.route('/groups-risk', groupsRiskOverviewRoute); return app; } describe('GET /groups-risk/overview', () => { beforeEach(() => { vi.clearAllMocks(); }); it('devuelve items vacĂ­os si el usuario no tiene grupos', async () => { prismaFetchGroupFindMany([]); prismaFetchAttendeeFindMany([]); prismaFetchClassSessionFindMany([]); prismaFetchAttendanceFindMany([]); const res = await makeApp({ id: userId }).request('/groups-risk/overview'); expect(res.status).toBe(200); expect(await res.json()).toEqual({ items: [] }); }); it('marca HIGH a un grupo con un alumno con 3 ausencias consecutivas', async () => { prismaFetchGroupFindMany([ { id: 'group-1', name: 'Funcional' }, { id: 'group-2', name: 'Yoga' }, ]); prismaFetchAttendeeFindMany([ { id: 'a-1', groupId: 'group-1' }, { id: 'a-2', groupId: 'group-2' }, ]); prismaFetchClassSessionFindMany([ { id: 's1', groupId: 'group-1', startsAt: at(-1) }, { id: 's2', groupId: 'group-1', startsAt: at(-2) }, { id: 's3', groupId: 'group-1', startsAt: at(-3) }, { id: 's4', groupId: 'group-2', startsAt: at(-1) }, ]); prismaFetchAttendanceFindMany([ attendance('a-1', 'ABSENT', at(-1)), attendance('a-1', 'ABSENT', at(-2)), attendance('a-1', 'ABSENT', at(-3)), attendance('a-2', 'PRESENT', at(-1)), ]); const res = await makeApp({ id: userId }).request('/groups-risk/overview'); expect(res.status).toBe(200); expect(await res.json()).toEqual({ items: [ { groupId: 'group-1', groupName: 'Funcional', riskLevel: 'HIGH' }, { groupId: 'group-2', groupName: 'Yoga', riskLevel: 'NONE' }, ], }); }); }); function prismaFetchGroupFindMany(rows: unknown[]) { db.group.findMany.mockResolvedValue(rows); } function prismaFetchAttendeeFindMany(rows: unknown[]) { db.attendee.findMany.mockResolvedValue(rows); } function prismaFetchClassSessionFindMany(rows: unknown[]) { db.classSession.findMany.mockResolvedValue(rows); } function prismaFetchAttendanceFindMany(rows: unknown[]) { db.attendance.findMany.mockResolvedValue(rows); } function attendance(attendeeId: string, status: string, startsAt: string) { return { attendeeId, status, classSession: { startsAt } }; }