import { beforeEach, describe, expect, it, vi } from 'vitest' // ---------- Prisma mockeado ---------- const prismaMock = vi.hoisted(() => ({ leadCaptura: { findMany: vi.fn() }, cliente: { findMany: vi.fn() }, cotizacion: { findMany: vi.fn() }, unidad: { findMany: vi.fn() }, })) vi.mock('./prisma.js', () => ({ prisma: prismaMock })) import { buscar } from './buscar.service.js' beforeEach(() => { vi.clearAllMocks() prismaMock.leadCaptura.findMany.mockResolvedValue([]) prismaMock.cliente.findMany.mockResolvedValue([]) prismaMock.cotizacion.findMany.mockResolvedValue([]) prismaMock.unidad.findMany.mockResolvedValue([]) }) describe('buscar (GET /api/buscar)', () => { it('término vacío retorna [] sin consultar la base', async () => { const r = await buscar('') expect(r).toEqual([]) expect(prismaMock.leadCaptura.findMany).not.toHaveBeenCalled() }) it('término con solo espacios también retorna []', async () => { const r = await buscar(' ') expect(r).toEqual([]) expect(prismaMock.leadCaptura.findMany).not.toHaveBeenCalled() }) it('consulta los 4 modelos con el patrón contains', async () => { prismaMock.leadCaptura.findMany.mockResolvedValue([ { leadId: 'l1', nombreLead: 'María López', telefono: '4771112233', email: null, origen: 'Facebook', board: 'LEADS', updatedAt: new Date() }, ]) prismaMock.cliente.findMany.mockResolvedValue([ { id: 'c1', nombreCompleto: 'María López', curp: '', rfc: '', email: 'maria@x.com', celular: '4771112233', proyecto: 'DIAGONAL', createdAt: new Date() }, ]) prismaMock.cotizacion.findMany.mockResolvedValue([]) prismaMock.unidad.findMany.mockResolvedValue([]) const r = await buscar('María') expect(prismaMock.leadCaptura.findMany).toHaveBeenCalledTimes(1) expect(prismaMock.cliente.findMany).toHaveBeenCalledTimes(1) expect(prismaMock.cotizacion.findMany).toHaveBeenCalledTimes(1) expect(prismaMock.unidad.findMany).toHaveBeenCalledTimes(1) expect(r.length).toBe(2) expect(r[0]?.tipo).toBe('lead') expect(r[1]?.tipo).toBe('contacto') }) it('aplica take: limite por modelo', async () => { await buscar('test', 5) expect(prismaMock.leadCaptura.findMany).toHaveBeenCalledWith( expect.objectContaining({ take: 5 }), ) }) it('prioriza leads sobre contactos, contactos sobre oportunidades, oportunidades sobre unidades', async () => { prismaMock.leadCaptura.findMany.mockResolvedValue([ { leadId: 'l1', nombreLead: 'X', telefono: null, email: null, origen: null, board: 'LEADS', updatedAt: new Date() }, ]) prismaMock.cliente.findMany.mockResolvedValue([ { id: 'c1', nombreCompleto: 'X', curp: '', rfc: '', email: null, celular: null, proyecto: '', createdAt: new Date() }, ]) prismaMock.cotizacion.findMany.mockResolvedValue([ { id: 'ct1', clienteNombre: 'X', clienteEmail: null, unidadCodigo: null, desarrollo: null, tipoPropuesta: null, createdAt: new Date() }, ]) prismaMock.unidad.findMany.mockResolvedValue([ { id: 'u1', codigo: 'X', desarrollo: 'SENDA', modelo: null, status: null, updatedAt: new Date() }, ]) const r = await buscar('X') expect(r.map((i) => i.tipo)).toEqual(['lead', 'contacto', 'oportunidad', 'unidad']) }) it('mezcla los resultados y limita al total indicado', async () => { const leads = Array.from({ length: 10 }, (_, i) => ({ leadId: `l${i}`, nombreLead: `L${i}`, telefono: null, email: null, origen: null, board: 'LEADS', updatedAt: new Date(), })) const clientes = Array.from({ length: 10 }, (_, i) => ({ id: `c${i}`, nombreCompleto: `C${i}`, curp: '', rfc: '', email: null, celular: null, proyecto: '', createdAt: new Date(), })) prismaMock.leadCaptura.findMany.mockResolvedValue(leads) prismaMock.cliente.findMany.mockResolvedValue(clientes) const r = await buscar('x', 15) expect(r).toHaveLength(15) // Primeros 10 son leads (prioridad), siguientes 5 son contactos expect(r.filter((i) => i.tipo === 'lead')).toHaveLength(10) expect(r.filter((i) => i.tipo === 'contacto')).toHaveLength(5) }) it('lead: detalle muestra teléfono · email, o origen como fallback', async () => { prismaMock.leadCaptura.findMany.mockResolvedValue([ { leadId: 'l1', nombreLead: 'Ana', telefono: '4771112233', email: 'ana@x.com', origen: null, board: 'LEADS', updatedAt: new Date() }, ]) const r = await buscar('Ana') expect(r[0]?.detalle).toBe('4771112233 · ana@x.com') prismaMock.leadCaptura.findMany.mockResolvedValue([ { leadId: 'l2', nombreLead: 'Bob', telefono: null, email: null, origen: 'Facebook', board: 'LEADS', updatedAt: new Date() }, ]) const r2 = await buscar('Bob') expect(r2[0]?.detalle).toBe('Facebook') }) it('contacto: detalle muestra email · celular · proyecto', async () => { prismaMock.cliente.findMany.mockResolvedValue([ { id: 'c1', nombreCompleto: 'Luis', curp: '', rfc: '', email: 'luis@x.com', celular: '4772223333', proyecto: 'SENDA', createdAt: new Date() }, ]) const r = await buscar('Luis') expect(r[0]?.detalle).toBe('luis@x.com · 4772223333 · SENDA') }) it('oportunidad: detalle muestra unidadCodigo · desarrollo, o tipoPropuesta como fallback', async () => { prismaMock.cotizacion.findMany.mockResolvedValue([ { id: 'ct1', clienteNombre: 'Carlos', clienteEmail: null, unidadCodigo: 'U-101', desarrollo: 'DIAGONAL', tipoPropuesta: null, createdAt: new Date() }, ]) const r = await buscar('Carlos') expect(r[0]?.detalle).toBe('U-101 · DIAGONAL') prismaMock.cotizacion.findMany.mockResolvedValue([ { id: 'ct2', clienteNombre: 'Diana', clienteEmail: null, unidadCodigo: null, desarrollo: null, tipoPropuesta: 'CONTADO', createdAt: new Date() }, ]) const r2 = await buscar('Diana') expect(r2[0]?.detalle).toBe('CONTADO') }) it('unidad: detalle muestra desarrollo · modelo · status', async () => { prismaMock.unidad.findMany.mockResolvedValue([ { id: 'u1', codigo: 'U-202', desarrollo: 'ESTANZA', modelo: 'SOHO', status: 'Disponible', updatedAt: new Date() }, ]) const r = await buscar('U-202') expect(r[0]?.detalle).toBe('ESTANZA · SOHO · Disponible') }) it('urls de navegación son correctas por tipo', async () => { prismaMock.leadCaptura.findMany.mockResolvedValue([ { leadId: 'l1', nombreLead: 'X', telefono: null, email: null, origen: null, board: 'LEADS', updatedAt: new Date() }, ]) prismaMock.cliente.findMany.mockResolvedValue([ { id: 'c1', nombreCompleto: 'X', curp: '', rfc: '', email: null, celular: null, proyecto: '', createdAt: new Date() }, ]) prismaMock.cotizacion.findMany.mockResolvedValue([ { id: 'ct1', clienteNombre: 'X', clienteEmail: null, unidadCodigo: null, desarrollo: null, tipoPropuesta: null, createdAt: new Date() }, ]) prismaMock.unidad.findMany.mockResolvedValue([ { id: 'u1', codigo: 'X', desarrollo: null, modelo: null, status: null, updatedAt: new Date() }, ]) const r = await buscar('X') expect(r.find((i) => i.tipo === 'lead')?.url).toBe('/hoy/') expect(r.find((i) => i.tipo === 'contacto')?.url).toBe('/') expect(r.find((i) => i.tipo === 'oportunidad')?.url).toBe('/quote/?cotizacion=ct1') expect(r.find((i) => i.tipo === 'unidad')?.url).toBe('/quote/?unidad=X') }) })