@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.
- package/dist/src/ws/message-router.service.d.ts.map +1 -1
- package/dist/src/ws/message-router.service.js +6 -6
- package/dist/src/ws/message-router.service.js.map +1 -1
- package/package.json +1 -1
- package/dist/test/connection/connection.service.spec.d.ts +0 -2
- package/dist/test/connection/connection.service.spec.d.ts.map +0 -1
- package/dist/test/connection/connection.service.spec.js +0 -204
- package/dist/test/connection/connection.service.spec.js.map +0 -1
- package/dist/test/e2e/gateway-worker.e2e.spec.d.ts +0 -2
- package/dist/test/e2e/gateway-worker.e2e.spec.d.ts.map +0 -1
- package/dist/test/e2e/gateway-worker.e2e.spec.js +0 -412
- package/dist/test/e2e/gateway-worker.e2e.spec.js.map +0 -1
- package/dist/test/integration/admin-api.spec.d.ts +0 -2
- package/dist/test/integration/admin-api.spec.d.ts.map +0 -1
- package/dist/test/integration/admin-api.spec.js +0 -218
- package/dist/test/integration/admin-api.spec.js.map +0 -1
- package/dist/test/ratelimit/rate-limiter.service.spec.d.ts +0 -2
- package/dist/test/ratelimit/rate-limiter.service.spec.d.ts.map +0 -1
- package/dist/test/ratelimit/rate-limiter.service.spec.js +0 -139
- package/dist/test/ratelimit/rate-limiter.service.spec.js.map +0 -1
- package/dist/test/setup.d.ts +0 -2
- package/dist/test/setup.d.ts.map +0 -1
- package/dist/test/setup.js +0 -56
- package/dist/test/setup.js.map +0 -1
- package/dist/test/ws/message-router.service.spec.d.ts +0 -2
- package/dist/test/ws/message-router.service.spec.d.ts.map +0 -1
- package/dist/test/ws/message-router.service.spec.js +0 -403
- package/dist/test/ws/message-router.service.spec.js.map +0 -1
|
@@ -1,412 +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
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
-
};
|
|
38
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
-
/// <reference types="jest" />
|
|
40
|
-
/**
|
|
41
|
-
* Gateway-Worker 端到端集成测试
|
|
42
|
-
* 测试完整的消息流:Client -> Gateway -> Redis -> Worker -> Redis -> Gateway -> Client
|
|
43
|
-
*/
|
|
44
|
-
const dotenv = __importStar(require("dotenv"));
|
|
45
|
-
const path = __importStar(require("path"));
|
|
46
|
-
// 加载根目录的 .env 文件
|
|
47
|
-
dotenv.config({ path: path.resolve(__dirname, '../../../../.env') });
|
|
48
|
-
const testing_1 = require("@nestjs/testing");
|
|
49
|
-
const ws_1 = __importDefault(require("ws"));
|
|
50
|
-
const ioredis_1 = __importDefault(require("ioredis"));
|
|
51
|
-
const gateway_module_1 = require("../../src/gateway.module");
|
|
52
|
-
const core_1 = require("@roomkit/core");
|
|
53
|
-
// 模拟Worker服务
|
|
54
|
-
class MockWorkerService {
|
|
55
|
-
redisClient;
|
|
56
|
-
redisSub;
|
|
57
|
-
workerId;
|
|
58
|
-
constructor(workerId) {
|
|
59
|
-
this.workerId = workerId;
|
|
60
|
-
this.redisClient = new ioredis_1.default({
|
|
61
|
-
host: process.env.REDIS_HOST || 'localhost',
|
|
62
|
-
port: parseInt(process.env.REDIS_PORT || '16379'),
|
|
63
|
-
password: process.env.REDIS_PASSWORD,
|
|
64
|
-
});
|
|
65
|
-
this.redisSub = this.redisClient.duplicate();
|
|
66
|
-
}
|
|
67
|
-
async start() {
|
|
68
|
-
// 注册Worker
|
|
69
|
-
await this.redisClient.hset(`gf:worker:${this.workerId}`, 'status', 'active', 'connectedAt', Date.now().toString());
|
|
70
|
-
// 订阅Worker的RPC通道
|
|
71
|
-
await this.redisSub.subscribe(`gf:rpc:worker:${this.workerId}`);
|
|
72
|
-
this.redisSub.on('message', this.handleMessage.bind(this));
|
|
73
|
-
}
|
|
74
|
-
async handleMessage(channel, message) {
|
|
75
|
-
try {
|
|
76
|
-
const msg = JSON.parse(message);
|
|
77
|
-
switch (msg.type) {
|
|
78
|
-
case 'auth':
|
|
79
|
-
await this.handleAuth(msg);
|
|
80
|
-
break;
|
|
81
|
-
case 'create-room':
|
|
82
|
-
await this.handleCreateRoom(msg);
|
|
83
|
-
break;
|
|
84
|
-
case 'join-room':
|
|
85
|
-
await this.handleJoinRoom(msg);
|
|
86
|
-
break;
|
|
87
|
-
case 'client-message':
|
|
88
|
-
await this.handleClientMessage(msg);
|
|
89
|
-
break;
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
catch (error) {
|
|
93
|
-
console.error('MockWorker error:', error);
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
async handleAuth(msg) {
|
|
97
|
-
const { gatewayId, connectionId, payload } = msg;
|
|
98
|
-
// 模拟token验证
|
|
99
|
-
const isValid = payload.token && payload.token.startsWith('token_');
|
|
100
|
-
const userId = isValid ? payload.token.replace('token_', 'user_') : null;
|
|
101
|
-
const response = {
|
|
102
|
-
type: 'auth-response',
|
|
103
|
-
workerId: this.workerId,
|
|
104
|
-
requestId: msg.requestId,
|
|
105
|
-
connectionId,
|
|
106
|
-
success: isValid,
|
|
107
|
-
userId,
|
|
108
|
-
error: isValid ? undefined : 'INVALID_TOKEN',
|
|
109
|
-
timestamp: Date.now(),
|
|
110
|
-
};
|
|
111
|
-
await this.redisClient.publish(`gf:rpc:gateway:${gatewayId}`, JSON.stringify(response));
|
|
112
|
-
}
|
|
113
|
-
async handleCreateRoom(msg) {
|
|
114
|
-
const { gatewayId, connectionId, payload } = msg;
|
|
115
|
-
const roomId = payload.roomId || `room_${Date.now()}`;
|
|
116
|
-
// 注册房间到Worker的映射
|
|
117
|
-
await this.redisClient.set(`gf:room:${roomId}:worker`, this.workerId);
|
|
118
|
-
const response = {
|
|
119
|
-
type: 'client-response',
|
|
120
|
-
workerId: this.workerId,
|
|
121
|
-
connectionId,
|
|
122
|
-
msgId: core_1.MessageId.CREATE_ROOM_RES,
|
|
123
|
-
payload: {
|
|
124
|
-
success: true,
|
|
125
|
-
roomId,
|
|
126
|
-
workerId: this.workerId,
|
|
127
|
-
},
|
|
128
|
-
timestamp: Date.now(),
|
|
129
|
-
};
|
|
130
|
-
await this.redisClient.publish(`gf:rpc:gateway:${gatewayId}`, JSON.stringify(response));
|
|
131
|
-
}
|
|
132
|
-
async handleJoinRoom(msg) {
|
|
133
|
-
const { gatewayId, connectionId, userId, payload } = msg;
|
|
134
|
-
const { roomId } = payload;
|
|
135
|
-
// 验证房间是否存在
|
|
136
|
-
const workerId = await this.redisClient.get(`gf:room:${roomId}:worker`);
|
|
137
|
-
if (!workerId) {
|
|
138
|
-
const errorResponse = {
|
|
139
|
-
type: 'client-response',
|
|
140
|
-
workerId: this.workerId,
|
|
141
|
-
connectionId,
|
|
142
|
-
msgId: core_1.MessageId.ERROR,
|
|
143
|
-
payload: {
|
|
144
|
-
error: 'ROOM_NOT_FOUND',
|
|
145
|
-
message: 'Room does not exist',
|
|
146
|
-
},
|
|
147
|
-
timestamp: Date.now(),
|
|
148
|
-
};
|
|
149
|
-
await this.redisClient.publish(`gf:rpc:gateway:${gatewayId}`, JSON.stringify(errorResponse));
|
|
150
|
-
return;
|
|
151
|
-
}
|
|
152
|
-
// 添加成员到房间
|
|
153
|
-
await this.redisClient.hset(`gf:room:${roomId}:members`, userId, JSON.stringify({ gatewayId, connectionId, joinedAt: Date.now() }));
|
|
154
|
-
// 发送加入成功响应
|
|
155
|
-
const response = {
|
|
156
|
-
type: 'client-response',
|
|
157
|
-
workerId: this.workerId,
|
|
158
|
-
connectionId,
|
|
159
|
-
msgId: core_1.MessageId.JOIN_ROOM_RES,
|
|
160
|
-
payload: {
|
|
161
|
-
success: true,
|
|
162
|
-
roomId,
|
|
163
|
-
players: await this.getRoomPlayers(roomId),
|
|
164
|
-
},
|
|
165
|
-
timestamp: Date.now(),
|
|
166
|
-
};
|
|
167
|
-
await this.redisClient.publish(`gf:rpc:gateway:${gatewayId}`, JSON.stringify(response));
|
|
168
|
-
// 广播玩家加入消息
|
|
169
|
-
const broadcastMsg = {
|
|
170
|
-
type: 'room-broadcast',
|
|
171
|
-
workerId: this.workerId,
|
|
172
|
-
roomId,
|
|
173
|
-
msgId: core_1.MessageId.PLAYER_JOINED,
|
|
174
|
-
payload: {
|
|
175
|
-
userId,
|
|
176
|
-
timestamp: Date.now(),
|
|
177
|
-
},
|
|
178
|
-
excludeUserId: userId,
|
|
179
|
-
timestamp: Date.now(),
|
|
180
|
-
};
|
|
181
|
-
await this.redisClient.publish('gf:broadcast', JSON.stringify(broadcastMsg));
|
|
182
|
-
}
|
|
183
|
-
async handleClientMessage(msg) {
|
|
184
|
-
const { gatewayId, connectionId, msgId, payload } = msg;
|
|
185
|
-
// 简单回显消息
|
|
186
|
-
const response = {
|
|
187
|
-
type: 'client-response',
|
|
188
|
-
workerId: this.workerId,
|
|
189
|
-
connectionId,
|
|
190
|
-
msgId: msgId + 1, // 响应ID = 请求ID + 1
|
|
191
|
-
payload: {
|
|
192
|
-
echo: payload,
|
|
193
|
-
serverTime: Date.now(),
|
|
194
|
-
},
|
|
195
|
-
timestamp: Date.now(),
|
|
196
|
-
};
|
|
197
|
-
await this.redisClient.publish(`gf:rpc:gateway:${gatewayId}`, JSON.stringify(response));
|
|
198
|
-
}
|
|
199
|
-
async getRoomPlayers(roomId) {
|
|
200
|
-
const members = await this.redisClient.hgetall(`gf:room:${roomId}:members`);
|
|
201
|
-
return Object.entries(members).map(([userId, data]) => ({
|
|
202
|
-
userId,
|
|
203
|
-
...JSON.parse(data),
|
|
204
|
-
}));
|
|
205
|
-
}
|
|
206
|
-
async stop() {
|
|
207
|
-
await this.redisSub.unsubscribe();
|
|
208
|
-
await this.redisSub.quit();
|
|
209
|
-
await this.redisClient.del(`gf:worker:${this.workerId}`);
|
|
210
|
-
await this.redisClient.quit();
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
// 测试客户端
|
|
214
|
-
class TestClient {
|
|
215
|
-
ws = null;
|
|
216
|
-
messages = [];
|
|
217
|
-
messageResolvers = new Map();
|
|
218
|
-
async connect(url) {
|
|
219
|
-
return new Promise((resolve, reject) => {
|
|
220
|
-
this.ws = new ws_1.default(url);
|
|
221
|
-
this.ws.on('open', () => resolve());
|
|
222
|
-
this.ws.on('error', reject);
|
|
223
|
-
this.ws.on('message', (data) => {
|
|
224
|
-
const msg = JSON.parse(data.toString());
|
|
225
|
-
this.messages.push(msg);
|
|
226
|
-
const resolver = this.messageResolvers.get(msg.msgId);
|
|
227
|
-
if (resolver) {
|
|
228
|
-
resolver(msg);
|
|
229
|
-
this.messageResolvers.delete(msg.msgId);
|
|
230
|
-
}
|
|
231
|
-
});
|
|
232
|
-
});
|
|
233
|
-
}
|
|
234
|
-
send(msgId, payload) {
|
|
235
|
-
if (!this.ws || this.ws.readyState !== ws_1.default.OPEN) {
|
|
236
|
-
throw new Error('WebSocket not connected');
|
|
237
|
-
}
|
|
238
|
-
this.ws.send(JSON.stringify({ msgId, payload }));
|
|
239
|
-
}
|
|
240
|
-
async sendAndWait(msgId, payload, expectedResMsgId, timeout = 5000) {
|
|
241
|
-
return new Promise((resolve, reject) => {
|
|
242
|
-
const timer = setTimeout(() => {
|
|
243
|
-
this.messageResolvers.delete(expectedResMsgId);
|
|
244
|
-
reject(new Error(`Timeout waiting for message ${expectedResMsgId}`));
|
|
245
|
-
}, timeout);
|
|
246
|
-
this.messageResolvers.set(expectedResMsgId, (msg) => {
|
|
247
|
-
clearTimeout(timer);
|
|
248
|
-
resolve(msg);
|
|
249
|
-
});
|
|
250
|
-
this.send(msgId, payload);
|
|
251
|
-
});
|
|
252
|
-
}
|
|
253
|
-
getMessages() {
|
|
254
|
-
return this.messages;
|
|
255
|
-
}
|
|
256
|
-
clearMessages() {
|
|
257
|
-
this.messages = [];
|
|
258
|
-
}
|
|
259
|
-
disconnect() {
|
|
260
|
-
if (this.ws) {
|
|
261
|
-
this.ws.close();
|
|
262
|
-
this.ws = null;
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
describe('Gateway-Worker E2E Tests', () => {
|
|
267
|
-
let app;
|
|
268
|
-
let mockWorker;
|
|
269
|
-
let redis;
|
|
270
|
-
const workerId = 'worker-e2e-test';
|
|
271
|
-
const gatewayUrl = 'ws://localhost:3001/ws';
|
|
272
|
-
beforeAll(async () => {
|
|
273
|
-
// 创建Gateway应用
|
|
274
|
-
const moduleFixture = await testing_1.Test.createTestingModule({
|
|
275
|
-
imports: [gateway_module_1.GatewayModule],
|
|
276
|
-
}).compile();
|
|
277
|
-
app = moduleFixture.createNestApplication();
|
|
278
|
-
await app.listen(3001);
|
|
279
|
-
// 创建Redis客户端
|
|
280
|
-
redis = new ioredis_1.default({
|
|
281
|
-
host: process.env.REDIS_HOST || 'localhost',
|
|
282
|
-
port: parseInt(process.env.REDIS_PORT || '6379'),
|
|
283
|
-
});
|
|
284
|
-
// 启动模拟Worker
|
|
285
|
-
mockWorker = new MockWorkerService(workerId);
|
|
286
|
-
await mockWorker.start();
|
|
287
|
-
// 等待服务初始化
|
|
288
|
-
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
289
|
-
});
|
|
290
|
-
afterAll(async () => {
|
|
291
|
-
await mockWorker.stop();
|
|
292
|
-
await redis.quit();
|
|
293
|
-
await app.close();
|
|
294
|
-
});
|
|
295
|
-
beforeEach(async () => {
|
|
296
|
-
// 清理测试数据
|
|
297
|
-
const keys = await redis.keys('gf:*');
|
|
298
|
-
if (keys.length > 0) {
|
|
299
|
-
await redis.del(...keys);
|
|
300
|
-
}
|
|
301
|
-
// 重新注册Worker
|
|
302
|
-
await redis.hset(`gf:worker:${workerId}`, 'status', 'active', 'connectedAt', Date.now().toString());
|
|
303
|
-
});
|
|
304
|
-
describe('鉴权流程', () => {
|
|
305
|
-
it('应该成功完成鉴权', async () => {
|
|
306
|
-
const client = new TestClient();
|
|
307
|
-
await client.connect(gatewayUrl);
|
|
308
|
-
const authRes = await client.sendAndWait(core_1.MessageId.AUTH_REQ, { token: 'token_test123' }, core_1.MessageId.AUTH_RES);
|
|
309
|
-
expect(authRes.payload.success).toBe(true);
|
|
310
|
-
expect(authRes.payload.userId).toBe('user_test123');
|
|
311
|
-
client.disconnect();
|
|
312
|
-
});
|
|
313
|
-
it('应该拒绝无效token', async () => {
|
|
314
|
-
const client = new TestClient();
|
|
315
|
-
await client.connect(gatewayUrl);
|
|
316
|
-
const authRes = await client.sendAndWait(core_1.MessageId.AUTH_REQ, { token: 'invalid' }, core_1.MessageId.AUTH_RES);
|
|
317
|
-
expect(authRes.payload.success).toBe(false);
|
|
318
|
-
expect(authRes.payload.error).toBe('INVALID_TOKEN');
|
|
319
|
-
client.disconnect();
|
|
320
|
-
});
|
|
321
|
-
});
|
|
322
|
-
describe('房间管理', () => {
|
|
323
|
-
it('应该成功创建房间', async () => {
|
|
324
|
-
const client = new TestClient();
|
|
325
|
-
await client.connect(gatewayUrl);
|
|
326
|
-
// 先鉴权
|
|
327
|
-
await client.sendAndWait(core_1.MessageId.AUTH_REQ, { token: 'token_test123' }, core_1.MessageId.AUTH_RES);
|
|
328
|
-
// 创建房间
|
|
329
|
-
const createRes = await client.sendAndWait(core_1.MessageId.CREATE_ROOM_REQ, { gameType: 'test', maxPlayers: 4 }, core_1.MessageId.CREATE_ROOM_RES);
|
|
330
|
-
expect(createRes.payload.success).toBe(true);
|
|
331
|
-
expect(createRes.payload.roomId).toBeDefined();
|
|
332
|
-
expect(createRes.payload.workerId).toBe(workerId);
|
|
333
|
-
client.disconnect();
|
|
334
|
-
});
|
|
335
|
-
it('应该成功加入房间', async () => {
|
|
336
|
-
const client1 = new TestClient();
|
|
337
|
-
const client2 = new TestClient();
|
|
338
|
-
await client1.connect(gatewayUrl);
|
|
339
|
-
await client2.connect(gatewayUrl);
|
|
340
|
-
// Client1 鉴权并创建房间
|
|
341
|
-
await client1.sendAndWait(core_1.MessageId.AUTH_REQ, { token: 'token_user1' }, core_1.MessageId.AUTH_RES);
|
|
342
|
-
const createRes = await client1.sendAndWait(core_1.MessageId.CREATE_ROOM_REQ, { roomId: 'test-room-123', gameType: 'test', maxPlayers: 4 }, core_1.MessageId.CREATE_ROOM_RES);
|
|
343
|
-
const roomId = createRes.payload.roomId;
|
|
344
|
-
// Client2 鉴权并加入房间
|
|
345
|
-
await client2.sendAndWait(core_1.MessageId.AUTH_REQ, { token: 'token_user2' }, core_1.MessageId.AUTH_RES);
|
|
346
|
-
client1.clearMessages();
|
|
347
|
-
client2.clearMessages();
|
|
348
|
-
const joinRes = await client2.sendAndWait(core_1.MessageId.JOIN_ROOM_REQ, { roomId }, core_1.MessageId.JOIN_ROOM_RES);
|
|
349
|
-
expect(joinRes.payload.success).toBe(true);
|
|
350
|
-
expect(joinRes.payload.roomId).toBe(roomId);
|
|
351
|
-
// 等待广播消息
|
|
352
|
-
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
353
|
-
// Client1 应该收到玩家加入的广播
|
|
354
|
-
const client1Messages = client1.getMessages();
|
|
355
|
-
const joinBroadcast = client1Messages.find((msg) => msg.msgId === core_1.MessageId.PLAYER_JOINED);
|
|
356
|
-
expect(joinBroadcast).toBeDefined();
|
|
357
|
-
expect(joinBroadcast.payload.userId).toBe('user_user2');
|
|
358
|
-
client1.disconnect();
|
|
359
|
-
client2.disconnect();
|
|
360
|
-
});
|
|
361
|
-
});
|
|
362
|
-
describe('消息路由', () => {
|
|
363
|
-
it('应该正确转发并响应客户端消息', async () => {
|
|
364
|
-
const client = new TestClient();
|
|
365
|
-
await client.connect(gatewayUrl);
|
|
366
|
-
// 鉴权
|
|
367
|
-
await client.sendAndWait(core_1.MessageId.AUTH_REQ, { token: 'token_test123' }, core_1.MessageId.AUTH_RES);
|
|
368
|
-
// 创建房间
|
|
369
|
-
await client.sendAndWait(core_1.MessageId.CREATE_ROOM_REQ, { gameType: 'test', maxPlayers: 4 }, core_1.MessageId.CREATE_ROOM_RES);
|
|
370
|
-
// 发送自定义消息
|
|
371
|
-
const customMsgId = 3001;
|
|
372
|
-
const customPayload = { action: 'test', data: 'hello' };
|
|
373
|
-
const response = await client.sendAndWait(customMsgId, customPayload, customMsgId + 1);
|
|
374
|
-
expect(response.payload.echo).toEqual(customPayload);
|
|
375
|
-
expect(response.payload.serverTime).toBeDefined();
|
|
376
|
-
client.disconnect();
|
|
377
|
-
});
|
|
378
|
-
});
|
|
379
|
-
describe('心跳机制', () => {
|
|
380
|
-
it('应该正确响应心跳', async () => {
|
|
381
|
-
const client = new TestClient();
|
|
382
|
-
await client.connect(gatewayUrl);
|
|
383
|
-
const timestamp = Date.now();
|
|
384
|
-
const heartbeatRes = await client.sendAndWait(core_1.MessageId.HEARTBEAT, { timestamp }, core_1.MessageId.HEARTBEAT_ACK);
|
|
385
|
-
expect(heartbeatRes.payload.timestamp).toBe(timestamp);
|
|
386
|
-
expect(heartbeatRes.payload.serverTime).toBeDefined();
|
|
387
|
-
client.disconnect();
|
|
388
|
-
});
|
|
389
|
-
});
|
|
390
|
-
describe('并发场景', () => {
|
|
391
|
-
it('应该处理多个客户端同时连接', async () => {
|
|
392
|
-
const clients = [];
|
|
393
|
-
const clientCount = 10;
|
|
394
|
-
// 创建并连接多个客户端
|
|
395
|
-
for (let i = 0; i < clientCount; i++) {
|
|
396
|
-
const client = new TestClient();
|
|
397
|
-
await client.connect(gatewayUrl);
|
|
398
|
-
clients.push(client);
|
|
399
|
-
}
|
|
400
|
-
// 所有客户端同时鉴权
|
|
401
|
-
const authPromises = clients.map((client, i) => client.sendAndWait(core_1.MessageId.AUTH_REQ, { token: `token_user${i}` }, core_1.MessageId.AUTH_RES));
|
|
402
|
-
const authResults = await Promise.all(authPromises);
|
|
403
|
-
authResults.forEach((res, i) => {
|
|
404
|
-
expect(res.payload.success).toBe(true);
|
|
405
|
-
expect(res.payload.userId).toBe(`user_user${i}`);
|
|
406
|
-
});
|
|
407
|
-
// 清理
|
|
408
|
-
clients.forEach((client) => client.disconnect());
|
|
409
|
-
});
|
|
410
|
-
});
|
|
411
|
-
});
|
|
412
|
-
//# sourceMappingURL=gateway-worker.e2e.spec.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"gateway-worker.e2e.spec.js","sourceRoot":"","sources":["../../../test/e2e/gateway-worker.e2e.spec.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,8BAA8B;AAC9B;;;GAGG;AACH,+CAAiC;AACjC,2CAA6B;AAE7B,iBAAiB;AACjB,MAAM,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,kBAAkB,CAAC,EAAE,CAAC,CAAC;AAErE,6CAAsD;AAEtD,4CAA2B;AAC3B,sDAA4B;AAC5B,6DAAyD;AACzD,wCAA0C;AAE1C,aAAa;AACb,MAAM,iBAAiB;IACb,WAAW,CAAQ;IACnB,QAAQ,CAAQ;IAChB,QAAQ,CAAS;IAEzB,YAAY,QAAgB;QAC1B,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,WAAW,GAAG,IAAI,iBAAK,CAAC;YAC3B,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,WAAW;YAC3C,IAAI,EAAE,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,OAAO,CAAC;YACjD,QAAQ,EAAE,OAAO,CAAC,GAAG,CAAC,cAAc;SACrC,CAAC,CAAC;QACH,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,WAAW,CAAC,SAAS,EAAE,CAAC;IAC/C,CAAC;IAED,KAAK,CAAC,KAAK;QACT,WAAW;QACX,MAAM,IAAI,CAAC,WAAW,CAAC,IAAI,CACzB,aAAa,IAAI,CAAC,QAAQ,EAAE,EAC5B,QAAQ,EACR,QAAQ,EACR,aAAa,EACb,IAAI,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CACtB,CAAC;QAEF,iBAAiB;QACjB,MAAM,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,iBAAiB,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;QAChE,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,SAAS,EAAE,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IAC7D,CAAC;IAEO,KAAK,CAAC,aAAa,CAAC,OAAe,EAAE,OAAe;QAC1D,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YAEhC,QAAQ,GAAG,CAAC,IAAI,EAAE,CAAC;gBACjB,KAAK,MAAM;oBACT,MAAM,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;oBAC3B,MAAM;gBACR,KAAK,aAAa;oBAChB,MAAM,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAAC;oBACjC,MAAM;gBACR,KAAK,WAAW;oBACd,MAAM,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC;oBAC/B,MAAM;gBACR,KAAK,gBAAgB;oBACnB,MAAM,IAAI,CAAC,mBAAmB,CAAC,GAAG,CAAC,CAAC;oBACpC,MAAM;YACV,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,mBAAmB,EAAE,KAAK,CAAC,CAAC;QAC5C,CAAC;IACH,CAAC;IAEO,KAAK,CAAC,UAAU,CAAC,GAAQ;QAC/B,MAAM,EAAE,SAAS,EAAE,YAAY,EAAE,OAAO,EAAE,GAAG,GAAG,CAAC;QAEjD,YAAY;QACZ,MAAM,OAAO,GAAG,OAAO,CAAC,KAAK,IAAI,OAAO,CAAC,KAAK,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;QACpE,MAAM,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QAEzE,MAAM,QAAQ,GAAG;YACf,IAAI,EAAE,eAAe;YACrB,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,SAAS,EAAE,GAAG,CAAC,SAAS;YACxB,YAAY;YACZ,OAAO,EAAE,OAAO;YAChB,MAAM;YACN,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,eAAe;YAC5C,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;SACtB,CAAC;QAEF,MAAM,IAAI,CAAC,WAAW,CAAC,OAAO,CAC5B,kBAAkB,SAAS,EAAE,EAC7B,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CACzB,CAAC;IACJ,CAAC;IAEO,KAAK,CAAC,gBAAgB,CAAC,GAAQ;QACrC,MAAM,EAAE,SAAS,EAAE,YAAY,EAAE,OAAO,EAAE,GAAG,GAAG,CAAC;QACjD,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,QAAQ,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;QAEtD,iBAAiB;QACjB,MAAM,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,WAAW,MAAM,SAAS,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;QAEtE,MAAM,QAAQ,GAAG;YACf,IAAI,EAAE,iBAAiB;YACvB,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,YAAY;YACZ,KAAK,EAAE,gBAAS,CAAC,eAAe;YAChC,OAAO,EAAE;gBACP,OAAO,EAAE,IAAI;gBACb,MAAM;gBACN,QAAQ,EAAE,IAAI,CAAC,QAAQ;aACxB;YACD,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;SACtB,CAAC;QAEF,MAAM,IAAI,CAAC,WAAW,CAAC,OAAO,CAC5B,kBAAkB,SAAS,EAAE,EAC7B,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CACzB,CAAC;IACJ,CAAC;IAEO,KAAK,CAAC,cAAc,CAAC,GAAQ;QACnC,MAAM,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,GAAG,CAAC;QACzD,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC;QAE3B,WAAW;QACX,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,WAAW,MAAM,SAAS,CAAC,CAAC;QACxE,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,MAAM,aAAa,GAAG;gBACpB,IAAI,EAAE,iBAAiB;gBACvB,QAAQ,EAAE,IAAI,CAAC,QAAQ;gBACvB,YAAY;gBACZ,KAAK,EAAE,gBAAS,CAAC,KAAK;gBACtB,OAAO,EAAE;oBACP,KAAK,EAAE,gBAAgB;oBACvB,OAAO,EAAE,qBAAqB;iBAC/B;gBACD,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;aACtB,CAAC;YACF,MAAM,IAAI,CAAC,WAAW,CAAC,OAAO,CAC5B,kBAAkB,SAAS,EAAE,EAC7B,IAAI,CAAC,SAAS,CAAC,aAAa,CAAC,CAC9B,CAAC;YACF,OAAO;QACT,CAAC;QAED,UAAU;QACV,MAAM,IAAI,CAAC,WAAW,CAAC,IAAI,CACzB,WAAW,MAAM,UAAU,EAC3B,MAAM,EACN,IAAI,CAAC,SAAS,CAAC,EAAE,SAAS,EAAE,YAAY,EAAE,QAAQ,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC,CAClE,CAAC;QAEF,WAAW;QACX,MAAM,QAAQ,GAAG;YACf,IAAI,EAAE,iBAAiB;YACvB,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,YAAY;YACZ,KAAK,EAAE,gBAAS,CAAC,aAAa;YAC9B,OAAO,EAAE;gBACP,OAAO,EAAE,IAAI;gBACb,MAAM;gBACN,OAAO,EAAE,MAAM,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC;aAC3C;YACD,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;SACtB,CAAC;QAEF,MAAM,IAAI,CAAC,WAAW,CAAC,OAAO,CAC5B,kBAAkB,SAAS,EAAE,EAC7B,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CACzB,CAAC;QAEF,WAAW;QACX,MAAM,YAAY,GAAG;YACnB,IAAI,EAAE,gBAAgB;YACtB,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,MAAM;YACN,KAAK,EAAE,gBAAS,CAAC,aAAa;YAC9B,OAAO,EAAE;gBACP,MAAM;gBACN,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;aACtB;YACD,aAAa,EAAE,MAAM;YACrB,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;SACtB,CAAC;QAEF,MAAM,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,cAAc,EAAE,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC,CAAC;IAC/E,CAAC;IAEO,KAAK,CAAC,mBAAmB,CAAC,GAAQ;QACxC,MAAM,EAAE,SAAS,EAAE,YAAY,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,GAAG,CAAC;QAExD,SAAS;QACT,MAAM,QAAQ,GAAG;YACf,IAAI,EAAE,iBAAiB;YACvB,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,YAAY;YACZ,KAAK,EAAE,KAAK,GAAG,CAAC,EAAE,kBAAkB;YACpC,OAAO,EAAE;gBACP,IAAI,EAAE,OAAO;gBACb,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE;aACvB;YACD,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;SACtB,CAAC;QAEF,MAAM,IAAI,CAAC,WAAW,CAAC,OAAO,CAC5B,kBAAkB,SAAS,EAAE,EAC7B,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CACzB,CAAC;IACJ,CAAC;IAEO,KAAK,CAAC,cAAc,CAAC,MAAc;QACzC,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,WAAW,MAAM,UAAU,CAAC,CAAC;QAC5E,OAAO,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC;YACtD,MAAM;YACN,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC;SACpB,CAAC,CAAC,CAAC;IACN,CAAC;IAED,KAAK,CAAC,IAAI;QACR,MAAM,IAAI,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC;QAClC,MAAM,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC;QAC3B,MAAM,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,aAAa,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;QACzD,MAAM,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC;IAChC,CAAC;CACF;AAED,QAAQ;AACR,MAAM,UAAU;IACN,EAAE,GAAqB,IAAI,CAAC;IAC5B,QAAQ,GAAU,EAAE,CAAC;IACrB,gBAAgB,GAAG,IAAI,GAAG,EAA8B,CAAC;IAEjE,KAAK,CAAC,OAAO,CAAC,GAAW;QACvB,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACrC,IAAI,CAAC,EAAE,GAAG,IAAI,YAAS,CAAC,GAAG,CAAC,CAAC;YAE7B,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC;YACpC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;YAC5B,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,IAAI,EAAE,EAAE;gBAC7B,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;gBACxC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;gBAExB,MAAM,QAAQ,GAAG,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;gBACtD,IAAI,QAAQ,EAAE,CAAC;oBACb,QAAQ,CAAC,GAAG,CAAC,CAAC;oBACd,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;gBAC1C,CAAC;YACH,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC;IAED,IAAI,CAAC,KAAa,EAAE,OAAY;QAC9B,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,IAAI,CAAC,EAAE,CAAC,UAAU,KAAK,YAAS,CAAC,IAAI,EAAE,CAAC;YACtD,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;QAC7C,CAAC;QACD,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC;IACnD,CAAC;IAED,KAAK,CAAC,WAAW,CAAC,KAAa,EAAE,OAAY,EAAE,gBAAwB,EAAE,OAAO,GAAG,IAAI;QACrF,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACrC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;gBAC5B,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC;gBAC/C,MAAM,CAAC,IAAI,KAAK,CAAC,+BAA+B,gBAAgB,EAAE,CAAC,CAAC,CAAC;YACvE,CAAC,EAAE,OAAO,CAAC,CAAC;YAEZ,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC,GAAG,EAAE,EAAE;gBAClD,YAAY,CAAC,KAAK,CAAC,CAAC;gBACpB,OAAO,CAAC,GAAG,CAAC,CAAC;YACf,CAAC,CAAC,CAAC;YAEH,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;QAC5B,CAAC,CAAC,CAAC;IACL,CAAC;IAED,WAAW;QACT,OAAO,IAAI,CAAC,QAAQ,CAAC;IACvB,CAAC;IAED,aAAa;QACX,IAAI,CAAC,QAAQ,GAAG,EAAE,CAAC;IACrB,CAAC;IAED,UAAU;QACR,IAAI,IAAI,CAAC,EAAE,EAAE,CAAC;YACZ,IAAI,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC;YAChB,IAAI,CAAC,EAAE,GAAG,IAAI,CAAC;QACjB,CAAC;IACH,CAAC;CACF;AAED,QAAQ,CAAC,0BAA0B,EAAE,GAAG,EAAE;IACxC,IAAI,GAAqB,CAAC;IAC1B,IAAI,UAA6B,CAAC;IAClC,IAAI,KAAY,CAAC;IACjB,MAAM,QAAQ,GAAG,iBAAiB,CAAC;IACnC,MAAM,UAAU,GAAG,wBAAwB,CAAC;IAE5C,SAAS,CAAC,KAAK,IAAI,EAAE;QACnB,cAAc;QACd,MAAM,aAAa,GAAkB,MAAM,cAAI,CAAC,mBAAmB,CAAC;YAClE,OAAO,EAAE,CAAC,8BAAa,CAAC;SACzB,CAAC,CAAC,OAAO,EAAE,CAAC;QAEb,GAAG,GAAG,aAAa,CAAC,qBAAqB,EAAE,CAAC;QAC5C,MAAM,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QAEvB,aAAa;QACb,KAAK,GAAG,IAAI,iBAAK,CAAC;YAChB,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,WAAW;YAC3C,IAAI,EAAE,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,MAAM,CAAC;SACjD,CAAC,CAAC;QAEH,aAAa;QACb,UAAU,GAAG,IAAI,iBAAiB,CAAC,QAAQ,CAAC,CAAC;QAC7C,MAAM,UAAU,CAAC,KAAK,EAAE,CAAC;QAEzB,UAAU;QACV,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC;IAC5D,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,KAAK,IAAI,EAAE;QAClB,MAAM,UAAU,CAAC,IAAI,EAAE,CAAC;QACxB,MAAM,KAAK,CAAC,IAAI,EAAE,CAAC;QACnB,MAAM,GAAG,CAAC,KAAK,EAAE,CAAC;IACpB,CAAC,CAAC,CAAC;IAEH,UAAU,CAAC,KAAK,IAAI,EAAE;QACpB,SAAS;QACT,MAAM,IAAI,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACtC,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACpB,MAAM,KAAK,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,CAAC;QAC3B,CAAC;QACD,aAAa;QACb,MAAM,KAAK,CAAC,IAAI,CACd,aAAa,QAAQ,EAAE,EACvB,QAAQ,EACR,QAAQ,EACR,aAAa,EACb,IAAI,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CACtB,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,MAAM,EAAE,GAAG,EAAE;QACpB,EAAE,CAAC,UAAU,EAAE,KAAK,IAAI,EAAE;YACxB,MAAM,MAAM,GAAG,IAAI,UAAU,EAAE,CAAC;YAChC,MAAM,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;YAEjC,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,WAAW,CACtC,gBAAS,CAAC,QAAQ,EAClB,EAAE,KAAK,EAAE,eAAe,EAAE,EAC1B,gBAAS,CAAC,QAAQ,CACnB,CAAC;YAEF,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC3C,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;YAEpD,MAAM,CAAC,UAAU,EAAE,CAAC;QACtB,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,aAAa,EAAE,KAAK,IAAI,EAAE;YAC3B,MAAM,MAAM,GAAG,IAAI,UAAU,EAAE,CAAC;YAChC,MAAM,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;YAEjC,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,WAAW,CACtC,gBAAS,CAAC,QAAQ,EAClB,EAAE,KAAK,EAAE,SAAS,EAAE,EACpB,gBAAS,CAAC,QAAQ,CACnB,CAAC;YAEF,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAC5C,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;YAEpD,MAAM,CAAC,UAAU,EAAE,CAAC;QACtB,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,MAAM,EAAE,GAAG,EAAE;QACpB,EAAE,CAAC,UAAU,EAAE,KAAK,IAAI,EAAE;YACxB,MAAM,MAAM,GAAG,IAAI,UAAU,EAAE,CAAC;YAChC,MAAM,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;YAEjC,MAAM;YACN,MAAM,MAAM,CAAC,WAAW,CACtB,gBAAS,CAAC,QAAQ,EAClB,EAAE,KAAK,EAAE,eAAe,EAAE,EAC1B,gBAAS,CAAC,QAAQ,CACnB,CAAC;YAEF,OAAO;YACP,MAAM,SAAS,GAAG,MAAM,MAAM,CAAC,WAAW,CACxC,gBAAS,CAAC,eAAe,EACzB,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,EAAE,EACnC,gBAAS,CAAC,eAAe,CAC1B,CAAC;YAEF,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC7C,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,WAAW,EAAE,CAAC;YAC/C,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YAElD,MAAM,CAAC,UAAU,EAAE,CAAC;QACtB,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,UAAU,EAAE,KAAK,IAAI,EAAE;YACxB,MAAM,OAAO,GAAG,IAAI,UAAU,EAAE,CAAC;YACjC,MAAM,OAAO,GAAG,IAAI,UAAU,EAAE,CAAC;YAEjC,MAAM,OAAO,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;YAClC,MAAM,OAAO,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;YAElC,kBAAkB;YAClB,MAAM,OAAO,CAAC,WAAW,CACvB,gBAAS,CAAC,QAAQ,EAClB,EAAE,KAAK,EAAE,aAAa,EAAE,EACxB,gBAAS,CAAC,QAAQ,CACnB,CAAC;YAEF,MAAM,SAAS,GAAG,MAAM,OAAO,CAAC,WAAW,CACzC,gBAAS,CAAC,eAAe,EACzB,EAAE,MAAM,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,EAAE,EAC5D,gBAAS,CAAC,eAAe,CAC1B,CAAC;YAEF,MAAM,MAAM,GAAG,SAAS,CAAC,OAAO,CAAC,MAAM,CAAC;YAExC,kBAAkB;YAClB,MAAM,OAAO,CAAC,WAAW,CACvB,gBAAS,CAAC,QAAQ,EAClB,EAAE,KAAK,EAAE,aAAa,EAAE,EACxB,gBAAS,CAAC,QAAQ,CACnB,CAAC;YAEF,OAAO,CAAC,aAAa,EAAE,CAAC;YACxB,OAAO,CAAC,aAAa,EAAE,CAAC;YAExB,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,WAAW,CACvC,gBAAS,CAAC,aAAa,EACvB,EAAE,MAAM,EAAE,EACV,gBAAS,CAAC,aAAa,CACxB,CAAC;YAEF,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC3C,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YAE5C,SAAS;YACT,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,CAAC;YAEzD,sBAAsB;YACtB,MAAM,eAAe,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;YAC9C,MAAM,aAAa,GAAG,eAAe,CAAC,IAAI,CACxC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,KAAK,KAAK,gBAAS,CAAC,aAAa,CAC/C,CAAC;YACF,MAAM,CAAC,aAAa,CAAC,CAAC,WAAW,EAAE,CAAC;YACpC,MAAM,CAAC,aAAa,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;YAExD,OAAO,CAAC,UAAU,EAAE,CAAC;YACrB,OAAO,CAAC,UAAU,EAAE,CAAC;QACvB,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,MAAM,EAAE,GAAG,EAAE;QACpB,EAAE,CAAC,gBAAgB,EAAE,KAAK,IAAI,EAAE;YAC9B,MAAM,MAAM,GAAG,IAAI,UAAU,EAAE,CAAC;YAChC,MAAM,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;YAEjC,KAAK;YACL,MAAM,MAAM,CAAC,WAAW,CACtB,gBAAS,CAAC,QAAQ,EAClB,EAAE,KAAK,EAAE,eAAe,EAAE,EAC1B,gBAAS,CAAC,QAAQ,CACnB,CAAC;YAEF,OAAO;YACP,MAAM,MAAM,CAAC,WAAW,CACtB,gBAAS,CAAC,eAAe,EACzB,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,EAAE,EACnC,gBAAS,CAAC,eAAe,CAC1B,CAAC;YAEF,UAAU;YACV,MAAM,WAAW,GAAG,IAAI,CAAC;YACzB,MAAM,aAAa,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;YAExD,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,WAAW,CACvC,WAAW,EACX,aAAa,EACb,WAAW,GAAG,CAAC,CAChB,CAAC;YAEF,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;YACrD,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,WAAW,EAAE,CAAC;YAElD,MAAM,CAAC,UAAU,EAAE,CAAC;QACtB,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,MAAM,EAAE,GAAG,EAAE;QACpB,EAAE,CAAC,UAAU,EAAE,KAAK,IAAI,EAAE;YACxB,MAAM,MAAM,GAAG,IAAI,UAAU,EAAE,CAAC;YAChC,MAAM,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;YAEjC,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;YAC7B,MAAM,YAAY,GAAG,MAAM,MAAM,CAAC,WAAW,CAC3C,gBAAS,CAAC,SAAS,EACnB,EAAE,SAAS,EAAE,EACb,gBAAS,CAAC,aAAa,CACxB,CAAC;YAEF,MAAM,CAAC,YAAY,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YACvD,MAAM,CAAC,YAAY,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,WAAW,EAAE,CAAC;YAEtD,MAAM,CAAC,UAAU,EAAE,CAAC;QACtB,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,MAAM,EAAE,GAAG,EAAE;QACpB,EAAE,CAAC,eAAe,EAAE,KAAK,IAAI,EAAE;YAC7B,MAAM,OAAO,GAAiB,EAAE,CAAC;YACjC,MAAM,WAAW,GAAG,EAAE,CAAC;YAEvB,aAAa;YACb,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,WAAW,EAAE,CAAC,EAAE,EAAE,CAAC;gBACrC,MAAM,MAAM,GAAG,IAAI,UAAU,EAAE,CAAC;gBAChC,MAAM,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;gBACjC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YACvB,CAAC;YAED,YAAY;YACZ,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAC7C,MAAM,CAAC,WAAW,CAChB,gBAAS,CAAC,QAAQ,EAClB,EAAE,KAAK,EAAE,aAAa,CAAC,EAAE,EAAE,EAC3B,gBAAS,CAAC,QAAQ,CACnB,CACF,CAAC;YAEF,MAAM,WAAW,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;YAEpD,WAAW,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE;gBAC7B,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBACvC,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC;YACnD,CAAC,CAAC,CAAC;YAEH,KAAK;YACL,OAAO,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC,CAAC;QACnD,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"admin-api.spec.d.ts","sourceRoot":"","sources":["../../../test/integration/admin-api.spec.ts"],"names":[],"mappings":""}
|
|
@@ -1,218 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
/// <reference types="jest" />
|
|
7
|
-
const testing_1 = require("@nestjs/testing");
|
|
8
|
-
const supertest_1 = __importDefault(require("supertest"));
|
|
9
|
-
const admin_controller_1 = require("../../src/admin/admin.controller");
|
|
10
|
-
const admin_service_1 = require("../../src/admin/admin.service");
|
|
11
|
-
const config_service_1 = require("../../src/config/config.service");
|
|
12
|
-
const connection_service_1 = require("../../src/connection/connection.service");
|
|
13
|
-
const redis_service_1 = require("../../src/redis/redis.service");
|
|
14
|
-
const rate_limiter_service_1 = require("../../src/ratelimit/rate-limiter.service");
|
|
15
|
-
/**
|
|
16
|
-
* Gateway Admin API 集成测试
|
|
17
|
-
* 只测试 Admin API 模块,不启动 WebSocket
|
|
18
|
-
*/
|
|
19
|
-
describe('Gateway Admin API (Integration)', () => {
|
|
20
|
-
let app;
|
|
21
|
-
const mockConfigService = {
|
|
22
|
-
gatewayId: 'gateway-test-integration',
|
|
23
|
-
port: 18080,
|
|
24
|
-
adminPort: 18081,
|
|
25
|
-
redisHost: process.env.REDIS_HOST || 'localhost',
|
|
26
|
-
redisPort: parseInt(process.env.REDIS_PORT || '6379', 10),
|
|
27
|
-
redisPassword: process.env.REDIS_PASSWORD,
|
|
28
|
-
heartbeatInterval: 30000,
|
|
29
|
-
heartbeatTimeout: 90000,
|
|
30
|
-
maxConnections: 10000,
|
|
31
|
-
connectionRateLimit: 100,
|
|
32
|
-
messageRateLimitPerConnection: 50,
|
|
33
|
-
messageBurstLimit: 100,
|
|
34
|
-
globalMessageRateLimit: 50000,
|
|
35
|
-
};
|
|
36
|
-
const mockConnectionService = {
|
|
37
|
-
getConnectionCount: jest.fn().mockReturnValue(0),
|
|
38
|
-
getAuthenticatedCount: jest.fn().mockReturnValue(0),
|
|
39
|
-
getAllConnections: jest.fn().mockReturnValue(new Map()),
|
|
40
|
-
getConnection: jest.fn().mockReturnValue(null),
|
|
41
|
-
getConnectionByUserId: jest.fn().mockReturnValue(null),
|
|
42
|
-
getConnectionIdByUserId: jest.fn().mockReturnValue(null),
|
|
43
|
-
getAllUsers: jest.fn().mockReturnValue([]),
|
|
44
|
-
broadcast: jest.fn().mockReturnValue({ success: 0, failed: 0 }),
|
|
45
|
-
removeConnection: jest.fn(),
|
|
46
|
-
send: jest.fn(),
|
|
47
|
-
};
|
|
48
|
-
const mockRedisService = {
|
|
49
|
-
client: {
|
|
50
|
-
status: 'ready',
|
|
51
|
-
},
|
|
52
|
-
};
|
|
53
|
-
const mockRateLimiterService = {
|
|
54
|
-
getCurrentConnectionRate: jest.fn().mockReturnValue(0),
|
|
55
|
-
getCurrentGlobalMessageRate: jest.fn().mockReturnValue(0),
|
|
56
|
-
getConnectionRateLimitStats: jest.fn().mockReturnValue({
|
|
57
|
-
count: 0,
|
|
58
|
-
window: 1000,
|
|
59
|
-
limit: 100,
|
|
60
|
-
}),
|
|
61
|
-
getMessageRateLimitStats: jest.fn().mockReturnValue({
|
|
62
|
-
trackedConnections: 0,
|
|
63
|
-
totalMessages: 0,
|
|
64
|
-
}),
|
|
65
|
-
getGlobalMessageRateLimitStats: jest.fn().mockReturnValue({
|
|
66
|
-
count: 0,
|
|
67
|
-
window: 1000,
|
|
68
|
-
limit: 50000,
|
|
69
|
-
}),
|
|
70
|
-
getTrackedConnectionsCount: jest.fn().mockReturnValue(0),
|
|
71
|
-
getConfig: jest.fn().mockReturnValue({
|
|
72
|
-
connectionRateLimit: 100,
|
|
73
|
-
messageRateLimitPerConnection: 50,
|
|
74
|
-
messageBurstLimit: 100,
|
|
75
|
-
globalMessageRateLimit: 50000,
|
|
76
|
-
}),
|
|
77
|
-
getStats: jest.fn().mockReturnValue({
|
|
78
|
-
connectionRate: {
|
|
79
|
-
current: 0,
|
|
80
|
-
limit: 100,
|
|
81
|
-
window: 1000,
|
|
82
|
-
},
|
|
83
|
-
globalMessageRate: {
|
|
84
|
-
current: 0,
|
|
85
|
-
limit: 50000,
|
|
86
|
-
window: 1000,
|
|
87
|
-
},
|
|
88
|
-
trackedConnections: 0,
|
|
89
|
-
}),
|
|
90
|
-
};
|
|
91
|
-
beforeAll(async () => {
|
|
92
|
-
const moduleFixture = await testing_1.Test.createTestingModule({
|
|
93
|
-
controllers: [admin_controller_1.AdminController],
|
|
94
|
-
providers: [
|
|
95
|
-
admin_service_1.AdminService,
|
|
96
|
-
{ provide: config_service_1.ConfigService, useValue: mockConfigService },
|
|
97
|
-
{ provide: connection_service_1.ConnectionService, useValue: mockConnectionService },
|
|
98
|
-
{ provide: redis_service_1.RedisService, useValue: mockRedisService },
|
|
99
|
-
{ provide: rate_limiter_service_1.RateLimiterService, useValue: mockRateLimiterService },
|
|
100
|
-
],
|
|
101
|
-
}).compile();
|
|
102
|
-
app = moduleFixture.createNestApplication();
|
|
103
|
-
await app.init();
|
|
104
|
-
});
|
|
105
|
-
afterAll(async () => {
|
|
106
|
-
await app.close();
|
|
107
|
-
});
|
|
108
|
-
describe('GET /admin/health', () => {
|
|
109
|
-
it('should return health status', async () => {
|
|
110
|
-
const response = await (0, supertest_1.default)(app.getHttpServer())
|
|
111
|
-
.get('/admin/health')
|
|
112
|
-
.expect(200);
|
|
113
|
-
expect(response.body).toHaveProperty('status', 'ok');
|
|
114
|
-
expect(response.body).toHaveProperty('gatewayId');
|
|
115
|
-
expect(response.body).toHaveProperty('timestamp');
|
|
116
|
-
});
|
|
117
|
-
});
|
|
118
|
-
describe('GET /admin/status', () => {
|
|
119
|
-
it('should return system status', async () => {
|
|
120
|
-
const response = await (0, supertest_1.default)(app.getHttpServer())
|
|
121
|
-
.get('/admin/status')
|
|
122
|
-
.expect(200);
|
|
123
|
-
expect(response.body).toHaveProperty('gatewayId');
|
|
124
|
-
expect(response.body).toHaveProperty('uptime');
|
|
125
|
-
expect(response.body).toHaveProperty('connections');
|
|
126
|
-
expect(response.body.connections).toHaveProperty('total');
|
|
127
|
-
expect(response.body.connections).toHaveProperty('authenticated');
|
|
128
|
-
expect(response.body).toHaveProperty('memory');
|
|
129
|
-
});
|
|
130
|
-
});
|
|
131
|
-
describe('GET /admin/connections', () => {
|
|
132
|
-
it('should return empty connections list initially', async () => {
|
|
133
|
-
const response = await (0, supertest_1.default)(app.getHttpServer())
|
|
134
|
-
.get('/admin/connections')
|
|
135
|
-
.expect(200);
|
|
136
|
-
expect(response.body).toHaveProperty('count', 0);
|
|
137
|
-
expect(response.body).toHaveProperty('connections');
|
|
138
|
-
expect(response.body.connections).toEqual([]);
|
|
139
|
-
});
|
|
140
|
-
});
|
|
141
|
-
describe('GET /admin/connections/:id', () => {
|
|
142
|
-
it('should return 404 for non-existent connection', async () => {
|
|
143
|
-
await (0, supertest_1.default)(app.getHttpServer())
|
|
144
|
-
.get('/admin/connections/non-existent-id')
|
|
145
|
-
.expect(404);
|
|
146
|
-
});
|
|
147
|
-
});
|
|
148
|
-
describe('GET /admin/ratelimit/stats', () => {
|
|
149
|
-
it('should return rate limit statistics', async () => {
|
|
150
|
-
const response = await (0, supertest_1.default)(app.getHttpServer())
|
|
151
|
-
.get('/admin/ratelimit/stats')
|
|
152
|
-
.expect(200);
|
|
153
|
-
expect(response.body).toHaveProperty('connectionRate');
|
|
154
|
-
expect(response.body).toHaveProperty('globalMessageRate');
|
|
155
|
-
expect(response.body).toHaveProperty('trackedConnections');
|
|
156
|
-
expect(response.body).toHaveProperty('config');
|
|
157
|
-
});
|
|
158
|
-
});
|
|
159
|
-
describe('GET /admin/redis/status', () => {
|
|
160
|
-
it('should return Redis status', async () => {
|
|
161
|
-
const response = await (0, supertest_1.default)(app.getHttpServer())
|
|
162
|
-
.get('/admin/redis/status')
|
|
163
|
-
.expect(200);
|
|
164
|
-
expect(response.body).toHaveProperty('status');
|
|
165
|
-
expect(response.body).toHaveProperty('host');
|
|
166
|
-
expect(response.body).toHaveProperty('port');
|
|
167
|
-
});
|
|
168
|
-
});
|
|
169
|
-
describe('GET /admin/users', () => {
|
|
170
|
-
it('should return empty users list initially', async () => {
|
|
171
|
-
const response = await (0, supertest_1.default)(app.getHttpServer())
|
|
172
|
-
.get('/admin/users')
|
|
173
|
-
.expect(200);
|
|
174
|
-
expect(response.body).toHaveProperty('count', 0);
|
|
175
|
-
expect(response.body).toHaveProperty('users');
|
|
176
|
-
expect(response.body.users).toEqual([]);
|
|
177
|
-
});
|
|
178
|
-
});
|
|
179
|
-
describe('GET /admin/users/:id', () => {
|
|
180
|
-
it('should return 404 for non-existent user', async () => {
|
|
181
|
-
await (0, supertest_1.default)(app.getHttpServer())
|
|
182
|
-
.get('/admin/users/non-existent-user')
|
|
183
|
-
.expect(404);
|
|
184
|
-
});
|
|
185
|
-
});
|
|
186
|
-
describe('POST /admin/users/:id/kick', () => {
|
|
187
|
-
it('should return 404 for non-existent user', async () => {
|
|
188
|
-
await (0, supertest_1.default)(app.getHttpServer())
|
|
189
|
-
.post('/admin/users/non-existent-user/kick')
|
|
190
|
-
.send({ reason: 'test' })
|
|
191
|
-
.expect(404);
|
|
192
|
-
});
|
|
193
|
-
});
|
|
194
|
-
describe('POST /admin/broadcast', () => {
|
|
195
|
-
it('should broadcast message (0 recipients when no connections)', async () => {
|
|
196
|
-
const response = await (0, supertest_1.default)(app.getHttpServer())
|
|
197
|
-
.post('/admin/broadcast')
|
|
198
|
-
.send({ msgId: 9999, payload: { message: 'test' } })
|
|
199
|
-
.expect(200);
|
|
200
|
-
expect(response.body).toHaveProperty('success', true);
|
|
201
|
-
expect(response.body).toHaveProperty('sent', 0);
|
|
202
|
-
expect(response.body).toHaveProperty('total', 0);
|
|
203
|
-
});
|
|
204
|
-
});
|
|
205
|
-
describe('GET /admin/metrics', () => {
|
|
206
|
-
it('should return metrics data', async () => {
|
|
207
|
-
const response = await (0, supertest_1.default)(app.getHttpServer())
|
|
208
|
-
.get('/admin/metrics')
|
|
209
|
-
.expect(200);
|
|
210
|
-
expect(response.body).toHaveProperty('timestamp');
|
|
211
|
-
expect(response.body).toHaveProperty('gateway');
|
|
212
|
-
expect(response.body).toHaveProperty('connections');
|
|
213
|
-
expect(response.body).toHaveProperty('rateLimit');
|
|
214
|
-
expect(response.body).toHaveProperty('memory');
|
|
215
|
-
});
|
|
216
|
-
});
|
|
217
|
-
});
|
|
218
|
-
//# sourceMappingURL=admin-api.spec.js.map
|