@roomkit/gateway 1.1.2 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (28) hide show
  1. package/dist/src/ws/message-router.service.d.ts.map +1 -1
  2. package/dist/src/ws/message-router.service.js +6 -6
  3. package/dist/src/ws/message-router.service.js.map +1 -1
  4. package/package.json +1 -1
  5. package/dist/test/connection/connection.service.spec.d.ts +0 -2
  6. package/dist/test/connection/connection.service.spec.d.ts.map +0 -1
  7. package/dist/test/connection/connection.service.spec.js +0 -204
  8. package/dist/test/connection/connection.service.spec.js.map +0 -1
  9. package/dist/test/e2e/gateway-worker.e2e.spec.d.ts +0 -2
  10. package/dist/test/e2e/gateway-worker.e2e.spec.d.ts.map +0 -1
  11. package/dist/test/e2e/gateway-worker.e2e.spec.js +0 -412
  12. package/dist/test/e2e/gateway-worker.e2e.spec.js.map +0 -1
  13. package/dist/test/integration/admin-api.spec.d.ts +0 -2
  14. package/dist/test/integration/admin-api.spec.d.ts.map +0 -1
  15. package/dist/test/integration/admin-api.spec.js +0 -218
  16. package/dist/test/integration/admin-api.spec.js.map +0 -1
  17. package/dist/test/ratelimit/rate-limiter.service.spec.d.ts +0 -2
  18. package/dist/test/ratelimit/rate-limiter.service.spec.d.ts.map +0 -1
  19. package/dist/test/ratelimit/rate-limiter.service.spec.js +0 -139
  20. package/dist/test/ratelimit/rate-limiter.service.spec.js.map +0 -1
  21. package/dist/test/setup.d.ts +0 -2
  22. package/dist/test/setup.d.ts.map +0 -1
  23. package/dist/test/setup.js +0 -56
  24. package/dist/test/setup.js.map +0 -1
  25. package/dist/test/ws/message-router.service.spec.d.ts +0 -2
  26. package/dist/test/ws/message-router.service.spec.d.ts.map +0 -1
  27. package/dist/test/ws/message-router.service.spec.js +0 -403
  28. package/dist/test/ws/message-router.service.spec.js.map +0 -1
@@ -1 +0,0 @@
1
- {"version":3,"file":"admin-api.spec.js","sourceRoot":"","sources":["../../../test/integration/admin-api.spec.ts"],"names":[],"mappings":"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"}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=rate-limiter.service.spec.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"rate-limiter.service.spec.d.ts","sourceRoot":"","sources":["../../../test/ratelimit/rate-limiter.service.spec.ts"],"names":[],"mappings":""}
@@ -1,139 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- /// <reference types="jest" />
4
- const rate_limiter_service_1 = require("../../src/ratelimit/rate-limiter.service");
5
- describe('RateLimiterService', () => {
6
- let service;
7
- let configService;
8
- beforeEach(() => {
9
- configService = {
10
- connectionRateLimit: 10,
11
- messageRateLimitPerConnection: 5,
12
- messageBurstLimit: 10,
13
- globalMessageRateLimit: 100,
14
- };
15
- service = new rate_limiter_service_1.RateLimiterService(configService);
16
- });
17
- afterEach(() => {
18
- jest.clearAllMocks();
19
- });
20
- describe('checkConnectionRate', () => {
21
- it('should allow connections under the limit', () => {
22
- for (let i = 0; i < 5; i++) {
23
- expect(service.checkConnectionRate()).toBe(true);
24
- }
25
- });
26
- it('should reject connections over the limit', () => {
27
- // 使用所有配额
28
- for (let i = 0; i < 10; i++) {
29
- service.checkConnectionRate();
30
- }
31
- // 下一个连接应该被拒绝
32
- expect(service.checkConnectionRate()).toBe(false);
33
- });
34
- it('should reset after window expires', async () => {
35
- // 使用所有配额
36
- for (let i = 0; i < 10; i++) {
37
- service.checkConnectionRate();
38
- }
39
- expect(service.checkConnectionRate()).toBe(false);
40
- // 等待窗口过期(1秒+)
41
- await new Promise((resolve) => setTimeout(resolve, 1100));
42
- // 现在应该允许
43
- expect(service.checkConnectionRate()).toBe(true);
44
- });
45
- });
46
- describe('checkMessageRate', () => {
47
- const connectionId = 'conn-1';
48
- it('should allow messages under the limit', () => {
49
- for (let i = 0; i < 5; i++) {
50
- const result = service.checkMessageRate(connectionId);
51
- expect(result.allowed).toBe(true);
52
- }
53
- });
54
- it('should reject messages over per-connection limit', () => {
55
- // 使用所有配额
56
- for (let i = 0; i < 5; i++) {
57
- service.checkMessageRate(connectionId);
58
- }
59
- // 下一个消息应该被拒绝
60
- const result = service.checkMessageRate(connectionId);
61
- expect(result.allowed).toBe(false);
62
- expect(result.retryAfter).toBeDefined();
63
- });
64
- it('should track connections separately', () => {
65
- // conn-1 使用完配额
66
- for (let i = 0; i < 5; i++) {
67
- service.checkMessageRate('conn-1');
68
- }
69
- // conn-2 应该仍然可用
70
- const result = service.checkMessageRate('conn-2');
71
- expect(result.allowed).toBe(true);
72
- });
73
- it('should enforce burst limit', () => {
74
- // 创建一个较大突发限制的服务
75
- const burstConfig = {
76
- ...configService,
77
- messageRateLimitPerConnection: 100,
78
- messageBurstLimit: 5,
79
- };
80
- const burstService = new rate_limiter_service_1.RateLimiterService(burstConfig);
81
- // 快速发送消息直到突发限制
82
- for (let i = 0; i < 5; i++) {
83
- const result = burstService.checkMessageRate(connectionId);
84
- expect(result.allowed).toBe(true);
85
- }
86
- // 下一个消息应该因突发限制被拒绝
87
- const result = burstService.checkMessageRate(connectionId);
88
- expect(result.allowed).toBe(false);
89
- });
90
- });
91
- describe('removeConnection', () => {
92
- it('should clean up connection data', () => {
93
- const connectionId = 'conn-1';
94
- // 发送一些消息
95
- service.checkMessageRate(connectionId);
96
- // 移除连接
97
- service.removeConnection(connectionId);
98
- // 应该不会报错
99
- expect(() => service.removeConnection(connectionId)).not.toThrow();
100
- });
101
- });
102
- describe('getStats', () => {
103
- it('should return current statistics', () => {
104
- // 创建一些活动
105
- service.checkConnectionRate();
106
- service.checkConnectionRate();
107
- service.checkMessageRate('conn-1');
108
- service.checkMessageRate('conn-2');
109
- const stats = service.getStats();
110
- expect(stats.connectionRate).toBe(2);
111
- expect(stats.globalMessageRate).toBe(2);
112
- expect(stats.trackedConnections).toBe(2);
113
- });
114
- it('should return zero stats initially', () => {
115
- const stats = service.getStats();
116
- expect(stats.connectionRate).toBe(0);
117
- expect(stats.globalMessageRate).toBe(0);
118
- expect(stats.trackedConnections).toBe(0);
119
- });
120
- });
121
- describe('sliding window behavior', () => {
122
- it('should slide window and reset old counts', async () => {
123
- // 发送一些连接
124
- for (let i = 0; i < 5; i++) {
125
- service.checkConnectionRate();
126
- }
127
- const statsBefore = service.getStats();
128
- expect(statsBefore.connectionRate).toBe(5);
129
- // 等待窗口滑动
130
- await new Promise((resolve) => setTimeout(resolve, 1100));
131
- // 再检查一次以触发滑动
132
- service.checkConnectionRate();
133
- const statsAfter = service.getStats();
134
- // 旧的计数应该被清除,只剩下最新的1个
135
- expect(statsAfter.connectionRate).toBeLessThanOrEqual(2);
136
- });
137
- });
138
- });
139
- //# sourceMappingURL=rate-limiter.service.spec.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"rate-limiter.service.spec.js","sourceRoot":"","sources":["../../../test/ratelimit/rate-limiter.service.spec.ts"],"names":[],"mappings":"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"}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=setup.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../../test/setup.ts"],"names":[],"mappings":""}
@@ -1,56 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
- Object.defineProperty(exports, "__esModule", { value: true });
36
- /// <reference types="jest" />
37
- const dotenv = __importStar(require("dotenv"));
38
- const path = __importStar(require("path"));
39
- /**
40
- * Jest 测试环境设置
41
- */
42
- // 加载根目录的 .env 文件
43
- dotenv.config({ path: path.resolve(__dirname, '../../../.env') });
44
- // 设置测试超时时间
45
- jest.setTimeout(10000);
46
- // 模拟环境变量(优先使用已有的环境变量)
47
- process.env.NODE_ENV = 'test';
48
- process.env.GATEWAY_ID = process.env.GATEWAY_ID || 'gateway-test';
49
- process.env.REDIS_HOST = process.env.REDIS_HOST || 'localhost';
50
- process.env.REDIS_PORT = process.env.REDIS_PORT || '6379';
51
- // 全局清理
52
- afterAll(async () => {
53
- // 确保所有异步操作完成
54
- await new Promise((resolve) => setTimeout(resolve, 100));
55
- });
56
- //# sourceMappingURL=setup.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"setup.js","sourceRoot":"","sources":["../../test/setup.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,8BAA8B;AAC9B,+CAAiC;AACjC,2CAA6B;AAE7B;;GAEG;AAEH,iBAAiB;AACjB,MAAM,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,eAAe,CAAC,EAAE,CAAC,CAAC;AAElE,WAAW;AACX,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;AAEvB,sBAAsB;AACtB,OAAO,CAAC,GAAG,CAAC,QAAQ,GAAG,MAAM,CAAC;AAC9B,OAAO,CAAC,GAAG,CAAC,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,cAAc,CAAC;AAClE,OAAO,CAAC,GAAG,CAAC,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,WAAW,CAAC;AAC/D,OAAO,CAAC,GAAG,CAAC,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,MAAM,CAAC;AAE1D,OAAO;AACP,QAAQ,CAAC,KAAK,IAAI,EAAE;IAClB,aAAa;IACb,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,CAAC;AAC3D,CAAC,CAAC,CAAC"}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=message-router.service.spec.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"message-router.service.spec.d.ts","sourceRoot":"","sources":["../../../test/ws/message-router.service.spec.ts"],"names":[],"mappings":""}
@@ -1,403 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- /// <reference types="jest" />
4
- const message_router_service_1 = require("../../src/ws/message-router.service");
5
- const core_1 = require("@roomkit/core");
6
- describe('MessageRouterService', () => {
7
- let service;
8
- let configService;
9
- let redisService;
10
- let connectionService;
11
- const mockGatewayId = 'gateway-test-001';
12
- const mockWorkerId = 'worker-test-001';
13
- beforeEach(() => {
14
- // 创建模拟的依赖服务
15
- configService = {
16
- gatewayId: mockGatewayId,
17
- };
18
- redisService = {
19
- client: {
20
- get: jest.fn(),
21
- setnx: jest.fn(),
22
- smembers: jest.fn(),
23
- srandmember: jest.fn(),
24
- hgetall: jest.fn(),
25
- set: jest.fn(),
26
- del: jest.fn(),
27
- },
28
- publish: jest.fn().mockResolvedValue(undefined),
29
- evalScript: jest.fn().mockResolvedValue(null),
30
- };
31
- connectionService = {
32
- touch: jest.fn(),
33
- send: jest.fn().mockReturnValue(true),
34
- sendByType: jest.fn().mockReturnValue(true),
35
- updateStatus: jest.fn(),
36
- setRoom: jest.fn(),
37
- getConnection: jest.fn(),
38
- };
39
- const sessionService = {
40
- createOrUpdateSession: jest.fn(),
41
- getSessionByUserId: jest.fn(),
42
- getSessionByToken: jest.fn(),
43
- updateConnectionInfo: jest.fn(),
44
- addRoomToSession: jest.fn(),
45
- removeRoomFromSession: jest.fn(),
46
- };
47
- service = new message_router_service_1.MessageRouterService(configService, redisService, connectionService, sessionService);
48
- });
49
- afterEach(() => {
50
- jest.clearAllMocks();
51
- });
52
- describe('handleMessage - Heartbeat', () => {
53
- it('should respond to heartbeat directly without forwarding to worker', async () => {
54
- const connection = {
55
- id: 'conn-1',
56
- status: 'identified',
57
- lastActivity: Date.now(),
58
- createdAt: Date.now(),
59
- rooms: new Set(),
60
- };
61
- const message = {
62
- msgType: core_1.MessageType.MSG_HEARTBEAT,
63
- payload: { timestamp: 1234567890 },
64
- };
65
- await service.handleMessage(connection, message);
66
- // 应该更新活动时间
67
- expect(connectionService.touch).toHaveBeenCalledWith(connection.id);
68
- // 应该直接响应心跳
69
- expect(connectionService.sendByType).toHaveBeenCalledWith(connection.id, core_1.MessageType.MSG_HEARTBEAT_ACK, expect.objectContaining({
70
- timestamp: 1234567890,
71
- serverTime: expect.any(Number),
72
- }));
73
- // 不应发布到Redis
74
- expect(redisService.publish).not.toHaveBeenCalled();
75
- });
76
- });
77
- describe('handleMessage - Auth', () => {
78
- it('should reject auth request if connection is not in connected state', async () => {
79
- const connection = {
80
- id: 'conn-1',
81
- status: 'identified', // 已认证状态
82
- lastActivity: Date.now(),
83
- createdAt: Date.now(),
84
- rooms: new Set(),
85
- };
86
- const message = {
87
- msgType: core_1.MessageType.MSG_AUTH,
88
- payload: { token: 'test-token' },
89
- };
90
- await service.handleMessage(connection, message);
91
- expect(connectionService.sendByType).toHaveBeenCalledWith(connection.id, core_1.MessageType.MSG_AUTH_FAIL, expect.objectContaining({
92
- success: false,
93
- error: 'INVALID_STATE',
94
- }));
95
- });
96
- it('should reject auth request with invalid payload', async () => {
97
- const connection = {
98
- id: 'conn-1',
99
- status: 'connected',
100
- lastActivity: Date.now(),
101
- createdAt: Date.now(),
102
- rooms: new Set(),
103
- };
104
- const message = {
105
- msgType: core_1.MessageType.MSG_AUTH,
106
- payload: {}, // 缺少必需的 token 字段
107
- };
108
- await service.handleMessage(connection, message);
109
- expect(connectionService.sendByType).toHaveBeenCalledWith(connection.id, core_1.MessageType.MSG_AUTH_FAIL, expect.objectContaining({
110
- success: false,
111
- error: 'INVALID_PAYLOAD',
112
- }));
113
- });
114
- it('should forward valid auth request to worker', async () => {
115
- const connection = {
116
- id: 'conn-1',
117
- status: 'connected',
118
- lastActivity: Date.now(),
119
- createdAt: Date.now(),
120
- rooms: new Set(),
121
- };
122
- // 模拟有可用的 Worker
123
- redisService.client.smembers.mockResolvedValue([mockWorkerId]);
124
- redisService.client.srandmember.mockResolvedValue(mockWorkerId);
125
- const message = {
126
- msgType: core_1.MessageType.MSG_AUTH,
127
- payload: { token: 'valid-token' },
128
- };
129
- await service.handleMessage(connection, message);
130
- // 应该更新状态为 authenticating
131
- expect(connectionService.updateStatus).toHaveBeenCalledWith(connection.id, 'authenticating');
132
- // 应该发布消息到 Worker
133
- expect(redisService.publish).toHaveBeenCalledWith(expect.stringContaining('worker'), expect.objectContaining({
134
- type: 'auth',
135
- gatewayId: mockGatewayId,
136
- connectionId: connection.id,
137
- msgType: core_1.MessageType.MSG_AUTH,
138
- }));
139
- });
140
- it('should reject auth when no worker available', async () => {
141
- const connection = {
142
- id: 'conn-1',
143
- status: 'connected',
144
- lastActivity: Date.now(),
145
- createdAt: Date.now(),
146
- rooms: new Set(),
147
- };
148
- // 模拟没有可用的 Worker
149
- redisService.client.smembers.mockResolvedValue([]);
150
- const message = {
151
- msgType: core_1.MessageType.MSG_AUTH,
152
- payload: { token: 'valid-token' },
153
- };
154
- await service.handleMessage(connection, message);
155
- expect(connectionService.sendByType).toHaveBeenCalledWith(connection.id, core_1.MessageType.MSG_AUTH_FAIL, expect.objectContaining({
156
- success: false,
157
- error: 'NO_WORKER_AVAILABLE',
158
- }));
159
- });
160
- });
161
- describe('handleMessage - Unauthenticated access', () => {
162
- it('should reject non-auth messages from unauthenticated connection', async () => {
163
- const connection = {
164
- id: 'conn-1',
165
- status: 'connected', // 未认证
166
- lastActivity: Date.now(),
167
- createdAt: Date.now(),
168
- rooms: new Set(),
169
- };
170
- const message = {
171
- msgType: core_1.MessageType.MSG_ROOM_CREATE,
172
- payload: { gameType: 'echo' },
173
- };
174
- await service.handleMessage(connection, message);
175
- expect(connectionService.sendByType).toHaveBeenCalledWith(connection.id, core_1.MessageType.MSG_ERROR, expect.objectContaining({
176
- error: 'NOT_AUTHENTICATED',
177
- }));
178
- });
179
- });
180
- describe('handleMessage - Create Room', () => {
181
- it('should reject create room with invalid payload', async () => {
182
- const connection = {
183
- id: 'conn-1',
184
- userId: 'user-1',
185
- status: 'identified',
186
- lastActivity: Date.now(),
187
- createdAt: Date.now(),
188
- rooms: new Set(),
189
- };
190
- const message = {
191
- msgType: core_1.MessageType.MSG_ROOM_CREATE,
192
- payload: {}, // 缺少 gameType
193
- };
194
- await service.handleMessage(connection, message);
195
- expect(connectionService.sendByType).toHaveBeenCalledWith(connection.id, core_1.MessageType.MSG_ROOM_CREATE_FAIL, expect.objectContaining({
196
- success: false,
197
- error: 'INVALID_PAYLOAD',
198
- }));
199
- });
200
- it('should reject create room when no worker available', async () => {
201
- const connection = {
202
- id: 'conn-1',
203
- userId: 'user-1',
204
- status: 'identified',
205
- lastActivity: Date.now(),
206
- createdAt: Date.now(),
207
- rooms: new Set(),
208
- };
209
- // 模拟没有可用的 Worker
210
- redisService.client.smembers.mockResolvedValue([]);
211
- const message = {
212
- msgType: core_1.MessageType.MSG_ROOM_CREATE,
213
- payload: { gameType: 'echo', maxPlayers: 10 },
214
- };
215
- await service.handleMessage(connection, message);
216
- expect(connectionService.sendByType).toHaveBeenCalledWith(connection.id, core_1.MessageType.MSG_ROOM_CREATE_FAIL, expect.objectContaining({
217
- success: false,
218
- error: 'NO_WORKER_AVAILABLE',
219
- }));
220
- });
221
- it('should reject duplicate room creation', async () => {
222
- const connection = {
223
- id: 'conn-1',
224
- userId: 'user-1',
225
- status: 'identified',
226
- lastActivity: Date.now(),
227
- createdAt: Date.now(),
228
- rooms: new Set(),
229
- };
230
- redisService.client.smembers.mockResolvedValue([mockWorkerId]);
231
- redisService.client.srandmember.mockResolvedValue(mockWorkerId);
232
- // 模拟房间已存在(setnx 返回 0)
233
- redisService.client.setnx.mockResolvedValue(0);
234
- const message = {
235
- msgType: core_1.MessageType.MSG_ROOM_CREATE,
236
- payload: { roomId: 'existing-room', gameType: 'echo', maxPlayers: 10 },
237
- };
238
- await service.handleMessage(connection, message);
239
- expect(connectionService.sendByType).toHaveBeenCalledWith(connection.id, core_1.MessageType.MSG_ROOM_CREATE_FAIL, expect.objectContaining({
240
- success: false,
241
- error: 'ROOM_ALREADY_EXISTS',
242
- }));
243
- });
244
- it('should forward valid create room request to worker', async () => {
245
- const connection = {
246
- id: 'conn-1',
247
- userId: 'user-1',
248
- status: 'identified',
249
- lastActivity: Date.now(),
250
- createdAt: Date.now(),
251
- rooms: new Set(),
252
- };
253
- redisService.client.smembers.mockResolvedValue([mockWorkerId]);
254
- redisService.client.srandmember.mockResolvedValue(mockWorkerId);
255
- // 模拟房间不存在(setnx 返回 1)
256
- redisService.client.setnx.mockResolvedValue(1);
257
- const message = {
258
- msgType: core_1.MessageType.MSG_ROOM_CREATE,
259
- payload: { gameType: 'echo', maxPlayers: 10, config: {} },
260
- };
261
- await service.handleMessage(connection, message);
262
- expect(redisService.publish).toHaveBeenCalledWith(expect.stringContaining('worker'), expect.objectContaining({
263
- type: 'client-message',
264
- msgType: core_1.MessageType.MSG_ROOM_CREATE,
265
- userId: 'user-1',
266
- }));
267
- });
268
- });
269
- describe('handleMessage - Join Room', () => {
270
- it('should reject join room when room not found', async () => {
271
- const connection = {
272
- id: 'conn-1',
273
- userId: 'user-1',
274
- status: 'identified',
275
- lastActivity: Date.now(),
276
- createdAt: Date.now(),
277
- rooms: new Set(),
278
- };
279
- // 模拟房间不存在
280
- redisService.client.get.mockResolvedValue(null);
281
- const message = {
282
- msgType: core_1.MessageType.MSG_ROOM_JOIN,
283
- payload: { roomId: 'non-existent-room' },
284
- };
285
- await service.handleMessage(connection, message);
286
- expect(connectionService.sendByType).toHaveBeenCalledWith(connection.id, core_1.MessageType.MSG_ROOM_JOIN_FAIL, expect.objectContaining({
287
- success: false,
288
- error: 'ROOM_NOT_FOUND',
289
- }));
290
- });
291
- it('should forward valid join room request to worker', async () => {
292
- const connection = {
293
- id: 'conn-1',
294
- userId: 'user-1',
295
- status: 'identified',
296
- lastActivity: Date.now(),
297
- createdAt: Date.now(),
298
- rooms: new Set(),
299
- };
300
- // 模拟房间存在
301
- redisService.client.get.mockResolvedValue(mockWorkerId);
302
- const message = {
303
- msgType: core_1.MessageType.MSG_ROOM_JOIN,
304
- payload: { roomId: 'room-123' },
305
- };
306
- await service.handleMessage(connection, message);
307
- expect(redisService.publish).toHaveBeenCalledWith(expect.stringContaining('worker'), expect.objectContaining({
308
- type: 'client-message',
309
- msgType: core_1.MessageType.MSG_ROOM_JOIN,
310
- roomId: 'room-123',
311
- }));
312
- });
313
- });
314
- describe('handleMessage - Leave Room', () => {
315
- it('should reject leave room when not in room', async () => {
316
- const connection = {
317
- id: 'conn-1',
318
- userId: 'user-1',
319
- status: 'identified',
320
- currentRoom: undefined, // 不在房间中
321
- lastActivity: Date.now(),
322
- createdAt: Date.now(),
323
- rooms: new Set(),
324
- };
325
- const message = {
326
- msgType: core_1.MessageType.MSG_ROOM_LEAVE,
327
- payload: {},
328
- };
329
- await service.handleMessage(connection, message);
330
- expect(connectionService.sendByType).toHaveBeenCalledWith(connection.id, core_1.MessageType.MSG_ERROR, expect.objectContaining({
331
- success: false,
332
- error: 'NOT_IN_ROOM',
333
- }));
334
- });
335
- it('should forward leave room request to worker', async () => {
336
- const connection = {
337
- id: 'conn-1',
338
- userId: 'user-1',
339
- status: 'in-room',
340
- currentRoom: 'room-123',
341
- lastActivity: Date.now(),
342
- createdAt: Date.now(),
343
- rooms: new Set(),
344
- };
345
- redisService.client.get.mockResolvedValue(mockWorkerId);
346
- const message = {
347
- msgType: core_1.MessageType.MSG_ROOM_LEAVE,
348
- payload: {},
349
- };
350
- await service.handleMessage(connection, message);
351
- expect(redisService.publish).toHaveBeenCalledWith(expect.stringContaining('worker'), expect.objectContaining({
352
- type: 'client-message',
353
- msgType: core_1.MessageType.MSG_ROOM_LEAVE,
354
- roomId: 'room-123',
355
- }));
356
- });
357
- });
358
- describe('handleMessage - Game Logic', () => {
359
- it('should reject game message when not in room', async () => {
360
- const connection = {
361
- id: 'conn-1',
362
- userId: 'user-1',
363
- status: 'identified',
364
- currentRoom: undefined,
365
- lastActivity: Date.now(),
366
- createdAt: Date.now(),
367
- rooms: new Set(),
368
- };
369
- const message = {
370
- msgType: 'chat',
371
- payload: { message: 'Hello!' },
372
- };
373
- await service.handleMessage(connection, message);
374
- expect(connectionService.sendByType).toHaveBeenCalledWith(connection.id, core_1.MessageType.MSG_ERROR, expect.objectContaining({
375
- error: 'NOT_IN_ROOM',
376
- }));
377
- });
378
- it('should forward game message to worker when in room', async () => {
379
- const connection = {
380
- id: 'conn-1',
381
- userId: 'user-1',
382
- status: 'in-room',
383
- currentRoom: 'room-123',
384
- lastActivity: Date.now(),
385
- createdAt: Date.now(),
386
- rooms: new Set(),
387
- };
388
- redisService.client.get.mockResolvedValue(mockWorkerId);
389
- const message = {
390
- msgType: 'chat',
391
- payload: { message: 'Hello!' },
392
- };
393
- await service.handleMessage(connection, message);
394
- expect(redisService.publish).toHaveBeenCalledWith(expect.stringContaining('worker'), expect.objectContaining({
395
- type: 'client-message',
396
- msgType: 'chat',
397
- userId: 'user-1',
398
- roomId: 'room-123',
399
- }));
400
- });
401
- });
402
- });
403
- //# sourceMappingURL=message-router.service.spec.js.map