const http = require('http'); const { Server } = require('socket.io'); const Client = require('socket.io-client'); const { initializeSocket } = require('../socket'); const { generateToken } = require('../utils/auth'); const { prisma } = require('../utils/db'); describe('Socket.IO Server', () => { let httpServer; let io; let serverSocket; let clientSocket; let testUser; let testToken; const port = 3001; beforeAll(async () => { // Create test user testUser = await prisma.user.create({ data: { username: 'sockettest', email: 'sockettest@example.com', passwordHash: 'hashedpassword', avatar: 'https://example.com/avatar.jpg', }, }); testToken = generateToken({ userId: testUser.id }); // Create HTTP server and initialize Socket.IO httpServer = http.createServer(); io = initializeSocket(httpServer); httpServer.listen(port); // Wait for server to be ready await new Promise((resolve) => { httpServer.once('listening', resolve); }); }); afterAll(async () => { // Cleanup test user await prisma.user.delete({ where: { id: testUser.id }, }); // Close server and client if (clientSocket) clientSocket.close(); io.close(); httpServer.close(); }); afterEach(() => { if (clientSocket && clientSocket.connected) { clientSocket.close(); } }); describe('Authentication', () => { test('should reject connection without token', (done) => { clientSocket = Client(`http://localhost:${port}`); clientSocket.on('connect_error', (error) => { expect(error.message).toBe('Authentication required'); done(); }); clientSocket.on('connect', () => { done(new Error('Should not connect without token')); }); }); test('should reject connection with invalid token', (done) => { clientSocket = Client(`http://localhost:${port}`, { auth: { token: 'invalid-token' }, }); clientSocket.on('connect_error', (error) => { expect(error.message).toBe('Invalid token'); done(); }); }); test('should accept connection with valid token', (done) => { clientSocket = Client(`http://localhost:${port}`, { auth: { token: testToken }, }); clientSocket.on('connect', () => { expect(clientSocket.connected).toBe(true); done(); }); clientSocket.on('connect_error', (error) => { done(error); }); }); }); describe('Event Rooms', () => { let testEvent; let testChatRoom; beforeAll(async () => { // Create test event and chat room testEvent = await prisma.event.create({ data: { name: 'Test Event', location: 'Test Location', startDate: new Date('2025-12-01'), endDate: new Date('2025-12-03'), }, }); testChatRoom = await prisma.chatRoom.create({ data: { eventId: testEvent.id, type: 'event', }, }); }); afterAll(async () => { // Cleanup test data await prisma.chatRoom.delete({ where: { id: testChatRoom.id }, }); await prisma.event.delete({ where: { id: testEvent.id }, }); }); test('should join event room successfully', (done) => { clientSocket = Client(`http://localhost:${port}`, { auth: { token: testToken }, }); clientSocket.on('connect', () => { clientSocket.emit('join_event_room', { eventId: testEvent.id }); }); clientSocket.on('active_users', (users) => { expect(Array.isArray(users)).toBe(true); expect(users.length).toBeGreaterThan(0); const currentUser = users.find(u => u.userId === testUser.id); expect(currentUser).toBeDefined(); expect(currentUser.username).toBe(testUser.username); done(); }); clientSocket.on('error', (error) => { done(error); }); }); test('should receive user_joined notification', (done) => { const client1 = Client(`http://localhost:${port}`, { auth: { token: testToken }, }); client1.on('connect', () => { client1.emit('join_event_room', { eventId: testEvent.id }); // Create second user and join the same room const client2Token = generateToken({ userId: testUser.id }); const client2 = Client(`http://localhost:${port}`, { auth: { token: client2Token }, }); client1.on('user_joined', (userData) => { expect(userData.userId).toBe(testUser.id); expect(userData.username).toBe(testUser.username); client1.close(); client2.close(); done(); }); client2.on('connect', () => { client2.emit('join_event_room', { eventId: testEvent.id }); }); }); }); test('should send and receive event messages', (done) => { clientSocket = Client(`http://localhost:${port}`, { auth: { token: testToken }, }); const messageContent = 'Test message content'; clientSocket.on('connect', () => { clientSocket.emit('join_event_room', { eventId: testEvent.id }); clientSocket.on('active_users', () => { // Wait for active_users, then send message clientSocket.emit('send_event_message', { eventId: testEvent.id, content: messageContent, }); }); }); clientSocket.on('event_message', async (message) => { expect(message.content).toBe(messageContent); expect(message.userId).toBe(testUser.id); expect(message.username).toBe(testUser.username); expect(message.type).toBe('text'); expect(message.createdAt).toBeDefined(); // Verify message was saved to database const dbMessage = await prisma.message.findUnique({ where: { id: message.id }, }); expect(dbMessage).toBeDefined(); expect(dbMessage.content).toBe(messageContent); // Cleanup await prisma.message.delete({ where: { id: message.id } }); done(); }); clientSocket.on('error', (error) => { done(error); }); }); test('should leave event room and update active users', (done) => { clientSocket = Client(`http://localhost:${port}`, { auth: { token: testToken }, }); clientSocket.on('connect', () => { clientSocket.emit('join_event_room', { eventId: testEvent.id }); clientSocket.on('active_users', (users) => { if (users.length > 0) { // User joined, now leave clientSocket.emit('leave_event_room', { eventId: testEvent.id }); setTimeout(() => { done(); }, 100); } }); }); }); }); describe('Match Rooms', () => { let testUser2; let testMatch; let testMatchRoom; let testEvent; beforeAll(async () => { // Create test event for match testEvent = await prisma.event.create({ data: { name: 'Match Test Event', location: 'Test Location', startDate: new Date('2025-12-01'), endDate: new Date('2025-12-03'), }, }); // Create second user with timestamp to avoid conflicts const timestamp = Date.now(); testUser2 = await prisma.user.create({ data: { username: `sockettest2_${timestamp}`, email: `sockettest2_${timestamp}@example.com`, passwordHash: 'hashedpassword', avatar: 'https://example.com/avatar2.jpg', }, }); // Create match testMatch = await prisma.match.create({ data: { status: 'active', user1: { connect: { id: testUser.id } }, user2: { connect: { id: testUser2.id } }, event: { connect: { id: testEvent.id } }, }, }); // Create match chat room testMatchRoom = await prisma.chatRoom.create({ data: { type: 'private', }, }); // Link room to match await prisma.match.update({ where: { id: testMatch.id }, data: { roomId: testMatchRoom.id }, }); }); afterAll(async () => { // Cleanup await prisma.match.delete({ where: { id: testMatch.id }, }); await prisma.chatRoom.delete({ where: { id: testMatchRoom.id }, }); await prisma.user.delete({ where: { id: testUser2.id }, }); await prisma.event.delete({ where: { id: testEvent.id }, }); }); test('should join match room successfully', (done) => { clientSocket = Client(`http://localhost:${port}`, { auth: { token: testToken }, }); clientSocket.on('connect', () => { clientSocket.emit('join_match_room', { matchId: testMatch.id }); // Just verify no error occurs setTimeout(() => { expect(clientSocket.connected).toBe(true); done(); }, 100); }); clientSocket.on('error', (error) => { done(error); }); }); test('should send and receive match messages', (done) => { clientSocket = Client(`http://localhost:${port}`, { auth: { token: testToken }, }); const messageContent = 'Private match message'; clientSocket.on('connect', () => { clientSocket.emit('join_match_room', { matchId: testMatch.id }); setTimeout(() => { clientSocket.emit('send_match_message', { matchId: testMatch.id, content: messageContent, }); }, 100); }); clientSocket.on('match_message', async (message) => { expect(message.content).toBe(messageContent); expect(message.userId).toBe(testUser.id); expect(message.username).toBe(testUser.username); expect(message.type).toBe('text'); // Verify message was saved const dbMessage = await prisma.message.findUnique({ where: { id: message.id }, }); expect(dbMessage).toBeDefined(); expect(dbMessage.roomId).toBe(testMatchRoom.id); // Cleanup await prisma.message.delete({ where: { id: message.id } }); done(); }); clientSocket.on('error', (error) => { done(error); }); }); test('should handle match room not found error', (done) => { clientSocket = Client(`http://localhost:${port}`, { auth: { token: testToken }, }); clientSocket.on('connect', () => { clientSocket.emit('join_match_room', { matchId: 99999 }); setTimeout(() => { clientSocket.emit('send_match_message', { matchId: 99999, content: 'Test', }); }, 100); }); clientSocket.on('error', (error) => { expect(error.message).toBe('Match room not found'); done(); }); }); }); describe('Disconnect Handling', () => { let testEvent; let testChatRoom; beforeAll(async () => { testEvent = await prisma.event.create({ data: { name: 'Disconnect Test Event', location: 'Test Location', startDate: new Date('2025-12-01'), endDate: new Date('2025-12-03'), }, }); testChatRoom = await prisma.chatRoom.create({ data: { eventId: testEvent.id, type: 'event', }, }); }); afterAll(async () => { await prisma.chatRoom.delete({ where: { id: testChatRoom.id }, }); await prisma.event.delete({ where: { id: testEvent.id }, }); }); test('should handle disconnect and update active users', (done) => { const client1 = Client(`http://localhost:${port}`, { auth: { token: testToken }, }); const client2Token = generateToken({ userId: testUser.id }); const client2 = Client(`http://localhost:${port}`, { auth: { token: client2Token }, }); let client1Connected = false; let client2Connected = false; client1.on('connect', () => { client1Connected = true; client1.emit('join_event_room', { eventId: testEvent.id }); }); client2.on('connect', () => { client2Connected = true; client2.emit('join_event_room', { eventId: testEvent.id }); }); client2.on('user_left', (userData) => { expect(userData.userId).toBe(testUser.id); expect(userData.username).toBe(testUser.username); client2.close(); done(); }); // Wait for both to join, then disconnect client1 setTimeout(() => { if (client1Connected && client2Connected) { client1.close(); } }, 500); }); }); describe('Error Handling', () => { let testEvent; beforeAll(async () => { testEvent = await prisma.event.create({ data: { name: 'Error Test Event', location: 'Test Location', startDate: new Date('2025-12-01'), endDate: new Date('2025-12-03'), }, }); }); afterAll(async () => { await prisma.event.delete({ where: { id: testEvent.id }, }); }); test('should handle chat room not found error', (done) => { clientSocket = Client(`http://localhost:${port}`, { auth: { token: testToken }, }); clientSocket.on('connect', () => { clientSocket.emit('join_event_room', { eventId: testEvent.id }); setTimeout(() => { clientSocket.emit('send_event_message', { eventId: testEvent.id, content: 'Test message', }); }, 100); }); clientSocket.on('error', (error) => { expect(error.message).toBe('Chat room not found'); done(); }); }); }); });