feat: vínculo con usuario Monday en landing + alta prellenada desde leads
API: - GET /api/monday/usuarios: usuarios activos no-guest del workspace - PATCH /api/usuarios/me/monday: vincula/desvincula el mondayUserId del usuario autenticado (valida contra el workspace) - GET /api/leads/buscar?q=: búsqueda de leads por nombre/teléfono/email (sin acentos, orden updatedAt desc, límite 15) - aPublico/me ahora incluyen mondayUserId Landing (/): - Nueva sección 'Configuración de cuenta': login inline, selector del usuario de Monday, guardar/desvincular, cerrar sesión Alta de clientes (/client): - Búsqueda de leads de Monday con debounce en la pantalla de entrada; al elegir uno prellena nombre/email/celular y guarda leadMondayId - El picker (borradores + alta nueva + búsqueda de lead) es ahora siempre la pantalla de entrada Tests: api 294, landing 8, alta-clientes 96 — todos verdes
This commit is contained in:
139
api-gateway/src/controllers/usuarios.controller.test.ts
Normal file
139
api-gateway/src/controllers/usuarios.controller.test.ts
Normal file
@@ -0,0 +1,139 @@
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import type { Request, Response } from 'express'
|
||||
import type { Usuario } from '@prisma/client'
|
||||
import {
|
||||
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,
|
||||
})
|
||||
})
|
||||
|
||||
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()
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user