import { beforeEach, describe, expect, it, vi } from 'vitest' import type { Request, Response } from 'express' // ---------- Prisma mockeado ---------- const prismaMock = vi.hoisted(() => ({ capturaDepartamento: { findMany: vi.fn(), findUnique: vi.fn(), count: vi.fn(), upsert: vi.fn(), }, $transaction: vi.fn(), })) vi.mock('../services/prisma.js', () => ({ prisma: prismaMock })) // Mock del service para no depender de Prisma real vi.mock('../services/captura-departamento.service.js', () => ({ obtenerUnidades: vi.fn(), obtenerCapturas: vi.fn(), obtenerCapturaUnidad: vi.fn(), guardarCampo: vi.fn(), guardarBulk: vi.fn(), resumenAvance: vi.fn(), importarDesdeExcel: vi.fn(), })) import { listarEndpoint, unidadesEndpoint, resumenEndpoint, guardarEndpoint, bulkEndpoint, importarEndpoint, requireAdminCaptura, } from './captura-departamento.controller.js' import * as capturaService from '../services/captura-departamento.service.js' function reqFake(opts: { query?: Record; body?: unknown; params?: Record; token?: { sub: string; rol: string } }): Request { return { query: opts.query ?? {}, body: opts.body ?? {}, params: opts.params ?? {}, usuarioToken: opts.token, } as unknown as Request } function resFake() { const json = vi.fn() const end = vi.fn() const statusFn = vi.fn().mockReturnValue({ json, end }) return { res: { json, end, status: statusFn } as unknown as Response, status: statusFn, json, end } } beforeEach(() => { vi.clearAllMocks() }) // ── requireAdminCaptura ───────────────────────────────────────────── describe('requireAdminCaptura', () => { it('pasa si rol es admin', () => { const next = vi.fn() const req = reqFake({ token: { sub: 'u1', rol: 'admin' } }) requireAdminCaptura(req, {} as Response, next) expect(next).toHaveBeenCalled() }) it('lanza 403 si rol no es admin', () => { const req = reqFake({ token: { sub: 'u1', rol: 'vendedor' } }) expect(() => requireAdminCaptura(req, {} as Response, vi.fn())).toThrow('Se requiere rol admin') }) it('lanza 403 si no hay token', () => { const req = reqFake({}) expect(() => requireAdminCaptura(req, {} as Response, vi.fn())).toThrow('Se requiere rol admin') }) }) // ── listarEndpoint ────────────────────────────────────────────────── describe('listarEndpoint (GET /api/captura)', () => { it('retorna capturas agrupadas', async () => { const fake = { U101: { estatus: 'VENDIDO' } } vi.mocked(capturaService.obtenerCapturas).mockResolvedValue(fake) const { res, json } = resFake() await listarEndpoint(reqFake({ query: { desarrollo: 'SENDA' }, token: { sub: 'u1', rol: 'admin' } }), res) expect(json).toHaveBeenCalledWith(fake) expect(capturaService.obtenerCapturas).toHaveBeenCalledWith('SENDA', undefined) }) it('pasa departamento cuando se provee', async () => { vi.mocked(capturaService.obtenerCapturas).mockResolvedValue({}) const { res } = resFake() await listarEndpoint( reqFake({ query: { desarrollo: 'SENDA', departamento: 'OBRA' }, token: { sub: 'u1', rol: 'admin' } }), res, ) expect(capturaService.obtenerCapturas).toHaveBeenCalledWith('SENDA', 'OBRA') }) it('rechaza sin desarrollo', async () => { const { res } = resFake() await expect(listarEndpoint(reqFake({ query: {}, token: { sub: 'u1', rol: 'admin' } }), res)).rejects.toThrow() }) }) // ── unidadesEndpoint ──────────────────────────────────────────────── describe('unidadesEndpoint (GET /api/captura/unidades)', () => { it('retorna lista de unidades', async () => { vi.mocked(capturaService.obtenerUnidades).mockResolvedValue(['U-101', 'U-102']) const { res, json } = resFake() await unidadesEndpoint(reqFake({ query: { desarrollo: 'SENDA' }, token: { sub: 'u1', rol: 'admin' } }), res) expect(json).toHaveBeenCalledWith(['U-101', 'U-102']) }) }) // ── resumenEndpoint ───────────────────────────────────────────────── describe('resumenEndpoint (GET /api/captura/resumen)', () => { it('retorna resumen de avance', async () => { const fake = [{ departamento: 'OBRA', camposTotales: 8, camposLlenos: 4, porcentaje: 50 }] vi.mocked(capturaService.resumenAvance).mockResolvedValue(fake) const { res, json } = resFake() await resumenEndpoint(reqFake({ query: { desarrollo: 'SENDA' }, token: { sub: 'u1', rol: 'admin' } }), res) expect(json).toHaveBeenCalledWith(fake) }) }) // ── guardarEndpoint ───────────────────────────────────────────────── describe('guardarEndpoint (POST /api/captura)', () => { it('guarda campo y retorna 204', async () => { vi.mocked(capturaService.guardarCampo).mockResolvedValue(undefined) const { res, status } = resFake() await guardarEndpoint( reqFake({ body: { desarrollo: 'SENDA', unidadCodigo: 'U-101', departamento: 'VENTAS', campo: 'estatus', valor: 'VENDIDO' }, token: { sub: 'u1', rol: 'admin' }, }), res, ) expect(status).toHaveBeenCalledWith(204) expect(capturaService.guardarCampo).toHaveBeenCalledWith('SENDA', 'U-101', 'VENTAS', 'estatus', 'VENDIDO', 'u1') }) it('rechaza departamento inválido', async () => { const { res } = resFake() await expect( guardarEndpoint( reqFake({ body: { desarrollo: 'SENDA', unidadCodigo: 'U-101', departamento: 'INVALIDO', campo: 'x', valor: 'y' }, token: { sub: 'u1', rol: 'admin' }, }), res, ), ).rejects.toThrow('Departamento inválido') }) }) // ── bulkEndpoint ──────────────────────────────────────────────────── describe('bulkEndpoint (POST /api/captura/bulk)', () => { it('guarda múltiple y retorna 204', async () => { vi.mocked(capturaService.guardarBulk).mockResolvedValue(undefined) const { res, status } = resFake() await bulkEndpoint( reqFake({ body: { desarrollo: 'SENDA', departamento: 'VENTAS', registros: [ { unidad: 'U-101', campo: 'estatus', valor: 'VENDIDO' }, { unidad: 'U-102', campo: 'estatus', valor: 'DISPONIBLE' }, ], }, token: { sub: 'u1', rol: 'admin' }, }), res, ) expect(status).toHaveBeenCalledWith(204) expect(capturaService.guardarBulk).toHaveBeenCalledWith('SENDA', 'VENTAS', [ { unidad: 'U-101', campo: 'estatus', valor: 'VENDIDO' }, { unidad: 'U-102', campo: 'estatus', valor: 'DISPONIBLE' }, ], 'u1') }) }) // ── importarEndpoint ──────────────────────────────────────────────── describe('importarEndpoint (POST /api/captura/importar)', () => { it('importa datos y retorna resultado (admin)', async () => { const fake = { registrosCreados: 10, registrosActualizados: 5 } vi.mocked(capturaService.importarDesdeExcel).mockResolvedValue(fake) const { res, json } = resFake() await importarEndpoint( reqFake({ body: { desarrollo: 'SENDA', datos: [['CLIENTE', 'ESTATUS'], ['U-101', 'VENDIDO']] }, token: { sub: 'u1', rol: 'admin' }, }), res, ) expect(json).toHaveBeenCalledWith(fake) expect(capturaService.importarDesdeExcel).toHaveBeenCalledWith('SENDA', [['CLIENTE', 'ESTATUS'], ['U-101', 'VENDIDO']], 'u1') }) it('lanza 403 si no es admin', async () => { const req = reqFake({ body: { desarrollo: 'SENDA', datos: [] }, token: { sub: 'u1', rol: 'vendedor' }, }) expect(() => requireAdminCaptura(req, {} as Response, vi.fn())).toThrow('Se requiere rol admin') }) })