import { jest } from '@jest/globals'; // Mock dependencies jest.unstable_mockModule('../../config.js', () => ({ loadConfig: jest.fn(() => ({ server: { logLevel: 'info' } })) })); jest.unstable_mockModule('../../auth/auth.js', () => ({ KeycloakAuth: jest.fn().mockImplementation(() => ({ verifyToken: jest.fn() })), createAuthMiddleware: jest.fn(() => (socket, next) => next()) })); jest.unstable_mockModule('../../utils.js', () => ({ generateHostOTP: jest.fn() })); jest.unstable_mockModule('../../lock/lockmanager.js', () => ({ LockManager: jest.fn().mockImplementation(() => ({ lockObject: jest.fn(), unlockObject: jest.fn(), getObjectLock: jest.fn() })) })); jest.unstable_mockModule('../../updates/updatemanager.js', () => ({ UpdateManager: jest.fn().mockImplementation(() => ({ subscribeToObjectNew: jest.fn(), subscribeToObjectDelete: jest.fn(), subscribeToObjectUpdate: jest.fn(), removeObjectNewListener: jest.fn(), removeObjectDeleteListener: jest.fn(), removeObjectUpdateListener: jest.fn() })) })); jest.unstable_mockModule('../../actions/actionmanager.js', () => ({ ActionManager: jest.fn().mockImplementation(() => ({ sendObjectAction: jest.fn(), removeAllListeners: jest.fn() })) })); jest.unstable_mockModule('../../events/eventmanager.js', () => ({ EventManager: jest.fn().mockImplementation(() => ({ subscribeToObjectEvent: jest.fn(), removeObjectEventsListener: jest.fn(), removeAllListeners: jest.fn() })) })); jest.unstable_mockModule('../../stats/statsmanager.js', () => ({ StatsManager: jest.fn().mockImplementation(() => ({ subscribeToStats: jest.fn(), removeStatsListener: jest.fn(), removeAllListeners: jest.fn() })) })); jest.unstable_mockModule('../../notification/notificationmanager.js', () => ({ NotificationManager: jest.fn().mockImplementation(() => ({ subscribe: jest.fn().mockResolvedValue({ success: true }), removeAllListeners: jest.fn().mockResolvedValue({ success: true }) })) })); jest.unstable_mockModule('../../server/servermanager.js', () => ({ ServerManager: jest.fn().mockImplementation(() => ({ getServerVersion: jest.fn(() => ({ version: '1.0.0', buildNumber: 'dev' })) })) })); jest.unstable_mockModule('log4js', () => ({ default: { getLogger: () => ({ level: 'info', debug: jest.fn(), error: jest.fn(), warn: jest.fn(), trace: jest.fn(), info: jest.fn() }) } })); const { SocketUser } = await import('../socketuser.js'); const { generateHostOTP } = await import('../../utils.js'); describe('SocketUser', () => { let mockSocket; let mockSocketManager; let socketUser; beforeEach(() => { jest.clearAllMocks(); mockSocket = { id: 'test-user-socket-id', use: jest.fn(), on: jest.fn(), emit: jest.fn() }; mockSocketManager = { templateManager: { renderTemplate: jest.fn(), renderPDF: jest.fn() } }; socketUser = new SocketUser(mockSocket, mockSocketManager); }); it('should initialize correctly and setup event handlers', () => { expect(mockSocket.use).toHaveBeenCalled(); expect(mockSocket.on).toHaveBeenCalledWith( 'authenticate', expect.any(Function) ); expect(mockSocket.on).toHaveBeenCalledWith('lock', expect.any(Function)); expect(mockSocket.on).toHaveBeenCalledWith( 'disconnect', expect.any(Function) ); expect(mockSocket.on).toHaveBeenCalledWith( 'getServerVersion', expect.any(Function) ); }); describe('handleAuthenticateEvent', () => { it('should authenticate user with valid token', async () => { const data = { token: 'valid-token' }; const callback = jest.fn(); const mockUser = { _id: 'user-id-obj', username: 'testuser' }; socketUser.keycloakAuth.verifyToken.mockResolvedValue({ valid: true, user: mockUser }); await socketUser.handleAuthenticateEvent(data, callback); expect(socketUser.keycloakAuth.verifyToken).toHaveBeenCalledWith( 'valid-token' ); expect(socketUser.authenticated).toBe(true); expect(socketUser.user).toEqual(mockUser); expect(socketUser.id).toBe('user-id-obj'); expect(socketUser.notificationManager.subscribe).toHaveBeenCalled(); expect(callback).toHaveBeenCalledWith({ valid: true, user: mockUser }); }); it('should not authenticate user with invalid token', async () => { const data = { token: 'invalid-token' }; const callback = jest.fn(); socketUser.keycloakAuth.verifyToken.mockResolvedValue({ valid: false }); await socketUser.handleAuthenticateEvent(data, callback); expect(socketUser.authenticated).toBe(false); expect(callback).toHaveBeenCalledWith({ valid: false }); }); }); describe('lock event handlers', () => { beforeEach(() => { socketUser.user = { _id: 'user-id' }; }); it('handleLockEvent should call lockManager.lockObject', async () => { const data = { _id: 'obj-1', type: 'printer' }; await socketUser.handleLockEvent(data); expect(socketUser.lockManager.lockObject).toHaveBeenCalledWith({ ...data, user: 'user-id' }); }); it('handleUnlockEvent should call lockManager.unlockObject', async () => { const data = { _id: 'obj-1' }; await socketUser.handleUnlockEvent(data); expect(socketUser.lockManager.unlockObject).toHaveBeenCalledWith({ ...data, user: 'user-id' }); }); }); describe('object type subscription handlers', () => { it('should pass filters to type subscription methods', async () => { const data = { objectType: 'note', filter: { 'parent._id': 'parent-id' } }; const callback = jest.fn(); await socketUser.handleSubscribeToObjectTypeUpdateEvent(data, callback); expect(socketUser.updateManager.subscribeToObjectNew).toHaveBeenCalledWith( 'note', data.filter ); expect( socketUser.updateManager.subscribeToObjectDelete ).toHaveBeenCalledWith('note', data.filter); expect(callback).toHaveBeenCalledWith({ success: true }); }); it('should pass filters to type unsubscribe methods', async () => { const data = { objectType: 'note', filter: { 'parent._id': 'parent-id' } }; await socketUser.handleUnsubscribeToObjectTypeUpdateEvent(data); expect( socketUser.updateManager.removeObjectNewListener ).toHaveBeenCalledWith('note', data.filter); expect( socketUser.updateManager.removeObjectDeleteListener ).toHaveBeenCalledWith('note', data.filter); }); }); describe('handleGenerateHostOtpEvent', () => { it('should call generateHostOTP and callback', async () => { const data = { _id: 'host-id' }; const callback = jest.fn(); generateHostOTP.mockResolvedValue('otp-123'); await socketUser.handleGenerateHostOtpEvent(data, callback); expect(generateHostOTP).toHaveBeenCalledWith('host-id'); expect(callback).toHaveBeenCalledWith('otp-123'); }); }); describe('handleGetServerVersionEvent', () => { it('should return server version details', async () => { const callback = jest.fn(); await socketUser.handleGetServerVersionEvent({}, callback); expect(socketUser.serverManager.getServerVersion).toHaveBeenCalled(); expect(callback).toHaveBeenCalledWith({ version: '1.0.0', buildNumber: 'dev' }); }); }); describe('handleDisconnect', () => { it('should remove all listeners', async () => { await socketUser.handleDisconnect(); expect(socketUser.actionManager.removeAllListeners).toHaveBeenCalled(); expect(socketUser.eventManager.removeAllListeners).toHaveBeenCalled(); expect(socketUser.statsManager.removeAllListeners).toHaveBeenCalled(); expect(socketUser.notificationManager.removeAllListeners).toHaveBeenCalled(); }); }); });