Skip to content

Commit b861e99

Browse files
authored
Merge pull request #127 from pixie-git/bugfix/user-wide-disconnection
fix(socket): disconnect all user sessions from lobby on kick/ban
2 parents ae5bc7b + 707bb9c commit b861e99

2 files changed

Lines changed: 61 additions & 5 deletions

File tree

server/src/utils/socketUtils.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,15 @@ export const disconnectUserFromLobby = async (
2929
reason: string
3030
): Promise<boolean> => {
3131
const sockets = await io.in(lobbyId).fetchSockets();
32-
const targetSocket = sockets.find((s: any) => s.data.user?.id === userId);
32+
const targetSockets = sockets.filter((s: any) => s.data.user?.id === userId);
3333

34-
if (!targetSocket) return false;
34+
if (targetSockets.length === 0) return false;
3535

36-
targetSocket.emit('FORCE_DISCONNECT', { lobbyId, reason });
37-
io.to(lobbyId).except(targetSocket.id).emit('USER_LEFT', targetSocket.data.user);
38-
targetSocket.leave(lobbyId);
36+
for (const socket of targetSockets) {
37+
socket.emit('FORCE_DISCONNECT', { lobbyId, reason });
38+
io.to(lobbyId).except(socket.id).emit('USER_LEFT', socket.data.user);
39+
socket.leave(lobbyId);
40+
}
3941

4042
return true;
4143
};

server/test/socket.utils.test.ts

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import { describe, it, expect, vi } from 'vitest';
2+
import { disconnectUserFromLobby } from '../src/utils/socketUtils.js';
3+
4+
describe('socketUtils - disconnectUserFromLobby', () => {
5+
it('should disconnect ALL sockets belonging to the same user in a lobby', async () => {
6+
const lobbyId = 'lobby-1';
7+
const userId = 'user-1';
8+
const reason = 'test-reason';
9+
10+
// Mock sockets
11+
const mockSocket1 = {
12+
id: 'socket-1',
13+
data: { user: { id: userId, username: 'user1' } },
14+
emit: vi.fn(),
15+
leave: vi.fn(),
16+
};
17+
const mockSocket2 = {
18+
id: 'socket-2',
19+
data: { user: { id: userId, username: 'user1' } },
20+
emit: vi.fn(),
21+
leave: vi.fn(),
22+
};
23+
const mockSocket3 = {
24+
id: 'socket-3',
25+
data: { user: { id: 'user-2', username: 'user2' } },
26+
emit: vi.fn(),
27+
leave: vi.fn(),
28+
};
29+
30+
// Mock IO
31+
const mockIo = {
32+
in: vi.fn().mockReturnThis(),
33+
fetchSockets: vi.fn().mockResolvedValue([mockSocket1, mockSocket2, mockSocket3]),
34+
to: vi.fn().mockReturnThis(),
35+
except: vi.fn().mockReturnThis(),
36+
emit: vi.fn(),
37+
};
38+
39+
const result = await disconnectUserFromLobby(mockIo as any, lobbyId, userId, reason);
40+
41+
expect(result).toBe(true);
42+
43+
// We want BOTH mockSocket1 and mockSocket2 to be disconnected
44+
expect(mockSocket1.emit).toHaveBeenCalledWith('FORCE_DISCONNECT', { lobbyId, reason });
45+
expect(mockSocket1.leave).toHaveBeenCalledWith(lobbyId);
46+
47+
expect(mockSocket2.emit).toHaveBeenCalledWith('FORCE_DISCONNECT', { lobbyId, reason });
48+
expect(mockSocket2.leave).toHaveBeenCalledWith(lobbyId);
49+
50+
// mockSocket3 should NOT be disconnected
51+
expect(mockSocket3.emit).not.toHaveBeenCalled();
52+
expect(mockSocket3.leave).not.toHaveBeenCalled();
53+
});
54+
});

0 commit comments

Comments
 (0)