@thejrsoft/subway-protocol 1.4.14 → 1.6.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.
@@ -1,297 +0,0 @@
1
- /**
2
- * 协议规范测试套件
3
- * 验证实现是否符合 PROTOCOL_SPECIFICATION.md
4
- */
5
-
6
- import {
7
- MessageFactory,
8
- MessageValidator,
9
- MessageType,
10
- ClientType,
11
- Priority,
12
- OperationType,
13
- CommandType,
14
- GatewayUtils,
15
- GATEWAY_SITE_ID,
16
- isRegisterMessage,
17
- isCommandMessage
18
- } from '../index';
19
-
20
- describe('JRSoft Subway Protocol v1.0', () => {
21
- describe('基础消息格式', () => {
22
- test('所有消息必须包含 type 字段', () => {
23
- const message = { type: 'register', clientId: 'test' };
24
- expect(MessageValidator.validateMessage(message)).toBe(true);
25
-
26
- const invalidMessage = { clientId: 'test' };
27
- expect(MessageValidator.validateMessage(invalidMessage)).toBe(false);
28
- });
29
-
30
- test('timestamp 和 version 字段是可选的', () => {
31
- const minimalMessage = { type: 'register', clientId: 'test' };
32
- expect(MessageValidator.validateRegisterMessage(minimalMessage)).toBe(true);
33
-
34
- const fullMessage = {
35
- type: 'register',
36
- clientId: 'test',
37
- timestamp: new Date().toISOString(),
38
- version: '1.0'
39
- };
40
- expect(MessageValidator.validateRegisterMessage(fullMessage)).toBe(true);
41
- });
42
- });
43
-
44
- describe('注册消息兼容性', () => {
45
- test('支持简单注册格式(v1.0)', () => {
46
- const simpleRegister = {
47
- type: 'register',
48
- clientId: 'device001'
49
- };
50
-
51
- expect(isRegisterMessage(simpleRegister)).toBe(true);
52
- expect(MessageValidator.validateRegisterMessage(simpleRegister)).toBe(true);
53
- });
54
-
55
- test('支持完整注册格式(v1.0)', () => {
56
- const fullRegister = MessageFactory.createRegisterMessage(
57
- 'backend-server',
58
- ClientType.BACKEND,
59
- {
60
- version: '1.0.0',
61
- platform: 'nodejs',
62
- capabilities: ['command', 'program']
63
- }
64
- );
65
-
66
- expect(isRegisterMessage(fullRegister)).toBe(true);
67
- expect(MessageValidator.validateRegisterMessage(fullRegister)).toBe(true);
68
- expect(fullRegister.clientType).toBe(ClientType.BACKEND);
69
- expect(fullRegister.clientInfo?.capabilities).toContain('command');
70
- });
71
- });
72
-
73
- describe('命令消息格式', () => {
74
- test('标准命令消息', () => {
75
- const command = MessageFactory.createCommandMessage(
76
- 'req-123',
77
- 'device001',
78
- {
79
- commandType: CommandType.SIMPLE,
80
- commandCode: 'READ_STATUS',
81
- deviceType: 'controller',
82
- operationType: OperationType.READ,
83
- parameters: {}
84
- },
85
- 'http://callback.example.com/response'
86
- );
87
-
88
- expect(isCommandMessage(command)).toBe(true);
89
- expect(MessageValidator.validateCommandMessage(command)).toBe(true);
90
- expect(command.priority).toBe(Priority.NORMAL);
91
- expect(command.timeout).toBe(10000);
92
- });
93
-
94
- test('高优先级命令', () => {
95
- const command = MessageFactory.createCommandMessage(
96
- 'req-urgent',
97
- 'device001',
98
- {
99
- commandType: CommandType.SIMPLE,
100
- commandCode: 'EMERGENCY_STOP',
101
- deviceType: 'controller',
102
- operationType: OperationType.WRITE,
103
- parameters: {}
104
- },
105
- 'http://callback.example.com/response',
106
- {
107
- priority: Priority.CRITICAL,
108
- timeout: 5000
109
- }
110
- );
111
-
112
- expect(command.priority).toBe(Priority.CRITICAL);
113
- expect(command.timeout).toBe(5000);
114
- });
115
- });
116
-
117
- describe('Gateway 特殊命令', () => {
118
- test('查询设备状态命令', () => {
119
- const query = GatewayUtils.createDeviceStatusQuery('device001', 'query-001');
120
-
121
- expect(query.targetClientId).toBe(GATEWAY_SITE_ID);
122
- expect(query.command.commandCode).toBe('GET_DEVICE_STATUS');
123
- expect(query.command.operationType).toBe('read');
124
- expect(query.command.parameters.targetClientId).toBe('device001');
125
- expect(GatewayUtils.isGatewayCommand(query)).toBe(true);
126
- });
127
-
128
- test('列出设备命令', () => {
129
- const listCmd = GatewayUtils.createListDevicesCommand({
130
- status: 'online',
131
- clientType: 'device'
132
- });
133
-
134
- expect(listCmd.clientId).toBe(GATEWAY_SITE_ID);
135
- expect(listCmd.command.commandCode).toBe('LIST_DEVICES');
136
- expect(listCmd.command.parameters.filter).toEqual({
137
- status: 'online',
138
- clientType: 'device'
139
- });
140
- });
141
-
142
-
143
- test('命令链', () => {
144
- const chainCmd = GatewayUtils.createCommandChain(
145
- 'device001',
146
- [
147
- { commandCode: 'STOP', parameters: {} },
148
- { commandCode: 'RESET', parameters: {}, delay: 1000 },
149
- { commandCode: 'START', parameters: {} }
150
- ]
151
- );
152
-
153
- expect(chainCmd.commandChain).toHaveLength(3);
154
- expect(chainCmd.commandChain![1].delay).toBe(1000);
155
- expect(chainCmd.timeout).toBeGreaterThan(30000); // 默认超时应考虑延迟
156
- });
157
- });
158
-
159
- describe('消息签名', () => {
160
- test('签名消息', () => {
161
- const message = { type: 'command', requestRef: 'test', clientId: 'test' };
162
- const signedMessage = GatewayUtils.signMessage(message, 'secret-key');
163
-
164
- expect(signedMessage.signature).toBeDefined();
165
- expect(signedMessage.signatureMethod).toBe('sha256');
166
- });
167
-
168
- test('验证签名', () => {
169
- const message = { type: 'command', requestRef: 'test', clientId: 'test' };
170
- const signedMessage = GatewayUtils.signMessage(message, 'secret-key');
171
-
172
- const isValid = GatewayUtils.verifyMessageSignature(signedMessage, 'secret-key');
173
- expect(isValid).toBe(true);
174
- });
175
- });
176
-
177
- describe('心跳消息', () => {
178
- test('创建心跳消息', () => {
179
- const heartbeat = MessageFactory.createHeartbeatMessage('device001', 12345);
180
-
181
- expect(heartbeat.type).toBe(MessageType.HEARTBEAT);
182
- expect(heartbeat.clientId).toBe('device001');
183
- expect(heartbeat.sequence).toBe(12345);
184
- expect(heartbeat.timestamp).toBeDefined();
185
- });
186
- });
187
-
188
- describe('错误处理', () => {
189
- test('创建错误消息', () => {
190
- const error = MessageFactory.createErrorMessage(
191
- 'DEVICE_OFFLINE',
192
- 'Target device is not connected',
193
- 'medium',
194
- 'device',
195
- {
196
- context: {
197
- clientId: 'device001',
198
- requestRef: 'req-123'
199
- },
200
- retryable: true
201
- }
202
- );
203
-
204
- expect(error.type).toBe(MessageType.ERROR);
205
- expect(error.code).toBe('DEVICE_OFFLINE');
206
- expect(error.retryable).toBe(true);
207
- expect(error.context?.clientId).toBe('device001');
208
- });
209
- });
210
-
211
- describe('程序上传', () => {
212
- test('创建程序上传消息', () => {
213
- const program = MessageFactory.createProgramMessage(
214
- 'prog-123',
215
- 'device001',
216
- {
217
- device_id: 'device001',
218
- programId: 'prog-v1.0.1',
219
- downloadUrl: 'https://example.com/program.zip',
220
- checksum: 'sha256:abcd1234',
221
- hashAlgorithm: 'sha256'
222
- }
223
- );
224
-
225
- expect(program.type).toBe(MessageType.PROGRAM);
226
- expect(program.command.commandCode).toBe('UPLOAD_PROGRAM');
227
- expect(program.command.operationType).toBe('write');
228
- expect(program.timeout).toBe(1800000); // 30分钟
229
- });
230
- });
231
-
232
- describe('协议版本兼容性', () => {
233
- test('消息类型支持字符串和枚举', () => {
234
- const stringType = { type: 'register', clientId: 'test' };
235
- const enumType = { type: MessageType.REGISTER, clientId: 'test' };
236
-
237
- expect(isRegisterMessage(stringType)).toBe(true);
238
- expect(isRegisterMessage(enumType)).toBe(true);
239
- });
240
-
241
- test('字段名兼容性(device_id vs deviceId)', () => {
242
- const oldFormat = {
243
- device_id: 'device001',
244
- programId: 'test'
245
- };
246
-
247
- const newFormat = {
248
- deviceId: 'device001',
249
- programId: 'test'
250
- };
251
-
252
- // 两种格式都应该被接受
253
- expect(oldFormat.device_id || oldFormat.deviceId).toBe('device001');
254
- expect(newFormat.deviceId || newFormat.device_id).toBe('device001');
255
- });
256
- });
257
- });
258
-
259
- describe('协议设计原则验证', () => {
260
- test('简单性优先 - 最小注册消息只需2个字段', () => {
261
- const minimalRegister = {
262
- type: 'register',
263
- clientId: 'device001'
264
- };
265
-
266
- const keys = Object.keys(minimalRegister);
267
- expect(keys).toHaveLength(2);
268
- expect(MessageValidator.validateRegisterMessage(minimalRegister)).toBe(true);
269
- });
270
-
271
- test('渐进增强 - 可选字段不影响基础功能', () => {
272
- const basic = { type: 'register', clientId: 'test' };
273
- const enhanced = {
274
- ...basic,
275
- clientType: 'device',
276
- clientInfo: { version: '1.0' },
277
- timestamp: new Date().toISOString(),
278
- version: '2.0'
279
- };
280
-
281
- // 两种格式都有效
282
- expect(MessageValidator.validateRegisterMessage(basic)).toBe(true);
283
- expect(MessageValidator.validateRegisterMessage(enhanced)).toBe(true);
284
- });
285
-
286
- test('明确语义 - 每个消息类型有唯一用途', () => {
287
- const messageTypes = Object.values(MessageType);
288
- const uniqueTypes = new Set(messageTypes);
289
-
290
- // 没有重复的消息类型
291
- expect(uniqueTypes.size).toBe(messageTypes.length);
292
-
293
- // 每个类型都有明确的用途
294
- expect(MessageType.REGISTER).not.toBe(MessageType.COMMAND);
295
- expect(MessageType.HEARTBEAT).not.toBe(MessageType.PROGRAM);
296
- });
297
- });