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') }) }) })