Files
FRASA-CAS/api-gateway/src/controllers/usuarios.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

197 lines
6.7 KiB
TypeScript

import { beforeEach, describe, expect, it, vi } from 'vitest'
import type { Request, Response } from 'express'
import type { Usuario } from '@prisma/client'
import {
actualizarRolEndpoint,
actualizarVinculoMondayEndpoint,
listarUsuariosMondayEndpoint,
} from './usuarios.controller.js'
import { AppError } from '../utils/errors.js'
// ---------- Monday y Prisma mockeados (los tests no tocan red ni BD) ----------
const mondayMock = vi.hoisted(() => ({ getUsuariosWorkspace: vi.fn() }))
const prismaMock = vi.hoisted(() => ({ usuario: { update: vi.fn() } }))
vi.mock('../services/monday.service.js', () => ({ mondayService: mondayMock }))
vi.mock('../services/prisma.js', () => ({ prisma: prismaMock }))
const USUARIOS_WORKSPACE = [
{ id: 13676, nombre: 'Erick Peña', email: 'erick@frasa.mx' },
{ id: 20400, nombre: 'Cristina Barrera', email: 'cristina@frasa.mx' },
]
function usuarioFake(overrides: Partial<Usuario> = {}): Usuario {
return {
id: 'u-1',
username: 'erick israel pena ramos',
nombre: 'ERICK ISRAEL PEÑA RAMOS',
passwordHash: 'hash',
rol: 'vendedor',
asignacion: 'MONTEBELLO',
empleadoId: 13676,
mondayUserId: null,
activo: true,
createdAt: new Date(),
updatedAt: new Date(),
...overrides,
}
}
function reqFake(overrides: Partial<Request> = {}): Request {
return {
body: {},
usuarioToken: { sub: 'u-1', username: 'erick israel pena ramos', rol: 'vendedor' },
...overrides,
} as Request
}
function resFake() {
const json = vi.fn()
const status = vi.fn().mockReturnValue({ json })
return { res: { json, status } as unknown as Response, status, json }
}
beforeEach(() => {
vi.clearAllMocks()
mondayMock.getUsuariosWorkspace.mockResolvedValue(USUARIOS_WORKSPACE)
})
describe('listarUsuariosMondayEndpoint (GET /api/monday/usuarios)', () => {
it('devuelve los usuarios del workspace con id numérico', async () => {
const { res, json } = resFake()
await listarUsuariosMondayEndpoint(reqFake(), res)
expect(json).toHaveBeenCalledWith([
{ id: 13676, nombre: 'Erick Peña', email: 'erick@frasa.mx' },
{ id: 20400, nombre: 'Cristina Barrera', email: 'cristina@frasa.mx' },
])
})
it('propaga el error si Monday falla (el error handler responde 5xx)', async () => {
mondayMock.getUsuariosWorkspace.mockRejectedValue(new Error('Monday caído'))
const { res } = resFake()
await expect(listarUsuariosMondayEndpoint(reqFake(), res)).rejects.toThrow('Monday caído')
})
})
describe('actualizarVinculoMondayEndpoint (PATCH /api/usuarios/me/monday)', () => {
it('con id válido guarda el vínculo y devuelve el perfil actualizado', async () => {
prismaMock.usuario.update.mockResolvedValue(usuarioFake({ mondayUserId: 13676 }))
const { res, json } = resFake()
await actualizarVinculoMondayEndpoint(reqFake({ body: { mondayUserId: 13676 } }), res)
expect(prismaMock.usuario.update).toHaveBeenCalledWith({
where: { id: 'u-1' },
data: { mondayUserId: 13676 },
})
expect(json).toHaveBeenCalledWith({
nombre: 'ERICK ISRAEL PEÑA RAMOS',
rol: 'vendedor',
asignacion: 'MONTEBELLO',
mondayUserId: 13676,
modulos: ['mi-dia', 'alta-cliente', 'alta-afluencia', 'cotizador', 'mi-tablero', 'contactos', 'chat'],
acciones: [],
})
})
it('con id inexistente en el workspace responde 400 y no toca la BD', async () => {
const { res } = resFake()
const error = await actualizarVinculoMondayEndpoint(
reqFake({ body: { mondayUserId: 99999 } }),
res,
).catch((e: unknown) => e)
expect(error).toBeInstanceOf(AppError)
expect(error).toMatchObject({ status: 400 })
expect(prismaMock.usuario.update).not.toHaveBeenCalled()
})
it('con null desvincula sin consultar Monday', async () => {
prismaMock.usuario.update.mockResolvedValue(usuarioFake({ mondayUserId: null }))
const { res, json } = resFake()
await actualizarVinculoMondayEndpoint(reqFake({ body: { mondayUserId: null } }), res)
expect(mondayMock.getUsuariosWorkspace).not.toHaveBeenCalled()
expect(prismaMock.usuario.update).toHaveBeenCalledWith({
where: { id: 'u-1' },
data: { mondayUserId: null },
})
expect(json).toHaveBeenCalledWith(expect.objectContaining({ mondayUserId: null }))
})
it('sin token verificado: 400', async () => {
const { res } = resFake()
await expect(
actualizarVinculoMondayEndpoint(reqFake({ usuarioToken: undefined }), res),
).rejects.toThrow('Falta el token verificado')
expect(prismaMock.usuario.update).not.toHaveBeenCalled()
})
it('body inválido (string): ZodError (el error handler lo traduce a 400)', async () => {
const { res } = resFake()
await expect(
actualizarVinculoMondayEndpoint(reqFake({ body: { mondayUserId: '13676' } }), res),
).rejects.toThrow()
expect(prismaMock.usuario.update).not.toHaveBeenCalled()
})
})
describe('actualizarRolEndpoint (PATCH /api/usuarios/:id/rol)', () => {
function reqAdmin(overrides: Partial<Request> = {}): Request {
return {
body: { rol: 'escrituracion' },
params: { id: 'u-9' },
usuarioToken: { sub: 'u-1', username: 'admin', rol: 'admin' },
...overrides,
} as Request
}
it('admin con rol válido: actualiza y devuelve el perfil con sus módulos', async () => {
prismaMock.usuario.update.mockResolvedValue(usuarioFake({ id: 'u-9', rol: 'escrituracion' }))
const { res, json } = resFake()
await actualizarRolEndpoint(reqAdmin(), res)
expect(prismaMock.usuario.update).toHaveBeenCalledWith({
where: { id: 'u-9' },
data: { rol: 'escrituracion' },
})
expect(json).toHaveBeenCalledWith(
expect.objectContaining({ rol: 'escrituracion', modulos: expect.arrayContaining(['cobranza']) }),
)
})
it('sin rol admin: 403 y no toca la BD', async () => {
const { res } = resFake()
const error = await actualizarRolEndpoint(
reqAdmin({ usuarioToken: { sub: 'u-2', username: 'x', rol: 'vendedor' } }),
res,
).catch((e: unknown) => e)
expect(error).toMatchObject({ status: 403 })
expect(prismaMock.usuario.update).not.toHaveBeenCalled()
})
it('rol inválido: 400 y no toca la BD', async () => {
const { res } = resFake()
const error = await actualizarRolEndpoint(reqAdmin({ body: { rol: 'coordinador' } }), res).catch(
(e: unknown) => e,
)
expect(error).toMatchObject({ status: 400 })
expect(prismaMock.usuario.update).not.toHaveBeenCalled()
})
it('sin token verificado: 400', async () => {
const { res } = resFake()
await expect(actualizarRolEndpoint(reqAdmin({ usuarioToken: undefined }), res)).rejects.toThrow(
'Falta el token verificado',
)
expect(prismaMock.usuario.update).not.toHaveBeenCalled()
})
})