import { beforeEach, describe, expect, it, vi } from 'vitest' import type { Request, Response } from 'express' // ---------- Servicio mockeado ---------- const servicioMock = vi.hoisted(() => ({ listar: vi.fn(), obtener: vi.fn(), contar: vi.fn(), exportar: vi.fn(), importarDesdeExcel: vi.fn(), })) vi.mock('../services/cliente-maestro.service.js', () => servicioMock) import { listarEndpoint, exportarEndpoint, obtenerEndpoint, importarEndpoint, } from './cliente-maestro.controller.js' function reqFake(overrides: Partial = {}): Request { return { body: {}, params: {}, query: {}, usuarioToken: { sub: 'u-1', username: 'admin', rol: 'admin' }, ...overrides, } as Request } function resFake() { const json = vi.fn() const end = vi.fn() const status = vi.fn().mockReturnValue({ json, end }) return { res: { json, status, end } as unknown as Response, status, json, end } } beforeEach(() => { vi.clearAllMocks() }) // ── GET /api/clientes-maestro ──────────────────────────────────────── describe('listarEndpoint', () => { it('200 con datos paginados', async () => { servicioMock.listar.mockResolvedValue({ datos: [{ id: '1', numCliente: 'C001' }], total: 1, pagina: 1, limite: 50, }) const { res, json } = resFake() await listarEndpoint(reqFake(), res) expect(json).toHaveBeenCalledWith({ datos: [{ id: '1', numCliente: 'C001' }], total: 1, pagina: 1, limite: 50, }) }) it('pasa filtros de query al servicio', async () => { servicioMock.listar.mockResolvedValue({ datos: [], total: 0, pagina: 1, limite: 50 }) const { res } = resFake() await listarEndpoint( reqFake({ query: { busqueda: 'Juan', desarrollo: 'SENDA', estatus: 'VENDIDO', pagina: '2', limite: '25' } }), res, ) expect(servicioMock.listar).toHaveBeenCalledWith({ busqueda: 'Juan', desarrollo: 'SENDA', estatus: 'VENDIDO', pagina: 2, limite: 25, }) }) }) // ── GET /api/clientes-maestro/exportar ─────────────────────────────── describe('exportarEndpoint', () => { it('200 con array de datos', async () => { servicioMock.exportar.mockResolvedValue([{ id: '1', numCliente: 'C001' }]) const { res, json } = resFake() await exportarEndpoint(reqFake(), res) expect(json).toHaveBeenCalledWith([{ id: '1', numCliente: 'C001' }]) }) }) // ── GET /api/clientes-maestro/:numCliente ──────────────────────────── describe('obtenerEndpoint', () => { it('200 con el cliente', async () => { servicioMock.obtener.mockResolvedValue({ id: '1', numCliente: 'C001', nombre: 'Juan' }) const { res, json } = resFake() await obtenerEndpoint(reqFake({ params: { numCliente: 'C001' } }), res) expect(json).toHaveBeenCalledWith({ id: '1', numCliente: 'C001', nombre: 'Juan' }) }) it('404 si no existe', async () => { servicioMock.obtener.mockResolvedValue(null) const { res } = resFake() await expect( obtenerEndpoint(reqFake({ params: { numCliente: 'NOPE' } }), res), ).rejects.toThrow('Cliente no encontrado') }) it('400 si numCliente vacío', async () => { const { res } = resFake() await expect(obtenerEndpoint(reqFake({ params: { numCliente: '' } }), res)).rejects.toThrow('numCliente requerido') }) }) // ── POST /api/clientes-maestro/importar ────────────────────────────── describe('importarEndpoint', () => { it('200 con resultado de importación', async () => { servicioMock.importarDesdeExcel.mockResolvedValue({ insertados: 5, actualizados: 2, errores: 1 }) const { res, json } = resFake() await importarEndpoint(reqFake({ body: [{ numCliente: 'C001', nombre: 'Juan' }] }), res) expect(json).toHaveBeenCalledWith({ insertados: 5, actualizados: 2, errores: 1 }) }) it('400 si body no es array', async () => { const { res } = resFake() await expect(importarEndpoint(reqFake({ body: {} }), res)).rejects.toThrow('Se esperaba un array') }) it('400 si array vacío', async () => { const { res } = resFake() await expect(importarEndpoint(reqFake({ body: [] }), res)).rejects.toThrow('Se esperaba un array') }) })