"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