@f2a/network 0.1.2 → 0.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 (226) hide show
  1. package/README.md +278 -63
  2. package/dist/cli/commands.d.ts.map +1 -1
  3. package/dist/cli/commands.js +29 -2
  4. package/dist/cli/commands.js.map +1 -1
  5. package/dist/cli/config.d.ts +176 -0
  6. package/dist/cli/config.d.ts.map +1 -0
  7. package/dist/cli/config.js +386 -0
  8. package/dist/cli/config.js.map +1 -0
  9. package/dist/cli/daemon.d.ts +54 -0
  10. package/dist/cli/daemon.d.ts.map +1 -0
  11. package/dist/cli/daemon.js +572 -0
  12. package/dist/cli/daemon.js.map +1 -0
  13. package/dist/cli/index.js +90 -16
  14. package/dist/cli/index.js.map +1 -1
  15. package/dist/cli/init.d.ts +13 -0
  16. package/dist/cli/init.d.ts.map +1 -0
  17. package/dist/cli/init.js +352 -0
  18. package/dist/cli/init.js.map +1 -0
  19. package/dist/core/e2ee-crypto.d.ts +127 -1
  20. package/dist/core/e2ee-crypto.d.ts.map +1 -1
  21. package/dist/core/e2ee-crypto.js +446 -12
  22. package/dist/core/e2ee-crypto.js.map +1 -1
  23. package/dist/core/f2a.d.ts +2 -1
  24. package/dist/core/f2a.d.ts.map +1 -1
  25. package/dist/core/f2a.js +6 -2
  26. package/dist/core/f2a.js.map +1 -1
  27. package/dist/core/identity/encrypted-key-store.d.ts +19 -0
  28. package/dist/core/identity/encrypted-key-store.d.ts.map +1 -0
  29. package/dist/core/identity/encrypted-key-store.js +72 -0
  30. package/dist/core/identity/encrypted-key-store.js.map +1 -0
  31. package/dist/core/identity/identity-manager.d.ts +133 -0
  32. package/dist/core/identity/identity-manager.d.ts.map +1 -0
  33. package/dist/core/identity/identity-manager.js +454 -0
  34. package/dist/core/identity/identity-manager.js.map +1 -0
  35. package/dist/core/identity/index.d.ts +8 -0
  36. package/dist/core/identity/index.d.ts.map +1 -0
  37. package/dist/core/identity/index.js +7 -0
  38. package/dist/core/identity/index.js.map +1 -0
  39. package/dist/core/identity/types.d.ts +70 -0
  40. package/dist/core/identity/types.d.ts.map +1 -0
  41. package/dist/core/identity/types.js +17 -0
  42. package/dist/core/identity/types.js.map +1 -0
  43. package/dist/core/p2p-network.d.ts +26 -0
  44. package/dist/core/p2p-network.d.ts.map +1 -1
  45. package/dist/core/p2p-network.js +434 -105
  46. package/dist/core/p2p-network.js.map +1 -1
  47. package/dist/core/reputation-security.d.ts +15 -0
  48. package/dist/core/reputation-security.d.ts.map +1 -1
  49. package/dist/core/reputation-security.js +73 -3
  50. package/dist/core/reputation-security.js.map +1 -1
  51. package/dist/core/reputation.d.ts +129 -4
  52. package/dist/core/reputation.d.ts.map +1 -1
  53. package/dist/core/reputation.js +294 -1
  54. package/dist/core/reputation.js.map +1 -1
  55. package/dist/core/review-committee.d.ts +2 -2
  56. package/dist/core/review-committee.d.ts.map +1 -1
  57. package/dist/core/review-committee.js +17 -0
  58. package/dist/core/review-committee.js.map +1 -1
  59. package/dist/daemon/control-server.d.ts.map +1 -1
  60. package/dist/daemon/control-server.js +44 -1
  61. package/dist/daemon/control-server.js.map +1 -1
  62. package/dist/daemon/webhook.d.ts +3 -0
  63. package/dist/daemon/webhook.d.ts.map +1 -1
  64. package/dist/daemon/webhook.js +318 -6
  65. package/dist/daemon/webhook.js.map +1 -1
  66. package/dist/index.d.ts +3 -3
  67. package/dist/index.d.ts.map +1 -1
  68. package/dist/index.js +7 -3
  69. package/dist/index.js.map +1 -1
  70. package/dist/types/index.d.ts +4 -0
  71. package/dist/types/index.d.ts.map +1 -1
  72. package/dist/types/index.js.map +1 -1
  73. package/dist/types/result.d.ts +1 -1
  74. package/dist/types/result.d.ts.map +1 -1
  75. package/dist/types/result.js.map +1 -1
  76. package/dist/utils/crypto-utils.d.ts +17 -0
  77. package/dist/utils/crypto-utils.d.ts.map +1 -0
  78. package/dist/utils/crypto-utils.js +28 -0
  79. package/dist/utils/crypto-utils.js.map +1 -0
  80. package/dist/utils/logger.d.ts +1 -0
  81. package/dist/utils/logger.d.ts.map +1 -1
  82. package/dist/utils/logger.js +9 -3
  83. package/dist/utils/logger.js.map +1 -1
  84. package/dist/utils/rate-limiter.d.ts.map +1 -1
  85. package/dist/utils/rate-limiter.js +3 -1
  86. package/dist/utils/rate-limiter.js.map +1 -1
  87. package/dist/utils/signature.d.ts +47 -1
  88. package/dist/utils/signature.d.ts.map +1 -1
  89. package/dist/utils/signature.js +166 -11
  90. package/dist/utils/signature.js.map +1 -1
  91. package/package.json +9 -1
  92. package/.github/workflows/ci.yml +0 -113
  93. package/.github/workflows/publish.yml +0 -60
  94. package/MONOREPO.md +0 -58
  95. package/SKILL.md +0 -137
  96. package/dist/adapters/openclaw.d.ts +0 -103
  97. package/dist/adapters/openclaw.d.ts.map +0 -1
  98. package/dist/adapters/openclaw.js +0 -297
  99. package/dist/adapters/openclaw.js.map +0 -1
  100. package/dist/core/connection-manager.d.ts +0 -80
  101. package/dist/core/connection-manager.d.ts.map +0 -1
  102. package/dist/core/connection-manager.js +0 -235
  103. package/dist/core/connection-manager.js.map +0 -1
  104. package/dist/core/connection-manager.test.d.ts +0 -2
  105. package/dist/core/connection-manager.test.d.ts.map +0 -1
  106. package/dist/core/connection-manager.test.js +0 -52
  107. package/dist/core/connection-manager.test.js.map +0 -1
  108. package/dist/core/identity.d.ts +0 -47
  109. package/dist/core/identity.d.ts.map +0 -1
  110. package/dist/core/identity.js +0 -130
  111. package/dist/core/identity.js.map +0 -1
  112. package/dist/core/identity.test.d.ts +0 -2
  113. package/dist/core/identity.test.d.ts.map +0 -1
  114. package/dist/core/identity.test.js +0 -43
  115. package/dist/core/identity.test.js.map +0 -1
  116. package/dist/core/serverless.d.ts +0 -155
  117. package/dist/core/serverless.d.ts.map +0 -1
  118. package/dist/core/serverless.js +0 -615
  119. package/dist/core/serverless.js.map +0 -1
  120. package/dist/daemon/webhook.test.d.ts +0 -2
  121. package/dist/daemon/webhook.test.d.ts.map +0 -1
  122. package/dist/daemon/webhook.test.js +0 -24
  123. package/dist/daemon/webhook.test.js.map +0 -1
  124. package/dist/protocol/messages.d.ts +0 -739
  125. package/dist/protocol/messages.d.ts.map +0 -1
  126. package/dist/protocol/messages.js +0 -188
  127. package/dist/protocol/messages.js.map +0 -1
  128. package/dist/protocol/messages.test.d.ts +0 -2
  129. package/dist/protocol/messages.test.d.ts.map +0 -1
  130. package/dist/protocol/messages.test.js +0 -55
  131. package/dist/protocol/messages.test.js.map +0 -1
  132. package/docs/F2A-PROTOCOL.md +0 -61
  133. package/docs/MOBILE_BOOTSTRAP_DESIGN.md +0 -126
  134. package/docs/a2a-lessons.md +0 -316
  135. package/docs/middleware-guide.md +0 -448
  136. package/docs/readme-update-checklist.md +0 -90
  137. package/docs/reputation-guide.md +0 -396
  138. package/docs/rfcs/001-reputation-system.md +0 -712
  139. package/docs/security-design.md +0 -247
  140. package/install.sh +0 -231
  141. package/packages/openclaw-adapter/README.md +0 -510
  142. package/packages/openclaw-adapter/openclaw.plugin.json +0 -106
  143. package/packages/openclaw-adapter/package.json +0 -40
  144. package/packages/openclaw-adapter/src/announcement-queue.test.ts +0 -449
  145. package/packages/openclaw-adapter/src/announcement-queue.ts +0 -403
  146. package/packages/openclaw-adapter/src/capability-detector.test.ts +0 -99
  147. package/packages/openclaw-adapter/src/capability-detector.ts +0 -183
  148. package/packages/openclaw-adapter/src/claim-handlers.test.ts +0 -974
  149. package/packages/openclaw-adapter/src/claim-handlers.ts +0 -482
  150. package/packages/openclaw-adapter/src/connector.business.test.ts +0 -583
  151. package/packages/openclaw-adapter/src/connector.ts +0 -795
  152. package/packages/openclaw-adapter/src/index.test.ts +0 -82
  153. package/packages/openclaw-adapter/src/index.ts +0 -18
  154. package/packages/openclaw-adapter/src/integration.e2e.test.ts +0 -829
  155. package/packages/openclaw-adapter/src/logger.ts +0 -51
  156. package/packages/openclaw-adapter/src/network-client.test.ts +0 -266
  157. package/packages/openclaw-adapter/src/network-client.ts +0 -251
  158. package/packages/openclaw-adapter/src/network-recovery.test.ts +0 -465
  159. package/packages/openclaw-adapter/src/node-manager.test.ts +0 -136
  160. package/packages/openclaw-adapter/src/node-manager.ts +0 -429
  161. package/packages/openclaw-adapter/src/plugin.test.ts +0 -439
  162. package/packages/openclaw-adapter/src/plugin.ts +0 -104
  163. package/packages/openclaw-adapter/src/reputation.test.ts +0 -221
  164. package/packages/openclaw-adapter/src/reputation.ts +0 -368
  165. package/packages/openclaw-adapter/src/task-guard.test.ts +0 -502
  166. package/packages/openclaw-adapter/src/task-guard.ts +0 -860
  167. package/packages/openclaw-adapter/src/task-queue.concurrency.test.ts +0 -462
  168. package/packages/openclaw-adapter/src/task-queue.edge-cases.test.ts +0 -284
  169. package/packages/openclaw-adapter/src/task-queue.persistence.test.ts +0 -408
  170. package/packages/openclaw-adapter/src/task-queue.ts +0 -668
  171. package/packages/openclaw-adapter/src/tool-handlers.test.ts +0 -906
  172. package/packages/openclaw-adapter/src/tool-handlers.ts +0 -574
  173. package/packages/openclaw-adapter/src/types.ts +0 -361
  174. package/packages/openclaw-adapter/src/webhook-pusher.test.ts +0 -188
  175. package/packages/openclaw-adapter/src/webhook-pusher.ts +0 -220
  176. package/packages/openclaw-adapter/src/webhook-server.test.ts +0 -580
  177. package/packages/openclaw-adapter/src/webhook-server.ts +0 -202
  178. package/packages/openclaw-adapter/tsconfig.json +0 -20
  179. package/src/cli/commands.test.ts +0 -157
  180. package/src/cli/commands.ts +0 -129
  181. package/src/cli/index.test.ts +0 -77
  182. package/src/cli/index.ts +0 -234
  183. package/src/core/autonomous-economy.test.ts +0 -291
  184. package/src/core/autonomous-economy.ts +0 -428
  185. package/src/core/e2ee-crypto.test.ts +0 -125
  186. package/src/core/e2ee-crypto.ts +0 -246
  187. package/src/core/f2a.test.ts +0 -269
  188. package/src/core/f2a.ts +0 -618
  189. package/src/core/p2p-network.test.ts +0 -199
  190. package/src/core/p2p-network.ts +0 -1432
  191. package/src/core/reputation-security.test.ts +0 -403
  192. package/src/core/reputation-security.ts +0 -562
  193. package/src/core/reputation.test.ts +0 -260
  194. package/src/core/reputation.ts +0 -576
  195. package/src/core/review-committee.test.ts +0 -380
  196. package/src/core/review-committee.ts +0 -401
  197. package/src/core/token-manager.test.ts +0 -133
  198. package/src/core/token-manager.ts +0 -140
  199. package/src/daemon/control-server.test.ts +0 -216
  200. package/src/daemon/control-server.ts +0 -292
  201. package/src/daemon/index.test.ts +0 -85
  202. package/src/daemon/index.ts +0 -89
  203. package/src/daemon/main.ts +0 -44
  204. package/src/daemon/start.ts +0 -29
  205. package/src/daemon/webhook.test.ts +0 -68
  206. package/src/daemon/webhook.ts +0 -105
  207. package/src/index.test.ts +0 -436
  208. package/src/index.ts +0 -72
  209. package/src/types/index.test.ts +0 -87
  210. package/src/types/index.ts +0 -341
  211. package/src/types/result.ts +0 -68
  212. package/src/utils/benchmark.ts +0 -237
  213. package/src/utils/logger.ts +0 -331
  214. package/src/utils/middleware.ts +0 -229
  215. package/src/utils/rate-limiter.ts +0 -207
  216. package/src/utils/signature.ts +0 -136
  217. package/src/utils/validation.ts +0 -186
  218. package/tests/docker/Dockerfile.node +0 -23
  219. package/tests/docker/Dockerfile.runner +0 -18
  220. package/tests/docker/docker-compose.test.yml +0 -73
  221. package/tests/integration/message-passing.test.ts +0 -109
  222. package/tests/integration/multi-node.test.ts +0 -92
  223. package/tests/integration/p2p-connection.test.ts +0 -83
  224. package/tests/integration/test-config.ts +0 -32
  225. package/tsconfig.json +0 -21
  226. package/vitest.config.ts +0 -26
@@ -1,465 +0,0 @@
1
- /**
2
- * F2A 网络故障恢复集成测试
3
- * 测试网络故障、重连和状态恢复场景
4
- */
5
-
6
- import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
7
- import { TaskQueue } from './task-queue.js';
8
- import { ReputationSystem } from './reputation.js';
9
- import { F2ANetworkClient } from './network-client.js';
10
- import { WebhookPusher } from './webhook-pusher.js';
11
- import { createServer, IncomingMessage, ServerResponse } from 'http';
12
- import { mkdirSync, rmSync } from 'fs';
13
-
14
- const TEST_DATA_DIR = `/tmp/f2a-network-test-${Date.now()}`;
15
-
16
- // 辅助函数:获取可用端口
17
- async function getAvailablePort(): Promise<number> {
18
- return new Promise((resolve, reject) => {
19
- const tempServer = createServer();
20
- tempServer.listen(0, () => {
21
- const address = tempServer.address();
22
- if (address && typeof address === 'object') {
23
- const port = address.port;
24
- tempServer.close(() => resolve(port));
25
- } else {
26
- reject(new Error('Failed to get port'));
27
- }
28
- });
29
- });
30
- }
31
-
32
- describe('网络故障恢复集成测试', () => {
33
- beforeEach(() => {
34
- mkdirSync(TEST_DATA_DIR, { recursive: true });
35
- });
36
-
37
- afterEach(() => {
38
- try {
39
- rmSync(TEST_DATA_DIR, { recursive: true, force: true });
40
- } catch {
41
- // 忽略清理错误
42
- }
43
- });
44
-
45
- describe('WebhookPusher 故障恢复', () => {
46
- it('应该在服务器恢复后自动恢复推送能力', async () => {
47
- const port = await getAvailablePort();
48
- let requestCount = 0;
49
- let serverStarted = false;
50
-
51
- const createServerWithDelay = () => {
52
- return createServer((req: IncomingMessage, res: ServerResponse) => {
53
- if (!serverStarted) {
54
- // 服务器未准备好,返回错误
55
- res.writeHead(503);
56
- res.end(JSON.stringify({ error: 'Service starting' }));
57
- return;
58
- }
59
-
60
- requestCount++;
61
- let body = '';
62
- req.on('data', (chunk) => (body += chunk));
63
- req.on('end', () => {
64
- res.writeHead(200, { 'Content-Type': 'application/json' });
65
- res.end(JSON.stringify({ success: true }));
66
- });
67
- });
68
- };
69
-
70
- const server = createServerWithDelay();
71
- await new Promise<void>((resolve) => server.listen(port, () => resolve()));
72
-
73
- try {
74
- const pusher = new WebhookPusher({
75
- url: `http://localhost:${port}/webhook`,
76
- token: 'test-token',
77
- timeout: 5000,
78
- enabled: true,
79
- });
80
-
81
- // 服务器未就绪时的推送应该失败
82
- const result1 = await pusher.pushTask({
83
- taskId: 'task-1',
84
- status: 'pending',
85
- createdAt: Date.now(),
86
- });
87
-
88
- expect(result1.success).toBe(false);
89
-
90
- // 模拟服务器启动完成
91
- serverStarted = true;
92
-
93
- // 等待冷却期后重试
94
- // 由于冷却期机制,我们需要手动重置状态
95
- const pusherWithReset = new WebhookPusher({
96
- url: `http://localhost:${port}/webhook`,
97
- token: 'test-token',
98
- timeout: 5000,
99
- enabled: true,
100
- });
101
-
102
- const result2 = await pusherWithReset.pushTask({
103
- taskId: 'task-2',
104
- status: 'pending',
105
- createdAt: Date.now(),
106
- });
107
-
108
- expect(result2.success).toBe(true);
109
- } finally {
110
- await new Promise<void>((resolve) => server.close(() => resolve()));
111
- }
112
- });
113
-
114
- it('应该正确处理连接超时场景', async () => {
115
- const pusher = new WebhookPusher({
116
- url: 'http://localhost:9999/webhook', // 不存在的端口
117
- token: 'test-token',
118
- timeout: 1000, // 1 秒超时
119
- enabled: true,
120
- });
121
-
122
- const startTime = Date.now();
123
- const result = await pusher.pushTask({
124
- taskId: 'timeout-task',
125
- status: 'pending',
126
- createdAt: Date.now(),
127
- });
128
-
129
- const elapsed = Date.now() - startTime;
130
-
131
- expect(result.success).toBe(false);
132
- // 应该在超时时间内返回
133
- expect(elapsed).toBeLessThan(3000);
134
- });
135
-
136
- it('应该在连续失败后进入冷却期', async () => {
137
- const pusher = new WebhookPusher({
138
- url: 'http://localhost:9998/webhook', // 不存在的端口
139
- token: 'test-token',
140
- timeout: 500,
141
- enabled: true,
142
- });
143
-
144
- // 连续失败 3 次
145
- await pusher.pushTask({ taskId: 'fail-1', status: 'pending', createdAt: Date.now() });
146
- await pusher.pushTask({ taskId: 'fail-2', status: 'pending', createdAt: Date.now() });
147
- await pusher.pushTask({ taskId: 'fail-3', status: 'pending', createdAt: Date.now() });
148
-
149
- const status = pusher.getStatus();
150
- expect(status.inCooldown).toBe(true);
151
- expect(status.consecutiveFailures).toBeGreaterThanOrEqual(3);
152
-
153
- // 冷却期内的推送应该被拒绝
154
- const result = await pusher.pushTask({
155
- taskId: 'cooldown-task',
156
- status: 'pending',
157
- createdAt: Date.now(),
158
- });
159
-
160
- expect(result.success).toBe(false);
161
- expect(result.error).toMatch(/cooldown/i);
162
- });
163
- });
164
-
165
- describe('TaskQueue 持久化恢复', () => {
166
- it('应该在持久化数据损坏时自动重建', async () => {
167
- const taskQueue = new TaskQueue({
168
- maxSize: 100,
169
- persistDir: TEST_DATA_DIR,
170
- persistEnabled: true,
171
- });
172
-
173
- // 添加一些任务
174
- taskQueue.add({
175
- taskId: 'before-corrupt',
176
- taskType: 'test',
177
- description: 'Task before corruption',
178
- from: 'peer-1',
179
- timestamp: Date.now(),
180
- timeout: 30000,
181
- });
182
-
183
- taskQueue.close();
184
-
185
- // 手动损坏数据库文件
186
- const dbPath = `${TEST_DATA_DIR}/task-queue.db`;
187
- const fs = await import('fs');
188
- fs.writeFileSync(dbPath, 'CORRUPTED DATA');
189
-
190
- // 重新打开,应该能够恢复
191
- const recoveredQueue = new TaskQueue({
192
- maxSize: 100,
193
- persistDir: TEST_DATA_DIR,
194
- persistEnabled: true,
195
- });
196
-
197
- // 应该能够正常操作
198
- const task = recoveredQueue.add({
199
- taskId: 'after-corrupt',
200
- taskType: 'test',
201
- description: 'Task after corruption recovery',
202
- from: 'peer-2',
203
- timestamp: Date.now(),
204
- timeout: 30000,
205
- });
206
-
207
- expect(task.taskId).toBe('after-corrupt');
208
-
209
- recoveredQueue.close();
210
- });
211
-
212
- it('应该正确恢复大量任务', async () => {
213
- const taskCount = 100;
214
-
215
- // 第一阶段:创建大量任务
216
- {
217
- const taskQueue = new TaskQueue({
218
- maxSize: 1000,
219
- persistDir: TEST_DATA_DIR,
220
- persistEnabled: true,
221
- });
222
-
223
- for (let i = 0; i < taskCount; i++) {
224
- taskQueue.add({
225
- taskId: `bulk-task-${i}`,
226
- taskType: 'bulk-test',
227
- description: `Bulk task ${i}`,
228
- from: `peer-${i % 10}`,
229
- timestamp: Date.now() + i,
230
- timeout: 30000,
231
- });
232
- }
233
-
234
- // 部分标记为处理中
235
- for (let i = 0; i < 20; i++) {
236
- taskQueue.markProcessing(`bulk-task-${i}`);
237
- }
238
-
239
- taskQueue.close();
240
- }
241
-
242
- // 第二阶段:恢复并验证
243
- {
244
- const taskQueue = new TaskQueue({
245
- maxSize: 1000,
246
- persistDir: TEST_DATA_DIR,
247
- persistEnabled: true,
248
- });
249
-
250
- // 等待恢复
251
- await new Promise((r) => setTimeout(r, 100));
252
-
253
- const stats = taskQueue.getStats();
254
-
255
- // 所有任务都应该恢复为 pending(processing 被重置)
256
- expect(stats.pending).toBe(taskCount);
257
- expect(stats.processing).toBe(0);
258
- expect(stats.total).toBe(taskCount);
259
-
260
- // 验证几个任务的内容
261
- const task50 = taskQueue.get('bulk-task-50');
262
- expect(task50).toBeDefined();
263
- expect(task50?.description).toBe('Bulk task 50');
264
-
265
- taskQueue.close();
266
- }
267
- });
268
- });
269
-
270
- describe('ReputationSystem 持久化', () => {
271
- it('应该在重启后保持信誉记录', async () => {
272
- const peerId = 'peer-reputation-test';
273
-
274
- // 第一阶段:创建信誉记录
275
- {
276
- const reputationSystem = new ReputationSystem(
277
- {
278
- enabled: true,
279
- initialScore: 50,
280
- minScoreForService: 20,
281
- decayRate: 0.01,
282
- },
283
- TEST_DATA_DIR
284
- );
285
-
286
- // 模拟多次交互
287
- reputationSystem.recordSuccess(peerId, 'task-1', 100);
288
- reputationSystem.recordSuccess(peerId, 'task-2', 150);
289
- reputationSystem.recordSuccess(peerId, 'task-3', 200);
290
- reputationSystem.recordFailure(peerId, 'task-fail', 'Error');
291
-
292
- reputationSystem.flush();
293
- }
294
-
295
- // 第二阶段:恢复并验证
296
- {
297
- const reputationSystem = new ReputationSystem(
298
- {
299
- enabled: true,
300
- initialScore: 50,
301
- minScoreForService: 20,
302
- decayRate: 0.01,
303
- },
304
- TEST_DATA_DIR
305
- );
306
-
307
- const rep = reputationSystem.getReputation(peerId);
308
-
309
- // 50 + 10*3 - 20 = 60
310
- expect(rep.score).toBe(60);
311
- expect(rep.successfulTasks).toBe(3);
312
- expect(rep.failedTasks).toBe(1);
313
- expect(rep.history).toHaveLength(4);
314
- }
315
- });
316
-
317
- it('应该在并发更新时保持数据一致性', async () => {
318
- const reputationSystem = new ReputationSystem(
319
- {
320
- enabled: true,
321
- initialScore: 50,
322
- minScoreForService: 20,
323
- decayRate: 0.01,
324
- },
325
- TEST_DATA_DIR
326
- );
327
-
328
- const peerId = 'peer-concurrent-test';
329
- const concurrentUpdates = 50;
330
-
331
- // 并发更新
332
- const updates = Array.from({ length: concurrentUpdates }, (_, i) =>
333
- new Promise<void>((resolve) => {
334
- setImmediate(() => {
335
- if (i % 2 === 0) {
336
- reputationSystem.recordSuccess(peerId, `task-${i}`, 100 + i);
337
- } else {
338
- reputationSystem.recordFailure(peerId, `task-${i}`, 'Error');
339
- }
340
- resolve();
341
- });
342
- })
343
- );
344
-
345
- await Promise.all(updates);
346
-
347
- const rep = reputationSystem.getReputation(peerId);
348
-
349
- // 验证一致性:成功和失败次数之和应该等于总更新次数
350
- expect(rep.successfulTasks + rep.failedTasks).toBe(concurrentUpdates);
351
-
352
- // 验证分数在合理范围内
353
- // 每次成功 +10,每次失败 -20
354
- // 25 次成功,25 次失败
355
- // 50 + 25*10 - 25*20 = 50 + 250 - 500 = -200 -> 0
356
- expect(rep.score).toBeGreaterThanOrEqual(0);
357
- expect(rep.score).toBeLessThanOrEqual(100);
358
-
359
- reputationSystem.flush();
360
- });
361
- });
362
-
363
- describe('端到端网络恢复', () => {
364
- it('应该正确处理网络断开重连场景', async () => {
365
- const port = await getAvailablePort();
366
- let isServerUp = true;
367
- let receivedTasks: any[] = [];
368
-
369
- const server = createServer((req: IncomingMessage, res: ServerResponse) => {
370
- if (!isServerUp) {
371
- // 模拟服务器宕机
372
- res.socket?.destroy();
373
- return;
374
- }
375
-
376
- let body = '';
377
- req.on('data', (chunk) => (body += chunk));
378
- req.on('end', () => {
379
- try {
380
- const data = JSON.parse(body);
381
- receivedTasks.push(data);
382
- res.writeHead(200, { 'Content-Type': 'application/json' });
383
- res.end(JSON.stringify({ success: true }));
384
- } catch {
385
- res.writeHead(400);
386
- res.end();
387
- }
388
- });
389
- });
390
-
391
- await new Promise<void>((resolve) => server.listen(port, () => resolve()));
392
-
393
- try {
394
- const taskQueue = new TaskQueue({
395
- maxSize: 100,
396
- persistDir: TEST_DATA_DIR,
397
- persistEnabled: true,
398
- });
399
-
400
- // 阶段 1:正常推送
401
- taskQueue.add({
402
- taskId: 'network-task-1',
403
- taskType: 'test',
404
- description: 'Task during network up',
405
- from: 'peer-1',
406
- timestamp: Date.now(),
407
- timeout: 30000,
408
- });
409
-
410
- await fetch(`http://localhost:${port}/webhook`, {
411
- method: 'POST',
412
- headers: { 'Content-Type': 'application/json' },
413
- body: JSON.stringify({ taskId: 'network-task-1' }),
414
- });
415
-
416
- expect(receivedTasks).toHaveLength(1);
417
-
418
- // 阶段 2:模拟网络断开
419
- isServerUp = false;
420
-
421
- // 任务入队(无法推送)
422
- taskQueue.add({
423
- taskId: 'network-task-2',
424
- taskType: 'test',
425
- description: 'Task during network down',
426
- from: 'peer-1',
427
- timestamp: Date.now(),
428
- timeout: 30000,
429
- });
430
-
431
- // 推送应该失败
432
- const downResult = await fetch(`http://localhost:${port}/webhook`, {
433
- method: 'POST',
434
- headers: { 'Content-Type': 'application/json' },
435
- body: JSON.stringify({ taskId: 'network-task-2' }),
436
- }).catch(() => null);
437
-
438
- expect(downResult).toBeNull();
439
-
440
- // 阶段 3:网络恢复
441
- isServerUp = true;
442
-
443
- // 重新推送待处理任务
444
- const pendingTasks = taskQueue.getWebhookPending();
445
- expect(pendingTasks.length).toBeGreaterThan(0);
446
-
447
- for (const task of pendingTasks) {
448
- await fetch(`http://localhost:${port}/webhook`, {
449
- method: 'POST',
450
- headers: { 'Content-Type': 'application/json' },
451
- body: JSON.stringify(task),
452
- });
453
- taskQueue.markWebhookPushed(task.taskId);
454
- }
455
-
456
- // 验证所有任务都已推送
457
- expect(receivedTasks.length).toBeGreaterThan(1);
458
-
459
- taskQueue.close();
460
- } finally {
461
- await new Promise<void>((resolve) => server.close(() => resolve()));
462
- }
463
- });
464
- });
465
- });
@@ -1,136 +0,0 @@
1
- import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
2
- import { F2ANodeManager } from './node-manager';
3
- import { F2ANodeConfig } from './types';
4
-
5
- // Mock child_process
6
- vi.mock('child_process', () => ({
7
- spawn: vi.fn(),
8
- exec: vi.fn(),
9
- }));
10
-
11
- // Mock fs - 返回合理的默认值
12
- vi.mock('fs', () => ({
13
- existsSync: vi.fn(() => false), // 默认 PID 文件不存在
14
- readFileSync: vi.fn(() => ''), // 默认返回空字符串
15
- writeFileSync: vi.fn(),
16
- mkdirSync: vi.fn(),
17
- unlinkSync: vi.fn(),
18
- }));
19
-
20
- // Mock util
21
- vi.mock('util', () => ({
22
- promisify: vi.fn((fn) => fn),
23
- }));
24
-
25
- describe('F2ANodeManager', () => {
26
- let manager: F2ANodeManager;
27
- const mockConfig: F2ANodeConfig = {
28
- nodePath: '/test/F2A',
29
- controlPort: 9001,
30
- controlToken: 'test-token',
31
- p2pPort: 9000,
32
- enableMDNS: true,
33
- };
34
-
35
- beforeEach(() => {
36
- vi.clearAllMocks();
37
- vi.useFakeTimers();
38
- manager = new F2ANodeManager(mockConfig);
39
- });
40
-
41
- afterEach(() => {
42
- vi.useRealTimers();
43
- });
44
-
45
- describe('isRunning', () => {
46
- it('should return false initially', async () => {
47
- const result = await manager.isRunning();
48
- expect(result).toBe(false);
49
- });
50
- });
51
-
52
- describe('getStatus', () => {
53
- it('should return error status when node is down', async () => {
54
- const status = await manager.getStatus();
55
- expect(status.success).toBe(false);
56
- });
57
- });
58
-
59
- describe('ensureRunning', () => {
60
- it('should return error if start fails', async () => {
61
- const result = await manager.ensureRunning();
62
- // Since we mocked everything, it will fail to start
63
- expect(result.success).toBe(false);
64
- });
65
- });
66
-
67
- describe('start', () => {
68
- it('should return error when daemon not found', async () => {
69
- const { existsSync } = await import('fs');
70
- (existsSync as any).mockReturnValue(false);
71
-
72
- const result = await manager.start();
73
- expect(result.success).toBe(false);
74
- expect(result.error).toContain('F2A Node 未找到');
75
- });
76
- });
77
-
78
- describe('stop', () => {
79
- it('should stop without error when not running', async () => {
80
- await expect(manager.stop()).resolves.not.toThrow();
81
- });
82
- });
83
-
84
- describe('getConfig', () => {
85
- it('should return config copy', () => {
86
- const config = manager.getConfig();
87
- expect(config.nodePath).toBe(mockConfig.nodePath);
88
- expect(config.controlPort).toBe(mockConfig.controlPort);
89
- });
90
- });
91
-
92
- // P1 修复:测试健康检查重启限制
93
- describe('健康检查重启限制', () => {
94
- it('应该限制连续重启次数', async () => {
95
- // 模拟进程对象
96
- const mockProcess = {
97
- pid: 12345,
98
- kill: vi.fn(),
99
- on: vi.fn(),
100
- unref: vi.fn(),
101
- stdout: { on: vi.fn() },
102
- stderr: { on: vi.fn() },
103
- exitCode: null,
104
- };
105
-
106
- const { spawn } = await import('child_process');
107
- const { existsSync } = await import('fs');
108
-
109
- (existsSync as any).mockReturnValue(true);
110
- (spawn as any).mockReturnValue(mockProcess);
111
-
112
- // 启动 manager
113
- await manager.start();
114
-
115
- // 模拟健康检查失败
116
- // 连续 3 次重启后,应该停止尝试
117
- for (let i = 0; i < 5; i++) {
118
- // 触发健康检查间隔(30秒)
119
- vi.advanceTimersByTime(30000);
120
- // 等待异步操作完成
121
- await Promise.resolve();
122
- }
123
-
124
- // spawn 应该被调用最多 4 次(初始启动 + 3 次重启)
125
- // 由于我们在 mock 环境中,实际行为可能不同
126
- // 但我们验证重启限制的逻辑存在
127
- });
128
-
129
- it('应该有冷却期机制', () => {
130
- // 验证 manager 有重启限制相关的属性
131
- // 这是一个内部实现测试,确保机制存在
132
- const config = manager.getConfig();
133
- expect(config).toBeDefined();
134
- });
135
- });
136
- });