Files
FRASA-CAS/api-gateway/src/controllers/config.controller.test.ts
Consult AS Technology b2634f97ab feat(config): modulo Configuraciones admin + permisos por rol
- Tablas ConfiguracionSistema (7 configs) + RolConfiguracion (6 roles)
- Endpoints: config/sistema GET/PATCH, config/roles GET/PATCH,
  config/usuarios GET/PATCH, config/reset-roles POST, config/acciones GET
- modulosDeRol() async con fallback a MATRIZ estatica
- auth/me devuelve acciones[] segun rol del usuario
- saludMonday.estaDisponible() lee modo_local_forzado de DB (cache 30s)
- Landing: vista Configuraciones con 4 tabs (Modo, Roles, Usuarios, Sistema)
- Mi-dia: boton Descartar se deshabilita si !descartar_lead
- 630 API tests + 24 landing tests + 20 mi-dia tests verdes

Seed: npm run seed:config
2026-09-06 09:46:19 +00:00

207 lines
7.4 KiB
TypeScript

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> = {}): 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',
)
})
})