feat(mejoras): 14 mejoras al CRM Frasa
Fase 1 - UI: dark mode, skeletons, toasts Fase 2 - Filtros: avanzados Mi día, búsqueda global Ctrl+K Fase 3 - Datos: export Excel, notas apartados, auditoría Fase 4 - Dashboard: gráficas vendedor + métricas uso Fase 5 - Responsive: tablets optimizadas Fase 6 - PWA: IndexedDB offline, push notifications Backend: 3 tablas nuevas, 6 services, 6 endpoints nuevos 721 API tests + fronts verdes.
This commit is contained in:
140
api-gateway/src/services/push.service.test.ts
Normal file
140
api-gateway/src/services/push.service.test.ts
Normal file
@@ -0,0 +1,140 @@
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
|
||||
// ---------- Prisma mockeado ----------
|
||||
const prismaMock = vi.hoisted(() => ({
|
||||
pushSubscription: {
|
||||
upsert: vi.fn(),
|
||||
deleteMany: vi.fn(),
|
||||
findUnique: vi.fn(),
|
||||
},
|
||||
usuario: {
|
||||
findFirst: vi.fn(),
|
||||
},
|
||||
}))
|
||||
|
||||
vi.mock('./prisma.js', () => ({ prisma: prismaMock }))
|
||||
|
||||
// ---------- web-push mockeado ----------
|
||||
const webPushMock = vi.hoisted(() => ({
|
||||
setVapidDetails: vi.fn(),
|
||||
sendNotification: vi.fn(),
|
||||
}))
|
||||
|
||||
vi.mock('web-push', () => ({ default: webPushMock }))
|
||||
|
||||
// ---------- config mockeado ----------
|
||||
vi.mock('../config/index.js', () => ({
|
||||
config: {
|
||||
push: {
|
||||
vapidPublicKey: 'test-public-key',
|
||||
vapidPrivateKey: 'test-private-key',
|
||||
vapidEmail: 'mailto:test@frasa.com',
|
||||
},
|
||||
},
|
||||
}))
|
||||
|
||||
import {
|
||||
registrarSuscripcion,
|
||||
eliminarSuscripcion,
|
||||
enviarPush,
|
||||
usuarioIdPorMondayUserId,
|
||||
obtenerVapidPublicKey,
|
||||
} from './push.service.js'
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
describe('push.service', () => {
|
||||
describe('registrarSuscripcion', () => {
|
||||
it('hace upsert con los datos de la suscripción', async () => {
|
||||
prismaMock.pushSubscription.upsert.mockResolvedValue({})
|
||||
await registrarSuscripcion('user-1', {
|
||||
endpoint: 'https://fcm.googleapis.com/fcm/send/abc',
|
||||
p256dh: 'key123',
|
||||
auth: 'auth456',
|
||||
})
|
||||
expect(prismaMock.pushSubscription.upsert).toHaveBeenCalledWith({
|
||||
where: { usuarioId: 'user-1' },
|
||||
update: {
|
||||
endpoint: 'https://fcm.googleapis.com/fcm/send/abc',
|
||||
p256dh: 'key123',
|
||||
auth: 'auth456',
|
||||
},
|
||||
create: {
|
||||
usuarioId: 'user-1',
|
||||
endpoint: 'https://fcm.googleapis.com/fcm/send/abc',
|
||||
p256dh: 'key123',
|
||||
auth: 'auth456',
|
||||
},
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('eliminarSuscripcion', () => {
|
||||
it('elimina todas las suscripciones del usuario', async () => {
|
||||
prismaMock.pushSubscription.deleteMany.mockResolvedValue({ count: 1 })
|
||||
await eliminarSuscripcion('user-1')
|
||||
expect(prismaMock.pushSubscription.deleteMany).toHaveBeenCalledWith({ where: { usuarioId: 'user-1' } })
|
||||
})
|
||||
})
|
||||
|
||||
describe('enviarPush', () => {
|
||||
it('envía push si el usuario tiene suscripción', async () => {
|
||||
prismaMock.pushSubscription.findUnique.mockResolvedValue({
|
||||
endpoint: 'https://fcm.googleapis.com/fcm/send/abc',
|
||||
p256dh: 'key123',
|
||||
auth: 'auth456',
|
||||
})
|
||||
webPushMock.sendNotification.mockResolvedValue({ statusCode: 201 })
|
||||
|
||||
const result = await enviarPush('user-1', 'Test', 'Cuerpo del mensaje')
|
||||
expect(result).toBe(true)
|
||||
expect(webPushMock.sendNotification).toHaveBeenCalledWith(
|
||||
{ endpoint: 'https://fcm.googleapis.com/fcm/send/abc', keys: { p256dh: 'key123', auth: 'auth456' } },
|
||||
expect.stringContaining('"titulo":"Test"'),
|
||||
)
|
||||
})
|
||||
|
||||
it('devuelve false si el usuario no tiene suscripción', async () => {
|
||||
prismaMock.pushSubscription.findUnique.mockResolvedValue(null)
|
||||
const result = await enviarPush('user-1', 'Test', 'Cuerpo')
|
||||
expect(result).toBe(false)
|
||||
expect(webPushMock.sendNotification).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('elimina suscripción inválida (404) y devuelve false', async () => {
|
||||
prismaMock.pushSubscription.findUnique.mockResolvedValue({
|
||||
endpoint: 'https://expired',
|
||||
p256dh: 'key',
|
||||
auth: 'auth',
|
||||
})
|
||||
webPushMock.sendNotification.mockRejectedValue({ statusCode: 404 })
|
||||
prismaMock.pushSubscription.deleteMany.mockResolvedValue({ count: 1 })
|
||||
|
||||
const result = await enviarPush('user-1', 'Test', 'Cuerpo')
|
||||
expect(result).toBe(false)
|
||||
expect(prismaMock.pushSubscription.deleteMany).toHaveBeenCalledWith({ where: { usuarioId: 'user-1' } })
|
||||
})
|
||||
})
|
||||
|
||||
describe('usuarioIdPorMondayUserId', () => {
|
||||
it('devuelve el usuarioId si encuentra el usuario', async () => {
|
||||
prismaMock.usuario.findFirst.mockResolvedValue({ id: 'local-user-1' })
|
||||
const result = await usuarioIdPorMondayUserId(12345)
|
||||
expect(result).toBe('local-user-1')
|
||||
})
|
||||
|
||||
it('devuelve null si no encuentra el usuario', async () => {
|
||||
prismaMock.usuario.findFirst.mockResolvedValue(null)
|
||||
const result = await usuarioIdPorMondayUserId(99999)
|
||||
expect(result).toBeNull()
|
||||
})
|
||||
})
|
||||
|
||||
describe('obtenerVapidPublicKey', () => {
|
||||
it('devuelve la VAPID public key del config', () => {
|
||||
expect(obtenerVapidPublicKey()).toBe('test-public-key')
|
||||
})
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user