237 lines
8.4 KiB
TypeScript
237 lines
8.4 KiB
TypeScript
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|
|
|
// ---------- Prisma mockeado ----------
|
|
const prismaMock = vi.hoisted(() => ({
|
|
clienteMaestro: {
|
|
findMany: vi.fn(),
|
|
findUnique: vi.fn(),
|
|
count: vi.fn(),
|
|
upsert: vi.fn(),
|
|
create: vi.fn(),
|
|
update: vi.fn(),
|
|
},
|
|
}))
|
|
|
|
vi.mock('./prisma.js', () => ({ prisma: prismaMock }))
|
|
|
|
import {
|
|
listar,
|
|
obtener,
|
|
contar,
|
|
catalogos,
|
|
exportar,
|
|
importarDesdeExcel,
|
|
} from './cliente-maestro.service.js'
|
|
|
|
beforeEach(() => {
|
|
vi.clearAllMocks()
|
|
})
|
|
|
|
// ── listar ───────────────────────────────────────────────────────────
|
|
|
|
describe('listar', () => {
|
|
it('devuelve datos, total, pagina y limite', async () => {
|
|
prismaMock.clienteMaestro.findMany.mockResolvedValue([
|
|
{ id: '1', numCliente: 'C001', nombre: 'Juan', apellidoPaterno: 'Perez', importadoEn: new Date() },
|
|
])
|
|
prismaMock.clienteMaestro.count.mockResolvedValue(1)
|
|
|
|
const result = await listar({ pagina: 1, limite: 50 })
|
|
expect(result.datos).toHaveLength(1)
|
|
expect(result.total).toBe(1)
|
|
expect(result.pagina).toBe(1)
|
|
expect(result.limite).toBe(50)
|
|
})
|
|
|
|
it('aplica filtro de búsqueda con OR', async () => {
|
|
prismaMock.clienteMaestro.findMany.mockResolvedValue([])
|
|
prismaMock.clienteMaestro.count.mockResolvedValue(0)
|
|
|
|
await listar({ busqueda: 'Juan', pagina: 1, limite: 50 })
|
|
expect(prismaMock.clienteMaestro.findMany).toHaveBeenCalledWith(
|
|
expect.objectContaining({
|
|
where: {
|
|
AND: [
|
|
expect.objectContaining({
|
|
OR: expect.arrayContaining([{ nombre: { contains: 'Juan' } }]),
|
|
}),
|
|
],
|
|
},
|
|
}),
|
|
)
|
|
})
|
|
|
|
it('aplica filtro de desarrollo (valor exacto o con sufijo " - ...")', async () => {
|
|
prismaMock.clienteMaestro.findMany.mockResolvedValue([])
|
|
prismaMock.clienteMaestro.count.mockResolvedValue(0)
|
|
|
|
await listar({ desarrollo: 'SENDA', pagina: 1, limite: 50 })
|
|
expect(prismaMock.clienteMaestro.findMany).toHaveBeenCalledWith(
|
|
expect.objectContaining({
|
|
where: {
|
|
AND: [
|
|
{
|
|
OR: [{ desarrollo: 'SENDA' }, { desarrollo: { startsWith: 'SENDA -' } }],
|
|
},
|
|
],
|
|
},
|
|
}),
|
|
)
|
|
})
|
|
|
|
it('aplica filtro de estatus', async () => {
|
|
prismaMock.clienteMaestro.findMany.mockResolvedValue([])
|
|
prismaMock.clienteMaestro.count.mockResolvedValue(0)
|
|
|
|
await listar({ estatus: 'VENDIDO', pagina: 1, limite: 50 })
|
|
expect(prismaMock.clienteMaestro.findMany).toHaveBeenCalledWith(
|
|
expect.objectContaining({
|
|
where: { AND: [{ estatus: 'VENDIDO' }] },
|
|
}),
|
|
)
|
|
})
|
|
|
|
it('combina búsqueda, desarrollo y estatus en la misma consulta', async () => {
|
|
prismaMock.clienteMaestro.findMany.mockResolvedValue([])
|
|
prismaMock.clienteMaestro.count.mockResolvedValue(0)
|
|
|
|
await listar({ busqueda: 'Ana', desarrollo: 'SOLE', estatus: 'ENTREGA', pagina: 1, limite: 50 })
|
|
const llamada = prismaMock.clienteMaestro.findMany.mock.calls[0]![0] as { where: { AND: unknown[] } }
|
|
expect(llamada.where.AND).toHaveLength(3)
|
|
})
|
|
|
|
it('sin filtros no arma condiciones', async () => {
|
|
prismaMock.clienteMaestro.findMany.mockResolvedValue([])
|
|
prismaMock.clienteMaestro.count.mockResolvedValue(0)
|
|
|
|
await listar({ pagina: 1, limite: 50 })
|
|
expect(prismaMock.clienteMaestro.findMany).toHaveBeenCalledWith(
|
|
expect.objectContaining({ where: {} }),
|
|
)
|
|
})
|
|
})
|
|
|
|
// ── catalogos ────────────────────────────────────────────────────────
|
|
|
|
describe('catalogos', () => {
|
|
it('devuelve los desarrollos y estatus reales, sin vacíos ni nulos', async () => {
|
|
prismaMock.clienteMaestro.findMany
|
|
.mockResolvedValueOnce([{ desarrollo: 'MUTUO - EDIFICACION' }, { desarrollo: 'SOLE' }, { desarrollo: null }, { desarrollo: ' ' }])
|
|
.mockResolvedValueOnce([{ estatus: 'ENTREGA' }, { estatus: 'VENDIDO' }, { estatus: null }])
|
|
|
|
const result = await catalogos()
|
|
expect(result.desarrollos).toEqual(['MUTUO - EDIFICACION', 'SOLE'])
|
|
expect(result.estatuses).toEqual(['ENTREGA', 'VENDIDO'])
|
|
expect(prismaMock.clienteMaestro.findMany).toHaveBeenCalledWith({
|
|
distinct: ['desarrollo'],
|
|
select: { desarrollo: true },
|
|
orderBy: { desarrollo: 'asc' },
|
|
})
|
|
})
|
|
})
|
|
|
|
// ── obtener ──────────────────────────────────────────────────────────
|
|
|
|
describe('obtener', () => {
|
|
it('devuelve el cliente si existe', async () => {
|
|
prismaMock.clienteMaestro.findUnique.mockResolvedValue({
|
|
id: '1', numCliente: 'C001', nombre: 'Juan', importadoEn: new Date(),
|
|
})
|
|
const result = await obtener('C001')
|
|
expect(result).not.toBeNull()
|
|
expect(result!.numCliente).toBe('C001')
|
|
})
|
|
|
|
it('devuelve null si no existe', async () => {
|
|
prismaMock.clienteMaestro.findUnique.mockResolvedValue(null)
|
|
expect(await obtener('NOPE')).toBeNull()
|
|
})
|
|
})
|
|
|
|
// ── contar ───────────────────────────────────────────────────────────
|
|
|
|
describe('contar', () => {
|
|
it('devuelve el conteo total', async () => {
|
|
prismaMock.clienteMaestro.count.mockResolvedValue(42)
|
|
expect(await contar({})).toBe(42)
|
|
})
|
|
|
|
it('cuenta con filtros', async () => {
|
|
prismaMock.clienteMaestro.count.mockResolvedValue(5)
|
|
expect(await contar({ desarrollo: 'SENDA' })).toBe(5)
|
|
expect(prismaMock.clienteMaestro.count).toHaveBeenCalledWith(
|
|
{
|
|
where: {
|
|
AND: [{ OR: [{ desarrollo: 'SENDA' }, { desarrollo: { startsWith: 'SENDA -' } }] }],
|
|
},
|
|
},
|
|
)
|
|
})
|
|
})
|
|
|
|
// ── exportar ─────────────────────────────────────────────────────────
|
|
|
|
describe('exportar', () => {
|
|
it('devuelve array de clientes', async () => {
|
|
prismaMock.clienteMaestro.findMany.mockResolvedValue([
|
|
{ id: '1', numCliente: 'C001', nombre: 'Juan', importadoEn: new Date() },
|
|
{ id: '2', numCliente: 'C002', nombre: 'Ana', importadoEn: new Date() },
|
|
])
|
|
const result = await exportar({})
|
|
expect(result).toHaveLength(2)
|
|
expect(prismaMock.clienteMaestro.findMany).toHaveBeenCalledWith(
|
|
expect.objectContaining({ take: 50000 }),
|
|
)
|
|
})
|
|
})
|
|
|
|
// ── importarDesdeExcel ───────────────────────────────────────────────
|
|
|
|
describe('importarDesdeExcel', () => {
|
|
it('inserta clientes nuevos', async () => {
|
|
prismaMock.clienteMaestro.findUnique.mockResolvedValue(null)
|
|
prismaMock.clienteMaestro.create.mockResolvedValue({})
|
|
|
|
const result = await importarDesdeExcel([
|
|
{ numCliente: 'C001', nombre: 'Juan', apellidoPaterno: 'Perez' },
|
|
])
|
|
expect(result.insertados).toBe(1)
|
|
expect(result.actualizados).toBe(0)
|
|
expect(result.errores).toBe(0)
|
|
expect(prismaMock.clienteMaestro.create).toHaveBeenCalled()
|
|
})
|
|
|
|
it('actualiza clientes existentes', async () => {
|
|
prismaMock.clienteMaestro.findUnique.mockResolvedValue({ id: '1', numCliente: 'C001' })
|
|
prismaMock.clienteMaestro.update.mockResolvedValue({})
|
|
|
|
const result = await importarDesdeExcel([
|
|
{ numCliente: 'C001', nombre: 'Juan', apellidoPaterno: 'Perez' },
|
|
])
|
|
expect(result.insertados).toBe(0)
|
|
expect(result.actualizados).toBe(1)
|
|
expect(result.errores).toBe(0)
|
|
expect(prismaMock.clienteMaestro.update).toHaveBeenCalled()
|
|
})
|
|
|
|
it('salta registros sin numCliente o nombre', async () => {
|
|
const result = await importarDesdeExcel([
|
|
{ numCliente: '', nombre: 'Juan' },
|
|
{ numCliente: 'C001', nombre: '' },
|
|
])
|
|
expect(result.errores).toBe(2)
|
|
expect(prismaMock.clienteMaestro.create).not.toHaveBeenCalled()
|
|
})
|
|
|
|
it('maneja errores de DB como errores', async () => {
|
|
prismaMock.clienteMaestro.findUnique.mockResolvedValue(null)
|
|
prismaMock.clienteMaestro.create.mockRejectedValue(new Error('DB error'))
|
|
|
|
const result = await importarDesdeExcel([
|
|
{ numCliente: 'C001', nombre: 'Juan', apellidoPaterno: 'Perez' },
|
|
])
|
|
expect(result.errores).toBe(1)
|
|
expect(result.insertados).toBe(0)
|
|
})
|
|
})
|