Backend: mi-dia siempre local, vocabulario canónico, AplicacionPago con usuario Mi día: modal español real, cita desde modal, score falso eliminado Cotizador: barra sticky, persistencia local con folio Alta Clientes: redirect eliminado, foto conectada, asesor desde sesión, refs dinámicas Cobranza: contrato Mora arreglado, link a Plan, abono próximo pago, WhatsApp Landing: contactos locales, ModalContacto eliminado, ModoTrabajo eliminado Dashboards: MiTablero unificado, funnel con descartados, drill-down, rango fechas Chat FRASA: corpus 5 desarrollos, 6 herramientas, historial conversaciones 69 archivos, 7 apps construidas
153 lines
5.2 KiB
TypeScript
153 lines
5.2 KiB
TypeScript
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|
import type { Request, Response } from 'express'
|
|
import { AppError } from '../utils/errors.js'
|
|
|
|
// ---------- Prisma y Monday mockeados (los tests no tocan BD ni red) ----------
|
|
|
|
const prismaMock = vi.hoisted(() => ({
|
|
usuario: {
|
|
findUnique: vi.fn(),
|
|
findMany: vi.fn(),
|
|
},
|
|
leadCaptura: {
|
|
findMany: vi.fn(),
|
|
},
|
|
cliente: {
|
|
findMany: vi.fn(),
|
|
},
|
|
actividadLocal: {
|
|
findMany: vi.fn(),
|
|
},
|
|
}))
|
|
|
|
const mondayMock = vi.hoisted(() => ({
|
|
getUsuariosWorkspace: vi.fn(),
|
|
getLeads: vi.fn(),
|
|
getOportunidades: vi.fn(),
|
|
getActividades: vi.fn(),
|
|
getApartados: vi.fn(),
|
|
getUltimosCambios: vi.fn(),
|
|
}))
|
|
|
|
vi.mock('../services/prisma.js', () => ({ prisma: prismaMock }))
|
|
vi.mock('../services/monday.service.js', () => ({ mondayService: mondayMock }))
|
|
|
|
import { resumenMiDiaEndpoint } from './mi-dia.controller.js'
|
|
|
|
const usuarioFake = {
|
|
id: 'u-1',
|
|
nombre: 'Martin Zavala',
|
|
rol: 'vendedor',
|
|
activo: true,
|
|
mondayUserId: 123,
|
|
}
|
|
|
|
function capturaFake(parcial: Record<string, unknown> & { leadId: string }) {
|
|
return {
|
|
id: `row-${parcial.leadId}`,
|
|
nombreLead: `Persona ${parcial.leadId}`,
|
|
telefono: null,
|
|
email: null,
|
|
origen: null,
|
|
desarrollo: null,
|
|
modelo: null,
|
|
board: 'LEADS',
|
|
etapa: 'NUEVO',
|
|
motivo: null,
|
|
submotivo: null,
|
|
datosJson: null,
|
|
fechaLimite: null,
|
|
mondayItemId: null,
|
|
pendienteSync: false,
|
|
usuarioId: 'u-1',
|
|
createdAt: new Date('2026-09-03T10:00:00'),
|
|
updatedAt: new Date('2026-09-04T10:00:00'),
|
|
...parcial,
|
|
}
|
|
}
|
|
|
|
function resFake() {
|
|
const json = vi.fn()
|
|
const status = vi.fn().mockReturnValue({ json })
|
|
return { res: { json, status } as unknown as Response, status, json }
|
|
}
|
|
|
|
const reqFake = { usuarioToken: { sub: 'u-1' } } as unknown as Request
|
|
|
|
beforeEach(() => {
|
|
vi.clearAllMocks()
|
|
prismaMock.usuario.findUnique.mockResolvedValue(usuarioFake)
|
|
prismaMock.usuario.findMany.mockResolvedValue([])
|
|
prismaMock.leadCaptura.findMany.mockResolvedValue([])
|
|
prismaMock.cliente.findMany.mockResolvedValue([])
|
|
prismaMock.actividadLocal.findMany.mockResolvedValue([])
|
|
mondayMock.getUsuariosWorkspace.mockResolvedValue([])
|
|
mondayMock.getLeads.mockResolvedValue([])
|
|
mondayMock.getOportunidades.mockResolvedValue([])
|
|
mondayMock.getActividades.mockResolvedValue([])
|
|
mondayMock.getApartados.mockResolvedValue([])
|
|
mondayMock.getUltimosCambios.mockResolvedValue(new Map())
|
|
})
|
|
|
|
describe('resumenMiDiaEndpoint (siempre local, Monday purgado)', () => {
|
|
it('responde 100% desde LeadCaptura: Monday no se llama nunca', async () => {
|
|
prismaMock.leadCaptura.findMany.mockResolvedValue([
|
|
capturaFake({ leadId: 'loc_l1', board: 'LEADS', etapa: 'NUEVO', pendienteSync: true }),
|
|
capturaFake({ leadId: 'loc_o1', board: 'OPORTUNIDADES', etapa: 'Afluencia', pendienteSync: true }),
|
|
])
|
|
const { res, json } = resFake()
|
|
|
|
await resumenMiDiaEndpoint(reqFake, res)
|
|
|
|
expect(mondayMock.getLeads).not.toHaveBeenCalled()
|
|
expect(mondayMock.getOportunidades).not.toHaveBeenCalled()
|
|
expect(mondayMock.getApartados).not.toHaveBeenCalled()
|
|
expect(mondayMock.getUsuariosWorkspace).not.toHaveBeenCalled()
|
|
const resumen = json.mock.calls[0]?.[0] as Record<string, unknown>
|
|
expect((resumen['misLeads'] as unknown[]).length).toBe(1) // solo leads, no oportunidades
|
|
expect((resumen['misOportunidades'] as { items: unknown[] }).items).toHaveLength(1)
|
|
expect(resumen['misActividadesHoy']).toEqual([])
|
|
expect(resumen['conteos']).toMatchObject({ actividadesAbiertas: 0 })
|
|
})
|
|
|
|
it('las capturas locales llegan con pendienteSync (badge ⏳ del front)', async () => {
|
|
prismaMock.leadCaptura.findMany.mockResolvedValue([
|
|
capturaFake({ leadId: 'loc_l1', board: 'LEADS', pendienteSync: true }),
|
|
])
|
|
const { res, json } = resFake()
|
|
|
|
await resumenMiDiaEndpoint(reqFake, res)
|
|
|
|
const resumen = json.mock.calls[0]?.[0] as Record<string, unknown>
|
|
expect((resumen['misLeads'] as { id: string; pendienteSync?: boolean }[]).map((l) => l.id)).toEqual(['loc_l1'])
|
|
expect((resumen['misLeads'] as { pendienteSync?: boolean }[])[0]?.pendienteSync).toBe(true)
|
|
})
|
|
|
|
it('sin capturas: listas vacías y sin flags de sync', async () => {
|
|
const { res, json } = resFake()
|
|
|
|
await resumenMiDiaEndpoint(reqFake, res)
|
|
|
|
const resumen = json.mock.calls[0]?.[0] as Record<string, unknown>
|
|
expect(resumen['misLeads']).toEqual([])
|
|
expect(JSON.stringify(resumen)).not.toContain('pendienteSync')
|
|
})
|
|
|
|
it('los usuarios del equipo solo se consultan para roles coordinador/admin/corporativo', async () => {
|
|
await resumenMiDiaEndpoint(reqFake, resFake().res)
|
|
expect(prismaMock.usuario.findMany).not.toHaveBeenCalled()
|
|
|
|
prismaMock.usuario.findUnique.mockResolvedValue({ ...usuarioFake, rol: 'coordinador' })
|
|
await resumenMiDiaEndpoint(reqFake, resFake().res)
|
|
expect(prismaMock.usuario.findMany).toHaveBeenCalledWith({
|
|
where: { activo: true },
|
|
select: { id: true, nombre: true, rol: true, activo: true },
|
|
})
|
|
})
|
|
|
|
it('un error de BD se propaga (no se traga nada)', async () => {
|
|
prismaMock.leadCaptura.findMany.mockRejectedValue(new AppError(500, 'bug'))
|
|
await expect(resumenMiDiaEndpoint(reqFake, resFake().res)).rejects.toThrow('bug')
|
|
})
|
|
})
|