template-saas/apps/backend/dist/modules/notifications/__tests__/notifications.gateway.spec.js
rckrdmrd 50a821a415
Some checks failed
CI / Backend CI (push) Has been cancelled
CI / Frontend CI (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / CI Summary (push) Has been cancelled
[SIMCO-V38] feat: Actualizar a SIMCO v3.8.0
- HERENCIA-SIMCO.md actualizado con directivas v3.7 y v3.8
- Actualizaciones de configuracion

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-10 08:53:08 -06:00

373 lines
17 KiB
JavaScript

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const testing_1 = require("@nestjs/testing");
const notifications_gateway_1 = require("../gateways/notifications.gateway");
describe('NotificationsGateway', () => {
let gateway;
let mockServer;
const mockTenantId = '550e8400-e29b-41d4-a716-446655440001';
const mockUserId = '550e8400-e29b-41d4-a716-446655440002';
const mockSocketId = 'socket-123';
const createMockSocket = (overrides = {}) => ({
id: mockSocketId,
disconnect: jest.fn(),
join: jest.fn(),
emit: jest.fn(),
handshake: {
auth: { userId: mockUserId, tenantId: mockTenantId },
query: {},
},
...overrides,
});
const mockNotification = {
id: 'notif-001',
tenant_id: mockTenantId,
user_id: mockUserId,
type: 'info',
channel: 'in_app',
title: 'Test Notification',
message: 'This is a test notification',
is_read: false,
delivery_status: 'delivered',
created_at: new Date(),
};
beforeEach(async () => {
const module = await testing_1.Test.createTestingModule({
providers: [notifications_gateway_1.NotificationsGateway],
}).compile();
gateway = module.get(notifications_gateway_1.NotificationsGateway);
mockServer = {
to: jest.fn().mockReturnThis(),
emit: jest.fn(),
};
gateway.server = mockServer;
});
afterEach(() => {
jest.clearAllMocks();
});
describe('handleConnection', () => {
it('should accept connection with valid auth in handshake.auth', () => {
const mockSocket = createMockSocket();
gateway.handleConnection(mockSocket);
expect(mockSocket.userId).toBe(mockUserId);
expect(mockSocket.tenantId).toBe(mockTenantId);
expect(mockSocket.join).toHaveBeenCalledWith(`tenant:${mockTenantId}`);
expect(mockSocket.join).toHaveBeenCalledWith(`user:${mockUserId}`);
expect(mockSocket.emit).toHaveBeenCalledWith('connected', {
socketId: mockSocketId,
userId: mockUserId,
tenantId: mockTenantId,
});
});
it('should accept connection with valid auth in handshake.query', () => {
const mockSocket = createMockSocket({
handshake: {
auth: {},
query: { userId: mockUserId, tenantId: mockTenantId },
},
});
gateway.handleConnection(mockSocket);
expect(mockSocket.userId).toBe(mockUserId);
expect(mockSocket.tenantId).toBe(mockTenantId);
expect(mockSocket.disconnect).not.toHaveBeenCalled();
});
it('should disconnect client without userId', () => {
const mockSocket = createMockSocket({
handshake: {
auth: { tenantId: mockTenantId },
query: {},
},
});
gateway.handleConnection(mockSocket);
expect(mockSocket.disconnect).toHaveBeenCalled();
});
it('should disconnect client without tenantId', () => {
const mockSocket = createMockSocket({
handshake: {
auth: { userId: mockUserId },
query: {},
},
});
gateway.handleConnection(mockSocket);
expect(mockSocket.disconnect).toHaveBeenCalled();
});
it('should disconnect client with no auth', () => {
const mockSocket = createMockSocket({
handshake: {
auth: {},
query: {},
},
});
gateway.handleConnection(mockSocket);
expect(mockSocket.disconnect).toHaveBeenCalled();
});
it('should track multiple sockets for same user', () => {
const socket1 = createMockSocket({ id: 'socket-1' });
const socket2 = createMockSocket({ id: 'socket-2' });
gateway.handleConnection(socket1);
gateway.handleConnection(socket2);
expect(gateway.getTotalConnections()).toBe(2);
expect(gateway.getConnectedUsersCount()).toBe(1);
});
it('should handle connection error gracefully', () => {
const mockSocket = createMockSocket();
mockSocket.join = jest.fn().mockImplementation(() => {
throw new Error('Join failed');
});
gateway.handleConnection(mockSocket);
expect(mockSocket.disconnect).toHaveBeenCalled();
});
});
describe('handleDisconnect', () => {
it('should remove socket from tracking maps', () => {
const mockSocket = createMockSocket();
gateway.handleConnection(mockSocket);
expect(gateway.getTotalConnections()).toBe(1);
gateway.handleDisconnect(mockSocket);
expect(gateway.getTotalConnections()).toBe(0);
expect(gateway.getConnectedUsersCount()).toBe(0);
});
it('should keep user in map if other sockets remain', () => {
const socket1 = createMockSocket({ id: 'socket-1' });
const socket2 = createMockSocket({ id: 'socket-2' });
gateway.handleConnection(socket1);
gateway.handleConnection(socket2);
expect(gateway.getTotalConnections()).toBe(2);
gateway.handleDisconnect(socket1);
expect(gateway.getTotalConnections()).toBe(1);
expect(gateway.getConnectedUsersCount()).toBe(1);
});
it('should handle disconnect of unknown socket gracefully', () => {
const mockSocket = createMockSocket();
expect(() => gateway.handleDisconnect(mockSocket)).not.toThrow();
});
});
describe('emitToUser', () => {
it('should emit notification to all user sockets', async () => {
const socket1 = createMockSocket({ id: 'socket-1' });
const socket2 = createMockSocket({ id: 'socket-2' });
gateway.handleConnection(socket1);
gateway.handleConnection(socket2);
const result = await gateway.emitToUser(mockTenantId, mockUserId, mockNotification);
expect(result).toBe(2);
expect(mockServer.to).toHaveBeenCalledWith('socket-1');
expect(mockServer.to).toHaveBeenCalledWith('socket-2');
expect(mockServer.emit).toHaveBeenCalledWith('notification:created', mockNotification);
});
it('should return 0 if user has no active sockets', async () => {
const result = await gateway.emitToUser(mockTenantId, mockUserId, mockNotification);
expect(result).toBe(0);
expect(mockServer.to).not.toHaveBeenCalled();
});
it('should return 0 if user key does not exist', async () => {
const result = await gateway.emitToUser('different-tenant', 'different-user', mockNotification);
expect(result).toBe(0);
});
});
describe('emitToTenant', () => {
it('should broadcast event to tenant room', async () => {
const mockSocket = createMockSocket();
gateway.handleConnection(mockSocket);
await gateway.emitToTenant(mockTenantId, 'announcement', {
message: 'Hello everyone!',
});
expect(mockServer.to).toHaveBeenCalledWith(`tenant:${mockTenantId}`);
expect(mockServer.emit).toHaveBeenCalledWith('announcement', {
message: 'Hello everyone!',
});
});
it('should emit custom events to tenant', async () => {
await gateway.emitToTenant(mockTenantId, 'system:maintenance', {
startTime: '2024-01-01T00:00:00Z',
});
expect(mockServer.to).toHaveBeenCalledWith(`tenant:${mockTenantId}`);
expect(mockServer.emit).toHaveBeenCalledWith('system:maintenance', {
startTime: '2024-01-01T00:00:00Z',
});
});
});
describe('handleMarkAsRead', () => {
it('should broadcast read event to other user sockets', () => {
const socket1 = createMockSocket({ id: 'socket-1' });
const socket2 = createMockSocket({ id: 'socket-2' });
gateway.handleConnection(socket1);
gateway.handleConnection(socket2);
const payload = { notificationId: 'notif-001' };
socket1.userId = mockUserId;
socket1.tenantId = mockTenantId;
gateway.handleMarkAsRead(socket1, payload);
expect(mockServer.to).toHaveBeenCalledWith('socket-2');
expect(mockServer.to).not.toHaveBeenCalledWith('socket-1');
expect(mockServer.emit).toHaveBeenCalledWith('notification:read', payload);
});
it('should do nothing if client is not authenticated', () => {
const mockSocket = createMockSocket();
delete mockSocket.userId;
delete mockSocket.tenantId;
gateway.handleMarkAsRead(mockSocket, { notificationId: 'notif-001' });
expect(mockServer.to).not.toHaveBeenCalled();
});
it('should handle single socket user', () => {
const mockSocket = createMockSocket();
gateway.handleConnection(mockSocket);
mockSocket.userId = mockUserId;
mockSocket.tenantId = mockTenantId;
gateway.handleMarkAsRead(mockSocket, { notificationId: 'notif-001' });
expect(mockServer.to).not.toHaveBeenCalled();
});
});
describe('handleMarkAllAsRead', () => {
it('should broadcast read-all event to other user sockets', () => {
const socket1 = createMockSocket({ id: 'socket-1' });
const socket2 = createMockSocket({ id: 'socket-2' });
gateway.handleConnection(socket1);
gateway.handleConnection(socket2);
socket1.userId = mockUserId;
socket1.tenantId = mockTenantId;
gateway.handleMarkAllAsRead(socket1);
expect(mockServer.to).toHaveBeenCalledWith('socket-2');
expect(mockServer.emit).toHaveBeenCalledWith('notification:read-all', {});
});
it('should do nothing if client is not authenticated', () => {
const mockSocket = createMockSocket();
delete mockSocket.userId;
delete mockSocket.tenantId;
gateway.handleMarkAllAsRead(mockSocket);
expect(mockServer.to).not.toHaveBeenCalled();
});
});
describe('handleGetUnreadCount', () => {
it('should return acknowledgement event', () => {
const mockSocket = createMockSocket();
mockSocket.userId = mockUserId;
mockSocket.tenantId = mockTenantId;
const result = gateway.handleGetUnreadCount(mockSocket);
expect(result).toEqual({ event: 'notification:unread-count-requested' });
});
});
describe('emitUnreadCount', () => {
it('should emit unread count to all user sockets', async () => {
const socket1 = createMockSocket({ id: 'socket-1' });
const socket2 = createMockSocket({ id: 'socket-2' });
gateway.handleConnection(socket1);
gateway.handleConnection(socket2);
await gateway.emitUnreadCount(mockTenantId, mockUserId, 5);
expect(mockServer.to).toHaveBeenCalledWith('socket-1');
expect(mockServer.to).toHaveBeenCalledWith('socket-2');
expect(mockServer.emit).toHaveBeenCalledWith('notification:unread-count', {
count: 5,
});
});
it('should do nothing if user has no sockets', async () => {
await gateway.emitUnreadCount(mockTenantId, 'unknown-user', 5);
expect(mockServer.to).not.toHaveBeenCalled();
});
});
describe('emitNotificationDeleted', () => {
it('should emit deleted event to all user sockets', async () => {
const mockSocket = createMockSocket();
gateway.handleConnection(mockSocket);
await gateway.emitNotificationDeleted(mockTenantId, mockUserId, 'notif-001');
expect(mockServer.to).toHaveBeenCalledWith(mockSocketId);
expect(mockServer.emit).toHaveBeenCalledWith('notification:deleted', {
notificationId: 'notif-001',
});
});
it('should do nothing if user has no sockets', async () => {
await gateway.emitNotificationDeleted(mockTenantId, 'unknown-user', 'notif-001');
expect(mockServer.to).not.toHaveBeenCalled();
});
});
describe('getConnectedUsersCount', () => {
it('should return count of unique connected users', () => {
const socket1 = createMockSocket({ id: 'socket-1' });
const socket2 = createMockSocket({
id: 'socket-2',
handshake: {
auth: { userId: 'user-2', tenantId: mockTenantId },
query: {},
},
});
gateway.handleConnection(socket1);
gateway.handleConnection(socket2);
expect(gateway.getConnectedUsersCount()).toBe(2);
});
it('should return 0 when no users connected', () => {
expect(gateway.getConnectedUsersCount()).toBe(0);
});
});
describe('getTotalConnections', () => {
it('should return total socket connections', () => {
const socket1 = createMockSocket({ id: 'socket-1' });
const socket2 = createMockSocket({ id: 'socket-2' });
const socket3 = createMockSocket({ id: 'socket-3' });
gateway.handleConnection(socket1);
gateway.handleConnection(socket2);
gateway.handleConnection(socket3);
expect(gateway.getTotalConnections()).toBe(3);
});
it('should return 0 when no connections', () => {
expect(gateway.getTotalConnections()).toBe(0);
});
});
describe('isUserOnline', () => {
it('should return true if user has active sockets', () => {
const mockSocket = createMockSocket();
gateway.handleConnection(mockSocket);
expect(gateway.isUserOnline(mockTenantId, mockUserId)).toBe(true);
});
it('should return false if user has no sockets', () => {
expect(gateway.isUserOnline(mockTenantId, mockUserId)).toBe(false);
});
it('should return false after user disconnects', () => {
const mockSocket = createMockSocket();
gateway.handleConnection(mockSocket);
gateway.handleDisconnect(mockSocket);
expect(gateway.isUserOnline(mockTenantId, mockUserId)).toBe(false);
});
it('should check correct tenant and user combination', () => {
const mockSocket = createMockSocket();
gateway.handleConnection(mockSocket);
expect(gateway.isUserOnline(mockTenantId, mockUserId)).toBe(true);
expect(gateway.isUserOnline('other-tenant', mockUserId)).toBe(false);
expect(gateway.isUserOnline(mockTenantId, 'other-user')).toBe(false);
});
});
describe('multi-tenant isolation', () => {
it('should isolate users by tenant', async () => {
const tenant1User = createMockSocket({
id: 'socket-t1',
handshake: {
auth: { userId: 'user-1', tenantId: 'tenant-1' },
query: {},
},
});
const tenant2User = createMockSocket({
id: 'socket-t2',
handshake: {
auth: { userId: 'user-1', tenantId: 'tenant-2' },
query: {},
},
});
gateway.handleConnection(tenant1User);
gateway.handleConnection(tenant2User);
const result1 = await gateway.emitToUser('tenant-1', 'user-1', mockNotification);
expect(result1).toBe(1);
expect(mockServer.to).toHaveBeenCalledWith('socket-t1');
expect(mockServer.to).not.toHaveBeenCalledWith('socket-t2');
});
it('should broadcast to correct tenant only', async () => {
const tenant1User = createMockSocket({
id: 'socket-t1',
handshake: {
auth: { userId: 'user-1', tenantId: 'tenant-1' },
query: {},
},
});
gateway.handleConnection(tenant1User);
await gateway.emitToTenant('tenant-1', 'test-event', { data: 'test' });
expect(mockServer.to).toHaveBeenCalledWith('tenant:tenant-1');
});
});
});
//# sourceMappingURL=notifications.gateway.spec.js.map