import { Test, TestingModule } from '@nestjs/testing'; import { GetOnlineCountHandler } from '../../../../src/application/queries/get-online-count/get-online-count.handler'; import { GetOnlineCountQuery } from '../../../../src/application/queries/get-online-count/get-online-count.query'; import { PresenceRedisRepository } from '../../../../src/infrastructure/redis/presence-redis.repository'; import { OnlineDetectionService } from '../../../../src/domain/services/online-detection.service'; import { TimeWindow } from '../../../../src/domain/value-objects/time-window.vo'; describe('GetOnlineCountHandler', () => { let handler: GetOnlineCountHandler; let presenceRedisRepository: jest.Mocked; let onlineDetectionService: OnlineDetectionService; beforeEach(async () => { const mockPresenceRedisRepository = { updateUserPresence: jest.fn(), countOnlineUsers: jest.fn(), getOnlineUsers: jest.fn(), removeExpiredUsers: jest.fn(), }; const module: TestingModule = await Test.createTestingModule({ providers: [ GetOnlineCountHandler, { provide: PresenceRedisRepository, useValue: mockPresenceRedisRepository, }, OnlineDetectionService, ], }).compile(); handler = module.get(GetOnlineCountHandler); presenceRedisRepository = module.get(PresenceRedisRepository); onlineDetectionService = module.get(OnlineDetectionService); }); afterEach(() => { jest.clearAllMocks(); }); describe('execute', () => { it('should return online count', async () => { presenceRedisRepository.countOnlineUsers.mockResolvedValue(1500); const query = new GetOnlineCountQuery(); const result = await handler.execute(query); expect(result.count).toBe(1500); }); it('should return window seconds from OnlineDetectionService', async () => { presenceRedisRepository.countOnlineUsers.mockResolvedValue(100); const query = new GetOnlineCountQuery(); const result = await handler.execute(query); expect(result.windowSeconds).toBe(TimeWindow.DEFAULT_ONLINE_WINDOW_SECONDS); }); it('should return queriedAt timestamp', async () => { presenceRedisRepository.countOnlineUsers.mockResolvedValue(100); const beforeQuery = new Date(); const query = new GetOnlineCountQuery(); const result = await handler.execute(query); const afterQuery = new Date(); expect(result.queriedAt).toBeInstanceOf(Date); expect(result.queriedAt.getTime()).toBeGreaterThanOrEqual(beforeQuery.getTime()); expect(result.queriedAt.getTime()).toBeLessThanOrEqual(afterQuery.getTime()); }); it('should call countOnlineUsers with correct threshold', async () => { presenceRedisRepository.countOnlineUsers.mockResolvedValue(500); const query = new GetOnlineCountQuery(); await handler.execute(query); expect(presenceRedisRepository.countOnlineUsers).toHaveBeenCalledWith( expect.any(Number), ); // Verify threshold is approximately (now - 180 seconds) const calledThreshold = presenceRedisRepository.countOnlineUsers.mock.calls[0][0]; const expectedThreshold = Math.floor(Date.now() / 1000) - 180; expect(Math.abs(calledThreshold - expectedThreshold)).toBeLessThanOrEqual(1); }); it('should handle zero online users', async () => { presenceRedisRepository.countOnlineUsers.mockResolvedValue(0); const query = new GetOnlineCountQuery(); const result = await handler.execute(query); expect(result.count).toBe(0); }); it('should handle large number of online users', async () => { presenceRedisRepository.countOnlineUsers.mockResolvedValue(1000000); const query = new GetOnlineCountQuery(); const result = await handler.execute(query); expect(result.count).toBe(1000000); }); it('should throw error when Redis fails', async () => { presenceRedisRepository.countOnlineUsers.mockRejectedValue( new Error('Redis connection timeout'), ); const query = new GetOnlineCountQuery(); await expect(handler.execute(query)).rejects.toThrow('Redis connection timeout'); }); }); });