import { beforeEach, describe, expect, it, vi } from 'vitest' import type { Request, Response } from 'express' import { requireAdmin, obtenerConfigsEndpoint, actualizarConfigsEndpoint, obtenerRolesConfigEndpoint, actualizarRolConfigEndpoint, obtenerUsuariosEndpoint, actualizarUsuarioEndpoint, resetRolesEndpoint, obtenerAccionesEndpoint, } from './config.controller.js' // ---------- Servicio mockeado ---------- const servicioMock = vi.hoisted(() => ({ obtenerConfigs: vi.fn(), actualizarConfigs: vi.fn(), obtenerRolesConfig: vi.fn(), actualizarRolConfig: vi.fn(), obtenerUsuarios: vi.fn(), actualizarUsuario: vi.fn(), resetRoles: vi.fn(), obtenerAcciones: vi.fn(), })) vi.mock('../services/config.service.js', () => servicioMock) function reqFake(overrides: Partial = {}): Request { return { body: {}, params: {}, 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() }) // ── requireAdmin ─────────────────────────────────────────────────────── describe('requireAdmin', () => { it('next() si rol es admin', () => { const next = vi.fn() requireAdmin(reqFake(), {} as Response, next) expect(next).toHaveBeenCalled() }) it('lanza 403 si rol no es admin', () => { expect(() => requireAdmin(reqFake({ usuarioToken: { sub: 'u-2', username: 'x', rol: 'vendedor' } }), {} as Response, vi.fn()), ).toThrow('Se requiere rol admin') }) it('lanza 403 si no hay token', () => { expect(() => requireAdmin(reqFake({ usuarioToken: undefined }), {} as Response, vi.fn()), ).toThrow('Se requiere rol admin') }) }) // ── GET /api/config/sistema ──────────────────────────────────────────── describe('obtenerConfigsEndpoint', () => { it('200 con todas las configs', async () => { servicioMock.obtenerConfigs.mockResolvedValue({ a: '1' }) const { res, json } = resFake() await obtenerConfigsEndpoint(reqFake(), res) expect(json).toHaveBeenCalledWith({ a: '1' }) }) }) // ── PATCH /api/config/sistema ────────────────────────────────────────── describe('actualizarConfigsEndpoint', () => { it('204 tras actualizar configs parciales', async () => { servicioMock.actualizarConfigs.mockResolvedValue(undefined) const { res, end } = resFake() await actualizarConfigsEndpoint(reqFake({ body: { x: '1' } }), res) expect(servicioMock.actualizarConfigs).toHaveBeenCalledWith({ x: '1' }) expect(end).toHaveBeenCalled() }) it('400 si body vacío', async () => { const { res } = resFake() await expect(actualizarConfigsEndpoint(reqFake({ body: {} }), res)).rejects.toThrow('Body vacío') }) it('400 si body inválido (ZodError)', async () => { const { res } = resFake() await expect(actualizarConfigsEndpoint(reqFake({ body: { x: 123 } }), res)).rejects.toThrow() }) }) // ── GET /api/config/roles ────────────────────────────────────────────── describe('obtenerRolesConfigEndpoint', () => { it('200 con roles parseados', async () => { servicioMock.obtenerRolesConfig.mockResolvedValue([ { rol: 'admin', modulos: '["mi-dia"]', acciones: '{"x":true}', updatedAt: new Date() }, ]) const { res, json } = resFake() await obtenerRolesConfigEndpoint(reqFake(), res) expect(json).toHaveBeenCalledWith([ { rol: 'admin', modulos: ['mi-dia'], acciones: { x: true }, updatedAt: expect.any(Date) }, ]) }) }) // ── PATCH /api/config/roles/:rol ─────────────────────────────────────── describe('actualizarRolConfigEndpoint', () => { it('204 tras actualizar un rol', async () => { servicioMock.actualizarRolConfig.mockResolvedValue(undefined) const { res, end } = resFake() await actualizarRolConfigEndpoint( reqFake({ params: { rol: 'vendedor' }, body: { modulos: ['a'], acciones: { b: true } } }), res, ) expect(servicioMock.actualizarRolConfig).toHaveBeenCalledWith('vendedor', { modulos: ['a'], acciones: { b: true }, }) expect(end).toHaveBeenCalled() }) it('400 si body inválido (faltan campos)', async () => { const { res } = resFake() await expect( actualizarRolConfigEndpoint(reqFake({ params: { rol: 'x' }, body: { modulos: 'no' } }), res), ).rejects.toThrow() }) }) // ── GET /api/config/usuarios ─────────────────────────────────────────── describe('obtenerUsuariosEndpoint', () => { it('200 con la lista de usuarios', async () => { servicioMock.obtenerUsuarios.mockResolvedValue([{ id: 'u1', nombre: 'A' }]) const { res, json } = resFake() await obtenerUsuariosEndpoint(reqFake(), res) expect(json).toHaveBeenCalledWith([{ id: 'u1', nombre: 'A' }]) }) }) // ── PATCH /api/config/usuarios/:id ───────────────────────────────────── describe('actualizarUsuarioEndpoint', () => { it('204 tras actualizar un usuario', async () => { servicioMock.actualizarUsuario.mockResolvedValue(undefined) const { res, end } = resFake() await actualizarUsuarioEndpoint( reqFake({ params: { id: 'u1' }, body: { rol: 'admin' } }), res, ) expect(servicioMock.actualizarUsuario).toHaveBeenCalledWith('u1', { rol: 'admin' }) expect(end).toHaveBeenCalled() }) it('400 si body vacío', async () => { const { res } = resFake() await expect( actualizarUsuarioEndpoint(reqFake({ params: { id: 'u1' }, body: {} }), res), ).rejects.toThrow('Body vacío') }) }) // ── POST /api/config/reset-roles ─────────────────────────────────────── describe('resetRolesEndpoint', () => { it('204 tras resetear roles', async () => { servicioMock.resetRoles.mockResolvedValue(undefined) const { res, end } = resFake() await resetRolesEndpoint(reqFake(), res) expect(servicioMock.resetRoles).toHaveBeenCalled() expect(end).toHaveBeenCalled() }) }) // ── GET /api/config/acciones ─────────────────────────────────────────── describe('obtenerAccionesEndpoint', () => { it('200 con las acciones del rol del usuario', async () => { servicioMock.obtenerAcciones.mockResolvedValue({ descartar_lead: true }) const { res, json } = resFake() await obtenerAccionesEndpoint(reqFake(), res) expect(servicioMock.obtenerAcciones).toHaveBeenCalledWith('admin') expect(json).toHaveBeenCalledWith({ descartar_lead: true }) }) it('400 sin token verificado', async () => { const { res } = resFake() await expect(obtenerAccionesEndpoint(reqFake({ usuarioToken: undefined }), res)).rejects.toThrow( 'Falta el token verificado', ) }) })