import { describe, expect, it, vi, beforeEach } from 'vitest' import { ActividadLocalService } from './actividad-local.service.js' // Mock de Prisma: solo operaciones de CRUD sobre ActividadLocal const mockActividades: Record = {} let consecutivo = 0 vi.mock('./prisma.js', () => ({ prisma: { actividadLocal: { create: vi.fn(async ({ data }: { data: Record }) => { consecutivo++ const id = `act_${consecutivo}` const fila = { id, titulo: data.titulo as string, descripcion: (data.descripcion as string) ?? null, usuarioId: data.usuarioId as string, leadCapturaId: (data.leadCapturaId as string) ?? null, fechaVencimiento: (data.fechaVencimiento as Date) ?? null, fechaCierre: null, estado: (data.estado as string) ?? 'ABIERTA', tipo: (data.tipo as string) ?? 'SEGUIMIENTO', createdAt: new Date(), updatedAt: new Date(), } mockActividades[id] = fila return fila }), findMany: vi.fn(async ({ where }: { where: Record }) => { let filas = Object.values(mockActividades) if (where.usuarioId !== undefined) { filas = filas.filter((f) => f.usuarioId === where.usuarioId) } if (where.estado !== undefined) { filas = filas.filter((f) => f.estado === where.estado) } if (where.fechaVencimiento !== undefined && typeof where.fechaVencimiento === 'object') { const fv = where.fechaVencimiento as Record if (fv.gte !== undefined) filas = filas.filter((f) => f.fechaVencimiento !== null && f.fechaVencimiento >= fv.gte) if (fv.lt !== undefined) filas = filas.filter((f) => f.fechaVencimiento !== null && f.fechaVencimiento < fv.lt) } return filas }), count: vi.fn(async ({ where }: { where: Record }) => { let filas = Object.values(mockActividades) if (where.usuarioId !== undefined) { filas = filas.filter((f) => f.usuarioId === where.usuarioId) } if (where.estado !== undefined) { filas = filas.filter((f) => f.estado === where.estado) } if (where.fechaVencimiento !== undefined && typeof where.fechaVencimiento === 'object') { const fv = where.fechaVencimiento as Record if (fv.gte !== undefined && fv.lt !== undefined) { filas = filas.filter((f) => f.fechaVencimiento !== null && f.fechaVencimiento >= fv.gte && f.fechaVencimiento < fv.lt) } } return filas.length }), findUnique: vi.fn(async ({ where }: { where: { id: string } }) => mockActividades[where.id] ?? null), update: vi.fn(async ({ where, data }: { where: { id: string }; data: Record }) => { const fila = mockActividades[where.id] if (!fila) throw new Error('Not found') Object.assign(fila, data, { updatedAt: new Date() }) return fila }), delete: vi.fn(async ({ where }: { where: { id: string } }) => { const fila = mockActividades[where.id] if (!fila) throw new Error('Not found') delete mockActividades[where.id] return fila }), }, }, })) beforeEach(() => { Object.keys(mockActividades).forEach((k) => delete mockActividades[k]) consecutivo = 0 vi.clearAllMocks() }) describe('ActividadLocalService', () => { const svc = new ActividadLocalService() it('crearActividad crea una actividad con valores por defecto', async () => { const act = await svc.crearActividad({ titulo: 'Llamar a Juan', usuarioId: 'u1' }) expect(act.id).toBe('act_1') expect(act.titulo).toBe('Llamar a Juan') expect(act.estado).toBe('ABIERTA') expect(act.tipo).toBe('SEGUIMIENTO') expect(act.descripcion).toBeNull() expect(act.leadCapturaId).toBeNull() }) it('crearActividad con campos opcionales', async () => { const fv = new Date('2026-09-10T10:00:00') const act = await svc.crearActividad({ titulo: 'Reunión', descripcion: 'Seguimiento del lead', usuarioId: 'u2', leadCapturaId: 'loc_123', fechaVencimiento: fv, tipo: 'REUNION', }) expect(act.descripcion).toBe('Seguimiento del lead') expect(act.leadCapturaId).toBe('loc_123') expect(act.fechaVencimiento).toBe(fv) expect(act.tipo).toBe('REUNION') }) it('listarActividades filtra por usuario', async () => { await svc.crearActividad({ titulo: 'A1', usuarioId: 'u1' }) await svc.crearActividad({ titulo: 'A2', usuarioId: 'u2' }) await svc.crearActividad({ titulo: 'A3', usuarioId: 'u1' }) const lista = await svc.listarActividades('u1') expect(lista).toHaveLength(2) expect(lista.every((a) => a.usuarioId === 'u1')).toBe(true) }) it('listarActividades con soloAbiertas', async () => { await svc.crearActividad({ titulo: 'Abierta', usuarioId: 'u1' }) const cerrada = await svc.crearActividad({ titulo: 'Cerrada', usuarioId: 'u1' }) await svc.cerrarActividad(cerrada.id) const abiertas = await svc.listarActividades('u1', { soloAbiertas: true }) expect(abiertas).toHaveLength(1) expect(abiertas[0].titulo).toBe('Abierta') }) it('cerrarActividad marca estado CERRADA y fechaCierre', async () => { const act = await svc.crearActividad({ titulo: 'Test', usuarioId: 'u1' }) const cerrada = await svc.cerrarActividad(act.id) expect(cerrada.estado).toBe('CERRADA') expect(cerrada.fechaCierre).toBeInstanceOf(Date) }) it('cerrarActividad lanza error si no existe', async () => { await expect(svc.cerrarActividad('no_existe')).rejects.toThrow('no encontrada') }) it('eliminarActividad borra la actividad', async () => { const act = await svc.crearActividad({ titulo: 'Borrar', usuarioId: 'u1' }) await svc.eliminarActividad(act.id) const todas = await svc.listarActividades('u1') expect(todas).toHaveLength(0) }) it('eliminarActividad lanza error si no existe', async () => { await expect(svc.eliminarActividad('no_existe')).rejects.toThrow('no encontrada') }) it('contarActividadesAbiertas sin filtro de usuario', async () => { await svc.crearActividad({ titulo: 'A1', usuarioId: 'u1' }) await svc.crearActividad({ titulo: 'A2', usuarioId: 'u2' }) expect(await svc.contarActividadesAbiertas()).toBe(2) }) it('contarActividadesAbiertas con filtro de usuario', async () => { await svc.crearActividad({ titulo: 'A1', usuarioId: 'u1' }) await svc.crearActividad({ titulo: 'A2', usuarioId: 'u2' }) expect(await svc.contarActividadesAbiertas('u1')).toBe(1) }) it('contarActividadesHoy cuenta solo las de hoy', async () => { const hoy = new Date('2026-09-07T10:00:00') const ayer = new Date('2026-09-06T10:00:00') await svc.crearActividad({ titulo: 'Hoy', usuarioId: 'u1', fechaVencimiento: hoy }) await svc.crearActividad({ titulo: 'Ayer', usuarioId: 'u1', fechaVencimiento: ayer }) // contarActividadesHoy usa new Date() internamente, mockeamos el Date global const DateOrig = Date vi.stubGlobal('Date', class extends DateOrig { constructor(...args: unknown[]) { if (args.length === 0) super('2026-09-07T12:00:00') else super(...args) } }) const count = await svc.contarActividadesHoy('u1') expect(count).toBe(1) vi.unstubAllGlobals() }) it('actividadesVencidas retorna solo las que vencieron y están abiertas', async () => { const pasada = new Date('2026-09-06T10:00:00') const futura = new Date('2026-09-10T10:00:00') await svc.crearActividad({ titulo: 'Pasada', usuarioId: 'u1', fechaVencimiento: pasada }) await svc.crearActividad({ titulo: 'Futura', usuarioId: 'u1', fechaVencimiento: futura }) // actividadesVencidas compara con new Date() → 2026-09-07T12:00:00 const DateOrig = Date vi.stubGlobal('Date', class extends DateOrig { constructor(...args: unknown[]) { if (args.length === 0) super('2026-09-07T12:00:00') else super(...args) } }) const vencidas = await svc.actividadesVencidas('u1') expect(vencidas).toHaveLength(1) expect(vencidas[0].titulo).toBe('Pasada') vi.unstubAllGlobals() }) it('actividadesVencidas con filtro de usuario', async () => { const pasada = new Date('2026-09-06T10:00:00') await svc.crearActividad({ titulo: 'U1', usuarioId: 'u1', fechaVencimiento: pasada }) await svc.crearActividad({ titulo: 'U2', usuarioId: 'u2', fechaVencimiento: pasada }) const DateOrig = Date vi.stubGlobal('Date', class extends DateOrig { constructor(...args: unknown[]) { if (args.length === 0) super('2026-09-07T12:00:00') else super(...args) } }) const vencidas = await svc.actividadesVencidas('u1') expect(vencidas).toHaveLength(1) expect(vencidas[0].titulo).toBe('U1') vi.unstubAllGlobals() }) })